diff --git a/assets/shaders/compute/compute_edge_detection.nzsl b/assets/shaders/compute/compute_edge_detection.nzsl new file mode 100644 index 000000000..1efee0151 --- /dev/null +++ b/assets/shaders/compute/compute_edge_detection.nzsl @@ -0,0 +1,59 @@ +[nzsl_version("1.0")] +module Compute.EdgeDetection; + +external +{ + [binding(0)] input_tex: texture2D[f32, readonly, rgba8], + [binding(1)] output_tex: texture2D[f32, writeonly, rgba8] +} + +struct Input +{ + [builtin(global_invocation_indices)] global_invocation_id: vec3[u32] +} + +[entry(compute)] +[workgroup(32, 32, 1)] +fn main(input: Input) +{ + let indices = vec2[i32](input.global_invocation_id.xy); + + // 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); +} \ No newline at end of file diff --git a/assets/shaders/compute/compute_sepia.nzsl b/assets/shaders/compute/compute_sepia.nzsl new file mode 100644 index 000000000..2edd1814d --- /dev/null +++ b/assets/shaders/compute/compute_sepia.nzsl @@ -0,0 +1,32 @@ +[nzsl_version("1.0")] +module Compute.Sepia; + +external +{ + [binding(0)] input_tex: texture2D[f32, readonly, rgba8], + [binding(1)] output_tex: texture2D[f32, writeonly, rgba8] +} + +struct Input +{ + [builtin(global_invocation_indices)] global_invocation_id: vec3[u32] +} + +[entry(compute)] +[workgroup(32, 32, 1)] +fn main(input: Input) +{ + let indices = vec2[i32](input.global_invocation_id.xy); + + let inputColor = input_tex.Read(indices).rgb; + + let outputColor = vec4[f32] + ( + inputColor.x * 0.393 + inputColor.y * 0.769 + inputColor.z * 0.189, + inputColor.x * 0.349 + inputColor.y * 0.686 + inputColor.z * 0.168, + inputColor.x * 0.372 + inputColor.y * 0.534 + inputColor.z * 0.131, + 1.0 + ); + + output_tex.Write(indices, outputColor); +} \ No newline at end of file diff --git a/tests/ComputeTest/main.cpp b/tests/ComputeTest/main.cpp index abb6f0b3e..aaa3e1c9e 100644 --- a/tests/ComputeTest/main.cpp +++ b/tests/ComputeTest/main.cpp @@ -2,6 +2,7 @@ #include #include #include +#include #include #include #include @@ -12,12 +13,6 @@ NAZARA_REQUEST_DEDICATED_GPU() -struct ComputePipeline -{ - std::shared_ptr layout; - std::shared_ptr pipeline; -}; - struct SpriteRenderData { std::shared_ptr vertexBuffer; @@ -31,7 +26,7 @@ struct SpriteRenderPipeline }; -ComputePipeline BuildComputePipeline(Nz::RenderDevice& device); +std::shared_ptr BuildComputePipeline(Nz::RenderDevice& device, std::shared_ptr pipelineLayout, std::shared_ptr moduleResolver); 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); @@ -50,7 +45,6 @@ int main() Nz::Modules nazara(rendererConfig); - Nz::RenderDeviceFeatures enabledFeatures; enabledFeatures.computeShaders = true; enabledFeatures.textureReadWrite = true; @@ -73,8 +67,30 @@ int main() std::shared_ptr textureSampler = device->InstantiateTextureSampler({}); // Compute part - ComputePipeline computePipeline = BuildComputePipeline(*device); - std::shared_ptr computeBinding = computePipeline.layout->AllocateShaderBinding(0); + Nz::RenderPipelineLayoutInfo computePipelineLayoutInfo; + computePipelineLayoutInfo.bindings.assign({ + { + 0, 0, 1, + Nz::ShaderBindingType::Texture, + nzsl::ShaderStageType::Compute + }, + { + 0, 1, 1, + Nz::ShaderBindingType::Texture, + nzsl::ShaderStageType::Compute + }, + }); + + std::shared_ptr computePipelineLayout = device->InstantiateRenderPipelineLayout(computePipelineLayoutInfo); + + std::shared_ptr moduleResolver = std::make_shared(); + moduleResolver->RegisterModuleDirectory(resourceDir / "../shaders/", true); + + std::shared_ptr computePipeline = BuildComputePipeline(*device, computePipelineLayout, moduleResolver); + std::shared_ptr newComputePipeline; + std::atomic_bool hasNewPipeline = false; + + std::shared_ptr computeBinding = computePipelineLayout->AllocateShaderBinding(0); computeBinding->Update({ { 0, @@ -91,6 +107,14 @@ int main() } } }); + + moduleResolver->OnModuleUpdated.Connect([&](nzsl::ModuleResolver*, const std::string& moduleName) + { + std::cout << moduleName << " has been updated" << std::endl; + newComputePipeline = BuildComputePipeline(*device, computePipelineLayout, moduleResolver); + hasNewPipeline = true; + }); + std::string windowTitle = "Compute test"; @@ -123,15 +147,22 @@ int main() continue; } + if (hasNewPipeline) + { + frame.PushForRelease(std::move(computePipeline)); + computePipeline = std::move(newComputePipeline); + hasNewPipeline = false; + } + const Nz::RenderTarget* windowRT = window.GetRenderTarget(); frame.Execute([&](Nz::CommandBufferBuilder& builder) { - builder.BeginDebugRegion("Compute part", Nz::Color::Blue); + 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.BindComputePipeline(*computePipeline); builder.BindComputeShaderBinding(0, *computeBinding); builder.Dispatch(destTexParams.width / 32, destTexParams.height / 32, 1); @@ -187,74 +218,11 @@ int main() return EXIT_SUCCESS; } -// Edge detection, translated to NZSL from Sascha Willems compute shader example -const char computeSource[] = R"( -[nzsl_version("1.0")] -module; - -external -{ - [binding(0)] input_tex: texture2D[f32, readonly, rgba8], - [binding(1)] output_tex: texture2D[f32, writeonly, rgba8] -} - -struct Input -{ - [builtin(global_invocation_indices)] global_invocation_id: vec3[u32] -} - -[entry(compute)] -[workgroup(32, 32, 1)] -fn main(input: Input) -{ - let indices = vec2[i32](input.global_invocation_id.xy); - - // 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); -} -)"; - -ComputePipeline BuildComputePipeline(Nz::RenderDevice& device) +std::shared_ptr BuildComputePipeline(Nz::RenderDevice& device, std::shared_ptr pipelineLayout, std::shared_ptr moduleResolver) { try { - nzsl::Ast::ModulePtr shaderModule = nzsl::Parse(std::string_view(computeSource, sizeof(computeSource))); + nzsl::Ast::ModulePtr shaderModule = moduleResolver->Resolve("Compute.Sepia"); if (!shaderModule) { std::cout << "Failed to parse shader module" << std::endl; @@ -271,22 +239,6 @@ ComputePipeline BuildComputePipeline(Nz::RenderDevice& device) std::abort(); } - Nz::RenderPipelineLayoutInfo computePipelineLayoutInfo; - - auto& inputBinding = computePipelineLayoutInfo.bindings.emplace_back(); - inputBinding.setIndex = 0; - inputBinding.bindingIndex = 0; - inputBinding.shaderStageFlags = nzsl::ShaderStageType::Compute; - inputBinding.type = Nz::ShaderBindingType::Texture; - - auto& outputBinding = computePipelineLayoutInfo.bindings.emplace_back(); - outputBinding.setIndex = 0; - outputBinding.bindingIndex = 1; - outputBinding.shaderStageFlags = nzsl::ShaderStageType::Compute; - outputBinding.type = Nz::ShaderBindingType::Texture; - - std::shared_ptr pipelineLayout = device.InstantiateRenderPipelineLayout(computePipelineLayoutInfo); - Nz::ComputePipelineInfo computePipelineInfo; computePipelineInfo.pipelineLayout = pipelineLayout; computePipelineInfo.shaderModule = computeShader; @@ -298,10 +250,7 @@ ComputePipeline BuildComputePipeline(Nz::RenderDevice& device) std::abort(); } - ComputePipeline result; - result.layout = std::move(pipelineLayout); - result.pipeline = std::move(pipeline); - return result; + return pipeline; } catch (const std::exception& e) {