From 2abc5617216ada6a34f9ed816bd071b520465f5d Mon Sep 17 00:00:00 2001 From: Swung0x48 Date: Sat, 19 Sep 2026 22:29:36 -0400 Subject: [PATCH] p5f fm: cover GPU texture writes and view subresources --- .../Scenarios/F1WireScenario.cpp | 78 +++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/MobileGL/MG_IntegrationTest/Scenarios/F1WireScenario.cpp b/MobileGL/MG_IntegrationTest/Scenarios/F1WireScenario.cpp index acc02e95..8a8dce51 100644 --- a/MobileGL/MG_IntegrationTest/Scenarios/F1WireScenario.cpp +++ b/MobileGL/MG_IntegrationTest/Scenarios/F1WireScenario.cpp @@ -259,6 +259,84 @@ TEST_F(F1WireScenario, GenerateMipmapDepthPixels) { EXPECT_NEAR(pixel, 0.375f, 0.00001f); } +TEST_F(F1WireScenario, PartialTextureUploadPreservesGpuClearPixels) { + if (!Ready()) return; + Attach(GL_RGBA8); + // The client shadow is red. The GPU then changes every texel to green, so + // applying a later one-pixel upload as a whole shadow loses observable data. + std::array red{}; + for (size_t i = 0; i < red.size(); i += 4) { + red[i] = 255; + red[i + 3] = 255; + } + glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 8, 8, GL_RGBA, GL_UNSIGNED_BYTE, red.data()); + glClearColor(0, 1, 0, 1); + glClear(GL_COLOR_BUFFER_BIT); + const std::array blue{0, 0, 255, 255}; + glTexSubImage2D(GL_TEXTURE_2D, 0, 1, 2, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, blue.data()); + + std::array pixels{}; + glReadPixels(0, 0, 8, 8, GL_RGBA, GL_UNSIGNED_BYTE, pixels.data()); + ASSERT_EQ(FirstGLError(), GLenum(GL_NO_ERROR)); + for (int y = 0; y < 8; ++y) { + for (int x = 0; x < 8; ++x) { + const std::array expected = x == 1 && y == 2 + ? blue : std::array{0, 255, 0, 255}; + const size_t at = static_cast(y * 8 + x) * 4; + const std::array actual{pixels[at], pixels[at + 1], pixels[at + 2], pixels[at + 3]}; + EXPECT_EQ(actual, expected) << "GPU clear survived outside upload at " << x << ", " << y; + } + } +} + +TEST_F(F1WireScenario, TextureViewClearTargetsItsRootMipAndLayer) { + if (!Ready()) return; + GLuint root = 0, view = 0; + glGenTextures(1, &root); + glBindTexture(GL_TEXTURE_2D_ARRAY, root); + glTexStorage3D(GL_TEXTURE_2D_ARRAY, 2, GL_RGBA8, 8, 8, 2); + std::array red{}; + for (size_t i = 0; i < red.size(); i += 4) { + red[i] = 255; + red[i + 3] = 255; + } + glTexSubImage3D(GL_TEXTURE_2D_ARRAY, 0, 0, 0, 0, 8, 8, 2, + GL_RGBA, GL_UNSIGNED_BYTE, red.data()); + glTexSubImage3D(GL_TEXTURE_2D_ARRAY, 1, 0, 0, 0, 4, 4, 2, + GL_RGBA, GL_UNSIGNED_BYTE, red.data()); + std::array blue{}; + for (size_t i = 0; i < blue.size(); i += 4) { + blue[i + 2] = 255; + blue[i + 3] = 255; + } + glTexSubImage3D(GL_TEXTURE_2D_ARRAY, 1, 0, 0, 0, 4, 4, 1, + GL_RGBA, GL_UNSIGNED_BYTE, blue.data()); + glGenTextures(1, &view); + glTextureView(view, GL_TEXTURE_2D, root, GL_RGBA8, 1, 1, 1, 1); + ASSERT_EQ(FirstGLError(), GLenum(GL_NO_ERROR)) << "view construction"; + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, view, 0); + ASSERT_EQ(glCheckFramebufferStatus(GL_FRAMEBUFFER), GLenum(GL_FRAMEBUFFER_COMPLETE)); + glClearColor(0, 1, 0, 1); + glClear(GL_COLOR_BUFFER_BIT); + + std::array pixel{}; + glReadPixels(1, 2, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, pixel.data()); + EXPECT_EQ(pixel, (std::array{0, 255, 0, 255})) << "view reads its cleared window"; + glFramebufferTextureLayer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, root, 1, 1); + glReadPixels(1, 2, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, pixel.data()); + EXPECT_EQ(pixel, (std::array{0, 255, 0, 255})) << "view clear reaches root mip 1, layer 1"; + glFramebufferTextureLayer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, root, 1, 0); + glReadPixels(1, 2, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, pixel.data()); + EXPECT_EQ(pixel, (std::array{0, 0, 255, 255})) << "the neighboring root layer is preserved"; + glFramebufferTextureLayer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, root, 0, 1); + glReadPixels(1, 2, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, pixel.data()); + EXPECT_EQ(pixel, (std::array{255, 0, 0, 255})) << "the neighboring root mip is preserved"; + EXPECT_EQ(FirstGLError(), GLenum(GL_NO_ERROR)); + glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, 0, 0); + glDeleteTextures(1, &view); + glDeleteTextures(1, &root); +} + TEST_F(F1WireScenario, NamedBlitPreservesBindingsAndRestoresNextVerbsPixels) { if (!Ready()) return; Attach(GL_RGBA8);