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