diff options
author | metamuffin <metamuffin@disroot.org> | 2025-01-22 00:23:58 +0100 |
---|---|---|
committer | metamuffin <metamuffin@disroot.org> | 2025-01-22 00:23:58 +0100 |
commit | 0612ce58890741428f10c73a63bcb417dcd43a9f (patch) | |
tree | a06a6d180722cc3adc35d6a8e30789773b56d508 /client | |
parent | 13eb6abfe0e766e432a2f08d58fa9f5a6c5026e7 (diff) | |
download | weareserver-0612ce58890741428f10c73a63bcb417dcd43a9f.tar weareserver-0612ce58890741428f10c73a63bcb417dcd43a9f.tar.bz2 weareserver-0612ce58890741428f10c73a63bcb417dcd43a9f.tar.zst |
generalize pipeline configuration in preparation for skinning
Diffstat (limited to 'client')
-rw-r--r-- | client/src/armature.rs | 21 | ||||
-rw-r--r-- | client/src/main.rs | 1 | ||||
-rw-r--r-- | client/src/renderer.rs | 5 | ||||
-rw-r--r-- | client/src/scene_prepare.rs | 158 | ||||
-rw-r--r-- | client/src/scene_render.rs | 142 | ||||
-rw-r--r-- | client/src/shaders/mod.rs | 21 | ||||
-rw-r--r-- | client/src/shaders/vertex_world_skin.wgsl | 4 |
7 files changed, 210 insertions, 142 deletions
diff --git a/client/src/armature.rs b/client/src/armature.rs index d955415..abfc455 100644 --- a/client/src/armature.rs +++ b/client/src/armature.rs @@ -1,13 +1,28 @@ -use std::sync::Arc; +/* + wearechat - generic multiplayer game with voip + Copyright (C) 2025 metamuffin + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published by + the Free Software Foundation, version 3 of the License only. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see <https://www.gnu.org/licenses/>. +*/ use glam::Mat4; +use std::sync::Arc; use weareshared::resources::Armature; use wgpu::{Buffer, BufferDescriptor, BufferUsages, Device, Queue}; pub struct RArmature { pub joint_mat_uniform_buffer: Arc<Buffer>, joint_mat: Vec<Mat4>, - data: Armature, + _data: Armature, } impl RArmature { @@ -19,7 +34,7 @@ impl RArmature { usage: BufferUsages::COPY_DST | BufferUsages::UNIFORM, mapped_at_creation: false, })), - data: armature, + _data: armature, joint_mat: vec![], } } diff --git a/client/src/main.rs b/client/src/main.rs index 66bd21f..e72e3b0 100644 --- a/client/src/main.rs +++ b/client/src/main.rs @@ -28,6 +28,7 @@ pub mod scene_render; pub mod state; pub mod ui; pub mod window; +pub mod shaders; use anyhow::Result; use clap::Parser; diff --git a/client/src/renderer.rs b/client/src/renderer.rs index e655420..c69f4bf 100644 --- a/client/src/renderer.rs +++ b/client/src/renderer.rs @@ -92,11 +92,10 @@ impl<'a> Renderer<'a> { let device = Arc::new(device); let queue = Arc::new(queue); - let (scene_pipeline, layouts) = ScenePipeline::new(&device, surface_configuration.format); let scene_prepare = Arc::new(ScenePreparer::new( device.clone(), queue.clone(), - layouts + surface_configuration.format, )); let ui_renderer = @@ -135,7 +134,7 @@ impl<'a> Renderer<'a> { let adapter_info = Arc::new(adapter.get_info()); Ok(Self { - scene_pipeline, + scene_pipeline: ScenePipeline, scene_prepare, surface, adapter_info, diff --git a/client/src/scene_prepare.rs b/client/src/scene_prepare.rs index 4e57e77..4b298a1 100644 --- a/client/src/scene_prepare.rs +++ b/client/src/scene_prepare.rs @@ -15,10 +15,11 @@ along with this program. If not, see <https://www.gnu.org/licenses/>. */ use crate::{ - armature::{self, RArmature}, + armature::RArmature, download::Downloader, meshops::{generate_normals, generate_tangents, generate_texcoords}, scene_render::SceneBgLayouts, + shaders::SceneShaders, }; use anyhow::Result; use bytemuck::{Pod, Zeroable}; @@ -38,12 +39,17 @@ use std::{ use weareshared::{ Affine3A, packets::Resource, - resources::{Armature, Image, MeshPart, Prefab}, + resources::{Image, MeshPart, Prefab}, }; use wgpu::{ AddressMode, BindGroup, BindGroupDescriptor, BindGroupEntry, BindGroupLayout, BindingResource, - Buffer, BufferUsages, Device, Extent3d, FilterMode, Queue, SamplerDescriptor, Texture, + BlendState, Buffer, BufferUsages, ColorTargetState, ColorWrites, CompareFunction, + DepthBiasState, DepthStencilState, Device, Extent3d, Face, FilterMode, FragmentState, + FrontFace, MultisampleState, PipelineCompilationOptions, PipelineLayoutDescriptor, PolygonMode, + PrimitiveState, PrimitiveTopology, PushConstantRange, Queue, RenderPipeline, + RenderPipelineDescriptor, SamplerDescriptor, ShaderStages, StencilState, Texture, TextureDescriptor, TextureDimension, TextureFormat, TextureUsages, TextureViewDescriptor, + VertexAttribute, VertexBufferLayout, VertexFormat, VertexState, VertexStepMode, util::{BufferInitDescriptor, DeviceExt, TextureDataOrder}, }; @@ -90,6 +96,8 @@ pub struct ScenePreparer { device: Arc<Device>, queue: Arc<Queue>, layouts: SceneBgLayouts, + shaders: SceneShaders, + render_format: TextureFormat, textures: DemandMap<(Resource<Image<'static>>, bool), (Arc<Texture>, Arc<BindGroup>)>, placeholder_textures: DemandMap<TextureIdentityKind, (Arc<Texture>, Arc<BindGroup>)>, @@ -100,11 +108,20 @@ pub struct ScenePreparer { generated_texcoord_buffers: DemandMap<TexcoordBufferSpec, Arc<Buffer>>, mesh_parts: DemandMap<Resource<MeshPart>, Arc<RMeshPart>>, materials: DemandMap<Material, Arc<BindGroup>>, + pipelines: DemandMap<PipelineConfig, Arc<RenderPipeline>>, pub prefabs: DemandMap<Resource<Prefab>, Arc<RPrefab>>, } +#[derive(Debug, Clone, Hash, PartialEq, Eq)] +pub struct PipelineConfig { + format: TextureFormat, + skin: bool, + backface_culling: bool, +} + pub struct RPrefab(pub Vec<(Affine3A, Arc<RMeshPart>)>); pub struct RMeshPart { + pub pipeline: Arc<RenderPipeline>, pub index_count: u32, pub index: Arc<Buffer>, pub va_position: Arc<Buffer>, @@ -157,11 +174,13 @@ struct Material { } impl ScenePreparer { - pub fn new(device: Arc<Device>, queue: Arc<Queue>, layouts: SceneBgLayouts) -> Self { + pub fn new(device: Arc<Device>, queue: Arc<Queue>, render_format: TextureFormat) -> Self { Self { + render_format, + layouts: SceneBgLayouts::load(&device), + shaders: SceneShaders::load(&device), device, queue, - layouts, index_buffers: DemandMap::new(), vertex_buffers: DemandMap::new(), mesh_parts: DemandMap::new(), @@ -172,6 +191,7 @@ impl ScenePreparer { generated_normal_buffers: DemandMap::new(), generated_texcoord_buffers: DemandMap::new(), materials: DemandMap::new(), + pipelines: DemandMap::new(), } } pub fn update(&self, dls: &Downloader) -> Result<usize> { @@ -347,7 +367,7 @@ impl ScenePreparer { }); let bind_group = self.device.create_bind_group(&BindGroupDescriptor { label: Some("material"), - layout: &self.material_bgl, + layout: &self.layouts.material, entries: &[BindGroupEntry { binding: 0, resource: buffer.as_entire_binding(), @@ -355,6 +375,13 @@ impl ScenePreparer { }); self.materials.insert(spec, Arc::new(bind_group), 0); } + for spec in self.pipelines.needed() { + self.pipelines.insert( + spec.clone(), + Arc::new(spec.create(&self.device, &self.layouts, &self.shaders)), + 0, + ); + } for pres in self.mesh_parts.needed() { let start = Instant::now(); if let Some(part) = dls.try_get(pres.clone())? { @@ -461,7 +488,14 @@ impl ScenePreparer { Some(None) }; + let pipeline = self.pipelines.try_get(PipelineConfig { + format: self.render_format, + skin: false, + backface_culling: part.g_double_sided.is_none(), + }); + if let ( + Some(pipeline), Some((index, index_count)), Some(va_normal), Some(va_tangent), @@ -474,6 +508,7 @@ impl ScenePreparer { Some(tex_albedo), Some(material), ) = ( + pipeline, index, normal, tangent, @@ -499,6 +534,7 @@ impl ScenePreparer { self.mesh_parts.insert( pres, Arc::new(RMeshPart { + pipeline, index_count, index, va_normal, @@ -582,6 +618,110 @@ fn create_texture( (Arc::new(texture), Arc::new(bindgroup)) } +impl PipelineConfig { + pub fn create( + &self, + device: &Device, + layouts: &SceneBgLayouts, + shaders: &SceneShaders, + ) -> RenderPipeline { + let pipeline_layout = device.create_pipeline_layout(&PipelineLayoutDescriptor { + label: None, + bind_group_layouts: &[&layouts.texture, &layouts.texture, &layouts.material], + push_constant_ranges: &[PushConstantRange { + range: 0..((4 * 4 + 3 * 4) * size_of::<f32>() as u32), + stages: ShaderStages::VERTEX, + }], + }); + device.create_render_pipeline(&RenderPipelineDescriptor { + label: None, + layout: Some(&pipeline_layout), + fragment: Some(FragmentState { + module: &shaders.fragment_pbr, + entry_point: Some("main"), + targets: &[Some(ColorTargetState { + blend: Some(BlendState::PREMULTIPLIED_ALPHA_BLENDING), + format: self.format, + write_mask: ColorWrites::all(), + })], + compilation_options: PipelineCompilationOptions::default(), + }), + vertex: VertexState { + module: if self.skin { + &shaders.vertex_world_skin + } else { + &shaders.vertex_world + }, + entry_point: Some("main"), + buffers: &[ + // position + VertexBufferLayout { + step_mode: VertexStepMode::Vertex, + array_stride: 3 * size_of::<f32>() as u64, + attributes: &[VertexAttribute { + format: VertexFormat::Float32x3, + offset: 0, + shader_location: 0, + }], + }, + // normal + VertexBufferLayout { + step_mode: VertexStepMode::Vertex, + array_stride: 3 * size_of::<f32>() as u64, + attributes: &[VertexAttribute { + format: VertexFormat::Float32x3, + offset: 0, + shader_location: 1, + }], + }, + // tangent + VertexBufferLayout { + step_mode: VertexStepMode::Vertex, + array_stride: 3 * size_of::<f32>() as u64, + attributes: &[VertexAttribute { + format: VertexFormat::Float32x3, + offset: 0, + shader_location: 2, + }], + }, + // texcoord + VertexBufferLayout { + step_mode: VertexStepMode::Vertex, + array_stride: 2 * size_of::<f32>() as u64, + attributes: &[VertexAttribute { + format: VertexFormat::Float32x2, + offset: 0, + shader_location: 3, + }], + }, + ], + compilation_options: PipelineCompilationOptions::default(), + }, + primitive: PrimitiveState { + topology: PrimitiveTopology::TriangleList, + front_face: FrontFace::Ccw, + cull_mode: if self.backface_culling { + Some(Face::Back) + } else { + None + }, + polygon_mode: PolygonMode::Fill, + ..Default::default() + }, + depth_stencil: Some(DepthStencilState { + depth_write_enabled: true, + depth_compare: CompareFunction::Less, + format: TextureFormat::Depth32Float, + bias: DepthBiasState::default(), + stencil: StencilState::default(), + }), + multisample: MultisampleState::default(), + multiview: None, + cache: None, + }) + } +} + impl<K, V> Widget for &DemandMap<K, V> { fn ui(self, ui: &mut egui::Ui) -> egui::Response { let state = self.inner.read().unwrap(); @@ -618,6 +758,8 @@ impl ScenePreparer { &self.generated_texcoord_buffers, ); visit("textures", &self.textures); + visit("materials", &self.materials); + visit("pipelines", &self.pipelines); } } @@ -644,6 +786,10 @@ impl Widget for &ScenePreparer { self.generated_texcoord_buffers.ui(ui); ui.label("textures"); self.textures.ui(ui); + ui.label("materials"); + self.materials.ui(ui); + ui.label("pipelines"); + self.pipelines.ui(ui); }) .response } diff --git a/client/src/scene_render.rs b/client/src/scene_render.rs index 77217d6..d621ba6 100644 --- a/client/src/scene_render.rs +++ b/client/src/scene_render.rs @@ -14,42 +14,28 @@ You should have received a copy of the GNU Affero General Public License along with this program. If not, see <https://www.gnu.org/licenses/>. */ +use crate::scene_prepare::{DemandMap, RPrefab}; use glam::{EulerRot, Mat3, Mat4}; use std::sync::Arc; use weareshared::{packets::Resource, resources::Prefab, tree::SceneTree}; use wgpu::{ - BindGroupLayout, BindGroupLayoutDescriptor, BindGroupLayoutEntry, BindingType, BlendState, - BufferBindingType, Color, ColorTargetState, ColorWrites, CommandEncoder, CompareFunction, - DepthBiasState, DepthStencilState, Device, Face, FragmentState, FrontFace, IndexFormat, LoadOp, - MultisampleState, Operations, PipelineCompilationOptions, PipelineLayoutDescriptor, - PolygonMode, PrimitiveState, PrimitiveTopology, PushConstantRange, RenderPassColorAttachment, - RenderPassDepthStencilAttachment, RenderPassDescriptor, RenderPipeline, - RenderPipelineDescriptor, SamplerBindingType, ShaderStages, StencilState, StoreOp, - TextureFormat, TextureSampleType, TextureView, TextureViewDimension, VertexAttribute, - VertexBufferLayout, VertexFormat, VertexState, VertexStepMode, include_wgsl, + BindGroupLayout, BindGroupLayoutDescriptor, BindGroupLayoutEntry, BindingType, + BufferBindingType, Color, CommandEncoder, Device, IndexFormat, LoadOp, Operations, + RenderPassColorAttachment, RenderPassDepthStencilAttachment, RenderPassDescriptor, + SamplerBindingType, ShaderStages, StoreOp, TextureSampleType, TextureView, + TextureViewDimension, }; -use crate::scene_prepare::{DemandMap, RPrefab}; - -pub struct ScenePipeline { - pipeline: RenderPipeline, - pipeline_no_cull: RenderPipeline, -} +pub struct ScenePipeline; pub struct SceneBgLayouts { pub texture: BindGroupLayout, pub material: BindGroupLayout, pub joints: BindGroupLayout, } - -impl ScenePipeline { - pub fn new(device: &Device, format: TextureFormat) -> (Self, SceneBgLayouts) { - let fragment_pbr = device.create_shader_module(include_wgsl!("shaders/fragment_pbr.wgsl")); - let vertex_world = device.create_shader_module(include_wgsl!("shaders/vertex_world.wgsl")); - let _vertex_world_skin = - device.create_shader_module(include_wgsl!("shaders/vertex_world_skin.wgsl")); - - let layouts = SceneBgLayouts { +impl SceneBgLayouts { + pub fn load(device: &Device) -> Self { + Self { texture: device.create_bind_group_layout(&BindGroupLayoutDescriptor { entries: &[ BindGroupLayoutEntry { @@ -97,105 +83,11 @@ impl ScenePipeline { }], label: None, }), - }; - - let pipeline_layout = device.create_pipeline_layout(&PipelineLayoutDescriptor { - label: None, - bind_group_layouts: &[&layouts.texture, &layouts.texture, &layouts.material], - push_constant_ranges: &[PushConstantRange { - range: 0..((4 * 4 + 3 * 4) * size_of::<f32>() as u32), - stages: ShaderStages::VERTEX, - }], - }); - let [pipeline, pipeline_no_cull] = [Some(Face::Back), None].map(|cull_mode| { - device.create_render_pipeline(&RenderPipelineDescriptor { - label: None, - layout: Some(&pipeline_layout), - fragment: Some(FragmentState { - module: &fragment_pbr, - entry_point: Some("main"), - targets: &[Some(ColorTargetState { - blend: Some(BlendState::PREMULTIPLIED_ALPHA_BLENDING), - format, - write_mask: ColorWrites::all(), - })], - compilation_options: PipelineCompilationOptions::default(), - }), - vertex: VertexState { - module: &vertex_world, - entry_point: Some("main"), - buffers: &[ - // position - VertexBufferLayout { - step_mode: VertexStepMode::Vertex, - array_stride: 3 * size_of::<f32>() as u64, - attributes: &[VertexAttribute { - format: VertexFormat::Float32x3, - offset: 0, - shader_location: 0, - }], - }, - // normal - VertexBufferLayout { - step_mode: VertexStepMode::Vertex, - array_stride: 3 * size_of::<f32>() as u64, - attributes: &[VertexAttribute { - format: VertexFormat::Float32x3, - offset: 0, - shader_location: 1, - }], - }, - // tangent - VertexBufferLayout { - step_mode: VertexStepMode::Vertex, - array_stride: 3 * size_of::<f32>() as u64, - attributes: &[VertexAttribute { - format: VertexFormat::Float32x3, - offset: 0, - shader_location: 2, - }], - }, - // texcoord - VertexBufferLayout { - step_mode: VertexStepMode::Vertex, - array_stride: 2 * size_of::<f32>() as u64, - attributes: &[VertexAttribute { - format: VertexFormat::Float32x2, - offset: 0, - shader_location: 3, - }], - }, - ], - compilation_options: PipelineCompilationOptions::default(), - }, - primitive: PrimitiveState { - topology: PrimitiveTopology::TriangleList, - front_face: FrontFace::Ccw, - cull_mode, - polygon_mode: PolygonMode::Fill, - ..Default::default() - }, - depth_stencil: Some(DepthStencilState { - depth_write_enabled: true, - depth_compare: CompareFunction::Less, - format: TextureFormat::Depth32Float, - bias: DepthBiasState::default(), - stencil: StencilState::default(), - }), - multisample: MultisampleState::default(), - multiview: None, - cache: None, - }) - }); - ( - Self { - pipeline, - pipeline_no_cull, - }, - layouts, - ) + } } +} +impl ScenePipeline { pub fn draw( &mut self, commands: &mut CommandEncoder, @@ -255,13 +147,7 @@ impl ScenePipeline { ]; let model_basis = bytemuck::cast_slice(&model_basis); - let pipeline = if part.double_sided { - &self.pipeline_no_cull - } else { - &self.pipeline - }; - - rpass.set_pipeline(pipeline); + rpass.set_pipeline(&part.pipeline); rpass.set_bind_group(0, &*part.tex_albedo, &[]); rpass.set_bind_group(1, &*part.tex_normal, &[]); rpass.set_bind_group(2, &*part.material, &[]); diff --git a/client/src/shaders/mod.rs b/client/src/shaders/mod.rs new file mode 100644 index 0000000..42b2164 --- /dev/null +++ b/client/src/shaders/mod.rs @@ -0,0 +1,21 @@ +use log::info; +use wgpu::{Device, ShaderModule, include_wgsl}; + +pub struct SceneShaders { + pub fragment_pbr: ShaderModule, + pub vertex_world: ShaderModule, + pub vertex_world_skin: ShaderModule, +} + +impl SceneShaders { + pub fn load(device: &Device) -> Self { + info!("compiling shaders..."); + let s = Self { + fragment_pbr: device.create_shader_module(include_wgsl!("fragment_pbr.wgsl")), + vertex_world: device.create_shader_module(include_wgsl!("vertex_world.wgsl")), + vertex_world_skin: device.create_shader_module(include_wgsl!("vertex_world_skin.wgsl")), + }; + info!("done"); + s + } +} diff --git a/client/src/shaders/vertex_world_skin.wgsl b/client/src/shaders/vertex_world_skin.wgsl index 86250f1..288950d 100644 --- a/client/src/shaders/vertex_world_skin.wgsl +++ b/client/src/shaders/vertex_world_skin.wgsl @@ -33,8 +33,8 @@ struct PushConst { model_basis: mat3x3<f32>, } -var<uniform> joints: array<mat4x4<f32>>; -var<push_constant> pc: PushConst; +@group(3) @binding(0) var<uniform> joints: array<mat4x4<f32>, 128>; +var<push_constant> pc: PushConst; @vertex fn main(vi: VertexIn) -> VertexOut { |