diff --git a/MobileGL/MG_Backend/DirectGLES/BackendObject_DirectGLES.cpp b/MobileGL/MG_Backend/DirectGLES/BackendObject_DirectGLES.cpp index a980ed35..d504c4e7 100644 --- a/MobileGL/MG_Backend/DirectGLES/BackendObject_DirectGLES.cpp +++ b/MobileGL/MG_Backend/DirectGLES/BackendObject_DirectGLES.cpp @@ -1028,6 +1028,106 @@ namespace MobileGL::MG_Backend::DirectGLES { #endif } + #if MOBILEGL_BUILD_DISAGGREGATED && defined(__ANDROID__) + namespace { + // The host may still be starting when the client asks for its surface: it is a separate + // app that has to come up and allocate its pool. Waiting is better than refusing a + // surface a client is entitled to, and the wait is answered by name if it expires. + constexpr Int kHostFrameWaitMs = 30000; + } + + Bool BackendObject_DirectGLES::InitHostFrameSurface(EGLint width, EGLint height) { + // The EGL context comes from the same place a pbuffer session's does. What changes is not + // how this side draws but what it draws INTO: a frame the display host allocated, bound as + // this context's framebuffer. The pbuffer stays as the draw target for the moment before + // the first frame arrives, which is the only moment it is used. + if (!DirectGLES::InitPbufferSurface(width, height)) { + MGLOG_E("InitHostFrameSurface: the context the host frames are drawn in did not come up"); + return false; + } + if (!m_hostFrameBridgeOpen) { + String why; + if (!m_hostFrameBridge.Open(MG_Backend::kHostFrameSocket, kHostFrameWaitMs, why)) { + MGLOG_E("InitHostFrameSurface: no display host: %s", why.c_str()); + return false; + } + m_hostFrameBridgeOpen = true; + } + return TakeHostFrame(); + } + + Bool BackendObject_DirectGLES::TakeHostFrame() { + String why; + struct AHardwareBuffer* frame = nullptr; + MG_Backend::HostFrameOffer offer{}; + if (!m_hostFrameBridge.Acquire(&frame, offer, why)) { + MGLOG_E("TakeHostFrame: %s", why.c_str()); + return false; + } + HostFrameTarget target; + if (!HostFrameTargetCreate(frame, target, why)) { + MGLOG_E("TakeHostFrame: the host frame %ux%u did not become a draw target: %s", offer.Width, + offer.Height, why.c_str()); + AHardwareBuffer_release(frame); + return false; + } + DestroyHostFrame(); + m_hostFrameTarget = target; + m_hostFrameBuffer = frame; + m_hostFrameOffer = offer; + // BEFORE the bind: every default-framebuffer bind the engine makes from here on has to + // resolve to this frame, and that resolution is what this global is. + g_hostFrameFramebufferId = m_hostFrameTarget.Framebuffer; + FramebufferImpl::InvalidateFramebufferBindingCache(); + HostFrameTargetBind(m_hostFrameTarget); + MGLOG_I("host frame %u is the draw target: %ux%u", offer.Index, target.Width, target.Height); + return true; + } + + void BackendObject_DirectGLES::DestroyHostFrame() { + g_hostFrameFramebufferId = 0; + HostFrameTargetDestroy(m_hostFrameTarget); + if (m_hostFrameBuffer != nullptr) { + AHardwareBuffer_release(m_hostFrameBuffer); + m_hostFrameBuffer = nullptr; + } + } + + Bool BackendObject_DirectGLES::PresentHostFrame() { + // When this returns, the pixels are complete: glFinish is the fence the host is told + // about, and the host's own readback - which Complete reads - is what proves the drawing + // landed where the screen will look. + if (g_GLESFuncs.glFinish != nullptr) g_GLESFuncs.glFinish(); + String why; + if (!m_hostFrameBridge.Complete(m_hostFrameOffer, /*imported=*/true, /*drawn=*/true, /*fenceOk=*/true, why)) { + MGLOG_E("PresentHostFrame: %s", why.c_str()); + return false; + } + // The answer has been read, so the host has already put this frame on the glass: it is + // this side's to drop, and the next one becomes the drawing target. + DestroyHostFrame(); + return TakeHostFrame(); + } + +#else // !(MOBILEGL_BUILD_DISAGGREGATED && __ANDROID__) + + Bool BackendObject_DirectGLES::InitHostFrameSurface(EGLint width, EGLint height) { + (void)width; + (void)height; + MGLOG_E("InitHostFrameSurface: host frames are a disaggregated Android path"); + return false; + } + + Bool BackendObject_DirectGLES::TakeHostFrame() { return false; } + void BackendObject_DirectGLES::DestroyHostFrame() {} + + Bool BackendObject_DirectGLES::PresentHostFrame() { + MGLOG_E("PresentHostFrame: host frames are a disaggregated Android path"); + return false; + } + +#endif + Bool BackendObject_DirectGLES::InitPbufferSurface(EGLint width, EGLint height) { return DirectGLES::InitPbufferSurface(width, height); } @@ -1107,6 +1207,15 @@ namespace MobileGL::MG_Backend::DirectGLES { #endif return false; } + // A HOST-FRAMED SURFACE HAS TO BE PUT BACK AFTER EVERY MAKE-CURRENT. The native call + // binds the surface's own default framebuffer - the pbuffer this path makes its context + // with - and that is not where these pixels go. Both the driver and the engine's binding + // shadow have to be told again, or the next default-framebuffer bind is deduped away and + // every draw lands in a pbuffer nobody sees. + if (m_eglSurfaceKind == SurfaceKind::HostFrame && m_hostFrameTarget.Framebuffer != 0) { + FramebufferImpl::InvalidateFramebufferBindingCache(); + HostFrameTargetBind(m_hostFrameTarget); + } return true; } diff --git a/MobileGL/MG_Backend/DirectGLES/BackendObject_DirectGLES.h b/MobileGL/MG_Backend/DirectGLES/BackendObject_DirectGLES.h index 8f3fd0c6..5c91315d 100644 --- a/MobileGL/MG_Backend/DirectGLES/BackendObject_DirectGLES.h +++ b/MobileGL/MG_Backend/DirectGLES/BackendObject_DirectGLES.h @@ -8,6 +8,8 @@ #pragma once #include +#include "HostFrameTarget.h" +#include "MG_Backend/HostFrameBridge.h" #include "../BackendObject.h" #include @@ -56,8 +58,27 @@ namespace MobileGL::MG_Backend::DirectGLES { private: void UpdateDynamicBackendParameters(); Bool InitPbufferSurface(EGLint width, EGLint height) override; + // A host-framed surface draws into a frame the display host owns; presenting one + // tells the host the frame is drawn, because the host is what puts it on the glass. + // NOT CreateEGLHostFrameSurface: the base class registers the surface and activates it, + // and activation is what reaches InitHostFrameSurface below. There is nothing about the + // EGL side of a host-framed surface this backend needs to do its own way. + Bool InitHostFrameSurface(EGLint width, EGLint height) override; + Bool PresentHostFrame() override; + // Takes the next frame from the host and makes it this context's framebuffer. + Bool TakeHostFrame(); + void DestroyHostFrame(); void OnEGLSurfaceReleased(EGLSurface surface) override; + // MOBILEGL_IPC_SURFACE=host: the frames the display host owns, one at a time, which + // is what the host offers - it hands one over and waits for the answer before it + // presents it and offers the next. + MG_Backend::HostFrameBridge m_hostFrameBridge; + HostFrameTarget m_hostFrameTarget; + struct AHardwareBuffer* m_hostFrameBuffer = nullptr; + MG_Backend::HostFrameOffer m_hostFrameOffer{}; + Bool m_hostFrameBridgeOpen = false; + Bool m_initialized = false; MG_External::EGLFunctionsTable m_EGLFunctions; MG_External::GLESFunctionsTable m_GLESFunctions; diff --git a/MobileGL/MG_Backend/DirectGLES/DirectGLES.cpp b/MobileGL/MG_Backend/DirectGLES/DirectGLES.cpp index 0e93d4b3..51a8387e 100644 --- a/MobileGL/MG_Backend/DirectGLES/DirectGLES.cpp +++ b/MobileGL/MG_Backend/DirectGLES/DirectGLES.cpp @@ -87,6 +87,7 @@ namespace MobileGL::MG_Backend::DirectGLES { #endif MG_External::EGLFunctionsTable g_EGLFuncs; + Uint g_hostFrameFramebufferId = 0; MG_External::GLESFunctionsTable g_GLESFuncs; MG_External::GLESCapabilities g_GLESCapabilities; @@ -6515,7 +6516,7 @@ namespace MobileGL::MG_Backend::DirectGLES { // Through the shadow: a raw bind here would leave the shadow claiming // the previous user FBO, false-skipping its next re-bind. FramebufferImpl::BindFramebufferId( - target == FramebufferTarget::Draw ? GL_DRAW_FRAMEBUFFER : GL_READ_FRAMEBUFFER, 0); + target == FramebufferTarget::Draw ? GL_DRAW_FRAMEBUFFER : GL_READ_FRAMEBUFFER, g_hostFrameFramebufferId); } return; } @@ -6584,7 +6585,7 @@ namespace MobileGL::MG_Backend::DirectGLES { // Through the shadow: a raw bind here would leave the shadow claiming // the previous user FBO, false-skipping its next re-bind. FramebufferImpl::BindFramebufferId( - target == FramebufferTarget::Draw ? GL_DRAW_FRAMEBUFFER : GL_READ_FRAMEBUFFER, 0); + target == FramebufferTarget::Draw ? GL_DRAW_FRAMEBUFFER : GL_READ_FRAMEBUFFER, g_hostFrameFramebufferId); } } @@ -6604,7 +6605,7 @@ namespace MobileGL::MG_Backend::DirectGLES { FramebufferImpl::g_integerColorDrawBufferMask = 0; } FramebufferImpl::BindFramebufferId( - target == FramebufferTarget::Draw ? GL_DRAW_FRAMEBUFFER : GL_READ_FRAMEBUFFER, 0); + target == FramebufferTarget::Draw ? GL_DRAW_FRAMEBUFFER : GL_READ_FRAMEBUFFER, g_hostFrameFramebufferId); return; } @@ -6645,7 +6646,7 @@ namespace MobileGL::MG_Backend::DirectGLES { FramebufferImpl::g_integerColorDrawBufferMask = 0; } FramebufferImpl::BindFramebufferId( - target == FramebufferTarget::Draw ? GL_DRAW_FRAMEBUFFER : GL_READ_FRAMEBUFFER, 0); + target == FramebufferTarget::Draw ? GL_DRAW_FRAMEBUFFER : GL_READ_FRAMEBUFFER, g_hostFrameFramebufferId); return; } diff --git a/MobileGL/MG_Backend/DirectGLES/DirectGLES.h b/MobileGL/MG_Backend/DirectGLES/DirectGLES.h index b6d59252..43aea248 100644 --- a/MobileGL/MG_Backend/DirectGLES/DirectGLES.h +++ b/MobileGL/MG_Backend/DirectGLES/DirectGLES.h @@ -249,6 +249,11 @@ namespace MobileGL::MG_Backend::DirectGLES { void InvalidateSyncedRenderState(); } // namespace RenderStateImpl + // MOBILEGL_IPC_SURFACE=host: what the client's DEFAULT framebuffer means while a host-framed + // surface is current. Zero everywhere else, because the driver's own 0 IS the EGL surface + // then - but on a host-framed surface the pixels belong to a frame the display host owns, + // and drawing into the driver's 0 would draw into a pbuffer nobody ever sees. + extern Uint g_hostFrameFramebufferId; extern MG_External::EGLFunctionsTable g_EGLFuncs; extern MG_External::GLESFunctionsTable g_GLESFuncs; extern MG_External::GLESCapabilities g_GLESCapabilities; diff --git a/MobileGL/MG_Backend/DirectGLES/HostFrameTarget.cpp b/MobileGL/MG_Backend/DirectGLES/HostFrameTarget.cpp index 0d1bf4c6..bc00406f 100644 --- a/MobileGL/MG_Backend/DirectGLES/HostFrameTarget.cpp +++ b/MobileGL/MG_Backend/DirectGLES/HostFrameTarget.cpp @@ -8,6 +8,7 @@ #include "HostFrameTarget.h" #include "DirectGLES.h" +#include "Managers.h" #if MOBILEGL_BUILD_DISAGGREGATED && defined(__ANDROID__) @@ -168,8 +169,10 @@ namespace MobileGL::MG_Backend::DirectGLES { } void HostFrameTargetBind(const HostFrameTarget& target) { - if (target.Framebuffer != 0 && g_GLESFuncs.glBindFramebuffer != nullptr) - g_GLESFuncs.glBindFramebuffer(GL_FRAMEBUFFER, target.Framebuffer); + if (target.Framebuffer == 0) return; + // THROUGH THE ENGINE'S SHADOW, never raw: a raw bind would leave the shadow claiming + // the previous framebuffer and false-skip the next re-bind (FramebufferImpl, Managers.h). + FramebufferImpl::BindFramebufferId(GL_FRAMEBUFFER, target.Framebuffer); } void HostFrameTargetDestroy(HostFrameTarget& target) {