| 1 | /* | 
|---|
| 2 | * Copyright 2018 Google Inc. | 
|---|
| 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/ccpr/GrCCStroker.h" | 
|---|
| 9 |  | 
|---|
| 10 | #include "include/core/SkStrokeRec.h" | 
|---|
| 11 | #include "src/core/SkPathPriv.h" | 
|---|
| 12 | #include "src/gpu/GrOnFlushResourceProvider.h" | 
|---|
| 13 | #include "src/gpu/GrOpsRenderPass.h" | 
|---|
| 14 | #include "src/gpu/GrProgramInfo.h" | 
|---|
| 15 | #include "src/gpu/ccpr/GrAutoMapVertexBuffer.h" | 
|---|
| 16 | #include "src/gpu/ccpr/GrCCCoverageProcessor.h" | 
|---|
| 17 | #include "src/gpu/glsl/GrGLSLFragmentShaderBuilder.h" | 
|---|
| 18 | #include "src/gpu/glsl/GrGLSLVertexGeoBuilder.h" | 
|---|
| 19 |  | 
|---|
| 20 | static constexpr int kMaxNumLinearSegmentsLog2 = GrCCStrokeGeometry::kMaxNumLinearSegmentsLog2; | 
|---|
| 21 | using TriangleInstance = GrCCCoverageProcessor::TriPointInstance; | 
|---|
| 22 | using ConicInstance = GrCCCoverageProcessor::QuadPointInstance; | 
|---|
| 23 |  | 
|---|
| 24 | namespace { | 
|---|
| 25 |  | 
|---|
| 26 | struct LinearStrokeInstance { | 
|---|
| 27 | float fEndpoints[4]; | 
|---|
| 28 | float fStrokeRadius; | 
|---|
| 29 |  | 
|---|
| 30 | inline void set(const SkPoint[2], float dx, float dy, float strokeRadius); | 
|---|
| 31 | }; | 
|---|
| 32 |  | 
|---|
| 33 | inline void LinearStrokeInstance::set(const SkPoint P[2], float dx, float dy, float strokeRadius) { | 
|---|
| 34 | Sk2f X, Y; | 
|---|
| 35 | Sk2f::Load2(P, &X, &Y); | 
|---|
| 36 | Sk2f::Store2(fEndpoints, X + dx, Y + dy); | 
|---|
| 37 | fStrokeRadius = strokeRadius; | 
|---|
| 38 | } | 
|---|
| 39 |  | 
|---|
| 40 | struct CubicStrokeInstance { | 
|---|
| 41 | float fX[4]; | 
|---|
| 42 | float fY[4]; | 
|---|
| 43 | float fStrokeRadius; | 
|---|
| 44 | float fNumSegments; | 
|---|
| 45 |  | 
|---|
| 46 | inline void set(const SkPoint[4], float dx, float dy, float strokeRadius, int numSegments); | 
|---|
| 47 | inline void set(const Sk4f& X, const Sk4f& Y, float dx, float dy, float strokeRadius, | 
|---|
| 48 | int numSegments); | 
|---|
| 49 | }; | 
|---|
| 50 |  | 
|---|
| 51 | inline void CubicStrokeInstance::set(const SkPoint P[4], float dx, float dy, float strokeRadius, | 
|---|
| 52 | int numSegments) { | 
|---|
| 53 | Sk4f X, Y; | 
|---|
| 54 | Sk4f::Load2(P, &X, &Y); | 
|---|
| 55 | this->set(X, Y, dx, dy, strokeRadius, numSegments); | 
|---|
| 56 | } | 
|---|
| 57 |  | 
|---|
| 58 | inline void CubicStrokeInstance::set(const Sk4f& X, const Sk4f& Y, float dx, float dy, | 
|---|
| 59 | float strokeRadius, int numSegments) { | 
|---|
| 60 | (X + dx).store(&fX); | 
|---|
| 61 | (Y + dy).store(&fY); | 
|---|
| 62 | fStrokeRadius = strokeRadius; | 
|---|
| 63 | fNumSegments = static_cast<float>(numSegments); | 
|---|
| 64 | } | 
|---|
| 65 |  | 
|---|
| 66 | // This class draws stroked lines in post-transform device space (a.k.a. rectangles). Rigid-body | 
|---|
| 67 | // transforms can be achieved by transforming the line ahead of time and adjusting the stroke | 
|---|
| 68 | // width. Skews of the stroke itself are not yet supported. | 
|---|
| 69 | // | 
|---|
| 70 | // Corner coverage is AA-correct, meaning, n^2 attenuation along the diagonals. This is important | 
|---|
| 71 | // for seamless integration with the connecting geometry. | 
|---|
| 72 | class LinearStrokeProcessor : public GrGeometryProcessor { | 
|---|
| 73 | public: | 
|---|
| 74 | LinearStrokeProcessor() : INHERITED(kLinearStrokeProcessor_ClassID) { | 
|---|
| 75 | this->setInstanceAttributes(kInstanceAttribs, 2); | 
|---|
| 76 | #ifdef SK_DEBUG | 
|---|
| 77 | using Instance = LinearStrokeInstance; | 
|---|
| 78 | SkASSERT(this->instanceStride() == sizeof(Instance)); | 
|---|
| 79 | #endif | 
|---|
| 80 | } | 
|---|
| 81 |  | 
|---|
| 82 | private: | 
|---|
| 83 | const char* name() const override { return "LinearStrokeProcessor"; } | 
|---|
| 84 | void getGLSLProcessorKey(const GrShaderCaps&, GrProcessorKeyBuilder*) const override {} | 
|---|
| 85 |  | 
|---|
| 86 | static constexpr Attribute kInstanceAttribs[2] = { | 
|---|
| 87 | { "endpts", kFloat4_GrVertexAttribType, kFloat4_GrSLType}, | 
|---|
| 88 | { "stroke_radius", kFloat_GrVertexAttribType, kFloat_GrSLType} | 
|---|
| 89 | }; | 
|---|
| 90 |  | 
|---|
| 91 | class Impl : public GrGLSLGeometryProcessor { | 
|---|
| 92 | void setData(const GrGLSLProgramDataManager&, const GrPrimitiveProcessor&) override {} | 
|---|
| 93 | void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override; | 
|---|
| 94 | }; | 
|---|
| 95 |  | 
|---|
| 96 | GrGLSLPrimitiveProcessor* createGLSLInstance(const GrShaderCaps&) const override { | 
|---|
| 97 | return new Impl(); | 
|---|
| 98 | } | 
|---|
| 99 |  | 
|---|
| 100 | typedef GrGeometryProcessor INHERITED; | 
|---|
| 101 | }; | 
|---|
| 102 |  | 
|---|
| 103 | void LinearStrokeProcessor::Impl::onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) { | 
|---|
| 104 | GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler; | 
|---|
| 105 |  | 
|---|
| 106 | varyingHandler->emitAttributes(args.fGP.cast<LinearStrokeProcessor>()); | 
|---|
| 107 |  | 
|---|
| 108 | GrGLSLVertexBuilder* v = args.fVertBuilder; | 
|---|
| 109 | v->codeAppend ( "float2 tan = normalize(endpts.zw - endpts.xy);"); | 
|---|
| 110 | v->codeAppend ( "float2 n = float2(tan.y, -tan.x);"); | 
|---|
| 111 | v->codeAppend ( "float nwidth = abs(n.x) + abs(n.y);"); | 
|---|
| 112 |  | 
|---|
| 113 | // Outset the vertex position for AA butt caps. | 
|---|
| 114 | v->codeAppend ( "float2 outset = tan*nwidth/2;"); | 
|---|
| 115 | v->codeAppend ( "float2 position = (sk_VertexID < 2) " | 
|---|
| 116 | "? endpts.xy - outset : endpts.zw + outset;"); | 
|---|
| 117 |  | 
|---|
| 118 | // Calculate Manhattan distance from both butt caps, where distance=0 on the actual endpoint and | 
|---|
| 119 | // distance=-.5 on the outset edge. | 
|---|
| 120 | GrGLSLVarying edgeDistances(kFloat4_GrSLType); | 
|---|
| 121 | varyingHandler->addVarying( "edge_distances", &edgeDistances); | 
|---|
| 122 | v->codeAppendf( "%s.xz = float2(-.5, dot(endpts.zw - endpts.xy, tan) / nwidth + .5);", | 
|---|
| 123 | edgeDistances.vsOut()); | 
|---|
| 124 | v->codeAppendf( "%s.xz = (sk_VertexID < 2) ? %s.xz : %s.zx;", | 
|---|
| 125 | edgeDistances.vsOut(), edgeDistances.vsOut(), edgeDistances.vsOut()); | 
|---|
| 126 |  | 
|---|
| 127 | // Outset the vertex position for stroke radius plus edge AA. | 
|---|
| 128 | v->codeAppend ( "outset = n * (stroke_radius + nwidth/2);"); | 
|---|
| 129 | v->codeAppend ( "position += (0 == (sk_VertexID & 1)) ? +outset : -outset;"); | 
|---|
| 130 |  | 
|---|
| 131 | // Calculate Manhattan distance from both edges, where distance=0 on the actual edge and | 
|---|
| 132 | // distance=-.5 on the outset. | 
|---|
| 133 | v->codeAppendf( "%s.yw = float2(-.5, 2*stroke_radius / nwidth + .5);", edgeDistances.vsOut()); | 
|---|
| 134 | v->codeAppendf( "%s.yw = (0 == (sk_VertexID & 1)) ? %s.yw : %s.wy;", | 
|---|
| 135 | edgeDistances.vsOut(), edgeDistances.vsOut(), edgeDistances.vsOut()); | 
|---|
| 136 |  | 
|---|
| 137 | gpArgs->fPositionVar.set(kFloat2_GrSLType, "position"); | 
|---|
| 138 | // Leave fLocalCoordVar uninitialized; this GP is not combined with frag processors | 
|---|
| 139 |  | 
|---|
| 140 | // Use the 4 edge distances to calculate coverage in the fragment shader. | 
|---|
| 141 | GrGLSLFPFragmentBuilder* f = args.fFragBuilder; | 
|---|
| 142 | f->codeAppendf( "half2 coverages = half2(min(%s.xy, .5) + min(%s.zw, .5));", | 
|---|
| 143 | edgeDistances.fsIn(), edgeDistances.fsIn()); | 
|---|
| 144 | f->codeAppendf( "%s = half4(coverages.x * coverages.y);", args.fOutputColor); | 
|---|
| 145 |  | 
|---|
| 146 | // This shader doesn't use the built-in Ganesh coverage. | 
|---|
| 147 | f->codeAppendf( "%s = half4(1);", args.fOutputCoverage); | 
|---|
| 148 | } | 
|---|
| 149 |  | 
|---|
| 150 | constexpr GrPrimitiveProcessor::Attribute LinearStrokeProcessor::kInstanceAttribs[]; | 
|---|
| 151 |  | 
|---|
| 152 | // This class draws stroked cubics in post-transform device space. Rigid-body transforms can be | 
|---|
| 153 | // achieved by transforming the curve ahead of time and adjusting the stroke width. Skews of the | 
|---|
| 154 | // stroke itself are not yet supported. Quadratics can be drawn by converting them to cubics. | 
|---|
| 155 | // | 
|---|
| 156 | // This class works by finding stroke-width line segments orthogonal to the curve at a | 
|---|
| 157 | // pre-determined number of evenly spaced points along the curve (evenly spaced in the parametric | 
|---|
| 158 | // sense). It then connects the segments with a triangle strip. As for common in CCPR, clockwise- | 
|---|
| 159 | // winding triangles from the strip emit positive coverage, counter-clockwise triangles emit | 
|---|
| 160 | // negative, and we use SkBlendMode::kPlus. | 
|---|
| 161 | class CubicStrokeProcessor : public GrGeometryProcessor { | 
|---|
| 162 | public: | 
|---|
| 163 | CubicStrokeProcessor() : GrGeometryProcessor(kCubicStrokeProcessor_ClassID) { | 
|---|
| 164 | this->setInstanceAttributes(kInstanceAttribs, 3); | 
|---|
| 165 | #ifdef SK_DEBUG | 
|---|
| 166 | using Instance = CubicStrokeInstance; | 
|---|
| 167 | SkASSERT(this->instanceStride() == sizeof(Instance)); | 
|---|
| 168 | #endif | 
|---|
| 169 | } | 
|---|
| 170 |  | 
|---|
| 171 | private: | 
|---|
| 172 | const char* name() const override { return "CubicStrokeProcessor"; } | 
|---|
| 173 | void getGLSLProcessorKey(const GrShaderCaps&, GrProcessorKeyBuilder*) const override {} | 
|---|
| 174 |  | 
|---|
| 175 | static constexpr Attribute kInstanceAttribs[3] = { | 
|---|
| 176 | { "X", kFloat4_GrVertexAttribType, kFloat4_GrSLType}, | 
|---|
| 177 | { "Y", kFloat4_GrVertexAttribType, kFloat4_GrSLType}, | 
|---|
| 178 | { "stroke_info", kFloat2_GrVertexAttribType, kFloat2_GrSLType} | 
|---|
| 179 | }; | 
|---|
| 180 |  | 
|---|
| 181 | class Impl : public GrGLSLGeometryProcessor { | 
|---|
| 182 | void setData(const GrGLSLProgramDataManager&, const GrPrimitiveProcessor&) override {} | 
|---|
| 183 | void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override; | 
|---|
| 184 | }; | 
|---|
| 185 |  | 
|---|
| 186 | GrGLSLPrimitiveProcessor* createGLSLInstance(const GrShaderCaps&) const override { | 
|---|
| 187 | return new Impl(); | 
|---|
| 188 | } | 
|---|
| 189 | }; | 
|---|
| 190 |  | 
|---|
| 191 | void CubicStrokeProcessor::Impl::onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) { | 
|---|
| 192 | GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler; | 
|---|
| 193 |  | 
|---|
| 194 | varyingHandler->emitAttributes(args.fGP.cast<CubicStrokeProcessor>()); | 
|---|
| 195 |  | 
|---|
| 196 | GrGLSLVertexBuilder* v = args.fVertBuilder; | 
|---|
| 197 | v->codeAppend ( "float4x2 P = transpose(float2x4(X, Y));"); | 
|---|
| 198 | v->codeAppend ( "float stroke_radius = stroke_info[0];"); | 
|---|
| 199 | v->codeAppend ( "float num_segments = stroke_info[1];"); | 
|---|
| 200 |  | 
|---|
| 201 | // Find the parametric T value at which we will emit our orthogonal line segment. We emit two | 
|---|
| 202 | // line segments at T=0 and double at T=1 as well for AA butt caps. | 
|---|
| 203 | v->codeAppend ( "float point_id = float(sk_VertexID/2);"); | 
|---|
| 204 | v->codeAppend ( "float T = max((point_id - 1) / num_segments, 0);"); | 
|---|
| 205 | v->codeAppend ( "T = (point_id >= num_segments + 1) ? 1 : T;");  // In case x/x !== 1. | 
|---|
| 206 |  | 
|---|
| 207 | // Use De Casteljau's algorithm to find the position and tangent for our orthogonal line | 
|---|
| 208 | // segment. De Casteljau's is more numerically stable than evaluating the curve and derivative | 
|---|
| 209 | // directly. | 
|---|
| 210 | v->codeAppend ( "float2 ab = mix(P[0], P[1], T);"); | 
|---|
| 211 | v->codeAppend ( "float2 bc = mix(P[1], P[2], T);"); | 
|---|
| 212 | v->codeAppend ( "float2 cd = mix(P[2], P[3], T);"); | 
|---|
| 213 | v->codeAppend ( "float2 abc = mix(ab, bc, T);"); | 
|---|
| 214 | v->codeAppend ( "float2 bcd = mix(bc, cd, T);"); | 
|---|
| 215 | v->codeAppend ( "float2 position = mix(abc, bcd, T);"); | 
|---|
| 216 | v->codeAppend ( "float2 tan = bcd - abc;"); | 
|---|
| 217 |  | 
|---|
| 218 | // Find actual tangents for the corner cases when De Casteljau's yields tan=0. (We shouldn't | 
|---|
| 219 | // encounter other numerically unstable cases where tan ~= 0, because GrCCStrokeGeometry snaps | 
|---|
| 220 | // control points to endpoints in curves where they are almost equal.) | 
|---|
| 221 | v->codeAppend ( "if (0 == T && P[0] == P[1]) {"); | 
|---|
| 222 | v->codeAppend ( "tan = P[2] - P[0];"); | 
|---|
| 223 | v->codeAppend ( "}"); | 
|---|
| 224 | v->codeAppend ( "if (1 == T && P[2] == P[3]) {"); | 
|---|
| 225 | v->codeAppend ( "tan = P[3] - P[1];"); | 
|---|
| 226 | v->codeAppend ( "}"); | 
|---|
| 227 | v->codeAppend ( "tan = normalize(tan);"); | 
|---|
| 228 | v->codeAppend ( "float2 n = float2(tan.y, -tan.x);"); | 
|---|
| 229 | v->codeAppend ( "float nwidth = abs(n.x) + abs(n.y);"); | 
|---|
| 230 |  | 
|---|
| 231 | // Outset the vertex position for stroke radius plus edge AA. | 
|---|
| 232 | v->codeAppend ( "float2 outset = n * (stroke_radius + nwidth/2);"); | 
|---|
| 233 | v->codeAppend ( "position += (0 == (sk_VertexID & 1)) ? -outset : +outset;"); | 
|---|
| 234 |  | 
|---|
| 235 | // Calculate the Manhattan distance from both edges, where distance=0 on the actual edge and | 
|---|
| 236 | // distance=-.5 on the outset. | 
|---|
| 237 | GrGLSLVarying coverages(kFloat3_GrSLType); | 
|---|
| 238 | varyingHandler->addVarying( "coverages", &coverages); | 
|---|
| 239 | v->codeAppendf( "%s.xy = float2(-.5, 2*stroke_radius / nwidth + .5);", coverages.vsOut()); | 
|---|
| 240 | v->codeAppendf( "%s.xy = (0 == (sk_VertexID & 1)) ? %s.xy : %s.yx;", | 
|---|
| 241 | coverages.vsOut(), coverages.vsOut(), coverages.vsOut()); | 
|---|
| 242 |  | 
|---|
| 243 | // Adjust the orthogonal line segments on the endpoints so they straddle the actual endpoint | 
|---|
| 244 | // at a Manhattan distance of .5 on either side. | 
|---|
| 245 | v->codeAppend ( "if (0 == point_id || num_segments+1 == point_id) {"); | 
|---|
| 246 | v->codeAppend ( "position -= tan*nwidth/2;"); | 
|---|
| 247 | v->codeAppend ( "}"); | 
|---|
| 248 | v->codeAppend ( "if (1 == point_id || num_segments+2 == point_id) {"); | 
|---|
| 249 | v->codeAppend ( "position += tan*nwidth/2;"); | 
|---|
| 250 | v->codeAppend ( "}"); | 
|---|
| 251 |  | 
|---|
| 252 | // Interpolate coverage for butt cap AA from 0 on the outer segment to 1 on the inner. | 
|---|
| 253 | v->codeAppendf( "%s.z = (0 == point_id || num_segments+2 == point_id) ? 0 : 1;", | 
|---|
| 254 | coverages.vsOut()); | 
|---|
| 255 |  | 
|---|
| 256 | gpArgs->fPositionVar.set(kFloat2_GrSLType, "position"); | 
|---|
| 257 | // Leave fLocalCoordVar uninitialized; this GP is not combined with frag processors | 
|---|
| 258 |  | 
|---|
| 259 | // Use the 2 edge distances and interpolated butt cap AA to calculate fragment coverage. | 
|---|
| 260 | GrGLSLFPFragmentBuilder* f = args.fFragBuilder; | 
|---|
| 261 | f->codeAppendf( "half2 edge_coverages = min(half2(%s.xy), .5);", coverages.fsIn()); | 
|---|
| 262 | f->codeAppend ( "half coverage = edge_coverages.x + edge_coverages.y;"); | 
|---|
| 263 | f->codeAppendf( "coverage *= half(%s.z);", coverages.fsIn());  // Butt cap AA. | 
|---|
| 264 |  | 
|---|
| 265 | // As is common for CCPR, clockwise-winding triangles from the strip emit positive coverage, and | 
|---|
| 266 | // counter-clockwise triangles emit negative. | 
|---|
| 267 | f->codeAppendf( "%s = half4(sk_Clockwise ? +coverage : -coverage);", args.fOutputColor); | 
|---|
| 268 |  | 
|---|
| 269 | // This shader doesn't use the built-in Ganesh coverage. | 
|---|
| 270 | f->codeAppendf( "%s = half4(1);", args.fOutputCoverage); | 
|---|
| 271 | } | 
|---|
| 272 |  | 
|---|
| 273 | constexpr GrPrimitiveProcessor::Attribute CubicStrokeProcessor::kInstanceAttribs[]; | 
|---|
| 274 |  | 
|---|
| 275 | }  // anonymous namespace | 
|---|
| 276 |  | 
|---|
| 277 | void GrCCStroker::parseDeviceSpaceStroke(const SkPath& path, const SkPoint* deviceSpacePts, | 
|---|
| 278 | const SkStrokeRec& stroke, float strokeDevWidth, | 
|---|
| 279 | GrScissorTest scissorTest, | 
|---|
| 280 | const SkIRect& clippedDevIBounds, | 
|---|
| 281 | const SkIVector& devToAtlasOffset) { | 
|---|
| 282 | SkASSERT(SkStrokeRec::kStroke_Style == stroke.getStyle() || | 
|---|
| 283 | SkStrokeRec::kHairline_Style == stroke.getStyle()); | 
|---|
| 284 | SkASSERT(!fInstanceBuffer); | 
|---|
| 285 | SkASSERT(!path.isEmpty()); | 
|---|
| 286 |  | 
|---|
| 287 | if (!fHasOpenBatch) { | 
|---|
| 288 | fBatches.emplace_back(&fTalliesAllocator, *fInstanceCounts[(int)GrScissorTest::kDisabled], | 
|---|
| 289 | fScissorSubBatches.count()); | 
|---|
| 290 | fInstanceCounts[(int)GrScissorTest::kDisabled] = fBatches.back().fNonScissorEndInstances; | 
|---|
| 291 | fHasOpenBatch = true; | 
|---|
| 292 | } | 
|---|
| 293 |  | 
|---|
| 294 | InstanceTallies* currStrokeEndIndices; | 
|---|
| 295 | if (GrScissorTest::kEnabled == scissorTest) { | 
|---|
| 296 | SkASSERT(fBatches.back().fEndScissorSubBatch == fScissorSubBatches.count()); | 
|---|
| 297 | fScissorSubBatches.emplace_back(&fTalliesAllocator, | 
|---|
| 298 | *fInstanceCounts[(int)GrScissorTest::kEnabled], | 
|---|
| 299 | clippedDevIBounds.makeOffset(devToAtlasOffset)); | 
|---|
| 300 | fBatches.back().fEndScissorSubBatch = fScissorSubBatches.count(); | 
|---|
| 301 | fInstanceCounts[(int)GrScissorTest::kEnabled] = | 
|---|
| 302 | currStrokeEndIndices = fScissorSubBatches.back().fEndInstances; | 
|---|
| 303 | } else { | 
|---|
| 304 | currStrokeEndIndices = fBatches.back().fNonScissorEndInstances; | 
|---|
| 305 | } | 
|---|
| 306 |  | 
|---|
| 307 | fGeometry.beginPath(stroke, strokeDevWidth, currStrokeEndIndices); | 
|---|
| 308 |  | 
|---|
| 309 | fPathInfos.push_back() = {devToAtlasOffset, strokeDevWidth/2, scissorTest}; | 
|---|
| 310 |  | 
|---|
| 311 | int devPtsIdx = 0; | 
|---|
| 312 | SkPath::Verb previousVerb = SkPath::kClose_Verb; | 
|---|
| 313 |  | 
|---|
| 314 | for (SkPath::Verb verb : SkPathPriv::Verbs(path)) { | 
|---|
| 315 | SkASSERT(SkPath::kDone_Verb != previousVerb); | 
|---|
| 316 | const SkPoint* P = &deviceSpacePts[devPtsIdx - 1]; | 
|---|
| 317 | switch (verb) { | 
|---|
| 318 | case SkPath::kMove_Verb: | 
|---|
| 319 | if (devPtsIdx > 0 && SkPath::kClose_Verb != previousVerb) { | 
|---|
| 320 | fGeometry.capContourAndExit(); | 
|---|
| 321 | } | 
|---|
| 322 | fGeometry.moveTo(deviceSpacePts[devPtsIdx]); | 
|---|
| 323 | ++devPtsIdx; | 
|---|
| 324 | break; | 
|---|
| 325 | case SkPath::kClose_Verb: | 
|---|
| 326 | SkASSERT(SkPath::kClose_Verb != previousVerb); | 
|---|
| 327 | fGeometry.closeContour(); | 
|---|
| 328 | break; | 
|---|
| 329 | case SkPath::kLine_Verb: | 
|---|
| 330 | SkASSERT(SkPath::kClose_Verb != previousVerb); | 
|---|
| 331 | fGeometry.lineTo(P[1]); | 
|---|
| 332 | ++devPtsIdx; | 
|---|
| 333 | break; | 
|---|
| 334 | case SkPath::kQuad_Verb: | 
|---|
| 335 | SkASSERT(SkPath::kClose_Verb != previousVerb); | 
|---|
| 336 | fGeometry.quadraticTo(P); | 
|---|
| 337 | devPtsIdx += 2; | 
|---|
| 338 | break; | 
|---|
| 339 | case SkPath::kCubic_Verb: { | 
|---|
| 340 | SkASSERT(SkPath::kClose_Verb != previousVerb); | 
|---|
| 341 | fGeometry.cubicTo(P); | 
|---|
| 342 | devPtsIdx += 3; | 
|---|
| 343 | break; | 
|---|
| 344 | } | 
|---|
| 345 | case SkPath::kConic_Verb: | 
|---|
| 346 | SkASSERT(SkPath::kClose_Verb != previousVerb); | 
|---|
| 347 | SK_ABORT( "Stroked conics not supported."); | 
|---|
| 348 | break; | 
|---|
| 349 | case SkPath::kDone_Verb: | 
|---|
| 350 | break; | 
|---|
| 351 | } | 
|---|
| 352 | previousVerb = verb; | 
|---|
| 353 | } | 
|---|
| 354 |  | 
|---|
| 355 | if (devPtsIdx > 0 && SkPath::kClose_Verb != previousVerb) { | 
|---|
| 356 | fGeometry.capContourAndExit(); | 
|---|
| 357 | } | 
|---|
| 358 | } | 
|---|
| 359 |  | 
|---|
| 360 | // This class encapsulates the process of expanding ready-to-draw geometry from GrCCStrokeGeometry | 
|---|
| 361 | // directly into GPU instance buffers. | 
|---|
| 362 | class GrCCStroker::InstanceBufferBuilder { | 
|---|
| 363 | public: | 
|---|
| 364 | InstanceBufferBuilder(GrOnFlushResourceProvider* onFlushRP, GrCCStroker* stroker) { | 
|---|
| 365 | memcpy(fNextInstances, stroker->fBaseInstances, sizeof(fNextInstances)); | 
|---|
| 366 | #ifdef SK_DEBUG | 
|---|
| 367 | fEndInstances[0] = stroker->fBaseInstances[0] + *stroker->fInstanceCounts[0]; | 
|---|
| 368 | fEndInstances[1] = stroker->fBaseInstances[1] + *stroker->fInstanceCounts[1]; | 
|---|
| 369 | #endif | 
|---|
| 370 |  | 
|---|
| 371 | int endConicsIdx = stroker->fBaseInstances[1].fConics + | 
|---|
| 372 | stroker->fInstanceCounts[1]->fConics; | 
|---|
| 373 | fInstanceBuffer.resetAndMapBuffer(onFlushRP, endConicsIdx * sizeof(ConicInstance)); | 
|---|
| 374 | if (!fInstanceBuffer.hasGpuBuffer()) { | 
|---|
| 375 | SkDebugf( "WARNING: failed to allocate CCPR stroke instance buffer.\n"); | 
|---|
| 376 | return; | 
|---|
| 377 | } | 
|---|
| 378 | } | 
|---|
| 379 |  | 
|---|
| 380 | bool isMapped() const { return fInstanceBuffer.isMapped(); } | 
|---|
| 381 |  | 
|---|
| 382 | void updateCurrentInfo(const PathInfo& pathInfo) { | 
|---|
| 383 | SkASSERT(this->isMapped()); | 
|---|
| 384 | fCurrDX = static_cast<float>(pathInfo.fDevToAtlasOffset.x()); | 
|---|
| 385 | fCurrDY = static_cast<float>(pathInfo.fDevToAtlasOffset.y()); | 
|---|
| 386 | fCurrStrokeRadius = pathInfo.fStrokeRadius; | 
|---|
| 387 | fCurrNextInstances = &fNextInstances[(int)pathInfo.fScissorTest]; | 
|---|
| 388 | SkDEBUGCODE(fCurrEndInstances = &fEndInstances[(int)pathInfo.fScissorTest]); | 
|---|
| 389 | } | 
|---|
| 390 |  | 
|---|
| 391 | void appendLinearStroke(const SkPoint endpts[2]) { | 
|---|
| 392 | SkASSERT(this->isMapped()); | 
|---|
| 393 | this->appendLinearStrokeInstance().set(endpts, fCurrDX, fCurrDY, fCurrStrokeRadius); | 
|---|
| 394 | } | 
|---|
| 395 |  | 
|---|
| 396 | void appendQuadraticStroke(const SkPoint P[3], int numLinearSegmentsLog2) { | 
|---|
| 397 | SkASSERT(this->isMapped()); | 
|---|
| 398 | SkASSERT(numLinearSegmentsLog2 > 0); | 
|---|
| 399 |  | 
|---|
| 400 | Sk4f ptsT[2]; | 
|---|
| 401 | Sk2f p0 = Sk2f::Load(P); | 
|---|
| 402 | Sk2f p1 = Sk2f::Load(P+1); | 
|---|
| 403 | Sk2f p2 = Sk2f::Load(P+2); | 
|---|
| 404 |  | 
|---|
| 405 | // Convert the quadratic to cubic. | 
|---|
| 406 | Sk2f c1 = SkNx_fma(Sk2f(2/3.f), p1 - p0, p0); | 
|---|
| 407 | Sk2f c2 = SkNx_fma(Sk2f(1/3.f), p2 - p1, p1); | 
|---|
| 408 | Sk2f::Store4(ptsT, p0, c1, c2, p2); | 
|---|
| 409 |  | 
|---|
| 410 | this->appendCubicStrokeInstance(numLinearSegmentsLog2).set( | 
|---|
| 411 | ptsT[0], ptsT[1], fCurrDX, fCurrDY, fCurrStrokeRadius, 1 << numLinearSegmentsLog2); | 
|---|
| 412 | } | 
|---|
| 413 |  | 
|---|
| 414 | void appendCubicStroke(const SkPoint P[3], int numLinearSegmentsLog2) { | 
|---|
| 415 | SkASSERT(this->isMapped()); | 
|---|
| 416 | SkASSERT(numLinearSegmentsLog2 > 0); | 
|---|
| 417 | this->appendCubicStrokeInstance(numLinearSegmentsLog2).set( | 
|---|
| 418 | P, fCurrDX, fCurrDY, fCurrStrokeRadius, 1 << numLinearSegmentsLog2); | 
|---|
| 419 | } | 
|---|
| 420 |  | 
|---|
| 421 | void appendJoin(Verb joinVerb, const SkPoint& center, const SkVector& leftNorm, | 
|---|
| 422 | const SkVector& rightNorm, float miterCapHeightOverWidth, float conicWeight) { | 
|---|
| 423 | SkASSERT(this->isMapped()); | 
|---|
| 424 |  | 
|---|
| 425 | Sk2f offset = Sk2f::Load(¢er) + Sk2f(fCurrDX, fCurrDY); | 
|---|
| 426 | Sk2f n0 = Sk2f::Load(&leftNorm); | 
|---|
| 427 | Sk2f n1 = Sk2f::Load(&rightNorm); | 
|---|
| 428 |  | 
|---|
| 429 | // Identify the outer edge. | 
|---|
| 430 | Sk2f cross = n0 * SkNx_shuffle<1,0>(n1); | 
|---|
| 431 | if (cross[0] < cross[1]) { | 
|---|
| 432 | Sk2f tmp = n0; | 
|---|
| 433 | n0 = -n1; | 
|---|
| 434 | n1 = -tmp; | 
|---|
| 435 | } | 
|---|
| 436 |  | 
|---|
| 437 | if (!GrCCStrokeGeometry::IsInternalJoinVerb(joinVerb)) { | 
|---|
| 438 | // Normal joins are a triangle that connects the outer corners of two adjoining strokes. | 
|---|
| 439 | this->appendTriangleInstance().set( | 
|---|
| 440 | n1 * fCurrStrokeRadius, Sk2f(0, 0), n0 * fCurrStrokeRadius, offset, | 
|---|
| 441 | TriangleInstance::Ordering::kXYTransposed); | 
|---|
| 442 | if (Verb::kBevelJoin == joinVerb) { | 
|---|
| 443 | return; | 
|---|
| 444 | } | 
|---|
| 445 | } else { | 
|---|
| 446 | // Internal joins are coverage-counted, self-intersecting quadrilaterals that tie the | 
|---|
| 447 | // four corners of two adjoining strokes together a like a shoelace. Coverage is | 
|---|
| 448 | // negative on the inside half. We implement this geometry with a pair of triangles. | 
|---|
| 449 | this->appendTriangleInstance().set( | 
|---|
| 450 | -n0 * fCurrStrokeRadius, n0 * fCurrStrokeRadius, n1 * fCurrStrokeRadius, | 
|---|
| 451 | offset, TriangleInstance::Ordering::kXYTransposed); | 
|---|
| 452 | if (Verb::kBevelJoin == joinVerb) { | 
|---|
| 453 | return; | 
|---|
| 454 | } | 
|---|
| 455 | this->appendTriangleInstance().set( | 
|---|
| 456 | -n0 * fCurrStrokeRadius, n1 * fCurrStrokeRadius, -n1 * fCurrStrokeRadius, | 
|---|
| 457 | offset, TriangleInstance::Ordering::kXYTransposed); | 
|---|
| 458 | if (Verb::kBevelJoin == joinVerb) { | 
|---|
| 459 | return; | 
|---|
| 460 | } | 
|---|
| 461 | if (Verb::kInternalBevelJoin == joinVerb) { | 
|---|
| 462 | return; | 
|---|
| 463 | } | 
|---|
| 464 | } | 
|---|
| 465 |  | 
|---|
| 466 | // For miter and round joins, we place an additional triangle cap on top of the bevel. This | 
|---|
| 467 | // triangle is literal for miters and is conic control points for round joins. | 
|---|
| 468 | SkASSERT(miterCapHeightOverWidth >= 0 || SkScalarIsNaN(miterCapHeightOverWidth)); | 
|---|
| 469 | Sk2f base = n1 - n0; | 
|---|
| 470 | Sk2f baseNorm = Sk2f(base[1], -base[0]); | 
|---|
| 471 | Sk2f c = (n0 + n1) * .5f + baseNorm * miterCapHeightOverWidth; | 
|---|
| 472 |  | 
|---|
| 473 | if (Verb::kMiterJoin == joinVerb) { | 
|---|
| 474 | this->appendTriangleInstance().set( | 
|---|
| 475 | n0 * fCurrStrokeRadius, c * fCurrStrokeRadius, n1 * fCurrStrokeRadius, offset, | 
|---|
| 476 | TriangleInstance::Ordering::kXYTransposed); | 
|---|
| 477 | } else { | 
|---|
| 478 | SkASSERT(Verb::kRoundJoin == joinVerb || Verb::kInternalRoundJoin == joinVerb); | 
|---|
| 479 | this->appendConicInstance().setW(n0 * fCurrStrokeRadius, c * fCurrStrokeRadius, | 
|---|
| 480 | n1 * fCurrStrokeRadius, offset, conicWeight); | 
|---|
| 481 | if (Verb::kInternalRoundJoin == joinVerb) { | 
|---|
| 482 | this->appendConicInstance().setW(-n1 * fCurrStrokeRadius, c * -fCurrStrokeRadius, | 
|---|
| 483 | -n0 * fCurrStrokeRadius, offset, conicWeight); | 
|---|
| 484 | } | 
|---|
| 485 | } | 
|---|
| 486 | } | 
|---|
| 487 |  | 
|---|
| 488 | void appendCap(Verb capType, const SkPoint& pt, const SkVector& norm) { | 
|---|
| 489 | SkASSERT(this->isMapped()); | 
|---|
| 490 |  | 
|---|
| 491 | Sk2f n = Sk2f::Load(&norm) * fCurrStrokeRadius; | 
|---|
| 492 | Sk2f v = Sk2f(-n[1], n[0]); | 
|---|
| 493 | Sk2f offset = Sk2f::Load(&pt) + Sk2f(fCurrDX, fCurrDY); | 
|---|
| 494 |  | 
|---|
| 495 | if (Verb::kSquareCap == capType) { | 
|---|
| 496 | SkPoint endPts[2] = {{0, 0}, {v[0], v[1]}}; | 
|---|
| 497 | this->appendLinearStrokeInstance().set(endPts, offset[0], offset[1], fCurrStrokeRadius); | 
|---|
| 498 | } else { | 
|---|
| 499 | SkASSERT(Verb::kRoundCap == capType); | 
|---|
| 500 | this->appendTriangleInstance().set( | 
|---|
| 501 | n, v, -n, offset, TriangleInstance::Ordering::kXYTransposed); | 
|---|
| 502 | this->appendConicInstance().setW(n, n + v, v, offset, SK_ScalarRoot2Over2); | 
|---|
| 503 | this->appendConicInstance().setW(v, v - n, -n, offset, SK_ScalarRoot2Over2); | 
|---|
| 504 | } | 
|---|
| 505 | } | 
|---|
| 506 |  | 
|---|
| 507 | sk_sp<const GrGpuBuffer> finish() { | 
|---|
| 508 | SkASSERT(this->isMapped()); | 
|---|
| 509 | SkASSERT(!memcmp(fNextInstances, fEndInstances, sizeof(fNextInstances))); | 
|---|
| 510 | fInstanceBuffer.unmapBuffer(); | 
|---|
| 511 | SkASSERT(!this->isMapped()); | 
|---|
| 512 | return fInstanceBuffer.gpuBuffer(); | 
|---|
| 513 | } | 
|---|
| 514 |  | 
|---|
| 515 | private: | 
|---|
| 516 | LinearStrokeInstance& appendLinearStrokeInstance() { | 
|---|
| 517 | int instanceIdx = fCurrNextInstances->fStrokes[0]++; | 
|---|
| 518 | SkASSERT(instanceIdx < fCurrEndInstances->fStrokes[0]); | 
|---|
| 519 |  | 
|---|
| 520 | return reinterpret_cast<LinearStrokeInstance*>(fInstanceBuffer.data())[instanceIdx]; | 
|---|
| 521 | } | 
|---|
| 522 |  | 
|---|
| 523 | CubicStrokeInstance& appendCubicStrokeInstance(int numLinearSegmentsLog2) { | 
|---|
| 524 | SkASSERT(numLinearSegmentsLog2 > 0); | 
|---|
| 525 | SkASSERT(numLinearSegmentsLog2 <= kMaxNumLinearSegmentsLog2); | 
|---|
| 526 |  | 
|---|
| 527 | int instanceIdx = fCurrNextInstances->fStrokes[numLinearSegmentsLog2]++; | 
|---|
| 528 | SkASSERT(instanceIdx < fCurrEndInstances->fStrokes[numLinearSegmentsLog2]); | 
|---|
| 529 |  | 
|---|
| 530 | return reinterpret_cast<CubicStrokeInstance*>(fInstanceBuffer.data())[instanceIdx]; | 
|---|
| 531 | } | 
|---|
| 532 |  | 
|---|
| 533 | TriangleInstance& appendTriangleInstance() { | 
|---|
| 534 | int instanceIdx = fCurrNextInstances->fTriangles++; | 
|---|
| 535 | SkASSERT(instanceIdx < fCurrEndInstances->fTriangles); | 
|---|
| 536 |  | 
|---|
| 537 | return reinterpret_cast<TriangleInstance*>(fInstanceBuffer.data())[instanceIdx]; | 
|---|
| 538 | } | 
|---|
| 539 |  | 
|---|
| 540 | ConicInstance& appendConicInstance() { | 
|---|
| 541 | int instanceIdx = fCurrNextInstances->fConics++; | 
|---|
| 542 | SkASSERT(instanceIdx < fCurrEndInstances->fConics); | 
|---|
| 543 |  | 
|---|
| 544 | return reinterpret_cast<ConicInstance*>(fInstanceBuffer.data())[instanceIdx]; | 
|---|
| 545 | } | 
|---|
| 546 |  | 
|---|
| 547 | float fCurrDX, fCurrDY; | 
|---|
| 548 | float fCurrStrokeRadius; | 
|---|
| 549 | InstanceTallies* fCurrNextInstances; | 
|---|
| 550 | SkDEBUGCODE(const InstanceTallies* fCurrEndInstances); | 
|---|
| 551 |  | 
|---|
| 552 | GrAutoMapVertexBuffer fInstanceBuffer; | 
|---|
| 553 | InstanceTallies fNextInstances[2]; | 
|---|
| 554 | SkDEBUGCODE(InstanceTallies fEndInstances[2]); | 
|---|
| 555 | }; | 
|---|
| 556 |  | 
|---|
| 557 | GrCCStroker::BatchID GrCCStroker::closeCurrentBatch() { | 
|---|
| 558 | if (!fHasOpenBatch) { | 
|---|
| 559 | return kEmptyBatchID; | 
|---|
| 560 | } | 
|---|
| 561 | int start = (fBatches.count() < 2) ? 0 : fBatches[fBatches.count() - 2].fEndScissorSubBatch; | 
|---|
| 562 | int end = fBatches.back().fEndScissorSubBatch; | 
|---|
| 563 | fMaxNumScissorSubBatches = std::max(fMaxNumScissorSubBatches, end - start); | 
|---|
| 564 | fHasOpenBatch = false; | 
|---|
| 565 | return fBatches.count() - 1; | 
|---|
| 566 | } | 
|---|
| 567 |  | 
|---|
| 568 | bool GrCCStroker::prepareToDraw(GrOnFlushResourceProvider* onFlushRP) { | 
|---|
| 569 | SkASSERT(!fInstanceBuffer); | 
|---|
| 570 | SkASSERT(!fHasOpenBatch);  // Call closeCurrentBatch() first. | 
|---|
| 571 |  | 
|---|
| 572 | // Here we layout a single instance buffer to share with every internal batch. | 
|---|
| 573 | // | 
|---|
| 574 | // Rather than place each instance array in its own GPU buffer, we allocate a single | 
|---|
| 575 | // megabuffer and lay them all out side-by-side. We can offset the "baseInstance" parameter in | 
|---|
| 576 | // our draw calls to direct the GPU to the applicable elements within a given array. | 
|---|
| 577 | fBaseInstances[0].fStrokes[0] = 0; | 
|---|
| 578 | fBaseInstances[1].fStrokes[0] = fInstanceCounts[0]->fStrokes[0]; | 
|---|
| 579 | int endLinearStrokesIdx = fBaseInstances[1].fStrokes[0] + fInstanceCounts[1]->fStrokes[0]; | 
|---|
| 580 |  | 
|---|
| 581 | int cubicStrokesIdx = GrSizeDivRoundUp(endLinearStrokesIdx * sizeof(LinearStrokeInstance), | 
|---|
| 582 | sizeof(CubicStrokeInstance)); | 
|---|
| 583 | for (int i = 1; i <= kMaxNumLinearSegmentsLog2; ++i) { | 
|---|
| 584 | for (int j = 0; j < kNumScissorModes; ++j) { | 
|---|
| 585 | fBaseInstances[j].fStrokes[i] = cubicStrokesIdx; | 
|---|
| 586 | cubicStrokesIdx += fInstanceCounts[j]->fStrokes[i]; | 
|---|
| 587 | } | 
|---|
| 588 | } | 
|---|
| 589 |  | 
|---|
| 590 | int trianglesIdx = GrSizeDivRoundUp(cubicStrokesIdx * sizeof(CubicStrokeInstance), | 
|---|
| 591 | sizeof(TriangleInstance)); | 
|---|
| 592 | fBaseInstances[0].fTriangles = trianglesIdx; | 
|---|
| 593 | fBaseInstances[1].fTriangles = | 
|---|
| 594 | fBaseInstances[0].fTriangles + fInstanceCounts[0]->fTriangles; | 
|---|
| 595 | int endTrianglesIdx = | 
|---|
| 596 | fBaseInstances[1].fTriangles + fInstanceCounts[1]->fTriangles; | 
|---|
| 597 |  | 
|---|
| 598 | int conicsIdx = | 
|---|
| 599 | GrSizeDivRoundUp(endTrianglesIdx * sizeof(TriangleInstance), sizeof(ConicInstance)); | 
|---|
| 600 | fBaseInstances[0].fConics = conicsIdx; | 
|---|
| 601 | fBaseInstances[1].fConics = fBaseInstances[0].fConics + fInstanceCounts[0]->fConics; | 
|---|
| 602 |  | 
|---|
| 603 | InstanceBufferBuilder builder(onFlushRP, this); | 
|---|
| 604 | if (!builder.isMapped()) { | 
|---|
| 605 | return false;  // Buffer allocation failed. | 
|---|
| 606 | } | 
|---|
| 607 |  | 
|---|
| 608 | // Now parse the GrCCStrokeGeometry and expand it into the instance buffer. | 
|---|
| 609 | int pathIdx = 0; | 
|---|
| 610 | int ptsIdx = 0; | 
|---|
| 611 | int paramsIdx = 0; | 
|---|
| 612 | int normalsIdx = 0; | 
|---|
| 613 |  | 
|---|
| 614 | const SkTArray<GrCCStrokeGeometry::Parameter, true>& params = fGeometry.params(); | 
|---|
| 615 | const SkTArray<SkPoint, true>& pts = fGeometry.points(); | 
|---|
| 616 | const SkTArray<SkVector, true>& normals = fGeometry.normals(); | 
|---|
| 617 |  | 
|---|
| 618 | float miterCapHeightOverWidth=0, conicWeight=0; | 
|---|
| 619 |  | 
|---|
| 620 | for (Verb verb : fGeometry.verbs()) { | 
|---|
| 621 | switch (verb) { | 
|---|
| 622 | case Verb::kBeginPath: | 
|---|
| 623 | builder.updateCurrentInfo(fPathInfos[pathIdx]); | 
|---|
| 624 | ++pathIdx; | 
|---|
| 625 | continue; | 
|---|
| 626 |  | 
|---|
| 627 | case Verb::kLinearStroke: | 
|---|
| 628 | builder.appendLinearStroke(&pts[ptsIdx]); | 
|---|
| 629 | ++ptsIdx; | 
|---|
| 630 | continue; | 
|---|
| 631 | case Verb::kQuadraticStroke: | 
|---|
| 632 | builder.appendQuadraticStroke(&pts[ptsIdx], | 
|---|
| 633 | params[paramsIdx++].fNumLinearSegmentsLog2); | 
|---|
| 634 | ptsIdx += 2; | 
|---|
| 635 | ++normalsIdx; | 
|---|
| 636 | continue; | 
|---|
| 637 | case Verb::kCubicStroke: | 
|---|
| 638 | builder.appendCubicStroke(&pts[ptsIdx], params[paramsIdx++].fNumLinearSegmentsLog2); | 
|---|
| 639 | ptsIdx += 3; | 
|---|
| 640 | ++normalsIdx; | 
|---|
| 641 | continue; | 
|---|
| 642 |  | 
|---|
| 643 | case Verb::kRoundJoin: | 
|---|
| 644 | case Verb::kInternalRoundJoin: | 
|---|
| 645 | conicWeight = params[paramsIdx++].fConicWeight; | 
|---|
| 646 | [[fallthrough]]; | 
|---|
| 647 | case Verb::kMiterJoin: | 
|---|
| 648 | miterCapHeightOverWidth = params[paramsIdx++].fMiterCapHeightOverWidth; | 
|---|
| 649 | [[fallthrough]]; | 
|---|
| 650 | case Verb::kBevelJoin: | 
|---|
| 651 | case Verb::kInternalBevelJoin: | 
|---|
| 652 | builder.appendJoin(verb, pts[ptsIdx], normals[normalsIdx], normals[normalsIdx + 1], | 
|---|
| 653 | miterCapHeightOverWidth, conicWeight); | 
|---|
| 654 | ++normalsIdx; | 
|---|
| 655 | continue; | 
|---|
| 656 |  | 
|---|
| 657 | case Verb::kSquareCap: | 
|---|
| 658 | case Verb::kRoundCap: | 
|---|
| 659 | builder.appendCap(verb, pts[ptsIdx], normals[normalsIdx]); | 
|---|
| 660 | continue; | 
|---|
| 661 |  | 
|---|
| 662 | case Verb::kEndContour: | 
|---|
| 663 | ++ptsIdx; | 
|---|
| 664 | ++normalsIdx; | 
|---|
| 665 | continue; | 
|---|
| 666 | } | 
|---|
| 667 | SK_ABORT( "Invalid CCPR stroke element."); | 
|---|
| 668 | } | 
|---|
| 669 |  | 
|---|
| 670 | fInstanceBuffer = builder.finish(); | 
|---|
| 671 | SkASSERT(fPathInfos.count() == pathIdx); | 
|---|
| 672 | SkASSERT(pts.count() == ptsIdx); | 
|---|
| 673 | SkASSERT(normals.count() == normalsIdx); | 
|---|
| 674 | return true; | 
|---|
| 675 | } | 
|---|
| 676 |  | 
|---|
| 677 | void GrCCStroker::drawStrokes(GrOpFlushState* flushState, GrCCCoverageProcessor* proc, | 
|---|
| 678 | BatchID batchID, const SkIRect& drawBounds) const { | 
|---|
| 679 | using PrimitiveType = GrCCCoverageProcessor::PrimitiveType; | 
|---|
| 680 | SkASSERT(fInstanceBuffer); | 
|---|
| 681 |  | 
|---|
| 682 | if (kEmptyBatchID == batchID) { | 
|---|
| 683 | return; | 
|---|
| 684 | } | 
|---|
| 685 | const Batch& batch = fBatches[batchID]; | 
|---|
| 686 | int startScissorSubBatch = (!batchID) ? 0 : fBatches[batchID - 1].fEndScissorSubBatch; | 
|---|
| 687 |  | 
|---|
| 688 | const InstanceTallies* startIndices[2]; | 
|---|
| 689 | startIndices[(int)GrScissorTest::kDisabled] = (!batchID) | 
|---|
| 690 | ? &fZeroTallies : fBatches[batchID - 1].fNonScissorEndInstances; | 
|---|
| 691 | startIndices[(int)GrScissorTest::kEnabled] = (!startScissorSubBatch) | 
|---|
| 692 | ? &fZeroTallies : fScissorSubBatches[startScissorSubBatch - 1].fEndInstances; | 
|---|
| 693 |  | 
|---|
| 694 | GrPipeline pipeline(GrScissorTest::kEnabled, SkBlendMode::kPlus, | 
|---|
| 695 | flushState->drawOpArgs().writeSwizzle()); | 
|---|
| 696 |  | 
|---|
| 697 | // Draw linear strokes. | 
|---|
| 698 | this->drawLog2Strokes(0, flushState, LinearStrokeProcessor(), pipeline, batch, startIndices, | 
|---|
| 699 | startScissorSubBatch, drawBounds); | 
|---|
| 700 |  | 
|---|
| 701 | // Draw cubic strokes. (Quadratics were converted to cubics for GPU processing.) | 
|---|
| 702 | CubicStrokeProcessor cubicProc; | 
|---|
| 703 | for (int i = 1; i <= kMaxNumLinearSegmentsLog2; ++i) { | 
|---|
| 704 | this->drawLog2Strokes(i, flushState, cubicProc, pipeline, batch, startIndices, | 
|---|
| 705 | startScissorSubBatch, drawBounds); | 
|---|
| 706 | } | 
|---|
| 707 |  | 
|---|
| 708 | int numConnectingGeometrySubpasses = proc->numSubpasses(); | 
|---|
| 709 |  | 
|---|
| 710 | // Draw triangles. | 
|---|
| 711 | for (int i = 0; i < numConnectingGeometrySubpasses; ++i) { | 
|---|
| 712 | proc->reset(PrimitiveType::kTriangles, i, flushState->resourceProvider()); | 
|---|
| 713 | this->drawConnectingGeometry<&InstanceTallies::fTriangles>( | 
|---|
| 714 | flushState, pipeline, *proc, batch, startIndices, startScissorSubBatch, drawBounds); | 
|---|
| 715 | } | 
|---|
| 716 |  | 
|---|
| 717 | // Draw conics. | 
|---|
| 718 | for (int i = 0; i < numConnectingGeometrySubpasses; ++i) { | 
|---|
| 719 | proc->reset(PrimitiveType::kConics, i, flushState->resourceProvider()); | 
|---|
| 720 | this->drawConnectingGeometry<&InstanceTallies::fConics>( | 
|---|
| 721 | flushState, pipeline, *proc, batch, startIndices, startScissorSubBatch, drawBounds); | 
|---|
| 722 | } | 
|---|
| 723 | } | 
|---|
| 724 |  | 
|---|
| 725 | void GrCCStroker::drawLog2Strokes(int numSegmentsLog2, GrOpFlushState* flushState, | 
|---|
| 726 | const GrPrimitiveProcessor& processor, const GrPipeline& pipeline, | 
|---|
| 727 | const Batch& batch, const InstanceTallies* startIndices[2], | 
|---|
| 728 | int startScissorSubBatch, const SkIRect& drawBounds) const { | 
|---|
| 729 | GrProgramInfo programInfo(flushState->proxy()->numSamples(), | 
|---|
| 730 | flushState->proxy()->numStencilSamples(), | 
|---|
| 731 | flushState->proxy()->backendFormat(), | 
|---|
| 732 | flushState->writeView()->origin(), &pipeline, &processor, | 
|---|
| 733 | GrPrimitiveType::kTriangleStrip); | 
|---|
| 734 |  | 
|---|
| 735 | flushState->bindPipeline(programInfo, SkRect::Make(drawBounds)); | 
|---|
| 736 | flushState->bindBuffers(nullptr, fInstanceBuffer, nullptr); | 
|---|
| 737 |  | 
|---|
| 738 | // Linear strokes draw a quad. Cubic strokes emit a strip with normals at "numSegments" | 
|---|
| 739 | // evenly-spaced points along the curve, plus one more for the final endpoint, plus two more for | 
|---|
| 740 | // AA butt caps. (i.e., 2 vertices * (numSegments + 3).) | 
|---|
| 741 | int numStripVertices = (0 == numSegmentsLog2) ? 4 : ((1 << numSegmentsLog2) + 3) * 2; | 
|---|
| 742 |  | 
|---|
| 743 | // Draw non-scissored strokes. | 
|---|
| 744 | int baseInstance = fBaseInstances[(int)GrScissorTest::kDisabled].fStrokes[numSegmentsLog2]; | 
|---|
| 745 | int startIdx = startIndices[(int)GrScissorTest::kDisabled]->fStrokes[numSegmentsLog2]; | 
|---|
| 746 | int endIdx = batch.fNonScissorEndInstances->fStrokes[numSegmentsLog2]; | 
|---|
| 747 | SkASSERT(endIdx >= startIdx); | 
|---|
| 748 | if (int instanceCount = endIdx - startIdx) { | 
|---|
| 749 | flushState->setScissorRect(drawBounds); | 
|---|
| 750 | flushState->drawInstanced(instanceCount, baseInstance + startIdx, numStripVertices, 0); | 
|---|
| 751 | } | 
|---|
| 752 |  | 
|---|
| 753 | // Draw scissored strokes. | 
|---|
| 754 | baseInstance = fBaseInstances[(int)GrScissorTest::kEnabled].fStrokes[numSegmentsLog2]; | 
|---|
| 755 | startIdx = startIndices[(int)GrScissorTest::kEnabled]->fStrokes[numSegmentsLog2]; | 
|---|
| 756 | for (int i = startScissorSubBatch; i < batch.fEndScissorSubBatch; ++i) { | 
|---|
| 757 | const ScissorSubBatch& subBatch = fScissorSubBatches[i]; | 
|---|
| 758 | endIdx = subBatch.fEndInstances->fStrokes[numSegmentsLog2]; | 
|---|
| 759 | SkASSERT(endIdx >= startIdx); | 
|---|
| 760 | if (int instanceCount = endIdx - startIdx) { | 
|---|
| 761 | flushState->setScissorRect(subBatch.fScissor); | 
|---|
| 762 | flushState->drawInstanced(instanceCount, baseInstance + startIdx, numStripVertices, 0); | 
|---|
| 763 | startIdx = endIdx; | 
|---|
| 764 | } | 
|---|
| 765 | } | 
|---|
| 766 | } | 
|---|
| 767 |  | 
|---|
| 768 | template<int GrCCStrokeGeometry::InstanceTallies::* InstanceType> | 
|---|
| 769 | void GrCCStroker::drawConnectingGeometry(GrOpFlushState* flushState, const GrPipeline& pipeline, | 
|---|
| 770 | const GrCCCoverageProcessor& processor, | 
|---|
| 771 | const Batch& batch, const InstanceTallies* startIndices[2], | 
|---|
| 772 | int startScissorSubBatch, | 
|---|
| 773 | const SkIRect& drawBounds) const { | 
|---|
| 774 | processor.bindPipeline(flushState, pipeline, SkRect::Make(drawBounds)); | 
|---|
| 775 | processor.bindBuffers(flushState->opsRenderPass(), fInstanceBuffer); | 
|---|
| 776 |  | 
|---|
| 777 | // Append non-scissored meshes. | 
|---|
| 778 | int baseInstance = fBaseInstances[(int)GrScissorTest::kDisabled].*InstanceType; | 
|---|
| 779 | int startIdx = startIndices[(int)GrScissorTest::kDisabled]->*InstanceType; | 
|---|
| 780 | int endIdx = batch.fNonScissorEndInstances->*InstanceType; | 
|---|
| 781 | SkASSERT(endIdx >= startIdx); | 
|---|
| 782 | if (int instanceCount = endIdx - startIdx) { | 
|---|
| 783 | flushState->setScissorRect(drawBounds); | 
|---|
| 784 | processor.drawInstances(flushState->opsRenderPass(), instanceCount, | 
|---|
| 785 | baseInstance + startIdx); | 
|---|
| 786 | } | 
|---|
| 787 |  | 
|---|
| 788 | // Append scissored meshes. | 
|---|
| 789 | baseInstance = fBaseInstances[(int)GrScissorTest::kEnabled].*InstanceType; | 
|---|
| 790 | startIdx = startIndices[(int)GrScissorTest::kEnabled]->*InstanceType; | 
|---|
| 791 | for (int i = startScissorSubBatch; i < batch.fEndScissorSubBatch; ++i) { | 
|---|
| 792 | const ScissorSubBatch& subBatch = fScissorSubBatches[i]; | 
|---|
| 793 | endIdx = subBatch.fEndInstances->*InstanceType; | 
|---|
| 794 | SkASSERT(endIdx >= startIdx); | 
|---|
| 795 | if (int instanceCount = endIdx - startIdx) { | 
|---|
| 796 | flushState->setScissorRect(subBatch.fScissor); | 
|---|
| 797 | processor.drawInstances(flushState->opsRenderPass(), instanceCount, | 
|---|
| 798 | baseInstance + startIdx); | 
|---|
| 799 | startIdx = endIdx; | 
|---|
| 800 | } | 
|---|
| 801 | } | 
|---|
| 802 | } | 
|---|
| 803 |  | 
|---|