Bug 1632310 [wpt PR 23186] - Add test for computed versus resolved style., a=testonly
[gecko.git] / gfx / layers / BufferTexture.cpp
blobcbdc54611ae8f0567f81b2e84bae97d1fb0efd99
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"
19 #ifdef MOZ_WIDGET_GTK
20 # include "gfxPlatformGtk.h"
21 #endif
23 using mozilla::ipc::IShmemAllocator;
25 namespace mozilla {
26 namespace layers {
28 class MemoryTextureData : public BufferTextureData {
29 public:
30 static MemoryTextureData* Create(gfx::IntSize aSize,
31 gfx::SurfaceFormat aFormat,
32 gfx::BackendType aMoz2DBackend,
33 LayersBackend aLayersBackend,
34 TextureFlags aFlags,
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,
49 size_t aBufferSize)
50 : BufferTextureData(aDesc, aMoz2DBackend),
51 mBuffer(aBuffer),
52 mBufferSize(aBufferSize) {
53 MOZ_ASSERT(aBuffer);
54 MOZ_ASSERT(aBufferSize);
57 virtual uint8_t* GetBuffer() override { return mBuffer; }
59 virtual size_t GetBufferSize() override { return mBufferSize; }
61 protected:
62 uint8_t* mBuffer;
63 size_t mBufferSize;
66 class ShmemTextureData : public BufferTextureData {
67 public:
68 static ShmemTextureData* Create(gfx::IntSize aSize,
69 gfx::SurfaceFormat aFormat,
70 gfx::BackendType aMoz2DBackend,
71 LayersBackend aLayersBackend,
72 TextureFlags aFlags,
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>(); }
95 protected:
96 mozilla::ipc::Shmem mShmem;
99 static bool UsingX11Compositor() {
100 #ifdef MOZ_WIDGET_GTK
101 return gfx::gfxVars::UseXRender();
102 #endif
103 return false;
106 bool ComputeHasIntermediateBuffer(gfx::SurfaceFormat aFormat,
107 LayersBackend aLayersBackend,
108 bool aSupportsTextureDirectMapping) {
109 if (aSupportsTextureDirectMapping) {
110 return false;
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,
125 aAllocator);
126 } else {
127 return ShmemTextureData::Create(aSize, aFormat, aMoz2DBackend,
128 aLayersBackend, aFlags, aAllocFlags,
129 aAllocator);
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];
139 if (!buffer) {
140 return nullptr;
143 GfxMemoryImageReporter::DidAlloc(buffer);
145 return new MemoryTextureData(aDesc, aMoz2DBackend, buffer, aBufferSize);
146 } else {
147 ipc::Shmem shm;
148 if (!aAllocator->AllocUnsafeShmem(aBufferSize, OptimalShmemType(), &shm)) {
149 return nullptr;
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);
163 if (bufSize == 0) {
164 return nullptr;
167 uint32_t yOffset;
168 uint32_t cbOffset;
169 uint32_t crOffset;
170 ImageDataSerializer::ComputeYCbCrOffsets(aYStride, aYSize.height, aCbCrStride,
171 aCbCrSize.height, yOffset, cbOffset,
172 crOffset);
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 =
182 aAllocator
183 ? ComputeHasIntermediateBuffer(gfx::SurfaceFormat::YUV,
184 aAllocator->GetCompositorBackendType(),
185 supportsTextureDirectMapping)
186 : true;
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();
207 } else {
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;
216 break;
217 default:
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) {
248 return nullptr;
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);
259 if (!dt) {
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,
263 rgb.format(), true);
266 if (!dt) {
267 gfxCriticalNote << "BorrowDrawTarget failure, original backend "
268 << (int)mMoz2DBackend;
271 return dt.forget();
274 bool BufferTextureData::BorrowMappedData(MappedTextureData& aData) {
275 if (GetFormat() == gfx::SurfaceFormat::YUV) {
276 return false;
279 gfx::IntSize size = GetSize();
281 aData.data = GetBuffer();
282 aData.size = size;
283 aData.format = GetFormat();
284 aData.stride =
285 ImageDataSerializer::ComputeRGBStride(aData.format, size.width);
287 return true;
290 bool BufferTextureData::BorrowMappedYCbCrData(MappedYCbCrTextureData& aMap) {
291 if (mDescriptor.type() != BufferDescriptor::TYCbCrDescriptor) {
292 return false;
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();
307 aMap.y.size = ySize;
308 aMap.y.stride = desc.yStride();
309 aMap.y.skip = 0;
310 aMap.y.bytesPerPixel = bytesPerPixel;
312 aMap.cb.data = data + desc.cbOffset();
313 aMap.cb.size = cbCrSize;
314 aMap.cb.stride = desc.cbCrStride();
315 aMap.cb.skip = 0;
316 aMap.cb.bytesPerPixel = bytesPerPixel;
318 aMap.cr.data = data + desc.crOffset();
319 aMap.cr.size = cbCrSize;
320 aMap.cr.stride = desc.cbCrStride();
321 aMap.cr.skip = 0;
322 aMap.cr.bytesPerPixel = bytesPerPixel;
324 return true;
327 bool BufferTextureData::UpdateFromSurface(gfx::SourceSurface* aSurface) {
328 if (mDescriptor.type() != BufferDescriptor::TRGBDescriptor) {
329 return false;
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());
338 if (!surface) {
339 gfxCriticalError() << "Failed to get serializer as surface!";
340 return false;
343 RefPtr<gfx::DataSourceSurface> srcSurf = aSurface->GetDataSurface();
345 if (!srcSurf) {
346 gfxCriticalError() << "Failed to GetDataSurface in UpdateFromSurface (BT).";
347 return false;
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();
357 return false;
360 gfx::DataSourceSurface::MappedSurface sourceMap;
361 gfx::DataSourceSurface::MappedSurface destMap;
362 if (!srcSurf->Map(gfx::DataSourceSurface::READ, &sourceMap)) {
363 gfxCriticalError()
364 << "Failed to map source surface for UpdateFromSurface (BT).";
365 return false;
368 if (!surface->Map(gfx::DataSourceSurface::WRITE, &destMap)) {
369 srcSurf->Unmap();
370 gfxCriticalError()
371 << "Failed to map destination surface for UpdateFromSurface.";
372 return false;
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()));
381 srcSurf->Unmap();
382 surface->Unmap();
384 return true;
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) {
396 return false;
399 uintptr_t ptr = reinterpret_cast<uintptr_t>(mBuffer);
400 aOutDescriptor = SurfaceDescriptorBuffer(mDescriptor, MemoryOrShmem(ptr));
402 return true;
405 static bool InitBuffer(uint8_t* buf, size_t bufSize, gfx::SurfaceFormat aFormat,
406 TextureAllocationFlags aAllocFlags, bool aAlreadyZero) {
407 if (!buf) {
408 gfxDebug() << "BufferTextureData: Failed to allocate " << bufSize
409 << " bytes";
410 return false;
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,
419 0xFF000000);
420 } else if (!aAlreadyZero) {
421 memset(buf, 0, bufSize);
425 if (aAllocFlags & ALLOC_CLEAR_BUFFER_WHITE) {
426 memset(buf, 0xFF, bufSize);
429 return true;
432 MemoryTextureData* MemoryTextureData::Create(gfx::IntSize aSize,
433 gfx::SurfaceFormat aFormat,
434 gfx::BackendType aMoz2DBackend,
435 LayersBackend aLayersBackend,
436 TextureFlags aFlags,
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"
444 << aSize.height;
445 return nullptr;
448 uint32_t bufSize = ImageDataSerializer::ComputeRGBBufferSize(aSize, aFormat);
449 if (!bufSize) {
450 return nullptr;
453 uint8_t* buf = new (fallible) uint8_t[bufSize];
454 if (!InitBuffer(buf, bufSize, aFormat, aAllocFlags, false)) {
455 return nullptr;
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*) {
470 MOZ_ASSERT(mBuffer);
471 GfxMemoryImageReporter::WillFree(mBuffer);
472 delete[] mBuffer;
473 mBuffer = nullptr;
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,
481 aAllocator);
484 bool ShmemTextureData::Serialize(SurfaceDescriptor& aOutDescriptor) {
485 MOZ_ASSERT(GetFormat() != gfx::SurfaceFormat::UNKNOWN);
486 if (GetFormat() == gfx::SurfaceFormat::UNKNOWN) {
487 return false;
490 aOutDescriptor =
491 SurfaceDescriptorBuffer(mDescriptor, MemoryOrShmem(std::move(mShmem)));
493 return true;
496 ShmemTextureData* ShmemTextureData::Create(gfx::IntSize aSize,
497 gfx::SurfaceFormat aFormat,
498 gfx::BackendType aMoz2DBackend,
499 LayersBackend aLayersBackend,
500 TextureFlags aFlags,
501 TextureAllocationFlags aAllocFlags,
502 IShmemAllocator* aAllocator) {
503 MOZ_ASSERT(aAllocator);
504 // Should have used CreateForYCbCr.
505 MOZ_ASSERT(aFormat != gfx::SurfaceFormat::YUV);
507 if (!aAllocator) {
508 return nullptr;
511 if (aSize.width <= 0 || aSize.height <= 0) {
512 gfxDebug() << "Asking for buffer of invalid size " << aSize.width << "x"
513 << aSize.height;
514 return nullptr;
517 uint32_t bufSize = ImageDataSerializer::ComputeRGBBufferSize(aSize, aFormat);
518 if (!bufSize) {
519 return nullptr;
522 mozilla::ipc::Shmem shm;
523 if (!aAllocator->AllocUnsafeShmem(bufSize, OptimalShmemType(), &shm)) {
524 return nullptr;
527 uint8_t* buf = shm.get<uint8_t>();
528 if (!InitBuffer(buf, bufSize, aFormat, aAllocFlags, true)) {
529 return nullptr;
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,
546 aAllocator);
549 void ShmemTextureData::Deallocate(LayersIPCChannel* aAllocator) {
550 aAllocator->DeallocShmem(mShmem);
553 } // namespace layers
554 } // namespace mozilla