From 7059869848f2ac610035bec7e32c2d6110a5a723 Mon Sep 17 00:00:00 2001 From: Swung0x48 Date: Sun, 20 Sep 2026 19:22:38 -0400 Subject: [PATCH] [Test] fix the two new stage-chunk cases' own expectations Both cases were written against the wrong property and went red on the first WGB build, which is the one thing a case may not do. TextureEmit.ALevelTooLargeForTheStageChunkIsCutIntoSlabs asserted D == 8 slices for every slab of the 512x128x33 level, whose last slab is the 33rd slice alone. The per-slab check now names the scheme (Z = 8 * index, D = 8 for every slab but the remainder) instead of restating one number for all of them. StagedTextureStoreTest.ALevelWithAPieceMissingIsRefusedAsAWholeLevelReadByName wanted "not covered" out of a single LEADING run, and the store cannot answer that: it knows no format, so it cannot know how many bytes the level should have - the check is "the range the reader asks is covered". The case now uses the shape the check really is about, two runs with a hole between them (ServerLoopTest's R-11 coverage case, one store over), and the looseness is written down: in the header's coverage ruling, and as an assertion on the leading-run case beside it. --- .../MG_Remote/Server/StagedTextureStore.h | 6 +++++- MobileGL/MG_Test/Pipe/TextureEmitTest.cpp | 6 ++++-- .../MG_Test/Wire/StagedTextureStoreTest.cpp | 21 ++++++++++++++++--- 3 files changed, 27 insertions(+), 6 deletions(-) diff --git a/MobileGL/MG_Remote/Server/StagedTextureStore.h b/MobileGL/MG_Remote/Server/StagedTextureStore.h index 7abbbce3..059a5d53 100644 --- a/MobileGL/MG_Remote/Server/StagedTextureStore.h +++ b/MobileGL/MG_Remote/Server/StagedTextureStore.h @@ -61,7 +61,11 @@ // that lets the sync read - the set is complete exactly when every piece has landed, and a level // with a hole in it is Fatal{StageSnapshotTooNarrow}: the same words as the buffer half, for the // same reason, because those bytes have never existed on this side and inventing them is silent -// data loss rather than a missing optimisation. +// data loss rather than a missing optimisation. THE QUESTION IS "IS THE RANGE THE READER ASKS +// FOR COVERED", not "is the level the size the client meant": no format crosses the wire, so a +// leading run that already reaches the image's high-water mark reads as complete when later +// pieces are still missing - unreachable, because nothing reads between the pieces of one verb, +// and written down here because it is the one place this check is looser than it looks. // // DEFINED-NESS IS TRACKED, NOT DERIVED ALONE. ยง1's per-level extent derivation (max(1, // base >> level), layer axes fixed) computes the extent of a level that EXISTS; it cannot diff --git a/MobileGL/MG_Test/Pipe/TextureEmitTest.cpp b/MobileGL/MG_Test/Pipe/TextureEmitTest.cpp index 3c7cedb4..46dff10e 100644 --- a/MobileGL/MG_Test/Pipe/TextureEmitTest.cpp +++ b/MobileGL/MG_Test/Pipe/TextureEmitTest.cpp @@ -666,12 +666,14 @@ TEST(TextureEmit, ALevelTooLargeForTheStageChunkIsCutIntoSlabs) { const Uint64 volumeBytes = 512ull * 128ull * 33ull * 16ull; const std::vector volumeSlabs = cut(volume, volumeBytes, cap); ASSERT_EQ(volumeSlabs.size(), 5u); - for (const MGPipeTextureSlab& slab : volumeSlabs) { + for (SizeT i = 0; i < volumeSlabs.size(); ++i) { + const MGPipeTextureSlab& slab = volumeSlabs[i]; EXPECT_EQ(slab.Box.X, 0); EXPECT_EQ(slab.Box.Y, 0); EXPECT_EQ(slab.Box.W, 512u) << "a slab is not the level's full width"; EXPECT_EQ(slab.Box.H, 128u); - EXPECT_EQ(slab.Box.D, 8u); + EXPECT_EQ(slab.Box.Z, static_cast(8 * i)) << "the slabs are not in slice order"; + EXPECT_EQ(slab.Box.D, i + 1 == volumeSlabs.size() ? 1u : 8u); } EXPECT_EQ(volumeSlabs.back().Box.D, 1u) << "the remainder slice is its own piece"; diff --git a/MobileGL/MG_Test/Wire/StagedTextureStoreTest.cpp b/MobileGL/MG_Test/Wire/StagedTextureStoreTest.cpp index 72e3ee85..780611e4 100644 --- a/MobileGL/MG_Test/Wire/StagedTextureStoreTest.cpp +++ b/MobileGL/MG_Test/Wire/StagedTextureStoreTest.cpp @@ -281,6 +281,17 @@ TEST(StagedTextureStoreTest, StageChunkRunsAssembleTheLevelImageAndAGapIsNotCove EXPECT_EQ(store.LevelCoveredRunCount(holed, kTex2DTarget, 0), 2u) << "two runs with a 16-byte gap merged into one"; EXPECT_FALSE(store.IsCovered(holed, kTex2DTarget, 0)) << "a span across the gap reads covered"; + // AND WHAT THE CHECK CANNOT SEE, written down rather than left to be discovered: the store + // knows no format, so it cannot know how many bytes a level SHOULD have. A leading run that + // already reaches the image's high-water mark therefore reads as covered - the check is "the + // range the reader asks for is covered", not "the level is the size the client meant", and no + // reader runs between the pieces of one level anyway (they are records of ONE verb, and the + // texture sync runs at the barrier that closes it). + const Uint64 leading = Server::StagedTextureStore::KeyForHandle(TestHandle(14, 1)); + store.AdoptRun(leading, kTex2DTarget, 0, IntVec3{4, 4, 1}, 0, part.data(), part.size()); + EXPECT_TRUE(store.IsCovered(leading, kTex2DTarget, 0)) + << "a leading run is the whole image as far as this store can tell"; + EXPECT_EQ(store.LevelByteSize(leading, kTex2DTarget, 0), 16u); // Filling the gap merges all three into one run: adjacency, not proximity, is the rule. store.AdoptRun(holed, kTex2DTarget, 0, extent, 16, part.data(), part.size()); EXPECT_EQ(store.LevelCoveredRunCount(holed, kTex2DTarget, 0), 1u); @@ -398,11 +409,15 @@ TEST(StagedTextureStoreTest, AGpuGeneratedLevelHasNoBytesAndItsTexelReadIsFatalB TEST(StagedTextureStoreTest, ALevelWithAPieceMissingIsRefusedAsAWholeLevelReadByName) { Server::StagedTextureStore store(/*copies=*/true); const Uint64 key = Server::StagedTextureStore::KeyForHandle(TestHandle(13, 1)); - Vector half(32, 0x55); + Vector part(16, 0x55); Vector whole(64, 0x22); - store.AdoptRun(key, kTex2DTarget, 0, IntVec3{4, 4, 1}, 0, half.data(), half.size()); + // TWO RUNS WITH A HOLE BETWEEN THEM, which is what a missing piece looks like from here: the + // image's high-water mark is the level's size and the covered set does not reach across it. + store.AdoptRun(key, kTex2DTarget, 0, IntVec3{4, 4, 1}, 0, part.data(), part.size()); + store.AdoptRun(key, kTex2DTarget, 0, IntVec3{4, 4, 1}, 32, part.data(), part.size()); ASSERT_FALSE(store.IsCovered(key, kTex2DTarget, 0)); - ASSERT_EQ(store.LevelCoveredRunCount(key, kTex2DTarget, 0), 1u); + ASSERT_EQ(store.LevelCoveredRunCount(key, kTex2DTarget, 0), 2u); + ASSERT_EQ(store.LevelByteSize(key, kTex2DTarget, 0), 48u); // A DIFFERENT level of the same store, adopted whole, reads fine - asserted first so that the // death below cannot be a function that aborts on everything.