diff --git a/include/wayland-eglsurface.h b/include/wayland-eglsurface.h index 5a4b41f..f155227 100644 --- a/include/wayland-eglsurface.h +++ b/include/wayland-eglsurface.h @@ -34,6 +34,9 @@ extern "C" { #endif +#define WL_EGL_STREAM_DAMAGE_BUFFER_N_FRAMES 3 +#define WL_EGL_STREAM_DAMAGE_BUFFER_N_RECTS 32 + typedef struct WlEglStreamImageRec { /* Pointer back to the parent surface for use in Wayland callbacks */ struct WlEglSurfaceRec *surface; @@ -52,6 +55,19 @@ typedef struct WlEglStreamImageRec { struct wl_list acquiredLink; } WlEglStreamImage; +typedef struct WlEglStreamDamageRec { + EGLuint64KHR frameNumber; + EGLint n_rects; + EGLint _padding; + EGLint rects[4 * WL_EGL_STREAM_DAMAGE_BUFFER_N_RECTS]; +} WlEglStreamDamage; + +typedef struct WlEglStreamDamageBufferRec { + volatile EGLint head; + volatile EGLint tail; + WlEglStreamDamage frames[WL_EGL_STREAM_DAMAGE_BUFFER_N_FRAMES]; +} WlEglStreamDamageBuffer; + typedef struct WlEglSurfaceCtxRec { EGLBoolean isOffscreen; EGLSurface eglSurface; @@ -79,6 +95,8 @@ typedef struct WlEglSurfaceCtxRec { uint32_t numStreamImages; struct wl_list link; + + WlEglStreamDamageBuffer damageBuffer; } WlEglSurfaceCtx; typedef struct WlEglSurfaceRec { @@ -164,6 +182,15 @@ EGLBoolean wlEglSendDamageEvent(WlEglSurface *surface, EGLint *rects, EGLint n_rects); +void wlEglInitializeStreamDamageBuffer(WlEglStreamDamageBuffer *buffer); +EGLBoolean wlEglPutStreamDamage(WlEglStreamDamageBuffer *buffer, + EGLuint64KHR frameNumber, + EGLint *rects, + EGLint n_rects); +EGLBoolean wlEglGetStreamDamageForFrame(WlEglStreamDamageBuffer *buffer, + EGLuint64KHR frameNumber, + WlEglStreamDamage *damage); + void wlEglCreateFrameSync(WlEglSurface *surface); EGLint wlEglWaitFrameSync(WlEglSurface *surface); diff --git a/src/wayland-eglsurface.c b/src/wayland-eglsurface.c index 16eae97..4667cec 100644 --- a/src/wayland-eglsurface.c +++ b/src/wayland-eglsurface.c @@ -40,6 +40,7 @@ #include #include #include +#include #define WL_EGL_WINDOW_DESTROY_CALLBACK_SINCE 3 @@ -268,13 +269,20 @@ damage_thread(void *args) if (ok) { // If there's an unprocessed frame ready, send damage event - if (surface->ctx.framesFinished != - surface->ctx.framesProcessed) { + if (surface->ctx.framesFinished != surface->ctx.framesProcessed) { + WlEglStreamDamage damage; + if (display->devDpy->exts.stream_flush) { data->egl.streamFlush(display->devDpy->eglDisplay, surface->ctx.eglStream); } - ok = wlEglSendDamageEvent(surface, queue, NULL, 0); + + if (wlEglGetStreamDamageForFrame(&surface->ctx.damageBuffer, surface->ctx.framesProcessed, &damage)) { + ok = wlEglSendDamageEvent(surface, queue, damage.rects, damage.n_rects); + } else { + ok = wlEglSendDamageEvent(surface, queue, NULL, 0); + } + surface->ctx.framesProcessed++; } @@ -2223,3 +2231,84 @@ EGLBoolean wlEglQueryNativeResourceHook(EGLDisplay dpy, wlExternalApiUnlock(); return res; } + +void +wlEglInitializeStreamDamageBuffer(WlEglStreamDamageBuffer *buffer) +{ + memset (buffer, 0, sizeof *buffer); +} + +EGLBoolean +wlEglGetStreamDamageForFrame(WlEglStreamDamageBuffer *buffer, + EGLuint64KHR frameNumber, + WlEglStreamDamage *damage) +{ + EGLint tail = buffer->tail; + + atomic_thread_fence (memory_order_acquire); + + for (;;) { + if (buffer->head == tail) { + return EGL_FALSE; + } + + if (buffer->frames[tail].frameNumber > frameNumber) { + /* We must have dropped our desired frame damage on the floor + * because there was not space or it had too many rectangles. + */ + return EGL_FALSE; + } + + /* Avoid copying empty rectangles */ + memcpy (damage, &buffer->frames[tail], + offsetof (WlEglStreamDamage, rects) + buffer->frames[tail].n_rects * sizeof(EGLint) * 4); + + tail++; + if (tail == WL_EGL_STREAM_DAMAGE_BUFFER_N_FRAMES) { + tail = 0; + } + + atomic_thread_fence (memory_order_release); + + buffer->tail = tail; + + if (damage->frameNumber == frameNumber) { + return EGL_TRUE; + } + } + + /* Not reached */ +} + +EGLBoolean +wlEglPutStreamDamage(WlEglStreamDamageBuffer *buffer, + EGLuint64KHR frameNumber, + EGLint *rects, + EGLint n_rects) +{ + EGLint head = buffer->head + 1; + + if (n_rects == 0 || n_rects > WL_EGL_STREAM_DAMAGE_BUFFER_N_RECTS) { + return EGL_FALSE; + } + + if (head == WL_EGL_STREAM_DAMAGE_BUFFER_N_FRAMES) { + head = 0; + } + + atomic_thread_fence (memory_order_acquire); + + if (head != buffer->tail) { + buffer->frames[buffer->head].frameNumber = frameNumber; + buffer->frames[buffer->head].n_rects = n_rects; + memcpy (buffer->frames[buffer->head].rects, rects, sizeof (EGLint) * 4 * n_rects); + + atomic_thread_fence (memory_order_release); + + buffer->head = head; + + return EGL_TRUE; + } + + return EGL_FALSE; +} diff --git a/src/wayland-eglswap.c b/src/wayland-eglswap.c index 74e8e3a..8a8d8a4 100644 --- a/src/wayland-eglswap.c +++ b/src/wayland-eglswap.c @@ -123,6 +123,9 @@ EGLBoolean wlEglSwapBuffersWithDamageHook(EGLDisplay eglDisplay, EGLSurface eglS if (res) { if (surface->ctx.useDamageThread) { + wlEglPutStreamDamage(&surface->ctx.damageBuffer, + surface->ctx.framesProduced, + rects, n_rects); surface->ctx.framesProduced++; } else { res = wlEglSendDamageEvent(surface, surface->wlEventQueue, rects, n_rects);