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