diff --git a/include/Nazara/Graphics/DeferredGeometryPass.hpp b/include/Nazara/Graphics/DeferredGeometryPass.hpp index 3c7890bfe..043378642 100644 --- a/include/Nazara/Graphics/DeferredGeometryPass.hpp +++ b/include/Nazara/Graphics/DeferredGeometryPass.hpp @@ -51,8 +51,16 @@ namespace Nz int textureOverlay; }; + struct SpriteBatch + { + std::size_t spriteCount; + const Material* material; + const Texture* overlayTexture; + Recti scissorRect; + }; + mutable std::unordered_map m_shaderUniforms; - mutable std::vector> m_spriteChains; + mutable std::vector m_spriteBatches; Buffer m_vertexBuffer; RenderStates m_clearStates; ShaderRef m_clearShader; diff --git a/include/Nazara/Graphics/DepthRenderTechnique.hpp b/include/Nazara/Graphics/DepthRenderTechnique.hpp index f8f7bc4e3..5ae883352 100644 --- a/include/Nazara/Graphics/DepthRenderTechnique.hpp +++ b/include/Nazara/Graphics/DepthRenderTechnique.hpp @@ -61,8 +61,16 @@ namespace Nz int textureOverlay; }; + struct SpriteBatch + { + std::size_t spriteCount; + const Material* material; + const Texture* overlayTexture; + Recti scissorRect; + }; + mutable std::unordered_map m_shaderUniforms; - mutable std::vector> m_spriteChains; + mutable std::vector m_spriteBatches; Buffer m_vertexBuffer; RenderStates m_clearStates; ShaderRef m_clearShader; diff --git a/src/Nazara/Graphics/DeferredGeometryPass.cpp b/src/Nazara/Graphics/DeferredGeometryPass.cpp index dbd500ea4..0129c4980 100644 --- a/src/Nazara/Graphics/DeferredGeometryPass.cpp +++ b/src/Nazara/Graphics/DeferredGeometryPass.cpp @@ -29,8 +29,8 @@ namespace Nz Vector2f uv; }; - UInt32 s_maxQuads = std::numeric_limits::max() / 6; - UInt32 s_vertexBufferSize = 4 * 1024 * 1024; // 4 MiB + constexpr UInt32 s_vertexBufferSize = 4 * 1024 * 1024; // 4 MiB + constexpr UInt32 s_maxQuadPerDraw = s_vertexBufferSize / sizeof(VertexLayout_XYZ_Color_UV); } /*! @@ -468,62 +468,9 @@ 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 std::size_t maxSpriteCount = std::min(s_maxQuadPerDraw, m_spriteBuffer.GetVertexCount() / 4); const unsigned int overlayTextureUnit = Material::GetTextureUnit(TextureMap_Overlay); - const std::size_t maxSpriteCount = std::min(s_maxQuads, m_spriteBuffer.GetVertexCount() / 4); - - m_spriteChains.clear(); - - auto Commit = [&]() - { - 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 vertexMapper(m_spriteBuffer, BufferAccess_DiscardAndWrite); - VertexStruct_XYZ_Color_UV* vertices = static_cast(vertexMapper.GetPointer()); - - std::size_t spriteCount = 0; - - do - { - 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(); - }; - const Material* lastMaterial = nullptr; const MaterialPipeline* lastPipeline = nullptr; const Shader* lastShader = nullptr; @@ -533,18 +480,19 @@ namespace Nz 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); + + auto Draw = [&]() { - const Nz::Recti& scissorRect = (basicSprites.scissorRect.width > 0) ? basicSprites.scissorRect : fullscreenScissorRect; - - 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_Deferred | ShaderFlags_TextureOverlay | ShaderFlags_VertexColor); + pipelineInstance = &batch.material->GetPipeline()->Apply(ShaderFlags_TextureOverlay | ShaderFlags_VertexColor); const Shader* shader = pipelineInstance->uberInstance->GetShader(); if (shader != lastShader) @@ -566,33 +514,105 @@ 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; } + 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; + } + }; + + m_spriteBatches.clear(); + { + BufferMapper vertexMapper; + VertexStruct_XYZ_Color_UV* vertices = nullptr; + + std::size_t remainingSprite = maxSpriteCount; + + const Material* lastMaterial = nullptr; + const Texture* lastOverlay = nullptr; + Recti lastScissorRect = Recti(-1, -1); + + for (const BasicRenderQueue::SpriteChain& basicSprites : spriteList) + { const Nz::Texture* overlayTexture = (basicSprites.overlay) ? basicSprites.overlay.Get() : m_whiteTexture.Get(); - if (overlayTexture != lastOverlay) - { - Renderer::SetTexture(overlayTextureUnit, overlayTexture); - lastOverlay = overlayTexture; - } + const Nz::Recti& scissorRect = (basicSprites.scissorRect.width > 0) ? basicSprites.scissorRect : fullscreenScissorRect; - if (basicSprites.material->IsScissorTestEnabled() && scissorRect != lastScissorRect) + const VertexStruct_XYZ_Color_UV* spriteVertices = basicSprites.vertices; + std::size_t spriteCount = basicSprites.spriteCount; + + for (;;) { - Renderer::SetScissorRect(scissorRect); - lastScissorRect = scissorRect; + if (m_spriteBatches.empty() || basicSprites.material != lastMaterial || overlayTexture != lastOverlay || (basicSprites.material->IsScissorTestEnabled() && scissorRect != lastScissorRect)) + { + m_spriteBatches.emplace_back(); + SpriteBatch& newBatch = m_spriteBatches.back(); + newBatch.material = basicSprites.material; + newBatch.overlayTexture = overlayTexture; + newBatch.scissorRect = scissorRect; + newBatch.spriteCount = 0; + + lastMaterial = basicSprites.material; + lastOverlay = overlayTexture; + lastScissorRect = scissorRect; + } + + SpriteBatch& currentBatch = m_spriteBatches.back(); + + if (!vertices) + { + vertexMapper.Map(m_spriteBuffer, BufferAccess_DiscardAndWrite); + vertices = static_cast(vertexMapper.GetPointer()); + } + + std::size_t processedSpriteCount = std::min(remainingSprite, spriteCount); + std::size_t processedVertices = processedSpriteCount * 4; + + std::memcpy(vertices, spriteVertices, processedVertices * sizeof(VertexStruct_XYZ_Color_UV)); + vertices += processedVertices; + spriteVertices += processedVertices; + + currentBatch.spriteCount += processedSpriteCount; + spriteCount -= processedSpriteCount; + + remainingSprite -= processedSpriteCount; + if (remainingSprite == 0) + { + vertexMapper.Unmap(); + vertices = nullptr; + + Draw(); + + remainingSprite = maxSpriteCount; + m_spriteBatches.clear(); + } + + if (spriteCount == 0) + break; } } - - m_spriteChains.emplace_back(basicSprites.vertices, basicSprites.spriteCount); } - Commit(); + Draw(); } const DeferredGeometryPass::ShaderUniforms* DeferredGeometryPass::GetShaderUniforms(const Shader* shader) const @@ -631,12 +651,12 @@ namespace Nz { ErrorFlags flags(ErrorFlag_ThrowException, true); - s_quadIndexBuffer.Reset(false, s_maxQuads * 6, DataStorage_Hardware, 0); + s_quadIndexBuffer.Reset(true, s_maxQuadPerDraw * 6, DataStorage_Hardware, 0); BufferMapper mapper(s_quadIndexBuffer, BufferAccess_WriteOnly); - UInt16* indices = static_cast(mapper.GetPointer()); + UInt32* indices = static_cast(mapper.GetPointer()); - for (unsigned int i = 0; i < s_maxQuads; ++i) + for (UInt32 i = 0; i < s_maxQuadPerDraw; ++i) { *indices++ = i * 4 + 0; *indices++ = i * 4 + 2; diff --git a/src/Nazara/Graphics/DepthRenderTechnique.cpp b/src/Nazara/Graphics/DepthRenderTechnique.cpp index 5435d7497..531b9988a 100644 --- a/src/Nazara/Graphics/DepthRenderTechnique.cpp +++ b/src/Nazara/Graphics/DepthRenderTechnique.cpp @@ -31,8 +31,8 @@ namespace Nz Vector2f uv; }; - unsigned int s_maxQuads = std::numeric_limits::max() / 6; - unsigned int s_vertexBufferSize = 4 * 1024 * 1024; // 4 MiB + constexpr UInt32 s_vertexBufferSize = 4 * 1024 * 1024; // 4 MiB + constexpr UInt32 s_maxQuadPerDraw = s_vertexBufferSize / sizeof(VertexLayout_XYZ_Color_UV); } /*! @@ -46,7 +46,7 @@ namespace Nz */ DepthRenderTechnique::DepthRenderTechnique() : - m_vertexBuffer(BufferType_Vertex) + m_vertexBuffer(BufferType_Vertex) { ErrorFlags flags(ErrorFlag_ThrowException, true); @@ -148,12 +148,12 @@ namespace Nz { ErrorFlags flags(ErrorFlag_ThrowException, true); - s_quadIndexBuffer.Reset(false, s_maxQuads * 6, DataStorage_Hardware, 0); + s_quadIndexBuffer.Reset(true, s_maxQuadPerDraw * 6, DataStorage_Hardware, 0); BufferMapper mapper(s_quadIndexBuffer, BufferAccess_WriteOnly); - UInt16* indices = static_cast(mapper.GetPointer()); + UInt32* indices = static_cast(mapper.GetPointer()); - for (unsigned int i = 0; i < s_maxQuads; ++i) + for (UInt32 i = 0; i < s_maxQuadPerDraw; ++i) { *indices++ = i * 4 + 0; *indices++ = i * 4 + 2; @@ -486,62 +486,9 @@ 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 std::size_t maxSpriteCount = std::min(s_maxQuadPerDraw, m_spriteBuffer.GetVertexCount() / 4); const unsigned int overlayTextureUnit = Material::GetTextureUnit(TextureMap_Overlay); - const std::size_t maxSpriteCount = std::min(s_maxQuads, m_spriteBuffer.GetVertexCount() / 4); - - m_spriteChains.clear(); - - auto Commit = [&]() - { - 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 vertexMapper(m_spriteBuffer, BufferAccess_DiscardAndWrite); - VertexStruct_XYZ_Color_UV* vertices = static_cast(vertexMapper.GetPointer()); - - std::size_t spriteCount = 0; - - do - { - 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(); - }; - const Material* lastMaterial = nullptr; const MaterialPipeline* lastPipeline = nullptr; const Shader* lastShader = nullptr; @@ -551,18 +498,19 @@ namespace Nz 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); + + auto Draw = [&]() { - const Nz::Recti& scissorRect = (basicSprites.scissorRect.width > 0) ? basicSprites.scissorRect : fullscreenScissorRect; - - 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_Deferred | ShaderFlags_TextureOverlay | ShaderFlags_VertexColor); + pipelineInstance = &batch.material->GetPipeline()->Apply(ShaderFlags_TextureOverlay | ShaderFlags_VertexColor); const Shader* shader = pipelineInstance->uberInstance->GetShader(); if (shader != lastShader) @@ -582,33 +530,105 @@ 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; } + 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; + } + }; + + m_spriteBatches.clear(); + { + BufferMapper vertexMapper; + VertexStruct_XYZ_Color_UV* vertices = nullptr; + + std::size_t remainingSprite = maxSpriteCount; + + const Material* lastMaterial = nullptr; + const Texture* lastOverlay = nullptr; + Recti lastScissorRect = Recti(-1, -1); + + for (const BasicRenderQueue::SpriteChain& basicSprites : spriteList) + { const Nz::Texture* overlayTexture = (basicSprites.overlay) ? basicSprites.overlay.Get() : m_whiteTexture.Get(); - if (overlayTexture != lastOverlay) - { - Renderer::SetTexture(overlayTextureUnit, overlayTexture); - lastOverlay = overlayTexture; - } + const Nz::Recti& scissorRect = (basicSprites.scissorRect.width > 0) ? basicSprites.scissorRect : fullscreenScissorRect; - if (basicSprites.material->IsScissorTestEnabled() && scissorRect != lastScissorRect) + const VertexStruct_XYZ_Color_UV* spriteVertices = basicSprites.vertices; + std::size_t spriteCount = basicSprites.spriteCount; + + for (;;) { - Renderer::SetScissorRect(scissorRect); - lastScissorRect = scissorRect; + if (m_spriteBatches.empty() || basicSprites.material != lastMaterial || overlayTexture != lastOverlay || (basicSprites.material->IsScissorTestEnabled() && scissorRect != lastScissorRect)) + { + m_spriteBatches.emplace_back(); + SpriteBatch& newBatch = m_spriteBatches.back(); + newBatch.material = basicSprites.material; + newBatch.overlayTexture = overlayTexture; + newBatch.scissorRect = scissorRect; + newBatch.spriteCount = 0; + + lastMaterial = basicSprites.material; + lastOverlay = overlayTexture; + lastScissorRect = scissorRect; + } + + SpriteBatch& currentBatch = m_spriteBatches.back(); + + if (!vertices) + { + vertexMapper.Map(m_spriteBuffer, BufferAccess_DiscardAndWrite); + vertices = static_cast(vertexMapper.GetPointer()); + } + + std::size_t processedSpriteCount = std::min(remainingSprite, spriteCount); + std::size_t processedVertices = processedSpriteCount * 4; + + std::memcpy(vertices, spriteVertices, processedVertices * sizeof(VertexStruct_XYZ_Color_UV)); + vertices += processedVertices; + spriteVertices += processedVertices; + + currentBatch.spriteCount += processedSpriteCount; + spriteCount -= processedSpriteCount; + + remainingSprite -= processedSpriteCount; + if (remainingSprite == 0) + { + vertexMapper.Unmap(); + vertices = nullptr; + + Draw(); + + remainingSprite = maxSpriteCount; + m_spriteBatches.clear(); + } + + if (spriteCount == 0) + break; } } - - m_spriteChains.emplace_back(basicSprites.vertices, basicSprites.spriteCount); } - Commit(); + Draw(); } /*! diff --git a/src/Nazara/Graphics/ForwardRenderTechnique.cpp b/src/Nazara/Graphics/ForwardRenderTechnique.cpp index 934d54d6d..77098ecf5 100644 --- a/src/Nazara/Graphics/ForwardRenderTechnique.cpp +++ b/src/Nazara/Graphics/ForwardRenderTechnique.cpp @@ -32,8 +32,8 @@ namespace Nz Vector2f uv; }; - UInt32 s_maxQuads = std::numeric_limits::max() / 6; - UInt32 s_vertexBufferSize = 4 * 1024 * 1024; // 4 MiB + constexpr UInt32 s_vertexBufferSize = 4 * 1024 * 1024; // 4 MiB + constexpr UInt32 s_maxQuadPerDraw = s_vertexBufferSize / sizeof(VertexLayout_XYZ_Color_UV); } /*! @@ -175,12 +175,12 @@ namespace Nz { ErrorFlags flags(ErrorFlag_ThrowException, true); - s_quadIndexBuffer.Reset(false, s_maxQuads * 6, DataStorage_Hardware, 0); + s_quadIndexBuffer.Reset(true, s_maxQuadPerDraw * 6, DataStorage_Hardware, 0); BufferMapper mapper(s_quadIndexBuffer, BufferAccess_WriteOnly); - UInt16* indices = static_cast(mapper.GetPointer()); + UInt32* indices = static_cast(mapper.GetPointer()); - for (unsigned int i = 0; i < s_maxQuads; ++i) + for (UInt32 i = 0; i < s_maxQuadPerDraw; ++i) { *indices++ = i * 4 + 0; *indices++ = i * 4 + 2; @@ -618,52 +618,9 @@ namespace Nz const RenderTarget* renderTarget = sceneData.viewer->GetTarget(); Recti fullscreenScissorRect = Recti(Vector2i(renderTarget->GetSize())); + const std::size_t maxSpriteCount = std::min(s_maxQuadPerDraw, m_spriteBuffer.GetVertexCount() / 4); + const unsigned int overlayTextureUnit = Material::GetTextureUnit(TextureMap_Overlay); - const std::size_t maxSpriteCount = std::min(s_maxQuads, m_spriteBuffer.GetVertexCount() / 4); - - m_spriteBatches.clear(); - { - BufferMapper vertexMapper(m_spriteBuffer, BufferAccess_DiscardAndWrite); - VertexStruct_XYZ_Color_UV* vertices = static_cast(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) - { - 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)) - { - m_spriteBatches.emplace_back(); - SpriteBatch& newBatch = m_spriteBatches.back(); - newBatch.material = basicSprites.material; - newBatch.overlayTexture = overlayTexture; - newBatch.scissorRect = scissorRect; - newBatch.spriteCount = 0; - - lastMaterial = basicSprites.material; - lastOverlay = overlayTexture; - lastScissorRect = scissorRect; - } - - 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; const Shader* lastShader = nullptr; @@ -677,59 +634,135 @@ namespace Nz Renderer::SetMatrix(MatrixType_World, Matrix4f::Identity()); Renderer::SetVertexBuffer(&m_spriteBuffer); - unsigned int firstIndex = 0; - for (const auto& batch : m_spriteBatches) + auto Draw = [&]() { - const MaterialPipeline* pipeline = batch.material->GetPipeline(); - if (pipeline != lastPipeline) + unsigned int firstIndex = 0; + for (const auto& batch : m_spriteBatches) { - pipelineInstance = &batch.material->GetPipeline()->Apply(ShaderFlags_TextureOverlay | ShaderFlags_VertexColor); - - const Shader* shader = pipelineInstance->uberInstance->GetShader(); - if (shader != lastShader) + const MaterialPipeline* pipeline = batch.material->GetPipeline(); + if (pipeline != lastPipeline) { - // Index of uniforms in the shader - shaderUniforms = GetShaderUniforms(shader); + pipelineInstance = &batch.material->GetPipeline()->Apply(ShaderFlags_TextureOverlay | ShaderFlags_VertexColor); - // Ambient color of the scene - shader->SendColor(shaderUniforms->sceneAmbient, sceneData.ambientColor); - // Position of the camera - shader->SendVector(shaderUniforms->eyePosition, sceneData.viewer->GetEyePosition()); + const Shader* shader = pipelineInstance->uberInstance->GetShader(); + if (shader != lastShader) + { + // Index of uniforms in the shader + shaderUniforms = GetShaderUniforms(shader); - // Overlay texture unit - shader->SendInteger(shaderUniforms->textureOverlay, overlayTextureUnit); + // Ambient color of the scene + shader->SendColor(shaderUniforms->sceneAmbient, sceneData.ambientColor); + // Position of the camera + shader->SendVector(shaderUniforms->eyePosition, sceneData.viewer->GetEyePosition()); - lastShader = shader; + // Overlay texture unit + shader->SendInteger(shaderUniforms->textureOverlay, overlayTextureUnit); + + lastShader = shader; + } + + lastPipeline = pipeline; } - lastPipeline = pipeline; - } + if (batch.material != lastMaterial) + { + batch.material->Apply(*pipelineInstance); - if (batch.material != lastMaterial) + 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; + } + }; + + m_spriteBatches.clear(); + { + BufferMapper vertexMapper; + VertexStruct_XYZ_Color_UV* vertices = nullptr; + + std::size_t remainingSprite = maxSpriteCount; + + const Material* lastMaterial = nullptr; + const Texture* lastOverlay = nullptr; + Recti lastScissorRect = Recti(-1, -1); + + for (const BasicRenderQueue::SpriteChain& basicSprites : spriteList) { - batch.material->Apply(*pipelineInstance); + const Nz::Texture* overlayTexture = (basicSprites.overlay) ? basicSprites.overlay.Get() : m_whiteTexture.Get(); + const Nz::Recti& scissorRect = (basicSprites.scissorRect.width > 0) ? basicSprites.scissorRect : fullscreenScissorRect; - Renderer::SetTextureSampler(overlayTextureUnit, batch.material->GetDiffuseSampler()); + const VertexStruct_XYZ_Color_UV* spriteVertices = basicSprites.vertices; + std::size_t spriteCount = basicSprites.spriteCount; + + for (;;) + { + if (m_spriteBatches.empty() || basicSprites.material != lastMaterial || overlayTexture != lastOverlay || (basicSprites.material->IsScissorTestEnabled() && scissorRect != lastScissorRect)) + { + m_spriteBatches.emplace_back(); + SpriteBatch& newBatch = m_spriteBatches.back(); + newBatch.material = basicSprites.material; + newBatch.overlayTexture = overlayTexture; + newBatch.scissorRect = scissorRect; + newBatch.spriteCount = 0; - lastMaterial = batch.material; + lastMaterial = basicSprites.material; + lastOverlay = overlayTexture; + lastScissorRect = scissorRect; + } + + SpriteBatch& currentBatch = m_spriteBatches.back(); + + if (!vertices) + { + vertexMapper.Map(m_spriteBuffer, BufferAccess_DiscardAndWrite); + vertices = static_cast(vertexMapper.GetPointer()); + } + + std::size_t processedSpriteCount = std::min(remainingSprite, spriteCount); + std::size_t processedVertices = processedSpriteCount * 4; + + std::memcpy(vertices, spriteVertices, processedVertices * sizeof(VertexStruct_XYZ_Color_UV)); + vertices += processedVertices; + spriteVertices += processedVertices; + + currentBatch.spriteCount += processedSpriteCount; + spriteCount -= processedSpriteCount; + + remainingSprite -= processedSpriteCount; + if (remainingSprite == 0) + { + vertexMapper.Unmap(); + vertices = nullptr; + + Draw(); + + remainingSprite = maxSpriteCount; + m_spriteBatches.clear(); + } + + if (spriteCount == 0) + break; + } } - - 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; } + + Draw(); } const ForwardRenderTechnique::ShaderUniforms* ForwardRenderTechnique::GetShaderUniforms(const Shader* shader) const