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 @@ @@ -1,9 +1,8 @@
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::hid::KeyPad;
use ctru::services::{Apt, Hid};
use ctru::Gfx;
use std::time::Duration;
const WIDTH: usize = 400;
@ -89,7 +88,7 @@ fn main() { @@ -89,7 +88,7 @@ fn main() {
unsafe {
gfx.top_screen
.borrow_mut()
.get_raw_framebuffer(Side::Left)
.get_raw_framebuffer()
.ptr
.copy_from(img.as_ptr(), img.len());
}

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

@ -0,0 +1,72 @@ @@ -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 @@ @@ -1,4 +1,3 @@
use ctru::gfx::Side;
use ctru::prelude::*;
use std::io::BufWriter;
@ -8,7 +7,7 @@ fn main() { @@ -8,7 +7,7 @@ fn main() {
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.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 width = 24;

140
ctru-rs/src/gfx.rs

@ -9,10 +9,20 @@ use crate::error::Result; @@ -9,10 +9,20 @@ use crate::error::Result;
use crate::services::gspgpu::{self, FramebufferFormat};
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
/// drawing to the screens. Graphics-related code can be made generic over this
/// 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
fn as_raw(&self) -> ctru_sys::gfxScreen_t;
@ -41,23 +51,23 @@ pub trait Screen { @@ -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]
pub struct TopLeftScreen;
#[non_exhaustive]
pub struct TopRightScreen;
pub struct TopScreen;
/// A helper container for both sides of the top screen.
pub struct TopScreenInner {
left: TopLeftScreen,
right: TopRightScreen,
pub struct TopScreen3D<'a> {
// morally, this should be &mut or RefMut, but if we do
// - &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]
pub struct TopScreen(RefCell<TopScreenInner>);
// TODO: it feels a little weird to have an asymmetric separate type like this,
// but maybe if it's not `pub` it's not as weird...
struct TopScreenRight;
#[non_exhaustive]
pub struct BottomScreen;
@ -81,7 +91,7 @@ pub struct RawFrameBuffer<'screen> { @@ -81,7 +91,7 @@ pub struct RawFrameBuffer<'screen> {
/// 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 {
enum Side {
/// The left framebuffer. This framebuffer is also the one used when 3D is disabled
Left,
/// The right framebuffer
@ -94,7 +104,7 @@ pub enum Side { @@ -94,7 +104,7 @@ pub enum Side {
/// The service exits when this struct is dropped.
#[non_exhaustive]
pub struct Gfx {
pub top_screen: TopScreen,
pub top_screen: RefCell<TopScreen>,
pub bottom_screen: RefCell<BottomScreen>,
_service_handler: ServiceReference,
}
@ -123,7 +133,7 @@ impl Gfx { @@ -123,7 +133,7 @@ impl Gfx {
)?;
Ok(Self {
top_screen: TopScreen::new(TopLeftScreen, TopRightScreen),
top_screen: RefCell::new(TopScreen),
bottom_screen: RefCell::new(BottomScreen),
_service_handler,
})
@ -163,87 +173,73 @@ impl Gfx { @@ -163,87 +173,73 @@ impl Gfx {
}
}
impl TopScreen {
fn new(left: TopLeftScreen, right: TopRightScreen) -> Self {
Self(RefCell::new(TopScreenInner { left, right }))
impl<'screen> RawFrameBuffer<'screen> {
fn for_screen_side(screen: &'screen mut dyn Screen, side: Side) -> Self {
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> {
self.0.borrow()
}
}
pub fn borrow_mut(&self) -> RefMut<'_, TopScreenInner> {
self.0.borrow_mut()
impl TopScreen3D<'_> {
pub fn left(&self) -> Ref<dyn Screen> {
self.left.borrow()
}
pub fn borrow_side(&self, side: Side) -> Ref<'_, dyn Screen> {
let borrow = self.0.borrow();
match side {
Side::Left => Ref::map(borrow, |top| &top.left),
Side::Right => Ref::map(borrow, |top| &top.right),
}
pub fn left_mut(&self) -> RefMut<dyn Screen> {
self.left.borrow_mut()
}
pub fn borrow_side_mut(&self, side: Side) -> RefMut<'_, dyn Screen> {
let borrow = self.0.borrow_mut();
match side {
Side::Left => RefMut::map(borrow, |top| &mut top.left),
Side::Right => RefMut::map(borrow, |top| &mut top.right),
pub fn right(&self) -> Ref<dyn Screen> {
self.right.borrow()
}
}
}
impl TopScreenInner {
/// Enable or disable the 3D stereoscopic effect
pub fn set_3d_enabled(&mut self, enabled: bool) {
unsafe {
ctru_sys::gfxSet3D(enabled);
}
pub fn right_mut(&self) -> RefMut<dyn Screen> {
self.right.borrow_mut()
}
}
/// Enable or disable the wide screen mode (top screen).
/// This only works when 3D is disabled.
pub fn set_wide_mode(&mut self, enabled: bool) {
impl<'a> From<&'a RefCell<TopScreen>> for TopScreen3D<'a> {
fn from(top_screen: &'a RefCell<TopScreen>) -> Self {
unsafe {
ctru_sys::gfxSetWide(enabled);
ctru_sys::gfxSet3D(true);
}
TopScreen3D {
left: top_screen,
right: RefCell::new(TopScreenRight),
}
/// Get the status of wide screen mode.
pub fn get_wide_mode(&self) -> bool {
unsafe { ctru_sys::gfxIsWide() }
}
}
impl<'screen> RawFrameBuffer<'screen> {
fn for_screen_side(screen: &'screen mut dyn Screen, side: Side) -> Self {
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,
impl Drop for TopScreen3D<'_> {
fn drop(&mut self) {
unsafe {
ctru_sys::gfxSet3D(false);
}
}
}
impl Screen for TopScreenInner {
fn as_raw(&self) -> ctru_sys::gfxScreen_t {
self.left.as_raw()
impl TopScreen {
pub fn set_wide_mode(&mut self, enable: bool) {
unsafe {
ctru_sys::gfxSetWide(enable);
}
}
fn get_raw_framebuffer(&mut self) -> RawFrameBuffer {
// When dealing with the "whole" top screen, the left framebuffer is the
// one used for writing pixel data.
self.left.get_raw_framebuffer()
pub fn get_wide_mode(&self) -> bool {
unsafe { ctru_sys::gfxIsWide() }
}
}
impl Screen for TopLeftScreen {
impl Screen for TopScreen {
fn as_raw(&self) -> ctru_sys::gfxScreen_t {
ctru_sys::GFX_TOP
}
@ -253,7 +249,7 @@ impl Screen for TopLeftScreen { @@ -253,7 +249,7 @@ impl Screen for TopLeftScreen {
}
}
impl Screen for TopRightScreen {
impl Screen for TopScreenRight {
fn as_raw(&self) -> ctru_sys::gfxScreen_t {
ctru_sys::GFX_TOP
}

Loading…
Cancel
Save