mirror of
https://github.com/MobileGL-Dev/MobileGL
synced 2026-11-07 01:53:24 +09:00
[MG_IntegrationTest] (P7): ObjectSubsystemControl reads the D3 refusal sentence and the four F1Wire decline cases are Magma-only
This commit is contained in:
@@ -3935,9 +3935,15 @@ if (MOBILEGL_BUILD_DISAGGREGATED AND MOBILEGL_PIPE_VERIFY)
|
||||
# this build with MOBILEGL_PIPE_VERIFY=0 in the same environment, the same four assertions
|
||||
# (GL error 0x0502 where the decline promises none, or none where it promises one; the
|
||||
# overlap copy writes the level the decline leaves alone) - because Espryt does not decline
|
||||
# those shapes at all. No DirectGLES split lane had ever run them; that is a finding about the
|
||||
# DirectGLES whole-binary set on split, not about verify, and it is recorded in
|
||||
# notes/p7/verify-split.md rather than hidden in a green. The DirectVulkan half keeps them.
|
||||
# those shapes at all. That is a finding about the DirectGLES whole-binary set on split, not
|
||||
# about verify, and it is recorded in notes/p7/verify-split.md rather than hidden in a green.
|
||||
# This lane was NOT the only DirectGLES split lane to reach them: the primary integration job
|
||||
# runs every integration-gpu entry under MOBILEGL_TRANSPORT=inproc, and there the same four
|
||||
# were red in CI (the depth-stencil and overlap pair from run 35781886930, the two blits from
|
||||
# 35793903365). Each case now skips by backend name at its top (F1WireScenario.cpp,
|
||||
# "Magma wire-arm decline"), in every DirectGLES lane; this subtraction is therefore
|
||||
# redundant, and kept so the verify-split registration and its counts do not move. The
|
||||
# DirectVulkan half keeps them.
|
||||
set(MGL_ITEST_VERIFY_SPLIT_GLES_MAGMA_DECLINE_FILTER
|
||||
"F1WireScenario.GenerateMipmapDepthStencilDeclinesAndKeepsTheSession:F1WireScenario.MultisampleBlitOntoAMultisampleDestinationDeclinesAndKeepsTheSession:F1WireScenario.MultisampleBlitFromASingleSampleSourceDeclinesAndKeepsTheSession:F1WireScenario.CopyImageInPlaceOverlapDeclinesAndLeavesTheLevelAlone")
|
||||
|
||||
|
||||
@@ -633,8 +633,20 @@ TEST_F(F1WireScenario, GenerateMipmapDepthWithoutNativeBlitPixels) {
|
||||
// so it fires on every driver.
|
||||
// Red once (executed, reverted): restore the MagmaWireFatal and this case dies with
|
||||
// Fatal{UnmigratedVerb, "Magma:depth-stencil-mipmap@P7"}.
|
||||
//
|
||||
// MAGMA ONLY, like the three other `*Declines*` cases below. Rule I and §3.2 are MG_Backend/
|
||||
// DirectVulkan's wire-arm contract, but the DirectGLES whole-binary entry discovers this case
|
||||
// too and reaches its body whenever the process resolves a split transport - the primary
|
||||
// integration lane runs every integration-gpu entry under MOBILEGL_TRANSPORT=inproc. Espryt
|
||||
// answers this shape with GL_INVALID_OPERATION and leaves level zero and the session intact
|
||||
// (measured, DirectGLES x inproc on lavapipe): a different answer, not a broken decline.
|
||||
TEST_F(F1WireScenario, GenerateMipmapDepthStencilDeclinesAndKeepsTheSession) {
|
||||
if (!Ready()) return;
|
||||
if (Gl().BackendName() != "DirectVulkan")
|
||||
GTEST_SKIP() << "Magma wire-arm decline (CONTRACT-P7 3.2, depth-stencil-mipmap): Espryt ("
|
||||
<< Gl().BackendName()
|
||||
<< ") answers a depth/stencil glGenerateMipmap with GL_INVALID_OPERATION and "
|
||||
"leaves level 0 intact, which is not the no-error decline this case pins";
|
||||
GLuint depthStencil = 0;
|
||||
glGenTextures(1, &depthStencil);
|
||||
glBindTexture(GL_TEXTURE_2D, depthStencil);
|
||||
@@ -1767,6 +1779,15 @@ TEST_F(F1WireScenario, MultisampleColorBlitScalesAndFlipsThroughTheResolveScratc
|
||||
// case dies as Fatal{UnmigratedVerb, "Magma:multisample-blit-shape@P7"}.
|
||||
TEST_F(F1WireScenario, MultisampleBlitOntoAMultisampleDestinationDeclinesAndKeepsTheSession) {
|
||||
if (!Ready()) return;
|
||||
// Magma only (see GenerateMipmapDepthStencilDeclinesAndKeepsTheSession). Espryt on the split
|
||||
// arm leaves the destination untouched but no GL_INVALID_OPERATION reaches the client, which
|
||||
// GL 4.6 core 18.3.1 asks for here - an Espryt debt of the same class as
|
||||
// DepthStencilReadbackMatrixScenario's scaled-resolve gate, not this decline's to pin.
|
||||
if (Gl().BackendName() != "DirectVulkan")
|
||||
GTEST_SKIP() << "Magma wire-arm decline (CONTRACT-P7 3.2, multisample-blit-shape): Espryt ("
|
||||
<< Gl().BackendName()
|
||||
<< ") writes nothing for a scaled blit onto a multisample destination but "
|
||||
"records no GL_INVALID_OPERATION - a P3b/P4b debt, not this decline";
|
||||
GLuint msFbo = 0, msRenderbuffer = 0;
|
||||
glGenFramebuffers(1, &msFbo);
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, msFbo);
|
||||
@@ -1854,6 +1875,15 @@ TEST_F(F1WireScenario, MultisampleBlitOntoAMultisampleDestinationDeclinesAndKeep
|
||||
// case dies as Fatal{UnmigratedVerb, "Magma:multisample-blit-aspect@P7"}.
|
||||
TEST_F(F1WireScenario, MultisampleBlitFromASingleSampleSourceDeclinesAndKeepsTheSession) {
|
||||
if (!Ready()) return;
|
||||
// Magma only (see GenerateMipmapDepthStencilDeclinesAndKeepsTheSession). The decline itself
|
||||
// is a recorded Magma debt (CONTRACT-P7 §12: a same-rectangle 1 -> N blit is legal sample
|
||||
// replication in GL 4.6 core 18.3.1); Espryt on the split arm neither replicates nor
|
||||
// records an error, which is neither answer this case can pin.
|
||||
if (Gl().BackendName() != "DirectVulkan")
|
||||
GTEST_SKIP() << "Magma wire-arm decline (CONTRACT-P7 3.2, multisample-blit-aspect): Espryt ("
|
||||
<< Gl().BackendName()
|
||||
<< ") writes nothing for a single-sample to multisample blit and records no "
|
||||
"GL error - a P3b/P4b debt, not this decline";
|
||||
GLuint msFbo = 0, msRenderbuffer = 0;
|
||||
glGenFramebuffers(1, &msFbo);
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, msFbo);
|
||||
@@ -2047,6 +2077,12 @@ TEST_F(F1WireScenario, CopyImageInPlaceAcrossLevelsAndLayers) {
|
||||
// no GL error, and the level still holds exactly what it held.
|
||||
TEST_F(F1WireScenario, CopyImageInPlaceOverlapDeclinesAndLeavesTheLevelAlone) {
|
||||
if (!Ready()) return;
|
||||
// Magma only (see GenerateMipmapDepthStencilDeclinesAndKeepsTheSession). GL leaves this copy
|
||||
// undefined and Espryt performs it; only the Magma wire arm promises to record nothing.
|
||||
if (Gl().BackendName() != "DirectVulkan")
|
||||
GTEST_SKIP() << "Magma wire-arm decline (CONTRACT-P7 3.2, copy-image-in-place): GL 4.6 core "
|
||||
"18.3.2 leaves an overlapping same-subresource copy undefined and Espryt ("
|
||||
<< Gl().BackendName() << ") performs it";
|
||||
const GLuint array = MakeInPlaceCopyTexture();
|
||||
ASSERT_EQ(FirstGLError(), GLenum(GL_NO_ERROR)) << "F1.CopyInPlaceOverlap.setup";
|
||||
// (0,0)-(4,4) onto (2,2): four of the sixteen texels are in both rectangles.
|
||||
|
||||
@@ -188,11 +188,21 @@ void main() { oColor = texture(uTex, vUv); }
|
||||
// ORDER - see the direction check below.
|
||||
//
|
||||
// Espryt's text is one MGLOG_E from the helper the three dependent families share
|
||||
// (Managers.cpp, PipeSubsystemDependencyMissing): "MGPipe: <A> (bit N) is set but <B>
|
||||
// (bit M) is clear; <why> - REFUSING the dependent bit and running the legacy arm. Set
|
||||
// both bits, or clear both". Three spellings are accepted per bit - the constant's name,
|
||||
// "(bit N)", and the hexadecimal mask - so the assertion pins the DECISION and the
|
||||
// DIRECTION, and not the family-specific prose in <why>.
|
||||
// (Managers.cpp, PipeSubsystemDependencyMissing). Since P3b/P4b wave 2-D package D3
|
||||
// (1c36987e, ID-P7-28) that helper reads the family's row from MG_Pipe/SubsystemDeps.def
|
||||
// instead of taking one hand-written dependency bit, and it computes the missing bits
|
||||
// rather than naming them: "MGPipe: <A> (bit N) is set but MOBILEGL_PIPE_PUSH=0x<mask>
|
||||
// does not carry every bit MG_Pipe/SubsystemDeps.def says it requires (requires 0x<R>,
|
||||
// missing 0x<M>): <why> - REFUSING the dependent bit and running the legacy arm. Set every
|
||||
// bit of the row, or clear the family's own". The SET bit is accepted in three spellings -
|
||||
// the constant's name, "(bit N)", and the hexadecimal mask - and the NEEDED bit is read
|
||||
// from the one place the helper names it, the "missing 0x<M>)" field, so the assertion
|
||||
// pins the DECISION and the DIRECTION, and not the family-specific prose in <why>.
|
||||
//
|
||||
// THE OLD SENTENCE ("<A> (bit N) is set but <B> (bit M) is clear; ...") IS GONE FROM THE
|
||||
// TREE, and this matcher used to require its " is clear": after D3 both refusal lanes went
|
||||
// red with the refusal sitting on the ERROR line the lane was reading (CI run
|
||||
// 35792628470 onward). Nothing about the decision changed - only the wording did.
|
||||
//
|
||||
// THE DIRECTION IS THE HALF THIS FILE USED TO BE MISSING (review F-v2-m1). The first form
|
||||
// of the matcher asked "does the line name bit A?" AND "does the line name bit B?", which
|
||||
@@ -207,12 +217,18 @@ void main() { oColor = texture(uTex, vUv); }
|
||||
// away at all times.
|
||||
//
|
||||
// What makes the direction readable is the sentence's own shape: the SET bit is named
|
||||
// before " is set but " and the NEEDED bit between that and " is clear". So the check is
|
||||
// four offsets in strictly increasing order, and it is the sentence Espryt emits rather
|
||||
// than a re-statement of it.
|
||||
// before " is set but " and the NEEDED bit in the "missing 0x<M>)" field after it. So the
|
||||
// check is three offsets in strictly increasing order, and it is the sentence Espryt
|
||||
// emits rather than a re-statement of it. The field is matched WITH its closing
|
||||
// parenthesis, so "missing 0x400)" cannot be satisfied by "missing 0x4000)" (bit 14), and
|
||||
// the mirror sentence cannot satisfy it either: there the SET-bit spellings first occur
|
||||
// inside that field, after " is set but ".
|
||||
constexpr const char* kSaysItRefused = "REFUSING the dependent bit and running the legacy arm";
|
||||
constexpr const char* kSaysWhichIsSet = " is set but ";
|
||||
constexpr const char* kSaysWhichIsClear = " is clear";
|
||||
|
||||
std::string SaysWhichIsMissing(const std::string& neededMaskHex) {
|
||||
return "missing " + neededMaskHex + ")";
|
||||
}
|
||||
|
||||
// The earliest offset at which any accepted spelling of one bit appears, or npos. The
|
||||
// EARLIEST rather than any: a spelling that also occurs later in <why> (Espryt's
|
||||
@@ -234,7 +250,8 @@ void main() { oColor = texture(uTex, vUv); }
|
||||
// what it found: a reader of a green refusal lane must be able to see the sentence.
|
||||
std::string FindTheRefusalLine(const std::string& log,
|
||||
const std::vector<std::string>& bitThatWasSet,
|
||||
const std::vector<std::string>& bitThatWasNeeded) {
|
||||
const std::string& neededMaskHex) {
|
||||
const std::string missingField = SaysWhichIsMissing(neededMaskHex);
|
||||
std::size_t pos = 0;
|
||||
while (pos <= log.size()) {
|
||||
const std::size_t newline = log.find('\n', pos);
|
||||
@@ -244,18 +261,15 @@ void main() { oColor = texture(uTex, vUv); }
|
||||
const std::size_t refusedAt = line.find(kSaysItRefused);
|
||||
const std::size_t setAt = EarliestSpellingOffset(line, bitThatWasSet);
|
||||
const std::size_t setClauseAt = line.find(kSaysWhichIsSet);
|
||||
const std::size_t neededAt = EarliestSpellingOffset(line, bitThatWasNeeded);
|
||||
const std::size_t clearClauseAt = line.find(kSaysWhichIsClear);
|
||||
const std::size_t missingAt = line.find(missingField);
|
||||
const bool everyPartIsThere =
|
||||
refusedAt != std::string::npos && setAt != std::string::npos &&
|
||||
setClauseAt != std::string::npos && neededAt != std::string::npos &&
|
||||
clearClauseAt != std::string::npos;
|
||||
// "<set bit> ... is set but ... <needed bit> ... is clear", strictly in that
|
||||
// order. Swapping the caller's two arguments breaks the chain, which is the
|
||||
// whole of F-v2-m1.
|
||||
setClauseAt != std::string::npos && missingAt != std::string::npos;
|
||||
// "<set bit> ... is set but ... missing <needed bit>)", strictly in that order.
|
||||
// Swapping the caller's two arguments breaks the chain, which is the whole of
|
||||
// F-v2-m1.
|
||||
const bool inTheRightDirection =
|
||||
everyPartIsThere && setAt < setClauseAt && setClauseAt < neededAt &&
|
||||
neededAt < clearClauseAt;
|
||||
everyPartIsThere && setAt < setClauseAt && setClauseAt < missingAt;
|
||||
if (atErrorSeverity && inTheRightDirection) {
|
||||
return line;
|
||||
}
|
||||
@@ -266,14 +280,18 @@ void main() { oColor = texture(uTex, vUv); }
|
||||
}
|
||||
|
||||
// The three accepted spellings of each of the two P4a bits this file's two refusal lanes
|
||||
// are about. MGPipe.h: bit 10 = kMGPipeSubsystemTextureResources = 0x400,
|
||||
// bit 11 = kMGPipeSubsystemSamplers = 0x800.
|
||||
// are about, when the bit is the one that was SET; the mask alone when it is the one that
|
||||
// was NEEDED (the helper prints `required & ~mask` with %llx). MGPipe.h: bit 10 =
|
||||
// kMGPipeSubsystemTextureResources = 0x400, bit 11 = kMGPipeSubsystemSamplers = 0x800.
|
||||
constexpr const char* kSamplerBitMask = "0x800";
|
||||
constexpr const char* kTextureResourceBitMask = "0x400";
|
||||
|
||||
std::vector<std::string> SamplerBitSpellings() {
|
||||
return {"kMGPipeSubsystemSamplers", "(bit 11)", "0x800"};
|
||||
return {"kMGPipeSubsystemSamplers", "(bit 11)", kSamplerBitMask};
|
||||
}
|
||||
|
||||
std::vector<std::string> TextureResourceBitSpellings() {
|
||||
return {"kMGPipeSubsystemTextureResources", "(bit 10)", "0x400"};
|
||||
return {"kMGPipeSubsystemTextureResources", "(bit 10)", kTextureResourceBitMask};
|
||||
}
|
||||
|
||||
class ObjectSubsystemControlScenario : public ScenarioTest {
|
||||
@@ -626,15 +644,16 @@ void main() { oColor = texture(uTex, vUv); }
|
||||
// FindTheRefusalLine: a substring search over the whole file cannot go red for the
|
||||
// reason this case claims (F-M6).
|
||||
const std::string refusal =
|
||||
FindTheRefusalLine(log, SamplerBitSpellings(), TextureResourceBitSpellings());
|
||||
FindTheRefusalLine(log, SamplerBitSpellings(), kTextureResourceBitMask);
|
||||
EXPECT_FALSE(refusal.empty())
|
||||
<< "MOBILEGL_PIPE_PUSH=0x9ff sets the sampler subsystem (bit 11) without the texture "
|
||||
"resource subsystem (bit 10) it depends on, and no single ERROR line of the "
|
||||
"library's log both says it REFUSED and names the two bits. D-K2 requires ONE "
|
||||
"MGLOG_E naming both and a fall back to the legacy sampler arm; a mask that is "
|
||||
"silently half-honoured is the failure this case exists to catch, and it is "
|
||||
"invisible in the pixels by construction. Accepted spellings per bit are the "
|
||||
"constant's name, '(bit 11)' / '(bit 10)', and '0x800' / '0x400'. The log was "
|
||||
"invisible in the pixels by construction. The set bit may be spelled by the "
|
||||
"constant's name, '(bit 11)' or '0x800', before ' is set but '; the needed bit "
|
||||
"must be the helper's 'missing 0x400)' field after it. The log was "
|
||||
<< log.size() << " bytes and is at " << PipeStatsWindow::LibraryLogPath() << ".";
|
||||
if (!refusal.empty()) {
|
||||
// Printed on the pass as well: a reader of a green refusal lane must be able to
|
||||
@@ -742,15 +761,17 @@ void main() { oColor = texture(uTex, vUv); }
|
||||
// The same line shape as the 0x9ff arm, with the two bits' roles swapped: the bit that
|
||||
// was SET is the texture-resource one and the bit it NEEDED is the sampler one. The
|
||||
// swap is now a REAL difference between the two cases: FindTheRefusalLine requires the
|
||||
// set bit to be named before " is set but " and the needed bit after it (F-v2-m1), so
|
||||
// this call and the 0x9ff one above accept disjoint sentences. Espryt's is
|
||||
// "kMGPipeSubsystemTextureResources (bit 10) is set but kMGPipeSubsystemSamplers
|
||||
// (bit 11) is clear; MGPTextureParams::BuiltinSampler is a SamplerCso handle, only
|
||||
// bit 11 mints sampler CSOs, and the applier's verdict for a null one is
|
||||
// Fatal{ProtocolCorruption} - REFUSING the dependent bit and running the legacy arm.
|
||||
// Set both bits, or clear both" (Managers.cpp, ResolveTextureResourceSubsystemArm).
|
||||
// set bit to be named before " is set but " and the needed bit in the "missing 0x<M>)"
|
||||
// field after it (F-v2-m1), so this call and the 0x9ff one above accept disjoint
|
||||
// sentences. Espryt's is "kMGPipeSubsystemTextureResources (bit 10) is set but
|
||||
// MOBILEGL_PIPE_PUSH=0x5ff does not carry every bit MG_Pipe/SubsystemDeps.def says it
|
||||
// requires (requires 0x880, missing 0x800): <the row's why> - REFUSING the dependent
|
||||
// bit and running the legacy arm. Set every bit of the row, or clear the family's own"
|
||||
// (Managers.cpp, PipeSubsystemDependencyMissing via
|
||||
// ResolveTextureResourceSubsystemArm). The row requires bit 7 as well; 0x5ff carries
|
||||
// it, so the missing field is bit 11 alone.
|
||||
const std::string refusal =
|
||||
FindTheRefusalLine(log, TextureResourceBitSpellings(), SamplerBitSpellings());
|
||||
FindTheRefusalLine(log, TextureResourceBitSpellings(), kSamplerBitMask);
|
||||
EXPECT_FALSE(refusal.empty())
|
||||
<< "MOBILEGL_PIPE_PUSH=0x5ff sets the texture resource subsystem (bit 10) without "
|
||||
"the sampler subsystem (bit 11) that MGPTextureParams::BuiltinSampler depends on, "
|
||||
@@ -758,8 +779,9 @@ void main() { oColor = texture(uTex, vUv); }
|
||||
"two bits. Only bit 11 mints sampler CSOs, so every set_texture_params emitted "
|
||||
"under this mask would carry a null BuiltinSampler and the applier's Fatal is the "
|
||||
"next thing that happens - which is why this pair is a refusal at bring-up and "
|
||||
"not the harmless mirror of the 0x9ff one. Accepted spellings per bit are the "
|
||||
"constant's name, '(bit 10)' / '(bit 11)', and '0x400' / '0x800'. The log was "
|
||||
"not the harmless mirror of the 0x9ff one. The set bit may be spelled by the "
|
||||
"constant's name, '(bit 10)' or '0x400', before ' is set but '; the needed bit "
|
||||
"must be the helper's 'missing 0x800)' field after it. The log was "
|
||||
<< log.size() << " bytes and is at " << PipeStatsWindow::LibraryLogPath() << ".";
|
||||
if (!refusal.empty()) {
|
||||
std::cout << "[ ObjectSubsystemControl ] refusal line: " << refusal << std::endl;
|
||||
|
||||
Reference in New Issue
Block a user