feat(BgfxRenderer): Add layer sorting for correct sprite Z-order

Sprites are now sorted by layer (ascending) then by textureId for batching.
Batches are flushed when layer OR texture changes to maintain correct draw order.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
StillHammer 2025-11-28 07:13:34 +08:00
parent 613283d75c
commit 5795bbb37e

View File

@ -101,32 +101,40 @@ void SpritePass::execute(const FramePacket& frame, rhi::IRHIDevice& device, rhi:
state.depthWrite = false;
cmd.setState(state);
// Build sorted indices by textureId for batching
// Build sorted indices by layer (primary) and textureId (secondary) for batching
m_sortedIndices.clear();
m_sortedIndices.reserve(frame.spriteCount);
for (size_t i = 0; i < frame.spriteCount; ++i) {
m_sortedIndices.push_back(static_cast<uint32_t>(i));
}
// Sort by textureId (stable sort to preserve layer order within same texture)
std::stable_sort(m_sortedIndices.begin(), m_sortedIndices.end(),
// Sort by layer first (ascending: layer 0 = background, rendered first)
// Then by textureId to batch sprites on the same layer
std::sort(m_sortedIndices.begin(), m_sortedIndices.end(),
[&frame](uint32_t a, uint32_t b) {
return frame.sprites[a].textureId < frame.sprites[b].textureId;
const SpriteInstance& sa = frame.sprites[a];
const SpriteInstance& sb = frame.sprites[b];
if (sa.layer != sb.layer) {
return sa.layer < sb.layer;
}
return sa.textureId < sb.textureId;
});
// Process sprites in batches by texture
// Process sprites in batches by layer and texture
// Flush batch when layer OR texture changes to maintain correct draw order
std::vector<SpriteInstance> batchData;
batchData.reserve(MAX_SPRITES_PER_BATCH);
uint16_t currentTextureId = UINT16_MAX;
float currentLayer = -1e9f; // Use a value that won't match any real layer
rhi::TextureHandle currentTexture;
for (uint32_t idx : m_sortedIndices) {
const SpriteInstance& sprite = frame.sprites[idx];
uint16_t texId = static_cast<uint16_t>(sprite.textureId);
// Check if texture changed
if (texId != currentTextureId) {
// Check if texture OR layer changed (must flush to maintain layer order)
if (texId != currentTextureId || sprite.layer != currentLayer) {
// Flush previous batch
if (!batchData.empty()) {
device.updateBuffer(m_instanceBuffer, batchData.data(),
@ -135,8 +143,9 @@ void SpritePass::execute(const FramePacket& frame, rhi::IRHIDevice& device, rhi:
batchData.clear();
}
// Update current texture
// Update current state
currentTextureId = texId;
currentLayer = sprite.layer;
if (texId == 0 || !m_resourceCache) {
// Use default/active texture for textureId=0
currentTexture = m_activeTexture.isValid() ? m_activeTexture : m_defaultTexture;