diff --git a/client/displayservers/SDL/sdl.c b/client/displayservers/SDL/sdl.c index 63cd99f7..8bd6b873 100644 --- a/client/displayservers/SDL/sdl.c +++ b/client/displayservers/SDL/sdl.c @@ -254,7 +254,7 @@ static EGLNativeWindowType sdlGetEGLNativeWindow(void) } } -static void sdlEGLSwapBuffers(EGLDisplay display, EGLSurface surface) +static void sdlEGLSwapBuffers(EGLDisplay display, EGLSurface surface, const struct Rect * damage, int count) { eglSwapBuffers(display, surface); } diff --git a/client/displayservers/Wayland/gl.c b/client/displayservers/Wayland/gl.c index c59d4a0d..3f4e199b 100644 --- a/client/displayservers/Wayland/gl.c +++ b/client/displayservers/Wayland/gl.c @@ -41,6 +41,7 @@ bool waylandEGLInit(int w, int h) DEBUG_ERROR("Failed to create EGL window"); return false; } + return true; } @@ -68,9 +69,51 @@ EGLDisplay waylandGetEGLDisplay(void) return eglGetDisplay(native); } -void waylandEGLSwapBuffers(EGLDisplay display, EGLSurface surface) +void waylandEGLSwapBuffers(EGLDisplay display, EGLSurface surface, const struct Rect * damage, int count) { - eglSwapBuffers(display, surface); + if (!wlWm.eglSwapWithDamageInit) + { + const char *exts = eglQueryString(display, EGL_EXTENSIONS); + wlWm.eglSwapWithDamageInit = true; + if (wl_proxy_get_version((struct wl_proxy *) wlWm.surface) < 4) + DEBUG_INFO("Swapping buffers with damage: not supported, need wl_compositor v4"); + else if (strstr(exts, "EGL_KHR_swap_buffers_with_damage") && g_egl_dynProcs.eglSwapBuffersWithDamageKHR) + { + wlWm.eglSwapWithDamage = g_egl_dynProcs.eglSwapBuffersWithDamageKHR; + DEBUG_INFO("Using EGL_KHR_swap_buffers_with_damage"); + } + else if (strstr(exts, "EGL_EXT_swap_buffers_with_damage") && g_egl_dynProcs.eglSwapBuffersWithDamageEXT) + { + wlWm.eglSwapWithDamage = g_egl_dynProcs.eglSwapBuffersWithDamageEXT; + DEBUG_INFO("Using EGL_EXT_swap_buffers_with_damage"); + } + else + DEBUG_INFO("Swapping buffers with damage: not supported"); + } + + if (wlWm.eglSwapWithDamage && count) + { + if (count * 4 > wlWm.eglDamageRectCount) + { + free(wlWm.eglDamageRects); + wlWm.eglDamageRects = malloc(sizeof(EGLint) * count * 4); + if (!wlWm.eglDamageRects) + DEBUG_FATAL("Out of memory"); + wlWm.eglDamageRectCount = count * 4; + } + + for (int i = 0; i < count; ++i) + { + wlWm.eglDamageRects[i*4+0] = damage[i].x; + wlWm.eglDamageRects[i*4+1] = damage[i].y; + wlWm.eglDamageRects[i*4+2] = damage[i].w; + wlWm.eglDamageRects[i*4+3] = damage[i].h; + } + + wlWm.eglSwapWithDamage(display, surface, wlWm.eglDamageRects, count); + } + else + eglSwapBuffers(display, surface); if (wlWm.needsResize) { @@ -169,6 +212,6 @@ void waylandGLSetSwapInterval(int interval) void waylandGLSwapBuffers(void) { - waylandEGLSwapBuffers(wlWm.glDisplay, wlWm.glSurface); + waylandEGLSwapBuffers(wlWm.glDisplay, wlWm.glSurface, NULL, 0); } #endif diff --git a/client/displayservers/Wayland/registry.c b/client/displayservers/Wayland/registry.c index 3504ca0e..6889bd8a 100644 --- a/client/displayservers/Wayland/registry.c +++ b/client/displayservers/Wayland/registry.c @@ -37,7 +37,9 @@ static void registryGlobalHandler(void * data, struct wl_registry * registry, else if (!strcmp(interface, wl_shm_interface.name)) wlWm.shm = wl_registry_bind(wlWm.registry, name, &wl_shm_interface, 1); else if (!strcmp(interface, wl_compositor_interface.name) && version >= 3) - wlWm.compositor = wl_registry_bind(wlWm.registry, name, &wl_compositor_interface, 3); + wlWm.compositor = wl_registry_bind(wlWm.registry, name, + // we only need v3 to run, but v4 can use eglSwapBuffersWithDamageKHR + &wl_compositor_interface, version > 4 ? 4 : version); #ifndef ENABLE_LIBDECOR else if (!strcmp(interface, xdg_wm_base_interface.name)) wlWm.xdgWmBase = wl_registry_bind(wlWm.registry, name, &xdg_wm_base_interface, 1); diff --git a/client/displayservers/Wayland/wayland.h b/client/displayservers/Wayland/wayland.h index c9c53933..63a7c2ff 100644 --- a/client/displayservers/Wayland/wayland.h +++ b/client/displayservers/Wayland/wayland.h @@ -29,6 +29,7 @@ Place, Suite 330, Boston, MA 02111-1307 USA # include #endif +#include "egl_dynprocs.h" #include "common/locking.h" #include "common/countedbuffer.h" #include "interface/displayserver.h" @@ -66,6 +67,13 @@ struct SurfaceOutput struct wl_list link; }; +enum EGLSwapWithDamageState { + SWAP_WITH_DAMAGE_UNKNOWN, + SWAP_WITH_DAMAGE_UNSUPPORTED, + SWAP_WITH_DAMAGE_KHR, + SWAP_WITH_DAMAGE_EXT, +}; + struct WaylandDSState { bool pointerGrabbed; @@ -88,8 +96,12 @@ struct WaylandDSState bool warpSupport; double cursorX, cursorY; -#ifdef ENABLE_EGL +#if defined(ENABLE_EGL) || defined(ENABLE_OPENGL) struct wl_egl_window * eglWindow; + bool eglSwapWithDamageInit; + eglSwapBuffersWithDamageKHR_t eglSwapWithDamage; + EGLint * eglDamageRects; + int eglDamageRectCount; #endif #ifdef ENABLE_OPENGL @@ -197,7 +209,7 @@ void waylandShowPointer(bool show); #if defined(ENABLE_EGL) || defined(ENABLE_OPENGL) bool waylandEGLInit(int w, int h); EGLDisplay waylandGetEGLDisplay(void); -void waylandEGLSwapBuffers(EGLDisplay display, EGLSurface surface); +void waylandEGLSwapBuffers(EGLDisplay display, EGLSurface surface, const struct Rect * damage, int count); #endif #ifdef ENABLE_EGL diff --git a/client/displayservers/Wayland/window.c b/client/displayservers/Wayland/window.c index aa1196f1..9824913e 100644 --- a/client/displayservers/Wayland/window.c +++ b/client/displayservers/Wayland/window.c @@ -114,5 +114,3 @@ bool waylandIsValidPointerPos(int x, int y) { return x >= 0 && x < wlWm.width && y >= 0 && y < wlWm.height; } - - diff --git a/client/displayservers/X11/x11.c b/client/displayservers/X11/x11.c index 231f091c..4677fe54 100644 --- a/client/displayservers/X11/x11.c +++ b/client/displayservers/X11/x11.c @@ -930,7 +930,7 @@ static EGLNativeWindowType x11GetEGLNativeWindow(void) return (EGLNativeWindowType)x11.window; } -static void x11EGLSwapBuffers(EGLDisplay display, EGLSurface surface) +static void x11EGLSwapBuffers(EGLDisplay display, EGLSurface surface, const struct Rect * damage, int count) { eglSwapBuffers(display, surface); } diff --git a/client/include/app.h b/client/include/app.h index 360382f3..b99db81d 100644 --- a/client/include/app.h +++ b/client/include/app.h @@ -66,7 +66,7 @@ bool app_getProp(LG_DSProperty prop, void * ret); #ifdef ENABLE_EGL EGLDisplay app_getEGLDisplay(void); EGLNativeWindowType app_getEGLNativeWindow(void); -void app_eglSwapBuffers(EGLDisplay display, EGLSurface surface); +void app_eglSwapBuffers(EGLDisplay display, EGLSurface surface, const struct Rect * damage, int count); #endif #ifdef ENABLE_OPENGL diff --git a/client/include/egl_dynprocs.h b/client/include/egl_dynprocs.h index a1912317..4adf24ee 100644 --- a/client/include/egl_dynprocs.h +++ b/client/include/egl_dynprocs.h @@ -17,6 +17,8 @@ this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +#ifndef _H_LG_EGL_DYNPROCS_ +#define _H_LG_EGL_DYNPROCS_ #ifdef ENABLE_EGL #include @@ -24,6 +26,8 @@ Place, Suite 330, Boston, MA 02111-1307 USA typedef EGLDisplay (*eglGetPlatformDisplayEXT_t)(EGLenum platform, void *native_display, const EGLint *attrib_list); +typedef void (*eglSwapBuffersWithDamageKHR_t)(EGLDisplay dpy, + EGLSurface surface, const EGLint *rects, EGLint n_rects); typedef void (*glEGLImageTargetTexture2DOES_t)(GLenum target, GLeglImageOES image); @@ -31,6 +35,8 @@ struct EGLDynProcs { eglGetPlatformDisplayEXT_t eglGetPlatformDisplay; eglGetPlatformDisplayEXT_t eglGetPlatformDisplayEXT; + eglSwapBuffersWithDamageKHR_t eglSwapBuffersWithDamageKHR; + eglSwapBuffersWithDamageKHR_t eglSwapBuffersWithDamageEXT; glEGLImageTargetTexture2DOES_t glEGLImageTargetTexture2DOES; }; @@ -41,3 +47,5 @@ void egl_dynProcsInit(void); #else #define egl_dynProcsInit(...) #endif + +#endif // _H_LG_EGL_DYNPROCS_ diff --git a/client/include/interface/displayserver.h b/client/include/interface/displayserver.h index af0577e8..977fce01 100644 --- a/client/include/interface/displayserver.h +++ b/client/include/interface/displayserver.h @@ -22,6 +22,7 @@ Place, Suite 330, Boston, MA 02111-1307 USA #include #include +#include "common/types.h" typedef enum LG_ClipboardData { @@ -115,7 +116,7 @@ struct LG_DisplayServerOps /* EGL support */ EGLDisplay (*getEGLDisplay)(void); EGLNativeWindowType (*getEGLNativeWindow)(void); - void (*eglSwapBuffers)(EGLDisplay display, EGLSurface surface); + void (*eglSwapBuffers)(EGLDisplay display, EGLSurface surface, const struct Rect * damage, int count); #endif #ifdef ENABLE_OPENGL diff --git a/client/renderers/EGL/cursor.c b/client/renderers/EGL/cursor.c index a4b53d89..25889813 100644 --- a/client/renderers/EGL/cursor.c +++ b/client/renderers/EGL/cursor.c @@ -215,6 +215,16 @@ void egl_cursor_set_state(EGL_Cursor * cursor, const bool visible, const float x cursor->y = y; } +struct CursorState egl_cursor_get_state(EGL_Cursor * cursor, int width, int height) { + return (struct CursorState) { + .visible = cursor->visible, + .rect.x = (cursor->x * width + width) / 2, + .rect.y = (-cursor->y * height + height) / 2 - cursor->h * height, + .rect.w = cursor->w * width + 2, + .rect.h = cursor->h * height + 2, + }; +} + void egl_cursor_render(EGL_Cursor * cursor, LG_RendererRotate rotate) { if (!cursor->visible) diff --git a/client/renderers/EGL/cursor.h b/client/renderers/EGL/cursor.h index bd9002fb..06763afa 100644 --- a/client/renderers/EGL/cursor.h +++ b/client/renderers/EGL/cursor.h @@ -25,6 +25,11 @@ Place, Suite 330, Boston, MA 02111-1307 USA typedef struct EGL_Cursor EGL_Cursor; +struct CursorState { + bool visible; + struct Rect rect; +}; + bool egl_cursor_init(EGL_Cursor ** cursor); void egl_cursor_free(EGL_Cursor ** cursor); @@ -41,4 +46,6 @@ void egl_cursor_set_size(EGL_Cursor * cursor, const float x, const float y); void egl_cursor_set_state(EGL_Cursor * cursor, const bool visible, const float x, const float y); +struct CursorState egl_cursor_get_state(EGL_Cursor * cursor, int width, int height); + void egl_cursor_render(EGL_Cursor * cursor, LG_RendererRotate rotate); diff --git a/client/renderers/EGL/egl.c b/client/renderers/EGL/egl.c index 57c286cb..2ae45002 100644 --- a/client/renderers/EGL/egl.c +++ b/client/renderers/EGL/egl.c @@ -106,6 +106,9 @@ struct Inst unsigned fontSize; LG_FontObj helpFontObj; unsigned helpFontSize; + + bool cursorLastValid; + struct CursorState cursorLast; }; static struct Option egl_options[] = @@ -472,6 +475,8 @@ void egl_on_resize(void * opaque, const int width, const int height, const doubl egl_calc_mouse_state(this); egl_update_font(this); egl_update_help_font(this); + + this->cursorLastValid = false; } bool egl_on_mouse_shape(void * opaque, const LG_RendererCursor cursor, @@ -545,6 +550,7 @@ bool egl_on_frame(void * opaque, const FrameBuffer * frame, int dmaFd) } this->start = true; + this->cursorLastValid = false; return true; } @@ -581,12 +587,14 @@ void egl_on_alert(void * opaque, const LG_MsgAlert alert, const char * message, } this->showAlert = true; + this->cursorLastValid = false; } void egl_on_help(void * opaque, const char * message) { struct Inst * this = (struct Inst *)opaque; egl_help_set_text(this->help, message); + this->cursorLastValid = false; } void egl_on_show_fps(void * opaque, bool showFPS) @@ -767,6 +775,10 @@ bool egl_render(void * opaque, LG_RendererRotate rotate) glClearColor(0.0f, 0.0f, 0.0f, 1.0f); glClear(GL_COLOR_BUFFER_BIT); + bool hasLastCursor = this->cursorLastValid; + bool cursorRendered = false; + struct CursorState cursorState; + if (this->start && egl_desktop_render(this->desktop, this->translateX, this->translateY, this->scaleX , this->scaleY , @@ -780,6 +792,8 @@ bool egl_render(void * opaque, LG_RendererRotate rotate) this->waitDone = true; } + cursorRendered = true; + cursorState = egl_cursor_get_state(this->cursor, this->width, this->height); egl_cursor_render(this->cursor, (this->format.rotate + rotate) % LG_ROTATE_MAX); } @@ -819,14 +833,39 @@ bool egl_render(void * opaque, LG_RendererRotate rotate) close = true; if (close) + { this->showAlert = false; + this->cursorLastValid = false; + } else egl_alert_render(this->alert, this->screenScaleX, this->screenScaleY); } + struct Rect damage[2]; + int damageIdx = 0; + + if (this->waitDone) + { + if (cursorRendered && hasLastCursor) + { + if (this->cursorLast.visible) + damage[damageIdx++] = this->cursorLast.rect; + + if (cursorState.visible) + damage[damageIdx++] = cursorState.rect; + + this->cursorLast = cursorState; + } + else if (cursorRendered) + { + this->cursorLast = cursorState; + this->cursorLastValid = true; + } + } + egl_fps_render(this->fps, this->screenScaleX, this->screenScaleY); egl_help_render(this->help, this->screenScaleX, this->screenScaleY); - app_eglSwapBuffers(this->display, this->surface); + app_eglSwapBuffers(this->display, this->surface, damage, damageIdx); return true; } @@ -834,6 +873,7 @@ void egl_update_fps(void * opaque, const float avgUPS, const float avgFPS) { struct Inst * this = (struct Inst *)opaque; egl_fps_update(this->fps, avgUPS, avgFPS); + this->cursorLastValid = false; } struct LG_Renderer LGR_EGL = diff --git a/client/src/app.c b/client/src/app.c index 25d80a7b..443fa539 100644 --- a/client/src/app.c +++ b/client/src/app.c @@ -448,9 +448,9 @@ EGLNativeWindowType app_getEGLNativeWindow(void) return g_state.ds->getEGLNativeWindow(); } -void app_eglSwapBuffers(EGLDisplay display, EGLSurface surface) +void app_eglSwapBuffers(EGLDisplay display, EGLSurface surface, const struct Rect * damage, int count) { - g_state.ds->eglSwapBuffers(display, surface); + g_state.ds->eglSwapBuffers(display, surface, damage, count); } #endif diff --git a/client/src/egl_dynprocs.c b/client/src/egl_dynprocs.c index f99783ba..b7bc3f26 100644 --- a/client/src/egl_dynprocs.c +++ b/client/src/egl_dynprocs.c @@ -31,6 +31,10 @@ void egl_dynProcsInit(void) eglGetProcAddress("eglGetPlatformDisplayEXT"); g_egl_dynProcs.glEGLImageTargetTexture2DOES = (glEGLImageTargetTexture2DOES_t) eglGetProcAddress("glEGLImageTargetTexture2DOES"); + g_egl_dynProcs.eglSwapBuffersWithDamageKHR = (eglSwapBuffersWithDamageKHR_t) + eglGetProcAddress("eglSwapBuffersWithDamageKHR"); + g_egl_dynProcs.eglSwapBuffersWithDamageEXT = (eglSwapBuffersWithDamageKHR_t) + eglGetProcAddress("eglSwapBuffersWithDamageEXT"); }; #endif