[client/host] added new asyncronous memory copy

This changes the method of the memory copy from the host application to
the guest. Instead of performing a full copy from the capture device
into shared memory, and then flagging the new frame, we instead set a
write pointer, flag the client that there is a new frame and then copy
in chunks of 1024 bytes until the entire frame is copied. The client
upon seeing the new frame flag begins to poll at high frequency the
write pointer and upon each update copies as much as it can into the
texture.

This should improve latency but also slightly increase CPU usage on the
client due to the high frequency polling.
This commit is contained in:
Geoffrey McRae 2019-10-01 23:17:20 +10:00
parent 6d2c464436
commit bca54ab1f6
17 changed files with 358 additions and 277 deletions

View file

@ -21,6 +21,7 @@ Place, Suite 330, Boston, MA 02111-1307 USA
#include <stdbool.h>
#include <stdint.h>
#include "common/framebuffer.h"
typedef enum CaptureResult
{
@ -55,7 +56,6 @@ typedef struct CaptureFrame
unsigned int pitch;
unsigned int stride;
CaptureFormat format;
void * data;
}
CaptureFrame;
@ -84,7 +84,8 @@ typedef struct CaptureInterface
unsigned int (*getMaxFrameSize)();
CaptureResult (*capture )();
CaptureResult (*getFrame )(CaptureFrame * frame );
CaptureResult (*waitFrame )(CaptureFrame * frame );
CaptureResult (*getFrame )(FrameBuffer frame );
CaptureResult (*getPointer)(CapturePointer * pointer);
}
CaptureInterface;

View file

@ -766,7 +766,7 @@ static CaptureResult dxgi_capture()
return CAPTURE_RESULT_OK;
}
static CaptureResult dxgi_getFrame(CaptureFrame * frame)
static CaptureResult dxgi_waitFrame(CaptureFrame * frame)
{
assert(this);
assert(this->initialized);
@ -778,7 +778,6 @@ static CaptureResult dxgi_getFrame(CaptureFrame * frame)
if (this->stop)
return CAPTURE_RESULT_REINIT;
// only reset the event if we used the texture
os_resetEvent(tex->mapped);
frame->width = this->width;
@ -787,7 +786,16 @@ static CaptureResult dxgi_getFrame(CaptureFrame * frame)
frame->stride = this->stride;
frame->format = this->format;
memcpy(frame->data, tex->map.pData, this->pitch * this->height);
return CAPTURE_RESULT_OK;
}
static CaptureResult dxgi_getFrame(FrameBuffer frame)
{
assert(this);
assert(this->initialized);
Texture * tex = &this->texture[this->texRIndex];
framebuffer_write(frame, tex->map.pData, this->pitch * this->height);
os_signalEvent(tex->free);
if (++this->texRIndex == this->maxTextures)
@ -867,6 +875,7 @@ struct CaptureInterface Capture_DXGI =
.free = dxgi_free,
.getMaxFrameSize = dxgi_getMaxFrameSize,
.capture = dxgi_capture,
.waitFrame = dxgi_waitFrame,
.getFrame = dxgi_getFrame,
.getPointer = dxgi_getPointer
};

View file

@ -23,6 +23,7 @@ Place, Suite 330, Boston, MA 02111-1307 USA
#include "windows/debug.h"
#include "windows/mousehook.h"
#include "common/option.h"
#include "common/framebuffer.h"
#include <assert.h>
#include <stdlib.h>
#include <windows.h>
@ -236,7 +237,7 @@ static CaptureResult nvfbc_capture()
return CAPTURE_RESULT_OK;
}
static CaptureResult nvfbc_getFrame(CaptureFrame * frame)
static CaptureResult nvfbc_waitFrame(CaptureFrame * frame)
{
if (!os_waitEvent(this->frameEvent, 1000))
return CAPTURE_RESULT_TIMEOUT;
@ -266,7 +267,16 @@ static CaptureResult nvfbc_getFrame(CaptureFrame * frame)
#endif
frame->format = this->grabInfo.bIsHDR ? CAPTURE_FMT_RGBA10 : CAPTURE_FMT_BGRA;
memcpy(frame->data, this->frameBuffer, frame->pitch * frame->height);
return CAPTURE_RESULT_OK;
}
static CaptureResult nvfbc_getFrame(FrameBuffer frame)
{
framebuffer_write(
frame,
this->frameBuffer,
this->grabInfo.dwHeight * this->grabInfo.dwBufferWidth * 4
);
return CAPTURE_RESULT_OK;
}
@ -310,6 +320,7 @@ struct CaptureInterface Capture_NVFBC =
.free = nvfbc_free,
.getMaxFrameSize = nvfbc_getMaxFrameSize,
.capture = nvfbc_capture,
.waitFrame = nvfbc_waitFrame,
.getFrame = nvfbc_getFrame,
.getPointer = nvfbc_getPointer
};

View file

@ -49,7 +49,7 @@ struct app
uint8_t * frames;
unsigned int frameSize;
uint8_t * frame[MAX_FRAMES];
FrameBuffer frame[MAX_FRAMES];
unsigned int frameOffset[MAX_FRAMES];
bool running;
@ -168,9 +168,7 @@ static int frameThread(void * opaque)
while(app.running)
{
frame.data = app.frame[frameIndex];
switch(app.iface->getFrame(&frame))
switch(app.iface->waitFrame(&frame))
{
case CAPTURE_RESULT_OK:
break;
@ -226,7 +224,9 @@ static int frameThread(void * opaque)
fi->dataPos = app.frameOffset[frameIndex];
frameValid = true;
framebuffer_prepare(app.frame[frameIndex]);
INTERLOCKED_OR8(&fi->flags, KVMFR_FRAME_FLAG_UPDATE);
app.iface->getFrame(app.frame[frameIndex]);
if (++frameIndex == MAX_FRAMES)
frameIndex = 0;
@ -369,8 +369,8 @@ int app_main(int argc, char * argv[])
for (int i = 0; i < MAX_FRAMES; ++i)
{
app.frame [i] = app.frames + i * app.frameSize;
app.frameOffset[i] = app.frame[i] - shmemMap;
app.frame [i] = (FrameBuffer)(app.frames + i * app.frameSize);
app.frameOffset[i] = (uint8_t *)app.frame[i] - shmemMap;
DEBUG_INFO("Frame %d : 0x%" PRIXPTR " (0x%08x)", i, (uintptr_t)app.frame[i], app.frameOffset[i]);
}