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