Files
tubestation/gfx/layers/composite/TextureHost.cpp
sotaro eb2d5e90e3 Bug 1354411 - Rebuild CompositorSessions if WebRender is disabled r=kats
When WebRender creation is failed, WebRender is disabled in gecko. There is a case that WebRenderBridgeParents exist when WebRender is disabled. To handle this, gecko needs to rebuild all CompositorSessions.

There is also a problem related to gfxVars::UseWebRender on compositor thread. If e10s is enabled, but no-gpu process(default on linux and mac), gfxVars::UseWebRender change is soon notified by compositor thread tasks. If WebRender creation failure happens at 2nd WebRender creation, several WebRenderBridgeParents for 1st WebRender could exist. IPC messages from WebRenderLayerManager are normally async, then there is a chance that the WebRenderBridgeParents receive the messages after the gfxVars::UseWebRender change. Further the gfxVars::UseWebRender change in content process could be delayed than WebRenderBridgeParents, then content process does not have a way to stop sending PWebRenderBridge IPC until the change of gfxVars::UseWebRender is received. WebRenderBridgeParent related tasks handle the message, but some tasks are done based on gfxVars::UseWebRender. At this time, gfxVars::UseWebRender returned false on compositor thread, then it cause unexpected result for WebRenderBridgeParent and WebRender. To addres this inconsistent situation, WebRenderBridgeParent related tasks on compositor thread stop to use gfxVars::UseWebRender.
2017-08-04 14:36:41 +09:00

1268 lines
38 KiB
C++

/* -*- 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/gfx/gfxVars.h"
#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 "mozilla/layers/WebRenderTextureHost.h"
#include "mozilla/webrender/RenderBufferTextureHost.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 "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, const wr::MaybeExternalImageId& aExternalImageId);
~TextureParent();
bool Init(const SurfaceDescriptor& aSharedData,
const LayersBackend& aLayersBackend,
const TextureFlags& aFlags);
void NotifyNotUsed(uint64_t aTransactionId);
virtual mozilla::ipc::IPCResult RecvRecycleTexture(const TextureFlags& aTextureFlags) override;
TextureHost* GetTextureHost() { return mTextureHost; }
virtual void Destroy() override;
uint64_t GetSerial() const { return mSerial; }
HostIPCAllocator* mSurfaceAllocator;
RefPtr<TextureHost> mTextureHost;
// mSerial is unique in TextureClient's process.
const uint64_t mSerial;
wr::MaybeExternalImageId mExternalImageId;
};
static bool
WrapWithWebRenderTextureHost(ISurfaceAllocator* aDeallocator,
LayersBackend aBackend,
TextureFlags aFlags)
{
if ((aFlags & TextureFlags::SNAPSHOT) ||
(aBackend != LayersBackend::LAYERS_WR) ||
(!aDeallocator->UsesImageBridge() && !aDeallocator->AsCompositorBridgeParentBase())) {
return false;
}
return true;
}
////////////////////////////////////////////////////////////////////////////////
PTextureParent*
TextureHost::CreateIPDLActor(HostIPCAllocator* aAllocator,
const SurfaceDescriptor& aSharedData,
LayersBackend aLayersBackend,
TextureFlags aFlags,
uint64_t aSerial,
const wr::MaybeExternalImageId& aExternalImageId)
{
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, aExternalImageId);
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,
LayersBackend aBackend,
TextureFlags aFlags);
// implemented in TextureHostBasic.cpp
already_AddRefed<TextureHost> CreateTextureHostBasic(const SurfaceDescriptor& aDesc,
ISurfaceAllocator* aDeallocator,
LayersBackend aBackend,
TextureFlags aFlags);
// implemented in TextureD3D11.cpp
already_AddRefed<TextureHost> CreateTextureHostD3D11(const SurfaceDescriptor& aDesc,
ISurfaceAllocator* aDeallocator,
LayersBackend aBackend,
TextureFlags aFlags);
already_AddRefed<TextureHost>
TextureHost::Create(const SurfaceDescriptor& aDesc,
ISurfaceAllocator* aDeallocator,
LayersBackend aBackend,
TextureFlags aFlags,
wr::MaybeExternalImageId& aExternalImageId)
{
RefPtr<TextureHost> result;
switch (aDesc.type()) {
case SurfaceDescriptor::TSurfaceDescriptorBuffer:
case SurfaceDescriptor::TSurfaceDescriptorDIB:
case SurfaceDescriptor::TSurfaceDescriptorFileMapping:
case SurfaceDescriptor::TSurfaceDescriptorGPUVideo:
result = CreateBackendIndependentTextureHost(aDesc, aDeallocator, aBackend, aFlags);
break;
case SurfaceDescriptor::TEGLImageDescriptor:
case SurfaceDescriptor::TSurfaceTextureDescriptor:
case SurfaceDescriptor::TSurfaceDescriptorSharedGLTexture:
result = CreateTextureHostOGL(aDesc, aDeallocator, aBackend, aFlags);
break;
case SurfaceDescriptor::TSurfaceDescriptorMacIOSurface:
if (aBackend == LayersBackend::LAYERS_OPENGL ||
aBackend == LayersBackend::LAYERS_WR) {
result = CreateTextureHostOGL(aDesc, aDeallocator, aBackend, aFlags);
break;
} else {
result = CreateTextureHostBasic(aDesc, aDeallocator, aBackend, aFlags);
break;
}
#ifdef MOZ_X11
case SurfaceDescriptor::TSurfaceDescriptorX11: {
const SurfaceDescriptorX11& desc = aDesc.get_SurfaceDescriptorX11();
result = MakeAndAddRef<X11TextureHost>(aFlags, desc);
break;
}
#endif
#ifdef XP_WIN
case SurfaceDescriptor::TSurfaceDescriptorD3D10:
case SurfaceDescriptor::TSurfaceDescriptorDXGIYCbCr:
result = CreateTextureHostD3D11(aDesc, aDeallocator, aBackend, aFlags);
break;
#endif
default:
MOZ_CRASH("GFX: Unsupported Surface type host");
}
if (WrapWithWebRenderTextureHost(aDeallocator, aBackend, aFlags)) {
MOZ_ASSERT(aExternalImageId.isSome());
result = new WebRenderTextureHost(aDesc, aFlags, result, aExternalImageId.ref());
}
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: {
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) {
// This TextureHost is not used anymore. Since most compositor backends are
// working asynchronously under the hood a compositor could still be using
// this texture, so it is generally best to wait until the end of the next
// composition before calling ReadUnlock. We ask the compositor to take care
// of that for us.
if (mProvider) {
mProvider->UnlockAfterComposition(this);
} else {
// GetCompositor returned null which means no compositor can be using this
// texture. We can ReadUnlock right away.
ReadUnlock();
}
}
}
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;
}
// The following cases do not need to defer NotifyNotUsed until next Composite.
// - TextureHost does not have Compositor.
// - Compositor is BasicCompositor.
// - TextureHost has intermediate buffer.
// end of buffer usage.
if (!mProvider ||
HasIntermediateBuffer() ||
!mProvider->NotifyNotUsedAfterComposition(this))
{
static_cast<TextureParent*>(mActor)->NotifyNotUsed(mFwdTransactionId);
return;
}
}
void
TextureHost::CallNotifyNotUsed()
{
if (!mActor) {
return;
}
static_cast<TextureParent*>(mActor)->NotifyNotUsed(mFwdTransactionId);
}
void
TextureHost::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(ProfilerFeature::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)
{
}
TextureSource::~TextureSource()
{
}
const char*
TextureSource::Name() const
{
MOZ_CRASH("GFX: TextureSource without class name");
return "TextureSource";
}
BufferTextureHost::BufferTextureHost(const BufferDescriptor& aDesc,
TextureFlags aFlags)
: TextureHost(aFlags)
, mUpdateSerial(1)
, mLocked(false)
, mNeedsFullUpdate(false)
{
mDescriptor = aDesc;
switch (mDescriptor.type()) {
case BufferDescriptor::TYCbCrDescriptor: {
const YCbCrDescriptor& ycbcr = mDescriptor.get_YCbCrDescriptor();
mSize = ycbcr.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::SetTextureSourceProvider(TextureSourceProvider* aProvider)
{
if (mProvider == aProvider) {
return;
}
if (mFirstSource && mFirstSource->IsOwnedBy(this)) {
mFirstSource->SetOwner(nullptr);
}
if (mFirstSource) {
mFirstSource = nullptr;
mNeedsFullUpdate = true;
}
mProvider = aProvider;
}
void
BufferTextureHost::DeallocateDeviceData()
{
if (mFirstSource && mFirstSource->NumCompositableRefs() > 0) {
return;
}
if (!mFirstSource || !mFirstSource->IsOwnedBy(this)) {
mFirstSource = nullptr;
return;
}
mFirstSource->SetOwner(nullptr);
RefPtr<TextureSource> it = mFirstSource;
while (it) {
it->DeallocateDeviceData();
it = it->GetNextSibling();
}
}
bool
BufferTextureHost::Lock()
{
MOZ_ASSERT(!mLocked);
if (!UploadIfNeeded()) {
return false;
}
mLocked = !!mFirstSource;
return mLocked;
}
void
BufferTextureHost::Unlock()
{
MOZ_ASSERT(mLocked);
mLocked = false;
}
void
BufferTextureHost::CreateRenderTexture(const wr::ExternalImageId& aExternalImageId)
{
RefPtr<wr::RenderTextureHost> texture =
new wr::RenderBufferTextureHost(GetBuffer(), GetBufferDescriptor());
wr::RenderThread::Get()->RegisterExternalImage(wr::AsUint64(aExternalImageId), texture.forget());
}
void
BufferTextureHost::GetWRImageKeys(nsTArray<wr::ImageKey>& aImageKeys,
const std::function<wr::ImageKey()>& aImageKeyAllocator)
{
MOZ_ASSERT(aImageKeys.IsEmpty());
if (GetFormat() != gfx::SurfaceFormat::YUV) {
// 1 image key
aImageKeys.AppendElement(aImageKeyAllocator());
MOZ_ASSERT(aImageKeys.Length() == 1);
} else {
// 3 image key
aImageKeys.AppendElement(aImageKeyAllocator());
aImageKeys.AppendElement(aImageKeyAllocator());
aImageKeys.AppendElement(aImageKeyAllocator());
MOZ_ASSERT(aImageKeys.Length() == 3);
}
}
void
BufferTextureHost::AddWRImage(wr::WebRenderAPI* aAPI,
Range<const wr::ImageKey>& aImageKeys,
const wr::ExternalImageId& aExtID)
{
if (GetFormat() != gfx::SurfaceFormat::YUV) {
MOZ_ASSERT(aImageKeys.length() == 1);
wr::ImageDescriptor descriptor(GetSize(),
ImageDataSerializer::ComputeRGBStride(GetFormat(), GetSize().width),
GetFormat());
aAPI->AddExternalImageBuffer(aImageKeys[0], descriptor, aExtID);
} else {
MOZ_ASSERT(aImageKeys.length() == 3);
const layers::YCbCrDescriptor& desc = mDescriptor.get_YCbCrDescriptor();
wr::ImageDescriptor yDescriptor(desc.ySize(), desc.ySize().width, gfx::SurfaceFormat::A8);
wr::ImageDescriptor cbcrDescriptor(desc.cbCrSize(), desc.cbCrSize().width, gfx::SurfaceFormat::A8);
aAPI->AddExternalImage(aImageKeys[0],
yDescriptor,
aExtID,
wr::WrExternalImageBufferType::ExternalBuffer,
0);
aAPI->AddExternalImage(aImageKeys[1],
cbcrDescriptor,
aExtID,
wr::WrExternalImageBufferType::ExternalBuffer,
1);
aAPI->AddExternalImage(aImageKeys[2],
cbcrDescriptor,
aExtID,
wr::WrExternalImageBufferType::ExternalBuffer,
2);
}
}
void
BufferTextureHost::PushExternalImage(wr::DisplayListBuilder& aBuilder,
const wr::LayoutRect& aBounds,
const wr::LayoutRect& aClip,
wr::ImageRendering aFilter,
Range<const wr::ImageKey>& aImageKeys)
{
if (GetFormat() != gfx::SurfaceFormat::YUV) {
MOZ_ASSERT(aImageKeys.length() == 1);
aBuilder.PushImage(aBounds, aClip, aFilter, aImageKeys[0]);
} else {
MOZ_ASSERT(aImageKeys.length() == 3);
aBuilder.PushYCbCrPlanarImage(aBounds,
aClip,
aImageKeys[0],
aImageKeys[1],
aImageKeys[2],
wr::WrYuvColorSpace::Rec601,
aFilter);
}
}
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::SetReadLock(TextureReadLock* aReadLock)
{
if (!aReadLock) {
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 = aReadLock;
}
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 (!mProvider) {
return false;
}
if (mFormat == gfx::SurfaceFormat::YUV) {
mFirstSource = mProvider->CreateDataTextureSourceAroundYCbCr(this);
} else {
RefPtr<gfx::DataSourceSurface> surf =
gfx::Factory::CreateWrappingDataSourceSurface(GetBuffer(),
ImageDataSerializer::ComputeRGBStride(mFormat, mSize.width), mSize, mFormat);
if (!surf) {
return false;
}
mFirstSource = mProvider->CreateDataTextureSourceAround(surf);
}
if (!mFirstSource) {
// BasicCompositor::CreateDataTextureSourceAround never returns null
// and we don't expect to take this branch if we are using another backend.
// Returning false is fine but if we get into this situation it probably
// means something fishy is going on, like a texture being used with
// several compositor backends.
NS_WARNING("Failed to use a BufferTextureHost without intermediate buffer");
return false;
}
mFirstSource->SetUpdateSerial(mUpdateSerial);
mFirstSource->SetOwner(this);
return true;
}
static
bool IsCompatibleTextureSource(TextureSource* aTexture,
const BufferDescriptor& aDescriptor,
TextureSourceProvider* aProvider)
{
if (!aProvider) {
return false;
}
switch (aDescriptor.type()) {
case BufferDescriptor::TYCbCrDescriptor: {
const YCbCrDescriptor& ycbcr = aDescriptor.get_YCbCrDescriptor();
if (!aProvider->SupportsEffect(EffectTypes::YCBCR)) {
return aTexture->GetFormat() == gfx::SurfaceFormat::B8G8R8X8
&& aTexture->GetSize() == ycbcr.ySize();
}
if (aTexture->GetFormat() != gfx::SurfaceFormat::A8
|| aTexture->GetSize() != ycbcr.ySize()) {
return false;
}
auto cbTexture = aTexture->GetSubSource(1);
if (!cbTexture
|| cbTexture->GetFormat() != gfx::SurfaceFormat::A8
|| cbTexture->GetSize() != ycbcr.cbCrSize()) {
return false;
}
auto crTexture = aTexture->GetSubSource(2);
if (!crTexture
|| crTexture->GetFormat() != gfx::SurfaceFormat::A8
|| crTexture->GetSize() != ycbcr.cbCrSize()) {
return false;
}
return true;
}
case BufferDescriptor::TRGBDescriptor: {
const RGBDescriptor& rgb = aDescriptor.get_RGBDescriptor();
return aTexture->GetFormat() == rgb.format()
&& aTexture->GetSize() == rgb.size();
}
default: {
return false;
}
}
}
void
BufferTextureHost::PrepareTextureSource(CompositableTextureSourceRef& aTexture)
{
// Reuse WrappingTextureSourceYCbCrBasic to reduce memory consumption.
if (mFormat == gfx::SurfaceFormat::YUV &&
!mHasIntermediateBuffer &&
aTexture.get() &&
aTexture->AsWrappingTextureSourceYCbCrBasic() &&
aTexture->NumCompositableRefs() <= 1 &&
aTexture->GetSize() == GetSize()) {
aTexture->AsSourceBasic()->SetBufferTextureHost(this);
aTexture->AsDataTextureSource()->SetOwner(this);
mFirstSource = aTexture->AsDataTextureSource();
mNeedsFullUpdate = true;
}
if (!mHasIntermediateBuffer) {
EnsureWrappingTextureSource();
}
if (mFirstSource && mFirstSource->IsOwnedBy(this)) {
// We are already attached to a TextureSource, nothing to do except tell
// the compositable to use it.
aTexture = mFirstSource.get();
return;
}
// We don't own it, apparently.
if (mFirstSource) {
mNeedsFullUpdate = true;
mFirstSource = nullptr;
}
DataTextureSource* texture = aTexture.get() ? aTexture->AsDataTextureSource() : nullptr;
bool compatibleFormats = texture && IsCompatibleTextureSource(texture,
mDescriptor,
mProvider);
bool shouldCreateTexture = !compatibleFormats
|| texture->NumCompositableRefs() > 1
|| texture->HasOwner();
if (!shouldCreateTexture) {
mFirstSource = texture;
mFirstSource->SetOwner(this);
mNeedsFullUpdate = true;
// It's possible that texture belonged to a different compositor,
// so make sure we update it (and all of its siblings) to the
// current one.
RefPtr<TextureSource> it = mFirstSource;
while (it) {
it->SetTextureSourceProvider(mProvider);
it = it->GetNextSibling();
}
}
}
bool
BufferTextureHost::BindTextureSource(CompositableTextureSourceRef& aTexture)
{
MOZ_ASSERT(mLocked);
MOZ_ASSERT(mFirstSource);
aTexture = mFirstSource;
return !!aTexture;
}
bool
BufferTextureHost::AcquireTextureSource(CompositableTextureSourceRef& aTexture)
{
if (!UploadIfNeeded()) {
return false;
}
aTexture = mFirstSource;
return !!mFirstSource;
}
void
BufferTextureHost::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 &&
mProvider &&
!mProvider->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::UploadIfNeeded()
{
return MaybeUpload(!mNeedsFullUpdate ? &mMaybeUpdatedRegion : nullptr);
}
bool
BufferTextureHost::MaybeUpload(nsIntRegion *aRegion)
{
auto serial = mFirstSource ? mFirstSource->GetUpdateSerial() : 0;
if (serial == mUpdateSerial) {
return true;
}
if (serial == 0) {
// 0 means the source has no valid content
aRegion = nullptr;
}
if (!Upload(aRegion)) {
return false;
}
if (mHasIntermediateBuffer) {
// We just did the texture upload, the content side can now freely write
// into the shared buffer.
ReadUnlock();
}
// We no longer have an invalid region.
mNeedsFullUpdate = false;
mMaybeUpdatedRegion.SetEmpty();
// If upload returns true we know mFirstSource is not null
mFirstSource->SetUpdateSerial(mUpdateSerial);
return true;
}
bool
BufferTextureHost::Upload(nsIntRegion *aRegion)
{
uint8_t* buf = GetBuffer();
if (!buf) {
// We don't have a buffer; a possible cause is that the IPDL actor
// is already dead. This inevitably happens as IPDL actors can die
// at any time, so we want to silently return in this case.
// another possible cause is that IPDL failed to map the shmem when
// deserializing it.
return false;
}
if (!mProvider) {
// This can happen if we send textures to a compositable that isn't yet
// attached to a layer.
return false;
}
if (!mHasIntermediateBuffer && EnsureWrappingTextureSource()) {
return true;
}
if (mFormat == gfx::SurfaceFormat::UNKNOWN) {
NS_WARNING("BufferTextureHost: unsupported format!");
return false;
} else if (mFormat == gfx::SurfaceFormat::YUV) {
const YCbCrDescriptor& desc = mDescriptor.get_YCbCrDescriptor();
if (!mProvider->SupportsEffect(EffectTypes::YCBCR)) {
RefPtr<gfx::DataSourceSurface> surf =
ImageDataSerializer::DataSourceSurfaceFromYCbCrDescriptor(buf, mDescriptor.get_YCbCrDescriptor());
if (NS_WARN_IF(!surf)) {
return false;
}
if (!mFirstSource) {
mFirstSource = mProvider->CreateDataTextureSource(mFlags|TextureFlags::RGB_FROM_YCBCR);
mFirstSource->SetOwner(this);
}
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 = mProvider->CreateDataTextureSource(mFlags|TextureFlags::DISALLOW_BIGIMAGE);
srcU = mProvider->CreateDataTextureSource(mFlags|TextureFlags::DISALLOW_BIGIMAGE);
srcV = mProvider->CreateDataTextureSource(mFlags|TextureFlags::DISALLOW_BIGIMAGE);
mFirstSource = srcY;
mFirstSource->SetOwner(this);
srcY->SetNextSibling(srcU);
srcU->SetNextSibling(srcV);
} else {
// mFormat never changes so if this was created as a YCbCr host and already
// contains a source it should already have 3 sources.
// BufferTextureHost only uses DataTextureSources so it is safe to assume
// all 3 sources are DataTextureSource.
MOZ_ASSERT(mFirstSource->GetNextSibling());
MOZ_ASSERT(mFirstSource->GetNextSibling()->GetNextSibling());
srcY = mFirstSource;
srcU = mFirstSource->GetNextSibling()->AsDataTextureSource();
srcV = mFirstSource->GetNextSibling()->GetNextSibling()->AsDataTextureSource();
}
RefPtr<gfx::DataSourceSurface> tempY =
gfx::Factory::CreateWrappingDataSourceSurface(ImageDataSerializer::GetYChannel(buf, desc),
desc.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 = mProvider->CreateDataTextureSource(mFlags);
mFirstSource->SetOwner(this);
if (mFlags & TextureFlags::COMPONENT_ALPHA) {
// Update the full region the first time for component alpha textures.
regionToUpdate = nullptr;
}
}
RefPtr<gfx::DataSourceSurface> surf =
gfx::Factory::CreateWrappingDataSourceSurface(GetBuffer(),
ImageDataSerializer::ComputeRGBStride(mFormat, mSize.width), mSize, mFormat);
if (!surf) {
return false;
}
if (!mFirstSource->Update(surf.get(), regionToUpdate)) {
NS_WARNING("failed to update the DataTextureSource");
return false;
}
}
MOZ_ASSERT(mFirstSource);
return true;
}
already_AddRefed<gfx::DataSourceSurface>
BufferTextureHost::GetAsSurface()
{
RefPtr<gfx::DataSourceSurface> result;
if (mFormat == gfx::SurfaceFormat::UNKNOWN) {
NS_WARNING("BufferTextureHost: unsupported format!");
return nullptr;
} else if (mFormat == gfx::SurfaceFormat::YUV) {
result = ImageDataSerializer::DataSourceSurfaceFromYCbCrDescriptor(
GetBuffer(), mDescriptor.get_YCbCrDescriptor());
if (NS_WARN_IF(!result)) {
return nullptr;
}
} else {
result =
gfx::Factory::CreateWrappingDataSourceSurface(GetBuffer(),
ImageDataSerializer::GetRGBStride(mDescriptor.get_RGBDescriptor()),
mSize, mFormat);
}
return result.forget();
}
ShmemTextureHost::ShmemTextureHost(const ipc::Shmem& aShmem,
const BufferDescriptor& aDesc,
ISurfaceAllocator* aDeallocator,
TextureFlags aFlags)
: BufferTextureHost(aDesc, aFlags)
, mDeallocator(aDeallocator)
{
if (aShmem.IsReadable()) {
mShmem = MakeUnique<ipc::Shmem>(aShmem);
} else {
// This can happen if we failed to map the shmem on this process, perhaps
// because it was big and we didn't have enough contiguous address space
// available, even though we did on the child process.
// As a result this texture will be in an invalid state and Lock will
// always fail.
gfxCriticalNote << "Failed to create a valid ShmemTextureHost";
}
MOZ_COUNT_CTOR(ShmemTextureHost);
}
ShmemTextureHost::~ShmemTextureHost()
{
MOZ_ASSERT(!mShmem || (mFlags & TextureFlags::DEALLOCATE_CLIENT),
"Leaking our buffer");
DeallocateDeviceData();
MOZ_COUNT_DTOR(ShmemTextureHost);
}
void
ShmemTextureHost::DeallocateSharedData()
{
if (mShmem) {
MOZ_ASSERT(mDeallocator,
"Shared memory would leak without a ISurfaceAllocator");
mDeallocator->AsShmemAllocator()->DeallocShmem(*mShmem);
mShmem = nullptr;
}
}
void
ShmemTextureHost::ForgetSharedData()
{
if (mShmem) {
mShmem = nullptr;
}
}
void
ShmemTextureHost::OnShutdown()
{
mShmem = nullptr;
}
uint8_t* ShmemTextureHost::GetBuffer()
{
return mShmem ? mShmem->get<uint8_t>() : nullptr;
}
size_t ShmemTextureHost::GetBufferSize()
{
return mShmem ? mShmem->Size<uint8_t>() : 0;
}
MemoryTextureHost::MemoryTextureHost(uint8_t* aBuffer,
const BufferDescriptor& aDesc,
TextureFlags aFlags)
: BufferTextureHost(aDesc, aFlags)
, mBuffer(aBuffer)
{
MOZ_COUNT_CTOR(MemoryTextureHost);
}
MemoryTextureHost::~MemoryTextureHost()
{
MOZ_ASSERT(!mBuffer || (mFlags & TextureFlags::DEALLOCATE_CLIENT),
"Leaking our buffer");
DeallocateDeviceData();
MOZ_COUNT_DTOR(MemoryTextureHost);
}
void
MemoryTextureHost::DeallocateSharedData()
{
if (mBuffer) {
GfxMemoryImageReporter::WillFree(mBuffer);
}
delete[] mBuffer;
mBuffer = nullptr;
}
void
MemoryTextureHost::ForgetSharedData()
{
mBuffer = nullptr;
}
uint8_t* MemoryTextureHost::GetBuffer()
{
return mBuffer;
}
size_t MemoryTextureHost::GetBufferSize()
{
// MemoryTextureHost just trusts that the buffer size is large enough to read
// anything we need to. That's because MemoryTextureHost has to trust the buffer
// pointer anyway, so the security model here is just that MemoryTexture's
// are restricted to same-process clients.
return std::numeric_limits<size_t>::max();
}
TextureParent::TextureParent(HostIPCAllocator* aSurfaceAllocator, uint64_t aSerial, const wr::MaybeExternalImageId& aExternalImageId)
: mSurfaceAllocator(aSurfaceAllocator)
, mSerial(aSerial)
, mExternalImageId(aExternalImageId)
{
MOZ_COUNT_CTOR(TextureParent);
}
TextureParent::~TextureParent()
{
MOZ_COUNT_DTOR(TextureParent);
}
void
TextureParent::NotifyNotUsed(uint64_t aTransactionId)
{
if (!mTextureHost) {
return;
}
mSurfaceAllocator->NotifyNotUsed(this, aTransactionId);
}
bool
TextureParent::Init(const SurfaceDescriptor& aSharedData,
const LayersBackend& aBackend,
const TextureFlags& aFlags)
{
mTextureHost = TextureHost::Create(aSharedData,
mSurfaceAllocator,
aBackend,
aFlags,
mExternalImageId);
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();
}
mozilla::ipc::IPCResult
TextureParent::RecvRecycleTexture(const TextureFlags& aTextureFlags)
{
if (!mTextureHost) {
return IPC_OK();
}
mTextureHost->RecycleTexture(aTextureFlags);
return IPC_OK();
}
////////////////////////////////////////////////////////////////////////////////
} // namespace layers
} // namespace mozilla