[Fix] (Espryt): take the sampler CSO record from the handle the caller carries - a content-addressed record can never be named by the twins own identity handle, so every bound sampler objects parameters were refused on the integrated tree

This commit is contained in:
rereview
2026-09-08 18:14:22 -04:00
parent a632e26e47
commit 1e8cdc5931
2 changed files with 93 additions and 17 deletions
+68 -17
View File
@@ -12287,8 +12287,14 @@ namespace MobileGL::MG_Backend::DirectGLES {
m_backendSamplerId = 0;
}
#if MOBILEGL_PIPE_PUSH
void BackendSamplerObject::SyncToBackend(
const SharedPtr<MG_State::GLState::SamplerObject>& stateSamplerObject,
MG_Pipe::MGPipeHandle pushedCso) {
#else
void BackendSamplerObject::SyncToBackend(
const SharedPtr<MG_State::GLState::SamplerObject>& stateSamplerObject) {
#endif
#ifdef TRACY_ENABLE
ZoneScopedC(TRACY_ZONECOLOR_BACKEND);
#endif
@@ -12309,24 +12315,69 @@ namespace MobileGL::MG_Backend::DirectGLES {
#if MOBILEGL_PIPE_PUSH
const SamplerParameters* pushedParams = nullptr;
if (SamplerSubsystemEnabled()) {
// MONOLITH GLUE: the SamplerCso handle of an object this backend still arrives
// holding. Under a real split it rides in the payload, and every path P4a
// switches over already carries it - this is for the paths that do not.
const MG_Pipe::MGPipeHandle cso = g_backendSamplerObjects.HandleOf(stateSamplerObject.get());
const auto* record = PipeSamplerCsoRecordForHandle(cso);
if (record == nullptr) {
MGLOG_E_ONCE("MGPipe: sampler %u has no applier record on the handle arm, so its "
"parameters cannot be pushed (handle {%u, %u})",
stateSamplerObject->GetExternalIndex(), cso.Slot, cso.Gen);
return;
// THE HANDLE COMES FROM THE CALLER, NOT FROM THIS TWIN'S REGISTRY, and the
// difference is the seam D's verification round found on the integrated tree.
//
// v2 asked g_backendSamplerObjects.HandleOf(object) - the twin's own identity
// handle, minted off the frontend lifetime id - and looked the record up by it.
// That can only ever miss: a SamplerCso is CONTENT-ADDRESSED on the client
// (D-F1, ID-14/ID-17), its handles come out of MGPipeSlots().Allocate keyed on a
// parameter hash, and nothing ever emits a create_sampler_state at an identity
// handle. On the integrated tree every glBindSampler-driven parameter set was
// therefore refused and the driver sampler kept its defaults - two integration
// scenarios red, and the twin's own comment below (content-addressed, the
// record's Serial is the authority) already said why it could not work.
//
// The carried fact is MGPipeApplier().BoundSamplerStates[unit], written by the
// client at bind_sampler_states; the caller that knows the unit passes it as
// `pushedCso`. See the declaration for why the parameter is push-only.
if (!MG_Pipe::MGPipeHandleIsNull(pushedCso)) {
const auto* record = PipeSamplerCsoRecordForHandle(pushedCso);
if (record == nullptr) {
MGLOG_E_ONCE("MGPipe: sampler %u has no applier record on the handle arm, so its "
"parameters cannot be pushed (handle {%u, %u})",
stateSamplerObject->GetExternalIndex(), pushedCso.Slot, pushedCso.Gen);
return;
}
if (m_isInitialized && m_syncedSamplerSerial != 0 && m_syncedSamplerSerial == record->Serial) {
MGLOG_D("Sampler parameters have not changed for sampler ID: %u, skipping sync.",
stateSamplerObject->GetExternalIndex());
return;
}
m_syncedSamplerSerial = record->Serial;
pushedParams = &record->Params;
} else {
// NO HANDLE CARRIED. Two shapes, and they are told apart by whether this
// object is in the twin registry at all:
//
// * not registered -> a sampler this BACKEND minted for its own use (the
// raw-depth-fetch sampler, DirectGLES.cpp:207-218). The client has never
// seen it, no record can exist for it now or after any package lands,
// and the object IS the authority for server-owned state. Not a seam.
// * registered -> an application sampler whose caller did not carry the
// unit's handle. That is the E-side call-site gap; it is named once and
// the values are taken from the object, which in monolith are the very
// values the client content-addressed, so the picture stays right while
// the gap is visible rather than silent.
if (!MG_Pipe::MGPipeHandleIsNull(
g_backendSamplerObjects.HandleOf(stateSamplerObject.get()))) {
MGLOG_E_ONCE("MGPipe: sampler %u was synced without its unit's SamplerCso handle, so "
"the content-addressed record cannot be named and the object's own "
"parameters are used - the caller must pass "
"MGPipeApplier().BoundSamplerStates[unit]",
stateSamplerObject->GetExternalIndex());
}
// Spelled exactly as the pre-handle arm below spells it (compare widened,
// assign narrowed) so the two cannot drift on a version past 65535.
const Uint currentSamplerVersion = stateSamplerObject->GetVersion();
if (m_isInitialized && m_syncedSamplerVersion == currentSamplerVersion) {
MGLOG_D("Sampler parameters have not changed for sampler ID: %u, skipping sync.",
stateSamplerObject->GetExternalIndex());
return;
}
m_syncedSamplerVersion = currentSamplerVersion;
pushedParams = &stateSamplerObject->GetAllSamplerParameters();
}
if (m_isInitialized && m_syncedSamplerSerial != 0 && m_syncedSamplerSerial == record->Serial) {
MGLOG_D("Sampler parameters have not changed for sampler ID: %u, skipping sync.",
stateSamplerObject->GetExternalIndex());
return;
}
m_syncedSamplerSerial = record->Serial;
pushedParams = &record->Params;
} else {
#if !MOBILEGL_PIPE_LEGACY_MEMOS
// UNREACHABLE: ResolveSamplerSubsystemArm stops at its first call when the bit is
+25
View File
@@ -2527,7 +2527,32 @@ namespace MobileGL::MG_Backend::DirectGLES {
~BackendSamplerObject();
BackendSamplerObject(const BackendSamplerObject&) = delete;
BackendSamplerObject& operator=(const BackendSamplerObject&) = delete;
#if MOBILEGL_PIPE_PUSH
// THE SAMPLER CSO HANDLE IS CARRIED BY THE CALLER, and it has to be, because a
// SamplerCso is CONTENT-ADDRESSED on the client (D-F1) while this twin is keyed on
// the frontend OBJECT. g_backendSamplerObjects mints a SamplerCso slot off the
// SamplerObject's lifetime id - that handle is this twin's identity and is what
// FindByHandle memos index - but the client's cache allocates its handles by
// CONTENT (MGPipeSlots().Allocate, SamplerEmit.h), so no create_sampler_state ever
// lands at the identity handle and looking a record up by it can only ever miss.
// The carried fact that DOES name the right record is the applier's own
// MGPipeApplier().BoundSamplerStates[unit], which the client writes per unit at
// bind_sampler_states; the caller that knows the unit passes it here.
//
// Defaulted so a caller that has no unit - the backend's OWN raw-depth-fetch
// sampler (DirectGLES.cpp:217), a SamplerObject the client has never seen and for
// which no record can exist - keeps working: that arm reads the object, which is
// the authority for server-owned state. An APPLICATION sampler reaching here
// without a handle is the E-side call-site gap and says so once.
//
// Push-only spelling on purpose: a defaulted parameter is still part of the
// signature, so widening it unconditionally would rename this symbol in the PULL
// build and P4a's admitted-change set is EMPTY (D-P/G1).
void SyncToBackend(const SharedPtr<MG_State::GLState::SamplerObject>& stateSamplerObject,
MG_Pipe::MGPipeHandle pushedCso = MG_Pipe::kMGPipeNullHandle);
#else
void SyncToBackend(const SharedPtr<MG_State::GLState::SamplerObject>& stateSamplerObject);
#endif
void Bind(Uint unit);
Uint GetBackendSamplerId() const;