[Test] (MG_IntegrationTest): baseline the CSO slot leak case after two warm-up rounds so it measures growth with the churn, not the first draws one-off slots

This commit is contained in:
2026-09-08 07:09:25 -04:00
parent 959ca30810
commit 680ea63360
@@ -1023,11 +1023,8 @@ void main() { oColor = texture(uTex, vUv); }
"allocator to leak from."; "allocator to leak from.";
} }
unsigned liveBefore = 0; unsigned probe = 0;
unsigned highWaterBefore = 0; if (!MGITest::PeekPipeSlotLiveCount(MGITest::PipeSlotKind::VertexElementsCso, &probe)) {
if (!MGITest::PeekPipeSlotLiveCount(MGITest::PipeSlotKind::VertexElementsCso, &liveBefore) ||
!MGITest::PeekPipeSlotHighWater(MGITest::PipeSlotKind::VertexElementsCso,
&highWaterBefore)) {
GTEST_SKIP() << "the client slot allocator is out of reach from this module (a pull " GTEST_SKIP() << "the client slot allocator is out of reach from this module (a pull "
"build has none, and the Android link resolves no internal symbol), so " "build has none, and the Android link resolves no internal symbol), so "
"'could not look' would be reported as 'did not leak'"; "'could not look' would be reported as 'did not leak'";
@@ -1037,31 +1034,54 @@ void main() { oColor = texture(uTex, vUv); }
// Buffer kind's slots alongside them and blur which allocator answered. // Buffer kind's slots alongside them and blur which allocator answered.
const GLuint buffer = MakeQuadBuffer(0.0f, 1.0f, 0.0f); const GLuint buffer = MakeQuadBuffer(0.0f, 1.0f, 0.0f);
// Each round creates a vertex array, DRAWS with it - which is what mints the slot and // One round: create a vertex array, DRAW with it - which is what mints the slot and
// publishes the applier's record; a VAO that never reaches a validate point has // publishes the applier's record; a VAO that never reaches a validate point has
// neither - unbinds it and deletes it. glGenVertexArrays hands the same name back // neither - unbind it and delete it. glGenVertexArrays hands the same name back every
// every time, exactly as a chunk renderer's does, so a death path that keyed on the // time, exactly as a chunk renderer's does, so a death path that keyed on the GL NAME
// GL NAME rather than on the lifetime id would look correct here too - which is why // rather than on the lifetime id would look correct here too, which is why the
// the assertion is on the allocator and not on the name. // assertion is on the allocator and not on the name.
constexpr int kChurn = 48; unsigned peakLive = 0;
unsigned peakLive = liveBefore; const auto round = [&](const char* when, bool checkPixels) {
for (int round = 0; round < kChurn; ++round) {
GLuint vao = 0; GLuint vao = 0;
glGenVertexArrays(1, &vao); glGenVertexArrays(1, &vao);
ConfigureQuadVao(vao, buffer); ConfigureQuadVao(vao, buffer);
const Image image = DrawQuadAndRead(vao); const Image image = DrawQuadAndRead(vao);
if (round == 0) { if (checkPixels) {
// One picture check, so a green here cannot mean "the draws never happened // One picture check, so a green here cannot mean "the draws never happened
// and therefore nothing was ever minted". // and therefore nothing was ever minted".
ExpectWholeViewportIs(image, "green", "the churn's first draw"); ExpectWholeViewportIs(image, "green", when);
} }
unsigned live = 0; unsigned live = 0;
ASSERT_TRUE(MGITest::PeekPipeSlotLiveCount(MGITest::PipeSlotKind::VertexElementsCso, if (MGITest::PeekPipeSlotLiveCount(MGITest::PipeSlotKind::VertexElementsCso, &live) &&
&live)); live > peakLive) {
if (live > peakLive) peakLive = live; peakLive = live;
}
glBindVertexArray(0); glBindVertexArray(0);
glDeleteVertexArrays(1, &vao); glDeleteVertexArrays(1, &vao);
} };
// TWO WARM-UP ROUNDS BEFORE THE BASELINE IS TAKEN, so what is measured is growth WITH
// the churn and not the one-off cost of drawing at all. The first draws in a process
// mint slots that legitimately never come back inside this case - the DEFAULT vertex
// array's above all, which this scenario's frames bind and which lives as long as the
// context does - and the second round is what proves the steady state has been
// reached, since a per-round leak would still be growing at that point. Sampling
// before them would score a one-off as the churn's leak; sampling after makes the
// assertion the exact one that matters: "N more create/destroy cycles cost ZERO more
// slots", with no slack in it.
round("the first warm-up draw", /*checkPixels=*/true);
round("the second warm-up draw", /*checkPixels=*/false);
peakLive = 0;
unsigned liveBefore = 0;
unsigned highWaterBefore = 0;
ASSERT_TRUE(MGITest::PeekPipeSlotLiveCount(MGITest::PipeSlotKind::VertexElementsCso,
&liveBefore));
ASSERT_TRUE(MGITest::PeekPipeSlotHighWater(MGITest::PipeSlotKind::VertexElementsCso,
&highWaterBefore));
constexpr int kChurn = 48;
for (int i = 0; i < kChurn; ++i) round("a churn draw", /*checkPixels=*/false);
ASSERT_EQ(FirstGLError(), GLenum(GL_NO_ERROR)) << "the churn left a GL error behind"; ASSERT_EQ(FirstGLError(), GLenum(GL_NO_ERROR)) << "the churn left a GL error behind";
unsigned liveAfter = 0; unsigned liveAfter = 0;
@@ -1082,10 +1102,11 @@ void main() { oColor = texture(uTex, vUv); }
"process, and past kMGPipeMaxVertexElementsSlots every create_vertex_elements " "process, and past kMGPipeMaxVertexElementsSlots every create_vertex_elements "
"trips Fatal{ProtocolCorruption} for good. Backend " "trips Fatal{ProtocolCorruption} for good. Backend "
<< Gl().BackendName(); << Gl().BackendName();
EXPECT_LE(highWaterAfter - highWaterBefore, 4u) EXPECT_EQ(highWaterAfter, highWaterBefore)
<< "the CSO slot space grew with the churn instead of recycling one slot; the " << "the CSO slot space grew with the churn instead of recycling the one slot the "
"frees are not reaching the allocator's free list"; "warm-up round already handed out; the frees are not reaching the allocator's "
EXPECT_LE(peakLive - liveBefore, 2u) "free list";
EXPECT_LE(peakLive - liveBefore, 1u)
<< "more than one churned vertex array was live at the allocator at once, so the " << "more than one churned vertex array was live at the allocator at once, so the "
"deaths are arriving late rather than at the destructor"; "deaths are arriving late rather than at the destructor";