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"
30 #define NTDDI_VERSION NTDDI_WIN8
36 // Flag for PrintWindow() that is defined in Winuser.h. It is defined since
37 // Windows 8.1. This allows PrintWindow to capture window content that is
38 // rendered with DirectComposition.
39 #undef PW_RENDERFULLCONTENT
40 #define PW_RENDERFULLCONTENT 0x00000002
45 extern LazyLogModule gRenderThreadLog
;
46 #define LOG(...) MOZ_LOG(gRenderThreadLog, LogLevel::Debug, (__VA_ARGS__))
49 UniquePtr
<RenderCompositor
> RenderCompositorANGLE::Create(
50 const RefPtr
<widget::CompositorWidget
>& aWidget
, nsACString
& aError
) {
51 RefPtr
<gl::GLContext
> gl
= RenderThread::Get()->SingletonGL(aError
);
53 if (aError
.IsEmpty()) {
54 aError
.Assign("RcANGLE(no shared GL)"_ns
);
56 aError
.Append("(Create)"_ns
);
61 UniquePtr
<RenderCompositorANGLE
> compositor
=
62 MakeUnique
<RenderCompositorANGLE
>(aWidget
, std::move(gl
));
63 if (!compositor
->Initialize(aError
)) {
69 RenderCompositorANGLE::RenderCompositorANGLE(
70 const RefPtr
<widget::CompositorWidget
>& aWidget
,
71 RefPtr
<gl::GLContext
>&& aGL
)
72 : RenderCompositor(aWidget
),
76 mUseTripleBuffering(false),
78 mUseNativeCompositor(true),
79 mUsePartialPresent(false),
81 mDisablingNativeCompositor(false) {
83 LOG("RenderCompositorANGLE::RenderCompositorANGLE()");
86 RenderCompositorANGLE::~RenderCompositorANGLE() {
87 LOG("RenderCompositorANGLE::~RenderCompositorANGLE()");
90 MOZ_ASSERT(!mEGLSurface
);
93 ID3D11Device
* RenderCompositorANGLE::GetDeviceOfEGLDisplay(nsACString
& aError
) {
94 const auto& gle
= gl::GLContextEGL::Cast(mGL
);
95 const auto& egl
= gle
->mEgl
;
98 !egl
->mLib
->IsExtensionSupported(gl::EGLLibExtension::EXT_device_query
)) {
99 aError
.Assign("RcANGLE(no EXT_device_query support)"_ns
);
103 // Fetch the D3D11 device.
104 EGLDeviceEXT eglDevice
= nullptr;
105 egl
->fQueryDisplayAttribEXT(LOCAL_EGL_DEVICE_EXT
, (EGLAttrib
*)&eglDevice
);
106 MOZ_ASSERT(eglDevice
);
107 ID3D11Device
* device
= nullptr;
108 egl
->mLib
->fQueryDeviceAttribEXT(eglDevice
, LOCAL_EGL_D3D11_DEVICE_ANGLE
,
109 (EGLAttrib
*)&device
);
111 aError
.Assign("RcANGLE(get D3D11Device from EGLDisplay failed)"_ns
);
117 bool RenderCompositorANGLE::Initialize(nsACString
& aError
) {
118 // TODO(aosmond): This causes us to lose WebRender because it is unable to
119 // distinguish why we failed and retry once the reset is complete. This does
120 // appear to happen in the wild, so we really should try to do something
122 if (RenderThread::Get()->IsHandlingDeviceReset()) {
123 aError
.Assign("RcANGLE(waiting device reset)"_ns
);
127 // Force enable alpha channel to make sure ANGLE use correct framebuffer
129 const auto& gle
= gl::GLContextEGL::Cast(mGL
);
130 const auto& egl
= gle
->mEgl
;
131 if (!gl::CreateConfig(*egl
, &mEGLConfig
, /* bpp */ 32,
132 /* enableDepthBuffer */ false, mGL
->IsGLES())) {
133 aError
.Assign("RcANGLE(create EGLConfig failed)"_ns
);
136 MOZ_ASSERT(mEGLConfig
);
138 mDevice
= GetDeviceOfEGLDisplay(aError
);
144 mDevice
->GetImmediateContext(getter_AddRefs(mCtx
));
146 aError
.Assign("RcANGLE(get immediate context failed)"_ns
);
150 // Disable native compositor when fast snapshot is needed.
151 // Taking snapshot of native compositor is very slow on Windows.
152 if (mWidget
->GetCompositorOptions().NeedFastSnaphot()) {
153 mUseNativeCompositor
= false;
156 // Create DCLayerTree when DirectComposition is used.
157 if (gfx::gfxVars::UseWebRenderDCompWin()) {
158 HWND compositorHwnd
= GetCompositorHwnd();
159 if (compositorHwnd
) {
160 mDCLayerTree
= DCLayerTree::Create(mGL
, mEGLConfig
, mDevice
, mCtx
,
161 compositorHwnd
, aError
);
166 aError
.Assign("RcANGLE(no compositor window)"_ns
);
171 // Create SwapChain when compositor is not used
172 if (!UseCompositor()) {
173 if (!CreateSwapChain(aError
)) {
174 // SwapChain creation failed.
179 mSyncObject
= layers::SyncObjectHost::CreateSyncObjectHost(mDevice
);
180 if (!mSyncObject
->Init()) {
181 // Some errors occur. Clear the mSyncObject here.
182 // Then, there will be no texture synchronization.
183 aError
.Assign("RcANGLE(create SyncObject failed)"_ns
);
187 InitializeUsePartialPresent();
192 HWND
RenderCompositorANGLE::GetCompositorHwnd() {
195 if (XRE_IsGPUProcess()) {
196 hwnd
= mWidget
->AsWindows()->GetCompositorHwnd();
199 gfx_webrender_enabled_no_gpu_process_with_angle_win_AtStartup()) {
200 MOZ_ASSERT(XRE_IsParentProcess());
202 // When GPU process does not exist, we do not need to use compositor window.
203 hwnd
= mWidget
->AsWindows()->GetHwnd();
209 bool RenderCompositorANGLE::CreateSwapChain(nsACString
& aError
) {
210 MOZ_ASSERT(!UseCompositor());
212 HWND hwnd
= mWidget
->AsWindows()->GetHwnd();
214 RefPtr
<IDXGIDevice
> dxgiDevice
;
215 mDevice
->QueryInterface((IDXGIDevice
**)getter_AddRefs(dxgiDevice
));
217 RefPtr
<IDXGIFactory
> dxgiFactory
;
219 RefPtr
<IDXGIAdapter
> adapter
;
220 dxgiDevice
->GetAdapter(getter_AddRefs(adapter
));
223 IID_PPV_ARGS((IDXGIFactory
**)getter_AddRefs(dxgiFactory
)));
226 RefPtr
<IDXGIFactory2
> dxgiFactory2
;
227 HRESULT hr
= dxgiFactory
->QueryInterface(
228 (IDXGIFactory2
**)getter_AddRefs(dxgiFactory2
));
230 dxgiFactory2
= nullptr;
233 CreateSwapChainForDCompIfPossible(dxgiFactory2
);
234 if (gfx::gfxVars::UseWebRenderDCompWin() && !mSwapChain
) {
235 MOZ_ASSERT(GetCompositorHwnd());
236 aError
.Assign("RcANGLE(create swapchain for dcomp failed)"_ns
);
240 if (!mSwapChain
&& dxgiFactory2
) {
241 RefPtr
<IDXGISwapChain1
> swapChain1
;
242 bool useTripleBuffering
= false;
244 DXGI_SWAP_CHAIN_DESC1 desc
{};
247 desc
.Format
= DXGI_FORMAT_B8G8R8A8_UNORM
;
248 desc
.SampleDesc
.Count
= 1;
249 desc
.SampleDesc
.Quality
= 0;
250 desc
.BufferUsage
= DXGI_USAGE_RENDER_TARGET_OUTPUT
;
252 bool useFlipSequential
= gfx::gfxVars::UseWebRenderFlipSequentialWin();
253 if (useFlipSequential
&& !mWidget
->AsWindows()->GetCompositorHwnd()) {
254 useFlipSequential
= false;
255 gfxCriticalNoteOnce
<< "FLIP_SEQUENTIAL needs CompositorHwnd. Fallback";
258 if (useFlipSequential
) {
259 useTripleBuffering
= gfx::gfxVars::UseWebRenderTripleBufferingWin();
260 if (useTripleBuffering
) {
261 desc
.BufferCount
= 3;
263 desc
.BufferCount
= 2;
265 desc
.SwapEffect
= DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL
;
266 desc
.Scaling
= DXGI_SCALING_NONE
;
268 desc
.BufferCount
= 1;
269 desc
.SwapEffect
= DXGI_SWAP_EFFECT_SEQUENTIAL
;
270 desc
.Scaling
= DXGI_SCALING_STRETCH
;
274 hr
= dxgiFactory2
->CreateSwapChainForHwnd(
275 mDevice
, hwnd
, &desc
, nullptr, nullptr, getter_AddRefs(swapChain1
));
276 if (SUCCEEDED(hr
) && swapChain1
) {
277 DXGI_RGBA color
= {1.0f
, 1.0f
, 1.0f
, 1.0f
};
278 swapChain1
->SetBackgroundColor(&color
);
279 mSwapChain
= swapChain1
;
280 mSwapChain1
= swapChain1
;
281 mUseTripleBuffering
= useTripleBuffering
;
282 } else if (useFlipSequential
) {
283 gfxCriticalNoteOnce
<< "FLIP_SEQUENTIAL is not supported. Fallback";
288 if (mWidget
->AsWindows()->GetCompositorHwnd()) {
289 // Destroy compositor window.
290 mWidget
->AsWindows()->DestroyCompositorWindow();
291 hwnd
= mWidget
->AsWindows()->GetHwnd();
294 DXGI_SWAP_CHAIN_DESC swapDesc
{};
295 swapDesc
.BufferDesc
.Width
= 0;
296 swapDesc
.BufferDesc
.Height
= 0;
297 swapDesc
.BufferDesc
.Format
= DXGI_FORMAT_B8G8R8A8_UNORM
;
298 swapDesc
.BufferDesc
.RefreshRate
.Numerator
= 60;
299 swapDesc
.BufferDesc
.RefreshRate
.Denominator
= 1;
300 swapDesc
.SampleDesc
.Count
= 1;
301 swapDesc
.SampleDesc
.Quality
= 0;
302 swapDesc
.BufferUsage
= DXGI_USAGE_RENDER_TARGET_OUTPUT
;
303 swapDesc
.BufferCount
= 1;
304 swapDesc
.OutputWindow
= hwnd
;
305 swapDesc
.Windowed
= TRUE
;
307 swapDesc
.SwapEffect
= DXGI_SWAP_EFFECT_SEQUENTIAL
;
309 HRESULT hr
= dxgiFactory
->CreateSwapChain(dxgiDevice
, &swapDesc
,
310 getter_AddRefs(mSwapChain
));
313 nsPrintfCString("RcANGLE(swap chain create failed %lx)", hr
));
317 RefPtr
<IDXGISwapChain1
> swapChain1
;
318 hr
= mSwapChain
->QueryInterface(
319 (IDXGISwapChain1
**)getter_AddRefs(swapChain1
));
321 mSwapChain1
= swapChain1
;
325 // We need this because we don't want DXGI to respond to Alt+Enter.
326 dxgiFactory
->MakeWindowAssociation(hwnd
, DXGI_MWA_NO_WINDOW_CHANGES
);
328 if (!ResizeBufferIfNeeded()) {
329 aError
.Assign("RcANGLE(resize buffer failed)"_ns
);
336 void RenderCompositorANGLE::CreateSwapChainForDCompIfPossible(
337 IDXGIFactory2
* aDXGIFactory2
) {
338 if (!aDXGIFactory2
|| !mDCLayerTree
) {
342 HWND hwnd
= GetCompositorHwnd();
344 // When DirectComposition or DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL is used,
345 // compositor window needs to exist.
346 if (gfx::gfxVars::UseWebRenderDCompWin() ||
347 gfx::gfxVars::UseWebRenderFlipSequentialWin()) {
348 gfxCriticalNote
<< "Compositor window was not created";
353 // When compositor is enabled, CompositionSurface is used for rendering.
354 // It does not support triple buffering.
355 bool useTripleBuffering
=
356 gfx::gfxVars::UseWebRenderTripleBufferingWin() && !UseCompositor();
357 // Non Glass window is common since Windows 10.
358 bool useAlpha
= false;
359 RefPtr
<IDXGISwapChain1
> swapChain1
=
360 CreateSwapChainForDComp(useTripleBuffering
, useAlpha
);
362 mSwapChain
= swapChain1
;
363 mSwapChain1
= swapChain1
;
364 mUseTripleBuffering
= useTripleBuffering
;
365 mUseAlpha
= useAlpha
;
366 mDCLayerTree
->SetDefaultSwapChain(swapChain1
);
368 // Clear CLayerTree on falire
369 mDCLayerTree
= nullptr;
373 RefPtr
<IDXGISwapChain1
> RenderCompositorANGLE::CreateSwapChainForDComp(
374 bool aUseTripleBuffering
, bool aUseAlpha
) {
376 RefPtr
<IDXGIDevice
> dxgiDevice
;
377 mDevice
->QueryInterface((IDXGIDevice
**)getter_AddRefs(dxgiDevice
));
379 RefPtr
<IDXGIFactory
> dxgiFactory
;
381 RefPtr
<IDXGIAdapter
> adapter
;
382 dxgiDevice
->GetAdapter(getter_AddRefs(adapter
));
385 IID_PPV_ARGS((IDXGIFactory
**)getter_AddRefs(dxgiFactory
)));
388 RefPtr
<IDXGIFactory2
> dxgiFactory2
;
389 hr
= dxgiFactory
->QueryInterface(
390 (IDXGIFactory2
**)getter_AddRefs(dxgiFactory2
));
395 RefPtr
<IDXGISwapChain1
> swapChain1
;
396 DXGI_SWAP_CHAIN_DESC1 desc
{};
397 // DXGI does not like 0x0 swapchains. Swap chain creation failed when 0x0 was
401 desc
.Format
= DXGI_FORMAT_B8G8R8A8_UNORM
;
402 desc
.SampleDesc
.Count
= 1;
403 desc
.SampleDesc
.Quality
= 0;
404 desc
.BufferUsage
= DXGI_USAGE_RENDER_TARGET_OUTPUT
;
405 if (aUseTripleBuffering
) {
406 desc
.BufferCount
= 3;
408 desc
.BufferCount
= 2;
410 // DXGI_SCALING_NONE caused swap chain creation failure.
411 desc
.Scaling
= DXGI_SCALING_STRETCH
;
412 desc
.SwapEffect
= DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL
;
414 // This could degrade performance. Use it only when it is necessary.
415 desc
.AlphaMode
= DXGI_ALPHA_MODE_PREMULTIPLIED
;
417 desc
.AlphaMode
= DXGI_ALPHA_MODE_IGNORE
;
421 hr
= dxgiFactory2
->CreateSwapChainForComposition(mDevice
, &desc
, nullptr,
422 getter_AddRefs(swapChain1
));
423 if (SUCCEEDED(hr
) && swapChain1
) {
424 DXGI_RGBA color
= {1.0f
, 1.0f
, 1.0f
, 1.0f
};
425 swapChain1
->SetBackgroundColor(&color
);
432 bool RenderCompositorANGLE::BeginFrame() {
433 mWidget
->AsWindows()->UpdateCompositorWndSizeIfNecessary();
435 if (!UseCompositor()) {
437 bool useAlpha
= mWidget
->AsWindows()->HasGlass();
438 // When Alpha usage is changed, SwapChain needs to be recreatd.
439 if (useAlpha
!= mUseAlpha
) {
443 RefPtr
<IDXGISwapChain1
> swapChain1
=
444 CreateSwapChainForDComp(mUseTripleBuffering
, useAlpha
);
446 mSwapChain
= swapChain1
;
447 mUseAlpha
= useAlpha
;
448 mDCLayerTree
->SetDefaultSwapChain(swapChain1
);
449 // When alpha is used, we want to disable partial present.
455 gfxCriticalNote
<< "Failed to re-create SwapChain";
456 RenderThread::Get()->HandleWebRenderError(
457 WebRenderError::NEW_SURFACE
);
463 if (!ResizeBufferIfNeeded()) {
468 if (!MakeCurrent()) {
469 gfxCriticalNote
<< "Failed to make render context current, can't draw.";
473 if (RenderThread::Get()->SyncObjectNeeded() && mSyncObject
) {
474 if (!mSyncObject
->Synchronize(/* aFallible */ true)) {
475 // It's timeout or other error. Handle the device-reset here.
476 RenderThread::Get()->HandleDeviceReset(
477 "SyncObject", LOCAL_GL_UNKNOWN_CONTEXT_RESET_ARB
);
484 RenderedFrameId
RenderCompositorANGLE::EndFrame(
485 const nsTArray
<DeviceIntRect
>& aDirtyRects
) {
486 RenderedFrameId frameId
= GetNextRenderFrameId();
487 InsertGraphicsCommandsFinishedWaitQuery(frameId
);
489 if (!UseCompositor()) {
490 auto start
= TimeStamp::Now();
491 if (mWidget
->AsWindows()->HasFxrOutputHandler()) {
492 // There is a Firefox Reality handler for this swapchain. Update this
493 // window's contents to the VR window.
494 FxROutputHandler
* fxrHandler
=
495 mWidget
->AsWindows()->GetFxrOutputHandler();
496 if (fxrHandler
->TryInitialize(mSwapChain
, mDevice
)) {
497 fxrHandler
->UpdateOutput(mCtx
);
501 const LayoutDeviceIntSize
& bufferSize
= mBufferSize
.ref();
503 // During high contrast mode, alpha is used. In this case,
504 // IDXGISwapChain1::Present1 shows nothing with compositor window.
505 // In this case, we want to disable partial present by full render.
507 MOZ_ASSERT_IF(mUsePartialPresent
&& mUseAlpha
, mFullRender
);
509 if (mUsePartialPresent
&& !mUseAlpha
&& mSwapChain1
) {
510 // Clear full render flag.
512 // If there is no diry rect, we skip SwapChain present.
513 if (!aDirtyRects
.IsEmpty()) {
515 StackArray
<RECT
, 1> rects(aDirtyRects
.Length());
517 for (size_t i
= 0; i
< aDirtyRects
.Length(); ++i
) {
518 const DeviceIntRect
& rect
= aDirtyRects
[i
];
519 // Clip rect to bufferSize
520 int left
= std::max(0, std::min(rect
.min
.x
, bufferSize
.width
));
521 int top
= std::max(0, std::min(rect
.min
.y
, bufferSize
.height
));
522 int right
= std::max(0, std::min(rect
.max
.x
, bufferSize
.width
));
523 int bottom
= std::max(0, std::min(rect
.max
.y
, bufferSize
.height
));
525 // When rect is not empty, the rect could be passed to Present1().
526 if (left
< right
&& top
< bottom
) {
527 rects
[rectsCount
].left
= left
;
528 rects
[rectsCount
].top
= top
;
529 rects
[rectsCount
].right
= right
;
530 rects
[rectsCount
].bottom
= bottom
;
535 if (rectsCount
> 0) {
536 DXGI_PRESENT_PARAMETERS params
;
538 params
.DirtyRectsCount
= rectsCount
;
539 params
.pDirtyRects
= rects
.data();
542 hr
= mSwapChain1
->Present1(0, 0, ¶ms
);
543 if (FAILED(hr
) && hr
!= DXGI_STATUS_OCCLUDED
) {
544 gfxCriticalNote
<< "Present1 failed: " << gfx::hexa(hr
);
550 mSwapChain
->Present(0, 0);
552 auto end
= TimeStamp::Now();
553 mozilla::Telemetry::Accumulate(mozilla::Telemetry::COMPOSITE_SWAP_TIME
,
554 (end
- start
).ToMilliseconds() * 10.);
557 if (mDisablingNativeCompositor
) {
558 // During disabling native compositor, we need to wait all gpu tasks
559 // complete. Otherwise, rendering window could cause white flash.
560 WaitForPreviousGraphicsCommandsFinishedQuery(/* aWaitAll */ true);
561 mDisablingNativeCompositor
= false;
565 mDCLayerTree
->MaybeUpdateDebug();
566 mDCLayerTree
->MaybeCommit();
572 bool RenderCompositorANGLE::WaitForGPU() {
573 // Note: this waits on the query we inserted in the previous frame,
574 // not the one we just inserted now. Example:
577 // (first frame, no wait)
580 // Wait for query #1.
583 // Wait for query #2.
585 // This ensures we're done reading textures before swapping buffers.
586 if (!StaticPrefs::gfx_webrender_wait_gpu_finished_disabled_AtStartup()) {
587 return WaitForPreviousGraphicsCommandsFinishedQuery();
592 bool RenderCompositorANGLE::ResizeBufferIfNeeded() {
593 MOZ_ASSERT(mSwapChain
);
595 LayoutDeviceIntSize size
= mWidget
->GetClientSize();
597 // DXGI does not like 0x0 swapchains. ResizeBuffers() failed when 0x0 was set
598 // when DComp is used.
599 size
.width
= std::max(size
.width
, 1);
600 size
.height
= std::max(size
.height
, 1);
602 if (mBufferSize
.isSome() && mBufferSize
.ref() == size
) {
603 MOZ_ASSERT(mEGLSurface
);
607 // Release EGLSurface of back buffer before calling ResizeBuffers().
610 mBufferSize
= Some(size
);
612 if (!CreateEGLSurface()) {
617 if (mUsePartialPresent
) {
623 bool RenderCompositorANGLE::CreateEGLSurface() {
624 MOZ_ASSERT(mBufferSize
.isSome());
625 MOZ_ASSERT(mEGLSurface
== EGL_NO_SURFACE
);
628 RefPtr
<ID3D11Texture2D
> backBuf
;
630 if (mBufferSize
.isNothing()) {
631 gfxCriticalNote
<< "Buffer size is invalid";
635 const LayoutDeviceIntSize
& size
= mBufferSize
.ref();
638 DXGI_SWAP_CHAIN_DESC desc
;
639 hr
= mSwapChain
->GetDesc(&desc
);
641 gfxCriticalNote
<< "Failed to read swap chain description: "
642 << gfx::hexa(hr
) << " Size : " << size
;
645 hr
= mSwapChain
->ResizeBuffers(desc
.BufferCount
, size
.width
, size
.height
,
646 DXGI_FORMAT_B8G8R8A8_UNORM
, 0);
648 gfxCriticalNote
<< "Failed to resize swap chain buffers: " << gfx::hexa(hr
)
649 << " Size : " << size
;
653 hr
= mSwapChain
->GetBuffer(0, __uuidof(ID3D11Texture2D
),
654 (void**)getter_AddRefs(backBuf
));
655 if (hr
== DXGI_ERROR_INVALID_CALL
) {
656 // This happens on some GPUs/drivers when there's a TDR.
657 if (mDevice
->GetDeviceRemovedReason() != S_OK
) {
658 gfxCriticalError() << "GetBuffer returned invalid call: " << gfx::hexa(hr
)
659 << " Size : " << size
;
664 const EGLint pbuffer_attribs
[]{LOCAL_EGL_WIDTH
, size
.width
, LOCAL_EGL_HEIGHT
,
665 size
.height
, LOCAL_EGL_NONE
};
667 const auto buffer
= reinterpret_cast<EGLClientBuffer
>(backBuf
.get());
669 const auto& gle
= gl::GLContextEGL::Cast(mGL
);
670 const auto& egl
= gle
->mEgl
;
671 const EGLSurface surface
= egl
->fCreatePbufferFromClientBuffer(
672 LOCAL_EGL_D3D_TEXTURE_ANGLE
, buffer
, mEGLConfig
, pbuffer_attribs
);
675 EGLint err
= egl
->mLib
->fGetError();
676 gfxCriticalError() << "Failed to create Pbuffer of back buffer error: "
677 << gfx::hexa(err
) << " Size : " << size
;
681 mEGLSurface
= surface
;
686 void RenderCompositorANGLE::DestroyEGLSurface() {
687 // Release EGLSurface of back buffer before calling ResizeBuffers().
689 const auto& gle
= gl::GLContextEGL::Cast(gl());
690 const auto& egl
= gle
->mEgl
;
691 gle
->SetEGLSurfaceOverride(EGL_NO_SURFACE
);
692 egl
->fDestroySurface(mEGLSurface
);
693 mEGLSurface
= nullptr;
697 void RenderCompositorANGLE::Pause() {}
699 bool RenderCompositorANGLE::Resume() { return true; }
701 void RenderCompositorANGLE::Update() {
702 // Update compositor window's size if it exists.
703 // It needs to be called here, since OS might update compositor
704 // window's size at unexpected timing.
705 mWidget
->AsWindows()->UpdateCompositorWndSizeIfNecessary();
708 bool RenderCompositorANGLE::MakeCurrent() {
709 gl::GLContextEGL::Cast(gl())->SetEGLSurfaceOverride(mEGLSurface
);
710 return gl()->MakeCurrent();
713 LayoutDeviceIntSize
RenderCompositorANGLE::GetBufferSize() {
714 if (!UseCompositor()) {
715 MOZ_ASSERT(mBufferSize
.isSome());
716 if (mBufferSize
.isNothing()) {
717 return LayoutDeviceIntSize();
719 return mBufferSize
.ref();
721 auto size
= mWidget
->GetClientSize();
722 // This size is used for WR DEBUG_OVERLAY. Its DCTile does not like 0.
723 size
.width
= std::max(size
.width
, 1);
724 size
.height
= std::max(size
.height
, 1);
729 RefPtr
<ID3D11Query
> RenderCompositorANGLE::GetD3D11Query() {
730 RefPtr
<ID3D11Query
> query
;
732 if (mRecycledQuery
) {
733 query
= mRecycledQuery
.forget();
737 CD3D11_QUERY_DESC
desc(D3D11_QUERY_EVENT
);
738 HRESULT hr
= mDevice
->CreateQuery(&desc
, getter_AddRefs(query
));
739 if (FAILED(hr
) || !query
) {
740 gfxWarning() << "Could not create D3D11_QUERY_EVENT: " << gfx::hexa(hr
);
746 void RenderCompositorANGLE::InsertGraphicsCommandsFinishedWaitQuery(
747 RenderedFrameId aFrameId
) {
748 RefPtr
<ID3D11Query
> query
;
749 query
= GetD3D11Query();
756 mWaitForPresentQueries
.emplace(aFrameId
, query
);
759 bool RenderCompositorANGLE::WaitForPreviousGraphicsCommandsFinishedQuery(
761 size_t waitLatency
= mUseTripleBuffering
? 3 : 2;
766 while (mWaitForPresentQueries
.size() >= waitLatency
) {
767 auto queryPair
= mWaitForPresentQueries
.front();
770 layers::WaitForFrameGPUQuery(mDevice
, mCtx
, queryPair
.second
, &result
);
773 mWaitForPresentQueries
.pop();
777 // Recycle query for later use.
778 mRecycledQuery
= queryPair
.second
;
779 mLastCompletedFrameId
= queryPair
.first
;
780 mWaitForPresentQueries
.pop();
785 RenderedFrameId
RenderCompositorANGLE::GetLastCompletedFrameId() {
786 while (!mWaitForPresentQueries
.empty()) {
787 auto queryPair
= mWaitForPresentQueries
.front();
788 if (mCtx
->GetData(queryPair
.second
, nullptr, 0,
789 D3D11_ASYNC_GETDATA_DONOTFLUSH
) != S_OK
) {
793 mRecycledQuery
= queryPair
.second
;
794 mLastCompletedFrameId
= queryPair
.first
;
795 mWaitForPresentQueries
.pop();
798 nsPrintfCString
marker("Pending frames %u",
799 (uint32_t)mWaitForPresentQueries
.size());
800 PROFILER_MARKER_TEXT("GetLastCompletedFrameId", GRAPHICS
, {}, marker
);
802 return mLastCompletedFrameId
;
805 RenderedFrameId
RenderCompositorANGLE::UpdateFrameId() {
806 RenderedFrameId frameId
= GetNextRenderFrameId();
807 InsertGraphicsCommandsFinishedWaitQuery(frameId
);
811 GLenum
RenderCompositorANGLE::IsContextLost(bool aForce
) {
812 // glGetGraphicsResetStatus does not always work to detect timeout detection
813 // and recovery (TDR). On Windows, ANGLE itself is just relying upon the same
814 // API, so we should not need to check it separately.
815 auto reason
= mDevice
->GetDeviceRemovedReason();
818 return LOCAL_GL_NO_ERROR
;
819 case DXGI_ERROR_DEVICE_REMOVED
:
820 case DXGI_ERROR_DRIVER_INTERNAL_ERROR
:
821 NS_WARNING("Device reset due to system / different device");
822 return LOCAL_GL_INNOCENT_CONTEXT_RESET_ARB
;
823 case DXGI_ERROR_DEVICE_HUNG
:
824 case DXGI_ERROR_DEVICE_RESET
:
825 case DXGI_ERROR_INVALID_CALL
:
826 gfxCriticalError() << "Device reset due to WR device: "
827 << gfx::hexa(reason
);
828 return LOCAL_GL_GUILTY_CONTEXT_RESET_ARB
;
830 gfxCriticalError() << "Device reset with WR device unexpected reason: "
831 << gfx::hexa(reason
);
832 return LOCAL_GL_UNKNOWN_CONTEXT_RESET_ARB
;
836 bool RenderCompositorANGLE::UseCompositor() {
837 if (!mUseNativeCompositor
) {
841 if (!mDCLayerTree
|| !gfx::gfxVars::UseWebRenderCompositor()) {
847 bool RenderCompositorANGLE::SupportAsyncScreenshot() {
848 return !UseCompositor() && !mDisablingNativeCompositor
;
851 bool RenderCompositorANGLE::ShouldUseNativeCompositor() {
852 return UseCompositor();
855 void RenderCompositorANGLE::CompositorBeginFrame() {
856 mDCLayerTree
->CompositorBeginFrame();
859 void RenderCompositorANGLE::CompositorEndFrame() {
860 mDCLayerTree
->CompositorEndFrame();
863 void RenderCompositorANGLE::Bind(wr::NativeTileId aId
,
864 wr::DeviceIntPoint
* aOffset
, uint32_t* aFboId
,
865 wr::DeviceIntRect aDirtyRect
,
866 wr::DeviceIntRect aValidRect
) {
867 mDCLayerTree
->Bind(aId
, aOffset
, aFboId
, aDirtyRect
, aValidRect
);
870 void RenderCompositorANGLE::Unbind() { mDCLayerTree
->Unbind(); }
872 void RenderCompositorANGLE::CreateSurface(wr::NativeSurfaceId aId
,
873 wr::DeviceIntPoint aVirtualOffset
,
874 wr::DeviceIntSize aTileSize
,
876 mDCLayerTree
->CreateSurface(aId
, aVirtualOffset
, aTileSize
, aIsOpaque
);
879 void RenderCompositorANGLE::CreateExternalSurface(wr::NativeSurfaceId aId
,
881 mDCLayerTree
->CreateExternalSurface(aId
, aIsOpaque
);
884 void RenderCompositorANGLE::DestroySurface(NativeSurfaceId aId
) {
885 mDCLayerTree
->DestroySurface(aId
);
888 void RenderCompositorANGLE::CreateTile(wr::NativeSurfaceId aId
, int aX
,
890 mDCLayerTree
->CreateTile(aId
, aX
, aY
);
893 void RenderCompositorANGLE::DestroyTile(wr::NativeSurfaceId aId
, int aX
,
895 mDCLayerTree
->DestroyTile(aId
, aX
, aY
);
898 void RenderCompositorANGLE::AttachExternalImage(
899 wr::NativeSurfaceId aId
, wr::ExternalImageId aExternalImage
) {
900 mDCLayerTree
->AttachExternalImage(aId
, aExternalImage
);
903 void RenderCompositorANGLE::AddSurface(
904 wr::NativeSurfaceId aId
, const wr::CompositorSurfaceTransform
& aTransform
,
905 wr::DeviceIntRect aClipRect
, wr::ImageRendering aImageRendering
) {
906 mDCLayerTree
->AddSurface(aId
, aTransform
, aClipRect
, aImageRendering
);
909 void RenderCompositorANGLE::GetCompositorCapabilities(
910 CompositorCapabilities
* aCaps
) {
911 RenderCompositor::GetCompositorCapabilities(aCaps
);
913 if (StaticPrefs::gfx_webrender_dcomp_use_virtual_surfaces_AtStartup()) {
914 aCaps
->virtual_surface_size
= VIRTUAL_SURFACE_SIZE
;
916 aCaps
->virtual_surface_size
= 0;
918 // DComp video overlay does not support negative scaling. See Bug 1831820
919 aCaps
->supports_external_compositor_surface_negative_scaling
= false;
922 void RenderCompositorANGLE::EnableNativeCompositor(bool aEnable
) {
923 // XXX Re-enable native compositor is not handled yet.
924 MOZ_RELEASE_ASSERT(!mDisablingNativeCompositor
);
925 MOZ_RELEASE_ASSERT(!aEnable
);
926 LOG("RenderCompositorANGLE::EnableNativeCompositor() aEnable %d", aEnable
);
928 if (!UseCompositor()) {
932 mUseNativeCompositor
= false;
933 mDCLayerTree
->DisableNativeCompositor();
935 bool useAlpha
= mWidget
->AsWindows()->HasGlass();
939 RefPtr
<IDXGISwapChain1
> swapChain1
=
940 CreateSwapChainForDComp(mUseTripleBuffering
, useAlpha
);
942 mSwapChain
= swapChain1
;
943 mUseAlpha
= useAlpha
;
944 mDCLayerTree
->SetDefaultSwapChain(swapChain1
);
945 // When alpha is used, we want to disable partial present.
950 ResizeBufferIfNeeded();
952 gfxCriticalNote
<< "Failed to re-create SwapChain";
953 RenderThread::Get()->HandleWebRenderError(WebRenderError::NEW_SURFACE
);
956 mDisablingNativeCompositor
= true;
959 void RenderCompositorANGLE::InitializeUsePartialPresent() {
960 // Even when mSwapChain1 is null, we could enable WR partial present, since
961 // when mSwapChain1 is null, SwapChain is blit model swap chain with one
963 if (UseCompositor() || mWidget
->AsWindows()->HasFxrOutputHandler() ||
964 gfx::gfxVars::WebRenderMaxPartialPresentRects() <= 0) {
965 mUsePartialPresent
= false;
967 mUsePartialPresent
= true;
971 bool RenderCompositorANGLE::UsePartialPresent() { return mUsePartialPresent
; }
973 bool RenderCompositorANGLE::RequestFullRender() { return mFullRender
; }
975 uint32_t RenderCompositorANGLE::GetMaxPartialPresentRects() {
976 if (!mUsePartialPresent
) {
979 return gfx::gfxVars::WebRenderMaxPartialPresentRects();
982 bool RenderCompositorANGLE::MaybeReadback(
983 const gfx::IntSize
& aReadbackSize
, const wr::ImageFormat
& aReadbackFormat
,
984 const Range
<uint8_t>& aReadbackBuffer
, bool* aNeedsYFlip
) {
985 MOZ_ASSERT(aReadbackFormat
== wr::ImageFormat::BGRA8
);
987 if (!UseCompositor()) {
991 auto start
= TimeStamp::Now();
993 HDC nulldc
= ::GetDC(NULL
);
994 HDC dc
= ::CreateCompatibleDC(nulldc
);
995 ::ReleaseDC(nullptr, nulldc
);
997 gfxCriticalError() << "CreateCompatibleDC failed";
1001 BITMAPV4HEADER header
;
1002 memset(&header
, 0, sizeof(BITMAPV4HEADER
));
1003 header
.bV4Size
= sizeof(BITMAPV4HEADER
);
1004 header
.bV4Width
= aReadbackSize
.width
;
1005 header
.bV4Height
= -LONG(aReadbackSize
.height
); // top-to-buttom DIB
1006 header
.bV4Planes
= 1;
1007 header
.bV4BitCount
= 32;
1008 header
.bV4V4Compression
= BI_BITFIELDS
;
1009 header
.bV4RedMask
= 0x00FF0000;
1010 header
.bV4GreenMask
= 0x0000FF00;
1011 header
.bV4BlueMask
= 0x000000FF;
1012 header
.bV4AlphaMask
= 0xFF000000;
1014 void* readbackBits
= nullptr;
1016 ::CreateDIBSection(dc
, reinterpret_cast<BITMAPINFO
*>(&header
),
1017 DIB_RGB_COLORS
, &readbackBits
, nullptr, 0);
1020 gfxCriticalError() << "CreateDIBSection failed";
1024 ::SelectObject(dc
, bitmap
);
1026 UINT flags
= PW_CLIENTONLY
| PW_RENDERFULLCONTENT
;
1027 HWND hwnd
= mWidget
->AsWindows()->GetHwnd();
1029 mDCLayerTree
->WaitForCommitCompletion();
1031 BOOL result
= ::PrintWindow(hwnd
, dc
, flags
);
1033 ::DeleteObject(bitmap
);
1035 gfxCriticalError() << "PrintWindow failed";
1041 memcpy(&aReadbackBuffer
[0], readbackBits
, aReadbackBuffer
.length());
1043 ::DeleteObject(bitmap
);
1046 uint32_t latencyMs
= round((TimeStamp::Now() - start
).ToMilliseconds());
1047 if (latencyMs
> 500) {
1048 gfxCriticalNote
<< "Readback took too long: " << latencyMs
<< " ms";
1052 *aNeedsYFlip
= false;
1059 } // namespace mozilla