Files
palemoon27/gfx/layers/BufferTexture.cpp
T
roytam1 ca19b65a80 import changes from `dev' branch of rmottola/Arctic-Fox:
- Bug 1250873 - Rename HasInternalBuffer into HasIntermediateBuffer in layers. r=sotaro (578235105f)
- Bug 1256045 - Add a null-check in BufferTextureHost::EnsureWrappingTextureSource. r=jnicol (943c73559d)
- Bug 1251726 - Check if Compositor is set r=nical (550d5b0164)
- Bug 1251427 - Require a full update when a TextureHost switches from a TextureSource to another. r=sotaro (bc59ac4cd7)
- Bug 1256693 - ISurfaceAllocator cleanup. r=sotaro (098e824d4d)
- Bug 1257939 - initialize BGRX alpha channel to opaque when clearing and ignore uninitialized alpha in texture clients. r=mchang (73d778496f)
- more of reapply Bug 1200595 - Consolidate the TextureClient's destruction logic (74517415ed)
- Bug 1256693 - Refer to ClientIPCAllocator instead of ISurfaceAllocator where it makes sense. r=sotaro (e81f2dd923)
- Bug 1236112 - Block on d3d9 video frames to complete before returning them from the decoder. r=cpearce (25114bb3c4)
- Bug 1196409 - Disable D3D11-DXVA for resolutions not supported in hardware. r=jya (3007b1ebff)
- Bug 1196411 - Disable DXVA on 60fps 1080p videos for AMD cards that can't decode quick enough. r=jya (9f8f67e12b)
- Bug 1200775 - Check intel specific h264 decoder when checking for DXVA support. r=cpearce (e7bcbb10be)
- Bug 1257013 - Part 1: Use readback to synchronize d3d9 video. r=cpearce,Bas (d247a9bed6)
- Bug 1257013 - Part 2: Use readback to synchronize d3d11 video. r=cpearce,Bas (43883c1607)
- missing bit of Bug 1206568: P2 (58de11b22f)
- Bug 1239093 - Add pref to allow overriding of hardcoded DXVA blacklist. r=jrmuizel (dfd5e57c2f)
- Bug 1217185: To allow for sandboxing, use null HWNDs when creating the D3D device for video decoding. r=mattwoodrow (0c96e66a47)
- Bug 1200775 - Followup to fix typo and indent issues (b1d1c76788)
- bits of  Bug 1207245 - part 3 (52a1939b74)
- Bug 1224199 - Don't make the TextureClient wait for compositor recycle if the GLContext is shutting down - r=nical (9a0081f217)
- more of Bug 1200595 (047201fd60)
- Bug 1253094, part 2 - Stop using DebugOnly for class/struct members in gfx/. r=Bas (bab6569366)
2024-02-01 10:25:42 +08:00

558 lines
17 KiB
C++

/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
// * This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "BufferTexture.h"
#include "mozilla/layers/ImageDataSerializer.h"
#include "mozilla/layers/ISurfaceAllocator.h"
#include "mozilla/layers/CompositableForwarder.h"
#include "mozilla/gfx/Logging.h"
#include "mozilla/gfx/2D.h"
#include "mozilla/fallible.h"
#include <algorithm>
#ifdef MOZ_WIDGET_GTK
#include "gfxPlatformGtk.h"
#endif
namespace mozilla {
namespace layers {
class MemoryTextureData : public BufferTextureData
{
public:
static MemoryTextureData* Create(gfx::IntSize aSize, gfx::SurfaceFormat aFormat,
gfx::BackendType aMoz2DBackend,TextureFlags aFlags,
TextureAllocationFlags aAllocFlags,
ClientIPCAllocator* aAllocator);
virtual TextureData*
CreateSimilar(ClientIPCAllocator* aAllocator,
TextureFlags aFlags = TextureFlags::DEFAULT,
TextureAllocationFlags aAllocFlags = ALLOC_DEFAULT) const override;
virtual bool Serialize(SurfaceDescriptor& aOutDescriptor) override;
virtual void Deallocate(ClientIPCAllocator*) override;
MemoryTextureData(const BufferDescriptor& aDesc,
gfx::BackendType aMoz2DBackend,
uint8_t* aBuffer, size_t aBufferSize)
: BufferTextureData(aDesc, aMoz2DBackend)
, mBuffer(aBuffer)
, mBufferSize(aBufferSize)
{
MOZ_ASSERT(aBuffer);
MOZ_ASSERT(aBufferSize);
}
virtual uint8_t* GetBuffer() override { return mBuffer; }
virtual size_t GetBufferSize() override { return mBufferSize; }
protected:
uint8_t* mBuffer;
size_t mBufferSize;
};
class ShmemTextureData : public BufferTextureData
{
public:
static ShmemTextureData* Create(gfx::IntSize aSize, gfx::SurfaceFormat aFormat,
gfx::BackendType aMoz2DBackend, TextureFlags aFlags,
TextureAllocationFlags aAllocFlags,
ClientIPCAllocator* aAllocator);
virtual TextureData*
CreateSimilar(ClientIPCAllocator* aAllocator,
TextureFlags aFlags = TextureFlags::DEFAULT,
TextureAllocationFlags aAllocFlags = ALLOC_DEFAULT) const override;
virtual bool Serialize(SurfaceDescriptor& aOutDescriptor) override;
virtual void Deallocate(ClientIPCAllocator* aAllocator) override;
ShmemTextureData(const BufferDescriptor& aDesc,
gfx::BackendType aMoz2DBackend, mozilla::ipc::Shmem aShmem)
: BufferTextureData(aDesc, aMoz2DBackend)
, mShmem(aShmem)
{
MOZ_ASSERT(mShmem.Size<uint8_t>());
}
virtual uint8_t* GetBuffer() override { return mShmem.get<uint8_t>(); }
virtual size_t GetBufferSize() override { return mShmem.Size<uint8_t>(); }
protected:
mozilla::ipc::Shmem mShmem;
};
static bool UsingX11Compositor()
{
#ifdef MOZ_WIDGET_GTK
return gfxPlatformGtk::GetPlatform()->UseXRender();
#endif
return false;
}
static bool ComputeHasIntermediateBuffer(gfx::SurfaceFormat aFormat,
LayersBackend aLayersBackend)
{
return aLayersBackend != LayersBackend::LAYERS_BASIC
|| UsingX11Compositor()
|| aFormat == gfx::SurfaceFormat::UNKNOWN
|| aFormat == gfx::SurfaceFormat::YUV
|| aFormat == gfx::SurfaceFormat::NV12;
}
BufferTextureData*
BufferTextureData::Create(gfx::IntSize aSize, gfx::SurfaceFormat aFormat,
gfx::BackendType aMoz2DBackend, TextureFlags aFlags,
TextureAllocationFlags aAllocFlags,
ClientIPCAllocator* aAllocator)
{
if (!aAllocator || aAllocator->IsSameProcess()) {
return MemoryTextureData::Create(aSize, aFormat, aMoz2DBackend, aFlags,
aAllocFlags, aAllocator);
} else if (aAllocator->AsShmemAllocator()) {
return ShmemTextureData::Create(aSize, aFormat, aMoz2DBackend, aFlags,
aAllocFlags, aAllocator);
}
return nullptr;
}
BufferTextureData*
BufferTextureData::CreateInternal(ClientIPCAllocator* aAllocator,
const BufferDescriptor& aDesc,
gfx::BackendType aMoz2DBackend,
int32_t aBufferSize,
TextureFlags aTextureFlags)
{
if (!aAllocator || aAllocator->IsSameProcess()) {
uint8_t* buffer = new (fallible) uint8_t[aBufferSize];
if (!buffer) {
return nullptr;
}
GfxMemoryImageReporter::DidAlloc(buffer);
return new MemoryTextureData(aDesc, aMoz2DBackend, buffer, aBufferSize);
} else if (aAllocator->AsShmemAllocator()) {
ipc::Shmem shm;
if (!aAllocator->AsShmemAllocator()->AllocUnsafeShmem(aBufferSize, OptimalShmemType(), &shm)) {
return nullptr;
}
return new ShmemTextureData(aDesc, aMoz2DBackend, shm);
}
return nullptr;
}
BufferTextureData*
BufferTextureData::CreateForYCbCrWithBufferSize(ClientIPCAllocator* aAllocator,
gfx::SurfaceFormat aFormat,
int32_t aBufferSize,
TextureFlags aTextureFlags)
{
if (aBufferSize == 0 || !gfx::Factory::CheckBufferSize(aBufferSize)) {
return nullptr;
}
// Initialize the metadata with something, even if it will have to be rewritten
// afterwards since we don't know the dimensions of the texture at this point.
BufferDescriptor desc = YCbCrDescriptor(gfx::IntSize(), gfx::IntSize(),
0, 0, 0, StereoMode::MONO);
return CreateInternal(aAllocator, desc, gfx::BackendType::NONE, aBufferSize,
aTextureFlags);
}
BufferTextureData*
BufferTextureData::CreateForYCbCr(ClientIPCAllocator* aAllocator,
gfx::IntSize aYSize,
gfx::IntSize aCbCrSize,
StereoMode aStereoMode,
TextureFlags aTextureFlags)
{
uint32_t bufSize = ImageDataSerializer::ComputeYCbCrBufferSize(aYSize, aCbCrSize);
if (bufSize == 0) {
return nullptr;
}
uint32_t yOffset;
uint32_t cbOffset;
uint32_t crOffset;
ImageDataSerializer::ComputeYCbCrOffsets(aYSize.width, aYSize.height,
aCbCrSize.width, aCbCrSize.height,
yOffset, cbOffset, crOffset);
YCbCrDescriptor descriptor = YCbCrDescriptor(aYSize, aCbCrSize, yOffset, cbOffset,
crOffset, aStereoMode);
return CreateInternal(aAllocator, descriptor, gfx::BackendType::NONE, bufSize,
aTextureFlags);
}
gfx::IntSize
BufferTextureData::GetSize() const
{
return ImageDataSerializer::SizeFromBufferDescriptor(mDescriptor);
}
gfx::SurfaceFormat
BufferTextureData::GetFormat() const
{
return ImageDataSerializer::FormatFromBufferDescriptor(mDescriptor);
}
bool
BufferTextureData::HasIntermediateBuffer() const
{
if (mDescriptor.type() == BufferDescriptor::TYCbCrDescriptor) {
return true;
}
return mDescriptor.get_RGBDescriptor().hasIntermediateBuffer();
}
bool
BufferTextureData::SupportsMoz2D() const
{
switch (GetFormat()) {
case gfx::SurfaceFormat::YUV:
case gfx::SurfaceFormat::NV12:
case gfx::SurfaceFormat::UNKNOWN:
return false;
default:
return true;
}
}
already_AddRefed<gfx::DrawTarget>
BufferTextureData::BorrowDrawTarget()
{
if (mDrawTarget) {
mDrawTarget->SetTransform(gfx::Matrix());
RefPtr<gfx::DrawTarget> dt = mDrawTarget;
return dt.forget();
}
if (mDescriptor.type() != BufferDescriptor::TRGBDescriptor) {
return nullptr;
}
const RGBDescriptor& rgb = mDescriptor.get_RGBDescriptor();
uint32_t stride = ImageDataSerializer::GetRGBStride(rgb);
mDrawTarget = gfx::Factory::CreateDrawTargetForData(mMoz2DBackend,
GetBuffer(), rgb.size(),
stride, rgb.format(), true);
if (mDrawTarget) {
RefPtr<gfx::DrawTarget> dt = mDrawTarget;
return dt.forget();
}
// TODO - should we warn? should we really fallback to cairo? perhaps
// at least update mMoz2DBackend...
if (mMoz2DBackend != gfx::BackendType::CAIRO) {
mDrawTarget = gfx::Factory::CreateDrawTargetForData(gfx::BackendType::CAIRO,
GetBuffer(), rgb.size(),
stride, rgb.format(), true);
}
if (!mDrawTarget) {
gfxCriticalNote << "BorrowDrawTarget failure, original backend " << (int)mMoz2DBackend;
}
RefPtr<gfx::DrawTarget> dt = mDrawTarget;
return dt.forget();
}
bool
BufferTextureData::BorrowMappedData(MappedTextureData& aData)
{
if (GetFormat() == gfx::SurfaceFormat::YUV) {
return false;
}
gfx::IntSize size = GetSize();
aData.data = GetBuffer();
aData.size = size;
aData.format = GetFormat();
aData.stride = ImageDataSerializer::ComputeRGBStride(aData.format, size.width);
return true;
}
bool
BufferTextureData::BorrowMappedYCbCrData(MappedYCbCrTextureData& aMap)
{
if (mDescriptor.type() != BufferDescriptor::TYCbCrDescriptor) {
return false;
}
const YCbCrDescriptor& desc = mDescriptor.get_YCbCrDescriptor();
uint8_t* data = GetBuffer();
auto ySize = desc.ySize();
auto cbCrSize = desc.cbCrSize();
aMap.stereoMode = desc.stereoMode();
aMap.metadata = nullptr;
aMap.y.data = data + desc.yOffset();
aMap.y.size = ySize;
aMap.y.stride = ySize.width;
aMap.y.skip = 0;
aMap.cb.data = data + desc.cbOffset();
aMap.cb.size = cbCrSize;
aMap.cb.stride = cbCrSize.width;
aMap.cb.skip = 0;
aMap.cr.data = data + desc.crOffset();
aMap.cr.size = cbCrSize;
aMap.cr.stride = cbCrSize.width;
aMap.cr.skip = 0;
return true;
}
bool
BufferTextureData::UpdateFromSurface(gfx::SourceSurface* aSurface)
{
if (mDescriptor.type() != BufferDescriptor::TRGBDescriptor) {
return false;
}
const RGBDescriptor& rgb = mDescriptor.get_RGBDescriptor();
uint32_t stride = ImageDataSerializer::GetRGBStride(rgb);
RefPtr<gfx::DataSourceSurface> surface =
gfx::Factory::CreateWrappingDataSourceSurface(GetBuffer(), stride,
rgb.size(), rgb.format());
if (!surface) {
gfxCriticalError() << "Failed to get serializer as surface!";
return false;
}
RefPtr<gfx::DataSourceSurface> srcSurf = aSurface->GetDataSurface();
if (!srcSurf) {
gfxCriticalError() << "Failed to GetDataSurface in UpdateFromSurface (BT).";
return false;
}
if (surface->GetSize() != srcSurf->GetSize() || surface->GetFormat() != srcSurf->GetFormat()) {
gfxCriticalError() << "Attempt to update texture client from a surface with a different size or format (BT)! This: " << surface->GetSize() << " " << surface->GetFormat() << " Other: " << aSurface->GetSize() << " " << aSurface->GetFormat();
return false;
}
gfx::DataSourceSurface::MappedSurface sourceMap;
gfx::DataSourceSurface::MappedSurface destMap;
if (!srcSurf->Map(gfx::DataSourceSurface::READ, &sourceMap)) {
gfxCriticalError() << "Failed to map source surface for UpdateFromSurface (BT).";
return false;
}
if (!surface->Map(gfx::DataSourceSurface::WRITE, &destMap)) {
srcSurf->Unmap();
gfxCriticalError() << "Failed to map destination surface for UpdateFromSurface.";
return false;
}
for (int y = 0; y < srcSurf->GetSize().height; y++) {
memcpy(destMap.mData + destMap.mStride * y,
sourceMap.mData + sourceMap.mStride * y,
srcSurf->GetSize().width * BytesPerPixel(srcSurf->GetFormat()));
}
srcSurf->Unmap();
surface->Unmap();
return true;
}
void
BufferTextureData::SetDesciptor(const BufferDescriptor& aDescriptor)
{
MOZ_ASSERT(mDescriptor.type() == BufferDescriptor::TYCbCrDescriptor);
MOZ_ASSERT(mDescriptor.get_YCbCrDescriptor().ySize() == gfx::IntSize());
mDescriptor = aDescriptor;
}
bool
MemoryTextureData::Serialize(SurfaceDescriptor& aOutDescriptor)
{
MOZ_ASSERT(GetFormat() != gfx::SurfaceFormat::UNKNOWN);
if (GetFormat() == gfx::SurfaceFormat::UNKNOWN) {
return false;
}
uintptr_t ptr = reinterpret_cast<uintptr_t>(mBuffer);
aOutDescriptor = SurfaceDescriptorBuffer(mDescriptor, MemoryOrShmem(ptr));
return true;
}
static bool InitBuffer(uint8_t* buf, size_t bufSize, gfx::SurfaceFormat aFormat, TextureAllocationFlags aAllocFlags)
{
if (!buf) {
gfxDebug() << "BufferTextureData: Failed to allocate " << bufSize << " bytes";
return false;
}
if ((aAllocFlags & ALLOC_CLEAR_BUFFER) ||
(aAllocFlags & ALLOC_CLEAR_BUFFER_BLACK)) {
if (aFormat == gfx::SurfaceFormat::B8G8R8X8) {
// Even though BGRX was requested, XRGB_UINT32 is what is meant,
// so use 0xFF000000 to put alpha in the right place.
std::fill_n(reinterpret_cast<uint32_t*>(buf), bufSize / sizeof(uint32_t), 0xFF000000);
} else {
memset(buf, 0, bufSize);
}
}
if (aAllocFlags & ALLOC_CLEAR_BUFFER_WHITE) {
memset(buf, 0xFF, bufSize);
}
return true;
}
MemoryTextureData*
MemoryTextureData::Create(gfx::IntSize aSize, gfx::SurfaceFormat aFormat,
gfx::BackendType aMoz2DBackend, TextureFlags aFlags,
TextureAllocationFlags aAllocFlags,
ClientIPCAllocator* aAllocator)
{
// Should have used CreateForYCbCr.
MOZ_ASSERT(aFormat != gfx::SurfaceFormat::YUV);
if (aSize.width <= 0 || aSize.height <= 0) {
gfxDebug() << "Asking for buffer of invalid size " << aSize.width << "x" << aSize.height;
return nullptr;
}
uint32_t bufSize = ImageDataSerializer::ComputeRGBBufferSize(aSize, aFormat);
if (!bufSize) {
return nullptr;
}
uint8_t* buf = new (fallible) uint8_t[bufSize];
if (!InitBuffer(buf, bufSize, aFormat, aAllocFlags)) {
return nullptr;
}
auto fwd = aAllocator ? aAllocator->AsCompositableForwarder() : nullptr;
bool hasIntermediateBuffer = fwd ? ComputeHasIntermediateBuffer(aFormat,
fwd->GetCompositorBackendType())
: true;
GfxMemoryImageReporter::DidAlloc(buf);
BufferDescriptor descriptor = RGBDescriptor(aSize, aFormat, hasIntermediateBuffer);
return new MemoryTextureData(descriptor, aMoz2DBackend, buf, bufSize);
}
void
MemoryTextureData::Deallocate(ClientIPCAllocator*)
{
MOZ_ASSERT(mBuffer);
GfxMemoryImageReporter::WillFree(mBuffer);
delete [] mBuffer;
mBuffer = nullptr;
}
TextureData*
MemoryTextureData::CreateSimilar(ClientIPCAllocator* aAllocator,
TextureFlags aFlags,
TextureAllocationFlags aAllocFlags) const
{
return MemoryTextureData::Create(GetSize(), GetFormat(), mMoz2DBackend,
aFlags, aAllocFlags, aAllocator);
}
bool
ShmemTextureData::Serialize(SurfaceDescriptor& aOutDescriptor)
{
MOZ_ASSERT(GetFormat() != gfx::SurfaceFormat::UNKNOWN);
if (GetFormat() == gfx::SurfaceFormat::UNKNOWN) {
return false;
}
aOutDescriptor = SurfaceDescriptorBuffer(mDescriptor, MemoryOrShmem(mShmem));
return true;
}
ShmemTextureData*
ShmemTextureData::Create(gfx::IntSize aSize, gfx::SurfaceFormat aFormat,
gfx::BackendType aMoz2DBackend, TextureFlags aFlags,
TextureAllocationFlags aAllocFlags,
ClientIPCAllocator* aAllocator)
{
MOZ_ASSERT(aAllocator);
// Should have used CreateForYCbCr.
MOZ_ASSERT(aFormat != gfx::SurfaceFormat::YUV);
if (!aAllocator || !aAllocator->AsShmemAllocator()) {
return nullptr;
}
if (aSize.width <= 0 || aSize.height <= 0) {
gfxDebug() << "Asking for buffer of invalid size " << aSize.width << "x" << aSize.height;
return nullptr;
}
uint32_t bufSize = ImageDataSerializer::ComputeRGBBufferSize(aSize, aFormat);
if (!bufSize) {
return nullptr;
}
mozilla::ipc::Shmem shm;
if (!aAllocator->AsShmemAllocator()->AllocUnsafeShmem(bufSize, OptimalShmemType(), &shm)) {
return nullptr;
}
uint8_t* buf = shm.get<uint8_t>();
if (!InitBuffer(buf, bufSize, aFormat, aAllocFlags)) {
return nullptr;
}
auto fwd = aAllocator->AsCompositableForwarder();
bool hasIntermediateBuffer = fwd ? ComputeHasIntermediateBuffer(aFormat,
fwd->GetCompositorBackendType())
: true;
BufferDescriptor descriptor = RGBDescriptor(aSize, aFormat, hasIntermediateBuffer);
return new ShmemTextureData(descriptor, aMoz2DBackend, shm);
return nullptr;
}
TextureData*
ShmemTextureData::CreateSimilar(ClientIPCAllocator* aAllocator,
TextureFlags aFlags,
TextureAllocationFlags aAllocFlags) const
{
return ShmemTextureData::Create(GetSize(), GetFormat(), mMoz2DBackend,
aFlags, aAllocFlags, aAllocator);
}
void
ShmemTextureData::Deallocate(ClientIPCAllocator* aAllocator)
{
aAllocator->AsShmemAllocator()->DeallocShmem(mShmem);
}
} // namespace
} // namespace