Index: src/gpu/GrInstancedRendering.cpp |
diff --git a/src/gpu/GrInstancedRendering.cpp b/src/gpu/GrInstancedRendering.cpp |
new file mode 100644 |
index 0000000000000000000000000000000000000000..ed331c380a7588c1dee7067f38fc1a5d7e97b5ea |
--- /dev/null |
+++ b/src/gpu/GrInstancedRendering.cpp |
@@ -0,0 +1,437 @@ |
+/* |
+ * 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 "GrInstancedRendering.h" |
+ |
+#include "GrBatchFlushState.h" |
+#include "GrPipeline.h" |
+#include "GrResourceProvider.h" |
+#include "effects/GrInstanceProcessor.h" |
+ |
+static const SkScalar* matrix_as_scalars(const SkMatrix& matrix) { |
+ const SkScalar* scalars = reinterpret_cast<const SkScalar*>(&matrix); |
+ SkASSERT(scalars[0] == matrix.getScaleX()); |
+ SkASSERT(scalars[1] == matrix.getSkewX()); |
+ SkASSERT(scalars[2] == matrix.getTranslateX()); |
+ SkASSERT(scalars[3] == matrix.getSkewY()); |
+ SkASSERT(scalars[4] == matrix.getScaleY()); |
+ SkASSERT(scalars[5] == matrix.getTranslateY()); |
+ SkASSERT(scalars[6] == matrix.getPerspX()); |
+ SkASSERT(scalars[7] == matrix.getPerspY()); |
+ SkASSERT(scalars[8] == matrix[8]); |
+ return scalars; |
+} |
+ |
+GrInstancedRendering::GrInstancedRendering(GrGpu* gpu, uint32_t supportedAAModes, |
+ size_t sizeofBatchClass) |
+ : fGpu(SkRef(gpu)), |
+ fSupportedAAModes(supportedAAModes), |
+ fState(State::kRecordingShapes), |
+ fBatchAllocator(sizeofBatchClass) { |
+ SkDEBUGCODE(fInUseBatchCount = 0;) |
+} |
+ |
+GrDrawBatch* GrInstancedRendering::recordRect(const SkRect& rect, const SkMatrix& viewMatrix, |
+ GrColor color, bool antialias, uint32_t flags, |
+ bool* requireHWAA) { |
+ return this->recordShape(kRect_ShapeType, rect, viewMatrix, color, rect, antialias, flags, |
+ requireHWAA); |
+} |
+ |
+GrDrawBatch* GrInstancedRendering::recordRect(const SkRect& rect, const SkMatrix& viewMatrix, |
+ GrColor color, const SkRect& localRect, |
+ bool antialias, uint32_t flags, bool* requireHWAA) { |
+ return this->recordShape(kRect_ShapeType, rect, viewMatrix, color, localRect, antialias, flags, |
+ requireHWAA); |
+} |
+ |
+GrDrawBatch* GrInstancedRendering::recordRect(const SkRect& rect, const SkMatrix& viewMatrix, |
+ GrColor color, const SkMatrix& localMatrix, |
+ bool antialias, uint32_t flags, bool* requireHWAA) { |
+ if (localMatrix.hasPerspective()) { |
+ return nullptr; // Perspective local matrix not supported yet. |
+ } |
+ if (Batch* batch = this->recordShape(kRect_ShapeType, rect, viewMatrix, color, rect, antialias, |
+ flags, requireHWAA)) { |
+ fInstances.back().fInfo |= kLocalMatrix_InfoFlag; |
+ this->appendParamsTexel(matrix_as_scalars(localMatrix), 3); |
+ this->appendParamsTexel(matrix_as_scalars(localMatrix) + 3, 3); |
+ batch->fTracker.fHasLocalMatrix = true; |
+ batch->fTracker.fHasParams = true; |
+ return batch; |
+ } |
+ return nullptr; |
+} |
+ |
+GrDrawBatch* GrInstancedRendering::recordOval(const SkRect& oval, const SkMatrix& viewMatrix, |
+ GrColor color, bool antialias, uint32_t flags, |
+ bool* requireHWAA) { |
+ return this->recordShape(kOval_ShapeType, oval, viewMatrix, color, oval, antialias, flags, |
+ requireHWAA); |
+} |
+ |
+GrDrawBatch* GrInstancedRendering::recordRRect(const SkRRect& rrect, const SkMatrix& viewMatrix, |
+ GrColor color, bool antialias, uint32_t flags, |
+ bool* requireHWAA) { |
+ if (Batch* batch = this->recordShape(RRectShapeType(rrect), rrect.rect(), viewMatrix, color, |
+ rrect.rect(), antialias, flags, requireHWAA)) { |
+ this->appendRRectParams(rrect, &batch->fTracker); |
+ return batch; |
+ } |
+ return nullptr; |
+} |
+ |
+GrDrawBatch* GrInstancedRendering::recordDRRect(const SkRRect& outer, const SkRRect& inner, |
+ const SkMatrix& viewMatrix, GrColor color, |
+ bool antialias, uint32_t flags, bool* requireHWAA) { |
+ if (inner.getType() > SkRRect::kSimple_Type) { |
+ return nullptr; // Complex inner rrects are not supported. |
+ } |
+ if (SkRRect::kEmpty_Type == inner.getType()) { |
+ return this->recordRRect(outer, viewMatrix, color, antialias, flags, requireHWAA); |
+ } |
+ if (Batch* batch = this->recordShape(RRectShapeType(outer), outer.rect(), viewMatrix, color, |
+ outer.rect(), antialias, flags, requireHWAA)) { |
+ this->appendRRectParams(outer, &batch->fTracker); |
+ ShapeType innerShapeType = RRectShapeType(inner); |
+ batch->fTracker.fInnerShapeTypes |= (1 << innerShapeType); |
+ fInstances.back().fInfo |= (innerShapeType << kInnerShapeType_InfoBit); |
+ this->appendParamsTexel(inner.rect().asScalars(), 4); |
+ this->appendRRectParams(inner, &batch->fTracker); |
+ batch->fTracker.fHasParams = true; |
+ return batch; |
+ } |
+ return nullptr; |
+} |
+ |
+GrInstancedRendering::Batch* GrInstancedRendering::recordShape(ShapeType type, const SkRect& bounds, |
+ const SkMatrix& viewMatrix, |
+ GrColor color, |
+ const SkRect& localRect, |
+ bool antialias, uint32_t flags, |
+ bool* requireHWAA) { |
+ SkASSERT(State::kRecordingShapes == fState); |
+ |
+ uint32_t paramsIdx = fParams.count(); |
+ if (paramsIdx > kParamsIdx_InfoMask) { |
+ return nullptr; // Params index is too large for its allotted space. |
+ } |
+ |
+ AntialiasMode aa; |
+ if (!this->selectAntialiasMode(viewMatrix, antialias, flags, &aa, requireHWAA)) { |
+ return nullptr; |
+ } |
+ // We can't return null after this point since requireHWAA might be set. |
+ |
+ Batch* batch = this->constructBatch(fBatchAllocator.push_back(), aa, flags, fInstances.count()); |
+ SkASSERT(batch == fBatchAllocator.back()); // We rely on batch ptr equality with the allocator. |
+ SkDEBUGCODE(++fInUseBatchCount;) |
+ batch->fTracker.fShapeTypes |= (1 << type); |
+ |
+ Instance& instance = fInstances.push_back(); |
+ instance.fInfo = (type << kShapeType_InfoBit) | paramsIdx; |
+ |
+ // 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[SkMatrix::kMScaleX] * sx; |
+ m[1] = viewMatrix[SkMatrix::kMSkewX] * sy; |
+ m[2] = viewMatrix[SkMatrix::kMTransX] + |
+ viewMatrix[SkMatrix::kMScaleX] * tx + viewMatrix[SkMatrix::kMSkewX] * ty; |
+ |
+ m[3] = viewMatrix[SkMatrix::kMSkewY] * sx; |
+ m[4] = viewMatrix[SkMatrix::kMScaleY] * sy; |
+ m[5] = viewMatrix[SkMatrix::kMTransY] + |
+ viewMatrix[SkMatrix::kMSkewY] * tx + viewMatrix[SkMatrix::kMScaleY] * ty; |
+ |
+ // Since 'm' is a 2x3 matrix that maps the rect [-1, -1, +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->fTracker.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; |
+#if SK_SCALAR_IS_FLOAT |
+ const float* matrixAsFloats = matrix_as_scalars(shapeMatrix); |
+ memcpy(instance.fShapeMatrix2x3, matrixAsFloats, 6 * sizeof(float)); |
+#else |
+ float* m = instance.fShapeMatrix2x3; |
+ m[0] == static_cast<float>(matrix.getScaleX()); |
+ m[1] == static_cast<float>(matrix.getSkewX()); |
+ m[2] == static_cast<float>(matrix.getTranslateX()); |
+ m[3] == static_cast<float>(matrix.getSkewY()); |
+ m[4] == static_cast<float>(matrix.getScaleY()); |
+ m[5] == static_cast<float>(matrix.getTranslateY()); |
+ m[6] == static_cast<float>(matrix.getPerspX()); |
+#endif |
+ // Send the perspective column as a param. |
+ this->appendParamsTexel(matrix_as_scalars(shapeMatrix) + 6, 3); |
+ batch->fTracker.fHasPerspective = true; |
+ batch->fTracker.fHasParams = true; |
+ |
+ viewMatrix.mapRect(&batch->fBounds, bounds); |
+ |
+ batch->fTracker.fNonSquare = true; |
+ } |
+ |
+ instance.fColor = color; |
+#if SK_SCALAR_IS_FLOAT |
+ const float* rectAsFloats = localRect.asScalars(); |
+ memcpy(&instance.fLocalRect, rectAsFloats, 4 * sizeof(float)); |
+#else |
+ instance.fLocalRect[0] = static_cast<float>(localRect.left()); |
+ instance.fLocalRect[1] = static_cast<float>(localRect.top()); |
+ instance.fLocalRect[2] = static_cast<float>(localRect.right()); |
+ instance.fLocalRect[3] = static_cast<float>(localRect.bottom()); |
+#endif |
+ |
+ return batch; |
+} |
+ |
+inline bool GrInstancedRendering::selectAntialiasMode(const SkMatrix& viewMatrix, bool antialias, |
+ uint32_t flags, AntialiasMode* antialiasMode, |
+ bool* requireHWAA) { |
+ SkASSERT(flags & (kColorWrite_Flag | kStencilWrite_Flag)); |
+ SkASSERT((flags & (kColorBufferMSAA_Flag | kStencilBufferMSAA_Flag)) != kColorBufferMSAA_Flag); |
+ |
+ if (!antialias) { |
+ if (!(fSupportedAAModes & kNone_AntialiasFlag)) { |
+ return false; |
+ } |
+ if ((flags & (kStencilWrite_Flag | kCanDiscardFragments_Flag)) == kStencilWrite_Flag) { |
+ // We can only subtract coverage from the stencil test via discard when no MSAA. |
+ return false; |
+ } |
+ *antialiasMode = kNone_AntialiasMode; |
+ return true; |
+ } |
+ |
+ if (!(flags & (kColorBufferMSAA_Flag | kStencilWrite_Flag)) && |
+ viewMatrix.preservesRightAngles()) { |
+ SkASSERT(fSupportedAAModes & kCoverage_AntialiasFlag); |
+ *antialiasMode = kCoverage_AntialiasMode; |
+ return true; |
+ } |
+ |
+ if ((fSupportedAAModes & kMSAA_AntialiasFlag) && (flags & kStencilBufferMSAA_Flag)) { |
+ if ((flags ^ kColorWrite_Flag) & (kColorWrite_Flag | kColorBufferMSAA_Flag)) { |
+ // We either do not write color, or the color buffer is multisampled. |
+ *antialiasMode = kMSAA_AntialiasMode; |
+ return true; |
+ } |
+ if (fSupportedAAModes & kMixedSamples_AntialiasFlag) { |
+ SkASSERT(requireHWAA); |
+ *antialiasMode = kMixedSamples_AntialiasMode; |
+ *requireHWAA = true; |
+ return true; |
+ } |
+ } |
+ |
+ return false; |
+} |
+ |
+void GrInstancedRendering::appendRRectParams(const SkRRect& rrect, BatchTracker* tracker) { |
+ switch (rrect.getType()) { |
+ case SkRRect::kSimple_Type: { |
+ const SkVector& radii = rrect.getSimpleRadii(); |
+ this->appendParamsTexel(radii.x(), radii.y(), rrect.width(), rrect.height()); |
+ tracker->fHasParams = true; |
+ 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); |
+ tracker->fHasParams = true; |
+ 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); |
+ tracker->fHasParams = true; |
+ return; |
+ } |
+ default: return; |
+ } |
+} |
+ |
+void GrInstancedRendering::appendParamsTexel(const SkScalar* vals, int count) { |
+ SkASSERT(count <= 4 && count >= 0); |
+#if SK_SCALAR_IS_FLOAT |
+ const float* valsAsFloats = vals; |
+ memcpy(&fParams.push_back(), valsAsFloats, count * sizeof(float)); |
+#else |
+ float* params = fParams.push_back().fValues; |
+ for (int i = 0; i < count; i++) { |
+ params[i] = static_cast<float>(vals[i]); |
+ } |
+#endif |
+} |
+ |
+void GrInstancedRendering::appendParamsTexel(float x, float y, float z, float w) { |
+ ParamsTexel& texel = fParams.push_back(); |
+ texel.fX = x; |
+ texel.fY = y; |
+ texel.fZ = z; |
+ texel.fW = w; |
+} |
+ |
+void GrInstancedRendering::appendParamsTexel(float x, float y, float z) { |
+ ParamsTexel& texel = fParams.push_back(); |
+ texel.fX = x; |
+ texel.fY = y; |
+ texel.fZ = z; |
+} |
+ |
+void GrInstancedRendering::Batch::computePipelineOptimizations(GrInitInvariantOutput* color, |
+ GrInitInvariantOutput* coverage, |
+ GrBatchToXPOverrides* overrides) const { |
+ // We can't rely on fTracker here because it might change later as batches are combined. |
+ color->setUnknownFourComponents(); |
+ if (fAntialiasMode >= kMSAA_AntialiasMode) { |
+ coverage->setKnownSingleComponent(255); |
+ } else if (kNone_AntialiasMode == fAntialiasMode && (fFlags & kCanDiscardFragments_Flag)) { |
+ coverage->setKnownSingleComponent(255); |
+ } else { |
+ coverage->setUnknownSingleComponent(); |
+ } |
+} |
+ |
+void GrInstancedRendering::Batch::initBatchTracker(const GrXPOverridesForBatch& overrides) { |
+ fTracker.fUsesColor = overrides.readsColor(); |
+ fTracker.fUsesCoverage = overrides.readsCoverage(); |
+ fTracker.fUsesLocalCoords = overrides.readsLocalCoords(); |
+ fTracker.fCannotTweakAlphaForCoverage = !overrides.canTweakAlphaForCoverage(); |
+ |
+ GrColor overrideColor; |
+ if (overrides.getOverrideColorIfSet(&overrideColor)) { |
+ SkASSERT(State::kRecordingShapes == fInstancedRendering->fState); |
+ SkASSERT(!fIsCombined); |
+ fInstancedRendering->fInstances[fFirstInstanceIdx].fColor = overrideColor; |
+ } |
+} |
+ |
+void GrInstancedRendering::commitToGpu(GrResourceProvider* rp) { |
+ SkASSERT(State::kRecordingShapes == fState); |
+ fState = State::kDrawingBatches; |
+ |
+ if (fInstances.empty()) { |
+ return; |
+ } |
+ |
+ if (!fVertexBuffer) { |
+ fVertexBuffer.reset(GrInstanceProcessor::FindOrCreateVertexBuffer(fGpu)); |
+ if (!fVertexBuffer) { |
+ return; |
+ } |
+ } |
+ |
+ if (!fIndexBuffer) { |
+ fIndexBuffer.reset(GrInstanceProcessor::FindOrCreateIndex8Buffer(fGpu)); |
+ if (!fIndexBuffer) { |
+ return; |
+ } |
+ } |
+ |
+ fInstanceBuffer.reset(rp->createBuffer(fInstances.count() * sizeof(Instance), |
+ kVertex_GrBufferType, kDynamic_GrAccessPattern, |
+ GrResourceProvider::kNoPendingIO_Flag, |
+ fInstances.begin())); |
+ if (!fInstanceBuffer) { |
+ 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->onCommitToGpu(rp); |
+} |
+ |
+void GrInstancedRendering::Batch::onDraw(GrBatchFlushState* state) { |
+ SkASSERT(State::kDrawingBatches == fInstancedRendering->fState); |
+ SkASSERT(state->gpu() == fInstancedRendering->gpu()); |
+ |
+ GrInstanceProcessor instProc(fTracker, fInstancedRendering->fParamsBuffer, fAntialiasMode, |
+ (fFlags & kCanDiscardFragments_Flag)); |
+ fInstancedRendering->onDraw(*this->pipeline(), instProc, this); |
+} |
+ |
+void GrInstancedRendering::Batch::onDelete() const { |
+ this->~Batch(); |
+ SkDEBUGCODE(--fInstancedRendering->fInUseBatchCount); |
+ if (this == fInstancedRendering->fBatchAllocator.back()) { |
+ fInstancedRendering->fBatchAllocator.pop_back(); |
+ } |
+} |
+ |
+void GrInstancedRendering::restart() { |
+ SkASSERT(0 == fInUseBatchCount); |
+ fBatchAllocator.reset(); |
+ // Hold on to the shape coords and index buffers. |
+ fInstances.reset(); |
+ fParams.reset(); |
+ fInstanceBuffer.reset(); |
+ fParamsBuffer.reset(); |
+ this->onRestart(); |
+ fState = State::kRecordingShapes; |
+} |
+ |
+void GrInstancedRendering::clearGpuResources(ClearType clearType) { |
+ fVertexBuffer.reset(); |
+ fIndexBuffer.reset(); |
+ fInstanceBuffer.reset(); |
+ fParamsBuffer.reset(); |
+ this->onClearGpuResources(clearType); |
+} |