Browse Source

Update doc comments and bitmap example

Clarify the behavior of `swap_buffers` for single buffering mode and
update bitmap example to prove that this is actually how it works.
pull/118/head
Ian Chamberlain 2 years ago
parent
commit
b239fb8d9f
No known key found for this signature in database
GPG Key ID: AE5484D09405AA60
  1. 35
      ctru-rs/examples/gfx-bitmap.rs
  2. 47
      ctru-rs/src/services/gfx.rs

35
ctru-rs/examples/graphics-bitmap.rs → ctru-rs/examples/gfx-bitmap.rs

@ -22,21 +22,20 @@ fn main() {
let apt = Apt::new().expect("Couldn't obtain APT controller"); let apt = Apt::new().expect("Couldn't obtain APT controller");
let _console = Console::new(gfx.top_screen.borrow_mut()); let _console = Console::new(gfx.top_screen.borrow_mut());
println!("\x1b[21;16HPress Start to exit."); println!("\x1b[21;4HPress Start to exit, or A to flip the image.");
let mut bottom_screen = gfx.bottom_screen.borrow_mut(); let mut bottom_screen = gfx.bottom_screen.borrow_mut();
// We don't need double buffering in this example. // We don't need double buffering in this example.
// In this way we can draw our image only once on screen. // In this way we can draw our image only once on screen.
bottom_screen.set_double_buffering(false); bottom_screen.set_double_buffering(false);
// Swapping buffers commits the change from the line above.
bottom_screen.swap_buffers();
// We assume the image is the correct size already, so we drop width + height. // 3 bytes per pixel, we just want to reverse the pixels but not individual bytes
let frame_buffer = bottom_screen.raw_framebuffer(); let flipped_image: Vec<_> = IMAGE.chunks(3).rev().flatten().copied().collect();
// Copy the image into the frame buffer let mut image_bytes = IMAGE;
unsafe {
frame_buffer.ptr.copy_from(IMAGE.as_ptr(), IMAGE.len());
}
// Main loop // Main loop
while apt.main_loop() { while apt.main_loop() {
@ -47,9 +46,27 @@ fn main() {
break; break;
} }
// Flush and swap framebuffers // We assume the image is the correct size already, so we drop width + height.
let frame_buffer = bottom_screen.raw_framebuffer();
if hid.keys_down().contains(KeyPad::A) {
image_bytes = if std::ptr::eq(image_bytes, IMAGE) {
&flipped_image[..]
} else {
IMAGE
};
}
// this copies more than necessary (once per frame) but it's fine...
unsafe {
frame_buffer
.ptr
.copy_from(image_bytes.as_ptr(), image_bytes.len());
}
// Flush framebuffers. Since we're not using double buffering,
// this will render the pixels immediately
bottom_screen.flush_buffers(); bottom_screen.flush_buffers();
bottom_screen.swap_buffers();
//Wait for VBlank //Wait for VBlank
gfx.wait_for_vblank(); gfx.wait_for_vblank();

47
ctru-rs/src/services/gfx.rs

@ -50,18 +50,21 @@ pub trait Screen: private::Sealed {
/// Sets whether to use double buffering. Enabled by default. /// Sets whether to use double buffering. Enabled by default.
/// ///
/// Note that even when double buffering is disabled, one should still use the `swap_buffers` /// [`Swap::swap_buffers`] must be called after this function for the configuration
/// method on each frame to keep the gsp configuration up to date /// change to take effect.
fn set_double_buffering(&mut self, enabled: bool) { fn set_double_buffering(&mut self, enabled: bool) {
unsafe { ctru_sys::gfxSetDoubleBuffering(self.as_raw(), enabled) } unsafe { ctru_sys::gfxSetDoubleBuffering(self.as_raw(), enabled) }
} }
/// Gets the framebuffer format /// Gets the framebuffer format.
fn framebuffer_format(&self) -> FramebufferFormat { fn framebuffer_format(&self) -> FramebufferFormat {
unsafe { ctru_sys::gfxGetScreenFormat(self.as_raw()) }.into() unsafe { ctru_sys::gfxGetScreenFormat(self.as_raw()) }.into()
} }
/// Change the framebuffer format /// Change the framebuffer format.
///
/// [`Swap::swap_buffers`] must be called after this method for the configuration
/// change to take effect.
fn set_framebuffer_format(&mut self, fmt: FramebufferFormat) { fn set_framebuffer_format(&mut self, fmt: FramebufferFormat) {
unsafe { ctru_sys::gfxSetScreenFormat(self.as_raw(), fmt.into()) } unsafe { ctru_sys::gfxSetScreenFormat(self.as_raw(), fmt.into()) }
} }
@ -76,14 +79,25 @@ pub struct TopScreen {
/// A helper container for both sides of the top screen. Once the [`TopScreen`] is /// A helper container for both sides of the top screen. Once the [`TopScreen`] is
/// converted into this, 3D mode will be enabled until this struct is dropped. /// converted into this, 3D mode will be enabled until this struct is dropped.
pub struct TopScreen3D<'top_screen> { pub struct TopScreen3D<'screen> {
screen: &'top_screen RefCell<TopScreen>, screen: &'screen RefCell<TopScreen>,
} }
/// A screen that can have its frame buffers swapped, if double buffering is enabled.
///
/// This trait applies to all [`Screen`]s that have swappable frame buffers.
pub trait Swap: private::Sealed { pub trait Swap: private::Sealed {
/// Swaps the video buffers. /// Swaps the video buffers.
/// ///
/// This should be used even if double buffering is disabled. /// If double buffering is disabled, "swapping" the buffers has the side effect
/// of committing any configuration changes to the buffers (e.g. [`set_wide_mode`],
/// [`set_framebuffer_format`], [`set_double_buffering`]).
///
/// This should be called once per frame at most.
///
/// [`set_wide_mode`]: TopScreen::set_wide_mode
/// [`set_framebuffer_format`]: Screen::set_framebuffer_format
/// [`set_double_buffering`]: Screen::set_double_buffering
fn swap_buffers(&mut self); fn swap_buffers(&mut self);
} }
@ -111,6 +125,8 @@ impl Swap for BottomScreen {
} }
} }
/// A screen with buffers that can be flushed. This trait applies to any [`Screen`]
/// that has data written to its frame buffer.
pub trait Flush: private::Sealed { pub trait Flush: private::Sealed {
/// Flushes the video buffer(s) for this screen. Note that you must still call /// Flushes the video buffer(s) for this screen. Note that you must still call
/// [`Swap::swap_buffers`] after this method for the buffer contents to be displayed. /// [`Swap::swap_buffers`] after this method for the buffer contents to be displayed.
@ -242,21 +258,19 @@ impl Gfx {
impl TopScreen3D<'_> { impl TopScreen3D<'_> {
/// Immutably borrow the two sides of the screen as `(left, right)`. /// Immutably borrow the two sides of the screen as `(left, right)`.
pub fn split(&self) -> (Ref<TopScreenLeft>, Ref<TopScreenRight>) { pub fn split(&self) -> (Ref<TopScreenLeft>, Ref<TopScreenRight>) {
Ref::map_split(self.screen.borrow(), |screen| { Ref::map_split(self.screen.borrow(), |screen| (&screen.left, &screen.right))
(&screen.left as _, &screen.right as _)
})
} }
/// Mutably borrow the two sides of the screen as `(left, right)`. /// Mutably borrow the two sides of the screen as `(left, right)`.
pub fn split_mut(&self) -> (RefMut<TopScreenLeft>, RefMut<TopScreenRight>) { pub fn split_mut(&self) -> (RefMut<TopScreenLeft>, RefMut<TopScreenRight>) {
RefMut::map_split(self.screen.borrow_mut(), |screen| { RefMut::map_split(self.screen.borrow_mut(), |screen| {
(&mut screen.left as _, &mut screen.right as _) (&mut screen.left, &mut screen.right)
}) })
} }
} }
impl<'top_screen> From<&'top_screen RefCell<TopScreen>> for TopScreen3D<'top_screen> { impl<'screen> From<&'screen RefCell<TopScreen>> for TopScreen3D<'screen> {
fn from(top_screen: &'top_screen RefCell<TopScreen>) -> Self { fn from(top_screen: &'screen RefCell<TopScreen>) -> Self {
unsafe { unsafe {
ctru_sys::gfxSet3D(true); ctru_sys::gfxSet3D(true);
} }
@ -282,6 +296,9 @@ impl TopScreen {
} }
/// Enable or disable wide mode on the top screen. /// Enable or disable wide mode on the top screen.
///
/// [`Swap::swap_buffers`] must be called after this method for the configuration
/// to take effect.
pub fn set_wide_mode(&mut self, enable: bool) { pub fn set_wide_mode(&mut self, enable: bool) {
unsafe { unsafe {
ctru_sys::gfxSetWide(enable); ctru_sys::gfxSetWide(enable);
@ -294,9 +311,11 @@ impl TopScreen {
} }
} }
// When 3D mode is disabled, only the left side is used, so this Screen impl
// just forwards everything to the TopScreenLeft.
impl Screen for TopScreen { impl Screen for TopScreen {
fn as_raw(&self) -> ctru_sys::gfxScreen_t { fn as_raw(&self) -> ctru_sys::gfxScreen_t {
ctru_sys::GFX_TOP self.left.as_raw()
} }
fn side(&self) -> Side { fn side(&self) -> Side {

Loading…
Cancel
Save