mirror of
https://github.com/gnif/LookingGlass.git
synced 2024-11-10 08:38:20 +00:00
[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.
This commit is contained in:
parent
f9977332a6
commit
87aac8cf03
@ -40,7 +40,7 @@ bool util_guestCurToLocal(struct DoublePoint *local);
|
|||||||
void util_localCurToGuest(struct DoublePoint *guest);
|
void util_localCurToGuest(struct DoublePoint *guest);
|
||||||
void util_rotatePoint(struct DoublePoint *point);
|
void util_rotatePoint(struct DoublePoint *point);
|
||||||
bool util_hasGLExt(const char * exts, const char * ext);
|
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)
|
static inline double util_clamp(double x, double min, double max)
|
||||||
{
|
{
|
||||||
|
@ -47,9 +47,13 @@
|
|||||||
#include "desktop.h"
|
#include "desktop.h"
|
||||||
#include "cursor.h"
|
#include "cursor.h"
|
||||||
#include "splash.h"
|
#include "splash.h"
|
||||||
|
#include "util.h"
|
||||||
|
|
||||||
#define SPLASH_FADE_TIME 1000000
|
#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
|
struct Options
|
||||||
{
|
{
|
||||||
@ -107,6 +111,11 @@ struct Inst
|
|||||||
unsigned int desktopDamageIdx;
|
unsigned int desktopDamageIdx;
|
||||||
LG_Lock desktopDamageLock;
|
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;
|
RingBuffer importTimings;
|
||||||
GraphHandle importGraph;
|
GraphHandle importGraph;
|
||||||
};
|
};
|
||||||
@ -725,6 +734,8 @@ static bool egl_render_startup(void * opaque, bool useDMA)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this->hasBufferAge = util_hasGLExt(client_exts, "EGL_EXT_buffer_age");
|
||||||
|
|
||||||
if (g_egl_dynProcs.glEGLImageTargetTexture2DOES)
|
if (g_egl_dynProcs.glEGLImageTargetTexture2DOES)
|
||||||
{
|
{
|
||||||
if (util_hasGLExt(client_exts, "EGL_EXT_image_dma_buf_import"))
|
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);
|
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");
|
DEBUG_ERROR("Failed to initialize the desktop");
|
||||||
return false;
|
return false;
|
||||||
@ -811,29 +822,132 @@ static bool egl_needs_render(void * opaque)
|
|||||||
return !this->waitDone;
|
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,
|
static bool egl_render(void * opaque, LG_RendererRotate rotate, const bool newFrame,
|
||||||
const bool invalidateWindow)
|
const bool invalidateWindow)
|
||||||
{
|
{
|
||||||
struct Inst * this = (struct Inst *)opaque;
|
struct Inst * this = (struct Inst *)opaque;
|
||||||
|
EGLint bufferAge = egl_buffer_age(this);
|
||||||
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
|
bool renderAll = invalidateWindow || !this->start || this->hadOverlay ||
|
||||||
glClear(GL_COLOR_BUFFER_BIT);
|
bufferAge <= 0 || bufferAge > MAX_BUFFER_AGE;
|
||||||
|
|
||||||
bool hasOverlay = false;
|
bool hasOverlay = false;
|
||||||
struct CursorState cursorState = { .visible = false };
|
struct CursorState cursorState = { .visible = false };
|
||||||
struct DesktopDamage * desktopDamage;
|
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, {
|
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;
|
desktopDamage = this->desktopDamage + this->desktopDamageIdx;
|
||||||
this->desktopDamageIdx = (this->desktopDamageIdx + 1) % DESKTOP_DAMAGE_COUNT;
|
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 (this->start)
|
||||||
{
|
{
|
||||||
if (egl_desktop_render(this->desktop,
|
if (egl_desktop_render(this->desktop,
|
||||||
this->translateX, this->translateY,
|
this->translateX, this->translateY,
|
||||||
this->scaleX , this->scaleY ,
|
this->scaleX , this->scaleY ,
|
||||||
this->scaleType , rotate, NULL))
|
this->scaleType , rotate, renderAll ? NULL : accumulated))
|
||||||
{
|
{
|
||||||
if (!this->waitFadeTime)
|
if (!this->waitFadeTime)
|
||||||
{
|
{
|
||||||
@ -848,7 +962,7 @@ static bool egl_render(void * opaque, LG_RendererRotate rotate, const bool newFr
|
|||||||
this->width, this->height);
|
this->width, this->height);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
desktopDamage->count = -1;
|
hasOverlay = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!this->waitDone)
|
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;
|
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 (!hasOverlay && !this->hadOverlay)
|
||||||
{
|
{
|
||||||
if (this->cursorLast.visible)
|
if (this->cursorLast.visible)
|
||||||
damage[damageIdx++] = this->cursorLast.rect;
|
damage[damageIdx++] = this->cursorLast.rect;
|
||||||
|
|
||||||
if (cursorState.visible)
|
|
||||||
damage[damageIdx++] = cursorState.rect;
|
|
||||||
|
|
||||||
if (desktopDamage->count == -1)
|
if (desktopDamage->count == -1)
|
||||||
// -1 damage count means invalidating entire window.
|
// -1 damage count means invalidating entire window.
|
||||||
damageIdx = 0;
|
damageIdx = 0;
|
||||||
@ -928,7 +1052,6 @@ static bool egl_render(void * opaque, LG_RendererRotate rotate, const bool newFr
|
|||||||
|
|
||||||
this->hadOverlay = hasOverlay;
|
this->hadOverlay = hasOverlay;
|
||||||
this->cursorLast = cursorState;
|
this->cursorLast = cursorState;
|
||||||
desktopDamage->count = 0;
|
|
||||||
|
|
||||||
app_eglSwapBuffers(this->display, this->surface, damage, damageIdx);
|
app_eglSwapBuffers(this->display, this->surface, damage, damageIdx);
|
||||||
return true;
|
return true;
|
||||||
|
@ -225,13 +225,12 @@ static bool rectIntersects(const FrameDamageRect * r1, const FrameDamageRect * r
|
|||||||
r2->y + r1->height > r2->y;
|
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 removed[count];
|
||||||
bool changed;
|
bool changed;
|
||||||
|
|
||||||
memset(removed, 0, sizeof(removed));
|
memset(removed, 0, sizeof(removed));
|
||||||
memcpy(out, rects, count * sizeof(FrameDamageRect));
|
|
||||||
|
|
||||||
do
|
do
|
||||||
{
|
{
|
||||||
@ -239,14 +238,14 @@ int util_mergeOverlappingRects(FrameDamageRect * out, const FrameDamageRect * re
|
|||||||
for (int i = 0; i < count; ++i)
|
for (int i = 0; i < count; ++i)
|
||||||
if (!removed[i])
|
if (!removed[i])
|
||||||
for (int j = i + 1; j < count; ++j)
|
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 x2 = max(rects[i].x + rects[i].width, rects[j].x + rects[j].width);
|
||||||
uint32_t y2 = max(out[i].y + out[i].height, out[j].y + out[j].height);
|
uint32_t y2 = max(rects[i].y + rects[i].height, rects[j].y + rects[j].height);
|
||||||
out[i].x = min(out[i].x, out[j].x);
|
rects[i].x = min(rects[i].x, rects[j].x);
|
||||||
out[i].y = min(out[i].y, out[j].y);
|
rects[i].y = min(rects[i].y, rects[j].y);
|
||||||
out[i].width = x2 - out[i].x;
|
rects[i].width = x2 - rects[i].x;
|
||||||
out[i].height = y2 - out[i].y;
|
rects[i].height = y2 - rects[i].y;
|
||||||
removed[j] = true;
|
removed[j] = true;
|
||||||
changed = true;
|
changed = true;
|
||||||
}
|
}
|
||||||
@ -256,7 +255,7 @@ int util_mergeOverlappingRects(FrameDamageRect * out, const FrameDamageRect * re
|
|||||||
int o = 0;
|
int o = 0;
|
||||||
for (int i = 0; i < count; ++i)
|
for (int i = 0; i < count; ++i)
|
||||||
if (!removed[i])
|
if (!removed[i])
|
||||||
out[o++] = out[i];
|
rects[o++] = rects[i];
|
||||||
return o;
|
return o;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user