Browse Source

First pass at some safe wrappers for render target

pull/18/head
Ian Chamberlain 3 years ago
parent
commit
83b613684a
No known key found for this signature in database
GPG Key ID: AE5484D09405AA60
  1. 3
      citro3d/Cargo.toml
  2. 1
      citro3d/examples/assets/.gitignore
  3. 5
      citro3d/examples/assets/vshader.pica
  4. 60
      citro3d/examples/triangle.rs
  5. 22
      citro3d/src/error.rs
  6. 72
      citro3d/src/lib.rs
  7. 147
      citro3d/src/render.rs
  8. 0
      citro3d/src/shader.rs
  9. 0
      citro3d/src/texture.rs
  10. 0
      citro3d/src/vbo.rs

3
citro3d/Cargo.toml

@ -5,8 +5,9 @@ edition = "2021" @@ -5,8 +5,9 @@ edition = "2021"
authors = [""]
[dependencies]
bitflags = "1.3.2"
citro3d-sys = { git = "https://github.com/ian-h-chamberlain/citro3d-rs.git" }
ctru-rs = { git = "https://github.com/Meziu/ctru-rs.git" }
libc = "0.2.125"
[dev-dependencies]
ctru-rs = { git = "https://github.com/Meziu/ctru-rs.git" }

1
citro3d/examples/assets/.gitignore vendored

@ -1 +0,0 @@ @@ -1 +0,0 @@
*.shbin

5
citro3d/examples/assets/vshader.pica

@ -1,11 +1,10 @@ @@ -1,11 +1,10 @@
; Example PICA200 vertex shader
; Basic PICA200 vertex shader
; Uniforms
.fvec projection[4]
; Constants
.constf myconst(0.0, 1.0, -1.0, 0.1)
.alias ones myconst.yyyy ; Vector full of ones
.constf ones(1.0, 1.0, 1.0, 1.0)
; Outputs
.out outpos position

60
citro3d/examples/triangle.rs

@ -1,10 +1,14 @@ @@ -1,10 +1,14 @@
use citro3d_sys::C3D_Mtx;
use citro3d_sys::{shaderProgram_s, DVLB_s};
use ctru::gfx::{Gfx, Screen, Side};
use ctru::gfx::{Gfx, Side};
use ctru::services::apt::Apt;
use ctru::services::gspgpu::FramebufferFormat;
use ctru::services::hid::{Hid, KeyPad};
use ctru::services::soc::Soc;
use citro3d::render::{ClearFlags, DepthFormat, TransferFormat};
use citro3d::C3DContext;
use std::ffi::CStr;
use std::mem::MaybeUninit;
@ -27,7 +31,7 @@ struct Vertex { @@ -27,7 +31,7 @@ struct Vertex {
color: Vec3,
}
const VERTICES: [Vertex; 3] = [
const VERTICES: &[Vertex] = &[
Vertex {
pos: Vec3::new(0.0, 0.5, 0.5),
color: Vec3::new(1.0, 0.0, 0.0),
@ -45,37 +49,30 @@ const VERTICES: [Vertex; 3] = [ @@ -45,37 +49,30 @@ const VERTICES: [Vertex; 3] = [
fn main() {
ctru::init();
let gfx = Gfx::init().expect("Couldn't obtain GFX controller");
let hid = Hid::init().expect("Couldn't obtain HID controller");
let apt = Apt::init().expect("Couldn't obtain APT controller");
let mut soc = Soc::init().expect("failed to get SOC");
drop(soc.redirect_to_3dslink(true, true));
let top_screen = gfx.top_screen.borrow_mut();
let target = unsafe {
citro3d_sys::C3D_Init(citro3d_sys::C3D_DEFAULT_CMDBUF_SIZE);
let depth_fmt = citro3d_sys::C3D_DEPTHTYPE {
__e: citro3d_sys::GPU_RB_DEPTH24_STENCIL8,
};
let gfx = Gfx::init().expect("Couldn't obtain GFX controller");
let hid = Hid::init().expect("Couldn't obtain HID controller");
let apt = Apt::init().expect("Couldn't obtain APT controller");
let target =
citro3d_sys::C3D_RenderTargetCreate(240, 400, citro3d_sys::GPU_RB_RGBA8, depth_fmt);
let mut top_screen = gfx.top_screen.borrow_mut();
let frame_buffer = top_screen.get_raw_framebuffer(Side::Left);
// TODO: easier construction of flags
let transfer_flags =
citro3d_sys::GX_TRANSFER_FMT_RGBA8 << 8 | citro3d_sys::GX_TRANSFER_FMT_RGB8 << 12;
let ctx = C3DContext::new().expect("failed to initialize Citro3D");
let mut render_target = ctx
.render_target_for_screen(
&frame_buffer,
// TODO: why doesn't getting this from the screen work?
FramebufferFormat::Rgba8.into(),
DepthFormat::Depth24Stencil8,
)
.expect("failed to create render target");
citro3d_sys::C3D_RenderTargetSetOutput(
target,
top_screen.as_raw(),
Side::Left.into(),
transfer_flags,
);
// TODO: easier construction of flags, see macros in <3ds/gpu/gx.h>
let transfer_flags = (TransferFormat::RGBA8 as u32) << 8 | (TransferFormat::RGB8 as u32) << 12;
target
};
render_target.set_output(&*top_screen, Side::Left, transfer_flags);
let (program, uloc_projection, projection, vbo_data, vshader_dvlb) = scene_init();
@ -86,18 +83,19 @@ fn main() { @@ -86,18 +83,19 @@ fn main() {
break;
}
let clear_color: u32 = 0x7F_7F_7F_FF;
unsafe {
citro3d_sys::C3D_FrameBegin(
citro3d_sys::C3D_FRAME_SYNCDRAW
.try_into()
.expect("const is valid u8"),
);
citro3d_sys::C3D_RenderTargetClear(target, citro3d_sys::C3D_CLEAR_ALL, clear_color, 0);
citro3d_sys::C3D_FrameDrawOn(target);
}
// Is this format-dependent? because we used RGBA8 for transfer?
let clear_color: u32 = 0x7F_7F_7F_FF;
render_target.clear(ClearFlags::ALL, clear_color, 0);
render_target.set_for_draw();
scene_render(uloc_projection.into(), &projection);
unsafe {
citro3d_sys::C3D_FrameEnd(0);

22
citro3d/src/error.rs

@ -0,0 +1,22 @@ @@ -0,0 +1,22 @@
//! General-purpose error and result types returned by public APIs of this crate.
use std::num::TryFromIntError;
/// The common result type returned by `citro3d` functions.
pub type Result<T> = std::result::Result<T, Error>;
/// The common error type that may be returned by `citro3d` functions.
#[non_exhaustive]
#[derive(Debug)]
pub enum Error {
/// A C3D object or context could not be initialized.
FailedToInitialize,
/// A size parameter was specified that cannot be converted to the proper type.
InvalidSize,
}
impl From<TryFromIntError> for Error {
fn from(_: TryFromIntError) -> Self {
Self::InvalidSize
}
}

72
citro3d/src/lib.rs

@ -1,8 +1,68 @@ @@ -1,8 +1,68 @@
#[cfg(test)]
mod tests {
#[test]
fn it_works() {
let result = 2 + 2;
assert_eq!(result, 4);
//! Safe Rust bindings to `citro3d`.
pub mod error;
pub mod render;
pub mod shader;
pub mod texture;
pub mod vbo;
use ctru::gfx::RawFrameBuffer;
pub use error::{Error, Result};
use render::Target;
/// The base context for using `citro3d`. This type must be used for
#[non_exhaustive]
#[derive(Debug)]
pub struct C3DContext;
impl C3DContext {
/// Initialize the default context.
///
/// # Errors
///
/// Fails if the `citro3d` library cannot be initialized.
pub fn new() -> Result<Self> {
Self::with_command_buffer_size(citro3d_sys::C3D_DEFAULT_CMDBUF_SIZE)
}
/// Initialize the context with a specified command buffer
///
/// # Errors
///
/// Fails if the `citro3d` library cannot be initialized.
pub fn with_command_buffer_size(size: u32) -> Result<Self> {
if unsafe { citro3d_sys::C3D_Init(size) } {
Ok(Self)
} else {
Err(Error::FailedToInitialize)
}
}
/// 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> {
Target::new(
frame_buffer.width.into(),
frame_buffer.height.into(),
color_format,
depth_format,
)
}
}
impl Drop for C3DContext {
fn drop(&mut self) {
unsafe {
citro3d_sys::C3D_Fini();
}
}
}

147
citro3d/src/render.rs

@ -0,0 +1,147 @@ @@ -0,0 +1,147 @@
use citro3d_sys::{
C3D_RenderTargetCreate, C3D_RenderTargetDelete, C3D_DEPTHTYPE, GPU_COLORBUF, GPU_DEPTHBUF,
};
use ctru::gfx;
use ctru::services::gspgpu;
use crate::{Error, Result};
/// A render target for `citro3d`. This is the data structure which handles sending
/// data to the GPU
pub struct Target {
tag: *mut citro3d_sys::C3D_RenderTarget_tag,
}
impl Target {
/// 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.
pub fn new(
width: u32,
height: u32,
color_format: ColorFormat,
depth_format: DepthFormat,
) -> Result<Self> {
let tag = unsafe {
C3D_RenderTargetCreate(
width.try_into()?,
height.try_into()?,
color_format as GPU_COLORBUF,
depth_format.as_raw(),
)
};
if tag.is_null() {
Err(Error::FailedToInitialize)
} else {
Ok(Self { tag })
}
}
/// 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, transfer_flags: u32) {
unsafe {
citro3d_sys::C3D_RenderTargetSetOutput(
self.tag,
screen.as_raw(),
side.into(),
transfer_flags,
);
}
}
pub fn clear(&mut self, flags: ClearFlags, color: u32, depth: u32) {
unsafe {
citro3d_sys::C3D_RenderTargetClear(self.tag, flags.bits(), color, depth);
}
}
// TODO: this should maybe be a method on C3DContext instead?
pub fn set_for_draw(&mut self) {
unsafe {
citro3d_sys::C3D_FrameDrawOn(self.tag);
}
}
}
#[repr(u32)]
pub enum TransferFormat {
RGBA8 = citro3d_sys::GX_TRANSFER_FMT_RGBA8,
RGB8 = citro3d_sys::GX_TRANSFER_FMT_RGB8,
RGB565 = citro3d_sys::GX_TRANSFER_FMT_RGB565,
RGB5A1 = citro3d_sys::GX_TRANSFER_FMT_RGB5A1,
RGBA4 = citro3d_sys::GX_TRANSFER_FMT_RGBA4,
}
// TODO: more flags
bitflags::bitflags! {
pub struct TransferFlags: u32 {
const SCALE_NO = citro3d_sys::GX_TRANSFER_SCALE_NO;
const SCALE_X = citro3d_sys::GX_TRANSFER_SCALE_X;
const SCALE_XY = citro3d_sys::GX_TRANSFER_SCALE_XY;
}
}
bitflags::bitflags! {
pub struct ClearFlags: u32 {
const COLOR = citro3d_sys::C3D_CLEAR_COLOR;
const DEPTH = citro3d_sys::C3D_CLEAR_DEPTH;
const ALL = citro3d_sys::C3D_CLEAR_ALL;
}
}
impl Drop for Target {
fn drop(&mut self) {
unsafe {
C3D_RenderTargetDelete(self.tag);
}
}
}
/// The color format to use when rendering on the GPU.
#[repr(u32)]
#[derive(Clone, Copy, Debug)]
pub enum ColorFormat {
/// 8-bit Red + 8-bit Green + 8-bit Blue + 8-bit Alpha.
RGBA8 = citro3d_sys::GPU_RB_RGBA8,
/// 8-bit Red + 8-bit Green + 8-bit Blue.
RGB8 = citro3d_sys::GPU_RB_RGB8,
/// 5-bit Red + 5-bit Green + 5-bit Blue + 1-bit Alpha.
RGBA5551 = citro3d_sys::GPU_RB_RGBA5551,
/// 5-bit Red + 6-bit Green + 5-bit Blue.
RGB565 = citro3d_sys::GPU_RB_RGB565,
/// 4-bit Red + 4-bit Green + 4-bit Blue + 4-bit Alpha.
RGBA4 = citro3d_sys::GPU_RB_RGBA4,
}
impl From<gspgpu::FramebufferFormat> for ColorFormat {
fn from(format: gspgpu::FramebufferFormat) -> Self {
match format {
gspgpu::FramebufferFormat::Rgba8 => Self::RGBA8,
gspgpu::FramebufferFormat::Rgb565 => Self::RGB565,
gspgpu::FramebufferFormat::Rgb5A1 => Self::RGBA5551,
gspgpu::FramebufferFormat::Rgba4 => Self::RGBA4,
fmt => panic!("Unsupported frame buffer format {fmt:?}"),
}
}
}
#[repr(u32)]
#[derive(Clone, Copy, Debug)]
pub enum DepthFormat {
Depth16 = citro3d_sys::GPU_RB_DEPTH16,
Depth24 = citro3d_sys::GPU_RB_DEPTH24,
Depth24Stencil8 = citro3d_sys::GPU_RB_DEPTH24_STENCIL8,
}
impl DepthFormat {
fn as_raw(self) -> C3D_DEPTHTYPE {
C3D_DEPTHTYPE {
__e: self as GPU_DEPTHBUF,
}
}
}

0
citro3d/src/shader.rs

0
citro3d/src/texture.rs

0
citro3d/src/vbo.rs

Loading…
Cancel
Save