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