use crate::error::{RenderError, RenderResult};
pub struct ShaderProgram {
pub pipeline: wgpu::RenderPipeline,
pub bind_group_layouts: Vec<wgpu::BindGroupLayout>,
}
pub struct ShaderBuilder {
vertex_source: Option<String>,
fragment_source: Option<String>,
vertex_entry: String,
fragment_entry: String,
label: Option<String>,
}
impl ShaderBuilder {
#[must_use]
pub fn new() -> Self {
Self {
vertex_source: None,
fragment_source: None,
vertex_entry: "vs_main".to_string(),
fragment_entry: "fs_main".to_string(),
label: None,
}
}
#[must_use]
pub fn with_vertex(mut self, source: impl Into<String>) -> Self {
self.vertex_source = Some(source.into());
self
}
#[must_use]
pub fn with_fragment(mut self, source: impl Into<String>) -> Self {
self.fragment_source = Some(source.into());
self
}
#[must_use]
pub fn with_vertex_entry(mut self, entry: impl Into<String>) -> Self {
self.vertex_entry = entry.into();
self
}
#[must_use]
pub fn with_fragment_entry(mut self, entry: impl Into<String>) -> Self {
self.fragment_entry = entry.into();
self
}
#[must_use]
pub fn with_label(mut self, label: impl Into<String>) -> Self {
self.label = Some(label.into());
self
}
pub fn build_module(self, device: &wgpu::Device) -> RenderResult<wgpu::ShaderModule> {
let source = self.combined_source()?;
let module = device.create_shader_module(wgpu::ShaderModuleDescriptor {
label: self.label.as_deref(),
source: wgpu::ShaderSource::Wgsl(source.into()),
});
Ok(module)
}
fn combined_source(&self) -> RenderResult<String> {
let vertex = self
.vertex_source
.as_ref()
.ok_or_else(|| RenderError::ShaderCompilationFailed("missing vertex shader".into()))?;
let fragment = self.fragment_source.as_ref().ok_or_else(|| {
RenderError::ShaderCompilationFailed("missing fragment shader".into())
})?;
if vertex == fragment {
return Ok(vertex.clone());
}
Ok(format!("{vertex}\n\n{fragment}"))
}
}
impl Default for ShaderBuilder {
fn default() -> Self {
Self::new()
}
}