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, ¤tFboId);
1150 gl->fGetIntegerv(LOCAL_GL_RENDERBUFFER_BINDING, ¤tRboId);
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