#![no_std]
#![no_main]
use core::fmt::Write;
use rpi_hal::halt;
use rpi_hal::mailbox::{ClockId, Mailbox, PixelOrder};
use rpi_hal::pac;
use rpi_hal::uart::Uart;
use rpi_hal::v3d::{self, bcl, rcl, shader_record, texture, uniforms, GpuBuffer, V3d};
const WIDTH_PX: u16 = 512;
const HEIGHT_PX: u16 = 512;
const WIDTH_IN_TILES: u8 = (WIDTH_PX / 64) as u8;
const HEIGHT_IN_TILES: u8 = (HEIGHT_PX / 64) as u8;
const DEPTH_TEST_ENABLED: bool = true;
const DEPTH_STORE_ENABLED: bool = true;
const SINGLE_AXIS_ROTATION: bool = false;
const TILE_STATE_SIZE: usize = bcl::tile_state_size(WIDTH_IN_TILES, HEIGHT_IN_TILES) as usize;
const TILE_ALLOC_SIZE: usize = 16 * 1024 * (WIDTH_IN_TILES as usize * HEIGHT_IN_TILES as usize);
const RCL_SIZE: usize = rcl::size(WIDTH_IN_TILES, HEIGHT_IN_TILES, DEPTH_STORE_ENABLED);
const DEPTH_BUFFER_SIZE: usize = WIDTH_PX as usize * HEIGHT_PX as usize * 4;
const TEXTURE_WIDTH: usize = 4;
const TEXTURE_HEIGHT: usize = 4;
const TEXTURE_SIZE: usize = TEXTURE_WIDTH * TEXTURE_HEIGHT * 4;
#[repr(C, align(8))]
struct Shader<const N: usize>([u8; N]);
static COORD_SHADER: Shader<320> = Shader([
0x00, 0x1a, 0x40, 0x00, 0x67, 0x4c, 0x02, 0xe0, 0x80, 0x7d, 0xc2, 0x15, 0xe7, 0x10, 0x02, 0x10,
0x80, 0x7d, 0xc2, 0x15, 0x27, 0x01, 0x02, 0x10, 0x37, 0x30, 0x80, 0x20, 0xe1, 0x49, 0x00, 0x10,
0x3e, 0x00, 0x12, 0x20, 0xe0, 0x49, 0x00, 0x10, 0x37, 0x32, 0x80, 0x21, 0x62, 0x51, 0x02, 0x10,
0x3e, 0x00, 0x12, 0x20, 0xe3, 0x49, 0x00, 0x10, 0x3e, 0x00, 0x12, 0x20, 0xe1, 0x49, 0x00, 0x10,
0x77, 0x34, 0x80, 0x21, 0xe0, 0x40, 0x02, 0x10, 0xfe, 0x00, 0x12, 0x21, 0x22, 0x51, 0x02, 0x10,
0x37, 0x30, 0x80, 0x20, 0xe3, 0x49, 0x00, 0x10, 0xb6, 0x76, 0xc2, 0x81, 0x22, 0x50, 0x02, 0x10,
0x32, 0x70, 0x82, 0x20, 0xe0, 0x49, 0x00, 0x10, 0x32, 0x5e, 0x80, 0x21, 0x63, 0x40, 0x02, 0x10,
0xf2, 0x4e, 0x80, 0x21, 0xa1, 0x50, 0x02, 0x10, 0x7a, 0x0c, 0x0e, 0x21, 0x62, 0x50, 0x02, 0x10,
0xb6, 0x0e, 0xc0, 0x81, 0xa2, 0x40, 0x02, 0x10, 0x32, 0x70, 0x82, 0x20, 0xe3, 0x49, 0x00, 0x10,
0xf2, 0x2e, 0x80, 0x21, 0xe0, 0x48, 0x02, 0x10, 0xf2, 0x76, 0x82, 0x35, 0x21, 0x4d, 0x02, 0x10,
0x3a, 0x0c, 0x06, 0x21, 0xa2, 0x51, 0x02, 0x10, 0x40, 0x1e, 0x9c, 0x01, 0xa7, 0x01, 0x02, 0x10,
0x9c, 0x7c, 0x0a, 0x21, 0xe0, 0x41, 0x02, 0x10, 0x00, 0x1a, 0x00, 0x00, 0x67, 0x5c, 0x02, 0xe0,
0x00, 0x1e, 0x9e, 0x02, 0x67, 0x08, 0x02, 0xd0, 0xe1, 0x6f, 0x9c, 0x35, 0x05, 0x5c, 0x02, 0x10,
0x80, 0x7d, 0x1a, 0x15, 0x27, 0x0c, 0x02, 0x10, 0x3e, 0x60, 0x80, 0x20, 0xe1, 0x49, 0x00, 0x10,
0x80, 0x7d, 0x1e, 0x15, 0x27, 0x0c, 0x02, 0x10, 0xce, 0x76, 0x16, 0x35, 0x22, 0x4c, 0x02, 0x10,
0xb7, 0x04, 0x1a, 0x27, 0x23, 0x40, 0x12, 0x10, 0x1e, 0x70, 0x16, 0x20, 0xe0, 0x49, 0x00, 0x10,
0x37, 0x00, 0x1e, 0x27, 0x22, 0x40, 0x22, 0x10, 0x16, 0x70, 0x16, 0x20, 0xe3, 0x49, 0x00, 0x10,
0x80, 0x7d, 0x02, 0x15, 0x27, 0x0c, 0x02, 0x10, 0x80, 0x77, 0x82, 0x01, 0x27, 0x0c, 0x02, 0x10,
0x80, 0x7d, 0x16, 0x15, 0x27, 0x0c, 0x02, 0x10, 0x00, 0x70, 0x9e, 0x00, 0xe7, 0x09, 0x00, 0x30,
0x00, 0x70, 0x9e, 0x00, 0xe7, 0x09, 0x00, 0x10, 0x00, 0x70, 0x9e, 0x00, 0xe7, 0x09, 0x00, 0x10,
]);
static VERTEX_SHADER: Shader<328> = Shader([
0x00, 0x1a, 0x60, 0x00, 0x67, 0x4c, 0x02, 0xe0, 0x80, 0x7d, 0xc2, 0x15, 0x27, 0x01, 0x02, 0x10,
0x80, 0x7d, 0xc2, 0x15, 0x27, 0x11, 0x02, 0x10, 0x3e, 0x00, 0x12, 0x20, 0xe0, 0x49, 0x00, 0x10,
0x37, 0x40, 0x80, 0x20, 0xe1, 0x49, 0x00, 0x10, 0x77, 0x40, 0x80, 0x21, 0xa2, 0x51, 0x02, 0x10,
0x3e, 0x00, 0x12, 0x20, 0xe1, 0x49, 0x00, 0x10, 0xb7, 0x42, 0x80, 0x21, 0x63, 0x41, 0x02, 0x10,
0x3e, 0x00, 0x12, 0x20, 0xe2, 0x49, 0x00, 0x10, 0xf7, 0x44, 0x80, 0x21, 0xa0, 0x50, 0x02, 0x10,
0x3e, 0x00, 0x12, 0x20, 0xe3, 0x49, 0x00, 0x10, 0x36, 0x76, 0xc2, 0x81, 0x60, 0x40, 0x02, 0x10,
0x30, 0x70, 0x82, 0x20, 0xe1, 0x49, 0x00, 0x10, 0x70, 0x6e, 0x80, 0x21, 0x62, 0x50, 0x02, 0x10,
0xb8, 0x0c, 0x16, 0x21, 0x63, 0x48, 0x02, 0x10, 0xf0, 0x2e, 0x80, 0x21, 0xe0, 0x40, 0x02, 0x10,
0x00, 0x7c, 0x06, 0x01, 0xe7, 0x10, 0x02, 0x10, 0x80, 0x7d, 0xc2, 0x15, 0xe7, 0x08, 0x02, 0x10,
0x33, 0x70, 0x82, 0x20, 0xe0, 0x49, 0x00, 0x10, 0x33, 0x1e, 0x80, 0x21, 0x22, 0x50, 0x02, 0x10,
0xb3, 0x72, 0x82, 0x21, 0xa1, 0x40, 0x02, 0x10, 0x7f, 0x0c, 0x0c, 0x81, 0x34, 0x48, 0x02, 0x10,
0x33, 0x70, 0x82, 0x20, 0xe3, 0x49, 0x00, 0x10, 0xc6, 0x3e, 0x80, 0x21, 0x60, 0x51, 0x02, 0x10,
0x3c, 0x00, 0x9c, 0x20, 0xe1, 0x49, 0x00, 0x10, 0x40, 0x1e, 0x9e, 0x02, 0xa7, 0x08, 0x02, 0xd0,
0x22, 0x70, 0x9e, 0x20, 0xc6, 0x59, 0x00, 0x10, 0x37, 0x00, 0x0a, 0x20, 0xe2, 0x49, 0x00, 0x10,
0x16, 0x70, 0x1a, 0x20, 0xe3, 0x49, 0x00, 0x10, 0xc6, 0x76, 0x1a, 0x27, 0x21, 0x40, 0x12, 0x10,
0x7e, 0x52, 0x80, 0x27, 0x23, 0x40, 0x22, 0x10, 0x00, 0x1a, 0x00, 0x00, 0x67, 0x5c, 0x02, 0xe0,
0x1e, 0x70, 0x1a, 0x20, 0xe0, 0x49, 0x00, 0x10, 0x80, 0x7d, 0x02, 0x15, 0x27, 0x0c, 0x02, 0x10,
0xf6, 0x01, 0xc2, 0x81, 0x20, 0x4c, 0x02, 0x10, 0x80, 0x7d, 0x1a, 0x15, 0x27, 0x0c, 0x02, 0x10,
0x36, 0x70, 0xc2, 0x95, 0x21, 0x4c, 0x02, 0x10, 0x40, 0x72, 0x9e, 0x15, 0x27, 0x0c, 0x02, 0x10,
0x00, 0x70, 0x9e, 0x00, 0xe7, 0x09, 0x00, 0x30, 0x00, 0x70, 0x9e, 0x00, 0xe7, 0x09, 0x00, 0x10,
0x00, 0x70, 0x9e, 0x00, 0xe7, 0x09, 0x00, 0x10,
]);
static FRAGMENT_SHADER: Shader<128> = Shader([
0x3e, 0x30, 0x3e, 0x20, 0xe0, 0x49, 0x00, 0x10, 0x7e, 0x31, 0x3e, 0x21, 0xe1, 0x48, 0x02, 0x10,
0x40, 0x73, 0x9e, 0x01, 0xa7, 0x08, 0x02, 0x60, 0x80, 0x74, 0x9e, 0x15, 0x67, 0x1e, 0x02, 0x10,
0xc0, 0x76, 0x9e, 0x15, 0x27, 0x1e, 0x02, 0x10, 0x00, 0x70, 0x9e, 0x00, 0xe7, 0x09, 0x00, 0xa0,
0x00, 0x79, 0x9e, 0x04, 0x67, 0x08, 0x02, 0x1d, 0x09, 0x79, 0x9e, 0x84, 0x21, 0x48, 0x42, 0x1b,
0x00, 0x79, 0x9e, 0x84, 0xe1, 0x48, 0x52, 0x19, 0x1b, 0x79, 0x9e, 0x84, 0xa1, 0x48, 0x62, 0x1f,
0x12, 0x70, 0x9e, 0x80, 0xe1, 0x49, 0x70, 0x11, 0xc0, 0xff, 0x9c, 0x15, 0x27, 0x0b, 0x02, 0x10,
0x40, 0x72, 0x9e, 0x15, 0xa7, 0x0b, 0x02, 0x10, 0x00, 0x70, 0x9e, 0x00, 0xe7, 0x09, 0x00, 0x30,
0x00, 0x70, 0x9e, 0x00, 0xe7, 0x09, 0x00, 0x10, 0x00, 0x70, 0x9e, 0x00, 0xe7, 0x09, 0x00, 0x50,
]);
#[repr(C, align(4096))]
struct AlignedBuffer<const N: usize>([u8; N]);
const FACE_COLORS: bool = true;
fn face_texcoord(face: usize) -> (f32, f32) {
let u = ((face % 4) as f32 + 0.5) / TEXTURE_WIDTH as f32;
let v = ((face / 4) as f32 + 0.5) / TEXTURE_HEIGHT as f32;
(u, v)
}
const fn face_palette() -> [u8; TEXTURE_SIZE] {
let mut data = [0u8; TEXTURE_SIZE];
let colors: [[u8; 4]; 6] = [
[0, 0, 255, 255], [0, 255, 0, 255], [255, 0, 0, 255], [0, 255, 255, 255], [255, 0, 255, 255], [255, 255, 0, 255], ];
let mut face = 0;
while face < 6 {
let idx = face * 4;
data[idx] = colors[face][0];
data[idx + 1] = colors[face][1];
data[idx + 2] = colors[face][2];
data[idx + 3] = colors[face][3];
face += 1;
}
data
}
const fn checkerboard() -> [u8; TEXTURE_SIZE] {
const RED: [u8; 4] = [0, 0, 255, 255];
const WHITE: [u8; 4] = [255, 255, 255, 255];
let mut data = [0u8; TEXTURE_SIZE];
let mut y = 0;
while y < TEXTURE_HEIGHT {
let mut x = 0;
while x < TEXTURE_WIDTH {
let color = if (x + y) % 2 == 0 { RED } else { WHITE };
let idx = (y * TEXTURE_WIDTH + x) * 4;
data[idx] = color[0];
data[idx + 1] = color[1];
data[idx + 2] = color[2];
data[idx + 3] = color[3];
x += 1;
}
y += 1;
}
data
}
static TEXTURE_DATA: AlignedBuffer<TEXTURE_SIZE> = AlignedBuffer(if FACE_COLORS {
face_palette()
} else {
checkerboard()
});
#[rustfmt::skip]
const VERTICES: [f32; 24 * 6] = [
1.0, -1.0, -1.0, 1.0, 0.0, 0.0,
1.0, 1.0, -1.0, 1.0, 1.0, 0.0,
1.0, 1.0, 1.0, 1.0, 1.0, 1.0,
1.0, -1.0, 1.0, 1.0, 0.0, 1.0,
-1.0, -1.0, 1.0, 1.0, 0.0, 0.0,
-1.0, 1.0, 1.0, 1.0, 1.0, 0.0,
-1.0, 1.0, -1.0, 1.0, 1.0, 1.0,
-1.0, -1.0, -1.0, 1.0, 0.0, 1.0,
-1.0, 1.0, -1.0, 1.0, 0.0, 0.0,
-1.0, 1.0, 1.0, 1.0, 1.0, 0.0,
1.0, 1.0, 1.0, 1.0, 1.0, 1.0,
1.0, 1.0, -1.0, 1.0, 0.0, 1.0,
-1.0, -1.0, 1.0, 1.0, 0.0, 0.0,
-1.0, -1.0, -1.0, 1.0, 1.0, 0.0,
1.0, -1.0, -1.0, 1.0, 1.0, 1.0,
1.0, -1.0, 1.0, 1.0, 0.0, 1.0,
-1.0, -1.0, 1.0, 1.0, 0.0, 0.0,
1.0, -1.0, 1.0, 1.0, 1.0, 0.0,
1.0, 1.0, 1.0, 1.0, 1.0, 1.0,
-1.0, 1.0, 1.0, 1.0, 0.0, 1.0,
1.0, -1.0, -1.0, 1.0, 0.0, 0.0,
-1.0, -1.0, -1.0, 1.0, 1.0, 0.0,
-1.0, 1.0, -1.0, 1.0, 1.0, 1.0,
1.0, 1.0, -1.0, 1.0, 0.0, 1.0,
];
#[rustfmt::skip]
const INDICES: [u16; 36] = [
0, 1, 2, 0, 2, 3, 4, 5, 6, 4, 6, 7, 8, 9, 10, 8, 10, 11, 12, 13, 14, 12, 14, 15, 16, 17, 18, 16, 18, 19, 20, 21, 22, 20, 22, 23, ];
mod math {
pub type Mat4 = [f32; 16];
pub fn multiply(a: &Mat4, b: &Mat4) -> Mat4 {
let mut r = [0.0; 16];
for col in 0..4 {
for row in 0..4 {
let mut sum = 0.0;
for k in 0..4 {
sum += a[k * 4 + row] * b[col * 4 + k];
}
r[col * 4 + row] = sum;
}
}
r
}
pub fn rotation_xy(angle_x: f32, angle_y: f32) -> Mat4 {
let (sx, cx) = (libm::sinf(angle_x), libm::cosf(angle_x));
let (sy, cy) = (libm::sinf(angle_y), libm::cosf(angle_y));
#[rustfmt::skip]
let rotate_x: Mat4 = [
1.0, 0.0, 0.0, 0.0,
0.0, cx, sx, 0.0,
0.0, -sx, cx, 0.0,
0.0, 0.0, 0.0, 1.0,
];
#[rustfmt::skip]
let rotate_y: Mat4 = [
cy, 0.0, -sy, 0.0,
0.0, 1.0, 0.0, 0.0,
sy, 0.0, cy, 0.0,
0.0, 0.0, 0.0, 1.0,
];
multiply(&rotate_y, &rotate_x)
}
pub fn rotation_z(angle: f32) -> Mat4 {
let (s, c) = (libm::sinf(angle), libm::cosf(angle));
#[rustfmt::skip]
let m: Mat4 = [
c, s, 0.0, 0.0,
-s, c, 0.0, 0.0,
0.0, 0.0, 1.0, 0.0,
0.0, 0.0, 0.0, 1.0,
];
m
}
pub fn translation(x: f32, y: f32, z: f32) -> Mat4 {
#[rustfmt::skip]
let m: Mat4 = [
1.0, 0.0, 0.0, 0.0,
0.0, 1.0, 0.0, 0.0,
0.0, 0.0, 1.0, 0.0,
x, y, z, 1.0,
];
m
}
pub fn perspective(fovy_radians: f32, aspect: f32, near: f32, far: f32) -> Mat4 {
let f = 1.0 / libm::tanf(fovy_radians / 2.0);
#[rustfmt::skip]
let m: Mat4 = [
f / aspect, 0.0, 0.0, 0.0,
0.0, f, 0.0, 0.0,
0.0, 0.0, (far + near) / (near - far), -1.0,
0.0, 0.0, (2.0 * far * near) / (near - far), 0.0,
];
m
}
}
const CL_END_SLACK: usize = 32;
static mut BCL_BUF: GpuBuffer<{ bcl::SIZE + CL_END_SLACK }> = GpuBuffer::new();
static mut RCL_BUF: GpuBuffer<{ RCL_SIZE + CL_END_SLACK }> = GpuBuffer::new();
static mut TILE_STATE_BUF: GpuBuffer<TILE_STATE_SIZE> = GpuBuffer::new();
static mut TILE_ALLOC_BUF: GpuBuffer<TILE_ALLOC_SIZE> = GpuBuffer::new();
static mut DEPTH_BUF: GpuBuffer<DEPTH_BUFFER_SIZE> = GpuBuffer::new();
static mut SHADER_RECORD_BUF: GpuBuffer<52> = GpuBuffer::new();
static mut VERTEX_BUF: GpuBuffer<{ VERTICES.len() * 4 }> = GpuBuffer::new();
static mut INDEX_BUF: GpuBuffer<{ INDICES.len() * 2 }> = GpuBuffer::new();
static mut COORD_UNIFORMS_BUF: GpuBuffer<{ uniforms::UNIFORM_COUNT * 4 }> = GpuBuffer::new();
static mut VERTEX_UNIFORMS_BUF: GpuBuffer<{ uniforms::UNIFORM_COUNT * 4 }> = GpuBuffer::new();
static mut FRAGMENT_UNIFORMS_BUF: GpuBuffer<8> = GpuBuffer::new();
#[panic_handler]
fn panic(info: &core::panic::PanicInfo) -> ! {
let peripherals = unsafe { pac::Peripherals::steal() };
let mut uart = Uart::init(&peripherals.GPIO, peripherals.UART0);
let _ = writeln!(uart, "PANIC: {info}");
halt();
}
#[no_mangle]
pub extern "C" fn kmain() -> ! {
let peripherals = unsafe { pac::Peripherals::steal() };
let mut uart = Uart::init(&peripherals.GPIO, peripherals.UART0);
let mut mailbox = Mailbox::new(peripherals.VCMAILBOX);
match mailbox.clock_rate_hz(ClockId::V3d) {
Ok(hz) => {
let _ = writeln!(uart, "v3d clock before: {hz} Hz");
}
Err(e) => {
let _ = writeln!(uart, "v3d clock query failed: {e:?}");
}
}
match mailbox.set_clock_rate_hz(ClockId::V3d, 250_000_000) {
Ok(hz) => {
let _ = writeln!(uart, "v3d clock set to: {hz} Hz");
}
Err(e) => {
let _ = writeln!(uart, "v3d clock set failed: {e:?}");
}
}
match mailbox.set_enable_qpu(true) {
Ok(()) => {
let _ = writeln!(uart, "set_enable_qpu(true): ok");
}
Err(e) => {
let _ = writeln!(uart, "set_enable_qpu(true): error {e:?}");
}
}
let v3d = unsafe { V3d::new() };
let (ident0, ident1, ident2) = v3d.ident();
let _ = writeln!(
uart,
"V3D IDENT: 0x{ident0:08x} 0x{ident1:08x} 0x{ident2:08x}"
);
let framebuffer = match mailbox.allocate_framebuffer(
u32::from(WIDTH_PX),
u32::from(HEIGHT_PX),
32,
PixelOrder::Rgb,
) {
Ok(fb) => fb,
Err(e) => {
let _ = writeln!(uart, "framebuffer allocation failed: {e:?}");
halt();
}
};
let _ = writeln!(
uart,
"framebuffer: {}x{} @ 0x{:08x}, pitch {}",
framebuffer.width, framebuffer.height, framebuffer.address, framebuffer.pitch_bytes
);
assert_eq!(
framebuffer.pitch_bytes,
framebuffer.width * 4,
"framebuffer pitch has padding V3D's fixed-pitch render target can't handle"
);
let (
bcl_buf,
rcl_buf,
tile_state_buf,
tile_alloc_buf,
depth_buf,
shader_record_buf,
vertex_buf,
index_buf,
coord_uniforms_buf,
vertex_uniforms_buf,
fragment_uniforms_buf,
) = unsafe {
(
&mut *core::ptr::addr_of_mut!(BCL_BUF),
&mut *core::ptr::addr_of_mut!(RCL_BUF),
&mut *core::ptr::addr_of_mut!(TILE_STATE_BUF),
&mut *core::ptr::addr_of_mut!(TILE_ALLOC_BUF),
&mut *core::ptr::addr_of_mut!(DEPTH_BUF),
&mut *core::ptr::addr_of_mut!(SHADER_RECORD_BUF),
&mut *core::ptr::addr_of_mut!(VERTEX_BUF),
&mut *core::ptr::addr_of_mut!(INDEX_BUF),
&mut *core::ptr::addr_of_mut!(COORD_UNIFORMS_BUF),
&mut *core::ptr::addr_of_mut!(VERTEX_UNIFORMS_BUF),
&mut *core::ptr::addr_of_mut!(FRAGMENT_UNIFORMS_BUF),
)
};
for (i, value) in VERTICES.iter().enumerate() {
let vertex = i / 6;
let component = i % 6;
let value = if FACE_COLORS && component >= 4 {
let (u, v) = face_texcoord(vertex / 4);
if component == 4 {
u
} else {
v
}
} else {
*value
};
vertex_buf.as_bytes_mut()[i * 4..i * 4 + 4].copy_from_slice(&value.to_le_bytes());
}
vertex_buf.flush();
for (i, value) in INDICES.iter().enumerate() {
index_buf.as_bytes_mut()[i * 2..i * 2 + 2].copy_from_slice(&value.to_le_bytes());
}
index_buf.flush();
tile_state_buf.flush();
tile_alloc_buf.flush();
v3d::flush(COORD_SHADER.0.as_ptr() as u32, COORD_SHADER.0.len());
v3d::flush(VERTEX_SHADER.0.as_ptr() as u32, VERTEX_SHADER.0.len());
v3d::flush(FRAGMENT_SHADER.0.as_ptr() as u32, FRAGMENT_SHADER.0.len());
v3d::flush(TEXTURE_DATA.0.as_ptr() as u32, TEXTURE_DATA.0.len());
texture::build_fragment_shader_uniforms(
fragment_uniforms_buf.as_bytes_mut(),
&texture::TextureParams {
address: v3d::bus_address(TEXTURE_DATA.0.as_ptr() as u32),
width_px: TEXTURE_WIDTH as u16,
height_px: TEXTURE_HEIGHT as u16,
},
);
fragment_uniforms_buf.flush();
shader_record::build(
shader_record_buf.as_bytes_mut(),
&shader_record::ShaderRecordParams {
fragment_shader_code_address: v3d::bus_address(FRAGMENT_SHADER.0.as_ptr() as u32),
fragment_shader_uniforms_address: fragment_uniforms_buf.bus_address(),
fragment_shader_num_varyings: 2,
vertex_shader_code_address: v3d::bus_address(VERTEX_SHADER.0.as_ptr() as u32),
vertex_shader_uniforms_address: vertex_uniforms_buf.bus_address(),
coordinate_shader_code_address: v3d::bus_address(COORD_SHADER.0.as_ptr() as u32),
coordinate_shader_uniforms_address: coord_uniforms_buf.bus_address(),
vertex_buffer_address: vertex_buf.bus_address(),
},
);
shader_record_buf.flush();
let bcl_len = bcl::build(
bcl_buf.as_bytes_mut(),
&bcl::BclParams {
tile_alloc_address: tile_alloc_buf.bus_address(),
tile_alloc_size: TILE_ALLOC_SIZE as u32,
tile_state_address: tile_state_buf.bus_address(),
width_in_tiles: WIDTH_IN_TILES,
height_in_tiles: HEIGHT_IN_TILES,
width_px: WIDTH_PX,
height_px: HEIGHT_PX,
shader_state_address: shader_record_buf.bus_address(),
attribute_array_count: 2,
index_buffer_address: index_buf.bus_address(),
index_count: INDICES.len() as u32,
max_index: (VERTICES.len() / 6 - 1) as u16,
depth_test_enabled: DEPTH_TEST_ENABLED,
},
);
bcl_buf.flush();
let rcl_len = rcl::build(
rcl_buf.as_bytes_mut(),
&rcl::RclParams {
tile_alloc_address: tile_alloc_buf.bus_address(),
width_in_tiles: WIDTH_IN_TILES,
height_in_tiles: HEIGHT_IN_TILES,
color_write_address: v3d::bus_address(framebuffer.address),
depth_write_address: DEPTH_STORE_ENABLED.then(|| depth_buf.bus_address()),
width_px: WIDTH_PX,
height_px: HEIGHT_PX,
clear_color_rgba8888: 0xff80_4020,
},
);
rcl_buf.flush();
let bcl_range = (
bcl_buf.bus_address(),
bcl_buf.bus_address() + bcl_len as u32,
);
let rcl_range = (
rcl_buf.bus_address(),
rcl_buf.bus_address() + rcl_len as u32,
);
let projection = math::perspective(1.0, f32::from(WIDTH_PX) / f32::from(HEIGHT_PX), 0.1, 100.0);
let mut angle = 0.0f32;
let mut frame = 0u32;
loop {
let model = if SINGLE_AXIS_ROTATION {
math::rotation_xy(0.0, angle)
} else {
math::multiply(
&math::rotation_z(angle * 0.43),
&math::rotation_xy(angle, angle * 0.7),
)
};
let view = math::translation(0.0, 0.0, -4.0);
let mvp = math::multiply(&projection, &math::multiply(&view, &model));
uniforms::build_coordinate_shader_uniforms(
coord_uniforms_buf.as_bytes_mut(),
&mvp,
WIDTH_PX,
HEIGHT_PX,
);
coord_uniforms_buf.flush();
uniforms::build_vertex_shader_uniforms(
vertex_uniforms_buf.as_bytes_mut(),
&mvp,
WIDTH_PX,
HEIGHT_PX,
);
vertex_uniforms_buf.flush();
match v3d.submit_bin(bcl_range) {
Ok(()) => {
if v3d.debug_status().pcs & (1 << 8) != 0 {
let _ = writeln!(
uart,
"frame {frame}: binner out of memory -- tile allocation pool ({TILE_ALLOC_SIZE} bytes) too small"
);
}
}
Err(e) => {
let status = v3d.debug_status();
let _ = writeln!(uart, "frame {frame}: submit_bin failed: {e:?}");
let _ = writeln!(
uart,
" ct0cs=0x{:08x} pcs=0x{:08x} bfc=0x{:08x} ct0pc=0x{:08x} errstat=0x{:08x}",
status.ct0cs, status.pcs, status.bfc, status.ct0pc, status.errstat
);
}
}
match v3d.submit_render(rcl_range) {
Ok(()) => {}
Err(e) => {
let status = v3d.debug_status();
let _ = writeln!(uart, "frame {frame}: submit_render failed: {e:?}");
let _ = writeln!(
uart,
" ct1cs=0x{:08x} pcs=0x{:08x} rfc=0x{:08x} errstat=0x{:08x}",
status.ct1cs, status.pcs, status.rfc, status.errstat
);
}
}
if frame.is_multiple_of(60) {
let _ = writeln!(uart, "frame {frame}, angle {angle}");
}
angle += 0.008;
frame = frame.wrapping_add(1);
delay(2_000_000);
}
}
fn delay(cycles: u32) {
for _ in 0..cycles {
unsafe { core::arch::asm!("nop") };
}
}