summaryrefslogtreecommitdiffstats
path: root/gfx/layers/composite/TextureHost.cpp
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--gfx/layers/composite/TextureHost.cpp1371
1 files changed, 1371 insertions, 0 deletions
diff --git a/gfx/layers/composite/TextureHost.cpp b/gfx/layers/composite/TextureHost.cpp
new file mode 100644
index 0000000000..92ced1ee89
--- /dev/null
+++ b/gfx/layers/composite/TextureHost.cpp
@@ -0,0 +1,1371 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* 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 "TextureHost.h"
+
+#include "CompositableHost.h" // for CompositableHost
+#include "LayerScope.h"
+#include "mozilla/gfx/2D.h" // for DataSourceSurface, Factory
+#include "mozilla/gfx/gfxVars.h"
+#include "mozilla/ipc/Shmem.h" // for Shmem
+#include "mozilla/layers/AsyncImagePipelineManager.h"
+#include "mozilla/layers/BufferTexture.h"
+#include "mozilla/layers/CompositableTransactionParent.h" // for CompositableParentManager
+#include "mozilla/layers/CompositorBridgeParent.h"
+#include "mozilla/layers/Compositor.h" // for Compositor
+#include "mozilla/layers/ISurfaceAllocator.h" // for ISurfaceAllocator
+#include "mozilla/layers/ImageBridgeParent.h" // for ImageBridgeParent
+#include "mozilla/layers/LayersSurfaces.h" // for SurfaceDescriptor, etc
+#include "mozilla/layers/TextureHostBasic.h"
+#include "mozilla/layers/TextureHostOGL.h" // for TextureHostOGL
+#include "mozilla/layers/ImageDataSerializer.h"
+#include "mozilla/layers/TextureClient.h"
+#ifdef XP_DARWIN
+# include "mozilla/layers/TextureSync.h"
+#endif
+#include "mozilla/layers/GPUVideoTextureHost.h"
+#include "mozilla/layers/WebRenderTextureHost.h"
+#include "mozilla/StaticPrefs_layers.h"
+#include "mozilla/StaticPrefs_gfx.h"
+#include "mozilla/webrender/RenderBufferTextureHost.h"
+#include "mozilla/webrender/RenderBufferTextureHostSWGL.h"
+#include "mozilla/webrender/RenderExternalTextureHost.h"
+#include "mozilla/webrender/RenderThread.h"
+#include "mozilla/webrender/WebRenderAPI.h"
+#include "nsAString.h"
+#include "mozilla/RefPtr.h" // for nsRefPtr
+#include "nsPrintfCString.h" // for nsPrintfCString
+#include "mozilla/layers/PTextureParent.h"
+#include "mozilla/Unused.h"
+#include <limits>
+#include "../opengl/CompositorOGL.h"
+
+#include "gfxUtils.h"
+#include "IPDLActor.h"
+
+#ifdef MOZ_ENABLE_D3D10_LAYER
+# include "../d3d11/CompositorD3D11.h"
+#endif
+
+#ifdef MOZ_X11
+# include "mozilla/layers/X11TextureHost.h"
+#endif
+
+#ifdef XP_MACOSX
+# include "../opengl/MacIOSurfaceTextureHostOGL.h"
+#endif
+
+#ifdef XP_WIN
+# include "mozilla/layers/TextureD3D11.h"
+# include "mozilla/layers/TextureDIB.h"
+#endif
+
+#if 0
+# define RECYCLE_LOG(...) printf_stderr(__VA_ARGS__)
+#else
+# define RECYCLE_LOG(...) \
+ do { \
+ } while (0)
+#endif
+
+namespace mozilla {
+namespace layers {
+
+/**
+ * TextureParent is the host-side IPDL glue between TextureClient and
+ * TextureHost. It is an IPDL actor just like LayerParent, CompositableParent,
+ * etc.
+ */
+class TextureParent : public ParentActor<PTextureParent> {
+ public:
+ TextureParent(HostIPCAllocator* aAllocator, uint64_t aSerial,
+ const wr::MaybeExternalImageId& aExternalImageId);
+
+ virtual ~TextureParent();
+
+ bool Init(const SurfaceDescriptor& aSharedData,
+ const ReadLockDescriptor& aReadLock,
+ const LayersBackend& aLayersBackend, const TextureFlags& aFlags);
+
+ void NotifyNotUsed(uint64_t aTransactionId);
+
+ mozilla::ipc::IPCResult RecvRecycleTexture(
+ const TextureFlags& aTextureFlags) final;
+
+ TextureHost* GetTextureHost() { return mTextureHost; }
+
+ void Destroy() override;
+
+ uint64_t GetSerial() const { return mSerial; }
+
+ HostIPCAllocator* mSurfaceAllocator;
+ RefPtr<TextureHost> mTextureHost;
+ // mSerial is unique in TextureClient's process.
+ const uint64_t mSerial;
+ wr::MaybeExternalImageId mExternalImageId;
+};
+
+static bool WrapWithWebRenderTextureHost(ISurfaceAllocator* aDeallocator,
+ LayersBackend aBackend,
+ TextureFlags aFlags) {
+ if ((aFlags & TextureFlags::SNAPSHOT) ||
+ (aBackend != LayersBackend::LAYERS_WR) ||
+ (!aDeallocator->UsesImageBridge() &&
+ !aDeallocator->AsCompositorBridgeParentBase())) {
+ return false;
+ }
+ return true;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+PTextureParent* TextureHost::CreateIPDLActor(
+ HostIPCAllocator* aAllocator, const SurfaceDescriptor& aSharedData,
+ const ReadLockDescriptor& aReadLock, LayersBackend aLayersBackend,
+ TextureFlags aFlags, uint64_t aSerial,
+ const wr::MaybeExternalImageId& aExternalImageId) {
+ TextureParent* actor =
+ new TextureParent(aAllocator, aSerial, aExternalImageId);
+ if (!actor->Init(aSharedData, aReadLock, aLayersBackend, aFlags)) {
+ actor->ActorDestroy(ipc::IProtocol::ActorDestroyReason::FailedConstructor);
+ delete actor;
+ return nullptr;
+ }
+ return actor;
+}
+
+// static
+bool TextureHost::DestroyIPDLActor(PTextureParent* actor) {
+ delete actor;
+ return true;
+}
+
+// static
+bool TextureHost::SendDeleteIPDLActor(PTextureParent* actor) {
+ return PTextureParent::Send__delete__(actor);
+}
+
+// static
+TextureHost* TextureHost::AsTextureHost(PTextureParent* actor) {
+ if (!actor) {
+ return nullptr;
+ }
+ return static_cast<TextureParent*>(actor)->mTextureHost;
+}
+
+// static
+uint64_t TextureHost::GetTextureSerial(PTextureParent* actor) {
+ if (!actor) {
+ return UINT64_MAX;
+ }
+ return static_cast<TextureParent*>(actor)->mSerial;
+}
+
+PTextureParent* TextureHost::GetIPDLActor() { return mActor; }
+
+void TextureHost::SetLastFwdTransactionId(uint64_t aTransactionId) {
+ MOZ_ASSERT(mFwdTransactionId <= aTransactionId);
+ mFwdTransactionId = aTransactionId;
+}
+
+already_AddRefed<TextureHost> CreateDummyBufferTextureHost(
+ mozilla::layers::LayersBackend aBackend,
+ mozilla::layers::TextureFlags aFlags) {
+ // Ensure that the host will delete the memory.
+ aFlags &= ~TextureFlags::DEALLOCATE_CLIENT;
+ UniquePtr<TextureData> textureData(BufferTextureData::Create(
+ gfx::IntSize(1, 1), gfx::SurfaceFormat::B8G8R8A8, gfx::BackendType::SKIA,
+ aBackend, aFlags, TextureAllocationFlags::ALLOC_DEFAULT, nullptr));
+ SurfaceDescriptor surfDesc;
+ textureData->Serialize(surfDesc);
+ const SurfaceDescriptorBuffer& bufferDesc =
+ surfDesc.get_SurfaceDescriptorBuffer();
+ const MemoryOrShmem& data = bufferDesc.data();
+ RefPtr<TextureHost> host =
+ new MemoryTextureHost(reinterpret_cast<uint8_t*>(data.get_uintptr_t()),
+ bufferDesc.desc(), aFlags);
+ return host.forget();
+}
+
+already_AddRefed<TextureHost> TextureHost::Create(
+ const SurfaceDescriptor& aDesc, const ReadLockDescriptor& aReadLock,
+ ISurfaceAllocator* aDeallocator, LayersBackend aBackend,
+ TextureFlags aFlags, wr::MaybeExternalImageId& aExternalImageId) {
+ RefPtr<TextureHost> result;
+
+ switch (aDesc.type()) {
+ case SurfaceDescriptor::TSurfaceDescriptorBuffer:
+ case SurfaceDescriptor::TSurfaceDescriptorDIB:
+ case SurfaceDescriptor::TSurfaceDescriptorFileMapping:
+ case SurfaceDescriptor::TSurfaceDescriptorGPUVideo:
+ result = CreateBackendIndependentTextureHost(aDesc, aDeallocator,
+ aBackend, aFlags);
+ break;
+
+ case SurfaceDescriptor::TEGLImageDescriptor:
+ case SurfaceDescriptor::TSurfaceTextureDescriptor:
+ case SurfaceDescriptor::TSurfaceDescriptorAndroidHardwareBuffer:
+ case SurfaceDescriptor::TSurfaceDescriptorSharedGLTexture:
+ case SurfaceDescriptor::TSurfaceDescriptorDMABuf:
+ result = CreateTextureHostOGL(aDesc, aDeallocator, aBackend, aFlags);
+ break;
+
+ case SurfaceDescriptor::TSurfaceDescriptorMacIOSurface:
+ if (aBackend == LayersBackend::LAYERS_OPENGL ||
+ aBackend == LayersBackend::LAYERS_WR) {
+ result = CreateTextureHostOGL(aDesc, aDeallocator, aBackend, aFlags);
+ break;
+ } else {
+ result = CreateTextureHostBasic(aDesc, aDeallocator, aBackend, aFlags);
+ break;
+ }
+
+#ifdef MOZ_X11
+ case SurfaceDescriptor::TSurfaceDescriptorX11: {
+ if (!aDeallocator->IsSameProcess()) {
+ NS_ERROR(
+ "A client process is trying to peek at our address space using a "
+ "X11Texture!");
+ return nullptr;
+ }
+
+ const SurfaceDescriptorX11& desc = aDesc.get_SurfaceDescriptorX11();
+ result = MakeAndAddRef<X11TextureHost>(aFlags, desc);
+ break;
+ }
+#endif
+
+#ifdef XP_WIN
+ case SurfaceDescriptor::TSurfaceDescriptorD3D10:
+ case SurfaceDescriptor::TSurfaceDescriptorDXGIYCbCr:
+ result = CreateTextureHostD3D11(aDesc, aDeallocator, aBackend, aFlags);
+ break;
+#endif
+ case SurfaceDescriptor::TSurfaceDescriptorRecorded: {
+ const SurfaceDescriptorRecorded& desc =
+ aDesc.get_SurfaceDescriptorRecorded();
+ UniquePtr<SurfaceDescriptor> realDesc =
+ aDeallocator->AsCompositorBridgeParentBase()
+ ->LookupSurfaceDescriptorForClientTexture(desc.textureId());
+ if (!realDesc) {
+ gfxCriticalNote << "Failed to get descriptor for recorded texture.";
+ // Create a dummy to prevent any crashes due to missing IPDL actors.
+ result = CreateDummyBufferTextureHost(aBackend, aFlags);
+ break;
+ }
+
+ result = TextureHost::Create(*realDesc, aReadLock, aDeallocator, aBackend,
+ aFlags, aExternalImageId);
+ return result.forget();
+ }
+ default:
+ MOZ_CRASH("GFX: Unsupported Surface type host");
+ }
+
+ if (!result) {
+ gfxCriticalNote << "TextureHost creation failure type=" << aDesc.type();
+ }
+
+ if (result && WrapWithWebRenderTextureHost(aDeallocator, aBackend, aFlags)) {
+ MOZ_ASSERT(aExternalImageId.isSome());
+ result =
+ new WebRenderTextureHost(aDesc, aFlags, result, aExternalImageId.ref());
+ }
+
+ if (result) {
+ result->DeserializeReadLock(aReadLock, aDeallocator);
+ }
+
+ return result.forget();
+}
+
+already_AddRefed<TextureHost> CreateBackendIndependentTextureHost(
+ const SurfaceDescriptor& aDesc, ISurfaceAllocator* aDeallocator,
+ LayersBackend aBackend, TextureFlags aFlags) {
+ RefPtr<TextureHost> result;
+ switch (aDesc.type()) {
+ case SurfaceDescriptor::TSurfaceDescriptorBuffer: {
+ const SurfaceDescriptorBuffer& bufferDesc =
+ aDesc.get_SurfaceDescriptorBuffer();
+ const MemoryOrShmem& data = bufferDesc.data();
+ switch (data.type()) {
+ case MemoryOrShmem::TShmem: {
+ const ipc::Shmem& shmem = data.get_Shmem();
+ const BufferDescriptor& desc = bufferDesc.desc();
+ if (!shmem.IsReadable()) {
+ // We failed to map the shmem so we can't verify its size. This
+ // should not be a fatal error, so just create the texture with
+ // nothing backing it.
+ result = new ShmemTextureHost(shmem, desc, aDeallocator, aFlags);
+ break;
+ }
+
+ size_t bufSize = shmem.Size<char>();
+ size_t reqSize = SIZE_MAX;
+ switch (desc.type()) {
+ case BufferDescriptor::TYCbCrDescriptor: {
+ const YCbCrDescriptor& ycbcr = desc.get_YCbCrDescriptor();
+ reqSize = ImageDataSerializer::ComputeYCbCrBufferSize(
+ ycbcr.ySize(), ycbcr.yStride(), ycbcr.cbCrSize(),
+ ycbcr.cbCrStride(), ycbcr.yOffset(), ycbcr.cbOffset(),
+ ycbcr.crOffset());
+ break;
+ }
+ case BufferDescriptor::TRGBDescriptor: {
+ const RGBDescriptor& rgb = desc.get_RGBDescriptor();
+ reqSize = ImageDataSerializer::ComputeRGBBufferSize(rgb.size(),
+ rgb.format());
+ break;
+ }
+ default:
+ gfxCriticalError()
+ << "Bad buffer host descriptor " << (int)desc.type();
+ MOZ_CRASH("GFX: Bad descriptor");
+ }
+
+ if (reqSize == 0 || bufSize < reqSize) {
+ NS_ERROR(
+ "A client process gave a shmem too small to fit for its "
+ "descriptor!");
+ return nullptr;
+ }
+
+ result = new ShmemTextureHost(shmem, desc, aDeallocator, aFlags);
+ break;
+ }
+ case MemoryOrShmem::Tuintptr_t: {
+ if (!aDeallocator->IsSameProcess()) {
+ NS_ERROR(
+ "A client process is trying to peek at our address space using "
+ "a MemoryTexture!");
+ return nullptr;
+ }
+
+ result = new MemoryTextureHost(
+ reinterpret_cast<uint8_t*>(data.get_uintptr_t()),
+ bufferDesc.desc(), aFlags);
+ break;
+ }
+ default:
+ gfxCriticalError()
+ << "Failed texture host for backend " << (int)data.type();
+ MOZ_CRASH("GFX: No texture host for backend");
+ }
+ break;
+ }
+ case SurfaceDescriptor::TSurfaceDescriptorGPUVideo: {
+ if (aDesc.get_SurfaceDescriptorGPUVideo().type() ==
+ SurfaceDescriptorGPUVideo::TSurfaceDescriptorPlugin) {
+ MOZ_ASSERT(aDeallocator && aDeallocator->UsesImageBridge());
+ auto ibpBase = static_cast<ImageBridgeParent*>(aDeallocator);
+ result =
+ ibpBase->LookupTextureHost(aDesc.get_SurfaceDescriptorGPUVideo());
+ if (!result) {
+ return nullptr;
+ }
+ MOZ_ASSERT(aFlags == result->GetFlags());
+ break;
+ }
+
+ MOZ_ASSERT(aDesc.get_SurfaceDescriptorGPUVideo().type() ==
+ SurfaceDescriptorGPUVideo::TSurfaceDescriptorRemoteDecoder);
+ result = GPUVideoTextureHost::CreateFromDescriptor(
+ aFlags, aDesc.get_SurfaceDescriptorGPUVideo());
+ break;
+ }
+#ifdef XP_WIN
+ case SurfaceDescriptor::TSurfaceDescriptorDIB: {
+ if (!aDeallocator->IsSameProcess()) {
+ NS_ERROR(
+ "A client process is trying to peek at our address space using a "
+ "DIBTexture!");
+ return nullptr;
+ }
+
+ result = new DIBTextureHost(aFlags, aDesc);
+ break;
+ }
+ case SurfaceDescriptor::TSurfaceDescriptorFileMapping: {
+ result = new TextureHostFileMapping(aFlags, aDesc);
+ break;
+ }
+#endif
+ default: {
+ NS_WARNING("No backend independent TextureHost for this descriptor type");
+ }
+ }
+ return result.forget();
+}
+
+TextureHost::TextureHost(TextureFlags aFlags)
+ : AtomicRefCountedWithFinalize("TextureHost"),
+ mActor(nullptr),
+ mFlags(aFlags),
+ mCompositableCount(0),
+ mFwdTransactionId(0),
+ mReadLocked(false) {}
+
+TextureHost::~TextureHost() {
+ if (mReadLocked) {
+ // If we still have a ReadLock, unlock it. At this point we don't care about
+ // the texture client being written into on the other side since it should
+ // be destroyed by now. But we will hit assertions if we don't ReadUnlock
+ // before destroying the lock itself.
+ ReadUnlock();
+ MaybeNotifyUnlocked();
+ }
+}
+
+void TextureHost::Finalize() {
+ MaybeDestroyRenderTexture();
+
+ if (!(GetFlags() & TextureFlags::DEALLOCATE_CLIENT)) {
+ DeallocateSharedData();
+ DeallocateDeviceData();
+ }
+}
+
+void TextureHost::UnbindTextureSource() {
+ if (mReadLocked) {
+ // This TextureHost is not used anymore. Since most compositor backends are
+ // working asynchronously under the hood a compositor could still be using
+ // this texture, so it is generally best to wait until the end of the next
+ // composition before calling ReadUnlock. We ask the compositor to take care
+ // of that for us.
+ if (mProvider) {
+ mProvider->UnlockAfterComposition(this);
+ } else {
+ // GetCompositor returned null which means no compositor can be using this
+ // texture. We can ReadUnlock right away.
+ ReadUnlock();
+ MaybeNotifyUnlocked();
+ }
+ }
+}
+
+void TextureHost::RecycleTexture(TextureFlags aFlags) {
+ MOZ_ASSERT(GetFlags() & TextureFlags::RECYCLE);
+ MOZ_ASSERT(aFlags & TextureFlags::RECYCLE);
+ mFlags = aFlags;
+}
+
+void TextureHost::NotifyNotUsed() {
+ if (!mActor) {
+ return;
+ }
+
+ // Do not need to call NotifyNotUsed() if TextureHost does not have
+ // TextureFlags::RECYCLE flag nor TextureFlags::WAIT_HOST_USAGE_END flag.
+ if (!(GetFlags() & TextureFlags::RECYCLE) &&
+ !(GetFlags() & TextureFlags::WAIT_HOST_USAGE_END)) {
+ return;
+ }
+
+ // The following cases do not need to defer NotifyNotUsed until next
+ // Composite.
+ // - TextureHost does not have Compositor.
+ // - Compositor is BasicCompositor.
+ // - TextureHost has intermediate buffer.
+ // end of buffer usage.
+ if (!mProvider || HasIntermediateBuffer() ||
+ !mProvider->NotifyNotUsedAfterComposition(this)) {
+ static_cast<TextureParent*>(mActor)->NotifyNotUsed(mFwdTransactionId);
+ return;
+ }
+}
+
+void TextureHost::CallNotifyNotUsed() {
+ if (!mActor) {
+ return;
+ }
+ static_cast<TextureParent*>(mActor)->NotifyNotUsed(mFwdTransactionId);
+}
+
+void TextureHost::MaybeDestroyRenderTexture() {
+ if (mExternalImageId.isNothing()) {
+ // RenderTextureHost was not created
+ return;
+ }
+ // When TextureHost created RenderTextureHost, delete it here.
+ TextureHost::DestroyRenderTexture(mExternalImageId.ref());
+}
+
+void TextureHost::DestroyRenderTexture(
+ const wr::ExternalImageId& aExternalImageId) {
+ wr::RenderThread::Get()->UnregisterExternalImage(
+ wr::AsUint64(aExternalImageId));
+}
+
+void TextureHost::EnsureRenderTexture(
+ const wr::MaybeExternalImageId& aExternalImageId) {
+ if (aExternalImageId.isNothing()) {
+ // TextureHost is wrapped by GPUVideoTextureHost.
+ if (mExternalImageId.isSome()) {
+ // RenderTextureHost was already created.
+ return;
+ }
+ mExternalImageId =
+ Some(AsyncImagePipelineManager::GetNextExternalImageId());
+ } else {
+ // TextureHost is wrapped by WebRenderTextureHost.
+ if (aExternalImageId == mExternalImageId) {
+ // The texture has already been created.
+ return;
+ }
+ MOZ_ASSERT(mExternalImageId.isNothing());
+ mExternalImageId = aExternalImageId;
+ }
+ CreateRenderTexture(mExternalImageId.ref());
+}
+
+void TextureHost::PrintInfo(std::stringstream& aStream, const char* aPrefix) {
+ aStream << aPrefix;
+ aStream << nsPrintfCString("%s (0x%p)", Name(), this).get();
+ // Note: the TextureHost needs to be locked before it is safe to call
+ // GetSize() and GetFormat() on it.
+ if (Lock()) {
+ aStream << " [size=" << GetSize() << "]"
+ << " [format=" << GetFormat() << "]";
+ Unlock();
+ }
+ aStream << " [flags=" << mFlags << "]";
+#ifdef MOZ_DUMP_PAINTING
+ if (StaticPrefs::layers_dump_texture()) {
+ nsAutoCString pfx(aPrefix);
+ pfx += " ";
+
+ aStream << "\n" << pfx.get() << "Surface: ";
+ RefPtr<gfx::DataSourceSurface> dSurf = GetAsSurface();
+ if (dSurf) {
+ aStream << gfxUtils::GetAsLZ4Base64Str(dSurf).get();
+ }
+ }
+#endif
+}
+
+void TextureHost::Updated(const nsIntRegion* aRegion) {
+ LayerScope::ContentChanged(this);
+ UpdatedInternal(aRegion);
+}
+
+TextureSource::TextureSource() : mCompositableCount(0) {}
+
+TextureSource::~TextureSource() = default;
+BufferTextureHost::BufferTextureHost(const BufferDescriptor& aDesc,
+ TextureFlags aFlags)
+ : TextureHost(aFlags),
+ mUpdateSerial(1),
+ mLocked(false),
+ mNeedsFullUpdate(false) {
+ mDescriptor = aDesc;
+ switch (mDescriptor.type()) {
+ case BufferDescriptor::TYCbCrDescriptor: {
+ const YCbCrDescriptor& ycbcr = mDescriptor.get_YCbCrDescriptor();
+ mSize = ycbcr.display().Size();
+ mFormat = gfx::SurfaceFormat::YUV;
+ mHasIntermediateBuffer = ycbcr.hasIntermediateBuffer();
+ break;
+ }
+ case BufferDescriptor::TRGBDescriptor: {
+ const RGBDescriptor& rgb = mDescriptor.get_RGBDescriptor();
+ mSize = rgb.size();
+ mFormat = rgb.format();
+ mHasIntermediateBuffer = rgb.hasIntermediateBuffer();
+ break;
+ }
+ default:
+ gfxCriticalError() << "Bad buffer host descriptor "
+ << (int)mDescriptor.type();
+ MOZ_CRASH("GFX: Bad descriptor");
+ }
+ if (aFlags & TextureFlags::COMPONENT_ALPHA) {
+ // One texture of a component alpha texture pair will start out all white.
+ // This hack allows us to easily make sure that white will be uploaded.
+ // See bug 1138934
+ mNeedsFullUpdate = true;
+ }
+
+#ifdef XP_MACOSX
+ const int kMinSize = 1024;
+ const int kMaxSize = 4096;
+ mUseExternalTextures =
+ kMaxSize >= mSize.width && mSize.width >= kMinSize &&
+ kMaxSize >= mSize.height && mSize.height >= kMinSize &&
+ StaticPrefs::gfx_webrender_enable_client_storage_AtStartup();
+#else
+ mUseExternalTextures = false;
+#endif
+}
+
+BufferTextureHost::~BufferTextureHost() = default;
+
+void BufferTextureHost::UpdatedInternal(const nsIntRegion* aRegion) {
+ ++mUpdateSerial;
+ // If the last frame wasn't uploaded yet, and we -don't- have a partial
+ // update, we still need to update the full surface.
+ if (aRegion && !mNeedsFullUpdate) {
+ mMaybeUpdatedRegion.OrWith(*aRegion);
+ } else {
+ mNeedsFullUpdate = true;
+ }
+ if (GetFlags() & TextureFlags::IMMEDIATE_UPLOAD) {
+ DebugOnly<bool> result =
+ MaybeUpload(!mNeedsFullUpdate ? &mMaybeUpdatedRegion : nullptr);
+ NS_WARNING_ASSERTION(result, "Failed to upload a texture");
+ }
+}
+
+void BufferTextureHost::SetTextureSourceProvider(
+ TextureSourceProvider* aProvider) {
+ if (mProvider == aProvider) {
+ return;
+ }
+ if (mFirstSource && mFirstSource->IsOwnedBy(this)) {
+ mFirstSource->SetOwner(nullptr);
+ }
+ if (mFirstSource) {
+ mFirstSource = nullptr;
+ mNeedsFullUpdate = true;
+ }
+ mProvider = aProvider;
+}
+
+void BufferTextureHost::DeallocateDeviceData() {
+ if (mFirstSource && mFirstSource->NumCompositableRefs() > 0) {
+ // WrappingTextureSourceYCbCrBasic wraps YUV format BufferTextureHost.
+ // When BufferTextureHost is destroyed, data of
+ // WrappingTextureSourceYCbCrBasic becomes invalid.
+ if (mFirstSource->AsWrappingTextureSourceYCbCrBasic() &&
+ mFirstSource->IsOwnedBy(this)) {
+ mFirstSource->SetOwner(nullptr);
+ mFirstSource->DeallocateDeviceData();
+ }
+ return;
+ }
+
+ if (!mFirstSource || !mFirstSource->IsOwnedBy(this)) {
+ mFirstSource = nullptr;
+ return;
+ }
+
+ mFirstSource->SetOwner(nullptr);
+
+ RefPtr<TextureSource> it = mFirstSource;
+ while (it) {
+ it->DeallocateDeviceData();
+ it = it->GetNextSibling();
+ }
+}
+
+bool BufferTextureHost::Lock() {
+ MOZ_ASSERT(!mLocked);
+ if (!UploadIfNeeded()) {
+ return false;
+ }
+ mLocked = !!mFirstSource;
+ return mLocked;
+}
+
+void BufferTextureHost::Unlock() {
+ MOZ_ASSERT(mLocked);
+ mLocked = false;
+}
+
+void BufferTextureHost::CreateRenderTexture(
+ const wr::ExternalImageId& aExternalImageId) {
+ RefPtr<wr::RenderTextureHost> texture;
+
+ if (gfx::gfxVars::UseSoftwareWebRender()) {
+ texture =
+ new wr::RenderBufferTextureHostSWGL(GetBuffer(), GetBufferDescriptor());
+ } else if (UseExternalTextures()) {
+ texture =
+ new wr::RenderExternalTextureHost(GetBuffer(), GetBufferDescriptor());
+ } else {
+ texture =
+ new wr::RenderBufferTextureHost(GetBuffer(), GetBufferDescriptor());
+ }
+
+ wr::RenderThread::Get()->RegisterExternalImage(wr::AsUint64(aExternalImageId),
+ texture.forget());
+}
+
+uint32_t BufferTextureHost::NumSubTextures() {
+ if (GetFormat() == gfx::SurfaceFormat::YUV) {
+ return 3;
+ }
+
+ return 1;
+}
+
+void BufferTextureHost::PushResourceUpdates(
+ wr::TransactionBuilder& aResources, ResourceUpdateOp aOp,
+ const Range<wr::ImageKey>& aImageKeys, const wr::ExternalImageId& aExtID) {
+ auto method = aOp == TextureHost::ADD_IMAGE
+ ? &wr::TransactionBuilder::AddExternalImage
+ : &wr::TransactionBuilder::UpdateExternalImage;
+
+ auto imageType = UseExternalTextures() || gfx::gfxVars::UseSoftwareWebRender()
+ ? wr::ExternalImageType::TextureHandle(
+ wr::ImageBufferKind::TextureRect)
+ : wr::ExternalImageType::Buffer();
+
+ if (GetFormat() != gfx::SurfaceFormat::YUV) {
+ MOZ_ASSERT(aImageKeys.length() == 1);
+
+ wr::ImageDescriptor descriptor(
+ GetSize(),
+ ImageDataSerializer::ComputeRGBStride(GetFormat(), GetSize().width),
+ GetFormat());
+ (aResources.*method)(aImageKeys[0], descriptor, aExtID, imageType, 0);
+ } else {
+ MOZ_ASSERT(aImageKeys.length() == 3);
+
+ const layers::YCbCrDescriptor& desc = mDescriptor.get_YCbCrDescriptor();
+ wr::ImageDescriptor yDescriptor(
+ desc.ySize(), desc.yStride(),
+ SurfaceFormatForColorDepth(desc.colorDepth()));
+ wr::ImageDescriptor cbcrDescriptor(
+ desc.cbCrSize(), desc.cbCrStride(),
+ SurfaceFormatForColorDepth(desc.colorDepth()));
+ (aResources.*method)(aImageKeys[0], yDescriptor, aExtID, imageType, 0);
+ (aResources.*method)(aImageKeys[1], cbcrDescriptor, aExtID, imageType, 1);
+ (aResources.*method)(aImageKeys[2], cbcrDescriptor, aExtID, imageType, 2);
+ }
+}
+
+void BufferTextureHost::PushDisplayItems(wr::DisplayListBuilder& aBuilder,
+ const wr::LayoutRect& aBounds,
+ const wr::LayoutRect& aClip,
+ wr::ImageRendering aFilter,
+ const Range<wr::ImageKey>& aImageKeys,
+ PushDisplayItemFlagSet aFlags) {
+ // SWGL should always try to bypass shaders and composite directly.
+ bool preferCompositorSurface =
+ aFlags.contains(PushDisplayItemFlag::PREFER_COMPOSITOR_SURFACE);
+ bool useExternalSurface =
+ aFlags.contains(PushDisplayItemFlag::SUPPORTS_EXTERNAL_BUFFER_TEXTURES);
+ if (GetFormat() != gfx::SurfaceFormat::YUV) {
+ MOZ_ASSERT(aImageKeys.length() == 1);
+ aBuilder.PushImage(aBounds, aClip, true, aFilter, aImageKeys[0],
+ !(mFlags & TextureFlags::NON_PREMULTIPLIED),
+ wr::ColorF{1.0f, 1.0f, 1.0f, 1.0f},
+ preferCompositorSurface, useExternalSurface);
+ } else {
+ MOZ_ASSERT(aImageKeys.length() == 3);
+ const YCbCrDescriptor& desc = mDescriptor.get_YCbCrDescriptor();
+ aBuilder.PushYCbCrPlanarImage(
+ aBounds, aClip, true, aImageKeys[0], aImageKeys[1], aImageKeys[2],
+ wr::ToWrColorDepth(desc.colorDepth()),
+ wr::ToWrYuvColorSpace(desc.yUVColorSpace()),
+ wr::ToWrColorRange(desc.colorRange()), aFilter, preferCompositorSurface,
+ useExternalSurface);
+ }
+}
+
+void TextureHost::DeserializeReadLock(const ReadLockDescriptor& aDesc,
+ ISurfaceAllocator* aAllocator) {
+ if (mReadLock) {
+ return;
+ }
+
+ mReadLock = TextureReadLock::Deserialize(aDesc, aAllocator);
+}
+
+void TextureHost::SetReadLocked() {
+ if (!mReadLock) {
+ return;
+ }
+ // If mReadLocked is true it means we haven't read unlocked yet and the
+ // content side should not have been able to write into this texture and read
+ // lock again!
+ MOZ_ASSERT(!mReadLocked);
+ mReadLocked = true;
+ if (mProvider) {
+ mProvider->MaybeUnlockBeforeNextComposition(this);
+ }
+}
+
+void TextureHost::ReadUnlock() {
+ if (mReadLock && mReadLocked) {
+ mReadLock->ReadUnlock();
+ mReadLocked = false;
+ }
+}
+
+bool TextureHost::NeedsYFlip() const {
+ return bool(mFlags & TextureFlags::ORIGIN_BOTTOM_LEFT);
+}
+
+bool BufferTextureHost::EnsureWrappingTextureSource() {
+ MOZ_ASSERT(!mHasIntermediateBuffer);
+
+ if (mFirstSource && mFirstSource->IsOwnedBy(this)) {
+ return true;
+ }
+ // We don't own it, apparently.
+ if (mFirstSource) {
+ mNeedsFullUpdate = true;
+ mFirstSource = nullptr;
+ }
+
+ if (!mProvider) {
+ return false;
+ }
+
+ if (mFormat == gfx::SurfaceFormat::YUV) {
+ mFirstSource = mProvider->CreateDataTextureSourceAroundYCbCr(this);
+ } else {
+ uint8_t* data = GetBuffer();
+ if (!data) {
+ return false;
+ }
+ RefPtr<gfx::DataSourceSurface> surf =
+ gfx::Factory::CreateWrappingDataSourceSurface(
+ data, ImageDataSerializer::ComputeRGBStride(mFormat, mSize.width),
+ mSize, mFormat);
+ if (!surf) {
+ return false;
+ }
+ mFirstSource = mProvider->CreateDataTextureSourceAround(surf);
+ }
+
+ if (!mFirstSource) {
+ // BasicCompositor::CreateDataTextureSourceAround never returns null
+ // and we don't expect to take this branch if we are using another backend.
+ // Returning false is fine but if we get into this situation it probably
+ // means something fishy is going on, like a texture being used with
+ // several compositor backends.
+ NS_WARNING("Failed to use a BufferTextureHost without intermediate buffer");
+ return false;
+ }
+
+ mFirstSource->SetUpdateSerial(mUpdateSerial);
+ mFirstSource->SetOwner(this);
+
+ return true;
+}
+
+static bool IsCompatibleTextureSource(TextureSource* aTexture,
+ const BufferDescriptor& aDescriptor,
+ TextureSourceProvider* aProvider) {
+ if (!aProvider) {
+ return false;
+ }
+
+ switch (aDescriptor.type()) {
+ case BufferDescriptor::TYCbCrDescriptor: {
+ const YCbCrDescriptor& ycbcr = aDescriptor.get_YCbCrDescriptor();
+
+ if (!aProvider->SupportsEffect(EffectTypes::YCBCR)) {
+ return aTexture->GetFormat() == gfx::SurfaceFormat::B8G8R8X8 &&
+ aTexture->GetSize() == ycbcr.ySize();
+ }
+
+ if (aTexture->GetFormat() != gfx::SurfaceFormat::A8 ||
+ aTexture->GetSize() != ycbcr.ySize()) {
+ return false;
+ }
+
+ auto cbTexture = aTexture->GetSubSource(1);
+ if (!cbTexture || cbTexture->GetFormat() != gfx::SurfaceFormat::A8 ||
+ cbTexture->GetSize() != ycbcr.cbCrSize()) {
+ return false;
+ }
+
+ auto crTexture = aTexture->GetSubSource(2);
+ if (!crTexture || crTexture->GetFormat() != gfx::SurfaceFormat::A8 ||
+ crTexture->GetSize() != ycbcr.cbCrSize()) {
+ return false;
+ }
+
+ return true;
+ }
+ case BufferDescriptor::TRGBDescriptor: {
+ const RGBDescriptor& rgb = aDescriptor.get_RGBDescriptor();
+ return aTexture->GetFormat() == rgb.format() &&
+ aTexture->GetSize() == rgb.size();
+ }
+ default: {
+ return false;
+ }
+ }
+}
+
+void BufferTextureHost::PrepareTextureSource(
+ CompositableTextureSourceRef& aTexture) {
+ // Reuse WrappingTextureSourceYCbCrBasic to reduce memory consumption.
+ if (mFormat == gfx::SurfaceFormat::YUV && !mHasIntermediateBuffer &&
+ aTexture.get() && aTexture->AsWrappingTextureSourceYCbCrBasic() &&
+ aTexture->NumCompositableRefs() <= 1 &&
+ aTexture->GetSize() == GetSize()) {
+ aTexture->AsSourceBasic()->SetBufferTextureHost(this);
+ aTexture->AsDataTextureSource()->SetOwner(this);
+ mFirstSource = aTexture->AsDataTextureSource();
+ mNeedsFullUpdate = true;
+ }
+
+ if (!mHasIntermediateBuffer) {
+ EnsureWrappingTextureSource();
+ }
+
+ if (mFirstSource && mFirstSource->IsOwnedBy(this)) {
+ // We are already attached to a TextureSource, nothing to do except tell
+ // the compositable to use it.
+ aTexture = mFirstSource.get();
+ return;
+ }
+
+ // We don't own it, apparently.
+ if (mFirstSource) {
+ mNeedsFullUpdate = true;
+ mFirstSource = nullptr;
+ }
+
+ DataTextureSource* texture =
+ aTexture.get() ? aTexture->AsDataTextureSource() : nullptr;
+
+ bool compatibleFormats =
+ texture && IsCompatibleTextureSource(texture, mDescriptor, mProvider);
+
+ bool shouldCreateTexture = !compatibleFormats ||
+ texture->NumCompositableRefs() > 1 ||
+ texture->HasOwner();
+
+ if (!shouldCreateTexture) {
+ mFirstSource = texture;
+ mFirstSource->SetOwner(this);
+ mNeedsFullUpdate = true;
+
+ // It's possible that texture belonged to a different compositor,
+ // so make sure we update it (and all of its siblings) to the
+ // current one.
+ RefPtr<TextureSource> it = mFirstSource;
+ while (it) {
+ it->SetTextureSourceProvider(mProvider);
+ it = it->GetNextSibling();
+ }
+ }
+}
+
+bool BufferTextureHost::BindTextureSource(
+ CompositableTextureSourceRef& aTexture) {
+ MOZ_ASSERT(mLocked);
+ MOZ_ASSERT(mFirstSource);
+ aTexture = mFirstSource;
+ return !!aTexture;
+}
+
+bool BufferTextureHost::AcquireTextureSource(
+ CompositableTextureSourceRef& aTexture) {
+ if (!UploadIfNeeded()) {
+ return false;
+ }
+ aTexture = mFirstSource;
+ return !!mFirstSource;
+}
+
+void BufferTextureHost::ReadUnlock() {
+ if (mFirstSource) {
+ mFirstSource->Sync(true);
+ }
+
+ TextureHost::ReadUnlock();
+}
+
+void BufferTextureHost::MaybeNotifyUnlocked() {
+#ifdef XP_DARWIN
+ auto actor = GetIPDLActor();
+ if (actor) {
+ AutoTArray<uint64_t, 1> serials;
+ serials.AppendElement(TextureHost::GetTextureSerial(actor));
+ TextureSync::SetTexturesUnlocked(actor->OtherPid(), serials);
+ }
+#endif
+}
+
+void BufferTextureHost::UnbindTextureSource() {
+ if (mFirstSource && mFirstSource->IsOwnedBy(this)) {
+ mFirstSource->Unbind();
+ }
+
+ // This texture is not used by any layer anymore.
+ // If the texture doesn't have an intermediate buffer, it means we are
+ // compositing synchronously on the CPU, so we don't need to wait until
+ // the end of the next composition to ReadUnlock (which other textures do
+ // by default).
+ // If the texture has an intermediate buffer we don't care either because
+ // texture uploads are also performed synchronously for BufferTextureHost.
+ ReadUnlock();
+ MaybeNotifyUnlocked();
+}
+
+gfx::SurfaceFormat BufferTextureHost::GetFormat() const {
+ // mFormat is the format of the data that we share with the content process.
+ // GetFormat, on the other hand, expects the format that we present to the
+ // Compositor (it is used to choose the effect type).
+ // if the compositor does not support YCbCr effects, we give it a RGBX texture
+ // instead (see BufferTextureHost::Upload)
+ if (mFormat == gfx::SurfaceFormat::YUV && mProvider &&
+ !mProvider->SupportsEffect(EffectTypes::YCBCR)) {
+ return gfx::SurfaceFormat::R8G8B8X8;
+ }
+ return mFormat;
+}
+
+gfx::YUVColorSpace BufferTextureHost::GetYUVColorSpace() const {
+ if (mFormat == gfx::SurfaceFormat::YUV) {
+ const YCbCrDescriptor& desc = mDescriptor.get_YCbCrDescriptor();
+ return desc.yUVColorSpace();
+ }
+ return gfx::YUVColorSpace::UNKNOWN;
+}
+
+gfx::ColorDepth BufferTextureHost::GetColorDepth() const {
+ if (mFormat == gfx::SurfaceFormat::YUV) {
+ const YCbCrDescriptor& desc = mDescriptor.get_YCbCrDescriptor();
+ return desc.colorDepth();
+ }
+ return gfx::ColorDepth::COLOR_8;
+}
+
+gfx::ColorRange BufferTextureHost::GetColorRange() const {
+ if (mFormat == gfx::SurfaceFormat::YUV) {
+ const YCbCrDescriptor& desc = mDescriptor.get_YCbCrDescriptor();
+ return desc.colorRange();
+ }
+ return TextureHost::GetColorRange();
+}
+
+bool BufferTextureHost::UploadIfNeeded() {
+ return MaybeUpload(!mNeedsFullUpdate ? &mMaybeUpdatedRegion : nullptr);
+}
+
+bool BufferTextureHost::MaybeUpload(nsIntRegion* aRegion) {
+ auto serial = mFirstSource ? mFirstSource->GetUpdateSerial() : 0;
+
+ if (serial == mUpdateSerial) {
+ return true;
+ }
+
+ if (serial == 0) {
+ // 0 means the source has no valid content
+ aRegion = nullptr;
+ }
+
+ if (!Upload(aRegion)) {
+ return false;
+ }
+
+ if (mHasIntermediateBuffer) {
+ // We just did the texture upload, the content side can now freely write
+ // into the shared buffer.
+ ReadUnlock();
+ MaybeNotifyUnlocked();
+ }
+
+ // We no longer have an invalid region.
+ mNeedsFullUpdate = false;
+ mMaybeUpdatedRegion.SetEmpty();
+
+ // If upload returns true we know mFirstSource is not null
+ mFirstSource->SetUpdateSerial(mUpdateSerial);
+ return true;
+}
+
+bool BufferTextureHost::Upload(nsIntRegion* aRegion) {
+ uint8_t* buf = GetBuffer();
+ if (!buf) {
+ // We don't have a buffer; a possible cause is that the IPDL actor
+ // is already dead. This inevitably happens as IPDL actors can die
+ // at any time, so we want to silently return in this case.
+ // another possible cause is that IPDL failed to map the shmem when
+ // deserializing it.
+ return false;
+ }
+ if (!mProvider) {
+ // This can happen if we send textures to a compositable that isn't yet
+ // attached to a layer.
+ return false;
+ }
+ if (!mHasIntermediateBuffer && EnsureWrappingTextureSource()) {
+ if (!mFirstSource || !mFirstSource->IsDirectMap()) {
+ return true;
+ }
+ }
+
+ if (mFormat == gfx::SurfaceFormat::UNKNOWN) {
+ NS_WARNING("BufferTextureHost: unsupported format!");
+ return false;
+ } else if (mFormat == gfx::SurfaceFormat::YUV) {
+ const YCbCrDescriptor& desc = mDescriptor.get_YCbCrDescriptor();
+
+ if (!mProvider->SupportsEffect(EffectTypes::YCBCR)) {
+ RefPtr<gfx::DataSourceSurface> surf =
+ ImageDataSerializer::DataSourceSurfaceFromYCbCrDescriptor(
+ buf, mDescriptor.get_YCbCrDescriptor());
+ if (NS_WARN_IF(!surf)) {
+ return false;
+ }
+ if (!mFirstSource) {
+ mFirstSource = mProvider->CreateDataTextureSource(
+ mFlags | TextureFlags::RGB_FROM_YCBCR);
+ mFirstSource->SetOwner(this);
+ }
+ return mFirstSource->Update(surf, aRegion);
+ }
+
+ RefPtr<DataTextureSource> srcY;
+ RefPtr<DataTextureSource> srcU;
+ RefPtr<DataTextureSource> srcV;
+ if (!mFirstSource) {
+ // We don't support BigImages for YCbCr compositing.
+ srcY = mProvider->CreateDataTextureSource(
+ mFlags | TextureFlags::DISALLOW_BIGIMAGE);
+ srcU = mProvider->CreateDataTextureSource(
+ mFlags | TextureFlags::DISALLOW_BIGIMAGE);
+ srcV = mProvider->CreateDataTextureSource(
+ mFlags | TextureFlags::DISALLOW_BIGIMAGE);
+ mFirstSource = srcY;
+ mFirstSource->SetOwner(this);
+ srcY->SetNextSibling(srcU);
+ srcU->SetNextSibling(srcV);
+ } else {
+ // mFormat never changes so if this was created as a YCbCr host and
+ // already contains a source it should already have 3 sources.
+ // BufferTextureHost only uses DataTextureSources so it is safe to assume
+ // all 3 sources are DataTextureSource.
+ MOZ_ASSERT(mFirstSource->GetNextSibling());
+ MOZ_ASSERT(mFirstSource->GetNextSibling()->GetNextSibling());
+ srcY = mFirstSource;
+ srcU = mFirstSource->GetNextSibling()->AsDataTextureSource();
+ srcV = mFirstSource->GetNextSibling()
+ ->GetNextSibling()
+ ->AsDataTextureSource();
+ }
+
+ RefPtr<gfx::DataSourceSurface> tempY =
+ gfx::Factory::CreateWrappingDataSourceSurface(
+ ImageDataSerializer::GetYChannel(buf, desc), desc.yStride(),
+ desc.ySize(), SurfaceFormatForColorDepth(desc.colorDepth()));
+ RefPtr<gfx::DataSourceSurface> tempCb =
+ gfx::Factory::CreateWrappingDataSourceSurface(
+ ImageDataSerializer::GetCbChannel(buf, desc), desc.cbCrStride(),
+ desc.cbCrSize(), SurfaceFormatForColorDepth(desc.colorDepth()));
+ RefPtr<gfx::DataSourceSurface> tempCr =
+ gfx::Factory::CreateWrappingDataSourceSurface(
+ ImageDataSerializer::GetCrChannel(buf, desc), desc.cbCrStride(),
+ desc.cbCrSize(), SurfaceFormatForColorDepth(desc.colorDepth()));
+ // We don't support partial updates for Y U V textures
+ NS_ASSERTION(!aRegion, "Unsupported partial updates for YCbCr textures");
+ if (!tempY || !tempCb || !tempCr || !srcY->Update(tempY) ||
+ !srcU->Update(tempCb) || !srcV->Update(tempCr)) {
+ NS_WARNING("failed to update the DataTextureSource");
+ return false;
+ }
+ } else {
+ // non-YCbCr case
+ nsIntRegion* regionToUpdate = aRegion;
+ if (!mFirstSource) {
+ mFirstSource = mProvider->CreateDataTextureSource(mFlags);
+ mFirstSource->SetOwner(this);
+ if (mFlags & TextureFlags::COMPONENT_ALPHA) {
+ // Update the full region the first time for component alpha textures.
+ regionToUpdate = nullptr;
+ }
+ }
+
+ RefPtr<gfx::DataSourceSurface> surf =
+ gfx::Factory::CreateWrappingDataSourceSurface(
+ GetBuffer(),
+ ImageDataSerializer::ComputeRGBStride(mFormat, mSize.width), mSize,
+ mFormat);
+ if (!surf) {
+ return false;
+ }
+
+ if (!mFirstSource->Update(surf.get(), regionToUpdate)) {
+ NS_WARNING("failed to update the DataTextureSource");
+ return false;
+ }
+ }
+ MOZ_ASSERT(mFirstSource);
+ return true;
+}
+
+already_AddRefed<gfx::DataSourceSurface> BufferTextureHost::GetAsSurface() {
+ RefPtr<gfx::DataSourceSurface> result;
+ if (mFormat == gfx::SurfaceFormat::UNKNOWN) {
+ NS_WARNING("BufferTextureHost: unsupported format!");
+ return nullptr;
+ } else if (mFormat == gfx::SurfaceFormat::YUV) {
+ result = ImageDataSerializer::DataSourceSurfaceFromYCbCrDescriptor(
+ GetBuffer(), mDescriptor.get_YCbCrDescriptor());
+ if (NS_WARN_IF(!result)) {
+ return nullptr;
+ }
+ } else {
+ result = gfx::Factory::CreateWrappingDataSourceSurface(
+ GetBuffer(),
+ ImageDataSerializer::GetRGBStride(mDescriptor.get_RGBDescriptor()),
+ mSize, mFormat);
+ }
+ return result.forget();
+}
+
+ShmemTextureHost::ShmemTextureHost(const ipc::Shmem& aShmem,
+ const BufferDescriptor& aDesc,
+ ISurfaceAllocator* aDeallocator,
+ TextureFlags aFlags)
+ : BufferTextureHost(aDesc, aFlags), mDeallocator(aDeallocator) {
+ if (aShmem.IsReadable()) {
+ mShmem = MakeUnique<ipc::Shmem>(aShmem);
+ } else {
+ // This can happen if we failed to map the shmem on this process, perhaps
+ // because it was big and we didn't have enough contiguous address space
+ // available, even though we did on the child process.
+ // As a result this texture will be in an invalid state and Lock will
+ // always fail.
+
+ gfxCriticalNote << "Failed to create a valid ShmemTextureHost";
+ }
+
+ MOZ_COUNT_CTOR(ShmemTextureHost);
+}
+
+ShmemTextureHost::~ShmemTextureHost() {
+ MOZ_ASSERT(!mShmem || (mFlags & TextureFlags::DEALLOCATE_CLIENT),
+ "Leaking our buffer");
+ DeallocateDeviceData();
+ MOZ_COUNT_DTOR(ShmemTextureHost);
+}
+
+void ShmemTextureHost::DeallocateSharedData() {
+ if (mShmem) {
+ MOZ_ASSERT(mDeallocator,
+ "Shared memory would leak without a ISurfaceAllocator");
+ mDeallocator->AsShmemAllocator()->DeallocShmem(*mShmem);
+ mShmem = nullptr;
+ }
+}
+
+void ShmemTextureHost::ForgetSharedData() {
+ if (mShmem) {
+ mShmem = nullptr;
+ }
+}
+
+void ShmemTextureHost::OnShutdown() { mShmem = nullptr; }
+
+uint8_t* ShmemTextureHost::GetBuffer() {
+ return mShmem ? mShmem->get<uint8_t>() : nullptr;
+}
+
+size_t ShmemTextureHost::GetBufferSize() {
+ return mShmem ? mShmem->Size<uint8_t>() : 0;
+}
+
+MemoryTextureHost::MemoryTextureHost(uint8_t* aBuffer,
+ const BufferDescriptor& aDesc,
+ TextureFlags aFlags)
+ : BufferTextureHost(aDesc, aFlags), mBuffer(aBuffer) {
+ MOZ_COUNT_CTOR(MemoryTextureHost);
+}
+
+MemoryTextureHost::~MemoryTextureHost() {
+ MOZ_ASSERT(!mBuffer || (mFlags & TextureFlags::DEALLOCATE_CLIENT),
+ "Leaking our buffer");
+ DeallocateDeviceData();
+ MOZ_COUNT_DTOR(MemoryTextureHost);
+}
+
+void MemoryTextureHost::DeallocateSharedData() {
+ if (mBuffer) {
+ GfxMemoryImageReporter::WillFree(mBuffer);
+ }
+ delete[] mBuffer;
+ mBuffer = nullptr;
+}
+
+void MemoryTextureHost::ForgetSharedData() { mBuffer = nullptr; }
+
+uint8_t* MemoryTextureHost::GetBuffer() { return mBuffer; }
+
+size_t MemoryTextureHost::GetBufferSize() {
+ // MemoryTextureHost just trusts that the buffer size is large enough to read
+ // anything we need to. That's because MemoryTextureHost has to trust the
+ // buffer pointer anyway, so the security model here is just that
+ // MemoryTexture's are restricted to same-process clients.
+ return std::numeric_limits<size_t>::max();
+}
+
+TextureParent::TextureParent(HostIPCAllocator* aSurfaceAllocator,
+ uint64_t aSerial,
+ const wr::MaybeExternalImageId& aExternalImageId)
+ : mSurfaceAllocator(aSurfaceAllocator),
+ mSerial(aSerial),
+ mExternalImageId(aExternalImageId) {
+ MOZ_COUNT_CTOR(TextureParent);
+}
+
+TextureParent::~TextureParent() { MOZ_COUNT_DTOR(TextureParent); }
+
+void TextureParent::NotifyNotUsed(uint64_t aTransactionId) {
+ if (!mTextureHost) {
+ return;
+ }
+ mSurfaceAllocator->NotifyNotUsed(this, aTransactionId);
+}
+
+bool TextureParent::Init(const SurfaceDescriptor& aSharedData,
+ const ReadLockDescriptor& aReadLock,
+ const LayersBackend& aBackend,
+ const TextureFlags& aFlags) {
+ mTextureHost = TextureHost::Create(aSharedData, aReadLock, mSurfaceAllocator,
+ aBackend, aFlags, mExternalImageId);
+ if (mTextureHost) {
+ mTextureHost->mActor = this;
+ }
+
+ return !!mTextureHost;
+}
+
+void TextureParent::Destroy() {
+ if (!mTextureHost) {
+ return;
+ }
+
+ if (mTextureHost->mReadLocked) {
+ // ReadUnlock here to make sure the ReadLock's shmem does not outlive the
+ // protocol that created it.
+ mTextureHost->ReadUnlock();
+ mTextureHost->MaybeNotifyUnlocked();
+ }
+
+ if (mTextureHost->GetFlags() & TextureFlags::DEALLOCATE_CLIENT) {
+ mTextureHost->ForgetSharedData();
+ }
+
+ mTextureHost->mActor = nullptr;
+ mTextureHost = nullptr;
+}
+
+void TextureHost::ReceivedDestroy(PTextureParent* aActor) {
+ static_cast<TextureParent*>(aActor)->RecvDestroy();
+}
+
+mozilla::ipc::IPCResult TextureParent::RecvRecycleTexture(
+ const TextureFlags& aTextureFlags) {
+ if (!mTextureHost) {
+ return IPC_OK();
+ }
+ mTextureHost->RecycleTexture(aTextureFlags);
+ return IPC_OK();
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+} // namespace layers
+} // namespace mozilla