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