OpenGLRenderer/CommandBuffer: Replace std::visit by a switch (to improve performance)

This commit is contained in:
SirLynix 2023-04-30 21:12:33 +02:00
parent 97f1c2c56c
commit 3957687a31
3 changed files with 339 additions and 296 deletions

View File

@ -8,6 +8,7 @@
#define NAZARA_OPENGLRENDERER_OPENGLCOMMANDBUFFER_HPP
#include <NazaraUtils/Prerequisites.hpp>
#include <NazaraUtils/TypeList.hpp>
#include <Nazara/Core/Color.hpp>
#include <Nazara/Math/Rect.hpp>
#include <Nazara/OpenGLRenderer/Config.hpp>
@ -79,17 +80,57 @@ namespace Nz
struct DrawStates;
struct ShaderBindings;
#define NAZARA_OPENGL_FOREACH_COMMANDS(cb, lastCb) \
cb(BeginDebugRegionCommand) \
cb(BlitTextureCommand) \
cb(CopyBufferCommand) \
cb(CopyBufferFromMemoryCommand) \
cb(CopyTextureCommand) \
cb(DispatchCommand) \
cb(DrawCommand) \
cb(DrawIndexedCommand) \
cb(EndDebugRegionCommand) \
cb(MemoryBarrier) \
lastCb(SetFrameBufferCommand) \
#define NAZARA_OPENGL_COMMAND_CALLBACK(Command) struct Command;
NAZARA_OPENGL_FOREACH_COMMANDS(NAZARA_OPENGL_COMMAND_CALLBACK, NAZARA_OPENGL_COMMAND_CALLBACK)
#undef NAZARA_OPENGL_COMMAND_CALLBACK
using CommandList = TypeList<
#define NAZARA_OPENGL_COMMAND_CALLBACK(Command) Command,
#define NAZARA_OPENGL_COMMAND_CALLBACK_LAST(Command) Command
NAZARA_OPENGL_FOREACH_COMMANDS(NAZARA_OPENGL_COMMAND_CALLBACK, NAZARA_OPENGL_COMMAND_CALLBACK_LAST)
#undef NAZARA_OPENGL_COMMAND_CALLBACK_LAST
#undef NAZARA_OPENGL_COMMAND_CALLBACK
>;
void ApplyBindings(const GL::Context& context, const ShaderBindings& bindings);
void ApplyStates(const GL::Context& context, const DrawStates& states);
inline void Execute(const GL::Context* context, const BeginDebugRegionCommand& command);
inline void Execute(const GL::Context* context, const BlitTextureCommand& command);
inline void Execute(const GL::Context* context, const CopyBufferCommand& command);
inline void Execute(const GL::Context* context, const CopyBufferFromMemoryCommand& command);
inline void Execute(const GL::Context* context, const CopyTextureCommand& command);
inline void Execute(const GL::Context* context, const DispatchCommand& command);
inline void Execute(const GL::Context* context, const DrawCommand& command);
inline void Execute(const GL::Context* context, const DrawIndexedCommand& command);
inline void Execute(const GL::Context* context, const EndDebugRegionCommand& command);
inline void Execute(const GL::Context* context, const MemoryBarrier& command);
inline void Execute(const GL::Context*& context, const SetFrameBufferCommand& command);
void Release() override;
struct BeginDebugRegionData
struct BeginDebugRegionCommand
{
std::string regionName;
Color color;
};
struct BlitTextureData
struct BlitTextureCommand
{
const OpenGLTexture* source;
const OpenGLTexture* target;
@ -103,7 +144,7 @@ namespace Nz
const OpenGLComputePipeline* pipeline = nullptr;
};
struct CopyBufferData
struct CopyBufferCommand
{
GLuint source;
GLuint target;
@ -112,7 +153,7 @@ namespace Nz
UInt64 targetOffset;
};
struct CopyTextureData
struct CopyTextureCommand
{
const OpenGLTexture* source;
const OpenGLTexture* target;
@ -120,7 +161,7 @@ namespace Nz
Vector3ui targetPoint;
};
struct CopyBufferFromMemoryData
struct CopyBufferFromMemoryCommand
{
const void* memory;
GLuint target;
@ -133,7 +174,7 @@ namespace Nz
std::vector<std::pair<const OpenGLRenderPipelineLayout*, const OpenGLShaderBinding*>> shaderBindings;
};
struct DispatchData
struct DispatchCommand
{
ComputeStates states;
ShaderBindings bindings;
@ -160,7 +201,7 @@ namespace Nz
bool shouldFlipY = false;
};
struct DrawData
struct DrawCommand
{
DrawStates states;
ShaderBindings bindings;
@ -170,7 +211,7 @@ namespace Nz
UInt32 vertexCount;
};
struct DrawIndexedData
struct DrawIndexedCommand
{
DrawStates states;
ShaderBindings bindings;
@ -180,7 +221,7 @@ namespace Nz
UInt32 instanceCount;
};
struct EndDebugRegionData
struct EndDebugRegionCommand
{
};
@ -189,26 +230,14 @@ namespace Nz
GLbitfield barriers;
};
struct SetFrameBufferData
struct SetFrameBufferCommand
{
std::array<CommandBufferBuilder::ClearValues, 16> clearValues; //< TODO: Remove hard limit?
const OpenGLFramebuffer* framebuffer;
const OpenGLRenderPass* renderpass;
};
using CommandData = std::variant<
BeginDebugRegionData,
BlitTextureData,
CopyBufferData,
CopyBufferFromMemoryData,
CopyTextureData,
DispatchData,
DrawData,
DrawIndexedData,
EndDebugRegionData,
MemoryBarrier,
SetFrameBufferData
>;
using CommandData = TypeListInstantiate<CommandList, std::variant>;
ComputeStates m_currentComputeStates;
DrawStates m_currentDrawStates;

View File

@ -25,7 +25,7 @@ namespace Nz
inline void OpenGLCommandBuffer::BeginDebugRegion(const std::string_view& regionName, const Color& color)
{
BeginDebugRegionData beginDebugRegion;
BeginDebugRegionCommand beginDebugRegion;
beginDebugRegion.color = color;
beginDebugRegion.regionName = regionName;
@ -77,7 +77,7 @@ namespace Nz
inline void OpenGLCommandBuffer::BlitTexture(const OpenGLTexture& source, const Boxui& sourceBox, const OpenGLTexture& target, const Boxui& targetBox, SamplerFilter filter)
{
BlitTextureData blitTexture = {
BlitTextureCommand blitTexture = {
&source,
&target,
sourceBox,
@ -90,7 +90,7 @@ namespace Nz
inline void OpenGLCommandBuffer::CopyBuffer(GLuint source, GLuint target, UInt64 size, UInt64 sourceOffset, UInt64 targetOffset)
{
CopyBufferData copyBuffer = {
CopyBufferCommand copyBuffer = {
source,
target,
size,
@ -103,7 +103,7 @@ namespace Nz
inline void OpenGLCommandBuffer::CopyBuffer(const UploadPool::Allocation& allocation, GLuint target, UInt64 size, UInt64 sourceOffset, UInt64 targetOffset)
{
CopyBufferFromMemoryData copyBuffer = {
CopyBufferFromMemoryCommand copyBuffer = {
static_cast<const UInt8*>(allocation.mappedPtr) + sourceOffset,
target,
size,
@ -115,7 +115,7 @@ namespace Nz
inline void OpenGLCommandBuffer::CopyTexture(const OpenGLTexture& source, const Boxui& sourceBox, const OpenGLTexture& target, const Vector3ui& targetPoint)
{
CopyTextureData copyTexture = {
CopyTextureCommand copyTexture = {
&source,
&target,
sourceBox,
@ -130,7 +130,7 @@ namespace Nz
if (!m_currentComputeStates.pipeline)
throw std::runtime_error("no pipeline bound");
DispatchData dispatch;
DispatchCommand dispatch;
dispatch.bindings = m_currentComputeShaderBindings;
dispatch.states = m_currentComputeStates;
dispatch.numGroupsX = numGroupsX;
@ -145,7 +145,7 @@ namespace Nz
if (!m_currentDrawStates.pipeline)
throw std::runtime_error("no pipeline bound");
DrawData draw;
DrawCommand draw;
draw.bindings = m_currentGraphicsShaderBindings;
draw.states = m_currentDrawStates;
draw.firstInstance = firstInstance;
@ -161,7 +161,7 @@ namespace Nz
if (!m_currentDrawStates.pipeline)
throw std::runtime_error("no pipeline bound");
DrawIndexedData draw;
DrawIndexedCommand draw;
draw.bindings = m_currentGraphicsShaderBindings;
draw.states = m_currentDrawStates;
draw.firstIndex = firstIndex;
@ -174,7 +174,7 @@ namespace Nz
inline void OpenGLCommandBuffer::EndDebugRegion()
{
m_commands.emplace_back(EndDebugRegionData{});
m_commands.emplace_back(EndDebugRegionCommand{});
}
inline std::size_t OpenGLCommandBuffer::GetBindingIndex() const
@ -214,7 +214,7 @@ namespace Nz
{
m_maxColorBufferCount = std::max(m_maxColorBufferCount, framebuffer.GetColorBufferCount());
SetFrameBufferData setFramebuffer;
SetFrameBufferCommand setFramebuffer;
setFramebuffer.framebuffer = &framebuffer;
setFramebuffer.renderpass = &renderPass;

View File

@ -62,39 +62,108 @@ namespace Nz
{
const GL::Context* context = GL::Context::GetCurrentContext();
StackArray<std::size_t> colorIndexes = NazaraStackArrayNoInit(std::size_t, m_maxColorBufferCount);
for (const auto& commandVariant : m_commands)
{
std::visit([&](auto&& command)
switch (commandVariant.index())
{
using T = std::decay_t<decltype(command)>;
#define NAZARA_OPENGL_COMMAND_CALLBACK(Command) \
case TypeListFind<CommandList, Command>: \
Execute(context, std::get<TypeListFind<CommandList, Command>>(commandVariant)); \
break;
if constexpr (std::is_same_v<T, BeginDebugRegionData>)
NAZARA_OPENGL_FOREACH_COMMANDS(NAZARA_OPENGL_COMMAND_CALLBACK, NAZARA_OPENGL_COMMAND_CALLBACK)
#undef NAZARA_OPENGL_COMMAND_CALLBACK
}
}
}
void OpenGLCommandBuffer::UpdateDebugName(std::string_view /*name*/)
{
// No OpenGL object to name
}
void OpenGLCommandBuffer::ApplyBindings(const GL::Context& context, const ShaderBindings& states)
{
unsigned int setIndex = 0;
for (const auto& [pipelineLayout, shaderBinding] : states.shaderBindings)
{
if (shaderBinding)
shaderBinding->Apply(*pipelineLayout, setIndex, context);
else
NazaraWarning("no shader binding for set #" + std::to_string(setIndex));
setIndex++;
}
}
void OpenGLCommandBuffer::ApplyStates(const GL::Context& context, const DrawStates& states)
{
states.pipeline->Apply(context, states.shouldFlipY);
if (states.scissorRegion)
context.SetScissorBox(states.scissorRegion->x, states.scissorRegion->y, states.scissorRegion->width, states.scissorRegion->height);
if (states.viewportRegion)
context.SetViewport(states.viewportRegion->x, states.viewportRegion->y, states.viewportRegion->width, states.viewportRegion->height);
GL::OpenGLVaoSetup vaoSetup;
vaoSetup.indexBuffer = states.indexBuffer;
std::uint32_t locationIndex = 0;
const std::uint8_t* originPtr = 0;
for (const auto& bufferData : states.pipeline->GetPipelineInfo().vertexBuffers)
{
assert(bufferData.binding < states.vertexBuffers.size());
const auto& vertexBufferInfo = states.vertexBuffers[bufferData.binding];
GLsizei stride = GLsizei(bufferData.declaration->GetStride());
for (const auto& componentInfo : bufferData.declaration->GetComponents())
{
auto& bufferAttribute = vaoSetup.vertexAttribs[locationIndex++].emplace();
BuildAttrib(bufferAttribute, componentInfo.type);
bufferAttribute.pointer = originPtr + vertexBufferInfo.offset + componentInfo.offset;
bufferAttribute.stride = stride;
bufferAttribute.vertexBuffer = vertexBufferInfo.vertexBuffer;
}
}
const GL::VertexArray& vao = context.GetVaoCache().Get(vaoSetup);
context.BindVertexArray(vao.GetObjectId());
}
inline void OpenGLCommandBuffer::Execute(const GL::Context* context, const BeginDebugRegionCommand& command)
{
if (context->glPushDebugGroup)
context->glPushDebugGroup(GL_DEBUG_SOURCE_APPLICATION, 0, GLsizei(command.regionName.size()), command.regionName.data());
}
else if constexpr (std::is_same_v<T, BlitTextureData>)
inline void OpenGLCommandBuffer::Execute(const GL::Context* context, const BlitTextureCommand& command)
{
context->BlitTexture(*command.source, *command.target, command.sourceBox, command.targetBox, command.filter);
}
else if constexpr (std::is_same_v<T, CopyBufferData>)
inline void OpenGLCommandBuffer::Execute(const GL::Context* context, const CopyBufferCommand& command)
{
context->BindBuffer(GL::BufferTarget::CopyRead, command.source);
context->BindBuffer(GL::BufferTarget::CopyWrite, command.target);
context->glCopyBufferSubData(GL_COPY_READ_BUFFER, GL_COPY_WRITE_BUFFER, command.sourceOffset, command.targetOffset, command.size);
}
else if constexpr (std::is_same_v<T, CopyBufferFromMemoryData>)
inline void OpenGLCommandBuffer::Execute(const GL::Context* context, const CopyBufferFromMemoryCommand& command)
{
context->BindBuffer(GL::BufferTarget::CopyWrite, command.target);
context->glBufferSubData(GL_COPY_WRITE_BUFFER, command.targetOffset, command.size, command.memory);
}
else if constexpr (std::is_same_v<T, CopyTextureData>)
inline void OpenGLCommandBuffer::Execute(const GL::Context* context, const CopyTextureCommand& command)
{
context->CopyTexture(*command.source, *command.target, command.sourceBox, command.targetPoint);
}
else if constexpr (std::is_same_v<T, DispatchData>)
inline void OpenGLCommandBuffer::Execute(const GL::Context* context, const DispatchCommand& command)
{
if (!context->glDispatchCompute)
throw std::runtime_error("compute shaders are not supported on this device");
@ -103,13 +172,15 @@ namespace Nz
ApplyBindings(*context, command.bindings);
context->glDispatchCompute(command.numGroupsX, command.numGroupsY, command.numGroupsZ);
}
else if constexpr (std::is_same_v<T, DrawData>)
inline void OpenGLCommandBuffer::Execute(const GL::Context* context, const DrawCommand& command)
{
ApplyStates(*context, command.states);
ApplyBindings(*context, command.bindings);
context->glDrawArraysInstanced(ToOpenGL(command.states.pipeline->GetPipelineInfo().primitiveMode), command.firstVertex, command.vertexCount, command.instanceCount);
}
else if constexpr (std::is_same_v<T, DrawIndexedData>)
inline void OpenGLCommandBuffer::Execute(const GL::Context* context, const DrawIndexedCommand& command)
{
const UInt8* origin = 0; //< For an easy way to cast an integer to a pointer
origin += command.states.indexBufferOffset;
@ -125,21 +196,24 @@ namespace Nz
ApplyBindings(*context, command.bindings);
context->glDrawElementsInstanced(ToOpenGL(command.states.pipeline->GetPipelineInfo().primitiveMode), command.indexCount, ToOpenGL(command.states.indexBufferType), origin, command.instanceCount);
}
else if constexpr (std::is_same_v<T, EndDebugRegionData>)
inline void OpenGLCommandBuffer::Execute(const GL::Context* context, const EndDebugRegionCommand& command)
{
if (context->glPopDebugGroup)
context->glPopDebugGroup();
}
else if constexpr (std::is_same_v<T, MemoryBarrier>)
inline void OpenGLCommandBuffer::Execute(const GL::Context* context, const MemoryBarrier& command)
{
if (context->glMemoryBarrier)
context->glMemoryBarrier(command.barriers);
}
else if constexpr (std::is_same_v<T, SetFrameBufferData>)
inline void OpenGLCommandBuffer::Execute(const GL::Context*& context, const SetFrameBufferCommand& command)
{
command.framebuffer->Activate();
context = GL::Context::GetCurrentContext();
StackArray<std::size_t> colorIndexes = NazaraStackArrayNoInit(std::size_t, m_maxColorBufferCount);
std::size_t colorBufferCount = command.framebuffer->GetColorBufferCount();
assert(colorBufferCount <= colorIndexes.size());
@ -256,6 +330,9 @@ namespace Nz
{
assert(command.framebuffer->GetType() == FramebufferType::Window);
// Window framebuffers have their own contexts
context = GL::Context::GetCurrentContext();
// glDrawBuffers doesn't accept GL_BACK on OpenGL non-ES, and glDrawBuffer must be used instead
if (context->GetParams().type != GL::ContextType::OpenGL_ES && context->glDrawBuffer)
context->glDrawBuffer(GL_BACK);
@ -325,69 +402,6 @@ namespace Nz
if (!invalidateAttachments.empty())
context->glInvalidateFramebuffer(GL_FRAMEBUFFER, GLsizei(invalidateAttachments.size()), invalidateAttachments.data());
}
else
static_assert(AlwaysFalse<T>::value, "non-exhaustive visitor");
}, commandVariant);
}
}
void OpenGLCommandBuffer::UpdateDebugName(std::string_view /*name*/)
{
// No OpenGL object to name
}
void OpenGLCommandBuffer::ApplyBindings(const GL::Context& context, const ShaderBindings& states)
{
unsigned int setIndex = 0;
for (const auto& [pipelineLayout, shaderBinding] : states.shaderBindings)
{
if (shaderBinding)
shaderBinding->Apply(*pipelineLayout, setIndex, context);
else
NazaraWarning("no shader binding for set #" + std::to_string(setIndex));
setIndex++;
}
}
void OpenGLCommandBuffer::ApplyStates(const GL::Context& context, const DrawStates& states)
{
states.pipeline->Apply(context, states.shouldFlipY);
if (states.scissorRegion)
context.SetScissorBox(states.scissorRegion->x, states.scissorRegion->y, states.scissorRegion->width, states.scissorRegion->height);
if (states.viewportRegion)
context.SetViewport(states.viewportRegion->x, states.viewportRegion->y, states.viewportRegion->width, states.viewportRegion->height);
GL::OpenGLVaoSetup vaoSetup;
vaoSetup.indexBuffer = states.indexBuffer;
std::uint32_t locationIndex = 0;
const std::uint8_t* originPtr = 0;
for (const auto& bufferData : states.pipeline->GetPipelineInfo().vertexBuffers)
{
assert(bufferData.binding < states.vertexBuffers.size());
const auto& vertexBufferInfo = states.vertexBuffers[bufferData.binding];
GLsizei stride = GLsizei(bufferData.declaration->GetStride());
for (const auto& componentInfo : bufferData.declaration->GetComponents())
{
auto& bufferAttribute = vaoSetup.vertexAttribs[locationIndex++].emplace();
BuildAttrib(bufferAttribute, componentInfo.type);
bufferAttribute.pointer = originPtr + vertexBufferInfo.offset + componentInfo.offset;
bufferAttribute.stride = stride;
bufferAttribute.vertexBuffer = vertexBufferInfo.vertexBuffer;
}
}
const GL::VertexArray& vao = context.GetVaoCache().Get(vaoSetup);
context.BindVertexArray(vao.GetObjectId());
}
void OpenGLCommandBuffer::Release()
{