use crate::backend::{GpuBackend, ShaderHandle};
use crate::device::Device;
use crate::slang::{layout_validation_enabled, LayoutCheck, OwnedLayoutCheck};
use anyhow::{Context, Result};
use std::sync::{Arc, Mutex};
pub struct ShaderModule {
_device: Device,
backend: Arc<Mutex<Box<dyn GpuBackend>>>,
pub(crate) handle: ShaderHandle,
}
impl ShaderModule {
pub fn from_slang(device: &Device, source: &str) -> Result<Self> {
Self::from_slang_with_options(device, source, &[], &[], Default::default(), &[])
}
pub fn from_slang_with_paths(device: &Device, source: &str, extra_paths: &[&str]) -> Result<Self> {
Self::from_slang_with_options(device, source, extra_paths, &[], Default::default(), &[])
}
pub fn from_slang_with_paths_and_defines(
device: &Device,
source: &str,
extra_paths: &[&str],
defines: &[(&str, &str)],
) -> Result<Self> {
Self::from_slang_with_options(device, source, extra_paths, defines, Default::default(), &[])
}
pub fn from_slang_with_options(
device: &Device,
source: &str,
extra_paths: &[&str],
defines: &[(&str, &str)],
optimization_level: crate::types::OptimizationLevel,
layout_checks: &[LayoutCheck<'_>],
) -> Result<Self> {
let validate = layout_validation_enabled() && !layout_checks.is_empty();
tracing::debug!(
source_len = source.len(),
extra_paths = extra_paths.len(),
defines = defines.len(),
layout_checks = layout_checks.len(),
validate,
?optimization_level,
"Compiling shader module"
);
let library_paths = device
.get_shader_search_paths()
.context("Failed to prepare shader library paths")?;
let all_paths: Vec<String> = library_paths
.iter()
.map(|p| p.to_string_lossy().into_owned())
.chain(extra_paths.iter().map(|s| s.to_string()))
.collect();
let path_refs: Vec<&str> = all_paths.iter().map(|s| s.as_str()).collect();
let mut backend = device.inner.backend.lock().unwrap();
let handle = if validate {
let owned_checks: Vec<OwnedLayoutCheck> =
layout_checks.iter().map(OwnedLayoutCheck::from_layout_check).collect();
backend.create_shader_with_checks(
device.inner.handle,
source,
&path_refs,
defines,
optimization_level,
owned_checks,
)?
} else {
backend.create_shader_with_paths(device.inner.handle, source, &path_refs, defines, optimization_level)?
};
tracing::debug!("Shader module created");
Ok(Self {
_device: device.clone(),
backend: Arc::clone(&device.inner.backend),
handle,
})
}
}
impl Drop for ShaderModule {
fn drop(&mut self) {
tracing::trace!("Destroying shader module");
let mut backend = self.backend.lock().unwrap();
backend.destroy_shader(self.handle);
}
}
pub mod builtins {
pub const VERTEX_COLOR_2D: &str = r#"
struct VertexInput {
float2 position : POSITION;
float4 color : COLOR;
};
struct VertexOutput {
float4 position : SV_Position;
float4 color : COLOR;
};
[shader("vertex")]
VertexOutput vs_main(VertexInput input) {
VertexOutput output;
output.position = float4(input.position, 0.0, 1.0);
output.color = input.color;
return output;
}
[shader("fragment")]
float4 fs_main(VertexOutput input) : SV_Target {
return input.color;
}
"#;
pub const SOLID_COLOR: &str = r#"
struct VertexInput {
float2 position : POSITION;
};
struct VertexOutput {
float4 position : SV_Position;
};
cbuffer Uniforms {
float4 color;
};
[shader("vertex")]
VertexOutput vs_main(VertexInput input) {
VertexOutput output;
output.position = float4(input.position, 0.0, 1.0);
return output;
}
[shader("fragment")]
float4 fs_main(VertexOutput input) : SV_Target {
return color;
}
"#;
}