| Index: src/gpu/GrInstancedRendering.cpp
|
| diff --git a/src/gpu/GrInstancedRendering.cpp b/src/gpu/GrInstancedRendering.cpp
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..75a029bc4832494582d5ef24af0fb24af28f0e74
|
| --- /dev/null
|
| +++ b/src/gpu/GrInstancedRendering.cpp
|
| @@ -0,0 +1,412 @@
|
| +/*
|
| + * 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"
|
| +
|
| +GrInstancedRendering::GrInstancedRendering(GrGpu* gpu, uint32_t supportedAAModes,
|
| + size_t sizeofBatchClass)
|
| + : fGpu(SkRef(gpu)),
|
| + fSupportedAAModes(supportedAAModes),
|
| + fState(State::kRecordingDraws),
|
| + fBatchAllocator(sizeofBatchClass) {
|
| + SkDEBUGCODE(fInUseBatchCount = 0;)
|
| +}
|
| +
|
| +GrDrawBatch* GrInstancedRendering::recordRect(const SkRect& rect, const SkMatrix& viewMatrix,
|
| + GrColor color, bool antialias, uint32_t flags,
|
| + bool* useHWAA) {
|
| + return this->recordShape(kRect_ShapeType, rect, viewMatrix, color, rect, antialias, flags,
|
| + useHWAA);
|
| +}
|
| +
|
| +GrDrawBatch* GrInstancedRendering::recordRect(const SkRect& rect, const SkMatrix& viewMatrix,
|
| + GrColor color, const SkRect& localRect,
|
| + bool antialias, uint32_t flags, bool* useHWAA) {
|
| + return this->recordShape(kRect_ShapeType, rect, viewMatrix, color, localRect, antialias, flags,
|
| + useHWAA);
|
| +}
|
| +
|
| +GrDrawBatch* GrInstancedRendering::recordRect(const SkRect& rect, const SkMatrix& viewMatrix,
|
| + GrColor color, const SkMatrix& localMatrix,
|
| + bool antialias, uint32_t flags, bool* useHWAA) {
|
| + if (localMatrix.hasPerspective()) {
|
| + return nullptr; // Perspective is not yet supported in the local matrix.
|
| + }
|
| + if (Batch* batch = this->recordShape(kRect_ShapeType, rect, viewMatrix, color, rect, antialias,
|
| + flags, useHWAA)) {
|
| + fInstances.back().fInfo |= kLocalMatrix_InfoFlag;
|
| + this->appendParamsTexel(localMatrix.getScaleX(), localMatrix.getSkewX(),
|
| + localMatrix.getTranslateX());
|
| + this->appendParamsTexel(localMatrix.getSkewY(), localMatrix.getScaleY(),
|
| + localMatrix.getTranslateY());
|
| + batch->fInfo.fHasLocalMatrix = true;
|
| + batch->fInfo.fHasParams = true;
|
| + return batch;
|
| + }
|
| + return nullptr;
|
| +}
|
| +
|
| +GrDrawBatch* GrInstancedRendering::recordOval(const SkRect& oval, const SkMatrix& viewMatrix,
|
| + GrColor color, bool antialias, uint32_t flags,
|
| + bool* useHWAA) {
|
| + return this->recordShape(kOval_ShapeType, oval, viewMatrix, color, oval, antialias, flags,
|
| + useHWAA);
|
| +}
|
| +
|
| +GrDrawBatch* GrInstancedRendering::recordRRect(const SkRRect& rrect, const SkMatrix& viewMatrix,
|
| + GrColor color, bool antialias, uint32_t flags,
|
| + bool* useHWAA) {
|
| + if (Batch* batch = this->recordShape(RRectShapeType(rrect), rrect.rect(), viewMatrix, color,
|
| + rrect.rect(), antialias, flags, useHWAA)) {
|
| + this->appendRRectParams(rrect, &batch->fInfo);
|
| + return batch;
|
| + }
|
| + return nullptr;
|
| +}
|
| +
|
| +GrDrawBatch* GrInstancedRendering::recordDRRect(const SkRRect& outer, const SkRRect& inner,
|
| + const SkMatrix& viewMatrix, GrColor color,
|
| + bool antialias, uint32_t flags, 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, flags, useHWAA);
|
| + }
|
| + if (Batch* batch = this->recordShape(RRectShapeType(outer), outer.rect(), viewMatrix, color,
|
| + outer.rect(), antialias, flags, useHWAA)) {
|
| + this->appendRRectParams(outer, &batch->fInfo);
|
| + ShapeType innerShapeType = RRectShapeType(inner);
|
| + batch->fInfo.fInnerShapeTypes |= (1 << innerShapeType);
|
| + fInstances.back().fInfo |= (innerShapeType << kInnerShapeType_InfoBit);
|
| + this->appendParamsTexel(inner.rect().asScalars(), 4);
|
| + this->appendRRectParams(inner, &batch->fInfo);
|
| + batch->fInfo.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* useHWAA) {
|
| + SkASSERT(State::kRecordingDraws == fState);
|
| +
|
| + uint32_t paramsIdx = fParams.count();
|
| + if (paramsIdx > kParamsIdx_InfoMask) {
|
| + return nullptr; // paramsIdx is too large for its allotted space.
|
| + }
|
| +
|
| + AntialiasMode antialiasMode;
|
| + if (!this->selectAntialiasMode(viewMatrix, antialias, flags, &antialiasMode, useHWAA)) {
|
| + return nullptr;
|
| + }
|
| +
|
| + Batch* batch = this->constructBatch(fBatchAllocator.push_back(), fInstances.count());
|
| + SkASSERT(batch == fBatchAllocator.back()); // We rely on batch ptr equality with the allocator.
|
| + SkDEBUGCODE(++fInUseBatchCount;)
|
| + batch->fInfo.fAntialiasMode = antialiasMode;
|
| + batch->fInfo.fShapeTypes = (1 << type);
|
| + batch->fInfo.fCannotDiscard = !(flags & kUseDiscard_Flag);
|
| +
|
| + 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.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.
|
| + this->appendParamsTexel(shapeMatrix[SkMatrix::kMPersp0], shapeMatrix[SkMatrix::kMPersp1],
|
| + shapeMatrix[SkMatrix::kMPersp2]);
|
| + batch->fInfo.fHasPerspective = true;
|
| + batch->fInfo.fHasParams = 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));
|
| +
|
| + return batch;
|
| +}
|
| +
|
| +inline bool GrInstancedRendering::selectAntialiasMode(const SkMatrix& viewMatrix, bool antialias,
|
| + uint32_t flags, AntialiasMode* antialiasMode,
|
| + bool* useHWAA) {
|
| + 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 | kUseDiscard_Flag)) == kStencilWrite_Flag) {
|
| + // We can only subtract coverage from the stencil output via discard when no MSAA.
|
| + return false;
|
| + }
|
| + *antialiasMode = kNone_AntialiasMode;
|
| + *useHWAA = false;
|
| + return true;
|
| + }
|
| +
|
| + if (!(flags & (kColorBufferMSAA_Flag | kStencilWrite_Flag)) &&
|
| + viewMatrix.preservesRightAngles()) {
|
| + SkASSERT(fSupportedAAModes & kCoverage_AntialiasFlag);
|
| + *antialiasMode = kCoverage_AntialiasMode;
|
| + *useHWAA = false;
|
| + 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;
|
| + *useHWAA = true;
|
| + return true;
|
| + }
|
| + if (fSupportedAAModes & kMixedSamples_AntialiasFlag) {
|
| + *antialiasMode = kMixedSamples_AntialiasMode;
|
| + *useHWAA = true;
|
| + return true;
|
| + }
|
| + }
|
| +
|
| + return false;
|
| +}
|
| +
|
| +void GrInstancedRendering::appendRRectParams(const SkRRect& rrect, BatchInfo* batchInfo) {
|
| + switch (rrect.getType()) {
|
| + case SkRRect::kSimple_Type: {
|
| + const SkVector& radii = rrect.getSimpleRadii();
|
| + this->appendParamsTexel(radii.x(), radii.y(), rrect.width(), rrect.height());
|
| + batchInfo->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);
|
| + batchInfo->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);
|
| + batchInfo->fHasParams = true;
|
| + return;
|
| + }
|
| + default: return;
|
| + }
|
| +}
|
| +
|
| +void GrInstancedRendering::appendParamsTexel(const SkScalar* vals, int count) {
|
| + SkASSERT(count <= 4 && count >= 0);
|
| + const float* valsAsFloats = vals; // Ensure SkScalar == float.
|
| + memcpy(&fParams.push_back(), valsAsFloats, count * sizeof(float));
|
| +}
|
| +
|
| +void GrInstancedRendering::appendParamsTexel(SkScalar x, SkScalar y, SkScalar z, SkScalar w) {
|
| + ParamsTexel& texel = fParams.push_back();
|
| + texel.fX = SkScalarToFloat(x);
|
| + texel.fY = SkScalarToFloat(y);
|
| + texel.fZ = SkScalarToFloat(z);
|
| + texel.fW = SkScalarToFloat(w);
|
| +}
|
| +
|
| +void GrInstancedRendering::appendParamsTexel(SkScalar x, SkScalar y, SkScalar z) {
|
| + ParamsTexel& texel = fParams.push_back();
|
| + texel.fX = SkScalarToFloat(x);
|
| + texel.fY = SkScalarToFloat(y);
|
| + texel.fZ = SkScalarToFloat(z);
|
| +}
|
| +
|
| +void GrInstancedRendering::Batch::computePipelineOptimizations(GrInitInvariantOutput* color,
|
| + GrInitInvariantOutput* coverage,
|
| + GrBatchToXPOverrides* overrides) const {
|
| + // We need to be careful about fInfo here and consider how it might change as batches combine.
|
| + // e.g. We can't make an assumption based on fInfo.isSimpleRects() because the batch might
|
| + // later combine with an oval.
|
| + color->setUnknownFourComponents();
|
| + if (fInfo.fAntialiasMode >= kMSAA_AntialiasMode) {
|
| + coverage->setKnownSingleComponent(255);
|
| + } else if (kNone_AntialiasMode == fInfo.fAntialiasMode && !fInfo.fCannotDiscard) {
|
| + // We can rely on fCannotDiscard because this particular field does not change.
|
| + coverage->setKnownSingleComponent(255);
|
| + } else {
|
| + coverage->setUnknownSingleComponent();
|
| + }
|
| +}
|
| +
|
| +void GrInstancedRendering::Batch::initBatchTracker(const GrXPOverridesForBatch& overrides) {
|
| + fInfo.fUsesLocalCoords = overrides.readsLocalCoords();
|
| + fInfo.fCannotTweakAlphaForCoverage = !overrides.canTweakAlphaForCoverage();
|
| +
|
| + GrColor overrideColor;
|
| + if (overrides.getOverrideColorIfSet(&overrideColor)) {
|
| + SkASSERT(State::kRecordingDraws == fInstancedRendering->fState);
|
| + SkASSERT(!fIsCombined);
|
| + fInstancedRendering->fInstances[fFirstInstanceIdx].fColor = overrideColor;
|
| + }
|
| +}
|
| +
|
| +void GrInstancedRendering::beginFlush(GrResourceProvider* rp) {
|
| + SkASSERT(State::kRecordingDraws == fState);
|
| + fState = State::kFlushing;
|
| +
|
| + 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->onBeginFlush(rp);
|
| +}
|
| +
|
| +void GrInstancedRendering::Batch::onDraw(GrBatchFlushState* state) {
|
| + SkASSERT(State::kFlushing == fInstancedRendering->fState);
|
| + SkASSERT(state->gpu() == fInstancedRendering->gpu());
|
| +
|
| + GrInstanceProcessor instProc(fInfo, fInstancedRendering->fParamsBuffer);
|
| + 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::endFlush() {
|
| + SkASSERT(0 == fInUseBatchCount);
|
| + fBatchAllocator.reset();
|
| + // Hold on to the shape coords and index buffers.
|
| + fInstances.reset();
|
| + fParams.reset();
|
| + fInstanceBuffer.reset();
|
| + fParamsBuffer.reset();
|
| + this->onEndFlush();
|
| + fState = State::kRecordingDraws;
|
| +}
|
| +
|
| +void GrInstancedRendering::resetGpuResources(ResetType resetType) {
|
| + fVertexBuffer.reset();
|
| + fIndexBuffer.reset();
|
| + fInstanceBuffer.reset();
|
| + fParamsBuffer.reset();
|
| + this->onResetGpuResources(resetType);
|
| +}
|
|
|