Skip to content

Commit

Permalink
Merge #53
Browse files Browse the repository at this point in the history
53: Add slice access r=azriel91 a=willglynn

I added slice access to `specs` (amethyst/specs#634) to support OpenCL (amethyst/specs#553). Usage looks like:

```rust
impl<'a> System<'a> for SomeSystem {
    type SystemData = (WriteStorage<'a, Something>, );

    fn run(&mut self, (mut some_storage, ): Self::SystemData) {
        // 1. get a mutable slice of the component storage
        let slice = some_storage.unprotected_storage_mut().as_mut_slice();

        // 2. process the slice with OpenCL or whatever

        // 3. there is no step 3
    }
}
```

Components can be processed using an OpenCL kernel:

```c
struct Something {
    float foo;
    float bar;
    float baz;
};

__kernel void process_something(__global struct Something* slice) {
    __global struct Something* something = &slice[get_global_id(0)];
    // do stuff with this data
}
```

There's a catch when using slices from `DefaultVecStorage<T>` or `VecStorage<T>`. The slices are sparse, which means the kernel will either uselessly process `T::default()` or perform unchecked access into a `MaybeUninit<T>` for any entity which does not have component `T`.

Exposing a slice from `hibitset::BitSet` would allow an OpenCL kernel check the storage mask itself:

```c
struct Something {
    float foo;
    float bar;
    float baz;
};

#define BITS_PER_SIZE_T (sizeof(size_t) * 8)

__kernel void process_something(__global size_t* bitset_layer0, __global struct Something* slice) {
    // check that this index contains data
    size_t mask_index = get_global_id(0) / BITS_PER_SIZE_T;
    size_t shift = get_global_id(0) % BITS_PER_SIZE_T;
    if ((bitset_layer0[mask_index] >> shift) & 1 == 0) {
        return;
    }
  
    __global struct Something* something = &slice[get_global_id(0)];
    // do stuff with this data
}
```

This PR adds `layer{0,1,2}_as_slice(&self) -> &[usize]` to `BitSet`, as well as trivial constants which make correct usage more obvious. Layer 3 is specified to be a single `usize` so I didn't see any benefit to exposing it as a slice.

Co-authored-by: Will Glynn <will@willglynn.com>
  • Loading branch information
bors[bot] and willglynn committed Feb 16, 2020
2 parents 022b79a + ff41a6d commit 60775d1
Showing 1 changed file with 147 additions and 0 deletions.
147 changes: 147 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -230,6 +230,153 @@ impl BitSet {
self.layer2.clear();
self.layer3 = 0;
}

/// How many bits are in a `usize`.
///
/// This value can be trivially determined. It is provided here as a constant for clarity.
///
/// # Example
///
/// ```
/// use hibitset::BitSet;
/// assert_eq!(BitSet::BITS_PER_USIZE, std::mem::size_of::<usize>()*8);
/// ```
#[cfg(target_pointer_width = "32")]
pub const BITS_PER_USIZE: usize = 32;

/// How many bits are in a `usize`.
///
/// This value can be trivially determined. It is provided here as a constant for clarity.
///
/// # Example
///
/// ```
/// use hibitset::BitSet;
/// assert_eq!(BitSet::BITS_PER_USIZE, std::mem::size_of::<usize>()*8);
/// ```
#[cfg(target_pointer_width = "64")]
pub const BITS_PER_USIZE: usize = 64;

/// Returns the bottom layer of the bitset as a slice. Each bit in this slice refers to a single
/// `Index`.
///
/// The slice's length will be at least the length needed to reflect all the `1`s in the bitset,
/// but is not otherwise guaranteed. Consider it to be an implementation detail.
///
/// # Example
///
/// ```
/// use hibitset::BitSet;
///
/// let index: u32 = 12345;
///
/// let mut bitset = BitSet::new();
/// bitset.add(index);
///
/// // layer 0 is 1:1 with Indexes, so we expect that bit in the slice to be set
/// let slice = bitset.layer0_as_slice();
/// let bit_index = index as usize;
///
/// // map that bit index to a usize in the slice and a bit within that usize
/// let slice_index = bit_index / BitSet::BITS_PER_USIZE;
/// let bit_at_index = bit_index % BitSet::BITS_PER_USIZE;
///
/// assert_eq!(slice[slice_index], 1 << bit_at_index);
/// ```
pub fn layer0_as_slice(&self) -> &[usize] {
self.layer0.as_slice()
}

/// How many `Index`es are described by as single layer 1 bit, intended for use with
/// `BitSet::layer1_as_slice()`.
///
/// `BitSet`s are defined in terms of `usize`s summarizing `usize`s, so this value can be
/// trivially determined. It is provided here as a constant for clarity.
///
/// # Example
///
/// ```
/// use hibitset::BitSet;
/// assert_eq!(BitSet::LAYER1_GRANULARITY, BitSet::BITS_PER_USIZE);
/// ```
pub const LAYER1_GRANULARITY: usize = Self::BITS_PER_USIZE;

/// Returns the second layer of the bitset as a slice. Each bit in this slice summarizes a
/// corresponding `usize` from `layer0`. (If `usize` is 64 bits, bit 0 will be set if any
/// `Index`es 0-63 are set, bit 1 will be set if any `Index`es 64-127 are set, etc.)
/// `BitSet::LAYER1_GRANULARITY` reflects how many indexes are summarized per layer 1 bit.
///
/// The slice's length is not guaranteed, except that it will be at least the length needed to
/// reflect all the `1`s in the bitset.
///
/// # Example
///
/// ```
/// use hibitset::BitSet;
///
/// let index: u32 = 12345;
///
/// let mut bitset = BitSet::new();
/// bitset.add(index);
///
/// // layer 1 summarizes multiple indexes per bit, so divide appropriately
/// let slice = bitset.layer1_as_slice();
/// let bit_index = index as usize / BitSet::LAYER1_GRANULARITY;
///
/// // map that bit index to a usize in the slice and a bit within that usize
/// let slice_index = bit_index / BitSet::BITS_PER_USIZE;
/// let bit_at_index = bit_index % BitSet::BITS_PER_USIZE;
///
/// assert_eq!(slice[slice_index], 1 << bit_at_index);
/// ```
pub fn layer1_as_slice(&self) -> &[usize] {
self.layer1.as_slice()
}

/// How many `Index`es are described by as single layer 2 bit, intended for use with
/// `BitSet::layer2_as_slice()`.
///
/// `BitSet`s are defined in terms of `usize`s summarizing `usize`s, so this value can be
/// trivially determined. It is provided here as a constant for clarity.
///
/// # Example
///
/// ```
/// use hibitset::BitSet;
/// assert_eq!(BitSet::LAYER2_GRANULARITY, BitSet::LAYER1_GRANULARITY * BitSet::BITS_PER_USIZE);
/// ```
pub const LAYER2_GRANULARITY: usize = Self::LAYER1_GRANULARITY * Self::BITS_PER_USIZE;

/// Returns the third layer of the bitset as a slice. Each bit in this slice summarizes a
/// corresponding `usize` from `layer1`. If `usize` is 64 bits, bit 0 will be set if any
/// `Index`es 0-4095 are set, bit 1 will be set if any `Index`es 4096-8191 are set, etc.
///
/// The slice's length is not guaranteed, except that it will be at least the length needed to
/// reflect all the `1`s in the bitset.
///
/// # Example
///
/// ```
/// use hibitset::BitSet;
///
/// let index: u32 = 12345;
///
/// let mut bitset = BitSet::new();
/// bitset.add(index);
///
/// // layer 2 summarizes multiple indexes per bit, so divide appropriately
/// let slice = bitset.layer2_as_slice();
/// let bit_index = index as usize / BitSet::LAYER2_GRANULARITY;
///
/// // map that bit index to a usize in the slice and a bit within that usize
/// let slice_index = bit_index / BitSet::BITS_PER_USIZE;
/// let bit_at_index = bit_index % BitSet::BITS_PER_USIZE;
///
/// assert_eq!(slice[slice_index], 1 << bit_at_index);
/// ```
pub fn layer2_as_slice(&self) -> &[usize] {
self.layer2.as_slice()
}
}

/// A generic interface for [`BitSetLike`]-like types.
Expand Down

0 comments on commit 60775d1

Please sign in to comment.