Bug 1842773 - Part 11: Make DataView byteOffset and byteLength accessors aware of...
[gecko.git] / gfx / webrender_bindings / RenderCompositorANGLE.cpp
blobe01ac6a187d67a9e6a0aaf80b61d3c20d78efb31
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 #include <d3d11.h>
30 #include <dcomp.h>
31 #include <dxgi1_2.h>
33 // Flag for PrintWindow() that is defined in Winuser.h. It is defined since
34 // Windows 8.1. This allows PrintWindow to capture window content that is
35 // rendered with DirectComposition.
36 #undef PW_RENDERFULLCONTENT
37 #define PW_RENDERFULLCONTENT 0x00000002
39 namespace mozilla {
40 namespace 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 if (!mSwapChain && dxgiFactory2) {
230 RefPtr<IDXGISwapChain1> swapChain1;
231 bool useTripleBuffering = false;
233 DXGI_SWAP_CHAIN_DESC1 desc{};
234 desc.Width = 0;
235 desc.Height = 0;
236 desc.Format = DXGI_FORMAT_B8G8R8A8_UNORM;
237 desc.SampleDesc.Count = 1;
238 desc.SampleDesc.Quality = 0;
239 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.Flags = 0;
263 hr = dxgiFactory2->CreateSwapChainForHwnd(
264 mDevice, hwnd, &desc, nullptr, nullptr, getter_AddRefs(swapChain1));
265 if (SUCCEEDED(hr) && swapChain1) {
266 DXGI_RGBA color = {1.0f, 1.0f, 1.0f, 1.0f};
267 swapChain1->SetBackgroundColor(&color);
268 mSwapChain = swapChain1;
269 mSwapChain1 = swapChain1;
270 mUseTripleBuffering = useTripleBuffering;
271 } else if (useFlipSequential) {
272 gfxCriticalNoteOnce << "FLIP_SEQUENTIAL is not supported. Fallback";
276 if (!mSwapChain) {
277 if (mWidget->AsWindows()->GetCompositorHwnd()) {
278 // Destroy compositor window.
279 mWidget->AsWindows()->DestroyCompositorWindow();
280 hwnd = mWidget->AsWindows()->GetHwnd();
283 DXGI_SWAP_CHAIN_DESC swapDesc{};
284 swapDesc.BufferDesc.Width = 0;
285 swapDesc.BufferDesc.Height = 0;
286 swapDesc.BufferDesc.Format = DXGI_FORMAT_B8G8R8A8_UNORM;
287 swapDesc.BufferDesc.RefreshRate.Numerator = 60;
288 swapDesc.BufferDesc.RefreshRate.Denominator = 1;
289 swapDesc.SampleDesc.Count = 1;
290 swapDesc.SampleDesc.Quality = 0;
291 swapDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
292 swapDesc.BufferCount = 1;
293 swapDesc.OutputWindow = hwnd;
294 swapDesc.Windowed = TRUE;
295 swapDesc.Flags = 0;
296 swapDesc.SwapEffect = DXGI_SWAP_EFFECT_SEQUENTIAL;
298 HRESULT hr = dxgiFactory->CreateSwapChain(dxgiDevice, &swapDesc,
299 getter_AddRefs(mSwapChain));
300 if (FAILED(hr)) {
301 aError.Assign(
302 nsPrintfCString("RcANGLE(swap chain create failed %lx)", hr));
303 return false;
306 RefPtr<IDXGISwapChain1> swapChain1;
307 hr = mSwapChain->QueryInterface(
308 (IDXGISwapChain1**)getter_AddRefs(swapChain1));
309 if (SUCCEEDED(hr)) {
310 mSwapChain1 = swapChain1;
314 // We need this because we don't want DXGI to respond to Alt+Enter.
315 dxgiFactory->MakeWindowAssociation(hwnd, DXGI_MWA_NO_WINDOW_CHANGES);
317 if (!ResizeBufferIfNeeded()) {
318 aError.Assign("RcANGLE(resize buffer failed)"_ns);
319 return false;
322 return true;
325 void RenderCompositorANGLE::CreateSwapChainForDCompIfPossible(
326 IDXGIFactory2* aDXGIFactory2) {
327 if (!aDXGIFactory2 || !mDCLayerTree) {
328 return;
331 HWND hwnd = GetCompositorHwnd();
332 if (!hwnd) {
333 // When DirectComposition or DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL is used,
334 // compositor window needs to exist.
335 if (gfx::gfxVars::UseWebRenderDCompWin() ||
336 gfx::gfxVars::UseWebRenderFlipSequentialWin()) {
337 gfxCriticalNote << "Compositor window was not created";
339 return;
342 // When compositor is enabled, CompositionSurface is used for rendering.
343 // It does not support triple buffering.
344 bool useTripleBuffering =
345 gfx::gfxVars::UseWebRenderTripleBufferingWin() && !UseCompositor();
346 RefPtr<IDXGISwapChain1> swapChain1 =
347 CreateSwapChainForDComp(useTripleBuffering);
348 if (swapChain1) {
349 mSwapChain = swapChain1;
350 mSwapChain1 = swapChain1;
351 mUseTripleBuffering = useTripleBuffering;
352 mDCLayerTree->SetDefaultSwapChain(swapChain1);
353 } else {
354 // Clear CLayerTree on falire
355 mDCLayerTree = nullptr;
359 RefPtr<IDXGISwapChain1> RenderCompositorANGLE::CreateSwapChainForDComp(
360 bool aUseTripleBuffering) {
361 HRESULT hr;
362 RefPtr<IDXGIDevice> dxgiDevice;
363 mDevice->QueryInterface((IDXGIDevice**)getter_AddRefs(dxgiDevice));
365 RefPtr<IDXGIFactory> dxgiFactory;
367 RefPtr<IDXGIAdapter> adapter;
368 dxgiDevice->GetAdapter(getter_AddRefs(adapter));
370 adapter->GetParent(
371 IID_PPV_ARGS((IDXGIFactory**)getter_AddRefs(dxgiFactory)));
374 RefPtr<IDXGIFactory2> dxgiFactory2;
375 hr = dxgiFactory->QueryInterface(
376 (IDXGIFactory2**)getter_AddRefs(dxgiFactory2));
377 if (FAILED(hr)) {
378 return nullptr;
381 RefPtr<IDXGISwapChain1> swapChain1;
382 DXGI_SWAP_CHAIN_DESC1 desc{};
383 // DXGI does not like 0x0 swapchains. Swap chain creation failed when 0x0 was
384 // set.
385 desc.Width = 1;
386 desc.Height = 1;
387 desc.Format = DXGI_FORMAT_B8G8R8A8_UNORM;
388 desc.SampleDesc.Count = 1;
389 desc.SampleDesc.Quality = 0;
390 desc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
391 if (aUseTripleBuffering) {
392 desc.BufferCount = 3;
393 } else {
394 desc.BufferCount = 2;
396 // DXGI_SCALING_NONE caused swap chain creation failure.
397 desc.Scaling = DXGI_SCALING_STRETCH;
398 desc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL;
399 desc.AlphaMode = DXGI_ALPHA_MODE_IGNORE;
400 desc.Flags = 0;
402 hr = dxgiFactory2->CreateSwapChainForComposition(mDevice, &desc, nullptr,
403 getter_AddRefs(swapChain1));
404 if (SUCCEEDED(hr) && swapChain1) {
405 DXGI_RGBA color = {1.0f, 1.0f, 1.0f, 1.0f};
406 swapChain1->SetBackgroundColor(&color);
407 return swapChain1;
410 return nullptr;
413 bool RenderCompositorANGLE::BeginFrame() {
414 mWidget->AsWindows()->UpdateCompositorWndSizeIfNecessary();
416 if (!UseCompositor() && !ResizeBufferIfNeeded()) {
417 return false;
420 if (!MakeCurrent()) {
421 gfxCriticalNote << "Failed to make render context current, can't draw.";
422 return false;
425 if (RenderThread::Get()->SyncObjectNeeded() && mSyncObject) {
426 if (!mSyncObject->Synchronize(/* aFallible */ true)) {
427 // It's timeout or other error. Handle the device-reset here.
428 RenderThread::Get()->HandleDeviceReset(
429 "SyncObject", LOCAL_GL_UNKNOWN_CONTEXT_RESET_ARB);
430 return false;
433 return true;
436 RenderedFrameId RenderCompositorANGLE::EndFrame(
437 const nsTArray<DeviceIntRect>& aDirtyRects) {
438 RenderedFrameId frameId = GetNextRenderFrameId();
439 InsertGraphicsCommandsFinishedWaitQuery(frameId);
441 if (!UseCompositor()) {
442 auto start = TimeStamp::Now();
443 if (mWidget->AsWindows()->HasFxrOutputHandler()) {
444 // There is a Firefox Reality handler for this swapchain. Update this
445 // window's contents to the VR window.
446 FxROutputHandler* fxrHandler =
447 mWidget->AsWindows()->GetFxrOutputHandler();
448 if (fxrHandler->TryInitialize(mSwapChain, mDevice)) {
449 fxrHandler->UpdateOutput(mCtx);
453 const UINT interval =
454 mFirstPresent ||
455 StaticPrefs::
456 gfx_webrender_dcomp_video_swap_chain_present_interval_0()
458 : 1;
459 const UINT flags = 0;
461 const LayoutDeviceIntSize& bufferSize = mBufferSize.ref();
462 if (mUsePartialPresent && mSwapChain1) {
463 // Clear full render flag.
464 mFullRender = false;
465 // If there is no diry rect, we skip SwapChain present.
466 if (!aDirtyRects.IsEmpty()) {
467 int rectsCount = 0;
468 StackArray<RECT, 1> rects(aDirtyRects.Length());
470 for (size_t i = 0; i < aDirtyRects.Length(); ++i) {
471 const DeviceIntRect& rect = aDirtyRects[i];
472 // Clip rect to bufferSize
473 int left = std::max(0, std::min(rect.min.x, bufferSize.width));
474 int top = std::max(0, std::min(rect.min.y, bufferSize.height));
475 int right = std::max(0, std::min(rect.max.x, bufferSize.width));
476 int bottom = std::max(0, std::min(rect.max.y, bufferSize.height));
478 // When rect is not empty, the rect could be passed to Present1().
479 if (left < right && top < bottom) {
480 rects[rectsCount].left = left;
481 rects[rectsCount].top = top;
482 rects[rectsCount].right = right;
483 rects[rectsCount].bottom = bottom;
484 rectsCount++;
488 if (rectsCount > 0) {
489 DXGI_PRESENT_PARAMETERS params;
490 PodZero(&params);
491 params.DirtyRectsCount = rectsCount;
492 params.pDirtyRects = rects.data();
494 HRESULT hr;
495 hr = mSwapChain1->Present1(interval, flags, &params);
496 if (FAILED(hr) && hr != DXGI_STATUS_OCCLUDED) {
497 gfxCriticalNote << "Present1 failed: " << gfx::hexa(hr);
498 mFullRender = true;
502 } else {
503 mSwapChain->Present(interval, flags);
505 auto end = TimeStamp::Now();
506 mozilla::Telemetry::Accumulate(mozilla::Telemetry::COMPOSITE_SWAP_TIME,
507 (end - start).ToMilliseconds() * 10.);
509 if (mFirstPresent && mDCLayerTree) {
510 // Wait for the GPU to finish executing its commands before
511 // committing the DirectComposition tree, or else the swapchain
512 // may flicker black when it's first presented.
513 RefPtr<IDXGIDevice2> dxgiDevice2;
514 mDevice->QueryInterface((IDXGIDevice2**)getter_AddRefs(dxgiDevice2));
515 MOZ_ASSERT(dxgiDevice2);
517 HANDLE event = ::CreateEvent(nullptr, false, false, nullptr);
518 HRESULT hr = dxgiDevice2->EnqueueSetEvent(event);
519 if (SUCCEEDED(hr)) {
520 DebugOnly<DWORD> result = ::WaitForSingleObject(event, INFINITE);
521 MOZ_ASSERT(result == WAIT_OBJECT_0);
522 } else {
523 gfxCriticalNoteOnce << "EnqueueSetEvent failed: " << gfx::hexa(hr);
525 ::CloseHandle(event);
527 mFirstPresent = false;
530 if (mDisablingNativeCompositor) {
531 // During disabling native compositor, we need to wait all gpu tasks
532 // complete. Otherwise, rendering window could cause white flash.
533 WaitForPreviousGraphicsCommandsFinishedQuery(/* aWaitAll */ true);
534 mDisablingNativeCompositor = false;
537 if (mDCLayerTree) {
538 mDCLayerTree->MaybeUpdateDebug();
539 mDCLayerTree->MaybeCommit();
542 return frameId;
545 bool RenderCompositorANGLE::WaitForGPU() {
546 // Note: this waits on the query we inserted in the previous frame,
547 // not the one we just inserted now. Example:
548 // Insert query #1
549 // Present #1
550 // (first frame, no wait)
551 // Insert query #2
552 // Present #2
553 // Wait for query #1.
554 // Insert query #3
555 // Present #3
556 // Wait for query #2.
558 // This ensures we're done reading textures before swapping buffers.
559 if (!StaticPrefs::gfx_webrender_wait_gpu_finished_disabled_AtStartup()) {
560 return WaitForPreviousGraphicsCommandsFinishedQuery();
562 return true;
565 bool RenderCompositorANGLE::ResizeBufferIfNeeded() {
566 MOZ_ASSERT(mSwapChain);
568 LayoutDeviceIntSize size = mWidget->GetClientSize();
570 // DXGI does not like 0x0 swapchains. ResizeBuffers() failed when 0x0 was set
571 // when DComp is used.
572 size.width = std::max(size.width, 1);
573 size.height = std::max(size.height, 1);
575 if (mBufferSize.isSome() && mBufferSize.ref() == size) {
576 MOZ_ASSERT(mEGLSurface);
577 return true;
580 // Release EGLSurface of back buffer before calling ResizeBuffers().
581 DestroyEGLSurface();
583 mBufferSize = Some(size);
585 if (!CreateEGLSurface()) {
586 mBufferSize.reset();
587 return false;
590 if (mUsePartialPresent) {
591 mFullRender = true;
593 return true;
596 bool RenderCompositorANGLE::CreateEGLSurface() {
597 MOZ_ASSERT(mBufferSize.isSome());
598 MOZ_ASSERT(mEGLSurface == EGL_NO_SURFACE);
600 HRESULT hr;
601 RefPtr<ID3D11Texture2D> backBuf;
603 if (mBufferSize.isNothing()) {
604 gfxCriticalNote << "Buffer size is invalid";
605 return false;
608 const LayoutDeviceIntSize& size = mBufferSize.ref();
610 // Resize swap chain
611 DXGI_SWAP_CHAIN_DESC desc;
612 hr = mSwapChain->GetDesc(&desc);
613 if (FAILED(hr)) {
614 gfxCriticalNote << "Failed to read swap chain description: "
615 << gfx::hexa(hr) << " Size : " << size;
616 return false;
618 hr = mSwapChain->ResizeBuffers(desc.BufferCount, size.width, size.height,
619 DXGI_FORMAT_B8G8R8A8_UNORM, 0);
620 if (FAILED(hr)) {
621 gfxCriticalNote << "Failed to resize swap chain buffers: " << gfx::hexa(hr)
622 << " Size : " << size;
623 return false;
626 hr = mSwapChain->GetBuffer(0, __uuidof(ID3D11Texture2D),
627 (void**)getter_AddRefs(backBuf));
628 if (hr == DXGI_ERROR_INVALID_CALL) {
629 // This happens on some GPUs/drivers when there's a TDR.
630 if (mDevice->GetDeviceRemovedReason() != S_OK) {
631 gfxCriticalError() << "GetBuffer returned invalid call: " << gfx::hexa(hr)
632 << " Size : " << size;
633 return false;
637 const EGLint pbuffer_attribs[]{LOCAL_EGL_WIDTH, size.width, LOCAL_EGL_HEIGHT,
638 size.height, LOCAL_EGL_NONE};
640 const auto buffer = reinterpret_cast<EGLClientBuffer>(backBuf.get());
642 const auto& gle = gl::GLContextEGL::Cast(mGL);
643 const auto& egl = gle->mEgl;
644 const EGLSurface surface = egl->fCreatePbufferFromClientBuffer(
645 LOCAL_EGL_D3D_TEXTURE_ANGLE, buffer, mEGLConfig, pbuffer_attribs);
647 if (!surface) {
648 EGLint err = egl->mLib->fGetError();
649 gfxCriticalError() << "Failed to create Pbuffer of back buffer error: "
650 << gfx::hexa(err) << " Size : " << size;
651 return false;
654 mEGLSurface = surface;
656 return true;
659 void RenderCompositorANGLE::DestroyEGLSurface() {
660 // Release EGLSurface of back buffer before calling ResizeBuffers().
661 if (mEGLSurface) {
662 const auto& gle = gl::GLContextEGL::Cast(gl());
663 const auto& egl = gle->mEgl;
664 gle->SetEGLSurfaceOverride(EGL_NO_SURFACE);
665 egl->fDestroySurface(mEGLSurface);
666 mEGLSurface = nullptr;
670 void RenderCompositorANGLE::Pause() {}
672 bool RenderCompositorANGLE::Resume() { return true; }
674 void RenderCompositorANGLE::Update() {
675 // Update compositor window's size if it exists.
676 // It needs to be called here, since OS might update compositor
677 // window's size at unexpected timing.
678 mWidget->AsWindows()->UpdateCompositorWndSizeIfNecessary();
681 bool RenderCompositorANGLE::MakeCurrent() {
682 gl::GLContextEGL::Cast(gl())->SetEGLSurfaceOverride(mEGLSurface);
683 return gl()->MakeCurrent();
686 LayoutDeviceIntSize RenderCompositorANGLE::GetBufferSize() {
687 if (!UseCompositor()) {
688 MOZ_ASSERT(mBufferSize.isSome());
689 if (mBufferSize.isNothing()) {
690 return LayoutDeviceIntSize();
692 return mBufferSize.ref();
693 } else {
694 auto size = mWidget->GetClientSize();
695 // This size is used for WR DEBUG_OVERLAY. Its DCTile does not like 0.
696 size.width = std::max(size.width, 1);
697 size.height = std::max(size.height, 1);
698 return size;
702 RefPtr<ID3D11Query> RenderCompositorANGLE::GetD3D11Query() {
703 RefPtr<ID3D11Query> query;
705 if (mRecycledQuery) {
706 query = mRecycledQuery.forget();
707 return query;
710 CD3D11_QUERY_DESC desc(D3D11_QUERY_EVENT);
711 HRESULT hr = mDevice->CreateQuery(&desc, getter_AddRefs(query));
712 if (FAILED(hr) || !query) {
713 gfxWarning() << "Could not create D3D11_QUERY_EVENT: " << gfx::hexa(hr);
714 return nullptr;
716 return query;
719 void RenderCompositorANGLE::InsertGraphicsCommandsFinishedWaitQuery(
720 RenderedFrameId aFrameId) {
721 RefPtr<ID3D11Query> query;
722 query = GetD3D11Query();
723 if (!query) {
724 return;
727 mCtx->End(query);
728 mCtx->Flush();
729 mWaitForPresentQueries.emplace(aFrameId, query);
732 bool RenderCompositorANGLE::WaitForPreviousGraphicsCommandsFinishedQuery(
733 bool aWaitAll) {
734 size_t waitLatency = mUseTripleBuffering ? 3 : 2;
735 if (aWaitAll) {
736 waitLatency = 1;
739 while (mWaitForPresentQueries.size() >= waitLatency) {
740 auto queryPair = mWaitForPresentQueries.front();
741 BOOL result;
742 bool ret =
743 layers::WaitForFrameGPUQuery(mDevice, mCtx, queryPair.second, &result);
745 if (!ret) {
746 mWaitForPresentQueries.pop();
747 return false;
750 // Recycle query for later use.
751 mRecycledQuery = queryPair.second;
752 mLastCompletedFrameId = queryPair.first;
753 mWaitForPresentQueries.pop();
755 return true;
758 RenderedFrameId RenderCompositorANGLE::GetLastCompletedFrameId() {
759 while (!mWaitForPresentQueries.empty()) {
760 auto queryPair = mWaitForPresentQueries.front();
761 if (mCtx->GetData(queryPair.second, nullptr, 0,
762 D3D11_ASYNC_GETDATA_DONOTFLUSH) != S_OK) {
763 break;
766 mRecycledQuery = queryPair.second;
767 mLastCompletedFrameId = queryPair.first;
768 mWaitForPresentQueries.pop();
771 nsPrintfCString marker("Pending frames %u",
772 (uint32_t)mWaitForPresentQueries.size());
773 PROFILER_MARKER_TEXT("GetLastCompletedFrameId", GRAPHICS, {}, marker);
775 return mLastCompletedFrameId;
778 RenderedFrameId RenderCompositorANGLE::UpdateFrameId() {
779 RenderedFrameId frameId = GetNextRenderFrameId();
780 InsertGraphicsCommandsFinishedWaitQuery(frameId);
781 return frameId;
784 GLenum RenderCompositorANGLE::IsContextLost(bool aForce) {
785 // glGetGraphicsResetStatus does not always work to detect timeout detection
786 // and recovery (TDR). On Windows, ANGLE itself is just relying upon the same
787 // API, so we should not need to check it separately.
788 auto reason = mDevice->GetDeviceRemovedReason();
789 switch (reason) {
790 case S_OK:
791 return LOCAL_GL_NO_ERROR;
792 case DXGI_ERROR_DEVICE_REMOVED:
793 case DXGI_ERROR_DRIVER_INTERNAL_ERROR:
794 NS_WARNING("Device reset due to system / different device");
795 return LOCAL_GL_INNOCENT_CONTEXT_RESET_ARB;
796 case DXGI_ERROR_DEVICE_HUNG:
797 case DXGI_ERROR_DEVICE_RESET:
798 case DXGI_ERROR_INVALID_CALL:
799 gfxCriticalError() << "Device reset due to WR device: "
800 << gfx::hexa(reason);
801 return LOCAL_GL_GUILTY_CONTEXT_RESET_ARB;
802 default:
803 gfxCriticalError() << "Device reset with WR device unexpected reason: "
804 << gfx::hexa(reason);
805 return LOCAL_GL_UNKNOWN_CONTEXT_RESET_ARB;
809 bool RenderCompositorANGLE::UseCompositor() {
810 if (!mUseNativeCompositor) {
811 return false;
814 if (!mDCLayerTree || !gfx::gfxVars::UseWebRenderCompositor()) {
815 return false;
817 return true;
820 bool RenderCompositorANGLE::SupportAsyncScreenshot() {
821 return !UseCompositor() && !mDisablingNativeCompositor;
824 bool RenderCompositorANGLE::ShouldUseNativeCompositor() {
825 return UseCompositor();
828 void RenderCompositorANGLE::CompositorBeginFrame() {
829 mDCLayerTree->CompositorBeginFrame();
832 void RenderCompositorANGLE::CompositorEndFrame() {
833 mDCLayerTree->CompositorEndFrame();
836 void RenderCompositorANGLE::Bind(wr::NativeTileId aId,
837 wr::DeviceIntPoint* aOffset, uint32_t* aFboId,
838 wr::DeviceIntRect aDirtyRect,
839 wr::DeviceIntRect aValidRect) {
840 mDCLayerTree->Bind(aId, aOffset, aFboId, aDirtyRect, aValidRect);
843 void RenderCompositorANGLE::Unbind() { mDCLayerTree->Unbind(); }
845 void RenderCompositorANGLE::CreateSurface(wr::NativeSurfaceId aId,
846 wr::DeviceIntPoint aVirtualOffset,
847 wr::DeviceIntSize aTileSize,
848 bool aIsOpaque) {
849 mDCLayerTree->CreateSurface(aId, aVirtualOffset, aTileSize, aIsOpaque);
852 void RenderCompositorANGLE::CreateExternalSurface(wr::NativeSurfaceId aId,
853 bool aIsOpaque) {
854 mDCLayerTree->CreateExternalSurface(aId, aIsOpaque);
857 void RenderCompositorANGLE::DestroySurface(NativeSurfaceId aId) {
858 mDCLayerTree->DestroySurface(aId);
861 void RenderCompositorANGLE::CreateTile(wr::NativeSurfaceId aId, int aX,
862 int aY) {
863 mDCLayerTree->CreateTile(aId, aX, aY);
866 void RenderCompositorANGLE::DestroyTile(wr::NativeSurfaceId aId, int aX,
867 int aY) {
868 mDCLayerTree->DestroyTile(aId, aX, aY);
871 void RenderCompositorANGLE::AttachExternalImage(
872 wr::NativeSurfaceId aId, wr::ExternalImageId aExternalImage) {
873 mDCLayerTree->AttachExternalImage(aId, aExternalImage);
876 void RenderCompositorANGLE::AddSurface(
877 wr::NativeSurfaceId aId, const wr::CompositorSurfaceTransform& aTransform,
878 wr::DeviceIntRect aClipRect, wr::ImageRendering aImageRendering) {
879 mDCLayerTree->AddSurface(aId, aTransform, aClipRect, aImageRendering);
882 void RenderCompositorANGLE::GetCompositorCapabilities(
883 CompositorCapabilities* aCaps) {
884 RenderCompositor::GetCompositorCapabilities(aCaps);
886 if (StaticPrefs::gfx_webrender_dcomp_use_virtual_surfaces_AtStartup()) {
887 aCaps->virtual_surface_size = VIRTUAL_SURFACE_SIZE;
888 } else {
889 aCaps->virtual_surface_size = 0;
891 // DComp video overlay does not support negative scaling. See Bug 1831820
892 aCaps->supports_external_compositor_surface_negative_scaling = false;
895 void RenderCompositorANGLE::EnableNativeCompositor(bool aEnable) {
896 // XXX Re-enable native compositor is not handled yet.
897 MOZ_RELEASE_ASSERT(!mDisablingNativeCompositor);
898 MOZ_RELEASE_ASSERT(!aEnable);
899 LOG("RenderCompositorANGLE::EnableNativeCompositor() aEnable %d", aEnable);
901 if (!UseCompositor()) {
902 return;
905 mUseNativeCompositor = false;
906 mDCLayerTree->DisableNativeCompositor();
908 DestroyEGLSurface();
909 mBufferSize.reset();
911 RefPtr<IDXGISwapChain1> swapChain1 =
912 CreateSwapChainForDComp(mUseTripleBuffering);
913 if (swapChain1) {
914 mSwapChain = swapChain1;
915 mDCLayerTree->SetDefaultSwapChain(swapChain1);
916 ResizeBufferIfNeeded();
917 } else {
918 gfxCriticalNote << "Failed to re-create SwapChain";
919 RenderThread::Get()->HandleWebRenderError(WebRenderError::NEW_SURFACE);
920 return;
922 mDisablingNativeCompositor = true;
925 void RenderCompositorANGLE::InitializeUsePartialPresent() {
926 // Even when mSwapChain1 is null, we could enable WR partial present, since
927 // when mSwapChain1 is null, SwapChain is blit model swap chain with one
928 // buffer.
929 if (UseCompositor() || mWidget->AsWindows()->HasFxrOutputHandler() ||
930 gfx::gfxVars::WebRenderMaxPartialPresentRects() <= 0) {
931 mUsePartialPresent = false;
932 } else {
933 mUsePartialPresent = true;
937 bool RenderCompositorANGLE::UsePartialPresent() { return mUsePartialPresent; }
939 bool RenderCompositorANGLE::RequestFullRender() { return mFullRender; }
941 uint32_t RenderCompositorANGLE::GetMaxPartialPresentRects() {
942 if (!mUsePartialPresent) {
943 return 0;
945 return gfx::gfxVars::WebRenderMaxPartialPresentRects();
948 bool RenderCompositorANGLE::MaybeReadback(
949 const gfx::IntSize& aReadbackSize, const wr::ImageFormat& aReadbackFormat,
950 const Range<uint8_t>& aReadbackBuffer, bool* aNeedsYFlip) {
951 MOZ_ASSERT(aReadbackFormat == wr::ImageFormat::BGRA8);
953 if (!UseCompositor()) {
954 return false;
957 auto start = TimeStamp::Now();
959 HDC nulldc = ::GetDC(NULL);
960 HDC dc = ::CreateCompatibleDC(nulldc);
961 ::ReleaseDC(nullptr, nulldc);
962 if (!dc) {
963 gfxCriticalError() << "CreateCompatibleDC failed";
964 return false;
967 BITMAPV4HEADER header;
968 memset(&header, 0, sizeof(BITMAPV4HEADER));
969 header.bV4Size = sizeof(BITMAPV4HEADER);
970 header.bV4Width = aReadbackSize.width;
971 header.bV4Height = -LONG(aReadbackSize.height); // top-to-buttom DIB
972 header.bV4Planes = 1;
973 header.bV4BitCount = 32;
974 header.bV4V4Compression = BI_BITFIELDS;
975 header.bV4RedMask = 0x00FF0000;
976 header.bV4GreenMask = 0x0000FF00;
977 header.bV4BlueMask = 0x000000FF;
978 header.bV4AlphaMask = 0xFF000000;
980 void* readbackBits = nullptr;
981 HBITMAP bitmap =
982 ::CreateDIBSection(dc, reinterpret_cast<BITMAPINFO*>(&header),
983 DIB_RGB_COLORS, &readbackBits, nullptr, 0);
984 if (!bitmap) {
985 ::DeleteDC(dc);
986 gfxCriticalError() << "CreateDIBSection failed";
987 return false;
990 ::SelectObject(dc, bitmap);
992 UINT flags = PW_CLIENTONLY | PW_RENDERFULLCONTENT;
993 HWND hwnd = mWidget->AsWindows()->GetHwnd();
995 mDCLayerTree->WaitForCommitCompletion();
997 BOOL result = ::PrintWindow(hwnd, dc, flags);
998 if (!result) {
999 ::DeleteObject(bitmap);
1000 ::DeleteDC(dc);
1001 gfxCriticalError() << "PrintWindow failed";
1002 return false;
1005 ::GdiFlush();
1007 memcpy(&aReadbackBuffer[0], readbackBits, aReadbackBuffer.length());
1009 ::DeleteObject(bitmap);
1010 ::DeleteDC(dc);
1012 uint32_t latencyMs = round((TimeStamp::Now() - start).ToMilliseconds());
1013 if (latencyMs > 500) {
1014 gfxCriticalNote << "Readback took too long: " << latencyMs << " ms";
1017 if (aNeedsYFlip) {
1018 *aNeedsYFlip = false;
1021 return true;
1024 } // namespace wr
1025 } // namespace mozilla