mirror of
https://dev.lirent.ru/Vatrog/vm-automation-signaling.git
synced 2026-06-26 04:36:37 +03:00
vgpu in-guest producer in-tree, release CI, flexible vmie discovery
- src/si/vgpu-stream: in-guest vgpu producer built as a Windows cross-compiled target (if(WIN32)) - .gitea: release workflow — cross-build the agent and build/publish the deb against system vmie - cmake/makefile: resolve vmie from a source tree (LIBVMIE_PATH) or installed libvmie-dev
This commit is contained in:
@@ -0,0 +1,162 @@
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <windows.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include "capture_nvfbc.h"
|
||||
#include "capture-win32.h" /* capture_thread_arg (win32-private) */
|
||||
#include "present.h"
|
||||
#include "cursor.h" /* cursor_apply_shape / ctx->cursor */
|
||||
#include "geometry.h" /* reactive geometry resample on recreate */
|
||||
#include "stream.h" /* vgpu_publish_cursor / vgpu_publish_cursor_shape */
|
||||
#include "nvfbc_tosys_c.h"
|
||||
|
||||
typedef struct {
|
||||
NvFBCToSys_c* fbc;
|
||||
void* buf;
|
||||
NvFBC_CreateFunctionExType create;
|
||||
HCURSOR last_handle; /* shape-gate by HCURSOR change */
|
||||
} nvfbc_state;
|
||||
|
||||
/* Source the cursor for an NvFBC grab and publish it under the cursor_seq gate. NvFBC reports
|
||||
* only HW-cursor visibility (gi.bHWMouseVisible); position is not exposed, so one GetCursorInfo
|
||||
* per frame supplies x/y (the minimum possible). Shape is re-extracted only on HCURSOR change.
|
||||
* NvFBC composites the cursor itself (draw_cursor_cap==0) → present never reads ctx->cursor for
|
||||
* drawing, so no ctx->lock is required around the compose fields here.
|
||||
* gi.bProtectedContent / gi.dwSourcePID are available but out of scope (not in the contract). */
|
||||
static void nvfbc_source_cursor(vgpu_ctx* ctx, nvfbc_state* st,
|
||||
const NvFBCFrameGrabInfo* gi) {
|
||||
CURSORINFO ci; ci.cbSize = sizeof ci;
|
||||
int vis = gi->bHWMouseVisible ? 1 : 0;
|
||||
int x = ctx->cursor.x, y = ctx->cursor.y;
|
||||
if (GetCursorInfo(&ci)) {
|
||||
x = ci.ptScreenPos.x; y = ci.ptScreenPos.y;
|
||||
if (ci.hCursor && ci.hCursor != st->last_handle) {
|
||||
cursor_apply_shape(ctx, ci.hCursor);
|
||||
st->last_handle = ci.hCursor;
|
||||
}
|
||||
}
|
||||
ctx->cursor.visible = vis; ctx->cursor.x = x; ctx->cursor.y = y;
|
||||
|
||||
vgpu_publish_cursor_shape(&ctx->view,
|
||||
(uint32_t)ctx->cursor.hot_x, (uint32_t)ctx->cursor.hot_y,
|
||||
(uint32_t)ctx->cursor.gw, (uint32_t)ctx->cursor.gh,
|
||||
(uint32_t)ctx->cursor.cursor_id);
|
||||
vgpu_publish_cursor(&ctx->view, (int32_t)x, (int32_t)y, (uint32_t)vis);
|
||||
}
|
||||
|
||||
static NvFBCToSys_c* nvfbc_create(NvFBC_CreateFunctionExType pCreate, void** ppBuf) {
|
||||
NvFBCCreateParams cp; memset(&cp, 0, sizeof cp);
|
||||
cp.dwVersion = NVFBC_CREATE_PARAMS_VER;
|
||||
cp.dwInterfaceType = NVFBC_TO_SYS_C;
|
||||
cp.dwAdapterIdx = 0;
|
||||
if (pCreate(&cp) != NVFBC_SUCCESS || !cp.pNvFBC) return NULL;
|
||||
|
||||
NvFBCToSys_c* fbc = (NvFBCToSys_c*)cp.pNvFBC;
|
||||
*ppBuf = NULL;
|
||||
|
||||
NVFBC_TOSYS_SETUP_PARAMS_C sp; memset(&sp, 0, sizeof sp);
|
||||
sp.dwVersion = NVFBC_TOSYS_SETUP_PARAMS_VER_C;
|
||||
sp.bits = 1u; /* bWithHWCursor = 1 (bit 0) */
|
||||
sp.eMode = NVFBC_TOSYS_ARGB;
|
||||
sp.ppBuffer = ppBuf;
|
||||
if (fbc->lpVtbl->NvFBCToSysSetUp(fbc, &sp) != NVFBC_SUCCESS || !*ppBuf) {
|
||||
fbc->lpVtbl->NvFBCToSysRelease(fbc);
|
||||
return NULL;
|
||||
}
|
||||
return fbc;
|
||||
}
|
||||
|
||||
static DWORD WINAPI nvfbc_thread(LPVOID param) {
|
||||
capture_thread_arg* arg = (capture_thread_arg*)param;
|
||||
vgpu_ctx* ctx = arg->ctx;
|
||||
nvfbc_state* st = (nvfbc_state*)arg->backend_state;
|
||||
free(arg);
|
||||
|
||||
NvFBCToSys_c* fbc = st->fbc;
|
||||
void* buf = st->buf;
|
||||
|
||||
for (;;) {
|
||||
NvFBCFrameGrabInfo gi; memset(&gi, 0, sizeof gi);
|
||||
NVFBC_TOSYS_GRAB_FRAME_PARAMS_C gp; memset(&gp, 0, sizeof gp);
|
||||
gp.dwVersion = NVFBC_TOSYS_GRAB_FRAME_PARAMS_VER_C;
|
||||
gp.dwFlags = NVFBC_TOSYS_WAIT_WITH_TIMEOUT_C;
|
||||
gp.dwWaitTime = 1000;
|
||||
gp.eGMode = NVFBC_TOSYS_SOURCEMODE_FULL;
|
||||
gp.pNvFBCFrameGrabInfo = &gi;
|
||||
|
||||
NVFBCRESULT r = fbc->lpVtbl->NvFBCToSysGrabFrame(fbc, &gp);
|
||||
if (r != NVFBC_SUCCESS) {
|
||||
if (r == NVFBC_ERROR_INVALIDATED_SESSION || gi.bMustRecreate) {
|
||||
fprintf(stderr, "eyes(nvfbc): session invalidated (r=%d), recreating\n", (int)r);
|
||||
fbc->lpVtbl->NvFBCToSysRelease(fbc);
|
||||
fbc = NULL;
|
||||
while (!(fbc = nvfbc_create(st->create, &buf))) Sleep(200);
|
||||
st->fbc = fbc; st->buf = buf;
|
||||
/* grab session was recreated → display config may have changed: resample */
|
||||
geometry_sample_and_publish(ctx, 0, 0);
|
||||
} else {
|
||||
Sleep(50);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (gi.dwWidth && gi.dwHeight)
|
||||
vgpu_present_submit(ctx, (const uint8_t*)buf,
|
||||
gi.dwWidth, gi.dwHeight, gi.dwBufferWidth * 4u);
|
||||
nvfbc_source_cursor(ctx, st, &gi);
|
||||
}
|
||||
return 0; /* unreachable; satisfies -Wreturn-type */
|
||||
}
|
||||
|
||||
int nvfbc_start(vgpu_ctx* ctx, int fps) {
|
||||
(void)fps;
|
||||
HMODULE lib = LoadLibraryA("NvFBC64.dll");
|
||||
if (!lib) {
|
||||
fprintf(stderr, "eyes(nvfbc): LoadLibrary NvFBC64.dll failed (%lu)\n", GetLastError());
|
||||
return 0;
|
||||
}
|
||||
NvFBC_SetGlobalFlagsType pSetFlags = (NvFBC_SetGlobalFlagsType)(void*)GetProcAddress(lib, "NvFBC_SetGlobalFlags");
|
||||
NvFBC_EnableFunctionType pEnable = (NvFBC_EnableFunctionType)(void*)GetProcAddress(lib, "NvFBC_Enable");
|
||||
NvFBC_CreateFunctionExType pCreate = (NvFBC_CreateFunctionExType)(void*)GetProcAddress(lib, "NvFBC_CreateEx");
|
||||
NvFBC_GetStatusExFunctionType pStatus = (NvFBC_GetStatusExFunctionType)(void*)GetProcAddress(lib, "NvFBC_GetStatusEx");
|
||||
if (!pEnable || !pCreate || !pStatus) {
|
||||
fprintf(stderr, "eyes(nvfbc): missing exports\n");
|
||||
return 0;
|
||||
}
|
||||
if (pSetFlags) pSetFlags(NVFBC_GLOBAL_FLAGS_NO_INITIAL_REFRESH);
|
||||
if (pEnable(NVFBC_STATE_ENABLE) != NVFBC_SUCCESS) {
|
||||
fprintf(stderr, "eyes(nvfbc): NvFBC_Enable failed\n");
|
||||
return 0;
|
||||
}
|
||||
NvFBCStatusEx stx; memset(&stx, 0, sizeof stx);
|
||||
stx.dwVersion = NVFBC_STATUS_VER; stx.dwAdapterIdx = 0;
|
||||
if (pStatus(&stx) != NVFBC_SUCCESS || !stx.bIsCapturePossible) {
|
||||
fprintf(stderr, "eyes(nvfbc): capture NOT possible on this GPU/license\n");
|
||||
return 0;
|
||||
}
|
||||
void* buf = NULL;
|
||||
NvFBCToSys_c* fbc = nvfbc_create(pCreate, &buf);
|
||||
if (!fbc) {
|
||||
fprintf(stderr, "eyes(nvfbc): CreateEx/ToSysSetUp failed\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
nvfbc_state* st = (nvfbc_state*)malloc(sizeof *st);
|
||||
if (!st) { fbc->lpVtbl->NvFBCToSysRelease(fbc); return 0; }
|
||||
st->fbc = fbc; st->buf = buf; st->create = pCreate; st->last_handle = NULL;
|
||||
|
||||
capture_thread_arg* arg = (capture_thread_arg*)malloc(sizeof *arg);
|
||||
if (!arg) { fbc->lpVtbl->NvFBCToSysRelease(fbc); free(st); return 0; }
|
||||
arg->ctx = ctx; arg->fps = fps; arg->backend_state = st;
|
||||
|
||||
ctx->backend = VGPU_BK_NVFBC;
|
||||
ctx->draw_cursor_cap = 0; /* NvFBC composites HW cursor itself */
|
||||
|
||||
HANDLE t = CreateThread(NULL, 0, nvfbc_thread, arg, 0, NULL);
|
||||
if (!t) { fbc->lpVtbl->NvFBCToSysRelease(fbc); free(st); free(arg); return 0; }
|
||||
CloseHandle(t);
|
||||
|
||||
fprintf(stderr, "eyes(nvfbc): session up (ToSys ARGB/BGRA), iface=0x%lx\n",
|
||||
(unsigned long)stx.dwNvFBCVersion);
|
||||
return 1;
|
||||
}
|
||||
Reference in New Issue
Block a user