mirror of
https://github.com/MobileGL-Dev/MobileGL
synced 2026-11-07 01:53:24 +09:00
[MG_IntegrationTest, ci] (P7): the verify-split arm proof is a per-entry census, not a floor
- the landing shape was a count - at least 850 armed of at least 1000 client logs - which leaves the remaining 155 unnamed: the lane could lose fifty arms, gain fifty skips, and the floor would not move - the rule is now per entry and two-sided, the one dualblock-expected-fatals.txt already rides: each client log must carry the arming line, or belong to an entry ctest reported SKIPPED, or be named in the new Harness/verify-split-unarmed-expected.txt - a gtest skip is the recognised no-verb marker and is NOT named, because the skip set moves with the driver - naming it would ratchet on lavapipe rather than on the comparator - both other directions fail: a listed entry that armed, and a listed name with no client log in this run - split_log_paths.py gains the verify-split-arming mode; the lane step gains --output-junit so a skip can be told from a pass; test_split_log_paths.py gains six controls covering the four ways the census can be wrong - measured on the host over 1064 private client logs: 909 armed, 99 unarmed and skipped, 56 unarmed and named in five classes - caps/limits/extension advertisement 22, shader and program object surfaces 16, object state written and read back or a call rejected before it becomes a verb 14, framebuffer completeness queries 2 (DirectGLES only; DirectVulkan skips both), the poison parent that only forks 2
This commit is contained in:
+21
-15
@@ -995,18 +995,26 @@ jobs:
|
||||
run: |
|
||||
ulimit -c unlimited
|
||||
sudo sysctl -w kernel.core_pattern='/tmp/core.%e.%p'
|
||||
ctest --output-on-failure -L '^integration-verify-split$' --no-tests=error -j 4
|
||||
# --output-junit, because the arm proof below has to tell a gtest SKIP from a pass: an
|
||||
# entry that skipped reached no verb honestly, and the skip set moves with the driver.
|
||||
ctest --output-on-failure -L '^integration-verify-split$' --no-tests=error -j 4 \
|
||||
--output-junit "${RUNNER_TEMP}/verify-split.xml"
|
||||
|
||||
# THE ARM PROOF, per entry. Every ambient entry has a private log path (SplitLogPaths.cmake.in),
|
||||
# so this grep - unlike integration-verify's, over a shared log - is a census:
|
||||
# THE ARM PROOF, PER ENTRY AND BY NAME. Every ambient entry has a private log path
|
||||
# (SplitLogPaths.cmake.in), so this - unlike integration-verify's grep over a shared log -
|
||||
# is a census:
|
||||
# * ConfigLoader's `MOBILEGL_TRANSPORT=inproc` line in EVERY client log: an entry that ran
|
||||
# monolith would have passed every assertion above and fails here by name;
|
||||
# * `MGPipe: verify armed` in both VerifySplitArming. logs, and in at least 850 per-entry
|
||||
# client logs. The line is latched at a process's first fill, so the processes that never
|
||||
# reach a verb (limits queries, CapsMirror answers, the poison parent) honestly lack it -
|
||||
# 909 of 1064 carried it on the landing run - and a count that falls through the floor
|
||||
# means the comparator stopped arming somewhere after bring-up.
|
||||
- name: The verify-split entries ran inproc and armed the comparator
|
||||
# * `MGPipe: verify armed` in both VerifySplitArming. logs;
|
||||
# * and then the census proper. This used to be a FLOOR - "at least 850 armed of at least
|
||||
# 1000 logs" - which leaves the remaining ~155 unnamed, so the lane could lose fifty
|
||||
# arms, gain fifty skips, and the floor would not move. It is now the two-sided rule
|
||||
# dualblock-expected-fatals.txt already rides: each client log must ARM, or belong to an
|
||||
# entry ctest reported SKIPPED, or be NAMED in Harness/verify-split-unarmed-expected.txt
|
||||
# (56 names in five classes, each class saying why it reaches no verb). A listed entry
|
||||
# that arms, and a listed name with no log at all, both fail - an exception set that
|
||||
# keeps rows nothing needs is how a proof rots green.
|
||||
- name: The verify-split entries ran inproc, and every one armed, skipped or is named
|
||||
working-directory: build-verify-split/MobileGL/MG_IntegrationTest
|
||||
run: |
|
||||
shopt -s nullglob
|
||||
@@ -1027,12 +1035,10 @@ jobs:
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
armed=$(grep -l 'MGPipe: verify armed' "${logs[@]}" | wc -l)
|
||||
if [ "${armed}" -lt 850 ]; then
|
||||
echo "::error::only ${armed} of ${#logs[@]} verify-split entries armed the comparator (floor 850, 909 at landing)"
|
||||
exit 1
|
||||
fi
|
||||
echo "${#logs[@]} entries resolved inproc; ${armed} of them armed the comparator"
|
||||
python3 "${GITHUB_WORKSPACE}/MobileGL/MG_IntegrationTest/Harness/split_log_paths.py" \
|
||||
verify-split-arming verify-split-logs "${RUNNER_TEMP}/verify-split.xml" \
|
||||
"${GITHUB_WORKSPACE}/MobileGL/MG_IntegrationTest/Harness/verify-split-unarmed-expected.txt"
|
||||
|
||||
|
||||
# NEGATIVE CONTROL A ON THE SPLIT ARM - integration-verify's step A, same knob, same shape
|
||||
# (this step passes when ctest FAILS), aimed at the ambient VerifySplit. entries of BOTH
|
||||
|
||||
@@ -202,6 +202,105 @@ def require_green(document, junit_path, required):
|
||||
print(f"SplitLogPaths require-green: {len(by_name)} PASS, 0 skip, 0 failed, complete discovery")
|
||||
|
||||
|
||||
VERIFY_ARMED_LINE = "MGPipe: verify armed"
|
||||
|
||||
|
||||
def verify_split_arming(logs_dir, junit_path, expected_path):
|
||||
"""THE VERIFY-SPLIT LANE'S ARM PROOF, PER ENTRY (P7 wave 3, V1 fix round).
|
||||
|
||||
What this replaces: a COUNT. The landing shape of the lane's arm proof asserted that at
|
||||
least 850 of at least 1000 per-entry client logs carried `MGPipe: verify armed`, which
|
||||
leaves ~117 entries unnamed and therefore unaccounted - a lane can lose fifty arms and
|
||||
gain fifty skips and the floor never moves. The census is the point (dualblock-expected-
|
||||
fatals.txt's rule, ID-119), so this is the same two-sided ratchet applied to arming:
|
||||
|
||||
* an entry whose client log carries the arming line is ARMED and needs nothing else;
|
||||
* an entry that ctest reported SKIPPED is recognised without a name - a gtest skip is a
|
||||
process that brought a session up and left before any verb, which is honestly unarmed,
|
||||
and the skip set is driver-dependent (a GitHub runner's lavapipe/llvmpipe skips more
|
||||
or fewer than the host's), so naming those would ratchet on the driver;
|
||||
* every OTHER unarmed entry must be named in the committed expected file, which says why
|
||||
each class is there.
|
||||
|
||||
Two-sided, and both directions are failures:
|
||||
* an unlisted, unskipped entry that did not arm - the comparator stopped reaching it, or
|
||||
a case stopped issuing a verb, and either way the lane is quieter than it looks;
|
||||
* a LISTED entry that armed - the name is stale and the file must lose the row in the
|
||||
commit that made it arm, or the exception set rots into a permanent amnesty;
|
||||
* a LISTED entry with no client log in this run at all - the name no longer matches an
|
||||
entry (a rename, a filter change), which would otherwise be an invisible amnesty too.
|
||||
A listed entry that SKIPPED on this run is neither: it is reported and allowed, because a
|
||||
driver difference is exactly what the skip recognition above exists to absorb."""
|
||||
directory = Path(logs_dir)
|
||||
logs = sorted(directory.glob("*.client.log"))
|
||||
if not logs:
|
||||
raise ValueError(f"verify-split arming: no *.client.log under {directory} - the per-entry "
|
||||
f"private log paths did not take, so nothing here is a per-entry proof")
|
||||
status = {}
|
||||
for case in ET.parse(junit_path).getroot().iter("testcase"):
|
||||
name = case.get("name")
|
||||
if case.find("skipped") is not None:
|
||||
status[name] = "skipped"
|
||||
elif (case.find("failure") is not None or case.find("error") is not None
|
||||
or case.get("status") == "fail"):
|
||||
status[name] = "failed"
|
||||
else:
|
||||
status[name] = "passed"
|
||||
|
||||
expected = read_pair_set(expected_path)
|
||||
armed, skipped, named, unnamed, listed_armed, no_result = [], [], [], [], [], []
|
||||
for path in logs:
|
||||
entry = path.name[: -len(".client.log")]
|
||||
if entry not in status:
|
||||
no_result.append(entry)
|
||||
continue
|
||||
if VERIFY_ARMED_LINE in path.read_text(errors="replace"):
|
||||
armed.append(entry)
|
||||
if entry in expected:
|
||||
listed_armed.append(entry)
|
||||
elif status[entry] == "skipped":
|
||||
skipped.append(entry)
|
||||
elif entry in expected:
|
||||
named.append(entry)
|
||||
else:
|
||||
unnamed.append(entry)
|
||||
|
||||
seen = set(armed) | set(skipped) | set(named) | set(unnamed)
|
||||
stale = sorted(expected - seen)
|
||||
listed_and_skipped = sorted(expected & set(skipped))
|
||||
print(f"SplitLogPaths verify-split-arming: {len(logs)} per-entry client log(s) - "
|
||||
f"{len(armed)} armed, {len(skipped)} unarmed and skipped, "
|
||||
f"{len(named)} unarmed and named in {Path(expected_path).name}")
|
||||
if listed_and_skipped:
|
||||
print(f"SplitLogPaths verify-split-arming: {len(listed_and_skipped)} named entrie(s) "
|
||||
f"skipped on this driver instead of running unarmed (allowed): "
|
||||
f"{', '.join(listed_and_skipped)}")
|
||||
problems = []
|
||||
if no_result:
|
||||
problems.append("%d private log(s) have no entry in the JUnit result: %s. The glob and "
|
||||
"the run disagree about the lane's members, so neither side is a census."
|
||||
% (len(no_result), ", ".join(sorted(no_result)[:20])))
|
||||
if unnamed:
|
||||
problems.append("%d entrie(s) ran, did not skip, and never armed the comparator: %s. Each "
|
||||
"is a process that reached no verb - add it to %s with the class it "
|
||||
"belongs to, or find out why the fill stopped happening."
|
||||
% (len(unnamed), ", ".join(sorted(unnamed)[:20]), Path(expected_path).name))
|
||||
if listed_armed:
|
||||
problems.append("%d entrie(s) are named in %s but DID arm: %s. Remove the row in the "
|
||||
"commit that made them arm - an exception set that keeps rows nothing "
|
||||
"needs any more is how this proof rots."
|
||||
% (len(listed_armed), Path(expected_path).name,
|
||||
", ".join(sorted(listed_armed)[:20])))
|
||||
if stale:
|
||||
problems.append("%d name(s) in %s match no per-entry client log in this run: %s. The "
|
||||
"entry was renamed or filtered out; drop the row in the same commit."
|
||||
% (len(stale), Path(expected_path).name, ", ".join(stale[:20])))
|
||||
if problems:
|
||||
raise ValueError("the verify-split arm proof: " + " | ".join(problems))
|
||||
print(f"SplitLogPaths verify-split-arming: per-entry arm proof OK - every log armed, skipped "
|
||||
f"or named ({len(expected)} name(s) in the expected set)")
|
||||
|
||||
|
||||
def expect_fatal(document, junit_path, expected_path):
|
||||
"""THE DUAL-BLOCK LANE'S CENSUS AND TWO-SIDED RATCHET (P5f f1, P5F §4/§6).
|
||||
|
||||
@@ -372,6 +471,13 @@ def main():
|
||||
if mode == "expect-fatal":
|
||||
expect_fatal(json.loads(Path(sys.argv[2]).read_text()), sys.argv[3], sys.argv[4])
|
||||
return
|
||||
if mode == "verify-split-arming":
|
||||
# <logs dir> <junit xml> <expected names>. It takes the LOG DIRECTORY rather than the
|
||||
# discovery JSON on purpose: the claim is about the files a run left on disk, and a
|
||||
# helper that rebuilt the set from discovery could not notice a log that was never
|
||||
# written.
|
||||
verify_split_arming(sys.argv[2], sys.argv[3], sys.argv[4])
|
||||
return
|
||||
if mode == "require-green":
|
||||
require_green(json.loads(Path(sys.argv[2]).read_text()), sys.argv[3], sys.argv[4:])
|
||||
return
|
||||
|
||||
@@ -117,5 +117,79 @@ class ResultAccountingTest(unittest.TestCase):
|
||||
helper.require_green(self.document, self.xml, [A, B])
|
||||
|
||||
|
||||
class VerifySplitArmProofTest(unittest.TestCase):
|
||||
"""The per-entry arm proof, BOTH SIDES (P7 wave 3, V1 fix round).
|
||||
|
||||
The thing it replaced was a count, and a count cannot be tested for the failure that
|
||||
matters - "fifty arms became fifty skips" leaves it unmoved. These four cases are the four
|
||||
ways the census can be wrong, and each one has to raise."""
|
||||
|
||||
ARMED = "DirectGLES.VerifySplit.TriangleScenario.DrawsATriangle"
|
||||
QUIET = "DirectGLES.VerifySplit.AdvertisedLimitsScenario.EveryGL45CoreMinimumIsMet"
|
||||
SKIPPED = "DirectVulkan.VerifySplit.F1WireScenario.SomethingLavapipeCannotDo"
|
||||
|
||||
def setUp(self):
|
||||
self.temp = tempfile.TemporaryDirectory()
|
||||
self.root = Path(self.temp.name)
|
||||
self.logs = self.root / "verify-split-logs"
|
||||
self.logs.mkdir()
|
||||
self.xml = self.root / "run.xml"
|
||||
self.expected = self.root / "expected.txt"
|
||||
self.expected.write_text("# the quiet one\n" + self.QUIET + "\n")
|
||||
self.write_log(self.ARMED, helper.VERIFY_ARMED_LINE)
|
||||
self.write_log(self.QUIET, "no fill here")
|
||||
self.write_log(self.SKIPPED, "no fill here either")
|
||||
self.results([(self.ARMED, "run", None), (self.QUIET, "run", None),
|
||||
(self.SKIPPED, "notrun", "skipped")])
|
||||
|
||||
def tearDown(self):
|
||||
self.temp.cleanup()
|
||||
|
||||
def write_log(self, entry, text):
|
||||
(self.logs / (entry + ".client.log")).write_text(text + "\n")
|
||||
|
||||
def results(self, rows):
|
||||
suite = ET.Element("testsuite")
|
||||
for name, status, kind in rows:
|
||||
case = ET.SubElement(suite, "testcase", name=name, status=status)
|
||||
if kind:
|
||||
ET.SubElement(case, kind)
|
||||
ET.ElementTree(suite).write(self.xml)
|
||||
|
||||
def proof(self):
|
||||
with contextlib.redirect_stdout(io.StringIO()):
|
||||
helper.verify_split_arming(self.logs, self.xml, self.expected)
|
||||
|
||||
def test_armed_skipped_or_named_is_the_whole_rule(self):
|
||||
self.proof()
|
||||
|
||||
def test_an_unlisted_quiet_entry_fails(self):
|
||||
self.expected.write_text("# nobody\n")
|
||||
with self.assertRaisesRegex(ValueError, "never armed"):
|
||||
self.proof()
|
||||
|
||||
def test_a_listed_entry_that_armed_fails(self):
|
||||
self.write_log(self.QUIET, helper.VERIFY_ARMED_LINE)
|
||||
with self.assertRaisesRegex(ValueError, "DID arm"):
|
||||
self.proof()
|
||||
|
||||
def test_a_listed_name_with_no_log_fails(self):
|
||||
(self.logs / (self.QUIET + ".client.log")).unlink()
|
||||
self.results([(self.ARMED, "run", None), (self.SKIPPED, "notrun", "skipped")])
|
||||
with self.assertRaisesRegex(ValueError, "match no per-entry client log"):
|
||||
self.proof()
|
||||
|
||||
def test_a_log_with_no_result_fails(self):
|
||||
self.results([(self.ARMED, "run", None), (self.QUIET, "run", None)])
|
||||
with self.assertRaisesRegex(ValueError, "no entry in the JUnit result"):
|
||||
self.proof()
|
||||
|
||||
def test_an_empty_log_directory_is_not_a_green(self):
|
||||
for path in self.logs.glob("*.client.log"):
|
||||
path.unlink()
|
||||
with self.assertRaisesRegex(ValueError, "did not take"):
|
||||
self.proof()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
|
||||
@@ -0,0 +1,129 @@
|
||||
# THE VERIFY-SPLIT ENTRIES THAT HONESTLY NEVER ARM THE COMPARATOR, BY NAME.
|
||||
#
|
||||
# `MGPipe: verify armed` is latched at a process's FIRST FILL, so a process that reaches no verb
|
||||
# never prints it. The lane's arm proof (.github/workflows/test.yml, integration-verify-split)
|
||||
# used to allow that with a COUNT - "at least 850 of at least 1000 client logs carry the line" -
|
||||
# which leaves the remainder unnamed: the lane could lose fifty arms, gain fifty skips, and the
|
||||
# floor would not move. This file is the census instead, and split_log_paths.py's
|
||||
# `verify-split-arming` mode is what reads it.
|
||||
#
|
||||
# THE RULE, PER ENTRY. Every verify-split-logs/*.client.log must either
|
||||
# (a) carry the arming line, or
|
||||
# (b) belong to an entry ctest reported SKIPPED - a gtest skip is a process that brought a
|
||||
# session up and left before any verb, and the skip set is DRIVER-DEPENDENT (a GitHub
|
||||
# runner's lavapipe/llvmpipe skips more or fewer cases than the host's), so naming those
|
||||
# would ratchet on the driver rather than on the comparator, or
|
||||
# (c) be named below.
|
||||
#
|
||||
# TWO-SIDED, the rule dualblock-expected-fatals.txt already rides (ID-119):
|
||||
# * an unlisted, unskipped entry that did not arm FAILS - a case stopped issuing a verb, or
|
||||
# the comparator stopped reaching it, and both are quieter lanes than they look;
|
||||
# * a LISTED entry that ARMED fails - remove the row in the commit that made it arm;
|
||||
# * a LISTED name with no client log in this run fails - the entry was renamed or filtered
|
||||
# out, and the row is an amnesty nobody asked for.
|
||||
# A listed entry that SKIPS on some driver is allowed and reported: that is (b) doing its job.
|
||||
#
|
||||
# HOW TO REFRESH (never by hand from a guess):
|
||||
# ctest --test-dir build-split -L '^integration-verify-split$' -j 6 \
|
||||
# --output-junit /tmp/verify-split.xml
|
||||
# python3 MobileGL/MG_IntegrationTest/Harness/split_log_paths.py verify-split-arming \
|
||||
# build-split/MobileGL/MG_IntegrationTest/verify-split-logs /tmp/verify-split.xml this-file
|
||||
# and put the names it prints here, under the class they belong to.
|
||||
#
|
||||
# MEASURED ON THE HOST (WSL Arch + lavapipe, build-split, 1064 entries with a private log):
|
||||
# 909 armed, 99 unarmed and skipped, 56 unarmed and named below.
|
||||
|
||||
# --------------------------------------------------------------------------------------------
|
||||
# CAPS, LIMITS AND EXTENSION ADVERTISEMENT - answered from the client's CapsMirror, no verb (22)
|
||||
# These call glGetIntegerv / glGetString / glGetInternalformativ and compare the answer
|
||||
# against a floor or against the other width of the same query. Nothing in them reaches a fill
|
||||
# point, so the arming line - latched at the process's FIRST fill - is honestly absent. They are
|
||||
# the same 13 entries per backend that tier 3 measured as reds under MGITEST_SPLIT_LANE=1, for
|
||||
# the same reason (a query answered without moving the encoder's record ordinal).
|
||||
# --------------------------------------------------------------------------------------------
|
||||
DirectGLES.VerifySplit.AdvertisedLimitsScenario.ComputeLocalSizeComesFromTheLinkedProgram
|
||||
DirectGLES.VerifySplit.AdvertisedLimitsScenario.ComputeUniformBlocksIsAnAmountAnApplicationCouldActuallyAllocate
|
||||
DirectGLES.VerifySplit.AdvertisedLimitsScenario.ComputeWorkGroupLimitsAreTheCapsBlocksAnswer
|
||||
DirectGLES.VerifySplit.AdvertisedLimitsScenario.EveryBufferLimitIsWithinItsAdvertisedRange
|
||||
DirectGLES.VerifySplit.AdvertisedLimitsScenario.EveryGL45CoreMinimumIsMet
|
||||
DirectGLES.VerifySplit.AdvertisedLimitsScenario.IndexedBufferBindingsAreReportedVerbatimOnBothWidths
|
||||
DirectGLES.VerifySplit.AdvertisedLimitsScenario.IndexedTargetSizeSurvivesDivisionByItsBindingCount
|
||||
DirectGLES.VerifySplit.AdvertisedLimitsScenario.PerStageBlockCountsFitInTheirBindingPoints
|
||||
DirectGLES.VerifySplit.AdvertisedLimitsScenario.ViewportArrayLimitsMeetTheirGL43Floors
|
||||
DirectGLES.VerifySplit.DoublePrecisionScenario.TheFp64ExtensionIsNotAdvertised
|
||||
DirectGLES.VerifySplit.SpirvShaderBinaryScenario.ShaderBinaryFormatIsAdvertisedExactlyOnce
|
||||
DirectVulkan.VerifySplit.AdvertisedLimitsScenario.ComputeLocalSizeComesFromTheLinkedProgram
|
||||
DirectVulkan.VerifySplit.AdvertisedLimitsScenario.ComputeUniformBlocksIsAnAmountAnApplicationCouldActuallyAllocate
|
||||
DirectVulkan.VerifySplit.AdvertisedLimitsScenario.ComputeWorkGroupLimitsAreTheCapsBlocksAnswer
|
||||
DirectVulkan.VerifySplit.AdvertisedLimitsScenario.EveryBufferLimitIsWithinItsAdvertisedRange
|
||||
DirectVulkan.VerifySplit.AdvertisedLimitsScenario.EveryGL45CoreMinimumIsMet
|
||||
DirectVulkan.VerifySplit.AdvertisedLimitsScenario.IndexedBufferBindingsAreReportedVerbatimOnBothWidths
|
||||
DirectVulkan.VerifySplit.AdvertisedLimitsScenario.IndexedTargetSizeSurvivesDivisionByItsBindingCount
|
||||
DirectVulkan.VerifySplit.AdvertisedLimitsScenario.PerStageBlockCountsFitInTheirBindingPoints
|
||||
DirectVulkan.VerifySplit.AdvertisedLimitsScenario.ViewportArrayLimitsMeetTheirGL43Floors
|
||||
DirectVulkan.VerifySplit.DoublePrecisionScenario.TheFp64ExtensionIsNotAdvertised
|
||||
DirectVulkan.VerifySplit.SpirvShaderBinaryScenario.ShaderBinaryFormatIsAdvertisedExactlyOnce
|
||||
|
||||
# --------------------------------------------------------------------------------------------
|
||||
# SHADER AND PROGRAM OBJECT SURFACES - compile, link, specialize, query, glGetError (16)
|
||||
# glShaderBinary / glSpecializeShader / glCompileShader / glGetUniformdv / the compiler
|
||||
# thread pool's settle-and-join, and the reserved-name pipeline case. Every one of them asserts on
|
||||
# an error code or a program query; none issues a draw, a dispatch, a clear or a readback.
|
||||
# --------------------------------------------------------------------------------------------
|
||||
DirectGLES.VerifySplit.AsyncCompileScenario.CompletionStatusPollingThenForcedJoin
|
||||
DirectGLES.VerifySplit.AsyncCompileScenario.ZeroCompilerThreadsSettlesEverythingImmediately
|
||||
DirectGLES.VerifySplit.DoublePrecisionScenario.GetUniformdvReadsBackWhatWasStored
|
||||
DirectGLES.VerifySplit.DoublePrecisionScenario.TheUniformIsStillReportedAsADouble
|
||||
DirectGLES.VerifySplit.ProgramPipelineScenario.AReservedNameTakesStateBeforeItIsAProgramPipeline
|
||||
DirectGLES.VerifySplit.SpirvShaderBinaryScenario.AnUnsupportedBinaryFormatIsRejectedInsteadOfSilentlyAccepted
|
||||
DirectGLES.VerifySplit.SpirvShaderBinaryScenario.CompileShaderOnASpirvShaderIsInvalidOperationAndShaderSourceTakesItBack
|
||||
DirectGLES.VerifySplit.SpirvShaderBinaryScenario.SpecializeShaderErrorSurfaceMatchesTheExtension
|
||||
DirectVulkan.VerifySplit.AsyncCompileScenario.CompletionStatusPollingThenForcedJoin
|
||||
DirectVulkan.VerifySplit.AsyncCompileScenario.ZeroCompilerThreadsSettlesEverythingImmediately
|
||||
DirectVulkan.VerifySplit.DoublePrecisionScenario.GetUniformdvReadsBackWhatWasStored
|
||||
DirectVulkan.VerifySplit.DoublePrecisionScenario.TheUniformIsStillReportedAsADouble
|
||||
DirectVulkan.VerifySplit.ProgramPipelineScenario.AReservedNameTakesStateBeforeItIsAProgramPipeline
|
||||
DirectVulkan.VerifySplit.SpirvShaderBinaryScenario.AnUnsupportedBinaryFormatIsRejectedInsteadOfSilentlyAccepted
|
||||
DirectVulkan.VerifySplit.SpirvShaderBinaryScenario.CompileShaderOnASpirvShaderIsInvalidOperationAndShaderSourceTakesItBack
|
||||
DirectVulkan.VerifySplit.SpirvShaderBinaryScenario.SpecializeShaderErrorSurfaceMatchesTheExtension
|
||||
|
||||
# --------------------------------------------------------------------------------------------
|
||||
# OBJECT STATE WRITTEN AND READ BACK, OR A CALL REJECTED BEFORE IT BECOMES A VERB (14)
|
||||
# glVertexAttribLFormat then glGetVertexAttribiv (the 64-bit format case does NOT draw - the
|
||||
# comment above it names the Adreno null-deref that is why), glTexBufferRange then the level
|
||||
# queries, glEnable then glIsEnabled, glGetIntegerv(GL_SAMPLE_MASK_WORDS) then glSampleMaski, the
|
||||
# two glCopyImageSubData level-range REJECTIONS (the call errors out, so no verb is filled), and
|
||||
# the arena readback, which is glBufferSubData + glGetBufferSubData and not a pipe verb.
|
||||
# --------------------------------------------------------------------------------------------
|
||||
DirectGLES.VerifySplit.BufferTextureScenario.LevelQueriesDescribeTheAttachedBufferRange
|
||||
DirectGLES.VerifySplit.ClipDistanceScenario.EnableIsObservableThroughIsEnabled
|
||||
DirectGLES.VerifySplit.CopyImageLevelRangeScenario.LevelOneOfASingleLevelTextureIsRejected
|
||||
DirectGLES.VerifySplit.CopyImageLevelRangeScenario.LevelTwoOfATwoLevelTextureIsRejected
|
||||
DirectGLES.VerifySplit.DoublePrecisionScenario.A64BitVertexFormatIsRecordedAndItsArrayIsDroppedAtDraw
|
||||
DirectGLES.VerifySplit.LargeArenaAdoptionScenario.ReadbackSeesTheLatestCpuWrite
|
||||
DirectGLES.VerifySplit.SampleMaskScopeScenario.TheAdvertisedSampleMaskWordCountMatchesWhatSampleMaskiAccepts
|
||||
DirectVulkan.VerifySplit.BufferTextureScenario.LevelQueriesDescribeTheAttachedBufferRange
|
||||
DirectVulkan.VerifySplit.ClipDistanceScenario.EnableIsObservableThroughIsEnabled
|
||||
DirectVulkan.VerifySplit.CopyImageLevelRangeScenario.LevelOneOfASingleLevelTextureIsRejected
|
||||
DirectVulkan.VerifySplit.CopyImageLevelRangeScenario.LevelTwoOfATwoLevelTextureIsRejected
|
||||
DirectVulkan.VerifySplit.DoublePrecisionScenario.A64BitVertexFormatIsRecordedAndItsArrayIsDroppedAtDraw
|
||||
DirectVulkan.VerifySplit.LargeArenaAdoptionScenario.ReadbackSeesTheLatestCpuWrite
|
||||
DirectVulkan.VerifySplit.SampleMaskScopeScenario.TheAdvertisedSampleMaskWordCountMatchesWhatSampleMaskiAccepts
|
||||
|
||||
# --------------------------------------------------------------------------------------------
|
||||
# FRAMEBUFFER COMPLETENESS QUERIES - attach and ask, never render (2)
|
||||
# glCheckFramebufferStatus on an attachment of that format. Only the DirectGLES half is
|
||||
# here: on DirectVulkan both cases SKIP (the format is not renderable on lavapipe), and a skip is
|
||||
# recognised without a name.
|
||||
# --------------------------------------------------------------------------------------------
|
||||
DirectGLES.VerifySplit.SnormAttachmentScenario.SignedNormalizedColorAttachmentsReportComplete
|
||||
DirectGLES.VerifySplit.ThreeChannelAttachmentScenario.ThreeChannelColorAttachmentsReportComplete
|
||||
|
||||
# --------------------------------------------------------------------------------------------
|
||||
# THE POISON PARENT THAT ONLY FORKS (2)
|
||||
# PoisonOmissionScenario.WithoutOmissionCompletes re-executes the binary and waits; the
|
||||
# frame is drawn in the CHILD, which writes its own log (<base>.poison-child). The parent process
|
||||
# brings a session up and forks, so it reaches no verb of its own.
|
||||
# --------------------------------------------------------------------------------------------
|
||||
DirectGLES.VerifySplit.PoisonOmissionScenario.WithoutOmissionCompletes
|
||||
DirectVulkan.VerifySplit.PoisonOmissionScenario.WithoutOmissionCompletes
|
||||
@@ -96,9 +96,20 @@ upload,所以描述符宣告 `(target, level)` 而调用递空指针,会走
|
||||
集合是错的(tier 3 实测 13 红全是 CapsMirror 回答的查询)。取而代之的是**逐条目私有日志**
|
||||
(`SplitLogPaths.cmake.in` 的 `MGL_VERIFY_SPLIT_TEST_LISTS`)和 CI 的逐条目臂证明:每个 client 日志都必须有
|
||||
ConfigLoader 的 `MOBILEGL_TRANSPORT=inproc` 行;`MGPipe: verify armed` 是进程首次 fill 时才打的,所以从不到达
|
||||
动词的进程(limits 查询、CapsMirror 回答、只 fork 的 poison 父进程)诚实地没有它——落地这一轮 1064 个里
|
||||
909 个有、155 个没有(其中 99 个是 skip)——CI 要求两个 `VerifySplitArming.` 日志都有,并给逐条目计数设下限
|
||||
850。
|
||||
动词的进程诚实地没有它。
|
||||
- **臂证明是逐条目的普查,不是计数(V1 修复轮改)。** 落地形状是「≥1000 个日志里 ≥850 个 armed」,剩下的
|
||||
~155 条无名——车道掉 50 个 arm、多 50 个 skip,这个下限一动不动。现在用 `dualblock-expected-fatals.txt` 那条
|
||||
两侧规则(ID-119):每个 client 日志要么 **armed**,要么属于 **ctest 报 SKIPPED 的条目**(gtest skip = 起了会话
|
||||
没到动词,而且 skip 集合随驱动变,点名会变成对驱动上棘轮),要么**按名**列在
|
||||
`MG_IntegrationTest/Harness/verify-split-unarmed-expected.txt` 里。两侧都红:列了却 armed、列了却本轮没有日志、
|
||||
没列却既没 armed 也没 skip。读表的是 `split_log_paths.py` 新增的 `verify-split-arming` 模式(`test_split_log_paths.py`
|
||||
里六条控制用例覆盖四种错法);车道步骤为此加 `--output-junit`。
|
||||
- **本机实测(1064 个私有 client 日志):909 armed、99 unarmed 且 skip、56 unarmed 且按名列出。** 五类,逐类原因
|
||||
写在文件里:caps/limits/扩展广告(22,`glGet*` 由 client 的 CapsMirror 回答,不经填充点);shader / program 对象
|
||||
与其错误面(16,编译、链接、specialize、uniform 回读、编译线程池 settle,都不画);对象状态写了再查回、或调用在
|
||||
成为动词之前就被拒(14,含两条 `glCopyImageSubData` level 拒绝与 arena 的 `glGetBufferSubData` 回读);framebuffer
|
||||
完整性查询(2,只有 DirectGLES 半边——DirectVulkan 上这两条 skip,由 skip 规则吸收);只 fork 的 poison 父进程(2,
|
||||
帧画在子进程里、写子进程自己的日志)。
|
||||
|
||||
### 2.2 发现 A(真实分歧,已定位;改的是预言,不是规则)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user