Skip to content

Commit

Permalink
Allow converting impl Textures to buffers
Browse files Browse the repository at this point in the history
  • Loading branch information
zesterer committed Nov 3, 2023
1 parent f370333 commit 290e14c
Show file tree
Hide file tree
Showing 3 changed files with 60 additions and 20 deletions.
4 changes: 4 additions & 0 deletions examples/texture_mapping.rs
Expand Up @@ -110,6 +110,10 @@ fn main() {
// Load a texture from disk
let texture = image::open("examples/data/rust.png").unwrap().to_rgba8();

// We can use the original texture when renderering, but `image::ImageBuffer` is slow to sample, so we convert it
// to euc's buffer types.
let texture = Buffer2d::from_texture(&texture);

// Create a sampler from the texture. Because the underlying texture is a bitmap, we map its texels to a
// floating-point color. From here, we allow it to be bilinearly interpolated by the shader.
let sampler = texture
Expand Down
64 changes: 50 additions & 14 deletions src/buffer.rs
Expand Up @@ -26,6 +26,37 @@ unsafe impl<T: Send, const N: usize> Send for Buffer<T, N> {}
unsafe impl<T: Sync, const N: usize> Sync for Buffer<T, N> {}

impl<T, const N: usize> Buffer<T, N> {
/// Copy the texels of an existing [`Texture`] into a new [`Buffer`].
///
/// This is useful if the original texture has slow access times or isn't usable as a render target.
pub fn from_texture<U: Texture<N, Index = usize, Texel = T>>(tex: &U) -> Self {
let tex_size = tex.size();
let mut idx = [0; N];
let iter = core::iter::once([0; N]).chain(core::iter::from_fn(move || {
let mut i = 0;
loop {
if i == N {
break None;
} else if idx[i] + 1 == tex_size[i] {
i += 1;
} else {
idx[..i].iter_mut().for_each(|x| *x = 0);
idx[i] += 1;
break Some(idx);
}
}
}));

Self {
size: tex_size,
items: unsafe {
iter.map(|idx| UnsafeCell::new(tex.read_unchecked(idx)))
.collect::<Vec<_>>()
.into_boxed_slice()
},
}
}

/// Create a new buffer with the given size, filled with duplicates of the given element.
#[inline]
pub fn fill(size: [usize; N], item: T) -> Self
Expand All @@ -52,15 +83,20 @@ impl<T, const N: usize> Buffer<T, N> {
}

/// Convert the given index into a linear index that can be used to index into the raw data of this buffer.
#[inline]
#[inline(always)]
pub fn linear_index(&self, index: [usize; N]) -> usize {
let mut idx = 0;
let mut factor = 1;
(0..N).for_each(|i| {
idx += index[i] * factor;
factor *= self.size[i];
});
idx
// Special-case
if N == 2 {
index[0] + self.size[0] * index[1]
} else {
let mut idx = 0;
let mut factor = 1;
(0..N).for_each(|i| {
idx += index[i] * factor;
factor *= self.size[i];
});
idx
}
}

/// View this buffer as a linear slice of elements.
Expand Down Expand Up @@ -99,7 +135,7 @@ impl<T, const N: usize> Buffer<T, N> {
/// # Safety
///
/// Undefined behaviour will occur if the index is not within bounds.
#[inline]
#[inline(always)]
pub unsafe fn get_unchecked_mut(&mut self, index: [usize; N]) -> &mut T {
let idx = self.linear_index(index);
self.items.get_unchecked_mut(idx).get_mut()
Expand Down Expand Up @@ -136,7 +172,7 @@ impl<T: Clone, const N: usize> Texture<N> for Buffer<T, N> {
unsafe { (*item.get()).clone() }
}

#[inline]
#[inline(always)]
unsafe fn read_unchecked(&self, index: [Self::Index; N]) -> Self::Texel {
let item = self.items.get_unchecked(self.linear_index(index));
// SAFETY: Invariants can only be violated by `write_exclusive_unchecked`
Expand All @@ -145,14 +181,14 @@ impl<T: Clone, const N: usize> Texture<N> for Buffer<T, N> {
}

impl<T: Clone> Target for Buffer<T, 2> {
#[inline]
#[inline(always)]
unsafe fn read_exclusive_unchecked(&self, x: usize, y: usize) -> Self::Texel {
let item = self.items.get_unchecked(self.linear_index2(x, y));
// SAFETY: Invariants can only be violated by `write_exclusive_unchecked`
unsafe { (*item.get()).clone() }
}

#[inline]
#[inline(always)]
unsafe fn write_exclusive_unchecked(&self, x: usize, y: usize, texel: Self::Texel) {
let item = self.items.get_unchecked(self.linear_index2(x, y));
// This is safe to do provided the caller has guaranteed exclusive access to the texels being written to, as
Expand All @@ -162,13 +198,13 @@ impl<T: Clone> Target for Buffer<T, 2> {
}
}

#[inline]
#[inline(always)]
unsafe fn write_unchecked(&mut self, x: usize, y: usize, texel: Self::Texel) {
let idx = self.linear_index2(x, y);
*self.items.get_unchecked_mut(idx) = UnsafeCell::new(texel);
}

#[inline]
#[inline(always)]
fn write(&mut self, x: usize, y: usize, texel: Self::Texel) {
let idx = self.linear_index2(x, y);
self.items[idx] = UnsafeCell::new(texel);
Expand Down
12 changes: 6 additions & 6 deletions src/texture.rs
Expand Up @@ -301,7 +301,7 @@ impl<T> Default for Empty<T> {
impl<T: Clone, const N: usize> Texture<N> for Empty<T> {
type Index = usize;
type Texel = T;
#[inline]
#[inline(always)]
fn size(&self) -> [Self::Index; N] {
[0; N]
}
Expand All @@ -312,11 +312,11 @@ impl<T: Clone, const N: usize> Texture<N> for Empty<T> {
}

impl<T: Clone + Default> Target for Empty<T> {
#[inline]
#[inline(always)]
unsafe fn read_exclusive_unchecked(&self, _: usize, _: usize) -> Self::Texel {
T::default()
}
#[inline]
#[inline(always)]
unsafe fn write_exclusive_unchecked(&self, _: usize, _: usize, _: Self::Texel) {}
}

Expand All @@ -329,17 +329,17 @@ where
type Index = usize;
type Texel = P;

#[inline]
#[inline(always)]
fn size(&self) -> [Self::Index; 2] {
[self.width() as usize, self.height() as usize]
}

#[inline]
#[inline(always)]
fn preferred_axes(&self) -> Option<[usize; 2]> {
Some([0, 1])
}

#[inline]
#[inline(always)]
fn read(&self, [x, y]: [Self::Index; 2]) -> Self::Texel {
self.get_pixel(x as u32, y as u32).clone()
}
Expand Down

0 comments on commit 290e14c

Please sign in to comment.