Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add no_std support #265

Open
wants to merge 17 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 12 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 8 additions & 5 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,22 +16,23 @@ include = [
"/README.md",
"/LICENSE-*",
"/src",
"/examples",
"/examples"
]

[package.metadata.docs.rs]
all-features = true

[dependencies]
log = "0.4"
thiserror = "1.0"
presser = { version = "0.3" }
log = { version = "0.4", default-features = false }
thiserror = { version = "2.0", default-features = false }
presser = { version = "0.3", default-features = false }
# Only needed for Vulkan. Disable all default features as good practice,
# such as the ability to link/load a Vulkan library.
ash = { version = "0.38", optional = true, default-features = false, features = ["debug"] }
# Only needed for visualizer.
egui = { version = ">=0.24, <=0.27", optional = true, default-features = false }
egui_extras = { version = ">=0.24, <=0.27", optional = true, default-features = false }
hashbrown = { version = "0.15.2", optional = true }

[target.'cfg(target_vendor = "apple")'.dependencies]
objc2 = { version = "0.6", default-features = false, optional = true }
Expand Down Expand Up @@ -98,11 +99,13 @@ name = "metal-buffer"
required-features = ["metal"]

[features]
std = ["presser/std"]
visualizer = ["dep:egui", "dep:egui_extras"]
vulkan = ["dep:ash"]
d3d12 = ["dep:windows"]
metal = ["dep:objc2", "dep:objc2-metal", "dep:objc2-foundation"]
# Expose helper functionality for winapi types to interface with gpu-allocator, which is primarily windows-rs driven
public-winapi = ["dep:winapi"]
hashbrown = ["dep:hashbrown"]

default = ["d3d12", "vulkan", "metal"]
default = ["std", "d3d12", "vulkan", "metal"]
56 changes: 39 additions & 17 deletions src/allocator/dedicated_block_allocator/mod.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,18 @@
#![deny(unsafe_code, clippy::unwrap_used)]

#[cfg(feature = "visualizer")]
pub(crate) mod visualizer;

#[cfg(feature = "std")]
use std::{backtrace::Backtrace, sync::Arc};

use alloc::{
borrow::ToOwned,
string::{String, ToString},
vec::Vec,
};

use log::{log, Level};

#[cfg(feature = "visualizer")]
pub(crate) mod visualizer;

use super::{AllocationReport, AllocationType, SubAllocator, SubAllocatorBase};
use crate::{AllocationError, Result};

Expand All @@ -16,6 +22,7 @@ pub(crate) struct DedicatedBlockAllocator {
allocated: u64,
/// Only used if [`crate::AllocatorDebugSettings::store_stack_traces`] is [`true`]
name: Option<String>,
#[cfg(feature = "std")]
backtrace: Arc<Backtrace>,
}

Expand All @@ -25,6 +32,7 @@ impl DedicatedBlockAllocator {
size,
allocated: 0,
name: None,
#[cfg(feature = "std")]
backtrace: Arc::new(Backtrace::disabled()),
}
}
Expand All @@ -39,8 +47,8 @@ impl SubAllocator for DedicatedBlockAllocator {
_allocation_type: AllocationType,
_granularity: u64,
name: &str,
backtrace: Arc<Backtrace>,
) -> Result<(u64, std::num::NonZeroU64)> {
#[cfg(feature = "std")] backtrace: Arc<Backtrace>,
) -> Result<(u64, core::num::NonZeroU64)> {
if self.allocated != 0 {
return Err(AllocationError::OutOfMemory);
}
Expand All @@ -53,15 +61,18 @@ impl SubAllocator for DedicatedBlockAllocator {

self.allocated = size;
self.name = Some(name.to_string());
self.backtrace = backtrace;
#[cfg(feature = "std")]
{
self.backtrace = backtrace;
}

#[allow(clippy::unwrap_used)]
let dummy_id = std::num::NonZeroU64::new(1).unwrap();
let dummy_id = core::num::NonZeroU64::new(1).unwrap();
Ok((0, dummy_id))
}

fn free(&mut self, chunk_id: Option<std::num::NonZeroU64>) -> Result<()> {
if chunk_id != std::num::NonZeroU64::new(1) {
fn free(&mut self, chunk_id: Option<core::num::NonZeroU64>) -> Result<()> {
if chunk_id != core::num::NonZeroU64::new(1) {
Err(AllocationError::Internal("Chunk ID must be 1.".into()))
} else {
self.allocated = 0;
Expand All @@ -71,10 +82,10 @@ impl SubAllocator for DedicatedBlockAllocator {

fn rename_allocation(
&mut self,
chunk_id: Option<std::num::NonZeroU64>,
chunk_id: Option<core::num::NonZeroU64>,
name: &str,
) -> Result<()> {
if chunk_id != std::num::NonZeroU64::new(1) {
if chunk_id != core::num::NonZeroU64::new(1) {
Err(AllocationError::Internal("Chunk ID must be 1.".into()))
} else {
self.name = Some(name.into());
Expand All @@ -90,6 +101,20 @@ impl SubAllocator for DedicatedBlockAllocator {
) {
let empty = "".to_string();
let name = self.name.as_ref().unwrap_or(&empty);
let backtrace_info;
#[cfg(feature = "std")]
{
backtrace_info = format!(
r#"
backtrace: {}
"#,
self.backtrace
)
}
#[cfg(not(feature = "std"))]
{
backtrace_info = "".to_owned()
}

log!(
log_level,
Expand All @@ -98,16 +123,13 @@ impl SubAllocator for DedicatedBlockAllocator {
memory block: {}
dedicated allocation: {{
size: 0x{:x},
name: {},
backtrace: {}
}}
name: {},{backtrace_info}}}
}}"#,
memory_type_index,
memory_block_index,
self.size,
name,
self.backtrace
)
);
}

fn report_allocations(&self) -> Vec<AllocationReport> {
Expand Down
78 changes: 53 additions & 25 deletions src/allocator/free_list_allocator/mod.rs
Original file line number Diff line number Diff line change
@@ -1,16 +1,25 @@
#![deny(unsafe_code, clippy::unwrap_used)]

#[cfg(feature = "visualizer")]
pub(crate) mod visualizer;
use alloc::{
borrow::ToOwned,
string::{String, ToString},
vec::Vec,
};

#[cfg(feature = "std")]
use std::{
backtrace::Backtrace,
collections::{HashMap, HashSet},
sync::Arc,
};

#[cfg(feature = "hashbrown")]
use hashbrown::{HashMap, HashSet};
use log::{log, Level};

#[cfg(feature = "visualizer")]
pub(crate) mod visualizer;

use super::{AllocationReport, AllocationType, SubAllocator, SubAllocatorBase};
use crate::{AllocationError, Result};

Expand All @@ -26,24 +35,25 @@ fn align_up(val: u64, alignment: u64) -> u64 {

#[derive(Debug)]
pub(crate) struct MemoryChunk {
pub(crate) chunk_id: std::num::NonZeroU64,
pub(crate) chunk_id: core::num::NonZeroU64,
pub(crate) size: u64,
pub(crate) offset: u64,
pub(crate) allocation_type: AllocationType,
pub(crate) name: Option<String>,
/// Only used if [`crate::AllocatorDebugSettings::store_stack_traces`] is [`true`]
#[cfg(feature = "std")]
pub(crate) backtrace: Arc<Backtrace>,
next: Option<std::num::NonZeroU64>,
prev: Option<std::num::NonZeroU64>,
next: Option<core::num::NonZeroU64>,
prev: Option<core::num::NonZeroU64>,
}

#[derive(Debug)]
pub(crate) struct FreeListAllocator {
size: u64,
allocated: u64,
pub(crate) chunk_id_counter: u64,
pub(crate) chunks: HashMap<std::num::NonZeroU64, MemoryChunk>,
free_chunks: HashSet<std::num::NonZeroU64>,
pub(crate) chunks: HashMap<core::num::NonZeroU64, MemoryChunk>,
free_chunks: HashSet<core::num::NonZeroU64>,
}

/// Test if two suballocations will overlap the same page.
Expand All @@ -68,7 +78,7 @@ fn has_granularity_conflict(type0: AllocationType, type1: AllocationType) -> boo
impl FreeListAllocator {
pub(crate) fn new(size: u64) -> Self {
#[allow(clippy::unwrap_used)]
let initial_chunk_id = std::num::NonZeroU64::new(1).unwrap();
let initial_chunk_id = core::num::NonZeroU64::new(1).unwrap();

let mut chunks = HashMap::default();
chunks.insert(
Expand All @@ -79,6 +89,7 @@ impl FreeListAllocator {
offset: 0,
allocation_type: AllocationType::Free,
name: None,
#[cfg(feature = "std")]
backtrace: Arc::new(Backtrace::disabled()),
prev: None,
next: None,
Expand All @@ -100,27 +111,27 @@ impl FreeListAllocator {
}

/// Generates a new unique chunk ID
fn get_new_chunk_id(&mut self) -> Result<std::num::NonZeroU64> {
fn get_new_chunk_id(&mut self) -> Result<core::num::NonZeroU64> {
if self.chunk_id_counter == u64::MAX {
// End of chunk id counter reached, no more allocations are possible.
return Err(AllocationError::OutOfMemory);
}

let id = self.chunk_id_counter;
self.chunk_id_counter += 1;
std::num::NonZeroU64::new(id).ok_or_else(|| {
core::num::NonZeroU64::new(id).ok_or_else(|| {
AllocationError::Internal("New chunk id was 0, which is not allowed.".into())
})
}
/// Finds the specified `chunk_id` in the list of free chunks and removes if from the list
fn remove_id_from_free_list(&mut self, chunk_id: std::num::NonZeroU64) {
fn remove_id_from_free_list(&mut self, chunk_id: core::num::NonZeroU64) {
self.free_chunks.remove(&chunk_id);
}
/// Merges two adjacent chunks. Right chunk will be merged into the left chunk
fn merge_free_chunks(
&mut self,
chunk_left: std::num::NonZeroU64,
chunk_right: std::num::NonZeroU64,
chunk_left: core::num::NonZeroU64,
chunk_right: core::num::NonZeroU64,
) -> Result<()> {
// Gather data from right chunk and remove it
let (right_size, right_next) = {
Expand Down Expand Up @@ -162,14 +173,14 @@ impl SubAllocator for FreeListAllocator {
allocation_type: AllocationType,
granularity: u64,
name: &str,
backtrace: Arc<Backtrace>,
) -> Result<(u64, std::num::NonZeroU64)> {
#[cfg(feature = "std")] backtrace: Arc<Backtrace>,
) -> Result<(u64, core::num::NonZeroU64)> {
let free_size = self.size - self.allocated;
if size > free_size {
return Err(AllocationError::OutOfMemory);
}

let mut best_fit_id: Option<std::num::NonZeroU64> = None;
let mut best_fit_id: Option<core::num::NonZeroU64> = None;
let mut best_offset = 0u64;
let mut best_aligned_size = 0u64;
let mut best_chunk_size = 0u64;
Expand Down Expand Up @@ -249,6 +260,7 @@ impl SubAllocator for FreeListAllocator {
offset: free_chunk.offset,
allocation_type,
name: Some(name.to_string()),
#[cfg(feature = "std")]
backtrace,
prev: free_chunk.prev,
next: Some(first_fit_id),
Expand Down Expand Up @@ -278,7 +290,10 @@ impl SubAllocator for FreeListAllocator {

chunk.allocation_type = allocation_type;
chunk.name = Some(name.to_string());
chunk.backtrace = backtrace;
#[cfg(feature = "std")]
{
chunk.backtrace = backtrace;
}

self.remove_id_from_free_list(first_fit_id);

Expand All @@ -290,7 +305,7 @@ impl SubAllocator for FreeListAllocator {
Ok((best_offset, chunk_id))
}

fn free(&mut self, chunk_id: Option<std::num::NonZeroU64>) -> Result<()> {
fn free(&mut self, chunk_id: Option<core::num::NonZeroU64>) -> Result<()> {
let chunk_id = chunk_id
.ok_or_else(|| AllocationError::Internal("Chunk ID must be a valid value.".into()))?;

Expand All @@ -302,7 +317,10 @@ impl SubAllocator for FreeListAllocator {
})?;
chunk.allocation_type = AllocationType::Free;
chunk.name = None;
chunk.backtrace = Arc::new(Backtrace::disabled());
#[cfg(feature = "std")]
{
chunk.backtrace = Arc::new(Backtrace::disabled());
}

self.allocated -= chunk.size;

Expand All @@ -327,7 +345,7 @@ impl SubAllocator for FreeListAllocator {

fn rename_allocation(
&mut self,
chunk_id: Option<std::num::NonZeroU64>,
chunk_id: Option<core::num::NonZeroU64>,
name: &str,
) -> Result<()> {
let chunk_id = chunk_id
Expand Down Expand Up @@ -362,7 +380,20 @@ impl SubAllocator for FreeListAllocator {
}
let empty = "".to_string();
let name = chunk.name.as_ref().unwrap_or(&empty);

let backtrace_info;
#[cfg(feature = "std")]
{
backtrace_info = format!(
r#"
backtrace: {}
"#,
chunk.backtrace
)
}
#[cfg(not(feature = "std"))]
{
backtrace_info = "".to_owned()
}
log!(
log_level,
r#"leak detected: {{
Expand All @@ -373,9 +404,7 @@ impl SubAllocator for FreeListAllocator {
size: 0x{:x},
offset: 0x{:x},
allocation_type: {:?},
name: {},
backtrace: {}
}}
name: {},{backtrace_info}}}
}}"#,
memory_type_index,
memory_block_index,
Expand All @@ -384,7 +413,6 @@ impl SubAllocator for FreeListAllocator {
chunk.offset,
chunk.allocation_type,
name,
chunk.backtrace
);
}
}
Expand Down
Loading
Loading