From 91f8089277cdba55bebf6cf9b1b50931e6dbe030 Mon Sep 17 00:00:00 2001 From: chyyran Date: Thu, 26 Sep 2024 21:08:39 -0400 Subject: [PATCH] cli(render): add ability to specify params and passes enabled --- CLI.md | 29 +++++- librashader-cli/src/cli/main.rs | 123 ++++++++++++++++++++++-- librashader-cli/src/render/d3d11.rs | 18 +++- librashader-cli/src/render/d3d12/mod.rs | 17 +++- librashader-cli/src/render/d3d9.rs | 17 +++- librashader-cli/src/render/gl/mod.rs | 32 ++++-- librashader-cli/src/render/mod.rs | 38 +++++++- librashader-cli/src/render/mtl.rs | 17 +++- librashader-cli/src/render/vk/mod.rs | 17 +++- librashader-cli/src/render/wgpu.rs | 17 +++- librashader-runtime/src/parameters.rs | 8 ++ 11 files changed, 297 insertions(+), 36 deletions(-) diff --git a/CLI.md b/CLI.md index f953a02..9702dce 100644 --- a/CLI.md +++ b/CLI.md @@ -46,6 +46,19 @@ Options: -p, --preset The path to the shader preset to load + -w, --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 ... + Parameters to pass to the shader preset, comma separated with equals signs. + + For example, crt_gamma=2.5,halation_weight=0.001 + + --passes-enabled + Set the number of passes enabled for the preset + -i, --image The path to the input image @@ -57,10 +70,11 @@ Options: -r, --runtime 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 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 @@ -93,6 +107,19 @@ Options: -p, --preset The path to the shader preset to load + -w, --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 ... + Parameters to pass to the shader preset, comma separated with equals signs. + + For example, crt_gamma=2.5,halation_weight=0.001 + + --passes-enabled + Set the number of passes enabled for the preset + -i, --image The path to the input image diff --git a/librashader-cli/src/cli/main.rs b/librashader-cli/src/cli/main.rs index 6f71450..2c8fee4 100644 --- a/librashader-cli/src/cli/main.rs +++ b/librashader-cli/src/cli/main.rs @@ -7,6 +7,8 @@ use librashader::reflect::cross::{GlslVersion, HlslShaderModel, MslVersion, Spir use librashader::reflect::naga::{Naga, NagaLoweringOptions}; use librashader::reflect::semantics::ShaderSemantics; use librashader::reflect::{CompileShader, FromCompilation, ReflectShader, SpirvCompilation}; +use librashader::{FastHashMap, ShortString}; +use librashader_runtime::parameters::RuntimeParameters; use librashader_test::render::RenderTest; use std::path::{Path, PathBuf}; @@ -28,6 +30,21 @@ enum Commands { /// The path to the shader preset to load. #[arg(short, long)] 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>, + /// 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>, + + /// Set the number of passes enabled for the preset. + #[arg(long)] + passes_enabled: Option, /// The path to the input image. #[arg(short, long)] image: PathBuf, @@ -49,6 +66,20 @@ enum Commands { /// The path to the shader preset to load. #[arg(short, long)] 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>, + /// 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>, + /// Set the number of passes enabled for the preset. + #[arg(long)] + passes_enabled: Option, /// The path to the input image. #[arg(short, long)] image: PathBuf, @@ -241,12 +272,22 @@ pub fn main() -> Result<(), anyhow::Error> { Commands::Render { frame, preset, + wildcards, + params, + passes_enabled, image, out, runtime, } => { 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, ¶ms, passes_enabled)), + )?; if out.as_path() == Path::new("-") { let out = std::io::stdout(); @@ -258,6 +299,9 @@ pub fn main() -> Result<(), anyhow::Error> { Commands::Compare { frame, preset, + wildcards, + params, + passes_enabled, image, left, right, @@ -265,9 +309,22 @@ pub fn main() -> Result<(), anyhow::Error> { } => { let left: &mut dyn RenderTest = get_runtime!(left, 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, ¶ms, 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, ¶ms, 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)?; print!("{}", similarity.score); @@ -312,7 +369,8 @@ pub fn main() -> Result<(), anyhow::Error> { librashader::reflect::targets::GLSL::from_compilation(compilation)?; 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))?; let output = compilation.compile(version)?; @@ -326,7 +384,8 @@ pub fn main() -> Result<(), anyhow::Error> { librashader::reflect::targets::HLSL::from_compilation(compilation)?; 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))?; let output = compilation.compile(Some(shader_model))?; @@ -356,7 +415,8 @@ pub fn main() -> Result<(), anyhow::Error> { >>::from_compilation(compilation)?; 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)))?; let output = compilation.compile(Some(version))?; @@ -459,6 +519,49 @@ fn get_shader_preset( Ok(preset) } +fn parse_params( + assignments: Option>, +) -> anyhow::Result>> { + 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::() + .map_err(|_| anyhow!("Encountered invalid parameter value: {right}"))?; + + map.insert(ShortString::from(left), value); + } + + Ok(Some(map)) +} + +fn set_params( + params: &RuntimeParameters, + assignments: &Option>, + passes_enabled: Option, +) { + 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) -> anyhow::Result { let binary = spq_spvasm::SpirvBinary::from(spirv); spq_spvasm::Disassembler::new() @@ -473,7 +576,7 @@ fn spirv_to_dis(spirv: Vec) -> anyhow::Result { fn parse_glsl_version(version_str: &str) -> anyhow::Result { if version_str.contains("es") { 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 { @@ -516,7 +619,9 @@ fn version_to_usize(version_str: &str) -> anyhow::Result { version_str }; - let version = version.parse::().map_err(|_| anyhow!("Invalid version string"))?; + let version = version + .parse::() + .map_err(|_| anyhow!("Invalid version string"))?; Ok(version) } @@ -562,4 +667,4 @@ fn parse_msl_version(version_str: &str) -> anyhow::Result { } _ => return Err(anyhow!("Unknown MSL version")), }) -} \ No newline at end of file +} diff --git a/librashader-cli/src/render/d3d11.rs b/librashader-cli/src/render/d3d11.rs index f226a70..816a01c 100644 --- a/librashader-cli/src/render/d3d11.rs +++ b/librashader-cli/src/render/d3d11.rs @@ -2,6 +2,7 @@ use crate::render::RenderTest; use anyhow::anyhow; use image::RgbaImage; use librashader::runtime::d3d11::*; +use librashader::runtime::{FilterChainParameters, RuntimeParameters}; use librashader::runtime::{Size, Viewport}; use std::path::Path; @@ -13,18 +14,28 @@ impl RenderTest for Direct3D11 { Direct3D11::new(path) } - fn render(&mut self, path: &Path, frame_count: usize) -> anyhow::Result { + fn render_with_preset_and_params( + &mut self, + preset: ShaderPreset, + frame_count: usize, + param_setter: Option<&dyn Fn(&RuntimeParameters)>, + ) -> anyhow::Result { let (renderbuffer, rtv) = self.create_renderbuffer(self.image_bytes.size)?; unsafe { - let mut filter_chain = FilterChain::load_from_path( - path, + let mut filter_chain = FilterChain::load_from_preset( + preset, &self.device, Some(&FilterChainOptions { force_no_mipmaps: false, disable_cache: false, }), )?; + + if let Some(setter) = param_setter { + setter(filter_chain.parameters()); + } + filter_chain.frame( None, &self.image_srv, @@ -78,6 +89,7 @@ impl RenderTest for Direct3D11 { } } +use librashader::presets::ShaderPreset; use librashader_runtime::image::{Image, UVDirection}; use windows::{ Win32::Foundation::*, Win32::Graphics::Direct3D::*, Win32::Graphics::Direct3D11::*, diff --git a/librashader-cli/src/render/d3d12/mod.rs b/librashader-cli/src/render/d3d12/mod.rs index 791f88a..51f8377 100644 --- a/librashader-cli/src/render/d3d12/mod.rs +++ b/librashader-cli/src/render/d3d12/mod.rs @@ -6,10 +6,12 @@ use crate::render::RenderTest; use anyhow::anyhow; use d3d12_descriptor_heap::{D3D12DescriptorHeap, D3D12DescriptorHeapSlot}; use image::RgbaImage; +use librashader::presets::ShaderPreset; use librashader::runtime::d3d12::{ D3D12InputImage, D3D12OutputView, FilterChain, FilterChainOptions, }; use librashader::runtime::Viewport; +use librashader::runtime::{FilterChainParameters, RuntimeParameters}; use librashader_runtime::image::{Image, PixelFormat, UVDirection, BGRA8}; use std::path::Path; use windows::core::Interface; @@ -58,7 +60,12 @@ impl RenderTest for Direct3D12 { Direct3D12::new(path) } - fn render(&mut self, path: &Path, frame_count: usize) -> anyhow::Result { + fn render_with_preset_and_params( + &mut self, + preset: ShaderPreset, + frame_count: usize, + param_setter: Option<&dyn Fn(&RuntimeParameters)>, + ) -> anyhow::Result { unsafe { 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: ID3D12Fence = self.device.CreateFence(0, D3D12_FENCE_FLAG_NONE)?; - let mut filter_chain = FilterChain::load_from_path( - path, + let mut filter_chain = FilterChain::load_from_preset( + preset, &self.device, Some(&FilterChainOptions { 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 desc = D3D12_RESOURCE_DESC { Dimension: D3D12_RESOURCE_DIMENSION_TEXTURE2D, diff --git a/librashader-cli/src/render/d3d9.rs b/librashader-cli/src/render/d3d9.rs index fb68ebf..d2d62cb 100644 --- a/librashader-cli/src/render/d3d9.rs +++ b/librashader-cli/src/render/d3d9.rs @@ -1,8 +1,10 @@ use crate::render::RenderTest; use anyhow::anyhow; use image::RgbaImage; +use librashader::presets::ShaderPreset; use librashader::runtime::d3d9::{FilterChain, FilterChainOptions}; use librashader::runtime::Viewport; +use librashader::runtime::{FilterChainParameters, RuntimeParameters}; use librashader_runtime::image::{Image, PixelFormat, UVDirection, BGRA8}; use std::path::Path; use windows::Win32::Foundation::{HWND, TRUE}; @@ -29,10 +31,15 @@ impl RenderTest for Direct3D9 { Direct3D9::new(path) } - fn render(&mut self, path: &Path, frame_count: usize) -> anyhow::Result { + fn render_with_preset_and_params( + &mut self, + preset: ShaderPreset, + frame_count: usize, + param_setter: Option<&dyn Fn(&RuntimeParameters)>, + ) -> anyhow::Result { unsafe { - let mut filter_chain = FilterChain::load_from_path( - path, + let mut filter_chain = FilterChain::load_from_preset( + preset, &self.device, Some(&FilterChainOptions { 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; self.device.CreateTexture( diff --git a/librashader-cli/src/render/gl/mod.rs b/librashader-cli/src/render/gl/mod.rs index 62fd0dc..3d459fa 100644 --- a/librashader-cli/src/render/gl/mod.rs +++ b/librashader-cli/src/render/gl/mod.rs @@ -5,8 +5,10 @@ use crate::render::RenderTest; use anyhow::anyhow; use glow::{HasContext, PixelPackData, PixelUnpackData}; use image::RgbaImage; +use librashader::presets::ShaderPreset; use librashader::runtime::gl::{FilterChain, FilterChainOptions, GLImage}; use librashader::runtime::Viewport; +use librashader::runtime::{FilterChainParameters, RuntimeParameters}; use librashader_runtime::image::{Image, UVDirection, RGBA8}; use std::path::Path; use std::sync::Arc; @@ -28,10 +30,15 @@ impl RenderTest for OpenGl3 { OpenGl3::new(path) } - fn render(&mut self, path: &Path, frame_count: usize) -> anyhow::Result { + fn render_with_preset_and_params( + &mut self, + preset: ShaderPreset, + frame_count: usize, + param_setter: Option<&dyn Fn(&RuntimeParameters)>, + ) -> anyhow::Result { let mut filter_chain = unsafe { - FilterChain::load_from_path( - path, + FilterChain::load_from_preset( + preset, Arc::clone(&self.0.context.gl), Some(&FilterChainOptions { 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)?) } } @@ -54,10 +65,15 @@ impl RenderTest for OpenGl4 { OpenGl4::new(path) } - fn render(&mut self, path: &Path, frame_count: usize) -> anyhow::Result { + fn render_with_preset_and_params( + &mut self, + preset: ShaderPreset, + frame_count: usize, + param_setter: Option<&dyn Fn(&RuntimeParameters)>, + ) -> anyhow::Result { let mut filter_chain = unsafe { - FilterChain::load_from_path( - path, + FilterChain::load_from_preset( + preset, Arc::clone(&self.0.context.gl), Some(&FilterChainOptions { 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)?) } } diff --git a/librashader-cli/src/render/mod.rs b/librashader-cli/src/render/mod.rs index e374ff9..de97f67 100644 --- a/librashader-cli/src/render/mod.rs +++ b/librashader-cli/src/render/mod.rs @@ -19,6 +19,8 @@ pub mod wgpu; #[cfg(all(target_vendor = "apple", feature = "metal"))] pub mod mtl; +use librashader::presets::ShaderPreset; +use librashader_runtime::parameters::RuntimeParameters; use std::path::Path; /// 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 /// shader presets, so the actual image that a shader will be applied to /// will often be part of the test harness object. - fn render(&mut self, path: &Path, frame_count: usize) -> anyhow::Result; + fn render(&mut self, path: &Path, frame_count: usize) -> anyhow::Result { + 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 { + 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; } #[cfg(test)] diff --git a/librashader-cli/src/render/mtl.rs b/librashader-cli/src/render/mtl.rs index 9d45740..555edfb 100644 --- a/librashader-cli/src/render/mtl.rs +++ b/librashader-cli/src/render/mtl.rs @@ -1,8 +1,10 @@ use crate::render::RenderTest; use anyhow::anyhow; use image::RgbaImage; +use librashader::presets::ShaderPreset; use librashader::runtime::mtl::{FilterChain, FilterChainOptions}; use librashader::runtime::Viewport; +use librashader::runtime::{FilterChainParameters, RuntimeParameters}; use librashader_runtime::image::{Image, PixelFormat, UVDirection, BGRA8, RGBA8}; use objc2::ffi::NSUInteger; use objc2::rc::Retained; @@ -29,7 +31,12 @@ impl RenderTest for Metal { Metal::new(path) } - fn render(&mut self, path: &Path, frame_count: usize) -> anyhow::Result { + fn render_with_preset_and_params( + &mut self, + preset: ShaderPreset, + frame_count: usize, + param_setter: Option<&dyn Fn(&RuntimeParameters)>, + ) -> anyhow::Result { let queue = self .device .newCommandQueue() @@ -39,14 +46,18 @@ impl RenderTest for Metal { .commandBuffer() .ok_or_else(|| anyhow!("Unable to create command buffer"))?; - let mut filter_chain = FilterChain::load_from_path( - path, + let mut filter_chain = FilterChain::load_from_preset( + preset, &queue, Some(&FilterChainOptions { force_no_mipmaps: false, }), )?; + if let Some(setter) = param_setter { + setter(filter_chain.parameters()); + } + let render_texture = unsafe { let texture_descriptor = MTLTextureDescriptor::texture2DDescriptorWithPixelFormat_width_height_mipmapped( diff --git a/librashader-cli/src/render/vk/mod.rs b/librashader-cli/src/render/vk/mod.rs index 4592890..8be8fd4 100644 --- a/librashader-cli/src/render/vk/mod.rs +++ b/librashader-cli/src/render/vk/mod.rs @@ -5,8 +5,10 @@ use anyhow::anyhow; use ash::vk; use gpu_allocator::MemoryLocation; use image::RgbaImage; +use librashader::presets::ShaderPreset; use librashader::runtime::vk::{FilterChain, FilterChainOptions, VulkanImage}; use librashader::runtime::Viewport; +use librashader::runtime::{FilterChainParameters, RuntimeParameters}; use librashader_runtime::image::{Image, UVDirection, BGRA8}; use std::path::Path; @@ -30,10 +32,15 @@ impl RenderTest for Vulkan { Vulkan::new(path) } - fn render(&mut self, path: &Path, frame_count: usize) -> anyhow::Result { + fn render_with_preset_and_params( + &mut self, + preset: ShaderPreset, + frame_count: usize, + param_setter: Option<&dyn Fn(&RuntimeParameters)>, + ) -> anyhow::Result { unsafe { - let mut filter_chain = FilterChain::load_from_path( - path, + let mut filter_chain = FilterChain::load_from_preset( + preset, &self.vk, Some(&FilterChainOptions { 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() .image_type(vk::ImageType::TYPE_2D) .format(vk::Format::B8G8R8A8_UNORM) diff --git a/librashader-cli/src/render/wgpu.rs b/librashader-cli/src/render/wgpu.rs index 446fbf4..3fee5e1 100644 --- a/librashader-cli/src/render/wgpu.rs +++ b/librashader-cli/src/render/wgpu.rs @@ -14,6 +14,8 @@ use wgpu_types::{ ImageDataLayout, Maintain, TextureDescriptor, TextureDimension, TextureFormat, TextureUsages, }; +use librashader::presets::ShaderPreset; +use librashader::runtime::{FilterChainParameters, RuntimeParameters}; use parking_lot::Mutex; pub struct Wgpu { @@ -54,9 +56,14 @@ impl RenderTest for Wgpu { Wgpu::new(path) } - fn render(&mut self, path: &Path, frame_count: usize) -> anyhow::Result { - let mut chain = FilterChain::load_from_path( - path, + fn render_with_preset_and_params( + &mut self, + preset: ShaderPreset, + frame_count: usize, + param_setter: Option<&dyn Fn(&RuntimeParameters)>, + ) -> anyhow::Result { + let mut chain = FilterChain::load_from_preset( + preset, Arc::clone(&self.device), Arc::clone(&self.queue), Some(&FilterChainOptions { @@ -65,7 +72,9 @@ impl RenderTest for Wgpu { adapter_info: None, }), )?; - + if let Some(setter) = param_setter { + setter(&chain.parameters()); + } let mut cmd = self .device .create_command_encoder(&CommandEncoderDescriptor { label: None }); diff --git a/librashader-runtime/src/parameters.rs b/librashader-runtime/src/parameters.rs index 9ba0fe4..10f449b 100644 --- a/librashader-runtime/src/parameters.rs +++ b/librashader-runtime/src/parameters.rs @@ -42,6 +42,7 @@ impl RuntimeParameters { /// Set a runtime parameter. /// /// 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 { 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)) { + 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. pub fn parameters(&self) -> Arc> { self.parameters.load_full()