/* * 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 "GLInstancedRendering.h" #include "GrResourceProvider.h" #include "gl/GrGLGpu.h" #include "instanced/InstanceProcessor.h" #define GL_CALL(X) GR_GL_CALL(this->glGpu()->glInterface(), X) namespace gr_instanced { class GLInstancedRendering::GLOp final : public InstancedRendering::Op { public: DEFINE_OP_CLASS_ID GLOp(GLInstancedRendering* instRendering) : INHERITED(ClassID(), instRendering) {} int numGLCommands() const { return 1 + fNumChangesInGeometry; } private: int fEmulatedBaseInstance; int fGLDrawCmdsIdx; friend class GLInstancedRendering; typedef Op INHERITED; }; GrCaps::InstancedSupport GLInstancedRendering::CheckSupport(const GrGLCaps& glCaps) { // This method is only intended to be used for initializing fInstancedSupport in the caps. SkASSERT(GrCaps::InstancedSupport::kNone == glCaps.instancedSupport()); if (!glCaps.vertexArrayObjectSupport() || (!glCaps.drawIndirectSupport() && !glCaps.drawInstancedSupport())) { return GrCaps::InstancedSupport::kNone; } return InstanceProcessor::CheckSupport(*glCaps.shaderCaps(), glCaps); } GLInstancedRendering::GLInstancedRendering(GrGLGpu* gpu) : INHERITED(gpu), fVertexArrayID(0), fGLDrawCmdsInfo(0), fInstanceAttribsBufferUniqueId(SK_InvalidUniqueID) { SkASSERT(GrCaps::InstancedSupport::kNone != this->gpu()->caps()->instancedSupport()); } GLInstancedRendering::~GLInstancedRendering() { if (fVertexArrayID) { GL_CALL(DeleteVertexArrays(1, &fVertexArrayID)); this->glGpu()->notifyVertexArrayDelete(fVertexArrayID); } } inline GrGLGpu* GLInstancedRendering::glGpu() const { return static_cast(this->gpu()); } sk_sp GLInstancedRendering::makeOp() { return sk_sp(new GLOp(this)); } void GLInstancedRendering::onBeginFlush(GrResourceProvider* rp) { // Count what there is to draw. OpList::Iter iter; iter.init(this->trackedOps(), OpList::Iter::kHead_IterStart); int numGLInstances = 0; int numGLDrawCmds = 0; while (Op* o = iter.get()) { GLOp* op = static_cast(o); iter.next(); numGLInstances += op->fNumDraws; numGLDrawCmds += op->numGLCommands(); } if (!numGLDrawCmds) { return; } SkASSERT(numGLInstances); // Lazily create a vertex array object. if (!fVertexArrayID) { GL_CALL(GenVertexArrays(1, &fVertexArrayID)); if (!fVertexArrayID) { return; } this->glGpu()->bindVertexArray(fVertexArrayID); // Attach our index buffer to the vertex array. SkASSERT(!this->indexBuffer()->isCPUBacked()); GL_CALL(BindBuffer(GR_GL_ELEMENT_ARRAY_BUFFER, static_cast(this->indexBuffer())->bufferID())); // Set up the non-instanced attribs. this->glGpu()->bindBuffer(kVertex_GrBufferType, this->vertexBuffer()); GL_CALL(EnableVertexAttribArray((int)Attrib::kShapeCoords)); GL_CALL(VertexAttribPointer((int)Attrib::kShapeCoords, 2, GR_GL_FLOAT, GR_GL_FALSE, sizeof(ShapeVertex), (void*) offsetof(ShapeVertex, fX))); GL_CALL(EnableVertexAttribArray((int)Attrib::kVertexAttrs)); GL_CALL(VertexAttribIPointer((int)Attrib::kVertexAttrs, 1, GR_GL_INT, sizeof(ShapeVertex), (void*) offsetof(ShapeVertex, fAttrs))); SkASSERT(fInstanceAttribsBufferUniqueId.isInvalid()); } // Create and map instance and draw-indirect buffers. SkASSERT(!fInstanceBuffer); fInstanceBuffer.reset( rp->createBuffer(sizeof(Instance) * numGLInstances, kVertex_GrBufferType, kDynamic_GrAccessPattern, GrResourceProvider::kNoPendingIO_Flag | GrResourceProvider::kRequireGpuMemory_Flag)); if (!fInstanceBuffer) { return; } SkASSERT(!fDrawIndirectBuffer); if (this->glGpu()->glCaps().drawIndirectSupport()) { fDrawIndirectBuffer.reset( rp->createBuffer(sizeof(GrGLDrawElementsIndirectCommand) * numGLDrawCmds, kDrawIndirect_GrBufferType, kDynamic_GrAccessPattern, GrResourceProvider::kNoPendingIO_Flag | GrResourceProvider::kRequireGpuMemory_Flag)); if (!fDrawIndirectBuffer) { return; } } Instance* glMappedInstances = static_cast(fInstanceBuffer->map()); SkASSERT(glMappedInstances); int glInstancesIdx = 0; GrGLDrawElementsIndirectCommand* glMappedCmds = nullptr; int glDrawCmdsIdx = 0; if (fDrawIndirectBuffer) { glMappedCmds = static_cast(fDrawIndirectBuffer->map()); SkASSERT(glMappedCmds); } bool baseInstanceSupport = this->glGpu()->glCaps().baseInstanceSupport(); SkASSERT(!baseInstanceSupport || fDrawIndirectBuffer); SkASSERT(!fGLDrawCmdsInfo); if (GR_GL_LOG_INSTANCED_OPS || !baseInstanceSupport) { fGLDrawCmdsInfo.reset(numGLDrawCmds); } // Generate the instance and draw-indirect buffer contents based on the tracked ops. iter.init(this->trackedOps(), OpList::Iter::kHead_IterStart); while (Op* o = iter.get()) { GLOp* op = static_cast(o); iter.next(); op->fEmulatedBaseInstance = baseInstanceSupport ? 0 : glInstancesIdx; op->fGLDrawCmdsIdx = glDrawCmdsIdx; const Op::Draw* draw = op->fHeadDraw; SkASSERT(draw); do { int instanceCount = 0; IndexRange geometry = draw->fGeometry; SkASSERT(!geometry.isEmpty()); do { glMappedInstances[glInstancesIdx + instanceCount++] = draw->fInstance; draw = draw->fNext; } while (draw && draw->fGeometry == geometry); if (fDrawIndirectBuffer) { GrGLDrawElementsIndirectCommand& glCmd = glMappedCmds[glDrawCmdsIdx]; glCmd.fCount = geometry.fCount; glCmd.fInstanceCount = instanceCount; glCmd.fFirstIndex = geometry.fStart; glCmd.fBaseVertex = 0; glCmd.fBaseInstance = baseInstanceSupport ? glInstancesIdx : 0; } if (GR_GL_LOG_INSTANCED_OPS || !baseInstanceSupport) { GLDrawCmdInfo& cmdInfo = fGLDrawCmdsInfo[glDrawCmdsIdx]; cmdInfo.fGeometry = geometry; cmdInfo.fInstanceCount = instanceCount; } glInstancesIdx += instanceCount; ++glDrawCmdsIdx; } while (draw); } SkASSERT(glDrawCmdsIdx == numGLDrawCmds); if (fDrawIndirectBuffer) { fDrawIndirectBuffer->unmap(); } SkASSERT(glInstancesIdx == numGLInstances); fInstanceBuffer->unmap(); } void GLInstancedRendering::onDraw(const GrPipeline& pipeline, const InstanceProcessor& instProc, const Op* baseOp) { if (!fDrawIndirectBuffer && !fGLDrawCmdsInfo) { return; // beginFlush was not successful. } if (!this->glGpu()->flushGLState(pipeline, instProc, false)) { return; } if (fDrawIndirectBuffer) { this->glGpu()->bindBuffer(kDrawIndirect_GrBufferType, fDrawIndirectBuffer.get()); } const GrGLCaps& glCaps = this->glGpu()->glCaps(); const GLOp* op = static_cast(baseOp); int numCommands = op->numGLCommands(); #if GR_GL_LOG_INSTANCED_OPS SkASSERT(fGLDrawCmdsInfo); SkDebugf("Instanced op: ["); for (int i = 0; i < numCommands; ++i) { int glCmdIdx = op->fGLDrawCmdsIdx + i; SkDebugf("%s%i * %s", (i ? ", " : ""), fGLDrawCmdsInfo[glCmdIdx].fInstanceCount, InstanceProcessor::GetNameOfIndexRange(fGLDrawCmdsInfo[glCmdIdx].fGeometry)); } SkDebugf("]\n"); #else SkASSERT(SkToBool(fGLDrawCmdsInfo) == !glCaps.baseInstanceSupport()); #endif if (numCommands > 1 && glCaps.multiDrawIndirectSupport() && glCaps.baseInstanceSupport()) { SkASSERT(fDrawIndirectBuffer); int glCmdsIdx = op->fGLDrawCmdsIdx; this->flushInstanceAttribs(op->fEmulatedBaseInstance); GL_CALL(MultiDrawElementsIndirect(GR_GL_TRIANGLES, GR_GL_UNSIGNED_BYTE, (GrGLDrawElementsIndirectCommand*) nullptr + glCmdsIdx, numCommands, 0)); return; } int emulatedBaseInstance = op->fEmulatedBaseInstance; for (int i = 0; i < numCommands; ++i) { int glCmdIdx = op->fGLDrawCmdsIdx + i; this->flushInstanceAttribs(emulatedBaseInstance); if (fDrawIndirectBuffer) { GL_CALL(DrawElementsIndirect(GR_GL_TRIANGLES, GR_GL_UNSIGNED_BYTE, (GrGLDrawElementsIndirectCommand*) nullptr + glCmdIdx)); } else { const GLDrawCmdInfo& cmdInfo = fGLDrawCmdsInfo[glCmdIdx]; GL_CALL(DrawElementsInstanced(GR_GL_TRIANGLES, cmdInfo.fGeometry.fCount, GR_GL_UNSIGNED_BYTE, (GrGLubyte*) nullptr + cmdInfo.fGeometry.fStart, cmdInfo.fInstanceCount)); } if (!glCaps.baseInstanceSupport()) { const GLDrawCmdInfo& cmdInfo = fGLDrawCmdsInfo[glCmdIdx]; emulatedBaseInstance += cmdInfo.fInstanceCount; } } } void GLInstancedRendering::flushInstanceAttribs(int baseInstance) { SkASSERT(fVertexArrayID); this->glGpu()->bindVertexArray(fVertexArrayID); SkASSERT(fInstanceBuffer); if (fInstanceAttribsBufferUniqueId != fInstanceBuffer->uniqueID() || fInstanceAttribsBaseInstance != baseInstance) { Instance* offsetInBuffer = (Instance*) nullptr + baseInstance; this->glGpu()->bindBuffer(kVertex_GrBufferType, fInstanceBuffer.get()); // Info attrib. GL_CALL(EnableVertexAttribArray((int)Attrib::kInstanceInfo)); GL_CALL(VertexAttribIPointer((int)Attrib::kInstanceInfo, 1, GR_GL_UNSIGNED_INT, sizeof(Instance), &offsetInBuffer->fInfo)); GL_CALL(VertexAttribDivisor((int)Attrib::kInstanceInfo, 1)); // Shape matrix attrib. GL_CALL(EnableVertexAttribArray((int)Attrib::kShapeMatrixX)); GL_CALL(EnableVertexAttribArray((int)Attrib::kShapeMatrixY)); GL_CALL(VertexAttribPointer((int)Attrib::kShapeMatrixX, 3, GR_GL_FLOAT, GR_GL_FALSE, sizeof(Instance), &offsetInBuffer->fShapeMatrix2x3[0])); GL_CALL(VertexAttribPointer((int)Attrib::kShapeMatrixY, 3, GR_GL_FLOAT, GR_GL_FALSE, sizeof(Instance), &offsetInBuffer->fShapeMatrix2x3[3])); GL_CALL(VertexAttribDivisor((int)Attrib::kShapeMatrixX, 1)); GL_CALL(VertexAttribDivisor((int)Attrib::kShapeMatrixY, 1)); // Color attrib. GL_CALL(EnableVertexAttribArray((int)Attrib::kColor)); GL_CALL(VertexAttribPointer((int)Attrib::kColor, 4, GR_GL_UNSIGNED_BYTE, GR_GL_TRUE, sizeof(Instance), &offsetInBuffer->fColor)); GL_CALL(VertexAttribDivisor((int)Attrib::kColor, 1)); // Local rect attrib. GL_CALL(EnableVertexAttribArray((int)Attrib::kLocalRect)); GL_CALL(VertexAttribPointer((int)Attrib::kLocalRect, 4, GR_GL_FLOAT, GR_GL_FALSE, sizeof(Instance), &offsetInBuffer->fLocalRect)); GL_CALL(VertexAttribDivisor((int)Attrib::kLocalRect, 1)); fInstanceAttribsBufferUniqueId = fInstanceBuffer->uniqueID(); fInstanceAttribsBaseInstance = baseInstance; } } void GLInstancedRendering::onEndFlush() { fInstanceBuffer.reset(); fDrawIndirectBuffer.reset(); fGLDrawCmdsInfo.reset(0); } void GLInstancedRendering::onResetGpuResources(ResetType resetType) { if (fVertexArrayID && ResetType::kDestroy == resetType) { GL_CALL(DeleteVertexArrays(1, &fVertexArrayID)); this->glGpu()->notifyVertexArrayDelete(fVertexArrayID); } fVertexArrayID = 0; fInstanceBuffer.reset(); fDrawIndirectBuffer.reset(); fInstanceAttribsBufferUniqueId.makeInvalid(); } }