From 9578ba3ef569e19154606d2adf50de557f0a3617 Mon Sep 17 00:00:00 2001 From: SirLynix Date: Sat, 24 Dec 2022 11:54:55 +0100 Subject: [PATCH] Add initial support for compute pipelines --- examples/DeferredShading/main.cpp | 58 +++++++------- examples/RenderTest/main.cpp | 8 +- include/Nazara/Graphics/ShaderReflection.hpp | 14 +++- include/Nazara/Graphics/SubmeshRenderer.hpp | 2 +- include/Nazara/Graphics/Tilemap.hpp | 2 +- .../OpenGLRenderer/OpenGLCommandBuffer.hpp | 23 +++++- .../OpenGLRenderer/OpenGLCommandBuffer.inl | 57 +++++++++----- .../OpenGLCommandBufferBuilder.hpp | 5 +- .../OpenGLRenderer/OpenGLComputePipeline.hpp | 39 ++++++++++ .../OpenGLRenderer/OpenGLComputePipeline.inl | 16 ++++ .../Nazara/OpenGLRenderer/OpenGLDevice.hpp | 1 + .../OpenGLRenderPipelineLayout.hpp | 18 ++++- .../OpenGLRenderer/OpenGLShaderBinding.hpp | 2 +- include/Nazara/OpenGLRenderer/Utils.hpp | 1 + include/Nazara/OpenGLRenderer/Utils.inl | 14 ++++ .../Nazara/OpenGLRenderer/Wrapper/Context.hpp | 24 +++++- .../Nazara/OpenGLRenderer/Wrapper/Context.inl | 66 +++++++++++++++- .../OpenGLRenderer/Wrapper/ContextObject.inl | 2 +- .../OpenGLRenderer/Wrapper/CoreFunctions.hpp | 7 ++ .../Nazara/Renderer/CommandBufferBuilder.hpp | 6 +- include/Nazara/Renderer/ComputePipeline.hpp | 39 ++++++++++ include/Nazara/Renderer/ComputePipeline.inl | 13 ++++ include/Nazara/Renderer/Enums.hpp | 9 +++ include/Nazara/Renderer/RenderDevice.hpp | 2 + include/Nazara/Renderer/RenderDeviceInfo.hpp | 14 +++- include/Nazara/Renderer/ShaderBinding.hpp | 23 ++++-- include/Nazara/Utility/ImageStream.hpp | 1 - include/Nazara/VulkanRenderer/Utils.inl | 5 +- .../VulkanCommandBufferBuilder.hpp | 5 +- .../VulkanRenderer/VulkanComputePipeline.hpp | 45 +++++++++++ .../VulkanRenderer/VulkanComputePipeline.inl | 21 ++++++ .../Nazara/VulkanRenderer/VulkanDevice.hpp | 1 + .../VulkanRenderer/Wrapper/CommandBuffer.hpp | 2 + .../VulkanRenderer/Wrapper/CommandBuffer.inl | 5 ++ src/Nazara/Graphics/ForwardFramePipeline.cpp | 10 +-- src/Nazara/Graphics/Graphics.cpp | 7 +- src/Nazara/Graphics/MaterialInstance.cpp | 2 +- src/Nazara/Graphics/ShaderReflection.cpp | 33 ++++++-- src/Nazara/Graphics/SpriteChainRenderer.cpp | 4 +- src/Nazara/Graphics/SubmeshRenderer.cpp | 8 +- .../OpenGLRenderer/OpenGLCommandBuffer.cpp | 9 +++ .../OpenGLCommandBufferBuilder.cpp | 17 ++++- .../OpenGLRenderer/OpenGLComputePipeline.cpp | 75 +++++++++++++++++++ src/Nazara/OpenGLRenderer/OpenGLDevice.cpp | 71 +++++++++++------- .../OpenGLRenderer/OpenGLRenderPipeline.cpp | 2 +- .../OpenGLRenderPipelineLayout.cpp | 10 +++ .../OpenGLRenderer/OpenGLShaderBinding.cpp | 64 +++++++++++++--- src/Nazara/OpenGLRenderer/Wrapper/Context.cpp | 69 ++++++++++++----- src/Nazara/Renderer/ComputePipeline.cpp | 11 +++ src/Nazara/Renderer/DebugDrawer.cpp | 2 +- src/Nazara/Renderer/RenderDevice.cpp | 5 ++ src/Nazara/Utility/Formats/GIFLoader.cpp | 2 +- src/Nazara/VulkanRenderer/Vulkan.cpp | 9 +++ .../VulkanCommandBufferBuilder.cpp | 19 ++++- .../VulkanRenderer/VulkanComputePipeline.cpp | 61 +++++++++++++++ src/Nazara/VulkanRenderer/VulkanDevice.cpp | 10 +++ .../VulkanRenderer/VulkanShaderBinding.cpp | 47 +++++++----- 57 files changed, 915 insertions(+), 182 deletions(-) create mode 100644 include/Nazara/OpenGLRenderer/OpenGLComputePipeline.hpp create mode 100644 include/Nazara/OpenGLRenderer/OpenGLComputePipeline.inl create mode 100644 include/Nazara/Renderer/ComputePipeline.hpp create mode 100644 include/Nazara/Renderer/ComputePipeline.inl create mode 100644 include/Nazara/VulkanRenderer/VulkanComputePipeline.hpp create mode 100644 include/Nazara/VulkanRenderer/VulkanComputePipeline.inl create mode 100644 src/Nazara/OpenGLRenderer/OpenGLComputePipeline.cpp create mode 100644 src/Nazara/Renderer/ComputePipeline.cpp create mode 100644 src/Nazara/VulkanRenderer/VulkanComputePipeline.cpp diff --git a/examples/DeferredShading/main.cpp b/examples/DeferredShading/main.cpp index 862ed227d..2db636cc6 100644 --- a/examples/DeferredShading/main.cpp +++ b/examples/DeferredShading/main.cpp @@ -151,7 +151,7 @@ int main() textureBinding.setIndex = 0; textureBinding.bindingIndex = 1; textureBinding.shaderStageFlags = nzsl::ShaderStageType::Fragment; - textureBinding.type = Nz::ShaderBindingType::Texture; + textureBinding.type = Nz::ShaderBindingType::Sampler; std::shared_ptr skyboxPipelineLayout = device->InstantiateRenderPipelineLayout(std::move(skyboxPipelineLayoutInfo)); @@ -275,7 +275,7 @@ int main() 0, i + 1, 1, - Nz::ShaderBindingType::Texture, + Nz::ShaderBindingType::Sampler, nzsl::ShaderStageType::Fragment, }); } @@ -367,7 +367,7 @@ int main() fullscreenPipelineLayoutInfoViewer.bindings.push_back({ 0, 1, 1, - Nz::ShaderBindingType::Texture, + Nz::ShaderBindingType::Sampler, nzsl::ShaderStageType::Fragment, }); @@ -452,7 +452,7 @@ int main() bloomBlendPipelineLayoutInfo.bindings.push_back({ 0, 2, 1, - Nz::ShaderBindingType::Texture, + Nz::ShaderBindingType::Sampler, nzsl::ShaderStageType::Fragment, }); @@ -476,7 +476,7 @@ int main() fullscreenPipelineLayoutInfo.bindings.push_back({ 0, 0, 1, - Nz::ShaderBindingType::Texture, + Nz::ShaderBindingType::Sampler, nzsl::ShaderStageType::Fragment, }); @@ -504,7 +504,7 @@ int main() }, { 0, 2, 1, - Nz::ShaderBindingType::Texture, + Nz::ShaderBindingType::Sampler, nzsl::ShaderStageType::Fragment, }, } @@ -613,7 +613,7 @@ int main() }, { 1, - Nz::ShaderBinding::TextureBinding { + Nz::ShaderBinding::SampledTextureBinding { skyboxTexture.get(), textureSampler.get() } @@ -837,10 +837,10 @@ int main() { builder.BindShaderBinding(1, *lightingShaderBindings[i]); - builder.BindPipeline(*stencilPipeline); + builder.BindRenderPipeline(*stencilPipeline); builder.DrawIndexed(coneMeshGfx->GetIndexCount(0)); - builder.BindPipeline(*lightingPipeline); + builder.BindRenderPipeline(*lightingPipeline); builder.DrawIndexed(coneMeshGfx->GetIndexCount(0)); } }); @@ -862,7 +862,7 @@ int main() builder.BindIndexBuffer(*cubeMeshGfx->GetIndexBuffer(0), Nz::IndexType::U16); builder.BindVertexBuffer(0, *cubeMeshGfx->GetVertexBuffer(0)); - builder.BindPipeline(*skyboxPipeline); + builder.BindRenderPipeline(*skyboxPipeline); builder.DrawIndexed(Nz::SafeCast(cubeMeshGfx->GetIndexCount(0))); @@ -932,7 +932,7 @@ int main() builder.BindShaderBinding(0, *godRaysShaderBinding); - builder.BindPipeline(*godraysPipeline); + builder.BindRenderPipeline(*godraysPipeline); builder.Draw(3); }); @@ -948,7 +948,7 @@ int main() builder.BindShaderBinding(0, *bloomBrightShaderBinding); - builder.BindPipeline(*bloomBrightPipeline); + builder.BindRenderPipeline(*bloomBrightPipeline); builder.Draw(3); }); @@ -970,7 +970,7 @@ int main() builder.SetViewport(env.renderRect); builder.BindShaderBinding(0, *gaussianBlurShaderBinding[i * 2 + 0]); - builder.BindPipeline(*gaussianBlurPipeline); + builder.BindRenderPipeline(*gaussianBlurPipeline); builder.Draw(3); }); @@ -990,7 +990,7 @@ int main() builder.SetViewport(env.renderRect); builder.BindShaderBinding(0, *gaussianBlurShaderBinding[i * 2 + 1]); - builder.BindPipeline(*gaussianBlurPipeline); + builder.BindRenderPipeline(*gaussianBlurPipeline); builder.Draw(3); }); @@ -1011,7 +1011,7 @@ int main() builder.SetViewport(env.renderRect); // Blend bloom - builder.BindPipeline(*bloomBlendPipeline); + builder.BindRenderPipeline(*bloomBlendPipeline); for (std::size_t i = 0; i < BloomSubdivisionCount; ++i) { builder.BindShaderBinding(0, *bloomBlendShaderBinding[i]); @@ -1046,7 +1046,7 @@ int main() builder.SetViewport(env.renderRect); builder.BindShaderBinding(0, *toneMappingShaderBinding); - builder.BindPipeline(*toneMappingPipeline); + builder.BindRenderPipeline(*toneMappingPipeline); builder.Draw(3); }); @@ -1218,21 +1218,21 @@ int main() }, { 1, - Nz::ShaderBinding::TextureBinding { + Nz::ShaderBinding::SampledTextureBinding { bakedGraph.GetAttachmentTexture(colorTexture).get(), textureSampler.get() } }, { 2, - Nz::ShaderBinding::TextureBinding { + Nz::ShaderBinding::SampledTextureBinding { bakedGraph.GetAttachmentTexture(normalTexture).get(), textureSampler.get() } }, { 3, - Nz::ShaderBinding::TextureBinding { + Nz::ShaderBinding::SampledTextureBinding { bakedGraph.GetAttachmentTexture(positionTexture).get(), textureSampler.get() } @@ -1271,7 +1271,7 @@ int main() }, { 1, - Nz::ShaderBinding::TextureBinding { + Nz::ShaderBinding::SampledTextureBinding { bakedGraph.GetAttachmentTexture(lightOutput).get(), textureSampler.get() } @@ -1296,7 +1296,7 @@ int main() }, { 1, - Nz::ShaderBinding::TextureBinding { + Nz::ShaderBinding::SampledTextureBinding { bakedGraph.GetAttachmentTexture((i == 0 && j == 0) ? bloomBrightOutput : bloomTextures[bloomTextureIndex++]).get(), textureSampler.get() } @@ -1331,7 +1331,7 @@ int main() },*/ { 2, - Nz::ShaderBinding::TextureBinding { + Nz::ShaderBinding::SampledTextureBinding { bakedGraph.GetAttachmentTexture(bloomTextures[i * 2 + 1]).get(), textureSampler.get() } @@ -1345,7 +1345,7 @@ int main() bloomBlitBinding->Update({ { 0, - Nz::ShaderBinding::TextureBinding { + Nz::ShaderBinding::SampledTextureBinding { bakedGraph.GetAttachmentTexture(lightOutput).get(), textureSampler.get() } @@ -1358,7 +1358,7 @@ int main() bloomSkipBlit->Update({ { 0, - Nz::ShaderBinding::TextureBinding { + Nz::ShaderBinding::SampledTextureBinding { bakedGraph.GetAttachmentTexture(lightOutput).get(), textureSampler.get() } @@ -1385,7 +1385,7 @@ int main() }, { 2, - Nz::ShaderBinding::TextureBinding { + Nz::ShaderBinding::SampledTextureBinding { bakedGraph.GetAttachmentTexture(occluderTexture).get(), textureSampler.get() } @@ -1405,7 +1405,7 @@ int main() }, { 1, - Nz::ShaderBinding::TextureBinding { + Nz::ShaderBinding::SampledTextureBinding { bakedGraph.GetAttachmentTexture(bloomOutput).get(), textureSampler.get() } @@ -1432,7 +1432,7 @@ int main() },*/ { 2, - Nz::ShaderBinding::TextureBinding { + Nz::ShaderBinding::SampledTextureBinding { bakedGraph.GetAttachmentTexture(godRaysTexture).get(), textureSampler.get() } @@ -1445,7 +1445,7 @@ int main() finalBlitBinding->Update({ { 0, - Nz::ShaderBinding::TextureBinding { + Nz::ShaderBinding::SampledTextureBinding { bakedGraph.GetAttachmentTexture(toneMappingOutput).get(), textureSampler.get() } @@ -1548,7 +1548,7 @@ int main() builder.SetViewport(Nz::Recti{ 0, 0, int(windowSize.x), int(windowSize.y) }); builder.BindShaderBinding(0, *finalBlitBinding); - builder.BindPipeline(*fullscreenPipeline); + builder.BindRenderPipeline(*fullscreenPipeline); builder.Draw(3); } diff --git a/examples/RenderTest/main.cpp b/examples/RenderTest/main.cpp index 512177deb..13d5dbb51 100644 --- a/examples/RenderTest/main.cpp +++ b/examples/RenderTest/main.cpp @@ -200,7 +200,7 @@ int main() pipelineTextureBinding.setIndex = 1; pipelineTextureBinding.bindingIndex = 0; pipelineTextureBinding.shaderStageFlags = nzsl::ShaderStageType::Fragment; - pipelineTextureBinding.type = Nz::ShaderBindingType::Texture; + pipelineTextureBinding.type = Nz::ShaderBindingType::Sampler; std::shared_ptr renderPipelineLayout = device->InstantiateRenderPipelineLayout(std::move(pipelineLayoutInfo)); @@ -218,14 +218,14 @@ int main() } }); - Nz::ShaderBinding::TextureBinding textureBinding { + Nz::ShaderBinding::SampledTextureBinding textureBinding { texture.get(), textureSampler.get() }; textureShaderBinding->Update({ { 0, - Nz::ShaderBinding::TextureBindings { + Nz::ShaderBinding::SampledTextureBindings { 1, &textureBinding } } @@ -389,7 +389,7 @@ int main() builder.BeginRenderPass(windowRT->GetFramebuffer(frame.GetFramebufferIndex()), windowRT->GetRenderPass(), renderRect, { clearValues[0], clearValues[1] }); { builder.BindIndexBuffer(*renderBufferIB, Nz::IndexType::U16); - builder.BindPipeline(*pipeline); + builder.BindRenderPipeline(*pipeline); builder.BindVertexBuffer(0, *renderBufferVB); builder.BindShaderBinding(0, *viewerShaderBinding); builder.BindShaderBinding(1, *textureShaderBinding); diff --git a/include/Nazara/Graphics/ShaderReflection.hpp b/include/Nazara/Graphics/ShaderReflection.hpp index d66d3a445..3e26ce7d7 100644 --- a/include/Nazara/Graphics/ShaderReflection.hpp +++ b/include/Nazara/Graphics/ShaderReflection.hpp @@ -45,6 +45,13 @@ namespace Nz UInt32 bindingIndex; }; + struct ExternalSampler : ExternalData + { + UInt32 arraySize; + nzsl::ImageType imageType; + nzsl::Ast::PrimitiveType sampledType; + }; + struct ExternalStorageBlock : ExternalData { std::size_t structIndex; @@ -53,8 +60,10 @@ namespace Nz struct ExternalTexture : ExternalData { UInt32 arraySize; + nzsl::AccessPolicy accessPolicy; + nzsl::ImageFormat imageFormat; nzsl::ImageType imageType; - nzsl::Ast::PrimitiveType sampledType; + nzsl::Ast::PrimitiveType baseType; }; struct ExternalUniformBlock : ExternalData @@ -64,8 +73,9 @@ namespace Nz struct ExternalBlockData { + std::unordered_map samplers; std::unordered_map storageBlocks; - std::unordered_map samplers; + std::unordered_map textures; std::unordered_map uniformBlocks; }; diff --git a/include/Nazara/Graphics/SubmeshRenderer.hpp b/include/Nazara/Graphics/SubmeshRenderer.hpp index 7efcb7821..f80593638 100644 --- a/include/Nazara/Graphics/SubmeshRenderer.hpp +++ b/include/Nazara/Graphics/SubmeshRenderer.hpp @@ -33,7 +33,7 @@ namespace Nz private: std::vector m_bindingCache; - std::vector m_textureBindingCache; + std::vector m_textureBindingCache; RenderElementPool m_submeshPool; }; diff --git a/include/Nazara/Graphics/Tilemap.hpp b/include/Nazara/Graphics/Tilemap.hpp index 17f5b9b29..e215a3414 100644 --- a/include/Nazara/Graphics/Tilemap.hpp +++ b/include/Nazara/Graphics/Tilemap.hpp @@ -11,8 +11,8 @@ #include #include #include -#include #include +#include #include namespace Nz diff --git a/include/Nazara/OpenGLRenderer/OpenGLCommandBuffer.hpp b/include/Nazara/OpenGLRenderer/OpenGLCommandBuffer.hpp index 00e985305..8aa4fc537 100644 --- a/include/Nazara/OpenGLRenderer/OpenGLCommandBuffer.hpp +++ b/include/Nazara/OpenGLRenderer/OpenGLCommandBuffer.hpp @@ -23,6 +23,7 @@ namespace Nz { class OpenGLCommandPool; + class OpenGLComputePipeline; class OpenGLFramebuffer; class OpenGLRenderPass; class OpenGLTexture; @@ -38,8 +39,9 @@ namespace Nz inline void BeginDebugRegion(const std::string_view& regionName, const Color& color); + inline void BindComputePipeline(const OpenGLComputePipeline* pipeline); inline void BindIndexBuffer(GLuint indexBuffer, IndexType indexType, UInt64 offset = 0); - inline void BindPipeline(const OpenGLRenderPipeline* pipeline); + inline void BindRenderPipeline(const OpenGLRenderPipeline* pipeline); inline void BindShaderBinding(const OpenGLRenderPipelineLayout& pipelineLayout, UInt32 set, const OpenGLShaderBinding* binding); inline void BindVertexBuffer(UInt32 binding, GLuint vertexBuffer, UInt64 offset = 0); inline void BlitTexture(const OpenGLTexture& source, const Boxui& sourceBox, const OpenGLTexture& target, const Boxui& targetBox, SamplerFilter filter = SamplerFilter::Nearest); @@ -48,6 +50,8 @@ namespace Nz inline void CopyBuffer(const UploadPool::Allocation& allocation, GLuint target, UInt64 size, UInt64 sourceOffset = 0, UInt64 targetOffset = 0); inline void CopyTexture(const OpenGLTexture& source, const Boxui& sourceBox, const OpenGLTexture& target, const Vector3ui& targetPoint); + inline void Dispatch(UInt32 numGroupsX, UInt32 numGroupsY, UInt32 numGroupsZ); + inline void Draw(UInt32 vertexCount, UInt32 instanceCount = 1, UInt32 firstVertex = 0, UInt32 firstInstance = 0); inline void DrawIndexed(UInt32 indexCount, UInt32 instanceCount = 1, UInt32 firstIndex = 0, UInt32 firstInstance = 0); @@ -89,6 +93,11 @@ namespace Nz SamplerFilter filter; }; + struct ComputeStates + { + const OpenGLComputePipeline* pipeline = nullptr; + }; + struct CopyBufferData { GLuint source; @@ -114,6 +123,14 @@ namespace Nz UInt64 targetOffset; }; + struct DispatchData + { + ComputeStates states; + UInt32 numGroupsX; + UInt32 numGroupsY; + UInt32 numGroupsZ; + }; + struct DrawStates { struct VertexBuffer @@ -168,13 +185,15 @@ namespace Nz CopyBufferData, CopyBufferFromMemoryData, CopyTextureData, + DispatchData, DrawData, DrawIndexedData, EndDebugRegionData, SetFrameBufferData >; - DrawStates m_currentStates; + ComputeStates m_currentComputeStates; + DrawStates m_currentDrawStates; std::size_t m_bindingIndex; std::size_t m_maxColorBufferCount; std::size_t m_poolIndex; diff --git a/include/Nazara/OpenGLRenderer/OpenGLCommandBuffer.inl b/include/Nazara/OpenGLRenderer/OpenGLCommandBuffer.inl index 826d0b111..21344ead8 100644 --- a/include/Nazara/OpenGLRenderer/OpenGLCommandBuffer.inl +++ b/include/Nazara/OpenGLRenderer/OpenGLCommandBuffer.inl @@ -33,32 +33,37 @@ namespace Nz m_commands.emplace_back(std::move(beginDebugRegion)); } - inline void OpenGLCommandBuffer::BindIndexBuffer(GLuint indexBuffer, IndexType indexType, UInt64 offset) + inline void OpenGLCommandBuffer::BindComputePipeline(const OpenGLComputePipeline* pipeline) { - m_currentStates.indexBuffer = indexBuffer; - m_currentStates.indexBufferOffset = offset; - m_currentStates.indexBufferType = indexType; + m_currentComputeStates.pipeline = pipeline; } - inline void OpenGLCommandBuffer::BindPipeline(const OpenGLRenderPipeline* pipeline) + inline void OpenGLCommandBuffer::BindIndexBuffer(GLuint indexBuffer, IndexType indexType, UInt64 offset) { - m_currentStates.pipeline = pipeline; + m_currentDrawStates.indexBuffer = indexBuffer; + m_currentDrawStates.indexBufferOffset = offset; + m_currentDrawStates.indexBufferType = indexType; + } + + inline void OpenGLCommandBuffer::BindRenderPipeline(const OpenGLRenderPipeline* pipeline) + { + m_currentDrawStates.pipeline = pipeline; } inline void OpenGLCommandBuffer::BindShaderBinding(const OpenGLRenderPipelineLayout& pipelineLayout, UInt32 set, const OpenGLShaderBinding* binding) { - if (set >= m_currentStates.shaderBindings.size()) - m_currentStates.shaderBindings.resize(set + 1); + if (set >= m_currentDrawStates.shaderBindings.size()) + m_currentDrawStates.shaderBindings.resize(set + 1); - m_currentStates.shaderBindings[set] = std::make_pair(&pipelineLayout, binding); + m_currentDrawStates.shaderBindings[set] = std::make_pair(&pipelineLayout, binding); } inline void OpenGLCommandBuffer::BindVertexBuffer(UInt32 binding, GLuint vertexBuffer, UInt64 offset) { - if (binding >= m_currentStates.vertexBuffers.size()) - m_currentStates.vertexBuffers.resize(binding + 1); + if (binding >= m_currentDrawStates.vertexBuffers.size()) + m_currentDrawStates.vertexBuffers.resize(binding + 1); - auto& vertexBufferData = m_currentStates.vertexBuffers[binding]; + auto& vertexBufferData = m_currentDrawStates.vertexBuffers[binding]; vertexBufferData.offset = offset; vertexBufferData.vertexBuffer = vertexBuffer; } @@ -113,13 +118,27 @@ namespace Nz m_commands.emplace_back(std::move(copyTexture)); } + inline void OpenGLCommandBuffer::Dispatch(UInt32 numGroupsX, UInt32 numGroupsY, UInt32 numGroupsZ) + { + if (!m_currentComputeStates.pipeline) + throw std::runtime_error("no pipeline bound"); + + DispatchData dispatch; + dispatch.states = m_currentComputeStates; + dispatch.numGroupsX = numGroupsX; + dispatch.numGroupsY = numGroupsY; + dispatch.numGroupsZ = numGroupsZ; + + m_commands.emplace_back(std::move(dispatch)); + } + inline void OpenGLCommandBuffer::Draw(UInt32 vertexCount, UInt32 instanceCount, UInt32 firstVertex, UInt32 firstInstance) { - if (!m_currentStates.pipeline) + if (!m_currentDrawStates.pipeline) throw std::runtime_error("no pipeline bound"); DrawData draw; - draw.states = m_currentStates; + draw.states = m_currentDrawStates; draw.firstInstance = firstInstance; draw.firstVertex = firstVertex; draw.instanceCount = instanceCount; @@ -130,11 +149,11 @@ namespace Nz inline void OpenGLCommandBuffer::DrawIndexed(UInt32 indexCount, UInt32 instanceCount, UInt32 firstIndex, UInt32 firstInstance) { - if (!m_currentStates.pipeline) + if (!m_currentDrawStates.pipeline) throw std::runtime_error("no pipeline bound"); DrawIndexedData draw; - draw.states = m_currentStates; + draw.states = m_currentDrawStates; draw.firstIndex = firstIndex; draw.firstInstance = firstInstance; draw.indexCount = indexCount; @@ -177,17 +196,17 @@ namespace Nz m_commands.emplace_back(std::move(setFramebuffer)); - m_currentStates.shouldFlipY = (framebuffer.GetType() == FramebufferType::Window); + m_currentDrawStates.shouldFlipY = (framebuffer.GetType() == FramebufferType::Window); } inline void OpenGLCommandBuffer::SetScissor(const Recti& scissorRegion) { - m_currentStates.scissorRegion = scissorRegion; + m_currentDrawStates.scissorRegion = scissorRegion; } inline void OpenGLCommandBuffer::SetViewport(const Recti& viewportRegion) { - m_currentStates.viewportRegion = viewportRegion; + m_currentDrawStates.viewportRegion = viewportRegion; } } diff --git a/include/Nazara/OpenGLRenderer/OpenGLCommandBufferBuilder.hpp b/include/Nazara/OpenGLRenderer/OpenGLCommandBufferBuilder.hpp index 2c5355d28..abe22627f 100644 --- a/include/Nazara/OpenGLRenderer/OpenGLCommandBufferBuilder.hpp +++ b/include/Nazara/OpenGLRenderer/OpenGLCommandBufferBuilder.hpp @@ -27,8 +27,9 @@ namespace Nz void BeginDebugRegion(const std::string_view& regionName, const Color& color) override; void BeginRenderPass(const Framebuffer& framebuffer, const RenderPass& renderPass, const Recti& renderRect, const ClearValues* clearValues, std::size_t clearValueCount) override; + void BindComputePipeline(const ComputePipeline& pipeline) override; void BindIndexBuffer(const RenderBuffer& indexBuffer, IndexType indexType, UInt64 offset = 0) override; - void BindPipeline(const RenderPipeline& pipeline) override; + void BindRenderPipeline(const RenderPipeline& pipeline) override; void BindShaderBinding(UInt32 set, const ShaderBinding& binding) override; void BindShaderBinding(const RenderPipelineLayout& pipelineLayout, UInt32 set, const ShaderBinding& binding) override; void BindVertexBuffer(UInt32 binding, const RenderBuffer& vertexBuffer, UInt64 offset = 0) override; @@ -39,6 +40,8 @@ namespace Nz void CopyBuffer(const UploadPool::Allocation& allocation, const RenderBufferView& target, UInt64 size, UInt64 sourceOffset = 0, UInt64 targetOffset = 0) override; void CopyTexture(const Texture& fromTexture, const Boxui& fromBox, TextureLayout fromLayout, const Texture& toTexture, const Vector3ui& toPos, TextureLayout toLayout) override; + void Dispatch(UInt32 workgroupX, UInt32 workgroupY, UInt32 workgroupZ) override; + void Draw(UInt32 vertexCount, UInt32 instanceCount = 1, UInt32 firstVertex = 0, UInt32 firstInstance = 0) override; void DrawIndexed(UInt32 indexCount, UInt32 instanceCount = 1, UInt32 firstIndex = 0, UInt32 firstInstance = 0) override; diff --git a/include/Nazara/OpenGLRenderer/OpenGLComputePipeline.hpp b/include/Nazara/OpenGLRenderer/OpenGLComputePipeline.hpp new file mode 100644 index 000000000..806dec697 --- /dev/null +++ b/include/Nazara/OpenGLRenderer/OpenGLComputePipeline.hpp @@ -0,0 +1,39 @@ +// Copyright (C) 2022 Jérôme "Lynix" Leclercq (lynix680@gmail.com) +// This file is part of the "Nazara Engine - OpenGL renderer" +// For conditions of distribution and use, see copyright notice in Config.hpp + +#pragma once + +#ifndef NAZARA_OPENGLRENDERER_OPENGLCOMPUTEPIPELINE_HPP +#define NAZARA_OPENGLRENDERER_OPENGLCOMPUTEPIPELINE_HPP + +#include +#include +#include +#include +#include +#include + +namespace Nz +{ + class NAZARA_OPENGLRENDERER_API OpenGLComputePipeline : public ComputePipeline + { + public: + OpenGLComputePipeline(OpenGLDevice& device, ComputePipelineInfo pipelineInfo); + ~OpenGLComputePipeline() = default; + + void Apply(const GL::Context& context) const; + + inline const ComputePipelineInfo& GetPipelineInfo() const override; + + void UpdateDebugName(std::string_view name) override; + + private: + ComputePipelineInfo m_pipelineInfo; + GL::Program m_program; + }; +} + +#include + +#endif // NAZARA_OPENGLRENDERER_OPENGLCOMPUTEPIPELINE_HPP diff --git a/include/Nazara/OpenGLRenderer/OpenGLComputePipeline.inl b/include/Nazara/OpenGLRenderer/OpenGLComputePipeline.inl new file mode 100644 index 000000000..0a3d3d29c --- /dev/null +++ b/include/Nazara/OpenGLRenderer/OpenGLComputePipeline.inl @@ -0,0 +1,16 @@ +// Copyright (C) 2022 Jérôme "Lynix" Leclercq (lynix680@gmail.com) +// This file is part of the "Nazara Engine - OpenGL renderer" +// For conditions of distribution and use, see copyright notice in Config.hpp + +#include +#include + +namespace Nz +{ + inline const ComputePipelineInfo& OpenGLComputePipeline::GetPipelineInfo() const + { + return m_pipelineInfo; + } +} + +#include diff --git a/include/Nazara/OpenGLRenderer/OpenGLDevice.hpp b/include/Nazara/OpenGLRenderer/OpenGLDevice.hpp index e212d753a..ff9983df3 100644 --- a/include/Nazara/OpenGLRenderer/OpenGLDevice.hpp +++ b/include/Nazara/OpenGLRenderer/OpenGLDevice.hpp @@ -38,6 +38,7 @@ namespace Nz std::shared_ptr InstantiateBuffer(BufferType type, UInt64 size, BufferUsageFlags usageFlags, const void* initialData = nullptr) override; std::shared_ptr InstantiateCommandPool(QueueType queueType) override; + std::shared_ptr InstantiateComputePipeline(ComputePipelineInfo pipelineInfo) override; std::shared_ptr InstantiateFramebuffer(unsigned int width, unsigned int height, const std::shared_ptr& renderPass, const std::vector>& attachments) override; std::shared_ptr InstantiateRenderPass(std::vector attachments, std::vector subpassDescriptions, std::vector subpassDependencies) override; std::shared_ptr InstantiateRenderPipeline(RenderPipelineInfo pipelineInfo) override; diff --git a/include/Nazara/OpenGLRenderer/OpenGLRenderPipelineLayout.hpp b/include/Nazara/OpenGLRenderer/OpenGLRenderPipelineLayout.hpp index 50d804c54..ead489818 100644 --- a/include/Nazara/OpenGLRenderer/OpenGLRenderPipelineLayout.hpp +++ b/include/Nazara/OpenGLRenderer/OpenGLRenderPipelineLayout.hpp @@ -43,6 +43,7 @@ namespace Nz private: struct DescriptorPool; + struct SampledTextureDescriptor; struct StorageBufferDescriptor; struct TextureDescriptor; struct UniformBufferDescriptor; @@ -50,12 +51,20 @@ namespace Nz DescriptorPool& AllocatePool(); ShaderBindingPtr AllocateFromPool(std::size_t poolIndex, UInt32 setIndex); template void ForEachDescriptor(std::size_t poolIndex, std::size_t bindingIndex, F&& functor); + SampledTextureDescriptor& GetSampledTextureDescriptor(std::size_t poolIndex, std::size_t bindingIndex, std::size_t descriptorIndex); StorageBufferDescriptor& GetStorageBufferDescriptor(std::size_t poolIndex, std::size_t bindingIndex, std::size_t descriptorIndex); TextureDescriptor& GetTextureDescriptor(std::size_t poolIndex, std::size_t bindingIndex, std::size_t descriptorIndex); UniformBufferDescriptor& GetUniformBufferDescriptor(std::size_t poolIndex, std::size_t bindingIndex, std::size_t descriptorIndex); void Release(ShaderBinding& binding); inline void TryToShrink(); + struct SampledTextureDescriptor + { + GLuint texture; + GLuint sampler; + GL::TextureTarget textureTarget; + }; + struct StorageBufferDescriptor { GLuint buffer; @@ -65,9 +74,12 @@ namespace Nz struct TextureDescriptor { + GLboolean layered; + GLenum access; + GLenum format; + GLint layer; + GLint level; GLuint texture; - GLuint sampler; - GL::TextureTarget textureTarget; }; struct UniformBufferDescriptor @@ -77,7 +89,7 @@ namespace Nz GLsizeiptr size; }; - using Descriptor = std::variant; + using Descriptor = std::variant; struct DescriptorPool { diff --git a/include/Nazara/OpenGLRenderer/OpenGLShaderBinding.hpp b/include/Nazara/OpenGLRenderer/OpenGLShaderBinding.hpp index fcf14c438..4ec35abd8 100644 --- a/include/Nazara/OpenGLRenderer/OpenGLShaderBinding.hpp +++ b/include/Nazara/OpenGLRenderer/OpenGLShaderBinding.hpp @@ -38,7 +38,7 @@ namespace Nz OpenGLShaderBinding& operator=(OpenGLShaderBinding&&) = delete; private: - void HandleTextureBinding(UInt32 bindingIndex, const TextureBinding& textureBinding); + void HandleTextureBinding(UInt32 bindingIndex, const SampledTextureBinding& textureBinding); void Release() override; OpenGLRenderPipelineLayout& m_owner; diff --git a/include/Nazara/OpenGLRenderer/Utils.hpp b/include/Nazara/OpenGLRenderer/Utils.hpp index c0abb831f..ef42e5e02 100644 --- a/include/Nazara/OpenGLRenderer/Utils.hpp +++ b/include/Nazara/OpenGLRenderer/Utils.hpp @@ -42,6 +42,7 @@ namespace Nz inline GLenum ToOpenGL(SamplerWrap wrapMode); inline GLenum ToOpenGL(nzsl::ShaderStageType stageType); inline GLenum ToOpenGL(StencilOperation stencilOp); + inline GLenum ToOpenGL(TextureAccess textureAccess); inline GLenum ToOpenGL(GL::BufferTarget bufferTarget); inline GLenum ToOpenGL(GL::TextureTarget bufferTarget); diff --git a/include/Nazara/OpenGLRenderer/Utils.inl b/include/Nazara/OpenGLRenderer/Utils.inl index 71efdb3e7..9a1b5a961 100644 --- a/include/Nazara/OpenGLRenderer/Utils.inl +++ b/include/Nazara/OpenGLRenderer/Utils.inl @@ -229,6 +229,7 @@ namespace Nz { switch (stageType) { + case nzsl::ShaderStageType::Compute: return GL_COMPUTE_SHADER; case nzsl::ShaderStageType::Fragment: return GL_FRAGMENT_SHADER; case nzsl::ShaderStageType::Vertex: return GL_VERTEX_SHADER; } @@ -255,6 +256,19 @@ namespace Nz return {}; } + inline GLenum ToOpenGL(TextureAccess textureAccess) + { + switch (textureAccess) + { + case TextureAccess::ReadOnly: return GL_READ_ONLY; + case TextureAccess::ReadWrite: return GL_READ_WRITE; + case TextureAccess::WriteOnly: return GL_WRITE_ONLY; + } + + NazaraError("Unhandled TextureAccess 0x" + NumberToString(UnderlyingCast(textureAccess), 16)); + return {}; + } + inline GLenum ToOpenGL(GL::BufferTarget bufferTarget) { switch (bufferTarget) diff --git a/include/Nazara/OpenGLRenderer/Wrapper/Context.hpp b/include/Nazara/OpenGLRenderer/Wrapper/Context.hpp index ec4c79112..4df1455a3 100644 --- a/include/Nazara/OpenGLRenderer/Wrapper/Context.hpp +++ b/include/Nazara/OpenGLRenderer/Wrapper/Context.hpp @@ -54,9 +54,12 @@ namespace Nz::GL enum class Extension { ClipControl, + ComputeShader, DebugOutput, DepthClamp, PolygonMode, + ShaderImageLoadFormatted, + ShaderImageLoadStore, SpirV, StorageBuffers, TextureCompressionS3tc, @@ -127,6 +130,7 @@ namespace Nz::GL void BindBuffer(BufferTarget target, GLuint buffer, bool force = false) const; [[nodiscard]] GLenum BindFramebuffer(GLuint fbo) const; void BindFramebuffer(FramebufferTarget target, GLuint fbo) const; + void BindImageTexture(GLuint imageUnit, GLuint texture, GLint level, GLboolean layered, GLint layer, GLenum access, GLenum format) const; void BindProgram(GLuint program) const; void BindSampler(UInt32 textureUnit, GLuint sampler) const; void BindStorageBuffer(UInt32 storageUnit, GLuint buffer, GLintptr offset, GLsizeiptr size) const; @@ -145,11 +149,16 @@ namespace Nz::GL virtual void EnableVerticalSync(bool enabled) = 0; + inline bool GetBoolean(GLenum name) const; + inline bool GetBoolean(GLenum name, GLuint index) const; inline const OpenGLDevice* GetDevice() const; inline ExtensionStatus GetExtensionStatus(Extension extension) const; + inline float GetFloat(GLenum name) const; inline GLFunction GetFunctionByIndex(std::size_t funcIndex) const; - inline const OpenGLVaoCache& GetVaoCache() const; + template T GetInteger(GLenum name) const; + template T GetInteger(GLenum name, GLuint index) const; inline const ContextParams& GetParams() const; + inline const OpenGLVaoCache& GetVaoCache() const; inline bool IsExtensionSupported(Extension extension) const; inline bool IsExtensionSupported(const std::string& extension) const; @@ -232,6 +241,16 @@ namespace Nz::GL GLsizeiptr size = 0; }; + struct ImageUnits + { + GLboolean layered = GL_FALSE; + GLenum access = GL_READ_ONLY; + GLenum format = GL_RGBA8; + GLint layer = 0; + GLint level = 0; + GLuint texture = 0; + }; + struct TextureUnit { GLuint sampler = 0; @@ -239,9 +258,10 @@ namespace Nz::GL }; std::array bufferTargets = { 0 }; - std::vector textureUnits; std::vector storageUnits; std::vector uboUnits; + std::vector imageUnits; + std::vector textureUnits; Box scissorBox; Box viewport; GLuint boundProgram = 0; diff --git a/include/Nazara/OpenGLRenderer/Wrapper/Context.inl b/include/Nazara/OpenGLRenderer/Wrapper/Context.inl index 73fc0f8c0..c2983a721 100644 --- a/include/Nazara/OpenGLRenderer/Wrapper/Context.inl +++ b/include/Nazara/OpenGLRenderer/Wrapper/Context.inl @@ -15,6 +15,22 @@ namespace Nz::GL return !m_hadAnyError; } + inline bool Context::GetBoolean(GLenum name) const + { + GLboolean value; + glGetBooleanv(name, &value); + + return value != GL_FALSE; + } + + inline bool Context::GetBoolean(GLenum name, GLuint index) const + { + GLboolean value; + glGetBooleani_v(name, index, &value); + + return value != GL_FALSE; + } + inline const OpenGLDevice* Context::GetDevice() const { return m_device; @@ -25,15 +41,56 @@ namespace Nz::GL return m_extensionStatus[UnderlyingCast(extension)]; } + inline float Context::GetFloat(GLenum name) const + { + GLfloat value; + glGetFloatv(name, &value); + + return value; + } + inline GLFunction Context::GetFunctionByIndex(std::size_t funcIndex) const { assert(funcIndex < m_originalFunctionPointer.size()); return m_originalFunctionPointer[funcIndex]; } - inline const OpenGLVaoCache& Context::GetVaoCache() const + template + T Context::GetInteger(GLenum name) const { - return m_vaoCache; + if constexpr (std::is_same_v || std::is_same_v) + { + GLint64 value; + glGetInteger64v(name, &value); + + return SafeCast(value); + } + else + { + GLint value; + glGetIntegerv(name, &value); + + return SafeCast(value); + } + } + + template + T Context::GetInteger(GLenum name, GLuint index) const + { + if constexpr (std::is_same_v || std::is_same_v) + { + GLint64 value; + glGetInteger64i_v(name, index, &value); + + return SafeCast(value); + } + else + { + GLint value; + glGetIntegeri_v(name, index, &value); + + return SafeCast(value); + } } inline const ContextParams& Context::GetParams() const @@ -41,6 +98,11 @@ namespace Nz::GL return m_params; } + inline const OpenGLVaoCache& Context::GetVaoCache() const + { + return m_vaoCache; + } + inline bool Context::IsExtensionSupported(Extension extension) const { return GetExtensionStatus(extension) != ExtensionStatus::NotSupported; diff --git a/include/Nazara/OpenGLRenderer/Wrapper/ContextObject.inl b/include/Nazara/OpenGLRenderer/Wrapper/ContextObject.inl index 2755c9739..5420c683e 100644 --- a/include/Nazara/OpenGLRenderer/Wrapper/ContextObject.inl +++ b/include/Nazara/OpenGLRenderer/Wrapper/ContextObject.inl @@ -91,7 +91,7 @@ namespace Nz::GL EnsureContext(); if (m_context->glObjectLabel) - m_context->glObjectLabel(ObjectType, m_objectId, name.size(), name.data()); + m_context->glObjectLabel(ObjectType, m_objectId, SafeCast(name.size()), name.data()); } } diff --git a/include/Nazara/OpenGLRenderer/Wrapper/CoreFunctions.hpp b/include/Nazara/OpenGLRenderer/Wrapper/CoreFunctions.hpp index 294166530..1154bfd9f 100644 --- a/include/Nazara/OpenGLRenderer/Wrapper/CoreFunctions.hpp +++ b/include/Nazara/OpenGLRenderer/Wrapper/CoreFunctions.hpp @@ -118,9 +118,13 @@ typedef void (GL_APIENTRYP PFNGLSPECIALIZESHADERPROC) (GLuint shader, const GLch cb(glGetActiveUniformBlockiv, PFNGLGETACTIVEUNIFORMBLOCKIVPROC) \ cb(glGetActiveUniformBlockName, PFNGLGETACTIVEUNIFORMBLOCKNAMEPROC) \ cb(glGetBooleanv, PFNGLGETBOOLEANVPROC) \ + cb(glGetBooleani_v, PFNGLGETBOOLEANI_VPROC) \ cb(glGetBufferParameteriv, PFNGLGETBUFFERPARAMETERIVPROC) \ cb(glGetError, PFNGLGETERRORPROC) \ cb(glGetFloatv, PFNGLGETFLOATVPROC) \ + cb(glGetInteger64i_v, PFNGLGETINTEGER64I_VPROC) \ + cb(glGetInteger64v, PFNGLGETINTEGER64VPROC) \ + cb(glGetIntegeri_v, PFNGLGETINTEGERI_VPROC) \ cb(glGetIntegerv, PFNGLGETINTEGERVPROC) \ cb(glGetProgramBinary, PFNGLGETPROGRAMBINARYPROC) \ cb(glGetProgramInfoLog, PFNGLGETPROGRAMINFOLOGPROC) \ @@ -195,8 +199,11 @@ typedef void (GL_APIENTRYP PFNGLSPECIALIZESHADERPROC) (GLuint shader, const GLch extCb(glDrawBuffer, PFNGLDRAWBUFFERPROC) \ extCb(glPolygonMode, PFNGLPOLYGONMODEPROC) \ /* OpenGL 4.2 - OpenGL ES 3.1 */\ + extCb(glBindImageTexture, PFNGLBINDIMAGETEXTUREPROC) \ extCb(glMemoryBarrier, PFNGLMEMORYBARRIERPROC) \ extCb(glMemoryBarrierByRegion, PFNGLMEMORYBARRIERBYREGIONPROC) \ + /* OpenGL 4.3 - OpenGL ES 3.1 */\ + extCb(glDispatchCompute, PFNGLDISPATCHCOMPUTEPROC) \ /* OpenGL 4.3 - OpenGL ES 3.2 */\ extCb(glCopyImageSubData, PFNGLCOPYIMAGESUBDATAPROC) \ extCb(glDebugMessageCallback, PFNGLDEBUGMESSAGECALLBACKPROC) \ diff --git a/include/Nazara/Renderer/CommandBufferBuilder.hpp b/include/Nazara/Renderer/CommandBufferBuilder.hpp index 5a9fb2af2..61034bf61 100644 --- a/include/Nazara/Renderer/CommandBufferBuilder.hpp +++ b/include/Nazara/Renderer/CommandBufferBuilder.hpp @@ -20,6 +20,7 @@ namespace Nz { + class ComputePipeline; class Framebuffer; class RenderPass; class RenderPipeline; @@ -42,8 +43,9 @@ namespace Nz inline void BeginRenderPass(const Framebuffer& framebuffer, const RenderPass& renderPass, const Recti& renderRect); inline void BeginRenderPass(const Framebuffer& framebuffer, const RenderPass& renderPass, const Recti& renderRect, std::initializer_list clearValues); + virtual void BindComputePipeline(const ComputePipeline& pipeline) = 0; virtual void BindIndexBuffer(const RenderBuffer& indexBuffer, IndexType indexType, UInt64 offset = 0) = 0; - virtual void BindPipeline(const RenderPipeline& pipeline) = 0; + virtual void BindRenderPipeline(const RenderPipeline& pipeline) = 0; virtual void BindShaderBinding(UInt32 set, const ShaderBinding& binding) = 0; virtual void BindShaderBinding(const RenderPipelineLayout& pipelineLayout, UInt32 set, const ShaderBinding& binding) = 0; virtual void BindVertexBuffer(UInt32 binding, const RenderBuffer& vertexBuffer, UInt64 offset = 0) = 0; @@ -59,6 +61,8 @@ namespace Nz virtual void Draw(UInt32 vertexCount, UInt32 instanceCount = 1, UInt32 firstVertex = 0, UInt32 firstInstance = 0) = 0; virtual void DrawIndexed(UInt32 indexCount, UInt32 instanceCount = 1, UInt32 firstIndex = 0, UInt32 firstInstance = 0) = 0; + virtual void Dispatch(UInt32 workgroupX, UInt32 workgroupY, UInt32 workgroupZ) = 0; + virtual void EndDebugRegion() = 0; virtual void EndRenderPass() = 0; diff --git a/include/Nazara/Renderer/ComputePipeline.hpp b/include/Nazara/Renderer/ComputePipeline.hpp new file mode 100644 index 000000000..8265ca133 --- /dev/null +++ b/include/Nazara/Renderer/ComputePipeline.hpp @@ -0,0 +1,39 @@ +// Copyright (C) 2022 Jérôme "Lynix" Leclercq (lynix680@gmail.com) +// This file is part of the "Nazara Engine - Renderer module" +// For conditions of distribution and use, see copyright notice in Config.hpp + +#pragma once + +#ifndef NAZARA_RENDERER_COMPUTEPIPELINE_HPP +#define NAZARA_RENDERER_COMPUTEPIPELINE_HPP + +#include +#include +#include + +namespace Nz +{ + class RenderPipelineLayout; + class ShaderModule; + + struct ComputePipelineInfo + { + std::shared_ptr pipelineLayout; + std::shared_ptr shaderModule; + }; + + class NAZARA_RENDERER_API ComputePipeline + { + public: + ComputePipeline() = default; + virtual ~ComputePipeline(); + + virtual const ComputePipelineInfo& GetPipelineInfo() const = 0; + + virtual void UpdateDebugName(std::string_view name) = 0; + }; +} + +#include + +#endif // NAZARA_RENDERER_COMPUTEPIPELINE_HPP diff --git a/include/Nazara/Renderer/ComputePipeline.inl b/include/Nazara/Renderer/ComputePipeline.inl new file mode 100644 index 000000000..c20521cc0 --- /dev/null +++ b/include/Nazara/Renderer/ComputePipeline.inl @@ -0,0 +1,13 @@ +// Copyright (C) 2022 Jérôme "Lynix" Leclercq (lynix680@gmail.com) +// This file is part of the "Nazara Engine - Renderer module" +// For conditions of distribution and use, see copyright notice in Config.hpp + +#include +#include +#include + +namespace Nz +{ +} + +#include diff --git a/include/Nazara/Renderer/Enums.hpp b/include/Nazara/Renderer/Enums.hpp index 8a58feff4..6564950ce 100644 --- a/include/Nazara/Renderer/Enums.hpp +++ b/include/Nazara/Renderer/Enums.hpp @@ -165,6 +165,7 @@ namespace Nz enum class ShaderBindingType { + Sampler, StorageBuffer, Texture, UniformBuffer, @@ -182,6 +183,13 @@ namespace Nz SpirV }; + enum class TextureAccess + { + ReadOnly, + ReadWrite, + WriteOnly + }; + enum class TextureLayout { ColorInput, @@ -199,6 +207,7 @@ namespace Nz ColorAttachment, DepthStencilAttachment, InputAttachment, + ShaderReadWrite, ShaderSampling, TransferSource, TransferDestination, diff --git a/include/Nazara/Renderer/RenderDevice.hpp b/include/Nazara/Renderer/RenderDevice.hpp index 86be80a1a..68772fe7d 100644 --- a/include/Nazara/Renderer/RenderDevice.hpp +++ b/include/Nazara/Renderer/RenderDevice.hpp @@ -8,6 +8,7 @@ #define NAZARA_RENDERER_RENDERDEVICE_HPP #include +#include #include #include #include @@ -40,6 +41,7 @@ namespace Nz virtual std::shared_ptr InstantiateBuffer(BufferType type, UInt64 size, BufferUsageFlags usageFlags, const void* initialData = nullptr) = 0; virtual std::shared_ptr InstantiateCommandPool(QueueType queueType) = 0; + virtual std::shared_ptr InstantiateComputePipeline(ComputePipelineInfo pipelineInfo) = 0; virtual std::shared_ptr InstantiateFramebuffer(unsigned int width, unsigned int height, const std::shared_ptr& renderPass, const std::vector>& attachments) = 0; virtual std::shared_ptr InstantiateRenderPass(std::vector attachments, std::vector subpassDescriptions, std::vector subpassDependencies) = 0; virtual std::shared_ptr InstantiateRenderPipeline(RenderPipelineInfo pipelineInfo) = 0; diff --git a/include/Nazara/Renderer/RenderDeviceInfo.hpp b/include/Nazara/Renderer/RenderDeviceInfo.hpp index 4f2eb4209..2c576c2ef 100644 --- a/include/Nazara/Renderer/RenderDeviceInfo.hpp +++ b/include/Nazara/Renderer/RenderDeviceInfo.hpp @@ -8,6 +8,7 @@ #define NAZARA_RENDERER_RENDERDEVICEINFO_HPP #include +#include #include #include @@ -16,18 +17,27 @@ namespace Nz struct RenderDeviceFeatures { bool anisotropicFiltering = false; + bool computeShaders = false; bool depthClamping = false; bool nonSolidFaceFilling = false; bool storageBuffers = false; + bool textureRead = false; + bool textureReadWithoutFormat = false; + bool textureWrite = false; + bool textureWriteWithoutFormat = false; bool unrestrictedTextureViews = false; }; struct RenderDeviceLimits { - UInt64 minStorageBufferOffsetAlignment; - UInt64 minUniformBufferOffsetAlignment; + UInt64 maxComputeSharedMemorySize; + UInt32 maxComputeWorkGroupInvocations; + Vector3ui32 maxComputeWorkGroupCount; + Vector3ui32 maxComputeWorkGroupSize; UInt64 maxStorageBufferSize; UInt64 maxUniformBufferSize; + UInt64 minStorageBufferOffsetAlignment; + UInt64 minUniformBufferOffsetAlignment; }; struct RenderDeviceInfo diff --git a/include/Nazara/Renderer/ShaderBinding.hpp b/include/Nazara/Renderer/ShaderBinding.hpp index 8e4c6eedf..f0776accd 100644 --- a/include/Nazara/Renderer/ShaderBinding.hpp +++ b/include/Nazara/Renderer/ShaderBinding.hpp @@ -9,6 +9,7 @@ #include #include +#include #include #include #include @@ -43,6 +44,18 @@ namespace Nz ShaderBinding& operator=(const ShaderBinding&) = delete; ShaderBinding& operator=(ShaderBinding&&) = delete; + struct SampledTextureBinding + { + const Texture* texture; + const TextureSampler* sampler; + }; + + struct SampledTextureBindings + { + UInt32 arraySize; + const SampledTextureBinding* textureBindings; + }; + struct StorageBufferBinding { RenderBuffer* buffer; @@ -53,13 +66,7 @@ namespace Nz struct TextureBinding { const Texture* texture; - const TextureSampler* sampler; - }; - - struct TextureBindings - { - UInt32 arraySize; - const TextureBinding* textureBindings; + TextureAccess access; }; struct UniformBufferBinding @@ -72,7 +79,7 @@ namespace Nz struct Binding { UInt32 bindingIndex; - std::variant content; + std::variant content; }; protected: diff --git a/include/Nazara/Utility/ImageStream.hpp b/include/Nazara/Utility/ImageStream.hpp index 87e6aeef8..aa03f3aae 100644 --- a/include/Nazara/Utility/ImageStream.hpp +++ b/include/Nazara/Utility/ImageStream.hpp @@ -23,7 +23,6 @@ namespace Nz bool IsValid() const; }; - class Mutex; class ImageStream; using ImageStreamLoader = ResourceLoader; diff --git a/include/Nazara/VulkanRenderer/Utils.inl b/include/Nazara/VulkanRenderer/Utils.inl index 5dfe4840e..7087c40e5 100644 --- a/include/Nazara/VulkanRenderer/Utils.inl +++ b/include/Nazara/VulkanRenderer/Utils.inl @@ -382,8 +382,9 @@ namespace Nz { switch (bindingType) { + case ShaderBindingType::Sampler: return VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; case ShaderBindingType::StorageBuffer: return VK_DESCRIPTOR_TYPE_STORAGE_BUFFER; - case ShaderBindingType::Texture: return VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; + case ShaderBindingType::Texture: return VK_DESCRIPTOR_TYPE_STORAGE_IMAGE; case ShaderBindingType::UniformBuffer: return VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; } @@ -395,6 +396,7 @@ namespace Nz { switch (stageType) { + case nzsl::ShaderStageType::Compute: return VK_SHADER_STAGE_COMPUTE_BIT; case nzsl::ShaderStageType::Fragment: return VK_SHADER_STAGE_FRAGMENT_BIT; case nzsl::ShaderStageType::Vertex: return VK_SHADER_STAGE_VERTEX_BIT; } @@ -459,6 +461,7 @@ namespace Nz case TextureUsage::ColorAttachment: return VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; case TextureUsage::DepthStencilAttachment: return VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT; case TextureUsage::InputAttachment: return VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT; + case TextureUsage::ShaderReadWrite: return VK_IMAGE_USAGE_STORAGE_BIT; case TextureUsage::ShaderSampling: return VK_IMAGE_USAGE_SAMPLED_BIT; case TextureUsage::TransferSource: return VK_IMAGE_USAGE_TRANSFER_SRC_BIT; case TextureUsage::TransferDestination: return VK_IMAGE_USAGE_TRANSFER_DST_BIT; diff --git a/include/Nazara/VulkanRenderer/VulkanCommandBufferBuilder.hpp b/include/Nazara/VulkanRenderer/VulkanCommandBufferBuilder.hpp index dd0feaab9..03ea16a08 100644 --- a/include/Nazara/VulkanRenderer/VulkanCommandBufferBuilder.hpp +++ b/include/Nazara/VulkanRenderer/VulkanCommandBufferBuilder.hpp @@ -27,8 +27,9 @@ namespace Nz void BeginDebugRegion(const std::string_view& regionName, const Color& color) override; void BeginRenderPass(const Framebuffer& framebuffer, const RenderPass& renderPass, const Recti& renderRect, const ClearValues* clearValues, std::size_t clearValueCount) override; + void BindComputePipeline(const ComputePipeline& pipeline) override; void BindIndexBuffer(const RenderBuffer& indexBuffer, IndexType indexType, UInt64 offset = 0) override; - void BindPipeline(const RenderPipeline& pipeline) override; + void BindRenderPipeline(const RenderPipeline& pipeline) override; void BindShaderBinding(UInt32 set, const ShaderBinding& binding) override; void BindShaderBinding(const RenderPipelineLayout& pipelineLayout, UInt32 set, const ShaderBinding& binding) override; void BindVertexBuffer(UInt32 binding, const RenderBuffer& vertexBuffer, UInt64 offset = 0) override; @@ -39,6 +40,8 @@ namespace Nz void CopyBuffer(const UploadPool::Allocation& allocation, const RenderBufferView& target, UInt64 size, UInt64 sourceOffset = 0, UInt64 targetOffset = 0) override; void CopyTexture(const Texture& fromTexture, const Boxui& fromBox, TextureLayout fromLayout, const Texture& toTexture, const Vector3ui& toPos, TextureLayout toLayout) override; + void Dispatch(UInt32 workgroupX, UInt32 workgroupY, UInt32 workgroupZ) override; + void Draw(UInt32 vertexCount, UInt32 instanceCount = 1, UInt32 firstVertex = 0, UInt32 firstInstance = 0) override; void DrawIndexed(UInt32 indexCount, UInt32 instanceCount = 1, UInt32 firstIndex = 0, UInt32 firstInstance = 0) override; diff --git a/include/Nazara/VulkanRenderer/VulkanComputePipeline.hpp b/include/Nazara/VulkanRenderer/VulkanComputePipeline.hpp new file mode 100644 index 000000000..e97aaa53e --- /dev/null +++ b/include/Nazara/VulkanRenderer/VulkanComputePipeline.hpp @@ -0,0 +1,45 @@ +// Copyright (C) 2022 Jérôme "Lynix" Leclercq (lynix680@gmail.com) +// This file is part of the "Nazara Engine - Vulkan renderer" +// For conditions of distribution and use, see copyright notice in Config.hpp + +#pragma once + +#ifndef NAZARA_VULKANRENDERER_VULKANCOMPUTEPIPELINE_HPP +#define NAZARA_VULKANRENDERER_VULKANCOMPUTEPIPELINE_HPP + +#include +#include +#include +#include +#include +#include + +namespace Nz +{ + class VulkanDevice; + + class NAZARA_VULKANRENDERER_API VulkanComputePipeline : public ComputePipeline + { + public: + VulkanComputePipeline(VulkanDevice& device, ComputePipelineInfo pipelineInfo); + VulkanComputePipeline(const VulkanComputePipeline&) = delete; + VulkanComputePipeline(VulkanComputePipeline&&) = delete; + ~VulkanComputePipeline() = default; + + inline const Vk::Pipeline& GetPipeline() const; + inline const ComputePipelineInfo& GetPipelineInfo() const override; + + void UpdateDebugName(std::string_view name) override; + + VulkanComputePipeline& operator=(const VulkanComputePipeline&) = delete; + VulkanComputePipeline& operator=(VulkanComputePipeline&&) = delete; + + private: + Vk::Pipeline m_pipeline; + ComputePipelineInfo m_pipelineInfo; + }; +} + +#include + +#endif // NAZARA_VULKANRENDERER_VULKANCOMPUTEPIPELINE_HPP diff --git a/include/Nazara/VulkanRenderer/VulkanComputePipeline.inl b/include/Nazara/VulkanRenderer/VulkanComputePipeline.inl new file mode 100644 index 000000000..9ab836693 --- /dev/null +++ b/include/Nazara/VulkanRenderer/VulkanComputePipeline.inl @@ -0,0 +1,21 @@ +// Copyright (C) 2022 Jérôme "Lynix" Leclercq (lynix680@gmail.com) +// This file is part of the "Nazara Engine - Vulkan renderer" +// For conditions of distribution and use, see copyright notice in Config.hpp + +#include +#include + +namespace Nz +{ + inline const Vk::Pipeline& VulkanComputePipeline::GetPipeline() const + { + return m_pipeline; + } + + inline const ComputePipelineInfo& VulkanComputePipeline::GetPipelineInfo() const + { + return m_pipelineInfo; + } +} + +#include diff --git a/include/Nazara/VulkanRenderer/VulkanDevice.hpp b/include/Nazara/VulkanRenderer/VulkanDevice.hpp index ddb2fad56..0c6c76fa6 100644 --- a/include/Nazara/VulkanRenderer/VulkanDevice.hpp +++ b/include/Nazara/VulkanRenderer/VulkanDevice.hpp @@ -28,6 +28,7 @@ namespace Nz std::shared_ptr InstantiateBuffer(BufferType type, UInt64 size, BufferUsageFlags usageFlags, const void* initialData = nullptr) override; std::shared_ptr InstantiateCommandPool(QueueType queueType) override; + std::shared_ptr InstantiateComputePipeline(ComputePipelineInfo pipelineInfo) override; std::shared_ptr InstantiateFramebuffer(unsigned int width, unsigned int height, const std::shared_ptr& renderPass, const std::vector>& attachments) override; std::shared_ptr InstantiateRenderPass(std::vector attachments, std::vector subpassDescriptions, std::vector subpassDependencies) override; std::shared_ptr InstantiateRenderPipeline(RenderPipelineInfo pipelineInfo) override; diff --git a/include/Nazara/VulkanRenderer/Wrapper/CommandBuffer.hpp b/include/Nazara/VulkanRenderer/Wrapper/CommandBuffer.hpp index f07c55428..819c97889 100644 --- a/include/Nazara/VulkanRenderer/Wrapper/CommandBuffer.hpp +++ b/include/Nazara/VulkanRenderer/Wrapper/CommandBuffer.hpp @@ -67,6 +67,8 @@ namespace Nz inline void CopyImage(VkImage srcImage, VkImageLayout srcImageLayout, VkImage dstImage, VkImageLayout dstImageLayout, const VkImageCopy& region); inline void CopyImage(VkImage srcImage, VkImageLayout srcImageLayout, VkImage dstImage, VkImageLayout dstImageLayout, UInt32 regionCount, const VkImageCopy* regions); + inline void Dispatch(UInt32 groupCountX, UInt32 groupCountY, UInt32 groupCountZ); + inline void Draw(UInt32 vertexCount, UInt32 instanceCount = 1, UInt32 firstVertex = 0, UInt32 firstInstance = 0); inline void DrawIndexed(UInt32 indexCount, UInt32 instanceCount = 1, UInt32 firstVertex = 0, Int32 vertexOffset = 0, UInt32 firstInstance = 0); diff --git a/include/Nazara/VulkanRenderer/Wrapper/CommandBuffer.inl b/include/Nazara/VulkanRenderer/Wrapper/CommandBuffer.inl index 51920184c..61fed0897 100644 --- a/include/Nazara/VulkanRenderer/Wrapper/CommandBuffer.inl +++ b/include/Nazara/VulkanRenderer/Wrapper/CommandBuffer.inl @@ -302,6 +302,11 @@ namespace Nz return m_pool->GetDevice()->vkCmdCopyImage(m_handle, srcImage, srcImageLayout, dstImage, dstImageLayout, regionCount, regions); } + inline void CommandBuffer::Dispatch(UInt32 groupCountX, UInt32 groupCountY, UInt32 groupCountZ) + { + return m_pool->GetDevice()->vkCmdDispatch(m_handle, groupCountX, groupCountY, groupCountZ); + } + inline void CommandBuffer::Draw(UInt32 vertexCount, UInt32 instanceCount, UInt32 firstVertex, UInt32 firstInstance) { return m_pool->GetDevice()->vkCmdDraw(m_handle, vertexCount, instanceCount, firstVertex, firstInstance); diff --git a/src/Nazara/Graphics/ForwardFramePipeline.cpp b/src/Nazara/Graphics/ForwardFramePipeline.cpp index 782c033af..688a0887c 100644 --- a/src/Nazara/Graphics/ForwardFramePipeline.cpp +++ b/src/Nazara/Graphics/ForwardFramePipeline.cpp @@ -402,7 +402,7 @@ namespace Nz viewerData.blitShaderBinding->Update({ { 0, - ShaderBinding::TextureBinding { + ShaderBinding::SampledTextureBinding { m_bakedFrameGraph.GetAttachmentTexture(viewerData.debugColorAttachment).get(), sampler.get() } @@ -419,7 +419,7 @@ namespace Nz renderTargetData.blitShaderBinding->Update({ { 0, - ShaderBinding::TextureBinding { + ShaderBinding::SampledTextureBinding { m_bakedFrameGraph.GetAttachmentTexture(renderTargetData.finalAttachment).get(), sampler.get() } @@ -456,7 +456,7 @@ namespace Nz { builder.SetScissor(renderRegion); builder.SetViewport(renderRegion); - builder.BindPipeline(*graphics->GetBlitPipeline(false)); + builder.BindRenderPipeline(*graphics->GetBlitPipeline(false)); builder.BindShaderBinding(0, *data.blitShaderBinding); builder.Draw(3); @@ -644,7 +644,7 @@ namespace Nz builder.SetViewport(env.renderRect); Graphics* graphics = Graphics::Instance(); - builder.BindPipeline(*graphics->GetBlitPipeline(false)); + builder.BindRenderPipeline(*graphics->GetBlitPipeline(false)); bool first = true; @@ -657,7 +657,7 @@ namespace Nz if (first) { - builder.BindPipeline(*graphics->GetBlitPipeline(true)); + builder.BindRenderPipeline(*graphics->GetBlitPipeline(true)); first = false; } } diff --git a/src/Nazara/Graphics/Graphics.cpp b/src/Nazara/Graphics/Graphics.cpp index 9deb9efcb..39b7166a4 100644 --- a/src/Nazara/Graphics/Graphics.cpp +++ b/src/Nazara/Graphics/Graphics.cpp @@ -108,9 +108,14 @@ namespace Nz RenderDeviceFeatures enabledFeatures; enabledFeatures.anisotropicFiltering = !config.forceDisableFeatures.anisotropicFiltering && renderDeviceInfo[bestRenderDeviceIndex].features.anisotropicFiltering; + enabledFeatures.computeShaders = !config.forceDisableFeatures.computeShaders && renderDeviceInfo[bestRenderDeviceIndex].features.computeShaders; enabledFeatures.depthClamping = !config.forceDisableFeatures.depthClamping && renderDeviceInfo[bestRenderDeviceIndex].features.depthClamping; enabledFeatures.nonSolidFaceFilling = !config.forceDisableFeatures.nonSolidFaceFilling && renderDeviceInfo[bestRenderDeviceIndex].features.nonSolidFaceFilling; enabledFeatures.storageBuffers = !config.forceDisableFeatures.storageBuffers && renderDeviceInfo[bestRenderDeviceIndex].features.storageBuffers; + enabledFeatures.textureRead = !config.forceDisableFeatures.textureRead && renderDeviceInfo[bestRenderDeviceIndex].features.textureRead; + enabledFeatures.textureReadWithoutFormat = !config.forceDisableFeatures.textureReadWithoutFormat && renderDeviceInfo[bestRenderDeviceIndex].features.textureReadWithoutFormat; + enabledFeatures.textureWrite = !config.forceDisableFeatures.textureWrite && renderDeviceInfo[bestRenderDeviceIndex].features.textureWrite; + enabledFeatures.textureWriteWithoutFormat = !config.forceDisableFeatures.textureWriteWithoutFormat && renderDeviceInfo[bestRenderDeviceIndex].features.textureWriteWithoutFormat; enabledFeatures.unrestrictedTextureViews = !config.forceDisableFeatures.unrestrictedTextureViews && renderDeviceInfo[bestRenderDeviceIndex].features.unrestrictedTextureViews; m_renderDevice = renderer->InstanciateRenderDevice(bestRenderDeviceIndex, enabledFeatures); @@ -176,7 +181,7 @@ namespace Nz layoutInfo.bindings.assign({ { 0, 0, 1, - ShaderBindingType::Texture, + ShaderBindingType::Sampler, nzsl::ShaderStageType::Fragment } }); diff --git a/src/Nazara/Graphics/MaterialInstance.cpp b/src/Nazara/Graphics/MaterialInstance.cpp index 7699c5d71..f85079c0f 100644 --- a/src/Nazara/Graphics/MaterialInstance.cpp +++ b/src/Nazara/Graphics/MaterialInstance.cpp @@ -150,7 +150,7 @@ namespace Nz bindings.push_back({ textureSlot.bindingIndex, - ShaderBinding::TextureBinding { + ShaderBinding::SampledTextureBinding { texture.get(), sampler.get() } }); diff --git a/src/Nazara/Graphics/ShaderReflection.cpp b/src/Nazara/Graphics/ShaderReflection.cpp index ee46acfcf..cd1e13541 100644 --- a/src/Nazara/Graphics/ShaderReflection.cpp +++ b/src/Nazara/Graphics/ShaderReflection.cpp @@ -56,6 +56,8 @@ namespace Nz if (IsStorageType(*varType)) bindingType = ShaderBindingType::StorageBuffer; else if (IsSamplerType(*varType)) + bindingType = ShaderBindingType::Sampler; + else if (IsTextureType(*varType)) bindingType = ShaderBindingType::Texture; else if (IsUniformType(*varType)) bindingType = ShaderBindingType::UniformBuffer; @@ -67,7 +69,7 @@ namespace Nz throw std::runtime_error("unexpected type " + ToString(innerType) + " in array " + ToString(arrayType)); arraySize = arrayType.length; - bindingType = ShaderBindingType::Texture; + bindingType = ShaderBindingType::Sampler; varType = &innerType; } else @@ -86,6 +88,21 @@ namespace Nz { switch (bindingType) { + case ShaderBindingType::Sampler: + { + if (externalBlock->samplers.find(externalVar.tag) != externalBlock->samplers.end()) + throw std::runtime_error("duplicate sampler tag " + externalVar.tag + " in external block " + node.tag); + + const auto& samplerType = std::get(*varType); + + ExternalSampler& texture = externalBlock->samplers[externalVar.tag]; + texture.bindingIndex = bindingIndex; + texture.bindingSet = bindingSet; + texture.imageType = samplerType.dim; + texture.sampledType = samplerType.sampledType; + break; + } + case ShaderBindingType::StorageBuffer: { if (externalBlock->storageBlocks.find(externalVar.tag) != externalBlock->storageBlocks.end()) @@ -100,16 +117,18 @@ namespace Nz case ShaderBindingType::Texture: { - if (externalBlock->samplers.find(externalVar.tag) != externalBlock->samplers.end()) + if (externalBlock->textures.find(externalVar.tag) != externalBlock->textures.end()) throw std::runtime_error("duplicate textures tag " + externalVar.tag + " in external block " + node.tag); - const auto& samplerType = std::get(*varType); + const auto& textureType = std::get(*varType); - ExternalTexture& texture = externalBlock->samplers[externalVar.tag]; + ExternalTexture& texture = externalBlock->textures[externalVar.tag]; texture.bindingIndex = bindingIndex; - texture.bindingSet = bindingSet; - texture.imageType = samplerType.dim; - texture.sampledType = samplerType.sampledType; + texture.bindingSet = bindingSet; + texture.accessPolicy = textureType.accessPolicy; + texture.baseType = textureType.baseType; + texture.imageFormat = textureType.format; + texture.imageType = textureType.dim; break; } diff --git a/src/Nazara/Graphics/SpriteChainRenderer.cpp b/src/Nazara/Graphics/SpriteChainRenderer.cpp index 164268b27..e73535268 100644 --- a/src/Nazara/Graphics/SpriteChainRenderer.cpp +++ b/src/Nazara/Graphics/SpriteChainRenderer.cpp @@ -199,7 +199,7 @@ namespace Nz { auto& bindingEntry = m_bindingCache.emplace_back(); bindingEntry.bindingIndex = bindingIndex; - bindingEntry.content = ShaderBinding::TextureBinding{ + bindingEntry.content = ShaderBinding::SampledTextureBinding{ m_pendingData.currentTextureOverlay, defaultSampler.get() }; } @@ -310,7 +310,7 @@ namespace Nz if (currentPipeline != drawData.renderPipeline) { - commandBuffer.BindPipeline(*drawData.renderPipeline); + commandBuffer.BindRenderPipeline(*drawData.renderPipeline); currentPipeline = drawData.renderPipeline; } diff --git a/src/Nazara/Graphics/SubmeshRenderer.cpp b/src/Nazara/Graphics/SubmeshRenderer.cpp index ea1bd6535..53f50da7e 100644 --- a/src/Nazara/Graphics/SubmeshRenderer.cpp +++ b/src/Nazara/Graphics/SubmeshRenderer.cpp @@ -175,7 +175,7 @@ namespace Nz auto& bindingEntry = m_bindingCache.emplace_back(); bindingEntry.bindingIndex = bindingIndex; - bindingEntry.content = ShaderBinding::TextureBindings { + bindingEntry.content = ShaderBinding::SampledTextureBindings { SafeCast(renderState.shadowMaps2D.size()), &m_textureBindingCache[textureBindingBaseIndex] }; } @@ -197,7 +197,7 @@ namespace Nz auto& bindingEntry = m_bindingCache.emplace_back(); bindingEntry.bindingIndex = bindingIndex; - bindingEntry.content = ShaderBinding::TextureBindings { + bindingEntry.content = ShaderBinding::SampledTextureBindings { SafeCast(renderState.shadowMapsCube.size()), &m_textureBindingCache[textureBindingBaseIndex] }; } @@ -230,7 +230,7 @@ namespace Nz { auto& bindingEntry = m_bindingCache.emplace_back(); bindingEntry.bindingIndex = bindingIndex; - bindingEntry.content = ShaderBinding::TextureBinding{ + bindingEntry.content = ShaderBinding::SampledTextureBinding{ whiteTexture2D.get(), defaultSampler.get() }; } @@ -285,7 +285,7 @@ namespace Nz if (currentPipeline != drawData.renderPipeline) { - commandBuffer.BindPipeline(*drawData.renderPipeline); + commandBuffer.BindRenderPipeline(*drawData.renderPipeline); currentPipeline = drawData.renderPipeline; } diff --git a/src/Nazara/OpenGLRenderer/OpenGLCommandBuffer.cpp b/src/Nazara/OpenGLRenderer/OpenGLCommandBuffer.cpp index 17d4d6f8e..dbb00fe66 100644 --- a/src/Nazara/OpenGLRenderer/OpenGLCommandBuffer.cpp +++ b/src/Nazara/OpenGLRenderer/OpenGLCommandBuffer.cpp @@ -4,6 +4,7 @@ #include #include +#include #include #include #include @@ -93,6 +94,14 @@ namespace Nz { 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); + context->glDispatchCompute(command.numGroupsX, command.numGroupsY, command.numGroupsZ); + } else if constexpr (std::is_same_v) { ApplyStates(*context, command.states); diff --git a/src/Nazara/OpenGLRenderer/OpenGLCommandBufferBuilder.cpp b/src/Nazara/OpenGLRenderer/OpenGLCommandBufferBuilder.cpp index a017bbcf1..bff9b0b67 100644 --- a/src/Nazara/OpenGLRenderer/OpenGLCommandBufferBuilder.cpp +++ b/src/Nazara/OpenGLRenderer/OpenGLCommandBufferBuilder.cpp @@ -4,6 +4,7 @@ #include #include +#include #include #include #include @@ -26,6 +27,13 @@ namespace Nz m_commandBuffer.SetFramebuffer(static_cast(framebuffer), static_cast(renderPass), clearValues, clearValueCount); } + void OpenGLCommandBufferBuilder::BindComputePipeline(const ComputePipeline& pipeline) + { + const OpenGLComputePipeline& glPipeline = static_cast(pipeline); + + m_commandBuffer.BindComputePipeline(&glPipeline); + } + void OpenGLCommandBufferBuilder::BindIndexBuffer(const RenderBuffer& indexBuffer, IndexType indexType, UInt64 offset) { const OpenGLBuffer& glBuffer = static_cast(indexBuffer); @@ -33,11 +41,11 @@ namespace Nz m_commandBuffer.BindIndexBuffer(glBuffer.GetBuffer().GetObjectId(), indexType, offset); } - void OpenGLCommandBufferBuilder::BindPipeline(const RenderPipeline& pipeline) + void OpenGLCommandBufferBuilder::BindRenderPipeline(const RenderPipeline& pipeline) { const OpenGLRenderPipeline& glPipeline = static_cast(pipeline); - m_commandBuffer.BindPipeline(&glPipeline); + m_commandBuffer.BindRenderPipeline(&glPipeline); } void OpenGLCommandBufferBuilder::BindShaderBinding(UInt32 set, const ShaderBinding& binding) @@ -93,6 +101,11 @@ namespace Nz m_commandBuffer.CopyTexture(sourceTexture, fromBox, targetTexture, toPos); } + void OpenGLCommandBufferBuilder::Dispatch(UInt32 workgroupX, UInt32 workgroupY, UInt32 workgroupZ) + { + m_commandBuffer.Dispatch(workgroupX, workgroupY, workgroupZ); + } + void OpenGLCommandBufferBuilder::Draw(UInt32 vertexCount, UInt32 instanceCount, UInt32 firstVertex, UInt32 firstInstance) { m_commandBuffer.Draw(vertexCount, instanceCount, firstVertex, firstInstance); diff --git a/src/Nazara/OpenGLRenderer/OpenGLComputePipeline.cpp b/src/Nazara/OpenGLRenderer/OpenGLComputePipeline.cpp new file mode 100644 index 000000000..1003fc389 --- /dev/null +++ b/src/Nazara/OpenGLRenderer/OpenGLComputePipeline.cpp @@ -0,0 +1,75 @@ +// Copyright (C) 2022 Jérôme "Lynix" Leclercq (lynix680@gmail.com) +// This file is part of the "Nazara Engine - OpenGL renderer" +// For conditions of distribution and use, see copyright notice in Config.hpp + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace Nz +{ + OpenGLComputePipeline::OpenGLComputePipeline(OpenGLDevice& device, ComputePipelineInfo pipelineInfo) : + m_pipelineInfo(std::move(pipelineInfo)) + { + if (device.GetEnabledFeatures().computeShaders) + throw std::runtime_error("compute shaders are not enabled on the device"); + + OpenGLRenderPipelineLayout& pipelineLayout = static_cast(*m_pipelineInfo.pipelineLayout); + + if (!m_program.Create(device)) + throw std::runtime_error("failed to create program"); + + NazaraAssert(pipelineInfo.shaderModule, "invalid shader module"); + + OpenGLShaderModule& shaderModule = static_cast(*pipelineInfo.shaderModule); + + std::vector explicitBindings; + nzsl::ShaderStageTypeFlags stageFlags = shaderModule.Attach(m_program, pipelineLayout.GetBindingMapping(), &explicitBindings); + if (!stageFlags.Test(nzsl::ShaderStageType::Compute)) + throw std::runtime_error("shader module has no compute stage"); + + m_program.Link(); + + std::string errLog; + if (!m_program.GetLinkStatus(&errLog)) + throw std::runtime_error("failed to link program: " + errLog); + + for (const auto& explicitBinding : explicitBindings) + { + if (explicitBinding.isBlock) + { + GLuint blockIndex = m_program.GetUniformBlockIndex(explicitBinding.name); + if (blockIndex == GL_INVALID_INDEX) + continue; + + m_program.UniformBlockBinding(blockIndex, explicitBinding.binding); + } + else + { + int location = m_program.GetUniformLocation(explicitBinding.name); + if (location == -1) + continue; + + m_program.Uniform(location, SafeCast(explicitBinding.binding)); + } + } + } + + void OpenGLComputePipeline::Apply(const GL::Context& context) const + { + context.BindProgram(m_program.GetObjectId()); + } + + void OpenGLComputePipeline::UpdateDebugName(std::string_view name) + { + m_program.SetDebugName(name); + } +} diff --git a/src/Nazara/OpenGLRenderer/OpenGLDevice.cpp b/src/Nazara/OpenGLRenderer/OpenGLDevice.cpp index f5964b417..e46764795 100644 --- a/src/Nazara/OpenGLRenderer/OpenGLDevice.cpp +++ b/src/Nazara/OpenGLRenderer/OpenGLDevice.cpp @@ -5,6 +5,7 @@ #include #include #include +#include #include #include #include @@ -62,47 +63,58 @@ namespace Nz if (m_referenceContext->IsExtensionSupported(GL::Extension::TextureFilterAnisotropic)) m_deviceInfo.features.anisotropicFiltering = true; + if (m_referenceContext->IsExtensionSupported(GL::Extension::ComputeShader)) + m_deviceInfo.features.computeShaders = true; + if (m_referenceContext->IsExtensionSupported(GL::Extension::DepthClamp)) m_deviceInfo.features.depthClamping = true; + if (m_referenceContext->glPolygonMode) //< not supported in core OpenGL ES, but supported in OpenGL or with GL_NV_polygon_mode extension + m_deviceInfo.features.nonSolidFaceFilling = true; + if (m_referenceContext->IsExtensionSupported(GL::Extension::StorageBuffers)) m_deviceInfo.features.storageBuffers = true; - if (m_referenceContext->glPolygonMode) //< not supported in core OpenGL ES, but supported in OpenGL or with GL_NV_polygon_mode extension - m_deviceInfo.features.nonSolidFaceFilling = true; + if (m_referenceContext->IsExtensionSupported(GL::Extension::ShaderImageLoadStore)) + { + m_deviceInfo.features.textureRead = true; + m_deviceInfo.features.textureWrite = true; + m_deviceInfo.features.textureWriteWithoutFormat = true; + } + + if (m_referenceContext->IsExtensionSupported(GL::Extension::ShaderImageLoadFormatted)) + m_deviceInfo.features.textureReadWithoutFormat = true; if (m_referenceContext->IsExtensionSupported(GL::Extension::TextureView)) m_deviceInfo.features.unrestrictedTextureViews = true; // Limits - GLint minUboOffsetAlignment; - m_referenceContext->glGetIntegerv(GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT, &minUboOffsetAlignment); - - assert(minUboOffsetAlignment >= 1); - m_deviceInfo.limits.minUniformBufferOffsetAlignment = static_cast(minUboOffsetAlignment); - - GLint maxUboBlockSize; - m_referenceContext->glGetIntegerv(GL_MAX_UNIFORM_BLOCK_SIZE, &maxUboBlockSize); - - assert(maxUboBlockSize >= 1); - m_deviceInfo.limits.maxUniformBufferSize = static_cast(maxUboBlockSize); + m_deviceInfo.limits.maxUniformBufferSize = m_referenceContext->GetInteger(GL_MAX_UNIFORM_BLOCK_SIZE); + m_deviceInfo.limits.minUniformBufferOffsetAlignment = m_referenceContext->GetInteger(GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT); if (m_deviceInfo.features.storageBuffers) { - GLint minStorageOffsetAlignment; - m_referenceContext->glGetIntegerv(GL_SHADER_STORAGE_BUFFER_OFFSET_ALIGNMENT, &minStorageOffsetAlignment); - - assert(minStorageOffsetAlignment >= 1); - m_deviceInfo.limits.minStorageBufferOffsetAlignment = static_cast(minStorageOffsetAlignment); - - GLint maxStorageBlockSize; - m_referenceContext->glGetIntegerv(GL_MAX_SHADER_STORAGE_BLOCK_SIZE, &maxStorageBlockSize); - - assert(maxStorageBlockSize >= 1); - m_deviceInfo.limits.maxStorageBufferSize = static_cast(maxStorageBlockSize); + m_deviceInfo.limits.maxStorageBufferSize = m_referenceContext->GetInteger(GL_MAX_SHADER_STORAGE_BLOCK_SIZE); + m_deviceInfo.limits.minStorageBufferOffsetAlignment = m_referenceContext->GetInteger(GL_SHADER_STORAGE_BUFFER_OFFSET_ALIGNMENT); + } + + if (m_deviceInfo.features.computeShaders) + { + m_deviceInfo.limits.maxComputeSharedMemorySize = m_referenceContext->GetInteger(GL_MAX_COMPUTE_SHARED_MEMORY_SIZE); + m_deviceInfo.limits.maxComputeWorkGroupInvocations = m_referenceContext->GetInteger(GL_MAX_COMPUTE_WORK_GROUP_INVOCATIONS); + + m_deviceInfo.limits.maxComputeWorkGroupCount = { + m_referenceContext->GetInteger(GL_MAX_COMPUTE_WORK_GROUP_COUNT, 0), + m_referenceContext->GetInteger(GL_MAX_COMPUTE_WORK_GROUP_COUNT, 1), + m_referenceContext->GetInteger(GL_MAX_COMPUTE_WORK_GROUP_COUNT, 2) + }; + + m_deviceInfo.limits.maxComputeWorkGroupSize = { + m_referenceContext->GetInteger(GL_MAX_COMPUTE_WORK_GROUP_SIZE, 0), + m_referenceContext->GetInteger(GL_MAX_COMPUTE_WORK_GROUP_SIZE, 1), + m_referenceContext->GetInteger(GL_MAX_COMPUTE_WORK_GROUP_SIZE, 2) + }; } - else - m_deviceInfo.limits.maxStorageBufferSize = 0; m_contexts.insert(m_referenceContext.get()); } @@ -154,6 +166,11 @@ namespace Nz return std::make_shared(); } + std::shared_ptr OpenGLDevice::InstantiateComputePipeline(ComputePipelineInfo pipelineInfo) + { + return std::make_shared(*this, std::move(pipelineInfo)); + } + std::shared_ptr OpenGLDevice::InstantiateFramebuffer(unsigned int /*width*/, unsigned int /*height*/, const std::shared_ptr& /*renderPass*/, const std::vector>& attachments) { return std::make_shared(*this, attachments); @@ -246,7 +263,7 @@ namespace Nz case PixelFormat::RGBA32F: case PixelFormat::RGBA32I: case PixelFormat::RGBA32UI: - return usage == TextureUsage::ColorAttachment || usage == TextureUsage::InputAttachment || usage == TextureUsage::ShaderSampling || usage == TextureUsage::TransferDestination || usage == TextureUsage::TransferSource; + return usage == TextureUsage::ColorAttachment || usage == TextureUsage::InputAttachment || usage == TextureUsage::ShaderSampling || usage == TextureUsage::ShaderReadWrite || usage == TextureUsage::TransferDestination || usage == TextureUsage::TransferSource; case PixelFormat::DXT1: case PixelFormat::DXT3: diff --git a/src/Nazara/OpenGLRenderer/OpenGLRenderPipeline.cpp b/src/Nazara/OpenGLRenderer/OpenGLRenderPipeline.cpp index 6039801aa..67d3a050f 100644 --- a/src/Nazara/OpenGLRenderer/OpenGLRenderPipeline.cpp +++ b/src/Nazara/OpenGLRenderer/OpenGLRenderPipeline.cpp @@ -88,7 +88,7 @@ namespace Nz if (location == -1) continue; - m_program.Uniform(location, static_cast(explicitBinding.binding)); //< FIXME: Use SafeCast + m_program.Uniform(location, SafeCast(explicitBinding.binding)); } } } diff --git a/src/Nazara/OpenGLRenderer/OpenGLRenderPipelineLayout.cpp b/src/Nazara/OpenGLRenderer/OpenGLRenderPipelineLayout.cpp index 8f39b1611..18cdba58b 100644 --- a/src/Nazara/OpenGLRenderer/OpenGLRenderPipelineLayout.cpp +++ b/src/Nazara/OpenGLRenderer/OpenGLRenderPipelineLayout.cpp @@ -96,6 +96,16 @@ namespace Nz return ShaderBindingPtr(PlacementNew(freeBindingMemory, *this, poolIndex, freeBindingId)); } + auto OpenGLRenderPipelineLayout::GetSampledTextureDescriptor(std::size_t poolIndex, std::size_t bindingIndex, std::size_t descriptorIndex) -> SampledTextureDescriptor& + { + assert(poolIndex < m_descriptorPools.size()); + auto& pool = m_descriptorPools[poolIndex]; + assert(!pool.freeBindings.Test(bindingIndex)); + assert(descriptorIndex < m_maxDescriptorCount); + + return pool.descriptors[bindingIndex * m_maxDescriptorCount + descriptorIndex].emplace(); + } + auto OpenGLRenderPipelineLayout::GetStorageBufferDescriptor(std::size_t poolIndex, std::size_t bindingIndex, std::size_t descriptorIndex) -> StorageBufferDescriptor& { assert(poolIndex < m_descriptorPools.size()); diff --git a/src/Nazara/OpenGLRenderer/OpenGLShaderBinding.cpp b/src/Nazara/OpenGLRenderer/OpenGLShaderBinding.cpp index ee19f5c81..bdcb8d443 100644 --- a/src/Nazara/OpenGLRenderer/OpenGLShaderBinding.cpp +++ b/src/Nazara/OpenGLRenderer/OpenGLShaderBinding.cpp @@ -45,7 +45,15 @@ namespace Nz UInt32 bindingPoint = bindingMappingIt->second; - if constexpr (std::is_same_v) + if constexpr (std::is_same_v) + { + if (bindingInfo.type != ShaderBindingType::Sampler) + throw std::runtime_error("descriptor (set=" + std::to_string(setIndex) + ", binding=" + std::to_string(bindingIndex) + ") is not a sampler"); + + context.BindSampler(bindingPoint, descriptor.sampler); + context.BindTexture(bindingPoint, descriptor.textureTarget, descriptor.texture); + } + else if constexpr (std::is_same_v) { if (bindingInfo.type != ShaderBindingType::StorageBuffer) throw std::runtime_error("descriptor (set=" + std::to_string(setIndex) + ", binding=" + std::to_string(bindingIndex) + ") is not a storage buffer"); @@ -57,8 +65,7 @@ namespace Nz if (bindingInfo.type != ShaderBindingType::Texture) throw std::runtime_error("descriptor (set=" + std::to_string(setIndex) + ", binding=" + std::to_string(bindingIndex) + ") is not a texture"); - context.BindSampler(bindingPoint, descriptor.sampler); - context.BindTexture(bindingPoint, descriptor.textureTarget, descriptor.texture); + context.BindImageTexture(bindingPoint, descriptor.texture, descriptor.level, descriptor.layered, descriptor.layer, descriptor.access, descriptor.format); } else if constexpr (std::is_same_v) { @@ -82,7 +89,14 @@ namespace Nz { using T = std::decay_t; - if constexpr (std::is_same_v) + if constexpr (std::is_same_v) + HandleTextureBinding(binding.bindingIndex, arg); + else if constexpr (std::is_same_v) + { + for (UInt32 i = 0; i < arg.arraySize; ++i) + HandleTextureBinding(binding.bindingIndex + i, arg.textureBindings[i]); + } + else if constexpr (std::is_same_v) { auto& storageDescriptor = m_owner.GetStorageBufferDescriptor(m_poolIndex, m_bindingIndex, binding.bindingIndex); storageDescriptor.offset = arg.offset; @@ -99,11 +113,41 @@ namespace Nz storageDescriptor.buffer = 0; } else if constexpr (std::is_same_v) - HandleTextureBinding(binding.bindingIndex, arg); - else if constexpr (std::is_same_v) { - for (UInt32 i = 0; i < arg.arraySize; ++i) - HandleTextureBinding(binding.bindingIndex + i, arg.textureBindings[i]); + auto& textureDescriptor = m_owner.GetTextureDescriptor(m_poolIndex, m_bindingIndex, binding.bindingIndex); + if (const OpenGLTexture* glTexture = static_cast(arg.texture)) + { + const TextureViewInfo& viewInfo = glTexture->GetTextureViewInfo(); + + std::optional format = DescribeTextureFormat(viewInfo.reinterpretFormat); + if (!format) + throw std::runtime_error("unexpected texture format"); + + textureDescriptor.access = ToOpenGL(arg.access); + textureDescriptor.format = format->internalFormat; + if (viewInfo.layerCount > 1) + { + textureDescriptor.layered = true; + textureDescriptor.layer = 0; + } + else + { + textureDescriptor.layered = false; + textureDescriptor.layer = viewInfo.baseArrayLayer; + } + + textureDescriptor.level = viewInfo.baseMipLevel; + textureDescriptor.texture = glTexture->GetTexture().GetObjectId(); + } + else + { + textureDescriptor.access = GL_READ_ONLY; + textureDescriptor.format = GL_RGBA8; + textureDescriptor.layer = 0; + textureDescriptor.layered = GL_FALSE; + textureDescriptor.level = 0; + textureDescriptor.texture = 0; + } } else if constexpr (std::is_same_v) { @@ -133,9 +177,9 @@ namespace Nz // No OpenGL object to name } - void OpenGLShaderBinding::HandleTextureBinding(UInt32 bindingIndex, const TextureBinding& textureBinding) + void OpenGLShaderBinding::HandleTextureBinding(UInt32 bindingIndex, const SampledTextureBinding& textureBinding) { - auto& textureDescriptor = m_owner.GetTextureDescriptor(m_poolIndex, m_bindingIndex, bindingIndex); + auto& textureDescriptor = m_owner.GetSampledTextureDescriptor(m_poolIndex, m_bindingIndex, bindingIndex); if (const OpenGLTexture* glTexture = static_cast(textureBinding.texture)) { diff --git a/src/Nazara/OpenGLRenderer/Wrapper/Context.cpp b/src/Nazara/OpenGLRenderer/Wrapper/Context.cpp index 80247c192..2a3159b00 100644 --- a/src/Nazara/OpenGLRenderer/Wrapper/Context.cpp +++ b/src/Nazara/OpenGLRenderer/Wrapper/Context.cpp @@ -172,6 +172,32 @@ namespace Nz::GL } } + void Context::BindImageTexture(GLuint imageUnit, GLuint texture, GLint level, GLboolean layered, GLint layer, GLenum access, GLenum format) const + { + if (imageUnit >= m_state.imageUnits.size()) + throw std::runtime_error("unsupported image unit #" + std::to_string(imageUnit)); + + layer = (layered == GL_TRUE) ? layer : 0; + + auto& unit = m_state.imageUnits[imageUnit]; + if (unit.texture != texture || unit.level != level || unit.layered != layered || unit.layer != layer || unit.access != access || unit.format != format) + { + if (!SetCurrentContext(this)) + throw std::runtime_error("failed to activate context"); + + if (!glBindImageTexture) + throw std::runtime_error("image binding is not supported"); + + glBindImageTexture(imageUnit, texture, level, layered, layer, access, format); + unit.access = access; + unit.format = format; + unit.layer = layer; + unit.layered = layered; + unit.level = level; + unit.texture = texture; + } + } + void Context::BindProgram(GLuint program) const { if (m_state.boundProgram != program) @@ -413,21 +439,13 @@ namespace Nz::GL return false; } - GLint majorVersion = 0; - glGetIntegerv(GL_MAJOR_VERSION, &majorVersion); + m_params.glMajorVersion = GetInteger(GL_MAJOR_VERSION); + m_params.glMinorVersion = GetInteger(GL_MINOR_VERSION); - GLint minorVersion = 0; - glGetIntegerv(GL_MINOR_VERSION, &minorVersion); - - m_params.glMajorVersion = majorVersion; - m_params.glMinorVersion = minorVersion; - - unsigned int glVersion = majorVersion * 100 + minorVersion * 10; + unsigned int glVersion = m_params.glMajorVersion * 100 + m_params.glMinorVersion * 10; // Load extensions - GLint extensionCount = 0; - glGetIntegerv(GL_NUM_EXTENSIONS, &extensionCount); - + GLint extensionCount = GetInteger(GL_NUM_EXTENSIONS); for (GLint i = 0; i < extensionCount; ++i) m_supportedExtensions.emplace(reinterpret_cast(glGetStringi(GL_EXTENSIONS, i))); @@ -441,6 +459,12 @@ namespace Nz::GL else if (m_supportedExtensions.count("GL_EXT_clip_control")) m_extensionStatus[UnderlyingCast(Extension::ClipControl)] = ExtensionStatus::EXT; + // Compute shaders + if ((m_params.type == ContextType::OpenGL && glVersion >= 430) || (m_params.type == ContextType::OpenGL_ES && glVersion >= 310)) + m_extensionStatus[UnderlyingCast(Extension::ComputeShader)] = ExtensionStatus::Core; + else if (m_supportedExtensions.count("GL_ARB_compute_shader")) + m_extensionStatus[UnderlyingCast(Extension::ComputeShader)] = ExtensionStatus::ARB; + // Debug output if ((m_params.type == ContextType::OpenGL && glVersion >= 430) || (m_params.type == ContextType::OpenGL_ES && glVersion >= 320)) m_extensionStatus[UnderlyingCast(Extension::DebugOutput)] = ExtensionStatus::Core; @@ -465,6 +489,18 @@ namespace Nz::GL else if (m_supportedExtensions.count("GL_NV_polygon_mode")) m_extensionStatus[UnderlyingCast(Extension::DepthClamp)] = ExtensionStatus::Vendor; + // Shader image load formatted + if (m_supportedExtensions.count("GL_EXT_shader_image_load_formatted")) + m_extensionStatus[UnderlyingCast(Extension::ShaderImageLoadFormatted)] = ExtensionStatus::EXT; + + // Shader image load/store + if ((m_params.type == ContextType::OpenGL && glVersion >= 420) || (m_params.type == ContextType::OpenGL_ES && glVersion >= 310)) + m_extensionStatus[UnderlyingCast(Extension::ShaderImageLoadStore)] = ExtensionStatus::Core; + else if (m_supportedExtensions.count("GL_ARB_shader_image_load_store")) + m_extensionStatus[UnderlyingCast(Extension::ShaderImageLoadStore)] = ExtensionStatus::ARB; + else if (m_supportedExtensions.count("GL_EXT_shader_image_load_store")) + m_extensionStatus[UnderlyingCast(Extension::ShaderImageLoadStore)] = ExtensionStatus::EXT; + // SPIR-V support if (m_params.type == ContextType::OpenGL && glVersion >= 460) m_extensionStatus[UnderlyingCast(Extension::SpirV)] = ExtensionStatus::Core; @@ -583,16 +619,14 @@ namespace Nz::GL else m_params.validationLevel = m_params.validationLevel; - GLint maxTextureUnits = -1; - glGetIntegerv(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, &maxTextureUnits); + unsigned int maxTextureUnits = GetInteger(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS); if (maxTextureUnits < 32) //< OpenGL ES 3.0 requires at least 32 textures units NazaraWarning("GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS is " + std::to_string(maxTextureUnits) + ", expected >= 32"); assert(maxTextureUnits > 0); m_state.textureUnits.resize(maxTextureUnits); - GLint maxUniformBufferUnits = -1; - glGetIntegerv(GL_MAX_UNIFORM_BUFFER_BINDINGS, &maxUniformBufferUnits); + unsigned int maxUniformBufferUnits = GetInteger(GL_MAX_UNIFORM_BUFFER_BINDINGS); if (maxUniformBufferUnits < 24) //< OpenGL ES 3.0 requires at least 24 uniform buffers units NazaraWarning("GL_MAX_UNIFORM_BUFFER_BINDINGS is " + std::to_string(maxUniformBufferUnits) + ", expected >= 24"); @@ -601,8 +635,7 @@ namespace Nz::GL if (IsExtensionSupported(Extension::StorageBuffers)) { - GLint maxStorageBufferUnits = -1; - glGetIntegerv(GL_MAX_SHADER_STORAGE_BUFFER_BINDINGS, &maxStorageBufferUnits); + unsigned int maxStorageBufferUnits = GetInteger(GL_MAX_SHADER_STORAGE_BUFFER_BINDINGS); if (maxStorageBufferUnits < 8) //< OpenGL ES 3.1 requires at least 8 storage buffers units NazaraWarning("GL_MAX_SHADER_STORAGE_BUFFER_BINDINGS is " + std::to_string(maxUniformBufferUnits) + ", expected >= 8"); diff --git a/src/Nazara/Renderer/ComputePipeline.cpp b/src/Nazara/Renderer/ComputePipeline.cpp new file mode 100644 index 000000000..f5f8c31e0 --- /dev/null +++ b/src/Nazara/Renderer/ComputePipeline.cpp @@ -0,0 +1,11 @@ +// Copyright (C) 2022 Jérôme "Lynix" Leclercq (lynix680@gmail.com) +// This file is part of the "Nazara Engine - Renderer module" +// For conditions of distribution and use, see copyright notice in Config.hpp + +#include +#include + +namespace Nz +{ + ComputePipeline::~ComputePipeline() = default; +} diff --git a/src/Nazara/Renderer/DebugDrawer.cpp b/src/Nazara/Renderer/DebugDrawer.cpp index 6c2edb08a..38209acdc 100644 --- a/src/Nazara/Renderer/DebugDrawer.cpp +++ b/src/Nazara/Renderer/DebugDrawer.cpp @@ -83,7 +83,7 @@ namespace Nz return; builder.BindShaderBinding(0, *m_currentViewerData.binding); - builder.BindPipeline(*m_renderPipeline); + builder.BindRenderPipeline(*m_renderPipeline); for (auto& drawCall : m_drawCalls) { diff --git a/src/Nazara/Renderer/RenderDevice.cpp b/src/Nazara/Renderer/RenderDevice.cpp index adddf0368..59461c83d 100644 --- a/src/Nazara/Renderer/RenderDevice.cpp +++ b/src/Nazara/Renderer/RenderDevice.cpp @@ -42,9 +42,14 @@ namespace Nz } NzValidateFeature(anisotropicFiltering, "anistropic filtering feature") + NzValidateFeature(computeShaders, "compute shaders feature") NzValidateFeature(depthClamping, "depth clamping feature") NzValidateFeature(nonSolidFaceFilling, "non-solid face filling feature") NzValidateFeature(storageBuffers, "storage buffers support") + NzValidateFeature(textureRead, "texture read") + NzValidateFeature(textureReadWithoutFormat, "texture read without format") + NzValidateFeature(textureWrite, "texture write") + NzValidateFeature(textureWriteWithoutFormat, "texture write without format") NzValidateFeature(unrestrictedTextureViews, "unrestricted texture view support") #undef NzValidateFeature diff --git a/src/Nazara/Utility/Formats/GIFLoader.cpp b/src/Nazara/Utility/Formats/GIFLoader.cpp index 9dbee6def..3c3c8044a 100644 --- a/src/Nazara/Utility/Formats/GIFLoader.cpp +++ b/src/Nazara/Utility/Formats/GIFLoader.cpp @@ -243,7 +243,7 @@ namespace Nz void Seek(UInt64 frameIndex) override { - assert(frameIndex < m_frames.size()); + assert(frameIndex <= m_frames.size()); if (m_requiresFrameHistory) { diff --git a/src/Nazara/VulkanRenderer/Vulkan.cpp b/src/Nazara/VulkanRenderer/Vulkan.cpp index c190e4a8f..8e092e376 100644 --- a/src/Nazara/VulkanRenderer/Vulkan.cpp +++ b/src/Nazara/VulkanRenderer/Vulkan.cpp @@ -54,11 +54,20 @@ namespace Nz deviceInfo.name = physDevice.properties.deviceName; deviceInfo.features.anisotropicFiltering = physDevice.features.samplerAnisotropy; + deviceInfo.features.computeShaders = true; deviceInfo.features.depthClamping = physDevice.features.depthClamp; deviceInfo.features.nonSolidFaceFilling = physDevice.features.fillModeNonSolid; deviceInfo.features.storageBuffers = true; + deviceInfo.features.textureRead = true; + deviceInfo.features.textureReadWithoutFormat = physDevice.features.shaderStorageImageReadWithoutFormat; + deviceInfo.features.textureWrite = true; + deviceInfo.features.textureWriteWithoutFormat = physDevice.features.shaderStorageImageWriteWithoutFormat; deviceInfo.features.unrestrictedTextureViews = true; + deviceInfo.limits.maxComputeSharedMemorySize = physDevice.properties.limits.maxComputeSharedMemorySize; + deviceInfo.limits.maxComputeWorkGroupCount = { physDevice.properties.limits.maxComputeWorkGroupCount[0], physDevice.properties.limits.maxComputeWorkGroupCount[1], physDevice.properties.limits.maxComputeWorkGroupCount[2] }; + deviceInfo.limits.maxComputeWorkGroupSize = { physDevice.properties.limits.maxComputeWorkGroupSize[0], physDevice.properties.limits.maxComputeWorkGroupSize[1], physDevice.properties.limits.maxComputeWorkGroupSize[2] }; + deviceInfo.limits.maxComputeWorkGroupInvocations = physDevice.properties.limits.maxComputeWorkGroupInvocations; deviceInfo.limits.maxStorageBufferSize = physDevice.properties.limits.maxStorageBufferRange; deviceInfo.limits.maxUniformBufferSize = physDevice.properties.limits.maxUniformBufferRange; deviceInfo.limits.minStorageBufferOffsetAlignment = physDevice.properties.limits.minStorageBufferOffsetAlignment; diff --git a/src/Nazara/VulkanRenderer/VulkanCommandBufferBuilder.cpp b/src/Nazara/VulkanRenderer/VulkanCommandBufferBuilder.cpp index 70b2c5ead..bc6b2aa51 100644 --- a/src/Nazara/VulkanRenderer/VulkanCommandBufferBuilder.cpp +++ b/src/Nazara/VulkanRenderer/VulkanCommandBufferBuilder.cpp @@ -5,6 +5,7 @@ #include #include #include +#include #include #include #include @@ -72,6 +73,13 @@ namespace Nz m_currentSubpassIndex = 0; } + void VulkanCommandBufferBuilder::BindComputePipeline(const ComputePipeline& pipeline) + { + const VulkanComputePipeline& vkPipeline = static_cast(pipeline); + + m_commandBuffer.BindPipeline(VK_PIPELINE_BIND_POINT_COMPUTE, vkPipeline.GetPipeline()); + } + void VulkanCommandBufferBuilder::BindIndexBuffer(const RenderBuffer& indexBuffer, IndexType indexType, UInt64 offset) { const VulkanBuffer& vkBuffer = static_cast(indexBuffer); @@ -79,14 +87,14 @@ namespace Nz m_commandBuffer.BindIndexBuffer(vkBuffer.GetBuffer(), offset, ToVulkan(indexType)); } - void VulkanCommandBufferBuilder::BindPipeline(const RenderPipeline& pipeline) + void VulkanCommandBufferBuilder::BindRenderPipeline(const RenderPipeline& pipeline) { if (!m_currentRenderPass) throw std::runtime_error("BindPipeline must be called in a RenderPass"); - const VulkanRenderPipeline& vkBinding = static_cast(pipeline); + const VulkanRenderPipeline& vkPipeline = static_cast(pipeline); - m_commandBuffer.BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, vkBinding.Get(*m_currentRenderPass, m_currentSubpassIndex)); + m_commandBuffer.BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, vkPipeline.Get(*m_currentRenderPass, m_currentSubpassIndex)); } void VulkanCommandBufferBuilder::BindShaderBinding(UInt32 set, const ShaderBinding& binding) @@ -207,6 +215,11 @@ namespace Nz m_commandBuffer.CopyImage(vkFromTexture.GetImage(), ToVulkan(fromLayout), vkToTexture.GetImage(), ToVulkan(toLayout), region); } + void VulkanCommandBufferBuilder::Dispatch(UInt32 workgroupX, UInt32 workgroupY, UInt32 workgroupZ) + { + m_commandBuffer.Dispatch(workgroupX, workgroupY, workgroupZ); + } + void VulkanCommandBufferBuilder::Draw(UInt32 vertexCount, UInt32 instanceCount, UInt32 firstVertex, UInt32 firstInstance) { m_commandBuffer.Draw(vertexCount, instanceCount, firstVertex, firstInstance); diff --git a/src/Nazara/VulkanRenderer/VulkanComputePipeline.cpp b/src/Nazara/VulkanRenderer/VulkanComputePipeline.cpp new file mode 100644 index 000000000..3c878b57f --- /dev/null +++ b/src/Nazara/VulkanRenderer/VulkanComputePipeline.cpp @@ -0,0 +1,61 @@ +// Copyright (C) 2022 Jérôme "Lynix" Leclercq (lynix680@gmail.com) +// This file is part of the "Nazara Engine - Vulkan renderer" +// For conditions of distribution and use, see copyright notice in Config.hpp + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace Nz +{ + VulkanComputePipeline::VulkanComputePipeline(VulkanDevice& device, ComputePipelineInfo pipelineInfo) : + m_pipelineInfo(std::move(pipelineInfo)) + { + if (device.GetEnabledFeatures().computeShaders) + throw std::runtime_error("compute shaders are not enabled on the device"); + + VulkanShaderModule& vulkanModule = static_cast(*m_pipelineInfo.shaderModule); + const VulkanShaderModule::Stage* computeStage = nullptr; + for (const auto& stage : vulkanModule.GetStages()) + { + if (stage.stage != nzsl::ShaderStageType::Compute) + continue; + + if (computeStage) + throw std::runtime_error("multiple compute stages found in shader module"); + + computeStage = &stage; + } + + if (!computeStage) + throw std::runtime_error("no compute stages found in shader module"); + + VkComputePipelineCreateInfo createInfo = {}; + createInfo.sType = VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO; + createInfo.stage.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; + createInfo.stage.module = vulkanModule.GetHandle(); + createInfo.stage.pName = computeStage->name.data(); + createInfo.stage.stage = ToVulkan(computeStage->stage); + + VulkanRenderPipelineLayout& pipelineLayout = *static_cast(m_pipelineInfo.pipelineLayout.get()); + createInfo.layout = pipelineLayout.GetPipelineLayout(); + + if (!m_pipeline.CreateCompute(device, createInfo)) + throw std::runtime_error("failed to create compute pipeline: " + TranslateVulkanError(m_pipeline.GetLastErrorCode())); + } + + void VulkanComputePipeline::UpdateDebugName(std::string_view name) + { + m_pipeline.SetDebugName(name); + } +} + +#if defined(NAZARA_PLATFORM_WINDOWS) +#include +#endif diff --git a/src/Nazara/VulkanRenderer/VulkanDevice.cpp b/src/Nazara/VulkanRenderer/VulkanDevice.cpp index c4d757b77..b70b582fd 100644 --- a/src/Nazara/VulkanRenderer/VulkanDevice.cpp +++ b/src/Nazara/VulkanRenderer/VulkanDevice.cpp @@ -4,6 +4,7 @@ #include #include +#include #include #include #include @@ -37,6 +38,11 @@ namespace Nz return std::make_shared(*this, queueType); } + std::shared_ptr VulkanDevice::InstantiateComputePipeline(ComputePipelineInfo pipelineInfo) + { + return std::make_shared(*this, std::move(pipelineInfo)); + } + std::shared_ptr VulkanDevice::InstantiateFramebuffer(unsigned int width, unsigned int height, const std::shared_ptr& renderPass, const std::vector>& attachments) { return std::make_shared(*this, width, height, renderPass, attachments); @@ -111,6 +117,10 @@ namespace Nz flags = VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT; break; + case TextureUsage::ShaderReadWrite: + flags = VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT; + break; + case TextureUsage::TransferSource: flags = VK_FORMAT_FEATURE_TRANSFER_SRC_BIT; break; diff --git a/src/Nazara/VulkanRenderer/VulkanShaderBinding.cpp b/src/Nazara/VulkanRenderer/VulkanShaderBinding.cpp index f1361d78a..a0d4702d5 100644 --- a/src/Nazara/VulkanRenderer/VulkanShaderBinding.cpp +++ b/src/Nazara/VulkanRenderer/VulkanShaderBinding.cpp @@ -27,9 +27,9 @@ namespace Nz if constexpr (std::is_same_v || std::is_same_v) bufferBindingCount++; - else if constexpr (std::is_same_v) + else if constexpr (std::is_same_v || std::is_same_v) imageBindingCount++; - else if constexpr (std::is_same_v) + else if constexpr (std::is_same_v) imageBindingCount += arg.arraySize; else static_assert(AlwaysFalse(), "non-exhaustive visitor"); @@ -58,20 +58,7 @@ namespace Nz { using T = std::decay_t; - if constexpr (std::is_same_v) - { - VulkanBuffer* vkBuffer = SafeCast(arg.buffer); - - VkDescriptorBufferInfo& bufferInfo = bufferBinding.emplace_back(); - bufferInfo.buffer = (vkBuffer) ? vkBuffer->GetBuffer() : VK_NULL_HANDLE; - bufferInfo.offset = arg.offset; - bufferInfo.range = arg.range; - - writeOp.descriptorCount = 1; - writeOp.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER; - writeOp.pBufferInfo = &bufferInfo; - } - else if constexpr (std::is_same_v) + if constexpr (std::is_same_v) { const VulkanTexture* vkTexture = SafeCast(arg.texture); const VulkanTextureSampler* vkSampler = SafeCast(arg.sampler); @@ -85,7 +72,7 @@ namespace Nz writeOp.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; writeOp.pImageInfo = &imageInfo; } - else if constexpr (std::is_same_v) + else if constexpr (std::is_same_v) { for (UInt32 i = 0; i < arg.arraySize; ++i) { @@ -102,6 +89,32 @@ namespace Nz writeOp.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; writeOp.pImageInfo = &imageBinding[imageBinding.size() - arg.arraySize]; } + else if constexpr (std::is_same_v) + { + VulkanBuffer* vkBuffer = SafeCast(arg.buffer); + + VkDescriptorBufferInfo& bufferInfo = bufferBinding.emplace_back(); + bufferInfo.buffer = (vkBuffer) ? vkBuffer->GetBuffer() : VK_NULL_HANDLE; + bufferInfo.offset = arg.offset; + bufferInfo.range = arg.range; + + writeOp.descriptorCount = 1; + writeOp.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER; + writeOp.pBufferInfo = &bufferInfo; + } + else if constexpr (std::is_same_v) + { + const VulkanTexture* vkTexture = SafeCast(arg.texture); + + VkDescriptorImageInfo& imageInfo = imageBinding.emplace_back(); + imageInfo.imageLayout = VK_IMAGE_LAYOUT_GENERAL; + imageInfo.imageView = (vkTexture) ? vkTexture->GetImageView() : VK_NULL_HANDLE; + imageInfo.sampler = VK_NULL_HANDLE; + + writeOp.descriptorCount = 1; + writeOp.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE; + writeOp.pImageInfo = &imageInfo; + } else if constexpr (std::is_same_v) { VulkanBuffer* vkBuffer = static_cast(arg.buffer);