Add a dynamic color attrib to hw tessellated stroking
Only adds color to the hardware tessellator. The indirect tessellator reorders draws with is log2 binning, so we can't have different colors. Bug: chromium:1172543 Bug: skia:10419 Change-Id: I2a3700cd4572e8222002bfb028af05c6ec447708 Reviewed-on: https://skia-review.googlesource.com/c/skia/+/369976 Commit-Queue: Chris Dalton <csmartdalton@google.com> Reviewed-by: Brian Osman <brianosman@google.com>
This commit is contained in:
parent
fac4efcabe
commit
1017a3558f
@ -231,7 +231,8 @@ private:
|
||||
SkMatrix matrix = SkMatrix::Scale(fMatrixScale, fMatrixScale);
|
||||
GrStrokeHardwareTessellator tessellator(ShaderFlags::kNone,
|
||||
*fTarget->caps().shaderCaps());
|
||||
tessellator.prepare(fTarget.get(), matrix, {fPath, fStrokeRec}, fPath.countVerbs());
|
||||
tessellator.prepare(fTarget.get(), matrix, {fPath, fStrokeRec, SK_PMColor4fWHITE},
|
||||
fPath.countVerbs());
|
||||
}
|
||||
}
|
||||
|
||||
@ -270,10 +271,10 @@ private:
|
||||
for (int i = 0; i < loops; ++i) {
|
||||
for (const SkPath& path : fPaths) {
|
||||
GrStrokeIndirectTessellator tessellator(ShaderFlags::kNone, SkMatrix::I(),
|
||||
{path, fStrokeRec}, path.countVerbs(),
|
||||
fTarget->allocator());
|
||||
tessellator.prepare(fTarget.get(), SkMatrix::I(), {path, fStrokeRec},
|
||||
path.countVerbs());
|
||||
{path, fStrokeRec, SK_PMColor4fWHITE},
|
||||
path.countVerbs(), fTarget->allocator());
|
||||
tessellator.prepare(fTarget.get(), SkMatrix::I(),
|
||||
{path, fStrokeRec, SK_PMColor4fWHITE}, path.countVerbs());
|
||||
}
|
||||
fTarget->resetAllocator();
|
||||
}
|
||||
|
@ -92,13 +92,19 @@ static inline uint64_t SkPMColor4f_toFP16(const SkPMColor4f& color) {
|
||||
*/
|
||||
class GrVertexColor {
|
||||
public:
|
||||
explicit GrVertexColor(const SkPMColor4f& color, bool wideColor)
|
||||
: fWideColor(wideColor) {
|
||||
GrVertexColor() = default;
|
||||
|
||||
explicit GrVertexColor(const SkPMColor4f& color, bool wideColor) {
|
||||
this->set(color, wideColor);
|
||||
}
|
||||
|
||||
void set(const SkPMColor4f& color, bool wideColor) {
|
||||
if (wideColor) {
|
||||
memcpy(fColor, color.vec(), sizeof(fColor));
|
||||
} else {
|
||||
fColor[0] = color.toBytes_RGBA();
|
||||
}
|
||||
fWideColor = wideColor;
|
||||
}
|
||||
|
||||
size_t size() const { return fWideColor ? 16 : 4; }
|
||||
|
@ -121,10 +121,17 @@ void GrStrokeHardwareTessellator::prepare(GrMeshDrawOp::Target* target, const Sk
|
||||
auto tolerances = Tolerances::MakePreTransform(matrixScales.data(), stroke.getWidth());
|
||||
this->updateTolerances(tolerances, stroke.getJoin());
|
||||
}
|
||||
fStroke = &stroke;
|
||||
|
||||
if (fShaderFlags & ShaderFlags::kDynamicStroke) {
|
||||
fDynamicStroke.set(stroke);
|
||||
}
|
||||
fStroke = &stroke;
|
||||
if (fShaderFlags & ShaderFlags::kDynamicColor) {
|
||||
bool wideColor = fShaderFlags & ShaderFlags::kWideColor;
|
||||
SkASSERT(wideColor || pathStroke.fColor.fitsInBytes());
|
||||
fDynamicColor.set(pathStroke.fColor, wideColor);
|
||||
}
|
||||
|
||||
fHasLastControlPoint = false;
|
||||
SkDEBUGCODE(fHasCurrentPoint = false;)
|
||||
SkPathVerb previousVerb = SkPathVerb::kClose;
|
||||
@ -655,6 +662,9 @@ void GrStrokeHardwareTessellator::emitDynamicAttribs() {
|
||||
if (fShaderFlags & ShaderFlags::kDynamicStroke) {
|
||||
fPatchWriter.write(fDynamicStroke);
|
||||
}
|
||||
if (fShaderFlags & ShaderFlags::kDynamicColor) {
|
||||
fPatchWriter.write(fDynamicColor);
|
||||
}
|
||||
}
|
||||
|
||||
bool GrStrokeHardwareTessellator::reservePatch() {
|
||||
|
@ -125,6 +125,7 @@ private:
|
||||
|
||||
// Stateful values for the dynamic state (if any) that will get written out with each patch.
|
||||
GrStrokeTessellateShader::DynamicStroke fDynamicStroke;
|
||||
GrVertexColor fDynamicColor;
|
||||
|
||||
friend class GrOp; // For ctor.
|
||||
|
||||
|
@ -444,6 +444,8 @@ GrStrokeIndirectTessellator::GrStrokeIndirectTessellator(
|
||||
const GrSTArenaList<PathStroke>& pathStrokeList, int totalCombinedVerbCnt,
|
||||
SkArenaAlloc* alloc)
|
||||
: GrStrokeTessellator(shaderFlags) {
|
||||
// We can't combine colors because our log2 binning draws things out of order.
|
||||
SkASSERT(!(fShaderFlags & ShaderFlags::kDynamicColor));
|
||||
SkASSERT(!fTotalInstanceCount);
|
||||
SkASSERT(!fResolveLevels);
|
||||
SkASSERT(!fResolveLevelArrayCount);
|
||||
|
@ -20,13 +20,15 @@ GrStrokeTessellateOp::GrStrokeTessellateOp(GrAAType aaType, const SkMatrix& view
|
||||
: GrDrawOp(ClassID())
|
||||
, fAAType(aaType)
|
||||
, fViewMatrix(viewMatrix)
|
||||
, fColor(paint.getColor4f())
|
||||
, fProcessors(std::move(paint))
|
||||
, fPathStrokeList(path, stroke)
|
||||
, fTotalCombinedVerbCnt(path.countVerbs()) {
|
||||
, fPathStrokeList(path, stroke, paint.getColor4f())
|
||||
, fTotalCombinedVerbCnt(path.countVerbs())
|
||||
, fProcessors(std::move(paint)) {
|
||||
if (SkPathPriv::ConicWeightCnt(path) != 0) {
|
||||
fShaderFlags |= ShaderFlags::kHasConics;
|
||||
}
|
||||
if (!this->headColor().fitsInBytes()) {
|
||||
fShaderFlags |= ShaderFlags::kWideColor;
|
||||
}
|
||||
SkRect devBounds = path.getBounds();
|
||||
float inflationRadius = stroke.getInflationRadius();
|
||||
devBounds.outset(inflationRadius, inflationRadius);
|
||||
@ -62,21 +64,21 @@ GrProcessorSet::Analysis GrStrokeTessellateOp::finalize(const GrCaps& caps,
|
||||
SkASSERT(fPathStrokeList.begin().fCurr->fNext == nullptr);
|
||||
SkASSERT(fAAType != GrAAType::kCoverage || hasMixedSampledCoverage);
|
||||
const GrProcessorSet::Analysis& analysis = fProcessors.finalize(
|
||||
fColor, GrProcessorAnalysisCoverage::kNone, clip, &GrUserStencilSettings::kUnused,
|
||||
hasMixedSampledCoverage, caps, clampType, &fColor);
|
||||
this->headColor(), GrProcessorAnalysisCoverage::kNone, clip,
|
||||
&GrUserStencilSettings::kUnused, hasMixedSampledCoverage, caps, clampType,
|
||||
&this->headColor());
|
||||
fNeedsStencil = !analysis.unaffectedByDstValue();
|
||||
return analysis;
|
||||
}
|
||||
|
||||
GrOp::CombineResult GrStrokeTessellateOp::onCombineIfPossible(GrOp* grOp, SkArenaAlloc* alloc,
|
||||
const GrCaps&) {
|
||||
const GrCaps& caps) {
|
||||
using DynamicStroke = GrStrokeTessellateShader::DynamicStroke;
|
||||
SkASSERT(grOp->classID() == this->classID());
|
||||
auto* op = static_cast<GrStrokeTessellateOp*>(grOp);
|
||||
|
||||
if (fNeedsStencil ||
|
||||
op->fNeedsStencil ||
|
||||
fColor != op->fColor ||
|
||||
fViewMatrix != op->fViewMatrix ||
|
||||
fAAType != op->fAAType ||
|
||||
fProcessors != op->fProcessors ||
|
||||
@ -91,14 +93,29 @@ GrOp::CombineResult GrStrokeTessellateOp::onCombineIfPossible(GrOp* grOp, SkAren
|
||||
// still decide to combine them.
|
||||
combinedFlags |= ShaderFlags::kDynamicStroke;
|
||||
}
|
||||
if (combinedFlags & ShaderFlags::kDynamicStroke) {
|
||||
// Don't actually enable dynamic stroke on ops that already have lots of verbs.
|
||||
if (!this->shouldUseDynamicState(ShaderFlags::kDynamicStroke) ||
|
||||
!op->shouldUseDynamicState(ShaderFlags::kDynamicStroke)) {
|
||||
if (!(combinedFlags & ShaderFlags::kDynamicColor) && this->headColor() != op->headColor()) {
|
||||
// The paths have different colors. We will need to enable dynamic color if we still decide
|
||||
// to combine them.
|
||||
combinedFlags |= ShaderFlags::kDynamicColor;
|
||||
}
|
||||
|
||||
// Don't actually enable new dynamic state on ops that already have lots of verbs.
|
||||
constexpr static GrTFlagsMask<ShaderFlags> kDynamicStatesMask(ShaderFlags::kDynamicStroke |
|
||||
ShaderFlags::kDynamicColor);
|
||||
ShaderFlags neededDynamicStates = combinedFlags & kDynamicStatesMask;
|
||||
if (neededDynamicStates != ShaderFlags::kNone) {
|
||||
if (!this->shouldUseDynamicStates(neededDynamicStates) ||
|
||||
!op->shouldUseDynamicStates(neededDynamicStates)) {
|
||||
return CombineResult::kCannotCombine;
|
||||
}
|
||||
}
|
||||
|
||||
// The indirect tessellator can't combine colors because its log2 binning draws things out of
|
||||
// order. Only enable dynamic color if we have hardware tessellation.
|
||||
if ((combinedFlags & ShaderFlags::kDynamicColor) && !this->canUseHardwareTessellation(caps)) {
|
||||
return CombineResult::kCannotCombine;
|
||||
}
|
||||
|
||||
fPathStrokeList.concat(std::move(op->fPathStrokeList), alloc);
|
||||
fTotalCombinedVerbCnt += op->fTotalCombinedVerbCnt;
|
||||
fShaderFlags = combinedFlags;
|
||||
@ -136,14 +153,11 @@ void GrStrokeTessellateOp::prePrepareTessellator(GrPathShader::ProgramArgs&& arg
|
||||
const GrCaps& caps = *args.fCaps;
|
||||
SkArenaAlloc* arena = args.fArena;
|
||||
|
||||
// Only use hardware tessellation if the path has a somewhat large number of verbs. Otherwise we
|
||||
// seem to be better off using indirect draws. Our back door for HW tessellation shaders isn't
|
||||
// currently capable of passing varyings to the fragment shader either, so if the processors
|
||||
// have varyings we need to use indirect draws.
|
||||
// Only use hardware tessellation if we need dynamic color or if the path has a somewhat large
|
||||
// number of verbs. Otherwise we seem to be better off using indirect draws.
|
||||
GrStrokeTessellateShader::Mode shaderMode;
|
||||
if (caps.shaderCaps()->tessellationSupport() &&
|
||||
fTotalCombinedVerbCnt > 50 &&
|
||||
!fProcessors.usesVaryingCoords()) {
|
||||
if (this->canUseHardwareTessellation(caps) &&
|
||||
((fShaderFlags & ShaderFlags::kDynamicColor) || fTotalCombinedVerbCnt > 50)) {
|
||||
fTessellator = arena->make<GrStrokeHardwareTessellator>(fShaderFlags, *caps.shaderCaps());
|
||||
shaderMode = GrStrokeTessellateShader::Mode::kTessellation;
|
||||
} else {
|
||||
@ -164,7 +178,7 @@ void GrStrokeTessellateOp::prePrepareTessellator(GrPathShader::ProgramArgs&& arg
|
||||
}
|
||||
|
||||
auto* strokeTessellateShader = arena->make<GrStrokeTessellateShader>(
|
||||
shaderMode, fShaderFlags, fViewMatrix, this->headStroke(), fColor);
|
||||
shaderMode, fShaderFlags, fViewMatrix, this->headStroke(), this->headColor());
|
||||
auto* fillPipeline = GrFillPathShader::MakeFillPassPipeline(args, fAAType, std::move(clip),
|
||||
std::move(fProcessors));
|
||||
auto fillStencil = &GrUserStencilSettings::kUnused;
|
||||
|
@ -24,9 +24,11 @@ public:
|
||||
GrStrokeTessellator(ShaderFlags shaderFlags) : fShaderFlags(shaderFlags) {}
|
||||
|
||||
struct PathStroke {
|
||||
PathStroke(const SkPath& path, const SkStrokeRec& stroke) : fPath(path), fStroke(stroke) {}
|
||||
PathStroke(const SkPath& path, const SkStrokeRec& stroke, const SkPMColor4f& color)
|
||||
: fPath(path), fStroke(stroke), fColor(color) {}
|
||||
SkPath fPath;
|
||||
SkStrokeRec fStroke;
|
||||
SkPMColor4f fColor;
|
||||
};
|
||||
|
||||
// Called before draw(). Prepares GPU buffers containing the geometry to tessellate.
|
||||
@ -55,16 +57,25 @@ private:
|
||||
DEFINE_OP_CLASS_ID
|
||||
|
||||
SkStrokeRec& headStroke() { return fPathStrokeList.head().fStroke; }
|
||||
SkPMColor4f& headColor() { return fPathStrokeList.head().fColor; }
|
||||
|
||||
// Returns whether it is a good tradeoff to use the given dynamic state. Dynamic state improves
|
||||
// batching, but if it isn't already enabled, it comes at the cost of having to write out more
|
||||
// data with each patch or instance.
|
||||
bool shouldUseDynamicState(ShaderFlags dynamicState) const {
|
||||
// Use the dynamic state if either (1) the state is already enabled anyway, or (2) we don't
|
||||
// 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;
|
||||
return (fShaderFlags & dynamicState) ||
|
||||
(fTotalCombinedVerbCnt <= kMaxVerbsToEnableDynamicState);
|
||||
bool anyStateDisabled = (bool)(~fShaderFlags & neededDynamicStates);
|
||||
bool allStatesEnabled = !anyStateDisabled;
|
||||
return allStatesEnabled || (fTotalCombinedVerbCnt <= kMaxVerbsToEnableDynamicState);
|
||||
}
|
||||
|
||||
bool canUseHardwareTessellation(const GrCaps& caps) {
|
||||
SkASSERT(!fStencilProgram && !fFillProgram); // Ensure we haven't std::moved fProcessors.
|
||||
// Our back door for HW tessellation shaders isn't currently capable of passing varyings to
|
||||
// the fragment shader, so if the processors have varyings we need to use indirect draws.
|
||||
return caps.shaderCaps()->tessellationSupport() && !fProcessors.usesVaryingCoords();
|
||||
}
|
||||
|
||||
const char* name() const override { return "GrStrokeTessellateOp"; }
|
||||
@ -87,13 +98,11 @@ private:
|
||||
|
||||
const GrAAType fAAType;
|
||||
const SkMatrix fViewMatrix;
|
||||
SkPMColor4f fColor;
|
||||
bool fNeedsStencil = false;
|
||||
GrProcessorSet fProcessors;
|
||||
|
||||
ShaderFlags fShaderFlags = ShaderFlags::kNone;
|
||||
GrSTArenaList<PathStroke> fPathStrokeList;
|
||||
int fTotalCombinedVerbCnt = 0;
|
||||
GrProcessorSet fProcessors;
|
||||
bool fNeedsStencil = false;
|
||||
|
||||
GrStrokeTessellator* fTessellator = nullptr;
|
||||
const GrProgramInfo* fStencilProgram = nullptr; // Only used if the stroke has transparency.
|
||||
|
@ -135,6 +135,9 @@ private:
|
||||
// [NUM_RADIAL_SEGMENTS_PER_RADIAN, STROKE_RADIUS]
|
||||
v->declareGlobal(GrShaderVar("vsStrokeArgs", kFloat2_GrSLType, TypeModifier::Out));
|
||||
}
|
||||
if (shader.hasDynamicColor()) {
|
||||
v->declareGlobal(GrShaderVar("vsColor", kHalf4_GrSLType, TypeModifier::Out));
|
||||
}
|
||||
|
||||
v->insertFunction(kAtan2Fn);
|
||||
v->insertFunction(kCosineBetweenVectorsFn);
|
||||
@ -380,12 +383,25 @@ private:
|
||||
v->codeAppend(R"(
|
||||
vsStrokeArgs = float2(NUM_RADIAL_SEGMENTS_PER_RADIAN, STROKE_RADIUS);)");
|
||||
}
|
||||
if (shader.hasDynamicColor()) {
|
||||
v->codeAppend(R"(
|
||||
vsColor = dynamicColorAttr;)");
|
||||
}
|
||||
|
||||
if (!shader.hasDynamicColor()) {
|
||||
// The fragment shader just outputs a uniform color.
|
||||
const char* colorUniformName;
|
||||
fColorUniform = uniHandler->addUniform(nullptr, kFragment_GrShaderFlag, kHalf4_GrSLType,
|
||||
"color", &colorUniformName);
|
||||
args.fFragBuilder->codeAppendf("%s = %s;", args.fOutputColor, colorUniformName);
|
||||
} else {
|
||||
// Color gets passed in from the tess evaluation shader.
|
||||
SkString flatness(args.fShaderCaps->preferFlatInterpolation() ? "flat" : "");
|
||||
args.fFragBuilder->declareGlobal(GrShaderVar(SkString("tesColor"), kHalf4_GrSLType,
|
||||
TypeModifier::In, 0, SkString(),
|
||||
flatness));
|
||||
args.fFragBuilder->codeAppendf("%s = tesColor;", args.fOutputColor);
|
||||
}
|
||||
args.fFragBuilder->codeAppendf("%s = half4(1);", args.fOutputCoverage);
|
||||
}
|
||||
|
||||
@ -423,8 +439,10 @@ private:
|
||||
m.getScaleY());
|
||||
}
|
||||
|
||||
if (!shader.hasDynamicColor()) {
|
||||
pdman.set4fv(fColorUniform, 1, shader.fColor.vec());
|
||||
}
|
||||
}
|
||||
|
||||
GrGLSLUniformHandler::UniformHandle fTessArgsUniform;
|
||||
GrGLSLUniformHandler::UniformHandle fTranslateUniform;
|
||||
@ -487,6 +505,10 @@ SkString GrStrokeTessellateShader::getTessControlShaderGLSL(
|
||||
code.append(R"(
|
||||
in vec2 vsStrokeArgs[];)");
|
||||
}
|
||||
if (this->hasDynamicColor()) {
|
||||
code.append(R"(
|
||||
in mediump vec4 vsColor[];)");
|
||||
}
|
||||
|
||||
code.append(R"(
|
||||
out vec4 tcsPts01[];
|
||||
@ -500,6 +522,10 @@ SkString GrStrokeTessellateShader::getTessControlShaderGLSL(
|
||||
code.append(R"(
|
||||
patch out float tcsStrokeRadius;)");
|
||||
}
|
||||
if (this->hasDynamicColor()) {
|
||||
code.append(R"(
|
||||
patch out mediump vec4 tcsColor;)");
|
||||
}
|
||||
|
||||
code.append(R"(
|
||||
void main() {
|
||||
@ -510,6 +536,10 @@ SkString GrStrokeTessellateShader::getTessControlShaderGLSL(
|
||||
code.append(R"(
|
||||
tcsStrokeRadius = vsStrokeArgs[0].y;)");
|
||||
}
|
||||
if (this->hasDynamicColor()) {
|
||||
code.append(R"(
|
||||
tcsColor = vsColor[0];)");
|
||||
}
|
||||
|
||||
code.append(R"(
|
||||
// Unpack the curve args from the vertex shader.
|
||||
@ -831,6 +861,11 @@ SkString GrStrokeTessellateShader::getTessEvaluationShaderGLSL(
|
||||
code.append(R"(
|
||||
patch in float tcsStrokeRadius;)");
|
||||
}
|
||||
if (this->hasDynamicColor()) {
|
||||
code.appendf(R"(
|
||||
patch in mediump vec4 tcsColor;
|
||||
%s out mediump vec4 tesColor;)", shaderCaps.preferFlatInterpolation() ? "flat" : "");
|
||||
}
|
||||
|
||||
code.append(R"(
|
||||
uniform vec4 sk_RTAdjust;)");
|
||||
@ -932,7 +967,14 @@ SkString GrStrokeTessellateShader::getTessEvaluationShaderGLSL(
|
||||
}
|
||||
|
||||
code.append(R"(
|
||||
gl_Position = vec4(vertexPos * sk_RTAdjust.xz + sk_RTAdjust.yw, 0.0, 1.0);
|
||||
gl_Position = vec4(vertexPos * sk_RTAdjust.xz + sk_RTAdjust.yw, 0.0, 1.0);)");
|
||||
|
||||
if (this->hasDynamicColor()) {
|
||||
code.append(R"(
|
||||
tesColor = tcsColor;)");
|
||||
}
|
||||
|
||||
code.append(R"(
|
||||
})");
|
||||
|
||||
return code;
|
||||
@ -1242,9 +1284,11 @@ class GrStrokeTessellateShader::IndirectImpl : public GrGLSLGeometryProcessor {
|
||||
|
||||
void GrStrokeTessellateShader::getGLSLProcessorKey(const GrShaderCaps&,
|
||||
GrProcessorKeyBuilder* b) const {
|
||||
bool keyNeedsJoin = fMode == Mode::kIndirect && !(fShaderFlags & ShaderFlags::kDynamicStroke);
|
||||
bool keyNeedsJoin = (fMode == Mode::kIndirect) && !(fShaderFlags & ShaderFlags::kDynamicStroke);
|
||||
SkASSERT(fStroke.getJoin() >> 2 == 0);
|
||||
uint32_t key = (uint32_t)fShaderFlags;
|
||||
// Attribs get worked into the key automatically during GrPrimitiveProcessor::getAttributeKey().
|
||||
// When color is in a uniform, it's always wide. kWideColor doesn't need to be considered here.
|
||||
uint32_t key = (uint32_t)(fShaderFlags & ~ShaderFlags::kWideColor);
|
||||
key = (key << 1) | (uint32_t)fMode;
|
||||
key = (key << 2) | ((keyNeedsJoin) ? fStroke.getJoin() : 0);
|
||||
key = (key << 1) | (uint32_t)fStroke.isHairlineStyle();
|
||||
|
@ -34,7 +34,9 @@ public:
|
||||
enum class ShaderFlags {
|
||||
kNone = 0,
|
||||
kHasConics = 1 << 0,
|
||||
kDynamicStroke = 1 << 1 // Each patch or instance has its own stroke width and join type.
|
||||
kWideColor = 1 << 1,
|
||||
kDynamicStroke = 1 << 2, // Each patch or instance has its own stroke width and join type.
|
||||
kDynamicColor = 1 << 3, // Each patch or instance has its own color.
|
||||
};
|
||||
|
||||
GR_DECL_BITFIELD_CLASS_OPS_FRIENDS(ShaderFlags);
|
||||
@ -142,19 +144,23 @@ public:
|
||||
|
||||
// Size in bytes of a tessellation patch with the given shader flags.
|
||||
static size_t PatchStride(ShaderFlags shaderFlags) {
|
||||
size_t stride = sizeof(SkPoint) * 5;
|
||||
if (shaderFlags & ShaderFlags::kDynamicStroke) {
|
||||
stride += sizeof(DynamicStroke);
|
||||
}
|
||||
return stride;
|
||||
return sizeof(SkPoint) * 5 + DynamicStateStride(shaderFlags);
|
||||
}
|
||||
|
||||
// Size in bytes of an indirect draw instance with the given shader flags.
|
||||
static size_t IndirectInstanceStride(ShaderFlags shaderFlags) {
|
||||
size_t stride = sizeof(float) * 11;
|
||||
return sizeof(float) * 11 + DynamicStateStride(shaderFlags);
|
||||
}
|
||||
|
||||
// Combined size in bytes of the dynamic state attribs enabled in the given shader flags.
|
||||
static size_t DynamicStateStride(ShaderFlags shaderFlags) {
|
||||
size_t stride = 0;
|
||||
if (shaderFlags & ShaderFlags::kDynamicStroke) {
|
||||
stride += sizeof(DynamicStroke);
|
||||
}
|
||||
if (shaderFlags & ShaderFlags::kDynamicColor) {
|
||||
stride += (shaderFlags & ShaderFlags::kWideColor) ? sizeof(float) * 4 : 4;
|
||||
}
|
||||
return stride;
|
||||
}
|
||||
|
||||
@ -207,6 +213,13 @@ public:
|
||||
fAttribs.emplace_back("dynamicStrokeAttr", kFloat2_GrVertexAttribType,
|
||||
kFloat2_GrSLType);
|
||||
}
|
||||
if (fShaderFlags & ShaderFlags::kDynamicColor) {
|
||||
fAttribs.emplace_back("dynamicColorAttr",
|
||||
(fShaderFlags & ShaderFlags::kWideColor)
|
||||
? kFloat4_GrVertexAttribType
|
||||
: kUByte4_norm_GrVertexAttribType,
|
||||
kHalf4_GrSLType);
|
||||
}
|
||||
if (fMode == Mode::kTessellation) {
|
||||
this->setVertexAttributes(fAttribs.data(), fAttribs.count());
|
||||
SkASSERT(this->vertexStride() == PatchStride(fShaderFlags));
|
||||
@ -214,10 +227,12 @@ public:
|
||||
this->setInstanceAttributes(fAttribs.data(), fAttribs.count());
|
||||
SkASSERT(this->instanceStride() == IndirectInstanceStride(fShaderFlags));
|
||||
}
|
||||
SkASSERT(fAttribs.count() <= kMaxAttribCount);
|
||||
}
|
||||
|
||||
bool hasConics() const { return fShaderFlags & ShaderFlags::kHasConics; }
|
||||
bool hasDynamicStroke() const { return fShaderFlags & ShaderFlags::kDynamicStroke; }
|
||||
bool hasDynamicColor() const { return fShaderFlags & ShaderFlags::kDynamicColor; }
|
||||
|
||||
private:
|
||||
const char* name() const override { return "GrStrokeTessellateShader"; }
|
||||
@ -237,7 +252,9 @@ private:
|
||||
const ShaderFlags fShaderFlags;
|
||||
const SkStrokeRec fStroke;
|
||||
const SkPMColor4f fColor;
|
||||
SkSTArray<4, Attribute> fAttribs;
|
||||
|
||||
constexpr static int kMaxAttribCount = 5;
|
||||
SkSTArray<kMaxAttribCount, Attribute> fAttribs;
|
||||
|
||||
class TessellationImpl;
|
||||
class IndirectImpl;
|
||||
|
@ -42,10 +42,11 @@ static void test_stroke(skiatest::Reporter* r, GrDirectContext* ctx, GrMockOpTar
|
||||
float scale = ldexpf(rand.nextF() + 1, i);
|
||||
auto matrix = SkMatrix::Scale(scale, scale);
|
||||
GrStrokeIndirectTessellator tessellator(GrStrokeTessellateShader::ShaderFlags::kNone,
|
||||
matrix, {path, stroke}, path.countVerbs(),
|
||||
target->allocator());
|
||||
matrix, {path, stroke, SK_PMColor4fWHITE},
|
||||
path.countVerbs(), target->allocator());
|
||||
tessellator.verifyResolveLevels(r, target, matrix, path, stroke);
|
||||
tessellator.prepare(target, matrix, {path, stroke}, path.countVerbs());
|
||||
tessellator.prepare(target, matrix, {path, stroke, SK_PMColor4fWHITE},
|
||||
path.countVerbs());
|
||||
tessellator.verifyBuffers(r, target, matrix, stroke);
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user