mirror of
https://github.com/MobileGL-Dev/MobileGL
synced 2026-11-07 18:13:23 +09:00
Breaks the one process = one server = one session = one context equation
that the whole stack's correctness used to rest on. Design and per-slice
shape: docs/Disaggregated/design/11-state-ownership.md.
S1 wire: bind_context ring record (opcode 84) announces the current
context at the make-current edge; CreateContext/DestroyContext join the
SurfaceOp control frames; control revision 5 -> 6; the server keeps a
per-session token table and refuses unknown tokens by name.
S2 server: SessionRuntime per session with thread-scoped resolution
(singletons become fallbacks), per-thread gPipeInputs, per-session latch
domains, per-thread segment resolvers; the in-process unix supervisor
serves up to MOBILEGL_IPC_INPROC_MAX_SESSIONS (16) concurrent sessions;
surface mode is declared per session and the display lease refuses a
second on-screen session by name.
S3 frontend: ShareGroupState owns the shared object stores and name
generators (buffer/texture/renderbuffer/sampler/program), GLContext keeps
per-context bindings/errors/container objects; pGLContext becomes
thread-local with a process default context preserving the single-context
world verbatim; eglCreateContext builds a real GLContext and joins the
sharing context's group.
S4 backend: native EGL tuples keyed by (session, context token);
InitDisplayAndContext's unconditional teardown is gone - creating a
second context or surface no longer destroys the first; eglCreateContext
sharing is wired to the native driver; bind_context is the native switch
point.
S5 applier: MGPipeApplierState splits into MGPipeObjectRecords (per
share group) and MGPipeWorkingState (per context), resolved through a
{session, token} registry with a thread-local cached fast path.
Tests: MultiSessionTest (incl. two sessions x two contexts interleaved
with real clear/readback witnesses), ApplierOwnershipTest (with a
null-resolver negative control), ContextStateOwnershipTest,
ServerLoopEglTest native-context liveness; 99/99 of the acceptance set
green on the host build.
Known debts (documented in the design doc): twin registries still keyed
by handle alone, no per-record ContextSlot, process-global backend
generations, MakeCurrent dedup per current binding.
324 lines
18 KiB
C++
324 lines
18 KiB
C++
// MobileGL - MobileGL/MG_State/EGLState/Core.h
|
|
// Copyright (c) 2025-2026 MobileGL-Dev
|
|
// Licensed under the GNU Lesser General Public License v3.0:
|
|
// https://www.gnu.org/licenses/gpl-3.0.txt
|
|
// https://www.gnu.org/licenses/lgpl-3.0.txt
|
|
// SPDX-License-Identifier: LGPL-3.0-only
|
|
// End of Source File Header
|
|
|
|
#pragma once
|
|
#include <Includes.h>
|
|
#include <type_traits>
|
|
#include <MG_State/GLState/Core.h>
|
|
|
|
namespace MobileGL {
|
|
namespace MG_State {
|
|
namespace EGLState {
|
|
class EGLContext {
|
|
public:
|
|
using EGLDisplayHandle = ::EGLDisplay;
|
|
using EGLConfigHandle = ::EGLConfig;
|
|
using EGLSurfaceHandle = ::EGLSurface;
|
|
using EGLContextHandle = ::EGLContext;
|
|
using EGLSyncHandle = ::EGLSync;
|
|
using EGLImageHandle = ::EGLImage;
|
|
|
|
EGLContext() = default;
|
|
|
|
// Error
|
|
void SetError(EGLint errorCode);
|
|
EGLint ConsumeError();
|
|
|
|
// API
|
|
void SetBoundAPI(EGLenum api);
|
|
EGLenum GetBoundAPI() const;
|
|
|
|
// Display
|
|
EGLDisplayHandle GetDisplay(NativeDisplayType nativeDisplay);
|
|
EGLDisplayHandle GetPlatformDisplay(EGLenum platform, void* nativeDisplay);
|
|
Bool ValidateDisplay(EGLDisplayHandle display) const;
|
|
Bool IsDisplayInitialized(EGLDisplayHandle display) const;
|
|
// The native display a display was made for and the platform it was asked for on
|
|
// (EGL_NONE for eglGetDisplay): what decides how its window surfaces are presented.
|
|
Bool GetDisplayNative(EGLDisplayHandle display, Uint64* nativeDisplayKey, EGLenum* platform) const;
|
|
Bool InitializeDisplay(EGLDisplayHandle display, EGLint* major, EGLint* minor);
|
|
Bool TerminateDisplay(EGLDisplayHandle display);
|
|
// Whole-library idle checks used by EGLImpl::Terminate to decide
|
|
// when the last eglTerminate may tear MobileGL down entirely.
|
|
Bool HasAnyInitializedDisplay() const;
|
|
Bool HasAnyCurrentContext() const;
|
|
|
|
// Config
|
|
Bool ChooseConfig(EGLDisplayHandle display, const EGLint* attribList, EGLConfigHandle* configs,
|
|
EGLint configSize, EGLint* numConfig);
|
|
Bool GetConfigs(EGLDisplayHandle display, EGLConfigHandle* configs, EGLint configSize,
|
|
EGLint* numConfig);
|
|
Bool ValidateConfig(EGLConfigHandle config) const;
|
|
Bool ValidateConfigOnDisplay(EGLDisplayHandle display, EGLConfigHandle config) const;
|
|
Bool GetConfigAttrib(EGLDisplayHandle display, EGLConfigHandle config, EGLint attribute,
|
|
EGLint* value) const;
|
|
|
|
// Context
|
|
EGLContextHandle CreateContext(EGLDisplayHandle display, EGLConfigHandle config,
|
|
EGLContextHandle shareCtx, const EGLint* attribList);
|
|
Bool DestroyContext(EGLDisplayHandle display, EGLContextHandle context);
|
|
Bool QueryContext(EGLDisplayHandle display, EGLContextHandle context, EGLint attribute,
|
|
EGLint* value) const;
|
|
Bool ValidateContext(EGLContextHandle context) const;
|
|
Bool ValidateContextOnDisplay(EGLDisplayHandle display, EGLContextHandle context) const;
|
|
Bool IsCurrentContextOpenGLCoreProfile() const;
|
|
Bool IsCurrentContextOpenGLCompatibilityProfile() const;
|
|
EGLint GetCurrentContextFlags() const;
|
|
|
|
// P14 S1. The wire identity of a context, and of the context the CALLING THREAD
|
|
// has current (0 when it has none - the release edge's value, and the value a
|
|
// session that never crossed a bind_context answers with). Read by MG_Impl's two
|
|
// producers: EGLImpl's CreateContext/DestroyContext control frames and PipeFill's
|
|
// bind_context emission at the make-current edge.
|
|
Uint64 GetContextClientToken(EGLContextHandle context) const;
|
|
Uint64 GetContextShareGroupToken(EGLContextHandle context) const;
|
|
Uint64 CurrentContextClientToken() const;
|
|
|
|
// P14 S3. The frontend GL state this context owns, so a caller outside MG_State
|
|
// can reach the other half of a context it just made current. Null for an
|
|
// unknown handle.
|
|
SharedPtr<GLState::GLContext> GetContextGLState(EGLContextHandle context) const;
|
|
|
|
// Surface
|
|
EGLSurfaceHandle CreateWindowSurface(EGLDisplayHandle display, EGLConfigHandle config,
|
|
NativeWindowType window, const EGLint* attribList);
|
|
EGLSurfaceHandle CreatePbufferSurface(EGLDisplayHandle display, EGLConfigHandle config,
|
|
const EGLint* attribList);
|
|
EGLSurfaceHandle CreatePixmapSurface(EGLDisplayHandle display, EGLConfigHandle config,
|
|
EGLNativePixmapType pixmap, const EGLint* attribList);
|
|
EGLSurfaceHandle CreatePbufferFromClientBuffer(EGLDisplayHandle display, EGLenum bufferType,
|
|
EGLClientBuffer buffer, EGLConfigHandle config,
|
|
const EGLint* attribList);
|
|
EGLSurfaceHandle CreatePlatformWindowSurface(EGLDisplayHandle display, EGLConfigHandle config,
|
|
void* nativeWindow, const EGLAttrib* attribList);
|
|
EGLSurfaceHandle CreatePlatformPixmapSurface(EGLDisplayHandle display, EGLConfigHandle config,
|
|
void* nativePixmap, const EGLAttrib* attribList);
|
|
Bool DestroySurface(EGLDisplayHandle display, EGLSurfaceHandle surface);
|
|
Bool ResizeSurface(EGLDisplayHandle display, EGLSurfaceHandle surface, EGLint width, EGLint height);
|
|
Bool QuerySurface(EGLDisplayHandle display, EGLSurfaceHandle surface, EGLint attribute,
|
|
EGLint* value) const;
|
|
Bool ValidateSurface(EGLSurfaceHandle surface) const;
|
|
Bool ValidateSurfaceOnDisplay(EGLDisplayHandle display, EGLSurfaceHandle surface) const;
|
|
Bool SwapInterval(EGLDisplayHandle display, EGLint interval);
|
|
#if MOBILEGL_BUILD_DISAGGREGATED
|
|
// P12 (on-screen server window), MOBILEGL_IPC_SURFACE=server. A window surface whose
|
|
// window is the SERVER's: the client's native window may be NULL (a headless client
|
|
// has none), and the extent it asked for (EGL_WIDTH/EGL_HEIGHT; 0/0 = the server
|
|
// window's own) stands until the server's real one arrives through SetSurfaceExtent.
|
|
// `platformWindow` keeps eglCreatePlatformWindowSurface's surface type. Split builds
|
|
// only: the pull build's state class is unchanged (G1).
|
|
EGLSurfaceHandle CreateServerOwnedWindowSurface(EGLDisplayHandle display, EGLConfigHandle config,
|
|
const void* nativeWindow, EGLint width,
|
|
EGLint height, Bool platformWindow);
|
|
// P12: the server's REAL extent of a surface - a server-owned window's geometry, from
|
|
// the CreateWindowSurface reply and from every surface-changed event after it - so
|
|
// eglQuerySurface(EGL_WIDTH/EGL_HEIGHT) answers what the server renders at. Unlike
|
|
// ResizeSurface it needs no display (the event carries none) and keeps a 0 as 0.
|
|
Bool SetSurfaceExtent(EGLSurfaceHandle surface, EGLint width, EGLint height);
|
|
#endif
|
|
|
|
// Current
|
|
Bool MakeCurrent(EGLDisplayHandle display, EGLSurfaceHandle draw, EGLSurfaceHandle read,
|
|
EGLContextHandle context);
|
|
void ReleaseThread();
|
|
EGLContextHandle GetCurrentContext() const;
|
|
EGLDisplayHandle GetCurrentDisplay() const;
|
|
EGLSurfaceHandle GetCurrentSurface(EGLint readdraw) const;
|
|
Bool IsDoubleBufferedSurface(EGLSurfaceHandle surface) const;
|
|
|
|
// Sync
|
|
EGLSyncHandle CreateSync(EGLDisplayHandle display, EGLenum type, const EGLAttrib* attribList);
|
|
Bool DestroySync(EGLDisplayHandle display, EGLSyncHandle sync);
|
|
EGLint ClientWaitSync(EGLDisplayHandle display, EGLSyncHandle sync, EGLint flags, EGLTime timeout);
|
|
Bool GetSyncAttrib(EGLDisplayHandle display, EGLSyncHandle sync, EGLint attribute,
|
|
EGLAttrib* value) const;
|
|
Bool WaitSync(EGLDisplayHandle display, EGLSyncHandle sync, EGLint flags) const;
|
|
|
|
// Image
|
|
EGLImageHandle CreateImage(EGLDisplayHandle display, EGLContextHandle context, EGLenum target,
|
|
EGLClientBuffer buffer, const EGLAttrib* attribList);
|
|
Bool DestroyImage(EGLDisplayHandle display, EGLImageHandle image);
|
|
|
|
private:
|
|
enum class SurfaceType {
|
|
Window,
|
|
Pbuffer,
|
|
Pixmap,
|
|
PbufferFromClientBuffer,
|
|
PlatformWindow,
|
|
PlatformPixmap
|
|
};
|
|
|
|
struct DisplayLookupKey {
|
|
Uint64 NativeDisplayKey = 0;
|
|
EGLenum Platform = EGL_NONE;
|
|
|
|
Bool operator==(const DisplayLookupKey& rhs) const;
|
|
};
|
|
|
|
struct DisplayLookupHasher {
|
|
SizeT operator()(const DisplayLookupKey& key) const;
|
|
};
|
|
|
|
struct DisplayObject {
|
|
Uint64 NativeDisplayKey = 0;
|
|
EGLenum Platform = EGL_NONE;
|
|
Bool Initialized = false;
|
|
EGLint MajorVersion = 1;
|
|
EGLint MinorVersion = 5;
|
|
EGLint SwapInterval = 1;
|
|
Vector<EGLConfigHandle> Configs;
|
|
};
|
|
|
|
struct ConfigObject {
|
|
EGLDisplayHandle Display = EGL_NO_DISPLAY;
|
|
EGLint ConfigId = 1;
|
|
EGLint RedSize = 8;
|
|
EGLint GreenSize = 8;
|
|
EGLint BlueSize = 8;
|
|
EGLint AlphaSize = 8;
|
|
EGLint DepthSize = 24;
|
|
EGLint StencilSize = 8;
|
|
EGLint SurfaceType = EGL_WINDOW_BIT | EGL_PBUFFER_BIT | EGL_PIXMAP_BIT;
|
|
EGLint RenderableType = EGL_OPENGL_BIT | EGL_OPENGL_ES2_BIT | EGL_OPENGL_ES3_BIT;
|
|
EGLint MinSwapInterval = 0;
|
|
EGLint MaxSwapInterval = 4;
|
|
EGLint NativeVisualId = 0;
|
|
};
|
|
|
|
struct ContextObject {
|
|
EGLDisplayHandle Display = EGL_NO_DISPLAY;
|
|
EGLConfigHandle Config = nullptr;
|
|
EGLContextHandle SharedContext = nullptr;
|
|
// P14 S1 (docs/Disaggregated/design/11-state-ownership.md). The wire identity of
|
|
// this context: `ClientContextToken` is what bind_context carries and what the
|
|
// server's per-session context table is keyed by, `ShareGroupToken` the group
|
|
// it was derived into (ShareContext's group, or one of its own). Minted here,
|
|
// in the one place a context is born, so no caller can invent a second one.
|
|
Uint64 ClientContextToken = 0;
|
|
Uint64 ShareGroupToken = 0;
|
|
// P14 S3. The GL state of THIS EGL context. Its object registries live in the
|
|
// share group's ShareGroupState (shared with every context that shares with
|
|
// this one); its bindings, errors and current program are its own.
|
|
SharedPtr<GLState::GLContext> GLStateObject;
|
|
EGLenum ClientAPI = EGL_OPENGL_API;
|
|
EGLint ClientVersion = 1;
|
|
EGLint MajorVersion = 1;
|
|
EGLint MinorVersion = 0;
|
|
EGLint OpenGLProfileMask = 0;
|
|
EGLint EGLContextFlags = 0;
|
|
EGLint OpenGLContextFlags = 0;
|
|
};
|
|
|
|
struct SurfaceObject {
|
|
EGLDisplayHandle Display = EGL_NO_DISPLAY;
|
|
EGLConfigHandle Config = nullptr;
|
|
SurfaceType Type = SurfaceType::Window;
|
|
Bool DestroyPending = false;
|
|
Uint64 NativeHandleKey = 0;
|
|
EGLClientBuffer ClientBuffer = nullptr;
|
|
EGLenum BufferType = EGL_NONE;
|
|
EGLint Width = 0;
|
|
EGLint Height = 0;
|
|
EGLint TextureFormat = EGL_NO_TEXTURE;
|
|
EGLint TextureTarget = EGL_NO_TEXTURE;
|
|
EGLint MipmapLevel = 0;
|
|
EGLint MipmapTexture = EGL_FALSE;
|
|
EGLint RenderBuffer = EGL_BACK_BUFFER;
|
|
EGLint SwapBehavior = EGL_BUFFER_DESTROYED;
|
|
};
|
|
|
|
struct SyncObject {
|
|
EGLDisplayHandle Display = EGL_NO_DISPLAY;
|
|
EGLenum Type = EGL_SYNC_FENCE;
|
|
EGLenum Condition = EGL_SYNC_PRIOR_COMMANDS_COMPLETE;
|
|
EGLenum Status = EGL_SIGNALED;
|
|
};
|
|
|
|
struct ImageObject {
|
|
EGLDisplayHandle Display = EGL_NO_DISPLAY;
|
|
EGLContextHandle Context = nullptr;
|
|
EGLenum Target = EGL_NONE;
|
|
EGLClientBuffer Buffer = nullptr;
|
|
};
|
|
|
|
struct ThreadCurrentState {
|
|
EGLDisplayHandle Display = EGL_NO_DISPLAY;
|
|
EGLSurfaceHandle DrawSurface = EGL_NO_SURFACE;
|
|
EGLSurfaceHandle ReadSurface = EGL_NO_SURFACE;
|
|
EGLContextHandle Context = nullptr;
|
|
// P14 S3. What pGLContext held on this thread BEFORE the first eglMakeCurrent
|
|
// bound an EGL context to it, so releasing restores exactly the binding the
|
|
// thread had rather than a guessed default.
|
|
SharedPtr<GLState::GLContext> RestoreThreadGLState;
|
|
};
|
|
|
|
template <typename HandleType>
|
|
static HandleType EncodeHandle(Uint64 rawHandle) {
|
|
return reinterpret_cast<HandleType>(static_cast<SizeT>(rawHandle));
|
|
}
|
|
|
|
template <typename NativeType>
|
|
static Uint64 ToNativeKey(NativeType nativeHandle) {
|
|
if constexpr (std::is_pointer_v<NativeType>) {
|
|
return static_cast<Uint64>(reinterpret_cast<SizeT>(nativeHandle));
|
|
} else {
|
|
return static_cast<Uint64>(nativeHandle);
|
|
}
|
|
}
|
|
|
|
static std::thread::id CurrentThreadKey();
|
|
|
|
EGLDisplayHandle GetOrCreateDisplay(Uint64 nativeDisplayKey, EGLenum platform);
|
|
EGLConfigHandle CreateDefaultConfig(EGLDisplayHandle display, EGLint configId, EGLint stencilSize);
|
|
|
|
DisplayObject* TryGetDisplay(EGLDisplayHandle display);
|
|
const DisplayObject* TryGetDisplay(EGLDisplayHandle display) const;
|
|
const ConfigObject* TryGetConfig(EGLConfigHandle config) const;
|
|
const ContextObject* TryGetContext(EGLContextHandle context) const;
|
|
const SurfaceObject* TryGetSurface(EGLSurfaceHandle surface) const;
|
|
const SyncObject* TryGetSync(EGLSyncHandle sync) const;
|
|
const ImageObject* TryGetImage(EGLImageHandle image) const;
|
|
|
|
void ReleaseDisplayObjects(EGLDisplayHandle display);
|
|
void ReleaseThreadUnlocked(const std::thread::id& threadKey);
|
|
Bool IsSurfaceCurrentUnlocked(EGLSurfaceHandle surface) const;
|
|
void DestroyPendingSurfaceIfUnused(EGLSurfaceHandle surface);
|
|
|
|
private:
|
|
mutable std::recursive_mutex m_mutex;
|
|
|
|
Uint64 m_nextDisplayHandle = 1;
|
|
Uint64 m_nextConfigHandle = 1;
|
|
Uint64 m_nextSurfaceHandle = 1;
|
|
Uint64 m_nextContextHandle = 1;
|
|
Uint64 m_nextSyncHandle = 1;
|
|
Uint64 m_nextImageHandle = 1;
|
|
// P14 S1. Dense from 1, never reused, never 0: 0 is "no context" on both planes.
|
|
Uint64 m_nextClientContextToken = 1;
|
|
Uint64 m_nextShareGroupToken = 1;
|
|
|
|
UnorderedMap<DisplayLookupKey, EGLDisplayHandle, DisplayLookupHasher> m_displayLookup;
|
|
UnorderedMap<EGLDisplayHandle, DisplayObject> m_displays;
|
|
UnorderedMap<EGLConfigHandle, ConfigObject> m_configs;
|
|
UnorderedMap<EGLSurfaceHandle, SurfaceObject> m_surfaces;
|
|
UnorderedMap<EGLContextHandle, ContextObject> m_contexts;
|
|
UnorderedMap<EGLSyncHandle, SyncObject> m_syncs;
|
|
UnorderedMap<EGLImageHandle, ImageObject> m_images;
|
|
|
|
UnorderedMap<std::thread::id, EGLint> m_threadErrors;
|
|
UnorderedMap<std::thread::id, EGLenum> m_threadBoundAPI;
|
|
UnorderedMap<std::thread::id, ThreadCurrentState> m_threadCurrents;
|
|
UnorderedMap<EGLContextHandle, std::thread::id> m_contextOwners;
|
|
};
|
|
} // namespace EGLState
|
|
|
|
extern UniquePtr<EGLState::EGLContext>& pEGLContext;
|
|
} // namespace MG_State
|
|
} // namespace MobileGL
|