vkobject_rs/
lib.rs

1
2#![allow(dead_code)]
3#![allow(clippy::too_many_arguments)]
4
5/// The common helper library
6pub mod common;
7
8/// The VkCore initializer
9pub mod init;
10
11/// The Vulkan basics
12pub mod basics;
13
14/// The Vulkan device
15pub mod device;
16
17/// The Vulkan surface
18pub mod surface;
19
20/// The Vulkan renderpass
21pub mod renderpass;
22
23/// The Vulkan framebuffer
24pub mod framebuffer;
25
26/// The render target
27pub mod rendertarget;
28
29/// The Vulkan swapchain
30pub mod swapchain;
31
32/// The Vulkan command pool
33pub mod cmdpool;
34
35/// The Vulkan shader
36pub mod shader;
37
38/// The Vulkan descriptor pool object
39pub mod descpool;
40
41/// The Vulkan context
42pub mod context;
43
44/// The buffer object
45pub mod buffer;
46
47/// The advanced buffer object that could be used as a vector
48pub mod buffervec;
49
50/// The texture object
51pub mod texture;
52
53/// The material object
54pub mod material;
55
56/// The mesh object
57pub mod mesh;
58
59/// The descriptor set properties
60pub mod descprops;
61
62/// The pipeline object to wiring up buffers from a mesh, shaders, rendertargets together.
63pub mod pipeline;
64
65/// The module for loading OBJ meshes
66pub mod wavefrontobj;
67
68extern crate nalgebra_glm as glm;
69
70/// The common things for you to use
71pub mod prelude {
72	pub use vkcore_rs::*;
73	pub use glm::*;
74	pub use half::f16;
75	pub use ffi_struct::*;
76	pub use crate::common::*;
77	pub use crate::init::*;
78	pub use crate::basics::*;
79	pub use crate::device::*;
80	pub use crate::surface::*;
81	pub use crate::renderpass::*;
82	pub use crate::framebuffer::*;
83	pub use crate::rendertarget::*;
84	pub use crate::swapchain::*;
85	pub use crate::cmdpool::*;
86	pub use crate::shader::*;
87	pub use crate::descpool::*;
88	pub use crate::context::*;
89	pub use crate::buffer::*;
90	pub use crate::buffervec::*;
91	pub use crate::texture::*;
92	pub use crate::material::*;
93	pub use crate::mesh::*;
94	pub use crate::descprops::*;
95	pub use crate::pipeline::*;
96	pub use crate::wavefrontobj::*;
97	pub use crate::derive_vertex_type;
98	pub use crate::derive_uniform_buffer_type;
99	pub use crate::derive_storage_buffer_type;
100	pub use crate::get_generic_uniform_buffer_cache;
101	pub use crate::get_generic_storage_buffer_cache;
102
103	unsafe extern "C" {
104		fn glfwGetTime() -> f64;
105		fn glfwSetTime(time: f64);
106	}
107
108	pub fn glfw_get_time() -> f64 {
109		unsafe {glfwGetTime()}
110	}
111
112	pub fn glfw_set_time(time: f64) {
113		unsafe {glfwSetTime(time)}
114	}
115}
116
117#[cfg(test)]
118mod tests {
119	use glfw::*;
120	use crate::prelude::*;
121	use std::{
122		collections::HashMap,
123		ffi::CStr,
124		path::PathBuf,
125		sync::{
126			Arc,
127			Mutex,
128			RwLock,
129			atomic::{
130				AtomicBool,
131				Ordering,
132			}
133		},
134		thread,
135		time::Duration,
136	};
137
138	const TEST_TIME: f64 = 10.0;
139
140	#[derive(Debug)]
141	pub struct TestInstance {
142		pub ctx: Arc<VulkanContext>,
143		pub window: PWindow,
144		pub events: GlfwReceiver<(f64, WindowEvent)>,
145		pub glfw: Glfw,
146	}
147
148	impl TestInstance {
149		pub fn new(width: u32, height: u32, title: &str, window_mode: glfw::WindowMode, additional_hints: Option<&[WindowHint]>) -> Result<Self, VulkanError> {
150			static GLFW_LOCK: Mutex<u32> = Mutex::new(0);
151			let glfw_lock = GLFW_LOCK.lock().unwrap();
152			let mut glfw = glfw::init(glfw::fail_on_errors).unwrap();
153			glfw.window_hint(WindowHint::ClientApi(ClientApiHint::NoApi));
154			additional_hints.map(|hints| hints.iter().map(|hint| glfw.window_hint(hint.clone())));
155			let (mut window, events) = glfw.create_window(width, height, title, window_mode).expect("Failed to create GLFW window.");
156			drop(glfw_lock);
157			window.set_key_polling(true);
158			let device_requirement = DeviceRequirement {
159				can_graphics: true,
160				can_compute: false,
161				name_subtring: "",
162			};
163			let ctx = Arc::new(create_vulkan_context(&window, device_requirement, PresentInterval::VSync, 1, false)?);
164			for gpu in VulkanGpuInfo::get_gpu_info(&ctx.vkcore)?.iter() {
165				println!("Found GPU: {}", unsafe{CStr::from_ptr(gpu.properties.deviceName.as_ptr())}.to_str().unwrap());
166			}
167			println!("Chosen GPU name: {}", unsafe{CStr::from_ptr(ctx.device.get_gpu().properties.deviceName.as_ptr())}.to_str().unwrap());
168			println!("Chosen GPU type: {:?}", ctx.device.get_gpu().properties.deviceType);
169			Ok(Self {
170				glfw,
171				window,
172				events,
173				ctx,
174			})
175		}
176
177		pub fn get_time(&self) -> f64 {
178			glfw_get_time()
179		}
180
181		pub fn set_time(&self, time: f64) {
182			glfw_set_time(time)
183		}
184
185		pub fn run(&mut self,
186			test_time: Option<f64>,
187			mut on_render: impl FnMut(&VulkanContext, f64) -> Result<(), VulkanError> + Send + 'static
188		) -> Result<(), VulkanError> {
189			let exit_flag = Arc::new(AtomicBool::new(false));
190			let exit_flag_cloned = exit_flag.clone();
191			let start_time = self.glfw.get_time();
192			let ctx = self.ctx.clone();
193			let renderer_thread = thread::spawn(move || {
194				let mut num_frames = 0;
195				let mut time_in_sec: u64 = 0;
196				let mut num_frames_prev: u64 = 0;
197				while !exit_flag_cloned.load(Ordering::Relaxed) {
198					let cur_frame_time = glfw_get_time();
199					let run_time = cur_frame_time - start_time;
200					on_render(&ctx, run_time).unwrap();
201					num_frames += 1;
202					let new_time_in_sec = run_time.floor() as u64;
203					if new_time_in_sec > time_in_sec {
204						let fps = num_frames - num_frames_prev;
205						println!("FPS: {fps}\tat {new_time_in_sec}s");
206						time_in_sec = new_time_in_sec;
207						num_frames_prev = num_frames;
208					}
209				}
210			});
211			while !self.window.should_close() {
212				let run_time = glfw_get_time() - start_time;
213				thread::sleep(Duration::from_millis(1));
214				self.glfw.poll_events();
215				for (_, event) in glfw::flush_messages(&self.events) {
216					match event {
217						glfw::WindowEvent::Key(Key::Escape, _, Action::Press, _) => {
218							self.window.set_should_close(true);
219						}
220						_ => {}
221					}
222				}
223				if let Some(test_time) = test_time {
224					if run_time >= test_time {
225						self.window.set_should_close(true);
226					}
227				}
228			}
229			exit_flag.store(true, Ordering::Relaxed);
230			renderer_thread.join().unwrap();
231			println!("End of the test");
232			Ok(())
233		}
234	}
235
236	unsafe impl Send for TestInstance {}
237	unsafe impl Sync for TestInstance {}
238
239	#[test]
240	fn device_info() {
241		let additional_hints = [WindowHint::Visible(false)];
242		let inst = Box::new(TestInstance::new(1024, 768, "Vulkan test", glfw::WindowMode::Windowed, Some(&additional_hints)).unwrap());
243		let gpu = inst.ctx.device.get_gpu();
244		for i in 0..gpu.mem_properties.memoryTypeCount {
245			let memtype = &gpu.mem_properties.memoryTypes[i as usize];
246			println!("memory type {i}: {memtype:#?}");
247		}
248		for i in 0..gpu.mem_properties.memoryHeapCount {
249			let heap = &gpu.mem_properties.memoryHeaps[i as usize];
250			println!("heap {i}: {}: {heap:#?}", format_size(heap.size));
251		}
252	}
253
254	#[test]
255	fn basic_test() {
256		derive_vertex_type! {
257			pub struct VertexType {
258				pub position: Vec2,
259			}
260		}
261		derive_uniform_buffer_type! {
262			pub struct UniformInput {
263				resolution: Vec3,
264				time: f32,
265			}
266		}
267		struct Resources {
268			uniform_input: Arc<dyn GenericUniformBuffer>,
269			pipeline: Pipeline,
270		}
271
272		impl Resources {
273			pub fn new(ctx: &VulkanContext) -> Result<Self, VulkanError> {
274				let device = ctx.device.clone();
275				let draw_shaders = Arc::new(DrawShaders::new(
276					Arc::new(VulkanShader::new_from_source_file_or_cache(device.clone(), ShaderSourcePath::VertexShader(PathBuf::from("shaders/test.vsh")), false, "main", OptimizationLevel::Performance, false)?),
277					None,
278					None,
279					None,
280					Arc::new(VulkanShader::new_from_source_file_or_cache(device.clone(), ShaderSourcePath::FragmentShader(PathBuf::from("shaders/test.fsh")), false, "main", OptimizationLevel::Performance, false)?),
281				));
282				let uniform_input: Arc<dyn GenericUniformBuffer> = Arc::new(UniformBuffer::<UniformInput>::new(device.clone(), None)?);
283				let desc_props = Arc::new(DescriptorProps::default());
284				desc_props.new_uniform_buffer(0, 0, uniform_input.clone());
285				let pool_in_use = ctx.cmdpools[0].use_pool(None)?;
286				let vertices_data = vec![
287					VertexType {
288						position: Vec2::new(-1.0,  1.0),
289					},
290					VertexType {
291						position: Vec2::new( 1.0,  1.0),
292					},
293					VertexType {
294						position: Vec2::new(-1.0, -1.0),
295					},
296					VertexType {
297						position: Vec2::new( 1.0, -1.0),
298					},
299				];
300				let vertices = Arc::new(RwLock::new(BufferWithType::new(device.clone(), &vertices_data, pool_in_use.cmdbuf, VkBufferUsageFlagBits::VK_BUFFER_USAGE_VERTEX_BUFFER_BIT as VkBufferUsageFlags)?));
301				let mesh = Arc::new(GenericMeshWithMaterial::new(Arc::new(Mesh::new(VkPrimitiveTopology::VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP, vertices, buffer_unused(), buffer_unused(), buffer_unused())), "", None));
302				mesh.geometry.flush(pool_in_use.cmdbuf)?;
303				drop(pool_in_use);
304				ctx.cmdpools[0].wait_for_submit(u64::MAX)?;
305				mesh.geometry.discard_staging_buffers();
306				let pipeline = ctx.create_pipeline_builder(mesh, draw_shaders, desc_props)?
307				.set_depth_test(false)
308				.set_depth_write(false)
309				.build()?;
310				Ok(Self {
311					uniform_input,
312					pipeline,
313				})
314			}
315
316			pub fn draw(&self, ctx: &VulkanContext, run_time: f64) -> Result<(), VulkanError> {
317				let scene = ctx.begin_scene(0, None)?;
318				let cmdbuf = scene.get_cmdbuf();
319				let extent = scene.get_rendertarget_extent();
320
321				let ui_data = unsafe {get_generic_uniform_buffer_cache!(self.uniform_input, UniformInput)};
322				*ui_data = UniformInput {
323					resolution: Vec3::new(extent.width as f32, extent.height as f32, 1.0),
324					time: run_time as f32,
325				};
326				self.uniform_input.flush(cmdbuf)?;
327
328				scene.set_viewport_swapchain(0.0, 1.0)?;
329				scene.set_scissor_swapchain()?;
330				scene.begin_renderpass(Vec4::new(0.0, 0.0, 0.2, 1.0), 1.0, 0)?;
331				self.pipeline.draw(cmdbuf)?;
332				scene.end_renderpass()?;
333				scene.finish();
334				Ok(())
335			}
336		}
337
338		let mut inst = Box::new(TestInstance::new(1024, 768, "Vulkan test", glfw::WindowMode::Windowed, None).unwrap());
339		let resources = Resources::new(&inst.ctx).unwrap();
340		inst.run(Some(TEST_TIME),
341		move |ctx: &VulkanContext, run_time: f64| -> Result<(), VulkanError> {
342			resources.draw(ctx, run_time)
343		}).unwrap();
344	}
345
346	#[test]
347	fn avocado() {
348		derive_vertex_type! {
349			pub struct InstanceType {
350				pub transform: Mat4,
351			}
352		}
353		derive_uniform_buffer_type! {
354			pub struct UniformInputScene {
355				view_matrix: Mat4,
356				proj_matrix: Mat4,
357				light_dir: Vec3,
358				light_color: Vec3,
359				ambient_color: Vec3,
360			}
361		}
362		struct Resources {
363			uniform_input_scene: Arc<dyn GenericUniformBuffer>,
364			object: GenericMeshSet<InstanceType>,
365			pipelines: HashMap<String, Pipeline>,
366		}
367
368		impl Resources {
369			const OBJ_ROWS: usize = 4;
370			const OBJ_COLS: usize = 4;
371
372			pub fn new(ctx: &VulkanContext) -> Result<Self, VulkanError> {
373				let device = ctx.device.clone();
374				let draw_shaders = Arc::new(DrawShaders::new(
375					Arc::new(VulkanShader::new_from_source_file_or_cache(device.clone(), ShaderSourcePath::VertexShader(PathBuf::from("shaders/objdisp.vsh")), false, "main", OptimizationLevel::Performance, false)?),
376					None,
377					None,
378					None,
379					Arc::new(VulkanShader::new_from_source_file_or_cache(device.clone(), ShaderSourcePath::FragmentShader(PathBuf::from("shaders/objdisp.fsh")), false, "main", OptimizationLevel::Performance, false)?),
380				));
381				let pool_in_use = ctx.cmdpools[0].use_pool(None)?;
382				let object = GenericMeshSet::create_meshset_from_obj_file::<f32, ObjVertPositionTexcoord2DNormalTangent, _>(device.clone(), "assets/testobj/avocado.obj", pool_in_use.cmdbuf, Some(&[InstanceType {transform: Mat4::identity()}; Self::OBJ_ROWS * Self::OBJ_COLS]))?;
383				let uniform_input_scene: Arc<dyn GenericUniformBuffer> = Arc::new(UniformBuffer::<UniformInputScene>::new(device.clone(), None)?);
384				let mut desc_props_set = HashMap::new();
385				let mut pipelines: HashMap<String, Pipeline> = HashMap::with_capacity(object.meshset.len());
386				for (set_name, mesh) in object.meshset.iter() {
387					let desc_props = Arc::new(DescriptorProps::default());
388					desc_props.new_uniform_buffer(0, 0, uniform_input_scene.clone());
389					if let Some(material) = &mesh.material {
390						if let Some(albedo) = material.get_albedo() {
391							if let MaterialComponent::Texture(texture) = albedo {
392								texture.prepare_for_sample(pool_in_use.cmdbuf)?;
393								let texture_input_albedo = TextureForSample {
394									texture: texture.clone(),
395									sampler: Arc::new(VulkanSampler::new_linear(device.clone(), true, false)?),
396								};
397								desc_props.new_texture(0, 1, texture_input_albedo);
398							}
399						}
400						if let Some(normal) = material.get_normal() {
401							if let MaterialComponent::Texture(texture) = normal {
402								texture.prepare_for_sample(pool_in_use.cmdbuf)?;
403								let texture_input_normal = TextureForSample {
404									texture: texture.clone(),
405									sampler: Arc::new(VulkanSampler::new_linear(device.clone(), true, false)?),
406								};
407								desc_props.new_texture(0, 2, texture_input_normal);
408							}
409						}
410					}
411					desc_props_set.insert(set_name, desc_props);
412				}
413				drop(pool_in_use);
414				ctx.cmdpools[0].wait_for_submit(u64::MAX)?;
415				object.discard_staging_buffers();
416				for (set_name, mesh) in object.meshset.iter() {
417					let pipeline = ctx.create_pipeline_builder(mesh.clone(), draw_shaders.clone(), desc_props_set.get(set_name).unwrap().clone())?
418					.set_depth_test(true)
419					.set_depth_write(true)
420					.build()?;
421					pipelines.insert(set_name.clone(), pipeline);
422				}
423				Ok(Self {
424					uniform_input_scene,
425					object,
426					pipelines,
427				})
428			}
429
430			pub fn draw(&self, ctx: &VulkanContext, run_time: f64) -> Result<(), VulkanError> {
431				let scene = ctx.begin_scene(0, None)?;
432				let cmdbuf = scene.get_cmdbuf();
433				let extent = scene.get_rendertarget_extent();
434
435				let view_matrix = {
436					let eye = glm::vec3(25.0, 25.0, 25.0);
437					let center = glm::vec3(0.0, 0.0, 0.0);
438					let up = glm::vec3(0.0, 1.0, 0.0);
439					glm::look_at(&eye, &center, &up)
440				};
441
442				let mut proj_matrix = {
443					let fovy = pi::<f32>() / 3.0;
444					let aspect = extent.width as f32 / extent.height as f32;
445					perspective(aspect, fovy, 0.1, 1000.0)
446				};
447				proj_matrix[(1, 1)] *= -1.0;
448
449				let mut lock = self.object.edit_instances().unwrap();
450				for (i, x, y) in (0..Self::OBJ_ROWS).flat_map(|y| (0..Self::OBJ_COLS).map(move |x| (y * Self::OBJ_COLS + x, x, y))) {
451					let x = x as f32 - Self::OBJ_ROWS as f32 * 0.5;
452					let y = y as f32 - Self::OBJ_COLS as f32 * 0.5;
453					lock[i] = InstanceType {
454						transform: glm::rotate(&glm::translate(&Mat4::identity(), &Vec3::new(-x * 8.0, -5.0, -y * 8.0)), run_time as f32 + i as f32, &normalize(&glm::vec3(0.0, 1.0, i as f32))),
455					};
456				}
457				drop(lock);
458
459				scene.set_viewport_swapchain(0.0, 1.0)?;
460				scene.set_scissor_swapchain()?;
461				for pipeline in self.pipelines.values() {
462					let ui_data = unsafe {get_generic_uniform_buffer_cache!(self.uniform_input_scene, UniformInputScene)};
463					ui_data.view_matrix = view_matrix;
464					ui_data.proj_matrix = proj_matrix;
465					ui_data.light_dir = normalize(&Vec3::new(-1.2, -1.0, -1.0));
466					ui_data.light_color = Vec3::new(1.0, 1.0, 1.0);
467					ui_data.ambient_color = Vec3::new(0.1, 0.2, 0.1);
468					self.uniform_input_scene.flush(cmdbuf)?;
469					pipeline.prepare_data(cmdbuf)?;
470					scene.begin_renderpass(Vec4::new(0.0, 0.2, 0.3, 1.0), 1.0, 0)?;
471					pipeline.draw(cmdbuf)?;
472					scene.end_renderpass()?;
473				}
474				scene.finish();
475				Ok(())
476			}
477		}
478
479		let mut inst = Box::new(TestInstance::new(1024, 768, "Vulkan avocado test", glfw::WindowMode::Windowed, None).unwrap());
480		let resources = Resources::new(&inst.ctx).unwrap();
481		inst.run(Some(TEST_TIME),
482		move |ctx: &VulkanContext, run_time: f64| -> Result<(), VulkanError> {
483			resources.draw(ctx, run_time)
484		}).unwrap();
485	}
486
487	#[test]
488	fn pano() {
489		derive_vertex_type! {
490			pub struct VertexType {
491				pub position: Vec2,
492			}
493		}
494		derive_vertex_type! {
495			pub struct InstanceType {
496				pub transform: Mat4,
497			}
498		}
499		derive_uniform_buffer_type! {
500			pub struct UniformInputSky {
501				resolution: Vec2,
502				view_matrix: Mat4,
503				proj_matrix: Mat4,
504			}
505		}
506		derive_uniform_buffer_type! {
507			pub struct UniformInputScene {
508				camera: Vec3,
509				view_matrix: Mat4,
510				proj_matrix: Mat4,
511			}
512		}
513
514		struct Resources {
515			uniform_input_sky: Arc<dyn GenericUniformBuffer>,
516			uniform_input_scene: Arc<dyn GenericUniformBuffer>,
517			pipeline_pano: Pipeline,
518			pipelines_avocado: HashMap<String, Pipeline>,
519		}
520
521		impl Resources {
522			pub fn new(ctx: &VulkanContext) -> Result<Self, VulkanError> {
523				let device = ctx.device.clone();
524				let pano_shaders = Arc::new(DrawShaders::new(
525					Arc::new(VulkanShader::new_from_source_file_or_cache(device.clone(), ShaderSourcePath::VertexShader(PathBuf::from("shaders/panosky.vsh")), false, "main", OptimizationLevel::Performance, false)?),
526					None,
527					None,
528					None,
529					Arc::new(VulkanShader::new_from_source_file_or_cache(device.clone(), ShaderSourcePath::FragmentShader(PathBuf::from("shaders/panosky.fsh")), false, "main", OptimizationLevel::Performance, false)?),
530				));
531				let reflection_shaders = Arc::new(DrawShaders::new(
532					Arc::new(VulkanShader::new_from_source_file_or_cache(device.clone(), ShaderSourcePath::VertexShader(PathBuf::from("shaders/objdisp_refl.vsh")), false, "main", OptimizationLevel::Performance, false)?),
533					None,
534					None,
535					None,
536					Arc::new(VulkanShader::new_from_source_file_or_cache(device.clone(), ShaderSourcePath::FragmentShader(PathBuf::from("shaders/objdisp_refl.fsh")), false, "main", OptimizationLevel::Performance, false)?),
537				));
538				let pool_in_use = ctx.cmdpools[0].use_pool(None)?;
539				let vertices_data = vec![
540					VertexType {
541						position: Vec2::new(-1.0,  1.0),
542					},
543					VertexType {
544						position: Vec2::new( 1.0,  1.0),
545					},
546					VertexType {
547						position: Vec2::new(-1.0, -1.0),
548					},
549					VertexType {
550						position: Vec2::new( 1.0, -1.0),
551					},
552				];
553				let uniform_input_sky: Arc<dyn GenericUniformBuffer> = Arc::new(UniformBuffer::<UniformInputSky>::new(device.clone(), None)?);
554				let uniform_input_scene: Arc<dyn GenericUniformBuffer> = Arc::new(UniformBuffer::<UniformInputScene>::new(device.clone(), None)?);
555				let texture_pano = Arc::new(VulkanTexture::new_from_path(device.clone(), pool_in_use.cmdbuf, "assets/pano.jpg", true, None, VkImageUsageFlagBits::VK_IMAGE_USAGE_SAMPLED_BIT as VkImageUsageFlags)?);
556				let texture_pano_sample = TextureForSample {
557					texture: texture_pano.clone(),
558					sampler: Arc::new(VulkanSampler::new_linear_clamp(device.clone(), false, false)?),
559				};
560				texture_pano.prepare_for_sample(pool_in_use.cmdbuf)?;
561				let pano_shaders_inputs = Arc::new(DescriptorProps::default());
562				pano_shaders_inputs.new_uniform_buffer(0, 0, uniform_input_sky.clone());
563				pano_shaders_inputs.new_texture(0, 1, texture_pano_sample.clone());
564				let vertices = Arc::new(RwLock::new(BufferWithType::new(device.clone(), &vertices_data, pool_in_use.cmdbuf, VkBufferUsageFlagBits::VK_BUFFER_USAGE_VERTEX_BUFFER_BIT as VkBufferUsageFlags)?));
565				let mesh = Arc::new(GenericMeshWithMaterial::new(Arc::new(Mesh::new(VkPrimitiveTopology::VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP, vertices, buffer_unused(), buffer_unused(), buffer_unused())), "", None));
566				mesh.geometry.flush(pool_in_use.cmdbuf)?;
567				let object = GenericMeshSet::create_meshset_from_obj_file::<f32, ObjVertPositionTexcoord2DNormalTangent, _>(device.clone(), "assets/testobj/avocado.obj", pool_in_use.cmdbuf, Some(&[InstanceType {transform: Mat4::identity()}; 1]))?;
568				let mut avocado_shader_inputs = HashMap::new();
569				let mut pipelines_avocado: HashMap<String, Pipeline> = HashMap::with_capacity(object.meshset.len());
570				for (set_name, mesh) in object.meshset.iter() {
571					let desc_props = Arc::new(DescriptorProps::default());
572					desc_props.new_uniform_buffer(0, 0, uniform_input_scene.clone());
573					desc_props.new_texture(0, 3, texture_pano_sample.clone());
574					if let Some(material) = &mesh.material {
575						if let Some(albedo) = material.get_albedo() {
576							if let MaterialComponent::Texture(texture) = albedo {
577								texture.prepare_for_sample(pool_in_use.cmdbuf)?;
578								let texture_input_albedo = TextureForSample {
579									texture: texture.clone(),
580									sampler: Arc::new(VulkanSampler::new_linear(device.clone(), true, false)?),
581								};
582								desc_props.new_texture(0, 1, texture_input_albedo);
583							}
584						}
585						if let Some(normal) = material.get_normal() {
586							if let MaterialComponent::Texture(texture) = normal {
587								texture.prepare_for_sample(pool_in_use.cmdbuf)?;
588								let texture_input_normal = TextureForSample {
589									texture: texture.clone(),
590									sampler: Arc::new(VulkanSampler::new_linear(device.clone(), true, false)?),
591								};
592								desc_props.new_texture(0, 2, texture_input_normal);
593							}
594						}
595					}
596					avocado_shader_inputs.insert(set_name, desc_props);
597				}
598				drop(pool_in_use);
599				ctx.cmdpools[0].wait_for_submit(u64::MAX)?;
600				mesh.geometry.discard_staging_buffers();
601				texture_pano.discard_staging_buffer();
602				let pipeline_pano = ctx.create_pipeline_builder(mesh, pano_shaders, pano_shaders_inputs)?
603				.set_depth_test(false)
604				.set_depth_write(false)
605				.build()?;
606				for (set_name, mesh) in object.meshset.iter() {
607					let pipeline = ctx.create_pipeline_builder(mesh.clone(), reflection_shaders.clone(), avocado_shader_inputs.get(set_name).unwrap().clone())?
608					.set_depth_test(true)
609					.set_depth_write(true)
610					.build()?;
611					pipelines_avocado.insert(set_name.clone(), pipeline);
612				}
613				Ok(Self {
614					uniform_input_sky,
615					uniform_input_scene,
616					pipeline_pano,
617					pipelines_avocado,
618				})
619			}
620
621			pub fn draw(&self, ctx: &VulkanContext, run_time: f64) -> Result<(), VulkanError> {
622				let scene = ctx.begin_scene(0, None)?;
623				let cmdbuf = scene.get_cmdbuf();
624				let extent = scene.get_rendertarget_extent();
625
626				let h_rotation = run_time * pi::<f64>() * 0.2;
627				let eye = glm::normalize(&glm::vec3(h_rotation.cos() as f32, (run_time * 0.3).sin() as f32, h_rotation.sin() as f32)) * 30.0;
628				let view_matrix = {
629					let center = glm::vec3(0.0, 0.0, 0.0);
630					let up = glm::vec3(0.0, 1.0, 0.0);
631					glm::look_at(&eye, &center, &up)
632				};
633
634				let mut proj_matrix = {
635					let fovy = pi::<f32>() / 3.0;
636					let aspect = extent.width as f32 / extent.height as f32;
637					perspective(aspect, fovy, 0.1, 1000.0)
638				};
639				proj_matrix[(1, 1)] *= -1.0;
640
641				let ui_data = unsafe {get_generic_uniform_buffer_cache!(self.uniform_input_sky, UniformInputSky)};
642				ui_data.resolution = Vec2::new(extent.width as f32, extent.height as f32);
643				ui_data.view_matrix = view_matrix;
644				ui_data.proj_matrix = proj_matrix;
645				self.uniform_input_sky.flush(cmdbuf)?;
646
647				let ui_data = unsafe {get_generic_uniform_buffer_cache!(self.uniform_input_scene, UniformInputScene)};
648				ui_data.camera = eye;
649				ui_data.view_matrix = view_matrix;
650				ui_data.proj_matrix = proj_matrix;
651				self.uniform_input_scene.flush(cmdbuf)?;
652				for pipeline_avocado in self.pipelines_avocado.values() {
653					pipeline_avocado.prepare_data(cmdbuf)?;
654				}
655
656				scene.set_viewport_swapchain(0.0, 1.0)?;
657				scene.set_scissor_swapchain()?;
658				scene.begin_renderpass(Vec4::new(0.0, 0.0, 0.2, 1.0), 1.0, 0)?;
659				self.pipeline_pano.draw(cmdbuf)?;
660				for pipeline_avocado in self.pipelines_avocado.values() {
661					pipeline_avocado.draw(cmdbuf)?;
662				}
663				scene.end_renderpass()?;
664				scene.finish();
665				Ok(())
666			}
667		}
668
669		let mut inst = Box::new(TestInstance::new(1024, 768, "Vulkan panorama test", glfw::WindowMode::Windowed, None).unwrap());
670		let resources = Resources::new(&inst.ctx).unwrap();
671		inst.run(Some(TEST_TIME),
672		move |ctx: &VulkanContext, run_time: f64| -> Result<(), VulkanError> {
673			resources.draw(ctx, run_time)
674		}).unwrap();
675	}
676}