mirror of
https://dev.lirent.ru/Vatrog/vm-vgpu-streamer.git
synced 2026-07-09 21:36:37 +03:00
130 lines
4.7 KiB
C
130 lines
4.7 KiB
C
|
|
#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;
|
||
|
|
}
|