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/Math.hpp>
|
||||||
#include <Nazara/Platform.hpp>
|
#include <Nazara/Platform.hpp>
|
||||||
#include <Nazara/Renderer.hpp>
|
#include <Nazara/Renderer.hpp>
|
||||||
|
#include <NZSL/FilesystemModuleResolver.hpp>
|
||||||
#include <NZSL/LangWriter.hpp>
|
#include <NZSL/LangWriter.hpp>
|
||||||
#include <NZSL/Parser.hpp>
|
#include <NZSL/Parser.hpp>
|
||||||
#include <Nazara/Utility.hpp>
|
#include <Nazara/Utility.hpp>
|
||||||
|
|
@ -12,12 +13,6 @@
|
||||||
|
|
||||||
NAZARA_REQUEST_DEDICATED_GPU()
|
NAZARA_REQUEST_DEDICATED_GPU()
|
||||||
|
|
||||||
struct ComputePipeline
|
|
||||||
{
|
|
||||||
std::shared_ptr<Nz::RenderPipelineLayout> layout;
|
|
||||||
std::shared_ptr<Nz::ComputePipeline> pipeline;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct SpriteRenderData
|
struct SpriteRenderData
|
||||||
{
|
{
|
||||||
std::shared_ptr<Nz::RenderBuffer> vertexBuffer;
|
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);
|
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);
|
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::Modules<Nz::Renderer> nazara(rendererConfig);
|
||||||
|
|
||||||
|
|
||||||
Nz::RenderDeviceFeatures enabledFeatures;
|
Nz::RenderDeviceFeatures enabledFeatures;
|
||||||
enabledFeatures.computeShaders = true;
|
enabledFeatures.computeShaders = true;
|
||||||
enabledFeatures.textureReadWrite = true;
|
enabledFeatures.textureReadWrite = true;
|
||||||
|
|
@ -73,8 +67,30 @@ int main()
|
||||||
std::shared_ptr<Nz::TextureSampler> textureSampler = device->InstantiateTextureSampler({});
|
std::shared_ptr<Nz::TextureSampler> textureSampler = device->InstantiateTextureSampler({});
|
||||||
|
|
||||||
// Compute part
|
// Compute part
|
||||||
ComputePipeline computePipeline = BuildComputePipeline(*device);
|
Nz::RenderPipelineLayoutInfo computePipelineLayoutInfo;
|
||||||
std::shared_ptr<Nz::ShaderBinding> computeBinding = computePipeline.layout->AllocateShaderBinding(0);
|
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({
|
computeBinding->Update({
|
||||||
{
|
{
|
||||||
0,
|
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";
|
std::string windowTitle = "Compute test";
|
||||||
|
|
||||||
|
|
@ -123,15 +147,22 @@ int main()
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (hasNewPipeline)
|
||||||
|
{
|
||||||
|
frame.PushForRelease(std::move(computePipeline));
|
||||||
|
computePipeline = std::move(newComputePipeline);
|
||||||
|
hasNewPipeline = false;
|
||||||
|
}
|
||||||
|
|
||||||
const Nz::RenderTarget* windowRT = window.GetRenderTarget();
|
const Nz::RenderTarget* windowRT = window.GetRenderTarget();
|
||||||
frame.Execute([&](Nz::CommandBufferBuilder& builder)
|
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::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.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.BindComputeShaderBinding(0, *computeBinding);
|
||||||
builder.Dispatch(destTexParams.width / 32, destTexParams.height / 32, 1);
|
builder.Dispatch(destTexParams.width / 32, destTexParams.height / 32, 1);
|
||||||
|
|
||||||
|
|
@ -187,74 +218,11 @@ int main()
|
||||||
return EXIT_SUCCESS;
|
return EXIT_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Edge detection, translated to NZSL from Sascha Willems compute shader example
|
std::shared_ptr<Nz::ComputePipeline> BuildComputePipeline(Nz::RenderDevice& device, std::shared_ptr<Nz::RenderPipelineLayout> pipelineLayout, std::shared_ptr<nzsl::ModuleResolver> moduleResolver)
|
||||||
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)
|
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
nzsl::Ast::ModulePtr shaderModule = nzsl::Parse(std::string_view(computeSource, sizeof(computeSource)));
|
nzsl::Ast::ModulePtr shaderModule = moduleResolver->Resolve("Compute.Sepia");
|
||||||
if (!shaderModule)
|
if (!shaderModule)
|
||||||
{
|
{
|
||||||
std::cout << "Failed to parse shader module" << std::endl;
|
std::cout << "Failed to parse shader module" << std::endl;
|
||||||
|
|
@ -271,22 +239,6 @@ ComputePipeline BuildComputePipeline(Nz::RenderDevice& device)
|
||||||
std::abort();
|
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;
|
Nz::ComputePipelineInfo computePipelineInfo;
|
||||||
computePipelineInfo.pipelineLayout = pipelineLayout;
|
computePipelineInfo.pipelineLayout = pipelineLayout;
|
||||||
computePipelineInfo.shaderModule = computeShader;
|
computePipelineInfo.shaderModule = computeShader;
|
||||||
|
|
@ -298,10 +250,7 @@ ComputePipeline BuildComputePipeline(Nz::RenderDevice& device)
|
||||||
std::abort();
|
std::abort();
|
||||||
}
|
}
|
||||||
|
|
||||||
ComputePipeline result;
|
return pipeline;
|
||||||
result.layout = std::move(pipelineLayout);
|
|
||||||
result.pipeline = std::move(pipeline);
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
catch (const std::exception& e)
|
catch (const std::exception& e)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue