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