Browse Source

Merge pull request #74 from FenrirWolf/gfx

Update and document Gfx module
pull/10/head
FenrirWolf 6 years ago committed by GitHub
parent
commit
97c7652fdd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 2
      ctru-rs/Cargo.toml
  2. 148
      ctru-rs/src/gfx.rs
  3. 58
      ctru-rs/src/services/gspgpu.rs

2
ctru-rs/Cargo.toml

@ -3,7 +3,7 @@ authors = ["Ronald Kinard <furyhunter600@gmail.com>"]
description = "A safe wrapper around smealum's ctrulib." description = "A safe wrapper around smealum's ctrulib."
license = "https://en.wikipedia.org/wiki/Zlib_License" license = "https://en.wikipedia.org/wiki/Zlib_License"
name = "ctru-rs" name = "ctru-rs"
version = "0.7.0" version = "0.7.1"
[lib] [lib]
crate-type = ["rlib"] crate-type = ["rlib"]

148
ctru-rs/src/gfx.rs

@ -1,118 +1,138 @@
//! LCD screens manipulation helper
use std::default::Default; use std::default::Default;
use std::ops::Drop; use std::ops::Drop;
use services::gspgpu::FramebufferFormat; use services::gspgpu::{self, FramebufferFormat};
/// A handle to libctru's gfx module. This module is a wrapper around the GSPGPU service that
/// provides helper functions and utilities for software rendering.
///
/// The service exits when this struct is dropped.
pub struct Gfx(()); pub struct Gfx(());
#[derive(Copy, Clone)] /// Available screens on the 3DS
#[derive(Copy, Clone, Debug)]
pub enum Screen { pub enum Screen {
/// The top screen
Top, Top,
/// The bottom screen
Bottom, Bottom,
} }
#[derive(Copy, Clone)] #[derive(Copy, Clone, Debug)]
/// Side of top screen framebuffer
///
/// The top screen of the 3DS can have two separate sets of framebuffers to support its 3D functionality
pub enum Side { pub enum Side {
/// The left framebuffer. This framebuffer is also the one used when 3D is disabled
Left, Left,
/// The right framebuffer
Right, Right,
} }
impl From<::libctru::gfxScreen_t> for Screen { impl Gfx {
fn from(g: ::libctru::gfxScreen_t) -> Screen { /// Initialize the Gfx module with the chosen framebuffer formats for the top and bottom
use self::Screen::*; /// screens
match g { ///
::libctru::GFX_TOP => Top, /// Use `Gfx::default()` instead of this function to initialize the module with default parameters
::libctru::GFX_BOTTOM => Bottom, pub fn new(
_ => unreachable!(), top_fb_fmt: FramebufferFormat, bottom_fb_fmt: FramebufferFormat, use_vram_buffers: bool) -> Self {
} unsafe { ::libctru::gfxInit(top_fb_fmt.into(), bottom_fb_fmt.into(), use_vram_buffers); }
} Gfx(())
}
impl From<Screen> for ::libctru::gfxScreen_t {
fn from(g: Screen) -> ::libctru::gfxScreen_t {
use self::Screen::*;
match g {
Top => ::libctru::GFX_TOP,
Bottom => ::libctru::GFX_BOTTOM,
}
}
}
impl From<::libctru::gfx3dSide_t> for Side {
fn from(s: ::libctru::gfx3dSide_t) -> Side {
use self::Side::*;
match s {
::libctru::GFX_LEFT => Left,
::libctru::GFX_RIGHT => Right,
_ => unreachable!(),
}
}
}
impl From<Side> for ::libctru::gfx3dSide_t {
fn from(s: Side) -> ::libctru::gfx3dSide_t {
use self::Side::*;
match s {
Left => ::libctru::GFX_LEFT,
Right => ::libctru::GFX_RIGHT,
}
} }
}
impl Gfx { /// Enable or disable the 3D stereoscopic effect
pub fn set_3d_enabled(&self, enabled: bool) { pub fn set_3d_enabled(&self, enabled: bool) {
unsafe { unsafe {
::libctru::gfxSet3D(enabled) ::libctru::gfxSet3D(enabled)
} }
} }
pub fn get_framebuffer(&self, screen: Screen, side: Side) -> (&'static mut [u8], u16, u16) { /// Sets whether to use double buffering. Enabled by default.
///
/// Note that even when double buffering is disabled, one should still use the `swap_buffers`
/// method on each frame to keep the gsp configuration up to date
pub fn set_double_buffering(&self, screen: Screen, enabled: bool) {
unsafe { unsafe {
use std::slice::from_raw_parts_mut; ::libctru::gfxSetDoubleBuffering(screen.into(), enabled)
let mut w: u16 = 0;
let mut h: u16 = 0;
let buf: *mut u8 = ::libctru::gfxGetFramebuffer(screen.into(),
side.into(),
&mut w as *mut u16,
&mut h as &mut u16);
let fbfmt = self.get_framebuffer_format(screen);
(from_raw_parts_mut(buf, (w as usize * h as usize) * fbfmt.pixel_depth_bytes()), w, h)
} }
} }
/// Flushes the current framebuffers
pub fn flush_buffers(&self) { pub fn flush_buffers(&self) {
unsafe { ::libctru::gfxFlushBuffers() }; unsafe { ::libctru::gfxFlushBuffers() };
} }
/// Swaps the framebuffers and sets the gsp state
///
/// Use this function when working with software rendering
pub fn swap_buffers(&self) { pub fn swap_buffers(&self) {
unsafe { ::libctru::gfxSwapBuffers() }; unsafe { ::libctru::gfxSwapBuffers() };
} }
/// Swaps the framebuffers without manipulating the gsp state
///
/// Use this function when working with GPU rendering
pub fn swap_buffers_gpu(&self) { pub fn swap_buffers_gpu(&self) {
unsafe { ::libctru::gfxSwapBuffersGpu() }; unsafe { ::libctru::gfxSwapBuffersGpu() };
} }
/// Waits for the vertical blank interrupt
///
/// Use this to synchronize your application with the refresh rate of the LCD screens
pub fn wait_for_vblank(&self) {
gspgpu::wait_for_event(gspgpu::Event::VBlank0, true);
}
/// Gets the framebuffer format for a screen
pub fn get_framebuffer_format(&self, screen: Screen) -> FramebufferFormat { pub fn get_framebuffer_format(&self, screen: Screen) -> FramebufferFormat {
unsafe { ::libctru::gfxGetScreenFormat(screen.into()).into() } unsafe { ::libctru::gfxGetScreenFormat(screen.into()).into() }
} }
pub fn set_framebuffer_format(&self, screen: Screen, /// Change the framebuffer format for a screen
fmt: FramebufferFormat) { pub fn set_framebuffer_format(&self, screen: Screen, fmt: FramebufferFormat) {
unsafe { ::libctru::gfxSetScreenFormat(screen.into(), fmt.into()) } unsafe { ::libctru::gfxSetScreenFormat(screen.into(), fmt.into()) }
} }
pub fn set_double_buffering(&self, screen: Screen, enabled: bool) { /// Returns a tuple containing a pointer to the specifified framebuffer (as determined by the
/// provided `Screen` and `Side`), the width of the framebuffer in pixels, and the height of
/// the framebuffer in pixels
///
/// Note that the pointer returned by this function can change after each call to this function
/// if double buffering is enabled
pub fn get_raw_framebuffer(&self, screen: Screen, side: Side) -> (*mut u8, u16, u16) {
unsafe { unsafe {
::libctru::gfxSetDoubleBuffering(screen.into(), enabled) let mut width: u16 = 0;
let mut height: u16 = 0;
let buf: *mut u8 = ::libctru::gfxGetFramebuffer(
screen.into(),
side.into(),
&mut width,
&mut height,
);
(buf, width, height)
} }
} }
}
pub fn wait_for_vblank(&self) { impl From<Screen> for ::libctru::gfxScreen_t {
unsafe { fn from(g: Screen) -> ::libctru::gfxScreen_t {
::libctru::gspWaitForEvent(::libctru::GSPGPU_EVENT_VBlank0, true) use self::Screen::*;
match g {
Top => ::libctru::GFX_TOP,
Bottom => ::libctru::GFX_BOTTOM,
}
}
}
impl From<Side> for ::libctru::gfx3dSide_t {
fn from(s: Side) -> ::libctru::gfx3dSide_t {
use self::Side::*;
match s {
Left => ::libctru::GFX_LEFT,
Right => ::libctru::GFX_RIGHT,
} }
} }
} }

58
ctru-rs/src/services/gspgpu.rs

@ -1,5 +1,8 @@
//! GSPGPU service
use std::convert::From; use std::convert::From;
#[derive(Copy, Clone, Debug)]
pub enum Event { pub enum Event {
Psc0, Psc0,
Psc1, Psc1,
@ -10,31 +13,46 @@ pub enum Event {
DMA, DMA,
} }
#[derive(Copy, Clone)] /// The different framebuffer formats supported by the 3DS
#[derive(Copy, Clone, Debug)]
pub enum FramebufferFormat { pub enum FramebufferFormat {
/// RGBA8. 4 bytes per pixel
Rgba8, Rgba8,
/// BGR8. 3 bytes per pixel
Bgr8, Bgr8,
/// RGB565. 2 bytes per pixel
Rgb565, Rgb565,
/// RGB5A1. 2 bytes per pixel
Rgb5A1, Rgb5A1,
/// RGBA4. 2 bytes per pixel
Rgba4, Rgba4,
} }
impl FramebufferFormat { impl FramebufferFormat {
/// Returns the number of bytes per pixel used by this FramebufferFormat
pub fn pixel_depth_bytes(&self) -> usize { pub fn pixel_depth_bytes(&self) -> usize {
use self::FramebufferFormat::*; use self::FramebufferFormat::*;
match *self { match *self {
Rgba8 => 4usize, Rgba8 => 4,
Bgr8 => 3usize, Bgr8 => 3,
Rgb565 => 2usize, Rgb565 => 2,
Rgb5A1 => 2usize, Rgb5A1 => 2,
Rgba4 => 2usize, Rgba4 => 2,
} }
} }
} }
/// Waits for a GSPGPU event to occur.
///
/// `discard_current` determines whether to discard the current event and wait for the next event
pub fn wait_for_event(ev: Event, discard_current: bool) {
unsafe {
::libctru::gspWaitForEvent(ev.into(), discard_current);
}
}
impl From<::libctru::GSPGPU_FramebufferFormats> for FramebufferFormat { impl From<::libctru::GSPGPU_FramebufferFormats> for FramebufferFormat {
#[inline] fn from(g: ::libctru::GSPGPU_FramebufferFormats) -> Self {
fn from(g: ::libctru::GSPGPU_FramebufferFormats) -> FramebufferFormat {
use self::FramebufferFormat::*; use self::FramebufferFormat::*;
match g { match g {
::libctru::GSP_RGBA8_OES => Rgba8, ::libctru::GSP_RGBA8_OES => Rgba8,
@ -48,8 +66,7 @@ impl From<::libctru::GSPGPU_FramebufferFormats> for FramebufferFormat {
} }
impl From<FramebufferFormat> for ::libctru::GSPGPU_FramebufferFormats { impl From<FramebufferFormat> for ::libctru::GSPGPU_FramebufferFormats {
#[inline] fn from(g: FramebufferFormat) -> Self {
fn from(g: FramebufferFormat) -> ::libctru::GSPGPU_FramebufferFormats {
use self::FramebufferFormat::*; use self::FramebufferFormat::*;
match g { match g {
Rgba8 => ::libctru::GSP_RGBA8_OES, Rgba8 => ::libctru::GSP_RGBA8_OES,
@ -61,9 +78,9 @@ impl From<FramebufferFormat> for ::libctru::GSPGPU_FramebufferFormats {
} }
} }
fn to_raw_event(ev: Event) -> ::libctru::GSPGPU_Event { impl From<Event> for ::libctru::GSPGPU_Event {
fn from(ev: Event) -> Self {
use self::Event::*; use self::Event::*;
match ev { match ev {
Psc0 => ::libctru::GSPGPU_EVENT_PSC0, Psc0 => ::libctru::GSPGPU_EVENT_PSC0,
Psc1 => ::libctru::GSPGPU_EVENT_PSC1, Psc1 => ::libctru::GSPGPU_EVENT_PSC1,
@ -73,22 +90,5 @@ fn to_raw_event(ev: Event) -> ::libctru::GSPGPU_Event {
P3D => ::libctru::GSPGPU_EVENT_P3D, P3D => ::libctru::GSPGPU_EVENT_P3D,
DMA => ::libctru::GSPGPU_EVENT_DMA, DMA => ::libctru::GSPGPU_EVENT_DMA,
} }
}
/// Sleep until GSP event fires.
///
/// # Examples
///
/// Wait for VBlank.
///
/// ```
/// use ctru::services::apt;
/// apt::main_loop(|| {
/// wait_for_event(Event::VBlank0);
/// });
pub fn wait_for_event(ev: Event) -> () {
unsafe {
// TODO second argument?
::libctru::gspWaitForEvent(to_raw_event(ev), false);
} }
} }

Loading…
Cancel
Save