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"
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"
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
42 extern LazyLogModule gRenderThreadLog
;
43 #define LOG(...) MOZ_LOG(gRenderThreadLog, LogLevel::Debug, (__VA_ARGS__))
46 UniquePtr
<RenderCompositor
> RenderCompositorANGLE::Create(
47 const RefPtr
<widget::CompositorWidget
>& aWidget
, nsACString
& aError
) {
48 RefPtr
<gl::GLContext
> gl
= RenderThread::Get()->SingletonGL(aError
);
50 if (aError
.IsEmpty()) {
51 aError
.Assign("RcANGLE(no shared GL)"_ns
);
53 aError
.Append("(Create)"_ns
);
58 UniquePtr
<RenderCompositorANGLE
> compositor
=
59 MakeUnique
<RenderCompositorANGLE
>(aWidget
, std::move(gl
));
60 if (!compositor
->Initialize(aError
)) {
66 RenderCompositorANGLE::RenderCompositorANGLE(
67 const RefPtr
<widget::CompositorWidget
>& aWidget
,
68 RefPtr
<gl::GLContext
>&& aGL
)
69 : RenderCompositor(aWidget
), mGL(aGL
) {
71 LOG("RenderCompositorANGLE::RenderCompositorANGLE()");
74 RenderCompositorANGLE::~RenderCompositorANGLE() {
75 LOG("RenderCompositorANGLE::~RenderCompositorANGLE()");
78 MOZ_ASSERT(!mEGLSurface
);
81 ID3D11Device
* RenderCompositorANGLE::GetDeviceOfEGLDisplay(nsACString
& aError
) {
82 const auto& gle
= gl::GLContextEGL::Cast(mGL
);
83 const auto& egl
= gle
->mEgl
;
86 !egl
->mLib
->IsExtensionSupported(gl::EGLLibExtension::EXT_device_query
)) {
87 aError
.Assign("RcANGLE(no EXT_device_query support)"_ns
);
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
,
99 aError
.Assign("RcANGLE(get D3D11Device from EGLDisplay failed)"_ns
);
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
110 if (RenderThread::Get()->IsHandlingDeviceReset()) {
111 aError
.Assign("RcANGLE(waiting device reset)"_ns
);
115 // Force enable alpha channel to make sure ANGLE use correct framebuffer
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
);
124 MOZ_ASSERT(mEGLConfig
);
126 mDevice
= GetDeviceOfEGLDisplay(aError
);
132 mDevice
->GetImmediateContext(getter_AddRefs(mCtx
));
134 aError
.Assign("RcANGLE(get immediate context failed)"_ns
);
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
);
154 aError
.Assign("RcANGLE(no compositor window)"_ns
);
159 // Create SwapChain when compositor is not used
160 if (!UseCompositor()) {
161 if (!CreateSwapChain(aError
)) {
162 // SwapChain creation failed.
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
);
175 InitializeUsePartialPresent();
180 HWND
RenderCompositorANGLE::GetCompositorHwnd() {
183 if (XRE_IsGPUProcess()) {
184 hwnd
= mWidget
->AsWindows()->GetCompositorHwnd();
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();
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
));
212 IID_PPV_ARGS((IDXGIFactory
**)getter_AddRefs(dxgiFactory
)));
215 RefPtr
<IDXGIFactory2
> dxgiFactory2
;
216 HRESULT hr
= dxgiFactory
->QueryInterface(
217 (IDXGIFactory2
**)getter_AddRefs(dxgiFactory2
));
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
);
229 if (!mSwapChain
&& dxgiFactory2
) {
230 RefPtr
<IDXGISwapChain1
> swapChain1
;
231 bool useTripleBuffering
= false;
233 DXGI_SWAP_CHAIN_DESC1 desc
{};
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;
252 desc
.BufferCount
= 2;
254 desc
.SwapEffect
= DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL
;
255 desc
.Scaling
= DXGI_SCALING_NONE
;
257 desc
.BufferCount
= 1;
258 desc
.SwapEffect
= DXGI_SWAP_EFFECT_SEQUENTIAL
;
259 desc
.Scaling
= DXGI_SCALING_STRETCH
;
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";
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
;
296 swapDesc
.SwapEffect
= DXGI_SWAP_EFFECT_SEQUENTIAL
;
298 HRESULT hr
= dxgiFactory
->CreateSwapChain(dxgiDevice
, &swapDesc
,
299 getter_AddRefs(mSwapChain
));
302 nsPrintfCString("RcANGLE(swap chain create failed %lx)", hr
));
306 RefPtr
<IDXGISwapChain1
> swapChain1
;
307 hr
= mSwapChain
->QueryInterface(
308 (IDXGISwapChain1
**)getter_AddRefs(swapChain1
));
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
);
325 void RenderCompositorANGLE::CreateSwapChainForDCompIfPossible(
326 IDXGIFactory2
* aDXGIFactory2
) {
327 if (!aDXGIFactory2
|| !mDCLayerTree
) {
331 HWND hwnd
= GetCompositorHwnd();
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";
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
);
349 mSwapChain
= swapChain1
;
350 mSwapChain1
= swapChain1
;
351 mUseTripleBuffering
= useTripleBuffering
;
352 mDCLayerTree
->SetDefaultSwapChain(swapChain1
);
354 // Clear CLayerTree on falire
355 mDCLayerTree
= nullptr;
359 RefPtr
<IDXGISwapChain1
> RenderCompositorANGLE::CreateSwapChainForDComp(
360 bool aUseTripleBuffering
) {
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
));
371 IID_PPV_ARGS((IDXGIFactory
**)getter_AddRefs(dxgiFactory
)));
374 RefPtr
<IDXGIFactory2
> dxgiFactory2
;
375 hr
= dxgiFactory
->QueryInterface(
376 (IDXGIFactory2
**)getter_AddRefs(dxgiFactory2
));
381 RefPtr
<IDXGISwapChain1
> swapChain1
;
382 DXGI_SWAP_CHAIN_DESC1 desc
{};
383 // DXGI does not like 0x0 swapchains. Swap chain creation failed when 0x0 was
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;
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
;
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
);
413 bool RenderCompositorANGLE::BeginFrame() {
414 mWidget
->AsWindows()->UpdateCompositorWndSizeIfNecessary();
416 if (!UseCompositor() && !ResizeBufferIfNeeded()) {
420 if (!MakeCurrent()) {
421 gfxCriticalNote
<< "Failed to make render context current, can't draw.";
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
);
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
=
456 gfx_webrender_dcomp_video_swap_chain_present_interval_0()
459 const UINT flags
= 0;
461 const LayoutDeviceIntSize
& bufferSize
= mBufferSize
.ref();
462 if (mUsePartialPresent
&& mSwapChain1
) {
463 // Clear full render flag.
465 // If there is no diry rect, we skip SwapChain present.
466 if (!aDirtyRects
.IsEmpty()) {
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
;
488 if (rectsCount
> 0) {
489 DXGI_PRESENT_PARAMETERS params
;
491 params
.DirtyRectsCount
= rectsCount
;
492 params
.pDirtyRects
= rects
.data();
495 hr
= mSwapChain1
->Present1(interval
, flags
, ¶ms
);
496 if (FAILED(hr
) && hr
!= DXGI_STATUS_OCCLUDED
) {
497 gfxCriticalNote
<< "Present1 failed: " << gfx::hexa(hr
);
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
);
520 DebugOnly
<DWORD
> result
= ::WaitForSingleObject(event
, INFINITE
);
521 MOZ_ASSERT(result
== WAIT_OBJECT_0
);
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;
538 mDCLayerTree
->MaybeUpdateDebug();
539 mDCLayerTree
->MaybeCommit();
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:
550 // (first frame, no wait)
553 // Wait for query #1.
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();
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
);
580 // Release EGLSurface of back buffer before calling ResizeBuffers().
583 mBufferSize
= Some(size
);
585 if (!CreateEGLSurface()) {
590 if (mUsePartialPresent
) {
596 bool RenderCompositorANGLE::CreateEGLSurface() {
597 MOZ_ASSERT(mBufferSize
.isSome());
598 MOZ_ASSERT(mEGLSurface
== EGL_NO_SURFACE
);
601 RefPtr
<ID3D11Texture2D
> backBuf
;
603 if (mBufferSize
.isNothing()) {
604 gfxCriticalNote
<< "Buffer size is invalid";
608 const LayoutDeviceIntSize
& size
= mBufferSize
.ref();
611 DXGI_SWAP_CHAIN_DESC desc
;
612 hr
= mSwapChain
->GetDesc(&desc
);
614 gfxCriticalNote
<< "Failed to read swap chain description: "
615 << gfx::hexa(hr
) << " Size : " << size
;
618 hr
= mSwapChain
->ResizeBuffers(desc
.BufferCount
, size
.width
, size
.height
,
619 DXGI_FORMAT_B8G8R8A8_UNORM
, 0);
621 gfxCriticalNote
<< "Failed to resize swap chain buffers: " << gfx::hexa(hr
)
622 << " Size : " << size
;
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
;
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
);
648 EGLint err
= egl
->mLib
->fGetError();
649 gfxCriticalError() << "Failed to create Pbuffer of back buffer error: "
650 << gfx::hexa(err
) << " Size : " << size
;
654 mEGLSurface
= surface
;
659 void RenderCompositorANGLE::DestroyEGLSurface() {
660 // Release EGLSurface of back buffer before calling ResizeBuffers().
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();
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);
702 RefPtr
<ID3D11Query
> RenderCompositorANGLE::GetD3D11Query() {
703 RefPtr
<ID3D11Query
> query
;
705 if (mRecycledQuery
) {
706 query
= mRecycledQuery
.forget();
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
);
719 void RenderCompositorANGLE::InsertGraphicsCommandsFinishedWaitQuery(
720 RenderedFrameId aFrameId
) {
721 RefPtr
<ID3D11Query
> query
;
722 query
= GetD3D11Query();
729 mWaitForPresentQueries
.emplace(aFrameId
, query
);
732 bool RenderCompositorANGLE::WaitForPreviousGraphicsCommandsFinishedQuery(
734 size_t waitLatency
= mUseTripleBuffering
? 3 : 2;
739 while (mWaitForPresentQueries
.size() >= waitLatency
) {
740 auto queryPair
= mWaitForPresentQueries
.front();
743 layers::WaitForFrameGPUQuery(mDevice
, mCtx
, queryPair
.second
, &result
);
746 mWaitForPresentQueries
.pop();
750 // Recycle query for later use.
751 mRecycledQuery
= queryPair
.second
;
752 mLastCompletedFrameId
= queryPair
.first
;
753 mWaitForPresentQueries
.pop();
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
) {
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
);
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();
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
;
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
) {
814 if (!mDCLayerTree
|| !gfx::gfxVars::UseWebRenderCompositor()) {
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
,
849 mDCLayerTree
->CreateSurface(aId
, aVirtualOffset
, aTileSize
, aIsOpaque
);
852 void RenderCompositorANGLE::CreateExternalSurface(wr::NativeSurfaceId aId
,
854 mDCLayerTree
->CreateExternalSurface(aId
, aIsOpaque
);
857 void RenderCompositorANGLE::DestroySurface(NativeSurfaceId aId
) {
858 mDCLayerTree
->DestroySurface(aId
);
861 void RenderCompositorANGLE::CreateTile(wr::NativeSurfaceId aId
, int aX
,
863 mDCLayerTree
->CreateTile(aId
, aX
, aY
);
866 void RenderCompositorANGLE::DestroyTile(wr::NativeSurfaceId aId
, int aX
,
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
;
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()) {
905 mUseNativeCompositor
= false;
906 mDCLayerTree
->DisableNativeCompositor();
911 RefPtr
<IDXGISwapChain1
> swapChain1
=
912 CreateSwapChainForDComp(mUseTripleBuffering
);
914 mSwapChain
= swapChain1
;
915 mDCLayerTree
->SetDefaultSwapChain(swapChain1
);
916 ResizeBufferIfNeeded();
918 gfxCriticalNote
<< "Failed to re-create SwapChain";
919 RenderThread::Get()->HandleWebRenderError(WebRenderError::NEW_SURFACE
);
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
929 if (UseCompositor() || mWidget
->AsWindows()->HasFxrOutputHandler() ||
930 gfx::gfxVars::WebRenderMaxPartialPresentRects() <= 0) {
931 mUsePartialPresent
= false;
933 mUsePartialPresent
= true;
937 bool RenderCompositorANGLE::UsePartialPresent() { return mUsePartialPresent
; }
939 bool RenderCompositorANGLE::RequestFullRender() { return mFullRender
; }
941 uint32_t RenderCompositorANGLE::GetMaxPartialPresentRects() {
942 if (!mUsePartialPresent
) {
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()) {
957 auto start
= TimeStamp::Now();
959 HDC nulldc
= ::GetDC(NULL
);
960 HDC dc
= ::CreateCompatibleDC(nulldc
);
961 ::ReleaseDC(nullptr, nulldc
);
963 gfxCriticalError() << "CreateCompatibleDC failed";
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;
982 ::CreateDIBSection(dc
, reinterpret_cast<BITMAPINFO
*>(&header
),
983 DIB_RGB_COLORS
, &readbackBits
, nullptr, 0);
986 gfxCriticalError() << "CreateDIBSection failed";
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
);
999 ::DeleteObject(bitmap
);
1001 gfxCriticalError() << "PrintWindow failed";
1007 memcpy(&aReadbackBuffer
[0], readbackBits
, aReadbackBuffer
.length());
1009 ::DeleteObject(bitmap
);
1012 uint32_t latencyMs
= round((TimeStamp::Now() - start
).ToMilliseconds());
1013 if (latencyMs
> 500) {
1014 gfxCriticalNote
<< "Readback took too long: " << latencyMs
<< " ms";
1018 *aNeedsYFlip
= false;
1025 } // namespace mozilla