1use std::sync::Arc;
9
10use bytemuck::Zeroable;
11use godot::classes::rendering_device::UniformType;
12use godot::classes::rendering_server::MultimeshTransformFormat;
13use godot::classes::{
14 ArrayMesh, Material, MultiMesh, RdUniform, RenderingDevice, RenderingServer, StandardMaterial3D,
15};
16use godot::prelude::*;
17
18use super::device;
19use super::owned::{
20 LocalDeviceSink, MainDeviceSink, Owned, RdBuffer, RdPipeline, RdShader, RdTexture, RdUniformSet,
21 RidSink,
22};
23use crate::chunk_descriptors::{pack_params, verts_per_chunk};
24use crate::chunk_mesh::reference_chunk_mesh;
25use crate::descriptors::TerrainGpu;
26use crate::gpu::ATTR_TEX_WIDTH;
27
28const CHUNK_REALIZE_SPV: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/ChunkRealize.spv"));
29const CHUNK_TILE_BAKE_SPV: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/ChunkTileBake.spv"));
30const SCATTER_PLACE_SPV: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/ScatterPlace.spv"));
31const SCATTER_COMPACT_SPV: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/ScatterCompact.spv"));
32
33#[derive(Clone, Copy, Debug)]
37pub struct ScatterConfig {
38 pub mm_rid: Rid,
39 pub capacity: u32,
40 pub max_instances: u32,
41}
42
43struct ScatterLayerGpu {
51 cfg: ScatterConfig,
52 place_set: RdUniformSet,
53 compact_set: RdUniformSet,
54 pool_buf: RdBuffer,
55 params_buf: RdBuffer,
56 counter_buf: RdBuffer,
57}
58
59pub struct ChunkGpuResources {
67 sink: Arc<dyn RidSink>,
70
71 mm_rid: Rid,
72 budget: u32,
74 res: u32,
75 radius: f32,
76 attr_w: u32,
77 vpc: u32,
79 tile_res: u32,
81 bump_enable: f32,
84 surface_enabled: f32,
87 surface_height_scale: f32,
90
91 realize_set: RdUniformSet,
93 bake_set: RdUniformSet,
94 custom_set: RdUniformSet,
95
96 scatter_layers: Vec<ScatterLayerGpu>,
100
101 pipeline: RdPipeline,
103 bake_pipeline: RdPipeline,
105 custom_pipeline: RdPipeline,
106 scatter_pipeline: RdPipeline,
107 compact_pipeline: RdPipeline,
108
109 shader: RdShader,
111 bake_shader: RdShader,
112 custom_shader: RdShader,
113 scatter_shader: RdShader,
114 compact_shader: RdShader,
115
116 desc_buf: RdBuffer,
119 params_buf: RdBuffer,
121 verts_tex: RdTexture,
124 verts_buf: RdBuffer,
127 pos_tex: RdTexture,
131 color_atlas: RdTexture,
134 normal_atlas: RdTexture,
136 surface_color_buf: RdBuffer,
140 surface_normal_buf: RdBuffer,
143 surface_height_buf: RdBuffer,
147 custom_params_buf: RdBuffer,
149 scatter_aux_buf: RdBuffer,
151 scatter_vis_buf: RdBuffer,
153
154 custom_source: Option<String>,
162 custom_water_height: f32,
164 custom_height_scale: f32,
168 custom_user_params: Vec<f32>,
171 custom_built: bool,
172 custom_failed: bool,
175 scatter_cfgs: Vec<ScatterConfig>,
176 scatter_built: bool,
177
178 compute_only: bool,
182 built: bool,
184 init_failed: bool,
185 multimesh: Option<Gd<MultiMesh>>,
190 _template: Option<Gd<ArrayMesh>>,
192 _material: Option<Gd<Material>>,
193}
194
195impl ChunkGpuResources {
196 pub fn new(mm_rid: Rid, budget: u32, res: u32, tile_res: u32, radius: f32) -> Self {
200 Self::with_sink(mm_rid, budget, res, tile_res, radius, false, MainDeviceSink::new())
201 }
202
203 pub fn new_compute_only(budget: u32, res: u32, tile_res: u32, radius: f32) -> Self {
208 Self::with_sink(Rid::Invalid, budget, res, tile_res, radius, true, LocalDeviceSink::new())
209 }
210
211 pub fn with_sink(
213 mm_rid: Rid,
214 budget: u32,
215 res: u32,
216 tile_res: u32,
217 radius: f32,
218 compute_only: bool,
219 sink: Arc<dyn RidSink>,
220 ) -> Self {
221 Self {
222 mm_rid,
223 budget: budget.max(1),
224 res,
225 radius,
226 attr_w: ATTR_TEX_WIDTH,
227 vpc: verts_per_chunk(res),
228 tile_res: tile_res.max(1),
229 bump_enable: 0.0,
230 surface_enabled: 0.0,
231 surface_height_scale: 0.0,
232 realize_set: Owned::invalid(sink.clone()),
233 bake_set: Owned::invalid(sink.clone()),
234 custom_set: Owned::invalid(sink.clone()),
235 scatter_layers: Vec::new(),
236 pipeline: Owned::invalid(sink.clone()),
237 bake_pipeline: Owned::invalid(sink.clone()),
238 custom_pipeline: Owned::invalid(sink.clone()),
239 scatter_pipeline: Owned::invalid(sink.clone()),
240 compact_pipeline: Owned::invalid(sink.clone()),
241 shader: Owned::invalid(sink.clone()),
242 bake_shader: Owned::invalid(sink.clone()),
243 custom_shader: Owned::invalid(sink.clone()),
244 scatter_shader: Owned::invalid(sink.clone()),
245 compact_shader: Owned::invalid(sink.clone()),
246 desc_buf: Owned::invalid(sink.clone()),
247 params_buf: Owned::invalid(sink.clone()),
248 verts_tex: Owned::invalid(sink.clone()),
249 verts_buf: Owned::invalid(sink.clone()),
250 pos_tex: Owned::invalid(sink.clone()),
251 color_atlas: Owned::invalid(sink.clone()),
252 normal_atlas: Owned::invalid(sink.clone()),
253 surface_color_buf: Owned::invalid(sink.clone()),
254 surface_normal_buf: Owned::invalid(sink.clone()),
255 surface_height_buf: Owned::invalid(sink.clone()),
256 custom_params_buf: Owned::invalid(sink.clone()),
257 scatter_aux_buf: Owned::invalid(sink.clone()),
258 scatter_vis_buf: Owned::invalid(sink.clone()),
259 custom_source: None,
260 custom_water_height: 0.45,
261 custom_height_scale: 1.0,
262 custom_user_params: Vec::new(),
263 custom_built: false,
264 custom_failed: false,
265 scatter_cfgs: Vec::new(),
266 scatter_built: false,
267 compute_only,
268 built: false,
269 init_failed: false,
270 multimesh: None,
271 _template: None,
272 _material: None,
273 sink,
274 }
275 }
276
277 pub fn set_bump_enable(&mut self, v: f32) {
280 self.bump_enable = v;
281 }
282
283 pub fn set_surface(&mut self, enabled: f32, height_scale: f32) {
287 self.surface_enabled = enabled;
288 self.surface_height_scale = height_scale;
289 }
290
291 pub fn set_custom_surface(
297 &mut self,
298 source: String,
299 water_height: f32,
300 height_scale: f32,
301 user_params: Vec<f32>,
302 ) {
303 debug_assert!(!self.custom_built, "custom surface must be set before ensure_ready");
304 self.custom_source = Some(source);
305 self.custom_water_height = water_height;
306 self.custom_height_scale = height_scale;
307 self.custom_user_params = user_params;
308 }
309
310 pub fn set_custom_knobs(&mut self, water_height: f32, height_scale: f32, user_params: Vec<f32>) {
316 self.custom_water_height = water_height;
317 self.custom_height_scale = height_scale;
318 self.custom_user_params = user_params;
319 }
320
321 pub fn custom_requested(&self) -> bool {
323 self.custom_source.is_some()
324 }
325
326 pub fn custom_ready(&self) -> bool {
330 self.custom_built && !self.custom_failed && self.custom_pipeline.is_valid()
331 }
332
333 fn sink(&self) -> Arc<dyn RidSink> {
335 self.sink.clone()
336 }
337
338 pub fn custom_height_scale(&self) -> f32 {
341 self.custom_height_scale
342 }
343
344 pub fn set_scatter_configs(&mut self, cfgs: Vec<ScatterConfig>) {
348 debug_assert!(!self.scatter_built, "scatter configs must be set before ensure_ready");
349 self.scatter_cfgs = cfgs;
350 }
351
352 fn verts_tex_rows(&self) -> u32 {
354 (self.budget as u64 * self.vpc as u64 * 2).div_ceil(self.attr_w as u64).max(1) as u32
355 }
356
357 fn pos_tex_rows(&self) -> u32 {
359 (self.budget as u64 * self.vpc as u64).div_ceil(self.attr_w as u64).max(1) as u32
360 }
361
362 fn tile_texels(&self) -> u64 {
364 self.tile_res as u64 * self.tile_res as u64
365 }
366
367 fn atlas_rows(&self) -> u32 {
369 (self.budget as u64 * self.tile_texels()).div_ceil(self.attr_w as u64).max(1) as u32
370 }
371
372 pub fn ensure_ready(&mut self, rd: &mut Gd<RenderingDevice>) -> bool {
376 if self.init_failed {
377 return false;
378 }
379 if !self.built {
380 let sink = self.sink();
383 let Some((shader, pipeline)) =
384 device::compute_pipeline(rd, &sink, CHUNK_REALIZE_SPV, "ChunkRealize")
385 else {
386 self.init_failed = true;
387 return false;
388 };
389 self.shader = shader;
390 self.pipeline = pipeline;
391
392 let Some((bake_shader, bake_pipeline)) =
394 device::compute_pipeline(rd, &sink, CHUNK_TILE_BAKE_SPV, "ChunkTileBake")
395 else {
396 self.init_failed = true;
397 return false;
398 };
399 self.bake_shader = bake_shader;
400 self.bake_pipeline = bake_pipeline;
401
402 let desc_bytes =
403 self.budget as usize * std::mem::size_of::<crate::chunk_descriptors::ChunkGpu>();
404 self.desc_buf = device::storage_buffer(rd, &sink, &vec![0u8; desc_bytes]);
405 let params0 = pack_params(
407 self.res,
408 self.vpc,
409 self.attr_w,
410 0,
411 self.tile_res,
412 self.bump_enable,
413 &TerrainGpu::zeroed(),
414 0.0,
415 0.0,
416 );
417 self.params_buf = device::storage_buffer(rd, &sink, ¶ms0);
418 self.verts_buf =
419 device::storage_buffer_empty(rd, &sink, self.budget as u64 * self.vpc as u64 * 16);
420 self.verts_tex =
421 device::attribute_texture(rd, &sink, self.attr_w, self.verts_tex_rows());
422 self.pos_tex = device::position_texture(rd, &sink, self.attr_w, self.pos_tex_rows());
423 self.color_atlas = device::atlas_texture(rd, &sink, self.attr_w, self.atlas_rows());
424 self.normal_atlas = device::atlas_texture(rd, &sink, self.attr_w, self.atlas_rows());
425
426 let surface_bytes = self.budget as u64 * self.tile_texels() * 4;
429 self.surface_color_buf = device::storage_buffer_empty(rd, &sink, surface_bytes);
430 self.surface_height_buf = device::storage_buffer_empty(rd, &sink, surface_bytes);
431 self.surface_normal_buf = device::storage_buffer_empty(rd, &sink, surface_bytes);
432
433 let uniforms: Array<Gd<RdUniform>> = [
434 device::uniform(UniformType::STORAGE_BUFFER, 0, self.desc_buf.rid()),
435 device::uniform(UniformType::STORAGE_BUFFER, 1, self.params_buf.rid()),
436 device::uniform(UniformType::IMAGE, 2, self.verts_tex.rid()),
437 device::uniform(UniformType::STORAGE_BUFFER, 3, self.verts_buf.rid()),
438 device::uniform(UniformType::IMAGE, 4, self.pos_tex.rid()),
439 device::uniform(UniformType::STORAGE_BUFFER, 5, self.surface_height_buf.rid()),
440 ]
441 .into_iter()
442 .collect();
443 self.realize_set =
444 Owned::new(rd.uniform_set_create(&uniforms, self.shader.rid(), 0), sink.clone());
445
446 let bake_uniforms: Array<Gd<RdUniform>> = [
448 device::uniform(UniformType::STORAGE_BUFFER, 0, self.desc_buf.rid()),
449 device::uniform(UniformType::STORAGE_BUFFER, 1, self.params_buf.rid()),
450 device::uniform(UniformType::IMAGE, 2, self.color_atlas.rid()),
451 device::uniform(UniformType::IMAGE, 3, self.normal_atlas.rid()),
452 device::uniform(UniformType::STORAGE_BUFFER, 4, self.surface_color_buf.rid()),
453 device::uniform(UniformType::STORAGE_BUFFER, 5, self.surface_height_buf.rid()),
454 device::uniform(UniformType::STORAGE_BUFFER, 6, self.surface_normal_buf.rid()),
455 ]
456 .into_iter()
457 .collect();
458 self.bake_set = Owned::new(
459 rd.uniform_set_create(&bake_uniforms, self.bake_shader.rid(), 0),
460 sink.clone(),
461 );
462
463 if !self.compute_only && self.mm_rid.is_invalid() {
464 self.create_multimesh();
465 }
466 self.built = true;
467 }
468
469 if self.custom_source.is_some() && !self.custom_built && !self.custom_failed {
473 self.ensure_custom_ready(rd);
474 }
475
476 if self.compute_only {
477 if !self.scatter_cfgs.is_empty() && !self.scatter_built {
481 return self.ensure_scatter_ready(rd);
482 }
483 return true;
484 }
485
486 let rs = RenderingServer::singleton();
488 if rs.multimesh_get_buffer_rd_rid(self.mm_rid).is_invalid()
489 || rs.multimesh_get_command_buffer_rd_rid(self.mm_rid).is_invalid()
490 {
491 return false;
492 }
493
494 if !self.scatter_cfgs.is_empty() && !self.scatter_built {
497 if !self.ensure_scatter_ready(rd) {
498 return false;
499 }
500 }
501 true
502 }
503
504 fn ensure_scatter_ready(&mut self, rd: &mut Gd<RenderingDevice>) -> bool {
508 let rs = RenderingServer::singleton();
509 for cfg in &self.scatter_cfgs {
510 if cfg.mm_rid.is_valid()
513 && (rs.multimesh_get_buffer_rd_rid(cfg.mm_rid).is_invalid()
514 || rs.multimesh_get_command_buffer_rd_rid(cfg.mm_rid).is_invalid())
515 {
516 return false;
517 }
518 }
519
520 let sink = self.sink();
521 let Some((shader, pipeline)) =
522 device::compute_pipeline(rd, &sink, SCATTER_PLACE_SPV, "ScatterPlace")
523 else {
524 self.init_failed = true;
525 return false;
526 };
527 self.scatter_shader = shader;
528 self.scatter_pipeline = pipeline;
529 let Some((cshader, cpipeline)) =
530 device::compute_pipeline(rd, &sink, SCATTER_COMPACT_SPV, "ScatterCompact")
531 else {
532 self.init_failed = true;
533 return false;
534 };
535 self.compact_shader = cshader;
536 self.compact_pipeline = cpipeline;
537
538 self.scatter_aux_buf = device::storage_buffer_empty(rd, &sink, self.budget as u64 * 16);
539 self.scatter_vis_buf = device::storage_buffer_empty(rd, &sink, self.budget as u64 * 4);
541
542 let cfgs = std::mem::take(&mut self.scatter_cfgs);
543 for cfg in &cfgs {
544 let pool_bytes = self.budget as u64 * cfg.capacity as u64 * 64;
545 let pool_buf = device::storage_buffer_empty(rd, &sink, pool_bytes);
546 let params_buf = device::storage_buffer(
547 rd,
548 &sink,
549 &[0u8; std::mem::size_of::<crate::scatter_descriptors::ScatterParamsGpu>()],
550 );
551 let counter_buf = device::storage_buffer_empty(rd, &sink, 4);
552
553 let mut layer = ScatterLayerGpu {
554 cfg: *cfg,
555 place_set: Owned::invalid(sink.clone()),
556 compact_set: Owned::invalid(sink.clone()),
557 pool_buf,
558 params_buf,
559 counter_buf,
560 };
561 self.build_scatter_sets(rd, &mut layer);
562 self.scatter_layers.push(layer);
563 }
564 self.scatter_built = true;
565 true
566 }
567
568 fn ensure_custom_ready(&mut self, rd: &mut Gd<RenderingDevice>) {
573 let Some(src) = self.custom_source.clone() else { return };
574 let sink = self.sink();
575 let Some((shader, pipeline)) =
576 device::compute_pipeline_from_glsl(rd, &sink, &src, "CustomSurface")
577 else {
578 self.custom_failed = true;
579 return;
580 };
581 self.custom_shader = shader;
582 self.custom_pipeline = pipeline;
583 let params0 = crate::custom_surface::pack_params(
584 0,
585 self.tile_res,
586 self.custom_water_height,
587 self.custom_height_scale,
588 &self.custom_user_params,
589 );
590 self.custom_params_buf = device::storage_buffer(rd, &sink, ¶ms0);
591 let uniforms: Array<Gd<RdUniform>> = [
592 device::uniform(UniformType::STORAGE_BUFFER, 0, self.desc_buf.rid()),
593 device::uniform(UniformType::STORAGE_BUFFER, 1, self.surface_color_buf.rid()),
594 device::uniform(UniformType::STORAGE_BUFFER, 2, self.surface_height_buf.rid()),
595 device::uniform(UniformType::STORAGE_BUFFER, 3, self.surface_normal_buf.rid()),
596 device::uniform(UniformType::STORAGE_BUFFER, 4, self.custom_params_buf.rid()),
597 ]
598 .into_iter()
599 .collect();
600 self.custom_set =
601 Owned::new(rd.uniform_set_create(&uniforms, self.custom_shader.rid(), 0), sink.clone());
602 self.custom_built = true;
603 }
604
605 fn build_scatter_sets(&self, rd: &mut Gd<RenderingDevice>, layer: &mut ScatterLayerGpu) {
612 let rs = RenderingServer::singleton();
613 let sink = self.sink();
614 let place_uniforms: Array<Gd<RdUniform>> = [
615 device::uniform(UniformType::STORAGE_BUFFER, 0, self.desc_buf.rid()),
616 device::uniform(UniformType::STORAGE_BUFFER, 1, self.scatter_aux_buf.rid()),
617 device::uniform(UniformType::STORAGE_BUFFER, 2, layer.params_buf.rid()),
618 device::uniform(UniformType::STORAGE_BUFFER, 3, layer.pool_buf.rid()),
619 device::uniform(UniformType::STORAGE_BUFFER, 4, self.surface_height_buf.rid()),
622 ]
623 .into_iter()
624 .collect();
625 layer.place_set = Owned::new(
626 rd.uniform_set_create(&place_uniforms, self.scatter_shader.rid(), 0),
627 sink.clone(),
628 );
629
630 if layer.cfg.mm_rid.is_invalid() {
631 layer.compact_set = Owned::invalid(sink);
633 return;
634 }
635 let mm_buf = rs.multimesh_get_buffer_rd_rid(layer.cfg.mm_rid);
636 let cmd_buf = rs.multimesh_get_command_buffer_rd_rid(layer.cfg.mm_rid);
637 let compact_uniforms: Array<Gd<RdUniform>> = [
638 device::uniform(UniformType::STORAGE_BUFFER, 0, layer.params_buf.rid()),
639 device::uniform(UniformType::STORAGE_BUFFER, 1, layer.pool_buf.rid()),
640 device::uniform(UniformType::STORAGE_BUFFER, 2, self.scatter_vis_buf.rid()),
641 device::uniform(UniformType::STORAGE_BUFFER, 3, layer.counter_buf.rid()),
642 device::uniform(UniformType::STORAGE_BUFFER, 4, mm_buf),
643 device::uniform(UniformType::STORAGE_BUFFER, 5, cmd_buf),
644 ]
645 .into_iter()
646 .collect();
647 layer.compact_set = Owned::new(
648 rd.uniform_set_create(&compact_uniforms, self.compact_shader.rid(), 0),
649 sink,
650 );
651 }
652
653 pub fn upload_scatter(
659 &mut self,
660 rd: &mut Gd<RenderingDevice>,
661 aux_bytes: &[u8],
662 vis_bytes: &[u8],
663 layer_params: &[Vec<u8>],
664 ) {
665 if !self.scatter_built {
666 return;
667 }
668 let mut layers = std::mem::take(&mut self.scatter_layers);
669 for layer in &mut layers {
670 if !rd.uniform_set_is_valid(layer.place_set.rid())
671 || (layer.cfg.mm_rid.is_valid() && !rd.uniform_set_is_valid(layer.compact_set.rid()))
672 {
673 self.build_scatter_sets(rd, layer);
674 }
675 }
676 self.scatter_layers = layers;
677 if !aux_bytes.is_empty() {
678 let n = aux_bytes.len().min(self.budget as usize * 16);
679 rd.buffer_update(
680 self.scatter_aux_buf.rid(),
681 0,
682 n as u32,
683 &PackedByteArray::from(&aux_bytes[..n]),
684 );
685 }
686 if !vis_bytes.is_empty() {
687 let n = vis_bytes.len().min(self.budget as usize * 4);
688 rd.buffer_update(
689 self.scatter_vis_buf.rid(),
690 0,
691 n as u32,
692 &PackedByteArray::from(&vis_bytes[..n]),
693 );
694 }
695 let rs = RenderingServer::singleton();
696 let zero = PackedByteArray::from(&0u32.to_le_bytes()[..]);
697 for (layer, params) in self.scatter_layers.iter().zip(layer_params.iter()) {
698 rd.buffer_update(
699 layer.params_buf.rid(),
700 0,
701 params.len() as u32,
702 &PackedByteArray::from(¶ms[..]),
703 );
704 rd.buffer_update(layer.counter_buf.rid(), 0, 4, &zero);
705 if layer.cfg.mm_rid.is_valid() {
706 let cmd = rs.multimesh_get_command_buffer_rd_rid(layer.cfg.mm_rid);
707 if cmd.is_valid() {
708 rd.buffer_update(cmd, 4, 4, &zero);
709 }
710 }
711 }
712 }
713
714 pub fn record_scatter_place(
717 &self,
718 rd: &mut Gd<RenderingDevice>,
719 list: i64,
720 li: usize,
721 chunk_count: u32,
722 ) {
723 let Some(layer) = self.scatter_layers.get(li) else { return };
724 let max_threads = self.budget * layer.cfg.capacity;
725 let threads = (chunk_count * layer.cfg.capacity).min(max_threads);
726 let groups = threads.div_ceil(64);
727 if groups == 0 {
728 return;
729 }
730 rd.compute_list_bind_compute_pipeline(list, self.scatter_pipeline.rid());
731 rd.compute_list_bind_uniform_set(list, layer.place_set.rid(), 0);
732 rd.compute_list_dispatch(list, groups, 1, 1);
733 rd.compute_list_add_barrier(list);
734 }
735
736 pub fn record_scatter_compact(
739 &self,
740 rd: &mut Gd<RenderingDevice>,
741 list: i64,
742 li: usize,
743 vis_count: u32,
744 ) {
745 let Some(layer) = self.scatter_layers.get(li) else { return };
746 if !layer.compact_set.is_valid() {
747 return; }
749 let max_threads = self.budget * layer.cfg.capacity;
750 let threads = (vis_count * layer.cfg.capacity).min(max_threads);
751 let groups = threads.div_ceil(64);
752 if groups == 0 {
753 return;
754 }
755 rd.compute_list_bind_compute_pipeline(list, self.compact_pipeline.rid());
756 rd.compute_list_bind_uniform_set(list, layer.compact_set.rid(), 0);
757 rd.compute_list_dispatch(list, groups, 1, 1);
758 rd.compute_list_add_barrier(list);
759 }
760
761 pub fn scatter_layer_count(&self) -> usize {
763 self.scatter_layers.len()
764 }
765
766 pub fn scatter_pool_buf(&self, li: usize) -> Rid {
769 self.scatter_layers.get(li).map_or(Rid::Invalid, |l| l.pool_buf.rid())
770 }
771
772 fn create_multimesh(&mut self) {
775 let mut rs = RenderingServer::singleton();
776 let material: Gd<Material> = StandardMaterial3D::new_gd().upcast();
777 let template = reference_chunk_mesh(self.res, &material);
778 let multimesh = MultiMesh::new_gd();
779 let mm_rid = multimesh.get_rid();
780 rs.multimesh_allocate_data_ex(mm_rid, self.budget as i32, MultimeshTransformFormat::TRANSFORM_3D)
781 .custom_data_format(true)
782 .use_indirect(true)
783 .done();
784 rs.multimesh_set_mesh(mm_rid, template.get_rid());
785 self.mm_rid = mm_rid;
786 self.multimesh = Some(multimesh);
787 self._template = Some(template);
788 self._material = Some(material);
789 }
790
791 pub fn upload_descs(&mut self, rd: &mut Gd<RenderingDevice>, bytes: &[u8], count: u32) {
797 if bytes.is_empty() {
798 return;
799 }
800 debug_assert!(
801 count <= self.budget,
802 "chunk realize count {count} exceeds budget {} — would overrun desc buffer",
803 self.budget
804 );
805 let max = self.budget as usize * std::mem::size_of::<crate::chunk_descriptors::ChunkGpu>();
806 let n = bytes.len().min(max);
807 rd.buffer_update(self.desc_buf.rid(), 0, n as u32, &PackedByteArray::from(&bytes[..n]));
808 }
809
810 pub fn upload_params(
813 &mut self,
814 rd: &mut Gd<RenderingDevice>,
815 chunk_count: u32,
816 terrain: &TerrainGpu,
817 ) {
818 let bytes = pack_params(
819 self.res,
820 self.vpc,
821 self.attr_w,
822 chunk_count,
823 self.tile_res,
824 self.bump_enable,
825 terrain,
826 self.surface_enabled,
827 self.surface_height_scale,
828 );
829 rd.buffer_update(
830 self.params_buf.rid(),
831 0,
832 bytes.len() as u32,
833 &PackedByteArray::from(&bytes[..]),
834 );
835 }
836
837 pub fn upload_terrain(&mut self, rd: &mut Gd<RenderingDevice>, terrain: &TerrainGpu) {
839 let bytes = bytemuck::bytes_of(terrain);
840 rd.buffer_update(self.params_buf.rid(), 16, bytes.len() as u32, &PackedByteArray::from(bytes));
841 }
842
843 pub fn upload_surface(
852 &mut self,
853 rd: &mut Gd<RenderingDevice>,
854 slot: u32,
855 color_bytes: &[u8],
856 height: &[f32],
857 normal_bytes: &[u8],
858 ) {
859 if slot >= self.budget
860 || !self.surface_color_buf.is_valid()
861 || !self.surface_height_buf.is_valid()
862 || !self.surface_normal_buf.is_valid()
863 {
864 return;
865 }
866 let off = slot as u64 * self.tile_texels() * 4;
867 if !color_bytes.is_empty() {
868 rd.buffer_update(
869 self.surface_color_buf.rid(),
870 off as u32,
871 color_bytes.len() as u32,
872 &PackedByteArray::from(color_bytes),
873 );
874 }
875 if !height.is_empty() {
876 let h: &[u8] = bytemuck::cast_slice(height);
877 rd.buffer_update(
878 self.surface_height_buf.rid(),
879 off as u32,
880 h.len() as u32,
881 &PackedByteArray::from(h),
882 );
883 }
884 if !normal_bytes.is_empty() {
885 rd.buffer_update(
886 self.surface_normal_buf.rid(),
887 off as u32,
888 normal_bytes.len() as u32,
889 &PackedByteArray::from(normal_bytes),
890 );
891 }
892 }
893
894 pub fn upload_instances(&mut self, rd: &mut Gd<RenderingDevice>, bytes: &[u8], count: u32) {
898 let rs = RenderingServer::singleton();
899 let buffer = rs.multimesh_get_buffer_rd_rid(self.mm_rid);
900 let command = rs.multimesh_get_command_buffer_rd_rid(self.mm_rid);
901 if buffer.is_invalid() || command.is_invalid() {
902 return;
903 }
904 if !bytes.is_empty() {
905 rd.buffer_update(buffer, 0, bytes.len() as u32, &PackedByteArray::from(bytes));
906 }
907 let c = count.min(self.budget);
908 rd.buffer_update(command, 4, 4, &PackedByteArray::from(&c.to_le_bytes()[..]));
909 }
910
911 pub fn record_realize(&self, rd: &mut Gd<RenderingDevice>, list: i64, vert_threads: u32) {
914 let max_threads = self.budget * self.vpc;
918 debug_assert!(
919 vert_threads <= max_threads,
920 "chunk realize vert_threads {vert_threads} exceeds pool capacity {max_threads}"
921 );
922 let vert_threads = vert_threads.min(max_threads);
923 let groups = vert_threads.div_ceil(64);
924 if groups == 0 {
925 return;
926 }
927 rd.compute_list_bind_compute_pipeline(list, self.pipeline.rid());
928 rd.compute_list_bind_uniform_set(list, self.realize_set.rid(), 0);
929 rd.compute_list_dispatch(list, groups, 1, 1);
930 rd.compute_list_add_barrier(list);
931 }
932
933 pub fn record_bake(&self, rd: &mut Gd<RenderingDevice>, list: i64, texel_threads: u32) {
938 let max_threads = (self.budget as u64 * self.tile_texels()).min(u32::MAX as u64) as u32;
939 debug_assert!(
940 texel_threads <= max_threads,
941 "chunk bake texel_threads {texel_threads} exceeds atlas capacity {max_threads}"
942 );
943 let texel_threads = texel_threads.min(max_threads);
944 let groups = texel_threads.div_ceil(64);
945 if groups == 0 {
946 return;
947 }
948 rd.compute_list_bind_compute_pipeline(list, self.bake_pipeline.rid());
949 rd.compute_list_bind_uniform_set(list, self.bake_set.rid(), 0);
950 rd.compute_list_dispatch(list, groups, 1, 1);
951 rd.compute_list_add_barrier(list);
952 }
953
954 pub fn upload_custom_params(&self, rd: &mut Gd<RenderingDevice>, chunk_count: u32) {
959 if !self.custom_ready() || !self.custom_params_buf.is_valid() {
960 return;
961 }
962 let bytes = crate::custom_surface::pack_params(
963 chunk_count.min(self.budget),
964 self.tile_res,
965 self.custom_water_height,
966 self.custom_height_scale,
967 &self.custom_user_params,
968 );
969 rd.buffer_update(
970 self.custom_params_buf.rid(),
971 0,
972 bytes.len() as u32,
973 &PackedByteArray::from(&bytes[..]),
974 );
975 }
976
977 pub fn record_custom_surface(&self, rd: &mut Gd<RenderingDevice>, list: i64, chunk_count: u32) {
981 if !self.custom_ready() {
982 return;
983 }
984 let max_threads = (self.budget as u64 * self.tile_texels()).min(u32::MAX as u64) as u32;
985 let threads = ((chunk_count as u64 * self.tile_texels()).min(max_threads as u64)) as u32;
986 let groups = threads.div_ceil(64);
987 if groups == 0 {
988 return;
989 }
990 rd.compute_list_bind_compute_pipeline(list, self.custom_pipeline.rid());
991 rd.compute_list_bind_uniform_set(list, self.custom_set.rid(), 0);
992 rd.compute_list_dispatch(list, groups, 1, 1);
993 rd.compute_list_add_barrier(list);
994 }
995
996 pub fn verts_per_chunk(&self) -> u32 {
998 self.vpc
999 }
1000
1001 pub fn tile_res(&self) -> u32 {
1003 self.tile_res
1004 }
1005
1006 pub fn color_atlas(&self) -> Rid {
1008 self.color_atlas.rid()
1009 }
1010
1011 pub fn normal_atlas(&self) -> Rid {
1013 self.normal_atlas.rid()
1014 }
1015
1016 pub fn radius(&self) -> f32 {
1018 self.radius
1019 }
1020
1021 pub fn pos_buf(&self) -> Rid {
1023 self.verts_buf.rid()
1024 }
1025
1026 pub fn attr_tex(&self) -> Rid {
1028 self.verts_tex.rid()
1029 }
1030
1031 pub fn pos_tex(&self) -> Rid {
1034 self.pos_tex.rid()
1035 }
1036
1037 pub fn mm_rid(&self) -> Rid {
1039 self.mm_rid
1040 }
1041
1042 pub fn init_failed(&self) -> bool {
1044 self.init_failed
1045 }
1046
1047}
1048
1049#[cfg(test)]
1050mod tests {
1051 use super::*;
1052 use crate::gpu::owned::{Owned, RidSink};
1053 use std::sync::{Arc, Mutex};
1054
1055 #[derive(Default)]
1057 struct SpySink {
1058 freed: Mutex<Vec<Rid>>,
1059 }
1060
1061 impl SpySink {
1062 fn freed(&self) -> Vec<Rid> {
1063 self.freed.lock().unwrap().clone()
1064 }
1065 }
1066
1067 impl RidSink for SpySink {
1068 fn free(&self, rid: Rid) {
1069 self.freed.lock().unwrap().push(rid);
1070 }
1071 }
1072
1073 #[test]
1078 fn dropping_resources_frees_every_rid_once_dependents_first() {
1079 let spy = Arc::new(SpySink::default());
1080 let sink: Arc<dyn RidSink> = spy.clone();
1081
1082 {
1083 let mut r =
1084 ChunkGpuResources::with_sink(Rid::Invalid, 4, 8, 8, 1.0, true, sink.clone());
1085 r.realize_set = Owned::new(Rid::new(101), sink.clone());
1087 r.bake_set = Owned::new(Rid::new(102), sink.clone());
1088 r.custom_set = Owned::new(Rid::new(103), sink.clone());
1089 r.pipeline = Owned::new(Rid::new(201), sink.clone());
1091 r.bake_pipeline = Owned::new(Rid::new(202), sink.clone());
1092 r.shader = Owned::new(Rid::new(301), sink.clone());
1094 r.bake_shader = Owned::new(Rid::new(302), sink.clone());
1095 r.custom_shader = Owned::new(Rid::new(303), sink.clone());
1096 r.scatter_shader = Owned::new(Rid::new(304), sink.clone());
1097 r.compact_shader = Owned::new(Rid::new(305), sink.clone());
1098 r.desc_buf = Owned::new(Rid::new(401), sink.clone());
1100 r.params_buf = Owned::new(Rid::new(402), sink.clone());
1101 r.verts_buf = Owned::new(Rid::new(403), sink.clone());
1102 r.verts_tex = Owned::new(Rid::new(404), sink.clone());
1103 r.pos_tex = Owned::new(Rid::new(405), sink.clone());
1104 r.scatter_layers.push(ScatterLayerGpu {
1107 cfg: ScatterConfig { mm_rid: Rid::Invalid, capacity: 1, max_instances: 1 },
1108 place_set: Owned::new(Rid::new(111), sink.clone()),
1109 compact_set: Owned::new(Rid::new(112), sink.clone()),
1110 pool_buf: Owned::new(Rid::new(411), sink.clone()),
1111 params_buf: Owned::new(Rid::new(412), sink.clone()),
1112 counter_buf: Owned::new(Rid::new(413), sink.clone()),
1113 });
1114 assert!(spy.freed().is_empty(), "nothing freed while alive");
1115 }
1116
1117 let freed = spy.freed();
1118 let expected: Vec<Rid> = [
1119 101, 102, 103, 111, 112, 201, 202, 301, 302, 303, 304, 305, 401, 402, 403, 404, 405,
1120 411, 412, 413,
1121 ]
1122 .iter()
1123 .map(|&n| Rid::new(n))
1124 .collect();
1125
1126 let mut sorted = freed.clone();
1128 sorted.sort_by_key(|r| r.to_u64());
1129 let mut want = expected.clone();
1130 want.sort_by_key(|r| r.to_u64());
1131 assert_eq!(sorted, want, "every owned RID freed exactly once");
1132
1133 let pos = |n: u64| freed.iter().position(|r| *r == Rid::new(n)).unwrap();
1134 assert!(pos(101) < pos(301), "realize_set freed before its shader");
1136 assert!(pos(102) < pos(302), "bake_set freed before its shader");
1137 assert!(pos(103) < pos(303), "custom_set freed before its shader");
1138 assert!(pos(111) < pos(304), "place_set freed before the scatter shader");
1139 assert!(pos(112) < pos(305), "compact_set freed before the compact shader");
1140 assert!(pos(111) < pos(411), "place_set freed before the layer pool buffer");
1141 assert!(pos(101) < pos(401), "realize_set freed before the desc buffer");
1142 assert!(pos(201) < pos(301), "pipeline freed before its shader");
1143 }
1144}