Merge branch 'graphics_improvements' into 'master'

Various improvements to the graphics subsystem

See merge request redox-os/drivers!276
This commit is contained in:
Jeremy Soller
2025-07-07 12:53:41 -06:00
8 changed files with 124 additions and 92 deletions
+34 -1
View File
@@ -1,7 +1,8 @@
#![feature(slice_as_array)]
use std::collections::{BTreeMap, HashMap};
use std::io;
use std::fs::File;
use std::io::{self, Write};
use std::mem::transmute;
use std::sync::Arc;
@@ -53,6 +54,7 @@ pub struct GraphicsScheme<T: GraphicsAdapter> {
adapter: T,
scheme_name: String,
disable_graphical_debug: Option<File>,
socket: Socket,
next_id: usize,
handles: BTreeMap<usize, Handle<T>>,
@@ -83,9 +85,15 @@ impl<T: GraphicsAdapter> GraphicsScheme<T> {
assert!(scheme_name.starts_with("display"));
let socket = Socket::nonblock(&scheme_name).expect("failed to create graphics scheme");
let disable_graphical_debug = Some(
File::open("/scheme/debug/disable-graphical-debug")
.expect("vesad: Failed to open /scheme/debug/disable-graphical-debug"),
);
GraphicsScheme {
adapter,
scheme_name,
disable_graphical_debug,
socket,
next_id: 0,
handles: BTreeMap::new(),
@@ -111,6 +119,14 @@ impl<T: GraphicsAdapter> GraphicsScheme<T> {
VtEventKind::Activate => {
log::info!("activate {}", vt_event.vt);
// Disable the kernel graphical debug writing once switching vt's for the
// first time. This way the kernel graphical debug remains enabled if the
// userspace logging infrastructure doesn't start up because for example a
// kernel panic happened prior to it starting up or logd crashed.
if let Some(mut disable_graphical_debug) = self.disable_graphical_debug.take() {
let _ = disable_graphical_debug.write(&[1]);
}
self.active_vt = vt_event.vt;
let vt_state =
@@ -480,6 +496,23 @@ impl<T: GraphicsAdapter> SchemeSync for GraphicsScheme<T> {
Ok(size_of::<ipc::DumbFramebufferMapOffset>())
}
ipc::DESTROY_DUMB_FRAMEBUFFER => {
if payload.len() < size_of::<ipc::DestroyDumbFramebuffer>() {
return Err(Error::new(EINVAL));
}
let payload = unsafe {
transmute::<
&mut [u8; size_of::<ipc::DestroyDumbFramebuffer>()],
&mut ipc::DestroyDumbFramebuffer,
>(payload.as_mut_array().unwrap())
};
if fbs.remove(&{ payload.fb_id }).is_none() {
return Err(Error::new(ENOENT));
}
Ok(size_of::<ipc::DestroyDumbFramebuffer>())
}
ipc::UPDATE_PLANE => {
if payload.len() < size_of::<ipc::UpdatePlane>() {
return Err(Error::new(EINVAL));
+2
View File
@@ -95,6 +95,8 @@ impl FbbootlogScheme {
},
);
let _ = map.display_handle.destroy_dumb_framebuffer(map.fb);
map.fb = fb;
map.inner = new_map;
+2 -7
View File
@@ -26,7 +26,8 @@ impl Display {
/// Re-open the display after a handoff.
pub fn reopen_for_handoff(&mut self) {
let new_display_handle = Self::open_display(&self.input_handle).unwrap();
let display_file = self.input_handle.open_display().unwrap();
let new_display_handle = V1GraphicsHandle::from_file(display_file).unwrap();
eprintln!("fbcond: Opened new display");
@@ -49,12 +50,6 @@ impl Display {
}
}
fn open_display(input_handle: &ConsumerHandle) -> io::Result<V1GraphicsHandle> {
let display_file = input_handle.open_display()?;
V1GraphicsHandle::from_file(display_file)
}
pub fn sync_rect(&mut self, sync_rect: Damage) {
if let Some(map) = &self.map {
map.display_handle.sync_rect(sync_rect).unwrap();
+2
View File
@@ -11,6 +11,8 @@ pub use crate::common::DisplayMap;
///
/// The v1 graphics API only allows a single framebuffer for each VT, requires each display to be
/// handled separately and doesn't support page flipping.
///
/// This API is stable. No breaking changes are allowed to be made without a version bump.
pub struct V1GraphicsHandle {
file: File,
}
+24 -2
View File
@@ -33,10 +33,13 @@ unsafe fn sys_call<T>(
))
}
/// A graphics handle using the (currently unstable) v2 graphics API.
/// A graphics handle using the v2 graphics API.
///
/// The v2 graphics API allows creating framebuffers on the fly, using them for page flipping and
/// handles all displays using a single fd.
///
/// This API is not yet stable. Do not depend on it outside of the drivers repo until it has been
/// stabilized.
pub struct V2GraphicsHandle {
file: File,
}
@@ -121,6 +124,19 @@ impl V2GraphicsHandle {
Ok(unsafe { DisplayMap::new(offscreen, width as usize, height as usize) })
}
pub fn destroy_dumb_framebuffer(&self, id: usize) -> io::Result<usize> {
let mut cmd = ipc::DestroyDumbFramebuffer { fb_id: id };
unsafe {
sys_call(
&self.file,
&mut cmd,
0,
&[ipc::DESTROY_DUMB_FRAMEBUFFER, 0, 0],
)?;
}
Ok(cmd.fb_id)
}
pub fn update_plane(&self, display_id: usize, fb_id: usize, damage: Damage) -> io::Result<()> {
let mut cmd = ipc::UpdatePlane {
display_id,
@@ -169,7 +185,13 @@ pub mod ipc {
pub offset: usize,
}
pub const UPDATE_PLANE: u64 = 5;
pub const DESTROY_DUMB_FRAMEBUFFER: u64 = 5;
#[repr(C, packed)]
pub struct DestroyDumbFramebuffer {
pub fb_id: usize,
}
pub const UPDATE_PLANE: u64 = 6;
#[repr(C, packed)]
pub struct UpdatePlane {
pub display_id: usize,
-59
View File
@@ -1,59 +0,0 @@
use std::ptr;
pub struct FrameBuffer {
pub onscreen: *mut [u32],
pub phys: usize,
pub width: usize,
pub height: usize,
pub stride: usize,
}
impl FrameBuffer {
pub unsafe fn new(phys: usize, width: usize, height: usize, stride: usize) -> Self {
let size = stride * height;
let virt = common::physmap(
phys,
size * 4,
common::Prot {
read: true,
write: true,
},
common::MemoryType::WriteCombining,
)
.expect("vesad: failed to map framebuffer") as *mut u32;
let onscreen = ptr::slice_from_raw_parts_mut(virt, size);
Self {
onscreen,
phys,
width,
height,
stride,
}
}
pub unsafe fn parse(var: &str) -> Option<Self> {
fn parse_number(part: &str) -> Option<usize> {
let (start, radix) = if part.starts_with("0x") {
(2, 16)
} else {
(0, 10)
};
match usize::from_str_radix(&part[start..], radix) {
Ok(ok) => Some(ok),
Err(err) => {
eprintln!("vesad: failed to parse '{}': {}", part, err);
None
}
}
}
let mut parts = var.split(',');
let phys = parse_number(parts.next()?)?;
let width = parse_number(parts.next()?)?;
let height = parse_number(parts.next()?)?;
let stride = parse_number(parts.next()?)?;
Some(Self::new(phys, width, height, stride))
}
}
+2 -21
View File
@@ -3,16 +3,12 @@ extern crate syscall;
use driver_graphics::GraphicsScheme;
use event::{user_data, EventQueue};
use inputd::{DisplayHandle, VtEventKind};
use inputd::DisplayHandle;
use std::env;
use std::fs::File;
use std::io::Write;
use std::os::fd::AsRawFd;
use crate::framebuffer::FrameBuffer;
use crate::scheme::FbAdapter;
use crate::scheme::{FbAdapter, FrameBuffer};
mod framebuffer;
mod scheme;
fn main() {
@@ -105,11 +101,6 @@ fn inner(daemon: redox_daemon::Daemon, framebuffers: Vec<FrameBuffer>) -> ! {
)
.unwrap();
let mut disable_graphical_debug = Some(
File::open("/scheme/debug/disable-graphical-debug")
.expect("vesad: Failed to open /scheme/debug/disable-graphical-debug"),
);
libredox::call::setrens(0, 0).expect("vesad: failed to enter null namespace");
daemon.ready().expect("failed to notify parent");
@@ -125,16 +116,6 @@ fn inner(daemon: redox_daemon::Daemon, framebuffers: Vec<FrameBuffer>) -> ! {
.read_vt_event()
.expect("vesad: failed to read display handle")
{
if let VtEventKind::Activate = vt_event.kind {
// Disable the kernel graphical debug writing once switching vt's for the
// first time. This way the kernel graphical debug remains enabled if the
// userspace logging infrastructure doesn't start up because for example a
// kernel panic happened prior to it starting up or logd crashed.
if let Some(mut disable_graphical_debug) = disable_graphical_debug.take() {
let _ = disable_graphical_debug.write(&[1]);
}
}
scheme.handle_vt_event(vt_event);
}
}
+58 -2
View File
@@ -6,8 +6,6 @@ use driver_graphics::{CursorFramebuffer, CursorPlane, Framebuffer, GraphicsAdapt
use graphics_ipc::v1::Damage;
use syscall::PAGE_SIZE;
use crate::framebuffer::FrameBuffer;
pub struct FbAdapter {
pub framebuffers: Vec<FrameBuffer>,
}
@@ -60,6 +58,64 @@ impl GraphicsAdapter for FbAdapter {
}
}
pub struct FrameBuffer {
pub onscreen: *mut [u32],
pub phys: usize,
pub width: usize,
pub height: usize,
pub stride: usize,
}
impl FrameBuffer {
pub unsafe fn new(phys: usize, width: usize, height: usize, stride: usize) -> Self {
let size = stride * height;
let virt = common::physmap(
phys,
size * 4,
common::Prot {
read: true,
write: true,
},
common::MemoryType::WriteCombining,
)
.expect("vesad: failed to map framebuffer") as *mut u32;
let onscreen = ptr::slice_from_raw_parts_mut(virt, size);
Self {
onscreen,
phys,
width,
height,
stride,
}
}
pub unsafe fn parse(var: &str) -> Option<Self> {
fn parse_number(part: &str) -> Option<usize> {
let (start, radix) = if part.starts_with("0x") {
(2, 16)
} else {
(0, 10)
};
match usize::from_str_radix(&part[start..], radix) {
Ok(ok) => Some(ok),
Err(err) => {
eprintln!("vesad: failed to parse '{}': {}", part, err);
None
}
}
}
let mut parts = var.split(',');
let phys = parse_number(parts.next()?)?;
let width = parse_number(parts.next()?)?;
let height = parse_number(parts.next()?)?;
let stride = parse_number(parts.next()?)?;
Some(Self::new(phys, width, height, stride))
}
}
pub struct GraphicScreen {
width: usize,
height: usize,