void Renderer::drawBatchedTriangles() { if(_queuedTriangleCommands.empty()) return; CCGL_DEBUG_INSERT_EVENT_MARKER("RENDERER_BATCH_TRIANGLES"); _filledVertex = 0; _filledIndex = 0; /************** 1: Setup up vertices/indices *************/ _triBatchesToDraw[0].offset = 0; _triBatchesToDraw[0].indicesToDraw = 0; _triBatchesToDraw[0].cmd = nullptr; int batchesTotal = 0; int prevMaterialID = -1; bool firstCommand = true; for(const auto& cmd : _queuedTriangleCommands) { auto currentMaterialID = cmd->getMaterialID(); const bool batchable = !cmd->isSkipBatching(); fillVerticesAndIndices(cmd); // in the same batch ? if (batchable && (prevMaterialID == currentMaterialID || firstCommand)) { CC_ASSERT(firstCommand || _triBatchesToDraw[batchesTotal].cmd->getMaterialID() == cmd->getMaterialID() && "argh... error in logic"); _triBatchesToDraw[batchesTotal].indicesToDraw += cmd->getIndexCount(); _triBatchesToDraw[batchesTotal].cmd = cmd; } else { // is this the first one? if (!firstCommand) { batchesTotal++; _triBatchesToDraw[batchesTotal].offset = _triBatchesToDraw[batchesTotal-1].offset + _triBatchesToDraw[batchesTotal-1].indicesToDraw; } _triBatchesToDraw[batchesTotal].cmd = cmd; _triBatchesToDraw[batchesTotal].indicesToDraw = (int) cmd->getIndexCount(); // is this a single batch ? Prevent creating a batch group then if (!batchable) currentMaterialID = -1; } // capacity full ? if (batchesTotal + 1 >= _triBatchesToDrawCapacity) { _triBatchesToDrawCapacity *= 1.4; _triBatchesToDraw = (TriBatchToDraw*) realloc(_triBatchesToDraw, sizeof(_triBatchesToDraw[0]) * _triBatchesToDrawCapacity); } prevMaterialID = currentMaterialID; firstCommand = false; } batchesTotal++; /************** 2: Copy vertices/indices to GL objects *************/ auto conf = Configuration::getInstance(); if (conf->supportsShareableVAO() && conf->supportsMapBuffer()) { //Bind VAO GL::bindVAO(_buffersVAO); //Set VBO data glBindBuffer(GL_ARRAY_BUFFER, _buffersVBO[0]); // option 1: subdata // glBufferSubData(GL_ARRAY_BUFFER, sizeof(_quads[0])*start, sizeof(_quads[0]) * n , &_quads[start] ); // option 2: data // glBufferData(GL_ARRAY_BUFFER, sizeof(_verts[0]) * _filledVertex, _verts, GL_STATIC_DRAW); // option 3: orphaning + glMapBuffer // FIXME: in order to work as fast as possible, it must "and the exact same size and usage hints it had before." // source: https://www.opengl.org/wiki/Buffer_Object_Streaming#Explicit_multiple_buffering // so most probably we won't have any benefit of using it glBufferData(GL_ARRAY_BUFFER, sizeof(_verts[0]) * _filledVertex, nullptr, GL_STATIC_DRAW); void *buf = glMapBuffer(GL_ARRAY_BUFFER, GL_WRITE_ONLY); memcpy(buf, _verts, sizeof(_verts[0]) * _filledVertex); glUnmapBuffer(GL_ARRAY_BUFFER); glBindBuffer(GL_ARRAY_BUFFER, 0); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _buffersVBO[1]); glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(_indices[0]) * _filledIndex, _indices, GL_STATIC_DRAW); } else { // Client Side Arrays #define kQuadSize sizeof(_verts[0]) glBindBuffer(GL_ARRAY_BUFFER, _buffersVBO[0]); glBufferData(GL_ARRAY_BUFFER, sizeof(_verts[0]) * _filledVertex , _verts, GL_DYNAMIC_DRAW); GL::enableVertexAttribs(GL::VERTEX_ATTRIB_FLAG_POS_COLOR_TEX); // vertices glVertexAttribPointer(GLProgram::VERTEX_ATTRIB_POSITION, 3, GL_FLOAT, GL_FALSE, kQuadSize, (GLvoid*) offsetof(V3F_C4B_T2F, vertices)); // colors glVertexAttribPointer(GLProgram::VERTEX_ATTRIB_COLOR, 4, GL_UNSIGNED_BYTE, GL_TRUE, kQuadSize, (GLvoid*) offsetof(V3F_C4B_T2F, colors)); // tex coords glVertexAttribPointer(GLProgram::VERTEX_ATTRIB_TEX_COORD, 2, GL_FLOAT, GL_FALSE, kQuadSize, (GLvoid*) offsetof(V3F_C4B_T2F, texCoords)); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _buffersVBO[1]); glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(_indices[0]) * _filledIndex, _indices, GL_STATIC_DRAW); } /************** 3: Draw *************/ for (int i=0; i<batchesTotal; ++i) { CC_ASSERT(_triBatchesToDraw[i].cmd && "Invalid batch"); _triBatchesToDraw[i].cmd->useMaterial(); glDrawElements(GL_TRIANGLES, (GLsizei) _triBatchesToDraw[i].indicesToDraw, GL_UNSIGNED_SHORT, (GLvoid*) (_triBatchesToDraw[i].offset*sizeof(_indices[0])) ); _drawnBatches++; _drawnVertices += _triBatchesToDraw[i].indicesToDraw; } /************** 4: Cleanup *************/ if (Configuration::getInstance()->supportsShareableVAO()) { //Unbind VAO GL::bindVAO(0); } else { glBindBuffer(GL_ARRAY_BUFFER, 0); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); } _queuedTriangleCommands.clear(); _filledVertex = 0; _filledIndex = 0; }
void TextureAtlas::drawNumberOfQuads(ssize_t numberOfQuads, ssize_t start) { CCASSERT(numberOfQuads>=0 && start>=0, "numberOfQuads and start must be >= 0"); if(!numberOfQuads) return; GL::bindTexture2D(_texture->getName()); auto conf = Configuration::getInstance(); if (conf->supportsShareableVAO() && conf->supportsMapBuffer()) { // // Using VBO and VAO // // FIXME:: update is done in draw... perhaps it should be done in a timer if (_dirty) { glBindBuffer(GL_ARRAY_BUFFER, _buffersVBO[0]); // option 1: subdata // glBufferSubData(GL_ARRAY_BUFFER, sizeof(_quads[0])*start, sizeof(_quads[0]) * n , &_quads[start] ); // option 2: data // glBufferData(GL_ARRAY_BUFFER, sizeof(quads_[0]) * (n-start), &quads_[start], GL_DYNAMIC_DRAW); // option 3: orphaning + glMapBuffer glBufferData(GL_ARRAY_BUFFER, sizeof(_quads[0]) * _capacity, nullptr, GL_DYNAMIC_DRAW); void *buf = glMapBuffer(GL_ARRAY_BUFFER, GL_WRITE_ONLY); memcpy(buf, _quads, sizeof(_quads[0])* _totalQuads); glUnmapBuffer(GL_ARRAY_BUFFER); glBindBuffer(GL_ARRAY_BUFFER, 0); _dirty = false; } GL::bindVAO(_VAOname); #if CC_REBIND_INDICES_BUFFER glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _buffersVBO[1]); #endif glDrawElements(GL_TRIANGLES, (GLsizei) numberOfQuads*6, GL_UNSIGNED_SHORT, (GLvoid*) (start*6*sizeof(_indices[0])) ); GL::bindVAO(0); #if CC_REBIND_INDICES_BUFFER glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); #endif // glBindVertexArray(0); } else { // // Using VBO without VAO // #define kQuadSize sizeof(_quads[0].bl) glBindBuffer(GL_ARRAY_BUFFER, _buffersVBO[0]); // FIXME:: update is done in draw... perhaps it should be done in a timer if (_dirty) { glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(_quads[0]) * _totalQuads , &_quads[0] ); _dirty = false; } GL::enableVertexAttribs(GL::VERTEX_ATTRIB_FLAG_POS_COLOR_TEX); // vertices glVertexAttribPointer(GLProgram::VERTEX_ATTRIB_POSITION, 3, GL_FLOAT, GL_FALSE, kQuadSize, (GLvoid*) offsetof(V3F_C4B_T2F, vertices)); // colors glVertexAttribPointer(GLProgram::VERTEX_ATTRIB_COLOR, 4, GL_UNSIGNED_BYTE, GL_TRUE, kQuadSize, (GLvoid*) offsetof(V3F_C4B_T2F, colors)); // tex coords glVertexAttribPointer(GLProgram::VERTEX_ATTRIB_TEX_COORD, 2, GL_FLOAT, GL_FALSE, kQuadSize, (GLvoid*) offsetof(V3F_C4B_T2F, texCoords)); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _buffersVBO[1]); glDrawElements(GL_TRIANGLES, (GLsizei)numberOfQuads*6, GL_UNSIGNED_SHORT, (GLvoid*) (start*6*sizeof(_indices[0]))); glBindBuffer(GL_ARRAY_BUFFER, 0); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); } CC_INCREMENT_GL_DRAWN_BATCHES_AND_VERTICES(1,numberOfQuads*6); CHECK_GL_ERROR_DEBUG(); }