/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set ts=8 sts=2 et sw=2 tw=80: */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #ifndef MOZILLA_GFX_DCLAYER_TREE_H #define MOZILLA_GFX_DCLAYER_TREE_H #include #include #include #include #include "GLTypes.h" #include "mozilla/HashFunctions.h" #include "mozilla/layers/OverlayInfo.h" #include "mozilla/Maybe.h" #include "mozilla/RefPtr.h" #include "mozilla/UniquePtr.h" #include "mozilla/webrender/WebRenderTypes.h" struct ID3D11Device; struct ID3D11DeviceContext; struct ID3D11VideoDevice; struct ID3D11VideoContext; struct ID3D11VideoProcessor; struct ID3D11VideoProcessorEnumerator; struct ID3D11VideoProcessorOutputView; struct IDCompositionDevice2; struct IDCompositionSurface; struct IDCompositionTarget; struct IDCompositionVisual2; struct IDXGIDecodeSwapChain; struct IDXGIResource; struct IDXGISwapChain1; struct IDCompositionVirtualSurface; namespace mozilla { namespace gl { class GLContext; } namespace wr { // The size of the virtual surface. This is large enough such that we // will never render a surface larger than this. #define VIRTUAL_SURFACE_SIZE (1024 * 1024) class DCTile; class DCSurface; class DCSurfaceVideo; class DCSurfaceHandle; class RenderTextureHost; class RenderDcompSurfaceTextureHost; struct GpuOverlayInfo { bool mSupportsOverlays = false; bool mSupportsHardwareOverlays = false; DXGI_FORMAT mOverlayFormatUsed = DXGI_FORMAT_B8G8R8A8_UNORM; DXGI_FORMAT mOverlayFormatUsedHdr = DXGI_FORMAT_R10G10B10A2_UNORM; UINT mNv12OverlaySupportFlags = 0; UINT mYuy2OverlaySupportFlags = 0; UINT mBgra8OverlaySupportFlags = 0; UINT mRgb10a2OverlaySupportFlags = 0; }; /** * DCLayerTree manages direct composition layers. * It does not manage gecko's layers::Layer. */ class DCLayerTree { public: static UniquePtr Create(gl::GLContext* aGL, EGLConfig aEGLConfig, ID3D11Device* aDevice, ID3D11DeviceContext* aCtx, HWND aHwnd, nsACString& aError); static void Shutdown(); explicit DCLayerTree(gl::GLContext* aGL, EGLConfig aEGLConfig, ID3D11Device* aDevice, ID3D11DeviceContext* aCtx, IDCompositionDevice2* aCompositionDevice); ~DCLayerTree(); void SetDefaultSwapChain(IDXGISwapChain1* aSwapChain); void MaybeUpdateDebug(); void MaybeCommit(); void WaitForCommitCompletion(); void DisableNativeCompositor(); // Interface for wr::Compositor void CompositorBeginFrame(); void CompositorEndFrame(); void Bind(wr::NativeTileId aId, wr::DeviceIntPoint* aOffset, uint32_t* aFboId, wr::DeviceIntRect aDirtyRect, wr::DeviceIntRect aValidRect); void Unbind(); void CreateSurface(wr::NativeSurfaceId aId, wr::DeviceIntPoint aVirtualOffset, wr::DeviceIntSize aTileSize, bool aIsOpaque); void CreateExternalSurface(wr::NativeSurfaceId aId, bool aIsOpaque); void DestroySurface(NativeSurfaceId aId); void CreateTile(wr::NativeSurfaceId aId, int32_t aX, int32_t aY); void DestroyTile(wr::NativeSurfaceId aId, int32_t aX, int32_t aY); void AttachExternalImage(wr::NativeSurfaceId aId, wr::ExternalImageId aExternalImage); void AddSurface(wr::NativeSurfaceId aId, const wr::CompositorSurfaceTransform& aTransform, wr::DeviceIntRect aClipRect, wr::ImageRendering aImageRendering); gl::GLContext* GetGLContext() const { return mGL; } EGLConfig GetEGLConfig() const { return mEGLConfig; } ID3D11Device* GetDevice() const { return mDevice; } IDCompositionDevice2* GetCompositionDevice() const { return mCompositionDevice; } ID3D11VideoDevice* GetVideoDevice() const { return mVideoDevice; } ID3D11VideoContext* GetVideoContext() const { return mVideoContext; } ID3D11VideoProcessor* GetVideoProcessor() const { return mVideoProcessor; } ID3D11VideoProcessorEnumerator* GetVideoProcessorEnumerator() const { return mVideoProcessorEnumerator; } bool EnsureVideoProcessor(const gfx::IntSize& aInputSize, const gfx::IntSize& aOutputSize); DCSurface* GetSurface(wr::NativeSurfaceId aId) const; // Get or create an FBO with depth buffer suitable for specified dimensions GLuint GetOrCreateFbo(int aWidth, int aHeight); bool SupportsHardwareOverlays(); DXGI_FORMAT GetOverlayFormatForSDR(); bool SupportsSwapChainTearing(); protected: bool Initialize(HWND aHwnd, nsACString& aError); bool InitializeVideoOverlaySupport(); bool MaybeUpdateDebugCounter(); bool MaybeUpdateDebugVisualRedrawRegions(); void DestroyEGLSurface(); GLuint CreateEGLSurfaceForCompositionSurface( wr::DeviceIntRect aDirtyRect, wr::DeviceIntPoint* aOffset, RefPtr aCompositionSurface, wr::DeviceIntPoint aSurfaceOffset); void ReleaseNativeCompositorResources(); layers::OverlayInfo GetOverlayInfo(); RefPtr mGL; EGLConfig mEGLConfig; RefPtr mDevice; RefPtr mCtx; RefPtr mCompositionDevice; RefPtr mCompositionTarget; RefPtr mRootVisual; RefPtr mDefaultSwapChainVisual; RefPtr mVideoDevice; RefPtr mVideoContext; RefPtr mVideoProcessor; RefPtr mVideoProcessorEnumerator; gfx::IntSize mVideoInputSize; gfx::IntSize mVideoOutputSize; bool mDebugCounter; bool mDebugVisualRedrawRegions; Maybe> mCurrentSurface; // The EGL image that is bound to the D3D texture provided by // DirectComposition. EGLImage mEGLImage; // The GL render buffer ID that maps the EGLImage to an RBO for attaching to // an FBO. GLuint mColorRBO; struct SurfaceIdHashFn { std::size_t operator()(const wr::NativeSurfaceId& aId) const { return HashGeneric(wr::AsUint64(aId)); } }; std::unordered_map, SurfaceIdHashFn> mDCSurfaces; // A list of layer IDs as they are added to the visual tree this frame. std::vector mCurrentLayers; // The previous frame's list of layer IDs in visual order. std::vector mPrevLayers; // Information about a cached FBO that is retained between frames. struct CachedFrameBuffer { int width; int height; GLuint fboId; GLuint depthRboId; int lastFrameUsed; }; // A cache of FBOs, containing a depth buffer allocated to a specific size. // TODO(gw): Might be faster as a hashmap? The length is typically much less // than 10. nsTArray mFrameBuffers; int mCurrentFrame = 0; bool mPendingCommit; static UniquePtr sGpuOverlayInfo; }; /** Represents a single picture cache slice. Each surface contains some number of tiles. An implementation may choose to allocate individual tiles to render in to (as the current impl does), or allocate a large single virtual surface to draw into (e.g. the DirectComposition virtual surface API in future). */ class DCSurface { public: const bool mIsVirtualSurface; explicit DCSurface(wr::DeviceIntSize aTileSize, wr::DeviceIntPoint aVirtualOffset, bool aIsVirtualSurface, bool aIsOpaque, DCLayerTree* aDCLayerTree); virtual ~DCSurface(); bool Initialize(); void CreateTile(int32_t aX, int32_t aY); void DestroyTile(int32_t aX, int32_t aY); IDCompositionVisual2* GetVisual() const { return mVisual; } DCTile* GetTile(int32_t aX, int32_t aY) const; struct TileKey { TileKey(int32_t aX, int32_t aY) : mX(aX), mY(aY) {} int32_t mX; int32_t mY; }; wr::DeviceIntSize GetTileSize() const { return mTileSize; } wr::DeviceIntPoint GetVirtualOffset() const { return mVirtualOffset; } IDCompositionVirtualSurface* GetCompositionSurface() const { return mVirtualSurface; } void UpdateAllocatedRect(); void DirtyAllocatedRect(); // Implement these if the inherited surface supports attaching external image. virtual void AttachExternalImage(wr::ExternalImageId aExternalImage) { MOZ_RELEASE_ASSERT(true, "Not support attaching external image"); } virtual void PresentExternalSurface(gfx::Matrix& aTransform) { MOZ_RELEASE_ASSERT(true, "Not support presenting external surface"); } virtual DCSurfaceVideo* AsDCSurfaceVideo() { return nullptr; } virtual DCSurfaceHandle* AsDCSurfaceHandle() { return nullptr; } protected: DCLayerTree* mDCLayerTree; struct TileKeyHashFn { std::size_t operator()(const TileKey& aId) const { return HashGeneric(aId.mX, aId.mY); } }; // The visual for this surface. No content is attached to here, but tiles // that belong to this surface are added as children. In this way, we can // set the clip and scroll offset once, on this visual, to affect all // children. // // However when using a virtual surface, it is directly attached to this // visual and the tiles do not own visuals. // // Whether mIsVirtualSurface is enabled is decided at DCSurface creation // time based on the pref gfx.webrender.dcomp-use-virtual-surfaces RefPtr mVisual; wr::DeviceIntSize mTileSize; bool mIsOpaque; bool mAllocatedRectDirty; std::unordered_map, TileKeyHashFn> mDCTiles; wr::DeviceIntPoint mVirtualOffset; RefPtr mVirtualSurface; }; /** * A wrapper surface which can contain either a DCVideo or a DCSurfaceHandle. */ class DCExternalSurfaceWrapper : public DCSurface { public: DCExternalSurfaceWrapper(bool aIsOpaque, DCLayerTree* aDCLayerTree) : DCSurface(wr::DeviceIntSize{}, wr::DeviceIntPoint{}, false /* virtual surface */, false /* opaque */, aDCLayerTree), mIsOpaque(aIsOpaque) {} ~DCExternalSurfaceWrapper() = default; void AttachExternalImage(wr::ExternalImageId aExternalImage) override; void PresentExternalSurface(gfx::Matrix& aTransform) override; DCSurfaceVideo* AsDCSurfaceVideo() override { return mSurface ? mSurface->AsDCSurfaceVideo() : nullptr; } DCSurfaceHandle* AsDCSurfaceHandle() override { return mSurface ? mSurface->AsDCSurfaceHandle() : nullptr; } private: DCSurface* EnsureSurfaceForExternalImage(wr::ExternalImageId aExternalImage); UniquePtr mSurface; const bool mIsOpaque; }; class DCSurfaceVideo : public DCSurface { public: DCSurfaceVideo(bool aIsOpaque, DCLayerTree* aDCLayerTree); void AttachExternalImage(wr::ExternalImageId aExternalImage) override; bool CalculateSwapChainSize(gfx::Matrix& aTransform); void PresentVideo(); DCSurfaceVideo* AsDCSurfaceVideo() override { return this; } protected: virtual ~DCSurfaceVideo(); DXGI_FORMAT GetSwapChainFormat(); bool CreateVideoSwapChain(); bool CallVideoProcessorBlt(); void ReleaseDecodeSwapChainResources(); RefPtr mOutputView; RefPtr mDecodeResource; RefPtr mVideoSwapChain; RefPtr mDecodeSwapChain; HANDLE mSwapChainSurfaceHandle = 0; gfx::IntSize mVideoSize; gfx::IntSize mSwapChainSize; DXGI_FORMAT mSwapChainFormat = DXGI_FORMAT_B8G8R8A8_UNORM; bool mFailedYuvSwapChain = false; RefPtr mRenderTextureHost; RefPtr mPrevTexture; }; /** * A DC surface contains a IDCompositionSurface that is directly constructed by * a handle. This is used by the Media Foundataion media engine, which would * store the decoded video content in the surface. */ class DCSurfaceHandle : public DCSurface { public: DCSurfaceHandle(bool aIsOpaque, DCLayerTree* aDCLayerTree); ~DCSurfaceHandle() = default; void AttachExternalImage(wr::ExternalImageId aExternalImage) override; void PresentSurfaceHandle(); DCSurfaceHandle* AsDCSurfaceHandle() override { return this; } protected: HANDLE GetSurfaceHandle() const; IDCompositionSurface* EnsureSurface(); RefPtr mDcompTextureHost; }; class DCTile { public: gfx::IntRect mValidRect; DCLayerTree* mDCLayerTree; // Indicates that when the first BeginDraw occurs on the surface it must be // full size - required by dcomp on non-virtual surfaces. bool mNeedsFullDraw; explicit DCTile(DCLayerTree* aDCLayerTree); ~DCTile(); bool Initialize(int aX, int aY, wr::DeviceIntSize aSize, bool aIsVirtualSurface, bool aIsOpaque, RefPtr mSurfaceVisual); RefPtr Bind(wr::DeviceIntRect aValidRect); IDCompositionVisual2* GetVisual() { return mVisual; } protected: // Size in pixels of this tile, some may be unused. Set by Initialize. wr::DeviceIntSize mSize; // Whether the tile is composited as opaque (ignores alpha) or transparent. // Set by Initialize. bool mIsOpaque; // Some code paths differ based on whether parent surface is virtual. bool mIsVirtualSurface; // Visual that displays the composition surface, or NULL if the tile belongs // to a virtual surface. RefPtr mVisual; // Surface for the visual, or NULL if the tile has not had its first Bind or // belongs to a virtual surface. RefPtr mCompositionSurface; RefPtr CreateCompositionSurface(wr::DeviceIntSize aSize, bool aIsOpaque); }; static inline bool operator==(const DCSurface::TileKey& a0, const DCSurface::TileKey& a1) { return a0.mX == a1.mX && a0.mY == a1.mY; } } // namespace wr } // namespace mozilla #endif