1use crate::engine::graphics::MeshUploader;
2use crate::engine::graphics::MsaaMode;
3use crate::engine::graphics::TextureUploader;
4use crate::engine::graphics::mesh::CpuMesh;
5use crate::engine::graphics::primitives::MeshHandle;
6use crate::engine::graphics::primitives::TextureHandle;
7use crate::engine::graphics::visual_world::VisualWorld;
8use std::sync::Arc;
9use winit::window::Window;
10
11mod vulkano_backend {
12 use std::collections::HashMap;
13 use std::mem::size_of;
14 use std::sync::Arc;
15 use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
16
17 use crate::engine::ecs::ComponentId;
18 use crate::engine::ecs::system::render_to_texture_system::INTERNAL_RENDERER_STENCIL_CLIP_DEBUG_SELECTOR;
19 use crate::engine::graphics::MsaaMode;
20 use crate::engine::graphics::mesh::{CpuMesh, CpuVertex};
21 use crate::engine::graphics::pipeline_descriptor_set_layouts::PipelineDescriptorSetLayouts;
22 use crate::engine::graphics::post_processing::{
23 PostProcessFrameTargets, PostProcessingConfig, PostProcessingRenderer,
24 };
25 use crate::engine::graphics::primitives::MeshHandle;
26 use crate::engine::graphics::primitives::TextureHandle;
27 use crate::engine::graphics::visual_world::{TextureFiltering, VisualWorld};
28 use crate::engine::graphics::vulkano_swapchain::VulkanoSwapchainState;
29 use crate::engine::graphics::vulkano_texture_upload;
30 use vulkano::buffer::{Buffer, BufferContents, BufferCreateInfo, BufferUsage, Subbuffer};
31 use vulkano::command_buffer::{
32 AutoCommandBufferBuilder, CommandBufferUsage, CopyBufferInfo, CopyImageInfo,
33 PrimaryCommandBufferAbstract, allocator::StandardCommandBufferAllocator,
34 };
35 use vulkano::command_buffer::{
36 ClearAttachment, ClearRect, RenderingAttachmentInfo, RenderingAttachmentResolveInfo,
37 RenderingInfo,
38 };
39 use vulkano::descriptor_set::allocator::StandardDescriptorSetAllocator;
40 use vulkano::descriptor_set::{DescriptorSet, WriteDescriptorSet};
41 use vulkano::format::ClearValue;
42 use vulkano::image::view::{ImageView, ImageViewCreateInfo};
43 use vulkano::image::{
44 Image, ImageAspects, ImageCreateInfo, ImageSubresourceRange, ImageType, ImageUsage,
45 SampleCount, SampleCounts,
46 };
47 use vulkano::memory::allocator::{
48 AllocationCreateInfo, MemoryTypeFilter, StandardMemoryAllocator,
49 };
50 use vulkano::pipeline::graphics::color_blend::{
51 AttachmentBlend, BlendFactor, BlendOp, ColorBlendAttachmentState, ColorBlendState,
52 ColorComponents,
53 };
54 use vulkano::pipeline::graphics::depth_stencil::{
55 CompareOp, DepthState, DepthStencilState, StencilOp, StencilOpState, StencilOps,
56 StencilState,
57 };
58 use vulkano::pipeline::graphics::input_assembly::InputAssemblyState;
59 use vulkano::pipeline::graphics::multisample::MultisampleState;
60 use vulkano::pipeline::graphics::rasterization::RasterizationState;
61 use vulkano::pipeline::graphics::subpass::PipelineRenderingCreateInfo;
62 use vulkano::pipeline::graphics::subpass::PipelineSubpassType;
63 use vulkano::pipeline::graphics::vertex_input::{
64 VertexInputAttributeDescription, VertexInputBindingDescription, VertexInputRate,
65 VertexInputState,
66 };
67 use vulkano::pipeline::graphics::viewport::{Scissor, Viewport, ViewportState};
68 use vulkano::pipeline::layout::{PipelineLayout, PipelineLayoutCreateInfo};
69
70 use vulkano::DeviceSize;
71 use vulkano::Version;
72 use vulkano::VulkanObject;
73 use vulkano::format::Format;
74 use vulkano::image::sampler::{
75 Filter, Sampler, SamplerAddressMode, SamplerCreateInfo, SamplerMipmapMode,
76 };
77 use vulkano::pipeline::{
78 DynamicState, GraphicsPipeline, Pipeline, PipelineBindPoint, PipelineShaderStageCreateInfo,
79 };
80 use vulkano::render_pass::{AttachmentLoadOp, AttachmentStoreOp};
81 use vulkano::swapchain::{self, SwapchainPresentInfo};
82 use vulkano::sync::{self, GpuFuture};
83 use vulkano::{Validated, VulkanError};
84 use vulkano_util::context::{VulkanoConfig, VulkanoContext};
85 use winit::window::Window;
86
87 fn env_flag(name: &str) -> bool {
88 std::env::var(name)
89 .ok()
90 .map(|s| {
91 let s = s.trim().to_ascii_lowercase();
92 s == "1" || s == "true" || s == "on" || s == "yes"
93 })
94 .unwrap_or(false)
95 }
96
97 fn env_usize(name: &str) -> Option<usize> {
98 std::env::var(name)
99 .ok()
100 .and_then(|s| s.trim().parse::<usize>().ok())
101 }
102
103 use vulkano::device::DeviceExtensions;
104
105 mod vulkano_cbb {
111 include!(concat!(
112 env!("CARGO_MANIFEST_DIR"),
113 "/src/engine/graphics/vulkano_cbb.rs"
114 ));
115 }
116
117 mod toon_mesh_vs {
118 vulkano_shaders::shader! {
119 ty: "vertex",
120 path: "assets/shaders/toon-mesh.vert",
121 }
122 }
123
124 mod mirror_mesh_vs {
125 vulkano_shaders::shader! {
126 ty: "vertex",
127 path: "assets/shaders/mirror-mesh.vert",
128 }
129 }
130
131 mod toon_mesh_fs {
132 vulkano_shaders::shader! {
133 ty: "fragment",
134 path: "assets/shaders/toon-mesh.frag",
135 }
136 }
137
138 mod emissive_toon_mesh_fs {
139 vulkano_shaders::shader! {
140 ty: "fragment",
141 path: "assets/shaders/emissive-toon-mesh.frag",
142 }
143 }
144
145 mod mirror_mesh_fs {
146 vulkano_shaders::shader! {
147 ty: "fragment",
148 path: "assets/shaders/mirror-mesh.frag",
149 }
150 }
151
152 mod skinned_toon_mesh_vs {
153 vulkano_shaders::shader! {
154 ty: "vertex",
155 path: "assets/shaders/skinned-toon-mesh.vert",
156 }
157 }
158
159 mod grid_mesh_vs {
160 vulkano_shaders::shader! {
161 ty: "vertex",
162 path: "assets/shaders/grid.vert",
163 }
164 }
165
166 mod grid_square_mesh_fs {
167 vulkano_shaders::shader! {
168 ty: "fragment",
169 path: "assets/shaders/grid-square.frag",
170 }
171 }
172
173 #[derive(BufferContents, Clone, Copy, Debug, Default)]
174 #[repr(C, align(16))]
175 pub struct CameraUBO {
176 pub view: [[f32; 4]; 4],
177 pub proj: [[f32; 4]; 4],
178 pub camera2d: [[f32; 4]; 3],
180 pub viewport: [f32; 2],
182 pub _pad0: [f32; 2],
183
184 pub ambient_light: [f32; 3],
186 pub _pad1: f32,
187 }
188
189 #[derive(BufferContents, Clone, Copy, Debug, Default)]
190 #[repr(C, align(16))]
191 struct MaterialUBO {
192 base_color: [f32; 4],
193 quant_steps: f32,
194 emissive: u32,
195 _pad0: u32,
196 _pad1: u32,
197 }
198
199 #[derive(BufferContents, Clone, Copy, Debug, Default)]
200 #[repr(C, align(16))]
201 struct GpuMat4 {
202 cols: [[f32; 4]; 4],
203 }
204
205 #[derive(BufferContents, Clone, Copy, Debug, Default)]
206 #[repr(C, align(16))]
207 struct DummyPerInstanceLightingSSBO {
208 _pad0: [u32; 4],
209 }
210
211 #[derive(
212 BufferContents,
213 vulkano::pipeline::graphics::vertex_input::Vertex,
214 Clone,
215 Copy,
216 Debug,
217 Default,
218 )]
219 #[repr(C)]
220 pub struct InstanceData {
221 #[format(R32G32B32A32_SFLOAT)]
222 pub i_model_c0: [f32; 4],
223 #[format(R32G32B32A32_SFLOAT)]
224 pub i_model_c1: [f32; 4],
225 #[format(R32G32B32A32_SFLOAT)]
226 pub i_model_c2: [f32; 4],
227 #[format(R32G32B32A32_SFLOAT)]
228 pub i_model_c3: [f32; 4],
229
230 #[format(R32G32B32A32_SFLOAT)]
231 pub i_color: [f32; 4],
232 #[format(R32_SFLOAT)]
233 pub i_emissive: f32,
234
235 #[format(R32_SFLOAT)]
236 pub i_opacity: f32,
237
238 #[format(R32_UINT)]
241 pub i_bones_base: u32,
242 #[format(R32_UINT)]
243 pub i_bones_count: u32,
244 }
245
246 #[derive(BufferContents, Debug, Clone, Copy, Default)]
248 #[repr(C)]
249 pub struct GpuSkinVertex {
250 pub joints0: [u16; 4],
251 pub weights0: [f32; 4],
252 }
253
254 #[derive(Debug, Clone)]
255 pub enum RenderViewKind {
256 Window,
257 XrEye {
258 eye: usize,
259 },
260 Mirror {
261 mirror_component: ComponentId,
262 family: crate::engine::graphics::visual_world::MirrorViewerFamily,
263 view_index: usize,
264 plane_origin: [f32; 3],
265 plane_normal: [f32; 3],
266 excluded_instance: Option<crate::engine::graphics::primitives::InstanceHandle>,
267 },
268 }
269
270 #[derive(Debug, Clone)]
271 pub struct RenderView {
272 pub view: [[f32; 4]; 4],
273 pub proj: [[f32; 4]; 4],
274 pub viewport: [f32; 2],
275 pub kind: RenderViewKind,
276 }
277
278 pub struct VulkanoGpuMesh {
279 #[allow(dead_code)]
280 pub vertices: Subbuffer<[CpuVertex]>,
281 #[allow(dead_code)]
282 pub skin_vertices: Option<Subbuffer<[GpuSkinVertex]>>,
283 #[allow(dead_code)]
284 pub indices: Subbuffer<[u32]>,
285 #[allow(dead_code)]
286 pub index_count: u32,
287 }
288
289 pub struct VulkanoGpuTexture {
290 pub view: Arc<ImageView>,
291 pub extent: [u32; 2],
292 pub format: Format,
293 }
294
295 pub struct VulkanoState {
296 #[allow(dead_code)]
297 pub context: VulkanoContext,
298 #[allow(dead_code)]
299 pub window: Arc<Window>,
300
301 #[allow(dead_code)]
302 pub swapchain_state: VulkanoSwapchainState,
303
304 #[allow(dead_code)]
305 pub command_buffer_allocator: Arc<StandardCommandBufferAllocator>,
306
307 #[allow(dead_code)]
308 pub descriptor_set_allocator: Arc<StandardDescriptorSetAllocator>,
309
310 pub post_processing_renderer: PostProcessingRenderer,
311
312 #[allow(dead_code)]
313 pub set_layouts: PipelineDescriptorSetLayouts,
314
315 #[allow(dead_code)]
316 pub meshes: HashMap<MeshHandle, VulkanoGpuMesh>,
317
318 pub textures: HashMap<TextureHandle, VulkanoGpuTexture>,
319 pub sampler_linear: Arc<Sampler>,
320 pub sampler_nearest: Arc<Sampler>,
321 pub sampler_nearest_mag: Arc<Sampler>,
322 pub default_white_texture: TextureHandle,
323
324 pub pipeline_toon_mesh: Arc<GraphicsPipeline>,
325 pub pipeline_toon_mesh_transparent: Arc<GraphicsPipeline>,
326 pub pipeline_toon_mesh_cutout: Arc<GraphicsPipeline>,
327 pub pipeline_toon_mesh_transparent_clipped: Arc<GraphicsPipeline>,
328 pub pipeline_toon_mesh_cutout_clipped: Arc<GraphicsPipeline>,
329
330 pub pipeline_mirror_mesh: Arc<GraphicsPipeline>,
331 pub pipeline_mirror_mesh_transparent: Arc<GraphicsPipeline>,
332 pub pipeline_mirror_mesh_cutout: Arc<GraphicsPipeline>,
333 pub pipeline_mirror_mesh_clipped: Arc<GraphicsPipeline>,
334 pub pipeline_mirror_mesh_transparent_clipped: Arc<GraphicsPipeline>,
335 pub pipeline_mirror_mesh_cutout_clipped: Arc<GraphicsPipeline>,
336
337 pub pipeline_grid_mesh: Arc<GraphicsPipeline>,
338 pub pipeline_grid_mesh_transparent: Arc<GraphicsPipeline>,
339 pub pipeline_grid_mesh_transparent_clipped: Arc<GraphicsPipeline>,
340
341 pub pipeline_emissive_toon_mesh: Arc<GraphicsPipeline>,
342 pub pipeline_emissive_toon_mesh_transparent: Arc<GraphicsPipeline>,
343 pub pipeline_emissive_toon_mesh_cutout: Arc<GraphicsPipeline>,
344 pub pipeline_emissive_toon_mesh_transparent_clipped: Arc<GraphicsPipeline>,
345 pub pipeline_emissive_toon_mesh_cutout_clipped: Arc<GraphicsPipeline>,
346 pub pipeline_emissive_prepass_toon_mesh: Arc<GraphicsPipeline>,
347 pub pipeline_emissive_prepass_toon_mesh_cutout: Arc<GraphicsPipeline>,
348 pub pipeline_emissive_prepass_depth_write_toon_mesh: Arc<GraphicsPipeline>,
349
350 pub pipeline_skinned_toon_mesh: Arc<GraphicsPipeline>,
351 pub pipeline_skinned_toon_mesh_transparent: Arc<GraphicsPipeline>,
352 pub pipeline_skinned_toon_mesh_cutout: Arc<GraphicsPipeline>,
353
354 pub pipeline_skinned_emissive_toon_mesh: Arc<GraphicsPipeline>,
355 pub pipeline_skinned_emissive_toon_mesh_transparent: Arc<GraphicsPipeline>,
356 pub pipeline_skinned_emissive_toon_mesh_cutout: Arc<GraphicsPipeline>,
357 pub pipeline_skinned_emissive_prepass_toon_mesh: Arc<GraphicsPipeline>,
358 pub pipeline_skinned_emissive_prepass_toon_mesh_cutout: Arc<GraphicsPipeline>,
359 pub pipeline_skinned_emissive_prepass_depth_write_toon_mesh: Arc<GraphicsPipeline>,
360
361 pub pipeline_stencil_incr: Arc<GraphicsPipeline>,
363 pub pipeline_stencil_decr: Arc<GraphicsPipeline>,
365 pub pipeline_overlay_clipped: Arc<GraphicsPipeline>,
367 pub pipeline_emissive_overlay_clipped: Arc<GraphicsPipeline>,
369 pub pipeline_opaque_clipped: Arc<GraphicsPipeline>,
371 pub pipeline_emissive_opaque_clipped: Arc<GraphicsPipeline>,
373
374 pub msaa_samples: SampleCount,
375
376 cached_instance_buffer: Option<Subbuffer<[InstanceData]>>,
378 cached_instance_count: usize,
379
380 cached_background_instance_buffer: Option<Subbuffer<[InstanceData]>>,
381 cached_background_instance_count: usize,
382
383 cached_background_occluded_lit_instance_buffer: Option<Subbuffer<[InstanceData]>>,
384 cached_background_occluded_lit_instance_count: usize,
385
386 cached_cutout_instance_buffer: Option<Subbuffer<[InstanceData]>>,
387 cached_cutout_instance_count: usize,
388
389 cached_overlay_instance_buffer: Option<Subbuffer<[InstanceData]>>,
390 cached_overlay_instance_count: usize,
391 cached_material_sets: HashMap<
392 (
393 crate::engine::graphics::MaterialHandle,
394 TextureHandle,
395 TextureFiltering,
396 u32,
397 ),
398 Arc<DescriptorSet>,
399 >,
400 pending_runtime_texture_updates: HashMap<TextureHandle, VulkanoGpuTexture>,
401 window_runtime_debug_targets: Option<WindowRuntimeDebugTargets>,
402
403 cached_bones_buffers: Vec<Subbuffer<[GpuMat4]>>,
408 cached_bones_slot_valid: Vec<bool>,
409 cached_bones_capacity: usize,
410
411 xr_offscreen: Option<XrOffscreenTargets>,
412 mirror_offscreen: std::collections::HashMap<String, MirrorOffscreenTargets>,
413
414 pub window_resized: bool,
415 pub recreate_swapchain: bool,
416 pub images_in_flight: Vec<Option<Box<dyn GpuFuture>>>,
417 }
418
419 struct XrOffscreenTargets {
420 extent: [u32; 2],
421 color_format: Format,
422 color_images: Vec<Arc<vulkano::image::Image>>,
423 msaa_color_views: Vec<Arc<ImageView>>,
424 color_views: Vec<Arc<ImageView>>,
425 depth_views: Vec<Arc<ImageView>>,
427 }
428
429 struct MirrorOffscreenTargets {
430 extent: [u32; 2],
431 color_format: Format,
432 color_images: Vec<Arc<vulkano::image::Image>>,
433 msaa_color_views: Vec<Arc<ImageView>>,
434 color_views: Vec<Arc<ImageView>>,
435 depth_views: Vec<Arc<ImageView>>,
437 }
438
439 struct WindowRuntimeDebugTargets {
440 extent: [u32; 2],
441 color_format: Format,
442 msaa_color_views: Vec<Arc<ImageView>>,
443 color_views: Vec<Arc<ImageView>>,
444 }
445
446 #[derive(Clone)]
447 struct PostProcessInvocation {
448 final_output_view: Arc<ImageView>,
449 final_color_format: Format,
450 config: PostProcessingConfig,
451 targets: PostProcessFrameTargets,
452 }
453
454 const MAX_LIGHTS: usize = 64;
455
456 const LIGHT_TYPE_POINT: u32 = 1;
457 const LIGHT_TYPE_DIRECTIONAL: u32 = 2;
458
459 #[derive(BufferContents, Clone, Copy, Debug, Default)]
460 #[repr(C, align(16))]
461 struct GpuLight {
462 pos_intensity: [f32; 4],
464 color_distance: [f32; 4],
466 meta: [u32; 4],
469 }
470
471 #[derive(BufferContents, Clone, Copy, Debug)]
472 #[repr(C, align(16))]
473 struct LightsSSBO {
474 count: u32,
475 _pad0: [u32; 3],
476 lights: [GpuLight; MAX_LIGHTS],
477 }
478
479 impl Default for LightsSSBO {
480 fn default() -> Self {
481 Self {
482 count: 0,
483 _pad0: [0, 0, 0],
484 lights: [GpuLight::default(); MAX_LIGHTS],
485 }
486 }
487 }
488
489 impl VulkanoState {
490 fn sampler_for(&self, filtering: TextureFiltering) -> &Arc<Sampler> {
491 match filtering {
492 TextureFiltering::Linear => &self.sampler_linear,
493 TextureFiltering::Nearest => &self.sampler_nearest,
494 TextureFiltering::NearestMagnification => &self.sampler_nearest_mag,
495 }
496 }
497
498 fn create_material_ubo(
499 material: crate::engine::graphics::MaterialHandle,
500 quant_steps: f32,
501 ) -> MaterialUBO {
502 let quant_steps = if quant_steps.is_finite() {
503 quant_steps.clamp(1.0, 64.0)
504 } else {
505 3.0
506 };
507
508 match material {
509 crate::engine::graphics::MaterialHandle::TOON_MESH => MaterialUBO {
510 base_color: [1.0, 1.0, 1.0, 1.0],
511 quant_steps,
512 emissive: 0,
513 _pad0: 0,
514 _pad1: 0,
515 },
516 crate::engine::graphics::MaterialHandle::SKINNED_TOON_MESH => MaterialUBO {
517 base_color: [1.0, 1.0, 1.0, 1.0],
518 quant_steps,
519 emissive: 0,
520 _pad0: 0,
521 _pad1: 0,
522 },
523 crate::engine::graphics::MaterialHandle::EMISSIVE_TOON_MESH => MaterialUBO {
524 base_color: [1.0, 1.0, 1.0, 1.0],
525 quant_steps,
526 emissive: 0,
527 _pad0: 0,
528 _pad1: 0,
529 },
530 crate::engine::graphics::MaterialHandle::SKINNED_EMISSIVE_TOON_MESH => {
531 MaterialUBO {
532 base_color: [1.0, 1.0, 1.0, 1.0],
533 quant_steps,
534 emissive: 0,
535 _pad0: 0,
536 _pad1: 0,
537 }
538 }
539 crate::engine::graphics::MaterialHandle::GRID_MESH => MaterialUBO {
540 base_color: [1.0, 1.0, 1.0, 1.0],
541 quant_steps: 1.0,
542 emissive: 1,
543 _pad0: 0,
544 _pad1: 0,
545 },
546 crate::engine::graphics::MaterialHandle::UNLIT_MESH => MaterialUBO {
548 base_color: [1.0, 1.0, 1.0, 1.0],
549 quant_steps,
550 emissive: 1,
551 _pad0: 0,
552 _pad1: 0,
553 },
554 crate::engine::graphics::MaterialHandle::MIRROR => MaterialUBO {
555 base_color: [1.0, 1.0, 1.0, 1.0],
556 quant_steps: 1.0,
557 emissive: 1,
558 _pad0: 0,
559 _pad1: 0,
560 },
561 _ => MaterialUBO::default(),
562 }
563 }
564
565 pub fn new(
566 window: Arc<Window>,
567 xr_required: Option<(&[String], &[String])>,
568 msaa_mode: MsaaMode,
569 ) -> Result<Self, Box<dyn std::error::Error>> {
570 let context = {
573 let mut config = VulkanoConfig::default();
574
575 config.instance_create_info.max_api_version = Some(Version::V1_2);
578
579 config.device_extensions.khr_dynamic_rendering = true;
585 config.device_features.dynamic_rendering = true;
586
587 if let Some((instance_exts, device_exts)) = xr_required {
588 let mut enabled_instance_exts = config.instance_create_info.enabled_extensions;
589 let mut enabled_device_exts = config.device_extensions;
590
591 let mut unknown_instance_exts: Vec<&str> = Vec::new();
592 for name in instance_exts {
593 let ok = match name.as_str() {
594 "VK_KHR_get_physical_device_properties2" => {
595 enabled_instance_exts.khr_get_physical_device_properties2 = true;
596 true
597 }
598 "VK_KHR_external_memory_capabilities" => {
599 enabled_instance_exts.khr_external_memory_capabilities = true;
600 true
601 }
602 "VK_KHR_external_fence_capabilities" => {
603 enabled_instance_exts.khr_external_fence_capabilities = true;
604 true
605 }
606 "VK_KHR_external_semaphore_capabilities" => {
607 enabled_instance_exts.khr_external_semaphore_capabilities = true;
608 true
609 }
610 "VK_KHR_surface" => {
611 enabled_instance_exts.khr_surface = true;
613 true
614 }
615 _ => false,
616 };
617 if !ok {
618 unknown_instance_exts.push(name);
619 }
620 }
621
622 let mut unknown_device_exts: Vec<&str> = Vec::new();
623 for name in device_exts {
624 let ok = match name.as_str() {
625 "VK_KHR_external_memory" => {
626 enabled_device_exts.khr_external_memory = true;
627 true
628 }
629 "VK_KHR_external_memory_fd" => {
630 enabled_device_exts.khr_external_memory_fd = true;
631 true
632 }
633 "VK_KHR_external_fence" => {
634 enabled_device_exts.khr_external_fence = true;
635 true
636 }
637 "VK_KHR_external_fence_fd" => {
638 enabled_device_exts.khr_external_fence_fd = true;
639 true
640 }
641 "VK_KHR_external_semaphore" => {
642 enabled_device_exts.khr_external_semaphore = true;
643 true
644 }
645 "VK_KHR_external_semaphore_fd" => {
646 enabled_device_exts.khr_external_semaphore_fd = true;
647 true
648 }
649 "VK_KHR_get_memory_requirements2" => {
650 enabled_device_exts.khr_get_memory_requirements2 = true;
651 true
652 }
653 "VK_KHR_dedicated_allocation" => {
654 enabled_device_exts.khr_dedicated_allocation = true;
655 true
656 }
657 "VK_KHR_bind_memory2" => {
658 enabled_device_exts.khr_bind_memory2 = true;
659 true
660 }
661 "VK_KHR_timeline_semaphore" => {
662 enabled_device_exts.khr_timeline_semaphore = true;
663 true
664 }
665 "VK_KHR_image_format_list" => {
666 enabled_device_exts.khr_image_format_list = true;
667 true
668 }
669 _ => false,
670 };
671 if !ok {
672 unknown_device_exts.push(name);
673 }
674 }
675
676 config.instance_create_info.enabled_extensions = enabled_instance_exts;
677 config.device_extensions = enabled_device_exts;
678
679 let required_dev_exts: DeviceExtensions = enabled_device_exts;
681 config.device_filter_fn =
682 Arc::new(move |p| p.supported_extensions().contains(&required_dev_exts));
683
684 if !unknown_instance_exts.is_empty() || !unknown_device_exts.is_empty() {
685 eprintln!(
688 "[VulkanoRenderer] Note: some OpenXR-required Vulkan extensions were not mapped: instance={:?} device={:?}",
689 unknown_instance_exts, unknown_device_exts
690 );
691 }
692 }
693
694 VulkanoContext::new(config)
695 };
696 let device = context.device().clone();
697
698 let msaa_samples = match msaa_mode {
700 MsaaMode::Off => SampleCount::Sample1,
701 MsaaMode::Msaa4x => {
702 let props = device.physical_device().properties();
703 let counts = props.framebuffer_color_sample_counts
704 & props.framebuffer_depth_sample_counts;
705 if counts.intersects(SampleCounts::SAMPLE_4) {
706 SampleCount::Sample4
707 } else {
708 SampleCount::Sample1
709 }
710 }
711 };
712
713 match msaa_samples {
714 SampleCount::Sample4 => println!("[VulkanoRenderer] MSAA enabled: 4x"),
715 _ => println!("[VulkanoRenderer] MSAA disabled"),
716 }
717
718 let swapchain_state =
719 VulkanoSwapchainState::new(&context, window.clone(), msaa_samples)?;
720 let framebuffer_count = swapchain_state.swapchain_views.len();
721
722 let set_layouts = PipelineDescriptorSetLayouts::new(device.clone())?;
723
724 let vs = toon_mesh_vs::load(device.clone())?;
725 let mirror_vs = mirror_mesh_vs::load(device.clone())?;
726 let fs = toon_mesh_fs::load(device.clone())?;
727 let emissive_fs = emissive_toon_mesh_fs::load(device.clone())?;
728 let mirror_fs = mirror_mesh_fs::load(device.clone())?;
729 let grid_vs = grid_mesh_vs::load(device.clone())?;
730 let grid_fs = grid_square_mesh_fs::load(device.clone())?;
731
732 let skinned_vs = skinned_toon_mesh_vs::load(device.clone())?;
733
734 let stages = vec![
735 PipelineShaderStageCreateInfo::new(
736 vs.entry_point("main")
737 .ok_or("missing toon-mesh.vert entry point")?,
738 ),
739 PipelineShaderStageCreateInfo::new(
740 fs.entry_point("main")
741 .ok_or("missing toon-mesh.frag entry point")?,
742 ),
743 ];
744
745 let grid_stages = vec![
746 PipelineShaderStageCreateInfo::new(
747 grid_vs
748 .entry_point("main")
749 .ok_or("missing grid.vert entry point")?,
750 ),
751 PipelineShaderStageCreateInfo::new(
752 grid_fs
753 .entry_point("main")
754 .ok_or("missing grid-square.frag entry point")?,
755 ),
756 ];
757
758 let skinned_stages = vec![
759 PipelineShaderStageCreateInfo::new(
760 skinned_vs
761 .entry_point("main")
762 .ok_or("missing skinned-toon-mesh.vert entry point")?,
763 ),
764 PipelineShaderStageCreateInfo::new(
765 fs.entry_point("main")
766 .ok_or("missing toon-mesh.frag entry point")?,
767 ),
768 ];
769
770 let emissive_stages = vec![
771 PipelineShaderStageCreateInfo::new(
772 vs.entry_point("main")
773 .ok_or("missing toon-mesh.vert entry point")?,
774 ),
775 PipelineShaderStageCreateInfo::new(
776 emissive_fs
777 .entry_point("main")
778 .ok_or("missing emissive-toon-mesh.frag entry point")?,
779 ),
780 ];
781
782 let mirror_stages = vec![
783 PipelineShaderStageCreateInfo::new(
784 mirror_vs
785 .entry_point("main")
786 .ok_or("missing mirror-mesh.vert entry point")?,
787 ),
788 PipelineShaderStageCreateInfo::new(
789 mirror_fs
790 .entry_point("main")
791 .ok_or("missing mirror-mesh.frag entry point")?,
792 ),
793 ];
794
795 let skinned_emissive_stages = vec![
796 PipelineShaderStageCreateInfo::new(
797 skinned_vs
798 .entry_point("main")
799 .ok_or("missing skinned-toon-mesh.vert entry point")?,
800 ),
801 PipelineShaderStageCreateInfo::new(
802 emissive_fs
803 .entry_point("main")
804 .ok_or("missing emissive-toon-mesh.frag entry point")?,
805 ),
806 ];
807
808 let layout = PipelineLayout::new(
809 device.clone(),
810 PipelineLayoutCreateInfo {
811 set_layouts: vec![
812 set_layouts.global.clone(),
813 set_layouts.material.clone(),
814 set_layouts.rig.clone(),
815 ],
816 ..Default::default()
817 },
818 )?;
819
820 let vertex_input_state_static = VertexInputState::new()
824 .binding(
825 0,
826 VertexInputBindingDescription {
827 stride: size_of::<CpuVertex>() as u32,
828 input_rate: VertexInputRate::Vertex,
829 ..Default::default()
830 },
831 )
832 .binding(
833 1,
834 VertexInputBindingDescription {
835 stride: size_of::<InstanceData>() as u32,
836 input_rate: VertexInputRate::Instance { divisor: 1 },
837 ..Default::default()
838 },
839 )
840 .attribute(
841 0,
842 VertexInputAttributeDescription {
843 binding: 0,
844 format: Format::R32G32B32_SFLOAT,
845 offset: 0,
846 ..Default::default()
847 },
848 )
849 .attribute(
850 5,
851 VertexInputAttributeDescription {
852 binding: 0,
853 format: Format::R32G32_SFLOAT,
854 offset: 12,
855 ..Default::default()
856 },
857 )
858 .attribute(
859 8,
860 VertexInputAttributeDescription {
861 binding: 0,
862 format: Format::R32G32B32_SFLOAT,
863 offset: 20,
864 ..Default::default()
865 },
866 )
867 .attribute(
868 1,
869 VertexInputAttributeDescription {
870 binding: 1,
871 format: Format::R32G32B32A32_SFLOAT,
872 offset: 0,
873 ..Default::default()
874 },
875 )
876 .attribute(
877 2,
878 VertexInputAttributeDescription {
879 binding: 1,
880 format: Format::R32G32B32A32_SFLOAT,
881 offset: 16,
882 ..Default::default()
883 },
884 )
885 .attribute(
886 3,
887 VertexInputAttributeDescription {
888 binding: 1,
889 format: Format::R32G32B32A32_SFLOAT,
890 offset: 32,
891 ..Default::default()
892 },
893 )
894 .attribute(
895 4,
896 VertexInputAttributeDescription {
897 binding: 1,
898 format: Format::R32G32B32A32_SFLOAT,
899 offset: 48,
900 ..Default::default()
901 },
902 )
903 .attribute(
904 6,
905 VertexInputAttributeDescription {
906 binding: 1,
907 format: Format::R32G32B32A32_SFLOAT,
908 offset: 64,
909 ..Default::default()
910 },
911 )
912 .attribute(
913 7,
914 VertexInputAttributeDescription {
915 binding: 1,
916 format: Format::R32_SFLOAT,
917 offset: 80,
918 ..Default::default()
919 },
920 )
921 .attribute(
922 9,
923 VertexInputAttributeDescription {
924 binding: 1,
925 format: Format::R32_SFLOAT,
926 offset: 84,
927 ..Default::default()
928 },
929 )
930 .attribute(
931 10,
932 VertexInputAttributeDescription {
933 binding: 1,
934 format: Format::R32_UINT,
935 offset: 88,
936 ..Default::default()
937 },
938 )
939 .attribute(
940 11,
941 VertexInputAttributeDescription {
942 binding: 1,
943 format: Format::R32_UINT,
944 offset: 92,
945 ..Default::default()
946 },
947 );
948
949 let vertex_input_state_skinned = VertexInputState::new()
952 .binding(
953 0,
954 VertexInputBindingDescription {
955 stride: size_of::<CpuVertex>() as u32,
956 input_rate: VertexInputRate::Vertex,
957 ..Default::default()
958 },
959 )
960 .binding(
961 1,
962 VertexInputBindingDescription {
963 stride: size_of::<GpuSkinVertex>() as u32,
964 input_rate: VertexInputRate::Vertex,
965 ..Default::default()
966 },
967 )
968 .binding(
969 2,
970 VertexInputBindingDescription {
971 stride: size_of::<InstanceData>() as u32,
972 input_rate: VertexInputRate::Instance { divisor: 1 },
973 ..Default::default()
974 },
975 )
976 .attribute(
978 0,
979 VertexInputAttributeDescription {
980 binding: 0,
981 format: Format::R32G32B32_SFLOAT,
982 offset: 0,
983 ..Default::default()
984 },
985 )
986 .attribute(
987 5,
988 VertexInputAttributeDescription {
989 binding: 0,
990 format: Format::R32G32_SFLOAT,
991 offset: 12,
992 ..Default::default()
993 },
994 )
995 .attribute(
996 8,
997 VertexInputAttributeDescription {
998 binding: 0,
999 format: Format::R32G32B32_SFLOAT,
1000 offset: 20,
1001 ..Default::default()
1002 },
1003 )
1004 .attribute(
1006 12,
1007 VertexInputAttributeDescription {
1008 binding: 1,
1009 format: Format::R16G16B16A16_UINT,
1010 offset: 0,
1011 ..Default::default()
1012 },
1013 )
1014 .attribute(
1015 13,
1016 VertexInputAttributeDescription {
1017 binding: 1,
1018 format: Format::R32G32B32A32_SFLOAT,
1019 offset: 8,
1020 ..Default::default()
1021 },
1022 )
1023 .attribute(
1025 1,
1026 VertexInputAttributeDescription {
1027 binding: 2,
1028 format: Format::R32G32B32A32_SFLOAT,
1029 offset: 0,
1030 ..Default::default()
1031 },
1032 )
1033 .attribute(
1034 2,
1035 VertexInputAttributeDescription {
1036 binding: 2,
1037 format: Format::R32G32B32A32_SFLOAT,
1038 offset: 16,
1039 ..Default::default()
1040 },
1041 )
1042 .attribute(
1043 3,
1044 VertexInputAttributeDescription {
1045 binding: 2,
1046 format: Format::R32G32B32A32_SFLOAT,
1047 offset: 32,
1048 ..Default::default()
1049 },
1050 )
1051 .attribute(
1052 4,
1053 VertexInputAttributeDescription {
1054 binding: 2,
1055 format: Format::R32G32B32A32_SFLOAT,
1056 offset: 48,
1057 ..Default::default()
1058 },
1059 )
1060 .attribute(
1061 6,
1062 VertexInputAttributeDescription {
1063 binding: 2,
1064 format: Format::R32G32B32A32_SFLOAT,
1065 offset: 64,
1066 ..Default::default()
1067 },
1068 )
1069 .attribute(
1070 7,
1071 VertexInputAttributeDescription {
1072 binding: 2,
1073 format: Format::R32_SFLOAT,
1074 offset: 80,
1075 ..Default::default()
1076 },
1077 )
1078 .attribute(
1079 9,
1080 VertexInputAttributeDescription {
1081 binding: 2,
1082 format: Format::R32_SFLOAT,
1083 offset: 84,
1084 ..Default::default()
1085 },
1086 )
1087 .attribute(
1088 10,
1089 VertexInputAttributeDescription {
1090 binding: 2,
1091 format: Format::R32_UINT,
1092 offset: 88,
1093 ..Default::default()
1094 },
1095 )
1096 .attribute(
1097 11,
1098 VertexInputAttributeDescription {
1099 binding: 2,
1100 format: Format::R32_UINT,
1101 offset: 92,
1102 ..Default::default()
1103 },
1104 );
1105
1106 let color_format = swapchain_state.swapchain.image_format();
1107 let mut pipeline_ci =
1108 vulkano::pipeline::graphics::GraphicsPipelineCreateInfo::layout(layout);
1109 pipeline_ci.stages = stages.into();
1110 pipeline_ci.vertex_input_state = Some(vertex_input_state_static);
1111 pipeline_ci.input_assembly_state = Some(InputAssemblyState::default());
1112 pipeline_ci.viewport_state = Some(ViewportState::default());
1113 pipeline_ci.rasterization_state = Some(RasterizationState::default());
1114 pipeline_ci.multisample_state = Some(MultisampleState {
1115 rasterization_samples: msaa_samples,
1116 ..Default::default()
1117 });
1118 pipeline_ci.depth_stencil_state = Some(DepthStencilState {
1120 depth: Some(DepthState::simple()),
1121 ..Default::default()
1122 });
1123 pipeline_ci.color_blend_state = Some(ColorBlendState::with_attachment_states(
1126 1,
1127 ColorBlendAttachmentState {
1128 blend: Some(AttachmentBlend {
1129 src_color_blend_factor: BlendFactor::SrcAlpha,
1130 dst_color_blend_factor: BlendFactor::OneMinusSrcAlpha,
1131 color_blend_op: BlendOp::Add,
1132 src_alpha_blend_factor: BlendFactor::One,
1133 dst_alpha_blend_factor: BlendFactor::OneMinusSrcAlpha,
1134 alpha_blend_op: BlendOp::Add,
1135 }),
1136 color_write_enable: true,
1137 color_write_mask: ColorComponents::all(),
1138 },
1139 ));
1140 pipeline_ci.dynamic_state = [DynamicState::Viewport, DynamicState::Scissor]
1141 .into_iter()
1142 .collect();
1143 let mut pipeline_rendering = PipelineRenderingCreateInfo::default();
1146 pipeline_rendering.color_attachment_formats = vec![Some(color_format)];
1147 pipeline_rendering.depth_attachment_format = Some(VulkanoSwapchainState::DEPTH_FORMAT);
1148 pipeline_rendering.stencil_attachment_format =
1149 Some(VulkanoSwapchainState::DEPTH_FORMAT);
1150
1151 pipeline_ci.subpass = Some(PipelineSubpassType::BeginRendering(pipeline_rendering));
1152
1153 let pipeline_toon_mesh =
1154 GraphicsPipeline::new(device.clone(), None, pipeline_ci.clone())?;
1155 let mut pipeline_ci_mirror = pipeline_ci.clone();
1156 pipeline_ci_mirror.stages = mirror_stages.clone().into();
1157 let pipeline_mirror_mesh =
1158 GraphicsPipeline::new(device.clone(), None, pipeline_ci_mirror.clone())?;
1159 let mut pipeline_ci_grid = pipeline_ci.clone();
1160 pipeline_ci_grid.stages = grid_stages.clone().into();
1161 let pipeline_grid_mesh =
1162 GraphicsPipeline::new(device.clone(), None, pipeline_ci_grid.clone())?;
1163
1164 let mut pipeline_ci_emissive = pipeline_ci.clone();
1165 pipeline_ci_emissive.stages = emissive_stages.clone().into();
1166 let pipeline_emissive_toon_mesh =
1167 GraphicsPipeline::new(device.clone(), None, pipeline_ci_emissive.clone())?;
1168
1169 let mut pipeline_ci_emissive_prepass = pipeline_ci_emissive.clone();
1170 pipeline_ci_emissive_prepass.depth_stencil_state = Some(DepthStencilState {
1171 depth: Some(DepthState {
1172 write_enable: false,
1173 compare_op: CompareOp::LessOrEqual,
1174 ..DepthState::simple()
1175 }),
1176 ..Default::default()
1177 });
1178 let pipeline_emissive_prepass_toon_mesh =
1179 GraphicsPipeline::new(device.clone(), None, pipeline_ci_emissive_prepass)?;
1180
1181 let mut pipeline_ci_emissive_prepass_depth_write = pipeline_ci_emissive.clone();
1182 pipeline_ci_emissive_prepass_depth_write.depth_stencil_state =
1183 Some(DepthStencilState {
1184 depth: Some(DepthState {
1185 write_enable: true,
1186 compare_op: CompareOp::LessOrEqual,
1187 ..DepthState::simple()
1188 }),
1189 ..Default::default()
1190 });
1191 let pipeline_emissive_prepass_depth_write_toon_mesh = GraphicsPipeline::new(
1192 device.clone(),
1193 None,
1194 pipeline_ci_emissive_prepass_depth_write,
1195 )?;
1196
1197 let mut pipeline_ci_transparent = pipeline_ci.clone();
1199 pipeline_ci_transparent.depth_stencil_state = Some(DepthStencilState {
1200 depth: Some(DepthState {
1201 write_enable: false,
1202 ..DepthState::simple()
1203 }),
1204 ..Default::default()
1205 });
1206 let pipeline_toon_mesh_transparent =
1207 GraphicsPipeline::new(device.clone(), None, pipeline_ci_transparent.clone())?;
1208 let mut pipeline_ci_mirror_transparent = pipeline_ci_transparent.clone();
1209 pipeline_ci_mirror_transparent.stages = mirror_stages.clone().into();
1210 let pipeline_mirror_mesh_transparent =
1211 GraphicsPipeline::new(device.clone(), None, pipeline_ci_mirror_transparent)?;
1212 let mut pipeline_ci_grid_transparent = pipeline_ci_transparent.clone();
1213 pipeline_ci_grid_transparent.stages = grid_stages.clone().into();
1214 let pipeline_grid_mesh_transparent =
1215 GraphicsPipeline::new(device.clone(), None, pipeline_ci_grid_transparent.clone())?;
1216
1217 let mut pipeline_ci_emissive_transparent = pipeline_ci_transparent.clone();
1218 pipeline_ci_emissive_transparent.stages = emissive_stages.clone().into();
1219 let pipeline_emissive_toon_mesh_transparent = GraphicsPipeline::new(
1220 device.clone(),
1221 None,
1222 pipeline_ci_emissive_transparent.clone(),
1223 )?;
1224
1225 let mut pipeline_ci_cutout = pipeline_ci.clone();
1230 pipeline_ci_cutout.multisample_state = Some(MultisampleState {
1231 rasterization_samples: msaa_samples,
1232 alpha_to_coverage_enable: msaa_samples != SampleCount::Sample1,
1233 ..Default::default()
1234 });
1235 pipeline_ci_cutout.color_blend_state = Some(ColorBlendState::with_attachment_states(
1236 1,
1237 ColorBlendAttachmentState {
1238 blend: None,
1239 color_write_enable: true,
1240 color_write_mask: ColorComponents::all(),
1241 },
1242 ));
1243 let pipeline_toon_mesh_cutout =
1244 GraphicsPipeline::new(device.clone(), None, pipeline_ci_cutout.clone())?;
1245 let mut pipeline_ci_mirror_cutout = pipeline_ci_cutout.clone();
1246 pipeline_ci_mirror_cutout.stages = mirror_stages.clone().into();
1247 let pipeline_mirror_mesh_cutout =
1248 GraphicsPipeline::new(device.clone(), None, pipeline_ci_mirror_cutout)?;
1249
1250 let mut pipeline_ci_emissive_cutout = pipeline_ci_cutout.clone();
1251 pipeline_ci_emissive_cutout.stages = emissive_stages.clone().into();
1252 let pipeline_emissive_toon_mesh_cutout =
1253 GraphicsPipeline::new(device.clone(), None, pipeline_ci_emissive_cutout.clone())?;
1254
1255 let mut pipeline_ci_emissive_prepass_cutout = pipeline_ci_emissive_cutout.clone();
1256 pipeline_ci_emissive_prepass_cutout.depth_stencil_state = Some(DepthStencilState {
1257 depth: Some(DepthState {
1258 write_enable: false,
1259 compare_op: CompareOp::LessOrEqual,
1260 ..DepthState::simple()
1261 }),
1262 ..Default::default()
1263 });
1264 let pipeline_emissive_prepass_toon_mesh_cutout =
1265 GraphicsPipeline::new(device.clone(), None, pipeline_ci_emissive_prepass_cutout)?;
1266
1267 let mut pipeline_ci_skinned = pipeline_ci.clone();
1269 pipeline_ci_skinned.stages = skinned_stages.clone().into();
1270 pipeline_ci_skinned.vertex_input_state = Some(vertex_input_state_skinned.clone());
1271 let pipeline_skinned_toon_mesh =
1272 GraphicsPipeline::new(device.clone(), None, pipeline_ci_skinned.clone())?;
1273
1274 let mut pipeline_ci_skinned_emissive = pipeline_ci.clone();
1275 pipeline_ci_skinned_emissive.stages = skinned_emissive_stages.clone().into();
1276 pipeline_ci_skinned_emissive.vertex_input_state =
1277 Some(vertex_input_state_skinned.clone());
1278 let pipeline_skinned_emissive_toon_mesh =
1279 GraphicsPipeline::new(device.clone(), None, pipeline_ci_skinned_emissive.clone())?;
1280
1281 let mut pipeline_ci_skinned_emissive_prepass = pipeline_ci_skinned_emissive.clone();
1282 pipeline_ci_skinned_emissive_prepass.depth_stencil_state = Some(DepthStencilState {
1283 depth: Some(DepthState {
1284 write_enable: false,
1285 compare_op: CompareOp::LessOrEqual,
1286 ..DepthState::simple()
1287 }),
1288 ..Default::default()
1289 });
1290 let pipeline_skinned_emissive_prepass_toon_mesh =
1291 GraphicsPipeline::new(device.clone(), None, pipeline_ci_skinned_emissive_prepass)?;
1292
1293 let mut pipeline_ci_skinned_emissive_prepass_depth_write =
1294 pipeline_ci_skinned_emissive.clone();
1295 pipeline_ci_skinned_emissive_prepass_depth_write.depth_stencil_state =
1296 Some(DepthStencilState {
1297 depth: Some(DepthState {
1298 write_enable: true,
1299 compare_op: CompareOp::LessOrEqual,
1300 ..DepthState::simple()
1301 }),
1302 ..Default::default()
1303 });
1304 let pipeline_skinned_emissive_prepass_depth_write_toon_mesh = GraphicsPipeline::new(
1305 device.clone(),
1306 None,
1307 pipeline_ci_skinned_emissive_prepass_depth_write,
1308 )?;
1309
1310 let mut pipeline_ci_skinned_transparent = pipeline_ci_transparent.clone();
1311 pipeline_ci_skinned_transparent.stages = skinned_stages.clone().into();
1312 pipeline_ci_skinned_transparent.vertex_input_state =
1313 Some(vertex_input_state_skinned.clone());
1314 let pipeline_skinned_toon_mesh_transparent =
1315 GraphicsPipeline::new(device.clone(), None, pipeline_ci_skinned_transparent)?;
1316
1317 let mut pipeline_ci_skinned_emissive_transparent = pipeline_ci_transparent.clone();
1318 pipeline_ci_skinned_emissive_transparent.stages =
1319 skinned_emissive_stages.clone().into();
1320 pipeline_ci_skinned_emissive_transparent.vertex_input_state =
1321 Some(vertex_input_state_skinned.clone());
1322 let pipeline_skinned_emissive_toon_mesh_transparent = GraphicsPipeline::new(
1323 device.clone(),
1324 None,
1325 pipeline_ci_skinned_emissive_transparent,
1326 )?;
1327
1328 let mut pipeline_ci_skinned_cutout = pipeline_ci_cutout.clone();
1329 pipeline_ci_skinned_cutout.stages = skinned_stages.into();
1330 pipeline_ci_skinned_cutout.vertex_input_state =
1331 Some(vertex_input_state_skinned.clone());
1332 let pipeline_skinned_toon_mesh_cutout =
1333 GraphicsPipeline::new(device.clone(), None, pipeline_ci_skinned_cutout)?;
1334
1335 let mut pipeline_ci_skinned_emissive_cutout = pipeline_ci_cutout.clone();
1336 pipeline_ci_skinned_emissive_cutout.stages = skinned_emissive_stages.clone().into();
1337 pipeline_ci_skinned_emissive_cutout.vertex_input_state =
1338 Some(vertex_input_state_skinned.clone());
1339 let pipeline_skinned_emissive_toon_mesh_cutout = GraphicsPipeline::new(
1340 device.clone(),
1341 None,
1342 pipeline_ci_skinned_emissive_cutout.clone(),
1343 )?;
1344
1345 let mut pipeline_ci_skinned_emissive_prepass_cutout =
1346 pipeline_ci_skinned_emissive_cutout.clone();
1347 pipeline_ci_skinned_emissive_prepass_cutout.depth_stencil_state =
1348 Some(DepthStencilState {
1349 depth: Some(DepthState {
1350 write_enable: false,
1351 compare_op: CompareOp::LessOrEqual,
1352 ..DepthState::simple()
1353 }),
1354 ..Default::default()
1355 });
1356 let pipeline_skinned_emissive_prepass_toon_mesh_cutout = GraphicsPipeline::new(
1357 device.clone(),
1358 None,
1359 pipeline_ci_skinned_emissive_prepass_cutout,
1360 )?;
1361
1362 let stencil_ops_incr = StencilOps {
1365 compare_op: CompareOp::Equal,
1366 pass_op: StencilOp::IncrementAndClamp,
1367 fail_op: StencilOp::Keep,
1368 depth_fail_op: StencilOp::Keep,
1369 };
1370 let stencil_ops_decr = StencilOps {
1371 compare_op: CompareOp::Equal,
1372 pass_op: StencilOp::DecrementAndClamp,
1373 fail_op: StencilOp::Keep,
1374 depth_fail_op: StencilOp::Keep,
1375 };
1376 let stencil_ops_test = StencilOps {
1377 compare_op: CompareOp::Equal,
1378 pass_op: StencilOp::Keep,
1379 fail_op: StencilOp::Keep,
1380 depth_fail_op: StencilOp::Keep,
1381 };
1382 let stencil_write_color_blend = ColorBlendState::with_attachment_states(
1383 1,
1384 ColorBlendAttachmentState {
1385 blend: None,
1386 color_write_enable: true,
1387 color_write_mask: ColorComponents::empty(),
1388 },
1389 );
1390 let mut stencil_dynamic_state = pipeline_ci.dynamic_state.clone();
1391 stencil_dynamic_state.insert(DynamicState::StencilReference);
1392
1393 let mut pipeline_ci_stencil_incr = pipeline_ci.clone();
1394 pipeline_ci_stencil_incr.color_blend_state = Some(stencil_write_color_blend.clone());
1395 pipeline_ci_stencil_incr.depth_stencil_state = Some(DepthStencilState {
1396 depth: None,
1397 stencil: Some(StencilState {
1398 front: StencilOpState {
1399 ops: stencil_ops_incr,
1400 ..Default::default()
1401 },
1402 back: StencilOpState {
1403 ops: stencil_ops_incr,
1404 ..Default::default()
1405 },
1406 }),
1407 ..Default::default()
1408 });
1409 pipeline_ci_stencil_incr.dynamic_state = stencil_dynamic_state.clone();
1410 let pipeline_stencil_incr =
1411 GraphicsPipeline::new(device.clone(), None, pipeline_ci_stencil_incr)?;
1412
1413 let mut pipeline_ci_stencil_decr = pipeline_ci.clone();
1414 pipeline_ci_stencil_decr.color_blend_state = Some(stencil_write_color_blend);
1415 pipeline_ci_stencil_decr.depth_stencil_state = Some(DepthStencilState {
1416 depth: None,
1417 stencil: Some(StencilState {
1418 front: StencilOpState {
1419 ops: stencil_ops_decr,
1420 ..Default::default()
1421 },
1422 back: StencilOpState {
1423 ops: stencil_ops_decr,
1424 ..Default::default()
1425 },
1426 }),
1427 ..Default::default()
1428 });
1429 pipeline_ci_stencil_decr.dynamic_state = stencil_dynamic_state.clone();
1430 let pipeline_stencil_decr =
1431 GraphicsPipeline::new(device.clone(), None, pipeline_ci_stencil_decr)?;
1432
1433 let clipped_depth_stencil = DepthStencilState {
1435 depth: Some(DepthState::simple()),
1436 stencil: Some(StencilState {
1437 front: StencilOpState {
1438 ops: stencil_ops_test,
1439 ..Default::default()
1440 },
1441 back: StencilOpState {
1442 ops: stencil_ops_test,
1443 ..Default::default()
1444 },
1445 }),
1446 ..Default::default()
1447 };
1448
1449 let mut pipeline_ci_overlay_clipped = pipeline_ci.clone();
1450 pipeline_ci_overlay_clipped.depth_stencil_state = Some(clipped_depth_stencil.clone());
1451 pipeline_ci_overlay_clipped.dynamic_state = stencil_dynamic_state.clone();
1452 let pipeline_overlay_clipped =
1453 GraphicsPipeline::new(device.clone(), None, pipeline_ci_overlay_clipped)?;
1454
1455 let mut pipeline_ci_emissive_overlay_clipped = pipeline_ci_emissive.clone();
1456 pipeline_ci_emissive_overlay_clipped.depth_stencil_state =
1457 Some(clipped_depth_stencil.clone());
1458 pipeline_ci_emissive_overlay_clipped.dynamic_state = stencil_dynamic_state.clone();
1459 let pipeline_emissive_overlay_clipped =
1460 GraphicsPipeline::new(device.clone(), None, pipeline_ci_emissive_overlay_clipped)?;
1461
1462 let mut pipeline_ci_opaque_clipped = pipeline_ci.clone();
1465 pipeline_ci_opaque_clipped.depth_stencil_state = Some(clipped_depth_stencil.clone());
1466 pipeline_ci_opaque_clipped.dynamic_state = stencil_dynamic_state.clone();
1467 let pipeline_opaque_clipped =
1468 GraphicsPipeline::new(device.clone(), None, pipeline_ci_opaque_clipped)?;
1469
1470 let mut pipeline_ci_mirror_clipped = pipeline_ci_mirror.clone();
1471 pipeline_ci_mirror_clipped.depth_stencil_state = Some(clipped_depth_stencil.clone());
1472 pipeline_ci_mirror_clipped.dynamic_state = stencil_dynamic_state.clone();
1473 let pipeline_mirror_mesh_clipped =
1474 GraphicsPipeline::new(device.clone(), None, pipeline_ci_mirror_clipped)?;
1475
1476 let mut pipeline_ci_emissive_opaque_clipped = pipeline_ci_emissive.clone();
1477 pipeline_ci_emissive_opaque_clipped.depth_stencil_state = Some(clipped_depth_stencil);
1478 pipeline_ci_emissive_opaque_clipped.dynamic_state = stencil_dynamic_state.clone();
1479 let pipeline_emissive_opaque_clipped =
1480 GraphicsPipeline::new(device.clone(), None, pipeline_ci_emissive_opaque_clipped)?;
1481
1482 let transparent_clipped_depth_stencil = DepthStencilState {
1483 depth: Some(DepthState {
1484 write_enable: false,
1485 ..DepthState::simple()
1486 }),
1487 stencil: Some(StencilState {
1488 front: StencilOpState {
1489 ops: stencil_ops_test,
1490 ..Default::default()
1491 },
1492 back: StencilOpState {
1493 ops: stencil_ops_test,
1494 ..Default::default()
1495 },
1496 }),
1497 ..Default::default()
1498 };
1499
1500 let mut pipeline_ci_transparent_clipped = pipeline_ci_transparent.clone();
1501 pipeline_ci_transparent_clipped.depth_stencil_state =
1502 Some(transparent_clipped_depth_stencil.clone());
1503 pipeline_ci_transparent_clipped.dynamic_state = stencil_dynamic_state.clone();
1504 let pipeline_toon_mesh_transparent_clipped =
1505 GraphicsPipeline::new(device.clone(), None, pipeline_ci_transparent_clipped)?;
1506 let mut pipeline_ci_mirror_transparent_clipped = pipeline_ci_transparent.clone();
1507 pipeline_ci_mirror_transparent_clipped.stages = mirror_stages.clone().into();
1508 pipeline_ci_mirror_transparent_clipped.depth_stencil_state =
1509 Some(transparent_clipped_depth_stencil.clone());
1510 pipeline_ci_mirror_transparent_clipped.dynamic_state = stencil_dynamic_state.clone();
1511 let pipeline_mirror_mesh_transparent_clipped = GraphicsPipeline::new(
1512 device.clone(),
1513 None,
1514 pipeline_ci_mirror_transparent_clipped,
1515 )?;
1516 let mut pipeline_ci_grid_transparent_clipped = pipeline_ci_grid_transparent.clone();
1517 pipeline_ci_grid_transparent_clipped.depth_stencil_state =
1518 Some(transparent_clipped_depth_stencil.clone());
1519 pipeline_ci_grid_transparent_clipped.dynamic_state = stencil_dynamic_state.clone();
1520 let pipeline_grid_mesh_transparent_clipped =
1521 GraphicsPipeline::new(device.clone(), None, pipeline_ci_grid_transparent_clipped)?;
1522
1523 let mut pipeline_ci_emissive_transparent_clipped =
1524 pipeline_ci_emissive_transparent.clone();
1525 pipeline_ci_emissive_transparent_clipped.depth_stencil_state =
1526 Some(transparent_clipped_depth_stencil);
1527 pipeline_ci_emissive_transparent_clipped.dynamic_state = stencil_dynamic_state.clone();
1528 let pipeline_emissive_toon_mesh_transparent_clipped = GraphicsPipeline::new(
1529 device.clone(),
1530 None,
1531 pipeline_ci_emissive_transparent_clipped,
1532 )?;
1533
1534 let mut pipeline_ci_cutout_clipped = pipeline_ci_cutout.clone();
1535 pipeline_ci_cutout_clipped.depth_stencil_state = Some(DepthStencilState {
1536 depth: Some(DepthState::simple()),
1537 stencil: Some(StencilState {
1538 front: StencilOpState {
1539 ops: stencil_ops_test,
1540 ..Default::default()
1541 },
1542 back: StencilOpState {
1543 ops: stencil_ops_test,
1544 ..Default::default()
1545 },
1546 }),
1547 ..Default::default()
1548 });
1549 pipeline_ci_cutout_clipped.dynamic_state = stencil_dynamic_state.clone();
1550 let pipeline_toon_mesh_cutout_clipped =
1551 GraphicsPipeline::new(device.clone(), None, pipeline_ci_cutout_clipped)?;
1552 let mut pipeline_ci_mirror_cutout_clipped = pipeline_ci_cutout.clone();
1553 pipeline_ci_mirror_cutout_clipped.stages = mirror_stages.into();
1554 pipeline_ci_mirror_cutout_clipped.depth_stencil_state = Some(DepthStencilState {
1555 depth: Some(DepthState::simple()),
1556 stencil: Some(StencilState {
1557 front: StencilOpState {
1558 ops: stencil_ops_test,
1559 ..Default::default()
1560 },
1561 back: StencilOpState {
1562 ops: stencil_ops_test,
1563 ..Default::default()
1564 },
1565 }),
1566 ..Default::default()
1567 });
1568 pipeline_ci_mirror_cutout_clipped.dynamic_state = stencil_dynamic_state.clone();
1569 let pipeline_mirror_mesh_cutout_clipped =
1570 GraphicsPipeline::new(device.clone(), None, pipeline_ci_mirror_cutout_clipped)?;
1571
1572 let mut pipeline_ci_emissive_cutout_clipped = pipeline_ci_emissive_cutout.clone();
1573 pipeline_ci_emissive_cutout_clipped.depth_stencil_state = Some(DepthStencilState {
1574 depth: Some(DepthState::simple()),
1575 stencil: Some(StencilState {
1576 front: StencilOpState {
1577 ops: stencil_ops_test,
1578 ..Default::default()
1579 },
1580 back: StencilOpState {
1581 ops: stencil_ops_test,
1582 ..Default::default()
1583 },
1584 }),
1585 ..Default::default()
1586 });
1587 pipeline_ci_emissive_cutout_clipped.dynamic_state = stencil_dynamic_state;
1588 let pipeline_emissive_toon_mesh_cutout_clipped =
1589 GraphicsPipeline::new(device.clone(), None, pipeline_ci_emissive_cutout_clipped)?;
1590
1591 let command_buffer_allocator = Arc::new(StandardCommandBufferAllocator::new(
1592 device.clone(),
1593 Default::default(),
1594 ));
1595
1596 let descriptor_set_allocator = Arc::new(StandardDescriptorSetAllocator::new(
1597 device.clone(),
1598 Default::default(),
1599 ));
1600
1601 let post_processing_renderer = PostProcessingRenderer::new(
1602 device.clone(),
1603 context.memory_allocator().clone(),
1604 descriptor_set_allocator.clone(),
1605 )?;
1606
1607 let sampler_linear =
1608 Sampler::new(device.clone(), SamplerCreateInfo::simple_repeat_linear())?;
1609
1610 let sampler_nearest = Sampler::new(
1611 device.clone(),
1612 SamplerCreateInfo {
1613 mag_filter: Filter::Nearest,
1614 min_filter: Filter::Nearest,
1615 mipmap_mode: SamplerMipmapMode::Nearest,
1616 address_mode: [SamplerAddressMode::Repeat; 3],
1617 ..Default::default()
1618 },
1619 )?;
1620
1621 let sampler_nearest_mag = Sampler::new(
1622 device.clone(),
1623 SamplerCreateInfo {
1624 mag_filter: Filter::Nearest,
1625 min_filter: Filter::Linear,
1626 mipmap_mode: SamplerMipmapMode::Nearest,
1627 address_mode: [SamplerAddressMode::Repeat; 3],
1628 ..Default::default()
1629 },
1630 )?;
1631
1632 let mut state = Self {
1633 context,
1634 window,
1635
1636 swapchain_state,
1637
1638 command_buffer_allocator,
1639 descriptor_set_allocator,
1640 post_processing_renderer,
1641 meshes: HashMap::new(),
1642
1643 textures: HashMap::new(),
1644 sampler_linear,
1645 sampler_nearest,
1646 sampler_nearest_mag,
1647 default_white_texture: TextureHandle(0),
1648
1649 set_layouts,
1650
1651 pipeline_toon_mesh,
1652 pipeline_toon_mesh_transparent,
1653 pipeline_toon_mesh_cutout,
1654 pipeline_toon_mesh_transparent_clipped,
1655 pipeline_toon_mesh_cutout_clipped,
1656 pipeline_mirror_mesh,
1657 pipeline_mirror_mesh_transparent,
1658 pipeline_mirror_mesh_cutout,
1659 pipeline_mirror_mesh_clipped,
1660 pipeline_mirror_mesh_transparent_clipped,
1661 pipeline_mirror_mesh_cutout_clipped,
1662
1663 pipeline_grid_mesh,
1664 pipeline_grid_mesh_transparent,
1665 pipeline_grid_mesh_transparent_clipped,
1666
1667 pipeline_emissive_toon_mesh,
1668 pipeline_emissive_toon_mesh_transparent,
1669 pipeline_emissive_toon_mesh_cutout,
1670 pipeline_emissive_toon_mesh_transparent_clipped,
1671 pipeline_emissive_toon_mesh_cutout_clipped,
1672 pipeline_emissive_prepass_toon_mesh,
1673 pipeline_emissive_prepass_toon_mesh_cutout,
1674 pipeline_emissive_prepass_depth_write_toon_mesh,
1675
1676 pipeline_skinned_toon_mesh,
1677 pipeline_skinned_toon_mesh_transparent,
1678 pipeline_skinned_toon_mesh_cutout,
1679
1680 pipeline_skinned_emissive_toon_mesh,
1681 pipeline_skinned_emissive_toon_mesh_transparent,
1682 pipeline_skinned_emissive_toon_mesh_cutout,
1683 pipeline_skinned_emissive_prepass_toon_mesh,
1684 pipeline_skinned_emissive_prepass_toon_mesh_cutout,
1685 pipeline_skinned_emissive_prepass_depth_write_toon_mesh,
1686
1687 pipeline_stencil_incr,
1688 pipeline_stencil_decr,
1689 pipeline_overlay_clipped,
1690 pipeline_emissive_overlay_clipped,
1691 pipeline_opaque_clipped,
1692 pipeline_emissive_opaque_clipped,
1693
1694 msaa_samples,
1695
1696 cached_instance_buffer: None,
1697 cached_instance_count: 0,
1698
1699 cached_background_instance_buffer: None,
1700 cached_background_instance_count: 0,
1701
1702 cached_background_occluded_lit_instance_buffer: None,
1703 cached_background_occluded_lit_instance_count: 0,
1704
1705 cached_cutout_instance_buffer: None,
1706 cached_cutout_instance_count: 0,
1707
1708 cached_overlay_instance_buffer: None,
1709 cached_overlay_instance_count: 0,
1710 cached_material_sets: HashMap::new(),
1711 pending_runtime_texture_updates: HashMap::new(),
1712 window_runtime_debug_targets: None,
1713
1714 cached_bones_buffers: Vec::new(),
1715 cached_bones_slot_valid: Vec::new(),
1716 cached_bones_capacity: 0,
1717
1718 xr_offscreen: None,
1719 mirror_offscreen: HashMap::new(),
1720
1721 window_resized: false,
1722 recreate_swapchain: false,
1723 images_in_flight: (0..framebuffer_count).map(|_| None).collect(),
1724 };
1725
1726 state.upload_texture_rgba8(TextureHandle(0), &[255, 255, 255, 255], 1, 1)?;
1728
1729 Ok(state)
1730 }
1731
1732 pub fn window_color_format(&self) -> Format {
1733 self.swapchain_state.swapchain.image_format()
1734 }
1735
1736 fn ensure_xr_offscreen_targets(
1737 &mut self,
1738 view_count: usize,
1739 extent: [u32; 2],
1740 ) -> Result<(), Box<dyn std::error::Error>> {
1741 let color_format = self.swapchain_state.swapchain.image_format();
1742
1743 let needs_recreate = self.xr_offscreen.as_ref().is_none_or(|t| {
1744 t.extent != extent
1745 || t.color_format != color_format
1746 || t.color_views.len() != view_count
1747 || (self.msaa_samples == SampleCount::Sample1) != t.msaa_color_views.is_empty()
1748 || (self.msaa_samples != SampleCount::Sample1
1749 && t.msaa_color_views.len() != view_count)
1750 });
1751
1752 if !needs_recreate {
1753 return Ok(());
1754 }
1755
1756 let memory_allocator = self.context.memory_allocator().clone();
1757
1758 let mut color_images = Vec::with_capacity(view_count);
1759 let mut msaa_color_views = Vec::with_capacity(view_count);
1760 let mut color_views = Vec::with_capacity(view_count);
1761 let mut depth_views = Vec::with_capacity(view_count);
1762
1763 for _ in 0..view_count {
1764 let color_image = vulkano::image::Image::new(
1766 memory_allocator.clone(),
1767 vulkano::image::ImageCreateInfo {
1768 image_type: vulkano::image::ImageType::Dim2d,
1769 format: color_format,
1770 extent: [extent[0], extent[1], 1],
1771 samples: SampleCount::Sample1,
1772 usage: vulkano::image::ImageUsage::COLOR_ATTACHMENT
1775 | vulkano::image::ImageUsage::SAMPLED
1776 | vulkano::image::ImageUsage::TRANSFER_SRC,
1777 ..Default::default()
1778 },
1779 AllocationCreateInfo {
1780 memory_type_filter: MemoryTypeFilter::PREFER_DEVICE,
1781 ..Default::default()
1782 },
1783 )?;
1784
1785 let color_view = ImageView::new_default(color_image.clone())
1786 .map_err(|e| -> Box<dyn std::error::Error> { format!("{e:?}").into() })?;
1787
1788 if self.msaa_samples != SampleCount::Sample1 {
1790 let msaa_color_image = vulkano::image::Image::new(
1791 memory_allocator.clone(),
1792 vulkano::image::ImageCreateInfo {
1793 image_type: vulkano::image::ImageType::Dim2d,
1794 format: color_format,
1795 extent: [extent[0], extent[1], 1],
1796 samples: self.msaa_samples,
1797 usage: vulkano::image::ImageUsage::COLOR_ATTACHMENT
1798 | vulkano::image::ImageUsage::TRANSIENT_ATTACHMENT,
1799 ..Default::default()
1800 },
1801 AllocationCreateInfo {
1802 memory_type_filter: MemoryTypeFilter::PREFER_DEVICE,
1803 ..Default::default()
1804 },
1805 )?;
1806
1807 let msaa_color_view = ImageView::new_default(msaa_color_image)
1808 .map_err(|e| -> Box<dyn std::error::Error> { format!("{e:?}").into() })?;
1809 msaa_color_views.push(msaa_color_view);
1810 }
1811
1812 let depth_image = vulkano::image::Image::new(
1813 memory_allocator.clone(),
1814 vulkano::image::ImageCreateInfo {
1815 image_type: vulkano::image::ImageType::Dim2d,
1816 format: VulkanoSwapchainState::DEPTH_FORMAT,
1817 extent: [extent[0], extent[1], 1],
1818 samples: self.msaa_samples,
1819 usage: vulkano::image::ImageUsage::DEPTH_STENCIL_ATTACHMENT,
1820 ..Default::default()
1821 },
1822 AllocationCreateInfo {
1823 memory_type_filter: MemoryTypeFilter::PREFER_DEVICE,
1824 ..Default::default()
1825 },
1826 )?;
1827
1828 let depth_view = ImageView::new(
1829 depth_image.clone(),
1830 ImageViewCreateInfo {
1831 subresource_range: ImageSubresourceRange {
1832 aspects: ImageAspects::DEPTH | ImageAspects::STENCIL,
1833 ..depth_image.subresource_range()
1834 },
1835 ..ImageViewCreateInfo::from_image(&depth_image)
1836 },
1837 )
1838 .map_err(|e| -> Box<dyn std::error::Error> { format!("{e:?}").into() })?;
1839
1840 color_images.push(color_image);
1841 color_views.push(color_view);
1842 depth_views.push(depth_view);
1843 }
1844
1845 self.xr_offscreen = Some(XrOffscreenTargets {
1846 extent,
1847 color_format,
1848 color_images,
1849 msaa_color_views,
1850 color_views,
1851 depth_views,
1852 });
1853
1854 Ok(())
1855 }
1856
1857 pub fn ensure_mirror_offscreen_targets(
1858 &mut self,
1859 mirror_key: &str,
1860 view_count: usize,
1861 extent: [u32; 2],
1862 color_format: Format,
1863 ) -> Result<&MirrorOffscreenTargets, Box<dyn std::error::Error>> {
1864 let needs_recreate = self
1865 .mirror_offscreen
1866 .get(mirror_key)
1867 .map_or(true, |targets| {
1868 targets.extent != extent
1869 || targets.color_format != color_format
1870 || targets.color_images.len() != view_count
1871 });
1872
1873 if needs_recreate {
1874 let mut color_images = Vec::new();
1875 let mut msaa_color_views = Vec::new();
1876 let mut color_views = Vec::new();
1877 let mut depth_views = Vec::new();
1878
1879 let memory_allocator = self.context.memory_allocator().clone();
1880 for _ in 0..view_count {
1881 let color_image = Image::new(
1882 memory_allocator.clone(),
1883 ImageCreateInfo {
1884 image_type: ImageType::Dim2d,
1885 format: color_format,
1886 extent: [extent[0], extent[1], 1],
1887 samples: SampleCount::Sample1,
1888 usage: ImageUsage::COLOR_ATTACHMENT
1889 | ImageUsage::SAMPLED
1890 | ImageUsage::TRANSFER_SRC,
1891 ..Default::default()
1892 },
1893 AllocationCreateInfo {
1894 memory_type_filter: MemoryTypeFilter::PREFER_DEVICE,
1895 ..Default::default()
1896 },
1897 )?;
1898
1899 let color_view = ImageView::new_default(color_image.clone())
1900 .map_err(|e| -> Box<dyn std::error::Error> { format!("{e:?}").into() })?;
1901
1902 if self.msaa_samples != SampleCount::Sample1 {
1903 let msaa_color_image = Image::new(
1904 memory_allocator.clone(),
1905 ImageCreateInfo {
1906 image_type: ImageType::Dim2d,
1907 format: color_format,
1908 extent: [extent[0], extent[1], 1],
1909 samples: self.msaa_samples,
1910 usage: ImageUsage::COLOR_ATTACHMENT
1911 | ImageUsage::TRANSIENT_ATTACHMENT,
1912 ..Default::default()
1913 },
1914 AllocationCreateInfo {
1915 memory_type_filter: MemoryTypeFilter::PREFER_DEVICE,
1916 ..Default::default()
1917 },
1918 )?;
1919
1920 let msaa_color_view = ImageView::new_default(msaa_color_image).map_err(
1921 |e| -> Box<dyn std::error::Error> { format!("{e:?}").into() },
1922 )?;
1923 msaa_color_views.push(msaa_color_view);
1924 }
1925
1926 let depth_image = Image::new(
1927 memory_allocator.clone(),
1928 ImageCreateInfo {
1929 image_type: ImageType::Dim2d,
1930 format: VulkanoSwapchainState::DEPTH_FORMAT,
1931 extent: [extent[0], extent[1], 1],
1932 samples: self.msaa_samples,
1933 usage: ImageUsage::DEPTH_STENCIL_ATTACHMENT,
1934 ..Default::default()
1935 },
1936 AllocationCreateInfo {
1937 memory_type_filter: MemoryTypeFilter::PREFER_DEVICE,
1938 ..Default::default()
1939 },
1940 )?;
1941
1942 let depth_view = ImageView::new(
1943 depth_image.clone(),
1944 ImageViewCreateInfo {
1945 subresource_range: ImageSubresourceRange {
1946 aspects: ImageAspects::DEPTH | ImageAspects::STENCIL,
1947 ..depth_image.subresource_range()
1948 },
1949 ..ImageViewCreateInfo::from_image(&depth_image)
1950 },
1951 )
1952 .map_err(|e| -> Box<dyn std::error::Error> { format!("{e:?}").into() })?;
1953
1954 color_images.push(color_image);
1955 color_views.push(color_view);
1956 depth_views.push(depth_view);
1957 }
1958
1959 self.mirror_offscreen.insert(
1960 mirror_key.to_string(),
1961 MirrorOffscreenTargets {
1962 extent,
1963 color_format,
1964 color_images,
1965 msaa_color_views,
1966 color_views,
1967 depth_views,
1968 },
1969 );
1970 }
1971
1972 Ok(self.mirror_offscreen.get(mirror_key).unwrap())
1973 }
1974
1975 pub fn render_xr_eye_offscreen(
1976 &mut self,
1977 visual_world: &mut VisualWorld,
1978 eye: usize,
1979 extent: [u32; 2],
1980 ) -> Result<(), Box<dyn std::error::Error>> {
1981 if !self.pending_runtime_texture_updates.is_empty() {
1982 unsafe {
1983 self.context.device().wait_idle().map_err(
1984 |e| -> Box<dyn std::error::Error> {
1985 format!("wait_idle failed before runtime texture swap: {e}").into()
1986 },
1987 )?;
1988 }
1989 }
1990 self.apply_pending_runtime_texture_updates();
1991
1992 let view_count = 2;
1994 self.ensure_xr_offscreen_targets(view_count, extent)?;
1995 let color_format = self
1996 .xr_offscreen
1997 .as_ref()
1998 .ok_or("XR offscreen targets missing")?
1999 .color_format;
2000
2001 let post_process_config = visual_world.post_processing().clone();
2002 let post_process_active = post_process_config.is_active();
2003 if post_process_active {
2004 self.post_processing_renderer.ensure_xr_targets(
2005 view_count,
2006 extent,
2007 color_format,
2008 self.msaa_samples,
2009 &post_process_config,
2010 )?;
2011 }
2012
2013 self.render_mirror_captures(visual_world, color_format)?;
2014
2015 let Some(targets) = self.xr_offscreen.as_ref() else {
2016 return Err("XR offscreen targets missing".into());
2017 };
2018
2019 let resolve_view = targets
2020 .color_views
2021 .get(eye)
2022 .ok_or("XR offscreen eye out of range")?
2023 .clone();
2024
2025 let post_process = if post_process_active {
2026 let pp_targets = self
2027 .post_processing_renderer
2028 .xr_frame_targets(eye)
2029 .ok_or("missing XR post-processing targets")?
2030 .clone();
2031 Some(PostProcessInvocation {
2032 final_output_view: resolve_view.clone(),
2033 final_color_format: targets.color_format,
2034 config: post_process_config,
2035 targets: pp_targets,
2036 })
2037 } else {
2038 None
2039 };
2040
2041 let (color_attachment_view, color_resolve_view, depth_view) =
2042 if let Some(post) = post_process.as_ref() {
2043 (
2044 post.targets
2045 .main_msaa_color
2046 .clone()
2047 .unwrap_or_else(|| post.targets.main_color.clone()),
2048 if post.targets.main_msaa_color.is_some() {
2049 Some(post.targets.main_color.clone())
2050 } else {
2051 None
2052 },
2053 post.targets.depth.clone(),
2054 )
2055 } else if self.msaa_samples != SampleCount::Sample1 {
2056 let msaa_view = targets
2057 .msaa_color_views
2058 .get(eye)
2059 .ok_or("XR MSAA color eye out of range")?
2060 .clone();
2061 let depth_view = targets
2062 .depth_views
2063 .get(eye)
2064 .ok_or("XR depth eye out of range")?
2065 .clone();
2066 (msaa_view, Some(resolve_view.clone()), depth_view)
2067 } else {
2068 let depth_view = targets
2069 .depth_views
2070 .get(eye)
2071 .ok_or("XR depth eye out of range")?
2072 .clone();
2073 (resolve_view.clone(), None, depth_view)
2074 };
2075
2076 let xr_camera = visual_world
2077 .visual_camera(crate::engine::graphics::CameraTarget::Xr)
2078 .ok_or("missing XR camera")?;
2079 let eye_data = xr_camera
2080 .eyes
2081 .get(eye)
2082 .ok_or("XR camera eye out of range")?;
2083 let render_view = RenderView {
2084 view: eye_data.view,
2085 proj: eye_data.proj,
2086 viewport: [extent[0] as f32, extent[1] as f32],
2087 kind: RenderViewKind::XrEye { eye },
2088 };
2089
2090 let window_slots = self.swapchain_state.swapchain_views.len().max(1);
2091 let bones_slots_total = window_slots + view_count;
2092 let bones_slot = window_slots + eye;
2093
2094 let cb = self.build_draw_batches_command_buffer(
2095 visual_world,
2096 &render_view,
2097 bones_slot,
2098 bones_slots_total,
2099 color_attachment_view,
2100 color_resolve_view,
2101 depth_view,
2102 extent,
2103 post_process,
2104 None,
2105 )?;
2106
2107 let device = self.context.device().clone();
2108 let queue = self.context.graphics_queue().clone();
2109
2110 sync::now(device)
2111 .then_execute(queue, cb)?
2112 .then_signal_fence_and_flush()?
2113 .wait(None)?;
2114
2115 Ok(())
2116 }
2117
2118 fn render_mirror_captures(
2119 &mut self,
2120 visual_world: &mut VisualWorld,
2121 color_format: Format,
2122 ) -> Result<(), Box<dyn std::error::Error>> {
2123 let device = self.context.device().clone();
2124 let queue = self.context.graphics_queue().clone();
2125 let mirrors = visual_world.mirrors().to_vec();
2126 let msaa_samples = self.msaa_samples;
2127
2128 for mirror in mirrors {
2129 if mirror.captures.is_empty() {
2130 continue;
2131 }
2132 let capture_count = mirror.captures.len();
2133
2134 let aspect = mirror.aspect_ratio.max(1e-6);
2135 let base_extent = (1024.0 * mirror.resolution_scale).max(1.0).floor() as u32;
2136 let mirror_extent = if aspect >= 1.0 {
2137 [
2138 ((base_extent as f32) * aspect).max(1.0).floor() as u32,
2139 base_extent.max(1),
2140 ]
2141 } else {
2142 [
2143 base_extent.max(1),
2144 ((base_extent as f32) / aspect).max(1.0).floor() as u32,
2145 ]
2146 };
2147 if mirror_extent[0] == 0 || mirror_extent[1] == 0 {
2148 continue;
2149 }
2150
2151 let (color_views, msaa_color_views, depth_views) = {
2152 let mirror_offscreen_key = mirror
2153 .captures
2154 .first()
2155 .map(|capture| capture.target_key.as_str())
2156 .ok_or("mirror capture key missing")?;
2157 let targets = self.ensure_mirror_offscreen_targets(
2158 mirror_offscreen_key,
2159 capture_count,
2160 mirror_extent,
2161 color_format,
2162 )?;
2163 (
2164 targets.color_views.clone(),
2165 targets.msaa_color_views.clone(),
2166 targets.depth_views.clone(),
2167 )
2168 };
2169
2170 for (capture_slot, capture) in mirror.captures.iter().enumerate() {
2171 let eye_data = &capture.camera;
2172
2173 let render_view = RenderView {
2174 view: eye_data.view,
2175 proj: eye_data.proj,
2176 viewport: [mirror_extent[0] as f32, mirror_extent[1] as f32],
2177 kind: RenderViewKind::Mirror {
2178 mirror_component: mirror.mirror_component,
2179 family: capture.family,
2180 view_index: capture.view_index,
2181 plane_origin: mirror.plane_origin,
2182 plane_normal: mirror.plane_normal,
2183 excluded_instance: Some(mirror.source_instance),
2184 },
2185 };
2186
2187 let (color_attachment_view, color_resolve_view) =
2188 if msaa_samples != SampleCount::Sample1 {
2189 (
2190 msaa_color_views[capture_slot].clone(),
2191 Some(color_views[capture_slot].clone()),
2192 )
2193 } else {
2194 (color_views[capture_slot].clone(), None)
2195 };
2196 let depth_view = depth_views[capture_slot].clone();
2197
2198 let runtime_texture_publication = visual_world
2199 .runtime_texture_handle(&capture.target_key)
2200 .map(|handle| {
2201 let src_view = color_resolve_view
2202 .clone()
2203 .unwrap_or_else(|| color_attachment_view.clone());
2204 (handle, src_view)
2205 });
2206
2207 let cb = self.build_draw_batches_command_buffer(
2208 visual_world,
2209 &render_view,
2210 0,
2211 1,
2212 color_attachment_view,
2213 color_resolve_view,
2214 depth_view,
2215 mirror_extent,
2216 None,
2217 runtime_texture_publication,
2218 )?;
2219
2220 sync::now(device.clone())
2221 .then_execute(queue.clone(), cb)?
2222 .then_signal_fence_and_flush()?
2223 .wait(None)?;
2224 }
2225 }
2226
2227 Ok(())
2228 }
2229
2230 pub fn xr_offscreen_vk_image(&self, eye: usize) -> Option<ash::vk::Image> {
2231 let targets = self.xr_offscreen.as_ref()?;
2232 let img = targets.color_images.get(eye)?;
2233 Some(img.handle())
2234 }
2235
2236 pub fn upload_texture_rgba8(
2237 &mut self,
2238 handle: TextureHandle,
2239 rgba: &[u8],
2240 width: u32,
2241 height: u32,
2242 ) -> Result<(), Box<dyn std::error::Error>> {
2243 if self.textures.contains_key(&handle) {
2244 return Ok(());
2245 }
2246
2247 let view = vulkano_texture_upload::upload_texture_rgba8(
2248 &self.context,
2249 &self.command_buffer_allocator,
2250 rgba,
2251 width,
2252 height,
2253 )?;
2254
2255 self.textures.insert(
2256 handle,
2257 VulkanoGpuTexture {
2258 view,
2259 extent: [width, height],
2260 format: Format::R8G8B8A8_UNORM,
2261 },
2262 );
2263 Ok(())
2264 }
2265
2266 pub fn upload_texture_bc7(
2267 &mut self,
2268 handle: TextureHandle,
2269 bc7_blocks: &[u8],
2270 width: u32,
2271 height: u32,
2272 srgb: bool,
2273 ) -> Result<(), Box<dyn std::error::Error>> {
2274 if self.textures.contains_key(&handle) {
2275 return Ok(());
2276 }
2277
2278 let view = vulkano_texture_upload::upload_texture_bc7(
2279 &self.context,
2280 &self.command_buffer_allocator,
2281 bc7_blocks,
2282 width,
2283 height,
2284 srgb,
2285 )?;
2286
2287 self.textures.insert(
2288 handle,
2289 VulkanoGpuTexture {
2290 view,
2291 extent: [width, height],
2292 format: if srgb {
2293 Format::BC7_SRGB_BLOCK
2294 } else {
2295 Format::BC7_UNORM_BLOCK
2296 },
2297 },
2298 );
2299 Ok(())
2300 }
2301
2302 fn ensure_runtime_texture_target(
2303 &mut self,
2304 handle: TextureHandle,
2305 src_view: &Arc<ImageView>,
2306 ) -> Result<Arc<ImageView>, Box<dyn std::error::Error>> {
2307 let src_image = src_view.image().clone();
2308 let src_extent = src_image.extent();
2309 let extent = [src_extent[0], src_extent[1]];
2310 let format = src_image.format();
2311
2312 let image = Image::new(
2313 self.context.memory_allocator().clone(),
2314 ImageCreateInfo {
2315 image_type: ImageType::Dim2d,
2316 format,
2317 extent: [extent[0], extent[1], 1],
2318 usage: ImageUsage::TRANSFER_DST | ImageUsage::SAMPLED,
2319 ..Default::default()
2320 },
2321 AllocationCreateInfo {
2322 memory_type_filter: MemoryTypeFilter::PREFER_DEVICE,
2323 ..Default::default()
2324 },
2325 )?;
2326
2327 let view = ImageView::new_default(image)
2328 .map_err(|e| -> Box<dyn std::error::Error> { format!("{e:?}").into() })?;
2329
2330 self.pending_runtime_texture_updates.insert(
2331 handle,
2332 VulkanoGpuTexture {
2333 view: view.clone(),
2334 extent,
2335 format,
2336 },
2337 );
2338
2339 Ok(view)
2340 }
2341
2342 fn create_color_target_view(
2343 memory_allocator: Arc<StandardMemoryAllocator>,
2344 format: Format,
2345 extent: [u32; 2],
2346 samples: SampleCount,
2347 usage: ImageUsage,
2348 ) -> Result<Arc<ImageView>, Box<dyn std::error::Error>> {
2349 let image = Image::new(
2350 memory_allocator,
2351 ImageCreateInfo {
2352 image_type: ImageType::Dim2d,
2353 format,
2354 extent: [extent[0], extent[1], 1],
2355 samples,
2356 usage,
2357 ..Default::default()
2358 },
2359 AllocationCreateInfo {
2360 memory_type_filter: MemoryTypeFilter::PREFER_DEVICE,
2361 ..Default::default()
2362 },
2363 )?;
2364
2365 Ok(ImageView::new_default(image)?)
2366 }
2367
2368 fn ensure_window_runtime_debug_targets(
2369 &mut self,
2370 frame_count: usize,
2371 extent: [u32; 2],
2372 color_format: Format,
2373 ) -> Result<(), Box<dyn std::error::Error>> {
2374 let needs_recreate = self
2375 .window_runtime_debug_targets
2376 .as_ref()
2377 .is_none_or(|targets| {
2378 targets.extent != extent
2379 || targets.color_format != color_format
2380 || targets.color_views.len() != frame_count
2381 || (self.msaa_samples == SampleCount::Sample1)
2382 != targets.msaa_color_views.is_empty()
2383 || (self.msaa_samples != SampleCount::Sample1
2384 && targets.msaa_color_views.len() != frame_count)
2385 });
2386
2387 if !needs_recreate {
2388 return Ok(());
2389 }
2390
2391 let mut msaa_color_views = Vec::with_capacity(frame_count);
2392 let mut color_views = Vec::with_capacity(frame_count);
2393 for _ in 0..frame_count {
2394 if self.msaa_samples != SampleCount::Sample1 {
2395 msaa_color_views.push(Self::create_color_target_view(
2396 self.context.memory_allocator().clone(),
2397 color_format,
2398 extent,
2399 self.msaa_samples,
2400 ImageUsage::COLOR_ATTACHMENT | ImageUsage::TRANSIENT_ATTACHMENT,
2401 )?);
2402 }
2403
2404 color_views.push(Self::create_color_target_view(
2405 self.context.memory_allocator().clone(),
2406 color_format,
2407 extent,
2408 SampleCount::Sample1,
2409 ImageUsage::COLOR_ATTACHMENT | ImageUsage::SAMPLED | ImageUsage::TRANSFER_SRC,
2410 )?);
2411 }
2412
2413 self.window_runtime_debug_targets = Some(WindowRuntimeDebugTargets {
2414 extent,
2415 color_format,
2416 msaa_color_views,
2417 color_views,
2418 });
2419
2420 Ok(())
2421 }
2422
2423 fn build_stencil_clip_debug_batches(
2424 visual_world: &VisualWorld,
2425 ) -> Vec<crate::engine::graphics::visual_world::DrawBatch> {
2426 visual_world
2427 .stencil_clip_order()
2428 .iter()
2429 .enumerate()
2430 .filter_map(|(slot, &instance_index)| {
2431 let instance = visual_world.instances().get(instance_index as usize)?;
2432 Some(crate::engine::graphics::visual_world::DrawBatch {
2433 material: crate::engine::graphics::MaterialHandle::UNLIT_MESH,
2434 mesh: instance.renderable.mesh,
2435 texture: None,
2436 texture_filtering: TextureFiltering::Nearest,
2437 quant_steps: 1.0,
2438 stencil_ref: 0,
2439 start: slot,
2440 count: 1,
2441 })
2442 })
2443 .collect()
2444 }
2445
2446 fn retarget_mirror_surface_textures_for_render_view(
2447 visual_world: &mut VisualWorld,
2448 kind: &RenderViewKind,
2449 ) {
2450 let (family, view_index) = match *kind {
2451 RenderViewKind::Window => (
2452 crate::engine::graphics::visual_world::MirrorViewerFamily::Monoscopic,
2453 0,
2454 ),
2455 RenderViewKind::XrEye { eye } => (
2456 crate::engine::graphics::visual_world::MirrorViewerFamily::Stereoscopic,
2457 eye,
2458 ),
2459 RenderViewKind::Mirror {
2460 family, view_index, ..
2461 } => (family, view_index),
2462 };
2463
2464 let updates: Vec<_> = visual_world
2465 .mirrors()
2466 .iter()
2467 .filter_map(|mirror| {
2468 let key = visual_world.mirror_texture_key_for_view(
2469 mirror.mirror_component,
2470 family,
2471 view_index,
2472 )?;
2473 let handle = visual_world.runtime_texture_handle(key)?;
2474 Some((mirror.source_instance, handle))
2475 })
2476 .collect();
2477
2478 for (instance, handle) in updates {
2479 let _ = visual_world.update_texture(instance, Some(handle));
2480 }
2481 }
2482
2483 fn hsv_debug_color_for_stencil_ref(stencil_ref: u8) -> [f32; 4] {
2484 if stencil_ref == 0 {
2485 return [0.08, 0.08, 0.08, 1.0];
2486 }
2487
2488 let hue_deg = (((stencil_ref - 1) as f32) * 100.0) % 360.0;
2489 let saturation = 0.9;
2490 let value = 1.0;
2491 let chroma = value * saturation;
2492 let hue_sector = hue_deg / 60.0;
2493 let x = chroma * (1.0 - ((hue_sector % 2.0) - 1.0).abs());
2494
2495 let (r1, g1, b1) = if hue_sector < 1.0 {
2496 (chroma, x, 0.0)
2497 } else if hue_sector < 2.0 {
2498 (x, chroma, 0.0)
2499 } else if hue_sector < 3.0 {
2500 (0.0, chroma, x)
2501 } else if hue_sector < 4.0 {
2502 (0.0, x, chroma)
2503 } else if hue_sector < 5.0 {
2504 (x, 0.0, chroma)
2505 } else {
2506 (chroma, 0.0, x)
2507 };
2508
2509 let match_value = value - chroma;
2510 [r1 + match_value, g1 + match_value, b1 + match_value, 1.0]
2511 }
2512
2513 fn build_stencil_clip_debug_instance_buffer(
2514 &self,
2515 visual_world: &VisualWorld,
2516 order: &[u32],
2517 ) -> Result<Option<Subbuffer<[InstanceData]>>, Box<dyn std::error::Error>> {
2518 if order.is_empty() {
2519 return Ok(None);
2520 }
2521
2522 let instances_ref = visual_world.instances();
2523 let instance_data_iter = order.iter().map(|&idx| {
2524 let inst = instances_ref[idx as usize];
2525 let m = inst.transform.model;
2526 InstanceData {
2527 i_model_c0: m[0],
2528 i_model_c1: m[1],
2529 i_model_c2: m[2],
2530 i_model_c3: m[3],
2531 i_color: Self::hsv_debug_color_for_stencil_ref(
2532 inst.stencil_ref.saturating_add(1),
2533 ),
2534 i_emissive: 1.0,
2535 i_opacity: 1.0,
2536 i_bones_base: inst.bones_base,
2537 i_bones_count: inst.bones_count,
2538 }
2539 });
2540
2541 let buf: Subbuffer<[InstanceData]> = Buffer::from_iter(
2542 self.context.memory_allocator().clone(),
2543 BufferCreateInfo {
2544 usage: BufferUsage::VERTEX_BUFFER,
2545 ..Default::default()
2546 },
2547 AllocationCreateInfo {
2548 memory_type_filter: MemoryTypeFilter::PREFER_HOST
2549 | MemoryTypeFilter::HOST_SEQUENTIAL_WRITE,
2550 ..Default::default()
2551 },
2552 instance_data_iter,
2553 )?;
2554
2555 Ok(Some(buf))
2556 }
2557
2558 fn apply_pending_runtime_texture_updates(&mut self) {
2559 if self.pending_runtime_texture_updates.is_empty() {
2560 return;
2561 }
2562
2563 for (handle, texture) in self.pending_runtime_texture_updates.drain() {
2564 self.textures.insert(handle, texture);
2565 self.cached_material_sets
2566 .retain(|(_, texture_handle, _, _), _| *texture_handle != handle);
2567 }
2568 }
2569
2570 fn collect_runtime_texture_publications(
2571 &self,
2572 visual_world: &VisualWorld,
2573 post_process: &PostProcessInvocation,
2574 ) -> Vec<(TextureHandle, Arc<ImageView>)> {
2575 let mut publications = Vec::new();
2576
2577 if let (Some(emissive_pass), Some(view)) = (
2578 post_process.config.emissive_pass.as_ref(),
2579 post_process.targets.bloom_source.clone(),
2580 ) {
2581 if let Some(key) = emissive_pass.output_texture.as_deref() {
2582 if let Some(handle) = visual_world.runtime_texture_handle(key) {
2583 publications.push((handle, view));
2584 }
2585 }
2586 }
2587
2588 if let Some(bloom) = post_process.config.bloom.as_ref() {
2589 if let (Some(key), Some(view)) = (
2590 bloom.output_texture.as_deref(),
2591 post_process.targets.bloom_a.clone(),
2592 ) {
2593 if let Some(handle) = visual_world.runtime_texture_handle(key) {
2594 publications.push((handle, view));
2595 }
2596 }
2597 }
2598
2599 publications
2600 }
2601
2602 fn recreate_swapchain_if_needed(&mut self) -> Result<(), Box<dyn std::error::Error>> {
2603 if !(self.window_resized || self.recreate_swapchain) {
2604 return Ok(());
2605 }
2606
2607 unsafe {
2611 self.context
2612 .device()
2613 .wait_idle()
2614 .map_err(|e| -> Box<dyn std::error::Error> {
2615 format!("wait_idle failed: {e}").into()
2616 })?;
2617
2618 for slot in self.images_in_flight.iter_mut() {
2622 if let Some(mut fut) = slot.take() {
2623 fut.signal_finished();
2624 fut.cleanup_finished();
2625 }
2626 }
2627 }
2628
2629 self.recreate_swapchain = false;
2630
2631 if let Err(e) = self.swapchain_state.recreate(&self.context, &self.window) {
2632 self.recreate_swapchain = true;
2633 println!("[VulkanoRenderer] failed to recreate swapchain: {}", e);
2634 return Ok(());
2635 }
2636
2637 self.images_in_flight = (0..self.swapchain_state.swapchain_views.len())
2640 .map(|_| None)
2641 .collect();
2642
2643 self.cached_bones_buffers.clear();
2645 self.cached_bones_slot_valid.clear();
2646 self.cached_bones_capacity = 0;
2647
2648 self.window_resized = false;
2649 Ok(())
2650 }
2651
2652 fn build_instance_buffer_for_order_or_dummy(
2653 &self,
2654 visual_world: &VisualWorld,
2655 order: &[u32],
2656 ) -> Result<Subbuffer<[InstanceData]>, Box<dyn std::error::Error>> {
2657 static DID_LOG_SKIN_INSTANCE_RANGES: AtomicBool = AtomicBool::new(false);
2658
2659 if !order.is_empty() && env_flag("CAT_DEBUG_SKIN_INSTANCE_RANGES") {
2660 let instances_ref = visual_world.instances();
2661 let skinned_count = order
2662 .iter()
2663 .filter(|&&idx| instances_ref[idx as usize].bones_count > 0)
2664 .count();
2665
2666 if skinned_count > 0 && !DID_LOG_SKIN_INSTANCE_RANGES.swap(true, Ordering::Relaxed)
2667 {
2668 let mut skinned = Vec::new();
2669 for &idx in order.iter() {
2670 let inst = instances_ref[idx as usize];
2671 if inst.bones_count > 0 {
2672 skinned.push((idx, inst.renderable, inst.bones_base, inst.bones_count));
2673 if skinned.len() >= 24 {
2674 break;
2675 }
2676 }
2677 }
2678
2679 let total = order.len();
2680 println!(
2681 "[VulkanoRenderer] instances: total={} with_bones={} (showing up to {})",
2682 total,
2683 skinned_count,
2684 skinned.len()
2685 );
2686 for (i, (idx, renderable, base, count)) in skinned.iter().enumerate() {
2687 println!(
2688 " skinned[{i:02}] instance_index={idx} renderable={renderable:?} bones_base={base} bones_count={count}"
2689 );
2690 }
2691 }
2692 }
2693
2694 let buf: Subbuffer<[InstanceData]> = if order.is_empty() {
2696 Buffer::from_iter(
2697 self.context.memory_allocator().clone(),
2698 BufferCreateInfo {
2699 usage: BufferUsage::VERTEX_BUFFER,
2700 ..Default::default()
2701 },
2702 AllocationCreateInfo {
2703 memory_type_filter: MemoryTypeFilter::PREFER_HOST
2704 | MemoryTypeFilter::HOST_SEQUENTIAL_WRITE,
2705 ..Default::default()
2706 },
2707 std::iter::once(InstanceData::default()),
2708 )?
2709 } else {
2710 let instances_ref = visual_world.instances();
2711 let instance_data_iter = order.iter().map(|&idx| {
2712 let inst = instances_ref[idx as usize];
2713 let m = inst.transform.model;
2714 InstanceData {
2715 i_model_c0: m[0],
2716 i_model_c1: m[1],
2717 i_model_c2: m[2],
2718 i_model_c3: m[3],
2719 i_color: inst.color,
2720 i_emissive: inst.emissive,
2721 i_opacity: inst.opacity,
2722 i_bones_base: inst.bones_base,
2723 i_bones_count: inst.bones_count,
2724 }
2725 });
2726
2727 Buffer::from_iter(
2728 self.context.memory_allocator().clone(),
2729 BufferCreateInfo {
2730 usage: BufferUsage::VERTEX_BUFFER,
2731 ..Default::default()
2732 },
2733 AllocationCreateInfo {
2734 memory_type_filter: MemoryTypeFilter::PREFER_HOST
2735 | MemoryTypeFilter::HOST_SEQUENTIAL_WRITE,
2736 ..Default::default()
2737 },
2738 instance_data_iter,
2739 )?
2740 };
2741
2742 Ok(buf)
2743 }
2744
2745 fn build_instance_buffer_for_order_opt(
2746 &self,
2747 visual_world: &VisualWorld,
2748 order: &[u32],
2749 ) -> Result<Option<Subbuffer<[InstanceData]>>, Box<dyn std::error::Error>> {
2750 if order.is_empty() {
2751 return Ok(None);
2752 }
2753
2754 let instances_ref = visual_world.instances();
2755 let instance_data_iter = order.iter().map(|&idx| {
2756 let inst = instances_ref[idx as usize];
2757 let m = inst.transform.model;
2758 InstanceData {
2759 i_model_c0: m[0],
2760 i_model_c1: m[1],
2761 i_model_c2: m[2],
2762 i_model_c3: m[3],
2763 i_color: inst.color,
2764 i_emissive: inst.emissive,
2765 i_opacity: inst.opacity,
2766 i_bones_base: inst.bones_base,
2767 i_bones_count: inst.bones_count,
2768 }
2769 });
2770
2771 let buf: Subbuffer<[InstanceData]> = Buffer::from_iter(
2772 self.context.memory_allocator().clone(),
2773 BufferCreateInfo {
2774 usage: BufferUsage::VERTEX_BUFFER,
2775 ..Default::default()
2776 },
2777 AllocationCreateInfo {
2778 memory_type_filter: MemoryTypeFilter::PREFER_HOST
2779 | MemoryTypeFilter::HOST_SEQUENTIAL_WRITE,
2780 ..Default::default()
2781 },
2782 instance_data_iter,
2783 )?;
2784
2785 Ok(Some(buf))
2786 }
2787
2788 fn get_or_create_material_set(
2789 &mut self,
2790 material: crate::engine::graphics::MaterialHandle,
2791 texture_handle: TextureHandle,
2792 filtering: TextureFiltering,
2793 quant_steps: f32,
2794 ) -> Result<Option<Arc<DescriptorSet>>, Box<dyn std::error::Error>> {
2795 match material {
2796 crate::engine::graphics::MaterialHandle::TOON_MESH
2797 | crate::engine::graphics::MaterialHandle::UNLIT_MESH
2798 | crate::engine::graphics::MaterialHandle::SKINNED_TOON_MESH
2799 | crate::engine::graphics::MaterialHandle::EMISSIVE_TOON_MESH
2800 | crate::engine::graphics::MaterialHandle::SKINNED_EMISSIVE_TOON_MESH
2801 | crate::engine::graphics::MaterialHandle::GRID_MESH
2802 | crate::engine::graphics::MaterialHandle::MIRROR => {}
2803 _ => return Ok(None),
2804 }
2805
2806 let Some(tex) = self.textures.get(&texture_handle) else {
2807 return Ok(None);
2808 };
2809
2810 let quant_bits = quant_steps.to_bits();
2811 let material_key = (material, texture_handle, filtering, quant_bits);
2812 if let Some(set) = self.cached_material_sets.get(&material_key) {
2813 return Ok(Some(set.clone()));
2814 }
2815
2816 let material_ubo = Self::create_material_ubo(material, quant_steps);
2817 let material_buffer: Subbuffer<MaterialUBO> = Buffer::from_data(
2818 self.context.memory_allocator().clone(),
2819 BufferCreateInfo {
2820 usage: BufferUsage::UNIFORM_BUFFER,
2821 ..Default::default()
2822 },
2823 AllocationCreateInfo {
2824 memory_type_filter: MemoryTypeFilter::PREFER_HOST
2825 | MemoryTypeFilter::HOST_SEQUENTIAL_WRITE,
2826 ..Default::default()
2827 },
2828 material_ubo,
2829 )?;
2830
2831 let sampler = self.sampler_for(filtering).clone();
2832 let set = DescriptorSet::new(
2833 self.descriptor_set_allocator.clone(),
2834 self.set_layouts.material.clone(),
2835 [
2836 WriteDescriptorSet::buffer(0, material_buffer),
2837 WriteDescriptorSet::image_view_sampler(1, tex.view.clone(), sampler),
2838 ],
2839 [],
2840 )?;
2841
2842 self.cached_material_sets.insert(material_key, set.clone());
2843 Ok(Some(set))
2844 }
2845
2846 fn build_draw_batches_command_buffer(
2847 &mut self,
2848 visual_world: &mut VisualWorld,
2849 render_view: &RenderView,
2850 bones_slot: usize,
2851 bones_slots_total: usize,
2852 color_attachment_view: Arc<ImageView>,
2853 color_resolve_view: Option<Arc<ImageView>>,
2854 depth_view: Arc<ImageView>,
2855 extent: [u32; 2],
2856 post_process: Option<PostProcessInvocation>,
2857 runtime_texture_publication: Option<(TextureHandle, Arc<ImageView>)>,
2858 ) -> Result<
2859 Arc<vulkano::command_buffer::PrimaryAutoCommandBuffer>,
2860 Box<dyn std::error::Error>,
2861 > {
2862 Self::retarget_mirror_surface_textures_for_render_view(visual_world, &render_view.kind);
2863
2864 let (camera_target, eye) = match render_view.kind {
2865 RenderViewKind::Window => (crate::engine::graphics::CameraTarget::Window, 0),
2866 RenderViewKind::XrEye { eye } => (crate::engine::graphics::CameraTarget::Xr, eye),
2867 RenderViewKind::Mirror {
2868 family, view_index, ..
2869 } => match family {
2870 crate::engine::graphics::visual_world::MirrorViewerFamily::Monoscopic => {
2871 (crate::engine::graphics::CameraTarget::Window, view_index)
2872 }
2873 crate::engine::graphics::visual_world::MirrorViewerFamily::Stereoscopic => {
2874 (crate::engine::graphics::CameraTarget::Xr, view_index)
2875 }
2876 },
2877 };
2878 let excluded_instance = match &render_view.kind {
2879 RenderViewKind::Mirror {
2880 excluded_instance, ..
2881 } => *excluded_instance,
2882 _ => None,
2883 };
2884
2885 let queue = self.context.graphics_queue().clone();
2886
2887 let draw_cache_rebuilt = visual_world.prepare_draw_cache();
2889
2890 visual_world.prepare_transparent_multi_draw_cache_for_view(render_view.view);
2892
2893 let instance_data_dirty = if eye == 0 {
2896 visual_world.take_instance_data_dirty()
2897 } else {
2898 visual_world.instance_data_dirty()
2899 };
2900
2901 let bones_palette_dirty = if eye == 0 {
2903 visual_world.take_bones_palette_dirty()
2904 } else {
2905 false
2906 };
2907
2908 let (opaque_ops, opaque_instances) = if excluded_instance.is_some() {
2911 visual_world.opaque_stream_excluding(excluded_instance)
2912 } else {
2913 let (ops, instances) = visual_world.opaque_stream();
2914 (ops.to_vec(), instances.to_vec())
2915 };
2916 let instance_count = opaque_instances.len();
2917
2918 let background_instance_count = visual_world.background_order().len();
2921 let background_occluded_lit_instance_count =
2922 visual_world.background_occluded_lit_order().len();
2923 let any_background =
2924 background_instance_count > 0 || background_occluded_lit_instance_count > 0;
2925
2926 let (cutout_ops, cutout_instances) = if excluded_instance.is_some() {
2928 visual_world.cutout_stream_excluding(excluded_instance)
2929 } else {
2930 let (ops, instances) = visual_world.cutout_stream();
2931 (ops.to_vec(), instances.to_vec())
2932 };
2933 let cutout_instance_count = cutout_instances.len();
2934
2935 let (overlay_ops, overlay_instances) = if excluded_instance.is_some() {
2938 visual_world.overlay_stream_excluding(excluded_instance)
2939 } else {
2940 let (ops, instances) = visual_world.overlay_stream();
2941 (ops.to_vec(), instances.to_vec())
2942 };
2943 let overlay_instance_count = overlay_instances.len();
2944
2945 let stencil_clip_debug_requested = camera_target
2946 == crate::engine::graphics::CameraTarget::Window
2947 && eye == 0
2948 && visual_world.stencil_clip_debug_requested();
2949 let stencil_clip_debug_handle = if stencil_clip_debug_requested {
2950 visual_world.runtime_texture_handle(INTERNAL_RENDERER_STENCIL_CLIP_DEBUG_SELECTOR)
2951 } else {
2952 None
2953 };
2954 let stencil_clip_debug_enabled =
2955 stencil_clip_debug_requested && stencil_clip_debug_handle.is_some();
2956 let stencil_clip_debug_batches = if stencil_clip_debug_enabled {
2957 Self::build_stencil_clip_debug_batches(visual_world)
2958 } else {
2959 Vec::new()
2960 };
2961 let stencil_clip_debug_instance_count = stencil_clip_debug_batches.len();
2962
2963 let emissive_instance_count = visual_world.emissive_draw_order().len();
2965 let emissive_cutout_instance_count = visual_world.emissive_cutout_order().len();
2966
2967 let need_instance_buffer = instance_data_dirty
2968 || draw_cache_rebuilt
2969 || self.cached_instance_buffer.is_none()
2970 || self.cached_instance_count != instance_count;
2971
2972 let instance_buffer: Subbuffer<[InstanceData]> = if !need_instance_buffer {
2975 self.cached_instance_buffer
2976 .as_ref()
2977 .expect("cached_instance_buffer")
2978 .clone()
2979 } else {
2980 let buf = self
2981 .build_instance_buffer_for_order_or_dummy(&*visual_world, &opaque_instances)?;
2982
2983 self.cached_instance_count = instance_count;
2984 self.cached_instance_buffer = Some(buf.clone());
2985 buf
2986 };
2987
2988 let need_background_instance_buffer = instance_data_dirty
2989 || draw_cache_rebuilt
2990 || self.cached_background_instance_count != background_instance_count;
2991 let background_instance_buffer = if !need_background_instance_buffer {
2992 self.cached_background_instance_buffer.clone()
2993 } else {
2994 let buf = self.build_instance_buffer_for_order_opt(
2995 &*visual_world,
2996 visual_world.background_order(),
2997 )?;
2998 self.cached_background_instance_count = background_instance_count;
2999 self.cached_background_instance_buffer = buf.clone();
3000 buf
3001 };
3002
3003 let need_background_occluded_lit_instance_buffer = instance_data_dirty
3004 || draw_cache_rebuilt
3005 || self.cached_background_occluded_lit_instance_count
3006 != background_occluded_lit_instance_count;
3007 let background_occluded_lit_instance_buffer =
3008 if !need_background_occluded_lit_instance_buffer {
3009 self.cached_background_occluded_lit_instance_buffer.clone()
3010 } else {
3011 let buf = self.build_instance_buffer_for_order_opt(
3012 &*visual_world,
3013 visual_world.background_occluded_lit_order(),
3014 )?;
3015 self.cached_background_occluded_lit_instance_count =
3016 background_occluded_lit_instance_count;
3017 self.cached_background_occluded_lit_instance_buffer = buf.clone();
3018 buf
3019 };
3020
3021 let need_cutout_instance_buffer = instance_data_dirty
3022 || draw_cache_rebuilt
3023 || self.cached_cutout_instance_count != cutout_instance_count;
3024 let cutout_instance_buffer = if !need_cutout_instance_buffer {
3025 self.cached_cutout_instance_buffer.clone()
3026 } else {
3027 let buf =
3028 self.build_instance_buffer_for_order_opt(&*visual_world, &cutout_instances)?;
3029 self.cached_cutout_instance_count = cutout_instance_count;
3030 self.cached_cutout_instance_buffer = buf.clone();
3031 buf
3032 };
3033
3034 let need_overlay_instance_buffer = instance_data_dirty
3035 || draw_cache_rebuilt
3036 || self.cached_overlay_instance_count != overlay_instance_count;
3037 let overlay_instance_buffer = if !need_overlay_instance_buffer {
3039 self.cached_overlay_instance_buffer.clone()
3040 } else {
3041 let buf =
3042 self.build_instance_buffer_for_order_opt(&*visual_world, &overlay_instances)?;
3043 self.cached_overlay_instance_count = overlay_instance_count;
3044 self.cached_overlay_instance_buffer = buf.clone();
3045 buf
3046 };
3047
3048 let stencil_clip_debug_instance_buffer = if stencil_clip_debug_enabled {
3049 self.build_stencil_clip_debug_instance_buffer(
3050 &*visual_world,
3051 visual_world.stencil_clip_order(),
3052 )?
3053 } else {
3054 None
3055 };
3056 let emissive_instance_buffer = self.build_instance_buffer_for_order_opt(
3057 &*visual_world,
3058 visual_world.emissive_draw_order(),
3059 )?;
3060 let background_occluded_lit_emissive_instance_count =
3061 visual_world.background_occluded_lit_emissive_order().len();
3062 let background_occluded_lit_emissive_instance_buffer =
3063 if background_occluded_lit_emissive_instance_count > 0 {
3064 self.build_instance_buffer_for_order_opt(
3065 &*visual_world,
3066 visual_world.background_occluded_lit_emissive_order(),
3067 )?
3068 } else {
3069 None
3070 };
3071
3072 let emissive_cutout_instance_buffer = self.build_instance_buffer_for_order_opt(
3073 &*visual_world,
3074 visual_world.emissive_cutout_order(),
3075 )?;
3076
3077 let clear_color = visual_world.clear_color();
3078 let defer_overlay_until_before_final_composite =
3079 post_process.is_some() && overlay_instance_count > 0;
3080
3081 let mut color_attachment_clear = RenderingAttachmentInfo {
3082 load_op: AttachmentLoadOp::Clear,
3083 store_op: AttachmentStoreOp::Store,
3084 clear_value: Some(ClearValue::from(clear_color)),
3085 ..RenderingAttachmentInfo::image_view(color_attachment_view.clone())
3086 };
3087
3088 if let Some(resolve_view) = color_resolve_view.clone() {
3089 color_attachment_clear.resolve_info =
3090 Some(RenderingAttachmentResolveInfo::image_view(resolve_view));
3091 color_attachment_clear.store_op = if defer_overlay_until_before_final_composite {
3095 AttachmentStoreOp::Store
3096 } else {
3097 AttachmentStoreOp::DontCare
3098 };
3099 }
3100
3101 let depth_attachment_clear = RenderingAttachmentInfo {
3102 load_op: AttachmentLoadOp::Clear,
3103 store_op: if post_process.is_some() {
3104 AttachmentStoreOp::Store
3105 } else {
3106 AttachmentStoreOp::DontCare
3107 },
3108 clear_value: Some(ClearValue::Depth(1.0)),
3109 ..RenderingAttachmentInfo::image_view(depth_view.clone())
3110 };
3111
3112 let rendering_info_clear_color_and_depth = RenderingInfo {
3113 render_area_offset: [0, 0],
3114 render_area_extent: [extent[0], extent[1]],
3115 layer_count: 1,
3116 color_attachments: vec![Some(color_attachment_clear)],
3117 depth_attachment: Some(depth_attachment_clear.clone()),
3118 stencil_attachment: Some(RenderingAttachmentInfo {
3119 load_op: AttachmentLoadOp::Clear,
3120 store_op: if defer_overlay_until_before_final_composite {
3121 AttachmentStoreOp::Store
3122 } else {
3123 AttachmentStoreOp::DontCare
3124 },
3125 clear_value: Some(ClearValue::Stencil(0)),
3126 ..RenderingAttachmentInfo::image_view(depth_view.clone())
3127 }),
3128 ..Default::default()
3129 };
3130
3131 let viewport = Viewport {
3135 offset: [0.0, extent[1] as f32],
3136 extent: [extent[0] as f32, -(extent[1] as f32)],
3137 depth_range: 0.0..=1.0,
3138 ..Default::default()
3139 };
3140
3141 let camera_ubo_fg = CameraUBO {
3143 view: render_view.view,
3144 proj: render_view.proj,
3145 camera2d: visual_world.camera_2d(),
3146 viewport: [extent[0] as f32, extent[1] as f32],
3147 _pad0: [0.0, 0.0],
3148
3149 ambient_light: visual_world.ambient_light(),
3150 _pad1: 0.0,
3151 };
3152
3153 let camera_buffer_fg: Subbuffer<CameraUBO> = Buffer::from_data(
3154 self.context.memory_allocator().clone(),
3155 BufferCreateInfo {
3156 usage: BufferUsage::UNIFORM_BUFFER,
3157 ..Default::default()
3158 },
3159 AllocationCreateInfo {
3160 memory_type_filter: MemoryTypeFilter::PREFER_HOST
3161 | MemoryTypeFilter::HOST_SEQUENTIAL_WRITE,
3162 ..Default::default()
3163 },
3164 camera_ubo_fg,
3165 )?;
3166
3167 let mut lights_ssbo = LightsSSBO::default();
3169 let lights = visual_world.point_lights();
3170 let count = (lights.len()).min(MAX_LIGHTS);
3171 lights_ssbo.count = count as u32;
3172 for (i, l) in lights.iter().take(count).enumerate() {
3173 let light_type = match l.light_type {
3174 LIGHT_TYPE_POINT => LIGHT_TYPE_POINT,
3175 LIGHT_TYPE_DIRECTIONAL => LIGHT_TYPE_DIRECTIONAL,
3176 _ => LIGHT_TYPE_POINT,
3178 };
3179
3180 lights_ssbo.lights[i] = GpuLight {
3181 pos_intensity: [
3182 l.position_ws[0],
3183 l.position_ws[1],
3184 l.position_ws[2],
3185 l.intensity,
3186 ],
3187 color_distance: [l.color[0], l.color[1], l.color[2], l.distance],
3188 meta: [light_type, 0, 0, 0],
3189 };
3190 }
3191
3192 let lights_buffer: Subbuffer<LightsSSBO> = Buffer::from_data(
3193 self.context.memory_allocator().clone(),
3194 BufferCreateInfo {
3195 usage: BufferUsage::STORAGE_BUFFER,
3196 ..Default::default()
3197 },
3198 AllocationCreateInfo {
3199 memory_type_filter: MemoryTypeFilter::PREFER_HOST
3200 | MemoryTypeFilter::HOST_SEQUENTIAL_WRITE,
3201 ..Default::default()
3202 },
3203 lights_ssbo,
3204 )?;
3205
3206 let global_set_fg = DescriptorSet::new(
3212 self.descriptor_set_allocator.clone(),
3213 self.set_layouts.global.clone(),
3214 [
3215 WriteDescriptorSet::buffer(0, camera_buffer_fg),
3216 WriteDescriptorSet::buffer(1, lights_buffer.clone()),
3217 ],
3218 [],
3219 )?;
3220
3221 let global_set_bg: Option<Arc<DescriptorSet>> = if !any_background {
3226 None
3227 } else {
3228 let mut view_bg = render_view.view;
3229 view_bg[3] = [0.0, 0.0, 0.0, 1.0];
3230
3231 let camera_ubo_bg = CameraUBO {
3232 view: view_bg,
3233 proj: render_view.proj,
3234 camera2d: visual_world.camera_2d(),
3235 viewport: [extent[0] as f32, extent[1] as f32],
3236 _pad0: [0.0, 0.0],
3237
3238 ambient_light: visual_world.ambient_light(),
3239 _pad1: 0.0,
3240 };
3241
3242 let camera_buffer_bg: Subbuffer<CameraUBO> = Buffer::from_data(
3243 self.context.memory_allocator().clone(),
3244 BufferCreateInfo {
3245 usage: BufferUsage::UNIFORM_BUFFER,
3246 ..Default::default()
3247 },
3248 AllocationCreateInfo {
3249 memory_type_filter: MemoryTypeFilter::PREFER_HOST
3250 | MemoryTypeFilter::HOST_SEQUENTIAL_WRITE,
3251 ..Default::default()
3252 },
3253 camera_ubo_bg,
3254 )?;
3255
3256 let set = DescriptorSet::new(
3257 self.descriptor_set_allocator.clone(),
3258 self.set_layouts.global.clone(),
3259 [
3260 WriteDescriptorSet::buffer(0, camera_buffer_bg),
3261 WriteDescriptorSet::buffer(1, lights_buffer.clone()),
3262 ],
3263 [],
3264 )?;
3265 Some(set)
3266 };
3267
3268 let rig_set: Arc<DescriptorSet> = {
3271 static DID_LOG_BONES_PALETTE_UPLOAD: AtomicBool = AtomicBool::new(false);
3272
3273 let want_len = visual_world.bones_palette().len().max(1);
3274
3275 let want_slots = bones_slots_total.max(1);
3276 let slot = bones_slot.min(want_slots - 1);
3277
3278 let needs_realloc = self.cached_bones_buffers.len() != want_slots
3279 || self.cached_bones_capacity < want_len;
3280
3281 if needs_realloc {
3282 let new_cap = want_len.next_power_of_two().max(1);
3283
3284 let mut buffers = Vec::with_capacity(want_slots);
3285 for _ in 0..want_slots {
3286 let buffer: Subbuffer<[GpuMat4]> = Buffer::new_slice(
3287 self.context.memory_allocator().clone(),
3288 BufferCreateInfo {
3289 usage: BufferUsage::STORAGE_BUFFER,
3290 ..Default::default()
3291 },
3292 AllocationCreateInfo {
3293 memory_type_filter: MemoryTypeFilter::PREFER_HOST
3294 | MemoryTypeFilter::HOST_SEQUENTIAL_WRITE,
3295 ..Default::default()
3296 },
3297 new_cap as DeviceSize,
3298 )?;
3299 buffers.push(buffer);
3300 }
3301
3302 self.cached_bones_buffers = buffers;
3303 self.cached_bones_slot_valid = vec![false; want_slots];
3304 self.cached_bones_capacity = new_cap;
3305 }
3306
3307 if bones_palette_dirty {
3308 for v in self.cached_bones_slot_valid.iter_mut() {
3309 *v = false;
3310 }
3311 }
3312
3313 let slot_needs_upload = !self
3314 .cached_bones_slot_valid
3315 .get(slot)
3316 .copied()
3317 .unwrap_or(false);
3318
3319 if slot_needs_upload {
3320 let bones_src = visual_world.bones_palette();
3321
3322 if env_flag("CAT_DEBUG_BONES_PALETTE")
3323 && bones_src.len() > 1
3324 && !DID_LOG_BONES_PALETTE_UPLOAD.swap(true, Ordering::Relaxed)
3325 {
3326 println!(
3327 "[VulkanoRenderer] bones palette upload: dirty={} realloc={} want_len={} cached_cap={} src_len={}",
3328 bones_palette_dirty,
3329 needs_realloc,
3330 want_len,
3331 self.cached_bones_capacity,
3332 bones_src.len()
3333 );
3334
3335 for (i, m) in bones_src.iter().take(3).enumerate() {
3336 println!(" bone[{i:03}]={m:?}");
3337 }
3338 }
3339
3340 let identity = [
3341 [1.0, 0.0, 0.0, 0.0],
3342 [0.0, 1.0, 0.0, 0.0],
3343 [0.0, 0.0, 1.0, 0.0],
3344 [0.0, 0.0, 0.0, 1.0],
3345 ];
3346
3347 let mut dst = self.cached_bones_buffers[slot].write()?;
3348
3349 if bones_src.is_empty() {
3351 dst[0] = GpuMat4 { cols: identity };
3352 for slot in dst.iter_mut().skip(1) {
3353 *slot = GpuMat4 { cols: identity };
3354 }
3355 } else {
3356 for (i, m) in bones_src.iter().copied().enumerate() {
3357 dst[i] = GpuMat4 { cols: m };
3358 }
3359 for slot in dst.iter_mut().skip(bones_src.len()) {
3360 *slot = GpuMat4 { cols: identity };
3361 }
3362 }
3363
3364 if let Some(v) = self.cached_bones_slot_valid.get_mut(slot) {
3365 *v = true;
3366 }
3367 }
3368
3369 let bones_buffer = self.cached_bones_buffers[slot].clone();
3370
3371 let per_instance_lighting_buffer: Subbuffer<DummyPerInstanceLightingSSBO> =
3372 Buffer::from_data(
3373 self.context.memory_allocator().clone(),
3374 BufferCreateInfo {
3375 usage: BufferUsage::STORAGE_BUFFER,
3376 ..Default::default()
3377 },
3378 AllocationCreateInfo {
3379 memory_type_filter: MemoryTypeFilter::PREFER_HOST
3380 | MemoryTypeFilter::HOST_SEQUENTIAL_WRITE,
3381 ..Default::default()
3382 },
3383 DummyPerInstanceLightingSSBO::default(),
3384 )?;
3385
3386 DescriptorSet::new(
3387 self.descriptor_set_allocator.clone(),
3388 self.set_layouts.rig.clone(),
3389 [
3390 WriteDescriptorSet::buffer(0, per_instance_lighting_buffer),
3391 WriteDescriptorSet::buffer(1, bones_buffer),
3392 ],
3393 [],
3394 )?
3395 };
3396
3397 let mut cbb = AutoCommandBufferBuilder::primary(
3398 self.command_buffer_allocator.clone(),
3399 queue.queue_family_index(),
3400 CommandBufferUsage::OneTimeSubmit,
3401 )?;
3402
3403 cbb.begin_rendering(rendering_info_clear_color_and_depth)?;
3405
3406 cbb.set_viewport(0, vec![viewport.clone()].into())?;
3407 cbb.set_scissor(
3408 0,
3409 vec![Scissor {
3410 offset: [0, 0],
3411 extent: [extent[0], extent[1]],
3412 ..Default::default()
3413 }]
3414 .into(),
3415 )?;
3416
3417 if any_background {
3418 if let Some(global_set_bg) = global_set_bg.as_ref() {
3422 if let Some(background_instance_buffer) = background_instance_buffer.as_ref() {
3423 self.record_background_draws(
3424 &mut cbb,
3425 visual_world,
3426 global_set_bg,
3427 &rig_set,
3428 background_instance_buffer,
3429 background_instance_count,
3430 )?;
3431 }
3432
3433 if let Some(background_occluded_lit_instance_buffer) =
3434 background_occluded_lit_instance_buffer.as_ref()
3435 {
3436 self.record_background_occluded_lit_draws(
3437 &mut cbb,
3438 visual_world,
3439 global_set_bg,
3440 &rig_set,
3441 background_occluded_lit_instance_buffer,
3442 background_occluded_lit_instance_count,
3443 )?;
3444 }
3445 }
3446
3447 cbb.bind_pipeline_graphics(self.pipeline_toon_mesh.clone())?;
3450 cbb.clear_attachments(
3451 smallvec::smallvec![ClearAttachment::Depth(1.0)],
3452 smallvec::smallvec![ClearRect {
3453 offset: [0, 0],
3454 extent: [extent[0], extent[1]],
3455 array_layers: 0..1,
3456 }],
3457 )?;
3458 }
3459
3460 self.record_opaque_draws(
3461 &mut cbb,
3462 visual_world,
3463 &global_set_fg,
3464 &rig_set,
3465 &instance_buffer,
3466 instance_count,
3467 Some((&opaque_ops, &opaque_instances)),
3468 )?;
3469
3470 if let Some(cutout_instance_buffer) = cutout_instance_buffer.as_ref() {
3471 self.record_cutout_draws(
3472 &mut cbb,
3473 visual_world,
3474 &global_set_fg,
3475 &rig_set,
3476 cutout_instance_buffer,
3477 cutout_instance_count,
3478 Some((&cutout_ops, &cutout_instances)),
3479 )?;
3480 }
3481
3482 self.record_transparent_single_draws(
3483 &mut cbb,
3484 visual_world,
3485 &global_set_fg,
3486 &rig_set,
3487 eye,
3488 excluded_instance,
3489 )?;
3490
3491 self.record_transparent_multi_draws(
3492 &mut cbb,
3493 visual_world,
3494 &global_set_fg,
3495 &rig_set,
3496 camera_target,
3497 eye,
3498 excluded_instance,
3499 )?;
3500
3501 if overlay_instance_count > 0 && !defer_overlay_until_before_final_composite {
3505 cbb.bind_pipeline_graphics(self.pipeline_toon_mesh.clone())?;
3507 cbb.clear_attachments(
3508 smallvec::smallvec![ClearAttachment::Depth(1.0)],
3509 smallvec::smallvec![ClearRect {
3510 offset: [0, 0],
3511 extent: [extent[0], extent[1]],
3512 array_layers: 0..1,
3513 }],
3514 )?;
3515
3516 if let Some(overlay_instance_buffer) = overlay_instance_buffer.as_ref() {
3517 self.record_overlay_draws(
3518 &mut cbb,
3519 visual_world,
3520 &global_set_fg,
3521 &rig_set,
3522 overlay_instance_buffer,
3523 overlay_instance_count,
3524 Some((&overlay_ops, &overlay_instances)),
3525 )?;
3526 }
3527 }
3528
3529 cbb.end_rendering()?;
3530
3531 if let Some((handle, src_view)) = runtime_texture_publication {
3532 let dst_view = self.ensure_runtime_texture_target(handle, &src_view)?;
3533 cbb.copy_image(CopyImageInfo::images(
3534 src_view.image().clone(),
3535 dst_view.image().clone(),
3536 ))?;
3537 }
3538
3539 if let Some(post_process) = post_process {
3540 let bloom_radius_pixels = post_process
3541 .config
3542 .effective_blur_radius_pixels(post_process.targets.bloom_extent[0]);
3543
3544 let mut blurred_bloom: Option<Arc<ImageView>> = None;
3545 if let (
3546 Some(bloom_cfg),
3547 Some(bloom_source),
3548 Some(bloom_a),
3549 Some(bloom_b),
3550 Some(radius_pixels),
3551 ) = (
3552 post_process.config.bloom.as_ref(),
3553 post_process.targets.bloom_source.clone(),
3554 post_process.targets.bloom_a.clone(),
3555 post_process.targets.bloom_b.clone(),
3556 bloom_radius_pixels,
3557 ) {
3558 let has_foreground_emissive_content =
3559 emissive_instance_count > 0 || emissive_cutout_instance_count > 0;
3560 let has_background_occluded_lit_emissive_content =
3561 background_occluded_lit_emissive_instance_count > 0;
3562
3563 if has_foreground_emissive_content
3564 || has_background_occluded_lit_emissive_content
3565 {
3566 let begin_bloom_extraction =
3567 |cbb: &mut AutoCommandBufferBuilder<
3568 vulkano::command_buffer::PrimaryAutoCommandBuffer,
3569 >,
3570 load_op: AttachmentLoadOp,
3571 store_msaa_for_followup: bool|
3572 -> Result<(), Box<dyn std::error::Error>> {
3573 let mut bloom_attachment = RenderingAttachmentInfo {
3574 load_op,
3575 store_op: AttachmentStoreOp::Store,
3576 clear_value: match load_op {
3577 AttachmentLoadOp::Clear => {
3578 Some(ClearValue::from([0.0, 0.0, 0.0, 0.0]))
3579 }
3580 _ => None,
3581 },
3582 ..RenderingAttachmentInfo::image_view(
3583 post_process
3584 .targets
3585 .bloom_source_msaa
3586 .clone()
3587 .unwrap_or_else(|| bloom_source.clone()),
3588 )
3589 };
3590
3591 if post_process.targets.bloom_source_msaa.is_some() {
3592 bloom_attachment.resolve_info =
3593 Some(RenderingAttachmentResolveInfo::image_view(
3594 bloom_source.clone(),
3595 ));
3596 bloom_attachment.store_op = if store_msaa_for_followup {
3597 AttachmentStoreOp::Store
3598 } else {
3599 AttachmentStoreOp::DontCare
3600 };
3601 }
3602
3603 cbb.begin_rendering(RenderingInfo {
3604 render_area_offset: [0, 0],
3605 render_area_extent: [extent[0], extent[1]],
3606 layer_count: 1,
3607 color_attachments: vec![Some(bloom_attachment)],
3608 depth_attachment: Some(RenderingAttachmentInfo {
3609 load_op: AttachmentLoadOp::Load,
3610 store_op: AttachmentStoreOp::DontCare,
3611 ..RenderingAttachmentInfo::image_view(
3612 post_process.targets.depth.clone(),
3613 )
3614 }),
3615 ..Default::default()
3616 })?;
3617
3618 let bloom_viewport = Viewport {
3619 offset: [0.0, extent[1] as f32],
3620 extent: [extent[0] as f32, -(extent[1] as f32)],
3621 depth_range: 0.0..=1.0,
3622 ..Default::default()
3623 };
3624
3625 cbb.set_viewport(0, vec![bloom_viewport].into())?;
3626 cbb.set_scissor(
3627 0,
3628 vec![Scissor {
3629 offset: [0, 0],
3630 extent: [extent[0], extent[1]],
3631 ..Default::default()
3632 }]
3633 .into(),
3634 )?;
3635
3636 Ok(())
3637 };
3638
3639 if has_foreground_emissive_content {
3640 begin_bloom_extraction(
3641 &mut cbb,
3642 AttachmentLoadOp::Clear,
3643 has_background_occluded_lit_emissive_content,
3644 )?;
3645
3646 if let Some(emissive_instance_buffer) =
3647 emissive_instance_buffer.as_ref()
3648 {
3649 self.record_instanced_draws_for_batches(
3650 &mut cbb,
3651 &global_set_fg,
3652 &rig_set,
3653 emissive_instance_buffer,
3654 emissive_instance_count,
3655 visual_world.emissive_draw_batches(),
3656 self.pipeline_emissive_prepass_toon_mesh.clone(),
3657 self.pipeline_emissive_prepass_toon_mesh.clone(),
3658 self.pipeline_emissive_prepass_toon_mesh.clone(),
3659 self.pipeline_emissive_prepass_toon_mesh.clone(),
3660 self.pipeline_skinned_emissive_prepass_toon_mesh.clone(),
3661 self.pipeline_skinned_emissive_prepass_toon_mesh.clone(),
3662 )?;
3663 }
3664
3665 if let Some(emissive_cutout_instance_buffer) =
3666 emissive_cutout_instance_buffer.as_ref()
3667 {
3668 self.record_instanced_draws_for_batches(
3669 &mut cbb,
3670 &global_set_fg,
3671 &rig_set,
3672 emissive_cutout_instance_buffer,
3673 emissive_cutout_instance_count,
3674 visual_world.emissive_cutout_batches(),
3675 self.pipeline_emissive_prepass_toon_mesh_cutout.clone(),
3676 self.pipeline_emissive_prepass_toon_mesh_cutout.clone(),
3677 self.pipeline_emissive_prepass_toon_mesh_cutout.clone(),
3678 self.pipeline_emissive_prepass_toon_mesh_cutout.clone(),
3679 self.pipeline_skinned_emissive_prepass_toon_mesh_cutout
3680 .clone(),
3681 self.pipeline_skinned_emissive_prepass_toon_mesh_cutout
3682 .clone(),
3683 )?;
3684 }
3685
3686 cbb.end_rendering()?;
3687 }
3688
3689 if has_background_occluded_lit_emissive_content {
3690 begin_bloom_extraction(
3691 &mut cbb,
3692 if has_foreground_emissive_content {
3693 AttachmentLoadOp::Load
3694 } else {
3695 AttachmentLoadOp::Clear
3696 },
3697 false,
3698 )?;
3699
3700 if let Some(background_occluded_lit_emissive_instance_buffer) =
3701 background_occluded_lit_emissive_instance_buffer.as_ref()
3702 {
3703 let global_set_bg = global_set_bg.as_ref().expect(
3704 "background emissive extraction requires bg camera set",
3705 );
3706 self.record_instanced_draws_for_batches(
3707 &mut cbb,
3708 global_set_bg,
3709 &rig_set,
3710 background_occluded_lit_emissive_instance_buffer,
3711 background_occluded_lit_emissive_instance_count,
3712 visual_world.background_occluded_lit_emissive_batches(),
3713 self.pipeline_emissive_prepass_depth_write_toon_mesh.clone(),
3714 self.pipeline_emissive_prepass_depth_write_toon_mesh.clone(),
3715 self.pipeline_emissive_prepass_depth_write_toon_mesh.clone(),
3716 self.pipeline_emissive_prepass_depth_write_toon_mesh.clone(),
3717 self.pipeline_skinned_emissive_prepass_depth_write_toon_mesh
3718 .clone(),
3719 self.pipeline_skinned_emissive_prepass_depth_write_toon_mesh
3720 .clone(),
3721 )?;
3722 }
3723
3724 cbb.end_rendering()?;
3725 }
3726
3727 let bloom_format = post_process.final_color_format;
3728 let blur_h_dir = [1.0 / post_process.targets.bloom_extent[0] as f32, 0.0];
3729 let blur_v_dir = [0.0, 1.0 / post_process.targets.bloom_extent[1] as f32];
3730
3731 self.post_processing_renderer.record_final_pass(
3732 &mut cbb,
3733 bloom_format,
3734 bloom_a.clone(),
3735 post_process.targets.bloom_extent,
3736 bloom_source,
3737 None,
3738 &post_process.config,
3739 )?;
3740
3741 self.post_processing_renderer.record_blur_pass(
3742 &mut cbb,
3743 bloom_format,
3744 bloom_a.clone(),
3745 bloom_b.clone(),
3746 post_process.targets.bloom_extent,
3747 blur_h_dir,
3748 radius_pixels,
3749 )?;
3750 self.post_processing_renderer.record_blur_pass(
3751 &mut cbb,
3752 bloom_format,
3753 bloom_b,
3754 bloom_a.clone(),
3755 post_process.targets.bloom_extent,
3756 blur_v_dir,
3757 radius_pixels,
3758 )?;
3759
3760 if bloom_cfg.intensity > 0.0 {
3761 blurred_bloom = Some(bloom_a);
3762 }
3763 }
3764 }
3765
3766 let final_output_view = post_process.final_output_view.clone();
3767
3768 if overlay_instance_count > 0 {
3769 cbb.begin_rendering(RenderingInfo {
3770 render_area_offset: [0, 0],
3771 render_area_extent: [extent[0], extent[1]],
3772 layer_count: 1,
3773 color_attachments: vec![Some({
3774 let mut color_attachment_load = RenderingAttachmentInfo {
3775 load_op: AttachmentLoadOp::Load,
3776 store_op: AttachmentStoreOp::Store,
3777 ..RenderingAttachmentInfo::image_view(color_attachment_view.clone())
3778 };
3779 if let Some(resolve_view) = color_resolve_view.clone() {
3780 color_attachment_load.resolve_info =
3781 Some(RenderingAttachmentResolveInfo::image_view(resolve_view));
3782 color_attachment_load.store_op = AttachmentStoreOp::DontCare;
3783 }
3784 color_attachment_load
3785 })],
3786 depth_attachment: Some(RenderingAttachmentInfo {
3787 load_op: AttachmentLoadOp::Clear,
3788 store_op: AttachmentStoreOp::DontCare,
3789 clear_value: Some(ClearValue::Depth(1.0)),
3790 ..RenderingAttachmentInfo::image_view(depth_view.clone())
3791 }),
3792 stencil_attachment: Some(RenderingAttachmentInfo {
3793 load_op: AttachmentLoadOp::Load,
3794 store_op: AttachmentStoreOp::DontCare,
3795 ..RenderingAttachmentInfo::image_view(depth_view.clone())
3796 }),
3797 ..Default::default()
3798 })?;
3799
3800 cbb.set_viewport(0, vec![viewport.clone()].into())?;
3801 cbb.set_scissor(
3802 0,
3803 vec![Scissor {
3804 offset: [0, 0],
3805 extent: [extent[0], extent[1]],
3806 ..Default::default()
3807 }]
3808 .into(),
3809 )?;
3810
3811 if let Some(overlay_instance_buffer) = overlay_instance_buffer.as_ref() {
3812 self.record_overlay_draws(
3813 &mut cbb,
3814 visual_world,
3815 &global_set_fg,
3816 &rig_set,
3817 overlay_instance_buffer,
3818 overlay_instance_count,
3819 Some((&overlay_ops, &overlay_instances)),
3820 )?;
3821 }
3822 cbb.end_rendering()?;
3823 }
3824
3825 self.post_processing_renderer.record_final_pass(
3826 &mut cbb,
3827 post_process.final_color_format,
3828 final_output_view.clone(),
3829 extent,
3830 post_process.targets.main_color.clone(),
3831 blurred_bloom,
3832 &post_process.config,
3833 )?;
3834
3835 for (handle, src_view) in
3836 self.collect_runtime_texture_publications(visual_world, &post_process)
3837 {
3838 let dst_view = self.ensure_runtime_texture_target(handle, &src_view)?;
3839 cbb.copy_image(CopyImageInfo::images(
3840 src_view.image().clone(),
3841 dst_view.image().clone(),
3842 ))?;
3843 }
3844 }
3845
3846 if let (Some(handle), Some(debug_instance_buffer)) = (
3847 stencil_clip_debug_handle,
3848 stencil_clip_debug_instance_buffer.as_ref(),
3849 ) {
3850 self.ensure_window_runtime_debug_targets(
3851 self.swapchain_state.swapchain_views.len(),
3852 extent,
3853 color_attachment_view.image().format(),
3854 )?;
3855
3856 let debug_view = self
3857 .window_runtime_debug_targets
3858 .as_ref()
3859 .and_then(|targets| targets.color_views.get(bones_slot))
3860 .cloned()
3861 .ok_or("missing window runtime debug target")?;
3862
3863 let debug_msaa_view = self
3864 .window_runtime_debug_targets
3865 .as_ref()
3866 .and_then(|targets| targets.msaa_color_views.get(bones_slot))
3867 .cloned();
3868
3869 let mut debug_color_attachment = RenderingAttachmentInfo {
3870 load_op: AttachmentLoadOp::Clear,
3871 store_op: AttachmentStoreOp::Store,
3872 clear_value: Some(ClearValue::from([1.0, 0.0, 1.0, 1.0])),
3873 ..RenderingAttachmentInfo::image_view(
3874 debug_msaa_view
3875 .clone()
3876 .unwrap_or_else(|| debug_view.clone()),
3877 )
3878 };
3879 if debug_msaa_view.is_some() {
3880 debug_color_attachment.resolve_info = Some(
3881 RenderingAttachmentResolveInfo::image_view(debug_view.clone()),
3882 );
3883 debug_color_attachment.store_op = AttachmentStoreOp::DontCare;
3884 }
3885
3886 cbb.begin_rendering(RenderingInfo {
3887 render_area_offset: [0, 0],
3888 render_area_extent: [extent[0], extent[1]],
3889 layer_count: 1,
3890 color_attachments: vec![Some(debug_color_attachment)],
3891 depth_attachment: Some(RenderingAttachmentInfo {
3892 load_op: AttachmentLoadOp::Clear,
3893 store_op: AttachmentStoreOp::DontCare,
3894 clear_value: Some(ClearValue::Depth(1.0)),
3895 ..RenderingAttachmentInfo::image_view(depth_view.clone())
3896 }),
3897 stencil_attachment: Some(RenderingAttachmentInfo {
3898 load_op: AttachmentLoadOp::Clear,
3899 store_op: AttachmentStoreOp::DontCare,
3900 clear_value: Some(ClearValue::Stencil(0)),
3901 ..RenderingAttachmentInfo::image_view(depth_view.clone())
3902 }),
3903 ..Default::default()
3904 })?;
3905
3906 cbb.set_viewport(0, vec![viewport.clone()].into())?;
3907 cbb.set_scissor(
3908 0,
3909 vec![Scissor {
3910 offset: [0, 0],
3911 extent: [extent[0], extent[1]],
3912 ..Default::default()
3913 }]
3914 .into(),
3915 )?;
3916
3917 self.record_instanced_draws_for_batches(
3918 &mut cbb,
3919 &global_set_fg,
3920 &rig_set,
3921 debug_instance_buffer,
3922 stencil_clip_debug_instance_count,
3923 &stencil_clip_debug_batches,
3924 self.pipeline_toon_mesh.clone(),
3925 self.pipeline_mirror_mesh.clone(),
3926 self.pipeline_grid_mesh.clone(),
3927 self.pipeline_emissive_toon_mesh.clone(),
3928 self.pipeline_skinned_toon_mesh.clone(),
3929 self.pipeline_skinned_emissive_toon_mesh.clone(),
3930 )?;
3931
3932 cbb.end_rendering()?;
3933
3934 let dst_view = self.ensure_runtime_texture_target(handle, &debug_view)?;
3935 cbb.copy_image(CopyImageInfo::images(
3936 debug_view.image().clone(),
3937 dst_view.image().clone(),
3938 ))?;
3939 }
3940
3941 let cb = cbb.build()?;
3942
3943 Ok(cb)
3944 }
3945
3946 pub fn render_visual_world(
3947 &mut self,
3948 visual_world: &mut VisualWorld,
3949 ) -> Result<(), Box<dyn std::error::Error>> {
3950 self.recreate_swapchain_if_needed()?;
3951 if !self.pending_runtime_texture_updates.is_empty() {
3952 unsafe {
3953 self.context.device().wait_idle().map_err(
3954 |e| -> Box<dyn std::error::Error> {
3955 format!("wait_idle failed before runtime texture swap: {e}").into()
3956 },
3957 )?;
3958 }
3959 }
3960 self.apply_pending_runtime_texture_updates();
3961
3962 for fut in self.images_in_flight.iter_mut() {
3964 if let Some(fut) = fut.as_mut() {
3965 fut.cleanup_finished();
3966 }
3967 }
3968
3969 let (image_i, suboptimal, acquire_future) =
3970 match swapchain::acquire_next_image(self.swapchain_state.swapchain.clone(), None)
3971 .map_err(Validated::unwrap)
3972 {
3973 Ok(r) => r,
3974 Err(VulkanError::OutOfDate) => {
3975 self.recreate_swapchain = true;
3976 return Ok(());
3977 }
3978 Err(e) => return Err(Box::new(e)),
3979 };
3980
3981 if suboptimal {
3982 self.recreate_swapchain = true;
3983 }
3984
3985 let extent = self.swapchain_state.swapchain.image_extent();
3986
3987 visual_world.set_viewport([extent[0] as f32, extent[1] as f32]);
3990
3991 let post_process_config = visual_world.post_processing().clone();
3992 let post_process_active = post_process_config.is_active();
3993
3994 if post_process_active {
3995 self.post_processing_renderer.ensure_window_targets(
3996 self.swapchain_state.swapchain_views.len(),
3997 extent,
3998 self.swapchain_state.swapchain.image_format(),
3999 self.swapchain_state.msaa_samples,
4000 &post_process_config,
4001 )?;
4002 }
4003
4004 let resolve_view = self.swapchain_state.swapchain_views[image_i as usize].clone();
4005 let post_process = if post_process_active {
4006 let targets = self
4007 .post_processing_renderer
4008 .window_frame_targets(image_i as usize)
4009 .ok_or("missing window post-processing targets")?
4010 .clone();
4011
4012 Some(PostProcessInvocation {
4013 final_output_view: resolve_view.clone(),
4014 final_color_format: self.swapchain_state.swapchain.image_format(),
4015 config: post_process_config.clone(),
4016 targets,
4017 })
4018 } else {
4019 None
4020 };
4021
4022 let device = self.context.device().clone();
4023 let queue = self.context.graphics_queue().clone();
4024
4025 self.render_mirror_captures(
4026 visual_world,
4027 self.swapchain_state.swapchain.image_format(),
4028 )?;
4029
4030 let (color_attachment_view, color_resolve_view, depth_view) =
4031 if let Some(post) = post_process.as_ref() {
4032 (
4033 post.targets
4034 .main_msaa_color
4035 .clone()
4036 .unwrap_or_else(|| post.targets.main_color.clone()),
4037 if post.targets.main_msaa_color.is_some() {
4038 Some(post.targets.main_color.clone())
4039 } else {
4040 None
4041 },
4042 post.targets.depth.clone(),
4043 )
4044 } else if self.swapchain_state.msaa_samples != SampleCount::Sample1 {
4045 (
4046 self.swapchain_state.msaa_color_views[image_i as usize].clone(),
4047 Some(resolve_view.clone()),
4048 self.swapchain_state.depth_views[image_i as usize].clone(),
4049 )
4050 } else {
4051 (
4052 resolve_view.clone(),
4053 None,
4054 self.swapchain_state.depth_views[image_i as usize].clone(),
4055 )
4056 };
4057
4058 let window_camera = visual_world
4059 .visual_camera(crate::engine::graphics::CameraTarget::Window)
4060 .ok_or("missing window camera")?;
4061 let window_eye = window_camera
4062 .eyes
4063 .get(0)
4064 .ok_or("window camera has no eyes")?;
4065 let render_view = RenderView {
4066 view: window_eye.view,
4067 proj: window_eye.proj,
4068 viewport: [extent[0] as f32, extent[1] as f32],
4069 kind: RenderViewKind::Window,
4070 };
4071
4072 let cb = self.build_draw_batches_command_buffer(
4073 visual_world,
4074 &render_view,
4075 image_i as usize,
4076 self.swapchain_state.swapchain_views.len().max(1),
4077 color_attachment_view,
4078 color_resolve_view,
4079 depth_view,
4080 extent,
4081 post_process,
4082 None,
4083 )?;
4084
4085 let image_i_usize = image_i as usize;
4088 if self.images_in_flight.len() != self.swapchain_state.swapchain_views.len() {
4089 self.images_in_flight = (0..self.swapchain_state.swapchain_views.len())
4091 .map(|_| None)
4092 .collect();
4093 }
4094
4095 let image_future: Box<dyn GpuFuture> = self.images_in_flight[image_i_usize]
4096 .take()
4097 .unwrap_or_else(|| sync::now(device.clone()).boxed());
4098
4099 self.window.pre_present_notify();
4102
4103 let execution = image_future
4104 .join(acquire_future)
4105 .then_execute(queue.clone(), cb)?
4106 .then_swapchain_present(
4107 queue.clone(),
4108 SwapchainPresentInfo::swapchain_image_index(
4109 self.swapchain_state.swapchain.clone(),
4110 image_i,
4111 ),
4112 )
4113 .then_signal_fence_and_flush();
4114
4115 match execution.map_err(Validated::unwrap) {
4116 Ok(future) => {
4117 self.images_in_flight[image_i_usize] = Some(future.boxed());
4120 }
4121 Err(VulkanError::OutOfDate) => {
4122 self.recreate_swapchain = true;
4123 unsafe {
4128 println!("[VulkanoRenderer] swapchain out of date during flush");
4129 let _ = device.wait_idle();
4130 for slot in self.images_in_flight.iter_mut() {
4131 if let Some(mut fut) = slot.take() {
4132 fut.signal_finished();
4133 fut.cleanup_finished();
4134 }
4135 }
4136 }
4137 }
4138 Err(e) => {
4139 println!("[VulkanoRenderer] failed to flush future: {e}");
4140
4141 unsafe {
4142 println!("[VulkanoRenderer] waiting for device idle after flush failure");
4143 let _ = device.wait_idle();
4144 for slot in self.images_in_flight.iter_mut() {
4145 if let Some(mut fut) = slot.take() {
4146 fut.signal_finished();
4147 fut.cleanup_finished();
4148 }
4149 }
4150 }
4151 }
4152 }
4153
4154 Ok(())
4155 }
4156
4157 pub fn upload_mesh(
4158 &mut self,
4159 handle: MeshHandle,
4160 mesh: &CpuMesh,
4161 ) -> Result<(), Box<dyn std::error::Error>> {
4162 static SKIN_UPLOAD_LOG_COUNT: AtomicUsize = AtomicUsize::new(0);
4163
4164 if self.meshes.contains_key(&handle) {
4165 return Ok(());
4166 }
4167
4168 if mesh.vertices.is_empty() {
4169 return Err("mesh has no vertices".into());
4170 }
4171 if mesh.indices_u32.is_empty() {
4172 return Err("mesh has no indices".into());
4173 }
4174
4175 let memory_allocator = self.context.memory_allocator().clone();
4176 let queue = self.context.graphics_queue().clone();
4177
4178 let vertices_src = Buffer::from_iter(
4180 memory_allocator.clone(),
4181 BufferCreateInfo {
4182 usage: BufferUsage::TRANSFER_SRC,
4183 ..Default::default()
4184 },
4185 AllocationCreateInfo {
4186 memory_type_filter: MemoryTypeFilter::PREFER_HOST
4187 | MemoryTypeFilter::HOST_SEQUENTIAL_WRITE,
4188 ..Default::default()
4189 },
4190 mesh.vertices.iter().copied(),
4191 )?;
4192
4193 let skin_src: Option<Subbuffer<[GpuSkinVertex]>> = match (&mesh.joints0, &mesh.weights0)
4194 {
4195 (Some(joints0), Some(weights0))
4196 if joints0.len() == mesh.vertices.len()
4197 && weights0.len() == mesh.vertices.len() =>
4198 {
4199 if env_flag("CAT_DEBUG_SKIN_UPLOAD") {
4200 let limit = env_usize("CAT_DEBUG_SKIN_UPLOAD_LIMIT").unwrap_or(3);
4201 let n = SKIN_UPLOAD_LOG_COUNT.fetch_add(1, Ordering::Relaxed);
4202 if n < limit {
4203 println!(
4204 "[VulkanoRenderer] skin upload: mesh={handle:?} verts={} indices={} joints0_verts={} weights0_verts={}",
4205 mesh.vertices.len(),
4206 mesh.indices_u32.len(),
4207 joints0.len(),
4208 weights0.len()
4209 );
4210 for vi in 0..mesh.vertices.len().min(8) {
4211 let j = joints0[vi];
4212 let w = weights0[vi];
4213 let sum = w[0] + w[1] + w[2] + w[3];
4214 println!(" v[{vi:04}] joints={j:?} weights={w:?} sum={sum:.6}",);
4215 }
4216
4217 if env_flag("CAT_DEBUG_SKIN_HIST") {
4218 let mut joint_weight: HashMap<u16, f32> = HashMap::new();
4219 for (j, w) in joints0.iter().copied().zip(weights0.iter().copied())
4220 {
4221 for lane in 0..4 {
4222 let jw = w[lane];
4223 if jw > 0.0 {
4224 *joint_weight.entry(j[lane]).or_insert(0.0) += jw;
4225 }
4226 }
4227 }
4228
4229 let mut entries: Vec<(u16, f32)> =
4230 joint_weight.into_iter().collect();
4231 entries.sort_by(|a, b| b.1.total_cmp(&a.1));
4232 println!(
4233 "[VulkanoRenderer] skin joint histogram (top 12 by total weight):"
4234 );
4235 for (rank, (joint, total)) in
4236 entries.into_iter().take(12).enumerate()
4237 {
4238 println!(" #{rank:02} joint={joint} total_weight={total:.3}");
4239 }
4240 }
4241 }
4242 }
4243
4244 let skin_iter =
4245 joints0
4246 .iter()
4247 .copied()
4248 .zip(weights0.iter().copied())
4249 .map(|(j, w)| GpuSkinVertex {
4250 joints0: j,
4251 weights0: w,
4252 });
4253
4254 Some(Buffer::from_iter(
4255 memory_allocator.clone(),
4256 BufferCreateInfo {
4257 usage: BufferUsage::TRANSFER_SRC,
4258 ..Default::default()
4259 },
4260 AllocationCreateInfo {
4261 memory_type_filter: MemoryTypeFilter::PREFER_HOST
4262 | MemoryTypeFilter::HOST_SEQUENTIAL_WRITE,
4263 ..Default::default()
4264 },
4265 skin_iter,
4266 )?)
4267 }
4268 (Some(joints0), Some(weights0)) => {
4269 if env_flag("CAT_DEBUG_SKIN_UPLOAD") {
4270 println!(
4271 "[VulkanoRenderer] skin upload skipped (len mismatch): mesh={handle:?} verts={} joints0_len={} weights0_len={}",
4272 mesh.vertices.len(),
4273 joints0.len(),
4274 weights0.len()
4275 );
4276 }
4277 None
4278 }
4279 (Some(joints0), None) => {
4280 if env_flag("CAT_DEBUG_SKIN_UPLOAD") {
4281 println!(
4282 "[VulkanoRenderer] skin upload skipped (missing weights0): mesh={handle:?} verts={} joints0_len={}",
4283 mesh.vertices.len(),
4284 joints0.len(),
4285 );
4286 }
4287 None
4288 }
4289 (None, Some(weights0)) => {
4290 if env_flag("CAT_DEBUG_SKIN_UPLOAD") {
4291 println!(
4292 "[VulkanoRenderer] skin upload skipped (missing joints0): mesh={handle:?} verts={} weights0_len={}",
4293 mesh.vertices.len(),
4294 weights0.len(),
4295 );
4296 }
4297 None
4298 }
4299 (None, None) => None,
4300 };
4301
4302 let indices_src = Buffer::from_iter(
4303 memory_allocator.clone(),
4304 BufferCreateInfo {
4305 usage: BufferUsage::TRANSFER_SRC,
4306 ..Default::default()
4307 },
4308 AllocationCreateInfo {
4309 memory_type_filter: MemoryTypeFilter::PREFER_HOST
4310 | MemoryTypeFilter::HOST_SEQUENTIAL_WRITE,
4311 ..Default::default()
4312 },
4313 mesh.indices_u32.iter().copied(),
4314 )?;
4315
4316 let vertices_dst = Buffer::new_slice::<CpuVertex>(
4318 memory_allocator.clone(),
4319 BufferCreateInfo {
4320 usage: BufferUsage::VERTEX_BUFFER | BufferUsage::TRANSFER_DST,
4321 ..Default::default()
4322 },
4323 AllocationCreateInfo {
4324 memory_type_filter: MemoryTypeFilter::PREFER_DEVICE,
4325 ..Default::default()
4326 },
4327 mesh.vertices.len() as DeviceSize,
4328 )?;
4329
4330 let skin_dst: Option<Subbuffer<[GpuSkinVertex]>> = skin_src
4331 .as_ref()
4332 .map(|_| {
4333 Buffer::new_slice::<GpuSkinVertex>(
4334 memory_allocator.clone(),
4335 BufferCreateInfo {
4336 usage: BufferUsage::VERTEX_BUFFER | BufferUsage::TRANSFER_DST,
4337 ..Default::default()
4338 },
4339 AllocationCreateInfo {
4340 memory_type_filter: MemoryTypeFilter::PREFER_DEVICE,
4341 ..Default::default()
4342 },
4343 mesh.vertices.len() as DeviceSize,
4344 )
4345 })
4346 .transpose()?;
4347
4348 let indices_dst = Buffer::new_slice::<u32>(
4349 memory_allocator.clone(),
4350 BufferCreateInfo {
4351 usage: BufferUsage::INDEX_BUFFER | BufferUsage::TRANSFER_DST,
4352 ..Default::default()
4353 },
4354 AllocationCreateInfo {
4355 memory_type_filter: MemoryTypeFilter::PREFER_DEVICE,
4356 ..Default::default()
4357 },
4358 mesh.indices_u32.len() as DeviceSize,
4359 )?;
4360
4361 let mut cbb = AutoCommandBufferBuilder::primary(
4363 self.command_buffer_allocator.clone(),
4364 queue.queue_family_index(),
4365 CommandBufferUsage::OneTimeSubmit,
4366 )?;
4367
4368 cbb.copy_buffer(CopyBufferInfo::buffers(vertices_src, vertices_dst.clone()))?;
4369 if let (Some(src), Some(dst)) = (skin_src, skin_dst.as_ref()) {
4370 cbb.copy_buffer(CopyBufferInfo::buffers(src, dst.clone()))?;
4371 }
4372 cbb.copy_buffer(CopyBufferInfo::buffers(indices_src, indices_dst.clone()))?;
4373
4374 let cb = cbb.build()?;
4375
4376 cb.execute(queue.clone())?
4377 .then_signal_fence_and_flush()?
4378 .wait(None)?;
4379
4380 self.meshes.insert(
4381 handle,
4382 VulkanoGpuMesh {
4383 vertices: vertices_dst,
4384 skin_vertices: skin_dst,
4385 indices: indices_dst,
4386 index_count: mesh.index_count(),
4387 },
4388 );
4389
4390 Ok(())
4391 }
4392 }
4393}
4394
4395pub struct VulkanoRenderer {
4397 vulkano: Option<vulkano_backend::VulkanoState>,
4398 next_mesh_handle: u32,
4399 next_texture_handle: u32,
4400 did_enable_present_loop_log: bool,
4401 msaa_mode_override: Option<MsaaMode>,
4402}
4403
4404impl VulkanoRenderer {
4405 pub fn new() -> Self {
4406 Self {
4407 vulkano: None,
4408 next_mesh_handle: 0,
4409 next_texture_handle: 1,
4411 did_enable_present_loop_log: false,
4412 msaa_mode_override: None,
4413 }
4414 }
4415
4416 pub fn msaa_mode_override(&self) -> Option<MsaaMode> {
4417 self.msaa_mode_override
4418 }
4419
4420 pub fn set_msaa_mode(&mut self, mode: MsaaMode) -> Result<(), &'static str> {
4421 if self.vulkano.is_some() {
4422 return Err("cannot change MSAA mode after renderer initialization");
4423 }
4424 self.msaa_mode_override = Some(mode);
4425 Ok(())
4426 }
4427
4428 pub fn init_for_window(
4429 &mut self,
4430 window: &Arc<Window>,
4431 xr_required: Option<(&[String], &[String])>,
4432 ) -> Result<(), Box<dyn std::error::Error>> {
4433 if self.vulkano.is_none() {
4434 let msaa_mode = self.msaa_mode_override.unwrap_or_default();
4435 self.vulkano = Some(vulkano_backend::VulkanoState::new(
4436 window.clone(),
4437 xr_required,
4438 msaa_mode,
4439 )?);
4440 println!("[VulkanoRenderer] Vulkano swapchain/render-pass initialized");
4441 }
4442
4443 Ok(())
4444 }
4445
4446 pub fn resize(&mut self, size: winit::dpi::PhysicalSize<u32>) {
4447 let _ = size;
4448 if let Some(vulkano) = self.vulkano.as_mut() {
4449 vulkano.window_resized = true;
4450 }
4451 }
4452
4453 pub fn upload_mesh(
4454 &mut self,
4455 mesh: &CpuMesh,
4456 ) -> Result<MeshHandle, Box<dyn std::error::Error>> {
4457 let Some(vulkano) = self.vulkano.as_mut() else {
4458 return Err("VulkanoRenderer not initialized (call init_for_window first)".into());
4459 };
4460
4461 let handle = MeshHandle(self.next_mesh_handle);
4462 self.next_mesh_handle = self.next_mesh_handle.wrapping_add(1);
4463
4464 vulkano.upload_mesh(handle, mesh)?;
4465 Ok(handle)
4466 }
4467
4468 pub fn render_visual_world(
4469 &mut self,
4470 visual_world: &mut VisualWorld,
4471 ) -> Result<(), Box<dyn std::error::Error>> {
4472 let Some(vulkano) = self.vulkano.as_mut() else {
4473 return Err("VulkanoRenderer not initialized (call init_for_window first)".into());
4474 };
4475
4476 if !self.did_enable_present_loop_log {
4477 self.did_enable_present_loop_log = true;
4478 println!("[VulkanoRenderer] Present loop enabled");
4479 }
4480
4481 vulkano.render_visual_world(visual_world)
4482 }
4483
4484 pub fn window_vk_format_raw(&self) -> Option<u32> {
4485 let vulkano = self.vulkano.as_ref()?;
4486 let format = vulkano.window_color_format();
4487 let vk: ash::vk::Format = format.into();
4488 Some(vk.as_raw() as u32)
4489 }
4490
4491 pub fn render_xr_eye_offscreen(
4492 &mut self,
4493 visual_world: &mut VisualWorld,
4494 eye: usize,
4495 extent: [u32; 2],
4496 ) -> Result<(), Box<dyn std::error::Error>> {
4497 let Some(vulkano) = self.vulkano.as_mut() else {
4498 return Err("VulkanoRenderer not initialized (call init_for_window first)".into());
4499 };
4500
4501 vulkano.render_xr_eye_offscreen(visual_world, eye, extent)
4502 }
4503
4504 pub fn xr_offscreen_vk_image(&self, eye: usize) -> Option<ash::vk::Image> {
4505 self.vulkano.as_ref()?.xr_offscreen_vk_image(eye)
4506 }
4507
4508 pub fn xr_vulkan_graphics(&self) -> Option<crate::engine::graphics::XrVulkanGraphics> {
4512 use ash::vk::Handle as _;
4513 use std::ffi::c_void;
4514 use vulkano::VulkanObject;
4515
4516 let vulkano = self.vulkano.as_ref()?;
4517
4518 let device = vulkano.context.device().clone();
4519 let queue = vulkano.context.graphics_queue().clone();
4520 let instance = device.instance().clone();
4521 let physical_device = device.physical_device();
4522
4523 let vk_instance = instance.handle().as_raw() as usize as *const c_void;
4524 let vk_physical_device = physical_device.handle().as_raw() as usize as *const c_void;
4525 let vk_device = device.handle().as_raw() as usize as *const c_void;
4526 let vk_queue = queue.handle().as_raw() as usize as *const c_void;
4527
4528 Some(crate::engine::graphics::XrVulkanGraphics {
4529 vk_instance,
4530 vk_physical_device,
4531 vk_device,
4532 vk_queue,
4533 queue_family_index: queue.queue_family_index(),
4534 queue_index: 0,
4537 })
4538 }
4539}
4540
4541impl MeshUploader for VulkanoRenderer {
4542 fn upload_mesh(&mut self, mesh: &CpuMesh) -> Result<MeshHandle, Box<dyn std::error::Error>> {
4543 self.upload_mesh(mesh)
4544 }
4545}
4546
4547impl TextureUploader for VulkanoRenderer {
4548 fn upload_texture_rgba8(
4549 &mut self,
4550 rgba: &[u8],
4551 width: u32,
4552 height: u32,
4553 ) -> Result<TextureHandle, Box<dyn std::error::Error>> {
4554 let Some(vulkano) = self.vulkano.as_mut() else {
4555 return Err("VulkanoRenderer not initialized (call init_for_window first)".into());
4556 };
4557
4558 let handle = TextureHandle(self.next_texture_handle);
4559 self.next_texture_handle = self.next_texture_handle.wrapping_add(1);
4560
4561 vulkano.upload_texture_rgba8(handle, rgba, width, height)?;
4562 Ok(handle)
4563 }
4564
4565 fn upload_texture_bc7(
4566 &mut self,
4567 bc7_blocks: &[u8],
4568 width: u32,
4569 height: u32,
4570 srgb: bool,
4571 ) -> Result<TextureHandle, Box<dyn std::error::Error>> {
4572 let Some(vulkano) = self.vulkano.as_mut() else {
4573 return Err("VulkanoRenderer not initialized (call init_for_window first)".into());
4574 };
4575
4576 let handle = TextureHandle(self.next_texture_handle);
4577 self.next_texture_handle = self.next_texture_handle.wrapping_add(1);
4578
4579 vulkano.upload_texture_bc7(handle, bc7_blocks, width, height, srgb)?;
4580 Ok(handle)
4581 }
4582}