An Analysis of Michael Abrash’s Assembly Optimizations in Quake’s Software Renderer
The article examines whether Michael Abrash’s hand-written x86 assembly code truly doubled the performance of Quake’s software renderer, as suggested by John Carmack’s original readme after the 1999 source code release. To verify the claim, the author compiled and benchmarked three versions of WinQuake on a Pentium MMX 233 MHz machine: the original assembly-optimized build, a rebuilt version with assembly enabled, and a version compiled entirely in C by disabling the id386 flag. Using a timedemo benchmark, the assembly-enabled builds achieved approximately 42 frames per second, while the pure C version ran at only 22.7 fps—almost exactly half the performance.
The investigation then breaks down the 63 assembly functions in the source tree, narrowing them to 32 relevant performance-critical routines related to drawing, rendering, math, and sound. By selectively enabling individual assembly routines, the author quantified their performance impact. The most significant gains came from low-level drawing functions such as D_DrawSpans8, which alone added 12.6 fps, and R_DrawSurfaceBlock8_mip variants responsible for texture and lightmap combination. Model rendering routines (the D_Polyset group) also contributed notable improvements.
A detailed comparison between the assembly and compiler-generated code for TransformVector illustrates how Abrash leveraged the Pentium’s dual pipelines and pipelined FPU to avoid instruction stalls. Techniques included instruction reordering, parallel dot-product computation, latency hiding, loop unrolling, self-modifying code, and careful scheduling of floating-point stores. The article highlights how these optimizations exploited the Pentium architecture in ways compilers of the era could not, ultimately confirming that Abrash’s assembly work nearly doubled Quake’s frame rate on late-1990s hardware.
