pub struct BclParams {
pub tile_alloc_address: u32,
pub tile_alloc_size: u32,
pub tile_state_address: u32,
pub width_in_tiles: u8,
pub height_in_tiles: u8,
pub width_px: u16,
pub height_px: u16,
pub shader_state_address: u32,
pub attribute_array_count: u8,
pub index_buffer_address: u32,
pub index_count: u32,
pub max_index: u16,
pub depth_test_enabled: bool,
}
pub const fn tile_state_size(width_in_tiles: u8, height_in_tiles: u8) -> u32 {
let tile_count = width_in_tiles as u32 * height_in_tiles as u32;
(48 * tile_count).next_multiple_of(4096)
}
pub const SIZE: usize = 96;
pub fn build(cl: &mut [u8], params: &BclParams) -> usize {
let mut b = Builder { bytes: cl, len: 0 };
b.tile_binning_mode_configuration(params);
b.start_tile_binning();
b.primitive_list_format();
b.clip_window(params);
b.configuration_bits(params);
b.depth_offset();
b.point_size();
b.line_width();
b.clipper_xy_scaling(params);
b.clipper_z_scale_and_offset();
b.viewport_offset(params);
b.flat_shade_flags();
b.gl_shader_state(params);
b.indexed_primitive_list(params);
b.increment_semaphore();
b.flush();
b.len
}
struct Builder<'a> {
bytes: &'a mut [u8],
len: usize,
}
impl<'a> Builder<'a> {
fn push_u8(&mut self, v: u8) {
self.bytes[self.len] = v;
self.len += 1;
}
fn push_u16(&mut self, v: u16) {
self.bytes[self.len..self.len + 2].copy_from_slice(&v.to_le_bytes());
self.len += 2;
}
fn push_u32(&mut self, v: u32) {
self.bytes[self.len..self.len + 4].copy_from_slice(&v.to_le_bytes());
self.len += 4;
}
fn push_f32(&mut self, v: f32) {
self.push_u32(v.to_bits());
}
fn tile_binning_mode_configuration(&mut self, params: &BclParams) {
self.push_u8(0x70);
self.push_u32(params.tile_alloc_address);
self.push_u32(params.tile_alloc_size);
self.push_u32(params.tile_state_address);
self.push_u8(params.width_in_tiles);
self.push_u8(params.height_in_tiles);
self.push_u8(0x44); }
fn start_tile_binning(&mut self) {
self.push_u8(0x06);
}
fn primitive_list_format(&mut self) {
self.push_u8(0x38);
self.push_u8(0x12);
}
fn clip_window(&mut self, params: &BclParams) {
self.push_u8(0x66);
self.push_u16(0); self.push_u16(0); self.push_u16(params.width_px);
self.push_u16(params.height_px);
}
fn configuration_bits(&mut self, params: &BclParams) {
self.push_u8(0x60);
self.push_u8(0x07);
if params.depth_test_enabled {
self.push_u8(0x90);
self.push_u8(0x03);
} else {
self.push_u8(0x70);
self.push_u8(0x02);
}
}
fn depth_offset(&mut self) {
self.push_u8(0x65);
self.push_u16(0); self.push_u16(0); }
fn point_size(&mut self) {
self.push_u8(0x62);
self.push_f32(1.0);
}
fn line_width(&mut self) {
self.push_u8(0x63);
self.push_f32(1.0);
}
fn clipper_xy_scaling(&mut self, params: &BclParams) {
self.push_u8(0x69);
self.push_f32(f32::from(params.width_px) / 2.0 * 16.0);
self.push_f32(-(f32::from(params.height_px) / 2.0 * 16.0));
}
fn clipper_z_scale_and_offset(&mut self) {
self.push_u8(0x6a);
self.push_f32(0.5); self.push_f32(0.5); }
fn viewport_offset(&mut self, params: &BclParams) {
self.push_u8(0x67);
self.push_u16((f32::from(params.width_px) / 2.0 * 16.0) as u16);
self.push_u16((f32::from(params.height_px) / 2.0 * 16.0) as u16);
}
fn flat_shade_flags(&mut self) {
self.push_u8(0x61);
self.push_u32(0);
}
fn gl_shader_state(&mut self, params: &BclParams) {
debug_assert_eq!(
params.shader_state_address & 0xf,
0,
"shader-state record must be 16-byte aligned"
);
debug_assert!(
(1..=8).contains(¶ms.attribute_array_count),
"attribute array count must be 1-8"
);
self.push_u8(0x40);
let word =
(params.shader_state_address & !0xf) | u32::from(params.attribute_array_count & 0x7);
self.push_u32(word);
}
fn indexed_primitive_list(&mut self, params: &BclParams) {
self.push_u8(0x20);
self.push_u8(0x14);
self.push_u32(params.index_count);
self.push_u32(params.index_buffer_address);
self.push_u32(u32::from(params.max_index));
}
fn increment_semaphore(&mut self) {
self.push_u8(0x07);
}
fn flush(&mut self) {
self.push_u8(0x04);
}
}