michael@0: /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- michael@0: * This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "mozilla/layers/TextureClient.h" michael@0: #include // for uint8_t, uint32_t, etc michael@0: #include "Layers.h" // for Layer, etc michael@0: #include "gfx2DGlue.h" michael@0: #include "gfxContext.h" // for gfxContext, etc michael@0: #include "gfxPlatform.h" // for gfxPlatform michael@0: #include "gfxPoint.h" // for gfxIntSize, gfxSize michael@0: #include "gfxReusableSurfaceWrapper.h" // for gfxReusableSurfaceWrapper michael@0: #include "mozilla/gfx/BaseSize.h" // for BaseSize michael@0: #include "mozilla/ipc/SharedMemory.h" // for SharedMemory, etc michael@0: #include "mozilla/layers/CompositableClient.h" // for CompositableClient michael@0: #include "mozilla/layers/CompositableForwarder.h" michael@0: #include "mozilla/layers/ISurfaceAllocator.h" michael@0: #include "mozilla/layers/ImageDataSerializer.h" michael@0: #include "mozilla/layers/ShadowLayers.h" // for ShadowLayerForwarder michael@0: #include "mozilla/layers/SharedPlanarYCbCrImage.h" michael@0: #include "mozilla/layers/YCbCrImageDataSerializer.h" michael@0: #include "nsDebug.h" // for NS_ASSERTION, NS_WARNING, etc michael@0: #include "nsISupportsImpl.h" // for MOZ_COUNT_CTOR, etc michael@0: #include "ImageContainer.h" // for PlanarYCbCrImage, etc michael@0: #include "mozilla/gfx/2D.h" michael@0: #include "mozilla/layers/TextureClientOGL.h" michael@0: michael@0: #ifdef XP_WIN michael@0: #include "mozilla/layers/TextureD3D9.h" michael@0: #include "mozilla/layers/TextureD3D11.h" michael@0: #include "gfxWindowsPlatform.h" michael@0: #include "gfx2DGlue.h" michael@0: #endif michael@0: #ifdef MOZ_X11 michael@0: #include "mozilla/layers/TextureClientX11.h" michael@0: #ifdef GL_PROVIDER_GLX michael@0: #include "GLXLibrary.h" michael@0: #endif michael@0: #endif michael@0: michael@0: #ifdef MOZ_WIDGET_GONK michael@0: #include michael@0: #include "mozilla/layers/GrallocTextureClient.h" michael@0: #endif michael@0: michael@0: #ifdef MOZ_ANDROID_OMTC michael@0: # include "gfxReusableImageSurfaceWrapper.h" michael@0: #else michael@0: # include "gfxReusableSharedImageSurfaceWrapper.h" michael@0: # include "gfxSharedImageSurface.h" michael@0: #endif michael@0: michael@0: #if 0 michael@0: #define RECYCLE_LOG(...) printf_stderr(__VA_ARGS__) michael@0: #else michael@0: #define RECYCLE_LOG(...) do { } while (0) michael@0: #endif michael@0: michael@0: using namespace mozilla::gl; michael@0: using namespace mozilla::gfx; michael@0: michael@0: namespace mozilla { michael@0: namespace layers { michael@0: michael@0: /** michael@0: * TextureChild is the content-side incarnation of the PTexture IPDL actor. michael@0: * michael@0: * TextureChild is used to synchronize a texture client and its corresponding michael@0: * TextureHost if needed (a TextureClient that is not shared with the compositor michael@0: * does not have a TextureChild) michael@0: * michael@0: * During the deallocation phase, a TextureChild may hold its recently destroyed michael@0: * TextureClient's data until the compositor side confirmed that it is safe to michael@0: * deallocte or recycle the it. michael@0: */ michael@0: class TextureChild MOZ_FINAL : public PTextureChild michael@0: { michael@0: public: michael@0: NS_INLINE_DECL_THREADSAFE_REFCOUNTING(TextureChild) michael@0: michael@0: TextureChild() michael@0: : mForwarder(nullptr) michael@0: , mTextureData(nullptr) michael@0: , mTextureClient(nullptr) michael@0: , mIPCOpen(false) michael@0: { michael@0: } michael@0: michael@0: bool Recv__delete__() MOZ_OVERRIDE; michael@0: michael@0: bool RecvCompositorRecycle(const MaybeFenceHandle& aFence) michael@0: { michael@0: RECYCLE_LOG("Receive recycle %p (%p)\n", mTextureClient, mWaitForRecycle.get()); michael@0: if (aFence.type() != aFence.Tnull_t) { michael@0: FenceHandle fence = aFence.get_FenceHandle(); michael@0: if (fence.IsValid() && mTextureClient) { michael@0: mTextureClient->SetReleaseFenceHandle(aFence); michael@0: // HWC might not provide Fence. michael@0: // In this case, HWC implicitly handles buffer's fence. michael@0: } michael@0: } michael@0: mWaitForRecycle = nullptr; michael@0: return true; michael@0: } michael@0: michael@0: void WaitForCompositorRecycle() michael@0: { michael@0: mWaitForRecycle = mTextureClient; michael@0: RECYCLE_LOG("Wait for recycle %p\n", mWaitForRecycle.get()); michael@0: SendClientRecycle(); michael@0: } michael@0: michael@0: /** michael@0: * Only used during the deallocation phase iff we need synchronization between michael@0: * the client and host side for deallocation (that is, when the data is going michael@0: * to be deallocated or recycled on the client side). michael@0: */ michael@0: void SetTextureData(TextureClientData* aData) michael@0: { michael@0: mTextureData = aData; michael@0: } michael@0: michael@0: void DeleteTextureData(); michael@0: michael@0: CompositableForwarder* GetForwarder() { return mForwarder; } michael@0: michael@0: ISurfaceAllocator* GetAllocator() { return mForwarder; } michael@0: michael@0: void ActorDestroy(ActorDestroyReason why) MOZ_OVERRIDE; michael@0: michael@0: bool IPCOpen() const { return mIPCOpen; } michael@0: michael@0: private: michael@0: michael@0: // AddIPDLReference and ReleaseIPDLReference are only to be called by CreateIPDLActor michael@0: // and DestroyIPDLActor, respectively. We intentionally make them private to prevent misuse. michael@0: // The purpose of these methods is to be aware of when the IPC system around this michael@0: // actor goes down: mIPCOpen is then set to false. michael@0: void AddIPDLReference() { michael@0: MOZ_ASSERT(mIPCOpen == false); michael@0: mIPCOpen = true; michael@0: AddRef(); michael@0: } michael@0: void ReleaseIPDLReference() { michael@0: MOZ_ASSERT(mIPCOpen == true); michael@0: mIPCOpen = false; michael@0: Release(); michael@0: } michael@0: michael@0: RefPtr mForwarder; michael@0: RefPtr mWaitForRecycle; michael@0: TextureClientData* mTextureData; michael@0: TextureClient* mTextureClient; michael@0: bool mIPCOpen; michael@0: michael@0: friend class TextureClient; michael@0: }; michael@0: michael@0: void michael@0: TextureChild::DeleteTextureData() michael@0: { michael@0: mWaitForRecycle = nullptr; michael@0: if (mTextureData) { michael@0: mTextureData->DeallocateSharedData(GetAllocator()); michael@0: delete mTextureData; michael@0: mTextureData = nullptr; michael@0: } michael@0: } michael@0: michael@0: bool michael@0: TextureChild::Recv__delete__() michael@0: { michael@0: DeleteTextureData(); michael@0: return true; michael@0: } michael@0: michael@0: void michael@0: TextureChild::ActorDestroy(ActorDestroyReason why) michael@0: { michael@0: if (mTextureClient) { michael@0: mTextureClient->mActor = nullptr; michael@0: } michael@0: mWaitForRecycle = nullptr; michael@0: } michael@0: michael@0: // static michael@0: PTextureChild* michael@0: TextureClient::CreateIPDLActor() michael@0: { michael@0: TextureChild* c = new TextureChild(); michael@0: c->AddIPDLReference(); michael@0: return c; michael@0: } michael@0: michael@0: // static michael@0: bool michael@0: TextureClient::DestroyIPDLActor(PTextureChild* actor) michael@0: { michael@0: static_cast(actor)->ReleaseIPDLReference(); michael@0: return true; michael@0: } michael@0: michael@0: // static michael@0: TextureClient* michael@0: TextureClient::AsTextureClient(PTextureChild* actor) michael@0: { michael@0: return actor ? static_cast(actor)->mTextureClient : nullptr; michael@0: } michael@0: michael@0: void michael@0: TextureClient::WaitForCompositorRecycle() michael@0: { michael@0: mActor->WaitForCompositorRecycle(); michael@0: } michael@0: michael@0: bool michael@0: TextureClient::InitIPDLActor(CompositableForwarder* aForwarder) michael@0: { michael@0: MOZ_ASSERT(aForwarder); michael@0: if (mActor && mActor->GetForwarder() == aForwarder) { michael@0: return true; michael@0: } michael@0: MOZ_ASSERT(!mActor, "Cannot use a texture on several IPC channels."); michael@0: michael@0: SurfaceDescriptor desc; michael@0: if (!ToSurfaceDescriptor(desc)) { michael@0: return false; michael@0: } michael@0: michael@0: mActor = static_cast(aForwarder->CreateTexture(desc, GetFlags())); michael@0: MOZ_ASSERT(mActor); michael@0: mActor->mForwarder = aForwarder; michael@0: mActor->mTextureClient = this; michael@0: mShared = true; michael@0: return mActor->IPCOpen(); michael@0: } michael@0: michael@0: PTextureChild* michael@0: TextureClient::GetIPDLActor() michael@0: { michael@0: return mActor; michael@0: } michael@0: michael@0: #ifdef MOZ_WIDGET_GONK michael@0: static bool michael@0: DisableGralloc(SurfaceFormat aFormat, const gfx::IntSize& aSizeHint) michael@0: { michael@0: if (aFormat == gfx::SurfaceFormat::A8) { michael@0: return true; michael@0: } michael@0: michael@0: #if ANDROID_VERSION <= 15 michael@0: // Adreno 200 has a problem of drawing gralloc buffer width less than 64 and michael@0: // drawing gralloc buffer with a height 9px-16px. michael@0: // See Bug 983971. michael@0: if (aSizeHint.width < 64 || aSizeHint.height < 32) { michael@0: return true; michael@0: } michael@0: #endif michael@0: michael@0: return false; michael@0: } michael@0: #endif michael@0: michael@0: // static michael@0: TemporaryRef michael@0: TextureClient::CreateTextureClientForDrawing(ISurfaceAllocator* aAllocator, michael@0: SurfaceFormat aFormat, michael@0: TextureFlags aTextureFlags, michael@0: gfx::BackendType aMoz2DBackend, michael@0: const gfx::IntSize& aSizeHint) michael@0: { michael@0: if (aMoz2DBackend == gfx::BackendType::NONE) { michael@0: aMoz2DBackend = gfxPlatform::GetPlatform()->GetContentBackend(); michael@0: } michael@0: michael@0: RefPtr result; michael@0: michael@0: #if defined(MOZ_WIDGET_GONK) || defined(XP_WIN) michael@0: int32_t maxTextureSize = aAllocator->GetMaxTextureSize(); michael@0: #endif michael@0: michael@0: #ifdef XP_WIN michael@0: LayersBackend parentBackend = aAllocator->GetCompositorBackendType(); michael@0: if (parentBackend == LayersBackend::LAYERS_D3D11 && michael@0: (aMoz2DBackend == gfx::BackendType::DIRECT2D || michael@0: aMoz2DBackend == gfx::BackendType::DIRECT2D1_1) && michael@0: gfxWindowsPlatform::GetPlatform()->GetD2DDevice() && michael@0: aSizeHint.width <= maxTextureSize && michael@0: aSizeHint.height <= maxTextureSize && michael@0: !(aTextureFlags & TEXTURE_ALLOC_FALLBACK)) { michael@0: result = new TextureClientD3D11(aFormat, aTextureFlags); michael@0: } michael@0: if (parentBackend == LayersBackend::LAYERS_D3D9 && michael@0: aMoz2DBackend == gfx::BackendType::CAIRO && michael@0: aAllocator->IsSameProcess() && michael@0: aSizeHint.width <= maxTextureSize && michael@0: aSizeHint.height <= maxTextureSize && michael@0: !(aTextureFlags & TEXTURE_ALLOC_FALLBACK)) { michael@0: if (!gfxWindowsPlatform::GetPlatform()->GetD3D9Device()) { michael@0: result = new DIBTextureClientD3D9(aFormat, aTextureFlags); michael@0: } else { michael@0: result = new CairoTextureClientD3D9(aFormat, aTextureFlags); michael@0: } michael@0: } michael@0: #endif michael@0: michael@0: #ifdef MOZ_X11 michael@0: LayersBackend parentBackend = aAllocator->GetCompositorBackendType(); michael@0: gfxSurfaceType type = michael@0: gfxPlatform::GetPlatform()->ScreenReferenceSurface()->GetType(); michael@0: michael@0: if (parentBackend == LayersBackend::LAYERS_BASIC && michael@0: aMoz2DBackend == gfx::BackendType::CAIRO && michael@0: type == gfxSurfaceType::Xlib && michael@0: !(aTextureFlags & TEXTURE_ALLOC_FALLBACK)) michael@0: { michael@0: result = new TextureClientX11(aFormat, aTextureFlags); michael@0: } michael@0: #ifdef GL_PROVIDER_GLX michael@0: if (parentBackend == LayersBackend::LAYERS_OPENGL && michael@0: type == gfxSurfaceType::Xlib && michael@0: !(aTextureFlags & TEXTURE_ALLOC_FALLBACK) && michael@0: aFormat != SurfaceFormat::A8 && michael@0: gl::sGLXLibrary.UseTextureFromPixmap()) michael@0: { michael@0: result = new TextureClientX11(aFormat, aTextureFlags); michael@0: } michael@0: #endif michael@0: #endif michael@0: michael@0: #ifdef MOZ_WIDGET_GONK michael@0: if (!DisableGralloc(aFormat, aSizeHint)) { michael@0: // Don't allow Gralloc texture clients to exceed the maximum texture size. michael@0: // BufferTextureClients have code to handle tiling the surface client-side. michael@0: if (aSizeHint.width <= maxTextureSize && aSizeHint.height <= maxTextureSize) { michael@0: result = new GrallocTextureClientOGL(aAllocator, aFormat, aMoz2DBackend, michael@0: aTextureFlags); michael@0: } michael@0: } michael@0: #endif michael@0: michael@0: // Can't do any better than a buffer texture client. michael@0: if (!result) { michael@0: result = CreateBufferTextureClient(aAllocator, aFormat, aTextureFlags, aMoz2DBackend); michael@0: } michael@0: michael@0: MOZ_ASSERT(!result || result->CanExposeDrawTarget(), "texture cannot expose a DrawTarget?"); michael@0: return result; michael@0: } michael@0: michael@0: // static michael@0: TemporaryRef michael@0: TextureClient::CreateBufferTextureClient(ISurfaceAllocator* aAllocator, michael@0: SurfaceFormat aFormat, michael@0: TextureFlags aTextureFlags, michael@0: gfx::BackendType aMoz2DBackend) michael@0: { michael@0: if (gfxPlatform::GetPlatform()->PreferMemoryOverShmem()) { michael@0: RefPtr result = new MemoryTextureClient(aAllocator, aFormat, michael@0: aMoz2DBackend, michael@0: aTextureFlags); michael@0: return result.forget(); michael@0: } michael@0: RefPtr result = new ShmemTextureClient(aAllocator, aFormat, michael@0: aMoz2DBackend, michael@0: aTextureFlags); michael@0: return result.forget(); michael@0: } michael@0: michael@0: michael@0: class ShmemTextureClientData : public TextureClientData michael@0: { michael@0: public: michael@0: ShmemTextureClientData(ipc::Shmem& aShmem) michael@0: : mShmem(aShmem) michael@0: { michael@0: MOZ_COUNT_CTOR(ShmemTextureClientData); michael@0: } michael@0: michael@0: ~ShmemTextureClientData() michael@0: { michael@0: MOZ_COUNT_CTOR(ShmemTextureClientData); michael@0: } michael@0: michael@0: virtual void DeallocateSharedData(ISurfaceAllocator* allocator) michael@0: { michael@0: allocator->DeallocShmem(mShmem); michael@0: mShmem = ipc::Shmem(); michael@0: } michael@0: michael@0: private: michael@0: ipc::Shmem mShmem; michael@0: }; michael@0: michael@0: class MemoryTextureClientData : public TextureClientData michael@0: { michael@0: public: michael@0: MemoryTextureClientData(uint8_t* aBuffer) michael@0: : mBuffer(aBuffer) michael@0: { michael@0: MOZ_COUNT_CTOR(MemoryTextureClientData); michael@0: } michael@0: michael@0: ~MemoryTextureClientData() michael@0: { michael@0: MOZ_ASSERT(!mBuffer, "Forgot to deallocate the shared texture data?"); michael@0: MOZ_COUNT_DTOR(MemoryTextureClientData); michael@0: } michael@0: michael@0: virtual void DeallocateSharedData(ISurfaceAllocator*) michael@0: { michael@0: delete[] mBuffer; michael@0: mBuffer = nullptr; michael@0: } michael@0: michael@0: private: michael@0: uint8_t* mBuffer; michael@0: }; michael@0: michael@0: TextureClientData* michael@0: MemoryTextureClient::DropTextureData() michael@0: { michael@0: if (!mBuffer) { michael@0: return nullptr; michael@0: } michael@0: TextureClientData* result = new MemoryTextureClientData(mBuffer); michael@0: MarkInvalid(); michael@0: mBuffer = nullptr; michael@0: return result; michael@0: } michael@0: michael@0: TextureClientData* michael@0: ShmemTextureClient::DropTextureData() michael@0: { michael@0: if (!mShmem.IsReadable()) { michael@0: return nullptr; michael@0: } michael@0: TextureClientData* result = new ShmemTextureClientData(mShmem); michael@0: MarkInvalid(); michael@0: mShmem = ipc::Shmem(); michael@0: return result; michael@0: } michael@0: michael@0: TextureClient::TextureClient(TextureFlags aFlags) michael@0: : mFlags(aFlags) michael@0: , mShared(false) michael@0: , mValid(true) michael@0: {} michael@0: michael@0: TextureClient::~TextureClient() michael@0: { michael@0: // All the destruction code that may lead to virtual method calls must michael@0: // be in Finalize() which is called just before the destructor. michael@0: } michael@0: michael@0: void TextureClient::ForceRemove() michael@0: { michael@0: if (mValid && mActor) { michael@0: if (GetFlags() & TEXTURE_DEALLOCATE_CLIENT) { michael@0: mActor->SetTextureData(DropTextureData()); michael@0: if (mActor->IPCOpen()) { michael@0: mActor->SendRemoveTextureSync(); michael@0: } michael@0: mActor->DeleteTextureData(); michael@0: } else { michael@0: if (mActor->IPCOpen()) { michael@0: mActor->SendRemoveTexture(); michael@0: } michael@0: } michael@0: } michael@0: MarkInvalid(); michael@0: } michael@0: michael@0: bool TextureClient::CopyToTextureClient(TextureClient* aTarget, michael@0: const gfx::IntRect* aRect, michael@0: const gfx::IntPoint* aPoint) michael@0: { michael@0: MOZ_ASSERT(IsLocked()); michael@0: MOZ_ASSERT(aTarget->IsLocked()); michael@0: michael@0: if (!aTarget->CanExposeDrawTarget() || !CanExposeDrawTarget()) { michael@0: return false; michael@0: } michael@0: michael@0: RefPtr destinationTarget = aTarget->GetAsDrawTarget(); michael@0: RefPtr sourceTarget = GetAsDrawTarget(); michael@0: RefPtr source = sourceTarget->Snapshot(); michael@0: destinationTarget->CopySurface(source, michael@0: aRect ? *aRect : gfx::IntRect(gfx::IntPoint(0, 0), GetSize()), michael@0: aPoint ? *aPoint : gfx::IntPoint(0, 0)); michael@0: destinationTarget = nullptr; michael@0: source = nullptr; michael@0: sourceTarget = nullptr; michael@0: michael@0: return true; michael@0: } michael@0: michael@0: void michael@0: TextureClient::Finalize() michael@0: { michael@0: MOZ_ASSERT(!IsLocked()); michael@0: // Always make a temporary strong reference to the actor before we use it, michael@0: // in case TextureChild::ActorDestroy might null mActor concurrently. michael@0: RefPtr actor = mActor; michael@0: michael@0: if (actor) { michael@0: // The actor has a raw pointer to us, actor->mTextureClient. michael@0: // Null it before RemoveTexture calls to avoid invalid actor->mTextureClient michael@0: // when calling TextureChild::ActorDestroy() michael@0: actor->mTextureClient = nullptr; michael@0: // this will call ForceRemove in the right thread, using a sync proxy if needed michael@0: if (actor->GetForwarder()) { michael@0: actor->GetForwarder()->RemoveTexture(this); michael@0: } michael@0: } michael@0: } michael@0: michael@0: bool michael@0: TextureClient::ShouldDeallocateInDestructor() const michael@0: { michael@0: if (!IsAllocated()) { michael@0: return false; michael@0: } michael@0: michael@0: // If we're meant to be deallocated by the host, michael@0: // but we haven't been shared yet, then we should michael@0: // deallocate on the client instead. michael@0: return !IsSharedWithCompositor(); michael@0: } michael@0: michael@0: bool michael@0: ShmemTextureClient::ToSurfaceDescriptor(SurfaceDescriptor& aDescriptor) michael@0: { michael@0: MOZ_ASSERT(IsValid()); michael@0: if (!IsAllocated() || GetFormat() == gfx::SurfaceFormat::UNKNOWN) { michael@0: return false; michael@0: } michael@0: michael@0: aDescriptor = SurfaceDescriptorShmem(mShmem, GetFormat()); michael@0: michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: ShmemTextureClient::Allocate(uint32_t aSize) michael@0: { michael@0: MOZ_ASSERT(mValid); michael@0: ipc::SharedMemory::SharedMemoryType memType = OptimalShmemType(); michael@0: mAllocated = GetAllocator()->AllocUnsafeShmem(aSize, memType, &mShmem); michael@0: return mAllocated; michael@0: } michael@0: michael@0: uint8_t* michael@0: ShmemTextureClient::GetBuffer() const michael@0: { michael@0: MOZ_ASSERT(IsValid()); michael@0: if (mAllocated) { michael@0: return mShmem.get(); michael@0: } michael@0: return nullptr; michael@0: } michael@0: michael@0: size_t michael@0: ShmemTextureClient::GetBufferSize() const michael@0: { michael@0: MOZ_ASSERT(IsValid()); michael@0: return mShmem.Size(); michael@0: } michael@0: michael@0: ShmemTextureClient::ShmemTextureClient(ISurfaceAllocator* aAllocator, michael@0: gfx::SurfaceFormat aFormat, michael@0: gfx::BackendType aMoz2DBackend, michael@0: TextureFlags aFlags) michael@0: : BufferTextureClient(aAllocator, aFormat, aMoz2DBackend, aFlags) michael@0: , mAllocated(false) michael@0: { michael@0: MOZ_COUNT_CTOR(ShmemTextureClient); michael@0: } michael@0: michael@0: ShmemTextureClient::~ShmemTextureClient() michael@0: { michael@0: MOZ_COUNT_DTOR(ShmemTextureClient); michael@0: if (ShouldDeallocateInDestructor()) { michael@0: // if the buffer has never been shared we must deallocate it or ir would michael@0: // leak. michael@0: GetAllocator()->DeallocShmem(mShmem); michael@0: } michael@0: } michael@0: michael@0: bool michael@0: MemoryTextureClient::ToSurfaceDescriptor(SurfaceDescriptor& aDescriptor) michael@0: { michael@0: MOZ_ASSERT(IsValid()); michael@0: if (!IsAllocated() || GetFormat() == gfx::SurfaceFormat::UNKNOWN) { michael@0: return false; michael@0: } michael@0: aDescriptor = SurfaceDescriptorMemory(reinterpret_cast(mBuffer), michael@0: GetFormat()); michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: MemoryTextureClient::Allocate(uint32_t aSize) michael@0: { michael@0: MOZ_ASSERT(!mBuffer); michael@0: static const fallible_t fallible = fallible_t(); michael@0: mBuffer = new(fallible) uint8_t[aSize]; michael@0: if (!mBuffer) { michael@0: NS_WARNING("Failed to allocate buffer"); michael@0: return false; michael@0: } michael@0: GfxMemoryImageReporter::DidAlloc(mBuffer); michael@0: mBufSize = aSize; michael@0: return true; michael@0: } michael@0: michael@0: MemoryTextureClient::MemoryTextureClient(ISurfaceAllocator* aAllocator, michael@0: gfx::SurfaceFormat aFormat, michael@0: gfx::BackendType aMoz2DBackend, michael@0: TextureFlags aFlags) michael@0: : BufferTextureClient(aAllocator, aFormat, aMoz2DBackend, aFlags) michael@0: , mBuffer(nullptr) michael@0: , mBufSize(0) michael@0: { michael@0: MOZ_COUNT_CTOR(MemoryTextureClient); michael@0: } michael@0: michael@0: MemoryTextureClient::~MemoryTextureClient() michael@0: { michael@0: MOZ_COUNT_DTOR(MemoryTextureClient); michael@0: if (mBuffer && ShouldDeallocateInDestructor()) { michael@0: // if the buffer has never been shared we must deallocate it or it would michael@0: // leak. michael@0: GfxMemoryImageReporter::WillFree(mBuffer); michael@0: delete [] mBuffer; michael@0: } michael@0: } michael@0: michael@0: BufferTextureClient::BufferTextureClient(ISurfaceAllocator* aAllocator, michael@0: gfx::SurfaceFormat aFormat, michael@0: gfx::BackendType aMoz2DBackend, michael@0: TextureFlags aFlags) michael@0: : TextureClient(aFlags) michael@0: , mAllocator(aAllocator) michael@0: , mFormat(aFormat) michael@0: , mBackend(aMoz2DBackend) michael@0: , mOpenMode(0) michael@0: , mUsingFallbackDrawTarget(false) michael@0: , mLocked(false) michael@0: {} michael@0: michael@0: BufferTextureClient::~BufferTextureClient() michael@0: {} michael@0: michael@0: ISurfaceAllocator* michael@0: BufferTextureClient::GetAllocator() const michael@0: { michael@0: return mAllocator; michael@0: } michael@0: michael@0: bool michael@0: BufferTextureClient::AllocateForSurface(gfx::IntSize aSize, TextureAllocationFlags aFlags) michael@0: { michael@0: MOZ_ASSERT(IsValid()); michael@0: MOZ_ASSERT(mFormat != gfx::SurfaceFormat::YUV, "This textureClient cannot use YCbCr data"); michael@0: MOZ_ASSERT(aSize.width * aSize.height); michael@0: michael@0: int bufSize michael@0: = ImageDataSerializer::ComputeMinBufferSize(aSize, mFormat); michael@0: if (!Allocate(bufSize)) { michael@0: return false; michael@0: } michael@0: michael@0: if (aFlags & ALLOC_CLEAR_BUFFER) { michael@0: memset(GetBuffer(), 0, bufSize); michael@0: } michael@0: michael@0: ImageDataSerializer serializer(GetBuffer(), GetBufferSize()); michael@0: serializer.InitializeBufferInfo(aSize, mFormat); michael@0: mSize = aSize; michael@0: return true; michael@0: } michael@0: michael@0: TemporaryRef michael@0: BufferTextureClient::GetAsDrawTarget() michael@0: { michael@0: MOZ_ASSERT(IsValid()); michael@0: MOZ_ASSERT(mLocked, "GetAsDrawTarget should be called on locked textures only"); michael@0: michael@0: if (mDrawTarget) { michael@0: return mDrawTarget; michael@0: } michael@0: michael@0: ImageDataSerializer serializer(GetBuffer(), GetBufferSize()); michael@0: if (!serializer.IsValid()) { michael@0: return nullptr; michael@0: } michael@0: michael@0: MOZ_ASSERT(mUsingFallbackDrawTarget == false); michael@0: mDrawTarget = serializer.GetAsDrawTarget(mBackend); michael@0: if (mDrawTarget) { michael@0: return mDrawTarget; michael@0: } michael@0: michael@0: // fallback path, probably because the Moz2D backend can't create a michael@0: // DrawTarget around raw memory. This is going to be slow :( michael@0: mDrawTarget = gfx::Factory::CreateDrawTarget(mBackend, serializer.GetSize(), michael@0: serializer.GetFormat()); michael@0: if (!mDrawTarget) { michael@0: return nullptr; michael@0: } michael@0: michael@0: mUsingFallbackDrawTarget = true; michael@0: if (mOpenMode & OPEN_READ) { michael@0: RefPtr surface = serializer.GetAsSurface(); michael@0: IntRect rect(0, 0, surface->GetSize().width, surface->GetSize().height); michael@0: mDrawTarget->CopySurface(surface, rect, IntPoint(0,0)); michael@0: } michael@0: return mDrawTarget; michael@0: } michael@0: michael@0: bool michael@0: BufferTextureClient::Lock(OpenMode aMode) michael@0: { michael@0: MOZ_ASSERT(!mLocked, "The TextureClient is already Locked!"); michael@0: mOpenMode = aMode; michael@0: mLocked = IsValid() && IsAllocated();; michael@0: return mLocked; michael@0: } michael@0: michael@0: void michael@0: BufferTextureClient::Unlock() michael@0: { michael@0: MOZ_ASSERT(mLocked, "The TextureClient is already Unlocked!"); michael@0: mLocked = false; michael@0: if (!mDrawTarget) { michael@0: mUsingFallbackDrawTarget = false; michael@0: return; michael@0: } michael@0: michael@0: // see the comment on TextureClient::GetAsDrawTarget. michael@0: // This DrawTarget is internal to the TextureClient and is only exposed to the michael@0: // outside world between Lock() and Unlock(). This assertion checks that no outside michael@0: // reference remains by the time Unlock() is called. michael@0: MOZ_ASSERT(mDrawTarget->refCount() == 1); michael@0: michael@0: mDrawTarget->Flush(); michael@0: if (mUsingFallbackDrawTarget && (mOpenMode & OPEN_WRITE)) { michael@0: // When we are using a fallback DrawTarget, it means we could not create michael@0: // a DrawTarget wrapping the TextureClient's shared memory. In this scenario michael@0: // we need to put the content of the fallback draw target back into our shared michael@0: // memory. michael@0: RefPtr snapshot = mDrawTarget->Snapshot(); michael@0: RefPtr surface = snapshot->GetDataSurface(); michael@0: ImageDataSerializer serializer(GetBuffer(), GetBufferSize()); michael@0: if (!serializer.IsValid() || serializer.GetSize() != surface->GetSize()) { michael@0: NS_WARNING("Could not write the data back into the texture."); michael@0: mDrawTarget = nullptr; michael@0: mUsingFallbackDrawTarget = false; michael@0: return; michael@0: } michael@0: MOZ_ASSERT(surface->GetSize() == serializer.GetSize()); michael@0: MOZ_ASSERT(surface->GetFormat() == serializer.GetFormat()); michael@0: int bpp = BytesPerPixel(surface->GetFormat()); michael@0: for (int i = 0; i < surface->GetSize().height; ++i) { michael@0: memcpy(serializer.GetData() + i*serializer.GetStride(), michael@0: surface->GetData() + i*surface->Stride(), michael@0: surface->GetSize().width * bpp); michael@0: } michael@0: } michael@0: mDrawTarget = nullptr; michael@0: mUsingFallbackDrawTarget = false; michael@0: } michael@0: michael@0: bool michael@0: BufferTextureClient::UpdateYCbCr(const PlanarYCbCrData& aData) michael@0: { michael@0: MOZ_ASSERT(mLocked); michael@0: MOZ_ASSERT(mFormat == gfx::SurfaceFormat::YUV, "This textureClient can only use YCbCr data"); michael@0: MOZ_ASSERT(!IsImmutable()); michael@0: MOZ_ASSERT(IsValid()); michael@0: MOZ_ASSERT(aData.mCbSkip == aData.mCrSkip); michael@0: michael@0: YCbCrImageDataSerializer serializer(GetBuffer(), GetBufferSize()); michael@0: MOZ_ASSERT(serializer.IsValid()); michael@0: if (!serializer.CopyData(aData.mYChannel, aData.mCbChannel, aData.mCrChannel, michael@0: aData.mYSize, aData.mYStride, michael@0: aData.mCbCrSize, aData.mCbCrStride, michael@0: aData.mYSkip, aData.mCbSkip)) { michael@0: NS_WARNING("Failed to copy image data!"); michael@0: return false; michael@0: } michael@0: michael@0: if (TextureRequiresLocking(mFlags)) { michael@0: // We don't have support for proper locking yet, so we'll michael@0: // have to be immutable instead. michael@0: MarkImmutable(); michael@0: } michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: BufferTextureClient::AllocateForYCbCr(gfx::IntSize aYSize, michael@0: gfx::IntSize aCbCrSize, michael@0: StereoMode aStereoMode) michael@0: { michael@0: MOZ_ASSERT(IsValid()); michael@0: michael@0: size_t bufSize = YCbCrImageDataSerializer::ComputeMinBufferSize(aYSize, michael@0: aCbCrSize); michael@0: if (!Allocate(bufSize)) { michael@0: return false; michael@0: } michael@0: YCbCrImageDataSerializer serializer(GetBuffer(), GetBufferSize()); michael@0: serializer.InitializeBufferInfo(aYSize, michael@0: aCbCrSize, michael@0: aStereoMode); michael@0: mSize = aYSize; michael@0: return true; michael@0: } michael@0: michael@0: } michael@0: }