Skip to content

Commit

Permalink
static shader cache, bloom WIP
Browse files Browse the repository at this point in the history
  • Loading branch information
schell committed Apr 27, 2024
1 parent 641b2d7 commit bc12544
Show file tree
Hide file tree
Showing 48 changed files with 1,224 additions and 423 deletions.
576 changes: 539 additions & 37 deletions crates/renderling/src/bloom.rs

Large diffs are not rendered by default.

92 changes: 48 additions & 44 deletions crates/renderling/src/linkage.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
//! Provides convenient wrappers around renderling shader linkage.
// TODO: add a static cache for shader modules
use std::sync::Arc;

pub mod bloom_downsample_fragment;
pub mod bloom_upsample_fragment;
pub mod bloom_vertex;
pub mod brdf_lut_convolution_fragment;
pub mod brdf_lut_convolution_vertex;
pub mod generate_mipmap_fragment;
Expand All @@ -22,7 +25,7 @@ pub mod tutorial_slabbed_vertices;
pub mod tutorial_slabbed_vertices_no_instance;

pub struct ShaderLinkage {
pub module: wgpu::ShaderModule,
pub module: Arc<wgpu::ShaderModule>,
pub entry_point: &'static str,
}

Expand Down Expand Up @@ -222,51 +225,43 @@ pub fn atlas_and_skybox_bindgroup(

#[cfg(test)]
mod test {
use snafu::prelude::*;

#[derive(Debug, Snafu)]
enum SrcError {
#[snafu(display("{source}"))]
Read { source: std::io::Error },
#[snafu(display("{source}"))]
ParseSpv { source: naga::front::spv::Error },
#[snafu(display("{source}"))]
Validate {
source: naga::WithSpan<naga::valid::ValidationError>,
},
#[snafu(display("{source}"))]
Write { source: naga::back::wgsl::Error },
#[snafu(display("{source}:\n{wgsl}"))]
ParseWgsl {
source: naga::front::wgsl::ParseError,
wgsl: String,
},
}

#[test]
// Ensure that the shaders can be converted to WGSL.
// This is necessary for WASM using WebGPU, because WebGPU only accepts
// WGSL as a shading language.
fn validate_shaders() {
fn validate_src(path: &std::path::PathBuf) -> Result<(), SrcError> {
println!("validating source");
println!(" reading {}", path.display());
let bytes = std::fs::read(path).context(ReadSnafu)?;
println!(" {:0.2}k bytes read", bytes.len() as f32 / 1000.0);
fn validate_src(path: &std::path::PathBuf) {
log::info!("validating source");
log::info!(" reading '{}'", path.display());
let bytes = std::fs::read(path).unwrap();
log::info!(" {:0.2}k bytes read", bytes.len() as f32 / 1000.0);
let opts = naga::front::spv::Options::default();
let module = naga::front::spv::parse_u8_slice(&bytes, &opts).context(ParseSpvSnafu)?;
println!(" parsed");
let module = match naga::front::spv::parse_u8_slice(&bytes, &opts) {
Ok(m) => m,
Err(e) => {
log::error!("{e}");
panic!("SPIR-V parse error");
}
};
println!(" SPIR-V parsed");
let mut validator =
naga::valid::Validator::new(Default::default(), naga::valid::Capabilities::empty());
let info = validator.validate(&module).context(ValidateSnafu)?;
println!(" validated");
let info = match validator.validate(&module) {
Ok(i) => i,
Err(e) => {
log::error!("{e}");
log::error!("{}", e.emit_to_string(&""));
panic!("SPIR-V validation error");
}
};
log::info!(" SPIR-V validated");
let wgsl = naga::back::wgsl::write_string(
&module,
&info,
naga::back::wgsl::WriterFlags::empty(),
)
.context(WriteSnafu)?;
println!(" output WGSL generated");
.unwrap();
log::info!(" output WGSL generated");
let print_var_name = path
.file_stem()
.unwrap()
Expand All @@ -275,20 +270,31 @@ mod test {
.replace("-", "_");
if let Ok(filepath) = std::env::var(&print_var_name) {
std::fs::write(&filepath, &wgsl).unwrap();
println!(" wrote generated WGSL to {filepath}");
log::info!(" wrote generated WGSL to {filepath}");
} else {
println!(
log::info!(
" to save the generated WGSL, use an env var '{print_var_name}={{filepath}}'"
);
}

let module = naga::front::wgsl::parse_str(&wgsl).context(ParseWgslSnafu { wgsl })?;
println!(" output WGSL parsed");
let module = match naga::front::wgsl::parse_str(&wgsl) {
Ok(m) => m,
Err(e) => {
log::error!("{}", e.emit_to_string(&wgsl));
panic!("wgsl parse error");
}
};
log::info!(" output WGSL parsed");
let mut validator =
naga::valid::Validator::new(Default::default(), naga::valid::Capabilities::empty());
let _info = validator.validate(&module).context(ValidateSnafu)?;
println!(" wgsl output validated");
Ok(())
let _info = match validator.validate(&module) {
Ok(i) => i,
Err(e) => {
log::error!("{}", e.emit_to_string(&wgsl));
panic!("wgsl validation error");
}
};
log::info!(" wgsl output validated");
}

let may_entries = std::fs::read_dir("src/linkage").unwrap();
Expand All @@ -297,9 +303,7 @@ mod test {
let path = entry.path();
let ext = path.extension().unwrap().to_str().unwrap();
if ext == "spv" {
if let Err(msg) = validate_src(&path) {
panic!("Invalid shader '{}': {msg}", path.display());
}
validate_src(&path);
}
}
}
Expand Down
37 changes: 25 additions & 12 deletions crates/renderling/src/linkage/array_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,21 +6,34 @@
//!
//! **source path**: `crates/renderling/src/linkage/shader_test-array_test.spv`
use super::ShaderLinkage;
pub fn linkage(device: &wgpu::Device) -> ShaderLinkage {
log::debug!("creating shader module for {}", stringify!(array_test));
#[cfg(not(target_arch = "wasm32"))]
let start = std::time::Instant::now();
let module = device.create_shader_module(wgpu::include_spirv!("shader_test-array_test.spv"));
#[cfg(not(target_arch = "wasm32"))]
{
let duration = std::time::Instant::now() - start;
log::debug!(
"...created shader module {} in {duration:?}",
stringify!(array_test)
use std::sync::{Arc, Mutex};
static ARRAY_TEST: Mutex<Option<Arc<wgpu::ShaderModule>>> = Mutex::new(None);
fn get_module(device: &wgpu::Device) -> Arc<wgpu::ShaderModule> {
let mut guard = ARRAY_TEST.lock().unwrap();
if let Some(module) = guard.as_ref() {
module.clone()
} else {
#[cfg(not(target_arch = "wasm32"))]
let start = std::time::Instant::now();
log::debug!("creating shader module for {}", stringify!(array_test));
let module = Arc::new(
device.create_shader_module(wgpu::include_spirv!("shader_test-array_test.spv")),
);
#[cfg(not(target_arch = "wasm32"))]
{
let duration = std::time::Instant::now() - start;
log::debug!(
"...created shader module {} in {duration:?}",
stringify!(array_test)
);
}
*guard = Some(module.clone());
module
}
}
pub fn linkage(device: &wgpu::Device) -> ShaderLinkage {
ShaderLinkage {
module,
module: get_module(device),
entry_point: "shader_test::array_test",
}
}
Binary file modified crates/renderling/src/linkage/bloom-bloom_downsample_fragment.spv
Binary file not shown.
Binary file modified crates/renderling/src/linkage/bloom-bloom_upsample_fragment.spv
Binary file not shown.
Binary file modified crates/renderling/src/linkage/bloom-bloom_vertex.spv
Binary file not shown.
41 changes: 27 additions & 14 deletions crates/renderling/src/linkage/bloom_downsample_fragment.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,25 +7,38 @@
//! **source path**:
//! `crates/renderling/src/linkage/bloom-bloom_downsample_fragment.spv`
use super::ShaderLinkage;
pub fn linkage(device: &wgpu::Device) -> ShaderLinkage {
log::debug!(
"creating shader module for {}",
stringify!(bloom_downsample_fragment)
);
#[cfg(not(target_arch = "wasm32"))]
let start = std::time::Instant::now();
let module =
device.create_shader_module(wgpu::include_spirv!("bloom-bloom_downsample_fragment.spv"));
#[cfg(not(target_arch = "wasm32"))]
{
let duration = std::time::Instant::now() - start;
use std::sync::{Arc, Mutex};
static BLOOM_DOWNSAMPLE_FRAGMENT: Mutex<Option<Arc<wgpu::ShaderModule>>> = Mutex::new(None);
fn get_module(device: &wgpu::Device) -> Arc<wgpu::ShaderModule> {
let mut guard = BLOOM_DOWNSAMPLE_FRAGMENT.lock().unwrap();
if let Some(module) = guard.as_ref() {
module.clone()
} else {
#[cfg(not(target_arch = "wasm32"))]
let start = std::time::Instant::now();
log::debug!(
"...created shader module {} in {duration:?}",
"creating shader module for {}",
stringify!(bloom_downsample_fragment)
);
let module = Arc::new(
device
.create_shader_module(wgpu::include_spirv!("bloom-bloom_downsample_fragment.spv")),
);
#[cfg(not(target_arch = "wasm32"))]
{
let duration = std::time::Instant::now() - start;
log::debug!(
"...created shader module {} in {duration:?}",
stringify!(bloom_downsample_fragment)
);
}
*guard = Some(module.clone());
module
}
}
pub fn linkage(device: &wgpu::Device) -> ShaderLinkage {
ShaderLinkage {
module,
module: get_module(device),
entry_point: "bloom::bloom_downsample_fragment",
}
}
40 changes: 26 additions & 14 deletions crates/renderling/src/linkage/bloom_upsample_fragment.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,25 +7,37 @@
//! **source path**:
//! `crates/renderling/src/linkage/bloom-bloom_upsample_fragment.spv`
use super::ShaderLinkage;
pub fn linkage(device: &wgpu::Device) -> ShaderLinkage {
log::debug!(
"creating shader module for {}",
stringify!(bloom_upsample_fragment)
);
#[cfg(not(target_arch = "wasm32"))]
let start = std::time::Instant::now();
let module =
device.create_shader_module(wgpu::include_spirv!("bloom-bloom_upsample_fragment.spv"));
#[cfg(not(target_arch = "wasm32"))]
{
let duration = std::time::Instant::now() - start;
use std::sync::{Arc, Mutex};
static BLOOM_UPSAMPLE_FRAGMENT: Mutex<Option<Arc<wgpu::ShaderModule>>> = Mutex::new(None);
fn get_module(device: &wgpu::Device) -> Arc<wgpu::ShaderModule> {
let mut guard = BLOOM_UPSAMPLE_FRAGMENT.lock().unwrap();
if let Some(module) = guard.as_ref() {
module.clone()
} else {
#[cfg(not(target_arch = "wasm32"))]
let start = std::time::Instant::now();
log::debug!(
"...created shader module {} in {duration:?}",
"creating shader module for {}",
stringify!(bloom_upsample_fragment)
);
let module = Arc::new(
device.create_shader_module(wgpu::include_spirv!("bloom-bloom_upsample_fragment.spv")),
);
#[cfg(not(target_arch = "wasm32"))]
{
let duration = std::time::Instant::now() - start;
log::debug!(
"...created shader module {} in {duration:?}",
stringify!(bloom_upsample_fragment)
);
}
*guard = Some(module.clone());
module
}
}
pub fn linkage(device: &wgpu::Device) -> ShaderLinkage {
ShaderLinkage {
module,
module: get_module(device),
entry_point: "bloom::bloom_upsample_fragment",
}
}
38 changes: 25 additions & 13 deletions crates/renderling/src/linkage/bloom_vertex.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,21 +6,33 @@
//!
//! **source path**: `crates/renderling/src/linkage/bloom-bloom_vertex.spv`
use super::ShaderLinkage;
pub fn linkage(device: &wgpu::Device) -> ShaderLinkage {
log::debug!("creating shader module for {}", stringify!(bloom_vertex));
#[cfg(not(target_arch = "wasm32"))]
let start = std::time::Instant::now();
let module = device.create_shader_module(wgpu::include_spirv!("bloom-bloom_vertex.spv"));
#[cfg(not(target_arch = "wasm32"))]
{
let duration = std::time::Instant::now() - start;
log::debug!(
"...created shader module {} in {duration:?}",
stringify!(bloom_vertex)
);
use std::sync::{Arc, Mutex};
static BLOOM_VERTEX: Mutex<Option<Arc<wgpu::ShaderModule>>> = Mutex::new(None);
fn get_module(device: &wgpu::Device) -> Arc<wgpu::ShaderModule> {
let mut guard = BLOOM_VERTEX.lock().unwrap();
if let Some(module) = guard.as_ref() {
module.clone()
} else {
#[cfg(not(target_arch = "wasm32"))]
let start = std::time::Instant::now();
log::debug!("creating shader module for {}", stringify!(bloom_vertex));
let module =
Arc::new(device.create_shader_module(wgpu::include_spirv!("bloom-bloom_vertex.spv")));
#[cfg(not(target_arch = "wasm32"))]
{
let duration = std::time::Instant::now() - start;
log::debug!(
"...created shader module {} in {duration:?}",
stringify!(bloom_vertex)
);
}
*guard = Some(module.clone());
module
}
}
pub fn linkage(device: &wgpu::Device) -> ShaderLinkage {
ShaderLinkage {
module,
module: get_module(device),
entry_point: "bloom::bloom_vertex",
}
}
41 changes: 26 additions & 15 deletions crates/renderling/src/linkage/brdf_lut_convolution_fragment.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,26 +8,37 @@
//! `crates/renderling/src/linkage/convolution-brdf_lut_convolution_fragment.
//! spv`
use super::ShaderLinkage;
pub fn linkage(device: &wgpu::Device) -> ShaderLinkage {
log::debug!(
"creating shader module for {}",
stringify!(brdf_lut_convolution_fragment)
);
#[cfg(not(target_arch = "wasm32"))]
let start = std::time::Instant::now();
let module = device.create_shader_module(wgpu::include_spirv!(
"convolution-brdf_lut_convolution_fragment.spv"
));
#[cfg(not(target_arch = "wasm32"))]
{
let duration = std::time::Instant::now() - start;
use std::sync::{Arc, Mutex};
static BRDF_LUT_CONVOLUTION_FRAGMENT: Mutex<Option<Arc<wgpu::ShaderModule>>> = Mutex::new(None);
fn get_module(device: &wgpu::Device) -> Arc<wgpu::ShaderModule> {
let mut guard = BRDF_LUT_CONVOLUTION_FRAGMENT.lock().unwrap();
if let Some(module) = guard.as_ref() {
module.clone()
} else {
#[cfg(not(target_arch = "wasm32"))]
let start = std::time::Instant::now();
log::debug!(
"...created shader module {} in {duration:?}",
"creating shader module for {}",
stringify!(brdf_lut_convolution_fragment)
);
let module = Arc::new(device.create_shader_module(wgpu::include_spirv!(
"convolution-brdf_lut_convolution_fragment.spv"
)));
#[cfg(not(target_arch = "wasm32"))]
{
let duration = std::time::Instant::now() - start;
log::debug!(
"...created shader module {} in {duration:?}",
stringify!(brdf_lut_convolution_fragment)
);
}
*guard = Some(module.clone());
module
}
}
pub fn linkage(device: &wgpu::Device) -> ShaderLinkage {
ShaderLinkage {
module,
module: get_module(device),
entry_point: "convolution::brdf_lut_convolution_fragment",
}
}
Loading

0 comments on commit bc12544

Please sign in to comment.