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/TextureD3D11.h"
17 #include "mozilla/layers/HelpersD3D11.h"
18 #include "mozilla/layers/SyncObject.h"
19 #include "mozilla/ProfilerMarkers.h"
20 #include "mozilla/StaticPrefs_gfx.h"
21 #include "mozilla/webrender/DCLayerTree.h"
22 #include "mozilla/webrender/RenderThread.h"
23 #include "mozilla/widget/CompositorWidget.h"
24 #include "mozilla/widget/WinCompositorWidget.h"
25 #include "mozilla/WindowsVersion.h"
26 #include "mozilla/Telemetry.h"
27 #include "nsPrintfCString.h"
28 #include "FxROutputHandler.h"
34 // Flag for PrintWindow() that is defined in Winuser.h. It is defined since
35 // Windows 8.1. This allows PrintWindow to capture window content that is
36 // rendered with DirectComposition.
37 #undef PW_RENDERFULLCONTENT
38 #define PW_RENDERFULLCONTENT 0x00000002
40 namespace mozilla::wr
{
42 extern LazyLogModule gRenderThreadLog
;
43 #define LOG(...) MOZ_LOG(gRenderThreadLog, LogLevel::Debug, (__VA_ARGS__))
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 const bool alpha
= ShouldUseAlpha();
230 if (!mSwapChain
&& dxgiFactory2
) {
231 RefPtr
<IDXGISwapChain1
> swapChain1
;
232 bool useTripleBuffering
= false;
234 DXGI_SWAP_CHAIN_DESC1 desc
{};
237 desc
.Format
= DXGI_FORMAT_B8G8R8A8_UNORM
;
238 desc
.SampleDesc
.Count
= 1;
239 desc
.SampleDesc
.Quality
= 0;
240 desc
.BufferUsage
= DXGI_USAGE_RENDER_TARGET_OUTPUT
;
241 bool useFlipSequential
= gfx::gfxVars::UseWebRenderFlipSequentialWin();
242 if (useFlipSequential
&& !mWidget
->AsWindows()->GetCompositorHwnd()) {
243 useFlipSequential
= false;
244 gfxCriticalNoteOnce
<< "FLIP_SEQUENTIAL needs CompositorHwnd. Fallback";
247 if (useFlipSequential
) {
248 useTripleBuffering
= gfx::gfxVars::UseWebRenderTripleBufferingWin();
249 if (useTripleBuffering
) {
250 desc
.BufferCount
= 3;
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
;
262 alpha
? DXGI_ALPHA_MODE_PREMULTIPLIED
: DXGI_ALPHA_MODE_IGNORE
;
265 hr
= dxgiFactory2
->CreateSwapChainForHwnd(
266 mDevice
, hwnd
, &desc
, nullptr, nullptr, getter_AddRefs(swapChain1
));
267 if (SUCCEEDED(hr
) && swapChain1
) {
268 DXGI_RGBA color
= {1.0f
, 1.0f
, 1.0f
, 1.0f
};
269 swapChain1
->SetBackgroundColor(&color
);
270 mSwapChain
= swapChain1
;
271 mSwapChain1
= swapChain1
;
272 mUseTripleBuffering
= useTripleBuffering
;
273 mSwapChainUsingAlpha
= alpha
;
274 } else if (useFlipSequential
) {
275 gfxCriticalNoteOnce
<< "FLIP_SEQUENTIAL is not supported. Fallback";
280 if (mWidget
->AsWindows()->GetCompositorHwnd()) {
281 // Destroy compositor window.
282 mWidget
->AsWindows()->DestroyCompositorWindow();
283 hwnd
= mWidget
->AsWindows()->GetHwnd();
286 DXGI_SWAP_CHAIN_DESC swapDesc
{};
287 swapDesc
.BufferDesc
.Width
= 0;
288 swapDesc
.BufferDesc
.Height
= 0;
289 swapDesc
.BufferDesc
.Format
= DXGI_FORMAT_B8G8R8A8_UNORM
;
290 swapDesc
.BufferDesc
.RefreshRate
.Numerator
= 60;
291 swapDesc
.BufferDesc
.RefreshRate
.Denominator
= 1;
292 swapDesc
.SampleDesc
.Count
= 1;
293 swapDesc
.SampleDesc
.Quality
= 0;
294 swapDesc
.BufferUsage
= DXGI_USAGE_RENDER_TARGET_OUTPUT
;
295 swapDesc
.BufferCount
= 1;
296 swapDesc
.OutputWindow
= hwnd
;
297 swapDesc
.Windowed
= TRUE
;
299 swapDesc
.SwapEffect
= DXGI_SWAP_EFFECT_SEQUENTIAL
;
301 HRESULT hr
= dxgiFactory
->CreateSwapChain(dxgiDevice
, &swapDesc
,
302 getter_AddRefs(mSwapChain
));
305 nsPrintfCString("RcANGLE(swap chain create failed %lx)", hr
));
309 RefPtr
<IDXGISwapChain1
> swapChain1
;
310 hr
= mSwapChain
->QueryInterface(
311 (IDXGISwapChain1
**)getter_AddRefs(swapChain1
));
313 mSwapChain1
= std::move(swapChain1
);
317 // We need this because we don't want DXGI to respond to Alt+Enter.
318 dxgiFactory
->MakeWindowAssociation(hwnd
, DXGI_MWA_NO_WINDOW_CHANGES
);
320 if (!ResizeBufferIfNeeded()) {
321 aError
.Assign("RcANGLE(resize buffer failed)"_ns
);
328 void RenderCompositorANGLE::CreateSwapChainForDCompIfPossible(
329 IDXGIFactory2
* aDXGIFactory2
) {
330 if (!aDXGIFactory2
|| !mDCLayerTree
) {
334 HWND hwnd
= GetCompositorHwnd();
336 // When DirectComposition or DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL is used,
337 // compositor window needs to exist.
338 if (gfx::gfxVars::UseWebRenderDCompWin() ||
339 gfx::gfxVars::UseWebRenderFlipSequentialWin()) {
340 gfxCriticalNote
<< "Compositor window was not created";
345 // When compositor is enabled, CompositionSurface is used for rendering.
346 // It does not support triple buffering.
347 const bool useTripleBuffering
=
348 gfx::gfxVars::UseWebRenderTripleBufferingWin() && !UseCompositor();
349 RefPtr
<IDXGISwapChain1
> swapChain1
=
350 CreateSwapChainForDComp(useTripleBuffering
);
352 mSwapChain
= swapChain1
;
353 mSwapChain1
= swapChain1
;
354 mUseTripleBuffering
= useTripleBuffering
;
355 mDCLayerTree
->SetDefaultSwapChain(swapChain1
);
357 // Clear CLayerTree on falire
358 mDCLayerTree
= nullptr;
362 RefPtr
<IDXGISwapChain1
> RenderCompositorANGLE::CreateSwapChainForDComp(
363 bool aUseTripleBuffering
) {
365 RefPtr
<IDXGIDevice
> dxgiDevice
;
366 mDevice
->QueryInterface((IDXGIDevice
**)getter_AddRefs(dxgiDevice
));
368 RefPtr
<IDXGIFactory
> dxgiFactory
;
370 RefPtr
<IDXGIAdapter
> adapter
;
371 dxgiDevice
->GetAdapter(getter_AddRefs(adapter
));
374 IID_PPV_ARGS((IDXGIFactory
**)getter_AddRefs(dxgiFactory
)));
377 RefPtr
<IDXGIFactory2
> dxgiFactory2
;
378 hr
= dxgiFactory
->QueryInterface(
379 (IDXGIFactory2
**)getter_AddRefs(dxgiFactory2
));
384 RefPtr
<IDXGISwapChain1
> swapChain1
;
385 DXGI_SWAP_CHAIN_DESC1 desc
{};
386 // DXGI does not like 0x0 swapchains. Swap chain creation failed when 0x0 was
390 desc
.Format
= DXGI_FORMAT_B8G8R8A8_UNORM
;
391 desc
.SampleDesc
.Count
= 1;
392 desc
.SampleDesc
.Quality
= 0;
393 desc
.BufferUsage
= DXGI_USAGE_RENDER_TARGET_OUTPUT
;
394 if (aUseTripleBuffering
) {
395 desc
.BufferCount
= 3;
397 desc
.BufferCount
= 2;
399 // DXGI_SCALING_NONE caused swap chain creation failure.
400 desc
.Scaling
= DXGI_SCALING_STRETCH
;
401 desc
.SwapEffect
= DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL
;
402 const bool alpha
= ShouldUseAlpha();
403 // See if we need to use transparency.
405 alpha
? DXGI_ALPHA_MODE_PREMULTIPLIED
: DXGI_ALPHA_MODE_IGNORE
;
408 hr
= dxgiFactory2
->CreateSwapChainForComposition(mDevice
, &desc
, nullptr,
409 getter_AddRefs(swapChain1
));
410 if (SUCCEEDED(hr
) && swapChain1
) {
411 DXGI_RGBA color
= {1.0f
, 1.0f
, 1.0f
, 1.0f
};
412 swapChain1
->SetBackgroundColor(&color
);
413 mSwapChainUsingAlpha
= alpha
;
420 bool RenderCompositorANGLE::ShouldUseAlpha() const {
421 return mWidget
->AsWindows()->TransparencyModeIs(
422 widget::TransparencyMode::Transparent
);
425 bool RenderCompositorANGLE::BeginFrame() {
426 mWidget
->AsWindows()->UpdateCompositorWndSizeIfNecessary();
428 if (!UseCompositor()) {
429 if (!mSwapChainUsingAlpha
&& ShouldUseAlpha()) {
430 if (!RecreateNonNativeCompositorSwapChain()) {
434 if (!ResizeBufferIfNeeded()) {
439 if (!MakeCurrent()) {
440 gfxCriticalNote
<< "Failed to make render context current, can't draw.";
444 if (RenderThread::Get()->SyncObjectNeeded() && mSyncObject
) {
445 if (!mSyncObject
->Synchronize(/* aFallible */ true)) {
446 // It's timeout or other error. Handle the device-reset here.
447 RenderThread::Get()->HandleDeviceReset(
448 gfx::DeviceResetDetectPlace::WR_SYNC_OBJRCT
,
449 gfx::DeviceResetReason::UNKNOWN
);
456 RenderedFrameId
RenderCompositorANGLE::EndFrame(
457 const nsTArray
<DeviceIntRect
>& aDirtyRects
) {
458 RenderedFrameId frameId
= GetNextRenderFrameId();
459 InsertGraphicsCommandsFinishedWaitQuery(frameId
);
461 if (!UseCompositor()) {
462 auto start
= TimeStamp::Now();
463 if (auto* fxrHandler
= mWidget
->AsWindows()->GetFxrOutputHandler()) {
464 // There is a Firefox Reality handler for this swapchain. Update this
465 // window's contents to the VR window.
466 if (fxrHandler
->TryInitialize(mSwapChain
, mDevice
)) {
467 fxrHandler
->UpdateOutput(mCtx
);
471 const UINT interval
=
474 gfx_webrender_dcomp_video_swap_chain_present_interval_0()
477 const UINT flags
= 0;
479 const LayoutDeviceIntSize
& bufferSize
= mBufferSize
.ref();
480 if (mUsePartialPresent
&& mSwapChain1
) {
481 // Clear full render flag.
483 // If there is no diry rect, we skip SwapChain present.
484 if (!aDirtyRects
.IsEmpty()) {
486 StackArray
<RECT
, 1> rects(aDirtyRects
.Length());
488 for (size_t i
= 0; i
< aDirtyRects
.Length(); ++i
) {
489 const DeviceIntRect
& rect
= aDirtyRects
[i
];
490 // Clip rect to bufferSize
491 int left
= std::clamp(rect
.min
.x
, 0, bufferSize
.width
);
492 int top
= std::clamp(rect
.min
.y
, 0, bufferSize
.height
);
493 int right
= std::clamp(rect
.max
.x
, 0, bufferSize
.width
);
494 int bottom
= std::clamp(rect
.max
.y
, 0, bufferSize
.height
);
496 // When rect is not empty, the rect could be passed to Present1().
497 if (left
< right
&& top
< bottom
) {
498 rects
[rectsCount
].left
= left
;
499 rects
[rectsCount
].top
= top
;
500 rects
[rectsCount
].right
= right
;
501 rects
[rectsCount
].bottom
= bottom
;
506 if (rectsCount
> 0) {
507 DXGI_PRESENT_PARAMETERS params
;
509 params
.DirtyRectsCount
= rectsCount
;
510 params
.pDirtyRects
= rects
.data();
513 hr
= mSwapChain1
->Present1(interval
, flags
, ¶ms
);
514 if (FAILED(hr
) && hr
!= DXGI_STATUS_OCCLUDED
) {
515 gfxCriticalNote
<< "Present1 failed: " << gfx::hexa(hr
);
521 mSwapChain
->Present(interval
, flags
);
523 auto end
= TimeStamp::Now();
524 mozilla::Telemetry::Accumulate(mozilla::Telemetry::COMPOSITE_SWAP_TIME
,
525 (end
- start
).ToMilliseconds() * 10.);
527 if (mFirstPresent
&& mDCLayerTree
) {
528 // Wait for the GPU to finish executing its commands before
529 // committing the DirectComposition tree, or else the swapchain
530 // may flicker black when it's first presented.
531 RefPtr
<IDXGIDevice2
> dxgiDevice2
;
532 mDevice
->QueryInterface((IDXGIDevice2
**)getter_AddRefs(dxgiDevice2
));
533 MOZ_ASSERT(dxgiDevice2
);
535 HANDLE event
= ::CreateEvent(nullptr, false, false, nullptr);
536 HRESULT hr
= dxgiDevice2
->EnqueueSetEvent(event
);
538 DebugOnly
<DWORD
> result
= ::WaitForSingleObject(event
, INFINITE
);
539 MOZ_ASSERT(result
== WAIT_OBJECT_0
);
541 gfxCriticalNoteOnce
<< "EnqueueSetEvent failed: " << gfx::hexa(hr
);
543 ::CloseHandle(event
);
545 mFirstPresent
= false;
548 if (mDisablingNativeCompositor
) {
549 // During disabling native compositor, we need to wait all gpu tasks
550 // complete. Otherwise, rendering window could cause white flash.
551 WaitForPreviousGraphicsCommandsFinishedQuery(/* aWaitAll */ true);
552 mDisablingNativeCompositor
= false;
556 mDCLayerTree
->MaybeUpdateDebug();
557 mDCLayerTree
->MaybeCommit();
563 bool RenderCompositorANGLE::WaitForGPU() {
564 // Note: this waits on the query we inserted in the previous frame,
565 // not the one we just inserted now. Example:
568 // (first frame, no wait)
571 // Wait for query #1.
574 // Wait for query #2.
576 // This ensures we're done reading textures before swapping buffers.
577 if (!StaticPrefs::gfx_webrender_wait_gpu_finished_disabled_AtStartup()) {
578 return WaitForPreviousGraphicsCommandsFinishedQuery();
583 bool RenderCompositorANGLE::ResizeBufferIfNeeded() {
584 MOZ_ASSERT(mSwapChain
);
586 LayoutDeviceIntSize size
= mWidget
->GetClientSize();
588 // DXGI does not like 0x0 swapchains. ResizeBuffers() failed when 0x0 was set
589 // when DComp is used.
590 size
.width
= std::max(size
.width
, 1);
591 size
.height
= std::max(size
.height
, 1);
593 if (mBufferSize
.isSome() && mBufferSize
.ref() == size
) {
594 MOZ_ASSERT(mEGLSurface
);
598 // Release EGLSurface of back buffer before calling ResizeBuffers().
601 mBufferSize
= Some(size
);
603 if (!CreateEGLSurface()) {
608 if (mUsePartialPresent
) {
614 bool RenderCompositorANGLE::CreateEGLSurface() {
615 MOZ_ASSERT(mBufferSize
.isSome());
616 MOZ_ASSERT(mEGLSurface
== EGL_NO_SURFACE
);
619 RefPtr
<ID3D11Texture2D
> backBuf
;
621 if (mBufferSize
.isNothing()) {
622 gfxCriticalNote
<< "Buffer size is invalid";
626 const LayoutDeviceIntSize
& size
= mBufferSize
.ref();
629 DXGI_SWAP_CHAIN_DESC desc
;
630 hr
= mSwapChain
->GetDesc(&desc
);
632 gfxCriticalNote
<< "Failed to read swap chain description: "
633 << gfx::hexa(hr
) << " Size : " << size
;
636 hr
= mSwapChain
->ResizeBuffers(desc
.BufferCount
, size
.width
, size
.height
,
637 DXGI_FORMAT_B8G8R8A8_UNORM
, 0);
639 gfxCriticalNote
<< "Failed to resize swap chain buffers: " << gfx::hexa(hr
)
640 << " Size : " << size
;
644 hr
= mSwapChain
->GetBuffer(0, __uuidof(ID3D11Texture2D
),
645 (void**)getter_AddRefs(backBuf
));
646 if (hr
== DXGI_ERROR_INVALID_CALL
) {
647 // This happens on some GPUs/drivers when there's a TDR.
648 if (mDevice
->GetDeviceRemovedReason() != S_OK
) {
649 gfxCriticalError() << "GetBuffer returned invalid call: " << gfx::hexa(hr
)
650 << " Size : " << size
;
655 const EGLint pbuffer_attribs
[]{LOCAL_EGL_WIDTH
, size
.width
, LOCAL_EGL_HEIGHT
,
656 size
.height
, LOCAL_EGL_NONE
};
658 const auto buffer
= reinterpret_cast<EGLClientBuffer
>(backBuf
.get());
660 const auto& gle
= gl::GLContextEGL::Cast(mGL
);
661 const auto& egl
= gle
->mEgl
;
662 const EGLSurface surface
= egl
->fCreatePbufferFromClientBuffer(
663 LOCAL_EGL_D3D_TEXTURE_ANGLE
, buffer
, mEGLConfig
, pbuffer_attribs
);
666 EGLint err
= egl
->mLib
->fGetError();
667 gfxCriticalError() << "Failed to create Pbuffer of back buffer error: "
668 << gfx::hexa(err
) << " Size : " << size
;
672 mEGLSurface
= surface
;
677 void RenderCompositorANGLE::DestroyEGLSurface() {
678 // Release EGLSurface of back buffer before calling ResizeBuffers().
680 const auto& gle
= gl::GLContextEGL::Cast(gl());
681 const auto& egl
= gle
->mEgl
;
682 gle
->SetEGLSurfaceOverride(EGL_NO_SURFACE
);
683 egl
->fDestroySurface(mEGLSurface
);
684 mEGLSurface
= nullptr;
688 void RenderCompositorANGLE::Pause() {}
690 bool RenderCompositorANGLE::Resume() { return true; }
692 void RenderCompositorANGLE::Update() {
693 // Update compositor window's size if it exists.
694 // It needs to be called here, since OS might update compositor
695 // window's size at unexpected timing.
696 mWidget
->AsWindows()->UpdateCompositorWndSizeIfNecessary();
699 bool RenderCompositorANGLE::MakeCurrent() {
700 gl::GLContextEGL::Cast(gl())->SetEGLSurfaceOverride(mEGLSurface
);
701 return gl()->MakeCurrent();
704 LayoutDeviceIntSize
RenderCompositorANGLE::GetBufferSize() {
705 if (!UseCompositor()) {
706 MOZ_ASSERT(mBufferSize
.isSome());
707 if (mBufferSize
.isNothing()) {
708 return LayoutDeviceIntSize();
710 return mBufferSize
.ref();
712 auto size
= mWidget
->GetClientSize();
713 // This size is used for WR DEBUG_OVERLAY. Its DCTile does not like 0.
714 size
.width
= std::max(size
.width
, 1);
715 size
.height
= std::max(size
.height
, 1);
720 RefPtr
<ID3D11Query
> RenderCompositorANGLE::GetD3D11Query() {
721 RefPtr
<ID3D11Query
> query
;
723 if (mRecycledQuery
) {
724 query
= mRecycledQuery
.forget();
728 CD3D11_QUERY_DESC
desc(D3D11_QUERY_EVENT
);
729 HRESULT hr
= mDevice
->CreateQuery(&desc
, getter_AddRefs(query
));
730 if (FAILED(hr
) || !query
) {
731 gfxWarning() << "Could not create D3D11_QUERY_EVENT: " << gfx::hexa(hr
);
737 void RenderCompositorANGLE::InsertGraphicsCommandsFinishedWaitQuery(
738 RenderedFrameId aFrameId
) {
739 RefPtr
<ID3D11Query
> query
;
740 query
= GetD3D11Query();
747 mWaitForPresentQueries
.emplace(aFrameId
, query
);
750 bool RenderCompositorANGLE::WaitForPreviousGraphicsCommandsFinishedQuery(
752 size_t waitLatency
= mUseTripleBuffering
? 3 : 2;
757 while (mWaitForPresentQueries
.size() >= waitLatency
) {
758 auto queryPair
= mWaitForPresentQueries
.front();
761 layers::WaitForFrameGPUQuery(mDevice
, mCtx
, queryPair
.second
, &result
);
764 mWaitForPresentQueries
.pop();
768 // Recycle query for later use.
769 mRecycledQuery
= queryPair
.second
;
770 mLastCompletedFrameId
= queryPair
.first
;
771 mWaitForPresentQueries
.pop();
776 RenderedFrameId
RenderCompositorANGLE::GetLastCompletedFrameId() {
777 while (!mWaitForPresentQueries
.empty()) {
778 auto queryPair
= mWaitForPresentQueries
.front();
779 if (mCtx
->GetData(queryPair
.second
, nullptr, 0,
780 D3D11_ASYNC_GETDATA_DONOTFLUSH
) != S_OK
) {
784 mRecycledQuery
= queryPair
.second
;
785 mLastCompletedFrameId
= queryPair
.first
;
786 mWaitForPresentQueries
.pop();
789 nsPrintfCString
marker("Pending frames %u",
790 (uint32_t)mWaitForPresentQueries
.size());
791 PROFILER_MARKER_TEXT("GetLastCompletedFrameId", GRAPHICS
, {}, marker
);
793 return mLastCompletedFrameId
;
796 RenderedFrameId
RenderCompositorANGLE::UpdateFrameId() {
797 RenderedFrameId frameId
= GetNextRenderFrameId();
798 InsertGraphicsCommandsFinishedWaitQuery(frameId
);
802 gfx::DeviceResetReason
RenderCompositorANGLE::IsContextLost(bool aForce
) {
803 // glGetGraphicsResetStatus does not always work to detect timeout detection
804 // and recovery (TDR). On Windows, ANGLE itself is just relying upon the same
805 // API, so we should not need to check it separately.
806 auto reason
= mDevice
->GetDeviceRemovedReason();
807 return layers::DXGIErrorToDeviceResetReason(reason
);
810 bool RenderCompositorANGLE::UseCompositor() const {
811 return mUseNativeCompositor
&& mDCLayerTree
&&
812 gfx::gfxVars::UseWebRenderCompositor();
815 bool RenderCompositorANGLE::SupportAsyncScreenshot() {
816 return !UseCompositor() && !mDisablingNativeCompositor
;
819 bool RenderCompositorANGLE::ShouldUseNativeCompositor() {
820 return UseCompositor();
823 void RenderCompositorANGLE::CompositorBeginFrame() {
824 mDCLayerTree
->CompositorBeginFrame();
827 void RenderCompositorANGLE::CompositorEndFrame() {
828 mDCLayerTree
->CompositorEndFrame();
831 void RenderCompositorANGLE::Bind(wr::NativeTileId aId
,
832 wr::DeviceIntPoint
* aOffset
, uint32_t* aFboId
,
833 wr::DeviceIntRect aDirtyRect
,
834 wr::DeviceIntRect aValidRect
) {
835 mDCLayerTree
->Bind(aId
, aOffset
, aFboId
, aDirtyRect
, aValidRect
);
838 void RenderCompositorANGLE::Unbind() { mDCLayerTree
->Unbind(); }
840 void RenderCompositorANGLE::BindSwapChain(wr::NativeSurfaceId aId
) {
841 mDCLayerTree
->BindSwapChain(aId
);
843 void RenderCompositorANGLE::PresentSwapChain(wr::NativeSurfaceId aId
) {
844 mDCLayerTree
->PresentSwapChain(aId
);
847 void RenderCompositorANGLE::CreateSurface(wr::NativeSurfaceId aId
,
848 wr::DeviceIntPoint aVirtualOffset
,
849 wr::DeviceIntSize aTileSize
,
851 mDCLayerTree
->CreateSurface(aId
, aVirtualOffset
, aTileSize
, aIsOpaque
);
854 void RenderCompositorANGLE::CreateSwapChainSurface(wr::NativeSurfaceId aId
,
855 wr::DeviceIntSize aSize
,
857 mDCLayerTree
->CreateSwapChainSurface(aId
, aSize
, aIsOpaque
);
860 void RenderCompositorANGLE::ResizeSwapChainSurface(wr::NativeSurfaceId aId
,
861 wr::DeviceIntSize aSize
) {
862 mDCLayerTree
->ResizeSwapChainSurface(aId
, aSize
);
865 void RenderCompositorANGLE::CreateExternalSurface(wr::NativeSurfaceId aId
,
867 mDCLayerTree
->CreateExternalSurface(aId
, aIsOpaque
);
870 void RenderCompositorANGLE::DestroySurface(NativeSurfaceId aId
) {
871 mDCLayerTree
->DestroySurface(aId
);
874 void RenderCompositorANGLE::CreateTile(wr::NativeSurfaceId aId
, int aX
,
876 mDCLayerTree
->CreateTile(aId
, aX
, aY
);
879 void RenderCompositorANGLE::DestroyTile(wr::NativeSurfaceId aId
, int aX
,
881 mDCLayerTree
->DestroyTile(aId
, aX
, aY
);
884 void RenderCompositorANGLE::AttachExternalImage(
885 wr::NativeSurfaceId aId
, wr::ExternalImageId aExternalImage
) {
886 mDCLayerTree
->AttachExternalImage(aId
, aExternalImage
);
889 void RenderCompositorANGLE::AddSurface(
890 wr::NativeSurfaceId aId
, const wr::CompositorSurfaceTransform
& aTransform
,
891 wr::DeviceIntRect aClipRect
, wr::ImageRendering aImageRendering
) {
892 mDCLayerTree
->AddSurface(aId
, aTransform
, aClipRect
, aImageRendering
);
895 void RenderCompositorANGLE::GetCompositorCapabilities(
896 CompositorCapabilities
* aCaps
) {
897 RenderCompositor::GetCompositorCapabilities(aCaps
);
899 if (StaticPrefs::gfx_webrender_dcomp_use_virtual_surfaces_AtStartup()) {
900 aCaps
->virtual_surface_size
= VIRTUAL_SURFACE_SIZE
;
902 aCaps
->virtual_surface_size
= 0;
904 // DComp video overlay does not support negative scaling. See Bug 1831820
905 aCaps
->supports_external_compositor_surface_negative_scaling
= false;
908 void RenderCompositorANGLE::EnableNativeCompositor(bool aEnable
) {
909 // XXX Re-enable native compositor is not handled yet.
910 MOZ_RELEASE_ASSERT(!mDisablingNativeCompositor
);
911 MOZ_RELEASE_ASSERT(!aEnable
);
912 LOG("RenderCompositorANGLE::EnableNativeCompositor() aEnable %d", aEnable
);
914 if (!UseCompositor()) {
918 mUseNativeCompositor
= false;
919 mDCLayerTree
->DisableNativeCompositor();
921 if (!RecreateNonNativeCompositorSwapChain()) {
922 gfxCriticalNote
<< "Failed to re-create SwapChain";
923 RenderThread::Get()->HandleWebRenderError(WebRenderError::NEW_SURFACE
);
927 mDisablingNativeCompositor
= true;
930 bool RenderCompositorANGLE::RecreateNonNativeCompositorSwapChain() {
934 RefPtr
<IDXGISwapChain1
> swapChain1
=
935 CreateSwapChainForDComp(mUseTripleBuffering
);
939 mSwapChain
= swapChain1
;
940 mSwapChain1
= swapChain1
;
942 mDCLayerTree
->SetDefaultSwapChain(swapChain1
);
944 return ResizeBufferIfNeeded();
947 void RenderCompositorANGLE::InitializeUsePartialPresent() {
948 // Even when mSwapChain1 is null, we could enable WR partial present, since
949 // when mSwapChain1 is null, SwapChain is blit model swap chain with one
951 mUsePartialPresent
= !UseCompositor() &&
952 !mWidget
->AsWindows()->HasFxrOutputHandler() &&
953 gfx::gfxVars::WebRenderMaxPartialPresentRects() > 0;
956 bool RenderCompositorANGLE::UsePartialPresent() { return mUsePartialPresent
; }
958 bool RenderCompositorANGLE::RequestFullRender() { return mFullRender
; }
960 uint32_t RenderCompositorANGLE::GetMaxPartialPresentRects() {
961 if (!mUsePartialPresent
) {
964 return gfx::gfxVars::WebRenderMaxPartialPresentRects();
967 bool RenderCompositorANGLE::MaybeReadback(
968 const gfx::IntSize
& aReadbackSize
, const wr::ImageFormat
& aReadbackFormat
,
969 const Range
<uint8_t>& aReadbackBuffer
, bool* aNeedsYFlip
) {
970 MOZ_ASSERT(aReadbackFormat
== wr::ImageFormat::BGRA8
);
972 if (!UseCompositor()) {
976 auto start
= TimeStamp::Now();
978 HDC nulldc
= ::GetDC(NULL
);
979 HDC dc
= ::CreateCompatibleDC(nulldc
);
980 ::ReleaseDC(nullptr, nulldc
);
982 gfxCriticalError() << "CreateCompatibleDC failed";
986 BITMAPV4HEADER header
;
987 memset(&header
, 0, sizeof(BITMAPV4HEADER
));
988 header
.bV4Size
= sizeof(BITMAPV4HEADER
);
989 header
.bV4Width
= aReadbackSize
.width
;
990 header
.bV4Height
= -LONG(aReadbackSize
.height
); // top-to-buttom DIB
991 header
.bV4Planes
= 1;
992 header
.bV4BitCount
= 32;
993 header
.bV4V4Compression
= BI_BITFIELDS
;
994 header
.bV4RedMask
= 0x00FF0000;
995 header
.bV4GreenMask
= 0x0000FF00;
996 header
.bV4BlueMask
= 0x000000FF;
997 header
.bV4AlphaMask
= 0xFF000000;
999 void* readbackBits
= nullptr;
1001 ::CreateDIBSection(dc
, reinterpret_cast<BITMAPINFO
*>(&header
),
1002 DIB_RGB_COLORS
, &readbackBits
, nullptr, 0);
1005 gfxCriticalError() << "CreateDIBSection failed";
1009 ::SelectObject(dc
, bitmap
);
1011 UINT flags
= PW_CLIENTONLY
| PW_RENDERFULLCONTENT
;
1012 HWND hwnd
= mWidget
->AsWindows()->GetHwnd();
1014 mDCLayerTree
->WaitForCommitCompletion();
1016 BOOL result
= ::PrintWindow(hwnd
, dc
, flags
);
1018 ::DeleteObject(bitmap
);
1020 gfxCriticalError() << "PrintWindow failed";
1026 memcpy(&aReadbackBuffer
[0], readbackBits
, aReadbackBuffer
.length());
1028 ::DeleteObject(bitmap
);
1031 uint32_t latencyMs
= round((TimeStamp::Now() - start
).ToMilliseconds());
1032 if (latencyMs
> 500) {
1033 gfxCriticalNote
<< "Readback took too long: " << latencyMs
<< " ms";
1037 *aNeedsYFlip
= false;
1043 } // namespace mozilla::wr