Skip to main content

myth_render/core/gpu/
geometry.rs

1//! Geometry operations
2
3use std::ops::Range;
4
5use crate::core::gpu::generate_gpu_resource_id;
6use crate::pipeline::vertex::GeneratedVertexLayout;
7use myth_assets::{AssetServer, GeometryHandle};
8use myth_resources::geometry::Geometry;
9
10use super::ResourceManager;
11
12/// GPU-side geometry resource
13///
14/// Vertex Buffer IDs are used for Pipeline cache validation and do not affect `BindGroup`
15pub struct GpuGeometry {
16    pub layout_info: GeneratedVertexLayout,
17    pub layout_id: u64,
18    pub vertex_buffers: Vec<wgpu::Buffer>,
19    pub vertex_buffer_ids: Vec<u64>,
20    pub index_buffer: Option<(wgpu::Buffer, wgpu::IndexFormat, u32, u64)>,
21    pub draw_range: Range<u32>,
22    pub instance_range: Range<u32>,
23    pub version: u64,
24    pub last_data_version: u64,
25    pub last_used_frame: u64,
26}
27
28/// Geometry preparation result
29///
30/// Vertex Buffer IDs are used for Pipeline cache validation and do not affect Object `BindGroup`
31#[derive(Debug, Clone)]
32#[allow(dead_code)]
33pub struct GeometryPrepareResult {
34    pub vertex_buffer_ids: Vec<u64>,
35    pub index_buffer_id: Option<u64>,
36    pub any_recreated: bool,
37}
38
39impl ResourceManager {
40    /// Prepare Geometry GPU resources
41    ///
42    /// Returns GeometryPrepareResult containing all physical resource IDs
43    pub(crate) fn prepare_geometry(
44        &mut self,
45        assets: &AssetServer,
46        handle: GeometryHandle,
47    ) -> Option<GeometryPrepareResult> {
48        let geometry = assets.geometries.get(handle)?;
49
50        // Fast path: check if any update is needed
51        if let Some(gpu_geo) = self.gpu_geometries.get_mut(handle)
52            && geometry.structure_version() == gpu_geo.version
53            && geometry.data_version() == gpu_geo.last_data_version
54        {
55            gpu_geo.last_used_frame = self.frame_index;
56            return Some(GeometryPrepareResult {
57                vertex_buffer_ids: gpu_geo.vertex_buffer_ids.clone(),
58                index_buffer_id: gpu_geo.index_buffer.as_ref().map(|(_, _, _, id)| *id),
59                any_recreated: false,
60            });
61        }
62
63        // Ensure all attribute buffers
64        let mut any_buffer_recreated = false;
65        let mut new_vertex_ids = Vec::new();
66
67        for attr in geometry.attributes().values() {
68            let result = self.prepare_attribute(attr);
69            new_vertex_ids.push(result.resource_id);
70            if result.was_recreated {
71                any_buffer_recreated = true;
72            }
73        }
74
75        let mut new_index_id = None;
76        if let Some(indices) = geometry.index_attribute() {
77            let result = self.prepare_index(indices);
78            new_index_id = Some(result.resource_id);
79            if result.was_recreated {
80                any_buffer_recreated = true;
81            }
82        }
83
84        // Check if GpuGeometry needs to be rebuilt
85        let needs_rebuild = if let Some(gpu_geo) = self.gpu_geometries.get(handle) {
86            geometry.structure_version() > gpu_geo.version
87                || any_buffer_recreated
88                || gpu_geo.vertex_buffer_ids != new_vertex_ids
89        } else {
90            true
91        };
92
93        if needs_rebuild {
94            self.create_gpu_geometry(&geometry, handle);
95        } else {
96            // Only update the data version
97            if let Some(gpu_geo) = self.gpu_geometries.get_mut(handle) {
98                gpu_geo.last_data_version = geometry.data_version();
99            }
100        }
101
102        if let Some(gpu_geo) = self.gpu_geometries.get_mut(handle) {
103            gpu_geo.last_used_frame = self.frame_index;
104            Some(GeometryPrepareResult {
105                vertex_buffer_ids: gpu_geo.vertex_buffer_ids.clone(),
106                index_buffer_id: new_index_id,
107                any_recreated: any_buffer_recreated || needs_rebuild,
108            })
109        } else {
110            None
111        }
112    }
113
114    fn create_gpu_geometry(&mut self, geometry: &Geometry, handle: GeometryHandle) {
115        let layout_info = crate::pipeline::vertex::generate_vertex_layout(geometry);
116
117        let layout_id = self.get_or_create_vertex_layout_id(&layout_info);
118
119        let mut vertex_buffers = Vec::new();
120        let mut vertex_buffer_ids = Vec::new();
121
122        for layout_desc in &layout_info.buffers {
123            let gpu_buf = self
124                .get_gpu_buffer_by_cpu_id(layout_desc.buffer.id())
125                .expect("Vertex buffer should be prepared");
126            vertex_buffers.push(gpu_buf.buffer.clone());
127            vertex_buffer_ids.push(gpu_buf.id);
128        }
129
130        let index_buffer = if let Some(indices) = geometry.index_attribute() {
131            let gpu_buf = self
132                .get_gpu_buffer_by_cpu_id(indices.buffer.id())
133                .expect("Index buffer should be prepared");
134            Some((
135                gpu_buf.buffer.clone(),
136                indices.format,
137                indices.count,
138                gpu_buf.id,
139            ))
140        } else {
141            None
142        };
143
144        let mut draw_range = geometry.draw_range.clone();
145        if draw_range == (0..u32::MAX) {
146            if let Some(attr) = geometry.attributes().get("position") {
147                draw_range = draw_range.start..std::cmp::min(attr.count, draw_range.end);
148            } else if let Some(attr) = geometry.attributes().values().next() {
149                draw_range = draw_range.start..std::cmp::min(attr.count, draw_range.end);
150            } else {
151                draw_range = 0..0;
152            }
153        }
154
155        let gpu_geo = GpuGeometry {
156            layout_info,
157            layout_id,
158            vertex_buffers,
159            vertex_buffer_ids,
160            index_buffer,
161            draw_range,
162            instance_range: 0..1,
163            version: geometry.structure_version(),
164            last_data_version: geometry.data_version(),
165            last_used_frame: self.frame_index,
166        };
167
168        self.gpu_geometries.insert(handle, gpu_geo);
169    }
170
171    pub fn get_geometry(&self, handle: GeometryHandle) -> Option<&GpuGeometry> {
172        self.gpu_geometries.get(handle)
173    }
174
175    pub fn get_or_create_vertex_layout_id(&mut self, layout: &GeneratedVertexLayout) -> u64 {
176        let signature = layout.to_signature();
177
178        if let Some(&id) = self.vertex_layout_cache.get(&signature) {
179            return id;
180        }
181
182        let id = generate_gpu_resource_id();
183        self.vertex_layout_cache.insert(signature, id);
184        id
185    }
186}