Browse Source

Use the lifetime of the input VBO data

We can return a "handle" that stores the index + size of the VBO data,
as well as keeping a phantom borrow on the original data. By requiring
this handle as input during the call to draw arrays, we ensure the VBO
data lives long enough for the draw call.
pull/16/head
Ian Chamberlain 2 years ago
parent
commit
ec91f7d2fe
No known key found for this signature in database
GPG Key ID: AE5484D09405AA60
  1. 104
      citro3d/examples/triangle.rs
  2. 50
      citro3d/src/buffers.rs
  3. 12
      citro3d/src/render.rs

104
citro3d/examples/triangle.rs

@ -1,7 +1,7 @@
#![feature(allocator_api)] #![feature(allocator_api)]
use citro3d::attrib::{self, AttrInfo}; use citro3d::attrib::{self, AttrInfo};
use citro3d::buffers::BufInfo; use citro3d::buffers::{self, BufInfo};
use citro3d::render::{ClearFlags, Target}; use citro3d::render::{ClearFlags, Target};
use citro3d::{include_aligned_bytes, shader}; use citro3d::{include_aligned_bytes, shader};
use citro3d_sys::C3D_Mtx; use citro3d_sys::C3D_Mtx;
@ -81,8 +81,9 @@ fn main() {
let mut vbo_data = Vec::with_capacity_in(VERTICES.len(), ctru::linear::LinearAllocator); let mut vbo_data = Vec::with_capacity_in(VERTICES.len(), ctru::linear::LinearAllocator);
vbo_data.extend_from_slice(VERTICES); vbo_data.extend_from_slice(VERTICES);
let vbo_idx = prepare_vbos(&vbo_data);
let (uloc_projection, projection) = scene_init(&mut program, &vbo_data); let (uloc_projection, projection) = scene_init(&mut program);
while apt.main_loop() { while apt.main_loop() {
hid.scan_input(); hid.scan_input();
@ -99,7 +100,17 @@ fn main() {
let clear_color: u32 = 0x7F_7F_7F_FF; let clear_color: u32 = 0x7F_7F_7F_FF;
target.clear(ClearFlags::ALL, clear_color, 0); target.clear(ClearFlags::ALL, clear_color, 0);
scene_render(uloc_projection.into(), &projection);
unsafe {
// Update the uniforms
citro3d_sys::C3D_FVUnifMtx4x4(
ctru_sys::GPU_VERTEX_SHADER,
uloc_projection.into(),
&projection,
);
}
target.draw_arrays(buffers::Primitive::Triangles, vbo_idx);
}); });
}; };
@ -108,50 +119,46 @@ fn main() {
} }
} }
fn scene_init(program: &mut shader::Program, vbo_data: &[Vertex]) -> (i8, C3D_Mtx) { fn prepare_vbos(vbo_data: &[Vertex]) -> buffers::Index {
// Load the vertex shader, create a shader program and bind it // Configure attributes for use with the vertex shader
unsafe { let mut attr_info = AttrInfo::get_mut().expect("failed to get global attr info");
citro3d_sys::C3D_BindProgram(program.as_raw());
// Get the location of the uniforms let reg0 = attrib::Register::new(0).unwrap();
let projection_name = CStr::from_bytes_with_nul(b"projection\0").unwrap(); let reg1 = attrib::Register::new(1).unwrap();
let uloc_projection = ctru_sys::shaderInstanceGetUniformLocation(
(*program.as_raw()).vertexShader,
projection_name.as_ptr(),
);
// Configure attributes for use with the vertex shader // The default permutation would actually already be what we want if we
let mut attr_info = AttrInfo::get_mut().expect("failed to get global attr info"); // inserted position, then color, but just show that it's customizable
// by swapping the order then using `set_permutation`.
let reg0 = attrib::Register::new(0).unwrap(); let color_attr = attr_info
let reg1 = attrib::Register::new(1).unwrap(); .add_loader(reg0, attrib::Format::Float, 3)
.unwrap();
// The default permutation would actually already be what we want if we let position_attr = attr_info
// inserted position, then color, but just show that it's customizable .add_loader(reg1, attrib::Format::Float, 3)
// by swapping the order then using `set_permutation`. .unwrap();
let color_attr = attr_info attr_info
.add_loader(reg0, attrib::Format::Float, 3) .set_permutation(&[position_attr, color_attr])
.unwrap(); .unwrap();
let position_attr = attr_info // Configure buffers
.add_loader(reg1, attrib::Format::Float, 3) let mut buf_info = BufInfo::get_mut().unwrap();
.unwrap(); let buf_idx = buf_info.add(vbo_data, &attr_info).unwrap();
eprintln!( buf_idx
"count {} permutation {:#x}", }
attr_info.count(),
attr_info.permutation()
);
attr_info fn scene_init(program: &mut shader::Program) -> (i8, C3D_Mtx) {
.set_permutation(&[position_attr, color_attr]) // Load the vertex shader, create a shader program and bind it
.unwrap(); unsafe {
citro3d_sys::C3D_BindProgram(program.as_raw());
eprintln!( // Get the location of the uniforms
"count {} permutation {:#x}", let projection_name = CStr::from_bytes_with_nul(b"projection\0").unwrap();
attr_info.count(), let uloc_projection = ctru_sys::shaderInstanceGetUniformLocation(
attr_info.permutation() (*program.as_raw()).vertexShader,
projection_name.as_ptr(),
); );
// Compute the projection matrix // Compute the projection matrix
@ -171,10 +178,6 @@ fn scene_init(program: &mut shader::Program, vbo_data: &[Vertex]) -> (i8, C3D_Mt
projection.assume_init() projection.assume_init()
}; };
// Configure buffers
let mut buf_info = BufInfo::get_mut().unwrap();
buf_info.add(vbo_data, &attr_info).unwrap();
// Configure the first fragment shading substage to just pass through the vertex color // Configure the first fragment shading substage to just pass through the vertex color
// See https://www.opengl.org/sdk/docs/man2/xhtml/glTexEnv.xml for more insight // See https://www.opengl.org/sdk/docs/man2/xhtml/glTexEnv.xml for more insight
let env = citro3d_sys::C3D_GetTexEnv(0); let env = citro3d_sys::C3D_GetTexEnv(0);
@ -191,20 +194,3 @@ fn scene_init(program: &mut shader::Program, vbo_data: &[Vertex]) -> (i8, C3D_Mt
(uloc_projection, projection) (uloc_projection, projection)
} }
} }
fn scene_render(uloc_projection: i32, projection: &C3D_Mtx) {
unsafe {
// Update the uniforms
citro3d_sys::C3D_FVUnifMtx4x4(ctru_sys::GPU_VERTEX_SHADER, uloc_projection, projection);
// Draw the VBO
citro3d_sys::C3D_DrawArrays(
ctru_sys::GPU_TRIANGLES,
0,
VERTICES
.len()
.try_into()
.expect("VERTICES.len() fits in i32"),
);
}
}

50
citro3d/src/buffers.rs

@ -1,3 +1,4 @@
use std::marker::PhantomData;
use std::ops::{Deref, DerefMut}; use std::ops::{Deref, DerefMut};
use std::sync::{LazyLock, RwLock}; use std::sync::{LazyLock, RwLock};
@ -22,6 +23,33 @@ pub struct BufInfo {
unsafe impl Sync for BufInfo {} unsafe impl Sync for BufInfo {}
unsafe impl Send for BufInfo {} unsafe impl Send for BufInfo {}
// TODO: is this a good name? It's more like a "handle" to the VBO data, or a slice.
#[derive(Debug, Clone, Copy)]
pub struct Index<'vbo> {
index: libc::c_int,
size: libc::c_int,
_data: PhantomData<&'vbo ()>,
}
impl Index<'_> {
pub fn as_raw(&self) -> libc::c_int {
self.index
}
pub fn size(&self) -> libc::c_int {
self.size
}
}
#[repr(u32)]
#[derive(Debug, Clone, Copy)]
pub enum Primitive {
Triangles = ctru_sys::GPU_TRIANGLES,
TriangleStrip = ctru_sys::GPU_TRIANGLE_STRIP,
TriangleFan = ctru_sys::GPU_TRIANGLE_FAN,
GeometryPrim = ctru_sys::GPU_GEOMETRY_PRIM,
}
impl BufInfo { impl BufInfo {
/// Get a reference to the global buffer info. /// Get a reference to the global buffer info.
pub fn get() -> crate::Result<impl Deref<Target = Self>> { pub fn get() -> crate::Result<impl Deref<Target = Self>> {
@ -33,12 +61,16 @@ impl BufInfo {
Ok(BUF_INFO.try_write()?) Ok(BUF_INFO.try_write()?)
} }
pub fn add<T>(&mut self, vbo_data: &[T], attrib_info: &attrib::AttrInfo) -> crate::Result<()> { pub fn add<'vbo, T>(
&mut self,
vbo_data: &'vbo [T],
attrib_info: &attrib::AttrInfo,
) -> crate::Result<Index<'vbo>> {
let stride = std::mem::size_of::<T>().try_into()?; let stride = std::mem::size_of::<T>().try_into()?;
let attrib_count = attrib_info.count(); let attrib_count = attrib_info.count();
let permutation = attrib_info.permutation(); let permutation = attrib_info.permutation();
unsafe { let res = unsafe {
citro3d_sys::BufInfo_Add( citro3d_sys::BufInfo_Add(
self.raw, self.raw,
// TODO: figure out how the hell to encode the lifetime of this // TODO: figure out how the hell to encode the lifetime of this
@ -47,9 +79,17 @@ impl BufInfo {
stride, stride,
attrib_count, attrib_count,
permutation, permutation,
); )
} };
Ok(()) if res < 0 {
Err(crate::Error::System(res))
} else {
Ok(Index {
index: res,
size: vbo_data.len().try_into()?,
_data: PhantomData,
})
}
} }
} }

12
citro3d/src/render.rs

@ -10,7 +10,7 @@ use ctru::gfx::Screen;
use ctru::services::gspgpu::FramebufferFormat; use ctru::services::gspgpu::FramebufferFormat;
use ctru_sys::{GPU_COLORBUF, GPU_DEPTHBUF}; use ctru_sys::{GPU_COLORBUF, GPU_DEPTHBUF};
use crate::{Error, Result}; use crate::{buffers, Error, Result};
mod transfer; mod transfer;
@ -91,6 +91,16 @@ impl<'screen> Target<'screen> {
pub(crate) fn as_raw(&self) -> *mut C3D_RenderTarget { pub(crate) fn as_raw(&self) -> *mut C3D_RenderTarget {
self.raw self.raw
} }
pub fn draw_arrays(&mut self, primitive: buffers::Primitive, index: buffers::Index) {
unsafe {
citro3d_sys::C3D_DrawArrays(
primitive as ctru_sys::GPU_Primitive_t,
index.as_raw(),
index.size(),
);
}
}
} }
bitflags::bitflags! { bitflags::bitflags! {

Loading…
Cancel
Save