From b5ced3af46c96ceb959fbbf1addfeba3bd4f76d5 Mon Sep 17 00:00:00 2001 From: Jon Santmyer Date: Wed, 15 Apr 2026 16:53:58 -0400 Subject: first commit. working body rendering --- src/wgpuctx/mod.rs | 245 ++++++++++++++++++++++++++++++++++++++++++++++++ src/wgpuctx/pipeline.rs | 121 ++++++++++++++++++++++++ 2 files changed, 366 insertions(+) create mode 100644 src/wgpuctx/mod.rs create mode 100644 src/wgpuctx/pipeline.rs (limited to 'src/wgpuctx') diff --git a/src/wgpuctx/mod.rs b/src/wgpuctx/mod.rs new file mode 100644 index 0000000..f8aa3ec --- /dev/null +++ b/src/wgpuctx/mod.rs @@ -0,0 +1,245 @@ +use std::sync::Arc; +use std::error::Error; +use wgpu::util::DeviceExt; +use winit::window::{Window}; + +use crate::texture::Texture; + +pub mod pipeline; + +pub struct WgpuCtx +{ + surface: wgpu::Surface<'static>, + surface_conf: wgpu::SurfaceConfiguration, + surface_texture: Option, + is_configured: bool, + + adapter: wgpu::Adapter, + device: wgpu::Device, + queue: wgpu::Queue, +} + +pub struct RenderPassBuilder<'encoder> +{ + label: &'static str, + view: &'encoder wgpu::TextureView, + clear_color: wgpu::Color +} + +impl WgpuCtx +{ + pub async fn new( + instance: &wgpu::Instance, + window: Arc, + ) -> Self { + let surface = instance.create_surface(window.clone()).unwrap(); + + let adapter = instance + .request_adapter(&wgpu::RequestAdapterOptions { + compatible_surface: Some(&surface), + ..Default::default() + }).await.expect("Failed to find valid adapter"); + + let (device, queue) = adapter + .request_device(&wgpu::DeviceDescriptor { + label: None, + required_features: wgpu::Features::empty(), + required_limits: wgpu::Limits::downlevel_defaults(), + experimental_features: wgpu::ExperimentalFeatures::disabled(), + memory_hints: wgpu::MemoryHints::Performance, + trace: wgpu::Trace::Off + }).await.expect("Failed to find device and queue"); + + let size = window.inner_size(); + let surface_config = surface.get_default_config(&adapter, size.width, size.height).unwrap(); + + Self { + surface: surface, + surface_conf: surface_config, + surface_texture: None, + is_configured: false, + adapter: adapter, + device: device, + queue: queue + } + } + + pub fn resize( + &mut self, + width: u32, + height: u32) + { + self.surface_conf.width = width; + self.surface_conf.height = height; + self.surface.configure(&self.device, &self.surface_conf); + self.is_configured = true; + } + + pub fn is_ready( + &self) + -> bool { + self.is_configured + } + + pub fn surface_config( + &self) + -> wgpu::SurfaceConfiguration { + self.surface_conf.clone() + } + + pub fn adapter( + &self) + -> &wgpu::Adapter { + &self.adapter + } + + pub fn device( + &self) + -> &wgpu::Device { + &self.device + } + + pub fn queue( + &self) + -> &wgpu::Queue { + &self.queue + } + + pub fn prepare_surface( + &mut self, + view_descr: &wgpu::TextureViewDescriptor) + -> Result + { + let texture = self.surface.get_current_texture()?; + let view = texture.texture.create_view(view_descr); + + self.surface_texture = Some(texture); + Ok(view) + } + + pub fn create_encoder( + &self, + descr: &wgpu::CommandEncoderDescriptor) + -> wgpu::CommandEncoder + { + self.device.create_command_encoder(descr) + } + + pub fn create_default_encoder( + &self, + label: &'static str) + -> wgpu::CommandEncoder { + self.create_encoder( + &wgpu::CommandEncoderDescriptor { + label: Some(label) + } + ) + } + + pub fn submit_encoder( + &self, + encoder: wgpu::CommandEncoder) + { + self.queue.submit(std::iter::once(encoder.finish())); + } + + pub fn present_surface( + &mut self) + { + let texture = self.surface_texture.take(); + match texture { + Some(t) => t.present(), + None => {} + } + } + + pub fn create_shader( + &self, + module_descr: wgpu::ShaderModuleDescriptor) + -> wgpu::ShaderModule { + self.device.create_shader_module(module_descr) + } + + pub fn create_pipeline_layout( + &self, + layout_descr: &wgpu::PipelineLayoutDescriptor) + -> wgpu::PipelineLayout { + self.device.create_pipeline_layout(layout_descr) + } + + pub fn create_render_pipeline( + &self, + pipeline_layout: &wgpu::RenderPipelineDescriptor) + -> wgpu::RenderPipeline { + self.device.create_render_pipeline(pipeline_layout) + } + + pub fn create_buffer_init( + &self, + descr: &wgpu::util::BufferInitDescriptor + ) -> wgpu::Buffer { + self.device.create_buffer_init(descr) + } + + pub fn create_buffer( + &self, + descr: &wgpu::BufferDescriptor) + -> wgpu::Buffer { + self.device.create_buffer(descr) + } + + pub fn new_texture( + &self, + descr: wgpu::TextureDescriptor) + -> Texture + { + Texture::new(&self.device, descr) + } + +} //impl WgpuCtx + +impl<'encoder> RenderPassBuilder<'encoder> +{ + pub fn new( + label: &'static str, + view: &'encoder wgpu::TextureView) + -> Self { + Self { + label: label, + view: view, + clear_color: wgpu::Color { r: 0.0, g: 0.0, b: 0.0, a: 1.0 } + } + } + + pub fn clear_color( + mut self, + color: wgpu::Color) + -> Self { + self.clear_color = color; + self + } + + pub fn build( + self, + encoder: &'encoder mut wgpu::CommandEncoder) + -> wgpu::RenderPass<'encoder> + { + encoder.begin_render_pass( + &wgpu::RenderPassDescriptor { + label: Some(self.label), + color_attachments: &[Some(wgpu::RenderPassColorAttachment { + view: self.view, + resolve_target: None, + depth_slice: None, + ops: wgpu::Operations { + load: wgpu::LoadOp::Clear(self.clear_color), + store: wgpu::StoreOp::Store + } + })], + depth_stencil_attachment: None, + occlusion_query_set: None, + timestamp_writes: None, + } + ) + } +} diff --git a/src/wgpuctx/pipeline.rs b/src/wgpuctx/pipeline.rs new file mode 100644 index 0000000..7606203 --- /dev/null +++ b/src/wgpuctx/pipeline.rs @@ -0,0 +1,121 @@ + +use crate::wgpuctx::WgpuCtx; + +pub struct RenderPipelineBuilder<'a> +{ + bind_groups: Vec<&'a wgpu::BindGroupLayout>, + shader: &'a wgpu::ShaderModule, + + vertex_entry_point: Option<&'static str>, + fragment_entry_point: Option<&'static str>, + + vertex_comp_options: Option>, + fragment_comp_options: Option>, + + vertex_buffer_layouts: Vec> +} + +impl<'a> RenderPipelineBuilder<'a> +{ + pub fn new( + shader: &'a wgpu::ShaderModule) + -> Self { + Self { + bind_groups: Vec::new(), + shader: shader, + + vertex_entry_point: Some("vs_main"), + fragment_entry_point: Some("fs_main"), + + vertex_comp_options: None, + fragment_comp_options: None, + + vertex_buffer_layouts: Vec::new() + } + } + + pub fn add_bindgroup( + mut self, + bindgroup: &'a wgpu::BindGroupLayout) + -> Self { + self.bind_groups.push(bindgroup); + self + } + + pub fn add_vertex_layout( + mut self, + layout: wgpu::VertexBufferLayout<'a>) + -> Self { + self.vertex_buffer_layouts.push(layout); + self + } + + pub fn build( + self, + label: Option<&'static str>, + wgpuctx: &WgpuCtx) + -> wgpu::RenderPipeline + { + let layout_descr = wgpu::PipelineLayoutDescriptor { + label: label, + bind_group_layouts: self.bind_groups.as_slice(), + push_constant_ranges: &[] + }; + + let layout = wgpuctx.create_pipeline_layout(&layout_descr); + + wgpuctx.create_render_pipeline( + &wgpu::RenderPipelineDescriptor { + label: label, + layout: Some(&layout), + vertex: wgpu::VertexState { + module: self.shader, + entry_point: self.vertex_entry_point, + compilation_options: match self.vertex_comp_options { + Some(option) => option, + None => wgpu::PipelineCompilationOptions::default() + }, + buffers: self.vertex_buffer_layouts.as_slice() + }, + fragment: Some(wgpu::FragmentState { + module: self.shader, + entry_point: self.fragment_entry_point, + compilation_options: match self.fragment_comp_options { + Some(option) => option, + None => wgpu::PipelineCompilationOptions::default() + }, + targets: &[Some(wgpu::ColorTargetState { + format: wgpuctx.surface_config().format, + blend: Some(wgpu::BlendState{ + color: wgpu::BlendComponent { + src_factor: wgpu::BlendFactor::SrcAlpha, + dst_factor: wgpu::BlendFactor::OneMinusSrcAlpha, + operation: wgpu::BlendOperation::Add + }, + alpha: wgpu::BlendComponent::OVER + }), + write_mask: wgpu::ColorWrites::ALL + })], + }), + 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 + }, + multiview: None, + cache: None + + } + ) + } +} -- cgit v1.2.3