Files
MobileGL/MobileGL/MG_State/EGLState/Core.cpp
T
gold 035f1b07ae [Feature] (disaggregated): MOBILEGL_IPC_SURFACE=host - the display host owns the screen and the frames
The third on-screen shape, and the one this whole line was built toward: Anland keeps Wayland,
input and the display, and the render server draws into the frames the display host allocates.

    client (container)      asks for a window surface with no window of its own
    server (Android)        takes a frame from the host over the host frame bridge and binds it as
                            the draw target of that surface
    host (the app that
    owns the screen)        offers each frame, presents it when the server says it is drawn

The pieces, in the order a frame travels:

 - Config: MOBILEGL_IPC_SURFACE=host beside =server, and HostFrameWindowSurfaces() as the one
   predicate every client-side consumer asks (EGLImpl accepts a NULL window for either on-screen
   shape, and BackendObject_Remote routes the creation to the server instead of sending a handle).
 - Wire: the frame-local tag kHostFrameWindowBackend, the mirror of kServerOwnedWindowBackend - it
   names no WindowBackend of the pull build's enum, and the server consumes it before the unpack
   that would refuse it.
 - Server: ApplyHostFrameWindowSurface replaces nothing, unlike the server-owned arm: there is no
   window of either side's to substitute, because the backend takes the frames from the bridge.
 - Backend: SurfaceKind::HostFrame, InitHostFrameSurface (the EGL context comes from the pbuffer
   path; what changes is what it draws INTO) and PresentHostFrame (glFinish, answer the host, take
   the next frame) - and SwapEGLBuffers routes to it, because a host-framed surface has no
   swapchain to swap: the host is what puts the frame on the glass.

HostFrameTarget and HostFrameBridge, committed just before this, are what the backend uses.

Compiles and links in this tree (libMobileGL.so and libMobileGLServer.so, exit 0).  What is NOT
here yet, and is the next thing: the run that puts Plasma on the phone's screen through it.
2026-10-01 01:11:21 +08:00

1501 lines
67 KiB
C++

// MobileGL - MobileGL/MG_State/EGLState/Core.cpp
// 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
#include "Core.h"
#include <EGL/eglext.h>
namespace MobileGL {
namespace MG_State {
namespace EGLState {
namespace {
constexpr EGLint EGL_DISPLAY_MAJOR_VERSION = 1;
constexpr EGLint EGL_DISPLAY_MINOR_VERSION = 5;
constexpr EGLint DEFAULT_MAX_PBUFFER_SIZE = 16384;
template <typename AttrType>
Optional<AttrType> ParseAttribValue(const AttrType* attribList, EGLint attrib) {
if (!attribList) {
return Nullopt;
}
for (SizeT i = 0; attribList[i] != EGL_NONE; i += 2) {
if (attribList[i] == static_cast<AttrType>(attrib)) {
return attribList[i + 1];
}
}
return Nullopt;
}
EGLint QueryDefaultX11VisualId() {
#if defined(__linux__) && !defined(__ANDROID__)
const char* displayName = std::getenv("DISPLAY");
if (!displayName) {
return 0;
}
void* x11Lib = dlopen("libX11.so.6", RTLD_LOCAL | RTLD_NOW);
if (!x11Lib) {
x11Lib = dlopen("libX11.so", RTLD_LOCAL | RTLD_NOW);
}
if (!x11Lib) {
return 0;
}
using XOpenDisplayFn = void* (*)(const char*);
using XDefaultScreenFn = int (*)(void*);
using XDefaultVisualFn = void* (*)(void*, int);
using XVisualIDFromVisualFn = unsigned long (*)(void*);
using XCloseDisplayFn = int (*)(void*);
auto* xOpenDisplay = reinterpret_cast<XOpenDisplayFn>(dlsym(x11Lib, "XOpenDisplay"));
auto* xDefaultScreen = reinterpret_cast<XDefaultScreenFn>(dlsym(x11Lib, "XDefaultScreen"));
auto* xDefaultVisual = reinterpret_cast<XDefaultVisualFn>(dlsym(x11Lib, "XDefaultVisual"));
auto* xVisualIDFromVisual =
reinterpret_cast<XVisualIDFromVisualFn>(dlsym(x11Lib, "XVisualIDFromVisual"));
auto* xCloseDisplay = reinterpret_cast<XCloseDisplayFn>(dlsym(x11Lib, "XCloseDisplay"));
if (!xOpenDisplay || !xDefaultScreen || !xDefaultVisual || !xVisualIDFromVisual || !xCloseDisplay) {
dlclose(x11Lib);
return 0;
}
void* display = xOpenDisplay(displayName);
if (!display) {
dlclose(x11Lib);
return 0;
}
const int screen = xDefaultScreen(display);
void* visual = xDefaultVisual(display, screen);
const auto visualId = visual ? static_cast<EGLint>(xVisualIDFromVisual(visual)) : 0;
xCloseDisplay(display);
dlclose(x11Lib);
return visualId;
#else
return 0;
#endif
}
} // namespace
Bool EGLContext::DisplayLookupKey::operator==(const DisplayLookupKey& rhs) const {
return this->NativeDisplayKey == rhs.NativeDisplayKey && this->Platform == rhs.Platform;
}
SizeT EGLContext::DisplayLookupHasher::operator()(const DisplayLookupKey& key) const {
const auto nativeHash = std::hash<Uint64>{}(key.NativeDisplayKey);
const auto platformHash = std::hash<Uint64>{}(static_cast<Uint64>(key.Platform));
return nativeHash ^ (platformHash + 0x9e3779b97f4a7c15ull + (nativeHash << 6) + (nativeHash >> 2));
}
std::thread::id EGLContext::CurrentThreadKey() {
return std::this_thread::get_id();
}
void EGLContext::SetError(EGLint errorCode) {
if (errorCode == EGL_SUCCESS) {
return;
}
const std::lock_guard<std::recursive_mutex> lock(m_mutex);
m_threadErrors[CurrentThreadKey()] = errorCode;
}
EGLint EGLContext::ConsumeError() {
const std::lock_guard<std::recursive_mutex> lock(m_mutex);
const auto threadKey = CurrentThreadKey();
auto errorIt = m_threadErrors.find(threadKey);
if (errorIt == m_threadErrors.end()) {
return EGL_SUCCESS;
}
const EGLint error = errorIt->second;
m_threadErrors.erase(errorIt);
return error;
}
void EGLContext::SetBoundAPI(EGLenum api) {
const std::lock_guard<std::recursive_mutex> lock(m_mutex);
m_threadBoundAPI[CurrentThreadKey()] = api;
}
EGLenum EGLContext::GetBoundAPI() const {
const std::lock_guard<std::recursive_mutex> lock(m_mutex);
auto apiIt = m_threadBoundAPI.find(CurrentThreadKey());
if (apiIt == m_threadBoundAPI.end()) {
return EGL_OPENGL_API;
}
return apiIt->second;
}
EGLContext::EGLDisplayHandle EGLContext::GetOrCreateDisplay(Uint64 nativeDisplayKey, EGLenum platform) {
const DisplayLookupKey key = {
.NativeDisplayKey = nativeDisplayKey,
.Platform = platform,
};
auto lookupIt = m_displayLookup.find(key);
if (lookupIt != m_displayLookup.end()) {
return lookupIt->second;
}
const auto display = EncodeHandle<EGLDisplayHandle>(m_nextDisplayHandle++);
DisplayObject displayObject = {
.NativeDisplayKey = nativeDisplayKey,
.Platform = platform,
.Initialized = false,
.MajorVersion = EGL_DISPLAY_MAJOR_VERSION,
.MinorVersion = EGL_DISPLAY_MINOR_VERSION,
.SwapInterval = 1,
};
displayObject.Configs.push_back(CreateDefaultConfig(display, 1, 0));
displayObject.Configs.push_back(CreateDefaultConfig(display, 2, 8));
m_displays[display] = displayObject;
m_displayLookup[key] = display;
return display;
}
EGLContext::EGLConfigHandle EGLContext::CreateDefaultConfig(EGLDisplayHandle display, EGLint configId,
EGLint stencilSize) {
const auto config = EncodeHandle<EGLConfigHandle>(m_nextConfigHandle++);
ConfigObject cfg = {
.Display = display,
.ConfigId = configId,
.RedSize = 8,
.GreenSize = 8,
.BlueSize = 8,
.AlphaSize = 8,
.DepthSize = 24,
.StencilSize = stencilSize,
.SurfaceType = EGL_WINDOW_BIT | EGL_PBUFFER_BIT | EGL_PIXMAP_BIT,
.RenderableType = EGL_OPENGL_BIT | EGL_OPENGL_ES2_BIT | EGL_OPENGL_ES3_BIT,
.MinSwapInterval = 0,
.MaxSwapInterval = 4,
.NativeVisualId = QueryDefaultX11VisualId(),
};
#if defined(ANDROID) || defined(__ANDROID__)
cfg.NativeVisualId = AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM;
#endif
m_configs[config] = cfg;
return config;
}
EGLContext::DisplayObject* EGLContext::TryGetDisplay(EGLDisplayHandle display) {
auto it = m_displays.find(display);
return it == m_displays.end() ? nullptr : &it->second;
}
const EGLContext::DisplayObject* EGLContext::TryGetDisplay(EGLDisplayHandle display) const {
auto it = m_displays.find(display);
return it == m_displays.end() ? nullptr : &it->second;
}
const EGLContext::ConfigObject* EGLContext::TryGetConfig(EGLConfigHandle config) const {
auto it = m_configs.find(config);
return it == m_configs.end() ? nullptr : &it->second;
}
const EGLContext::ContextObject* EGLContext::TryGetContext(EGLContextHandle context) const {
auto it = m_contexts.find(context);
return it == m_contexts.end() ? nullptr : &it->second;
}
const EGLContext::SurfaceObject* EGLContext::TryGetSurface(EGLSurfaceHandle surface) const {
auto it = m_surfaces.find(surface);
return it == m_surfaces.end() ? nullptr : &it->second;
}
const EGLContext::SyncObject* EGLContext::TryGetSync(EGLSyncHandle sync) const {
auto it = m_syncs.find(sync);
return it == m_syncs.end() ? nullptr : &it->second;
}
const EGLContext::ImageObject* EGLContext::TryGetImage(EGLImageHandle image) const {
auto it = m_images.find(image);
return it == m_images.end() ? nullptr : &it->second;
}
void EGLContext::ReleaseThreadUnlocked(const std::thread::id& threadKey) {
auto currentIt = m_threadCurrents.find(threadKey);
if (currentIt == m_threadCurrents.end()) {
return;
}
const EGLSurfaceHandle drawSurface = currentIt->second.DrawSurface;
const EGLSurfaceHandle readSurface = currentIt->second.ReadSurface;
const EGLContextHandle context = currentIt->second.Context;
if (context != nullptr) {
auto ownerIt = m_contextOwners.find(context);
if (ownerIt != m_contextOwners.end() && ownerIt->second == threadKey) {
m_contextOwners.erase(ownerIt);
}
}
m_threadCurrents.erase(currentIt);
DestroyPendingSurfaceIfUnused(drawSurface);
DestroyPendingSurfaceIfUnused(readSurface);
}
Bool EGLContext::IsSurfaceCurrentUnlocked(EGLSurfaceHandle surface) const {
if (surface == EGL_NO_SURFACE) {
return false;
}
for (const auto& current : m_threadCurrents) {
if (current.second.DrawSurface == surface || current.second.ReadSurface == surface) {
return true;
}
}
return false;
}
void EGLContext::DestroyPendingSurfaceIfUnused(EGLSurfaceHandle surface) {
auto surfaceIt = m_surfaces.find(surface);
if (surfaceIt == m_surfaces.end() || !surfaceIt->second.DestroyPending ||
IsSurfaceCurrentUnlocked(surface)) {
return;
}
m_surfaces.erase(surfaceIt);
}
void EGLContext::ReleaseDisplayObjects(EGLDisplayHandle display) {
Vector<EGLContextHandle> removedContexts;
for (auto contextIt = m_contexts.begin(); contextIt != m_contexts.end();) {
if (contextIt->second.Display == display) {
removedContexts.push_back(contextIt->first);
contextIt = m_contexts.erase(contextIt);
} else {
++contextIt;
}
}
for (const auto context : removedContexts) {
m_contextOwners.erase(context);
}
for (auto surfaceIt = m_surfaces.begin(); surfaceIt != m_surfaces.end();) {
if (surfaceIt->second.Display == display) {
surfaceIt = m_surfaces.erase(surfaceIt);
} else {
++surfaceIt;
}
}
for (auto syncIt = m_syncs.begin(); syncIt != m_syncs.end();) {
if (syncIt->second.Display == display) {
syncIt = m_syncs.erase(syncIt);
} else {
++syncIt;
}
}
for (auto imageIt = m_images.begin(); imageIt != m_images.end();) {
if (imageIt->second.Display == display) {
imageIt = m_images.erase(imageIt);
} else {
++imageIt;
}
}
for (auto threadIt = m_threadCurrents.begin(); threadIt != m_threadCurrents.end();) {
if (threadIt->second.Display == display) {
if (threadIt->second.Context != nullptr) {
auto ownerIt = m_contextOwners.find(threadIt->second.Context);
if (ownerIt != m_contextOwners.end() && ownerIt->second == threadIt->first) {
m_contextOwners.erase(ownerIt);
}
}
threadIt = m_threadCurrents.erase(threadIt);
} else {
++threadIt;
}
}
}
EGLContext::EGLDisplayHandle EGLContext::GetDisplay(NativeDisplayType nativeDisplay) {
const std::lock_guard<std::recursive_mutex> lock(m_mutex);
return GetOrCreateDisplay(ToNativeKey(nativeDisplay), EGL_NONE);
}
EGLContext::EGLDisplayHandle EGLContext::GetPlatformDisplay(EGLenum platform, void* nativeDisplay) {
const std::lock_guard<std::recursive_mutex> lock(m_mutex);
return GetOrCreateDisplay(ToNativeKey(nativeDisplay), platform);
}
Bool EGLContext::ValidateDisplay(EGLDisplayHandle display) const {
const std::lock_guard<std::recursive_mutex> lock(m_mutex);
return m_displays.find(display) != m_displays.end();
}
Bool EGLContext::IsDisplayInitialized(EGLDisplayHandle display) const {
const std::lock_guard<std::recursive_mutex> lock(m_mutex);
const auto* displayObject = TryGetDisplay(display);
return displayObject && displayObject->Initialized;
}
Bool EGLContext::InitializeDisplay(EGLDisplayHandle display, EGLint* major, EGLint* minor) {
const std::lock_guard<std::recursive_mutex> lock(m_mutex);
auto* displayObject = TryGetDisplay(display);
if (!displayObject) {
SetError(EGL_BAD_DISPLAY);
return false;
}
displayObject->Initialized = true;
displayObject->MajorVersion = EGL_DISPLAY_MAJOR_VERSION;
displayObject->MinorVersion = EGL_DISPLAY_MINOR_VERSION;
if (major) {
*major = displayObject->MajorVersion;
}
if (minor) {
*minor = displayObject->MinorVersion;
}
return true;
}
Bool EGLContext::TerminateDisplay(EGLDisplayHandle display) {
const std::lock_guard<std::recursive_mutex> lock(m_mutex);
auto* displayObject = TryGetDisplay(display);
if (!displayObject) {
SetError(EGL_BAD_DISPLAY);
return false;
}
ReleaseDisplayObjects(display);
displayObject->Initialized = false;
return true;
}
Bool EGLContext::HasAnyInitializedDisplay() const {
const std::lock_guard<std::recursive_mutex> lock(m_mutex);
for (const auto& [handle, displayObject] : m_displays) {
if (displayObject.Initialized) {
return true;
}
}
return false;
}
Bool EGLContext::HasAnyCurrentContext() const {
const std::lock_guard<std::recursive_mutex> lock(m_mutex);
for (const auto& [threadId, current] : m_threadCurrents) {
if (current.Context != nullptr) {
return true;
}
}
return false;
}
Bool EGLContext::ChooseConfig(EGLDisplayHandle display, const EGLint* attribList, EGLConfigHandle* configs,
EGLint configSize, EGLint* numConfig) {
const std::lock_guard<std::recursive_mutex> lock(m_mutex);
auto* displayObject = TryGetDisplay(display);
if (!displayObject) {
SetError(EGL_BAD_DISPLAY);
return false;
}
if (!displayObject->Initialized) {
SetError(EGL_NOT_INITIALIZED);
return false;
}
if (!numConfig) {
SetError(EGL_BAD_PARAMETER);
return false;
}
Vector<EGLConfigHandle> matchedConfigs;
for (const auto config : displayObject->Configs) {
const auto* cfg = TryGetConfig(config);
if (!cfg) {
continue;
}
Bool matches = true;
if (attribList) {
for (SizeT i = 0; attribList[i] != EGL_NONE; i += 2) {
const EGLint attr = attribList[i];
const EGLint requested = attribList[i + 1];
if (requested == EGL_DONT_CARE) {
continue;
}
switch (attr) {
case EGL_CONFIG_ID:
matches = cfg->ConfigId == requested;
break;
case EGL_RED_SIZE:
matches = cfg->RedSize >= requested;
break;
case EGL_GREEN_SIZE:
matches = cfg->GreenSize >= requested;
break;
case EGL_BLUE_SIZE:
matches = cfg->BlueSize >= requested;
break;
case EGL_ALPHA_SIZE:
matches = cfg->AlphaSize >= requested;
break;
case EGL_BUFFER_SIZE:
matches = (cfg->RedSize + cfg->GreenSize + cfg->BlueSize + cfg->AlphaSize) >= requested;
break;
case EGL_DEPTH_SIZE:
matches = cfg->DepthSize >= requested;
break;
case EGL_STENCIL_SIZE:
matches = cfg->StencilSize >= requested;
break;
case EGL_SURFACE_TYPE:
matches = (cfg->SurfaceType & requested) == requested;
break;
case EGL_RENDERABLE_TYPE:
case EGL_CONFORMANT:
matches = (cfg->RenderableType & requested) == requested;
break;
case EGL_SAMPLE_BUFFERS:
case EGL_SAMPLES:
matches = requested == 0;
break;
case EGL_NATIVE_VISUAL_ID:
matches = cfg->NativeVisualId == requested;
break;
default:
break;
}
if (!matches) {
break;
}
}
}
if (matches) {
matchedConfigs.push_back(config);
}
}
*numConfig = static_cast<EGLint>(matchedConfigs.size());
if (!configs || configSize <= 0) {
return true;
}
const SizeT copyCount = std::min(static_cast<SizeT>(configSize), matchedConfigs.size());
for (SizeT i = 0; i < copyCount; ++i) {
configs[i] = matchedConfigs[i];
}
return true;
}
Bool EGLContext::GetConfigs(EGLDisplayHandle display, EGLConfigHandle* configs, EGLint configSize,
EGLint* numConfig) {
const std::lock_guard<std::recursive_mutex> lock(m_mutex);
return ChooseConfig(display, nullptr, configs, configSize, numConfig);
}
Bool EGLContext::ValidateConfig(EGLConfigHandle config) const {
const std::lock_guard<std::recursive_mutex> lock(m_mutex);
return m_configs.find(config) != m_configs.end();
}
Bool EGLContext::ValidateConfigOnDisplay(EGLDisplayHandle display, EGLConfigHandle config) const {
const std::lock_guard<std::recursive_mutex> lock(m_mutex);
const auto* cfg = TryGetConfig(config);
return cfg && cfg->Display == display;
}
Bool EGLContext::GetConfigAttrib(EGLDisplayHandle display, EGLConfigHandle config, EGLint attribute,
EGLint* value) const {
const std::lock_guard<std::recursive_mutex> lock(m_mutex);
if (!value) {
const_cast<EGLContext*>(this)->SetError(EGL_BAD_PARAMETER);
return false;
}
const auto* cfg = TryGetConfig(config);
if (!cfg) {
const_cast<EGLContext*>(this)->SetError(EGL_BAD_CONFIG);
return false;
}
if (cfg->Display != display) {
const_cast<EGLContext*>(this)->SetError(EGL_BAD_MATCH);
return false;
}
switch (attribute) {
case EGL_BUFFER_SIZE:
*value = cfg->RedSize + cfg->GreenSize + cfg->BlueSize + cfg->AlphaSize;
return true;
case EGL_ALPHA_MASK_SIZE:
*value = 0;
return true;
case EGL_BIND_TO_TEXTURE_RGB:
case EGL_BIND_TO_TEXTURE_RGBA:
*value = EGL_FALSE;
return true;
case EGL_COLOR_BUFFER_TYPE:
*value = EGL_RGB_BUFFER;
return true;
case EGL_CONFIG_CAVEAT:
*value = EGL_NONE;
return true;
case EGL_CONFIG_ID:
*value = cfg->ConfigId;
return true;
case EGL_LEVEL:
*value = 0;
return true;
case EGL_LUMINANCE_SIZE:
*value = 0;
return true;
case EGL_MAX_PBUFFER_WIDTH:
case EGL_MAX_PBUFFER_HEIGHT:
*value = DEFAULT_MAX_PBUFFER_SIZE;
return true;
case EGL_MAX_PBUFFER_PIXELS:
*value = DEFAULT_MAX_PBUFFER_SIZE * DEFAULT_MAX_PBUFFER_SIZE;
return true;
case EGL_RED_SIZE:
*value = cfg->RedSize;
return true;
case EGL_GREEN_SIZE:
*value = cfg->GreenSize;
return true;
case EGL_BLUE_SIZE:
*value = cfg->BlueSize;
return true;
case EGL_ALPHA_SIZE:
*value = cfg->AlphaSize;
return true;
case EGL_DEPTH_SIZE:
*value = cfg->DepthSize;
return true;
case EGL_STENCIL_SIZE:
*value = cfg->StencilSize;
return true;
case EGL_SURFACE_TYPE:
*value = cfg->SurfaceType;
return true;
case EGL_RENDERABLE_TYPE:
case EGL_CONFORMANT:
*value = cfg->RenderableType;
return true;
case EGL_MIN_SWAP_INTERVAL:
*value = cfg->MinSwapInterval;
return true;
case EGL_MAX_SWAP_INTERVAL:
*value = cfg->MaxSwapInterval;
return true;
case EGL_NATIVE_RENDERABLE:
*value = EGL_TRUE;
return true;
case EGL_NATIVE_VISUAL_ID:
*value = cfg->NativeVisualId;
return true;
case EGL_NATIVE_VISUAL_TYPE:
*value = cfg->NativeVisualId;
return true;
case EGL_SAMPLE_BUFFERS:
case EGL_SAMPLES:
*value = 0;
return true;
case EGL_TRANSPARENT_TYPE:
*value = EGL_NONE;
return true;
case EGL_TRANSPARENT_RED_VALUE:
case EGL_TRANSPARENT_GREEN_VALUE:
case EGL_TRANSPARENT_BLUE_VALUE:
*value = 0;
return true;
default:
const_cast<EGLContext*>(this)->SetError(EGL_BAD_ATTRIBUTE);
return false;
}
}
EGLContext::EGLContextHandle EGLContext::CreateContext(EGLDisplayHandle display, EGLConfigHandle config,
EGLContextHandle shareCtx,
const EGLint* attribList) {
const std::lock_guard<std::recursive_mutex> lock(m_mutex);
const auto* displayObject = TryGetDisplay(display);
if (!displayObject) {
SetError(EGL_BAD_DISPLAY);
return nullptr;
}
if (!displayObject->Initialized) {
SetError(EGL_NOT_INITIALIZED);
return nullptr;
}
if (!ValidateConfigOnDisplay(display, config)) {
SetError(EGL_BAD_CONFIG);
return nullptr;
}
if (shareCtx != nullptr && !ValidateContextOnDisplay(display, shareCtx)) {
SetError(EGL_BAD_CONTEXT);
return nullptr;
}
ContextObject contextObject = {
.Display = display,
.Config = config,
.SharedContext = shareCtx,
.ClientAPI = GetBoundAPI(),
.ClientVersion = 1,
.MajorVersion = 1,
.MinorVersion = 0,
};
if (auto value = ParseAttribValue(attribList, EGL_CONTEXT_CLIENT_VERSION); value) {
contextObject.ClientVersion = *value;
contextObject.MajorVersion = *value;
}
if (auto value = ParseAttribValue(attribList, EGL_CONTEXT_MAJOR_VERSION); value) {
contextObject.MajorVersion = *value;
}
if (auto value = ParseAttribValue(attribList, EGL_CONTEXT_MINOR_VERSION); value) {
contextObject.MinorVersion = *value;
}
if (auto value = ParseAttribValue(attribList, EGL_CONTEXT_OPENGL_PROFILE_MASK); value) {
contextObject.OpenGLProfileMask = *value;
}
if (auto value = ParseAttribValue(attribList, EGL_CONTEXT_FLAGS_KHR); value) {
contextObject.EGLContextFlags = *value;
if (*value & EGL_CONTEXT_OPENGL_FORWARD_COMPATIBLE_BIT_KHR) {
contextObject.OpenGLContextFlags |= GL_CONTEXT_FLAG_FORWARD_COMPATIBLE_BIT;
}
if (*value & EGL_CONTEXT_OPENGL_DEBUG_BIT_KHR) {
contextObject.OpenGLContextFlags |= GL_CONTEXT_FLAG_DEBUG_BIT;
}
if (*value & EGL_CONTEXT_OPENGL_ROBUST_ACCESS_BIT_KHR) {
contextObject.OpenGLContextFlags |= GL_CONTEXT_FLAG_ROBUST_ACCESS_BIT;
}
}
if (auto value = ParseAttribValue(attribList, EGL_CONTEXT_OPENGL_FORWARD_COMPATIBLE);
value && *value == EGL_TRUE) {
contextObject.EGLContextFlags |= EGL_CONTEXT_OPENGL_FORWARD_COMPATIBLE_BIT_KHR;
contextObject.OpenGLContextFlags |= GL_CONTEXT_FLAG_FORWARD_COMPATIBLE_BIT;
}
if (auto value = ParseAttribValue(attribList, EGL_CONTEXT_OPENGL_DEBUG); value && *value == EGL_TRUE) {
contextObject.EGLContextFlags |= EGL_CONTEXT_OPENGL_DEBUG_BIT_KHR;
contextObject.OpenGLContextFlags |= GL_CONTEXT_FLAG_DEBUG_BIT;
}
if (auto value = ParseAttribValue(attribList, EGL_CONTEXT_OPENGL_ROBUST_ACCESS);
value && *value == EGL_TRUE) {
contextObject.EGLContextFlags |= EGL_CONTEXT_OPENGL_ROBUST_ACCESS_BIT_KHR;
contextObject.OpenGLContextFlags |= GL_CONTEXT_FLAG_ROBUST_ACCESS_BIT;
}
const auto context = EncodeHandle<EGLContextHandle>(m_nextContextHandle++);
m_contexts[context] = contextObject;
return context;
}
Bool EGLContext::DestroyContext(EGLDisplayHandle display, EGLContextHandle context) {
const std::lock_guard<std::recursive_mutex> lock(m_mutex);
auto contextIt = m_contexts.find(context);
if (contextIt == m_contexts.end()) {
SetError(EGL_BAD_CONTEXT);
return false;
}
if (contextIt->second.Display != display) {
SetError(EGL_BAD_MATCH);
return false;
}
auto ownerIt = m_contextOwners.find(context);
if (ownerIt != m_contextOwners.end()) {
SetError(EGL_BAD_ACCESS);
return false;
}
m_contexts.erase(contextIt);
return true;
}
Bool EGLContext::QueryContext(EGLDisplayHandle display, EGLContextHandle context, EGLint attribute,
EGLint* value) const {
const std::lock_guard<std::recursive_mutex> lock(m_mutex);
if (!value) {
const_cast<EGLContext*>(this)->SetError(EGL_BAD_PARAMETER);
return false;
}
const auto* contextObject = TryGetContext(context);
if (!contextObject) {
const_cast<EGLContext*>(this)->SetError(EGL_BAD_CONTEXT);
return false;
}
if (contextObject->Display != display) {
const_cast<EGLContext*>(this)->SetError(EGL_BAD_MATCH);
return false;
}
switch (attribute) {
case EGL_CONTEXT_CLIENT_TYPE:
*value = static_cast<EGLint>(contextObject->ClientAPI);
return true;
case EGL_CONTEXT_CLIENT_VERSION:
#if EGL_CONTEXT_MAJOR_VERSION != EGL_CONTEXT_CLIENT_VERSION
case EGL_CONTEXT_MAJOR_VERSION:
#endif
*value = contextObject->MajorVersion;
return true;
case EGL_CONTEXT_MINOR_VERSION:
*value = contextObject->MinorVersion;
return true;
case EGL_CONTEXT_FLAGS_KHR:
*value = contextObject->EGLContextFlags;
return true;
case EGL_CONFIG_ID: {
const auto* cfg = TryGetConfig(contextObject->Config);
if (!cfg) {
const_cast<EGLContext*>(this)->SetError(EGL_BAD_CONFIG);
return false;
}
*value = cfg->ConfigId;
return true;
}
default:
const_cast<EGLContext*>(this)->SetError(EGL_BAD_ATTRIBUTE);
return false;
}
}
Bool EGLContext::ValidateContext(EGLContextHandle context) const {
const std::lock_guard<std::recursive_mutex> lock(m_mutex);
return m_contexts.find(context) != m_contexts.end();
}
Bool EGLContext::ValidateContextOnDisplay(EGLDisplayHandle display, EGLContextHandle context) const {
const std::lock_guard<std::recursive_mutex> lock(m_mutex);
const auto* ctx = TryGetContext(context);
return ctx && ctx->Display == display;
}
Bool EGLContext::IsCurrentContextOpenGLCoreProfile() const {
const std::lock_guard<std::recursive_mutex> lock(m_mutex);
auto currentIt = m_threadCurrents.find(CurrentThreadKey());
if (currentIt == m_threadCurrents.end()) {
return false;
}
const auto* ctx = TryGetContext(currentIt->second.Context);
if (!ctx || ctx->ClientAPI != EGL_OPENGL_API ||
(ctx->OpenGLProfileMask & EGL_CONTEXT_OPENGL_COMPATIBILITY_PROFILE_BIT)) {
return false;
}
return (ctx->OpenGLProfileMask & EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT) ||
ctx->MajorVersion > 3 || (ctx->MajorVersion == 3 && ctx->MinorVersion >= 1);
}
Bool EGLContext::IsCurrentContextOpenGLCompatibilityProfile() const {
const std::lock_guard<std::recursive_mutex> lock(m_mutex);
auto currentIt = m_threadCurrents.find(CurrentThreadKey());
if (currentIt == m_threadCurrents.end()) {
return false;
}
const auto* ctx = TryGetContext(currentIt->second.Context);
// Affirmative check: true only when the host explicitly requested the
// compatibility bit. Attrib-less contexts report false and thus read as core
// for GL_CONTEXT_PROFILE_MASK (EGL defaults 3.x contexts to the core profile).
return ctx && ctx->ClientAPI == EGL_OPENGL_API &&
(ctx->OpenGLProfileMask & EGL_CONTEXT_OPENGL_COMPATIBILITY_PROFILE_BIT);
}
EGLint EGLContext::GetCurrentContextFlags() const {
const std::lock_guard<std::recursive_mutex> lock(m_mutex);
auto currentIt = m_threadCurrents.find(CurrentThreadKey());
if (currentIt == m_threadCurrents.end()) {
return 0;
}
const auto* ctx = TryGetContext(currentIt->second.Context);
return ctx ? ctx->OpenGLContextFlags : 0;
}
EGLContext::EGLSurfaceHandle EGLContext::CreateWindowSurface(EGLDisplayHandle display,
EGLConfigHandle config,
NativeWindowType window,
const EGLint* attribList) {
const std::lock_guard<std::recursive_mutex> lock(m_mutex);
(void)attribList;
if (!IsDisplayInitialized(display)) {
SetError(EGL_NOT_INITIALIZED);
return EGL_NO_SURFACE;
}
if (!ValidateConfigOnDisplay(display, config)) {
SetError(EGL_BAD_CONFIG);
return EGL_NO_SURFACE;
}
if (ToNativeKey(window) == 0) {
SetError(EGL_BAD_NATIVE_WINDOW);
return EGL_NO_SURFACE;
}
const auto surface = EncodeHandle<EGLSurfaceHandle>(m_nextSurfaceHandle++);
m_surfaces[surface] = SurfaceObject{
.Display = display,
.Config = config,
.Type = SurfaceType::Window,
.NativeHandleKey = ToNativeKey(window),
.ClientBuffer = nullptr,
.BufferType = EGL_NONE,
};
return surface;
}
EGLContext::EGLSurfaceHandle EGLContext::CreatePbufferSurface(EGLDisplayHandle display,
EGLConfigHandle config,
const EGLint* attribList) {
const std::lock_guard<std::recursive_mutex> lock(m_mutex);
if (!IsDisplayInitialized(display)) {
SetError(EGL_NOT_INITIALIZED);
return EGL_NO_SURFACE;
}
if (!ValidateConfigOnDisplay(display, config)) {
SetError(EGL_BAD_CONFIG);
return EGL_NO_SURFACE;
}
EGLint width = 1;
EGLint height = 1;
if (auto value = ParseAttribValue(attribList, EGL_WIDTH); value) {
width = *value;
}
if (auto value = ParseAttribValue(attribList, EGL_HEIGHT); value) {
height = *value;
}
if (width < 0 || height < 0) {
SetError(EGL_BAD_ATTRIBUTE);
return EGL_NO_SURFACE;
}
const auto surface = EncodeHandle<EGLSurfaceHandle>(m_nextSurfaceHandle++);
m_surfaces[surface] = SurfaceObject{
.Display = display,
.Config = config,
.Type = SurfaceType::Pbuffer,
.NativeHandleKey = 0,
.ClientBuffer = nullptr,
.BufferType = EGL_NONE,
.Width = width,
.Height = height,
.TextureFormat = ParseAttribValue(attribList, EGL_TEXTURE_FORMAT).value_or(EGL_NO_TEXTURE),
.TextureTarget = ParseAttribValue(attribList, EGL_TEXTURE_TARGET).value_or(EGL_NO_TEXTURE),
.MipmapLevel = ParseAttribValue(attribList, EGL_MIPMAP_LEVEL).value_or(0),
.MipmapTexture = ParseAttribValue(attribList, EGL_MIPMAP_TEXTURE).value_or(EGL_FALSE),
};
return surface;
}
EGLContext::EGLSurfaceHandle EGLContext::CreatePixmapSurface(EGLDisplayHandle display,
EGLConfigHandle config,
EGLNativePixmapType pixmap,
const EGLint* attribList) {
const std::lock_guard<std::recursive_mutex> lock(m_mutex);
(void)attribList;
if (!IsDisplayInitialized(display)) {
SetError(EGL_NOT_INITIALIZED);
return EGL_NO_SURFACE;
}
if (!ValidateConfigOnDisplay(display, config)) {
SetError(EGL_BAD_CONFIG);
return EGL_NO_SURFACE;
}
if (ToNativeKey(pixmap) == 0) {
SetError(EGL_BAD_NATIVE_PIXMAP);
return EGL_NO_SURFACE;
}
const auto surface = EncodeHandle<EGLSurfaceHandle>(m_nextSurfaceHandle++);
m_surfaces[surface] = SurfaceObject{
.Display = display,
.Config = config,
.Type = SurfaceType::Pixmap,
.NativeHandleKey = ToNativeKey(pixmap),
.ClientBuffer = nullptr,
.BufferType = EGL_NONE,
};
return surface;
}
EGLContext::EGLSurfaceHandle EGLContext::CreatePbufferFromClientBuffer(EGLDisplayHandle display,
EGLenum bufferType,
EGLClientBuffer buffer,
EGLConfigHandle config,
const EGLint* attribList) {
const std::lock_guard<std::recursive_mutex> lock(m_mutex);
if (!IsDisplayInitialized(display)) {
SetError(EGL_NOT_INITIALIZED);
return EGL_NO_SURFACE;
}
if (!ValidateConfigOnDisplay(display, config)) {
SetError(EGL_BAD_CONFIG);
return EGL_NO_SURFACE;
}
if (!buffer) {
SetError(EGL_BAD_PARAMETER);
return EGL_NO_SURFACE;
}
EGLint width = 1;
EGLint height = 1;
if (auto value = ParseAttribValue(attribList, EGL_WIDTH); value) {
width = *value;
}
if (auto value = ParseAttribValue(attribList, EGL_HEIGHT); value) {
height = *value;
}
const auto surface = EncodeHandle<EGLSurfaceHandle>(m_nextSurfaceHandle++);
m_surfaces[surface] = SurfaceObject{
.Display = display,
.Config = config,
.Type = SurfaceType::PbufferFromClientBuffer,
.NativeHandleKey = 0,
.ClientBuffer = buffer,
.BufferType = bufferType,
.Width = width,
.Height = height,
};
return surface;
}
EGLContext::EGLSurfaceHandle EGLContext::CreatePlatformWindowSurface(EGLDisplayHandle display,
EGLConfigHandle config,
void* nativeWindow,
const EGLAttrib* attribList) {
const std::lock_guard<std::recursive_mutex> lock(m_mutex);
if (!IsDisplayInitialized(display)) {
SetError(EGL_NOT_INITIALIZED);
return EGL_NO_SURFACE;
}
if (!ValidateConfigOnDisplay(display, config)) {
SetError(EGL_BAD_CONFIG);
return EGL_NO_SURFACE;
}
if (ToNativeKey(nativeWindow) == 0) {
SetError(EGL_BAD_NATIVE_WINDOW);
return EGL_NO_SURFACE;
}
const auto surface = EncodeHandle<EGLSurfaceHandle>(m_nextSurfaceHandle++);
m_surfaces[surface] = SurfaceObject{
.Display = display,
.Config = config,
.Type = SurfaceType::PlatformWindow,
.NativeHandleKey = ToNativeKey(nativeWindow),
.ClientBuffer = nullptr,
.BufferType = EGL_NONE,
.Width = static_cast<EGLint>(ParseAttribValue(attribList, EGL_WIDTH).value_or(0)),
.Height = static_cast<EGLint>(ParseAttribValue(attribList, EGL_HEIGHT).value_or(0)),
};
return surface;
}
EGLContext::EGLSurfaceHandle EGLContext::CreatePlatformPixmapSurface(EGLDisplayHandle display,
EGLConfigHandle config,
void* nativePixmap,
const EGLAttrib* attribList) {
const std::lock_guard<std::recursive_mutex> lock(m_mutex);
(void)attribList;
if (!IsDisplayInitialized(display)) {
SetError(EGL_NOT_INITIALIZED);
return EGL_NO_SURFACE;
}
if (!ValidateConfigOnDisplay(display, config)) {
SetError(EGL_BAD_CONFIG);
return EGL_NO_SURFACE;
}
if (ToNativeKey(nativePixmap) == 0) {
SetError(EGL_BAD_NATIVE_PIXMAP);
return EGL_NO_SURFACE;
}
const auto surface = EncodeHandle<EGLSurfaceHandle>(m_nextSurfaceHandle++);
m_surfaces[surface] = SurfaceObject{
.Display = display,
.Config = config,
.Type = SurfaceType::PlatformPixmap,
.NativeHandleKey = ToNativeKey(nativePixmap),
.ClientBuffer = nullptr,
.BufferType = EGL_NONE,
};
return surface;
}
Bool EGLContext::DestroySurface(EGLDisplayHandle display, EGLSurfaceHandle surface) {
const std::lock_guard<std::recursive_mutex> lock(m_mutex);
auto surfaceIt = m_surfaces.find(surface);
if (surfaceIt == m_surfaces.end()) {
SetError(EGL_BAD_SURFACE);
return false;
}
if (surfaceIt->second.Display != display) {
SetError(EGL_BAD_MATCH);
return false;
}
if (IsSurfaceCurrentUnlocked(surface)) {
surfaceIt->second.DestroyPending = true;
return true;
}
m_surfaces.erase(surfaceIt);
return true;
}
Bool EGLContext::ResizeSurface(EGLDisplayHandle display, EGLSurfaceHandle surface,
EGLint width, EGLint height) {
const std::lock_guard<std::recursive_mutex> lock(m_mutex);
auto surfaceIt = m_surfaces.find(surface);
if (surfaceIt == m_surfaces.end()) {
SetError(EGL_BAD_SURFACE);
return false;
}
if (surfaceIt->second.Display != display) {
SetError(EGL_BAD_MATCH);
return false;
}
surfaceIt->second.Width = std::max<EGLint>(width, 1);
surfaceIt->second.Height = std::max<EGLint>(height, 1);
return true;
}
#if MOBILEGL_BUILD_DISAGGREGATED
// P12 (on-screen server window). See Core.h. Split builds only (G1).
EGLContext::EGLSurfaceHandle EGLContext::CreateServerOwnedWindowSurface(EGLDisplayHandle display,
EGLConfigHandle config,
const void* nativeWindow,
EGLint width, EGLint height,
Bool platformWindow) {
const std::lock_guard<std::recursive_mutex> lock(m_mutex);
if (!IsDisplayInitialized(display)) {
SetError(EGL_NOT_INITIALIZED);
return EGL_NO_SURFACE;
}
if (!ValidateConfigOnDisplay(display, config)) {
SetError(EGL_BAD_CONFIG);
return EGL_NO_SURFACE;
}
if (width < 0 || height < 0) {
SetError(EGL_BAD_ATTRIBUTE);
return EGL_NO_SURFACE;
}
// No EGL_BAD_NATIVE_WINDOW for a null window: the window is the server's.
const auto surface = EncodeHandle<EGLSurfaceHandle>(m_nextSurfaceHandle++);
m_surfaces[surface] = SurfaceObject{
.Display = display,
.Config = config,
.Type = platformWindow ? SurfaceType::PlatformWindow : SurfaceType::Window,
.NativeHandleKey = ToNativeKey(nativeWindow),
.ClientBuffer = nullptr,
.BufferType = EGL_NONE,
.Width = width,
.Height = height,
};
return surface;
}
// MOBILEGL_IPC_SURFACE=host: the registration is the server-owned one verbatim, because
// what makes a window surface host-framed is not its EGL state but where its pixels go -
// and that is decided one layer down, by the backend the creation calls next.
EGLContext::EGLSurfaceHandle EGLContext::CreateHostFrameWindowSurface(EGLDisplayHandle display,
EGLConfigHandle config,
const void* nativeWindow,
EGLint width, EGLint height,
Bool platformWindow) {
return CreateServerOwnedWindowSurface(display, config, nativeWindow, width, height,
platformWindow);
}
Bool EGLContext::SetSurfaceExtent(EGLSurfaceHandle surface, EGLint width, EGLint height) {
const std::lock_guard<std::recursive_mutex> lock(m_mutex);
auto surfaceIt = m_surfaces.find(surface);
if (surfaceIt == m_surfaces.end() || width < 0 || height < 0) {
return false;
}
surfaceIt->second.Width = width;
surfaceIt->second.Height = height;
return true;
}
#endif
Bool EGLContext::QuerySurface(EGLDisplayHandle display, EGLSurfaceHandle surface, EGLint attribute,
EGLint* value) const {
const std::lock_guard<std::recursive_mutex> lock(m_mutex);
if (!value) {
const_cast<EGLContext*>(this)->SetError(EGL_BAD_PARAMETER);
return false;
}
const auto* surfaceObject = TryGetSurface(surface);
if (!surfaceObject) {
const_cast<EGLContext*>(this)->SetError(EGL_BAD_SURFACE);
return false;
}
if (surfaceObject->Display != display) {
const_cast<EGLContext*>(this)->SetError(EGL_BAD_MATCH);
return false;
}
switch (attribute) {
case EGL_WIDTH:
*value = surfaceObject->Width;
return true;
case EGL_HEIGHT:
*value = surfaceObject->Height;
return true;
case EGL_TEXTURE_FORMAT:
*value = surfaceObject->TextureFormat;
return true;
case EGL_TEXTURE_TARGET:
*value = surfaceObject->TextureTarget;
return true;
case EGL_MIPMAP_LEVEL:
*value = surfaceObject->MipmapLevel;
return true;
case EGL_MIPMAP_TEXTURE:
*value = surfaceObject->MipmapTexture;
return true;
case EGL_RENDER_BUFFER:
*value = surfaceObject->RenderBuffer;
return true;
case EGL_SWAP_BEHAVIOR:
*value = surfaceObject->SwapBehavior;
return true;
case EGL_CONFIG_ID: {
const auto* cfg = TryGetConfig(surfaceObject->Config);
if (!cfg) {
const_cast<EGLContext*>(this)->SetError(EGL_BAD_CONFIG);
return false;
}
*value = cfg->ConfigId;
return true;
}
default:
const_cast<EGLContext*>(this)->SetError(EGL_BAD_ATTRIBUTE);
return false;
}
}
Bool EGLContext::ValidateSurface(EGLSurfaceHandle surface) const {
const std::lock_guard<std::recursive_mutex> lock(m_mutex);
return m_surfaces.find(surface) != m_surfaces.end();
}
Bool EGLContext::ValidateSurfaceOnDisplay(EGLDisplayHandle display, EGLSurfaceHandle surface) const {
const std::lock_guard<std::recursive_mutex> lock(m_mutex);
const auto* surf = TryGetSurface(surface);
return surf && surf->Display == display;
}
Bool EGLContext::SwapInterval(EGLDisplayHandle display, EGLint interval) {
const std::lock_guard<std::recursive_mutex> lock(m_mutex);
auto* displayObject = TryGetDisplay(display);
if (!displayObject) {
SetError(EGL_BAD_DISPLAY);
return false;
}
if (!displayObject->Initialized) {
SetError(EGL_NOT_INITIALIZED);
return false;
}
if (displayObject->Configs.empty()) {
SetError(EGL_BAD_CONFIG);
return false;
}
const auto* cfg = TryGetConfig(displayObject->Configs.front());
if (!cfg) {
SetError(EGL_BAD_CONFIG);
return false;
}
if (interval < cfg->MinSwapInterval || interval > cfg->MaxSwapInterval) {
SetError(EGL_BAD_PARAMETER);
return false;
}
displayObject->SwapInterval = interval;
return true;
}
Bool EGLContext::MakeCurrent(EGLDisplayHandle display, EGLSurfaceHandle draw, EGLSurfaceHandle read,
EGLContextHandle context) {
const std::lock_guard<std::recursive_mutex> lock(m_mutex);
const Bool releaseCurrentRequest =
draw == EGL_NO_SURFACE && read == EGL_NO_SURFACE && context == nullptr;
const auto threadKey = CurrentThreadKey();
if (releaseCurrentRequest) {
if (display != EGL_NO_DISPLAY) {
auto* displayObject = TryGetDisplay(display);
if (!displayObject) {
SetError(EGL_BAD_DISPLAY);
return false;
}
if (!displayObject->Initialized) {
SetError(EGL_NOT_INITIALIZED);
return false;
}
}
ReleaseThreadUnlocked(threadKey);
return true;
}
auto* displayObject = TryGetDisplay(display);
if (!displayObject) {
SetError(EGL_BAD_DISPLAY);
return false;
}
if (!displayObject->Initialized) {
SetError(EGL_NOT_INITIALIZED);
return false;
}
if (context != nullptr) {
if (!ValidateContextOnDisplay(display, context)) {
SetError(EGL_BAD_CONTEXT);
return false;
}
if (draw == EGL_NO_SURFACE || read == EGL_NO_SURFACE) {
SetError(EGL_BAD_MATCH);
return false;
}
} else {
if (draw != EGL_NO_SURFACE || read != EGL_NO_SURFACE) {
SetError(EGL_BAD_MATCH);
return false;
}
}
if (draw != EGL_NO_SURFACE && !ValidateSurfaceOnDisplay(display, draw)) {
SetError(EGL_BAD_SURFACE);
return false;
}
if (read != EGL_NO_SURFACE && !ValidateSurfaceOnDisplay(display, read)) {
SetError(EGL_BAD_SURFACE);
return false;
}
if (context != nullptr) {
auto ownerIt = m_contextOwners.find(context);
if (ownerIt != m_contextOwners.end() && ownerIt->second != threadKey) {
SetError(EGL_BAD_ACCESS);
return false;
}
}
ReleaseThreadUnlocked(threadKey);
m_threadCurrents[threadKey] = ThreadCurrentState{
.Display = display,
.DrawSurface = draw,
.ReadSurface = read,
.Context = context,
};
if (context != nullptr) {
m_contextOwners[context] = threadKey;
}
return true;
}
void EGLContext::ReleaseThread() {
const std::lock_guard<std::recursive_mutex> lock(m_mutex);
ReleaseThreadUnlocked(CurrentThreadKey());
}
EGLContext::EGLContextHandle EGLContext::GetCurrentContext() const {
const std::lock_guard<std::recursive_mutex> lock(m_mutex);
auto currentIt = m_threadCurrents.find(CurrentThreadKey());
if (currentIt == m_threadCurrents.end()) {
return nullptr;
}
return currentIt->second.Context;
}
EGLContext::EGLDisplayHandle EGLContext::GetCurrentDisplay() const {
const std::lock_guard<std::recursive_mutex> lock(m_mutex);
auto currentIt = m_threadCurrents.find(CurrentThreadKey());
if (currentIt == m_threadCurrents.end()) {
return EGL_NO_DISPLAY;
}
return currentIt->second.Display;
}
EGLContext::EGLSurfaceHandle EGLContext::GetCurrentSurface(EGLint readdraw) const {
const std::lock_guard<std::recursive_mutex> lock(m_mutex);
auto currentIt = m_threadCurrents.find(CurrentThreadKey());
if (currentIt == m_threadCurrents.end()) {
return EGL_NO_SURFACE;
}
if (readdraw == EGL_DRAW) {
return currentIt->second.DrawSurface;
}
if (readdraw == EGL_READ) {
return currentIt->second.ReadSurface;
}
return EGL_NO_SURFACE;
}
Bool EGLContext::IsDoubleBufferedSurface(EGLSurfaceHandle surface) const {
const std::lock_guard<std::recursive_mutex> lock(m_mutex);
const auto* surfaceObject = TryGetSurface(surface);
if (!surfaceObject) {
return false;
}
if (surfaceObject->RenderBuffer != EGL_BACK_BUFFER) {
return false;
}
return surfaceObject->Type == SurfaceType::Window ||
surfaceObject->Type == SurfaceType::PlatformWindow;
}
EGLContext::EGLSyncHandle EGLContext::CreateSync(EGLDisplayHandle display, EGLenum type,
const EGLAttrib* attribList) {
const std::lock_guard<std::recursive_mutex> lock(m_mutex);
if (!IsDisplayInitialized(display)) {
SetError(EGL_NOT_INITIALIZED);
return EGL_NO_SYNC;
}
if (type != EGL_SYNC_FENCE) {
SetError(EGL_BAD_ATTRIBUTE);
return EGL_NO_SYNC;
}
SyncObject sync = {
.Display = display,
.Type = type,
.Condition = EGL_SYNC_PRIOR_COMMANDS_COMPLETE,
.Status = EGL_SIGNALED,
};
if (auto value = ParseAttribValue(attribList, EGL_SYNC_CONDITION); value) {
sync.Condition = static_cast<EGLenum>(*value);
}
const auto syncHandle = EncodeHandle<EGLSyncHandle>(m_nextSyncHandle++);
m_syncs[syncHandle] = sync;
return syncHandle;
}
Bool EGLContext::DestroySync(EGLDisplayHandle display, EGLSyncHandle sync) {
const std::lock_guard<std::recursive_mutex> lock(m_mutex);
auto syncIt = m_syncs.find(sync);
if (syncIt == m_syncs.end()) {
SetError(EGL_BAD_PARAMETER);
return false;
}
if (syncIt->second.Display != display) {
SetError(EGL_BAD_MATCH);
return false;
}
m_syncs.erase(syncIt);
return true;
}
EGLint EGLContext::ClientWaitSync(EGLDisplayHandle display, EGLSyncHandle sync, EGLint flags,
EGLTime timeout) {
const std::lock_guard<std::recursive_mutex> lock(m_mutex);
(void)flags;
(void)timeout;
const auto* syncObject = TryGetSync(sync);
if (!syncObject) {
SetError(EGL_BAD_PARAMETER);
return EGL_FALSE;
}
if (syncObject->Display != display) {
SetError(EGL_BAD_MATCH);
return EGL_FALSE;
}
return EGL_CONDITION_SATISFIED;
}
Bool EGLContext::GetSyncAttrib(EGLDisplayHandle display, EGLSyncHandle sync, EGLint attribute,
EGLAttrib* value) const {
const std::lock_guard<std::recursive_mutex> lock(m_mutex);
if (!value) {
const_cast<EGLContext*>(this)->SetError(EGL_BAD_PARAMETER);
return false;
}
const auto* syncObject = TryGetSync(sync);
if (!syncObject) {
const_cast<EGLContext*>(this)->SetError(EGL_BAD_PARAMETER);
return false;
}
if (syncObject->Display != display) {
const_cast<EGLContext*>(this)->SetError(EGL_BAD_MATCH);
return false;
}
switch (attribute) {
case EGL_SYNC_TYPE:
*value = static_cast<EGLAttrib>(syncObject->Type);
return true;
case EGL_SYNC_STATUS:
*value = static_cast<EGLAttrib>(syncObject->Status);
return true;
case EGL_SYNC_CONDITION:
*value = static_cast<EGLAttrib>(syncObject->Condition);
return true;
default:
const_cast<EGLContext*>(this)->SetError(EGL_BAD_ATTRIBUTE);
return false;
}
}
Bool EGLContext::WaitSync(EGLDisplayHandle display, EGLSyncHandle sync, EGLint flags) const {
const std::lock_guard<std::recursive_mutex> lock(m_mutex);
(void)flags;
const auto* syncObject = TryGetSync(sync);
if (!syncObject) {
const_cast<EGLContext*>(this)->SetError(EGL_BAD_PARAMETER);
return false;
}
if (syncObject->Display != display) {
const_cast<EGLContext*>(this)->SetError(EGL_BAD_MATCH);
return false;
}
return true;
}
EGLContext::EGLImageHandle EGLContext::CreateImage(EGLDisplayHandle display, EGLContextHandle context,
EGLenum target, EGLClientBuffer buffer,
const EGLAttrib* attribList) {
const std::lock_guard<std::recursive_mutex> lock(m_mutex);
(void)attribList;
if (!IsDisplayInitialized(display)) {
SetError(EGL_NOT_INITIALIZED);
return EGL_NO_IMAGE;
}
if (context != nullptr && !ValidateContextOnDisplay(display, context)) {
SetError(EGL_BAD_CONTEXT);
return EGL_NO_IMAGE;
}
const auto image = EncodeHandle<EGLImageHandle>(m_nextImageHandle++);
m_images[image] = ImageObject{
.Display = display,
.Context = context,
.Target = target,
.Buffer = buffer,
};
return image;
}
Bool EGLContext::DestroyImage(EGLDisplayHandle display, EGLImageHandle image) {
const std::lock_guard<std::recursive_mutex> lock(m_mutex);
auto imageIt = m_images.find(image);
if (imageIt == m_images.end()) {
SetError(EGL_BAD_PARAMETER);
return false;
}
if (imageIt->second.Display != display) {
SetError(EGL_BAD_MATCH);
return false;
}
m_images.erase(imageIt);
return true;
}
} // namespace EGLState
// Leak-at-exit storage; see GlobalObjects.cpp.
UniquePtr<EGLState::EGLContext>& pEGLContext = *new UniquePtr<EGLState::EGLContext>();
} // namespace MG_State
} // namespace MobileGL