summaryrefslogtreecommitdiffstats
path: root/gfx/layers/composite/TextureHost.cpp
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 19:33:14 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 19:33:14 +0000
commit36d22d82aa202bb199967e9512281e9a53db42c9 (patch)
tree105e8c98ddea1c1e4784a60a5a6410fa416be2de /gfx/layers/composite/TextureHost.cpp
parentInitial commit. (diff)
downloadfirefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.tar.xz
firefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.zip
Adding upstream version 115.7.0esr.upstream/115.7.0esr
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'gfx/layers/composite/TextureHost.cpp')
-rw-r--r--gfx/layers/composite/TextureHost.cpp848
1 files changed, 848 insertions, 0 deletions
diff --git a/gfx/layers/composite/TextureHost.cpp b/gfx/layers/composite/TextureHost.cpp
new file mode 100644
index 0000000000..bb11b3463b
--- /dev/null
+++ b/gfx/layers/composite/TextureHost.cpp
@@ -0,0 +1,848 @@
+/* -*- 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 "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/RemoteTextureMap.h"
+#include "mozilla/layers/TextureHostOGL.h" // for TextureHostOGL
+#include "mozilla/layers/ImageDataSerializer.h"
+#include "mozilla/layers/TextureClient.h"
+#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/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 XP_MACOSX
+# include "../opengl/MacIOSurfaceTextureHostOGL.h"
+#endif
+
+#ifdef XP_WIN
+# include "../d3d11/CompositorD3D11.h"
+# include "mozilla/layers/TextureD3D11.h"
+# ifdef MOZ_WMF_MEDIA_ENGINE
+# include "mozilla/layers/DcompSurfaceImage.h"
+# endif
+#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,
+ const dom::ContentParentId& aContentId, uint64_t aSerial,
+ const wr::MaybeExternalImageId& aExternalImageId);
+
+ virtual ~TextureParent();
+
+ bool Init(const SurfaceDescriptor& aSharedData,
+ 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;
+
+ const dom::ContentParentId& GetContentId() const { return mContentId; }
+
+ uint64_t GetSerial() const { return mSerial; }
+
+ HostIPCAllocator* mSurfaceAllocator;
+ RefPtr<TextureHost> mTextureHost;
+ dom::ContentParentId mContentId;
+ // mSerial is unique in TextureClient's process.
+ const uint64_t mSerial;
+ wr::MaybeExternalImageId mExternalImageId;
+};
+
+static bool WrapWithWebRenderTextureHost(ISurfaceAllocator* aDeallocator,
+ LayersBackend aBackend,
+ TextureFlags aFlags) {
+ if (!aDeallocator) {
+ return false;
+ }
+ if ((aFlags & TextureFlags::SNAPSHOT) ||
+ (!aDeallocator->UsesImageBridge() &&
+ !aDeallocator->AsCompositorBridgeParentBase())) {
+ return false;
+ }
+ return true;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+PTextureParent* TextureHost::CreateIPDLActor(
+ HostIPCAllocator* aAllocator, const SurfaceDescriptor& aSharedData,
+ ReadLockDescriptor&& aReadLock, LayersBackend aLayersBackend,
+ TextureFlags aFlags, const dom::ContentParentId& aContentId,
+ uint64_t aSerial, const wr::MaybeExternalImageId& aExternalImageId) {
+ TextureParent* actor =
+ new TextureParent(aAllocator, aContentId, aSerial, aExternalImageId);
+ if (!actor->Init(aSharedData, std::move(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;
+}
+
+// static
+dom::ContentParentId TextureHost::GetTextureContentId(PTextureParent* actor) {
+ if (!actor) {
+ return dom::ContentParentId();
+ }
+ return static_cast<TextureParent*>(actor)->mContentId;
+}
+
+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;
+ aFlags |= TextureFlags::DUMMY_TEXTURE;
+ 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, ReadLockDescriptor&& aReadLock,
+ ISurfaceAllocator* aDeallocator, LayersBackend aBackend,
+ TextureFlags aFlags, wr::MaybeExternalImageId& aExternalImageId) {
+ RefPtr<TextureHost> result;
+
+ switch (aDesc.type()) {
+ case SurfaceDescriptor::TSurfaceDescriptorBuffer:
+ 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:
+ result = CreateTextureHostOGL(aDesc, aDeallocator, aBackend, aFlags);
+ break;
+
+#ifdef XP_WIN
+ case SurfaceDescriptor::TSurfaceDescriptorD3D10:
+ case SurfaceDescriptor::TSurfaceDescriptorDXGIYCbCr:
+ result = CreateTextureHostD3D11(aDesc, aDeallocator, aBackend, aFlags);
+ break;
+# ifdef MOZ_WMF_MEDIA_ENGINE
+ case SurfaceDescriptor::TSurfaceDescriptorDcompSurface:
+ result =
+ CreateTextureHostDcompSurface(aDesc, aDeallocator, aBackend, aFlags);
+ break;
+# endif
+#endif
+ case SurfaceDescriptor::TSurfaceDescriptorRecorded: {
+ const SurfaceDescriptorRecorded& desc =
+ aDesc.get_SurfaceDescriptorRecorded();
+ CompositorBridgeParentBase* actor =
+ aDeallocator ? aDeallocator->AsCompositorBridgeParentBase() : nullptr;
+ UniquePtr<SurfaceDescriptor> realDesc =
+ actor
+ ? actor->LookupSurfaceDescriptorForClientTexture(desc.textureId())
+ : nullptr;
+ 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, std::move(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(aFlags, result, aExternalImageId.ref());
+ }
+
+ if (result) {
+ result->DeserializeReadLock(std::move(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 && !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: {
+ MOZ_ASSERT(aDesc.get_SurfaceDescriptorGPUVideo().type() ==
+ SurfaceDescriptorGPUVideo::TSurfaceDescriptorRemoteDecoder);
+ result = GPUVideoTextureHost::CreateFromDescriptor(
+ aDeallocator->GetContentId(), aFlags,
+ aDesc.get_SurfaceDescriptorGPUVideo());
+ break;
+ }
+ default: {
+ NS_WARNING("No backend independent TextureHost for this descriptor type");
+ }
+ }
+ return result.forget();
+}
+
+TextureHost::TextureHost(TextureHostType aType, TextureFlags aFlags)
+ : AtomicRefCountedWithFinalize("TextureHost"),
+ mTextureHostType(aType),
+ 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();
+ }
+ if (mDestroyedCallback) {
+ mDestroyedCallback();
+ }
+}
+
+void TextureHost::Finalize() {
+ MaybeDestroyRenderTexture();
+
+ if (!(GetFlags() & TextureFlags::DEALLOCATE_CLIENT)) {
+ DeallocateSharedData();
+ DeallocateDeviceData();
+ }
+}
+
+void TextureHost::UnbindTextureSource() {
+ if (mReadLocked) {
+ ReadUnlock();
+ }
+}
+
+void TextureHost::RecycleTexture(TextureFlags aFlags) {
+ MOZ_ASSERT(GetFlags() & TextureFlags::RECYCLE);
+ MOZ_ASSERT(aFlags & TextureFlags::RECYCLE);
+ mFlags = aFlags;
+}
+
+void TextureHost::PrepareForUse() {}
+
+void TextureHost::NotifyNotUsed() {
+ if (!mActor) {
+ if ((mFlags & TextureFlags::REMOTE_TEXTURE) && AsSurfaceTextureHost()) {
+ MOZ_ASSERT(mExternalImageId.isSome());
+ wr::RenderThread::Get()->NotifyNotUsed(*mExternalImageId);
+ }
+ 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;
+ }
+
+ static_cast<TextureParent*>(mActor)->NotifyNotUsed(mFwdTransactionId);
+}
+
+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());
+ mExternalImageId = Nothing();
+}
+
+void TextureHost::DestroyRenderTexture(
+ const wr::ExternalImageId& aExternalImageId) {
+ wr::RenderThread::Get()->UnregisterExternalImage(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());
+}
+
+TextureSource::TextureSource() : mCompositableCount(0) {}
+
+TextureSource::~TextureSource() = default;
+BufferTextureHost::BufferTextureHost(const BufferDescriptor& aDesc,
+ TextureFlags aFlags)
+ : TextureHost(TextureHostType::Buffer, aFlags), mLocked(false) {
+ mDescriptor = aDesc;
+ switch (mDescriptor.type()) {
+ case BufferDescriptor::TYCbCrDescriptor: {
+ const YCbCrDescriptor& ycbcr = mDescriptor.get_YCbCrDescriptor();
+ mSize = ycbcr.display().Size();
+ mFormat = gfx::SurfaceFormat::YUV;
+ break;
+ }
+ case BufferDescriptor::TRGBDescriptor: {
+ const RGBDescriptor& rgb = mDescriptor.get_RGBDescriptor();
+ mSize = rgb.size();
+ mFormat = rgb.format();
+ break;
+ }
+ default:
+ gfxCriticalError() << "Bad buffer host descriptor "
+ << (int)mDescriptor.type();
+ MOZ_CRASH("GFX: Bad descriptor");
+ }
+
+#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::DeallocateDeviceData() {}
+
+void BufferTextureHost::CreateRenderTexture(
+ const wr::ExternalImageId& aExternalImageId) {
+ RefPtr<wr::RenderTextureHost> texture;
+
+ if (UseExternalTextures()) {
+ texture =
+ new wr::RenderExternalTextureHost(GetBuffer(), GetBufferDescriptor());
+ } else {
+ texture =
+ new wr::RenderBufferTextureHost(GetBuffer(), GetBufferDescriptor());
+ }
+
+ wr::RenderThread::Get()->RegisterExternalImage(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;
+
+ // Use native textures if our backend requires it, or if our backend doesn't
+ // forbid it and we want to use them.
+ NativeTexturePolicy policy =
+ BackendNativeTexturePolicy(aResources.GetBackendType(), GetSize());
+ bool useNativeTexture =
+ (policy == REQUIRE) || (policy != FORBID && UseExternalTextures());
+ auto imageType = useNativeTexture ? 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();
+ gfx::IntSize ySize = desc.display().Size();
+ gfx::IntSize cbcrSize = ImageDataSerializer::GetCroppedCbCrSize(desc);
+ wr::ImageDescriptor yDescriptor(
+ ySize, desc.yStride(), SurfaceFormatForColorDepth(desc.colorDepth()));
+ wr::ImageDescriptor cbcrDescriptor(
+ 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, false, 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(ReadLockDescriptor&& aDesc,
+ ISurfaceAllocator* aAllocator) {
+ if (mReadLock) {
+ return;
+ }
+
+ mReadLock = TextureReadLock::Deserialize(std::move(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;
+}
+
+void TextureHost::ReadUnlock() {
+ if (mReadLock && mReadLocked) {
+ mReadLock->ReadUnlock();
+ mReadLocked = false;
+ }
+}
+
+bool TextureHost::NeedsYFlip() const {
+ return bool(mFlags & TextureFlags::ORIGIN_BOTTOM_LEFT);
+}
+
+void BufferTextureHost::UnbindTextureSource() {
+ // This texture is not used by any layer anymore.
+ // If the texture has an intermediate buffer we don't care either because
+ // texture uploads are also performed synchronously for BufferTextureHost.
+ ReadUnlock();
+}
+
+gfx::SurfaceFormat BufferTextureHost::GetFormat() const { return mFormat; }
+
+gfx::YUVColorSpace BufferTextureHost::GetYUVColorSpace() const {
+ if (mFormat == gfx::SurfaceFormat::YUV) {
+ const YCbCrDescriptor& desc = mDescriptor.get_YCbCrDescriptor();
+ return desc.yUVColorSpace();
+ }
+ return gfx::YUVColorSpace::Identity;
+}
+
+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();
+}
+
+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,
+ const dom::ContentParentId& aContentId,
+ uint64_t aSerial,
+ const wr::MaybeExternalImageId& aExternalImageId)
+ : mSurfaceAllocator(aSurfaceAllocator),
+ mContentId(aContentId),
+ 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,
+ ReadLockDescriptor&& aReadLock,
+ const LayersBackend& aBackend,
+ const TextureFlags& aFlags) {
+ mTextureHost =
+ TextureHost::Create(aSharedData, std::move(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();
+ }
+
+ 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