Bug 1250500 - Avoid copying BufferTextureHost when possible. r=sotaro
--- a/gfx/layers/BufferTexture.cpp
+++ b/gfx/layers/BufferTexture.cpp
@@ -1,20 +1,25 @@
/* -*- 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 "BufferTexture.h"
#include "mozilla/layers/ImageDataSerializer.h"
#include "mozilla/layers/ISurfaceAllocator.h"
+#include "mozilla/layers/CompositableForwarder.h"
#include "mozilla/gfx/Logging.h"
#include "mozilla/gfx/2D.h"
#include "mozilla/fallible.h"
+#ifdef MOZ_WIDGET_GTK
+#include "gfxPlatformGtk.h"
+#endif
+
namespace mozilla {
namespace layers {
class MemoryTextureData : public BufferTextureData
{
public:
static MemoryTextureData* Create(gfx::IntSize aSize, gfx::SurfaceFormat aFormat,
gfx::BackendType aMoz2DBackend,TextureFlags aFlags,
@@ -78,26 +83,46 @@ public:
virtual uint8_t* GetBuffer() override { return mShmem.get<uint8_t>(); }
virtual size_t GetBufferSize() override { return mShmem.Size<uint8_t>(); }
protected:
mozilla::ipc::Shmem mShmem;
};
+static bool UsingX11Compositor()
+{
+#ifdef MOZ_WIDGET_GTK
+ return gfxPlatformGtk::GetPlatform()->UseXRender();
+#endif
+ return false;
+}
+
+static bool ComputeHasIntermediateBuffer(gfx::SurfaceFormat aFormat,
+ LayersBackend aLayersBackend)
+{
+ return aLayersBackend != LayersBackend::LAYERS_BASIC
+ || UsingX11Compositor()
+ || aFormat == gfx::SurfaceFormat::UNKNOWN
+ || aFormat == gfx::SurfaceFormat::YUV
+ || aFormat == gfx::SurfaceFormat::NV12;
+}
+
BufferTextureData*
BufferTextureData::Create(gfx::IntSize aSize, gfx::SurfaceFormat aFormat,
gfx::BackendType aMoz2DBackend, TextureFlags aFlags,
TextureAllocationFlags aAllocFlags,
ISurfaceAllocator* aAllocator)
{
if (!aAllocator || aAllocator->IsSameProcess()) {
- return MemoryTextureData::Create(aSize, aFormat, aMoz2DBackend, aFlags, aAllocFlags, aAllocator);
+ return MemoryTextureData::Create(aSize, aFormat, aMoz2DBackend, aFlags,
+ aAllocFlags, aAllocator);
} else {
- return ShmemTextureData::Create(aSize, aFormat, aMoz2DBackend, aFlags, aAllocFlags, aAllocator);
+ return ShmemTextureData::Create(aSize, aFormat, aMoz2DBackend, aFlags,
+ aAllocFlags, aAllocator);
}
}
BufferTextureData*
BufferTextureData::CreateInternal(ISurfaceAllocator* aAllocator,
const BufferDescriptor& aDesc,
gfx::BackendType aMoz2DBackend,
int32_t aBufferSize,
@@ -174,16 +199,26 @@ BufferTextureData::GetSize() const
}
gfx::SurfaceFormat
BufferTextureData::GetFormat() const
{
return ImageDataSerializer::FormatFromBufferDescriptor(mDescriptor);
}
+
+bool
+BufferTextureData::HasInternalBuffer() const
+{
+ if (mDescriptor.type() == BufferDescriptor::TYCbCrDescriptor) {
+ return true;
+ }
+ return mDescriptor.get_RGBDescriptor().hasInternalBuffer();
+}
+
bool
BufferTextureData::SupportsMoz2D() const
{
switch (GetFormat()) {
case gfx::SurfaceFormat::YUV:
case gfx::SurfaceFormat::NV12:
case gfx::SurfaceFormat::UNKNOWN:
return false;
@@ -380,17 +415,17 @@ static bool InitBuffer(uint8_t* buf, siz
return true;
}
MemoryTextureData*
MemoryTextureData::Create(gfx::IntSize aSize, gfx::SurfaceFormat aFormat,
gfx::BackendType aMoz2DBackend, TextureFlags aFlags,
TextureAllocationFlags aAllocFlags,
- ISurfaceAllocator*)
+ ISurfaceAllocator* aAllocator)
{
// Should have used CreateForYCbCr.
MOZ_ASSERT(aFormat != gfx::SurfaceFormat::YUV);
if (aSize.width <= 0 || aSize.height <= 0) {
gfxDebug() << "Asking for buffer of invalid size " << aSize.width << "x" << aSize.height;
return nullptr;
}
@@ -400,19 +435,24 @@ MemoryTextureData::Create(gfx::IntSize a
return nullptr;
}
uint8_t* buf = new (fallible) uint8_t[bufSize];
if (!InitBuffer(buf, bufSize, aAllocFlags)) {
return nullptr;
}
+ auto fwd = aAllocator ? aAllocator->AsCompositableForwarder() : nullptr;
+ bool hasInternalBuffer = fwd ? ComputeHasIntermediateBuffer(aFormat,
+ fwd->GetCompositorBackendType())
+ : true;
+
GfxMemoryImageReporter::DidAlloc(buf);
- BufferDescriptor descriptor = RGBDescriptor(aSize, aFormat);
+ BufferDescriptor descriptor = RGBDescriptor(aSize, aFormat, hasInternalBuffer);
return new MemoryTextureData(descriptor, aMoz2DBackend, buf, bufSize);
}
void
MemoryTextureData::Deallocate(ISurfaceAllocator*)
{
MOZ_ASSERT(mBuffer);
@@ -472,17 +512,22 @@ ShmemTextureData::Create(gfx::IntSize aS
return nullptr;
}
uint8_t* buf = shm.get<uint8_t>();
if (!InitBuffer(buf, bufSize, aAllocFlags)) {
return nullptr;
}
- BufferDescriptor descriptor = RGBDescriptor(aSize, aFormat);
+ auto fwd = aAllocator->AsCompositableForwarder();
+ bool hasInternalBuffer = fwd ? ComputeHasIntermediateBuffer(aFormat,
+ fwd->GetCompositorBackendType())
+ : true;
+
+ BufferDescriptor descriptor = RGBDescriptor(aSize, aFormat, hasInternalBuffer);
return new ShmemTextureData(descriptor, aMoz2DBackend, shm);
return nullptr;
}
TextureData*
ShmemTextureData::CreateSimilar(ISurfaceAllocator* aAllocator,
--- a/gfx/layers/BufferTexture.h
+++ b/gfx/layers/BufferTexture.h
@@ -50,17 +50,17 @@ public:
virtual bool CanExposeMappedData() const override { return true; }
virtual bool BorrowMappedData(MappedTextureData& aMap) override;
virtual bool BorrowMappedYCbCrData(MappedYCbCrTextureData& aMap) override;
virtual bool SupportsMoz2D() const override;
- virtual bool HasInternalBuffer() const override { return true; }
+ virtual bool HasInternalBuffer() const override;
// use TextureClient's default implementation
virtual bool UpdateFromSurface(gfx::SourceSurface* aSurface) override;
// Don't use this.
void SetDesciptor(const BufferDescriptor& aDesc);
protected:
--- a/gfx/layers/Compositor.h
+++ b/gfx/layers/Compositor.h
@@ -107,16 +107,17 @@
*/
class nsIWidget;
namespace mozilla {
namespace gfx {
class Matrix;
class DrawTarget;
+class DataSourceSurface;
} // namespace gfx
namespace layers {
struct Effect;
struct EffectChain;
class Image;
class ImageHostOverlay;
@@ -187,16 +188,20 @@ public:
: mCompositorID(0)
, mDiagnosticTypes(DiagnosticTypes::NO_DIAGNOSTIC)
, mParent(aParent)
, mScreenRotation(ROTATION_0)
{
}
virtual already_AddRefed<DataTextureSource> CreateDataTextureSource(TextureFlags aFlags = TextureFlags::NO_FLAGS) = 0;
+
+ virtual already_AddRefed<DataTextureSource>
+ CreateDataTextureSourceAround(gfx::DataSourceSurface* aSurface) { return nullptr; }
+
virtual bool Initialize() = 0;
virtual void Destroy() = 0;
/**
* Return true if the effect type is supported.
*
* By default Compositor implementations should support all effects but in
* some rare cases it is not possible to support an effect efficiently.
--- a/gfx/layers/basic/BasicCompositor.cpp
+++ b/gfx/layers/basic/BasicCompositor.cpp
@@ -31,16 +31,28 @@ using namespace mozilla::gfx;
namespace layers {
class DataTextureSourceBasic : public DataTextureSource
, public TextureSourceBasic
{
public:
virtual const char* Name() const override { return "DataTextureSourceBasic"; }
+ explicit DataTextureSourceBasic(DataSourceSurface* aSurface)
+ : mSurface(aSurface)
+ , mWrappingExistingData(!!aSurface)
+ {}
+
+ virtual DataTextureSource* AsDataTextureSource() override
+ {
+ // If the texture wraps someone else's memory we'd rather not use it as
+ // a DataTextureSource per say (that is call Update on it).
+ return mWrappingExistingData ? nullptr : this;
+ }
+
virtual TextureSourceBasic* AsSourceBasic() override { return this; }
virtual gfx::SourceSurface* GetSurface(DrawTarget* aTarget) override { return mSurface; }
SurfaceFormat GetFormat() const override
{
return mSurface ? mSurface->GetFormat() : gfx::SurfaceFormat::UNKNOWN;
}
@@ -49,28 +61,33 @@ public:
{
return mSurface ? mSurface->GetSize() : gfx::IntSize(0, 0);
}
virtual bool Update(gfx::DataSourceSurface* aSurface,
nsIntRegion* aDestRegion = nullptr,
gfx::IntPoint* aSrcOffset = nullptr) override
{
+ MOZ_ASSERT(!mWrappingExistingData);
+ if (mWrappingExistingData) {
+ return false;
+ }
mSurface = aSurface;
return true;
}
virtual void DeallocateDeviceData() override
{
mSurface = nullptr;
SetUpdateSerial(0);
}
public:
RefPtr<gfx::DataSourceSurface> mSurface;
+ bool mWrappingExistingData;
};
BasicCompositor::BasicCompositor(nsIWidget *aWidget)
: mWidget(aWidget)
, mDidExternalComposition(false)
{
MOZ_COUNT_CTOR(BasicCompositor);
@@ -177,17 +194,24 @@ BasicCompositor::CreateRenderTargetForWi
}
return rt.forget();
}
already_AddRefed<DataTextureSource>
BasicCompositor::CreateDataTextureSource(TextureFlags aFlags)
{
- RefPtr<DataTextureSource> result = new DataTextureSourceBasic();
+ RefPtr<DataTextureSource> result = new DataTextureSourceBasic(nullptr);
+ return result.forget();
+}
+
+already_AddRefed<DataTextureSource>
+BasicCompositor::CreateDataTextureSourceAround(DataSourceSurface* aSurface)
+{
+ RefPtr<DataTextureSource> result = new DataTextureSourceBasic(aSurface);
return result.forget();
}
bool
BasicCompositor::SupportsEffect(EffectTypes aEffect)
{
return aEffect != EffectTypes::YCBCR && aEffect != EffectTypes::COMPONENT_ALPHA;
}
--- a/gfx/layers/basic/BasicCompositor.h
+++ b/gfx/layers/basic/BasicCompositor.h
@@ -65,16 +65,19 @@ public:
virtual already_AddRefed<CompositingRenderTarget>
CreateRenderTargetForWindow(const gfx::IntRect& aRect,
SurfaceInitMode aInit,
BufferMode aBufferMode);
virtual already_AddRefed<DataTextureSource>
CreateDataTextureSource(TextureFlags aFlags = TextureFlags::NO_FLAGS) override;
+ virtual already_AddRefed<DataTextureSource>
+ CreateDataTextureSourceAround(gfx::DataSourceSurface* aSurface) override;
+
virtual bool SupportsEffect(EffectTypes aEffect) override;
virtual void SetRenderTarget(CompositingRenderTarget *aSource) override
{
mRenderTarget = static_cast<BasicCompositingRenderTarget*>(aSource);
mRenderTarget->BindRenderTarget();
}
virtual CompositingRenderTarget* GetCurrentRenderTarget() const override
--- a/gfx/layers/basic/X11BasicCompositor.h
+++ b/gfx/layers/basic/X11BasicCompositor.h
@@ -47,15 +47,18 @@ class X11BasicCompositor : public BasicC
{
public:
explicit X11BasicCompositor(nsIWidget *aWidget) : BasicCompositor(aWidget) {}
virtual already_AddRefed<DataTextureSource>
CreateDataTextureSource(TextureFlags aFlags = TextureFlags::NO_FLAGS) override;
+ virtual already_AddRefed<DataTextureSource>
+ CreateDataTextureSourceAround(gfx::DataSourceSurface* aSurface) override { return nullptr; }
+
virtual void EndFrame() override;
};
} // namespace layers
} // namespace mozilla
#endif /* MOZILLA_GFX_X11BASICCOMPOSITOR_H */
--- a/gfx/layers/composite/TextureHost.cpp
+++ b/gfx/layers/composite/TextureHost.cpp
@@ -390,22 +390,24 @@ BufferTextureHost::BufferTextureHost(con
, mNeedsFullUpdate(false)
{
mDescriptor = aDesc;
switch (mDescriptor.type()) {
case BufferDescriptor::TYCbCrDescriptor: {
const YCbCrDescriptor& ycbcr = mDescriptor.get_YCbCrDescriptor();
mSize = ycbcr.ySize();
mFormat = gfx::SurfaceFormat::YUV;
+ mHasInternalBuffer = true;
break;
}
case BufferDescriptor::TRGBDescriptor: {
const RGBDescriptor& rgb = mDescriptor.get_RGBDescriptor();
mSize = rgb.size();
mFormat = rgb.format();
+ mHasInternalBuffer = rgb.hasInternalBuffer();
break;
}
default: MOZ_CRASH();
}
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
@@ -486,19 +488,52 @@ BufferTextureHost::Lock()
void
BufferTextureHost::Unlock()
{
MOZ_ASSERT(mLocked);
mLocked = false;
}
+bool
+BufferTextureHost::EnsureWrappingTextureSource()
+{
+ MOZ_ASSERT(!mHasInternalBuffer);
+ MOZ_ASSERT(mFormat != gfx::SurfaceFormat::YUV);
+
+ if (mFirstSource) {
+ return true;
+ }
+
+ if (!mCompositor) {
+ return false;
+ }
+
+ RefPtr<gfx::DataSourceSurface> surf =
+ gfx::Factory::CreateWrappingDataSourceSurface(GetBuffer(),
+ ImageDataSerializer::ComputeRGBStride(mFormat, mSize.width), mSize, mFormat);
+
+ if (!surf) {
+ return false;
+ }
+
+ mFirstSource = mCompositor->CreateDataTextureSourceAround(surf);
+ mFirstSource->SetUpdateSerial(mUpdateSerial);
+ mFirstSource->SetOwner(this);
+
+ return true;
+}
+
void
BufferTextureHost::PrepareTextureSource(CompositableTextureSourceRef& aTexture)
{
+ if (!mHasInternalBuffer) {
+ 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.
@@ -589,16 +624,20 @@ BufferTextureHost::Upload(nsIntRegion *a
// 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 (!mHasInternalBuffer && 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 =
--- a/gfx/layers/composite/TextureHost.h
+++ b/gfx/layers/composite/TextureHost.h
@@ -625,33 +625,35 @@ public:
* GetFormat will be RGB32 (even though mFormat is SurfaceFormat::YUV).
*/
virtual gfx::SurfaceFormat GetFormat() const override;
virtual gfx::IntSize GetSize() const override { return mSize; }
virtual already_AddRefed<gfx::DataSourceSurface> GetAsSurface() override;
- virtual bool HasInternalBuffer() const override { return true; }
+ virtual bool HasInternalBuffer() const override { return mHasInternalBuffer; }
protected:
bool Upload(nsIntRegion *aRegion = nullptr);
bool MaybeUpload(nsIntRegion *aRegion = nullptr);
+ bool EnsureWrappingTextureSource();
virtual void UpdatedInternal(const nsIntRegion* aRegion = nullptr) override;
BufferDescriptor mDescriptor;
RefPtr<Compositor> mCompositor;
RefPtr<DataTextureSource> mFirstSource;
nsIntRegion mMaybeUpdatedRegion;
gfx::IntSize mSize;
gfx::SurfaceFormat mFormat;
uint32_t mUpdateSerial;
bool mLocked;
bool mNeedsFullUpdate;
+ bool mHasInternalBuffer;
};
/**
* TextureHost that wraps shared memory.
* the corresponding texture on the client side is ShmemTextureClient.
* This TextureHost is backend-independent.
*/
class ShmemTextureHost : public BufferTextureHost
--- a/gfx/layers/ipc/CompositableForwarder.h
+++ b/gfx/layers/ipc/CompositableForwarder.h
@@ -167,16 +167,18 @@ public:
{
return mTextureFactoryIdentifier;
}
int32_t GetSerial() { return mSerial; }
SyncObject* GetSyncObject() { return mSyncObject; }
+ virtual CompositableForwarder* AsCompositableForwarder() override { return this; }
+
protected:
TextureFactoryIdentifier mTextureFactoryIdentifier;
nsTArray<RefPtr<TextureClient> > mTexturesToRemove;
nsTArray<RefPtr<CompositableClient>> mCompositableClientsToRemove;
RefPtr<SyncObject> mSyncObject;
const int32_t mSerial;
static mozilla::Atomic<int32_t> sSerialCounter;
};
--- a/gfx/layers/ipc/ISurfaceAllocator.cpp
+++ b/gfx/layers/ipc/ISurfaceAllocator.cpp
@@ -142,17 +142,21 @@ ISurfaceAllocator::AllocSurfaceDescripto
mozilla::ipc::Shmem shmem;
if (!AllocUnsafeShmem(size, shmemType, &shmem)) {
return false;
}
bufferDesc = shmem;
}
- *aBuffer = SurfaceDescriptorBuffer(RGBDescriptor(aSize, format), bufferDesc);
+ // Use an intermediate buffer by default. Skipping the intermediate buffer is
+ // only possible in certain configurations so let's keep it simple here for now.
+ const bool hasIntermediateBuffer = true;
+ *aBuffer = SurfaceDescriptorBuffer(RGBDescriptor(aSize, format, hasIntermediateBuffer),
+ bufferDesc);
return true;
}
/* static */ bool
ISurfaceAllocator::IsShmem(SurfaceDescriptor* aSurface)
{
return aSurface && (aSurface->type() == SurfaceDescriptor::TSurfaceDescriptorBuffer)
--- a/gfx/layers/ipc/ISurfaceAllocator.h
+++ b/gfx/layers/ipc/ISurfaceAllocator.h
@@ -36,16 +36,17 @@ class Shmem;
} // namespace ipc
namespace gfx {
class DataSourceSurface;
} // namespace gfx
namespace layers {
class MaybeMagicGrallocBufferHandle;
+class CompositableForwarder;
enum BufferCapabilities {
DEFAULT_BUFFER_CAPS = 0,
/**
* The allocated buffer must be efficiently mappable as a DataSourceSurface.
*/
MAP_AS_IMAGE_SURFACE = 1 << 0,
/**
@@ -155,16 +156,17 @@ public:
virtual MessageLoop * GetMessageLoop() const
{
return mDefaultMessageLoop;
}
// Returns true if aSurface wraps a Shmem.
static bool IsShmem(SurfaceDescriptor* aSurface);
+ virtual CompositableForwarder* AsCompositableForwarder() { return nullptr; }
protected:
virtual bool IsOnCompositorSide() const = 0;
virtual ~ISurfaceAllocator();
void ShrinkShmemSectionHeap();
--- a/gfx/layers/ipc/LayersSurfaces.ipdlh
+++ b/gfx/layers/ipc/LayersSurfaces.ipdlh
@@ -96,16 +96,17 @@ struct SurfaceDescriptorSharedGLTexture
struct SurfaceDescriptorGralloc {
MaybeMagicGrallocBufferHandle buffer;
bool isOpaque;
};
struct RGBDescriptor {
IntSize size;
SurfaceFormat format;
+ bool hasInternalBuffer;
};
struct YCbCrDescriptor {
IntSize ySize;
IntSize cbCrSize;
uint32_t yOffset;
uint32_t cbOffset;
uint32_t crOffset;