1 /* 2 * Copyright 2015 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 "GrAALinearizingConvexPathRenderer.h" 9 #include "GrAAConvexTessellator.h" 10 #include "GrContext.h" 11 #include "GrDefaultGeoProcFactory.h" 12 #include "GrDrawOpTest.h" 13 #include "GrGeometryProcessor.h" 14 #include "GrOpFlushState.h" 15 #include "GrPathUtils.h" 16 #include "GrProcessor.h" 17 #include "GrRenderTargetContext.h" 18 #include "GrShape.h" 19 #include "GrStyle.h" 20 #include "GrVertexWriter.h" 21 #include "SkGeometry.h" 22 #include "SkPathPriv.h" 23 #include "SkString.h" 24 #include "SkTraceEvent.h" 25 #include "glsl/GrGLSLGeometryProcessor.h" 26 #include "ops/GrMeshDrawOp.h" 27 #include "ops/GrSimpleMeshDrawOpHelper.h" 28 29 static const int DEFAULT_BUFFER_SIZE = 100; 30 31 // The thicker the stroke, the harder it is to produce high-quality results using tessellation. For 32 // the time being, we simply drop back to software rendering above this stroke width. 33 static const SkScalar kMaxStrokeWidth = 20.0; 34 35 GrAALinearizingConvexPathRenderer::GrAALinearizingConvexPathRenderer() { 36 } 37 38 /////////////////////////////////////////////////////////////////////////////// 39 40 GrPathRenderer::CanDrawPath 41 GrAALinearizingConvexPathRenderer::onCanDrawPath(const CanDrawPathArgs& args) const { 42 if (GrAAType::kCoverage != args.fAAType) { 43 return CanDrawPath::kNo; 44 } 45 if (!args.fShape->knownToBeConvex()) { 46 return CanDrawPath::kNo; 47 } 48 if (args.fShape->style().pathEffect()) { 49 return CanDrawPath::kNo; 50 } 51 if (args.fShape->inverseFilled()) { 52 return CanDrawPath::kNo; 53 } 54 if (args.fShape->bounds().width() <= 0 && args.fShape->bounds().height() <= 0) { 55 // Stroked zero length lines should draw, but this PR doesn't handle that case 56 return CanDrawPath::kNo; 57 } 58 const SkStrokeRec& stroke = args.fShape->style().strokeRec(); 59 60 if (stroke.getStyle() == SkStrokeRec::kStroke_Style || 61 stroke.getStyle() == SkStrokeRec::kStrokeAndFill_Style) { 62 if (!args.fViewMatrix->isSimilarity()) { 63 return CanDrawPath::kNo; 64 } 65 SkScalar strokeWidth = args.fViewMatrix->getMaxScale() * stroke.getWidth(); 66 if (strokeWidth < 1.0f && stroke.getStyle() == SkStrokeRec::kStroke_Style) { 67 return CanDrawPath::kNo; 68 } 69 if (strokeWidth > kMaxStrokeWidth || 70 !args.fShape->knownToBeClosed() || 71 stroke.getJoin() == SkPaint::Join::kRound_Join) { 72 return CanDrawPath::kNo; 73 } 74 return CanDrawPath::kYes; 75 } 76 if (stroke.getStyle() != SkStrokeRec::kFill_Style) { 77 return CanDrawPath::kNo; 78 } 79 return CanDrawPath::kYes; 80 } 81 82 // extract the result vertices and indices from the GrAAConvexTessellator 83 static void extract_verts(const GrAAConvexTessellator& tess, 84 void* vertData, 85 const GrVertexColor& color, 86 uint16_t firstIndex, 87 uint16_t* idxs) { 88 GrVertexWriter verts{vertData}; 89 for (int i = 0; i < tess.numPts(); ++i) { 90 verts.write(tess.point(i), color, tess.coverage(i)); 91 } 92 93 for (int i = 0; i < tess.numIndices(); ++i) { 94 idxs[i] = tess.index(i) + firstIndex; 95 } 96 } 97 98 static sk_sp<GrGeometryProcessor> create_lines_only_gp(const GrShaderCaps* shaderCaps, 99 bool tweakAlphaForCoverage, 100 const SkMatrix& viewMatrix, 101 bool usesLocalCoords, 102 bool wideColor) { 103 using namespace GrDefaultGeoProcFactory; 104 105 Coverage::Type coverageType = 106 tweakAlphaForCoverage ? Coverage::kAttributeTweakAlpha_Type : Coverage::kAttribute_Type; 107 LocalCoords::Type localCoordsType = 108 usesLocalCoords ? LocalCoords::kUsePosition_Type : LocalCoords::kUnused_Type; 109 Color::Type colorType = 110 wideColor ? Color::kPremulWideColorAttribute_Type : Color::kPremulGrColorAttribute_Type; 111 112 return MakeForDeviceSpace(shaderCaps, colorType, coverageType, localCoordsType, viewMatrix); 113 } 114 115 namespace { 116 117 class AAFlatteningConvexPathOp final : public GrMeshDrawOp { 118 private: 119 using Helper = GrSimpleMeshDrawOpHelperWithStencil; 120 121 public: 122 DEFINE_OP_CLASS_ID 123 static std::unique_ptr<GrDrawOp> Make(GrContext* context, 124 GrPaint&& paint, 125 const SkMatrix& viewMatrix, 126 const SkPath& path, 127 SkScalar strokeWidth, 128 SkStrokeRec::Style style, 129 SkPaint::Join join, 130 SkScalar miterLimit, 131 const GrUserStencilSettings* stencilSettings) { 132 return Helper::FactoryHelper<AAFlatteningConvexPathOp>(context, std::move(paint), 133 viewMatrix, path, 134 strokeWidth, style, join, miterLimit, 135 stencilSettings); 136 } 137 138 AAFlatteningConvexPathOp(const Helper::MakeArgs& helperArgs, 139 const SkPMColor4f& color, 140 const SkMatrix& viewMatrix, 141 const SkPath& path, 142 SkScalar strokeWidth, 143 SkStrokeRec::Style style, 144 SkPaint::Join join, 145 SkScalar miterLimit, 146 const GrUserStencilSettings* stencilSettings) 147 : INHERITED(ClassID()), fHelper(helperArgs, GrAAType::kCoverage, stencilSettings) { 148 fPaths.emplace_back( 149 PathData{color, viewMatrix, path, strokeWidth, style, join, miterLimit}); 150 151 // compute bounds 152 SkRect bounds = path.getBounds(); 153 SkScalar w = strokeWidth; 154 if (w > 0) { 155 w /= 2; 156 // If the half stroke width is < 1 then we effectively fallback to bevel joins. 157 if (SkPaint::kMiter_Join == join && w > 1.f) { 158 w *= miterLimit; 159 } 160 bounds.outset(w, w); 161 } 162 this->setTransformedBounds(bounds, viewMatrix, HasAABloat::kYes, IsZeroArea::kNo); 163 fWideColor = !SkPMColor4fFitsInBytes(color); 164 } 165 166 const char* name() const override { return "AAFlatteningConvexPathOp"; } 167 168 void visitProxies(const VisitProxyFunc& func, VisitorType) const override { 169 fHelper.visitProxies(func); 170 } 171 172 #ifdef SK_DEBUG 173 SkString dumpInfo() const override { 174 SkString string; 175 for (const auto& path : fPaths) { 176 string.appendf( 177 "Color: 0x%08x, StrokeWidth: %.2f, Style: %d, Join: %d, " 178 "MiterLimit: %.2f\n", 179 path.fColor.toBytes_RGBA(), path.fStrokeWidth, path.fStyle, path.fJoin, 180 path.fMiterLimit); 181 } 182 string += fHelper.dumpInfo(); 183 string += INHERITED::dumpInfo(); 184 return string; 185 } 186 #endif 187 188 FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); } 189 190 GrProcessorSet::Analysis finalize(const GrCaps& caps, const GrAppliedClip* clip) override { 191 return fHelper.finalizeProcessors(caps, clip, GrProcessorAnalysisCoverage::kSingleChannel, 192 &fPaths.back().fColor); 193 } 194 195 private: 196 void draw(Target* target, sk_sp<const GrGeometryProcessor> gp, const GrPipeline* pipeline, 197 const GrPipeline::FixedDynamicState* fixedDynamicState, int vertexCount, 198 size_t vertexStride, void* vertices, int indexCount, uint16_t* indices) const { 199 if (vertexCount == 0 || indexCount == 0) { 200 return; 201 } 202 sk_sp<const GrBuffer> vertexBuffer; 203 int firstVertex; 204 void* verts = target->makeVertexSpace(vertexStride, vertexCount, &vertexBuffer, 205 &firstVertex); 206 if (!verts) { 207 SkDebugf("Could not allocate vertices\n"); 208 return; 209 } 210 memcpy(verts, vertices, vertexCount * vertexStride); 211 212 sk_sp<const GrBuffer> indexBuffer; 213 int firstIndex; 214 uint16_t* idxs = target->makeIndexSpace(indexCount, &indexBuffer, &firstIndex); 215 if (!idxs) { 216 SkDebugf("Could not allocate indices\n"); 217 return; 218 } 219 memcpy(idxs, indices, indexCount * sizeof(uint16_t)); 220 GrMesh* mesh = target->allocMesh(GrPrimitiveType::kTriangles); 221 mesh->setIndexed(std::move(indexBuffer), indexCount, firstIndex, 0, vertexCount - 1, 222 GrPrimitiveRestart::kNo); 223 mesh->setVertexData(std::move(vertexBuffer), firstVertex); 224 target->draw(std::move(gp), pipeline, fixedDynamicState, mesh); 225 } 226 227 void onPrepareDraws(Target* target) override { 228 auto pipe = fHelper.makePipeline(target); 229 // Setup GrGeometryProcessor 230 sk_sp<GrGeometryProcessor> gp(create_lines_only_gp(target->caps().shaderCaps(), 231 fHelper.compatibleWithAlphaAsCoverage(), 232 this->viewMatrix(), 233 fHelper.usesLocalCoords(), 234 fWideColor)); 235 if (!gp) { 236 SkDebugf("Couldn't create a GrGeometryProcessor\n"); 237 return; 238 } 239 240 size_t vertexStride = gp->vertexStride(); 241 int instanceCount = fPaths.count(); 242 243 int64_t vertexCount = 0; 244 int64_t indexCount = 0; 245 int64_t maxVertices = DEFAULT_BUFFER_SIZE; 246 int64_t maxIndices = DEFAULT_BUFFER_SIZE; 247 uint8_t* vertices = (uint8_t*) sk_malloc_throw(maxVertices * vertexStride); 248 uint16_t* indices = (uint16_t*) sk_malloc_throw(maxIndices * sizeof(uint16_t)); 249 for (int i = 0; i < instanceCount; i++) { 250 const PathData& args = fPaths[i]; 251 GrAAConvexTessellator tess(args.fStyle, args.fStrokeWidth, 252 args.fJoin, args.fMiterLimit); 253 254 if (!tess.tessellate(args.fViewMatrix, args.fPath)) { 255 continue; 256 } 257 258 int currentVertices = tess.numPts(); 259 if (vertexCount + currentVertices > static_cast<int>(UINT16_MAX)) { 260 // if we added the current instance, we would overflow the indices we can store in a 261 // uint16_t. Draw what we've got so far and reset. 262 this->draw(target, gp, pipe.fPipeline, pipe.fFixedDynamicState, vertexCount, 263 vertexStride, vertices, indexCount, indices); 264 vertexCount = 0; 265 indexCount = 0; 266 } 267 if (vertexCount + currentVertices > maxVertices) { 268 maxVertices = SkTMax(vertexCount + currentVertices, maxVertices * 2); 269 if (maxVertices * vertexStride > SK_MaxS32) { 270 sk_free(vertices); 271 sk_free(indices); 272 return; 273 } 274 vertices = (uint8_t*) sk_realloc_throw(vertices, maxVertices * vertexStride); 275 } 276 int currentIndices = tess.numIndices(); 277 if (indexCount + currentIndices > maxIndices) { 278 maxIndices = SkTMax(indexCount + currentIndices, maxIndices * 2); 279 if (maxIndices * sizeof(uint16_t) > SK_MaxS32) { 280 sk_free(vertices); 281 sk_free(indices); 282 return; 283 } 284 indices = (uint16_t*) sk_realloc_throw(indices, maxIndices * sizeof(uint16_t)); 285 } 286 287 extract_verts(tess, vertices + vertexStride * vertexCount, 288 GrVertexColor(args.fColor, fWideColor), vertexCount, 289 indices + indexCount); 290 vertexCount += currentVertices; 291 indexCount += currentIndices; 292 } 293 if (vertexCount <= SK_MaxS32 && indexCount <= SK_MaxS32) { 294 this->draw(target, std::move(gp), pipe.fPipeline, pipe.fFixedDynamicState, vertexCount, 295 vertexStride, vertices, indexCount, indices); 296 } 297 sk_free(vertices); 298 sk_free(indices); 299 } 300 301 CombineResult onCombineIfPossible(GrOp* t, const GrCaps& caps) override { 302 AAFlatteningConvexPathOp* that = t->cast<AAFlatteningConvexPathOp>(); 303 if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) { 304 return CombineResult::kCannotCombine; 305 } 306 307 fPaths.push_back_n(that->fPaths.count(), that->fPaths.begin()); 308 fWideColor |= that->fWideColor; 309 return CombineResult::kMerged; 310 } 311 312 const SkMatrix& viewMatrix() const { return fPaths[0].fViewMatrix; } 313 314 struct PathData { 315 SkPMColor4f fColor; 316 SkMatrix fViewMatrix; 317 SkPath fPath; 318 SkScalar fStrokeWidth; 319 SkStrokeRec::Style fStyle; 320 SkPaint::Join fJoin; 321 SkScalar fMiterLimit; 322 }; 323 324 SkSTArray<1, PathData, true> fPaths; 325 Helper fHelper; 326 bool fWideColor; 327 328 typedef GrMeshDrawOp INHERITED; 329 }; 330 331 } // anonymous namespace 332 333 bool GrAALinearizingConvexPathRenderer::onDrawPath(const DrawPathArgs& args) { 334 GR_AUDIT_TRAIL_AUTO_FRAME(args.fRenderTargetContext->auditTrail(), 335 "GrAALinearizingConvexPathRenderer::onDrawPath"); 336 SkASSERT(GrFSAAType::kUnifiedMSAA != args.fRenderTargetContext->fsaaType()); 337 SkASSERT(!args.fShape->isEmpty()); 338 SkASSERT(!args.fShape->style().pathEffect()); 339 340 SkPath path; 341 args.fShape->asPath(&path); 342 bool fill = args.fShape->style().isSimpleFill(); 343 const SkStrokeRec& stroke = args.fShape->style().strokeRec(); 344 SkScalar strokeWidth = fill ? -1.0f : stroke.getWidth(); 345 SkPaint::Join join = fill ? SkPaint::Join::kMiter_Join : stroke.getJoin(); 346 SkScalar miterLimit = stroke.getMiter(); 347 348 std::unique_ptr<GrDrawOp> op = AAFlatteningConvexPathOp::Make( 349 args.fContext, std::move(args.fPaint), *args.fViewMatrix, path, strokeWidth, 350 stroke.getStyle(), join, miterLimit, args.fUserStencilSettings); 351 args.fRenderTargetContext->addDrawOp(*args.fClip, std::move(op)); 352 return true; 353 } 354 355 /////////////////////////////////////////////////////////////////////////////////////////////////// 356 357 #if GR_TEST_UTILS 358 359 GR_DRAW_OP_TEST_DEFINE(AAFlatteningConvexPathOp) { 360 SkMatrix viewMatrix = GrTest::TestMatrixPreservesRightAngles(random); 361 SkPath path = GrTest::TestPathConvex(random); 362 363 SkStrokeRec::Style styles[3] = { SkStrokeRec::kFill_Style, 364 SkStrokeRec::kStroke_Style, 365 SkStrokeRec::kStrokeAndFill_Style }; 366 367 SkStrokeRec::Style style = styles[random->nextU() % 3]; 368 369 SkScalar strokeWidth = -1.f; 370 SkPaint::Join join = SkPaint::kMiter_Join; 371 SkScalar miterLimit = 0.5f; 372 373 if (SkStrokeRec::kFill_Style != style) { 374 strokeWidth = random->nextRangeF(1.0f, 10.0f); 375 if (random->nextBool()) { 376 join = SkPaint::kMiter_Join; 377 } else { 378 join = SkPaint::kBevel_Join; 379 } 380 miterLimit = random->nextRangeF(0.5f, 2.0f); 381 } 382 const GrUserStencilSettings* stencilSettings = GrGetRandomStencil(random, context); 383 return AAFlatteningConvexPathOp::Make(context, std::move(paint), viewMatrix, path, strokeWidth, 384 style, join, miterLimit, stencilSettings); 385 } 386 387 #endif 388