From fe8715f1fb864cbdb8c523aa37f2d5b195e530b1 Mon Sep 17 00:00:00 2001 From: SirLynix Date: Sun, 25 Dec 2022 16:08:35 +0100 Subject: [PATCH] Renderer: Working compute implementation --- examples/ComputeTest/main.cpp | 397 ++++++++++++++++-- examples/DeferredShading/main.cpp | 22 +- examples/RenderTest/main.cpp | 4 +- .../OpenGLRenderer/OpenGLCommandBuffer.hpp | 25 +- .../OpenGLRenderer/OpenGLCommandBuffer.inl | 36 +- .../OpenGLCommandBufferBuilder.hpp | 6 +- .../Nazara/OpenGLRenderer/OpenGLTexture.hpp | 13 +- .../Nazara/OpenGLRenderer/OpenGLTexture.inl | 30 ++ .../Nazara/Renderer/CommandBufferBuilder.hpp | 6 +- include/Nazara/Renderer/Enums.hpp | 2 + include/Nazara/Renderer/RenderDeviceInfo.hpp | 3 +- include/Nazara/Renderer/Texture.hpp | 5 +- include/Nazara/VulkanRenderer/Utils.inl | 2 + .../VulkanCommandBufferBuilder.hpp | 6 +- .../Nazara/VulkanRenderer/VulkanTexture.hpp | 13 +- .../Nazara/VulkanRenderer/VulkanTexture.inl | 30 ++ src/Nazara/Graphics/ForwardFramePipeline.cpp | 4 +- src/Nazara/Graphics/Graphics.cpp | 3 +- src/Nazara/Graphics/SpriteChainRenderer.cpp | 2 +- src/Nazara/Graphics/SubmeshRenderer.cpp | 2 +- .../OpenGLRenderer/OpenGLCommandBuffer.cpp | 17 +- .../OpenGLCommandBufferBuilder.cpp | 42 +- src/Nazara/OpenGLRenderer/OpenGLDevice.cpp | 3 +- .../OpenGLRenderer/OpenGLShaderBinding.cpp | 24 +- src/Nazara/OpenGLRenderer/OpenGLTexture.cpp | 25 -- src/Nazara/OpenGLRenderer/Wrapper/Context.cpp | 7 + src/Nazara/Renderer/DebugDrawer.cpp | 2 +- src/Nazara/Renderer/RenderDevice.cpp | 3 +- src/Nazara/VulkanRenderer/Vulkan.cpp | 3 +- .../VulkanCommandBufferBuilder.cpp | 20 +- src/Nazara/VulkanRenderer/VulkanTexture.cpp | 25 -- 31 files changed, 615 insertions(+), 167 deletions(-) diff --git a/examples/ComputeTest/main.cpp b/examples/ComputeTest/main.cpp index 0dcd879cd..abb6f0b3e 100644 --- a/examples/ComputeTest/main.cpp +++ b/examples/ComputeTest/main.cpp @@ -12,7 +12,183 @@ NAZARA_REQUEST_DEDICATED_GPU() -const char shaderSource[] = R"( +struct ComputePipeline +{ + std::shared_ptr layout; + std::shared_ptr pipeline; +}; + +struct SpriteRenderData +{ + std::shared_ptr vertexBuffer; + std::shared_ptr shaderBinding; +}; + +struct SpriteRenderPipeline +{ + std::shared_ptr pipeline; + std::shared_ptr pipelineLayout; + +}; + +ComputePipeline BuildComputePipeline(Nz::RenderDevice& device); +SpriteRenderPipeline BuildSpritePipeline(Nz::RenderDevice& device); +SpriteRenderData BuildSpriteData(Nz::RenderDevice& device, const SpriteRenderPipeline& pipelineData, const Nz::Rectf& textureRect, const Nz::Vector2f& screenSize, const Nz::Texture& texture, const Nz::TextureSampler& sampler); + +int main() +{ + std::filesystem::path resourceDir = "assets/examples"; + if (!std::filesystem::is_directory(resourceDir) && std::filesystem::is_directory("../.." / resourceDir)) + resourceDir = "../.." / resourceDir; + + Nz::Renderer::Config rendererConfig; + std::cout << "Run using Vulkan? (y/n)" << std::endl; + if (std::getchar() == 'y') + rendererConfig.preferredAPI = Nz::RenderAPI::Vulkan; + else + rendererConfig.preferredAPI = Nz::RenderAPI::OpenGL; + + Nz::Modules nazara(rendererConfig); + + + Nz::RenderDeviceFeatures enabledFeatures; + enabledFeatures.computeShaders = true; + enabledFeatures.textureReadWrite = true; + + std::shared_ptr device = Nz::Renderer::Instance()->InstanciateRenderDevice(0, enabledFeatures); + + // Source texture + Nz::TextureParams texParams; + texParams.renderDevice = device; + texParams.loadFormat = Nz::PixelFormat::RGBA8; + texParams.usageFlags = Nz::TextureUsage::ShaderReadWrite | Nz::TextureUsage::ShaderSampling | Nz::TextureUsage::TransferDestination; + + std::shared_ptr texture = Nz::Texture::LoadFromFile(resourceDir / "lynix.jpg", texParams); + + // Destination texture + Nz::TextureInfo destTexParams = texture->GetTextureInfo(); + destTexParams.usageFlags = Nz::TextureUsage::ShaderReadWrite | Nz::TextureUsage::ShaderSampling; + + std::shared_ptr targetTexture = device->InstantiateTexture(destTexParams); + std::shared_ptr textureSampler = device->InstantiateTextureSampler({}); + + // Compute part + ComputePipeline computePipeline = BuildComputePipeline(*device); + std::shared_ptr computeBinding = computePipeline.layout->AllocateShaderBinding(0); + computeBinding->Update({ + { + 0, + Nz::ShaderBinding::TextureBinding { + texture.get(), + Nz::TextureAccess::ReadOnly + } + }, + { + 1, + Nz::ShaderBinding::TextureBinding { + targetTexture.get(), + Nz::TextureAccess::WriteOnly + } + } + }); + + std::string windowTitle = "Compute test"; + + Nz::RenderWindow window; + if (!window.Create(device, Nz::VideoMode(1280, 720, 32), windowTitle)) + { + std::cout << "Failed to create Window" << std::endl; + std::abort(); + } + + Nz::Vector2ui windowSize = window.GetSize(); + constexpr float textureSize = 512.f; + float margin = (windowSize.y - textureSize) * 0.5f; + + SpriteRenderPipeline spriteRenderPipeline = BuildSpritePipeline(*device); + SpriteRenderData spriteRenderData1 = BuildSpriteData(*device, spriteRenderPipeline, Nz::Rectf(margin, windowSize.y - margin - textureSize, textureSize, textureSize), Nz::Vector2f(windowSize), *texture, *textureSampler); + SpriteRenderData spriteRenderData2 = BuildSpriteData(*device, spriteRenderPipeline, Nz::Rectf(windowSize.x - textureSize - margin, windowSize.y - margin - textureSize, textureSize, textureSize), Nz::Vector2f(windowSize), *targetTexture, *textureSampler); + + Nz::Clock secondClock; + unsigned int fps = 0; + + while (window.IsOpen()) + { + window.ProcessEvents(); + + Nz::RenderFrame frame = window.AcquireFrame(); + if (!frame) + { + std::this_thread::sleep_for(std::chrono::milliseconds(1)); + continue; + } + + const Nz::RenderTarget* windowRT = window.GetRenderTarget(); + frame.Execute([&](Nz::CommandBufferBuilder& builder) + { + builder.BeginDebugRegion("Compute part", Nz::Color::Blue); + { + builder.TextureBarrier(Nz::PipelineStage::FragmentShader, Nz::PipelineStage::ComputeShader, Nz::MemoryAccess::ShaderRead, Nz::MemoryAccess::ShaderRead, Nz::TextureLayout::ColorInput, Nz::TextureLayout::General, *texture); + builder.TextureBarrier(Nz::PipelineStage::FragmentShader, Nz::PipelineStage::ComputeShader, Nz::MemoryAccess::ShaderRead, Nz::MemoryAccess::ShaderWrite, Nz::TextureLayout::Undefined, Nz::TextureLayout::General, *targetTexture); + + builder.BindComputePipeline(*computePipeline.pipeline); + builder.BindComputeShaderBinding(0, *computeBinding); + builder.Dispatch(destTexParams.width / 32, destTexParams.height / 32, 1); + + builder.TextureBarrier(Nz::PipelineStage::ComputeShader, Nz::PipelineStage::FragmentShader, Nz::MemoryAccess::ShaderRead, Nz::MemoryAccess::ShaderRead, Nz::TextureLayout::General, Nz::TextureLayout::ColorInput, *texture); + builder.TextureBarrier(Nz::PipelineStage::ComputeShader, Nz::PipelineStage::FragmentShader, Nz::MemoryAccess::ShaderWrite, Nz::MemoryAccess::ShaderRead, Nz::TextureLayout::General, Nz::TextureLayout::ColorInput, *targetTexture); + } + builder.EndDebugRegion(); + + builder.BeginDebugRegion("Main window rendering", Nz::Color::Green); + { + Nz::Recti renderRect(0, 0, window.GetSize().x, window.GetSize().y); + + Nz::CommandBufferBuilder::ClearValues clearValues[2]; + clearValues[0].color = Nz::Color::Gray; + clearValues[1].depth = 1.f; + clearValues[1].stencil = 0; + + builder.BeginRenderPass(windowRT->GetFramebuffer(frame.GetFramebufferIndex()), windowRT->GetRenderPass(), renderRect, { clearValues[0], clearValues[1] }); + { + builder.SetScissor(Nz::Recti{ 0, 0, int(windowSize.x), int(windowSize.y) }); + builder.SetViewport(Nz::Recti{ 0, 0, int(windowSize.x), int(windowSize.y) }); + + builder.BindRenderPipeline(*spriteRenderPipeline.pipeline); + + builder.BindVertexBuffer(0, *spriteRenderData1.vertexBuffer); + builder.BindRenderShaderBinding(0, *spriteRenderData1.shaderBinding); + builder.Draw(4); + + builder.BindVertexBuffer(0, *spriteRenderData2.vertexBuffer); + builder.BindRenderShaderBinding(0, *spriteRenderData2.shaderBinding); + builder.Draw(4); + } + builder.EndRenderPass(); + } + builder.EndDebugRegion(); + + }, Nz::QueueType::Graphics); + + frame.Present(); + + fps++; + + if (secondClock.GetMilliseconds() >= 1000) + { + window.SetTitle(windowTitle + " - " + Nz::NumberToString(fps) + " FPS"); + + fps = 0; + + secondClock.Restart(); + } + } + + return EXIT_SUCCESS; +} + +// Edge detection, translated to NZSL from Sascha Willems compute shader example +const char computeSource[] = R"( [nzsl_version("1.0")] module; @@ -28,26 +204,57 @@ struct Input } [entry(compute)] -[workgroup(16, 16, 1)] +[workgroup(32, 32, 1)] fn main(input: Input) { let indices = vec2[i32](input.global_invocation_id.xy); - let color = input_tex.Read(indices); - output_tex.Write(indices, color); + + // Fetch neighbouring texels + let avg: array[f32, 9]; + + let n = 0; + [unroll] + for i in -1 -> 2 + { + [unroll] + for j in -1 -> 2 + { + let rgb = input_tex.Read(indices + vec2[i32](i, j)).rgb; + avg[n] = (rgb.r + rgb.b + rgb.b) / 3.0; + n += 1; + } + } + + let kernel: array[f32, 9]; + [unroll] + for i in 0 -> 9 + { + if (i == 4) + kernel[i] = 1.0; + else + kernel[i] = -1.0/8.0; + } + + let res = vec4[f32](conv(kernel, avg, 0.1, 0.0).rrr, 1.0); + output_tex.Write(indices, res); +} + +fn conv(kernel: array[f32, 9], data: array[f32, 9], denom: f32, offset: f32) -> f32 +{ + let res = 0.0; + [unroll] + for i in 0 -> 9 + res += kernel[i] * data[i]; + + return clamp(res/denom + offset, 0.0, 1.0); } )"; -struct ComputePipeline -{ - std::shared_ptr layout; - std::shared_ptr pipeline; -}; - ComputePipeline BuildComputePipeline(Nz::RenderDevice& device) { try { - nzsl::Ast::ModulePtr shaderModule = nzsl::Parse(std::string_view(shaderSource, sizeof(shaderSource))); + nzsl::Ast::ModulePtr shaderModule = nzsl::Parse(std::string_view(computeSource, sizeof(computeSource))); if (!shaderModule) { std::cout << "Failed to parse shader module" << std::endl; @@ -103,30 +310,148 @@ ComputePipeline BuildComputePipeline(Nz::RenderDevice& device) } } -int main() +const char fragVertSource[] = R"( +[nzsl_version("1.0")] +module; + +external { - std::filesystem::path resourceDir = "assets/examples"; - if (!std::filesystem::is_directory(resourceDir) && std::filesystem::is_directory("../.." / resourceDir)) - resourceDir = "../.." / resourceDir; - - Nz::Renderer::Config rendererConfig; - std::cout << "Run using Vulkan? (y/n)" << std::endl; - if (std::getchar() == 'y') - rendererConfig.preferredAPI = Nz::RenderAPI::Vulkan; - else - rendererConfig.preferredAPI = Nz::RenderAPI::OpenGL; - - Nz::Modules nazara(rendererConfig); - - Nz::RenderDeviceFeatures enabledFeatures; - enabledFeatures.computeShaders = true; - enabledFeatures.textureRead = true; - enabledFeatures.textureWrite = true; - - std::shared_ptr device = Nz::Renderer::Instance()->InstanciateRenderDevice(0, enabledFeatures); - - ComputePipeline result = BuildComputePipeline(*device); - - - return EXIT_SUCCESS; + [binding(0)] texture: sampler2D[f32] +} + +struct FragOut +{ + [location(0)] color: vec4[f32] +} + +struct VertIn +{ + [location(0)] pos: vec2[f32], + [location(1)] uv: vec2[f32] +} + +struct VertOut +{ + [location(0)] uv: vec2[f32], + [builtin(position)] pos: vec4[f32] +} + +[entry(frag)] +fn main(input: VertOut) -> FragOut +{ + let output: FragOut; + output.color = texture.Sample(input.uv); + + return output; +} + +[entry(vert)] +fn main(input: VertIn) -> VertOut +{ + let output: VertOut; + output.pos = vec4[f32](input.pos, 0.0, 1.0); + output.uv = input.uv; + return output; +} +)"; + +SpriteRenderPipeline BuildSpritePipeline(Nz::RenderDevice& device) +{ + try + { + nzsl::Ast::ModulePtr shaderModule = nzsl::Parse(std::string_view(fragVertSource, sizeof(fragVertSource))); + if (!shaderModule) + { + std::cout << "Failed to parse shader module" << std::endl; + std::abort(); + } + + nzsl::ShaderWriter::States states; + states.optimize = true; + + auto fragVertShader = device.InstantiateShaderModule(nzsl::ShaderStageType::Fragment | nzsl::ShaderStageType::Vertex, *shaderModule, states); + if (!fragVertShader) + { + std::cout << "Failed to instantiate shader" << std::endl; + std::abort(); + } + + std::shared_ptr vertexDeclaration = Nz::VertexDeclaration::Get(Nz::VertexLayout::XY_UV); + + SpriteRenderPipeline pipelineData; + + Nz::RenderPipelineLayoutInfo pipelineLayoutInfo; + pipelineLayoutInfo.bindings.push_back({ + 0, 0, 1, + Nz::ShaderBindingType::Sampler, + nzsl::ShaderStageType::Fragment + }); + + pipelineData.pipelineLayout = device.InstantiateRenderPipelineLayout(std::move(pipelineLayoutInfo)); + + Nz::RenderPipelineInfo pipelineInfo; + pipelineInfo.primitiveMode = Nz::PrimitiveMode::TriangleStrip; + pipelineInfo.pipelineLayout = pipelineData.pipelineLayout; + pipelineInfo.shaderModules.push_back(fragVertShader); + pipelineInfo.vertexBuffers.push_back({ + 0, vertexDeclaration + }); + + pipelineData.pipeline = device.InstantiateRenderPipeline(std::move(pipelineInfo)); + + return pipelineData; + } + catch (const std::exception& e) + { + std::cerr << e.what() << std::endl; + std::abort(); + } +} + +SpriteRenderData BuildSpriteData(Nz::RenderDevice& device, const SpriteRenderPipeline& pipelineData, const Nz::Rectf& textureRect, const Nz::Vector2f& screenSize, const Nz::Texture& texture, const Nz::TextureSampler& sampler) +{ + try + { + auto ToClipSpace = [&](Nz::Vector2f pos) -> Nz::Vector2f + { + // From 0..size to 0..1 + pos /= screenSize; + // From 0..1 to -1..1 + pos *= 2.f; + pos -= Nz::Vector2f(1.f, 1.f); + // Reverse Y + pos.y = -pos.y; + return pos; + }; + + std::array pos; + pos[0].position = ToClipSpace(textureRect.GetCorner(Nz::RectCorner::LeftBottom)); + pos[0].uv = Nz::Vector2f(0.f, 0.f); + pos[1].position = ToClipSpace(textureRect.GetCorner(Nz::RectCorner::LeftTop)); + pos[1].uv = Nz::Vector2f(0.f, 1.f); + pos[2].position = ToClipSpace(textureRect.GetCorner(Nz::RectCorner::RightBottom)); + pos[2].uv = Nz::Vector2f(1.f, 0.f); + pos[3].position = ToClipSpace(textureRect.GetCorner(Nz::RectCorner::RightTop)); + pos[3].uv = Nz::Vector2f(1.f, 1.f); + + SpriteRenderData renderData; + renderData.vertexBuffer = device.InstantiateBuffer(Nz::BufferType::Vertex, 4 * 4 * sizeof(float), Nz::BufferUsage::DeviceLocal, pos.data()); + + renderData.shaderBinding = pipelineData.pipelineLayout->AllocateShaderBinding(0); + renderData.shaderBinding->Update({ + { + 0, + Nz::ShaderBinding::SampledTextureBinding { + &texture, &sampler + } + } + }); + + return renderData; + } + catch (const std::exception& e) + { + std::cerr << e.what() << std::endl; + std::abort(); + } } diff --git a/examples/DeferredShading/main.cpp b/examples/DeferredShading/main.cpp index 2db636cc6..5dd4be0f6 100644 --- a/examples/DeferredShading/main.cpp +++ b/examples/DeferredShading/main.cpp @@ -831,11 +831,11 @@ int main() builder.BindIndexBuffer(*coneMeshGfx->GetIndexBuffer(0).get(), Nz::IndexType::U16); builder.BindVertexBuffer(0, *coneMeshGfx->GetVertexBuffer(0).get()); - builder.BindShaderBinding(0, *gbufferShaderBinding); + builder.BindRenderShaderBinding(0, *gbufferShaderBinding); for (std::size_t i = 0; i < spotLights.size(); ++i) { - builder.BindShaderBinding(1, *lightingShaderBindings[i]); + builder.BindRenderShaderBinding(1, *lightingShaderBindings[i]); builder.BindRenderPipeline(*stencilPipeline); builder.DrawIndexed(coneMeshGfx->GetIndexCount(0)); @@ -858,7 +858,7 @@ int main() builder.SetScissor(env.renderRect); builder.SetViewport(env.renderRect); - builder.BindShaderBinding(0, *skyboxShaderBinding); + builder.BindRenderShaderBinding(0, *skyboxShaderBinding); builder.BindIndexBuffer(*cubeMeshGfx->GetIndexBuffer(0), Nz::IndexType::U16); builder.BindVertexBuffer(0, *cubeMeshGfx->GetVertexBuffer(0)); @@ -930,7 +930,7 @@ int main() builder.SetScissor(env.renderRect); builder.SetViewport(env.renderRect); - builder.BindShaderBinding(0, *godRaysShaderBinding); + builder.BindRenderShaderBinding(0, *godRaysShaderBinding); builder.BindRenderPipeline(*godraysPipeline); @@ -946,7 +946,7 @@ int main() builder.SetScissor(env.renderRect); builder.SetViewport(env.renderRect); - builder.BindShaderBinding(0, *bloomBrightShaderBinding); + builder.BindRenderShaderBinding(0, *bloomBrightShaderBinding); builder.BindRenderPipeline(*bloomBrightPipeline); @@ -969,7 +969,7 @@ int main() builder.SetScissor(env.renderRect); builder.SetViewport(env.renderRect); - builder.BindShaderBinding(0, *gaussianBlurShaderBinding[i * 2 + 0]); + builder.BindRenderShaderBinding(0, *gaussianBlurShaderBinding[i * 2 + 0]); builder.BindRenderPipeline(*gaussianBlurPipeline); builder.Draw(3); @@ -989,7 +989,7 @@ int main() builder.SetScissor(env.renderRect); builder.SetViewport(env.renderRect); - builder.BindShaderBinding(0, *gaussianBlurShaderBinding[i * 2 + 1]); + builder.BindRenderShaderBinding(0, *gaussianBlurShaderBinding[i * 2 + 1]); builder.BindRenderPipeline(*gaussianBlurPipeline); builder.Draw(3); @@ -1014,12 +1014,12 @@ int main() builder.BindRenderPipeline(*bloomBlendPipeline); for (std::size_t i = 0; i < BloomSubdivisionCount; ++i) { - builder.BindShaderBinding(0, *bloomBlendShaderBinding[i]); + builder.BindRenderShaderBinding(0, *bloomBlendShaderBinding[i]); builder.Draw(3); } // God rays - builder.BindShaderBinding(0, *godRaysBlitShaderBinding); + builder.BindRenderShaderBinding(0, *godRaysBlitShaderBinding); builder.Draw(3); }); @@ -1045,7 +1045,7 @@ int main() builder.SetScissor(env.renderRect); builder.SetViewport(env.renderRect); - builder.BindShaderBinding(0, *toneMappingShaderBinding); + builder.BindRenderShaderBinding(0, *toneMappingShaderBinding); builder.BindRenderPipeline(*toneMappingPipeline); builder.Draw(3); @@ -1547,7 +1547,7 @@ int main() builder.SetScissor(Nz::Recti{ 0, 0, int(windowSize.x), int(windowSize.y) }); builder.SetViewport(Nz::Recti{ 0, 0, int(windowSize.x), int(windowSize.y) }); - builder.BindShaderBinding(0, *finalBlitBinding); + builder.BindRenderShaderBinding(0, *finalBlitBinding); builder.BindRenderPipeline(*fullscreenPipeline); builder.Draw(3); diff --git a/examples/RenderTest/main.cpp b/examples/RenderTest/main.cpp index 13d5dbb51..2f4a989eb 100644 --- a/examples/RenderTest/main.cpp +++ b/examples/RenderTest/main.cpp @@ -391,8 +391,8 @@ int main() builder.BindIndexBuffer(*renderBufferIB, Nz::IndexType::U16); builder.BindRenderPipeline(*pipeline); builder.BindVertexBuffer(0, *renderBufferVB); - builder.BindShaderBinding(0, *viewerShaderBinding); - builder.BindShaderBinding(1, *textureShaderBinding); + builder.BindRenderShaderBinding(0, *viewerShaderBinding); + builder.BindRenderShaderBinding(1, *textureShaderBinding); builder.SetScissor(Nz::Recti{ 0, 0, int(windowSize.x), int(windowSize.y) }); builder.SetViewport(Nz::Recti{ 0, 0, int(windowSize.x), int(windowSize.y) }); diff --git a/include/Nazara/OpenGLRenderer/OpenGLCommandBuffer.hpp b/include/Nazara/OpenGLRenderer/OpenGLCommandBuffer.hpp index 8aa4fc537..d93ba168b 100644 --- a/include/Nazara/OpenGLRenderer/OpenGLCommandBuffer.hpp +++ b/include/Nazara/OpenGLRenderer/OpenGLCommandBuffer.hpp @@ -40,9 +40,10 @@ namespace Nz inline void BeginDebugRegion(const std::string_view& regionName, const Color& color); inline void BindComputePipeline(const OpenGLComputePipeline* pipeline); + inline void BindComputeShaderBinding(const OpenGLRenderPipelineLayout& pipelineLayout, UInt32 set, const OpenGLShaderBinding* binding); inline void BindIndexBuffer(GLuint indexBuffer, IndexType indexType, UInt64 offset = 0); inline void BindRenderPipeline(const OpenGLRenderPipeline* pipeline); - inline void BindShaderBinding(const OpenGLRenderPipelineLayout& pipelineLayout, UInt32 set, const OpenGLShaderBinding* binding); + inline void BindRenderShaderBinding(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); @@ -63,6 +64,8 @@ namespace Nz inline std::size_t GetPoolIndex() const; inline const OpenGLCommandPool& GetOwner() const; + inline void InsertMemoryBarrier(GLbitfield barriers); + inline void SetFramebuffer(const OpenGLFramebuffer& framebuffer, const OpenGLRenderPass& renderPass, const CommandBufferBuilder::ClearValues* clearValues, std::size_t clearValueCount); inline void SetScissor(const Recti& scissorRegion); inline void SetViewport(const Recti& viewportRegion); @@ -74,7 +77,9 @@ namespace Nz private: struct DrawStates; + struct ShaderBindings; + void ApplyBindings(const GL::Context& context, const ShaderBindings& bindings); void ApplyStates(const GL::Context& context, const DrawStates& states); void Release() override; @@ -123,9 +128,15 @@ namespace Nz UInt64 targetOffset; }; + struct ShaderBindings + { + std::vector> shaderBindings; + }; + struct DispatchData { ComputeStates states; + ShaderBindings bindings; UInt32 numGroupsX; UInt32 numGroupsY; UInt32 numGroupsZ; @@ -145,7 +156,6 @@ namespace Nz IndexType indexBufferType; std::optional scissorRegion; std::optional viewportRegion; - std::vector> shaderBindings; std::vector vertexBuffers; bool shouldFlipY = false; }; @@ -153,6 +163,7 @@ namespace Nz struct DrawData { DrawStates states; + ShaderBindings bindings; UInt32 firstInstance; UInt32 firstVertex; UInt32 instanceCount; @@ -162,6 +173,7 @@ namespace Nz struct DrawIndexedData { DrawStates states; + ShaderBindings bindings; UInt32 firstIndex; UInt32 firstInstance; UInt32 indexCount; @@ -172,6 +184,11 @@ namespace Nz { }; + struct MemoryBarrier + { + GLbitfield barriers; + }; + struct SetFrameBufferData { std::array clearValues; //< TODO: Remove hard limit? @@ -189,11 +206,15 @@ namespace Nz DrawData, DrawIndexedData, EndDebugRegionData, + MemoryBarrier, SetFrameBufferData >; ComputeStates m_currentComputeStates; DrawStates m_currentDrawStates; + ShaderBindings m_currentComputeShaderBindings; + ShaderBindings m_currentGraphicsShaderBindings; + std::optional m_pendingBarrier; 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 21344ead8..5778ef8f1 100644 --- a/include/Nazara/OpenGLRenderer/OpenGLCommandBuffer.inl +++ b/include/Nazara/OpenGLRenderer/OpenGLCommandBuffer.inl @@ -38,6 +38,14 @@ namespace Nz m_currentComputeStates.pipeline = pipeline; } + inline void OpenGLCommandBuffer::BindComputeShaderBinding(const OpenGLRenderPipelineLayout& pipelineLayout, UInt32 set, const OpenGLShaderBinding* binding) + { + if (set >= m_currentComputeShaderBindings.shaderBindings.size()) + m_currentComputeShaderBindings.shaderBindings.resize(set + 1); + + m_currentComputeShaderBindings.shaderBindings[set] = std::make_pair(&pipelineLayout, binding); + } + inline void OpenGLCommandBuffer::BindIndexBuffer(GLuint indexBuffer, IndexType indexType, UInt64 offset) { m_currentDrawStates.indexBuffer = indexBuffer; @@ -50,12 +58,12 @@ namespace Nz m_currentDrawStates.pipeline = pipeline; } - inline void OpenGLCommandBuffer::BindShaderBinding(const OpenGLRenderPipelineLayout& pipelineLayout, UInt32 set, const OpenGLShaderBinding* binding) + inline void OpenGLCommandBuffer::BindRenderShaderBinding(const OpenGLRenderPipelineLayout& pipelineLayout, UInt32 set, const OpenGLShaderBinding* binding) { - if (set >= m_currentDrawStates.shaderBindings.size()) - m_currentDrawStates.shaderBindings.resize(set + 1); + if (set >= m_currentGraphicsShaderBindings.shaderBindings.size()) + m_currentGraphicsShaderBindings.shaderBindings.resize(set + 1); - m_currentDrawStates.shaderBindings[set] = std::make_pair(&pipelineLayout, binding); + m_currentGraphicsShaderBindings.shaderBindings[set] = std::make_pair(&pipelineLayout, binding); } inline void OpenGLCommandBuffer::BindVertexBuffer(UInt32 binding, GLuint vertexBuffer, UInt64 offset) @@ -124,6 +132,7 @@ namespace Nz throw std::runtime_error("no pipeline bound"); DispatchData dispatch; + dispatch.bindings = m_currentComputeShaderBindings; dispatch.states = m_currentComputeStates; dispatch.numGroupsX = numGroupsX; dispatch.numGroupsY = numGroupsY; @@ -138,6 +147,7 @@ namespace Nz throw std::runtime_error("no pipeline bound"); DrawData draw; + draw.bindings = m_currentGraphicsShaderBindings; draw.states = m_currentDrawStates; draw.firstInstance = firstInstance; draw.firstVertex = firstVertex; @@ -153,6 +163,7 @@ namespace Nz throw std::runtime_error("no pipeline bound"); DrawIndexedData draw; + draw.bindings = m_currentGraphicsShaderBindings; draw.states = m_currentDrawStates; draw.firstIndex = firstIndex; draw.firstInstance = firstInstance; @@ -183,6 +194,23 @@ namespace Nz return *m_owner; } + inline void OpenGLCommandBuffer::InsertMemoryBarrier(GLbitfield barriers) + { + // Merge with previous barrier, if any (may happen because memory barriers are not relative to a texture with OpenGL) + if (!m_commands.empty() && std::holds_alternative(m_commands.back())) + { + MemoryBarrier& memBarrier = std::get(m_commands.back()); + memBarrier.barriers |= barriers; + } + else + { + MemoryBarrier memBarrier; + memBarrier.barriers = barriers; + + m_commands.emplace_back(std::move(memBarrier)); + } + } + inline void OpenGLCommandBuffer::SetFramebuffer(const OpenGLFramebuffer& framebuffer, const OpenGLRenderPass& renderPass, const CommandBufferBuilder::ClearValues* clearValues, std::size_t clearValueCount) { m_maxColorBufferCount = std::max(m_maxColorBufferCount, framebuffer.GetColorBufferCount()); diff --git a/include/Nazara/OpenGLRenderer/OpenGLCommandBufferBuilder.hpp b/include/Nazara/OpenGLRenderer/OpenGLCommandBufferBuilder.hpp index abe22627f..1924aabec 100644 --- a/include/Nazara/OpenGLRenderer/OpenGLCommandBufferBuilder.hpp +++ b/include/Nazara/OpenGLRenderer/OpenGLCommandBufferBuilder.hpp @@ -28,10 +28,12 @@ namespace Nz 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 BindComputeShaderBinding(UInt32 set, const ShaderBinding& binding) override; + void BindComputeShaderBinding(const RenderPipelineLayout& pipelineLayout, UInt32 set, const ShaderBinding& binding) override; void BindIndexBuffer(const RenderBuffer& indexBuffer, IndexType indexType, UInt64 offset = 0) 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 BindRenderShaderBinding(UInt32 set, const ShaderBinding& binding) override; + void BindRenderShaderBinding(const RenderPipelineLayout& pipelineLayout, UInt32 set, const ShaderBinding& binding) override; void BindVertexBuffer(UInt32 binding, const RenderBuffer& vertexBuffer, UInt64 offset = 0) override; void BlitTexture(const Texture& fromTexture, const Boxui& fromBox, TextureLayout fromLayout, const Texture& toTexture, const Boxui& toBox, TextureLayout toLayout, SamplerFilter filter) override; diff --git a/include/Nazara/OpenGLRenderer/OpenGLTexture.hpp b/include/Nazara/OpenGLRenderer/OpenGLTexture.hpp index 2e15132e4..3c1d24442 100644 --- a/include/Nazara/OpenGLRenderer/OpenGLTexture.hpp +++ b/include/Nazara/OpenGLRenderer/OpenGLTexture.hpp @@ -16,7 +16,7 @@ namespace Nz { - class NAZARA_OPENGLRENDERER_API OpenGLTexture : public Texture + class NAZARA_OPENGLRENDERER_API OpenGLTexture final : public Texture { public: OpenGLTexture(OpenGLDevice& device, const TextureInfo& textureInfo); @@ -28,13 +28,14 @@ namespace Nz bool Copy(const Texture& source, const Boxui& srcBox, const Vector3ui& dstPos) override; std::shared_ptr CreateView(const TextureViewInfo& viewInfo) override; - PixelFormat GetFormat() const override; - UInt8 GetLevelCount() const override; - OpenGLTexture* GetParentTexture() const override; - Vector3ui GetSize(UInt8 level = 0) const override; + inline PixelFormat GetFormat() const override; + inline UInt8 GetLevelCount() const override; + inline OpenGLTexture* GetParentTexture() const override; + inline Vector3ui GetSize(UInt8 level = 0) const override; inline const GL::Texture& GetTexture() const; + inline const TextureInfo& GetTextureInfo() const override; inline const TextureViewInfo& GetTextureViewInfo() const; - ImageType GetType() const override; + inline ImageType GetType() const override; inline bool RequiresTextureViewEmulation() const; diff --git a/include/Nazara/OpenGLRenderer/OpenGLTexture.inl b/include/Nazara/OpenGLRenderer/OpenGLTexture.inl index 29b1b4c7b..cd693f2f0 100644 --- a/include/Nazara/OpenGLRenderer/OpenGLTexture.inl +++ b/include/Nazara/OpenGLRenderer/OpenGLTexture.inl @@ -8,17 +8,47 @@ namespace Nz { + inline PixelFormat OpenGLTexture::GetFormat() const + { + return m_textureInfo.pixelFormat; + } + + inline UInt8 OpenGLTexture::GetLevelCount() const + { + return m_textureInfo.levelCount; + } + + inline OpenGLTexture* OpenGLTexture::GetParentTexture() const + { + return m_parentTexture.get(); + } + + inline Vector3ui OpenGLTexture::GetSize(UInt8 level) const + { + return Vector3ui(GetLevelSize(m_textureInfo.width, level), GetLevelSize(m_textureInfo.height, level), GetLevelSize(m_textureInfo.depth, level)); + } + inline const GL::Texture& OpenGLTexture::GetTexture() const { return m_texture; } + inline const TextureInfo& OpenGLTexture::GetTextureInfo() const + { + return m_textureInfo; + } + inline const TextureViewInfo& OpenGLTexture::GetTextureViewInfo() const { assert(m_viewInfo); return *m_viewInfo; } + inline ImageType OpenGLTexture::GetType() const + { + return m_textureInfo.type; + } + inline bool OpenGLTexture::RequiresTextureViewEmulation() const { return m_viewInfo.has_value() && !m_texture.IsValid(); diff --git a/include/Nazara/Renderer/CommandBufferBuilder.hpp b/include/Nazara/Renderer/CommandBufferBuilder.hpp index 61034bf61..1929b2d62 100644 --- a/include/Nazara/Renderer/CommandBufferBuilder.hpp +++ b/include/Nazara/Renderer/CommandBufferBuilder.hpp @@ -44,10 +44,12 @@ namespace Nz 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 BindComputeShaderBinding(UInt32 set, const ShaderBinding& binding) = 0; + virtual void BindComputeShaderBinding(const RenderPipelineLayout& pipelineLayout, UInt32 set, const ShaderBinding& binding) = 0; virtual void BindIndexBuffer(const RenderBuffer& indexBuffer, IndexType indexType, UInt64 offset = 0) = 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 BindRenderShaderBinding(UInt32 set, const ShaderBinding& binding) = 0; + virtual void BindRenderShaderBinding(const RenderPipelineLayout& pipelineLayout, UInt32 set, const ShaderBinding& binding) = 0; virtual void BindVertexBuffer(UInt32 binding, const RenderBuffer& vertexBuffer, UInt64 offset = 0) = 0; virtual void BlitTexture(const Texture& fromTexture, const Boxui& fromBox, TextureLayout fromLayout, const Texture& toTexture, const Boxui& toBox, TextureLayout toLayout, SamplerFilter filter) = 0; diff --git a/include/Nazara/Renderer/Enums.hpp b/include/Nazara/Renderer/Enums.hpp index 6564950ce..254256e5b 100644 --- a/include/Nazara/Renderer/Enums.hpp +++ b/include/Nazara/Renderer/Enums.hpp @@ -83,6 +83,7 @@ namespace Nz TopOfPipe, ColorOutput, + ComputeShader, DrawIndirect, FragmentShader, FragmentTestsEarly, @@ -196,6 +197,7 @@ namespace Nz ColorOutput, DepthStencilReadOnly, DepthStencilReadWrite, + General, Present, TransferSource, TransferDestination, diff --git a/include/Nazara/Renderer/RenderDeviceInfo.hpp b/include/Nazara/Renderer/RenderDeviceInfo.hpp index 2c576c2ef..08de7ec89 100644 --- a/include/Nazara/Renderer/RenderDeviceInfo.hpp +++ b/include/Nazara/Renderer/RenderDeviceInfo.hpp @@ -21,9 +21,8 @@ namespace Nz bool depthClamping = false; bool nonSolidFaceFilling = false; bool storageBuffers = false; - bool textureRead = false; bool textureReadWithoutFormat = false; - bool textureWrite = false; + bool textureReadWrite = false; bool textureWriteWithoutFormat = false; bool unrestrictedTextureViews = false; }; diff --git a/include/Nazara/Renderer/Texture.hpp b/include/Nazara/Renderer/Texture.hpp index d301ab9a5..6a0f423d4 100644 --- a/include/Nazara/Renderer/Texture.hpp +++ b/include/Nazara/Renderer/Texture.hpp @@ -68,11 +68,8 @@ namespace Nz virtual bool Copy(const Texture& source, const Boxui& srcBox, const Vector3ui& dstPos = Vector3ui::Zero()) = 0; virtual std::shared_ptr CreateView(const TextureViewInfo& viewInfo) = 0; - virtual PixelFormat GetFormat() const = 0; - virtual UInt8 GetLevelCount() const = 0; virtual Texture* GetParentTexture() const = 0; - virtual Vector3ui GetSize(UInt8 level = 0) const = 0; - virtual ImageType GetType() const = 0; + virtual const TextureInfo& GetTextureInfo() const = 0; virtual void UpdateDebugName(std::string_view name) = 0; diff --git a/include/Nazara/VulkanRenderer/Utils.inl b/include/Nazara/VulkanRenderer/Utils.inl index 7087c40e5..9db682e7b 100644 --- a/include/Nazara/VulkanRenderer/Utils.inl +++ b/include/Nazara/VulkanRenderer/Utils.inl @@ -227,6 +227,7 @@ namespace Nz { case PipelineStage::TopOfPipe: return VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT; case PipelineStage::ColorOutput: return VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; + case PipelineStage::ComputeShader: return VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT; case PipelineStage::DrawIndirect: return VK_PIPELINE_STAGE_DRAW_INDIRECT_BIT; case PipelineStage::FragmentShader: return VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; case PipelineStage::FragmentTestsEarly: return VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT; @@ -444,6 +445,7 @@ namespace Nz case TextureLayout::ColorOutput: return VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; case TextureLayout::DepthStencilReadOnly: return VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL; case TextureLayout::DepthStencilReadWrite: return VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; + case TextureLayout::General: return VK_IMAGE_LAYOUT_GENERAL; case TextureLayout::Present: return VK_IMAGE_LAYOUT_PRESENT_SRC_KHR; case TextureLayout::TransferSource: return VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL; case TextureLayout::TransferDestination: return VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; diff --git a/include/Nazara/VulkanRenderer/VulkanCommandBufferBuilder.hpp b/include/Nazara/VulkanRenderer/VulkanCommandBufferBuilder.hpp index 03ea16a08..14e840589 100644 --- a/include/Nazara/VulkanRenderer/VulkanCommandBufferBuilder.hpp +++ b/include/Nazara/VulkanRenderer/VulkanCommandBufferBuilder.hpp @@ -28,10 +28,12 @@ namespace Nz 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 BindComputeShaderBinding(UInt32 set, const ShaderBinding& binding) override; + void BindComputeShaderBinding(const RenderPipelineLayout& pipelineLayout, UInt32 set, const ShaderBinding& binding) override; void BindIndexBuffer(const RenderBuffer& indexBuffer, IndexType indexType, UInt64 offset = 0) 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 BindRenderShaderBinding(UInt32 set, const ShaderBinding& binding) override; + void BindRenderShaderBinding(const RenderPipelineLayout& pipelineLayout, UInt32 set, const ShaderBinding& binding) override; void BindVertexBuffer(UInt32 binding, const RenderBuffer& vertexBuffer, UInt64 offset = 0) override; void BlitTexture(const Texture& fromTexture, const Boxui& fromBox, TextureLayout fromLayout, const Texture& toTexture, const Boxui& toBox, TextureLayout toLayout, SamplerFilter filter) override; diff --git a/include/Nazara/VulkanRenderer/VulkanTexture.hpp b/include/Nazara/VulkanRenderer/VulkanTexture.hpp index 62c81e5e1..ececd2dc5 100644 --- a/include/Nazara/VulkanRenderer/VulkanTexture.hpp +++ b/include/Nazara/VulkanRenderer/VulkanTexture.hpp @@ -16,7 +16,7 @@ namespace Nz { - class NAZARA_VULKANRENDERER_API VulkanTexture : public Texture + class NAZARA_VULKANRENDERER_API VulkanTexture final : public Texture { public: VulkanTexture(Vk::Device& device, const TextureInfo& textureInfo); @@ -28,14 +28,15 @@ namespace Nz bool Copy(const Texture& source, const Boxui& srcBox, const Vector3ui& dstPos) override; std::shared_ptr CreateView(const TextureViewInfo& viewInfo) override; - PixelFormat GetFormat() const override; + inline PixelFormat GetFormat() const override; inline VkImage GetImage() const; inline VkImageView GetImageView() const; - UInt8 GetLevelCount() const override; - VulkanTexture* GetParentTexture() const override; - Vector3ui GetSize(UInt8 level = 0) const override; + inline UInt8 GetLevelCount() const override; + inline VulkanTexture* GetParentTexture() const override; + inline Vector3ui GetSize(UInt8 level = 0) const override; inline const VkImageSubresourceRange& GetSubresourceRange() const; - ImageType GetType() const override; + inline const TextureInfo& GetTextureInfo() const override; + inline ImageType GetType() const override; using Texture::Update; bool Update(const void* ptr, const Boxui& box, unsigned int srcWidth, unsigned int srcHeight, UInt8 level) override; diff --git a/include/Nazara/VulkanRenderer/VulkanTexture.inl b/include/Nazara/VulkanRenderer/VulkanTexture.inl index f44837013..0a543050e 100644 --- a/include/Nazara/VulkanRenderer/VulkanTexture.inl +++ b/include/Nazara/VulkanRenderer/VulkanTexture.inl @@ -7,6 +7,11 @@ namespace Nz { + inline PixelFormat VulkanTexture::GetFormat() const + { + return m_textureInfo.pixelFormat; + } + inline VkImage VulkanTexture::GetImage() const { return m_image; @@ -17,10 +22,35 @@ namespace Nz return m_imageView; } + inline UInt8 VulkanTexture::GetLevelCount() const + { + return m_textureInfo.levelCount; + } + + inline VulkanTexture* VulkanTexture::GetParentTexture() const + { + return m_parentTexture.get(); + } + + inline Vector3ui VulkanTexture::GetSize(UInt8 level) const + { + return Vector3ui(GetLevelSize(m_textureInfo.width, level), GetLevelSize(m_textureInfo.height, level), GetLevelSize(m_textureInfo.depth, level)); + } + inline const VkImageSubresourceRange& VulkanTexture::GetSubresourceRange() const { return m_imageRange; } + + inline const TextureInfo& VulkanTexture::GetTextureInfo() const + { + return m_textureInfo; + } + + inline ImageType VulkanTexture::GetType() const + { + return m_textureInfo.type; + } } #include diff --git a/src/Nazara/Graphics/ForwardFramePipeline.cpp b/src/Nazara/Graphics/ForwardFramePipeline.cpp index 688a0887c..cc3ce6f5a 100644 --- a/src/Nazara/Graphics/ForwardFramePipeline.cpp +++ b/src/Nazara/Graphics/ForwardFramePipeline.cpp @@ -458,7 +458,7 @@ namespace Nz builder.SetViewport(renderRegion); builder.BindRenderPipeline(*graphics->GetBlitPipeline(false)); - builder.BindShaderBinding(0, *data.blitShaderBinding); + builder.BindRenderShaderBinding(0, *data.blitShaderBinding); builder.Draw(3); } builder.EndDebugRegion(); @@ -652,7 +652,7 @@ namespace Nz { const ShaderBindingPtr& blitShaderBinding = viewerData->blitShaderBinding; - builder.BindShaderBinding(0, *blitShaderBinding); + builder.BindRenderShaderBinding(0, *blitShaderBinding); builder.Draw(3); if (first) diff --git a/src/Nazara/Graphics/Graphics.cpp b/src/Nazara/Graphics/Graphics.cpp index 39b7166a4..f2ddeafd1 100644 --- a/src/Nazara/Graphics/Graphics.cpp +++ b/src/Nazara/Graphics/Graphics.cpp @@ -112,9 +112,8 @@ namespace Nz 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.textureReadWrite = !config.forceDisableFeatures.textureReadWrite && renderDeviceInfo[bestRenderDeviceIndex].features.textureReadWrite; enabledFeatures.textureWriteWithoutFormat = !config.forceDisableFeatures.textureWriteWithoutFormat && renderDeviceInfo[bestRenderDeviceIndex].features.textureWriteWithoutFormat; enabledFeatures.unrestrictedTextureViews = !config.forceDisableFeatures.unrestrictedTextureViews && renderDeviceInfo[bestRenderDeviceIndex].features.unrestrictedTextureViews; diff --git a/src/Nazara/Graphics/SpriteChainRenderer.cpp b/src/Nazara/Graphics/SpriteChainRenderer.cpp index e73535268..ed447e13e 100644 --- a/src/Nazara/Graphics/SpriteChainRenderer.cpp +++ b/src/Nazara/Graphics/SpriteChainRenderer.cpp @@ -316,7 +316,7 @@ namespace Nz if (currentShaderBinding != drawData.shaderBinding) { - commandBuffer.BindShaderBinding(0, *drawData.shaderBinding); + commandBuffer.BindRenderShaderBinding(0, *drawData.shaderBinding); currentShaderBinding = drawData.shaderBinding; } diff --git a/src/Nazara/Graphics/SubmeshRenderer.cpp b/src/Nazara/Graphics/SubmeshRenderer.cpp index 53f50da7e..f306df3c7 100644 --- a/src/Nazara/Graphics/SubmeshRenderer.cpp +++ b/src/Nazara/Graphics/SubmeshRenderer.cpp @@ -291,7 +291,7 @@ namespace Nz if (currentShaderBinding != drawData.shaderBinding) { - commandBuffer.BindShaderBinding(0, *drawData.shaderBinding); + commandBuffer.BindRenderShaderBinding(0, *drawData.shaderBinding); currentShaderBinding = drawData.shaderBinding; } diff --git a/src/Nazara/OpenGLRenderer/OpenGLCommandBuffer.cpp b/src/Nazara/OpenGLRenderer/OpenGLCommandBuffer.cpp index dbb00fe66..c29265b38 100644 --- a/src/Nazara/OpenGLRenderer/OpenGLCommandBuffer.cpp +++ b/src/Nazara/OpenGLRenderer/OpenGLCommandBuffer.cpp @@ -100,11 +100,13 @@ namespace Nz throw std::runtime_error("compute shaders are not supported on this device"); command.states.pipeline->Apply(*context); + ApplyBindings(*context, command.bindings); context->glDispatchCompute(command.numGroupsX, command.numGroupsY, command.numGroupsZ); } else if constexpr (std::is_same_v) { ApplyStates(*context, command.states); + ApplyBindings(*context, command.bindings); context->glDrawArraysInstanced(ToOpenGL(command.states.pipeline->GetPipelineInfo().primitiveMode), command.firstVertex, command.vertexCount, command.instanceCount); } else if constexpr (std::is_same_v) @@ -120,6 +122,7 @@ namespace Nz } ApplyStates(*context, command.states); + ApplyBindings(*context, command.bindings); context->glDrawElementsInstanced(ToOpenGL(command.states.pipeline->GetPipelineInfo().primitiveMode), command.indexCount, ToOpenGL(command.states.indexBufferType), origin, command.instanceCount); } else if constexpr (std::is_same_v) @@ -127,6 +130,11 @@ namespace Nz if (context->glPopDebugGroup) context->glPopDebugGroup(); } + else if constexpr (std::is_same_v) + { + if (context->glMemoryBarrier) + context->glMemoryBarrier(command.barriers); + } else if constexpr (std::is_same_v) { command.framebuffer->Activate(); @@ -329,10 +337,8 @@ namespace Nz // No OpenGL object to name } - void OpenGLCommandBuffer::ApplyStates(const GL::Context& context, const DrawStates& states) + void OpenGLCommandBuffer::ApplyBindings(const GL::Context& context, const ShaderBindings& states) { - states.pipeline->Apply(context, states.shouldFlipY); - unsigned int setIndex = 0; for (const auto& [pipelineLayout, shaderBinding] : states.shaderBindings) { @@ -343,6 +349,11 @@ namespace Nz setIndex++; } + } + + void OpenGLCommandBuffer::ApplyStates(const GL::Context& context, const DrawStates& states) + { + states.pipeline->Apply(context, states.shouldFlipY); if (states.scissorRegion) context.SetScissorBox(states.scissorRegion->x, states.scissorRegion->y, states.scissorRegion->width, states.scissorRegion->height); diff --git a/src/Nazara/OpenGLRenderer/OpenGLCommandBufferBuilder.cpp b/src/Nazara/OpenGLRenderer/OpenGLCommandBufferBuilder.cpp index bff9b0b67..d99c848d8 100644 --- a/src/Nazara/OpenGLRenderer/OpenGLCommandBufferBuilder.cpp +++ b/src/Nazara/OpenGLRenderer/OpenGLCommandBufferBuilder.cpp @@ -34,6 +34,21 @@ namespace Nz m_commandBuffer.BindComputePipeline(&glPipeline); } + void OpenGLCommandBufferBuilder::BindComputeShaderBinding(UInt32 set, const ShaderBinding& binding) + { + const OpenGLShaderBinding& glBinding = static_cast(binding); + + m_commandBuffer.BindComputeShaderBinding(glBinding.GetOwner(), set, &glBinding); + } + + void OpenGLCommandBufferBuilder::BindComputeShaderBinding(const RenderPipelineLayout& pipelineLayout, UInt32 set, const ShaderBinding& binding) + { + const OpenGLRenderPipelineLayout& glPipelineLayout = static_cast(pipelineLayout); + const OpenGLShaderBinding& glBinding = static_cast(binding); + + m_commandBuffer.BindComputeShaderBinding(glPipelineLayout, set, &glBinding); + } + void OpenGLCommandBufferBuilder::BindIndexBuffer(const RenderBuffer& indexBuffer, IndexType indexType, UInt64 offset) { const OpenGLBuffer& glBuffer = static_cast(indexBuffer); @@ -48,19 +63,19 @@ namespace Nz m_commandBuffer.BindRenderPipeline(&glPipeline); } - void OpenGLCommandBufferBuilder::BindShaderBinding(UInt32 set, const ShaderBinding& binding) + void OpenGLCommandBufferBuilder::BindRenderShaderBinding(UInt32 set, const ShaderBinding& binding) { const OpenGLShaderBinding& glBinding = static_cast(binding); - m_commandBuffer.BindShaderBinding(glBinding.GetOwner(), set, &glBinding); + m_commandBuffer.BindRenderShaderBinding(glBinding.GetOwner(), set, &glBinding); } - void OpenGLCommandBufferBuilder::BindShaderBinding(const RenderPipelineLayout& pipelineLayout, UInt32 set, const ShaderBinding& binding) + void OpenGLCommandBufferBuilder::BindRenderShaderBinding(const RenderPipelineLayout& pipelineLayout, UInt32 set, const ShaderBinding& binding) { const OpenGLRenderPipelineLayout& glPipelineLayout = static_cast(pipelineLayout); const OpenGLShaderBinding& glBinding = static_cast(binding); - m_commandBuffer.BindShaderBinding(glPipelineLayout, set, &glBinding); + m_commandBuffer.BindRenderShaderBinding(glPipelineLayout, set, &glBinding); } void OpenGLCommandBufferBuilder::BindVertexBuffer(UInt32 binding, const RenderBuffer& vertexBuffer, UInt64 offset) @@ -151,8 +166,23 @@ namespace Nz m_commandBuffer.SetViewport(viewportRegion); } - void OpenGLCommandBufferBuilder::TextureBarrier(PipelineStageFlags /*srcStageMask*/, PipelineStageFlags /*dstStageMask*/, MemoryAccessFlags /*srcAccessMask*/, MemoryAccessFlags /*dstAccessMask*/, TextureLayout /*oldLayout*/, TextureLayout /*newLayout*/, const Texture& /*texture*/) + void OpenGLCommandBufferBuilder::TextureBarrier(PipelineStageFlags /*srcStageMask*/, PipelineStageFlags /*dstStageMask*/, MemoryAccessFlags srcAccessMask, MemoryAccessFlags dstAccessMask, TextureLayout /*oldLayout*/, TextureLayout /*newLayout*/, const Texture& /*texture*/) { - /* nothing to do */ + if (srcAccessMask.Test(MemoryAccess::ShaderWrite)) + { + GLbitfield barriers = 0; + + if (dstAccessMask.Test(MemoryAccess::ColorRead)) + barriers |= GL_TEXTURE_FETCH_BARRIER_BIT; + + if (dstAccessMask.Test(MemoryAccess::ShaderRead)) + barriers |= GL_TEXTURE_FETCH_BARRIER_BIT | GL_SHADER_IMAGE_ACCESS_BARRIER_BIT; + + if (dstAccessMask.Test(MemoryAccess::ShaderWrite)) + barriers |= GL_SHADER_IMAGE_ACCESS_BARRIER_BIT; + + if (barriers != 0) + m_commandBuffer.InsertMemoryBarrier(barriers); + } } } diff --git a/src/Nazara/OpenGLRenderer/OpenGLDevice.cpp b/src/Nazara/OpenGLRenderer/OpenGLDevice.cpp index e46764795..70854a40f 100644 --- a/src/Nazara/OpenGLRenderer/OpenGLDevice.cpp +++ b/src/Nazara/OpenGLRenderer/OpenGLDevice.cpp @@ -77,8 +77,7 @@ namespace Nz if (m_referenceContext->IsExtensionSupported(GL::Extension::ShaderImageLoadStore)) { - m_deviceInfo.features.textureRead = true; - m_deviceInfo.features.textureWrite = true; + m_deviceInfo.features.textureReadWrite = true; m_deviceInfo.features.textureWriteWithoutFormat = true; } diff --git a/src/Nazara/OpenGLRenderer/OpenGLShaderBinding.cpp b/src/Nazara/OpenGLRenderer/OpenGLShaderBinding.cpp index bdcb8d443..14638440c 100644 --- a/src/Nazara/OpenGLRenderer/OpenGLShaderBinding.cpp +++ b/src/Nazara/OpenGLRenderer/OpenGLShaderBinding.cpp @@ -117,26 +117,22 @@ namespace Nz 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); + std::optional format = DescribeTextureFormat(glTexture->GetFormat()); 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; + // Don't bother emulating texture views as their support is virtually guaranteed when compute shaders are available + if (Nz::ImageType imageType = glTexture->GetType(); imageType == ImageType::Cubemap || imageType == ImageType::E1D_Array || imageType == ImageType::E2D_Array) + textureDescriptor.layered = true; + else + textureDescriptor.layered = false; + + textureDescriptor.level = 0; + textureDescriptor.layer = 0; + textureDescriptor.texture = glTexture->GetTexture().GetObjectId(); } else diff --git a/src/Nazara/OpenGLRenderer/OpenGLTexture.cpp b/src/Nazara/OpenGLRenderer/OpenGLTexture.cpp index 1f578fde7..7636a6208 100644 --- a/src/Nazara/OpenGLRenderer/OpenGLTexture.cpp +++ b/src/Nazara/OpenGLRenderer/OpenGLTexture.cpp @@ -117,31 +117,6 @@ namespace Nz return std::make_shared(std::static_pointer_cast(shared_from_this()), viewInfo); } - PixelFormat OpenGLTexture::GetFormat() const - { - return m_textureInfo.pixelFormat; - } - - UInt8 OpenGLTexture::GetLevelCount() const - { - return m_textureInfo.levelCount; - } - - OpenGLTexture* OpenGLTexture::GetParentTexture() const - { - return m_parentTexture.get(); - } - - Vector3ui OpenGLTexture::GetSize(UInt8 level) const - { - return Vector3ui(GetLevelSize(m_textureInfo.width, level), GetLevelSize(m_textureInfo.height, level), GetLevelSize(m_textureInfo.depth, level)); - } - - ImageType OpenGLTexture::GetType() const - { - return m_textureInfo.type; - } - bool OpenGLTexture::Update(const void* ptr, const Boxui& box, unsigned int srcWidth, unsigned int srcHeight, UInt8 level) { auto format = DescribeTextureFormat(m_textureInfo.pixelFormat); diff --git a/src/Nazara/OpenGLRenderer/Wrapper/Context.cpp b/src/Nazara/OpenGLRenderer/Wrapper/Context.cpp index 2a3159b00..dc10ee47a 100644 --- a/src/Nazara/OpenGLRenderer/Wrapper/Context.cpp +++ b/src/Nazara/OpenGLRenderer/Wrapper/Context.cpp @@ -633,6 +633,13 @@ namespace Nz::GL assert(maxUniformBufferUnits > 0); m_state.uboUnits.resize(maxUniformBufferUnits); + if (IsExtensionSupported(Extension::ShaderImageLoadStore)) + { + unsigned int maxImageUnits = GetInteger(GL_MAX_IMAGE_UNITS); + assert(maxImageUnits > 0); + m_state.imageUnits.resize(maxImageUnits); + } + if (IsExtensionSupported(Extension::StorageBuffers)) { unsigned int maxStorageBufferUnits = GetInteger(GL_MAX_SHADER_STORAGE_BUFFER_BINDINGS); diff --git a/src/Nazara/Renderer/DebugDrawer.cpp b/src/Nazara/Renderer/DebugDrawer.cpp index 38209acdc..fdcfc8d19 100644 --- a/src/Nazara/Renderer/DebugDrawer.cpp +++ b/src/Nazara/Renderer/DebugDrawer.cpp @@ -82,7 +82,7 @@ namespace Nz if (m_drawCalls.empty()) return; - builder.BindShaderBinding(0, *m_currentViewerData.binding); + builder.BindRenderShaderBinding(0, *m_currentViewerData.binding); builder.BindRenderPipeline(*m_renderPipeline); for (auto& drawCall : m_drawCalls) diff --git a/src/Nazara/Renderer/RenderDevice.cpp b/src/Nazara/Renderer/RenderDevice.cpp index 59461c83d..1ab5f061b 100644 --- a/src/Nazara/Renderer/RenderDevice.cpp +++ b/src/Nazara/Renderer/RenderDevice.cpp @@ -46,9 +46,8 @@ namespace Nz 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(textureReadWrite, "texture read/write") NzValidateFeature(textureWriteWithoutFormat, "texture write without format") NzValidateFeature(unrestrictedTextureViews, "unrestricted texture view support") diff --git a/src/Nazara/VulkanRenderer/Vulkan.cpp b/src/Nazara/VulkanRenderer/Vulkan.cpp index 8e092e376..e384ae7ac 100644 --- a/src/Nazara/VulkanRenderer/Vulkan.cpp +++ b/src/Nazara/VulkanRenderer/Vulkan.cpp @@ -58,9 +58,8 @@ namespace Nz 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.textureReadWrite = true; deviceInfo.features.textureWriteWithoutFormat = physDevice.features.shaderStorageImageWriteWithoutFormat; deviceInfo.features.unrestrictedTextureViews = true; diff --git a/src/Nazara/VulkanRenderer/VulkanCommandBufferBuilder.cpp b/src/Nazara/VulkanRenderer/VulkanCommandBufferBuilder.cpp index bc6b2aa51..5122bbb05 100644 --- a/src/Nazara/VulkanRenderer/VulkanCommandBufferBuilder.cpp +++ b/src/Nazara/VulkanRenderer/VulkanCommandBufferBuilder.cpp @@ -80,6 +80,22 @@ namespace Nz m_commandBuffer.BindPipeline(VK_PIPELINE_BIND_POINT_COMPUTE, vkPipeline.GetPipeline()); } + void VulkanCommandBufferBuilder::BindComputeShaderBinding(UInt32 set, const ShaderBinding& binding) + { + const VulkanShaderBinding& vkBinding = static_cast(binding); + const VulkanRenderPipelineLayout& pipelineLayout = vkBinding.GetOwner(); + + m_commandBuffer.BindDescriptorSet(VK_PIPELINE_BIND_POINT_COMPUTE, pipelineLayout.GetPipelineLayout(), set, vkBinding.GetDescriptorSet()); + } + + void VulkanCommandBufferBuilder::BindComputeShaderBinding(const RenderPipelineLayout& pipelineLayout, UInt32 set, const ShaderBinding& binding) + { + const VulkanRenderPipelineLayout& vkPipelineLayout = static_cast(pipelineLayout); + const VulkanShaderBinding& vkBinding = static_cast(binding); + + m_commandBuffer.BindDescriptorSet(VK_PIPELINE_BIND_POINT_COMPUTE, vkPipelineLayout.GetPipelineLayout(), set, vkBinding.GetDescriptorSet()); + } + void VulkanCommandBufferBuilder::BindIndexBuffer(const RenderBuffer& indexBuffer, IndexType indexType, UInt64 offset) { const VulkanBuffer& vkBuffer = static_cast(indexBuffer); @@ -97,7 +113,7 @@ namespace Nz m_commandBuffer.BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, vkPipeline.Get(*m_currentRenderPass, m_currentSubpassIndex)); } - void VulkanCommandBufferBuilder::BindShaderBinding(UInt32 set, const ShaderBinding& binding) + void VulkanCommandBufferBuilder::BindRenderShaderBinding(UInt32 set, const ShaderBinding& binding) { const VulkanShaderBinding& vkBinding = static_cast(binding); const VulkanRenderPipelineLayout& pipelineLayout = vkBinding.GetOwner(); @@ -105,7 +121,7 @@ namespace Nz m_commandBuffer.BindDescriptorSet(VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout.GetPipelineLayout(), set, vkBinding.GetDescriptorSet()); } - void VulkanCommandBufferBuilder::BindShaderBinding(const RenderPipelineLayout& pipelineLayout, UInt32 set, const ShaderBinding& binding) + void VulkanCommandBufferBuilder::BindRenderShaderBinding(const RenderPipelineLayout& pipelineLayout, UInt32 set, const ShaderBinding& binding) { const VulkanRenderPipelineLayout& vkPipelineLayout = static_cast(pipelineLayout); const VulkanShaderBinding& vkBinding = static_cast(binding); diff --git a/src/Nazara/VulkanRenderer/VulkanTexture.cpp b/src/Nazara/VulkanRenderer/VulkanTexture.cpp index 7325c79a1..cc07cdaf6 100644 --- a/src/Nazara/VulkanRenderer/VulkanTexture.cpp +++ b/src/Nazara/VulkanRenderer/VulkanTexture.cpp @@ -294,31 +294,6 @@ namespace Nz return std::make_shared(std::static_pointer_cast(shared_from_this()), viewInfo); } - PixelFormat VulkanTexture::GetFormat() const - { - return m_textureInfo.pixelFormat; - } - - UInt8 VulkanTexture::GetLevelCount() const - { - return m_textureInfo.levelCount; - } - - VulkanTexture* VulkanTexture::GetParentTexture() const - { - return m_parentTexture.get(); - } - - Vector3ui VulkanTexture::GetSize(UInt8 level) const - { - return Vector3ui(GetLevelSize(m_textureInfo.width, level), GetLevelSize(m_textureInfo.height, level), GetLevelSize(m_textureInfo.depth, level)); - } - - ImageType VulkanTexture::GetType() const - { - return m_textureInfo.type; - } - bool VulkanTexture::Update(const void* ptr, const Boxui& box, unsigned int srcWidth, unsigned int srcHeight, UInt8 level) { std::size_t textureSize = box.width * box.height * box.depth * PixelFormatInfo::GetBytesPerPixel(m_textureInfo.pixelFormat);