]> git.nega.tv - josh/narcissus/commitdiff
narcissus-core: Add integer widening helper trait.
authorJoshua Simmons <josh@nega.tv>
Sun, 4 Jun 2023 08:24:21 +0000 (10:24 +0200)
committerJoshua Simmons <josh@nega.tv>
Sun, 4 Jun 2023 08:44:49 +0000 (10:44 +0200)
bins/narcissus/src/helpers.rs
libs/narcissus-core/src/bitset.rs
libs/narcissus-core/src/lib.rs
libs/narcissus-core/src/linear_log_binning.rs
libs/narcissus-core/src/pool.rs
libs/narcissus-core/src/rand.rs
libs/narcissus-core/src/widen.rs [new file with mode: 0644]
libs/narcissus-font/src/cache.rs
libs/narcissus-gpu/src/backend/vulkan/mod.rs
libs/narcissus-gpu/src/tlsf.rs

index 16c86de9ecd647fdbd7dc2d7159d8fbbd2b92e76..dd3c3f5fca9ee5d152509c7a192e7d02bb8edc6c 100644 (file)
@@ -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<P: AsRef<Path>>(path: P) -> (Vec<Vertex>, Vec<u16>) {
         .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(),
index 34a1eb2226f4a90b3e254b20c42a836b06d1c0df..f7e852527732e1d4b420fc9c4fd9b249d3965053 100644 (file)
@@ -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::<T>() * 8;
         }
         let index = self.word.clear_least_significant_set_bit();
-        Some(self.base + index as usize)
+        Some(self.base + index.widen())
     }
 }
 
index 7c84cf4f02cc22e5dab3327563d6db1fab9382b3..d001603ab31143a9755173c01742d66c8a91aeb3 100644 (file)
@@ -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]
index 72f0d617d943a283148a4b3ea8929ceaccfd5f90..a7025de9edd44514839c4e1f8e228a6312eacfd1 100644 (file)
@@ -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<const LINEAR_LOG2: u32, const SUB_BINS_LOG2: u32> Bin<LINEAR_LOG2, SUB_BINS
 
     #[inline(always)]
     pub fn index(&self) -> usize {
-        self.index as usize
+        self.index.widen()
     }
 
     #[inline(always)]
index 810983fdc98af61efc0df88812a620b446a483c3..63f502bee4e7ef5cd16570f92fca11b787dbb0b5 100644 (file)
@@ -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<T> Values<T> {
     /// 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<T> Values<T> {
     /// 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<T> Values<T> {
                 .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<T> Values<T> {
             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<T> Values<T> {
     /// 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<T> Values<T> {
     /// 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() }
index a8e2b424628fa24cf70edd6716680b770faa4d5e..dcf4250a22bc614230ac02ed2bc6902ddd33b5c8 100644 (file)
@@ -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 <https://github.com/apple/swift/pull/39143/commits/87b3f607042e653a42b505442cc803ec20319c1c>
+    #[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 (file)
index 0000000..20036e4
--- /dev/null
@@ -0,0 +1,87 @@
+use crate::static_assert;
+
+/// Trait that allows explicit integer widening.
+pub trait Widen<T> {
+    /// 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<usize> for u8 {
+    #[inline(always)]
+    fn widen(self) -> usize {
+        self as usize
+    }
+}
+
+impl Widen<usize> for u16 {
+    #[inline(always)]
+    fn widen(self) -> usize {
+        self as usize
+    }
+}
+
+impl Widen<usize> for u32 {
+    #[inline(always)]
+    fn widen(self) -> usize {
+        self as usize
+    }
+}
+
+impl Widen<usize> for u64 {
+    #[inline(always)]
+    fn widen(self) -> usize {
+        self as usize
+    }
+}
+
+impl Widen<usize> for i8 {
+    #[inline(always)]
+    fn widen(self) -> usize {
+        if self < 0 {
+            widening_failure()
+        }
+        self as usize
+    }
+}
+
+impl Widen<usize> for i16 {
+    #[inline(always)]
+    fn widen(self) -> usize {
+        if self < 0 {
+            widening_failure()
+        }
+        self as usize
+    }
+}
+
+impl Widen<usize> for i32 {
+    #[inline(always)]
+    fn widen(self) -> usize {
+        if self < 0 {
+            widening_failure()
+        }
+        self as usize
+    }
+}
+
+impl Widen<usize> for i64 {
+    #[inline(always)]
+    fn widen(self) -> usize {
+        if self < 0 {
+            widening_failure()
+        }
+        self as usize
+    }
+}
index 4347f0c55f24b6c817b6ad541cfda6a3aacd7483..8a027eb5a724cf16e3d35845208a4480c8c8fa5e 100644 (file)
@@ -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;
index 78360f6b92f365b4dba90a05e5f8d97c645ed19b..7120d2d673afa6535164f0aba392cc8e07a78874 100644 (file)
@@ -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<T, F: FnMut(&mut u32, *mut T) -> vulkan_sys::Result>(mut f: F) -> Vec<T> {
     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<Offset3d> 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));
                 }
index 889939e2e798db6608759e0e62ac62daae2d3d38..6138941a93752c2295682a4fc4478092463c8125 100644 (file)
@@ -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<BlockIndex> for Vec<Block> {
 
     #[inline(always)]
     fn index(&self, index: BlockIndex) -> &Self::Output {
-        &self[index.0.get() as usize]
+        &self[index.0.get().widen()]
     }
 }
 
 impl IndexMut<BlockIndex> for Vec<Block> {
     #[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<Block>,
+            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];