1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
//! State for the instanced (GPU-driven) mesh draw path: the per-frame batch
//! list, the cache used to skip unchanged uploads, the GPU culling pipelines,
//! and the indirect-draw readback buffers.
use super::indirect;
use super::types::InstancedBatch;
pub(crate) struct InstancingState {
/// Instanced batches prepared for the current frame. Empty when using the
/// per-object path.
pub(crate) batches: Vec<InstancedBatch>,
/// Whether the current frame uses the instanced draw path.
pub(crate) use_instancing: bool,
/// True when the device supports `INDIRECT_FIRST_INSTANCE`.
pub(crate) gpu_culling_supported: bool,
/// True when GPU-driven culling is active (supported and not disabled by the caller).
pub(crate) gpu_culling_enabled: bool,
/// GPU culling compute pipelines and frustum buffer. Created lazily on the first
/// frame where `gpu_culling_enabled` is true and instance buffers are present.
pub(crate) cull_resources: Option<indirect::CullResources>,
/// Last scene generation seen during prepare(). u64::MAX forces rebuild on first frame.
pub(crate) last_scene_generation: u64,
/// Last selection generation seen during prepare(). u64::MAX forces rebuild on first frame.
pub(crate) last_selection_generation: u64,
/// Last scene_items count seen during prepare(). usize::MAX forces rebuild on first frame.
/// Included in the cache key so frustum-culling changes (different visible set, different
/// count) invalidate the instance buffer even when scene_generation is stable.
pub(crate) last_scene_items_count: usize,
/// Count of items that passed the instanced-path filter on the last rebuild.
/// Used in place of has_per_frame_mutations so scenes that mix instanced and
/// non-instanced items still hit the instanced batch cache on frames where the
/// filtered set is unchanged.
pub(crate) last_instancable_count: usize,
/// Total instance count from the last rebuild. Fast length check in
/// `structure_preserved` and `instance_count` for GPU cull dispatches.
pub(crate) cached_instance_count: usize,
/// Per-batch content hash from the last rebuild, indexed by batch position.
/// A hash mismatch triggers a `write_buffer` for that batch; a match skips it.
pub(crate) cached_instance_hashes: Vec<u64>,
/// Cached instanced batch descriptors from the last rebuild.
pub(crate) cached_batches: Vec<InstancedBatch>,
/// When true, the next cache-miss forces a full buffer upload instead of the
/// per-batch partial-upload path. Set by `force_dirty()` and consumed once.
pub(crate) force_full_upload: bool,
/// CPU-readable copy of the indirect draw args, one frame behind.
pub(crate) indirect_readback_buf: Option<wgpu::Buffer>,
/// Number of batches whose data was copied into `indirect_readback_buf` last frame.
pub(crate) indirect_readback_batch_count: u32,
/// True when `indirect_readback_buf` holds resolved data that has not yet
/// been mapped for readback.
pub(crate) indirect_readback_pending: bool,
/// True when a map of `indirect_readback_buf` is in flight. The cull pass
/// skips the copy while this is set so the buffer is not overwritten before
/// `prepare()` has read it.
pub(crate) indirect_map_inflight: bool,
/// In-flight indirect map status: 0 = pending, 1 = mapped, 2 = failed.
pub(crate) indirect_map_status: std::sync::Arc<std::sync::atomic::AtomicU8>,
}
impl InstancingState {
pub(crate) fn new(gpu_culling_supported: bool) -> Self {
Self {
batches: Vec::new(),
use_instancing: false,
gpu_culling_supported,
gpu_culling_enabled: gpu_culling_supported,
cull_resources: None,
last_scene_generation: u64::MAX,
last_selection_generation: u64::MAX,
last_scene_items_count: usize::MAX,
last_instancable_count: usize::MAX,
cached_instance_count: 0,
cached_instance_hashes: Vec::new(),
cached_batches: Vec::new(),
force_full_upload: false,
indirect_readback_buf: None,
indirect_readback_batch_count: 0,
indirect_readback_pending: false,
indirect_map_inflight: false,
indirect_map_status: std::sync::Arc::new(std::sync::atomic::AtomicU8::new(0)),
}
}
}