[MG_Backend, MG_State] (Disaggregated): P5e tx2 - the two the lane found: Magma's blit read and the copy endpoint's own sync

- The widened MipmapStorage rows are backend-kind-keyed (ruling 12's shape): Magma is
  lockstep through P7 and its named blit still asks a frontend texture IsComplete()
  (VulkanRenderer's ResolveColorBlitBinding), which the unconditional widening aborted.
  The MUTATOR rows stay unconditional - no backend may write a frontend texture's storage
  from the apply thread, and none does.
- CopyTexImage2D / CopyTexSubImage2D sync their DESTINATION by handle instead of merely
  looking its twin up. A copy destination is not sampled by the draw program, so the
  sampler-view window never brings it across; the pre-P5e frontend walk over every slot of
  every touched unit happened to sync it as a side effect. This is the "waited texture
  ops" term of the named narrowing (P5e-5) being made real rather than assumed.
This commit is contained in:
2026-09-18 08:01:22 -04:00
parent 72e7902080
commit 859590901a
2 changed files with 36 additions and 13 deletions
+14 -6
View File
@@ -9775,14 +9775,20 @@ namespace MobileGL::MG_Backend::DirectGLES {
if (MG_Config::Transport != MG_Config::TransportMode::Monolith) {
const MG_Pipe::MGPipeHandle dstHandle = MG_Pipe::MGPipeApplier().VerbCopyTexDst;
const auto* dstRecord = PipeTextureRecordForHandle(dstHandle);
auto* backendTextureSlot = TextureImpl::g_backendTextureObjects.FindByHandle(dstHandle);
if (backendTextureSlot == nullptr || *backendTextureSlot == nullptr || dstRecord == nullptr) {
// P5e (tx2): SYNCED, not merely looked up. The destination of a copy is not sampled
// by the draw program, so the per-draw unit list - which is the sampler-view window
// now - never brings it across; before P5e the frontend walk over every slot of every
// touched unit happened to sync it as a side effect. The named narrowing (P5e-5) says
// the union that still covers every texture includes "the waited texture ops", and
// this is one of them: it syncs its own endpoint by handle.
auto& backendTexture = TextureImpl::SyncTextureToBackendByHandle(dstHandle);
if (!backendTexture || dstRecord == nullptr) {
MGLOG_E_ONCE("CopyTexImage2D: the verb's destination texture {%u, %u} has no twin "
"or no applier record on this side",
dstHandle.Slot, dstHandle.Gen);
return;
}
dstBackendTexture = backendTextureSlot->get();
dstBackendTexture = backendTexture.get();
mgInternalFormat = static_cast<TextureInternalFormat>(dstRecord->Desc.InternalFormat);
} else
#endif
@@ -9890,14 +9896,16 @@ namespace MobileGL::MG_Backend::DirectGLES {
// client's unit binding slot or the client allocator (T2/T4).
if (MG_Config::Transport != MG_Config::TransportMode::Monolith) {
const MG_Pipe::MGPipeHandle dstHandle = MG_Pipe::MGPipeApplier().VerbCopyTexDst;
auto* backendTextureSlot = TextureImpl::g_backendTextureObjects.FindByHandle(dstHandle);
if (backendTextureSlot == nullptr || *backendTextureSlot == nullptr) {
// P5e (tx2): synced by handle - see CopyTexImage2D's note. A copy destination is not
// in the sampler-view window, so nothing else on this path would build its twin.
auto& backendTexture = TextureImpl::SyncTextureToBackendByHandle(dstHandle);
if (!backendTexture) {
MGLOG_E_ONCE("CopyTexSubImage2D: the verb's destination texture {%u, %u} has no "
"twin on this side",
dstHandle.Slot, dstHandle.Gen);
return;
}
dstBackendTexture = backendTextureSlot->get();
dstBackendTexture = backendTexture.get();
} else
#endif
{
@@ -65,6 +65,21 @@ namespace MobileGL {
surface);
std::abort();
}
// P5e (tx2), ruling 12's shape (ID-90): THE NEW READ-ONLY ROWS ARE KEYED ON THE
// BACKEND KIND, and that is not a softening - it is the same line §6 draws for
// every other P5e guard. MAGMA KEEPS THE LOCKSTEP: a DirectVulkan server's
// records are all barriered and its blit path still asks a frontend texture
// whether it IsComplete() (VulkanRenderer's ResolveColorBlitBinding), which is a
// P7 debt and legal under P5C's rules for a client parked in its own wait. What
// P5e retires is ESPRYT's per-draw shape reads, so the new rows abort on a
// DirectGLES server and are silent on a DirectVulkan one. The MUTATOR rows above
// stay unconditional: no backend may write a frontend texture's storage from the
// apply thread, and none does.
void RefuseLegacyTextureShapeReadFromApplyThread(const char* surface) {
if (MG_Config::ActiveBackendType != BackendType::DirectGLES) return;
RefuseLegacyTextureArmFromApplyThread(surface);
}
#endif
// Overlapping OR abutting ([lo, hi) intervals meeting edge-to-edge) in
@@ -113,7 +128,7 @@ namespace MobileGL {
SizeT MipmapStorage::GetLevelCount() const {
#if MOBILEGL_BUILD_DISAGGREGATED
RefuseLegacyTextureArmFromApplyThread("GetMipmapLevelCount");
RefuseLegacyTextureShapeReadFromApplyThread("GetMipmapLevelCount");
#endif
return m_data.size();
}
@@ -190,7 +205,7 @@ namespace MobileGL {
GLenum MipmapStorage::GetCompressedFormat(Uint level) const {
#if MOBILEGL_BUILD_DISAGGREGATED
RefuseLegacyTextureArmFromApplyThread("GetCompressedFormat");
RefuseLegacyTextureShapeReadFromApplyThread("GetCompressedFormat");
#endif
if (level >= m_compressedFormats.size()) return GL_NONE;
return m_compressedFormats[level];
@@ -198,7 +213,7 @@ namespace MobileGL {
SizeT MipmapStorage::GetCompressedByteSize(Uint level) const {
#if MOBILEGL_BUILD_DISAGGREGATED
RefuseLegacyTextureArmFromApplyThread("GetCompressedByteSize");
RefuseLegacyTextureShapeReadFromApplyThread("GetCompressedByteSize");
#endif
if (level >= m_compressedData.size()) return 0;
return m_compressedData[level].size();
@@ -206,7 +221,7 @@ namespace MobileGL {
const void* MipmapStorage::MapCompressedData(Uint level) const {
#if MOBILEGL_BUILD_DISAGGREGATED
RefuseLegacyTextureArmFromApplyThread("MapCompressedMipmapData");
RefuseLegacyTextureShapeReadFromApplyThread("MapCompressedMipmapData");
#endif
if (level >= m_compressedData.size()) return nullptr;
return m_compressedData[level].data();
@@ -219,7 +234,7 @@ namespace MobileGL {
GLenum MipmapStorage::GetRequestedCompressedFormat(Uint level) const {
#if MOBILEGL_BUILD_DISAGGREGATED
RefuseLegacyTextureArmFromApplyThread("GetRequestedCompressedFormat");
RefuseLegacyTextureShapeReadFromApplyThread("GetRequestedCompressedFormat");
#endif
if (level >= m_requestedCompressedFormats.size()) return GL_NONE;
return m_requestedCompressedFormats[level];
@@ -270,7 +285,7 @@ namespace MobileGL {
IntVec3 MipmapStorage::GetTexelSize(Uint level) const {
#if MOBILEGL_BUILD_DISAGGREGATED
RefuseLegacyTextureArmFromApplyThread("GetMipmapTexelSize");
RefuseLegacyTextureShapeReadFromApplyThread("GetMipmapTexelSize");
#endif
auto& targetTexelSizes = m_texelSizes;
if (level >= targetTexelSizes.size()) return {0, 0, 0};
@@ -279,7 +294,7 @@ namespace MobileGL {
SizeT MipmapStorage::GetByteSize(Uint level) const {
#if MOBILEGL_BUILD_DISAGGREGATED
RefuseLegacyTextureArmFromApplyThread("GetMipmapByteSize");
RefuseLegacyTextureShapeReadFromApplyThread("GetMipmapByteSize");
#endif
if (level >= m_data.size()) return 0;
return m_data[level].size();