Skip to content

[rlsw] Variant dispatch refactor + bunch of improvements#5996

Merged
raysan5 merged 10 commits into
raysan5:masterfrom
Bigfoot71:variants-refactor
Jul 21, 2026
Merged

[rlsw] Variant dispatch refactor + bunch of improvements#5996
raysan5 merged 10 commits into
raysan5:masterfrom
Bigfoot71:variants-refactor

Conversation

@Bigfoot71

Copy link
Copy Markdown
Contributor

Summary

While experimenting with a different way to handle framebuffers, I ended up simplifying the variant-generation system for the rasterizer's pipeline specializations.
That simplification unlocked a series of small cascading optimizations, which I've bundled into this PR.

Changes

Variant generation

  • Replaced the hand-written SW_RASTER_VARIANTS(X) X-macro lists (one explicit NAME, FLAGS pair per specialization) with a token-based system, a variant is now just a plain list of state names (eg. TEXTURE_2D, DEPTH_TEST), from which the function name, its SW_STATE_* flags, and its dispatch table slot are all derived automatically via SW_FLAGS(...)/SW_NAME(...).
  • This removes the risk of a name/flag mismatch (previously two independent sources of truth per variant) and made adding new pipeline states straightforward.
  • Added a small vector-math helper macro SW_VEC_OP to cut down on repetitive per-component code across triangle/quad/line rasterizers.

New pipeline state: COLOR_INTERP

  • Added SW_STATE_COLOR_INTERP, toggled off automatically when all vertices of a primitive share the same color.
  • When disabled the rasterizer skips per-pixel/per-scanline color interpolation entirely, and uses a single flat color instead, cheaper than a "flat shading" pass since no interpolation math runs at all.
  • Applied to triangle, quad, and line rasterizers.

Rasterization optimizations

  • Triangle: attribute x/y-derivatives (dWdx, dZdx, dCdx, dTdx, dTdy) are now computed once per triangle via a whole-triangle plane-gradient solve, instead of being recomputed on every scanline.
  • Triangle: perspective-correct interpolation (w-tracking and block subdivision) is now skipped entirely for variants that don't need it (no COLOR_INTERP, no TEXTURE_2D; eg. plain depth/blend-only triangles).
  • Triangle/Quad: the min/mag texture filter is now resolved once per primitive (sw_texture_pick_sampler) instead of being recomputed for every sampled pixel.
  • Quad: avoids an unnecessary per-pixel color copy when there's no texture to modulate against.
  • Removed goto based depth-test discard in triangle/quad/line rasterizers in favor of a branch that fully disappears from the compiled variants where depth testing is disabled.

Misc

  • Removed explicit inline keywords, letting the compiler decide on its own, but kept SW_INLINE (forced inlining) only for the cases where it's known to matter.
  • Improved/added comments in a few non-obvious spots (perspective math, degenerate-triangle guards, etc).

Performance

Without detailed profiling, models_first_person_maze (O2, default conservative rlsw config) gained roughly 20 FPS avg on my Ryzen 5 3600.

textures_bunny_mark showed no noticeable difference, but rasterization itself isn't the main bottleneck now on "modern desktop hardware" (as implemented, naturally).
That said, the COLOR_INTERP elimination should matter more on more limited targets where every avoided interpolation counts.

Future work

  • The same "skip interpolation when constant across the primitive" could later be applied to clipping and vertex processing, not just rasterization, not an immediate priority though.
  • Rasterization still has room for further hardware-specific specializations, and other techniques like "deferred rendering" could also be beneficial, at the cost of using a bit more memory.

@Bigfoot71

Copy link
Copy Markdown
Contributor Author

Also worth noting, I moved all configuration #define to the top of the header, into their own dedicated section, for clarity.

While doing so I noticed a small naming inconsistency, RLSW_USE_SIMD_INTRINSICS uses the RLSW_ prefix, while every other config define uses SW_.

Happy to fix this either way, prefixing all config defines with RLSW_, or renaming this one to SW_USE_SIMD_INTRINSICS for consistency with the rest. Let me know which you'd prefer.

@NighthowlerStudios

NighthowlerStudios commented Jul 19, 2026

Copy link
Copy Markdown
Contributor

Added SW_STATE_COLOR_INTERP, toggled off automatically when all vertices of a primitive share the same color.

I was actually hoping somebody would get around to this. I attempted it previously but you know better than I do. Embedded platforms, in theory, should see serious speed ups with it. I'll leave my measurements here when I get around to it.

@raysan5

raysan5 commented Jul 20, 2026

Copy link
Copy Markdown
Owner

@Bigfoot71 thanks for this big update! I need some time to review it more carefully...

In the meantime, keeping consistency with SW_ prefix looks good to me.

It's just a detail but would it be possible to move the inline replacement into a separate PR, it would be easier to review main changes, also for future reviews over this PR once merged... if it supposes lot of inconvenience, no problem.

@NighthowlerStudios

NighthowlerStudios commented Jul 20, 2026

Copy link
Copy Markdown
Contributor

Added SW_STATE_COLOR_INTERP, toggled off automatically when all vertices of a primitive share the same color.

I was actually hoping somebody would get around to this. I attempted it previously but you know better than I do. Embedded platforms, in theory, should see serious speed ups with it. I'll leave my measurements here when I get around to it.

The final number is about a 10% uplift to waving_cubes (630ms worst to 570ms worst) and bunnymark for me (30 more alpha-clipped bunnies at 5fps, totalling 250).

Raster is definitely faster than it once was for very large single colour unlit triangles (say at least 10 pixels wide or larger). I did verify there wasn't any maths issues using prints on the interp check and indeed it does only set the interp flag when using the simple_shapes example. However dispatching many triangles (or even quads, apparently) still hits a limit purely because of the overhead needed to setup each span. And that's expected, so nothing we can do about it.

Developers on embedded should be making sure to control their overdraw anyway (harkens back to the N64, etc). But these uplifts are definitely welcome when drawing, say, a giant black box to clear some area of the screen for text (like bunnymark).

I think the most interesting benchmarks would be first person maze, etc, since those triangles are enormous. but I need to write a proper demo for it since my platform doesn't have enough buttons.

@NighthowlerStudios

NighthowlerStudios commented Jul 20, 2026

Copy link
Copy Markdown
Contributor

I think the most interesting benchmarks would be first person maze, etc, since those triangles are enormous. but I need to
write a proper demo for it since my platform doesn't have enough buttons.

Ran these examples. The optimization lifts us about 10% across the board. I'm having platform-specific issues with ram access completely irrelevant to this but these definitely help. I assume the uplift will be even more if the assets are in the fastest ram possible on the board.

@Bigfoot71
Bigfoot71 force-pushed the variants-refactor branch from 7350ed1 to 2fceb9c Compare July 20, 2026 12:51
@Bigfoot71

Bigfoot71 commented Jul 20, 2026

Copy link
Copy Markdown
Contributor Author

It's just a detail but would it be possible to move the inline replacement into a separate PR, it would be easier to review main changes, also for future reviews over this PR once merged... if it supposes lot of inconvenience, no problem.

No problem, I dropped the commit in question, there are no more inline related diffs.

I still kept the new SW_INLINE for sw_rcp since it was already using the attribute, and it avoids messing up the history too much.

Edit: I would propose this change in a separate PR, along with the fix for configuration defs consistency.

@raysan5
raysan5 merged commit aa4cf20 into raysan5:master Jul 21, 2026
16 checks passed
@raysan5

raysan5 commented Jul 21, 2026

Copy link
Copy Markdown
Owner

@Bigfoot71 thank you very much! great improvements!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants