Bug 1632310 [wpt PR 23186] - Add test for computed versus resolved style., a=testonly
[gecko.git] / gfx / layers / client / CanvasClient.cpp
blob8480a5bbefd7264c82f1d96d0d9417c81ebc1c41
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 "CanvasClient.h"
9 #include "ClientCanvasLayer.h" // for ClientCanvasLayer
10 #include "GLContext.h" // for GLContext
11 #include "GLScreenBuffer.h" // for GLScreenBuffer
12 #include "ScopedGLHelpers.h"
13 #include "gfx2DGlue.h" // for ImageFormatToSurfaceFormat
14 #include "gfxPlatform.h" // for gfxPlatform
15 #include "GLReadTexImageHelper.h"
16 #include "mozilla/gfx/BaseSize.h" // for BaseSize
17 #include "mozilla/gfx/gfxVars.h"
18 #include "mozilla/layers/BufferTexture.h"
19 #include "mozilla/layers/AsyncCanvasRenderer.h"
20 #include "mozilla/layers/CompositableForwarder.h"
21 #include "mozilla/layers/CompositorBridgeChild.h" // for CompositorBridgeChild
22 #include "mozilla/layers/LayersTypes.h"
23 #include "mozilla/layers/OOPCanvasRenderer.h"
24 #include "mozilla/layers/TextureClient.h" // for TextureClient, etc
25 #include "mozilla/layers/TextureClientOGL.h"
26 #include "mozilla/layers/TextureClientRecycleAllocator.h"
27 #include "nsDebug.h" // for printf_stderr, NS_ASSERTION
28 #include "nsICanvasRenderingContextInternal.h"
29 #include "nsXULAppAPI.h" // for XRE_GetProcessType, etc
30 #include "TextureClientSharedSurface.h"
32 using namespace mozilla::gfx;
33 using namespace mozilla::gl;
35 namespace mozilla {
36 namespace layers {
38 /* static */
39 already_AddRefed<CanvasClient> CanvasClient::CreateCanvasClient(
40 CanvasClientType aType, CompositableForwarder* aForwarder,
41 TextureFlags aFlags) {
42 switch (aType) {
43 case CanvasClientTypeShSurf:
44 return MakeAndAddRef<CanvasClientSharedSurface>(aForwarder, aFlags);
45 case CanvasClientAsync:
46 return MakeAndAddRef<CanvasClientBridge>(aForwarder, aFlags);
47 case CanvasClientTypeOOP:
48 return MakeAndAddRef<CanvasClientOOP>(aForwarder, aFlags);
49 default:
50 return MakeAndAddRef<CanvasClient2D>(aForwarder, aFlags);
51 break;
55 void CanvasClientBridge::UpdateAsync(AsyncCanvasRenderer* aRenderer) {
56 if (!GetForwarder() || !mLayer || !aRenderer ||
57 !aRenderer->GetCanvasClient()) {
58 return;
61 CompositableHandle asyncID = aRenderer->GetCanvasClientAsyncHandle();
62 if (!asyncID || mAsyncHandle == asyncID) {
63 return;
66 static_cast<ShadowLayerForwarder*>(GetForwarder())
67 ->AttachAsyncCompositable(asyncID, mLayer);
68 mAsyncHandle = asyncID;
71 void CanvasClient2D::UpdateFromTexture(TextureClient* aTexture,
72 wr::RenderRoot aRenderRoot) {
73 MOZ_ASSERT(aTexture);
75 if (!aTexture->IsSharedWithCompositor()) {
76 if (!AddTextureClient(aTexture)) {
77 return;
81 mBackBuffer = nullptr;
82 mFrontBuffer = nullptr;
83 mBufferProviderTexture = aTexture;
85 AutoTArray<CompositableForwarder::TimedTextureClient, 1> textures;
86 CompositableForwarder::TimedTextureClient* t = textures.AppendElement();
87 t->mTextureClient = aTexture;
88 t->mPictureRect = nsIntRect(nsIntPoint(0, 0), aTexture->GetSize());
89 t->mFrameID = mFrameID;
91 GetForwarder()->UseTextures(this, textures, Some(aRenderRoot));
92 aTexture->SyncWithObject(GetForwarder()->GetSyncObject());
95 void CanvasClient2D::Update(gfx::IntSize aSize,
96 ShareableCanvasRenderer* aCanvasRenderer,
97 wr::RenderRoot aRenderRoot) {
98 mBufferProviderTexture = nullptr;
100 AutoRemoveTexture autoRemove(this, aRenderRoot);
101 if (mBackBuffer &&
102 (mBackBuffer->IsReadLocked() || mBackBuffer->GetSize() != aSize)) {
103 autoRemove.mTexture = mBackBuffer;
104 mBackBuffer = nullptr;
107 bool bufferCreated = false;
108 if (!mBackBuffer) {
109 gfxContentType contentType = aCanvasRenderer->IsOpaque()
110 ? gfxContentType::COLOR
111 : gfxContentType::COLOR_ALPHA;
112 gfx::SurfaceFormat surfaceFormat =
113 gfxPlatform::GetPlatform()->Optimal2DFormatForContent(contentType);
114 TextureFlags flags = TextureFlags::DEFAULT;
115 if (mTextureFlags & TextureFlags::ORIGIN_BOTTOM_LEFT) {
116 flags |= TextureFlags::ORIGIN_BOTTOM_LEFT;
118 flags |= TextureFlags::NON_BLOCKING_READ_LOCK;
120 mBackBuffer = CreateTextureClientForCanvas(surfaceFormat, aSize, flags,
121 aCanvasRenderer);
122 if (!mBackBuffer) {
123 NS_WARNING("Failed to allocate the TextureClient");
124 return;
126 MOZ_ASSERT(mBackBuffer->CanExposeDrawTarget());
128 bufferCreated = true;
131 bool updated = false;
133 TextureClientAutoLock autoLock(mBackBuffer, OpenMode::OPEN_WRITE_ONLY);
134 if (!autoLock.Succeeded()) {
135 mBackBuffer = nullptr;
136 return;
139 RefPtr<DrawTarget> target = mBackBuffer->BorrowDrawTarget();
140 if (target) {
141 if (!aCanvasRenderer->UpdateTarget(target)) {
142 NS_WARNING("Failed to copy the canvas into a TextureClient.");
143 return;
145 updated = true;
149 if (bufferCreated && !AddTextureClient(mBackBuffer)) {
150 mBackBuffer = nullptr;
151 return;
154 if (updated) {
155 AutoTArray<CompositableForwarder::TimedTextureClient, 1> textures;
156 CompositableForwarder::TimedTextureClient* t = textures.AppendElement();
157 t->mTextureClient = mBackBuffer;
158 t->mPictureRect = nsIntRect(nsIntPoint(0, 0), mBackBuffer->GetSize());
159 t->mFrameID = mFrameID;
160 GetForwarder()->UseTextures(this, textures, Some(aRenderRoot));
161 mBackBuffer->SyncWithObject(GetForwarder()->GetSyncObject());
164 mBackBuffer.swap(mFrontBuffer);
167 already_AddRefed<TextureClient> CanvasClient2D::CreateTextureClientForCanvas(
168 gfx::SurfaceFormat aFormat, gfx::IntSize aSize, TextureFlags aFlags,
169 ShareableCanvasRenderer* aCanvasRenderer) {
170 if (aCanvasRenderer->HasGLContext()) {
171 // We want a cairo backend here as we don't want to be copying into
172 // an accelerated backend and we like LockBits to work. This is currently
173 // the most effective way to make this work.
174 return TextureClient::CreateForRawBufferAccess(GetForwarder(), aFormat,
175 aSize, BackendType::CAIRO,
176 mTextureFlags | aFlags);
179 #ifdef XP_WIN
180 // With WebRender, host side uses data of TextureClient longer.
181 // Then back buffer reuse in CanvasClient2D::Update() does not work. It causes
182 // a lot of TextureClient allocations.
183 // For reducing the allocations, TextureClientRecycler is used.
184 if (GetForwarder() &&
185 GetForwarder()->GetCompositorBackendType() == LayersBackend::LAYERS_WR) {
186 return GetTextureClientRecycler()->CreateOrRecycle(
187 aFormat, aSize, BackendSelector::Canvas, aFlags);
189 return CreateTextureClientForDrawing(aFormat, aSize, BackendSelector::Canvas,
190 aFlags);
191 #else
192 // XXX - We should use CreateTextureClientForDrawing, but we first need
193 // to use double buffering.
194 gfx::BackendType backend =
195 gfxPlatform::GetPlatform()->GetPreferredCanvasBackend();
196 return TextureClient::CreateForRawBufferAccess(
197 GetForwarder(), aFormat, aSize, backend, mTextureFlags | aFlags);
198 #endif
201 ////////////////////////////////////////////////////////////////////////
203 CanvasClientSharedSurface::CanvasClientSharedSurface(
204 CompositableForwarder* aLayerForwarder, TextureFlags aFlags)
205 : CanvasClient(aLayerForwarder, aFlags) {}
207 CanvasClientSharedSurface::~CanvasClientSharedSurface() { ClearSurfaces(); }
209 ////////////////////////////////////////
210 // Readback
212 // For formats compatible with R8G8B8A8.
213 static inline void SwapRB_R8G8B8A8(uint8_t* pixel) {
214 // [RR, GG, BB, AA]
215 std::swap(pixel[0], pixel[2]);
218 class TexClientFactory {
219 CompositableForwarder* const mAllocator;
220 const bool mHasAlpha;
221 const gfx::IntSize mSize;
222 const gfx::BackendType mBackendType;
223 const TextureFlags mBaseTexFlags;
224 const LayersBackend mLayersBackend;
226 public:
227 TexClientFactory(CompositableForwarder* allocator, bool hasAlpha,
228 const gfx::IntSize& size, gfx::BackendType backendType,
229 TextureFlags baseTexFlags, LayersBackend layersBackend)
230 : mAllocator(allocator),
231 mHasAlpha(hasAlpha),
232 mSize(size),
233 mBackendType(backendType),
234 mBaseTexFlags(baseTexFlags),
235 mLayersBackend(layersBackend) {}
237 protected:
238 already_AddRefed<TextureClient> Create(gfx::SurfaceFormat format) {
239 return TextureClient::CreateForRawBufferAccess(mAllocator, format, mSize,
240 mBackendType, mBaseTexFlags);
243 public:
244 already_AddRefed<TextureClient> CreateB8G8R8AX8() {
245 gfx::SurfaceFormat format =
246 mHasAlpha ? gfx::SurfaceFormat::B8G8R8A8 : gfx::SurfaceFormat::B8G8R8X8;
247 return Create(format);
250 already_AddRefed<TextureClient> CreateR8G8B8AX8() {
251 RefPtr<TextureClient> ret;
253 bool areRGBAFormatsBroken = mLayersBackend == LayersBackend::LAYERS_BASIC;
254 if (!areRGBAFormatsBroken) {
255 gfx::SurfaceFormat format = mHasAlpha ? gfx::SurfaceFormat::R8G8B8A8
256 : gfx::SurfaceFormat::R8G8B8X8;
257 if (gfxVars::UseWebRender() && format == gfx::SurfaceFormat::R8G8B8X8) {
258 MOZ_CRASH("R8G8B8X8 is not supported on WebRender");
260 ret = Create(format);
263 if (!ret) {
264 ret = CreateB8G8R8AX8();
265 if (ret) {
266 ret->AddFlags(TextureFlags::RB_SWAPPED);
270 return ret.forget();
274 static already_AddRefed<TextureClient> TexClientFromReadback(
275 SharedSurface* src, CompositableForwarder* allocator,
276 TextureFlags baseFlags, LayersBackend layersBackend) {
277 auto backendType = gfx::BackendType::SKIA;
278 TexClientFactory factory(allocator, src->mHasAlpha, src->mSize, backendType,
279 baseFlags, layersBackend);
281 RefPtr<TextureClient> texClient;
284 gl::ScopedReadbackFB autoReadback(src);
286 // We have a source FB, now we need a format.
287 GLenum destFormat = LOCAL_GL_BGRA;
288 GLenum destType = LOCAL_GL_UNSIGNED_BYTE;
289 GLenum readFormat;
290 GLenum readType;
292 // We actually don't care if they match, since we can handle
293 // any read{Format,Type} we get.
294 auto gl = src->mGL;
295 GetActualReadFormats(gl, destFormat, destType, &readFormat, &readType);
297 MOZ_ASSERT(readFormat == LOCAL_GL_RGBA || readFormat == LOCAL_GL_BGRA);
298 MOZ_ASSERT(readType == LOCAL_GL_UNSIGNED_BYTE);
300 // With a format and type, we can create texClient.
301 if (readFormat == LOCAL_GL_BGRA && readType == LOCAL_GL_UNSIGNED_BYTE) {
302 // 0xAARRGGBB
303 // In Lendian: [BB, GG, RR, AA]
304 texClient = factory.CreateB8G8R8AX8();
306 } else if (readFormat == LOCAL_GL_RGBA &&
307 readType == LOCAL_GL_UNSIGNED_BYTE) {
308 // [RR, GG, BB, AA]
309 texClient = factory.CreateR8G8B8AX8();
310 } else {
311 MOZ_CRASH("GFX: Bad `read{Format,Type}`.");
314 if (!texClient) {
315 gfxWarning() << "Couldn't create texClient for readback.";
316 return nullptr;
319 // With a texClient, we can lock for writing.
320 TextureClientAutoLock autoLock(texClient, OpenMode::OPEN_WRITE);
321 DebugOnly<bool> succeeded = autoLock.Succeeded();
322 MOZ_ASSERT(succeeded, "texture should have locked");
324 MappedTextureData mapped;
325 texClient->BorrowMappedData(mapped);
327 // ReadPixels from the current FB into mapped.data.
328 auto width = src->mSize.width;
329 auto height = src->mSize.height;
330 auto stride = mapped.stride;
333 ScopedPackState scopedPackState(gl);
334 bool handled = scopedPackState.SetForWidthAndStrideRGBA(width, stride);
335 MOZ_RELEASE_ASSERT(handled, "Unhandled stride");
336 gl->raw_fReadPixels(0, 0, width, height, readFormat, readType,
337 mapped.data);
340 // RB_SWAPPED doesn't work with D3D11. (bug 1051010)
341 // RB_SWAPPED doesn't work with Basic. (bug ???????)
342 bool layersNeedsManualSwap = layersBackend == LayersBackend::LAYERS_BASIC ||
343 layersBackend == LayersBackend::LAYERS_D3D11;
344 if (texClient->HasFlags(TextureFlags::RB_SWAPPED) &&
345 layersNeedsManualSwap) {
346 size_t pixels = width * height;
347 uint8_t* itr = mapped.data;
348 for (size_t i = 0; i < pixels; i++) {
349 SwapRB_R8G8B8A8(itr);
350 itr += 4;
353 texClient->RemoveFlags(TextureFlags::RB_SWAPPED);
357 return texClient.forget();
360 ////////////////////////////////////////
362 static already_AddRefed<SharedSurfaceTextureClient> CloneSurface(
363 gl::SharedSurface* src, gl::SurfaceFactory* factory) {
364 RefPtr<SharedSurfaceTextureClient> dest = factory->NewTexClient(src->mSize);
365 if (!dest) {
366 return nullptr;
369 gl::SharedSurface* destSurf = dest->Surf();
371 destSurf->ProducerAcquire();
372 bool ret = SharedSurface::ProdCopy(src, dest->Surf(), factory);
373 destSurf->ProducerRelease();
375 if (!ret) {
376 return nullptr;
379 return dest.forget();
382 void CanvasClientSharedSurface::Update(gfx::IntSize aSize,
383 ShareableCanvasRenderer* aCanvasRenderer,
384 wr::RenderRoot aRenderRoot) {
385 Renderer renderer;
386 renderer.construct<ShareableCanvasRenderer*>(aCanvasRenderer);
387 UpdateRenderer(aSize, renderer);
390 void CanvasClientSharedSurface::UpdateAsync(AsyncCanvasRenderer* aRenderer) {
391 Renderer renderer;
392 renderer.construct<AsyncCanvasRenderer*>(aRenderer);
393 UpdateRenderer(aRenderer->GetSize(), renderer);
396 void CanvasClientSharedSurface::UpdateRenderer(gfx::IntSize aSize,
397 Renderer& aRenderer) {
398 GLContext* gl = nullptr;
399 ShareableCanvasRenderer* canvasRenderer = nullptr;
400 AsyncCanvasRenderer* asyncRenderer = nullptr;
401 if (aRenderer.constructed<ShareableCanvasRenderer*>()) {
402 canvasRenderer = aRenderer.ref<ShareableCanvasRenderer*>();
403 gl = canvasRenderer->mGLContext;
404 } else {
405 asyncRenderer = aRenderer.ref<AsyncCanvasRenderer*>();
406 gl = asyncRenderer->mGLContext;
408 if (!gl->MakeCurrent()) return;
410 RefPtr<TextureClient> newFront;
412 mShSurfClient = nullptr;
413 if (gl->Screen()) {
414 mShSurfClient = gl->Screen()->Front();
415 if (mShSurfClient && mShSurfClient->GetAllocator() &&
416 mShSurfClient->GetAllocator() !=
417 GetForwarder()->GetTextureForwarder()) {
418 mShSurfClient =
419 CloneSurface(mShSurfClient->Surf(), gl->Screen()->Factory().get());
423 if (!mShSurfClient) {
424 gfxCriticalError() << "Invalid canvas front buffer or screen";
425 return;
428 newFront = mShSurfClient;
430 SharedSurface* surf = mShSurfClient->Surf();
432 if (!surf->IsBufferAvailable()) {
433 // SharedSurface is already forwared to compositor side.
434 // SharedSurface::Commit() could not be called again.
435 // It happens only with SharedSurface_SurfaceTexture.
436 if (!mNewFront && !mFront) {
437 // This could happen when CanvasClientSharedSurface is re-created, but
438 // GLScreenBuffer is not re-created.
439 // See Bug 1626142
440 mNewFront = newFront;
441 } else {
442 NS_WARNING("SharedSurface buffer not available, skip update");
444 return;
447 // Readback if needed.
448 mReadbackClient = nullptr;
450 auto forwarder = GetForwarder();
452 bool needsReadback = (surf->mType == SharedSurfaceType::Basic);
453 if (needsReadback) {
454 TextureFlags flags = TextureFlags::IMMUTABLE;
456 CompositableForwarder* shadowForwarder = nullptr;
457 if (canvasRenderer) {
458 flags |= canvasRenderer->Flags();
459 shadowForwarder = canvasRenderer->GetForwarder();
460 } else {
461 MOZ_ASSERT(asyncRenderer);
462 flags |= mTextureFlags;
463 shadowForwarder = GetForwarder();
466 auto layersBackend = shadowForwarder->GetCompositorBackendType();
467 mReadbackClient =
468 TexClientFromReadback(surf, forwarder, flags, layersBackend);
470 newFront = mReadbackClient;
471 } else {
472 mReadbackClient = nullptr;
475 surf->Commit();
477 if (asyncRenderer) {
478 // If surface type is Basic, above codes will readback
479 // the GLContext to mReadbackClient in order to send frame to
480 // compositor. We copy from this TextureClient directly by
481 // calling CopyFromTextureClient().
482 // Therefore, if main-thread want the content of GLContext,
483 // it doesn't have to readback from GLContext again.
485 // Otherwise, if surface type isn't Basic, we will read from
486 // SharedSurface directly from main-thread. We still pass
487 // mReadbackClient which is nullptr here to tell
488 // AsyncCanvasRenderer reset some properties.
489 asyncRenderer->CopyFromTextureClient(mReadbackClient);
492 if (!newFront) {
493 // May happen in a release build in case of memory pressure.
494 gfxWarning()
495 << "Failed to allocate a TextureClient for SharedSurface Canvas. Size: "
496 << aSize;
497 return;
500 mNewFront = newFront;
503 void CanvasClientSharedSurface::Updated(wr::RenderRoot aRenderRoot) {
504 if (!mNewFront) {
505 return;
508 auto forwarder = GetForwarder();
510 mFront = mNewFront;
511 mNewFront = nullptr;
513 // Add the new TexClient.
514 if (!AddTextureClient(mFront)) {
515 return;
518 AutoTArray<CompositableForwarder::TimedTextureClient, 1> textures;
519 CompositableForwarder::TimedTextureClient* t = textures.AppendElement();
520 t->mTextureClient = mFront;
521 t->mPictureRect = nsIntRect(nsIntPoint(0, 0), mFront->GetSize());
522 t->mFrameID = mFrameID;
523 forwarder->UseTextures(this, textures, Some(aRenderRoot));
526 void CanvasClientSharedSurface::OnDetach() { ClearSurfaces(); }
528 void CanvasClientSharedSurface::ClearSurfaces() {
529 mFront = nullptr;
530 mNewFront = nullptr;
531 mShSurfClient = nullptr;
532 mReadbackClient = nullptr;
535 //----------------------------------------------------------------------------
537 CanvasClientOOP::CanvasClientOOP(CompositableForwarder* aLayerForwarder,
538 TextureFlags aFlags)
539 : CanvasClient(aLayerForwarder, aFlags) {}
541 CanvasClientOOP::~CanvasClientOOP() = default;
543 void CanvasClientOOP::SetLayer(ShadowableLayer* aLayer,
544 OOPCanvasRenderer* aRenderer) {
545 mLayer = aLayer;
546 mCanvasContext = aRenderer->mContext;
547 MOZ_ASSERT(mCanvasContext);
548 Connect();
549 aRenderer->mCanvasClient = this;
552 void CanvasClientOOP::Update(gfx::IntSize aSize,
553 ShareableCanvasRenderer* aRenderer,
554 wr::RenderRoot aRenderRoot) {
555 // DLP: TODO: aRenderRoot?
556 if (!GetForwarder() || !mLayer || !mCanvasContext || !aRenderer) {
557 return;
560 // Make sure the host is using the right Compositable.
561 CompositableHandle handle = GetIPCHandle();
562 if (!handle || mHandle == handle) {
563 return;
566 MOZ_ASSERT(GetForwarder() && GetForwarder()->AsLayerForwarder() &&
567 GetForwarder()->AsLayerForwarder()->GetShadowManager());
569 static_cast<ShadowLayerForwarder*>(GetForwarder())->Attach(this, mLayer);
570 LayerTransactionChild* ltc =
571 GetForwarder()->AsLayerForwarder()->GetShadowManager();
572 bool success = mCanvasContext->UpdateCompositableHandle(ltc, handle);
573 if (!success) {
574 return;
577 mHandle = handle;
580 } // namespace layers
581 } // namespace mozilla