diff --git a/include/Nazara/OpenGLRenderer/OpenGLCommandBuffer.hpp b/include/Nazara/OpenGLRenderer/OpenGLCommandBuffer.hpp index b0c3777a6..31a5c0867 100644 --- a/include/Nazara/OpenGLRenderer/OpenGLCommandBuffer.hpp +++ b/include/Nazara/OpenGLRenderer/OpenGLCommandBuffer.hpp @@ -8,6 +8,7 @@ #define NAZARA_OPENGLRENDERER_OPENGLCOMMANDBUFFER_HPP #include +#include #include #include #include @@ -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> 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 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; ComputeStates m_currentComputeStates; DrawStates m_currentDrawStates; diff --git a/include/Nazara/OpenGLRenderer/OpenGLCommandBuffer.inl b/include/Nazara/OpenGLRenderer/OpenGLCommandBuffer.inl index 9ed6f891a..8bf7f602b 100644 --- a/include/Nazara/OpenGLRenderer/OpenGLCommandBuffer.inl +++ b/include/Nazara/OpenGLRenderer/OpenGLCommandBuffer.inl @@ -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(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; diff --git a/src/Nazara/OpenGLRenderer/OpenGLCommandBuffer.cpp b/src/Nazara/OpenGLRenderer/OpenGLCommandBuffer.cpp index 8133fe238..7a49f59ff 100644 --- a/src/Nazara/OpenGLRenderer/OpenGLCommandBuffer.cpp +++ b/src/Nazara/OpenGLRenderer/OpenGLCommandBuffer.cpp @@ -62,273 +62,18 @@ namespace Nz { const GL::Context* context = GL::Context::GetCurrentContext(); - StackArray 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; +#define NAZARA_OPENGL_COMMAND_CALLBACK(Command) \ + case TypeListFind: \ + Execute(context, std::get>(commandVariant)); \ + break; - if constexpr (std::is_same_v) - { - if (context->glPushDebugGroup) - context->glPushDebugGroup(GL_DEBUG_SOURCE_APPLICATION, 0, GLsizei(command.regionName.size()), command.regionName.data()); - } - else if constexpr (std::is_same_v) - { - context->BlitTexture(*command.source, *command.target, command.sourceBox, command.targetBox, command.filter); - } - else if constexpr (std::is_same_v) - { - 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) - { - 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) - { - context->CopyTexture(*command.source, *command.target, command.sourceBox, command.targetPoint); - } - else if constexpr (std::is_same_v) - { - if (!context->glDispatchCompute) - throw std::runtime_error("compute shaders are not supported on this device"); - - command.states.pipeline->Apply(*context); - ApplyBindings(*context, command.bindings); - context->glDispatchCompute(command.numGroupsX, command.numGroupsY, command.numGroupsZ); - } - else if constexpr (std::is_same_v) - { - 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) - { - const UInt8* origin = 0; //< For an easy way to cast an integer to a pointer - origin += command.states.indexBufferOffset; - - switch (command.states.indexBufferType) - { - case IndexType::U8: origin += command.firstIndex * sizeof(UInt8); break; - case IndexType::U16: origin += command.firstIndex * sizeof(UInt16); break; - case IndexType::U32: origin += command.firstIndex * sizeof(UInt32); break; - } - - ApplyStates(*context, command.states); - 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) - { - if (context->glPopDebugGroup) - context->glPopDebugGroup(); - } - else if constexpr (std::is_same_v) - { - if (context->glMemoryBarrier) - context->glMemoryBarrier(command.barriers); - } - else if constexpr (std::is_same_v) - { - command.framebuffer->Activate(); - - context = GL::Context::GetCurrentContext(); - - std::size_t colorBufferCount = command.framebuffer->GetColorBufferCount(); - assert(colorBufferCount <= colorIndexes.size()); - - colorIndexes.fill(0); - std::size_t colorIndex = 0; - - GLbitfield clearFields = 0; - std::optional depthStencilIndex; - - std::size_t attachmentCount = command.renderpass->GetAttachmentCount(); - - for (std::size_t i = 0; i < attachmentCount; ++i) - { - const auto& attachmentInfo = command.renderpass->GetAttachment(i); - switch (PixelFormatInfo::GetContent(attachmentInfo.format)) - { - case PixelFormatContent::Undefined: - break; - - case PixelFormatContent::ColorRGBA: - colorIndexes[colorIndex++] = i; - break; - - case PixelFormatContent::Depth: - if (!depthStencilIndex) - depthStencilIndex = i; - break; - - case PixelFormatContent::DepthStencil: - if (!depthStencilIndex) - depthStencilIndex = i; - break; - - case PixelFormatContent::Stencil: - //FIXME: I'm not sure stencil is properly handled here - if (!depthStencilIndex) - depthStencilIndex = i; - break; - } - } - - StackVector invalidateAttachments; - - if (command.framebuffer->GetType() == FramebufferType::Texture) - { - const OpenGLFboFramebuffer& fboFramebuffer = static_cast(*command.framebuffer); - - invalidateAttachments = NazaraStackVector(GLenum, colorBufferCount + 1); - - for (std::size_t i = 0; i < colorBufferCount; ++i) - { - std::size_t attachmentIndex = colorIndexes[i]; - - Color color = command.clearValues[attachmentIndex].color; - std::array clearColor = { color.r, color.g, color.b, color.a }; - - const auto& attachmentInfo = command.renderpass->GetAttachment(attachmentIndex); - if (attachmentInfo.loadOp == AttachmentLoadOp::Clear) - { - context->ResetColorWriteMasks(); - - // Reset scissor as it affects clear commands if enabled (disabling it would work too but it seems more expansive) - const Vector2ui& attachmentSize = fboFramebuffer.GetAttachmentSize(i); - context->SetScissorBox(0, 0, attachmentSize.x, attachmentSize.y); - - context->glClearBufferfv(GL_COLOR, GLint(i), clearColor.data()); - } - else if (attachmentInfo.loadOp == AttachmentLoadOp::Discard) - invalidateAttachments.push_back(static_cast(GL_COLOR_ATTACHMENT0 + i)); - } - - if (depthStencilIndex) - { - std::size_t attachmentIndex = *depthStencilIndex; - const auto& clearValues = command.clearValues[attachmentIndex]; - - const auto& depthStencilAttachment = command.renderpass->GetAttachment(attachmentIndex); - - // Reset scissor as it affects clear commands if enabled (disabling it would work too but it seems more expansive) - if (depthStencilAttachment.loadOp == AttachmentLoadOp::Clear || depthStencilAttachment.stencilLoadOp == AttachmentLoadOp::Clear) - { - const Vector2ui& attachmentSize = fboFramebuffer.GetAttachmentSize(attachmentIndex); - context->SetScissorBox(0, 0, attachmentSize.x, attachmentSize.y); - } - - if (depthStencilAttachment.loadOp == AttachmentLoadOp::Clear && depthStencilAttachment.stencilLoadOp == AttachmentLoadOp::Clear) - { - context->ResetDepthWriteMasks(); - context->ResetStencilWriteMasks(); - context->glClearBufferfi(GL_DEPTH_STENCIL, 0, clearValues.depth, clearValues.stencil); - } - else if (depthStencilAttachment.loadOp == AttachmentLoadOp::Clear) - { - context->ResetDepthWriteMasks(); - context->glClearBufferfv(GL_DEPTH, 0, &clearValues.depth); - - if (depthStencilAttachment.stencilLoadOp == AttachmentLoadOp::Discard) - invalidateAttachments.push_back(GL_STENCIL_ATTACHMENT); - } - else if (depthStencilAttachment.stencilLoadOp == AttachmentLoadOp::Clear) - { - context->ResetStencilWriteMasks(); - context->glClearBufferuiv(GL_STENCIL, 0, &clearValues.stencil); - - if (depthStencilAttachment.loadOp == AttachmentLoadOp::Discard) - invalidateAttachments.push_back(GL_DEPTH_ATTACHMENT); - } - else if (depthStencilAttachment.loadOp == AttachmentLoadOp::Discard && depthStencilAttachment.stencilLoadOp == AttachmentLoadOp::Discard) - invalidateAttachments.push_back(GL_DEPTH_STENCIL_ATTACHMENT); - } - } - else - { - assert(command.framebuffer->GetType() == FramebufferType::Window); - - // 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); - else - { - GLenum buffer = GL_BACK; - context->glDrawBuffers(1, &buffer); - } - - invalidateAttachments = NazaraStackVector(GLenum, 3); //< color + depth + stencil - - if (colorIndex > 0) - { - assert(colorBufferCount <= 1); - - std::size_t colorAttachmentIndex = colorIndexes.front(); - - const auto& colorAttachment = command.renderpass->GetAttachment(colorAttachmentIndex); - if (colorAttachment.loadOp == AttachmentLoadOp::Clear) - { - context->ResetColorWriteMasks(); - - const Color& color = command.clearValues[colorAttachmentIndex].color; - context->glClearColor(color.r, color.g, color.b, color.a); - - clearFields |= GL_COLOR_BUFFER_BIT; - } - else if (colorAttachment.loadOp == AttachmentLoadOp::Discard) - invalidateAttachments.push_back(GL_COLOR); - } - - if (depthStencilIndex) - { - std::size_t attachmentIndex = *depthStencilIndex; - const auto& clearValues = command.clearValues[attachmentIndex]; - - const auto& depthStencilAttachment = command.renderpass->GetAttachment(attachmentIndex); - if (depthStencilAttachment.loadOp == AttachmentLoadOp::Clear) - { - context->ResetDepthWriteMasks(); - context->glClearDepthf(clearValues.depth); - clearFields |= GL_DEPTH_BUFFER_BIT; - } - else if (depthStencilAttachment.loadOp == AttachmentLoadOp::Discard) - invalidateAttachments.push_back(GL_DEPTH); - - if (depthStencilAttachment.stencilLoadOp == AttachmentLoadOp::Clear && PixelFormatInfo::GetContent(depthStencilAttachment.format) == PixelFormatContent::DepthStencil) - { - context->ResetStencilWriteMasks(); - context->glClearStencil(clearValues.stencil); - clearFields |= GL_STENCIL_BUFFER_BIT; - } - else if (depthStencilAttachment.stencilLoadOp == AttachmentLoadOp::Discard) - invalidateAttachments.push_back(GL_STENCIL); - } - - if (clearFields) - { - // Reset scissor as it affects clear commands if enabled (disabling it would work too but it seems more expansive) - const Vector2ui& size = command.framebuffer->GetSize(); - context->SetScissorBox(0, 0, size.x, size.y); - - context->glClear(clearFields); - } - } - - if (!invalidateAttachments.empty()) - context->glInvalidateFramebuffer(GL_FRAMEBUFFER, GLsizei(invalidateAttachments.size()), invalidateAttachments.data()); - } - else - static_assert(AlwaysFalse::value, "non-exhaustive visitor"); - - }, commandVariant); + NAZARA_OPENGL_FOREACH_COMMANDS(NAZARA_OPENGL_COMMAND_CALLBACK, NAZARA_OPENGL_COMMAND_CALLBACK) +#undef NAZARA_OPENGL_COMMAND_CALLBACK + } } } @@ -389,6 +134,275 @@ namespace Nz 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()); + } + + inline void OpenGLCommandBuffer::Execute(const GL::Context* context, const BlitTextureCommand& command) + { + context->BlitTexture(*command.source, *command.target, command.sourceBox, command.targetBox, command.filter); + } + + 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); + } + + 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); + } + + inline void OpenGLCommandBuffer::Execute(const GL::Context* context, const CopyTextureCommand& command) + { + context->CopyTexture(*command.source, *command.target, command.sourceBox, command.targetPoint); + } + + 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"); + + command.states.pipeline->Apply(*context); + ApplyBindings(*context, command.bindings); + context->glDispatchCompute(command.numGroupsX, command.numGroupsY, command.numGroupsZ); + } + + 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); + } + + 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; + + switch (command.states.indexBufferType) + { + case IndexType::U8: origin += command.firstIndex * sizeof(UInt8); break; + case IndexType::U16: origin += command.firstIndex * sizeof(UInt16); break; + case IndexType::U32: origin += command.firstIndex * sizeof(UInt32); break; + } + + ApplyStates(*context, command.states); + ApplyBindings(*context, command.bindings); + context->glDrawElementsInstanced(ToOpenGL(command.states.pipeline->GetPipelineInfo().primitiveMode), command.indexCount, ToOpenGL(command.states.indexBufferType), origin, command.instanceCount); + } + + inline void OpenGLCommandBuffer::Execute(const GL::Context* context, const EndDebugRegionCommand& command) + { + if (context->glPopDebugGroup) + context->glPopDebugGroup(); + } + + inline void OpenGLCommandBuffer::Execute(const GL::Context* context, const MemoryBarrier& command) + { + if (context->glMemoryBarrier) + context->glMemoryBarrier(command.barriers); + } + + inline void OpenGLCommandBuffer::Execute(const GL::Context*& context, const SetFrameBufferCommand& command) + { + command.framebuffer->Activate(); + + StackArray colorIndexes = NazaraStackArrayNoInit(std::size_t, m_maxColorBufferCount); + + std::size_t colorBufferCount = command.framebuffer->GetColorBufferCount(); + assert(colorBufferCount <= colorIndexes.size()); + + colorIndexes.fill(0); + std::size_t colorIndex = 0; + + GLbitfield clearFields = 0; + std::optional depthStencilIndex; + + std::size_t attachmentCount = command.renderpass->GetAttachmentCount(); + + for (std::size_t i = 0; i < attachmentCount; ++i) + { + const auto& attachmentInfo = command.renderpass->GetAttachment(i); + switch (PixelFormatInfo::GetContent(attachmentInfo.format)) + { + case PixelFormatContent::Undefined: + break; + + case PixelFormatContent::ColorRGBA: + colorIndexes[colorIndex++] = i; + break; + + case PixelFormatContent::Depth: + if (!depthStencilIndex) + depthStencilIndex = i; + break; + + case PixelFormatContent::DepthStencil: + if (!depthStencilIndex) + depthStencilIndex = i; + break; + + case PixelFormatContent::Stencil: + //FIXME: I'm not sure stencil is properly handled here + if (!depthStencilIndex) + depthStencilIndex = i; + break; + } + } + + StackVector invalidateAttachments; + + if (command.framebuffer->GetType() == FramebufferType::Texture) + { + const OpenGLFboFramebuffer& fboFramebuffer = static_cast(*command.framebuffer); + + invalidateAttachments = NazaraStackVector(GLenum, colorBufferCount + 1); + + for (std::size_t i = 0; i < colorBufferCount; ++i) + { + std::size_t attachmentIndex = colorIndexes[i]; + + Color color = command.clearValues[attachmentIndex].color; + std::array clearColor = { color.r, color.g, color.b, color.a }; + + const auto& attachmentInfo = command.renderpass->GetAttachment(attachmentIndex); + if (attachmentInfo.loadOp == AttachmentLoadOp::Clear) + { + context->ResetColorWriteMasks(); + + // Reset scissor as it affects clear commands if enabled (disabling it would work too but it seems more expansive) + const Vector2ui& attachmentSize = fboFramebuffer.GetAttachmentSize(i); + context->SetScissorBox(0, 0, attachmentSize.x, attachmentSize.y); + + context->glClearBufferfv(GL_COLOR, GLint(i), clearColor.data()); + } + else if (attachmentInfo.loadOp == AttachmentLoadOp::Discard) + invalidateAttachments.push_back(static_cast(GL_COLOR_ATTACHMENT0 + i)); + } + + if (depthStencilIndex) + { + std::size_t attachmentIndex = *depthStencilIndex; + const auto& clearValues = command.clearValues[attachmentIndex]; + + const auto& depthStencilAttachment = command.renderpass->GetAttachment(attachmentIndex); + + // Reset scissor as it affects clear commands if enabled (disabling it would work too but it seems more expansive) + if (depthStencilAttachment.loadOp == AttachmentLoadOp::Clear || depthStencilAttachment.stencilLoadOp == AttachmentLoadOp::Clear) + { + const Vector2ui& attachmentSize = fboFramebuffer.GetAttachmentSize(attachmentIndex); + context->SetScissorBox(0, 0, attachmentSize.x, attachmentSize.y); + } + + if (depthStencilAttachment.loadOp == AttachmentLoadOp::Clear && depthStencilAttachment.stencilLoadOp == AttachmentLoadOp::Clear) + { + context->ResetDepthWriteMasks(); + context->ResetStencilWriteMasks(); + context->glClearBufferfi(GL_DEPTH_STENCIL, 0, clearValues.depth, clearValues.stencil); + } + else if (depthStencilAttachment.loadOp == AttachmentLoadOp::Clear) + { + context->ResetDepthWriteMasks(); + context->glClearBufferfv(GL_DEPTH, 0, &clearValues.depth); + + if (depthStencilAttachment.stencilLoadOp == AttachmentLoadOp::Discard) + invalidateAttachments.push_back(GL_STENCIL_ATTACHMENT); + } + else if (depthStencilAttachment.stencilLoadOp == AttachmentLoadOp::Clear) + { + context->ResetStencilWriteMasks(); + context->glClearBufferuiv(GL_STENCIL, 0, &clearValues.stencil); + + if (depthStencilAttachment.loadOp == AttachmentLoadOp::Discard) + invalidateAttachments.push_back(GL_DEPTH_ATTACHMENT); + } + else if (depthStencilAttachment.loadOp == AttachmentLoadOp::Discard && depthStencilAttachment.stencilLoadOp == AttachmentLoadOp::Discard) + invalidateAttachments.push_back(GL_DEPTH_STENCIL_ATTACHMENT); + } + } + else + { + 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); + else + { + GLenum buffer = GL_BACK; + context->glDrawBuffers(1, &buffer); + } + + invalidateAttachments = NazaraStackVector(GLenum, 3); //< color + depth + stencil + + if (colorIndex > 0) + { + assert(colorBufferCount <= 1); + + std::size_t colorAttachmentIndex = colorIndexes.front(); + + const auto& colorAttachment = command.renderpass->GetAttachment(colorAttachmentIndex); + if (colorAttachment.loadOp == AttachmentLoadOp::Clear) + { + context->ResetColorWriteMasks(); + + const Color& color = command.clearValues[colorAttachmentIndex].color; + context->glClearColor(color.r, color.g, color.b, color.a); + + clearFields |= GL_COLOR_BUFFER_BIT; + } + else if (colorAttachment.loadOp == AttachmentLoadOp::Discard) + invalidateAttachments.push_back(GL_COLOR); + } + + if (depthStencilIndex) + { + std::size_t attachmentIndex = *depthStencilIndex; + const auto& clearValues = command.clearValues[attachmentIndex]; + + const auto& depthStencilAttachment = command.renderpass->GetAttachment(attachmentIndex); + if (depthStencilAttachment.loadOp == AttachmentLoadOp::Clear) + { + context->ResetDepthWriteMasks(); + context->glClearDepthf(clearValues.depth); + clearFields |= GL_DEPTH_BUFFER_BIT; + } + else if (depthStencilAttachment.loadOp == AttachmentLoadOp::Discard) + invalidateAttachments.push_back(GL_DEPTH); + + if (depthStencilAttachment.stencilLoadOp == AttachmentLoadOp::Clear && PixelFormatInfo::GetContent(depthStencilAttachment.format) == PixelFormatContent::DepthStencil) + { + context->ResetStencilWriteMasks(); + context->glClearStencil(clearValues.stencil); + clearFields |= GL_STENCIL_BUFFER_BIT; + } + else if (depthStencilAttachment.stencilLoadOp == AttachmentLoadOp::Discard) + invalidateAttachments.push_back(GL_STENCIL); + } + + if (clearFields) + { + // Reset scissor as it affects clear commands if enabled (disabling it would work too but it seems more expansive) + const Vector2ui& size = command.framebuffer->GetSize(); + context->SetScissorBox(0, 0, size.x, size.y); + + context->glClear(clearFields); + } + } + + if (!invalidateAttachments.empty()) + context->glInvalidateFramebuffer(GL_FRAMEBUFFER, GLsizei(invalidateAttachments.size()), invalidateAttachments.data()); + } + void OpenGLCommandBuffer::Release() { assert(m_owner);