1 /* 2 * Copyright 2017 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 "SkTypes.h" 9 10 #if SK_SUPPORT_GPU 11 12 #include "GrClip.h" 13 #include "GrContextPriv.h" 14 #include "GrMemoryPool.h" 15 #include "GrPathUtils.h" 16 #include "GrRenderTargetContext.h" 17 #include "GrRenderTargetContextPriv.h" 18 #include "GrResourceProvider.h" 19 #include "Sample.h" 20 #include "SkCanvas.h" 21 #include "SkMakeUnique.h" 22 #include "SkPaint.h" 23 #include "SkPath.h" 24 #include "SkRectPriv.h" 25 #include "ccpr/GrCCCoverageProcessor.h" 26 #include "ccpr/GrCCFillGeometry.h" 27 #include "ccpr/GrCCStroker.h" 28 #include "gl/GrGLGpu.h" 29 #include "glsl/GrGLSLFragmentProcessor.h" 30 #include "ops/GrDrawOp.h" 31 32 using TriPointInstance = GrCCCoverageProcessor::TriPointInstance; 33 using QuadPointInstance = GrCCCoverageProcessor::QuadPointInstance; 34 using PrimitiveType = GrCCCoverageProcessor::PrimitiveType; 35 36 static constexpr float kDebugBloat = 40; 37 38 /** 39 * This sample visualizes the AA bloat geometry generated by the ccpr geometry shaders. It 40 * increases the AA bloat by 50x and outputs color instead of coverage (coverage=+1 -> green, 41 * coverage=0 -> black, coverage=-1 -> red). Use the keys 1-7 to cycle through the different 42 * geometry processors. 43 */ 44 class CCPRGeometryView : public Sample { 45 public: 46 CCPRGeometryView() { this->updateGpuData(); } 47 void onDrawContent(SkCanvas*) override; 48 49 Sample::Click* onFindClickHandler(SkScalar x, SkScalar y, unsigned) override; 50 bool onClick(Sample::Click*) override; 51 bool onQuery(Sample::Event* evt) override; 52 53 private: 54 class Click; 55 class DrawCoverageCountOp; 56 class VisualizeCoverageCountFP; 57 58 void updateAndInval() { this->updateGpuData(); } 59 60 void updateGpuData(); 61 62 PrimitiveType fPrimitiveType = PrimitiveType::kTriangles; 63 SkCubicType fCubicType; 64 SkMatrix fCubicKLM; 65 66 SkPoint fPoints[4] = { 67 {100.05f, 100.05f}, {400.75f, 100.05f}, {400.75f, 300.95f}, {100.05f, 300.95f}}; 68 69 float fConicWeight = .5; 70 float fStrokeWidth = 40; 71 bool fDoStroke = false; 72 73 SkTArray<TriPointInstance> fTriPointInstances; 74 SkTArray<QuadPointInstance> fQuadPointInstances; 75 SkPath fPath; 76 77 typedef Sample INHERITED; 78 }; 79 80 class CCPRGeometryView::DrawCoverageCountOp : public GrDrawOp { 81 DEFINE_OP_CLASS_ID 82 83 public: 84 DrawCoverageCountOp(CCPRGeometryView* view) : INHERITED(ClassID()), fView(view) { 85 this->setBounds(SkRect::MakeIWH(fView->width(), fView->height()), GrOp::HasAABloat::kNo, 86 GrOp::IsZeroArea::kNo); 87 } 88 89 const char* name() const override { 90 return "[Testing/Sample code] CCPRGeometryView::DrawCoverageCountOp"; 91 } 92 93 private: 94 FixedFunctionFlags fixedFunctionFlags() const override { return FixedFunctionFlags::kNone; } 95 GrProcessorSet::Analysis finalize( 96 const GrCaps&, const GrAppliedClip*, GrFSAAType, GrClampType) override { 97 return GrProcessorSet::EmptySetAnalysis(); 98 } 99 void onPrepare(GrOpFlushState*) override {} 100 void onExecute(GrOpFlushState*, const SkRect& chainBounds) override; 101 102 CCPRGeometryView* fView; 103 104 typedef GrDrawOp INHERITED; 105 }; 106 107 class CCPRGeometryView::VisualizeCoverageCountFP : public GrFragmentProcessor { 108 public: 109 VisualizeCoverageCountFP() : GrFragmentProcessor(kTestFP_ClassID, kNone_OptimizationFlags) {} 110 111 private: 112 const char* name() const override { 113 return "[Testing/Sample code] CCPRGeometryView::VisualizeCoverageCountFP"; 114 } 115 std::unique_ptr<GrFragmentProcessor> clone() const override { 116 return skstd::make_unique<VisualizeCoverageCountFP>(); 117 } 118 void onGetGLSLProcessorKey(const GrShaderCaps&, GrProcessorKeyBuilder*) const override {} 119 bool onIsEqual(const GrFragmentProcessor&) const override { return true; } 120 121 class Impl : public GrGLSLFragmentProcessor { 122 void emitCode(EmitArgs& args) override { 123 GrGLSLFPFragmentBuilder* f = args.fFragBuilder; 124 f->codeAppendf("half count = %s.a;", args.fInputColor); 125 f->codeAppendf("%s = half4(clamp(-count, 0, 1), clamp(+count, 0, 1), 0, abs(count));", 126 args.fOutputColor); 127 } 128 }; 129 130 GrGLSLFragmentProcessor* onCreateGLSLInstance() const override { return new Impl; } 131 }; 132 133 static void draw_klm_line(int w, int h, SkCanvas* canvas, const SkScalar line[3], SkColor color) { 134 SkPoint p1, p2; 135 if (SkScalarAbs(line[1]) > SkScalarAbs(line[0])) { 136 // Draw from vertical edge to vertical edge. 137 p1 = {0, -line[2] / line[1]}; 138 p2 = {(SkScalar)w, (-line[2] - w * line[0]) / line[1]}; 139 } else { 140 // Draw from horizontal edge to horizontal edge. 141 p1 = {-line[2] / line[0], 0}; 142 p2 = {(-line[2] - h * line[1]) / line[0], (SkScalar)h}; 143 } 144 145 SkPaint linePaint; 146 linePaint.setColor(color); 147 linePaint.setAlpha(128); 148 linePaint.setStyle(SkPaint::kStroke_Style); 149 linePaint.setStrokeWidth(0); 150 linePaint.setAntiAlias(true); 151 canvas->drawLine(p1, p2, linePaint); 152 } 153 154 void CCPRGeometryView::onDrawContent(SkCanvas* canvas) { 155 canvas->clear(SK_ColorBLACK); 156 157 if (!fDoStroke) { 158 SkPaint outlinePaint; 159 outlinePaint.setColor(0x80ffffff); 160 outlinePaint.setStyle(SkPaint::kStroke_Style); 161 outlinePaint.setStrokeWidth(0); 162 outlinePaint.setAntiAlias(true); 163 canvas->drawPath(fPath, outlinePaint); 164 } 165 166 #if 0 167 SkPaint gridPaint; 168 gridPaint.setColor(0x10000000); 169 gridPaint.setStyle(SkPaint::kStroke_Style); 170 gridPaint.setStrokeWidth(0); 171 gridPaint.setAntiAlias(true); 172 for (int y = 0; y < this->height(); y += kDebugBloat) { 173 canvas->drawLine(0, y, this->width(), y, gridPaint); 174 } 175 for (int x = 0; x < this->width(); x += kDebugBloat) { 176 canvas->drawLine(x, 0, x, this->height(), outlinePaint); 177 } 178 #endif 179 180 SkString caption; 181 if (GrRenderTargetContext* rtc = canvas->internal_private_accessTopLayerRenderTargetContext()) { 182 // Render coverage count. 183 GrContext* ctx = canvas->getGrContext(); 184 SkASSERT(ctx); 185 186 GrOpMemoryPool* pool = ctx->priv().opMemoryPool(); 187 188 const GrBackendFormat format = 189 ctx->priv().caps()->getBackendFormatFromGrColorType(GrColorType::kAlpha_F16, 190 GrSRGBEncoded::kNo); 191 sk_sp<GrRenderTargetContext> ccbuff = 192 ctx->priv().makeDeferredRenderTargetContext(format, SkBackingFit::kApprox, 193 this->width(), this->height(), 194 kAlpha_half_GrPixelConfig, 195 nullptr); 196 SkASSERT(ccbuff); 197 ccbuff->clear(nullptr, SK_PMColor4fTRANSPARENT, 198 GrRenderTargetContext::CanClearFullscreen::kYes); 199 ccbuff->priv().testingOnly_addDrawOp(pool->allocate<DrawCoverageCountOp>(this)); 200 201 // Visualize coverage count in main canvas. 202 GrPaint paint; 203 paint.addColorFragmentProcessor( 204 GrSimpleTextureEffect::Make(sk_ref_sp(ccbuff->asTextureProxy()), SkMatrix::I())); 205 paint.addColorFragmentProcessor( 206 skstd::make_unique<VisualizeCoverageCountFP>()); 207 paint.setPorterDuffXPFactory(SkBlendMode::kSrcOver); 208 rtc->drawRect(GrNoClip(), std::move(paint), GrAA::kNo, SkMatrix::I(), 209 SkRect::MakeIWH(this->width(), this->height())); 210 211 // Add label. 212 caption.appendf("PrimitiveType_%s", 213 GrCCCoverageProcessor::PrimitiveTypeName(fPrimitiveType)); 214 if (PrimitiveType::kCubics == fPrimitiveType) { 215 caption.appendf(" (%s)", SkCubicTypeName(fCubicType)); 216 } else if (PrimitiveType::kConics == fPrimitiveType) { 217 caption.appendf(" (w=%f)", fConicWeight); 218 } 219 if (fDoStroke) { 220 caption.appendf(" (stroke_width=%f)", fStrokeWidth); 221 } 222 } else { 223 caption = "Use GPU backend to visualize geometry."; 224 } 225 226 SkPaint pointsPaint; 227 pointsPaint.setColor(SK_ColorBLUE); 228 pointsPaint.setStrokeWidth(8); 229 pointsPaint.setAntiAlias(true); 230 231 if (PrimitiveType::kCubics == fPrimitiveType) { 232 canvas->drawPoints(SkCanvas::kPoints_PointMode, 4, fPoints, pointsPaint); 233 if (!fDoStroke) { 234 int w = this->width(), h = this->height(); 235 draw_klm_line(w, h, canvas, &fCubicKLM[0], SK_ColorYELLOW); 236 draw_klm_line(w, h, canvas, &fCubicKLM[3], SK_ColorBLUE); 237 draw_klm_line(w, h, canvas, &fCubicKLM[6], SK_ColorRED); 238 } 239 } else { 240 canvas->drawPoints(SkCanvas::kPoints_PointMode, 2, fPoints, pointsPaint); 241 canvas->drawPoints(SkCanvas::kPoints_PointMode, 1, fPoints + 3, pointsPaint); 242 } 243 244 SkFont font(nullptr, 20); 245 SkPaint captionPaint; 246 captionPaint.setColor(SK_ColorWHITE); 247 canvas->drawString(caption, 10, 30, font, captionPaint); 248 } 249 250 void CCPRGeometryView::updateGpuData() { 251 using Verb = GrCCFillGeometry::Verb; 252 fTriPointInstances.reset(); 253 fQuadPointInstances.reset(); 254 255 fPath.reset(); 256 fPath.moveTo(fPoints[0]); 257 258 if (PrimitiveType::kCubics == fPrimitiveType) { 259 double t[2], s[2]; 260 fCubicType = GrPathUtils::getCubicKLM(fPoints, &fCubicKLM, t, s); 261 GrCCFillGeometry geometry; 262 geometry.beginContour(fPoints[0]); 263 geometry.cubicTo(fPoints, kDebugBloat / 2, kDebugBloat / 2); 264 geometry.endContour(); 265 int ptsIdx = 0; 266 for (Verb verb : geometry.verbs()) { 267 switch (verb) { 268 case Verb::kLineTo: 269 ++ptsIdx; 270 continue; 271 case Verb::kMonotonicQuadraticTo: 272 ptsIdx += 2; 273 continue; 274 case Verb::kMonotonicCubicTo: 275 fQuadPointInstances.push_back().set(&geometry.points()[ptsIdx], 0, 0); 276 ptsIdx += 3; 277 continue; 278 default: 279 continue; 280 } 281 } 282 fPath.cubicTo(fPoints[1], fPoints[2], fPoints[3]); 283 } else if (PrimitiveType::kTriangles != fPrimitiveType) { 284 SkPoint P3[3] = {fPoints[0], fPoints[1], fPoints[3]}; 285 GrCCFillGeometry geometry; 286 geometry.beginContour(P3[0]); 287 if (PrimitiveType::kQuadratics == fPrimitiveType) { 288 geometry.quadraticTo(P3); 289 fPath.quadTo(fPoints[1], fPoints[3]); 290 } else { 291 SkASSERT(PrimitiveType::kConics == fPrimitiveType); 292 geometry.conicTo(P3, fConicWeight); 293 fPath.conicTo(fPoints[1], fPoints[3], fConicWeight); 294 } 295 geometry.endContour(); 296 int ptsIdx = 0, conicWeightIdx = 0; 297 for (Verb verb : geometry.verbs()) { 298 if (Verb::kBeginContour == verb || 299 Verb::kEndOpenContour == verb || 300 Verb::kEndClosedContour == verb) { 301 continue; 302 } 303 if (Verb::kLineTo == verb) { 304 ++ptsIdx; 305 continue; 306 } 307 SkASSERT(Verb::kMonotonicQuadraticTo == verb || Verb::kMonotonicConicTo == verb); 308 if (PrimitiveType::kQuadratics == fPrimitiveType && 309 Verb::kMonotonicQuadraticTo == verb) { 310 fTriPointInstances.push_back().set(&geometry.points()[ptsIdx], Sk2f(0, 0)); 311 } else if (PrimitiveType::kConics == fPrimitiveType && 312 Verb::kMonotonicConicTo == verb) { 313 fQuadPointInstances.push_back().setW(&geometry.points()[ptsIdx], Sk2f(0, 0), 314 geometry.getConicWeight(conicWeightIdx++)); 315 } 316 ptsIdx += 2; 317 } 318 } else { 319 fTriPointInstances.push_back().set(fPoints[0], fPoints[1], fPoints[3], Sk2f(0, 0)); 320 fPath.lineTo(fPoints[1]); 321 fPath.lineTo(fPoints[3]); 322 fPath.close(); 323 } 324 } 325 326 void CCPRGeometryView::DrawCoverageCountOp::onExecute(GrOpFlushState* state, 327 const SkRect& chainBounds) { 328 GrResourceProvider* rp = state->resourceProvider(); 329 GrContext* context = state->gpu()->getContext(); 330 GrGLGpu* glGpu = GrBackendApi::kOpenGL == context->backend() 331 ? static_cast<GrGLGpu*>(state->gpu()) 332 : nullptr; 333 if (glGpu) { 334 glGpu->handleDirtyContext(); 335 // GR_GL_CALL(glGpu->glInterface(), PolygonMode(GR_GL_FRONT_AND_BACK, GR_GL_LINE)); 336 GR_GL_CALL(glGpu->glInterface(), Enable(GR_GL_LINE_SMOOTH)); 337 } 338 339 GrPipeline pipeline(GrScissorTest::kDisabled, SkBlendMode::kPlus); 340 341 if (!fView->fDoStroke) { 342 GrCCCoverageProcessor proc(rp, fView->fPrimitiveType); 343 SkDEBUGCODE(proc.enableDebugBloat(kDebugBloat)); 344 345 SkSTArray<1, GrMesh> mesh; 346 if (PrimitiveType::kCubics == fView->fPrimitiveType || 347 PrimitiveType::kConics == fView->fPrimitiveType) { 348 sk_sp<GrGpuBuffer> instBuff( 349 rp->createBuffer(fView->fQuadPointInstances.count() * sizeof(QuadPointInstance), 350 GrGpuBufferType::kVertex, kDynamic_GrAccessPattern, 351 fView->fQuadPointInstances.begin())); 352 if (!fView->fQuadPointInstances.empty() && instBuff) { 353 proc.appendMesh(std::move(instBuff), fView->fQuadPointInstances.count(), 0, &mesh); 354 } 355 } else { 356 sk_sp<GrGpuBuffer> instBuff( 357 rp->createBuffer(fView->fTriPointInstances.count() * sizeof(TriPointInstance), 358 GrGpuBufferType::kVertex, kDynamic_GrAccessPattern, 359 fView->fTriPointInstances.begin())); 360 if (!fView->fTriPointInstances.empty() && instBuff) { 361 proc.appendMesh(std::move(instBuff), fView->fTriPointInstances.count(), 0, &mesh); 362 } 363 } 364 365 if (!mesh.empty()) { 366 SkASSERT(1 == mesh.count()); 367 proc.draw(state, pipeline, nullptr, mesh.begin(), 1, this->bounds()); 368 } 369 } else if (PrimitiveType::kConics != fView->fPrimitiveType) { // No conic stroke support yet. 370 GrCCStroker stroker(0,0,0); 371 372 SkPaint p; 373 p.setStyle(SkPaint::kStroke_Style); 374 p.setStrokeWidth(fView->fStrokeWidth); 375 p.setStrokeJoin(SkPaint::kMiter_Join); 376 p.setStrokeMiter(4); 377 // p.setStrokeCap(SkPaint::kRound_Cap); 378 stroker.parseDeviceSpaceStroke(fView->fPath, SkPathPriv::PointData(fView->fPath), 379 SkStrokeRec(p), p.getStrokeWidth(), GrScissorTest::kDisabled, 380 SkIRect::MakeWH(fView->width(), fView->height()), {0, 0}); 381 GrCCStroker::BatchID batchID = stroker.closeCurrentBatch(); 382 383 GrOnFlushResourceProvider onFlushRP(context->priv().drawingManager()); 384 stroker.prepareToDraw(&onFlushRP); 385 386 SkIRect ibounds; 387 this->bounds().roundOut(&ibounds); 388 stroker.drawStrokes(state, batchID, ibounds); 389 } 390 391 if (glGpu) { 392 context->resetContext(kMisc_GrGLBackendState); 393 } 394 } 395 396 class CCPRGeometryView::Click : public Sample::Click { 397 public: 398 Click(Sample* target, int ptIdx) : Sample::Click(target), fPtIdx(ptIdx) {} 399 400 void doClick(SkPoint points[]) { 401 if (fPtIdx >= 0) { 402 this->dragPoint(points, fPtIdx); 403 } else { 404 for (int i = 0; i < 4; ++i) { 405 this->dragPoint(points, i); 406 } 407 } 408 } 409 410 private: 411 void dragPoint(SkPoint points[], int idx) { 412 SkIPoint delta = fICurr - fIPrev; 413 points[idx] += SkPoint::Make(delta.x(), delta.y()); 414 } 415 416 int fPtIdx; 417 }; 418 419 Sample::Click* CCPRGeometryView::onFindClickHandler(SkScalar x, SkScalar y, unsigned) { 420 for (int i = 0; i < 4; ++i) { 421 if (PrimitiveType::kCubics != fPrimitiveType && 2 == i) { 422 continue; 423 } 424 if (fabs(x - fPoints[i].x()) < 20 && fabsf(y - fPoints[i].y()) < 20) { 425 return new Click(this, i); 426 } 427 } 428 return new Click(this, -1); 429 } 430 431 bool CCPRGeometryView::onClick(Sample::Click* click) { 432 Click* myClick = (Click*)click; 433 myClick->doClick(fPoints); 434 this->updateAndInval(); 435 return true; 436 } 437 438 bool CCPRGeometryView::onQuery(Sample::Event* evt) { 439 if (Sample::TitleQ(*evt)) { 440 Sample::TitleR(evt, "CCPRGeometry"); 441 return true; 442 } 443 SkUnichar unichar; 444 if (Sample::CharQ(*evt, &unichar)) { 445 if (unichar >= '1' && unichar <= '4') { 446 fPrimitiveType = PrimitiveType(unichar - '1'); 447 if (fPrimitiveType >= PrimitiveType::kWeightedTriangles) { 448 fPrimitiveType = (PrimitiveType) ((int)fPrimitiveType + 1); 449 } 450 this->updateAndInval(); 451 return true; 452 } 453 float* valueToScale = nullptr; 454 if (fDoStroke) { 455 valueToScale = &fStrokeWidth; 456 } else if (PrimitiveType::kConics == fPrimitiveType) { 457 valueToScale = &fConicWeight; 458 } 459 if (valueToScale) { 460 if (unichar == '+') { 461 *valueToScale *= 2; 462 this->updateAndInval(); 463 return true; 464 } 465 if (unichar == '+' || unichar == '=') { 466 *valueToScale *= 5/4.f; 467 this->updateAndInval(); 468 return true; 469 } 470 if (unichar == '-') { 471 *valueToScale *= 4/5.f; 472 this->updateAndInval(); 473 return true; 474 } 475 if (unichar == '_') { 476 *valueToScale *= .5f; 477 this->updateAndInval(); 478 return true; 479 } 480 } 481 if (unichar == 'D') { 482 SkDebugf(" SkPoint fPoints[4] = {\n"); 483 SkDebugf(" {%ff, %ff},\n", fPoints[0].x(), fPoints[0].y()); 484 SkDebugf(" {%ff, %ff},\n", fPoints[1].x(), fPoints[1].y()); 485 SkDebugf(" {%ff, %ff},\n", fPoints[2].x(), fPoints[2].y()); 486 SkDebugf(" {%ff, %ff}\n", fPoints[3].x(), fPoints[3].y()); 487 SkDebugf(" };\n"); 488 return true; 489 } 490 if (unichar == 'S') { 491 fDoStroke = !fDoStroke; 492 this->updateAndInval(); 493 } 494 } 495 return this->INHERITED::onQuery(evt); 496 } 497 498 DEF_SAMPLE(return new CCPRGeometryView;) 499 500 #endif // SK_SUPPORT_GPU 501