• 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 "GrRenderTargetContext.h"
14 #include "GrRenderTargetContextPriv.h"
15 #include "GrResourceProvider.h"
16 #include "SampleCode.h"
17 #include "SkCanvas.h"
18 #include "SkGeometry.h"
19 #include "SkMakeUnique.h"
20 #include "SkPaint.h"
21 #include "SkPath.h"
22 #include "SkView.h"
23 #include "ccpr/GrCCPRCoverageProcessor.h"
24 #include "gl/GrGLGpu.cpp"
25 #include "ops/GrDrawOp.h"
26 
27 using PrimitiveInstance = GrCCPRCoverageProcessor::PrimitiveInstance;
28 using Mode = GrCCPRCoverageProcessor::Mode;
29 
num_points(Mode mode)30 static int num_points(Mode mode)  {
31     return mode >= GrCCPRCoverageProcessor::Mode::kSerpentineInsets ? 4 : 3;
32 }
33 
is_curve(Mode mode)34 static int is_curve(Mode mode)  {
35     return mode >= GrCCPRCoverageProcessor::Mode::kQuadraticHulls;
36 }
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 SampleView {
45 public:
CCPRGeometryView()46     CCPRGeometryView() { this->updateGpuData(); }
47     void onDrawContent(SkCanvas*) override;
48 
49     SkView::Click* onFindClickHandler(SkScalar x, SkScalar y, unsigned) override;
50     bool onClick(SampleView::Click*) override;
51     bool onQuery(SkEvent* evt) override;
52 
53 private:
54     class Click;
55     class Op;
56 
updateAndInval()57     void updateAndInval() {
58         this->updateGpuData();
59         this->inval(nullptr);
60     }
61 
62     void updateGpuData();
63 
64     Mode fMode = Mode::kTriangleHulls;
65 
66     SkPoint fPoints[4] = {
67         {100.05f, 100.05f},
68         {100.05f, 300.95f},
69         {400.75f, 300.95f},
70         {400.75f, 100.05f}
71     };
72 
73     SkSTArray<16, SkPoint>            fGpuPoints;
74     SkSTArray<3, PrimitiveInstance>   fGpuInstances;
75 
76     typedef SampleView INHERITED;
77 };
78 
79 class CCPRGeometryView::Op : public GrDrawOp {
80     DEFINE_OP_CLASS_ID
81 
82 public:
Op(CCPRGeometryView * view)83     Op(CCPRGeometryView* view)
84             : INHERITED(ClassID())
85             , fView(view) {
86         this->setBounds(SkRect::MakeLargest(), GrOp::HasAABloat::kNo, GrOp::IsZeroArea::kNo);
87     }
88 
name() const89     const char* name() const override { return "[Testing/Sample code] CCPRGeometryView::Op"; }
90 
91 private:
fixedFunctionFlags() const92     FixedFunctionFlags fixedFunctionFlags() const override { return FixedFunctionFlags::kNone; }
finalize(const GrCaps &,const GrAppliedClip *)93     RequiresDstTexture finalize(const GrCaps&, const GrAppliedClip*) override {
94         return RequiresDstTexture::kNo;
95     }
onCombineIfPossible(GrOp * other,const GrCaps & caps)96     bool onCombineIfPossible(GrOp* other, const GrCaps& caps) override { return false; }
onPrepare(GrOpFlushState *)97     void onPrepare(GrOpFlushState*) override {}
98     void onExecute(GrOpFlushState*) override;
99 
100     CCPRGeometryView* fView;
101 
102     typedef GrDrawOp INHERITED;
103 };
104 
onDrawContent(SkCanvas * canvas)105 void CCPRGeometryView::onDrawContent(SkCanvas* canvas) {
106     SkAutoCanvasRestore acr(canvas, true);
107     canvas->setMatrix(SkMatrix::I());
108 
109     SkPath outline;
110     outline.moveTo(fPoints[0]);
111     if (4 == num_points(fMode)) {
112         outline.cubicTo(fPoints[1], fPoints[2], fPoints[3]);
113     } else if (is_curve(fMode)) {
114         outline.quadTo(fPoints[1], fPoints[3]);
115     } else {
116         outline.lineTo(fPoints[1]);
117         outline.lineTo(fPoints[3]);
118     }
119     outline.close();
120 
121     SkPaint outlinePaint;
122     outlinePaint.setColor(0x30000000);
123     outlinePaint.setStyle(SkPaint::kStroke_Style);
124     outlinePaint.setStrokeWidth(0);
125     outlinePaint.setAntiAlias(true);
126 
127     canvas->drawPath(outline, outlinePaint);
128 
129     const char* caption = "Use GPU backend to visualize geometry.";
130 
131     if (GrRenderTargetContext* rtc =
132         canvas->internal_private_accessTopLayerRenderTargetContext()) {
133         rtc->priv().testingOnly_addDrawOp(skstd::make_unique<Op>(this));
134         caption = GrCCPRCoverageProcessor::GetProcessorName(fMode);
135     }
136 
137     SkPaint pointsPaint;
138     pointsPaint.setColor(SK_ColorBLUE);
139     pointsPaint.setStrokeWidth(8);
140     pointsPaint.setAntiAlias(true);
141 
142     if (4 == num_points(fMode)) {
143         canvas->drawPoints(SkCanvas::kPoints_PointMode, 4, fPoints, pointsPaint);
144     } else {
145         canvas->drawPoints(SkCanvas::kPoints_PointMode, 2, fPoints, pointsPaint);
146         canvas->drawPoints(SkCanvas::kPoints_PointMode, 1, fPoints + 3, pointsPaint);
147     }
148 
149     SkPaint captionPaint;
150     captionPaint.setTextSize(20);
151     captionPaint.setColor(SK_ColorBLACK);
152     captionPaint.setAntiAlias(true);
153     canvas->drawText(caption, strlen(caption), 10, 30, captionPaint);
154 }
155 
updateGpuData()156 void CCPRGeometryView::updateGpuData() {
157     int vertexCount = num_points(fMode);
158     int instanceCount = 1;
159 
160     fGpuPoints.reset();
161     fGpuInstances.reset();
162 
163     if (4 == vertexCount) {
164         double t[2], s[2];
165         SkCubicType type = SkClassifyCubic(fPoints, t, s);
166         SkSTArray<2, float> chops;
167         for (int i = 0; i < 2; ++i) {
168             float chop = t[i] / s[i];
169             if (chop > 0 && chop < 1) {
170                 chops.push_back(chop);
171             }
172         }
173 
174         instanceCount = chops.count() + 1;
175         SkPoint chopped[10];
176         SkChopCubicAt(fPoints, chopped, chops.begin(), chops.count());
177 
178         // Endpoints first, then control points.
179         for (int i = 0; i <= instanceCount; ++i) {
180             fGpuPoints.push_back(chopped[3*i]);
181         }
182         if (3 == instanceCount && SkCubicType::kLoop == type) {
183             fGpuPoints[2] = fGpuPoints[1]; // Account for floating point error.
184         }
185         for (int i = 0; i < instanceCount; ++i) {
186             fGpuPoints.push_back(chopped[3*i + 1]);
187             fGpuPoints.push_back(chopped[3*i + 2]);
188             // FIXME: we don't bother to send down the correct KLM t,s roots.
189             fGpuPoints.push_back({0, 0});
190             fGpuPoints.push_back({0, 0});
191         }
192 
193         if (fMode < Mode::kLoopInsets && SkCubicType::kLoop == type) {
194             fMode = (Mode) ((int) fMode + 2);
195         }
196         if (fMode >= Mode::kLoopInsets && SkCubicType::kLoop != type) {
197             fMode = (Mode) ((int) fMode - 2);
198         }
199     } else {
200         // Endpoints.
201         fGpuPoints.push_back(fPoints[0]);
202         fGpuPoints.push_back(fPoints[3]);
203         // Control points.
204         fGpuPoints.push_back(fPoints[1]);
205     }
206 
207     if (4 == vertexCount) {
208         int controlPointsIdx = instanceCount + 1;
209         for (int i = 0; i < instanceCount; ++i) {
210             fGpuInstances.push_back().fCubicData = {controlPointsIdx + i * 4, i};
211         }
212     } else if (is_curve(fMode)) {
213         fGpuInstances.push_back().fQuadraticData = {2, 0};
214     } else {
215         fGpuInstances.push_back().fTriangleData = {0, 2, 1}; // Texel buffer has endpoints first.
216     }
217 
218     for (PrimitiveInstance& instance : fGpuInstances) {
219         instance.fPackedAtlasOffset = 0;
220     }
221 }
222 
onExecute(GrOpFlushState * state)223 void CCPRGeometryView::Op::onExecute(GrOpFlushState* state) {
224     GrResourceProvider* rp = state->resourceProvider();
225     GrContext* context = state->gpu()->getContext();
226     GrGLGpu* glGpu = kOpenGL_GrBackend == context->contextPriv().getBackend() ?
227                      static_cast<GrGLGpu*>(state->gpu()) : nullptr;
228     int vertexCount = num_points(fView->fMode);
229 
230     sk_sp<GrBuffer> pointsBuffer(rp->createBuffer(fView->fGpuPoints.count() * sizeof(SkPoint),
231                                                   kTexel_GrBufferType, kDynamic_GrAccessPattern,
232                                                   GrResourceProvider::kNoPendingIO_Flag |
233                                                   GrResourceProvider::kRequireGpuMemory_Flag,
234                                                   fView->fGpuPoints.begin()));
235     if (!pointsBuffer) {
236         return;
237     }
238 
239     sk_sp<GrBuffer> instanceBuffer(rp->createBuffer(fView->fGpuInstances.count() * 4 * sizeof(int),
240                                                     kVertex_GrBufferType, kDynamic_GrAccessPattern,
241                                                     GrResourceProvider::kNoPendingIO_Flag |
242                                                     GrResourceProvider::kRequireGpuMemory_Flag,
243                                                     fView->fGpuInstances.begin()));
244     if (!instanceBuffer) {
245         return;
246     }
247 
248     GrPipeline pipeline(state->drawOpArgs().fRenderTarget, GrPipeline::ScissorState::kDisabled,
249                         SkBlendMode::kSrcOver);
250 
251     GrCCPRCoverageProcessor ccprProc(fView->fMode, pointsBuffer.get());
252     SkDEBUGCODE(ccprProc.enableDebugVisualizations();)
253 
254     GrMesh mesh(4 == vertexCount ?  GrPrimitiveType::kLinesAdjacency : GrPrimitiveType::kTriangles);
255     mesh.setInstanced(instanceBuffer.get(), fView->fGpuInstances.count(), 0, vertexCount);
256 
257     if (glGpu) {
258         glGpu->handleDirtyContext();
259         GR_GL_CALL(glGpu->glInterface(), PolygonMode(GR_GL_FRONT_AND_BACK, GR_GL_LINE));
260         GR_GL_CALL(glGpu->glInterface(), Enable(GR_GL_LINE_SMOOTH));
261     }
262 
263     state->commandBuffer()->draw(pipeline, ccprProc, &mesh, nullptr, 1, this->bounds());
264 
265     if (glGpu) {
266         context->resetContext(kMisc_GrGLBackendState);
267     }
268 }
269 
270 class CCPRGeometryView::Click : public SampleView::Click {
271 public:
Click(SkView * target,int ptIdx)272     Click(SkView* target, int ptIdx) : SampleView::Click(target), fPtIdx(ptIdx) {}
273 
doClick(SkPoint points[])274     void doClick(SkPoint points[]) {
275         if (fPtIdx >= 0) {
276             this->dragPoint(points, fPtIdx);
277         } else {
278             for (int i = 0; i < 4; ++i) {
279                 this->dragPoint(points, i);
280             }
281         }
282     }
283 
284 private:
dragPoint(SkPoint points[],int idx)285     void dragPoint(SkPoint points[], int idx)  {
286         SkIPoint delta = fICurr - fIPrev;
287         points[idx] += SkPoint::Make(delta.x(), delta.y());
288     }
289 
290     int fPtIdx;
291 };
292 
onFindClickHandler(SkScalar x,SkScalar y,unsigned)293 SkView::Click* CCPRGeometryView::onFindClickHandler(SkScalar x, SkScalar y, unsigned) {
294     for (int i = 0; i < 4; ++i) {
295         if (4 != num_points(fMode) && 2 == i) {
296             continue;
297         }
298         if (fabs(x - fPoints[i].x()) < 20 && fabsf(y - fPoints[i].y()) < 20) {
299             return new Click(this, i);
300         }
301     }
302     return new Click(this, -1);
303 }
304 
onClick(SampleView::Click * click)305 bool CCPRGeometryView::onClick(SampleView::Click* click) {
306     Click* myClick = (Click*) click;
307     myClick->doClick(fPoints);
308     this->updateAndInval();
309     return true;
310 }
311 
onQuery(SkEvent * evt)312 bool CCPRGeometryView::onQuery(SkEvent* evt) {
313     if (SampleCode::TitleQ(*evt)) {
314         SampleCode::TitleR(evt, "CCPRGeometry");
315         return true;
316     }
317     SkUnichar unichar;
318     if (SampleCode::CharQ(*evt, &unichar)) {
319         if (unichar >= '1' && unichar <= '7') {
320             fMode = Mode(unichar - '1');
321             if (fMode >= Mode::kCombinedTriangleHullsAndEdges) {
322                 fMode = Mode(int(fMode) + 1);
323             }
324             this->updateAndInval();
325             return true;
326         }
327         if (unichar == 'D') {
328             SkDebugf("    SkPoint fPoints[4] = {\n");
329             SkDebugf("        {%f, %f},\n", fPoints[0].x(), fPoints[0].y());
330             SkDebugf("        {%f, %f},\n", fPoints[1].x(), fPoints[1].y());
331             SkDebugf("        {%f, %f},\n", fPoints[2].x(), fPoints[2].y());
332             SkDebugf("        {%f, %f}\n", fPoints[3].x(), fPoints[3].y());
333             SkDebugf("    };\n");
334             return true;
335         }
336     }
337     return this->INHERITED::onQuery(evt);
338 }
339 
340 DEF_SAMPLE( return new CCPRGeometryView; )
341 
342 #endif // SK_SUPPORT_GPU
343