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