/* -*- 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_DRAWTARGETWEBGL_H #define _MOZILLA_GFX_DRAWTARGETWEBGL_H #include "mozilla/gfx/2D.h" #include "mozilla/gfx/PathSkia.h" #include "mozilla/LinkedList.h" #include "mozilla/WeakPtr.h" #include "mozilla/ThreadLocal.h" #include "mozilla/ipc/Shmem.h" #include namespace WGR { struct OutputVertex; } namespace mozilla { class ClientWebGLContext; class WebGLBufferJS; class WebGLFramebufferJS; class WebGLProgramJS; class WebGLRenderbufferJS; class WebGLTextureJS; class WebGLUniformLocationJS; class WebGLVertexArrayJS; namespace layers { class SurfaceDescriptor; } namespace gfx { class DataSourceSurface; class DrawTargetSkia; class DrawTargetWebgl; class PathSkia; class SourceSurfaceSkia; class SourceSurfaceWebgl; class TextureHandle; class SharedTexture; class SharedTextureHandle; class StandaloneTexture; class GlyphCache; class PathCache; struct PathVertexRange; // DrawTargetWebgl implements a subset of the DrawTarget API suitable for use // by CanvasRenderingContext2D. It maps these to a client WebGL context so that // they can be accelerated where possible by WebGL. It manages both routing to // appropriate shaders and texture allocation/caching for surfaces. For commands // that are not feasible to accelerate with WebGL, it mirrors state to a backup // DrawTargetSkia that can be used as a fallback software renderer. Multiple // instances of DrawTargetWebgl within a process will actually share a single // WebGL context so that data can be more easily interchanged between them and // also to enable more reasonable limiting of resource usage. class DrawTargetWebgl : public DrawTarget, public SupportsWeakPtr { friend class SharedTextureHandle; friend class StandaloneTexture; friend class TextureHandle; friend class SourceSurfaceWebgl; friend class AutoSaveContext; public: MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(DrawTargetWebgl, override) private: IntSize mSize; RefPtr mFramebuffer; RefPtr mTex; RefPtr mClipMask; // The integer-aligned, scissor-compatible conservative bounds of the clip. IntRect mClipBounds; // The fractional, AA'd bounds of the clip rect, if applicable. Rect mClipAARect; RefPtr mSkia; // Skia DT pointing to the same pixel data, but without any applied clips. RefPtr mSkiaNoClip; // The Shmem backing the Skia DT, if applicable. mozilla::ipc::Shmem mShmem; // The currently cached snapshot of the WebGL context RefPtr mSnapshot; // Whether the framebuffer is still in the initially clear state. bool mIsClear = true; // Whether or not the Skia target has valid contents and is being drawn to bool mSkiaValid = false; // Whether or not Skia layering over the WebGL context is enabled bool mSkiaLayer = false; // Whether the WebGL target was clear when the Skia layer was established. bool mSkiaLayerClear = false; // Whether or not the WebGL context has valid contents and is being drawn to bool mWebglValid = true; // Whether or not the clip state has changed since last used by SharedContext. bool mClipChanged = true; // Whether or not the clip state needs to be refreshed. Sometimes the clip // state may be overwritten and require a refresh later, even though it has // not changed. bool mRefreshClipState = true; // The framebuffer has been modified and should be copied to the swap chain. bool mNeedsPresent = true; // The number of layers currently pushed. int32_t mLayerDepth = 0; RefPtr mSnapshotTexture; // Store a log of clips currently pushed so that they can be used to init // the clip state of temporary DTs. struct ClipStack { Matrix mTransform; Rect mRect; RefPtr mPath; bool operator==(const ClipStack& aOther) const; }; std::vector mClipStack; // The previous state of the clip stack when a mask was generated. std::vector mCachedClipStack; // UsageProfile stores per-frame counters for significant profiling events // that assist in determining whether acceleration should still be used for // a Canvas2D user. struct UsageProfile { uint32_t mFailedFrames = 0; uint32_t mFrameCount = 0; uint32_t mCacheMisses = 0; uint32_t mCacheHits = 0; uint32_t mUncachedDraws = 0; uint32_t mLayers = 0; uint32_t mReadbacks = 0; uint32_t mFallbacks = 0; void BeginFrame(); void EndFrame(); bool RequiresRefresh() const; void OnCacheMiss() { ++mCacheMisses; } void OnCacheHit() { ++mCacheHits; } void OnUncachedDraw() { ++mUncachedDraws; } void OnLayer() { ++mLayers; } void OnReadback() { ++mReadbacks; } void OnFallback() { ++mFallbacks; } }; UsageProfile mProfile; // SharedContext stores most of the actual WebGL state that may be used by // any number of DrawTargetWebgl's that use it. Foremost, it holds the actual // WebGL client context, programs, and buffers for mapping to WebGL. // Secondarily, it holds shared caches for surfaces, glyphs, paths, and // shadows so that each DrawTargetWebgl does not require its own cache. It is // important that SetTarget is called to install the current DrawTargetWebgl // before actually using the SharedContext, as the individual framebuffers // and viewport are still maintained in DrawTargetWebgl itself. class SharedContext : public mozilla::RefCounted, public mozilla::SupportsWeakPtr { public: MOZ_DECLARE_REFCOUNTED_TYPENAME(SharedContext) SharedContext(); ~SharedContext(); WeakPtr mCurrentTarget; IntSize mViewportSize; // The current integer-aligned scissor rect. IntRect mClipRect; // The current fractional AA'd clip rect bounds. Rect mClipAARect; RefPtr mWebgl; // Avoid spurious state changes by caching last used state. RefPtr mLastProgram; RefPtr mLastTexture; RefPtr mLastClipMask; // Whether the shader viewport state requires updating. bool mDirtyViewport = true; // Whether the shader anti-aliasing state requires updating. bool mDirtyAA = true; // Whether the shader clip AA bounds require updating. bool mDirtyClip = true; // WebGL shader resources RefPtr mPathVertexBuffer; RefPtr mPathVertexArray; // The current insertion offset into the GPU path buffer. uint32_t mPathVertexOffset = 0; // The maximum size of the GPU path buffer. uint32_t mPathVertexCapacity = 0; // The maximum supported type complexity of a GPU path. uint32_t mPathMaxComplexity = 0; // Whether to accelerate stroked paths with AAStroke. bool mPathAAStroke = true; // Whether to accelerate stroked paths with WGR. bool mPathWGRStroke = false; // Temporary buffer for generating WGR output into. UniquePtr mWGROutputBuffer; RefPtr mSolidProgram; RefPtr mSolidProgramViewport; RefPtr mSolidProgramAA; RefPtr mSolidProgramTransform; RefPtr mSolidProgramColor; RefPtr mSolidProgramClipMask; RefPtr mSolidProgramClipBounds; RefPtr mImageProgram; RefPtr mImageProgramViewport; RefPtr mImageProgramAA; RefPtr mImageProgramTransform; RefPtr mImageProgramTexMatrix; RefPtr mImageProgramTexBounds; RefPtr mImageProgramColor; RefPtr mImageProgramSwizzle; RefPtr mImageProgramSampler; RefPtr mImageProgramClipMask; RefPtr mImageProgramClipBounds; // Scratch framebuffer used to wrap textures for miscellaneous utility ops. RefPtr mScratchFramebuffer; // Buffer filled with zero data for initializing textures. RefPtr mZeroBuffer; size_t mZeroSize = 0; // 1x1 texture with solid white mask for disabling clipping RefPtr mNoClipMask; uint32_t mMaxTextureSize = 0; bool mRasterizationTruncates = false; // The current blending operation. CompositionOp mLastCompositionOp = CompositionOp::OP_SOURCE; // The constant blend color used for the blending operation. Maybe mLastBlendColor; // The cached scissor state. Operations that rely on scissor state should // take care to enable or disable the cached scissor state as necessary. bool mScissorEnabled = false; IntRect mLastScissor = {-1, -1, -1, -1}; // A most-recently-used list of allocated texture handles. LinkedList> mTextureHandles; size_t mNumTextureHandles = 0; // User data key linking a SourceSurface with its TextureHandle. UserDataKey mTextureHandleKey = {0}; // User data key linking a SourceSurface with its shadow blur TextureHandle. UserDataKey mShadowTextureKey = {0}; // User data key linking a ScaledFont with its GlyphCache. UserDataKey mGlyphCacheKey = {0}; // List of all GlyphCaches currently allocated to fonts. LinkedList mGlyphCaches; // Cache of rasterized paths. UniquePtr mPathCache; // Collection of allocated shared texture pages that may be shared amongst // many handles. std::vector> mSharedTextures; // Collection of allocated standalone textures that have a single assigned // handle. std::vector> mStandaloneTextures; size_t mUsedTextureMemory = 0; size_t mTotalTextureMemory = 0; // The total reserved memory for empty texture pages that are kept around // for future allocations. size_t mEmptyTextureMemory = 0; // A memory pressure event may signal from another thread that caches should // be cleared if possible. Atomic mShouldClearCaches; // Whether the Shmem is currently being processed by the remote side. If so, // we need to wait for processing to complete before any further commands // modifying the Skia DT can proceed. bool mWaitForShmem = false; const Matrix& GetTransform() const { return mCurrentTarget->mTransform; } bool IsContextLost() const; bool Initialize(); bool CreateShaders(); void ResetPathVertexBuffer(bool aChanged = true); void SetBlendState(CompositionOp aOp, const Maybe& aBlendColor = Nothing()); void SetClipRect(const Rect& aClipRect); void SetClipRect(const IntRect& aClipRect) { SetClipRect(Rect(aClipRect)); } bool SetClipMask(const RefPtr& aTex); bool SetNoClipMask(); bool HasClipMask() const { return mLastClipMask && mLastClipMask != mNoClipMask; } bool IsCurrentTarget(DrawTargetWebgl* aDT) const { return aDT == mCurrentTarget; } bool SetTarget(DrawTargetWebgl* aDT); // Reset the current target. void ClearTarget() { mCurrentTarget = nullptr; } // Reset the last used texture to force binding next use. void ClearLastTexture(); bool SupportsPattern(const Pattern& aPattern); void EnableScissor(const IntRect& aRect); void DisableScissor(); void SetTexFilter(WebGLTextureJS* aTex, bool aFilter); void InitTexParameters(WebGLTextureJS* aTex, bool aFilter = true); bool ReadInto(uint8_t* aDstData, int32_t aDstStride, SurfaceFormat aFormat, const IntRect& aBounds, TextureHandle* aHandle = nullptr); already_AddRefed ReadSnapshot( TextureHandle* aHandle = nullptr); already_AddRefed WrapSnapshot(const IntSize& aSize, SurfaceFormat aFormat, RefPtr aTex); already_AddRefed CopySnapshot( const IntRect& aRect, TextureHandle* aHandle = nullptr); already_AddRefed GetCompatibleSnapshot( SourceSurface* aSurface) const; bool IsCompatibleSurface(SourceSurface* aSurface) const; bool UploadSurface(DataSourceSurface* aData, SurfaceFormat aFormat, const IntRect& aSrcRect, const IntPoint& aDstOffset, bool aInit, bool aZero = false, const RefPtr& aTex = nullptr); bool DrawRectAccel(const Rect& aRect, const Pattern& aPattern, const DrawOptions& aOptions, Maybe aMaskColor = Nothing(), RefPtr* aHandle = nullptr, bool aTransformed = true, bool aClipped = true, bool aAccelOnly = false, bool aForceUpdate = false, const StrokeOptions* aStrokeOptions = nullptr, const PathVertexRange* aVertexRange = nullptr); bool DrawPathAccel(const Path* aPath, const Pattern& aPattern, const DrawOptions& aOptions, const StrokeOptions* aStrokeOptions = nullptr, const ShadowOptions* aShadow = nullptr, bool aCacheable = true); bool DrawGlyphsAccel(ScaledFont* aFont, const GlyphBuffer& aBuffer, const Pattern& aPattern, const DrawOptions& aOptions, const StrokeOptions* aStrokeOptions, bool aUseSubpixelAA); void PruneTextureHandle(const RefPtr& aHandle); bool PruneTextureMemory(size_t aMargin = 0, bool aPruneUnused = true); bool RemoveSharedTexture(const RefPtr& aTexture); bool RemoveStandaloneTexture(const RefPtr& aTexture); void UnlinkSurfaceTextures(); void UnlinkSurfaceTexture(const RefPtr& aHandle); void UnlinkGlyphCaches(); void OnMemoryPressure(); void ClearAllTextures(); void ClearEmptyTextureMemory(); void ClearCachesIfNecessary(); void WaitForShmem(DrawTargetWebgl* aTarget); void CachePrefs(); }; RefPtr mSharedContext; static MOZ_THREAD_LOCAL(SharedContext*) sSharedContext; // Try to keep around the shared context for the main thread in case canvases // are rapidly recreated and destroyed. static RefPtr sMainSharedContext; public: DrawTargetWebgl(); ~DrawTargetWebgl(); static already_AddRefed Create(const IntSize& aSize, SurfaceFormat aFormat); bool Init(const IntSize& aSize, SurfaceFormat aFormat); bool IsValid() const override; DrawTargetType GetType() const override { return DrawTargetType::HARDWARE_RASTER; } BackendType GetBackendType() const override { return BackendType::WEBGL; } IntSize GetSize() const override { return mSize; } already_AddRefed GetDataSnapshot(); already_AddRefed Snapshot() override; already_AddRefed GetOptimizedSnapshot(DrawTarget* aTarget); already_AddRefed GetBackingSurface() override; void DetachAllSnapshots() override; void BeginFrame(const IntRect& aPersistedRect); void EndFrame(); bool RequiresRefresh() const { return mProfile.RequiresRefresh(); } bool LockBits(uint8_t** aData, IntSize* aSize, int32_t* aStride, SurfaceFormat* aFormat, IntPoint* aOrigin = nullptr) override; void ReleaseBits(uint8_t* aData) override; void Flush() override {} void DrawSurface( SourceSurface* aSurface, const Rect& aDest, const Rect& aSource, const DrawSurfaceOptions& aSurfOptions = DrawSurfaceOptions(), const DrawOptions& aOptions = DrawOptions()) override; void DrawFilter(FilterNode* aNode, const Rect& aSourceRect, const Point& aDestPoint, const DrawOptions& aOptions = DrawOptions()) override; void DrawSurfaceWithShadow(SourceSurface* aSurface, const Point& aDest, const ShadowOptions& aShadow, CompositionOp aOperator) override; void DrawShadow(const Path* aPath, const Pattern& aPattern, const ShadowOptions& aShadow, const DrawOptions& aOptions, const StrokeOptions* aStrokeOptions = nullptr) override; void ClearRect(const Rect& aRect) override; void CopySurface(SourceSurface* aSurface, const IntRect& aSourceRect, const IntPoint& aDestination) override; void FillRect(const Rect& aRect, const Pattern& aPattern, const DrawOptions& aOptions = DrawOptions()) override; void StrokeRect(const Rect& aRect, const Pattern& aPattern, const StrokeOptions& aStrokeOptions = StrokeOptions(), const DrawOptions& aOptions = DrawOptions()) override; bool StrokeLineAccel(const Point& aStart, const Point& aEnd, const Pattern& aPattern, const StrokeOptions& aStrokeOptions, const DrawOptions& aOptions, bool aClosed = false); void StrokeLine(const Point& aStart, const Point& aEnd, const Pattern& aPattern, const StrokeOptions& aStrokeOptions = StrokeOptions(), const DrawOptions& aOptions = DrawOptions()) override; void Stroke(const Path* aPath, const Pattern& aPattern, const StrokeOptions& aStrokeOptions = StrokeOptions(), const DrawOptions& aOptions = DrawOptions()) override; void Fill(const Path* aPath, const Pattern& aPattern, const DrawOptions& aOptions = DrawOptions()) override; void SetPermitSubpixelAA(bool aPermitSubpixelAA) override; void FillGlyphs(ScaledFont* aFont, const GlyphBuffer& aBuffer, const Pattern& aPattern, const DrawOptions& aOptions = DrawOptions()) override; void StrokeGlyphs(ScaledFont* aFont, const GlyphBuffer& aBuffer, const Pattern& aPattern, const StrokeOptions& aStrokeOptions = StrokeOptions(), const DrawOptions& aOptions = DrawOptions()) override; void Mask(const Pattern& aSource, const Pattern& aMask, const DrawOptions& aOptions = DrawOptions()) override; void MaskSurface(const Pattern& aSource, SourceSurface* aMask, Point aOffset, const DrawOptions& aOptions = DrawOptions()) override; bool Draw3DTransformedSurface(SourceSurface* aSurface, const Matrix4x4& aMatrix) override; void PushClip(const Path* aPath) override; void PushClipRect(const Rect& aRect) override; void PushDeviceSpaceClipRects(const IntRect* aRects, uint32_t aCount) override; void PopClip() override; bool RemoveAllClips() override; void PushLayer(bool aOpaque, Float aOpacity, SourceSurface* aMask, const Matrix& aMaskTransform, const IntRect& aBounds = IntRect(), bool aCopyBackground = false) override; void PushLayerWithBlend( bool aOpaque, Float aOpacity, SourceSurface* aMask, const Matrix& aMaskTransform, const IntRect& aBounds = IntRect(), bool aCopyBackground = false, CompositionOp aCompositionOp = CompositionOp::OP_OVER) override; void PopLayer() override; already_AddRefed CreateSourceSurfaceFromData( unsigned char* aData, const IntSize& aSize, int32_t aStride, SurfaceFormat aFormat) const override; already_AddRefed OptimizeSourceSurface( SourceSurface* aSurface) const override; already_AddRefed OptimizeSourceSurfaceForUnknownAlpha( SourceSurface* aSurface) const override; already_AddRefed CreateSourceSurfaceFromNativeSurface( const NativeSurface& aSurface) const override; already_AddRefed CreateSimilarDrawTarget( const IntSize& aSize, SurfaceFormat aFormat) const override; bool CanCreateSimilarDrawTarget(const IntSize& aSize, SurfaceFormat aFormat) const override; RefPtr CreateClippedDrawTarget(const Rect& aBounds, SurfaceFormat aFormat) override; already_AddRefed CreatePathBuilder( FillRule aFillRule = FillRule::FILL_WINDING) const override; already_AddRefed CreateGradientStops( GradientStop* aStops, uint32_t aNumStops, ExtendMode aExtendMode = ExtendMode::CLAMP) const override; already_AddRefed CreateFilter(FilterType aType) override; void SetTransform(const Matrix& aTransform) override; void* GetNativeSurface(NativeSurfaceType aType) override; Maybe GetFrontBuffer(); void OnMemoryPressure() { mSharedContext->OnMemoryPressure(); } operator std::string() const { std::stringstream stream; stream << "DrawTargetWebgl(" << this << ")"; return stream.str(); } private: bool SupportsPattern(const Pattern& aPattern) { return mSharedContext->SupportsPattern(aPattern); } bool SetSimpleClipRect(); bool GenerateComplexClipMask(); bool PrepareContext(bool aClipped = true); void DrawRectFallback(const Rect& aRect, const Pattern& aPattern, const DrawOptions& aOptions, Maybe aMaskColor = Nothing(), bool aTransform = true, bool aClipped = true, const StrokeOptions* aStrokeOptions = nullptr); bool DrawRect(const Rect& aRect, const Pattern& aPattern, const DrawOptions& aOptions, Maybe aMaskColor = Nothing(), RefPtr* aHandle = nullptr, bool aTransformed = true, bool aClipped = true, bool aAccelOnly = false, bool aForceUpdate = false, const StrokeOptions* aStrokeOptions = nullptr); ColorPattern GetClearPattern() const; bool RectContainsViewport(const Rect& aRect) const; bool ShouldAccelPath(const DrawOptions& aOptions, const StrokeOptions* aStrokeOptions); void DrawPath(const Path* aPath, const Pattern& aPattern, const DrawOptions& aOptions, const StrokeOptions* aStrokeOptions = nullptr); bool MarkChanged(); void ReadIntoSkia(); void FlattenSkia(); bool FlushFromSkia(); void WaitForShmem() { if (mSharedContext->mWaitForShmem) { mSharedContext->WaitForShmem(this); } } void MarkSkiaChanged(bool aOverwrite = false) { WaitForShmem(); if (aOverwrite) { mSkiaValid = true; mSkiaLayer = false; } else if (!mSkiaValid) { ReadIntoSkia(); } else if (mSkiaLayer) { FlattenSkia(); } mWebglValid = false; mIsClear = false; } void MarkSkiaChanged(const DrawOptions& aOptions); bool ShouldUseSubpixelAA(ScaledFont* aFont, const DrawOptions& aOptions); bool ReadInto(uint8_t* aDstData, int32_t aDstStride); already_AddRefed ReadSnapshot(); already_AddRefed CopySnapshot(const IntRect& aRect); already_AddRefed CopySnapshot() { return CopySnapshot(GetRect()); } void ClearSnapshot(bool aCopyOnWrite = true, bool aNeedHandle = false); bool CreateFramebuffer(); // PrepareContext may sometimes be used recursively. When this occurs, ensure // that clip state is restored after the context is used. struct AutoRestoreContext { DrawTargetWebgl* mTarget; Rect mClipAARect; RefPtr mLastClipMask; explicit AutoRestoreContext(DrawTargetWebgl* aTarget); ~AutoRestoreContext(); }; }; } // namespace gfx } // namespace mozilla #endif // _MOZILLA_GFX_DRAWTARGETWEBGL_H