mirror of
https://github.com/MobileGL-Dev/MobileGL
synced 2026-09-09 12:48:32 +09:00
[Fix] (Espryt): re-sync a framebuffer's cross-object masks after an attachment's storage is redefined in place - a mutable texture regenerated on its driver id or a renderbuffer re-storaged on its moved neither the FBO memo's frontend versions nor the backend-id generation, so the pre-handle arm kept the widening masks of the storage the object was attached with; the renderbuffer half of the F-3 scenario asserts on the handle arm only, where the resource record closes D-D2 (found by the F-3 scenarios on the 0x1ff, 0 and pull lanes)
This commit is contained in:
@@ -6667,6 +6667,19 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
|||||||
if (needsRegeneration) {
|
if (needsRegeneration) {
|
||||||
MGLOG_D("Texture state changed significantly or not initialized, regenerating texture with ID: %u",
|
MGLOG_D("Texture state changed significantly or not initialized, regenerating texture with ID: %u",
|
||||||
m_backendTextureId);
|
m_backendTextureId);
|
||||||
|
// A REDEFINITION IN PLACE TAKES THE SAME GENERATION A RE-MINT TAKES (P4a fable
|
||||||
|
// seam F-3, the pre-handle half). Mutable driver storage is redefined on the
|
||||||
|
// SAME id below, so unlike RecreateBackendTexture nothing moves the FBO twins'
|
||||||
|
// memo: the frontend framebuffer versions do not see a texture's respecify
|
||||||
|
// and the id did not change. An attached texture whose format moved to one
|
||||||
|
// with the same carrier (GL_SRGB8 -> GL_SRGB8_ALPHA8 on a driver that widens
|
||||||
|
// the first) therefore kept the framebuffer's alpha-widening mask, and every
|
||||||
|
// draw into it stayed masked. The first definition is not a redefinition and
|
||||||
|
// bumps nothing; a redefinition that went through RecreateBackendTexture above
|
||||||
|
// has already bumped.
|
||||||
|
if (m_isInitialized && !m_backendStorageImmutable) {
|
||||||
|
++FramebufferImpl::g_attachmentBackendIdGeneration;
|
||||||
|
}
|
||||||
|
|
||||||
// Regenerate all mipmap levels
|
// Regenerate all mipmap levels
|
||||||
GLenum glInternalFormat, glType, glFormat;
|
GLenum glInternalFormat, glType, glFormat;
|
||||||
@@ -12726,6 +12739,17 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
// A RE-STORAGE IS A REDEFINITION ON THE SAME DRIVER ID (P4a fable seam F-3, the
|
||||||
|
// pre-handle half): glRenderbufferStorage below re-allocates behind the name the
|
||||||
|
// framebuffer twins already attached, the frontend's renderbuffer setters bump no
|
||||||
|
// version and no framebuffer version sees them, so without this the FBO memo kept
|
||||||
|
// the widening masks of the storage the renderbuffer was attached with. Same
|
||||||
|
// generation a texture re-mint takes, for the same reason; the first allocation is
|
||||||
|
// not a redefinition.
|
||||||
|
if (m_isInitialized) {
|
||||||
|
++FramebufferImpl::g_attachmentBackendIdGeneration;
|
||||||
|
}
|
||||||
|
|
||||||
Bind();
|
Bind();
|
||||||
|
|
||||||
// Allocate storage
|
// Allocate storage
|
||||||
|
|||||||
@@ -1978,6 +1978,14 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
|||||||
// the driver FBO would keep the deleted texture name attached forever. The
|
// the driver FBO would keep the deleted texture name attached forever. The
|
||||||
// SyncCurrentFBO gate compares this generation (below) to re-enter the sync,
|
// SyncCurrentFBO gate compares this generation (below) to re-enter the sync,
|
||||||
// and each twin re-arms its per-attachment memo on a mismatch (SyncToBackend).
|
// and each twin re-arms its per-attachment memo on a mismatch (SyncToBackend).
|
||||||
|
//
|
||||||
|
// AND WHENEVER AN ATTACHABLE OBJECT'S DRIVER STORAGE IS REDEFINED IN PLACE (P4a fable
|
||||||
|
// seam F-3): a mutable texture regenerated on the same id, a renderbuffer re-storaged
|
||||||
|
// on the same id. The id did not move, but the four cross-object masks SyncToBackend
|
||||||
|
// computes from the attachment's format did, and nothing else the FBO memo reads sees
|
||||||
|
// a respecify of an attached object. So "did I change something under an attachment
|
||||||
|
// point that no frontend version can tell the framebuffer about" is what this counts,
|
||||||
|
// and the re-mint is one case of it.
|
||||||
extern Uint64 g_attachmentBackendIdGeneration;
|
extern Uint64 g_attachmentBackendIdGeneration;
|
||||||
// What g_attachmentBackendIdGeneration was when SyncCurrentFBO last stamped each
|
// What g_attachmentBackendIdGeneration was when SyncCurrentFBO last stamped each
|
||||||
// target; part of the synced tuple above.
|
// target; part of the synced tuple above.
|
||||||
|
|||||||
@@ -40,6 +40,13 @@ namespace MGITest {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool PeekEsprytFramebufferHandleArmIsLive(bool* outLive) {
|
||||||
|
if (outLive == nullptr) return false;
|
||||||
|
if (!EsprytIsRunning()) return false;
|
||||||
|
*outLive = MGB::FramebufferSubsystemEnabled();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
bool PeekPipeShaderImageWindow(PipeShaderImageWindowPeek* out) {
|
bool PeekPipeShaderImageWindow(PipeShaderImageWindowPeek* out) {
|
||||||
if (out == nullptr) return false;
|
if (out == nullptr) return false;
|
||||||
const MGP::MGPipeApplierState& applier = MGP::MGPipeApplier();
|
const MGP::MGPipeApplierState& applier = MGP::MGPipeApplier();
|
||||||
@@ -92,6 +99,7 @@ namespace MGITest {
|
|||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
bool PeekEsprytSamplerHandleArmIsLive(bool*) { return false; }
|
bool PeekEsprytSamplerHandleArmIsLive(bool*) { return false; }
|
||||||
|
bool PeekEsprytFramebufferHandleArmIsLive(bool*) { return false; }
|
||||||
bool PeekPipeShaderImageWindow(PipeShaderImageWindowPeek*) { return false; }
|
bool PeekPipeShaderImageWindow(PipeShaderImageWindowPeek*) { return false; }
|
||||||
bool PeekEsprytUnitSampler(unsigned, unsigned, EsprytUnitSamplerPeek*) { return false; }
|
bool PeekEsprytUnitSampler(unsigned, unsigned, EsprytUnitSamplerPeek*) { return false; }
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -36,6 +36,12 @@ namespace MGITest {
|
|||||||
// other. Written only on true.
|
// other. Written only on true.
|
||||||
bool PeekEsprytSamplerHandleArmIsLive(bool* outLive);
|
bool PeekEsprytSamplerHandleArmIsLive(bool* outLive);
|
||||||
|
|
||||||
|
// The same question for the FRAMEBUFFER family (bit 9): true when Espryt consumes
|
||||||
|
// set_framebuffer_state in this process. The renderbuffer half of the F-3 case asserts only
|
||||||
|
// there - on the pre-handle arm a renderbuffer re-storaged while attached moves nothing the
|
||||||
|
// FBO memo reads (D-D2's documented hole, pre-P4a code), and the record is what closes it.
|
||||||
|
bool PeekEsprytFramebufferHandleArmIsLive(bool* outLive);
|
||||||
|
|
||||||
// ---- the applier's shader-image window, as last received ------------------------------
|
// ---- the applier's shader-image window, as last received ------------------------------
|
||||||
//
|
//
|
||||||
// MGPipeApplierState::ShaderImageStart / ShaderImageCount / ShaderImagesSerial. Count is
|
// MGPipeApplierState::ShaderImageStart / ShaderImageCount / ShaderImagesSerial. Count is
|
||||||
|
|||||||
@@ -18,9 +18,18 @@
|
|||||||
//
|
//
|
||||||
// F-3 set_framebuffer_state INLINES an attachment's format (D-C1) and a storage redefinition of
|
// F-3 set_framebuffer_state INLINES an attachment's format (D-C1) and a storage redefinition of
|
||||||
// an ATTACHED texture or renderbuffer moved nothing bit 11 read: Espryt's handle arm then
|
// an ATTACHED texture or renderbuffer moved nothing bit 11 read: Espryt's handle arm then
|
||||||
// answered its alpha-widening / snorm-clamp / integer masks from the stale copy while the
|
// answered its alpha-widening / snorm-clamp / integer masks from the stale copy. AND THE
|
||||||
// legacy arm re-read the frontend. Three cases, both directions, texture and renderbuffer.
|
// PRE-HANDLE ARM WAS NOT FRESH EITHER, which these cases found on the 0x1ff / 0 / pull
|
||||||
// DirectGLES only: the masks are Espryt's substitution machinery.
|
// lanes: a redefinition that keeps the driver id (mutable texture storage regenerated in
|
||||||
|
// place, a renderbuffer re-storaged in place) moves neither the framebuffer's frontend
|
||||||
|
// versions nor the backend-id generation the FBO memo reads, so SyncToBackend never
|
||||||
|
// re-ran and the masks stayed on both arms. The texture half is fixed on both arms (an
|
||||||
|
// in-place regeneration now takes the same generation a re-mint takes); the renderbuffer
|
||||||
|
// half only on the handle arm, where the resource record carries the re-storage - on the
|
||||||
|
// pre-handle arm a renderbuffer's twin is only ever reached from inside the FBO walk the
|
||||||
|
// memo skips (D-D2's documented hole, pre-P4a code), so that case asserts on the handle
|
||||||
|
// arm and declines by name elsewhere. Three cases, both directions, texture and
|
||||||
|
// renderbuffer. DirectGLES only: the masks are Espryt's substitution machinery.
|
||||||
// F-1 set_sampler_views is resolved for the PROGRAM IN USE and bit 12's shutter read no program
|
// F-1 set_sampler_views is resolved for the PROGRAM IN USE and bit 12's shutter read no program
|
||||||
// input, so a glUseProgram alone never re-emitted it; E's record epoch (the two set serials)
|
// input, so a glUseProgram alone never re-emitted it; E's record epoch (the two set serials)
|
||||||
// then kept the program-independent texture sync list from ever rebuilding, and a texture
|
// then kept the program-independent texture sync list from ever rebuilding, and a texture
|
||||||
@@ -273,7 +282,9 @@ void main() { imageStore(i1, 0, imageLoad(i0, 0) + uvec4(2u, 0u, 0u, 0u)); }
|
|||||||
// alpha masked off so the stored alpha stays at the 1.0 a three-channel format implies.
|
// alpha masked off so the stored alpha stays at the 1.0 a three-channel format implies.
|
||||||
// Redefine the same attached texture as GL_SRGB8_ALPHA8 and the application owns alpha
|
// Redefine the same attached texture as GL_SRGB8_ALPHA8 and the application owns alpha
|
||||||
// again - the mask must clear. On the tree the audit read the record still said SRGB8, the
|
// again - the mask must clear. On the tree the audit read the record still said SRGB8, the
|
||||||
// handle arm kept masking, and the 0.25 this case draws never reached the storage.
|
// handle arm kept masking, and the 0.25 this case draws never reached the storage; on the
|
||||||
|
// pre-handle arm the twin regenerated the (mutable) storage on the same driver id, nothing
|
||||||
|
// the FBO memo reads moved, and the masks stayed the same way.
|
||||||
|
|
||||||
TEST_F(P4aSeamAuditScenario, ATextureRespecifiedWhileAttachedReachesTheFramebufferRecord) {
|
TEST_F(P4aSeamAuditScenario, ATextureRespecifiedWhileAttachedReachesTheFramebufferRecord) {
|
||||||
if (!Ready()) return;
|
if (!Ready()) return;
|
||||||
@@ -327,8 +338,8 @@ void main() { imageStore(i1, 0, imageLoad(i0, 0) + uvec4(2u, 0u, 0u, 0u)); }
|
|||||||
EXPECT_NEAR(pixel[1], 1.0f, 0.05f) << "the draw did not land at all";
|
EXPECT_NEAR(pixel[1], 1.0f, 0.05f) << "the draw did not land at all";
|
||||||
EXPECT_NEAR(pixel[3], 0.25f, 0.02f)
|
EXPECT_NEAR(pixel[3], 0.25f, 0.02f)
|
||||||
<< "the draw's alpha never reached a four-channel attachment: the framebuffer record "
|
<< "the draw's alpha never reached a four-channel attachment: the framebuffer record "
|
||||||
"still describes the three-channel storage the texture was attached with, so the "
|
"(handle arm) or the FBO twin's memo (pre-handle arm) still describes the "
|
||||||
"handle arm kept masking alpha off (F-3)";
|
"three-channel storage the texture was attached with, so alpha stayed masked off (F-3)";
|
||||||
|
|
||||||
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||||
glDeleteFramebuffers(1, &fbo);
|
glDeleteFramebuffers(1, &fbo);
|
||||||
@@ -386,9 +397,27 @@ void main() { imageStore(i1, 0, imageLoad(i0, 0) + uvec4(2u, 0u, 0u, 0u)); }
|
|||||||
DrawQuad();
|
DrawQuad();
|
||||||
ReadPixelFloat(kSize / 2, kSize / 2, pixel);
|
ReadPixelFloat(kSize / 2, kSize / 2, pixel);
|
||||||
EXPECT_NEAR(pixel[1], 1.0f, 0.05f) << "the draw did not land at all";
|
EXPECT_NEAR(pixel[1], 1.0f, 0.05f) << "the draw did not land at all";
|
||||||
EXPECT_NEAR(pixel[3], 0.25f, 0.02f)
|
// THE HANDLE ARM ONLY. On the pre-handle arm a renderbuffer's twin is reached only from
|
||||||
<< "the draw's alpha never reached the four-channel renderbuffer: the framebuffer "
|
// inside the FBO walk, and nothing that walk's memo reads moves on glRenderbufferStorage
|
||||||
"record still describes the storage it was attached with (F-3)";
|
// - the frontend setters bump no version (D-D2), no framebuffer version sees them, and
|
||||||
|
// the twin that would bump the backend generation is exactly what the memo skips. That
|
||||||
|
// is pre-P4a code and D-D2's documented hole; the resource record is what closes it,
|
||||||
|
// so the verdict is taken where the record is consumed and declined by name elsewhere
|
||||||
|
// (measured: alpha 1.0 on the pull build and at 0x1ff / 0, the mask of the storage the
|
||||||
|
// renderbuffer was attached with).
|
||||||
|
bool framebufferArmLive = false;
|
||||||
|
if (PeekEsprytFramebufferHandleArmIsLive(&framebufferArmLive) && framebufferArmLive) {
|
||||||
|
EXPECT_NEAR(pixel[3], 0.25f, 0.02f)
|
||||||
|
<< "the draw's alpha never reached the four-channel renderbuffer: the framebuffer "
|
||||||
|
"record still describes the storage it was attached with (F-3)";
|
||||||
|
} else {
|
||||||
|
std::cout << "[ P4aSeamAudit ] renderbuffer re-storage verdict DECLINED on the pre-handle arm "
|
||||||
|
"(D-D2's documented hole: no frontend version and no backend generation moves on a "
|
||||||
|
"renderbuffer re-storage until the FBO walk the memo skips); alpha read "
|
||||||
|
<< pixel[3] << std::endl;
|
||||||
|
RecordProperty("p4a_seam_white_box", "declined");
|
||||||
|
RecordProperty("p4a_seam_white_box_reason", "renderbuffer re-storage: pre-handle arm (D-D2)");
|
||||||
|
}
|
||||||
|
|
||||||
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||||
glDeleteFramebuffers(1, &fbo);
|
glDeleteFramebuffers(1, &fbo);
|
||||||
@@ -461,8 +490,9 @@ void main() { imageStore(i1, 0, imageLoad(i0, 0) + uvec4(2u, 0u, 0u, 0u)); }
|
|||||||
ReadPixelFloat(kSize / 2, kSize / 2, pixel);
|
ReadPixelFloat(kSize / 2, kSize / 2, pixel);
|
||||||
EXPECT_NEAR(pixel[0], 1.0f, 0.05f)
|
EXPECT_NEAR(pixel[0], 1.0f, 0.05f)
|
||||||
<< "GL_DST_ALPHA read the stored alpha of a three-channel attachment and it was not "
|
<< "GL_DST_ALPHA read the stored alpha of a three-channel attachment and it was not "
|
||||||
"1.0: the framebuffer record still describes the four-channel storage the texture "
|
"1.0: the framebuffer record (handle arm) or the FBO twin's memo (pre-handle arm) "
|
||||||
"was attached with, so the handle arm let the draw write alpha (F-3, mirror)";
|
"still describes the four-channel storage the texture was attached with, so the "
|
||||||
|
"draw was let write alpha (F-3, mirror)";
|
||||||
|
|
||||||
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||||
glDeleteFramebuffers(1, &fbo);
|
glDeleteFramebuffers(1, &fbo);
|
||||||
|
|||||||
Reference in New Issue
Block a user