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/. */
6 
7 #include "DCLayerTree.h"
8 
9 #include "GLContext.h"
10 #include "GLContextEGL.h"
11 #include "mozilla/gfx/DeviceManagerDx.h"
12 #include "mozilla/gfx/Logging.h"
13 #include "mozilla/gfx/gfxVars.h"
14 #include "mozilla/StaticPrefs_gfx.h"
15 #include "mozilla/webrender/RenderD3D11TextureHost.h"
16 #include "mozilla/webrender/RenderTextureHost.h"
17 #include "mozilla/webrender/RenderThread.h"
18 #include "mozilla/WindowsVersion.h"
19 #include "mozilla/Telemetry.h"
20 #include "nsPrintfCString.h"
21 #include "WinUtils.h"
22 
23 #undef _WIN32_WINNT
24 #define _WIN32_WINNT _WIN32_WINNT_WINBLUE
25 #undef NTDDI_VERSION
26 #define NTDDI_VERSION NTDDI_WINBLUE
27 
28 #include <d3d11.h>
29 #include <d3d11_1.h>
30 #include <dcomp.h>
31 #include <dxgi1_2.h>
32 
33 namespace mozilla {
34 namespace wr {
35 
36 extern LazyLogModule gRenderThreadLog;
37 #define LOG(...) MOZ_LOG(gRenderThreadLog, LogLevel::Debug, (__VA_ARGS__))
38 
39 UniquePtr<GpuOverlayInfo> DCLayerTree::sGpuOverlayInfo;
40 
41 /* static */
Create(gl::GLContext * aGL,EGLConfig aEGLConfig,ID3D11Device * aDevice,ID3D11DeviceContext * aCtx,HWND aHwnd,nsACString & aError)42 UniquePtr<DCLayerTree> DCLayerTree::Create(gl::GLContext* aGL,
43                                            EGLConfig aEGLConfig,
44                                            ID3D11Device* aDevice,
45                                            ID3D11DeviceContext* aCtx,
46                                            HWND aHwnd, nsACString& aError) {
47   RefPtr<IDCompositionDevice2> dCompDevice =
48       gfx::DeviceManagerDx::Get()->GetDirectCompositionDevice();
49   if (!dCompDevice) {
50     aError.Assign("DCLayerTree(no device)"_ns);
51     return nullptr;
52   }
53 
54   auto layerTree =
55       MakeUnique<DCLayerTree>(aGL, aEGLConfig, aDevice, aCtx, dCompDevice);
56   if (!layerTree->Initialize(aHwnd, aError)) {
57     return nullptr;
58   }
59 
60   return layerTree;
61 }
62 
Shutdown()63 void DCLayerTree::Shutdown() { DCLayerTree::sGpuOverlayInfo = nullptr; }
64 
DCLayerTree(gl::GLContext * aGL,EGLConfig aEGLConfig,ID3D11Device * aDevice,ID3D11DeviceContext * aCtx,IDCompositionDevice2 * aCompositionDevice)65 DCLayerTree::DCLayerTree(gl::GLContext* aGL, EGLConfig aEGLConfig,
66                          ID3D11Device* aDevice, ID3D11DeviceContext* aCtx,
67                          IDCompositionDevice2* aCompositionDevice)
68     : mGL(aGL),
69       mEGLConfig(aEGLConfig),
70       mDevice(aDevice),
71       mCtx(aCtx),
72       mCompositionDevice(aCompositionDevice),
73       mDebugCounter(false),
74       mDebugVisualRedrawRegions(false),
75       mEGLImage(EGL_NO_IMAGE),
76       mColorRBO(0),
77       mPendingCommit(false) {
78   LOG("DCLayerTree::DCLayerTree()");
79 }
80 
~DCLayerTree()81 DCLayerTree::~DCLayerTree() {
82   LOG("DCLayerTree::~DCLayerTree()");
83 
84   ReleaseNativeCompositorResources();
85 }
86 
ReleaseNativeCompositorResources()87 void DCLayerTree::ReleaseNativeCompositorResources() {
88   const auto gl = GetGLContext();
89 
90   DestroyEGLSurface();
91 
92   // Delete any cached FBO objects
93   for (auto it = mFrameBuffers.begin(); it != mFrameBuffers.end(); ++it) {
94     gl->fDeleteRenderbuffers(1, &it->depthRboId);
95     gl->fDeleteFramebuffers(1, &it->fboId);
96   }
97 }
98 
Initialize(HWND aHwnd,nsACString & aError)99 bool DCLayerTree::Initialize(HWND aHwnd, nsACString& aError) {
100   HRESULT hr;
101 
102   RefPtr<IDCompositionDesktopDevice> desktopDevice;
103   hr = mCompositionDevice->QueryInterface(
104       (IDCompositionDesktopDevice**)getter_AddRefs(desktopDevice));
105   if (FAILED(hr)) {
106     aError.Assign(nsPrintfCString(
107         "DCLayerTree(get IDCompositionDesktopDevice failed %x)", hr));
108     return false;
109   }
110 
111   hr = desktopDevice->CreateTargetForHwnd(aHwnd, TRUE,
112                                           getter_AddRefs(mCompositionTarget));
113   if (FAILED(hr)) {
114     aError.Assign(nsPrintfCString(
115         "DCLayerTree(create DCompositionTarget failed %x)", hr));
116     return false;
117   }
118 
119   hr = mCompositionDevice->CreateVisual(getter_AddRefs(mRootVisual));
120   if (FAILED(hr)) {
121     aError.Assign(nsPrintfCString(
122         "DCLayerTree(create root DCompositionVisual failed %x)", hr));
123     return false;
124   }
125 
126   hr =
127       mCompositionDevice->CreateVisual(getter_AddRefs(mDefaultSwapChainVisual));
128   if (FAILED(hr)) {
129     aError.Assign(nsPrintfCString(
130         "DCLayerTree(create swap chain DCompositionVisual failed %x)", hr));
131     return false;
132   }
133 
134   if (gfx::gfxVars::UseWebRenderDCompVideoOverlayWin()) {
135     if (!InitializeVideoOverlaySupport()) {
136       RenderThread::Get()->HandleWebRenderError(WebRenderError::VIDEO_OVERLAY);
137     }
138   }
139   if (!sGpuOverlayInfo) {
140     // Set default if sGpuOverlayInfo was not set.
141     sGpuOverlayInfo = MakeUnique<GpuOverlayInfo>();
142   }
143 
144   mCompositionTarget->SetRoot(mRootVisual);
145   // Set interporation mode to nearest, to ensure 1:1 sampling.
146   // By default, a visual inherits the interpolation mode of the parent visual.
147   // If no visuals set the interpolation mode, the default for the entire visual
148   // tree is nearest neighbor interpolation.
149   mRootVisual->SetBitmapInterpolationMode(
150       DCOMPOSITION_BITMAP_INTERPOLATION_MODE_NEAREST_NEIGHBOR);
151   return true;
152 }
153 
FlagsSupportsOverlays(UINT flags)154 bool FlagsSupportsOverlays(UINT flags) {
155   return (flags & (DXGI_OVERLAY_SUPPORT_FLAG_DIRECT |
156                    DXGI_OVERLAY_SUPPORT_FLAG_SCALING));
157 }
158 
159 // A warpper of IDXGIOutput4::CheckOverlayColorSpaceSupport()
CheckOverlayColorSpaceSupport(DXGI_FORMAT aDxgiFormat,DXGI_COLOR_SPACE_TYPE aDxgiColorSpace,RefPtr<IDXGIOutput> aOutput,RefPtr<ID3D11Device> aD3d11Device)160 bool CheckOverlayColorSpaceSupport(DXGI_FORMAT aDxgiFormat,
161                                    DXGI_COLOR_SPACE_TYPE aDxgiColorSpace,
162                                    RefPtr<IDXGIOutput> aOutput,
163                                    RefPtr<ID3D11Device> aD3d11Device) {
164   UINT colorSpaceSupportFlags = 0;
165   RefPtr<IDXGIOutput4> output4;
166 
167   if (FAILED(aOutput->QueryInterface(__uuidof(IDXGIOutput4),
168                                      getter_AddRefs(output4)))) {
169     return false;
170   }
171 
172   if (FAILED(output4->CheckOverlayColorSpaceSupport(
173           aDxgiFormat, aDxgiColorSpace, aD3d11Device,
174           &colorSpaceSupportFlags))) {
175     return false;
176   }
177 
178   return (colorSpaceSupportFlags &
179           DXGI_OVERLAY_COLOR_SPACE_SUPPORT_FLAG_PRESENT);
180 }
181 
InitializeVideoOverlaySupport()182 bool DCLayerTree::InitializeVideoOverlaySupport() {
183   MOZ_ASSERT(IsWin10AnniversaryUpdateOrLater());
184 
185   HRESULT hr;
186 
187   hr = mDevice->QueryInterface(
188       (ID3D11VideoDevice**)getter_AddRefs(mVideoDevice));
189   if (FAILED(hr)) {
190     gfxCriticalNote << "Failed to get D3D11VideoDevice: " << gfx::hexa(hr);
191     return false;
192   }
193 
194   hr =
195       mCtx->QueryInterface((ID3D11VideoContext**)getter_AddRefs(mVideoContext));
196   if (FAILED(hr)) {
197     gfxCriticalNote << "Failed to get D3D11VideoContext: " << gfx::hexa(hr);
198     return false;
199   }
200 
201   if (sGpuOverlayInfo) {
202     return true;
203   }
204 
205   UniquePtr<GpuOverlayInfo> info = MakeUnique<GpuOverlayInfo>();
206 
207   RefPtr<IDXGIDevice> dxgiDevice;
208   RefPtr<IDXGIAdapter> adapter;
209   mDevice->QueryInterface((IDXGIDevice**)getter_AddRefs(dxgiDevice));
210   dxgiDevice->GetAdapter(getter_AddRefs(adapter));
211 
212   unsigned int i = 0;
213   while (true) {
214     RefPtr<IDXGIOutput> output;
215     if (FAILED(adapter->EnumOutputs(i++, getter_AddRefs(output)))) {
216       break;
217     }
218     RefPtr<IDXGIOutput3> output3;
219     if (FAILED(output->QueryInterface(__uuidof(IDXGIOutput3),
220                                       getter_AddRefs(output3)))) {
221       break;
222     }
223 
224     output3->CheckOverlaySupport(DXGI_FORMAT_NV12, mDevice,
225                                  &info->mNv12OverlaySupportFlags);
226     output3->CheckOverlaySupport(DXGI_FORMAT_YUY2, mDevice,
227                                  &info->mYuy2OverlaySupportFlags);
228     output3->CheckOverlaySupport(DXGI_FORMAT_R10G10B10A2_UNORM, mDevice,
229                                  &info->mRgb10a2OverlaySupportFlags);
230 
231     if (FlagsSupportsOverlays(info->mNv12OverlaySupportFlags)) {
232       // NV12 format is preferred if it's supported.
233       info->mOverlayFormatUsed = DXGI_FORMAT_NV12;
234       info->mSupportsHardwareOverlays = true;
235     }
236 
237     if (!info->mSupportsHardwareOverlays &&
238         FlagsSupportsOverlays(info->mYuy2OverlaySupportFlags)) {
239       // If NV12 isn't supported, fallback to YUY2 if it's supported.
240       info->mOverlayFormatUsed = DXGI_FORMAT_YUY2;
241       info->mSupportsHardwareOverlays = true;
242     }
243 
244     // RGB10A2 overlay is used for displaying HDR content. In Intel's
245     // platform, RGB10A2 overlay is enabled only when
246     // DXGI_COLOR_SPACE_RGB_FULL_G2084_NONE_P2020 is supported.
247     if (FlagsSupportsOverlays(info->mRgb10a2OverlaySupportFlags)) {
248       if (!CheckOverlayColorSpaceSupport(
249               DXGI_FORMAT_R10G10B10A2_UNORM,
250               DXGI_COLOR_SPACE_RGB_FULL_G2084_NONE_P2020, output, mDevice))
251         info->mRgb10a2OverlaySupportFlags = 0;
252     }
253 
254     // Early out after the first output that reports overlay support. All
255     // outputs are expected to report the same overlay support according to
256     // Microsoft's WDDM documentation:
257     // https://docs.microsoft.com/en-us/windows-hardware/drivers/display/multiplane-overlay-hardware-requirements
258     if (info->mSupportsHardwareOverlays) {
259       break;
260     }
261   }
262 
263   if (!StaticPrefs::gfx_webrender_dcomp_video_yuv_overlay_win_AtStartup()) {
264     info->mOverlayFormatUsed = DXGI_FORMAT_B8G8R8A8_UNORM;
265     info->mSupportsHardwareOverlays = false;
266   }
267 
268   info->mSupportsOverlays = info->mSupportsHardwareOverlays;
269 
270   sGpuOverlayInfo = std::move(info);
271   return true;
272 }
273 
GetSurface(wr::NativeSurfaceId aId) const274 DCSurface* DCLayerTree::GetSurface(wr::NativeSurfaceId aId) const {
275   auto surface_it = mDCSurfaces.find(aId);
276   MOZ_RELEASE_ASSERT(surface_it != mDCSurfaces.end());
277   return surface_it->second.get();
278 }
279 
SetDefaultSwapChain(IDXGISwapChain1 * aSwapChain)280 void DCLayerTree::SetDefaultSwapChain(IDXGISwapChain1* aSwapChain) {
281   LOG("DCLayerTree::SetDefaultSwapChain()");
282 
283   mRootVisual->AddVisual(mDefaultSwapChainVisual, TRUE, nullptr);
284   mDefaultSwapChainVisual->SetContent(aSwapChain);
285   // Default SwapChain's visual does not need linear interporation.
286   mDefaultSwapChainVisual->SetBitmapInterpolationMode(
287       DCOMPOSITION_BITMAP_INTERPOLATION_MODE_NEAREST_NEIGHBOR);
288   mPendingCommit = true;
289 }
290 
MaybeUpdateDebug()291 void DCLayerTree::MaybeUpdateDebug() {
292   bool updated = false;
293   updated |= MaybeUpdateDebugCounter();
294   updated |= MaybeUpdateDebugVisualRedrawRegions();
295   if (updated) {
296     mPendingCommit = true;
297   }
298 }
299 
MaybeCommit()300 void DCLayerTree::MaybeCommit() {
301   if (!mPendingCommit) {
302     return;
303   }
304   mCompositionDevice->Commit();
305 }
306 
WaitForCommitCompletion()307 void DCLayerTree::WaitForCommitCompletion() {
308   mCompositionDevice->WaitForCommitCompletion();
309 }
310 
DisableNativeCompositor()311 void DCLayerTree::DisableNativeCompositor() {
312   MOZ_ASSERT(mCurrentSurface.isNothing());
313   MOZ_ASSERT(mCurrentLayers.empty());
314 
315   ReleaseNativeCompositorResources();
316   mPrevLayers.clear();
317   mRootVisual->RemoveAllVisuals();
318 }
319 
MaybeUpdateDebugCounter()320 bool DCLayerTree::MaybeUpdateDebugCounter() {
321   bool debugCounter = StaticPrefs::gfx_webrender_debug_dcomp_counter();
322   if (mDebugCounter == debugCounter) {
323     return false;
324   }
325 
326   RefPtr<IDCompositionDeviceDebug> debugDevice;
327   HRESULT hr = mCompositionDevice->QueryInterface(
328       (IDCompositionDeviceDebug**)getter_AddRefs(debugDevice));
329   if (FAILED(hr)) {
330     return false;
331   }
332 
333   if (debugCounter) {
334     debugDevice->EnableDebugCounters();
335   } else {
336     debugDevice->DisableDebugCounters();
337   }
338 
339   mDebugCounter = debugCounter;
340   return true;
341 }
342 
MaybeUpdateDebugVisualRedrawRegions()343 bool DCLayerTree::MaybeUpdateDebugVisualRedrawRegions() {
344   bool debugVisualRedrawRegions =
345       StaticPrefs::gfx_webrender_debug_dcomp_redraw_regions();
346   if (mDebugVisualRedrawRegions == debugVisualRedrawRegions) {
347     return false;
348   }
349 
350   RefPtr<IDCompositionVisualDebug> visualDebug;
351   HRESULT hr = mRootVisual->QueryInterface(
352       (IDCompositionVisualDebug**)getter_AddRefs(visualDebug));
353   if (FAILED(hr)) {
354     return false;
355   }
356 
357   if (debugVisualRedrawRegions) {
358     visualDebug->EnableRedrawRegions();
359   } else {
360     visualDebug->DisableRedrawRegions();
361   }
362 
363   mDebugVisualRedrawRegions = debugVisualRedrawRegions;
364   return true;
365 }
366 
CompositorBeginFrame()367 void DCLayerTree::CompositorBeginFrame() { mCurrentFrame++; }
368 
CompositorEndFrame()369 void DCLayerTree::CompositorEndFrame() {
370   auto start = TimeStamp::Now();
371   // Check if the visual tree of surfaces is the same as last frame.
372   bool same = mPrevLayers == mCurrentLayers;
373 
374   if (!same) {
375     // If not, we need to rebuild the visual tree. Note that addition or
376     // removal of tiles no longer needs to rebuild the main visual tree
377     // here, since they are added as children of the surface visual.
378     mRootVisual->RemoveAllVisuals();
379   }
380 
381   for (auto it = mCurrentLayers.begin(); it != mCurrentLayers.end(); ++it) {
382     auto surface_it = mDCSurfaces.find(*it);
383     MOZ_RELEASE_ASSERT(surface_it != mDCSurfaces.end());
384     const auto surface = surface_it->second.get();
385     // Ensure surface is trimmed to updated tile valid rects
386     surface->UpdateAllocatedRect();
387     if (!same) {
388       // Add surfaces in z-order they were added to the scene.
389       const auto visual = surface->GetVisual();
390       mRootVisual->AddVisual(visual, FALSE, nullptr);
391     }
392   }
393 
394   mPrevLayers.swap(mCurrentLayers);
395   mCurrentLayers.clear();
396 
397   mCompositionDevice->Commit();
398 
399   auto end = TimeStamp::Now();
400   mozilla::Telemetry::Accumulate(mozilla::Telemetry::COMPOSITE_SWAP_TIME,
401                                  (end - start).ToMilliseconds() * 10.);
402 
403   // Remove any framebuffers that haven't been
404   // used in the last 60 frames.
405   //
406   // This should use nsTArray::RemoveElementsBy once
407   // CachedFrameBuffer is able to properly destroy
408   // itself in the destructor.
409   const auto gl = GetGLContext();
410   for (uint32_t i = 0, len = mFrameBuffers.Length(); i < len; ++i) {
411     auto& fb = mFrameBuffers[i];
412     if ((mCurrentFrame - fb.lastFrameUsed) > 60) {
413       gl->fDeleteRenderbuffers(1, &fb.depthRboId);
414       gl->fDeleteFramebuffers(1, &fb.fboId);
415       mFrameBuffers.UnorderedRemoveElementAt(i);
416       --i;  // Examine the element again, if necessary.
417       --len;
418     }
419   }
420 }
421 
Bind(wr::NativeTileId aId,wr::DeviceIntPoint * aOffset,uint32_t * aFboId,wr::DeviceIntRect aDirtyRect,wr::DeviceIntRect aValidRect)422 void DCLayerTree::Bind(wr::NativeTileId aId, wr::DeviceIntPoint* aOffset,
423                        uint32_t* aFboId, wr::DeviceIntRect aDirtyRect,
424                        wr::DeviceIntRect aValidRect) {
425   auto surface = GetSurface(aId.surface_id);
426   auto tile = surface->GetTile(aId.x, aId.y);
427   wr::DeviceIntPoint targetOffset{0, 0};
428 
429   gfx::IntRect validRect(aValidRect.min.x, aValidRect.min.y, aValidRect.width(),
430                          aValidRect.height());
431   if (!tile->mValidRect.IsEqualEdges(validRect)) {
432     tile->mValidRect = validRect;
433     surface->DirtyAllocatedRect();
434   }
435   wr::DeviceIntSize tileSize = surface->GetTileSize();
436   RefPtr<IDCompositionSurface> compositionSurface =
437       surface->GetCompositionSurface();
438   wr::DeviceIntPoint virtualOffset = surface->GetVirtualOffset();
439   targetOffset.x = virtualOffset.x + tileSize.width * aId.x;
440   targetOffset.y = virtualOffset.y + tileSize.height * aId.y;
441 
442   *aFboId = CreateEGLSurfaceForCompositionSurface(
443       aDirtyRect, aOffset, compositionSurface, targetOffset);
444   mCurrentSurface = Some(compositionSurface);
445 }
446 
Unbind()447 void DCLayerTree::Unbind() {
448   if (mCurrentSurface.isNothing()) {
449     return;
450   }
451 
452   RefPtr<IDCompositionSurface> surface = mCurrentSurface.ref();
453   surface->EndDraw();
454 
455   DestroyEGLSurface();
456   mCurrentSurface = Nothing();
457 }
458 
CreateSurface(wr::NativeSurfaceId aId,wr::DeviceIntPoint aVirtualOffset,wr::DeviceIntSize aTileSize,bool aIsOpaque)459 void DCLayerTree::CreateSurface(wr::NativeSurfaceId aId,
460                                 wr::DeviceIntPoint aVirtualOffset,
461                                 wr::DeviceIntSize aTileSize, bool aIsOpaque) {
462   auto it = mDCSurfaces.find(aId);
463   MOZ_RELEASE_ASSERT(it == mDCSurfaces.end());
464   if (it != mDCSurfaces.end()) {
465     // DCSurface already exists.
466     return;
467   }
468 
469   // Tile size needs to be positive.
470   if (aTileSize.width <= 0 || aTileSize.height <= 0) {
471     gfxCriticalNote << "TileSize is not positive aId: " << wr::AsUint64(aId)
472                     << " aTileSize(" << aTileSize.width << ","
473                     << aTileSize.height << ")";
474   }
475 
476   auto surface =
477       MakeUnique<DCSurface>(aTileSize, aVirtualOffset, aIsOpaque, this);
478   if (!surface->Initialize()) {
479     gfxCriticalNote << "Failed to initialize DCSurface: " << wr::AsUint64(aId);
480     return;
481   }
482 
483   mDCSurfaces[aId] = std::move(surface);
484 }
485 
CreateExternalSurface(wr::NativeSurfaceId aId,bool aIsOpaque)486 void DCLayerTree::CreateExternalSurface(wr::NativeSurfaceId aId,
487                                         bool aIsOpaque) {
488   auto it = mDCSurfaces.find(aId);
489   MOZ_RELEASE_ASSERT(it == mDCSurfaces.end());
490 
491   auto surface = MakeUnique<DCSurfaceVideo>(aIsOpaque, this);
492   if (!surface->Initialize()) {
493     gfxCriticalNote << "Failed to initialize DCSurfaceVideo: "
494                     << wr::AsUint64(aId);
495     return;
496   }
497 
498   mDCSurfaces[aId] = std::move(surface);
499 }
500 
DestroySurface(NativeSurfaceId aId)501 void DCLayerTree::DestroySurface(NativeSurfaceId aId) {
502   auto surface_it = mDCSurfaces.find(aId);
503   MOZ_RELEASE_ASSERT(surface_it != mDCSurfaces.end());
504   auto surface = surface_it->second.get();
505 
506   mRootVisual->RemoveVisual(surface->GetVisual());
507   mDCSurfaces.erase(surface_it);
508 }
509 
CreateTile(wr::NativeSurfaceId aId,int aX,int aY)510 void DCLayerTree::CreateTile(wr::NativeSurfaceId aId, int aX, int aY) {
511   auto surface = GetSurface(aId);
512   surface->CreateTile(aX, aY);
513 }
514 
DestroyTile(wr::NativeSurfaceId aId,int aX,int aY)515 void DCLayerTree::DestroyTile(wr::NativeSurfaceId aId, int aX, int aY) {
516   auto surface = GetSurface(aId);
517   surface->DestroyTile(aX, aY);
518 }
519 
AttachExternalImage(wr::NativeSurfaceId aId,wr::ExternalImageId aExternalImage)520 void DCLayerTree::AttachExternalImage(wr::NativeSurfaceId aId,
521                                       wr::ExternalImageId aExternalImage) {
522   auto surface_it = mDCSurfaces.find(aId);
523   MOZ_RELEASE_ASSERT(surface_it != mDCSurfaces.end());
524   auto* surfaceVideo = surface_it->second->AsDCSurfaceVideo();
525   MOZ_RELEASE_ASSERT(surfaceVideo);
526 
527   surfaceVideo->AttachExternalImage(aExternalImage);
528 }
529 
530 template <typename T>
D2DRect(const T & aRect)531 static inline D2D1_RECT_F D2DRect(const T& aRect) {
532   return D2D1::RectF(aRect.X(), aRect.Y(), aRect.XMost(), aRect.YMost());
533 }
534 
D2DMatrix(const gfx::Matrix & aTransform)535 static inline D2D1_MATRIX_3X2_F D2DMatrix(const gfx::Matrix& aTransform) {
536   return D2D1::Matrix3x2F(aTransform._11, aTransform._12, aTransform._21,
537                           aTransform._22, aTransform._31, aTransform._32);
538 }
539 
AddSurface(wr::NativeSurfaceId aId,const wr::CompositorSurfaceTransform & aTransform,wr::DeviceIntRect aClipRect,wr::ImageRendering aImageRendering)540 void DCLayerTree::AddSurface(wr::NativeSurfaceId aId,
541                              const wr::CompositorSurfaceTransform& aTransform,
542                              wr::DeviceIntRect aClipRect,
543                              wr::ImageRendering aImageRendering) {
544   auto it = mDCSurfaces.find(aId);
545   MOZ_RELEASE_ASSERT(it != mDCSurfaces.end());
546   const auto surface = it->second.get();
547   const auto visual = surface->GetVisual();
548 
549   wr::DeviceIntPoint virtualOffset = surface->GetVirtualOffset();
550 
551   gfx::Matrix transform(aTransform.m11, aTransform.m12, aTransform.m21,
552                         aTransform.m22, aTransform.m41, aTransform.m42);
553   transform.PreTranslate(-virtualOffset.x, -virtualOffset.y);
554 
555   // The DirectComposition API applies clipping *before* any transforms/offset,
556   // whereas we want the clip applied after.
557   // Right now, we only support rectilinear transforms, and then we transform
558   // our clip into pre-transform coordinate space for it to be applied there.
559   // DirectComposition does have an option for pre-transform clipping, if you
560   // create an explicit IDCompositionEffectGroup object and set a 3D transform
561   // on that. I suspect that will perform worse though, so we should only do
562   // that for complex transforms (which are never provided right now).
563   MOZ_ASSERT(transform.IsRectilinear());
564   gfx::Rect clip = transform.Inverse().TransformBounds(gfx::Rect(
565       aClipRect.min.x, aClipRect.min.y, aClipRect.width(), aClipRect.height()));
566   // Set the clip rect - converting from world space to the pre-offset space
567   // that DC requires for rectangle clips.
568   visual->SetClip(D2DRect(clip));
569 
570   // TODO: The input matrix is a 4x4, but we only support a 3x2 at
571   // the D3D API level (unless we QI to IDCompositionVisual3, which might
572   // not be available?).
573   // Should we assert here, or restrict at the WR API level.
574   visual->SetTransform(D2DMatrix(transform));
575 
576   if (aImageRendering == wr::ImageRendering::Auto) {
577     visual->SetBitmapInterpolationMode(
578         DCOMPOSITION_BITMAP_INTERPOLATION_MODE_LINEAR);
579   } else {
580     visual->SetBitmapInterpolationMode(
581         DCOMPOSITION_BITMAP_INTERPOLATION_MODE_NEAREST_NEIGHBOR);
582   }
583 
584   mCurrentLayers.push_back(aId);
585 }
586 
GetOrCreateFbo(int aWidth,int aHeight)587 GLuint DCLayerTree::GetOrCreateFbo(int aWidth, int aHeight) {
588   const auto gl = GetGLContext();
589   GLuint fboId = 0;
590 
591   // Check if we have a cached FBO with matching dimensions
592   for (auto it = mFrameBuffers.begin(); it != mFrameBuffers.end(); ++it) {
593     if (it->width == aWidth && it->height == aHeight) {
594       fboId = it->fboId;
595       it->lastFrameUsed = mCurrentFrame;
596       break;
597     }
598   }
599 
600   // If not, create a new FBO with attached depth buffer
601   if (fboId == 0) {
602     // Create the depth buffer
603     GLuint depthRboId;
604     gl->fGenRenderbuffers(1, &depthRboId);
605     gl->fBindRenderbuffer(LOCAL_GL_RENDERBUFFER, depthRboId);
606     gl->fRenderbufferStorage(LOCAL_GL_RENDERBUFFER, LOCAL_GL_DEPTH_COMPONENT24,
607                              aWidth, aHeight);
608 
609     // Create the framebuffer and attach the depth buffer to it
610     gl->fGenFramebuffers(1, &fboId);
611     gl->fBindFramebuffer(LOCAL_GL_DRAW_FRAMEBUFFER, fboId);
612     gl->fFramebufferRenderbuffer(LOCAL_GL_DRAW_FRAMEBUFFER,
613                                  LOCAL_GL_DEPTH_ATTACHMENT,
614                                  LOCAL_GL_RENDERBUFFER, depthRboId);
615 
616     // Store this in the cache for future calls.
617     // TODO(gw): Maybe we should periodically scan this list and remove old
618     // entries that
619     //           haven't been used for some time?
620     DCLayerTree::CachedFrameBuffer frame_buffer_info;
621     frame_buffer_info.width = aWidth;
622     frame_buffer_info.height = aHeight;
623     frame_buffer_info.fboId = fboId;
624     frame_buffer_info.depthRboId = depthRboId;
625     frame_buffer_info.lastFrameUsed = mCurrentFrame;
626     mFrameBuffers.AppendElement(frame_buffer_info);
627   }
628 
629   return fboId;
630 }
631 
EnsureVideoProcessor(const gfx::IntSize & aVideoSize)632 bool DCLayerTree::EnsureVideoProcessor(const gfx::IntSize& aVideoSize) {
633   HRESULT hr;
634 
635   if (!mVideoDevice || !mVideoContext) {
636     return false;
637   }
638 
639   if (mVideoProcessor && aVideoSize == mVideoSize) {
640     return true;
641   }
642 
643   mVideoProcessor = nullptr;
644   mVideoProcessorEnumerator = nullptr;
645 
646   D3D11_VIDEO_PROCESSOR_CONTENT_DESC desc = {};
647   desc.InputFrameFormat = D3D11_VIDEO_FRAME_FORMAT_PROGRESSIVE;
648   desc.InputFrameRate.Numerator = 60;
649   desc.InputFrameRate.Denominator = 1;
650   desc.InputWidth = aVideoSize.width;
651   desc.InputHeight = aVideoSize.height;
652   desc.OutputFrameRate.Numerator = 60;
653   desc.OutputFrameRate.Denominator = 1;
654   desc.OutputWidth = aVideoSize.width;
655   desc.OutputHeight = aVideoSize.height;
656   desc.Usage = D3D11_VIDEO_USAGE_PLAYBACK_NORMAL;
657 
658   hr = mVideoDevice->CreateVideoProcessorEnumerator(
659       &desc, getter_AddRefs(mVideoProcessorEnumerator));
660   if (FAILED(hr)) {
661     gfxCriticalNote << "Failed to create VideoProcessorEnumerator: "
662                     << gfx::hexa(hr);
663     return false;
664   }
665 
666   hr = mVideoDevice->CreateVideoProcessor(mVideoProcessorEnumerator, 0,
667                                           getter_AddRefs(mVideoProcessor));
668   if (FAILED(hr)) {
669     mVideoProcessor = nullptr;
670     mVideoProcessorEnumerator = nullptr;
671     gfxCriticalNote << "Failed to create VideoProcessor: " << gfx::hexa(hr);
672     return false;
673   }
674 
675   // Reduce power cosumption
676   // By default, the driver might perform certain processing tasks automatically
677   mVideoContext->VideoProcessorSetStreamAutoProcessingMode(mVideoProcessor, 0,
678                                                            FALSE);
679 
680   mVideoSize = aVideoSize;
681   return true;
682 }
683 
SupportsHardwareOverlays()684 bool DCLayerTree::SupportsHardwareOverlays() {
685   return sGpuOverlayInfo->mSupportsHardwareOverlays;
686 }
687 
GetOverlayFormatForSDR()688 DXGI_FORMAT DCLayerTree::GetOverlayFormatForSDR() {
689   return sGpuOverlayInfo->mOverlayFormatUsed;
690 }
691 
DCSurface(wr::DeviceIntSize aTileSize,wr::DeviceIntPoint aVirtualOffset,bool aIsOpaque,DCLayerTree * aDCLayerTree)692 DCSurface::DCSurface(wr::DeviceIntSize aTileSize,
693                      wr::DeviceIntPoint aVirtualOffset, bool aIsOpaque,
694                      DCLayerTree* aDCLayerTree)
695     : mDCLayerTree(aDCLayerTree),
696       mTileSize(aTileSize),
697       mIsOpaque(aIsOpaque),
698       mAllocatedRectDirty(true),
699       mVirtualOffset(aVirtualOffset) {}
700 
~DCSurface()701 DCSurface::~DCSurface() {}
702 
Initialize()703 bool DCSurface::Initialize() {
704   HRESULT hr;
705   const auto dCompDevice = mDCLayerTree->GetCompositionDevice();
706   hr = dCompDevice->CreateVisual(getter_AddRefs(mVisual));
707   if (FAILED(hr)) {
708     gfxCriticalNote << "Failed to create DCompositionVisual: " << gfx::hexa(hr);
709     return false;
710   }
711 
712   DXGI_ALPHA_MODE alpha_mode =
713       mIsOpaque ? DXGI_ALPHA_MODE_IGNORE : DXGI_ALPHA_MODE_PREMULTIPLIED;
714 
715   hr = dCompDevice->CreateVirtualSurface(
716       VIRTUAL_SURFACE_SIZE, VIRTUAL_SURFACE_SIZE, DXGI_FORMAT_R8G8B8A8_UNORM,
717       alpha_mode, getter_AddRefs(mVirtualSurface));
718   MOZ_ASSERT(SUCCEEDED(hr));
719 
720   // Bind the surface memory to this visual
721   hr = mVisual->SetContent(mVirtualSurface);
722   MOZ_ASSERT(SUCCEEDED(hr));
723 
724   return true;
725 }
726 
CreateTile(int aX,int aY)727 void DCSurface::CreateTile(int aX, int aY) {
728   TileKey key(aX, aY);
729   MOZ_RELEASE_ASSERT(mDCTiles.find(key) == mDCTiles.end());
730 
731   auto tile = MakeUnique<DCTile>(mDCLayerTree);
732   if (!tile->Initialize(aX, aY, mTileSize, mIsOpaque)) {
733     gfxCriticalNote << "Failed to initialize DCTile: " << aX << aY;
734     return;
735   }
736 
737   mAllocatedRectDirty = true;
738 
739   mDCTiles[key] = std::move(tile);
740 }
741 
DestroyTile(int aX,int aY)742 void DCSurface::DestroyTile(int aX, int aY) {
743   TileKey key(aX, aY);
744   mAllocatedRectDirty = true;
745   mDCTiles.erase(key);
746 }
747 
DirtyAllocatedRect()748 void DCSurface::DirtyAllocatedRect() { mAllocatedRectDirty = true; }
749 
UpdateAllocatedRect()750 void DCSurface::UpdateAllocatedRect() {
751   if (mAllocatedRectDirty) {
752     // The virtual surface may have holes in it (for example, an empty tile
753     // that has no primitives). Instead of trimming to a single bounding
754     // rect, supply the rect of each valid tile to handle this case.
755     std::vector<RECT> validRects;
756 
757     for (auto it = mDCTiles.begin(); it != mDCTiles.end(); ++it) {
758       auto tile = GetTile(it->first.mX, it->first.mY);
759       RECT rect;
760 
761       rect.left = (LONG)(mVirtualOffset.x + it->first.mX * mTileSize.width +
762                          tile->mValidRect.x);
763       rect.top = (LONG)(mVirtualOffset.y + it->first.mY * mTileSize.height +
764                         tile->mValidRect.y);
765       rect.right = rect.left + tile->mValidRect.width;
766       rect.bottom = rect.top + tile->mValidRect.height;
767 
768       validRects.push_back(rect);
769     }
770 
771     mVirtualSurface->Trim(validRects.data(), validRects.size());
772     mAllocatedRectDirty = false;
773   }
774 }
775 
GetTile(int aX,int aY) const776 DCTile* DCSurface::GetTile(int aX, int aY) const {
777   TileKey key(aX, aY);
778   auto tile_it = mDCTiles.find(key);
779   MOZ_RELEASE_ASSERT(tile_it != mDCTiles.end());
780   return tile_it->second.get();
781 }
782 
DCSurfaceVideo(bool aIsOpaque,DCLayerTree * aDCLayerTree)783 DCSurfaceVideo::DCSurfaceVideo(bool aIsOpaque, DCLayerTree* aDCLayerTree)
784     : DCSurface(wr::DeviceIntSize{}, wr::DeviceIntPoint{}, aIsOpaque,
785                 aDCLayerTree) {}
786 
AttachExternalImage(wr::ExternalImageId aExternalImage)787 void DCSurfaceVideo::AttachExternalImage(wr::ExternalImageId aExternalImage) {
788   RenderTextureHost* texture =
789       RenderThread::Get()->GetRenderTexture(aExternalImage);
790   MOZ_RELEASE_ASSERT(texture);
791 
792   if (mPrevTexture == texture) {
793     return;
794   }
795 
796   // XXX if software decoded video frame format is nv12, it could be used as
797   // video overlay.
798   if (!texture || !texture->AsRenderDXGITextureHost() ||
799       texture->AsRenderDXGITextureHost()->GetFormat() !=
800           gfx::SurfaceFormat::NV12) {
801     gfxCriticalNote << "Unsupported RenderTexture for overlay: "
802                     << gfx::hexa(texture);
803     return;
804   }
805 
806   gfx::IntSize size = texture->AsRenderDXGITextureHost()->GetSize(0);
807   if (!mVideoSwapChain || mSwapChainSize != size) {
808     ReleaseDecodeSwapChainResources();
809     CreateVideoSwapChain(texture);
810   }
811 
812   if (!mVideoSwapChain) {
813     gfxCriticalNote << "Failed to create VideoSwapChain";
814     RenderThread::Get()->NotifyWebRenderError(
815         wr::WebRenderError::VIDEO_OVERLAY);
816     return;
817   }
818 
819   mVisual->SetContent(mVideoSwapChain);
820 
821   if (!CallVideoProcessorBlt(texture)) {
822     RenderThread::Get()->NotifyWebRenderError(
823         wr::WebRenderError::VIDEO_OVERLAY);
824     return;
825   }
826 
827   mVideoSwapChain->Present(0, 0);
828   mPrevTexture = texture;
829 }
830 
IsYUVSwapChainFormat(DXGI_FORMAT aFormat)831 bool IsYUVSwapChainFormat(DXGI_FORMAT aFormat) {
832   if (aFormat == DXGI_FORMAT_NV12 || aFormat == DXGI_FORMAT_YUY2) {
833     return true;
834   }
835   return false;
836 }
837 
GetSwapChainFormat()838 DXGI_FORMAT DCSurfaceVideo::GetSwapChainFormat() {
839   if (mFailedToCreateYuvSwapChain ||
840       !mDCLayerTree->SupportsHardwareOverlays()) {
841     return DXGI_FORMAT_B8G8R8A8_UNORM;
842   }
843   return mDCLayerTree->GetOverlayFormatForSDR();
844 }
845 
CreateVideoSwapChain(RenderTextureHost * aTexture)846 bool DCSurfaceVideo::CreateVideoSwapChain(RenderTextureHost* aTexture) {
847   const auto device = mDCLayerTree->GetDevice();
848 
849   RefPtr<IDXGIDevice> dxgiDevice;
850   device->QueryInterface((IDXGIDevice**)getter_AddRefs(dxgiDevice));
851 
852   RefPtr<IDXGIFactoryMedia> dxgiFactoryMedia;
853   {
854     RefPtr<IDXGIAdapter> adapter;
855     dxgiDevice->GetAdapter(getter_AddRefs(adapter));
856     adapter->GetParent(
857         IID_PPV_ARGS((IDXGIFactoryMedia**)getter_AddRefs(dxgiFactoryMedia)));
858   }
859 
860   mSwapChainSurfaceHandle = gfx::DeviceManagerDx::CreateDCompSurfaceHandle();
861   if (!mSwapChainSurfaceHandle) {
862     gfxCriticalNote << "Failed to create DCompSurfaceHandle";
863     return false;
864   }
865 
866   gfx::IntSize size = aTexture->AsRenderDXGITextureHost()->GetSize(0);
867   auto swapChainFormat = GetSwapChainFormat();
868 
869   DXGI_SWAP_CHAIN_DESC1 desc = {};
870   desc.Width = size.width;
871   desc.Height = size.height;
872   desc.Format = swapChainFormat;
873   desc.Stereo = FALSE;
874   desc.SampleDesc.Count = 1;
875   desc.BufferCount = 2;
876   desc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
877   desc.Scaling = DXGI_SCALING_STRETCH;
878   desc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL;
879   desc.Flags = DXGI_SWAP_CHAIN_FLAG_FULLSCREEN_VIDEO;
880   if (IsYUVSwapChainFormat(swapChainFormat)) {
881     desc.Flags |= DXGI_SWAP_CHAIN_FLAG_YUV_VIDEO;
882   }
883   desc.AlphaMode = DXGI_ALPHA_MODE_IGNORE;
884 
885   HRESULT hr;
886   hr = dxgiFactoryMedia->CreateSwapChainForCompositionSurfaceHandle(
887       device, mSwapChainSurfaceHandle, &desc, nullptr,
888       getter_AddRefs(mVideoSwapChain));
889 
890   if (FAILED(hr)) {
891     mFailedToCreateYuvSwapChain = true;
892     gfxCriticalNote << "Failed to create video SwapChain: " << gfx::hexa(hr);
893     return false;
894   }
895 
896   mSwapChainSize = size;
897   mSwapChainFormat = swapChainFormat;
898   return true;
899 }
900 
901 // TODO: Replace with YUVRangedColorSpace
GetSourceDXGIColorSpace(const gfx::YUVColorSpace aYUVColorSpace,const gfx::ColorRange aColorRange)902 static Maybe<DXGI_COLOR_SPACE_TYPE> GetSourceDXGIColorSpace(
903     const gfx::YUVColorSpace aYUVColorSpace,
904     const gfx::ColorRange aColorRange) {
905   if (aYUVColorSpace == gfx::YUVColorSpace::BT601) {
906     if (aColorRange == gfx::ColorRange::FULL) {
907       return Some(DXGI_COLOR_SPACE_YCBCR_FULL_G22_LEFT_P601);
908     } else {
909       return Some(DXGI_COLOR_SPACE_YCBCR_STUDIO_G22_LEFT_P601);
910     }
911   } else if (aYUVColorSpace == gfx::YUVColorSpace::BT709) {
912     if (aColorRange == gfx::ColorRange::FULL) {
913       return Some(DXGI_COLOR_SPACE_YCBCR_FULL_G22_LEFT_P709);
914     } else {
915       return Some(DXGI_COLOR_SPACE_YCBCR_STUDIO_G22_LEFT_P709);
916     }
917   } else if (aYUVColorSpace == gfx::YUVColorSpace::BT2020) {
918     if (aColorRange == gfx::ColorRange::FULL) {
919       // XXX Add SMPTEST2084 handling. HDR content is not handled yet by
920       // video overlay.
921       return Some(DXGI_COLOR_SPACE_YCBCR_FULL_G22_LEFT_P2020);
922     } else {
923       return Some(DXGI_COLOR_SPACE_YCBCR_STUDIO_G22_LEFT_P2020);
924     }
925   }
926 
927   return Nothing();
928 }
929 
GetSourceDXGIColorSpace(const gfx::YUVRangedColorSpace aYUVColorSpace)930 static Maybe<DXGI_COLOR_SPACE_TYPE> GetSourceDXGIColorSpace(
931     const gfx::YUVRangedColorSpace aYUVColorSpace) {
932   const auto info = FromYUVRangedColorSpace(aYUVColorSpace);
933   return GetSourceDXGIColorSpace(info.space, info.range);
934 }
935 
CallVideoProcessorBlt(RenderTextureHost * aTexture)936 bool DCSurfaceVideo::CallVideoProcessorBlt(RenderTextureHost* aTexture) {
937   HRESULT hr;
938   const auto videoDevice = mDCLayerTree->GetVideoDevice();
939   const auto videoContext = mDCLayerTree->GetVideoContext();
940   const auto texture = aTexture->AsRenderDXGITextureHost();
941 
942   Maybe<DXGI_COLOR_SPACE_TYPE> sourceColorSpace =
943       GetSourceDXGIColorSpace(texture->GetYUVColorSpace());
944   if (sourceColorSpace.isNothing()) {
945     gfxCriticalNote << "Unsupported color space";
946     return false;
947   }
948 
949   RefPtr<ID3D11Texture2D> texture2D = texture->GetD3D11Texture2DWithGL();
950   if (!texture2D) {
951     gfxCriticalNote << "Failed to get D3D11Texture2D";
952     return false;
953   }
954 
955   if (!mVideoSwapChain) {
956     return false;
957   }
958 
959   if (!mDCLayerTree->EnsureVideoProcessor(mSwapChainSize)) {
960     gfxCriticalNote << "EnsureVideoProcessor Failed";
961     return false;
962   }
963 
964   RefPtr<IDXGISwapChain3> swapChain3;
965   mVideoSwapChain->QueryInterface(
966       (IDXGISwapChain3**)getter_AddRefs(swapChain3));
967   if (!swapChain3) {
968     gfxCriticalNote << "Failed to get IDXGISwapChain3";
969     return false;
970   }
971 
972   RefPtr<ID3D11VideoContext1> videoContext1;
973   videoContext->QueryInterface(
974       (ID3D11VideoContext1**)getter_AddRefs(videoContext1));
975   if (!videoContext1) {
976     gfxCriticalNote << "Failed to get ID3D11VideoContext1";
977     return false;
978   }
979 
980   const auto videoProcessor = mDCLayerTree->GetVideoProcessor();
981   const auto videoProcessorEnumerator =
982       mDCLayerTree->GetVideoProcessorEnumerator();
983 
984   DXGI_COLOR_SPACE_TYPE inputColorSpace = sourceColorSpace.ref();
985   videoContext1->VideoProcessorSetStreamColorSpace1(videoProcessor, 0,
986                                                     inputColorSpace);
987 
988   DXGI_COLOR_SPACE_TYPE outputColorSpace =
989       IsYUVSwapChainFormat(mSwapChainFormat)
990           ? inputColorSpace
991           : DXGI_COLOR_SPACE_RGB_FULL_G22_NONE_P709;
992   hr = swapChain3->SetColorSpace1(outputColorSpace);
993   if (FAILED(hr)) {
994     gfxCriticalNote << "SetColorSpace1 failed: " << gfx::hexa(hr);
995     return false;
996   }
997   videoContext1->VideoProcessorSetOutputColorSpace1(videoProcessor,
998                                                     outputColorSpace);
999 
1000   D3D11_VIDEO_PROCESSOR_INPUT_VIEW_DESC inputDesc = {};
1001   inputDesc.ViewDimension = D3D11_VPIV_DIMENSION_TEXTURE2D;
1002   inputDesc.Texture2D.ArraySlice = texture->ArrayIndex();
1003 
1004   RefPtr<ID3D11VideoProcessorInputView> inputView;
1005   hr = videoDevice->CreateVideoProcessorInputView(
1006       texture2D, videoProcessorEnumerator, &inputDesc,
1007       getter_AddRefs(inputView));
1008   if (FAILED(hr)) {
1009     gfxCriticalNote << "ID3D11VideoProcessorInputView creation failed: "
1010                     << gfx::hexa(hr);
1011     return false;
1012   }
1013 
1014   D3D11_VIDEO_PROCESSOR_STREAM stream = {};
1015   stream.Enable = true;
1016   stream.OutputIndex = 0;
1017   stream.InputFrameOrField = 0;
1018   stream.PastFrames = 0;
1019   stream.FutureFrames = 0;
1020   stream.pInputSurface = inputView.get();
1021 
1022   RECT destRect;
1023   destRect.left = 0;
1024   destRect.top = 0;
1025   destRect.right = mSwapChainSize.width;
1026   destRect.bottom = mSwapChainSize.height;
1027 
1028   videoContext->VideoProcessorSetOutputTargetRect(videoProcessor, TRUE,
1029                                                   &destRect);
1030   videoContext->VideoProcessorSetStreamDestRect(videoProcessor, 0, TRUE,
1031                                                 &destRect);
1032   RECT sourceRect;
1033   sourceRect.left = 0;
1034   sourceRect.top = 0;
1035   sourceRect.right = mSwapChainSize.width;
1036   sourceRect.bottom = mSwapChainSize.height;
1037   videoContext->VideoProcessorSetStreamSourceRect(videoProcessor, 0, TRUE,
1038                                                   &sourceRect);
1039 
1040   if (!mOutputView) {
1041     RefPtr<ID3D11Texture2D> backBuf;
1042     mVideoSwapChain->GetBuffer(0, __uuidof(ID3D11Texture2D),
1043                                (void**)getter_AddRefs(backBuf));
1044 
1045     D3D11_VIDEO_PROCESSOR_OUTPUT_VIEW_DESC outputDesc = {};
1046     outputDesc.ViewDimension = D3D11_VPOV_DIMENSION_TEXTURE2D;
1047     outputDesc.Texture2D.MipSlice = 0;
1048 
1049     hr = videoDevice->CreateVideoProcessorOutputView(
1050         backBuf, videoProcessorEnumerator, &outputDesc,
1051         getter_AddRefs(mOutputView));
1052     if (FAILED(hr)) {
1053       gfxCriticalNote << "ID3D11VideoProcessorOutputView creation failed: "
1054                       << gfx::hexa(hr);
1055       return false;
1056     }
1057   }
1058 
1059   hr = videoContext->VideoProcessorBlt(videoProcessor, mOutputView, 0, 1,
1060                                        &stream);
1061   if (FAILED(hr)) {
1062     gfxCriticalNote << "VideoProcessorBlt failed: " << gfx::hexa(hr);
1063     return false;
1064   }
1065 
1066   return true;
1067 }
1068 
ReleaseDecodeSwapChainResources()1069 void DCSurfaceVideo::ReleaseDecodeSwapChainResources() {
1070   mOutputView = nullptr;
1071   mVideoSwapChain = nullptr;
1072   mDecodeSwapChain = nullptr;
1073   mDecodeResource = nullptr;
1074   if (mSwapChainSurfaceHandle) {
1075     ::CloseHandle(mSwapChainSurfaceHandle);
1076     mSwapChainSurfaceHandle = 0;
1077   }
1078   mSwapChainSize = gfx::IntSize();
1079 }
1080 
DCTile(DCLayerTree * aDCLayerTree)1081 DCTile::DCTile(DCLayerTree* aDCLayerTree) : mDCLayerTree(aDCLayerTree) {}
1082 
~DCTile()1083 DCTile::~DCTile() {}
1084 
Initialize(int aX,int aY,wr::DeviceIntSize aSize,bool aIsOpaque)1085 bool DCTile::Initialize(int aX, int aY, wr::DeviceIntSize aSize,
1086                         bool aIsOpaque) {
1087   if (aSize.width <= 0 || aSize.height <= 0) {
1088     return false;
1089   }
1090 
1091   // Initially, the entire tile is considered valid, unless it is set by
1092   // the SetTileProperties method.
1093   mValidRect.x = 0;
1094   mValidRect.y = 0;
1095   mValidRect.width = aSize.width;
1096   mValidRect.height = aSize.height;
1097 
1098   return true;
1099 }
1100 
CreateEGLSurfaceForCompositionSurface(wr::DeviceIntRect aDirtyRect,wr::DeviceIntPoint * aOffset,RefPtr<IDCompositionSurface> aCompositionSurface,wr::DeviceIntPoint aSurfaceOffset)1101 GLuint DCLayerTree::CreateEGLSurfaceForCompositionSurface(
1102     wr::DeviceIntRect aDirtyRect, wr::DeviceIntPoint* aOffset,
1103     RefPtr<IDCompositionSurface> aCompositionSurface,
1104     wr::DeviceIntPoint aSurfaceOffset) {
1105   MOZ_ASSERT(aCompositionSurface.get());
1106 
1107   HRESULT hr;
1108   const auto gl = GetGLContext();
1109   RefPtr<ID3D11Texture2D> backBuf;
1110   POINT offset;
1111 
1112   RECT update_rect;
1113   update_rect.left = aSurfaceOffset.x + aDirtyRect.min.x;
1114   update_rect.top = aSurfaceOffset.y + aDirtyRect.min.y;
1115   update_rect.right = aSurfaceOffset.x + aDirtyRect.max.x;
1116   update_rect.bottom = aSurfaceOffset.y + aDirtyRect.max.y;
1117 
1118   hr = aCompositionSurface->BeginDraw(&update_rect, __uuidof(ID3D11Texture2D),
1119                                       (void**)getter_AddRefs(backBuf), &offset);
1120   if (FAILED(hr)) {
1121     LayoutDeviceIntRect rect = widget::WinUtils::ToIntRect(update_rect);
1122 
1123     gfxCriticalNote << "DCompositionSurface::BeginDraw failed: "
1124                     << gfx::hexa(hr) << " " << rect;
1125     RenderThread::Get()->HandleWebRenderError(WebRenderError::BEGIN_DRAW);
1126     return false;
1127   }
1128 
1129   // DC includes the origin of the dirty / update rect in the draw offset,
1130   // undo that here since WR expects it to be an absolute offset.
1131   offset.x -= aDirtyRect.min.x;
1132   offset.y -= aDirtyRect.min.y;
1133 
1134   D3D11_TEXTURE2D_DESC desc;
1135   backBuf->GetDesc(&desc);
1136 
1137   const auto& gle = gl::GLContextEGL::Cast(gl);
1138   const auto& egl = gle->mEgl;
1139 
1140   const auto buffer = reinterpret_cast<EGLClientBuffer>(backBuf.get());
1141 
1142   // Construct an EGLImage wrapper around the D3D texture for ANGLE.
1143   const EGLint attribs[] = {LOCAL_EGL_NONE};
1144   mEGLImage = egl->fCreateImage(EGL_NO_CONTEXT, LOCAL_EGL_D3D11_TEXTURE_ANGLE,
1145                                 buffer, attribs);
1146 
1147   // Get the current FBO and RBO id, so we can restore them later
1148   GLint currentFboId, currentRboId;
1149   gl->fGetIntegerv(LOCAL_GL_DRAW_FRAMEBUFFER_BINDING, &currentFboId);
1150   gl->fGetIntegerv(LOCAL_GL_RENDERBUFFER_BINDING, &currentRboId);
1151 
1152   // Create a render buffer object that is backed by the EGL image.
1153   gl->fGenRenderbuffers(1, &mColorRBO);
1154   gl->fBindRenderbuffer(LOCAL_GL_RENDERBUFFER, mColorRBO);
1155   gl->fEGLImageTargetRenderbufferStorage(LOCAL_GL_RENDERBUFFER, mEGLImage);
1156 
1157   // Get or create an FBO for the specified dimensions
1158   GLuint fboId = GetOrCreateFbo(desc.Width, desc.Height);
1159 
1160   // Attach the new renderbuffer to the FBO
1161   gl->fBindFramebuffer(LOCAL_GL_DRAW_FRAMEBUFFER, fboId);
1162   gl->fFramebufferRenderbuffer(LOCAL_GL_DRAW_FRAMEBUFFER,
1163                                LOCAL_GL_COLOR_ATTACHMENT0,
1164                                LOCAL_GL_RENDERBUFFER, mColorRBO);
1165 
1166   // Restore previous FBO and RBO bindings
1167   gl->fBindFramebuffer(LOCAL_GL_DRAW_FRAMEBUFFER, currentFboId);
1168   gl->fBindRenderbuffer(LOCAL_GL_RENDERBUFFER, currentRboId);
1169 
1170   aOffset->x = offset.x;
1171   aOffset->y = offset.y;
1172 
1173   return fboId;
1174 }
1175 
DestroyEGLSurface()1176 void DCLayerTree::DestroyEGLSurface() {
1177   const auto gl = GetGLContext();
1178 
1179   if (mColorRBO) {
1180     gl->fDeleteRenderbuffers(1, &mColorRBO);
1181     mColorRBO = 0;
1182   }
1183 
1184   if (mEGLImage) {
1185     const auto& gle = gl::GLContextEGL::Cast(gl);
1186     const auto& egl = gle->mEgl;
1187     egl->fDestroyImage(mEGLImage);
1188     mEGLImage = EGL_NO_IMAGE;
1189   }
1190 }
1191 
1192 }  // namespace wr
1193 }  // namespace mozilla
1194