diff --git a/client/include/interface/renderer.h b/client/include/interface/renderer.h index cf8a25dd..f9bf9de3 100644 --- a/client/include/interface/renderer.h +++ b/client/include/interface/renderer.h @@ -114,7 +114,7 @@ typedef void (* LG_RendererOnResize )(void * opaque, const int width typedef bool (* LG_RendererOnMouseShape )(void * opaque, const LG_RendererCursor cursor, const int width, const int height, const int pitch, const uint8_t * data); typedef bool (* LG_RendererOnMouseEvent )(void * opaque, const bool visible , const int x, const int y); typedef bool (* LG_RendererOnFrameFormat)(void * opaque, const LG_RendererFormat format, bool useDMA); -typedef bool (* LG_RendererOnFrame )(void * opaque, const FrameBuffer * frame, int dmaFD); +typedef bool (* LG_RendererOnFrame )(void * opaque, const FrameBuffer * frame, int dmaFD, const FrameDamageRect * damage, int damageCount); typedef void (* LG_RendererOnAlert )(void * opaque, const LG_MsgAlert alert, const char * message, bool ** closeFlag); typedef void (* LG_RendererOnHelp )(void * opaque, const char * message); typedef void (* LG_RendererOnShowFPS )(void * opaque, bool showFPS); diff --git a/client/renderers/EGL/egl.c b/client/renderers/EGL/egl.c index 20a28ff7..726c18af 100644 --- a/client/renderers/EGL/egl.c +++ b/client/renderers/EGL/egl.c @@ -21,6 +21,7 @@ #include "interface/renderer.h" #include "common/debug.h" +#include "common/KVMFR.h" #include "common/option.h" #include "common/sysinfo.h" #include "common/time.h" @@ -59,6 +60,12 @@ struct Options bool doubleBuffer; }; +struct DesktopDamage +{ + int count; + FrameDamageRect rects[KVMFR_MAX_DAMAGE_RECTS]; +}; + struct Inst { bool dmaSupport; @@ -116,6 +123,8 @@ struct Inst bool cursorLastValid; struct CursorState cursorLast; + + _Atomic(struct DesktopDamage *) desktopDamage; }; static struct Option egl_options[] = @@ -263,6 +272,8 @@ bool egl_create(void ** opaque, const LG_RendererParams params, bool * needsOpen this->screenScaleY = 1.0f; this->uiScale = 1.0; + atomic_init(&this->desktopDamage, NULL); + this->font = LG_Fonts[0]; if (!egl_update_font(this)) return false; @@ -486,6 +497,15 @@ void egl_on_resize(void * opaque, const int width, const int height, const doubl egl_update_help_font(this); this->cursorLastValid = false; + + struct DesktopDamage * damage = malloc(sizeof(struct DesktopDamage)); + if (!damage) + { + DEBUG_FATAL("Out of memory"); + abort(); + } + damage->count = 0; + free(atomic_exchange(&this->desktopDamage, damage)); } bool egl_on_mouse_shape(void * opaque, const LG_RendererCursor cursor, @@ -548,7 +568,8 @@ bool egl_on_frame_format(void * opaque, const LG_RendererFormat format, bool use return egl_desktop_setup(this->desktop, format, useDMA); } -bool egl_on_frame(void * opaque, const FrameBuffer * frame, int dmaFd) +bool egl_on_frame(void * opaque, const FrameBuffer * frame, int dmaFd, + const FrameDamageRect * damageRects, int damageRectsCount) { struct Inst * this = (struct Inst *)opaque; @@ -560,6 +581,17 @@ bool egl_on_frame(void * opaque, const FrameBuffer * frame, int dmaFd) this->start = true; this->cursorLastValid = false; + + struct DesktopDamage * damage = malloc(sizeof(struct DesktopDamage)); + if (!damage) + { + DEBUG_FATAL("Out of memory"); + abort(); + } + damage->count = damageRectsCount; + memcpy(damage->rects, damageRects, damageRectsCount * sizeof(FrameDamageRect)); + free(atomic_exchange(&this->desktopDamage, damage)); + return true; } @@ -801,24 +833,31 @@ bool egl_render(void * opaque, LG_RendererRotate rotate) bool hasLastCursor = this->cursorLastValid; bool cursorRendered = false; struct CursorState cursorState; + struct DesktopDamage * desktopDamage = NULL; - if (this->start && egl_desktop_render(this->desktop, + if (this->start) + { + desktopDamage = atomic_exchange(&this->desktopDamage, NULL); + if (egl_desktop_render(this->desktop, this->translateX, this->translateY, this->scaleX , this->scaleY , this->scaleType , rotate)) - { - if (!this->waitFadeTime) { - if (!this->params.quickSplash) - this->waitFadeTime = microtime() + SPLASH_FADE_TIME; - else - this->waitDone = true; - } + if (!this->waitFadeTime) + { + if (!this->params.quickSplash) + this->waitFadeTime = microtime() + SPLASH_FADE_TIME; + else + this->waitDone = true; + } - cursorRendered = true; - cursorState = egl_cursor_get_state(this->cursor, this->width, this->height); - egl_cursor_render(this->cursor, - (this->format.rotate + rotate) % LG_ROTATE_MAX); + cursorRendered = true; + cursorState = egl_cursor_get_state(this->cursor, this->width, this->height); + egl_cursor_render(this->cursor, + (this->format.rotate + rotate) % LG_ROTATE_MAX); + } + else + desktopDamage->count = 0; } if (!this->waitDone) @@ -864,7 +903,7 @@ bool egl_render(void * opaque, LG_RendererRotate rotate) egl_alert_render(this->alert, this->screenScaleX, this->screenScaleY); } - struct Rect damage[2]; + struct Rect damage[KVMFR_MAX_DAMAGE_RECTS + 2]; int damageIdx = 0; if (this->waitDone) @@ -883,6 +922,35 @@ bool egl_render(void * opaque, LG_RendererRotate rotate) { this->cursorLast = cursorState; this->cursorLastValid = true; + + if (cursorState.visible) + damage[damageIdx++] = cursorState.rect; + } + + if (desktopDamage) + { + if (desktopDamage->count == 0) + // Zero damage count means invalidating entire window. + damageIdx = 0; + else + { + double scaleX = (double) this->destRect.w / this->format.width; + double scaleY = (double) this->destRect.h / this->format.height; + for (int i = 0; i < desktopDamage->count; ++i) + { + FrameDamageRect rect = desktopDamage->rects[i]; + int x1 = (int) (rect.x * scaleX); + int y1 = (int) (rect.y * scaleY); + int x2 = (int) ceil((rect.x + rect.width) * scaleX); + int y2 = (int) ceil((rect.y + rect.height) * scaleY); + damage[damageIdx++] = (struct Rect) { + .x = this->destRect.x + x1, + .y = this->height - (this->destRect.y + y2), + .w = x2 - x1, + .h = y2 - y1, + }; + } + } } } diff --git a/client/renderers/OpenGL/opengl.c b/client/renderers/OpenGL/opengl.c index 81fa2ed0..13e8bcbc 100644 --- a/client/renderers/OpenGL/opengl.c +++ b/client/renderers/OpenGL/opengl.c @@ -419,7 +419,8 @@ bool opengl_on_frame_format(void * opaque, const LG_RendererFormat format, bool return true; } -bool opengl_on_frame(void * opaque, const FrameBuffer * frame, int dmaFd) +bool opengl_on_frame(void * opaque, const FrameBuffer * frame, int dmaFd, + const FrameDamageRect * damage, int damageCount) { struct Inst * this = (struct Inst *)opaque; diff --git a/client/src/main.c b/client/src/main.c index e0e4460c..1e538492 100644 --- a/client/src/main.c +++ b/client/src/main.c @@ -589,7 +589,8 @@ int main_frameThread(void * unused) } FrameBuffer * fb = (FrameBuffer *)(((uint8_t*)frame) + frame->offset); - if (!g_state.lgr->on_frame(g_state.lgrData, fb, useDMA ? dma->fd : -1)) + if (!g_state.lgr->on_frame(g_state.lgrData, fb, useDMA ? dma->fd : -1, + frame->damageRects, frame->damageRectsCount)) { lgmpClientMessageDone(queue); DEBUG_ERROR("renderer on frame returned failure");