0

First of all, I'm new to Rust so excuse any "not so great" implementations.

I'm building a game where I need to draw a texture by changing values in a rgba vector that later gets turned into an image (it's for a raycaster game if you're wondering). Inside said function, I mutiply the current pixel with a floating point value called shade that darkens or brightens the pixel depending on the lighting. Since this gets called a lot of times and the profiler flags it as a hotspot, I'm trying to make it as fast as possible, given the current implementation is not the best.

pub fn draw_texture(
    &mut self,
    texture: &Vec<u8>,
    texture_position: [usize; 2],
    pixel_position: [usize; 2],
    width_rect: usize,
    shade: f32,
    length: usize,
) {
    let pos = (texture_position[1] * length + texture_position[0]) << 2; // Position of current pixel
    (0..width_rect).for_each(|i| { // Draws in rectangles of size 1 x width_rect 
        let mut pixel: [u8; 4] = texture[pos..pos + 4].try_into().unwrap(); //rgba pixel

        if shade != 1.0 {  //Draws shade depending on current lighting, darkening or brightening the pixel

            pixel[0] = (pixel[0] as f32 * shade) as u8;
            pixel[1] = (pixel[1] as f32 * shade) as u8;
            pixel[2] = (pixel[2] as f32 * shade) as u8;
        }

        if pixel[3] == 255 {
            //Doesn't draw transparent pixels
            self.draw_pixel([pixel_position[0] + i, pixel_position[1]], &pixel);
        }
    });
}

  pub fn draw_pixel(&mut self, position: [usize; 2], pixel: &[u8; 4]) {
        let i = position[1] * self.width + position[0];
        if (i << 2) + 4 < self.img_arr_len {
            self.img_arr[(i << 2)..(i << 2) + 4].copy_from_slice(pixel);
        }
    }
  • 5
    This might not answer your question, but texture rendering isn't really something CPUs are good at. You can throw multithreading, unsafe code, and vectorization in the mix, add a ton of cruft, but even a mid-range GPU will run circles around any CPU-based Rust implementation. – aedm Mar 17 '22 at 13:41
  • I know, that's why old-school raycaster games are not made anymore haha – Miguel Guerrero Mar 17 '22 at 13:49
  • [Your texture should be input as an `&[u8]` rather than an `&Vec`](https://stackoverflow.com/questions/40006219/why-is-it-discouraged-to-accept-a-reference-to-a-string-string-vec-vec-o) – Aiden4 Mar 17 '22 at 14:11
  • One thing comes to mind: you shade pixels even when you don't draw them. An easy fix is to test sooner. If the texture doesn't change, then a more performant solution might be [run-length encoding](https://en.wikipedia.org/wiki/Run-length_encoding). – aedm Mar 17 '22 at 14:29
  • Bound checks in `draw_pixel` are probably killing you performance-wise → try replacing `self.img_arr[(i << 2)..(i << 2) + 4]` with `unsafe { self.img_arr.get_unchecked ((i << 2)..(i << 2) + 4) }` and add `debug_assert!((i << 2) + 4 < self.img_arr.len())` to recreate the bounds checks in debug mode. – Jmb Mar 17 '22 at 15:45

0 Answers0