From 87aac8cf03fa483a6e6344be8a5e838392d8a6c7 Mon Sep 17 00:00:00 2001 From: Quantum Date: Mon, 2 Aug 2021 21:37:58 -0400 Subject: [PATCH] [client] egl: use buffer age extension to render only damaged parts We avoid rendering any area that has not changed since the buffer was used and also not covered by an overlay. --- client/include/util.h | 2 +- client/renderers/EGL/egl.c | 145 ++++++++++++++++++++++++++++++++++--- client/src/util.c | 19 +++-- 3 files changed, 144 insertions(+), 22 deletions(-) diff --git a/client/include/util.h b/client/include/util.h index da6dfcdc..738488a6 100644 --- a/client/include/util.h +++ b/client/include/util.h @@ -40,7 +40,7 @@ bool util_guestCurToLocal(struct DoublePoint *local); void util_localCurToGuest(struct DoublePoint *guest); void util_rotatePoint(struct DoublePoint *point); bool util_hasGLExt(const char * exts, const char * ext); -int util_mergeOverlappingRects(FrameDamageRect * out, const FrameDamageRect * rects, int count); +int util_mergeOverlappingRects(FrameDamageRect * rects, int count); static inline double util_clamp(double x, double min, double max) { diff --git a/client/renderers/EGL/egl.c b/client/renderers/EGL/egl.c index 890303fe..d467045d 100644 --- a/client/renderers/EGL/egl.c +++ b/client/renderers/EGL/egl.c @@ -47,9 +47,13 @@ #include "desktop.h" #include "cursor.h" #include "splash.h" +#include "util.h" #define SPLASH_FADE_TIME 1000000 -#define DESKTOP_DAMAGE_COUNT 2 +#define MAX_BUFFER_AGE 3 +#define DESKTOP_DAMAGE_COUNT 4 +#define MAX_ACCUMULATED_DAMAGE ((KVMFR_MAX_DAMAGE_RECTS + MAX_OVERLAY_RECTS + 2) * MAX_BUFFER_AGE) +#define IDX_AGO(counter, i, total) ((counter) + (total) - i) % (total) struct Options { @@ -107,6 +111,11 @@ struct Inst unsigned int desktopDamageIdx; LG_Lock desktopDamageLock; + bool hasBufferAge; + struct Rect overlayHistory[DESKTOP_DAMAGE_COUNT][MAX_OVERLAY_RECTS + 1]; + int overlayHistoryCount[DESKTOP_DAMAGE_COUNT]; + unsigned int overlayHistoryIdx; + RingBuffer importTimings; GraphHandle importGraph; }; @@ -725,6 +734,8 @@ static bool egl_render_startup(void * opaque, bool useDMA) return false; } + this->hasBufferAge = util_hasGLExt(client_exts, "EGL_EXT_buffer_age"); + if (g_egl_dynProcs.glEGLImageTargetTexture2DOES) { if (util_hasGLExt(client_exts, "EGL_EXT_image_dma_buf_import")) @@ -771,7 +782,7 @@ static bool egl_render_startup(void * opaque, bool useDMA) eglSwapInterval(this->display, this->opt.vsync ? 1 : 0); - if (!egl_desktop_init(&this->desktop, this->display, useDMA, 1)) + if (!egl_desktop_init(&this->desktop, this->display, useDMA, MAX_ACCUMULATED_DAMAGE)) { DEBUG_ERROR("Failed to initialize the desktop"); return false; @@ -811,29 +822,132 @@ static bool egl_needs_render(void * opaque) return !this->waitDone; } +inline static EGLint egl_buffer_age(struct Inst * this) +{ + if (!this->hasBufferAge) + return 0; + + EGLint result; + eglQuerySurface(this->display, this->surface, EGL_BUFFER_AGE_EXT, &result); + return result; +} + +inline static void renderLetterBox(struct Inst * this) +{ + bool hLB = this->destRect.x > 0; + bool vLB = this->destRect.y > 0; + + if (hLB || vLB) + { + glClearColor(0.0f, 0.0f, 0.0f, 1.0f); + glEnable(GL_SCISSOR_TEST); + + if (hLB) + { + glScissor(0.0f, 0.0f, this->destRect.x, this->height); + glClear(GL_COLOR_BUFFER_BIT); + + float x2 = this->destRect.x + this->destRect.w; + glScissor(x2, 0.0f, this->width - x2, this->height); + glClear(GL_COLOR_BUFFER_BIT); + } + + if (vLB) + { + glScissor(0.0f, 0.0f, this->width, this->destRect.y); + glClear(GL_COLOR_BUFFER_BIT); + + float y2 = this->destRect.y + this->destRect.h; + glScissor(0.0f, y2, this->width, this->height - y2); + glClear(GL_COLOR_BUFFER_BIT); + } + + glDisable(GL_SCISSOR_TEST); + } +} + static bool egl_render(void * opaque, LG_RendererRotate rotate, const bool newFrame, const bool invalidateWindow) { struct Inst * this = (struct Inst *)opaque; - - glClearColor(0.0f, 0.0f, 0.0f, 1.0f); - glClear(GL_COLOR_BUFFER_BIT); + EGLint bufferAge = egl_buffer_age(this); + bool renderAll = invalidateWindow || !this->start || this->hadOverlay || + bufferAge <= 0 || bufferAge > MAX_BUFFER_AGE; bool hasOverlay = false; struct CursorState cursorState = { .visible = false }; struct DesktopDamage * desktopDamage; + char accumulated_[ + sizeof(struct DamageRects) + + MAX_ACCUMULATED_DAMAGE * sizeof(struct FrameDamageRect) + ]; + struct DamageRects * accumulated = (struct DamageRects *) accumulated_; + accumulated->count = 0; + INTERLOCKED_SECTION(this->desktopDamageLock, { + if (!renderAll) + { + for (int i = 0; i < bufferAge; ++i) + { + struct DesktopDamage * damage = this->desktopDamage + + IDX_AGO(this->desktopDamageIdx, i, DESKTOP_DAMAGE_COUNT); + + if (damage->count < 0) + { + renderAll = true; + break; + } + + memcpy(accumulated->rects + accumulated->count, damage->rects, + damage->count * sizeof(struct FrameDamageRect)); + accumulated->count += damage->count; + } + } desktopDamage = this->desktopDamage + this->desktopDamageIdx; this->desktopDamageIdx = (this->desktopDamageIdx + 1) % DESKTOP_DAMAGE_COUNT; + this->desktopDamage[this->desktopDamageIdx].count = 0; }); + if (!renderAll) + { + double matrix[6]; + egl_screenToDesktopMatrix(matrix, this->format.width, this->format.height, + this->translateX, this->translateY, this->scaleX, this->scaleY, rotate, + this->width, this->height); + + for (int i = 0; i < bufferAge; ++i) + { + int idx = IDX_AGO(this->overlayHistoryIdx, i, DESKTOP_DAMAGE_COUNT); + int count = this->overlayHistoryCount[idx]; + struct Rect * damage = this->overlayHistory[idx]; + + if (count < 0) + { + renderAll = true; + break; + } + + for (int j = 0; j < count; ++j) + accumulated->count += egl_screenToDesktop( + accumulated->rects + accumulated->count, matrix, damage + j, + this->format.width, this->format.height + ); + } + + ++this->overlayHistoryIdx; + accumulated->count = util_mergeOverlappingRects(accumulated->rects, accumulated->count); + } + + if (renderAll) + renderLetterBox(this); + if (this->start) { if (egl_desktop_render(this->desktop, this->translateX, this->translateY, this->scaleX , this->scaleY , - this->scaleType , rotate, NULL)) + this->scaleType , rotate, renderAll ? NULL : accumulated)) { if (!this->waitFadeTime) { @@ -848,7 +962,7 @@ static bool egl_render(void * opaque, LG_RendererRotate rotate, const bool newFr this->width, this->height); } else - desktopDamage->count = -1; + hasOverlay = true; } if (!this->waitDone) @@ -901,14 +1015,24 @@ static bool egl_render(void * opaque, LG_RendererRotate rotate, const bool newFr damage[i].y = this->height - damage[i].y - damage[i].h; } + if (damageIdx >= 0 && cursorState.visible) + damage[damageIdx++] = cursorState.rect; + + int overlayHistoryIdx = this->overlayHistoryIdx % DESKTOP_DAMAGE_COUNT; + if (hasOverlay) + this->overlayHistoryCount[overlayHistoryIdx] = -1; + else + { + if (damageIdx > 0) + memcpy(this->overlayHistory[overlayHistoryIdx], damage, damageIdx * sizeof(struct Rect)); + this->overlayHistoryCount[overlayHistoryIdx] = damageIdx; + } + if (!hasOverlay && !this->hadOverlay) { if (this->cursorLast.visible) damage[damageIdx++] = this->cursorLast.rect; - if (cursorState.visible) - damage[damageIdx++] = cursorState.rect; - if (desktopDamage->count == -1) // -1 damage count means invalidating entire window. damageIdx = 0; @@ -928,7 +1052,6 @@ static bool egl_render(void * opaque, LG_RendererRotate rotate, const bool newFr this->hadOverlay = hasOverlay; this->cursorLast = cursorState; - desktopDamage->count = 0; app_eglSwapBuffers(this->display, this->surface, damage, damageIdx); return true; diff --git a/client/src/util.c b/client/src/util.c index d4493054..e2470416 100644 --- a/client/src/util.c +++ b/client/src/util.c @@ -225,13 +225,12 @@ static bool rectIntersects(const FrameDamageRect * r1, const FrameDamageRect * r r2->y + r1->height > r2->y; } -int util_mergeOverlappingRects(FrameDamageRect * out, const FrameDamageRect * rects, int count) +int util_mergeOverlappingRects(FrameDamageRect * rects, int count) { bool removed[count]; bool changed; memset(removed, 0, sizeof(removed)); - memcpy(out, rects, count * sizeof(FrameDamageRect)); do { @@ -239,14 +238,14 @@ int util_mergeOverlappingRects(FrameDamageRect * out, const FrameDamageRect * re for (int i = 0; i < count; ++i) if (!removed[i]) for (int j = i + 1; j < count; ++j) - if (!removed[j] && rectIntersects(out + i, out + j)) + if (!removed[j] && rectIntersects(rects + i, rects + j)) { - uint32_t x2 = max(out[i].x + out[i].width, out[j].x + out[j].width); - uint32_t y2 = max(out[i].y + out[i].height, out[j].y + out[j].height); - out[i].x = min(out[i].x, out[j].x); - out[i].y = min(out[i].y, out[j].y); - out[i].width = x2 - out[i].x; - out[i].height = y2 - out[i].y; + uint32_t x2 = max(rects[i].x + rects[i].width, rects[j].x + rects[j].width); + uint32_t y2 = max(rects[i].y + rects[i].height, rects[j].y + rects[j].height); + rects[i].x = min(rects[i].x, rects[j].x); + rects[i].y = min(rects[i].y, rects[j].y); + rects[i].width = x2 - rects[i].x; + rects[i].height = y2 - rects[i].y; removed[j] = true; changed = true; } @@ -256,7 +255,7 @@ int util_mergeOverlappingRects(FrameDamageRect * out, const FrameDamageRect * re int o = 0; for (int i = 0; i < count; ++i) if (!removed[i]) - out[o++] = out[i]; + rects[o++] = rects[i]; return o; }