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. 69
      citro3d/examples/triangle.rs
  5. 25
      citro3d/src/lib.rs
  6. 56
      citro3d/src/render.rs

2
Cargo.toml

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
[workspace]
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" }

13
citro3d-sys/src/renderqueue.rs

@ -1,11 +1,18 @@ @@ -1,11 +1,18 @@
//! 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]
pub unsafe fn C3D_RenderTargetClear(
target: *mut crate::C3D_RenderTarget,
clearBits: crate::C3D_ClearBits,
target: *mut C3D_RenderTarget,
clearBits: C3D_ClearBits,
clearColor: 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" @@ -6,7 +6,7 @@ edition = "2021"
[dependencies]
bitflags = "1.3.2"
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-sys = { git = "https://github.com/rust3ds/ctru-rs.git" }
libc = "0.2.125"

69
citro3d/examples/triangle.rs

@ -1,16 +1,18 @@ @@ -1,16 +1,18 @@
#![feature(allocator_api)]
use citro3d::render::{ClearFlags, Target};
use citro3d::{include_aligned_bytes, shader};
use citro3d_sys::C3D_Mtx;
use ctru::gfx::{Gfx, Side};
use ctru::gfx::{Gfx, RawFrameBuffer, Screen};
use ctru::services::apt::Apt;
use ctru::services::hid::{Hid, KeyPad};
use ctru::services::soc::Soc;
use citro3d::render::{ClearFlags, ColorFormat, DepthFormat};
use std::ffi::CStr;
use std::mem::MaybeUninit;
#[repr(C)]
#[derive(Copy, Clone)]
struct Vec3 {
x: f32,
y: f32,
@ -24,12 +26,13 @@ impl Vec3 { @@ -24,12 +26,13 @@ impl Vec3 {
}
#[repr(C)]
#[derive(Copy, Clone)]
struct Vertex {
pos: Vec3,
color: Vec3,
}
const VERTICES: &[Vertex] = &[
static VERTICES: &[Vertex] = &[
Vertex {
pos: Vec3::new(0.0, 0.5, 0.5),
color: Vec3::new(1.0, 0.0, 0.0),
@ -44,7 +47,7 @@ const VERTICES: &[Vertex] = &[ @@ -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"));
fn main() {
@ -58,26 +61,28 @@ fn main() { @@ -58,26 +61,28 @@ fn main() {
let apt = Apt::init().expect("Couldn't obtain APT controller");
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 render_target = instance
.render_target_for_screen(
&frame_buffer,
ColorFormat::RGBA8,
DepthFormat::Depth24Stencil8,
)
let mut top_target = citro3d::render::Target::new(width, height, top_screen, None)
.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 vertex_shader = shader.get(0).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() {
hid.scan_input();
@ -86,22 +91,24 @@ fn main() { @@ -86,22 +91,24 @@ fn main() {
break;
}
let mut render_to = |target: &mut Target| {
instance.render_frame_with(|instance| {
let clear_color: u32 = 0x7F_7F_7F_FF;
render_target.clear(ClearFlags::ALL, clear_color, 0);
instance
.select_render_target(&render_target)
.select_render_target(target)
.expect("failed to set render target");
let clear_color: u32 = 0x7F_7F_7F_FF;
target.clear(ClearFlags::ALL, clear_color, 0);
scene_render(uloc_projection.into(), &projection);
});
}
};
scene_exit(vbo_data);
render_to(&mut top_target);
render_to(&mut bottom_target);
}
}
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
unsafe {
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) @@ -136,22 +143,12 @@ fn scene_init(program: &mut shader::Program) -> (i8, C3D_Mtx, *mut libc::c_void)
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
let buf_info = citro3d_sys::C3D_GetBufInfo();
citro3d_sys::BufInfo_Init(buf_info);
citro3d_sys::BufInfo_Add(
buf_info,
vbo_data.cast(),
vbo_data.as_ptr().cast(),
std::mem::size_of::<Vertex>()
.try_into()
.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) @@ -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);
(uloc_projection, projection, vbo_data.cast())
(uloc_projection, projection)
}
}
@ -192,9 +189,3 @@ fn scene_render(uloc_projection: i32, projection: &C3D_Mtx) { @@ -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; @@ -7,11 +7,8 @@ pub mod texture;
pub mod vbo;
use citro3d_sys::C3D_FrameDrawOn;
use ctru::gfx::RawFrameBuffer;
pub use error::{Error, Result};
use render::Target;
/// The single instance for using `citro3d`. This is the base type that an application
/// should instantiate to use this library.
#[non_exhaustive]
@ -41,32 +38,12 @@ impl Instance { @@ -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.
///
/// # Errors
///
/// 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;
if unsafe { C3D_FrameDrawOn(target.as_raw()) } {
Ok(())

56
citro3d/src/render.rs

@ -1,10 +1,12 @@ @@ -1,10 +1,12 @@
//! 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.
use std::cell::RefMut;
use citro3d_sys::{
C3D_RenderTarget, C3D_RenderTargetCreate, C3D_RenderTargetDelete, C3D_DEPTHTYPE,
};
use ctru::gfx;
use ctru::gfx::Screen;
use ctru::services::gspgpu::FramebufferFormat;
use ctru_sys::{GPU_COLORBUF, GPU_DEPTHBUF};
@ -14,12 +16,14 @@ mod transfer; @@ -14,12 +16,14 @@ mod transfer;
/// A render target for `citro3d`. Frame data will be written to this target
/// to be rendered on the GPU and displayed on the screen.
pub struct Target {
pub struct Target<'screen> {
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) {
unsafe {
C3D_RenderTargetDelete(self.raw);
@ -27,52 +31,52 @@ impl Drop for Target { @@ -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,
/// and depth format.
///
/// # Errors
///
/// Fails if the specified sizes are invalid, or the target could not be
/// created.
/// Fails if the target could not be created.
pub fn new(
width: u32,
height: u32,
color_format: ColorFormat,
depth_format: DepthFormat,
width: u16,
height: u16,
screen: RefMut<'screen, dyn Screen>,
depth_format: Option<DepthFormat>,
) -> Result<Self> {
let color_format: ColorFormat = screen.get_framebuffer_format().into();
let raw = unsafe {
C3D_RenderTargetCreate(
width.try_into()?,
height.try_into()?,
width.into(),
height.into(),
color_format as GPU_COLORBUF,
depth_format.as_raw(),
depth_format.map_or(C3D_DEPTHTYPE { __i: -1 }, DepthFormat::as_raw),
)
};
if raw.is_null() {
Err(Error::FailedToInitialize)
} else {
Ok(Self { raw, color_format })
}
return Err(Error::FailedToInitialize);
}
/// 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()
.in_format(self.color_format.into())
.out_format(ColorFormat::from(framebuf_format).into());
.in_format(color_format.into())
.out_format(color_format.into());
unsafe {
citro3d_sys::C3D_RenderTargetSetOutput(
self.raw,
raw,
screen.as_raw(),
side.into(),
screen.side().into(),
flags.bits(),
);
}
Ok(Self {
raw,
_screen: screen,
})
}
/// Clear the render target with the given 32-bit RGBA color and depth buffer value.

Loading…
Cancel
Save