mirror of
https://dev.lirent.ru/Vatrog/vm-vgpu-streamer.git
synced 2026-07-09 21:36:37 +03:00
Initial commit: win32 vGPU stream capture module
Capture backends (NvFBC/DDA/GDI), cursor/region/present helpers, publish API, vendor NvFBC headers; CMake build with mingw-w64 toolchain.
This commit is contained in:
@@ -0,0 +1,129 @@
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#define COBJMACROS
|
||||
#include <windows.h>
|
||||
#include <d3d11.h>
|
||||
#include <dxgi1_2.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "capture_dda.h"
|
||||
#include "capture-win32.h" /* capture_thread_arg (win32-private) */
|
||||
#include "present.h"
|
||||
|
||||
typedef struct {
|
||||
ID3D11Device* dev;
|
||||
ID3D11DeviceContext* dctx;
|
||||
IDXGIOutput1* out1;
|
||||
IDXGIOutputDuplication* dup;
|
||||
ID3D11Texture2D* staging;
|
||||
UINT W, H;
|
||||
} dda_state;
|
||||
|
||||
static DWORD WINAPI dda_thread(LPVOID param) {
|
||||
capture_thread_arg* arg = (capture_thread_arg*)param;
|
||||
vgpu_ctx* ctx = arg->ctx;
|
||||
dda_state* st = (dda_state*)arg->backend_state;
|
||||
free(arg);
|
||||
|
||||
for (;;) {
|
||||
DXGI_OUTDUPL_FRAME_INFO fi;
|
||||
IDXGIResource* res = NULL;
|
||||
HRESULT hr = st->dup->lpVtbl->AcquireNextFrame(st->dup, 1000, &fi, &res);
|
||||
if (hr == DXGI_ERROR_WAIT_TIMEOUT) continue;
|
||||
if (hr == DXGI_ERROR_ACCESS_LOST) {
|
||||
if (st->dup) { st->dup->lpVtbl->Release(st->dup); st->dup = NULL; }
|
||||
if (FAILED(st->out1->lpVtbl->DuplicateOutput(st->out1,
|
||||
(IUnknown*)st->dev, &st->dup)))
|
||||
Sleep(200);
|
||||
continue;
|
||||
}
|
||||
if (FAILED(hr)) { Sleep(50); continue; }
|
||||
|
||||
ID3D11Texture2D* tex = NULL;
|
||||
res->lpVtbl->QueryInterface(res, &IID_ID3D11Texture2D, (void**)&tex);
|
||||
if (tex) {
|
||||
st->dctx->lpVtbl->CopyResource(st->dctx,
|
||||
(ID3D11Resource*)st->staging, (ID3D11Resource*)tex);
|
||||
D3D11_MAPPED_SUBRESOURCE m;
|
||||
if (SUCCEEDED(st->dctx->lpVtbl->Map(st->dctx,
|
||||
(ID3D11Resource*)st->staging, 0, D3D11_MAP_READ, 0, &m))) {
|
||||
vgpu_present_submit(ctx, (const uint8_t*)m.pData, st->W, st->H, m.RowPitch);
|
||||
st->dctx->lpVtbl->Unmap(st->dctx, (ID3D11Resource*)st->staging, 0);
|
||||
}
|
||||
tex->lpVtbl->Release(tex);
|
||||
}
|
||||
if (res) res->lpVtbl->Release(res);
|
||||
st->dup->lpVtbl->ReleaseFrame(st->dup);
|
||||
}
|
||||
return 0; /* unreachable; satisfies -Wreturn-type */
|
||||
}
|
||||
|
||||
int dda_start(vgpu_ctx* ctx, int fps) {
|
||||
(void)fps;
|
||||
dda_state* st = (dda_state*)calloc(1, sizeof *st);
|
||||
if (!st) return 0;
|
||||
|
||||
D3D_FEATURE_LEVEL fl;
|
||||
if (FAILED(D3D11CreateDevice(NULL, D3D_DRIVER_TYPE_HARDWARE, NULL, 0, NULL, 0,
|
||||
D3D11_SDK_VERSION, &st->dev, &fl, &st->dctx))) {
|
||||
fprintf(stderr, "eyes(dda): D3D11CreateDevice failed\n");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
IDXGIDevice* dxgiDev = NULL;
|
||||
IDXGIAdapter* adapter = NULL;
|
||||
IDXGIOutput* output = NULL;
|
||||
st->dev->lpVtbl->QueryInterface(st->dev, &IID_IDXGIDevice, (void**)&dxgiDev);
|
||||
if (dxgiDev) dxgiDev->lpVtbl->GetAdapter(dxgiDev, &adapter);
|
||||
if (adapter) adapter->lpVtbl->EnumOutputs(adapter, 0, &output);
|
||||
if (output) output->lpVtbl->QueryInterface(output, &IID_IDXGIOutput1, (void**)&st->out1);
|
||||
|
||||
if (output) output->lpVtbl->Release(output);
|
||||
if (adapter) adapter->lpVtbl->Release(adapter);
|
||||
if (dxgiDev) dxgiDev->lpVtbl->Release(dxgiDev);
|
||||
|
||||
if (!st->out1 || FAILED(st->out1->lpVtbl->DuplicateOutput(st->out1,
|
||||
(IUnknown*)st->dev, &st->dup))) {
|
||||
fprintf(stderr, "eyes(dda): DuplicateOutput failed\n");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
DXGI_OUTDUPL_DESC dd;
|
||||
st->dup->lpVtbl->GetDesc(st->dup, &dd);
|
||||
st->W = dd.ModeDesc.Width;
|
||||
st->H = dd.ModeDesc.Height;
|
||||
|
||||
D3D11_TEXTURE2D_DESC td; memset(&td, 0, sizeof td);
|
||||
td.Width = st->W; td.Height = st->H; td.MipLevels = 1; td.ArraySize = 1;
|
||||
td.Format = DXGI_FORMAT_B8G8R8A8_UNORM; td.SampleDesc.Count = 1;
|
||||
td.Usage = D3D11_USAGE_STAGING; td.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
|
||||
if (FAILED(st->dev->lpVtbl->CreateTexture2D(st->dev, &td, NULL, &st->staging))) {
|
||||
fprintf(stderr, "eyes(dda): CreateTexture2D failed\n");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
capture_thread_arg* arg = (capture_thread_arg*)malloc(sizeof *arg);
|
||||
if (!arg) goto fail;
|
||||
arg->ctx = ctx; arg->fps = fps; arg->backend_state = st;
|
||||
|
||||
ctx->backend = VGPU_BK_DDA;
|
||||
ctx->draw_cursor_cap = 1; /* DDA frames are content-only → presenter draws cursor */
|
||||
|
||||
HANDLE t = CreateThread(NULL, 0, dda_thread, arg, 0, NULL);
|
||||
if (!t) { free(arg); goto fail; }
|
||||
CloseHandle(t);
|
||||
|
||||
fprintf(stderr, "eyes(dda): desktop %ux%u (content-only; cursor by presenter)\n",
|
||||
st->W, st->H);
|
||||
return 1;
|
||||
|
||||
fail:
|
||||
/* release any COM objects created before the failure (no ref leaks) */
|
||||
if (st->staging) st->staging->lpVtbl->Release(st->staging);
|
||||
if (st->dup) st->dup->lpVtbl->Release(st->dup);
|
||||
if (st->out1) st->out1->lpVtbl->Release(st->out1);
|
||||
if (st->dctx) st->dctx->lpVtbl->Release(st->dctx);
|
||||
if (st->dev) st->dev->lpVtbl->Release(st->dev);
|
||||
free(st);
|
||||
return 0;
|
||||
}
|
||||
Reference in New Issue
Block a user