summaryrefslogtreecommitdiffstats
path: root/gfx/layers/composite/TextureHost.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'gfx/layers/composite/TextureHost.cpp')
-rw-r--r--gfx/layers/composite/TextureHost.cpp1142
1 files changed, 1142 insertions, 0 deletions
diff --git a/gfx/layers/composite/TextureHost.cpp b/gfx/layers/composite/TextureHost.cpp
new file mode 100644
index 000000000..8c5b8c7b7
--- /dev/null
+++ b/gfx/layers/composite/TextureHost.cpp
@@ -0,0 +1,1142 @@
+/* -*- 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 "TextureHost.h"
+
+#include "CompositableHost.h" // for CompositableHost
+#include "LayerScope.h"
+#include "LayersLogging.h" // for AppendToString
+#include "mozilla/gfx/2D.h" // for DataSourceSurface, Factory
+#include "mozilla/ipc/Shmem.h" // for Shmem
+#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/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"
+#include "mozilla/layers/GPUVideoTextureHost.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 "gfxPrefs.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/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:
+ explicit TextureParent(HostIPCAllocator* aAllocator, uint64_t aSerial);
+
+ ~TextureParent();
+
+ bool Init(const SurfaceDescriptor& aSharedData,
+ const LayersBackend& aLayersBackend,
+ const TextureFlags& aFlags);
+
+ void NotifyNotUsed(uint64_t aTransactionId);
+
+ virtual bool RecvRecycleTexture(const TextureFlags& aTextureFlags) override;
+
+ TextureHost* GetTextureHost() { return mTextureHost; }
+
+ virtual void Destroy() override;
+
+ uint64_t GetSerial() const { return mSerial; }
+
+ virtual bool RecvDestroySync() override {
+ DestroyIfNeeded();
+ return true;
+ }
+
+ HostIPCAllocator* mSurfaceAllocator;
+ RefPtr<TextureHost> mTextureHost;
+ // mSerial is unique in TextureClient's process.
+ const uint64_t mSerial;
+};
+
+////////////////////////////////////////////////////////////////////////////////
+PTextureParent*
+TextureHost::CreateIPDLActor(HostIPCAllocator* aAllocator,
+ const SurfaceDescriptor& aSharedData,
+ LayersBackend aLayersBackend,
+ TextureFlags aFlags,
+ uint64_t aSerial)
+{
+ if (aSharedData.type() == SurfaceDescriptor::TSurfaceDescriptorBuffer &&
+ aSharedData.get_SurfaceDescriptorBuffer().data().type() == MemoryOrShmem::Tuintptr_t &&
+ !aAllocator->IsSameProcess())
+ {
+ NS_ERROR("A client process is trying to peek at our address space using a MemoryTexture!");
+ return nullptr;
+ }
+ TextureParent* actor = new TextureParent(aAllocator, aSerial);
+ if (!actor->Init(aSharedData, aLayersBackend, aFlags)) {
+ 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;
+}
+
+// implemented in TextureHostOGL.cpp
+already_AddRefed<TextureHost> CreateTextureHostOGL(const SurfaceDescriptor& aDesc,
+ ISurfaceAllocator* aDeallocator,
+ TextureFlags aFlags);
+
+// implemented in TextureHostBasic.cpp
+already_AddRefed<TextureHost> CreateTextureHostBasic(const SurfaceDescriptor& aDesc,
+ ISurfaceAllocator* aDeallocator,
+ TextureFlags aFlags);
+
+// implemented in TextureD3D11.cpp
+already_AddRefed<TextureHost> CreateTextureHostD3D11(const SurfaceDescriptor& aDesc,
+ ISurfaceAllocator* aDeallocator,
+ TextureFlags aFlags);
+
+// implemented in TextureD3D9.cpp
+already_AddRefed<TextureHost> CreateTextureHostD3D9(const SurfaceDescriptor& aDesc,
+ ISurfaceAllocator* aDeallocator,
+ TextureFlags aFlags);
+
+already_AddRefed<TextureHost>
+TextureHost::Create(const SurfaceDescriptor& aDesc,
+ ISurfaceAllocator* aDeallocator,
+ LayersBackend aBackend,
+ TextureFlags aFlags)
+{
+ switch (aDesc.type()) {
+ case SurfaceDescriptor::TSurfaceDescriptorBuffer:
+ case SurfaceDescriptor::TSurfaceDescriptorDIB:
+ case SurfaceDescriptor::TSurfaceDescriptorFileMapping:
+ case SurfaceDescriptor::TSurfaceDescriptorGPUVideo:
+ return CreateBackendIndependentTextureHost(aDesc, aDeallocator, aFlags);
+
+ case SurfaceDescriptor::TEGLImageDescriptor:
+ case SurfaceDescriptor::TSurfaceTextureDescriptor:
+ case SurfaceDescriptor::TSurfaceDescriptorSharedGLTexture:
+ return CreateTextureHostOGL(aDesc, aDeallocator, aFlags);
+
+ case SurfaceDescriptor::TSurfaceDescriptorMacIOSurface:
+ if (aBackend == LayersBackend::LAYERS_OPENGL) {
+ return CreateTextureHostOGL(aDesc, aDeallocator, aFlags);
+ } else {
+ return CreateTextureHostBasic(aDesc, aDeallocator, aFlags);
+ }
+
+#ifdef MOZ_X11
+ case SurfaceDescriptor::TSurfaceDescriptorX11: {
+ const SurfaceDescriptorX11& desc = aDesc.get_SurfaceDescriptorX11();
+ return MakeAndAddRef<X11TextureHost>(aFlags, desc);
+ }
+#endif
+
+#ifdef XP_WIN
+ case SurfaceDescriptor::TSurfaceDescriptorD3D9:
+ return CreateTextureHostD3D9(aDesc, aDeallocator, aFlags);
+
+ case SurfaceDescriptor::TSurfaceDescriptorD3D10:
+ case SurfaceDescriptor::TSurfaceDescriptorDXGIYCbCr:
+ if (aBackend == LayersBackend::LAYERS_D3D9) {
+ return CreateTextureHostD3D9(aDesc, aDeallocator, aFlags);
+ } else {
+ return CreateTextureHostD3D11(aDesc, aDeallocator, aFlags);
+ }
+#endif
+ default:
+ MOZ_CRASH("GFX: Unsupported Surface type host");
+ }
+}
+
+already_AddRefed<TextureHost>
+CreateBackendIndependentTextureHost(const SurfaceDescriptor& aDesc,
+ ISurfaceAllocator* aDeallocator,
+ 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: {
+ result = new ShmemTextureHost(data.get_Shmem(),
+ bufferDesc.desc(),
+ aDeallocator,
+ aFlags);
+ break;
+ }
+ case MemoryOrShmem::Tuintptr_t: {
+ 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: {
+ result = new GPUVideoTextureHost(aFlags, aDesc.get_SurfaceDescriptorGPUVideo());
+ break;
+ }
+#ifdef XP_WIN
+ case SurfaceDescriptor::TSurfaceDescriptorDIB: {
+ 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)
+{
+}
+
+TextureHost::~TextureHost()
+{
+ // 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();
+}
+
+void TextureHost::Finalize()
+{
+ if (!(GetFlags() & TextureFlags::DEALLOCATE_CLIENT)) {
+ DeallocateSharedData();
+ DeallocateDeviceData();
+ }
+}
+
+void
+TextureHost::UnbindTextureSource()
+{
+ if (mReadLock) {
+ auto compositor = GetCompositor();
+ // 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 (compositor) {
+ compositor->UnlockAfterComposition(this);
+ } else {
+ // GetCompositor returned null which means no compositor can be using this
+ // texture. We can ReadUnlock right away.
+ ReadUnlock();
+ }
+ }
+}
+
+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.
+ if (!(GetFlags() & TextureFlags::RECYCLE)) {
+ return;
+ }
+
+ auto compositor = GetCompositor();
+ // 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 (!compositor ||
+ compositor->IsDestroyed() ||
+ compositor->AsBasicCompositor() ||
+ HasIntermediateBuffer()) {
+ static_cast<TextureParent*>(mActor)->NotifyNotUsed(mFwdTransactionId);
+ return;
+ }
+
+ compositor->NotifyNotUsedAfterComposition(this);
+}
+
+void
+TextureHost::CallNotifyNotUsed()
+{
+ if (!mActor) {
+ return;
+ }
+ static_cast<TextureParent*>(mActor)->NotifyNotUsed(mFwdTransactionId);
+}
+
+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()) {
+ AppendToString(aStream, GetSize(), " [size=", "]");
+ AppendToString(aStream, GetFormat(), " [format=", "]");
+ Unlock();
+ }
+ AppendToString(aStream, mFlags, " [flags=", "]");
+#ifdef MOZ_DUMP_PAINTING
+ if (gfxPrefs::LayersDumpTexture() || profiler_feature_active("layersdump")) {
+ 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)
+{
+ MOZ_COUNT_CTOR(TextureSource);
+}
+
+TextureSource::~TextureSource()
+{
+ MOZ_COUNT_DTOR(TextureSource);
+}
+
+const char*
+TextureSource::Name() const
+{
+ MOZ_CRASH("GFX: TextureSource without class name");
+ return "TextureSource";
+}
+
+BufferTextureHost::BufferTextureHost(const BufferDescriptor& aDesc,
+ TextureFlags aFlags)
+: TextureHost(aFlags)
+, mCompositor(nullptr)
+, mUpdateSerial(1)
+, mLocked(false)
+, mNeedsFullUpdate(false)
+{
+ mDescriptor = aDesc;
+ switch (mDescriptor.type()) {
+ case BufferDescriptor::TYCbCrDescriptor: {
+ const YCbCrDescriptor& ycbcr = mDescriptor.get_YCbCrDescriptor();
+ mSize = ycbcr.ySize();
+ 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;
+ }
+}
+
+BufferTextureHost::~BufferTextureHost()
+{}
+
+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::SetCompositor(Compositor* aCompositor)
+{
+ MOZ_ASSERT(aCompositor);
+ if (mCompositor == aCompositor) {
+ return;
+ }
+ if (aCompositor && mCompositor &&
+ aCompositor->GetBackendType() == mCompositor->GetBackendType()) {
+ RefPtr<TextureSource> it = mFirstSource;
+ while (it) {
+ it->SetCompositor(aCompositor);
+ it = it->GetNextSibling();
+ }
+ }
+ if (mFirstSource && mFirstSource->IsOwnedBy(this)) {
+ mFirstSource->SetOwner(nullptr);
+ }
+ if (mFirstSource) {
+ mFirstSource = nullptr;
+ mNeedsFullUpdate = true;
+ }
+ mCompositor = aCompositor;
+}
+
+void
+BufferTextureHost::DeallocateDeviceData()
+{
+ if (mFirstSource && mFirstSource->NumCompositableRefs() > 0) {
+ 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 (!MaybeUpload(!mNeedsFullUpdate ? &mMaybeUpdatedRegion : nullptr)) {
+ return false;
+ }
+ mLocked = !!mFirstSource;
+ return mLocked;
+}
+
+void
+BufferTextureHost::Unlock()
+{
+ MOZ_ASSERT(mLocked);
+ mLocked = false;
+}
+
+void
+TextureHost::DeserializeReadLock(const ReadLockDescriptor& aDesc,
+ ISurfaceAllocator* aAllocator)
+{
+ RefPtr<TextureReadLock> lock = TextureReadLock::Deserialize(aDesc, aAllocator);
+ if (!lock) {
+ return;
+ }
+
+ // If mReadLock is not null it means we haven't unlocked it yet and the content
+ // side should not have been able to write into this texture and send a new lock!
+ MOZ_ASSERT(!mReadLock);
+ mReadLock = lock.forget();
+}
+
+void
+TextureHost::ReadUnlock()
+{
+ if (mReadLock) {
+ mReadLock->ReadUnlock();
+ mReadLock = nullptr;
+ }
+}
+
+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 (!mCompositor) {
+ return false;
+ }
+
+ if (mFormat == gfx::SurfaceFormat::YUV) {
+ mFirstSource = mCompositor->CreateDataTextureSourceAroundYCbCr(this);
+ } else {
+ RefPtr<gfx::DataSourceSurface> surf =
+ gfx::Factory::CreateWrappingDataSourceSurface(GetBuffer(),
+ ImageDataSerializer::ComputeRGBStride(mFormat, mSize.width), mSize, mFormat);
+ if (!surf) {
+ return false;
+ }
+ mFirstSource = mCompositor->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,
+ Compositor* aCompositor)
+{
+ if (!aCompositor) {
+ return false;
+ }
+
+ switch (aDescriptor.type()) {
+ case BufferDescriptor::TYCbCrDescriptor: {
+ const YCbCrDescriptor& ycbcr = aDescriptor.get_YCbCrDescriptor();
+
+ if (!aCompositor->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,
+ mCompositor);
+
+ 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->SetCompositor(mCompositor);
+ it = it->GetNextSibling();
+ }
+ }
+}
+
+bool
+BufferTextureHost::BindTextureSource(CompositableTextureSourceRef& aTexture)
+{
+ MOZ_ASSERT(mLocked);
+ MOZ_ASSERT(mFirstSource);
+ aTexture = mFirstSource;
+ return !!aTexture;
+}
+
+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();
+}
+
+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 &&
+ mCompositor &&
+ !mCompositor->SupportsEffect(EffectTypes::YCBCR)) {
+ return gfx::SurfaceFormat::R8G8B8X8;
+ }
+ return mFormat;
+}
+
+YUVColorSpace
+BufferTextureHost::GetYUVColorSpace() const
+{
+ if (mFormat == gfx::SurfaceFormat::YUV) {
+ const YCbCrDescriptor& desc = mDescriptor.get_YCbCrDescriptor();
+ return desc.yUVColorSpace();
+ }
+ return YUVColorSpace::UNKNOWN;
+}
+
+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();
+ }
+
+ // 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 (!mCompositor) {
+ // This can happen if we send textures to a compositable that isn't yet
+ // attached to a layer.
+ return false;
+ }
+ if (!mHasIntermediateBuffer && EnsureWrappingTextureSource()) {
+ 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 (!mCompositor->SupportsEffect(EffectTypes::YCBCR)) {
+ RefPtr<gfx::DataSourceSurface> surf =
+ ImageDataSerializer::DataSourceSurfaceFromYCbCrDescriptor(buf, mDescriptor.get_YCbCrDescriptor());
+ if (NS_WARN_IF(!surf)) {
+ return false;
+ }
+ if (!mFirstSource) {
+ mFirstSource = mCompositor->CreateDataTextureSource(mFlags|TextureFlags::RGB_FROM_YCBCR);
+ mFirstSource->SetOwner(this);
+ }
+ mFirstSource->Update(surf, aRegion);
+ return true;
+ }
+
+ RefPtr<DataTextureSource> srcY;
+ RefPtr<DataTextureSource> srcU;
+ RefPtr<DataTextureSource> srcV;
+ if (!mFirstSource) {
+ // We don't support BigImages for YCbCr compositing.
+ srcY = mCompositor->CreateDataTextureSource(mFlags|TextureFlags::DISALLOW_BIGIMAGE);
+ srcU = mCompositor->CreateDataTextureSource(mFlags|TextureFlags::DISALLOW_BIGIMAGE);
+ srcV = mCompositor->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.ySize().width,
+ desc.ySize(),
+ gfx::SurfaceFormat::A8);
+ RefPtr<gfx::DataSourceSurface> tempCb =
+ gfx::Factory::CreateWrappingDataSourceSurface(ImageDataSerializer::GetCbChannel(buf, desc),
+ desc.cbCrSize().width,
+ desc.cbCrSize(),
+ gfx::SurfaceFormat::A8);
+ RefPtr<gfx::DataSourceSurface> tempCr =
+ gfx::Factory::CreateWrappingDataSourceSurface(ImageDataSerializer::GetCrChannel(buf, desc),
+ desc.cbCrSize().width,
+ desc.cbCrSize(),
+ gfx::SurfaceFormat::A8);
+ // 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 = mCompositor->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)
+: mSurfaceAllocator(aSurfaceAllocator)
+, mSerial(aSerial)
+{
+ 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 LayersBackend& aBackend,
+ const TextureFlags& aFlags)
+{
+ mTextureHost = TextureHost::Create(aSharedData,
+ mSurfaceAllocator,
+ aBackend,
+ aFlags);
+ if (mTextureHost) {
+ mTextureHost->mActor = this;
+ }
+
+ return !!mTextureHost;
+}
+
+void
+TextureParent::Destroy()
+{
+ if (!mTextureHost) {
+ return;
+ }
+
+ // 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();
+}
+
+bool
+TextureParent::RecvRecycleTexture(const TextureFlags& aTextureFlags)
+{
+ if (!mTextureHost) {
+ return true;
+ }
+ mTextureHost->RecycleTexture(aTextureFlags);
+ return true;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+} // namespace layers
+} // namespace mozilla