mirror of
https://github.com/MobileGL-Dev/MobileGL
synced 2026-11-07 01:53:24 +09:00
[Fix] (BackendLoader, DirectGLES): check EGL extensions before resolving damage swap entry points
This commit is contained in:
@@ -17767,6 +17767,8 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
||||
MGLOG_E_ONCE("DirectGLES::MakeCurrent failed: native eglMakeCurrent returned error 0x%04x", error);
|
||||
return false;
|
||||
}
|
||||
g_EGLFuncs.eglSwapBuffersWithDamageEXT =
|
||||
MG_Util::BackendLoader::ResolveSwapBuffersWithDamage(g_EGLFuncs);
|
||||
InvalidateEglVerifiedStamp();
|
||||
t_boundNativeContext = tuple.Context;
|
||||
t_boundNativeDraw = session.Draw;
|
||||
|
||||
@@ -1590,6 +1590,19 @@ TEST(ComputeWorkGroupCapabilities, TakesEveryAxisFromTheIndexedQuery) {
|
||||
}
|
||||
|
||||
namespace {
|
||||
const char* g_damageExtensions = nullptr;
|
||||
EGLDisplay g_damageDisplay = EGL_NO_DISPLAY;
|
||||
bool g_damageExtAvailable = false;
|
||||
bool g_damageKhrAvailable = false;
|
||||
std::vector<std::string> g_damageQueries;
|
||||
|
||||
EGLDisplay FakeDamageDisplay() { return g_damageDisplay; }
|
||||
const char* FakeDamageQueryString(EGLDisplay display, EGLint name) {
|
||||
EXPECT_EQ(display, g_damageDisplay);
|
||||
EXPECT_EQ(name, EGL_EXTENSIONS);
|
||||
return g_damageExtensions;
|
||||
}
|
||||
|
||||
EGLBoolean FakeSwapBuffersWithDamage(EGLDisplay dpy, EGLSurface surface, const EGLint* rects, EGLint nRects) {
|
||||
(void)dpy;
|
||||
(void)surface;
|
||||
@@ -1597,6 +1610,14 @@ namespace {
|
||||
(void)nRects;
|
||||
return EGL_TRUE;
|
||||
}
|
||||
|
||||
__eglMustCastToProperFunctionPointerType FakeDamageGetProcAddress(const char* name) {
|
||||
g_damageQueries.emplace_back(name);
|
||||
const bool available = std::string(name) == "eglSwapBuffersWithDamageEXT"
|
||||
? g_damageExtAvailable : g_damageKhrAvailable;
|
||||
return available ? reinterpret_cast<__eglMustCastToProperFunctionPointerType>(&FakeSwapBuffersWithDamage)
|
||||
: nullptr;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
// EXT -> KHR -> nothing, the whole policy behind the loader's single damage-present slot.
|
||||
@@ -1609,13 +1630,46 @@ namespace {
|
||||
// call to either spelling is defined to be equivalent to that anyway.
|
||||
TEST(SwapBuffersWithDamageResolution, PrefersExtThenKhrThenAnswersNothing) {
|
||||
using namespace MobileGL;
|
||||
MG_External::EGL::eglSwapBuffersWithDamageEXT_PTR ext = &FakeSwapBuffersWithDamage;
|
||||
MG_External::EGL::eglSwapBuffersWithDamageKHR_PTR khr = &FakeSwapBuffersWithDamage;
|
||||
MG_External::EGLFunctionsTable funcs{};
|
||||
funcs.eglGetCurrentDisplay = &FakeDamageDisplay;
|
||||
funcs.eglQueryString = &FakeDamageQueryString;
|
||||
funcs.eglGetProcAddress = &FakeDamageGetProcAddress;
|
||||
g_damageDisplay = reinterpret_cast<EGLDisplay>(1);
|
||||
g_damageExtensions = "EGL_EXT_swap_buffers_with_damage EGL_KHR_swap_buffers_with_damage";
|
||||
g_damageExtAvailable = g_damageKhrAvailable = true;
|
||||
g_damageQueries.clear();
|
||||
EXPECT_EQ(MG_Util::BackendLoader::ResolveSwapBuffersWithDamage(funcs), &FakeSwapBuffersWithDamage);
|
||||
EXPECT_EQ(g_damageQueries, (std::vector<std::string>{"eglSwapBuffersWithDamageEXT"}));
|
||||
|
||||
EXPECT_EQ(MG_Util::BackendLoader::ResolveSwapBuffersWithDamage(ext, khr), ext)
|
||||
<< "a driver that exports both spellings is answered with the EXT one";
|
||||
EXPECT_EQ(MG_Util::BackendLoader::ResolveSwapBuffersWithDamage(nullptr, khr), khr)
|
||||
<< "the KHR spelling alone must still fill the slot";
|
||||
EXPECT_EQ(MG_Util::BackendLoader::ResolveSwapBuffersWithDamage(nullptr, nullptr), nullptr)
|
||||
<< "neither spelling stays null, so the caller keeps eglSwapBuffers";
|
||||
g_damageExtAvailable = false;
|
||||
g_damageQueries.clear();
|
||||
EXPECT_EQ(MG_Util::BackendLoader::ResolveSwapBuffersWithDamage(funcs), &FakeSwapBuffersWithDamage);
|
||||
EXPECT_EQ(g_damageQueries, (std::vector<std::string>{"eglSwapBuffersWithDamageEXT", "eglSwapBuffersWithDamageKHR"}));
|
||||
|
||||
g_damageExtensions = "EGL_KHR_swap_buffers_with_damage";
|
||||
g_damageQueries.clear();
|
||||
EXPECT_EQ(MG_Util::BackendLoader::ResolveSwapBuffersWithDamage(funcs), &FakeSwapBuffersWithDamage);
|
||||
EXPECT_EQ(g_damageQueries, (std::vector<std::string>{"eglSwapBuffersWithDamageKHR"}));
|
||||
|
||||
g_damageKhrAvailable = false;
|
||||
EXPECT_EQ(MG_Util::BackendLoader::ResolveSwapBuffersWithDamage(funcs), nullptr);
|
||||
|
||||
g_damageExtensions = "EGL_EXT_swap_buffers_with_damage";
|
||||
g_damageQueries.clear();
|
||||
g_damageKhrAvailable = true;
|
||||
EXPECT_EQ(MG_Util::BackendLoader::ResolveSwapBuffersWithDamage(funcs), nullptr);
|
||||
EXPECT_EQ(g_damageQueries, (std::vector<std::string>{"eglSwapBuffersWithDamageEXT"}));
|
||||
|
||||
g_damageExtAvailable = true;
|
||||
for (const char* extensions : {"", "EGL_EXT_swap_buffers_with_damage_extra", static_cast<const char*>(nullptr)}) {
|
||||
g_damageExtensions = extensions;
|
||||
g_damageQueries.clear();
|
||||
EXPECT_EQ(MG_Util::BackendLoader::ResolveSwapBuffersWithDamage(funcs), nullptr);
|
||||
EXPECT_TRUE(g_damageQueries.empty());
|
||||
}
|
||||
g_damageExtensions = "EGL_EXT_swap_buffers_with_damage EGL_KHR_swap_buffers_with_damage";
|
||||
g_damageDisplay = EGL_NO_DISPLAY;
|
||||
g_damageQueries.clear();
|
||||
EXPECT_EQ(MG_Util::BackendLoader::ResolveSwapBuffersWithDamage(funcs), nullptr);
|
||||
EXPECT_TRUE(g_damageQueries.empty());
|
||||
}
|
||||
|
||||
@@ -181,12 +181,30 @@ namespace MobileGL::MG_Util::BackendLoader {
|
||||
}
|
||||
|
||||
MG_External::EGL::eglSwapBuffersWithDamageEXT_PTR
|
||||
ResolveSwapBuffersWithDamage(MG_External::EGL::eglSwapBuffersWithDamageEXT_PTR ext,
|
||||
MG_External::EGL::eglSwapBuffersWithDamageKHR_PTR khr) {
|
||||
if (ext != nullptr) {
|
||||
return ext;
|
||||
ResolveSwapBuffersWithDamage(const MG_External::EGLFunctionsTable& funcs) {
|
||||
if (!funcs.eglGetCurrentDisplay || !funcs.eglQueryString || !funcs.eglGetProcAddress) {
|
||||
return nullptr;
|
||||
}
|
||||
return (MG_External::EGL::eglSwapBuffersWithDamageEXT_PTR)khr;
|
||||
const EGLDisplay display = funcs.eglGetCurrentDisplay();
|
||||
if (display == EGL_NO_DISPLAY) {
|
||||
return nullptr;
|
||||
}
|
||||
const char* extensions = funcs.eglQueryString(display, EGL_EXTENSIONS);
|
||||
if (HasExtensionWord(extensions, "EGL_EXT_swap_buffers_with_damage")) {
|
||||
auto ext = reinterpret_cast<MG_External::EGL::eglSwapBuffersWithDamageEXT_PTR>(
|
||||
funcs.eglGetProcAddress("eglSwapBuffersWithDamageEXT"));
|
||||
if (ext != nullptr) {
|
||||
return ext;
|
||||
}
|
||||
}
|
||||
if (HasExtensionWord(extensions, "EGL_KHR_swap_buffers_with_damage")) {
|
||||
auto khr = reinterpret_cast<MG_External::EGL::eglSwapBuffersWithDamageKHR_PTR>(
|
||||
funcs.eglGetProcAddress("eglSwapBuffersWithDamageKHR"));
|
||||
if (khr != nullptr) {
|
||||
return khr;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void AcquireGLESFunctions(MG_External::GLESFunctionsTable& funcs,
|
||||
@@ -817,25 +835,8 @@ namespace MobileGL::MG_Util::BackendLoader {
|
||||
INIT_EGL_FUNC(eglWaitSync)
|
||||
}
|
||||
|
||||
{
|
||||
// One slot, two names. EGL_EXT_swap_buffers_with_damage and
|
||||
// EGL_KHR_swap_buffers_with_damage define the same entry point with the same
|
||||
// signature, and a driver may expose either, both or neither - Adreno exposes only
|
||||
// the KHR spelling - so the winner of the EXT -> KHR chain lands in the EXT slot.
|
||||
// Null when the driver offers neither, which is the normal "no damage-based present
|
||||
// here" answer: the caller keeps plain eglSwapBuffers (and note that a call with
|
||||
// n_rects == 0 is defined to be equivalent to eglSwapBuffers anyway).
|
||||
funcs.eglSwapBuffersWithDamageEXT = ResolveSwapBuffersWithDamage(
|
||||
reinterpret_cast<MG_External::EGL::eglSwapBuffersWithDamageEXT_PTR>(
|
||||
resolveEGLProc("eglSwapBuffersWithDamageEXT")),
|
||||
reinterpret_cast<MG_External::EGL::eglSwapBuffersWithDamageKHR_PTR>(
|
||||
resolveEGLProc("eglSwapBuffersWithDamageKHR")));
|
||||
if (funcs.eglSwapBuffersWithDamageEXT == nullptr) {
|
||||
MGLOG_I_ONCE("This driver has no damage-based present entry point (neither "
|
||||
"eglSwapBuffersWithDamageEXT nor eglSwapBuffersWithDamageKHR); "
|
||||
"present calls fall back to eglSwapBuffers");
|
||||
}
|
||||
}
|
||||
// Display extensions can only be checked after the display is initialized.
|
||||
funcs.eglSwapBuffersWithDamageEXT = nullptr;
|
||||
}
|
||||
|
||||
// Detects whether indirect draws leak the command's baseInstance word ("reserved, must
|
||||
|
||||
@@ -124,7 +124,7 @@ namespace MobileGL {
|
||||
EGL_FUNC_DECL(eglSurfaceAttrib)
|
||||
EGL_FUNC_DECL(eglSwapBuffers)
|
||||
// Holds whichever spelling of the damage-based present the driver provided, EXT
|
||||
// preferred; null when it provides neither (see AcquireEGLFunctions).
|
||||
// preferred; resolved after making the display current.
|
||||
EGL_FUNC_DECL(eglSwapBuffersWithDamageEXT)
|
||||
EGL_FUNC_DECL(eglSwapInterval)
|
||||
EGL_FUNC_DECL(eglTerminate)
|
||||
@@ -1369,14 +1369,14 @@ namespace MobileGL {
|
||||
void AcquireGLESFunctions(MG_External::GLESFunctionsTable& funcs,
|
||||
MG_External::EGL::eglGetProcAddress_PTR procAddress);
|
||||
void AcquireEGLFunctions(MG_External::EGLFunctionsTable& funcs);
|
||||
// The EXT -> KHR -> nothing chain for the damage-based present entry point. A null
|
||||
// Query the current display's extensions before resolving EXT -> KHR -> nothing.
|
||||
// Requires a current, initialized EGL display. A null
|
||||
// result is not a failure: it means this driver offers neither spelling, and the
|
||||
// caller presents with plain eglSwapBuffers, which a zero-rectangle call to either
|
||||
// spelling is defined to be equivalent to anyway. Exposed so MG_Test can pin the
|
||||
// preference order without a driver.
|
||||
MG_External::EGL::eglSwapBuffersWithDamageEXT_PTR
|
||||
ResolveSwapBuffersWithDamage(MG_External::EGL::eglSwapBuffersWithDamageEXT_PTR ext,
|
||||
MG_External::EGL::eglSwapBuffersWithDamageKHR_PTR khr);
|
||||
ResolveSwapBuffersWithDamage(const MG_External::EGLFunctionsTable& funcs);
|
||||
Bool FillInGLESCapabilities(MG_External::GLESCapabilities& caps,
|
||||
const MG_External::GLESFunctionsTable& glesFuncs);
|
||||
// Detects whether indirect draws leak the command's baseInstance word into
|
||||
|
||||
Reference in New Issue
Block a user