diff --git a/example/build.rs b/example/build.rs index c70487f..09899b4 100644 --- a/example/build.rs +++ b/example/build.rs @@ -1,31 +1,32 @@ use miette::{IntoDiagnostic, Result}; +use wgsl_bindgen::qs::quote; use wgsl_bindgen::{ - qs::quote, GlamWgslTypeMap, Regex, WgslBindgenOptionBuilder, WgslShaderSourceType, - WgslTypeSerializeStrategy, + GlamWgslTypeMap, Regex, WgslBindgenOptionBuilder, WgslShaderSourceType, + WgslTypeSerializeStrategy, }; fn main() -> Result<()> { - WgslBindgenOptionBuilder::default() - .workspace_root("shaders") - .add_entry_point("shaders/testbed.wgsl") - .add_entry_point("shaders/triangle.wgsl") - .skip_hash_check(true) - .serialization_strategy(WgslTypeSerializeStrategy::Bytemuck) - .type_map(GlamWgslTypeMap) - .override_struct_field_type( - [("utils::types::VectorsU32", "a", quote!(crate::MyTwoU32))].map(Into::into), - ) - .add_override_struct_mapping(("utils::types::Scalars", quote!(crate::MyScalars))) - .add_custom_padding_field_regexp(Regex::new("_pad.*").unwrap()) - .short_constructor(2) - .shader_source_type( - WgslShaderSourceType::UseComposerWithPath - | WgslShaderSourceType::UseComposerEmbed - | WgslShaderSourceType::UseEmbed, - ) - .derive_serde(false) - .output("src/shader_bindings.rs") - .build()? - .generate() - .into_diagnostic() + WgslBindgenOptionBuilder::default() + .workspace_root("shaders") + .add_entry_point("shaders/testbed.wgsl") + .add_entry_point("shaders/triangle.wgsl") + .skip_hash_check(true) + .serialization_strategy(WgslTypeSerializeStrategy::Bytemuck) + .type_map(GlamWgslTypeMap) + .override_struct_field_type( + [("utils::types::VectorsU32", "a", quote!(crate::MyTwoU32))].map(Into::into), + ) + .add_override_struct_mapping(("utils::types::Scalars", quote!(crate::MyScalars))) + .add_custom_padding_field_regexp(Regex::new("_pad.*").unwrap()) + .short_constructor(2) + .shader_source_type( + WgslShaderSourceType::UseComposerWithPath + | WgslShaderSourceType::UseComposerEmbed + | WgslShaderSourceType::UseEmbed, + ) + .derive_serde(false) + .output("src/shader_bindings.rs") + .build()? + .generate() + .into_diagnostic() } diff --git a/example/src/main.rs b/example/src/main.rs index d7c1ade..59d4717 100644 --- a/example/src/main.rs +++ b/example/src/main.rs @@ -1,317 +1,323 @@ #![allow(dead_code)] -use std::{iter, sync::Arc}; +use std::iter; +use std::sync::Arc; use futures::executor::block_on; use glam::{vec3a, vec4}; use wgpu::util::DeviceExt; -use winit::{application::ApplicationHandler, event::*, event_loop::EventLoop, window::Window}; +use winit::application::ApplicationHandler; +use winit::event::*; +use winit::event_loop::EventLoop; +use winit::window::Window; pub type MyTwoU32 = [u32; 2]; #[repr(C)] #[derive(Debug, Clone, Copy, Default, PartialEq)] pub struct MyScalars { - a: u32, - b: i32, - c: f32, + a: u32, + b: i32, + c: f32, } // Include the bindings generated by build.rs. mod shader_bindings; struct State { - window: Arc, - surface: wgpu::Surface<'static>, - device: wgpu::Device, - queue: wgpu::Queue, - size: winit::dpi::PhysicalSize, - config: wgpu::SurfaceConfiguration, - pipeline: wgpu::RenderPipeline, - bind_group0: shader_bindings::triangle::WgpuBindGroup0, - bind_group1: shader_bindings::triangle::WgpuBindGroup1, - vertex_buffer: wgpu::Buffer, + window: Arc, + surface: wgpu::Surface<'static>, + device: wgpu::Device, + queue: wgpu::Queue, + size: winit::dpi::PhysicalSize, + config: wgpu::SurfaceConfiguration, + pipeline: wgpu::RenderPipeline, + bind_group0: shader_bindings::triangle::WgpuBindGroup0, + bind_group1: shader_bindings::triangle::WgpuBindGroup1, + vertex_buffer: wgpu::Buffer, } impl State { - async fn new(window: Window) -> Self { - let window = Arc::new(window); - let instance = wgpu::Instance::new(wgpu::InstanceDescriptor { - backends: wgpu::Backends::all(), - ..Default::default() - }); - let surface = instance.create_surface(window.clone()).unwrap(); - let adapter = instance - .request_adapter(&wgpu::RequestAdapterOptions { - power_preference: wgpu::PowerPreference::default(), - compatible_surface: Some(&surface), - force_fallback_adapter: false, - }) - .await - .unwrap(); - - let (device, queue) = adapter - .request_device( - &wgpu::DeviceDescriptor { - label: None, - required_features: wgpu::Features::TEXTURE_COMPRESSION_BC, - required_limits: wgpu::Limits::default(), - }, - None, - ) - .await - .unwrap(); - - let size = window.inner_size(); - let caps = surface.get_capabilities(&adapter); - let surface_format = caps.formats[0]; - let config = surface - .get_default_config(&adapter, size.width, size.height) - .unwrap(); - surface.configure(&device, &config); - - // Use the generated bindings to create the pipeline. - let shader = shader_bindings::triangle::create_shader_module_from_path( - &device, - [("VERTEX_UVS".to_owned(), Default::default())].into(), - ) - .unwrap(); - let render_pipeline_layout = shader_bindings::triangle::create_pipeline_layout(&device); - - let pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor { - label: Some("Render Pipeline"), - layout: Some(&render_pipeline_layout), - vertex: shader_bindings::triangle::vertex_state( - &shader, - &shader_bindings::triangle::vs_main_entry(wgpu::VertexStepMode::Vertex), - ), - fragment: Some(wgpu::FragmentState { - module: &shader, - entry_point: shader_bindings::triangle::ENTRY_FS_MAIN, - targets: &[Some(surface_format.into())], - compilation_options: Default::default(), - }), - primitive: wgpu::PrimitiveState::default(), - depth_stencil: None, - multisample: wgpu::MultisampleState::default(), - multiview: None, - }); - - // Create a gradient texture. - let texture = device.create_texture_with_data( - &queue, - &wgpu::TextureDescriptor { - label: None, - size: wgpu::Extent3d { - width: 4, - height: 4, - depth_or_array_layers: 1, - }, - mip_level_count: 1, - sample_count: 1, - dimension: wgpu::TextureDimension::D2, - format: wgpu::TextureFormat::Rgba8Unorm, - usage: wgpu::TextureUsages::all(), - view_formats: &[], - }, - wgpu::util::TextureDataOrder::LayerMajor, - &vec![ - [0, 0, 255, 255], - [64, 0, 255, 255], - [128, 0, 255, 255], - [255, 0, 255, 255], - [0, 64, 255, 255], - [64, 64, 255, 255], - [128, 64, 255, 255], - [255, 64, 255, 255], - [0, 128, 255, 255], - [64, 128, 255, 255], - [128, 128, 255, 255], - [255, 128, 255, 255], - [0, 255, 255, 255], - [64, 255, 255, 255], - [128, 255, 255, 255], - [255, 255, 255, 255], - ] - .into_iter() - .flatten() - .collect::>(), - ); - - let view = texture.create_view(&wgpu::TextureViewDescriptor::default()); - let sampler = device.create_sampler(&wgpu::SamplerDescriptor { - mag_filter: wgpu::FilterMode::Linear, - min_filter: wgpu::FilterMode::Linear, - ..Default::default() - }); - - // Use the generated types to ensure the correct bind group is assigned to each slot. - let bind_group0 = shader_bindings::triangle::WgpuBindGroup0::from_bindings( - &device, - shader_bindings::triangle::WgpuBindGroup0Entries::new( - shader_bindings::triangle::WgpuBindGroup0EntriesParams { - color_texture: &view, - color_sampler: &sampler, - }, - ), - ); - - let uniforms_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor { - label: Some("uniforms"), - contents: bytemuck::cast_slice(&[shader_bindings::triangle::Uniforms(vec4( - 1.0, 1.0, 1.0, 1.0, - ))]), - usage: wgpu::BufferUsages::UNIFORM, - }); - - let bind_group1 = shader_bindings::triangle::WgpuBindGroup1::from_bindings( - &device, - shader_bindings::triangle::WgpuBindGroup1Entries::new( - shader_bindings::triangle::WgpuBindGroup1EntriesParams { - uniforms: uniforms_buffer.as_entire_buffer_binding(), - }, - ), - ); - - // Initialize the vertex buffer based on the expected input structs. - // For storage buffer compatibility, consider using encase instead. - let vertex_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor { - label: Some("vertex buffer"), - contents: bytemuck::cast_slice(&[ - shader_bindings::triangle::VertexInput(vec3a(-1.0, -1.0, 0.0)), - shader_bindings::triangle::VertexInput(vec3a(3.0, -1.0, 0.0)), - shader_bindings::triangle::VertexInput(vec3a(-1.0, 3.0, 0.0)), - ]), - usage: wgpu::BufferUsages::VERTEX, - }); - - Self { - surface, - window, - device, - queue, - size, - config, - pipeline, - bind_group0, - bind_group1, - vertex_buffer, - } + async fn new(window: Window) -> Self { + let window = Arc::new(window); + let instance = wgpu::Instance::new(wgpu::InstanceDescriptor { + backends: wgpu::Backends::all(), + ..Default::default() + }); + let surface = instance.create_surface(window.clone()).unwrap(); + let adapter = instance + .request_adapter(&wgpu::RequestAdapterOptions { + power_preference: wgpu::PowerPreference::default(), + compatible_surface: Some(&surface), + force_fallback_adapter: false, + }) + .await + .unwrap(); + + let (device, queue) = adapter + .request_device( + &wgpu::DeviceDescriptor { + label: None, + required_features: wgpu::Features::TEXTURE_COMPRESSION_BC, + required_limits: wgpu::Limits::default(), + }, + None, + ) + .await + .unwrap(); + + let size = window.inner_size(); + let caps = surface.get_capabilities(&adapter); + let surface_format = caps.formats[0]; + let config = surface + .get_default_config(&adapter, size.width, size.height) + .unwrap(); + surface.configure(&device, &config); + + // Use the generated bindings to create the pipeline. + let shader = shader_bindings::triangle::create_shader_module_from_path( + &device, + [("VERTEX_UVS".to_owned(), Default::default())].into(), + ) + .unwrap(); + let render_pipeline_layout = + shader_bindings::triangle::create_pipeline_layout(&device); + + let pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor { + label: Some("Render Pipeline"), + layout: Some(&render_pipeline_layout), + vertex: shader_bindings::triangle::vertex_state( + &shader, + &shader_bindings::triangle::vs_main_entry(wgpu::VertexStepMode::Vertex), + ), + fragment: Some(wgpu::FragmentState { + module: &shader, + entry_point: shader_bindings::triangle::ENTRY_FS_MAIN, + targets: &[Some(surface_format.into())], + compilation_options: Default::default(), + }), + primitive: wgpu::PrimitiveState::default(), + depth_stencil: None, + multisample: wgpu::MultisampleState::default(), + multiview: None, + }); + + // Create a gradient texture. + let texture = device.create_texture_with_data( + &queue, + &wgpu::TextureDescriptor { + label: None, + size: wgpu::Extent3d { + width: 4, + height: 4, + depth_or_array_layers: 1, + }, + mip_level_count: 1, + sample_count: 1, + dimension: wgpu::TextureDimension::D2, + format: wgpu::TextureFormat::Rgba8Unorm, + usage: wgpu::TextureUsages::all(), + view_formats: &[], + }, + wgpu::util::TextureDataOrder::LayerMajor, + &vec![ + [0, 0, 255, 255], + [64, 0, 255, 255], + [128, 0, 255, 255], + [255, 0, 255, 255], + [0, 64, 255, 255], + [64, 64, 255, 255], + [128, 64, 255, 255], + [255, 64, 255, 255], + [0, 128, 255, 255], + [64, 128, 255, 255], + [128, 128, 255, 255], + [255, 128, 255, 255], + [0, 255, 255, 255], + [64, 255, 255, 255], + [128, 255, 255, 255], + [255, 255, 255, 255], + ] + .into_iter() + .flatten() + .collect::>(), + ); + + let view = texture.create_view(&wgpu::TextureViewDescriptor::default()); + let sampler = device.create_sampler(&wgpu::SamplerDescriptor { + mag_filter: wgpu::FilterMode::Linear, + min_filter: wgpu::FilterMode::Linear, + ..Default::default() + }); + + // Use the generated types to ensure the correct bind group is assigned to each slot. + let bind_group0 = shader_bindings::triangle::WgpuBindGroup0::from_bindings( + &device, + shader_bindings::triangle::WgpuBindGroup0Entries::new( + shader_bindings::triangle::WgpuBindGroup0EntriesParams { + color_texture: &view, + color_sampler: &sampler, + }, + ), + ); + + let uniforms_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor { + label: Some("uniforms"), + contents: bytemuck::cast_slice(&[shader_bindings::triangle::Uniforms(vec4( + 1.0, 1.0, 1.0, 1.0, + ))]), + usage: wgpu::BufferUsages::UNIFORM, + }); + + let bind_group1 = shader_bindings::triangle::WgpuBindGroup1::from_bindings( + &device, + shader_bindings::triangle::WgpuBindGroup1Entries::new( + shader_bindings::triangle::WgpuBindGroup1EntriesParams { + uniforms: uniforms_buffer.as_entire_buffer_binding(), + }, + ), + ); + + // Initialize the vertex buffer based on the expected input structs. + // For storage buffer compatibility, consider using encase instead. + let vertex_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor { + label: Some("vertex buffer"), + contents: bytemuck::cast_slice(&[ + shader_bindings::triangle::VertexInput(vec3a(-1.0, -1.0, 0.0)), + shader_bindings::triangle::VertexInput(vec3a(3.0, -1.0, 0.0)), + shader_bindings::triangle::VertexInput(vec3a(-1.0, 3.0, 0.0)), + ]), + usage: wgpu::BufferUsages::VERTEX, + }); + + Self { + surface, + window, + device, + queue, + size, + config, + pipeline, + bind_group0, + bind_group1, + vertex_buffer, } - - pub fn resize(&mut self, new_size: winit::dpi::PhysicalSize) { - if new_size.width > 0 && new_size.height > 0 { - self.size = new_size; - self.config.width = new_size.width; - self.config.height = new_size.height; - self.surface.configure(&self.device, &self.config); - } + } + + pub fn resize(&mut self, new_size: winit::dpi::PhysicalSize) { + if new_size.width > 0 && new_size.height > 0 { + self.size = new_size; + self.config.width = new_size.width; + self.config.height = new_size.height; + self.surface.configure(&self.device, &self.config); } - - fn render(&mut self) -> Result<(), wgpu::SurfaceError> { - let output = self.surface.get_current_texture()?; - let output_view = output - .texture - .create_view(&wgpu::TextureViewDescriptor::default()); - - let mut encoder = self - .device - .create_command_encoder(&wgpu::CommandEncoderDescriptor { - label: Some("Render Encoder"), - }); - - let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor { - label: Some("Render Pass"), - color_attachments: &[Some(wgpu::RenderPassColorAttachment { - view: &output_view, - resolve_target: None, - ops: wgpu::Operations { - load: wgpu::LoadOp::Clear(wgpu::Color::BLACK), - store: wgpu::StoreOp::Store, - }, - })], - depth_stencil_attachment: None, - timestamp_writes: None, - occlusion_query_set: None, + } + + fn render(&mut self) -> Result<(), wgpu::SurfaceError> { + let output = self.surface.get_current_texture()?; + let output_view = output + .texture + .create_view(&wgpu::TextureViewDescriptor::default()); + + let mut encoder = + self + .device + .create_command_encoder(&wgpu::CommandEncoderDescriptor { + label: Some("Render Encoder"), }); - render_pass.set_pipeline(&self.pipeline); - - // Use this function to ensure all bind groups are set. - self.bind_group0.set(&mut render_pass); - self.bind_group1.set(&mut render_pass); - - render_pass.set_vertex_buffer(0, self.vertex_buffer.slice(..)); - render_pass.draw(0..3, 0..1); - - drop(render_pass); - self.queue.submit(iter::once(encoder.finish())); - - // Actually draw the frame. - output.present(); - - Ok(()) - } + let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor { + label: Some("Render Pass"), + color_attachments: &[Some(wgpu::RenderPassColorAttachment { + view: &output_view, + resolve_target: None, + ops: wgpu::Operations { + load: wgpu::LoadOp::Clear(wgpu::Color::BLACK), + store: wgpu::StoreOp::Store, + }, + })], + depth_stencil_attachment: None, + timestamp_writes: None, + occlusion_query_set: None, + }); + + render_pass.set_pipeline(&self.pipeline); + + // Use this function to ensure all bind groups are set. + self.bind_group0.set(&mut render_pass); + self.bind_group1.set(&mut render_pass); + + render_pass.set_vertex_buffer(0, self.vertex_buffer.slice(..)); + render_pass.draw(0..3, 0..1); + + drop(render_pass); + self.queue.submit(iter::once(encoder.finish())); + + // Actually draw the frame. + output.present(); + + Ok(()) + } } fn main() { - let event_loop = EventLoop::new().unwrap(); - let mut app = App { state: None }; - event_loop.run_app(&mut app).unwrap(); + let event_loop = EventLoop::new().unwrap(); + let mut app = App { state: None }; + event_loop.run_app(&mut app).unwrap(); } struct App { - state: Option, + state: Option, } impl ApplicationHandler<()> for App { - fn resumed(&mut self, event_loop: &winit::event_loop::ActiveEventLoop) { - if self.state.is_some() { - return; - } - - let window = event_loop - .create_window(Window::default_attributes().with_title("wgsl_to_wgpu")) - .unwrap(); - - self.state = Some(block_on(State::new(window))); + fn resumed(&mut self, event_loop: &winit::event_loop::ActiveEventLoop) { + if self.state.is_some() { + return; } - fn window_event( - &mut self, - event_loop: &winit::event_loop::ActiveEventLoop, - window_id: winit::window::WindowId, - event: WindowEvent, - ) { - if event == WindowEvent::CloseRequested { - event_loop.exit(); - return; - }; - - // Window specific event handling. - if let Some(state) = self.state.as_mut() { - if window_id != state.window.id() { - return; - } - - match event { - WindowEvent::Resized(physical_size) => { - state.resize(physical_size); - state.window.request_redraw(); - } - WindowEvent::ScaleFactorChanged { .. } => {} - WindowEvent::RedrawRequested => { - match state.render() { - Ok(_) => {} - Err(wgpu::SurfaceError::Lost) => state.resize(state.size), - Err(wgpu::SurfaceError::OutOfMemory) => event_loop.exit(), - Err(e) => eprintln!("{e:?}"), - } - state.window.request_redraw(); - } - _ => { - state.window.request_redraw(); - } - } + let window = event_loop + .create_window(Window::default_attributes().with_title("wgsl_to_wgpu")) + .unwrap(); + + self.state = Some(block_on(State::new(window))); + } + + fn window_event( + &mut self, + event_loop: &winit::event_loop::ActiveEventLoop, + window_id: winit::window::WindowId, + event: WindowEvent, + ) { + if event == WindowEvent::CloseRequested { + event_loop.exit(); + return; + }; + + // Window specific event handling. + if let Some(state) = self.state.as_mut() { + if window_id != state.window.id() { + return; + } + + match event { + WindowEvent::Resized(physical_size) => { + state.resize(physical_size); + state.window.request_redraw(); + } + WindowEvent::ScaleFactorChanged { .. } => {} + WindowEvent::RedrawRequested => { + match state.render() { + Ok(_) => {} + Err(wgpu::SurfaceError::Lost) => state.resize(state.size), + Err(wgpu::SurfaceError::OutOfMemory) => event_loop.exit(), + Err(e) => eprintln!("{e:?}"), + } + state.window.request_redraw(); + } + _ => { + state.window.request_redraw(); } + } } + } }