[Fix] (Espryt): swap a pbuffer without damage rectangles

This commit is contained in:
2026-10-02 16:27:16 -04:00
parent 74919a3bc1
commit 233e2f8ced
3 changed files with 36 additions and 7 deletions
+12 -7
View File
@@ -18159,21 +18159,26 @@ namespace MobileGL::MG_Backend::DirectGLES {
//
// The damage is the client's, from the present record (CurrentPresentDamage): GL window
// coordinates, which is what the native call takes. Whole surface = no rectangles.
const EGLSurface drawSurface = ActiveNativeSession().Draw;
//
// ONLY A WINDOW SURFACE TAKES RECTANGLES: the platform's swap-with-damage hands them to the
// surface's native window, and a pbuffer has none (Android's dereferences it regardless).
NativeSessionState& session = ActiveNativeSession(); // before the lock: it takes it too
const EGLSurface drawSurface = session.Draw;
Bool windowSurface = false;
if (drawSurface != EGL_NO_SURFACE) {
const std::lock_guard<std::mutex> lock(g_nativeRegistryMutex);
if (const auto* entry = FindSessionSurfaceLocked(session, drawSurface))
windowSurface = entry->Window != static_cast<NativeWindowType>(0);
}
if (g_EGLFuncs.eglSwapBuffersWithDamageEXT != nullptr) {
const MG_Util::Damage::Region& damage = MG_Backend::CurrentPresentDamage();
EGLint rects[MG_Util::Damage::kMaxRects * 4] = {};
EGLint count = 0;
if (!damage.IsFull() && !damage.IsEmpty()) {
count = static_cast<EGLint>(MG_Util::Damage::PackRects(damage, rects, MG_Util::Damage::kMaxRects));
}
const EGLint count = static_cast<EGLint>(MG_Util::Damage::NativeSwapRects(windowSurface, damage, rects));
g_EGLFuncs.eglSwapBuffersWithDamageEXT(g_Display, drawSurface, count > 0 ? rects : nullptr, count);
} else {
g_EGLFuncs.eglSwapBuffers(g_Display, drawSurface);
}
if (drawSurface != EGL_NO_SURFACE) {
// Resolved before the lock: ActiveNativeSession takes it too.
NativeSessionState& session = ActiveNativeSession();
const std::lock_guard<std::mutex> lock(g_nativeRegistryMutex);
if (auto* entry = FindSessionSurfaceLocked(session, drawSurface)) ++entry->Presents;
}
+15
View File
@@ -137,6 +137,21 @@ TEST(Damage, PackedRectsRoundTripThroughARecord) {
EXPECT_EQ(Region::FromEglRects(out, 1), Of({{1, 2, 56, 66}}));
}
// The native swap gets rectangles only for a window surface: Android's swap-with-damage on a
// pbuffer (a client window's drawable) dereferences the native window it does not have.
TEST(Damage, OnlyAWindowSurfaceSwapsWithRectangles) {
Int32 out[kMaxRects * 4] = {};
const Region partial = Of({{10, 20, 30, 40}});
EXPECT_EQ(NativeSwapRects(false, partial, out), 0u) << "a pbuffer swaps plainly";
EXPECT_EQ(NativeSwapRects(true, Region::Full(), out), 0u);
EXPECT_EQ(NativeSwapRects(true, Region(), out), 0u) << "nothing damaged has no rectangle spelling";
ASSERT_EQ(NativeSwapRects(true, partial, out), 1u);
EXPECT_EQ(out[0], 10);
EXPECT_EQ(out[1], 20);
EXPECT_EQ(out[2], 30);
EXPECT_EQ(out[3], 40);
}
// Three shared images rotate; each write must bring over what that image missed.
TEST(Damage, EachBufferCopiesTheDamageItMissedSinceItsLastWrite) {
constexpr Int32 W = 200, H = 100;
+9
View File
@@ -266,6 +266,15 @@ namespace MobileGL::MG_Util::Damage {
return static_cast<Uint32>(rects.size());
}
// The rectangles a native swap-with-damage gets for `damage` (into `out`, room for kMaxRects),
// and their count; 0 = a plain swap. Only a window surface takes any: the platform hands them to
// the surface's native window, and a pbuffer has none (Android's EGL dereferences it anyway).
// An empty damage has no spelling (no rectangles means the whole surface), so it swaps plainly.
inline Uint32 NativeSwapRects(Bool windowSurface, const Region& damage, Int32* out) {
if (!windowSurface || damage.IsFull() || damage.IsEmpty()) return 0;
return PackRects(damage, out, kMaxRects);
}
// Per buffer of a rotating set: the damage each has missed since it was last written.
class BufferDamageTracker {
public: