blob: 778750f7211280bccc9deb30f20b4a0a5d0516c2 [file] [log] [blame]
Chris Daltond9bdc322021-06-01 19:22:05 -06001/*
2 * Copyright 2021 Google LLC.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
8#include "src/gpu/tessellate/GrPathWedgeTessellator.h"
9
Robert Phillips06273bc2021-08-11 15:43:50 -040010#include "src/gpu/GrMeshDrawTarget.h"
Robert Phillips1a82a4e2021-07-01 10:27:44 -040011#include "src/gpu/GrResourceProvider.h"
Chris Daltond9bdc322021-06-01 19:22:05 -060012#include "src/gpu/geometry/GrPathUtils.h"
13#include "src/gpu/geometry/GrWangsFormula.h"
14#include "src/gpu/tessellate/GrCullTest.h"
Chris Dalton69669812021-07-27 10:00:12 -060015#include "src/gpu/tessellate/GrPathXform.h"
Chris Daltond9bdc322021-06-01 19:22:05 -060016#include "src/gpu/tessellate/shaders/GrPathTessellationShader.h"
17
18namespace {
19
Chris Daltone1f72372021-06-29 16:45:49 -060020constexpr static float kPrecision = GrTessellationShader::kLinearizationPrecision;
Chris Daltond9bdc322021-06-01 19:22:05 -060021
22// Parses out each contour in a path and tracks the midpoint. Example usage:
23//
24// SkTPathContourParser parser;
25// while (parser.parseNextContour()) {
26// SkPoint midpoint = parser.currentMidpoint();
27// for (auto [verb, pts] : parser.currentContour()) {
28// ...
29// }
30// }
31//
32class MidpointContourParser {
33public:
34 MidpointContourParser(const SkPath& path)
35 : fPath(path)
36 , fVerbs(SkPathPriv::VerbData(fPath))
37 , fNumRemainingVerbs(fPath.countVerbs())
38 , fPoints(SkPathPriv::PointData(fPath))
39 , fWeights(SkPathPriv::ConicWeightData(fPath)) {}
40 // Advances the internal state to the next contour in the path. Returns false if there are no
41 // more contours.
42 bool parseNextContour() {
43 bool hasGeometry = false;
44 for (; fVerbsIdx < fNumRemainingVerbs; ++fVerbsIdx) {
45 switch (fVerbs[fVerbsIdx]) {
46 case SkPath::kMove_Verb:
47 if (!hasGeometry) {
48 fMidpoint = fPoints[fPtsIdx];
49 fMidpointWeight = 1;
50 this->advance();
51 ++fPtsIdx;
52 continue;
53 }
54 return true;
55 default:
56 continue;
57 case SkPath::kLine_Verb:
58 ++fPtsIdx;
59 break;
60 case SkPath::kConic_Verb:
61 ++fWtsIdx;
62 [[fallthrough]];
63 case SkPath::kQuad_Verb:
64 fPtsIdx += 2;
65 break;
66 case SkPath::kCubic_Verb:
67 fPtsIdx += 3;
68 break;
69 }
70 fMidpoint += fPoints[fPtsIdx - 1];
71 ++fMidpointWeight;
72 hasGeometry = true;
73 }
74 return hasGeometry;
75 }
76
77 // Allows for iterating the current contour using a range-for loop.
78 SkPathPriv::Iterate currentContour() {
79 return SkPathPriv::Iterate(fVerbs, fVerbs + fVerbsIdx, fPoints, fWeights);
80 }
81
82 SkPoint currentMidpoint() { return fMidpoint * (1.f / fMidpointWeight); }
83
84private:
85 void advance() {
86 fVerbs += fVerbsIdx;
87 fNumRemainingVerbs -= fVerbsIdx;
88 fVerbsIdx = 0;
89 fPoints += fPtsIdx;
90 fPtsIdx = 0;
91 fWeights += fWtsIdx;
92 fWtsIdx = 0;
93 }
94
95 const SkPath& fPath;
96
97 const uint8_t* fVerbs;
98 int fNumRemainingVerbs = 0;
99 int fVerbsIdx = 0;
100
101 const SkPoint* fPoints;
102 int fPtsIdx = 0;
103
104 const float* fWeights;
105 int fWtsIdx = 0;
106
107 SkPoint fMidpoint;
108 int fMidpointWeight;
109};
110
111// Writes out wedge patches, chopping as necessary so none require more segments than are supported
112// by the hardware.
113class WedgeWriter {
114public:
Chris Dalton17eaf622021-07-28 09:43:52 -0600115 WedgeWriter(GrMeshDrawTarget* target,
116 GrVertexChunkArray* vertexChunkArray,
117 size_t patchStride,
118 int initialPatchAllocCount,
Chris Dalton69669812021-07-27 10:00:12 -0600119 int maxSegments)
Chris Dalton17eaf622021-07-28 09:43:52 -0600120 : fChunker(target, vertexChunkArray, patchStride, initialPatchAllocCount)
Chris Daltond2b8ba32021-06-09 00:12:59 -0600121 , fMaxSegments_pow2(maxSegments * maxSegments)
122 , fMaxSegments_pow4(fMaxSegments_pow2 * fMaxSegments_pow2) {
Chris Daltond9bdc322021-06-01 19:22:05 -0600123 }
124
Chris Dalton17eaf622021-07-28 09:43:52 -0600125 void setMatrices(const SkRect& cullBounds,
126 const SkMatrix& shaderMatrix,
127 const SkMatrix& pathMatrix) {
128 SkMatrix totalMatrix;
129 totalMatrix.setConcat(shaderMatrix, pathMatrix);
130 fCullTest.set(cullBounds, totalMatrix);
131 fTotalVectorXform = totalMatrix;
132 fPathXform = pathMatrix;
133 }
134
Chris Dalton69669812021-07-27 10:00:12 -0600135 const GrPathXform& pathXform() const { return fPathXform; }
136
Chris Dalton50516f32021-07-18 22:26:27 -0600137 SK_ALWAYS_INLINE void writeFlatWedge(const GrShaderCaps& shaderCaps,
Chris Dalton17eaf622021-07-28 09:43:52 -0600138 SkPoint p0,
139 SkPoint p1,
Chris Daltond9bdc322021-06-01 19:22:05 -0600140 SkPoint midpoint) {
Chris Dalton17eaf622021-07-28 09:43:52 -0600141 if (GrVertexWriter vertexWriter = fChunker.appendVertex()) {
Chris Dalton69669812021-07-27 10:00:12 -0600142 fPathXform.mapLineToCubic(&vertexWriter, p0, p1);
Chris Daltond9bdc322021-06-01 19:22:05 -0600143 vertexWriter.write(midpoint);
Chris Dalton50516f32021-07-18 22:26:27 -0600144 vertexWriter.write(GrVertexWriter::If(!shaderCaps.infinitySupport(),
145 GrTessellationShader::kCubicCurveType));
Chris Daltond9bdc322021-06-01 19:22:05 -0600146 }
147 }
148
Chris Dalton50516f32021-07-18 22:26:27 -0600149 SK_ALWAYS_INLINE void writeQuadraticWedge(const GrShaderCaps& shaderCaps,
Chris Dalton17eaf622021-07-28 09:43:52 -0600150 const SkPoint p[3],
Chris Daltond9bdc322021-06-01 19:22:05 -0600151 SkPoint midpoint) {
Chris Dalton69669812021-07-27 10:00:12 -0600152 float numSegments_pow4 = GrWangsFormula::quadratic_pow4(kPrecision, p, fTotalVectorXform);
Chris Daltond2b8ba32021-06-09 00:12:59 -0600153 if (numSegments_pow4 > fMaxSegments_pow4) {
Chris Dalton17eaf622021-07-28 09:43:52 -0600154 this->chopAndWriteQuadraticWedges(shaderCaps, p, midpoint);
Chris Daltond9bdc322021-06-01 19:22:05 -0600155 return;
156 }
Chris Dalton17eaf622021-07-28 09:43:52 -0600157 if (GrVertexWriter vertexWriter = fChunker.appendVertex()) {
Chris Dalton69669812021-07-27 10:00:12 -0600158 fPathXform.mapQuadToCubic(&vertexWriter, p);
Chris Daltond9bdc322021-06-01 19:22:05 -0600159 vertexWriter.write(midpoint);
Chris Dalton50516f32021-07-18 22:26:27 -0600160 vertexWriter.write(GrVertexWriter::If(!shaderCaps.infinitySupport(),
161 GrTessellationShader::kCubicCurveType));
Chris Daltond9bdc322021-06-01 19:22:05 -0600162 }
Chris Daltond2b8ba32021-06-09 00:12:59 -0600163 fNumFixedSegments_pow4 = std::max(numSegments_pow4, fNumFixedSegments_pow4);
Chris Daltond9bdc322021-06-01 19:22:05 -0600164 }
165
Chris Dalton50516f32021-07-18 22:26:27 -0600166 SK_ALWAYS_INLINE void writeConicWedge(const GrShaderCaps& shaderCaps,
Chris Dalton17eaf622021-07-28 09:43:52 -0600167 const SkPoint p[3],
168 float w,
169 SkPoint midpoint) {
Chris Dalton69669812021-07-27 10:00:12 -0600170 float numSegments_pow2 = GrWangsFormula::conic_pow2(kPrecision, p, w, fTotalVectorXform);
171 if (numSegments_pow2 > fMaxSegments_pow2) {
Chris Dalton17eaf622021-07-28 09:43:52 -0600172 this->chopAndWriteConicWedges(shaderCaps, {p, w}, midpoint);
Chris Daltond9bdc322021-06-01 19:22:05 -0600173 return;
174 }
Chris Dalton17eaf622021-07-28 09:43:52 -0600175 if (GrVertexWriter vertexWriter = fChunker.appendVertex()) {
Chris Dalton69669812021-07-27 10:00:12 -0600176 fPathXform.mapConicToPatch(&vertexWriter, p, w);
Chris Daltond9bdc322021-06-01 19:22:05 -0600177 vertexWriter.write(midpoint);
Chris Dalton50516f32021-07-18 22:26:27 -0600178 vertexWriter.write(GrVertexWriter::If(!shaderCaps.infinitySupport(),
179 GrTessellationShader::kConicCurveType));
Chris Daltond9bdc322021-06-01 19:22:05 -0600180 }
Chris Daltond2b8ba32021-06-09 00:12:59 -0600181 fNumFixedSegments_pow4 = std::max(numSegments_pow2 * numSegments_pow2,
182 fNumFixedSegments_pow4);
Chris Daltond9bdc322021-06-01 19:22:05 -0600183 }
184
Chris Dalton50516f32021-07-18 22:26:27 -0600185 SK_ALWAYS_INLINE void writeCubicWedge(const GrShaderCaps& shaderCaps,
Chris Dalton17eaf622021-07-28 09:43:52 -0600186 const SkPoint p[4],
Chris Daltond9bdc322021-06-01 19:22:05 -0600187 SkPoint midpoint) {
Chris Dalton69669812021-07-27 10:00:12 -0600188 float numSegments_pow4 = GrWangsFormula::cubic_pow4(kPrecision, p, fTotalVectorXform);
Chris Daltond2b8ba32021-06-09 00:12:59 -0600189 if (numSegments_pow4 > fMaxSegments_pow4) {
Chris Dalton17eaf622021-07-28 09:43:52 -0600190 this->chopAndWriteCubicWedges(shaderCaps, p, midpoint);
Chris Daltond9bdc322021-06-01 19:22:05 -0600191 return;
192 }
Chris Dalton17eaf622021-07-28 09:43:52 -0600193 if (GrVertexWriter vertexWriter = fChunker.appendVertex()) {
Chris Dalton69669812021-07-27 10:00:12 -0600194 fPathXform.map4Points(&vertexWriter, p);
Chris Daltond9bdc322021-06-01 19:22:05 -0600195 vertexWriter.write(midpoint);
Chris Dalton50516f32021-07-18 22:26:27 -0600196 vertexWriter.write(GrVertexWriter::If(!shaderCaps.infinitySupport(),
197 GrTessellationShader::kCubicCurveType));
Chris Daltond9bdc322021-06-01 19:22:05 -0600198 }
Chris Daltond2b8ba32021-06-09 00:12:59 -0600199 fNumFixedSegments_pow4 = std::max(numSegments_pow4, fNumFixedSegments_pow4);
Chris Daltond9bdc322021-06-01 19:22:05 -0600200 }
201
Chris Daltond2b8ba32021-06-09 00:12:59 -0600202 int numFixedSegments_pow4() const { return fNumFixedSegments_pow4; }
203
Chris Daltond9bdc322021-06-01 19:22:05 -0600204private:
Chris Dalton17eaf622021-07-28 09:43:52 -0600205 void chopAndWriteQuadraticWedges(const GrShaderCaps& shaderCaps,
206 const SkPoint p[3],
207 SkPoint midpoint) {
Chris Daltond9bdc322021-06-01 19:22:05 -0600208 SkPoint chops[5];
209 SkChopQuadAtHalf(p, chops);
210 for (int i = 0; i < 2; ++i) {
211 const SkPoint* q = chops + i*2;
212 if (fCullTest.areVisible3(q)) {
Chris Dalton17eaf622021-07-28 09:43:52 -0600213 this->writeQuadraticWedge(shaderCaps, q, midpoint);
Chris Daltond9bdc322021-06-01 19:22:05 -0600214 } else {
Chris Dalton17eaf622021-07-28 09:43:52 -0600215 this->writeFlatWedge(shaderCaps, q[0], q[2], midpoint);
Chris Daltond9bdc322021-06-01 19:22:05 -0600216 }
217 }
218 }
219
Chris Dalton17eaf622021-07-28 09:43:52 -0600220 void chopAndWriteConicWedges(const GrShaderCaps& shaderCaps,
221 const SkConic& conic,
222 SkPoint midpoint) {
Chris Daltond9bdc322021-06-01 19:22:05 -0600223 SkConic chops[2];
224 if (!conic.chopAt(.5, chops)) {
225 return;
226 }
227 for (int i = 0; i < 2; ++i) {
228 if (fCullTest.areVisible3(chops[i].fPts)) {
Chris Dalton17eaf622021-07-28 09:43:52 -0600229 this->writeConicWedge(shaderCaps, chops[i].fPts, chops[i].fW, midpoint);
Chris Daltond9bdc322021-06-01 19:22:05 -0600230 } else {
Chris Dalton17eaf622021-07-28 09:43:52 -0600231 this->writeFlatWedge(shaderCaps, chops[i].fPts[0], chops[i].fPts[2], midpoint);
Chris Daltond9bdc322021-06-01 19:22:05 -0600232 }
233 }
234 }
235
Chris Dalton17eaf622021-07-28 09:43:52 -0600236 void chopAndWriteCubicWedges(const GrShaderCaps& shaderCaps,
237 const SkPoint p[4],
238 SkPoint midpoint) {
Chris Daltond9bdc322021-06-01 19:22:05 -0600239 SkPoint chops[7];
240 SkChopCubicAtHalf(p, chops);
241 for (int i = 0; i < 2; ++i) {
242 const SkPoint* c = chops + i*3;
243 if (fCullTest.areVisible4(c)) {
Chris Dalton17eaf622021-07-28 09:43:52 -0600244 this->writeCubicWedge(shaderCaps, c, midpoint);
Chris Daltond9bdc322021-06-01 19:22:05 -0600245 } else {
Chris Dalton17eaf622021-07-28 09:43:52 -0600246 this->writeFlatWedge(shaderCaps, c[0], c[3], midpoint);
Chris Daltond9bdc322021-06-01 19:22:05 -0600247 }
248 }
249 }
250
Chris Dalton17eaf622021-07-28 09:43:52 -0600251 GrVertexChunkBuilder fChunker;
Chris Daltond9bdc322021-06-01 19:22:05 -0600252 GrCullTest fCullTest;
Chris Dalton69669812021-07-27 10:00:12 -0600253 GrVectorXform fTotalVectorXform;
254 GrPathXform fPathXform;
Chris Daltond2b8ba32021-06-09 00:12:59 -0600255 const float fMaxSegments_pow2;
256 const float fMaxSegments_pow4;
257
258 // If using fixed count, this is the max number of curve segments we need to draw per instance.
259 float fNumFixedSegments_pow4 = 1;
Chris Daltond9bdc322021-06-01 19:22:05 -0600260};
261
262} // namespace
263
264
265GrPathTessellator* GrPathWedgeTessellator::Make(SkArenaAlloc* arena, const SkMatrix& viewMatrix,
Chris Daltond2b8ba32021-06-09 00:12:59 -0600266 const SkPMColor4f& color, int numPathVerbs,
Chris Dalton198ac152021-06-09 13:49:43 -0600267 const GrPipeline& pipeline, const GrCaps& caps) {
Chris Daltond2b8ba32021-06-09 00:12:59 -0600268 using PatchType = GrPathTessellationShader::PatchType;
269 GrPathTessellationShader* shader;
270 if (caps.shaderCaps()->tessellationSupport() &&
Chris Dalton50516f32021-07-18 22:26:27 -0600271 caps.shaderCaps()->infinitySupport() && // The hw tessellation shaders use infinity.
Brian Salomon66b500a2021-08-02 12:37:14 -0400272 !pipeline.usesLocalCoords() && // Our tessellation back door doesn't handle varyings.
Chris Daltond2b8ba32021-06-09 00:12:59 -0600273 numPathVerbs >= caps.minPathVerbsForHwTessellation()) {
274 shader = GrPathTessellationShader::MakeHardwareTessellationShader(arena, viewMatrix, color,
275 PatchType::kWedges);
276 } else {
Chris Dalton50516f32021-07-18 22:26:27 -0600277 shader = GrPathTessellationShader::MakeMiddleOutFixedCountShader(*caps.shaderCaps(), arena,
278 viewMatrix, color,
Chris Daltond2b8ba32021-06-09 00:12:59 -0600279 PatchType::kWedges);
280 }
281 return arena->make([=](void* objStart) {
282 return new(objStart) GrPathWedgeTessellator(shader);
283 });
Chris Daltond9bdc322021-06-01 19:22:05 -0600284}
285
Chris Daltone1f72372021-06-29 16:45:49 -0600286GR_DECLARE_STATIC_UNIQUE_KEY(gFixedCountVertexBufferKey);
287GR_DECLARE_STATIC_UNIQUE_KEY(gFixedCountIndexBufferKey);
288
Chris Dalton69669812021-07-27 10:00:12 -0600289void GrPathWedgeTessellator::prepare(GrMeshDrawTarget* target,
290 const SkRect& cullBounds,
Chris Dalton17eaf622021-07-28 09:43:52 -0600291 const PathDrawList& pathDrawList,
292 int totalCombinedPathVerbCnt) {
Chris Daltond9bdc322021-06-01 19:22:05 -0600293 SkASSERT(fVertexChunkArray.empty());
294
Chris Dalton50516f32021-07-18 22:26:27 -0600295 const GrShaderCaps& shaderCaps = *target->caps().shaderCaps();
296
Chris Daltond9bdc322021-06-01 19:22:05 -0600297 // Over-allocate enough wedges for 1 in 4 to chop.
Chris Dalton17eaf622021-07-28 09:43:52 -0600298 int maxWedges = MaxCombinedFanEdgesInPathDrawList(totalCombinedPathVerbCnt);
Chris Daltond9bdc322021-06-01 19:22:05 -0600299 int wedgeAllocCount = (maxWedges * 5 + 3) / 4; // i.e., ceil(maxWedges * 5/4)
300 if (!wedgeAllocCount) {
301 return;
302 }
Chris Dalton50516f32021-07-18 22:26:27 -0600303 size_t patchStride = fShader->willUseTessellationShaders() ? fShader->vertexStride() * 5
304 : fShader->instanceStride();
Chris Daltond9bdc322021-06-01 19:22:05 -0600305
Chris Daltond2b8ba32021-06-09 00:12:59 -0600306 int maxSegments;
307 if (fShader->willUseTessellationShaders()) {
Chris Dalton50516f32021-07-18 22:26:27 -0600308 maxSegments = shaderCaps.maxTessellationSegments();
Chris Daltond2b8ba32021-06-09 00:12:59 -0600309 } else {
Chris Daltone1f72372021-06-29 16:45:49 -0600310 maxSegments = GrPathTessellationShader::kMaxFixedCountSegments;
Chris Daltond2b8ba32021-06-09 00:12:59 -0600311 }
312
Chris Dalton17eaf622021-07-28 09:43:52 -0600313 WedgeWriter wedgeWriter(target, &fVertexChunkArray, patchStride, wedgeAllocCount, maxSegments);
314 for (auto [pathMatrix, path] : pathDrawList) {
315 wedgeWriter.setMatrices(cullBounds, fShader->viewMatrix(), pathMatrix);
316 MidpointContourParser parser(path);
317 while (parser.parseNextContour()) {
318 SkPoint midpoint = wedgeWriter.pathXform().mapPoint(parser.currentMidpoint());
319 SkPoint startPoint = {0, 0};
320 SkPoint lastPoint = startPoint;
321 for (auto [verb, pts, w] : parser.currentContour()) {
322 switch (verb) {
323 case SkPathVerb::kMove:
324 startPoint = lastPoint = pts[0];
325 break;
326 case SkPathVerb::kClose:
327 break; // Ignore. We can assume an implicit close at the end.
328 case SkPathVerb::kLine:
329 wedgeWriter.writeFlatWedge(shaderCaps, pts[0], pts[1], midpoint);
330 lastPoint = pts[1];
331 break;
332 case SkPathVerb::kQuad:
333 wedgeWriter.writeQuadraticWedge(shaderCaps, pts, midpoint);
334 lastPoint = pts[2];
335 break;
336 case SkPathVerb::kConic:
337 wedgeWriter.writeConicWedge(shaderCaps, pts, *w, midpoint);
338 lastPoint = pts[2];
339 break;
340 case SkPathVerb::kCubic:
341 wedgeWriter.writeCubicWedge(shaderCaps, pts, midpoint);
342 lastPoint = pts[3];
343 break;
344 }
Chris Daltond9bdc322021-06-01 19:22:05 -0600345 }
Chris Dalton17eaf622021-07-28 09:43:52 -0600346 if (lastPoint != startPoint) {
347 wedgeWriter.writeFlatWedge(shaderCaps, lastPoint, startPoint, midpoint);
348 }
Chris Daltond9bdc322021-06-01 19:22:05 -0600349 }
350 }
Chris Daltond2b8ba32021-06-09 00:12:59 -0600351
352 if (!fShader->willUseTessellationShaders()) {
353 // log2(n) == log16(n^4).
354 int fixedResolveLevel = GrWangsFormula::nextlog16(wedgeWriter.numFixedSegments_pow4());
355 int numCurveTriangles =
356 GrPathTessellationShader::NumCurveTrianglesAtResolveLevel(fixedResolveLevel);
357 // Emit 3 vertices per curve triangle, plus 3 more for the fan triangle.
Chris Daltone1f72372021-06-29 16:45:49 -0600358 fFixedIndexCount = numCurveTriangles * 3 + 3;
359
360 GR_DEFINE_STATIC_UNIQUE_KEY(gFixedCountVertexBufferKey);
361
362 fFixedCountVertexBuffer = target->resourceProvider()->findOrMakeStaticBuffer(
363 GrGpuBufferType::kVertex,
364 GrPathTessellationShader::SizeOfVertexBufferForMiddleOutWedges(),
365 gFixedCountVertexBufferKey,
366 GrPathTessellationShader::InitializeVertexBufferForMiddleOutWedges);
367
368 GR_DEFINE_STATIC_UNIQUE_KEY(gFixedCountIndexBufferKey);
369
370 fFixedCountIndexBuffer = target->resourceProvider()->findOrMakeStaticBuffer(
371 GrGpuBufferType::kIndex,
372 GrPathTessellationShader::SizeOfIndexBufferForMiddleOutWedges(),
373 gFixedCountIndexBufferKey,
374 GrPathTessellationShader::InitializeIndexBufferForMiddleOutWedges);
Chris Daltond2b8ba32021-06-09 00:12:59 -0600375 }
Chris Daltond9bdc322021-06-01 19:22:05 -0600376}
377
Robert Phillipsf0d21cf2021-08-13 10:26:37 -0400378#if SK_GPU_V1
379#include "src/gpu/GrOpFlushState.h"
380
Chris Daltond9bdc322021-06-01 19:22:05 -0600381void GrPathWedgeTessellator::draw(GrOpFlushState* flushState) const {
Chris Daltond2b8ba32021-06-09 00:12:59 -0600382 if (fShader->willUseTessellationShaders()) {
383 for (const GrVertexChunk& chunk : fVertexChunkArray) {
384 flushState->bindBuffers(nullptr, nullptr, chunk.fBuffer);
385 flushState->draw(chunk.fCount * 5, chunk.fBase * 5);
386 }
387 } else {
388 SkASSERT(fShader->hasInstanceAttributes());
389 for (const GrVertexChunk& chunk : fVertexChunkArray) {
Chris Daltone1f72372021-06-29 16:45:49 -0600390 flushState->bindBuffers(fFixedCountIndexBuffer, chunk.fBuffer, fFixedCountVertexBuffer);
391 flushState->drawIndexedInstanced(fFixedIndexCount, 0, chunk.fCount, chunk.fBase, 0);
Chris Daltond2b8ba32021-06-09 00:12:59 -0600392 }
Chris Daltond9bdc322021-06-01 19:22:05 -0600393 }
394}
Robert Phillipsf0d21cf2021-08-13 10:26:37 -0400395#endif