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; 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::unordered_map<const Shader*, ShaderUniforms> m_shaderUniforms;
mutable std::vector<LightIndex> m_lights; 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; Buffer m_vertexBuffer;
mutable BasicRenderQueue m_renderQueue; mutable BasicRenderQueue m_renderQueue;
TextureRef m_whiteCubemap; TextureRef m_whiteCubemap;

View File

@ -618,61 +618,51 @@ namespace Nz
const RenderTarget* renderTarget = sceneData.viewer->GetTarget(); const RenderTarget* renderTarget = sceneData.viewer->GetTarget();
Recti fullscreenScissorRect = Recti(Vector2i(renderTarget->GetSize())); 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 unsigned int overlayTextureUnit = Material::GetTextureUnit(TextureMap_Overlay);
const std::size_t maxSpriteCount = std::min<std::size_t>(s_maxQuads, m_spriteBuffer.GetVertexCount() / 4); const std::size_t maxSpriteCount = std::min<std::size_t>(s_maxQuads, m_spriteBuffer.GetVertexCount() / 4);
m_spriteChains.clear(); m_spriteBatches.clear();
auto Commit = [&]()
{ {
std::size_t spriteChainCount = m_spriteChains.size(); BufferMapper<VertexBuffer> vertexMapper(m_spriteBuffer, BufferAccess_DiscardAndWrite);
if (spriteChainCount > 0) VertexStruct_XYZ_Color_UV* vertices = static_cast<VertexStruct_XYZ_Color_UV*>(vertexMapper.GetPointer());
std::size_t remainingSprite = maxSpriteCount;
const Material* lastMaterial = nullptr;
const Texture* lastOverlay = nullptr;
Recti lastScissorRect = Recti(-1, -1);
for (const BasicRenderQueue::SpriteChain& basicSprites : spriteList)
{ {
std::size_t spriteChain = 0; // Which chain of sprites are we treating const Nz::Texture* overlayTexture = (basicSprites.overlay) ? basicSprites.overlay.Get() : m_whiteTexture.Get();
std::size_t spriteChainOffset = 0; // Where was the last offset where we stopped in the last chain const Nz::Recti& scissorRect = (basicSprites.scissorRect.width > 0) ? basicSprites.scissorRect : fullscreenScissorRect;
if (basicSprites.material != lastMaterial || overlayTexture != lastOverlay || (basicSprites.material->IsScissorTestEnabled() && scissorRect != lastScissorRect))
do
{ {
// We open the buffer in writing mode m_spriteBatches.emplace_back();
BufferMapper<VertexBuffer> vertexMapper(m_spriteBuffer, BufferAccess_DiscardAndWrite); SpriteBatch& newBatch = m_spriteBatches.back();
VertexStruct_XYZ_Color_UV* vertices = static_cast<VertexStruct_XYZ_Color_UV*>(vertexMapper.GetPointer()); newBatch.material = basicSprites.material;
newBatch.overlayTexture = overlayTexture;
newBatch.scissorRect = scissorRect;
newBatch.spriteCount = 0;
std::size_t spriteCount = 0; lastMaterial = basicSprites.material;
lastOverlay = overlayTexture;
do lastScissorRect = scissorRect;
{
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)
{
spriteChain++;
spriteChainOffset = 0;
}
}
while (spriteCount < maxSpriteCount && spriteChain < spriteChainCount);
vertexMapper.Unmap();
Renderer::DrawIndexedPrimitives(PrimitiveMode_TriangleList, 0, spriteCount * 6);
} }
while (spriteChain < spriteChainCount);
}
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 Material* lastMaterial = nullptr;
const MaterialPipeline* lastPipeline = nullptr; const MaterialPipeline* lastPipeline = nullptr;
@ -683,66 +673,63 @@ namespace Nz
const MaterialPipeline::Instance* pipelineInstance = nullptr; const MaterialPipeline::Instance* pipelineInstance = nullptr;
for (const BasicRenderQueue::SpriteChain& basicSprites : spriteList) Renderer::SetIndexBuffer(&s_quadIndexBuffer);
Renderer::SetMatrix(MatrixType_World, Matrix4f::Identity());
Renderer::SetVertexBuffer(&m_spriteBuffer);
unsigned int firstIndex = 0;
for (const auto& batch : m_spriteBatches)
{ {
const Nz::Recti& scissorRect = (basicSprites.scissorRect.width > 0) ? basicSprites.scissorRect : fullscreenScissorRect; const MaterialPipeline* pipeline = batch.material->GetPipeline();
if (pipeline != lastPipeline)
if (basicSprites.material != lastMaterial || basicSprites.overlay != lastOverlay || (basicSprites.material->IsScissorTestEnabled() && scissorRect != lastScissorRect))
{ {
Commit(); pipelineInstance = &batch.material->GetPipeline()->Apply(ShaderFlags_TextureOverlay | ShaderFlags_VertexColor);
const MaterialPipeline* pipeline = basicSprites.material->GetPipeline(); const Shader* shader = pipelineInstance->uberInstance->GetShader();
if (lastPipeline != pipeline) if (shader != lastShader)
{ {
pipelineInstance = &basicSprites.material->GetPipeline()->Apply(ShaderFlags_TextureOverlay | ShaderFlags_VertexColor); // Index of uniforms in the shader
shaderUniforms = GetShaderUniforms(shader);
const Shader* shader = pipelineInstance->uberInstance->GetShader(); // Ambient color of the scene
if (shader != lastShader) shader->SendColor(shaderUniforms->sceneAmbient, sceneData.ambientColor);
{ // Position of the camera
// Index of uniforms in the shader shader->SendVector(shaderUniforms->eyePosition, sceneData.viewer->GetEyePosition());
shaderUniforms = GetShaderUniforms(shader);
// Ambient color of the scene // Overlay texture unit
shader->SendColor(shaderUniforms->sceneAmbient, sceneData.ambientColor); shader->SendInteger(shaderUniforms->textureOverlay, overlayTextureUnit);
// Position of the camera
shader->SendVector(shaderUniforms->eyePosition, sceneData.viewer->GetEyePosition());
// Overlay texture unit lastShader = shader;
shader->SendInteger(shaderUniforms->textureOverlay, overlayTextureUnit);
lastShader = shader;
}
lastPipeline = pipeline;
} }
if (lastMaterial != basicSprites.material) lastPipeline = pipeline;
{
basicSprites.material->Apply(*pipelineInstance);
Renderer::SetTextureSampler(overlayTextureUnit, basicSprites.material->GetDiffuseSampler());
lastMaterial = basicSprites.material;
}
const Nz::Texture* overlayTexture = (basicSprites.overlay) ? basicSprites.overlay.Get() : m_whiteTexture.Get();
if (overlayTexture != lastOverlay)
{
Renderer::SetTexture(overlayTextureUnit, overlayTexture);
lastOverlay = overlayTexture;
}
if (basicSprites.material->IsScissorTestEnabled() && scissorRect != lastScissorRect)
{
Renderer::SetScissorRect(scissorRect);
lastScissorRect = scissorRect;
}
} }
m_spriteChains.emplace_back(basicSprites.vertices, basicSprites.spriteCount); if (batch.material != lastMaterial)
} {
batch.material->Apply(*pipelineInstance);
Commit(); Renderer::SetTextureSampler(overlayTextureUnit, batch.material->GetDiffuseSampler());
lastMaterial = batch.material;
}
if (batch.overlayTexture != lastOverlay)
{
Renderer::SetTexture(overlayTextureUnit, batch.overlayTexture);
lastOverlay = batch.overlayTexture;
}
if (batch.material->IsScissorTestEnabled() && batch.scissorRect != lastScissorRect)
{
Renderer::SetScissorRect(batch.scissorRect);
lastScissorRect = batch.scissorRect;
}
unsigned int indexCount = batch.spriteCount * 6;
Renderer::DrawIndexedPrimitives(PrimitiveMode_TriangleList, firstIndex, indexCount);
firstIndex += indexCount;
}
} }
const ForwardRenderTechnique::ShaderUniforms* ForwardRenderTechnique::GetShaderUniforms(const Shader* shader) const const ForwardRenderTechnique::ShaderUniforms* ForwardRenderTechnique::GetShaderUniforms(const Shader* shader) const