[MG_Backend, MG_Remote, MG_Impl, MG_State] (Disaggregated): P5c hd - sinks and twins resolve by the handle the record carried

A split-only verb-handle workspace in MGPipeApplierState (written by the sinks before
dispatch, read by the backend within the same verb) carries the record's handles to the
backend entries whose shared function-table signatures cannot grow them without moving
G1. OnBlit resolves ReadFbo/DrawFbo through the FBO twin table by handle (the named arm
of BlitFramebuffer, a loud refusal when the backend does not consume it) and the split
path's ScopedBlitBindings rebinding is deleted; CopyTex resolves MGPCopyFromFramebuffer::Dst;
the mip descriptor check resolves MGPMipPlan::Res; the indirect families resolve their
buffer handles from the records and read the bytes from the server staged shadow with
RequireStagedCoverage. EnsureBufferResourceForHandle answers HasDefinedContent from
Desc.HasDefinedContent OR the staged coverage set (the streaming idiom defines content
through subdata with no new descriptor). The minting GetOrCreate(StatePtr), HandleOf and
the death switch's lifetime-id arms take Fatal{RoleViolation, MGPipeSlots} from the apply
thread, as do the three MGPipeSlots() entry points; the frontend BufferObject legacy
accessors take Fatal{RoleViolation, buffer-legacy-arm}; Magma's four caps reads go to the
server's own backend and the client-mirror fallback arm is a named refusal.

Evidence: unit 2156/2156 (incl. 9 new RemoteF1 + 10 new RemoteGuards death tests pinning
the four Fatal names); monolith smoke green; six production mutations each turned their
named test red and were restored; c1f_redcheck M5 RED then RESTORED GREEN. The mip
descriptor check overlaps tx and is coordinated at the merge (tx's sync rekey wins the
sync body, this handle resolution wins the identity check). CONTRACT-P5C section 3.
This commit is contained in:
2026-09-17 04:55:50 -04:00
parent 851bc70c1d
commit 7f5bdd3aa5
18 changed files with 1286 additions and 148 deletions
@@ -1,4 +1,4 @@
// MobileGL - MobileGL/MG_Backend/DirectVulkan/DirectVulkan.cpp
// MobileGL - MobileGL/MG_Backend/DirectVulkan/DirectVulkan.cpp
// Copyright (c) 2025-2026 MobileGL-Dev
// Licensed under the GNU Lesser General Public License v3.0:
// https://www.gnu.org/licenses/gpl-3.0.txt
@@ -17,6 +17,10 @@
#include "MG_Util/Metrics/PipeStats.h"
#include "MG_Util/Metrics/TextureMetrics.h"
#include "MG_Util/Miscellany/IndexGenerator.h"
#if MOBILEGL_BUILD_DISAGGREGATED
#include <Config.h>
#include <MG_Remote/Server/ServerLoop.h>
#endif
#include <atomic>
#include <bit>
#include <cstring>
@@ -710,9 +714,23 @@ namespace MobileGL::MG_Backend::DirectVulkan {
void ShaderStorageBlockBinding(GLuint program, const GLchar* storageBlockName, GLuint storageBlockBinding) {
auto* programObject = TryGetDirectVulkanProgram(program);
if (!programObject || storageBlockName == nullptr) return;
const Int maxBindings = pActiveBackendObject
? pActiveBackendObject->GetDynamicParameters().MaxShaderStorageBufferBindings
: 0;
const Int maxBindings =
#if MOBILEGL_BUILD_DISAGGREGATED
// P5c (hd, CONTRACT-P5C §3.7): with an active transport the dynamic parameters are
// the SERVER's own backend's - the client caps mirror (pActiveBackendObject) is
// client memory the apply thread may not name (rule E). Monolith reads the mirror
// as it always did.
MG_Config::Transport != MG_Config::TransportMode::Monolith
? (MG_Remote::Server::ServerLoopInstance().Backend() != nullptr
? static_cast<Int>(MG_Remote::Server::ServerLoopInstance().Backend()
->GetDynamicParameters()
.MaxShaderStorageBufferBindings)
: 0)
:
#endif
pActiveBackendObject
? pActiveBackendObject->GetDynamicParameters().MaxShaderStorageBufferBindings
: 0;
if (storageBlockBinding >= static_cast<GLuint>(maxBindings)) {
MGB_CTX->RecordError(
ErrorCode::InvalidValue,