Bug 1839315: part 4) Link from `SheetLoadData::mWasAlternate` to spec. r=emilio DONTBUILD
[gecko.git] / gfx / webrender_bindings / DCLayerTree.cpp
blobf7cc2b7821a5190de8fdc2765e5c5964005ef0c0
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 "DCLayerTree.h"
9 // -
11 #include <d3d11.h>
12 #include <dcomp.h>
13 #include <d3d11_1.h>
14 #include <dxgi1_2.h>
16 // -
18 #include "gfxWindowsPlatform.h"
19 #include "GLContext.h"
20 #include "GLContextEGL.h"
21 #include "mozilla/gfx/DeviceManagerDx.h"
22 #include "mozilla/gfx/Logging.h"
23 #include "mozilla/gfx/gfxVars.h"
24 #include "mozilla/gfx/GPUParent.h"
25 #include "mozilla/gfx/Matrix.h"
26 #include "mozilla/StaticPrefs_gfx.h"
27 #include "mozilla/webrender/RenderD3D11TextureHost.h"
28 #include "mozilla/webrender/RenderDcompSurfaceTextureHost.h"
29 #include "mozilla/webrender/RenderTextureHost.h"
30 #include "mozilla/webrender/RenderThread.h"
31 #include "mozilla/WindowsVersion.h"
32 #include "mozilla/Telemetry.h"
33 #include "nsPrintfCString.h"
34 #include "WinUtils.h"
36 // -
38 #if defined(__MINGW32__) // 64 defines both 32 and 64
39 // We need to fake some things, while we wait on updates to mingw's dcomp.h
40 // header. Just enough that we can successfully fail to work there.
41 # define MOZ_MINGW_DCOMP_H_INCOMPLETE
42 struct IDCompositionColorMatrixEffect : public IDCompositionFilterEffect {};
43 struct IDCompositionTableTransferEffect : public IDCompositionFilterEffect {};
44 #endif // defined(__MINGW32__)
46 namespace mozilla {
47 namespace wr {
49 extern LazyLogModule gRenderThreadLog;
50 #define LOG(...) MOZ_LOG(gRenderThreadLog, LogLevel::Debug, (__VA_ARGS__))
52 #define LOG_H(msg, ...) \
53 MOZ_LOG(gDcompSurface, LogLevel::Debug, \
54 ("DCSurfaceHandle=%p, " msg, this, ##__VA_ARGS__))
56 UniquePtr<GpuOverlayInfo> DCLayerTree::sGpuOverlayInfo;
58 /* static */
59 UniquePtr<DCLayerTree> DCLayerTree::Create(gl::GLContext* aGL,
60 EGLConfig aEGLConfig,
61 ID3D11Device* aDevice,
62 ID3D11DeviceContext* aCtx,
63 HWND aHwnd, nsACString& aError) {
64 RefPtr<IDCompositionDevice2> dCompDevice =
65 gfx::DeviceManagerDx::Get()->GetDirectCompositionDevice();
66 if (!dCompDevice) {
67 aError.Assign("DCLayerTree(no device)"_ns);
68 return nullptr;
71 auto layerTree =
72 MakeUnique<DCLayerTree>(aGL, aEGLConfig, aDevice, aCtx, dCompDevice);
73 if (!layerTree->Initialize(aHwnd, aError)) {
74 return nullptr;
77 return layerTree;
80 void DCLayerTree::Shutdown() { DCLayerTree::sGpuOverlayInfo = nullptr; }
82 DCLayerTree::DCLayerTree(gl::GLContext* aGL, EGLConfig aEGLConfig,
83 ID3D11Device* aDevice, ID3D11DeviceContext* aCtx,
84 IDCompositionDevice2* aCompositionDevice)
85 : mGL(aGL),
86 mEGLConfig(aEGLConfig),
87 mDevice(aDevice),
88 mCtx(aCtx),
89 mCompositionDevice(aCompositionDevice),
90 mDebugCounter(false),
91 mDebugVisualRedrawRegions(false),
92 mEGLImage(EGL_NO_IMAGE),
93 mColorRBO(0),
94 mPendingCommit(false) {
95 LOG("DCLayerTree::DCLayerTree()");
98 DCLayerTree::~DCLayerTree() {
99 LOG("DCLayerTree::~DCLayerTree()");
101 ReleaseNativeCompositorResources();
104 void DCLayerTree::ReleaseNativeCompositorResources() {
105 const auto gl = GetGLContext();
107 DestroyEGLSurface();
109 // Delete any cached FBO objects
110 for (auto it = mFrameBuffers.begin(); it != mFrameBuffers.end(); ++it) {
111 gl->fDeleteRenderbuffers(1, &it->depthRboId);
112 gl->fDeleteFramebuffers(1, &it->fboId);
116 bool DCLayerTree::Initialize(HWND aHwnd, nsACString& aError) {
117 HRESULT hr;
119 RefPtr<IDCompositionDesktopDevice> desktopDevice;
120 hr = mCompositionDevice->QueryInterface(
121 (IDCompositionDesktopDevice**)getter_AddRefs(desktopDevice));
122 if (FAILED(hr)) {
123 aError.Assign(nsPrintfCString(
124 "DCLayerTree(get IDCompositionDesktopDevice failed %lx)", hr));
125 return false;
128 hr = desktopDevice->CreateTargetForHwnd(aHwnd, TRUE,
129 getter_AddRefs(mCompositionTarget));
130 if (FAILED(hr)) {
131 aError.Assign(nsPrintfCString(
132 "DCLayerTree(create DCompositionTarget failed %lx)", hr));
133 return false;
136 hr = mCompositionDevice->CreateVisual(getter_AddRefs(mRootVisual));
137 if (FAILED(hr)) {
138 aError.Assign(nsPrintfCString(
139 "DCLayerTree(create root DCompositionVisual failed %lx)", hr));
140 return false;
143 hr =
144 mCompositionDevice->CreateVisual(getter_AddRefs(mDefaultSwapChainVisual));
145 if (FAILED(hr)) {
146 aError.Assign(nsPrintfCString(
147 "DCLayerTree(create swap chain DCompositionVisual failed %lx)", hr));
148 return false;
151 if (gfx::gfxVars::UseWebRenderDCompVideoHwOverlayWin() ||
152 gfx::gfxVars::UseWebRenderDCompVideoSwOverlayWin()) {
153 if (!InitializeVideoOverlaySupport()) {
154 RenderThread::Get()->HandleWebRenderError(WebRenderError::VIDEO_OVERLAY);
157 if (!sGpuOverlayInfo) {
158 // Set default if sGpuOverlayInfo was not set.
159 sGpuOverlayInfo = MakeUnique<GpuOverlayInfo>();
162 // Initialize SwapChainInfo
163 SupportsSwapChainTearing();
165 mCompositionTarget->SetRoot(mRootVisual);
166 // Set interporation mode to nearest, to ensure 1:1 sampling.
167 // By default, a visual inherits the interpolation mode of the parent visual.
168 // If no visuals set the interpolation mode, the default for the entire visual
169 // tree is nearest neighbor interpolation.
170 mRootVisual->SetBitmapInterpolationMode(
171 DCOMPOSITION_BITMAP_INTERPOLATION_MODE_NEAREST_NEIGHBOR);
172 return true;
175 bool FlagsSupportsOverlays(UINT flags) {
176 return (flags & (DXGI_OVERLAY_SUPPORT_FLAG_DIRECT |
177 DXGI_OVERLAY_SUPPORT_FLAG_SCALING));
180 // A warpper of IDXGIOutput4::CheckOverlayColorSpaceSupport()
181 bool CheckOverlayColorSpaceSupport(DXGI_FORMAT aDxgiFormat,
182 DXGI_COLOR_SPACE_TYPE aDxgiColorSpace,
183 RefPtr<IDXGIOutput> aOutput,
184 RefPtr<ID3D11Device> aD3d11Device) {
185 UINT colorSpaceSupportFlags = 0;
186 RefPtr<IDXGIOutput4> output4;
188 if (FAILED(aOutput->QueryInterface(__uuidof(IDXGIOutput4),
189 getter_AddRefs(output4)))) {
190 return false;
193 if (FAILED(output4->CheckOverlayColorSpaceSupport(
194 aDxgiFormat, aDxgiColorSpace, aD3d11Device,
195 &colorSpaceSupportFlags))) {
196 return false;
199 return (colorSpaceSupportFlags &
200 DXGI_OVERLAY_COLOR_SPACE_SUPPORT_FLAG_PRESENT);
203 bool DCLayerTree::InitializeVideoOverlaySupport() {
204 MOZ_ASSERT(IsWin10AnniversaryUpdateOrLater());
206 HRESULT hr;
208 hr = mDevice->QueryInterface(
209 (ID3D11VideoDevice**)getter_AddRefs(mVideoDevice));
210 if (FAILED(hr)) {
211 gfxCriticalNote << "Failed to get D3D11VideoDevice: " << gfx::hexa(hr);
212 return false;
215 hr =
216 mCtx->QueryInterface((ID3D11VideoContext**)getter_AddRefs(mVideoContext));
217 if (FAILED(hr)) {
218 gfxCriticalNote << "Failed to get D3D11VideoContext: " << gfx::hexa(hr);
219 return false;
222 if (sGpuOverlayInfo) {
223 return true;
226 UniquePtr<GpuOverlayInfo> info = MakeUnique<GpuOverlayInfo>();
228 RefPtr<IDXGIDevice> dxgiDevice;
229 RefPtr<IDXGIAdapter> adapter;
230 mDevice->QueryInterface((IDXGIDevice**)getter_AddRefs(dxgiDevice));
231 dxgiDevice->GetAdapter(getter_AddRefs(adapter));
233 unsigned int i = 0;
234 while (true) {
235 RefPtr<IDXGIOutput> output;
236 if (FAILED(adapter->EnumOutputs(i++, getter_AddRefs(output)))) {
237 break;
239 RefPtr<IDXGIOutput3> output3;
240 if (FAILED(output->QueryInterface(__uuidof(IDXGIOutput3),
241 getter_AddRefs(output3)))) {
242 break;
245 output3->CheckOverlaySupport(DXGI_FORMAT_NV12, mDevice,
246 &info->mNv12OverlaySupportFlags);
247 output3->CheckOverlaySupport(DXGI_FORMAT_YUY2, mDevice,
248 &info->mYuy2OverlaySupportFlags);
249 output3->CheckOverlaySupport(DXGI_FORMAT_B8G8R8A8_UNORM, mDevice,
250 &info->mBgra8OverlaySupportFlags);
251 output3->CheckOverlaySupport(DXGI_FORMAT_R10G10B10A2_UNORM, mDevice,
252 &info->mRgb10a2OverlaySupportFlags);
254 if (FlagsSupportsOverlays(info->mNv12OverlaySupportFlags)) {
255 // NV12 format is preferred if it's supported.
256 info->mOverlayFormatUsed = DXGI_FORMAT_NV12;
257 info->mSupportsHardwareOverlays = true;
260 if (!info->mSupportsHardwareOverlays &&
261 FlagsSupportsOverlays(info->mYuy2OverlaySupportFlags)) {
262 // If NV12 isn't supported, fallback to YUY2 if it's supported.
263 info->mOverlayFormatUsed = DXGI_FORMAT_YUY2;
264 info->mSupportsHardwareOverlays = true;
267 // RGB10A2 overlay is used for displaying HDR content. In Intel's
268 // platform, RGB10A2 overlay is enabled only when
269 // DXGI_COLOR_SPACE_RGB_FULL_G2084_NONE_P2020 is supported.
270 if (FlagsSupportsOverlays(info->mRgb10a2OverlaySupportFlags)) {
271 if (!CheckOverlayColorSpaceSupport(
272 DXGI_FORMAT_R10G10B10A2_UNORM,
273 DXGI_COLOR_SPACE_RGB_FULL_G2084_NONE_P2020, output, mDevice))
274 info->mRgb10a2OverlaySupportFlags = 0;
277 // Early out after the first output that reports overlay support. All
278 // outputs are expected to report the same overlay support according to
279 // Microsoft's WDDM documentation:
280 // https://docs.microsoft.com/en-us/windows-hardware/drivers/display/multiplane-overlay-hardware-requirements
281 if (info->mSupportsHardwareOverlays) {
282 break;
286 if (!StaticPrefs::gfx_webrender_dcomp_video_yuv_overlay_win_AtStartup()) {
287 info->mOverlayFormatUsed = DXGI_FORMAT_B8G8R8A8_UNORM;
288 info->mSupportsHardwareOverlays = false;
291 info->mSupportsOverlays = info->mSupportsHardwareOverlays;
293 sGpuOverlayInfo = std::move(info);
295 if (auto* gpuParent = gfx::GPUParent::GetSingleton()) {
296 gpuParent->NotifyOverlayInfo(GetOverlayInfo());
299 return true;
302 DCSurface* DCLayerTree::GetSurface(wr::NativeSurfaceId aId) const {
303 auto surface_it = mDCSurfaces.find(aId);
304 MOZ_RELEASE_ASSERT(surface_it != mDCSurfaces.end());
305 return surface_it->second.get();
308 void DCLayerTree::SetDefaultSwapChain(IDXGISwapChain1* aSwapChain) {
309 LOG("DCLayerTree::SetDefaultSwapChain()");
311 mRootVisual->AddVisual(mDefaultSwapChainVisual, TRUE, nullptr);
312 mDefaultSwapChainVisual->SetContent(aSwapChain);
313 // Default SwapChain's visual does not need linear interporation.
314 mDefaultSwapChainVisual->SetBitmapInterpolationMode(
315 DCOMPOSITION_BITMAP_INTERPOLATION_MODE_NEAREST_NEIGHBOR);
316 mPendingCommit = true;
319 void DCLayerTree::MaybeUpdateDebug() {
320 bool updated = false;
321 updated |= MaybeUpdateDebugCounter();
322 updated |= MaybeUpdateDebugVisualRedrawRegions();
323 if (updated) {
324 mPendingCommit = true;
328 void DCLayerTree::MaybeCommit() {
329 if (!mPendingCommit) {
330 return;
332 mCompositionDevice->Commit();
335 void DCLayerTree::WaitForCommitCompletion() {
336 mCompositionDevice->WaitForCommitCompletion();
339 void DCLayerTree::DisableNativeCompositor() {
340 MOZ_ASSERT(mCurrentSurface.isNothing());
341 MOZ_ASSERT(mCurrentLayers.empty());
343 ReleaseNativeCompositorResources();
344 mPrevLayers.clear();
345 mRootVisual->RemoveAllVisuals();
348 bool DCLayerTree::MaybeUpdateDebugCounter() {
349 bool debugCounter = StaticPrefs::gfx_webrender_debug_dcomp_counter();
350 if (mDebugCounter == debugCounter) {
351 return false;
354 RefPtr<IDCompositionDeviceDebug> debugDevice;
355 HRESULT hr = mCompositionDevice->QueryInterface(
356 (IDCompositionDeviceDebug**)getter_AddRefs(debugDevice));
357 if (FAILED(hr)) {
358 return false;
361 if (debugCounter) {
362 debugDevice->EnableDebugCounters();
363 } else {
364 debugDevice->DisableDebugCounters();
367 mDebugCounter = debugCounter;
368 return true;
371 bool DCLayerTree::MaybeUpdateDebugVisualRedrawRegions() {
372 bool debugVisualRedrawRegions =
373 StaticPrefs::gfx_webrender_debug_dcomp_redraw_regions();
374 if (mDebugVisualRedrawRegions == debugVisualRedrawRegions) {
375 return false;
378 RefPtr<IDCompositionVisualDebug> visualDebug;
379 HRESULT hr = mRootVisual->QueryInterface(
380 (IDCompositionVisualDebug**)getter_AddRefs(visualDebug));
381 if (FAILED(hr)) {
382 return false;
385 if (debugVisualRedrawRegions) {
386 visualDebug->EnableRedrawRegions();
387 } else {
388 visualDebug->DisableRedrawRegions();
391 mDebugVisualRedrawRegions = debugVisualRedrawRegions;
392 return true;
395 void DCLayerTree::CompositorBeginFrame() {
396 mCurrentFrame++;
397 mUsedOverlayTypesInFrame = DCompOverlayTypes::NO_OVERLAY;
400 void DCLayerTree::CompositorEndFrame() {
401 auto start = TimeStamp::Now();
402 // Check if the visual tree of surfaces is the same as last frame.
403 bool same = mPrevLayers == mCurrentLayers;
405 if (!same) {
406 // If not, we need to rebuild the visual tree. Note that addition or
407 // removal of tiles no longer needs to rebuild the main visual tree
408 // here, since they are added as children of the surface visual.
409 mRootVisual->RemoveAllVisuals();
412 for (auto it = mCurrentLayers.begin(); it != mCurrentLayers.end(); ++it) {
413 auto surface_it = mDCSurfaces.find(*it);
414 MOZ_RELEASE_ASSERT(surface_it != mDCSurfaces.end());
415 const auto surface = surface_it->second.get();
416 // Ensure surface is trimmed to updated tile valid rects
417 surface->UpdateAllocatedRect();
418 if (!same) {
419 // Add surfaces in z-order they were added to the scene.
420 const auto visual = surface->GetVisual();
421 mRootVisual->AddVisual(visual, false, nullptr);
425 mPrevLayers.swap(mCurrentLayers);
426 mCurrentLayers.clear();
428 mCompositionDevice->Commit();
430 auto end = TimeStamp::Now();
431 mozilla::Telemetry::Accumulate(mozilla::Telemetry::COMPOSITE_SWAP_TIME,
432 (end - start).ToMilliseconds() * 10.);
434 // Remove any framebuffers that haven't been
435 // used in the last 60 frames.
437 // This should use nsTArray::RemoveElementsBy once
438 // CachedFrameBuffer is able to properly destroy
439 // itself in the destructor.
440 const auto gl = GetGLContext();
441 for (uint32_t i = 0, len = mFrameBuffers.Length(); i < len; ++i) {
442 auto& fb = mFrameBuffers[i];
443 if ((mCurrentFrame - fb.lastFrameUsed) > 60) {
444 gl->fDeleteRenderbuffers(1, &fb.depthRboId);
445 gl->fDeleteFramebuffers(1, &fb.fboId);
446 mFrameBuffers.UnorderedRemoveElementAt(i);
447 --i; // Examine the element again, if necessary.
448 --len;
452 if (!StaticPrefs::gfx_webrender_dcomp_video_check_slow_present()) {
453 return;
456 // Disable video overlay if mCompositionDevice->Commit() with video overlay is
457 // too slow. It drops fps.
459 const auto maxCommitWaitDurationMs = 20;
460 const auto maxSlowCommitCount = 5;
461 const auto commitDurationMs =
462 static_cast<uint32_t>((end - start).ToMilliseconds());
464 nsPrintfCString marker("CommitWait overlay %u %ums ",
465 (uint8_t)mUsedOverlayTypesInFrame, commitDurationMs);
466 PROFILER_MARKER_TEXT("CommitWait", GRAPHICS, {}, marker);
468 if (mUsedOverlayTypesInFrame != DCompOverlayTypes::NO_OVERLAY &&
469 commitDurationMs > maxCommitWaitDurationMs) {
470 mSlowCommitCount++;
471 } else {
472 mSlowCommitCount = 0;
475 if (mSlowCommitCount <= maxSlowCommitCount) {
476 return;
479 if (mUsedOverlayTypesInFrame & DCompOverlayTypes::SOFTWARE_DECODED_VIDEO) {
480 gfxCriticalNoteOnce << "Sw video swapchain present is slow";
481 RenderThread::Get()->NotifyWebRenderError(
482 wr::WebRenderError::VIDEO_SW_OVERLAY);
484 if (mUsedOverlayTypesInFrame & DCompOverlayTypes::HARDWARE_DECODED_VIDEO) {
485 gfxCriticalNoteOnce << "Hw video swapchain present is slow";
486 RenderThread::Get()->NotifyWebRenderError(
487 wr::WebRenderError::VIDEO_HW_OVERLAY);
491 void DCLayerTree::Bind(wr::NativeTileId aId, wr::DeviceIntPoint* aOffset,
492 uint32_t* aFboId, wr::DeviceIntRect aDirtyRect,
493 wr::DeviceIntRect aValidRect) {
494 auto surface = GetSurface(aId.surface_id);
495 auto tile = surface->GetTile(aId.x, aId.y);
496 wr::DeviceIntPoint targetOffset{0, 0};
498 // If tile owns an IDCompositionSurface we use it, otherwise we're using an
499 // IDCompositionVirtualSurface owned by the DCSurface.
500 RefPtr<IDCompositionSurface> compositionSurface;
501 if (surface->mIsVirtualSurface) {
502 gfx::IntRect validRect(aValidRect.min.x, aValidRect.min.y,
503 aValidRect.width(), aValidRect.height());
504 if (!tile->mValidRect.IsEqualEdges(validRect)) {
505 tile->mValidRect = validRect;
506 surface->DirtyAllocatedRect();
508 wr::DeviceIntSize tileSize = surface->GetTileSize();
509 compositionSurface = surface->GetCompositionSurface();
510 wr::DeviceIntPoint virtualOffset = surface->GetVirtualOffset();
511 targetOffset.x = virtualOffset.x + tileSize.width * aId.x;
512 targetOffset.y = virtualOffset.y + tileSize.height * aId.y;
513 } else {
514 compositionSurface = tile->Bind(aValidRect);
517 if (tile->mNeedsFullDraw) {
518 // dcomp requires that the first BeginDraw on a non-virtual surface is the
519 // full size of the pixel buffer.
520 auto tileSize = surface->GetTileSize();
521 aDirtyRect.min.x = 0;
522 aDirtyRect.min.y = 0;
523 aDirtyRect.max.x = tileSize.width;
524 aDirtyRect.max.y = tileSize.height;
525 tile->mNeedsFullDraw = false;
528 *aFboId = CreateEGLSurfaceForCompositionSurface(
529 aDirtyRect, aOffset, compositionSurface, targetOffset);
530 mCurrentSurface = Some(compositionSurface);
533 void DCLayerTree::Unbind() {
534 if (mCurrentSurface.isNothing()) {
535 return;
538 RefPtr<IDCompositionSurface> surface = mCurrentSurface.ref();
539 surface->EndDraw();
541 DestroyEGLSurface();
542 mCurrentSurface = Nothing();
545 void DCLayerTree::CreateSurface(wr::NativeSurfaceId aId,
546 wr::DeviceIntPoint aVirtualOffset,
547 wr::DeviceIntSize aTileSize, bool aIsOpaque) {
548 auto it = mDCSurfaces.find(aId);
549 MOZ_RELEASE_ASSERT(it == mDCSurfaces.end());
550 if (it != mDCSurfaces.end()) {
551 // DCSurface already exists.
552 return;
555 // Tile size needs to be positive.
556 if (aTileSize.width <= 0 || aTileSize.height <= 0) {
557 gfxCriticalNote << "TileSize is not positive aId: " << wr::AsUint64(aId)
558 << " aTileSize(" << aTileSize.width << ","
559 << aTileSize.height << ")";
562 bool isVirtualSurface =
563 StaticPrefs::gfx_webrender_dcomp_use_virtual_surfaces_AtStartup();
564 auto surface = MakeUnique<DCSurface>(aTileSize, aVirtualOffset,
565 isVirtualSurface, aIsOpaque, this);
566 if (!surface->Initialize()) {
567 gfxCriticalNote << "Failed to initialize DCSurface: " << wr::AsUint64(aId);
568 return;
571 mDCSurfaces[aId] = std::move(surface);
574 void DCLayerTree::CreateExternalSurface(wr::NativeSurfaceId aId,
575 bool aIsOpaque) {
576 auto it = mDCSurfaces.find(aId);
577 MOZ_RELEASE_ASSERT(it == mDCSurfaces.end());
579 auto surface = MakeUnique<DCExternalSurfaceWrapper>(aIsOpaque, this);
580 if (!surface->Initialize()) {
581 gfxCriticalNote << "Failed to initialize DCExternalSurfaceWrapper: "
582 << wr::AsUint64(aId);
583 return;
586 mDCSurfaces[aId] = std::move(surface);
589 void DCLayerTree::DestroySurface(NativeSurfaceId aId) {
590 auto surface_it = mDCSurfaces.find(aId);
591 MOZ_RELEASE_ASSERT(surface_it != mDCSurfaces.end());
592 auto surface = surface_it->second.get();
594 mRootVisual->RemoveVisual(surface->GetVisual());
595 mDCSurfaces.erase(surface_it);
598 void DCLayerTree::CreateTile(wr::NativeSurfaceId aId, int32_t aX, int32_t aY) {
599 auto surface = GetSurface(aId);
600 surface->CreateTile(aX, aY);
603 void DCLayerTree::DestroyTile(wr::NativeSurfaceId aId, int32_t aX, int32_t aY) {
604 auto surface = GetSurface(aId);
605 surface->DestroyTile(aX, aY);
608 void DCLayerTree::AttachExternalImage(wr::NativeSurfaceId aId,
609 wr::ExternalImageId aExternalImage) {
610 auto surface_it = mDCSurfaces.find(aId);
611 MOZ_RELEASE_ASSERT(surface_it != mDCSurfaces.end());
612 surface_it->second->AttachExternalImage(aExternalImage);
615 void DCExternalSurfaceWrapper::AttachExternalImage(
616 wr::ExternalImageId aExternalImage) {
617 if (auto* surface = EnsureSurfaceForExternalImage(aExternalImage)) {
618 surface->AttachExternalImage(aExternalImage);
622 template <class ToT>
623 struct QI {
624 template <class FromT>
625 [[nodiscard]] static inline RefPtr<ToT> From(FromT* const from) {
626 RefPtr<ToT> to;
627 (void)from->QueryInterface(static_cast<ToT**>(getter_AddRefs(to)));
628 return to;
632 DCSurface* DCExternalSurfaceWrapper::EnsureSurfaceForExternalImage(
633 wr::ExternalImageId aExternalImage) {
634 if (mSurface) {
635 return mSurface.get();
638 // Create a new surface based on the texture type.
639 RenderTextureHost* texture =
640 RenderThread::Get()->GetRenderTexture(aExternalImage);
641 if (texture && texture->AsRenderDXGITextureHost()) {
642 mSurface.reset(new DCSurfaceVideo(mIsOpaque, mDCLayerTree));
643 if (!mSurface->Initialize()) {
644 gfxCriticalNote << "Failed to initialize DCSurfaceVideo: "
645 << wr::AsUint64(aExternalImage);
646 mSurface = nullptr;
648 } else if (texture && texture->AsRenderDcompSurfaceTextureHost()) {
649 mSurface.reset(new DCSurfaceHandle(mIsOpaque, mDCLayerTree));
650 if (!mSurface->Initialize()) {
651 gfxCriticalNote << "Failed to initialize DCSurfaceHandle: "
652 << wr::AsUint64(aExternalImage);
653 mSurface = nullptr;
656 if (!mSurface) {
657 gfxCriticalNote << "Failed to create a surface for external image: "
658 << gfx::hexa(texture);
659 return nullptr;
662 // Add surface's visual which will contain video data to our root visual.
663 const auto surfaceVisual = mSurface->GetVisual();
664 mVisual->AddVisual(surfaceVisual, true, nullptr);
666 // -
667 // Apply color management.
669 [&]() {
670 const auto cmsMode = GfxColorManagementMode();
671 if (cmsMode == CMSMode::Off) return;
673 const auto dcomp = mDCLayerTree->GetCompositionDevice();
674 const auto dcomp3 = QI<IDCompositionDevice3>::From(dcomp);
675 if (!dcomp3) {
676 NS_WARNING(
677 "No IDCompositionDevice3, cannot use dcomp for color management.");
678 return;
681 // -
683 const auto cspace = [&]() {
684 const auto rangedCspace = texture->GetYUVColorSpace();
685 const auto info = FromYUVRangedColorSpace(rangedCspace);
686 auto ret = ToColorSpace2(info.space);
687 if (ret == gfx::ColorSpace2::Display && cmsMode == CMSMode::All) {
688 ret = gfx::ColorSpace2::SRGB;
690 return ret;
691 }();
693 const bool rec709GammaAsSrgb =
694 StaticPrefs::gfx_color_management_rec709_gamma_as_srgb();
695 const bool rec2020GammaAsRec709 =
696 StaticPrefs::gfx_color_management_rec2020_gamma_as_rec709();
698 auto cspaceDesc = color::ColorspaceDesc{};
699 switch (cspace) {
700 case gfx::ColorSpace2::Display:
701 return; // No color management needed!
702 case gfx::ColorSpace2::SRGB:
703 cspaceDesc.chrom = color::Chromaticities::Srgb();
704 cspaceDesc.tf = color::PiecewiseGammaDesc::Srgb();
705 break;
707 case gfx::ColorSpace2::DISPLAY_P3:
708 cspaceDesc.chrom = color::Chromaticities::DisplayP3();
709 cspaceDesc.tf = color::PiecewiseGammaDesc::DisplayP3();
710 break;
712 case gfx::ColorSpace2::BT601_525:
713 cspaceDesc.chrom = color::Chromaticities::Rec601_525_Ntsc();
714 if (rec709GammaAsSrgb) {
715 cspaceDesc.tf = color::PiecewiseGammaDesc::Srgb();
716 } else {
717 cspaceDesc.tf = color::PiecewiseGammaDesc::Rec709();
719 break;
721 case gfx::ColorSpace2::BT709:
722 cspaceDesc.chrom = color::Chromaticities::Rec709();
723 if (rec709GammaAsSrgb) {
724 cspaceDesc.tf = color::PiecewiseGammaDesc::Srgb();
725 } else {
726 cspaceDesc.tf = color::PiecewiseGammaDesc::Rec709();
728 break;
730 case gfx::ColorSpace2::BT2020:
731 cspaceDesc.chrom = color::Chromaticities::Rec2020();
732 if (rec2020GammaAsRec709 && rec709GammaAsSrgb) {
733 cspaceDesc.tf = color::PiecewiseGammaDesc::Srgb();
734 } else if (rec2020GammaAsRec709) {
735 cspaceDesc.tf = color::PiecewiseGammaDesc::Rec709();
736 } else {
737 // Just Rec709 with slightly more precision.
738 cspaceDesc.tf = color::PiecewiseGammaDesc::Rec2020_12bit();
740 break;
743 const auto cprofileIn = color::ColorProfileDesc::From(cspaceDesc);
744 auto cprofileOut = mDCLayerTree->OutputColorProfile();
745 bool pretendSrgb = true;
746 if (pretendSrgb) {
747 cprofileOut = color::ColorProfileDesc::From({
748 color::Chromaticities::Srgb(),
749 color::PiecewiseGammaDesc::Srgb(),
752 const auto conversion = color::ColorProfileConversionDesc::From({
753 .src = cprofileIn,
754 .dst = cprofileOut,
757 // -
759 auto chain = ColorManagementChain::From(*dcomp3, conversion);
760 mCManageChain = Some(chain);
762 surfaceVisual->SetEffect(mCManageChain->last.get());
763 }();
765 return mSurface.get();
768 void DCExternalSurfaceWrapper::PresentExternalSurface(gfx::Matrix& aTransform) {
769 MOZ_ASSERT(mSurface);
770 if (auto* surface = mSurface->AsDCSurfaceVideo()) {
771 if (surface->CalculateSwapChainSize(aTransform)) {
772 surface->PresentVideo();
774 } else if (auto* surface = mSurface->AsDCSurfaceHandle()) {
775 surface->PresentSurfaceHandle();
779 template <typename T>
780 static inline D2D1_RECT_F D2DRect(const T& aRect) {
781 return D2D1::RectF(aRect.X(), aRect.Y(), aRect.XMost(), aRect.YMost());
784 static inline D2D1_MATRIX_3X2_F D2DMatrix(const gfx::Matrix& aTransform) {
785 return D2D1::Matrix3x2F(aTransform._11, aTransform._12, aTransform._21,
786 aTransform._22, aTransform._31, aTransform._32);
789 void DCLayerTree::AddSurface(wr::NativeSurfaceId aId,
790 const wr::CompositorSurfaceTransform& aTransform,
791 wr::DeviceIntRect aClipRect,
792 wr::ImageRendering aImageRendering) {
793 auto it = mDCSurfaces.find(aId);
794 MOZ_RELEASE_ASSERT(it != mDCSurfaces.end());
795 const auto surface = it->second.get();
796 const auto visual = surface->GetVisual();
798 wr::DeviceIntPoint virtualOffset = surface->GetVirtualOffset();
800 float sx = aTransform.scale.x;
801 float sy = aTransform.scale.y;
802 float tx = aTransform.offset.x;
803 float ty = aTransform.offset.y;
804 gfx::Matrix transform(sx, 0.0, 0.0, sy, tx, ty);
806 surface->PresentExternalSurface(transform);
808 transform.PreTranslate(-virtualOffset.x, -virtualOffset.y);
810 // The DirectComposition API applies clipping *before* any
811 // transforms/offset, whereas we want the clip applied after. Right now, we
812 // only support rectilinear transforms, and then we transform our clip into
813 // pre-transform coordinate space for it to be applied there.
814 // DirectComposition does have an option for pre-transform clipping, if you
815 // create an explicit IDCompositionEffectGroup object and set a 3D transform
816 // on that. I suspect that will perform worse though, so we should only do
817 // that for complex transforms (which are never provided right now).
818 MOZ_ASSERT(transform.IsRectilinear());
819 gfx::Rect clip = transform.Inverse().TransformBounds(gfx::Rect(
820 aClipRect.min.x, aClipRect.min.y, aClipRect.width(), aClipRect.height()));
821 // Set the clip rect - converting from world space to the pre-offset space
822 // that DC requires for rectangle clips.
823 visual->SetClip(D2DRect(clip));
825 // TODO: The input matrix is a 4x4, but we only support a 3x2 at
826 // the D3D API level (unless we QI to IDCompositionVisual3, which might
827 // not be available?).
828 // Should we assert here, or restrict at the WR API level.
829 visual->SetTransform(D2DMatrix(transform));
831 if (aImageRendering == wr::ImageRendering::Auto) {
832 visual->SetBitmapInterpolationMode(
833 DCOMPOSITION_BITMAP_INTERPOLATION_MODE_LINEAR);
834 } else {
835 visual->SetBitmapInterpolationMode(
836 DCOMPOSITION_BITMAP_INTERPOLATION_MODE_NEAREST_NEIGHBOR);
839 mCurrentLayers.push_back(aId);
842 GLuint DCLayerTree::GetOrCreateFbo(int aWidth, int aHeight) {
843 const auto gl = GetGLContext();
844 GLuint fboId = 0;
846 // Check if we have a cached FBO with matching dimensions
847 for (auto it = mFrameBuffers.begin(); it != mFrameBuffers.end(); ++it) {
848 if (it->width == aWidth && it->height == aHeight) {
849 fboId = it->fboId;
850 it->lastFrameUsed = mCurrentFrame;
851 break;
855 // If not, create a new FBO with attached depth buffer
856 if (fboId == 0) {
857 // Create the depth buffer
858 GLuint depthRboId;
859 gl->fGenRenderbuffers(1, &depthRboId);
860 gl->fBindRenderbuffer(LOCAL_GL_RENDERBUFFER, depthRboId);
861 gl->fRenderbufferStorage(LOCAL_GL_RENDERBUFFER, LOCAL_GL_DEPTH_COMPONENT24,
862 aWidth, aHeight);
864 // Create the framebuffer and attach the depth buffer to it
865 gl->fGenFramebuffers(1, &fboId);
866 gl->fBindFramebuffer(LOCAL_GL_DRAW_FRAMEBUFFER, fboId);
867 gl->fFramebufferRenderbuffer(LOCAL_GL_DRAW_FRAMEBUFFER,
868 LOCAL_GL_DEPTH_ATTACHMENT,
869 LOCAL_GL_RENDERBUFFER, depthRboId);
871 // Store this in the cache for future calls.
872 // TODO(gw): Maybe we should periodically scan this list and remove old
873 // entries that
874 // haven't been used for some time?
875 DCLayerTree::CachedFrameBuffer frame_buffer_info;
876 frame_buffer_info.width = aWidth;
877 frame_buffer_info.height = aHeight;
878 frame_buffer_info.fboId = fboId;
879 frame_buffer_info.depthRboId = depthRboId;
880 frame_buffer_info.lastFrameUsed = mCurrentFrame;
881 mFrameBuffers.AppendElement(frame_buffer_info);
884 return fboId;
887 bool DCLayerTree::EnsureVideoProcessor(const gfx::IntSize& aInputSize,
888 const gfx::IntSize& aOutputSize) {
889 HRESULT hr;
891 if (!mVideoDevice || !mVideoContext) {
892 return false;
895 if (mVideoProcessor && (aInputSize <= mVideoInputSize) &&
896 (aOutputSize <= mVideoOutputSize)) {
897 return true;
900 mVideoProcessor = nullptr;
901 mVideoProcessorEnumerator = nullptr;
903 D3D11_VIDEO_PROCESSOR_CONTENT_DESC desc = {};
904 desc.InputFrameFormat = D3D11_VIDEO_FRAME_FORMAT_PROGRESSIVE;
905 desc.InputFrameRate.Numerator = 60;
906 desc.InputFrameRate.Denominator = 1;
907 desc.InputWidth = aInputSize.width;
908 desc.InputHeight = aInputSize.height;
909 desc.OutputFrameRate.Numerator = 60;
910 desc.OutputFrameRate.Denominator = 1;
911 desc.OutputWidth = aOutputSize.width;
912 desc.OutputHeight = aOutputSize.height;
913 desc.Usage = D3D11_VIDEO_USAGE_PLAYBACK_NORMAL;
915 hr = mVideoDevice->CreateVideoProcessorEnumerator(
916 &desc, getter_AddRefs(mVideoProcessorEnumerator));
917 if (FAILED(hr)) {
918 gfxCriticalNote << "Failed to create VideoProcessorEnumerator: "
919 << gfx::hexa(hr);
920 return false;
923 hr = mVideoDevice->CreateVideoProcessor(mVideoProcessorEnumerator, 0,
924 getter_AddRefs(mVideoProcessor));
925 if (FAILED(hr)) {
926 mVideoProcessor = nullptr;
927 mVideoProcessorEnumerator = nullptr;
928 gfxCriticalNote << "Failed to create VideoProcessor: " << gfx::hexa(hr);
929 return false;
932 // Reduce power cosumption
933 // By default, the driver might perform certain processing tasks
934 // automatically
935 mVideoContext->VideoProcessorSetStreamAutoProcessingMode(mVideoProcessor, 0,
936 FALSE);
938 mVideoInputSize = aInputSize;
939 mVideoOutputSize = aOutputSize;
941 return true;
944 bool DCLayerTree::SupportsHardwareOverlays() {
945 return sGpuOverlayInfo->mSupportsHardwareOverlays;
948 bool DCLayerTree::SupportsSwapChainTearing() {
949 RefPtr<ID3D11Device> device = mDevice;
950 static const bool supported = [device] {
951 RefPtr<IDXGIDevice> dxgiDevice;
952 RefPtr<IDXGIAdapter> adapter;
953 device->QueryInterface((IDXGIDevice**)getter_AddRefs(dxgiDevice));
954 dxgiDevice->GetAdapter(getter_AddRefs(adapter));
956 RefPtr<IDXGIFactory5> dxgiFactory;
957 HRESULT hr = adapter->GetParent(
958 IID_PPV_ARGS((IDXGIFactory5**)getter_AddRefs(dxgiFactory)));
959 if (FAILED(hr)) {
960 return false;
963 BOOL presentAllowTearing = FALSE;
964 hr = dxgiFactory->CheckFeatureSupport(DXGI_FEATURE_PRESENT_ALLOW_TEARING,
965 &presentAllowTearing,
966 sizeof(presentAllowTearing));
967 if (FAILED(hr)) {
968 return false;
971 if (auto* gpuParent = gfx::GPUParent::GetSingleton()) {
972 gpuParent->NotifySwapChainInfo(
973 layers::SwapChainInfo(!!presentAllowTearing));
974 } else if (XRE_IsParentProcess()) {
975 MOZ_ASSERT_UNREACHABLE("unexpected to be called");
977 return !!presentAllowTearing;
978 }();
979 return supported;
982 DXGI_FORMAT DCLayerTree::GetOverlayFormatForSDR() {
983 return sGpuOverlayInfo->mOverlayFormatUsed;
986 static layers::OverlaySupportType FlagsToOverlaySupportType(
987 UINT aFlags, bool aSoftwareOverlaySupported) {
988 if (aFlags & DXGI_OVERLAY_SUPPORT_FLAG_SCALING) {
989 return layers::OverlaySupportType::Scaling;
991 if (aFlags & DXGI_OVERLAY_SUPPORT_FLAG_DIRECT) {
992 return layers::OverlaySupportType::Direct;
994 if (aSoftwareOverlaySupported) {
995 return layers::OverlaySupportType::Software;
997 return layers::OverlaySupportType::None;
1000 layers::OverlayInfo DCLayerTree::GetOverlayInfo() {
1001 layers::OverlayInfo info;
1003 info.mSupportsOverlays = sGpuOverlayInfo->mSupportsHardwareOverlays;
1004 info.mNv12Overlay =
1005 FlagsToOverlaySupportType(sGpuOverlayInfo->mNv12OverlaySupportFlags,
1006 /* aSoftwareOverlaySupported */ false);
1007 info.mYuy2Overlay =
1008 FlagsToOverlaySupportType(sGpuOverlayInfo->mYuy2OverlaySupportFlags,
1009 /* aSoftwareOverlaySupported */ false);
1010 info.mBgra8Overlay =
1011 FlagsToOverlaySupportType(sGpuOverlayInfo->mBgra8OverlaySupportFlags,
1012 /* aSoftwareOverlaySupported */ true);
1013 info.mRgb10a2Overlay =
1014 FlagsToOverlaySupportType(sGpuOverlayInfo->mRgb10a2OverlaySupportFlags,
1015 /* aSoftwareOverlaySupported */ false);
1017 return info;
1020 void DCLayerTree::SetUsedOverlayTypeInFrame(DCompOverlayTypes aTypes) {
1021 mUsedOverlayTypesInFrame |= aTypes;
1024 DCSurface::DCSurface(wr::DeviceIntSize aTileSize,
1025 wr::DeviceIntPoint aVirtualOffset, bool aIsVirtualSurface,
1026 bool aIsOpaque, DCLayerTree* aDCLayerTree)
1027 : mIsVirtualSurface(aIsVirtualSurface),
1028 mDCLayerTree(aDCLayerTree),
1029 mTileSize(aTileSize),
1030 mIsOpaque(aIsOpaque),
1031 mAllocatedRectDirty(true),
1032 mVirtualOffset(aVirtualOffset) {}
1034 DCSurface::~DCSurface() {}
1036 bool DCSurface::Initialize() {
1037 // Create a visual for tiles to attach to, whether virtual or not.
1038 HRESULT hr;
1039 const auto dCompDevice = mDCLayerTree->GetCompositionDevice();
1040 hr = dCompDevice->CreateVisual(getter_AddRefs(mVisual));
1041 if (FAILED(hr)) {
1042 gfxCriticalNote << "Failed to create DCompositionVisual: " << gfx::hexa(hr);
1043 return false;
1046 // If virtual surface is enabled, create and attach to visual, in this case
1047 // the tiles won't own visuals or surfaces.
1048 if (mIsVirtualSurface) {
1049 DXGI_ALPHA_MODE alpha_mode =
1050 mIsOpaque ? DXGI_ALPHA_MODE_IGNORE : DXGI_ALPHA_MODE_PREMULTIPLIED;
1052 hr = dCompDevice->CreateVirtualSurface(
1053 VIRTUAL_SURFACE_SIZE, VIRTUAL_SURFACE_SIZE, DXGI_FORMAT_R8G8B8A8_UNORM,
1054 alpha_mode, getter_AddRefs(mVirtualSurface));
1055 MOZ_ASSERT(SUCCEEDED(hr));
1057 // Bind the surface memory to this visual
1058 hr = mVisual->SetContent(mVirtualSurface);
1059 MOZ_ASSERT(SUCCEEDED(hr));
1062 return true;
1065 void DCSurface::CreateTile(int32_t aX, int32_t aY) {
1066 TileKey key(aX, aY);
1067 MOZ_RELEASE_ASSERT(mDCTiles.find(key) == mDCTiles.end());
1069 auto tile = MakeUnique<DCTile>(mDCLayerTree);
1070 if (!tile->Initialize(aX, aY, mTileSize, mIsVirtualSurface, mIsOpaque,
1071 mVisual)) {
1072 gfxCriticalNote << "Failed to initialize DCTile: " << aX << aY;
1073 return;
1076 if (mIsVirtualSurface) {
1077 mAllocatedRectDirty = true;
1078 } else {
1079 mVisual->AddVisual(tile->GetVisual(), false, nullptr);
1082 mDCTiles[key] = std::move(tile);
1085 void DCSurface::DestroyTile(int32_t aX, int32_t aY) {
1086 TileKey key(aX, aY);
1087 if (mIsVirtualSurface) {
1088 mAllocatedRectDirty = true;
1089 } else {
1090 auto tile = GetTile(aX, aY);
1091 mVisual->RemoveVisual(tile->GetVisual());
1093 mDCTiles.erase(key);
1096 void DCSurface::DirtyAllocatedRect() { mAllocatedRectDirty = true; }
1098 void DCSurface::UpdateAllocatedRect() {
1099 if (mAllocatedRectDirty) {
1100 if (mVirtualSurface) {
1101 // The virtual surface may have holes in it (for example, an empty tile
1102 // that has no primitives). Instead of trimming to a single bounding
1103 // rect, supply the rect of each valid tile to handle this case.
1104 std::vector<RECT> validRects;
1106 for (auto it = mDCTiles.begin(); it != mDCTiles.end(); ++it) {
1107 auto tile = GetTile(it->first.mX, it->first.mY);
1108 RECT rect;
1110 rect.left = (LONG)(mVirtualOffset.x + it->first.mX * mTileSize.width +
1111 tile->mValidRect.x);
1112 rect.top = (LONG)(mVirtualOffset.y + it->first.mY * mTileSize.height +
1113 tile->mValidRect.y);
1114 rect.right = rect.left + tile->mValidRect.width;
1115 rect.bottom = rect.top + tile->mValidRect.height;
1117 validRects.push_back(rect);
1120 mVirtualSurface->Trim(validRects.data(), validRects.size());
1122 // When not using a virtual surface, we still want to reset this
1123 mAllocatedRectDirty = false;
1127 DCTile* DCSurface::GetTile(int32_t aX, int32_t aY) const {
1128 TileKey key(aX, aY);
1129 auto tile_it = mDCTiles.find(key);
1130 MOZ_RELEASE_ASSERT(tile_it != mDCTiles.end());
1131 return tile_it->second.get();
1134 DCSurfaceVideo::DCSurfaceVideo(bool aIsOpaque, DCLayerTree* aDCLayerTree)
1135 : DCSurface(wr::DeviceIntSize{}, wr::DeviceIntPoint{}, false, aIsOpaque,
1136 aDCLayerTree) {}
1138 DCSurfaceVideo::~DCSurfaceVideo() {
1139 ReleaseDecodeSwapChainResources();
1140 MOZ_ASSERT(!mSwapChainSurfaceHandle);
1143 bool IsYUVSwapChainFormat(DXGI_FORMAT aFormat) {
1144 if (aFormat == DXGI_FORMAT_NV12 || aFormat == DXGI_FORMAT_YUY2) {
1145 return true;
1147 return false;
1150 void DCSurfaceVideo::AttachExternalImage(wr::ExternalImageId aExternalImage) {
1151 RenderTextureHost* texture =
1152 RenderThread::Get()->GetRenderTexture(aExternalImage);
1153 MOZ_RELEASE_ASSERT(texture);
1155 if (mPrevTexture == texture) {
1156 return;
1159 // XXX if software decoded video frame format is nv12, it could be used as
1160 // video overlay.
1161 if (!texture || !texture->AsRenderDXGITextureHost() ||
1162 texture->GetFormat() != gfx::SurfaceFormat::NV12) {
1163 gfxCriticalNote << "Unsupported RenderTexture for overlay: "
1164 << gfx::hexa(texture);
1165 return;
1168 mRenderTextureHost = texture;
1171 bool DCSurfaceVideo::CalculateSwapChainSize(gfx::Matrix& aTransform) {
1172 if (!mRenderTextureHost) {
1173 MOZ_ASSERT_UNREACHABLE("unexpected to be called");
1174 return false;
1177 const auto overlayType = mRenderTextureHost->IsSoftwareDecodedVideo()
1178 ? DCompOverlayTypes::SOFTWARE_DECODED_VIDEO
1179 : DCompOverlayTypes::HARDWARE_DECODED_VIDEO;
1180 mDCLayerTree->SetUsedOverlayTypeInFrame(overlayType);
1182 mVideoSize = mRenderTextureHost->AsRenderDXGITextureHost()->GetSize(0);
1184 // When RenderTextureHost, swapChainSize or VideoSwapChain are updated,
1185 // DCSurfaceVideo::PresentVideo() needs to be called.
1186 bool needsToPresent = mPrevTexture != mRenderTextureHost;
1187 gfx::IntSize swapChainSize = mVideoSize;
1188 gfx::Matrix transform = aTransform;
1189 const bool isDRM = mRenderTextureHost->IsFromDRMSource();
1191 // When video is rendered to axis aligned integer rectangle, video scaling
1192 // could be done by VideoProcessor
1193 bool scaleVideoAtVideoProcessor = false;
1194 if (StaticPrefs::gfx_webrender_dcomp_video_vp_scaling_win_AtStartup() &&
1195 aTransform.PreservesAxisAlignedRectangles()) {
1196 gfx::Size scaledSize = gfx::Size(mVideoSize) * aTransform.ScaleFactors();
1197 gfx::IntSize size(int32_t(std::round(scaledSize.width)),
1198 int32_t(std::round(scaledSize.height)));
1199 if (gfx::FuzzyEqual(scaledSize.width, size.width, 0.1f) &&
1200 gfx::FuzzyEqual(scaledSize.height, size.height, 0.1f)) {
1201 scaleVideoAtVideoProcessor = true;
1202 swapChainSize = size;
1206 if (scaleVideoAtVideoProcessor) {
1207 // 4:2:2 subsampled formats like YUY2 must have an even width, and 4:2:0
1208 // subsampled formats like NV12 must have an even width and height.
1209 if (swapChainSize.width % 2 == 1) {
1210 swapChainSize.width += 1;
1212 if (swapChainSize.height % 2 == 1) {
1213 swapChainSize.height += 1;
1215 transform = gfx::Matrix::Translation(aTransform.GetTranslation());
1218 if (!mVideoSwapChain || mSwapChainSize != swapChainSize || mIsDRM != isDRM) {
1219 needsToPresent = true;
1220 ReleaseDecodeSwapChainResources();
1221 // Update mSwapChainSize before creating SwapChain
1222 mSwapChainSize = swapChainSize;
1223 mIsDRM = isDRM;
1225 auto swapChainFormat = GetSwapChainFormat();
1226 bool useYUVSwapChain = IsYUVSwapChainFormat(swapChainFormat);
1227 if (useYUVSwapChain) {
1228 // Tries to create YUV SwapChain
1229 CreateVideoSwapChain();
1230 if (!mVideoSwapChain) {
1231 mFailedYuvSwapChain = true;
1232 ReleaseDecodeSwapChainResources();
1234 gfxCriticalNote << "Fallback to RGB SwapChain";
1237 // Tries to create RGB SwapChain
1238 if (!mVideoSwapChain) {
1239 CreateVideoSwapChain();
1243 aTransform = transform;
1245 return needsToPresent;
1248 void DCSurfaceVideo::PresentVideo() {
1249 if (!mRenderTextureHost) {
1250 return;
1253 if (!mVideoSwapChain) {
1254 gfxCriticalNote << "Failed to create VideoSwapChain";
1255 RenderThread::Get()->NotifyWebRenderError(
1256 wr::WebRenderError::VIDEO_OVERLAY);
1257 return;
1260 mVisual->SetContent(mVideoSwapChain);
1262 if (!CallVideoProcessorBlt()) {
1263 auto swapChainFormat = GetSwapChainFormat();
1264 bool useYUVSwapChain = IsYUVSwapChainFormat(swapChainFormat);
1265 if (useYUVSwapChain) {
1266 mFailedYuvSwapChain = true;
1267 ReleaseDecodeSwapChainResources();
1268 return;
1270 RenderThread::Get()->NotifyWebRenderError(
1271 wr::WebRenderError::VIDEO_OVERLAY);
1272 return;
1275 auto start = TimeStamp::Now();
1276 HRESULT hr = mVideoSwapChain->Present(0, 0);
1277 auto end = TimeStamp::Now();
1279 if (FAILED(hr) && hr != DXGI_STATUS_OCCLUDED) {
1280 gfxCriticalNoteOnce << "video Present failed: " << gfx::hexa(hr);
1283 mPrevTexture = mRenderTextureHost;
1285 // Disable video overlay if mVideoSwapChain->Present() is too slow. It drops
1286 // fps.
1288 if (!StaticPrefs::gfx_webrender_dcomp_video_check_slow_present()) {
1289 return;
1292 const auto maxPresentWaitDurationMs = 2;
1293 const auto maxSlowPresentCount = 5;
1294 const auto presentDurationMs =
1295 static_cast<uint32_t>((end - start).ToMilliseconds());
1296 const auto overlayType = mRenderTextureHost->IsSoftwareDecodedVideo()
1297 ? DCompOverlayTypes::SOFTWARE_DECODED_VIDEO
1298 : DCompOverlayTypes::HARDWARE_DECODED_VIDEO;
1300 nsPrintfCString marker("PresentWait overlay %u %ums ", (uint8_t)overlayType,
1301 presentDurationMs);
1302 PROFILER_MARKER_TEXT("PresentWait", GRAPHICS, {}, marker);
1304 if (presentDurationMs > maxPresentWaitDurationMs) {
1305 mSlowPresentCount++;
1306 } else {
1307 mSlowPresentCount = 0;
1310 if (mSlowPresentCount <= maxSlowPresentCount) {
1311 return;
1314 if (overlayType == DCompOverlayTypes::SOFTWARE_DECODED_VIDEO) {
1315 gfxCriticalNoteOnce << "Sw video swapchain present is slow";
1316 RenderThread::Get()->NotifyWebRenderError(
1317 wr::WebRenderError::VIDEO_SW_OVERLAY);
1318 } else {
1319 gfxCriticalNoteOnce << "Hw video swapchain present is slow";
1320 RenderThread::Get()->NotifyWebRenderError(
1321 wr::WebRenderError::VIDEO_HW_OVERLAY);
1325 DXGI_FORMAT DCSurfaceVideo::GetSwapChainFormat() {
1326 if (mFailedYuvSwapChain || !mDCLayerTree->SupportsHardwareOverlays()) {
1327 return DXGI_FORMAT_B8G8R8A8_UNORM;
1329 return mDCLayerTree->GetOverlayFormatForSDR();
1332 bool DCSurfaceVideo::CreateVideoSwapChain() {
1333 MOZ_ASSERT(mRenderTextureHost);
1335 const auto device = mDCLayerTree->GetDevice();
1337 RefPtr<IDXGIDevice> dxgiDevice;
1338 device->QueryInterface((IDXGIDevice**)getter_AddRefs(dxgiDevice));
1340 RefPtr<IDXGIFactoryMedia> dxgiFactoryMedia;
1342 RefPtr<IDXGIAdapter> adapter;
1343 dxgiDevice->GetAdapter(getter_AddRefs(adapter));
1344 adapter->GetParent(
1345 IID_PPV_ARGS((IDXGIFactoryMedia**)getter_AddRefs(dxgiFactoryMedia)));
1348 mSwapChainSurfaceHandle = gfx::DeviceManagerDx::CreateDCompSurfaceHandle();
1349 if (!mSwapChainSurfaceHandle) {
1350 gfxCriticalNote << "Failed to create DCompSurfaceHandle";
1351 return false;
1354 auto swapChainFormat = GetSwapChainFormat();
1355 bool useTripleBuffering =
1356 StaticPrefs::gfx_webrender_dcomp_video_force_triple_buffering();
1358 DXGI_SWAP_CHAIN_DESC1 desc = {};
1359 desc.Width = mSwapChainSize.width;
1360 desc.Height = mSwapChainSize.height;
1361 desc.Format = swapChainFormat;
1362 desc.Stereo = FALSE;
1363 desc.SampleDesc.Count = 1;
1364 desc.BufferCount = useTripleBuffering ? 3 : 2;
1365 desc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
1366 desc.Scaling = DXGI_SCALING_STRETCH;
1367 desc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL;
1368 desc.Flags = DXGI_SWAP_CHAIN_FLAG_FULLSCREEN_VIDEO;
1369 if (IsYUVSwapChainFormat(swapChainFormat)) {
1370 desc.Flags |= DXGI_SWAP_CHAIN_FLAG_YUV_VIDEO;
1372 if (mIsDRM) {
1373 desc.Flags |= DXGI_SWAP_CHAIN_FLAG_DISPLAY_ONLY;
1375 desc.AlphaMode = DXGI_ALPHA_MODE_IGNORE;
1377 HRESULT hr;
1378 hr = dxgiFactoryMedia->CreateSwapChainForCompositionSurfaceHandle(
1379 device, mSwapChainSurfaceHandle, &desc, nullptr,
1380 getter_AddRefs(mVideoSwapChain));
1382 if (FAILED(hr)) {
1383 gfxCriticalNote << "Failed to create video SwapChain: " << gfx::hexa(hr)
1384 << " " << mSwapChainSize;
1385 return false;
1388 mSwapChainFormat = swapChainFormat;
1389 return true;
1392 // TODO: Replace with YUVRangedColorSpace
1393 static Maybe<DXGI_COLOR_SPACE_TYPE> GetSourceDXGIColorSpace(
1394 const gfx::YUVColorSpace aYUVColorSpace,
1395 const gfx::ColorRange aColorRange) {
1396 if (aYUVColorSpace == gfx::YUVColorSpace::BT601) {
1397 if (aColorRange == gfx::ColorRange::FULL) {
1398 return Some(DXGI_COLOR_SPACE_YCBCR_FULL_G22_LEFT_P601);
1399 } else {
1400 return Some(DXGI_COLOR_SPACE_YCBCR_STUDIO_G22_LEFT_P601);
1402 } else if (aYUVColorSpace == gfx::YUVColorSpace::BT709) {
1403 if (aColorRange == gfx::ColorRange::FULL) {
1404 return Some(DXGI_COLOR_SPACE_YCBCR_FULL_G22_LEFT_P709);
1405 } else {
1406 return Some(DXGI_COLOR_SPACE_YCBCR_STUDIO_G22_LEFT_P709);
1408 } else if (aYUVColorSpace == gfx::YUVColorSpace::BT2020) {
1409 if (aColorRange == gfx::ColorRange::FULL) {
1410 // XXX Add SMPTEST2084 handling. HDR content is not handled yet by
1411 // video overlay.
1412 return Some(DXGI_COLOR_SPACE_YCBCR_FULL_G22_LEFT_P2020);
1413 } else {
1414 return Some(DXGI_COLOR_SPACE_YCBCR_STUDIO_G22_LEFT_P2020);
1418 return Nothing();
1421 static Maybe<DXGI_COLOR_SPACE_TYPE> GetSourceDXGIColorSpace(
1422 const gfx::YUVRangedColorSpace aYUVColorSpace) {
1423 const auto info = FromYUVRangedColorSpace(aYUVColorSpace);
1424 return GetSourceDXGIColorSpace(info.space, info.range);
1427 static void SetNvidiaVideoSuperRes(ID3D11VideoContext* videoContext,
1428 ID3D11VideoProcessor* videoProcessor,
1429 bool enabled) {
1430 LOG("SetNvidiaVideoSuperRes() enabled=%d", enabled);
1432 // Undocumented NVIDIA driver constants
1433 constexpr GUID nvGUID = {0xD43CE1B3,
1434 0x1F4B,
1435 0x48AC,
1436 {0xBA, 0xEE, 0xC3, 0xC2, 0x53, 0x75, 0xE6, 0xF7}};
1438 constexpr UINT nvExtensionVersion = 0x1;
1439 constexpr UINT nvExtensionMethodSuperResolution = 0x2;
1440 struct {
1441 UINT version;
1442 UINT method;
1443 UINT enable;
1444 } streamExtensionInfo = {nvExtensionVersion, nvExtensionMethodSuperResolution,
1445 enabled ? 1u : 0};
1447 HRESULT hr;
1448 hr = videoContext->VideoProcessorSetStreamExtension(
1449 videoProcessor, 0, &nvGUID, sizeof(streamExtensionInfo),
1450 &streamExtensionInfo);
1452 // Ignore errors as could be unsupported
1453 if (FAILED(hr)) {
1454 LOG("SetNvidiaVideoSuperRes() error: %lx", hr);
1455 return;
1459 static UINT GetVendorId(ID3D11VideoDevice* const videoDevice) {
1460 RefPtr<IDXGIDevice> dxgiDevice;
1461 RefPtr<IDXGIAdapter> adapter;
1462 videoDevice->QueryInterface((IDXGIDevice**)getter_AddRefs(dxgiDevice));
1463 dxgiDevice->GetAdapter(getter_AddRefs(adapter));
1465 DXGI_ADAPTER_DESC adapterDesc;
1466 adapter->GetDesc(&adapterDesc);
1468 return adapterDesc.VendorId;
1471 bool DCSurfaceVideo::CallVideoProcessorBlt() {
1472 MOZ_ASSERT(mRenderTextureHost);
1474 HRESULT hr;
1475 const auto videoDevice = mDCLayerTree->GetVideoDevice();
1476 const auto videoContext = mDCLayerTree->GetVideoContext();
1477 const auto texture = mRenderTextureHost->AsRenderDXGITextureHost();
1479 Maybe<DXGI_COLOR_SPACE_TYPE> sourceColorSpace =
1480 GetSourceDXGIColorSpace(texture->GetYUVColorSpace());
1481 if (sourceColorSpace.isNothing()) {
1482 gfxCriticalNote << "Unsupported color space";
1483 return false;
1486 RefPtr<ID3D11Texture2D> texture2D = texture->GetD3D11Texture2DWithGL();
1487 if (!texture2D) {
1488 gfxCriticalNote << "Failed to get D3D11Texture2D";
1489 return false;
1492 if (!mVideoSwapChain) {
1493 return false;
1496 if (!mDCLayerTree->EnsureVideoProcessor(mVideoSize, mSwapChainSize)) {
1497 gfxCriticalNote << "EnsureVideoProcessor Failed";
1498 return false;
1501 RefPtr<IDXGISwapChain3> swapChain3;
1502 mVideoSwapChain->QueryInterface(
1503 (IDXGISwapChain3**)getter_AddRefs(swapChain3));
1504 if (!swapChain3) {
1505 gfxCriticalNote << "Failed to get IDXGISwapChain3";
1506 return false;
1509 RefPtr<ID3D11VideoContext1> videoContext1;
1510 videoContext->QueryInterface(
1511 (ID3D11VideoContext1**)getter_AddRefs(videoContext1));
1512 if (!videoContext1) {
1513 gfxCriticalNote << "Failed to get ID3D11VideoContext1";
1514 return false;
1517 const auto videoProcessor = mDCLayerTree->GetVideoProcessor();
1518 const auto videoProcessorEnumerator =
1519 mDCLayerTree->GetVideoProcessorEnumerator();
1521 DXGI_COLOR_SPACE_TYPE inputColorSpace = sourceColorSpace.ref();
1522 videoContext1->VideoProcessorSetStreamColorSpace1(videoProcessor, 0,
1523 inputColorSpace);
1525 DXGI_COLOR_SPACE_TYPE outputColorSpace =
1526 IsYUVSwapChainFormat(mSwapChainFormat)
1527 ? inputColorSpace
1528 : DXGI_COLOR_SPACE_RGB_FULL_G22_NONE_P709;
1529 hr = swapChain3->SetColorSpace1(outputColorSpace);
1530 if (FAILED(hr)) {
1531 gfxCriticalNoteOnce << "SetColorSpace1 failed: " << gfx::hexa(hr);
1532 RenderThread::Get()->NotifyWebRenderError(
1533 wr::WebRenderError::VIDEO_OVERLAY);
1534 return false;
1536 videoContext1->VideoProcessorSetOutputColorSpace1(videoProcessor,
1537 outputColorSpace);
1539 D3D11_VIDEO_PROCESSOR_INPUT_VIEW_DESC inputDesc = {};
1540 inputDesc.ViewDimension = D3D11_VPIV_DIMENSION_TEXTURE2D;
1541 inputDesc.Texture2D.ArraySlice = texture->ArrayIndex();
1543 RefPtr<ID3D11VideoProcessorInputView> inputView;
1544 hr = videoDevice->CreateVideoProcessorInputView(
1545 texture2D, videoProcessorEnumerator, &inputDesc,
1546 getter_AddRefs(inputView));
1547 if (FAILED(hr)) {
1548 gfxCriticalNote << "ID3D11VideoProcessorInputView creation failed: "
1549 << gfx::hexa(hr);
1550 return false;
1553 D3D11_VIDEO_PROCESSOR_STREAM stream = {};
1554 stream.Enable = true;
1555 stream.OutputIndex = 0;
1556 stream.InputFrameOrField = 0;
1557 stream.PastFrames = 0;
1558 stream.FutureFrames = 0;
1559 stream.pInputSurface = inputView.get();
1561 RECT destRect;
1562 destRect.left = 0;
1563 destRect.top = 0;
1564 destRect.right = mSwapChainSize.width;
1565 destRect.bottom = mSwapChainSize.height;
1567 videoContext->VideoProcessorSetOutputTargetRect(videoProcessor, TRUE,
1568 &destRect);
1569 videoContext->VideoProcessorSetStreamDestRect(videoProcessor, 0, TRUE,
1570 &destRect);
1571 RECT sourceRect;
1572 sourceRect.left = 0;
1573 sourceRect.top = 0;
1574 sourceRect.right = mVideoSize.width;
1575 sourceRect.bottom = mVideoSize.height;
1576 videoContext->VideoProcessorSetStreamSourceRect(videoProcessor, 0, TRUE,
1577 &sourceRect);
1579 if (!mOutputView) {
1580 RefPtr<ID3D11Texture2D> backBuf;
1581 mVideoSwapChain->GetBuffer(0, __uuidof(ID3D11Texture2D),
1582 (void**)getter_AddRefs(backBuf));
1584 D3D11_VIDEO_PROCESSOR_OUTPUT_VIEW_DESC outputDesc = {};
1585 outputDesc.ViewDimension = D3D11_VPOV_DIMENSION_TEXTURE2D;
1586 outputDesc.Texture2D.MipSlice = 0;
1588 hr = videoDevice->CreateVideoProcessorOutputView(
1589 backBuf, videoProcessorEnumerator, &outputDesc,
1590 getter_AddRefs(mOutputView));
1591 if (FAILED(hr)) {
1592 gfxCriticalNote << "ID3D11VideoProcessorOutputView creation failed: "
1593 << gfx::hexa(hr);
1594 return false;
1598 const UINT vendorId = GetVendorId(videoDevice);
1599 if (vendorId == 0x10DE &&
1600 StaticPrefs::gfx_webrender_super_resolution_nvidia_AtStartup()) {
1601 SetNvidiaVideoSuperRes(videoContext, videoProcessor, true);
1604 hr = videoContext->VideoProcessorBlt(videoProcessor, mOutputView, 0, 1,
1605 &stream);
1606 if (FAILED(hr)) {
1607 gfxCriticalNote << "VideoProcessorBlt failed: " << gfx::hexa(hr);
1608 return false;
1611 return true;
1614 void DCSurfaceVideo::ReleaseDecodeSwapChainResources() {
1615 mOutputView = nullptr;
1616 mVideoSwapChain = nullptr;
1617 mDecodeSwapChain = nullptr;
1618 mDecodeResource = nullptr;
1619 if (mSwapChainSurfaceHandle) {
1620 ::CloseHandle(mSwapChainSurfaceHandle);
1621 mSwapChainSurfaceHandle = 0;
1625 DCSurfaceHandle::DCSurfaceHandle(bool aIsOpaque, DCLayerTree* aDCLayerTree)
1626 : DCSurface(wr::DeviceIntSize{}, wr::DeviceIntPoint{}, false, aIsOpaque,
1627 aDCLayerTree) {}
1629 void DCSurfaceHandle::AttachExternalImage(wr::ExternalImageId aExternalImage) {
1630 RenderTextureHost* texture =
1631 RenderThread::Get()->GetRenderTexture(aExternalImage);
1632 RenderDcompSurfaceTextureHost* renderTexture =
1633 texture ? texture->AsRenderDcompSurfaceTextureHost() : nullptr;
1634 if (!renderTexture) {
1635 gfxCriticalNote << "Unsupported RenderTexture for DCSurfaceHandle: "
1636 << gfx::hexa(texture);
1637 return;
1640 const auto handle = renderTexture->GetDcompSurfaceHandle();
1641 if (GetSurfaceHandle() == handle) {
1642 return;
1645 LOG_H("AttachExternalImage, ext-image=%" PRIu64 ", texture=%p, handle=%p",
1646 wr::AsUint64(aExternalImage), renderTexture, handle);
1647 mDcompTextureHost = renderTexture;
1650 HANDLE DCSurfaceHandle::GetSurfaceHandle() const {
1651 if (mDcompTextureHost) {
1652 return mDcompTextureHost->GetDcompSurfaceHandle();
1654 return nullptr;
1657 IDCompositionSurface* DCSurfaceHandle::EnsureSurface() {
1658 if (auto* surface = mDcompTextureHost->GetSurface()) {
1659 return surface;
1662 // Texture host hasn't created the surface yet, ask it to create a new one.
1663 RefPtr<IDCompositionDevice> device;
1664 HRESULT hr = mDCLayerTree->GetCompositionDevice()->QueryInterface(
1665 (IDCompositionDevice**)getter_AddRefs(device));
1666 if (FAILED(hr)) {
1667 gfxCriticalNote
1668 << "Failed to convert IDCompositionDevice2 to IDCompositionDevice: "
1669 << gfx::hexa(hr);
1670 return nullptr;
1673 return mDcompTextureHost->CreateSurfaceFromDevice(device);
1676 void DCSurfaceHandle::PresentSurfaceHandle() {
1677 LOG_H("PresentSurfaceHandle");
1678 if (IDCompositionSurface* surface = EnsureSurface()) {
1679 LOG_H("Set surface %p to visual", surface);
1680 mVisual->SetContent(surface);
1681 } else {
1682 mVisual->SetContent(nullptr);
1686 DCTile::DCTile(DCLayerTree* aDCLayerTree) : mDCLayerTree(aDCLayerTree) {}
1688 DCTile::~DCTile() {}
1690 bool DCTile::Initialize(int aX, int aY, wr::DeviceIntSize aSize,
1691 bool aIsVirtualSurface, bool aIsOpaque,
1692 RefPtr<IDCompositionVisual2> mSurfaceVisual) {
1693 if (aSize.width <= 0 || aSize.height <= 0) {
1694 return false;
1697 mSize = aSize;
1698 mIsOpaque = aIsOpaque;
1699 mIsVirtualSurface = aIsVirtualSurface;
1700 mNeedsFullDraw = !aIsVirtualSurface;
1702 if (aIsVirtualSurface) {
1703 // Initially, the entire tile is considered valid, unless it is set by
1704 // the SetTileProperties method.
1705 mValidRect.x = 0;
1706 mValidRect.y = 0;
1707 mValidRect.width = aSize.width;
1708 mValidRect.height = aSize.height;
1709 } else {
1710 HRESULT hr;
1711 const auto dCompDevice = mDCLayerTree->GetCompositionDevice();
1712 // Create the visual and put it in the tree under the surface visual
1713 hr = dCompDevice->CreateVisual(getter_AddRefs(mVisual));
1714 if (FAILED(hr)) {
1715 gfxCriticalNote << "Failed to CreateVisual for DCTile: " << gfx::hexa(hr);
1716 return false;
1718 mSurfaceVisual->AddVisual(mVisual, false, nullptr);
1719 // Position the tile relative to the surface visual
1720 mVisual->SetOffsetX(aX * aSize.width);
1721 mVisual->SetOffsetY(aY * aSize.height);
1722 // Clip the visual so it doesn't show anything until we update it
1723 D2D_RECT_F clip = {0, 0, 0, 0};
1724 mVisual->SetClip(clip);
1725 // Create the underlying pixel buffer.
1726 mCompositionSurface = CreateCompositionSurface(aSize, aIsOpaque);
1727 if (!mCompositionSurface) {
1728 return false;
1730 hr = mVisual->SetContent(mCompositionSurface);
1731 if (FAILED(hr)) {
1732 gfxCriticalNote << "Failed to SetContent for DCTile: " << gfx::hexa(hr);
1733 return false;
1737 return true;
1740 RefPtr<IDCompositionSurface> DCTile::CreateCompositionSurface(
1741 wr::DeviceIntSize aSize, bool aIsOpaque) {
1742 HRESULT hr;
1743 const auto dCompDevice = mDCLayerTree->GetCompositionDevice();
1744 const auto alphaMode =
1745 aIsOpaque ? DXGI_ALPHA_MODE_IGNORE : DXGI_ALPHA_MODE_PREMULTIPLIED;
1746 RefPtr<IDCompositionSurface> compositionSurface;
1748 hr = dCompDevice->CreateSurface(aSize.width, aSize.height,
1749 DXGI_FORMAT_R8G8B8A8_UNORM, alphaMode,
1750 getter_AddRefs(compositionSurface));
1751 if (FAILED(hr)) {
1752 gfxCriticalNote << "Failed to CreateSurface for DCTile: " << gfx::hexa(hr);
1753 return nullptr;
1755 return compositionSurface;
1758 RefPtr<IDCompositionSurface> DCTile::Bind(wr::DeviceIntRect aValidRect) {
1759 if (mVisual != nullptr) {
1760 // Tile owns a visual, set the size of the visual to match the portion we
1761 // want to be visible.
1762 D2D_RECT_F clip_rect;
1763 clip_rect.left = aValidRect.min.x;
1764 clip_rect.top = aValidRect.min.y;
1765 clip_rect.right = aValidRect.max.x;
1766 clip_rect.bottom = aValidRect.max.y;
1767 mVisual->SetClip(clip_rect);
1769 return mCompositionSurface;
1772 GLuint DCLayerTree::CreateEGLSurfaceForCompositionSurface(
1773 wr::DeviceIntRect aDirtyRect, wr::DeviceIntPoint* aOffset,
1774 RefPtr<IDCompositionSurface> aCompositionSurface,
1775 wr::DeviceIntPoint aSurfaceOffset) {
1776 MOZ_ASSERT(aCompositionSurface.get());
1778 HRESULT hr;
1779 const auto gl = GetGLContext();
1780 RefPtr<ID3D11Texture2D> backBuf;
1781 POINT offset;
1783 RECT update_rect;
1784 update_rect.left = aSurfaceOffset.x + aDirtyRect.min.x;
1785 update_rect.top = aSurfaceOffset.y + aDirtyRect.min.y;
1786 update_rect.right = aSurfaceOffset.x + aDirtyRect.max.x;
1787 update_rect.bottom = aSurfaceOffset.y + aDirtyRect.max.y;
1788 hr = aCompositionSurface->BeginDraw(&update_rect, __uuidof(ID3D11Texture2D),
1789 (void**)getter_AddRefs(backBuf), &offset);
1791 if (FAILED(hr)) {
1792 LayoutDeviceIntRect rect = widget::WinUtils::ToIntRect(update_rect);
1794 gfxCriticalNote << "DCompositionSurface::BeginDraw failed: "
1795 << gfx::hexa(hr) << " " << rect;
1796 RenderThread::Get()->HandleWebRenderError(WebRenderError::BEGIN_DRAW);
1797 return false;
1800 // DC includes the origin of the dirty / update rect in the draw offset,
1801 // undo that here since WR expects it to be an absolute offset.
1802 offset.x -= aDirtyRect.min.x;
1803 offset.y -= aDirtyRect.min.y;
1805 D3D11_TEXTURE2D_DESC desc;
1806 backBuf->GetDesc(&desc);
1808 const auto& gle = gl::GLContextEGL::Cast(gl);
1809 const auto& egl = gle->mEgl;
1811 const auto buffer = reinterpret_cast<EGLClientBuffer>(backBuf.get());
1813 // Construct an EGLImage wrapper around the D3D texture for ANGLE.
1814 const EGLint attribs[] = {LOCAL_EGL_NONE};
1815 mEGLImage = egl->fCreateImage(EGL_NO_CONTEXT, LOCAL_EGL_D3D11_TEXTURE_ANGLE,
1816 buffer, attribs);
1818 // Get the current FBO and RBO id, so we can restore them later
1819 GLint currentFboId, currentRboId;
1820 gl->fGetIntegerv(LOCAL_GL_DRAW_FRAMEBUFFER_BINDING, &currentFboId);
1821 gl->fGetIntegerv(LOCAL_GL_RENDERBUFFER_BINDING, &currentRboId);
1823 // Create a render buffer object that is backed by the EGL image.
1824 gl->fGenRenderbuffers(1, &mColorRBO);
1825 gl->fBindRenderbuffer(LOCAL_GL_RENDERBUFFER, mColorRBO);
1826 gl->fEGLImageTargetRenderbufferStorage(LOCAL_GL_RENDERBUFFER, mEGLImage);
1828 // Get or create an FBO for the specified dimensions
1829 GLuint fboId = GetOrCreateFbo(desc.Width, desc.Height);
1831 // Attach the new renderbuffer to the FBO
1832 gl->fBindFramebuffer(LOCAL_GL_DRAW_FRAMEBUFFER, fboId);
1833 gl->fFramebufferRenderbuffer(LOCAL_GL_DRAW_FRAMEBUFFER,
1834 LOCAL_GL_COLOR_ATTACHMENT0,
1835 LOCAL_GL_RENDERBUFFER, mColorRBO);
1837 // Restore previous FBO and RBO bindings
1838 gl->fBindFramebuffer(LOCAL_GL_DRAW_FRAMEBUFFER, currentFboId);
1839 gl->fBindRenderbuffer(LOCAL_GL_RENDERBUFFER, currentRboId);
1841 aOffset->x = offset.x;
1842 aOffset->y = offset.y;
1844 return fboId;
1847 void DCLayerTree::DestroyEGLSurface() {
1848 const auto gl = GetGLContext();
1850 if (mColorRBO) {
1851 gl->fDeleteRenderbuffers(1, &mColorRBO);
1852 mColorRBO = 0;
1855 if (mEGLImage) {
1856 const auto& gle = gl::GLContextEGL::Cast(gl);
1857 const auto& egl = gle->mEgl;
1858 egl->fDestroyImage(mEGLImage);
1859 mEGLImage = EGL_NO_IMAGE;
1863 // -
1865 color::ColorProfileDesc DCLayerTree::QueryOutputColorProfile() {
1866 // GPU process can't simply init gfxPlatform, (and we don't need most of it)
1867 // but we do need gfxPlatform::GetCMSOutputProfile().
1868 // So we steal what we need through the window:
1869 const auto outputProfileData =
1870 gfxWindowsPlatform::GetPlatformCMSOutputProfileData_Impl();
1872 const auto qcmsProfile = qcms_profile_from_memory(
1873 outputProfileData.Elements(), outputProfileData.Length());
1874 const auto release = MakeScopeExit([&]() {
1875 if (qcmsProfile) {
1876 qcms_profile_release(qcmsProfile);
1880 const bool print = gfxEnv::MOZ_GL_SPEW();
1882 const auto ret = [&]() {
1883 if (qcmsProfile) {
1884 return color::ColorProfileDesc::From(*qcmsProfile);
1886 if (print) {
1887 printf_stderr(
1888 "Missing or failed to load display color profile, defaulting to "
1889 "sRGB.\n");
1891 const auto MISSING_PROFILE_DEFAULT_SPACE = color::ColorspaceDesc{
1892 color::Chromaticities::Srgb(),
1893 color::PiecewiseGammaDesc::Srgb(),
1895 return color::ColorProfileDesc::From(MISSING_PROFILE_DEFAULT_SPACE);
1896 }();
1898 if (print) {
1899 const auto gammaGuess = color::GuessGamma(ret.linearFromTf.r);
1900 printf_stderr(
1901 "Display profile:\n"
1902 " Approx Gamma: %f\n"
1903 " XYZ-D65 Red : %f, %f, %f\n"
1904 " XYZ-D65 Green: %f, %f, %f\n"
1905 " XYZ-D65 Blue : %f, %f, %f\n",
1906 gammaGuess, ret.xyzd65FromLinearRgb.at(0, 0),
1907 ret.xyzd65FromLinearRgb.at(0, 1), ret.xyzd65FromLinearRgb.at(0, 2),
1909 ret.xyzd65FromLinearRgb.at(1, 0), ret.xyzd65FromLinearRgb.at(1, 1),
1910 ret.xyzd65FromLinearRgb.at(1, 2),
1912 ret.xyzd65FromLinearRgb.at(2, 0), ret.xyzd65FromLinearRgb.at(2, 1),
1913 ret.xyzd65FromLinearRgb.at(2, 2));
1916 return ret;
1919 inline D2D1_MATRIX_5X4_F to_D2D1_MATRIX_5X4_F(const color::mat4& m) {
1920 return D2D1_MATRIX_5X4_F{{{
1921 m.rows[0][0],
1922 m.rows[1][0],
1923 m.rows[2][0],
1924 m.rows[3][0],
1925 m.rows[0][1],
1926 m.rows[1][1],
1927 m.rows[2][1],
1928 m.rows[3][1],
1929 m.rows[0][2],
1930 m.rows[1][2],
1931 m.rows[2][2],
1932 m.rows[3][2],
1933 m.rows[0][3],
1934 m.rows[1][3],
1935 m.rows[2][3],
1936 m.rows[3][3],
1941 }}};
1944 ColorManagementChain ColorManagementChain::From(
1945 IDCompositionDevice3& dcomp,
1946 const color::ColorProfileConversionDesc& conv) {
1947 auto ret = ColorManagementChain{};
1949 #if !defined(MOZ_MINGW_DCOMP_H_INCOMPLETE)
1951 const auto Append = [&](const RefPtr<IDCompositionFilterEffect>& afterLast) {
1952 if (ret.last) {
1953 afterLast->SetInput(0, ret.last, 0);
1955 ret.last = afterLast;
1958 const auto MaybeAppendColorMatrix = [&](const color::mat4& m) {
1959 RefPtr<IDCompositionColorMatrixEffect> e;
1960 if (approx(m, color::mat4::Identity())) return e;
1961 dcomp.CreateColorMatrixEffect(getter_AddRefs(e));
1962 MOZ_ASSERT(e);
1963 if (!e) return e;
1964 e->SetMatrix(to_D2D1_MATRIX_5X4_F(m));
1965 Append(e);
1966 return e;
1968 const auto MaybeAppendTableTransfer = [&](const color::RgbTransferTables& t) {
1969 RefPtr<IDCompositionTableTransferEffect> e;
1970 if (!t.r.size() && !t.g.size() && !t.b.size()) return e;
1971 dcomp.CreateTableTransferEffect(getter_AddRefs(e));
1972 MOZ_ASSERT(e);
1973 if (!e) return e;
1974 e->SetRedTable(t.r.data(), t.r.size());
1975 e->SetGreenTable(t.g.data(), t.g.size());
1976 e->SetBlueTable(t.b.data(), t.b.size());
1977 Append(e);
1978 return e;
1981 ret.srcRgbFromSrcYuv = MaybeAppendColorMatrix(conv.srcRgbFromSrcYuv);
1982 ret.srcLinearFromSrcTf = MaybeAppendTableTransfer(conv.srcLinearFromSrcTf);
1983 ret.dstLinearFromSrcLinear =
1984 MaybeAppendColorMatrix(color::mat4(conv.dstLinearFromSrcLinear));
1985 ret.dstTfFromDstLinear = MaybeAppendTableTransfer(conv.dstTfFromDstLinear);
1987 #endif // !defined(MOZ_MINGW_DCOMP_H_INCOMPLETE)
1989 return ret;
1992 ColorManagementChain::~ColorManagementChain() = default;
1994 } // namespace wr
1995 } // namespace mozilla
1997 #undef LOG_H