From: Joshua Simmons Date: Sun, 4 Jun 2023 08:24:21 +0000 (+0200) Subject: narcissus-core: Add integer widening helper trait. X-Git-Url: https://git.nega.tv//gitweb.cgi?a=commitdiff_plain;h=5eed2ca0241759293e895b4a9f0fe09f3c2a9088;p=josh%2Fnarcissus narcissus-core: Add integer widening helper trait. --- diff --git a/bins/narcissus/src/helpers.rs b/bins/narcissus/src/helpers.rs index 16c86de..dd3c3f5 100644 --- a/bins/narcissus/src/helpers.rs +++ b/bins/narcissus/src/helpers.rs @@ -1,6 +1,6 @@ use std::path::Path; -use narcissus_core::{default, obj}; +use narcissus_core::{default, obj, Widen}; use narcissus_gpu::{ Access, Buffer, BufferDesc, BufferImageCopy, BufferUsageFlags, Device, Extent3d, Image, ImageAspectFlags, ImageBarrier, ImageDesc, ImageDimension, ImageFormat, ImageLayout, @@ -58,9 +58,9 @@ pub fn load_obj>(path: P) -> (Vec, Vec) { .flatten() .enumerate() .map(|(index, &(position_index, texcoord_index, normal_index))| { - let position = visitor.positions[position_index as usize - 1]; - let normal = visitor.normals[normal_index as usize - 1]; - let texcoord = visitor.texcoords[texcoord_index as usize - 1]; + let position = visitor.positions[position_index.widen() - 1]; + let normal = visitor.normals[normal_index.widen() - 1]; + let texcoord = visitor.texcoords[texcoord_index.widen() - 1]; ( Vertex { position: vec4(position.x, position.y, position.z, 0.0).into(), diff --git a/libs/narcissus-core/src/bitset.rs b/libs/narcissus-core/src/bitset.rs index 34a1eb2..f7e8525 100644 --- a/libs/narcissus-core/src/bitset.rs +++ b/libs/narcissus-core/src/bitset.rs @@ -1,3 +1,5 @@ +use crate::Widen; + pub trait Bits: Copy + Default { fn is_zero(self) -> bool; /// Clear the least significant set bit and return its index. @@ -40,7 +42,7 @@ where self.base += std::mem::size_of::() * 8; } let index = self.word.clear_least_significant_set_bit(); - Some(self.base + index as usize) + Some(self.base + index.widen()) } } diff --git a/libs/narcissus-core/src/lib.rs b/libs/narcissus-core/src/lib.rs index 7c84cf4..d001603 100644 --- a/libs/narcissus-core/src/lib.rs +++ b/libs/narcissus-core/src/lib.rs @@ -17,6 +17,7 @@ mod uuid; mod virtual_mem; mod virtual_vec; mod waiter; +mod widen; pub use arena::{Arena, HybridArena}; pub use bitset::BitIter; @@ -30,6 +31,8 @@ pub use virtual_vec::{VirtualDeque, VirtualVec}; pub use finite::{FiniteF32, FiniteF64, NotFiniteError}; +pub use widen::Widen; + use std::{ffi::CStr, mem::MaybeUninit}; #[macro_export] diff --git a/libs/narcissus-core/src/linear_log_binning.rs b/libs/narcissus-core/src/linear_log_binning.rs index 72f0d61..a7025de 100644 --- a/libs/narcissus-core/src/linear_log_binning.rs +++ b/libs/narcissus-core/src/linear_log_binning.rs @@ -1,3 +1,5 @@ +use crate::Widen; + #[derive(Clone, Copy, PartialEq, Eq, Debug)] pub struct Bin< // The log2 of the size of the linear bin. @@ -78,7 +80,7 @@ impl Bin usize { - self.index as usize + self.index.widen() } #[inline(always)] diff --git a/libs/narcissus-core/src/pool.rs b/libs/narcissus-core/src/pool.rs index 810983f..63f502b 100644 --- a/libs/narcissus-core/src/pool.rs +++ b/libs/narcissus-core/src/pool.rs @@ -2,6 +2,7 @@ use std::{marker::PhantomData, mem::size_of, ptr::NonNull}; use crate::{ align_offset, mod_inverse_u32, static_assert, virtual_commit, virtual_free, virtual_reserve, + Widen, }; /// Each handle uses `GEN_BITS` bits of per-slot generation counter. Looking up @@ -291,7 +292,7 @@ impl Slots { #[inline(always)] fn get(&self, slot_index: SlotIndex) -> Option<&Slot> { - let index = slot_index.0 as usize; + let index = slot_index.0.widen(); if index < self.len { Some(unsafe { self.ptr.as_ptr().add(index).as_ref().unwrap() }) } else { @@ -301,7 +302,7 @@ impl Slots { #[inline(always)] fn get_mut(&mut self, slot_index: SlotIndex) -> Option<&mut Slot> { - let index = slot_index.0 as usize; + let index = slot_index.0.widen(); if index < self.len { Some(unsafe { self.ptr.as_ptr().add(index).as_mut().unwrap() }) } else { @@ -370,7 +371,7 @@ impl Values { /// Update the lookup table for the given `ValueIndex` with a new `SlotIndex` #[inline(always)] fn set_slot(&mut self, value_index: ValueIndex, slot_index: SlotIndex) { - let value_index = value_index.0 as usize; + let value_index = value_index.0.widen(); assert!(value_index < self.len); unsafe { std::ptr::write( @@ -384,7 +385,7 @@ impl Values { /// lookup table. #[inline(always)] fn get_slot(&mut self, value_index: ValueIndex) -> SlotIndex { - let value_index = value_index.0 as usize; + let value_index = value_index.0.widen(); assert!(value_index < self.len); // SAFETY: SlotIndex is Copy so we don't invalidate the value being read. unsafe { std::ptr::read(self.slots_ptr.as_ptr().add(value_index).as_ref().unwrap()) } @@ -423,7 +424,7 @@ impl Values { .update_value_index(value_index); } - let value_index = value_index.0 as usize; + let value_index = value_index.0.widen(); assert!(value_index < self.len); unsafe { @@ -431,11 +432,7 @@ impl Values { self.len -= 1; let value = std::ptr::read(ptr.add(value_index)); - std::ptr::copy( - ptr.add(last_value_index.0 as usize), - ptr.add(value_index), - 1, - ); + std::ptr::copy(ptr.add(last_value_index.0.widen()), ptr.add(value_index), 1); value } @@ -446,7 +443,7 @@ impl Values { /// Panics if `value_index` is out of bounds #[inline(always)] fn get(&self, value_index: ValueIndex) -> &T { - let value_index = value_index.0 as usize; + let value_index = value_index.0.widen(); assert!(value_index < self.len); let ptr = self.values_ptr.as_ptr(); unsafe { ptr.add(value_index).as_ref().unwrap() } @@ -457,7 +454,7 @@ impl Values { /// Panics if `value_index` is out of bounds #[inline(always)] fn get_mut(&mut self, value_index: ValueIndex) -> &mut T { - let value_index = value_index.0 as usize; + let value_index = value_index.0.widen(); assert!(value_index < self.len); let ptr = self.values_ptr.as_ptr(); unsafe { ptr.add(value_index).as_mut().unwrap() } diff --git a/libs/narcissus-core/src/rand.rs b/libs/narcissus-core/src/rand.rs index a8e2b42..dcf4250 100644 --- a/libs/narcissus-core/src/rand.rs +++ b/libs/narcissus-core/src/rand.rs @@ -1,4 +1,4 @@ -use crate::mul_full_width_u64; +use crate::{mul_full_width_u64, Widen}; #[derive(Clone, Copy, PartialEq, Eq)] pub struct Pcg64 { @@ -46,6 +46,22 @@ impl Pcg64 { result + carry as u64 } + /// Generates a uniformly distributed random number in the range + /// `0..upper_bound` + /// + /// Always draws two 64 bit words from the PRNG. + /// + /// Based on + #[inline] + #[must_use] + pub fn next_bound_usize(&mut self, upper_bound: usize) -> usize { + let upper_bound = upper_bound as u64; + let (result, fraction) = mul_full_width_u64(upper_bound, self.next_u64()); + let (hi, _) = mul_full_width_u64(upper_bound, self.next_u64()); + let (_, carry) = fraction.overflowing_add(hi); + (result + carry as u64).widen() + } + /// Generates a uniformly distributed random float in the range `-1.0..1.0` /// /// Always draws two 64 bit words from the PRNG. @@ -63,8 +79,7 @@ impl Pcg64 { if slice.is_empty() { None } else { - let index = self.next_bound_u64(slice.len() as u64) as usize; - slice.get(index) + slice.get(self.next_bound_usize(slice.len())) } } diff --git a/libs/narcissus-core/src/widen.rs b/libs/narcissus-core/src/widen.rs new file mode 100644 index 0000000..20036e4 --- /dev/null +++ b/libs/narcissus-core/src/widen.rs @@ -0,0 +1,87 @@ +use crate::static_assert; + +/// Trait that allows explicit integer widening. +pub trait Widen { + /// Returns `self` "widened" to `T`, panics if the conversion would wrap. + fn widen(self) -> T; +} + +// Would need to further restrict widen cases for 32 bit support. +static_assert!( + usize::BITS == 64, + "only supports machines with 64 bit usize" +); + +#[cold] +#[inline(never)] +fn widening_failure() { + panic!("failed to widen type, out of bounds") +} + +impl Widen for u8 { + #[inline(always)] + fn widen(self) -> usize { + self as usize + } +} + +impl Widen for u16 { + #[inline(always)] + fn widen(self) -> usize { + self as usize + } +} + +impl Widen for u32 { + #[inline(always)] + fn widen(self) -> usize { + self as usize + } +} + +impl Widen for u64 { + #[inline(always)] + fn widen(self) -> usize { + self as usize + } +} + +impl Widen for i8 { + #[inline(always)] + fn widen(self) -> usize { + if self < 0 { + widening_failure() + } + self as usize + } +} + +impl Widen for i16 { + #[inline(always)] + fn widen(self) -> usize { + if self < 0 { + widening_failure() + } + self as usize + } +} + +impl Widen for i32 { + #[inline(always)] + fn widen(self) -> usize { + if self < 0 { + widening_failure() + } + self as usize + } +} + +impl Widen for i64 { + #[inline(always)] + fn widen(self) -> usize { + if self < 0 { + widening_failure() + } + self as usize + } +} diff --git a/libs/narcissus-font/src/cache.rs b/libs/narcissus-font/src/cache.rs index 4347f0c..8a027eb 100644 --- a/libs/narcissus-font/src/cache.rs +++ b/libs/narcissus-font/src/cache.rs @@ -1,8 +1,8 @@ use std::collections::hash_map::Entry; use crate::{font::GlyphBitmapBox, FontCollection, GlyphIndex, Oversample, Packer}; -use narcissus_core::default; pub use narcissus_core::FiniteF32; +use narcissus_core::{default, Widen}; use rustc_hash::FxHashMap; use stb_truetype_sys::rectpack::Rect; @@ -167,8 +167,7 @@ where let cached_glyph = &self.cached_glyphs[cached_glyph_index]; let rect = &self.rects[cached_glyph_index]; - let touched_glyph = - &mut self.touched_glyphs[touched_glyph_index.0 as usize]; + let touched_glyph = &mut self.touched_glyphs[touched_glyph_index.0.widen()]; touched_glyph.x0 = rect.x; touched_glyph.x1 = rect.x + rect.w; diff --git a/libs/narcissus-gpu/src/backend/vulkan/mod.rs b/libs/narcissus-gpu/src/backend/vulkan/mod.rs index 78360f6..7120d2d 100644 --- a/libs/narcissus-gpu/src/backend/vulkan/mod.rs +++ b/libs/narcissus-gpu/src/backend/vulkan/mod.rs @@ -11,7 +11,7 @@ use narcissus_core::{ cstr, cstr_from_bytes_until_nul, default, is_aligned_to, manual_arc, manual_arc::ManualArc, raw_window::{AsRawWindow, RawWindow}, - Arena, HybridArena, Mutex, PhantomUnsend, Pool, + Arena, HybridArena, Mutex, PhantomUnsend, Pool, Widen, }; use vulkan_sys as vk; @@ -71,7 +71,7 @@ macro_rules! vk_check { fn vk_vec vulkan_sys::Result>(mut f: F) -> Vec { let mut count = 0; vk_check!(f(&mut count, std::ptr::null_mut())); - let mut v = Vec::with_capacity(count as usize); + let mut v = Vec::with_capacity(count.widen()); vk_check!(f(&mut count, v.as_mut_ptr())); unsafe { v.set_len(count as usize) }; v @@ -117,8 +117,10 @@ impl From for vk::Offset3d { #[must_use] fn vulkan_bool32(b: bool) -> vk::Bool32 { - const VALUES: [vk::Bool32; 2] = [vk::Bool32::False, vk::Bool32::True]; - VALUES[b as usize] + match b { + false => vk::Bool32::False, + true => vk::Bool32::True, + } } #[must_use] @@ -1314,7 +1316,7 @@ impl VulkanDevice { })); let allocators = std::array::from_fn(|i| { - if i < physical_device_memory_properties.memory_type_count as usize { + if i < physical_device_memory_properties.memory_type_count.widen() { Some(default()) } else { None @@ -1394,7 +1396,7 @@ impl VulkanDevice { .map(|memory_type_index| { ( memory_type_index, - self.physical_device_memory_properties.memory_types[memory_type_index as usize], + self.physical_device_memory_properties.memory_types[memory_type_index.widen()], ) }) .find(|(i, memory_type)| { @@ -1417,7 +1419,7 @@ impl VulkanDevice { let memory_type_index = self.find_memory_type_index(desc.requirements.memory_type_bits, memory_property_flags); - let allocator = self.allocators[memory_type_index as usize] + let allocator = self.allocators[memory_type_index.widen()] .as_ref() .expect("returned a memory type index that has no associated allocator"); @@ -1446,9 +1448,9 @@ impl VulkanDevice { allocator.dedicated.lock().insert(memory); let mapped_ptr = if self.physical_device_memory_properties.memory_types - [memory_type_index as usize] - .property_flags - .contains(vk::MemoryPropertyFlags::HOST_VISIBLE) + [memory_type_index.widen()] + .property_flags + .contains(vk::MemoryPropertyFlags::HOST_VISIBLE) { let mut data = std::ptr::null_mut(); vk_check!(self.device_fn.map_memory( @@ -1481,7 +1483,7 @@ impl VulkanDevice { let memory_type_index = self.find_memory_type_index(desc.requirements.memory_type_bits, memory_property_flags); - let allocator = self.allocators[memory_type_index as usize] + let allocator = self.allocators[memory_type_index.widen()] .as_ref() .expect("returned a memory type index that has no associated allocator"); @@ -1510,9 +1512,9 @@ impl VulkanDevice { )); let mapped_ptr = if self.physical_device_memory_properties.memory_types - [memory_type_index as usize] - .property_flags - .contains(vk::MemoryPropertyFlags::HOST_VISIBLE) + [memory_type_index.widen()] + .property_flags + .contains(vk::MemoryPropertyFlags::HOST_VISIBLE) { let mut data = std::ptr::null_mut(); vk_check!(self.device_fn.map_memory( @@ -1670,7 +1672,7 @@ impl VulkanDevice { // non-null, then we can create a slice for it. unsafe { let dst = - std::slice::from_raw_parts_mut(memory.mapped_ptr(), memory.size() as usize); + std::slice::from_raw_parts_mut(memory.mapped_ptr(), memory.size().widen()); dst.copy_from_slice(initial_data); } } @@ -3131,14 +3133,14 @@ impl Device for VulkanDevice { for allocation in frame.destroyed_allocations.get_mut().drain(..) { match allocation { VulkanMemory::Dedicated(dedicated) => { - let allocator = self.allocators[dedicated.memory_type_index as usize] + let allocator = self.allocators[dedicated.memory_type_index.widen()] .as_ref() .unwrap(); allocator.dedicated.lock().remove(&dedicated.memory); unsafe { device_fn.free_memory(device, dedicated.memory, None) } } VulkanMemory::SubAlloc(sub_alloc) => { - let allocator = self.allocators[sub_alloc.memory_type_index as usize] + let allocator = self.allocators[sub_alloc.memory_type_index.widen()] .as_ref() .unwrap(); allocator.tlsf.lock().free(sub_alloc.allocation) @@ -3562,7 +3564,7 @@ impl VulkanDevice { present_info.acquire = acquire; present_info.image_index = image_index; present_info.swapchain = swapchain; - let view = image_views[image_index as usize]; + let view = image_views[image_index.widen()]; return Ok((width, height, view)); } diff --git a/libs/narcissus-gpu/src/tlsf.rs b/libs/narcissus-gpu/src/tlsf.rs index 889939e..6138941 100644 --- a/libs/narcissus-gpu/src/tlsf.rs +++ b/libs/narcissus-gpu/src/tlsf.rs @@ -90,7 +90,7 @@ use std::{ ops::{Index, IndexMut}, }; -use narcissus_core::{linear_log_binning, static_assert}; +use narcissus_core::{linear_log_binning, static_assert, Widen}; // The log2 of the size of the 'linear' bin. pub const LINEAR_LOG2: u32 = 7; // 2^7 = 128 @@ -219,14 +219,14 @@ impl Index for Vec { #[inline(always)] fn index(&self, index: BlockIndex) -> &Self::Output { - &self[index.0.get() as usize] + &self[index.0.get().widen()] } } impl IndexMut for Vec { #[inline(always)] fn index_mut(&mut self, index: BlockIndex) -> &mut Self::Output { - &mut self[index.0.get() as usize] + &mut self[index.0.get().widen()] } } @@ -238,7 +238,7 @@ where #[inline(always)] fn index(&self, index: SuperBlockIndex) -> &Self::Output { - &self[index.0 as usize] + &self[index.0.widen()] } } @@ -248,7 +248,7 @@ where { #[inline(always)] fn index_mut(&mut self, index: SuperBlockIndex) -> &mut Self::Output { - &mut self[index.0 as usize] + &mut self[index.0.widen()] } } @@ -339,7 +339,7 @@ where // First we scan the second-level bitmap from sub_bin, masking out the earlier // sub-bins so we don't end up returning a bin that's too small for the // allocation. - let mut second_level = self.bitmap_1[bin as usize] & (!0 << sub_bin); + let mut second_level = self.bitmap_1[bin.widen()] & (!0 << sub_bin); // If that search failed, then we must scan the first-level bitmap from the next // bin forward. If we find anything here it cannot possibly be smaller than the @@ -354,7 +354,7 @@ where // Recalculate the bin from the first level bitmap. bin = first_level.trailing_zeros(); - second_level = self.bitmap_1[bin as usize]; + second_level = self.bitmap_1[bin.widen()]; } // Find the sub-bin from the second level bitmap. @@ -366,7 +366,7 @@ where /// structure. fn set_metadata_bit(&mut self, bin: Bin) { let sub_bin = bin.sub_bin(); - let bin = bin.bin() as usize; + let bin = bin.bin().widen(); self.bitmap_0 |= 1 << bin; self.bitmap_1[bin] |= 1 << sub_bin; } @@ -375,7 +375,7 @@ where /// structure. fn clear_metadata_bit(&mut self, bin: Bin) { let sub_bin = bin.sub_bin(); - let bin = bin.bin() as usize; + let bin = bin.bin().widen(); self.bitmap_1[bin] &= !(1 << sub_bin); if self.bitmap_1[bin] == 0 { self.bitmap_0 &= !(1 << bin); @@ -383,6 +383,7 @@ where } /// Inserts a block into the empty blocks lists. + #[inline(always)] fn insert_block(&mut self, block_index: BlockIndex) { debug_assert!(self.blocks[block_index].is_free()); debug_assert!(self.blocks[block_index].free_link.is_unlinked()); @@ -400,6 +401,7 @@ where } /// Removes a block from the empty blocks lists. + #[inline(always)] fn extract_block(&mut self, block_index: BlockIndex) { debug_assert!(self.blocks[block_index].is_free()); @@ -448,12 +450,33 @@ where } /// Requests a new block, and returns its `BlockIndex`. + #[inline(always)] fn request_block( &mut self, offset: u32, size: u32, super_block_index: SuperBlockIndex, ) -> BlockIndex { + #[cold] + fn create_block( + blocks: &mut Vec, + size: u32, + offset: u32, + super_block_index: SuperBlockIndex, + ) -> BlockIndex { + assert!(blocks.len() < i32::MAX as usize); + let block_index = BlockIndex(NonZeroU32::new(blocks.len() as u32).unwrap()); + blocks.push(Block { + generation: 0, + size, + offset, + free_link: BlockLink::new(block_index), + phys_link: BlockLink::new(block_index), + super_block_index, + }); + block_index + } + let block_index = if let Some(free_block_index) = self.free_block_head { let next_index = self.blocks[free_block_index].free_link.next; self.free_block_head = if next_index != free_block_index { @@ -464,17 +487,7 @@ where list_unlink!(self.blocks, free_link, free_block_index); free_block_index } else { - assert!(self.blocks.len() < i32::MAX as usize); - let block_index = BlockIndex(NonZeroU32::new(self.blocks.len() as u32).unwrap()); - self.blocks.push(Block { - generation: 0, - size, - offset, - free_link: BlockLink::new(block_index), - phys_link: BlockLink::new(block_index), - super_block_index, - }); - block_index + create_block(&mut self.blocks, size, offset, super_block_index) }; let block = &mut self.blocks[block_index];