Tests/ComputeTest: Load shader from file (and support hot-reload)
This commit is contained in:
parent
4b804dc613
commit
98f2feecc7
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -2,6 +2,7 @@
|
|||
#include <Nazara/Math.hpp>
|
||||
#include <Nazara/Platform.hpp>
|
||||
#include <Nazara/Renderer.hpp>
|
||||
#include <NZSL/FilesystemModuleResolver.hpp>
|
||||
#include <NZSL/LangWriter.hpp>
|
||||
#include <NZSL/Parser.hpp>
|
||||
#include <Nazara/Utility.hpp>
|
||||
|
|
@ -12,12 +13,6 @@
|
|||
|
||||
NAZARA_REQUEST_DEDICATED_GPU()
|
||||
|
||||
struct ComputePipeline
|
||||
{
|
||||
std::shared_ptr<Nz::RenderPipelineLayout> layout;
|
||||
std::shared_ptr<Nz::ComputePipeline> pipeline;
|
||||
};
|
||||
|
||||
struct SpriteRenderData
|
||||
{
|
||||
std::shared_ptr<Nz::RenderBuffer> vertexBuffer;
|
||||
|
|
@ -31,7 +26,7 @@ struct SpriteRenderPipeline
|
|||
|
||||
};
|
||||
|
||||
ComputePipeline BuildComputePipeline(Nz::RenderDevice& device);
|
||||
std::shared_ptr<Nz::ComputePipeline> BuildComputePipeline(Nz::RenderDevice& device, std::shared_ptr<Nz::RenderPipelineLayout> pipelineLayout, std::shared_ptr<nzsl::ModuleResolver> 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<Nz::Renderer> nazara(rendererConfig);
|
||||
|
||||
|
||||
Nz::RenderDeviceFeatures enabledFeatures;
|
||||
enabledFeatures.computeShaders = true;
|
||||
enabledFeatures.textureReadWrite = true;
|
||||
|
|
@ -73,8 +67,30 @@ int main()
|
|||
std::shared_ptr<Nz::TextureSampler> textureSampler = device->InstantiateTextureSampler({});
|
||||
|
||||
// Compute part
|
||||
ComputePipeline computePipeline = BuildComputePipeline(*device);
|
||||
std::shared_ptr<Nz::ShaderBinding> 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<Nz::RenderPipelineLayout> computePipelineLayout = device->InstantiateRenderPipelineLayout(computePipelineLayoutInfo);
|
||||
|
||||
std::shared_ptr<nzsl::FilesystemModuleResolver> moduleResolver = std::make_shared<nzsl::FilesystemModuleResolver>();
|
||||
moduleResolver->RegisterModuleDirectory(resourceDir / "../shaders/", true);
|
||||
|
||||
std::shared_ptr<Nz::ComputePipeline> computePipeline = BuildComputePipeline(*device, computePipelineLayout, moduleResolver);
|
||||
std::shared_ptr<Nz::ComputePipeline> newComputePipeline;
|
||||
std::atomic_bool hasNewPipeline = false;
|
||||
|
||||
std::shared_ptr<Nz::ShaderBinding> computeBinding = computePipelineLayout->AllocateShaderBinding(0);
|
||||
computeBinding->Update({
|
||||
{
|
||||
0,
|
||||
|
|
@ -92,6 +108,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";
|
||||
|
||||
Nz::RenderWindow window;
|
||||
|
|
@ -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<Nz::ComputePipeline> BuildComputePipeline(Nz::RenderDevice& device, std::shared_ptr<Nz::RenderPipelineLayout> pipelineLayout, std::shared_ptr<nzsl::ModuleResolver> 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<Nz::RenderPipelineLayout> 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)
|
||||
{
|
||||
|
|
|
|||
Loading…
Reference in New Issue