blob: 9552ff34c22bca3fbcd23bafc2c91e8f5277439b [file] [log] [blame]
/*
* Copyright 2020 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#ifndef GrStrokeIndirectTessellator_DEFINED
#define GrStrokeIndirectTessellator_DEFINED
#include "src/gpu/tessellate/GrStrokeTessellateOp.h"
struct GrVertexWriter;
struct SkPoint;
namespace skiatest { class Reporter; }
// This class bins strokes into indirect draws for consumption by GrStrokeTessellateShader.
class GrStrokeIndirectTessellator : public GrStrokeTessellator {
public:
// Don't allow more than 2^15 stroke edges in a triangle strip. GrTessellationPathRenderer
// already crops paths that require more than 2^10 parametric segments, so this should only
// become an issue if we try to draw a stroke with an astronomically wide width.
constexpr static int8_t kMaxResolveLevel = 15;
GrStrokeIndirectTessellator(ShaderFlags, const SkMatrix&, GrSTArenaList<PathStroke>&&,
int totalCombinedVerbCnt, SkArenaAlloc*);
// Adds the given tessellator to our chain. The chained tessellators all append to a shared
// indirect draw list during prepare().
void addToChain(GrStrokeIndirectTessellator*);
void prepare(GrMeshDrawOp::Target*, const SkMatrix&) override;
void draw(GrOpFlushState*) const override;
private:
// Called during prepare(). Appends our indirect-draw commands and instance data onto the
// provided writers.
void writeBuffers(GrDrawIndirectWriter*, GrVertexWriter*, const SkMatrix&,
size_t instanceStride, int baseInstance, int numExtraEdgesInJoin);
int fResolveLevelCounts[kMaxResolveLevel + 1] = {0}; // # of instances at each resolve level.
int fTotalInstanceCount = 0; // Total number of stroke instances we will draw.
// Total number of indirect draw commands in the chain, or zero if we are not the chain head.
int fChainedDrawIndirectCount = 0;
// Total number of stroke instances in the entire chain, or zero if we are not the chain head.
int fChainedInstanceCount = 0;
// This array holds a resolveLevel for each stroke in the path, stored in the iteration order of
// GrStrokeIterator. If a stroke needs to be chopped, the array will contain a negative number
// whose absolute value is the number of chops required, followed by a resolveLevel for each
// resulting stroke after the chop(s).
int8_t* fResolveLevels = nullptr;
// fResolveLevelArrayCount != fTotalInstanceCount because we don't always need to write out
// resolve levels for line instances. (If they don't have round caps then their resolve level is
// just 0.)
SkDEBUGCODE(int fResolveLevelArrayCount = 0;)
// Stores the in-order chop locations for all chops indicated by fResolveLevels.
float* fChopTs = nullptr;
SkDEBUGCODE(int fChopTsArrayCount = 0;)
// Bevel, miter, and round joins require us to add different numbers of additional edges onto
// their triangle strips. When using dynamic stroke, we just append the maximum required number
// of additional edges to every instance.
int fMaxNumExtraEdgesInJoin = 0;
// Chained tessellators. These all append to our shared indirect draw list during prepare().
GrStrokeIndirectTessellator* fNextInChain = nullptr;
GrStrokeIndirectTessellator** fChainTail = &fNextInChain; // Null if we are not the chain head.
// GPU buffers for drawing.
sk_sp<const GrBuffer> fDrawIndirectBuffer;
sk_sp<const GrBuffer> fInstanceBuffer;
size_t fDrawIndirectOffset;
friend class GrOp; // For ctor.
#if GR_TEST_UTILS
public:
void verifyResolveLevels(skiatest::Reporter*, class GrMockOpTarget*, const SkMatrix&,
const SkPath&, const SkStrokeRec&);
void verifyBuffers(skiatest::Reporter*, class GrMockOpTarget*, const SkMatrix&,
const SkStrokeRec&);
class Benchmark;
#endif
};
#endif