• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "InstancedOp.h"
9 #include "InstanceProcessor.h"
10 #include "InstancedRendering.h"
11 #include "GrGpu.h"
12 #include "GrOpFlushState.h"
13 #include "GrRenderTargetOpList.h"
14 
15 
16 namespace gr_instanced {
17 
InstancedOp(uint32_t classID,GrPaint && paint,OpAllocator * alloc)18 InstancedOp::InstancedOp(uint32_t classID, GrPaint&& paint, OpAllocator* alloc)
19         : INHERITED(classID)
20         , fIsTracked(false)
21         , fRequiresBarrierOnOverlap(false)
22         , fAllowsSRGBInputs(paint.getAllowSRGBInputs())
23         , fDisableSRGBOutputConversion(paint.getDisableOutputConversionToSRGB())
24         , fNumDraws(1)
25         , fNumChangesInGeometry(0)
26         , fAllocator(alloc)
27         , fInstancedRendering(nullptr)
28         , fProcessors(std::move(paint)) {
29     fHeadDraw = fTailDraw = alloc->allocateDraw();
30 #ifdef SK_DEBUG
31     fHeadDraw->fGeometry = {-1, 0};
32 #endif
33     fHeadDraw->fNext = nullptr;
34 }
35 
~InstancedOp()36 InstancedOp::~InstancedOp() {
37     if (fIsTracked) {
38         fInstancedRendering->removeOp(this);
39     }
40 
41     Draw* draw = fHeadDraw;
42     while (draw) {
43         Draw* next = draw->fNext;
44         fAllocator->releaseDraw(draw);
45         draw = next;
46     }
47 }
48 
appendRRectParams(const SkRRect & rrect)49 void InstancedOp::appendRRectParams(const SkRRect& rrect) {
50     SkASSERT(!fIsTracked);
51     switch (rrect.getType()) {
52         case SkRRect::kSimple_Type: {
53             const SkVector& radii = rrect.getSimpleRadii();
54             this->appendParamsTexel(radii.x(), radii.y(), rrect.width(), rrect.height());
55             return;
56         }
57         case SkRRect::kNinePatch_Type: {
58             float twoOverW = 2 / rrect.width();
59             float twoOverH = 2 / rrect.height();
60             const SkVector& radiiTL = rrect.radii(SkRRect::kUpperLeft_Corner);
61             const SkVector& radiiBR = rrect.radii(SkRRect::kLowerRight_Corner);
62             this->appendParamsTexel(radiiTL.x() * twoOverW, radiiBR.x() * twoOverW,
63                                     radiiTL.y() * twoOverH, radiiBR.y() * twoOverH);
64             return;
65         }
66         case SkRRect::kComplex_Type: {
67             /**
68              * The x and y radii of each arc are stored in separate vectors,
69              * in the following order:
70              *
71              *        __x1 _ _ _ x3__
72              *    y1 |               | y2
73              *
74              *       |               |
75              *
76              *    y3 |__   _ _ _   __| y4
77              *          x2       x4
78              *
79              */
80             float twoOverW = 2 / rrect.width();
81             float twoOverH = 2 / rrect.height();
82             const SkVector& radiiTL = rrect.radii(SkRRect::kUpperLeft_Corner);
83             const SkVector& radiiTR = rrect.radii(SkRRect::kUpperRight_Corner);
84             const SkVector& radiiBR = rrect.radii(SkRRect::kLowerRight_Corner);
85             const SkVector& radiiBL = rrect.radii(SkRRect::kLowerLeft_Corner);
86             this->appendParamsTexel(radiiTL.x() * twoOverW, radiiBL.x() * twoOverW,
87                                     radiiTR.x() * twoOverW, radiiBR.x() * twoOverW);
88             this->appendParamsTexel(radiiTL.y() * twoOverH, radiiTR.y() * twoOverH,
89                                     radiiBL.y() * twoOverH, radiiBR.y() * twoOverH);
90             return;
91         }
92         default: return;
93     }
94 }
95 
appendParamsTexel(const SkScalar * vals,int count)96 void InstancedOp::appendParamsTexel(const SkScalar* vals, int count) {
97     SkASSERT(!fIsTracked);
98     SkASSERT(count <= 4 && count >= 0);
99     const float* valsAsFloats = vals; // Ensure SkScalar == float.
100     memcpy(&fParams.push_back(), valsAsFloats, count * sizeof(float));
101     fInfo.fHasParams = true;
102 }
103 
appendParamsTexel(SkScalar x,SkScalar y,SkScalar z,SkScalar w)104 void InstancedOp::appendParamsTexel(SkScalar x, SkScalar y, SkScalar z, SkScalar w) {
105     SkASSERT(!fIsTracked);
106     ParamsTexel& texel = fParams.push_back();
107     texel.fX = SkScalarToFloat(x);
108     texel.fY = SkScalarToFloat(y);
109     texel.fZ = SkScalarToFloat(z);
110     texel.fW = SkScalarToFloat(w);
111     fInfo.fHasParams = true;
112 }
113 
appendParamsTexel(SkScalar x,SkScalar y,SkScalar z)114 void InstancedOp::appendParamsTexel(SkScalar x, SkScalar y, SkScalar z) {
115     SkASSERT(!fIsTracked);
116     ParamsTexel& texel = fParams.push_back();
117     texel.fX = SkScalarToFloat(x);
118     texel.fY = SkScalarToFloat(y);
119     texel.fZ = SkScalarToFloat(z);
120     fInfo.fHasParams = true;
121 }
122 
finalize(const GrCaps & caps,const GrAppliedClip * clip)123 GrDrawOp::RequiresDstTexture InstancedOp::finalize(const GrCaps& caps, const GrAppliedClip* clip) {
124     GrProcessorAnalysisCoverage coverageInput;
125     bool isMixedSamples = false;
126     if (GrAAType::kCoverage == fInfo.aaType() ||
127         (GrAAType::kNone == fInfo.aaType() && !fInfo.isSimpleRects() && fInfo.fCannotDiscard)) {
128         coverageInput = GrProcessorAnalysisCoverage::kSingleChannel;
129     } else {
130         coverageInput = GrProcessorAnalysisCoverage::kNone;
131         isMixedSamples = GrAAType::kMixedSamples == fInfo.aaType();
132     }
133     GrProcessorSet::Analysis analysis =
134             fProcessors.finalize(this->getSingleInstance().fColor, coverageInput, clip,
135                                  isMixedSamples, caps, &this->getSingleDraw().fInstance.fColor);
136 
137     Draw& draw = this->getSingleDraw(); // This will assert if we have > 1 command.
138     SkASSERT(draw.fGeometry.isEmpty());
139     SkASSERT(SkIsPow2(fInfo.fShapeTypes));
140     SkASSERT(!fIsTracked);
141 
142     if (kRect_ShapeFlag == fInfo.fShapeTypes) {
143         draw.fGeometry = InstanceProcessor::GetIndexRangeForRect(fInfo.aaType());
144     } else if (kOval_ShapeFlag == fInfo.fShapeTypes) {
145         draw.fGeometry = InstanceProcessor::GetIndexRangeForOval(fInfo.aaType(), this->bounds());
146     } else {
147         draw.fGeometry = InstanceProcessor::GetIndexRangeForRRect(fInfo.aaType());
148     }
149 
150     fInfo.fCannotTweakAlphaForCoverage = !analysis.isCompatibleWithCoverageAsAlpha();
151 
152     fInfo.fUsesLocalCoords = analysis.usesLocalCoords();
153     fRequiresBarrierOnOverlap = analysis.requiresBarrierBetweenOverlappingDraws();
154     return analysis.requiresDstTexture() ? RequiresDstTexture::kYes : RequiresDstTexture::kNo;
155 }
156 
wasRecorded(GrRenderTargetOpList * opList)157 void InstancedOp::wasRecorded(GrRenderTargetOpList* opList) {
158     SkASSERT(!fInstancedRendering);
159     SkASSERT(!fIsTracked);
160 
161     fInstancedRendering = opList->instancedRendering();
162 
163     this->getSingleInstance().fInfo |= fInstancedRendering->addOpParams(this);
164     fInstancedRendering->addOp(this);
165     fIsTracked = true;
166 }
167 
onCombineIfPossible(GrOp * other,const GrCaps &)168 bool InstancedOp::onCombineIfPossible(GrOp* other, const GrCaps&) {
169     InstancedOp* that = static_cast<InstancedOp*>(other);
170     SkASSERT(!that->fInstancedRendering || (fInstancedRendering == that->fInstancedRendering));
171     SkASSERT(fTailDraw);
172     SkASSERT(that->fTailDraw);
173 
174     if (!OpInfo::CanCombine(fInfo, that->fInfo) || fProcessors != that->fProcessors) {
175         return false;
176     }
177 
178     if (fAllowsSRGBInputs != that->fAllowsSRGBInputs ||
179         fDisableSRGBOutputConversion != that->fDisableSRGBOutputConversion) {
180         return false;
181     }
182     SkASSERT(fRequiresBarrierOnOverlap == that->fRequiresBarrierOnOverlap);
183     if (fRequiresBarrierOnOverlap && this->bounds().intersects(that->bounds())) {
184         return false;
185     }
186     OpInfo combinedInfo = fInfo | that->fInfo;
187     if (!combinedInfo.isSimpleRects()) {
188         // This threshold was chosen with the "shapes_mixed" bench on a MacBook with Intel graphics.
189         // There seems to be a wide range where it doesn't matter if we combine or not. What matters
190         // is that the itty bitty rects combine with other shapes and the giant ones don't.
191         constexpr SkScalar kMaxPixelsToGeneralizeRects = 256 * 256;
192         if (fInfo.isSimpleRects() && fPixelLoad > kMaxPixelsToGeneralizeRects) {
193             return false;
194         }
195         if (that->fInfo.isSimpleRects() && that->fPixelLoad > kMaxPixelsToGeneralizeRects) {
196             return false;
197         }
198     }
199 
200     if (!that->fInstancedRendering) {
201         that->fInstancedRendering = fInstancedRendering;
202         that->getSingleInstance().fInfo |= fInstancedRendering->addOpParams(that);
203     }
204 
205     this->joinBounds(*that);
206     fInfo = combinedInfo;
207     fPixelLoad += that->fPixelLoad;
208     // Adopt the other op's draws.
209     fNumDraws += that->fNumDraws;
210     fNumChangesInGeometry += that->fNumChangesInGeometry;
211     if (fTailDraw->fGeometry != that->fHeadDraw->fGeometry) {
212         ++fNumChangesInGeometry;
213     }
214     fTailDraw->fNext = that->fHeadDraw;
215     fTailDraw = that->fTailDraw;
216 
217     that->fHeadDraw = that->fTailDraw = nullptr;
218 
219     return true;
220 }
221 
onExecute(GrOpFlushState * state)222 void InstancedOp::onExecute(GrOpFlushState* state) {
223     SkASSERT(fInstancedRendering->isFlushing());
224     SkASSERT(state->gpu() == fInstancedRendering->gpu());
225 
226     state->gpu()->handleDirtyContext();
227 
228     GrPipeline pipeline;
229     GrPipeline::InitArgs args;
230     args.fAppliedClip = state->drawOpArgs().fAppliedClip;
231     args.fCaps = &state->caps();
232     args.fResourceProvider = state->resourceProvider();
233     args.fProcessors = &fProcessors;
234     args.fFlags = GrAATypeIsHW(fInfo.aaType()) ? GrPipeline::kHWAntialias_Flag : 0;
235     if (fAllowsSRGBInputs) {
236         args.fFlags |= GrPipeline::kAllowSRGBInputs_Flag;
237     }
238     if (fDisableSRGBOutputConversion) {
239         args.fFlags |= GrPipeline::kDisableOutputConversionToSRGB_Flag;
240     }
241     args.fRenderTarget = state->drawOpArgs().fRenderTarget;
242     args.fDstProxy = state->drawOpArgs().fDstProxy;
243     pipeline.init(args);
244 
245     if (GrXferBarrierType barrierType = pipeline.xferBarrierType(*state->gpu()->caps())) {
246         state->gpu()->xferBarrier(pipeline.getRenderTarget(), barrierType);
247     }
248     fInstancedRendering->draw(pipeline, fInfo, this);
249 }
250 
251 ////////////////////////////////////////////////////////////////////////////////////////////////////
252 
OpAllocator(const GrCaps * caps)253 OpAllocator::OpAllocator(const GrCaps* caps)
254     : fDrawPool(1024, 1024)
255     , fCaps(sk_ref_sp(caps)) {
256 }
257 
~OpAllocator()258 OpAllocator::~OpAllocator() {}
259 
recordRect(const SkRect & rect,const SkMatrix & viewMatrix,GrPaint && paint,GrAA aa,const GrInstancedPipelineInfo & info)260 std::unique_ptr<GrDrawOp> OpAllocator::recordRect(const SkRect& rect,
261                                                   const SkMatrix& viewMatrix,
262                                                   GrPaint&& paint, GrAA aa,
263                                                   const GrInstancedPipelineInfo& info) {
264     return this->recordShape(ShapeType::kRect, rect, viewMatrix, std::move(paint), rect, aa, info);
265 }
266 
recordRect(const SkRect & rect,const SkMatrix & viewMatrix,GrPaint && paint,const SkRect & localRect,GrAA aa,const GrInstancedPipelineInfo & info)267 std::unique_ptr<GrDrawOp> OpAllocator::recordRect(const SkRect& rect,
268                                                   const SkMatrix& viewMatrix,
269                                                   GrPaint&& paint, const SkRect& localRect,
270                                                   GrAA aa,
271                                                   const GrInstancedPipelineInfo& info) {
272     return this->recordShape(ShapeType::kRect, rect, viewMatrix, std::move(paint), localRect, aa,
273                              info);
274 }
275 
recordRect(const SkRect & rect,const SkMatrix & viewMatrix,GrPaint && paint,const SkMatrix & localMatrix,GrAA aa,const GrInstancedPipelineInfo & info)276 std::unique_ptr<GrDrawOp> OpAllocator::recordRect(const SkRect& rect,
277                                                   const SkMatrix& viewMatrix,
278                                                   GrPaint&& paint,
279                                                   const SkMatrix& localMatrix, GrAA aa,
280                                                   const GrInstancedPipelineInfo& info) {
281     if (localMatrix.hasPerspective()) {
282         return nullptr; // Perspective is not yet supported in the local matrix.
283     }
284     if (std::unique_ptr<InstancedOp> op = this->recordShape(ShapeType::kRect, rect, viewMatrix,
285                                                             std::move(paint), rect, aa, info)) {
286         op->getSingleInstance().fInfo |= kLocalMatrix_InfoFlag;
287         op->appendParamsTexel(localMatrix.getScaleX(), localMatrix.getSkewX(),
288                               localMatrix.getTranslateX());
289         op->appendParamsTexel(localMatrix.getSkewY(), localMatrix.getScaleY(),
290                               localMatrix.getTranslateY());
291         op->fInfo.fHasLocalMatrix = true;
292         return std::move(op);
293     }
294     return nullptr;
295 }
296 
recordOval(const SkRect & oval,const SkMatrix & viewMatrix,GrPaint && paint,GrAA aa,const GrInstancedPipelineInfo & info)297 std::unique_ptr<GrDrawOp> OpAllocator::recordOval(const SkRect& oval,
298                                                   const SkMatrix& viewMatrix,
299                                                   GrPaint&& paint, GrAA aa,
300                                                   const GrInstancedPipelineInfo& info) {
301     return this->recordShape(ShapeType::kOval, oval, viewMatrix, std::move(paint), oval, aa, info);
302 }
303 
recordRRect(const SkRRect & rrect,const SkMatrix & viewMatrix,GrPaint && paint,GrAA aa,const GrInstancedPipelineInfo & info)304 std::unique_ptr<GrDrawOp> OpAllocator::recordRRect(const SkRRect& rrect,
305                                                    const SkMatrix& viewMatrix,
306                                                    GrPaint&& paint, GrAA aa,
307                                                    const GrInstancedPipelineInfo& info) {
308     if (std::unique_ptr<InstancedOp> op =
309                 this->recordShape(GetRRectShapeType(rrect), rrect.rect(), viewMatrix,
310                                   std::move(paint), rrect.rect(), aa, info)) {
311         op->appendRRectParams(rrect);
312         return std::move(op);
313     }
314     return nullptr;
315 }
316 
recordDRRect(const SkRRect & outer,const SkRRect & inner,const SkMatrix & viewMatrix,GrPaint && paint,GrAA aa,const GrInstancedPipelineInfo & info)317 std::unique_ptr<GrDrawOp> OpAllocator::recordDRRect(const SkRRect& outer,
318                                                     const SkRRect& inner,
319                                                     const SkMatrix& viewMatrix,
320                                                     GrPaint&& paint, GrAA aa,
321                                                     const GrInstancedPipelineInfo& info) {
322     if (inner.getType() > SkRRect::kSimple_Type) {
323        return nullptr; // Complex inner round rects are not yet supported.
324     }
325     if (SkRRect::kEmpty_Type == inner.getType()) {
326         return this->recordRRect(outer, viewMatrix, std::move(paint), aa, info);
327     }
328     if (std::unique_ptr<InstancedOp> op =
329                 this->recordShape(GetRRectShapeType(outer), outer.rect(), viewMatrix,
330                                   std::move(paint), outer.rect(), aa, info)) {
331         op->appendRRectParams(outer);
332         ShapeType innerShapeType = GetRRectShapeType(inner);
333         op->fInfo.fInnerShapeTypes |= GetShapeFlag(innerShapeType);
334         op->getSingleInstance().fInfo |= ((int)innerShapeType << kInnerShapeType_InfoBit);
335         op->appendParamsTexel(inner.rect().asScalars(), 4);
336         op->appendRRectParams(inner);
337         return std::move(op);
338     }
339     return nullptr;
340 }
341 
recordShape(ShapeType type,const SkRect & bounds,const SkMatrix & viewMatrix,GrPaint && paint,const SkRect & localRect,GrAA aa,const GrInstancedPipelineInfo & info)342 std::unique_ptr<InstancedOp> OpAllocator::recordShape(
343         ShapeType type, const SkRect& bounds, const SkMatrix& viewMatrix, GrPaint&& paint,
344         const SkRect& localRect, GrAA aa, const GrInstancedPipelineInfo& info) {
345 
346     if (info.fIsRenderingToFloat && fCaps->avoidInstancedDrawsToFPTargets()) {
347         return nullptr;
348     }
349 
350     GrAAType aaType;
351     if (!this->selectAntialiasMode(viewMatrix, aa, info, &aaType)) {
352         return nullptr;
353     }
354 
355     GrColor color = paint.getColor();
356     std::unique_ptr<InstancedOp> op = this->makeOp(std::move(paint));
357     op->fInfo.setAAType(aaType);
358     op->fInfo.fShapeTypes = GetShapeFlag(type);
359     op->fInfo.fCannotDiscard = true;
360     Instance& instance = op->getSingleInstance();
361     instance.fInfo = (int)type << kShapeType_InfoBit;
362 
363     InstancedOp::HasAABloat aaBloat =
364             (aaType == GrAAType::kCoverage) ? InstancedOp::HasAABloat::kYes
365                                             : InstancedOp::HasAABloat::kNo;
366     InstancedOp::IsZeroArea zeroArea = bounds.isEmpty() ? InstancedOp::IsZeroArea::kYes
367                                                         : InstancedOp::IsZeroArea::kNo;
368 
369     // The instanced shape renderer draws rectangles of [-1, -1, +1, +1], so we find the matrix that
370     // will map this rectangle to the same device coordinates as "viewMatrix * bounds".
371     float sx = 0.5f * bounds.width();
372     float sy = 0.5f * bounds.height();
373     float tx = sx + bounds.fLeft;
374     float ty = sy + bounds.fTop;
375     if (!viewMatrix.hasPerspective()) {
376         float* m = instance.fShapeMatrix2x3;
377         m[0] = viewMatrix.getScaleX() * sx;
378         m[1] = viewMatrix.getSkewX() * sy;
379         m[2] = viewMatrix.getTranslateX() +
380                viewMatrix.getScaleX() * tx + viewMatrix.getSkewX() * ty;
381 
382         m[3] = viewMatrix.getSkewY() * sx;
383         m[4] = viewMatrix.getScaleY() * sy;
384         m[5] = viewMatrix.getTranslateY() +
385                viewMatrix.getSkewY() * tx + viewMatrix.getScaleY() * ty;
386 
387         // Since 'm' is a 2x3 matrix that maps the rect [-1, +1] into the shape's device-space quad,
388         // it's quite simple to find the bounding rectangle:
389         float devBoundsHalfWidth = fabsf(m[0]) + fabsf(m[1]);
390         float devBoundsHalfHeight = fabsf(m[3]) + fabsf(m[4]);
391         SkRect opBounds;
392         opBounds.fLeft = m[2] - devBoundsHalfWidth;
393         opBounds.fRight = m[2] + devBoundsHalfWidth;
394         opBounds.fTop = m[5] - devBoundsHalfHeight;
395         opBounds.fBottom = m[5] + devBoundsHalfHeight;
396         op->setBounds(opBounds, aaBloat, zeroArea);
397 
398         // TODO: Is this worth the CPU overhead?
399         op->fInfo.fNonSquare =
400                 fabsf(devBoundsHalfHeight - devBoundsHalfWidth) > 0.5f ||  // Early out.
401                 fabs(m[0] * m[3] + m[1] * m[4]) > 1e-3f ||                 // Skew?
402                 fabs(m[0] * m[0] + m[1] * m[1] - m[3] * m[3] - m[4] * m[4]) >
403                         1e-2f;  // Diff. lengths?
404     } else {
405         SkMatrix shapeMatrix(viewMatrix);
406         shapeMatrix.preTranslate(tx, ty);
407         shapeMatrix.preScale(sx, sy);
408         instance.fInfo |= kPerspective_InfoFlag;
409 
410         float* m = instance.fShapeMatrix2x3;
411         m[0] = SkScalarToFloat(shapeMatrix.getScaleX());
412         m[1] = SkScalarToFloat(shapeMatrix.getSkewX());
413         m[2] = SkScalarToFloat(shapeMatrix.getTranslateX());
414         m[3] = SkScalarToFloat(shapeMatrix.getSkewY());
415         m[4] = SkScalarToFloat(shapeMatrix.getScaleY());
416         m[5] = SkScalarToFloat(shapeMatrix.getTranslateY());
417 
418         // Send the perspective column as a param.
419         op->appendParamsTexel(shapeMatrix[SkMatrix::kMPersp0], shapeMatrix[SkMatrix::kMPersp1],
420                               shapeMatrix[SkMatrix::kMPersp2]);
421         op->fInfo.fHasPerspective = true;
422 
423         op->setBounds(bounds, aaBloat, zeroArea);
424         op->fInfo.fNonSquare = true;
425     }
426 
427     instance.fColor = color;
428 
429     const float* rectAsFloats = localRect.asScalars(); // Ensure SkScalar == float.
430     memcpy(&instance.fLocalRect, rectAsFloats, 4 * sizeof(float));
431 
432     op->fPixelLoad = op->bounds().height() * op->bounds().width();
433     return op;
434 }
435 
selectAntialiasMode(const SkMatrix & viewMatrix,GrAA aa,const GrInstancedPipelineInfo & info,GrAAType * aaType)436 inline bool OpAllocator::selectAntialiasMode(const SkMatrix& viewMatrix, GrAA aa,
437                                              const GrInstancedPipelineInfo& info,
438                                              GrAAType* aaType) {
439     SkASSERT(!info.fIsMixedSampled || info.fIsMultisampled);
440     SkASSERT(GrCaps::InstancedSupport::kNone != fCaps->instancedSupport());
441 
442     if (!info.fIsMultisampled || fCaps->multisampleDisableSupport()) {
443         if (GrAA::kNo == aa) {
444             *aaType = GrAAType::kNone;
445             return true;
446         }
447 
448         if (info.canUseCoverageAA() && viewMatrix.preservesRightAngles()) {
449             *aaType = GrAAType::kCoverage;
450             return true;
451         }
452     }
453 
454     if (info.fIsMultisampled &&
455         fCaps->instancedSupport() >= GrCaps::InstancedSupport::kMultisampled) {
456         if (!info.fIsMixedSampled) {
457             *aaType = GrAAType::kMSAA;
458             return true;
459         }
460         if (fCaps->instancedSupport() >= GrCaps::InstancedSupport::kMixedSampled) {
461             *aaType = GrAAType::kMixedSamples;
462             return true;
463         }
464     }
465 
466     return false;
467 }
468 
469 }
470