1 /* 2 * Copyright 2013 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 // This test only works with the GPU backend. 9 10 #include "gm/gm.h" 11 #include "include/core/SkBlendMode.h" 12 #include "include/core/SkCanvas.h" 13 #include "include/core/SkMatrix.h" 14 #include "include/core/SkPaint.h" 15 #include "include/core/SkPoint.h" 16 #include "include/core/SkPoint3.h" 17 #include "include/core/SkRect.h" 18 #include "include/core/SkRefCnt.h" 19 #include "include/core/SkScalar.h" 20 #include "include/core/SkSize.h" 21 #include "include/core/SkString.h" 22 #include "include/core/SkTypes.h" 23 #include "include/gpu/ganesh/GrRecordingContext.h" 24 #include "include/private/SkColorData.h" 25 #include "include/private/gpu/ganesh/GrTypesPriv.h" 26 #include "src/base/SkRandom.h" 27 #include "src/core/SkCanvasPriv.h" 28 #include "src/core/SkGeometry.h" 29 #include "src/core/SkPointPriv.h" 30 #include "src/gpu/ganesh/GrCanvas.h" 31 #include "src/gpu/ganesh/GrCaps.h" 32 #include "src/gpu/ganesh/GrDirectContextPriv.h" 33 #include "src/gpu/ganesh/GrGeometryProcessor.h" 34 #include "src/gpu/ganesh/GrMemoryPool.h" 35 #include "src/gpu/ganesh/GrOpFlushState.h" 36 #include "src/gpu/ganesh/GrOpsRenderPass.h" 37 #include "src/gpu/ganesh/GrPaint.h" 38 #include "src/gpu/ganesh/GrProcessorAnalysis.h" 39 #include "src/gpu/ganesh/GrProcessorSet.h" 40 #include "src/gpu/ganesh/GrProgramInfo.h" 41 #include "src/gpu/ganesh/GrRecordingContextPriv.h" 42 #include "src/gpu/ganesh/GrUserStencilSettings.h" 43 #include "src/gpu/ganesh/SurfaceDrawContext.h" 44 #include "src/gpu/ganesh/effects/GrBezierEffect.h" 45 #include "src/gpu/ganesh/effects/GrPorterDuffXferProcessor.h" 46 #include "src/gpu/ganesh/geometry/GrPathUtils.h" 47 #include "src/gpu/ganesh/ops/GrDrawOp.h" 48 #include "src/gpu/ganesh/ops/GrMeshDrawOp.h" 49 #include "src/gpu/ganesh/ops/GrOp.h" 50 #include "src/gpu/ganesh/ops/GrSimpleMeshDrawOpHelper.h" 51 52 #include <memory> 53 #include <utility> 54 55 class GrAppliedClip; 56 57 namespace skiagm { 58 59 class BezierTestOp : public GrMeshDrawOp { 60 public: fixedFunctionFlags() const61 FixedFunctionFlags fixedFunctionFlags() const override { return FixedFunctionFlags::kNone; } 62 finalize(const GrCaps & caps,const GrAppliedClip * clip,GrClampType clampType)63 GrProcessorSet::Analysis finalize( 64 const GrCaps& caps, const GrAppliedClip* clip, GrClampType clampType) override { 65 return fProcessorSet.finalize( 66 fColor, GrProcessorAnalysisCoverage::kSingleChannel, clip, 67 &GrUserStencilSettings::kUnused, caps, clampType, &fColor); 68 } 69 visitProxies(const GrVisitProxyFunc & func) const70 void visitProxies(const GrVisitProxyFunc& func) const override { 71 if (fProgramInfo) { 72 fProgramInfo->visitFPProxies(func); 73 } else { 74 fProcessorSet.visitProxies(func); 75 } 76 } 77 78 protected: BezierTestOp(const SkRect & rect,const SkPMColor4f & color,int32_t classID)79 BezierTestOp(const SkRect& rect, const SkPMColor4f& color, int32_t classID) 80 : INHERITED(classID) 81 , fRect(rect) 82 , fColor(color) 83 , fProcessorSet(SkBlendMode::kSrc) { 84 this->setBounds(rect, HasAABloat::kYes, IsHairline::kNo); 85 } 86 87 virtual GrGeometryProcessor* makeGP(const GrCaps& caps, SkArenaAlloc* arena) = 0; 88 programInfo()89 GrProgramInfo* programInfo() override { return fProgramInfo; } 90 onCreateProgramInfo(const GrCaps * caps,SkArenaAlloc * arena,const GrSurfaceProxyView & writeView,bool usesMSAASurface,GrAppliedClip && appliedClip,const GrDstProxyView & dstProxyView,GrXferBarrierFlags renderPassXferBarriers,GrLoadOp colorLoadOp)91 void onCreateProgramInfo(const GrCaps* caps, 92 SkArenaAlloc* arena, 93 const GrSurfaceProxyView& writeView, 94 bool usesMSAASurface, 95 GrAppliedClip&& appliedClip, 96 const GrDstProxyView& dstProxyView, 97 GrXferBarrierFlags renderPassXferBarriers, 98 GrLoadOp colorLoadOp) override { 99 auto gp = this->makeGP(*caps, arena); 100 if (!gp) { 101 return; 102 } 103 104 GrPipeline::InputFlags flags = GrPipeline::InputFlags::kNone; 105 106 fProgramInfo = GrSimpleMeshDrawOpHelper::CreateProgramInfo(caps, arena, writeView, 107 usesMSAASurface, 108 std::move(appliedClip), 109 dstProxyView, gp, 110 std::move(fProcessorSet), 111 GrPrimitiveType::kTriangles, 112 renderPassXferBarriers, 113 colorLoadOp, 114 flags); 115 } 116 onExecute(GrOpFlushState * flushState,const SkRect & chainBounds)117 void onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) final { 118 if (!fProgramInfo) { 119 this->createProgramInfo(flushState); 120 } 121 122 if (!fProgramInfo) { 123 return; 124 } 125 126 flushState->bindPipelineAndScissorClip(*fProgramInfo, chainBounds); 127 flushState->bindTextures(fProgramInfo->geomProc(), nullptr, fProgramInfo->pipeline()); 128 flushState->drawMesh(*fMesh); 129 } 130 rect() const131 const SkRect& rect() const { return fRect; } color() const132 const SkPMColor4f& color() const { return fColor; } 133 134 protected: 135 GrSimpleMesh* fMesh = nullptr; // filled in by the derived classes 136 137 private: 138 SkRect fRect; 139 SkPMColor4f fColor; 140 GrProcessorSet fProcessorSet; 141 GrProgramInfo* fProgramInfo = nullptr; 142 143 using INHERITED = GrMeshDrawOp; 144 }; 145 146 /** 147 * This GM directly exercises effects that draw Bezier curves in the GPU backend. 148 */ 149 class BezierConicTestOp : public BezierTestOp { 150 public: 151 DEFINE_OP_CLASS_ID 152 name() const153 const char* name() const final { return "BezierConicTestOp"; } 154 Make(GrRecordingContext * context,const SkRect & rect,const SkPMColor4f & color,const SkMatrix & klm)155 static GrOp::Owner Make(GrRecordingContext* context, 156 const SkRect& rect, 157 const SkPMColor4f& color, 158 const SkMatrix& klm) { 159 return GrOp::Make<BezierConicTestOp>(context, rect, color, klm); 160 } 161 162 private: 163 friend class ::GrOp; // for ctor 164 BezierConicTestOp(const SkRect & rect,const SkPMColor4f & color,const SkMatrix & klm)165 BezierConicTestOp(const SkRect& rect, const SkPMColor4f& color, const SkMatrix& klm) 166 : INHERITED(rect, color, ClassID()) 167 , fKLM(klm) {} 168 169 struct Vertex { 170 SkPoint fPosition; 171 float fKLM[4]; // The last value is ignored. The effect expects a vec4f. 172 }; 173 makeGP(const GrCaps & caps,SkArenaAlloc * arena)174 GrGeometryProcessor* makeGP(const GrCaps& caps, SkArenaAlloc* arena) final { 175 auto tmp = GrConicEffect::Make(arena, this->color(), SkMatrix::I(), caps, SkMatrix::I(), 176 false); 177 if (!tmp) { 178 return nullptr; 179 } 180 SkASSERT(tmp->vertexStride() == sizeof(Vertex)); 181 return tmp; 182 } 183 onPrepareDraws(GrMeshDrawTarget * target)184 void onPrepareDraws(GrMeshDrawTarget* target) final { 185 QuadHelper helper(target, sizeof(Vertex), 1); 186 Vertex* verts = reinterpret_cast<Vertex*>(helper.vertices()); 187 if (!verts) { 188 return; 189 } 190 SkRect rect = this->rect(); 191 SkPointPriv::SetRectTriStrip(&verts[0].fPosition, rect, sizeof(Vertex)); 192 for (int v = 0; v < 4; ++v) { 193 SkPoint3 pt3 = {verts[v].fPosition.x(), verts[v].fPosition.y(), 1.f}; 194 fKLM.mapHomogeneousPoints((SkPoint3* ) verts[v].fKLM, &pt3, 1); 195 } 196 197 fMesh = helper.mesh(); 198 } 199 200 SkMatrix fKLM; 201 202 inline static constexpr int kVertsPerCubic = 4; 203 inline static constexpr int kIndicesPerCubic = 6; 204 205 using INHERITED = BezierTestOp; 206 }; 207 208 209 /** 210 * This GM directly exercises effects that draw Bezier curves in the GPU backend. 211 */ 212 class BezierConicEffects : public GpuGM { 213 public: BezierConicEffects()214 BezierConicEffects() { 215 this->setBGColor(0xFFFFFFFF); 216 } 217 218 protected: 219 static const int kNumConics = 10; 220 static const int kCellWidth = 128; 221 static const int kCellHeight = 128; 222 getName() const223 SkString getName() const override { return SkString("bezier_conic_effects"); } 224 getISize()225 SkISize getISize() override { return SkISize::Make(kCellWidth, kNumConics * kCellHeight); } 226 onDraw(GrRecordingContext * rContext,SkCanvas * canvas,SkString * errorMsg)227 DrawResult onDraw(GrRecordingContext* rContext, SkCanvas* canvas, SkString* errorMsg) override { 228 auto sdc = skgpu::ganesh::TopDeviceSurfaceDrawContext(canvas); 229 if (!sdc) { 230 *errorMsg = kErrorMsg_DrawSkippedGpuOnly; 231 return DrawResult::kSkip; 232 } 233 234 const SkScalar w = kCellWidth, h = kCellHeight; 235 const SkPMColor4f kOpaqueBlack = SkPMColor4f::FromBytes_RGBA(0xff000000); 236 237 const SkPoint baseControlPts[kNumConics][3] = { 238 { { 0.31f * w, 0.01f * h}, { 0.48f * w, 0.74f * h }, { 0.19f * w, 0.33f * h } }, 239 { { 0.00f * w, 0.07f * h}, { 0.30f * w, 0.70f * h }, { 0.47f * w, 0.37f * h } }, 240 { { 0.15f * w, 0.23f * h}, { 0.49f * w, 0.87f * h }, { 0.85f * w, 0.66f * h } }, 241 { { 0.09f * w, 0.15f * h}, { 0.42f * w, 0.33f * h }, { 0.17f * w, 0.38f * h } }, 242 { { 0.98f * w, 0.54f * h}, { 0.83f * w, 0.91f * h }, { 0.62f * w, 0.40f * h } }, 243 { { 0.96f * w, 0.65f * h}, { 0.03f * w, 0.79f * h }, { 0.24f * w, 0.56f * h } }, 244 { { 0.57f * w, 0.12f * h}, { 0.33f * w, 0.67f * h }, { 0.59f * w, 0.33f * h } }, 245 { { 0.12f * w, 0.72f * h}, { 0.69f * w, 0.85f * h }, { 0.46f * w, 0.32f * h } }, 246 { { 0.27f * w, 0.49f * h}, { 0.41f * w, 0.02f * h }, { 0.11f * w, 0.42f * h } }, 247 { { 0.40f * w, 0.13f * h}, { 0.83f * w, 0.30f * h }, { 0.31f * w, 0.68f * h } }, 248 }; 249 const SkScalar weights[kNumConics] = { 0.62f, 0.01f, 0.95f, 1.48f, 0.37f, 250 0.66f, 0.15f, 0.14f, 0.61f, 1.4f }; 251 252 SkPaint ctrlPtPaint; 253 ctrlPtPaint.setColor(SK_ColorRED); 254 255 SkPaint choppedPtPaint; 256 choppedPtPaint.setColor(~ctrlPtPaint.getColor() | 0xFF000000); 257 258 SkPaint polyPaint; 259 polyPaint.setColor(0xffA0A0A0); 260 polyPaint.setStrokeWidth(0); 261 polyPaint.setStyle(SkPaint::kStroke_Style); 262 263 SkPaint boundsPaint; 264 boundsPaint.setColor(0xff808080); 265 boundsPaint.setStrokeWidth(0); 266 boundsPaint.setStyle(SkPaint::kStroke_Style); 267 268 269 for (int row = 0; row < kNumConics; ++row) { 270 SkScalar x = 0; 271 SkScalar y = row * h; 272 SkPoint controlPts[] = { 273 {x + baseControlPts[row][0].fX, y + baseControlPts[row][0].fY}, 274 {x + baseControlPts[row][1].fX, y + baseControlPts[row][1].fY}, 275 {x + baseControlPts[row][2].fX, y + baseControlPts[row][2].fY} 276 }; 277 278 for (int i = 0; i < 3; ++i) { 279 canvas->drawCircle(controlPts[i], 6.f, ctrlPtPaint); 280 } 281 282 canvas->drawPoints(SkCanvas::kPolygon_PointMode, 3, controlPts, polyPaint); 283 284 SkConic dst[4]; 285 SkMatrix klm; 286 int cnt = ChopConic(controlPts, dst, weights[row]); 287 GrPathUtils::getConicKLM(controlPts, weights[row], &klm); 288 289 for (int c = 0; c < cnt; ++c) { 290 SkPoint* pts = dst[c].fPts; 291 for (int i = 0; i < 3; ++i) { 292 canvas->drawCircle(pts[i], 3.f, choppedPtPaint); 293 } 294 295 SkRect bounds; 296 bounds.setBounds(pts, 3); 297 298 canvas->drawRect(bounds, boundsPaint); 299 300 GrOp::Owner op = BezierConicTestOp::Make(rContext, bounds, 301 kOpaqueBlack, klm); 302 sdc->addDrawOp(std::move(op)); 303 } 304 } 305 306 return DrawResult::kOk; 307 } 308 309 private: 310 // Uses the max curvature function for quads to estimate 311 // where to chop the conic. If the max curvature is not 312 // found along the curve segment it will return 1 and 313 // dst[0] is the original conic. If it returns 2 the dst[0] 314 // and dst[1] are the two new conics. SplitConic(const SkPoint src[3],SkConic dst[2],const SkScalar weight)315 static int SplitConic(const SkPoint src[3], SkConic dst[2], const SkScalar weight) { 316 SkScalar t = SkFindQuadMaxCurvature(src); 317 if (t == 0 || t == 1) { 318 if (dst) { 319 dst[0].set(src, weight); 320 } 321 return 1; 322 } else { 323 if (dst) { 324 SkConic conic; 325 conic.set(src, weight); 326 if (!conic.chopAt(t, dst)) { 327 dst[0].set(src, weight); 328 return 1; 329 } 330 } 331 return 2; 332 } 333 } 334 335 // Calls SplitConic on the entire conic and then once more on each subsection. 336 // Most cases will result in either 1 conic (chop point is not within t range) 337 // or 3 points (split once and then one subsection is split again). ChopConic(const SkPoint src[3],SkConic dst[4],const SkScalar weight)338 static int ChopConic(const SkPoint src[3], SkConic dst[4], const SkScalar weight) { 339 SkConic dstTemp[2]; 340 int conicCnt = SplitConic(src, dstTemp, weight); 341 if (2 == conicCnt) { 342 int conicCnt2 = SplitConic(dstTemp[0].fPts, dst, dstTemp[0].fW); 343 conicCnt = conicCnt2 + SplitConic(dstTemp[1].fPts, &dst[conicCnt2], dstTemp[1].fW); 344 } else { 345 dst[0] = dstTemp[0]; 346 } 347 return conicCnt; 348 } 349 350 using INHERITED = GM; 351 }; 352 353 ////////////////////////////////////////////////////////////////////////////// 354 355 class BezierQuadTestOp : public BezierTestOp { 356 public: 357 DEFINE_OP_CLASS_ID name() const358 const char* name() const override { return "BezierQuadTestOp"; } 359 Make(GrRecordingContext * context,const SkRect & rect,const SkPMColor4f & color,const GrPathUtils::QuadUVMatrix & devToUV)360 static GrOp::Owner Make(GrRecordingContext* context, 361 const SkRect& rect, 362 const SkPMColor4f& color, 363 const GrPathUtils::QuadUVMatrix& devToUV) { 364 return GrOp::Make<BezierQuadTestOp>(context, rect, color, devToUV); 365 } 366 367 private: 368 friend class ::GrOp; // for ctor 369 BezierQuadTestOp(const SkRect & rect,const SkPMColor4f & color,const GrPathUtils::QuadUVMatrix & devToUV)370 BezierQuadTestOp(const SkRect& rect, const SkPMColor4f& color, 371 const GrPathUtils::QuadUVMatrix& devToUV) 372 : INHERITED(rect, color, ClassID()) 373 , fDevToUV(devToUV) {} 374 375 struct Vertex { 376 SkPoint fPosition; 377 float fKLM[4]; // The last value is ignored. The effect expects a vec4f. 378 }; 379 makeGP(const GrCaps & caps,SkArenaAlloc * arena)380 GrGeometryProcessor* makeGP(const GrCaps& caps, SkArenaAlloc* arena) final { 381 auto tmp = GrQuadEffect::Make(arena, this->color(), SkMatrix::I(), caps, SkMatrix::I(), 382 false); 383 if (!tmp) { 384 return nullptr; 385 } 386 SkASSERT(tmp->vertexStride() == sizeof(Vertex)); 387 return tmp; 388 } 389 onPrepareDraws(GrMeshDrawTarget * target)390 void onPrepareDraws(GrMeshDrawTarget* target) final { 391 QuadHelper helper(target, sizeof(Vertex), 1); 392 Vertex* verts = reinterpret_cast<Vertex*>(helper.vertices()); 393 if (!verts) { 394 return; 395 } 396 SkRect rect = this->rect(); 397 SkPointPriv::SetRectTriStrip(&verts[0].fPosition, rect, sizeof(Vertex)); 398 fDevToUV.apply(verts, 4, sizeof(Vertex), sizeof(SkPoint)); 399 400 fMesh = helper.mesh(); 401 } 402 403 GrPathUtils::QuadUVMatrix fDevToUV; 404 405 inline static constexpr int kVertsPerCubic = 4; 406 inline static constexpr int kIndicesPerCubic = 6; 407 408 using INHERITED = BezierTestOp; 409 }; 410 411 /** 412 * This GM directly exercises effects that draw Bezier quad curves in the GPU backend. 413 */ 414 class BezierQuadEffects : public GpuGM { 415 public: BezierQuadEffects()416 BezierQuadEffects() { 417 this->setBGColor(0xFFFFFFFF); 418 } 419 420 protected: 421 static const int kNumQuads = 5; 422 static const int kCellWidth = 128; 423 static const int kCellHeight = 128; 424 getName() const425 SkString getName() const override { return SkString("bezier_quad_effects"); } 426 getISize()427 SkISize getISize() override { return SkISize::Make(kCellWidth, kNumQuads * kCellHeight); } 428 onDraw(GrRecordingContext * rContext,SkCanvas * canvas,SkString * errorMsg)429 DrawResult onDraw(GrRecordingContext* rContext, SkCanvas* canvas, SkString* errorMsg) override { 430 auto sdc = skgpu::ganesh::TopDeviceSurfaceDrawContext(canvas); 431 if (!sdc) { 432 *errorMsg = kErrorMsg_DrawSkippedGpuOnly; 433 return DrawResult::kSkip; 434 } 435 436 const SkScalar w = kCellWidth, h = kCellHeight; 437 const SkPMColor4f kOpaqueBlack = SkPMColor4f::FromBytes_RGBA(0xff000000); 438 439 const SkPoint baseControlPts[kNumQuads][3] = { 440 { { 0.31f * w, 0.01f * h}, { 0.48f * w, 0.74f * h }, { 0.19f * w, 0.33f * h } }, 441 { { 0.00f * w, 0.07f * h}, { 0.30f * w, 0.70f * h }, { 0.47f * w, 0.37f * h } }, 442 { { 0.15f * w, 0.23f * h}, { 0.49f * w, 0.87f * h }, { 0.85f * w, 0.66f * h } }, 443 { { 0.09f * w, 0.15f * h}, { 0.42f * w, 0.33f * h }, { 0.17f * w, 0.38f * h } }, 444 { { 0.98f * w, 0.54f * h}, { 0.83f * w, 0.91f * h }, { 0.62f * w, 0.40f * h } }, 445 }; 446 447 SkPaint ctrlPtPaint; 448 ctrlPtPaint.setColor(SK_ColorRED); 449 450 SkPaint choppedPtPaint; 451 choppedPtPaint.setColor(~ctrlPtPaint.getColor() | 0xFF000000); 452 453 SkPaint polyPaint; 454 polyPaint.setColor(0xffA0A0A0); 455 polyPaint.setStrokeWidth(0); 456 polyPaint.setStyle(SkPaint::kStroke_Style); 457 458 SkPaint boundsPaint; 459 boundsPaint.setColor(0xff808080); 460 boundsPaint.setStrokeWidth(0); 461 boundsPaint.setStyle(SkPaint::kStroke_Style); 462 463 for (int row = 0; row < kNumQuads; ++row) { 464 SkScalar x = 0; 465 SkScalar y = row * h; 466 SkPoint controlPts[] = { 467 {x + baseControlPts[row][0].fX, y + baseControlPts[row][0].fY}, 468 {x + baseControlPts[row][1].fX, y + baseControlPts[row][1].fY}, 469 {x + baseControlPts[row][2].fX, y + baseControlPts[row][2].fY} 470 }; 471 472 for (int i = 0; i < 3; ++i) { 473 canvas->drawCircle(controlPts[i], 6.f, ctrlPtPaint); 474 } 475 476 canvas->drawPoints(SkCanvas::kPolygon_PointMode, 3, controlPts, polyPaint); 477 478 SkPoint chopped[5]; 479 int cnt = SkChopQuadAtMaxCurvature(controlPts, chopped); 480 481 for (int c = 0; c < cnt; ++c) { 482 SkPoint* pts = chopped + 2 * c; 483 484 for (int i = 0; i < 3; ++i) { 485 canvas->drawCircle(pts[i], 3.f, choppedPtPaint); 486 } 487 488 SkRect bounds; 489 bounds.setBounds(pts, 3); 490 491 canvas->drawRect(bounds, boundsPaint); 492 493 GrPathUtils::QuadUVMatrix DevToUV(pts); 494 495 GrOp::Owner op = BezierQuadTestOp::Make(rContext, bounds, 496 kOpaqueBlack, DevToUV); 497 sdc->addDrawOp(std::move(op)); 498 } 499 } 500 501 return DrawResult::kOk; 502 } 503 504 private: 505 using INHERITED = GM; 506 }; 507 508 DEF_GM(return new BezierConicEffects;) 509 DEF_GM(return new BezierQuadEffects;) 510 } // namespace skiagm 511