Browse Source

Update render target API for auto framebuffer fmt

Transfer flags can be an implementation detail this way, although it
might need to be exposed eventually.
pull/18/head
Ian Chamberlain 3 years ago
parent
commit
184060c63d
No known key found for this signature in database
GPG Key ID: AE5484D09405AA60
  1. 28
      citro3d/examples/triangle.rs
  2. 2
      citro3d/src/error.rs
  3. 37
      citro3d/src/lib.rs
  4. 103
      citro3d/src/render.rs
  5. 57
      citro3d/src/render/transfer.rs
  6. 1
      citro3d/src/shader.rs
  7. 1
      citro3d/src/texture.rs
  8. 1
      citro3d/src/vbo.rs

28
citro3d/examples/triangle.rs

@ -2,12 +2,10 @@ use citro3d_sys::C3D_Mtx; @@ -2,12 +2,10 @@ use citro3d_sys::C3D_Mtx;
use citro3d_sys::{shaderProgram_s, DVLB_s};
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 citro3d::render::{ClearFlags, ColorFormat, DepthFormat};
use std::ffi::CStr;
use std::mem::MaybeUninit;
@ -59,20 +57,17 @@ fn main() { @@ -59,20 +57,17 @@ fn main() {
let mut top_screen = gfx.top_screen.borrow_mut();
let frame_buffer = top_screen.get_raw_framebuffer(Side::Left);
let ctx = C3DContext::new().expect("failed to initialize Citro3D");
let mut render_target = ctx
let mut instance = citro3d::Instance::new().expect("failed to initialize Citro3D");
let mut render_target = instance
.render_target_for_screen(
&frame_buffer,
// TODO: why doesn't getting this from the screen work?
FramebufferFormat::Rgba8.into(),
ColorFormat::RGBA8,
DepthFormat::Depth24Stencil8,
)
.expect("failed to create render target");
// 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;
render_target.set_output(&*top_screen, Side::Left, transfer_flags);
render_target.set_output(&*top_screen, Side::Left);
let (program, uloc_projection, projection, vbo_data, vshader_dvlb) = scene_init();
@ -91,22 +86,21 @@ fn main() { @@ -91,22 +86,21 @@ fn main() {
);
}
// 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();
instance
.select_render_target(&render_target)
.expect("failed to set render target");
scene_render(uloc_projection.into(), &projection);
unsafe {
citro3d_sys::C3D_FrameEnd(0);
}
}
scene_exit(vbo_data, program, vshader_dvlb);
unsafe {
citro3d_sys::C3D_Fini();
}
}
static SHBIN_BYTES: &[u8] =

2
citro3d/src/error.rs

@ -13,6 +13,8 @@ pub enum Error { @@ -13,6 +13,8 @@ pub enum Error {
FailedToInitialize,
/// A size parameter was specified that cannot be converted to the proper type.
InvalidSize,
/// Failed to select the given render target for drawing to.
InvalidRenderTarget,
}
impl From<TryFromIntError> for Error {

37
citro3d/src/lib.rs

@ -6,32 +6,34 @@ pub mod shader; @@ -6,32 +6,34 @@ pub mod shader;
pub mod texture;
pub mod vbo;
use citro3d_sys::C3D_FrameDrawOn;
use ctru::gfx::RawFrameBuffer;
pub use error::{Error, Result};
use render::Target;
/// The base context for using `citro3d`. This type must be used for
/// The single instance for using `citro3d`. This is the base type that an application
/// should instantiate to use this library.
#[non_exhaustive]
#[derive(Debug)]
pub struct C3DContext;
pub struct Instance;
impl C3DContext {
/// Initialize the default context.
impl Instance {
/// Initialize the default `citro3d` instance.
///
/// # Errors
///
/// Fails if the `citro3d` library cannot be initialized.
/// Fails if `citro3d` cannot be initialized.
pub fn new() -> Result<Self> {
Self::with_command_buffer_size(citro3d_sys::C3D_DEFAULT_CMDBUF_SIZE)
Self::with_cmdbuf_size(citro3d_sys::C3D_DEFAULT_CMDBUF_SIZE)
}
/// Initialize the context with a specified command buffer
/// Initialize the instance with a specified command buffer size.
///
/// # Errors
///
/// Fails if the `citro3d` library cannot be initialized.
pub fn with_command_buffer_size(size: u32) -> Result<Self> {
/// Fails if `citro3d` cannot be initialized.
pub fn with_cmdbuf_size(size: u32) -> Result<Self> {
if unsafe { citro3d_sys::C3D_Init(size) } {
Ok(Self)
} else {
@ -50,6 +52,7 @@ impl C3DContext { @@ -50,6 +52,7 @@ impl C3DContext {
color_format: render::ColorFormat,
depth_format: render::DepthFormat,
) -> Result<Target> {
let _ = self;
Target::new(
frame_buffer.width.into(),
frame_buffer.height.into(),
@ -57,9 +60,23 @@ impl C3DContext { @@ -57,9 +60,23 @@ impl C3DContext {
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<()> {
let _ = self;
if unsafe { C3D_FrameDrawOn(target.as_raw()) } {
Ok(())
} else {
Err(Error::InvalidRenderTarget)
}
}
}
impl Drop for C3DContext {
impl Drop for Instance {
fn drop(&mut self) {
unsafe {
citro3d_sys::C3D_Fini();

103
citro3d/src/render.rs

@ -1,15 +1,30 @@ @@ -1,15 +1,30 @@
//! 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 citro3d_sys::{
C3D_RenderTargetCreate, C3D_RenderTargetDelete, C3D_DEPTHTYPE, GPU_COLORBUF, GPU_DEPTHBUF,
C3D_RenderTarget, C3D_RenderTargetCreate, C3D_RenderTargetDelete, C3D_DEPTHTYPE, GPU_COLORBUF,
GPU_DEPTHBUF,
};
use ctru::gfx;
use ctru::services::gspgpu;
use ctru::services::gspgpu::FramebufferFormat;
use crate::{Error, Result};
/// A render target for `citro3d`. This is the data structure which handles sending
/// data to the GPU
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 {
tag: *mut citro3d_sys::C3D_RenderTarget_tag,
raw: *mut citro3d_sys::C3D_RenderTarget,
color_format: ColorFormat,
}
impl Drop for Target {
fn drop(&mut self) {
unsafe {
C3D_RenderTargetDelete(self.raw);
}
}
}
impl Target {
@ -26,7 +41,7 @@ impl Target { @@ -26,7 +41,7 @@ impl Target {
color_format: ColorFormat,
depth_format: DepthFormat,
) -> Result<Self> {
let tag = unsafe {
let raw = unsafe {
C3D_RenderTargetCreate(
width.try_into()?,
height.try_into()?,
@ -35,58 +50,47 @@ impl Target { @@ -35,58 +50,47 @@ impl Target {
)
};
if tag.is_null() {
if raw.is_null() {
Err(Error::FailedToInitialize)
} else {
Ok(Self { tag })
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, transfer_flags: u32) {
pub fn set_output(&mut self, screen: &impl gfx::Screen, side: gfx::Side) {
let framebuf_format = screen.get_framebuffer_format();
let flags = transfer::Flags::default()
.in_format(self.color_format.into())
.out_format(ColorFormat::from(framebuf_format).into());
unsafe {
citro3d_sys::C3D_RenderTargetSetOutput(
self.tag,
self.raw,
screen.as_raw(),
side.into(),
transfer_flags,
flags.bits(),
);
}
}
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) {
/// Clear the render target with the given 32-bit RGBA color and depth buffer value.
/// Use `flags` to specify whether color and/or depth should be overwritten.
pub fn clear(&mut self, flags: ClearFlags, rgba_color: u32, depth: u32) {
unsafe {
citro3d_sys::C3D_FrameDrawOn(self.tag);
citro3d_sys::C3D_RenderTargetClear(self.raw, flags.bits(), rgba_color, depth);
}
}
}
#[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;
/// Return the underlying `citro3d` render target for this target.
pub(crate) fn as_raw(&self) -> *mut C3D_RenderTarget {
self.raw
}
}
bitflags::bitflags! {
/// Indicate whether color, depth buffer, or both values should be cleared.
pub struct ClearFlags: u32 {
const COLOR = citro3d_sys::C3D_CLEAR_COLOR;
const DEPTH = citro3d_sys::C3D_CLEAR_DEPTH;
@ -94,14 +98,6 @@ bitflags::bitflags! { @@ -94,14 +98,6 @@ bitflags::bitflags! {
}
}
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)]
@ -118,23 +114,28 @@ pub enum ColorFormat { @@ -118,23 +114,28 @@ pub enum ColorFormat {
RGBA4 = citro3d_sys::GPU_RB_RGBA4,
}
impl From<gspgpu::FramebufferFormat> for ColorFormat {
fn from(format: gspgpu::FramebufferFormat) -> Self {
impl From<FramebufferFormat> for ColorFormat {
fn from(format: 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:?}"),
FramebufferFormat::Rgba8 => Self::RGBA8,
FramebufferFormat::Rgb565 => Self::RGB565,
FramebufferFormat::Rgb5A1 => Self::RGBA5551,
FramebufferFormat::Rgba4 => Self::RGBA4,
// this one seems unusual, but it appears to work fine:
FramebufferFormat::Bgr8 => Self::RGB8,
}
}
}
/// The depth buffer format to use when rendering.
#[repr(u32)]
#[derive(Clone, Copy, Debug)]
pub enum DepthFormat {
/// 16-bit depth.
Depth16 = citro3d_sys::GPU_RB_DEPTH16,
/// 24-bit depth.
Depth24 = citro3d_sys::GPU_RB_DEPTH24,
/// 24-bit depth + 8-bit Stencil.
Depth24Stencil8 = citro3d_sys::GPU_RB_DEPTH24_STENCIL8,
}

57
citro3d/src/render/transfer.rs

@ -0,0 +1,57 @@ @@ -0,0 +1,57 @@
use citro3d_sys::{GX_TRANSFER_FORMAT, GX_TRANSFER_IN_FORMAT, GX_TRANSFER_OUT_FORMAT};
use super::ColorFormat;
/// Control flags for a GX data transfer.
#[derive(Default, Clone, Copy)]
pub struct Flags(u32);
impl Flags {
/// Set the input format of the data transfer.
#[must_use]
pub fn in_format(self, fmt: Format) -> Self {
Self(self.0 | GX_TRANSFER_IN_FORMAT(fmt as GX_TRANSFER_FORMAT))
}
/// Set the output format of the data transfer.
#[must_use]
pub fn out_format(self, fmt: Format) -> Self {
Self(self.0 | GX_TRANSFER_OUT_FORMAT(fmt as GX_TRANSFER_FORMAT))
}
#[must_use]
pub fn bits(self) -> u32 {
self.0
}
}
/// The color format to use when transferring data to/from the GPU.
///
/// NOTE: this a distinct type from [`ColorFormat`] because they are not implicitly
/// convertible to one another. Use [`From::from`] to get the [`Format`] corresponding
/// to a given [`ColorFormat`].
#[repr(u32)]
pub enum Format {
/// 8-bit Red + 8-bit Green + 8-bit Blue + 8-bit Alpha.
RGBA8 = citro3d_sys::GX_TRANSFER_FMT_RGBA8,
/// 8-bit Red + 8-bit Green + 8-bit Blue.
RGB8 = citro3d_sys::GX_TRANSFER_FMT_RGB8,
/// 5-bit Red + 5-bit Green + 5-bit Blue + 1-bit Alpha.
RGB565 = citro3d_sys::GX_TRANSFER_FMT_RGB565,
/// 5-bit Red + 6-bit Green + 5-bit Blue.
RGB5A1 = citro3d_sys::GX_TRANSFER_FMT_RGB5A1,
/// 4-bit Red + 4-bit Green + 4-bit Blue + 4-bit Alpha.
RGBA4 = citro3d_sys::GX_TRANSFER_FMT_RGBA4,
}
impl From<ColorFormat> for Format {
fn from(color_fmt: ColorFormat) -> Self {
match color_fmt {
ColorFormat::RGBA8 => Self::RGBA8,
ColorFormat::RGB8 => Self::RGB8,
ColorFormat::RGBA5551 => Self::RGB5A1,
ColorFormat::RGB565 => Self::RGB565,
ColorFormat::RGBA4 => Self::RGBA4,
}
}
}

1
citro3d/src/shader.rs

@ -0,0 +1 @@ @@ -0,0 +1 @@

1
citro3d/src/texture.rs

@ -0,0 +1 @@ @@ -0,0 +1 @@

1
citro3d/src/vbo.rs

@ -0,0 +1 @@ @@ -0,0 +1 @@
Loading…
Cancel
Save