Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(358)

Unified Diff: src/gpu/instanced/InstancedRendering.cpp

Issue 2120163002: Enable blend optimizations with instanced draws (Closed) Base URL: https://skia.googlesource.com/skia.git@upload_ingorecov
Patch Set: fixes for perf regressions Created 4 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « src/gpu/instanced/InstancedRendering.h ('k') | src/gpu/instanced/InstancedRenderingTypes.h » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: src/gpu/instanced/InstancedRendering.cpp
diff --git a/src/gpu/instanced/InstancedRendering.cpp b/src/gpu/instanced/InstancedRendering.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..d96bb397862c60e03ade568ad79534710a3bbd16
--- /dev/null
+++ b/src/gpu/instanced/InstancedRendering.cpp
@@ -0,0 +1,488 @@
+/*
+ * Copyright 2016 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "InstancedRendering.h"
+
+#include "GrBatchFlushState.h"
+#include "GrPipeline.h"
+#include "GrResourceProvider.h"
+#include "instanced/InstanceProcessor.h"
+
+namespace gr_instanced {
+
+InstancedRendering::InstancedRendering(GrGpu* gpu, AntialiasMode lastSupportedAAMode,
+ bool canRenderToFloat)
+ : fGpu(SkRef(gpu)),
+ fLastSupportedAAMode(lastSupportedAAMode),
+ fCanRenderToFloat(canRenderToFloat),
+ fState(State::kRecordingDraws),
+ fDrawPool(1024 * sizeof(Batch::Draw), 1024 * sizeof(Batch::Draw)) {
+}
+
+GrDrawBatch* InstancedRendering::recordRect(const SkRect& rect, const SkMatrix& viewMatrix,
+ GrColor color, bool antialias,
+ const GrInstancedPipelineInfo& info, bool* useHWAA) {
+ return this->recordShape(ShapeType::kRect, rect, viewMatrix, color, rect, antialias, info,
+ useHWAA);
+}
+
+GrDrawBatch* InstancedRendering::recordRect(const SkRect& rect, const SkMatrix& viewMatrix,
+ GrColor color, const SkRect& localRect, bool antialias,
+ const GrInstancedPipelineInfo& info, bool* useHWAA) {
+ return this->recordShape(ShapeType::kRect, rect, viewMatrix, color, localRect, antialias, info,
+ useHWAA);
+}
+
+GrDrawBatch* InstancedRendering::recordRect(const SkRect& rect, const SkMatrix& viewMatrix,
+ GrColor color, const SkMatrix& localMatrix,
+ bool antialias, const GrInstancedPipelineInfo& info,
+ bool* useHWAA) {
+ if (localMatrix.hasPerspective()) {
+ return nullptr; // Perspective is not yet supported in the local matrix.
+ }
+ if (Batch* batch = this->recordShape(ShapeType::kRect, rect, viewMatrix, color, rect, antialias,
+ info, useHWAA)) {
+ batch->getSingleInstance().fInfo |= kLocalMatrix_InfoFlag;
+ batch->appendParamsTexel(localMatrix.getScaleX(), localMatrix.getSkewX(),
+ localMatrix.getTranslateX());
+ batch->appendParamsTexel(localMatrix.getSkewY(), localMatrix.getScaleY(),
+ localMatrix.getTranslateY());
+ batch->fInfo.fHasLocalMatrix = true;
+ return batch;
+ }
+ return nullptr;
+}
+
+GrDrawBatch* InstancedRendering::recordOval(const SkRect& oval, const SkMatrix& viewMatrix,
+ GrColor color, bool antialias,
+ const GrInstancedPipelineInfo& info, bool* useHWAA) {
+ return this->recordShape(ShapeType::kOval, oval, viewMatrix, color, oval, antialias, info,
+ useHWAA);
+}
+
+GrDrawBatch* InstancedRendering::recordRRect(const SkRRect& rrect, const SkMatrix& viewMatrix,
+ GrColor color, bool antialias,
+ const GrInstancedPipelineInfo& info, bool* useHWAA) {
+ if (Batch* batch = this->recordShape(GetRRectShapeType(rrect), rrect.rect(), viewMatrix, color,
+ rrect.rect(), antialias, info, useHWAA)) {
+ batch->appendRRectParams(rrect);
+ return batch;
+ }
+ return nullptr;
+}
+
+GrDrawBatch* InstancedRendering::recordDRRect(const SkRRect& outer, const SkRRect& inner,
+ const SkMatrix& viewMatrix, GrColor color,
+ bool antialias, const GrInstancedPipelineInfo& info,
+ bool* useHWAA) {
+ if (inner.getType() > SkRRect::kSimple_Type) {
+ return nullptr; // Complex inner round rects are not yet supported.
+ }
+ if (SkRRect::kEmpty_Type == inner.getType()) {
+ return this->recordRRect(outer, viewMatrix, color, antialias, info, useHWAA);
+ }
+ if (Batch* batch = this->recordShape(GetRRectShapeType(outer), outer.rect(), viewMatrix, color,
+ outer.rect(), antialias, info, useHWAA)) {
+ batch->appendRRectParams(outer);
+ ShapeType innerShapeType = GetRRectShapeType(inner);
+ batch->fInfo.fInnerShapeTypes |= GetShapeFlag(innerShapeType);
+ batch->getSingleInstance().fInfo |= ((int)innerShapeType << kInnerShapeType_InfoBit);
+ batch->appendParamsTexel(inner.rect().asScalars(), 4);
+ batch->appendRRectParams(inner);
+ return batch;
+ }
+ return nullptr;
+}
+
+InstancedRendering::Batch* InstancedRendering::recordShape(ShapeType type, const SkRect& bounds,
+ const SkMatrix& viewMatrix,
+ GrColor color, const SkRect& localRect,
+ bool antialias,
+ const GrInstancedPipelineInfo& info,
+ bool* useHWAA) {
+ SkASSERT(State::kRecordingDraws == fState);
+
+ if (info.fIsRenderingToFloat && !fCanRenderToFloat) {
+ return nullptr;
+ }
+
+ AntialiasMode antialiasMode;
+ if (!this->selectAntialiasMode(viewMatrix, antialias, info, useHWAA, &antialiasMode)) {
+ return nullptr;
+ }
+
+ Batch* batch = this->createBatch();
+ batch->fInfo.fAntialiasMode = antialiasMode;
+ batch->fInfo.fShapeTypes = GetShapeFlag(type);
+ batch->fInfo.fCannotDiscard = !info.fCanDiscard;
+
+ Instance& instance = batch->getSingleInstance();
+ instance.fInfo = (int)type << kShapeType_InfoBit;
+
+ // The instanced shape renderer draws rectangles of [-1, -1, +1, +1], so we find the matrix that
+ // will map this rectangle to the same device coordinates as "viewMatrix * bounds".
+ float sx = 0.5f * bounds.width();
+ float sy = 0.5f * bounds.height();
+ float tx = sx + bounds.fLeft;
+ float ty = sy + bounds.fTop;
+ if (!viewMatrix.hasPerspective()) {
+ float* m = instance.fShapeMatrix2x3;
+ m[0] = viewMatrix.getScaleX() * sx;
+ m[1] = viewMatrix.getSkewX() * sy;
+ m[2] = viewMatrix.getTranslateX() +
+ viewMatrix.getScaleX() * tx + viewMatrix.getSkewX() * ty;
+
+ m[3] = viewMatrix.getSkewY() * sx;
+ m[4] = viewMatrix.getScaleY() * sy;
+ m[5] = viewMatrix.getTranslateY() +
+ viewMatrix.getSkewY() * tx + viewMatrix.getScaleY() * ty;
+
+ // Since 'm' is a 2x3 matrix that maps the rect [-1, +1] into the shape's device-space quad,
+ // it's quite simple to find the bounding rectangle:
+ float devBoundsHalfWidth = fabsf(m[0]) + fabsf(m[1]);
+ float devBoundsHalfHeight = fabsf(m[3]) + fabsf(m[4]);
+ batch->fBounds.fLeft = m[2] - devBoundsHalfWidth;
+ batch->fBounds.fRight = m[2] + devBoundsHalfWidth;
+ batch->fBounds.fTop = m[5] - devBoundsHalfHeight;
+ batch->fBounds.fBottom = m[5] + devBoundsHalfHeight;
+
+ // TODO: Is this worth the CPU overhead?
+ batch->fInfo.fNonSquare =
+ fabsf(devBoundsHalfHeight - devBoundsHalfWidth) > 0.5f || // Early out.
+ fabs(m[0] * m[3] + m[1] * m[4]) > 1e-3f || // Skew?
+ fabs(m[0] * m[0] + m[1] * m[1] - m[3] * m[3] - m[4] * m[4]) > 1e-2f; // Diff. lengths?
+ } else {
+ SkMatrix shapeMatrix(viewMatrix);
+ shapeMatrix.preTranslate(tx, ty);
+ shapeMatrix.preScale(sx, sy);
+ instance.fInfo |= kPerspective_InfoFlag;
+
+ float* m = instance.fShapeMatrix2x3;
+ m[0] = SkScalarToFloat(shapeMatrix.getScaleX());
+ m[1] = SkScalarToFloat(shapeMatrix.getSkewX());
+ m[2] = SkScalarToFloat(shapeMatrix.getTranslateX());
+ m[3] = SkScalarToFloat(shapeMatrix.getSkewY());
+ m[4] = SkScalarToFloat(shapeMatrix.getScaleY());
+ m[5] = SkScalarToFloat(shapeMatrix.getTranslateY());
+
+ // Send the perspective column as a param.
+ batch->appendParamsTexel(shapeMatrix[SkMatrix::kMPersp0], shapeMatrix[SkMatrix::kMPersp1],
+ shapeMatrix[SkMatrix::kMPersp2]);
+ batch->fInfo.fHasPerspective = true;
+
+ viewMatrix.mapRect(&batch->fBounds, bounds);
+
+ batch->fInfo.fNonSquare = true;
+ }
+
+ instance.fColor = color;
+
+ const float* rectAsFloats = localRect.asScalars(); // Ensure SkScalar == float.
+ memcpy(&instance.fLocalRect, rectAsFloats, 4 * sizeof(float));
+
+ batch->fPixelLoad = batch->fBounds.height() * batch->fBounds.width();
+ return batch;
+}
+
+inline bool InstancedRendering::selectAntialiasMode(const SkMatrix& viewMatrix, bool antialias,
+ const GrInstancedPipelineInfo& info,
+ bool* useHWAA, AntialiasMode* antialiasMode) {
+ SkASSERT(!info.fColorDisabled || info.fDrawingShapeToStencil);
+ SkASSERT(!info.fIsMixedSampled || info.fIsMultisampled);
+
+ if (!info.fIsMultisampled || fGpu->caps()->multisampleDisableSupport()) {
+ SkASSERT(fLastSupportedAAMode >= AntialiasMode::kCoverage);
+ if (!antialias) {
+ if (info.fDrawingShapeToStencil && !info.fCanDiscard) {
+ // We can't draw to the stencil buffer without discard (or sample mask if MSAA).
+ return false;
+ }
+ *antialiasMode = AntialiasMode::kNone;
+ *useHWAA = false;
+ return true;
+ }
+
+ if (info.canUseCoverageAA() && viewMatrix.preservesRightAngles()) {
+ *antialiasMode = AntialiasMode::kCoverage;
+ *useHWAA = false;
+ return true;
+ }
+ }
+
+ if (info.fIsMultisampled && fLastSupportedAAMode >= AntialiasMode::kMSAA) {
+ if (!info.fIsMixedSampled || info.fColorDisabled) {
+ *antialiasMode = AntialiasMode::kMSAA;
+ *useHWAA = true;
+ return true;
+ }
+ if (fLastSupportedAAMode >= AntialiasMode::kMixedSamples) {
+ *antialiasMode = AntialiasMode::kMixedSamples;
+ *useHWAA = true;
+ return true;
+ }
+ }
+
+ return false;
+}
+
+InstancedRendering::Batch::Batch(uint32_t classID, InstancedRendering* ir)
+ : INHERITED(classID),
+ fInstancedRendering(ir),
+ fIsTracked(false),
+ fNumDraws(1),
+ fNumChangesInGeometry(0) {
+ fHeadDraw = fTailDraw = (Draw*)fInstancedRendering->fDrawPool.allocate(sizeof(Draw));
+#ifdef SK_DEBUG
+ fHeadDraw->fGeometry = {-1, 0};
+#endif
+ fHeadDraw->fNext = nullptr;
+}
+
+InstancedRendering::Batch::~Batch() {
+ if (fIsTracked) {
+ fInstancedRendering->fTrackedBatches.remove(this);
+ }
+
+ Draw* draw = fHeadDraw;
+ while (draw) {
+ Draw* next = draw->fNext;
+ fInstancedRendering->fDrawPool.release(draw);
+ draw = next;
+ }
+}
+
+void InstancedRendering::Batch::appendRRectParams(const SkRRect& rrect) {
+ SkASSERT(!fIsTracked);
+ switch (rrect.getType()) {
+ case SkRRect::kSimple_Type: {
+ const SkVector& radii = rrect.getSimpleRadii();
+ this->appendParamsTexel(radii.x(), radii.y(), rrect.width(), rrect.height());
+ return;
+ }
+ case SkRRect::kNinePatch_Type: {
+ float twoOverW = 2 / rrect.width();
+ float twoOverH = 2 / rrect.height();
+ const SkVector& radiiTL = rrect.radii(SkRRect::kUpperLeft_Corner);
+ const SkVector& radiiBR = rrect.radii(SkRRect::kLowerRight_Corner);
+ this->appendParamsTexel(radiiTL.x() * twoOverW, radiiBR.x() * twoOverW,
+ radiiTL.y() * twoOverH, radiiBR.y() * twoOverH);
+ return;
+ }
+ case SkRRect::kComplex_Type: {
+ /**
+ * The x and y radii of each arc are stored in separate vectors,
+ * in the following order:
+ *
+ * __x1 _ _ _ x3__
+ * y1 | | y2
+ *
+ * | |
+ *
+ * y3 |__ _ _ _ __| y4
+ * x2 x4
+ *
+ */
+ float twoOverW = 2 / rrect.width();
+ float twoOverH = 2 / rrect.height();
+ const SkVector& radiiTL = rrect.radii(SkRRect::kUpperLeft_Corner);
+ const SkVector& radiiTR = rrect.radii(SkRRect::kUpperRight_Corner);
+ const SkVector& radiiBR = rrect.radii(SkRRect::kLowerRight_Corner);
+ const SkVector& radiiBL = rrect.radii(SkRRect::kLowerLeft_Corner);
+ this->appendParamsTexel(radiiTL.x() * twoOverW, radiiBL.x() * twoOverW,
+ radiiTR.x() * twoOverW, radiiBR.x() * twoOverW);
+ this->appendParamsTexel(radiiTL.y() * twoOverH, radiiTR.y() * twoOverH,
+ radiiBL.y() * twoOverH, radiiBR.y() * twoOverH);
+ return;
+ }
+ default: return;
+ }
+}
+
+void InstancedRendering::Batch::appendParamsTexel(const SkScalar* vals, int count) {
+ SkASSERT(!fIsTracked);
+ SkASSERT(count <= 4 && count >= 0);
+ const float* valsAsFloats = vals; // Ensure SkScalar == float.
+ memcpy(&fParams.push_back(), valsAsFloats, count * sizeof(float));
+ fInfo.fHasParams = true;
+}
+
+void InstancedRendering::Batch::appendParamsTexel(SkScalar x, SkScalar y, SkScalar z, SkScalar w) {
+ SkASSERT(!fIsTracked);
+ ParamsTexel& texel = fParams.push_back();
+ texel.fX = SkScalarToFloat(x);
+ texel.fY = SkScalarToFloat(y);
+ texel.fZ = SkScalarToFloat(z);
+ texel.fW = SkScalarToFloat(w);
+ fInfo.fHasParams = true;
+}
+
+void InstancedRendering::Batch::appendParamsTexel(SkScalar x, SkScalar y, SkScalar z) {
+ SkASSERT(!fIsTracked);
+ ParamsTexel& texel = fParams.push_back();
+ texel.fX = SkScalarToFloat(x);
+ texel.fY = SkScalarToFloat(y);
+ texel.fZ = SkScalarToFloat(z);
+ fInfo.fHasParams = true;
+}
+
+void InstancedRendering::Batch::computePipelineOptimizations(GrInitInvariantOutput* color,
+ GrInitInvariantOutput* coverage,
+ GrBatchToXPOverrides* overrides) const {
+ color->setKnownFourComponents(this->getSingleInstance().fColor);
+
+ if (AntialiasMode::kCoverage == fInfo.fAntialiasMode ||
+ (AntialiasMode::kNone == fInfo.fAntialiasMode &&
+ !fInfo.isSimpleRects() && fInfo.fCannotDiscard)) {
+ coverage->setUnknownSingleComponent();
+ } else {
+ coverage->setKnownSingleComponent(255);
+ }
+}
+
+void InstancedRendering::Batch::initBatchTracker(const GrXPOverridesForBatch& overrides) {
+ Draw& draw = this->getSingleDraw(); // This will assert if we have > 1 command.
+ SkASSERT(draw.fGeometry.isEmpty());
+ SkASSERT(SkIsPow2(fInfo.fShapeTypes));
+ SkASSERT(!fIsTracked);
+
+ if (kRect_ShapeFlag == fInfo.fShapeTypes) {
+ draw.fGeometry = InstanceProcessor::GetIndexRangeForRect(fInfo.fAntialiasMode);
+ } else if (kOval_ShapeFlag == fInfo.fShapeTypes) {
+ draw.fGeometry = InstanceProcessor::GetIndexRangeForOval(fInfo.fAntialiasMode, fBounds);
+ } else {
+ draw.fGeometry = InstanceProcessor::GetIndexRangeForRRect(fInfo.fAntialiasMode);
+ }
+
+ if (!fParams.empty()) {
+ SkASSERT(fInstancedRendering->fParams.count() < (int)kParamsIdx_InfoMask); // TODO: cleaner.
+ this->getSingleInstance().fInfo |= fInstancedRendering->fParams.count();
+ fInstancedRendering->fParams.push_back_n(fParams.count(), fParams.begin());
+ }
+
+ GrColor overrideColor;
+ if (overrides.getOverrideColorIfSet(&overrideColor)) {
+ SkASSERT(State::kRecordingDraws == fInstancedRendering->fState);
+ this->getSingleInstance().fColor = overrideColor;
+ }
+ fInfo.fUsesLocalCoords = overrides.readsLocalCoords();
+ fInfo.fCannotTweakAlphaForCoverage = !overrides.canTweakAlphaForCoverage();
+
+ fInstancedRendering->fTrackedBatches.addToTail(this);
+ fIsTracked = true;
+}
+
+bool InstancedRendering::Batch::onCombineIfPossible(GrBatch* other, const GrCaps& caps) {
+ Batch* that = static_cast<Batch*>(other);
+ SkASSERT(fInstancedRendering == that->fInstancedRendering);
+ SkASSERT(fTailDraw);
+ SkASSERT(that->fTailDraw);
+
+ if (!BatchInfo::CanCombine(fInfo, that->fInfo) ||
+ !GrPipeline::CanCombine(*this->pipeline(), this->bounds(),
+ *that->pipeline(), that->bounds(), caps)) {
+ return false;
+ }
+
+ BatchInfo combinedInfo = fInfo | that->fInfo;
+ if (!combinedInfo.isSimpleRects()) {
+ // This threshold was chosen with the "shapes_mixed" bench on a MacBook with Intel graphics.
+ // There seems to be a wide range where it doesn't matter if we combine or not. What matters
+ // is that the itty bitty rects combine with other shapes and the giant ones don't.
+ constexpr SkScalar kMaxPixelsToGeneralizeRects = 256 * 256;
+ if (fInfo.isSimpleRects() && fPixelLoad > kMaxPixelsToGeneralizeRects) {
+ return false;
+ }
+ if (that->fInfo.isSimpleRects() && that->fPixelLoad > kMaxPixelsToGeneralizeRects) {
+ return false;
+ }
+ }
+
+ fBounds.join(that->fBounds);
+ fInfo = combinedInfo;
+ fPixelLoad += that->fPixelLoad;
+
+ // Adopt the other batch's draws.
+ fNumDraws += that->fNumDraws;
+ fNumChangesInGeometry += that->fNumChangesInGeometry;
+ if (fTailDraw->fGeometry != that->fHeadDraw->fGeometry) {
+ ++fNumChangesInGeometry;
+ }
+ fTailDraw->fNext = that->fHeadDraw;
+ fTailDraw = that->fTailDraw;
+
+ that->fHeadDraw = that->fTailDraw = nullptr;
+
+ return true;
+}
+
+void InstancedRendering::beginFlush(GrResourceProvider* rp) {
+ SkASSERT(State::kRecordingDraws == fState);
+ fState = State::kFlushing;
+
+ if (fTrackedBatches.isEmpty()) {
+ return;
+ }
+
+ if (!fVertexBuffer) {
+ fVertexBuffer.reset(InstanceProcessor::FindOrCreateVertexBuffer(fGpu));
+ if (!fVertexBuffer) {
+ return;
+ }
+ }
+
+ if (!fIndexBuffer) {
+ fIndexBuffer.reset(InstanceProcessor::FindOrCreateIndex8Buffer(fGpu));
+ if (!fIndexBuffer) {
+ return;
+ }
+ }
+
+ if (!fParams.empty()) {
+ fParamsBuffer.reset(rp->createBuffer(fParams.count() * sizeof(ParamsTexel),
+ kTexel_GrBufferType, kDynamic_GrAccessPattern,
+ GrResourceProvider::kNoPendingIO_Flag,
+ fParams.begin()));
+ if (!fParamsBuffer) {
+ return;
+ }
+ }
+
+ this->onBeginFlush(rp);
+}
+
+void InstancedRendering::Batch::onDraw(GrBatchFlushState* state) {
+ SkASSERT(State::kFlushing == fInstancedRendering->fState);
+ SkASSERT(state->gpu() == fInstancedRendering->gpu());
+
+ state->gpu()->handleDirtyContext();
+ if (GrXferBarrierType barrierType = this->pipeline()->xferBarrierType(*state->gpu()->caps())) {
+ state->gpu()->xferBarrier(this->pipeline()->getRenderTarget(), barrierType);
+ }
+
+ InstanceProcessor instProc(fInfo, fInstancedRendering->fParamsBuffer);
+ fInstancedRendering->onDraw(*this->pipeline(), instProc, this);
+}
+
+void InstancedRendering::endFlush() {
+ // The caller is expected to delete all tracked batches (i.e. batches whose initBatchTracker
+ // method has been called) before ending the flush.
+ SkASSERT(fTrackedBatches.isEmpty());
+ fParams.reset();
+ fParamsBuffer.reset();
+ this->onEndFlush();
+ fState = State::kRecordingDraws;
+ // Hold on to the shape coords and index buffers.
+}
+
+void InstancedRendering::resetGpuResources(ResetType resetType) {
+ fVertexBuffer.reset();
+ fIndexBuffer.reset();
+ fParamsBuffer.reset();
+ this->onResetGpuResources(resetType);
+}
+
+}
« no previous file with comments | « src/gpu/instanced/InstancedRendering.h ('k') | src/gpu/instanced/InstancedRenderingTypes.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698