1use super::common::*;
18use std::collections::HashMap;
19use std::sync::{Arc, Mutex};
20
21#[cfg(feature = "vulkan")]
23pub mod vulkan_impl {
24 use super::*;
25
26 type VkResult = i32;
28 type VkInstance = *mut std::ffi::c_void;
29 type VkPhysicalDevice = *mut std::ffi::c_void;
30 type VkDevice = *mut std::ffi::c_void;
31 type VkQueue = *mut std::ffi::c_void;
32 type VkCommandPool = *mut std::ffi::c_void;
33 type VkCommandBuffer = *mut std::ffi::c_void;
34 type VkBuffer = *mut std::ffi::c_void;
35 type VkDeviceMemory = *mut std::ffi::c_void;
36 type VkPipeline = *mut std::ffi::c_void;
37 type VkPipelineLayout = *mut std::ffi::c_void;
38 type VkDescriptorSetLayout = *mut std::ffi::c_void;
39 type VkDescriptorPool = *mut std::ffi::c_void;
40 type VkDescriptorSet = *mut std::ffi::c_void;
41 type VkShaderModule = *mut std::ffi::c_void;
42 type VkFence = *mut std::ffi::c_void;
43 type VkSemaphore = *mut std::ffi::c_void;
44
45 const VK_SUCCESS: VkResult = 0;
46 const VK_ERROR_OUT_OF_HOST_MEMORY: VkResult = -1;
47 const VK_ERROR_OUT_OF_DEVICE_MEMORY: VkResult = -2;
48
49 #[derive(Debug, Clone, Copy)]
51 pub struct SafeVkHandle(pub *mut std::ffi::c_void);
52
53 unsafe impl Send for SafeVkHandle {}
56 unsafe impl Sync for SafeVkHandle {}
57
58 impl SafeVkHandle {
59 fn null() -> Self {
60 Self(std::ptr::null_mut())
61 }
62
63 fn is_null(&self) -> bool {
64 self.0.is_null()
65 }
66 }
67
68 fn vk_create_instance() -> (VkResult, SafeVkHandle) {
70 (VK_SUCCESS, SafeVkHandle::null())
71 }
72
73 fn vk_enumerate_physical_devices(_instance: SafeVkHandle) -> (VkResult, Vec<SafeVkHandle>) {
74 (VK_SUCCESS, vec![])
75 }
76
77 fn vk_get_physical_device_properties(_device: SafeVkHandle) -> VulkanPhysicalDeviceProperties {
78 VulkanPhysicalDeviceProperties::default()
79 }
80
81 fn vk_get_physical_device_memory_properties(_device: SafeVkHandle) -> VulkanMemoryProperties {
82 VulkanMemoryProperties::default()
83 }
84
85 fn vk_create_device(_physical_device: SafeVkHandle) -> (VkResult, SafeVkHandle) {
86 (VK_SUCCESS, SafeVkHandle::null())
87 }
88
89 fn vk_get_device_queue(
90 _device: SafeVkHandle,
91 _queue_family: u32,
92 _queue_index: u32,
93 ) -> SafeVkHandle {
94 SafeVkHandle::null()
95 }
96
97 fn vk_allocate_memory(
98 _device: SafeVkHandle,
99 _size: usize,
100 _memory_type_index: u32,
101 ) -> (VkResult, SafeVkHandle) {
102 (VK_SUCCESS, SafeVkHandle::null())
103 }
104
105 fn vk_create_buffer(
106 _device: SafeVkHandle,
107 _size: usize,
108 _usage: u32,
109 ) -> (VkResult, SafeVkHandle) {
110 (VK_SUCCESS, SafeVkHandle::null())
111 }
112
113 fn vk_bind_buffer_memory(
114 _device: SafeVkHandle,
115 _buffer: SafeVkHandle,
116 _memory: SafeVkHandle,
117 _offset: usize,
118 ) -> VkResult {
119 VK_SUCCESS
120 }
121
122 fn vk_map_memory(
123 _device: SafeVkHandle,
124 _memory: SafeVkHandle,
125 _offset: usize,
126 _size: usize,
127 ) -> (VkResult, *mut std::ffi::c_void) {
128 (VK_SUCCESS, std::ptr::null_mut())
129 }
130
131 fn vk_unmap_memory(_device: SafeVkHandle, _memory: SafeVkHandle) {
132 }
134
135 fn vk_create_command_pool(
136 _device: SafeVkHandle,
137 _queue_family: u32,
138 ) -> (VkResult, SafeVkHandle) {
139 (VK_SUCCESS, SafeVkHandle::null())
140 }
141
142 fn vk_allocate_command_buffers(
143 _device: SafeVkHandle,
144 _pool: SafeVkHandle,
145 _count: u32,
146 ) -> (VkResult, Vec<SafeVkHandle>) {
147 (VK_SUCCESS, vec![])
148 }
149
150 fn vk_queue_submit(
151 _queue: SafeVkHandle,
152 _command_buffers: &[SafeVkHandle],
153 _fence: SafeVkHandle,
154 ) -> VkResult {
155 VK_SUCCESS
156 }
157
158 fn vk_queue_wait_idle(_queue: SafeVkHandle) -> VkResult {
159 VK_SUCCESS
160 }
161
162 fn vk_device_wait_idle(_device: SafeVkHandle) -> VkResult {
163 VK_SUCCESS
164 }
165
166 #[derive(Debug, Clone, Default)]
168 pub struct VulkanPhysicalDeviceProperties {
169 pub api_version: u32,
170 pub driver_version: u32,
171 pub vendor_id: u32,
172 pub device_id: u32,
173 pub device_type: u32,
174 pub device_name: String,
175 pub max_compute_work_groupcount: [u32; 3],
176 pub max_compute_work_groupsize: [u32; 3],
177 pub max_compute_work_group_invocations: u32,
178 pub max_push_constants_size: u32,
179 pub max_memory_allocation_count: u32,
180 pub max_bound_descriptor_sets: u32,
181 pub max_storage_bufferrange: u64,
182 pub subgroup_size: u32,
183 pub supported_subgroup_operations: u32,
184 pub timestamp_period: f32,
185 }
186
187 #[derive(Debug, Clone, Default)]
189 pub struct VulkanMemoryProperties {
190 pub memory_type_count: u32,
191 pub memory_types: Vec<VulkanMemoryType>,
192 pub memory_heap_count: u32,
193 pub memory_heaps: Vec<VulkanMemoryHeap>,
194 }
195
196 #[derive(Debug, Clone, Default)]
198 pub struct VulkanMemoryType {
199 pub property_flags: u32,
200 pub heap_index: u32,
201 }
202
203 #[derive(Debug, Clone, Default)]
205 pub struct VulkanMemoryHeap {
206 pub size: u64,
207 pub flags: u32,
208 }
209
210 #[derive(Debug, Clone, Default)]
212 pub struct VulkanQueueFamilyProperties {
213 pub queue_flags: u32,
214 pub queue_count: u32,
215 pub timestamp_valid_bits: u32,
216 pub min_image_transfer_granularity: [u32; 3],
217 }
218
219 #[derive(Debug)]
221 pub struct VulkanComputePipeline {
222 pipeline: SafeVkHandle,
223 layout: SafeVkHandle,
224 descriptor_set_layout: SafeVkHandle,
225 shader_module: SafeVkHandle,
226 entry_point: String,
227 specialization_constants: HashMap<u32, Vec<u8>>,
228 }
229
230 impl VulkanComputePipeline {
231 fn new(
232 pipeline: SafeVkHandle,
233 layout: SafeVkHandle,
234 descriptor_set_layout: SafeVkHandle,
235 shader_module: SafeVkHandle,
236 entry_point: &str,
237 ) -> Self {
238 Self {
239 pipeline,
240 layout,
241 descriptor_set_layout,
242 shader_module,
243 entry_point: entry_point.to_string(),
244 specialization_constants: HashMap::new(),
245 }
246 }
247 }
248
249 pub struct VulkanBackend {
251 instance: SafeVkHandle,
252 physical_devices: Vec<VulkanPhysicalDeviceInfo>,
253 api_version: u32,
254 extensions: Vec<String>,
255 validation_layers_enabled: bool,
256 }
257
258 #[derive(Debug, Clone)]
260 struct VulkanPhysicalDeviceInfo {
261 handle: SafeVkHandle,
262 properties: VulkanPhysicalDeviceProperties,
263 memory_properties: VulkanMemoryProperties,
264 queue_families: Vec<VulkanQueueFamilyProperties>,
265 supports_compute: bool,
266 compute_queue_family: Option<u32>,
267 transfer_queue_family: Option<u32>,
268 }
269
270 impl VulkanBackend {
271 pub fn new() -> LinalgResult<Self> {
273 Self::with_options(VulkanBackendOptions::default())
274 }
275
276 pub fn with_options(options: VulkanBackendOptions) -> LinalgResult<Self> {
278 let (result, instance) = vk_create_instance();
280 if result != VK_SUCCESS {
281 return Err(LinalgError::ComputationError(format!(
282 "Failed to create Vulkan instance: error code {}",
283 result
284 )));
285 }
286
287 let (result, physical_device_handles) = vk_enumerate_physical_devices(instance);
289 if result != VK_SUCCESS {
290 return Err(LinalgError::ComputationError(format!(
291 "Failed to enumerate Vulkan physical devices: error code {}",
292 result
293 )));
294 }
295
296 let mut physical_devices = Vec::new();
297
298 for handle in physical_device_handles {
299 let properties = vk_get_physical_device_properties(handle);
300 let memory_properties = vk_get_physical_device_memory_properties(handle);
301
302 let queue_families = vec![VulkanQueueFamilyProperties {
304 queue_flags: 0x0F, queue_count: 16,
306 timestamp_valid_bits: 64,
307 min_image_transfer_granularity: [1, 1, 1],
308 }];
309
310 let compute_queue_family = queue_families
312 .iter()
313 .position(|qf| (qf.queue_flags & 0x02) != 0)
314 .map(|i| i as u32);
315
316 let transfer_queue_family = queue_families
317 .iter()
318 .position(|qf| (qf.queue_flags & 0x04) != 0)
319 .map(|i| i as u32);
320
321 physical_devices.push(VulkanPhysicalDeviceInfo {
322 handle,
323 properties,
324 memory_properties,
325 queue_families,
326 supports_compute: compute_queue_family.is_some(),
327 compute_queue_family,
328 transfer_queue_family,
329 });
330 }
331
332 Ok(Self {
333 instance,
334 physical_devices,
335 api_version: options.api_version.unwrap_or(0x0010_2000), extensions: options.extensions,
337 validation_layers_enabled: options.enable_validation,
338 })
339 }
340
341 pub fn api_version(&self) -> u32 {
343 self.api_version
344 }
345
346 pub fn validation_layers_enabled(&self) -> bool {
348 self.validation_layers_enabled
349 }
350
351 pub fn extensions(&self) -> &[String] {
353 &self.extensions
354 }
355 }
356
357 impl GpuBackend for VulkanBackend {
358 fn name(&self) -> &str {
359 "Vulkan"
360 }
361
362 fn is_available(&self) -> bool {
363 !self.physical_devices.is_empty()
364 && self.physical_devices.iter().any(|d| d.supports_compute)
365 }
366
367 fn list_devices(&self) -> LinalgResult<Vec<GpuDeviceInfo>> {
368 let devices = self
369 .physical_devices
370 .iter()
371 .filter(|d| d.supports_compute)
372 .map(|device| {
373 let props = &device.properties;
374 let mem_props = &device.memory_properties;
375
376 let total_memory: usize = mem_props
378 .memory_heaps
379 .iter()
380 .filter(|h| (h.flags & 0x01) != 0) .map(|h| h.size as usize)
382 .sum();
383
384 let memory_bandwidth = 500.0; GpuDeviceInfo {
388 device_type: GpuDeviceType::Vulkan,
389 name: props.device_name.clone(),
390 total_memory,
391 compute_units: props.max_compute_work_group_invocations,
392 clock_frequency: 1500, supports_fp64: true,
394 supports_fp16: true,
395 max_work_groupsize: props.max_compute_work_group_invocations as usize,
396 memory_bandwidth,
397 l2_cachesize: 4 * 1024 * 1024, shared_memory_per_block: 48 * 1024, registers_per_block: 65536,
400 warpsize: props.subgroup_size,
401 max_threads_per_mp: props.max_compute_work_group_invocations,
402 multiprocessor_count: 32, supports_tensor_cores: false, supports_mixed_precision: true,
405 vendor: Self::vendor_name(props.vendor_id),
406 }
407 })
408 .collect();
409
410 Ok(devices)
411 }
412
413 fn create_context(&self, device_id: usize) -> LinalgResult<Box<dyn GpuContext>> {
414 let compute_devices: Vec<_> = self
415 .physical_devices
416 .iter()
417 .filter(|d| d.supports_compute)
418 .collect();
419
420 if device_id >= compute_devices.len() {
421 return Err(LinalgError::ComputationError(format!(
422 "Invalid device ID: {} (available devices: {})",
423 device_id,
424 compute_devices.len()
425 )));
426 }
427
428 let physical_device = &compute_devices[device_id];
429
430 let (result, device) = vk_create_device(physical_device.handle);
432 if result != VK_SUCCESS {
433 return Err(LinalgError::ComputationError(format!(
434 "Failed to create Vulkan logical device: error code {}",
435 result
436 )));
437 }
438
439 let compute_queue = physical_device
441 .compute_queue_family
442 .map(|family| vk_get_device_queue(device, family, 0));
443
444 let transfer_queue = physical_device.transfer_queue_family.and_then(|family| {
446 if family != physical_device.compute_queue_family.unwrap_or(u32::MAX) {
447 Some(vk_get_device_queue(device, family, 0))
448 } else {
449 None
450 }
451 });
452
453 let (result, command_pool) =
455 vk_create_command_pool(device, physical_device.compute_queue_family.unwrap_or(0));
456 if result != VK_SUCCESS {
457 return Err(LinalgError::ComputationError(format!(
458 "Failed to create Vulkan command pool: error code {}",
459 result
460 )));
461 }
462
463 Ok(Box::new(VulkanContext::new(
464 (*physical_device).clone(),
465 device,
466 compute_queue,
467 transfer_queue,
468 command_pool,
469 )))
470 }
471 }
472
473 impl VulkanBackend {
474 fn vendor_name(vendor_id: u32) -> String {
475 match vendor_id {
476 0x1002 => "AMD".to_string(),
477 0x10DE => "NVIDIA".to_string(),
478 0x8086 => "Intel".to_string(),
479 0x13B5 => "ARM".to_string(),
480 0x5143 => "Qualcomm".to_string(),
481 0x1010 => "ImgTec".to_string(),
482 0x106B => "Apple".to_string(),
483 _ => format!("Unknown (0x{:04X})", vendor_id),
484 }
485 }
486 }
487
488 #[derive(Debug, Clone, Default)]
490 pub struct VulkanBackendOptions {
491 pub api_version: Option<u32>,
492 pub extensions: Vec<String>,
493 pub enable_validation: bool,
494 pub prefer_discrete_gpu: bool,
495 }
496
497 #[derive(Debug)]
499 pub struct VulkanContext {
500 physical_device_info: VulkanPhysicalDeviceInfo,
501 device: SafeVkHandle,
502 compute_queue: Option<SafeVkHandle>,
503 transfer_queue: Option<SafeVkHandle>,
504 command_pool: SafeVkHandle,
505 memory_pool: VulkanMemoryPool,
506 pipeline_cache: Arc<Mutex<HashMap<String, VulkanComputePipeline>>>,
507 descriptor_pool: Option<SafeVkHandle>,
508 performance_stats: VulkanPerformanceStats,
509 }
510
511 impl VulkanContext {
512 fn new(
513 physical_device_info: VulkanPhysicalDeviceInfo,
514 device: SafeVkHandle,
515 compute_queue: Option<SafeVkHandle>,
516 transfer_queue: Option<SafeVkHandle>,
517 command_pool: SafeVkHandle,
518 ) -> Self {
519 let memory_pool = VulkanMemoryPool::new(device);
520
521 Self {
522 physical_device_info,
523 device,
524 compute_queue,
525 transfer_queue,
526 command_pool,
527 memory_pool,
528 pipeline_cache: Arc::new(Mutex::new(HashMap::new())),
529 descriptor_pool: None,
530 performance_stats: VulkanPerformanceStats::new(),
531 }
532 }
533
534 pub fn device(&self) -> SafeVkHandle {
536 self.device
537 }
538
539 pub fn compute_queue(&self) -> Option<SafeVkHandle> {
541 self.compute_queue
542 }
543
544 pub fn transfer_queue(&self) -> Option<SafeVkHandle> {
546 self.transfer_queue
547 }
548
549 pub fn command_pool(&self) -> SafeVkHandle {
551 self.command_pool
552 }
553
554 pub fn performance_stats(&self) -> &VulkanPerformanceStats {
556 &self.performance_stats
557 }
558
559 pub fn compile_shader(
561 &mut self,
562 _shader_name: &str,
563 _spirv_code: &[u32],
564 _entry_point: &str,
565 ) -> LinalgResult<()> {
566 Ok(())
568 }
569
570 pub fn get_pipeline(&self, _name: &str) -> Option<SafeVkHandle> {
572 None
574 }
575
576 pub fn allocate_command_buffers(&self, count: u32) -> LinalgResult<Vec<SafeVkHandle>> {
578 let (result, buffers) =
579 vk_allocate_command_buffers(self.device, self.command_pool, count);
580
581 if result != VK_SUCCESS {
582 return Err(LinalgError::ComputationError(format!(
583 "Failed to allocate command buffers: error code {}",
584 result
585 )));
586 }
587
588 Ok(buffers)
589 }
590
591 pub fn submit_compute(&self, command_buffers: &[SafeVkHandle]) -> LinalgResult<()> {
593 if let Some(queue) = self.compute_queue {
594 let result = vk_queue_submit(queue, command_buffers, SafeVkHandle::null());
595 if result != VK_SUCCESS {
596 return Err(LinalgError::ComputationError(format!(
597 "Failed to submit compute commands: error code {}",
598 result
599 )));
600 }
601 }
602 Ok(())
603 }
604 }
605
606 impl GpuContext for VulkanContext {
607 #[allow(static_mut_refs)]
608 fn device_info(&self) -> &GpuDeviceInfo {
609 static mut CACHED_INFO: Option<GpuDeviceInfo> = None;
610
611 unsafe {
612 if CACHED_INFO.is_none() {
613 let props = &self.physical_device_info.properties;
614 let mem_props = &self.physical_device_info.memory_properties;
615
616 let total_memory: usize = mem_props
617 .memory_heaps
618 .iter()
619 .filter(|h| (h.flags & 0x01) != 0)
620 .map(|h| h.size as usize)
621 .sum();
622
623 CACHED_INFO = Some(GpuDeviceInfo {
624 device_type: GpuDeviceType::Vulkan,
625 name: props.device_name.clone(),
626 total_memory,
627 compute_units: props.max_compute_work_group_invocations,
628 clock_frequency: 1500,
629 supports_fp64: true,
630 supports_fp16: true,
631 max_work_groupsize: props.max_compute_work_group_invocations as usize,
632 memory_bandwidth: 500.0,
633 l2_cachesize: 4 * 1024 * 1024,
634 shared_memory_per_block: 48 * 1024,
635 registers_per_block: 65536,
636 warpsize: props.subgroup_size,
637 max_threads_per_mp: props.max_compute_work_group_invocations,
638 multiprocessor_count: 32,
639 supports_tensor_cores: false,
640 supports_mixed_precision: true,
641 vendor: VulkanBackend::vendor_name(props.vendor_id),
642 });
643 }
644
645 CACHED_INFO.as_ref().expect("GpuDeviceInfo not initialized")
646 }
647 }
648
649 fn synchronize(&self) -> LinalgResult<()> {
650 let result = vk_device_wait_idle(self.device);
651 if result != VK_SUCCESS {
652 return Err(LinalgError::ComputationError(format!(
653 "Vulkan synchronization failed: error code {}",
654 result
655 )));
656 }
657 Ok(())
658 }
659
660 fn available_memory(&self) -> LinalgResult<usize> {
661 let total_memory: usize = self
667 .physical_device_info
668 .memory_properties
669 .memory_heaps
670 .iter()
671 .filter(|h| (h.flags & 0x01) != 0)
672 .map(|h| h.size as usize)
673 .sum();
674
675 Ok(total_memory / 2)
676 }
677 }
678
679 impl GpuContextAlloc for VulkanContext {
680 fn allocate_buffer<T: Clone + Send + Sync + Copy + 'static + std::fmt::Debug>(
681 &self,
682 size: usize,
683 ) -> LinalgResult<Box<dyn GpuBuffer<T>>> {
684 let buffer = VulkanBuffer::new(size, self.device)?;
685 Ok(Box::new(buffer))
686 }
687 }
688
689 #[derive(Debug)]
691 struct VulkanMemoryPool {
692 device: SafeVkHandle,
693 allocations: HashMap<usize, VulkanAllocation>,
694 free_blocks: HashMap<u32, Vec<VulkanMemoryBlock>>,
695 total_allocated: usize,
696 peak_usage: usize,
697 allocation_count: usize,
698 }
699
700 #[derive(Debug)]
701 struct VulkanAllocation {
702 memory: SafeVkHandle,
703 size: usize,
704 memory_type_index: u32,
705 mapped_ptr: Option<*mut std::ffi::c_void>,
706 }
707
708 unsafe impl Send for VulkanAllocation {}
712 unsafe impl Sync for VulkanAllocation {}
713
714 #[derive(Debug, Clone)]
715 struct VulkanMemoryBlock {
716 allocation_id: usize,
717 offset: usize,
718 size: usize,
719 }
720
721 impl VulkanMemoryPool {
722 fn new(device: SafeVkHandle) -> Self {
723 Self {
724 device,
725 allocations: HashMap::new(),
726 free_blocks: HashMap::new(),
727 total_allocated: 0,
728 peak_usage: 0,
729 allocation_count: 0,
730 }
731 }
732
733 #[allow(dead_code)]
734 fn allocate(
735 &mut self,
736 size: usize,
737 memory_type_index: u32,
738 ) -> LinalgResult<VulkanMemoryBlock> {
739 if let Some(blocks) = self.free_blocks.get_mut(&memory_type_index) {
741 if let Some(block) = blocks.iter().position(|b| b.size >= size) {
742 return Ok(blocks.swap_remove(block));
743 }
744 }
745
746 let (result, memory) = vk_allocate_memory(self.device, size, memory_type_index);
748 if result != VK_SUCCESS {
749 return Err(LinalgError::ComputationError(format!(
750 "Vulkan memory allocation failed: error code {}",
751 result
752 )));
753 }
754
755 let allocation_id = self.allocation_count;
756 self.allocation_count += 1;
757
758 self.allocations.insert(
759 allocation_id,
760 VulkanAllocation {
761 memory,
762 size,
763 memory_type_index,
764 mapped_ptr: None,
765 },
766 );
767
768 self.total_allocated += size;
769 self.peak_usage = self.peak_usage.max(self.total_allocated);
770
771 Ok(VulkanMemoryBlock {
772 allocation_id,
773 offset: 0,
774 size,
775 })
776 }
777
778 #[allow(dead_code)]
779 fn deallocate(&mut self, block: VulkanMemoryBlock) {
780 if let Some(allocation) = self.allocations.get(&block.allocation_id) {
781 self.free_blocks
782 .entry(allocation.memory_type_index)
783 .or_default()
784 .push(block);
785 }
786 }
787 }
788
789 #[derive(Debug)]
791 struct VulkanBuffer<T> {
792 buffer: SafeVkHandle,
793 memory: SafeVkHandle,
794 device: SafeVkHandle,
795 size: usize,
796 is_host_visible: bool,
797 _phantom: std::marker::PhantomData<T>,
798 }
799
800 unsafe impl<T> Send for VulkanBuffer<T> {}
802 unsafe impl<T> Sync for VulkanBuffer<T> {}
803
804 impl<T: Clone + Send + Sync + Copy> VulkanBuffer<T> {
805 fn new(size: usize, device: SafeVkHandle) -> LinalgResult<Self> {
806 let byte_size = size * std::mem::size_of::<T>();
807
808 let (result, buffer) = vk_create_buffer(
810 device,
811 byte_size,
812 0x80 | 0x01, );
814
815 if result != VK_SUCCESS {
816 return Err(LinalgError::ComputationError(format!(
817 "Failed to create Vulkan buffer: error code {}",
818 result
819 )));
820 }
821
822 let (result, memory) = vk_allocate_memory(device, byte_size, 0);
824 if result != VK_SUCCESS {
825 return Err(LinalgError::ComputationError(format!(
826 "Failed to allocate Vulkan buffer memory: error code {}",
827 result
828 )));
829 }
830
831 let result = vk_bind_buffer_memory(device, buffer, memory, 0);
833 if result != VK_SUCCESS {
834 return Err(LinalgError::ComputationError(format!(
835 "Failed to bind Vulkan buffer memory: error code {}",
836 result
837 )));
838 }
839
840 Ok(Self {
841 buffer,
842 memory,
843 device,
844 size,
845 is_host_visible: true,
846 _phantom: std::marker::PhantomData,
847 })
848 }
849
850 pub fn vk_buffer(&self) -> SafeVkHandle {
852 self.buffer
853 }
854 }
855
856 impl<T: Clone + Send + Sync + Copy + std::fmt::Debug> GpuBuffer<T> for VulkanBuffer<T> {
857 fn len(&self) -> usize {
858 self.size
859 }
860
861 fn copy_from_host(&mut self, data: &[T]) -> LinalgResult<()> {
862 if data.len() != self.size {
863 return Err(LinalgError::ShapeError(format!(
864 "Buffer size mismatch: expected {}, got {}",
865 self.size,
866 data.len()
867 )));
868 }
869
870 if !self.is_host_visible {
871 return Err(LinalgError::ComputationError(
872 "Buffer is not host visible".to_string(),
873 ));
874 }
875
876 let byte_size = data.len() * std::mem::size_of::<T>();
877 let (result, mapped_ptr) = vk_map_memory(self.device, self.memory, 0, byte_size);
878
879 if result != VK_SUCCESS {
880 return Err(LinalgError::ComputationError(format!(
881 "Failed to map Vulkan memory: error code {}",
882 result
883 )));
884 }
885
886 if !mapped_ptr.is_null() {
887 unsafe {
889 std::ptr::copy_nonoverlapping(
890 data.as_ptr() as *const u8,
891 mapped_ptr as *mut u8,
892 byte_size,
893 );
894 }
895 }
896
897 vk_unmap_memory(self.device, self.memory);
898 Ok(())
899 }
900
901 fn copy_to_host(&self, data: &mut [T]) -> LinalgResult<()> {
902 if data.len() != self.size {
903 return Err(LinalgError::ShapeError(format!(
904 "Buffer size mismatch: expected {}, got {}",
905 self.size,
906 data.len()
907 )));
908 }
909
910 if !self.is_host_visible {
911 return Err(LinalgError::ComputationError(
912 "Buffer is not host visible".to_string(),
913 ));
914 }
915
916 let byte_size = data.len() * std::mem::size_of::<T>();
917 let (result, mapped_ptr) = vk_map_memory(self.device, self.memory, 0, byte_size);
918
919 if result != VK_SUCCESS {
920 return Err(LinalgError::ComputationError(format!(
921 "Failed to map Vulkan memory: error code {}",
922 result
923 )));
924 }
925
926 if !mapped_ptr.is_null() {
927 unsafe {
929 std::ptr::copy_nonoverlapping(
930 mapped_ptr as *const u8,
931 data.as_mut_ptr() as *mut u8,
932 byte_size,
933 );
934 }
935 }
936
937 vk_unmap_memory(self.device, self.memory);
938 Ok(())
939 }
940
941 fn device_ptr(&self) -> *mut std::ffi::c_void {
942 self.buffer.0
943 }
944 }
945
946 #[derive(Debug, Clone)]
948 pub struct VulkanPerformanceStats {
949 pub compute_dispatches: usize,
950 pub buffer_operations: usize,
951 pub total_compute_time_ms: f64,
952 pub total_transfer_time_ms: f64,
953 pub pipeline_cache_hits: usize,
954 pub pipeline_cache_misses: usize,
955 pub memory_allocated: usize,
956 pub memory_freed: usize,
957 }
958
959 impl VulkanPerformanceStats {
960 fn new() -> Self {
961 Self {
962 compute_dispatches: 0,
963 buffer_operations: 0,
964 total_compute_time_ms: 0.0,
965 total_transfer_time_ms: 0.0,
966 pipeline_cache_hits: 0,
967 pipeline_cache_misses: 0,
968 memory_allocated: 0,
969 memory_freed: 0,
970 }
971 }
972
973 pub fn compute_efficiency(&self) -> f64 {
974 if self.total_compute_time_ms + self.total_transfer_time_ms == 0.0 {
975 return 0.0;
976 }
977 self.total_compute_time_ms / (self.total_compute_time_ms + self.total_transfer_time_ms)
978 }
979
980 pub fn cache_hit_rate(&self) -> f64 {
981 let total = self.pipeline_cache_hits + self.pipeline_cache_misses;
982 if total == 0 {
983 return 0.0;
984 }
985 self.pipeline_cache_hits as f64 / total as f64
986 }
987 }
988}
989
990#[cfg(feature = "vulkan")]
992pub use vulkan_impl::*;
993
994#[cfg(not(feature = "vulkan"))]
996pub struct VulkanBackend;
997
998#[cfg(not(feature = "vulkan"))]
999impl VulkanBackend {
1000 pub fn new() -> LinalgResult<Self> {
1001 Err(LinalgError::ComputationError(
1002 "Vulkan support not compiled in".to_string(),
1003 ))
1004 }
1005}
1006
1007#[cfg(not(feature = "vulkan"))]
1008impl GpuBackend for VulkanBackend {
1009 fn name(&self) -> &str {
1010 "Vulkan (not available)"
1011 }
1012
1013 fn is_available(&self) -> bool {
1014 false
1015 }
1016
1017 fn list_devices(&self) -> LinalgResult<Vec<GpuDeviceInfo>> {
1018 Ok(vec![])
1019 }
1020
1021 fn create_context(&self, _device_id: usize) -> LinalgResult<Box<dyn GpuContext>> {
1022 Err(LinalgError::ComputationError(
1023 "Vulkan support not compiled in".to_string(),
1024 ))
1025 }
1026}
1027
1028#[cfg(test)]
1029mod tests {
1030 use super::*;
1031
1032 #[test]
1033 fn test_vulkan_backend_stub() {
1034 #[cfg(not(feature = "vulkan"))]
1035 {
1036 let backend = VulkanBackend;
1037 assert!(!backend.is_available());
1038 assert_eq!(backend.name(), "Vulkan (not available)");
1039 }
1040 }
1041}