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"
18 #include "mozilla/layers/TextureForwarder.h"
21 # include "gfxPlatformGtk.h"
24 using mozilla::ipc::IShmemAllocator
;
29 class MemoryTextureData
: public BufferTextureData
{
31 static MemoryTextureData
* Create(gfx::IntSize aSize
,
32 gfx::SurfaceFormat aFormat
,
33 gfx::BackendType aMoz2DBackend
,
34 LayersBackend aLayersBackend
,
36 TextureAllocationFlags aAllocFlags
,
37 IShmemAllocator
* aAllocator
);
39 virtual TextureData
* CreateSimilar(
40 LayersIPCChannel
* aAllocator
, LayersBackend aLayersBackend
,
41 TextureFlags aFlags
= TextureFlags::DEFAULT
,
42 TextureAllocationFlags aAllocFlags
= ALLOC_DEFAULT
) const override
;
44 virtual bool Serialize(SurfaceDescriptor
& aOutDescriptor
) override
;
46 virtual void Deallocate(LayersIPCChannel
*) override
;
48 MemoryTextureData(const BufferDescriptor
& aDesc
,
49 gfx::BackendType aMoz2DBackend
, uint8_t* aBuffer
,
51 : BufferTextureData(aDesc
, aMoz2DBackend
),
53 mBufferSize(aBufferSize
) {
55 MOZ_ASSERT(aBufferSize
);
58 virtual uint8_t* GetBuffer() override
{ return mBuffer
; }
60 virtual size_t GetBufferSize() override
{ return mBufferSize
; }
67 class ShmemTextureData
: public BufferTextureData
{
69 static ShmemTextureData
* Create(gfx::IntSize aSize
,
70 gfx::SurfaceFormat aFormat
,
71 gfx::BackendType aMoz2DBackend
,
72 LayersBackend aLayersBackend
,
74 TextureAllocationFlags aAllocFlags
,
75 IShmemAllocator
* aAllocator
);
77 virtual TextureData
* CreateSimilar(
78 LayersIPCChannel
* aAllocator
, LayersBackend aLayersBackend
,
79 TextureFlags aFlags
= TextureFlags::DEFAULT
,
80 TextureAllocationFlags aAllocFlags
= ALLOC_DEFAULT
) const override
;
82 virtual bool Serialize(SurfaceDescriptor
& aOutDescriptor
) override
;
84 virtual void Deallocate(LayersIPCChannel
* aAllocator
) override
;
86 ShmemTextureData(const BufferDescriptor
& aDesc
,
87 gfx::BackendType aMoz2DBackend
, mozilla::ipc::Shmem aShmem
)
88 : BufferTextureData(aDesc
, aMoz2DBackend
), mShmem(aShmem
) {
89 MOZ_ASSERT(mShmem
.Size
<uint8_t>());
92 virtual uint8_t* GetBuffer() override
{ return mShmem
.get
<uint8_t>(); }
94 virtual size_t GetBufferSize() override
{ return mShmem
.Size
<uint8_t>(); }
97 mozilla::ipc::Shmem mShmem
;
100 static bool UsingX11Compositor() {
101 #ifdef MOZ_WIDGET_GTK
102 return gfx::gfxVars::UseXRender();
107 bool ComputeHasIntermediateBuffer(gfx::SurfaceFormat aFormat
,
108 LayersBackend aLayersBackend
,
109 bool aSupportsTextureDirectMapping
) {
110 if (aSupportsTextureDirectMapping
) {
114 return aLayersBackend
!= LayersBackend::LAYERS_BASIC
||
115 UsingX11Compositor() || aFormat
== gfx::SurfaceFormat::UNKNOWN
;
118 BufferTextureData
* BufferTextureData::Create(
119 gfx::IntSize aSize
, gfx::SurfaceFormat aFormat
,
120 gfx::BackendType aMoz2DBackend
, LayersBackend aLayersBackend
,
121 TextureFlags aFlags
, TextureAllocationFlags aAllocFlags
,
122 mozilla::ipc::IShmemAllocator
* aAllocator
, bool aIsSameProcess
) {
123 if (!aAllocator
|| aIsSameProcess
) {
124 return MemoryTextureData::Create(aSize
, aFormat
, aMoz2DBackend
,
125 aLayersBackend
, aFlags
, aAllocFlags
,
128 return ShmemTextureData::Create(aSize
, aFormat
, aMoz2DBackend
,
129 aLayersBackend
, aFlags
, aAllocFlags
,
134 BufferTextureData
* BufferTextureData::CreateInternal(
135 LayersIPCChannel
* aAllocator
, const BufferDescriptor
& aDesc
,
136 gfx::BackendType aMoz2DBackend
, int32_t aBufferSize
,
137 TextureFlags aTextureFlags
) {
138 if (!aAllocator
|| aAllocator
->IsSameProcess()) {
139 uint8_t* buffer
= new (fallible
) uint8_t[aBufferSize
];
144 GfxMemoryImageReporter::DidAlloc(buffer
);
146 return new MemoryTextureData(aDesc
, aMoz2DBackend
, buffer
, aBufferSize
);
149 if (!aAllocator
->AllocUnsafeShmem(aBufferSize
, OptimalShmemType(), &shm
)) {
153 return new ShmemTextureData(aDesc
, aMoz2DBackend
, shm
);
157 BufferTextureData
* BufferTextureData::CreateForYCbCr(
158 KnowsCompositor
* aAllocator
, const gfx::IntRect
& aDisplay
,
159 const gfx::IntSize
& aYSize
, uint32_t aYStride
,
160 const gfx::IntSize
& aCbCrSize
, uint32_t aCbCrStride
, StereoMode aStereoMode
,
161 gfx::ColorDepth aColorDepth
, gfx::YUVColorSpace aYUVColorSpace
,
162 gfx::ColorRange aColorRange
, TextureFlags aTextureFlags
) {
163 uint32_t bufSize
= ImageDataSerializer::ComputeYCbCrBufferSize(
164 aYSize
, aYStride
, aCbCrSize
, aCbCrStride
);
172 ImageDataSerializer::ComputeYCbCrOffsets(aYStride
, aYSize
.height
, aCbCrStride
,
173 aCbCrSize
.height
, yOffset
, cbOffset
,
176 bool supportsTextureDirectMapping
=
177 aAllocator
->SupportsTextureDirectMapping() &&
178 aAllocator
->GetMaxTextureSize() >
179 std::max(aYSize
.width
,
180 std::max(aYSize
.height
,
181 std::max(aCbCrSize
.width
, aCbCrSize
.height
)));
183 bool hasIntermediateBuffer
=
185 ? ComputeHasIntermediateBuffer(gfx::SurfaceFormat::YUV
,
186 aAllocator
->GetCompositorBackendType(),
187 supportsTextureDirectMapping
)
190 YCbCrDescriptor descriptor
=
191 YCbCrDescriptor(aDisplay
, aYSize
, aYStride
, aCbCrSize
, aCbCrStride
,
192 yOffset
, cbOffset
, crOffset
, aStereoMode
, aColorDepth
,
193 aYUVColorSpace
, aColorRange
, hasIntermediateBuffer
);
195 return CreateInternal(
196 aAllocator
? aAllocator
->GetTextureForwarder() : nullptr, descriptor
,
197 gfx::BackendType::NONE
, bufSize
, aTextureFlags
);
200 void BufferTextureData::FillInfo(TextureData::Info
& aInfo
) const {
201 aInfo
.size
= GetSize();
202 aInfo
.format
= GetFormat();
203 aInfo
.hasSynchronization
= false;
204 aInfo
.canExposeMappedData
= true;
206 if (mDescriptor
.type() == BufferDescriptor::TYCbCrDescriptor
) {
207 aInfo
.hasIntermediateBuffer
=
208 mDescriptor
.get_YCbCrDescriptor().hasIntermediateBuffer();
210 aInfo
.hasIntermediateBuffer
=
211 mDescriptor
.get_RGBDescriptor().hasIntermediateBuffer();
214 switch (aInfo
.format
) {
215 case gfx::SurfaceFormat::YUV
:
216 case gfx::SurfaceFormat::UNKNOWN
:
217 aInfo
.supportsMoz2D
= false;
220 aInfo
.supportsMoz2D
= true;
224 gfx::IntSize
BufferTextureData::GetSize() const {
225 return ImageDataSerializer::SizeFromBufferDescriptor(mDescriptor
);
228 gfx::IntRect
BufferTextureData::GetPictureRect() const {
229 return ImageDataSerializer::RectFromBufferDescriptor(mDescriptor
);
232 Maybe
<gfx::IntSize
> BufferTextureData::GetCbCrSize() const {
233 return ImageDataSerializer::CbCrSizeFromBufferDescriptor(mDescriptor
);
236 Maybe
<int32_t> BufferTextureData::GetYStride() const {
237 return ImageDataSerializer::YStrideFromBufferDescriptor(mDescriptor
);
240 Maybe
<int32_t> BufferTextureData::GetCbCrStride() const {
241 return ImageDataSerializer::CbCrStrideFromBufferDescriptor(mDescriptor
);
244 Maybe
<gfx::YUVColorSpace
> BufferTextureData::GetYUVColorSpace() const {
245 return ImageDataSerializer::YUVColorSpaceFromBufferDescriptor(mDescriptor
);
248 Maybe
<gfx::ColorDepth
> BufferTextureData::GetColorDepth() const {
249 return ImageDataSerializer::ColorDepthFromBufferDescriptor(mDescriptor
);
252 Maybe
<StereoMode
> BufferTextureData::GetStereoMode() const {
253 return ImageDataSerializer::StereoModeFromBufferDescriptor(mDescriptor
);
256 gfx::SurfaceFormat
BufferTextureData::GetFormat() const {
257 return ImageDataSerializer::FormatFromBufferDescriptor(mDescriptor
);
260 already_AddRefed
<gfx::DrawTarget
> BufferTextureData::BorrowDrawTarget() {
261 if (mDescriptor
.type() != BufferDescriptor::TRGBDescriptor
) {
265 const RGBDescriptor
& rgb
= mDescriptor
.get_RGBDescriptor();
267 uint32_t stride
= ImageDataSerializer::GetRGBStride(rgb
);
268 RefPtr
<gfx::DrawTarget
> dt
;
269 if (gfx::Factory::DoesBackendSupportDataDrawtarget(mMoz2DBackend
)) {
270 dt
= gfx::Factory::CreateDrawTargetForData(
271 mMoz2DBackend
, GetBuffer(), rgb
.size(), stride
, rgb
.format(), true);
274 // Fall back to supported platform backend. Note that mMoz2DBackend
275 // does not match the draw target type.
276 dt
= gfxPlatform::CreateDrawTargetForData(GetBuffer(), rgb
.size(), stride
,
281 gfxCriticalNote
<< "BorrowDrawTarget failure, original backend "
282 << (int)mMoz2DBackend
;
288 bool BufferTextureData::BorrowMappedData(MappedTextureData
& aData
) {
289 if (GetFormat() == gfx::SurfaceFormat::YUV
) {
293 gfx::IntSize size
= GetSize();
295 aData
.data
= GetBuffer();
297 aData
.format
= GetFormat();
299 ImageDataSerializer::ComputeRGBStride(aData
.format
, size
.width
);
304 bool BufferTextureData::BorrowMappedYCbCrData(MappedYCbCrTextureData
& aMap
) {
305 if (mDescriptor
.type() != BufferDescriptor::TYCbCrDescriptor
) {
309 const YCbCrDescriptor
& desc
= mDescriptor
.get_YCbCrDescriptor();
311 uint8_t* data
= GetBuffer();
312 auto ySize
= desc
.ySize();
313 auto cbCrSize
= desc
.cbCrSize();
315 aMap
.stereoMode
= desc
.stereoMode();
316 aMap
.metadata
= nullptr;
317 uint32_t bytesPerPixel
=
318 BytesPerPixel(SurfaceFormatForColorDepth(desc
.colorDepth()));
320 aMap
.y
.data
= data
+ desc
.yOffset();
322 aMap
.y
.stride
= desc
.yStride();
324 aMap
.y
.bytesPerPixel
= bytesPerPixel
;
326 aMap
.cb
.data
= data
+ desc
.cbOffset();
327 aMap
.cb
.size
= cbCrSize
;
328 aMap
.cb
.stride
= desc
.cbCrStride();
330 aMap
.cb
.bytesPerPixel
= bytesPerPixel
;
332 aMap
.cr
.data
= data
+ desc
.crOffset();
333 aMap
.cr
.size
= cbCrSize
;
334 aMap
.cr
.stride
= desc
.cbCrStride();
336 aMap
.cr
.bytesPerPixel
= bytesPerPixel
;
341 bool BufferTextureData::UpdateFromSurface(gfx::SourceSurface
* aSurface
) {
342 if (mDescriptor
.type() != BufferDescriptor::TRGBDescriptor
) {
345 const RGBDescriptor
& rgb
= mDescriptor
.get_RGBDescriptor();
347 uint32_t stride
= ImageDataSerializer::GetRGBStride(rgb
);
348 RefPtr
<gfx::DataSourceSurface
> surface
=
349 gfx::Factory::CreateWrappingDataSourceSurface(GetBuffer(), stride
,
350 rgb
.size(), rgb
.format());
353 gfxCriticalError() << "Failed to get serializer as surface!";
357 RefPtr
<gfx::DataSourceSurface
> srcSurf
= aSurface
->GetDataSurface();
360 gfxCriticalError() << "Failed to GetDataSurface in UpdateFromSurface (BT).";
364 if (surface
->GetSize() != srcSurf
->GetSize() ||
365 surface
->GetFormat() != srcSurf
->GetFormat()) {
366 gfxCriticalError() << "Attempt to update texture client from a surface "
367 "with a different size or format (BT)! This: "
368 << surface
->GetSize() << " " << surface
->GetFormat()
369 << " Other: " << aSurface
->GetSize() << " "
370 << aSurface
->GetFormat();
374 gfx::DataSourceSurface::MappedSurface sourceMap
;
375 gfx::DataSourceSurface::MappedSurface destMap
;
376 if (!srcSurf
->Map(gfx::DataSourceSurface::READ
, &sourceMap
)) {
378 << "Failed to map source surface for UpdateFromSurface (BT).";
382 if (!surface
->Map(gfx::DataSourceSurface::WRITE
, &destMap
)) {
385 << "Failed to map destination surface for UpdateFromSurface.";
389 for (int y
= 0; y
< srcSurf
->GetSize().height
; y
++) {
390 memcpy(destMap
.mData
+ destMap
.mStride
* y
,
391 sourceMap
.mData
+ sourceMap
.mStride
* y
,
392 srcSurf
->GetSize().width
* BytesPerPixel(srcSurf
->GetFormat()));
401 void BufferTextureData::SetDescriptor(BufferDescriptor
&& aDescriptor
) {
402 MOZ_ASSERT(mDescriptor
.type() == BufferDescriptor::TYCbCrDescriptor
);
403 MOZ_ASSERT(mDescriptor
.get_YCbCrDescriptor().ySize() == gfx::IntSize());
404 mDescriptor
= std::move(aDescriptor
);
407 bool MemoryTextureData::Serialize(SurfaceDescriptor
& aOutDescriptor
) {
408 MOZ_ASSERT(GetFormat() != gfx::SurfaceFormat::UNKNOWN
);
409 if (GetFormat() == gfx::SurfaceFormat::UNKNOWN
) {
413 uintptr_t ptr
= reinterpret_cast<uintptr_t>(mBuffer
);
414 aOutDescriptor
= SurfaceDescriptorBuffer(mDescriptor
, MemoryOrShmem(ptr
));
419 static bool InitBuffer(uint8_t* buf
, size_t bufSize
, gfx::SurfaceFormat aFormat
,
420 TextureAllocationFlags aAllocFlags
, bool aAlreadyZero
) {
422 gfxDebug() << "BufferTextureData: Failed to allocate " << bufSize
427 if ((aAllocFlags
& ALLOC_CLEAR_BUFFER
) ||
428 (aAllocFlags
& ALLOC_CLEAR_BUFFER_BLACK
)) {
429 if (aFormat
== gfx::SurfaceFormat::B8G8R8X8
) {
430 // Even though BGRX was requested, XRGB_UINT32 is what is meant,
431 // so use 0xFF000000 to put alpha in the right place.
432 libyuv::ARGBRect(buf
, bufSize
, 0, 0, bufSize
/ sizeof(uint32_t), 1,
434 } else if (!aAlreadyZero
) {
435 memset(buf
, 0, bufSize
);
439 if (aAllocFlags
& ALLOC_CLEAR_BUFFER_WHITE
) {
440 memset(buf
, 0xFF, bufSize
);
446 MemoryTextureData
* MemoryTextureData::Create(gfx::IntSize aSize
,
447 gfx::SurfaceFormat aFormat
,
448 gfx::BackendType aMoz2DBackend
,
449 LayersBackend aLayersBackend
,
451 TextureAllocationFlags aAllocFlags
,
452 IShmemAllocator
* aAllocator
) {
453 // Should have used CreateForYCbCr.
454 MOZ_ASSERT(aFormat
!= gfx::SurfaceFormat::YUV
);
456 if (aSize
.width
<= 0 || aSize
.height
<= 0) {
457 gfxDebug() << "Asking for buffer of invalid size " << aSize
.width
<< "x"
462 uint32_t bufSize
= ImageDataSerializer::ComputeRGBBufferSize(aSize
, aFormat
);
467 uint8_t* buf
= new (fallible
) uint8_t[bufSize
];
468 if (!InitBuffer(buf
, bufSize
, aFormat
, aAllocFlags
, false)) {
472 bool hasIntermediateBuffer
= ComputeHasIntermediateBuffer(
473 aFormat
, aLayersBackend
, aAllocFlags
& ALLOC_ALLOW_DIRECT_MAPPING
);
475 GfxMemoryImageReporter::DidAlloc(buf
);
477 BufferDescriptor descriptor
=
478 RGBDescriptor(aSize
, aFormat
, hasIntermediateBuffer
);
480 return new MemoryTextureData(descriptor
, aMoz2DBackend
, buf
, bufSize
);
483 void MemoryTextureData::Deallocate(LayersIPCChannel
*) {
485 GfxMemoryImageReporter::WillFree(mBuffer
);
490 TextureData
* MemoryTextureData::CreateSimilar(
491 LayersIPCChannel
* aAllocator
, LayersBackend aLayersBackend
,
492 TextureFlags aFlags
, TextureAllocationFlags aAllocFlags
) const {
493 return MemoryTextureData::Create(GetSize(), GetFormat(), mMoz2DBackend
,
494 aLayersBackend
, aFlags
, aAllocFlags
,
498 bool ShmemTextureData::Serialize(SurfaceDescriptor
& aOutDescriptor
) {
499 MOZ_ASSERT(GetFormat() != gfx::SurfaceFormat::UNKNOWN
);
500 if (GetFormat() == gfx::SurfaceFormat::UNKNOWN
) {
505 SurfaceDescriptorBuffer(mDescriptor
, MemoryOrShmem(std::move(mShmem
)));
510 ShmemTextureData
* ShmemTextureData::Create(gfx::IntSize aSize
,
511 gfx::SurfaceFormat aFormat
,
512 gfx::BackendType aMoz2DBackend
,
513 LayersBackend aLayersBackend
,
515 TextureAllocationFlags aAllocFlags
,
516 IShmemAllocator
* aAllocator
) {
517 MOZ_ASSERT(aAllocator
);
518 // Should have used CreateForYCbCr.
519 MOZ_ASSERT(aFormat
!= gfx::SurfaceFormat::YUV
);
525 if (aSize
.width
<= 0 || aSize
.height
<= 0) {
526 gfxDebug() << "Asking for buffer of invalid size " << aSize
.width
<< "x"
531 uint32_t bufSize
= ImageDataSerializer::ComputeRGBBufferSize(aSize
, aFormat
);
536 mozilla::ipc::Shmem shm
;
537 if (!aAllocator
->AllocUnsafeShmem(bufSize
, OptimalShmemType(), &shm
)) {
541 uint8_t* buf
= shm
.get
<uint8_t>();
542 if (!InitBuffer(buf
, bufSize
, aFormat
, aAllocFlags
, true)) {
546 bool hasIntermediateBuffer
= ComputeHasIntermediateBuffer(
547 aFormat
, aLayersBackend
, aAllocFlags
& ALLOC_ALLOW_DIRECT_MAPPING
);
549 BufferDescriptor descriptor
=
550 RGBDescriptor(aSize
, aFormat
, hasIntermediateBuffer
);
552 return new ShmemTextureData(descriptor
, aMoz2DBackend
, shm
);
555 TextureData
* ShmemTextureData::CreateSimilar(
556 LayersIPCChannel
* aAllocator
, LayersBackend aLayersBackend
,
557 TextureFlags aFlags
, TextureAllocationFlags aAllocFlags
) const {
558 return ShmemTextureData::Create(GetSize(), GetFormat(), mMoz2DBackend
,
559 aLayersBackend
, aFlags
, aAllocFlags
,
563 void ShmemTextureData::Deallocate(LayersIPCChannel
* aAllocator
) {
564 aAllocator
->DeallocShmem(mShmem
);
567 } // namespace layers
568 } // namespace mozilla