blob: fd9df6efe9dc7a73537c712c594f794ba08034cb [file] [log] [blame]
csmartdaltona7f29642016-07-07 08:49:11 -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 "InstancedRendering.h"
9
10#include "GrBatchFlushState.h"
11#include "GrPipeline.h"
12#include "GrResourceProvider.h"
13#include "instanced/InstanceProcessor.h"
14
15namespace gr_instanced {
16
17InstancedRendering::InstancedRendering(GrGpu* gpu, AntialiasMode lastSupportedAAMode,
18 bool canRenderToFloat)
19 : fGpu(SkRef(gpu)),
20 fLastSupportedAAMode(lastSupportedAAMode),
21 fCanRenderToFloat(canRenderToFloat),
22 fState(State::kRecordingDraws),
23 fDrawPool(1024 * sizeof(Batch::Draw), 1024 * sizeof(Batch::Draw)) {
24}
25
26GrDrawBatch* InstancedRendering::recordRect(const SkRect& rect, const SkMatrix& viewMatrix,
27 GrColor color, bool antialias,
28 const GrInstancedPipelineInfo& info, bool* useHWAA) {
29 return this->recordShape(ShapeType::kRect, rect, viewMatrix, color, rect, antialias, info,
30 useHWAA);
31}
32
33GrDrawBatch* InstancedRendering::recordRect(const SkRect& rect, const SkMatrix& viewMatrix,
34 GrColor color, const SkRect& localRect, bool antialias,
35 const GrInstancedPipelineInfo& info, bool* useHWAA) {
36 return this->recordShape(ShapeType::kRect, rect, viewMatrix, color, localRect, antialias, info,
37 useHWAA);
38}
39
40GrDrawBatch* InstancedRendering::recordRect(const SkRect& rect, const SkMatrix& viewMatrix,
41 GrColor color, const SkMatrix& localMatrix,
42 bool antialias, const GrInstancedPipelineInfo& info,
43 bool* useHWAA) {
44 if (localMatrix.hasPerspective()) {
45 return nullptr; // Perspective is not yet supported in the local matrix.
46 }
47 if (Batch* batch = this->recordShape(ShapeType::kRect, rect, viewMatrix, color, rect, antialias,
48 info, useHWAA)) {
49 batch->getSingleInstance().fInfo |= kLocalMatrix_InfoFlag;
50 batch->appendParamsTexel(localMatrix.getScaleX(), localMatrix.getSkewX(),
51 localMatrix.getTranslateX());
52 batch->appendParamsTexel(localMatrix.getSkewY(), localMatrix.getScaleY(),
53 localMatrix.getTranslateY());
54 batch->fInfo.fHasLocalMatrix = true;
55 return batch;
56 }
57 return nullptr;
58}
59
60GrDrawBatch* InstancedRendering::recordOval(const SkRect& oval, const SkMatrix& viewMatrix,
61 GrColor color, bool antialias,
62 const GrInstancedPipelineInfo& info, bool* useHWAA) {
63 return this->recordShape(ShapeType::kOval, oval, viewMatrix, color, oval, antialias, info,
64 useHWAA);
65}
66
67GrDrawBatch* InstancedRendering::recordRRect(const SkRRect& rrect, const SkMatrix& viewMatrix,
68 GrColor color, bool antialias,
69 const GrInstancedPipelineInfo& info, bool* useHWAA) {
70 if (Batch* batch = this->recordShape(GetRRectShapeType(rrect), rrect.rect(), viewMatrix, color,
71 rrect.rect(), antialias, info, useHWAA)) {
72 batch->appendRRectParams(rrect);
73 return batch;
74 }
75 return nullptr;
76}
77
78GrDrawBatch* InstancedRendering::recordDRRect(const SkRRect& outer, const SkRRect& inner,
79 const SkMatrix& viewMatrix, GrColor color,
80 bool antialias, const GrInstancedPipelineInfo& info,
81 bool* useHWAA) {
82 if (inner.getType() > SkRRect::kSimple_Type) {
83 return nullptr; // Complex inner round rects are not yet supported.
84 }
85 if (SkRRect::kEmpty_Type == inner.getType()) {
86 return this->recordRRect(outer, viewMatrix, color, antialias, info, useHWAA);
87 }
88 if (Batch* batch = this->recordShape(GetRRectShapeType(outer), outer.rect(), viewMatrix, color,
89 outer.rect(), antialias, info, useHWAA)) {
90 batch->appendRRectParams(outer);
91 ShapeType innerShapeType = GetRRectShapeType(inner);
92 batch->fInfo.fInnerShapeTypes |= GetShapeFlag(innerShapeType);
93 batch->getSingleInstance().fInfo |= ((int)innerShapeType << kInnerShapeType_InfoBit);
94 batch->appendParamsTexel(inner.rect().asScalars(), 4);
95 batch->appendRRectParams(inner);
96 return batch;
97 }
98 return nullptr;
99}
100
101InstancedRendering::Batch* InstancedRendering::recordShape(ShapeType type, const SkRect& bounds,
102 const SkMatrix& viewMatrix,
103 GrColor color, const SkRect& localRect,
104 bool antialias,
105 const GrInstancedPipelineInfo& info,
106 bool* useHWAA) {
107 SkASSERT(State::kRecordingDraws == fState);
108
109 if (info.fIsRenderingToFloat && !fCanRenderToFloat) {
110 return nullptr;
111 }
112
113 AntialiasMode antialiasMode;
114 if (!this->selectAntialiasMode(viewMatrix, antialias, info, useHWAA, &antialiasMode)) {
115 return nullptr;
116 }
117
118 Batch* batch = this->createBatch();
119 batch->fInfo.fAntialiasMode = antialiasMode;
120 batch->fInfo.fShapeTypes = GetShapeFlag(type);
121 batch->fInfo.fCannotDiscard = !info.fCanDiscard;
122
123 Instance& instance = batch->getSingleInstance();
124 instance.fInfo = (int)type << kShapeType_InfoBit;
125
bsalomon88cf17d2016-07-08 06:40:56 -0700126 Batch::HasAABloat aaBloat = (antialiasMode == AntialiasMode::kCoverage)
127 ? Batch::HasAABloat::kYes
128 : Batch::HasAABloat::kNo;
129 Batch::IsZeroArea zeroArea = (bounds.isEmpty()) ? Batch::IsZeroArea::kYes
130 : Batch::IsZeroArea::kNo;
131
csmartdaltona7f29642016-07-07 08:49:11 -0700132 // The instanced shape renderer draws rectangles of [-1, -1, +1, +1], so we find the matrix that
133 // will map this rectangle to the same device coordinates as "viewMatrix * bounds".
134 float sx = 0.5f * bounds.width();
135 float sy = 0.5f * bounds.height();
136 float tx = sx + bounds.fLeft;
137 float ty = sy + bounds.fTop;
138 if (!viewMatrix.hasPerspective()) {
139 float* m = instance.fShapeMatrix2x3;
140 m[0] = viewMatrix.getScaleX() * sx;
141 m[1] = viewMatrix.getSkewX() * sy;
142 m[2] = viewMatrix.getTranslateX() +
143 viewMatrix.getScaleX() * tx + viewMatrix.getSkewX() * ty;
144
145 m[3] = viewMatrix.getSkewY() * sx;
146 m[4] = viewMatrix.getScaleY() * sy;
147 m[5] = viewMatrix.getTranslateY() +
148 viewMatrix.getSkewY() * tx + viewMatrix.getScaleY() * ty;
149
150 // Since 'm' is a 2x3 matrix that maps the rect [-1, +1] into the shape's device-space quad,
151 // it's quite simple to find the bounding rectangle:
152 float devBoundsHalfWidth = fabsf(m[0]) + fabsf(m[1]);
153 float devBoundsHalfHeight = fabsf(m[3]) + fabsf(m[4]);
bsalomon88cf17d2016-07-08 06:40:56 -0700154 SkRect batchBounds;
155 batchBounds.fLeft = m[2] - devBoundsHalfWidth;
156 batchBounds.fRight = m[2] + devBoundsHalfWidth;
157 batchBounds.fTop = m[5] - devBoundsHalfHeight;
158 batchBounds.fBottom = m[5] + devBoundsHalfHeight;
159 batch->setBounds(batchBounds, aaBloat, zeroArea);
csmartdaltona7f29642016-07-07 08:49:11 -0700160
161 // TODO: Is this worth the CPU overhead?
162 batch->fInfo.fNonSquare =
163 fabsf(devBoundsHalfHeight - devBoundsHalfWidth) > 0.5f || // Early out.
164 fabs(m[0] * m[3] + m[1] * m[4]) > 1e-3f || // Skew?
165 fabs(m[0] * m[0] + m[1] * m[1] - m[3] * m[3] - m[4] * m[4]) > 1e-2f; // Diff. lengths?
166 } else {
167 SkMatrix shapeMatrix(viewMatrix);
168 shapeMatrix.preTranslate(tx, ty);
169 shapeMatrix.preScale(sx, sy);
170 instance.fInfo |= kPerspective_InfoFlag;
171
172 float* m = instance.fShapeMatrix2x3;
173 m[0] = SkScalarToFloat(shapeMatrix.getScaleX());
174 m[1] = SkScalarToFloat(shapeMatrix.getSkewX());
175 m[2] = SkScalarToFloat(shapeMatrix.getTranslateX());
176 m[3] = SkScalarToFloat(shapeMatrix.getSkewY());
177 m[4] = SkScalarToFloat(shapeMatrix.getScaleY());
178 m[5] = SkScalarToFloat(shapeMatrix.getTranslateY());
179
180 // Send the perspective column as a param.
181 batch->appendParamsTexel(shapeMatrix[SkMatrix::kMPersp0], shapeMatrix[SkMatrix::kMPersp1],
182 shapeMatrix[SkMatrix::kMPersp2]);
183 batch->fInfo.fHasPerspective = true;
184
bsalomon88cf17d2016-07-08 06:40:56 -0700185 batch->setBounds(bounds, aaBloat, zeroArea);
csmartdaltona7f29642016-07-07 08:49:11 -0700186 batch->fInfo.fNonSquare = true;
187 }
188
189 instance.fColor = color;
190
191 const float* rectAsFloats = localRect.asScalars(); // Ensure SkScalar == float.
192 memcpy(&instance.fLocalRect, rectAsFloats, 4 * sizeof(float));
193
bsalomon88cf17d2016-07-08 06:40:56 -0700194 batch->fPixelLoad = batch->bounds().height() * batch->bounds().width();
csmartdaltona7f29642016-07-07 08:49:11 -0700195 return batch;
196}
197
198inline bool InstancedRendering::selectAntialiasMode(const SkMatrix& viewMatrix, bool antialias,
199 const GrInstancedPipelineInfo& info,
200 bool* useHWAA, AntialiasMode* antialiasMode) {
201 SkASSERT(!info.fColorDisabled || info.fDrawingShapeToStencil);
202 SkASSERT(!info.fIsMixedSampled || info.fIsMultisampled);
203
204 if (!info.fIsMultisampled || fGpu->caps()->multisampleDisableSupport()) {
205 SkASSERT(fLastSupportedAAMode >= AntialiasMode::kCoverage);
206 if (!antialias) {
207 if (info.fDrawingShapeToStencil && !info.fCanDiscard) {
208 // We can't draw to the stencil buffer without discard (or sample mask if MSAA).
209 return false;
210 }
211 *antialiasMode = AntialiasMode::kNone;
212 *useHWAA = false;
213 return true;
214 }
215
216 if (info.canUseCoverageAA() && viewMatrix.preservesRightAngles()) {
217 *antialiasMode = AntialiasMode::kCoverage;
218 *useHWAA = false;
219 return true;
220 }
221 }
222
223 if (info.fIsMultisampled && fLastSupportedAAMode >= AntialiasMode::kMSAA) {
224 if (!info.fIsMixedSampled || info.fColorDisabled) {
225 *antialiasMode = AntialiasMode::kMSAA;
226 *useHWAA = true;
227 return true;
228 }
229 if (fLastSupportedAAMode >= AntialiasMode::kMixedSamples) {
230 *antialiasMode = AntialiasMode::kMixedSamples;
231 *useHWAA = true;
232 return true;
233 }
234 }
235
236 return false;
237}
238
239InstancedRendering::Batch::Batch(uint32_t classID, InstancedRendering* ir)
240 : INHERITED(classID),
241 fInstancedRendering(ir),
242 fIsTracked(false),
243 fNumDraws(1),
244 fNumChangesInGeometry(0) {
245 fHeadDraw = fTailDraw = (Draw*)fInstancedRendering->fDrawPool.allocate(sizeof(Draw));
246#ifdef SK_DEBUG
247 fHeadDraw->fGeometry = {-1, 0};
248#endif
249 fHeadDraw->fNext = nullptr;
250}
251
252InstancedRendering::Batch::~Batch() {
253 if (fIsTracked) {
254 fInstancedRendering->fTrackedBatches.remove(this);
255 }
256
257 Draw* draw = fHeadDraw;
258 while (draw) {
259 Draw* next = draw->fNext;
260 fInstancedRendering->fDrawPool.release(draw);
261 draw = next;
262 }
263}
264
265void InstancedRendering::Batch::appendRRectParams(const SkRRect& rrect) {
266 SkASSERT(!fIsTracked);
267 switch (rrect.getType()) {
268 case SkRRect::kSimple_Type: {
269 const SkVector& radii = rrect.getSimpleRadii();
270 this->appendParamsTexel(radii.x(), radii.y(), rrect.width(), rrect.height());
271 return;
272 }
273 case SkRRect::kNinePatch_Type: {
274 float twoOverW = 2 / rrect.width();
275 float twoOverH = 2 / rrect.height();
276 const SkVector& radiiTL = rrect.radii(SkRRect::kUpperLeft_Corner);
277 const SkVector& radiiBR = rrect.radii(SkRRect::kLowerRight_Corner);
278 this->appendParamsTexel(radiiTL.x() * twoOverW, radiiBR.x() * twoOverW,
279 radiiTL.y() * twoOverH, radiiBR.y() * twoOverH);
280 return;
281 }
282 case SkRRect::kComplex_Type: {
283 /**
284 * The x and y radii of each arc are stored in separate vectors,
285 * in the following order:
286 *
287 * __x1 _ _ _ x3__
288 * y1 | | y2
289 *
290 * | |
291 *
292 * y3 |__ _ _ _ __| y4
293 * x2 x4
294 *
295 */
296 float twoOverW = 2 / rrect.width();
297 float twoOverH = 2 / rrect.height();
298 const SkVector& radiiTL = rrect.radii(SkRRect::kUpperLeft_Corner);
299 const SkVector& radiiTR = rrect.radii(SkRRect::kUpperRight_Corner);
300 const SkVector& radiiBR = rrect.radii(SkRRect::kLowerRight_Corner);
301 const SkVector& radiiBL = rrect.radii(SkRRect::kLowerLeft_Corner);
302 this->appendParamsTexel(radiiTL.x() * twoOverW, radiiBL.x() * twoOverW,
303 radiiTR.x() * twoOverW, radiiBR.x() * twoOverW);
304 this->appendParamsTexel(radiiTL.y() * twoOverH, radiiTR.y() * twoOverH,
305 radiiBL.y() * twoOverH, radiiBR.y() * twoOverH);
306 return;
307 }
308 default: return;
309 }
310}
311
312void InstancedRendering::Batch::appendParamsTexel(const SkScalar* vals, int count) {
313 SkASSERT(!fIsTracked);
314 SkASSERT(count <= 4 && count >= 0);
315 const float* valsAsFloats = vals; // Ensure SkScalar == float.
316 memcpy(&fParams.push_back(), valsAsFloats, count * sizeof(float));
317 fInfo.fHasParams = true;
318}
319
320void InstancedRendering::Batch::appendParamsTexel(SkScalar x, SkScalar y, SkScalar z, SkScalar w) {
321 SkASSERT(!fIsTracked);
322 ParamsTexel& texel = fParams.push_back();
323 texel.fX = SkScalarToFloat(x);
324 texel.fY = SkScalarToFloat(y);
325 texel.fZ = SkScalarToFloat(z);
326 texel.fW = SkScalarToFloat(w);
327 fInfo.fHasParams = true;
328}
329
330void InstancedRendering::Batch::appendParamsTexel(SkScalar x, SkScalar y, SkScalar z) {
331 SkASSERT(!fIsTracked);
332 ParamsTexel& texel = fParams.push_back();
333 texel.fX = SkScalarToFloat(x);
334 texel.fY = SkScalarToFloat(y);
335 texel.fZ = SkScalarToFloat(z);
336 fInfo.fHasParams = true;
337}
338
339void InstancedRendering::Batch::computePipelineOptimizations(GrInitInvariantOutput* color,
340 GrInitInvariantOutput* coverage,
341 GrBatchToXPOverrides* overrides) const {
342 color->setKnownFourComponents(this->getSingleInstance().fColor);
343
344 if (AntialiasMode::kCoverage == fInfo.fAntialiasMode ||
345 (AntialiasMode::kNone == fInfo.fAntialiasMode &&
346 !fInfo.isSimpleRects() && fInfo.fCannotDiscard)) {
347 coverage->setUnknownSingleComponent();
348 } else {
349 coverage->setKnownSingleComponent(255);
350 }
351}
352
353void InstancedRendering::Batch::initBatchTracker(const GrXPOverridesForBatch& overrides) {
354 Draw& draw = this->getSingleDraw(); // This will assert if we have > 1 command.
355 SkASSERT(draw.fGeometry.isEmpty());
356 SkASSERT(SkIsPow2(fInfo.fShapeTypes));
357 SkASSERT(!fIsTracked);
358
359 if (kRect_ShapeFlag == fInfo.fShapeTypes) {
360 draw.fGeometry = InstanceProcessor::GetIndexRangeForRect(fInfo.fAntialiasMode);
361 } else if (kOval_ShapeFlag == fInfo.fShapeTypes) {
bsalomon88cf17d2016-07-08 06:40:56 -0700362 draw.fGeometry = InstanceProcessor::GetIndexRangeForOval(fInfo.fAntialiasMode,
363 this->bounds());
csmartdaltona7f29642016-07-07 08:49:11 -0700364 } else {
365 draw.fGeometry = InstanceProcessor::GetIndexRangeForRRect(fInfo.fAntialiasMode);
366 }
367
368 if (!fParams.empty()) {
369 SkASSERT(fInstancedRendering->fParams.count() < (int)kParamsIdx_InfoMask); // TODO: cleaner.
370 this->getSingleInstance().fInfo |= fInstancedRendering->fParams.count();
371 fInstancedRendering->fParams.push_back_n(fParams.count(), fParams.begin());
372 }
373
374 GrColor overrideColor;
375 if (overrides.getOverrideColorIfSet(&overrideColor)) {
376 SkASSERT(State::kRecordingDraws == fInstancedRendering->fState);
377 this->getSingleInstance().fColor = overrideColor;
378 }
379 fInfo.fUsesLocalCoords = overrides.readsLocalCoords();
380 fInfo.fCannotTweakAlphaForCoverage = !overrides.canTweakAlphaForCoverage();
381
382 fInstancedRendering->fTrackedBatches.addToTail(this);
383 fIsTracked = true;
384}
385
386bool InstancedRendering::Batch::onCombineIfPossible(GrBatch* other, const GrCaps& caps) {
387 Batch* that = static_cast<Batch*>(other);
388 SkASSERT(fInstancedRendering == that->fInstancedRendering);
389 SkASSERT(fTailDraw);
390 SkASSERT(that->fTailDraw);
391
392 if (!BatchInfo::CanCombine(fInfo, that->fInfo) ||
393 !GrPipeline::CanCombine(*this->pipeline(), this->bounds(),
394 *that->pipeline(), that->bounds(), caps)) {
395 return false;
396 }
397
398 BatchInfo combinedInfo = fInfo | that->fInfo;
399 if (!combinedInfo.isSimpleRects()) {
400 // This threshold was chosen with the "shapes_mixed" bench on a MacBook with Intel graphics.
401 // There seems to be a wide range where it doesn't matter if we combine or not. What matters
402 // is that the itty bitty rects combine with other shapes and the giant ones don't.
403 constexpr SkScalar kMaxPixelsToGeneralizeRects = 256 * 256;
404 if (fInfo.isSimpleRects() && fPixelLoad > kMaxPixelsToGeneralizeRects) {
405 return false;
406 }
407 if (that->fInfo.isSimpleRects() && that->fPixelLoad > kMaxPixelsToGeneralizeRects) {
408 return false;
409 }
410 }
411
bsalomon88cf17d2016-07-08 06:40:56 -0700412 this->joinBounds(*that);
csmartdaltona7f29642016-07-07 08:49:11 -0700413 fInfo = combinedInfo;
414 fPixelLoad += that->fPixelLoad;
415
416 // Adopt the other batch's draws.
417 fNumDraws += that->fNumDraws;
418 fNumChangesInGeometry += that->fNumChangesInGeometry;
419 if (fTailDraw->fGeometry != that->fHeadDraw->fGeometry) {
420 ++fNumChangesInGeometry;
421 }
422 fTailDraw->fNext = that->fHeadDraw;
423 fTailDraw = that->fTailDraw;
424
425 that->fHeadDraw = that->fTailDraw = nullptr;
426
427 return true;
428}
429
430void InstancedRendering::beginFlush(GrResourceProvider* rp) {
431 SkASSERT(State::kRecordingDraws == fState);
432 fState = State::kFlushing;
433
434 if (fTrackedBatches.isEmpty()) {
435 return;
436 }
437
438 if (!fVertexBuffer) {
439 fVertexBuffer.reset(InstanceProcessor::FindOrCreateVertexBuffer(fGpu));
440 if (!fVertexBuffer) {
441 return;
442 }
443 }
444
445 if (!fIndexBuffer) {
446 fIndexBuffer.reset(InstanceProcessor::FindOrCreateIndex8Buffer(fGpu));
447 if (!fIndexBuffer) {
448 return;
449 }
450 }
451
452 if (!fParams.empty()) {
453 fParamsBuffer.reset(rp->createBuffer(fParams.count() * sizeof(ParamsTexel),
454 kTexel_GrBufferType, kDynamic_GrAccessPattern,
455 GrResourceProvider::kNoPendingIO_Flag,
456 fParams.begin()));
457 if (!fParamsBuffer) {
458 return;
459 }
460 }
461
462 this->onBeginFlush(rp);
463}
464
465void InstancedRendering::Batch::onDraw(GrBatchFlushState* state) {
466 SkASSERT(State::kFlushing == fInstancedRendering->fState);
467 SkASSERT(state->gpu() == fInstancedRendering->gpu());
468
469 state->gpu()->handleDirtyContext();
470 if (GrXferBarrierType barrierType = this->pipeline()->xferBarrierType(*state->gpu()->caps())) {
471 state->gpu()->xferBarrier(this->pipeline()->getRenderTarget(), barrierType);
472 }
473
474 InstanceProcessor instProc(fInfo, fInstancedRendering->fParamsBuffer);
475 fInstancedRendering->onDraw(*this->pipeline(), instProc, this);
476}
477
478void InstancedRendering::endFlush() {
479 // The caller is expected to delete all tracked batches (i.e. batches whose initBatchTracker
480 // method has been called) before ending the flush.
481 SkASSERT(fTrackedBatches.isEmpty());
482 fParams.reset();
483 fParamsBuffer.reset();
484 this->onEndFlush();
485 fState = State::kRecordingDraws;
486 // Hold on to the shape coords and index buffers.
487}
488
489void InstancedRendering::resetGpuResources(ResetType resetType) {
490 fVertexBuffer.reset();
491 fIndexBuffer.reset();
492 fParamsBuffer.reset();
493 this->onResetGpuResources(resetType);
494}
495
496}