[Fix] (DirectGLES): verify generated mip storage from pushed descriptors

This commit is contained in:
2026-09-16 13:56:55 -04:00
parent f018c0ef05
commit 348d22a4b9
3 changed files with 73 additions and 1 deletions
@@ -8078,6 +8078,38 @@ namespace MobileGL::MG_Backend::DirectGLES {
} }
static Bool EnsureGenerateMipmapStorageAllocated(const SharedPtr<MG_State::GLState::ITextureObject>& texture) { static Bool EnsureGenerateMipmapStorageAllocated(const SharedPtr<MG_State::GLState::ITextureObject>& texture) {
#if MOBILEGL_BUILD_DISAGGREGATED
if (MG_Config::Transport != MG_Config::TransportMode::Monolith) {
// The frontend has already defined the generated chain before emitting this verb.
// Its resource_respecify carries that shape, so the server only verifies the
// descriptor; it must not allocate or dirty the client's level shadows again.
// Identity resolution is the same existing registry lookup used by texture sync.
const auto handle = TextureImpl::g_backendTextureObjects.HandleOf(texture.get());
const auto* record = PipeTextureRecordForHandle(handle);
if (record == nullptr || record->Desc.Width == 0 || record->Desc.Levels == 0) {
MG_Pipe::MGPipeUnmigratedEmulation("generate-mipmap-storage");
}
const auto& desc = record->Desc;
Uint maxDimension = desc.Width;
const auto target = static_cast<MG_Pipe::MGPipeResourceTarget>(desc.Target);
if (target != MG_Pipe::MGPipeResourceTarget::Tex1D &&
target != MG_Pipe::MGPipeResourceTarget::Tex1DArray) {
maxDimension = std::max(maxDimension, desc.Height);
}
if (target == MG_Pipe::MGPipeResourceTarget::Tex3D) {
maxDimension = std::max(maxDimension, desc.Depth);
}
Uint requiredLevels = 1;
while (maxDimension > 1) {
maxDimension /= 2;
++requiredLevels;
}
if (desc.Levels < requiredLevels) {
MG_Pipe::MGPipeUnmigratedEmulation("generate-mipmap-storage");
}
return false; // No server-side shadow allocation was necessary.
}
#endif
auto* mipmapTexture = dynamic_cast<MG_State::GLState::TextureObjectMipmap*>(texture.get()); auto* mipmapTexture = dynamic_cast<MG_State::GLState::TextureObjectMipmap*>(texture.get());
MOBILEGL_ASSERT(mipmapTexture != nullptr, "GenerateMipmap requires mipmap texture storage."); MOBILEGL_ASSERT(mipmapTexture != nullptr, "GenerateMipmap requires mipmap texture storage.");
Bool allocatedStorage = false; Bool allocatedStorage = false;
+1 -1
View File
@@ -2159,7 +2159,7 @@ endif()
# f1: split-only source keeps the monolith registry unchanged. # f1: split-only source keeps the monolith registry unchanged.
if (MOBILEGL_BUILD_DISAGGREGATED) if (MOBILEGL_BUILD_DISAGGREGATED)
target_sources(MobileGLIntegrationTest PRIVATE Scenarios/F1WireScenario.cpp) target_sources(MobileGLIntegrationTest PRIVATE Scenarios/F1WireScenario.cpp)
foreach(f1Slot ClearBufferfi ClearBufferfv ClearBufferiv ClearBufferuiv ClearNamedFramebufferfi ClearNamedFramebufferfv ClearNamedFramebufferiv ClearNamedFramebufferuiv CopyTexImage2D CopyTexSubImage2D GenerateMipmap) foreach(f1Slot ClearBufferfi ClearBufferfv ClearBufferiv ClearBufferuiv ClearNamedFramebufferfi ClearNamedFramebufferfv ClearNamedFramebufferiv ClearNamedFramebufferuiv CopyTexImage2D CopyTexSubImage2D GenerateMipmap GenerateMipmapPackedFloat GenerateMipmapDepth)
gtest_discover_tests(MobileGLIntegrationTest gtest_discover_tests(MobileGLIntegrationTest
TEST_PREFIX "DirectGLES.Split.F1." TEST_PREFIX "DirectGLES.Split.F1."
TEST_FILTER "F1WireScenario.${f1Slot}Pixels" TEST_FILTER "F1WireScenario.${f1Slot}Pixels"
@@ -219,6 +219,46 @@ TEST_F(F1WireScenario, GenerateMipmapPixels) {
const int expected[4] = {64, 128, 191, 255}; const int expected[4] = {64, 128, 191, 255};
for (int i = 0; i < 4; ++i) EXPECT_NEAR(pixel[i], expected[i], 1) << "F1.GenerateMipmap.pixels"; for (int i = 0; i < 4; ++i) EXPECT_NEAR(pixel[i], expected[i], 1) << "F1.GenerateMipmap.pixels";
} }
TEST_F(F1WireScenario, GenerateMipmapPackedFloatPixels) {
if (!Ready()) return;
// Only level zero exists initially. Generating the special-format chain must use the
// shape published by the client, and level two must contain the generated GPU pixels.
std::array<GLfloat, 8 * 8 * 3> source{};
for (size_t i = 0; i < source.size(); i += 3) {
source[i] = 0.25f; source[i + 1] = 0.5f; source[i + 2] = 0.75f;
}
glTexImage2D(GL_TEXTURE_2D, 0, GL_R11F_G11F_B10F, 8, 8, 0, GL_RGB, GL_FLOAT, source.data());
const auto before = PeekSplitRuntime().emitSeq;
glGenerateMipmap(GL_TEXTURE_2D);
ASSERT_GT(PeekSplitRuntime().emitSeq, before);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture, 2);
ASSERT_EQ(glCheckFramebufferStatus(GL_FRAMEBUFFER), GLenum(GL_FRAMEBUFFER_COMPLETE));
GLfloat pixel[4]{};
glReadPixels(1, 1, 1, 1, GL_RGBA, GL_FLOAT, pixel);
ASSERT_EQ(FirstGLError(), GLenum(GL_NO_ERROR));
const GLfloat expected[4] = {0.25f, 0.5f, 0.75f, 1.0f};
for (int i = 0; i < 4; ++i) EXPECT_NEAR(pixel[i], expected[i], 0.01f);
}
TEST_F(F1WireScenario, GenerateMipmapDepthPixels) {
if (!Ready()) return;
std::array<GLfloat, 8 * 8> source{};
source.fill(0.375f);
glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT32F, 8, 8, 0,
GL_DEPTH_COMPONENT, GL_FLOAT, source.data());
const auto before = PeekSplitRuntime().emitSeq;
glGenerateMipmap(GL_TEXTURE_2D);
ASSERT_GT(PeekSplitRuntime().emitSeq, before);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, texture, 2);
glDrawBuffer(GL_NONE);
glReadBuffer(GL_NONE);
ASSERT_EQ(glCheckFramebufferStatus(GL_FRAMEBUFFER), GLenum(GL_FRAMEBUFFER_COMPLETE));
GLfloat pixel = 0;
glReadPixels(1, 1, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT, &pixel);
ASSERT_EQ(FirstGLError(), GLenum(GL_NO_ERROR));
EXPECT_NEAR(pixel, 0.375f, 0.00001f);
}
TEST_F(F1WireScenario, NamedBlitPreservesBindingsAndRestoresNextVerbsPixels) { TEST_F(F1WireScenario, NamedBlitPreservesBindingsAndRestoresNextVerbsPixels) {
if (!Ready()) return; if (!Ready()) return;
Attach(GL_RGBA8); Attach(GL_RGBA8);