| Index: src/gpu/gl/GrGLInstancedRendering.cpp
|
| diff --git a/src/gpu/gl/GrGLInstancedRendering.cpp b/src/gpu/gl/GrGLInstancedRendering.cpp
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..eb2ab9f36aff04788612020c2ffbb2b502c5a6a1
|
| --- /dev/null
|
| +++ b/src/gpu/gl/GrGLInstancedRendering.cpp
|
| @@ -0,0 +1,295 @@
|
| +/*
|
| + * 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 "GrGLInstancedRendering.h"
|
| +
|
| +#include "GrGLBuffer.h"
|
| +#include "GrGLGpu.h"
|
| +#include "GrResourceProvider.h"
|
| +#include "effects/GrInstanceProcessor.h"
|
| +
|
| +#ifdef SK_DEBUG
|
| +#define DEBUG_PRINT(...) //SkDebugf(__VA_ARGS__)
|
| +#else
|
| +#define DEBUG_PRINT(...)
|
| +#endif
|
| +
|
| +#define GL_CALL(X) GR_GL_CALL(this->glGpu()->glInterface(), X)
|
| +
|
| +class GrGLInstancedRendering::GLBatch : public GrInstancedRendering::Batch {
|
| +public:
|
| + DEFINE_BATCH_CLASS_ID
|
| +
|
| + GLBatch(GrGLInstancedRendering* instRendering, int instanceIdx)
|
| + : INHERITED(ClassID(), instRendering, instanceIdx) {
|
| + }
|
| +
|
| + void initBatchTracker(const GrXPOverridesForBatch&) override;
|
| + bool onCombineIfPossible(GrBatch* other, const GrCaps& caps) override;
|
| +
|
| +private:
|
| + GrGLInstancedRendering* glInstancedRendering() const {
|
| + return static_cast<GrGLInstancedRendering*>(fInstancedRendering);
|
| + }
|
| +
|
| + SkSTArray<4, GrGLDrawElementsIndirectCommand, true> fDrawCmds;
|
| + GrGLDrawElementsIndirectCommand* fDrawCmdsOffsetInBuffer;
|
| +
|
| + friend class GrGLInstancedRendering;
|
| +
|
| + typedef Batch INHERITED;
|
| +};
|
| +
|
| +GrGLInstancedRendering* GrGLInstancedRendering::CreateIfSupported(GrGLGpu* gpu) {
|
| + const GrGLCaps& caps = gpu->glCaps();
|
| + if (!caps.vertexArrayObjectSupport() ||
|
| + !caps.drawIndirectSupport() ||
|
| + !caps.baseInstanceSupport()) {
|
| + return nullptr;
|
| + }
|
| + uint32_t supportedAAModes = GrInstanceProcessor::GetSupportedAAModes(*caps.glslCaps(), caps);
|
| + if (!caps.multisampleDisableSupport()) {
|
| + // The non-AA shaders require MSAA to be disabled.
|
| + supportedAAModes &= ~kNone_AntialiasFlag;
|
| + }
|
| + if (!supportedAAModes) {
|
| + return nullptr;
|
| + }
|
| + return new GrGLInstancedRendering(gpu, supportedAAModes);
|
| +}
|
| +
|
| +GrGLInstancedRendering::GrGLInstancedRendering(GrGLGpu* gpu, uint32_t supportedAAModes)
|
| + : INHERITED(gpu, supportedAAModes, sizeof(GLBatch)),
|
| + fVertexArrayID(0),
|
| + fInstanceBufferInVertexArrayID(SK_InvalidUniqueID),
|
| + fTotalDrawCmdCount(0) {
|
| +}
|
| +
|
| +GrGLInstancedRendering::~GrGLInstancedRendering() {
|
| + if (fVertexArrayID) {
|
| + GL_CALL(DeleteVertexArrays(1, &fVertexArrayID));
|
| + this->glGpu()->notifyVertexArrayDelete(fVertexArrayID);
|
| + }
|
| +}
|
| +
|
| +inline GrGLGpu* GrGLInstancedRendering::glGpu() const {
|
| + return static_cast<GrGLGpu*>(this->gpu());
|
| +}
|
| +
|
| +GrInstancedRendering::Batch* GrGLInstancedRendering::constructBatch(void* storage, int instIdx) {
|
| + return new (storage) GLBatch(this, instIdx);
|
| +}
|
| +
|
| +void GrGLInstancedRendering::GLBatch::initBatchTracker(const GrXPOverridesForBatch& overrides) {
|
| + SkASSERT(!fIsCombined);
|
| + SkASSERT(SkIsPow2(fInfo.fShapeTypes)); // There should only be one bit set at this point.
|
| +
|
| + INHERITED::initBatchTracker(overrides);
|
| +
|
| + GrGLDrawElementsIndirectCommand& cmd = fDrawCmds.push_back();
|
| + cmd.fBaseInstance = fFirstInstanceIdx;
|
| + cmd.fInstanceCount = 1;
|
| + if (kRect_ShapeFlag == fInfo.fShapeTypes) {
|
| + GrInstanceProcessor::GetIndexRangeForRect(fInfo.fAntialiasMode,
|
| + &cmd.fFirstIndex, &cmd.fCount);
|
| + } else if (kOval_ShapeFlag == fInfo.fShapeTypes) {
|
| + GrInstanceProcessor::GetIndexRangeForOval(fInfo.fAntialiasMode, fBounds,
|
| + &cmd.fFirstIndex, &cmd.fCount);
|
| + } else {
|
| + GrInstanceProcessor::GetIndexRangeForRRect(fInfo.fAntialiasMode,
|
| + &cmd.fFirstIndex, &cmd.fCount);
|
| + }
|
| + cmd.fBaseVertex = 0;
|
| +
|
| + ++this->glInstancedRendering()->fTotalDrawCmdCount;
|
| +}
|
| +
|
| +bool GrGLInstancedRendering::GLBatch::onCombineIfPossible(GrBatch* other, const GrCaps& caps) {
|
| + GLBatch* that = other->cast<GLBatch>();
|
| +
|
| + SkASSERT(fInstancedRendering == that->fInstancedRendering);
|
| + SkASSERT(fDrawCmds.count());
|
| + SkASSERT(that->fDrawCmds.count());
|
| +
|
| + if (!fInfo.canJoin(that->fInfo) ||
|
| + !GrPipeline::CanCombine(*this->pipeline(), this->bounds(),
|
| + *that->pipeline(), that->bounds(), caps)) {
|
| + return false;
|
| + }
|
| +
|
| + fBounds.join(that->fBounds);
|
| + fInfo.join(that->fInfo);
|
| +
|
| + // Join the draw commands.
|
| + int i = 0;
|
| + if (fDrawCmds.back().fBaseInstance + fDrawCmds.back().fInstanceCount ==
|
| + that->fDrawCmds.front().fBaseInstance &&
|
| + fDrawCmds.back().fFirstIndex == that->fDrawCmds.front().fFirstIndex) {
|
| + SkASSERT(fDrawCmds.back().fCount == that->fDrawCmds.front().fCount);
|
| + SkASSERT(0 == (fDrawCmds.back().fBaseVertex | that->fDrawCmds.back().fBaseVertex));
|
| + fDrawCmds.back().fInstanceCount += that->fDrawCmds.front().fInstanceCount;
|
| + ++i;
|
| + --this->glInstancedRendering()->fTotalDrawCmdCount;
|
| + }
|
| + if (i < that->fDrawCmds.count()) {
|
| + fDrawCmds.push_back_n(that->fDrawCmds.count() - i, &that->fDrawCmds[i]);
|
| + }
|
| +
|
| + return true;
|
| +}
|
| +
|
| +void GrGLInstancedRendering::onBeginFlush(GrResourceProvider* rp) {
|
| + SkASSERT(!fDrawIndirectBuffer);
|
| +
|
| + if (!fTotalDrawCmdCount) {
|
| + return; // All batches ended up getting culled.
|
| + }
|
| +
|
| + if (!fVertexArrayID) {
|
| + GL_CALL(GenVertexArrays(1, &fVertexArrayID));
|
| + if (!fVertexArrayID) {
|
| + return;
|
| + }
|
| + this->glGpu()->bindVertexArray(fVertexArrayID);
|
| +
|
| + // Attach our index buffer to the vertex array.
|
| + GL_CALL(BindBuffer(GR_GL_ELEMENT_ARRAY_BUFFER,
|
| + static_cast<const GrGLBuffer*>(this->indexBuffer())->bufferID()));
|
| +
|
| + // Set up the non-instanced attribs.
|
| + this->glGpu()->bindBuffer(kVertex_GrBufferType,
|
| + static_cast<const GrGLBuffer*>(this->vertexBuffer()));
|
| + GL_CALL(EnableVertexAttribArray(kShapeCoords_AttribIdx));
|
| + GL_CALL(VertexAttribPointer(kShapeCoords_AttribIdx, 2, GR_GL_FLOAT, GR_GL_FALSE,
|
| + sizeof(ShapeVertex), (void*) offsetof(ShapeVertex, fX)));
|
| + GL_CALL(EnableVertexAttribArray(kVertexAttrs_AttribIdx));
|
| + GL_CALL(VertexAttribIPointer(kVertexAttrs_AttribIdx, 1, GR_GL_INT, sizeof(ShapeVertex),
|
| + (void*) offsetof(ShapeVertex, fAttrs)));
|
| +
|
| + SkASSERT(SK_InvalidUniqueID == fInstanceBufferInVertexArrayID);
|
| + }
|
| +
|
| + fDrawIndirectBuffer.reset(rp->createBuffer(sizeof(GrGLDrawElementsIndirectCommand) *
|
| + fTotalDrawCmdCount, kDrawIndirect_GrBufferType,
|
| + kDynamic_GrAccessPattern,
|
| + GrResourceProvider::kNoPendingIO_Flag));
|
| + if (!fDrawIndirectBuffer) {
|
| + return;
|
| + }
|
| +
|
| + // Generate a draw indirect buffer based on the instanced batches in existence.
|
| + int idx = 0;
|
| + auto* mappedCmds = static_cast<GrGLDrawElementsIndirectCommand*>(fDrawIndirectBuffer->map());
|
| + SkDEBUGCODE(int inUseBatchCount = 0;)
|
| + for (BatchAllocator::Iter iter(this->batchAllocator()); iter.next();) {
|
| + GLBatch* batch = static_cast<GLBatch*>(iter.get());
|
| + if (!batch->fInUse) {
|
| + continue;
|
| + }
|
| + memcpy(&mappedCmds[idx], batch->fDrawCmds.begin(),
|
| + batch->fDrawCmds.count() * sizeof(GrGLDrawElementsIndirectCommand));
|
| + batch->fDrawCmdsOffsetInBuffer = (GrGLDrawElementsIndirectCommand*) nullptr + idx;
|
| + idx += batch->fDrawCmds.count();
|
| + SkDEBUGCODE(++inUseBatchCount;)
|
| + }
|
| + SkASSERT(fTotalDrawCmdCount == idx);
|
| + SkASSERT(inUseBatchCount == fInUseBatchCount);
|
| + fDrawIndirectBuffer->unmap();
|
| +}
|
| +
|
| +void GrGLInstancedRendering::onDraw(const GrPipeline& pipeline, const GrInstanceProcessor& instProc,
|
| + const Batch* baseBatch) {
|
| + if (!fDrawIndirectBuffer) {
|
| + return; // beginFlush was not successful.
|
| + }
|
| + if (!this->glGpu()->flushGLState(pipeline, instProc)) {
|
| + return;
|
| + }
|
| + this->flushAttribArrays();
|
| + this->glGpu()->bindBuffer(kDrawIndirect_GrBufferType,
|
| + static_cast<GrGLBuffer*>(fDrawIndirectBuffer.get()));
|
| +
|
| + const GLBatch* batch = static_cast<const GLBatch*>(baseBatch);
|
| + int numCommands = batch->fDrawCmds.count();
|
| +
|
| + if (1 == numCommands || !this->glGpu()->glCaps().multiDrawIndirectSupport()) {
|
| + for (int i = 0; i < numCommands; ++i) {
|
| + DEBUG_PRINT("DrawIndirect: [%u @ %u]\n",
|
| + batch->fDrawCmds[i].fInstanceCount, batch->fDrawCmds[i].fBaseInstance);
|
| + GL_CALL(DrawElementsIndirect(GR_GL_TRIANGLES, GR_GL_UNSIGNED_BYTE,
|
| + batch->fDrawCmdsOffsetInBuffer + i));
|
| + }
|
| + } else {
|
| +#ifdef SK_DEBUG
|
| + DEBUG_PRINT("MultiDrawIndirect:");
|
| + for (int i = 0; i < batch->fDrawCmds.count(); i++) {
|
| + DEBUG_PRINT(" [%u @ %u]", batch->fDrawCmds[i].fInstanceCount,
|
| + batch->fDrawCmds[i].fBaseInstance);
|
| + }
|
| + DEBUG_PRINT("\n");
|
| +#endif
|
| + GL_CALL(MultiDrawElementsIndirect(GR_GL_TRIANGLES, GR_GL_UNSIGNED_BYTE,
|
| + batch->fDrawCmdsOffsetInBuffer, numCommands, 0));
|
| + }
|
| +}
|
| +
|
| +void GrGLInstancedRendering::flushAttribArrays() {
|
| + SkASSERT(fVertexArrayID);
|
| + this->glGpu()->bindVertexArray(fVertexArrayID);
|
| +
|
| + if (fInstanceBufferInVertexArrayID != this->instanceBuffer()->getUniqueID()) {
|
| + this->glGpu()->bindBuffer(kVertex_GrBufferType,
|
| + static_cast<const GrGLBuffer*>(this->instanceBuffer()));
|
| +
|
| + // Info attrib.
|
| + GL_CALL(EnableVertexAttribArray(kInstanceInfo_AttribIdx));
|
| + GL_CALL(VertexAttribIPointer(kInstanceInfo_AttribIdx, 1, GR_GL_UNSIGNED_INT,
|
| + sizeof(Instance), (void*) offsetof(Instance, fInfo)));
|
| + GL_CALL(VertexAttribDivisor(kInstanceInfo_AttribIdx, 1));
|
| +
|
| + // Shape matrix attrib.
|
| + GL_CALL(EnableVertexAttribArray(kShapeMatrixX_AttribIdx));
|
| + GL_CALL(EnableVertexAttribArray(kShapeMatrixY_AttribIdx));
|
| + GL_CALL(VertexAttribPointer(kShapeMatrixX_AttribIdx, 3, GR_GL_FLOAT, GR_GL_FALSE,
|
| + sizeof(Instance),
|
| + (void*) offsetof(Instance, fShapeMatrix2x3[0])));
|
| + GL_CALL(VertexAttribPointer(kShapeMatrixY_AttribIdx, 3, GR_GL_FLOAT, GR_GL_FALSE,
|
| + sizeof(Instance),
|
| + (void*) offsetof(Instance, fShapeMatrix2x3[3])));
|
| + GL_CALL(VertexAttribDivisor(kShapeMatrixX_AttribIdx, 1));
|
| + GL_CALL(VertexAttribDivisor(kShapeMatrixY_AttribIdx, 1));
|
| +
|
| + // Color attrib.
|
| + GL_CALL(EnableVertexAttribArray(kColor_AttribIdx));
|
| + GL_CALL(VertexAttribPointer(kColor_AttribIdx, 4, GR_GL_UNSIGNED_BYTE, GR_GL_TRUE,
|
| + sizeof(Instance), (void*) offsetof(Instance, fColor)));
|
| + GL_CALL(VertexAttribDivisor(kColor_AttribIdx, 1));
|
| +
|
| + // Local rect attrib.
|
| + GL_CALL(EnableVertexAttribArray(kLocalRect_AttribIdx));
|
| + GL_CALL(VertexAttribPointer(kLocalRect_AttribIdx, 4, GR_GL_FLOAT, GR_GL_FALSE,
|
| + sizeof(Instance), (void*) offsetof(Instance, fLocalRect)));
|
| + GL_CALL(VertexAttribDivisor(kLocalRect_AttribIdx, 1));
|
| +
|
| + fInstanceBufferInVertexArrayID = this->instanceBuffer()->getUniqueID();
|
| + }
|
| +}
|
| +
|
| +void GrGLInstancedRendering::onEndFlush() {
|
| + fTotalDrawCmdCount = 0;
|
| + fDrawIndirectBuffer.reset();
|
| +}
|
| +
|
| +void GrGLInstancedRendering::onResetGpuResources(ResetType resetType) {
|
| + if (fVertexArrayID && ResetType::kDestroy == resetType) {
|
| + GL_CALL(DeleteVertexArrays(1, &fVertexArrayID));
|
| + this->glGpu()->notifyVertexArrayDelete(fVertexArrayID);
|
| + }
|
| + fVertexArrayID = 0;
|
| + fInstanceBufferInVertexArrayID = SK_InvalidUniqueID;
|
| + fDrawIndirectBuffer.reset();
|
| +}
|
|
|