[host] app: allow the capture interface to select async or sync mode

While it's correct for DXGI to use a asyncronous waitFrame model, other
capture interfaces such as NvFBC it is not correct. This change allows
the capture interface to specify which is more correct for it and moves
the waitFrame/post into the main thread if async is not desired.
This commit is contained in:
Geoffrey McRae 2021-07-12 16:53:46 +10:00
parent eb01efe0cb
commit e477663a7e
5 changed files with 139 additions and 114 deletions

View File

@ -91,7 +91,8 @@ typedef void (*CapturePostPointerBuffer)(CapturePointer pointer);
typedef struct CaptureInterface
{
const char *shortName;
const char * shortName;
const bool asyncCapture;
const char * (*getName )();
void (*initOptions )();

View File

@ -236,6 +236,7 @@ static CaptureResult xcb_getFrame(FrameBuffer * frame, const unsigned int height
struct CaptureInterface Capture_XCB =
{
.shortName = "XCB",
.asyncCapture = true,
.getName = xcb_getName,
.create = xcb_create,
.init = xcb_init,

View File

@ -996,6 +996,7 @@ static CaptureResult dxgi_releaseFrame(void)
struct CaptureInterface Capture_DXGI =
{
.shortName = "DXGI",
.asyncCapture = true,
.getName = dxgi_getName,
.initOptions = dxgi_initOptions,
.create = dxgi_create,

View File

@ -424,6 +424,7 @@ static int pointerThread(void * unused)
struct CaptureInterface Capture_NVFBC =
{
.shortName = "NvFBC",
.asyncCapture = false,
.getName = nvfbc_getName,
.initOptions = nvfbc_initOptions,

View File

@ -88,10 +88,12 @@ struct app
unsigned int pointerIndex;
unsigned int pointerShapeIndex;
long pageSize;
size_t maxFrameSize;
PLGMPHostQueue frameQueue;
PLGMPMemory frameMemory[LGMP_Q_FRAME_LEN];
unsigned int frameIndex;
bool frameValid;
CaptureInterface * iface;
@ -140,53 +142,38 @@ static bool lgmpTimer(void * opaque)
return true;
}
static int frameThread(void * opaque)
static bool sendFrame(void)
{
DEBUG_INFO("Frame thread started");
bool frameValid = false;
bool repeatFrame = false;
CaptureFrame frame = { 0 };
const long pageSize = sysinfo_getPageSize();
while(app.state == APP_STATE_RUNNING)
{
//wait until there is room in the queue
if(lgmpHostQueuePending(app.frameQueue) == LGMP_Q_FRAME_LEN)
{
usleep(1);
continue;
}
bool repeatFrame = false;
switch(app.iface->waitFrame(&frame, app.maxFrameSize))
{
case CAPTURE_RESULT_OK:
repeatFrame = false;
break;
case CAPTURE_RESULT_REINIT:
{
app.state = APP_STATE_RESTART;
DEBUG_INFO("Frame thread reinit");
return 0;
return false;
}
case CAPTURE_RESULT_ERROR:
{
DEBUG_ERROR("Failed to get the frame");
return 0;
return false;
}
case CAPTURE_RESULT_TIMEOUT:
{
if (frameValid && lgmpHostQueueNewSubs(app.frameQueue) > 0)
if (app.frameValid && lgmpHostQueueNewSubs(app.frameQueue) > 0)
{
// resend the last frame
repeatFrame = true;
break;
}
continue;
return true;
}
}
@ -197,7 +184,7 @@ static int frameThread(void * opaque)
{
if ((status = lgmpHostQueuePost(app.frameQueue, 0, app.frameMemory[app.frameIndex])) != LGMP_OK)
DEBUG_ERROR("%s", lgmpStatusString(status));
continue;
return true;
}
// we increment the index first so that if we need to repeat a frame
@ -214,7 +201,7 @@ static int frameThread(void * opaque)
case CAPTURE_FMT_RGBA16F: fi->type = FRAME_TYPE_RGBA16F; break;
default:
DEBUG_ERROR("Unsupported frame format %d, skipping frame", frame.format);
continue;
return true;
}
switch(frame.rotation)
@ -235,10 +222,10 @@ static int frameThread(void * opaque)
fi->realHeight = frame.realHeight;
fi->stride = frame.stride;
fi->pitch = frame.pitch;
fi->offset = pageSize - FrameBufferStructSize;
fi->offset = app.pageSize - FrameBufferStructSize;
fi->mouseScalePercent = app.iface->getMouseScale();
fi->blockScreensaver = os_blockScreensaver();
frameValid = true;
app.frameValid = true;
// put the framebuffer on the border of the next page
// this is to allow for aligned DMA transfers by the receiver
@ -249,9 +236,28 @@ static int frameThread(void * opaque)
if ((status = lgmpHostQueuePost(app.frameQueue, 0, app.frameMemory[app.frameIndex])) != LGMP_OK)
{
DEBUG_ERROR("%s", lgmpStatusString(status));
return true;
}
app.iface->getFrame(fb, frame.height);
return true;
}
static int frameThread(void * opaque)
{
DEBUG_INFO("Frame thread started");
while(app.state == APP_STATE_RUNNING)
{
//wait until there is room in the queue
if(lgmpHostQueuePending(app.frameQueue) == LGMP_Q_FRAME_LEN)
{
usleep(1);
continue;
}
app.iface->getFrame(fb, frame.height);
if (!sendFrame())
break;
}
DEBUG_INFO("Frame thread stopped");
return 0;
@ -260,6 +266,9 @@ static int frameThread(void * opaque)
bool startThreads(void)
{
app.state = APP_STATE_RUNNING;
if (!app.iface->asyncCapture)
return true;
if (!lgCreateThread("FrameThread", frameThread, NULL, &app.frameThread))
{
DEBUG_ERROR("Failed to create the frame thread");
@ -271,20 +280,25 @@ bool startThreads(void)
bool stopThreads(void)
{
bool ok = true;
app.iface->stop();
if (app.state != APP_STATE_SHUTDOWN)
app.state = APP_STATE_IDLE;
if (app.frameThread && !lgJoinThread(app.frameThread, NULL))
if (!app.iface->asyncCapture)
return true;
if (app.frameThread)
{
if (!lgJoinThread(app.frameThread, NULL))
{
DEBUG_WARN("Failed to join the frame thread");
ok = false;
app.frameThread = NULL;
return false;
}
app.frameThread = NULL;
}
return ok;
return true;
}
static bool captureStart(void)
@ -564,17 +578,19 @@ int app_main(int argc, char * argv[])
memset(lgmpHostMemPtr(app.pointerShapeMemory[i]), 0, MAX_POINTER_SIZE);
}
app.pageSize = sysinfo_getPageSize();
app.frameValid = false;
app.pointerShapeValid = false;
const long sz = sysinfo_getPageSize();
app.maxFrameSize = lgmpHostMemAvail(app.lgmp);
app.maxFrameSize = (app.maxFrameSize - (sz - 1)) & ~(sz - 1);
app.maxFrameSize = (app.maxFrameSize - (app.pageSize - 1)) & ~(app.pageSize - 1);
app.maxFrameSize /= LGMP_Q_FRAME_LEN;
DEBUG_INFO("Max Frame Size : %u MiB", (unsigned int)(app.maxFrameSize / 1048576LL));
for(int i = 0; i < LGMP_Q_FRAME_LEN; ++i)
{
if ((status = lgmpHostMemAllocAligned(app.lgmp, app.maxFrameSize, sz, &app.frameMemory[i])) != LGMP_OK)
if ((status = lgmpHostMemAllocAligned(app.lgmp, app.maxFrameSize,
app.pageSize, &app.frameMemory[i])) != LGMP_OK)
{
DEBUG_ERROR("lgmpHostMemAlloc Failed (Frame): %s", lgmpStatusString(status));
exitcode = LG_HOST_EXIT_FATAL;
@ -616,6 +632,8 @@ int app_main(int argc, char * argv[])
}
DEBUG_INFO("Using : %s", iface->getName());
DEBUG_INFO("Capture Method : %s", iface->asyncCapture ?
"Asynchronous" : "Synchronous");
app.state = APP_STATE_RUNNING;
app.iface = iface;
@ -710,6 +728,9 @@ int app_main(int argc, char * argv[])
exitcode = LG_HOST_EXIT_FAILED;
goto fail_capture;
}
if (!iface->asyncCapture)
sendFrame();
}
if (app.state != APP_STATE_SHUTDOWN)