cli(render): add ability to specify params and passes enabled

This commit is contained in:
chyyran 2024-09-26 21:08:39 -04:00 committed by Ronny Chan
parent 3993f57271
commit 91f8089277
11 changed files with 297 additions and 36 deletions

29
CLI.md
View file

@ -46,6 +46,19 @@ Options:
-p, --preset <PRESET> -p, --preset <PRESET>
The path to the shader preset to load The path to the shader preset to load
-w, --wildcards <WILDCARDS>...
Additional wildcard options, comma separated with equals signs. The PRESET and PRESET_DIR wildcards are always added to the preset parsing context.
For example, CONTENT-DIR=MyVerticalGames,GAME=mspacman
--params <PARAMS>...
Parameters to pass to the shader preset, comma separated with equals signs.
For example, crt_gamma=2.5,halation_weight=0.001
--passes-enabled <PASSES_ENABLED>
Set the number of passes enabled for the preset
-i, --image <IMAGE> -i, --image <IMAGE>
The path to the input image The path to the input image
@ -57,10 +70,11 @@ Options:
-r, --runtime <RUNTIME> -r, --runtime <RUNTIME>
The runtime to use to render the shader preset The runtime to use to render the shader preset
[possible values: opengl3, opengl4, vulkan, wgpu, d3d9, d3d11, d3d12, metal] [possible values: opengl3, opengl4, vulkan, wgpu, d3d9, d3d11, d3d12]
-h, --help -h, --help
Print help (see a summary with '-h') Print help (see a summary with '-h')
``` ```
The `render` command can be used to apply a shader preset to an image. The available runtimes will depend on the platform The `render` command can be used to apply a shader preset to an image. The available runtimes will depend on the platform
@ -93,6 +107,19 @@ Options:
-p, --preset <PRESET> -p, --preset <PRESET>
The path to the shader preset to load The path to the shader preset to load
-w, --wildcards <WILDCARDS>...
Additional wildcard options, comma separated with equals signs. The PRESET and PRESET_DIR wildcards are always added to the preset parsing context.
For example, CONTENT-DIR=MyVerticalGames,GAME=mspacman
--params <PARAMS>...
Parameters to pass to the shader preset, comma separated with equals signs.
For example, crt_gamma=2.5,halation_weight=0.001
--passes-enabled <PASSES_ENABLED>
Set the number of passes enabled for the preset
-i, --image <IMAGE> -i, --image <IMAGE>
The path to the input image The path to the input image

View file

@ -7,6 +7,8 @@ use librashader::reflect::cross::{GlslVersion, HlslShaderModel, MslVersion, Spir
use librashader::reflect::naga::{Naga, NagaLoweringOptions}; use librashader::reflect::naga::{Naga, NagaLoweringOptions};
use librashader::reflect::semantics::ShaderSemantics; use librashader::reflect::semantics::ShaderSemantics;
use librashader::reflect::{CompileShader, FromCompilation, ReflectShader, SpirvCompilation}; use librashader::reflect::{CompileShader, FromCompilation, ReflectShader, SpirvCompilation};
use librashader::{FastHashMap, ShortString};
use librashader_runtime::parameters::RuntimeParameters;
use librashader_test::render::RenderTest; use librashader_test::render::RenderTest;
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
@ -28,6 +30,21 @@ enum Commands {
/// The path to the shader preset to load. /// The path to the shader preset to load.
#[arg(short, long)] #[arg(short, long)]
preset: PathBuf, preset: PathBuf,
/// Additional wildcard options, comma separated with equals signs. The PRESET and PRESET_DIR
/// wildcards are always added to the preset parsing context.
///
/// For example, CONTENT-DIR=MyVerticalGames,GAME=mspacman
#[arg(short, long, value_delimiter = ',', num_args = 1..)]
wildcards: Option<Vec<String>>,
/// Parameters to pass to the shader preset, comma separated with equals signs.
///
/// For example, crt_gamma=2.5,halation_weight=0.001
#[arg(long, value_delimiter = ',', num_args = 1..)]
params: Option<Vec<String>>,
/// Set the number of passes enabled for the preset.
#[arg(long)]
passes_enabled: Option<usize>,
/// The path to the input image. /// The path to the input image.
#[arg(short, long)] #[arg(short, long)]
image: PathBuf, image: PathBuf,
@ -49,6 +66,20 @@ enum Commands {
/// The path to the shader preset to load. /// The path to the shader preset to load.
#[arg(short, long)] #[arg(short, long)]
preset: PathBuf, preset: PathBuf,
/// Additional wildcard options, comma separated with equals signs. The PRESET and PRESET_DIR
/// wildcards are always added to the preset parsing context.
///
/// For example, CONTENT-DIR=MyVerticalGames,GAME=mspacman
#[arg(short, long, value_delimiter = ',', num_args = 1..)]
wildcards: Option<Vec<String>>,
/// Parameters to pass to the shader preset, comma separated with equals signs.
///
/// For example, crt_gamma=2.5,halation_weight=0.001
#[arg(long, value_delimiter = ',', num_args = 1..)]
params: Option<Vec<String>>,
/// Set the number of passes enabled for the preset.
#[arg(long)]
passes_enabled: Option<usize>,
/// The path to the input image. /// The path to the input image.
#[arg(short, long)] #[arg(short, long)]
image: PathBuf, image: PathBuf,
@ -241,12 +272,22 @@ pub fn main() -> Result<(), anyhow::Error> {
Commands::Render { Commands::Render {
frame, frame,
preset, preset,
wildcards,
params,
passes_enabled,
image, image,
out, out,
runtime, runtime,
} => { } => {
let test: &mut dyn RenderTest = get_runtime!(runtime, image); let test: &mut dyn RenderTest = get_runtime!(runtime, image);
let image = test.render(preset.as_path(), frame)?; let preset = get_shader_preset(preset, wildcards)?;
let params = parse_params(params)?;
let image = test.render_with_preset_and_params(
preset,
frame,
Some(&|rp| set_params(rp, &params, passes_enabled)),
)?;
if out.as_path() == Path::new("-") { if out.as_path() == Path::new("-") {
let out = std::io::stdout(); let out = std::io::stdout();
@ -258,6 +299,9 @@ pub fn main() -> Result<(), anyhow::Error> {
Commands::Compare { Commands::Compare {
frame, frame,
preset, preset,
wildcards,
params,
passes_enabled,
image, image,
left, left,
right, right,
@ -265,9 +309,22 @@ pub fn main() -> Result<(), anyhow::Error> {
} => { } => {
let left: &mut dyn RenderTest = get_runtime!(left, image); let left: &mut dyn RenderTest = get_runtime!(left, image);
let right: &mut dyn RenderTest = get_runtime!(right, image); let right: &mut dyn RenderTest = get_runtime!(right, image);
let params = parse_params(params)?;
let left_preset = get_shader_preset(preset.clone(), wildcards.clone())?;
let left_image = left.render_with_preset_and_params(
left_preset,
frame,
Some(&|rp| set_params(rp, &params, passes_enabled)),
)?;
let right_preset = get_shader_preset(preset.clone(), wildcards.clone())?;
let right_image = right.render_with_preset_and_params(
right_preset,
frame,
Some(&|rp| set_params(rp, &params, passes_enabled)),
)?;
let left_image = left.render(preset.as_path(), frame)?;
let right_image = right.render(preset.as_path(), frame)?;
let similarity = image_compare::rgba_hybrid_compare(&left_image, &right_image)?; let similarity = image_compare::rgba_hybrid_compare(&left_image, &right_image)?;
print!("{}", similarity.score); print!("{}", similarity.score);
@ -312,7 +369,8 @@ pub fn main() -> Result<(), anyhow::Error> {
librashader::reflect::targets::GLSL::from_compilation(compilation)?; librashader::reflect::targets::GLSL::from_compilation(compilation)?;
compilation.validate()?; compilation.validate()?;
let version = version.map(|s| parse_glsl_version(&s)) let version = version
.map(|s| parse_glsl_version(&s))
.unwrap_or(Ok(GlslVersion::Glsl330))?; .unwrap_or(Ok(GlslVersion::Glsl330))?;
let output = compilation.compile(version)?; let output = compilation.compile(version)?;
@ -326,7 +384,8 @@ pub fn main() -> Result<(), anyhow::Error> {
librashader::reflect::targets::HLSL::from_compilation(compilation)?; librashader::reflect::targets::HLSL::from_compilation(compilation)?;
compilation.validate()?; compilation.validate()?;
let shader_model = version.map(|s| parse_hlsl_version(&s)) let shader_model = version
.map(|s| parse_hlsl_version(&s))
.unwrap_or(Ok(HlslShaderModel::ShaderModel5_0))?; .unwrap_or(Ok(HlslShaderModel::ShaderModel5_0))?;
let output = compilation.compile(Some(shader_model))?; let output = compilation.compile(Some(shader_model))?;
@ -356,7 +415,8 @@ pub fn main() -> Result<(), anyhow::Error> {
>>::from_compilation(compilation)?; >>::from_compilation(compilation)?;
compilation.validate()?; compilation.validate()?;
let version = version.map(|s| parse_msl_version(&s)) let version = version
.map(|s| parse_msl_version(&s))
.unwrap_or(Ok(MslVersion::new(1, 2, 0)))?; .unwrap_or(Ok(MslVersion::new(1, 2, 0)))?;
let output = compilation.compile(Some(version))?; let output = compilation.compile(Some(version))?;
@ -459,6 +519,49 @@ fn get_shader_preset(
Ok(preset) Ok(preset)
} }
fn parse_params(
assignments: Option<Vec<String>>,
) -> anyhow::Result<Option<FastHashMap<ShortString, f32>>> {
let Some(assignments) = assignments else {
return Ok(None);
};
let mut map = FastHashMap::default();
for string in assignments {
let Some((left, right)) = string.split_once("=") else {
return Err(anyhow!("Encountered invalid parameter string {string}"));
};
let value = right
.parse::<f32>()
.map_err(|_| anyhow!("Encountered invalid parameter value: {right}"))?;
map.insert(ShortString::from(left), value);
}
Ok(Some(map))
}
fn set_params(
params: &RuntimeParameters,
assignments: &Option<FastHashMap<ShortString, f32>>,
passes_enabled: Option<usize>,
) {
if let Some(passes_enabled) = passes_enabled {
params.set_passes_enabled(passes_enabled)
};
let Some(assignments) = assignments else {
return;
};
params.update_parameters(|params| {
for (key, param) in assignments {
params.insert(key.clone(), *param);
}
});
}
fn spirv_to_dis(spirv: Vec<u32>) -> anyhow::Result<String> { fn spirv_to_dis(spirv: Vec<u32>) -> anyhow::Result<String> {
let binary = spq_spvasm::SpirvBinary::from(spirv); let binary = spq_spvasm::SpirvBinary::from(spirv);
spq_spvasm::Disassembler::new() spq_spvasm::Disassembler::new()
@ -473,7 +576,7 @@ fn spirv_to_dis(spirv: Vec<u32>) -> anyhow::Result<String> {
fn parse_glsl_version(version_str: &str) -> anyhow::Result<GlslVersion> { fn parse_glsl_version(version_str: &str) -> anyhow::Result<GlslVersion> {
if version_str.contains("es") { if version_str.contains("es") {
let Some(version) = version_str.strip_suffix("es").map(|s| s.trim()) else { let Some(version) = version_str.strip_suffix("es").map(|s| s.trim()) else {
return Err(anyhow!("Unknown GLSL version")) return Err(anyhow!("Unknown GLSL version"));
}; };
Ok(match version { Ok(match version {
@ -516,7 +619,9 @@ fn version_to_usize(version_str: &str) -> anyhow::Result<usize> {
version_str version_str
}; };
let version = version.parse::<usize>().map_err(|_| anyhow!("Invalid version string"))?; let version = version
.parse::<usize>()
.map_err(|_| anyhow!("Invalid version string"))?;
Ok(version) Ok(version)
} }

View file

@ -2,6 +2,7 @@ use crate::render::RenderTest;
use anyhow::anyhow; use anyhow::anyhow;
use image::RgbaImage; use image::RgbaImage;
use librashader::runtime::d3d11::*; use librashader::runtime::d3d11::*;
use librashader::runtime::{FilterChainParameters, RuntimeParameters};
use librashader::runtime::{Size, Viewport}; use librashader::runtime::{Size, Viewport};
use std::path::Path; use std::path::Path;
@ -13,18 +14,28 @@ impl RenderTest for Direct3D11 {
Direct3D11::new(path) Direct3D11::new(path)
} }
fn render(&mut self, path: &Path, frame_count: usize) -> anyhow::Result<RgbaImage> { fn render_with_preset_and_params(
&mut self,
preset: ShaderPreset,
frame_count: usize,
param_setter: Option<&dyn Fn(&RuntimeParameters)>,
) -> anyhow::Result<image::RgbaImage> {
let (renderbuffer, rtv) = self.create_renderbuffer(self.image_bytes.size)?; let (renderbuffer, rtv) = self.create_renderbuffer(self.image_bytes.size)?;
unsafe { unsafe {
let mut filter_chain = FilterChain::load_from_path( let mut filter_chain = FilterChain::load_from_preset(
path, preset,
&self.device, &self.device,
Some(&FilterChainOptions { Some(&FilterChainOptions {
force_no_mipmaps: false, force_no_mipmaps: false,
disable_cache: false, disable_cache: false,
}), }),
)?; )?;
if let Some(setter) = param_setter {
setter(filter_chain.parameters());
}
filter_chain.frame( filter_chain.frame(
None, None,
&self.image_srv, &self.image_srv,
@ -78,6 +89,7 @@ impl RenderTest for Direct3D11 {
} }
} }
use librashader::presets::ShaderPreset;
use librashader_runtime::image::{Image, UVDirection}; use librashader_runtime::image::{Image, UVDirection};
use windows::{ use windows::{
Win32::Foundation::*, Win32::Graphics::Direct3D::*, Win32::Graphics::Direct3D11::*, Win32::Foundation::*, Win32::Graphics::Direct3D::*, Win32::Graphics::Direct3D11::*,

View file

@ -6,10 +6,12 @@ use crate::render::RenderTest;
use anyhow::anyhow; use anyhow::anyhow;
use d3d12_descriptor_heap::{D3D12DescriptorHeap, D3D12DescriptorHeapSlot}; use d3d12_descriptor_heap::{D3D12DescriptorHeap, D3D12DescriptorHeapSlot};
use image::RgbaImage; use image::RgbaImage;
use librashader::presets::ShaderPreset;
use librashader::runtime::d3d12::{ use librashader::runtime::d3d12::{
D3D12InputImage, D3D12OutputView, FilterChain, FilterChainOptions, D3D12InputImage, D3D12OutputView, FilterChain, FilterChainOptions,
}; };
use librashader::runtime::Viewport; use librashader::runtime::Viewport;
use librashader::runtime::{FilterChainParameters, RuntimeParameters};
use librashader_runtime::image::{Image, PixelFormat, UVDirection, BGRA8}; use librashader_runtime::image::{Image, PixelFormat, UVDirection, BGRA8};
use std::path::Path; use std::path::Path;
use windows::core::Interface; use windows::core::Interface;
@ -58,7 +60,12 @@ impl RenderTest for Direct3D12 {
Direct3D12::new(path) Direct3D12::new(path)
} }
fn render(&mut self, path: &Path, frame_count: usize) -> anyhow::Result<RgbaImage> { fn render_with_preset_and_params(
&mut self,
preset: ShaderPreset,
frame_count: usize,
param_setter: Option<&dyn Fn(&RuntimeParameters)>,
) -> anyhow::Result<image::RgbaImage> {
unsafe { unsafe {
let descriptor = self.rtv_heap.allocate_descriptor()?; let descriptor = self.rtv_heap.allocate_descriptor()?;
@ -72,8 +79,8 @@ impl RenderTest for Direct3D12 {
let fence_event = CreateEventA(None, false, false, None)?; let fence_event = CreateEventA(None, false, false, None)?;
let fence: ID3D12Fence = self.device.CreateFence(0, D3D12_FENCE_FLAG_NONE)?; let fence: ID3D12Fence = self.device.CreateFence(0, D3D12_FENCE_FLAG_NONE)?;
let mut filter_chain = FilterChain::load_from_path( let mut filter_chain = FilterChain::load_from_preset(
path, preset,
&self.device, &self.device,
Some(&FilterChainOptions { Some(&FilterChainOptions {
force_hlsl_pipeline: false, force_hlsl_pipeline: false,
@ -82,6 +89,10 @@ impl RenderTest for Direct3D12 {
}), }),
)?; )?;
if let Some(setter) = param_setter {
setter(filter_chain.parameters());
}
let mut output_texture = None; let mut output_texture = None;
let desc = D3D12_RESOURCE_DESC { let desc = D3D12_RESOURCE_DESC {
Dimension: D3D12_RESOURCE_DIMENSION_TEXTURE2D, Dimension: D3D12_RESOURCE_DIMENSION_TEXTURE2D,

View file

@ -1,8 +1,10 @@
use crate::render::RenderTest; use crate::render::RenderTest;
use anyhow::anyhow; use anyhow::anyhow;
use image::RgbaImage; use image::RgbaImage;
use librashader::presets::ShaderPreset;
use librashader::runtime::d3d9::{FilterChain, FilterChainOptions}; use librashader::runtime::d3d9::{FilterChain, FilterChainOptions};
use librashader::runtime::Viewport; use librashader::runtime::Viewport;
use librashader::runtime::{FilterChainParameters, RuntimeParameters};
use librashader_runtime::image::{Image, PixelFormat, UVDirection, BGRA8}; use librashader_runtime::image::{Image, PixelFormat, UVDirection, BGRA8};
use std::path::Path; use std::path::Path;
use windows::Win32::Foundation::{HWND, TRUE}; use windows::Win32::Foundation::{HWND, TRUE};
@ -29,10 +31,15 @@ impl RenderTest for Direct3D9 {
Direct3D9::new(path) Direct3D9::new(path)
} }
fn render(&mut self, path: &Path, frame_count: usize) -> anyhow::Result<RgbaImage> { fn render_with_preset_and_params(
&mut self,
preset: ShaderPreset,
frame_count: usize,
param_setter: Option<&dyn Fn(&RuntimeParameters)>,
) -> anyhow::Result<image::RgbaImage> {
unsafe { unsafe {
let mut filter_chain = FilterChain::load_from_path( let mut filter_chain = FilterChain::load_from_preset(
path, preset,
&self.device, &self.device,
Some(&FilterChainOptions { Some(&FilterChainOptions {
force_no_mipmaps: false, force_no_mipmaps: false,
@ -40,6 +47,10 @@ impl RenderTest for Direct3D9 {
}), }),
)?; )?;
if let Some(setter) = param_setter {
setter(filter_chain.parameters());
}
let mut render_texture = None; let mut render_texture = None;
self.device.CreateTexture( self.device.CreateTexture(

View file

@ -5,8 +5,10 @@ use crate::render::RenderTest;
use anyhow::anyhow; use anyhow::anyhow;
use glow::{HasContext, PixelPackData, PixelUnpackData}; use glow::{HasContext, PixelPackData, PixelUnpackData};
use image::RgbaImage; use image::RgbaImage;
use librashader::presets::ShaderPreset;
use librashader::runtime::gl::{FilterChain, FilterChainOptions, GLImage}; use librashader::runtime::gl::{FilterChain, FilterChainOptions, GLImage};
use librashader::runtime::Viewport; use librashader::runtime::Viewport;
use librashader::runtime::{FilterChainParameters, RuntimeParameters};
use librashader_runtime::image::{Image, UVDirection, RGBA8}; use librashader_runtime::image::{Image, UVDirection, RGBA8};
use std::path::Path; use std::path::Path;
use std::sync::Arc; use std::sync::Arc;
@ -28,10 +30,15 @@ impl RenderTest for OpenGl3 {
OpenGl3::new(path) OpenGl3::new(path)
} }
fn render(&mut self, path: &Path, frame_count: usize) -> anyhow::Result<RgbaImage> { fn render_with_preset_and_params(
&mut self,
preset: ShaderPreset,
frame_count: usize,
param_setter: Option<&dyn Fn(&RuntimeParameters)>,
) -> anyhow::Result<image::RgbaImage> {
let mut filter_chain = unsafe { let mut filter_chain = unsafe {
FilterChain::load_from_path( FilterChain::load_from_preset(
path, preset,
Arc::clone(&self.0.context.gl), Arc::clone(&self.0.context.gl),
Some(&FilterChainOptions { Some(&FilterChainOptions {
glsl_version: 330, glsl_version: 330,
@ -42,6 +49,10 @@ impl RenderTest for OpenGl3 {
) )
}?; }?;
if let Some(setter) = param_setter {
setter(filter_chain.parameters());
}
Ok(self.0.render(&mut filter_chain, frame_count)?) Ok(self.0.render(&mut filter_chain, frame_count)?)
} }
} }
@ -54,10 +65,15 @@ impl RenderTest for OpenGl4 {
OpenGl4::new(path) OpenGl4::new(path)
} }
fn render(&mut self, path: &Path, frame_count: usize) -> anyhow::Result<RgbaImage> { fn render_with_preset_and_params(
&mut self,
preset: ShaderPreset,
frame_count: usize,
param_setter: Option<&dyn Fn(&RuntimeParameters)>,
) -> anyhow::Result<image::RgbaImage> {
let mut filter_chain = unsafe { let mut filter_chain = unsafe {
FilterChain::load_from_path( FilterChain::load_from_preset(
path, preset,
Arc::clone(&self.0.context.gl), Arc::clone(&self.0.context.gl),
Some(&FilterChainOptions { Some(&FilterChainOptions {
glsl_version: 460, glsl_version: 460,
@ -68,6 +84,10 @@ impl RenderTest for OpenGl4 {
) )
}?; }?;
if let Some(setter) = param_setter {
setter(filter_chain.parameters());
}
Ok(self.0.render(&mut filter_chain, frame_count)?) Ok(self.0.render(&mut filter_chain, frame_count)?)
} }
} }

View file

@ -19,6 +19,8 @@ pub mod wgpu;
#[cfg(all(target_vendor = "apple", feature = "metal"))] #[cfg(all(target_vendor = "apple", feature = "metal"))]
pub mod mtl; pub mod mtl;
use librashader::presets::ShaderPreset;
use librashader_runtime::parameters::RuntimeParameters;
use std::path::Path; use std::path::Path;
/// Test harness to set up a device, render a triangle, and apply a shader /// Test harness to set up a device, render a triangle, and apply a shader
@ -36,7 +38,41 @@ pub trait RenderTest {
/// For testing purposes, it is often that a single image will be reused with multiple /// For testing purposes, it is often that a single image will be reused with multiple
/// shader presets, so the actual image that a shader will be applied to /// shader presets, so the actual image that a shader will be applied to
/// will often be part of the test harness object. /// will often be part of the test harness object.
fn render(&mut self, path: &Path, frame_count: usize) -> anyhow::Result<image::RgbaImage>; fn render(&mut self, path: &Path, frame_count: usize) -> anyhow::Result<image::RgbaImage> {
let preset = ShaderPreset::try_parse(path)?;
self.render_with_preset(preset, frame_count)
}
/// Render a shader onto an image buffer, applying the provided shader.
///
/// The test should render in linear colour space for proper comparison against
/// backends.
///
/// For testing purposes, it is often that a single image will be reused with multiple
/// shader presets, so the actual image that a shader will be applied to
/// will often be part of the test harness object.
fn render_with_preset(
&mut self,
preset: ShaderPreset,
frame_count: usize,
) -> anyhow::Result<image::RgbaImage> {
self.render_with_preset_and_params(preset, frame_count, None)
}
/// Render a shader onto an image buffer, applying the provided shader.
///
/// The test should render in linear colour space for proper comparison against
/// backends.
///
/// For testing purposes, it is often that a single image will be reused with multiple
/// shader presets, so the actual image that a shader will be applied to
/// will often be part of the test harness object.
fn render_with_preset_and_params(
&mut self,
preset: ShaderPreset,
frame_count: usize,
param_setter: Option<&dyn Fn(&RuntimeParameters)>,
) -> anyhow::Result<image::RgbaImage>;
} }
#[cfg(test)] #[cfg(test)]

View file

@ -1,8 +1,10 @@
use crate::render::RenderTest; use crate::render::RenderTest;
use anyhow::anyhow; use anyhow::anyhow;
use image::RgbaImage; use image::RgbaImage;
use librashader::presets::ShaderPreset;
use librashader::runtime::mtl::{FilterChain, FilterChainOptions}; use librashader::runtime::mtl::{FilterChain, FilterChainOptions};
use librashader::runtime::Viewport; use librashader::runtime::Viewport;
use librashader::runtime::{FilterChainParameters, RuntimeParameters};
use librashader_runtime::image::{Image, PixelFormat, UVDirection, BGRA8, RGBA8}; use librashader_runtime::image::{Image, PixelFormat, UVDirection, BGRA8, RGBA8};
use objc2::ffi::NSUInteger; use objc2::ffi::NSUInteger;
use objc2::rc::Retained; use objc2::rc::Retained;
@ -29,7 +31,12 @@ impl RenderTest for Metal {
Metal::new(path) Metal::new(path)
} }
fn render(&mut self, path: &Path, frame_count: usize) -> anyhow::Result<RgbaImage> { fn render_with_preset_and_params(
&mut self,
preset: ShaderPreset,
frame_count: usize,
param_setter: Option<&dyn Fn(&RuntimeParameters)>,
) -> anyhow::Result<image::RgbaImage> {
let queue = self let queue = self
.device .device
.newCommandQueue() .newCommandQueue()
@ -39,14 +46,18 @@ impl RenderTest for Metal {
.commandBuffer() .commandBuffer()
.ok_or_else(|| anyhow!("Unable to create command buffer"))?; .ok_or_else(|| anyhow!("Unable to create command buffer"))?;
let mut filter_chain = FilterChain::load_from_path( let mut filter_chain = FilterChain::load_from_preset(
path, preset,
&queue, &queue,
Some(&FilterChainOptions { Some(&FilterChainOptions {
force_no_mipmaps: false, force_no_mipmaps: false,
}), }),
)?; )?;
if let Some(setter) = param_setter {
setter(filter_chain.parameters());
}
let render_texture = unsafe { let render_texture = unsafe {
let texture_descriptor = let texture_descriptor =
MTLTextureDescriptor::texture2DDescriptorWithPixelFormat_width_height_mipmapped( MTLTextureDescriptor::texture2DDescriptorWithPixelFormat_width_height_mipmapped(

View file

@ -5,8 +5,10 @@ use anyhow::anyhow;
use ash::vk; use ash::vk;
use gpu_allocator::MemoryLocation; use gpu_allocator::MemoryLocation;
use image::RgbaImage; use image::RgbaImage;
use librashader::presets::ShaderPreset;
use librashader::runtime::vk::{FilterChain, FilterChainOptions, VulkanImage}; use librashader::runtime::vk::{FilterChain, FilterChainOptions, VulkanImage};
use librashader::runtime::Viewport; use librashader::runtime::Viewport;
use librashader::runtime::{FilterChainParameters, RuntimeParameters};
use librashader_runtime::image::{Image, UVDirection, BGRA8}; use librashader_runtime::image::{Image, UVDirection, BGRA8};
use std::path::Path; use std::path::Path;
@ -30,10 +32,15 @@ impl RenderTest for Vulkan {
Vulkan::new(path) Vulkan::new(path)
} }
fn render(&mut self, path: &Path, frame_count: usize) -> anyhow::Result<RgbaImage> { fn render_with_preset_and_params(
&mut self,
preset: ShaderPreset,
frame_count: usize,
param_setter: Option<&dyn Fn(&RuntimeParameters)>,
) -> anyhow::Result<image::RgbaImage> {
unsafe { unsafe {
let mut filter_chain = FilterChain::load_from_path( let mut filter_chain = FilterChain::load_from_preset(
path, preset,
&self.vk, &self.vk,
Some(&FilterChainOptions { Some(&FilterChainOptions {
frames_in_flight: 3, frames_in_flight: 3,
@ -43,6 +50,10 @@ impl RenderTest for Vulkan {
}), }),
)?; )?;
if let Some(setter) = param_setter {
setter(filter_chain.parameters());
}
let image_info = vk::ImageCreateInfo::default() let image_info = vk::ImageCreateInfo::default()
.image_type(vk::ImageType::TYPE_2D) .image_type(vk::ImageType::TYPE_2D)
.format(vk::Format::B8G8R8A8_UNORM) .format(vk::Format::B8G8R8A8_UNORM)

View file

@ -14,6 +14,8 @@ use wgpu_types::{
ImageDataLayout, Maintain, TextureDescriptor, TextureDimension, TextureFormat, TextureUsages, ImageDataLayout, Maintain, TextureDescriptor, TextureDimension, TextureFormat, TextureUsages,
}; };
use librashader::presets::ShaderPreset;
use librashader::runtime::{FilterChainParameters, RuntimeParameters};
use parking_lot::Mutex; use parking_lot::Mutex;
pub struct Wgpu { pub struct Wgpu {
@ -54,9 +56,14 @@ impl RenderTest for Wgpu {
Wgpu::new(path) Wgpu::new(path)
} }
fn render(&mut self, path: &Path, frame_count: usize) -> anyhow::Result<RgbaImage> { fn render_with_preset_and_params(
let mut chain = FilterChain::load_from_path( &mut self,
path, preset: ShaderPreset,
frame_count: usize,
param_setter: Option<&dyn Fn(&RuntimeParameters)>,
) -> anyhow::Result<image::RgbaImage> {
let mut chain = FilterChain::load_from_preset(
preset,
Arc::clone(&self.device), Arc::clone(&self.device),
Arc::clone(&self.queue), Arc::clone(&self.queue),
Some(&FilterChainOptions { Some(&FilterChainOptions {
@ -65,7 +72,9 @@ impl RenderTest for Wgpu {
adapter_info: None, adapter_info: None,
}), }),
)?; )?;
if let Some(setter) = param_setter {
setter(&chain.parameters());
}
let mut cmd = self let mut cmd = self
.device .device
.create_command_encoder(&CommandEncoderDescriptor { label: None }); .create_command_encoder(&CommandEncoderDescriptor { label: None });

View file

@ -42,6 +42,7 @@ impl RuntimeParameters {
/// Set a runtime parameter. /// Set a runtime parameter.
/// ///
/// This is a relatively slow operation as it will be synchronized across threads. /// This is a relatively slow operation as it will be synchronized across threads.
/// If updating multiple parameters, see [`RuntimeParameters::update_parameters`].
pub fn set_parameter_value(&self, name: &str, new_value: f32) -> Option<f32> { pub fn set_parameter_value(&self, name: &str, new_value: f32) -> Option<f32> {
let mut updated_map = FastHashMap::clone(&self.parameters.load()); let mut updated_map = FastHashMap::clone(&self.parameters.load());
@ -57,6 +58,13 @@ impl RuntimeParameters {
} }
} }
/// Update multiple runtime parameters atomically through a function.
pub fn update_parameters(&self, updater: impl FnOnce(&mut FastHashMap<ShortString, f32>)) {
let mut updated_map = FastHashMap::clone(&self.parameters.load());
updater(&mut updated_map);
self.parameters.store(Arc::new(updated_map));
}
/// Get a reference to the runtime parameters. /// Get a reference to the runtime parameters.
pub fn parameters(&self) -> Arc<FastHashMap<ShortString, f32>> { pub fn parameters(&self) -> Arc<FastHashMap<ShortString, f32>> {
self.parameters.load_full() self.parameters.load_full()