fix: VirtIO GPU driver and snake rendering through display server
- Fix swapped command IDs: attach_backing was 0x0104 (FLUSH), resource_flush was 0x0106 (ATTACH_BACKING). Now correct. - Increase process stack from 4 pages (16 KB) to 16 pages (64 KB) to prevent stack overflow in display process. - Restructure display-qemu to avoid MockFramebuffer's 32 KB array inflating the VirtIO path's stack frame. - Wire snake app to render through display server via IPC instead of UART escape codes. Uses existing renderer.rs pixel renderer. - Init now delegates display endpoint to snake process.
This commit is contained in:
3
Cargo.lock
generated
3
Cargo.lock
generated
@@ -55,6 +55,7 @@ name = "display-qemu"
|
|||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"display-server",
|
"display-server",
|
||||||
|
"driver-framebuffer-mock",
|
||||||
"driver-framebuffer-virtio",
|
"driver-framebuffer-virtio",
|
||||||
"port-framebuffer",
|
"port-framebuffer",
|
||||||
"server-framework",
|
"server-framework",
|
||||||
@@ -283,6 +284,8 @@ dependencies = [
|
|||||||
name = "snake"
|
name = "snake"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"display-server",
|
||||||
|
"server-framework",
|
||||||
"types",
|
"types",
|
||||||
"userspace",
|
"userspace",
|
||||||
"userspace-rt",
|
"userspace-rt",
|
||||||
|
|||||||
@@ -161,10 +161,10 @@ fn load_elf(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for i in 0..4usize {
|
for i in 0..16usize {
|
||||||
let sp = mmu::allocator::alloc_zeroed_page();
|
let sp = mmu::allocator::alloc_zeroed_page();
|
||||||
mmu::map(user_pt, mmu::Mapping {
|
mmu::map(user_pt, mmu::Mapping {
|
||||||
va: types::address::VirtualAddress::from_raw(USER_VA_BASE - (4 - i) * 4096),
|
va: types::address::VirtualAddress::from_raw(USER_VA_BASE - (16 - i) * 4096),
|
||||||
pa: types::address::PhysicalAddress::from_raw(sp),
|
pa: types::address::PhysicalAddress::from_raw(sp),
|
||||||
pages: 1,
|
pages: 1,
|
||||||
flags: mmu::Sv39Flags::user_rw(),
|
flags: mmu::Sv39Flags::user_rw(),
|
||||||
|
|||||||
@@ -12,3 +12,5 @@ bench = false
|
|||||||
types = { workspace = true }
|
types = { workspace = true }
|
||||||
userspace = { workspace = true }
|
userspace = { workspace = true }
|
||||||
userspace-rt = { workspace = true }
|
userspace-rt = { workspace = true }
|
||||||
|
display-server = { workspace = true }
|
||||||
|
server-framework = { workspace = true }
|
||||||
|
|||||||
@@ -3,119 +3,70 @@
|
|||||||
|
|
||||||
extern crate userspace_rt;
|
extern crate userspace_rt;
|
||||||
|
|
||||||
use snake::game::{Direction, GameState, Snake, GRID_WIDTH, GRID_HEIGHT};
|
use snake::game::{Direction, GameState, Snake};
|
||||||
|
use snake::renderer::{self, RenderCommand};
|
||||||
use types::bounded::CapSlot;
|
use types::bounded::CapSlot;
|
||||||
|
use types::display::{Color, Dimension, ScreenCoord};
|
||||||
use types::time::Ticks;
|
use types::time::Ticks;
|
||||||
use userspace_rt::syscall;
|
use userspace_rt::syscall;
|
||||||
|
use server_framework::Encode;
|
||||||
|
use display_server::protocol::DisplayRequest;
|
||||||
|
|
||||||
const INPUT_EP: CapSlot = CapSlot::const_new(0);
|
const INPUT_EP: CapSlot = CapSlot::const_new(0);
|
||||||
|
const DISPLAY_EP: CapSlot = CapSlot::const_new(1);
|
||||||
const TICK_DELAY: u64 = 2_000_000;
|
const TICK_DELAY: u64 = 2_000_000;
|
||||||
|
const MAX_RENDER_CMDS: usize = 140;
|
||||||
|
|
||||||
fn putchar(c: u8) { syscall::debug_putchar(c); }
|
fn display_send(req: &DisplayRequest) {
|
||||||
fn puts(s: &[u8]) { for &b in s { putchar(b); } }
|
let words = req.encode();
|
||||||
|
syscall::send(DISPLAY_EP, words[0], words[1], words[2], words[3]);
|
||||||
fn move_cursor(row: u16, col: u16) {
|
syscall::receive(DISPLAY_EP);
|
||||||
puts(b"\x1b[");
|
|
||||||
print_dec(row + 1);
|
|
||||||
putchar(b';');
|
|
||||||
print_dec(col + 1);
|
|
||||||
putchar(b'H');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn print_dec(mut v: u16) {
|
fn draw_rect(x: u16, y: u16, w: u16, h: u16, color: u32) {
|
||||||
if v == 0 { putchar(b'0'); return; }
|
display_send(&DisplayRequest::DrawRect {
|
||||||
let mut buf = [0u8; 5];
|
x: ScreenCoord::new(x),
|
||||||
let mut i = 0;
|
y: ScreenCoord::new(y),
|
||||||
while v > 0 { buf[i] = b'0' + (v % 10) as u8; v /= 10; i += 1; }
|
w: Dimension::new(w),
|
||||||
while i > 0 { i -= 1; putchar(buf[i]); }
|
h: Dimension::new(h),
|
||||||
|
color: Color::from_raw(color),
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_color(fg: u8) {
|
fn flush() {
|
||||||
puts(b"\x1b[");
|
display_send(&DisplayRequest::Flush);
|
||||||
print_dec(fg as u16);
|
|
||||||
putchar(b'm');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[unsafe(no_mangle)]
|
#[unsafe(no_mangle)]
|
||||||
extern "C" fn main() {
|
extern "C" fn main() {
|
||||||
let mut snake = Snake::new();
|
let mut snake = Snake::new();
|
||||||
|
let mut commands = [RenderCommand { x: 0, y: 0, w: 0, h: 0, color: 0 }; MAX_RENDER_CMDS];
|
||||||
puts(b"\x1b[2J"); // clear screen
|
|
||||||
puts(b"\x1b[?25l"); // hide cursor
|
|
||||||
|
|
||||||
// Draw border
|
|
||||||
set_color(90); // dark grey
|
|
||||||
move_cursor(0, 0);
|
|
||||||
for _ in 0..GRID_WIDTH + 2 { putchar(b'#'); }
|
|
||||||
for y in 0..GRID_HEIGHT {
|
|
||||||
move_cursor(y + 1, 0);
|
|
||||||
putchar(b'#');
|
|
||||||
move_cursor(y + 1, GRID_WIDTH + 1);
|
|
||||||
putchar(b'#');
|
|
||||||
}
|
|
||||||
move_cursor(GRID_HEIGHT + 1, 0);
|
|
||||||
for _ in 0..GRID_WIDTH + 2 { putchar(b'#'); }
|
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
// Render
|
let count = renderer::render(&snake, &mut commands);
|
||||||
render_game(&snake);
|
for i in 0..count {
|
||||||
|
let cmd = &commands[i];
|
||||||
|
draw_rect(cmd.x, cmd.y, cmd.w, cmd.h, cmd.color);
|
||||||
|
}
|
||||||
|
flush();
|
||||||
|
|
||||||
wait_tick();
|
wait_tick();
|
||||||
handle_input(&mut snake);
|
handle_input(&mut snake);
|
||||||
snake.tick();
|
snake.tick();
|
||||||
|
|
||||||
if snake.state() == GameState::GameOver {
|
if snake.state() == GameState::GameOver {
|
||||||
move_cursor(GRID_HEIGHT / 2, GRID_WIDTH / 2 - 4);
|
let gw = renderer::grid_pixel_width();
|
||||||
set_color(91); // bright red
|
let gh = renderer::grid_pixel_height();
|
||||||
puts(b"GAME OVER");
|
draw_rect(gw / 2 - 60, gh / 2 - 12, 120, 24, 0xFF800000);
|
||||||
set_color(0);
|
flush();
|
||||||
move_cursor(GRID_HEIGHT + 3, 0);
|
|
||||||
puts(b"Score: ");
|
|
||||||
print_dec(snake.score());
|
|
||||||
putchar(b'\n');
|
|
||||||
|
|
||||||
let end = Ticks::from_raw(syscall::get_time().raw() + 50_000_000);
|
let end = Ticks::from_raw(syscall::get_time().raw() + 50_000_000);
|
||||||
while syscall::get_time().raw() < end.raw() { syscall::yield_now(); }
|
while syscall::get_time().raw() < end.raw() { syscall::yield_now(); }
|
||||||
puts(b"\x1b[?25h"); // show cursor
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn render_game(snake: &Snake) {
|
|
||||||
// Clear game area
|
|
||||||
for y in 0..GRID_HEIGHT {
|
|
||||||
move_cursor(y + 1, 1);
|
|
||||||
for _ in 0..GRID_WIDTH { putchar(b' '); }
|
|
||||||
}
|
|
||||||
|
|
||||||
// Draw food
|
|
||||||
let food = snake.food();
|
|
||||||
move_cursor(food.y + 1, food.x + 1);
|
|
||||||
set_color(91); // bright red
|
|
||||||
putchar(b'@');
|
|
||||||
|
|
||||||
// Draw snake body
|
|
||||||
let body = snake.body();
|
|
||||||
for (i, pos) in body.iter().enumerate() {
|
|
||||||
move_cursor(pos.y + 1, pos.x + 1);
|
|
||||||
if i == 0 {
|
|
||||||
set_color(92); // bright green
|
|
||||||
putchar(b'O'); // head
|
|
||||||
} else {
|
|
||||||
set_color(32); // green
|
|
||||||
putchar(b'o'); // body
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Score
|
|
||||||
set_color(0);
|
|
||||||
move_cursor(GRID_HEIGHT + 2, 0);
|
|
||||||
puts(b"Score: ");
|
|
||||||
print_dec(snake.score());
|
|
||||||
puts(b" WASD to move");
|
|
||||||
}
|
|
||||||
|
|
||||||
fn handle_input(snake: &mut Snake) {
|
fn handle_input(snake: &mut Snake) {
|
||||||
let _ = syscall::send(INPUT_EP, 0, 0, 0, 0);
|
let _ = syscall::send(INPUT_EP, 0, 0, 0, 0);
|
||||||
let (tag, kind, _, _) = syscall::receive(INPUT_EP);
|
let (tag, kind, _, _) = syscall::receive(INPUT_EP);
|
||||||
|
|||||||
@@ -10,16 +10,29 @@ extern "C" fn main() {
|
|||||||
let ep = CapSlot::const_new(0);
|
let ep = CapSlot::const_new(0);
|
||||||
|
|
||||||
if let Some(fb) = driver_framebuffer_virtio::VirtioFramebuffer::try_new() {
|
if let Some(fb) = driver_framebuffer_virtio::VirtioFramebuffer::try_new() {
|
||||||
let mut srv = display_server::server::DisplayServer::new(fb);
|
run_virtio(fb, ep);
|
||||||
server_framework::serve(&mut srv, ep);
|
|
||||||
} else {
|
} else {
|
||||||
let fb = driver_framebuffer_mock::MockFramebuffer::new(
|
run_mock(ep);
|
||||||
types::display::Dimension::new(640),
|
|
||||||
types::display::Dimension::new(480),
|
|
||||||
);
|
|
||||||
let mut srv = display_server::server::DisplayServer::new(fb);
|
|
||||||
server_framework::serve(&mut srv, ep);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
loop { userspace::syscall::yield_now(); }
|
loop { userspace::syscall::yield_now(); }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline(never)]
|
||||||
|
fn run_virtio(mut fb: driver_framebuffer_virtio::VirtioFramebuffer, ep: CapSlot) {
|
||||||
|
use port_framebuffer::Framebuffer;
|
||||||
|
fb.clear(types::display::Color::rgb(30, 30, 60));
|
||||||
|
fb.flush();
|
||||||
|
let mut srv = display_server::server::DisplayServer::new(fb);
|
||||||
|
server_framework::serve(&mut srv, ep);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(never)]
|
||||||
|
fn run_mock(ep: CapSlot) {
|
||||||
|
let fb = driver_framebuffer_mock::MockFramebuffer::new(
|
||||||
|
types::display::Dimension::new(640),
|
||||||
|
types::display::Dimension::new(480),
|
||||||
|
);
|
||||||
|
let mut srv = display_server::server::DisplayServer::new(fb);
|
||||||
|
server_framework::serve(&mut srv, ep);
|
||||||
|
}
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ pub fn create_2d(mmio: &impl MmioAccess, queue: &VirtioQueue, dma: &GpuDmaAddrs,
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn attach_backing(mmio: &impl MmioAccess, queue: &VirtioQueue, dma: &GpuDmaAddrs, resource_id: u32, fb_pa: PhysicalAddress, fb_size: u32) -> bool {
|
pub fn attach_backing(mmio: &impl MmioAccess, queue: &VirtioQueue, dma: &GpuDmaAddrs, resource_id: u32, fb_pa: PhysicalAddress, fb_size: u32) -> bool {
|
||||||
write_cmd_header(dma.cmd_va, 0x0104);
|
write_cmd_header(dma.cmd_va, 0x0106);
|
||||||
unsafe {
|
unsafe {
|
||||||
let p = dma.cmd_va.raw() as *mut u8;
|
let p = dma.cmd_va.raw() as *mut u8;
|
||||||
core::ptr::write_volatile(p.add(24) as *mut u32, resource_id);
|
core::ptr::write_volatile(p.add(24) as *mut u32, resource_id);
|
||||||
@@ -60,7 +60,7 @@ pub fn transfer_to_host(mmio: &impl MmioAccess, queue: &VirtioQueue, dma: &GpuDm
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn resource_flush(mmio: &impl MmioAccess, queue: &VirtioQueue, dma: &GpuDmaAddrs, resource_id: u32, x: u32, y: u32, w: u32, h: u32) -> bool {
|
pub fn resource_flush(mmio: &impl MmioAccess, queue: &VirtioQueue, dma: &GpuDmaAddrs, resource_id: u32, x: u32, y: u32, w: u32, h: u32) -> bool {
|
||||||
write_cmd_header(dma.cmd_va, 0x0106);
|
write_cmd_header(dma.cmd_va, 0x0104);
|
||||||
unsafe {
|
unsafe {
|
||||||
let p = dma.cmd_va.raw() as *mut u8;
|
let p = dma.cmd_va.raw() as *mut u8;
|
||||||
core::ptr::write_volatile(p.add(24) as *mut u32, x);
|
core::ptr::write_volatile(p.add(24) as *mut u32, x);
|
||||||
|
|||||||
@@ -165,11 +165,10 @@ pub fn run(shared_buf_va: usize) -> ! {
|
|||||||
debug_print(b")\n");
|
debug_print(b")\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load snake — give it input endpoint (slot 0) for keyboard input
|
|
||||||
// Snake renders via debug_putchar (UART) — no display endpoint needed
|
|
||||||
debug_print(b"init: loading snake... ");
|
debug_print(b"init: loading snake... ");
|
||||||
if let Some(snake) = load_binary(shared_buf, b"snake.elf") {
|
if let Some(snake) = load_binary(shared_buf, b"snake.elf") {
|
||||||
syscall::delegate(INPUT_EP, snake.process_cap, ipc_perms);
|
syscall::delegate(INPUT_EP, snake.process_cap, ipc_perms);
|
||||||
|
syscall::delegate(DISPLAY_EP, snake.process_cap, ipc_perms);
|
||||||
syscall::spawn_thread_in(snake.process_cap, snake.entry_point, USER_STACK_TOP);
|
syscall::spawn_thread_in(snake.process_cap, snake.entry_point, USER_STACK_TOP);
|
||||||
debug_print(b"ok\n");
|
debug_print(b"ok\n");
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
Reference in New Issue
Block a user