Bug 1799258 - Share all-of-dcomp.h preamble, and deal with outdated mingw dcomp.h...
[gecko.git] / gfx / webrender_bindings / DCLayerTree.cpp
blobea3c82714eab56aa689e72eedb58ba60c30ac61a
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 "mozilla/gfx/AllOfDcomp.h"
12 #include <d3d11.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_OLD
42 struct IDCompositionFilterEffect : public IDCompositionEffect {};
43 struct IDCompositionColorMatrixEffect : public IDCompositionFilterEffect {};
44 struct IDCompositionTableTransferEffect : public IDCompositionFilterEffect {};
45 struct IDCompositionDevice3 : public IDCompositionDevice2 {};
46 #endif // defined(__MINGW32__)
48 namespace mozilla {
49 namespace wr {
51 extern LazyLogModule gRenderThreadLog;
52 #define LOG(...) MOZ_LOG(gRenderThreadLog, LogLevel::Debug, (__VA_ARGS__))
54 #define LOG_H(msg, ...) \
55 MOZ_LOG(gDcompSurface, LogLevel::Debug, \
56 ("DCSurfaceHandle=%p, " msg, this, ##__VA_ARGS__))
58 UniquePtr<GpuOverlayInfo> DCLayerTree::sGpuOverlayInfo;
60 /* static */
61 UniquePtr<DCLayerTree> DCLayerTree::Create(gl::GLContext* aGL,
62 EGLConfig aEGLConfig,
63 ID3D11Device* aDevice,
64 ID3D11DeviceContext* aCtx,
65 HWND aHwnd, nsACString& aError) {
66 RefPtr<IDCompositionDevice2> dCompDevice =
67 gfx::DeviceManagerDx::Get()->GetDirectCompositionDevice();
68 if (!dCompDevice) {
69 aError.Assign("DCLayerTree(no device)"_ns);
70 return nullptr;
73 auto layerTree =
74 MakeUnique<DCLayerTree>(aGL, aEGLConfig, aDevice, aCtx, dCompDevice);
75 if (!layerTree->Initialize(aHwnd, aError)) {
76 return nullptr;
79 return layerTree;
82 void DCLayerTree::Shutdown() { DCLayerTree::sGpuOverlayInfo = nullptr; }
84 DCLayerTree::DCLayerTree(gl::GLContext* aGL, EGLConfig aEGLConfig,
85 ID3D11Device* aDevice, ID3D11DeviceContext* aCtx,
86 IDCompositionDevice2* aCompositionDevice)
87 : mGL(aGL),
88 mEGLConfig(aEGLConfig),
89 mDevice(aDevice),
90 mCtx(aCtx),
91 mCompositionDevice(aCompositionDevice),
92 mDebugCounter(false),
93 mDebugVisualRedrawRegions(false),
94 mEGLImage(EGL_NO_IMAGE),
95 mColorRBO(0),
96 mPendingCommit(false) {
97 LOG("DCLayerTree::DCLayerTree()");
100 DCLayerTree::~DCLayerTree() {
101 LOG("DCLayerTree::~DCLayerTree()");
103 ReleaseNativeCompositorResources();
106 void DCLayerTree::ReleaseNativeCompositorResources() {
107 const auto gl = GetGLContext();
109 DestroyEGLSurface();
111 // Delete any cached FBO objects
112 for (auto it = mFrameBuffers.begin(); it != mFrameBuffers.end(); ++it) {
113 gl->fDeleteRenderbuffers(1, &it->depthRboId);
114 gl->fDeleteFramebuffers(1, &it->fboId);
118 bool DCLayerTree::Initialize(HWND aHwnd, nsACString& aError) {
119 HRESULT hr;
121 RefPtr<IDCompositionDesktopDevice> desktopDevice;
122 hr = mCompositionDevice->QueryInterface(
123 (IDCompositionDesktopDevice**)getter_AddRefs(desktopDevice));
124 if (FAILED(hr)) {
125 aError.Assign(nsPrintfCString(
126 "DCLayerTree(get IDCompositionDesktopDevice failed %lx)", hr));
127 return false;
130 hr = desktopDevice->CreateTargetForHwnd(aHwnd, TRUE,
131 getter_AddRefs(mCompositionTarget));
132 if (FAILED(hr)) {
133 aError.Assign(nsPrintfCString(
134 "DCLayerTree(create DCompositionTarget failed %lx)", hr));
135 return false;
138 hr = mCompositionDevice->CreateVisual(getter_AddRefs(mRootVisual));
139 if (FAILED(hr)) {
140 aError.Assign(nsPrintfCString(
141 "DCLayerTree(create root DCompositionVisual failed %lx)", hr));
142 return false;
145 hr =
146 mCompositionDevice->CreateVisual(getter_AddRefs(mDefaultSwapChainVisual));
147 if (FAILED(hr)) {
148 aError.Assign(nsPrintfCString(
149 "DCLayerTree(create swap chain DCompositionVisual failed %lx)", hr));
150 return false;
153 if (gfx::gfxVars::UseWebRenderDCompVideoOverlayWin()) {
154 if (!InitializeVideoOverlaySupport()) {
155 RenderThread::Get()->HandleWebRenderError(WebRenderError::VIDEO_OVERLAY);
158 if (!sGpuOverlayInfo) {
159 // Set default if sGpuOverlayInfo was not set.
160 sGpuOverlayInfo = MakeUnique<GpuOverlayInfo>();
163 // Initialize SwapChainInfo
164 SupportsSwapChainTearing();
166 mCompositionTarget->SetRoot(mRootVisual);
167 // Set interporation mode to nearest, to ensure 1:1 sampling.
168 // By default, a visual inherits the interpolation mode of the parent visual.
169 // If no visuals set the interpolation mode, the default for the entire visual
170 // tree is nearest neighbor interpolation.
171 mRootVisual->SetBitmapInterpolationMode(
172 DCOMPOSITION_BITMAP_INTERPOLATION_MODE_NEAREST_NEIGHBOR);
173 return true;
176 bool FlagsSupportsOverlays(UINT flags) {
177 return (flags & (DXGI_OVERLAY_SUPPORT_FLAG_DIRECT |
178 DXGI_OVERLAY_SUPPORT_FLAG_SCALING));
181 // A warpper of IDXGIOutput4::CheckOverlayColorSpaceSupport()
182 bool CheckOverlayColorSpaceSupport(DXGI_FORMAT aDxgiFormat,
183 DXGI_COLOR_SPACE_TYPE aDxgiColorSpace,
184 RefPtr<IDXGIOutput> aOutput,
185 RefPtr<ID3D11Device> aD3d11Device) {
186 UINT colorSpaceSupportFlags = 0;
187 RefPtr<IDXGIOutput4> output4;
189 if (FAILED(aOutput->QueryInterface(__uuidof(IDXGIOutput4),
190 getter_AddRefs(output4)))) {
191 return false;
194 if (FAILED(output4->CheckOverlayColorSpaceSupport(
195 aDxgiFormat, aDxgiColorSpace, aD3d11Device,
196 &colorSpaceSupportFlags))) {
197 return false;
200 return (colorSpaceSupportFlags &
201 DXGI_OVERLAY_COLOR_SPACE_SUPPORT_FLAG_PRESENT);
204 bool DCLayerTree::InitializeVideoOverlaySupport() {
205 MOZ_ASSERT(IsWin10AnniversaryUpdateOrLater());
207 HRESULT hr;
209 hr = mDevice->QueryInterface(
210 (ID3D11VideoDevice**)getter_AddRefs(mVideoDevice));
211 if (FAILED(hr)) {
212 gfxCriticalNote << "Failed to get D3D11VideoDevice: " << gfx::hexa(hr);
213 return false;
216 hr =
217 mCtx->QueryInterface((ID3D11VideoContext**)getter_AddRefs(mVideoContext));
218 if (FAILED(hr)) {
219 gfxCriticalNote << "Failed to get D3D11VideoContext: " << gfx::hexa(hr);
220 return false;
223 if (sGpuOverlayInfo) {
224 return true;
227 UniquePtr<GpuOverlayInfo> info = MakeUnique<GpuOverlayInfo>();
229 RefPtr<IDXGIDevice> dxgiDevice;
230 RefPtr<IDXGIAdapter> adapter;
231 mDevice->QueryInterface((IDXGIDevice**)getter_AddRefs(dxgiDevice));
232 dxgiDevice->GetAdapter(getter_AddRefs(adapter));
234 unsigned int i = 0;
235 while (true) {
236 RefPtr<IDXGIOutput> output;
237 if (FAILED(adapter->EnumOutputs(i++, getter_AddRefs(output)))) {
238 break;
240 RefPtr<IDXGIOutput3> output3;
241 if (FAILED(output->QueryInterface(__uuidof(IDXGIOutput3),
242 getter_AddRefs(output3)))) {
243 break;
246 output3->CheckOverlaySupport(DXGI_FORMAT_NV12, mDevice,
247 &info->mNv12OverlaySupportFlags);
248 output3->CheckOverlaySupport(DXGI_FORMAT_YUY2, mDevice,
249 &info->mYuy2OverlaySupportFlags);
250 output3->CheckOverlaySupport(DXGI_FORMAT_B8G8R8A8_UNORM, mDevice,
251 &info->mBgra8OverlaySupportFlags);
252 output3->CheckOverlaySupport(DXGI_FORMAT_R10G10B10A2_UNORM, mDevice,
253 &info->mRgb10a2OverlaySupportFlags);
255 if (FlagsSupportsOverlays(info->mNv12OverlaySupportFlags)) {
256 // NV12 format is preferred if it's supported.
257 info->mOverlayFormatUsed = DXGI_FORMAT_NV12;
258 info->mSupportsHardwareOverlays = true;
261 if (!info->mSupportsHardwareOverlays &&
262 FlagsSupportsOverlays(info->mYuy2OverlaySupportFlags)) {
263 // If NV12 isn't supported, fallback to YUY2 if it's supported.
264 info->mOverlayFormatUsed = DXGI_FORMAT_YUY2;
265 info->mSupportsHardwareOverlays = true;
268 // RGB10A2 overlay is used for displaying HDR content. In Intel's
269 // platform, RGB10A2 overlay is enabled only when
270 // DXGI_COLOR_SPACE_RGB_FULL_G2084_NONE_P2020 is supported.
271 if (FlagsSupportsOverlays(info->mRgb10a2OverlaySupportFlags)) {
272 if (!CheckOverlayColorSpaceSupport(
273 DXGI_FORMAT_R10G10B10A2_UNORM,
274 DXGI_COLOR_SPACE_RGB_FULL_G2084_NONE_P2020, output, mDevice))
275 info->mRgb10a2OverlaySupportFlags = 0;
278 // Early out after the first output that reports overlay support. All
279 // outputs are expected to report the same overlay support according to
280 // Microsoft's WDDM documentation:
281 // https://docs.microsoft.com/en-us/windows-hardware/drivers/display/multiplane-overlay-hardware-requirements
282 if (info->mSupportsHardwareOverlays) {
283 break;
287 if (!StaticPrefs::gfx_webrender_dcomp_video_yuv_overlay_win_AtStartup()) {
288 info->mOverlayFormatUsed = DXGI_FORMAT_B8G8R8A8_UNORM;
289 info->mSupportsHardwareOverlays = false;
292 info->mSupportsOverlays = info->mSupportsHardwareOverlays;
294 sGpuOverlayInfo = std::move(info);
296 if (auto* gpuParent = gfx::GPUParent::GetSingleton()) {
297 gpuParent->NotifyOverlayInfo(GetOverlayInfo());
300 return true;
303 DCSurface* DCLayerTree::GetSurface(wr::NativeSurfaceId aId) const {
304 auto surface_it = mDCSurfaces.find(aId);
305 MOZ_RELEASE_ASSERT(surface_it != mDCSurfaces.end());
306 return surface_it->second.get();
309 void DCLayerTree::SetDefaultSwapChain(IDXGISwapChain1* aSwapChain) {
310 LOG("DCLayerTree::SetDefaultSwapChain()");
312 mRootVisual->AddVisual(mDefaultSwapChainVisual, TRUE, nullptr);
313 mDefaultSwapChainVisual->SetContent(aSwapChain);
314 // Default SwapChain's visual does not need linear interporation.
315 mDefaultSwapChainVisual->SetBitmapInterpolationMode(
316 DCOMPOSITION_BITMAP_INTERPOLATION_MODE_NEAREST_NEIGHBOR);
317 mPendingCommit = true;
320 void DCLayerTree::MaybeUpdateDebug() {
321 bool updated = false;
322 updated |= MaybeUpdateDebugCounter();
323 updated |= MaybeUpdateDebugVisualRedrawRegions();
324 if (updated) {
325 mPendingCommit = true;
329 void DCLayerTree::MaybeCommit() {
330 if (!mPendingCommit) {
331 return;
333 mCompositionDevice->Commit();
336 void DCLayerTree::WaitForCommitCompletion() {
337 mCompositionDevice->WaitForCommitCompletion();
340 void DCLayerTree::DisableNativeCompositor() {
341 MOZ_ASSERT(mCurrentSurface.isNothing());
342 MOZ_ASSERT(mCurrentLayers.empty());
344 ReleaseNativeCompositorResources();
345 mPrevLayers.clear();
346 mRootVisual->RemoveAllVisuals();
349 bool DCLayerTree::MaybeUpdateDebugCounter() {
350 bool debugCounter = StaticPrefs::gfx_webrender_debug_dcomp_counter();
351 if (mDebugCounter == debugCounter) {
352 return false;
355 RefPtr<IDCompositionDeviceDebug> debugDevice;
356 HRESULT hr = mCompositionDevice->QueryInterface(
357 (IDCompositionDeviceDebug**)getter_AddRefs(debugDevice));
358 if (FAILED(hr)) {
359 return false;
362 if (debugCounter) {
363 debugDevice->EnableDebugCounters();
364 } else {
365 debugDevice->DisableDebugCounters();
368 mDebugCounter = debugCounter;
369 return true;
372 bool DCLayerTree::MaybeUpdateDebugVisualRedrawRegions() {
373 bool debugVisualRedrawRegions =
374 StaticPrefs::gfx_webrender_debug_dcomp_redraw_regions();
375 if (mDebugVisualRedrawRegions == debugVisualRedrawRegions) {
376 return false;
379 RefPtr<IDCompositionVisualDebug> visualDebug;
380 HRESULT hr = mRootVisual->QueryInterface(
381 (IDCompositionVisualDebug**)getter_AddRefs(visualDebug));
382 if (FAILED(hr)) {
383 return false;
386 if (debugVisualRedrawRegions) {
387 visualDebug->EnableRedrawRegions();
388 } else {
389 visualDebug->DisableRedrawRegions();
392 mDebugVisualRedrawRegions = debugVisualRedrawRegions;
393 return true;
396 void DCLayerTree::CompositorBeginFrame() { mCurrentFrame++; }
398 void DCLayerTree::CompositorEndFrame() {
399 auto start = TimeStamp::Now();
400 // Check if the visual tree of surfaces is the same as last frame.
401 bool same = mPrevLayers == mCurrentLayers;
403 if (!same) {
404 // If not, we need to rebuild the visual tree. Note that addition or
405 // removal of tiles no longer needs to rebuild the main visual tree
406 // here, since they are added as children of the surface visual.
407 mRootVisual->RemoveAllVisuals();
410 for (auto it = mCurrentLayers.begin(); it != mCurrentLayers.end(); ++it) {
411 auto surface_it = mDCSurfaces.find(*it);
412 MOZ_RELEASE_ASSERT(surface_it != mDCSurfaces.end());
413 const auto surface = surface_it->second.get();
414 // Ensure surface is trimmed to updated tile valid rects
415 surface->UpdateAllocatedRect();
416 if (!same) {
417 // Add surfaces in z-order they were added to the scene.
418 const auto visual = surface->GetVisual();
419 mRootVisual->AddVisual(visual, false, nullptr);
423 mPrevLayers.swap(mCurrentLayers);
424 mCurrentLayers.clear();
426 mCompositionDevice->Commit();
428 auto end = TimeStamp::Now();
429 mozilla::Telemetry::Accumulate(mozilla::Telemetry::COMPOSITE_SWAP_TIME,
430 (end - start).ToMilliseconds() * 10.);
432 // Remove any framebuffers that haven't been
433 // used in the last 60 frames.
435 // This should use nsTArray::RemoveElementsBy once
436 // CachedFrameBuffer is able to properly destroy
437 // itself in the destructor.
438 const auto gl = GetGLContext();
439 for (uint32_t i = 0, len = mFrameBuffers.Length(); i < len; ++i) {
440 auto& fb = mFrameBuffers[i];
441 if ((mCurrentFrame - fb.lastFrameUsed) > 60) {
442 gl->fDeleteRenderbuffers(1, &fb.depthRboId);
443 gl->fDeleteFramebuffers(1, &fb.fboId);
444 mFrameBuffers.UnorderedRemoveElementAt(i);
445 --i; // Examine the element again, if necessary.
446 --len;
451 void DCLayerTree::Bind(wr::NativeTileId aId, wr::DeviceIntPoint* aOffset,
452 uint32_t* aFboId, wr::DeviceIntRect aDirtyRect,
453 wr::DeviceIntRect aValidRect) {
454 auto surface = GetSurface(aId.surface_id);
455 auto tile = surface->GetTile(aId.x, aId.y);
456 wr::DeviceIntPoint targetOffset{0, 0};
458 // If tile owns an IDCompositionSurface we use it, otherwise we're using an
459 // IDCompositionVirtualSurface owned by the DCSurface.
460 RefPtr<IDCompositionSurface> compositionSurface;
461 if (surface->mIsVirtualSurface) {
462 gfx::IntRect validRect(aValidRect.min.x, aValidRect.min.y,
463 aValidRect.width(), aValidRect.height());
464 if (!tile->mValidRect.IsEqualEdges(validRect)) {
465 tile->mValidRect = validRect;
466 surface->DirtyAllocatedRect();
468 wr::DeviceIntSize tileSize = surface->GetTileSize();
469 compositionSurface = surface->GetCompositionSurface();
470 wr::DeviceIntPoint virtualOffset = surface->GetVirtualOffset();
471 targetOffset.x = virtualOffset.x + tileSize.width * aId.x;
472 targetOffset.y = virtualOffset.y + tileSize.height * aId.y;
473 } else {
474 compositionSurface = tile->Bind(aValidRect);
477 if (tile->mNeedsFullDraw) {
478 // dcomp requires that the first BeginDraw on a non-virtual surface is the
479 // full size of the pixel buffer.
480 auto tileSize = surface->GetTileSize();
481 aDirtyRect.min.x = 0;
482 aDirtyRect.min.y = 0;
483 aDirtyRect.max.x = tileSize.width;
484 aDirtyRect.max.y = tileSize.height;
485 tile->mNeedsFullDraw = false;
488 *aFboId = CreateEGLSurfaceForCompositionSurface(
489 aDirtyRect, aOffset, compositionSurface, targetOffset);
490 mCurrentSurface = Some(compositionSurface);
493 void DCLayerTree::Unbind() {
494 if (mCurrentSurface.isNothing()) {
495 return;
498 RefPtr<IDCompositionSurface> surface = mCurrentSurface.ref();
499 surface->EndDraw();
501 DestroyEGLSurface();
502 mCurrentSurface = Nothing();
505 void DCLayerTree::CreateSurface(wr::NativeSurfaceId aId,
506 wr::DeviceIntPoint aVirtualOffset,
507 wr::DeviceIntSize aTileSize, bool aIsOpaque) {
508 auto it = mDCSurfaces.find(aId);
509 MOZ_RELEASE_ASSERT(it == mDCSurfaces.end());
510 if (it != mDCSurfaces.end()) {
511 // DCSurface already exists.
512 return;
515 // Tile size needs to be positive.
516 if (aTileSize.width <= 0 || aTileSize.height <= 0) {
517 gfxCriticalNote << "TileSize is not positive aId: " << wr::AsUint64(aId)
518 << " aTileSize(" << aTileSize.width << ","
519 << aTileSize.height << ")";
522 bool isVirtualSurface =
523 StaticPrefs::gfx_webrender_dcomp_use_virtual_surfaces_AtStartup();
524 auto surface = MakeUnique<DCSurface>(aTileSize, aVirtualOffset,
525 isVirtualSurface, aIsOpaque, this);
526 if (!surface->Initialize()) {
527 gfxCriticalNote << "Failed to initialize DCSurface: " << wr::AsUint64(aId);
528 return;
531 mDCSurfaces[aId] = std::move(surface);
534 void DCLayerTree::CreateExternalSurface(wr::NativeSurfaceId aId,
535 bool aIsOpaque) {
536 auto it = mDCSurfaces.find(aId);
537 MOZ_RELEASE_ASSERT(it == mDCSurfaces.end());
539 auto surface = MakeUnique<DCExternalSurfaceWrapper>(aIsOpaque, this);
540 if (!surface->Initialize()) {
541 gfxCriticalNote << "Failed to initialize DCExternalSurfaceWrapper: "
542 << wr::AsUint64(aId);
543 return;
546 mDCSurfaces[aId] = std::move(surface);
549 void DCLayerTree::DestroySurface(NativeSurfaceId aId) {
550 auto surface_it = mDCSurfaces.find(aId);
551 MOZ_RELEASE_ASSERT(surface_it != mDCSurfaces.end());
552 auto surface = surface_it->second.get();
554 mRootVisual->RemoveVisual(surface->GetVisual());
555 mDCSurfaces.erase(surface_it);
558 void DCLayerTree::CreateTile(wr::NativeSurfaceId aId, int32_t aX, int32_t aY) {
559 auto surface = GetSurface(aId);
560 surface->CreateTile(aX, aY);
563 void DCLayerTree::DestroyTile(wr::NativeSurfaceId aId, int32_t aX, int32_t aY) {
564 auto surface = GetSurface(aId);
565 surface->DestroyTile(aX, aY);
568 void DCLayerTree::AttachExternalImage(wr::NativeSurfaceId aId,
569 wr::ExternalImageId aExternalImage) {
570 auto surface_it = mDCSurfaces.find(aId);
571 MOZ_RELEASE_ASSERT(surface_it != mDCSurfaces.end());
572 surface_it->second->AttachExternalImage(aExternalImage);
575 void DCExternalSurfaceWrapper::AttachExternalImage(
576 wr::ExternalImageId aExternalImage) {
577 if (auto* surface = EnsureSurfaceForExternalImage(aExternalImage)) {
578 surface->AttachExternalImage(aExternalImage);
582 template <class ToT>
583 struct QI {
584 template <class FromT>
585 [[nodiscard]] static inline RefPtr<ToT> From(FromT* const from) {
586 RefPtr<ToT> to;
587 (void)from->QueryInterface(static_cast<ToT**>(getter_AddRefs(to)));
588 return to;
592 DCSurface* DCExternalSurfaceWrapper::EnsureSurfaceForExternalImage(
593 wr::ExternalImageId aExternalImage) {
594 if (mSurface) {
595 return mSurface.get();
598 // Create a new surface based on the texture type.
599 RenderTextureHost* texture =
600 RenderThread::Get()->GetRenderTexture(aExternalImage);
601 if (texture && texture->AsRenderDXGITextureHost()) {
602 mSurface.reset(new DCSurfaceVideo(mIsOpaque, mDCLayerTree));
603 if (!mSurface->Initialize()) {
604 gfxCriticalNote << "Failed to initialize DCSurfaceVideo: "
605 << wr::AsUint64(aExternalImage);
606 mSurface = nullptr;
608 } else if (texture && texture->AsRenderDcompSurfaceTextureHost()) {
609 mSurface.reset(new DCSurfaceHandle(mIsOpaque, mDCLayerTree));
610 if (!mSurface->Initialize()) {
611 gfxCriticalNote << "Failed to initialize DCSurfaceHandle: "
612 << wr::AsUint64(aExternalImage);
613 mSurface = nullptr;
616 if (!mSurface) {
617 gfxCriticalNote << "Failed to create a surface for external image: "
618 << gfx::hexa(texture);
619 return nullptr;
621 const auto textureSwgl = texture->AsRenderTextureHostSWGL();
622 MOZ_ASSERT(textureSwgl); // Covered above.
624 // Add surface's visual which will contain video data to our root visual.
625 const auto surfaceVisual = mSurface->GetVisual();
626 mVisual->AddVisual(surfaceVisual, true, nullptr);
628 // -
629 // Apply color management.
631 [&]() {
632 const auto cmsMode = GfxColorManagementMode();
633 if (cmsMode == CMSMode::Off) return;
635 const auto dcomp = mDCLayerTree->GetCompositionDevice();
636 RefPtr<IDCompositionDevice3> dcomp3;
637 #if !defined(MOZ_MINGW_DCOMP_H_OLD)
638 dcomp3 = QI<IDCompositionDevice3>::From(dcomp);
639 #else
640 (void)dcomp;
641 #endif
642 if (!dcomp3) {
643 NS_WARNING(
644 "No IDCompositionDevice3, cannot use dcomp for color management.");
645 return;
648 // -
650 const auto cspace = [&]() {
651 const auto rangedCspace = textureSwgl->GetYUVColorSpace();
652 const auto info = FromYUVRangedColorSpace(rangedCspace);
653 auto ret = ToColorSpace2(info.space);
654 if (ret == gfx::ColorSpace2::Display && cmsMode == CMSMode::All) {
655 ret = gfx::ColorSpace2::SRGB;
657 return ret;
658 }();
660 const bool rec709GammaAsSrgb =
661 StaticPrefs::gfx_color_management_rec709_gamma_as_srgb();
662 const bool rec2020GammaAsRec709 =
663 StaticPrefs::gfx_color_management_rec2020_gamma_as_rec709();
665 auto cspaceDesc = color::ColorspaceDesc{};
666 switch (cspace) {
667 case gfx::ColorSpace2::Display:
668 return; // No color management needed!
669 case gfx::ColorSpace2::SRGB:
670 cspaceDesc.chrom = color::Chromaticities::Srgb();
671 cspaceDesc.tf = color::PiecewiseGammaDesc::Srgb();
672 break;
674 case gfx::ColorSpace2::DISPLAY_P3:
675 cspaceDesc.chrom = color::Chromaticities::DisplayP3();
676 cspaceDesc.tf = color::PiecewiseGammaDesc::DisplayP3();
677 break;
679 case gfx::ColorSpace2::BT601_525:
680 cspaceDesc.chrom = color::Chromaticities::Rec601_525_Ntsc();
681 if (rec709GammaAsSrgb) {
682 cspaceDesc.tf = color::PiecewiseGammaDesc::Srgb();
683 } else {
684 cspaceDesc.tf = color::PiecewiseGammaDesc::Rec709();
686 break;
688 case gfx::ColorSpace2::BT709:
689 cspaceDesc.chrom = color::Chromaticities::Rec709();
690 if (rec709GammaAsSrgb) {
691 cspaceDesc.tf = color::PiecewiseGammaDesc::Srgb();
692 } else {
693 cspaceDesc.tf = color::PiecewiseGammaDesc::Rec709();
695 break;
697 case gfx::ColorSpace2::BT2020:
698 cspaceDesc.chrom = color::Chromaticities::Rec2020();
699 if (rec2020GammaAsRec709 && rec709GammaAsSrgb) {
700 cspaceDesc.tf = color::PiecewiseGammaDesc::Srgb();
701 } else if (rec2020GammaAsRec709) {
702 cspaceDesc.tf = color::PiecewiseGammaDesc::Rec709();
703 } else {
704 // Just Rec709 with slightly more precision.
705 cspaceDesc.tf = color::PiecewiseGammaDesc::Rec2020_12bit();
707 break;
710 const auto cprofileIn = color::ColorProfileDesc::From(cspaceDesc);
711 auto cprofileOut = mDCLayerTree->OutputColorProfile();
712 bool pretendSrgb = StaticPrefs::gfx_color_management_native_srgb();
713 if (pretendSrgb) {
714 cprofileOut = color::ColorProfileDesc::From({
715 color::Chromaticities::Srgb(),
716 color::PiecewiseGammaDesc::Srgb(),
719 const auto conversion = color::ColorProfileConversionDesc::From({
720 .src = cprofileIn,
721 .dst = cprofileOut,
724 // -
726 auto chain = ColorManagementChain::From(*dcomp3, conversion);
727 mCManageChain = Some(chain);
729 surfaceVisual->SetEffect(mCManageChain->last.get());
730 }();
732 return mSurface.get();
735 void DCExternalSurfaceWrapper::PresentExternalSurface(gfx::Matrix& aTransform) {
736 MOZ_ASSERT(mSurface);
737 if (auto* surface = mSurface->AsDCSurfaceVideo()) {
738 if (surface->CalculateSwapChainSize(aTransform)) {
739 surface->PresentVideo();
741 } else if (auto* surface = mSurface->AsDCSurfaceHandle()) {
742 surface->PresentSurfaceHandle();
746 template <typename T>
747 static inline D2D1_RECT_F D2DRect(const T& aRect) {
748 return D2D1::RectF(aRect.X(), aRect.Y(), aRect.XMost(), aRect.YMost());
751 static inline D2D1_MATRIX_3X2_F D2DMatrix(const gfx::Matrix& aTransform) {
752 return D2D1::Matrix3x2F(aTransform._11, aTransform._12, aTransform._21,
753 aTransform._22, aTransform._31, aTransform._32);
756 void DCLayerTree::AddSurface(wr::NativeSurfaceId aId,
757 const wr::CompositorSurfaceTransform& aTransform,
758 wr::DeviceIntRect aClipRect,
759 wr::ImageRendering aImageRendering) {
760 auto it = mDCSurfaces.find(aId);
761 MOZ_RELEASE_ASSERT(it != mDCSurfaces.end());
762 const auto surface = it->second.get();
763 const auto visual = surface->GetVisual();
765 wr::DeviceIntPoint virtualOffset = surface->GetVirtualOffset();
767 float sx = aTransform.scale.x;
768 float sy = aTransform.scale.y;
769 float tx = aTransform.offset.x;
770 float ty = aTransform.offset.y;
771 gfx::Matrix transform(sx, 0.0, 0.0, sy, tx, ty);
773 surface->PresentExternalSurface(transform);
775 transform.PreTranslate(-virtualOffset.x, -virtualOffset.y);
777 // The DirectComposition API applies clipping *before* any
778 // transforms/offset, whereas we want the clip applied after. Right now, we
779 // only support rectilinear transforms, and then we transform our clip into
780 // pre-transform coordinate space for it to be applied there.
781 // DirectComposition does have an option for pre-transform clipping, if you
782 // create an explicit IDCompositionEffectGroup object and set a 3D transform
783 // on that. I suspect that will perform worse though, so we should only do
784 // that for complex transforms (which are never provided right now).
785 MOZ_ASSERT(transform.IsRectilinear());
786 gfx::Rect clip = transform.Inverse().TransformBounds(gfx::Rect(
787 aClipRect.min.x, aClipRect.min.y, aClipRect.width(), aClipRect.height()));
788 // Set the clip rect - converting from world space to the pre-offset space
789 // that DC requires for rectangle clips.
790 visual->SetClip(D2DRect(clip));
792 // TODO: The input matrix is a 4x4, but we only support a 3x2 at
793 // the D3D API level (unless we QI to IDCompositionVisual3, which might
794 // not be available?).
795 // Should we assert here, or restrict at the WR API level.
796 visual->SetTransform(D2DMatrix(transform));
798 if (aImageRendering == wr::ImageRendering::Auto) {
799 visual->SetBitmapInterpolationMode(
800 DCOMPOSITION_BITMAP_INTERPOLATION_MODE_LINEAR);
801 } else {
802 visual->SetBitmapInterpolationMode(
803 DCOMPOSITION_BITMAP_INTERPOLATION_MODE_NEAREST_NEIGHBOR);
806 mCurrentLayers.push_back(aId);
809 GLuint DCLayerTree::GetOrCreateFbo(int aWidth, int aHeight) {
810 const auto gl = GetGLContext();
811 GLuint fboId = 0;
813 // Check if we have a cached FBO with matching dimensions
814 for (auto it = mFrameBuffers.begin(); it != mFrameBuffers.end(); ++it) {
815 if (it->width == aWidth && it->height == aHeight) {
816 fboId = it->fboId;
817 it->lastFrameUsed = mCurrentFrame;
818 break;
822 // If not, create a new FBO with attached depth buffer
823 if (fboId == 0) {
824 // Create the depth buffer
825 GLuint depthRboId;
826 gl->fGenRenderbuffers(1, &depthRboId);
827 gl->fBindRenderbuffer(LOCAL_GL_RENDERBUFFER, depthRboId);
828 gl->fRenderbufferStorage(LOCAL_GL_RENDERBUFFER, LOCAL_GL_DEPTH_COMPONENT24,
829 aWidth, aHeight);
831 // Create the framebuffer and attach the depth buffer to it
832 gl->fGenFramebuffers(1, &fboId);
833 gl->fBindFramebuffer(LOCAL_GL_DRAW_FRAMEBUFFER, fboId);
834 gl->fFramebufferRenderbuffer(LOCAL_GL_DRAW_FRAMEBUFFER,
835 LOCAL_GL_DEPTH_ATTACHMENT,
836 LOCAL_GL_RENDERBUFFER, depthRboId);
838 // Store this in the cache for future calls.
839 // TODO(gw): Maybe we should periodically scan this list and remove old
840 // entries that
841 // haven't been used for some time?
842 DCLayerTree::CachedFrameBuffer frame_buffer_info;
843 frame_buffer_info.width = aWidth;
844 frame_buffer_info.height = aHeight;
845 frame_buffer_info.fboId = fboId;
846 frame_buffer_info.depthRboId = depthRboId;
847 frame_buffer_info.lastFrameUsed = mCurrentFrame;
848 mFrameBuffers.AppendElement(frame_buffer_info);
851 return fboId;
854 bool DCLayerTree::EnsureVideoProcessor(const gfx::IntSize& aInputSize,
855 const gfx::IntSize& aOutputSize) {
856 HRESULT hr;
858 if (!mVideoDevice || !mVideoContext) {
859 return false;
862 if (mVideoProcessor && (aInputSize <= mVideoInputSize) &&
863 (aOutputSize <= mVideoOutputSize)) {
864 return true;
867 mVideoProcessor = nullptr;
868 mVideoProcessorEnumerator = nullptr;
870 D3D11_VIDEO_PROCESSOR_CONTENT_DESC desc = {};
871 desc.InputFrameFormat = D3D11_VIDEO_FRAME_FORMAT_PROGRESSIVE;
872 desc.InputFrameRate.Numerator = 60;
873 desc.InputFrameRate.Denominator = 1;
874 desc.InputWidth = aInputSize.width;
875 desc.InputHeight = aInputSize.height;
876 desc.OutputFrameRate.Numerator = 60;
877 desc.OutputFrameRate.Denominator = 1;
878 desc.OutputWidth = aOutputSize.width;
879 desc.OutputHeight = aOutputSize.height;
880 desc.Usage = D3D11_VIDEO_USAGE_PLAYBACK_NORMAL;
882 hr = mVideoDevice->CreateVideoProcessorEnumerator(
883 &desc, getter_AddRefs(mVideoProcessorEnumerator));
884 if (FAILED(hr)) {
885 gfxCriticalNote << "Failed to create VideoProcessorEnumerator: "
886 << gfx::hexa(hr);
887 return false;
890 hr = mVideoDevice->CreateVideoProcessor(mVideoProcessorEnumerator, 0,
891 getter_AddRefs(mVideoProcessor));
892 if (FAILED(hr)) {
893 mVideoProcessor = nullptr;
894 mVideoProcessorEnumerator = nullptr;
895 gfxCriticalNote << "Failed to create VideoProcessor: " << gfx::hexa(hr);
896 return false;
899 // Reduce power cosumption
900 // By default, the driver might perform certain processing tasks
901 // automatically
902 mVideoContext->VideoProcessorSetStreamAutoProcessingMode(mVideoProcessor, 0,
903 FALSE);
905 mVideoInputSize = aInputSize;
906 mVideoOutputSize = aOutputSize;
908 return true;
911 bool DCLayerTree::SupportsHardwareOverlays() {
912 return sGpuOverlayInfo->mSupportsHardwareOverlays;
915 bool DCLayerTree::SupportsSwapChainTearing() {
916 RefPtr<ID3D11Device> device = mDevice;
917 static const bool supported = [device] {
918 RefPtr<IDXGIDevice> dxgiDevice;
919 RefPtr<IDXGIAdapter> adapter;
920 device->QueryInterface((IDXGIDevice**)getter_AddRefs(dxgiDevice));
921 dxgiDevice->GetAdapter(getter_AddRefs(adapter));
923 RefPtr<IDXGIFactory5> dxgiFactory;
924 HRESULT hr = adapter->GetParent(
925 IID_PPV_ARGS((IDXGIFactory5**)getter_AddRefs(dxgiFactory)));
926 if (FAILED(hr)) {
927 return false;
930 BOOL presentAllowTearing = FALSE;
931 hr = dxgiFactory->CheckFeatureSupport(DXGI_FEATURE_PRESENT_ALLOW_TEARING,
932 &presentAllowTearing,
933 sizeof(presentAllowTearing));
934 if (FAILED(hr)) {
935 return false;
938 if (auto* gpuParent = gfx::GPUParent::GetSingleton()) {
939 gpuParent->NotifySwapChainInfo(
940 layers::SwapChainInfo(!!presentAllowTearing));
941 } else if (XRE_IsParentProcess()) {
942 MOZ_ASSERT_UNREACHABLE("unexpected to be called");
944 return !!presentAllowTearing;
945 }();
946 return supported;
949 DXGI_FORMAT DCLayerTree::GetOverlayFormatForSDR() {
950 return sGpuOverlayInfo->mOverlayFormatUsed;
953 static layers::OverlaySupportType FlagsToOverlaySupportType(
954 UINT aFlags, bool aSoftwareOverlaySupported) {
955 if (aFlags & DXGI_OVERLAY_SUPPORT_FLAG_SCALING) {
956 return layers::OverlaySupportType::Scaling;
958 if (aFlags & DXGI_OVERLAY_SUPPORT_FLAG_DIRECT) {
959 return layers::OverlaySupportType::Direct;
961 if (aSoftwareOverlaySupported) {
962 return layers::OverlaySupportType::Software;
964 return layers::OverlaySupportType::None;
967 layers::OverlayInfo DCLayerTree::GetOverlayInfo() {
968 layers::OverlayInfo info;
970 info.mSupportsOverlays = sGpuOverlayInfo->mSupportsHardwareOverlays;
971 info.mNv12Overlay =
972 FlagsToOverlaySupportType(sGpuOverlayInfo->mNv12OverlaySupportFlags,
973 /* aSoftwareOverlaySupported */ false);
974 info.mYuy2Overlay =
975 FlagsToOverlaySupportType(sGpuOverlayInfo->mYuy2OverlaySupportFlags,
976 /* aSoftwareOverlaySupported */ false);
977 info.mBgra8Overlay =
978 FlagsToOverlaySupportType(sGpuOverlayInfo->mBgra8OverlaySupportFlags,
979 /* aSoftwareOverlaySupported */ true);
980 info.mRgb10a2Overlay =
981 FlagsToOverlaySupportType(sGpuOverlayInfo->mRgb10a2OverlaySupportFlags,
982 /* aSoftwareOverlaySupported */ false);
984 return info;
987 DCSurface::DCSurface(wr::DeviceIntSize aTileSize,
988 wr::DeviceIntPoint aVirtualOffset, bool aIsVirtualSurface,
989 bool aIsOpaque, DCLayerTree* aDCLayerTree)
990 : mIsVirtualSurface(aIsVirtualSurface),
991 mDCLayerTree(aDCLayerTree),
992 mTileSize(aTileSize),
993 mIsOpaque(aIsOpaque),
994 mAllocatedRectDirty(true),
995 mVirtualOffset(aVirtualOffset) {}
997 DCSurface::~DCSurface() {}
999 bool DCSurface::Initialize() {
1000 // Create a visual for tiles to attach to, whether virtual or not.
1001 HRESULT hr;
1002 const auto dCompDevice = mDCLayerTree->GetCompositionDevice();
1003 hr = dCompDevice->CreateVisual(getter_AddRefs(mVisual));
1004 if (FAILED(hr)) {
1005 gfxCriticalNote << "Failed to create DCompositionVisual: " << gfx::hexa(hr);
1006 return false;
1009 // If virtual surface is enabled, create and attach to visual, in this case
1010 // the tiles won't own visuals or surfaces.
1011 if (mIsVirtualSurface) {
1012 DXGI_ALPHA_MODE alpha_mode =
1013 mIsOpaque ? DXGI_ALPHA_MODE_IGNORE : DXGI_ALPHA_MODE_PREMULTIPLIED;
1015 hr = dCompDevice->CreateVirtualSurface(
1016 VIRTUAL_SURFACE_SIZE, VIRTUAL_SURFACE_SIZE, DXGI_FORMAT_R8G8B8A8_UNORM,
1017 alpha_mode, getter_AddRefs(mVirtualSurface));
1018 MOZ_ASSERT(SUCCEEDED(hr));
1020 // Bind the surface memory to this visual
1021 hr = mVisual->SetContent(mVirtualSurface);
1022 MOZ_ASSERT(SUCCEEDED(hr));
1025 return true;
1028 void DCSurface::CreateTile(int32_t aX, int32_t aY) {
1029 TileKey key(aX, aY);
1030 MOZ_RELEASE_ASSERT(mDCTiles.find(key) == mDCTiles.end());
1032 auto tile = MakeUnique<DCTile>(mDCLayerTree);
1033 if (!tile->Initialize(aX, aY, mTileSize, mIsVirtualSurface, mIsOpaque,
1034 mVisual)) {
1035 gfxCriticalNote << "Failed to initialize DCTile: " << aX << aY;
1036 return;
1039 if (mIsVirtualSurface) {
1040 mAllocatedRectDirty = true;
1041 } else {
1042 mVisual->AddVisual(tile->GetVisual(), false, nullptr);
1045 mDCTiles[key] = std::move(tile);
1048 void DCSurface::DestroyTile(int32_t aX, int32_t aY) {
1049 TileKey key(aX, aY);
1050 if (mIsVirtualSurface) {
1051 mAllocatedRectDirty = true;
1052 } else {
1053 auto tile = GetTile(aX, aY);
1054 mVisual->RemoveVisual(tile->GetVisual());
1056 mDCTiles.erase(key);
1059 void DCSurface::DirtyAllocatedRect() { mAllocatedRectDirty = true; }
1061 void DCSurface::UpdateAllocatedRect() {
1062 if (mAllocatedRectDirty) {
1063 if (mVirtualSurface) {
1064 // The virtual surface may have holes in it (for example, an empty tile
1065 // that has no primitives). Instead of trimming to a single bounding
1066 // rect, supply the rect of each valid tile to handle this case.
1067 std::vector<RECT> validRects;
1069 for (auto it = mDCTiles.begin(); it != mDCTiles.end(); ++it) {
1070 auto tile = GetTile(it->first.mX, it->first.mY);
1071 RECT rect;
1073 rect.left = (LONG)(mVirtualOffset.x + it->first.mX * mTileSize.width +
1074 tile->mValidRect.x);
1075 rect.top = (LONG)(mVirtualOffset.y + it->first.mY * mTileSize.height +
1076 tile->mValidRect.y);
1077 rect.right = rect.left + tile->mValidRect.width;
1078 rect.bottom = rect.top + tile->mValidRect.height;
1080 validRects.push_back(rect);
1083 mVirtualSurface->Trim(validRects.data(), validRects.size());
1085 // When not using a virtual surface, we still want to reset this
1086 mAllocatedRectDirty = false;
1090 DCTile* DCSurface::GetTile(int32_t aX, int32_t aY) const {
1091 TileKey key(aX, aY);
1092 auto tile_it = mDCTiles.find(key);
1093 MOZ_RELEASE_ASSERT(tile_it != mDCTiles.end());
1094 return tile_it->second.get();
1097 DCSurfaceVideo::DCSurfaceVideo(bool aIsOpaque, DCLayerTree* aDCLayerTree)
1098 : DCSurface(wr::DeviceIntSize{}, wr::DeviceIntPoint{}, false, aIsOpaque,
1099 aDCLayerTree) {}
1101 DCSurfaceVideo::~DCSurfaceVideo() {
1102 ReleaseDecodeSwapChainResources();
1103 MOZ_ASSERT(!mSwapChainSurfaceHandle);
1106 bool IsYUVSwapChainFormat(DXGI_FORMAT aFormat) {
1107 if (aFormat == DXGI_FORMAT_NV12 || aFormat == DXGI_FORMAT_YUY2) {
1108 return true;
1110 return false;
1113 void DCSurfaceVideo::AttachExternalImage(wr::ExternalImageId aExternalImage) {
1114 RenderTextureHost* texture =
1115 RenderThread::Get()->GetRenderTexture(aExternalImage);
1116 MOZ_RELEASE_ASSERT(texture);
1118 if (mPrevTexture == texture) {
1119 return;
1122 // XXX if software decoded video frame format is nv12, it could be used as
1123 // video overlay.
1124 if (!texture || !texture->AsRenderDXGITextureHost() ||
1125 texture->AsRenderDXGITextureHost()->GetFormat() !=
1126 gfx::SurfaceFormat::NV12) {
1127 gfxCriticalNote << "Unsupported RenderTexture for overlay: "
1128 << gfx::hexa(texture);
1129 return;
1132 mRenderTextureHost = texture;
1135 bool DCSurfaceVideo::CalculateSwapChainSize(gfx::Matrix& aTransform) {
1136 if (!mRenderTextureHost) {
1137 MOZ_ASSERT_UNREACHABLE("unexpected to be called");
1138 return false;
1141 mVideoSize = mRenderTextureHost->AsRenderDXGITextureHost()->GetSize(0);
1143 // When RenderTextureHost, swapChainSize or VideoSwapChain are updated,
1144 // DCSurfaceVideo::PresentVideo() needs to be called.
1145 bool needsToPresent = mPrevTexture != mRenderTextureHost;
1146 gfx::IntSize swapChainSize = mVideoSize;
1147 gfx::Matrix transform = aTransform;
1149 // When video is rendered to axis aligned integer rectangle, video scaling
1150 // could be done by VideoProcessor
1151 bool scaleVideoAtVideoProcessor = false;
1152 if (StaticPrefs::gfx_webrender_dcomp_video_vp_scaling_win_AtStartup() &&
1153 aTransform.PreservesAxisAlignedRectangles()) {
1154 gfx::Size scaledSize = gfx::Size(mVideoSize) * aTransform.ScaleFactors();
1155 gfx::IntSize size(int32_t(std::round(scaledSize.width)),
1156 int32_t(std::round(scaledSize.height)));
1157 if (gfx::FuzzyEqual(scaledSize.width, size.width, 0.1f) &&
1158 gfx::FuzzyEqual(scaledSize.height, size.height, 0.1f)) {
1159 scaleVideoAtVideoProcessor = true;
1160 swapChainSize = size;
1164 if (scaleVideoAtVideoProcessor) {
1165 // 4:2:2 subsampled formats like YUY2 must have an even width, and 4:2:0
1166 // subsampled formats like NV12 must have an even width and height.
1167 if (swapChainSize.width % 2 == 1) {
1168 swapChainSize.width += 1;
1170 if (swapChainSize.height % 2 == 1) {
1171 swapChainSize.height += 1;
1173 transform = gfx::Matrix::Translation(aTransform.GetTranslation());
1176 if (!mVideoSwapChain || mSwapChainSize != swapChainSize) {
1177 needsToPresent = true;
1178 ReleaseDecodeSwapChainResources();
1179 // Update mSwapChainSize before creating SwapChain
1180 mSwapChainSize = swapChainSize;
1182 auto swapChainFormat = GetSwapChainFormat();
1183 bool useYUVSwapChain = IsYUVSwapChainFormat(swapChainFormat);
1184 if (useYUVSwapChain) {
1185 // Tries to create YUV SwapChain
1186 CreateVideoSwapChain();
1187 if (!mVideoSwapChain) {
1188 mFailedYuvSwapChain = true;
1189 ReleaseDecodeSwapChainResources();
1191 gfxCriticalNote << "Fallback to RGB SwapChain";
1194 // Tries to create RGB SwapChain
1195 if (!mVideoSwapChain) {
1196 CreateVideoSwapChain();
1200 aTransform = transform;
1202 return needsToPresent;
1205 void DCSurfaceVideo::PresentVideo() {
1206 if (!mRenderTextureHost) {
1207 return;
1210 if (!mVideoSwapChain) {
1211 gfxCriticalNote << "Failed to create VideoSwapChain";
1212 RenderThread::Get()->NotifyWebRenderError(
1213 wr::WebRenderError::VIDEO_OVERLAY);
1214 return;
1217 mVisual->SetContent(mVideoSwapChain);
1219 if (!CallVideoProcessorBlt()) {
1220 auto swapChainFormat = GetSwapChainFormat();
1221 bool useYUVSwapChain = IsYUVSwapChainFormat(swapChainFormat);
1222 if (useYUVSwapChain) {
1223 mFailedYuvSwapChain = true;
1224 ReleaseDecodeSwapChainResources();
1225 return;
1227 RenderThread::Get()->NotifyWebRenderError(
1228 wr::WebRenderError::VIDEO_OVERLAY);
1229 return;
1232 HRESULT hr;
1233 hr = mVideoSwapChain->Present(0, 0);
1234 if (FAILED(hr) && hr != DXGI_STATUS_OCCLUDED) {
1235 gfxCriticalNoteOnce << "video Present failed: " << gfx::hexa(hr);
1238 mPrevTexture = mRenderTextureHost;
1241 DXGI_FORMAT DCSurfaceVideo::GetSwapChainFormat() {
1242 if (mFailedYuvSwapChain || !mDCLayerTree->SupportsHardwareOverlays()) {
1243 return DXGI_FORMAT_B8G8R8A8_UNORM;
1245 return mDCLayerTree->GetOverlayFormatForSDR();
1248 bool DCSurfaceVideo::CreateVideoSwapChain() {
1249 MOZ_ASSERT(mRenderTextureHost);
1251 const auto device = mDCLayerTree->GetDevice();
1253 RefPtr<IDXGIDevice> dxgiDevice;
1254 device->QueryInterface((IDXGIDevice**)getter_AddRefs(dxgiDevice));
1256 RefPtr<IDXGIFactoryMedia> dxgiFactoryMedia;
1258 RefPtr<IDXGIAdapter> adapter;
1259 dxgiDevice->GetAdapter(getter_AddRefs(adapter));
1260 adapter->GetParent(
1261 IID_PPV_ARGS((IDXGIFactoryMedia**)getter_AddRefs(dxgiFactoryMedia)));
1264 mSwapChainSurfaceHandle = gfx::DeviceManagerDx::CreateDCompSurfaceHandle();
1265 if (!mSwapChainSurfaceHandle) {
1266 gfxCriticalNote << "Failed to create DCompSurfaceHandle";
1267 return false;
1270 auto swapChainFormat = GetSwapChainFormat();
1272 DXGI_SWAP_CHAIN_DESC1 desc = {};
1273 desc.Width = mSwapChainSize.width;
1274 desc.Height = mSwapChainSize.height;
1275 desc.Format = swapChainFormat;
1276 desc.Stereo = FALSE;
1277 desc.SampleDesc.Count = 1;
1278 desc.BufferCount = 2;
1279 desc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
1280 desc.Scaling = DXGI_SCALING_STRETCH;
1281 desc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL;
1282 desc.Flags = DXGI_SWAP_CHAIN_FLAG_FULLSCREEN_VIDEO;
1283 if (IsYUVSwapChainFormat(swapChainFormat)) {
1284 desc.Flags |= DXGI_SWAP_CHAIN_FLAG_YUV_VIDEO;
1286 desc.AlphaMode = DXGI_ALPHA_MODE_IGNORE;
1288 HRESULT hr;
1289 hr = dxgiFactoryMedia->CreateSwapChainForCompositionSurfaceHandle(
1290 device, mSwapChainSurfaceHandle, &desc, nullptr,
1291 getter_AddRefs(mVideoSwapChain));
1293 if (FAILED(hr)) {
1294 gfxCriticalNote << "Failed to create video SwapChain: " << gfx::hexa(hr)
1295 << " " << mSwapChainSize;
1296 return false;
1299 mSwapChainFormat = swapChainFormat;
1300 return true;
1303 // TODO: Replace with YUVRangedColorSpace
1304 static Maybe<DXGI_COLOR_SPACE_TYPE> GetSourceDXGIColorSpace(
1305 const gfx::YUVColorSpace aYUVColorSpace,
1306 const gfx::ColorRange aColorRange) {
1307 if (aYUVColorSpace == gfx::YUVColorSpace::BT601) {
1308 if (aColorRange == gfx::ColorRange::FULL) {
1309 return Some(DXGI_COLOR_SPACE_YCBCR_FULL_G22_LEFT_P601);
1310 } else {
1311 return Some(DXGI_COLOR_SPACE_YCBCR_STUDIO_G22_LEFT_P601);
1313 } else if (aYUVColorSpace == gfx::YUVColorSpace::BT709) {
1314 if (aColorRange == gfx::ColorRange::FULL) {
1315 return Some(DXGI_COLOR_SPACE_YCBCR_FULL_G22_LEFT_P709);
1316 } else {
1317 return Some(DXGI_COLOR_SPACE_YCBCR_STUDIO_G22_LEFT_P709);
1319 } else if (aYUVColorSpace == gfx::YUVColorSpace::BT2020) {
1320 if (aColorRange == gfx::ColorRange::FULL) {
1321 // XXX Add SMPTEST2084 handling. HDR content is not handled yet by
1322 // video overlay.
1323 return Some(DXGI_COLOR_SPACE_YCBCR_FULL_G22_LEFT_P2020);
1324 } else {
1325 return Some(DXGI_COLOR_SPACE_YCBCR_STUDIO_G22_LEFT_P2020);
1329 return Nothing();
1332 static Maybe<DXGI_COLOR_SPACE_TYPE> GetSourceDXGIColorSpace(
1333 const gfx::YUVRangedColorSpace aYUVColorSpace) {
1334 const auto info = FromYUVRangedColorSpace(aYUVColorSpace);
1335 return GetSourceDXGIColorSpace(info.space, info.range);
1338 bool DCSurfaceVideo::CallVideoProcessorBlt() {
1339 MOZ_ASSERT(mRenderTextureHost);
1341 HRESULT hr;
1342 const auto videoDevice = mDCLayerTree->GetVideoDevice();
1343 const auto videoContext = mDCLayerTree->GetVideoContext();
1344 const auto texture = mRenderTextureHost->AsRenderDXGITextureHost();
1346 Maybe<DXGI_COLOR_SPACE_TYPE> sourceColorSpace =
1347 GetSourceDXGIColorSpace(texture->GetYUVColorSpace());
1348 if (sourceColorSpace.isNothing()) {
1349 gfxCriticalNote << "Unsupported color space";
1350 return false;
1353 RefPtr<ID3D11Texture2D> texture2D = texture->GetD3D11Texture2DWithGL();
1354 if (!texture2D) {
1355 gfxCriticalNote << "Failed to get D3D11Texture2D";
1356 return false;
1359 if (!mVideoSwapChain) {
1360 return false;
1363 if (!mDCLayerTree->EnsureVideoProcessor(mVideoSize, mSwapChainSize)) {
1364 gfxCriticalNote << "EnsureVideoProcessor Failed";
1365 return false;
1368 RefPtr<IDXGISwapChain3> swapChain3;
1369 mVideoSwapChain->QueryInterface(
1370 (IDXGISwapChain3**)getter_AddRefs(swapChain3));
1371 if (!swapChain3) {
1372 gfxCriticalNote << "Failed to get IDXGISwapChain3";
1373 return false;
1376 RefPtr<ID3D11VideoContext1> videoContext1;
1377 videoContext->QueryInterface(
1378 (ID3D11VideoContext1**)getter_AddRefs(videoContext1));
1379 if (!videoContext1) {
1380 gfxCriticalNote << "Failed to get ID3D11VideoContext1";
1381 return false;
1384 const auto videoProcessor = mDCLayerTree->GetVideoProcessor();
1385 const auto videoProcessorEnumerator =
1386 mDCLayerTree->GetVideoProcessorEnumerator();
1388 DXGI_COLOR_SPACE_TYPE inputColorSpace = sourceColorSpace.ref();
1389 videoContext1->VideoProcessorSetStreamColorSpace1(videoProcessor, 0,
1390 inputColorSpace);
1392 DXGI_COLOR_SPACE_TYPE outputColorSpace =
1393 IsYUVSwapChainFormat(mSwapChainFormat)
1394 ? inputColorSpace
1395 : DXGI_COLOR_SPACE_RGB_FULL_G22_NONE_P709;
1396 hr = swapChain3->SetColorSpace1(outputColorSpace);
1397 if (FAILED(hr)) {
1398 gfxCriticalNoteOnce << "SetColorSpace1 failed: " << gfx::hexa(hr);
1399 RenderThread::Get()->NotifyWebRenderError(
1400 wr::WebRenderError::VIDEO_OVERLAY);
1401 return false;
1403 videoContext1->VideoProcessorSetOutputColorSpace1(videoProcessor,
1404 outputColorSpace);
1406 D3D11_VIDEO_PROCESSOR_INPUT_VIEW_DESC inputDesc = {};
1407 inputDesc.ViewDimension = D3D11_VPIV_DIMENSION_TEXTURE2D;
1408 inputDesc.Texture2D.ArraySlice = texture->ArrayIndex();
1410 RefPtr<ID3D11VideoProcessorInputView> inputView;
1411 hr = videoDevice->CreateVideoProcessorInputView(
1412 texture2D, videoProcessorEnumerator, &inputDesc,
1413 getter_AddRefs(inputView));
1414 if (FAILED(hr)) {
1415 gfxCriticalNote << "ID3D11VideoProcessorInputView creation failed: "
1416 << gfx::hexa(hr);
1417 return false;
1420 D3D11_VIDEO_PROCESSOR_STREAM stream = {};
1421 stream.Enable = true;
1422 stream.OutputIndex = 0;
1423 stream.InputFrameOrField = 0;
1424 stream.PastFrames = 0;
1425 stream.FutureFrames = 0;
1426 stream.pInputSurface = inputView.get();
1428 RECT destRect;
1429 destRect.left = 0;
1430 destRect.top = 0;
1431 destRect.right = mSwapChainSize.width;
1432 destRect.bottom = mSwapChainSize.height;
1434 videoContext->VideoProcessorSetOutputTargetRect(videoProcessor, TRUE,
1435 &destRect);
1436 videoContext->VideoProcessorSetStreamDestRect(videoProcessor, 0, TRUE,
1437 &destRect);
1438 RECT sourceRect;
1439 sourceRect.left = 0;
1440 sourceRect.top = 0;
1441 sourceRect.right = mVideoSize.width;
1442 sourceRect.bottom = mVideoSize.height;
1443 videoContext->VideoProcessorSetStreamSourceRect(videoProcessor, 0, TRUE,
1444 &sourceRect);
1446 if (!mOutputView) {
1447 RefPtr<ID3D11Texture2D> backBuf;
1448 mVideoSwapChain->GetBuffer(0, __uuidof(ID3D11Texture2D),
1449 (void**)getter_AddRefs(backBuf));
1451 D3D11_VIDEO_PROCESSOR_OUTPUT_VIEW_DESC outputDesc = {};
1452 outputDesc.ViewDimension = D3D11_VPOV_DIMENSION_TEXTURE2D;
1453 outputDesc.Texture2D.MipSlice = 0;
1455 hr = videoDevice->CreateVideoProcessorOutputView(
1456 backBuf, videoProcessorEnumerator, &outputDesc,
1457 getter_AddRefs(mOutputView));
1458 if (FAILED(hr)) {
1459 gfxCriticalNote << "ID3D11VideoProcessorOutputView creation failed: "
1460 << gfx::hexa(hr);
1461 return false;
1465 hr = videoContext->VideoProcessorBlt(videoProcessor, mOutputView, 0, 1,
1466 &stream);
1467 if (FAILED(hr)) {
1468 gfxCriticalNote << "VideoProcessorBlt failed: " << gfx::hexa(hr);
1469 return false;
1472 return true;
1475 void DCSurfaceVideo::ReleaseDecodeSwapChainResources() {
1476 mOutputView = nullptr;
1477 mVideoSwapChain = nullptr;
1478 mDecodeSwapChain = nullptr;
1479 mDecodeResource = nullptr;
1480 if (mSwapChainSurfaceHandle) {
1481 ::CloseHandle(mSwapChainSurfaceHandle);
1482 mSwapChainSurfaceHandle = 0;
1486 DCSurfaceHandle::DCSurfaceHandle(bool aIsOpaque, DCLayerTree* aDCLayerTree)
1487 : DCSurface(wr::DeviceIntSize{}, wr::DeviceIntPoint{}, false, aIsOpaque,
1488 aDCLayerTree) {}
1490 void DCSurfaceHandle::AttachExternalImage(wr::ExternalImageId aExternalImage) {
1491 RenderTextureHost* texture =
1492 RenderThread::Get()->GetRenderTexture(aExternalImage);
1493 RenderDcompSurfaceTextureHost* renderTexture =
1494 texture ? texture->AsRenderDcompSurfaceTextureHost() : nullptr;
1495 if (!renderTexture) {
1496 gfxCriticalNote << "Unsupported RenderTexture for DCSurfaceHandle: "
1497 << gfx::hexa(texture);
1498 return;
1501 const auto handle = renderTexture->GetDcompSurfaceHandle();
1502 if (GetSurfaceHandle() == handle) {
1503 return;
1506 LOG_H("AttachExternalImage, ext-image=%" PRIu64 ", texture=%p, handle=%p",
1507 wr::AsUint64(aExternalImage), renderTexture, handle);
1508 mDcompTextureHost = renderTexture;
1511 HANDLE DCSurfaceHandle::GetSurfaceHandle() const {
1512 if (mDcompTextureHost) {
1513 return mDcompTextureHost->GetDcompSurfaceHandle();
1515 return nullptr;
1518 IDCompositionSurface* DCSurfaceHandle::EnsureSurface() {
1519 if (auto* surface = mDcompTextureHost->GetSurface()) {
1520 return surface;
1523 // Texture host hasn't created the surface yet, ask it to create a new one.
1524 RefPtr<IDCompositionDevice> device;
1525 HRESULT hr = mDCLayerTree->GetCompositionDevice()->QueryInterface(
1526 (IDCompositionDevice**)getter_AddRefs(device));
1527 if (FAILED(hr)) {
1528 gfxCriticalNote
1529 << "Failed to convert IDCompositionDevice2 to IDCompositionDevice: "
1530 << gfx::hexa(hr);
1531 return nullptr;
1534 return mDcompTextureHost->CreateSurfaceFromDevice(device);
1537 void DCSurfaceHandle::PresentSurfaceHandle() {
1538 LOG_H("PresentSurfaceHandle");
1539 if (IDCompositionSurface* surface = EnsureSurface()) {
1540 LOG_H("Set surface %p to visual", surface);
1541 mVisual->SetContent(surface);
1542 } else {
1543 mVisual->SetContent(nullptr);
1547 DCTile::DCTile(DCLayerTree* aDCLayerTree) : mDCLayerTree(aDCLayerTree) {}
1549 DCTile::~DCTile() {}
1551 bool DCTile::Initialize(int aX, int aY, wr::DeviceIntSize aSize,
1552 bool aIsVirtualSurface, bool aIsOpaque,
1553 RefPtr<IDCompositionVisual2> mSurfaceVisual) {
1554 if (aSize.width <= 0 || aSize.height <= 0) {
1555 return false;
1558 mSize = aSize;
1559 mIsOpaque = aIsOpaque;
1560 mIsVirtualSurface = aIsVirtualSurface;
1561 mNeedsFullDraw = !aIsVirtualSurface;
1563 if (aIsVirtualSurface) {
1564 // Initially, the entire tile is considered valid, unless it is set by
1565 // the SetTileProperties method.
1566 mValidRect.x = 0;
1567 mValidRect.y = 0;
1568 mValidRect.width = aSize.width;
1569 mValidRect.height = aSize.height;
1570 } else {
1571 HRESULT hr;
1572 const auto dCompDevice = mDCLayerTree->GetCompositionDevice();
1573 // Create the visual and put it in the tree under the surface visual
1574 hr = dCompDevice->CreateVisual(getter_AddRefs(mVisual));
1575 if (FAILED(hr)) {
1576 gfxCriticalNote << "Failed to CreateVisual for DCTile: " << gfx::hexa(hr);
1577 return false;
1579 mSurfaceVisual->AddVisual(mVisual, false, nullptr);
1580 // Position the tile relative to the surface visual
1581 mVisual->SetOffsetX(aX * aSize.width);
1582 mVisual->SetOffsetY(aY * aSize.height);
1583 // Clip the visual so it doesn't show anything until we update it
1584 D2D_RECT_F clip = {0, 0, 0, 0};
1585 mVisual->SetClip(clip);
1586 // Create the underlying pixel buffer.
1587 mCompositionSurface = CreateCompositionSurface(aSize, aIsOpaque);
1588 if (!mCompositionSurface) {
1589 return false;
1591 hr = mVisual->SetContent(mCompositionSurface);
1592 if (FAILED(hr)) {
1593 gfxCriticalNote << "Failed to SetContent for DCTile: " << gfx::hexa(hr);
1594 return false;
1598 return true;
1601 RefPtr<IDCompositionSurface> DCTile::CreateCompositionSurface(
1602 wr::DeviceIntSize aSize, bool aIsOpaque) {
1603 HRESULT hr;
1604 const auto dCompDevice = mDCLayerTree->GetCompositionDevice();
1605 const auto alphaMode =
1606 aIsOpaque ? DXGI_ALPHA_MODE_IGNORE : DXGI_ALPHA_MODE_PREMULTIPLIED;
1607 RefPtr<IDCompositionSurface> compositionSurface;
1609 hr = dCompDevice->CreateSurface(aSize.width, aSize.height,
1610 DXGI_FORMAT_R8G8B8A8_UNORM, alphaMode,
1611 getter_AddRefs(compositionSurface));
1612 if (FAILED(hr)) {
1613 gfxCriticalNote << "Failed to CreateSurface for DCTile: " << gfx::hexa(hr);
1614 return nullptr;
1616 return compositionSurface;
1619 RefPtr<IDCompositionSurface> DCTile::Bind(wr::DeviceIntRect aValidRect) {
1620 if (mVisual != nullptr) {
1621 // Tile owns a visual, set the size of the visual to match the portion we
1622 // want to be visible.
1623 D2D_RECT_F clip_rect;
1624 clip_rect.left = aValidRect.min.x;
1625 clip_rect.top = aValidRect.min.y;
1626 clip_rect.right = aValidRect.max.x;
1627 clip_rect.bottom = aValidRect.max.y;
1628 mVisual->SetClip(clip_rect);
1630 return mCompositionSurface;
1633 GLuint DCLayerTree::CreateEGLSurfaceForCompositionSurface(
1634 wr::DeviceIntRect aDirtyRect, wr::DeviceIntPoint* aOffset,
1635 RefPtr<IDCompositionSurface> aCompositionSurface,
1636 wr::DeviceIntPoint aSurfaceOffset) {
1637 MOZ_ASSERT(aCompositionSurface.get());
1639 HRESULT hr;
1640 const auto gl = GetGLContext();
1641 RefPtr<ID3D11Texture2D> backBuf;
1642 POINT offset;
1644 RECT update_rect;
1645 update_rect.left = aSurfaceOffset.x + aDirtyRect.min.x;
1646 update_rect.top = aSurfaceOffset.y + aDirtyRect.min.y;
1647 update_rect.right = aSurfaceOffset.x + aDirtyRect.max.x;
1648 update_rect.bottom = aSurfaceOffset.y + aDirtyRect.max.y;
1649 hr = aCompositionSurface->BeginDraw(&update_rect, __uuidof(ID3D11Texture2D),
1650 (void**)getter_AddRefs(backBuf), &offset);
1652 if (FAILED(hr)) {
1653 LayoutDeviceIntRect rect = widget::WinUtils::ToIntRect(update_rect);
1655 gfxCriticalNote << "DCompositionSurface::BeginDraw failed: "
1656 << gfx::hexa(hr) << " " << rect;
1657 RenderThread::Get()->HandleWebRenderError(WebRenderError::BEGIN_DRAW);
1658 return false;
1661 // DC includes the origin of the dirty / update rect in the draw offset,
1662 // undo that here since WR expects it to be an absolute offset.
1663 offset.x -= aDirtyRect.min.x;
1664 offset.y -= aDirtyRect.min.y;
1666 D3D11_TEXTURE2D_DESC desc;
1667 backBuf->GetDesc(&desc);
1669 const auto& gle = gl::GLContextEGL::Cast(gl);
1670 const auto& egl = gle->mEgl;
1672 const auto buffer = reinterpret_cast<EGLClientBuffer>(backBuf.get());
1674 // Construct an EGLImage wrapper around the D3D texture for ANGLE.
1675 const EGLint attribs[] = {LOCAL_EGL_NONE};
1676 mEGLImage = egl->fCreateImage(EGL_NO_CONTEXT, LOCAL_EGL_D3D11_TEXTURE_ANGLE,
1677 buffer, attribs);
1679 // Get the current FBO and RBO id, so we can restore them later
1680 GLint currentFboId, currentRboId;
1681 gl->fGetIntegerv(LOCAL_GL_DRAW_FRAMEBUFFER_BINDING, &currentFboId);
1682 gl->fGetIntegerv(LOCAL_GL_RENDERBUFFER_BINDING, &currentRboId);
1684 // Create a render buffer object that is backed by the EGL image.
1685 gl->fGenRenderbuffers(1, &mColorRBO);
1686 gl->fBindRenderbuffer(LOCAL_GL_RENDERBUFFER, mColorRBO);
1687 gl->fEGLImageTargetRenderbufferStorage(LOCAL_GL_RENDERBUFFER, mEGLImage);
1689 // Get or create an FBO for the specified dimensions
1690 GLuint fboId = GetOrCreateFbo(desc.Width, desc.Height);
1692 // Attach the new renderbuffer to the FBO
1693 gl->fBindFramebuffer(LOCAL_GL_DRAW_FRAMEBUFFER, fboId);
1694 gl->fFramebufferRenderbuffer(LOCAL_GL_DRAW_FRAMEBUFFER,
1695 LOCAL_GL_COLOR_ATTACHMENT0,
1696 LOCAL_GL_RENDERBUFFER, mColorRBO);
1698 // Restore previous FBO and RBO bindings
1699 gl->fBindFramebuffer(LOCAL_GL_DRAW_FRAMEBUFFER, currentFboId);
1700 gl->fBindRenderbuffer(LOCAL_GL_RENDERBUFFER, currentRboId);
1702 aOffset->x = offset.x;
1703 aOffset->y = offset.y;
1705 return fboId;
1708 void DCLayerTree::DestroyEGLSurface() {
1709 const auto gl = GetGLContext();
1711 if (mColorRBO) {
1712 gl->fDeleteRenderbuffers(1, &mColorRBO);
1713 mColorRBO = 0;
1716 if (mEGLImage) {
1717 const auto& gle = gl::GLContextEGL::Cast(gl);
1718 const auto& egl = gle->mEgl;
1719 egl->fDestroyImage(mEGLImage);
1720 mEGLImage = EGL_NO_IMAGE;
1724 // -
1726 color::ColorProfileDesc DCLayerTree::QueryOutputColorProfile() {
1727 // GPU process can't simply init gfxPlatform, (and we don't need most of it)
1728 // but we do need gfxPlatform::GetCMSOutputProfile().
1729 // So we steal what we need through the window:
1730 const auto outputProfileData =
1731 gfxWindowsPlatform::GetPlatformCMSOutputProfileData_Impl();
1733 const auto qcmsProfile = qcms_profile_from_memory(
1734 outputProfileData.Elements(), outputProfileData.Length());
1735 MOZ_ASSERT(qcmsProfile);
1736 const auto release =
1737 MakeScopeExit([&]() { qcms_profile_release(qcmsProfile); });
1739 const auto ret = color::ColorProfileDesc::From(*qcmsProfile);
1740 bool print = gfxEnv::MOZ_GL_SPEW();
1741 if (print) {
1742 const auto gammaGuess = color::GuessGamma(ret.linearFromTf.r);
1743 printf_stderr(
1744 "Display profile:\n"
1745 " Approx Gamma: %f\n"
1746 " XYZ-D65 Red : %f, %f, %f\n"
1747 " XYZ-D65 Green: %f, %f, %f\n"
1748 " XYZ-D65 Blue : %f, %f, %f\n",
1749 gammaGuess, ret.xyzd65FromLinearRgb.at(0, 0),
1750 ret.xyzd65FromLinearRgb.at(0, 1), ret.xyzd65FromLinearRgb.at(0, 2),
1752 ret.xyzd65FromLinearRgb.at(1, 0), ret.xyzd65FromLinearRgb.at(1, 1),
1753 ret.xyzd65FromLinearRgb.at(1, 2),
1755 ret.xyzd65FromLinearRgb.at(2, 0), ret.xyzd65FromLinearRgb.at(2, 1),
1756 ret.xyzd65FromLinearRgb.at(2, 2));
1759 return ret;
1762 inline D2D1_MATRIX_5X4_F to_D2D1_MATRIX_5X4_F(const color::mat4& m) {
1763 return D2D1_MATRIX_5X4_F{{{
1764 m.rows[0][0],
1765 m.rows[1][0],
1766 m.rows[2][0],
1767 m.rows[3][0],
1768 m.rows[0][1],
1769 m.rows[1][1],
1770 m.rows[2][1],
1771 m.rows[3][1],
1772 m.rows[0][2],
1773 m.rows[1][2],
1774 m.rows[2][2],
1775 m.rows[3][2],
1776 m.rows[0][3],
1777 m.rows[1][3],
1778 m.rows[2][3],
1779 m.rows[3][3],
1784 }}};
1787 ColorManagementChain ColorManagementChain::From(
1788 IDCompositionDevice3& dcomp,
1789 const color::ColorProfileConversionDesc& conv) {
1790 auto ret = ColorManagementChain{};
1792 #if !defined(MOZ_MINGW_DCOMP_H_OLD)
1794 const auto Append = [&](const RefPtr<IDCompositionFilterEffect>& afterLast) {
1795 if (ret.last) {
1796 afterLast->SetInput(0, ret.last, 0);
1798 ret.last = afterLast;
1801 const auto MaybeAppendColorMatrix = [&](const color::mat4& m) {
1802 RefPtr<IDCompositionColorMatrixEffect> e;
1803 if (approx(m, color::mat4::Identity())) return e;
1804 dcomp.CreateColorMatrixEffect(getter_AddRefs(e));
1805 MOZ_ASSERT(e);
1806 if (!e) return e;
1807 e->SetMatrix(to_D2D1_MATRIX_5X4_F(m));
1808 Append(e);
1809 return e;
1811 const auto MaybeAppendTableTransfer = [&](const color::RgbTransferTables& t) {
1812 RefPtr<IDCompositionTableTransferEffect> e;
1813 if (!t.r.size() && !t.g.size() && !t.b.size()) return e;
1814 dcomp.CreateTableTransferEffect(getter_AddRefs(e));
1815 MOZ_ASSERT(e);
1816 if (!e) return e;
1817 e->SetRedTable(t.r.data(), t.r.size());
1818 e->SetGreenTable(t.g.data(), t.g.size());
1819 e->SetBlueTable(t.b.data(), t.b.size());
1820 Append(e);
1821 return e;
1824 ret.srcRgbFromSrcYuv = MaybeAppendColorMatrix(conv.srcRgbFromSrcYuv);
1825 ret.srcLinearFromSrcTf = MaybeAppendTableTransfer(conv.srcLinearFromSrcTf);
1826 ret.dstLinearFromSrcLinear =
1827 MaybeAppendColorMatrix(color::mat4(conv.dstLinearFromSrcLinear));
1828 ret.dstTfFromDstLinear = MaybeAppendTableTransfer(conv.dstTfFromDstLinear);
1830 #endif // !defined(MOZ_MINGW_DCOMP_H_OLD)
1832 return ret;
1835 ColorManagementChain::~ColorManagementChain() = default;
1837 } // namespace wr
1838 } // namespace mozilla
1840 #undef LOG_H