Browse Source

Merge pull request #4 from rust3ds/render-target-screen-lifetime

Add mutable ref to `Screen` in `render::Target`
pull/23/head
Ian Chamberlain 2 years ago committed by GitHub
parent
commit
429c80f1cc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 2
      Cargo.toml
  2. 13
      citro3d-sys/src/renderqueue.rs
  3. 2
      citro3d/Cargo.toml
  4. 77
      citro3d/examples/triangle.rs
  5. 25
      citro3d/src/lib.rs
  6. 56
      citro3d/src/render.rs

2
Cargo.toml

@ -1,5 +1,5 @@
[workspace] [workspace]
members = ["citro3d-sys", "citro3d", "bindgen-citro3d"] members = ["citro3d-sys", "citro3d", "bindgen-citro3d"]
[patch."https://github.com/ian-h-chamberlain/citro3d-rs.git"] [patch."https://github.com/rust3ds/citro3d-rs.git"]
citro3d-sys = { path = "citro3d-sys" } citro3d-sys = { path = "citro3d-sys" }

13
citro3d-sys/src/renderqueue.rs

@ -1,11 +1,18 @@
//! Definitions from `<c3d/renderqueue.h>` //! Definitions from `<c3d/renderqueue.h>`
use crate::*;
#[inline]
pub unsafe fn C3D_RenderTargetDetachOutput(target: *mut C3D_RenderTarget) {
C3D_RenderTargetSetOutput(core::ptr::null_mut(), (*target).screen, (*target).side, 0);
}
#[inline] #[inline]
pub unsafe fn C3D_RenderTargetClear( pub unsafe fn C3D_RenderTargetClear(
target: *mut crate::C3D_RenderTarget, target: *mut C3D_RenderTarget,
clearBits: crate::C3D_ClearBits, clearBits: C3D_ClearBits,
clearColor: u32, clearColor: u32,
clearDepth: u32, clearDepth: u32,
) { ) {
crate::C3D_FrameBufClear(&mut (*target).frameBuf, clearBits, clearColor, clearDepth); C3D_FrameBufClear(&mut (*target).frameBuf, clearBits, clearColor, clearDepth);
} }

2
citro3d/Cargo.toml

@ -6,7 +6,7 @@ edition = "2021"
[dependencies] [dependencies]
bitflags = "1.3.2" bitflags = "1.3.2"
bytemuck = { version = "1.10.0", features = ["extern_crate_std"] } bytemuck = { version = "1.10.0", features = ["extern_crate_std"] }
citro3d-sys = { git = "https://github.com/ian-h-chamberlain/citro3d-rs.git" } citro3d-sys = { git = "https://github.com/rust3ds/citro3d-rs.git" }
ctru-rs = { git = "https://github.com/rust3ds/ctru-rs.git" } ctru-rs = { git = "https://github.com/rust3ds/ctru-rs.git" }
ctru-sys = { git = "https://github.com/rust3ds/ctru-rs.git" } ctru-sys = { git = "https://github.com/rust3ds/ctru-rs.git" }
libc = "0.2.125" libc = "0.2.125"

77
citro3d/examples/triangle.rs

@ -1,16 +1,18 @@
#![feature(allocator_api)]
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;
use ctru::gfx::{Gfx, Side}; use ctru::gfx::{Gfx, RawFrameBuffer, Screen};
use ctru::services::apt::Apt; use ctru::services::apt::Apt;
use ctru::services::hid::{Hid, KeyPad}; use ctru::services::hid::{Hid, KeyPad};
use ctru::services::soc::Soc; use ctru::services::soc::Soc;
use citro3d::render::{ClearFlags, ColorFormat, DepthFormat};
use std::ffi::CStr; use std::ffi::CStr;
use std::mem::MaybeUninit; use std::mem::MaybeUninit;
#[repr(C)] #[repr(C)]
#[derive(Copy, Clone)]
struct Vec3 { struct Vec3 {
x: f32, x: f32,
y: f32, y: f32,
@ -24,12 +26,13 @@ impl Vec3 {
} }
#[repr(C)] #[repr(C)]
#[derive(Copy, Clone)]
struct Vertex { struct Vertex {
pos: Vec3, pos: Vec3,
color: Vec3, color: Vec3,
} }
const VERTICES: &[Vertex] = &[ static VERTICES: &[Vertex] = &[
Vertex { Vertex {
pos: Vec3::new(0.0, 0.5, 0.5), pos: Vec3::new(0.0, 0.5, 0.5),
color: Vec3::new(1.0, 0.0, 0.0), color: Vec3::new(1.0, 0.0, 0.0),
@ -44,7 +47,7 @@ const VERTICES: &[Vertex] = &[
}, },
]; ];
const SHADER_BYTES: &[u8] = static SHADER_BYTES: &[u8] =
include_aligned_bytes!(concat!(env!("OUT_DIR"), "/examples/assets/vshader.shbin")); include_aligned_bytes!(concat!(env!("OUT_DIR"), "/examples/assets/vshader.shbin"));
fn main() { fn main() {
@ -58,26 +61,28 @@ fn main() {
let apt = Apt::init().expect("Couldn't obtain APT controller"); let apt = Apt::init().expect("Couldn't obtain APT controller");
let mut top_screen = gfx.top_screen.borrow_mut(); let mut top_screen = gfx.top_screen.borrow_mut();
let frame_buffer = top_screen.get_raw_framebuffer(Side::Left); let RawFrameBuffer { width, height, .. } = top_screen.get_raw_framebuffer();
let mut instance = citro3d::Instance::new().expect("failed to initialize Citro3D"); let mut instance = citro3d::Instance::new().expect("failed to initialize Citro3D");
let mut render_target = instance let mut top_target = citro3d::render::Target::new(width, height, top_screen, None)
.render_target_for_screen(
&frame_buffer,
ColorFormat::RGBA8,
DepthFormat::Depth24Stencil8,
)
.expect("failed to create render target"); .expect("failed to create render target");
render_target.set_output(&*top_screen, Side::Left); let mut bottom_screen = gfx.bottom_screen.borrow_mut();
let RawFrameBuffer { width, height, .. } = bottom_screen.get_raw_framebuffer();
let mut bottom_target = citro3d::render::Target::new(width, height, bottom_screen, None)
.expect("failed to create bottom screen render target");
let shader = shader::Library::from_bytes(SHADER_BYTES).unwrap(); let shader = shader::Library::from_bytes(SHADER_BYTES).unwrap();
let vertex_shader = shader.get(0).unwrap(); let vertex_shader = shader.get(0).unwrap();
let mut program = shader::Program::new(vertex_shader).unwrap(); let mut program = shader::Program::new(vertex_shader).unwrap();
let (uloc_projection, projection, vbo_data) = scene_init(&mut program); let mut vbo_data = Vec::with_capacity_in(VERTICES.len(), ctru::linear::LinearAllocator);
vbo_data.extend_from_slice(VERTICES);
let (uloc_projection, projection) = scene_init(&mut program, &vbo_data);
while apt.main_loop() { while apt.main_loop() {
hid.scan_input(); hid.scan_input();
@ -86,22 +91,24 @@ fn main() {
break; break;
} }
instance.render_frame_with(|instance| { let mut render_to = |target: &mut Target| {
let clear_color: u32 = 0x7F_7F_7F_FF; instance.render_frame_with(|instance| {
render_target.clear(ClearFlags::ALL, clear_color, 0); instance
.select_render_target(target)
.expect("failed to set render target");
instance let clear_color: u32 = 0x7F_7F_7F_FF;
.select_render_target(&render_target) target.clear(ClearFlags::ALL, clear_color, 0);
.expect("failed to set render target"); scene_render(uloc_projection.into(), &projection);
});
};
scene_render(uloc_projection.into(), &projection); render_to(&mut top_target);
}); render_to(&mut bottom_target);
} }
scene_exit(vbo_data);
} }
fn scene_init(program: &mut shader::Program) -> (i8, C3D_Mtx, *mut libc::c_void) { fn scene_init(program: &mut shader::Program, vbo_data: &[Vertex]) -> (i8, C3D_Mtx) {
// Load the vertex shader, create a shader program and bind it // Load the vertex shader, create a shader program and bind it
unsafe { unsafe {
citro3d_sys::C3D_BindProgram(program.as_raw()); citro3d_sys::C3D_BindProgram(program.as_raw());
@ -136,22 +143,12 @@ fn scene_init(program: &mut shader::Program) -> (i8, C3D_Mtx, *mut libc::c_void)
projection.assume_init() projection.assume_init()
}; };
// Create the vertex buffer object
let vbo_data: *mut Vertex = ctru_sys::linearAlloc(
std::mem::size_of_val(&VERTICES)
.try_into()
.expect("size fits in u32"),
)
.cast();
vbo_data.copy_from(VERTICES.as_ptr(), VERTICES.len());
// Configure buffers // Configure buffers
let buf_info = citro3d_sys::C3D_GetBufInfo(); let buf_info = citro3d_sys::C3D_GetBufInfo();
citro3d_sys::BufInfo_Init(buf_info); citro3d_sys::BufInfo_Init(buf_info);
citro3d_sys::BufInfo_Add( citro3d_sys::BufInfo_Add(
buf_info, buf_info,
vbo_data.cast(), vbo_data.as_ptr().cast(),
std::mem::size_of::<Vertex>() std::mem::size_of::<Vertex>()
.try_into() .try_into()
.expect("size of vec3 fits in u32"), .expect("size of vec3 fits in u32"),
@ -172,7 +169,7 @@ fn scene_init(program: &mut shader::Program) -> (i8, C3D_Mtx, *mut libc::c_void)
); );
citro3d_sys::C3D_TexEnvFunc(env, citro3d_sys::C3D_Both, ctru_sys::GPU_REPLACE); citro3d_sys::C3D_TexEnvFunc(env, citro3d_sys::C3D_Both, ctru_sys::GPU_REPLACE);
(uloc_projection, projection, vbo_data.cast()) (uloc_projection, projection)
} }
} }
@ -192,9 +189,3 @@ fn scene_render(uloc_projection: i32, projection: &C3D_Mtx) {
); );
} }
} }
fn scene_exit(vbo_data: *mut libc::c_void) {
unsafe {
ctru_sys::linearFree(vbo_data);
}
}

25
citro3d/src/lib.rs

@ -7,11 +7,8 @@ pub mod texture;
pub mod vbo; pub mod vbo;
use citro3d_sys::C3D_FrameDrawOn; use citro3d_sys::C3D_FrameDrawOn;
use ctru::gfx::RawFrameBuffer;
pub use error::{Error, Result}; pub use error::{Error, Result};
use render::Target;
/// The single instance for using `citro3d`. This is the base type that an application /// The single instance for using `citro3d`. This is the base type that an application
/// should instantiate to use this library. /// should instantiate to use this library.
#[non_exhaustive] #[non_exhaustive]
@ -41,32 +38,12 @@ impl Instance {
} }
} }
/// Create a default render target for the given screen.
///
/// # Errors
///
/// Fails if the render target could not be created.
pub fn render_target_for_screen(
&self,
frame_buffer: &RawFrameBuffer,
color_format: render::ColorFormat,
depth_format: render::DepthFormat,
) -> Result<Target> {
let _ = self;
Target::new(
frame_buffer.width.into(),
frame_buffer.height.into(),
color_format,
depth_format,
)
}
/// Select the given render target for drawing the frame. /// Select the given render target for drawing the frame.
/// ///
/// # Errors /// # Errors
/// ///
/// Fails if the given target cannot be used for drawing. /// Fails if the given target cannot be used for drawing.
pub fn select_render_target(&mut self, target: &render::Target) -> Result<()> { pub fn select_render_target(&mut self, target: &render::Target<'_>) -> Result<()> {
let _ = self; let _ = self;
if unsafe { C3D_FrameDrawOn(target.as_raw()) } { if unsafe { C3D_FrameDrawOn(target.as_raw()) } {
Ok(()) Ok(())

56
citro3d/src/render.rs

@ -1,10 +1,12 @@
//! This module provides render target types and options for controlling transfer //! This module provides render target types and options for controlling transfer
//! of data to the GPU, including the format of color and depth data to be rendered. //! of data to the GPU, including the format of color and depth data to be rendered.
use std::cell::RefMut;
use citro3d_sys::{ use citro3d_sys::{
C3D_RenderTarget, C3D_RenderTargetCreate, C3D_RenderTargetDelete, C3D_DEPTHTYPE, C3D_RenderTarget, C3D_RenderTargetCreate, C3D_RenderTargetDelete, C3D_DEPTHTYPE,
}; };
use ctru::gfx; 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};
@ -14,12 +16,14 @@ mod transfer;
/// A render target for `citro3d`. Frame data will be written to this target /// A render target for `citro3d`. Frame data will be written to this target
/// to be rendered on the GPU and displayed on the screen. /// to be rendered on the GPU and displayed on the screen.
pub struct Target { pub struct Target<'screen> {
raw: *mut citro3d_sys::C3D_RenderTarget, raw: *mut citro3d_sys::C3D_RenderTarget,
color_format: ColorFormat, // This is unused after construction, but ensures unique access to the
// screen this target writes to during rendering
_screen: RefMut<'screen, dyn Screen>,
} }
impl Drop for Target { impl Drop for Target<'_> {
fn drop(&mut self) { fn drop(&mut self) {
unsafe { unsafe {
C3D_RenderTargetDelete(self.raw); C3D_RenderTargetDelete(self.raw);
@ -27,52 +31,52 @@ impl Drop for Target {
} }
} }
impl Target { impl<'screen> Target<'screen> {
/// Create a new render target with the specified size, color format, /// Create a new render target with the specified size, color format,
/// and depth format. /// and depth format.
/// ///
/// # Errors /// # Errors
/// ///
/// Fails if the specified sizes are invalid, or the target could not be /// Fails if the target could not be created.
/// created.
pub fn new( pub fn new(
width: u32, width: u16,
height: u32, height: u16,
color_format: ColorFormat, screen: RefMut<'screen, dyn Screen>,
depth_format: DepthFormat, depth_format: Option<DepthFormat>,
) -> Result<Self> { ) -> Result<Self> {
let color_format: ColorFormat = screen.get_framebuffer_format().into();
let raw = unsafe { let raw = unsafe {
C3D_RenderTargetCreate( C3D_RenderTargetCreate(
width.try_into()?, width.into(),
height.try_into()?, height.into(),
color_format as GPU_COLORBUF, color_format as GPU_COLORBUF,
depth_format.as_raw(), depth_format.map_or(C3D_DEPTHTYPE { __i: -1 }, DepthFormat::as_raw),
) )
}; };
if raw.is_null() { if raw.is_null() {
Err(Error::FailedToInitialize) return Err(Error::FailedToInitialize);
} else {
Ok(Self { raw, color_format })
} }
}
/// Sets the screen to actually display the output of this render target.
pub fn set_output(&mut self, screen: &impl gfx::Screen, side: gfx::Side) {
let framebuf_format = screen.get_framebuffer_format();
// Set the render target to actually output to the given screen
let flags = transfer::Flags::default() let flags = transfer::Flags::default()
.in_format(self.color_format.into()) .in_format(color_format.into())
.out_format(ColorFormat::from(framebuf_format).into()); .out_format(color_format.into());
unsafe { unsafe {
citro3d_sys::C3D_RenderTargetSetOutput( citro3d_sys::C3D_RenderTargetSetOutput(
self.raw, raw,
screen.as_raw(), screen.as_raw(),
side.into(), screen.side().into(),
flags.bits(), flags.bits(),
); );
} }
Ok(Self {
raw,
_screen: screen,
})
} }
/// Clear the render target with the given 32-bit RGBA color and depth buffer value. /// Clear the render target with the given 32-bit RGBA color and depth buffer value.

Loading…
Cancel
Save