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 @@ @@ -1,7 +1,7 @@
#![feature(allocator_api)]
use citro3d::attrib::{self, AttrInfo};
use citro3d::buffers::BufInfo;
use citro3d::buffers::{self, BufInfo};
use citro3d::render::{ClearFlags, Target};
use citro3d::{include_aligned_bytes, shader};
use citro3d_sys::C3D_Mtx;
@ -81,8 +81,9 @@ fn main() { @@ -81,8 +81,9 @@ fn main() {
let mut vbo_data = Vec::with_capacity_in(VERTICES.len(), ctru::linear::LinearAllocator);
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() {
hid.scan_input();
@ -99,7 +100,17 @@ fn main() { @@ -99,7 +100,17 @@ fn main() {
let clear_color: u32 = 0x7F_7F_7F_FF;
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() { @@ -108,50 +119,46 @@ fn main() {
}
}
fn scene_init(program: &mut shader::Program, vbo_data: &[Vertex]) -> (i8, C3D_Mtx) {
// Load the vertex shader, create a shader program and bind it
unsafe {
citro3d_sys::C3D_BindProgram(program.as_raw());
fn prepare_vbos(vbo_data: &[Vertex]) -> buffers::Index {
// Configure attributes for use with the vertex shader
let mut attr_info = AttrInfo::get_mut().expect("failed to get global attr info");
// Get the location of the uniforms
let projection_name = CStr::from_bytes_with_nul(b"projection\0").unwrap();
let uloc_projection = ctru_sys::shaderInstanceGetUniformLocation(
(*program.as_raw()).vertexShader,
projection_name.as_ptr(),
);
let reg0 = attrib::Register::new(0).unwrap();
let reg1 = attrib::Register::new(1).unwrap();
// Configure attributes for use with the vertex shader
let mut attr_info = AttrInfo::get_mut().expect("failed to get global attr info");
// The default permutation would actually already be what we want if we
// 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 reg1 = attrib::Register::new(1).unwrap();
let color_attr = attr_info
.add_loader(reg0, attrib::Format::Float, 3)
.unwrap();
// The default permutation would actually already be what we want if we
// inserted position, then color, but just show that it's customizable
// by swapping the order then using `set_permutation`.
let position_attr = attr_info
.add_loader(reg1, attrib::Format::Float, 3)
.unwrap();
let color_attr = attr_info
.add_loader(reg0, attrib::Format::Float, 3)
.unwrap();
attr_info
.set_permutation(&[position_attr, color_attr])
.unwrap();
let position_attr = attr_info
.add_loader(reg1, attrib::Format::Float, 3)
.unwrap();
// Configure buffers
let mut buf_info = BufInfo::get_mut().unwrap();
let buf_idx = buf_info.add(vbo_data, &attr_info).unwrap();
eprintln!(
"count {} permutation {:#x}",
attr_info.count(),
attr_info.permutation()
);
buf_idx
}
attr_info
.set_permutation(&[position_attr, color_attr])
.unwrap();
fn scene_init(program: &mut shader::Program) -> (i8, C3D_Mtx) {
// Load the vertex shader, create a shader program and bind it
unsafe {
citro3d_sys::C3D_BindProgram(program.as_raw());
eprintln!(
"count {} permutation {:#x}",
attr_info.count(),
attr_info.permutation()
// Get the location of the uniforms
let projection_name = CStr::from_bytes_with_nul(b"projection\0").unwrap();
let uloc_projection = ctru_sys::shaderInstanceGetUniformLocation(
(*program.as_raw()).vertexShader,
projection_name.as_ptr(),
);
// Compute the projection matrix
@ -171,10 +178,6 @@ fn scene_init(program: &mut shader::Program, vbo_data: &[Vertex]) -> (i8, C3D_Mt @@ -171,10 +178,6 @@ fn scene_init(program: &mut shader::Program, vbo_data: &[Vertex]) -> (i8, C3D_Mt
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
// See https://www.opengl.org/sdk/docs/man2/xhtml/glTexEnv.xml for more insight
let env = citro3d_sys::C3D_GetTexEnv(0);
@ -191,20 +194,3 @@ fn scene_init(program: &mut shader::Program, vbo_data: &[Vertex]) -> (i8, C3D_Mt @@ -191,20 +194,3 @@ fn scene_init(program: &mut shader::Program, vbo_data: &[Vertex]) -> (i8, C3D_Mt
(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 @@ @@ -1,3 +1,4 @@
use std::marker::PhantomData;
use std::ops::{Deref, DerefMut};
use std::sync::{LazyLock, RwLock};
@ -22,6 +23,33 @@ pub struct BufInfo { @@ -22,6 +23,33 @@ pub struct BufInfo {
unsafe impl Sync 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 {
/// Get a reference to the global buffer info.
pub fn get() -> crate::Result<impl Deref<Target = Self>> {
@ -33,12 +61,16 @@ impl BufInfo { @@ -33,12 +61,16 @@ impl BufInfo {
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 attrib_count = attrib_info.count();
let permutation = attrib_info.permutation();
unsafe {
let res = unsafe {
citro3d_sys::BufInfo_Add(
self.raw,
// TODO: figure out how the hell to encode the lifetime of this
@ -47,9 +79,17 @@ impl BufInfo { @@ -47,9 +79,17 @@ impl BufInfo {
stride,
attrib_count,
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; @@ -10,7 +10,7 @@ use ctru::gfx::Screen;
use ctru::services::gspgpu::FramebufferFormat;
use ctru_sys::{GPU_COLORBUF, GPU_DEPTHBUF};
use crate::{Error, Result};
use crate::{buffers, Error, Result};
mod transfer;
@ -91,6 +91,16 @@ impl<'screen> Target<'screen> { @@ -91,6 +91,16 @@ impl<'screen> Target<'screen> {
pub(crate) fn as_raw(&self) -> *mut C3D_RenderTarget {
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! {

Loading…
Cancel
Save