viewport_lib/resources/device_resources.rs
1//! `DeviceResources`: the device-shared GPU resource container, its content
2//! and per-viewport scope structs, and the small feature-resource clusters.
3
4use crate::resources::types::*;
5
6/// Per-viewport HDR/post-process GPU state.
7///
8/// Holds all viewport-size-dependent render targets, their associated bind
9/// groups, and the per-viewport uniform buffers used by the post-process
10/// pipeline. Created lazily in `ViewportRenderer::ensure_viewport_hdr` and
11/// resized automatically when the viewport dimensions change.
12///
13/// Shared infrastructure (pipelines, BGLs, samplers, placeholder textures,
14/// SSAO noise/kernel) lives on [`DeviceResources`] and is created once
15/// by `ensure_hdr_shared`.
16#[allow(dead_code)]
17pub(crate) struct ViewportHdrState {
18 // --- HDR scene target ---
19 pub hdr_texture: wgpu::Texture,
20 pub hdr_view: wgpu::TextureView,
21 pub hdr_depth_texture: wgpu::Texture,
22 pub hdr_depth_view: wgpu::TextureView,
23 pub hdr_depth_only_view: wgpu::TextureView,
24 pub hdr_stencil_only_view: wgpu::TextureView,
25
26 // --- Bloom ---
27 pub bloom_threshold_texture: wgpu::Texture,
28 pub bloom_threshold_view: wgpu::TextureView,
29 pub bloom_ping_texture: wgpu::Texture,
30 pub bloom_ping_view: wgpu::TextureView,
31 pub bloom_pong_texture: wgpu::Texture,
32 pub bloom_pong_view: wgpu::TextureView,
33
34 // --- SSAO ---
35 pub ssao_texture: wgpu::Texture,
36 pub ssao_view: wgpu::TextureView,
37 pub ssao_blur_texture: wgpu::Texture,
38 pub ssao_blur_view: wgpu::TextureView,
39
40 // --- Depth of field ---
41 pub dof_texture: wgpu::Texture,
42 pub dof_view: wgpu::TextureView,
43 pub dof_bind_group: wgpu::BindGroup,
44 pub dof_uniform_buf: wgpu::Buffer,
45
46 // --- Contact shadow ---
47 pub contact_shadow_texture: wgpu::Texture,
48 pub contact_shadow_view: wgpu::TextureView,
49
50 // --- Surface LIC ---
51 /// Encodes screen-space flow vector per surface pixel (Rgba8Unorm, viewport-sized).
52 pub lic_vector_texture: wgpu::Texture,
53 pub lic_vector_view: wgpu::TextureView,
54 /// LIC intensity after advection (R8Unorm, viewport-sized). Read by tone_map.wgsl binding 7.
55 pub lic_output_texture: wgpu::Texture,
56 pub lic_output_view: wgpu::TextureView,
57 /// Per-pixel white noise (R8Unorm, viewport-sized). One independent random value per pixel.
58 /// Sampled with textureLoad (nearest) in lic_advect.wgsl to produce directional LIC contrast.
59 pub lic_noise_texture: wgpu::Texture,
60 pub lic_noise_view: wgpu::TextureView,
61 /// Bind group for the LIC advect render pass (reads lic_vector_texture + lic_noise_texture).
62 pub lic_advect_bind_group: wgpu::BindGroup,
63 /// Uniform buffer for LicAdvectUniform (steps, step_size, viewport dims).
64 pub lic_uniform_buf: wgpu::Buffer,
65
66 // --- FXAA ---
67 pub fxaa_texture: wgpu::Texture,
68 pub fxaa_view: wgpu::TextureView,
69
70 // --- SSAA (allocated when ssaa_factor > 1) ---
71 /// Supersampled colour render target. `None` when ssaa_factor == 1.
72 pub ssaa_colour_texture: Option<wgpu::Texture>,
73 pub ssaa_colour_view: Option<wgpu::TextureView>,
74 /// Supersampled depth render target. `None` when ssaa_factor == 1.
75 pub ssaa_depth_texture: Option<wgpu::Texture>,
76 pub ssaa_depth_view: Option<wgpu::TextureView>,
77 /// Depth-aspect-only view of `ssaa_depth_texture`, used as the soft-particle
78 /// sample source during the SSAA sprite post-pass. `None` when SSAA is off.
79 pub ssaa_depth_only_view: Option<wgpu::TextureView>,
80 /// Bind group for the SSAA resolve pass (reads ssaa_colour_texture). `None` when ssaa_factor == 1.
81 pub ssaa_resolve_bind_group: Option<wgpu::BindGroup>,
82 /// Uniform buffer holding the ssaa_factor value for the resolve shader.
83 pub ssaa_uniform_buf: Option<wgpu::Buffer>,
84 /// The ssaa_factor this state was created with (1 = no SSAA).
85 pub ssaa_factor: u32,
86
87 // --- OIT (lazily allocated when transparent geometry is present) ---
88 pub oit_accum_texture: Option<wgpu::Texture>,
89 pub oit_accum_view: Option<wgpu::TextureView>,
90 pub oit_reveal_texture: Option<wgpu::Texture>,
91 pub oit_reveal_view: Option<wgpu::TextureView>,
92 pub oit_composite_bind_group: Option<wgpu::BindGroup>,
93 pub oit_size: [u32; 2],
94
95 // --- Outline offscreen (used by the outline prepare pass) ---
96 /// R8Unorm mask: selected objects rendered as white on black.
97 pub outline_mask_texture: wgpu::Texture,
98 pub outline_mask_view: wgpu::TextureView,
99 /// RGBA output of the edge-detection pass (composited onto the main target).
100 pub outline_colour_texture: wgpu::Texture,
101 pub outline_colour_view: wgpu::TextureView,
102 pub outline_depth_texture: wgpu::Texture,
103 pub outline_depth_view: wgpu::TextureView,
104 /// Depth-aspect view of `outline_depth_texture` for sampling (the HiZ
105 /// occlusion prev-depth copy on the LDR path).
106 pub outline_depth_only_view: wgpu::TextureView,
107 /// Bind group for the edge-detection pass (reads mask, writes to colour).
108 pub outline_edge_bind_group: wgpu::BindGroup,
109 /// Uniform buffer for the edge-detection pass parameters.
110 pub outline_edge_uniform_buf: wgpu::Buffer,
111 pub outline_composite_bind_group: wgpu::BindGroup,
112
113 // --- Bind groups (rebuilt when viewport dimensions change) ---
114 pub tone_map_bind_group: wgpu::BindGroup,
115 pub bloom_threshold_bg: wgpu::BindGroup,
116 /// H-blur bind group that reads from bloom_threshold (pass 0 only).
117 pub bloom_blur_h_bg: wgpu::BindGroup,
118 /// V-blur bind group that reads from bloom_ping.
119 pub bloom_blur_v_bg: wgpu::BindGroup,
120 /// H-blur bind group that reads from bloom_pong (passes 1+).
121 pub bloom_blur_h_pong_bg: wgpu::BindGroup,
122 pub ssao_bg: wgpu::BindGroup,
123 pub ssao_blur_bg: wgpu::BindGroup,
124 pub dof_bg: wgpu::BindGroup,
125 pub contact_shadow_bg: wgpu::BindGroup,
126 pub fxaa_bind_group: wgpu::BindGroup,
127
128 // --- Per-viewport uniform buffers ---
129 pub tone_map_uniform_buf: wgpu::Buffer,
130 pub bloom_uniform_buf: wgpu::Buffer,
131 /// Constant H-blur uniform buffer (horizontal=1, written once at creation).
132 pub bloom_h_uniform_buf: wgpu::Buffer,
133 /// Constant V-blur uniform buffer (horizontal=0, written once at creation).
134 pub bloom_v_uniform_buf: wgpu::Buffer,
135 pub ssao_uniform_buf: wgpu::Buffer,
136 pub contact_shadow_uniform_buf: wgpu::Buffer,
137
138 // --- Post-tone-map depth buffer (native resolution) ---
139 // When scene_size == output_size (render_scale = 1.0) this is None and
140 // hdr_depth_view is used directly for post-tone-map passes.
141 // When scene_size != output_size the scene depth is blitted into this
142 // native-resolution texture so that post-tone-map passes (grid, gizmos,
143 // axes, etc.) can use it as a depth attachment alongside output_view.
144 pub output_depth_texture: Option<wgpu::Texture>,
145 pub output_depth_view: wgpu::TextureView,
146 /// Bind group for the depth blit pass (reads hdr_depth_only_view).
147 /// None when scene_size == output_size (no blit needed).
148 pub depth_blit_bind_group: Option<wgpu::BindGroup>,
149
150 // --- HDR upscale (allocated when scene_size != output_size) ---
151 // When render_scale < 1.0, tone-map and FXAA run at scene resolution.
152 // The result is written to upscale_texture, then upscale-blitted to output_view.
153 pub upscale_texture: Option<wgpu::Texture>,
154 pub upscale_view: Option<wgpu::TextureView>,
155 pub upscale_bind_group: Option<wgpu::BindGroup>,
156
157 /// Native output resolution [width, height].
158 pub output_size: [u32; 2],
159 /// Effective scene resolution after render scale: [output_size * render_scale].
160 /// Equals output_size when render_scale = 1.0.
161 pub scene_size: [u32; 2],
162
163 // --- Decal pass depth binding (D1) ---
164 /// Bind group for group 1 of the decal pass: reads hdr_depth_only_view as a depth texture.
165 /// Rebuilt on viewport resize alongside the other viewport-sized bind groups.
166 pub decal_depth_bg: wgpu::BindGroup,
167}
168/// Per-viewport scatter-pass intermediates: two RGBA16F ping-pong targets
169/// driven by the temporal-accumulation logic, plus the composite bind groups
170/// and previous-frame view-projection used for reprojection.
171///
172/// Lives on `ViewportRenderer` (not `ViewportHdrState`) so that the scatter
173/// pass can allocate and mutate it without conflicting with the immutable
174/// `slot_hdr` borrow held across the larger paint phase.
175pub(crate) struct ScatterViewportState {
176 // Textures keep the GPU allocation alive; views are sampled or rendered
177 // into.
178 /// Per-volume scatter draws accumulate into this target each frame.
179 /// Cleared at the start of the scatter pass.
180 #[allow(dead_code)]
181 pub raw_current_texture: wgpu::Texture,
182 pub raw_current_view: wgpu::TextureView,
183 /// History ping-pong. The temporal-resolve pass reads one slot
184 /// (history_prev) and writes the other (history_new). `parity` selects.
185 #[allow(dead_code)]
186 pub history_a_texture: wgpu::Texture,
187 pub history_a_view: wgpu::TextureView,
188 #[allow(dead_code)]
189 pub history_b_texture: wgpu::Texture,
190 pub history_b_view: wgpu::TextureView,
191 /// Composite bind group reading the raw-current texture.
192 /// Used when temporal accumulation is disabled.
193 pub composite_bg_raw: wgpu::BindGroup,
194 /// Composite bind groups reading either history slot, used as the source
195 /// after the temporal-resolve pass has written history_new.
196 pub composite_bg_history_a: wgpu::BindGroup,
197 pub composite_bg_history_b: wgpu::BindGroup,
198 /// Temporal-resolve bind groups, keyed by which history slot is being
199 /// read as the previous-frame input. Each binds raw_current + the chosen
200 /// history slot.
201 pub temporal_resolve_bg_read_a: wgpu::BindGroup,
202 pub temporal_resolve_bg_read_b: wgpu::BindGroup,
203 /// Current allocated intermediate size, [width, height].
204 pub size: [u32; 2],
205 /// Whether `size` reflects the downsampled (half-res) allocation.
206 pub downsampled: bool,
207 /// Index of the history slot the next frame writes to (0 = A, 1 = B).
208 /// The other slot is read as the previous-frame history.
209 pub parity: u32,
210 /// True when the history slot opposite `parity` holds a usable
211 /// previous-frame composite result.
212 pub history_valid: bool,
213 /// Previous frame's view-projection (row-major mat4).
214 pub prev_view_proj: [[f32; 4]; 4],
215 /// Scene colour copy sampled by the refraction pass. Allocated on demand
216 /// when at least one volume has refraction enabled. Matches the HDR
217 /// target's size and format.
218 #[allow(dead_code)]
219 pub refraction_source_texture: Option<wgpu::Texture>,
220 /// View paired with `refraction_source_texture`. Bound as the source
221 /// during the refraction pass and as the render target during the
222 /// preceding blit-copy of the HDR scene.
223 pub refraction_source_view: Option<wgpu::TextureView>,
224 /// Per-viewport bind group binding `(refraction_source_view, depth)` to
225 /// the refraction pass.
226 pub refraction_source_bg: Option<wgpu::BindGroup>,
227 /// Per-viewport bind group binding the HDR view as the source for the
228 /// blit-copy that fills `refraction_source_view`.
229 pub refraction_blit_bg: Option<wgpu::BindGroup>,
230 /// Allocated size of the refraction source, matched to the HDR target.
231 pub refraction_source_size: [u32; 2],
232}
233/// A render pipeline compiled for both the LDR swapchain format and the HDR
234/// intermediate format (`Rgba16Float`). Used for pipelines that draw into the
235/// primary scene colour attachment, which may be either format depending on
236/// whether post-processing is active.
237pub(crate) struct DualPipeline {
238 pub ldr: wgpu::RenderPipeline,
239 pub hdr: wgpu::RenderPipeline,
240}
241
242impl DualPipeline {
243 /// Select the pipeline matching the current render target format.
244 /// Pass `true` when drawing into the HDR scene pass (`Rgba16Float`),
245 /// `false` when drawing into the LDR swapchain pass.
246 pub fn for_format(&self, hdr: bool) -> &wgpu::RenderPipeline {
247 if hdr { &self.hdr } else { &self.ldr }
248 }
249}
250
251/// GPU object-ID picking pipeline and its bind group layouts. Lazily built.
252#[derive(Default)]
253pub(crate) struct PickResources {
254 /// Render pipeline that outputs flat u32 object IDs to R32Uint + R32Float targets.
255 pub(crate) pipeline: Option<wgpu::RenderPipeline>,
256 /// Group 1 layout (PickInstance storage buffer).
257 pub(crate) bind_group_layout_1: Option<wgpu::BindGroupLayout>,
258 /// Minimal camera-only bind group layout (group 0).
259 pub(crate) camera_bgl: Option<wgpu::BindGroupLayout>,
260}
261
262/// GPU implicit-surface ray-march pipeline and layout. Lazily built.
263#[derive(Default)]
264pub(crate) struct ImplicitResources {
265 /// Render pipeline for GPU-side implicit surface ray-marching.
266 pub(crate) pipeline: Option<DualPipeline>,
267 /// Group 1 layout (ImplicitUniformRaw).
268 pub(crate) bgl: Option<wgpu::BindGroupLayout>,
269 /// Outline mask pipeline for implicit surfaces. None until first selected item.
270 pub(crate) outline_mask_pipeline: Option<wgpu::RenderPipeline>,
271}
272
273/// Screen-space image quad pipelines (plain + depth-composite) and the rect
274/// outline mask pipeline. Lazily built.
275#[derive(Default)]
276pub(crate) struct ScreenImageResources {
277 /// Render pipeline for screen-space image quads.
278 pub(crate) pipeline: Option<wgpu::RenderPipeline>,
279 /// Group 0 layout (uniform + texture + sampler).
280 pub(crate) bgl: Option<wgpu::BindGroupLayout>,
281 /// Depth-composite pipeline (LessEqual depth, per-pixel image depth).
282 pub(crate) dc_pipeline: Option<wgpu::RenderPipeline>,
283 /// Group 0 layout for the dc pipeline (uniform + colour + sampler + depth).
284 pub(crate) dc_bgl: Option<wgpu::BindGroupLayout>,
285 /// Outline mask pipeline for screen-space rect images. None until first selected.
286 pub(crate) rect_outline_mask_pipeline: Option<wgpu::RenderPipeline>,
287 /// Layout for the rect outline mask pipeline (NdcRectUniform).
288 pub(crate) rect_outline_bgl: Option<wgpu::BindGroupLayout>,
289}
290
291/// Sub-object highlight pipelines (fill / edge / sprite, HDR + LDR) and layout.
292/// Lazily built the first frame a sub-selection is present.
293#[derive(Default)]
294pub(crate) struct SubHighlightResources {
295 /// Translucent face fill pipeline (HDR).
296 pub(crate) fill_pipeline: Option<wgpu::RenderPipeline>,
297 /// Depth-nudged billboard edge-line pipeline (HDR).
298 pub(crate) edge_pipeline: Option<wgpu::RenderPipeline>,
299 /// Billboard sprite pipeline for vertex/point highlights (HDR).
300 pub(crate) sprite_pipeline: Option<wgpu::RenderPipeline>,
301 /// Translucent face fill pipeline (LDR).
302 pub(crate) fill_ldr_pipeline: Option<wgpu::RenderPipeline>,
303 /// Depth-nudged billboard edge-line pipeline (LDR).
304 pub(crate) edge_ldr_pipeline: Option<wgpu::RenderPipeline>,
305 /// Billboard sprite pipeline for vertex/point highlights (LDR).
306 pub(crate) sprite_ldr_pipeline: Option<wgpu::RenderPipeline>,
307 /// Shared group 1 layout (SubHighlightUniform).
308 pub(crate) bgl: Option<wgpu::BindGroupLayout>,
309}
310
311/// Projected-tetrahedra transparent volume pipeline, layouts, and LUT bind
312/// group cache. Lazily built.
313#[derive(Default)]
314pub(crate) struct ProjectedTetResources {
315 /// Render pipeline for the projected tetrahedra pass.
316 pub(crate) pipeline: Option<wgpu::RenderPipeline>,
317 /// Group 1 layout (per-volume uniform + tet storage buffer).
318 pub(crate) bind_group_layout: Option<wgpu::BindGroupLayout>,
319 /// Group 2 layout (per-frame colourmap LUT + sampler).
320 pub(crate) lut_bind_group_layout: Option<wgpu::BindGroupLayout>,
321 /// Cache of LUT bind groups keyed by colourmap slot index.
322 pub(crate) lut_bind_groups: std::collections::HashMap<usize, wgpu::BindGroup>,
323 /// LUT bind group for the fallback colourmap.
324 pub(crate) fallback_lut_bind_group: Option<wgpu::BindGroup>,
325}
326
327/// Selection-outline and x-ray pipelines, the offscreen mask/composite targets,
328/// and their layouts. The mask/edge/xray/splat pipelines are built eagerly at
329/// init; the offscreen textures and composite pipelines are lazily created.
330pub(crate) struct OutlineResources {
331 /// Group 1 layout for OutlineUniform (mask/xray pipelines).
332 pub(crate) bind_group_layout: wgpu::BindGroupLayout,
333 /// Mask-write pipeline: selected objects as r=1.0 to an R8 mask.
334 pub(crate) mask_pipeline: wgpu::RenderPipeline,
335 /// Two-sided mask-write pipeline (no face culling).
336 pub(crate) mask_two_sided_pipeline: wgpu::RenderPipeline,
337 /// Fullscreen edge-detection pipeline: reads mask, outputs the outline ring.
338 pub(crate) edge_pipeline: wgpu::RenderPipeline,
339 /// Layout for the edge-detection pass (mask texture + sampler + uniform).
340 pub(crate) edge_bgl: wgpu::BindGroupLayout,
341 /// X-ray pipeline: draws selected objects through occluders (depth Always).
342 pub(crate) xray_pipeline: wgpu::RenderPipeline,
343 /// Billboard disc pipeline for the Gaussian splat outline mask pass.
344 pub(crate) splat_mask_pipeline: wgpu::RenderPipeline,
345 /// Offscreen RGBA texture the outline stencil pass renders into.
346 pub(crate) colour_texture: Option<wgpu::Texture>,
347 pub(crate) colour_view: Option<wgpu::TextureView>,
348 /// Depth+stencil texture for the offscreen outline pass.
349 pub(crate) depth_texture: Option<wgpu::Texture>,
350 pub(crate) depth_view: Option<wgpu::TextureView>,
351 /// Size of the current outline offscreen textures.
352 pub(crate) target_size: [u32; 2],
353 /// Fullscreen composite pipelines: single-sample LDR, MSAA, HDR.
354 pub(crate) composite_pipeline_single: Option<wgpu::RenderPipeline>,
355 pub(crate) composite_pipeline_msaa: Option<wgpu::RenderPipeline>,
356 pub(crate) composite_pipeline_hdr: Option<wgpu::RenderPipeline>,
357 pub(crate) composite_bgl: Option<wgpu::BindGroupLayout>,
358 pub(crate) composite_bind_group: Option<wgpu::BindGroup>,
359 pub(crate) composite_sampler: Option<wgpu::Sampler>,
360}
361
362/// Image slice render pipeline and layout. Lazily built.
363#[derive(Default)]
364pub(crate) struct ImageSliceResources {
365 /// Image slice render pipeline. None until first slice item is submitted.
366 pub(crate) pipeline: Option<DualPipeline>,
367 /// Group 1 layout for image slice uniforms.
368 pub(crate) bgl: Option<wgpu::BindGroupLayout>,
369}
370
371/// Former name of [`DeviceResources`]. Renamed to reflect that this holds the
372/// device-shared resources, not per-viewport state. Kept as an alias so existing
373/// code keeps compiling; prefer `DeviceResources` in new code.
374#[deprecated(note = "renamed to DeviceResources")]
375pub type ViewportGpuResources = DeviceResources;
376
377/// Uploaded GPU assets and their handle registries: user textures, geometry /
378/// scivis stores, colourmap and matcap tables, plus the fallback LUT and the
379/// zero-fill attribute buffers bound when an optional attribute is absent.
380///
381/// This is the content-residency set: everything addressed by a handle
382/// (`TextureId`, `PolylineId`, `ColourmapId`, ...) or provided as a default when
383/// a handle is missing. It carries no pipelines; the upload / registry methods
384/// stay on `DeviceResources` and reach these through `self.content`. The shared
385/// material and LUT samplers and the fallback material textures stay on the core
386/// because the lit pass samples them on every draw.
387pub struct ContentResources {
388 /// Cache of material bind groups keyed by (albedo_id, normal_map_id, ao_map_id).
389 /// u64::MAX sentinel = use fallback texture for that slot.
390 #[allow(dead_code)]
391 pub(crate) material_bind_groups: std::collections::HashMap<(u64, u64, u64), wgpu::BindGroup>,
392 /// User-uploaded textures, keyed by the `texture_id` in Material. Slotted
393 /// with generational ids so a freed slot cannot alias a later upload.
394 pub(crate) textures: crate::resources::material::texture_store::TextureStore,
395 /// Pre-uploaded polyline storage; entries are referenced from per-frame
396 /// `PolylineRefItem`s.
397 pub(crate) polyline_store: super::PolylineStore,
398 /// Pre-uploaded streamtube storage.
399 pub(crate) streamtube_store: super::StreamtubeStore,
400 /// Pre-uploaded tube storage.
401 pub(crate) tube_store: super::TubeStore,
402 /// Pre-uploaded ribbon storage.
403 pub(crate) ribbon_store: super::RibbonStore,
404 /// Pre-uploaded point cloud storage.
405 pub(crate) point_cloud_store: super::PointCloudStore,
406 /// Pre-uploaded glyph set storage.
407 pub(crate) glyph_set_store: super::GlyphSetStore,
408 /// Pre-uploaded tensor glyph set storage.
409 pub(crate) tensor_glyph_set_store: super::TensorGlyphSetStore,
410 /// Pre-uploaded sprite set storage.
411 pub(crate) sprite_set_store: super::SpriteSetStore,
412 /// Pre-uploaded sprite instance set storage.
413 pub(crate) sprite_instance_set_store: super::SpriteInstanceSetStore,
414 /// Slotted store of all uploaded Gaussian splat sets.
415 pub(crate) gaussian_splat_store: GaussianSplatStore,
416 /// Uploaded 3D volume textures. Index = VolumeId value.
417 pub(crate) volume_textures:
418 crate::resources::handle::Registry<(wgpu::Texture, wgpu::TextureView)>,
419 /// Uploaded projected-tet meshes. Index = ProjectedTetId value.
420 pub(crate) projected_tet_store: crate::resources::handle::Registry<GpuProjectedTetMesh>,
421 /// Glyph atlas for overlay text rendering (labels, scalar bars, rulers).
422 pub(crate) glyph_atlas: crate::resources::overlay::font::GlyphAtlas,
423 /// Persistent textures uploaded via `upload_overlay_texture`.
424 pub(crate) overlay_textures: crate::resources::handle::Registry<OverlayShapeTextureEntry>,
425 /// Matcap textures (256x256 RGBA), indexed by `MatcapId::index`.
426 pub(crate) matcap_textures: Vec<wgpu::Texture>,
427 /// Texture views for each uploaded matcap.
428 pub(crate) matcap_views: Vec<wgpu::TextureView>,
429 /// Linear-clamp sampler shared by all matcap texture lookups.
430 pub(crate) matcap_sampler: Option<wgpu::Sampler>,
431 /// Fallback 1x1 white view bound to binding 7 when no matcap is active.
432 pub(crate) fallback_matcap_view: Option<wgpu::TextureView>,
433 /// Whether built-in matcaps have been uploaded to the GPU.
434 pub(crate) matcaps_initialized: bool,
435 /// `MatcapId` for each built-in preset, populated by `ensure_matcaps_initialized`.
436 pub(crate) builtin_matcap_ids: Option<[MatcapId; 8]>,
437 /// Uploaded colourmap GPU textures. Index = ColourmapId value.
438 pub(crate) colourmap_textures: Vec<wgpu::Texture>,
439 /// Views into colourmap_textures. Index = ColourmapId value.
440 pub(crate) colourmap_views: Vec<wgpu::TextureView>,
441 /// CPU-side copy of each colourmap for egui scalar bar rendering. Index = ColourmapId value.
442 pub(crate) colourmaps_cpu: Vec<[[u8; 4]; 256]>,
443 /// Fallback 1x1 LUT texture (bound when has_attribute=0; content irrelevant to the shader).
444 #[allow(dead_code)]
445 pub(crate) fallback_lut_texture: wgpu::Texture,
446 /// View of fallback_lut_texture.
447 pub(crate) fallback_lut_view: wgpu::TextureView,
448 /// Fallback 4-byte zero storage buffer (bound when no scalar attribute is active).
449 pub(crate) fallback_scalar_buf: wgpu::Buffer,
450 /// Fallback 16-byte zero storage buffer (bound to binding 8 when no face colour attribute is active).
451 pub(crate) fallback_face_colour_buf: wgpu::Buffer,
452 /// Fallback 12-byte zero storage buffer (bound to binding 9 when no warp attribute is active).
453 pub(crate) fallback_warp_buf: wgpu::Buffer,
454 /// Fallback 12-byte zero storage buffer (bound to binding 13 when no
455 /// position override is active). Single `vec3<f32>(0,0,0)` entry; the
456 /// shader bounds-checks `arrayLength` before reading.
457 pub(crate) fallback_position_override_buf: wgpu::Buffer,
458 /// Fallback 12-byte zero storage buffer (bound to binding 14 when no
459 /// normal override is active).
460 pub(crate) fallback_normal_override_buf: wgpu::Buffer,
461 /// IDs of built-in preset colourmaps, in BuiltinColourmap discriminant order.
462 /// `None` until `ensure_colourmaps_initialized()` has been called.
463 pub(crate) builtin_colourmap_ids: Option<[ColourmapId; 10]>,
464 /// Whether built-in colourmaps have been uploaded to the GPU.
465 pub(crate) colourmaps_initialized: bool,
466}
467
468/// Device-shared GPU resources: pipelines, layouts, samplers, fallbacks, LUTs,
469/// and the per-feature pipeline clusters (`decal`, `scatter`, `volume`, ...).
470/// Created once at init and shared across every viewport.
471///
472/// Typically stored in the host framework's resource container and accessed
473/// by `ViewportRenderer` during prepare() and paint().
474#[allow(dead_code)]
475pub struct DeviceResources {
476 /// Swapchain texture format; all pipelines are compiled for this format.
477 pub target_format: wgpu::TextureFormat,
478 /// MSAA sample count used by all render pipelines.
479 pub sample_count: u32,
480 /// Optional pipeline cache shared by every pipeline built here. `Some` only
481 /// when the device enables `Features::PIPELINE_CACHE`. Persist its contents
482 /// across runs with `ViewportRenderer::pipeline_cache_data` to skip shader
483 /// recompilation on later launches.
484 pub pipeline_cache: Option<wgpu::PipelineCache>,
485 /// Solid-shaded render pipeline (TriangleList topology, no blending).
486 pub solid_pipeline: wgpu::RenderPipeline,
487 /// Solid-shaded render pipeline with back-face culling disabled (two-sided surfaces).
488 pub solid_two_sided_pipeline: wgpu::RenderPipeline,
489 /// Transparent render pipeline (TriangleList topology, alpha blending).
490 pub transparent_pipeline: wgpu::RenderPipeline,
491 /// Wireframe render pipeline (LineList topology, same shader).
492 pub wireframe_pipeline: wgpu::RenderPipeline,
493 /// Uniform buffer holding the per-frame `CameraUniform` (view-proj + eye position).
494 pub camera_uniform_buf: wgpu::Buffer,
495 /// Uniform buffer holding the per-frame `LightsUniform` header (count +
496 /// hemisphere + IBL + debug params). The per-light array lives in
497 /// `light_storage_buf` (binding 13).
498 pub light_uniform_buf: wgpu::Buffer,
499 /// Storage buffer of per-light `SingleLightUniform` entries (binding 13).
500 ///
501 /// Sized for `MAX_SCENE_LIGHTS`. The renderer truncates the consumer's
502 /// light list to this cap each frame, ranking surplus lights by
503 /// `LightSource::importance * proximity_weight`.
504 pub light_storage_buf: wgpu::Buffer,
505 /// Clustered-shading state: cluster grid, global light index list, and the
506 /// per-frame cluster build pipeline. Bindings 14/15/16 of the camera bind
507 /// group expose this state to every lit pipeline.
508 pub clustered: crate::resources::gpu::clustered::ClusteredResources,
509 /// Bind group (group 0) binding camera, light, clip-plane, and shadow uniforms.
510 pub camera_bind_group: wgpu::BindGroup,
511 /// Bind group layout for group 0 (shared by all scene pipelines).
512 pub camera_bind_group_layout: wgpu::BindGroupLayout,
513 /// Bind group layout for group 1 (per-object uniform: model, material, selection).
514 pub object_bind_group_layout: wgpu::BindGroupLayout,
515 /// Scene meshes (slotted storage with free-list removal).
516 pub(crate) mesh_store: crate::resources::mesh::mesh_store::MeshStore,
517 /// Registered LOD groups. Each groups several meshes that are detail
518 /// variants of one object; the renderer picks a level per frame.
519 pub(crate) lod_groups: crate::resources::mesh::lod::LodGroupStore,
520 /// Per-vertex deformation sidecar storage: header uniform, dummy fallback
521 /// buffers, and per-mesh slot bind groups. Every mesh-family pipeline
522 /// binds `@group(2)` from this state; meshes without attached deformer
523 /// data fall back to the renderer-owned dummy bind group.
524 pub(crate) deform: crate::resources::mesh_sidecar::deform::DeformationState,
525 // --- Shadow map resources ---
526 /// Shadow atlas depth texture (Depth32Float, atlas_size x atlas_size, 2x2 tile grid).
527 pub shadow_map_texture: wgpu::Texture,
528 /// Depth texture view for binding as a shader resource (sampling).
529 pub shadow_map_view: wgpu::TextureView,
530 /// Comparison sampler for PCF shadow filtering.
531 pub shadow_sampler: wgpu::Sampler,
532 /// Cubemap-array depth texture for point-light shadows. Layered as
533 /// `MAX_POINT_SHADOW_LIGHTS * 6` faces of `POINT_SHADOW_FACE_SIZE` px.
534 pub point_shadow_cube_texture: wgpu::Texture,
535 /// `texture_depth_cube_array` view bound to the lit-pass bind group.
536 pub point_shadow_cube_view: wgpu::TextureView,
537 /// One 2D-array view per face, used as the depth attachment during the
538 /// shadow render pass. `len() == MAX_POINT_SHADOW_LIGHTS * 6`, indexed
539 /// as `slot * 6 + face`.
540 pub point_shadow_face_views: Vec<wgpu::TextureView>,
541 /// Render pipeline for the point-shadow depth pass. Same vertex layout
542 /// as the cascade shadow pipeline; writes linear distance-to-light.
543 pub shadow_point_pipeline: wgpu::RenderPipeline,
544 /// Bind group layout for the point-shadow per-face uniform (group 0
545 /// of the point shadow pass). Kept for pipeline rebuilds.
546 pub(crate) shadow_point_face_bind_group_layout: wgpu::BindGroupLayout,
547 /// Per-face uniform buffer holding `view_proj`, `light_pos`, `range`
548 /// for every (slot, face) of the point shadow array. Sized as
549 /// `MAX_POINT_SHADOW_LIGHTS * 6 * 256` bytes (256-byte dynamic-offset
550 /// stride).
551 pub shadow_point_face_buf: wgpu::Buffer,
552 /// Bind group for the point-shadow per-face uniform. Stride is 256;
553 /// the per-face render pass sets a dynamic offset.
554 pub shadow_point_face_bind_group: wgpu::BindGroup,
555 /// Render pipeline for the shadow depth pass (depth-only, no fragment output).
556 ///
557 /// Culls front faces, so closed solids cast shadow from their back face
558 /// and a solid's own front face is never compared against itself in the
559 /// shadow map. Two-sided materials (`BackfacePolicy::Identical` and
560 /// friends) are routed to `shadow_pipeline_two_sided` instead so both
561 /// sides of cloth, foliage, and planar surfaces cast shadows.
562 pub shadow_pipeline: wgpu::RenderPipeline,
563 /// Shadow caster pipeline for two-sided materials. Same layout and shader
564 /// as `shadow_pipeline` but with `cull_mode: None` and a larger caster-side
565 /// depth bias (`CSM_SHADOW_BIAS_TWO_SIDED`) so both sides of a two-sided
566 /// mesh rasterise into the shadow atlas without the surface self-shadowing
567 /// where it is its own receiver.
568 pub shadow_pipeline_two_sided: wgpu::RenderPipeline,
569 /// Bind group layout for the shadow camera uniform (group 0 of the
570 /// shadow pass). Kept on the renderer so `register_deformer` can rebuild
571 /// the shadow pipeline from a freshly composed shader module.
572 pub(crate) shadow_camera_bind_group_layout: wgpu::BindGroupLayout,
573 /// Uniform buffer holding the per-cascade light-space view-projection matrix (64 bytes).
574 pub shadow_uniform_buf: wgpu::Buffer,
575 /// Bind group for the shadow pass (group 0: light uniform).
576 pub shadow_bind_group: wgpu::BindGroup,
577 /// Uniform buffer for the ShadowAtlasUniform (binding 5 of camera_bgl, 416 bytes).
578 pub shadow_info_buf: wgpu::Buffer,
579 /// Current shadow atlas texture size. Used to detect when atlas needs recreation.
580 #[allow(dead_code)]
581 pub(crate) shadow_atlas_size: u32,
582 /// Non-comparison sampler for reading depth values as float (atlas viewer).
583 pub shadow_atlas_depth_sampler: wgpu::Sampler,
584 /// Pipeline for the shadow atlas corner overlay.
585 pub shadow_atlas_viewer_pipeline: wgpu::RenderPipeline,
586 /// Bind group for the atlas viewer (uniform + depth texture + sampler).
587 pub shadow_atlas_viewer_bg: wgpu::BindGroup,
588 /// Uniform buffer: NDC rect of the atlas viewer quad.
589 pub shadow_atlas_viewer_buf: wgpu::Buffer,
590 /// 16-byte sentinel bound at group 0 binding 12 when the debug fragment buffer is inactive.
591 pub debug_frag_sentinel_buf: wgpu::Buffer,
592
593 // --- Gizmo resources ---
594 /// Gizmo render pipeline (TriangleList, depth_compare Always : always on top).
595 pub gizmo_pipeline: wgpu::RenderPipeline,
596 /// Gizmo vertex buffer (3 axis arrows, regenerated when hovered axis changes).
597 pub gizmo_vertex_buffer: wgpu::Buffer,
598 /// Gizmo index buffer.
599 pub gizmo_index_buffer: wgpu::Buffer,
600 /// Number of indices in the gizmo index buffer.
601 pub gizmo_index_count: u32,
602 /// Gizmo uniform buffer (model matrix: positions gizmo at selected object, scaled to screen size).
603 pub gizmo_uniform_buf: wgpu::Buffer,
604 /// Bind group for gizmo uniform (group 1).
605 pub gizmo_bind_group: wgpu::BindGroup,
606 /// Bind group layout for gizmo uniforms : stored so per-viewport gizmo bind groups can be created.
607 pub(crate) gizmo_bind_group_layout: wgpu::BindGroupLayout,
608
609 // --- Overlay resources ---
610 /// Overlay render pipeline (TriangleList with alpha blending : for semi-transparent BC quads).
611 pub overlay_pipeline: wgpu::RenderPipeline,
612 /// Overlay wireframe pipeline (LineList, no alpha blending needed).
613 pub overlay_line_pipeline: wgpu::RenderPipeline,
614 /// Full-screen analytical grid pipeline (no vertex buffer : positions hardcoded in shader).
615 pub grid_pipeline: wgpu::RenderPipeline,
616 /// Uniform buffer for the grid shader (GridUniform : written every frame in prepare()).
617 pub grid_uniform_buf: wgpu::Buffer,
618 /// Bind group for the grid uniform (group 0, single binding).
619 pub grid_bind_group: wgpu::BindGroup,
620 /// Bind group layout for the grid uniform (stored so per-viewport grid bind groups can be created).
621 pub(crate) grid_bind_group_layout: wgpu::BindGroupLayout,
622 /// Bind group layout for overlay uniforms (group 1: model + colour uniform).
623 pub overlay_bind_group_layout: wgpu::BindGroupLayout,
624
625 // --- Constraint guide lines ---
626 /// Transient constraint guide lines, rebuilt each frame in prepare().
627 /// Each entry: (vertex_buffer, index_buffer, index_count, uniform_buffer, bind_group).
628 pub constraint_line_buffers: Vec<(
629 wgpu::Buffer,
630 wgpu::Buffer,
631 u32,
632 wgpu::Buffer,
633 wgpu::BindGroup,
634 )>,
635
636 // --- Axes indicator ---
637 /// Screen-space axes indicator pipeline (TriangleList, no depth, alpha blending).
638 pub axes_pipeline: wgpu::RenderPipeline,
639 /// Vertex buffer for axes indicator geometry (rebuilt each frame).
640 pub axes_vertex_buffer: wgpu::Buffer,
641 /// Number of vertices in the axes indicator buffer.
642 pub axes_vertex_count: u32,
643
644 // --- Texture system ---
645 /// Bind group layout for texture group (group 2: albedo + sampler + normal_map + ao_map).
646 pub texture_bind_group_layout: wgpu::BindGroupLayout,
647 /// Fallback 1x1 white texture used when material.texture_id is None.
648 pub fallback_texture: GpuTexture,
649 /// Fallback 1x1 flat normal map [128,128,255,255] (tangent-space neutral).
650 pub(crate) fallback_normal_map: wgpu::Texture,
651 pub(crate) fallback_normal_map_view: wgpu::TextureView,
652 /// Fallback 1x1 AO map [255,255,255,255] (no occlusion).
653 pub(crate) fallback_ao_map: wgpu::Texture,
654 pub(crate) fallback_ao_map_view: wgpu::TextureView,
655 /// Fallback 1x1 metallic-roughness texture [0, 255, 255, 255].
656 /// G=1.0 and B=1.0 so scalar factors pass through unchanged when no ORM texture is set.
657 pub(crate) fallback_metallic_roughness_texture: wgpu::Texture,
658 pub(crate) fallback_metallic_roughness_texture_view: wgpu::TextureView,
659 /// Fallback 1x1 emissive texture [0, 0, 0, 255] (no emission).
660 pub(crate) fallback_emissive_texture: wgpu::Texture,
661 pub(crate) fallback_emissive_texture_view: wgpu::TextureView,
662 /// Shared linear-repeat sampler for material textures.
663 pub(crate) material_sampler: wgpu::Sampler,
664 /// Shared linear-clamp sampler for colourmap LUT lookups.
665 pub(crate) lut_sampler: wgpu::Sampler,
666 /// Uploaded GPU assets and their handle registries (textures, geometry /
667 /// scivis stores, colourmap and matcap tables, fallback LUT and attribute buffers).
668 pub(crate) content: ContentResources,
669 /// Background runner used by async upload entry points. Drained once per
670 /// frame during `prepare_scene` so completion is visible to the caller.
671 /// Wrapped in a mutex because mpsc receivers and boxed `FnOnce`
672 /// callbacks are `Send` but not `Sync`, and several host frameworks
673 /// require this struct to be `Sync`.
674 pub(crate) jobs: std::sync::Mutex<super::upload_jobs::JobRunner>,
675 /// Typed result slots for every async upload path, keyed by job id.
676 /// Grouped into one struct so the async bookkeeping is a single field
677 /// rather than a score of flat ones; see `upload_jobs::JobResults`.
678 pub(crate) job_results: super::upload_jobs::JobResults,
679
680 /// Whether fallback normal map / AO map pixels have been uploaded.
681 pub(crate) fallback_textures_uploaded: bool,
682
683 // --- Shared post-processing pipelines / layouts / samplers ---
684 /// FXAA/SSAA, bloom, SSAO, tone-map, DoF, contact shadows, placeholders,
685 /// PP samplers, depth blit, and dyn-res upscale. Viewport-sized targets and
686 /// per-frame uniforms live on `ViewportHdrState`.
687 pub(crate) post: crate::resources::postprocess::PostProcessResources,
688
689 // --- Clip planes ---
690 /// Uniform buffer for clip planes (binding 4 of camera bind group).
691 pub(crate) clip_planes_uniform_buf: wgpu::Buffer,
692 /// Uniform buffer for the extended clip volume (binding 6 of camera bind group, 128 bytes).
693 pub(crate) clip_volume_uniform_buf: wgpu::Buffer,
694
695 // --- Outline & x-ray resources ---
696 // The volume outline mask pipeline lives on `volume.outline_mask_pipeline`;
697 // the glyph / tensor-glyph ones on `glyph.outline_mask_pipeline` and
698 // `tensor_glyph.outline_mask_pipeline`.
699 /// Outline / x-ray pipelines, offscreen mask/composite targets, and layouts.
700 pub(crate) outline: OutlineResources,
701
702 // --- Instancing and GPU-culling clusters ---
703 /// Instanced-draw pipelines, shared instance storage buffer, and bind group cache.
704 pub(crate) instancing: crate::resources::mesh::instancing::InstancingResources,
705 /// GPU-cull inputs (per-instance AABBs, per-batch meta) and cull-variant pipelines.
706 /// The cull OUTPUTS (visibility indices, indirect args, batch counters) are
707 /// per-viewport and live in `ViewportCullState` on each `ViewportSlot`, not here.
708 pub(crate) cull: crate::resources::mesh::instancing::CullResources,
709
710 // --- Surface LIC shared resources ---
711 /// Surface LIC pipelines and layouts (surface + advect passes).
712 pub(crate) lic: crate::resources::postprocess::LicResources,
713
714 /// HDR-format variants of core scene pipelines.
715 pub(crate) hdr_solid_pipeline: Option<wgpu::RenderPipeline>,
716 /// HDR two-sided variant (cull_mode: None) for analytical surfaces.
717 pub(crate) hdr_solid_two_sided_pipeline: Option<wgpu::RenderPipeline>,
718 pub(crate) hdr_transparent_pipeline: Option<wgpu::RenderPipeline>,
719 pub(crate) hdr_wireframe_pipeline: Option<wgpu::RenderPipeline>,
720 /// HDR overlay pipeline (TriangleList, Rgba16Float, alpha blending) for cap fill in HDR path.
721 pub(crate) hdr_overlay_pipeline: Option<wgpu::RenderPipeline>,
722
723 // --- Gaussian splat pipelines (lazily created) ---
724 /// Gaussian splat render/sort pipelines and their bind group layouts.
725 pub(crate) gaussian_splat: crate::resources::scivis::gaussian_splat::GaussianSplatResources,
726
727 // --- Sprite billboard pipelines (lazily created) ---
728 /// Sprite (emissive + lit) pipelines, layouts, refraction, and soft-particle fallbacks.
729 pub(crate) sprite: crate::resources::scivis::sprite::SpriteResources,
730 // The polyline outline mask pipeline lives on `polyline.outline_mask_pipeline`.
731
732 // --- point cloud pipelines (lazily created) ---
733 /// Point cloud render pipeline. None until first point cloud is submitted.
734 pub(crate) point_cloud_pipeline: Option<DualPipeline>,
735 /// Bind group layout for point cloud uniforms (group 1).
736 pub(crate) point_cloud_bgl: Option<wgpu::BindGroupLayout>,
737
738 // --- glyph rendering (lazily created) ---
739 /// Arrow/sphere/cube glyph pipelines, layouts, and cached base meshes.
740 pub(crate) glyph: crate::resources::scivis::glyph::GlyphResources,
741 /// Tensor glyph pipelines and layouts.
742 pub(crate) tensor_glyph: crate::resources::scivis::glyph::TensorGlyphResources,
743
744 // --- polyline / streamtube / ribbon rendering (lazily created) ---
745 /// Polyline pipelines and layouts.
746 pub(crate) polyline: crate::resources::scivis::polyline::PolylineResources,
747 /// Streamtube pipelines and layout.
748 pub(crate) streamtube: crate::resources::scivis::tube::StreamtubeResources,
749 /// Ribbon pipelines (one per blend) and layout.
750 pub(crate) ribbon: crate::resources::scivis::tube::RibbonResources,
751
752 // --- Image slice rendering (lazily created) ---
753 /// Image slice render pipeline and layout.
754 pub(crate) image_slice: ImageSliceResources,
755
756 // --- volume rendering (lazily created) ---
757 /// Volume render/surface-slice/outline pipelines, layouts, cube geometry, and default LUT.
758 pub(crate) volume: crate::resources::volume::volumes::VolumeResources,
759
760 // --- GPU compute filtering (lazily created) ---
761 /// Compute pipeline for Clip / Threshold index compaction. None until first use.
762 pub(crate) compute_filter_pipeline: Option<wgpu::ComputePipeline>,
763 /// Bind group layout for the compute filter shader (group 0). None until first use.
764 pub(crate) compute_filter_bgl: Option<wgpu::BindGroupLayout>,
765
766 // --- Order-independent transparency (OIT) : lazily created ---
767 // The viewport-sized accum/reveal textures, composite bind group, and target
768 // size live on ViewportHdrState; only the shared pipelines and layout sit here.
769 /// Weighted-blended OIT pipelines and composite layout.
770 pub(crate) oit: crate::resources::postprocess::OitResources,
771
772 // --- Projected tetrahedra transparent volume rendering (lazily created) ---
773 /// Projected-tetrahedra pipeline, layouts, and LUT bind group cache.
774 pub(crate) pt: ProjectedTetResources,
775
776 // --- Scatter-volume (participating media) rendering (lazily created) ---
777 /// Scatter-volume pipelines, layouts, and per-frame upload buffers.
778 pub(crate) scatter: crate::resources::volume::scatter_volume::ScatterResources,
779
780 // --- IBL / environment map resources ---
781 /// IBL irradiance equirect texture view (binding 7). None until environment uploaded.
782 pub ibl_irradiance_view: Option<wgpu::TextureView>,
783 /// IBL prefiltered specular equirect texture view (binding 8). None until environment uploaded.
784 pub ibl_prefiltered_view: Option<wgpu::TextureView>,
785 /// BRDF integration LUT texture view (binding 9). None until the first
786 /// `upload_environment_map`; cached across subsequent uploads (the LUT is
787 /// scene-independent: function of roughness x N.V only).
788 pub ibl_brdf_lut_view: Option<wgpu::TextureView>,
789 /// IBL linear-clamp sampler (binding 10).
790 pub(crate) ibl_sampler: wgpu::Sampler,
791 /// Skybox / full-res environment equirect texture view (binding 11). None until uploaded.
792 pub ibl_skybox_view: Option<wgpu::TextureView>,
793 /// Fallback 1x1 black Rgba16Float texture for IBL slots when no environment is loaded.
794 #[allow(dead_code)]
795 pub(crate) ibl_fallback_texture: wgpu::Texture,
796 /// View of ibl_fallback_texture.
797 pub(crate) ibl_fallback_view: wgpu::TextureView,
798 /// Fallback 1x1 BRDF LUT placeholder; swapped for the real 128x128 LUT
799 /// on the first `upload_environment_map` call. Bound to satisfy the bind
800 /// group layout when no environment map has been uploaded yet.
801 #[allow(dead_code)]
802 pub(crate) ibl_fallback_brdf_texture: wgpu::Texture,
803 pub(crate) ibl_fallback_brdf_view: wgpu::TextureView,
804 /// Uploaded irradiance texture (owned, kept alive for view).
805 #[allow(dead_code)]
806 pub(crate) ibl_irradiance_texture: Option<wgpu::Texture>,
807 /// Uploaded prefiltered specular texture (owned).
808 #[allow(dead_code)]
809 pub(crate) ibl_prefiltered_texture: Option<wgpu::Texture>,
810 /// Uploaded BRDF LUT texture (owned).
811 #[allow(dead_code)]
812 pub(crate) ibl_brdf_lut_texture: Option<wgpu::Texture>,
813 /// Uploaded skybox equirect texture (owned).
814 #[allow(dead_code)]
815 pub(crate) ibl_skybox_texture: Option<wgpu::Texture>,
816 /// Skybox fullscreen render pipeline (renders equirect environment as background).
817 pub(crate) skybox_pipeline: wgpu::RenderPipeline,
818
819 // --- Ground plane ---
820 /// Full-screen ground plane render pipeline (alpha blending, LessEqual depth).
821 pub(crate) ground_plane_pipeline: wgpu::RenderPipeline,
822 /// Bind group layout for the ground plane (binding 0: uniform, 1: shadow depth, 2: comparison sampler).
823 pub(crate) _ground_plane_bgl: wgpu::BindGroupLayout,
824 /// Uniform buffer for GroundPlaneUniform (256 bytes, written each frame in prepare()).
825 pub(crate) ground_plane_uniform_buf: wgpu::Buffer,
826 /// Bind group for the ground plane pass (rebuilt when shadow atlas changes).
827 pub(crate) ground_plane_bind_group: wgpu::BindGroup,
828
829 // --- GPU implicit surface (lazily created) ---
830 /// Implicit-surface ray-march pipeline, layout, and outline mask.
831 pub(crate) implicit: ImplicitResources,
832
833 // --- GPU marching cubes (lazily created) ---
834 /// Marching-cubes compute/render pipelines, layouts, case tables, and per-item volumes.
835 pub(crate) mc: crate::resources::volume::gpu_marching_cubes::McResources,
836
837 // --- GPU particle systems ---
838 /// Particle compute/draw pipelines, their layouts, and the live systems.
839 pub(crate) particle: crate::resources::gpu::gpu_particles::ParticleResources,
840
841 // --- Screen-space image overlays (lazily created) ---
842 /// Screen-space image pipelines (plain + depth-composite) and rect outline mask.
843 pub(crate) screen_image: ScreenImageResources,
844
845 // --- GPU object-ID picking (lazily created) ---
846 /// Object-ID pick pipeline and its bind group layouts.
847 pub(crate) pick: PickResources,
848
849 // --- Sub-object highlight (lazily created) ---
850 /// Sub-object highlight pipelines (fill / edge / sprite, HDR + LDR) and layout.
851 pub(crate) sub_highlight: SubHighlightResources,
852
853 // --- Overlay text / SDF shape / backdrop-blur pipelines (lazily created) ---
854 /// Overlay text pipeline, layout, and sampler.
855 pub(crate) overlay_text: crate::resources::overlay::overlay_text::OverlayTextResources,
856 /// SDF overlay shape pipelines (solid + textured) and sampler.
857 pub(crate) overlay_shape: crate::resources::overlay::overlay_shape::OverlayShapeResources,
858 /// Backdrop blur pipeline, layout, and sampler.
859 pub(crate) backdrop_blur: crate::resources::overlay::overlay_shape::BackdropBlurResources,
860
861 // --- Depth blit pipeline (lazily created, shared across all viewports) ---
862 // Copies a scene-resolution depth texture to a native-resolution depth-only target.
863 // Used by the HDR path when render_scale < 1.0.
864 // The depth-blit and dynamic-resolution upscale pipelines live on `post`.
865
866 // --- Runtime performance tracking ---
867 /// Cumulative bytes of geometry data uploaded since the last `prepare()` reset.
868 ///
869 /// Incremented by `upload_mesh`, `upload_mesh_data`, and `replace_mesh_data`.
870 /// Read and reset at the start of each `prepare()` call to populate
871 /// `FrameStats::upload_bytes`.
872 pub frame_upload_bytes: u64,
873
874 // --- Screen-space decal pipelines (D1 + D5, lazily created) ---
875 /// Decal render/exclude pipelines and their bind group layouts.
876 pub(crate) decal: crate::resources::decal::DecalResources,
877
878 // --- HiZ occlusion culling ---
879 /// When true, the main-camera GPU cull runs the HiZ occlusion test on top
880 /// of the frustum test. Off by default (the test is scene-dependent and a
881 /// safety valve against the one-frame-stale depth source). The HiZ pyramid
882 /// itself is per-viewport and lives on `ViewportCullState::hiz`.
883 pub(crate) occlusion_culling_enabled: bool,
884}
885
886/// Per-viewport GPU culling outputs.
887///
888/// The frustum/occlusion cull runs against one camera and writes a compact
889/// visibility list plus the indirect draw args for that camera. Those results
890/// are viewport-specific: two viewports on different cameras must not share
891/// them, or the last one to run would clobber the other. The cull INPUTS
892/// (per-instance AABBs, per-batch meta) and all cull PIPELINES are
893/// camera-independent and stay on `DeviceResources`.
894///
895/// Owned by each `ViewportSlot`. The bind-group caches here reference this
896/// state's own buffers, so they are invalidated when those buffers resize.
897pub(crate) struct ViewportCullState {
898 /// Per-batch atomic counter buffer. Zeroed at the start of each cull dispatch.
899 pub(crate) batch_counter_buf: Option<wgpu::Buffer>,
900 /// Compact list of visible instance indices. Written by the compute cull pass.
901 pub(crate) visibility_index_buf: Option<wgpu::Buffer>,
902 pub(crate) visibility_index_capacity: usize,
903 /// Indirect draw args buffer for the main pass (one DrawIndexedIndirect per batch).
904 pub(crate) indirect_args_buf: Option<wgpu::Buffer>,
905 /// Capacity (in batches) of the counter and indirect-args buffers.
906 pub(crate) batch_output_capacity: usize,
907 /// Per-texture-key bind groups for the main cull pipelines.
908 /// Keyed by (albedo_id, normal_map_id, ao_map_id); invalidated when
909 /// `visibility_index_buf` is resized.
910 pub(crate) instance_cull_bind_groups:
911 std::collections::HashMap<(u64, u64, u64), wgpu::BindGroup>,
912 /// Generation of the shared instance buffers the main cull bind groups were
913 /// built against. When it falls behind `InstancingState::instance_gen` the
914 /// shared instance storage buffer was rebuilt, so those bind groups (which
915 /// bind it at binding 0) are stale and get cleared.
916 pub(crate) built_gen: u64,
917 /// Hierarchical-Z max-depth pyramid for this viewport's occlusion test.
918 /// Lazily created the first frame occlusion culling stores depth here, and
919 /// rebuilt when the depth target changes size. Per-viewport so two viewports
920 /// on different cameras reproject their own depth instead of clobbering a
921 /// shared pyramid.
922 pub(crate) hiz: Option<crate::resources::gpu::hiz::HizState>,
923}
924
925impl ViewportCullState {
926 pub(crate) fn new() -> Self {
927 Self {
928 batch_counter_buf: None,
929 visibility_index_buf: None,
930 visibility_index_capacity: 0,
931 indirect_args_buf: None,
932 batch_output_capacity: 0,
933 instance_cull_bind_groups: std::collections::HashMap::new(),
934 built_gen: u64::MAX,
935 hiz: None,
936 }
937 }
938
939 /// Allocate or grow this viewport's cull output buffers to fit the current
940 /// instance and batch counts. The visibility buffer grows with
941 /// `instance_count`; the counter and indirect-args buffers grow with
942 /// `batch_count`. Uses the same 2x growth as the shared input buffers. Bind
943 /// groups referencing a reallocated buffer are cleared.
944 pub(crate) fn ensure_outputs(
945 &mut self,
946 device: &wgpu::Device,
947 instance_count: u32,
948 batch_count: u32,
949 ) {
950 // Visibility buffer, sized like the shared AABB buffer.
951 let max_instances = (device.limits().max_storage_buffer_binding_size as usize)
952 / std::mem::size_of::<InstanceAabb>();
953 let instance_count = (instance_count as usize).min(max_instances);
954 if instance_count > self.visibility_index_capacity {
955 let new_cap = (instance_count * 2).max(64).min(max_instances);
956 let vis_size = (new_cap * std::mem::size_of::<u32>()) as u64;
957 self.visibility_index_buf = Some(device.create_buffer(&wgpu::BufferDescriptor {
958 label: Some("visibility_index_buf"),
959 size: vis_size,
960 usage: wgpu::BufferUsages::STORAGE | wgpu::BufferUsages::COPY_DST,
961 mapped_at_creation: false,
962 }));
963 self.visibility_index_capacity = new_cap;
964 // The cull bind groups bind the vis buffer at binding 5.
965 self.instance_cull_bind_groups.clear();
966 }
967
968 // Counter and indirect-args buffers, sized like the shared batch-meta buffer.
969 let max_batches = (device.limits().max_storage_buffer_binding_size as usize)
970 / std::mem::size_of::<BatchMeta>();
971 let batch_count = (batch_count as usize).min(max_batches);
972 if batch_count > self.batch_output_capacity {
973 let new_cap = (batch_count * 2).max(16).min(max_batches);
974 let counter_size = (new_cap * std::mem::size_of::<u32>()) as u64;
975 // wgpu::util::DrawIndexedIndirect is 5 x u32 = 20 bytes.
976 let indirect_size = (new_cap * 20) as u64;
977 self.batch_counter_buf = Some(device.create_buffer(&wgpu::BufferDescriptor {
978 label: Some("batch_counter_buf"),
979 size: counter_size,
980 usage: wgpu::BufferUsages::STORAGE | wgpu::BufferUsages::COPY_DST,
981 mapped_at_creation: false,
982 }));
983 // iOS Metal and Android (emulator and older devices) do not
984 // reliably support INDIRECT_EXECUTION. Leave these as None so the
985 // renderer falls back to direct draw calls.
986 if cfg!(not(any(target_os = "ios", target_os = "android"))) {
987 self.indirect_args_buf = Some(device.create_buffer(&wgpu::BufferDescriptor {
988 label: Some("indirect_args_buf"),
989 size: indirect_size,
990 usage: wgpu::BufferUsages::STORAGE
991 | wgpu::BufferUsages::INDIRECT
992 | wgpu::BufferUsages::COPY_DST
993 | wgpu::BufferUsages::COPY_SRC,
994 mapped_at_creation: false,
995 }));
996 }
997 self.batch_output_capacity = new_cap;
998 }
999 }
1000}
1001
1002/// Scene-scoped GPU culling outputs for the directional shadow cascades.
1003///
1004/// Shadows are fit to the primary camera and rendered once into a shared atlas,
1005/// so the shadow cull is not per-viewport: it runs once per frame and its
1006/// outputs live here rather than on any `ViewportSlot`. Owned by
1007/// `InstancingState`.
1008pub(crate) struct ShadowCullState {
1009 /// Per-batch atomic counter buffer. Zeroed at the start of each cascade's
1010 /// cull dispatch.
1011 pub(crate) batch_counter_buf: Option<wgpu::Buffer>,
1012 /// Per-cascade visibility index buffers (grow with the instance count).
1013 pub(crate) shadow_vis_bufs: [Option<wgpu::Buffer>; 4],
1014 /// Per-cascade indirect draw args buffers (grow with the batch count).
1015 pub(crate) shadow_indirect_bufs: [Option<wgpu::Buffer>; 4],
1016 /// Per-cascade instance+visibility bind groups. Invalidated when
1017 /// `shadow_vis_bufs` are reallocated.
1018 pub(crate) shadow_cull_instance_bgs: [Option<wgpu::BindGroup>; 4],
1019 /// Capacity (in instances) of `shadow_vis_bufs`.
1020 pub(crate) vis_capacity: usize,
1021 /// Capacity (in batches) of the counter and indirect-args buffers.
1022 pub(crate) batch_output_capacity: usize,
1023 /// Generation of the shared instance buffers the shadow cull bind groups were
1024 /// built against. Mirrors `ViewportCullState::built_gen`: when it falls behind
1025 /// `InstancingState::instance_gen` the instance storage buffer was rebuilt, so
1026 /// the bind groups (which bind it at binding 0) are stale.
1027 pub(crate) built_gen: u64,
1028}
1029
1030impl ShadowCullState {
1031 pub(crate) fn new() -> Self {
1032 Self {
1033 batch_counter_buf: None,
1034 shadow_vis_bufs: [None, None, None, None],
1035 shadow_indirect_bufs: [None, None, None, None],
1036 shadow_cull_instance_bgs: [None, None, None, None],
1037 vis_capacity: 0,
1038 batch_output_capacity: 0,
1039 built_gen: u64::MAX,
1040 }
1041 }
1042
1043 /// Allocate or grow the shadow cull output buffers to fit the current
1044 /// instance and batch counts. Mirrors `ViewportCullState::ensure_outputs`
1045 /// for the shadow cascades.
1046 pub(crate) fn ensure_outputs(
1047 &mut self,
1048 device: &wgpu::Device,
1049 instance_count: u32,
1050 batch_count: u32,
1051 ) {
1052 let max_instances = (device.limits().max_storage_buffer_binding_size as usize)
1053 / std::mem::size_of::<InstanceAabb>();
1054 let instance_count = (instance_count as usize).min(max_instances);
1055 if instance_count > self.vis_capacity {
1056 let new_cap = (instance_count * 2).max(64).min(max_instances);
1057 let vis_size = (new_cap * std::mem::size_of::<u32>()) as u64;
1058 for i in 0..4 {
1059 self.shadow_vis_bufs[i] = Some(device.create_buffer(&wgpu::BufferDescriptor {
1060 label: Some(&format!("shadow_vis_buf_{i}")),
1061 size: vis_size,
1062 usage: wgpu::BufferUsages::STORAGE | wgpu::BufferUsages::COPY_DST,
1063 mapped_at_creation: false,
1064 }));
1065 }
1066 self.vis_capacity = new_cap;
1067 // The shadow cull bind groups bind these vis buffers at binding 5.
1068 self.shadow_cull_instance_bgs = [None, None, None, None];
1069 }
1070
1071 let max_batches = (device.limits().max_storage_buffer_binding_size as usize)
1072 / std::mem::size_of::<BatchMeta>();
1073 let batch_count = (batch_count as usize).min(max_batches);
1074 if batch_count > self.batch_output_capacity {
1075 let new_cap = (batch_count * 2).max(16).min(max_batches);
1076 let counter_size = (new_cap * std::mem::size_of::<u32>()) as u64;
1077 let indirect_size = (new_cap * 20) as u64;
1078 self.batch_counter_buf = Some(device.create_buffer(&wgpu::BufferDescriptor {
1079 label: Some("shadow_batch_counter_buf"),
1080 size: counter_size,
1081 usage: wgpu::BufferUsages::STORAGE | wgpu::BufferUsages::COPY_DST,
1082 mapped_at_creation: false,
1083 }));
1084 if cfg!(not(any(target_os = "ios", target_os = "android"))) {
1085 for i in 0..4 {
1086 self.shadow_indirect_bufs[i] =
1087 Some(device.create_buffer(&wgpu::BufferDescriptor {
1088 label: Some(&format!("shadow_indirect_buf_{i}")),
1089 size: indirect_size,
1090 usage: wgpu::BufferUsages::STORAGE
1091 | wgpu::BufferUsages::INDIRECT
1092 | wgpu::BufferUsages::COPY_DST,
1093 mapped_at_creation: false,
1094 }));
1095 }
1096 }
1097 self.batch_output_capacity = new_cap;
1098 }
1099 }
1100}
1101
1102impl DeviceResources {
1103 /// Create a camera bind group (group 0) for the given per-viewport buffers.
1104 ///
1105 /// Per-viewport buffers (camera, clip planes, shadow info, clip volume) are
1106 /// passed explicitly. Scene-global resources (lights, shadow atlas, IBL) come
1107 /// from shared resources on `self`.
1108 ///
1109 /// NOTE: The initial bind group in `init.rs` is constructed inline (before
1110 /// `Self` exists). Keep the binding layout in sync when modifying either site.
1111 pub(crate) fn create_camera_bind_group(
1112 &self,
1113 device: &wgpu::Device,
1114 camera_buf: &wgpu::Buffer,
1115 clip_planes_buf: &wgpu::Buffer,
1116 shadow_info_buf: &wgpu::Buffer,
1117 clip_volume_buf: &wgpu::Buffer,
1118 debug_frag_buf: &wgpu::Buffer,
1119 label: &str,
1120 ) -> wgpu::BindGroup {
1121 let irr = self
1122 .ibl_irradiance_view
1123 .as_ref()
1124 .unwrap_or(&self.ibl_fallback_view);
1125 let spec = self
1126 .ibl_prefiltered_view
1127 .as_ref()
1128 .unwrap_or(&self.ibl_fallback_view);
1129 let brdf = self
1130 .ibl_brdf_lut_view
1131 .as_ref()
1132 .unwrap_or(&self.ibl_fallback_brdf_view);
1133 let skybox = self
1134 .ibl_skybox_view
1135 .as_ref()
1136 .unwrap_or(&self.ibl_fallback_view);
1137
1138 device.create_bind_group(&wgpu::BindGroupDescriptor {
1139 label: Some(label),
1140 layout: &self.camera_bind_group_layout,
1141 entries: &[
1142 wgpu::BindGroupEntry {
1143 binding: 0,
1144 resource: camera_buf.as_entire_binding(),
1145 },
1146 wgpu::BindGroupEntry {
1147 binding: 1,
1148 resource: wgpu::BindingResource::TextureView(&self.shadow_map_view),
1149 },
1150 wgpu::BindGroupEntry {
1151 binding: 2,
1152 resource: wgpu::BindingResource::Sampler(&self.shadow_sampler),
1153 },
1154 wgpu::BindGroupEntry {
1155 binding: 3,
1156 resource: self.light_uniform_buf.as_entire_binding(),
1157 },
1158 wgpu::BindGroupEntry {
1159 binding: 4,
1160 resource: clip_planes_buf.as_entire_binding(),
1161 },
1162 wgpu::BindGroupEntry {
1163 binding: 5,
1164 resource: shadow_info_buf.as_entire_binding(),
1165 },
1166 wgpu::BindGroupEntry {
1167 binding: 6,
1168 resource: clip_volume_buf.as_entire_binding(),
1169 },
1170 wgpu::BindGroupEntry {
1171 binding: 7,
1172 resource: wgpu::BindingResource::TextureView(irr),
1173 },
1174 wgpu::BindGroupEntry {
1175 binding: 8,
1176 resource: wgpu::BindingResource::TextureView(spec),
1177 },
1178 wgpu::BindGroupEntry {
1179 binding: 9,
1180 resource: wgpu::BindingResource::TextureView(brdf),
1181 },
1182 wgpu::BindGroupEntry {
1183 binding: 10,
1184 resource: wgpu::BindingResource::Sampler(&self.ibl_sampler),
1185 },
1186 wgpu::BindGroupEntry {
1187 binding: 11,
1188 resource: wgpu::BindingResource::TextureView(skybox),
1189 },
1190 wgpu::BindGroupEntry {
1191 binding: 12,
1192 resource: debug_frag_buf.as_entire_binding(),
1193 },
1194 wgpu::BindGroupEntry {
1195 binding: 13,
1196 resource: self.light_storage_buf.as_entire_binding(),
1197 },
1198 wgpu::BindGroupEntry {
1199 binding: 14,
1200 resource: self.clustered.grid_uniform_buf.as_entire_binding(),
1201 },
1202 wgpu::BindGroupEntry {
1203 binding: 15,
1204 resource: self.clustered.cluster_grid_buf.as_entire_binding(),
1205 },
1206 wgpu::BindGroupEntry {
1207 binding: 16,
1208 resource: self.clustered.light_index_buf.as_entire_binding(),
1209 },
1210 wgpu::BindGroupEntry {
1211 binding: 17,
1212 resource: wgpu::BindingResource::TextureView(&self.point_shadow_cube_view),
1213 },
1214 ],
1215 })
1216 }
1217}
1218
1219impl DeviceResources {
1220 /// Lazily create the GPU pick pipeline and associated bind group layouts.
1221 ///
1222 /// No-op if already created. Called from `ViewportRenderer::pick_scene_gpu`
1223 /// on first invocation : zero overhead when GPU picking is never used.
1224 pub(crate) fn ensure_pick_pipeline(&mut self, device: &wgpu::Device) {
1225 if self.pick.pipeline.is_some() {
1226 return;
1227 }
1228
1229 // --- group 0: pick camera bind group layout ---
1230 // Includes binding 0 (CameraUniform) and binding 6 (ClipVolumesUniform).
1231 // The full camera_bind_group_layout has many more bindings; a separate
1232 // minimal layout is cleaner and avoids binding unused resources.
1233 let pick_camera_bgl = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
1234 label: Some("pick_camera_bgl"),
1235 entries: &[
1236 wgpu::BindGroupLayoutEntry {
1237 binding: 0,
1238 visibility: wgpu::ShaderStages::VERTEX,
1239 ty: wgpu::BindingType::Buffer {
1240 ty: wgpu::BufferBindingType::Uniform,
1241 has_dynamic_offset: false,
1242 min_binding_size: None,
1243 },
1244 count: None,
1245 },
1246 wgpu::BindGroupLayoutEntry {
1247 binding: 6,
1248 visibility: wgpu::ShaderStages::FRAGMENT,
1249 ty: wgpu::BindingType::Buffer {
1250 ty: wgpu::BufferBindingType::Uniform,
1251 has_dynamic_offset: false,
1252 min_binding_size: None,
1253 },
1254 count: None,
1255 },
1256 ],
1257 });
1258
1259 // --- group 1: PickInstance storage buffer ---
1260 let pick_instance_bgl = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
1261 label: Some("pick_instance_bgl"),
1262 entries: &[wgpu::BindGroupLayoutEntry {
1263 binding: 0,
1264 visibility: wgpu::ShaderStages::VERTEX,
1265 ty: wgpu::BindingType::Buffer {
1266 ty: wgpu::BufferBindingType::Storage { read_only: true },
1267 has_dynamic_offset: false,
1268 min_binding_size: None,
1269 },
1270 count: None,
1271 }],
1272 });
1273
1274 let shader = crate::resources::builders::wgsl_module(
1275 device,
1276 "pick_id_shader",
1277 crate::resources::builders::wgsl_source!("pick_id"),
1278 );
1279
1280 let layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
1281 label: Some("pick_pipeline_layout"),
1282 bind_group_layouts: &[&pick_camera_bgl, &pick_instance_bgl],
1283 push_constant_ranges: &[],
1284 });
1285
1286 // Vertex layout: reuse the 64-byte Vertex stride but only declare position (location 0).
1287 let pick_vertex_layout = wgpu::VertexBufferLayout {
1288 array_stride: std::mem::size_of::<Vertex>() as wgpu::BufferAddress, // 64 bytes
1289 step_mode: wgpu::VertexStepMode::Vertex,
1290 attributes: &[wgpu::VertexAttribute {
1291 offset: 0,
1292 shader_location: 0,
1293 format: wgpu::VertexFormat::Float32x3,
1294 }],
1295 };
1296
1297 let pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
1298 label: Some("pick_pipeline"),
1299 layout: Some(&layout),
1300 vertex: wgpu::VertexState {
1301 module: &shader,
1302 entry_point: Some("vs_main"),
1303 buffers: &[pick_vertex_layout],
1304 compilation_options: wgpu::PipelineCompilationOptions::default(),
1305 },
1306 fragment: Some(wgpu::FragmentState {
1307 module: &shader,
1308 entry_point: Some("fs_main"),
1309 targets: &[
1310 // location 0: R32Uint object ID
1311 Some(wgpu::ColorTargetState {
1312 format: wgpu::TextureFormat::R32Uint,
1313 blend: None, // replace : no blending for integer targets
1314 write_mask: wgpu::ColorWrites::ALL,
1315 }),
1316 // location 1: R32Float depth
1317 Some(wgpu::ColorTargetState {
1318 format: wgpu::TextureFormat::R32Float,
1319 blend: None,
1320 write_mask: wgpu::ColorWrites::ALL,
1321 }),
1322 ],
1323 compilation_options: wgpu::PipelineCompilationOptions::default(),
1324 }),
1325 primitive: wgpu::PrimitiveState {
1326 topology: wgpu::PrimitiveTopology::TriangleList,
1327 front_face: wgpu::FrontFace::Ccw,
1328 cull_mode: None, // No culling: 3D meshes are often rendered two-sided; pick both faces.
1329 ..Default::default()
1330 },
1331 depth_stencil: Some(wgpu::DepthStencilState {
1332 format: wgpu::TextureFormat::Depth24PlusStencil8,
1333 depth_write_enabled: true,
1334 depth_compare: wgpu::CompareFunction::Less,
1335 stencil: wgpu::StencilState::default(),
1336 bias: wgpu::DepthBiasState::default(),
1337 }),
1338 multisample: wgpu::MultisampleState {
1339 count: 1, // pick pass is always 1x (no MSAA)
1340 ..Default::default()
1341 },
1342 multiview: None,
1343 cache: None,
1344 });
1345
1346 self.pick.camera_bgl = Some(pick_camera_bgl);
1347 self.pick.bind_group_layout_1 = Some(pick_instance_bgl);
1348 self.pick.pipeline = Some(pipeline);
1349 }
1350}