1#![allow(dead_code)]
16
17use crate::{
18 framebuffer::{CanvasBuffer, DirtyRectSet, FramebufferOps, MAX_DIRTY_RECTS, PRESENT_MIN_TICKS},
19 hardware::virtio::gpu,
20 memory::{self, phys_to_virt},
21};
22use core::sync::atomic::{AtomicBool, Ordering};
23use spin::{Mutex, Once};
24
25const MAX_WIDTH: u32 = 3840;
27const MAX_HEIGHT: u32 = 2160;
28
29#[derive(Clone, Copy, PartialEq, Eq, Debug)]
31pub enum FramebufferSource {
32 Bootloader,
33 VirtioGpu,
34 AmdGpu,
35 None,
36}
37
38#[derive(Clone, Copy, Debug)]
40pub struct PixelFormat {
41 pub red_mask: u32,
42 pub red_shift: u8,
43 pub green_mask: u32,
44 pub green_shift: u8,
45 pub blue_mask: u32,
46 pub blue_shift: u8,
47 pub bits_per_pixel: u8,
48}
49
50impl Default for PixelFormat {
51 fn default() -> Self {
53 Self {
54 red_mask: 0x00FF0000,
55 red_shift: 16,
56 green_mask: 0x0000FF00,
57 green_shift: 8,
58 blue_mask: 0x000000FF,
59 blue_shift: 0,
60 bits_per_pixel: 32,
61 }
62 }
63}
64
65#[derive(Clone, Copy, Debug)]
67pub struct FramebufferInfo {
68 pub base: u64,
69 pub base_virt: usize,
70 pub width: u32,
71 pub height: u32,
72 pub stride: u32,
73 pub format: PixelFormat,
74 pub source: FramebufferSource,
75}
76
77#[derive(Clone, Copy, Debug)]
78pub struct FramebufferRenderStats {
79 pub present_pending: bool,
80 pub dirty_region_count: usize,
81 pub last_present_tick: u64,
82}
83
84unsafe impl Send for FramebufferInfo {}
85unsafe impl Sync for FramebufferInfo {}
86
87pub struct Framebuffer {
89 info: FramebufferInfo,
90 pub canvas: CanvasBuffer,
92 double_buffer: Option<*mut u8>,
94 use_double_buffer: bool,
96 fb_size: usize,
98 console_defer_present: bool,
100 zero_copy_backing: bool,
102}
103
104unsafe impl Send for Framebuffer {}
105unsafe impl Sync for Framebuffer {}
106
107static FRAMEBUFFER: Mutex<Option<Framebuffer>> = Mutex::new(None);
108static FB_INFO: Once<FramebufferInfo> = Once::new();
114static FRAMEBUFFER_INITIALIZED: AtomicBool = AtomicBool::new(false);
115
116fn publish_info(info: FramebufferInfo) {
117 FB_INFO.call_once(|| info);
118}
119
120impl Framebuffer {
121 fn request_present(&mut self) {
122 self.canvas.present_pending = true;
123 }
124
125 fn present_if_due(&mut self, force: bool) {
126 if !self.canvas.present_pending {
127 return;
128 }
129 if self.use_double_buffer {
130 return;
131 }
132 let now = crate::process::scheduler::ticks();
133 if force || now.saturating_sub(self.canvas.last_present_tick) >= PRESENT_MIN_TICKS {
134 self.canvas.present_pending = false;
135 self.canvas.last_present_tick = now;
136 } else {
137 return;
138 }
139
140 if self.info.source != FramebufferSource::VirtioGpu {
141 self.canvas.dirty.clear();
142 return;
143 }
144
145 if let Some(gpu) = gpu::get_gpu() {
146 let mut idx = 0;
147 while idx < self.canvas.dirty.len {
148 let rect = self.canvas.dirty.rects[idx];
149 if rect.is_valid() {
150 let _ = unsafe {
151 gpu.present_from_linear(
152 self.info.base_virt as *const u8,
153 self.info.stride,
154 rect.x0,
155 rect.y0,
156 rect.x1.saturating_sub(rect.x0),
157 rect.y1.saturating_sub(rect.y0),
158 )
159 };
160 }
161 idx += 1;
162 }
163 }
164 self.canvas.dirty.clear();
165 }
166
167 pub fn init_bootloader(
169 addr: u64,
170 width: u32,
171 height: u32,
172 stride: u32,
173 format: PixelFormat,
174 ) -> Result<(), &'static str> {
175 if addr == 0 || width == 0 || height == 0 {
176 return Err("Invalid framebuffer parameters");
177 }
178
179 let base_virt = addr as usize;
180
181 let info = FramebufferInfo {
182 base: addr,
183 base_virt,
184 width,
185 height,
186 stride,
187 format,
188 source: FramebufferSource::Bootloader,
189 };
190
191 let canvas = CanvasBuffer {
192 addr: base_virt as *mut u8,
193 width: width as usize,
194 height: height as usize,
195 pitch: stride as usize,
196 bpp: format.bits_per_pixel as u16,
197 back_buffer: None,
198 draw_to_back: false,
199 dirty: DirtyRectSet::empty(),
200 track_dirty: true,
201 present_pending: false,
202 last_present_tick: 0,
203 ops: FramebufferOps::detect(),
204 present_row_buf: None,
205 };
206
207 let fb = Framebuffer {
208 info,
209 canvas,
210 double_buffer: None,
211 use_double_buffer: false,
212 fb_size: (stride as usize) * (height as usize),
213 console_defer_present: false,
214 zero_copy_backing: false,
215 };
216
217 *FRAMEBUFFER.lock() = Some(fb);
218 publish_info(info);
219 FRAMEBUFFER_INITIALIZED.store(true, Ordering::SeqCst);
220
221 log::info!(
222 "[FB] Bootloader framebuffer: {}x{} @ {}bpp, stride={}",
223 width,
224 height,
225 format.bits_per_pixel,
226 stride
227 );
228
229 Ok(())
230 }
231
232 pub fn init_virtio_gpu() -> Result<(), &'static str> {
238 let gpu = crate::hardware::virtio::gpu::get_gpu().ok_or("VirtIO GPU not initialized")?;
239 let gpu_info = gpu.info();
240 if gpu_info.width == 0 || gpu_info.height == 0 {
241 return Err("Invalid VirtIO GPU dimensions");
242 }
243
244 let format = PixelFormat {
245 red_mask: 0x00FF0000,
246 red_shift: 16,
247 green_mask: 0x0000FF00,
248 green_shift: 8,
249 blue_mask: 0x000000FF,
250 blue_shift: 0,
251 bits_per_pixel: 32,
252 };
253
254 let stride = gpu_info.stride;
256 let db_size = (stride as usize) * (gpu_info.height as usize);
257 if db_size == 0 {
258 return Err("Invalid VirtIO framebuffer size");
259 }
260 let db_pages = (db_size + 4095) / 4096;
261 let db_order = db_pages.next_power_of_two().trailing_zeros() as u8;
262 let db_frame = crate::sync::with_irqs_disabled(|token| {
263 memory::allocate_phys_contiguous(token, db_order)
264 })
265 .map_err(|_| "Failed to allocate double buffer")?;
266 let db_phys = db_frame.start_address.as_u64();
267 let db_virt = phys_to_virt(db_phys) as *mut u8;
268 unsafe {
269 core::ptr::write_bytes(db_virt, 0, db_size);
272 }
273
274 let zero_copy = match gpu.attach_external_backing(db_phys, db_size) {
277 Ok(()) => {
278 let _ = gpu.present_scanout_rect(0, 0, gpu_info.width, gpu_info.height);
280 true
281 }
282 Err(e) => {
283 log::warn!(
284 "[FB] external backing attach failed ({}): using bounce copy",
285 e
286 );
287 false
288 }
289 };
290
291 let info = FramebufferInfo {
292 base: db_phys,
293 base_virt: db_virt as usize,
294 width: gpu_info.width,
295 height: gpu_info.height,
296 stride: gpu_info.stride,
297 format,
298 source: FramebufferSource::VirtioGpu,
299 };
300
301 let canvas = CanvasBuffer {
302 addr: db_virt,
303 width: info.width as usize,
304 height: info.height as usize,
305 pitch: info.stride as usize,
306 bpp: 32,
307 back_buffer: None,
308 draw_to_back: false,
309 dirty: DirtyRectSet::empty(),
310 track_dirty: true,
311 present_pending: false,
312 last_present_tick: 0,
313 ops: FramebufferOps::detect(),
314 present_row_buf: None,
315 };
316
317 let fb = Framebuffer {
318 info,
319 canvas,
320 double_buffer: Some(db_virt),
321 use_double_buffer: true,
322 fb_size: (stride as usize) * (gpu_info.height as usize),
323 console_defer_present: false,
324 zero_copy_backing: zero_copy,
325 };
326
327 *FRAMEBUFFER.lock() = Some(fb);
328 publish_info(info);
329 FRAMEBUFFER_INITIALIZED.store(true, Ordering::SeqCst);
330
331 log::info!(
332 "[FB] VirtIO GPU framebuffer: {}x{} @ {}bpp, stride={} ({})",
333 info.width,
334 info.height,
335 info.format.bits_per_pixel,
336 info.stride,
337 if zero_copy {
338 "zero-copy backing"
339 } else {
340 "bounce copy"
341 }
342 );
343
344 Ok(())
345 }
346
347 pub fn init_amd_gpu() -> Result<(), &'static str> {
349 let gpu_guard = crate::hardware::amdgpu::get_gpu().ok_or("AMDGPU not available")?;
350 let gpu = gpu_guard.as_ref().ok_or("AMDGPU not initialized")?;
351
352 let (phys, width, height, pitch) = gpu.framebuffer_info();
353
354 let format = PixelFormat {
355 red_mask: 0x00FF0000,
356 red_shift: 16,
357 green_mask: 0x0000FF00,
358 green_shift: 8,
359 blue_mask: 0x000000FF,
360 blue_shift: 0,
361 bits_per_pixel: 32,
362 };
363
364 let hhdm = crate::memory::hhdm_offset();
365 let base_virt = (phys as u64 + hhdm) as usize;
366
367 let info = FramebufferInfo {
368 base: phys as u64,
369 base_virt,
370 width,
371 height,
372 stride: pitch,
373 format,
374 source: FramebufferSource::AmdGpu,
375 };
376
377 let canvas = CanvasBuffer {
378 addr: base_virt as *mut u8,
379 width: width as usize,
380 height: height as usize,
381 pitch: pitch as usize,
382 bpp: 32,
383 back_buffer: None,
384 draw_to_back: false,
385 dirty: DirtyRectSet::empty(),
386 track_dirty: true,
387 present_pending: false,
388 last_present_tick: 0,
389 ops: FramebufferOps::detect(),
390 present_row_buf: None,
391 };
392
393 *FRAMEBUFFER.lock() = Some(Framebuffer {
394 info,
395 canvas,
396 double_buffer: None,
397 use_double_buffer: false,
398 fb_size: (pitch as usize) * (height as usize),
399 console_defer_present: false,
400 zero_copy_backing: false,
401 });
402 publish_info(info);
403
404 log::info!(
405 "[FB] AMDGPU framebuffer: {}x{} @ {}bpp, {} bytes",
406 info.width,
407 info.height,
408 info.format.bits_per_pixel,
409 info.stride
410 );
411
412 Ok(())
413 }
414
415 pub fn info() -> Option<FramebufferInfo> {
417 FB_INFO.get().copied()
418 }
419
420 pub fn width() -> u32 {
422 FB_INFO.get().map(|i| i.width).unwrap_or(0)
423 }
424
425 pub fn height() -> u32 {
427 FB_INFO.get().map(|i| i.height).unwrap_or(0)
428 }
429
430 pub fn stride() -> u32 {
432 FB_INFO.get().map(|i| i.stride).unwrap_or(0)
433 }
434
435 pub fn is_available() -> bool {
437 FRAMEBUFFER_INITIALIZED.load(Ordering::Relaxed)
438 }
439
440 pub fn source() -> FramebufferSource {
442 FB_INFO
443 .get()
444 .map(|i| i.source)
445 .unwrap_or(FramebufferSource::None)
446 }
447
448 pub fn render_stats() -> Option<FramebufferRenderStats> {
449 FRAMEBUFFER
450 .lock()
451 .as_ref()
452 .map(|fb| FramebufferRenderStats {
453 present_pending: fb.canvas.present_pending,
454 dirty_region_count: fb.canvas.dirty.len,
455 last_present_tick: fb.canvas.last_present_tick,
456 })
457 }
458
459 pub fn set_pixel(x: u32, y: u32, r: u8, g: u8, b: u8) {
466 let mut guard = FRAMEBUFFER.lock();
467 let fb = match guard.as_mut() {
468 Some(f) => f,
469 None => return,
470 };
471
472 if x >= fb.info.width || y >= fb.info.height {
473 return;
474 }
475
476 let pixel = ((r as u32) << fb.info.format.red_shift)
477 | ((g as u32) << fb.info.format.green_shift)
478 | ((b as u32) << fb.info.format.blue_shift);
479
480 fb.canvas.write_pixel(x as usize, y as usize, pixel);
481 fb.canvas.dirty.include(x, y, 1, 1);
482 fb.request_present();
483 }
484
485 pub fn fill_rect(x: u32, y: u32, width: u32, height: u32, r: u8, g: u8, b: u8) {
492 if width == 0 || height == 0 {
493 return;
494 }
495
496 let mut guard = FRAMEBUFFER.lock();
497 let fb = match guard.as_mut() {
498 Some(f) => f,
499 None => return,
500 };
501
502 if x >= fb.info.width || y >= fb.info.height {
503 return;
504 }
505
506 let width = width.min(fb.info.width - x);
507 let height = height.min(fb.info.height - y);
508 if width == 0 || height == 0 {
509 return;
510 }
511
512 let pixel = ((r as u32) << fb.info.format.red_shift)
513 | ((g as u32) << fb.info.format.green_shift)
514 | ((b as u32) << fb.info.format.blue_shift);
515
516 fb.canvas.fill_rect(
517 x as usize,
518 y as usize,
519 width as usize,
520 height as usize,
521 pixel,
522 );
523 fb.request_present();
524 }
525
526 pub fn blit_rect(src: &[u8], x: u32, y: u32, w: u32, h: u32) {
534 if w == 0 || h == 0 || x >= Self::width() || y >= Self::height() {
535 return;
536 }
537 let needed = w as usize * h as usize * 4;
538 if src.len() < needed {
539 return;
540 }
541
542 let mut guard = FRAMEBUFFER.lock();
543 let fb = match guard.as_mut() {
544 Some(f) => f,
545 None => return,
546 };
547
548 let max_w = fb.info.width - x;
549 let max_h = fb.info.height - y;
550 let w = w.min(max_w);
551 let h = h.min(max_h);
552 if w == 0 || h == 0 {
553 return;
554 }
555
556 let offset = if fb.use_double_buffer {
557 fb.double_buffer.unwrap_or(fb.info.base_virt as *mut u8)
558 } else {
559 fb.info.base_virt as *mut u8
560 };
561
562 let stride = fb.info.stride as usize;
563 let stride_pixels = stride / 4;
564 let src_row_bytes = w as usize * 4;
565 let full_width_contiguous =
566 x == 0 && w as usize == fb.info.width as usize && stride_pixels == w as usize;
567
568 unsafe {
569 if full_width_contiguous {
570 let dst = offset.add(y as usize * stride) as *mut u32;
571 let s = src.as_ptr() as *const u32;
572 (fb.canvas.ops.blit)(dst, s, w as usize * h as usize);
573 } else {
574 for dy in 0..h as usize {
575 let dst = offset.add((y as usize + dy) * stride + x as usize * 4) as *mut u32;
576 let s = src.as_ptr().add(dy * src_row_bytes) as *const u32;
577 (fb.canvas.ops.blit)(dst, s, w as usize);
578 }
579 }
580 }
581
582 fb.canvas.dirty.include(x, y, w, h);
583 fb.request_present();
584 }
585
586 pub fn draw_hline(x: u32, y: u32, length: u32, r: u8, g: u8, b: u8) {
588 Self::fill_rect(x, y, length, 1, r, g, b);
589 }
590
591 pub fn draw_vline(x: u32, y: u32, length: u32, r: u8, g: u8, b: u8) {
593 Self::fill_rect(x, y, 1, length, r, g, b);
594 }
595
596 pub fn clear() {
598 let info = Self::info();
599 if let Some(info) = info {
600 Self::fill_rect(0, 0, info.width, info.height, 0, 0, 0);
601 }
602 }
603
604 pub fn swap_buffers() {
606 enum VirtioPresent {
607 ZeroCopy {
608 x: u32,
609 y: u32,
610 w: u32,
611 h: u32,
612 },
613 BounceCopy {
614 src: *const u8,
615 stride: u32,
616 regions: [(u32, u32, u32, u32); MAX_DIRTY_RECTS],
617 count: usize,
618 },
619 }
620
621 let mut virtio_present = None;
622 {
623 let mut guard = FRAMEBUFFER.lock();
624 let fb = match guard.as_mut() {
625 Some(f) => f,
626 None => return,
627 };
628
629 if !fb.use_double_buffer || fb.double_buffer.is_none() {
630 return;
631 }
632
633 let db = fb.double_buffer.unwrap();
634 if fb.canvas.dirty.len == 0 {
635 return;
636 }
637
638 if fb.info.source == FramebufferSource::VirtioGpu {
639 let mut bx0 = u32::MAX;
645 let mut by0 = u32::MAX;
646 let mut bx1 = 0u32;
647 let mut by1 = 0u32;
648 let mut idx = 0;
649 while idx < fb.canvas.dirty.len {
650 let rect = fb.canvas.dirty.rects[idx];
651 if rect.is_valid() {
652 bx0 = bx0.min(rect.x0);
653 by0 = by0.min(rect.y0);
654 bx1 = bx1.max(rect.x1);
655 by1 = by1.max(rect.y1);
656 }
657 idx += 1;
658 }
659
660 virtio_present = if fb.zero_copy_backing && bx1 > bx0 && by1 > by0 {
661 Some(VirtioPresent::ZeroCopy {
662 x: bx0,
663 y: by0,
664 w: bx1 - bx0,
665 h: by1 - by0,
666 })
667 } else {
668 let mut regions = [(0u32, 0u32, 0u32, 0u32); MAX_DIRTY_RECTS];
669 let mut idx = 0;
670 while idx < fb.canvas.dirty.len {
671 let rect = fb.canvas.dirty.rects[idx];
672 regions[idx] = (
673 rect.x0,
674 rect.y0,
675 rect.x1.saturating_sub(rect.x0),
676 rect.y1.saturating_sub(rect.y0),
677 );
678 idx += 1;
679 }
680 Some(VirtioPresent::BounceCopy {
681 src: db as *const u8,
682 stride: fb.info.stride,
683 regions,
684 count: fb.canvas.dirty.len,
685 })
686 };
687 } else {
688 let dst = fb.info.base_virt as *mut u32;
689 let src_base = db as *const u32;
690 let stride_pixels = (fb.info.stride / 4) as usize;
691 let mut idx = 0;
692 while idx < fb.canvas.dirty.len {
693 let rect = fb.canvas.dirty.rects[idx];
694 let x = rect.x0 as usize;
695 let y = rect.y0 as usize;
696 let width = rect.x1.saturating_sub(rect.x0) as usize;
697 let height = rect.y1.saturating_sub(rect.y0) as usize;
698 if stride_pixels == width {
700 let off = y * stride_pixels + x;
701 unsafe {
702 (fb.canvas.ops.blit)(dst.add(off), src_base.add(off), width * height);
703 }
704 } else {
705 for row in 0..height {
706 let row_off = (y + row) * stride_pixels + x;
707 unsafe {
708 (fb.canvas.ops.blit)(
709 dst.add(row_off),
710 src_base.add(row_off),
711 width,
712 );
713 }
714 }
715 }
716 idx += 1;
717 }
718 }
719 fb.canvas.dirty.clear();
720 fb.canvas.present_pending = false;
721 fb.canvas.last_present_tick = crate::process::scheduler::ticks();
722 }
723
724 match virtio_present {
725 Some(VirtioPresent::ZeroCopy { x, y, w, h }) => {
726 if let Some(gpu) = gpu::get_gpu() {
727 let _ = gpu.present_scanout_rect(x, y, w, h);
728 }
729 }
730 Some(VirtioPresent::BounceCopy {
731 src,
732 stride,
733 regions,
734 count: region_count,
735 }) => {
736 if let Some(gpu) = gpu::get_gpu() {
737 let mut idx = 0;
738 while idx < region_count {
739 let (px, py, pw, ph) = regions[idx];
740 let _ = unsafe { gpu.present_from_linear(src, stride, px, py, pw, ph) };
741 idx += 1;
742 }
743 }
744 }
745 None => {}
746 }
747 }
748
749 pub fn set_double_buffering(enable: bool) {
751 let mut fb = FRAMEBUFFER.lock();
752 if let Some(ref mut f) = fb.as_mut() {
753 f.use_double_buffer = enable && f.double_buffer.is_some();
754 }
755 }
756}
757
758pub use crate::framebuffer::RgbColor;
759
760pub fn init() {
762 log::info!("[FB] Initializing framebuffer subsystem...");
763
764 if gpu::is_available() {
766 if let Err(e) = Framebuffer::init_virtio_gpu() {
767 log::warn!("[FB] VirtIO GPU init failed: {}", e);
768 } else {
769 log::info!("[FB] Using VirtIO GPU framebuffer");
770 }
771 }
772
773 if !Framebuffer::is_available() && crate::hardware::amdgpu::is_available() {
775 if let Err(e) = Framebuffer::init_amd_gpu() {
776 log::warn!("[FB] AMDGPU init failed: {}", e);
777 } else {
778 log::info!("[FB] Using AMDGPU framebuffer");
779 }
780 }
781
782 if Framebuffer::is_available() {
784 log::info!("[FB] Using UEFI bootloader framebuffer");
785 } else {
786 log::warn!("[FB] No framebuffer available");
787 }
788
789 if Framebuffer::is_available() {
791 if let Err(e) = super::display_scheme::register_display_scheme() {
792 log::warn!("[FB] Failed to register display scheme: {:?}", e);
793 }
794 }
795}