wgpu: enable pipeline caching

This commit is contained in:
chyyran 2024-08-01 02:06:28 -04:00 committed by Ronny Chan
parent 6ce711db26
commit 35f499f5e1
8 changed files with 93 additions and 19 deletions

1
Cargo.lock generated
View file

@ -1876,6 +1876,7 @@ dependencies = [
"config",
"env_logger",
"image",
"librashader-cache",
"librashader-common",
"librashader-preprocess",
"librashader-presets",

View file

@ -16,3 +16,13 @@ impl Cacheable for Vec<u8> {
Some(self.to_vec())
}
}
impl Cacheable for Option<Vec<u8>> {
fn from_bytes(bytes: &[u8]) -> Option<Self> {
Some(Some(Vec::from(bytes)))
}
fn to_bytes(&self) -> Option<Vec<u8>> {
self.clone()
}
}

View file

@ -19,6 +19,7 @@ librashader-presets = { path = "../librashader-presets", version = "0.2.8" }
librashader-preprocess = { path = "../librashader-preprocess", version = "0.2.8" }
librashader-reflect = { path = "../librashader-reflect", version = "0.2.8", features = ["wgsl"], default-features = false }
librashader-runtime = { path = "../librashader-runtime" , version = "0.2.8" }
librashader-cache = { path = "../librashader-cache", version = "0.2.8" }
wgpu = { version = "22.0", default-features = false, features = ["wgsl"] }
image = "0.25.1"

View file

@ -25,7 +25,7 @@ use librashader_reflect::reflect::naga::{Naga, NagaLoweringOptions};
use librashader_runtime::framebuffer::FramebufferInit;
use librashader_runtime::render_target::RenderTarget;
use librashader_runtime::scaling::ScaleFramebuffer;
use wgpu::{Device, TextureFormat};
use wgpu::{AdapterInfo, Device, TextureFormat};
use crate::error;
use crate::error::FilterChainError;
@ -144,8 +144,17 @@ impl FilterChainWgpu {
) -> error::Result<FilterChainWgpu> {
let (passes, semantics) = compile_passes(preset.shaders, &preset.textures)?;
// // initialize passes
let filters = Self::init_passes(Arc::clone(&device), passes, &semantics)?;
// cache is opt-in for wgpu, not opt-out because of feature requirements.
let disable_cache = options.map_or(true, |o| !o.enable_cache);
// initialize passes
let filters = Self::init_passes(
Arc::clone(&device),
passes,
&semantics,
options.and_then(|o| o.adapter_info.as_ref()),
disable_cache,
)?;
let samplers = SamplerSet::new(&device);
let mut mipmapper = MipmapGen::new(Arc::clone(&device));
@ -268,6 +277,8 @@ impl FilterChainWgpu {
device: Arc<Device>,
passes: Vec<ShaderPassMeta>,
semantics: &ShaderSemantics,
adapter_info: Option<&wgpu::AdapterInfo>,
disable_cache: bool,
) -> error::Result<Box<[FilterPass]>> {
#[cfg(not(target_arch = "wasm32"))]
let filter_creation_fn = || {
@ -320,6 +331,8 @@ impl FilterChainWgpu {
&wgsl,
&reflection,
render_pass_format.unwrap_or(TextureFormat::Rgba8Unorm),
adapter_info,
disable_cache,
);
Ok(FilterPass {

View file

@ -1,11 +1,14 @@
use crate::error::FilterChainError;
use crate::framebuffer::WgpuOutputView;
use crate::util;
use librashader_cache::cache_pipeline;
use librashader_reflect::back::wgsl::NagaWgslContext;
use librashader_reflect::back::ShaderCompilerOutput;
use librashader_reflect::reflect::ShaderReflection;
use librashader_runtime::quad::VertexInput;
use librashader_runtime::render_target::RenderTarget;
use std::borrow::Cow;
use std::convert::Infallible;
use std::sync::Arc;
use wgpu::{
BindGroupLayout, BindGroupLayoutDescriptor, BindGroupLayoutEntry, BindingType,
@ -18,6 +21,7 @@ use wgpu::{
pub struct WgpuGraphicsPipeline {
pub layout: PipelineLayoutObjects,
render_pipeline: wgpu::RenderPipeline,
cache: Option<wgpu::PipelineCache>,
pub format: wgpu::TextureFormat,
}
@ -144,7 +148,11 @@ impl PipelineLayoutObjects {
}
}
pub fn create_pipeline(&self, framebuffer_format: TextureFormat) -> wgpu::RenderPipeline {
pub fn create_pipeline(
&self,
framebuffer_format: TextureFormat,
cache: Option<&wgpu::PipelineCache>,
) -> wgpu::RenderPipeline {
self.device
.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
label: Some("Render Pipeline"),
@ -198,8 +206,7 @@ impl PipelineLayoutObjects {
alpha_to_coverage_enabled: false,
},
multiview: None,
// todo: WGPU pipeline caching!!
cache: None,
cache,
})
}
}
@ -210,18 +217,50 @@ impl WgpuGraphicsPipeline {
shader_assembly: &ShaderCompilerOutput<String, NagaWgslContext>,
reflection: &ShaderReflection,
render_pass_format: TextureFormat,
adapter_info: Option<&wgpu::AdapterInfo>,
bypass_cache: bool,
) -> Self {
let cache = if bypass_cache {
None
} else {
let name = adapter_info
.and_then(|o| wgpu::util::pipeline_cache_key(o))
.unwrap_or_else(|| String::from("wgpu"));
cache_pipeline(
&name,
&[
&shader_assembly.vertex.as_str(),
&shader_assembly.fragment.as_str(),
],
|pipeline_data| {
let descriptor = wgpu::PipelineCacheDescriptor {
label: Some("librashader-wgpu"),
data: pipeline_data.as_deref(),
fallback: true,
};
let cache = unsafe { device.create_pipeline_cache(&descriptor) };
Ok::<_, Infallible>(cache)
},
|cache| Ok(cache.get_data()),
bypass_cache,
)
.ok()
};
let layout = PipelineLayoutObjects::new(reflection, shader_assembly, device);
let render_pipeline = layout.create_pipeline(render_pass_format);
let render_pipeline = layout.create_pipeline(render_pass_format, cache.as_ref());
Self {
layout,
render_pipeline,
format: render_pass_format,
cache,
}
}
pub fn recompile(&mut self, format: TextureFormat) {
let render_pipeline = self.layout.create_pipeline(format);
let render_pipeline = self.layout.create_pipeline(format, self.cache.as_ref());
self.render_pipeline = render_pipeline;
}

View file

@ -36,7 +36,6 @@ impl MipmapGen {
depth_stencil: None,
multisample: wgpu::MultisampleState::default(),
multiview: None,
// todo: caching!!
cache: None,
});

View file

@ -9,4 +9,11 @@ impl_default_frame_options!(FrameOptionsWgpu);
pub struct FilterChainOptionsWgpu {
/// Whether or not to explicitly disable mipmap generation regardless of shader preset settings.
pub force_no_mipmaps: bool,
/// Enable the shader object cache. Shaders will be loaded from the cache
/// if this is enabled.
pub enable_cache: bool,
/// WGPU adapter info for use to determine the name of the pipeline cache index.
/// If this is not provided, then it will fallback to a default "wgpu" index, which
/// may clobber the cache for a different device using WGPU.
pub adapter_info: Option<wgpu::AdapterInfo>,
}

View file

@ -93,9 +93,13 @@ impl<'a> State<'a> {
let (device, queue) = adapter
.request_device(
&wgpu::DeviceDescriptor {
required_features: wgpu::Features::ADDRESS_MODE_CLAMP_TO_BORDER,
required_features: wgpu::Features::ADDRESS_MODE_CLAMP_TO_BORDER
| wgpu::Features::PIPELINE_CACHE
| wgpu::Features::TEXTURE_ADAPTER_SPECIFIC_FORMAT_FEATURES
| wgpu::Features::FLOAT32_FILTERABLE,
required_limits: wgpu::Limits::default(),
label: None,
memory_hints: Default::default(),
},
None,
)
@ -118,17 +122,14 @@ impl<'a> State<'a> {
let device = Arc::new(device);
let queue = Arc::new(queue);
//
// let preset = ShaderPreset::try_parse(
// "../test/basic.slangp",
// )
// .unwrap();
// let preset = ShaderPreset::try_parse("../test/basic.slangp").unwrap();
//
let preset = ShaderPreset::try_parse("../test/shaders_slang/test/history.slangp").unwrap();
// let preset = ShaderPreset::try_parse("../test/shaders_slang/test/history.slangp").unwrap();
// let preset = ShaderPreset::try_parse(
// "../test/shaders_slang/bezel/Mega_Bezel/Presets/MBZ__0__SMOOTH-ADV.slangp",
// )
// .unwrap();
let preset = ShaderPreset::try_parse(
"../test/shaders_slang/bezel/Mega_Bezel/Presets/MBZ__0__SMOOTH-ADV.slangp",
)
.unwrap();
let chain = FilterChainWgpu::load_from_preset(
preset,
@ -154,11 +155,13 @@ impl<'a> State<'a> {
vertex: wgpu::VertexState {
module: &shader,
entry_point: "vs_main",
compilation_options: Default::default(),
buffers: &[Vertex::desc()],
},
fragment: Some(wgpu::FragmentState {
module: &shader,
entry_point: "fs_main",
compilation_options: Default::default(),
targets: &[Some(wgpu::ColorTargetState {
format: config.format,
blend: Some(wgpu::BlendState::REPLACE),
@ -181,6 +184,7 @@ impl<'a> State<'a> {
alpha_to_coverage_enabled: false,
},
multiview: None,
cache: None,
});
let vertex_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {