Skip to content

Commit

Permalink
SpriteRenderer batches over any number of textures
Browse files Browse the repository at this point in the history
  • Loading branch information
Jerboa-app committed Jul 31, 2024
1 parent 368d9de commit 67e6dc1
Show file tree
Hide file tree
Showing 5 changed files with 93 additions and 56 deletions.
17 changes: 17 additions & 0 deletions examples/Sprite/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,12 @@ int main(int argv, char ** argc)
jGL::Texture::Type::RGBA
);

std::shared_ptr<jGL::Texture> random = jGLInstance->createTexture
(
"resource/texture/random.png",
jGL::Texture::Type::RGBA
);

std::shared_ptr<jGL::SpriteRenderer> sprites = jGLInstance->createSpriteRenderer
(
2
Expand All @@ -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)}
Expand Down Expand Up @@ -159,6 +166,16 @@ int main(int argv, char ** argc)
"sHeart"
);

sprites->add
(
{
trans["sRandom"],
jGL::TextureOffset(),
random
},
"sRandom"
);

sprites->add
(
{
Expand Down
4 changes: 2 additions & 2 deletions include/jGL/sprite.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ namespace jGL
(
const Transform & tra,
TextureOffset to,
std::shared_ptr<Texture> tex,
const std::shared_ptr<Texture> tex,
float alpha = 1.0f
)
: transform(tra), texture(tex), alpha(std::clamp(alpha, 0.0f, 1.0f)), texOffset(toNormalised(to))
Expand Down Expand Up @@ -70,7 +70,7 @@ namespace jGL
void setTextureOffset(TextureOffset to) { texOffset = toNormalised(texOffset); }

const Transform & transform;
std::shared_ptr<Texture> texture;
const std::shared_ptr<Texture> texture;

protected:

Expand Down
7 changes: 3 additions & 4 deletions include/jGL/spriteRenderer.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -87,11 +87,10 @@ namespace jGL
protected:

static const uint8_t MAX_TEXTURE_SLOTS = 4;
std::vector<std::shared_ptr<Texture>> textureSlots;

uint8_t usedTextureSlots = 0;
std::vector<std::shared_ptr<Texture>> textures;

std::unordered_map<SpriteId, Sprite> sprites;
std::unordered_map<SpriteId, size_t> spriteToTexture;

std::multimap<RenderPriority, SpriteId> ids;

Expand Down
102 changes: 63 additions & 39 deletions src/jGL/OpenGL/glSpriteRenderer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,23 +16,31 @@ namespace jGL::GL
initGL();
}

std::vector<size_t> textureIndices(ids.size(), 0);
std::vector<std::pair<size_t, std::vector<size_t>>> batches;
batches.reserve(n);

uint64_t i = 0;
std::vector<size_t> 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<Texture> 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;
Expand All @@ -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<size_t>::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<glm::mat4>("proj", projection);
shader->setUniform<Sampler2D>("sampler0", Sampler2D(0));
shader->setUniform<Sampler2D>("sampler1", Sampler2D(1));
shader->setUniform<Sampler2D>("sampler2", Sampler2D(2));
shader->setUniform<Sampler2D>("sampler3", Sampler2D(3));
shader->setUniform<glm::mat4>("proj", projection);
shader->setUniform<Sampler2D>("sampler0", Sampler2D(0));
shader->setUniform<Sampler2D>("sampler1", Sampler2D(1));
shader->setUniform<Sampler2D>("sampler2", Sampler2D(2));
shader->setUniform<Sampler2D>("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);

Expand All @@ -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);
Expand All @@ -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;
}

}

Expand Down
19 changes: 8 additions & 11 deletions src/jGL/spriteRenderer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,19 +11,16 @@ namespace jGL
{
Id textureId = s.texture->getId();
auto is_equal = [textureId](std::shared_ptr<Texture> 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));
Expand Down

0 comments on commit 67e6dc1

Please sign in to comment.