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 struct_iterable::Iterable;
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
101	unsafe extern "C" {
102		fn glfwGetTime() -> f64;
103		fn glfwSetTime(time: f64);
104	}
105
106	pub fn glfw_get_time() -> f64 {
107		unsafe {glfwGetTime()}
108	}
109
110	pub fn glfw_set_time(time: f64) {
111		unsafe {glfwSetTime(time)}
112	}
113}
114
115#[cfg(test)]
116mod tests {
117	use glfw::*;
118	use crate::prelude::*;
119	use std::{
120		collections::HashMap,
121		ffi::CStr,
122		path::PathBuf,
123		slice::from_raw_parts_mut,
124		sync::{
125			Arc,
126			Mutex,
127			RwLock,
128			atomic::{
129				AtomicBool,
130				Ordering,
131			}
132		},
133		thread,
134		time::Duration,
135	};
136
137	const TEST_TIME: f64 = 10.0;
138
139	#[derive(Debug)]
140	pub struct TestInstance {
141		pub ctx: Arc<RwLock<VulkanContext>>,
142		pub window: PWindow,
143		pub events: GlfwReceiver<(f64, WindowEvent)>,
144		pub glfw: Glfw,
145	}
146
147	impl TestInstance {
148		pub fn new(width: u32, height: u32, title: &str, window_mode: glfw::WindowMode) -> Result<Self, VulkanError> {
149			static GLFW_LOCK: Mutex<u32> = Mutex::new(0);
150			let glfw_lock = GLFW_LOCK.lock().unwrap();
151			let mut glfw = glfw::init(glfw::fail_on_errors).unwrap();
152			glfw.window_hint(WindowHint::ClientApi(ClientApiHint::NoApi));
153			let (mut window, events) = glfw.create_window(width, height, title, window_mode).expect("Failed to create GLFW window.");
154			drop(glfw_lock);
155			window.set_key_polling(true);
156			let device_requirement = DeviceRequirement {
157				can_graphics: true,
158				can_compute: false,
159				name_subtring: "",
160			};
161			let ctx = Arc::new(RwLock::new(create_vulkan_context(&window, device_requirement, PresentInterval::VSync, 1, false)?));
162			let ctx_lock = ctx.read().unwrap();
163			for gpu in VulkanGpuInfo::get_gpu_info(&ctx_lock.vkcore)?.iter() {
164				println!("Found GPU: {}", unsafe{CStr::from_ptr(gpu.properties.deviceName.as_ptr())}.to_str().unwrap());
165			}
166			println!("Chosen GPU name: {}", unsafe{CStr::from_ptr(ctx_lock.device.get_gpu().properties.deviceName.as_ptr())}.to_str().unwrap());
167			println!("Chosen GPU type: {:?}", ctx_lock.device.get_gpu().properties.deviceType);
168			drop(ctx_lock);
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(&mut ctx.write().unwrap(), 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 basic_test() {
241		derive_vertex_type! {
242			pub struct VertexType {
243				pub position: Vec2,
244			}
245		}
246		derive_uniform_buffer_type! {
247			pub struct UniformInput {
248				resolution: Vec3,
249				time: f32,
250			}
251		}
252		struct Resources {
253			uniform_input: Arc<dyn GenericUniformBuffer>,
254			pipeline: Pipeline,
255		}
256
257		impl Resources {
258			pub fn new(ctx: &VulkanContext) -> Result<Self, VulkanError> {
259				let device = ctx.device.clone();
260				let draw_shaders = Arc::new(DrawShaders::new(
261					Arc::new(VulkanShader::new_from_source_file_or_cache(device.clone(), ShaderSourcePath::VertexShader(PathBuf::from("shaders/test.vsh")), false, "main", OptimizationLevel::Performance, false)?),
262					None,
263					None,
264					None,
265					Arc::new(VulkanShader::new_from_source_file_or_cache(device.clone(), ShaderSourcePath::FragmentShader(PathBuf::from("shaders/test.fsh")), false, "main", OptimizationLevel::Performance, false)?),
266				));
267				let uniform_input: Arc<dyn GenericUniformBuffer> = Arc::new(UniformBuffer::<UniformInput>::new(device.clone())?);
268				let desc_props = Arc::new(DescriptorProps::default());
269				desc_props.new_uniform_buffer(0, 0, uniform_input.clone());
270				let pool_in_use = ctx.cmdpools[0].use_pool(None)?;
271				let vertices_data = vec![
272					VertexType {
273						position: Vec2::new(-1.0, -1.0),
274					},
275					VertexType {
276						position: Vec2::new( 1.0, -1.0),
277					},
278					VertexType {
279						position: Vec2::new(-1.0,  1.0),
280					},
281					VertexType {
282						position: Vec2::new( 1.0,  1.0),
283					},
284				];
285				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)?));
286				let mesh = Arc::new(GenericMeshWithMaterial::new(Arc::new(Mesh::new(VkPrimitiveTopology::VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP, vertices, buffer_unused(), buffer_unused(), buffer_unused())), "", None));
287				mesh.geometry.flush(pool_in_use.cmdbuf)?;
288				drop(pool_in_use);
289				ctx.cmdpools[0].wait_for_submit(u64::MAX)?;
290				mesh.geometry.discard_staging_buffers();
291				let pipeline = ctx.create_pipeline_builder(mesh, draw_shaders, desc_props)?
292				.set_cull_mode(VkCullModeFlagBits::VK_CULL_MODE_NONE as VkCullModeFlags)
293				.set_depth_test(false)
294				.set_depth_write(false)
295				.build()?;
296				Ok(Self {
297					uniform_input,
298					pipeline,
299				})
300			}
301
302			pub fn draw(&self, ctx: &VulkanContext, run_time: f64) -> Result<(), VulkanError> {
303				let scene = ctx.begin_scene(0, None)?;
304				let cmdbuf = scene.get_cmdbuf();
305				let extent = scene.get_rendertarget_extent();
306
307				let ui_data = unsafe {from_raw_parts_mut(self.uniform_input.get_staging_buffer_address()? as *mut UniformInput, 1)};
308				ui_data[0] = UniformInput {
309					resolution: Vec3::new(extent.width as f32, extent.height as f32, 1.0),
310					time: run_time as f32,
311				};
312				self.uniform_input.flush(cmdbuf)?;
313
314				scene.set_viewport_swapchain(0.0, 1.0)?;
315				scene.set_scissor_swapchain()?;
316				scene.begin_renderpass(Vec4::new(0.0, 0.0, 0.2, 1.0), 1.0, 0)?;
317				self.pipeline.draw(cmdbuf)?;
318				scene.end_renderpass()?;
319				scene.finish();
320				Ok(())
321			}
322		}
323
324		let mut inst = Box::new(TestInstance::new(1024, 768, "Vulkan test", glfw::WindowMode::Windowed).unwrap());
325		let resources = Resources::new(&inst.ctx.write().unwrap()).unwrap();
326		inst.run(Some(TEST_TIME),
327		move |ctx: &VulkanContext, run_time: f64| -> Result<(), VulkanError> {
328			resources.draw(ctx, run_time)
329		}).unwrap();
330	}
331
332	#[test]
333	fn avocado() {
334		derive_vertex_type! {
335			pub struct InstanceType {
336				pub transform: Mat4,
337			}
338		}
339		derive_uniform_buffer_type! {
340			pub struct UniformInputScene {
341				view_matrix: Mat4,
342				proj_matrix: Mat4,
343				light_dir: Vec3,
344				_pad1: f32,
345				light_color: Vec3,
346				_pad2: f32,
347				ambient_color: Vec3,
348				_pad3: f32,
349			}
350		}
351		struct Resources {
352			uniform_input_scene: Arc<dyn GenericUniformBuffer>,
353			object: GenericMeshSet<InstanceType>,
354			pipelines: HashMap<String, Pipeline>,
355		}
356
357		impl Resources {
358			const OBJ_ROWS: usize = 4;
359			const OBJ_COLS: usize = 4;
360
361			pub fn new(ctx: &VulkanContext) -> Result<Self, VulkanError> {
362				let device = ctx.device.clone();
363				let draw_shaders = Arc::new(DrawShaders::new(
364					Arc::new(VulkanShader::new_from_source_file_or_cache(device.clone(), ShaderSourcePath::VertexShader(PathBuf::from("shaders/objdisp.vsh")), false, "main", OptimizationLevel::Performance, false)?),
365					None,
366					None,
367					None,
368					Arc::new(VulkanShader::new_from_source_file_or_cache(device.clone(), ShaderSourcePath::FragmentShader(PathBuf::from("shaders/objdisp.fsh")), false, "main", OptimizationLevel::Performance, false)?),
369				));
370				let pool_in_use = ctx.cmdpools[0].use_pool(None)?;
371				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]))?;
372				let uniform_input_scene: Arc<dyn GenericUniformBuffer> = Arc::new(UniformBuffer::<UniformInputScene>::new(device.clone())?);
373				let desc_props = Arc::new(DescriptorProps::default());
374				desc_props.new_uniform_buffer(0, 0, uniform_input_scene.clone());
375				let mut pipelines: HashMap<String, Pipeline> = HashMap::with_capacity(object.meshset.len());
376				for mesh in object.meshset.values() {
377					if let Some(material) = &mesh.material {
378						if let Some(albedo) = material.get_albedo() {
379							if let MaterialComponent::Texture(texture) = albedo {
380								texture.prepare_for_sample(pool_in_use.cmdbuf)?;
381								let texture_input_albedo = TextureForSample {
382									texture: texture.clone(),
383									sampler: Arc::new(VulkanSampler::new_linear(device.clone(), true, false)?),
384								};
385								desc_props.new_texture(0, 1, texture_input_albedo);
386							}
387						}
388						if let Some(normal) = material.get_normal() {
389							if let MaterialComponent::Texture(texture) = normal {
390								texture.prepare_for_sample(pool_in_use.cmdbuf)?;
391								let texture_input_normal = TextureForSample {
392									texture: texture.clone(),
393									sampler: Arc::new(VulkanSampler::new_linear(device.clone(), true, false)?),
394								};
395								desc_props.new_texture(0, 2, texture_input_normal);
396							}
397						}
398					}
399				}
400				drop(pool_in_use);
401				ctx.cmdpools[0].wait_for_submit(u64::MAX)?;
402				object.discard_staging_buffers();
403				for (matname, mesh) in object.meshset.iter() {
404					let pipeline = ctx.create_pipeline_builder(mesh.clone(), draw_shaders.clone(), desc_props.clone())?
405					.set_depth_test(true)
406					.set_depth_write(true)
407					.build()?;
408					pipelines.insert(matname.clone(), pipeline);
409				}
410				Ok(Self {
411					uniform_input_scene,
412					object,
413					pipelines,
414				})
415			}
416
417			pub fn draw(&self, ctx: &VulkanContext, run_time: f64) -> Result<(), VulkanError> {
418				let scene = ctx.begin_scene(0, None)?;
419				let cmdbuf = scene.get_cmdbuf();
420				let extent = scene.get_rendertarget_extent();
421
422				let view_matrix = {
423					let eye = glm::vec3(25.0, 25.0, 25.0);
424					let center = glm::vec3(0.0, 0.0, 0.0);
425					let up = glm::vec3(0.0, 1.0, 0.0);
426					glm::look_at(&eye, &center, &up)
427				};
428
429				let mut proj_matrix = {
430					let fovy = pi::<f32>() / 3.0;
431					let aspect = extent.width as f32 / extent.height as f32;
432					perspective(aspect, fovy, 0.1, 1000.0)
433				};
434				proj_matrix[(1, 1)] *= -1.0;
435
436				let ui_data = unsafe {from_raw_parts_mut(self.uniform_input_scene.get_staging_buffer_address()? as *mut UniformInputScene, 1)};
437				ui_data[0].view_matrix = view_matrix;
438				ui_data[0].proj_matrix = proj_matrix;
439				ui_data[0].light_dir = normalize(&Vec3::new(-1.2, -1.0, -1.0));
440				ui_data[0].light_color = Vec3::new(1.0, 1.0, 1.0);
441				ui_data[0].ambient_color = Vec3::new(0.1, 0.2, 0.1);
442				self.uniform_input_scene.flush(cmdbuf)?;
443				let mut lock = self.object.edit_instances().unwrap();
444				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))) {
445					let x = x as f32 - Self::OBJ_ROWS as f32 * 0.5;
446					let y = y as f32 - Self::OBJ_COLS as f32 * 0.5;
447					lock[i] = InstanceType {
448						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))),
449					};
450				}
451				drop(lock);
452
453				scene.set_viewport_swapchain(0.0, 1.0)?;
454				scene.set_scissor_swapchain()?;
455				for pipeline in self.pipelines.values() {
456					pipeline.prepare_data(cmdbuf)?;
457					scene.begin_renderpass(Vec4::new(0.0, 0.2, 0.3, 1.0), 1.0, 0)?;
458					pipeline.draw(cmdbuf)?;
459					scene.end_renderpass()?;
460				}
461				scene.finish();
462				Ok(())
463			}
464		}
465
466		let mut inst = Box::new(TestInstance::new(1024, 768, "Vulkan avocado test", glfw::WindowMode::Windowed).unwrap());
467		let resources = Resources::new(&inst.ctx.write().unwrap()).unwrap();
468		inst.run(Some(TEST_TIME),
469		move |ctx: &VulkanContext, run_time: f64| -> Result<(), VulkanError> {
470			resources.draw(ctx, run_time)
471		}).unwrap();
472	}
473}