1 /* 2 * Copyright 2016 Google Inc. 3 * 4 * Use of this source code is governed by a BSD-style license that can be 5 * found in the LICENSE file. 6 */ 7 8 #include "GLInstancedRendering.h" 9 10 #include "GrResourceProvider.h" 11 #include "gl/GrGLGpu.h" 12 #include "instanced/InstanceProcessor.h" 13 14 #define GL_CALL(X) GR_GL_CALL(this->glGpu()->glInterface(), X) 15 16 namespace gr_instanced { 17 18 class GLInstancedOp final : public InstancedOp { 19 public: 20 DEFINE_OP_CLASS_ID 21 22 GLInstancedOp(GLOpAllocator* alloc, GrPaint&& paint) 23 : INHERITED(ClassID(), std::move(paint), alloc) { 24 } 25 int numGLCommands() const { return 1 + fNumChangesInGeometry; } 26 27 private: 28 int fEmulatedBaseInstance; 29 int fGLDrawCmdsIdx; 30 31 friend class GLInstancedRendering; 32 33 typedef InstancedOp INHERITED; 34 }; 35 36 GrCaps::InstancedSupport GLInstancedRendering::CheckSupport(const GrGLCaps& glCaps) { 37 // This method is only intended to be used for initializing fInstancedSupport in the caps. 38 SkASSERT(GrCaps::InstancedSupport::kNone == glCaps.instancedSupport()); 39 if (!glCaps.vertexArrayObjectSupport() || 40 (!glCaps.drawIndirectSupport() && !glCaps.drawInstancedSupport())) { 41 return GrCaps::InstancedSupport::kNone; 42 } 43 return InstanceProcessor::CheckSupport(*glCaps.shaderCaps(), glCaps); 44 } 45 46 GLInstancedRendering::GLInstancedRendering(GrGLGpu* gpu) 47 : INHERITED(gpu) 48 , fVertexArrayID(0) 49 , fGLDrawCmdsInfo(0) 50 , fInstanceAttribsBufferUniqueId(SK_InvalidUniqueID) { 51 SkASSERT(GrCaps::InstancedSupport::kNone != this->gpu()->caps()->instancedSupport()); 52 } 53 54 GLInstancedRendering::~GLInstancedRendering() { 55 if (fVertexArrayID) { 56 GL_CALL(DeleteVertexArrays(1, &fVertexArrayID)); 57 this->glGpu()->notifyVertexArrayDelete(fVertexArrayID); 58 } 59 } 60 61 inline GrGLGpu* GLInstancedRendering::glGpu() const { 62 return static_cast<GrGLGpu*>(this->gpu()); 63 } 64 65 std::unique_ptr<InstancedOp> GLOpAllocator::makeOp(GrPaint&& paint) { 66 return std::unique_ptr<InstancedOp>(new GLInstancedOp(this, std::move(paint))); 67 } 68 69 void GLInstancedRendering::onBeginFlush(GrResourceProvider* rp) { 70 // Count what there is to draw. 71 OpList::Iter iter; 72 iter.init(this->trackedOps(), OpList::Iter::kHead_IterStart); 73 int numGLInstances = 0; 74 int numGLDrawCmds = 0; 75 while (InstancedOp* o = iter.get()) { 76 GLInstancedOp* op = (GLInstancedOp*) o; 77 iter.next(); 78 79 numGLInstances += op->fNumDraws; 80 numGLDrawCmds += op->numGLCommands(); 81 } 82 if (!numGLDrawCmds) { 83 return; 84 } 85 SkASSERT(numGLInstances); 86 87 // Lazily create a vertex array object. 88 if (!fVertexArrayID) { 89 GL_CALL(GenVertexArrays(1, &fVertexArrayID)); 90 if (!fVertexArrayID) { 91 return; 92 } 93 this->glGpu()->bindVertexArray(fVertexArrayID); 94 95 // Attach our index buffer to the vertex array. 96 SkASSERT(!this->indexBuffer()->isCPUBacked()); 97 GL_CALL(BindBuffer(GR_GL_ELEMENT_ARRAY_BUFFER, 98 static_cast<const GrGLBuffer*>(this->indexBuffer())->bufferID())); 99 100 // Set up the non-instanced attribs. 101 this->glGpu()->bindBuffer(kVertex_GrBufferType, this->vertexBuffer()); 102 GL_CALL(EnableVertexAttribArray((int)Attrib::kShapeCoords)); 103 GL_CALL(VertexAttribPointer((int)Attrib::kShapeCoords, 2, GR_GL_FLOAT, GR_GL_FALSE, 104 sizeof(ShapeVertex), (void*) offsetof(ShapeVertex, fX))); 105 GL_CALL(EnableVertexAttribArray((int)Attrib::kVertexAttrs)); 106 GL_CALL(VertexAttribIPointer((int)Attrib::kVertexAttrs, 1, GR_GL_INT, sizeof(ShapeVertex), 107 (void*) offsetof(ShapeVertex, fAttrs))); 108 109 SkASSERT(fInstanceAttribsBufferUniqueId.isInvalid()); 110 } 111 112 // Create and map instance and draw-indirect buffers. 113 SkASSERT(!fInstanceBuffer); 114 fInstanceBuffer.reset( 115 rp->createBuffer(sizeof(Instance) * numGLInstances, kVertex_GrBufferType, 116 kDynamic_GrAccessPattern, 117 GrResourceProvider::kNoPendingIO_Flag | 118 GrResourceProvider::kRequireGpuMemory_Flag)); 119 if (!fInstanceBuffer) { 120 return; 121 } 122 123 SkASSERT(!fDrawIndirectBuffer); 124 if (this->glGpu()->glCaps().drawIndirectSupport()) { 125 fDrawIndirectBuffer.reset( 126 rp->createBuffer(sizeof(GrGLDrawElementsIndirectCommand) * numGLDrawCmds, 127 kDrawIndirect_GrBufferType, kDynamic_GrAccessPattern, 128 GrResourceProvider::kNoPendingIO_Flag | 129 GrResourceProvider::kRequireGpuMemory_Flag)); 130 if (!fDrawIndirectBuffer) { 131 return; 132 } 133 } 134 135 Instance* glMappedInstances = static_cast<Instance*>(fInstanceBuffer->map()); 136 SkASSERT(glMappedInstances); 137 int glInstancesIdx = 0; 138 139 GrGLDrawElementsIndirectCommand* glMappedCmds = nullptr; 140 int glDrawCmdsIdx = 0; 141 if (fDrawIndirectBuffer) { 142 glMappedCmds = static_cast<GrGLDrawElementsIndirectCommand*>(fDrawIndirectBuffer->map()); 143 SkASSERT(glMappedCmds); 144 } 145 146 bool baseInstanceSupport = this->glGpu()->glCaps().baseInstanceSupport(); 147 SkASSERT(!baseInstanceSupport || fDrawIndirectBuffer); 148 149 SkASSERT(!fGLDrawCmdsInfo); 150 if (GR_GL_LOG_INSTANCED_OPS || !baseInstanceSupport) { 151 fGLDrawCmdsInfo.reset(numGLDrawCmds); 152 } 153 154 // Generate the instance and draw-indirect buffer contents based on the tracked ops. 155 iter.init(this->trackedOps(), OpList::Iter::kHead_IterStart); 156 while (InstancedOp* o = iter.get()) { 157 GLInstancedOp* op = static_cast<GLInstancedOp*>(o); 158 iter.next(); 159 160 op->fEmulatedBaseInstance = baseInstanceSupport ? 0 : glInstancesIdx; 161 op->fGLDrawCmdsIdx = glDrawCmdsIdx; 162 163 const InstancedOp::Draw* draw = op->fHeadDraw; 164 SkASSERT(draw); 165 do { 166 int instanceCount = 0; 167 IndexRange geometry = draw->fGeometry; 168 SkASSERT(!geometry.isEmpty()); 169 170 do { 171 glMappedInstances[glInstancesIdx + instanceCount++] = draw->fInstance; 172 draw = draw->fNext; 173 } while (draw && draw->fGeometry == geometry); 174 175 if (fDrawIndirectBuffer) { 176 GrGLDrawElementsIndirectCommand& glCmd = glMappedCmds[glDrawCmdsIdx]; 177 glCmd.fCount = geometry.fCount; 178 glCmd.fInstanceCount = instanceCount; 179 glCmd.fFirstIndex = geometry.fStart; 180 glCmd.fBaseVertex = 0; 181 glCmd.fBaseInstance = baseInstanceSupport ? glInstancesIdx : 0; 182 } 183 184 if (GR_GL_LOG_INSTANCED_OPS || !baseInstanceSupport) { 185 GLDrawCmdInfo& cmdInfo = fGLDrawCmdsInfo[glDrawCmdsIdx]; 186 cmdInfo.fGeometry = geometry; 187 cmdInfo.fInstanceCount = instanceCount; 188 } 189 190 glInstancesIdx += instanceCount; 191 ++glDrawCmdsIdx; 192 } while (draw); 193 } 194 195 SkASSERT(glDrawCmdsIdx == numGLDrawCmds); 196 if (fDrawIndirectBuffer) { 197 fDrawIndirectBuffer->unmap(); 198 } 199 200 SkASSERT(glInstancesIdx == numGLInstances); 201 fInstanceBuffer->unmap(); 202 } 203 204 void GLInstancedRendering::onDraw(const GrPipeline& pipeline, const InstanceProcessor& instProc, 205 const InstancedOp* baseOp) { 206 if (!fDrawIndirectBuffer && !fGLDrawCmdsInfo) { 207 return; // beginFlush was not successful. 208 } 209 if (!this->glGpu()->flushGLState(pipeline, instProc, false)) { 210 return; 211 } 212 213 if (fDrawIndirectBuffer) { 214 this->glGpu()->bindBuffer(kDrawIndirect_GrBufferType, fDrawIndirectBuffer.get()); 215 } 216 217 const GrGLCaps& glCaps = this->glGpu()->glCaps(); 218 const GLInstancedOp* op = static_cast<const GLInstancedOp*>(baseOp); 219 int numCommands = op->numGLCommands(); 220 221 #if GR_GL_LOG_INSTANCED_OPS 222 SkASSERT(fGLDrawCmdsInfo); 223 SkDebugf("Instanced op: ["); 224 for (int i = 0; i < numCommands; ++i) { 225 int glCmdIdx = op->fGLDrawCmdsIdx + i; 226 SkDebugf("%s%i * %s", (i ? ", " : ""), fGLDrawCmdsInfo[glCmdIdx].fInstanceCount, 227 InstanceProcessor::GetNameOfIndexRange(fGLDrawCmdsInfo[glCmdIdx].fGeometry)); 228 } 229 SkDebugf("]\n"); 230 #else 231 SkASSERT(SkToBool(fGLDrawCmdsInfo) == !glCaps.baseInstanceSupport()); 232 #endif 233 234 if (numCommands > 1 && glCaps.multiDrawIndirectSupport() && glCaps.baseInstanceSupport()) { 235 SkASSERT(fDrawIndirectBuffer); 236 int glCmdsIdx = op->fGLDrawCmdsIdx; 237 this->flushInstanceAttribs(op->fEmulatedBaseInstance); 238 GL_CALL(MultiDrawElementsIndirect(GR_GL_TRIANGLES, GR_GL_UNSIGNED_BYTE, 239 (GrGLDrawElementsIndirectCommand*) nullptr + glCmdsIdx, 240 numCommands, 0)); 241 return; 242 } 243 244 int emulatedBaseInstance = op->fEmulatedBaseInstance; 245 for (int i = 0; i < numCommands; ++i) { 246 int glCmdIdx = op->fGLDrawCmdsIdx + i; 247 this->flushInstanceAttribs(emulatedBaseInstance); 248 if (fDrawIndirectBuffer) { 249 GL_CALL(DrawElementsIndirect(GR_GL_TRIANGLES, GR_GL_UNSIGNED_BYTE, 250 (GrGLDrawElementsIndirectCommand*) nullptr + glCmdIdx)); 251 } else { 252 const GLDrawCmdInfo& cmdInfo = fGLDrawCmdsInfo[glCmdIdx]; 253 GL_CALL(DrawElementsInstanced(GR_GL_TRIANGLES, cmdInfo.fGeometry.fCount, 254 GR_GL_UNSIGNED_BYTE, 255 (GrGLubyte*) nullptr + cmdInfo.fGeometry.fStart, 256 cmdInfo.fInstanceCount)); 257 } 258 if (!glCaps.baseInstanceSupport()) { 259 const GLDrawCmdInfo& cmdInfo = fGLDrawCmdsInfo[glCmdIdx]; 260 emulatedBaseInstance += cmdInfo.fInstanceCount; 261 } 262 } 263 } 264 265 void GLInstancedRendering::flushInstanceAttribs(int baseInstance) { 266 SkASSERT(fVertexArrayID); 267 this->glGpu()->bindVertexArray(fVertexArrayID); 268 269 SkASSERT(fInstanceBuffer); 270 if (fInstanceAttribsBufferUniqueId != fInstanceBuffer->uniqueID() || 271 fInstanceAttribsBaseInstance != baseInstance) { 272 Instance* offsetInBuffer = (Instance*) nullptr + baseInstance; 273 274 this->glGpu()->bindBuffer(kVertex_GrBufferType, fInstanceBuffer.get()); 275 276 // Info attrib. 277 GL_CALL(EnableVertexAttribArray((int)Attrib::kInstanceInfo)); 278 GL_CALL(VertexAttribIPointer((int)Attrib::kInstanceInfo, 1, GR_GL_UNSIGNED_INT, 279 sizeof(Instance), &offsetInBuffer->fInfo)); 280 GL_CALL(VertexAttribDivisor((int)Attrib::kInstanceInfo, 1)); 281 282 // Shape matrix attrib. 283 GL_CALL(EnableVertexAttribArray((int)Attrib::kShapeMatrixX)); 284 GL_CALL(EnableVertexAttribArray((int)Attrib::kShapeMatrixY)); 285 GL_CALL(VertexAttribPointer((int)Attrib::kShapeMatrixX, 3, GR_GL_FLOAT, GR_GL_FALSE, 286 sizeof(Instance), &offsetInBuffer->fShapeMatrix2x3[0])); 287 GL_CALL(VertexAttribPointer((int)Attrib::kShapeMatrixY, 3, GR_GL_FLOAT, GR_GL_FALSE, 288 sizeof(Instance), &offsetInBuffer->fShapeMatrix2x3[3])); 289 GL_CALL(VertexAttribDivisor((int)Attrib::kShapeMatrixX, 1)); 290 GL_CALL(VertexAttribDivisor((int)Attrib::kShapeMatrixY, 1)); 291 292 // Color attrib. 293 GL_CALL(EnableVertexAttribArray((int)Attrib::kColor)); 294 GL_CALL(VertexAttribPointer((int)Attrib::kColor, 4, GR_GL_UNSIGNED_BYTE, GR_GL_TRUE, 295 sizeof(Instance), &offsetInBuffer->fColor)); 296 GL_CALL(VertexAttribDivisor((int)Attrib::kColor, 1)); 297 298 // Local rect attrib. 299 GL_CALL(EnableVertexAttribArray((int)Attrib::kLocalRect)); 300 GL_CALL(VertexAttribPointer((int)Attrib::kLocalRect, 4, GR_GL_FLOAT, GR_GL_FALSE, 301 sizeof(Instance), &offsetInBuffer->fLocalRect)); 302 GL_CALL(VertexAttribDivisor((int)Attrib::kLocalRect, 1)); 303 304 fInstanceAttribsBufferUniqueId = fInstanceBuffer->uniqueID(); 305 fInstanceAttribsBaseInstance = baseInstance; 306 } 307 } 308 309 void GLInstancedRendering::onEndFlush() { 310 fInstanceBuffer.reset(); 311 fDrawIndirectBuffer.reset(); 312 fGLDrawCmdsInfo.reset(0); 313 } 314 315 void GLInstancedRendering::onResetGpuResources(ResetType resetType) { 316 if (fVertexArrayID && ResetType::kDestroy == resetType) { 317 GL_CALL(DeleteVertexArrays(1, &fVertexArrayID)); 318 this->glGpu()->notifyVertexArrayDelete(fVertexArrayID); 319 } 320 fVertexArrayID = 0; 321 fInstanceBuffer.reset(); 322 fDrawIndirectBuffer.reset(); 323 fInstanceAttribsBufferUniqueId.makeInvalid(); 324 } 325 326 } 327