1#![allow(dead_code)]
16
17use crate::{
18 framebuffer::{CanvasBuffer, DirtyRect, DirtyRectSet, FramebufferOps, MAX_DIRTY_RECTS},
19 hardware::virtio::gpu,
20 memory::{self, phys_to_virt},
21};
22use core::sync::atomic::{AtomicBool, Ordering};
23use spin::Mutex;
24
25const MAX_WIDTH: u32 = 3840;
27const MAX_HEIGHT: u32 = 2160;
28const PRESENT_MIN_TICKS: u64 = 1;
29
30#[derive(Clone, Copy, PartialEq, Eq, Debug)]
32pub enum FramebufferSource {
33 Limine,
34 VirtioGpu,
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}
97
98unsafe impl Send for Framebuffer {}
99unsafe impl Sync for Framebuffer {}
100
101static FRAMEBUFFER: Mutex<Option<Framebuffer>> = Mutex::new(None);
102static FRAMEBUFFER_INITIALIZED: AtomicBool = AtomicBool::new(false);
103
104impl Framebuffer {
105 fn request_present(&mut self) {
106 self.canvas.present_pending = true;
107 }
108
109 fn present_if_due(&mut self, force: bool) {
110 if !self.canvas.present_pending {
111 return;
112 }
113 if self.use_double_buffer {
114 return;
115 }
116 let now = crate::process::scheduler::ticks();
117 if force || now.saturating_sub(self.canvas.last_present_tick) >= PRESENT_MIN_TICKS {
118 self.canvas.present_pending = false;
119 self.canvas.last_present_tick = now;
120 } else {
121 return;
122 }
123
124 if self.info.source != FramebufferSource::VirtioGpu {
125 self.canvas.dirty.clear();
126 return;
127 }
128
129 if let Some(gpu) = gpu::get_gpu() {
130 let mut idx = 0;
131 while idx < self.canvas.dirty.len {
132 let rect = self.canvas.dirty.rects[idx];
133 if rect.is_valid() {
134 let _ = gpu.present_from_linear(
135 self.info.base_virt as *const u8,
136 self.info.stride,
137 rect.x0,
138 rect.y0,
139 rect.x1.saturating_sub(rect.x0),
140 rect.y1.saturating_sub(rect.y0),
141 );
142 }
143 idx += 1;
144 }
145 }
146 self.canvas.dirty.clear();
147 }
148
149 pub fn init_limine(
151 addr: u64,
152 width: u32,
153 height: u32,
154 stride: u32,
155 format: PixelFormat,
156 ) -> Result<(), &'static str> {
157 if addr == 0 || width == 0 || height == 0 {
158 return Err("Invalid framebuffer parameters");
159 }
160
161 let base_virt = addr as usize;
162
163 let info = FramebufferInfo {
164 base: addr,
165 base_virt,
166 width,
167 height,
168 stride,
169 format,
170 source: FramebufferSource::Limine,
171 };
172
173 let canvas = CanvasBuffer {
174 addr: base_virt as *mut u8,
175 width: width as usize,
176 height: height as usize,
177 pitch: stride as usize,
178 bpp: format.bits_per_pixel as u16,
179 back_buffer: None,
180 draw_to_back: false,
181 dirty: DirtyRectSet::empty(),
182 track_dirty: false,
183 present_pending: false,
184 last_present_tick: 0,
185 ops: FramebufferOps::detect(),
186 };
187
188 let fb = Framebuffer {
189 info,
190 canvas,
191 double_buffer: None,
192 use_double_buffer: false,
193 };
194
195 *FRAMEBUFFER.lock() = Some(fb);
196 FRAMEBUFFER_INITIALIZED.store(true, Ordering::SeqCst);
197
198 log::info!(
199 "[FB] Limine framebuffer: {}x{} @ {}bpp, stride={}",
200 width,
201 height,
202 format.bits_per_pixel,
203 stride
204 );
205
206 Ok(())
207 }
208
209 pub fn init_virtio_gpu() -> Result<(), &'static str> {
211 let gpu_info = gpu::get_framebuffer_info().ok_or("VirtIO GPU not initialized")?;
212
213 let format = PixelFormat {
214 red_mask: 0x00FF0000,
215 red_shift: 16,
216 green_mask: 0x0000FF00,
217 green_shift: 8,
218 blue_mask: 0x000000FF,
219 blue_shift: 0,
220 bits_per_pixel: 32,
221 };
222
223 let info = FramebufferInfo {
224 base: gpu_info.framebuffer_phys,
225 base_virt: gpu_info.framebuffer_virt as usize,
226 width: gpu_info.width,
227 height: gpu_info.height,
228 stride: gpu_info.stride,
229 format,
230 source: FramebufferSource::VirtioGpu,
231 };
232
233 let db_size = (info.stride as usize) * (info.height as usize);
235 if db_size == 0 {
236 return Err("Invalid VirtIO framebuffer size");
237 }
238 let db_pages = (db_size + 4095) / 4096;
239 let db_order = db_pages.next_power_of_two().trailing_zeros() as u8;
240 let db_frame = crate::sync::with_irqs_disabled(|token| {
241 memory::allocate_phys_contiguous(token, db_order)
242 })
243 .map_err(|_| "Failed to allocate double buffer")?;
244 let db_virt = phys_to_virt(db_frame.start_address.as_u64()) as *mut u8;
245 unsafe {
246 core::ptr::write_bytes(db_virt, 0, db_size);
249 }
250
251 let canvas = CanvasBuffer {
252 addr: info.base_virt as *mut u8,
253 width: info.width as usize,
254 height: info.height as usize,
255 pitch: info.stride as usize,
256 bpp: 32,
257 back_buffer: None,
258 draw_to_back: false,
259 dirty: DirtyRectSet::empty(),
260 track_dirty: false,
261 present_pending: false,
262 last_present_tick: 0,
263 ops: FramebufferOps::detect(),
264 };
265
266 let fb = Framebuffer {
267 info,
268 canvas,
269 double_buffer: Some(db_virt),
270 use_double_buffer: true,
271 };
272
273 *FRAMEBUFFER.lock() = Some(fb);
274 FRAMEBUFFER_INITIALIZED.store(true, Ordering::SeqCst);
275
276 log::info!(
277 "[FB] VirtIO GPU framebuffer: {}x{} @ {}bpp, stride={}",
278 info.width,
279 info.height,
280 info.format.bits_per_pixel,
281 info.stride
282 );
283
284 Ok(())
285 }
286
287 pub fn info() -> Option<FramebufferInfo> {
289 FRAMEBUFFER.lock().as_ref().map(|fb| fb.info)
290 }
291
292 pub fn width() -> u32 {
294 FRAMEBUFFER
295 .lock()
296 .as_ref()
297 .map(|fb| fb.info.width)
298 .unwrap_or(0)
299 }
300
301 pub fn height() -> u32 {
303 FRAMEBUFFER
304 .lock()
305 .as_ref()
306 .map(|fb| fb.info.height)
307 .unwrap_or(0)
308 }
309
310 pub fn stride() -> u32 {
312 FRAMEBUFFER
313 .lock()
314 .as_ref()
315 .map(|fb| fb.info.stride)
316 .unwrap_or(0)
317 }
318
319 pub fn is_available() -> bool {
321 FRAMEBUFFER_INITIALIZED.load(Ordering::Relaxed)
322 }
323
324 pub fn source() -> FramebufferSource {
326 FRAMEBUFFER
327 .lock()
328 .as_ref()
329 .map(|fb| fb.info.source)
330 .unwrap_or(FramebufferSource::None)
331 }
332
333 pub fn render_stats() -> Option<FramebufferRenderStats> {
334 FRAMEBUFFER
335 .lock()
336 .as_ref()
337 .map(|fb| FramebufferRenderStats {
338 present_pending: fb.canvas.present_pending,
339 dirty_region_count: fb.canvas.dirty.len,
340 last_present_tick: fb.canvas.last_present_tick,
341 })
342 }
343
344 pub fn set_pixel(x: u32, y: u32, r: u8, g: u8, b: u8) {
349 {
350 let mut guard = FRAMEBUFFER.lock();
351 let fb = match guard.as_mut() {
352 Some(f) => f,
353 None => return,
354 };
355
356 if x >= fb.info.width || y >= fb.info.height {
357 return;
358 }
359
360 let pixel = ((r as u32) << fb.info.format.red_shift)
361 | ((g as u32) << fb.info.format.green_shift)
362 | ((b as u32) << fb.info.format.blue_shift);
363
364 let offset = if fb.use_double_buffer {
365 fb.double_buffer.unwrap_or(fb.info.base_virt as *mut u8)
366 } else {
367 fb.info.base_virt as *mut u8
368 };
369
370 unsafe {
371 let pixel_ptr = offset.add((y * fb.info.stride + x * 4) as usize);
372 core::ptr::write(pixel_ptr as *mut u32, pixel);
373 }
374
375 fb.canvas.dirty.include(x, y, 1, 1);
376 fb.request_present();
377 }
378 }
379
380 pub fn fill_rect(x: u32, y: u32, width: u32, height: u32, r: u8, g: u8, b: u8) {
385 if width == 0 || height == 0 {
386 return;
387 }
388
389 {
390 let mut guard = FRAMEBUFFER.lock();
391 let fb = match guard.as_mut() {
392 Some(f) => f,
393 None => return,
394 };
395
396 if x >= fb.info.width || y >= fb.info.height {
397 return;
398 }
399
400 let max_w = fb.info.width - x;
401 let max_h = fb.info.height - y;
402 let width = width.min(max_w);
403 let height = height.min(max_h);
404 if width == 0 || height == 0 {
405 return;
406 }
407
408 let pixel = ((r as u32) << fb.info.format.red_shift)
409 | ((g as u32) << fb.info.format.green_shift)
410 | ((b as u32) << fb.info.format.blue_shift);
411
412 let offset = if fb.use_double_buffer {
413 fb.double_buffer.unwrap_or(fb.info.base_virt as *mut u8)
414 } else {
415 fb.info.base_virt as *mut u8
416 };
417
418 let stride = fb.info.stride as usize;
419 let stride_pixels = fb.info.stride as usize / 4;
420 let width_pixels = width as usize;
421 if stride_pixels == width_pixels {
422 let first = unsafe { offset.add(y as usize * stride + x as usize * 4) as *mut u32 };
423 unsafe {
424 (fb.canvas.ops.fill)(first, pixel, width_pixels * height as usize);
425 }
426 } else {
427 for dy in 0..height as usize {
428 let row_ptr = unsafe {
429 offset.add((y as usize + dy) * stride + x as usize * 4) as *mut u32
430 };
431 unsafe {
432 (fb.canvas.ops.fill)(row_ptr, pixel, width_pixels);
433 }
434 }
435 }
436
437 fb.canvas.dirty.include(x, y, width, height);
438 fb.request_present();
439 }
440 }
441
442 pub fn draw_hline(x: u32, y: u32, length: u32, r: u8, g: u8, b: u8) {
444 Self::fill_rect(x, y, length, 1, r, g, b);
445 }
446
447 pub fn draw_vline(x: u32, y: u32, length: u32, r: u8, g: u8, b: u8) {
449 Self::fill_rect(x, y, 1, length, r, g, b);
450 }
451
452 pub fn clear() {
454 let info = Self::info();
455 if let Some(info) = info {
456 Self::fill_rect(0, 0, info.width, info.height, 0, 0, 0);
457 }
458 }
459
460 pub fn swap_buffers() {
462 let mut virtio_present = None;
463 {
464 let mut guard = FRAMEBUFFER.lock();
465 let fb = match guard.as_mut() {
466 Some(f) => f,
467 None => return,
468 };
469
470 if !fb.use_double_buffer || fb.double_buffer.is_none() {
471 return;
472 }
473
474 let db = fb.double_buffer.unwrap();
475 if fb.canvas.dirty.len == 0 {
476 return;
477 }
478
479 if fb.info.source == FramebufferSource::VirtioGpu {
480 let mut regions = [(0u32, 0u32, 0u32, 0u32); MAX_DIRTY_RECTS];
481 let mut idx = 0;
482 while idx < fb.canvas.dirty.len {
483 let rect = fb.canvas.dirty.rects[idx];
484 regions[idx] = (
485 rect.x0,
486 rect.y0,
487 rect.x1.saturating_sub(rect.x0),
488 rect.y1.saturating_sub(rect.y0),
489 );
490 idx += 1;
491 }
492 virtio_present = Some((
493 db as *const u8,
494 fb.info.stride,
495 regions,
496 fb.canvas.dirty.len,
497 ));
498 } else {
499 let dst = fb.info.base_virt as *mut u32;
500 let src_base = db as *const u32;
501 let stride_pixels = (fb.info.stride / 4) as usize;
502 let mut idx = 0;
503 while idx < fb.canvas.dirty.len {
504 let rect = fb.canvas.dirty.rects[idx];
505 let x = rect.x0 as usize;
506 let y = rect.y0 as usize;
507 let width = rect.x1.saturating_sub(rect.x0) as usize;
508 let height = rect.y1.saturating_sub(rect.y0) as usize;
509 if stride_pixels == width {
511 let off = y * stride_pixels + x;
512 unsafe {
513 (fb.canvas.ops.blit)(dst.add(off), src_base.add(off), width * height);
514 }
515 } else {
516 for row in 0..height {
517 let row_off = (y + row) * stride_pixels + x;
518 unsafe {
519 (fb.canvas.ops.blit)(
520 dst.add(row_off),
521 src_base.add(row_off),
522 width,
523 );
524 }
525 }
526 }
527 idx += 1;
528 }
529 }
530 fb.canvas.dirty.clear();
531 fb.canvas.present_pending = false;
532 fb.canvas.last_present_tick = crate::process::scheduler::ticks();
533 }
534
535 if let Some((src, src_stride, regions, region_count)) = virtio_present {
536 if let Some(gpu) = gpu::get_gpu() {
537 let mut idx = 0;
538 while idx < region_count {
539 let (px, py, pw, ph) = regions[idx];
540 let _ = gpu.present_from_linear(src, src_stride, px, py, pw, ph);
541 idx += 1;
542 }
543 }
544 }
545 }
546
547 pub fn set_double_buffering(enable: bool) {
549 let mut fb = FRAMEBUFFER.lock();
550 if let Some(ref mut f) = fb.as_mut() {
551 f.use_double_buffer = enable && f.double_buffer.is_some();
552 }
553 }
554}
555
556pub use crate::framebuffer::RgbColor;
557
558pub fn init() {
560 log::info!("[FB] Initializing framebuffer subsystem...");
561
562 if gpu::is_available() {
564 if let Err(e) = Framebuffer::init_virtio_gpu() {
565 log::warn!("[FB] VirtIO GPU init failed: {}", e);
566 } else {
567 log::info!("[FB] Using VirtIO GPU framebuffer");
568 }
569 }
570
571 if Framebuffer::is_available() {
574 log::info!("[FB] Using Limine framebuffer");
575 } else {
576 log::warn!("[FB] No framebuffer available");
577 }
578
579 if Framebuffer::is_available() {
581 if let Err(e) = super::display_scheme::register_display_scheme() {
582 log::warn!("[FB] Failed to register display scheme: {:?}", e);
583 }
584 }
585}