Bug 1918529 - fix some subpixel misalignment issues with gfx.webrender.svg-filter...
[gecko.git] / gfx / webrender_bindings / RenderCompositorANGLE.cpp
blob69ae53d2636ea2488d3773e91efeffd529c308db
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/TextureD3D11.h"
17 #include "mozilla/layers/HelpersD3D11.h"
18 #include "mozilla/layers/SyncObject.h"
19 #include "mozilla/ProfilerMarkers.h"
20 #include "mozilla/StaticPrefs_gfx.h"
21 #include "mozilla/webrender/DCLayerTree.h"
22 #include "mozilla/webrender/RenderThread.h"
23 #include "mozilla/widget/CompositorWidget.h"
24 #include "mozilla/widget/WinCompositorWidget.h"
25 #include "mozilla/WindowsVersion.h"
26 #include "mozilla/Telemetry.h"
27 #include "nsPrintfCString.h"
28 #include "FxROutputHandler.h"
30 #include <d3d11.h>
31 #include <dcomp.h>
32 #include <dxgi1_2.h>
34 // Flag for PrintWindow() that is defined in Winuser.h. It is defined since
35 // Windows 8.1. This allows PrintWindow to capture window content that is
36 // rendered with DirectComposition.
37 #undef PW_RENDERFULLCONTENT
38 #define PW_RENDERFULLCONTENT 0x00000002
40 namespace mozilla::wr {
42 extern LazyLogModule gRenderThreadLog;
43 #define LOG(...) MOZ_LOG(gRenderThreadLog, LogLevel::Debug, (__VA_ARGS__))
45 /* static */
46 UniquePtr<RenderCompositor> RenderCompositorANGLE::Create(
47 const RefPtr<widget::CompositorWidget>& aWidget, nsACString& aError) {
48 RefPtr<gl::GLContext> gl = RenderThread::Get()->SingletonGL(aError);
49 if (!gl) {
50 if (aError.IsEmpty()) {
51 aError.Assign("RcANGLE(no shared GL)"_ns);
52 } else {
53 aError.Append("(Create)"_ns);
55 return nullptr;
58 UniquePtr<RenderCompositorANGLE> compositor =
59 MakeUnique<RenderCompositorANGLE>(aWidget, std::move(gl));
60 if (!compositor->Initialize(aError)) {
61 return nullptr;
63 return compositor;
66 RenderCompositorANGLE::RenderCompositorANGLE(
67 const RefPtr<widget::CompositorWidget>& aWidget,
68 RefPtr<gl::GLContext>&& aGL)
69 : RenderCompositor(aWidget), mGL(aGL) {
70 MOZ_ASSERT(mGL);
71 LOG("RenderCompositorANGLE::RenderCompositorANGLE()");
74 RenderCompositorANGLE::~RenderCompositorANGLE() {
75 LOG("RenderCompositorANGLE::~RenderCompositorANGLE()");
77 DestroyEGLSurface();
78 MOZ_ASSERT(!mEGLSurface);
81 ID3D11Device* RenderCompositorANGLE::GetDeviceOfEGLDisplay(nsACString& aError) {
82 const auto& gle = gl::GLContextEGL::Cast(mGL);
83 const auto& egl = gle->mEgl;
84 MOZ_ASSERT(egl);
85 if (!egl ||
86 !egl->mLib->IsExtensionSupported(gl::EGLLibExtension::EXT_device_query)) {
87 aError.Assign("RcANGLE(no EXT_device_query support)"_ns);
88 return nullptr;
91 // Fetch the D3D11 device.
92 EGLDeviceEXT eglDevice = nullptr;
93 egl->fQueryDisplayAttribEXT(LOCAL_EGL_DEVICE_EXT, (EGLAttrib*)&eglDevice);
94 MOZ_ASSERT(eglDevice);
95 ID3D11Device* device = nullptr;
96 egl->mLib->fQueryDeviceAttribEXT(eglDevice, LOCAL_EGL_D3D11_DEVICE_ANGLE,
97 (EGLAttrib*)&device);
98 if (!device) {
99 aError.Assign("RcANGLE(get D3D11Device from EGLDisplay failed)"_ns);
100 return nullptr;
102 return device;
105 bool RenderCompositorANGLE::Initialize(nsACString& aError) {
106 // TODO(aosmond): This causes us to lose WebRender because it is unable to
107 // distinguish why we failed and retry once the reset is complete. This does
108 // appear to happen in the wild, so we really should try to do something
109 // differently here.
110 if (RenderThread::Get()->IsHandlingDeviceReset()) {
111 aError.Assign("RcANGLE(waiting device reset)"_ns);
112 return false;
115 // Force enable alpha channel to make sure ANGLE use correct framebuffer
116 // formart
117 const auto& gle = gl::GLContextEGL::Cast(mGL);
118 const auto& egl = gle->mEgl;
119 if (!gl::CreateConfig(*egl, &mEGLConfig, /* bpp */ 32,
120 /* enableDepthBuffer */ false, mGL->IsGLES())) {
121 aError.Assign("RcANGLE(create EGLConfig failed)"_ns);
122 return false;
124 MOZ_ASSERT(mEGLConfig);
126 mDevice = GetDeviceOfEGLDisplay(aError);
128 if (!mDevice) {
129 return false;
132 mDevice->GetImmediateContext(getter_AddRefs(mCtx));
133 if (!mCtx) {
134 aError.Assign("RcANGLE(get immediate context failed)"_ns);
135 return false;
138 // Disable native compositor when fast snapshot is needed.
139 // Taking snapshot of native compositor is very slow on Windows.
140 if (mWidget->GetCompositorOptions().NeedFastSnaphot()) {
141 mUseNativeCompositor = false;
144 // Create DCLayerTree when DirectComposition is used.
145 if (gfx::gfxVars::UseWebRenderDCompWin()) {
146 HWND compositorHwnd = GetCompositorHwnd();
147 if (compositorHwnd) {
148 mDCLayerTree = DCLayerTree::Create(mGL, mEGLConfig, mDevice, mCtx,
149 compositorHwnd, aError);
150 if (!mDCLayerTree) {
151 return false;
153 } else {
154 aError.Assign("RcANGLE(no compositor window)"_ns);
155 return false;
159 // Create SwapChain when compositor is not used
160 if (!UseCompositor()) {
161 if (!CreateSwapChain(aError)) {
162 // SwapChain creation failed.
163 return false;
167 mSyncObject = layers::SyncObjectHost::CreateSyncObjectHost(mDevice);
168 if (!mSyncObject->Init()) {
169 // Some errors occur. Clear the mSyncObject here.
170 // Then, there will be no texture synchronization.
171 aError.Assign("RcANGLE(create SyncObject failed)"_ns);
172 return false;
175 InitializeUsePartialPresent();
177 return true;
180 HWND RenderCompositorANGLE::GetCompositorHwnd() {
181 HWND hwnd = 0;
183 if (XRE_IsGPUProcess()) {
184 hwnd = mWidget->AsWindows()->GetCompositorHwnd();
185 } else if (
186 StaticPrefs::
187 gfx_webrender_enabled_no_gpu_process_with_angle_win_AtStartup()) {
188 MOZ_ASSERT(XRE_IsParentProcess());
190 // When GPU process does not exist, we do not need to use compositor window.
191 hwnd = mWidget->AsWindows()->GetHwnd();
194 return hwnd;
197 bool RenderCompositorANGLE::CreateSwapChain(nsACString& aError) {
198 MOZ_ASSERT(!UseCompositor());
200 mFirstPresent = true;
201 HWND hwnd = mWidget->AsWindows()->GetHwnd();
203 RefPtr<IDXGIDevice> dxgiDevice;
204 mDevice->QueryInterface((IDXGIDevice**)getter_AddRefs(dxgiDevice));
206 RefPtr<IDXGIFactory> dxgiFactory;
208 RefPtr<IDXGIAdapter> adapter;
209 dxgiDevice->GetAdapter(getter_AddRefs(adapter));
211 adapter->GetParent(
212 IID_PPV_ARGS((IDXGIFactory**)getter_AddRefs(dxgiFactory)));
215 RefPtr<IDXGIFactory2> dxgiFactory2;
216 HRESULT hr = dxgiFactory->QueryInterface(
217 (IDXGIFactory2**)getter_AddRefs(dxgiFactory2));
218 if (FAILED(hr)) {
219 dxgiFactory2 = nullptr;
222 CreateSwapChainForDCompIfPossible(dxgiFactory2);
223 if (gfx::gfxVars::UseWebRenderDCompWin() && !mSwapChain) {
224 MOZ_ASSERT(GetCompositorHwnd());
225 aError.Assign("RcANGLE(create swapchain for dcomp failed)"_ns);
226 return false;
229 const bool alpha = ShouldUseAlpha();
230 if (!mSwapChain && dxgiFactory2) {
231 RefPtr<IDXGISwapChain1> swapChain1;
232 bool useTripleBuffering = false;
234 DXGI_SWAP_CHAIN_DESC1 desc{};
235 desc.Width = 0;
236 desc.Height = 0;
237 desc.Format = DXGI_FORMAT_B8G8R8A8_UNORM;
238 desc.SampleDesc.Count = 1;
239 desc.SampleDesc.Quality = 0;
240 desc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
241 bool useFlipSequential = gfx::gfxVars::UseWebRenderFlipSequentialWin();
242 if (useFlipSequential && !mWidget->AsWindows()->GetCompositorHwnd()) {
243 useFlipSequential = false;
244 gfxCriticalNoteOnce << "FLIP_SEQUENTIAL needs CompositorHwnd. Fallback";
247 if (useFlipSequential) {
248 useTripleBuffering = gfx::gfxVars::UseWebRenderTripleBufferingWin();
249 if (useTripleBuffering) {
250 desc.BufferCount = 3;
251 } else {
252 desc.BufferCount = 2;
254 desc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL;
255 desc.Scaling = DXGI_SCALING_NONE;
256 } else {
257 desc.BufferCount = 1;
258 desc.SwapEffect = DXGI_SWAP_EFFECT_SEQUENTIAL;
259 desc.Scaling = DXGI_SCALING_STRETCH;
261 desc.AlphaMode =
262 alpha ? DXGI_ALPHA_MODE_PREMULTIPLIED : DXGI_ALPHA_MODE_IGNORE;
263 desc.Flags = 0;
265 hr = dxgiFactory2->CreateSwapChainForHwnd(
266 mDevice, hwnd, &desc, nullptr, nullptr, getter_AddRefs(swapChain1));
267 if (SUCCEEDED(hr) && swapChain1) {
268 DXGI_RGBA color = {1.0f, 1.0f, 1.0f, 1.0f};
269 swapChain1->SetBackgroundColor(&color);
270 mSwapChain = swapChain1;
271 mSwapChain1 = swapChain1;
272 mUseTripleBuffering = useTripleBuffering;
273 mSwapChainUsingAlpha = alpha;
274 } else if (useFlipSequential) {
275 gfxCriticalNoteOnce << "FLIP_SEQUENTIAL is not supported. Fallback";
279 if (!mSwapChain) {
280 if (mWidget->AsWindows()->GetCompositorHwnd()) {
281 // Destroy compositor window.
282 mWidget->AsWindows()->DestroyCompositorWindow();
283 hwnd = mWidget->AsWindows()->GetHwnd();
286 DXGI_SWAP_CHAIN_DESC swapDesc{};
287 swapDesc.BufferDesc.Width = 0;
288 swapDesc.BufferDesc.Height = 0;
289 swapDesc.BufferDesc.Format = DXGI_FORMAT_B8G8R8A8_UNORM;
290 swapDesc.BufferDesc.RefreshRate.Numerator = 60;
291 swapDesc.BufferDesc.RefreshRate.Denominator = 1;
292 swapDesc.SampleDesc.Count = 1;
293 swapDesc.SampleDesc.Quality = 0;
294 swapDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
295 swapDesc.BufferCount = 1;
296 swapDesc.OutputWindow = hwnd;
297 swapDesc.Windowed = TRUE;
298 swapDesc.Flags = 0;
299 swapDesc.SwapEffect = DXGI_SWAP_EFFECT_SEQUENTIAL;
301 HRESULT hr = dxgiFactory->CreateSwapChain(dxgiDevice, &swapDesc,
302 getter_AddRefs(mSwapChain));
303 if (FAILED(hr)) {
304 aError.Assign(
305 nsPrintfCString("RcANGLE(swap chain create failed %lx)", hr));
306 return false;
309 RefPtr<IDXGISwapChain1> swapChain1;
310 hr = mSwapChain->QueryInterface(
311 (IDXGISwapChain1**)getter_AddRefs(swapChain1));
312 if (SUCCEEDED(hr)) {
313 mSwapChain1 = std::move(swapChain1);
317 // We need this because we don't want DXGI to respond to Alt+Enter.
318 dxgiFactory->MakeWindowAssociation(hwnd, DXGI_MWA_NO_WINDOW_CHANGES);
320 if (!ResizeBufferIfNeeded()) {
321 aError.Assign("RcANGLE(resize buffer failed)"_ns);
322 return false;
325 return true;
328 void RenderCompositorANGLE::CreateSwapChainForDCompIfPossible(
329 IDXGIFactory2* aDXGIFactory2) {
330 if (!aDXGIFactory2 || !mDCLayerTree) {
331 return;
334 HWND hwnd = GetCompositorHwnd();
335 if (!hwnd) {
336 // When DirectComposition or DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL is used,
337 // compositor window needs to exist.
338 if (gfx::gfxVars::UseWebRenderDCompWin() ||
339 gfx::gfxVars::UseWebRenderFlipSequentialWin()) {
340 gfxCriticalNote << "Compositor window was not created";
342 return;
345 // When compositor is enabled, CompositionSurface is used for rendering.
346 // It does not support triple buffering.
347 const bool useTripleBuffering =
348 gfx::gfxVars::UseWebRenderTripleBufferingWin() && !UseCompositor();
349 RefPtr<IDXGISwapChain1> swapChain1 =
350 CreateSwapChainForDComp(useTripleBuffering);
351 if (swapChain1) {
352 mSwapChain = swapChain1;
353 mSwapChain1 = swapChain1;
354 mUseTripleBuffering = useTripleBuffering;
355 mDCLayerTree->SetDefaultSwapChain(swapChain1);
356 } else {
357 // Clear CLayerTree on falire
358 mDCLayerTree = nullptr;
362 RefPtr<IDXGISwapChain1> RenderCompositorANGLE::CreateSwapChainForDComp(
363 bool aUseTripleBuffering) {
364 HRESULT hr;
365 RefPtr<IDXGIDevice> dxgiDevice;
366 mDevice->QueryInterface((IDXGIDevice**)getter_AddRefs(dxgiDevice));
368 RefPtr<IDXGIFactory> dxgiFactory;
370 RefPtr<IDXGIAdapter> adapter;
371 dxgiDevice->GetAdapter(getter_AddRefs(adapter));
373 adapter->GetParent(
374 IID_PPV_ARGS((IDXGIFactory**)getter_AddRefs(dxgiFactory)));
377 RefPtr<IDXGIFactory2> dxgiFactory2;
378 hr = dxgiFactory->QueryInterface(
379 (IDXGIFactory2**)getter_AddRefs(dxgiFactory2));
380 if (FAILED(hr)) {
381 return nullptr;
384 RefPtr<IDXGISwapChain1> swapChain1;
385 DXGI_SWAP_CHAIN_DESC1 desc{};
386 // DXGI does not like 0x0 swapchains. Swap chain creation failed when 0x0 was
387 // set.
388 desc.Width = 1;
389 desc.Height = 1;
390 desc.Format = DXGI_FORMAT_B8G8R8A8_UNORM;
391 desc.SampleDesc.Count = 1;
392 desc.SampleDesc.Quality = 0;
393 desc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
394 if (aUseTripleBuffering) {
395 desc.BufferCount = 3;
396 } else {
397 desc.BufferCount = 2;
399 // DXGI_SCALING_NONE caused swap chain creation failure.
400 desc.Scaling = DXGI_SCALING_STRETCH;
401 desc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL;
402 const bool alpha = ShouldUseAlpha();
403 // See if we need to use transparency.
404 desc.AlphaMode =
405 alpha ? DXGI_ALPHA_MODE_PREMULTIPLIED : DXGI_ALPHA_MODE_IGNORE;
406 desc.Flags = 0;
408 hr = dxgiFactory2->CreateSwapChainForComposition(mDevice, &desc, nullptr,
409 getter_AddRefs(swapChain1));
410 if (SUCCEEDED(hr) && swapChain1) {
411 DXGI_RGBA color = {1.0f, 1.0f, 1.0f, 1.0f};
412 swapChain1->SetBackgroundColor(&color);
413 mSwapChainUsingAlpha = alpha;
414 return swapChain1;
417 return nullptr;
420 bool RenderCompositorANGLE::ShouldUseAlpha() const {
421 return mWidget->AsWindows()->TransparencyModeIs(
422 widget::TransparencyMode::Transparent);
425 bool RenderCompositorANGLE::BeginFrame() {
426 mWidget->AsWindows()->UpdateCompositorWndSizeIfNecessary();
428 if (!UseCompositor()) {
429 if (!mSwapChainUsingAlpha && ShouldUseAlpha()) {
430 if (!RecreateNonNativeCompositorSwapChain()) {
431 return false;
434 if (!ResizeBufferIfNeeded()) {
435 return false;
439 if (!MakeCurrent()) {
440 gfxCriticalNote << "Failed to make render context current, can't draw.";
441 return false;
444 if (RenderThread::Get()->SyncObjectNeeded() && mSyncObject) {
445 if (!mSyncObject->Synchronize(/* aFallible */ true)) {
446 // It's timeout or other error. Handle the device-reset here.
447 RenderThread::Get()->HandleDeviceReset(
448 gfx::DeviceResetDetectPlace::WR_SYNC_OBJRCT,
449 gfx::DeviceResetReason::UNKNOWN);
450 return false;
453 return true;
456 RenderedFrameId RenderCompositorANGLE::EndFrame(
457 const nsTArray<DeviceIntRect>& aDirtyRects) {
458 RenderedFrameId frameId = GetNextRenderFrameId();
459 InsertGraphicsCommandsFinishedWaitQuery(frameId);
461 if (!UseCompositor()) {
462 auto start = TimeStamp::Now();
463 if (auto* fxrHandler = mWidget->AsWindows()->GetFxrOutputHandler()) {
464 // There is a Firefox Reality handler for this swapchain. Update this
465 // window's contents to the VR window.
466 if (fxrHandler->TryInitialize(mSwapChain, mDevice)) {
467 fxrHandler->UpdateOutput(mCtx);
471 const UINT interval =
472 mFirstPresent ||
473 StaticPrefs::
474 gfx_webrender_dcomp_video_swap_chain_present_interval_0()
476 : 1;
477 const UINT flags = 0;
479 const LayoutDeviceIntSize& bufferSize = mBufferSize.ref();
480 if (mUsePartialPresent && mSwapChain1) {
481 // Clear full render flag.
482 mFullRender = false;
483 // If there is no diry rect, we skip SwapChain present.
484 if (!aDirtyRects.IsEmpty()) {
485 int rectsCount = 0;
486 StackArray<RECT, 1> rects(aDirtyRects.Length());
488 for (size_t i = 0; i < aDirtyRects.Length(); ++i) {
489 const DeviceIntRect& rect = aDirtyRects[i];
490 // Clip rect to bufferSize
491 int left = std::clamp(rect.min.x, 0, bufferSize.width);
492 int top = std::clamp(rect.min.y, 0, bufferSize.height);
493 int right = std::clamp(rect.max.x, 0, bufferSize.width);
494 int bottom = std::clamp(rect.max.y, 0, bufferSize.height);
496 // When rect is not empty, the rect could be passed to Present1().
497 if (left < right && top < bottom) {
498 rects[rectsCount].left = left;
499 rects[rectsCount].top = top;
500 rects[rectsCount].right = right;
501 rects[rectsCount].bottom = bottom;
502 rectsCount++;
506 if (rectsCount > 0) {
507 DXGI_PRESENT_PARAMETERS params;
508 PodZero(&params);
509 params.DirtyRectsCount = rectsCount;
510 params.pDirtyRects = rects.data();
512 HRESULT hr;
513 hr = mSwapChain1->Present1(interval, flags, &params);
514 if (FAILED(hr) && hr != DXGI_STATUS_OCCLUDED) {
515 gfxCriticalNote << "Present1 failed: " << gfx::hexa(hr);
516 mFullRender = true;
520 } else {
521 mSwapChain->Present(interval, flags);
523 auto end = TimeStamp::Now();
524 mozilla::Telemetry::Accumulate(mozilla::Telemetry::COMPOSITE_SWAP_TIME,
525 (end - start).ToMilliseconds() * 10.);
527 if (mFirstPresent && mDCLayerTree) {
528 // Wait for the GPU to finish executing its commands before
529 // committing the DirectComposition tree, or else the swapchain
530 // may flicker black when it's first presented.
531 RefPtr<IDXGIDevice2> dxgiDevice2;
532 mDevice->QueryInterface((IDXGIDevice2**)getter_AddRefs(dxgiDevice2));
533 MOZ_ASSERT(dxgiDevice2);
535 HANDLE event = ::CreateEvent(nullptr, false, false, nullptr);
536 HRESULT hr = dxgiDevice2->EnqueueSetEvent(event);
537 if (SUCCEEDED(hr)) {
538 DebugOnly<DWORD> result = ::WaitForSingleObject(event, INFINITE);
539 MOZ_ASSERT(result == WAIT_OBJECT_0);
540 } else {
541 gfxCriticalNoteOnce << "EnqueueSetEvent failed: " << gfx::hexa(hr);
543 ::CloseHandle(event);
545 mFirstPresent = false;
548 if (mDisablingNativeCompositor) {
549 // During disabling native compositor, we need to wait all gpu tasks
550 // complete. Otherwise, rendering window could cause white flash.
551 WaitForPreviousGraphicsCommandsFinishedQuery(/* aWaitAll */ true);
552 mDisablingNativeCompositor = false;
555 if (mDCLayerTree) {
556 mDCLayerTree->MaybeUpdateDebug();
557 mDCLayerTree->MaybeCommit();
560 return frameId;
563 bool RenderCompositorANGLE::WaitForGPU() {
564 // Note: this waits on the query we inserted in the previous frame,
565 // not the one we just inserted now. Example:
566 // Insert query #1
567 // Present #1
568 // (first frame, no wait)
569 // Insert query #2
570 // Present #2
571 // Wait for query #1.
572 // Insert query #3
573 // Present #3
574 // Wait for query #2.
576 // This ensures we're done reading textures before swapping buffers.
577 if (!StaticPrefs::gfx_webrender_wait_gpu_finished_disabled_AtStartup()) {
578 return WaitForPreviousGraphicsCommandsFinishedQuery();
580 return true;
583 bool RenderCompositorANGLE::ResizeBufferIfNeeded() {
584 MOZ_ASSERT(mSwapChain);
586 LayoutDeviceIntSize size = mWidget->GetClientSize();
588 // DXGI does not like 0x0 swapchains. ResizeBuffers() failed when 0x0 was set
589 // when DComp is used.
590 size.width = std::max(size.width, 1);
591 size.height = std::max(size.height, 1);
593 if (mBufferSize.isSome() && mBufferSize.ref() == size) {
594 MOZ_ASSERT(mEGLSurface);
595 return true;
598 // Release EGLSurface of back buffer before calling ResizeBuffers().
599 DestroyEGLSurface();
601 mBufferSize = Some(size);
603 if (!CreateEGLSurface()) {
604 mBufferSize.reset();
605 return false;
608 if (mUsePartialPresent) {
609 mFullRender = true;
611 return true;
614 bool RenderCompositorANGLE::CreateEGLSurface() {
615 MOZ_ASSERT(mBufferSize.isSome());
616 MOZ_ASSERT(mEGLSurface == EGL_NO_SURFACE);
618 HRESULT hr;
619 RefPtr<ID3D11Texture2D> backBuf;
621 if (mBufferSize.isNothing()) {
622 gfxCriticalNote << "Buffer size is invalid";
623 return false;
626 const LayoutDeviceIntSize& size = mBufferSize.ref();
628 // Resize swap chain
629 DXGI_SWAP_CHAIN_DESC desc;
630 hr = mSwapChain->GetDesc(&desc);
631 if (FAILED(hr)) {
632 gfxCriticalNote << "Failed to read swap chain description: "
633 << gfx::hexa(hr) << " Size : " << size;
634 return false;
636 hr = mSwapChain->ResizeBuffers(desc.BufferCount, size.width, size.height,
637 DXGI_FORMAT_B8G8R8A8_UNORM, 0);
638 if (FAILED(hr)) {
639 gfxCriticalNote << "Failed to resize swap chain buffers: " << gfx::hexa(hr)
640 << " Size : " << size;
641 return false;
644 hr = mSwapChain->GetBuffer(0, __uuidof(ID3D11Texture2D),
645 (void**)getter_AddRefs(backBuf));
646 if (hr == DXGI_ERROR_INVALID_CALL) {
647 // This happens on some GPUs/drivers when there's a TDR.
648 if (mDevice->GetDeviceRemovedReason() != S_OK) {
649 gfxCriticalError() << "GetBuffer returned invalid call: " << gfx::hexa(hr)
650 << " Size : " << size;
651 return false;
655 const EGLint pbuffer_attribs[]{LOCAL_EGL_WIDTH, size.width, LOCAL_EGL_HEIGHT,
656 size.height, LOCAL_EGL_NONE};
658 const auto buffer = reinterpret_cast<EGLClientBuffer>(backBuf.get());
660 const auto& gle = gl::GLContextEGL::Cast(mGL);
661 const auto& egl = gle->mEgl;
662 const EGLSurface surface = egl->fCreatePbufferFromClientBuffer(
663 LOCAL_EGL_D3D_TEXTURE_ANGLE, buffer, mEGLConfig, pbuffer_attribs);
665 if (!surface) {
666 EGLint err = egl->mLib->fGetError();
667 gfxCriticalError() << "Failed to create Pbuffer of back buffer error: "
668 << gfx::hexa(err) << " Size : " << size;
669 return false;
672 mEGLSurface = surface;
674 return true;
677 void RenderCompositorANGLE::DestroyEGLSurface() {
678 // Release EGLSurface of back buffer before calling ResizeBuffers().
679 if (mEGLSurface) {
680 const auto& gle = gl::GLContextEGL::Cast(gl());
681 const auto& egl = gle->mEgl;
682 gle->SetEGLSurfaceOverride(EGL_NO_SURFACE);
683 egl->fDestroySurface(mEGLSurface);
684 mEGLSurface = nullptr;
688 void RenderCompositorANGLE::Pause() {}
690 bool RenderCompositorANGLE::Resume() { return true; }
692 void RenderCompositorANGLE::Update() {
693 // Update compositor window's size if it exists.
694 // It needs to be called here, since OS might update compositor
695 // window's size at unexpected timing.
696 mWidget->AsWindows()->UpdateCompositorWndSizeIfNecessary();
699 bool RenderCompositorANGLE::MakeCurrent() {
700 gl::GLContextEGL::Cast(gl())->SetEGLSurfaceOverride(mEGLSurface);
701 return gl()->MakeCurrent();
704 LayoutDeviceIntSize RenderCompositorANGLE::GetBufferSize() {
705 if (!UseCompositor()) {
706 MOZ_ASSERT(mBufferSize.isSome());
707 if (mBufferSize.isNothing()) {
708 return LayoutDeviceIntSize();
710 return mBufferSize.ref();
711 } else {
712 auto size = mWidget->GetClientSize();
713 // This size is used for WR DEBUG_OVERLAY. Its DCTile does not like 0.
714 size.width = std::max(size.width, 1);
715 size.height = std::max(size.height, 1);
716 return size;
720 RefPtr<ID3D11Query> RenderCompositorANGLE::GetD3D11Query() {
721 RefPtr<ID3D11Query> query;
723 if (mRecycledQuery) {
724 query = mRecycledQuery.forget();
725 return query;
728 CD3D11_QUERY_DESC desc(D3D11_QUERY_EVENT);
729 HRESULT hr = mDevice->CreateQuery(&desc, getter_AddRefs(query));
730 if (FAILED(hr) || !query) {
731 gfxWarning() << "Could not create D3D11_QUERY_EVENT: " << gfx::hexa(hr);
732 return nullptr;
734 return query;
737 void RenderCompositorANGLE::InsertGraphicsCommandsFinishedWaitQuery(
738 RenderedFrameId aFrameId) {
739 RefPtr<ID3D11Query> query;
740 query = GetD3D11Query();
741 if (!query) {
742 return;
745 mCtx->End(query);
746 mCtx->Flush();
747 mWaitForPresentQueries.emplace(aFrameId, query);
750 bool RenderCompositorANGLE::WaitForPreviousGraphicsCommandsFinishedQuery(
751 bool aWaitAll) {
752 size_t waitLatency = mUseTripleBuffering ? 3 : 2;
753 if (aWaitAll) {
754 waitLatency = 1;
757 while (mWaitForPresentQueries.size() >= waitLatency) {
758 auto queryPair = mWaitForPresentQueries.front();
759 BOOL result;
760 bool ret =
761 layers::WaitForFrameGPUQuery(mDevice, mCtx, queryPair.second, &result);
763 if (!ret) {
764 mWaitForPresentQueries.pop();
765 return false;
768 // Recycle query for later use.
769 mRecycledQuery = queryPair.second;
770 mLastCompletedFrameId = queryPair.first;
771 mWaitForPresentQueries.pop();
773 return true;
776 RenderedFrameId RenderCompositorANGLE::GetLastCompletedFrameId() {
777 while (!mWaitForPresentQueries.empty()) {
778 auto queryPair = mWaitForPresentQueries.front();
779 if (mCtx->GetData(queryPair.second, nullptr, 0,
780 D3D11_ASYNC_GETDATA_DONOTFLUSH) != S_OK) {
781 break;
784 mRecycledQuery = queryPair.second;
785 mLastCompletedFrameId = queryPair.first;
786 mWaitForPresentQueries.pop();
789 nsPrintfCString marker("Pending frames %u",
790 (uint32_t)mWaitForPresentQueries.size());
791 PROFILER_MARKER_TEXT("GetLastCompletedFrameId", GRAPHICS, {}, marker);
793 return mLastCompletedFrameId;
796 RenderedFrameId RenderCompositorANGLE::UpdateFrameId() {
797 RenderedFrameId frameId = GetNextRenderFrameId();
798 InsertGraphicsCommandsFinishedWaitQuery(frameId);
799 return frameId;
802 gfx::DeviceResetReason RenderCompositorANGLE::IsContextLost(bool aForce) {
803 // glGetGraphicsResetStatus does not always work to detect timeout detection
804 // and recovery (TDR). On Windows, ANGLE itself is just relying upon the same
805 // API, so we should not need to check it separately.
806 auto reason = mDevice->GetDeviceRemovedReason();
807 return layers::DXGIErrorToDeviceResetReason(reason);
810 bool RenderCompositorANGLE::UseCompositor() const {
811 return mUseNativeCompositor && mDCLayerTree &&
812 gfx::gfxVars::UseWebRenderCompositor();
815 bool RenderCompositorANGLE::SupportAsyncScreenshot() {
816 return !UseCompositor() && !mDisablingNativeCompositor;
819 bool RenderCompositorANGLE::ShouldUseNativeCompositor() {
820 return UseCompositor();
823 void RenderCompositorANGLE::CompositorBeginFrame() {
824 mDCLayerTree->CompositorBeginFrame();
827 void RenderCompositorANGLE::CompositorEndFrame() {
828 mDCLayerTree->CompositorEndFrame();
831 void RenderCompositorANGLE::Bind(wr::NativeTileId aId,
832 wr::DeviceIntPoint* aOffset, uint32_t* aFboId,
833 wr::DeviceIntRect aDirtyRect,
834 wr::DeviceIntRect aValidRect) {
835 mDCLayerTree->Bind(aId, aOffset, aFboId, aDirtyRect, aValidRect);
838 void RenderCompositorANGLE::Unbind() { mDCLayerTree->Unbind(); }
840 void RenderCompositorANGLE::BindSwapChain(wr::NativeSurfaceId aId) {
841 mDCLayerTree->BindSwapChain(aId);
843 void RenderCompositorANGLE::PresentSwapChain(wr::NativeSurfaceId aId) {
844 mDCLayerTree->PresentSwapChain(aId);
847 void RenderCompositorANGLE::CreateSurface(wr::NativeSurfaceId aId,
848 wr::DeviceIntPoint aVirtualOffset,
849 wr::DeviceIntSize aTileSize,
850 bool aIsOpaque) {
851 mDCLayerTree->CreateSurface(aId, aVirtualOffset, aTileSize, aIsOpaque);
854 void RenderCompositorANGLE::CreateSwapChainSurface(wr::NativeSurfaceId aId,
855 wr::DeviceIntSize aSize,
856 bool aIsOpaque) {
857 mDCLayerTree->CreateSwapChainSurface(aId, aSize, aIsOpaque);
860 void RenderCompositorANGLE::ResizeSwapChainSurface(wr::NativeSurfaceId aId,
861 wr::DeviceIntSize aSize) {
862 mDCLayerTree->ResizeSwapChainSurface(aId, aSize);
865 void RenderCompositorANGLE::CreateExternalSurface(wr::NativeSurfaceId aId,
866 bool aIsOpaque) {
867 mDCLayerTree->CreateExternalSurface(aId, aIsOpaque);
870 void RenderCompositorANGLE::DestroySurface(NativeSurfaceId aId) {
871 mDCLayerTree->DestroySurface(aId);
874 void RenderCompositorANGLE::CreateTile(wr::NativeSurfaceId aId, int aX,
875 int aY) {
876 mDCLayerTree->CreateTile(aId, aX, aY);
879 void RenderCompositorANGLE::DestroyTile(wr::NativeSurfaceId aId, int aX,
880 int aY) {
881 mDCLayerTree->DestroyTile(aId, aX, aY);
884 void RenderCompositorANGLE::AttachExternalImage(
885 wr::NativeSurfaceId aId, wr::ExternalImageId aExternalImage) {
886 mDCLayerTree->AttachExternalImage(aId, aExternalImage);
889 void RenderCompositorANGLE::AddSurface(
890 wr::NativeSurfaceId aId, const wr::CompositorSurfaceTransform& aTransform,
891 wr::DeviceIntRect aClipRect, wr::ImageRendering aImageRendering) {
892 mDCLayerTree->AddSurface(aId, aTransform, aClipRect, aImageRendering);
895 void RenderCompositorANGLE::GetCompositorCapabilities(
896 CompositorCapabilities* aCaps) {
897 RenderCompositor::GetCompositorCapabilities(aCaps);
899 if (StaticPrefs::gfx_webrender_dcomp_use_virtual_surfaces_AtStartup()) {
900 aCaps->virtual_surface_size = VIRTUAL_SURFACE_SIZE;
901 } else {
902 aCaps->virtual_surface_size = 0;
904 // DComp video overlay does not support negative scaling. See Bug 1831820
905 aCaps->supports_external_compositor_surface_negative_scaling = false;
908 void RenderCompositorANGLE::EnableNativeCompositor(bool aEnable) {
909 // XXX Re-enable native compositor is not handled yet.
910 MOZ_RELEASE_ASSERT(!mDisablingNativeCompositor);
911 MOZ_RELEASE_ASSERT(!aEnable);
912 LOG("RenderCompositorANGLE::EnableNativeCompositor() aEnable %d", aEnable);
914 if (!UseCompositor()) {
915 return;
918 mUseNativeCompositor = false;
919 mDCLayerTree->DisableNativeCompositor();
921 if (!RecreateNonNativeCompositorSwapChain()) {
922 gfxCriticalNote << "Failed to re-create SwapChain";
923 RenderThread::Get()->HandleWebRenderError(WebRenderError::NEW_SURFACE);
924 return;
927 mDisablingNativeCompositor = true;
930 bool RenderCompositorANGLE::RecreateNonNativeCompositorSwapChain() {
931 DestroyEGLSurface();
932 mBufferSize.reset();
934 RefPtr<IDXGISwapChain1> swapChain1 =
935 CreateSwapChainForDComp(mUseTripleBuffering);
936 if (!swapChain1) {
937 return false;
939 mSwapChain = swapChain1;
940 mSwapChain1 = swapChain1;
941 if (mDCLayerTree) {
942 mDCLayerTree->SetDefaultSwapChain(swapChain1);
944 return ResizeBufferIfNeeded();
947 void RenderCompositorANGLE::InitializeUsePartialPresent() {
948 // Even when mSwapChain1 is null, we could enable WR partial present, since
949 // when mSwapChain1 is null, SwapChain is blit model swap chain with one
950 // buffer.
951 mUsePartialPresent = !UseCompositor() &&
952 !mWidget->AsWindows()->HasFxrOutputHandler() &&
953 gfx::gfxVars::WebRenderMaxPartialPresentRects() > 0;
956 bool RenderCompositorANGLE::UsePartialPresent() { return mUsePartialPresent; }
958 bool RenderCompositorANGLE::RequestFullRender() { return mFullRender; }
960 uint32_t RenderCompositorANGLE::GetMaxPartialPresentRects() {
961 if (!mUsePartialPresent) {
962 return 0;
964 return gfx::gfxVars::WebRenderMaxPartialPresentRects();
967 bool RenderCompositorANGLE::MaybeReadback(
968 const gfx::IntSize& aReadbackSize, const wr::ImageFormat& aReadbackFormat,
969 const Range<uint8_t>& aReadbackBuffer, bool* aNeedsYFlip) {
970 MOZ_ASSERT(aReadbackFormat == wr::ImageFormat::BGRA8);
972 if (!UseCompositor()) {
973 return false;
976 auto start = TimeStamp::Now();
978 HDC nulldc = ::GetDC(NULL);
979 HDC dc = ::CreateCompatibleDC(nulldc);
980 ::ReleaseDC(nullptr, nulldc);
981 if (!dc) {
982 gfxCriticalError() << "CreateCompatibleDC failed";
983 return false;
986 BITMAPV4HEADER header;
987 memset(&header, 0, sizeof(BITMAPV4HEADER));
988 header.bV4Size = sizeof(BITMAPV4HEADER);
989 header.bV4Width = aReadbackSize.width;
990 header.bV4Height = -LONG(aReadbackSize.height); // top-to-buttom DIB
991 header.bV4Planes = 1;
992 header.bV4BitCount = 32;
993 header.bV4V4Compression = BI_BITFIELDS;
994 header.bV4RedMask = 0x00FF0000;
995 header.bV4GreenMask = 0x0000FF00;
996 header.bV4BlueMask = 0x000000FF;
997 header.bV4AlphaMask = 0xFF000000;
999 void* readbackBits = nullptr;
1000 HBITMAP bitmap =
1001 ::CreateDIBSection(dc, reinterpret_cast<BITMAPINFO*>(&header),
1002 DIB_RGB_COLORS, &readbackBits, nullptr, 0);
1003 if (!bitmap) {
1004 ::DeleteDC(dc);
1005 gfxCriticalError() << "CreateDIBSection failed";
1006 return false;
1009 ::SelectObject(dc, bitmap);
1011 UINT flags = PW_CLIENTONLY | PW_RENDERFULLCONTENT;
1012 HWND hwnd = mWidget->AsWindows()->GetHwnd();
1014 mDCLayerTree->WaitForCommitCompletion();
1016 BOOL result = ::PrintWindow(hwnd, dc, flags);
1017 if (!result) {
1018 ::DeleteObject(bitmap);
1019 ::DeleteDC(dc);
1020 gfxCriticalError() << "PrintWindow failed";
1021 return false;
1024 ::GdiFlush();
1026 memcpy(&aReadbackBuffer[0], readbackBits, aReadbackBuffer.length());
1028 ::DeleteObject(bitmap);
1029 ::DeleteDC(dc);
1031 uint32_t latencyMs = round((TimeStamp::Now() - start).ToMilliseconds());
1032 if (latencyMs > 500) {
1033 gfxCriticalNote << "Readback took too long: " << latencyMs << " ms";
1036 if (aNeedsYFlip) {
1037 *aNeedsYFlip = false;
1040 return true;
1043 } // namespace mozilla::wr