GrAALinearizingConvexPathRenderer.cpp revision f8334781914363caf537f22f012fcd5c03c60dad
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 10#include "GrAAConvexTessellator.h" 11#include "GrContext.h" 12#include "GrDefaultGeoProcFactory.h" 13#include "GrDrawOpTest.h" 14#include "GrGeometryProcessor.h" 15#include "GrInvariantOutput.h" 16#include "GrOpFlushState.h" 17#include "GrPathUtils.h" 18#include "GrPipelineBuilder.h" 19#include "GrProcessor.h" 20#include "GrStyle.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 28static const int DEFAULT_BUFFER_SIZE = 100; 29 30// The thicker the stroke, the harder it is to produce high-quality results using tessellation. For 31// the time being, we simply drop back to software rendering above this stroke width. 32static const SkScalar kMaxStrokeWidth = 20.0; 33 34GrAALinearizingConvexPathRenderer::GrAALinearizingConvexPathRenderer() { 35} 36 37/////////////////////////////////////////////////////////////////////////////// 38 39bool GrAALinearizingConvexPathRenderer::onCanDrawPath(const CanDrawPathArgs& args) const { 40 if (GrAAType::kCoverage != args.fAAType) { 41 return false; 42 } 43 if (!args.fShape->knownToBeConvex()) { 44 return false; 45 } 46 if (args.fShape->style().pathEffect()) { 47 return false; 48 } 49 if (args.fShape->inverseFilled()) { 50 return false; 51 } 52 const SkStrokeRec& stroke = args.fShape->style().strokeRec(); 53 54 if (stroke.getStyle() == SkStrokeRec::kStroke_Style || 55 stroke.getStyle() == SkStrokeRec::kStrokeAndFill_Style) { 56 if (!args.fViewMatrix->isSimilarity()) { 57 return false; 58 } 59 SkScalar strokeWidth = args.fViewMatrix->getMaxScale() * stroke.getWidth(); 60 if (strokeWidth < 1.0f && stroke.getStyle() == SkStrokeRec::kStroke_Style) { 61 return false; 62 } 63 return strokeWidth <= kMaxStrokeWidth && 64 args.fShape->knownToBeClosed() && 65 stroke.getJoin() != SkPaint::Join::kRound_Join; 66 } 67 return stroke.getStyle() == SkStrokeRec::kFill_Style; 68} 69 70// extract the result vertices and indices from the GrAAConvexTessellator 71static void extract_verts(const GrAAConvexTessellator& tess, 72 void* vertices, 73 size_t vertexStride, 74 GrColor color, 75 uint16_t firstIndex, 76 uint16_t* idxs, 77 bool tweakAlphaForCoverage) { 78 intptr_t verts = reinterpret_cast<intptr_t>(vertices); 79 80 for (int i = 0; i < tess.numPts(); ++i) { 81 *((SkPoint*)((intptr_t)verts + i * vertexStride)) = tess.point(i); 82 } 83 84 // Make 'verts' point to the colors 85 verts += sizeof(SkPoint); 86 for (int i = 0; i < tess.numPts(); ++i) { 87 if (tweakAlphaForCoverage) { 88 SkASSERT(SkScalarRoundToInt(255.0f * tess.coverage(i)) <= 255); 89 unsigned scale = SkScalarRoundToInt(255.0f * tess.coverage(i)); 90 GrColor scaledColor = (0xff == scale) ? color : SkAlphaMulQ(color, scale); 91 *reinterpret_cast<GrColor*>(verts + i * vertexStride) = scaledColor; 92 } else { 93 *reinterpret_cast<GrColor*>(verts + i * vertexStride) = color; 94 *reinterpret_cast<float*>(verts + i * vertexStride + sizeof(GrColor)) = 95 tess.coverage(i); 96 } 97 } 98 99 for (int i = 0; i < tess.numIndices(); ++i) { 100 idxs[i] = tess.index(i) + firstIndex; 101 } 102} 103 104static sk_sp<GrGeometryProcessor> create_fill_gp(bool tweakAlphaForCoverage, 105 const SkMatrix& viewMatrix, 106 bool usesLocalCoords, 107 bool coverageIgnored) { 108 using namespace GrDefaultGeoProcFactory; 109 110 Color color(Color::kAttribute_Type); 111 Coverage::Type coverageType; 112 // TODO remove coverage if coverage is ignored 113 /*if (coverageIgnored) { 114 coverageType = Coverage::kNone_Type; 115 } else*/ if (tweakAlphaForCoverage) { 116 coverageType = Coverage::kSolid_Type; 117 } else { 118 coverageType = Coverage::kAttribute_Type; 119 } 120 Coverage coverage(coverageType); 121 LocalCoords localCoords(usesLocalCoords ? LocalCoords::kUsePosition_Type : 122 LocalCoords::kUnused_Type); 123 return MakeForDeviceSpace(color, coverage, localCoords, viewMatrix); 124} 125 126class AAFlatteningConvexPathOp final : public GrMeshDrawOp { 127public: 128 DEFINE_OP_CLASS_ID 129 static std::unique_ptr<GrDrawOp> Make(GrColor color, 130 const SkMatrix& viewMatrix, 131 const SkPath& path, 132 SkScalar strokeWidth, 133 SkStrokeRec::Style style, 134 SkPaint::Join join, 135 SkScalar miterLimit) { 136 return std::unique_ptr<GrDrawOp>(new AAFlatteningConvexPathOp( 137 color, viewMatrix, path, strokeWidth, style, join, miterLimit)); 138 } 139 140 const char* name() const override { return "AAFlatteningConvexPathOp"; } 141 142 SkString dumpInfo() const override { 143 SkString string; 144 for (const auto& path : fPaths) { 145 string.appendf( 146 "Color: 0x%08x, StrokeWidth: %.2f, Style: %d, Join: %d, " 147 "MiterLimit: %.2f\n", 148 path.fColor, path.fStrokeWidth, path.fStyle, path.fJoin, path.fMiterLimit); 149 } 150 string.append(DumpPipelineInfo(*this->pipeline())); 151 string.append(INHERITED::dumpInfo()); 152 return string; 153 } 154 155private: 156 AAFlatteningConvexPathOp(GrColor color, 157 const SkMatrix& viewMatrix, 158 const SkPath& path, 159 SkScalar strokeWidth, 160 SkStrokeRec::Style style, 161 SkPaint::Join join, 162 SkScalar miterLimit) 163 : INHERITED(ClassID()) { 164 fPaths.emplace_back( 165 PathData{color, viewMatrix, path, strokeWidth, style, join, miterLimit}); 166 167 // compute bounds 168 SkRect bounds = path.getBounds(); 169 SkScalar w = strokeWidth; 170 if (w > 0) { 171 w /= 2; 172 // If the half stroke width is < 1 then we effectively fallback to bevel joins. 173 if (SkPaint::kMiter_Join == join && w > 1.f) { 174 w *= miterLimit; 175 } 176 bounds.outset(w, w); 177 } 178 this->setTransformedBounds(bounds, viewMatrix, HasAABloat::kYes, IsZeroArea::kNo); 179 } 180 181 void getPipelineAnalysisInput(GrPipelineAnalysisDrawOpInput* input) const override { 182 input->pipelineColorInput()->setKnownFourComponents(fPaths[0].fColor); 183 input->pipelineCoverageInput()->setUnknownSingleComponent(); 184 } 185 186 void applyPipelineOptimizations(const GrPipelineOptimizations& optimizations) override { 187 if (!optimizations.readsColor()) { 188 fPaths[0].fColor = GrColor_ILLEGAL; 189 } 190 optimizations.getOverrideColorIfSet(&fPaths[0].fColor); 191 192 fColor = fPaths[0].fColor; 193 fUsesLocalCoords = optimizations.readsLocalCoords(); 194 fCoverageIgnored = !optimizations.readsCoverage(); 195 fCanTweakAlphaForCoverage = optimizations.canTweakAlphaForCoverage(); 196 } 197 198 void draw(GrMeshDrawOp::Target* target, const GrGeometryProcessor* gp, int vertexCount, 199 size_t vertexStride, void* vertices, int indexCount, uint16_t* indices) const { 200 if (vertexCount == 0 || indexCount == 0) { 201 return; 202 } 203 const GrBuffer* vertexBuffer; 204 GrMesh mesh; 205 int firstVertex; 206 void* verts = target->makeVertexSpace(vertexStride, vertexCount, &vertexBuffer, 207 &firstVertex); 208 if (!verts) { 209 SkDebugf("Could not allocate vertices\n"); 210 return; 211 } 212 memcpy(verts, vertices, vertexCount * vertexStride); 213 214 const GrBuffer* indexBuffer; 215 int firstIndex; 216 uint16_t* idxs = target->makeIndexSpace(indexCount, &indexBuffer, &firstIndex); 217 if (!idxs) { 218 SkDebugf("Could not allocate indices\n"); 219 return; 220 } 221 memcpy(idxs, indices, indexCount * sizeof(uint16_t)); 222 mesh.initIndexed(kTriangles_GrPrimitiveType, vertexBuffer, indexBuffer, firstVertex, 223 firstIndex, vertexCount, indexCount); 224 target->draw(gp, mesh); 225 } 226 227 void onPrepareDraws(Target* target) const override { 228 bool canTweakAlphaForCoverage = this->canTweakAlphaForCoverage(); 229 230 // Setup GrGeometryProcessor 231 sk_sp<GrGeometryProcessor> gp(create_fill_gp(canTweakAlphaForCoverage, 232 this->viewMatrix(), 233 this->usesLocalCoords(), 234 this->coverageIgnored())); 235 if (!gp) { 236 SkDebugf("Couldn't create a GrGeometryProcessor\n"); 237 return; 238 } 239 240 size_t vertexStride = gp->getVertexStride(); 241 242 SkASSERT(canTweakAlphaForCoverage ? 243 vertexStride == sizeof(GrDefaultGeoProcFactory::PositionColorAttr) : 244 vertexStride == sizeof(GrDefaultGeoProcFactory::PositionColorCoverageAttr)); 245 246 int instanceCount = fPaths.count(); 247 248 int vertexCount = 0; 249 int indexCount = 0; 250 int maxVertices = DEFAULT_BUFFER_SIZE; 251 int maxIndices = DEFAULT_BUFFER_SIZE; 252 uint8_t* vertices = (uint8_t*) sk_malloc_throw(maxVertices * vertexStride); 253 uint16_t* indices = (uint16_t*) sk_malloc_throw(maxIndices * sizeof(uint16_t)); 254 for (int i = 0; i < instanceCount; i++) { 255 const PathData& args = fPaths[i]; 256 GrAAConvexTessellator tess(args.fStyle, args.fStrokeWidth, 257 args.fJoin, args.fMiterLimit); 258 259 if (!tess.tessellate(args.fViewMatrix, args.fPath)) { 260 continue; 261 } 262 263 int currentIndices = tess.numIndices(); 264 SkASSERT(currentIndices <= UINT16_MAX); 265 if (indexCount + currentIndices > UINT16_MAX) { 266 // if we added the current instance, we would overflow the indices we can store in a 267 // uint16_t. Draw what we've got so far and reset. 268 this->draw(target, gp.get(), 269 vertexCount, vertexStride, vertices, indexCount, indices); 270 vertexCount = 0; 271 indexCount = 0; 272 } 273 int currentVertices = tess.numPts(); 274 if (vertexCount + currentVertices > maxVertices) { 275 maxVertices = SkTMax(vertexCount + currentVertices, maxVertices * 2); 276 vertices = (uint8_t*) sk_realloc_throw(vertices, maxVertices * vertexStride); 277 } 278 if (indexCount + currentIndices > maxIndices) { 279 maxIndices = SkTMax(indexCount + currentIndices, maxIndices * 2); 280 indices = (uint16_t*) sk_realloc_throw(indices, maxIndices * sizeof(uint16_t)); 281 } 282 283 extract_verts(tess, vertices + vertexStride * vertexCount, vertexStride, args.fColor, 284 vertexCount, indices + indexCount, canTweakAlphaForCoverage); 285 vertexCount += currentVertices; 286 indexCount += currentIndices; 287 } 288 this->draw(target, gp.get(), vertexCount, vertexStride, vertices, indexCount, indices); 289 sk_free(vertices); 290 sk_free(indices); 291 } 292 293 bool onCombineIfPossible(GrOp* t, const GrCaps& caps) override { 294 AAFlatteningConvexPathOp* that = t->cast<AAFlatteningConvexPathOp>(); 295 if (!GrPipeline::CanCombine(*this->pipeline(), this->bounds(), *that->pipeline(), 296 that->bounds(), caps)) { 297 return false; 298 } 299 300 SkASSERT(this->usesLocalCoords() == that->usesLocalCoords()); 301 if (this->usesLocalCoords() && !this->viewMatrix().cheapEqualTo(that->viewMatrix())) { 302 return false; 303 } 304 305 // In the event of two ops, one who can tweak, one who cannot, we just fall back to not 306 // tweaking 307 if (this->canTweakAlphaForCoverage() != that->canTweakAlphaForCoverage()) { 308 fCanTweakAlphaForCoverage = false; 309 } 310 311 fPaths.push_back_n(that->fPaths.count(), that->fPaths.begin()); 312 this->joinBounds(*that); 313 return true; 314 } 315 316 GrColor color() const { return fColor; } 317 bool usesLocalCoords() const { return fUsesLocalCoords; } 318 bool canTweakAlphaForCoverage() const { return fCanTweakAlphaForCoverage; } 319 const SkMatrix& viewMatrix() const { return fPaths[0].fViewMatrix; } 320 bool coverageIgnored() const { return fCoverageIgnored; } 321 322 struct PathData { 323 GrColor fColor; 324 SkMatrix fViewMatrix; 325 SkPath fPath; 326 SkScalar fStrokeWidth; 327 SkStrokeRec::Style fStyle; 328 SkPaint::Join fJoin; 329 SkScalar fMiterLimit; 330 }; 331 332 GrColor fColor; 333 bool fUsesLocalCoords; 334 bool fCoverageIgnored; 335 bool fCanTweakAlphaForCoverage; 336 SkSTArray<1, PathData, true> fPaths; 337 338 typedef GrMeshDrawOp INHERITED; 339}; 340 341bool GrAALinearizingConvexPathRenderer::onDrawPath(const DrawPathArgs& args) { 342 GR_AUDIT_TRAIL_AUTO_FRAME(args.fRenderTargetContext->auditTrail(), 343 "GrAALinearizingConvexPathRenderer::onDrawPath"); 344 SkASSERT(!args.fRenderTargetContext->isUnifiedMultisampled()); 345 SkASSERT(!args.fShape->isEmpty()); 346 SkASSERT(!args.fShape->style().pathEffect()); 347 348 SkPath path; 349 args.fShape->asPath(&path); 350 bool fill = args.fShape->style().isSimpleFill(); 351 const SkStrokeRec& stroke = args.fShape->style().strokeRec(); 352 SkScalar strokeWidth = fill ? -1.0f : stroke.getWidth(); 353 SkPaint::Join join = fill ? SkPaint::Join::kMiter_Join : stroke.getJoin(); 354 SkScalar miterLimit = stroke.getMiter(); 355 356 std::unique_ptr<GrDrawOp> op = 357 AAFlatteningConvexPathOp::Make(args.fPaint->getColor(), *args.fViewMatrix, path, 358 strokeWidth, stroke.getStyle(), join, miterLimit); 359 360 GrPipelineBuilder pipelineBuilder(*args.fPaint, args.fAAType); 361 pipelineBuilder.setUserStencil(args.fUserStencilSettings); 362 363 args.fRenderTargetContext->addDrawOp(pipelineBuilder, *args.fClip, std::move(op)); 364 365 return true; 366} 367 368/////////////////////////////////////////////////////////////////////////////////////////////////// 369 370#ifdef GR_TEST_UTILS 371 372DRAW_OP_TEST_DEFINE(AAFlatteningConvexPathOp) { 373 GrColor color = GrRandomColor(random); 374 SkMatrix viewMatrix = GrTest::TestMatrixPreservesRightAngles(random); 375 SkPath path = GrTest::TestPathConvex(random); 376 377 SkStrokeRec::Style styles[3] = { SkStrokeRec::kFill_Style, 378 SkStrokeRec::kStroke_Style, 379 SkStrokeRec::kStrokeAndFill_Style }; 380 381 SkStrokeRec::Style style = styles[random->nextU() % 3]; 382 383 SkScalar strokeWidth = -1.f; 384 SkPaint::Join join = SkPaint::kMiter_Join; 385 SkScalar miterLimit = 0.5f; 386 387 if (SkStrokeRec::kFill_Style != style) { 388 strokeWidth = random->nextRangeF(1.0f, 10.0f); 389 if (random->nextBool()) { 390 join = SkPaint::kMiter_Join; 391 } else { 392 join = SkPaint::kBevel_Join; 393 } 394 miterLimit = random->nextRangeF(0.5f, 2.0f); 395 } 396 397 return AAFlatteningConvexPathOp::Make(color, viewMatrix, path, strokeWidth, style, join, 398 miterLimit); 399} 400 401#endif 402