𝗧𝗿𝗮𝗻𝘀𝗽𝗮𝗿𝗲𝗻𝗰𝘆 in 2D Rendering isn't as straightforward as you might think.
Drawing an object with the 𝗽𝗿𝗲𝘃𝗶𝗼𝘂𝘀 𝗼𝗯𝗷𝗲𝗰𝘁'𝘀 𝗰𝗼𝗹𝗼𝗿? But why!?
Building 2D GPU-Driven renderers is a unique challenge. Instead of standard meshes, you are directly rendering lines, curves, UI components, and vector graphics. If you want transparency on these 2D components while leveraging the GPU's blending unit, you have to find a way to prevent it from blending with itself.
𝗧𝗵𝗲 𝗵𝗮𝗿𝗱𝘄𝗮𝗿𝗲 𝗹𝗶𝗺𝗶𝘁𝗮𝘁𝗶𝗼𝗻: The GPU doesn't inherently know which fragments belong to the same UI Component or Polyline. The graphics pipeline will just blindly blend overlapping pixels.
To solve this, we built a custom software blending algorithm (battle tested in n4ce's 2D survey renderer)
Here is how it works:
• We use an object-aware offscreen texture to accumulate alphas, rather than outputting to render target directly.
• When do we finally output to the target? Only when we are absolutely certain we are DONE drawing the current object.
• If the next object gets drawn on top, we render it using the previous object's color and resolve!
• A fullscreen pass at the end resolves the colors of anything that was accumulated but not yet written to the render target.
The logic is sound, but hardware execution introduces a caveat. While a GPU guarantees primitives will blend in the order they were submitted, 𝗶𝘁 𝗱𝗼𝗲𝘀 𝗻𝗼𝘁 𝗴𝘂𝗮𝗿𝗮𝗻𝘁𝗲𝗲 your fragment shaders will process them in that same order. To prevent race conditions, this algorithm relies on 𝗙𝗿𝗮𝗴𝗺𝗲𝗻𝘁 𝗦𝗵𝗮𝗱𝗲𝗿 𝗜𝗻𝘁𝗲𝗿𝗹𝗼𝗰𝗸. This hardware feature enforces strict synchronization, ensuring reads and writes to our offscreen alpha accumulation texture happen in the exact order the draws were submitted.
We’ve presented this algorithm at Vulkanised 2024:
devsh.eu/presentations