blob: 5e196cc674650cf6ebde567c72a5f4d9af5bfc19 [file] [log] [blame]
dvonbeck09e12a62016-08-12 12:50:36 -07001/*
2 * Copyright 2016 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 "GrAnalyticRectBatch.h"
9
10#include "GrBatchFlushState.h"
11#include "GrBatchTest.h"
12#include "GrGeometryProcessor.h"
13#include "GrInvariantOutput.h"
14#include "GrProcessor.h"
15#include "GrResourceProvider.h"
16#include "SkRRect.h"
17#include "SkStrokeRec.h"
18#include "batches/GrVertexBatch.h"
19#include "glsl/GrGLSLFragmentShaderBuilder.h"
20#include "glsl/GrGLSLGeometryProcessor.h"
bsalomona624bf32016-09-20 09:12:47 -070021#include "glsl/GrGLSLGeometryProcessor.h"
dvonbeck09e12a62016-08-12 12:50:36 -070022#include "glsl/GrGLSLProgramDataManager.h"
23#include "glsl/GrGLSLVarying.h"
24#include "glsl/GrGLSLVertexShaderBuilder.h"
25#include "glsl/GrGLSLUniformHandler.h"
26#include "glsl/GrGLSLUtil.h"
27
28namespace {
29
30struct RectVertex {
31 SkPoint fPos;
32 GrColor fColor;
33 SkPoint fCenter;
34 SkVector fDownDir;
35 SkScalar fHalfWidth;
36 SkScalar fHalfHeight;
37};
38
39}
40
41///////////////////////////////////////////////////////////////////////////////
42
43/**
44 * The output of this effect is the input color and coverage for an arbitrarily oriented rect. The
45 * rect is specified as:
46 * Center of the rect
47 * Unit vector point down the height of the rect
48 * Half width + 0.5
49 * Half height + 0.5
50 * The center and vector are stored in a vec4 varying ("RectEdge") with the
51 * center in the xy components and the vector in the zw components.
52 * The munged width and height are stored in a vec2 varying ("WidthHeight")
53 * with the width in x and the height in y.
54 */
55class RectGeometryProcessor : public GrGeometryProcessor {
56public:
57 RectGeometryProcessor(const SkMatrix& localMatrix) : fLocalMatrix(localMatrix) {
58 this->initClassID<RectGeometryProcessor>();
bsalomon6cb807b2016-08-17 11:33:39 -070059 fInPosition = &this->addVertexAttrib("inPosition", kVec2f_GrVertexAttribType,
60 kHigh_GrSLPrecision);
61 fInColor = &this->addVertexAttrib("inColor", kVec4ub_GrVertexAttribType);
62 fInRectEdge = &this->addVertexAttrib("inRectEdge", kVec4f_GrVertexAttribType);
63 fInWidthHeight = &this->addVertexAttrib("inWidthHeight", kVec2f_GrVertexAttribType);
dvonbeck09e12a62016-08-12 12:50:36 -070064 }
65
Mike Kleinfc6c37b2016-09-27 09:34:10 -040066 bool implementsDistanceVector() const override { return true; }
dvonbeck09e12a62016-08-12 12:50:36 -070067
68 const Attribute* inPosition() const { return fInPosition; }
69 const Attribute* inColor() const { return fInColor; }
70 const Attribute* inRectEdge() const { return fInRectEdge; }
71 const Attribute* inWidthHeight() const { return fInWidthHeight; }
72
73 const SkMatrix& localMatrix() const { return fLocalMatrix; }
74
75 virtual ~RectGeometryProcessor() {}
76
77 const char* name() const override { return "RectEdge"; }
78
79 class GLSLProcessor : public GrGLSLGeometryProcessor {
80 public:
81 GLSLProcessor() {}
82
83 void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override{
84 const RectGeometryProcessor& rgp = args.fGP.cast<RectGeometryProcessor>();
85 GrGLSLVertexBuilder* vertBuilder = args.fVertBuilder;
86 GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler;
87 GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
88
89 // emit attributes
90 varyingHandler->emitAttributes(rgp);
91
92 // setup the varying for the position
93 GrGLSLVertToFrag positionVary(kVec2f_GrSLType);
94 varyingHandler->addVarying("Position", &positionVary);
95 vertBuilder->codeAppendf("%s = %s;", positionVary.vsOut(), rgp.inPosition()->fName);
96
97 // setup the varying for the center point and the unit vector that points down the
98 // height of the rect
99 GrGLSLVertToFrag rectEdgeVary(kVec4f_GrSLType);
100 varyingHandler->addVarying("RectEdge", &rectEdgeVary);
101 vertBuilder->codeAppendf("%s = %s;", rectEdgeVary.vsOut(), rgp.inRectEdge()->fName);
102
103 // setup the varying for the width/2+.5 and height/2+.5
104 GrGLSLVertToFrag widthHeightVary(kVec2f_GrSLType);
105 varyingHandler->addVarying("WidthHeight", &widthHeightVary);
106 vertBuilder->codeAppendf("%s = %s;",
107 widthHeightVary.vsOut(), rgp.inWidthHeight()->fName);
108
109 GrGLSLPPFragmentBuilder* fragBuilder = args.fFragBuilder;
110
111 // setup pass through color
112 varyingHandler->addPassThroughAttribute(rgp.inColor(), args.fOutputColor);
113
114 // Setup position
115 this->setupPosition(vertBuilder, gpArgs, rgp.inPosition()->fName);
116
117 // emit transforms
118 this->emitTransforms(vertBuilder,
119 varyingHandler,
120 uniformHandler,
121 gpArgs->fPositionVar,
122 rgp.inPosition()->fName,
123 rgp.localMatrix(),
bsalomona624bf32016-09-20 09:12:47 -0700124 args.fFPCoordTransformHandler);
dvonbeck09e12a62016-08-12 12:50:36 -0700125
126 // TODO: compute all these offsets, spans, and scales in the VS
127 fragBuilder->codeAppendf("float insetW = min(1.0, %s.x) - 0.5;",
128 widthHeightVary.fsIn());
129 fragBuilder->codeAppendf("float insetH = min(1.0, %s.y) - 0.5;",
130 widthHeightVary.fsIn());
131 fragBuilder->codeAppend("float outset = 0.5;");
132 // For rects > 1 pixel wide and tall the span's are noops (i.e., 1.0). For rects
133 // < 1 pixel wide or tall they serve to normalize the < 1 ramp to a 0 .. 1 range.
134 fragBuilder->codeAppend("float spanW = insetW + outset;");
135 fragBuilder->codeAppend("float spanH = insetH + outset;");
136 // For rects < 1 pixel wide or tall, these scale factors are used to cap the maximum
137 // value of coverage that is used. In other words it is the coverage that is
138 // used in the interior of the rect after the ramp.
139 fragBuilder->codeAppend("float scaleW = min(1.0, 2.0*insetW/spanW);");
140 fragBuilder->codeAppend("float scaleH = min(1.0, 2.0*insetH/spanH);");
141 // Compute the coverage for the rect's width
142 fragBuilder->codeAppendf("vec2 offset = %s.xy - %s.xy;",
143 positionVary.fsIn(), rectEdgeVary.fsIn());
144 fragBuilder->codeAppendf("float perpDot = abs(offset.x * %s.w - offset.y * %s.z);",
145 rectEdgeVary.fsIn(), rectEdgeVary.fsIn());
146
147 if (args.fDistanceVectorName) {
148 fragBuilder->codeAppendf("float widthDistance = %s.x - perpDot;",
149 widthHeightVary.fsIn());
150 }
151
152 fragBuilder->codeAppendf(
153 "float coverage = scaleW*clamp((%s.x-perpDot)/spanW, 0.0, 1.0);",
154 widthHeightVary.fsIn());
155 // Compute the coverage for the rect's height and merge with the width
156 fragBuilder->codeAppendf("perpDot = abs(dot(offset, %s.zw));",
157 rectEdgeVary.fsIn());
158
159 if (args.fDistanceVectorName) {
160 fragBuilder->codeAppendf("float heightDistance = %s.y - perpDot;",
161 widthHeightVary.fsIn());
162 }
163
164 fragBuilder->codeAppendf(
165 "coverage = coverage*scaleH*clamp((%s.y-perpDot)/spanH, 0.0, 1.0);",
166 widthHeightVary.fsIn());
167
168 fragBuilder->codeAppendf("%s = vec4(coverage);", args.fOutputCoverage);
169
170 if (args.fDistanceVectorName) {
171 fragBuilder->codeAppend( "// Calculating distance vector\n");
172 fragBuilder->codeAppend( "vec2 dvAxis;");
173 fragBuilder->codeAppend( "float dvLength;");
174
175 fragBuilder->codeAppend( "if (heightDistance < widthDistance) {");
176 fragBuilder->codeAppendf(" dvAxis = %s.zw;", rectEdgeVary.fsIn());
177 fragBuilder->codeAppend( " dvLength = heightDistance;");
178 fragBuilder->codeAppend( "} else {");
179 fragBuilder->codeAppendf(" dvAxis = vec2(-%s.w, %s.z);",
180 rectEdgeVary.fsIn(), rectEdgeVary.fsIn());
181 fragBuilder->codeAppend( " dvLength = widthDistance;");
182 fragBuilder->codeAppend( "}");
183
184 fragBuilder->codeAppend( "float dvSign = sign(dot(offset, dvAxis));");
jvanverth6c177a12016-08-17 07:59:41 -0700185 fragBuilder->codeAppendf("%s = vec4(dvSign * dvAxis, dvLength, 0.0);",
dvonbeck09e12a62016-08-12 12:50:36 -0700186 args.fDistanceVectorName);
187
188 }
189 }
190
191 static void GenKey(const GrGeometryProcessor& gp,
Brian Salomon94efbf52016-11-29 13:43:05 -0500192 const GrShaderCaps&,
dvonbeck09e12a62016-08-12 12:50:36 -0700193 GrProcessorKeyBuilder* b) {
194 b->add32(0x0);
195 }
196
bsalomona624bf32016-09-20 09:12:47 -0700197 void setData(const GrGLSLProgramDataManager& pdman, const GrPrimitiveProcessor& primProc,
198 FPCoordTransformIter&& transformIter) override {
199 const RectGeometryProcessor& rgp = primProc.cast<RectGeometryProcessor>();
200 this->setTransformDataHelper(rgp.fLocalMatrix, pdman,&transformIter);
dvonbeck09e12a62016-08-12 12:50:36 -0700201 }
202
203 private:
204 typedef GrGLSLGeometryProcessor INHERITED;
205 };
206
Brian Salomon94efbf52016-11-29 13:43:05 -0500207 void getGLSLProcessorKey(const GrShaderCaps& caps, GrProcessorKeyBuilder* b) const override {
dvonbeck09e12a62016-08-12 12:50:36 -0700208 GLSLProcessor::GenKey(*this, caps, b);
209 }
210
Brian Salomon94efbf52016-11-29 13:43:05 -0500211 GrGLSLPrimitiveProcessor* createGLSLInstance(const GrShaderCaps&) const override {
dvonbeck09e12a62016-08-12 12:50:36 -0700212 return new GLSLProcessor();
213 }
214
215private:
216 SkMatrix fLocalMatrix;
217
218 const Attribute* fInPosition;
219 const Attribute* fInColor;
220 const Attribute* fInRectEdge;
221 const Attribute* fInWidthHeight;
222
223 GR_DECLARE_GEOMETRY_PROCESSOR_TEST;
224
225 typedef GrGeometryProcessor INHERITED;
226};
227
228GR_DEFINE_GEOMETRY_PROCESSOR_TEST(RectGeometryProcessor);
229
230sk_sp<GrGeometryProcessor> RectGeometryProcessor::TestCreate(GrProcessorTestData* d) {
231 return sk_sp<GrGeometryProcessor>(
232 new RectGeometryProcessor(GrTest::TestMatrix(d->fRandom)));
233}
234
235///////////////////////////////////////////////////////////////////////////////
236
237class AnalyticRectBatch : public GrVertexBatch {
238public:
Brian Salomon25a88092016-12-01 09:36:50 -0500239 DEFINE_OP_CLASS_ID
dvonbeck09e12a62016-08-12 12:50:36 -0700240
241 AnalyticRectBatch(GrColor color, const SkMatrix& viewMatrix, const SkRect& rect,
242 const SkRect& croppedRect, const SkRect& bounds)
243 : INHERITED(ClassID())
244 , fViewMatrixIfUsingLocalCoords(viewMatrix) {
245 SkPoint center = SkPoint::Make(rect.centerX(), rect.centerY());
246 viewMatrix.mapPoints(&center, 1);
247 SkScalar halfWidth = viewMatrix.mapRadius(SkScalarHalf(rect.width()));
248 SkScalar halfHeight = viewMatrix.mapRadius(SkScalarHalf(rect.height()));
249 SkVector downDir = viewMatrix.mapVector(0.0f, 1.0f);
250 downDir.normalize();
251
252 SkRect deviceSpaceCroppedRect = croppedRect;
253 viewMatrix.mapRect(&deviceSpaceCroppedRect);
254
255 fGeoData.emplace_back(Geometry {color, center, downDir, halfWidth, halfHeight,
256 deviceSpaceCroppedRect});
257
258 this->setBounds(bounds, HasAABloat::kYes, IsZeroArea::kNo);
259 }
260
261 const char* name() const override { return "AnalyticRectBatch"; }
262
263 SkString dumpInfo() const override {
264 SkString string;
265 for (int i = 0; i < fGeoData.count(); ++i) {
266 string.appendf("Color: 0x%08x Rect [C:(%.2f, %.2f) D:<%.2f,%.3f> W/2:%.2f H/2:%.2f]\n",
267 fGeoData[i].fColor,
268 fGeoData[i].fCenter.x(), fGeoData[i].fCenter.y(),
269 fGeoData[i].fDownDir.x(), fGeoData[i].fDownDir.y(),
270 fGeoData[i].fHalfWidth,
271 fGeoData[i].fHalfHeight);
272 }
Brian Salomon7c3e7182016-12-01 09:35:30 -0500273 string.append(DumpPipelineInfo(*this->pipeline()));
dvonbeck09e12a62016-08-12 12:50:36 -0700274 string.append(INHERITED::dumpInfo());
275 return string;
276 }
277
278 void computePipelineOptimizations(GrInitInvariantOutput* color,
279 GrInitInvariantOutput* coverage,
280 GrBatchToXPOverrides* overrides) const override {
281 // When this is called on a batch, there is only one geometry bundle
282 color->setKnownFourComponents(fGeoData[0].fColor);
283 coverage->setUnknownSingleComponent();
284 }
285
286private:
287 void initBatchTracker(const GrXPOverridesForBatch& overrides) override {
288 // Handle any overrides that affect our GP.
289 overrides.getOverrideColorIfSet(&fGeoData[0].fColor);
290 if (!overrides.readsLocalCoords()) {
291 fViewMatrixIfUsingLocalCoords.reset();
292 }
293 }
294
295 void onPrepareDraws(Target* target) const override {
296 SkMatrix localMatrix;
297 if (!fViewMatrixIfUsingLocalCoords.invert(&localMatrix)) {
298 return;
299 }
300
301 // Setup geometry processor
Hal Canary144caf52016-11-07 17:57:18 -0500302 sk_sp<GrGeometryProcessor> gp(new RectGeometryProcessor(localMatrix));
dvonbeck09e12a62016-08-12 12:50:36 -0700303
304 int instanceCount = fGeoData.count();
305 size_t vertexStride = gp->getVertexStride();
306 SkASSERT(vertexStride == sizeof(RectVertex));
307 QuadHelper helper;
308 RectVertex* verts = reinterpret_cast<RectVertex*>(helper.init(target, vertexStride,
309 instanceCount));
310 if (!verts) {
311 return;
312 }
313
314 for (int i = 0; i < instanceCount; i++) {
315 const Geometry& geom = fGeoData[i];
316
317 GrColor color = geom.fColor;
318 SkPoint center = geom.fCenter;
319 SkVector downDir = geom.fDownDir;
320 SkScalar halfWidth = geom.fHalfWidth;
321 SkScalar halfHeight = geom.fHalfHeight;
322 SkRect croppedRect = geom.fCroppedRect;
323
324 SkVector rightDir;
325 downDir.rotateCCW(&rightDir);
326
327 verts[0].fPos = {croppedRect.fLeft, croppedRect.fTop};
328 verts[0].fColor = color;
329 verts[0].fCenter = center;
330 verts[0].fDownDir = downDir;
331 verts[0].fHalfWidth = halfWidth;
332 verts[0].fHalfHeight = halfHeight;
333
334 verts[1].fPos = {croppedRect.fRight, croppedRect.fTop};
335 verts[1].fColor = color;
336 verts[1].fCenter = center;
337 verts[1].fDownDir = downDir;
338 verts[1].fHalfWidth = halfWidth;
339 verts[1].fHalfHeight = halfHeight;
340
341 verts[2].fPos = {croppedRect.fRight, croppedRect.fBottom};
342 verts[2].fColor = color;
343 verts[2].fCenter = center;
344 verts[2].fDownDir = downDir;
345 verts[2].fHalfWidth = halfWidth;
346 verts[2].fHalfHeight = halfHeight;
347
348 verts[3].fPos = {croppedRect.fLeft, croppedRect.fBottom};
349 verts[3].fColor = color;
350 verts[3].fCenter = center;
351 verts[3].fDownDir = downDir;
352 verts[3].fHalfWidth = halfWidth;
353 verts[3].fHalfHeight = halfHeight;
354
355 verts += kVerticesPerQuad;
356 }
Hal Canary144caf52016-11-07 17:57:18 -0500357 helper.recordDraw(target, gp.get());
dvonbeck09e12a62016-08-12 12:50:36 -0700358 }
359
Brian Salomon25a88092016-12-01 09:36:50 -0500360 bool onCombineIfPossible(GrOp* t, const GrCaps& caps) override {
dvonbeck09e12a62016-08-12 12:50:36 -0700361 AnalyticRectBatch* that = t->cast<AnalyticRectBatch>();
362 if (!GrPipeline::CanCombine(*this->pipeline(), this->bounds(), *that->pipeline(),
363 that->bounds(), caps)) {
364 return false;
365 }
366
367 if (!fViewMatrixIfUsingLocalCoords.cheapEqualTo(that->fViewMatrixIfUsingLocalCoords)) {
368 return false;
369 }
370
371 fGeoData.push_back_n(that->fGeoData.count(), that->fGeoData.begin());
372 this->joinBounds(*that);
373 return true;
374 }
375
376 struct Geometry {
377 GrColor fColor;
378 SkPoint fCenter;
379 SkVector fDownDir;
380 SkScalar fHalfWidth;
381 SkScalar fHalfHeight;
382 SkRect fCroppedRect;
383 };
384
385 SkMatrix fViewMatrixIfUsingLocalCoords;
386 SkSTArray<1, Geometry, true> fGeoData;
387
388 typedef GrVertexBatch INHERITED;
389};
390
391GrDrawBatch* GrAnalyticRectBatch::CreateAnalyticRectBatch(GrColor color,
392 const SkMatrix& viewMatrix,
393 const SkRect& rect,
394 const SkRect& croppedRect,
395 const SkRect& bounds) {
396 return new AnalyticRectBatch(color, viewMatrix, rect, croppedRect, bounds);
397}
398
399#ifdef GR_TEST_UTILS
400
401DRAW_BATCH_TEST_DEFINE(AnalyticRectBatch) {
402 SkMatrix viewMatrix = GrTest::TestMatrix(random);
403 GrColor color = GrRandomColor(random);
404 SkRect rect = GrTest::TestSquare(random);
405 SkRect croppedRect = GrTest::TestSquare(random);
406 SkRect bounds = GrTest::TestSquare(random);
407 return new AnalyticRectBatch(color, viewMatrix, rect, croppedRect, bounds);
408}
409
410#endif