⚠️ VeridianOS Kernel Documentation - This is low-level kernel code. All functions are unsafe unless explicitly marked otherwise. no_std

veridian_kernel/graphics/
gpu.rs

1//! GPU Acceleration Framework
2//!
3//! Provides GPU compute and rendering capabilities.
4//!
5//! ## Supported APIs
6//!
7//! - **Vulkan**: Modern cross-platform graphics API
8//! - **OpenGL ES**: Embedded graphics (compatibility)
9//! - **Compute**: GPU compute shaders for parallel processing
10
11// GPU acceleration framework -- Phase 7 VirtIO GPU integration
12#![allow(dead_code)]
13//! ## Architecture
14//!
15//! - Command buffers: Record rendering/compute commands
16//! - Memory management: GPU-visible memory allocation
17//! - Synchronization: Fences, semaphores for GPU/CPU sync
18//! - Queues: Graphics, compute, transfer queues
19
20use alloc::{string::String, vec::Vec};
21
22use spin::RwLock;
23
24use crate::{error::KernelError, sync::once_lock::GlobalState};
25
26/// GPU device
27#[derive(Clone)]
28pub struct GpuDevice {
29    /// Device name
30    pub name: String,
31    /// Vendor ID
32    pub vendor_id: u32,
33    /// Device ID
34    pub device_id: u32,
35    /// Memory size (bytes)
36    pub memory_size: u64,
37    /// Supported features
38    pub features: GpuFeatures,
39}
40
41/// GPU features
42#[derive(Debug, Clone, Copy)]
43pub struct GpuFeatures {
44    /// Supports Vulkan
45    pub vulkan: bool,
46    /// Supports OpenGL ES
47    pub opengl_es: bool,
48    /// Supports compute shaders
49    pub compute: bool,
50    /// Supports ray tracing
51    pub ray_tracing: bool,
52    /// Maximum texture size
53    pub max_texture_size: u32,
54}
55
56impl GpuDevice {
57    /// Detect GPU devices via PCI enumeration and VirtIO probe.
58    ///
59    /// Scans PCI class 0x03 (DISPLAY) for GPU devices and also checks for
60    /// an active VirtIO GPU driver. Returns all detected devices.
61    pub fn enumerate() -> Vec<GpuDevice> {
62        let mut devices = Vec::new();
63
64        // Check for VirtIO GPU first (most common in QEMU/KVM)
65        if crate::drivers::virtio_gpu::is_available() {
66            if let Some((width, height)) = crate::drivers::virtio_gpu::get_display_size() {
67                devices.push(GpuDevice {
68                    name: String::from("VirtIO GPU"),
69                    vendor_id: 0x1AF4,
70                    device_id: 0x1050,
71                    memory_size: (width as u64) * (height as u64) * 4, // framebuffer size
72                    features: GpuFeatures {
73                        vulkan: false,
74                        opengl_es: false,
75                        compute: false,
76                        ray_tracing: false,
77                        max_texture_size: width.max(height),
78                    },
79                });
80            }
81        }
82
83        // Enumerate PCI display-class devices
84        let pci_gpus = crate::drivers::virtio_gpu::enumerate_gpu_devices();
85        for (vendor_id, device_id, _class, _subclass) in pci_gpus {
86            // Skip VirtIO GPU (already added above)
87            if vendor_id == 0x1AF4 {
88                continue;
89            }
90
91            let name = match vendor_id {
92                0x10DE => String::from("NVIDIA GPU"),
93                0x1002 => String::from("AMD GPU"),
94                0x8086 => String::from("Intel GPU"),
95                _ => alloc::format!("GPU {:04x}:{:04x}", vendor_id, device_id),
96            };
97
98            devices.push(GpuDevice {
99                name,
100                vendor_id: vendor_id as u32,
101                device_id: device_id as u32,
102                memory_size: 256 * 1024 * 1024, // Default estimate
103                features: GpuFeatures {
104                    vulkan: false,
105                    opengl_es: false,
106                    compute: false,
107                    ray_tracing: false,
108                    max_texture_size: 4096,
109                },
110            });
111        }
112
113        // Always include a fallback virtual device if nothing found
114        if devices.is_empty() {
115            devices.push(GpuDevice {
116                name: String::from("Virtual GPU (Software)"),
117                vendor_id: 0x1234,
118                device_id: 0x5678,
119                memory_size: 256 * 1024 * 1024,
120                features: GpuFeatures {
121                    vulkan: true,
122                    opengl_es: true,
123                    compute: true,
124                    ray_tracing: false,
125                    max_texture_size: 4096,
126                },
127            });
128        }
129
130        devices
131    }
132}
133
134/// GPU memory allocation
135pub struct GpuMemory {
136    /// Physical address (GPU-visible)
137    pub physical_addr: u64,
138    /// Size in bytes
139    pub size: usize,
140    /// Memory type
141    pub memory_type: GpuMemoryType,
142}
143
144#[derive(Debug, Clone, Copy, PartialEq, Eq)]
145pub enum GpuMemoryType {
146    /// Device-local (fastest, not CPU-visible)
147    DeviceLocal,
148    /// Host-visible (CPU can write, slower for GPU)
149    HostVisible,
150    /// Host-cached (CPU can read efficiently)
151    HostCached,
152}
153
154/// GPU command buffer
155pub struct CommandBuffer {
156    /// Commands recorded
157    commands: Vec<GpuCommand>,
158}
159
160impl CommandBuffer {
161    pub fn new() -> Self {
162        Self {
163            commands: Vec::new(),
164        }
165    }
166
167    /// Record a draw command
168    pub fn draw(&mut self, vertex_count: u32, instance_count: u32) {
169        self.commands.push(GpuCommand::Draw {
170            vertex_count,
171            instance_count,
172        });
173    }
174
175    /// Record a compute dispatch
176    pub fn dispatch(&mut self, x: u32, y: u32, z: u32) {
177        self.commands.push(GpuCommand::Dispatch { x, y, z });
178    }
179
180    /// Record a memory barrier
181    pub fn barrier(&mut self) {
182        self.commands.push(GpuCommand::Barrier);
183    }
184
185    /// Submit command buffer to GPU.
186    ///
187    /// If a VirtIO GPU is available, flushes the framebuffer to the
188    /// display via transfer_to_host_2d + resource_flush. Otherwise
189    /// this is a no-op (software rendering path).
190    pub fn submit(&self) -> Result<(), KernelError> {
191        // Flush VirtIO GPU framebuffer if available
192        if crate::drivers::virtio_gpu::is_available() {
193            let _ = crate::drivers::virtio_gpu::flush_framebuffer();
194        }
195        Ok(())
196    }
197}
198
199impl Default for CommandBuffer {
200    fn default() -> Self {
201        Self::new()
202    }
203}
204
205/// GPU command
206#[derive(Debug, Clone)]
207enum GpuCommand {
208    Draw {
209        vertex_count: u32,
210        instance_count: u32,
211    },
212    Dispatch {
213        x: u32,
214        y: u32,
215        z: u32,
216    },
217    Barrier,
218}
219
220/// Vulkan support layer
221pub mod vulkan {
222    use super::*;
223
224    /// Vulkan instance
225    pub struct VulkanInstance {
226        /// Enabled layers
227        pub layers: Vec<String>,
228        /// Enabled extensions
229        pub extensions: Vec<String>,
230    }
231
232    impl VulkanInstance {
233        pub fn new() -> Self {
234            Self {
235                layers: Vec::new(),
236                extensions: Vec::new(),
237            }
238        }
239
240        pub fn enumerate_physical_devices(&self) -> Vec<GpuDevice> {
241            GpuDevice::enumerate()
242        }
243    }
244
245    impl Default for VulkanInstance {
246        fn default() -> Self {
247            Self::new()
248        }
249    }
250
251    /// Vulkan logical device
252    pub struct VulkanDevice {
253        /// Physical device
254        pub physical_device: GpuDevice,
255        /// Command queues
256        pub queues: Vec<CommandQueue>,
257    }
258
259    /// Command queue
260    pub struct CommandQueue {
261        /// Queue family index
262        pub family_index: u32,
263        /// Queue type
264        pub queue_type: QueueType,
265    }
266
267    #[derive(Debug, Clone, Copy, PartialEq, Eq)]
268    pub enum QueueType {
269        Graphics,
270        Compute,
271        Transfer,
272    }
273}
274
275/// OpenGL ES support layer
276pub mod opengl_es {
277    use super::*;
278
279    /// OpenGL ES context
280    pub struct GlContext {
281        /// Version (3.0, 3.1, 3.2)
282        pub version: (u32, u32),
283    }
284
285    impl GlContext {
286        pub fn new(version: (u32, u32)) -> Self {
287            Self { version }
288        }
289
290        /// Make context current.
291        ///
292        /// Binds the OpenGL ES context to the current thread.
293        /// With VirtIO GPU, this is a no-op (single context).
294        pub fn make_current(&self) -> Result<(), KernelError> {
295            // VirtIO GPU uses a single implicit context
296            Ok(())
297        }
298
299        /// Swap buffers.
300        ///
301        /// Presents the current framebuffer by flushing the VirtIO GPU
302        /// scanout if available, otherwise no-op (software rendering).
303        pub fn swap_buffers(&self) -> Result<(), KernelError> {
304            if crate::drivers::virtio_gpu::is_available() {
305                let _ = crate::drivers::virtio_gpu::flush_framebuffer();
306            }
307            Ok(())
308        }
309    }
310}
311
312/// GPU manager
313pub struct GpuManager {
314    /// Available devices
315    devices: RwLock<Vec<GpuDevice>>,
316}
317
318impl GpuManager {
319    pub fn new() -> Self {
320        Self {
321            devices: RwLock::new(Vec::new()),
322        }
323    }
324
325    /// Initialize GPU subsystem
326    pub fn init(&self) -> Result<(), KernelError> {
327        let devices = GpuDevice::enumerate();
328        let count = devices.len();
329        for dev in &devices {
330            crate::println!(
331                "[GPU] Found device: {} (vendor={:#06x} device={:#06x} mem={}KB)",
332                dev.name,
333                dev.vendor_id,
334                dev.device_id,
335                dev.memory_size / 1024
336            );
337        }
338        *self.devices.write() = devices;
339        crate::println!("[GPU] {} GPU device(s) enumerated", count);
340        Ok(())
341    }
342
343    /// Get available devices
344    pub fn devices(&self) -> Vec<GpuDevice> {
345        self.devices.read().clone()
346    }
347
348    /// Flush the primary GPU framebuffer to the display.
349    ///
350    /// If a VirtIO GPU is available, this triggers a transfer_to_host_2d
351    /// followed by resource_flush to present the framebuffer contents.
352    pub fn flush_framebuffer(&self) {
353        if crate::drivers::virtio_gpu::is_available() {
354            let _ = crate::drivers::virtio_gpu::flush_framebuffer();
355        }
356    }
357}
358
359impl Default for GpuManager {
360    fn default() -> Self {
361        Self::new()
362    }
363}
364
365/// Global GPU manager
366static GPU_MANAGER: GlobalState<GpuManager> = GlobalState::new();
367
368/// Initialize GPU subsystem
369pub fn init() -> Result<(), KernelError> {
370    let manager = GpuManager::new();
371    manager.init()?;
372
373    GPU_MANAGER
374        .init(manager)
375        .map_err(|_| KernelError::InvalidState {
376            expected: "uninitialized",
377            actual: "initialized",
378        })?;
379
380    crate::println!("[GPU] GPU acceleration initialized");
381    Ok(())
382}
383
384/// Execute a function with the GPU manager
385pub fn with_gpu_manager<R, F: FnOnce(&GpuManager) -> R>(f: F) -> Option<R> {
386    GPU_MANAGER.with(f)
387}
388
389#[cfg(test)]
390mod tests {
391    use super::*;
392
393    #[test]
394    fn test_gpu_enumeration() {
395        let devices = GpuDevice::enumerate();
396        assert!(!devices.is_empty());
397    }
398
399    #[test]
400    fn test_command_buffer() {
401        let mut cb = CommandBuffer::new();
402        cb.draw(3, 1);
403        cb.dispatch(64, 1, 1);
404        assert_eq!(cb.commands.len(), 2);
405    }
406
407    #[test]
408    fn test_vulkan_instance() {
409        let instance = vulkan::VulkanInstance::new();
410        let devices = instance.enumerate_physical_devices();
411        assert!(!devices.is_empty());
412    }
413}