Browse Source

Implement a TopScreen3D type for 3d mode

This type sets 3d mode on construction, and unsets it on drop, while
keeping a copy of the RefCell to the top screen.

I don't think this is perfect, but it seems like a decent option that
should still prevent any concurrent access to the top screen's buffer.
pull/76/head
Ian Chamberlain 2 years ago
parent
commit
6c7a74f5f6
No known key found for this signature in database
GPG Key ID: AE5484D09405AA60
  1. 5
      ctru-rs/examples/camera-image.rs
  2. 72
      ctru-rs/examples/gfx-3d-mode.rs
  3. 3
      ctru-rs/examples/hello-world.rs
  4. 140
      ctru-rs/src/gfx.rs

5
ctru-rs/examples/camera-image.rs

@ -1,9 +1,8 @@
use ctru::console::Console; use ctru::console::Console;
use ctru::gfx::{Screen, Side}; use ctru::gfx::{Gfx, Screen};
use ctru::services::cam::{Cam, CamOutputFormat, CamShutterSoundType, CamSize, Camera}; use ctru::services::cam::{Cam, CamOutputFormat, CamShutterSoundType, CamSize, Camera};
use ctru::services::hid::KeyPad; use ctru::services::hid::KeyPad;
use ctru::services::{Apt, Hid}; use ctru::services::{Apt, Hid};
use ctru::Gfx;
use std::time::Duration; use std::time::Duration;
const WIDTH: usize = 400; const WIDTH: usize = 400;
@ -89,7 +88,7 @@ fn main() {
unsafe { unsafe {
gfx.top_screen gfx.top_screen
.borrow_mut() .borrow_mut()
.get_raw_framebuffer(Side::Left) .get_raw_framebuffer()
.ptr .ptr
.copy_from(img.as_ptr(), img.len()); .copy_from(img.as_ptr(), img.len());
} }

72
ctru-rs/examples/gfx-3d-mode.rs

@ -0,0 +1,72 @@
use ctru::gfx::TopScreen3D;
use ctru::prelude::*;
/// See `graphics-bitmap.rs` for details on how the image is generated.
///
/// WARNING: this example uses 3D mode in a rather unnatural way, and should
/// probably not be viewed for too long or at all if you are photosensitive.
const IMAGE: &[u8] = include_bytes!("assets/ferris.rgb");
const ZERO: &[u8] = &[0; IMAGE.len()];
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 _console = Console::init(gfx.bottom_screen.borrow_mut());
println!("Press Start to exit.\nPress A to switch which side is drawn to.");
let top_screen = TopScreen3D::from(&gfx.top_screen);
// TODO set double buffering for top screen
let mut left = top_screen.left_mut();
let left_buf = left.get_raw_framebuffer();
let mut right = top_screen.right_mut();
let right_buf = right.get_raw_framebuffer();
// We assume the image is the correct size already, so we ignore width + height.
let mut buf = left_buf.ptr;
// Copy the image into the left-side frame buffer
unsafe {
buf.copy_from(IMAGE.as_ptr(), IMAGE.len());
}
// Main loop
while apt.main_loop() {
//Scan all the inputs. This should be done once for each frame
hid.scan_input();
if hid.keys_down().contains(KeyPad::KEY_START) {
break;
}
if hid.keys_down().contains(KeyPad::KEY_A) {
// Clear the side we just drew to by zeroing it out
unsafe {
buf.copy_from(ZERO.as_ptr(), ZERO.len());
}
// flip which buffer we're writing to, and redraw the image
buf = if buf == left_buf.ptr {
right_buf.ptr
} else {
left_buf.ptr
};
unsafe {
buf.copy_from(IMAGE.as_ptr(), IMAGE.len());
}
}
// Flush and swap framebuffers
gfx.flush_buffers();
gfx.swap_buffers();
//Wait for VBlank
gfx.wait_for_vblank();
}
}

3
ctru-rs/examples/hello-world.rs

@ -1,4 +1,3 @@
use ctru::gfx::Side;
use ctru::prelude::*; use ctru::prelude::*;
use std::io::BufWriter; use std::io::BufWriter;
@ -8,7 +7,7 @@ fn main() {
let gfx = Gfx::init().expect("Couldn't obtain GFX controller"); let gfx = Gfx::init().expect("Couldn't obtain GFX controller");
let hid = Hid::init().expect("Couldn't obtain HID controller"); let hid = Hid::init().expect("Couldn't obtain HID controller");
let apt = Apt::init().expect("Couldn't obtain APT controller"); let apt = Apt::init().expect("Couldn't obtain APT controller");
let _console = Console::init(gfx.top_screen.borrow_side_mut(Side::Left)); let _console = Console::init(gfx.top_screen.borrow_mut());
let out = b"Hello fellow Rustaceans, I'm on the Nintendo 3DS!"; let out = b"Hello fellow Rustaceans, I'm on the Nintendo 3DS!";
let width = 24; let width = 24;

140
ctru-rs/src/gfx.rs

@ -9,10 +9,20 @@ use crate::error::Result;
use crate::services::gspgpu::{self, FramebufferFormat}; use crate::services::gspgpu::{self, FramebufferFormat};
use crate::services::ServiceReference; use crate::services::ServiceReference;
mod private {
use super::{BottomScreen, TopScreen, TopScreenRight};
pub trait Sealed {}
impl Sealed for TopScreen {}
impl Sealed for TopScreenRight {}
impl Sealed for BottomScreen {}
}
/// This trait is implemented by the screen structs for working with frame buffers and /// This trait is implemented by the screen structs for working with frame buffers and
/// drawing to the screens. Graphics-related code can be made generic over this /// drawing to the screens. Graphics-related code can be made generic over this
/// trait to work with any of the given screens. /// trait to work with any of the given screens.
pub trait Screen { pub trait Screen: private::Sealed {
/// Returns the libctru value for the Screen kind /// Returns the libctru value for the Screen kind
fn as_raw(&self) -> ctru_sys::gfxScreen_t; fn as_raw(&self) -> ctru_sys::gfxScreen_t;
@ -41,23 +51,23 @@ pub trait Screen {
} }
} }
// TODO: it might be nice to do `TopScreen<const SIDE: Side>` but it requires
// #![feature(adt_const_params)] which is unstable, and doesn't seem worth it
// just for this.
#[non_exhaustive] #[non_exhaustive]
pub struct TopLeftScreen; pub struct TopScreen;
#[non_exhaustive]
pub struct TopRightScreen;
/// A helper container for both sides of the top screen. /// A helper container for both sides of the top screen.
pub struct TopScreenInner { pub struct TopScreen3D<'a> {
left: TopLeftScreen, // morally, this should be &mut or RefMut, but if we do
right: TopRightScreen, // - &mut: it means gfx can no longer be borrowed immutably while this exists
// - RefMut: we don't have an easy way to obtain Ref<dyn Screen> for the left side.
// maybe this one isn't as important since the use case is typically RefMut anyway.
// we could just return &dyn Screen instead of Ref<dyn Screen> ?
left: &'a RefCell<TopScreen>,
right: RefCell<TopScreenRight>,
} }
#[non_exhaustive] // TODO: it feels a little weird to have an asymmetric separate type like this,
pub struct TopScreen(RefCell<TopScreenInner>); // but maybe if it's not `pub` it's not as weird...
struct TopScreenRight;
#[non_exhaustive] #[non_exhaustive]
pub struct BottomScreen; pub struct BottomScreen;
@ -81,7 +91,7 @@ pub struct RawFrameBuffer<'screen> {
/// Side of top screen framebuffer /// Side of top screen framebuffer
/// ///
/// The top screen of the 3DS can have two separate sets of framebuffers to support its 3D functionality /// The top screen of the 3DS can have two separate sets of framebuffers to support its 3D functionality
pub enum Side { enum Side {
/// The left framebuffer. This framebuffer is also the one used when 3D is disabled /// The left framebuffer. This framebuffer is also the one used when 3D is disabled
Left, Left,
/// The right framebuffer /// The right framebuffer
@ -94,7 +104,7 @@ pub enum Side {
/// The service exits when this struct is dropped. /// The service exits when this struct is dropped.
#[non_exhaustive] #[non_exhaustive]
pub struct Gfx { pub struct Gfx {
pub top_screen: TopScreen, pub top_screen: RefCell<TopScreen>,
pub bottom_screen: RefCell<BottomScreen>, pub bottom_screen: RefCell<BottomScreen>,
_service_handler: ServiceReference, _service_handler: ServiceReference,
} }
@ -123,7 +133,7 @@ impl Gfx {
)?; )?;
Ok(Self { Ok(Self {
top_screen: TopScreen::new(TopLeftScreen, TopRightScreen), top_screen: RefCell::new(TopScreen),
bottom_screen: RefCell::new(BottomScreen), bottom_screen: RefCell::new(BottomScreen),
_service_handler, _service_handler,
}) })
@ -163,87 +173,73 @@ impl Gfx {
} }
} }
impl TopScreen { impl<'screen> RawFrameBuffer<'screen> {
fn new(left: TopLeftScreen, right: TopRightScreen) -> Self { fn for_screen_side(screen: &'screen mut dyn Screen, side: Side) -> Self {
Self(RefCell::new(TopScreenInner { left, right })) let mut width = 0;
let mut height = 0;
let ptr = unsafe {
ctru_sys::gfxGetFramebuffer(screen.as_raw(), side.into(), &mut width, &mut height)
};
Self {
ptr,
width,
height,
screen: PhantomData,
}
} }
}
pub fn borrow(&self) -> Ref<'_, TopScreenInner> { impl TopScreen3D<'_> {
self.0.borrow() pub fn left(&self) -> Ref<dyn Screen> {
self.left.borrow()
} }
pub fn borrow_mut(&self) -> RefMut<'_, TopScreenInner> { pub fn left_mut(&self) -> RefMut<dyn Screen> {
self.0.borrow_mut() self.left.borrow_mut()
} }
pub fn borrow_side(&self, side: Side) -> Ref<'_, dyn Screen> { pub fn right(&self) -> Ref<dyn Screen> {
let borrow = self.0.borrow(); self.right.borrow()
match side {
Side::Left => Ref::map(borrow, |top| &top.left),
Side::Right => Ref::map(borrow, |top| &top.right),
}
} }
pub fn borrow_side_mut(&self, side: Side) -> RefMut<'_, dyn Screen> { pub fn right_mut(&self) -> RefMut<dyn Screen> {
let borrow = self.0.borrow_mut(); self.right.borrow_mut()
match side {
Side::Left => RefMut::map(borrow, |top| &mut top.left),
Side::Right => RefMut::map(borrow, |top| &mut top.right),
}
} }
} }
impl TopScreenInner { impl<'a> From<&'a RefCell<TopScreen>> for TopScreen3D<'a> {
/// Enable or disable the 3D stereoscopic effect fn from(top_screen: &'a RefCell<TopScreen>) -> Self {
pub fn set_3d_enabled(&mut self, enabled: bool) {
unsafe { unsafe {
ctru_sys::gfxSet3D(enabled); ctru_sys::gfxSet3D(true);
} }
} TopScreen3D {
left: top_screen,
/// Enable or disable the wide screen mode (top screen). right: RefCell::new(TopScreenRight),
/// This only works when 3D is disabled.
pub fn set_wide_mode(&mut self, enabled: bool) {
unsafe {
ctru_sys::gfxSetWide(enabled);
} }
} }
/// Get the status of wide screen mode.
pub fn get_wide_mode(&self) -> bool {
unsafe { ctru_sys::gfxIsWide() }
}
} }
impl<'screen> RawFrameBuffer<'screen> { impl Drop for TopScreen3D<'_> {
fn for_screen_side(screen: &'screen mut dyn Screen, side: Side) -> Self { fn drop(&mut self) {
let mut width = 0; unsafe {
let mut height = 0; ctru_sys::gfxSet3D(false);
let ptr = unsafe {
ctru_sys::gfxGetFramebuffer(screen.as_raw(), side.into(), &mut width, &mut height)
};
Self {
ptr,
width,
height,
screen: PhantomData,
} }
} }
} }
impl Screen for TopScreenInner { impl TopScreen {
fn as_raw(&self) -> ctru_sys::gfxScreen_t { pub fn set_wide_mode(&mut self, enable: bool) {
self.left.as_raw() unsafe {
ctru_sys::gfxSetWide(enable);
}
} }
fn get_raw_framebuffer(&mut self) -> RawFrameBuffer { pub fn get_wide_mode(&self) -> bool {
// When dealing with the "whole" top screen, the left framebuffer is the unsafe { ctru_sys::gfxIsWide() }
// one used for writing pixel data.
self.left.get_raw_framebuffer()
} }
} }
impl Screen for TopLeftScreen { impl Screen for TopScreen {
fn as_raw(&self) -> ctru_sys::gfxScreen_t { fn as_raw(&self) -> ctru_sys::gfxScreen_t {
ctru_sys::GFX_TOP ctru_sys::GFX_TOP
} }
@ -253,7 +249,7 @@ impl Screen for TopLeftScreen {
} }
} }
impl Screen for TopRightScreen { impl Screen for TopScreenRight {
fn as_raw(&self) -> ctru_sys::gfxScreen_t { fn as_raw(&self) -> ctru_sys::gfxScreen_t {
ctru_sys::GFX_TOP ctru_sys::GFX_TOP
} }

Loading…
Cancel
Save