mirror of
https://github.com/MobileGL-Dev/MobileGL
synced 2026-11-07 01:53:24 +09:00
[Fix] (EGL+GL): let ANGLE's GL backend run on MobileGL (GBM displays, foreign wl_display, precision query, ES-free GL_VERSION)
This commit is contained in:
@@ -194,6 +194,19 @@ namespace MobileGL::MG_Impl::EGLImpl {
|
||||
// reads the frame back and attaches it to the wl_surface (WaylandWindow.h). Answers false
|
||||
// when the display is not a Wayland one; otherwise *out is the surface, or EGL_NO_SURFACE
|
||||
// with the error set.
|
||||
// A gbm_surface is scanout memory of a device this library does not draw with; a window
|
||||
// on a GBM display is refused rather than taken for some other kind of window.
|
||||
template <typename State>
|
||||
Bool RefuseGbmWindow(State* state, EGLDisplay dpy) {
|
||||
constexpr EGLenum kPlatformGbm = 0x31D7; // EGL_PLATFORM_GBM_KHR == EGL_PLATFORM_GBM_MESA
|
||||
Uint64 nativeDisplay = 0;
|
||||
EGLenum platform = EGL_NONE;
|
||||
if (!state->GetDisplayNative(dpy, &nativeDisplay, &platform) || platform != kPlatformGbm) return false;
|
||||
MGLOG_W_ONCE("EGL: window surfaces on a GBM display are not supported (offscreen display only)");
|
||||
state->SetError(EGL_BAD_NATIVE_WINDOW);
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename State>
|
||||
Bool CreateWaylandWindowSurface(State* state, EGLDisplay dpy, EGLConfig config, void* window,
|
||||
Bool platformWindow, EGLSurface* out) {
|
||||
@@ -274,6 +287,7 @@ namespace MobileGL::MG_Impl::EGLImpl {
|
||||
return EGL_NO_SURFACE;
|
||||
}
|
||||
#if MOBILEGL_WAYLAND_WINDOWS
|
||||
if (RefuseGbmWindow(state, dpy)) return EGL_NO_SURFACE;
|
||||
{
|
||||
const std::lock_guard<std::recursive_mutex> operationLock(EGLOperationMutex());
|
||||
EGLSurface waylandSurface = EGL_NO_SURFACE;
|
||||
@@ -960,6 +974,16 @@ namespace MobileGL::MG_Impl::EGLImpl {
|
||||
if (!state) {
|
||||
return EGL_NO_DISPLAY;
|
||||
}
|
||||
#if MOBILEGL_WAYLAND_WINDOWS
|
||||
// A "GBM" display whose native display is a wl_display is a Wayland one: Chromium's
|
||||
// Wayland GL layer names the GBM platform for every display it asks for, including the
|
||||
// one on its own Wayland connection whose windows are wl_egl_windows.
|
||||
constexpr EGLenum kPlatformGbm = 0x31D7;
|
||||
if (platform == kPlatformGbm && native_display != nullptr &&
|
||||
Wayland::IsWaylandDisplay(reinterpret_cast<Uint64>(native_display), EGL_NONE)) {
|
||||
platform = Wayland::kPlatformWayland;
|
||||
}
|
||||
#endif
|
||||
return state->GetPlatformDisplay(platform, native_display);
|
||||
}
|
||||
|
||||
@@ -970,6 +994,7 @@ namespace MobileGL::MG_Impl::EGLImpl {
|
||||
return EGL_NO_SURFACE;
|
||||
}
|
||||
#if MOBILEGL_WAYLAND_WINDOWS
|
||||
if (RefuseGbmWindow(state, dpy)) return EGL_NO_SURFACE;
|
||||
{
|
||||
const std::lock_guard<std::recursive_mutex> operationLock(EGLOperationMutex());
|
||||
EGLSurface waylandSurface = EGL_NO_SURFACE;
|
||||
|
||||
@@ -6,7 +6,12 @@ namespace MobileGL::MG_Impl::EGLImpl {
|
||||
// are absent until their entry points and image paths exist.
|
||||
inline constexpr const char* kDeviceExtensionString = "";
|
||||
#if defined(__linux__) && !defined(__ANDROID__)
|
||||
#define MOBILEGL_PLATFORM_WAYLAND_EXTENSIONS " EGL_KHR_platform_wayland EGL_EXT_platform_wayland"
|
||||
// GBM is claimed as a display platform only: a display on a gbm_device is an offscreen display
|
||||
// like a surfaceless one (the frames come from the backend, not from the device), and window
|
||||
// surfaces on it are refused. Chromium's Wayland GPU process hands its GL implementation nothing
|
||||
// but a GBM display, so without the claim it never reaches this library at all.
|
||||
#define MOBILEGL_PLATFORM_WAYLAND_EXTENSIONS \
|
||||
" EGL_KHR_platform_wayland EGL_EXT_platform_wayland EGL_KHR_platform_gbm EGL_MESA_platform_gbm"
|
||||
#else
|
||||
#define MOBILEGL_PLATFORM_WAYLAND_EXTENSIONS ""
|
||||
#endif
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
#include <fcntl.h>
|
||||
#include <mutex>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/uio.h>
|
||||
#include <unistd.h>
|
||||
|
||||
namespace MobileGL::MG_Impl::EGLImpl {
|
||||
@@ -302,12 +303,32 @@ namespace MobileGL::MG_Impl::EGLImpl::Wayland {
|
||||
void OnWindowDestroyed(void* data) { static_cast<WindowSurface::Impl*>(data)->window = nullptr; }
|
||||
} // namespace
|
||||
|
||||
namespace {
|
||||
// Copies `size` bytes at `address` of this process, or answers false where nothing is
|
||||
// mapped - the kernel does the reading, so a wild pointer is an error code, not a fault.
|
||||
Bool ReadOwnMemory(const void* address, void* out, SizeT size) {
|
||||
iovec local{out, size};
|
||||
iovec remote{const_cast<void*>(address), size};
|
||||
return process_vm_readv(getpid(), &local, 1, &remote, 1, 0) == static_cast<ssize_t>(size);
|
||||
}
|
||||
} // namespace
|
||||
|
||||
Bool IsWaylandDisplay(Uint64 nativeDisplay, EGLenum platform) {
|
||||
if (platform == kPlatformWayland) return true;
|
||||
if (nativeDisplay == 0 || (platform != EGL_NONE && platform != 0)) return false;
|
||||
const Api& api = WaylandApi();
|
||||
if (!api.loaded) return false;
|
||||
return *reinterpret_cast<const void* const*>(static_cast<uintptr_t>(nativeDisplay)) == api.displayInterface;
|
||||
const void* interface = *reinterpret_cast<const void* const*>(static_cast<uintptr_t>(nativeDisplay));
|
||||
if (interface == api.displayInterface) return true;
|
||||
// AN APPLICATION WITH ITS OWN COPY OF LIBWAYLAND-CLIENT (Chromium links one in statically)
|
||||
// has its own wl_display_interface, at its own address. The interface is still the
|
||||
// protocol's: a wl_interface whose first member names it "wl_display". Whatever else the
|
||||
// first word of a native display is (a gbm_device's is a function address), the probe
|
||||
// only reads through the kernel.
|
||||
const char* name = nullptr;
|
||||
char text[sizeof("wl_display")] = {};
|
||||
return interface != nullptr && ReadOwnMemory(interface, &name, sizeof(name)) && name != nullptr &&
|
||||
ReadOwnMemory(name, text, sizeof(text)) && std::memcmp(text, "wl_display", sizeof(text)) == 0;
|
||||
}
|
||||
|
||||
Bool IsWaylandWindow(const void* window) {
|
||||
|
||||
@@ -44,7 +44,8 @@ namespace MobileGL::MG_Impl::EGLImpl::Wayland {
|
||||
|
||||
// Whether a display is a Wayland one: asked for as EGL_PLATFORM_WAYLAND, or (eglGetDisplay with
|
||||
// no platform) a native display that is a wl_display - recognised the way Mesa does, by its
|
||||
// first word being libwayland-client's wl_display_interface.
|
||||
// first word being libwayland-client's wl_display_interface (or, for an application with its
|
||||
// own copy of libwayland, an interface named "wl_display").
|
||||
Bool IsWaylandDisplay(Uint64 nativeDisplay, EGLenum platform);
|
||||
|
||||
// Whether `window` looks like a wl_egl_window this library can present to.
|
||||
|
||||
@@ -156,7 +156,7 @@ DECLARE_GL_FUNCTION_HEAD(void, GetProgramInfoLog, GLuint program, GLsizei bufSiz
|
||||
DECLARE_GL_FUNCTION_HEAD(void, GetRenderbufferParameteriv, GLenum target, GLenum pname, GLint* params) DECLARE_GL_FUNCTION_END_NO_RETURN(void, GetRenderbufferParameteriv, target, pname, params)
|
||||
DECLARE_GL_FUNCTION_HEAD(void, GetShaderiv, GLuint shader, GLenum pname, GLint* params) DECLARE_GL_FUNCTION_END_NO_RETURN(void, GetShaderiv, shader, pname, params)
|
||||
DECLARE_GL_FUNCTION_HEAD(void, GetShaderInfoLog, GLuint shader, GLsizei bufSize, GLsizei* length, GLchar* infoLog) DECLARE_GL_FUNCTION_END_NO_RETURN(void, GetShaderInfoLog, shader, bufSize, length, infoLog)
|
||||
DECLARE_GL_FUNCTION_STUB_HEAD(void, GetShaderPrecisionFormat, GLenum shadertype, GLenum precisiontype, GLint* range, GLint* precision) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, GetShaderPrecisionFormat, shadertype, precisiontype, range, precision)
|
||||
DECLARE_GL_FUNCTION_HEAD(void, GetShaderPrecisionFormat, GLenum shadertype, GLenum precisiontype, GLint* range, GLint* precision) DECLARE_GL_FUNCTION_END_NO_RETURN(void, GetShaderPrecisionFormat, shadertype, precisiontype, range, precision)
|
||||
DECLARE_GL_FUNCTION_HEAD(void, GetShaderSource, GLuint shader, GLsizei bufSize, GLsizei* length, GLchar* source) DECLARE_GL_FUNCTION_END_NO_RETURN(void, GetShaderSource, shader, bufSize, length, source)
|
||||
DECLARE_GL_FUNCTION_HEAD(void, GetTexParameterfv, GLenum target, GLenum pname, GLfloat* params) DECLARE_GL_FUNCTION_END_NO_RETURN(void, GetTexParameterfv, target, pname, params)
|
||||
DECLARE_GL_FUNCTION_HEAD(void, GetTexParameteriv, GLenum target, GLenum pname, GLint* params) DECLARE_GL_FUNCTION_END_NO_RETURN(void, GetTexParameteriv, target, pname, params)
|
||||
|
||||
@@ -617,11 +617,19 @@ namespace MobileGL::MG_Impl::GLImpl {
|
||||
MGLOG_D("vendorString: %s", vendorString.c_str());
|
||||
return (const GLubyte*)vendorString.c_str();
|
||||
case GL_VERSION: {
|
||||
// "OpenGL ES" must not appear anywhere in a desktop GL_VERSION: some callers (ANGLE's GL
|
||||
// backend among them) search the whole string for it rather than its prefix, and
|
||||
// then take this for an ES driver, read the version from the wrong offsets and look
|
||||
// for ES-named extensions. The backend is named "GLES" here for that reason only.
|
||||
String backendName = rendererInfo.BackendName;
|
||||
if (const auto at = backendName.find("OpenGL ES"); at != String::npos) {
|
||||
backendName.replace(at, sizeof("OpenGL ES") - 1, "GLES");
|
||||
}
|
||||
versionStr =
|
||||
std::format("{} {} {}, {} Backend, GIT@" GIT_COMMIT_HASH_SHORT,
|
||||
rendererInfo.RendererGLInfo.TargetGLVersion.toString(), MG_Config::ProjectName,
|
||||
MG_Config::CoreVersion.toFormattedString(MG_Config::DefaultVersionStringFormatAttrib),
|
||||
rendererInfo.BackendName);
|
||||
backendName);
|
||||
MGLOG_D("versionStr: %s", versionStr.c_str());
|
||||
return (const GLubyte*)versionStr.c_str();
|
||||
}
|
||||
|
||||
@@ -2405,6 +2405,47 @@ namespace MobileGL::MG_Impl::GLImpl {
|
||||
ShaderBinary_State(count, shaders, binaryformat, binary, length);
|
||||
}
|
||||
|
||||
// GL 4.6 core 7.13 (ARB_ES2_compatibility). The shading language here is desktop GLSL, whose
|
||||
// float and int are 32-bit IEEE-754 / two's complement at every precision qualifier, so every
|
||||
// precision answers the same: log2 of the range and the mantissa bits. A silent stub answered
|
||||
// zeros, which an ES-on-GL layer reads as "no highp" and refuses ES 2.0 outright.
|
||||
void GetShaderPrecisionFormat(GLenum shadertype, GLenum precisiontype, GLint* range, GLint* precision) {
|
||||
if (shadertype != GL_VERTEX_SHADER && shadertype != GL_FRAGMENT_SHADER) {
|
||||
MG_State::pGLContext->RecordError(
|
||||
ErrorCode::InvalidEnum,
|
||||
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", __func__,
|
||||
"shadertype must be GL_VERTEX_SHADER or GL_FRAGMENT_SHADER."));
|
||||
return;
|
||||
}
|
||||
GLint rangeLow = 0, rangeHigh = 0, bits = 0;
|
||||
switch (precisiontype) {
|
||||
case GL_LOW_FLOAT:
|
||||
case GL_MEDIUM_FLOAT:
|
||||
case GL_HIGH_FLOAT:
|
||||
rangeLow = 127;
|
||||
rangeHigh = 127;
|
||||
bits = 23;
|
||||
break;
|
||||
case GL_LOW_INT:
|
||||
case GL_MEDIUM_INT:
|
||||
case GL_HIGH_INT:
|
||||
rangeLow = 31;
|
||||
rangeHigh = 30;
|
||||
bits = 0;
|
||||
break;
|
||||
default:
|
||||
MG_State::pGLContext->RecordError(
|
||||
ErrorCode::InvalidEnum,
|
||||
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", __func__, "precisiontype is not a precision."));
|
||||
return;
|
||||
}
|
||||
if (range) {
|
||||
range[0] = rangeLow;
|
||||
range[1] = rangeHigh;
|
||||
}
|
||||
if (precision) *precision = bits;
|
||||
}
|
||||
|
||||
void SpecializeShader(GLuint shader, const GLchar* pEntryPoint, GLuint numSpecializationConstants,
|
||||
const GLuint* pConstantIndex, const GLuint* pConstantValue) {
|
||||
SpecializeShader_State(shader, pEntryPoint, numSpecializationConstants, pConstantIndex, pConstantValue);
|
||||
|
||||
@@ -41,6 +41,7 @@ namespace MobileGL::MG_Impl::GLImpl {
|
||||
void GetShaderiv(GLuint shader, GLenum pname, GLint* params);
|
||||
void GetShaderInfoLog(GLuint shader, GLsizei bufSize, GLsizei* length, GLchar* infoLog);
|
||||
void GetShaderSource(GLuint shader, GLsizei bufSize, GLsizei* length, GLchar* source);
|
||||
void GetShaderPrecisionFormat(GLenum shadertype, GLenum precisiontype, GLint* range, GLint* precision);
|
||||
GLint GetUniformLocation(GLuint program, const GLchar* name);
|
||||
void GetUniformfv(GLuint program, GLint location, GLfloat* params);
|
||||
void GetUniformiv(GLuint program, GLint location, GLint* params);
|
||||
|
||||
Reference in New Issue
Block a user