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"
20 #include "gfxPlatform.h"
23 # include "gfxPlatformGtk.h"
26 using mozilla::ipc::IShmemAllocator
;
31 class MemoryTextureData
: public BufferTextureData
{
33 static MemoryTextureData
* Create(gfx::IntSize aSize
,
34 gfx::SurfaceFormat aFormat
,
35 gfx::BackendType aMoz2DBackend
,
36 LayersBackend aLayersBackend
,
38 TextureAllocationFlags aAllocFlags
,
39 IShmemAllocator
* aAllocator
);
41 virtual TextureData
* CreateSimilar(
42 LayersIPCChannel
* aAllocator
, LayersBackend aLayersBackend
,
43 TextureFlags aFlags
= TextureFlags::DEFAULT
,
44 TextureAllocationFlags aAllocFlags
= ALLOC_DEFAULT
) const override
;
46 virtual bool Serialize(SurfaceDescriptor
& aOutDescriptor
) override
;
48 virtual void Deallocate(LayersIPCChannel
*) override
;
50 MemoryTextureData(const BufferDescriptor
& aDesc
,
51 gfx::BackendType aMoz2DBackend
, uint8_t* aBuffer
,
52 size_t aBufferSize
, bool aAutoDeallocate
= false)
53 : BufferTextureData(aDesc
, aMoz2DBackend
),
55 mBufferSize(aBufferSize
),
56 mAutoDeallocate(aAutoDeallocate
) {
58 MOZ_ASSERT(aBufferSize
);
61 virtual ~MemoryTextureData() override
{
62 if (mAutoDeallocate
) {
67 virtual uint8_t* GetBuffer() override
{ return mBuffer
; }
69 virtual size_t GetBufferSize() override
{ return mBufferSize
; }
71 TextureType
GetTextureType() const override
{ return TextureType::Unknown
; }
79 class ShmemTextureData
: public BufferTextureData
{
81 static ShmemTextureData
* Create(gfx::IntSize aSize
,
82 gfx::SurfaceFormat aFormat
,
83 gfx::BackendType aMoz2DBackend
,
84 LayersBackend aLayersBackend
,
86 TextureAllocationFlags aAllocFlags
,
87 IShmemAllocator
* aAllocator
);
89 virtual TextureData
* CreateSimilar(
90 LayersIPCChannel
* aAllocator
, LayersBackend aLayersBackend
,
91 TextureFlags aFlags
= TextureFlags::DEFAULT
,
92 TextureAllocationFlags aAllocFlags
= ALLOC_DEFAULT
) const override
;
94 virtual bool Serialize(SurfaceDescriptor
& aOutDescriptor
) override
;
96 virtual void Deallocate(LayersIPCChannel
* aAllocator
) override
;
98 ShmemTextureData(const BufferDescriptor
& aDesc
,
99 gfx::BackendType aMoz2DBackend
, mozilla::ipc::Shmem aShmem
)
100 : BufferTextureData(aDesc
, aMoz2DBackend
), mShmem(aShmem
) {
101 MOZ_ASSERT(mShmem
.Size
<uint8_t>());
104 virtual uint8_t* GetBuffer() override
{ return mShmem
.get
<uint8_t>(); }
106 virtual size_t GetBufferSize() override
{ return mShmem
.Size
<uint8_t>(); }
109 mozilla::ipc::Shmem mShmem
;
112 BufferTextureData
* BufferTextureData::Create(
113 gfx::IntSize aSize
, gfx::SurfaceFormat aFormat
,
114 gfx::BackendType aMoz2DBackend
, LayersBackend aLayersBackend
,
115 TextureFlags aFlags
, TextureAllocationFlags aAllocFlags
,
116 mozilla::ipc::IShmemAllocator
* aAllocator
, bool aIsSameProcess
) {
117 if (!aAllocator
|| aIsSameProcess
) {
118 return MemoryTextureData::Create(aSize
, aFormat
, aMoz2DBackend
,
119 aLayersBackend
, aFlags
, aAllocFlags
,
122 return ShmemTextureData::Create(aSize
, aFormat
, aMoz2DBackend
,
123 aLayersBackend
, aFlags
, aAllocFlags
,
128 BufferTextureData
* BufferTextureData::CreateInternal(
129 LayersIPCChannel
* aAllocator
, const BufferDescriptor
& aDesc
,
130 gfx::BackendType aMoz2DBackend
, int32_t aBufferSize
,
131 TextureFlags aTextureFlags
) {
132 if (!aAllocator
|| aAllocator
->IsSameProcess()) {
133 uint8_t* buffer
= new (fallible
) uint8_t[aBufferSize
];
138 GfxMemoryImageReporter::DidAlloc(buffer
);
140 return new MemoryTextureData(aDesc
, aMoz2DBackend
, buffer
, aBufferSize
);
143 if (!aAllocator
->AllocUnsafeShmem(aBufferSize
, &shm
)) {
147 return new ShmemTextureData(aDesc
, aMoz2DBackend
, shm
);
151 BufferTextureData
* BufferTextureData::CreateForYCbCr(
152 KnowsCompositor
* aAllocator
, const gfx::IntRect
& aDisplay
,
153 const gfx::IntSize
& aYSize
, uint32_t aYStride
,
154 const gfx::IntSize
& aCbCrSize
, uint32_t aCbCrStride
, StereoMode aStereoMode
,
155 gfx::ColorDepth aColorDepth
, gfx::YUVColorSpace aYUVColorSpace
,
156 gfx::ColorRange aColorRange
, gfx::ChromaSubsampling aSubsampling
,
157 TextureFlags aTextureFlags
) {
158 uint32_t bufSize
= ImageDataSerializer::ComputeYCbCrBufferSize(
159 aYSize
, aYStride
, aCbCrSize
, aCbCrStride
);
167 ImageDataSerializer::ComputeYCbCrOffsets(aYStride
, aYSize
.height
, aCbCrStride
,
168 aCbCrSize
.height
, yOffset
, cbOffset
,
171 YCbCrDescriptor descriptor
=
172 YCbCrDescriptor(aDisplay
, aYSize
, aYStride
, aCbCrSize
, aCbCrStride
,
173 yOffset
, cbOffset
, crOffset
, aStereoMode
, aColorDepth
,
174 aYUVColorSpace
, aColorRange
, aSubsampling
);
176 return CreateInternal(
177 aAllocator
? aAllocator
->GetTextureForwarder() : nullptr, descriptor
,
178 gfx::BackendType::NONE
, bufSize
, aTextureFlags
);
181 void BufferTextureData::FillInfo(TextureData::Info
& aInfo
) const {
182 aInfo
.size
= GetSize();
183 aInfo
.format
= GetFormat();
184 aInfo
.hasSynchronization
= false;
185 aInfo
.canExposeMappedData
= true;
187 switch (aInfo
.format
) {
188 case gfx::SurfaceFormat::YUV420
:
189 case gfx::SurfaceFormat::UNKNOWN
:
190 aInfo
.supportsMoz2D
= false;
193 aInfo
.supportsMoz2D
= true;
197 gfx::IntSize
BufferTextureData::GetSize() const {
198 return ImageDataSerializer::SizeFromBufferDescriptor(mDescriptor
);
201 gfx::IntRect
BufferTextureData::GetPictureRect() const {
202 return ImageDataSerializer::RectFromBufferDescriptor(mDescriptor
);
205 Maybe
<gfx::IntSize
> BufferTextureData::GetYSize() const {
206 return ImageDataSerializer::YSizeFromBufferDescriptor(mDescriptor
);
209 Maybe
<gfx::IntSize
> BufferTextureData::GetCbCrSize() const {
210 return ImageDataSerializer::CbCrSizeFromBufferDescriptor(mDescriptor
);
213 Maybe
<int32_t> BufferTextureData::GetYStride() const {
214 return ImageDataSerializer::YStrideFromBufferDescriptor(mDescriptor
);
217 Maybe
<int32_t> BufferTextureData::GetCbCrStride() const {
218 return ImageDataSerializer::CbCrStrideFromBufferDescriptor(mDescriptor
);
221 Maybe
<gfx::YUVColorSpace
> BufferTextureData::GetYUVColorSpace() const {
222 return ImageDataSerializer::YUVColorSpaceFromBufferDescriptor(mDescriptor
);
225 Maybe
<gfx::ColorDepth
> BufferTextureData::GetColorDepth() const {
226 return ImageDataSerializer::ColorDepthFromBufferDescriptor(mDescriptor
);
229 Maybe
<StereoMode
> BufferTextureData::GetStereoMode() const {
230 return ImageDataSerializer::StereoModeFromBufferDescriptor(mDescriptor
);
233 Maybe
<gfx::ChromaSubsampling
> BufferTextureData::GetChromaSubsampling() const {
234 return ImageDataSerializer::ChromaSubsamplingFromBufferDescriptor(
238 gfx::SurfaceFormat
BufferTextureData::GetFormat() const {
239 return ImageDataSerializer::FormatFromBufferDescriptor(mDescriptor
);
242 already_AddRefed
<gfx::DrawTarget
> BufferTextureData::BorrowDrawTarget() {
243 if (mDescriptor
.type() != BufferDescriptor::TRGBDescriptor
) {
247 const RGBDescriptor
& rgb
= mDescriptor
.get_RGBDescriptor();
249 uint32_t stride
= ImageDataSerializer::GetRGBStride(rgb
);
250 RefPtr
<gfx::DrawTarget
> dt
;
251 if (gfx::Factory::DoesBackendSupportDataDrawtarget(mMoz2DBackend
)) {
252 dt
= gfx::Factory::CreateDrawTargetForData(
253 mMoz2DBackend
, GetBuffer(), rgb
.size(), stride
, rgb
.format(), true);
256 // Fall back to supported platform backend. Note that mMoz2DBackend
257 // does not match the draw target type.
258 dt
= gfxPlatform::CreateDrawTargetForData(GetBuffer(), rgb
.size(), stride
,
263 gfxCriticalNote
<< "BorrowDrawTarget failure, original backend "
264 << (int)mMoz2DBackend
;
270 bool BufferTextureData::BorrowMappedData(MappedTextureData
& aData
) {
271 if (GetFormat() == gfx::SurfaceFormat::YUV420
) {
275 gfx::IntSize size
= GetSize();
277 aData
.data
= GetBuffer();
279 aData
.format
= GetFormat();
281 ImageDataSerializer::ComputeRGBStride(aData
.format
, size
.width
);
286 bool BufferTextureData::BorrowMappedYCbCrData(MappedYCbCrTextureData
& aMap
) {
287 if (mDescriptor
.type() != BufferDescriptor::TYCbCrDescriptor
) {
291 const YCbCrDescriptor
& desc
= mDescriptor
.get_YCbCrDescriptor();
293 uint8_t* data
= GetBuffer();
294 auto ySize
= desc
.ySize();
295 auto cbCrSize
= desc
.cbCrSize();
297 aMap
.stereoMode
= desc
.stereoMode();
298 aMap
.metadata
= nullptr;
299 uint32_t bytesPerPixel
=
300 BytesPerPixel(SurfaceFormatForColorDepth(desc
.colorDepth()));
302 aMap
.y
.data
= data
+ desc
.yOffset();
304 aMap
.y
.stride
= desc
.yStride();
306 aMap
.y
.bytesPerPixel
= bytesPerPixel
;
308 aMap
.cb
.data
= data
+ desc
.cbOffset();
309 aMap
.cb
.size
= cbCrSize
;
310 aMap
.cb
.stride
= desc
.cbCrStride();
312 aMap
.cb
.bytesPerPixel
= bytesPerPixel
;
314 aMap
.cr
.data
= data
+ desc
.crOffset();
315 aMap
.cr
.size
= cbCrSize
;
316 aMap
.cr
.stride
= desc
.cbCrStride();
318 aMap
.cr
.bytesPerPixel
= bytesPerPixel
;
323 bool BufferTextureData::UpdateFromSurface(gfx::SourceSurface
* aSurface
) {
324 if (mDescriptor
.type() != BufferDescriptor::TRGBDescriptor
) {
327 const RGBDescriptor
& rgb
= mDescriptor
.get_RGBDescriptor();
329 uint32_t stride
= ImageDataSerializer::GetRGBStride(rgb
);
330 RefPtr
<gfx::DataSourceSurface
> surface
=
331 gfx::Factory::CreateWrappingDataSourceSurface(GetBuffer(), stride
,
332 rgb
.size(), rgb
.format());
335 gfxCriticalError() << "Failed to get serializer as surface!";
339 RefPtr
<gfx::DataSourceSurface
> srcSurf
= aSurface
->GetDataSurface();
342 gfxCriticalError() << "Failed to GetDataSurface in UpdateFromSurface (BT).";
346 if (surface
->GetSize() != srcSurf
->GetSize() ||
347 surface
->GetFormat() != srcSurf
->GetFormat()) {
348 gfxCriticalError() << "Attempt to update texture client from a surface "
349 "with a different size or format (BT)! This: "
350 << surface
->GetSize() << " " << surface
->GetFormat()
351 << " Other: " << aSurface
->GetSize() << " "
352 << aSurface
->GetFormat();
356 gfx::DataSourceSurface::MappedSurface sourceMap
;
357 gfx::DataSourceSurface::MappedSurface destMap
;
358 if (!srcSurf
->Map(gfx::DataSourceSurface::READ
, &sourceMap
)) {
360 << "Failed to map source surface for UpdateFromSurface (BT).";
364 if (!surface
->Map(gfx::DataSourceSurface::WRITE
, &destMap
)) {
367 << "Failed to map destination surface for UpdateFromSurface.";
371 for (int y
= 0; y
< srcSurf
->GetSize().height
; y
++) {
372 memcpy(destMap
.mData
+ destMap
.mStride
* y
,
373 sourceMap
.mData
+ sourceMap
.mStride
* y
,
374 srcSurf
->GetSize().width
* BytesPerPixel(srcSurf
->GetFormat()));
383 bool MemoryTextureData::Serialize(SurfaceDescriptor
& aOutDescriptor
) {
384 MOZ_ASSERT(GetFormat() != gfx::SurfaceFormat::UNKNOWN
);
385 if (GetFormat() == gfx::SurfaceFormat::UNKNOWN
) {
389 uintptr_t ptr
= reinterpret_cast<uintptr_t>(mBuffer
);
390 aOutDescriptor
= SurfaceDescriptorBuffer(mDescriptor
, MemoryOrShmem(ptr
));
395 static bool InitBuffer(uint8_t* buf
, size_t bufSize
, gfx::SurfaceFormat aFormat
,
396 TextureAllocationFlags aAllocFlags
, bool aAlreadyZero
) {
398 gfxDebug() << "BufferTextureData: Failed to allocate " << bufSize
403 if (aAllocFlags
& ALLOC_CLEAR_BUFFER
) {
404 if (aFormat
== gfx::SurfaceFormat::B8G8R8X8
) {
405 // Even though BGRX was requested, XRGB_UINT32 is what is meant,
406 // so use 0xFF000000 to put alpha in the right place.
407 libyuv::ARGBRect(buf
, bufSize
, 0, 0, bufSize
/ sizeof(uint32_t), 1,
409 } else if (!aAlreadyZero
) {
410 memset(buf
, 0, bufSize
);
417 MemoryTextureData
* MemoryTextureData::Create(gfx::IntSize aSize
,
418 gfx::SurfaceFormat aFormat
,
419 gfx::BackendType aMoz2DBackend
,
420 LayersBackend aLayersBackend
,
422 TextureAllocationFlags aAllocFlags
,
423 IShmemAllocator
* aAllocator
) {
424 // Should have used CreateForYCbCr.
425 MOZ_ASSERT(aFormat
!= gfx::SurfaceFormat::YUV420
);
427 if (aSize
.width
<= 0 || aSize
.height
<= 0) {
428 gfxDebug() << "Asking for buffer of invalid size " << aSize
.width
<< "x"
433 uint32_t bufSize
= ImageDataSerializer::ComputeRGBBufferSize(aSize
, aFormat
);
438 uint8_t* buf
= new (fallible
) uint8_t[bufSize
];
439 if (!InitBuffer(buf
, bufSize
, aFormat
, aAllocFlags
, false)) {
443 GfxMemoryImageReporter::DidAlloc(buf
);
445 BufferDescriptor descriptor
= RGBDescriptor(aSize
, aFormat
);
447 // Remote textures are not managed by a texture client, so we need to ensure
448 // that memory is freed when the owning MemoryTextureData goes away.
449 bool autoDeallocate
= !!(aFlags
& TextureFlags::REMOTE_TEXTURE
);
450 return new MemoryTextureData(descriptor
, aMoz2DBackend
, buf
, bufSize
,
454 void MemoryTextureData::Deallocate(LayersIPCChannel
*) {
456 GfxMemoryImageReporter::WillFree(mBuffer
);
461 TextureData
* MemoryTextureData::CreateSimilar(
462 LayersIPCChannel
* aAllocator
, LayersBackend aLayersBackend
,
463 TextureFlags aFlags
, TextureAllocationFlags aAllocFlags
) const {
464 return MemoryTextureData::Create(GetSize(), GetFormat(), mMoz2DBackend
,
465 aLayersBackend
, aFlags
, aAllocFlags
,
469 bool ShmemTextureData::Serialize(SurfaceDescriptor
& aOutDescriptor
) {
470 MOZ_ASSERT(GetFormat() != gfx::SurfaceFormat::UNKNOWN
);
471 if (GetFormat() == gfx::SurfaceFormat::UNKNOWN
) {
476 SurfaceDescriptorBuffer(mDescriptor
, MemoryOrShmem(std::move(mShmem
)));
481 ShmemTextureData
* ShmemTextureData::Create(gfx::IntSize aSize
,
482 gfx::SurfaceFormat aFormat
,
483 gfx::BackendType aMoz2DBackend
,
484 LayersBackend aLayersBackend
,
486 TextureAllocationFlags aAllocFlags
,
487 IShmemAllocator
* aAllocator
) {
488 MOZ_ASSERT(aAllocator
);
489 // Should have used CreateForYCbCr.
490 MOZ_ASSERT(aFormat
!= gfx::SurfaceFormat::YUV420
);
496 if (aSize
.width
<= 0 || aSize
.height
<= 0) {
497 gfxDebug() << "Asking for buffer of invalid size " << aSize
.width
<< "x"
502 uint32_t bufSize
= ImageDataSerializer::ComputeRGBBufferSize(aSize
, aFormat
);
507 mozilla::ipc::Shmem shm
;
508 if (!aAllocator
->AllocUnsafeShmem(bufSize
, &shm
)) {
512 uint8_t* buf
= shm
.get
<uint8_t>();
513 if (!InitBuffer(buf
, bufSize
, aFormat
, aAllocFlags
, true)) {
517 BufferDescriptor descriptor
= RGBDescriptor(aSize
, aFormat
);
519 return new ShmemTextureData(descriptor
, aMoz2DBackend
, shm
);
522 TextureData
* ShmemTextureData::CreateSimilar(
523 LayersIPCChannel
* aAllocator
, LayersBackend aLayersBackend
,
524 TextureFlags aFlags
, TextureAllocationFlags aAllocFlags
) const {
525 return ShmemTextureData::Create(GetSize(), GetFormat(), mMoz2DBackend
,
526 aLayersBackend
, aFlags
, aAllocFlags
,
530 void ShmemTextureData::Deallocate(LayersIPCChannel
* aAllocator
) {
531 aAllocator
->DeallocShmem(mShmem
);
534 } // namespace layers
535 } // namespace mozilla