mirror of
https://github.com/MobileGL-Dev/MobileGL
synced 2026-11-07 01:53:24 +09:00
[Fix] (EGL): decline another backend's GBM device and ask X for a visual only on X displays
This commit is contained in:
@@ -25,8 +25,12 @@
|
||||
// file must not name ServerLoop's forwarders directly.
|
||||
#include <MG_Remote/Client/WireTables.h>
|
||||
#endif
|
||||
#include <cstring>
|
||||
#include <mutex>
|
||||
#include <sstream>
|
||||
#if defined(__linux__) && !defined(__ANDROID__)
|
||||
#include <dlfcn.h>
|
||||
#endif
|
||||
#include <type_traits>
|
||||
#include <unordered_set>
|
||||
|
||||
@@ -1193,6 +1197,32 @@ namespace MobileGL::MG_Impl::EGLImpl {
|
||||
(void)attrib_list;
|
||||
|
||||
// As GetDisplay.
|
||||
constexpr EGLenum kPlatformGbm = 0x31D7;
|
||||
#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.
|
||||
if (platform == kPlatformGbm && native_display != nullptr &&
|
||||
Wayland::IsWaylandDisplay(reinterpret_cast<Uint64>(native_display), EGL_NONE)) {
|
||||
platform = Wayland::kPlatformWayland;
|
||||
}
|
||||
#endif
|
||||
#if defined(__linux__) && !defined(__ANDROID__)
|
||||
// A GBM device some other backend created (the system's own driver's) is not one this
|
||||
// implementation can draw for: its buffers come from that driver, never from the server.
|
||||
// Declined before anything is brought up, so a loader with more than one EGL vendor hands
|
||||
// the device to the one it belongs to. Only a device whose backend names itself is asked
|
||||
// about; one from a loader too old to say is served as before.
|
||||
if (platform == kPlatformGbm && native_display != nullptr) {
|
||||
using BackendNameFn = const char* (*)(void*);
|
||||
static const auto backendName =
|
||||
reinterpret_cast<BackendNameFn>(dlsym(RTLD_DEFAULT, "gbm_device_get_backend_name"));
|
||||
const char* name = backendName != nullptr ? backendName(native_display) : nullptr;
|
||||
if (name != nullptr && std::strcmp(name, "mobilegl") != 0) {
|
||||
return EGL_NO_DISPLAY;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
if (!MobileGL::ImplementationAvailable()) {
|
||||
return EGL_NO_DISPLAY;
|
||||
}
|
||||
@@ -1200,16 +1230,6 @@ 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);
|
||||
}
|
||||
|
||||
|
||||
@@ -152,6 +152,18 @@ namespace MobileGL {
|
||||
|
||||
displayObject.Configs.push_back(CreateDefaultConfig(display, 1, 0));
|
||||
displayObject.Configs.push_back(CreateDefaultConfig(display, 2, 8));
|
||||
#if !defined(ANDROID) && !defined(__ANDROID__)
|
||||
// The X visual only means something on an X display: the default display (which is
|
||||
// DISPLAY's) or an explicit X11 one. Asking the X server on any other platform
|
||||
// costs a connection a Wayland/GBM/surfaceless client never needed - and an X
|
||||
// server that is itself creating this display (a rootless X server's GL
|
||||
// acceleration) would be waiting on its own connection.
|
||||
constexpr EGLenum kPlatformX11 = 0x31D5;
|
||||
if (platform == EGL_NONE || platform == kPlatformX11) {
|
||||
const EGLint visualId = QueryDefaultX11VisualId();
|
||||
for (const auto config : displayObject.Configs) m_configs[config].NativeVisualId = visualId;
|
||||
}
|
||||
#endif
|
||||
|
||||
m_displays[display] = displayObject;
|
||||
m_displayLookup[key] = display;
|
||||
@@ -174,7 +186,7 @@ namespace MobileGL {
|
||||
.RenderableType = EGL_OPENGL_BIT | EGL_OPENGL_ES2_BIT | EGL_OPENGL_ES3_BIT,
|
||||
.MinSwapInterval = 0,
|
||||
.MaxSwapInterval = 4,
|
||||
.NativeVisualId = QueryDefaultX11VisualId(),
|
||||
.NativeVisualId = 0, // the display's X visual, set by GetOrCreateDisplay
|
||||
};
|
||||
#if defined(ANDROID) || defined(__ANDROID__)
|
||||
cfg.NativeVisualId = AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM;
|
||||
|
||||
@@ -121,8 +121,10 @@ foreach (test IN LISTS MOBILEGL_WIRE_TESTS)
|
||||
endif ()
|
||||
|
||||
if (test STREQUAL "ServerProbeTest")
|
||||
# It calls the glvnd vendor entry point the way libEGL does.
|
||||
# It calls the glvnd vendor entry point the way libEGL does, and stands in for the GBM
|
||||
# loader's backend-name query with a symbol of its own, which must be visible to dlsym.
|
||||
target_include_directories(${test} PRIVATE ${MGL_ROOT}/3rdparty/glvnd/include)
|
||||
set_target_properties(${test} PROPERTIES ENABLE_EXPORTS ON)
|
||||
endif ()
|
||||
|
||||
if (test STREQUAL "ServerSpawnTest" OR test STREQUAL "PeerLatchTest" OR test STREQUAL "InProcessServerTest" OR
|
||||
|
||||
@@ -37,6 +37,15 @@ extern "C" int __glx_Main(uint32_t version, const void* exports, void* vendor, v
|
||||
|
||||
using namespace MobileGL;
|
||||
|
||||
// A stand-in for the GBM loader's query, found by the implementation through the process's global
|
||||
// symbols (the target exports them): a "device" here is just the backend name it reports.
|
||||
struct FakeGbmDevice {
|
||||
const char* backend;
|
||||
};
|
||||
extern "C" __attribute__((visibility("default"))) const char* gbm_device_get_backend_name(void* device) {
|
||||
return static_cast<FakeGbmDevice*>(device)->backend;
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
namespace EGL = MobileGL::MG_Impl::EGLImpl;
|
||||
@@ -248,6 +257,51 @@ namespace {
|
||||
std::filesystem::remove(Debug::RoleLogPath(logBase.c_str(), Debug::LogRole::Client), ec);
|
||||
}
|
||||
|
||||
// A GBM device another backend created is declined before MobileGL brings anything up (so a
|
||||
// loader with a second vendor gives it to its owner); one of MobileGL's own is served.
|
||||
TEST_F(ServerProbeTest, AForeignGbmDeviceIsDeclinedAndOneOfOursIsServed) {
|
||||
P12::ServerProcess server;
|
||||
if (!P12::LaunchSupervisor("probe-gbm", &server)) GTEST_SKIP() << "no `--serve` supervisor on this host";
|
||||
const std::string logBase = "/tmp/" + UniqueName("gbm") + ".log";
|
||||
int fds[2] = {-1, -1};
|
||||
ASSERT_EQ(::pipe(fds), 0);
|
||||
std::fflush(nullptr);
|
||||
const pid_t pid = ::fork();
|
||||
if (pid == 0) {
|
||||
::close(fds[0]);
|
||||
::setenv("MOBILEGL_LOG_FILE_PATH", logBase.c_str(), 1);
|
||||
::setenv("MOBILEGL_TRANSPORT", "spawn", 1);
|
||||
::setenv("MOBILEGL_IPC_CONTROL", server.endpoint.c_str(), 1);
|
||||
::setenv("MOBILEGL_BACKEND_TYPE", "DirectGLES", 1);
|
||||
constexpr EGLenum kPlatformGbm = 0x31D7;
|
||||
FakeGbmDevice foreign{"drm"};
|
||||
FakeGbmDevice ours{"mobilegl"};
|
||||
Int32 result[3] = {-1, -1, -1};
|
||||
result[0] = EGL::GetPlatformDisplay(kPlatformGbm, &foreign, nullptr) != EGL_NO_DISPLAY;
|
||||
result[1] = MobileGL::MG_Remote::Client::ClientSession::Active() != nullptr; // nothing brought up for it
|
||||
result[2] = EGL::GetPlatformDisplay(kPlatformGbm, &ours, nullptr) != EGL_NO_DISPLAY;
|
||||
const ssize_t wrote = ::write(fds[1], result, sizeof(result));
|
||||
(void)wrote;
|
||||
std::fflush(nullptr);
|
||||
::_exit(0);
|
||||
}
|
||||
::close(fds[1]);
|
||||
Int32 result[3] = {-1, -1, -1};
|
||||
pollfd pfd{fds[0], POLLIN, 0};
|
||||
if (::poll(&pfd, 1, 60000) > 0) {
|
||||
const ssize_t got = ::read(fds[0], result, sizeof(result));
|
||||
(void)got;
|
||||
}
|
||||
::close(fds[0]);
|
||||
::kill(pid, SIGKILL);
|
||||
::waitpid(pid, nullptr, 0);
|
||||
EXPECT_EQ(result[0], 0) << "a device from another GBM backend must be declined";
|
||||
EXPECT_EQ(result[1], 0) << "and declined before the session was brought up";
|
||||
EXPECT_EQ(result[2], 1) << "a device of MobileGL's own GBM backend is served";
|
||||
std::error_code ec;
|
||||
std::filesystem::remove(Debug::RoleLogPath(logBase.c_str(), Debug::LogRole::Client), ec);
|
||||
}
|
||||
|
||||
TEST_F(ServerProbeTest, WithAServerListeningADisplayIsHandedOut) {
|
||||
P12::ServerProcess server;
|
||||
if (!P12::LaunchSupervisor("probe", &server)) GTEST_SKIP() << "no `--serve` supervisor on this host";
|
||||
|
||||
Reference in New Issue
Block a user