Bug 1691109 [wpt PR 27513] - Increase timeout duration for wpt/fetch/api/basic/keepal...
[gecko.git] / gfx / layers / BufferTexture.cpp
blob6021ada13457358642b35c460218b0cce5f798cf
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"
9 #include <utility>
11 #include "libyuv.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 #ifdef MOZ_WIDGET_GTK
21 # include "gfxPlatformGtk.h"
22 #endif
24 using mozilla::ipc::IShmemAllocator;
26 namespace mozilla {
27 namespace layers {
29 class MemoryTextureData : public BufferTextureData {
30 public:
31 static MemoryTextureData* Create(gfx::IntSize aSize,
32 gfx::SurfaceFormat aFormat,
33 gfx::BackendType aMoz2DBackend,
34 LayersBackend aLayersBackend,
35 TextureFlags aFlags,
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,
50 size_t aBufferSize)
51 : BufferTextureData(aDesc, aMoz2DBackend),
52 mBuffer(aBuffer),
53 mBufferSize(aBufferSize) {
54 MOZ_ASSERT(aBuffer);
55 MOZ_ASSERT(aBufferSize);
58 virtual uint8_t* GetBuffer() override { return mBuffer; }
60 virtual size_t GetBufferSize() override { return mBufferSize; }
62 protected:
63 uint8_t* mBuffer;
64 size_t mBufferSize;
67 class ShmemTextureData : public BufferTextureData {
68 public:
69 static ShmemTextureData* Create(gfx::IntSize aSize,
70 gfx::SurfaceFormat aFormat,
71 gfx::BackendType aMoz2DBackend,
72 LayersBackend aLayersBackend,
73 TextureFlags aFlags,
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>(); }
96 protected:
97 mozilla::ipc::Shmem mShmem;
100 static bool UsingX11Compositor() {
101 #ifdef MOZ_WIDGET_GTK
102 return gfx::gfxVars::UseXRender();
103 #endif
104 return false;
107 bool ComputeHasIntermediateBuffer(gfx::SurfaceFormat aFormat,
108 LayersBackend aLayersBackend,
109 bool aSupportsTextureDirectMapping) {
110 if (aSupportsTextureDirectMapping) {
111 return false;
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,
126 aAllocator);
127 } else {
128 return ShmemTextureData::Create(aSize, aFormat, aMoz2DBackend,
129 aLayersBackend, aFlags, aAllocFlags,
130 aAllocator);
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];
140 if (!buffer) {
141 return nullptr;
144 GfxMemoryImageReporter::DidAlloc(buffer);
146 return new MemoryTextureData(aDesc, aMoz2DBackend, buffer, aBufferSize);
147 } else {
148 ipc::Shmem shm;
149 if (!aAllocator->AllocUnsafeShmem(aBufferSize, OptimalShmemType(), &shm)) {
150 return nullptr;
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);
165 if (bufSize == 0) {
166 return nullptr;
169 uint32_t yOffset;
170 uint32_t cbOffset;
171 uint32_t crOffset;
172 ImageDataSerializer::ComputeYCbCrOffsets(aYStride, aYSize.height, aCbCrStride,
173 aCbCrSize.height, yOffset, cbOffset,
174 crOffset);
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 =
184 aAllocator
185 ? ComputeHasIntermediateBuffer(gfx::SurfaceFormat::YUV,
186 aAllocator->GetCompositorBackendType(),
187 supportsTextureDirectMapping)
188 : true;
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();
209 } else {
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;
218 break;
219 default:
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) {
262 return nullptr;
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);
273 if (!dt) {
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,
277 rgb.format(), true);
280 if (!dt) {
281 gfxCriticalNote << "BorrowDrawTarget failure, original backend "
282 << (int)mMoz2DBackend;
285 return dt.forget();
288 bool BufferTextureData::BorrowMappedData(MappedTextureData& aData) {
289 if (GetFormat() == gfx::SurfaceFormat::YUV) {
290 return false;
293 gfx::IntSize size = GetSize();
295 aData.data = GetBuffer();
296 aData.size = size;
297 aData.format = GetFormat();
298 aData.stride =
299 ImageDataSerializer::ComputeRGBStride(aData.format, size.width);
301 return true;
304 bool BufferTextureData::BorrowMappedYCbCrData(MappedYCbCrTextureData& aMap) {
305 if (mDescriptor.type() != BufferDescriptor::TYCbCrDescriptor) {
306 return false;
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();
321 aMap.y.size = ySize;
322 aMap.y.stride = desc.yStride();
323 aMap.y.skip = 0;
324 aMap.y.bytesPerPixel = bytesPerPixel;
326 aMap.cb.data = data + desc.cbOffset();
327 aMap.cb.size = cbCrSize;
328 aMap.cb.stride = desc.cbCrStride();
329 aMap.cb.skip = 0;
330 aMap.cb.bytesPerPixel = bytesPerPixel;
332 aMap.cr.data = data + desc.crOffset();
333 aMap.cr.size = cbCrSize;
334 aMap.cr.stride = desc.cbCrStride();
335 aMap.cr.skip = 0;
336 aMap.cr.bytesPerPixel = bytesPerPixel;
338 return true;
341 bool BufferTextureData::UpdateFromSurface(gfx::SourceSurface* aSurface) {
342 if (mDescriptor.type() != BufferDescriptor::TRGBDescriptor) {
343 return false;
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());
352 if (!surface) {
353 gfxCriticalError() << "Failed to get serializer as surface!";
354 return false;
357 RefPtr<gfx::DataSourceSurface> srcSurf = aSurface->GetDataSurface();
359 if (!srcSurf) {
360 gfxCriticalError() << "Failed to GetDataSurface in UpdateFromSurface (BT).";
361 return false;
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();
371 return false;
374 gfx::DataSourceSurface::MappedSurface sourceMap;
375 gfx::DataSourceSurface::MappedSurface destMap;
376 if (!srcSurf->Map(gfx::DataSourceSurface::READ, &sourceMap)) {
377 gfxCriticalError()
378 << "Failed to map source surface for UpdateFromSurface (BT).";
379 return false;
382 if (!surface->Map(gfx::DataSourceSurface::WRITE, &destMap)) {
383 srcSurf->Unmap();
384 gfxCriticalError()
385 << "Failed to map destination surface for UpdateFromSurface.";
386 return false;
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()));
395 srcSurf->Unmap();
396 surface->Unmap();
398 return true;
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) {
410 return false;
413 uintptr_t ptr = reinterpret_cast<uintptr_t>(mBuffer);
414 aOutDescriptor = SurfaceDescriptorBuffer(mDescriptor, MemoryOrShmem(ptr));
416 return true;
419 static bool InitBuffer(uint8_t* buf, size_t bufSize, gfx::SurfaceFormat aFormat,
420 TextureAllocationFlags aAllocFlags, bool aAlreadyZero) {
421 if (!buf) {
422 gfxDebug() << "BufferTextureData: Failed to allocate " << bufSize
423 << " bytes";
424 return false;
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,
433 0xFF000000);
434 } else if (!aAlreadyZero) {
435 memset(buf, 0, bufSize);
439 if (aAllocFlags & ALLOC_CLEAR_BUFFER_WHITE) {
440 memset(buf, 0xFF, bufSize);
443 return true;
446 MemoryTextureData* MemoryTextureData::Create(gfx::IntSize aSize,
447 gfx::SurfaceFormat aFormat,
448 gfx::BackendType aMoz2DBackend,
449 LayersBackend aLayersBackend,
450 TextureFlags aFlags,
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"
458 << aSize.height;
459 return nullptr;
462 uint32_t bufSize = ImageDataSerializer::ComputeRGBBufferSize(aSize, aFormat);
463 if (!bufSize) {
464 return nullptr;
467 uint8_t* buf = new (fallible) uint8_t[bufSize];
468 if (!InitBuffer(buf, bufSize, aFormat, aAllocFlags, false)) {
469 return nullptr;
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*) {
484 MOZ_ASSERT(mBuffer);
485 GfxMemoryImageReporter::WillFree(mBuffer);
486 delete[] mBuffer;
487 mBuffer = nullptr;
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,
495 aAllocator);
498 bool ShmemTextureData::Serialize(SurfaceDescriptor& aOutDescriptor) {
499 MOZ_ASSERT(GetFormat() != gfx::SurfaceFormat::UNKNOWN);
500 if (GetFormat() == gfx::SurfaceFormat::UNKNOWN) {
501 return false;
504 aOutDescriptor =
505 SurfaceDescriptorBuffer(mDescriptor, MemoryOrShmem(std::move(mShmem)));
507 return true;
510 ShmemTextureData* ShmemTextureData::Create(gfx::IntSize aSize,
511 gfx::SurfaceFormat aFormat,
512 gfx::BackendType aMoz2DBackend,
513 LayersBackend aLayersBackend,
514 TextureFlags aFlags,
515 TextureAllocationFlags aAllocFlags,
516 IShmemAllocator* aAllocator) {
517 MOZ_ASSERT(aAllocator);
518 // Should have used CreateForYCbCr.
519 MOZ_ASSERT(aFormat != gfx::SurfaceFormat::YUV);
521 if (!aAllocator) {
522 return nullptr;
525 if (aSize.width <= 0 || aSize.height <= 0) {
526 gfxDebug() << "Asking for buffer of invalid size " << aSize.width << "x"
527 << aSize.height;
528 return nullptr;
531 uint32_t bufSize = ImageDataSerializer::ComputeRGBBufferSize(aSize, aFormat);
532 if (!bufSize) {
533 return nullptr;
536 mozilla::ipc::Shmem shm;
537 if (!aAllocator->AllocUnsafeShmem(bufSize, OptimalShmemType(), &shm)) {
538 return nullptr;
541 uint8_t* buf = shm.get<uint8_t>();
542 if (!InitBuffer(buf, bufSize, aFormat, aAllocFlags, true)) {
543 return nullptr;
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,
560 aAllocator);
563 void ShmemTextureData::Deallocate(LayersIPCChannel* aAllocator) {
564 aAllocator->DeallocShmem(mShmem);
567 } // namespace layers
568 } // namespace mozilla