mirror of
https://github.com/MobileGL-Dev/MobileGL
synced 2026-11-07 01:53:24 +09:00
[Fix] (EGL, Wayland): a swap that runs into a device loss reports it and still commits the window, instead of showing an empty frame.
This commit is contained in:
@@ -692,7 +692,14 @@ namespace MobileGL::MG_Impl::EGLImpl {
|
||||
}
|
||||
#if MOBILEGL_BUILD_DISAGGREGATED
|
||||
// A swap of a lost context's frame: EGL_CONTEXT_LOST (EGL 1.5 3.2), and nothing presented.
|
||||
// A Wayland window is still committed, with no new buffer (WindowSurface::CommitPendingState):
|
||||
// Qt asks for a frame callback before every swap and draws the window again only once it
|
||||
// has fired, so a swap that never committed froze the window for good - a Plasma panel
|
||||
// that hit the loss in its own swap never redrew, not even to rebuild its context.
|
||||
if (ContextIsLost(state, state->GetCurrentContext())) {
|
||||
#if MOBILEGL_WAYLAND_WINDOWS
|
||||
if (auto it = WaylandSurfaces().find(draw); it != WaylandSurfaces().end()) it->second->CommitPendingState();
|
||||
#endif
|
||||
state->SetError(EGL_CONTEXT_LOST);
|
||||
return EGL_FALSE;
|
||||
}
|
||||
@@ -720,10 +727,28 @@ namespace MobileGL::MG_Impl::EGLImpl {
|
||||
#if MOBILEGL_WAYLAND_WINDOWS
|
||||
// A Wayland window shows what is attached to it: the frame is read back and attached
|
||||
// BEFORE the swap, while it is still the drawable's content.
|
||||
if (auto it = WaylandSurfaces().find(draw);
|
||||
it != WaylandSurfaces().end() && !it->second->Present(damage, state->GetSwapInterval(dpy))) {
|
||||
state->SetError(EGL_BAD_SURFACE);
|
||||
return EGL_FALSE;
|
||||
if (auto it = WaylandSurfaces().find(draw); it != WaylandSurfaces().end()) {
|
||||
#if MOBILEGL_BUILD_DISAGGREGATED
|
||||
// A loss that this very present runs into is reported here, as the one above: a
|
||||
// frame of the lost device shown as if it were the window's would leave the toolkit
|
||||
// with a transparent window it learns only afterwards it has to redraw.
|
||||
const auto sessionLost = +[]() -> Bool {
|
||||
auto* current = GetState();
|
||||
return current != nullptr && ContextIsLost(current, current->GetCurrentContext());
|
||||
};
|
||||
#else
|
||||
Bool (*const sessionLost)() = nullptr;
|
||||
#endif
|
||||
switch (it->second->Present(damage, state->GetSwapInterval(dpy), sessionLost)) {
|
||||
case Wayland::WindowSurface::PresentResult::kPresented: break;
|
||||
case Wayland::WindowSurface::PresentResult::kLost:
|
||||
it->second->CommitPendingState();
|
||||
state->SetError(EGL_CONTEXT_LOST);
|
||||
return EGL_FALSE;
|
||||
case Wayland::WindowSurface::PresentResult::kNotShown:
|
||||
state->SetError(EGL_BAD_SURFACE);
|
||||
return EGL_FALSE;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
const Bool swapped = damage.IsFull() ? backendObject->SwapEGLBuffers(dpy, draw)
|
||||
|
||||
@@ -822,9 +822,19 @@ namespace MobileGL::MG_Impl::EGLImpl::Wayland {
|
||||
impl.useDmabuf = impl.dmabufWanted && impl.dmabuf != nullptr;
|
||||
}
|
||||
|
||||
Bool WindowSurface::Present(const MG_Util::Damage::Region& damage, Int swapInterval) {
|
||||
void WindowSurface::CommitPendingState() {
|
||||
Impl& impl = *m_impl;
|
||||
if (impl.window == nullptr) return false;
|
||||
if (impl.window == nullptr || impl.window->surface == nullptr) return;
|
||||
const Api& api = WaylandApi();
|
||||
wl_proxy* surface = impl.window->surface;
|
||||
api.marshalFlags(surface, kSurfaceCommit, nullptr, api.getVersion(surface), 0);
|
||||
api.flush(impl.display);
|
||||
}
|
||||
|
||||
WindowSurface::PresentResult WindowSurface::Present(const MG_Util::Damage::Region& damage, Int swapInterval,
|
||||
Bool (*sessionLost)()) {
|
||||
Impl& impl = *m_impl;
|
||||
if (impl.window == nullptr) return PresentResult::kNotShown;
|
||||
if (swapInterval != impl.loggedInterval) {
|
||||
MGLOG_I("Wayland: window surface %dx%d swaps with interval %d (%s)", impl.width, impl.height,
|
||||
static_cast<int>(swapInterval),
|
||||
@@ -846,9 +856,12 @@ namespace MobileGL::MG_Impl::EGLImpl::Wayland {
|
||||
if (backendObject->PresentToSharedImage(buffer->imageId, copy)) {
|
||||
impl.Commit(buffer->buffer, damage);
|
||||
buffer->busy = true;
|
||||
return true;
|
||||
return PresentResult::kPresented;
|
||||
}
|
||||
}
|
||||
// The image went with a lost server: the window keeps its shared images (a fresh
|
||||
// session replaces them, OnSessionReplaced) and shows no frame of the lost device.
|
||||
if (sessionLost != nullptr && sessionLost()) return PresentResult::kLost;
|
||||
MGLOG_E_ONCE("Wayland: a frame could not be presented through a linux-dmabuf shared image; the window "
|
||||
"falls back to wl_shm");
|
||||
impl.FallBackToShm();
|
||||
@@ -857,15 +870,17 @@ namespace MobileGL::MG_Impl::EGLImpl::Wayland {
|
||||
ShmBuffer* buffer = impl.NextFree();
|
||||
if (buffer == nullptr) {
|
||||
MGLOG_E_ONCE("Wayland: no wl_shm buffer could be taken for the frame; it is not shown");
|
||||
return false;
|
||||
return PresentResult::kNotShown;
|
||||
}
|
||||
|
||||
// GL's rows run bottom-up and RGBA; a wl_shm buffer's run top-down and B, G, R, A.
|
||||
ReadBackFrameBGRA(impl.width, impl.height, impl.scratch, static_cast<Uint8*>(buffer->map),
|
||||
static_cast<SizeT>(buffer->stride));
|
||||
// A read-back the device was lost under answered zeros: the buffer stays free.
|
||||
if (sessionLost != nullptr && sessionLost()) return PresentResult::kLost;
|
||||
impl.Commit(buffer->buffer, MG_Util::Damage::Region::Full());
|
||||
buffer->busy = true;
|
||||
return true;
|
||||
return PresentResult::kPresented;
|
||||
}
|
||||
} // namespace MobileGL::MG_Impl::EGLImpl::Wayland
|
||||
|
||||
|
||||
@@ -75,8 +75,8 @@ namespace MobileGL::MG_Impl::EGLImpl::Wayland {
|
||||
// Puts the current context's default-framebuffer frame on the window: copied into a
|
||||
// shared image (or read back into a wl_shm buffer, the application's read-framebuffer and
|
||||
// pack state put back exactly as they were), then attach, damage, commit. A shared-image
|
||||
// present that fails moves the window to wl_shm for good. False (logged) when the frame
|
||||
// did not reach the window; the swap itself goes on regardless.
|
||||
// present that fails moves the window to wl_shm for good. kNotShown (logged) when the
|
||||
// frame did not reach the window; the swap itself goes on regardless.
|
||||
//
|
||||
// `damage` (GL window coordinates, clipped to the window; Full = all of it) is what the
|
||||
// frame changed. A shared image is copied only where it differs from the frame - the
|
||||
@@ -85,13 +85,25 @@ namespace MobileGL::MG_Impl::EGLImpl::Wayland {
|
||||
//
|
||||
// `swapInterval` above 0 paces the swaps by the compositor's frame callbacks: this waits
|
||||
// for the previous frame's before taking a buffer (FrameThrottle.h).
|
||||
Bool Present(const MG_Util::Damage::Region& damage, Int swapInterval);
|
||||
//
|
||||
// `sessionLost` (may be null) says whether the device was lost under the frame. A frame
|
||||
// that failed - or was read back - on a lost device is nothing but zeros: kLost, with no
|
||||
// buffer attached and the window left where it was, so the caller reports the loss at
|
||||
// this swap instead of showing a transparent window that the toolkit never redraws.
|
||||
enum class PresentResult { kPresented, kNotShown, kLost };
|
||||
PresentResult Present(const MG_Util::Damage::Region& damage, Int swapInterval, Bool (*sessionLost)());
|
||||
|
||||
// wl_egl_window_resize's effect, taken: true (with the new size) when the application gave
|
||||
// its wl_egl_window a size this presentation is not at yet. The presentation adopts it -
|
||||
// its next buffer is that size - and the caller resizes the drawable behind it.
|
||||
Bool TakeResize(EGLint* width, EGLint* height);
|
||||
|
||||
// A swap that presents nothing (its context was lost) still commits the application's
|
||||
// wl_surface, with no new buffer: the frame callback a toolkit asked for just before the
|
||||
// swap is surface state that only a commit applies, and a toolkit that waits for it before
|
||||
// drawing again would never draw again - not even to find out that it has to rebuild.
|
||||
void CommitPendingState();
|
||||
|
||||
// The server session that allocated this window's shared images was lost and a fresh one
|
||||
// replaced it: the images went with their server. Their wl_buffers are destroyed here,
|
||||
// nothing is released (the fresh server never issued those ids), and the window goes
|
||||
|
||||
Reference in New Issue
Block a user