[Fix] (DirectVulkan): clear an integer colour buffer with an integer value

glClearBufferiv and glClearBufferuiv flattened their values into the payload's float vector,
and every clear was later written into VkClearColorValue::float32. Vulkan reads that union
according to the destination image's format rather than converting between its members, so
an R8I attachment cleared to -16 received the bit pattern of -16.0f. On top of that,
QueueRenderbufferClear copied only the float vector into the pending clear, so even the
flattened value was dropped and the attachment kept reading zero - which is what the
conformance tests actually observed.

The payload now records which of the three entry points supplied the colour and keeps the
value in that form, and one helper builds the union member the encoding calls for. GL's rule
that a format with no alpha channel reads as one has to be applied in the value's own type,
so the "does this format lack alpha" question is now asked separately from the substitution
and the helper applies it to whichever member is live. glClear is left on the float path
explicitly: ClearFramebufferPayload has no other form.

Takes every integer renderbuffer format in direct_state_access.renderbuffers_storage from
failing to passing on Magma - 115 reported mismatches down to 20, the rest being the stencil
formats Espryt fails too and SRGB8_ALPHA8 - and makes framebuffers_clear pass on both
backends.
This commit is contained in:
BZLZHH
2026-08-04 23:55:43 -04:00
parent 19932f9e49
commit 95a7b17d45
4 changed files with 97 additions and 46 deletions
@@ -24,13 +24,32 @@ namespace MobileGL::MG_Backend::DirectVulkan {
Uint32 stencil{};
};
// A colour clear reaches us from one of glClear/ClearBufferfv, ClearBufferiv or
// ClearBufferuiv, and Vulkan reads VkClearColorValue's union according to the destination
// image's format rather than converting between the members - a float written where an
// integer format is expected is reinterpreted bit for bit, not rounded. Remember which entry
// point supplied the value so the member written when the clear is materialized matches.
enum class ClearColorEncoding : Uint8 { Float, Int, Uint };
struct ClearAttachmentPayload {
GLbitfield mask = 0;
FloatVec4 color = FloatVec4(0.0f, 0.0f, 0.0f, 0.0f);
ClearColorEncoding colorEncoding = ClearColorEncoding::Float;
IntVec4 colorInt = IntVec4(0, 0, 0, 0);
UintVec4 colorUint = UintVec4(0u, 0u, 0u, 0u);
Float depth = 1.0f;
Uint32 stencil = 0;
};
// Builds the clear value for `payload` in the union member its encoding calls for.
// `formatLacksAlpha` applies GL's rule that a format without an alpha channel reads as one,
// expressed in whichever type matches (GL 4.6 core 15.2.3).
VkClearColorValue MakeVkClearColorValue(const ClearAttachmentPayload& payload, Bool formatLacksAlpha);
// Applies that same rule in place, for the paths that have to bake it into the payload before
// the destination is known.
void ForceOpaqueClearAlpha(ClearAttachmentPayload& payload);
struct PendingClearKey {
MG_State::GLState::ITextureObject* texture = nullptr;
Uint64 textureLifetimeId = 0;