cli(render): add ability to specify frame options

This commit is contained in:
chyyran 2024-09-26 23:35:17 -04:00 committed by Ronny Chan
parent 91f8089277
commit 84e78f4e48
10 changed files with 213 additions and 101 deletions

View file

@ -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,13 +20,8 @@ struct Args {
command: Commands,
}
#[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,
#[derive(clap::Args, Debug)]
struct PresetArgs {
/// The path to the shader preset to load.
#[arg(short, long)]
preset: PathBuf,
@ -36,18 +31,65 @@ enum Commands {
/// For example, CONTENT-DIR=MyVerticalGames,GAME=mspacman
#[arg(short, long, value_delimiter = ',', num_args = 1..)]
wildcards: Option<Vec<String>>,
}
#[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<Vec<String>>,
/// Set the number of passes enabled for the preset.
#[arg(long)]
passes_enabled: Option<usize>,
/// The path to the input image.
#[arg(short, long)]
image: PathBuf,
#[clap(flatten)]
options: Option<FrameOptionsArgs>,
}
impl From<FrameOptionsArgs> 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 {
#[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<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.
#[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<Vec<String>>,
#[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<Vec<String>>,
#[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, &params, 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, &params, 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, &params, 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"));

View file

@ -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<CommonFrameOptions>,
) -> anyhow::Result<image::RgbaImage> {
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();

View file

@ -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<CommonFrameOptions>,
) -> anyhow::Result<image::RgbaImage> {
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()?;

View file

@ -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<CommonFrameOptions>,
) -> anyhow::Result<image::RgbaImage> {
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, &copy_texture)?;

View file

@ -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<CommonFrameOptions>,
) -> anyhow::Result<image::RgbaImage> {
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<CommonFrameOptions>,
) -> anyhow::Result<image::RgbaImage> {
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<RgbaImage, anyhow::Error> {
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,
)?;
}

View file

@ -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<image::RgbaImage> {
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<CommonFrameOptions>,
) -> anyhow::Result<image::RgbaImage>;
}
impl_default_frame_options!(CommonFrameOptions);
#[cfg(test)]
mod test {

View file

@ -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<CommonFrameOptions>,
) -> anyhow::Result<image::RgbaImage> {
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();

View file

@ -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<CommonFrameOptions>,
) -> anyhow::Result<image::RgbaImage> {
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(),
)?;
{

View file

@ -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<CommonFrameOptions>,
) -> anyhow::Result<image::RgbaImage> {
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(

View file

@ -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,
}