[client] spice: added initial framework for spice display fallback

This commit is contained in:
Geoffrey McRae 2022-05-22 11:25:34 +10:00
parent ffd27ac82c
commit 16f39450b5
9 changed files with 301 additions and 13 deletions

View file

@ -134,6 +134,7 @@ set(SOURCES
src/egl_dynprocs.c
src/eglutil.c
src/overlay_utils.c
src/render_queue.c
src/overlay/alert.c
src/overlay/fps.c

View file

@ -179,5 +179,9 @@ bool app_guestIsOSX(void);
bool app_guestIsBSD(void);
bool app_guestIsOther(void);
/**
* Enable/disable the spice display
*/
void app_useSpiceDisplay(bool enable);
#endif

View file

@ -37,7 +37,11 @@
(x)->onMouseEvent && \
(x)->renderStartup && \
(x)->needsRender && \
(x)->render)
(x)->render && \
(x)->spiceConfigure && \
(x)->spiceDrawFill && \
(x)->spiceDrawBitmap && \
(x)->spiceShow)
typedef struct LG_RendererParams
{
@ -167,6 +171,20 @@ typedef struct LG_RendererOps
bool (*render)(LG_Renderer * renderer, LG_RendererRotate rotate,
const bool newFrame, const bool invalidateWindow,
void (*preSwap)(void * udata), void * udata);
/* setup the spice display */
void (*spiceConfigure)(LG_Renderer * renderer, int width, int height);
/* draw a filled rect on the spice display with the specified color */
void (*spiceDrawFill)(LG_Renderer * renderer, int x, int y, int width,
int height, uint32_t color);
/* draw an image on the spice display, data is RGBA32 */
void (*spiceDrawBitmap)(LG_Renderer * renderer, int x, int y, int width,
int height, int stride, uint8_t * data);
/* show the spice display */
void (*spiceShow)(LG_Renderer * renderer, bool show);
}
LG_RendererOps;

View file

@ -1008,3 +1008,23 @@ bool app_guestIsOther(void)
{
return g_state.guestOS == KVMFR_OS_OTHER;
}
void app_useSpiceDisplay(bool enable)
{
if (!g_params.useSpice)
return;
if (!purespice_hasChannel(PS_CHANNEL_DISPLAY))
return;
if (enable)
{
purespice_connectChannel(PS_CHANNEL_DISPLAY);
// do not call spiceShow as the surface create callback will do this
}
else
{
RENDERER(spiceShow, false);
purespice_disconnectChannel(PS_CHANNEL_DISPLAY);
}
}

View file

@ -667,7 +667,7 @@ bool config_load(int argc, char * argv[])
g_params.minimizeOnFocusLoss = option_get_bool("win", "minimizeOnFocusLoss");
if (option_get_bool("spice", "enable"))
if ((g_params.useSpice = option_get_bool("spice", "enable")))
{
g_params.spiceHost = option_get_string("spice", "host");
g_params.spicePort = option_get_int ("spice", "port");

View file

@ -62,6 +62,7 @@
#include "overlays.h"
#include "overlay_utils.h"
#include "util.h"
#include "render_queue.h"
// forwards
static int renderThread(void * unused);
@ -280,6 +281,9 @@ static int renderThread(void * unused)
const uint64_t renderStart = nanotime();
LG_LOCK(g_state.lgrLock);
renderQueue_process();
if (!RENDERER(render, g_params.winRotate, newFrame, invalidate,
preSwapCallback, (void *)&renderStart))
{
@ -595,6 +599,9 @@ int main_frameThread(void * unused)
break;
}
// disable the spice display as we have a frame from the LG host
app_useSpiceDisplay(false);
KVMFRFrame * frame = (KVMFRFrame *)msg.mem;
// ignore any repeated frames, this happens when a new client connects to
@ -800,7 +807,9 @@ int main_frameThread(void * unused)
}
lgmpClientUnsubscribe(&queue);
RENDERER(onRestart);
app_useSpiceDisplay(true);
if (g_state.useDMA)
{
@ -809,7 +818,6 @@ int main_frameThread(void * unused)
close(dmaInfo[i].fd);
}
return 0;
}
@ -862,6 +870,32 @@ void spiceReady(void)
purespice_freeServerInfo(&info);
}
static void spice_surfaceCreate(unsigned int surfaceId, PSSurfaceFormat format,
unsigned int width, unsigned int height)
{
renderQueue_spiceConfigure(width, height);
if (g_state.lgr)
RENDERER(spiceShow, true);
}
static void spice_surfaceDestroy(unsigned int surfaceId)
{
if (g_state.lgr)
RENDERER(spiceShow, false);
}
static void spice_drawFill(unsigned int surfaceId, int x, int y, int width,
int height, uint32_t color)
{
renderQueue_spiceDrawFill(x, y, width, height, color);
}
static void spice_drawBitmap(unsigned int surfaceId, PSBitmapFormat format,
bool topDown, int x, int y, int width, int height, int stride, void * data)
{
renderQueue_spiceDrawBitmap(x, y, width, height, stride, data);
}
int spiceThread(void * arg)
{
if (g_params.useSpiceAudio)
@ -886,6 +920,14 @@ int spiceThread(void * arg)
.release = cb_spiceRelease,
.request = cb_spiceRequest
},
.display =
{
.enable = true,
.surfaceCreate = spice_surfaceCreate,
.surfaceDestroy = spice_surfaceDestroy,
.drawFill = spice_drawFill,
.drawBitmap = spice_drawBitmap
},
#if ENABLE_AUDIO
.playback =
{
@ -1117,6 +1159,9 @@ static int lg_run(void)
return -1;
}
//setup the render command queue
renderQueue_init();
const PSInit psInit =
{
.log =
@ -1261,6 +1306,10 @@ static int lg_run(void)
if (g_state.cbAvailable)
g_state.cbRequestList = ll_new();
// fallback to the spice display
if (g_params.useSpice && purespice_hasChannel(PS_CHANNEL_DISPLAY))
purespice_connectChannel(PS_CHANNEL_DISPLAY);
LGMP_STATUS status;
while(g_state.state == APP_STATE_RUNNING)
@ -1610,6 +1659,8 @@ static void lg_shutdown(void)
ivshmemClose(&g_state.shm);
renderQueue_free();
// free metrics ringbuffers
ringbuffer_free(&g_state.renderTimings);
ringbuffer_free(&g_state.uploadTimings);

View file

@ -159,6 +159,7 @@ struct AppParams
unsigned int w, h;
int fpsMin;
LG_RendererRotate winRotate;
bool useSpice;
bool useSpiceInput;
bool useSpiceClipboard;
bool useSpiceAudio;

121
client/src/render_queue.c Normal file
View file

@ -0,0 +1,121 @@
/**
* Looking Glass
* Copyright © 2017-2022 The Looking Glass Authors
* https://looking-glass.io
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation; either version 2 of the License, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc., 59
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "render_queue.h"
#include <string.h>
#include "common/ll.h"
#include "main.h"
struct ll * l_renderQueue = NULL;
void renderQueue_init(void)
{
l_renderQueue = ll_new();
}
void renderQueue_clear(void)
{
RenderCommand * cmd;
while(ll_shift(l_renderQueue, (void **)&cmd))
{
if (cmd->op == SPICE_OP_DRAW_BITMAP)
free(cmd->drawBitmap.data);
free(cmd);
}
}
void renderQueue_spiceConfigure(int width, int height)
{
RenderCommand * cmd = malloc(sizeof(*cmd));
cmd->op = SPICE_OP_CONFIGURE;
cmd->configure.width = width;
cmd->configure.height = height;
ll_push(l_renderQueue, cmd);
app_invalidateWindow(true);
}
void renderQueue_spiceDrawFill(int x, int y, int width, int height,
uint32_t color)
{
RenderCommand * cmd = malloc(sizeof(*cmd));
cmd->op = SPICE_OP_DRAW_FILL;
cmd->fillRect.x = x;
cmd->fillRect.y = y;
cmd->fillRect.width = width;
cmd->fillRect.height = height;
cmd->fillRect.color = color;
ll_push(l_renderQueue, cmd);
app_invalidateWindow(true);
}
void renderQueue_spiceDrawBitmap(int x, int y, int width, int height, int stride,
void * data)
{
RenderCommand * cmd = malloc(sizeof(*cmd));
cmd->op = SPICE_OP_DRAW_BITMAP;
cmd->drawBitmap.x = x;
cmd->drawBitmap.y = y;
cmd->drawBitmap.width = width;
cmd->drawBitmap.height = height;
cmd->drawBitmap.stride = stride;
cmd->drawBitmap.data = malloc(height * stride);
memcpy(cmd->drawBitmap.data, data, height * stride);
ll_push(l_renderQueue, cmd);
app_invalidateWindow(true);
}
void renderQueue_free(void)
{
renderQueue_clear();
ll_free(l_renderQueue);
}
void renderQueue_process(void)
{
RenderCommand * cmd;
while(ll_shift(l_renderQueue, (void **)&cmd))
{
switch(cmd->op)
{
case SPICE_OP_CONFIGURE:
RENDERER(spiceConfigure,
cmd->configure.width, cmd->configure.height);
break;
case SPICE_OP_DRAW_FILL:
RENDERER(spiceDrawFill,
cmd->fillRect.x , cmd->fillRect.y,
cmd->fillRect.width, cmd->fillRect.height,
cmd->fillRect.color);
break;
case SPICE_OP_DRAW_BITMAP:
RENDERER(spiceDrawBitmap,
cmd->drawBitmap.x , cmd->drawBitmap.y,
cmd->drawBitmap.width , cmd->drawBitmap.height,
cmd->drawBitmap.stride, cmd->drawBitmap.data);
free(cmd->drawBitmap.data);
break;
}
free(cmd);
}
}

72
client/src/render_queue.h Normal file
View file

@ -0,0 +1,72 @@
/**
* Looking Glass
* Copyright © 2017-2022 The Looking Glass Authors
* https://looking-glass.io
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation; either version 2 of the License, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc., 59
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "common/ll.h"
typedef struct
{
enum
{
SPICE_OP_CONFIGURE,
SPICE_OP_DRAW_FILL,
SPICE_OP_DRAW_BITMAP
}
op;
union
{
struct
{
int width, height;
}
configure;
struct
{
int x, y;
int width, height;
uint32_t color;
}
fillRect;
struct
{
int x , y;
int width, height;
int stride;
uint8_t * data;
}
drawBitmap;
};
}
RenderCommand;
void renderQueue_init(void);
void renderQueue_free(void);
void renderQueue_clear(void);
void renderQueue_process(void);
void renderQueue_spiceConfigure(int width, int height);
void renderQueue_spiceDrawFill(int x, int y, int width, int height,
uint32_t color);
void renderQueue_spiceDrawBitmap(int x, int y, int width, int height, int stride,
void * data);