[Fix] (Remote): glFinish waits for the GPU, Magma bounds unsubmitted clear objects, ES contexts advertise OES_mapbuffer/rgb8_rgba8/depth24

This commit is contained in:
2026-10-04 04:54:53 -04:00
parent 4722fa38bb
commit 66b47a101b
6 changed files with 54 additions and 7 deletions
@@ -233,6 +233,13 @@
void VulkanRenderer::ClearWireFramebuffer(const MG_Pipe::MGPFramebufferState& fbo,
const ClearAttachmentPayload& payload, GLint drawbuffer) {
RetireWireDrawPass();
// A stream that neither presents nor finishes (an off-screen loop) never submits, so its
// recording and the per-clear objects retired below would grow until host memory runs
// out. Past a bound, submit what is recorded: the objects then retire with that work.
constexpr SizeT kRetiredObjectsBeforeSubmit = 1024;
if (m_wireRetiredObjects.size() >= kRetiredObjectsBeforeSubmit && m_wirePreparationDepth == 0 &&
HasPendingRecordedWork())
(void)FlushPendingCommands();
if (MGB_CTX->IsCapabilityEnabled(CapabilityInput::RasterizerDiscard)) return;
const auto clearSurface = [&](const MG_Pipe::MGPSurface& surface, VkImageAspectFlags aspect,
Uint32 colorIndex) {
+21 -6
View File
@@ -610,7 +610,7 @@ namespace MobileGL::MG_Impl::GLImpl {
// GL_OES_EGL_image_external(_essl3) only where the server samples YUV images: an
// external texture is a 2D texture holding the image converted to RGBA, which needs
// the server to hold the YUV image in the first place (EGLImpl::SharedImageYuvAvailable).
static const auto build = [](Bool images, Bool external, Bool es) {
static const auto build = [](Bool images, Bool external, Bool es, Bool esApi) {
Vector<const char*> list;
if (images) list.push_back("GL_OES_EGL_image");
if (images && external) {
@@ -627,15 +627,30 @@ namespace MobileGL::MG_Impl::GLImpl {
list.push_back("GL_EXT_texture_format_BGRA8888");
list.push_back("GL_EXT_read_format_bgra");
}
// Any context created for the OpenGL ES API, whatever it calls itself: the ES 2.0
// names of what desktop GL has in core - RGB8/RGBA8 and DEPTH_COMPONENT24
// renderbuffers, and glMapBuffer (exported under its OES names as well).
if (esApi) {
list.push_back("GL_OES_rgb8_rgba8");
list.push_back("GL_OES_depth24");
list.push_back("GL_OES_mapbuffer");
}
return list;
};
static const Vector<const char*> lists[8] = {
build(false, false, false), build(false, false, true), build(false, true, false),
build(false, true, true), build(true, false, false), build(true, false, true),
build(true, true, false), build(true, true, true)};
static const Vector<const char*> lists[16] = {
build(false, false, false, false), build(false, false, false, true),
build(false, false, true, false), build(false, false, true, true),
build(false, true, false, false), build(false, true, false, true),
build(false, true, true, false), build(false, true, true, true),
build(true, false, false, false), build(true, false, false, true),
build(true, false, true, false), build(true, false, true, true),
build(true, true, false, false), build(true, true, false, true),
build(true, true, true, false), build(true, true, true, true)};
const Bool images = MG_Impl::EGLImpl::SharedImagesAvailable();
const Bool external = images && MG_Impl::EGLImpl::SharedImageYuvAvailable();
return lists[(images ? 4 : 0) + (external ? 2 : 0) + (EsIdentityActive() ? 1 : 0)];
const Bool esApi = MG_State::pEGLContext &&
MG_State::pEGLContext->GetCurrentContextClientAPI() == EGL_OPENGL_ES_API;
return lists[(images ? 8 : 0) + (external ? 4 : 0) + (EsIdentityActive() ? 2 : 0) + (esApi ? 1 : 0)];
}
// Robust buffer access as GL 4.5 / KHR_robust_buffer_access_behavior define it: the backend
+4
View File
@@ -246,6 +246,10 @@ namespace MobileGL::MG_Impl {
GETPROC(glBufferSubData, name);
GETPROC(glGetBufferSubData, name);
GETPROC(glMapBuffer, name);
// GL_OES_mapbuffer (ES contexts): the same three calls under their OES names.
if (strcmp(name, "glMapBufferOES") == 0) return (void*)glMapBuffer;
if (strcmp(name, "glUnmapBufferOES") == 0) return (void*)glUnmapBuffer;
if (strcmp(name, "glGetBufferPointervOES") == 0) return (void*)glGetBufferPointerv;
GETPROC(glUnmapBuffer, name);
GETPROC(glGetBufferParameteriv, name);
GETPROC(glGetBufferPointerv, name);
+3 -1
View File
@@ -2551,8 +2551,10 @@ namespace MobileGL::MG_Remote::Client {
// glFinish. Nothing goes on the wire - Flush and Finish have no record (ARCHITECTURE
// §11) - and under lockstep there was nothing to do, because the client had already
// waited out every command it issued. Under run-ahead "the commands issued so far have
// completed" is a promise the queue can break, so this is where it is kept.
// completed" is a promise the queue can break, so this is where it is kept - and
// "completed" includes the GPU, so a fence round trip follows the apply.
WaitForApplyToCatchUp("glFinish");
EmitFinishWait();
}
Uint64 ClientSession::AcquirePresentCredit() {
+17
View File
@@ -2085,6 +2085,23 @@ namespace MobileGL::MG_Remote::Client {
} // namespace
// glFinish's GPU half: a server fence created after everything the client issued, waited
// with GL_SYNC_FLUSH_COMMANDS_BIT, then destroyed. Waiting for the apply thread alone left
// the GPU queue unbounded behind a "finished" glFinish.
void EmitFinishWait() {
ClientSession* session = ClientSession::Active();
if (session == nullptr || !session->Started() || session->DeviceLost()) return;
const std::lock_guard<std::mutex> lock(g_fenceMutex);
const MG_Pipe::MGPipeHandle handle = MG_Pipe::MGPipeSlots().Allocate(MG_Pipe::MGPipeKind::Fence);
const MG_Pipe::MGPHandleOnly desc{handle, static_cast<Uint32>(MG_Pipe::MGPipeKind::Fence), 0};
session->EmitAndWait(MG_Pipe::MGPWireOp::FenceCreate, &desc, sizeof(desc), nullptr, 0, nullptr, 0, nullptr);
constexpr GLuint64 kFinishTimeoutNs = 10ull * 1000 * 1000 * 1000;
const MG_Pipe::MGPFenceWait request{handle, kFinishTimeoutNs, GL_SYNC_FLUSH_COMMANDS_BIT, 0};
(void)ReadFenceReply(*session, MG_Pipe::MGPWireOp::FenceWait, &request, sizeof(request));
session->EmitAndWait(MG_Pipe::MGPWireOp::FenceDestroy, &desc, sizeof(desc), nullptr, 0, nullptr, 0, nullptr);
MG_Pipe::MGPipeSlots().Free(MG_Pipe::MGPipeKind::Fence, handle);
}
void NoteFenceSignaledByServer(MG_Pipe::MGPipeHandle fence) {
RememberFenceSignaled(fence);
g_fenceServerReports.fetch_add(1, std::memory_order_relaxed);
+2
View File
@@ -291,6 +291,8 @@ namespace MobileGL::MG_Remote::Client {
//
// The event drain's half: the server reported `fence` signaled (kEventFenceSignaled). Every
// later glClientWaitSync / glGetSynciv(GL_SYNC_STATUS) on it answers without a round trip.
// glFinish's wait for the GPU (a server fence created, waited and destroyed).
void EmitFinishWait();
void NoteFenceSignaledByServer(MG_Pipe::MGPipeHandle fence);
// What the fence polls cost, for the lanes. `LocalAnswers` = polls answered with no record