use super::types::{WebGpuError, WebGpuShader};
use std::collections::HashMap;
use std::sync::Arc;
pub struct ShaderManager {
device: Arc<wgpu::Device>,
shader_cache: HashMap<String, wgpu::ShaderModule>,
render_pipelines: HashMap<String, wgpu::RenderPipeline>,
}
impl ShaderManager {
pub fn new(device: Arc<wgpu::Device>) -> Self {
Self {
device,
shader_cache: HashMap::new(),
render_pipelines: HashMap::new(),
}
}
pub fn compile_shader(&mut self, shader: &WebGpuShader) -> Result<wgpu::ShaderModule, WebGpuError> {
if let Some(cached_shader) = self.shader_cache.get(&shader.name) {
return Ok(cached_shader.clone());
}
let shader_module = self.device.create_shader_module(wgpu::ShaderModuleDescriptor {
label: Some(&shader.name),
source: wgpu::ShaderSource::Wgsl(shader.source.clone().into()),
});
self.shader_cache.insert(shader.name.clone(), shader_module.clone());
Ok(shader_module)
}
pub fn get_shader(&self, name: &str) -> Option<&wgpu::ShaderModule> {
self.shader_cache.get(name)
}
pub fn create_render_pipeline(
&mut self,
name: &str,
shader_module: &wgpu::ShaderModule,
vertex_buffer_layouts: &[wgpu::VertexBufferLayout],
color_targets: &[Option<wgpu::ColorTargetState>],
) -> Result<wgpu::RenderPipeline, WebGpuError> {
let pipeline = self.device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
label: Some(name),
layout: None, vertex: wgpu::VertexState {
module: shader_module,
entry_point: Some("vs_main"),
buffers: vertex_buffer_layouts,
compilation_options: wgpu::PipelineCompilationOptions::default(),
},
fragment: Some(wgpu::FragmentState {
module: shader_module,
entry_point: Some("fs_main"),
targets: color_targets,
compilation_options: wgpu::PipelineCompilationOptions::default(),
}),
primitive: wgpu::PrimitiveState {
topology: wgpu::PrimitiveTopology::TriangleList,
strip_index_format: None,
front_face: wgpu::FrontFace::Ccw,
cull_mode: Some(wgpu::Face::Back),
polygon_mode: wgpu::PolygonMode::Fill,
unclipped_depth: false,
conservative: false,
},
depth_stencil: None,
multisample: wgpu::MultisampleState {
count: 1,
mask: !0,
alpha_to_coverage_enabled: false,
},
cache: None,
multiview: None,
});
self.render_pipelines.insert(name.to_string(), pipeline.clone());
Ok(pipeline)
}
pub fn get_render_pipeline(&self, name: &str) -> Option<&wgpu::RenderPipeline> {
self.render_pipelines.get(name)
}
pub fn clear_cache(&mut self) {
self.shader_cache.clear();
self.render_pipelines.clear();
}
pub fn get_cache_stats(&self) -> (usize, usize) {
(self.shader_cache.len(), self.render_pipelines.len())
}
}
impl WebGpuShader {
pub fn new(name: String, source: String, entry_point: String) -> Self {
Self {
name,
source,
entry_point,
}
}
pub fn basic_vertex() -> Self {
Self::new(
"basic_vertex".to_string(),
include_str!("shaders/basic_vertex.wgsl").to_string(),
"vs_main".to_string(),
)
}
pub fn basic_fragment() -> Self {
Self::new(
"basic_fragment".to_string(),
include_str!("shaders/basic_fragment.wgsl").to_string(),
"fs_main".to_string(),
)
}
}