cvkg_render_gpu/passes/
skinning.rs1use crate::kvasir::nodes::PassId;
5use crate::kvasir::{ExecutionContext, KvasirNode, ResourceId};
6
7pub struct SkinningNode {
9 pub inputs: Vec<ResourceId>,
11 pub outputs: Vec<ResourceId>,
13}
14
15impl KvasirNode for SkinningNode {
16 fn label(&self) -> &'static str {
17 "SkinningComputePass"
18 }
19
20 fn inputs(&self) -> &[ResourceId] {
21 &self.inputs
22 }
23
24 fn outputs(&self) -> &[ResourceId] {
25 &self.outputs
26 }
27
28 fn pass_id(&self) -> PassId {
29 PassId::ComputeSkinning
30 }
31
32 fn execute(&self, ctx: &mut ExecutionContext) {
33 if ctx.renderer.skinning_buffer_pairs.is_empty() {
34 return;
35 }
36
37 let total_vertices: u32 = ctx.renderer.skinning_buffer_pairs.iter()
38 .map(|(src, _)| {
39 (src.size() as usize / std::mem::size_of::<crate::vertex::SkinnedVertex>()) as u32
41 })
42 .sum();
43
44 tracing::debug!(
45 "SkinningNode::execute - Dispatching GPU skinning compute shader for {} meshes ({} total vertices)",
46 ctx.renderer.skinning_buffer_pairs.len(),
47 total_vertices
48 );
49
50 let bind_group_1 = ctx.device.create_bind_group(&wgpu::BindGroupDescriptor {
52 label: Some("Skinning Bind Group 1 (Joints)"),
53 layout: &ctx.renderer.skinning_bgl1,
54 entries: &[
55 wgpu::BindGroupEntry {
56 binding: 0,
57 resource: ctx.renderer.skinning_joint_matrices.as_entire_binding(),
58 },
59 ],
60 });
61
62 let bind_group_2 = ctx.device.create_bind_group(&wgpu::BindGroupDescriptor {
63 label: Some("Skinning Bind Group 2 (Morphs)"),
64 layout: &ctx.renderer.skinning_bgl2,
65 entries: &[
66 wgpu::BindGroupEntry {
67 binding: 0,
68 resource: ctx.renderer.skinning_morph_positions.as_entire_binding(),
69 },
70 wgpu::BindGroupEntry {
71 binding: 1,
72 resource: ctx.renderer.skinning_morph_weights.as_entire_binding(),
73 },
74 ],
75 });
76
77 let mut compute_pass = ctx.encoder.begin_compute_pass(&wgpu::ComputePassDescriptor {
79 label: Some("Skeletal Skinning Compute Pass"),
80 timestamp_writes: None,
81 });
82
83 compute_pass.set_pipeline(&ctx.renderer.skinning_compute_pipeline);
84 compute_pass.set_bind_group(1, &bind_group_1, &[]);
85 compute_pass.set_bind_group(2, &bind_group_2, &[]);
86
87 for (src_buffer, dst_buffer) in ctx.renderer.skinning_buffer_pairs.iter() {
88 let vertex_count = (src_buffer.size() as usize / std::mem::size_of::<crate::vertex::SkinnedVertex>()) as u32;
89 if vertex_count == 0 {
90 continue;
91 }
92
93 let bind_group_0 = ctx.device.create_bind_group(&wgpu::BindGroupDescriptor {
95 label: Some("Skinning Bind Group 0 (Geometry)"),
96 layout: &ctx.renderer.skinning_bgl0,
97 entries: &[
98 wgpu::BindGroupEntry {
99 binding: 0,
100 resource: src_buffer.as_entire_binding(),
101 },
102 wgpu::BindGroupEntry {
103 binding: 1,
104 resource: dst_buffer.as_entire_binding(),
105 },
106 ],
107 });
108
109 compute_pass.set_bind_group(0, &bind_group_0, &[]);
110
111 let workgroup_count = (vertex_count + 63) / 64;
112 compute_pass.dispatch_workgroups(workgroup_count, 1, 1);
113 }
114
115 }
118}