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(&params);
526         params.DirtyRectsCount = aDirtyRects.Length();
527         params.pDirtyRects = rects.data();
528 
529         HRESULT hr;
530         hr = mSwapChain1->Present1(0, 0, &params);
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