blob: b13ea77a6604c7ed35da17866bb7388094165bfa [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"
21#include "glsl/GrGLSLProgramDataManager.h"
22#include "glsl/GrGLSLVarying.h"
23#include "glsl/GrGLSLVertexShaderBuilder.h"
24#include "glsl/GrGLSLUniformHandler.h"
25#include "glsl/GrGLSLUtil.h"
26
27namespace {
28
29struct RectVertex {
30 SkPoint fPos;
31 GrColor fColor;
32 SkPoint fCenter;
33 SkVector fDownDir;
34 SkScalar fHalfWidth;
35 SkScalar fHalfHeight;
36};
37
38}
39
40///////////////////////////////////////////////////////////////////////////////
41
42/**
43 * The output of this effect is the input color and coverage for an arbitrarily oriented rect. The
44 * rect is specified as:
45 * Center of the rect
46 * Unit vector point down the height of the rect
47 * Half width + 0.5
48 * Half height + 0.5
49 * The center and vector are stored in a vec4 varying ("RectEdge") with the
50 * center in the xy components and the vector in the zw components.
51 * The munged width and height are stored in a vec2 varying ("WidthHeight")
52 * with the width in x and the height in y.
53 */
54class RectGeometryProcessor : public GrGeometryProcessor {
55public:
56 RectGeometryProcessor(const SkMatrix& localMatrix) : fLocalMatrix(localMatrix) {
57 this->initClassID<RectGeometryProcessor>();
58 fInPosition = &this->addVertexAttrib(Attribute("inPosition", kVec2f_GrVertexAttribType,
59 kHigh_GrSLPrecision));
60 fInColor = &this->addVertexAttrib(Attribute("inColor", kVec4ub_GrVertexAttribType));
61 fInRectEdge = &this->addVertexAttrib(Attribute("inRectEdge", kVec4f_GrVertexAttribType));
62 fInWidthHeight = &this->addVertexAttrib(Attribute("inWidthHeight",
63 kVec2f_GrVertexAttribType));
64 }
65
66 bool implementsDistanceVector() const override { return true; };
67
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(),
124 args.fTransformsIn,
125 args.fTransformsOut);
126
127 // TODO: compute all these offsets, spans, and scales in the VS
128 fragBuilder->codeAppendf("float insetW = min(1.0, %s.x) - 0.5;",
129 widthHeightVary.fsIn());
130 fragBuilder->codeAppendf("float insetH = min(1.0, %s.y) - 0.5;",
131 widthHeightVary.fsIn());
132 fragBuilder->codeAppend("float outset = 0.5;");
133 // For rects > 1 pixel wide and tall the span's are noops (i.e., 1.0). For rects
134 // < 1 pixel wide or tall they serve to normalize the < 1 ramp to a 0 .. 1 range.
135 fragBuilder->codeAppend("float spanW = insetW + outset;");
136 fragBuilder->codeAppend("float spanH = insetH + outset;");
137 // For rects < 1 pixel wide or tall, these scale factors are used to cap the maximum
138 // value of coverage that is used. In other words it is the coverage that is
139 // used in the interior of the rect after the ramp.
140 fragBuilder->codeAppend("float scaleW = min(1.0, 2.0*insetW/spanW);");
141 fragBuilder->codeAppend("float scaleH = min(1.0, 2.0*insetH/spanH);");
142 // Compute the coverage for the rect's width
143 fragBuilder->codeAppendf("vec2 offset = %s.xy - %s.xy;",
144 positionVary.fsIn(), rectEdgeVary.fsIn());
145 fragBuilder->codeAppendf("float perpDot = abs(offset.x * %s.w - offset.y * %s.z);",
146 rectEdgeVary.fsIn(), rectEdgeVary.fsIn());
147
148 if (args.fDistanceVectorName) {
149 fragBuilder->codeAppendf("float widthDistance = %s.x - perpDot;",
150 widthHeightVary.fsIn());
151 }
152
153 fragBuilder->codeAppendf(
154 "float coverage = scaleW*clamp((%s.x-perpDot)/spanW, 0.0, 1.0);",
155 widthHeightVary.fsIn());
156 // Compute the coverage for the rect's height and merge with the width
157 fragBuilder->codeAppendf("perpDot = abs(dot(offset, %s.zw));",
158 rectEdgeVary.fsIn());
159
160 if (args.fDistanceVectorName) {
161 fragBuilder->codeAppendf("float heightDistance = %s.y - perpDot;",
162 widthHeightVary.fsIn());
163 }
164
165 fragBuilder->codeAppendf(
166 "coverage = coverage*scaleH*clamp((%s.y-perpDot)/spanH, 0.0, 1.0);",
167 widthHeightVary.fsIn());
168
169 fragBuilder->codeAppendf("%s = vec4(coverage);", args.fOutputCoverage);
170
171 if (args.fDistanceVectorName) {
172 fragBuilder->codeAppend( "// Calculating distance vector\n");
173 fragBuilder->codeAppend( "vec2 dvAxis;");
174 fragBuilder->codeAppend( "float dvLength;");
175
176 fragBuilder->codeAppend( "if (heightDistance < widthDistance) {");
177 fragBuilder->codeAppendf(" dvAxis = %s.zw;", rectEdgeVary.fsIn());
178 fragBuilder->codeAppend( " dvLength = heightDistance;");
179 fragBuilder->codeAppend( "} else {");
180 fragBuilder->codeAppendf(" dvAxis = vec2(-%s.w, %s.z);",
181 rectEdgeVary.fsIn(), rectEdgeVary.fsIn());
182 fragBuilder->codeAppend( " dvLength = widthDistance;");
183 fragBuilder->codeAppend( "}");
184
185 fragBuilder->codeAppend( "float dvSign = sign(dot(offset, dvAxis));");
jvanverth6c177a12016-08-17 07:59:41 -0700186 fragBuilder->codeAppendf("%s = vec4(dvSign * dvAxis, dvLength, 0.0);",
dvonbeck09e12a62016-08-12 12:50:36 -0700187 args.fDistanceVectorName);
188
189 }
190 }
191
192 static void GenKey(const GrGeometryProcessor& gp,
193 const GrGLSLCaps&,
194 GrProcessorKeyBuilder* b) {
195 b->add32(0x0);
196 }
197
198 void setData(const GrGLSLProgramDataManager& pdman,
199 const GrPrimitiveProcessor& gp) override {}
200
201 void setTransformData(const GrPrimitiveProcessor& primProc,
202 const GrGLSLProgramDataManager& pdman,
203 int index,
204 const SkTArray<const GrCoordTransform*, true>& transforms) override {
205 this->setTransformDataHelper<RectGeometryProcessor>(primProc, pdman, index, transforms);
206 }
207
208 private:
209 typedef GrGLSLGeometryProcessor INHERITED;
210 };
211
212 void getGLSLProcessorKey(const GrGLSLCaps& caps, GrProcessorKeyBuilder* b) const override {
213 GLSLProcessor::GenKey(*this, caps, b);
214 }
215
216 GrGLSLPrimitiveProcessor* createGLSLInstance(const GrGLSLCaps&) const override {
217 return new GLSLProcessor();
218 }
219
220private:
221 SkMatrix fLocalMatrix;
222
223 const Attribute* fInPosition;
224 const Attribute* fInColor;
225 const Attribute* fInRectEdge;
226 const Attribute* fInWidthHeight;
227
228 GR_DECLARE_GEOMETRY_PROCESSOR_TEST;
229
230 typedef GrGeometryProcessor INHERITED;
231};
232
233GR_DEFINE_GEOMETRY_PROCESSOR_TEST(RectGeometryProcessor);
234
235sk_sp<GrGeometryProcessor> RectGeometryProcessor::TestCreate(GrProcessorTestData* d) {
236 return sk_sp<GrGeometryProcessor>(
237 new RectGeometryProcessor(GrTest::TestMatrix(d->fRandom)));
238}
239
240///////////////////////////////////////////////////////////////////////////////
241
242class AnalyticRectBatch : public GrVertexBatch {
243public:
244 DEFINE_BATCH_CLASS_ID
245
246 AnalyticRectBatch(GrColor color, const SkMatrix& viewMatrix, const SkRect& rect,
247 const SkRect& croppedRect, const SkRect& bounds)
248 : INHERITED(ClassID())
249 , fViewMatrixIfUsingLocalCoords(viewMatrix) {
250 SkPoint center = SkPoint::Make(rect.centerX(), rect.centerY());
251 viewMatrix.mapPoints(&center, 1);
252 SkScalar halfWidth = viewMatrix.mapRadius(SkScalarHalf(rect.width()));
253 SkScalar halfHeight = viewMatrix.mapRadius(SkScalarHalf(rect.height()));
254 SkVector downDir = viewMatrix.mapVector(0.0f, 1.0f);
255 downDir.normalize();
256
257 SkRect deviceSpaceCroppedRect = croppedRect;
258 viewMatrix.mapRect(&deviceSpaceCroppedRect);
259
260 fGeoData.emplace_back(Geometry {color, center, downDir, halfWidth, halfHeight,
261 deviceSpaceCroppedRect});
262
263 this->setBounds(bounds, HasAABloat::kYes, IsZeroArea::kNo);
264 }
265
266 const char* name() const override { return "AnalyticRectBatch"; }
267
268 SkString dumpInfo() const override {
269 SkString string;
270 for (int i = 0; i < fGeoData.count(); ++i) {
271 string.appendf("Color: 0x%08x Rect [C:(%.2f, %.2f) D:<%.2f,%.3f> W/2:%.2f H/2:%.2f]\n",
272 fGeoData[i].fColor,
273 fGeoData[i].fCenter.x(), fGeoData[i].fCenter.y(),
274 fGeoData[i].fDownDir.x(), fGeoData[i].fDownDir.y(),
275 fGeoData[i].fHalfWidth,
276 fGeoData[i].fHalfHeight);
277 }
278 string.append(INHERITED::dumpInfo());
279 return string;
280 }
281
282 void computePipelineOptimizations(GrInitInvariantOutput* color,
283 GrInitInvariantOutput* coverage,
284 GrBatchToXPOverrides* overrides) const override {
285 // When this is called on a batch, there is only one geometry bundle
286 color->setKnownFourComponents(fGeoData[0].fColor);
287 coverage->setUnknownSingleComponent();
288 }
289
290private:
291 void initBatchTracker(const GrXPOverridesForBatch& overrides) override {
292 // Handle any overrides that affect our GP.
293 overrides.getOverrideColorIfSet(&fGeoData[0].fColor);
294 if (!overrides.readsLocalCoords()) {
295 fViewMatrixIfUsingLocalCoords.reset();
296 }
297 }
298
299 void onPrepareDraws(Target* target) const override {
300 SkMatrix localMatrix;
301 if (!fViewMatrixIfUsingLocalCoords.invert(&localMatrix)) {
302 return;
303 }
304
305 // Setup geometry processor
306 SkAutoTUnref<GrGeometryProcessor> gp(new RectGeometryProcessor(localMatrix));
307
308 int instanceCount = fGeoData.count();
309 size_t vertexStride = gp->getVertexStride();
310 SkASSERT(vertexStride == sizeof(RectVertex));
311 QuadHelper helper;
312 RectVertex* verts = reinterpret_cast<RectVertex*>(helper.init(target, vertexStride,
313 instanceCount));
314 if (!verts) {
315 return;
316 }
317
318 for (int i = 0; i < instanceCount; i++) {
319 const Geometry& geom = fGeoData[i];
320
321 GrColor color = geom.fColor;
322 SkPoint center = geom.fCenter;
323 SkVector downDir = geom.fDownDir;
324 SkScalar halfWidth = geom.fHalfWidth;
325 SkScalar halfHeight = geom.fHalfHeight;
326 SkRect croppedRect = geom.fCroppedRect;
327
328 SkVector rightDir;
329 downDir.rotateCCW(&rightDir);
330
331 verts[0].fPos = {croppedRect.fLeft, croppedRect.fTop};
332 verts[0].fColor = color;
333 verts[0].fCenter = center;
334 verts[0].fDownDir = downDir;
335 verts[0].fHalfWidth = halfWidth;
336 verts[0].fHalfHeight = halfHeight;
337
338 verts[1].fPos = {croppedRect.fRight, croppedRect.fTop};
339 verts[1].fColor = color;
340 verts[1].fCenter = center;
341 verts[1].fDownDir = downDir;
342 verts[1].fHalfWidth = halfWidth;
343 verts[1].fHalfHeight = halfHeight;
344
345 verts[2].fPos = {croppedRect.fRight, croppedRect.fBottom};
346 verts[2].fColor = color;
347 verts[2].fCenter = center;
348 verts[2].fDownDir = downDir;
349 verts[2].fHalfWidth = halfWidth;
350 verts[2].fHalfHeight = halfHeight;
351
352 verts[3].fPos = {croppedRect.fLeft, croppedRect.fBottom};
353 verts[3].fColor = color;
354 verts[3].fCenter = center;
355 verts[3].fDownDir = downDir;
356 verts[3].fHalfWidth = halfWidth;
357 verts[3].fHalfHeight = halfHeight;
358
359 verts += kVerticesPerQuad;
360 }
361 helper.recordDraw(target, gp);
362 }
363
364 bool onCombineIfPossible(GrBatch* t, const GrCaps& caps) override {
365 AnalyticRectBatch* that = t->cast<AnalyticRectBatch>();
366 if (!GrPipeline::CanCombine(*this->pipeline(), this->bounds(), *that->pipeline(),
367 that->bounds(), caps)) {
368 return false;
369 }
370
371 if (!fViewMatrixIfUsingLocalCoords.cheapEqualTo(that->fViewMatrixIfUsingLocalCoords)) {
372 return false;
373 }
374
375 fGeoData.push_back_n(that->fGeoData.count(), that->fGeoData.begin());
376 this->joinBounds(*that);
377 return true;
378 }
379
380 struct Geometry {
381 GrColor fColor;
382 SkPoint fCenter;
383 SkVector fDownDir;
384 SkScalar fHalfWidth;
385 SkScalar fHalfHeight;
386 SkRect fCroppedRect;
387 };
388
389 SkMatrix fViewMatrixIfUsingLocalCoords;
390 SkSTArray<1, Geometry, true> fGeoData;
391
392 typedef GrVertexBatch INHERITED;
393};
394
395GrDrawBatch* GrAnalyticRectBatch::CreateAnalyticRectBatch(GrColor color,
396 const SkMatrix& viewMatrix,
397 const SkRect& rect,
398 const SkRect& croppedRect,
399 const SkRect& bounds) {
400 return new AnalyticRectBatch(color, viewMatrix, rect, croppedRect, bounds);
401}
402
403#ifdef GR_TEST_UTILS
404
405DRAW_BATCH_TEST_DEFINE(AnalyticRectBatch) {
406 SkMatrix viewMatrix = GrTest::TestMatrix(random);
407 GrColor color = GrRandomColor(random);
408 SkRect rect = GrTest::TestSquare(random);
409 SkRect croppedRect = GrTest::TestSquare(random);
410 SkRect bounds = GrTest::TestSquare(random);
411 return new AnalyticRectBatch(color, viewMatrix, rect, croppedRect, bounds);
412}
413
414#endif