diff --git a/MobileGL/MG_Test/Wire/RemoteClientControls.inc b/MobileGL/MG_Test/Wire/RemoteClientControls.inc index f5ae64fa..ad6eade0 100644 --- a/MobileGL/MG_Test/Wire/RemoteClientControls.inc +++ b/MobileGL/MG_Test/Wire/RemoteClientControls.inc @@ -406,16 +406,28 @@ TEST(RemoteClientControls, DeleteTexturesAdvancesTheWireOrdinal) { } TEST(RemoteClientControls, BoundPackBufferOffsetReadRefusesByName) { + // Historical name retained: the live path now copies the reply into the PBO. const auto child = RunInChild([] { StartControlSession(); + ReadPeer peer; + peer.size = 4; + peer.Install(); MG_State::pGLContext = MakeUnique(); GLuint buffer = 0; MG_Impl::GLImpl::GenBuffers(1, &buffer); MG_Impl::GLImpl::BindBuffer(0x88EB, buffer); - MG_Impl::GLImpl::BufferData(0x88EB, 64, nullptr, 0x88E1); + Uint8 seed[64]; + std::memset(seed, 0x5a, sizeof(seed)); + MG_Impl::GLImpl::BufferData(0x88EB, sizeof(seed), seed, 0x88E1); RemoteEmitTable().GL.ReadPixels(0, 0, 1, 1, 0x1908, 0x1401, reinterpret_cast(16)); + Uint8 actual[64]{}; + MG_Impl::GLImpl::GetBufferSubData(0x88EB, 0, sizeof(actual), actual); + for (SizeT i = 0; i < sizeof(actual); ++i) + if (actual[i] != (i >= 16 && i < 20 ? 0x37 : 0x5a)) ::_exit(104); + if (peer.observed != 4) ::_exit(105); + ClientSessionInstance().Stop(); }); - ExpectNamedAbort(child, "Fatal{UnmigratedVerb, \"ReadPixels+PACK_BUFFER\"}"); + ExpectChildSuccess(child); } TEST(RemoteClientControls, ServerRoleRespecifyDoesNotEmitClientInitialBytes) { @@ -513,16 +525,14 @@ int RunPackGpuControl(bool split) { G::BindBuffer(GL_PIXEL_PACK_BUFFER, pbo); G::BufferData(GL_PIXEL_PACK_BUFFER, 64, nullptr, GL_STREAM_READ); G::ReadPixels(0, 0, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, reinterpret_cast(16)); - if (split) ::_exit(104); // Must have refused by name, never returned or dereferenced 16. Uint8 pixels[4]{}; G::GetBufferSubData(GL_PIXEL_PACK_BUFFER, 16, 4, pixels); if (pixels[0] != 0 || pixels[1] != 255 || pixels[2] != 0 || pixels[3] != 255) ::_exit(105); - std::printf("C1F_PACK_MONOLITH bytes at offset 16: %u %u %u %u\n", pixels[0], pixels[1], pixels[2], pixels[3]); + std::printf("C1F_PACK_%s bytes at offset 16: %u %u %u %u\n", split ? "INPROC" : "MONOLITH", + pixels[0], pixels[1], pixels[2], pixels[3]); std::fflush(stdout); }); - const bool ok = split - ? DiedOfAbort(child) && child.Log.find("Fatal{UnmigratedVerb, \"ReadPixels+PACK_BUFFER\"}") != std::string::npos - : WIFEXITED(child.Status) && WEXITSTATUS(child.Status) == 0; + const bool ok = WIFEXITED(child.Status) && WEXITSTATUS(child.Status) == 0; std::printf("C1F_PACK_%s: %s (%s)\n%s", split ? "INPROC" : "MONOLITH", ok ? "PASS" : "FAIL", DescribeStatus(child).c_str(), child.Log.c_str()); return ok ? 0 : 1; diff --git a/MobileGL/MG_Test/Wire/RemoteClientTest.cpp b/MobileGL/MG_Test/Wire/RemoteClientTest.cpp index d646b7c0..df294af2 100644 --- a/MobileGL/MG_Test/Wire/RemoteClientTest.cpp +++ b/MobileGL/MG_Test/Wire/RemoteClientTest.cpp @@ -1571,64 +1571,106 @@ TEST(RemoteF1, GenerateMipmapFieldsCross) { #if MGTEST_HAVE_FORK -TEST(RemoteF1, UnboundNamedfvRefusesByName) { - // Red once (executed, reverted): disable the named-FBO refusal; its exact Fatal disappears. - const auto child = RunInChild([] { +namespace { + MGPipeHandle observedNamedClear{}, observedNamedClearRead{}; + Uint32 observedNamedClearValues[4]{}; + Uint32 observedNamedClearCalls = 0; + GLenum observedNamedClearTarget = 0; + GLint observedNamedClearIndex = 0; + GLfloat observedNamedClearDepth = 0; + GLint observedNamedClearStencil = 0; + Bool observedNamedClearRecord = false; + + void ObserveNamedClear(GLenum target, GLint index) { + const auto& state = MGPipeApplier(); + observedNamedClear = state.BoundFramebuffer[0]; + observedNamedClearRead = state.BoundFramebuffer[1]; + observedNamedClearRecord = state.FramebufferRecordFor(observedNamedClear) != nullptr; + observedNamedClearTarget = target; + observedNamedClearIndex = index; + ++observedNamedClearCalls; + } + + void CheckNamedClearScopedTarget(Uint8 kind, Uint8 valueClass) { + MGPipeApplierReset(); + MGPipeResourceOps resources{}; + MGPipeSetResourceOps(&resources); + MGPFramebufferState state{}; + state.Fbo = {31, 1}; + state.Target = static_cast(MGPipeFramebufferTarget::Draw); + MGPipeApplySetFramebufferState(state); + const auto originalDraw = state.Fbo; + state.Fbo = {32, 2}; + state.Target = static_cast(MGPipeFramebufferTarget::Read); + MGPipeApplySetFramebufferState(state); + const auto originalRead = state.Fbo; + state.Fbo = {33, 3}; + state.Target = static_cast(MGPipeFramebufferTarget::Named); + MGPipeApplySetFramebufferState(state); CapsPeer backend; + backend.table.GL.ClearBufferfv = +[](GLenum target, GLint index, const GLfloat* values) { + ObserveNamedClear(target, index); + std::memcpy(observedNamedClearValues, values, sizeof(observedNamedClearValues)); + }; + backend.table.GL.ClearBufferiv = +[](GLenum target, GLint index, const GLint* values) { + ObserveNamedClear(target, index); + std::memcpy(observedNamedClearValues, values, sizeof(observedNamedClearValues)); + }; + backend.table.GL.ClearBufferuiv = +[](GLenum target, GLint index, const GLuint* values) { + ObserveNamedClear(target, index); + std::memcpy(observedNamedClearValues, values, sizeof(observedNamedClearValues)); + }; + backend.table.GL.ClearBufferfi = +[](GLenum target, GLint index, GLfloat depth, GLint stencil) { + ObserveNamedClear(target, index); + observedNamedClearDepth = depth; + observedNamedClearStencil = stencil; + }; Srv::ServerVerbSink sink; sink.SetBackend(&backend); - MGPClear r{}; - r.Fbo = {701, 1}; - r.Kind = kMGPipeClearKindColor; - r.ValueClass = kMGPipeClearValueClassFloat; - sink.OnClear(r); - }); - ExpectNamedAbort(child, "Fatal{UnmigratedVerb, \"ClearNamedFramebufferfv+UNBOUND\"}"); + MGPClear clear{}; + clear.Fbo = state.Fbo; + clear.Kind = kind; + clear.ValueClass = valueClass; + clear.DrawBufferIndex = kind == kMGPipeClearKindDepthStencil ? 0 : 3; + const Uint32 bits[4]{0x3e800000u, 0x3f000000u, 0xff000011u, 0x3f800000u}; + std::memcpy(clear.ColorValue, bits, sizeof(bits)); + clear.DepthValue = 0.375f; + clear.StencilValue = 91; + if (!sink.OnClear(clear) || observedNamedClearCalls != 1) ::_exit(101); + if (observedNamedClear != clear.Fbo || !observedNamedClearRecord || + observedNamedClearRead != originalRead) ::_exit(102); + if (MGPipeApplier().BoundFramebuffer[0] != originalDraw || + MGPipeApplier().BoundFramebuffer[1] != originalRead) ::_exit(103); + if (observedNamedClearIndex != clear.DrawBufferIndex) ::_exit(104); + if (kind == kMGPipeClearKindDepthStencil) { + if (observedNamedClearTarget != GL_DEPTH_STENCIL || observedNamedClearDepth != clear.DepthValue || + observedNamedClearStencil != clear.StencilValue) ::_exit(105); + } else if (observedNamedClearTarget != GL_COLOR || + std::memcmp(observedNamedClearValues, clear.ColorValue, sizeof(bits))) ::_exit(106); + MGPipeSetResourceOps(nullptr); + sink.SetBackend(nullptr); + } +} + +TEST(RemoteF1, UnboundNamedfvRefusesByName) { + // Historical name retained; a named target now reaches the native clear hook. + const auto child = RunInChild([] { CheckNamedClearScopedTarget(kMGPipeClearKindColor, kMGPipeClearValueClassFloat); }); + ExpectChildSuccess(child); } TEST(RemoteF1, UnboundNamedivRefusesByName) { - // Red once (executed, reverted): disable the named-FBO refusal; its exact Fatal disappears. - const auto child = RunInChild([] { - CapsPeer backend; - Srv::ServerVerbSink sink; - sink.SetBackend(&backend); - MGPClear r{}; - r.Fbo = {701, 1}; - r.Kind = kMGPipeClearKindColor; - r.ValueClass = kMGPipeClearValueClassInt; - sink.OnClear(r); - }); - ExpectNamedAbort(child, "Fatal{UnmigratedVerb, \"ClearNamedFramebufferiv+UNBOUND\"}"); + const auto child = RunInChild([] { CheckNamedClearScopedTarget(kMGPipeClearKindColor, kMGPipeClearValueClassInt); }); + ExpectChildSuccess(child); } TEST(RemoteF1, UnboundNameduivRefusesByName) { - // Red once (executed, reverted): disable the named-FBO refusal; its exact Fatal disappears. - const auto child = RunInChild([] { - CapsPeer backend; - Srv::ServerVerbSink sink; - sink.SetBackend(&backend); - MGPClear r{}; - r.Fbo = {701, 1}; - r.Kind = kMGPipeClearKindColor; - r.ValueClass = kMGPipeClearValueClassUint; - sink.OnClear(r); - }); - ExpectNamedAbort(child, "Fatal{UnmigratedVerb, \"ClearNamedFramebufferuiv+UNBOUND\"}"); + const auto child = RunInChild([] { CheckNamedClearScopedTarget(kMGPipeClearKindColor, kMGPipeClearValueClassUint); }); + ExpectChildSuccess(child); } TEST(RemoteF1, UnboundNamedfiRefusesByName) { - // Red once (executed, reverted): disable the named-FBO refusal; its exact Fatal disappears. - const auto child = RunInChild([] { - CapsPeer backend; - Srv::ServerVerbSink sink; - sink.SetBackend(&backend); - MGPClear r{}; - r.Fbo = {701, 1}; - r.Kind = kMGPipeClearKindDepthStencil; - r.ValueClass = kMGPipeClearValueClassFloat; - sink.OnClear(r); - }); - ExpectNamedAbort(child, "Fatal{UnmigratedVerb, \"ClearNamedFramebufferfi+UNBOUND\"}"); + const auto child = RunInChild([] { CheckNamedClearScopedTarget(kMGPipeClearKindDepthStencil, kMGPipeClearValueClassFloat); }); + ExpectChildSuccess(child); } #endif