[MG_Backend, MG_IntegrationTest, MG_Test] (P7): a multisample depth or stencil buffer resolves on a device with no resolve extension, and the sample-count shapes with no arm decline

CONTRACT-P7 §3.2's `multisample-blit-aspect@P7`, the last `@P7` refusal in the tree.
Two halves again, and neither is the other's fallback.

THE HALF WITH AN ARM. vkCmdResolveImage is COLOUR ONLY. The one way Vulkan
resolves a depth or stencil aspect without a shader is a render pass with a
depth/stencil resolve attachment, which needs VK_KHR_create_renderpass2 and
VK_KHR_depth_stencil_resolve - and a device with neither ended the session at
`Magma:multisample-depth-resolve-capability` on the first
glBlitFramebuffer(GL_DEPTH_BUFFER_BIT) out of a multisampled read framebuffer,
which is what every multisample depth resolve in GL is. §3.2 names the second arm
and this is it: WireMultisampleResolve.inc, three baked modules (one vertex stage,
one fragment stage per aspect), fetching SAMPLE ZERO - the same sample
VK_RESOLVE_MODE_SAMPLE_ZERO_BIT picks, so the two arms answer identically and the
lane can assert that.

The pass carries nothing: the scratch has the source's extent and coordinate
space, so the fragment stage indexes by gl_FragCoord and the rectangle is the
scissor. No push constants, no varyings, no frontend object. The stencil half
needs VK_EXT_shader_stencil_export to exist at all (a fragment shader cannot write
the stencil aspect otherwise); where it is absent the arm declines stencil rather
than leaving the aspect undefined, and the module is not even created, because
handing vkCreateShaderModule SPIR-V with an unsupported capability is undefined
rather than an error.

The two capability tests are deliberately not the same test: the subpass resolves
every aspect the FORMAT has and needs a mode for each, while the pass writes only
the aspect this blit asked for - which is safe because the copy-out reads exactly
that aspect.

THE HALF WITH NO ARM. Sample counts that differ with the DESTINATION multisampled:
one sample fanned out to N, or N reduced to M. vkCmdResolveImage only ever writes
a single-sample destination, vkCmdBlitImage refuses a multisampled side, and
vkCmdCopyImage needs the counts equal. Rule I (a): name both counts, record
INVALID_OPERATION, leave the destination alone.

Lanes. DepthStencilReadbackMatrixScenario.AResolvedMultisampleDepth… is PROMOTED
from the informational tier into the three gated Magma arms - it already owns the
observable (clear a 4x DEPTH24_STENCIL8, resolve with glBlitFramebuffer, read both
aspects back, check that a depth-only resolve left the destination's stencil alone
and vice versa), it just had no gated entry. Two entries over it per arm: plain,
and under MGITEST_MAGMA_FORCE_SHADER_DEPTH_RESOLVE, which is the only way the new
arm is reachable here at all - lavapipe carries BOTH resolve extensions, so the
extension arm answers every resolve on this host. The new F1 case is the decline.
It does NOT skip on this host: the shader arm resolves depth and stencil, because
lavapipe also carries VK_EXT_shader_stencil_export.

Three rows join MOBILEGL_BAKED_INTERNAL_SHADERS (8 -> 11).

Red once (R-16, executed and reverted), three times:
  * restore MagmaWireFatal("multisample-depth-resolve-capability") for the
    non-extension arm -> DirectVulkan.Split.MsResolve1.… dies by that name while
    MsResolve0 stays green, which is also the proof that the knob routes;
  * restore MagmaWireFatal("multisample-blit-aspect@P7") ->
    DirectVulkan.Split.MsBlit.…FromASingleSampleSourceDeclines… dies by that name;
  * fetch sample 1 instead of 0 in WireMultisampleStencilResolve.frag (no rebuild -
    the gate reads the source at run time) -> WireMultisampleStencilResolveFragment
    reds at word 158, naming the symbol, the header and the regeneration command.
Reverted, 15/15 green on the three arms and 11/11 on the freshness gate.

`grep -rn "@P7" MobileGL/ --include=*.cpp --include=*.inc --include=*.h` now
returns COMMENTS ONLY - 19 lines, every one of them prose about a retirement or a
red-once. No refusal in the tree carries the mark any more.

Gates: integration-magma-split 93 (+3), -spawn 72 (+3), -tcp 74 (+3),
integration-magma-full-split 524 (+1), ctest -L unit 2423 (+3), all green;
bake_internal_shaders.py --check reports all five headers fresh; fatal_census 79
abort sites / 44 family words / 0 unmarked; link_ratchet --assert-monotone
unchanged at 186; G1 pull .text 0xa52203, nm --defined-only identical to
p7-before/pull-syms.txt (30570 lines, 0 diffs) - every line lives under
`#if MOBILEGL_BUILD_DISAGGREGATED` or in the test tree.

Device debt: the shader arm is the arm the PHONE takes wherever
VK_KHR_depth_stencil_resolve is missing, and this host can only reach it through
the knob. Its geometry needs one look on the Redmi.
This commit is contained in:
2026-09-22 18:29:22 -04:00
parent 452d33b578
commit 51fa513d73
11 changed files with 739 additions and 14 deletions
@@ -3446,6 +3446,7 @@ void main() {
ClearAllWireDrawPassCaches();
DestroyWireColorBlitResources();
DestroyWireDepthMipmapResources();
DestroyWireMultisampleResolveResources();
#endif
OnSubmitsCompletedUpTo(m_submitCounter);
DestroySubmitFencePool();
@@ -7913,6 +7914,9 @@ void main() {
// AFTER WireColorBlit.inc: the depth mip reuses that file's window-Y convention and is
// written to be read beside it.
#include "WireDepthMipmap.inc"
// AFTER WireDepthMipmap.inc for the same reason: the multisample depth/stencil resolve is
// that file's pass with the box filter taken out and a sample index put in.
#include "WireMultisampleResolve.inc"
#include "WireDraw.inc"
void VulkanRenderer::Clear(GLbitfield mask) {
@@ -14777,6 +14781,16 @@ void main() {
m_wireDepthResolveModes = resolveProperties.supportedDepthResolveModes;
m_wireStencilResolveModes = resolveProperties.supportedStencilResolveModes;
}
// P7 wave 2-B2, CONTRACT-P7 §3.2 (`multisample-blit-aspect`): the shader resolve's
// stencil half needs SPV_EXT_shader_stencil_export, because a fragment shader cannot
// write the stencil aspect without it. Enabled on the same terms as the resolve
// extensions above - wire arms only - and the arm declines stencil where it is absent
// rather than producing an undefined aspect.
m_wireShaderStencilExport = MG_Config::Transport != MG_Config::TransportMode::Monolith &&
IsExtensionSupported(availableExtensions, VK_EXT_SHADER_STENCIL_EXPORT_EXTENSION_NAME);
if (m_wireShaderStencilExport)
EnableOptionalDeviceExtension(availableExtensions, enabledDeviceExtensions,
VK_EXT_SHADER_STENCIL_EXPORT_EXTENSION_NAME);
#endif
if ((descriptorIndexingCore || descriptorIndexingExtension) && getPhysicalDeviceFeatures2 != nullptr &&
getPhysicalDeviceProperties2 != nullptr) {