mirror of
https://github.com/MobileGL-Dev/MobileGL
synced 2026-11-15 14:03:23 +09:00
[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:
@@ -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) {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user