Browse Source

Fixed Screen unsafety on VRAM

pull/137/head
Andrea Ciliberti 1 year ago
parent
commit
a84d2045f2
  1. 7
      ctru-rs/src/console.rs
  2. 81
      ctru-rs/src/services/gfx.rs
  3. 2
      ctru-rs/src/services/romfs.rs

7
ctru-rs/src/console.rs

@ -75,6 +75,11 @@ impl<'screen> Console<'screen> {
/// ///
/// [`Console`] automatically takes care of flushing and swapping buffers for its screen when printing. /// [`Console`] automatically takes care of flushing and swapping buffers for its screen when printing.
/// ///
/// # Panics
///
/// If the [`Gfx`](crate::services::gfx::Gfx) service was initialised via [`Gfx::with_formats_vram()`](crate::services::gfx::Gfx::with_formats_vram)
/// this function will crash the program with an ARM exception.
///
/// # Example /// # Example
/// ///
/// ```no_run /// ```no_run
@ -84,7 +89,7 @@ impl<'screen> Console<'screen> {
/// use ctru::services::gfx::Gfx; /// use ctru::services::gfx::Gfx;
/// use ctru::console::Console; /// use ctru::console::Console;
/// ///
/// // Initialize graphics. /// // Initialize graphics (using framebuffers allocated on the HEAP).
/// let gfx = Gfx::new()?; /// let gfx = Gfx::new()?;
/// ///
/// // Create a `Console` that takes control of the upper LCD screen. /// // Create a `Console` that takes control of the upper LCD screen.

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

@ -37,10 +37,16 @@ pub trait Screen: private::Sealed {
/// Returns the Screen side (left or right). /// Returns the Screen side (left or right).
fn side(&self) -> Side; fn side(&self) -> Side;
/// Returns a [`RawFrameBuffer`] for the screen. /// Returns a [`RawFrameBuffer`] for the screen (if the framebuffer was allocated on the HEAP).
/// ///
/// Note that the pointer of the framebuffer returned by this function can /// # Notes
/// change after each call to this function if double buffering is enabled. ///
/// The pointer of the framebuffer returned by this function can change after each call
/// to this function if double buffering is enabled, so it's suggested to NOT save it for later use.
///
/// # Panics
///
/// If the [`Gfx`] service was initialised via [`Gfx::with_formats_vram()`] this function will crash the program with an ARM exception.
#[doc(alias = "gfxGetFramebuffer")] #[doc(alias = "gfxGetFramebuffer")]
fn raw_framebuffer(&mut self) -> RawFrameBuffer { fn raw_framebuffer(&mut self) -> RawFrameBuffer {
let mut width: u16 = 0; let mut width: u16 = 0;
@ -251,7 +257,8 @@ impl Gfx {
/// ///
/// # Notes /// # Notes
/// ///
/// It's the same as calling: /// The new `Gfx` instance will allocate the needed framebuffers in the CPU-GPU shared memory region (to ensure compatibiltiy with all possible uses of the `Gfx` service).
/// As such, it's the same as calling:
/// ///
/// ```no_run /// ```no_run
/// # use std::error::Error; /// # use std::error::Error;
@ -260,12 +267,14 @@ impl Gfx {
/// # use ctru::services::gfx::Gfx; /// # use ctru::services::gfx::Gfx;
/// # use ctru::services::gspgpu::FramebufferFormat; /// # use ctru::services::gspgpu::FramebufferFormat;
/// # /// #
/// Gfx::with_formats(FramebufferFormat::Bgr8, FramebufferFormat::Bgr8, false)?; /// Gfx::with_formats_shared(FramebufferFormat::Bgr8, FramebufferFormat::Bgr8)?;
/// # /// #
/// # Ok(()) /// # Ok(())
/// # } /// # }
/// ``` /// ```
/// ///
/// Have a look at [`Gfx::with_formats_vram()`] if you aren't interested in manipulating the framebuffers using the CPU.
///
/// # Example /// # Example
/// ///
/// ```no_run /// ```no_run
@ -281,10 +290,10 @@ impl Gfx {
/// ``` /// ```
#[doc(alias = "gfxInit")] #[doc(alias = "gfxInit")]
pub fn new() -> Result<Self> { pub fn new() -> Result<Self> {
Gfx::with_formats(FramebufferFormat::Bgr8, FramebufferFormat::Bgr8, false) Gfx::with_formats_shared(FramebufferFormat::Bgr8, FramebufferFormat::Bgr8)
} }
/// Initialize a new service handle with the chosen framebuffer formats for the top and bottom screens. /// Initialize a new service handle with the chosen framebuffer formats on the HEAP for the top and bottom screens.
/// ///
/// Use [`Gfx::new()`] instead of this function to initialize the module with default parameters /// Use [`Gfx::new()`] instead of this function to initialize the module with default parameters
/// ///
@ -298,21 +307,71 @@ impl Gfx {
/// ///
/// // Top screen uses RGBA8, bottom screen uses RGB565. /// // Top screen uses RGBA8, bottom screen uses RGB565.
/// // The screen buffers are allocated in the standard HEAP memory, and not in VRAM. /// // The screen buffers are allocated in the standard HEAP memory, and not in VRAM.
/// let gfx = Gfx::with_formats(FramebufferFormat::Rgba8, FramebufferFormat::Rgb565, false)?; /// let gfx = Gfx::with_formats_shared(FramebufferFormat::Rgba8, FramebufferFormat::Rgb565)?;
/// #
/// # Ok(())
/// # }
/// ```
#[doc(alias = "gfxInit")]
pub fn with_formats_shared(
top_fb_fmt: FramebufferFormat,
bottom_fb_fmt: FramebufferFormat,
) -> Result<Self> {
let handler = ServiceReference::new(
&GFX_ACTIVE,
|| unsafe {
ctru_sys::gfxInit(top_fb_fmt.into(), bottom_fb_fmt.into(), false);
Ok(())
},
|| unsafe { ctru_sys::gfxExit() },
)?;
Ok(Self {
top_screen: RefCell::new(TopScreen::new()),
bottom_screen: RefCell::new(BottomScreen),
_service_handler: handler,
})
}
/// Initialize a new service handle with the chosen framebuffer formats on the VRAM for the top and bottom screens.
///
/// # Notes
///
/// Though unsafe to do so, it's suggested to use VRAM buffers when working exclusively with the GPU,
/// since they result in faster performance and less memory waste.
///
/// # Safety
///
/// By initializing the [`Gfx`] service as such, all functionality that relies on CPU manipulation of the framebuffers will
/// be completely unavailable (usually resulting in an ARM panic if wrongly used).
///
/// Things such as [`Console`](crate::console::Console) and [`Screen::raw_framebuffer()`] will result in ARM exceptions.
///
/// # Example
///
/// ```no_run
/// # use std::error::Error;
/// # fn main() -> Result<(), Box<dyn Error>> {
/// #
/// use ctru::services::{gfx::Gfx, gspgpu::FramebufferFormat};
///
/// // Top screen uses RGBA8, bottom screen uses RGB565.
/// // The screen buffers are allocated in the in VRAM, so they will NOT be accessible from the CPU.
/// let gfx = unsafe { Gfx::with_formats_vram(FramebufferFormat::Rgba8, FramebufferFormat::Rgb565)? };
/// # /// #
/// # Ok(()) /// # Ok(())
/// # } /// # }
/// ``` /// ```
#[doc(alias = "gfxInit")] #[doc(alias = "gfxInit")]
pub fn with_formats( pub unsafe fn with_formats_vram(
top_fb_fmt: FramebufferFormat, top_fb_fmt: FramebufferFormat,
bottom_fb_fmt: FramebufferFormat, bottom_fb_fmt: FramebufferFormat,
use_vram_buffers: bool,
) -> Result<Self> { ) -> Result<Self> {
let handler = ServiceReference::new( let handler = ServiceReference::new(
&GFX_ACTIVE, &GFX_ACTIVE,
|| unsafe { || unsafe {
ctru_sys::gfxInit(top_fb_fmt.into(), bottom_fb_fmt.into(), use_vram_buffers); ctru_sys::gfxInit(top_fb_fmt.into(), bottom_fb_fmt.into(), true);
Ok(()) Ok(())
}, },

2
ctru-rs/src/services/romfs.rs

@ -87,7 +87,7 @@ mod tests {
fn romfs_lock() { fn romfs_lock() {
let romfs = RomFS::new().unwrap(); let romfs = RomFS::new().unwrap();
let _value = *ROMFS_ACTIVE.lock().unwrap(); let _value = *ROMFS_ACTIVE.try_lock().unwrap();
drop(romfs); drop(romfs);
} }

Loading…
Cancel
Save