From 67e6dc17e7ff84cef977d343477f942eeb48357b Mon Sep 17 00:00:00 2001 From: Jerboa-app Date: Wed, 31 Jul 2024 09:55:31 +0100 Subject: [PATCH] SpriteRenderer batches over any number of textures --- examples/Sprite/main.cpp | 17 +++++ include/jGL/sprite.h | 4 +- include/jGL/spriteRenderer.h | 7 +- src/jGL/OpenGL/glSpriteRenderer.cpp | 102 +++++++++++++++++----------- src/jGL/spriteRenderer.cpp | 19 +++--- 5 files changed, 93 insertions(+), 56 deletions(-) diff --git a/examples/Sprite/main.cpp b/examples/Sprite/main.cpp index 1d767ae4..7a400592 100644 --- a/examples/Sprite/main.cpp +++ b/examples/Sprite/main.cpp @@ -52,6 +52,12 @@ int main(int argv, char ** argc) jGL::Texture::Type::RGBA ); + std::shared_ptr random = jGLInstance->createTexture + ( + "resource/texture/random.png", + jGL::Texture::Type::RGBA + ); + std::shared_ptr sprites = jGLInstance->createSpriteRenderer ( 2 @@ -72,6 +78,7 @@ int main(int argv, char ** argc) {"sJerboa", jGL::Transform(0.1f, 0.9f, 0.0f, 0.1f)}, {"sPi", jGL::Transform(0.1f, 0.1f, 0.0f, 0.1f)}, {"sHeart", jGL::Transform(0.5f, 0.5f, 0.0f, 0.1f)}, + {"sRandom", jGL::Transform(0.5f, 0.25f, 0.0f, 0.1f)}, {"lowest", jGL::Transform(0.5f, 0.1f, 0.0f, 0.1f)}, {"middle", jGL::Transform(0.55f, 0.15f, 0.0f, 0.1f)}, {"highest", jGL::Transform(0.6f, 0.2f, 0.0f, 0.1f)} @@ -159,6 +166,16 @@ int main(int argv, char ** argc) "sHeart" ); + sprites->add + ( + { + trans["sRandom"], + jGL::TextureOffset(), + random + }, + "sRandom" + ); + sprites->add ( { diff --git a/include/jGL/sprite.h b/include/jGL/sprite.h index e7fb2112..c50dc519 100644 --- a/include/jGL/sprite.h +++ b/include/jGL/sprite.h @@ -27,7 +27,7 @@ namespace jGL ( const Transform & tra, TextureOffset to, - std::shared_ptr tex, + const std::shared_ptr tex, float alpha = 1.0f ) : transform(tra), texture(tex), alpha(std::clamp(alpha, 0.0f, 1.0f)), texOffset(toNormalised(to)) @@ -70,7 +70,7 @@ namespace jGL void setTextureOffset(TextureOffset to) { texOffset = toNormalised(texOffset); } const Transform & transform; - std::shared_ptr texture; + const std::shared_ptr texture; protected: diff --git a/include/jGL/spriteRenderer.h b/include/jGL/spriteRenderer.h index ba7e073a..4e4e332e 100644 --- a/include/jGL/spriteRenderer.h +++ b/include/jGL/spriteRenderer.h @@ -30,7 +30,7 @@ namespace jGL SpriteRenderer(size_t sizeHint = 8) { sprites.reserve(sizeHint); - textureSlots.reserve(MAX_TEXTURE_SLOTS); + textures.reserve(MAX_TEXTURE_SLOTS); } Sprite & getSprite(SpriteId id); @@ -87,11 +87,10 @@ namespace jGL protected: static const uint8_t MAX_TEXTURE_SLOTS = 4; - std::vector> textureSlots; - - uint8_t usedTextureSlots = 0; + std::vector> textures; std::unordered_map sprites; + std::unordered_map spriteToTexture; std::multimap ids; diff --git a/src/jGL/OpenGL/glSpriteRenderer.cpp b/src/jGL/OpenGL/glSpriteRenderer.cpp index 2b79b8ef..489073a3 100644 --- a/src/jGL/OpenGL/glSpriteRenderer.cpp +++ b/src/jGL/OpenGL/glSpriteRenderer.cpp @@ -16,23 +16,31 @@ namespace jGL::GL initGL(); } + std::vector textureIndices(ids.size(), 0); + std::vector>> batches; + batches.reserve(n); + uint64_t i = 0; + std::vector batch; + for (auto & sid : ids) { + size_t textureIndex = spriteToTexture[sid.second]; + bool newTexture = std::find(batch.begin(), batch.end(), textureIndex) == batch.end(); + if (newTexture) + { + if (batch.size() == MAX_TEXTURE_SLOTS) + { + batches.push_back(std::pair(i, batch)); + batch.clear(); + } + batch.push_back(textureIndex); + } + const Sprite & sprite = sprites.at(sid.second); const Transform & trans = sprite.transform; const TextureOffset toff = sprite.getTextureOffset(true); - const Id & texId = sprite.texture->getId(); const float alpha = sprite.getAlpha(); - - auto is_equal = [texId] (std::shared_ptr tex) { return tex->getId() == texId; }; - - auto found = std::find_if(textureSlots.begin(), textureSlots.end(), is_equal); - size_t index = std::distance(textureSlots.begin(), found); - if (index == textureSlots.size()) - { - throw std::runtime_error("sprite texture not found in sprite renderer"); - } offsets[i*offsetDim] = trans.x; offsets[i*offsetDim+1] = trans.y; @@ -42,38 +50,47 @@ namespace jGL::GL textureRegion[i*textureRegionDim+1] = toff.ty; textureRegion[i*textureRegionDim+2] = toff.lx; textureRegion[i*textureRegionDim+3] = toff.ly; - textureOptions[i*textureOptionsDim] = float(index); + textureOptions[i*textureOptionsDim] = float(std::distance(batch.begin(), std::find(batch.begin(), batch.end(), textureIndex))); textureOptions[i*textureOptionsDim+1] = alpha; - i += 1; + i++; } + batches.push_back(std::pair(i, batch)); + + uint64_t b = 0; + uint64_t current = 0; + uint64_t next = batches[b].first-1; + std::vector::const_iterator biter; - for (unsigned i = 0; i < MAX_TEXTURE_SLOTS; i++) + while (b < batches.size()) { - if (i >= usedTextureSlots) + biter = batches[b].second.cbegin(); + for (unsigned slot = 0; slot < MAX_TEXTURE_SLOTS; slot++) { - break; + if (biter == batches[b].second.cend()) { break; } + textures[*biter]->bind(slot); + biter++; } - textureSlots[i]->bind(i); - } - shader->use(); + shader->use(); - shader->setUniform("proj", projection); - shader->setUniform("sampler0", Sampler2D(0)); - shader->setUniform("sampler1", Sampler2D(1)); - shader->setUniform("sampler2", Sampler2D(2)); - shader->setUniform("sampler3", Sampler2D(3)); + shader->setUniform("proj", projection); + shader->setUniform("sampler0", Sampler2D(0)); + shader->setUniform("sampler1", Sampler2D(1)); + shader->setUniform("sampler2", Sampler2D(2)); + shader->setUniform("sampler3", Sampler2D(3)); - glBindVertexArray(vao); + size_t batchSize = next-current+1; + + glBindVertexArray(vao); glBindBuffer(GL_ARRAY_BUFFER, a_offset); glBufferSubData ( GL_ARRAY_BUFFER, 0, - offsetDim*n*sizeof(float), - &offsets[0] + offsetDim*batchSize*sizeof(float), + &offsets[current*offsetDim] ); glBindBuffer(GL_ARRAY_BUFFER, 0); @@ -83,8 +100,8 @@ namespace jGL::GL ( GL_ARRAY_BUFFER, 0, - textureRegionDim*n*sizeof(float), - &textureRegion[0] + textureRegionDim*batchSize*sizeof(float), + &textureRegion[current*textureRegionDim] ); glBindBuffer(GL_ARRAY_BUFFER, 0); @@ -95,25 +112,32 @@ namespace jGL::GL ( GL_ARRAY_BUFFER, 0, - textureOptionsDim*n*sizeof(float), - &textureOptions[0] + textureOptionsDim*batchSize*sizeof(float), + &textureOptions[current*textureOptionsDim] ); glBindBuffer(GL_ARRAY_BUFFER, 0); - glBindVertexArray(0); + glBindVertexArray(0); - glBindVertexArray(vao); + glBindVertexArray(vao); - glEnable(GL_BLEND); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - glDisable(GL_DEPTH_TEST); - - glDrawArraysInstanced(GL_TRIANGLES, 0, 6, n); + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glDisable(GL_DEPTH_TEST); - glBindVertexArray(0); + glDrawArraysInstanced(GL_TRIANGLES, 0, 6, batchSize); + + glBindVertexArray(0); + + glError("sprite draw"); - glError("sprite draw"); + if (b+1 == batches.size()){ break; } + + current = batches[b].first; + b++; + next = batches[b].first-1; + } } diff --git a/src/jGL/spriteRenderer.cpp b/src/jGL/spriteRenderer.cpp index cc00f4b2..39139d62 100644 --- a/src/jGL/spriteRenderer.cpp +++ b/src/jGL/spriteRenderer.cpp @@ -11,19 +11,16 @@ namespace jGL { Id textureId = s.texture->getId(); auto is_equal = [textureId](std::shared_ptr t) { return t->getId() == textureId; }; - auto match = std::find_if(textureSlots.begin(), textureSlots.end(), is_equal); + auto match = std::find_if(textures.begin(), textures.end(), is_equal); - if (match == textureSlots.end()) + if (match == textures.end()) { - if (usedTextureSlots < MAX_TEXTURE_SLOTS) - { - textureSlots.push_back(s.texture); - usedTextureSlots += 1; - } - else - { - throw std::runtime_error("Sprite renderer hit maximum texture slots"); - } + textures.push_back(s.texture); + spriteToTexture[id] = textures.size()-1; + } + else + { + spriteToTexture[id] = std::distance(textures.begin(), match); } sprites.insert(std::pair(id, s));