rt: redesign parameters to be thread-safe across FFI using arcswap and atomicusize

This should allow C FFI consumers to modify frame parameters from a different thread without it being UB.
This commit is contained in:
chyyran 2024-08-27 01:18:17 -04:00 committed by Ronny Chan
parent ae76bf9cc1
commit c447e40583
26 changed files with 216 additions and 310 deletions

7
Cargo.lock generated
View file

@ -123,6 +123,12 @@ version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7d5a26814d8dcb93b0e5a0ff3c6d80a8843bafb21b39e8e18a6f05471870e110"
[[package]]
name = "arc-swap"
version = "1.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "69f7f8c3906b62b754cd5326047894316021dcfe5a194c8ea52bdd94934a3457"
[[package]]
name = "arg_enum_proc_macro"
version = "0.3.4"
@ -1749,6 +1755,7 @@ dependencies = [
name = "librashader-runtime"
version = "0.3.3"
dependencies = [
"arc-swap",
"array-concat",
"bytemuck",
"image",

View file

@ -260,14 +260,14 @@ extern_fn! {
chain: *mut libra_d3d11_filter_chain_t,
param_name: *const c_char,
value: f32
) mut |chain| {
assert_some_ptr!(mut chain);
) |chain| {
assert_some_ptr!(chain);
assert_non_null!(param_name);
unsafe {
let name = CStr::from_ptr(param_name);
let name = name.to_str()?;
if chain.set_parameter(name, value).is_none() {
if chain.parameters().set_parameter(name, value).is_none() {
return LibrashaderError::UnknownShaderParameter(param_name).export()
}
}
@ -282,17 +282,17 @@ extern_fn! {
/// - `chain` must be either null or a valid and aligned pointer to an initialized `libra_d3d11_filter_chain_t`.
/// - `param_name` must be either null or a null terminated string.
fn libra_d3d11_filter_chain_get_param(
chain: *mut libra_d3d11_filter_chain_t,
chain: *const libra_d3d11_filter_chain_t,
param_name: *const c_char,
out: *mut MaybeUninit<f32>
) mut |chain| {
assert_some_ptr!(mut chain);
) |chain| {
assert_some_ptr!(chain);
assert_non_null!(param_name);
unsafe {
let name = CStr::from_ptr(param_name);
let name = name.to_str()?;
let Some(value) = chain.get_parameter(name) else {
let Some(value) = chain.parameters().get_parameter(name) else {
return LibrashaderError::UnknownShaderParameter(param_name).export()
};
@ -309,9 +309,9 @@ extern_fn! {
fn libra_d3d11_filter_chain_set_active_pass_count(
chain: *mut libra_d3d11_filter_chain_t,
value: u32
) mut |chain| {
assert_some_ptr!(mut chain);
chain.set_enabled_pass_count(value as usize);
) |chain| {
assert_some_ptr!(chain);
chain.parameters().set_passes_enabled(value as usize);
}
}
@ -321,12 +321,12 @@ extern_fn! {
/// ## Safety
/// - `chain` must be either null or a valid and aligned pointer to an initialized `libra_d3d11_filter_chain_t`.
fn libra_d3d11_filter_chain_get_active_pass_count(
chain: *mut libra_d3d11_filter_chain_t,
chain: *const libra_d3d11_filter_chain_t,
out: *mut MaybeUninit<u32>
) mut |chain| {
assert_some_ptr!(mut chain);
) |chain| {
assert_some_ptr!(chain);
unsafe {
let value = chain.get_enabled_pass_count();
let value = chain.parameters().passes_enabled();
out.write(MaybeUninit::new(value as u32))
}
}

View file

@ -280,14 +280,14 @@ extern_fn! {
chain: *mut libra_d3d12_filter_chain_t,
param_name: *const c_char,
value: f32
) mut |chain| {
assert_some_ptr!(mut chain);
) |chain| {
assert_some_ptr!(chain);
assert_non_null!(param_name);
unsafe {
let name = CStr::from_ptr(param_name);
let name = name.to_str()?;
if chain.set_parameter(name, value).is_none() {
if chain.parameters().set_parameter(name, value).is_none() {
return LibrashaderError::UnknownShaderParameter(param_name).export()
}
}
@ -302,17 +302,17 @@ extern_fn! {
/// - `chain` must be either null or a valid and aligned pointer to an initialized `libra_d3d12_filter_chain_t`.
/// - `param_name` must be either null or a null terminated string.
fn libra_d3d12_filter_chain_get_param(
chain: *mut libra_d3d12_filter_chain_t,
chain: *const libra_d3d12_filter_chain_t,
param_name: *const c_char,
out: *mut MaybeUninit<f32>
) mut |chain| {
assert_some_ptr!(mut chain);
) |chain| {
assert_some_ptr!(chain);
assert_non_null!(param_name);
unsafe {
let name = CStr::from_ptr(param_name);
let name = name.to_str()?;
let Some(value) = chain.get_parameter(name) else {
let Some(value) = chain.parameters().get_parameter(name) else {
return LibrashaderError::UnknownShaderParameter(param_name).export()
};
@ -329,9 +329,9 @@ extern_fn! {
fn libra_d3d12_filter_chain_set_active_pass_count(
chain: *mut libra_d3d12_filter_chain_t,
value: u32
) mut |chain| {
assert_some_ptr!(mut chain);
chain.set_enabled_pass_count(value as usize);
) |chain| {
assert_some_ptr!(chain);
chain.parameters().set_passes_enabled(value as usize);
}
}
@ -341,12 +341,12 @@ extern_fn! {
/// ## Safety
/// - `chain` must be either null or a valid and aligned pointer to an initialized `libra_d3d12_filter_chain_t`.
fn libra_d3d12_filter_chain_get_active_pass_count(
chain: *mut libra_d3d12_filter_chain_t,
chain: *const libra_d3d12_filter_chain_t,
out: *mut MaybeUninit<u32>
) mut |chain| {
assert_some_ptr!(mut chain);
) |chain| {
assert_some_ptr!(chain);
unsafe {
let value = chain.get_enabled_pass_count();
let value = chain.parameters().passes_enabled();
out.write(MaybeUninit::new(value as u32))
}
}

View file

@ -170,14 +170,14 @@ extern_fn! {
chain: *mut libra_d3d9_filter_chain_t,
param_name: *const c_char,
value: f32
) mut |chain| {
assert_some_ptr!(mut chain);
) |chain| {
assert_some_ptr!(chain);
assert_non_null!(param_name);
unsafe {
let name = CStr::from_ptr(param_name);
let name = name.to_str()?;
if chain.set_parameter(name, value).is_none() {
if chain.parameters().set_parameter(name, value).is_none() {
return LibrashaderError::UnknownShaderParameter(param_name).export()
}
}
@ -192,17 +192,17 @@ extern_fn! {
/// - `chain` must be either null or a valid and aligned pointer to an initialized `libra_d3d9_filter_chain_t`.
/// - `param_name` must be either null or a null terminated string.
fn libra_d3d9_filter_chain_get_param(
chain: *mut libra_d3d9_filter_chain_t,
chain: *const libra_d3d9_filter_chain_t,
param_name: *const c_char,
out: *mut MaybeUninit<f32>
) mut |chain| {
assert_some_ptr!(mut chain);
) |chain| {
assert_some_ptr!(chain);
assert_non_null!(param_name);
unsafe {
let name = CStr::from_ptr(param_name);
let name = name.to_str()?;
let Some(value) = chain.get_parameter(name) else {
let Some(value) = chain.parameters().get_parameter(name) else {
return LibrashaderError::UnknownShaderParameter(param_name).export()
};
@ -219,9 +219,9 @@ extern_fn! {
fn libra_d3d9_filter_chain_set_active_pass_count(
chain: *mut libra_d3d9_filter_chain_t,
value: u32
) mut |chain| {
assert_some_ptr!(mut chain);
chain.set_enabled_pass_count(value as usize);
) |chain| {
assert_some_ptr!(chain);
chain.parameters().set_passes_enabled(value as usize);
}
}
@ -231,12 +231,12 @@ extern_fn! {
/// ## Safety
/// - `chain` must be either null or a valid and aligned pointer to an initialized `libra_d3d9_filter_chain_t`.
fn libra_d3d9_filter_chain_get_active_pass_count(
chain: *mut libra_d3d9_filter_chain_t,
chain: *const libra_d3d9_filter_chain_t,
out: *mut MaybeUninit<u32>
) mut |chain| {
assert_some_ptr!(mut chain);
) |chain| {
assert_some_ptr!(chain);
unsafe {
let value = chain.get_enabled_pass_count();
let value = chain.parameters().passes_enabled();
out.write(MaybeUninit::new(value as u32))
}
}

View file

@ -227,14 +227,14 @@ extern_fn! {
chain: *mut libra_gl_filter_chain_t,
param_name: *const c_char,
value: f32
) mut |chain| {
assert_some_ptr!(mut chain);
) |chain| {
assert_some_ptr!(chain);
assert_non_null!(param_name);
unsafe {
let name = CStr::from_ptr(param_name);
let name = name.to_str()?;
if chain.set_parameter(name, value).is_none() {
if chain.parameters().set_parameter(name, value).is_none() {
return LibrashaderError::UnknownShaderParameter(param_name).export()
}
}
@ -249,17 +249,17 @@ extern_fn! {
/// - `chain` must be either null or a valid and aligned pointer to an initialized `libra_gl_filter_chain_t`.
/// - `param_name` must be either null or a null terminated string.
fn libra_gl_filter_chain_get_param(
chain: *mut libra_gl_filter_chain_t,
chain: *const libra_gl_filter_chain_t,
param_name: *const c_char,
out: *mut MaybeUninit<f32>
) mut |chain| {
assert_some_ptr!(mut chain);
) |chain| {
assert_some_ptr!(chain);
assert_non_null!(param_name);
unsafe {
let name = CStr::from_ptr(param_name);
let name = name.to_str()?;
let Some(value) = chain.get_parameter(name) else {
let Some(value) = chain.parameters().get_parameter(name) else {
return LibrashaderError::UnknownShaderParameter(param_name).export()
};
@ -276,9 +276,9 @@ extern_fn! {
fn libra_gl_filter_chain_set_active_pass_count(
chain: *mut libra_gl_filter_chain_t,
value: u32
) mut |chain| {
assert_some_ptr!(mut chain);
chain.set_enabled_pass_count(value as usize);
) |chain| {
assert_some_ptr!(chain);
chain.parameters().set_passes_enabled(value as usize);
}
}
@ -292,7 +292,7 @@ extern_fn! {
out: *mut MaybeUninit<u32>
) mut |chain| {
assert_some_ptr!(mut chain);
let value = chain.get_enabled_pass_count();
let value = chain.parameters().passes_enabled();
unsafe {
out.write(MaybeUninit::new(value as u32))
}

View file

@ -229,13 +229,13 @@ extern_fn! {
chain: *mut libra_mtl_filter_chain_t,
param_name: *const c_char,
value: f32
) mut |chain| {
assert_some_ptr!(mut chain);
) |chain| {
assert_some_ptr!(chain);
unsafe {
let name = CStr::from_ptr(param_name);
let name = name.to_str()?;
if chain.set_parameter(name, value).is_none() {
if chain.parameters().set_parameter(name, value).is_none() {
return LibrashaderError::UnknownShaderParameter(param_name).export()
}
}
@ -250,17 +250,17 @@ extern_fn! {
/// - `chain` must be either null or a valid and aligned pointer to an initialized `libra_mtl_filter_chain_t`.
/// - `param_name` must be either null or a null terminated string.
fn libra_mtl_filter_chain_get_param(
chain: *mut libra_mtl_filter_chain_t,
chain: *const libra_mtl_filter_chain_t,
param_name: *const c_char,
out: *mut MaybeUninit<f32>
) mut |chain| {
assert_some_ptr!(mut chain);
) |chain| {
assert_some_ptr!(chain);
unsafe {
let name = CStr::from_ptr(param_name);
let name = name.to_str()?;
let Some(value) = chain.get_parameter(name) else {
let Some(value) = chain.parameters().get_parameter(name) else {
return LibrashaderError::UnknownShaderParameter(param_name).export()
};
@ -277,9 +277,9 @@ extern_fn! {
fn libra_mtl_filter_chain_set_active_pass_count(
chain: *mut libra_mtl_filter_chain_t,
value: u32
) mut |chain| {
assert_some_ptr!(mut chain);
chain.set_enabled_pass_count(value as usize);
) |chain| {
assert_some_ptr!(chain);
chain.parameters().set_passes_enabled(value as usize);
}
}
@ -289,11 +289,11 @@ extern_fn! {
/// ## Safety
/// - `chain` must be either null or a valid and aligned pointer to an initialized `libra_mtl_filter_chain_t`.
fn libra_mtl_filter_chain_get_active_pass_count(
chain: *mut libra_mtl_filter_chain_t,
chain: *const libra_mtl_filter_chain_t,
out: *mut MaybeUninit<u32>
) mut |chain| {
assert_some_ptr!(mut chain);
let value = chain.get_enabled_pass_count();
) |chain| {
assert_some_ptr!(chain);
let value = chain.parameters().passes_enabled();
unsafe {
out.write(MaybeUninit::new(value as u32))
}

View file

@ -306,14 +306,14 @@ extern_fn! {
chain: *mut libra_vk_filter_chain_t,
param_name: *const c_char,
value: f32
) mut |chain| {
assert_some_ptr!(mut chain);
) |chain| {
assert_some_ptr!(chain);
assert_non_null!(param_name);
unsafe {
let name = CStr::from_ptr(param_name);
let name = name.to_str()?;
if chain.set_parameter(name, value).is_none() {
if chain.parameters().set_parameter(name, value).is_none() {
return LibrashaderError::UnknownShaderParameter(param_name).export()
}
}
@ -328,17 +328,17 @@ extern_fn! {
/// - `chain` must be either null or a valid and aligned pointer to an initialized `libra_vk_filter_chain_t`.
/// - `param_name` must be either null or a null terminated string.
fn libra_vk_filter_chain_get_param(
chain: *mut libra_vk_filter_chain_t,
chain: *const libra_vk_filter_chain_t,
param_name: *const c_char,
out: *mut MaybeUninit<f32>
) mut |chain| {
assert_some_ptr!(mut chain);
) |chain| {
assert_some_ptr!(chain);
assert_non_null!(param_name);
unsafe {
let name = CStr::from_ptr(param_name);
let name = name.to_str()?;
let Some(value) = chain.get_parameter(name) else {
let Some(value) = chain.parameters().get_parameter(name) else {
return LibrashaderError::UnknownShaderParameter(param_name).export()
};
@ -355,9 +355,9 @@ extern_fn! {
fn libra_vk_filter_chain_set_active_pass_count(
chain: *mut libra_vk_filter_chain_t,
value: u32
) mut |chain| {
assert_some_ptr!(mut chain);
chain.set_enabled_pass_count(value as usize);
) |chain| {
assert_some_ptr!(chain);
chain.parameters().set_passes_enabled(value as usize);
}
}
@ -367,11 +367,11 @@ extern_fn! {
/// ## Safety
/// - `chain` must be either null or a valid and aligned pointer to an initialized `libra_vk_filter_chain_t`.
fn libra_vk_filter_chain_get_active_pass_count(
chain: *mut libra_vk_filter_chain_t,
chain: *const libra_vk_filter_chain_t,
out: *mut MaybeUninit<u32>
) mut |chain| {
assert_some_ptr!(mut chain);
let value = chain.get_enabled_pass_count();
) |chain| {
assert_some_ptr!(chain);
let value = chain.parameters().passes_enabled();
unsafe {
out.write(MaybeUninit::new(value as u32))
}

View file

@ -43,11 +43,6 @@ use windows::Win32::Graphics::Direct3D11::{
};
use windows::Win32::Graphics::Dxgi::Common::DXGI_FORMAT_R8G8B8A8_UNORM;
pub struct FilterMutable {
pub(crate) passes_enabled: usize,
pub(crate) parameters: FastHashMap<String, f32>,
}
/// A Direct3D 11 filter chain.
pub struct FilterChainD3D11 {
pub(crate) common: FilterCommon,
@ -71,7 +66,7 @@ pub(crate) struct FilterCommon {
pub output_textures: Box<[Option<InputTexture>]>,
pub feedback_textures: Box<[Option<InputTexture>]>,
pub history_textures: Box<[Option<InputTexture>]>,
pub config: FilterMutable,
pub config: RuntimeParameters,
pub disable_mipmaps: bool,
pub(crate) draw_quad: DrawQuad,
}
@ -103,6 +98,7 @@ mod compile {
}
use compile::{compile_passes, ShaderPassMeta};
use librashader_runtime::parameters::RuntimeParameters;
impl FilterChainD3D11 {
/// Load the shader preset at the given path into a filter chain.
@ -193,14 +189,7 @@ impl FilterChainD3D11 {
_device: device.clone(),
immediate_context,
},
config: FilterMutable {
passes_enabled: preset.shader_count as usize,
parameters: preset
.parameters
.into_iter()
.map(|param| (param.name, param.value))
.collect(),
},
config: RuntimeParameters::new(preset.shader_count as usize, preset.parameters),
disable_mipmaps: options.map_or(false, |o| o.force_no_mipmaps),
luts,
samplers,
@ -405,7 +394,7 @@ impl FilterChainD3D11 {
frame_count: usize,
options: Option<&FrameOptionsD3D11>,
) -> error::Result<()> {
let max = std::cmp::min(self.passes.len(), self.common.config.passes_enabled);
let max = std::cmp::min(self.passes.len(), self.common.config.passes_enabled());
// Need to clone this because pushing history needs a mutable borrow.
let immediate_context = &self.common.d3d11.immediate_context.clone();

View file

@ -139,7 +139,7 @@ impl FilterPass {
parent.history_textures.iter().map(|o| o.as_ref()),
parent.luts.iter().map(|(u, i)| (*u, i.as_ref())),
&self.source.parameters,
&parent.config.parameters,
&parent.config,
);
}

View file

@ -60,11 +60,6 @@ use rayon::prelude::*;
const MIPMAP_RESERVED_WORKHEAP_DESCRIPTORS: usize = 4096;
pub struct FilterMutable {
pub(crate) passes_enabled: usize,
pub(crate) parameters: FastHashMap<String, f32>,
}
/// A Direct3D 12 filter chain.
pub struct FilterChainD3D12 {
pub(crate) common: FilterCommon,
@ -92,7 +87,7 @@ pub(crate) struct FilterCommon {
pub output_textures: Box<[Option<InputTexture>]>,
pub feedback_textures: Box<[Option<InputTexture>]>,
pub history_textures: Box<[Option<InputTexture>]>,
pub config: FilterMutable,
pub config: RuntimeParameters,
// pub disable_mipmaps: bool,
pub luts: FastHashMap<usize, LutTexture>,
pub mipmap_gen: D3D12MipmapGen,
@ -222,6 +217,7 @@ mod compile {
}
use compile::{compile_passes_dxil, compile_passes_hlsl, DxilShaderPassMeta, HlslShaderPassMeta};
use librashader_runtime::parameters::RuntimeParameters;
impl FilterChainD3D12 {
/// Load the shader preset at the given path into a filter chain.
@ -387,14 +383,7 @@ impl FilterChainD3D12 {
mipmap_gen,
root_signature,
draw_quad,
config: FilterMutable {
passes_enabled: preset.shader_count as usize,
parameters: preset
.parameters
.into_iter()
.map(|param| (param.name, param.value))
.collect(),
},
config: RuntimeParameters::new(preset.shader_count as usize, preset.parameters),
history_textures,
},
staging_heap,
@ -669,6 +658,13 @@ impl FilterChainD3D12 {
) -> error::Result<()> {
self.residuals.dispose();
// limit number of passes to those enabled.
let max = std::cmp::min(self.passes.len(), self.common.config.passes_enabled());
let passes = &mut self.passes[0..max];
if passes.is_empty() {
return Ok(());
}
if let Some(options) = options {
if options.clear_history {
for framebuffer in &mut self.history_framebuffers {
@ -677,22 +673,8 @@ impl FilterChainD3D12 {
}
}
// limit number of passes to those enabled.
let max = std::cmp::min(self.passes.len(), self.common.config.passes_enabled);
let passes = &mut self.passes[0..max];
if passes.is_empty() {
return Ok(());
}
let options = options.unwrap_or(&self.default_options);
let max = std::cmp::min(self.passes.len(), self.common.config.passes_enabled);
let passes = &mut self.passes[0..max];
if passes.is_empty() {
return Ok(());
}
let filter = passes[0].config.filter;
let wrap_mode = passes[0].config.wrap_mode;

View file

@ -122,7 +122,7 @@ impl FilterPass {
parent.history_textures.iter().map(|o| o.as_ref()),
parent.luts.iter().map(|(u, i)| (*u, i.as_ref())),
&self.source.parameters,
&parent.config.parameters,
&parent.config,
);
}

View file

@ -36,11 +36,6 @@ use crate::util::GetSize;
use windows::Win32::Graphics::Direct3D9::{IDirect3DDevice9, IDirect3DSurface9, IDirect3DTexture9};
pub struct FilterMutable {
pub(crate) passes_enabled: usize,
pub(crate) parameters: FastHashMap<String, f32>,
}
pub(crate) struct FilterCommon {
pub(crate) d3d9: IDirect3DDevice9,
pub(crate) luts: FastHashMap<usize, LutTexture>,
@ -48,7 +43,7 @@ pub(crate) struct FilterCommon {
pub output_textures: Box<[Option<D3D9InputTexture>]>,
pub feedback_textures: Box<[Option<D3D9InputTexture>]>,
pub history_textures: Box<[Option<D3D9InputTexture>]>,
pub config: FilterMutable,
pub config: RuntimeParameters,
pub disable_mipmaps: bool,
pub(crate) draw_quad: DrawQuad,
}
@ -90,6 +85,7 @@ mod compile {
}
use compile::{compile_passes, ShaderPassMeta};
use librashader_runtime::parameters::RuntimeParameters;
impl FilterChainD3D9 {
fn init_passes(
@ -255,14 +251,7 @@ impl FilterChainD3D9 {
history_framebuffers,
common: FilterCommon {
d3d9: device.clone(),
config: FilterMutable {
passes_enabled: preset.shader_count as usize,
parameters: preset
.parameters
.into_iter()
.map(|param| (param.name, param.value))
.collect(),
},
config: RuntimeParameters::new(preset.shader_count as usize, preset.parameters),
disable_mipmaps: options.map_or(false, |o| o.force_no_mipmaps),
luts,
samplers,
@ -295,7 +284,8 @@ impl FilterChainD3D9 {
frame_count: usize,
options: Option<&FrameOptionsD3D9>,
) -> error::Result<()> {
let max = std::cmp::min(self.passes.len(), self.common.config.passes_enabled);
let max = std::cmp::min(self.passes.len(), self.common.config.passes_enabled());
let passes = &mut self.passes[0..max];
if let Some(options) = options {
if options.clear_history {

View file

@ -133,7 +133,7 @@ impl FilterPass {
parent.history_textures.iter().map(|o| o.as_ref()),
parent.luts.iter().map(|(u, i)| (*u, i.as_ref())),
&self.source.parameters,
&parent.config.parameters,
&parent.config,
);
}

View file

@ -43,7 +43,7 @@ pub(crate) struct FilterChainImpl<T: GLInterface> {
pub(crate) struct FilterCommon {
// semantics: ReflectSemantics,
pub config: FilterMutable,
pub config: RuntimeParameters,
pub luts: FastHashMap<usize, InputTexture>,
pub samplers: SamplerSet,
pub output_textures: Box<[InputTexture]>,
@ -52,11 +52,6 @@ pub(crate) struct FilterCommon {
pub disable_mipmaps: bool,
}
pub struct FilterMutable {
pub(crate) passes_enabled: usize,
pub(crate) parameters: FastHashMap<String, f32>,
}
impl<T: GLInterface> FilterChainImpl<T> {
fn reflect_uniform_location(pipeline: GLuint, meta: &dyn UniformMeta) -> VariableLocation {
let mut location = VariableLocation {
@ -119,6 +114,7 @@ mod compile {
}
use compile::{compile_passes, ShaderPassMeta};
use librashader_runtime::parameters::RuntimeParameters;
impl<T: GLInterface> FilterChainImpl<T> {
/// Load a filter chain from a pre-parsed `ShaderPreset`.
@ -178,14 +174,7 @@ impl<T: GLInterface> FilterChainImpl<T> {
history_framebuffers,
draw_quad,
common: FilterCommon {
config: FilterMutable {
passes_enabled: preset.shader_count as usize,
parameters: preset
.parameters
.into_iter()
.map(|param| (param.name, param.value))
.collect(),
},
config: RuntimeParameters::new(preset.shader_count as usize, preset.parameters),
disable_mipmaps: options.map_or(false, |o| o.force_no_mipmaps),
luts,
samplers,
@ -274,7 +263,7 @@ impl<T: GLInterface> FilterChainImpl<T> {
options: Option<&FrameOptionsGL>,
) -> error::Result<()> {
// limit number of passes to those enabled.
let max = std::cmp::min(self.passes.len(), self.common.config.passes_enabled);
let max = std::cmp::min(self.passes.len(), self.common.config.passes_enabled());
let passes = &mut self.passes[0..max];
if let Some(options) = options {

View file

@ -2,7 +2,7 @@ use crate::filter_chain::filter_impl::FilterChainImpl;
use crate::filter_chain::inner::FilterChainDispatch;
use crate::gl::GLInterface;
use crate::FilterChainGL;
use librashader_runtime::parameters::FilterChainParameters;
use librashader_runtime::parameters::{FilterChainParameters, RuntimeParameters};
impl AsRef<dyn FilterChainParameters + 'static> for FilterChainDispatch {
fn as_ref<'a>(&'a self) -> &'a (dyn FilterChainParameters + 'static) {
@ -23,60 +23,13 @@ impl AsMut<dyn FilterChainParameters + 'static> for FilterChainDispatch {
}
impl FilterChainParameters for FilterChainGL {
fn get_enabled_pass_count(&self) -> usize {
self.filter.as_ref().get_enabled_pass_count()
}
fn set_enabled_pass_count(&mut self, count: usize) {
self.filter.as_mut().set_enabled_pass_count(count)
}
fn enumerate_parameters(&self) -> ::librashader_common::map::halfbrown::Iter<String, f32> {
self.filter.as_ref().enumerate_parameters()
}
fn get_parameter(&self, parameter: &str) -> Option<f32> {
self.filter.as_ref().get_parameter(parameter)
}
fn set_parameter(&mut self, parameter: &str, new_value: f32) -> Option<f32> {
self.filter.as_mut().set_parameter(parameter, new_value)
fn parameters(&self) -> &RuntimeParameters {
self.filter.as_ref().parameters()
}
}
impl<T: GLInterface> FilterChainParameters for FilterChainImpl<T> {
fn get_enabled_pass_count(&self) -> usize {
self.common.config.passes_enabled
}
fn set_enabled_pass_count(&mut self, count: usize) {
self.common.config.passes_enabled = count
}
fn enumerate_parameters(&self) -> ::librashader_common::map::halfbrown::Iter<String, f32> {
self.common.config.parameters.iter()
}
fn get_parameter(&self, parameter: &str) -> Option<f32> {
self.common
.config
.parameters
.get::<str>(parameter.as_ref())
.copied()
}
fn set_parameter(&mut self, parameter: &str, new_value: f32) -> Option<f32> {
if let Some(value) = self
.common
.config
.parameters
.get_mut::<str>(parameter.as_ref())
{
let old = *value;
*value = new_value;
Some(old)
} else {
None
}
fn parameters(&self) -> &RuntimeParameters {
&self.common.config
}
}

View file

@ -196,7 +196,7 @@ impl<T: GLInterface> FilterPass<T> {
parent.history_textures.iter().map(|o| o.bound()),
parent.luts.iter().map(|(u, i)| (*u, i)),
&self.source.parameters,
&parent.config.parameters,
&parent.config,
);
}
}

View file

@ -57,6 +57,7 @@ mod compile {
}
use compile::{compile_passes, ShaderPassMeta};
use librashader_runtime::parameters::RuntimeParameters;
/// A Metal filter chain.
pub struct FilterChainMetal {
@ -75,18 +76,13 @@ impl Debug for FilterChainMetal {
}
}
pub struct FilterMutable {
pub passes_enabled: usize,
pub(crate) parameters: FastHashMap<String, f32>,
}
pub(crate) struct FilterCommon {
pub output_textures: Box<[Option<InputTexture>]>,
pub feedback_textures: Box<[Option<InputTexture>]>,
pub history_textures: Box<[Option<InputTexture>]>,
pub luts: FastHashMap<usize, LutTexture>,
pub samplers: SamplerSet,
pub config: FilterMutable,
pub config: RuntimeParameters,
pub internal_frame_count: i32,
pub(crate) draw_quad: DrawQuad,
device: Id<ProtocolObject<dyn MTLDevice>>,
@ -301,14 +297,7 @@ impl FilterChainMetal {
common: FilterCommon {
luts,
samplers,
config: FilterMutable {
passes_enabled: preset.shader_count as usize,
parameters: preset
.parameters
.into_iter()
.map(|param| (param.name, param.value))
.collect(),
},
config: RuntimeParameters::new(preset.shader_count as usize, preset.parameters),
draw_quad,
device,
output_textures,
@ -336,7 +325,7 @@ impl FilterChainMetal {
frame_count: usize,
options: Option<&FrameOptionsMetal>,
) -> error::Result<()> {
let max = std::cmp::min(self.passes.len(), self.common.config.passes_enabled);
let max = std::cmp::min(self.passes.len(), self.common.config.passes_enabled());
let passes = &mut self.passes[0..max];
if let Some(options) = &options {
let desc = unsafe {

View file

@ -163,7 +163,7 @@ impl FilterPass {
parent.history_textures.iter().map(|o| o.as_ref()),
parent.luts.iter().map(|(u, i)| (*u, i.as_ref())),
&self.source.parameters,
&parent.config.parameters,
&parent.config,
);
// flush to buffers

View file

@ -128,11 +128,6 @@ pub struct FilterChainVulkan {
default_options: FrameOptionsVulkan,
}
pub struct FilterMutable {
pub(crate) passes_enabled: usize,
pub(crate) parameters: FastHashMap<String, f32>,
}
pub(crate) struct FilterCommon {
pub(crate) luts: FastHashMap<usize, LutTexture>,
pub samplers: SamplerSet,
@ -140,7 +135,7 @@ pub(crate) struct FilterCommon {
pub output_textures: Box<[Option<InputImage>]>,
pub feedback_textures: Box<[Option<InputImage>]>,
pub history_textures: Box<[Option<InputImage>]>,
pub config: FilterMutable,
pub config: RuntimeParameters,
pub device: Arc<ash::Device>,
pub(crate) internal_frame_count: usize,
}
@ -236,6 +231,7 @@ mod compile {
}
use compile::{compile_passes, ShaderPassMeta};
use librashader_runtime::parameters::RuntimeParameters;
impl FilterChainVulkan {
/// Load the shader preset at the given path into a filter chain.
@ -386,14 +382,7 @@ impl FilterChainVulkan {
common: FilterCommon {
luts,
samplers,
config: FilterMutable {
passes_enabled: preset.shader_count as usize,
parameters: preset
.parameters
.into_iter()
.map(|param| (param.name, param.value))
.collect(),
},
config: RuntimeParameters::new(preset.shader_count as usize, preset.parameters),
draw_quad: DrawQuad::new(&device.device, &device.alloc)?,
device: device.device.clone(),
output_textures,
@ -576,7 +565,7 @@ impl FilterChainVulkan {
intermediates.dispose();
// limit number of passes to those enabled.
let max = std::cmp::min(self.passes.len(), self.common.config.passes_enabled);
let max = std::cmp::min(self.passes.len(), self.common.config.passes_enabled());
let passes = &mut self.passes[0..max];
if let Some(options) = &options {

View file

@ -219,7 +219,7 @@ impl FilterPass {
parent.history_textures.iter().map(|o| o.as_ref()),
parent.luts.iter().map(|(u, i)| (*u, i.as_ref())),
&self.source.parameters,
&parent.config.parameters,
&parent.config,
);
}
}

View file

@ -56,6 +56,7 @@ mod compile {
}
use compile::{compile_passes, ShaderPassMeta};
use librashader_runtime::parameters::RuntimeParameters;
/// A wgpu filter chain.
pub struct FilterChainWgpu {
@ -69,19 +70,13 @@ pub struct FilterChainWgpu {
default_frame_options: FrameOptionsWgpu,
}
pub struct FilterMutable {
pub passes_enabled: usize,
pub(crate) parameters: FastHashMap<String, f32>,
}
pub(crate) struct FilterCommon {
pub output_textures: Box<[Option<InputImage>]>,
pub feedback_textures: Box<[Option<InputImage>]>,
pub history_textures: Box<[Option<InputImage>]>,
pub luts: FastHashMap<usize, LutTexture>,
pub samplers: SamplerSet,
pub config: FilterMutable,
pub internal_frame_count: i32,
pub config: RuntimeParameters,
pub(crate) draw_quad: DrawQuad,
device: Arc<Device>,
pub(crate) queue: Arc<wgpu::Queue>,
@ -199,21 +194,13 @@ impl FilterChainWgpu {
common: FilterCommon {
luts,
samplers,
config: FilterMutable {
passes_enabled: preset.shader_count as usize,
parameters: preset
.parameters
.into_iter()
.map(|param| (param.name, param.value))
.collect(),
},
config: RuntimeParameters::new(preset.shader_count as usize, preset.parameters),
draw_quad,
device,
queue,
output_textures,
feedback_textures,
history_textures,
internal_frame_count: 0,
},
passes: filters,
output_framebuffers,
@ -377,7 +364,7 @@ impl FilterChainWgpu {
frame_count: usize,
options: Option<&FrameOptionsWgpu>,
) -> error::Result<()> {
let max = std::cmp::min(self.passes.len(), self.common.config.passes_enabled);
let max = std::cmp::min(self.passes.len(), self.common.config.passes_enabled());
let passes = &mut self.passes[0..max];
if let Some(options) = &options {
@ -513,7 +500,6 @@ impl FilterChainWgpu {
}
self.push_history(&input, cmd);
self.common.internal_frame_count = self.common.internal_frame_count.wrapping_add(1);
Ok(())
}
}

View file

@ -238,7 +238,7 @@ impl FilterPass {
parent.history_textures.iter().map(|o| o.as_ref()),
parent.luts.iter().map(|(u, i)| (*u, i.as_ref())),
&self.source.parameters,
&parent.config.parameters,
&parent.config,
);
// flush to buffers

View file

@ -19,6 +19,7 @@ librashader-reflect = { path = "../librashader-reflect", version = "0.3.3" }
bytemuck = { version = "1.12.3", features = ["derive"] }
num-traits = "0.2.15"
array-concat = "0.5.2"
arc-swap = "1.7.1"
tinymap = "0.4.0"

View file

@ -1,3 +1,4 @@
use crate::parameters::RuntimeParameters;
use crate::uniforms::{BindUniform, NoUniformBinder, UniformStorage};
use librashader_common::map::FastHashMap;
use librashader_common::Size;
@ -120,8 +121,9 @@ where
original_history: impl Iterator<Item = Option<impl AsRef<Self::InputTexture>>>,
lookup_textures: impl Iterator<Item = (usize, impl AsRef<Self::InputTexture>)>,
parameter_defaults: &FastHashMap<String, ShaderParameter>,
runtime_parameters: &FastHashMap<String, f32>,
runtime_parameters: &RuntimeParameters,
) {
let runtime_parameters = runtime_parameters.parameters.load();
// Bind MVP
if let Some(offset) = uniform_bindings.get(&UniqueSemantics::MVP.into()) {
uniform_storage.bind_mat4(

View file

@ -1,64 +1,92 @@
use std::ops::Deref;
use arc_swap::ArcSwap;
use librashader_common::map::FastHashMap;
use librashader_presets::ParameterConfig;
use std::sync::atomic::{AtomicUsize, Ordering};
use std::sync::Arc;
/// Trait for filter chains that allow runtime reflection of shader parameters.
pub trait FilterChainParameters {
/// Gets the number of shader passes enabled at runtime.
fn get_enabled_pass_count(&self) -> usize;
/// Get the runtime parameters for this filter chain.
fn parameters(&self) -> &RuntimeParameters;
}
/// Sets the number of shader passes enabled at runtime.
fn set_enabled_pass_count(&mut self, count: usize);
/// Runtime reflection of shader parameters for filter chains.
///
/// All operations on runtime parameters are atomic and can be done on
/// any thread.
pub struct RuntimeParameters {
passes_enabled: AtomicUsize,
pub(crate) parameters: ArcSwap<FastHashMap<String, f32>>,
}
/// Enumerates the active parameters as well as their values in the current filter chain.
fn enumerate_parameters<'a>(
&'a self,
) -> ::librashader_common::map::halfbrown::Iter<String, f32>;
impl RuntimeParameters {
/// Create a new instance of runtime parameters from a `Vec` of
/// shader parameters from a [`ShaderPreset`](librashader_presets::ShaderPreset).
pub fn new(passes_enabled: usize, parameters: Vec<ParameterConfig>) -> Self {
RuntimeParameters {
passes_enabled: AtomicUsize::new(passes_enabled),
parameters: ArcSwap::new(Arc::new(
parameters
.into_iter()
.map(|param| (param.name, param.value))
.collect(),
)),
}
}
/// Get the value of the given parameter if present.
fn get_parameter(&self, parameter: &str) -> Option<f32>;
/// Get the value of a runtime parameter
pub fn get_parameter(&self, name: &str) -> Option<f32> {
self.parameters.load().get::<str>(name.as_ref()).copied()
}
/// Set the value of the given parameter if present.
/// Set a runtime parameter.
///
/// Returns `None` if the parameter did not exist, or the old value if successful.
fn set_parameter(&mut self, parameter: &str, new_value: f32) -> Option<f32>;
/// This is a relatively slow operation as it will be synchronized across threads.
pub fn set_parameter(&self, name: &str, new_value: f32) -> Option<f32> {
let mut updated_map = FastHashMap::clone(&self.parameters.load());
if let Some(value) = updated_map.get_mut::<str>(name.as_ref()) {
let old = *value;
*value = new_value;
self.parameters.store(Arc::new(updated_map));
Some(old)
} else {
None
}
}
/// Get a reference to the runtime parameters.
pub fn parameters(&self) -> Arc<FastHashMap<String, f32>> {
self.parameters.load_full()
}
/// Get the number of passes enabled.
///
/// If set from [`RuntimeParameters::set_passes_enabled`] from a different thread,
/// it is not guaranteed to be immediately visible.
#[inline(always)]
pub fn passes_enabled(&self) -> usize {
self.passes_enabled.load(Ordering::Relaxed)
}
/// Set the number of passes enabled.
///
/// This is an atomic operation and is thread-safe.
#[inline(always)]
pub fn set_passes_enabled(&self, count: usize) {
self.passes_enabled.store(count, Ordering::Relaxed);
}
}
#[macro_export]
macro_rules! impl_filter_chain_parameters {
($ty:ty) => {
impl ::librashader_runtime::parameters::FilterChainParameters for $ty {
fn get_enabled_pass_count(&self) -> usize {
self.common.config.passes_enabled
}
fn set_enabled_pass_count(&mut self, count: usize) {
self.common.config.passes_enabled = count
}
fn enumerate_parameters<'a>(
&'a self,
) -> ::librashader_common::map::halfbrown::Iter<String, f32> {
self.common.config.parameters.iter()
}
fn get_parameter(&self, parameter: &str) -> Option<f32> {
self.common
.config
.parameters
.get::<str>(parameter.as_ref())
.copied()
}
fn set_parameter(&mut self, parameter: &str, new_value: f32) -> Option<f32> {
if let Some(value) = self
.common
.config
.parameters
.get_mut::<str>(parameter.as_ref())
{
let old = *value;
*value = new_value;
Some(old)
} else {
None
}
fn parameters(&self) -> &::librashader_runtime::parameters::RuntimeParameters {
&self.common.config
}
}
};

View file

@ -236,6 +236,7 @@ pub mod reflect {
pub mod runtime {
pub use librashader_common::{Size, Viewport};
pub use librashader_runtime::parameters::FilterChainParameters;
pub use librashader_runtime::parameters::RuntimeParameters;
#[cfg(feature = "runtime-gl")]
#[doc(cfg(feature = "runtime-gl"))]