fmod/core/
memory.rs

1// Copyright (c) 2024 Lily Lyons
2//
3// This Source Code Form is subject to the terms of the Mozilla Public
4// License, v. 2.0. If a copy of the MPL was not distributed with this
5// file, You can obtain one at https://mozilla.org/MPL/2.0/.
6
7use fmod_sys::*;
8use std::ffi::{c_char, c_int, c_uint, c_void};
9
10#[derive(PartialEq, Eq, Debug)]
11pub enum MemoryType {
12    Pool(&'static mut [u8]),
13    Callback {
14        alloc: unsafe extern "C" fn(
15            size: c_uint,
16            type_: FMOD_MEMORY_TYPE,
17            sourcestr: *const c_char,
18        ) -> *mut c_void,
19        realloc: FMOD_MEMORY_REALLOC_CALLBACK,
20        free: unsafe extern "C" fn(
21            ptr: *mut c_void,
22            type_: FMOD_MEMORY_TYPE,
23            sourcestr: *const c_char,
24        ),
25    },
26}
27
28bitflags::bitflags! {
29    #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
30    pub struct MemoryFlags: FMOD_MEMORY_TYPE {
31      const NORMAL         = FMOD_MEMORY_NORMAL;
32      const STREAM_FILE    = FMOD_MEMORY_STREAM_FILE;
33      const STREAM_DECODE  = FMOD_MEMORY_STREAM_DECODE;
34      const SAMPLEDATA     = FMOD_MEMORY_SAMPLEDATA;
35      const DSP_BUFFER     = FMOD_MEMORY_DSP_BUFFER;
36      const PLUGIN         = FMOD_MEMORY_PLUGIN;
37      const PERSISTENT     = FMOD_MEMORY_PERSISTENT;
38      const ALL            = FMOD_MEMORY_ALL;
39    }
40}
41
42impl From<FMOD_MEMORY_TYPE> for MemoryFlags {
43    fn from(value: FMOD_MEMORY_TYPE) -> Self {
44        MemoryFlags::from_bits_truncate(value)
45    }
46}
47
48impl From<MemoryFlags> for FMOD_MEMORY_TYPE {
49    fn from(value: MemoryFlags) -> Self {
50        value.bits()
51    }
52}
53
54/// Specifies a method for FMOD to allocate and free memory, either through user supplied callbacks or through a user supplied memory buffer with a fixed size.
55///
56/// # Safety
57///
58/// This function must be called before any FMOD System object is created.
59///
60/// If [`MemoryType::Callback::alloc`] and [`MemoryType::Callback::free`] are provided without [`MemoryType::Callback::realloc`]
61/// the reallocation is implemented via an allocation of the new size, copy from old address to new, then a free of the old address.
62///
63/// Callback implementations must be thread safe.
64///
65/// If you specify a fixed size pool that is too small, FMOD will return [`FMOD_RESULT::FMOD_ERR_MEMORY`] when the limit of the fixed size pool is exceeded.
66/// At this point, it's possible that FMOD may become unstable. To maintain stability, do not allow FMOD to run out of memory.
67/// To find out the required fixed size call [`memory_initialize`] with an overly large pool size (or no pool) and find out the maximum RAM usage at any one time with [`memory_get_stats`].
68/// The size of the pool is limited to [`c_int::MAX`].
69pub unsafe fn memory_initialize(memory_type: MemoryType, flags: MemoryFlags) -> Result<()> {
70    match memory_type {
71        MemoryType::Pool(pool) => unsafe {
72            FMOD_Memory_Initialize(
73                pool.as_mut_ptr().cast(),
74                pool.len() as c_int,
75                None,
76                None,
77                None,
78                flags.into(),
79            )
80            .to_result()
81        },
82        MemoryType::Callback {
83            alloc,
84            realloc,
85            free,
86        } => unsafe {
87            FMOD_Memory_Initialize(
88                std::ptr::null_mut(),
89                0,
90                Some(alloc),
91                realloc,
92                Some(free),
93                flags.into(),
94            )
95            .to_result()
96        },
97    }
98}
99
100/// Returns information on the memory usage of FMOD.
101///
102/// This information is byte accurate and counts all allocs and frees internally.
103/// This is useful for determining a fixed memory size to make FMOD work within for fixed memory machines such as consoles.
104///
105/// Note that if using [`memory_initialize`], the memory usage will be slightly higher than without it, as FMOD has to have a small amount of memory overhead to manage the available memory.
106pub fn memory_get_stats(blocking: bool) -> Result<(c_int, c_int)> {
107    let mut current = 0;
108    let mut max = 0;
109    unsafe {
110        FMOD_Memory_GetStats(&mut current, &mut max, blocking.into()).to_result()?;
111    }
112    Ok((current, max))
113}