use std::borrow::Cow;
use crate::lazy_resource::LazyGpuResource;
#[derive(Clone, Copy)]
pub(crate) struct ShaderCode {
wgsl: fn() -> Cow<'static, str>,
#[cfg(all(target_arch = "wasm32", feature = "gl-templates"))]
glsl: &'static [wgpu::hal::GlslTemplate],
}
impl ShaderCode {
pub(crate) const fn wgsl(wgsl: fn() -> Cow<'static, str>) -> Self {
Self {
wgsl,
#[cfg(all(target_arch = "wasm32", feature = "gl-templates"))]
glsl: &[],
}
}
#[cfg(all(target_arch = "wasm32", feature = "gl-templates"))]
pub(crate) const fn built_in(
wgsl: fn() -> Cow<'static, str>,
glsl: &'static [wgpu::hal::GlslTemplate],
) -> Self {
Self { wgsl, glsl }
}
#[cfg(all(target_arch = "wasm32", feature = "gl-templates"))]
fn template_module(
&self,
device: &wgpu::Device,
backend: wgpu::Backend,
label: &str,
) -> Option<wgpu::ShaderModule> {
if backend != wgpu::Backend::Gl || self.glsl.is_empty() {
return None;
}
device.create_shader_module_glsl_templates(Some(label), self.glsl)
}
fn wgsl_module(&self, device: &wgpu::Device, label: &str) -> wgpu::ShaderModule {
device.create_shader_module(wgpu::ShaderModuleDescriptor {
label: Some(label),
source: wgpu::ShaderSource::Wgsl((self.wgsl)()),
})
}
}
#[derive(Clone)]
pub(crate) struct SharedShader {
device: wgpu::Device,
backend: wgpu::Backend,
label: &'static str,
code: ShaderCode,
module: LazyGpuResource<wgpu::ShaderModule>,
layout: wgpu::PipelineLayout,
}
impl SharedShader {
pub(crate) fn new(
device: &wgpu::Device,
backend: wgpu::Backend,
label: &'static str,
code: ShaderCode,
bind_group_layouts: &[Option<&wgpu::BindGroupLayout>],
) -> Self {
Self {
device: device.clone(),
backend,
label,
code,
module: LazyGpuResource::new(label),
layout: device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
label: Some(label),
bind_group_layouts,
immediate_size: 0,
}),
}
}
pub(crate) fn module(&self) -> &wgpu::ShaderModule {
self.module.get_or_init(self.backend, || {
#[cfg(all(target_arch = "wasm32", feature = "gl-templates"))]
if let Some(module) = self
.code
.template_module(&self.device, self.backend, self.label)
{
return module;
}
self.code.wgsl_module(&self.device, self.label)
})
}
pub(crate) fn layout(&self) -> &wgpu::PipelineLayout {
&self.layout
}
}
#[cfg(test)]
#[path = "tests/shared_shader_tests.rs"]
mod tests;