diff --git a/MobileGL/MG_Impl/EGLImpl/EGLImpl.cpp b/MobileGL/MG_Impl/EGLImpl/EGLImpl.cpp index cc7b84e3..9310c7e9 100644 --- a/MobileGL/MG_Impl/EGLImpl/EGLImpl.cpp +++ b/MobileGL/MG_Impl/EGLImpl/EGLImpl.cpp @@ -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 + 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 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 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(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 operationLock(EGLOperationMutex()); EGLSurface waylandSurface = EGL_NO_SURFACE; diff --git a/MobileGL/MG_Impl/EGLImpl/EGLPlatformExtensions.h b/MobileGL/MG_Impl/EGLImpl/EGLPlatformExtensions.h index 6709d854..09b3ea52 100644 --- a/MobileGL/MG_Impl/EGLImpl/EGLPlatformExtensions.h +++ b/MobileGL/MG_Impl/EGLImpl/EGLPlatformExtensions.h @@ -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 diff --git a/MobileGL/MG_Impl/EGLImpl/WaylandWindow.cpp b/MobileGL/MG_Impl/EGLImpl/WaylandWindow.cpp index 69d8bc7a..0384d57b 100644 --- a/MobileGL/MG_Impl/EGLImpl/WaylandWindow.cpp +++ b/MobileGL/MG_Impl/EGLImpl/WaylandWindow.cpp @@ -23,6 +23,7 @@ #include #include #include +#include #include namespace MobileGL::MG_Impl::EGLImpl { @@ -302,12 +303,32 @@ namespace MobileGL::MG_Impl::EGLImpl::Wayland { void OnWindowDestroyed(void* data) { static_cast(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(address), size}; + return process_vm_readv(getpid(), &local, 1, &remote, 1, 0) == static_cast(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(static_cast(nativeDisplay)) == api.displayInterface; + const void* interface = *reinterpret_cast(static_cast(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) { diff --git a/MobileGL/MG_Impl/EGLImpl/WaylandWindow.h b/MobileGL/MG_Impl/EGLImpl/WaylandWindow.h index 4fc984ab..384f41ff 100644 --- a/MobileGL/MG_Impl/EGLImpl/WaylandWindow.h +++ b/MobileGL/MG_Impl/EGLImpl/WaylandWindow.h @@ -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. diff --git a/MobileGL/MG_Impl/GLImpl/Exporting/Definitions.cpp b/MobileGL/MG_Impl/GLImpl/Exporting/Definitions.cpp index ec41f1cb..d38f161e 100644 --- a/MobileGL/MG_Impl/GLImpl/Exporting/Definitions.cpp +++ b/MobileGL/MG_Impl/GLImpl/Exporting/Definitions.cpp @@ -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) diff --git a/MobileGL/MG_Impl/GLImpl/Getter/GL_Getter.cpp b/MobileGL/MG_Impl/GLImpl/Getter/GL_Getter.cpp index 27876ad7..e47779ea 100644 --- a/MobileGL/MG_Impl/GLImpl/Getter/GL_Getter.cpp +++ b/MobileGL/MG_Impl/GLImpl/Getter/GL_Getter.cpp @@ -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(); } diff --git a/MobileGL/MG_Impl/GLImpl/Program/GL_Program.cpp b/MobileGL/MG_Impl/GLImpl/Program/GL_Program.cpp index dfbe2ca4..3db44c8d 100644 --- a/MobileGL/MG_Impl/GLImpl/Program/GL_Program.cpp +++ b/MobileGL/MG_Impl/GLImpl/Program/GL_Program.cpp @@ -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("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("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); diff --git a/MobileGL/MG_Impl/GLImpl/Program/GL_Program.h b/MobileGL/MG_Impl/GLImpl/Program/GL_Program.h index 5dd7bc8a..ea8d66a0 100644 --- a/MobileGL/MG_Impl/GLImpl/Program/GL_Program.h +++ b/MobileGL/MG_Impl/GLImpl/Program/GL_Program.h @@ -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);