/* * Copyright 2015 Google Inc. * * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ #ifndef SurfaceDrawContext_v1_DEFINED #define SurfaceDrawContext_v1_DEFINED #include "include/core/SkCanvas.h" #include "include/core/SkColor.h" #include "include/core/SkDrawable.h" #include "include/core/SkMatrix.h" #include "include/core/SkPaint.h" #include "include/core/SkRect.h" #include "include/core/SkRefCnt.h" #include "include/core/SkRegion.h" #include "include/core/SkStrokeRec.h" #include "include/core/SkSurfaceProps.h" #include "include/gpu/GpuTypes.h" #include "include/gpu/ganesh/GrTypes.h" #include "include/private/SkColorData.h" #include "include/private/base/SkPoint_impl.h" #include "include/private/base/SkTArray.h" #include "include/private/gpu/ganesh/GrTypesPriv.h" #include "src/gpu/ganesh/GrColorSpaceXform.h" #include "src/gpu/ganesh/GrPaint.h" #include "src/gpu/ganesh/GrRenderTargetProxy.h" #include "src/gpu/ganesh/GrSamplerState.h" #include "src/gpu/ganesh/GrSurfaceProxy.h" #include "src/gpu/ganesh/GrSurfaceProxyView.h" #include "src/gpu/ganesh/SurfaceFillContext.h" #include "src/gpu/ganesh/geometry/GrQuad.h" #include "src/gpu/ganesh/ops/GrOp.h" #include "src/gpu/ganesh/ops/OpsTask.h" #include #include #include #include #include class GrBackendFormat; class GrBackendSemaphore; class GrBackendTexture; class GrClip; class GrDstProxyView; class GrFragmentProcessor; class GrHardClip; class GrRecordingContext; class GrRenderTarget; class GrStyle; class GrStyledShape; class SkColorSpace; class SkLatticeIter; class SkMesh; class SkPath; class SkRRect; class SkVertices; enum SkAlphaType : int; enum class SkBackingFit; enum class SkBlendMode; struct GrQuadSetEntry; struct GrTextureSetEntry; struct GrUserStencilSettings; struct SkArc; struct SkDrawShadowRec; struct SkISize; struct SkRSXform; struct SkStrikeDeviceInfo; namespace skgpu { class RefCntedCallback; class Swizzle; } // namespace skgpu namespace sktext { class GlyphRunList; } namespace skgpu::ganesh { /** * A helper object to orchestrate commands (draws, etc...) for GrSurfaces that are GrRenderTargets. */ class SurfaceDrawContext final : public SurfaceFillContext { public: static std::unique_ptr Make(GrRecordingContext*, GrColorType, sk_sp, sk_sp, GrSurfaceOrigin, const SkSurfaceProps&); /* Uses the default texture format for the color type */ static std::unique_ptr Make(GrRecordingContext*, GrColorType, sk_sp, SkBackingFit, SkISize dimensions, const SkSurfaceProps&, std::string_view label, int sampleCnt = 1, skgpu::Mipmapped = skgpu::Mipmapped::kNo, skgpu::Protected = skgpu::Protected::kNo, GrSurfaceOrigin = kBottomLeft_GrSurfaceOrigin, skgpu::Budgeted = skgpu::Budgeted::kYes); /** * Takes custom swizzles rather than determining swizzles from color type and format. * It will have color type kUnknown. */ static std::unique_ptr Make(GrRecordingContext*, sk_sp, SkBackingFit, SkISize dimensions, const GrBackendFormat&, int sampleCnt, skgpu::Mipmapped, skgpu::Protected, skgpu::Swizzle readSwizzle, skgpu::Swizzle writeSwizzle, GrSurfaceOrigin, skgpu::Budgeted, const SkSurfaceProps&, std::string_view label); // Same as previous factory but will try to use fallback GrColorTypes if the one passed in // fails. The fallback GrColorType will have at least the number of channels and precision per // channel as the passed in GrColorType. It may also swizzle the changes (e.g., BGRA -> RGBA). // SRGB-ness will be preserved. static std::unique_ptr MakeWithFallback( GrRecordingContext*, GrColorType, sk_sp, SkBackingFit, SkISize dimensions, const SkSurfaceProps&, int sampleCnt, skgpu::Mipmapped, skgpu::Protected, GrSurfaceOrigin = kBottomLeft_GrSurfaceOrigin, skgpu::Budgeted = skgpu::Budgeted::kYes); // Creates a SurfaceDrawContext that wraps the passed in GrBackendTexture. static std::unique_ptr MakeFromBackendTexture( GrRecordingContext*, GrColorType, sk_sp, const GrBackendTexture&, int sampleCnt, GrSurfaceOrigin, const SkSurfaceProps&, sk_sp releaseHelper); SurfaceDrawContext(GrRecordingContext*, GrSurfaceProxyView readView, GrSurfaceProxyView writeView, GrColorType, sk_sp, const SkSurfaceProps&); ~SurfaceDrawContext() override; /** * Draw everywhere (respecting the clip) with the paint. */ void drawPaint(const GrClip*, GrPaint&&, const SkMatrix& viewMatrix); /** * Draw the rect using a paint. * @param paint describes how to color pixels. * @param GrAA Controls whether rect is antialiased * @param viewMatrix transformation matrix * @param style The style to apply. Null means fill. Currently path effects are not * allowed. * The rects coords are used to access the paint (through texture matrix) */ void drawRect(const GrClip*, GrPaint&& paint, GrAA, const SkMatrix& viewMatrix, const SkRect&, const GrStyle* style = nullptr); /** * Maps a rectangle of shader coordinates to a rectangle and fills that rectangle. * * @param GrPaint describes how to color pixels. * @param GrAA Controls whether rect is antialiased * @param SkMatrix transformation matrix which applies to rectToDraw * @param rectToDraw the rectangle to draw * @param localRect the rectangle of shader coordinates applied to rectToDraw */ void fillRectToRect(const GrClip*, GrPaint&&, GrAA, const SkMatrix&, const SkRect& rectToDraw, const SkRect& localRect); /** * Fills a block of pixels with a paint and a localMatrix, respecting the clip. */ void fillPixelsWithLocalMatrix(const GrClip* clip, GrPaint&& paint, const SkIRect& bounds, const SkMatrix& localMatrix) { SkRect rect = SkRect::Make(bounds); DrawQuad quad{GrQuad::MakeFromRect(rect, SkMatrix::I()), GrQuad::MakeFromRect(rect, localMatrix), GrQuadAAFlags::kNone}; this->drawFilledQuad(clip, std::move(paint), &quad); } /** * Creates an op that draws a fill rect with per-edge control over anti-aliasing. * * This is a specialized version of fillQuadWithEdgeAA, but is kept separate since knowing * the geometry is a rectangle affords more optimizations. */ void fillRectWithEdgeAA(const GrClip* clip, GrPaint&& paint, GrQuadAAFlags edgeAA, const SkMatrix& viewMatrix, const SkRect& rect, const SkRect* optionalLocalRect = nullptr) { if (edgeAA == GrQuadAAFlags::kAll) { this->fillRectToRect(clip, std::move(paint), GrAA::kYes, viewMatrix, rect, (optionalLocalRect) ? *optionalLocalRect : rect); return; } const SkRect& localRect = optionalLocalRect ? *optionalLocalRect : rect; DrawQuad quad{GrQuad::MakeFromRect(rect, viewMatrix), GrQuad(localRect), edgeAA}; this->drawFilledQuad(clip, std::move(paint), &quad); } /** * Similar to fillRectWithEdgeAA but draws an arbitrary 2D convex quadrilateral transformed * by 'viewMatrix', with per-edge control over anti-aliasing. The quad should follow the * ordering used by SkRect::toQuad(), which determines how the edge AA is applied: * - "top" = points [0] and [1] * - "right" = points[1] and [2] * - "bottom" = points[2] and [3] * - "left" = points[3] and [0] * * The last argument, 'optionalLocalQuad', can be null if no separate local coordinates are * necessary. */ void fillQuadWithEdgeAA(const GrClip* clip, GrPaint&& paint, GrQuadAAFlags edgeAA, const SkMatrix& viewMatrix, const SkPoint points[4], const SkPoint optionalLocalPoints[4]) { const SkPoint* localPoints = optionalLocalPoints ? optionalLocalPoints : points; DrawQuad quad{GrQuad::MakeFromSkQuad(points, viewMatrix), GrQuad::MakeFromSkQuad(localPoints, SkMatrix::I()), edgeAA}; this->drawFilledQuad(clip, std::move(paint), &quad); } // TODO(michaelludwig) - remove if the bulk API is not useful for SkiaRenderer void drawQuadSet(const GrClip* clip, GrPaint&& paint, const SkMatrix& viewMatrix, const GrQuadSetEntry[], int cnt); /** * Creates an op that draws a subrectangle of a texture. The passed color is modulated by the * texture's color. 'srcRect' specifies the rectangle of the texture to draw. 'dstRect' * specifies the rectangle to draw in local coords which will be transformed by 'viewMatrix' to * device space. */ void drawTexture(const GrClip*, GrSurfaceProxyView, SkAlphaType, GrSamplerState::Filter, GrSamplerState::MipmapMode, SkBlendMode, const SkPMColor4f&, const SkRect& srcRect, const SkRect& dstRect, GrQuadAAFlags, SkCanvas::SrcRectConstraint, const SkMatrix&, sk_sp); /** * Variant of drawTexture that instead draws the texture applied to 'dstQuad' transformed by * 'viewMatrix', using the 'srcQuad' texture coordinates clamped to the optional 'subset'. If * 'subset' is null, it's equivalent to using the fast src rect constraint. If 'subset' is * provided, the strict src rect constraint is applied using 'subset'. */ void drawTextureQuad(const GrClip* clip, GrSurfaceProxyView view, GrColorType srcColorType, SkAlphaType srcAlphaType, GrSamplerState::Filter filter, GrSamplerState::MipmapMode mm, SkBlendMode mode, const SkPMColor4f& color, const SkPoint srcQuad[4], const SkPoint dstQuad[4], GrQuadAAFlags edgeAA, const SkRect* subset, const SkMatrix& viewMatrix, sk_sp texXform) { DrawQuad quad{GrQuad::MakeFromSkQuad(dstQuad, viewMatrix), GrQuad::MakeFromSkQuad(srcQuad, SkMatrix::I()), edgeAA}; this->drawTexturedQuad(clip, std::move(view), srcAlphaType, std::move(texXform), filter, mm, color, mode, &quad, subset); } /** * Draws a set of textures with a shared filter, color, view matrix, color xform, and * texture color xform. The textures must all have the same GrTextureType and GrConfig. * * If any entries provide a non-null fDstClip array, it will be read from immediately based on * fDstClipCount, so the pointer can become invalid after this returns. * * 'proxRunCnt' is the number of proxy changes encountered in the entry array. Technically this * can be inferred from the array within this function, but the information is already known * by SkGpuDevice, so no need to incur another iteration over the array. */ void drawTextureSet(const GrClip*, GrTextureSetEntry[], int cnt, int proxyRunCnt, GrSamplerState::Filter, GrSamplerState::MipmapMode, SkBlendMode mode, SkCanvas::SrcRectConstraint, const SkMatrix& viewMatrix, sk_sp texXform); /** * Draw a roundrect using a paint. * * @param paint describes how to color pixels. * @param GrAA Controls whether rrect is antialiased. * @param viewMatrix transformation matrix * @param rrect the roundrect to draw * @param style style to apply to the rrect. Currently path effects are not allowed. */ void drawRRect(const GrClip*, GrPaint&&, GrAA, const SkMatrix& viewMatrix, const SkRRect& rrect, const GrStyle& style); /** * Use a fast method to render the ambient and spot shadows for a path. * Will return false if not possible for the given path. * * @param viewMatrix transformation matrix * @param path the path to shadow * @param rec parameters for shadow rendering */ bool drawFastShadow(const GrClip*, const SkMatrix& viewMatrix, const SkPath& path, const SkDrawShadowRec& rec); /** * Draws a path. * * @param paint describes how to color pixels. * @param GrAA Controls whether the path is antialiased. * @param viewMatrix transformation matrix * @param path the path to draw * @param style style to apply to the path. */ void drawPath(const GrClip*, GrPaint&&, GrAA, const SkMatrix& viewMatrix, const SkPath&, const GrStyle&); /** * Draws a shape. * * @param paint describes how to color pixels. * @param GrAA Controls whether the path is antialiased. * @param viewMatrix transformation matrix * @param shape the shape to draw */ void drawShape(const GrClip*, GrPaint&&, GrAA, const SkMatrix& viewMatrix, GrStyledShape&&); /** * Draws vertices with a paint. * * @param paint describes how to color pixels. * @param viewMatrix transformation matrix * @param vertices specifies the mesh to draw. * @param overridePrimType primitive type to draw. If NULL, derive prim type from vertices. * @param skipColorXform if true, do not apply a color space transfer function */ void drawVertices(const GrClip*, GrPaint&& paint, const SkMatrix& viewMatrix, sk_sp vertices, GrPrimitiveType* overridePrimType = nullptr, bool skipColorXform = false); /** * Draws a custom mesh with a paint. * * @param paint describes how to color pixels. * @param viewMatrix transformation matrix * @param mesh the mesh to draw. * @param children child effects referenced by SkMesh shaders */ void drawMesh(const GrClip*, GrPaint&& paint, const SkMatrix& viewMatrix, const SkMesh& mesh, skia_private::TArray> children); /** * Draws textured sprites from an atlas with a paint. This currently does not support AA for the * sprite rectangle edges. * * @param paint describes how to color pixels. * @param viewMatrix transformation matrix * @param spriteCount number of sprites. * @param xform array of compressed transformation data, required. * @param texRect array of texture rectangles used to access the paint. * @param colors optional array of per-sprite colors, supercedes * the paint's color field. */ void drawAtlas(const GrClip*, GrPaint&& paint, const SkMatrix& viewMatrix, int spriteCount, const SkRSXform xform[], const SkRect texRect[], const SkColor colors[]); /** * Draws a region. * * @param paint describes how to color pixels * @param viewMatrix transformation matrix * @param aa should the rects of the region be antialiased. * @param region the region to be drawn * @param style style to apply to the region */ void drawRegion(const GrClip*, GrPaint&& paint, GrAA aa, const SkMatrix& viewMatrix, const SkRegion& region, const GrStyle& style, const GrUserStencilSettings* ss = nullptr); /** * Draws an oval. * * @param paint describes how to color pixels. * @param GrAA Controls whether the oval is antialiased. * @param viewMatrix transformation matrix * @param oval the bounding rect of the oval. * @param style style to apply to the oval. Currently path effects are not allowed. */ void drawOval(const GrClip*, GrPaint&& paint, GrAA, const SkMatrix& viewMatrix, const SkRect& oval, const GrStyle& style); /** * Draws a partial arc of an oval. * * @param paint describes how to color pixels. * @param GrGrAA Controls whether the arc is antialiased. * @param viewMatrix transformation matrix. * @param oval the bounding rect of the oval. * @param startAngle starting angle in degrees. * @param sweepAngle angle to sweep in degrees. Must be in (-360, 360) * @param useCenter true means that the implied path begins at the oval center, connects as * a line to the point indicated by the start contains the arc indicated by * the sweep angle. If false the line beginning at the center point is * omitted. * @param style style to apply to the oval. */ void drawArc(const GrClip*, GrPaint&& paint, GrAA, const SkMatrix& viewMatrix, const SkArc& arc, const GrStyle& style); /** * Draw the image as a set of rects, specified by |iter|. */ void drawImageLattice(const GrClip*, GrPaint&&, const SkMatrix& viewMatrix, GrSurfaceProxyView, SkAlphaType alphaType, sk_sp, GrSamplerState::Filter, std::unique_ptr, const SkRect& dst); /** * Draw the text specified by the GlyphRunList. * * @param viewMatrix transformation matrix * @param glyphRunList text, text positions, and paint. */ void drawGlyphRunList(SkCanvas*, const GrClip*, const SkMatrix& viewMatrix, const sktext::GlyphRunList& glyphRunList, SkStrikeDeviceInfo strikeDeviceInfo, const SkPaint& paint); /** * Adds the necessary signal and wait semaphores and adds the passed in SkDrawable to the * command stream. */ void drawDrawable(std::unique_ptr, const SkRect& bounds); // called to note the last clip drawn to the stencil buffer. // TODO: remove after clipping overhaul. void setLastClip(uint32_t clipStackGenID, const SkIRect& devClipBounds, int numClipAnalyticElements); // called to determine if we have to render the clip into SB. // TODO: remove after clipping overhaul. bool mustRenderClip(uint32_t clipStackGenID, const SkIRect& devClipBounds, int numClipAnalyticElements); void clearStencilClip(const SkIRect& scissor, bool insideStencilMask) { this->internalStencilClear(&scissor, insideStencilMask); } // While this can take a general clip, since ClipStack relies on this function, it must take // care to only provide hard clips or we could get stuck in a loop. The general clip is needed // so that path renderers can use this function. void stencilRect(const GrClip* clip, const GrUserStencilSettings* ss, GrPaint&& paint, GrAA doStencilMSAA, const SkMatrix& viewMatrix, const SkRect& rect, const SkMatrix* localMatrix = nullptr) { // Since this provides stencil settings to drawFilledQuad, it performs a different AA type // resolution compared to regular rect draws, which is the main reason it remains separate. DrawQuad quad{GrQuad::MakeFromRect(rect, viewMatrix), localMatrix ? GrQuad::MakeFromRect(rect, *localMatrix) : GrQuad(rect), doStencilMSAA == GrAA::kYes ? GrQuadAAFlags::kAll : GrQuadAAFlags::kNone}; this->drawFilledQuad(clip, std::move(paint), &quad, ss); } // Fills the user stencil bits with a non-zero value at every sample inside the path. This will // likely be implemented with a Redbook algorithm, but it is not guaranteed. The samples being // rendered to must be zero initially. bool stencilPath(const GrHardClip*, GrAA doStencilMSAA, const SkMatrix& viewMatrix, const SkPath&); /** * Draws a path, either AA or not, and touches the stencil buffer with the user stencil settings * for each color sample written. */ bool drawAndStencilPath(const GrHardClip*, const GrUserStencilSettings*, SkRegion::Op op, bool invert, GrAA doStencilMSAA, const SkMatrix& viewMatrix, const SkPath&); skgpu::Budgeted isBudgeted() const; int maxWindowRectangles() const; /* * This unique ID will not change for a given SurfaceDrawContext. However, it is _NOT_ * guaranteed to match the uniqueID of the underlying GrRenderTarget - beware! */ GrSurfaceProxy::UniqueID uniqueID() const { return this->asSurfaceProxy()->uniqueID(); } // Allows caller of addDrawOp to know which op list an op will be added to. using WillAddOpFn = void(GrOp*, uint32_t opsTaskID); // These perform processing specific to GrDrawOp-derived ops before recording them into an // op list. Before adding the op to an op list the WillAddOpFn is called. Note that it // will not be called in the event that the op is discarded. Moreover, the op may merge into // another op after the function is called (either before addDrawOp returns or some time later). // // If the clip pointer is null, no clipping will be performed. void addDrawOp(const GrClip*, GrOp::Owner, const std::function& = std::function()); void addDrawOp(GrOp::Owner op) { this->addDrawOp(nullptr, std::move(op)); } bool refsWrappedObjects() const { return this->asRenderTargetProxy()->refsWrappedObjects(); } /** * The next time this SurfaceDrawContext is flushed, the gpu will wait on the passed in * semaphores before executing any commands. */ bool waitOnSemaphores(int numSemaphores, const GrBackendSemaphore waitSemaphores[], bool deleteSemaphoresAfterWait); int numSamples() const { return this->asRenderTargetProxy()->numSamples(); } const SkSurfaceProps& surfaceProps() const { return fSurfaceProps; } bool canUseDynamicMSAA() const { return fCanUseDynamicMSAA; } bool wrapsVkSecondaryCB() const { return this->asRenderTargetProxy()->wrapsVkSecondaryCB(); } bool alwaysAntialias() const { return fSurfaceProps.flags() & SkSurfaceProps::kDynamicMSAA_Flag; } GrAA chooseAA(const SkPaint& paint) { return GrAA(paint.isAntiAlias() || this->alwaysAntialias()); } GrAAType chooseAAType(GrAA aa) { if (this->numSamples() > 1 || fCanUseDynamicMSAA) { // Always trigger DMSAA when it's available. The coverage ops that know how to handle // both single and multisample targets without popping will do so without calling // chooseAAType. return GrAAType::kMSAA; } return (aa == GrAA::kYes) ? GrAAType::kCoverage : GrAAType::kNone; } // This entry point should only be called if the backing GPU object is known to be // instantiated. GrRenderTarget* accessRenderTarget() { return this->asSurfaceProxy()->peekRenderTarget(); } #if defined(GPU_TEST_UTILS) void testingOnly_SetPreserveOpsOnFullClear() { fPreserveOpsOnFullClear_TestingOnly = true; } #endif void drawStrokedLine(const GrClip*, GrPaint&&, GrAA, const SkMatrix&, const SkPoint[2], const SkStrokeRec&); private: enum class QuadOptimization; void willReplaceOpsTask(OpsTask* prevTask, OpsTask* nextTask) override; OpsTask::CanDiscardPreviousOps canDiscardPreviousOpsOnFullClear() const override; void setNeedsStencil(); void internalStencilClear(const SkIRect* scissor, bool insideStencilMask); // 'stencilSettings' are provided merely for decision making purposes; When non-null, // optimization strategies that submit special ops are avoided. // // 'quad' should be the original draw request on input, and will be updated as // appropriate depending on the returned optimization level. // // If kSubmitted is returned, the provided paint was consumed. Otherwise it is left unchanged. QuadOptimization attemptQuadOptimization(const GrClip* clip, const GrUserStencilSettings* stencilSettings, DrawQuad* quad, GrPaint* paint); // The overall AA policy is determined by the quad's edge flags: kNone is no AA, and anything // else uses some form of anti-aliasing. If 'ss' is non-null, that will be MSAA; otherwise it's // MSAA or analytic coverage per chooseAAType(). This will always attempt to apply // quad optimizations, so all quad/rect public APIs should rely on this function for consistent // clipping behavior. 'quad' will be modified in place to reflect final rendered geometry. void drawFilledQuad(const GrClip* clip, GrPaint&& paint, DrawQuad* quad, const GrUserStencilSettings* ss = nullptr); // Like drawFilledQuad but does not require using a GrPaint or FP for texturing. // 'quad' may be modified in place to reflect final geometry. void drawTexturedQuad(const GrClip* clip, GrSurfaceProxyView proxyView, SkAlphaType alphaType, sk_sp textureXform, GrSamplerState::Filter filter, GrSamplerState::MipmapMode, const SkPMColor4f& color, SkBlendMode blendMode, DrawQuad* quad, const SkRect* subset = nullptr); // Tries to detect if the given shape is a simple, and draws it without path rendering if // we know how. bool drawSimpleShape(const GrClip*, GrPaint*, GrAA, const SkMatrix&, const GrStyledShape&); // If 'attemptDrawSimple' is true, of if the original shape is marked as having been simplfied, // this will attempt to re-route through drawSimpleShape() to see if we can avoid path rendering // one more time. void drawShapeUsingPathRenderer(const GrClip*, GrPaint&&, GrAA, const SkMatrix&, GrStyledShape&&, bool attemptDrawSimple = false); // Makes a copy of the proxy if it is necessary for the draw and places the texture that should // be used by GrXferProcessor to access the destination color in 'result'. If the return // value is false then a texture copy could not be made. // // The op should have already had setClippedBounds called on it. [[nodiscard]] bool setupDstProxyView(const SkRect& opBounds, bool opRequiresMSAA, GrDstProxyView* result); OpsTask* replaceOpsTaskIfModifiesColor(); const SkSurfaceProps fSurfaceProps; const bool fCanUseDynamicMSAA; bool fNeedsStencil = false; #if defined(GPU_TEST_UTILS) bool fPreserveOpsOnFullClear_TestingOnly = false; #endif }; } // namespace skgpu::ganesh #endif // SurfaceDrawContext_v1_DEFINED