use crate::{ render::{data::PrimitiveInstance, util::ArrBuf}, UI, }; use data::WindowUniform; use wgpu::{ util::{BufferInitDescriptor, DeviceExt}, *, }; use winit::dpi::PhysicalSize; mod data; pub mod primitive; mod util; const SHAPE_SHADER: &str = include_str!("./shader.wgsl"); pub struct UIRenderNode { bind_group_layout: BindGroupLayout, bind_group: BindGroup, pipeline: RenderPipeline, window_buffer: Buffer, instance: ArrBuf, data: ArrBuf, } impl UIRenderNode { pub fn draw<'a>(&'a self, pass: &mut RenderPass<'a>) { pass.set_pipeline(&self.pipeline); pass.set_bind_group(0, &self.bind_group, &[]); if self.instance.len() != 0 { pass.set_vertex_buffer(0, self.instance.buffer.slice(..)); pass.draw(0..4, 0..self.instance.len() as u32); } } pub fn update(&mut self, device: &Device, queue: &Queue, ui: &UI) { let primitives = ui.to_primitives(); self.instance.update(device, queue, &primitives.instances); self.data.update(device, queue, &primitives.data); self.bind_group = Self::bind_group( device, &self.bind_group_layout, &self.window_buffer, &self.data.buffer, ) } pub fn resize(&mut self, size: &PhysicalSize, queue: &Queue) { let slice = &[WindowUniform { width: size.width as f32, height: size.height as f32, }]; queue.write_buffer(&self.window_buffer, 0, bytemuck::cast_slice(slice)); } pub fn new(device: &Device, config: &SurfaceConfiguration) -> Self { let shader = device.create_shader_module(ShaderModuleDescriptor { label: Some("UI Shape Shader"), source: ShaderSource::Wgsl(SHAPE_SHADER.into()), }); let window_uniform = WindowUniform::default(); let window_buffer = device.create_buffer_init(&BufferInitDescriptor { label: Some("Camera Buffer"), contents: bytemuck::cast_slice(&[window_uniform]), usage: BufferUsages::UNIFORM | BufferUsages::COPY_DST, }); let instance = ArrBuf::new( device, BufferUsages::VERTEX | BufferUsages::COPY_DST, "instance", ); let data = ArrBuf::new( device, BufferUsages::STORAGE | BufferUsages::COPY_DST, "data", ); let bind_group_layout = device.create_bind_group_layout(&BindGroupLayoutDescriptor { entries: &[ BindGroupLayoutEntry { binding: 0, visibility: ShaderStages::VERTEX, ty: BindingType::Buffer { ty: BufferBindingType::Uniform, has_dynamic_offset: false, min_binding_size: None, }, count: None, }, BindGroupLayoutEntry { binding: 1, visibility: ShaderStages::FRAGMENT, ty: BindingType::Buffer { ty: BufferBindingType::Storage { read_only: true }, has_dynamic_offset: false, min_binding_size: None, }, count: None, }, ], label: Some("camera_bind_group_layout"), }); let bind_group = Self::bind_group(device, &bind_group_layout, &window_buffer, &data.buffer); let pipeline_layout = device.create_pipeline_layout(&PipelineLayoutDescriptor { label: Some("UI Shape Pipeline Layout"), bind_group_layouts: &[&bind_group_layout], push_constant_ranges: &[], }); let pipeline = device.create_render_pipeline(&RenderPipelineDescriptor { label: Some("UI Shape Pipeline"), layout: Some(&pipeline_layout), vertex: VertexState { module: &shader, entry_point: Some("vs_main"), buffers: &[PrimitiveInstance::desc()], compilation_options: Default::default(), }, fragment: Some(FragmentState { module: &shader, entry_point: Some("fs_main"), targets: &[Some(ColorTargetState { format: config.format, blend: Some(BlendState::ALPHA_BLENDING), write_mask: ColorWrites::ALL, })], compilation_options: Default::default(), }), primitive: PrimitiveState { topology: PrimitiveTopology::TriangleStrip, strip_index_format: None, front_face: FrontFace::Cw, cull_mode: Some(Face::Back), polygon_mode: PolygonMode::Fill, unclipped_depth: false, conservative: false, }, depth_stencil: None, multisample: MultisampleState { count: 1, mask: !0, alpha_to_coverage_enabled: false, }, multiview: None, cache: None, }); Self { bind_group_layout, bind_group, pipeline, window_buffer, instance, data, } } pub fn bind_group( device: &Device, layout: &BindGroupLayout, window_buffer: &Buffer, data: &Buffer, ) -> BindGroup { device.create_bind_group(&BindGroupDescriptor { layout, entries: &[ BindGroupEntry { binding: 0, resource: window_buffer.as_entire_binding(), }, BindGroupEntry { binding: 1, resource: data.as_entire_binding(), }, ], label: Some("ui_bind_group"), }) } }