1use super::*;
2
3impl ViewportRenderer {
4 pub fn pick_scene_gpu(
28 &mut self,
29 device: &wgpu::Device,
30 queue: &wgpu::Queue,
31 cursor: glam::Vec2,
32 frame: &FrameData,
33 ) -> Option<crate::interaction::picking::GpuPickHit> {
34 let scene_items: &[SceneRenderItem] = match &frame.scene.surfaces {
36 SurfaceSubmission::Flat(items) => items,
37 };
38
39 let vp_w = frame.camera.viewport_size[0] as u32;
40 let vp_h = frame.camera.viewport_size[1] as u32;
41
42 if cursor.x < 0.0
44 || cursor.y < 0.0
45 || cursor.x >= frame.camera.viewport_size[0]
46 || cursor.y >= frame.camera.viewport_size[1]
47 || vp_w == 0
48 || vp_h == 0
49 {
50 return None;
51 }
52
53 self.resources.ensure_pick_pipeline(device);
55
56 let pickable_items: Vec<&SceneRenderItem> = scene_items
60 .iter()
61 .filter(|item| item.visible && item.pick_id != 0)
62 .collect();
63
64 let pick_instances: Vec<PickInstance> = pickable_items
65 .iter()
66 .map(|item| {
67 let m = item.model;
68 PickInstance {
69 model_c0: m[0],
70 model_c1: m[1],
71 model_c2: m[2],
72 model_c3: m[3],
73 object_id: item.pick_id as u32,
74 _pad: [0; 3],
75 }
76 })
77 .collect();
78
79 if pick_instances.is_empty() {
80 return None;
81 }
82
83 let pick_instance_bytes = bytemuck::cast_slice(&pick_instances);
85 let pick_instance_buf = device.create_buffer(&wgpu::BufferDescriptor {
86 label: Some("pick_instance_buf"),
87 size: pick_instance_bytes.len().max(80) as u64,
88 usage: wgpu::BufferUsages::STORAGE | wgpu::BufferUsages::COPY_DST,
89 mapped_at_creation: false,
90 });
91 queue.write_buffer(&pick_instance_buf, 0, pick_instance_bytes);
92
93 let pick_instance_bg = device.create_bind_group(&wgpu::BindGroupDescriptor {
94 label: Some("pick_instance_bg"),
95 layout: self
96 .resources
97 .pick_bind_group_layout_1
98 .as_ref()
99 .expect("ensure_pick_pipeline must be called first"),
100 entries: &[wgpu::BindGroupEntry {
101 binding: 0,
102 resource: pick_instance_buf.as_entire_binding(),
103 }],
104 });
105
106 let camera_uniform = frame.camera.render_camera.camera_uniform();
108 let camera_bytes = bytemuck::bytes_of(&camera_uniform);
109 let pick_camera_buf = device.create_buffer(&wgpu::BufferDescriptor {
110 label: Some("pick_camera_buf"),
111 size: std::mem::size_of::<CameraUniform>() as u64,
112 usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
113 mapped_at_creation: false,
114 });
115 queue.write_buffer(&pick_camera_buf, 0, camera_bytes);
116
117 let pick_camera_bg = device.create_bind_group(&wgpu::BindGroupDescriptor {
118 label: Some("pick_camera_bg"),
119 layout: self
120 .resources
121 .pick_camera_bgl
122 .as_ref()
123 .expect("ensure_pick_pipeline must be called first"),
124 entries: &[wgpu::BindGroupEntry {
125 binding: 0,
126 resource: pick_camera_buf.as_entire_binding(),
127 }],
128 });
129
130 let pick_id_texture = device.create_texture(&wgpu::TextureDescriptor {
132 label: Some("pick_id_texture"),
133 size: wgpu::Extent3d {
134 width: vp_w,
135 height: vp_h,
136 depth_or_array_layers: 1,
137 },
138 mip_level_count: 1,
139 sample_count: 1,
140 dimension: wgpu::TextureDimension::D2,
141 format: wgpu::TextureFormat::R32Uint,
142 usage: wgpu::TextureUsages::RENDER_ATTACHMENT | wgpu::TextureUsages::COPY_SRC,
143 view_formats: &[],
144 });
145 let pick_id_view = pick_id_texture.create_view(&wgpu::TextureViewDescriptor::default());
146
147 let pick_depth_texture = device.create_texture(&wgpu::TextureDescriptor {
148 label: Some("pick_depth_color_texture"),
149 size: wgpu::Extent3d {
150 width: vp_w,
151 height: vp_h,
152 depth_or_array_layers: 1,
153 },
154 mip_level_count: 1,
155 sample_count: 1,
156 dimension: wgpu::TextureDimension::D2,
157 format: wgpu::TextureFormat::R32Float,
158 usage: wgpu::TextureUsages::RENDER_ATTACHMENT | wgpu::TextureUsages::COPY_SRC,
159 view_formats: &[],
160 });
161 let pick_depth_view =
162 pick_depth_texture.create_view(&wgpu::TextureViewDescriptor::default());
163
164 let depth_stencil_texture = device.create_texture(&wgpu::TextureDescriptor {
165 label: Some("pick_ds_texture"),
166 size: wgpu::Extent3d {
167 width: vp_w,
168 height: vp_h,
169 depth_or_array_layers: 1,
170 },
171 mip_level_count: 1,
172 sample_count: 1,
173 dimension: wgpu::TextureDimension::D2,
174 format: wgpu::TextureFormat::Depth24PlusStencil8,
175 usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
176 view_formats: &[],
177 });
178 let depth_stencil_view =
179 depth_stencil_texture.create_view(&wgpu::TextureViewDescriptor::default());
180
181 let mut encoder = device.create_command_encoder(&wgpu::CommandEncoderDescriptor {
183 label: Some("pick_pass_encoder"),
184 });
185 {
186 let mut pick_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
187 label: Some("pick_pass"),
188 color_attachments: &[
189 Some(wgpu::RenderPassColorAttachment {
190 view: &pick_id_view,
191 resolve_target: None,
192 depth_slice: None,
193 ops: wgpu::Operations {
194 load: wgpu::LoadOp::Clear(wgpu::Color {
195 r: 0.0,
196 g: 0.0,
197 b: 0.0,
198 a: 0.0,
199 }),
200 store: wgpu::StoreOp::Store,
201 },
202 }),
203 Some(wgpu::RenderPassColorAttachment {
204 view: &pick_depth_view,
205 resolve_target: None,
206 depth_slice: None,
207 ops: wgpu::Operations {
208 load: wgpu::LoadOp::Clear(wgpu::Color {
209 r: 1.0,
210 g: 0.0,
211 b: 0.0,
212 a: 0.0,
213 }),
214 store: wgpu::StoreOp::Store,
215 },
216 }),
217 ],
218 depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment {
219 view: &depth_stencil_view,
220 depth_ops: Some(wgpu::Operations {
221 load: wgpu::LoadOp::Clear(1.0),
222 store: wgpu::StoreOp::Store,
223 }),
224 stencil_ops: None,
225 }),
226 timestamp_writes: None,
227 occlusion_query_set: None,
228 });
229
230 pick_pass.set_pipeline(
231 self.resources
232 .pick_pipeline
233 .as_ref()
234 .expect("ensure_pick_pipeline must be called first"),
235 );
236 pick_pass.set_bind_group(0, &pick_camera_bg, &[]);
237 pick_pass.set_bind_group(1, &pick_instance_bg, &[]);
238
239 for (instance_slot, item) in pickable_items.iter().enumerate() {
242 let Some(mesh) = self
243 .resources
244 .mesh_store
245 .get(crate::resources::mesh_store::MeshId(item.mesh_index))
246 else {
247 continue;
248 };
249 pick_pass.set_vertex_buffer(0, mesh.vertex_buffer.slice(..));
250 pick_pass.set_index_buffer(mesh.index_buffer.slice(..), wgpu::IndexFormat::Uint32);
251 let slot = instance_slot as u32;
252 pick_pass.draw_indexed(0..mesh.index_count, 0, slot..slot + 1);
253 }
254 }
255
256 let bytes_per_row_aligned = 256u32; let id_staging = device.create_buffer(&wgpu::BufferDescriptor {
261 label: Some("pick_id_staging"),
262 size: bytes_per_row_aligned as u64,
263 usage: wgpu::BufferUsages::COPY_DST | wgpu::BufferUsages::MAP_READ,
264 mapped_at_creation: false,
265 });
266 let depth_staging = device.create_buffer(&wgpu::BufferDescriptor {
267 label: Some("pick_depth_staging"),
268 size: bytes_per_row_aligned as u64,
269 usage: wgpu::BufferUsages::COPY_DST | wgpu::BufferUsages::MAP_READ,
270 mapped_at_creation: false,
271 });
272
273 let px = cursor.x as u32;
274 let py = cursor.y as u32;
275
276 encoder.copy_texture_to_buffer(
277 wgpu::TexelCopyTextureInfo {
278 texture: &pick_id_texture,
279 mip_level: 0,
280 origin: wgpu::Origin3d { x: px, y: py, z: 0 },
281 aspect: wgpu::TextureAspect::All,
282 },
283 wgpu::TexelCopyBufferInfo {
284 buffer: &id_staging,
285 layout: wgpu::TexelCopyBufferLayout {
286 offset: 0,
287 bytes_per_row: Some(bytes_per_row_aligned),
288 rows_per_image: Some(1),
289 },
290 },
291 wgpu::Extent3d {
292 width: 1,
293 height: 1,
294 depth_or_array_layers: 1,
295 },
296 );
297 encoder.copy_texture_to_buffer(
298 wgpu::TexelCopyTextureInfo {
299 texture: &pick_depth_texture,
300 mip_level: 0,
301 origin: wgpu::Origin3d { x: px, y: py, z: 0 },
302 aspect: wgpu::TextureAspect::All,
303 },
304 wgpu::TexelCopyBufferInfo {
305 buffer: &depth_staging,
306 layout: wgpu::TexelCopyBufferLayout {
307 offset: 0,
308 bytes_per_row: Some(bytes_per_row_aligned),
309 rows_per_image: Some(1),
310 },
311 },
312 wgpu::Extent3d {
313 width: 1,
314 height: 1,
315 depth_or_array_layers: 1,
316 },
317 );
318
319 queue.submit(std::iter::once(encoder.finish()));
320
321 let (tx_id, rx_id) = std::sync::mpsc::channel::<Result<(), wgpu::BufferAsyncError>>();
323 let (tx_dep, rx_dep) = std::sync::mpsc::channel::<Result<(), wgpu::BufferAsyncError>>();
324 id_staging
325 .slice(..)
326 .map_async(wgpu::MapMode::Read, move |r| {
327 let _ = tx_id.send(r);
328 });
329 depth_staging
330 .slice(..)
331 .map_async(wgpu::MapMode::Read, move |r| {
332 let _ = tx_dep.send(r);
333 });
334 device
335 .poll(wgpu::PollType::Wait {
336 submission_index: None,
337 timeout: Some(std::time::Duration::from_secs(5)),
338 })
339 .unwrap();
340 let _ = rx_id.recv().unwrap_or(Err(wgpu::BufferAsyncError));
341 let _ = rx_dep.recv().unwrap_or(Err(wgpu::BufferAsyncError));
342
343 let object_id = {
344 let data = id_staging.slice(..).get_mapped_range();
345 u32::from_le_bytes([data[0], data[1], data[2], data[3]])
346 };
347 id_staging.unmap();
348
349 let depth = {
350 let data = depth_staging.slice(..).get_mapped_range();
351 f32::from_le_bytes([data[0], data[1], data[2], data[3]])
352 };
353 depth_staging.unmap();
354
355 if object_id == 0 {
357 return None;
358 }
359
360 Some(crate::interaction::picking::GpuPickHit {
361 object_id: object_id as u64,
362 depth,
363 })
364 }
365}