render3d(vulkan): MSAA - multisampled renderbuffers, resolved in a draw-less pass

gpu_rb_storage makes the image with the samples asked for; a pass takes its attachments' count and
every pipeline in it matches; a blit from a multisampled source resolves on the end of an empty
dynamic-rendering pass (colour averaged, depth from sample zero - vkCmdResolveImage cannot do depth).
gpu_msaa_max reads the device's colour-and-depth sample limits, and post_set_msaa clamps to it, so
the Anti-aliasing setting is live on Vulkan. The pipeline cache's pass key no longer packs samples
into three bits.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Orkun ÇAKILKAYA 2026-09-15 16:21:52 +03:00
parent 97d75e1d5e
commit bda53f759e
4 changed files with 74 additions and 7 deletions

View file

@ -56,13 +56,15 @@ function post_free() -> void {
for i in 0 .. len(post_bloom) { target_free(post_bloom[i]) }
post_hdr = null
}
# Multisampled scene: 1 (temporal AA alone), 2 or 4. OpenGL remakes the scene targets at once;
# the Vulkan backend has no sample counts yet and keeps drawing single-sampled (post_msaa_live says so).
function post_msaa_live() -> bool { return gpu_is_gl() }
# Multisampled scene: 1 (temporal AA alone), 2 or 4, remade at once. Vulkan draws it into
# multisampled renderbuffers and resolves them in a pass; a device that cannot take the count asked
# for gets the most it can (gpu_msaa_max).
function post_msaa_live() -> bool { return gpu_is_gl() or gpu_msaa_max() > 1 }
function post_set_msaa(n: int) -> void {
var want = n
if want < 1 { want = 1 }
if not post_msaa_live() { want = 1 }
if want > gpu_msaa_max() and gpu_msaa_max() >= 1 { want = gpu_msaa_max() }
if want == post_ms_samples { return }
post_ms_samples = want
if post_hdr != null {