Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

egl-swap: provide damage rectangles to wl_surface #50

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 30 additions & 1 deletion include/wayland-eglsurface.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
chergert marked this conversation as resolved.
Show resolved Hide resolved
volatile EGLint tail;
WlEglStreamDamage frames[WL_EGL_STREAM_DAMAGE_BUFFER_N_FRAMES];
} WlEglStreamDamageBuffer;

typedef struct WlEglSurfaceCtxRec {
EGLBoolean isOffscreen;
EGLSurface eglSurface;
Expand Down Expand Up @@ -79,6 +95,8 @@ typedef struct WlEglSurfaceCtxRec {
uint32_t numStreamImages;

struct wl_list link;

WlEglStreamDamageBuffer damageBuffer;
} WlEglSurfaceCtx;

typedef struct WlEglSurfaceRec {
Expand Down Expand Up @@ -160,7 +178,18 @@ EGLBoolean wlEglQueryNativeResourceHook(EGLDisplay dpy,
int *value);

EGLBoolean wlEglSendDamageEvent(WlEglSurface *surface,
struct wl_event_queue *queue);
struct wl_event_queue *queue,
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);
Expand Down
118 changes: 112 additions & 6 deletions src/wayland-eglsurface.c
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
#include <fcntl.h>
#include <poll.h>
#include <errno.h>
#include <stdatomic.h>

#define WL_EGL_WINDOW_DESTROY_CALLBACK_SINCE 3

Expand Down Expand Up @@ -161,9 +162,13 @@ EGLint wlEglWaitFrameSync(WlEglSurface *surface)
}

EGLBoolean
wlEglSendDamageEvent(WlEglSurface *surface, struct wl_event_queue *queue)
wlEglSendDamageEvent(WlEglSurface *surface,
struct wl_event_queue *queue,
EGLint *rects,
EGLint n_rects)
{
struct wl_display *wlDpy = surface->wlEglDpy->nativeDpy;
EGLint i;

if (surface->ctx.wlStreamResource) {
/* Attach same buffer to indicate new content for the surface is
Expand Down Expand Up @@ -191,8 +196,21 @@ wlEglSendDamageEvent(WlEglSurface *surface, struct wl_event_queue *queue)
surface->dy);
}

wl_surface_damage(surface->wlSurface, 0, 0,
surface->width, surface->height);
if (n_rects > 0 &&
(wl_proxy_get_version((struct wl_proxy *)surface->wlSurface) >=
WL_SURFACE_DAMAGE_BUFFER_SINCE_VERSION)) {
for (i = 0; i < n_rects; i++) {
int y = rects[i*4+1] + rects[i*4+3];
wl_surface_damage_buffer(surface->wlSurface,
rects[i*4],
surface->height - y,
rects[i*4+2],
rects[i*4+3]);
}
} else {
wl_surface_damage(surface->wlSurface, 0, 0, UINT32_MAX, UINT32_MAX);
}

wl_surface_commit(surface->wlSurface);
surface->ctx.isAttached = EGL_TRUE;

Expand Down Expand Up @@ -251,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);

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++;
}

Expand Down Expand Up @@ -2206,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;
}
10 changes: 8 additions & 2 deletions src/wayland-eglswap.c
Original file line number Diff line number Diff line change
Expand Up @@ -123,9 +123,15 @@ EGLBoolean wlEglSwapBuffersWithDamageHook(EGLDisplay eglDisplay, EGLSurface eglS

if (res) {
if (surface->ctx.useDamageThread) {
/* Failure due to a full ring-buffer results in a full-surface
* composition by the damage thread.
*/
wlEglPutStreamDamage(&surface->ctx.damageBuffer,
surface->ctx.framesProduced,
rects, n_rects);
chergert marked this conversation as resolved.
Show resolved Hide resolved
surface->ctx.framesProduced++;
} else {
res = wlEglSendDamageEvent(surface, surface->wlEventQueue);
res = wlEglSendDamageEvent(surface, surface->wlEventQueue, rects, n_rects);
}
}
wlEglCreateFrameSync(surface);
Expand Down Expand Up @@ -285,7 +291,7 @@ EGLBoolean wlEglPostPresentExport(WlEglSurface *surface) {
if (surface->ctx.useDamageThread) {
surface->ctx.framesProduced++;
} else {
res = wlEglSendDamageEvent(surface, surface->wlEventQueue);
res = wlEglSendDamageEvent(surface, surface->wlEventQueue, NULL, 0);
}

wlEglCreateFrameSync(surface);
Expand Down