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/. */
6
7 #include "RenderCompositorANGLE.h"
8
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/StaticPrefs_gfx.h"
19 #include "mozilla/webrender/DCLayerTree.h"
20 #include "mozilla/webrender/RenderThread.h"
21 #include "mozilla/widget/CompositorWidget.h"
22 #include "mozilla/widget/WinCompositorWidget.h"
23 #include "mozilla/WindowsVersion.h"
24 #include "mozilla/Telemetry.h"
25 #include "FxROutputHandler.h"
26
27 #undef NTDDI_VERSION
28 #define NTDDI_VERSION NTDDI_WIN8
29
30 #include <d3d11.h>
31 #include <dcomp.h>
32 #include <dxgi1_2.h>
33
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
39
40 namespace mozilla {
41 namespace wr {
42
43 /* static */
Create(RefPtr<widget::CompositorWidget> && aWidget)44 UniquePtr<RenderCompositor> RenderCompositorANGLE::Create(
45 RefPtr<widget::CompositorWidget>&& aWidget) {
46 const auto& gl = RenderThread::Get()->SharedGL();
47 if (!gl) {
48 gfxCriticalNote << "Failed to get shared GL context";
49 return nullptr;
50 }
51
52 UniquePtr<RenderCompositorANGLE> compositor =
53 MakeUnique<RenderCompositorANGLE>(std::move(aWidget));
54 if (!compositor->Initialize()) {
55 return nullptr;
56 }
57 return compositor;
58 }
59
RenderCompositorANGLE(RefPtr<widget::CompositorWidget> && aWidget)60 RenderCompositorANGLE::RenderCompositorANGLE(
61 RefPtr<widget::CompositorWidget>&& aWidget)
62 : RenderCompositor(std::move(aWidget)),
63 mEGLConfig(nullptr),
64 mEGLSurface(nullptr),
65 mUseTripleBuffering(false),
66 mUseAlpha(false),
67 mUseNativeCompositor(true),
68 mUsePartialPresent(false),
69 mFullRender(false),
70 mDisablingNativeCompositor(false) {}
71
~RenderCompositorANGLE()72 RenderCompositorANGLE::~RenderCompositorANGLE() {
73 DestroyEGLSurface();
74 MOZ_ASSERT(!mEGLSurface);
75 }
76
GetDeviceOfEGLDisplay()77 ID3D11Device* RenderCompositorANGLE::GetDeviceOfEGLDisplay() {
78 auto* egl = gl::GLLibraryEGL::Get();
79 MOZ_ASSERT(egl);
80 if (!egl || !egl->IsExtensionSupported(gl::GLLibraryEGL::EXT_device_query)) {
81 return nullptr;
82 }
83
84 // Fetch the D3D11 device.
85 EGLDeviceEXT eglDevice = nullptr;
86 egl->fQueryDisplayAttribEXT(egl->Display(), LOCAL_EGL_DEVICE_EXT,
87 (EGLAttrib*)&eglDevice);
88 MOZ_ASSERT(eglDevice);
89 ID3D11Device* device = nullptr;
90 egl->fQueryDeviceAttribEXT(eglDevice, LOCAL_EGL_D3D11_DEVICE_ANGLE,
91 (EGLAttrib*)&device);
92 if (!device) {
93 gfxCriticalNote << "Failed to get D3D11Device from EGLDisplay";
94 return nullptr;
95 }
96 return device;
97 }
98
SutdownEGLLibraryIfNecessary()99 bool RenderCompositorANGLE::SutdownEGLLibraryIfNecessary() {
100 const RefPtr<gl::GLLibraryEGL> egl = gl::GLLibraryEGL::Get();
101 if (!egl) {
102 // egl is not initialized yet;
103 return true;
104 }
105
106 RefPtr<ID3D11Device> device =
107 gfx::DeviceManagerDx::Get()->GetCompositorDevice();
108 // When DeviceReset is handled by GPUProcessManager/GPUParent,
109 // CompositorDevice is updated to a new device. EGLDisplay also needs to be
110 // updated, since EGLDisplay uses DeviceManagerDx::mCompositorDevice on ANGLE
111 // WebRender use case. EGLDisplay could be updated when Renderer count becomes
112 // 0. It is ensured by GPUProcessManager during handling DeviceReset.
113 // GPUChild::RecvNotifyDeviceReset() destroys all CompositorSessions before
114 // re-creating them.
115 if (device.get() != GetDeviceOfEGLDisplay() &&
116 RenderThread::Get()->RendererCount() == 0) {
117 // Shutdown GLLibraryEGL for updating EGLDisplay.
118 RenderThread::Get()->ClearSharedGL();
119 egl->Shutdown();
120 }
121 return true;
122 }
123
Initialize()124 bool RenderCompositorANGLE::Initialize() {
125 if (RenderThread::Get()->IsHandlingDeviceReset()) {
126 gfxCriticalNote << "Waiting for handling device reset";
127 return false;
128 }
129
130 // Update device if necessary.
131 if (!SutdownEGLLibraryIfNecessary()) {
132 return false;
133 }
134 const auto gl = RenderThread::Get()->SharedGL();
135 if (!gl) {
136 gfxCriticalNote << "[WR] failed to get shared GL context.";
137 return false;
138 }
139
140 // Force enable alpha channel to make sure ANGLE use correct framebuffer
141 // formart
142 const auto& gle = gl::GLContextEGL::Cast(gl);
143 const auto& egl = gle->mEgl;
144 if (!gl::CreateConfig(egl, &mEGLConfig, /* bpp */ 32,
145 /* enableDepthBuffer */ true, gl->IsGLES())) {
146 gfxCriticalNote << "Failed to create EGLConfig for WebRender";
147 }
148 MOZ_ASSERT(mEGLConfig);
149
150 mDevice = GetDeviceOfEGLDisplay();
151
152 if (!mDevice) {
153 gfxCriticalNote << "[WR] failed to get compositor device.";
154 return false;
155 }
156
157 mDevice->GetImmediateContext(getter_AddRefs(mCtx));
158 if (!mCtx) {
159 gfxCriticalNote << "[WR] failed to get immediate context.";
160 return false;
161 }
162
163 // Create DCLayerTree when DirectComposition is used.
164 if (gfx::gfxVars::UseWebRenderDCompWin()) {
165 HWND compositorHwnd = GetCompositorHwnd();
166 if (compositorHwnd) {
167 mDCLayerTree =
168 DCLayerTree::Create(gl, mEGLConfig, mDevice, compositorHwnd);
169 if (!mDCLayerTree) {
170 gfxCriticalNote << "Failed to create DCLayerTree";
171 return false;
172 }
173 } else {
174 gfxCriticalNote << "Compositor window was not created";
175 return false;
176 }
177 }
178
179 // Create SwapChain when compositor is not used
180 if (!UseCompositor()) {
181 if (!CreateSwapChain()) {
182 // SwapChain creation failed.
183 return false;
184 }
185 }
186
187 mSyncObject = layers::SyncObjectHost::CreateSyncObjectHost(mDevice);
188 if (!mSyncObject->Init()) {
189 // Some errors occur. Clear the mSyncObject here.
190 // Then, there will be no texture synchronization.
191 return false;
192 }
193
194 InitializeUsePartialPresent();
195
196 return true;
197 }
198
GetCompositorHwnd()199 HWND RenderCompositorANGLE::GetCompositorHwnd() {
200 HWND hwnd = 0;
201
202 if (XRE_IsGPUProcess()) {
203 hwnd = mWidget->AsWindows()->GetCompositorHwnd();
204 }
205 #ifdef NIGHTLY_BUILD
206 else if (
207 StaticPrefs::
208 gfx_webrender_enabled_no_gpu_process_with_angle_win_AtStartup()) {
209 MOZ_ASSERT(XRE_IsParentProcess());
210
211 // When GPU process does not exist, we do not need to use compositor window.
212 hwnd = mWidget->AsWindows()->GetHwnd();
213 }
214 #endif
215
216 return hwnd;
217 }
218
CreateSwapChain()219 bool RenderCompositorANGLE::CreateSwapChain() {
220 MOZ_ASSERT(!UseCompositor());
221
222 HWND hwnd = mWidget->AsWindows()->GetHwnd();
223
224 RefPtr<IDXGIDevice> dxgiDevice;
225 mDevice->QueryInterface((IDXGIDevice**)getter_AddRefs(dxgiDevice));
226
227 RefPtr<IDXGIFactory> dxgiFactory;
228 {
229 RefPtr<IDXGIAdapter> adapter;
230 dxgiDevice->GetAdapter(getter_AddRefs(adapter));
231
232 adapter->GetParent(
233 IID_PPV_ARGS((IDXGIFactory**)getter_AddRefs(dxgiFactory)));
234 }
235
236 RefPtr<IDXGIFactory2> dxgiFactory2;
237 HRESULT hr = dxgiFactory->QueryInterface(
238 (IDXGIFactory2**)getter_AddRefs(dxgiFactory2));
239 if (FAILED(hr)) {
240 dxgiFactory2 = nullptr;
241 }
242
243 CreateSwapChainForDCompIfPossible(dxgiFactory2);
244 if (gfx::gfxVars::UseWebRenderDCompWin() && !mSwapChain) {
245 MOZ_ASSERT(GetCompositorHwnd());
246 gfxCriticalNote << "Failed to create SwapChain for DComp";
247 return false;
248 }
249
250 if (!mSwapChain && dxgiFactory2 && IsWin8OrLater()) {
251 RefPtr<IDXGISwapChain1> swapChain1;
252 bool useTripleBuffering = false;
253
254 DXGI_SWAP_CHAIN_DESC1 desc{};
255 desc.Width = 0;
256 desc.Height = 0;
257 desc.Format = DXGI_FORMAT_B8G8R8A8_UNORM;
258 desc.SampleDesc.Count = 1;
259 desc.SampleDesc.Quality = 0;
260 desc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
261
262 if (gfx::gfxVars::UseWebRenderFlipSequentialWin()) {
263 useTripleBuffering = gfx::gfxVars::UseWebRenderTripleBufferingWin();
264 if (useTripleBuffering) {
265 desc.BufferCount = 3;
266 } else {
267 desc.BufferCount = 2;
268 }
269 desc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL;
270 } else {
271 desc.BufferCount = 1;
272 desc.SwapEffect = DXGI_SWAP_EFFECT_SEQUENTIAL;
273 }
274 desc.Scaling = DXGI_SCALING_NONE;
275 desc.Flags = 0;
276
277 hr = dxgiFactory2->CreateSwapChainForHwnd(
278 mDevice, hwnd, &desc, nullptr, nullptr, getter_AddRefs(swapChain1));
279 if (SUCCEEDED(hr) && swapChain1) {
280 DXGI_RGBA color = {1.0f, 1.0f, 1.0f, 1.0f};
281 swapChain1->SetBackgroundColor(&color);
282 mSwapChain = swapChain1;
283 mSwapChain1 = swapChain1;
284 mUseTripleBuffering = useTripleBuffering;
285 } else if (gfx::gfxVars::UseWebRenderFlipSequentialWin()) {
286 MOZ_ASSERT(GetCompositorHwnd());
287 gfxCriticalNote << "Failed to create flip mode SwapChain";
288 return false;
289 }
290 }
291
292 if (!mSwapChain) {
293 DXGI_SWAP_CHAIN_DESC swapDesc{};
294 swapDesc.BufferDesc.Width = 0;
295 swapDesc.BufferDesc.Height = 0;
296 swapDesc.BufferDesc.Format = DXGI_FORMAT_B8G8R8A8_UNORM;
297 swapDesc.BufferDesc.RefreshRate.Numerator = 60;
298 swapDesc.BufferDesc.RefreshRate.Denominator = 1;
299 swapDesc.SampleDesc.Count = 1;
300 swapDesc.SampleDesc.Quality = 0;
301 swapDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
302 swapDesc.BufferCount = 1;
303 swapDesc.OutputWindow = hwnd;
304 swapDesc.Windowed = TRUE;
305 swapDesc.Flags = 0;
306 swapDesc.SwapEffect = DXGI_SWAP_EFFECT_SEQUENTIAL;
307
308 HRESULT hr = dxgiFactory->CreateSwapChain(dxgiDevice, &swapDesc,
309 getter_AddRefs(mSwapChain));
310 if (FAILED(hr)) {
311 gfxCriticalNote << "Could not create swap chain: " << gfx::hexa(hr);
312 return false;
313 }
314
315 RefPtr<IDXGISwapChain1> swapChain1;
316 hr = mSwapChain->QueryInterface(
317 (IDXGISwapChain1**)getter_AddRefs(swapChain1));
318 if (SUCCEEDED(hr)) {
319 mSwapChain1 = swapChain1;
320 }
321 }
322
323 // We need this because we don't want DXGI to respond to Alt+Enter.
324 dxgiFactory->MakeWindowAssociation(hwnd, DXGI_MWA_NO_WINDOW_CHANGES);
325
326 if (!ResizeBufferIfNeeded()) {
327 return false;
328 }
329
330 return true;
331 }
332
CreateSwapChainForDCompIfPossible(IDXGIFactory2 * aDXGIFactory2)333 void RenderCompositorANGLE::CreateSwapChainForDCompIfPossible(
334 IDXGIFactory2* aDXGIFactory2) {
335 if (!aDXGIFactory2 || !mDCLayerTree) {
336 return;
337 }
338
339 HWND hwnd = GetCompositorHwnd();
340 if (!hwnd) {
341 // When DirectComposition or DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL is used,
342 // compositor window needs to exist.
343 if (gfx::gfxVars::UseWebRenderDCompWin() ||
344 gfx::gfxVars::UseWebRenderFlipSequentialWin()) {
345 gfxCriticalNote << "Compositor window was not created";
346 }
347 return;
348 }
349
350 // When compositor is enabled, CompositionSurface is used for rendering.
351 // It does not support triple buffering.
352 bool useTripleBuffering =
353 gfx::gfxVars::UseWebRenderTripleBufferingWin() && !UseCompositor();
354 // Non Glass window is common since Windows 10.
355 bool useAlpha = false;
356 RefPtr<IDXGISwapChain1> swapChain1 =
357 CreateSwapChainForDComp(useTripleBuffering, useAlpha);
358 if (swapChain1) {
359 mSwapChain = swapChain1;
360 mSwapChain1 = swapChain1;
361 mUseTripleBuffering = useTripleBuffering;
362 mUseAlpha = useAlpha;
363 mDCLayerTree->SetDefaultSwapChain(swapChain1);
364 } else {
365 // Clear CLayerTree on falire
366 mDCLayerTree = nullptr;
367 }
368 }
369
CreateSwapChainForDComp(bool aUseTripleBuffering,bool aUseAlpha)370 RefPtr<IDXGISwapChain1> RenderCompositorANGLE::CreateSwapChainForDComp(
371 bool aUseTripleBuffering, bool aUseAlpha) {
372 HRESULT hr;
373 RefPtr<IDXGIDevice> dxgiDevice;
374 mDevice->QueryInterface((IDXGIDevice**)getter_AddRefs(dxgiDevice));
375
376 RefPtr<IDXGIFactory> dxgiFactory;
377 {
378 RefPtr<IDXGIAdapter> adapter;
379 dxgiDevice->GetAdapter(getter_AddRefs(adapter));
380
381 adapter->GetParent(
382 IID_PPV_ARGS((IDXGIFactory**)getter_AddRefs(dxgiFactory)));
383 }
384
385 RefPtr<IDXGIFactory2> dxgiFactory2;
386 hr = dxgiFactory->QueryInterface(
387 (IDXGIFactory2**)getter_AddRefs(dxgiFactory2));
388 if (FAILED(hr)) {
389 return nullptr;
390 }
391
392 RefPtr<IDXGISwapChain1> swapChain1;
393 DXGI_SWAP_CHAIN_DESC1 desc{};
394 // DXGI does not like 0x0 swapchains. Swap chain creation failed when 0x0 was
395 // set.
396 desc.Width = 1;
397 desc.Height = 1;
398 desc.Format = DXGI_FORMAT_B8G8R8A8_UNORM;
399 desc.SampleDesc.Count = 1;
400 desc.SampleDesc.Quality = 0;
401 desc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
402 if (aUseTripleBuffering) {
403 desc.BufferCount = 3;
404 } else {
405 desc.BufferCount = 2;
406 }
407 // DXGI_SCALING_NONE caused swap chain creation failure.
408 desc.Scaling = DXGI_SCALING_STRETCH;
409 desc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL;
410 if (aUseAlpha) {
411 // This could degrade performance. Use it only when it is necessary.
412 desc.AlphaMode = DXGI_ALPHA_MODE_PREMULTIPLIED;
413 } else {
414 desc.AlphaMode = DXGI_ALPHA_MODE_IGNORE;
415 }
416 desc.Flags = 0;
417
418 hr = dxgiFactory2->CreateSwapChainForComposition(mDevice, &desc, nullptr,
419 getter_AddRefs(swapChain1));
420 if (SUCCEEDED(hr) && swapChain1) {
421 DXGI_RGBA color = {1.0f, 1.0f, 1.0f, 1.0f};
422 swapChain1->SetBackgroundColor(&color);
423 return swapChain1;
424 }
425
426 return nullptr;
427 }
428
BeginFrame()429 bool RenderCompositorANGLE::BeginFrame() {
430 mWidget->AsWindows()->UpdateCompositorWndSizeIfNecessary();
431
432 if (!UseCompositor()) {
433 if (mDCLayerTree) {
434 bool useAlpha = mWidget->AsWindows()->HasGlass();
435 // When Alpha usage is changed, SwapChain needs to be recreatd.
436 if (useAlpha != mUseAlpha) {
437 DestroyEGLSurface();
438 mBufferSize.reset();
439
440 RefPtr<IDXGISwapChain1> swapChain1 =
441 CreateSwapChainForDComp(mUseTripleBuffering, useAlpha);
442 if (swapChain1) {
443 mSwapChain = swapChain1;
444 mUseAlpha = useAlpha;
445 mDCLayerTree->SetDefaultSwapChain(swapChain1);
446 // When alpha is used, we want to disable partial present.
447 // See Bug 1595027.
448 if (useAlpha) {
449 mFullRender = true;
450 }
451 } else {
452 gfxCriticalNote << "Failed to re-create SwapChain";
453 RenderThread::Get()->HandleWebRenderError(
454 WebRenderError::NEW_SURFACE);
455 return false;
456 }
457 }
458 }
459
460 if (!ResizeBufferIfNeeded()) {
461 return false;
462 }
463 }
464
465 if (!MakeCurrent()) {
466 gfxCriticalNote << "Failed to make render context current, can't draw.";
467 return false;
468 }
469
470 if (mSyncObject) {
471 if (!mSyncObject->Synchronize(/* aFallible */ true)) {
472 // It's timeout or other error. Handle the device-reset here.
473 RenderThread::Get()->HandleDeviceReset("SyncObject", /* aNotify */ true);
474 return false;
475 }
476 }
477 return true;
478 }
479
EndFrame(const nsTArray<DeviceIntRect> & aDirtyRects)480 RenderedFrameId RenderCompositorANGLE::EndFrame(
481 const nsTArray<DeviceIntRect>& aDirtyRects) {
482 RenderedFrameId frameId = GetNextRenderFrameId();
483 InsertGraphicsCommandsFinishedWaitQuery(frameId);
484
485 if (!UseCompositor()) {
486 auto start = TimeStamp::Now();
487 if (mWidget->AsWindows()->HasFxrOutputHandler()) {
488 // There is a Firefox Reality handler for this swapchain. Update this
489 // window's contents to the VR window.
490 FxROutputHandler* fxrHandler =
491 mWidget->AsWindows()->GetFxrOutputHandler();
492 if (fxrHandler->TryInitialize(mSwapChain, mDevice)) {
493 fxrHandler->UpdateOutput(mCtx);
494 }
495 }
496
497 const LayoutDeviceIntSize& bufferSize = mBufferSize.ref();
498
499 // During high contrast mode, alpha is used. In this case,
500 // IDXGISwapChain1::Present1 shows nothing with compositor window.
501 // In this case, we want to disable partial present by full render.
502 // See Bug 1595027
503 MOZ_ASSERT_IF(mUsePartialPresent && mUseAlpha, mFullRender);
504
505 if (mUsePartialPresent && !mUseAlpha) {
506 // Clear full render flag.
507 mFullRender = false;
508 // If there is no diry rect, we skip SwapChain present.
509 if (!aDirtyRects.IsEmpty()) {
510 StackArray<RECT, 1> rects(aDirtyRects.Length());
511 for (size_t i = 0; i < aDirtyRects.Length(); ++i) {
512 const DeviceIntRect& rect = aDirtyRects[i];
513 // Clip rect to bufferSize
514 rects[i].left =
515 std::max(0, std::min(rect.origin.x, bufferSize.width));
516 rects[i].top =
517 std::max(0, std::min(rect.origin.y, bufferSize.height));
518 rects[i].right = std::max(
519 0, std::min(rect.origin.x + rect.size.width, bufferSize.width));
520 rects[i].bottom = std::max(
521 0, std::min(rect.origin.y + rect.size.height, bufferSize.height));
522 }
523
524 DXGI_PRESENT_PARAMETERS params;
525 PodZero(¶ms);
526 params.DirtyRectsCount = aDirtyRects.Length();
527 params.pDirtyRects = rects.data();
528
529 HRESULT hr;
530 hr = mSwapChain1->Present1(0, 0, ¶ms);
531 if (FAILED(hr) && hr != DXGI_STATUS_OCCLUDED) {
532 gfxCriticalNote << "Present1 failed: " << gfx::hexa(hr);
533 mFullRender = true;
534 }
535 }
536 } else {
537 mSwapChain->Present(0, 0);
538 }
539 auto end = TimeStamp::Now();
540 mozilla::Telemetry::Accumulate(mozilla::Telemetry::COMPOSITE_SWAP_TIME,
541 (end - start).ToMilliseconds() * 10.);
542 }
543
544 if (mDisablingNativeCompositor) {
545 // During disabling native compositor, we need to wait all gpu tasks
546 // complete. Otherwise, rendering window could cause white flash.
547 WaitForPreviousGraphicsCommandsFinishedQuery(/* aWaitAll */ true);
548 mDisablingNativeCompositor = false;
549 }
550
551 if (mDCLayerTree) {
552 mDCLayerTree->MaybeUpdateDebug();
553 mDCLayerTree->MaybeCommit();
554 }
555
556 return frameId;
557 }
558
WaitForGPU()559 bool RenderCompositorANGLE::WaitForGPU() {
560 // Note: this waits on the query we inserted in the previous frame,
561 // not the one we just inserted now. Example:
562 // Insert query #1
563 // Present #1
564 // (first frame, no wait)
565 // Insert query #2
566 // Present #2
567 // Wait for query #1.
568 // Insert query #3
569 // Present #3
570 // Wait for query #2.
571 //
572 // This ensures we're done reading textures before swapping buffers.
573 return WaitForPreviousGraphicsCommandsFinishedQuery();
574 }
575
ResizeBufferIfNeeded()576 bool RenderCompositorANGLE::ResizeBufferIfNeeded() {
577 MOZ_ASSERT(mSwapChain);
578
579 LayoutDeviceIntSize size = mWidget->GetClientSize();
580
581 // DXGI does not like 0x0 swapchains. ResizeBuffers() failed when 0x0 was set
582 // when DComp is used.
583 size.width = std::max(size.width, 1);
584 size.height = std::max(size.height, 1);
585
586 if (mBufferSize.isSome() && mBufferSize.ref() == size) {
587 MOZ_ASSERT(mEGLSurface);
588 return true;
589 }
590
591 // Release EGLSurface of back buffer before calling ResizeBuffers().
592 DestroyEGLSurface();
593
594 mBufferSize = Some(size);
595
596 if (!CreateEGLSurface()) {
597 mBufferSize.reset();
598 return false;
599 }
600
601 if (mUsePartialPresent) {
602 mFullRender = true;
603 }
604 return true;
605 }
606
CreateEGLSurface()607 bool RenderCompositorANGLE::CreateEGLSurface() {
608 MOZ_ASSERT(mBufferSize.isSome());
609 MOZ_ASSERT(mEGLSurface == EGL_NO_SURFACE);
610
611 HRESULT hr;
612 RefPtr<ID3D11Texture2D> backBuf;
613
614 if (mBufferSize.isNothing()) {
615 gfxCriticalNote << "Buffer size is invalid";
616 return false;
617 }
618
619 const LayoutDeviceIntSize& size = mBufferSize.ref();
620
621 // Resize swap chain
622 DXGI_SWAP_CHAIN_DESC desc;
623 hr = mSwapChain->GetDesc(&desc);
624 if (FAILED(hr)) {
625 gfxCriticalNote << "Failed to read swap chain description: "
626 << gfx::hexa(hr) << " Size : " << size;
627 return false;
628 }
629 hr = mSwapChain->ResizeBuffers(desc.BufferCount, size.width, size.height,
630 DXGI_FORMAT_B8G8R8A8_UNORM, 0);
631 if (FAILED(hr)) {
632 gfxCriticalNote << "Failed to resize swap chain buffers: " << gfx::hexa(hr)
633 << " Size : " << size;
634 return false;
635 }
636
637 hr = mSwapChain->GetBuffer(0, __uuidof(ID3D11Texture2D),
638 (void**)getter_AddRefs(backBuf));
639 if (hr == DXGI_ERROR_INVALID_CALL) {
640 // This happens on some GPUs/drivers when there's a TDR.
641 if (mDevice->GetDeviceRemovedReason() != S_OK) {
642 gfxCriticalError() << "GetBuffer returned invalid call: " << gfx::hexa(hr)
643 << " Size : " << size;
644 return false;
645 }
646 }
647
648 const EGLint pbuffer_attribs[]{
649 LOCAL_EGL_WIDTH,
650 size.width,
651 LOCAL_EGL_HEIGHT,
652 size.height,
653 LOCAL_EGL_FLEXIBLE_SURFACE_COMPATIBILITY_SUPPORTED_ANGLE,
654 LOCAL_EGL_TRUE,
655 LOCAL_EGL_NONE};
656
657 const auto buffer = reinterpret_cast<EGLClientBuffer>(backBuf.get());
658
659 const auto gl = RenderThread::Get()->SharedGL();
660 const auto& gle = gl::GLContextEGL::Cast(gl);
661 const auto& egl = gle->mEgl;
662 const EGLSurface surface = egl->fCreatePbufferFromClientBuffer(
663 egl->Display(), LOCAL_EGL_D3D_TEXTURE_ANGLE, buffer, mEGLConfig,
664 pbuffer_attribs);
665
666 EGLint err = egl->fGetError();
667 if (err != LOCAL_EGL_SUCCESS) {
668 gfxCriticalError() << "Failed to create Pbuffer of back buffer error: "
669 << gfx::hexa(err) << " Size : " << size;
670 return false;
671 }
672
673 mEGLSurface = surface;
674
675 return true;
676 }
677
DestroyEGLSurface()678 void RenderCompositorANGLE::DestroyEGLSurface() {
679 // Release EGLSurface of back buffer before calling ResizeBuffers().
680 if (mEGLSurface) {
681 const auto& gle = gl::GLContextEGL::Cast(gl());
682 const auto& egl = gle->mEgl;
683 gle->SetEGLSurfaceOverride(EGL_NO_SURFACE);
684 egl->fDestroySurface(egl->Display(), mEGLSurface);
685 mEGLSurface = nullptr;
686 }
687 }
688
Pause()689 void RenderCompositorANGLE::Pause() {}
690
Resume()691 bool RenderCompositorANGLE::Resume() { return true; }
692
Update()693 void RenderCompositorANGLE::Update() {
694 // Update compositor window's size if it exists.
695 // It needs to be called here, since OS might update compositor
696 // window's size at unexpected timing.
697 mWidget->AsWindows()->UpdateCompositorWndSizeIfNecessary();
698 }
699
MakeCurrent()700 bool RenderCompositorANGLE::MakeCurrent() {
701 gl::GLContextEGL::Cast(gl())->SetEGLSurfaceOverride(mEGLSurface);
702 return gl()->MakeCurrent();
703 }
704
GetBufferSize()705 LayoutDeviceIntSize RenderCompositorANGLE::GetBufferSize() {
706 if (!UseCompositor()) {
707 MOZ_ASSERT(mBufferSize.isSome());
708 if (mBufferSize.isNothing()) {
709 return LayoutDeviceIntSize();
710 }
711 return mBufferSize.ref();
712 } else {
713 return mWidget->GetClientSize();
714 }
715 }
716
GetD3D11Query()717 RefPtr<ID3D11Query> RenderCompositorANGLE::GetD3D11Query() {
718 RefPtr<ID3D11Query> query;
719
720 if (mRecycledQuery) {
721 query = mRecycledQuery.forget();
722 return query;
723 }
724
725 CD3D11_QUERY_DESC desc(D3D11_QUERY_EVENT);
726 HRESULT hr = mDevice->CreateQuery(&desc, getter_AddRefs(query));
727 if (FAILED(hr) || !query) {
728 gfxWarning() << "Could not create D3D11_QUERY_EVENT: " << gfx::hexa(hr);
729 return nullptr;
730 }
731 return query;
732 }
733
InsertGraphicsCommandsFinishedWaitQuery(RenderedFrameId aFrameId)734 void RenderCompositorANGLE::InsertGraphicsCommandsFinishedWaitQuery(
735 RenderedFrameId aFrameId) {
736 RefPtr<ID3D11Query> query;
737 query = GetD3D11Query();
738 if (!query) {
739 return;
740 }
741
742 mCtx->End(query);
743 mWaitForPresentQueries.emplace(aFrameId, query);
744 }
745
WaitForPreviousGraphicsCommandsFinishedQuery(bool aWaitAll)746 bool RenderCompositorANGLE::WaitForPreviousGraphicsCommandsFinishedQuery(
747 bool aWaitAll) {
748 size_t waitLatency = mUseTripleBuffering ? 3 : 2;
749 if (aWaitAll) {
750 waitLatency = 1;
751 }
752
753 while (mWaitForPresentQueries.size() >= waitLatency) {
754 auto queryPair = mWaitForPresentQueries.front();
755 BOOL result;
756 bool ret =
757 layers::WaitForFrameGPUQuery(mDevice, mCtx, queryPair.second, &result);
758
759 if (!ret) {
760 mWaitForPresentQueries.pop();
761 return false;
762 }
763
764 // Recycle query for later use.
765 mRecycledQuery = queryPair.second;
766 mLastCompletedFrameId = queryPair.first;
767 mWaitForPresentQueries.pop();
768 }
769 return true;
770 }
771
GetLastCompletedFrameId()772 RenderedFrameId RenderCompositorANGLE::GetLastCompletedFrameId() {
773 while (!mWaitForPresentQueries.empty()) {
774 auto queryPair = mWaitForPresentQueries.front();
775 if (mCtx->GetData(queryPair.second, nullptr, 0, 0) != S_OK) {
776 break;
777 }
778
779 mRecycledQuery = queryPair.second;
780 mLastCompletedFrameId = queryPair.first;
781 mWaitForPresentQueries.pop();
782 }
783
784 return mLastCompletedFrameId;
785 }
786
UpdateFrameId()787 RenderedFrameId RenderCompositorANGLE::UpdateFrameId() {
788 RenderedFrameId frameId = GetNextRenderFrameId();
789 InsertGraphicsCommandsFinishedWaitQuery(frameId);
790 return frameId;
791 }
792
IsContextLost()793 bool RenderCompositorANGLE::IsContextLost() {
794 // XXX glGetGraphicsResetStatus sometimes did not work for detecting TDR.
795 // Then this function just uses GetDeviceRemovedReason().
796 if (mDevice->GetDeviceRemovedReason() != S_OK) {
797 return true;
798 }
799 return false;
800 }
801
UseCompositor()802 bool RenderCompositorANGLE::UseCompositor() {
803 if (!mUseNativeCompositor) {
804 return false;
805 }
806
807 if (!mDCLayerTree || !gfx::gfxVars::UseWebRenderCompositor()) {
808 return false;
809 }
810 return true;
811 }
812
SupportAsyncScreenshot()813 bool RenderCompositorANGLE::SupportAsyncScreenshot() {
814 return !UseCompositor() && !mDisablingNativeCompositor;
815 }
816
ShouldUseNativeCompositor()817 bool RenderCompositorANGLE::ShouldUseNativeCompositor() {
818 return UseCompositor();
819 }
820
GetMaxUpdateRects()821 uint32_t RenderCompositorANGLE::GetMaxUpdateRects() {
822 if (UseCompositor() &&
823 StaticPrefs::gfx_webrender_compositor_max_update_rects_AtStartup() > 0) {
824 return 1;
825 }
826 return 0;
827 }
828
CompositorBeginFrame()829 void RenderCompositorANGLE::CompositorBeginFrame() {
830 mDCLayerTree->CompositorBeginFrame();
831 }
832
CompositorEndFrame()833 void RenderCompositorANGLE::CompositorEndFrame() {
834 mDCLayerTree->CompositorEndFrame();
835 }
836
Bind(wr::NativeTileId aId,wr::DeviceIntPoint * aOffset,uint32_t * aFboId,wr::DeviceIntRect aDirtyRect,wr::DeviceIntRect aValidRect)837 void RenderCompositorANGLE::Bind(wr::NativeTileId aId,
838 wr::DeviceIntPoint* aOffset, uint32_t* aFboId,
839 wr::DeviceIntRect aDirtyRect,
840 wr::DeviceIntRect aValidRect) {
841 mDCLayerTree->Bind(aId, aOffset, aFboId, aDirtyRect, aValidRect);
842 }
843
Unbind()844 void RenderCompositorANGLE::Unbind() { mDCLayerTree->Unbind(); }
845
CreateSurface(wr::NativeSurfaceId aId,wr::DeviceIntPoint aVirtualOffset,wr::DeviceIntSize aTileSize,bool aIsOpaque)846 void RenderCompositorANGLE::CreateSurface(wr::NativeSurfaceId aId,
847 wr::DeviceIntPoint aVirtualOffset,
848 wr::DeviceIntSize aTileSize,
849 bool aIsOpaque) {
850 mDCLayerTree->CreateSurface(aId, aVirtualOffset, aTileSize, aIsOpaque);
851 }
852
DestroySurface(NativeSurfaceId aId)853 void RenderCompositorANGLE::DestroySurface(NativeSurfaceId aId) {
854 mDCLayerTree->DestroySurface(aId);
855 }
856
CreateTile(wr::NativeSurfaceId aId,int aX,int aY)857 void RenderCompositorANGLE::CreateTile(wr::NativeSurfaceId aId, int aX,
858 int aY) {
859 mDCLayerTree->CreateTile(aId, aX, aY);
860 }
861
DestroyTile(wr::NativeSurfaceId aId,int aX,int aY)862 void RenderCompositorANGLE::DestroyTile(wr::NativeSurfaceId aId, int aX,
863 int aY) {
864 mDCLayerTree->DestroyTile(aId, aX, aY);
865 }
866
AddSurface(wr::NativeSurfaceId aId,wr::DeviceIntPoint aPosition,wr::DeviceIntRect aClipRect)867 void RenderCompositorANGLE::AddSurface(wr::NativeSurfaceId aId,
868 wr::DeviceIntPoint aPosition,
869 wr::DeviceIntRect aClipRect) {
870 mDCLayerTree->AddSurface(aId, aPosition, aClipRect);
871 }
872
GetCompositorCapabilities()873 CompositorCapabilities RenderCompositorANGLE::GetCompositorCapabilities() {
874 CompositorCapabilities caps;
875
876 caps.virtual_surface_size = VIRTUAL_SURFACE_SIZE;
877
878 return caps;
879 }
880
EnableNativeCompositor(bool aEnable)881 void RenderCompositorANGLE::EnableNativeCompositor(bool aEnable) {
882 // XXX Re-enable native compositor is not handled yet.
883 MOZ_RELEASE_ASSERT(!mDisablingNativeCompositor);
884 MOZ_RELEASE_ASSERT(!aEnable);
885
886 if (!UseCompositor()) {
887 return;
888 }
889
890 mUseNativeCompositor = false;
891 mDCLayerTree->DisableNativeCompositor();
892
893 bool useAlpha = mWidget->AsWindows()->HasGlass();
894 DestroyEGLSurface();
895 mBufferSize.reset();
896
897 RefPtr<IDXGISwapChain1> swapChain1 =
898 CreateSwapChainForDComp(mUseTripleBuffering, useAlpha);
899 if (swapChain1) {
900 mSwapChain = swapChain1;
901 mUseAlpha = useAlpha;
902 mDCLayerTree->SetDefaultSwapChain(swapChain1);
903 // When alpha is used, we want to disable partial present.
904 // See Bug 1595027.
905 if (useAlpha) {
906 mFullRender = true;
907 }
908 ResizeBufferIfNeeded();
909 } else {
910 gfxCriticalNote << "Failed to re-create SwapChain";
911 RenderThread::Get()->HandleWebRenderError(WebRenderError::NEW_SURFACE);
912 return;
913 }
914 mDisablingNativeCompositor = true;
915 }
916
InitializeUsePartialPresent()917 void RenderCompositorANGLE::InitializeUsePartialPresent() {
918 // Even when mSwapChain1 is null, we could enable WR partial present, since
919 // when mSwapChain1 is null, SwapChain is blit model swap chain with one
920 // buffer.
921 if (UseCompositor() || mWidget->AsWindows()->HasFxrOutputHandler() ||
922 gfx::gfxVars::WebRenderMaxPartialPresentRects() <= 0) {
923 mUsePartialPresent = false;
924 } else {
925 mUsePartialPresent = true;
926 }
927 }
928
UsePartialPresent()929 bool RenderCompositorANGLE::UsePartialPresent() { return mUsePartialPresent; }
930
RequestFullRender()931 bool RenderCompositorANGLE::RequestFullRender() { return mFullRender; }
932
GetMaxPartialPresentRects()933 uint32_t RenderCompositorANGLE::GetMaxPartialPresentRects() {
934 if (!mUsePartialPresent) {
935 return 0;
936 }
937 return gfx::gfxVars::WebRenderMaxPartialPresentRects();
938 }
939
MaybeReadback(const gfx::IntSize & aReadbackSize,const wr::ImageFormat & aReadbackFormat,const Range<uint8_t> & aReadbackBuffer)940 bool RenderCompositorANGLE::MaybeReadback(
941 const gfx::IntSize& aReadbackSize, const wr::ImageFormat& aReadbackFormat,
942 const Range<uint8_t>& aReadbackBuffer) {
943 MOZ_ASSERT(aReadbackFormat == wr::ImageFormat::BGRA8);
944
945 if (!UseCompositor()) {
946 return false;
947 }
948
949 auto start = TimeStamp::Now();
950
951 HDC nulldc = ::GetDC(NULL);
952 HDC dc = ::CreateCompatibleDC(nulldc);
953 ::ReleaseDC(nullptr, nulldc);
954 if (!dc) {
955 gfxCriticalError() << "CreateCompatibleDC failed";
956 return false;
957 }
958
959 BITMAPV4HEADER header;
960 memset(&header, 0, sizeof(BITMAPV4HEADER));
961 header.bV4Size = sizeof(BITMAPV4HEADER);
962 header.bV4Width = aReadbackSize.width;
963 header.bV4Height = -LONG(aReadbackSize.height); // top-to-buttom DIB
964 header.bV4Planes = 1;
965 header.bV4BitCount = 32;
966 header.bV4V4Compression = BI_BITFIELDS;
967 header.bV4RedMask = 0x00FF0000;
968 header.bV4GreenMask = 0x0000FF00;
969 header.bV4BlueMask = 0x000000FF;
970 header.bV4AlphaMask = 0xFF000000;
971
972 void* readbackBits = nullptr;
973 HBITMAP bitmap =
974 ::CreateDIBSection(dc, reinterpret_cast<BITMAPINFO*>(&header),
975 DIB_RGB_COLORS, &readbackBits, nullptr, 0);
976 if (!bitmap) {
977 ::DeleteDC(dc);
978 gfxCriticalError() << "CreateDIBSection failed";
979 return false;
980 }
981
982 ::SelectObject(dc, bitmap);
983
984 UINT flags = PW_CLIENTONLY | PW_RENDERFULLCONTENT;
985 HWND hwnd = mWidget->AsWindows()->GetHwnd();
986
987 mDCLayerTree->WaitForCommitCompletion();
988
989 BOOL result = ::PrintWindow(hwnd, dc, flags);
990 if (!result) {
991 ::DeleteObject(bitmap);
992 ::DeleteDC(dc);
993 gfxCriticalError() << "PrintWindow failed";
994 return false;
995 }
996
997 ::GdiFlush();
998
999 memcpy(&aReadbackBuffer[0], readbackBits, aReadbackBuffer.length());
1000
1001 ::DeleteObject(bitmap);
1002 ::DeleteDC(dc);
1003
1004 uint32_t latencyMs = round((TimeStamp::Now() - start).ToMilliseconds());
1005 if (latencyMs > 500) {
1006 gfxCriticalNote << "Readback took too long: " << latencyMs << " ms";
1007 }
1008
1009 return true;
1010 }
1011
1012 } // namespace wr
1013 } // namespace mozilla
1014