mirror of
https://github.com/MobileGL-Dev/MobileGL
synced 2026-11-11 12:03:24 +09:00
[Feature] (disaggregated): the default framebuffer on a host-framed surface is the host's frame
Three things this path needs that the server-owned one never did, each because the pixels are not in an EGL surface this time: - The client's DEFAULT framebuffer has to resolve to the frame. Four places bind the driver's 0 for the default framebuffer; they now ask g_hostFrameFramebufferId, which is 0 everywhere except while a host-framed surface is current, where it is the frame's framebuffer. Without this every draw would land in the pbuffer the context was made with - a surface nobody ever sees. - eglMakeCurrent binds the surface's own default framebuffer behind the engine's binding shadow, so the frame has to be put back after every make-current, with the shadow invalidated first: a bind the shadow thinks already happened never reaches the driver. - The bind itself goes through FramebufferImpl::BindFramebufferId, not a raw glBindFramebuffer, for the same reason - a raw bind leaves the shadow claiming the previous framebuffer. Frame lifecycle is otherwise unchanged: the frame becomes the draw target when it arrives, and is dropped after the host has been answered and has presented it. Compiles and links (libMobileGL.so, libMobileGLServer.so, exit 0), deployed to the device, and the container's client is rebuilding against it. The run that shows Plasma through it is next.
This commit is contained in:
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -8,6 +8,8 @@
|
||||
|
||||
#pragma once
|
||||
#include <Includes.h>
|
||||
#include "HostFrameTarget.h"
|
||||
#include "MG_Backend/HostFrameBridge.h"
|
||||
#include "../BackendObject.h"
|
||||
#include <MG_Util/BackendLoaders/OpenGL/Loader.h>
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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) {
|
||||
|
||||
Reference in New Issue
Block a user