blob: df310b8360fa8d4e9247746e57d2154378f70e1e [file] [log] [blame]
/*
* Copyright 2020 Google LLC.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#ifndef GrStrokeTessellateOp_DEFINED
#define GrStrokeTessellateOp_DEFINED
#include "include/core/SkStrokeRec.h"
#include "src/gpu/ops/GrDrawOp.h"
#include "src/gpu/tessellate/GrStrokeTessellator.h"
#include "src/gpu/tessellate/shaders/GrTessellationShader.h"
class GrRecordingContext;
// Renders strokes by linearizing them into sorted "parametric" and "radial" edges. See
// GrStrokeTessellationShader.
class GrStrokeTessellateOp : public GrDrawOp {
public:
GrStrokeTessellateOp(GrAAType, const SkMatrix&, const SkPath&, const SkStrokeRec&, GrPaint&&);
private:
using ShaderFlags = GrStrokeTessellationShader::ShaderFlags;
using PathStrokeList = GrStrokeTessellator::PathStrokeList;
DEFINE_OP_CLASS_ID
SkStrokeRec& headStroke() { return fPathStrokeList.fStroke; }
SkPMColor4f& headColor() { return fPathStrokeList.fColor; }
// Returns whether it is a good tradeoff to use the dynamic states flagged in the given
// bitfield. Dynamic states improve batching, but if they aren't already enabled, they come at
// the cost of having to write out more data with each patch or instance.
bool shouldUseDynamicStates(ShaderFlags neededDynamicStates) const {
// Use the dynamic states if either (1) they are all already enabled anyway, or (2) we don't
// have many verbs.
constexpr static int kMaxVerbsToEnableDynamicState = 50;
bool anyStateDisabled = (bool)(~fShaderFlags & neededDynamicStates);
bool allStatesEnabled = !anyStateDisabled;
return allStatesEnabled || (fTotalCombinedVerbCnt <= kMaxVerbsToEnableDynamicState);
}
const char* name() const override { return "GrStrokeTessellateOp"; }
void visitProxies(const GrVisitProxyFunc&) const override;
bool usesMSAA() const override { return fAAType == GrAAType::kMSAA; }
GrProcessorSet::Analysis finalize(const GrCaps&, const GrAppliedClip*, GrClampType) override;
bool usesStencil() const override {
// This must be called after finalize(). fNeedsStencil can change in finalize().
SkASSERT(fProcessors.isFinalized());
return fNeedsStencil;
}
CombineResult onCombineIfPossible(GrOp*, SkArenaAlloc*, const GrCaps&) override;
// Creates the tessellator and the stencil/fill program(s) we will use with it.
void prePrepareTessellator(GrTessellationShader::ProgramArgs&&, GrAppliedClip&&);
void onPrePrepare(GrRecordingContext*, const GrSurfaceProxyView&, GrAppliedClip*,
const GrDstProxyView&, GrXferBarrierFlags, GrLoadOp colorLoadOp) override;
void onPrepare(GrOpFlushState*) override;
void onExecute(GrOpFlushState*, const SkRect& chainBounds) override;
const GrAAType fAAType;
const SkMatrix fViewMatrix;
ShaderFlags fShaderFlags = ShaderFlags::kNone;
PathStrokeList fPathStrokeList;
PathStrokeList** fPathStrokeTail = &fPathStrokeList.fNext;
float fInflationRadius = 0;
int fTotalCombinedVerbCnt = 0;
GrProcessorSet fProcessors;
bool fNeedsStencil;
GrStrokeTessellator* fTessellator = nullptr;
const GrProgramInfo* fStencilProgram = nullptr; // Only used if the stroke has transparency.
const GrProgramInfo* fFillProgram = nullptr;
};
#endif