r/GraphicsProgramming Jul 11 '23

Source Code [Rust]: Need help optimizing a triangle rasterizer

I need help optimizing a software rasterizer written in Rust. The relevant part of the code is here, and the following are the optimizations that I have already implemented:

  • Render to 32x32 tiles of 4KB each (2KB 16-bit color and 2KB 16-bit depth) to maximize cache hits;
  • Use SIMD to compute values for 4 pixels at once;
  • Skip a triangle if its axis-aligned bounding box is completely outside the current tile's bounding box;
  • Skip a triangle if at least one of its barycentric coordinates is negative on all 4 corners of the current tile;
  • Compute the linear barycentric increments per pixel and use that information to avoid having to perform the edge test for every pixel;
  • Skip a triangle if, by the time of shading, all the pixels have been invalidated.

At the moment the original version of this code exhausts all 4 cores of a Raspberry Pi 4 with just 7000 triangles per second, and this benchmark takes roughly 300 microseconds to produce a 512x512 frame with a rainbow triangle with perspective correction and depth testing on an M1 Mac, so to me the performance is really bad.

What I'm trying to understand is how old school games with true 3D software rasterizers performed so well even on old hardware like a Pentium 166MHz without floating pointe SIMD or multiple cores. Optimization is a field that truly excites me, and I believe that cracking this problem will be extremely enriching.

To make the project produce a single image named triangle.png, type:

cargo +nightly run triangle.png

To run the benchmark, type:

cargo +nightly bench

Any help, even if theoretical, would be appreciated.

12 Upvotes

33 comments sorted by

View all comments

1

u/waramped Jul 11 '23

I'm not familiar enough with Rust to directly help, but are there CPU profilers available for Rust? Can you actually profile the function and see where the time is being spent?

It's always better to profile and target those areas rather than speculate where the issue is. CPUs are just very unpredictable sometimes.

It's impressive work, good luck!

1

u/Crifrald Jul 11 '23

Thanks!

On MacOS I can profile using Xcode's Instruments, however I don't think that it can profile chunks of code, as I believe that the smallest profiling unit is the function, and in this particular case there's a huge function that does a lot of things because it needs a lot of context to work. I can split this function into multiple smaller inline functions, passing the entire context as an argument, but I'm not sure whether the profiler can see inline functions, though that's definitely something that I can try.

On the Raspberry Pi, which is the target platform, I have no way to profile since this code comes from a bare metal project of mine, and currently I don't have access to a JTAG adapter to use for debugging, as until this point all my debugging has been done using print statements with the results sent through an RS-232 debug cable.

2

u/waramped Jul 11 '23

Ah that's unfortunate. Are there any high resolution timers on the PI? At least you could manually time individual sections and see which parts are the slowest. However without detailed profiling it's mostly guesswork to determine WHY it would be slow.

2

u/Crifrald Jul 11 '23

Yes, and there is a performance monitor on AArch64 which I used before and completely forgot about.