ailloli_ui_openxr/openxr_runtime/
vulkan.rs1use std::ffi::CString;
4
5use ash::vk::{self, Handle};
6use ash::{Device, Entry as VkEntry, Instance as VkInstance};
7use openxr as xr;
8
9use super::error::OpenXrRuntimeError;
10use super::instance::OpenXrInstance;
11
12pub struct OpenXrVulkanContext {
24 pub vk_entry: VkEntry,
26 pub vk_instance: VkInstance,
28 pub vk_device: Device,
30 pub physical_device: vk::PhysicalDevice,
32 pub memory_properties: vk::PhysicalDeviceMemoryProperties,
34 pub queue: vk::Queue,
36 pub queue_family_index: u32,
38 pub command_pool: vk::CommandPool,
40}
41
42impl OpenXrVulkanContext {
43 pub(crate) fn new(
63 xr: &OpenXrInstance,
64 application_name: &str,
65 engine_name: &str,
66 ) -> Result<Self, OpenXrRuntimeError> {
67 let vk_target_version = vk::make_api_version(0, 1, 1, 0);
68 let xr_target_version = xr::Version::new(1, 1, 0);
69
70 let requirements = xr
71 .instance
72 .graphics_requirements::<xr::Vulkan>(xr.system)
73 .map_err(|result| OpenXrRuntimeError::GraphicsRequirements { result })?;
74 if xr_target_version < requirements.min_api_version_supported
75 || xr_target_version > requirements.max_api_version_supported
76 {
77 return Err(OpenXrRuntimeError::UnsupportedVulkanVersion {
78 requested: xr_target_version,
79 min: requirements.min_api_version_supported,
80 max: requirements.max_api_version_supported,
81 });
82 }
83
84 let vk_entry =
85 unsafe { VkEntry::load() }.map_err(|err| OpenXrRuntimeError::LoadVulkanEntry {
86 message: err.to_string(),
87 })?;
88
89 let app_name = CString::new(application_name).map_err(|err| {
90 OpenXrRuntimeError::InvalidVulkanName {
91 field: "application_name",
92 message: err.to_string(),
93 }
94 })?;
95 let engine_name =
96 CString::new(engine_name).map_err(|err| OpenXrRuntimeError::InvalidVulkanName {
97 field: "engine_name",
98 message: err.to_string(),
99 })?;
100 let vk_app_info = vk::ApplicationInfo::default()
101 .application_name(app_name.as_c_str())
102 .engine_name(engine_name.as_c_str())
103 .api_version(vk_target_version);
104 let vk_instance_create_info =
105 vk::InstanceCreateInfo::default().application_info(&vk_app_info);
106
107 let vk_instance_raw = unsafe {
108 xr.instance.create_vulkan_instance(
109 xr.system,
110 std::mem::transmute(vk_entry.static_fn().get_instance_proc_addr),
111 &vk_instance_create_info as *const _ as *const _,
112 )
113 }
114 .map_err(|result| OpenXrRuntimeError::CreateVulkanInstance { result })?
115 .map_err(vk::Result::from_raw)
116 .map_err(|result| OpenXrRuntimeError::CreateVulkanInstanceVk { result })?;
117
118 let vk_instance = unsafe {
119 VkInstance::load(
120 vk_entry.static_fn(),
121 vk::Instance::from_raw(vk_instance_raw as _),
122 )
123 };
124
125 let physical_device = unsafe {
126 vk::PhysicalDevice::from_raw(
127 xr.instance
128 .vulkan_graphics_device(xr.system, vk_instance.handle().as_raw() as _)
129 .map_err(|result| OpenXrRuntimeError::VulkanGraphicsDevice { result })?
130 as _,
131 )
132 };
133 let memory_properties =
134 unsafe { vk_instance.get_physical_device_memory_properties(physical_device) };
135
136 let queue_family_index = unsafe {
137 vk_instance
138 .get_physical_device_queue_family_properties(physical_device)
139 .into_iter()
140 .enumerate()
141 .find_map(|(index, info)| {
142 if info.queue_flags.contains(vk::QueueFlags::GRAPHICS) {
143 Some(index as u32)
144 } else {
145 None
146 }
147 })
148 }
149 .ok_or(OpenXrRuntimeError::NoGraphicsQueueFamily)?;
150
151 let queue_priorities = [1.0];
152 let queue_infos = [vk::DeviceQueueCreateInfo::default()
153 .queue_family_index(queue_family_index)
154 .queue_priorities(&queue_priorities)];
155 let device_create_info = vk::DeviceCreateInfo::default().queue_create_infos(&queue_infos);
156
157 let vk_device_raw = unsafe {
158 xr.instance.create_vulkan_device(
159 xr.system,
160 std::mem::transmute(vk_entry.static_fn().get_instance_proc_addr),
161 physical_device.as_raw() as _,
162 &device_create_info as *const _ as *const _,
163 )
164 }
165 .map_err(|result| OpenXrRuntimeError::CreateVulkanDevice { result })?
166 .map_err(vk::Result::from_raw)
167 .map_err(|result| OpenXrRuntimeError::CreateVulkanDeviceVk { result })?;
168
169 let vk_device = unsafe {
170 Device::load(
171 vk_instance.fp_v1_0(),
172 vk::Device::from_raw(vk_device_raw as _),
173 )
174 };
175 let queue = unsafe { vk_device.get_device_queue(queue_family_index, 0) };
176
177 let command_pool = unsafe {
178 vk_device.create_command_pool(
179 &vk::CommandPoolCreateInfo::default()
180 .queue_family_index(queue_family_index)
181 .flags(vk::CommandPoolCreateFlags::RESET_COMMAND_BUFFER),
182 None,
183 )
184 }
185 .map_err(|result| OpenXrRuntimeError::CreateCommandPool { result })?;
186
187 Ok(Self {
188 vk_entry,
189 vk_instance,
190 vk_device,
191 physical_device,
192 memory_properties,
193 queue,
194 queue_family_index,
195 command_pool,
196 })
197 }
198
199 pub(crate) fn submit_one_time_commands<F>(&self, record: F) -> Result<(), OpenXrRuntimeError>
216 where
217 F: FnOnce(vk::CommandBuffer),
218 {
219 let command_buffer = unsafe {
220 self.vk_device.allocate_command_buffers(
221 &vk::CommandBufferAllocateInfo::default()
222 .command_pool(self.command_pool)
223 .level(vk::CommandBufferLevel::PRIMARY)
224 .command_buffer_count(1),
225 )
226 }
227 .map_err(|result| OpenXrRuntimeError::AllocateCommandBuffer { result })?[0];
228
229 let result = (|| {
230 unsafe {
231 self.vk_device.begin_command_buffer(
232 command_buffer,
233 &vk::CommandBufferBeginInfo::default()
234 .flags(vk::CommandBufferUsageFlags::ONE_TIME_SUBMIT),
235 )
236 }
237 .map_err(|result| OpenXrRuntimeError::BeginCommandBuffer { result })?;
238
239 record(command_buffer);
240
241 unsafe { self.vk_device.end_command_buffer(command_buffer) }
242 .map_err(|result| OpenXrRuntimeError::EndCommandBuffer { result })?;
243
244 let command_buffers = [command_buffer];
245 let submit_infos = [vk::SubmitInfo::default().command_buffers(&command_buffers)];
246 unsafe {
247 self.vk_device
248 .queue_submit(self.queue, &submit_infos, vk::Fence::null())
249 }
250 .map_err(|result| OpenXrRuntimeError::QueueSubmit { result })?;
251
252 unsafe { self.vk_device.queue_wait_idle(self.queue) }
253 .map_err(|result| OpenXrRuntimeError::QueueWaitIdle { result })?;
254
255 Ok(())
256 })();
257
258 unsafe {
259 self.vk_device
260 .free_command_buffers(self.command_pool, &[command_buffer]);
261 }
262
263 result
264 }
265
266 pub fn render_context(&self) -> ailloli_ui_render_vulkan::VulkanRenderContext<'_> {
276 ailloli_ui_render_vulkan::VulkanRenderContext::with_memory_properties(
277 &self.vk_device,
278 self.queue,
279 self.queue_family_index,
280 self.command_pool,
281 &self.memory_properties,
282 )
283 }
284}
285
286impl Drop for OpenXrVulkanContext {
287 fn drop(&mut self) {
288 unsafe {
289 if self.command_pool != vk::CommandPool::null() {
290 self.vk_device.destroy_command_pool(self.command_pool, None);
291 }
292 self.vk_device.destroy_device(None);
293 self.vk_instance.destroy_instance(None);
294 }
295 }
296}