Bug 1833753 [wpt PR 40065] - Allow newly-added test to also pass when mutation events...
[gecko.git] / gfx / webrender_bindings / RenderCompositorANGLE.cpp
blob2ea2bf1124960055bda2a185f9c2da50d0b85215
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 "RenderCompositorANGLE.h"
9 #include "GLContext.h"
10 #include "GLContextEGL.h"
11 #include "GLContextProvider.h"
12 #include "mozilla/gfx/DeviceManagerDx.h"
13 #include "mozilla/gfx/gfxVars.h"
14 #include "mozilla/gfx/Logging.h"
15 #include "mozilla/gfx/StackArray.h"
16 #include "mozilla/layers/HelpersD3D11.h"
17 #include "mozilla/layers/SyncObject.h"
18 #include "mozilla/ProfilerMarkers.h"
19 #include "mozilla/StaticPrefs_gfx.h"
20 #include "mozilla/webrender/DCLayerTree.h"
21 #include "mozilla/webrender/RenderThread.h"
22 #include "mozilla/widget/CompositorWidget.h"
23 #include "mozilla/widget/WinCompositorWidget.h"
24 #include "mozilla/WindowsVersion.h"
25 #include "mozilla/Telemetry.h"
26 #include "nsPrintfCString.h"
27 #include "FxROutputHandler.h"
29 #undef NTDDI_VERSION
30 #define NTDDI_VERSION NTDDI_WIN8
32 #include <d3d11.h>
33 #include <dcomp.h>
34 #include <dxgi1_2.h>
36 // Flag for PrintWindow() that is defined in Winuser.h. It is defined since
37 // Windows 8.1. This allows PrintWindow to capture window content that is
38 // rendered with DirectComposition.
39 #undef PW_RENDERFULLCONTENT
40 #define PW_RENDERFULLCONTENT 0x00000002
42 namespace mozilla {
43 namespace wr {
45 extern LazyLogModule gRenderThreadLog;
46 #define LOG(...) MOZ_LOG(gRenderThreadLog, LogLevel::Debug, (__VA_ARGS__))
48 /* static */
49 UniquePtr<RenderCompositor> RenderCompositorANGLE::Create(
50 const RefPtr<widget::CompositorWidget>& aWidget, nsACString& aError) {
51 RefPtr<gl::GLContext> gl = RenderThread::Get()->SingletonGL(aError);
52 if (!gl) {
53 if (aError.IsEmpty()) {
54 aError.Assign("RcANGLE(no shared GL)"_ns);
55 } else {
56 aError.Append("(Create)"_ns);
58 return nullptr;
61 UniquePtr<RenderCompositorANGLE> compositor =
62 MakeUnique<RenderCompositorANGLE>(aWidget, std::move(gl));
63 if (!compositor->Initialize(aError)) {
64 return nullptr;
66 return compositor;
69 RenderCompositorANGLE::RenderCompositorANGLE(
70 const RefPtr<widget::CompositorWidget>& aWidget,
71 RefPtr<gl::GLContext>&& aGL)
72 : RenderCompositor(aWidget),
73 mGL(aGL),
74 mEGLConfig(nullptr),
75 mEGLSurface(nullptr),
76 mUseTripleBuffering(false),
77 mUseAlpha(false),
78 mUseNativeCompositor(true),
79 mUsePartialPresent(false),
80 mFullRender(false),
81 mDisablingNativeCompositor(false) {
82 MOZ_ASSERT(mGL);
83 LOG("RenderCompositorANGLE::RenderCompositorANGLE()");
86 RenderCompositorANGLE::~RenderCompositorANGLE() {
87 LOG("RenderCompositorANGLE::~RenderCompositorANGLE()");
89 DestroyEGLSurface();
90 MOZ_ASSERT(!mEGLSurface);
93 ID3D11Device* RenderCompositorANGLE::GetDeviceOfEGLDisplay(nsACString& aError) {
94 const auto& gle = gl::GLContextEGL::Cast(mGL);
95 const auto& egl = gle->mEgl;
96 MOZ_ASSERT(egl);
97 if (!egl ||
98 !egl->mLib->IsExtensionSupported(gl::EGLLibExtension::EXT_device_query)) {
99 aError.Assign("RcANGLE(no EXT_device_query support)"_ns);
100 return nullptr;
103 // Fetch the D3D11 device.
104 EGLDeviceEXT eglDevice = nullptr;
105 egl->fQueryDisplayAttribEXT(LOCAL_EGL_DEVICE_EXT, (EGLAttrib*)&eglDevice);
106 MOZ_ASSERT(eglDevice);
107 ID3D11Device* device = nullptr;
108 egl->mLib->fQueryDeviceAttribEXT(eglDevice, LOCAL_EGL_D3D11_DEVICE_ANGLE,
109 (EGLAttrib*)&device);
110 if (!device) {
111 aError.Assign("RcANGLE(get D3D11Device from EGLDisplay failed)"_ns);
112 return nullptr;
114 return device;
117 bool RenderCompositorANGLE::Initialize(nsACString& aError) {
118 // TODO(aosmond): This causes us to lose WebRender because it is unable to
119 // distinguish why we failed and retry once the reset is complete. This does
120 // appear to happen in the wild, so we really should try to do something
121 // differently here.
122 if (RenderThread::Get()->IsHandlingDeviceReset()) {
123 aError.Assign("RcANGLE(waiting device reset)"_ns);
124 return false;
127 // Force enable alpha channel to make sure ANGLE use correct framebuffer
128 // formart
129 const auto& gle = gl::GLContextEGL::Cast(mGL);
130 const auto& egl = gle->mEgl;
131 if (!gl::CreateConfig(*egl, &mEGLConfig, /* bpp */ 32,
132 /* enableDepthBuffer */ false, mGL->IsGLES())) {
133 aError.Assign("RcANGLE(create EGLConfig failed)"_ns);
134 return false;
136 MOZ_ASSERT(mEGLConfig);
138 mDevice = GetDeviceOfEGLDisplay(aError);
140 if (!mDevice) {
141 return false;
144 mDevice->GetImmediateContext(getter_AddRefs(mCtx));
145 if (!mCtx) {
146 aError.Assign("RcANGLE(get immediate context failed)"_ns);
147 return false;
150 // Disable native compositor when fast snapshot is needed.
151 // Taking snapshot of native compositor is very slow on Windows.
152 if (mWidget->GetCompositorOptions().NeedFastSnaphot()) {
153 mUseNativeCompositor = false;
156 // Create DCLayerTree when DirectComposition is used.
157 if (gfx::gfxVars::UseWebRenderDCompWin()) {
158 HWND compositorHwnd = GetCompositorHwnd();
159 if (compositorHwnd) {
160 mDCLayerTree = DCLayerTree::Create(mGL, mEGLConfig, mDevice, mCtx,
161 compositorHwnd, aError);
162 if (!mDCLayerTree) {
163 return false;
165 } else {
166 aError.Assign("RcANGLE(no compositor window)"_ns);
167 return false;
171 // Create SwapChain when compositor is not used
172 if (!UseCompositor()) {
173 if (!CreateSwapChain(aError)) {
174 // SwapChain creation failed.
175 return false;
179 mSyncObject = layers::SyncObjectHost::CreateSyncObjectHost(mDevice);
180 if (!mSyncObject->Init()) {
181 // Some errors occur. Clear the mSyncObject here.
182 // Then, there will be no texture synchronization.
183 aError.Assign("RcANGLE(create SyncObject failed)"_ns);
184 return false;
187 InitializeUsePartialPresent();
189 return true;
192 HWND RenderCompositorANGLE::GetCompositorHwnd() {
193 HWND hwnd = 0;
195 if (XRE_IsGPUProcess()) {
196 hwnd = mWidget->AsWindows()->GetCompositorHwnd();
197 } else if (
198 StaticPrefs::
199 gfx_webrender_enabled_no_gpu_process_with_angle_win_AtStartup()) {
200 MOZ_ASSERT(XRE_IsParentProcess());
202 // When GPU process does not exist, we do not need to use compositor window.
203 hwnd = mWidget->AsWindows()->GetHwnd();
206 return hwnd;
209 bool RenderCompositorANGLE::CreateSwapChain(nsACString& aError) {
210 MOZ_ASSERT(!UseCompositor());
212 HWND hwnd = mWidget->AsWindows()->GetHwnd();
214 RefPtr<IDXGIDevice> dxgiDevice;
215 mDevice->QueryInterface((IDXGIDevice**)getter_AddRefs(dxgiDevice));
217 RefPtr<IDXGIFactory> dxgiFactory;
219 RefPtr<IDXGIAdapter> adapter;
220 dxgiDevice->GetAdapter(getter_AddRefs(adapter));
222 adapter->GetParent(
223 IID_PPV_ARGS((IDXGIFactory**)getter_AddRefs(dxgiFactory)));
226 RefPtr<IDXGIFactory2> dxgiFactory2;
227 HRESULT hr = dxgiFactory->QueryInterface(
228 (IDXGIFactory2**)getter_AddRefs(dxgiFactory2));
229 if (FAILED(hr)) {
230 dxgiFactory2 = nullptr;
233 CreateSwapChainForDCompIfPossible(dxgiFactory2);
234 if (gfx::gfxVars::UseWebRenderDCompWin() && !mSwapChain) {
235 MOZ_ASSERT(GetCompositorHwnd());
236 aError.Assign("RcANGLE(create swapchain for dcomp failed)"_ns);
237 return false;
240 if (!mSwapChain && dxgiFactory2) {
241 RefPtr<IDXGISwapChain1> swapChain1;
242 bool useTripleBuffering = false;
244 DXGI_SWAP_CHAIN_DESC1 desc{};
245 desc.Width = 0;
246 desc.Height = 0;
247 desc.Format = DXGI_FORMAT_B8G8R8A8_UNORM;
248 desc.SampleDesc.Count = 1;
249 desc.SampleDesc.Quality = 0;
250 desc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
252 bool useFlipSequential = gfx::gfxVars::UseWebRenderFlipSequentialWin();
253 if (useFlipSequential && !mWidget->AsWindows()->GetCompositorHwnd()) {
254 useFlipSequential = false;
255 gfxCriticalNoteOnce << "FLIP_SEQUENTIAL needs CompositorHwnd. Fallback";
258 if (useFlipSequential) {
259 useTripleBuffering = gfx::gfxVars::UseWebRenderTripleBufferingWin();
260 if (useTripleBuffering) {
261 desc.BufferCount = 3;
262 } else {
263 desc.BufferCount = 2;
265 desc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL;
266 desc.Scaling = DXGI_SCALING_NONE;
267 } else {
268 desc.BufferCount = 1;
269 desc.SwapEffect = DXGI_SWAP_EFFECT_SEQUENTIAL;
270 desc.Scaling = DXGI_SCALING_STRETCH;
272 desc.Flags = 0;
274 hr = dxgiFactory2->CreateSwapChainForHwnd(
275 mDevice, hwnd, &desc, nullptr, nullptr, getter_AddRefs(swapChain1));
276 if (SUCCEEDED(hr) && swapChain1) {
277 DXGI_RGBA color = {1.0f, 1.0f, 1.0f, 1.0f};
278 swapChain1->SetBackgroundColor(&color);
279 mSwapChain = swapChain1;
280 mSwapChain1 = swapChain1;
281 mUseTripleBuffering = useTripleBuffering;
282 } else if (useFlipSequential) {
283 gfxCriticalNoteOnce << "FLIP_SEQUENTIAL is not supported. Fallback";
287 if (!mSwapChain) {
288 if (mWidget->AsWindows()->GetCompositorHwnd()) {
289 // Destroy compositor window.
290 mWidget->AsWindows()->DestroyCompositorWindow();
291 hwnd = mWidget->AsWindows()->GetHwnd();
294 DXGI_SWAP_CHAIN_DESC swapDesc{};
295 swapDesc.BufferDesc.Width = 0;
296 swapDesc.BufferDesc.Height = 0;
297 swapDesc.BufferDesc.Format = DXGI_FORMAT_B8G8R8A8_UNORM;
298 swapDesc.BufferDesc.RefreshRate.Numerator = 60;
299 swapDesc.BufferDesc.RefreshRate.Denominator = 1;
300 swapDesc.SampleDesc.Count = 1;
301 swapDesc.SampleDesc.Quality = 0;
302 swapDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
303 swapDesc.BufferCount = 1;
304 swapDesc.OutputWindow = hwnd;
305 swapDesc.Windowed = TRUE;
306 swapDesc.Flags = 0;
307 swapDesc.SwapEffect = DXGI_SWAP_EFFECT_SEQUENTIAL;
309 HRESULT hr = dxgiFactory->CreateSwapChain(dxgiDevice, &swapDesc,
310 getter_AddRefs(mSwapChain));
311 if (FAILED(hr)) {
312 aError.Assign(
313 nsPrintfCString("RcANGLE(swap chain create failed %lx)", hr));
314 return false;
317 RefPtr<IDXGISwapChain1> swapChain1;
318 hr = mSwapChain->QueryInterface(
319 (IDXGISwapChain1**)getter_AddRefs(swapChain1));
320 if (SUCCEEDED(hr)) {
321 mSwapChain1 = swapChain1;
325 // We need this because we don't want DXGI to respond to Alt+Enter.
326 dxgiFactory->MakeWindowAssociation(hwnd, DXGI_MWA_NO_WINDOW_CHANGES);
328 if (!ResizeBufferIfNeeded()) {
329 aError.Assign("RcANGLE(resize buffer failed)"_ns);
330 return false;
333 return true;
336 void RenderCompositorANGLE::CreateSwapChainForDCompIfPossible(
337 IDXGIFactory2* aDXGIFactory2) {
338 if (!aDXGIFactory2 || !mDCLayerTree) {
339 return;
342 HWND hwnd = GetCompositorHwnd();
343 if (!hwnd) {
344 // When DirectComposition or DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL is used,
345 // compositor window needs to exist.
346 if (gfx::gfxVars::UseWebRenderDCompWin() ||
347 gfx::gfxVars::UseWebRenderFlipSequentialWin()) {
348 gfxCriticalNote << "Compositor window was not created";
350 return;
353 // When compositor is enabled, CompositionSurface is used for rendering.
354 // It does not support triple buffering.
355 bool useTripleBuffering =
356 gfx::gfxVars::UseWebRenderTripleBufferingWin() && !UseCompositor();
357 // Non Glass window is common since Windows 10.
358 bool useAlpha = false;
359 RefPtr<IDXGISwapChain1> swapChain1 =
360 CreateSwapChainForDComp(useTripleBuffering, useAlpha);
361 if (swapChain1) {
362 mSwapChain = swapChain1;
363 mSwapChain1 = swapChain1;
364 mUseTripleBuffering = useTripleBuffering;
365 mUseAlpha = useAlpha;
366 mDCLayerTree->SetDefaultSwapChain(swapChain1);
367 } else {
368 // Clear CLayerTree on falire
369 mDCLayerTree = nullptr;
373 RefPtr<IDXGISwapChain1> RenderCompositorANGLE::CreateSwapChainForDComp(
374 bool aUseTripleBuffering, bool aUseAlpha) {
375 HRESULT hr;
376 RefPtr<IDXGIDevice> dxgiDevice;
377 mDevice->QueryInterface((IDXGIDevice**)getter_AddRefs(dxgiDevice));
379 RefPtr<IDXGIFactory> dxgiFactory;
381 RefPtr<IDXGIAdapter> adapter;
382 dxgiDevice->GetAdapter(getter_AddRefs(adapter));
384 adapter->GetParent(
385 IID_PPV_ARGS((IDXGIFactory**)getter_AddRefs(dxgiFactory)));
388 RefPtr<IDXGIFactory2> dxgiFactory2;
389 hr = dxgiFactory->QueryInterface(
390 (IDXGIFactory2**)getter_AddRefs(dxgiFactory2));
391 if (FAILED(hr)) {
392 return nullptr;
395 RefPtr<IDXGISwapChain1> swapChain1;
396 DXGI_SWAP_CHAIN_DESC1 desc{};
397 // DXGI does not like 0x0 swapchains. Swap chain creation failed when 0x0 was
398 // set.
399 desc.Width = 1;
400 desc.Height = 1;
401 desc.Format = DXGI_FORMAT_B8G8R8A8_UNORM;
402 desc.SampleDesc.Count = 1;
403 desc.SampleDesc.Quality = 0;
404 desc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
405 if (aUseTripleBuffering) {
406 desc.BufferCount = 3;
407 } else {
408 desc.BufferCount = 2;
410 // DXGI_SCALING_NONE caused swap chain creation failure.
411 desc.Scaling = DXGI_SCALING_STRETCH;
412 desc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL;
413 if (aUseAlpha) {
414 // This could degrade performance. Use it only when it is necessary.
415 desc.AlphaMode = DXGI_ALPHA_MODE_PREMULTIPLIED;
416 } else {
417 desc.AlphaMode = DXGI_ALPHA_MODE_IGNORE;
419 desc.Flags = 0;
421 hr = dxgiFactory2->CreateSwapChainForComposition(mDevice, &desc, nullptr,
422 getter_AddRefs(swapChain1));
423 if (SUCCEEDED(hr) && swapChain1) {
424 DXGI_RGBA color = {1.0f, 1.0f, 1.0f, 1.0f};
425 swapChain1->SetBackgroundColor(&color);
426 return swapChain1;
429 return nullptr;
432 bool RenderCompositorANGLE::BeginFrame() {
433 mWidget->AsWindows()->UpdateCompositorWndSizeIfNecessary();
435 if (!UseCompositor()) {
436 if (mDCLayerTree) {
437 bool useAlpha = mWidget->AsWindows()->HasGlass();
438 // When Alpha usage is changed, SwapChain needs to be recreatd.
439 if (useAlpha != mUseAlpha) {
440 DestroyEGLSurface();
441 mBufferSize.reset();
443 RefPtr<IDXGISwapChain1> swapChain1 =
444 CreateSwapChainForDComp(mUseTripleBuffering, useAlpha);
445 if (swapChain1) {
446 mSwapChain = swapChain1;
447 mUseAlpha = useAlpha;
448 mDCLayerTree->SetDefaultSwapChain(swapChain1);
449 // When alpha is used, we want to disable partial present.
450 // See Bug 1595027.
451 if (useAlpha) {
452 mFullRender = true;
454 } else {
455 gfxCriticalNote << "Failed to re-create SwapChain";
456 RenderThread::Get()->HandleWebRenderError(
457 WebRenderError::NEW_SURFACE);
458 return false;
463 if (!ResizeBufferIfNeeded()) {
464 return false;
468 if (!MakeCurrent()) {
469 gfxCriticalNote << "Failed to make render context current, can't draw.";
470 return false;
473 if (RenderThread::Get()->SyncObjectNeeded() && mSyncObject) {
474 if (!mSyncObject->Synchronize(/* aFallible */ true)) {
475 // It's timeout or other error. Handle the device-reset here.
476 RenderThread::Get()->HandleDeviceReset(
477 "SyncObject", LOCAL_GL_UNKNOWN_CONTEXT_RESET_ARB);
478 return false;
481 return true;
484 RenderedFrameId RenderCompositorANGLE::EndFrame(
485 const nsTArray<DeviceIntRect>& aDirtyRects) {
486 RenderedFrameId frameId = GetNextRenderFrameId();
487 InsertGraphicsCommandsFinishedWaitQuery(frameId);
489 if (!UseCompositor()) {
490 auto start = TimeStamp::Now();
491 if (mWidget->AsWindows()->HasFxrOutputHandler()) {
492 // There is a Firefox Reality handler for this swapchain. Update this
493 // window's contents to the VR window.
494 FxROutputHandler* fxrHandler =
495 mWidget->AsWindows()->GetFxrOutputHandler();
496 if (fxrHandler->TryInitialize(mSwapChain, mDevice)) {
497 fxrHandler->UpdateOutput(mCtx);
501 const LayoutDeviceIntSize& bufferSize = mBufferSize.ref();
503 // During high contrast mode, alpha is used. In this case,
504 // IDXGISwapChain1::Present1 shows nothing with compositor window.
505 // In this case, we want to disable partial present by full render.
506 // See Bug 1595027
507 MOZ_ASSERT_IF(mUsePartialPresent && mUseAlpha, mFullRender);
509 if (mUsePartialPresent && !mUseAlpha && mSwapChain1) {
510 // Clear full render flag.
511 mFullRender = false;
512 // If there is no diry rect, we skip SwapChain present.
513 if (!aDirtyRects.IsEmpty()) {
514 int rectsCount = 0;
515 StackArray<RECT, 1> rects(aDirtyRects.Length());
517 for (size_t i = 0; i < aDirtyRects.Length(); ++i) {
518 const DeviceIntRect& rect = aDirtyRects[i];
519 // Clip rect to bufferSize
520 int left = std::max(0, std::min(rect.min.x, bufferSize.width));
521 int top = std::max(0, std::min(rect.min.y, bufferSize.height));
522 int right = std::max(0, std::min(rect.max.x, bufferSize.width));
523 int bottom = std::max(0, std::min(rect.max.y, bufferSize.height));
525 // When rect is not empty, the rect could be passed to Present1().
526 if (left < right && top < bottom) {
527 rects[rectsCount].left = left;
528 rects[rectsCount].top = top;
529 rects[rectsCount].right = right;
530 rects[rectsCount].bottom = bottom;
531 rectsCount++;
535 if (rectsCount > 0) {
536 DXGI_PRESENT_PARAMETERS params;
537 PodZero(&params);
538 params.DirtyRectsCount = rectsCount;
539 params.pDirtyRects = rects.data();
541 HRESULT hr;
542 hr = mSwapChain1->Present1(0, 0, &params);
543 if (FAILED(hr) && hr != DXGI_STATUS_OCCLUDED) {
544 gfxCriticalNote << "Present1 failed: " << gfx::hexa(hr);
545 mFullRender = true;
549 } else {
550 mSwapChain->Present(0, 0);
552 auto end = TimeStamp::Now();
553 mozilla::Telemetry::Accumulate(mozilla::Telemetry::COMPOSITE_SWAP_TIME,
554 (end - start).ToMilliseconds() * 10.);
557 if (mDisablingNativeCompositor) {
558 // During disabling native compositor, we need to wait all gpu tasks
559 // complete. Otherwise, rendering window could cause white flash.
560 WaitForPreviousGraphicsCommandsFinishedQuery(/* aWaitAll */ true);
561 mDisablingNativeCompositor = false;
564 if (mDCLayerTree) {
565 mDCLayerTree->MaybeUpdateDebug();
566 mDCLayerTree->MaybeCommit();
569 return frameId;
572 bool RenderCompositorANGLE::WaitForGPU() {
573 // Note: this waits on the query we inserted in the previous frame,
574 // not the one we just inserted now. Example:
575 // Insert query #1
576 // Present #1
577 // (first frame, no wait)
578 // Insert query #2
579 // Present #2
580 // Wait for query #1.
581 // Insert query #3
582 // Present #3
583 // Wait for query #2.
585 // This ensures we're done reading textures before swapping buffers.
586 if (!StaticPrefs::gfx_webrender_wait_gpu_finished_disabled_AtStartup()) {
587 return WaitForPreviousGraphicsCommandsFinishedQuery();
589 return true;
592 bool RenderCompositorANGLE::ResizeBufferIfNeeded() {
593 MOZ_ASSERT(mSwapChain);
595 LayoutDeviceIntSize size = mWidget->GetClientSize();
597 // DXGI does not like 0x0 swapchains. ResizeBuffers() failed when 0x0 was set
598 // when DComp is used.
599 size.width = std::max(size.width, 1);
600 size.height = std::max(size.height, 1);
602 if (mBufferSize.isSome() && mBufferSize.ref() == size) {
603 MOZ_ASSERT(mEGLSurface);
604 return true;
607 // Release EGLSurface of back buffer before calling ResizeBuffers().
608 DestroyEGLSurface();
610 mBufferSize = Some(size);
612 if (!CreateEGLSurface()) {
613 mBufferSize.reset();
614 return false;
617 if (mUsePartialPresent) {
618 mFullRender = true;
620 return true;
623 bool RenderCompositorANGLE::CreateEGLSurface() {
624 MOZ_ASSERT(mBufferSize.isSome());
625 MOZ_ASSERT(mEGLSurface == EGL_NO_SURFACE);
627 HRESULT hr;
628 RefPtr<ID3D11Texture2D> backBuf;
630 if (mBufferSize.isNothing()) {
631 gfxCriticalNote << "Buffer size is invalid";
632 return false;
635 const LayoutDeviceIntSize& size = mBufferSize.ref();
637 // Resize swap chain
638 DXGI_SWAP_CHAIN_DESC desc;
639 hr = mSwapChain->GetDesc(&desc);
640 if (FAILED(hr)) {
641 gfxCriticalNote << "Failed to read swap chain description: "
642 << gfx::hexa(hr) << " Size : " << size;
643 return false;
645 hr = mSwapChain->ResizeBuffers(desc.BufferCount, size.width, size.height,
646 DXGI_FORMAT_B8G8R8A8_UNORM, 0);
647 if (FAILED(hr)) {
648 gfxCriticalNote << "Failed to resize swap chain buffers: " << gfx::hexa(hr)
649 << " Size : " << size;
650 return false;
653 hr = mSwapChain->GetBuffer(0, __uuidof(ID3D11Texture2D),
654 (void**)getter_AddRefs(backBuf));
655 if (hr == DXGI_ERROR_INVALID_CALL) {
656 // This happens on some GPUs/drivers when there's a TDR.
657 if (mDevice->GetDeviceRemovedReason() != S_OK) {
658 gfxCriticalError() << "GetBuffer returned invalid call: " << gfx::hexa(hr)
659 << " Size : " << size;
660 return false;
664 const EGLint pbuffer_attribs[]{LOCAL_EGL_WIDTH, size.width, LOCAL_EGL_HEIGHT,
665 size.height, LOCAL_EGL_NONE};
667 const auto buffer = reinterpret_cast<EGLClientBuffer>(backBuf.get());
669 const auto& gle = gl::GLContextEGL::Cast(mGL);
670 const auto& egl = gle->mEgl;
671 const EGLSurface surface = egl->fCreatePbufferFromClientBuffer(
672 LOCAL_EGL_D3D_TEXTURE_ANGLE, buffer, mEGLConfig, pbuffer_attribs);
674 if (!surface) {
675 EGLint err = egl->mLib->fGetError();
676 gfxCriticalError() << "Failed to create Pbuffer of back buffer error: "
677 << gfx::hexa(err) << " Size : " << size;
678 return false;
681 mEGLSurface = surface;
683 return true;
686 void RenderCompositorANGLE::DestroyEGLSurface() {
687 // Release EGLSurface of back buffer before calling ResizeBuffers().
688 if (mEGLSurface) {
689 const auto& gle = gl::GLContextEGL::Cast(gl());
690 const auto& egl = gle->mEgl;
691 gle->SetEGLSurfaceOverride(EGL_NO_SURFACE);
692 egl->fDestroySurface(mEGLSurface);
693 mEGLSurface = nullptr;
697 void RenderCompositorANGLE::Pause() {}
699 bool RenderCompositorANGLE::Resume() { return true; }
701 void RenderCompositorANGLE::Update() {
702 // Update compositor window's size if it exists.
703 // It needs to be called here, since OS might update compositor
704 // window's size at unexpected timing.
705 mWidget->AsWindows()->UpdateCompositorWndSizeIfNecessary();
708 bool RenderCompositorANGLE::MakeCurrent() {
709 gl::GLContextEGL::Cast(gl())->SetEGLSurfaceOverride(mEGLSurface);
710 return gl()->MakeCurrent();
713 LayoutDeviceIntSize RenderCompositorANGLE::GetBufferSize() {
714 if (!UseCompositor()) {
715 MOZ_ASSERT(mBufferSize.isSome());
716 if (mBufferSize.isNothing()) {
717 return LayoutDeviceIntSize();
719 return mBufferSize.ref();
720 } else {
721 auto size = mWidget->GetClientSize();
722 // This size is used for WR DEBUG_OVERLAY. Its DCTile does not like 0.
723 size.width = std::max(size.width, 1);
724 size.height = std::max(size.height, 1);
725 return size;
729 RefPtr<ID3D11Query> RenderCompositorANGLE::GetD3D11Query() {
730 RefPtr<ID3D11Query> query;
732 if (mRecycledQuery) {
733 query = mRecycledQuery.forget();
734 return query;
737 CD3D11_QUERY_DESC desc(D3D11_QUERY_EVENT);
738 HRESULT hr = mDevice->CreateQuery(&desc, getter_AddRefs(query));
739 if (FAILED(hr) || !query) {
740 gfxWarning() << "Could not create D3D11_QUERY_EVENT: " << gfx::hexa(hr);
741 return nullptr;
743 return query;
746 void RenderCompositorANGLE::InsertGraphicsCommandsFinishedWaitQuery(
747 RenderedFrameId aFrameId) {
748 RefPtr<ID3D11Query> query;
749 query = GetD3D11Query();
750 if (!query) {
751 return;
754 mCtx->End(query);
755 mCtx->Flush();
756 mWaitForPresentQueries.emplace(aFrameId, query);
759 bool RenderCompositorANGLE::WaitForPreviousGraphicsCommandsFinishedQuery(
760 bool aWaitAll) {
761 size_t waitLatency = mUseTripleBuffering ? 3 : 2;
762 if (aWaitAll) {
763 waitLatency = 1;
766 while (mWaitForPresentQueries.size() >= waitLatency) {
767 auto queryPair = mWaitForPresentQueries.front();
768 BOOL result;
769 bool ret =
770 layers::WaitForFrameGPUQuery(mDevice, mCtx, queryPair.second, &result);
772 if (!ret) {
773 mWaitForPresentQueries.pop();
774 return false;
777 // Recycle query for later use.
778 mRecycledQuery = queryPair.second;
779 mLastCompletedFrameId = queryPair.first;
780 mWaitForPresentQueries.pop();
782 return true;
785 RenderedFrameId RenderCompositorANGLE::GetLastCompletedFrameId() {
786 while (!mWaitForPresentQueries.empty()) {
787 auto queryPair = mWaitForPresentQueries.front();
788 if (mCtx->GetData(queryPair.second, nullptr, 0,
789 D3D11_ASYNC_GETDATA_DONOTFLUSH) != S_OK) {
790 break;
793 mRecycledQuery = queryPair.second;
794 mLastCompletedFrameId = queryPair.first;
795 mWaitForPresentQueries.pop();
798 nsPrintfCString marker("Pending frames %u",
799 (uint32_t)mWaitForPresentQueries.size());
800 PROFILER_MARKER_TEXT("GetLastCompletedFrameId", GRAPHICS, {}, marker);
802 return mLastCompletedFrameId;
805 RenderedFrameId RenderCompositorANGLE::UpdateFrameId() {
806 RenderedFrameId frameId = GetNextRenderFrameId();
807 InsertGraphicsCommandsFinishedWaitQuery(frameId);
808 return frameId;
811 GLenum RenderCompositorANGLE::IsContextLost(bool aForce) {
812 // glGetGraphicsResetStatus does not always work to detect timeout detection
813 // and recovery (TDR). On Windows, ANGLE itself is just relying upon the same
814 // API, so we should not need to check it separately.
815 auto reason = mDevice->GetDeviceRemovedReason();
816 switch (reason) {
817 case S_OK:
818 return LOCAL_GL_NO_ERROR;
819 case DXGI_ERROR_DEVICE_REMOVED:
820 case DXGI_ERROR_DRIVER_INTERNAL_ERROR:
821 NS_WARNING("Device reset due to system / different device");
822 return LOCAL_GL_INNOCENT_CONTEXT_RESET_ARB;
823 case DXGI_ERROR_DEVICE_HUNG:
824 case DXGI_ERROR_DEVICE_RESET:
825 case DXGI_ERROR_INVALID_CALL:
826 gfxCriticalError() << "Device reset due to WR device: "
827 << gfx::hexa(reason);
828 return LOCAL_GL_GUILTY_CONTEXT_RESET_ARB;
829 default:
830 gfxCriticalError() << "Device reset with WR device unexpected reason: "
831 << gfx::hexa(reason);
832 return LOCAL_GL_UNKNOWN_CONTEXT_RESET_ARB;
836 bool RenderCompositorANGLE::UseCompositor() {
837 if (!mUseNativeCompositor) {
838 return false;
841 if (!mDCLayerTree || !gfx::gfxVars::UseWebRenderCompositor()) {
842 return false;
844 return true;
847 bool RenderCompositorANGLE::SupportAsyncScreenshot() {
848 return !UseCompositor() && !mDisablingNativeCompositor;
851 bool RenderCompositorANGLE::ShouldUseNativeCompositor() {
852 return UseCompositor();
855 void RenderCompositorANGLE::CompositorBeginFrame() {
856 mDCLayerTree->CompositorBeginFrame();
859 void RenderCompositorANGLE::CompositorEndFrame() {
860 mDCLayerTree->CompositorEndFrame();
863 void RenderCompositorANGLE::Bind(wr::NativeTileId aId,
864 wr::DeviceIntPoint* aOffset, uint32_t* aFboId,
865 wr::DeviceIntRect aDirtyRect,
866 wr::DeviceIntRect aValidRect) {
867 mDCLayerTree->Bind(aId, aOffset, aFboId, aDirtyRect, aValidRect);
870 void RenderCompositorANGLE::Unbind() { mDCLayerTree->Unbind(); }
872 void RenderCompositorANGLE::CreateSurface(wr::NativeSurfaceId aId,
873 wr::DeviceIntPoint aVirtualOffset,
874 wr::DeviceIntSize aTileSize,
875 bool aIsOpaque) {
876 mDCLayerTree->CreateSurface(aId, aVirtualOffset, aTileSize, aIsOpaque);
879 void RenderCompositorANGLE::CreateExternalSurface(wr::NativeSurfaceId aId,
880 bool aIsOpaque) {
881 mDCLayerTree->CreateExternalSurface(aId, aIsOpaque);
884 void RenderCompositorANGLE::DestroySurface(NativeSurfaceId aId) {
885 mDCLayerTree->DestroySurface(aId);
888 void RenderCompositorANGLE::CreateTile(wr::NativeSurfaceId aId, int aX,
889 int aY) {
890 mDCLayerTree->CreateTile(aId, aX, aY);
893 void RenderCompositorANGLE::DestroyTile(wr::NativeSurfaceId aId, int aX,
894 int aY) {
895 mDCLayerTree->DestroyTile(aId, aX, aY);
898 void RenderCompositorANGLE::AttachExternalImage(
899 wr::NativeSurfaceId aId, wr::ExternalImageId aExternalImage) {
900 mDCLayerTree->AttachExternalImage(aId, aExternalImage);
903 void RenderCompositorANGLE::AddSurface(
904 wr::NativeSurfaceId aId, const wr::CompositorSurfaceTransform& aTransform,
905 wr::DeviceIntRect aClipRect, wr::ImageRendering aImageRendering) {
906 mDCLayerTree->AddSurface(aId, aTransform, aClipRect, aImageRendering);
909 void RenderCompositorANGLE::GetCompositorCapabilities(
910 CompositorCapabilities* aCaps) {
911 RenderCompositor::GetCompositorCapabilities(aCaps);
913 if (StaticPrefs::gfx_webrender_dcomp_use_virtual_surfaces_AtStartup()) {
914 aCaps->virtual_surface_size = VIRTUAL_SURFACE_SIZE;
915 } else {
916 aCaps->virtual_surface_size = 0;
918 // DComp video overlay does not support negative scaling. See Bug 1831820
919 aCaps->supports_external_compositor_surface_negative_scaling = false;
922 void RenderCompositorANGLE::EnableNativeCompositor(bool aEnable) {
923 // XXX Re-enable native compositor is not handled yet.
924 MOZ_RELEASE_ASSERT(!mDisablingNativeCompositor);
925 MOZ_RELEASE_ASSERT(!aEnable);
926 LOG("RenderCompositorANGLE::EnableNativeCompositor() aEnable %d", aEnable);
928 if (!UseCompositor()) {
929 return;
932 mUseNativeCompositor = false;
933 mDCLayerTree->DisableNativeCompositor();
935 bool useAlpha = mWidget->AsWindows()->HasGlass();
936 DestroyEGLSurface();
937 mBufferSize.reset();
939 RefPtr<IDXGISwapChain1> swapChain1 =
940 CreateSwapChainForDComp(mUseTripleBuffering, useAlpha);
941 if (swapChain1) {
942 mSwapChain = swapChain1;
943 mUseAlpha = useAlpha;
944 mDCLayerTree->SetDefaultSwapChain(swapChain1);
945 // When alpha is used, we want to disable partial present.
946 // See Bug 1595027.
947 if (useAlpha) {
948 mFullRender = true;
950 ResizeBufferIfNeeded();
951 } else {
952 gfxCriticalNote << "Failed to re-create SwapChain";
953 RenderThread::Get()->HandleWebRenderError(WebRenderError::NEW_SURFACE);
954 return;
956 mDisablingNativeCompositor = true;
959 void RenderCompositorANGLE::InitializeUsePartialPresent() {
960 // Even when mSwapChain1 is null, we could enable WR partial present, since
961 // when mSwapChain1 is null, SwapChain is blit model swap chain with one
962 // buffer.
963 if (UseCompositor() || mWidget->AsWindows()->HasFxrOutputHandler() ||
964 gfx::gfxVars::WebRenderMaxPartialPresentRects() <= 0) {
965 mUsePartialPresent = false;
966 } else {
967 mUsePartialPresent = true;
971 bool RenderCompositorANGLE::UsePartialPresent() { return mUsePartialPresent; }
973 bool RenderCompositorANGLE::RequestFullRender() { return mFullRender; }
975 uint32_t RenderCompositorANGLE::GetMaxPartialPresentRects() {
976 if (!mUsePartialPresent) {
977 return 0;
979 return gfx::gfxVars::WebRenderMaxPartialPresentRects();
982 bool RenderCompositorANGLE::MaybeReadback(
983 const gfx::IntSize& aReadbackSize, const wr::ImageFormat& aReadbackFormat,
984 const Range<uint8_t>& aReadbackBuffer, bool* aNeedsYFlip) {
985 MOZ_ASSERT(aReadbackFormat == wr::ImageFormat::BGRA8);
987 if (!UseCompositor()) {
988 return false;
991 auto start = TimeStamp::Now();
993 HDC nulldc = ::GetDC(NULL);
994 HDC dc = ::CreateCompatibleDC(nulldc);
995 ::ReleaseDC(nullptr, nulldc);
996 if (!dc) {
997 gfxCriticalError() << "CreateCompatibleDC failed";
998 return false;
1001 BITMAPV4HEADER header;
1002 memset(&header, 0, sizeof(BITMAPV4HEADER));
1003 header.bV4Size = sizeof(BITMAPV4HEADER);
1004 header.bV4Width = aReadbackSize.width;
1005 header.bV4Height = -LONG(aReadbackSize.height); // top-to-buttom DIB
1006 header.bV4Planes = 1;
1007 header.bV4BitCount = 32;
1008 header.bV4V4Compression = BI_BITFIELDS;
1009 header.bV4RedMask = 0x00FF0000;
1010 header.bV4GreenMask = 0x0000FF00;
1011 header.bV4BlueMask = 0x000000FF;
1012 header.bV4AlphaMask = 0xFF000000;
1014 void* readbackBits = nullptr;
1015 HBITMAP bitmap =
1016 ::CreateDIBSection(dc, reinterpret_cast<BITMAPINFO*>(&header),
1017 DIB_RGB_COLORS, &readbackBits, nullptr, 0);
1018 if (!bitmap) {
1019 ::DeleteDC(dc);
1020 gfxCriticalError() << "CreateDIBSection failed";
1021 return false;
1024 ::SelectObject(dc, bitmap);
1026 UINT flags = PW_CLIENTONLY | PW_RENDERFULLCONTENT;
1027 HWND hwnd = mWidget->AsWindows()->GetHwnd();
1029 mDCLayerTree->WaitForCommitCompletion();
1031 BOOL result = ::PrintWindow(hwnd, dc, flags);
1032 if (!result) {
1033 ::DeleteObject(bitmap);
1034 ::DeleteDC(dc);
1035 gfxCriticalError() << "PrintWindow failed";
1036 return false;
1039 ::GdiFlush();
1041 memcpy(&aReadbackBuffer[0], readbackBits, aReadbackBuffer.length());
1043 ::DeleteObject(bitmap);
1044 ::DeleteDC(dc);
1046 uint32_t latencyMs = round((TimeStamp::Now() - start).ToMilliseconds());
1047 if (latencyMs > 500) {
1048 gfxCriticalNote << "Readback took too long: " << latencyMs << " ms";
1051 if (aNeedsYFlip) {
1052 *aNeedsYFlip = false;
1055 return true;
1058 } // namespace wr
1059 } // namespace mozilla