1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "BufferTexture.h"
12 #include "mozilla/fallible.h"
13 #include "mozilla/gfx/2D.h"
14 #include "mozilla/gfx/Logging.h"
15 #include "mozilla/layers/CompositableForwarder.h"
16 #include "mozilla/layers/ISurfaceAllocator.h"
17 #include "mozilla/layers/ImageDataSerializer.h"
20 # include "gfxPlatformGtk.h"
23 using mozilla::ipc::IShmemAllocator
;
28 class MemoryTextureData
: public BufferTextureData
{
30 static MemoryTextureData
* Create(gfx::IntSize aSize
,
31 gfx::SurfaceFormat aFormat
,
32 gfx::BackendType aMoz2DBackend
,
33 LayersBackend aLayersBackend
,
35 TextureAllocationFlags aAllocFlags
,
36 IShmemAllocator
* aAllocator
);
38 virtual TextureData
* CreateSimilar(
39 LayersIPCChannel
* aAllocator
, LayersBackend aLayersBackend
,
40 TextureFlags aFlags
= TextureFlags::DEFAULT
,
41 TextureAllocationFlags aAllocFlags
= ALLOC_DEFAULT
) const override
;
43 virtual bool Serialize(SurfaceDescriptor
& aOutDescriptor
) override
;
45 virtual void Deallocate(LayersIPCChannel
*) override
;
47 MemoryTextureData(const BufferDescriptor
& aDesc
,
48 gfx::BackendType aMoz2DBackend
, uint8_t* aBuffer
,
50 : BufferTextureData(aDesc
, aMoz2DBackend
),
52 mBufferSize(aBufferSize
) {
54 MOZ_ASSERT(aBufferSize
);
57 virtual uint8_t* GetBuffer() override
{ return mBuffer
; }
59 virtual size_t GetBufferSize() override
{ return mBufferSize
; }
66 class ShmemTextureData
: public BufferTextureData
{
68 static ShmemTextureData
* Create(gfx::IntSize aSize
,
69 gfx::SurfaceFormat aFormat
,
70 gfx::BackendType aMoz2DBackend
,
71 LayersBackend aLayersBackend
,
73 TextureAllocationFlags aAllocFlags
,
74 IShmemAllocator
* aAllocator
);
76 virtual TextureData
* CreateSimilar(
77 LayersIPCChannel
* aAllocator
, LayersBackend aLayersBackend
,
78 TextureFlags aFlags
= TextureFlags::DEFAULT
,
79 TextureAllocationFlags aAllocFlags
= ALLOC_DEFAULT
) const override
;
81 virtual bool Serialize(SurfaceDescriptor
& aOutDescriptor
) override
;
83 virtual void Deallocate(LayersIPCChannel
* aAllocator
) override
;
85 ShmemTextureData(const BufferDescriptor
& aDesc
,
86 gfx::BackendType aMoz2DBackend
, mozilla::ipc::Shmem aShmem
)
87 : BufferTextureData(aDesc
, aMoz2DBackend
), mShmem(aShmem
) {
88 MOZ_ASSERT(mShmem
.Size
<uint8_t>());
91 virtual uint8_t* GetBuffer() override
{ return mShmem
.get
<uint8_t>(); }
93 virtual size_t GetBufferSize() override
{ return mShmem
.Size
<uint8_t>(); }
96 mozilla::ipc::Shmem mShmem
;
99 static bool UsingX11Compositor() {
100 #ifdef MOZ_WIDGET_GTK
101 return gfx::gfxVars::UseXRender();
106 bool ComputeHasIntermediateBuffer(gfx::SurfaceFormat aFormat
,
107 LayersBackend aLayersBackend
,
108 bool aSupportsTextureDirectMapping
) {
109 if (aSupportsTextureDirectMapping
) {
113 return aLayersBackend
!= LayersBackend::LAYERS_BASIC
||
114 UsingX11Compositor() || aFormat
== gfx::SurfaceFormat::UNKNOWN
;
117 BufferTextureData
* BufferTextureData::Create(
118 gfx::IntSize aSize
, gfx::SurfaceFormat aFormat
,
119 gfx::BackendType aMoz2DBackend
, LayersBackend aLayersBackend
,
120 TextureFlags aFlags
, TextureAllocationFlags aAllocFlags
,
121 mozilla::ipc::IShmemAllocator
* aAllocator
, bool aIsSameProcess
) {
122 if (!aAllocator
|| aIsSameProcess
) {
123 return MemoryTextureData::Create(aSize
, aFormat
, aMoz2DBackend
,
124 aLayersBackend
, aFlags
, aAllocFlags
,
127 return ShmemTextureData::Create(aSize
, aFormat
, aMoz2DBackend
,
128 aLayersBackend
, aFlags
, aAllocFlags
,
133 BufferTextureData
* BufferTextureData::CreateInternal(
134 LayersIPCChannel
* aAllocator
, const BufferDescriptor
& aDesc
,
135 gfx::BackendType aMoz2DBackend
, int32_t aBufferSize
,
136 TextureFlags aTextureFlags
) {
137 if (!aAllocator
|| aAllocator
->IsSameProcess()) {
138 uint8_t* buffer
= new (fallible
) uint8_t[aBufferSize
];
143 GfxMemoryImageReporter::DidAlloc(buffer
);
145 return new MemoryTextureData(aDesc
, aMoz2DBackend
, buffer
, aBufferSize
);
148 if (!aAllocator
->AllocUnsafeShmem(aBufferSize
, OptimalShmemType(), &shm
)) {
152 return new ShmemTextureData(aDesc
, aMoz2DBackend
, shm
);
156 BufferTextureData
* BufferTextureData::CreateForYCbCr(
157 KnowsCompositor
* aAllocator
, gfx::IntSize aYSize
, uint32_t aYStride
,
158 gfx::IntSize aCbCrSize
, uint32_t aCbCrStride
, StereoMode aStereoMode
,
159 gfx::ColorDepth aColorDepth
, gfx::YUVColorSpace aYUVColorSpace
,
160 gfx::ColorRange aColorRange
, TextureFlags aTextureFlags
) {
161 uint32_t bufSize
= ImageDataSerializer::ComputeYCbCrBufferSize(
162 aYSize
, aYStride
, aCbCrSize
, aCbCrStride
);
170 ImageDataSerializer::ComputeYCbCrOffsets(aYStride
, aYSize
.height
, aCbCrStride
,
171 aCbCrSize
.height
, yOffset
, cbOffset
,
174 bool supportsTextureDirectMapping
=
175 aAllocator
->SupportsTextureDirectMapping() &&
176 aAllocator
->GetMaxTextureSize() >
177 std::max(aYSize
.width
,
178 std::max(aYSize
.height
,
179 std::max(aCbCrSize
.width
, aCbCrSize
.height
)));
181 bool hasIntermediateBuffer
=
183 ? ComputeHasIntermediateBuffer(gfx::SurfaceFormat::YUV
,
184 aAllocator
->GetCompositorBackendType(),
185 supportsTextureDirectMapping
)
188 YCbCrDescriptor descriptor
=
189 YCbCrDescriptor(aYSize
, aYStride
, aCbCrSize
, aCbCrStride
, yOffset
,
190 cbOffset
, crOffset
, aStereoMode
, aColorDepth
,
191 aYUVColorSpace
, aColorRange
, hasIntermediateBuffer
);
193 return CreateInternal(
194 aAllocator
? aAllocator
->GetTextureForwarder() : nullptr, descriptor
,
195 gfx::BackendType::NONE
, bufSize
, aTextureFlags
);
198 void BufferTextureData::FillInfo(TextureData::Info
& aInfo
) const {
199 aInfo
.size
= GetSize();
200 aInfo
.format
= GetFormat();
201 aInfo
.hasSynchronization
= false;
202 aInfo
.canExposeMappedData
= true;
204 if (mDescriptor
.type() == BufferDescriptor::TYCbCrDescriptor
) {
205 aInfo
.hasIntermediateBuffer
=
206 mDescriptor
.get_YCbCrDescriptor().hasIntermediateBuffer();
208 aInfo
.hasIntermediateBuffer
=
209 mDescriptor
.get_RGBDescriptor().hasIntermediateBuffer();
212 switch (aInfo
.format
) {
213 case gfx::SurfaceFormat::YUV
:
214 case gfx::SurfaceFormat::UNKNOWN
:
215 aInfo
.supportsMoz2D
= false;
218 aInfo
.supportsMoz2D
= true;
222 gfx::IntSize
BufferTextureData::GetSize() const {
223 return ImageDataSerializer::SizeFromBufferDescriptor(mDescriptor
);
226 Maybe
<gfx::IntSize
> BufferTextureData::GetCbCrSize() const {
227 return ImageDataSerializer::CbCrSizeFromBufferDescriptor(mDescriptor
);
230 Maybe
<gfx::YUVColorSpace
> BufferTextureData::GetYUVColorSpace() const {
231 return ImageDataSerializer::YUVColorSpaceFromBufferDescriptor(mDescriptor
);
234 Maybe
<gfx::ColorDepth
> BufferTextureData::GetColorDepth() const {
235 return ImageDataSerializer::ColorDepthFromBufferDescriptor(mDescriptor
);
238 Maybe
<StereoMode
> BufferTextureData::GetStereoMode() const {
239 return ImageDataSerializer::StereoModeFromBufferDescriptor(mDescriptor
);
242 gfx::SurfaceFormat
BufferTextureData::GetFormat() const {
243 return ImageDataSerializer::FormatFromBufferDescriptor(mDescriptor
);
246 already_AddRefed
<gfx::DrawTarget
> BufferTextureData::BorrowDrawTarget() {
247 if (mDescriptor
.type() != BufferDescriptor::TRGBDescriptor
) {
251 const RGBDescriptor
& rgb
= mDescriptor
.get_RGBDescriptor();
253 uint32_t stride
= ImageDataSerializer::GetRGBStride(rgb
);
254 RefPtr
<gfx::DrawTarget
> dt
;
255 if (gfx::Factory::DoesBackendSupportDataDrawtarget(mMoz2DBackend
)) {
256 dt
= gfx::Factory::CreateDrawTargetForData(
257 mMoz2DBackend
, GetBuffer(), rgb
.size(), stride
, rgb
.format(), true);
260 // Fall back to supported platform backend. Note that mMoz2DBackend
261 // does not match the draw target type.
262 dt
= gfxPlatform::CreateDrawTargetForData(GetBuffer(), rgb
.size(), stride
,
267 gfxCriticalNote
<< "BorrowDrawTarget failure, original backend "
268 << (int)mMoz2DBackend
;
274 bool BufferTextureData::BorrowMappedData(MappedTextureData
& aData
) {
275 if (GetFormat() == gfx::SurfaceFormat::YUV
) {
279 gfx::IntSize size
= GetSize();
281 aData
.data
= GetBuffer();
283 aData
.format
= GetFormat();
285 ImageDataSerializer::ComputeRGBStride(aData
.format
, size
.width
);
290 bool BufferTextureData::BorrowMappedYCbCrData(MappedYCbCrTextureData
& aMap
) {
291 if (mDescriptor
.type() != BufferDescriptor::TYCbCrDescriptor
) {
295 const YCbCrDescriptor
& desc
= mDescriptor
.get_YCbCrDescriptor();
297 uint8_t* data
= GetBuffer();
298 auto ySize
= desc
.ySize();
299 auto cbCrSize
= desc
.cbCrSize();
301 aMap
.stereoMode
= desc
.stereoMode();
302 aMap
.metadata
= nullptr;
303 uint32_t bytesPerPixel
=
304 BytesPerPixel(SurfaceFormatForColorDepth(desc
.colorDepth()));
306 aMap
.y
.data
= data
+ desc
.yOffset();
308 aMap
.y
.stride
= desc
.yStride();
310 aMap
.y
.bytesPerPixel
= bytesPerPixel
;
312 aMap
.cb
.data
= data
+ desc
.cbOffset();
313 aMap
.cb
.size
= cbCrSize
;
314 aMap
.cb
.stride
= desc
.cbCrStride();
316 aMap
.cb
.bytesPerPixel
= bytesPerPixel
;
318 aMap
.cr
.data
= data
+ desc
.crOffset();
319 aMap
.cr
.size
= cbCrSize
;
320 aMap
.cr
.stride
= desc
.cbCrStride();
322 aMap
.cr
.bytesPerPixel
= bytesPerPixel
;
327 bool BufferTextureData::UpdateFromSurface(gfx::SourceSurface
* aSurface
) {
328 if (mDescriptor
.type() != BufferDescriptor::TRGBDescriptor
) {
331 const RGBDescriptor
& rgb
= mDescriptor
.get_RGBDescriptor();
333 uint32_t stride
= ImageDataSerializer::GetRGBStride(rgb
);
334 RefPtr
<gfx::DataSourceSurface
> surface
=
335 gfx::Factory::CreateWrappingDataSourceSurface(GetBuffer(), stride
,
336 rgb
.size(), rgb
.format());
339 gfxCriticalError() << "Failed to get serializer as surface!";
343 RefPtr
<gfx::DataSourceSurface
> srcSurf
= aSurface
->GetDataSurface();
346 gfxCriticalError() << "Failed to GetDataSurface in UpdateFromSurface (BT).";
350 if (surface
->GetSize() != srcSurf
->GetSize() ||
351 surface
->GetFormat() != srcSurf
->GetFormat()) {
352 gfxCriticalError() << "Attempt to update texture client from a surface "
353 "with a different size or format (BT)! This: "
354 << surface
->GetSize() << " " << surface
->GetFormat()
355 << " Other: " << aSurface
->GetSize() << " "
356 << aSurface
->GetFormat();
360 gfx::DataSourceSurface::MappedSurface sourceMap
;
361 gfx::DataSourceSurface::MappedSurface destMap
;
362 if (!srcSurf
->Map(gfx::DataSourceSurface::READ
, &sourceMap
)) {
364 << "Failed to map source surface for UpdateFromSurface (BT).";
368 if (!surface
->Map(gfx::DataSourceSurface::WRITE
, &destMap
)) {
371 << "Failed to map destination surface for UpdateFromSurface.";
375 for (int y
= 0; y
< srcSurf
->GetSize().height
; y
++) {
376 memcpy(destMap
.mData
+ destMap
.mStride
* y
,
377 sourceMap
.mData
+ sourceMap
.mStride
* y
,
378 srcSurf
->GetSize().width
* BytesPerPixel(srcSurf
->GetFormat()));
387 void BufferTextureData::SetDescriptor(BufferDescriptor
&& aDescriptor
) {
388 MOZ_ASSERT(mDescriptor
.type() == BufferDescriptor::TYCbCrDescriptor
);
389 MOZ_ASSERT(mDescriptor
.get_YCbCrDescriptor().ySize() == gfx::IntSize());
390 mDescriptor
= std::move(aDescriptor
);
393 bool MemoryTextureData::Serialize(SurfaceDescriptor
& aOutDescriptor
) {
394 MOZ_ASSERT(GetFormat() != gfx::SurfaceFormat::UNKNOWN
);
395 if (GetFormat() == gfx::SurfaceFormat::UNKNOWN
) {
399 uintptr_t ptr
= reinterpret_cast<uintptr_t>(mBuffer
);
400 aOutDescriptor
= SurfaceDescriptorBuffer(mDescriptor
, MemoryOrShmem(ptr
));
405 static bool InitBuffer(uint8_t* buf
, size_t bufSize
, gfx::SurfaceFormat aFormat
,
406 TextureAllocationFlags aAllocFlags
, bool aAlreadyZero
) {
408 gfxDebug() << "BufferTextureData: Failed to allocate " << bufSize
413 if ((aAllocFlags
& ALLOC_CLEAR_BUFFER
) ||
414 (aAllocFlags
& ALLOC_CLEAR_BUFFER_BLACK
)) {
415 if (aFormat
== gfx::SurfaceFormat::B8G8R8X8
) {
416 // Even though BGRX was requested, XRGB_UINT32 is what is meant,
417 // so use 0xFF000000 to put alpha in the right place.
418 libyuv::ARGBRect(buf
, bufSize
, 0, 0, bufSize
/ sizeof(uint32_t), 1,
420 } else if (!aAlreadyZero
) {
421 memset(buf
, 0, bufSize
);
425 if (aAllocFlags
& ALLOC_CLEAR_BUFFER_WHITE
) {
426 memset(buf
, 0xFF, bufSize
);
432 MemoryTextureData
* MemoryTextureData::Create(gfx::IntSize aSize
,
433 gfx::SurfaceFormat aFormat
,
434 gfx::BackendType aMoz2DBackend
,
435 LayersBackend aLayersBackend
,
437 TextureAllocationFlags aAllocFlags
,
438 IShmemAllocator
* aAllocator
) {
439 // Should have used CreateForYCbCr.
440 MOZ_ASSERT(aFormat
!= gfx::SurfaceFormat::YUV
);
442 if (aSize
.width
<= 0 || aSize
.height
<= 0) {
443 gfxDebug() << "Asking for buffer of invalid size " << aSize
.width
<< "x"
448 uint32_t bufSize
= ImageDataSerializer::ComputeRGBBufferSize(aSize
, aFormat
);
453 uint8_t* buf
= new (fallible
) uint8_t[bufSize
];
454 if (!InitBuffer(buf
, bufSize
, aFormat
, aAllocFlags
, false)) {
458 bool hasIntermediateBuffer
= ComputeHasIntermediateBuffer(
459 aFormat
, aLayersBackend
, aAllocFlags
& ALLOC_ALLOW_DIRECT_MAPPING
);
461 GfxMemoryImageReporter::DidAlloc(buf
);
463 BufferDescriptor descriptor
=
464 RGBDescriptor(aSize
, aFormat
, hasIntermediateBuffer
);
466 return new MemoryTextureData(descriptor
, aMoz2DBackend
, buf
, bufSize
);
469 void MemoryTextureData::Deallocate(LayersIPCChannel
*) {
471 GfxMemoryImageReporter::WillFree(mBuffer
);
476 TextureData
* MemoryTextureData::CreateSimilar(
477 LayersIPCChannel
* aAllocator
, LayersBackend aLayersBackend
,
478 TextureFlags aFlags
, TextureAllocationFlags aAllocFlags
) const {
479 return MemoryTextureData::Create(GetSize(), GetFormat(), mMoz2DBackend
,
480 aLayersBackend
, aFlags
, aAllocFlags
,
484 bool ShmemTextureData::Serialize(SurfaceDescriptor
& aOutDescriptor
) {
485 MOZ_ASSERT(GetFormat() != gfx::SurfaceFormat::UNKNOWN
);
486 if (GetFormat() == gfx::SurfaceFormat::UNKNOWN
) {
491 SurfaceDescriptorBuffer(mDescriptor
, MemoryOrShmem(std::move(mShmem
)));
496 ShmemTextureData
* ShmemTextureData::Create(gfx::IntSize aSize
,
497 gfx::SurfaceFormat aFormat
,
498 gfx::BackendType aMoz2DBackend
,
499 LayersBackend aLayersBackend
,
501 TextureAllocationFlags aAllocFlags
,
502 IShmemAllocator
* aAllocator
) {
503 MOZ_ASSERT(aAllocator
);
504 // Should have used CreateForYCbCr.
505 MOZ_ASSERT(aFormat
!= gfx::SurfaceFormat::YUV
);
511 if (aSize
.width
<= 0 || aSize
.height
<= 0) {
512 gfxDebug() << "Asking for buffer of invalid size " << aSize
.width
<< "x"
517 uint32_t bufSize
= ImageDataSerializer::ComputeRGBBufferSize(aSize
, aFormat
);
522 mozilla::ipc::Shmem shm
;
523 if (!aAllocator
->AllocUnsafeShmem(bufSize
, OptimalShmemType(), &shm
)) {
527 uint8_t* buf
= shm
.get
<uint8_t>();
528 if (!InitBuffer(buf
, bufSize
, aFormat
, aAllocFlags
, true)) {
532 bool hasIntermediateBuffer
= ComputeHasIntermediateBuffer(
533 aFormat
, aLayersBackend
, aAllocFlags
& ALLOC_ALLOW_DIRECT_MAPPING
);
535 BufferDescriptor descriptor
=
536 RGBDescriptor(aSize
, aFormat
, hasIntermediateBuffer
);
538 return new ShmemTextureData(descriptor
, aMoz2DBackend
, shm
);
541 TextureData
* ShmemTextureData::CreateSimilar(
542 LayersIPCChannel
* aAllocator
, LayersBackend aLayersBackend
,
543 TextureFlags aFlags
, TextureAllocationFlags aAllocFlags
) const {
544 return ShmemTextureData::Create(GetSize(), GetFormat(), mMoz2DBackend
,
545 aLayersBackend
, aFlags
, aAllocFlags
,
549 void ShmemTextureData::Deallocate(LayersIPCChannel
* aAllocator
) {
550 aAllocator
->DeallocShmem(mShmem
);
553 } // namespace layers
554 } // namespace mozilla