[kvmfr] decouple cursor flags from frame flags and fix timings

This commit is contained in:
Geoffrey McRae 2018-05-24 09:01:53 +10:00
parent 7a5bbb1e59
commit df7183a572
2 changed files with 79 additions and 74 deletions

View File

@ -186,11 +186,17 @@ int renderThread(void * unused)
{ {
if (state.started) if (state.started)
{ {
uint64_t start = microtime();
if (!state.lgr->render(state.lgrData, state.window)) if (!state.lgr->render(state.lgrData, state.window))
break; break;
// put some breaks on (400fps) if we are rendering too fast
if (microtime() - start < (1000000/400))
usleep((1000000/400) - (microtime() - start));
} }
else else
usleep(1000); usleep(1000000);
} }
return 0; return 0;
@ -198,36 +204,37 @@ int renderThread(void * unused)
int cursorThread(void * unused) int cursorThread(void * unused)
{ {
struct KVMFRHeader header; KVMFRCursor header;
LG_RendererCursor cursorType = LG_CURSOR_COLOR; LG_RendererCursor cursorType = LG_CURSOR_COLOR;
uint32_t version = 0; uint32_t version = 0;
memset(&header, 0, sizeof(struct KVMFRHeader)); memset(&header, 0, sizeof(KVMFRCursor));
while(state.running) while(state.running)
{ {
// poll until we have cursor data // poll until we have cursor data
if(!(state.shm->flags & KVMFR_HEADER_FLAG_CURSOR)) if(!(state.shm->cursor.flags & KVMFR_CURSOR_FLAG_UPDATE))
{ {
if (!state.running) if (!state.running)
break; break;
nsleep(100); // cursor shape updates are not so critical
// only check for udpates once every 15ms
usleep(150000);
continue; continue;
} }
// we must take a copy of the header, both to let the guest advance and to // we must take a copy of the header to prevent the contained arguments
// prevent the contained arguments being abused to overflow buffers // from being abused to overflow buffers.
memcpy(&header, (KVMFRHeader *)state.shm, sizeof(struct KVMFRHeader)); memcpy(&header, &state.shm->cursor, sizeof(struct KVMFRCursor));
__sync_and_and_fetch(&state.shm->flags, ~KVMFR_HEADER_FLAG_CURSOR);
if (header.detail.cursor.flags & KVMFR_CURSOR_FLAG_SHAPE && if (header.flags & KVMFR_CURSOR_FLAG_SHAPE &&
header.detail.cursor.version != version) header.version != version)
{ {
version = header.detail.cursor.version; version = header.version;
bool bad = false; bool bad = false;
switch(header.detail.cursor.type) switch(header.type)
{ {
case CURSOR_TYPE_COLOR : cursorType = LG_CURSOR_COLOR ; break; case CURSOR_TYPE_COLOR : cursorType = LG_CURSOR_COLOR ; break;
case CURSOR_TYPE_MONOCHROME : cursorType = LG_CURSOR_MONOCHROME ; break; case CURSOR_TYPE_MONOCHROME : cursorType = LG_CURSOR_MONOCHROME ; break;
@ -242,20 +249,20 @@ int cursorThread(void * unused)
break; break;
// check the data position is sane // check the data position is sane
const uint64_t dataSize = header.detail.cursor.height * header.detail.cursor.pitch; const uint64_t dataSize = header.height * header.pitch;
if (header.detail.cursor.dataPos + dataSize > state.shmSize) if (header.dataPos + dataSize > state.shmSize)
{ {
DEBUG_ERROR("The guest sent an invalid mouse dataPos"); DEBUG_ERROR("The guest sent an invalid mouse dataPos");
break; break;
} }
const uint8_t * data = (const uint8_t *)state.shm + header.detail.cursor.dataPos; const uint8_t * data = (const uint8_t *)state.shm + header.dataPos;
if (!state.lgr->on_mouse_shape( if (!state.lgr->on_mouse_shape(
state.lgrData, state.lgrData,
cursorType, cursorType,
header.detail.cursor.width, header.width,
header.detail.cursor.height, header.height,
header.detail.cursor.pitch, header.pitch,
data) data)
) )
{ {
@ -264,11 +271,14 @@ int cursorThread(void * unused)
} }
} }
if (header.detail.cursor.flags & KVMFR_CURSOR_FLAG_POS) // now we have taken the mouse data, we can flag to the host we are ready
__sync_and_and_fetch(&state.shm->cursor.flags, ~KVMFR_CURSOR_FLAG_UPDATE);
if (header.flags & KVMFR_CURSOR_FLAG_POS)
{ {
state.cursor.x = header.detail.cursor.x; state.cursor.x = header.x;
state.cursor.y = header.detail.cursor.y; state.cursor.y = header.y;
state.cursorVisible = header.detail.cursor.flags & KVMFR_CURSOR_FLAG_VISIBLE; state.cursorVisible = header.flags & KVMFR_CURSOR_FLAG_VISIBLE;
state.haveCursorPos = true; state.haveCursorPos = true;
state.lgr->on_mouse_event state.lgr->on_mouse_event
@ -279,13 +289,6 @@ int cursorThread(void * unused)
state.cursor.y state.cursor.y
); );
} }
// poll until we have cursor data
while(((state.shm->flags & KVMFR_HEADER_FLAG_CURSOR) == 0) && state.running)
{
usleep(1000);
continue;
}
} }
return 0; return 0;
@ -293,37 +296,42 @@ int cursorThread(void * unused)
int frameThread(void * unused) int frameThread(void * unused)
{ {
bool error = false; bool error = false;
struct KVMFRHeader header; KVMFRFrame header;
memset(&header, 0, sizeof(struct KVMFRHeader)); memset(&header, 0, sizeof(struct KVMFRFrame));
while(state.running) while(state.running)
{ {
// poll until we have a new frame // poll until we have a new frame
if(!(state.shm->flags & KVMFR_HEADER_FLAG_FRAME)) if(!(state.shm->frame.flags & KVMFR_FRAME_FLAG_UPDATE))
{ {
if (!state.running) if (!state.running)
break; break;
nsleep(100); // allow for a maximum refresh of 200fps (1000/200 = 5ms), this should
// befreqent enough without chewing up too much CPU time
usleep(5000);
continue; continue;
} }
// we must take a copy of the header, both to let the guest advance and to // we must take a copy of the header to prevent the contained
// prevent the contained arguments being abused to overflow buffers // arguments from being abused to overflow buffers.
memcpy(&header, (KVMFRHeader *)state.shm, sizeof(struct KVMFRHeader)); memcpy(&header, &state.shm->frame, sizeof(struct KVMFRFrame));
__sync_and_and_fetch(&state.shm->flags, ~KVMFR_HEADER_FLAG_FRAME);
// tell the host to continue as the host buffers up to one frame
// we can be sure the data for this frame wont be touched
__sync_and_and_fetch(&state.shm->frame.flags, ~KVMFR_FRAME_FLAG_UPDATE);
// sainty check of the frame format // sainty check of the frame format
if ( if (
header.detail.frame.type >= FRAME_TYPE_MAX || header.type >= FRAME_TYPE_MAX ||
header.detail.frame.width == 0 || header.width == 0 ||
header.detail.frame.height == 0 || header.height == 0 ||
header.detail.frame.pitch == 0 || header.pitch == 0 ||
header.detail.frame.dataPos == 0 || header.dataPos == 0 ||
header.detail.frame.dataPos > state.shmSize || header.dataPos > state.shmSize ||
header.detail.frame.pitch < header.detail.frame.width header.pitch < header.width
){ ){
usleep(1000); usleep(1000);
continue; continue;
@ -331,13 +339,13 @@ int frameThread(void * unused)
// setup the renderer format with the frame format details // setup the renderer format with the frame format details
LG_RendererFormat lgrFormat; LG_RendererFormat lgrFormat;
lgrFormat.width = header.detail.frame.width; lgrFormat.width = header.width;
lgrFormat.height = header.detail.frame.height; lgrFormat.height = header.height;
lgrFormat.stride = header.detail.frame.stride; lgrFormat.stride = header.stride;
lgrFormat.pitch = header.detail.frame.pitch; lgrFormat.pitch = header.pitch;
size_t dataSize; size_t dataSize;
switch(header.detail.frame.type) switch(header.type)
{ {
case FRAME_TYPE_ARGB: case FRAME_TYPE_ARGB:
dataSize = lgrFormat.height * lgrFormat.pitch; dataSize = lgrFormat.height * lgrFormat.pitch;
@ -361,22 +369,22 @@ int frameThread(void * unused)
break; break;
// check the header's dataPos is sane // check the header's dataPos is sane
if (header.detail.frame.dataPos + dataSize > state.shmSize) if (header.dataPos + dataSize > state.shmSize)
{ {
DEBUG_ERROR("The guest sent an invalid dataPos"); DEBUG_ERROR("The guest sent an invalid dataPos");
break; break;
} }
if (header.detail.frame.width != state.srcSize.x || header.detail.frame.height != state.srcSize.y) if (header.width != state.srcSize.x || header.height != state.srcSize.y)
{ {
state.srcSize.x = header.detail.frame.width; state.srcSize.x = header.width;
state.srcSize.y = header.detail.frame.height; state.srcSize.y = header.height;
if (params.autoResize) if (params.autoResize)
SDL_SetWindowSize(state.window, header.detail.frame.width, header.detail.frame.height); SDL_SetWindowSize(state.window, header.width, header.height);
updatePositionInfo(); updatePositionInfo();
} }
const uint8_t * data = (const uint8_t *)state.shm + header.detail.frame.dataPos; const uint8_t * data = (const uint8_t *)state.shm + header.dataPos;
if (!state.lgr->on_frame_event(state.lgrData, lgrFormat, data)) if (!state.lgr->on_frame_event(state.lgrData, lgrFormat, data))
{ {
DEBUG_ERROR("renderer on frame event returned failure"); DEBUG_ERROR("renderer on frame event returned failure");

View File

@ -21,7 +21,7 @@ Place, Suite 330, Boston, MA 02111-1307 USA
#include <stdint.h> #include <stdint.h>
#define KVMFR_HEADER_MAGIC "[[KVMFR]]" #define KVMFR_HEADER_MAGIC "[[KVMFR]]"
#define KVMFR_HEADER_VERSION 5 #define KVMFR_HEADER_VERSION 6
typedef enum FrameType typedef enum FrameType
{ {
@ -40,9 +40,10 @@ typedef enum CursorType
} }
CursorType; CursorType;
#define KVMFR_CURSOR_FLAG_VISIBLE 1 // cursor is visible #define KVMFR_CURSOR_FLAG_UPDATE 1 // cursor update available
#define KVMFR_CURSOR_FLAG_SHAPE 2 // shape updated #define KVMFR_CURSOR_FLAG_VISIBLE 2 // cursor is visible
#define KVMFR_CURSOR_FLAG_POS 4 // position updated #define KVMFR_CURSOR_FLAG_SHAPE 4 // shape updated
#define KVMFR_CURSOR_FLAG_POS 8 // position updated
typedef struct KVMFRCursor typedef struct KVMFRCursor
{ {
@ -58,8 +59,11 @@ typedef struct KVMFRCursor
} }
KVMFRCursor; KVMFRCursor;
#define KVMFR_FRAME_FLAG_UPDATE 1 // frame update available
typedef struct KVMFRFrame typedef struct KVMFRFrame
{ {
uint8_t flags; // KVMFR_FRAME_FLAGS
FrameType type; // the frame data type FrameType type; // the frame data type
uint32_t width; // the width uint32_t width; // the width
uint32_t height; // the height uint32_t height; // the height
@ -69,23 +73,16 @@ typedef struct KVMFRFrame
} }
KVMFRFrame; KVMFRFrame;
#define KVMFR_HEADER_FLAG_FRAME 1 // frame update available #define KVMFR_HEADER_FLAG_RESTART 1 // restart signal from client
#define KVMFR_HEADER_FLAG_CURSOR 2 // cursor update available #define KVMFR_HEADER_FLAG_READY 2 // ready signal from client
#define KVMFR_HEADER_FLAG_RESTART 4 // restart signal from client #define KVMFR_HEADER_FLAG_PAUSED 4 // capture has been paused by the host
#define KVMFR_HEADER_FLAG_READY 8 // ready signal from client
typedef struct KVMFRDetail
{
KVMFRFrame frame; // the frame information
KVMFRCursor cursor; // the cursor information
}
KVMFRDetail;
typedef struct KVMFRHeader typedef struct KVMFRHeader
{ {
char magic[sizeof(KVMFR_HEADER_MAGIC)]; char magic[sizeof(KVMFR_HEADER_MAGIC)];
uint32_t version; // version of this structure uint32_t version; // version of this structure
uint8_t flags; // KVMFR_HEADER_FLAGS uint8_t flags; // KVMFR_HEADER_FLAGS
KVMFRDetail detail; // details KVMFRFrame frame; // the frame information
KVMFRCursor cursor; // the cursor information
} }
KVMFRHeader; KVMFRHeader;