diff --git a/librashader-cli/src/cli/main.rs b/librashader-cli/src/cli/main.rs index 2c8fee4..8c8e907 100644 --- a/librashader-cli/src/cli/main.rs +++ b/librashader-cli/src/cli/main.rs @@ -9,7 +9,7 @@ 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 librashader_test::render::{CommonFrameOptions, RenderTest}; use std::path::{Path, PathBuf}; /// Helpers and utilities to reflect and debug 'slang' shaders and presets. @@ -20,34 +20,76 @@ struct Args { command: Commands, } +#[derive(clap::Args, Debug)] +struct PresetArgs { + /// 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>, +} + +#[derive(clap::Args, Debug)] +struct RenderArgs { + /// The frame to render. + #[arg(short, long, default_value_t = 60)] + frame: usize, + /// 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, + #[clap(flatten)] + options: Option, +} + +impl From for CommonFrameOptions { + fn from(value: FrameOptionsArgs) -> Self { + Self { + clear_history: false, + frame_direction: value.frame_direction, + rotation: value.rotation, + total_subframes: value.total_subframes, + current_subframe: value.current_subframe, + } + } +} + +#[derive(clap::Args, Debug)] +struct FrameOptionsArgs { + /// The direction of rendering. + /// -1 indicates that the frames are played in reverse order. + #[arg(long, default_value_t = 1, allow_hyphen_values = true)] + pub frame_direction: i32, + /// The rotation of the output. 0 = 0deg, 1 = 90deg, 2 = 180deg, 3 = 270deg. + #[arg(long, default_value_t = 0)] + pub rotation: u32, + /// The total number of subframes ran. Default is 1. + #[arg(long, default_value_t = 1)] + pub total_subframes: u32, + /// The current sub frame. Default is 1. + #[arg(long, default_value_t = 1)] + pub current_subframe: u32, +} + #[derive(Subcommand, Debug)] enum Commands { /// Render a shader preset against an image Render { - /// The frame to render. - #[arg(short, long, default_value_t = 60)] - frame: usize, - /// 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, + #[clap(flatten)] + preset: PresetArgs, + #[clap(flatten)] + render: RenderArgs, /// The path to the output image /// /// If `-`, writes the image in PNG format to stdout. @@ -60,29 +102,10 @@ enum Commands { /// Compare two runtimes and get a similarity score between the two /// runtimes rendering the same frame Compare { - /// The frame to render. - #[arg(short, long, default_value_t = 60)] - frame: usize, - /// 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, + #[clap(flatten)] + preset: PresetArgs, + #[clap(flatten)] + render: RenderArgs, /// The runtime to compare against #[arg(value_enum, short, long)] left: Runtime, @@ -97,15 +120,8 @@ enum Commands { }, /// Parse a preset and get a JSON representation of the data. Parse { - /// 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>, + #[clap(flatten)] + preset: PresetArgs, }, /// Get the raw GLSL output of a preprocessed shader. Preprocess { @@ -146,15 +162,8 @@ enum Commands { }, /// Reflect the shader relative to a preset, giving information about semantics used in a slang shader. Reflect { - /// 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>, + #[clap(flatten)] + preset: PresetArgs, /// The pass index to use. #[arg(short, long)] @@ -270,16 +279,22 @@ pub fn main() -> Result<(), anyhow::Error> { match args.command { Commands::Render { - frame, preset, - wildcards, - params, - passes_enabled, - image, + render, out, runtime, } => { + let PresetArgs { preset, wildcards } = preset; + let RenderArgs { + frame, + params, + passes_enabled, + image, + options, + } = render; + let test: &mut dyn RenderTest = get_runtime!(runtime, image); + let preset = get_shader_preset(preset, wildcards)?; let params = parse_params(params)?; @@ -287,6 +302,7 @@ pub fn main() -> Result<(), anyhow::Error> { preset, frame, Some(&|rp| set_params(rp, ¶ms, passes_enabled)), + options.map(CommonFrameOptions::from), )?; if out.as_path() == Path::new("-") { @@ -297,18 +313,24 @@ pub fn main() -> Result<(), anyhow::Error> { } } Commands::Compare { - frame, preset, - wildcards, - params, - passes_enabled, - image, + render, left, right, out, } => { + let PresetArgs { preset, wildcards } = preset; + let RenderArgs { + frame, + params, + passes_enabled, + image, + options, + } = render; + 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())?; @@ -316,6 +338,7 @@ pub fn main() -> Result<(), anyhow::Error> { left_preset, frame, Some(&|rp| set_params(rp, ¶ms, passes_enabled)), + None, )?; let right_preset = get_shader_preset(preset.clone(), wildcards.clone())?; @@ -323,6 +346,7 @@ pub fn main() -> Result<(), anyhow::Error> { right_preset, frame, Some(&|rp| set_params(rp, ¶ms, passes_enabled)), + options.map(CommonFrameOptions::from), )?; let similarity = image_compare::rgba_hybrid_compare(&left_image, &right_image)?; @@ -338,7 +362,9 @@ pub fn main() -> Result<(), anyhow::Error> { } } } - Commands::Parse { preset, wildcards } => { + Commands::Parse { preset } => { + let PresetArgs { preset, wildcards } = preset; + let preset = get_shader_preset(preset, wildcards)?; let out = serde_json::to_string_pretty(&preset)?; print!("{out:}"); @@ -451,10 +477,11 @@ pub fn main() -> Result<(), anyhow::Error> { } Commands::Reflect { preset, - wildcards, index, backend, } => { + let PresetArgs { preset, wildcards } = preset; + let preset = get_shader_preset(preset, wildcards)?; let Some(shader) = preset.shaders.get(index) else { return Err(anyhow!("Invalid pass index for the preset")); diff --git a/librashader-cli/src/render/d3d11.rs b/librashader-cli/src/render/d3d11.rs index 816a01c..d7ed57e 100644 --- a/librashader-cli/src/render/d3d11.rs +++ b/librashader-cli/src/render/d3d11.rs @@ -1,4 +1,4 @@ -use crate::render::RenderTest; +use crate::render::{CommonFrameOptions, RenderTest}; use anyhow::anyhow; use image::RgbaImage; use librashader::runtime::d3d11::*; @@ -19,6 +19,7 @@ impl RenderTest for Direct3D11 { preset: ShaderPreset, frame_count: usize, param_setter: Option<&dyn Fn(&RuntimeParameters)>, + frame_options: Option, ) -> anyhow::Result { let (renderbuffer, rtv) = self.create_renderbuffer(self.image_bytes.size)?; @@ -41,7 +42,15 @@ impl RenderTest for Direct3D11 { &self.image_srv, &Viewport::new_render_target_sized_origin(rtv, None)?, frame_count, - None, + frame_options + .map(|options| FrameOptions { + clear_history: options.clear_history, + frame_direction: options.frame_direction, + rotation: options.rotation, + total_subframes: options.total_subframes, + current_subframe: options.current_subframe, + }) + .as_ref(), )?; let mut renderbuffer_desc = Default::default(); diff --git a/librashader-cli/src/render/d3d12/mod.rs b/librashader-cli/src/render/d3d12/mod.rs index 51f8377..0f58f82 100644 --- a/librashader-cli/src/render/d3d12/mod.rs +++ b/librashader-cli/src/render/d3d12/mod.rs @@ -2,13 +2,13 @@ mod descriptor_heap; mod util; use crate::render::d3d12::descriptor_heap::{CpuStagingHeap, RenderTargetHeap}; -use crate::render::RenderTest; +use crate::render::{CommonFrameOptions, 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, + D3D12InputImage, D3D12OutputView, FilterChain, FilterChainOptions, FrameOptions, }; use librashader::runtime::Viewport; use librashader::runtime::{FilterChainParameters, RuntimeParameters}; @@ -65,6 +65,7 @@ impl RenderTest for Direct3D12 { preset: ShaderPreset, frame_count: usize, param_setter: Option<&dyn Fn(&RuntimeParameters)>, + frame_options: Option, ) -> anyhow::Result { unsafe { let descriptor = self.rtv_heap.allocate_descriptor()?; @@ -145,7 +146,15 @@ impl RenderTest for Direct3D12 { None, )?, frame_count, - None, + frame_options + .map(|options| FrameOptions { + clear_history: options.clear_history, + frame_direction: options.frame_direction, + rotation: options.rotation, + total_subframes: options.total_subframes, + current_subframe: options.current_subframe, + }) + .as_ref(), )?; cmd.Close()?; diff --git a/librashader-cli/src/render/d3d9.rs b/librashader-cli/src/render/d3d9.rs index d2d62cb..0ac25a8 100644 --- a/librashader-cli/src/render/d3d9.rs +++ b/librashader-cli/src/render/d3d9.rs @@ -1,8 +1,8 @@ -use crate::render::RenderTest; +use crate::render::{CommonFrameOptions, RenderTest}; use anyhow::anyhow; use image::RgbaImage; use librashader::presets::ShaderPreset; -use librashader::runtime::d3d9::{FilterChain, FilterChainOptions}; +use librashader::runtime::d3d9::{FilterChain, FilterChainOptions, FrameOptions}; use librashader::runtime::Viewport; use librashader::runtime::{FilterChainParameters, RuntimeParameters}; use librashader_runtime::image::{Image, PixelFormat, UVDirection, BGRA8}; @@ -36,6 +36,7 @@ impl RenderTest for Direct3D9 { preset: ShaderPreset, frame_count: usize, param_setter: Option<&dyn Fn(&RuntimeParameters)>, + frame_options: Option, ) -> anyhow::Result { unsafe { let mut filter_chain = FilterChain::load_from_preset( @@ -87,7 +88,15 @@ impl RenderTest for Direct3D9 { &self.texture, &Viewport::new_render_target_sized_origin(surface.clone(), None)?, frame_count, - None, + frame_options + .map(|options| FrameOptions { + clear_history: options.clear_history, + frame_direction: options.frame_direction, + rotation: options.rotation, + total_subframes: options.total_subframes, + current_subframe: options.current_subframe, + }) + .as_ref(), )?; self.device.GetRenderTargetData(&surface, ©_texture)?; diff --git a/librashader-cli/src/render/gl/mod.rs b/librashader-cli/src/render/gl/mod.rs index 3d459fa..6606a1f 100644 --- a/librashader-cli/src/render/gl/mod.rs +++ b/librashader-cli/src/render/gl/mod.rs @@ -1,12 +1,12 @@ mod context; use crate::render::gl::context::{GLVersion, GlfwContext}; -use crate::render::RenderTest; +use crate::render::{CommonFrameOptions, 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::gl::{FilterChain, FilterChainOptions, FrameOptions, GLImage}; use librashader::runtime::Viewport; use librashader::runtime::{FilterChainParameters, RuntimeParameters}; use librashader_runtime::image::{Image, UVDirection, RGBA8}; @@ -35,6 +35,7 @@ impl RenderTest for OpenGl3 { preset: ShaderPreset, frame_count: usize, param_setter: Option<&dyn Fn(&RuntimeParameters)>, + frame_options: Option, ) -> anyhow::Result { let mut filter_chain = unsafe { FilterChain::load_from_preset( @@ -53,7 +54,19 @@ impl RenderTest for OpenGl3 { setter(filter_chain.parameters()); } - Ok(self.0.render(&mut filter_chain, frame_count)?) + Ok(self.0.render( + &mut filter_chain, + frame_count, + frame_options + .map(|options| FrameOptions { + clear_history: options.clear_history, + frame_direction: options.frame_direction, + rotation: options.rotation, + total_subframes: options.total_subframes, + current_subframe: options.current_subframe, + }) + .as_ref(), + )?) } } @@ -70,6 +83,7 @@ impl RenderTest for OpenGl4 { preset: ShaderPreset, frame_count: usize, param_setter: Option<&dyn Fn(&RuntimeParameters)>, + frame_options: Option, ) -> anyhow::Result { let mut filter_chain = unsafe { FilterChain::load_from_preset( @@ -88,7 +102,19 @@ impl RenderTest for OpenGl4 { setter(filter_chain.parameters()); } - Ok(self.0.render(&mut filter_chain, frame_count)?) + Ok(self.0.render( + &mut filter_chain, + frame_count, + frame_options + .map(|options| FrameOptions { + clear_history: options.clear_history, + frame_direction: options.frame_direction, + rotation: options.rotation, + total_subframes: options.total_subframes, + current_subframe: options.current_subframe, + }) + .as_ref(), + )?) } } @@ -163,6 +189,7 @@ impl OpenGl { &self, chain: &mut FilterChain, frame_count: usize, + options: Option<&FrameOptions>, ) -> Result { let render_texture = unsafe { let tex = self @@ -193,7 +220,7 @@ impl OpenGl { &self.texture, &Viewport::new_render_target_sized_origin(&output, None)?, frame_count, - None, + options, )?; } diff --git a/librashader-cli/src/render/mod.rs b/librashader-cli/src/render/mod.rs index de97f67..e88e181 100644 --- a/librashader-cli/src/render/mod.rs +++ b/librashader-cli/src/render/mod.rs @@ -20,6 +20,7 @@ pub mod wgpu; pub mod mtl; use librashader::presets::ShaderPreset; +use librashader_runtime::impl_default_frame_options; use librashader_runtime::parameters::RuntimeParameters; use std::path::Path; @@ -56,7 +57,7 @@ pub trait RenderTest { preset: ShaderPreset, frame_count: usize, ) -> anyhow::Result { - self.render_with_preset_and_params(preset, frame_count, None) + self.render_with_preset_and_params(preset, frame_count, None, None) } /// Render a shader onto an image buffer, applying the provided shader. @@ -72,9 +73,12 @@ pub trait RenderTest { preset: ShaderPreset, frame_count: usize, param_setter: Option<&dyn Fn(&RuntimeParameters)>, + frame_options: Option, ) -> anyhow::Result; } +impl_default_frame_options!(CommonFrameOptions); + #[cfg(test)] mod test { diff --git a/librashader-cli/src/render/mtl.rs b/librashader-cli/src/render/mtl.rs index 555edfb..2cf055b 100644 --- a/librashader-cli/src/render/mtl.rs +++ b/librashader-cli/src/render/mtl.rs @@ -1,8 +1,8 @@ -use crate::render::RenderTest; +use crate::render::{CommonFrameOptions, RenderTest}; use anyhow::anyhow; use image::RgbaImage; use librashader::presets::ShaderPreset; -use librashader::runtime::mtl::{FilterChain, FilterChainOptions}; +use librashader::runtime::mtl::{FilterChain, FilterChainOptions, FrameOptions}; use librashader::runtime::Viewport; use librashader::runtime::{FilterChainParameters, RuntimeParameters}; use librashader_runtime::image::{Image, PixelFormat, UVDirection, BGRA8, RGBA8}; @@ -36,6 +36,7 @@ impl RenderTest for Metal { preset: ShaderPreset, frame_count: usize, param_setter: Option<&dyn Fn(&RuntimeParameters)>, + frame_options: Option, ) -> anyhow::Result { let queue = self .device @@ -90,7 +91,15 @@ impl RenderTest for Metal { &Viewport::new_render_target_sized_origin(render_texture.as_ref(), None)?, cmd.as_ref(), frame_count, - None, + frame_options + .map(|options| FrameOptions { + clear_history: options.clear_history, + frame_direction: options.frame_direction, + rotation: options.rotation, + total_subframes: options.total_subframes, + current_subframe: options.current_subframe, + }) + .as_ref(), )?; cmd.commit(); diff --git a/librashader-cli/src/render/vk/mod.rs b/librashader-cli/src/render/vk/mod.rs index 8be8fd4..29939ba 100644 --- a/librashader-cli/src/render/vk/mod.rs +++ b/librashader-cli/src/render/vk/mod.rs @@ -1,12 +1,12 @@ use crate::render::vk::base::VulkanBase; use crate::render::vk::memory::{VulkanBuffer, VulkanImageMemory}; -use crate::render::RenderTest; +use crate::render::{CommonFrameOptions, RenderTest}; 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::vk::{FilterChain, FilterChainOptions, FrameOptions, VulkanImage}; use librashader::runtime::Viewport; use librashader::runtime::{FilterChainParameters, RuntimeParameters}; use librashader_runtime::image::{Image, UVDirection, BGRA8}; @@ -37,6 +37,7 @@ impl RenderTest for Vulkan { preset: ShaderPreset, frame_count: usize, param_setter: Option<&dyn Fn(&RuntimeParameters)>, + frame_options: Option, ) -> anyhow::Result { unsafe { let mut filter_chain = FilterChain::load_from_preset( @@ -157,7 +158,15 @@ impl RenderTest for Vulkan { )?, cmd, frame_count, - None, + frame_options + .map(|options| FrameOptions { + clear_history: options.clear_history, + frame_direction: options.frame_direction, + rotation: options.rotation, + total_subframes: options.total_subframes, + current_subframe: options.current_subframe, + }) + .as_ref(), )?; { diff --git a/librashader-cli/src/render/wgpu.rs b/librashader-cli/src/render/wgpu.rs index 3fee5e1..fd2e9c0 100644 --- a/librashader-cli/src/render/wgpu.rs +++ b/librashader-cli/src/render/wgpu.rs @@ -1,4 +1,4 @@ -use crate::render::RenderTest; +use crate::render::{CommonFrameOptions, RenderTest}; use anyhow::anyhow; use image::RgbaImage; use librashader::runtime::wgpu::*; @@ -61,6 +61,7 @@ impl RenderTest for Wgpu { preset: ShaderPreset, frame_count: usize, param_setter: Option<&dyn Fn(&RuntimeParameters)>, + frame_options: Option, ) -> anyhow::Result { let mut chain = FilterChain::load_from_preset( preset, @@ -112,7 +113,15 @@ impl RenderTest for Wgpu { &Viewport::new_render_target_sized_origin(output, None)?, &mut cmd, frame_count, - None, + frame_options + .map(|options| FrameOptions { + clear_history: options.clear_history, + frame_direction: options.frame_direction, + rotation: options.rotation, + total_subframes: options.total_subframes, + current_subframe: options.current_subframe, + }) + .as_ref(), )?; cmd.copy_texture_to_buffer( diff --git a/librashader-runtime/src/binding.rs b/librashader-runtime/src/binding.rs index f3f627d..96b65e3 100644 --- a/librashader-runtime/src/binding.rs +++ b/librashader-runtime/src/binding.rs @@ -466,7 +466,7 @@ macro_rules! impl_default_frame_options { pub rotation: u32, /// The total number of subframes ran. Default is 1. pub total_subframes: u32, - // The current sub frame. Default is 1. + /// The current sub frame. Default is 1. pub current_subframe: u32, }