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 "GrTessellatingPathRenderer.h" 9 10 #include "GrAuditTrail.h" 11 #include "GrClip.h" 12 #include "GrDefaultGeoProcFactory.h" 13 #include "GrDrawOpTest.h" 14 #include "GrMesh.h" 15 #include "GrOpFlushState.h" 16 #include "GrPathUtils.h" 17 #include "GrResourceCache.h" 18 #include "GrResourceProvider.h" 19 #include "GrTessellator.h" 20 #include "SkGeometry.h" 21 22 #include "GrSimpleMeshDrawOpHelper.h" 23 #include "ops/GrMeshDrawOp.h" 24 25 #include <stdio.h> 26 27 #ifndef GR_AA_TESSELLATOR_MAX_VERB_COUNT 28 #define GR_AA_TESSELLATOR_MAX_VERB_COUNT 10 29 #endif 30 31 /* 32 * This path renderer tessellates the path into triangles using GrTessellator, uploads the 33 * triangles to a vertex buffer, and renders them with a single draw call. It can do screenspace 34 * antialiasing with a one-pixel coverage ramp. 35 */ 36 namespace { 37 38 struct TessInfo { 39 SkScalar fTolerance; 40 int fCount; 41 }; 42 43 // When the SkPathRef genID changes, invalidate a corresponding GrResource described by key. 44 class PathInvalidator : public SkPathRef::GenIDChangeListener { 45 public: 46 explicit PathInvalidator(const GrUniqueKey& key) : fMsg(key) {} 47 private: 48 GrUniqueKeyInvalidatedMessage fMsg; 49 50 void onChange() override { 51 SkMessageBus<GrUniqueKeyInvalidatedMessage>::Post(fMsg); 52 } 53 }; 54 55 bool cache_match(GrBuffer* vertexBuffer, SkScalar tol, int* actualCount) { 56 if (!vertexBuffer) { 57 return false; 58 } 59 const SkData* data = vertexBuffer->getUniqueKey().getCustomData(); 60 SkASSERT(data); 61 const TessInfo* info = static_cast<const TessInfo*>(data->data()); 62 if (info->fTolerance == 0 || info->fTolerance < 3.0f * tol) { 63 *actualCount = info->fCount; 64 return true; 65 } 66 return false; 67 } 68 69 class StaticVertexAllocator : public GrTessellator::VertexAllocator { 70 public: 71 StaticVertexAllocator(size_t stride, GrResourceProvider* resourceProvider, bool canMapVB) 72 : VertexAllocator(stride) 73 , fResourceProvider(resourceProvider) 74 , fCanMapVB(canMapVB) 75 , fVertices(nullptr) { 76 } 77 void* lock(int vertexCount) override { 78 size_t size = vertexCount * stride(); 79 fVertexBuffer.reset(fResourceProvider->createBuffer( 80 size, kVertex_GrBufferType, kStatic_GrAccessPattern, 0)); 81 if (!fVertexBuffer.get()) { 82 return nullptr; 83 } 84 if (fCanMapVB) { 85 fVertices = fVertexBuffer->map(); 86 } else { 87 fVertices = sk_malloc_throw(vertexCount * stride()); 88 } 89 return fVertices; 90 } 91 void unlock(int actualCount) override { 92 if (fCanMapVB) { 93 fVertexBuffer->unmap(); 94 } else { 95 fVertexBuffer->updateData(fVertices, actualCount * stride()); 96 sk_free(fVertices); 97 } 98 fVertices = nullptr; 99 } 100 GrBuffer* vertexBuffer() { return fVertexBuffer.get(); } 101 private: 102 sk_sp<GrBuffer> fVertexBuffer; 103 GrResourceProvider* fResourceProvider; 104 bool fCanMapVB; 105 void* fVertices; 106 }; 107 108 class DynamicVertexAllocator : public GrTessellator::VertexAllocator { 109 public: 110 DynamicVertexAllocator(size_t stride, GrMeshDrawOp::Target* target) 111 : VertexAllocator(stride) 112 , fTarget(target) 113 , fVertexBuffer(nullptr) 114 , fVertices(nullptr) {} 115 void* lock(int vertexCount) override { 116 fVertexCount = vertexCount; 117 fVertices = fTarget->makeVertexSpace(stride(), vertexCount, &fVertexBuffer, &fFirstVertex); 118 return fVertices; 119 } 120 void unlock(int actualCount) override { 121 fTarget->putBackVertices(fVertexCount - actualCount, stride()); 122 fVertices = nullptr; 123 } 124 const GrBuffer* vertexBuffer() const { return fVertexBuffer; } 125 int firstVertex() const { return fFirstVertex; } 126 private: 127 GrMeshDrawOp::Target* fTarget; 128 const GrBuffer* fVertexBuffer; 129 int fVertexCount; 130 int fFirstVertex; 131 void* fVertices; 132 }; 133 134 } // namespace 135 136 GrTessellatingPathRenderer::GrTessellatingPathRenderer() { 137 } 138 139 GrPathRenderer::CanDrawPath 140 GrTessellatingPathRenderer::onCanDrawPath(const CanDrawPathArgs& args) const { 141 // This path renderer can draw fill styles, and can do screenspace antialiasing via a 142 // one-pixel coverage ramp. It can do convex and concave paths, but we'll leave the convex 143 // ones to simpler algorithms. We pass on paths that have styles, though they may come back 144 // around after applying the styling information to the geometry to create a filled path. In 145 // the non-AA case, We skip paths that don't have a key since the real advantage of this path 146 // renderer comes from caching the tessellated geometry. In the AA case, we do not cache, so we 147 // accept paths without keys. 148 if (!args.fShape->style().isSimpleFill() || args.fShape->knownToBeConvex()) { 149 return CanDrawPath::kNo; 150 } 151 if (GrAAType::kCoverage == args.fAAType) { 152 SkPath path; 153 args.fShape->asPath(&path); 154 if (path.countVerbs() > GR_AA_TESSELLATOR_MAX_VERB_COUNT) { 155 return CanDrawPath::kNo; 156 } 157 } else if (!args.fShape->hasUnstyledKey()) { 158 return CanDrawPath::kNo; 159 } 160 return CanDrawPath::kYes; 161 } 162 163 namespace { 164 165 class TessellatingPathOp final : public GrMeshDrawOp { 166 private: 167 using Helper = GrSimpleMeshDrawOpHelperWithStencil; 168 169 public: 170 DEFINE_OP_CLASS_ID 171 172 static std::unique_ptr<GrDrawOp> Make(GrPaint&& paint, 173 const GrShape& shape, 174 const SkMatrix& viewMatrix, 175 SkIRect devClipBounds, 176 GrAAType aaType, 177 const GrUserStencilSettings* stencilSettings) { 178 return Helper::FactoryHelper<TessellatingPathOp>(std::move(paint), shape, viewMatrix, 179 devClipBounds, aaType, stencilSettings); 180 } 181 182 const char* name() const override { return "TessellatingPathOp"; } 183 184 void visitProxies(const VisitProxyFunc& func) const override { 185 fHelper.visitProxies(func); 186 } 187 188 SkString dumpInfo() const override { 189 SkString string; 190 string.appendf("Color 0x%08x, aa: %d\n", fColor, fAntiAlias); 191 string += fHelper.dumpInfo(); 192 string += INHERITED::dumpInfo(); 193 return string; 194 } 195 196 TessellatingPathOp(Helper::MakeArgs helperArgs, 197 GrColor color, 198 const GrShape& shape, 199 const SkMatrix& viewMatrix, 200 const SkIRect& devClipBounds, 201 GrAAType aaType, 202 const GrUserStencilSettings* stencilSettings) 203 : INHERITED(ClassID()) 204 , fHelper(helperArgs, aaType, stencilSettings) 205 , fColor(color) 206 , fShape(shape) 207 , fViewMatrix(viewMatrix) 208 , fDevClipBounds(devClipBounds) 209 , fAntiAlias(GrAAType::kCoverage == aaType) { 210 SkRect devBounds; 211 viewMatrix.mapRect(&devBounds, shape.bounds()); 212 if (shape.inverseFilled()) { 213 // Because the clip bounds are used to add a contour for inverse fills, they must also 214 // include the path bounds. 215 devBounds.join(SkRect::Make(fDevClipBounds)); 216 } 217 this->setBounds(devBounds, HasAABloat::kNo, IsZeroArea::kNo); 218 } 219 220 FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); } 221 222 RequiresDstTexture finalize(const GrCaps& caps, const GrAppliedClip* clip, 223 GrPixelConfigIsClamped dstIsClamped) override { 224 GrProcessorAnalysisCoverage coverage = fAntiAlias 225 ? GrProcessorAnalysisCoverage::kSingleChannel 226 : GrProcessorAnalysisCoverage::kNone; 227 return fHelper.xpRequiresDstTexture(caps, clip, dstIsClamped, coverage, &fColor); 228 } 229 230 private: 231 SkPath getPath() const { 232 SkASSERT(!fShape.style().applies()); 233 SkPath path; 234 fShape.asPath(&path); 235 return path; 236 } 237 238 void draw(Target* target, const GrGeometryProcessor* gp) { 239 SkASSERT(!fAntiAlias); 240 GrResourceProvider* rp = target->resourceProvider(); 241 bool inverseFill = fShape.inverseFilled(); 242 // construct a cache key from the path's genID and the view matrix 243 static const GrUniqueKey::Domain kDomain = GrUniqueKey::GenerateDomain(); 244 GrUniqueKey key; 245 static constexpr int kClipBoundsCnt = sizeof(fDevClipBounds) / sizeof(uint32_t); 246 int shapeKeyDataCnt = fShape.unstyledKeySize(); 247 SkASSERT(shapeKeyDataCnt >= 0); 248 GrUniqueKey::Builder builder(&key, kDomain, shapeKeyDataCnt + kClipBoundsCnt, "Path"); 249 fShape.writeUnstyledKey(&builder[0]); 250 // For inverse fills, the tessellation is dependent on clip bounds. 251 if (inverseFill) { 252 memcpy(&builder[shapeKeyDataCnt], &fDevClipBounds, sizeof(fDevClipBounds)); 253 } else { 254 memset(&builder[shapeKeyDataCnt], 0, sizeof(fDevClipBounds)); 255 } 256 builder.finish(); 257 sk_sp<GrBuffer> cachedVertexBuffer(rp->findByUniqueKey<GrBuffer>(key)); 258 int actualCount; 259 SkScalar tol = GrPathUtils::kDefaultTolerance; 260 tol = GrPathUtils::scaleToleranceToSrc(tol, fViewMatrix, fShape.bounds()); 261 if (cache_match(cachedVertexBuffer.get(), tol, &actualCount)) { 262 this->drawVertices(target, gp, cachedVertexBuffer.get(), 0, actualCount); 263 return; 264 } 265 266 SkRect clipBounds = SkRect::Make(fDevClipBounds); 267 268 SkMatrix vmi; 269 if (!fViewMatrix.invert(&vmi)) { 270 return; 271 } 272 vmi.mapRect(&clipBounds); 273 bool isLinear; 274 bool canMapVB = GrCaps::kNone_MapFlags != target->caps().mapBufferFlags(); 275 StaticVertexAllocator allocator(gp->getVertexStride(), rp, canMapVB); 276 int count = GrTessellator::PathToTriangles(getPath(), tol, clipBounds, &allocator, 277 false, GrColor(), false, &isLinear); 278 if (count == 0) { 279 return; 280 } 281 this->drawVertices(target, gp, allocator.vertexBuffer(), 0, count); 282 TessInfo info; 283 info.fTolerance = isLinear ? 0 : tol; 284 info.fCount = count; 285 key.setCustomData(SkData::MakeWithCopy(&info, sizeof(info))); 286 rp->assignUniqueKeyToResource(key, allocator.vertexBuffer()); 287 fShape.addGenIDChangeListener(new PathInvalidator(key)); 288 } 289 290 void drawAA(Target* target, const GrGeometryProcessor* gp) { 291 SkASSERT(fAntiAlias); 292 SkPath path = getPath(); 293 if (path.isEmpty()) { 294 return; 295 } 296 SkRect clipBounds = SkRect::Make(fDevClipBounds); 297 path.transform(fViewMatrix); 298 SkScalar tol = GrPathUtils::kDefaultTolerance; 299 bool isLinear; 300 DynamicVertexAllocator allocator(gp->getVertexStride(), target); 301 int count = 302 GrTessellator::PathToTriangles(path, tol, clipBounds, &allocator, true, fColor, 303 fHelper.compatibleWithAlphaAsCoverage(), &isLinear); 304 if (count == 0) { 305 return; 306 } 307 this->drawVertices(target, gp, allocator.vertexBuffer(), allocator.firstVertex(), count); 308 } 309 310 void onPrepareDraws(Target* target) override { 311 sk_sp<GrGeometryProcessor> gp; 312 { 313 using namespace GrDefaultGeoProcFactory; 314 315 Color color(fColor); 316 LocalCoords::Type localCoordsType = fHelper.usesLocalCoords() 317 ? LocalCoords::kUsePosition_Type 318 : LocalCoords::kUnused_Type; 319 Coverage::Type coverageType; 320 if (fAntiAlias) { 321 color = Color(Color::kPremulGrColorAttribute_Type); 322 if (fHelper.compatibleWithAlphaAsCoverage()) { 323 coverageType = Coverage::kSolid_Type; 324 } else { 325 coverageType = Coverage::kAttribute_Type; 326 } 327 } else { 328 coverageType = Coverage::kSolid_Type; 329 } 330 if (fAntiAlias) { 331 gp = GrDefaultGeoProcFactory::MakeForDeviceSpace(color, coverageType, 332 localCoordsType, fViewMatrix); 333 } else { 334 gp = GrDefaultGeoProcFactory::Make(color, coverageType, localCoordsType, 335 fViewMatrix); 336 } 337 } 338 if (!gp.get()) { 339 return; 340 } 341 if (fAntiAlias) { 342 this->drawAA(target, gp.get()); 343 } else { 344 this->draw(target, gp.get()); 345 } 346 } 347 348 void drawVertices(Target* target, const GrGeometryProcessor* gp, const GrBuffer* vb, 349 int firstVertex, int count) { 350 GrMesh mesh(TESSELLATOR_WIREFRAME ? GrPrimitiveType::kLines : GrPrimitiveType::kTriangles); 351 mesh.setNonIndexedNonInstanced(count); 352 mesh.setVertexData(vb, firstVertex); 353 target->draw(gp, fHelper.makePipeline(target), mesh); 354 } 355 356 bool onCombineIfPossible(GrOp*, const GrCaps&) override { return false; } 357 358 Helper fHelper; 359 GrColor fColor; 360 GrShape fShape; 361 SkMatrix fViewMatrix; 362 SkIRect fDevClipBounds; 363 bool fAntiAlias; 364 365 typedef GrMeshDrawOp INHERITED; 366 }; 367 368 } // anonymous namespace 369 370 bool GrTessellatingPathRenderer::onDrawPath(const DrawPathArgs& args) { 371 GR_AUDIT_TRAIL_AUTO_FRAME(args.fRenderTargetContext->auditTrail(), 372 "GrTessellatingPathRenderer::onDrawPath"); 373 SkIRect clipBoundsI; 374 args.fClip->getConservativeBounds(args.fRenderTargetContext->width(), 375 args.fRenderTargetContext->height(), 376 &clipBoundsI); 377 std::unique_ptr<GrDrawOp> op = TessellatingPathOp::Make(std::move(args.fPaint), 378 *args.fShape, 379 *args.fViewMatrix, 380 clipBoundsI, 381 args.fAAType, 382 args.fUserStencilSettings); 383 args.fRenderTargetContext->addDrawOp(*args.fClip, std::move(op)); 384 return true; 385 } 386 387 /////////////////////////////////////////////////////////////////////////////////////////////////// 388 389 #if GR_TEST_UTILS 390 391 GR_DRAW_OP_TEST_DEFINE(TesselatingPathOp) { 392 SkMatrix viewMatrix = GrTest::TestMatrixInvertible(random); 393 SkPath path = GrTest::TestPath(random); 394 SkIRect devClipBounds = SkIRect::MakeLTRB( 395 random->nextU(), random->nextU(), random->nextU(), random->nextU()); 396 devClipBounds.sort(); 397 static constexpr GrAAType kAATypes[] = {GrAAType::kNone, GrAAType::kMSAA, GrAAType::kCoverage}; 398 GrAAType aaType; 399 do { 400 aaType = kAATypes[random->nextULessThan(SK_ARRAY_COUNT(kAATypes))]; 401 } while(GrAAType::kMSAA == aaType && GrFSAAType::kUnifiedMSAA != fsaaType); 402 GrStyle style; 403 do { 404 GrTest::TestStyle(random, &style); 405 } while (!style.isSimpleFill()); 406 GrShape shape(path, style); 407 return TessellatingPathOp::Make(std::move(paint), shape, viewMatrix, devClipBounds, aaType, 408 GrGetRandomStencil(random, context)); 409 } 410 411 #endif 412