kernel/drivers/graphics/
virtio_gpu.rs

1//! # VirtIO GPU Device Driver
2//!
3//! This module provides a driver for VirtIO GPU devices, implementing the
4//! GraphicsDevice trait for integration with the kernel's graphics subsystem.
5//!
6//! The driver supports basic framebuffer operations and display management
7//! according to the VirtIO GPU specification.
8
9use alloc::{boxed::Box, sync::Arc};
10use spin::{Mutex, RwLock};
11
12use crate::{
13    device::{
14        Device, DeviceType,
15        graphics::{FramebufferConfig, GraphicsDevice, PixelFormat},
16    },
17    drivers::virtio::{
18        device::VirtioDevice,
19        queue::{DescriptorFlag, VirtQueue},
20    },
21    mem::page::{Page, allocate_raw_pages},
22    object::capability::{ControlOps, MemoryMappingOps, Selectable},
23    timer::{TimerHandler, add_timer, get_tick, ms_to_ticks},
24};
25use core::ptr;
26
27// VirtIO GPU Constants
28const VIRTIO_GPU_F_VIRGL: u32 = 0;
29const VIRTIO_GPU_F_EDID: u32 = 1;
30
31// VirtIO GPU Control Commands
32const VIRTIO_GPU_CMD_GET_DISPLAY_INFO: u32 = 0x0100;
33const VIRTIO_GPU_CMD_RESOURCE_CREATE_2D: u32 = 0x0101;
34const VIRTIO_GPU_CMD_RESOURCE_UNREF: u32 = 0x0102;
35const VIRTIO_GPU_CMD_SET_SCANOUT: u32 = 0x0103;
36const VIRTIO_GPU_CMD_RESOURCE_FLUSH: u32 = 0x0104;
37const VIRTIO_GPU_CMD_TRANSFER_TO_HOST_2D: u32 = 0x0105;
38const VIRTIO_GPU_CMD_RESOURCE_ATTACH_BACKING: u32 = 0x0106;
39const VIRTIO_GPU_CMD_RESOURCE_DETACH_BACKING: u32 = 0x0107;
40
41// VirtIO GPU Response Types
42const VIRTIO_GPU_RESP_OK_NODATA: u32 = 0x1100;
43const VIRTIO_GPU_RESP_OK_DISPLAY_INFO: u32 = 0x1101;
44
45// VirtIO GPU Formats
46const VIRTIO_GPU_FORMAT_B8G8R8A8_UNORM: u32 = 1;
47const VIRTIO_GPU_FORMAT_B8G8R8X8_UNORM: u32 = 2;
48const VIRTIO_GPU_FORMAT_A8R8G8B8_UNORM: u32 = 3;
49const VIRTIO_GPU_FORMAT_X8R8G8B8_UNORM: u32 = 4;
50const VIRTIO_GPU_FORMAT_R8G8B8A8_UNORM: u32 = 67;
51const VIRTIO_GPU_FORMAT_X8B8G8R8_UNORM: u32 = 68;
52const VIRTIO_GPU_FORMAT_A8B8G8R8_UNORM: u32 = 121;
53const VIRTIO_GPU_FORMAT_R8G8B8X8_UNORM: u32 = 134;
54
55// Maximum number of scanouts
56const VIRTIO_GPU_MAX_SCANOUTS: usize = 16;
57
58/// VirtIO GPU command header
59#[repr(C)]
60struct VirtioGpuCtrlHdr {
61    hdr_type: u32,
62    flags: u32,
63    fence_id: u64,
64    ctx_id: u32,
65    padding: u32,
66}
67
68/// VirtIO GPU rectangle
69#[repr(C)]
70#[derive(Clone, Copy)]
71struct VirtioGpuRect {
72    x: u32,
73    y: u32,
74    width: u32,
75    height: u32,
76}
77
78/// VirtIO GPU display info
79#[repr(C)]
80struct VirtioGpuRespDisplayInfo {
81    hdr: VirtioGpuCtrlHdr,
82    pmodes: [VirtioGpuDisplayOne; VIRTIO_GPU_MAX_SCANOUTS],
83}
84
85/// VirtIO GPU display mode
86#[repr(C)]
87#[derive(Clone, Copy)]
88struct VirtioGpuDisplayOne {
89    r: VirtioGpuRect,
90    enabled: u32,
91    flags: u32,
92}
93
94/// VirtIO GPU resource create 2D
95#[repr(C)]
96struct VirtioGpuResourceCreate2d {
97    hdr: VirtioGpuCtrlHdr,
98    resource_id: u32,
99    format: u32,
100    width: u32,
101    height: u32,
102}
103
104/// VirtIO GPU set scanout
105#[repr(C)]
106struct VirtioGpuSetScanout {
107    hdr: VirtioGpuCtrlHdr,
108    r: VirtioGpuRect,
109    scanout_id: u32,
110    resource_id: u32,
111}
112
113/// VirtIO GPU resource flush
114#[repr(C)]
115struct VirtioGpuResourceFlush {
116    hdr: VirtioGpuCtrlHdr,
117    r: VirtioGpuRect,
118    resource_id: u32,
119    padding: u32,
120}
121
122/// VirtIO GPU transfer to host 2D
123#[repr(C)]
124struct VirtioGpuTransferToHost2d {
125    hdr: VirtioGpuCtrlHdr,
126    r: VirtioGpuRect,
127    offset: u64,
128    resource_id: u32,
129    padding: u32,
130}
131
132/// VirtIO GPU resource attach backing
133#[repr(C)]
134struct VirtioGpuResourceAttachBacking {
135    hdr: VirtioGpuCtrlHdr,
136    resource_id: u32,
137    nr_entries: u32,
138}
139
140/// VirtIO GPU memory entry
141#[repr(C)]
142struct VirtioGpuMemEntry {
143    addr: u64,
144    length: u32,
145    padding: u32,
146}
147
148/// VirtIO GPU Device Core
149pub struct VirtioGpuDeviceCore {
150    base_addr: usize,
151    virtqueues: Mutex<[VirtQueue<'static>; 2]>, // Control queue (0) and Cursor queue (1)
152    display_info: RwLock<Option<VirtioGpuRespDisplayInfo>>,
153    framebuffer_addr: RwLock<Option<usize>>,
154    shadow_framebuffer_addr: RwLock<Option<usize>>,
155    boxed_framebuffer: RwLock<Option<Box<[Page]>>>, // Boxed framebuffer for easier management
156    boxed_shadow_framebuffer: RwLock<Option<Box<[Page]>>>, // Boxed shadow framebuffer
157    resource_id: Mutex<u32>,
158    initialized: Mutex<bool>,
159    // Track resources and their associated memory
160    resources: Mutex<alloc::collections::BTreeMap<u32, (usize, usize)>>, // resource_id -> (addr, size)
161}
162
163impl VirtioGpuDeviceCore {
164    /// Create a new VirtIO GPU device
165    ///
166    /// # Arguments
167    ///
168    /// * `base_addr` - The base address of the device
169    ///
170    /// # Returns
171    ///
172    /// A new instance of `VirtioGpuDevice`
173    pub fn new(base_addr: usize) -> Self {
174        let mut device = Self {
175            base_addr,
176            virtqueues: Mutex::new([VirtQueue::new(64), VirtQueue::new(64)]), // Control and Cursor queues with 64 descriptors each
177            display_info: RwLock::new(None),
178            framebuffer_addr: RwLock::new(None),
179            shadow_framebuffer_addr: RwLock::new(None),
180            boxed_framebuffer: RwLock::new(None),
181            boxed_shadow_framebuffer: RwLock::new(None),
182            resource_id: Mutex::new(1),
183            initialized: Mutex::new(false),
184            resources: Mutex::new(alloc::collections::BTreeMap::new()),
185        };
186
187        // Initialize virtqueues first
188        {
189            let mut virtqueues = device.virtqueues.lock();
190            for queue in virtqueues.iter_mut() {
191                queue.init();
192            }
193        }
194
195        // Initialize the VirtIO device - this will set up the queues with the device
196        if device.init().is_err() {
197            crate::early_println!("[Virtio GPU] Warning: Failed to initialize VirtIO device");
198        }
199
200        // crate::early_println!("[Virtio GPU] Device created and initialized at {:#x}", base_addr);
201        device
202    }
203
204    /// Get next resource ID
205    fn next_resource_id(&self) -> u32 {
206        let mut id = self.resource_id.lock();
207        let current = *id;
208        *id += 1;
209        current
210    }
211
212    /// Send a command to the control queue
213    fn send_control_command<T>(&self, cmd: &T) -> Result<(), &'static str> {
214        let mut resp_buffer = [0u8; 128];
215        self.send_control_command_with_resp_buffer(cmd, &mut resp_buffer)
216    }
217
218    /// Send a command to the control queue, using a caller-provided response buffer.
219    fn send_control_command_with_resp_buffer<T>(
220        &self,
221        cmd: &T,
222        resp_buffer: &mut [u8],
223    ) -> Result<(), &'static str> {
224        let mut virtqueues = self.virtqueues.lock();
225        let control_queue = &mut virtqueues[0]; // Control queue is index 0
226
227        // Allocate descriptors
228        let cmd_desc = control_queue
229            .alloc_desc()
230            .ok_or("Failed to allocate command descriptor")?;
231        let resp_desc = match control_queue.alloc_desc() {
232            Some(desc) => desc,
233            None => {
234                // Free the already allocated cmd_desc before returning error
235                control_queue.free_desc(cmd_desc);
236                return Err("Failed to allocate response descriptor");
237            }
238        };
239
240        // Set up command descriptor (device readable)
241        let cmd_desc_ptr =
242            &mut control_queue.desc[cmd_desc] as *mut crate::drivers::virtio::queue::Descriptor;
243        let cmd_virt_addr = cmd as *const T as usize;
244        let cmd_phys_addr = crate::vm::get_kernel_vm_manager()
245            .translate_vaddr(cmd_virt_addr)
246            .ok_or("Failed to translate cmd vaddr to paddr")?;
247        unsafe {
248            core::ptr::write_volatile(&mut (*cmd_desc_ptr).addr, cmd_phys_addr as u64);
249            core::ptr::write_volatile(&mut (*cmd_desc_ptr).len, core::mem::size_of::<T>() as u32);
250            core::ptr::write_volatile(&mut (*cmd_desc_ptr).flags, DescriptorFlag::Next as u16);
251            core::ptr::write_volatile(&mut (*cmd_desc_ptr).next, resp_desc as u16);
252        }
253
254        // Set up response descriptor (device writable)
255        let resp_desc_ptr =
256            &mut control_queue.desc[resp_desc] as *mut crate::drivers::virtio::queue::Descriptor;
257        let resp_virt_addr = resp_buffer.as_mut_ptr() as usize;
258        let resp_phys_addr = crate::vm::get_kernel_vm_manager()
259            .translate_vaddr(resp_virt_addr)
260            .ok_or("Failed to translate resp_buffer vaddr to paddr")?;
261        unsafe {
262            core::ptr::write_volatile(&mut (*resp_desc_ptr).addr, resp_phys_addr as u64);
263            core::ptr::write_volatile(&mut (*resp_desc_ptr).len, resp_buffer.len() as u32); // Use .len() for safety
264            core::ptr::write_volatile(&mut (*resp_desc_ptr).flags, DescriptorFlag::Write as u16);
265        }
266
267        // crate::early_println!("[Virtio GPU] Sending command to control queue: type={}",
268        //     unsafe { *(cmd as *const T as *const u32) });
269
270        // Submit the request to the queue
271        if let Err(e) = control_queue.push(cmd_desc) {
272            // Free descriptors if push fails
273            control_queue.free_desc(resp_desc);
274            control_queue.free_desc(cmd_desc);
275            return Err(e);
276        }
277
278        // Notify the device
279        self.notify(0); // Notify control queue
280
281        // Wait for response (simplified polling)
282        // crate::early_println!("[Virtio GPU] Waiting for command response...");
283        while control_queue.is_busy() {}
284        while *control_queue.used.idx == control_queue.last_used_idx {}
285
286        // Process response
287        let _resp_idx = match control_queue.pop() {
288            Some(idx) => idx,
289            None => {
290                // Free descriptors even if pop fails (device may have processed them)
291                control_queue.free_desc(resp_desc);
292                control_queue.free_desc(cmd_desc);
293                return Err("No response from device");
294            }
295        };
296
297        // Free descriptors (responsibility of driver, not VirtQueue)
298        control_queue.free_desc(resp_desc);
299        control_queue.free_desc(cmd_desc);
300
301        // crate::early_println!("[Virtio GPU] Command completed successfully");
302
303        Ok(())
304    }
305
306    /// Get display information from the device
307    fn get_display_info_internal(&mut self) -> Result<(), &'static str> {
308        // Create get display info command
309        let cmd = VirtioGpuCtrlHdr {
310            hdr_type: VIRTIO_GPU_CMD_GET_DISPLAY_INFO,
311            flags: 0,
312            fence_id: 0,
313            ctx_id: 0,
314            padding: 0,
315        };
316
317        // Send command.
318        // GET_DISPLAY_INFO returns a `virtio_gpu_resp_display_info`, which is 408 bytes
319        // (header + 16 scanouts). If we provide a smaller response buffer, QEMU logs:
320        // `virtio_gpu_ctrl_response: response size incorrect 128 vs 408`.
321        // Avoid allocating this response buffer on the stack; use the heap instead.
322        let mut resp_buffer = alloc::vec![0u8; core::mem::size_of::<VirtioGpuRespDisplayInfo>()];
323        self.send_control_command_with_resp_buffer(&cmd, &mut resp_buffer)?;
324
325        // For now, create a default display configuration
326        let mut display_info = VirtioGpuRespDisplayInfo {
327            hdr: VirtioGpuCtrlHdr {
328                hdr_type: VIRTIO_GPU_RESP_OK_DISPLAY_INFO,
329                flags: 0,
330                fence_id: 0,
331                ctx_id: 0,
332                padding: 0,
333            },
334            pmodes: [VirtioGpuDisplayOne {
335                r: VirtioGpuRect {
336                    x: 0,
337                    y: 0,
338                    width: 1024,
339                    height: 768,
340                },
341                enabled: 1,
342                flags: 0,
343            }; VIRTIO_GPU_MAX_SCANOUTS],
344        };
345
346        // Only enable the first display
347        for i in 1..VIRTIO_GPU_MAX_SCANOUTS {
348            display_info.pmodes[i].enabled = 0;
349        }
350
351        *self.display_info.write() = Some(display_info);
352        Ok(())
353    }
354
355    /// Create a 2D resource
356    fn create_2d_resource(
357        &self,
358        width: u32,
359        height: u32,
360        format: u32,
361    ) -> Result<u32, &'static str> {
362        let resource_id = self.next_resource_id();
363
364        let cmd = VirtioGpuResourceCreate2d {
365            hdr: VirtioGpuCtrlHdr {
366                hdr_type: VIRTIO_GPU_CMD_RESOURCE_CREATE_2D,
367                flags: 0,
368                fence_id: 0,
369                ctx_id: 0,
370                padding: 0,
371            },
372            resource_id,
373            format,
374            width,
375            height,
376        };
377
378        self.send_control_command(&cmd)?;
379        Ok(resource_id)
380    }
381
382    /// Attach backing memory to a resource
383    fn attach_backing_to_resource(
384        &self,
385        resource_id: u32,
386        addr: usize,
387        size: usize,
388    ) -> Result<(), &'static str> {
389        // Create attach backing command + memory entry in a single buffer
390        #[repr(C)]
391        struct AttachBackingWithEntry {
392            attach: VirtioGpuResourceAttachBacking,
393            entry: VirtioGpuMemEntry,
394        }
395
396        let cmd = AttachBackingWithEntry {
397            attach: VirtioGpuResourceAttachBacking {
398                hdr: VirtioGpuCtrlHdr {
399                    hdr_type: VIRTIO_GPU_CMD_RESOURCE_ATTACH_BACKING,
400                    flags: 0,
401                    fence_id: 0,
402                    ctx_id: 0,
403                    padding: 0,
404                },
405                resource_id,
406                nr_entries: 1,
407            },
408            entry: VirtioGpuMemEntry {
409                addr: addr as u64,
410                length: size as u32,
411                padding: 0,
412            },
413        };
414
415        // crate::early_println!("[Virtio GPU] Attaching framebuffer memory {:#x} (size {}) to resource {}",
416        //     addr, size, resource_id);
417        self.send_control_command(&cmd)?;
418        Ok(())
419    }
420
421    /// Set up framebuffer
422    fn setup_framebuffer(&self) -> Result<(), &'static str> {
423        let display_info = self.display_info.read();
424        let display_info = display_info.as_ref().ok_or("No display info available")?;
425        let primary_display = &display_info.pmodes[0];
426        if primary_display.enabled == 0 {
427            return Err("Primary display not enabled");
428        }
429        let width = primary_display.r.width;
430        let height = primary_display.r.height;
431        let resource_id =
432            self.create_2d_resource(width, height, VIRTIO_GPU_FORMAT_B8G8R8X8_UNORM)?;
433        let fb_size = (width * height * 4) as usize;
434        let fb_pages = (fb_size + 4095) / 4096;
435        let fb_pages_ptr = allocate_raw_pages(fb_pages);
436        if fb_pages_ptr.is_null() {
437            return Err("Failed to allocate framebuffer memory");
438        }
439        let fb_addr = fb_pages_ptr as usize;
440        // Store the framebuffer in boxed memory for easier management
441        self.boxed_framebuffer.write().replace(unsafe {
442            Box::from_raw(core::ptr::slice_from_raw_parts_mut(fb_pages_ptr, fb_pages))
443        });
444        self.attach_backing_to_resource(resource_id, fb_addr, fb_size)?; // Attach backing memory to the resource
445        // Set scanout to use this framebuffer
446        let scanout_cmd = VirtioGpuSetScanout {
447            hdr: VirtioGpuCtrlHdr {
448                hdr_type: VIRTIO_GPU_CMD_SET_SCANOUT,
449                flags: 0,
450                fence_id: 0,
451                ctx_id: 0,
452                padding: 0,
453            },
454            r: VirtioGpuRect {
455                x: 0,
456                y: 0,
457                width,
458                height,
459            },
460            scanout_id: 0,
461            resource_id,
462        };
463        self.send_control_command(&scanout_cmd)?;
464        {
465            let mut resources = self.resources.lock();
466            resources.insert(resource_id, (fb_addr, fb_size));
467        }
468        *self.framebuffer_addr.write() = Some(fb_addr);
469        // Allocate shadow framebuffer
470        let shadow_pages_ptr = allocate_raw_pages(fb_pages);
471        if shadow_pages_ptr.is_null() {
472            return Err("Failed to allocate shadow framebuffer memory");
473        }
474        let shadow_addr = shadow_pages_ptr as usize;
475        // Store the shadow framebuffer in boxed memory for easier management
476        self.boxed_shadow_framebuffer.write().replace(unsafe {
477            Box::from_raw(core::ptr::slice_from_raw_parts_mut(
478                shadow_pages_ptr,
479                fb_pages,
480            ))
481        });
482        // Initialize shadow framebuffer with the contents of the framebuffer
483        let fb_size = fb_size as usize;
484        unsafe {
485            ptr::copy_nonoverlapping(fb_addr as *const u8, shadow_addr as *mut u8, fb_size);
486        }
487        *self.shadow_framebuffer_addr.write() = Some(shadow_addr);
488        Ok(())
489    }
490
491    fn get_display_name(&self) -> &'static str {
492        "virtio-gpu"
493    }
494
495    fn get_framebuffer_config(&self) -> Result<FramebufferConfig, &'static str> {
496        let display_info = self.display_info.read();
497        let display_info = display_info.as_ref().ok_or("Device not initialized")?;
498
499        let primary_display = &display_info.pmodes[0];
500        if primary_display.enabled == 0 {
501            return Err("Primary display not enabled");
502        }
503
504        Ok(FramebufferConfig::new(
505            primary_display.r.width,
506            primary_display.r.height,
507            PixelFormat::BGRA8888, // VirtIO GPU typically uses BGRA format
508        ))
509    }
510
511    fn get_framebuffer_address(&self) -> Result<usize, &'static str> {
512        self.framebuffer_addr
513            .read()
514            .ok_or("Framebuffer not initialized")
515    }
516
517    fn flush_framebuffer(
518        &self,
519        x: u32,
520        y: u32,
521        width: u32,
522        height: u32,
523    ) -> Result<(), &'static str> {
524        let display_info = self.display_info.read();
525        let _display_info = display_info.as_ref().ok_or("Device not initialized")?;
526
527        // Get the resource ID from our tracked resources
528        let resource_id = {
529            let resources = self.resources.lock();
530            if let Some((_, _)) = resources.get(&1) {
531                1 // Use primary framebuffer resource
532            } else {
533                return Err("No framebuffer resource found");
534            }
535        };
536
537        // crate::early_println!("[Virtio GPU] Flushing framebuffer region: ({},{}) {}x{} for resource {}",
538        //     x, y, width, height, resource_id);
539
540        // Transfer to host - copies data from guest memory to host
541        // This is necessary because the host GPU driver needs to know
542        // that the framebuffer contents have changed
543        let transfer_cmd = VirtioGpuTransferToHost2d {
544            hdr: VirtioGpuCtrlHdr {
545                hdr_type: VIRTIO_GPU_CMD_TRANSFER_TO_HOST_2D,
546                flags: 0,
547                fence_id: 0,
548                ctx_id: 0,
549                padding: 0,
550            },
551            r: VirtioGpuRect {
552                x,
553                y,
554                width,
555                height,
556            },
557            offset: 0,
558            resource_id,
559            padding: 0,
560        };
561
562        self.send_control_command(&transfer_cmd)?;
563
564        // Flush resource - tells the display to update the specified region
565        // This actually triggers the display update
566        let flush_cmd = VirtioGpuResourceFlush {
567            hdr: VirtioGpuCtrlHdr {
568                hdr_type: VIRTIO_GPU_CMD_RESOURCE_FLUSH,
569                flags: 0,
570                fence_id: 0,
571                ctx_id: 0,
572                padding: 0,
573            },
574            r: VirtioGpuRect {
575                x,
576                y,
577                width,
578                height,
579            },
580            resource_id,
581            padding: 0,
582        };
583
584        self.send_control_command(&flush_cmd)?;
585        // crate::early_println!("[Virtio GPU] Framebuffer flush completed");
586        Ok(())
587    }
588}
589
590impl VirtioDevice for VirtioGpuDeviceCore {
591    fn get_base_addr(&self) -> usize {
592        self.base_addr
593    }
594
595    fn get_virtqueue_count(&self) -> usize {
596        2 // Control queue and cursor queue
597    }
598
599    fn get_virtqueue_size(&self, queue_idx: usize) -> usize {
600        if queue_idx >= self.get_virtqueue_count() {
601            panic!("Invalid queue index: {}", queue_idx);
602        }
603
604        let virtqueues = self.virtqueues.lock();
605        virtqueues[queue_idx].get_queue_size()
606    }
607
608    fn get_queue_desc_addr(&self, queue_idx: usize) -> Option<u64> {
609        if queue_idx >= self.get_virtqueue_count() {
610            return None;
611        }
612
613        let virtqueues = self.virtqueues.lock();
614        Some(virtqueues[queue_idx].desc.as_ptr() as u64)
615    }
616
617    fn get_queue_driver_addr(&self, queue_idx: usize) -> Option<u64> {
618        if queue_idx >= self.get_virtqueue_count() {
619            return None;
620        }
621
622        let virtqueues = self.virtqueues.lock();
623        Some(virtqueues[queue_idx].avail.flags as *const u16 as u64)
624    }
625
626    fn get_queue_device_addr(&self, queue_idx: usize) -> Option<u64> {
627        if queue_idx >= self.get_virtqueue_count() {
628            return None;
629        }
630
631        let virtqueues = self.virtqueues.lock();
632        Some(virtqueues[queue_idx].used.flags as *const u16 as u64)
633    }
634
635    fn get_supported_features(&self, _device_features: u32) -> u32 {
636        // For now, don't enable any advanced features
637        0
638    }
639}
640
641pub struct VirtioGpuDevice {
642    core: Arc<Mutex<VirtioGpuDeviceCore>>,
643    handler: RwLock<Option<Arc<dyn TimerHandler>>>,
644}
645
646impl VirtioGpuDevice {
647    /// Create a new VirtIO GPU device
648    ///
649    /// # Arguments
650    ///
651    /// * `base_addr` - The base address of the device
652    ///
653    /// # Returns
654    ///
655    /// A new instance of `VirtioGpuDevice`
656    pub fn new(base_addr: usize) -> Self {
657        Self {
658            core: Arc::new(Mutex::new(VirtioGpuDeviceCore::new(base_addr))),
659            handler: RwLock::new(None),
660        }
661    }
662}
663
664impl Device for VirtioGpuDevice {
665    fn device_type(&self) -> DeviceType {
666        DeviceType::Graphics
667    }
668
669    fn name(&self) -> &'static str {
670        "virtio-gpu"
671    }
672
673    fn as_any(&self) -> &dyn core::any::Any {
674        self
675    }
676
677    fn as_any_mut(&mut self) -> &mut dyn core::any::Any {
678        self
679    }
680
681    fn as_graphics_device(&self) -> Option<&dyn GraphicsDevice> {
682        Some(self)
683    }
684}
685
686impl ControlOps for VirtioGpuDevice {
687    // VirtIO GPU devices don't support control operations by default
688    fn control(&self, _command: u32, _arg: usize) -> Result<i32, &'static str> {
689        Err("Control operations not supported")
690    }
691}
692
693impl MemoryMappingOps for VirtioGpuDevice {
694    fn get_mapping_info(
695        &self,
696        _offset: usize,
697        _length: usize,
698    ) -> Result<(usize, usize, bool), &'static str> {
699        Err("Memory mapping not supported by VirtIO GPU device")
700    }
701
702    fn on_mapped(&self, _vaddr: usize, _paddr: usize, _length: usize, _offset: usize) {
703        // VirtIO GPU devices don't support memory mapping
704    }
705
706    fn on_unmapped(&self, _vaddr: usize, _length: usize) {
707        // VirtIO GPU devices don't support memory mapping
708    }
709
710    fn supports_mmap(&self) -> bool {
711        false
712    }
713}
714
715impl Selectable for VirtioGpuDevice {
716    fn wait_until_ready(
717        &self,
718        _interest: crate::object::capability::selectable::ReadyInterest,
719        _trapframe: &mut crate::arch::Trapframe,
720        _timeout_ticks: Option<u64>,
721    ) -> crate::object::capability::selectable::SelectWaitOutcome {
722        crate::object::capability::selectable::SelectWaitOutcome::Ready
723    }
724}
725
726impl GraphicsDevice for VirtioGpuDevice {
727    fn get_display_name(&self) -> &'static str {
728        "virtio-gpu"
729    }
730
731    fn get_framebuffer_config(&self) -> Result<FramebufferConfig, &'static str> {
732        self.core.lock().get_framebuffer_config()
733    }
734
735    fn get_framebuffer_address(&self) -> Result<usize, &'static str> {
736        self.core.lock().get_framebuffer_address()
737    }
738
739    fn flush_framebuffer(
740        &self,
741        x: u32,
742        y: u32,
743        width: u32,
744        height: u32,
745    ) -> Result<(), &'static str> {
746        self.core.lock().flush_framebuffer(x, y, width, height)
747    }
748
749    fn init_graphics(&self) -> Result<(), &'static str> {
750        {
751            let core = self.core.lock();
752            let mut initialized = core.initialized.lock();
753            if *initialized {
754                return Ok(());
755            }
756            *initialized = true;
757        }
758
759        // crate::early_println!("[Virtio GPU] Initializing graphics subsystem for device at {:#x}", self.base_addr);
760
761        // Get display information
762        self.core.lock().get_display_info_internal()?;
763
764        // Set up framebuffer
765        self.core.lock().setup_framebuffer()?;
766
767        let handler: Arc<dyn TimerHandler> = Arc::new(FramebufferUpdateHandler {
768            device: self.core.clone(),
769        });
770
771        add_timer(get_tick() + ms_to_ticks(16), &handler, 0);
772
773        // Store handler via interior mutability
774        *self.handler.write() = Some(handler);
775
776        // crate::early_println!("[Virtio GPU] Graphics subsystem initialization completed");
777        Ok(())
778    }
779}
780
781struct FramebufferUpdateHandler {
782    device: Arc<Mutex<VirtioGpuDeviceCore>>,
783}
784
785impl FramebufferUpdateHandler {
786    fn compare_and_flush(&self) {
787        let (_fb_addr, _shadow_addr, width, height, fb_size) = {
788            let core = self.device.lock();
789            let fb_addr = match *core.framebuffer_addr.read() {
790                Some(addr) => addr,
791                None => return,
792            };
793            let shadow_addr = match *core.shadow_framebuffer_addr.read() {
794                Some(addr) => addr,
795                None => return,
796            };
797            let display_info_guard = core.display_info.read();
798            let display_info = match display_info_guard.as_ref() {
799                Some(info) => info,
800                None => return,
801            };
802            let width = display_info.pmodes[0].r.width;
803            let height = display_info.pmodes[0].r.height;
804            let fb_size = (width * height * 4) as usize;
805            (fb_addr, shadow_addr, width, height, fb_size)
806        };
807        let _ = self.device.lock().flush_framebuffer(0, 0, width, height);
808
809        // // Determine if the framebuffer has changed
810        // let fb_ptr = fb_addr as *const u8;
811        // let shadow_ptr = shadow_addr as *const u8;
812        // let fb_slice = unsafe { core::slice::from_raw_parts(fb_ptr, fb_size) };
813        // let shadow_slice = unsafe { core::slice::from_raw_parts(shadow_ptr, fb_size) };
814        // let changed = fb_slice != shadow_slice;
815
816        // if changed {
817        //     let _ = self.device.lock().flush_framebuffer(0, 0, width, height);
818        //     unsafe {
819        //         ptr::copy_nonoverlapping(fb_addr as *const u8, shadow_addr as *mut u8, fb_size);
820        //     }
821        // }
822    }
823}
824
825impl TimerHandler for FramebufferUpdateHandler {
826    fn on_timer_expired(self: Arc<Self>, context: usize) {
827        self.compare_and_flush();
828        let handler = self as Arc<dyn TimerHandler>;
829        add_timer(get_tick() + ms_to_ticks(16), &handler, context);
830    }
831}
832
833#[cfg(test)]
834mod tests {
835    use super::*;
836
837    #[test_case]
838    fn test_virtio_gpu_device_creation() {
839        let device = VirtioGpuDevice::new(0x10002000);
840        assert_eq!(device.core.lock().get_base_addr(), 0x10002000);
841        assert_eq!(device.core.lock().get_virtqueue_count(), 2);
842        assert_eq!(device.device_type(), DeviceType::Graphics);
843        assert_eq!(device.name(), "virtio-gpu");
844        assert_eq!(device.core.lock().get_display_name(), "virtio-gpu");
845    }
846
847    #[test_case]
848    fn test_virtio_gpu_resource_id_generation() {
849        let device = VirtioGpuDevice::new(0x10002000);
850        assert_eq!(device.core.lock().next_resource_id(), 1);
851        assert_eq!(device.core.lock().next_resource_id(), 2);
852        assert_eq!(device.core.lock().next_resource_id(), 3);
853    }
854
855    #[test_case]
856    fn test_virtio_gpu_before_init() {
857        let device = VirtioGpuDevice::new(0x10002000);
858        // Should fail before initialization
859        assert!(device.get_framebuffer_config().is_err());
860        assert!(device.get_framebuffer_address().is_err());
861    }
862
863    #[cfg(target_arch = "riscv64")]
864    #[test_case]
865    fn test_virtio_gpu_init_graphics() {
866        let mut device = VirtioGpuDevice::new(0x10002000);
867        device.init_graphics().unwrap();
868    }
869
870    #[cfg(target_arch = "riscv64")]
871    #[test_case]
872    fn test_virtio_gpu_framebuffer_operations() {
873        let mut device = VirtioGpuDevice::new(0x10002000);
874
875        // Initialize the device
876        device.init_graphics().unwrap();
877
878        // Get framebuffer configuration
879        let config = device.get_framebuffer_config().unwrap();
880        assert_eq!(config.width, 1024);
881        assert_eq!(config.height, 768);
882        assert_eq!(config.format, PixelFormat::BGRA8888);
883
884        // Get framebuffer address
885        let fb_addr = device.get_framebuffer_address().unwrap();
886        assert_ne!(fb_addr, 0);
887
888        // Write some test pattern to framebuffer
889        unsafe {
890            let fb_ptr = fb_addr as *mut u32;
891            let pixel_count = (config.width * config.height) as usize;
892
893            // Fill with a gradient pattern
894            for y in 0..config.height {
895                for x in 0..config.width {
896                    let pixel_index = (y * config.width + x) as usize;
897                    if pixel_index < pixel_count {
898                        // Create a simple gradient: red increasing with x, blue with y
899                        let red = if config.width > 1 {
900                            (x * 255) / (config.width - 1)
901                        } else {
902                            0
903                        };
904                        let blue = if config.height > 1 {
905                            (y * 255) / (config.height - 1)
906                        } else {
907                            0
908                        };
909                        let green = 0x80; // Fixed green component
910                        let alpha = 0xFF; // Fully opaque
911
912                        // BGRA format: Blue | Green | Red | Alpha
913                        let pixel = (alpha << 24) | (red << 16) | (green << 8) | blue;
914                        *fb_ptr.add(pixel_index) = pixel;
915                    }
916                }
917            }
918        }
919
920        // Flush the entire framebuffer
921        device
922            .flush_framebuffer(0, 0, config.width, config.height)
923            .unwrap();
924
925        // Verify some pixels were written correctly
926        unsafe {
927            let fb_ptr = fb_addr as *mut u32;
928
929            // Check top-left corner (should be mostly blue)
930            let top_left = *fb_ptr;
931            assert_eq!((top_left >> 24) & 0xFF, 0xFF); // Alpha
932            assert_eq!((top_left >> 16) & 0xFF, 0x00); // Red (should be 0 at x=0)
933            assert_eq!((top_left >> 8) & 0xFF, 0x80); // Green
934            assert_eq!(top_left & 0xFF, 0x00); // Blue (should be 0 at y=0)
935
936            // Check bottom-right corner
937            let bottom_right_index =
938                ((config.height - 1) * config.width + (config.width - 1)) as usize;
939            let bottom_right = *fb_ptr.add(bottom_right_index);
940            assert_eq!((bottom_right >> 24) & 0xFF, 0xFF); // Alpha
941            assert_eq!((bottom_right >> 16) & 0xFF, 0xFF); // Red (should be max at x=width-1)
942            assert_eq!((bottom_right >> 8) & 0xFF, 0x80); // Green
943            assert_eq!(bottom_right & 0xFF, 0xFF); // Blue (should be max at y=height-1)
944        }
945    }
946
947    #[cfg(target_arch = "riscv64")]
948    #[test_case]
949    fn test_virtio_gpu_pixel_drawing() {
950        let mut device = VirtioGpuDevice::new(0x10002000);
951        device.init_graphics().unwrap();
952
953        let config = device.get_framebuffer_config().unwrap();
954        let fb_addr = device.get_framebuffer_address().unwrap();
955
956        // Helper function to set a pixel
957        let set_pixel = |x: u32, y: u32, color: u32| {
958            if x < config.width && y < config.height {
959                unsafe {
960                    let fb_ptr = fb_addr as *mut u32;
961                    let pixel_index = (y * config.width + x) as usize;
962                    *fb_ptr.add(pixel_index) = color;
963                }
964            }
965        };
966
967        // Draw a simple test pattern
968        // Red horizontal line at y=100
969        for x in 0..config.width {
970            set_pixel(x, 100, 0xFF0000FF); // Red in BGRA format
971        }
972
973        // Green vertical line at x=200
974        for y in 0..config.height {
975            set_pixel(200, y, 0xFF00FF00); // Green in BGRA format
976        }
977
978        // Blue diagonal line
979        let min_dim = config.width.min(config.height);
980        for i in 0..min_dim {
981            set_pixel(i, i, 0xFFFF0000); // Blue in BGRA format
982        }
983
984        // Flush the changes
985        device
986            .flush_framebuffer(0, 0, config.width, config.height)
987            .unwrap();
988
989        // Verify some of the drawn pixels
990        unsafe {
991            let fb_ptr = fb_addr as *mut u32;
992
993            // Check red line
994            let red_pixel_index = (100 * config.width + 50) as usize;
995            let red_pixel = *fb_ptr.add(red_pixel_index);
996            assert_eq!(red_pixel, 0xFF0000FF);
997
998            // Check green line
999            let green_pixel_index = (50 * config.width + 200) as usize;
1000            let green_pixel = *fb_ptr.add(green_pixel_index);
1001            assert_eq!(green_pixel, 0xFF00FF00);
1002
1003            // Check blue diagonal
1004            let blue_pixel_index = (100 * config.width + 100) as usize;
1005            let blue_pixel = *fb_ptr.add(blue_pixel_index);
1006            assert_eq!(blue_pixel, 0xFFFF0000);
1007        }
1008    }
1009
1010    #[cfg(target_arch = "riscv64")]
1011    #[test_case]
1012    fn test_virtio_gpu_rectangle_drawing() {
1013        let mut device = VirtioGpuDevice::new(0x10002000);
1014        device.init_graphics().unwrap();
1015
1016        let config = device.get_framebuffer_config().unwrap();
1017        let fb_addr = device.get_framebuffer_address().unwrap();
1018
1019        // Helper function to draw a filled rectangle
1020        let draw_rectangle = |x: u32, y: u32, width: u32, height: u32, color: u32| {
1021            for dy in 0..height {
1022                for dx in 0..width {
1023                    let px = x + dx;
1024                    let py = y + dy;
1025                    if px < config.width && py < config.height {
1026                        unsafe {
1027                            let fb_ptr = fb_addr as *mut u32;
1028                            let pixel_index = (py * config.width + px) as usize;
1029                            *fb_ptr.add(pixel_index) = color;
1030                        }
1031                    }
1032                }
1033            }
1034        };
1035
1036        // Clear framebuffer with black
1037        unsafe {
1038            let fb_ptr = fb_addr as *mut u32;
1039            let pixel_count = (config.width * config.height) as usize;
1040            for i in 0..pixel_count {
1041                *fb_ptr.add(i) = 0xFF000000; // Black with full alpha
1042            }
1043        }
1044
1045        // Draw some rectangles
1046        draw_rectangle(50, 50, 100, 75, 0xFF0000FF); // Red rectangle
1047        draw_rectangle(200, 100, 150, 100, 0xFF00FF00); // Green rectangle
1048        draw_rectangle(400, 200, 80, 120, 0xFFFF0000); // Blue rectangle
1049
1050        // Flush changes
1051        device
1052            .flush_framebuffer(0, 0, config.width, config.height)
1053            .unwrap();
1054
1055        // Verify the rectangles were drawn correctly
1056        unsafe {
1057            let fb_ptr = fb_addr as *mut u32;
1058
1059            // Check red rectangle center
1060            let red_center_index = ((50 + 37) * config.width + (50 + 50)) as usize;
1061            let red_pixel = *fb_ptr.add(red_center_index);
1062            assert_eq!(red_pixel, 0xFF0000FF);
1063
1064            // Check green rectangle center
1065            let green_center_index = ((100 + 50) * config.width + (200 + 75)) as usize;
1066            let green_pixel = *fb_ptr.add(green_center_index);
1067            assert_eq!(green_pixel, 0xFF00FF00);
1068
1069            // Check blue rectangle center
1070            let blue_center_index = ((200 + 60) * config.width + (400 + 40)) as usize;
1071            let blue_pixel = *fb_ptr.add(blue_center_index);
1072            assert_eq!(blue_pixel, 0xFFFF0000);
1073
1074            // Check that area outside rectangles is still black
1075            let background_index = (10 * config.width + 10) as usize;
1076            let background_pixel = *fb_ptr.add(background_index);
1077            assert_eq!(background_pixel, 0xFF000000);
1078        }
1079    }
1080
1081    #[cfg(target_arch = "riscv64")]
1082    #[test_case]
1083    fn test_virtio_gpu_border_drawing() {
1084        let mut device = VirtioGpuDevice::new(0x10002000);
1085        device.init_graphics().unwrap();
1086
1087        let config = device.get_framebuffer_config().unwrap();
1088        let fb_addr = device.get_framebuffer_address().unwrap();
1089
1090        // Helper function to draw a rectangle border
1091        let draw_border = |x: u32, y: u32, width: u32, height: u32, color: u32| {
1092            // Top and bottom edges
1093            for dx in 0..width {
1094                let px = x + dx;
1095                if px < config.width {
1096                    // Top edge
1097                    if y < config.height {
1098                        unsafe {
1099                            let fb_ptr = fb_addr as *mut u32;
1100                            let pixel_index = (y * config.width + px) as usize;
1101                            *fb_ptr.add(pixel_index) = color;
1102                        }
1103                    }
1104                    // Bottom edge
1105                    let bottom_y = y + height - 1;
1106                    if bottom_y < config.height {
1107                        unsafe {
1108                            let fb_ptr = fb_addr as *mut u32;
1109                            let pixel_index = (bottom_y * config.width + px) as usize;
1110                            *fb_ptr.add(pixel_index) = color;
1111                        }
1112                    }
1113                }
1114            }
1115
1116            // Left and right edges
1117            for dy in 0..height {
1118                let py = y + dy;
1119                if py < config.height {
1120                    // Left edge
1121                    if x < config.width {
1122                        unsafe {
1123                            let fb_ptr = fb_addr as *mut u32;
1124                            let pixel_index = (py * config.width + x) as usize;
1125                            *fb_ptr.add(pixel_index) = color;
1126                        }
1127                    }
1128                    // Right edge
1129                    let right_x = x + width - 1;
1130                    if right_x < config.width {
1131                        unsafe {
1132                            let fb_ptr = fb_addr as *mut u32;
1133                            let pixel_index = (py * config.width + right_x) as usize;
1134                            *fb_ptr.add(pixel_index) = color;
1135                        }
1136                    }
1137                }
1138            }
1139        };
1140
1141        // Clear framebuffer
1142        unsafe {
1143            let fb_ptr = fb_addr as *mut u32;
1144            let pixel_count = (config.width * config.height) as usize;
1145            for i in 0..pixel_count {
1146                *fb_ptr.add(i) = 0xFF000000; // Black
1147            }
1148        }
1149
1150        // Draw nested borders
1151        draw_border(10, 10, 200, 150, 0xFF0000FF); // Red outer border
1152        draw_border(20, 20, 180, 130, 0xFF00FF00); // Green middle border
1153        draw_border(30, 30, 160, 110, 0xFFFF0000); // Blue inner border
1154
1155        device
1156            .flush_framebuffer(0, 0, config.width, config.height)
1157            .unwrap();
1158
1159        // Verify borders
1160        unsafe {
1161            let fb_ptr = fb_addr as *mut u32;
1162
1163            // Check red border corners
1164            let top_left_red = *fb_ptr.add((10 * config.width + 10) as usize);
1165            assert_eq!(top_left_red, 0xFF0000FF);
1166
1167            let top_right_red = *fb_ptr.add((10 * config.width + 209) as usize);
1168            assert_eq!(top_right_red, 0xFF0000FF);
1169
1170            // Check green border
1171            let green_border = *fb_ptr.add((20 * config.width + 20) as usize);
1172            assert_eq!(green_border, 0xFF00FF00);
1173
1174            // Check blue border
1175            let blue_border = *fb_ptr.add((30 * config.width + 30) as usize);
1176            assert_eq!(blue_border, 0xFFFF0000);
1177
1178            // Check inside area is still black
1179            let inside = *fb_ptr.add((50 * config.width + 50) as usize);
1180            assert_eq!(inside, 0xFF000000);
1181        }
1182    }
1183
1184    #[cfg(target_arch = "riscv64")]
1185    #[test_case]
1186    fn test_virtio_gpu_pixel_format_verification() {
1187        let mut device = VirtioGpuDevice::new(0x10002000);
1188        device.init_graphics().unwrap();
1189
1190        let config = device.get_framebuffer_config().unwrap();
1191        let fb_addr = device.get_framebuffer_address().unwrap();
1192
1193        // Test various pixel format interpretations
1194        unsafe {
1195            let fb_ptr = fb_addr as *mut u32;
1196
1197            // Test pure colors in BGRA format
1198            let test_colors = [
1199                (0xFF0000FF, "red"),   // Red in BGRA: A=FF, R=00, G=00, B=FF
1200                (0xFF00FF00, "green"), // Green in BGRA: A=FF, R=00, G=FF, B=00
1201                (0xFFFF0000, "blue"),  // Blue in BGRA: A=FF, R=FF, G=00, B=00
1202                (0xFFFFFFFF, "white"), // White in BGRA: A=FF, R=FF, G=FF, B=FF
1203                (0xFF000000, "black"), // Black in BGRA: A=FF, R=00, G=00, B=00
1204                (0xFF808080, "gray"),  // Gray in BGRA: A=FF, R=80, G=80, B=80
1205            ];
1206
1207            // Write test pattern
1208            for (i, (color, _name)) in test_colors.iter().enumerate() {
1209                let x = (i as u32 * 100) % config.width;
1210                let y = (i as u32 * 100) / config.width;
1211                if y < config.height {
1212                    let pixel_index = (y * config.width + x) as usize;
1213                    *fb_ptr.add(pixel_index) = *color;
1214                }
1215            }
1216
1217            device
1218                .flush_framebuffer(0, 0, config.width, config.height)
1219                .unwrap();
1220
1221            // Verify the colors were written correctly
1222            for (i, (expected_color, _name)) in test_colors.iter().enumerate() {
1223                let x = (i as u32 * 100) % config.width;
1224                let y = (i as u32 * 100) / config.width;
1225                if y < config.height {
1226                    let pixel_index = (y * config.width + x) as usize;
1227                    let actual_color = *fb_ptr.add(pixel_index);
1228                    assert_eq!(actual_color, *expected_color);
1229                }
1230            }
1231        }
1232
1233        // Test partial transparency (though VirtIO GPU might not support it fully)
1234        unsafe {
1235            let fb_ptr = fb_addr as *mut u32;
1236            let semi_transparent_red = 0x800000FF; // 50% transparent red
1237            let pixel_index = (100 * config.width + 100) as usize;
1238            *fb_ptr.add(pixel_index) = semi_transparent_red;
1239
1240            device.flush_framebuffer(100, 100, 1, 1).unwrap();
1241
1242            let written_pixel = *fb_ptr.add(pixel_index);
1243            assert_eq!(written_pixel, semi_transparent_red);
1244        }
1245    }
1246
1247    #[cfg(target_arch = "riscv64")]
1248    #[test_case]
1249    fn test_virtio_gpu_command_flow_verification() {
1250        let mut device = VirtioGpuDevice::new(0x10002000);
1251
1252        // Test device initialization and command flow
1253        crate::early_println!("[Test] Starting VirtIO GPU command flow verification");
1254        device.init_graphics().unwrap();
1255
1256        let config = device.get_framebuffer_config().unwrap();
1257        let fb_addr = device.get_framebuffer_address().unwrap();
1258
1259        crate::early_println!(
1260            "[Test] Framebuffer initialized at {:#x}, config: {}x{}",
1261            fb_addr,
1262            config.width,
1263            config.height
1264        );
1265
1266        // Write a test pattern and verify the flush process
1267        unsafe {
1268            let fb_ptr = fb_addr as *mut u32;
1269
1270            // Write a simple checkerboard pattern
1271            for y in 0..config.height.min(100) {
1272                for x in 0..config.width.min(100) {
1273                    let pixel_index = (y * config.width + x) as usize;
1274                    let color = if (x / 10 + y / 10) % 2 == 0 {
1275                        0xFFFF0000 // Blue squares
1276                    } else {
1277                        0xFF00FF00 // Green squares
1278                    };
1279                    *fb_ptr.add(pixel_index) = color;
1280                }
1281            }
1282        }
1283
1284        crate::early_println!("[Test] Written checkerboard pattern to framebuffer");
1285
1286        // Test flushing different regions
1287        device.flush_framebuffer(0, 0, 50, 50).unwrap();
1288        device.flush_framebuffer(50, 50, 50, 50).unwrap();
1289        device
1290            .flush_framebuffer(0, 0, config.width, config.height)
1291            .unwrap();
1292
1293        crate::early_println!("[Test] VirtIO GPU command flow verification completed");
1294    }
1295
1296    #[cfg(target_arch = "riscv64")]
1297    #[test_case]
1298    fn test_virtio_gpu_resource_management() {
1299        let mut device = VirtioGpuDevice::new(0x10002000);
1300        device.init_graphics().unwrap();
1301
1302        // Test that resource IDs are managed correctly
1303        let config = device.get_framebuffer_config().unwrap();
1304        let fb_addr = device.get_framebuffer_address().unwrap();
1305
1306        crate::early_println!("[Test] Testing VirtIO GPU resource management");
1307        crate::early_println!("[Test] Primary framebuffer resource should be ID 1");
1308
1309        // The framebuffer should be associated with resource ID 1
1310        // (as set up in setup_framebuffer)
1311
1312        // Write some data and flush to verify resource association
1313        unsafe {
1314            let fb_ptr = fb_addr as *mut u32;
1315            // Write a diagonal line pattern
1316            for i in 0..config.width.min(config.height).min(500) {
1317                let pixel_index = (i * config.width + i) as usize;
1318                *fb_ptr.add(pixel_index) = 0xFFFFFF00; // Yellow diagonal
1319            }
1320        }
1321
1322        // Flush the diagonal region
1323        device
1324            .flush_framebuffer(0, 0, config.width.min(500), config.height.min(500))
1325            .unwrap();
1326
1327        crate::early_println!("[Test] Resource management test completed");
1328    }
1329}