Skip to content

Commit a233052

Browse files
committed
egl-swap: support eglSwapBuffersWithDamage with damage_thread
If we have a damage thread, we were previously ignoring all of the damage rectangles provided by the client. This uses a small lock-free ring-buffer between the producer and consumer threads to pass the damage region to the damage thread where they will ultimately be passed to the underlying EGL implementation. This fixes GTK 4 sending appropriate damage regions with NVIDIA when EGL is used on Wayland.
1 parent 7d6c362 commit a233052

File tree

3 files changed

+125
-3
lines changed

3 files changed

+125
-3
lines changed

include/wayland-eglsurface.h

+27
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,9 @@
3434
extern "C" {
3535
#endif
3636

37+
#define WL_EGL_STREAM_DAMAGE_BUFFER_N_FRAMES 3
38+
#define WL_EGL_STREAM_DAMAGE_BUFFER_N_RECTS 32
39+
3740
typedef struct WlEglStreamImageRec {
3841
/* Pointer back to the parent surface for use in Wayland callbacks */
3942
struct WlEglSurfaceRec *surface;
@@ -52,6 +55,19 @@ typedef struct WlEglStreamImageRec {
5255
struct wl_list acquiredLink;
5356
} WlEglStreamImage;
5457

58+
typedef struct WlEglStreamDamageRec {
59+
EGLuint64KHR frameNumber;
60+
EGLint n_rects;
61+
EGLint _padding;
62+
EGLint rects[4 * WL_EGL_STREAM_DAMAGE_BUFFER_N_RECTS];
63+
} WlEglStreamDamage;
64+
65+
typedef struct WlEglStreamDamageBufferRec {
66+
_Atomic EGLint head;
67+
_Atomic EGLint tail;
68+
WlEglStreamDamage frames[WL_EGL_STREAM_DAMAGE_BUFFER_N_FRAMES];
69+
} WlEglStreamDamageBuffer;
70+
5571
typedef struct WlEglSurfaceCtxRec {
5672
EGLBoolean isOffscreen;
5773
EGLSurface eglSurface;
@@ -79,6 +95,8 @@ typedef struct WlEglSurfaceCtxRec {
7995
uint32_t numStreamImages;
8096

8197
struct wl_list link;
98+
99+
WlEglStreamDamageBuffer damageBuffer;
82100
} WlEglSurfaceCtx;
83101

84102
typedef struct WlEglSurfaceRec {
@@ -164,6 +182,15 @@ EGLBoolean wlEglSendDamageEvent(WlEglSurface *surface,
164182
EGLint *rects,
165183
EGLint n_rects);
166184

185+
void wlEglInitializeStreamDamageBuffer(WlEglStreamDamageBuffer *buffer);
186+
EGLBoolean wlEglPutStreamDamage(WlEglStreamDamageBuffer *buffer,
187+
EGLuint64KHR frameNumber,
188+
EGLint *rects,
189+
EGLint n_rects);
190+
EGLBoolean wlEglGetStreamDamageForFrame(WlEglStreamDamageBuffer *buffer,
191+
EGLuint64KHR frameNumber,
192+
WlEglStreamDamage *damage);
193+
167194
void wlEglCreateFrameSync(WlEglSurface *surface);
168195
EGLint wlEglWaitFrameSync(WlEglSurface *surface);
169196

src/wayland-eglsurface.c

+92-3
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
#include <fcntl.h>
4141
#include <poll.h>
4242
#include <errno.h>
43+
#include <stdatomic.h>
4344

4445
#define WL_EGL_WINDOW_DESTROY_CALLBACK_SINCE 3
4546

@@ -268,13 +269,20 @@ damage_thread(void *args)
268269
if (ok) {
269270

270271
// If there's an unprocessed frame ready, send damage event
271-
if (surface->ctx.framesFinished !=
272-
surface->ctx.framesProcessed) {
272+
if (surface->ctx.framesFinished != surface->ctx.framesProcessed) {
273+
WlEglStreamDamage damage;
274+
273275
if (display->devDpy->exts.stream_flush) {
274276
data->egl.streamFlush(display->devDpy->eglDisplay,
275277
surface->ctx.eglStream);
276278
}
277-
ok = wlEglSendDamageEvent(surface, queue, NULL, 0);
279+
280+
if (wlEglGetStreamDamageForFrame(&surface->ctx.damageBuffer, surface->ctx.framesProcessed, &damage)) {
281+
ok = wlEglSendDamageEvent(surface, queue, damage.rects, damage.n_rects);
282+
} else {
283+
ok = wlEglSendDamageEvent(surface, queue, NULL, 0);
284+
}
285+
278286
surface->ctx.framesProcessed++;
279287
}
280288

@@ -2223,3 +2231,84 @@ EGLBoolean wlEglQueryNativeResourceHook(EGLDisplay dpy,
22232231
wlExternalApiUnlock();
22242232
return res;
22252233
}
2234+
2235+
void
2236+
wlEglInitializeStreamDamageBuffer(WlEglStreamDamageBuffer *buffer)
2237+
{
2238+
memset (buffer, 0, sizeof *buffer);
2239+
}
2240+
2241+
EGLBoolean
2242+
wlEglGetStreamDamageForFrame(WlEglStreamDamageBuffer *buffer,
2243+
EGLuint64KHR frameNumber,
2244+
WlEglStreamDamage *damage)
2245+
{
2246+
EGLint tail = buffer->tail;
2247+
2248+
atomic_thread_fence (memory_order_acquire);
2249+
2250+
for (;;) {
2251+
if (buffer->head == tail) {
2252+
return EGL_FALSE;
2253+
}
2254+
2255+
if (buffer->frames[tail].frameNumber > frameNumber) {
2256+
/* We must have dropped our desired frame damage on the floor
2257+
* because there was not space or it had too many rectangles.
2258+
*/
2259+
return EGL_FALSE;
2260+
}
2261+
2262+
/* Avoid copying empty rectangles */
2263+
memcpy (damage, &buffer->frames[tail],
2264+
offsetof (WlEglStreamDamage, rects) + buffer->frames[tail].n_rects * sizeof(EGLint) * 4);
2265+
2266+
tail++;
2267+
if (tail == WL_EGL_STREAM_DAMAGE_BUFFER_N_FRAMES) {
2268+
tail = 0;
2269+
}
2270+
2271+
atomic_thread_fence (memory_order_release);
2272+
2273+
buffer->tail = tail;
2274+
2275+
if (damage->frameNumber == frameNumber) {
2276+
return EGL_TRUE;
2277+
}
2278+
}
2279+
2280+
/* Not reached */
2281+
}
2282+
2283+
EGLBoolean
2284+
wlEglPutStreamDamage(WlEglStreamDamageBuffer *buffer,
2285+
EGLuint64KHR frameNumber,
2286+
EGLint *rects,
2287+
EGLint n_rects)
2288+
{
2289+
EGLint head = buffer->head + 1;
2290+
2291+
if (n_rects == 0 || n_rects > WL_EGL_STREAM_DAMAGE_BUFFER_N_RECTS) {
2292+
return EGL_FALSE;
2293+
}
2294+
2295+
if (head == WL_EGL_STREAM_DAMAGE_BUFFER_N_FRAMES) {
2296+
head = 0;
2297+
}
2298+
2299+
atomic_thread_fence (memory_order_acquire);
2300+
2301+
if (head != buffer->tail) {
2302+
buffer->frames[buffer->head].frameNumber = frameNumber;
2303+
buffer->frames[buffer->head].n_rects = n_rects;
2304+
memcpy (buffer->frames[buffer->head].rects, rects, sizeof (EGLint) * 4 * n_rects);
2305+
2306+
atomic_thread_fence (memory_order_release);
2307+
2308+
buffer->head = head;
2309+
2310+
return EGL_TRUE;
2311+
}
2312+
2313+
return EGL_FALSE;
2314+
}

src/wayland-eglswap.c

+6
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,12 @@ EGLBoolean wlEglSwapBuffersWithDamageHook(EGLDisplay eglDisplay, EGLSurface eglS
123123

124124
if (res) {
125125
if (surface->ctx.useDamageThread) {
126+
/* Failure due to a full ring-buffer results in a full-surface
127+
* composition by the damage thread.
128+
*/
129+
wlEglPutStreamDamage(&surface->ctx.damageBuffer,
130+
surface->ctx.framesProduced,
131+
rects, n_rects);
126132
surface->ctx.framesProduced++;
127133
} else {
128134
res = wlEglSendDamageEvent(surface, surface->wlEventQueue, rects, n_rects);

0 commit comments

Comments
 (0)