Bug 1799258 - Ask dcomp.h to define IDCompositionFilterEffect. r=gfx-reviewers,bradwerth
[gecko.git] / gfx / webrender_bindings / DCLayerTree.cpp
blob9be2ce7472ef19f18386d11f24e2a02d141d31d3
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 #if (_WIN32_WINNT < _WIN32_WINNT_WIN10)
13 # define XSTR(x) STR(x)
14 # define STR(x) #x
15 // clang-format off
17 # pragma message "IDCompositionFilterEffect in dcomp.h requires _WIN32_WINNT >= _WIN32_WINNT_WIN10."
18 // Pedantically, it actually requires _WIN32_WINNT_WINTHRESHOLD, but that's the
19 // same as _WIN32_WINNT_WIN10.
21 # pragma message "Forcing NTDDI_VERSION " XSTR(NTDDI_VERSION) " -> " XSTR(NTDDI_WIN10)
22 # undef NTDDI_VERSION
23 # define NTDDI_VERSION NTDDI_WIN10
25 # pragma message "Forcing _WIN32_WINNT " XSTR(_WIN32_WINNT) " -> " XSTR(_WIN32_WINNT_WIN10)
26 # undef _WIN32_WINNT
27 # define _WIN32_WINNT _WIN32_WINNT_WIN10
29 // clang-format on
30 # undef STR
31 # undef XSTR
33 #endif
35 #include <d3d11.h>
36 #include <d3d11_1.h>
37 #include <dcomp.h>
38 #include <dxgi1_2.h>
40 // -
42 #include "gfxWindowsPlatform.h"
43 #include "GLContext.h"
44 #include "GLContextEGL.h"
45 #include "mozilla/gfx/DeviceManagerDx.h"
46 #include "mozilla/gfx/Logging.h"
47 #include "mozilla/gfx/gfxVars.h"
48 #include "mozilla/gfx/GPUParent.h"
49 #include "mozilla/gfx/Matrix.h"
50 #include "mozilla/StaticPrefs_gfx.h"
51 #include "mozilla/webrender/RenderD3D11TextureHost.h"
52 #include "mozilla/webrender/RenderDcompSurfaceTextureHost.h"
53 #include "mozilla/webrender/RenderTextureHost.h"
54 #include "mozilla/webrender/RenderThread.h"
55 #include "mozilla/WindowsVersion.h"
56 #include "mozilla/Telemetry.h"
57 #include "nsPrintfCString.h"
58 #include "WinUtils.h"
60 namespace mozilla {
61 namespace wr {
63 extern LazyLogModule gRenderThreadLog;
64 #define LOG(...) MOZ_LOG(gRenderThreadLog, LogLevel::Debug, (__VA_ARGS__))
66 #define LOG_H(msg, ...) \
67 MOZ_LOG(gDcompSurface, LogLevel::Debug, \
68 ("DCSurfaceHandle=%p, " msg, this, ##__VA_ARGS__))
70 UniquePtr<GpuOverlayInfo> DCLayerTree::sGpuOverlayInfo;
72 /* static */
73 UniquePtr<DCLayerTree> DCLayerTree::Create(gl::GLContext* aGL,
74 EGLConfig aEGLConfig,
75 ID3D11Device* aDevice,
76 ID3D11DeviceContext* aCtx,
77 HWND aHwnd, nsACString& aError) {
78 RefPtr<IDCompositionDevice2> dCompDevice =
79 gfx::DeviceManagerDx::Get()->GetDirectCompositionDevice();
80 if (!dCompDevice) {
81 aError.Assign("DCLayerTree(no device)"_ns);
82 return nullptr;
85 auto layerTree =
86 MakeUnique<DCLayerTree>(aGL, aEGLConfig, aDevice, aCtx, dCompDevice);
87 if (!layerTree->Initialize(aHwnd, aError)) {
88 return nullptr;
91 return layerTree;
94 void DCLayerTree::Shutdown() { DCLayerTree::sGpuOverlayInfo = nullptr; }
96 DCLayerTree::DCLayerTree(gl::GLContext* aGL, EGLConfig aEGLConfig,
97 ID3D11Device* aDevice, ID3D11DeviceContext* aCtx,
98 IDCompositionDevice2* aCompositionDevice)
99 : mGL(aGL),
100 mEGLConfig(aEGLConfig),
101 mDevice(aDevice),
102 mCtx(aCtx),
103 mCompositionDevice(aCompositionDevice),
104 mDebugCounter(false),
105 mDebugVisualRedrawRegions(false),
106 mEGLImage(EGL_NO_IMAGE),
107 mColorRBO(0),
108 mPendingCommit(false) {
109 LOG("DCLayerTree::DCLayerTree()");
112 DCLayerTree::~DCLayerTree() {
113 LOG("DCLayerTree::~DCLayerTree()");
115 ReleaseNativeCompositorResources();
118 void DCLayerTree::ReleaseNativeCompositorResources() {
119 const auto gl = GetGLContext();
121 DestroyEGLSurface();
123 // Delete any cached FBO objects
124 for (auto it = mFrameBuffers.begin(); it != mFrameBuffers.end(); ++it) {
125 gl->fDeleteRenderbuffers(1, &it->depthRboId);
126 gl->fDeleteFramebuffers(1, &it->fboId);
130 bool DCLayerTree::Initialize(HWND aHwnd, nsACString& aError) {
131 HRESULT hr;
133 RefPtr<IDCompositionDesktopDevice> desktopDevice;
134 hr = mCompositionDevice->QueryInterface(
135 (IDCompositionDesktopDevice**)getter_AddRefs(desktopDevice));
136 if (FAILED(hr)) {
137 aError.Assign(nsPrintfCString(
138 "DCLayerTree(get IDCompositionDesktopDevice failed %lx)", hr));
139 return false;
142 hr = desktopDevice->CreateTargetForHwnd(aHwnd, TRUE,
143 getter_AddRefs(mCompositionTarget));
144 if (FAILED(hr)) {
145 aError.Assign(nsPrintfCString(
146 "DCLayerTree(create DCompositionTarget failed %lx)", hr));
147 return false;
150 hr = mCompositionDevice->CreateVisual(getter_AddRefs(mRootVisual));
151 if (FAILED(hr)) {
152 aError.Assign(nsPrintfCString(
153 "DCLayerTree(create root DCompositionVisual failed %lx)", hr));
154 return false;
157 hr =
158 mCompositionDevice->CreateVisual(getter_AddRefs(mDefaultSwapChainVisual));
159 if (FAILED(hr)) {
160 aError.Assign(nsPrintfCString(
161 "DCLayerTree(create swap chain DCompositionVisual failed %lx)", hr));
162 return false;
165 if (gfx::gfxVars::UseWebRenderDCompVideoOverlayWin()) {
166 if (!InitializeVideoOverlaySupport()) {
167 RenderThread::Get()->HandleWebRenderError(WebRenderError::VIDEO_OVERLAY);
170 if (!sGpuOverlayInfo) {
171 // Set default if sGpuOverlayInfo was not set.
172 sGpuOverlayInfo = MakeUnique<GpuOverlayInfo>();
175 // Initialize SwapChainInfo
176 SupportsSwapChainTearing();
178 mCompositionTarget->SetRoot(mRootVisual);
179 // Set interporation mode to nearest, to ensure 1:1 sampling.
180 // By default, a visual inherits the interpolation mode of the parent visual.
181 // If no visuals set the interpolation mode, the default for the entire visual
182 // tree is nearest neighbor interpolation.
183 mRootVisual->SetBitmapInterpolationMode(
184 DCOMPOSITION_BITMAP_INTERPOLATION_MODE_NEAREST_NEIGHBOR);
185 return true;
188 bool FlagsSupportsOverlays(UINT flags) {
189 return (flags & (DXGI_OVERLAY_SUPPORT_FLAG_DIRECT |
190 DXGI_OVERLAY_SUPPORT_FLAG_SCALING));
193 // A warpper of IDXGIOutput4::CheckOverlayColorSpaceSupport()
194 bool CheckOverlayColorSpaceSupport(DXGI_FORMAT aDxgiFormat,
195 DXGI_COLOR_SPACE_TYPE aDxgiColorSpace,
196 RefPtr<IDXGIOutput> aOutput,
197 RefPtr<ID3D11Device> aD3d11Device) {
198 UINT colorSpaceSupportFlags = 0;
199 RefPtr<IDXGIOutput4> output4;
201 if (FAILED(aOutput->QueryInterface(__uuidof(IDXGIOutput4),
202 getter_AddRefs(output4)))) {
203 return false;
206 if (FAILED(output4->CheckOverlayColorSpaceSupport(
207 aDxgiFormat, aDxgiColorSpace, aD3d11Device,
208 &colorSpaceSupportFlags))) {
209 return false;
212 return (colorSpaceSupportFlags &
213 DXGI_OVERLAY_COLOR_SPACE_SUPPORT_FLAG_PRESENT);
216 bool DCLayerTree::InitializeVideoOverlaySupport() {
217 MOZ_ASSERT(IsWin10AnniversaryUpdateOrLater());
219 HRESULT hr;
221 hr = mDevice->QueryInterface(
222 (ID3D11VideoDevice**)getter_AddRefs(mVideoDevice));
223 if (FAILED(hr)) {
224 gfxCriticalNote << "Failed to get D3D11VideoDevice: " << gfx::hexa(hr);
225 return false;
228 hr =
229 mCtx->QueryInterface((ID3D11VideoContext**)getter_AddRefs(mVideoContext));
230 if (FAILED(hr)) {
231 gfxCriticalNote << "Failed to get D3D11VideoContext: " << gfx::hexa(hr);
232 return false;
235 if (sGpuOverlayInfo) {
236 return true;
239 UniquePtr<GpuOverlayInfo> info = MakeUnique<GpuOverlayInfo>();
241 RefPtr<IDXGIDevice> dxgiDevice;
242 RefPtr<IDXGIAdapter> adapter;
243 mDevice->QueryInterface((IDXGIDevice**)getter_AddRefs(dxgiDevice));
244 dxgiDevice->GetAdapter(getter_AddRefs(adapter));
246 unsigned int i = 0;
247 while (true) {
248 RefPtr<IDXGIOutput> output;
249 if (FAILED(adapter->EnumOutputs(i++, getter_AddRefs(output)))) {
250 break;
252 RefPtr<IDXGIOutput3> output3;
253 if (FAILED(output->QueryInterface(__uuidof(IDXGIOutput3),
254 getter_AddRefs(output3)))) {
255 break;
258 output3->CheckOverlaySupport(DXGI_FORMAT_NV12, mDevice,
259 &info->mNv12OverlaySupportFlags);
260 output3->CheckOverlaySupport(DXGI_FORMAT_YUY2, mDevice,
261 &info->mYuy2OverlaySupportFlags);
262 output3->CheckOverlaySupport(DXGI_FORMAT_B8G8R8A8_UNORM, mDevice,
263 &info->mBgra8OverlaySupportFlags);
264 output3->CheckOverlaySupport(DXGI_FORMAT_R10G10B10A2_UNORM, mDevice,
265 &info->mRgb10a2OverlaySupportFlags);
267 if (FlagsSupportsOverlays(info->mNv12OverlaySupportFlags)) {
268 // NV12 format is preferred if it's supported.
269 info->mOverlayFormatUsed = DXGI_FORMAT_NV12;
270 info->mSupportsHardwareOverlays = true;
273 if (!info->mSupportsHardwareOverlays &&
274 FlagsSupportsOverlays(info->mYuy2OverlaySupportFlags)) {
275 // If NV12 isn't supported, fallback to YUY2 if it's supported.
276 info->mOverlayFormatUsed = DXGI_FORMAT_YUY2;
277 info->mSupportsHardwareOverlays = true;
280 // RGB10A2 overlay is used for displaying HDR content. In Intel's
281 // platform, RGB10A2 overlay is enabled only when
282 // DXGI_COLOR_SPACE_RGB_FULL_G2084_NONE_P2020 is supported.
283 if (FlagsSupportsOverlays(info->mRgb10a2OverlaySupportFlags)) {
284 if (!CheckOverlayColorSpaceSupport(
285 DXGI_FORMAT_R10G10B10A2_UNORM,
286 DXGI_COLOR_SPACE_RGB_FULL_G2084_NONE_P2020, output, mDevice))
287 info->mRgb10a2OverlaySupportFlags = 0;
290 // Early out after the first output that reports overlay support. All
291 // outputs are expected to report the same overlay support according to
292 // Microsoft's WDDM documentation:
293 // https://docs.microsoft.com/en-us/windows-hardware/drivers/display/multiplane-overlay-hardware-requirements
294 if (info->mSupportsHardwareOverlays) {
295 break;
299 if (!StaticPrefs::gfx_webrender_dcomp_video_yuv_overlay_win_AtStartup()) {
300 info->mOverlayFormatUsed = DXGI_FORMAT_B8G8R8A8_UNORM;
301 info->mSupportsHardwareOverlays = false;
304 info->mSupportsOverlays = info->mSupportsHardwareOverlays;
306 sGpuOverlayInfo = std::move(info);
308 if (auto* gpuParent = gfx::GPUParent::GetSingleton()) {
309 gpuParent->NotifyOverlayInfo(GetOverlayInfo());
312 return true;
315 DCSurface* DCLayerTree::GetSurface(wr::NativeSurfaceId aId) const {
316 auto surface_it = mDCSurfaces.find(aId);
317 MOZ_RELEASE_ASSERT(surface_it != mDCSurfaces.end());
318 return surface_it->second.get();
321 void DCLayerTree::SetDefaultSwapChain(IDXGISwapChain1* aSwapChain) {
322 LOG("DCLayerTree::SetDefaultSwapChain()");
324 mRootVisual->AddVisual(mDefaultSwapChainVisual, TRUE, nullptr);
325 mDefaultSwapChainVisual->SetContent(aSwapChain);
326 // Default SwapChain's visual does not need linear interporation.
327 mDefaultSwapChainVisual->SetBitmapInterpolationMode(
328 DCOMPOSITION_BITMAP_INTERPOLATION_MODE_NEAREST_NEIGHBOR);
329 mPendingCommit = true;
332 void DCLayerTree::MaybeUpdateDebug() {
333 bool updated = false;
334 updated |= MaybeUpdateDebugCounter();
335 updated |= MaybeUpdateDebugVisualRedrawRegions();
336 if (updated) {
337 mPendingCommit = true;
341 void DCLayerTree::MaybeCommit() {
342 if (!mPendingCommit) {
343 return;
345 mCompositionDevice->Commit();
348 void DCLayerTree::WaitForCommitCompletion() {
349 mCompositionDevice->WaitForCommitCompletion();
352 void DCLayerTree::DisableNativeCompositor() {
353 MOZ_ASSERT(mCurrentSurface.isNothing());
354 MOZ_ASSERT(mCurrentLayers.empty());
356 ReleaseNativeCompositorResources();
357 mPrevLayers.clear();
358 mRootVisual->RemoveAllVisuals();
361 bool DCLayerTree::MaybeUpdateDebugCounter() {
362 bool debugCounter = StaticPrefs::gfx_webrender_debug_dcomp_counter();
363 if (mDebugCounter == debugCounter) {
364 return false;
367 RefPtr<IDCompositionDeviceDebug> debugDevice;
368 HRESULT hr = mCompositionDevice->QueryInterface(
369 (IDCompositionDeviceDebug**)getter_AddRefs(debugDevice));
370 if (FAILED(hr)) {
371 return false;
374 if (debugCounter) {
375 debugDevice->EnableDebugCounters();
376 } else {
377 debugDevice->DisableDebugCounters();
380 mDebugCounter = debugCounter;
381 return true;
384 bool DCLayerTree::MaybeUpdateDebugVisualRedrawRegions() {
385 bool debugVisualRedrawRegions =
386 StaticPrefs::gfx_webrender_debug_dcomp_redraw_regions();
387 if (mDebugVisualRedrawRegions == debugVisualRedrawRegions) {
388 return false;
391 RefPtr<IDCompositionVisualDebug> visualDebug;
392 HRESULT hr = mRootVisual->QueryInterface(
393 (IDCompositionVisualDebug**)getter_AddRefs(visualDebug));
394 if (FAILED(hr)) {
395 return false;
398 if (debugVisualRedrawRegions) {
399 visualDebug->EnableRedrawRegions();
400 } else {
401 visualDebug->DisableRedrawRegions();
404 mDebugVisualRedrawRegions = debugVisualRedrawRegions;
405 return true;
408 void DCLayerTree::CompositorBeginFrame() { mCurrentFrame++; }
410 void DCLayerTree::CompositorEndFrame() {
411 auto start = TimeStamp::Now();
412 // Check if the visual tree of surfaces is the same as last frame.
413 bool same = mPrevLayers == mCurrentLayers;
415 if (!same) {
416 // If not, we need to rebuild the visual tree. Note that addition or
417 // removal of tiles no longer needs to rebuild the main visual tree
418 // here, since they are added as children of the surface visual.
419 mRootVisual->RemoveAllVisuals();
422 for (auto it = mCurrentLayers.begin(); it != mCurrentLayers.end(); ++it) {
423 auto surface_it = mDCSurfaces.find(*it);
424 MOZ_RELEASE_ASSERT(surface_it != mDCSurfaces.end());
425 const auto surface = surface_it->second.get();
426 // Ensure surface is trimmed to updated tile valid rects
427 surface->UpdateAllocatedRect();
428 if (!same) {
429 // Add surfaces in z-order they were added to the scene.
430 const auto visual = surface->GetVisual();
431 mRootVisual->AddVisual(visual, false, nullptr);
435 mPrevLayers.swap(mCurrentLayers);
436 mCurrentLayers.clear();
438 mCompositionDevice->Commit();
440 auto end = TimeStamp::Now();
441 mozilla::Telemetry::Accumulate(mozilla::Telemetry::COMPOSITE_SWAP_TIME,
442 (end - start).ToMilliseconds() * 10.);
444 // Remove any framebuffers that haven't been
445 // used in the last 60 frames.
447 // This should use nsTArray::RemoveElementsBy once
448 // CachedFrameBuffer is able to properly destroy
449 // itself in the destructor.
450 const auto gl = GetGLContext();
451 for (uint32_t i = 0, len = mFrameBuffers.Length(); i < len; ++i) {
452 auto& fb = mFrameBuffers[i];
453 if ((mCurrentFrame - fb.lastFrameUsed) > 60) {
454 gl->fDeleteRenderbuffers(1, &fb.depthRboId);
455 gl->fDeleteFramebuffers(1, &fb.fboId);
456 mFrameBuffers.UnorderedRemoveElementAt(i);
457 --i; // Examine the element again, if necessary.
458 --len;
463 void DCLayerTree::Bind(wr::NativeTileId aId, wr::DeviceIntPoint* aOffset,
464 uint32_t* aFboId, wr::DeviceIntRect aDirtyRect,
465 wr::DeviceIntRect aValidRect) {
466 auto surface = GetSurface(aId.surface_id);
467 auto tile = surface->GetTile(aId.x, aId.y);
468 wr::DeviceIntPoint targetOffset{0, 0};
470 // If tile owns an IDCompositionSurface we use it, otherwise we're using an
471 // IDCompositionVirtualSurface owned by the DCSurface.
472 RefPtr<IDCompositionSurface> compositionSurface;
473 if (surface->mIsVirtualSurface) {
474 gfx::IntRect validRect(aValidRect.min.x, aValidRect.min.y,
475 aValidRect.width(), aValidRect.height());
476 if (!tile->mValidRect.IsEqualEdges(validRect)) {
477 tile->mValidRect = validRect;
478 surface->DirtyAllocatedRect();
480 wr::DeviceIntSize tileSize = surface->GetTileSize();
481 compositionSurface = surface->GetCompositionSurface();
482 wr::DeviceIntPoint virtualOffset = surface->GetVirtualOffset();
483 targetOffset.x = virtualOffset.x + tileSize.width * aId.x;
484 targetOffset.y = virtualOffset.y + tileSize.height * aId.y;
485 } else {
486 compositionSurface = tile->Bind(aValidRect);
489 if (tile->mNeedsFullDraw) {
490 // dcomp requires that the first BeginDraw on a non-virtual surface is the
491 // full size of the pixel buffer.
492 auto tileSize = surface->GetTileSize();
493 aDirtyRect.min.x = 0;
494 aDirtyRect.min.y = 0;
495 aDirtyRect.max.x = tileSize.width;
496 aDirtyRect.max.y = tileSize.height;
497 tile->mNeedsFullDraw = false;
500 *aFboId = CreateEGLSurfaceForCompositionSurface(
501 aDirtyRect, aOffset, compositionSurface, targetOffset);
502 mCurrentSurface = Some(compositionSurface);
505 void DCLayerTree::Unbind() {
506 if (mCurrentSurface.isNothing()) {
507 return;
510 RefPtr<IDCompositionSurface> surface = mCurrentSurface.ref();
511 surface->EndDraw();
513 DestroyEGLSurface();
514 mCurrentSurface = Nothing();
517 void DCLayerTree::CreateSurface(wr::NativeSurfaceId aId,
518 wr::DeviceIntPoint aVirtualOffset,
519 wr::DeviceIntSize aTileSize, bool aIsOpaque) {
520 auto it = mDCSurfaces.find(aId);
521 MOZ_RELEASE_ASSERT(it == mDCSurfaces.end());
522 if (it != mDCSurfaces.end()) {
523 // DCSurface already exists.
524 return;
527 // Tile size needs to be positive.
528 if (aTileSize.width <= 0 || aTileSize.height <= 0) {
529 gfxCriticalNote << "TileSize is not positive aId: " << wr::AsUint64(aId)
530 << " aTileSize(" << aTileSize.width << ","
531 << aTileSize.height << ")";
534 bool isVirtualSurface =
535 StaticPrefs::gfx_webrender_dcomp_use_virtual_surfaces_AtStartup();
536 auto surface = MakeUnique<DCSurface>(aTileSize, aVirtualOffset,
537 isVirtualSurface, aIsOpaque, this);
538 if (!surface->Initialize()) {
539 gfxCriticalNote << "Failed to initialize DCSurface: " << wr::AsUint64(aId);
540 return;
543 mDCSurfaces[aId] = std::move(surface);
546 void DCLayerTree::CreateExternalSurface(wr::NativeSurfaceId aId,
547 bool aIsOpaque) {
548 auto it = mDCSurfaces.find(aId);
549 MOZ_RELEASE_ASSERT(it == mDCSurfaces.end());
551 auto surface = MakeUnique<DCExternalSurfaceWrapper>(aIsOpaque, this);
552 if (!surface->Initialize()) {
553 gfxCriticalNote << "Failed to initialize DCExternalSurfaceWrapper: "
554 << wr::AsUint64(aId);
555 return;
558 mDCSurfaces[aId] = std::move(surface);
561 void DCLayerTree::DestroySurface(NativeSurfaceId aId) {
562 auto surface_it = mDCSurfaces.find(aId);
563 MOZ_RELEASE_ASSERT(surface_it != mDCSurfaces.end());
564 auto surface = surface_it->second.get();
566 mRootVisual->RemoveVisual(surface->GetVisual());
567 mDCSurfaces.erase(surface_it);
570 void DCLayerTree::CreateTile(wr::NativeSurfaceId aId, int32_t aX, int32_t aY) {
571 auto surface = GetSurface(aId);
572 surface->CreateTile(aX, aY);
575 void DCLayerTree::DestroyTile(wr::NativeSurfaceId aId, int32_t aX, int32_t aY) {
576 auto surface = GetSurface(aId);
577 surface->DestroyTile(aX, aY);
580 void DCLayerTree::AttachExternalImage(wr::NativeSurfaceId aId,
581 wr::ExternalImageId aExternalImage) {
582 auto surface_it = mDCSurfaces.find(aId);
583 MOZ_RELEASE_ASSERT(surface_it != mDCSurfaces.end());
584 surface_it->second->AttachExternalImage(aExternalImage);
587 void DCExternalSurfaceWrapper::AttachExternalImage(
588 wr::ExternalImageId aExternalImage) {
589 if (auto* surface = EnsureSurfaceForExternalImage(aExternalImage)) {
590 surface->AttachExternalImage(aExternalImage);
594 template <class ToT>
595 struct QI {
596 template <class FromT>
597 [[nodiscard]] static inline RefPtr<ToT> From(FromT* const from) {
598 RefPtr<ToT> to;
599 (void)from->QueryInterface(static_cast<ToT**>(getter_AddRefs(to)));
600 return to;
604 DCSurface* DCExternalSurfaceWrapper::EnsureSurfaceForExternalImage(
605 wr::ExternalImageId aExternalImage) {
606 if (mSurface) {
607 return mSurface.get();
610 // Create a new surface based on the texture type.
611 RenderTextureHost* texture =
612 RenderThread::Get()->GetRenderTexture(aExternalImage);
613 if (texture && texture->AsRenderDXGITextureHost()) {
614 mSurface.reset(new DCSurfaceVideo(mIsOpaque, mDCLayerTree));
615 if (!mSurface->Initialize()) {
616 gfxCriticalNote << "Failed to initialize DCSurfaceVideo: "
617 << wr::AsUint64(aExternalImage);
618 mSurface = nullptr;
620 } else if (texture && texture->AsRenderDcompSurfaceTextureHost()) {
621 mSurface.reset(new DCSurfaceHandle(mIsOpaque, mDCLayerTree));
622 if (!mSurface->Initialize()) {
623 gfxCriticalNote << "Failed to initialize DCSurfaceHandle: "
624 << wr::AsUint64(aExternalImage);
625 mSurface = nullptr;
628 if (!mSurface) {
629 gfxCriticalNote << "Failed to create a surface for external image: "
630 << gfx::hexa(texture);
631 return nullptr;
633 const auto textureSwgl = texture->AsRenderTextureHostSWGL();
634 MOZ_ASSERT(textureSwgl); // Covered above.
636 // Add surface's visual which will contain video data to our root visual.
637 const auto surfaceVisual = mSurface->GetVisual();
638 mVisual->AddVisual(surfaceVisual, true, nullptr);
640 // -
641 // Apply color management.
643 [&]() {
644 const auto cmsMode = GfxColorManagementMode();
645 if (cmsMode == CMSMode::Off) return;
647 const auto dcomp = mDCLayerTree->GetCompositionDevice();
648 const auto dcomp3 = QI<IDCompositionDevice3>::From(dcomp);
649 if (!dcomp3) {
650 NS_WARNING(
651 "No IDCompositionDevice3, cannot use dcomp for color management.");
652 return;
655 // -
657 const auto cspace = [&]() {
658 const auto rangedCspace = textureSwgl->GetYUVColorSpace();
659 const auto info = FromYUVRangedColorSpace(rangedCspace);
660 auto ret = ToColorSpace2(info.space);
661 if (ret == gfx::ColorSpace2::Display && cmsMode == CMSMode::All) {
662 ret = gfx::ColorSpace2::SRGB;
664 return ret;
665 }();
667 const bool rec709GammaAsSrgb =
668 StaticPrefs::gfx_color_management_rec709_gamma_as_srgb();
669 const bool rec2020GammaAsRec709 =
670 StaticPrefs::gfx_color_management_rec2020_gamma_as_rec709();
672 auto cspaceDesc = color::ColorspaceDesc{};
673 switch (cspace) {
674 case gfx::ColorSpace2::Display:
675 return; // No color management needed!
676 case gfx::ColorSpace2::SRGB:
677 cspaceDesc.chrom = color::Chromaticities::Srgb();
678 cspaceDesc.tf = color::PiecewiseGammaDesc::Srgb();
679 break;
681 case gfx::ColorSpace2::DISPLAY_P3:
682 cspaceDesc.chrom = color::Chromaticities::DisplayP3();
683 cspaceDesc.tf = color::PiecewiseGammaDesc::DisplayP3();
684 break;
686 case gfx::ColorSpace2::BT601_525:
687 cspaceDesc.chrom = color::Chromaticities::Rec601_525_Ntsc();
688 if (rec709GammaAsSrgb) {
689 cspaceDesc.tf = color::PiecewiseGammaDesc::Srgb();
690 } else {
691 cspaceDesc.tf = color::PiecewiseGammaDesc::Rec709();
693 break;
695 case gfx::ColorSpace2::BT709:
696 cspaceDesc.chrom = color::Chromaticities::Rec709();
697 if (rec709GammaAsSrgb) {
698 cspaceDesc.tf = color::PiecewiseGammaDesc::Srgb();
699 } else {
700 cspaceDesc.tf = color::PiecewiseGammaDesc::Rec709();
702 break;
704 case gfx::ColorSpace2::BT2020:
705 cspaceDesc.chrom = color::Chromaticities::Rec2020();
706 if (rec2020GammaAsRec709 && rec709GammaAsSrgb) {
707 cspaceDesc.tf = color::PiecewiseGammaDesc::Srgb();
708 } else if (rec2020GammaAsRec709) {
709 cspaceDesc.tf = color::PiecewiseGammaDesc::Rec709();
710 } else {
711 // Just Rec709 with slightly more precision.
712 cspaceDesc.tf = color::PiecewiseGammaDesc::Rec2020_12bit();
714 break;
717 const auto cprofileIn = color::ColorProfileDesc::From(cspaceDesc);
718 auto cprofileOut = mDCLayerTree->OutputColorProfile();
719 bool pretendSrgb = StaticPrefs::gfx_color_management_native_srgb();
720 if (pretendSrgb) {
721 cprofileOut = color::ColorProfileDesc::From({
722 color::Chromaticities::Srgb(),
723 color::PiecewiseGammaDesc::Srgb(),
726 const auto conversion = color::ColorProfileConversionDesc::From({
727 .src = cprofileIn,
728 .dst = cprofileOut,
731 // -
733 auto chain = ColorManagementChain::From(*dcomp3, conversion);
734 mCManageChain = Some(chain);
736 surfaceVisual->SetEffect(mCManageChain->last.get());
737 }();
739 return mSurface.get();
742 void DCExternalSurfaceWrapper::PresentExternalSurface(gfx::Matrix& aTransform) {
743 MOZ_ASSERT(mSurface);
744 if (auto* surface = mSurface->AsDCSurfaceVideo()) {
745 if (surface->CalculateSwapChainSize(aTransform)) {
746 surface->PresentVideo();
748 } else if (auto* surface = mSurface->AsDCSurfaceHandle()) {
749 surface->PresentSurfaceHandle();
753 template <typename T>
754 static inline D2D1_RECT_F D2DRect(const T& aRect) {
755 return D2D1::RectF(aRect.X(), aRect.Y(), aRect.XMost(), aRect.YMost());
758 static inline D2D1_MATRIX_3X2_F D2DMatrix(const gfx::Matrix& aTransform) {
759 return D2D1::Matrix3x2F(aTransform._11, aTransform._12, aTransform._21,
760 aTransform._22, aTransform._31, aTransform._32);
763 void DCLayerTree::AddSurface(wr::NativeSurfaceId aId,
764 const wr::CompositorSurfaceTransform& aTransform,
765 wr::DeviceIntRect aClipRect,
766 wr::ImageRendering aImageRendering) {
767 auto it = mDCSurfaces.find(aId);
768 MOZ_RELEASE_ASSERT(it != mDCSurfaces.end());
769 const auto surface = it->second.get();
770 const auto visual = surface->GetVisual();
772 wr::DeviceIntPoint virtualOffset = surface->GetVirtualOffset();
774 float sx = aTransform.scale.x;
775 float sy = aTransform.scale.y;
776 float tx = aTransform.offset.x;
777 float ty = aTransform.offset.y;
778 gfx::Matrix transform(sx, 0.0, 0.0, sy, tx, ty);
780 surface->PresentExternalSurface(transform);
782 transform.PreTranslate(-virtualOffset.x, -virtualOffset.y);
784 // The DirectComposition API applies clipping *before* any
785 // transforms/offset, whereas we want the clip applied after. Right now, we
786 // only support rectilinear transforms, and then we transform our clip into
787 // pre-transform coordinate space for it to be applied there.
788 // DirectComposition does have an option for pre-transform clipping, if you
789 // create an explicit IDCompositionEffectGroup object and set a 3D transform
790 // on that. I suspect that will perform worse though, so we should only do
791 // that for complex transforms (which are never provided right now).
792 MOZ_ASSERT(transform.IsRectilinear());
793 gfx::Rect clip = transform.Inverse().TransformBounds(gfx::Rect(
794 aClipRect.min.x, aClipRect.min.y, aClipRect.width(), aClipRect.height()));
795 // Set the clip rect - converting from world space to the pre-offset space
796 // that DC requires for rectangle clips.
797 visual->SetClip(D2DRect(clip));
799 // TODO: The input matrix is a 4x4, but we only support a 3x2 at
800 // the D3D API level (unless we QI to IDCompositionVisual3, which might
801 // not be available?).
802 // Should we assert here, or restrict at the WR API level.
803 visual->SetTransform(D2DMatrix(transform));
805 if (aImageRendering == wr::ImageRendering::Auto) {
806 visual->SetBitmapInterpolationMode(
807 DCOMPOSITION_BITMAP_INTERPOLATION_MODE_LINEAR);
808 } else {
809 visual->SetBitmapInterpolationMode(
810 DCOMPOSITION_BITMAP_INTERPOLATION_MODE_NEAREST_NEIGHBOR);
813 mCurrentLayers.push_back(aId);
816 GLuint DCLayerTree::GetOrCreateFbo(int aWidth, int aHeight) {
817 const auto gl = GetGLContext();
818 GLuint fboId = 0;
820 // Check if we have a cached FBO with matching dimensions
821 for (auto it = mFrameBuffers.begin(); it != mFrameBuffers.end(); ++it) {
822 if (it->width == aWidth && it->height == aHeight) {
823 fboId = it->fboId;
824 it->lastFrameUsed = mCurrentFrame;
825 break;
829 // If not, create a new FBO with attached depth buffer
830 if (fboId == 0) {
831 // Create the depth buffer
832 GLuint depthRboId;
833 gl->fGenRenderbuffers(1, &depthRboId);
834 gl->fBindRenderbuffer(LOCAL_GL_RENDERBUFFER, depthRboId);
835 gl->fRenderbufferStorage(LOCAL_GL_RENDERBUFFER, LOCAL_GL_DEPTH_COMPONENT24,
836 aWidth, aHeight);
838 // Create the framebuffer and attach the depth buffer to it
839 gl->fGenFramebuffers(1, &fboId);
840 gl->fBindFramebuffer(LOCAL_GL_DRAW_FRAMEBUFFER, fboId);
841 gl->fFramebufferRenderbuffer(LOCAL_GL_DRAW_FRAMEBUFFER,
842 LOCAL_GL_DEPTH_ATTACHMENT,
843 LOCAL_GL_RENDERBUFFER, depthRboId);
845 // Store this in the cache for future calls.
846 // TODO(gw): Maybe we should periodically scan this list and remove old
847 // entries that
848 // haven't been used for some time?
849 DCLayerTree::CachedFrameBuffer frame_buffer_info;
850 frame_buffer_info.width = aWidth;
851 frame_buffer_info.height = aHeight;
852 frame_buffer_info.fboId = fboId;
853 frame_buffer_info.depthRboId = depthRboId;
854 frame_buffer_info.lastFrameUsed = mCurrentFrame;
855 mFrameBuffers.AppendElement(frame_buffer_info);
858 return fboId;
861 bool DCLayerTree::EnsureVideoProcessor(const gfx::IntSize& aInputSize,
862 const gfx::IntSize& aOutputSize) {
863 HRESULT hr;
865 if (!mVideoDevice || !mVideoContext) {
866 return false;
869 if (mVideoProcessor && (aInputSize <= mVideoInputSize) &&
870 (aOutputSize <= mVideoOutputSize)) {
871 return true;
874 mVideoProcessor = nullptr;
875 mVideoProcessorEnumerator = nullptr;
877 D3D11_VIDEO_PROCESSOR_CONTENT_DESC desc = {};
878 desc.InputFrameFormat = D3D11_VIDEO_FRAME_FORMAT_PROGRESSIVE;
879 desc.InputFrameRate.Numerator = 60;
880 desc.InputFrameRate.Denominator = 1;
881 desc.InputWidth = aInputSize.width;
882 desc.InputHeight = aInputSize.height;
883 desc.OutputFrameRate.Numerator = 60;
884 desc.OutputFrameRate.Denominator = 1;
885 desc.OutputWidth = aOutputSize.width;
886 desc.OutputHeight = aOutputSize.height;
887 desc.Usage = D3D11_VIDEO_USAGE_PLAYBACK_NORMAL;
889 hr = mVideoDevice->CreateVideoProcessorEnumerator(
890 &desc, getter_AddRefs(mVideoProcessorEnumerator));
891 if (FAILED(hr)) {
892 gfxCriticalNote << "Failed to create VideoProcessorEnumerator: "
893 << gfx::hexa(hr);
894 return false;
897 hr = mVideoDevice->CreateVideoProcessor(mVideoProcessorEnumerator, 0,
898 getter_AddRefs(mVideoProcessor));
899 if (FAILED(hr)) {
900 mVideoProcessor = nullptr;
901 mVideoProcessorEnumerator = nullptr;
902 gfxCriticalNote << "Failed to create VideoProcessor: " << gfx::hexa(hr);
903 return false;
906 // Reduce power cosumption
907 // By default, the driver might perform certain processing tasks
908 // automatically
909 mVideoContext->VideoProcessorSetStreamAutoProcessingMode(mVideoProcessor, 0,
910 FALSE);
912 mVideoInputSize = aInputSize;
913 mVideoOutputSize = aOutputSize;
915 return true;
918 bool DCLayerTree::SupportsHardwareOverlays() {
919 return sGpuOverlayInfo->mSupportsHardwareOverlays;
922 bool DCLayerTree::SupportsSwapChainTearing() {
923 RefPtr<ID3D11Device> device = mDevice;
924 static const bool supported = [device] {
925 RefPtr<IDXGIDevice> dxgiDevice;
926 RefPtr<IDXGIAdapter> adapter;
927 device->QueryInterface((IDXGIDevice**)getter_AddRefs(dxgiDevice));
928 dxgiDevice->GetAdapter(getter_AddRefs(adapter));
930 RefPtr<IDXGIFactory5> dxgiFactory;
931 HRESULT hr = adapter->GetParent(
932 IID_PPV_ARGS((IDXGIFactory5**)getter_AddRefs(dxgiFactory)));
933 if (FAILED(hr)) {
934 return false;
937 BOOL presentAllowTearing = FALSE;
938 hr = dxgiFactory->CheckFeatureSupport(DXGI_FEATURE_PRESENT_ALLOW_TEARING,
939 &presentAllowTearing,
940 sizeof(presentAllowTearing));
941 if (FAILED(hr)) {
942 return false;
945 if (auto* gpuParent = gfx::GPUParent::GetSingleton()) {
946 gpuParent->NotifySwapChainInfo(
947 layers::SwapChainInfo(!!presentAllowTearing));
948 } else if (XRE_IsParentProcess()) {
949 MOZ_ASSERT_UNREACHABLE("unexpected to be called");
951 return !!presentAllowTearing;
952 }();
953 return supported;
956 DXGI_FORMAT DCLayerTree::GetOverlayFormatForSDR() {
957 return sGpuOverlayInfo->mOverlayFormatUsed;
960 static layers::OverlaySupportType FlagsToOverlaySupportType(
961 UINT aFlags, bool aSoftwareOverlaySupported) {
962 if (aFlags & DXGI_OVERLAY_SUPPORT_FLAG_SCALING) {
963 return layers::OverlaySupportType::Scaling;
965 if (aFlags & DXGI_OVERLAY_SUPPORT_FLAG_DIRECT) {
966 return layers::OverlaySupportType::Direct;
968 if (aSoftwareOverlaySupported) {
969 return layers::OverlaySupportType::Software;
971 return layers::OverlaySupportType::None;
974 layers::OverlayInfo DCLayerTree::GetOverlayInfo() {
975 layers::OverlayInfo info;
977 info.mSupportsOverlays = sGpuOverlayInfo->mSupportsHardwareOverlays;
978 info.mNv12Overlay =
979 FlagsToOverlaySupportType(sGpuOverlayInfo->mNv12OverlaySupportFlags,
980 /* aSoftwareOverlaySupported */ false);
981 info.mYuy2Overlay =
982 FlagsToOverlaySupportType(sGpuOverlayInfo->mYuy2OverlaySupportFlags,
983 /* aSoftwareOverlaySupported */ false);
984 info.mBgra8Overlay =
985 FlagsToOverlaySupportType(sGpuOverlayInfo->mBgra8OverlaySupportFlags,
986 /* aSoftwareOverlaySupported */ true);
987 info.mRgb10a2Overlay =
988 FlagsToOverlaySupportType(sGpuOverlayInfo->mRgb10a2OverlaySupportFlags,
989 /* aSoftwareOverlaySupported */ false);
991 return info;
994 DCSurface::DCSurface(wr::DeviceIntSize aTileSize,
995 wr::DeviceIntPoint aVirtualOffset, bool aIsVirtualSurface,
996 bool aIsOpaque, DCLayerTree* aDCLayerTree)
997 : mIsVirtualSurface(aIsVirtualSurface),
998 mDCLayerTree(aDCLayerTree),
999 mTileSize(aTileSize),
1000 mIsOpaque(aIsOpaque),
1001 mAllocatedRectDirty(true),
1002 mVirtualOffset(aVirtualOffset) {}
1004 DCSurface::~DCSurface() {}
1006 bool DCSurface::Initialize() {
1007 // Create a visual for tiles to attach to, whether virtual or not.
1008 HRESULT hr;
1009 const auto dCompDevice = mDCLayerTree->GetCompositionDevice();
1010 hr = dCompDevice->CreateVisual(getter_AddRefs(mVisual));
1011 if (FAILED(hr)) {
1012 gfxCriticalNote << "Failed to create DCompositionVisual: " << gfx::hexa(hr);
1013 return false;
1016 // If virtual surface is enabled, create and attach to visual, in this case
1017 // the tiles won't own visuals or surfaces.
1018 if (mIsVirtualSurface) {
1019 DXGI_ALPHA_MODE alpha_mode =
1020 mIsOpaque ? DXGI_ALPHA_MODE_IGNORE : DXGI_ALPHA_MODE_PREMULTIPLIED;
1022 hr = dCompDevice->CreateVirtualSurface(
1023 VIRTUAL_SURFACE_SIZE, VIRTUAL_SURFACE_SIZE, DXGI_FORMAT_R8G8B8A8_UNORM,
1024 alpha_mode, getter_AddRefs(mVirtualSurface));
1025 MOZ_ASSERT(SUCCEEDED(hr));
1027 // Bind the surface memory to this visual
1028 hr = mVisual->SetContent(mVirtualSurface);
1029 MOZ_ASSERT(SUCCEEDED(hr));
1032 return true;
1035 void DCSurface::CreateTile(int32_t aX, int32_t aY) {
1036 TileKey key(aX, aY);
1037 MOZ_RELEASE_ASSERT(mDCTiles.find(key) == mDCTiles.end());
1039 auto tile = MakeUnique<DCTile>(mDCLayerTree);
1040 if (!tile->Initialize(aX, aY, mTileSize, mIsVirtualSurface, mIsOpaque,
1041 mVisual)) {
1042 gfxCriticalNote << "Failed to initialize DCTile: " << aX << aY;
1043 return;
1046 if (mIsVirtualSurface) {
1047 mAllocatedRectDirty = true;
1048 } else {
1049 mVisual->AddVisual(tile->GetVisual(), false, nullptr);
1052 mDCTiles[key] = std::move(tile);
1055 void DCSurface::DestroyTile(int32_t aX, int32_t aY) {
1056 TileKey key(aX, aY);
1057 if (mIsVirtualSurface) {
1058 mAllocatedRectDirty = true;
1059 } else {
1060 auto tile = GetTile(aX, aY);
1061 mVisual->RemoveVisual(tile->GetVisual());
1063 mDCTiles.erase(key);
1066 void DCSurface::DirtyAllocatedRect() { mAllocatedRectDirty = true; }
1068 void DCSurface::UpdateAllocatedRect() {
1069 if (mAllocatedRectDirty) {
1070 if (mVirtualSurface) {
1071 // The virtual surface may have holes in it (for example, an empty tile
1072 // that has no primitives). Instead of trimming to a single bounding
1073 // rect, supply the rect of each valid tile to handle this case.
1074 std::vector<RECT> validRects;
1076 for (auto it = mDCTiles.begin(); it != mDCTiles.end(); ++it) {
1077 auto tile = GetTile(it->first.mX, it->first.mY);
1078 RECT rect;
1080 rect.left = (LONG)(mVirtualOffset.x + it->first.mX * mTileSize.width +
1081 tile->mValidRect.x);
1082 rect.top = (LONG)(mVirtualOffset.y + it->first.mY * mTileSize.height +
1083 tile->mValidRect.y);
1084 rect.right = rect.left + tile->mValidRect.width;
1085 rect.bottom = rect.top + tile->mValidRect.height;
1087 validRects.push_back(rect);
1090 mVirtualSurface->Trim(validRects.data(), validRects.size());
1092 // When not using a virtual surface, we still want to reset this
1093 mAllocatedRectDirty = false;
1097 DCTile* DCSurface::GetTile(int32_t aX, int32_t aY) const {
1098 TileKey key(aX, aY);
1099 auto tile_it = mDCTiles.find(key);
1100 MOZ_RELEASE_ASSERT(tile_it != mDCTiles.end());
1101 return tile_it->second.get();
1104 DCSurfaceVideo::DCSurfaceVideo(bool aIsOpaque, DCLayerTree* aDCLayerTree)
1105 : DCSurface(wr::DeviceIntSize{}, wr::DeviceIntPoint{}, false, aIsOpaque,
1106 aDCLayerTree) {}
1108 DCSurfaceVideo::~DCSurfaceVideo() {
1109 ReleaseDecodeSwapChainResources();
1110 MOZ_ASSERT(!mSwapChainSurfaceHandle);
1113 bool IsYUVSwapChainFormat(DXGI_FORMAT aFormat) {
1114 if (aFormat == DXGI_FORMAT_NV12 || aFormat == DXGI_FORMAT_YUY2) {
1115 return true;
1117 return false;
1120 void DCSurfaceVideo::AttachExternalImage(wr::ExternalImageId aExternalImage) {
1121 RenderTextureHost* texture =
1122 RenderThread::Get()->GetRenderTexture(aExternalImage);
1123 MOZ_RELEASE_ASSERT(texture);
1125 if (mPrevTexture == texture) {
1126 return;
1129 // XXX if software decoded video frame format is nv12, it could be used as
1130 // video overlay.
1131 if (!texture || !texture->AsRenderDXGITextureHost() ||
1132 texture->AsRenderDXGITextureHost()->GetFormat() !=
1133 gfx::SurfaceFormat::NV12) {
1134 gfxCriticalNote << "Unsupported RenderTexture for overlay: "
1135 << gfx::hexa(texture);
1136 return;
1139 mRenderTextureHost = texture;
1142 bool DCSurfaceVideo::CalculateSwapChainSize(gfx::Matrix& aTransform) {
1143 if (!mRenderTextureHost) {
1144 MOZ_ASSERT_UNREACHABLE("unexpected to be called");
1145 return false;
1148 mVideoSize = mRenderTextureHost->AsRenderDXGITextureHost()->GetSize(0);
1150 // When RenderTextureHost, swapChainSize or VideoSwapChain are updated,
1151 // DCSurfaceVideo::PresentVideo() needs to be called.
1152 bool needsToPresent = mPrevTexture != mRenderTextureHost;
1153 gfx::IntSize swapChainSize = mVideoSize;
1154 gfx::Matrix transform = aTransform;
1156 // When video is rendered to axis aligned integer rectangle, video scaling
1157 // could be done by VideoProcessor
1158 bool scaleVideoAtVideoProcessor = false;
1159 if (StaticPrefs::gfx_webrender_dcomp_video_vp_scaling_win_AtStartup() &&
1160 aTransform.PreservesAxisAlignedRectangles()) {
1161 gfx::Size scaledSize = gfx::Size(mVideoSize) * aTransform.ScaleFactors();
1162 gfx::IntSize size(int32_t(std::round(scaledSize.width)),
1163 int32_t(std::round(scaledSize.height)));
1164 if (gfx::FuzzyEqual(scaledSize.width, size.width, 0.1f) &&
1165 gfx::FuzzyEqual(scaledSize.height, size.height, 0.1f)) {
1166 scaleVideoAtVideoProcessor = true;
1167 swapChainSize = size;
1171 if (scaleVideoAtVideoProcessor) {
1172 // 4:2:2 subsampled formats like YUY2 must have an even width, and 4:2:0
1173 // subsampled formats like NV12 must have an even width and height.
1174 if (swapChainSize.width % 2 == 1) {
1175 swapChainSize.width += 1;
1177 if (swapChainSize.height % 2 == 1) {
1178 swapChainSize.height += 1;
1180 transform = gfx::Matrix::Translation(aTransform.GetTranslation());
1183 if (!mVideoSwapChain || mSwapChainSize != swapChainSize) {
1184 needsToPresent = true;
1185 ReleaseDecodeSwapChainResources();
1186 // Update mSwapChainSize before creating SwapChain
1187 mSwapChainSize = swapChainSize;
1189 auto swapChainFormat = GetSwapChainFormat();
1190 bool useYUVSwapChain = IsYUVSwapChainFormat(swapChainFormat);
1191 if (useYUVSwapChain) {
1192 // Tries to create YUV SwapChain
1193 CreateVideoSwapChain();
1194 if (!mVideoSwapChain) {
1195 mFailedYuvSwapChain = true;
1196 ReleaseDecodeSwapChainResources();
1198 gfxCriticalNote << "Fallback to RGB SwapChain";
1201 // Tries to create RGB SwapChain
1202 if (!mVideoSwapChain) {
1203 CreateVideoSwapChain();
1207 aTransform = transform;
1209 return needsToPresent;
1212 void DCSurfaceVideo::PresentVideo() {
1213 if (!mRenderTextureHost) {
1214 return;
1217 if (!mVideoSwapChain) {
1218 gfxCriticalNote << "Failed to create VideoSwapChain";
1219 RenderThread::Get()->NotifyWebRenderError(
1220 wr::WebRenderError::VIDEO_OVERLAY);
1221 return;
1224 mVisual->SetContent(mVideoSwapChain);
1226 if (!CallVideoProcessorBlt()) {
1227 auto swapChainFormat = GetSwapChainFormat();
1228 bool useYUVSwapChain = IsYUVSwapChainFormat(swapChainFormat);
1229 if (useYUVSwapChain) {
1230 mFailedYuvSwapChain = true;
1231 ReleaseDecodeSwapChainResources();
1232 return;
1234 RenderThread::Get()->NotifyWebRenderError(
1235 wr::WebRenderError::VIDEO_OVERLAY);
1236 return;
1239 HRESULT hr;
1240 hr = mVideoSwapChain->Present(0, 0);
1241 if (FAILED(hr) && hr != DXGI_STATUS_OCCLUDED) {
1242 gfxCriticalNoteOnce << "video Present failed: " << gfx::hexa(hr);
1245 mPrevTexture = mRenderTextureHost;
1248 DXGI_FORMAT DCSurfaceVideo::GetSwapChainFormat() {
1249 if (mFailedYuvSwapChain || !mDCLayerTree->SupportsHardwareOverlays()) {
1250 return DXGI_FORMAT_B8G8R8A8_UNORM;
1252 return mDCLayerTree->GetOverlayFormatForSDR();
1255 bool DCSurfaceVideo::CreateVideoSwapChain() {
1256 MOZ_ASSERT(mRenderTextureHost);
1258 const auto device = mDCLayerTree->GetDevice();
1260 RefPtr<IDXGIDevice> dxgiDevice;
1261 device->QueryInterface((IDXGIDevice**)getter_AddRefs(dxgiDevice));
1263 RefPtr<IDXGIFactoryMedia> dxgiFactoryMedia;
1265 RefPtr<IDXGIAdapter> adapter;
1266 dxgiDevice->GetAdapter(getter_AddRefs(adapter));
1267 adapter->GetParent(
1268 IID_PPV_ARGS((IDXGIFactoryMedia**)getter_AddRefs(dxgiFactoryMedia)));
1271 mSwapChainSurfaceHandle = gfx::DeviceManagerDx::CreateDCompSurfaceHandle();
1272 if (!mSwapChainSurfaceHandle) {
1273 gfxCriticalNote << "Failed to create DCompSurfaceHandle";
1274 return false;
1277 auto swapChainFormat = GetSwapChainFormat();
1279 DXGI_SWAP_CHAIN_DESC1 desc = {};
1280 desc.Width = mSwapChainSize.width;
1281 desc.Height = mSwapChainSize.height;
1282 desc.Format = swapChainFormat;
1283 desc.Stereo = FALSE;
1284 desc.SampleDesc.Count = 1;
1285 desc.BufferCount = 2;
1286 desc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
1287 desc.Scaling = DXGI_SCALING_STRETCH;
1288 desc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL;
1289 desc.Flags = DXGI_SWAP_CHAIN_FLAG_FULLSCREEN_VIDEO;
1290 if (IsYUVSwapChainFormat(swapChainFormat)) {
1291 desc.Flags |= DXGI_SWAP_CHAIN_FLAG_YUV_VIDEO;
1293 desc.AlphaMode = DXGI_ALPHA_MODE_IGNORE;
1295 HRESULT hr;
1296 hr = dxgiFactoryMedia->CreateSwapChainForCompositionSurfaceHandle(
1297 device, mSwapChainSurfaceHandle, &desc, nullptr,
1298 getter_AddRefs(mVideoSwapChain));
1300 if (FAILED(hr)) {
1301 gfxCriticalNote << "Failed to create video SwapChain: " << gfx::hexa(hr)
1302 << " " << mSwapChainSize;
1303 return false;
1306 mSwapChainFormat = swapChainFormat;
1307 return true;
1310 // TODO: Replace with YUVRangedColorSpace
1311 static Maybe<DXGI_COLOR_SPACE_TYPE> GetSourceDXGIColorSpace(
1312 const gfx::YUVColorSpace aYUVColorSpace,
1313 const gfx::ColorRange aColorRange) {
1314 if (aYUVColorSpace == gfx::YUVColorSpace::BT601) {
1315 if (aColorRange == gfx::ColorRange::FULL) {
1316 return Some(DXGI_COLOR_SPACE_YCBCR_FULL_G22_LEFT_P601);
1317 } else {
1318 return Some(DXGI_COLOR_SPACE_YCBCR_STUDIO_G22_LEFT_P601);
1320 } else if (aYUVColorSpace == gfx::YUVColorSpace::BT709) {
1321 if (aColorRange == gfx::ColorRange::FULL) {
1322 return Some(DXGI_COLOR_SPACE_YCBCR_FULL_G22_LEFT_P709);
1323 } else {
1324 return Some(DXGI_COLOR_SPACE_YCBCR_STUDIO_G22_LEFT_P709);
1326 } else if (aYUVColorSpace == gfx::YUVColorSpace::BT2020) {
1327 if (aColorRange == gfx::ColorRange::FULL) {
1328 // XXX Add SMPTEST2084 handling. HDR content is not handled yet by
1329 // video overlay.
1330 return Some(DXGI_COLOR_SPACE_YCBCR_FULL_G22_LEFT_P2020);
1331 } else {
1332 return Some(DXGI_COLOR_SPACE_YCBCR_STUDIO_G22_LEFT_P2020);
1336 return Nothing();
1339 static Maybe<DXGI_COLOR_SPACE_TYPE> GetSourceDXGIColorSpace(
1340 const gfx::YUVRangedColorSpace aYUVColorSpace) {
1341 const auto info = FromYUVRangedColorSpace(aYUVColorSpace);
1342 return GetSourceDXGIColorSpace(info.space, info.range);
1345 bool DCSurfaceVideo::CallVideoProcessorBlt() {
1346 MOZ_ASSERT(mRenderTextureHost);
1348 HRESULT hr;
1349 const auto videoDevice = mDCLayerTree->GetVideoDevice();
1350 const auto videoContext = mDCLayerTree->GetVideoContext();
1351 const auto texture = mRenderTextureHost->AsRenderDXGITextureHost();
1353 Maybe<DXGI_COLOR_SPACE_TYPE> sourceColorSpace =
1354 GetSourceDXGIColorSpace(texture->GetYUVColorSpace());
1355 if (sourceColorSpace.isNothing()) {
1356 gfxCriticalNote << "Unsupported color space";
1357 return false;
1360 RefPtr<ID3D11Texture2D> texture2D = texture->GetD3D11Texture2DWithGL();
1361 if (!texture2D) {
1362 gfxCriticalNote << "Failed to get D3D11Texture2D";
1363 return false;
1366 if (!mVideoSwapChain) {
1367 return false;
1370 if (!mDCLayerTree->EnsureVideoProcessor(mVideoSize, mSwapChainSize)) {
1371 gfxCriticalNote << "EnsureVideoProcessor Failed";
1372 return false;
1375 RefPtr<IDXGISwapChain3> swapChain3;
1376 mVideoSwapChain->QueryInterface(
1377 (IDXGISwapChain3**)getter_AddRefs(swapChain3));
1378 if (!swapChain3) {
1379 gfxCriticalNote << "Failed to get IDXGISwapChain3";
1380 return false;
1383 RefPtr<ID3D11VideoContext1> videoContext1;
1384 videoContext->QueryInterface(
1385 (ID3D11VideoContext1**)getter_AddRefs(videoContext1));
1386 if (!videoContext1) {
1387 gfxCriticalNote << "Failed to get ID3D11VideoContext1";
1388 return false;
1391 const auto videoProcessor = mDCLayerTree->GetVideoProcessor();
1392 const auto videoProcessorEnumerator =
1393 mDCLayerTree->GetVideoProcessorEnumerator();
1395 DXGI_COLOR_SPACE_TYPE inputColorSpace = sourceColorSpace.ref();
1396 videoContext1->VideoProcessorSetStreamColorSpace1(videoProcessor, 0,
1397 inputColorSpace);
1399 DXGI_COLOR_SPACE_TYPE outputColorSpace =
1400 IsYUVSwapChainFormat(mSwapChainFormat)
1401 ? inputColorSpace
1402 : DXGI_COLOR_SPACE_RGB_FULL_G22_NONE_P709;
1403 hr = swapChain3->SetColorSpace1(outputColorSpace);
1404 if (FAILED(hr)) {
1405 gfxCriticalNoteOnce << "SetColorSpace1 failed: " << gfx::hexa(hr);
1406 RenderThread::Get()->NotifyWebRenderError(
1407 wr::WebRenderError::VIDEO_OVERLAY);
1408 return false;
1410 videoContext1->VideoProcessorSetOutputColorSpace1(videoProcessor,
1411 outputColorSpace);
1413 D3D11_VIDEO_PROCESSOR_INPUT_VIEW_DESC inputDesc = {};
1414 inputDesc.ViewDimension = D3D11_VPIV_DIMENSION_TEXTURE2D;
1415 inputDesc.Texture2D.ArraySlice = texture->ArrayIndex();
1417 RefPtr<ID3D11VideoProcessorInputView> inputView;
1418 hr = videoDevice->CreateVideoProcessorInputView(
1419 texture2D, videoProcessorEnumerator, &inputDesc,
1420 getter_AddRefs(inputView));
1421 if (FAILED(hr)) {
1422 gfxCriticalNote << "ID3D11VideoProcessorInputView creation failed: "
1423 << gfx::hexa(hr);
1424 return false;
1427 D3D11_VIDEO_PROCESSOR_STREAM stream = {};
1428 stream.Enable = true;
1429 stream.OutputIndex = 0;
1430 stream.InputFrameOrField = 0;
1431 stream.PastFrames = 0;
1432 stream.FutureFrames = 0;
1433 stream.pInputSurface = inputView.get();
1435 RECT destRect;
1436 destRect.left = 0;
1437 destRect.top = 0;
1438 destRect.right = mSwapChainSize.width;
1439 destRect.bottom = mSwapChainSize.height;
1441 videoContext->VideoProcessorSetOutputTargetRect(videoProcessor, TRUE,
1442 &destRect);
1443 videoContext->VideoProcessorSetStreamDestRect(videoProcessor, 0, TRUE,
1444 &destRect);
1445 RECT sourceRect;
1446 sourceRect.left = 0;
1447 sourceRect.top = 0;
1448 sourceRect.right = mVideoSize.width;
1449 sourceRect.bottom = mVideoSize.height;
1450 videoContext->VideoProcessorSetStreamSourceRect(videoProcessor, 0, TRUE,
1451 &sourceRect);
1453 if (!mOutputView) {
1454 RefPtr<ID3D11Texture2D> backBuf;
1455 mVideoSwapChain->GetBuffer(0, __uuidof(ID3D11Texture2D),
1456 (void**)getter_AddRefs(backBuf));
1458 D3D11_VIDEO_PROCESSOR_OUTPUT_VIEW_DESC outputDesc = {};
1459 outputDesc.ViewDimension = D3D11_VPOV_DIMENSION_TEXTURE2D;
1460 outputDesc.Texture2D.MipSlice = 0;
1462 hr = videoDevice->CreateVideoProcessorOutputView(
1463 backBuf, videoProcessorEnumerator, &outputDesc,
1464 getter_AddRefs(mOutputView));
1465 if (FAILED(hr)) {
1466 gfxCriticalNote << "ID3D11VideoProcessorOutputView creation failed: "
1467 << gfx::hexa(hr);
1468 return false;
1472 hr = videoContext->VideoProcessorBlt(videoProcessor, mOutputView, 0, 1,
1473 &stream);
1474 if (FAILED(hr)) {
1475 gfxCriticalNote << "VideoProcessorBlt failed: " << gfx::hexa(hr);
1476 return false;
1479 return true;
1482 void DCSurfaceVideo::ReleaseDecodeSwapChainResources() {
1483 mOutputView = nullptr;
1484 mVideoSwapChain = nullptr;
1485 mDecodeSwapChain = nullptr;
1486 mDecodeResource = nullptr;
1487 if (mSwapChainSurfaceHandle) {
1488 ::CloseHandle(mSwapChainSurfaceHandle);
1489 mSwapChainSurfaceHandle = 0;
1493 DCSurfaceHandle::DCSurfaceHandle(bool aIsOpaque, DCLayerTree* aDCLayerTree)
1494 : DCSurface(wr::DeviceIntSize{}, wr::DeviceIntPoint{}, false, aIsOpaque,
1495 aDCLayerTree) {}
1497 void DCSurfaceHandle::AttachExternalImage(wr::ExternalImageId aExternalImage) {
1498 RenderTextureHost* texture =
1499 RenderThread::Get()->GetRenderTexture(aExternalImage);
1500 RenderDcompSurfaceTextureHost* renderTexture =
1501 texture ? texture->AsRenderDcompSurfaceTextureHost() : nullptr;
1502 if (!renderTexture) {
1503 gfxCriticalNote << "Unsupported RenderTexture for DCSurfaceHandle: "
1504 << gfx::hexa(texture);
1505 return;
1508 const auto handle = renderTexture->GetDcompSurfaceHandle();
1509 if (GetSurfaceHandle() == handle) {
1510 return;
1513 LOG_H("AttachExternalImage, ext-image=%" PRIu64 ", texture=%p, handle=%p",
1514 wr::AsUint64(aExternalImage), renderTexture, handle);
1515 mDcompTextureHost = renderTexture;
1518 HANDLE DCSurfaceHandle::GetSurfaceHandle() const {
1519 if (mDcompTextureHost) {
1520 return mDcompTextureHost->GetDcompSurfaceHandle();
1522 return nullptr;
1525 IDCompositionSurface* DCSurfaceHandle::EnsureSurface() {
1526 if (auto* surface = mDcompTextureHost->GetSurface()) {
1527 return surface;
1530 // Texture host hasn't created the surface yet, ask it to create a new one.
1531 RefPtr<IDCompositionDevice> device;
1532 HRESULT hr = mDCLayerTree->GetCompositionDevice()->QueryInterface(
1533 (IDCompositionDevice**)getter_AddRefs(device));
1534 if (FAILED(hr)) {
1535 gfxCriticalNote
1536 << "Failed to convert IDCompositionDevice2 to IDCompositionDevice: "
1537 << gfx::hexa(hr);
1538 return nullptr;
1541 return mDcompTextureHost->CreateSurfaceFromDevice(device);
1544 void DCSurfaceHandle::PresentSurfaceHandle() {
1545 LOG_H("PresentSurfaceHandle");
1546 if (IDCompositionSurface* surface = EnsureSurface()) {
1547 LOG_H("Set surface %p to visual", surface);
1548 mVisual->SetContent(surface);
1549 } else {
1550 mVisual->SetContent(nullptr);
1554 DCTile::DCTile(DCLayerTree* aDCLayerTree) : mDCLayerTree(aDCLayerTree) {}
1556 DCTile::~DCTile() {}
1558 bool DCTile::Initialize(int aX, int aY, wr::DeviceIntSize aSize,
1559 bool aIsVirtualSurface, bool aIsOpaque,
1560 RefPtr<IDCompositionVisual2> mSurfaceVisual) {
1561 if (aSize.width <= 0 || aSize.height <= 0) {
1562 return false;
1565 mSize = aSize;
1566 mIsOpaque = aIsOpaque;
1567 mIsVirtualSurface = aIsVirtualSurface;
1568 mNeedsFullDraw = !aIsVirtualSurface;
1570 if (aIsVirtualSurface) {
1571 // Initially, the entire tile is considered valid, unless it is set by
1572 // the SetTileProperties method.
1573 mValidRect.x = 0;
1574 mValidRect.y = 0;
1575 mValidRect.width = aSize.width;
1576 mValidRect.height = aSize.height;
1577 } else {
1578 HRESULT hr;
1579 const auto dCompDevice = mDCLayerTree->GetCompositionDevice();
1580 // Create the visual and put it in the tree under the surface visual
1581 hr = dCompDevice->CreateVisual(getter_AddRefs(mVisual));
1582 if (FAILED(hr)) {
1583 gfxCriticalNote << "Failed to CreateVisual for DCTile: " << gfx::hexa(hr);
1584 return false;
1586 mSurfaceVisual->AddVisual(mVisual, false, nullptr);
1587 // Position the tile relative to the surface visual
1588 mVisual->SetOffsetX(aX * aSize.width);
1589 mVisual->SetOffsetY(aY * aSize.height);
1590 // Clip the visual so it doesn't show anything until we update it
1591 D2D_RECT_F clip = {0, 0, 0, 0};
1592 mVisual->SetClip(clip);
1593 // Create the underlying pixel buffer.
1594 mCompositionSurface = CreateCompositionSurface(aSize, aIsOpaque);
1595 if (!mCompositionSurface) {
1596 return false;
1598 hr = mVisual->SetContent(mCompositionSurface);
1599 if (FAILED(hr)) {
1600 gfxCriticalNote << "Failed to SetContent for DCTile: " << gfx::hexa(hr);
1601 return false;
1605 return true;
1608 RefPtr<IDCompositionSurface> DCTile::CreateCompositionSurface(
1609 wr::DeviceIntSize aSize, bool aIsOpaque) {
1610 HRESULT hr;
1611 const auto dCompDevice = mDCLayerTree->GetCompositionDevice();
1612 const auto alphaMode =
1613 aIsOpaque ? DXGI_ALPHA_MODE_IGNORE : DXGI_ALPHA_MODE_PREMULTIPLIED;
1614 RefPtr<IDCompositionSurface> compositionSurface;
1616 hr = dCompDevice->CreateSurface(aSize.width, aSize.height,
1617 DXGI_FORMAT_R8G8B8A8_UNORM, alphaMode,
1618 getter_AddRefs(compositionSurface));
1619 if (FAILED(hr)) {
1620 gfxCriticalNote << "Failed to CreateSurface for DCTile: " << gfx::hexa(hr);
1621 return nullptr;
1623 return compositionSurface;
1626 RefPtr<IDCompositionSurface> DCTile::Bind(wr::DeviceIntRect aValidRect) {
1627 if (mVisual != nullptr) {
1628 // Tile owns a visual, set the size of the visual to match the portion we
1629 // want to be visible.
1630 D2D_RECT_F clip_rect;
1631 clip_rect.left = aValidRect.min.x;
1632 clip_rect.top = aValidRect.min.y;
1633 clip_rect.right = aValidRect.max.x;
1634 clip_rect.bottom = aValidRect.max.y;
1635 mVisual->SetClip(clip_rect);
1637 return mCompositionSurface;
1640 GLuint DCLayerTree::CreateEGLSurfaceForCompositionSurface(
1641 wr::DeviceIntRect aDirtyRect, wr::DeviceIntPoint* aOffset,
1642 RefPtr<IDCompositionSurface> aCompositionSurface,
1643 wr::DeviceIntPoint aSurfaceOffset) {
1644 MOZ_ASSERT(aCompositionSurface.get());
1646 HRESULT hr;
1647 const auto gl = GetGLContext();
1648 RefPtr<ID3D11Texture2D> backBuf;
1649 POINT offset;
1651 RECT update_rect;
1652 update_rect.left = aSurfaceOffset.x + aDirtyRect.min.x;
1653 update_rect.top = aSurfaceOffset.y + aDirtyRect.min.y;
1654 update_rect.right = aSurfaceOffset.x + aDirtyRect.max.x;
1655 update_rect.bottom = aSurfaceOffset.y + aDirtyRect.max.y;
1656 hr = aCompositionSurface->BeginDraw(&update_rect, __uuidof(ID3D11Texture2D),
1657 (void**)getter_AddRefs(backBuf), &offset);
1659 if (FAILED(hr)) {
1660 LayoutDeviceIntRect rect = widget::WinUtils::ToIntRect(update_rect);
1662 gfxCriticalNote << "DCompositionSurface::BeginDraw failed: "
1663 << gfx::hexa(hr) << " " << rect;
1664 RenderThread::Get()->HandleWebRenderError(WebRenderError::BEGIN_DRAW);
1665 return false;
1668 // DC includes the origin of the dirty / update rect in the draw offset,
1669 // undo that here since WR expects it to be an absolute offset.
1670 offset.x -= aDirtyRect.min.x;
1671 offset.y -= aDirtyRect.min.y;
1673 D3D11_TEXTURE2D_DESC desc;
1674 backBuf->GetDesc(&desc);
1676 const auto& gle = gl::GLContextEGL::Cast(gl);
1677 const auto& egl = gle->mEgl;
1679 const auto buffer = reinterpret_cast<EGLClientBuffer>(backBuf.get());
1681 // Construct an EGLImage wrapper around the D3D texture for ANGLE.
1682 const EGLint attribs[] = {LOCAL_EGL_NONE};
1683 mEGLImage = egl->fCreateImage(EGL_NO_CONTEXT, LOCAL_EGL_D3D11_TEXTURE_ANGLE,
1684 buffer, attribs);
1686 // Get the current FBO and RBO id, so we can restore them later
1687 GLint currentFboId, currentRboId;
1688 gl->fGetIntegerv(LOCAL_GL_DRAW_FRAMEBUFFER_BINDING, &currentFboId);
1689 gl->fGetIntegerv(LOCAL_GL_RENDERBUFFER_BINDING, &currentRboId);
1691 // Create a render buffer object that is backed by the EGL image.
1692 gl->fGenRenderbuffers(1, &mColorRBO);
1693 gl->fBindRenderbuffer(LOCAL_GL_RENDERBUFFER, mColorRBO);
1694 gl->fEGLImageTargetRenderbufferStorage(LOCAL_GL_RENDERBUFFER, mEGLImage);
1696 // Get or create an FBO for the specified dimensions
1697 GLuint fboId = GetOrCreateFbo(desc.Width, desc.Height);
1699 // Attach the new renderbuffer to the FBO
1700 gl->fBindFramebuffer(LOCAL_GL_DRAW_FRAMEBUFFER, fboId);
1701 gl->fFramebufferRenderbuffer(LOCAL_GL_DRAW_FRAMEBUFFER,
1702 LOCAL_GL_COLOR_ATTACHMENT0,
1703 LOCAL_GL_RENDERBUFFER, mColorRBO);
1705 // Restore previous FBO and RBO bindings
1706 gl->fBindFramebuffer(LOCAL_GL_DRAW_FRAMEBUFFER, currentFboId);
1707 gl->fBindRenderbuffer(LOCAL_GL_RENDERBUFFER, currentRboId);
1709 aOffset->x = offset.x;
1710 aOffset->y = offset.y;
1712 return fboId;
1715 void DCLayerTree::DestroyEGLSurface() {
1716 const auto gl = GetGLContext();
1718 if (mColorRBO) {
1719 gl->fDeleteRenderbuffers(1, &mColorRBO);
1720 mColorRBO = 0;
1723 if (mEGLImage) {
1724 const auto& gle = gl::GLContextEGL::Cast(gl);
1725 const auto& egl = gle->mEgl;
1726 egl->fDestroyImage(mEGLImage);
1727 mEGLImage = EGL_NO_IMAGE;
1731 // -
1733 color::ColorProfileDesc DCLayerTree::QueryOutputColorProfile() {
1734 // GPU process can't simply init gfxPlatform, (and we don't need most of it)
1735 // but we do need gfxPlatform::GetCMSOutputProfile().
1736 // So we steal what we need through the window:
1737 const auto outputProfileData =
1738 gfxWindowsPlatform::GetPlatformCMSOutputProfileData_Impl();
1740 const auto qcmsProfile = qcms_profile_from_memory(
1741 outputProfileData.Elements(), outputProfileData.Length());
1742 MOZ_ASSERT(qcmsProfile);
1743 const auto release =
1744 MakeScopeExit([&]() { qcms_profile_release(qcmsProfile); });
1746 const auto ret = color::ColorProfileDesc::From(*qcmsProfile);
1747 bool print = gfxEnv::MOZ_GL_SPEW();
1748 if (print) {
1749 const auto gammaGuess = color::GuessGamma(ret.linearFromTf.r);
1750 printf_stderr(
1751 "Display profile:\n"
1752 " Approx Gamma: %f\n"
1753 " XYZ-D65 Red : %f, %f, %f\n"
1754 " XYZ-D65 Green: %f, %f, %f\n"
1755 " XYZ-D65 Blue : %f, %f, %f\n",
1756 gammaGuess, ret.xyzd65FromLinearRgb.at(0, 0),
1757 ret.xyzd65FromLinearRgb.at(0, 1), ret.xyzd65FromLinearRgb.at(0, 2),
1759 ret.xyzd65FromLinearRgb.at(1, 0), ret.xyzd65FromLinearRgb.at(1, 1),
1760 ret.xyzd65FromLinearRgb.at(1, 2),
1762 ret.xyzd65FromLinearRgb.at(2, 0), ret.xyzd65FromLinearRgb.at(2, 1),
1763 ret.xyzd65FromLinearRgb.at(2, 2));
1766 return ret;
1769 inline D2D1_MATRIX_5X4_F to_D2D1_MATRIX_5X4_F(const color::mat4& m) {
1770 return D2D1_MATRIX_5X4_F{{{
1771 m.rows[0][0],
1772 m.rows[1][0],
1773 m.rows[2][0],
1774 m.rows[3][0],
1775 m.rows[0][1],
1776 m.rows[1][1],
1777 m.rows[2][1],
1778 m.rows[3][1],
1779 m.rows[0][2],
1780 m.rows[1][2],
1781 m.rows[2][2],
1782 m.rows[3][2],
1783 m.rows[0][3],
1784 m.rows[1][3],
1785 m.rows[2][3],
1786 m.rows[3][3],
1791 }}};
1794 ColorManagementChain ColorManagementChain::From(
1795 IDCompositionDevice3& dcomp,
1796 const color::ColorProfileConversionDesc& conv) {
1797 auto ret = ColorManagementChain{};
1799 const auto Append = [&](const RefPtr<IDCompositionFilterEffect>& afterLast) {
1800 if (ret.last) {
1801 afterLast->SetInput(0, ret.last, 0);
1803 ret.last = afterLast;
1806 const auto MaybeAppendColorMatrix = [&](const color::mat4& m) {
1807 RefPtr<IDCompositionColorMatrixEffect> e;
1808 if (approx(m, color::mat4::Identity())) return e;
1809 dcomp.CreateColorMatrixEffect(getter_AddRefs(e));
1810 MOZ_ASSERT(e);
1811 if (!e) return e;
1812 e->SetMatrix(to_D2D1_MATRIX_5X4_F(m));
1813 Append(e);
1814 return e;
1816 const auto MaybeAppendTableTransfer = [&](const color::RgbTransferTables& t) {
1817 RefPtr<IDCompositionTableTransferEffect> e;
1818 if (!t.r.size() && !t.g.size() && !t.b.size()) return e;
1819 dcomp.CreateTableTransferEffect(getter_AddRefs(e));
1820 MOZ_ASSERT(e);
1821 if (!e) return e;
1822 e->SetRedTable(t.r.data(), t.r.size());
1823 e->SetGreenTable(t.g.data(), t.g.size());
1824 e->SetBlueTable(t.b.data(), t.b.size());
1825 Append(e);
1826 return e;
1829 ret.srcRgbFromSrcYuv = MaybeAppendColorMatrix(conv.srcRgbFromSrcYuv);
1830 ret.srcLinearFromSrcTf = MaybeAppendTableTransfer(conv.srcLinearFromSrcTf);
1831 ret.dstLinearFromSrcLinear =
1832 MaybeAppendColorMatrix(color::mat4(conv.dstLinearFromSrcLinear));
1833 ret.dstTfFromDstLinear = MaybeAppendTableTransfer(conv.dstTfFromDstLinear);
1834 return ret;
1837 ColorManagementChain::~ColorManagementChain() = default;
1839 } // namespace wr
1840 } // namespace mozilla
1842 #undef LOG_H