Graphics/ForwardRenderTechnique: Optimize sprite rendering

This commit is contained in:
Lynix 2018-06-06 21:38:40 +02:00
parent 056bd0efdd
commit e9f0bdeb25
2 changed files with 90 additions and 95 deletions

View File

@ -83,9 +83,17 @@ namespace Nz
int textureOverlay;
};
struct SpriteBatch
{
std::size_t spriteCount;
const Material* material;
const Texture* overlayTexture;
Recti scissorRect;
};
mutable std::unordered_map<const Shader*, ShaderUniforms> m_shaderUniforms;
mutable std::vector<LightIndex> m_lights;
mutable std::vector<std::pair<const VertexStruct_XYZ_Color_UV*, std::size_t>> m_spriteChains;
mutable std::vector<SpriteBatch> m_spriteBatches;
Buffer m_vertexBuffer;
mutable BasicRenderQueue m_renderQueue;
TextureRef m_whiteCubemap;

View File

@ -618,61 +618,51 @@ namespace Nz
const RenderTarget* renderTarget = sceneData.viewer->GetTarget();
Recti fullscreenScissorRect = Recti(Vector2i(renderTarget->GetSize()));
Renderer::SetIndexBuffer(&s_quadIndexBuffer);
Renderer::SetMatrix(MatrixType_World, Matrix4f::Identity());
Renderer::SetVertexBuffer(&m_spriteBuffer);
const unsigned int overlayTextureUnit = Material::GetTextureUnit(TextureMap_Overlay);
const std::size_t maxSpriteCount = std::min<std::size_t>(s_maxQuads, m_spriteBuffer.GetVertexCount() / 4);
m_spriteChains.clear();
auto Commit = [&]()
m_spriteBatches.clear();
{
std::size_t spriteChainCount = m_spriteChains.size();
if (spriteChainCount > 0)
{
std::size_t spriteChain = 0; // Which chain of sprites are we treating
std::size_t spriteChainOffset = 0; // Where was the last offset where we stopped in the last chain
do
{
// We open the buffer in writing mode
BufferMapper<VertexBuffer> vertexMapper(m_spriteBuffer, BufferAccess_DiscardAndWrite);
VertexStruct_XYZ_Color_UV* vertices = static_cast<VertexStruct_XYZ_Color_UV*>(vertexMapper.GetPointer());
std::size_t spriteCount = 0;
std::size_t remainingSprite = maxSpriteCount;
do
const Material* lastMaterial = nullptr;
const Texture* lastOverlay = nullptr;
Recti lastScissorRect = Recti(-1, -1);
for (const BasicRenderQueue::SpriteChain& basicSprites : spriteList)
{
const VertexStruct_XYZ_Color_UV* currentChain = m_spriteChains[spriteChain].first;
std::size_t currentChainSpriteCount = m_spriteChains[spriteChain].second;
std::size_t count = std::min(maxSpriteCount - spriteCount, currentChainSpriteCount - spriteChainOffset);
std::memcpy(vertices, currentChain + spriteChainOffset * 4, 4 * count * sizeof(VertexStruct_XYZ_Color_UV));
vertices += count * 4;
spriteCount += count;
spriteChainOffset += count;
// Have we treated the entire chain ?
if (spriteChainOffset == currentChainSpriteCount)
const Nz::Texture* overlayTexture = (basicSprites.overlay) ? basicSprites.overlay.Get() : m_whiteTexture.Get();
const Nz::Recti& scissorRect = (basicSprites.scissorRect.width > 0) ? basicSprites.scissorRect : fullscreenScissorRect;
if (basicSprites.material != lastMaterial || overlayTexture != lastOverlay || (basicSprites.material->IsScissorTestEnabled() && scissorRect != lastScissorRect))
{
spriteChain++;
spriteChainOffset = 0;
}
}
while (spriteCount < maxSpriteCount && spriteChain < spriteChainCount);
m_spriteBatches.emplace_back();
SpriteBatch& newBatch = m_spriteBatches.back();
newBatch.material = basicSprites.material;
newBatch.overlayTexture = overlayTexture;
newBatch.scissorRect = scissorRect;
newBatch.spriteCount = 0;
vertexMapper.Unmap();
Renderer::DrawIndexedPrimitives(PrimitiveMode_TriangleList, 0, spriteCount * 6);
}
while (spriteChain < spriteChainCount);
lastMaterial = basicSprites.material;
lastOverlay = overlayTexture;
lastScissorRect = scissorRect;
}
m_spriteChains.clear();
};
SpriteBatch& currentBatch = m_spriteBatches.back();
std::size_t spriteCount = std::min(remainingSprite, basicSprites.spriteCount);
std::memcpy(vertices, basicSprites.vertices, spriteCount * 4 * sizeof(VertexStruct_XYZ_Color_UV));
vertices += spriteCount * 4;
currentBatch.spriteCount += spriteCount;
remainingSprite -= spriteCount;
if (remainingSprite == 0)
break;
}
}
const Material* lastMaterial = nullptr;
const MaterialPipeline* lastPipeline = nullptr;
@ -683,18 +673,17 @@ namespace Nz
const MaterialPipeline::Instance* pipelineInstance = nullptr;
for (const BasicRenderQueue::SpriteChain& basicSprites : spriteList)
{
const Nz::Recti& scissorRect = (basicSprites.scissorRect.width > 0) ? basicSprites.scissorRect : fullscreenScissorRect;
Renderer::SetIndexBuffer(&s_quadIndexBuffer);
Renderer::SetMatrix(MatrixType_World, Matrix4f::Identity());
Renderer::SetVertexBuffer(&m_spriteBuffer);
if (basicSprites.material != lastMaterial || basicSprites.overlay != lastOverlay || (basicSprites.material->IsScissorTestEnabled() && scissorRect != lastScissorRect))
unsigned int firstIndex = 0;
for (const auto& batch : m_spriteBatches)
{
Commit();
const MaterialPipeline* pipeline = basicSprites.material->GetPipeline();
if (lastPipeline != pipeline)
const MaterialPipeline* pipeline = batch.material->GetPipeline();
if (pipeline != lastPipeline)
{
pipelineInstance = &basicSprites.material->GetPipeline()->Apply(ShaderFlags_TextureOverlay | ShaderFlags_VertexColor);
pipelineInstance = &batch.material->GetPipeline()->Apply(ShaderFlags_TextureOverlay | ShaderFlags_VertexColor);
const Shader* shader = pipelineInstance->uberInstance->GetShader();
if (shader != lastShader)
@ -716,33 +705,31 @@ namespace Nz
lastPipeline = pipeline;
}
if (lastMaterial != basicSprites.material)
if (batch.material != lastMaterial)
{
basicSprites.material->Apply(*pipelineInstance);
batch.material->Apply(*pipelineInstance);
Renderer::SetTextureSampler(overlayTextureUnit, basicSprites.material->GetDiffuseSampler());
Renderer::SetTextureSampler(overlayTextureUnit, batch.material->GetDiffuseSampler());
lastMaterial = basicSprites.material;
lastMaterial = batch.material;
}
const Nz::Texture* overlayTexture = (basicSprites.overlay) ? basicSprites.overlay.Get() : m_whiteTexture.Get();
if (overlayTexture != lastOverlay)
if (batch.overlayTexture != lastOverlay)
{
Renderer::SetTexture(overlayTextureUnit, overlayTexture);
lastOverlay = overlayTexture;
Renderer::SetTexture(overlayTextureUnit, batch.overlayTexture);
lastOverlay = batch.overlayTexture;
}
if (basicSprites.material->IsScissorTestEnabled() && scissorRect != lastScissorRect)
if (batch.material->IsScissorTestEnabled() && batch.scissorRect != lastScissorRect)
{
Renderer::SetScissorRect(scissorRect);
lastScissorRect = scissorRect;
}
Renderer::SetScissorRect(batch.scissorRect);
lastScissorRect = batch.scissorRect;
}
m_spriteChains.emplace_back(basicSprites.vertices, basicSprites.spriteCount);
unsigned int indexCount = batch.spriteCount * 6;
Renderer::DrawIndexedPrimitives(PrimitiveMode_TriangleList, firstIndex, indexCount);
firstIndex += indexCount;
}
Commit();
}
const ForwardRenderTechnique::ShaderUniforms* ForwardRenderTechnique::GetShaderUniforms(const Shader* shader) const