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 "GrPathUtils.h"
14 #include "GrRenderTargetContext.h"
15 #include "GrRenderTargetContextPriv.h"
16 #include "GrResourceProvider.h"
17 #include "SampleCode.h"
18 #include "SkCanvas.h"
19 #include "SkMakeUnique.h"
20 #include "SkPaint.h"
21 #include "SkPath.h"
22 #include "SkRectPriv.h"
23 #include "SkView.h"
24 #include "ccpr/GrCCCoverageProcessor.h"
25 #include "ccpr/GrCCGeometry.h"
26 #include "gl/GrGLGpu.cpp"
27 #include "ops/GrDrawOp.h"
28
29 using TriPointInstance = GrCCCoverageProcessor::TriPointInstance;
30 using QuadPointInstance = GrCCCoverageProcessor::QuadPointInstance;
31 using RenderPass = GrCCCoverageProcessor::RenderPass;
32
33 static constexpr float kDebugBloat = 40;
34
is_quadratic(RenderPass pass)35 static int is_quadratic(RenderPass pass) {
36 return pass == RenderPass::kQuadraticHulls || pass == RenderPass::kQuadraticCorners;
37 }
38
39 /**
40 * This sample visualizes the AA bloat geometry generated by the ccpr geometry shaders. It
41 * increases the AA bloat by 50x and outputs color instead of coverage (coverage=+1 -> green,
42 * coverage=0 -> black, coverage=-1 -> red). Use the keys 1-7 to cycle through the different
43 * geometry processors.
44 */
45 class CCPRGeometryView : public SampleView {
46 public:
CCPRGeometryView()47 CCPRGeometryView() { this->updateGpuData(); }
48 void onDrawContent(SkCanvas*) override;
49
50 SkView::Click* onFindClickHandler(SkScalar x, SkScalar y, unsigned) override;
51 bool onClick(SampleView::Click*) override;
52 bool onQuery(SkEvent* evt) override;
53
54 private:
55 class Click;
56 class Op;
57
updateAndInval()58 void updateAndInval() { this->updateGpuData(); }
59
60 void updateGpuData();
61
62 RenderPass fRenderPass = RenderPass::kTriangleHulls;
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 SkTArray<TriPointInstance> fTriPointInstances;
70 SkTArray<QuadPointInstance> fQuadPointInstances;
71
72 typedef SampleView INHERITED;
73 };
74
75 class CCPRGeometryView::Op : public GrDrawOp {
76 DEFINE_OP_CLASS_ID
77
78 public:
Op(CCPRGeometryView * view)79 Op(CCPRGeometryView* view) : INHERITED(ClassID()), fView(view) {
80 this->setBounds(SkRectPriv::MakeLargest(), GrOp::HasAABloat::kNo, GrOp::IsZeroArea::kNo);
81 }
82
name() const83 const char* name() const override { return "[Testing/Sample code] CCPRGeometryView::Op"; }
84
85 private:
fixedFunctionFlags() const86 FixedFunctionFlags fixedFunctionFlags() const override { return FixedFunctionFlags::kNone; }
finalize(const GrCaps &,const GrAppliedClip *,GrPixelConfigIsClamped)87 RequiresDstTexture finalize(const GrCaps&, const GrAppliedClip*,
88 GrPixelConfigIsClamped) override {
89 return RequiresDstTexture::kNo;
90 }
onCombineIfPossible(GrOp * other,const GrCaps & caps)91 bool onCombineIfPossible(GrOp* other, const GrCaps& caps) override { return false; }
onPrepare(GrOpFlushState *)92 void onPrepare(GrOpFlushState*) override {}
93 void onExecute(GrOpFlushState*) override;
94
95 CCPRGeometryView* fView;
96
97 typedef GrDrawOp INHERITED;
98 };
99
draw_klm_line(int w,int h,SkCanvas * canvas,const SkScalar line[3],SkColor color)100 static void draw_klm_line(int w, int h, SkCanvas* canvas, const SkScalar line[3], SkColor color) {
101 SkPoint p1, p2;
102 if (SkScalarAbs(line[1]) > SkScalarAbs(line[0])) {
103 // Draw from vertical edge to vertical edge.
104 p1 = {0, -line[2] / line[1]};
105 p2 = {(SkScalar)w, (-line[2] - w * line[0]) / line[1]};
106 } else {
107 // Draw from horizontal edge to horizontal edge.
108 p1 = {-line[2] / line[0], 0};
109 p2 = {(-line[2] - h * line[1]) / line[0], (SkScalar)h};
110 }
111
112 SkPaint linePaint;
113 linePaint.setColor(color);
114 linePaint.setAlpha(128);
115 linePaint.setStyle(SkPaint::kStroke_Style);
116 linePaint.setStrokeWidth(0);
117 linePaint.setAntiAlias(true);
118 canvas->drawLine(p1, p2, linePaint);
119 }
120
onDrawContent(SkCanvas * canvas)121 void CCPRGeometryView::onDrawContent(SkCanvas* canvas) {
122 SkAutoCanvasRestore acr(canvas, true);
123 canvas->setMatrix(SkMatrix::I());
124
125 SkPath outline;
126 outline.moveTo(fPoints[0]);
127 if (GrCCCoverageProcessor::RenderPassIsCubic(fRenderPass)) {
128 outline.cubicTo(fPoints[1], fPoints[2], fPoints[3]);
129 } else if (is_quadratic(fRenderPass)) {
130 outline.quadTo(fPoints[1], fPoints[3]);
131 } else {
132 outline.lineTo(fPoints[1]);
133 outline.lineTo(fPoints[3]);
134 outline.close();
135 }
136
137 SkPaint outlinePaint;
138 outlinePaint.setColor(0x30000000);
139 outlinePaint.setStyle(SkPaint::kStroke_Style);
140 outlinePaint.setStrokeWidth(0);
141 outlinePaint.setAntiAlias(true);
142 canvas->drawPath(outline, outlinePaint);
143
144 #if 0
145 SkPaint gridPaint;
146 gridPaint.setColor(0x10000000);
147 gridPaint.setStyle(SkPaint::kStroke_Style);
148 gridPaint.setStrokeWidth(0);
149 gridPaint.setAntiAlias(true);
150 for (int y = 0; y < this->height(); y += kDebugBloat) {
151 canvas->drawLine(0, y, this->width(), y, gridPaint);
152 }
153 for (int x = 0; x < this->width(); x += kDebugBloat) {
154 canvas->drawLine(x, 0, x, this->height(), outlinePaint);
155 }
156 #endif
157
158 SkString caption;
159 if (GrRenderTargetContext* rtc = canvas->internal_private_accessTopLayerRenderTargetContext()) {
160 rtc->priv().testingOnly_addDrawOp(skstd::make_unique<Op>(this));
161 caption.appendf("RenderPass_%s", GrCCCoverageProcessor::RenderPassName(fRenderPass));
162 if (GrCCCoverageProcessor::RenderPassIsCubic(fRenderPass)) {
163 caption.appendf(" (%s)", SkCubicTypeName(fCubicType));
164 }
165 } else {
166 caption = "Use GPU backend to visualize geometry.";
167 }
168
169 SkPaint pointsPaint;
170 pointsPaint.setColor(SK_ColorBLUE);
171 pointsPaint.setStrokeWidth(8);
172 pointsPaint.setAntiAlias(true);
173
174 if (GrCCCoverageProcessor::RenderPassIsCubic(fRenderPass)) {
175 int w = this->width(), h = this->height();
176 canvas->drawPoints(SkCanvas::kPoints_PointMode, 4, fPoints, pointsPaint);
177 draw_klm_line(w, h, canvas, &fCubicKLM[0], SK_ColorYELLOW);
178 draw_klm_line(w, h, canvas, &fCubicKLM[3], SK_ColorBLUE);
179 draw_klm_line(w, h, canvas, &fCubicKLM[6], SK_ColorRED);
180 } else {
181 canvas->drawPoints(SkCanvas::kPoints_PointMode, 2, fPoints, pointsPaint);
182 canvas->drawPoints(SkCanvas::kPoints_PointMode, 1, fPoints + 3, pointsPaint);
183 }
184
185 SkPaint captionPaint;
186 captionPaint.setTextSize(20);
187 captionPaint.setColor(SK_ColorBLACK);
188 captionPaint.setAntiAlias(true);
189 canvas->drawText(caption.c_str(), caption.size(), 10, 30, captionPaint);
190 }
191
updateGpuData()192 void CCPRGeometryView::updateGpuData() {
193 fTriPointInstances.reset();
194 fQuadPointInstances.reset();
195
196 if (GrCCCoverageProcessor::RenderPassIsCubic(fRenderPass)) {
197 double t[2], s[2];
198 fCubicType = GrPathUtils::getCubicKLM(fPoints, &fCubicKLM, t, s);
199 GrCCGeometry geometry;
200 geometry.beginContour(fPoints[0]);
201 geometry.cubicTo(fPoints[1], fPoints[2], fPoints[3], kDebugBloat / 2, kDebugBloat / 2);
202 geometry.endContour();
203 int ptsIdx = 0;
204 for (GrCCGeometry::Verb verb : geometry.verbs()) {
205 switch (verb) {
206 case GrCCGeometry::Verb::kLineTo:
207 ++ptsIdx;
208 continue;
209 case GrCCGeometry::Verb::kMonotonicQuadraticTo:
210 ptsIdx += 2;
211 continue;
212 case GrCCGeometry::Verb::kMonotonicCubicTo:
213 fQuadPointInstances.push_back().set(&geometry.points()[ptsIdx], 0, 0);
214 ptsIdx += 3;
215 continue;
216 default:
217 continue;
218 }
219 }
220 } else if (is_quadratic(fRenderPass)) {
221 GrCCGeometry geometry;
222 geometry.beginContour(fPoints[0]);
223 geometry.quadraticTo(fPoints[1], fPoints[3]);
224 geometry.endContour();
225 int ptsIdx = 0;
226 for (GrCCGeometry::Verb verb : geometry.verbs()) {
227 if (GrCCGeometry::Verb::kBeginContour == verb ||
228 GrCCGeometry::Verb::kEndOpenContour == verb ||
229 GrCCGeometry::Verb::kEndClosedContour == verb) {
230 continue;
231 }
232 if (GrCCGeometry::Verb::kLineTo == verb) {
233 ++ptsIdx;
234 continue;
235 }
236 SkASSERT(GrCCGeometry::Verb::kMonotonicQuadraticTo == verb);
237 fTriPointInstances.push_back().set(&geometry.points()[ptsIdx], Sk2f(0, 0));
238 ptsIdx += 2;
239 }
240 } else {
241 fTriPointInstances.push_back().set(fPoints[0], fPoints[1], fPoints[3], Sk2f(0, 0));
242 }
243 }
244
onExecute(GrOpFlushState * state)245 void CCPRGeometryView::Op::onExecute(GrOpFlushState* state) {
246 GrResourceProvider* rp = state->resourceProvider();
247 GrContext* context = state->gpu()->getContext();
248 GrGLGpu* glGpu = kOpenGL_GrBackend == context->contextPriv().getBackend()
249 ? static_cast<GrGLGpu*>(state->gpu())
250 : nullptr;
251
252 if (!GrCCCoverageProcessor::DoesRenderPass(fView->fRenderPass, state->caps())) {
253 return;
254 }
255
256 GrCCCoverageProcessor proc(rp, fView->fRenderPass,
257 GrCCCoverageProcessor::WindMethod::kCrossProduct);
258 SkDEBUGCODE(proc.enableDebugVisualizations(kDebugBloat));
259
260 SkSTArray<1, GrMesh> mesh;
261 if (GrCCCoverageProcessor::RenderPassIsCubic(fView->fRenderPass)) {
262 sk_sp<GrBuffer> instBuff(rp->createBuffer(
263 fView->fQuadPointInstances.count() * sizeof(QuadPointInstance),
264 kVertex_GrBufferType, kDynamic_GrAccessPattern,
265 GrResourceProvider::kNoPendingIO_Flag | GrResourceProvider::kRequireGpuMemory_Flag,
266 fView->fQuadPointInstances.begin()));
267 if (!fView->fQuadPointInstances.empty() && instBuff) {
268 proc.appendMesh(instBuff.get(), fView->fQuadPointInstances.count(), 0, &mesh);
269 }
270 } else {
271 sk_sp<GrBuffer> instBuff(rp->createBuffer(
272 fView->fTriPointInstances.count() * sizeof(TriPointInstance), kVertex_GrBufferType,
273 kDynamic_GrAccessPattern,
274 GrResourceProvider::kNoPendingIO_Flag | GrResourceProvider::kRequireGpuMemory_Flag,
275 fView->fTriPointInstances.begin()));
276 if (!fView->fTriPointInstances.empty() && instBuff) {
277 proc.appendMesh(instBuff.get(), fView->fTriPointInstances.count(), 0, &mesh);
278 }
279 }
280
281 GrPipeline pipeline(state->drawOpArgs().fProxy, GrPipeline::ScissorState::kDisabled,
282 SkBlendMode::kSrcOver);
283
284 if (glGpu) {
285 glGpu->handleDirtyContext();
286 GR_GL_CALL(glGpu->glInterface(), PolygonMode(GR_GL_FRONT_AND_BACK, GR_GL_LINE));
287 GR_GL_CALL(glGpu->glInterface(), Enable(GR_GL_LINE_SMOOTH));
288 }
289
290 if (!mesh.empty()) {
291 SkASSERT(1 == mesh.count());
292 state->rtCommandBuffer()->draw(pipeline, proc, mesh.begin(), nullptr, 1, this->bounds());
293 }
294
295 if (glGpu) {
296 context->resetContext(kMisc_GrGLBackendState);
297 }
298 }
299
300 class CCPRGeometryView::Click : public SampleView::Click {
301 public:
Click(SkView * target,int ptIdx)302 Click(SkView* target, int ptIdx) : SampleView::Click(target), fPtIdx(ptIdx) {}
303
doClick(SkPoint points[])304 void doClick(SkPoint points[]) {
305 if (fPtIdx >= 0) {
306 this->dragPoint(points, fPtIdx);
307 } else {
308 for (int i = 0; i < 4; ++i) {
309 this->dragPoint(points, i);
310 }
311 }
312 }
313
314 private:
dragPoint(SkPoint points[],int idx)315 void dragPoint(SkPoint points[], int idx) {
316 SkIPoint delta = fICurr - fIPrev;
317 points[idx] += SkPoint::Make(delta.x(), delta.y());
318 }
319
320 int fPtIdx;
321 };
322
onFindClickHandler(SkScalar x,SkScalar y,unsigned)323 SkView::Click* CCPRGeometryView::onFindClickHandler(SkScalar x, SkScalar y, unsigned) {
324 for (int i = 0; i < 4; ++i) {
325 if (!GrCCCoverageProcessor::RenderPassIsCubic(fRenderPass) && 2 == i) {
326 continue;
327 }
328 if (fabs(x - fPoints[i].x()) < 20 && fabsf(y - fPoints[i].y()) < 20) {
329 return new Click(this, i);
330 }
331 }
332 return new Click(this, -1);
333 }
334
onClick(SampleView::Click * click)335 bool CCPRGeometryView::onClick(SampleView::Click* click) {
336 Click* myClick = (Click*)click;
337 myClick->doClick(fPoints);
338 this->updateAndInval();
339 return true;
340 }
341
onQuery(SkEvent * evt)342 bool CCPRGeometryView::onQuery(SkEvent* evt) {
343 if (SampleCode::TitleQ(*evt)) {
344 SampleCode::TitleR(evt, "CCPRGeometry");
345 return true;
346 }
347 SkUnichar unichar;
348 if (SampleCode::CharQ(*evt, &unichar)) {
349 if (unichar >= '1' && unichar <= '7') {
350 fRenderPass = RenderPass(unichar - '1');
351 this->updateAndInval();
352 return true;
353 }
354 if (unichar == 'D') {
355 SkDebugf(" SkPoint fPoints[4] = {\n");
356 SkDebugf(" {%ff, %ff},\n", fPoints[0].x(), fPoints[0].y());
357 SkDebugf(" {%ff, %ff},\n", fPoints[1].x(), fPoints[1].y());
358 SkDebugf(" {%ff, %ff},\n", fPoints[2].x(), fPoints[2].y());
359 SkDebugf(" {%ff, %ff}\n", fPoints[3].x(), fPoints[3].y());
360 SkDebugf(" };\n");
361 return true;
362 }
363 }
364 return this->INHERITED::onQuery(evt);
365 }
366
367 DEF_SAMPLE(return new CCPRGeometryView;)
368
369 #endif // SK_SUPPORT_GPU
370