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:
2026-06-17 12:55:19 +03:00
commit fefa736fb8
28 changed files with 2008 additions and 0 deletions
+4
View File
@@ -0,0 +1,4 @@
.*/
cmake-*/
compile*
docs/plans/
+35
View File
@@ -0,0 +1,35 @@
cmake_minimum_required(VERSION 3.16)
project(vgpu-streamer C)
set(CMAKE_C_STANDARD 11)
set(CMAKE_C_STANDARD_REQUIRED ON)
set(CMAKE_C_EXTENSIONS OFF) # strict c11; shim uses __asm__/__atomic_* — ok
add_library(vgpustream STATIC
src/stream/publish.c
src/stream/win32/region.c
src/stream/win32/present.c
src/stream/win32/cursor.c
src/stream/win32/capture.c
src/stream/win32/capture_nvfbc.c
src/stream/win32/capture_dda.c
src/stream/win32/capture_gdi.c)
target_include_directories(vgpustream
PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include
PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/src/stream/include
${CMAKE_CURRENT_SOURCE_DIR}/src/stream/win32
${CMAKE_CURRENT_SOURCE_DIR}/third_party) # vendor NvFBC + Windows.h shim
target_compile_definitions(vgpustream PRIVATE CINTERFACE WIN32_LEAN_AND_MEAN=)
target_compile_options(vgpustream PRIVATE
$<$<C_COMPILER_ID:GNU>:-O2;-Wall;-Wextra>
$<$<C_COMPILER_ID:MSVC>:/O2;/W3>)
target_link_libraries(vgpustream PRIVATE d3d11 dxgi dxguid uuid user32 gdi32)
add_executable(vgpu-streamer src/stream/win32/main.c)
set_target_properties(vgpu-streamer PROPERTIES OUTPUT_NAME vgpu-streamer)
target_include_directories(vgpu-streamer
PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/include
${CMAKE_CURRENT_SOURCE_DIR}/src/stream/include
${CMAKE_CURRENT_SOURCE_DIR}/src/stream/win32)
target_link_libraries(vgpu-streamer PRIVATE vgpustream)
target_link_options(vgpu-streamer PRIVATE $<$<C_COMPILER_ID:GNU>:-static;-s>)
+8
View File
@@ -0,0 +1,8 @@
set(CMAKE_SYSTEM_NAME Windows)
set(CMAKE_SYSTEM_PROCESSOR x86_64)
set(CMAKE_C_COMPILER x86_64-w64-mingw32-gcc)
set(CMAKE_RC_COMPILER x86_64-w64-mingw32-windres)
set(CMAKE_FIND_ROOT_PATH /usr/x86_64-w64-mingw32)
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
+112
View File
@@ -0,0 +1,112 @@
#ifndef VGPU_STREAM_H
#define VGPU_STREAM_H
#include <stdint.h>
#include <stddef.h> /* offsetof */
#include <stdalign.h> /* alignas */
#include <assert.h> /* static_assert */
/* ===== Geometry — single source of truth (bare ABI, both ends agree) ===== */
#define VGPU_PAGE 4096u
#define VGPU_SLOT_COUNT 3u
#define VGPU_SLOT_STRIDE (32u * 1024u * 1024u)
#define VGPU_RING_OFFSET (2u * 1024u * 1024u)
#define VGPU_PRODUCER_OFFSET 0u
#define VGPU_CONTROL_OFFSET VGPU_PAGE
#define VGPU_REGION_BYTES (VGPU_RING_OFFSET + (uint64_t)VGPU_SLOT_COUNT * VGPU_SLOT_STRIDE)
#define VGPU_MAX_WIDTH 3840u
#define VGPU_MAX_HEIGHT 2160u
#define VGPU_HEARTBEAT_PERIOD_MS 250u /* producer ticks heartbeat >= 4 Hz always */
#define VGPU_LATEST_NONE 0xFFFFFFFFu
static_assert((uint64_t)VGPU_MAX_WIDTH * VGPU_MAX_HEIGHT * 4u <= VGPU_SLOT_STRIDE,
"max-mode tight BGRA must fit one slot");
/* enum values travel as uint32 wire-values (not as enum fields → no width instability) */
enum { VGPU_FMT_BGRA8888 = 0 };
enum { VGPU_ST_INIT=0, VGPU_ST_CAPTURING=1, VGPU_ST_PAUSED=2, VGPU_ST_STOPPED=3, VGPU_ST_ERROR=4 };
enum { VGPU_BK_NONE=0, VGPU_BK_NVFBC=1, VGPU_BK_DDA=2, VGPU_BK_GDI=3 };
enum { VGPU_CMD_STOP=0, VGPU_CMD_RUN=1, VGPU_CMD_PAUSE=2 };
/* ===== Per-slot descriptor (under hot.seq[slot]) ===== */
typedef struct {
uint32_t width; /* pixels */
uint32_t height; /* pixels */
uint32_t stride; /* bytes/row; INVARIANT: == width*4 (tight) */
uint32_t format; /* VGPU_FMT_* */
uint64_t frame_id; /* == producer.frame_id at publish time */
uint64_t timestamp_ns; /* capture time, monotonic */
} vgpu_desc_t;
static_assert(sizeof(vgpu_desc_t) == 32, "desc layout");
static_assert(offsetof(vgpu_desc_t, width) == 0, "desc.width");
static_assert(offsetof(vgpu_desc_t, height) == 4, "desc.height");
static_assert(offsetof(vgpu_desc_t, stride) == 8, "desc.stride");
static_assert(offsetof(vgpu_desc_t, format) == 12, "desc.format");
static_assert(offsetof(vgpu_desc_t, frame_id) == 16, "desc.frame_id");
static_assert(offsetof(vgpu_desc_t, timestamp_ns) == 24, "desc.timestamp_ns");
/* ===== Producer block (host-RO): hot publish line + cold status line ===== */
typedef struct {
/* --- hot publish line --- */
alignas(64)
uint32_t latest; /* index of last; VGPU_LATEST_NONE until 1st frame */
uint32_t _r0;
uint64_t frame_id; /* monotonic frame counter (8-aligned) */
uint32_t seq[VGPU_SLOT_COUNT]; /* per-slot seqlock: even=stable, odd=writing */
uint32_t _r1;
vgpu_desc_t desc[VGPU_SLOT_COUNT]; /* self-describing slots */
/* --- cold status line --- */
alignas(64)
uint64_t heartbeat; /* monotonic; ticks always (even STOPPED/PAUSED) */
uint32_t run_epoch; /* +1 per start (session break for host) */
uint32_t status; /* VGPU_ST_* */
uint32_t backend; /* VGPU_BK_* */
uint32_t error_code; /* 0=none; else fatal detail */
uint32_t applied_fps; /* publish-rate cap the producer actually applies;
actual rate may be lower on static content or
backend limits — host measures real fps from
desc.timestamp_ns */
uint32_t supported_formats; /* bitmask (1u<<VGPU_FMT_*) */
uint32_t ctrl_ack; /* echo of control.ctrl_gen (even) applied */
uint32_t full_frame_ack; /* echo of control.full_frame_req honored */
} vgpu_producer_t;
static_assert(alignof(vgpu_producer_t) == 64, "producer align");
static_assert(sizeof(vgpu_producer_t) <= VGPU_PAGE, "producer fits page 0");
/* host-read field layout frozen as ABI */
static_assert(offsetof(vgpu_producer_t, latest) == 0, "producer.latest");
static_assert(offsetof(vgpu_producer_t, frame_id) == 8, "producer.frame_id");
static_assert(offsetof(vgpu_producer_t, seq) == 16, "producer.seq");
static_assert(offsetof(vgpu_producer_t, desc) == 32, "producer.desc");
static_assert(offsetof(vgpu_producer_t, heartbeat) == 128, "producer.heartbeat");
static_assert(offsetof(vgpu_producer_t, run_epoch) == 136, "producer.run_epoch");
static_assert(offsetof(vgpu_producer_t, status) == 140, "producer.status");
static_assert(offsetof(vgpu_producer_t, backend) == 144, "producer.backend");
static_assert(offsetof(vgpu_producer_t, error_code) == 148, "producer.error_code");
static_assert(offsetof(vgpu_producer_t, applied_fps) == 152, "producer.applied_fps");
static_assert(offsetof(vgpu_producer_t, supported_formats) == 156, "producer.supported_formats");
static_assert(offsetof(vgpu_producer_t, ctrl_ack) == 160, "producer.ctrl_ack");
static_assert(offsetof(vgpu_producer_t, full_frame_ack) == 164, "producer.full_frame_ack");
/* ===== Control block (host-RW), own page, generation-guarded ===== */
typedef struct {
alignas(64)
uint32_t ctrl_gen; /* generation seqlock: even=stable, odd=writing (host writes) */
uint32_t desired_state; /* VGPU_CMD_* (STOP/RUN/PAUSE) */
uint32_t target_fps; /* desired fps; 0=producer default */
uint32_t draw_cursor; /* 1=compose cursor */
uint32_t full_frame_req; /* edge counter: bump → force fresh full frame */
uint32_t consumer_tick; /* host heartbeat (producer watches with timeout) */
uint32_t attached; /* 1=host attached (intent, not death-proof) */
} vgpu_control_t;
static_assert(alignof(vgpu_control_t) == 64, "control align");
static_assert(sizeof(vgpu_control_t) <= VGPU_PAGE, "control fits page 1");
/* host-write field layout frozen as ABI */
static_assert(offsetof(vgpu_control_t, ctrl_gen) == 0, "control.ctrl_gen");
static_assert(offsetof(vgpu_control_t, desired_state) == 4, "control.desired_state");
static_assert(offsetof(vgpu_control_t, target_fps) == 8, "control.target_fps");
static_assert(offsetof(vgpu_control_t, draw_cursor) == 12, "control.draw_cursor");
static_assert(offsetof(vgpu_control_t, full_frame_req) == 16, "control.full_frame_req");
static_assert(offsetof(vgpu_control_t, consumer_tick) == 20, "control.consumer_tick");
static_assert(offsetof(vgpu_control_t, attached) == 24, "control.attached");
#endif
+52
View File
@@ -0,0 +1,52 @@
#ifndef VGPU_ATOMIC_SHIM_H
#define VGPU_ATOMIC_SHIM_H
/* atomic-shim.h — x86-TSO memory-order accessors (arch, not OS).
*
* x86-TSO memory-order shim. NO _Atomic in the shared region type: the consumer
* maps the region as raw bytes. Synchronization lives entirely in the producer's
* accessors here. Per-compiler implementation, never exposed in the contract.
*
* On x86_64 every naturally-aligned MOV up to 8 bytes is atomic and stores are
* already release / loads already acquire at the hardware level; the only things
* we must prevent are (1) compiler reordering across the sync point and
* (2) store-buffer visibility delay between the data writes and the publish
* store, for which an explicit SFENCE is used at publish boundaries.
*/
#include <stdint.h>
#if defined(_MSC_VER)
#include <intrin.h>
static inline void vgpu_compiler_barrier(void) { _ReadWriteBarrier(); }
static inline void vgpu_sfence(void) { _mm_sfence(); }
static inline void vgpu_store_release32(volatile uint32_t* p, uint32_t v) {
_ReadWriteBarrier();
*p = v;
}
static inline uint32_t vgpu_load_acquire32(const volatile uint32_t* p) {
uint32_t v = *p;
_ReadWriteBarrier();
return v;
}
#else /* gcc / mingw / clang */
static inline void vgpu_compiler_barrier(void) { __asm__ __volatile__("" ::: "memory"); }
static inline void vgpu_sfence(void) { __asm__ __volatile__("sfence" ::: "memory"); }
static inline void vgpu_store_release32(volatile uint32_t* p, uint32_t v) {
__atomic_store_n(p, v, __ATOMIC_RELEASE);
}
static inline uint32_t vgpu_load_acquire32(const volatile uint32_t* p) {
return __atomic_load_n(p, __ATOMIC_ACQUIRE);
}
#endif
#endif /* VGPU_ATOMIC_SHIM_H */
+28
View File
@@ -0,0 +1,28 @@
#ifndef VGPU_CAPTURE_H
#define VGPU_CAPTURE_H
/* capture.h — extension seam for capture backends.
* A backend produces desktop frames and submits them to the presenter. This
* header is OS-agnostic: it names backends through an opaque vgpu_ctx* and a
* uniform start contract. A platform layer defines vgpu_ctx and any private
* backend plumbing (see src/stream/win32/capture-win32.h). A future Linux layer
* implements the same seam against its own vgpu_ctx + region/sync/clock. */
/* Opaque runtime context, defined by the platform layer (win32: ctx.h). */
typedef struct vgpu_ctx vgpu_ctx;
/* Start a capture backend. Returns 1 on success; on success the backend has
* spawned its capture thread(s) (which received ctx) and set ctx->backend /
* ctx->draw_cursor_cap. The submit contract: each captured desktop frame is
* handed to the presenter via vgpu_present_submit(). */
typedef int (*capture_start_fn)(vgpu_ctx* ctx, int fps);
typedef struct {
const char* name;
capture_start_fn start;
} capture_backend;
/* Data-driven backend table; the entry point selects by env or availability. */
const capture_backend* capture_backends(int* count);
#endif /* VGPU_CAPTURE_H */
+56
View File
@@ -0,0 +1,56 @@
#ifndef VGPU_STREAM_ENGINE_H
#define VGPU_STREAM_ENGINE_H
/* stream.h — OS-agnostic streaming protocol over the shared contract.
* Declares the neutral region-view handle (resolved contract pointers) and the
* seqlock publish / control-reconcile API. No platform headers: the engine
* operates purely on the contract; a platform layer (e.g. src/stream/win32/)
* builds the region and hands its pointers in as a vgpu_region_view. */
#include <stdint.h>
#include "vgpu_stream.h" /* contract: producer/control types, slot geometry */
/* Neutral view of the live contract: the three resolved blocks the engine
* publishes into / reconciles against. The platform region owns the backing
* memory; this is a borrowed view (no ownership). */
typedef struct {
vgpu_producer_t* producer;
vgpu_control_t* control;
uint8_t* ring;
} vgpu_region_view;
/* Resolved view of the control block after a clean generation read. */
typedef struct {
uint32_t gen; /* even generation that was read (for ctrl_ack) */
uint32_t desired_state; /* VGPU_CMD_* */
uint32_t target_fps;
uint32_t draw_cursor;
uint32_t full_frame_req;
uint32_t consumer_tick;
uint32_t attached;
} vgpu_control_view;
/* Seqlock-publish a tight BGRA frame into the next ring slot.
* Clamps by SLOT_STRIDE (rejects frames that do not fit). Writes desc[],
* bumps frame_id, release-stores latest. Returns 0 on publish, 1 if dropped
* (frame too large for a slot). */
int vgpu_publish_frame(const vgpu_region_view* rv, const uint8_t* tight_bgra,
uint32_t width, uint32_t height, uint64_t timestamp_ns);
/* Read control block under its generation seqlock (bounded retry). Returns 1
* on a clean read (view filled), 0 if the writer kept it busy past the limit. */
int vgpu_control_read(const vgpu_region_view* rv, vgpu_control_view* out);
/* Echo the applied generation back to the host. */
void vgpu_publish_ctrl_ack(const vgpu_region_view* rv, uint32_t gen);
/* Status / lifecycle helpers (cold line). */
void vgpu_set_status(const vgpu_region_view* rv, uint32_t status);
void vgpu_set_backend(const vgpu_region_view* rv, uint32_t backend);
void vgpu_set_error(const vgpu_region_view* rv, uint32_t error_code);
void vgpu_set_applied_fps(const vgpu_region_view* rv, uint32_t fps);
void vgpu_bump_run_epoch(const vgpu_region_view* rv);
void vgpu_tick_heartbeat(const vgpu_region_view* rv);
void vgpu_publish_full_frame_ack(const vgpu_region_view* rv, uint32_t req);
#endif /* VGPU_STREAM_ENGINE_H */
+118
View File
@@ -0,0 +1,118 @@
/* publish.c — OS-agnostic implementation of the streaming protocol.
* Operates purely on the contract through a borrowed vgpu_region_view; no
* platform headers, no runtime context. The x86-TSO ordering lives in the
* atomic shim. */
#include <string.h>
#include "vgpu_stream.h" /* contract types / slot geometry */
#include "atomic-shim.h" /* x86-TSO memory-order accessors */
#include "stream.h" /* region-view handle + this API */
#define VGPU_CTRL_READ_TRIES 16u
int vgpu_publish_frame(const vgpu_region_view* rv, const uint8_t* tight_bgra,
uint32_t width, uint32_t height, uint64_t timestamp_ns) {
vgpu_producer_t* p = rv->producer;
const uint32_t stride = width * 4u; /* tight invariant */
const uint64_t need = (uint64_t)height * stride;
if (need > VGPU_SLOT_STRIDE) /* clamp by slot size */
return 1;
uint32_t cur = vgpu_load_acquire32(&p->latest);
uint32_t S = (cur == VGPU_LATEST_NONE) ? 0u : ((cur + 1u) % VGPU_SLOT_COUNT);
uint8_t* dst = rv->ring + (size_t)S * VGPU_SLOT_STRIDE;
/* seqlock: even -> odd (writing) */
vgpu_store_release32(&p->seq[S], p->seq[S] + 1u);
vgpu_compiler_barrier();
/* descriptor (self-describing slot) */
p->desc[S].width = width;
p->desc[S].height = height;
p->desc[S].stride = stride;
p->desc[S].format = VGPU_FMT_BGRA8888;
p->desc[S].frame_id = p->frame_id + 1u;
p->desc[S].timestamp_ns = timestamp_ns;
/* pixels (source is already tight) */
memcpy(dst, tight_bgra, (size_t)need);
vgpu_sfence();
/* seqlock: odd -> even (stable) */
vgpu_store_release32(&p->seq[S], p->seq[S] + 1u);
vgpu_sfence();
p->frame_id += 1u;
vgpu_store_release32(&p->latest, S);
return 0;
}
int vgpu_control_read(const vgpu_region_view* rv, vgpu_control_view* out) {
volatile vgpu_control_t* c = rv->control;
for (uint32_t t = 0; t < VGPU_CTRL_READ_TRIES; t++) {
uint32_t g0 = vgpu_load_acquire32(&c->ctrl_gen);
if (g0 & 1u)
continue; /* writer in progress */
vgpu_compiler_barrier();
uint32_t desired = c->desired_state;
uint32_t fps = c->target_fps;
uint32_t cursor = c->draw_cursor;
uint32_t ffreq = c->full_frame_req;
uint32_t ctick = c->consumer_tick;
uint32_t att = c->attached;
vgpu_compiler_barrier();
uint32_t g1 = vgpu_load_acquire32(&c->ctrl_gen);
if (g0 != g1)
continue; /* torn read, retry */
out->gen = g0;
out->desired_state = desired;
out->target_fps = fps;
out->draw_cursor = cursor;
out->full_frame_req = ffreq;
out->consumer_tick = ctick;
out->attached = att;
return 1;
}
return 0;
}
void vgpu_publish_ctrl_ack(const vgpu_region_view* rv, uint32_t gen) {
vgpu_store_release32(&rv->producer->ctrl_ack, gen);
}
void vgpu_set_status(const vgpu_region_view* rv, uint32_t status) {
vgpu_store_release32(&rv->producer->status, status);
}
void vgpu_set_backend(const vgpu_region_view* rv, uint32_t backend) {
vgpu_store_release32(&rv->producer->backend, backend);
}
void vgpu_set_error(const vgpu_region_view* rv, uint32_t error_code) {
vgpu_store_release32(&rv->producer->error_code, error_code);
}
void vgpu_set_applied_fps(const vgpu_region_view* rv, uint32_t fps) {
vgpu_store_release32(&rv->producer->applied_fps, fps);
}
void vgpu_bump_run_epoch(const vgpu_region_view* rv) {
vgpu_producer_t* p = rv->producer;
vgpu_store_release32(&p->run_epoch, p->run_epoch + 1u);
}
void vgpu_tick_heartbeat(const vgpu_region_view* rv) {
/* 64-bit aligned single MOV is atomic on x86_64; barrier orders it */
rv->producer->heartbeat += 1u;
vgpu_compiler_barrier();
}
void vgpu_publish_full_frame_ack(const vgpu_region_view* rv, uint32_t req) {
vgpu_store_release32(&rv->producer->full_frame_ack, req);
}
+19
View File
@@ -0,0 +1,19 @@
#ifndef VGPU_CAPTURE_WIN32_H
#define VGPU_CAPTURE_WIN32_H
/* capture-win32.h — private win32 plumbing shared by the capture backends.
* Not part of the OS-agnostic capture seam (see src/stream/include/capture.h):
* it depends on the win32 vgpu_ctx and the thread-handoff convention. */
#include "ctx.h" /* win32 vgpu_ctx (full definition) */
/* Thread argument passed to capture threads via LPVOID. Heap-allocated by the
* backend's *_start, owned and freed by the thread. Carries the explicit ctx
* (no global state) plus per-backend state pointer. */
typedef struct {
vgpu_ctx* ctx;
int fps;
void* backend_state; /* opaque per-backend handle block */
} capture_thread_arg;
#endif /* VGPU_CAPTURE_WIN32_H */
+19
View File
@@ -0,0 +1,19 @@
/* capture.c — win32 registration of the capture backends into the neutral
* capture seam's backend table (data-driven; no per-backend branching). */
#include "capture.h" /* neutral seam: capture_backend / capture_backends */
#include "capture_nvfbc.h"
#include "capture_dda.h"
#include "capture_gdi.h"
/* data-driven backend table; main selects by EYES env or first available */
static const capture_backend g_backends[] = {
{ "nvfbc", nvfbc_start },
{ "dda", dda_start },
{ "gdi", gdi_start },
};
const capture_backend* capture_backends(int* count) {
*count = (int)(sizeof g_backends / sizeof g_backends[0]);
return g_backends;
}
+129
View File
@@ -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;
}
+10
View File
@@ -0,0 +1,10 @@
#ifndef VGPU_CAPTURE_DDA_H
#define VGPU_CAPTURE_DDA_H
/* capture_dda.h — DXGI Desktop Duplication capture backend (win32). */
#include "ctx.h" /* win32 vgpu_ctx */
int dda_start(vgpu_ctx* ctx, int fps);
#endif /* VGPU_CAPTURE_DDA_H */
+61
View File
@@ -0,0 +1,61 @@
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "capture_gdi.h"
#include "capture-win32.h" /* capture_thread_arg (win32-private) */
#include "present.h"
static DWORD WINAPI gdi_thread(LPVOID param) {
capture_thread_arg* arg = (capture_thread_arg*)param;
vgpu_ctx* ctx = arg->ctx;
int fps = arg->fps > 0 ? arg->fps : 30;
free(arg);
HDC screen = GetDC(NULL);
HDC mem = CreateCompatibleDC(screen);
HBITMAP dib = NULL;
void* bits = NULL;
int W = 0, H = 0;
const DWORD interval = (DWORD)(1000 / fps);
for (;;) {
int w = GetSystemMetrics(SM_CXSCREEN), h = GetSystemMetrics(SM_CYSCREEN);
if (w <= 0 || h <= 0) { Sleep(200); continue; }
if (w != W || h != H || !dib) {
if (dib) DeleteObject(dib);
BITMAPINFO bi; memset(&bi, 0, sizeof bi);
bi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
bi.bmiHeader.biWidth = w; bi.bmiHeader.biHeight = -h;
bi.bmiHeader.biPlanes = 1; bi.bmiHeader.biBitCount = 32;
bi.bmiHeader.biCompression = BI_RGB;
dib = CreateDIBSection(screen, &bi, DIB_RGB_COLORS, &bits, NULL, 0);
if (!dib) {
fprintf(stderr, "eyes(gdi): CreateDIBSection %dx%d failed\n", w, h);
Sleep(200); continue;
}
SelectObject(mem, dib);
W = w; H = h;
fprintf(stderr, "eyes(gdi): desktop %dx%d (BitBlt; cursor by presenter)\n", W, H);
}
if (BitBlt(mem, 0, 0, W, H, screen, 0, 0, SRCCOPY))
vgpu_present_submit(ctx, (const uint8_t*)bits,
(uint32_t)W, (uint32_t)H, (uint32_t)W * 4u);
Sleep(interval);
}
return 0; /* unreachable; satisfies -Wreturn-type */
}
int gdi_start(vgpu_ctx* ctx, int fps) {
ctx->backend = VGPU_BK_GDI;
ctx->draw_cursor_cap = 1; /* GDI BitBlt excludes cursor → presenter draws it */
capture_thread_arg* arg = (capture_thread_arg*)malloc(sizeof *arg);
if (!arg) return 0;
arg->ctx = ctx; arg->fps = fps; arg->backend_state = NULL;
HANDLE t = CreateThread(NULL, 0, gdi_thread, arg, 0, NULL);
if (!t) { free(arg); return 0; }
CloseHandle(t);
return 1;
}
+10
View File
@@ -0,0 +1,10 @@
#ifndef VGPU_CAPTURE_GDI_H
#define VGPU_CAPTURE_GDI_H
/* capture_gdi.h — GDI BitBlt capture backend (win32, universal fallback). */
#include "ctx.h" /* win32 vgpu_ctx */
int gdi_start(vgpu_ctx* ctx, int fps);
#endif /* VGPU_CAPTURE_GDI_H */
+128
View File
@@ -0,0 +1,128 @@
#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 "nvfbc_tosys_c.h"
typedef struct {
NvFBCToSys_c* fbc;
void* buf;
NvFBC_CreateFunctionExType create;
} nvfbc_state;
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;
} else {
Sleep(50);
}
continue;
}
if (gi.dwWidth && gi.dwHeight)
vgpu_present_submit(ctx, (const uint8_t*)buf,
gi.dwWidth, gi.dwHeight, gi.dwBufferWidth * 4u);
}
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;
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;
}
+10
View File
@@ -0,0 +1,10 @@
#ifndef VGPU_CAPTURE_NVFBC_H
#define VGPU_CAPTURE_NVFBC_H
/* capture_nvfbc.h — NVIDIA NvFBC ToSys capture backend (win32). */
#include "ctx.h" /* win32 vgpu_ctx */
int nvfbc_start(vgpu_ctx* ctx, int fps);
#endif /* VGPU_CAPTURE_NVFBC_H */
+65
View File
@@ -0,0 +1,65 @@
#ifndef VGPU_CTX_H
#define VGPU_CTX_H
/* ctx.h — win32 runtime context. Embeds the neutral region-view (the engine's
* borrowed handle onto the contract) alongside win32-owned staging/cursor/sync
* state. Object = memory: ctx owns the staging arena and cursor state. */
#include <stdint.h>
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include "stream.h" /* vgpu_region_view (neutral contract handle) */
#include "region.h" /* vgpu_region_t (win32 pinned region) */
/*
* vgpu_ctx — the explicitly-passed context. Replaces all former g_* shared
* state. Object = memory: ctx owns the producer staging arena and cursor
* state; capture threads receive a vgpu_ctx* via their LPVOID thread param.
*
* Staging is a fixed arena sized for the max mode (no STL, no per-frame
* malloc). content_buf holds the latest submitted desktop; frame_buf is the
* composed (cursor-drawn) frame the publisher copies into a ring slot.
*/
#define VGPU_STAGING_BYTES ((size_t)VGPU_MAX_WIDTH * VGPU_MAX_HEIGHT * 4u)
/* Cursor sample/compose state (GDI). Fixed buffers, no heap. */
typedef struct {
HCURSOR handle;
int visible;
int x, y;
int hot_x, hot_y;
int gw, gh; /* glyph dims */
int mono; /* 1 = AND/XOR monochrome cursor */
uint8_t* bgra; /* color cursor BGRA (arena) */
uint8_t* and_mask; /* mono AND (arena) */
uint8_t* xor_mask; /* mono XOR (arena) */
} vgpu_cursor_t;
typedef struct vgpu_ctx {
/* neutral contract handle (borrowed from region) — engine publishes through
* this; win32 code reads region blocks via view.producer / view.control */
vgpu_region_view view;
/* producer staging arena (owned) */
uint8_t* arena; /* one VirtualAlloc block for all buffers */
size_t arena_bytes;
uint8_t* content_buf; /* latest submitted desktop, tight BGRA */
uint8_t* frame_buf; /* composed frame to publish, tight BGRA */
/* submit handoff (capture thread -> publish pump) */
CRITICAL_SECTION lock;
HANDLE submit_event;
int64_t content_seq; /* bumped on every submit */
uint32_t content_w, content_h;
/* cursor */
vgpu_cursor_t cursor;
/* runtime config (resolved from control) */
uint32_t default_fps; /* fps from CLI; used when target_fps==0 */
uint32_t backend; /* VGPU_BK_* chosen */
int draw_cursor_cap; /* backend capability: does it need SW cursor */
} vgpu_ctx;
#endif /* VGPU_CTX_H */
+137
View File
@@ -0,0 +1,137 @@
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <string.h>
#include "cursor.h"
/* Max supported cursor glyph; buffers are pre-arena'd in ctx (no heap here). */
#define VGPU_CURSOR_MAX 256
static void read_mono(HBITMAP hbm, int w, int h, uint8_t* out /* w*h */) {
int stride = ((w + 31) / 32) * 4;
/* bounded scratch on stack: max (256/32*4)=32 bytes/row * 512 rows */
static const int kMaxRows = VGPU_CURSOR_MAX * 2;
uint8_t raw[(VGPU_CURSOR_MAX / 32 * 4) * (VGPU_CURSOR_MAX * 2)];
if (h > kMaxRows) h = kMaxRows;
if ((size_t)stride * h > sizeof raw) return;
struct { BITMAPINFOHEADER hdr; RGBQUAD pal[2]; } bi;
memset(&bi, 0, sizeof bi);
bi.hdr.biSize = sizeof(BITMAPINFOHEADER);
bi.hdr.biWidth = w; bi.hdr.biHeight = -h;
bi.hdr.biPlanes = 1; bi.hdr.biBitCount = 1; bi.hdr.biCompression = BI_RGB;
HDC dc = GetDC(NULL);
GetDIBits(dc, hbm, 0, h, raw, (BITMAPINFO*)&bi, DIB_RGB_COLORS);
ReleaseDC(NULL, dc);
memset(out, 0, (size_t)w * h);
for (int y = 0; y < h; y++)
for (int x = 0; x < w; x++) {
int bit = 7 - (x & 7);
out[(size_t)y * w + x] = (raw[(size_t)y * stride + (x >> 3)] >> bit) & 1u;
}
}
static void extract(vgpu_ctx* ctx, HCURSOR hc) {
vgpu_cursor_t* cur = &ctx->cursor;
cur->gw = cur->gh = 0;
cur->mono = 0;
ICONINFO ii;
if (!GetIconInfo(hc, &ii)) return;
cur->hot_x = (int)ii.xHotspot;
cur->hot_y = (int)ii.yHotspot;
if (ii.hbmColor) {
BITMAP bm; GetObject(ii.hbmColor, sizeof bm, &bm);
int w = bm.bmWidth, h = bm.bmHeight;
if (w > VGPU_CURSOR_MAX) w = VGPU_CURSOR_MAX;
if (h > VGPU_CURSOR_MAX) h = VGPU_CURSOR_MAX;
BITMAPINFO bi; memset(&bi, 0, sizeof bi);
bi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
bi.bmiHeader.biWidth = w; bi.bmiHeader.biHeight = -h;
bi.bmiHeader.biPlanes = 1; bi.bmiHeader.biBitCount = 32;
bi.bmiHeader.biCompression = BI_RGB;
memset(cur->bgra, 0, (size_t)w * h * 4);
HDC dc = GetDC(NULL);
GetDIBits(dc, ii.hbmColor, 0, h, cur->bgra, &bi, DIB_RGB_COLORS);
ReleaseDC(NULL, dc);
cur->gw = w; cur->gh = h; cur->mono = 0;
int has_alpha = 0;
for (size_t i = 0; i < (size_t)w * h; i++)
if (cur->bgra[i * 4 + 3]) { has_alpha = 1; break; }
if (!has_alpha && ii.hbmMask) {
read_mono(ii.hbmMask, w, h, cur->and_mask);
for (size_t i = 0; i < (size_t)w * h; i++)
cur->bgra[i * 4 + 3] = cur->and_mask[i] ? 0 : 255;
}
} else if (ii.hbmMask) {
BITMAP bm; GetObject(ii.hbmMask, sizeof bm, &bm);
int w = bm.bmWidth, h = bm.bmHeight / 2;
if (w > VGPU_CURSOR_MAX) w = VGPU_CURSOR_MAX;
if (h > VGPU_CURSOR_MAX) h = VGPU_CURSOR_MAX;
/* read both halves into a scratch laid over xor_mask region: reuse
* and_mask for AND and xor_mask for XOR; read full into a stack pass */
static uint8_t both[VGPU_CURSOR_MAX * VGPU_CURSOR_MAX * 2];
read_mono(ii.hbmMask, w, bm.bmHeight, both);
for (int y = 0; y < h; y++)
for (int x = 0; x < w; x++) {
cur->and_mask[(size_t)y * w + x] = both[(size_t)y * w + x];
cur->xor_mask[(size_t)y * w + x] = both[(size_t)(y + h) * w + x];
}
cur->gw = w; cur->gh = h; cur->mono = 1;
}
if (ii.hbmColor) DeleteObject(ii.hbmColor);
if (ii.hbmMask) DeleteObject(ii.hbmMask);
}
int cursor_sample(vgpu_ctx* ctx) {
vgpu_cursor_t* cur = &ctx->cursor;
CURSORINFO ci; ci.cbSize = sizeof ci;
if (!GetCursorInfo(&ci)) {
int changed = cur->visible;
cur->visible = 0;
return changed;
}
int vis = (ci.flags & CURSOR_SHOWING) != 0;
int x = ci.ptScreenPos.x, y = ci.ptScreenPos.y;
int changed = (vis != cur->visible) || (x != cur->x) || (y != cur->y)
|| (ci.hCursor != cur->handle);
if (vis && ci.hCursor && ci.hCursor != cur->handle) {
extract(ctx, ci.hCursor);
cur->handle = ci.hCursor;
}
cur->visible = vis; cur->x = x; cur->y = y;
return changed;
}
void cursor_draw(vgpu_ctx* ctx, uint8_t* dst, uint32_t W, uint32_t H) {
vgpu_cursor_t* cur = &ctx->cursor;
if (!cur->visible || cur->gw == 0) return;
int ox = cur->x - cur->hot_x, oy = cur->y - cur->hot_y;
for (int gy = 0; gy < cur->gh; gy++) {
int dy = oy + gy;
if (dy < 0 || dy >= (int)H) continue;
for (int gx = 0; gx < cur->gw; gx++) {
int dx = ox + gx;
if (dx < 0 || dx >= (int)W) continue;
uint8_t* d = dst + ((size_t)dy * W + dx) * 4;
if (!cur->mono) {
const uint8_t* s = &cur->bgra[((size_t)gy * cur->gw + gx) * 4];
uint32_t a = s[3];
if (!a) continue;
d[0] = (uint8_t)((s[0] * a + d[0] * (255 - a)) / 255);
d[1] = (uint8_t)((s[1] * a + d[1] * (255 - a)) / 255);
d[2] = (uint8_t)((s[2] * a + d[2] * (255 - a)) / 255);
} else {
int a = cur->and_mask[(size_t)gy * cur->gw + gx];
int xr = cur->xor_mask[(size_t)gy * cur->gw + gx];
if (a == 0 && xr == 0) { d[0] = d[1] = d[2] = 0; }
else if (a == 0 && xr == 1) { d[0] = d[1] = d[2] = 255; }
else if (a == 1 && xr == 1) { d[0] = (uint8_t)(255 - d[0]);
d[1] = (uint8_t)(255 - d[1]);
d[2] = (uint8_t)(255 - d[2]); }
}
}
}
}
+16
View File
@@ -0,0 +1,16 @@
#ifndef VGPU_CURSOR_H
#define VGPU_CURSOR_H
/* cursor.h — win32 GDI cursor sample/compose onto a tight BGRA frame. */
#include <stdint.h>
#include "ctx.h" /* win32 vgpu_ctx (cursor state) */
/* Sample the current cursor (position/shape) into ctx->cursor.
* Returns 1 if anything changed since last sample, else 0. */
int cursor_sample(vgpu_ctx* ctx);
/* Alpha/AND-XOR compose the sampled cursor onto a tight BGRA frame. */
void cursor_draw(vgpu_ctx* ctx, uint8_t* bgra, uint32_t width, uint32_t height);
#endif /* VGPU_CURSOR_H */
+55
View File
@@ -0,0 +1,55 @@
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "region.h" /* win32 pinned region */
#include "ctx.h" /* win32 vgpu_ctx (embeds region-view) */
#include "present.h" /* present/pump lifecycle */
#include "stream.h" /* OS-agnostic status/error/backend setters */
#include "capture.h" /* backend table */
int main(int argc, char** argv) {
int fps = argc > 1 ? atoi(argv[1]) : 30;
if (fps <= 0) fps = 30;
vgpu_region_t region;
if (vgpu_region_create(&region) != 0) {
fprintf(stderr, "main: region_create failed\n");
return 1;
}
vgpu_ctx ctx;
if (vgpu_present_init(&ctx, &region, (uint32_t)fps) != 0) {
fprintf(stderr, "main: present_init failed\n");
vgpu_region_destroy(&region);
return 1;
}
const char* eyes = getenv("EYES");
int n = 0;
const capture_backend* bks = capture_backends(&n);
int started = 0;
for (int i = 0; i < n && !started; i++) {
if (eyes && _stricmp(eyes, bks[i].name) != 0) continue;
fprintf(stderr, "eyes: trying %s\n", bks[i].name);
started = bks[i].start(&ctx, fps);
if (!started) fprintf(stderr, "eyes: %s unavailable\n", bks[i].name);
}
if (!started) {
fprintf(stderr, "eyes: no capture backend available\n");
vgpu_set_status(&ctx.view, VGPU_ST_ERROR);
vgpu_set_error(&ctx.view, 2u);
vgpu_present_deinit(&ctx);
vgpu_region_destroy(&region);
return 1;
}
vgpu_set_backend(&ctx.view, ctx.backend);
vgpu_present_run(&ctx); /* never returns */
vgpu_present_deinit(&ctx);
vgpu_region_destroy(&region);
return 0;
}
+93
View File
@@ -0,0 +1,93 @@
#ifndef VGPU_NVFBC_TOSYS_C_H
#define VGPU_NVFBC_TOSYS_C_H
/*
* C mirror of NvFBC's ToSys interface. The vendor header
* third_party/NvFBC/nvFBCToSys.h declares INvFBCToSys_v3 as a C++ abstract
* class (vtable of 5 pure-virtual
* __stdcall methods). We do NOT edit the vendor header; instead we replicate its
* single-inheritance vtable ABI as a COM-in-C interface so the producer stays
* pure C. Slot order MUST match declaration order in nvFBCToSys.h:
* 0 NvFBCToSysSetUp
* 1 NvFBCToSysGrabFrame
* 2 NvFBCToSysCursorCapture
* 3 NvFBCToSysGPUBasedCPUSleep
* 4 NvFBCToSysRelease
* On x64 (mingw/MSVC) `this` is the implicit first integer argument; __stdcall
* is a no-op for x64 so a plain pointer arg matches the vtable slot.
*/
#include "NvFBC/nvFBC.h" /* vendor (third_party/): NVFBCRESULT, NvU32, param structs */
/* SetUp / GrabFrame param structs come from nvFBCToSys.h, but that header is C++.
* Redeclare the two we use here (layout-identical, C-clean). */
typedef enum {
NVFBC_TOSYS_ARGB = 0,
NVFBC_TOSYS_RGB,
NVFBC_TOSYS_YYYYUV420p,
NVFBC_TOSYS_RGB_PLANAR,
NVFBC_TOSYS_XOR,
NVFBC_TOSYS_YUV444p,
NVFBC_TOSYS_BUF_FMT_LAST
} NVFBCToSysBufferFormat_c;
typedef enum {
NVFBC_TOSYS_SOURCEMODE_FULL = 0,
NVFBC_TOSYS_SOURCEMODE_SCALE,
NVFBC_TOSYS_SOURCEMODE_CROP,
NVFBC_TOSYS_SOURCEMODE_LAST
} NVFBCToSysGrabMode_c;
enum {
NVFBC_TOSYS_NOFLAGS_C = 0x0,
NVFBC_TOSYS_NOWAIT_C = 0x1,
NVFBC_TOSYS_WAIT_WITH_TIMEOUT_C = 0x10
};
#define NVFBC_TO_SYS_C (0x1204)
typedef struct {
NvU32 dwVersion;
NvU32 bits; /* bWithHWCursor:1, bDiffMap:1, bSep:1, rsvd:29 */
NVFBCToSysBufferFormat_c eMode;
NvU32 dwReserved1;
void **ppBuffer;
void **ppDiffMap;
void *hCursorCaptureEvent;
NvU32 dwReserved[58];
void *pReserved[29];
} NVFBC_TOSYS_SETUP_PARAMS_C;
#define NVFBC_TOSYS_SETUP_PARAMS_VER_C \
NVFBC_STRUCT_VERSION(NVFBC_TOSYS_SETUP_PARAMS_C, 2)
typedef struct {
NvU32 dwVersion;
NvU32 dwFlags;
NvU32 dwTargetWidth;
NvU32 dwTargetHeight;
NvU32 dwStartX;
NvU32 dwStartY;
NVFBCToSysGrabMode_c eGMode;
NvU32 dwWaitTime;
NvFBCFrameGrabInfo *pNvFBCFrameGrabInfo;
NvU32 dwReserved[56];
void *pReserved[31];
} NVFBC_TOSYS_GRAB_FRAME_PARAMS_C;
#define NVFBC_TOSYS_GRAB_FRAME_PARAMS_VER_C \
NVFBC_STRUCT_VERSION(NVFBC_TOSYS_GRAB_FRAME_PARAMS_C, 1)
/* COM-in-C interface mirror */
typedef struct NvFBCToSys_c NvFBCToSys_c;
typedef struct {
NVFBCRESULT (__stdcall *NvFBCToSysSetUp)(NvFBCToSys_c*, NVFBC_TOSYS_SETUP_PARAMS_C*);
NVFBCRESULT (__stdcall *NvFBCToSysGrabFrame)(NvFBCToSys_c*, NVFBC_TOSYS_GRAB_FRAME_PARAMS_C*);
NVFBCRESULT (__stdcall *NvFBCToSysCursorCapture)(NvFBCToSys_c*, void*);
NVFBCRESULT (__stdcall *NvFBCToSysGPUBasedCPUSleep)(NvFBCToSys_c*, __int64);
NVFBCRESULT (__stdcall *NvFBCToSysRelease)(NvFBCToSys_c*);
} NvFBCToSys_c_vtbl;
struct NvFBCToSys_c {
const NvFBCToSys_c_vtbl* lpVtbl;
};
#endif /* VGPU_NVFBC_TOSYS_C_H */
+192
View File
@@ -0,0 +1,192 @@
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <string.h>
#include <stdio.h>
#include "present.h"
#include "stream.h" /* OS-agnostic publish / control API + region-view */
#include "cursor.h"
/* cursor arena sizing */
#define VGPU_CUR_MAX 256u
#define VGPU_CUR_BGRA (VGPU_CUR_MAX * VGPU_CUR_MAX * 4u)
#define VGPU_CUR_MASK (VGPU_CUR_MAX * VGPU_CUR_MAX)
static uint64_t now_ns(void) {
static LARGE_INTEGER freq = { .QuadPart = 0 };
if (freq.QuadPart == 0) QueryPerformanceFrequency(&freq);
LARGE_INTEGER c; QueryPerformanceCounter(&c);
return (uint64_t)((double)c.QuadPart * 1e9 / (double)freq.QuadPart);
}
int vgpu_present_init(vgpu_ctx* ctx, vgpu_region_t* region, uint32_t default_fps) {
memset(ctx, 0, sizeof *ctx);
ctx->view.producer = region->producer;
ctx->view.control = region->control;
ctx->view.ring = region->ring;
ctx->default_fps = default_fps ? default_fps : 30u;
ctx->backend = VGPU_BK_NONE;
ctx->draw_cursor_cap = 1;
/* one arena: content + frame + cursor buffers */
size_t bytes = VGPU_STAGING_BYTES /* content */
+ VGPU_STAGING_BYTES /* frame */
+ VGPU_CUR_BGRA /* cursor bgra */
+ VGPU_CUR_MASK /* and */
+ VGPU_CUR_MASK; /* xor */
uint8_t* a = (uint8_t*)VirtualAlloc(NULL, bytes, MEM_RESERVE | MEM_COMMIT,
PAGE_READWRITE);
if (!a) {
fprintf(stderr, "present: arena VirtualAlloc %zu MiB failed (%lu)\n",
bytes / (1024 * 1024), GetLastError());
return 1;
}
ctx->arena = a;
ctx->arena_bytes = bytes;
size_t off = 0;
ctx->content_buf = a + off; off += VGPU_STAGING_BYTES;
ctx->frame_buf = a + off; off += VGPU_STAGING_BYTES;
ctx->cursor.bgra = a + off; off += VGPU_CUR_BGRA;
ctx->cursor.and_mask = a + off; off += VGPU_CUR_MASK;
ctx->cursor.xor_mask = a + off; off += VGPU_CUR_MASK;
InitializeCriticalSection(&ctx->lock);
ctx->submit_event = CreateEvent(NULL, FALSE, FALSE, NULL);
ctx->content_seq = 0;
ctx->content_w = ctx->content_h = 0;
return 0;
}
void vgpu_present_deinit(vgpu_ctx* ctx) {
if (ctx->submit_event) { CloseHandle(ctx->submit_event); ctx->submit_event = NULL; }
DeleteCriticalSection(&ctx->lock);
if (ctx->arena) { VirtualFree(ctx->arena, 0, MEM_RELEASE); ctx->arena = NULL; }
}
void vgpu_present_submit(vgpu_ctx* ctx, const uint8_t* src,
uint32_t W, uint32_t H, uint32_t src_pitch) {
if (W > VGPU_MAX_WIDTH) W = VGPU_MAX_WIDTH;
if (H > VGPU_MAX_HEIGHT) H = VGPU_MAX_HEIGHT;
if (W == 0 || H == 0) return;
EnterCriticalSection(&ctx->lock);
uint8_t* d = ctx->content_buf;
const uint32_t row = W * 4u;
for (uint32_t y = 0; y < H; y++)
memcpy(d + (size_t)y * row, src + (size_t)y * src_pitch, row);
ctx->content_w = W;
ctx->content_h = H;
ctx->content_seq++;
LeaveCriticalSection(&ctx->lock);
SetEvent(ctx->submit_event);
}
void vgpu_present_run(vgpu_ctx* ctx) {
const vgpu_region_view* rv = &ctx->view; /* neutral handle for the engine */
const DWORD poll_ms = 8;
int64_t last_seq = -1;
uint32_t prev_state = VGPU_CMD_STOP;
uint32_t last_ff_ack = rv->producer->full_frame_ack;
DWORD last_beat = GetTickCount();
uint64_t last_publish_ns = 0; /* 0 → first eligible frame publishes immediately */
for (;;) {
WaitForSingleObject(ctx->submit_event, poll_ms);
/* --- heartbeat: always ticks, independent of desired_state --- */
DWORD nowt = GetTickCount();
if (nowt - last_beat >= VGPU_HEARTBEAT_PERIOD_MS) {
vgpu_tick_heartbeat(rv);
last_beat = nowt;
}
/* --- reconcile control (gen-seqlock -> apply -> ack) --- */
vgpu_control_view cv;
uint32_t desired = prev_state;
uint32_t draw_cursor = 1;
int force_full = 0;
uint32_t fps = ctx->default_fps; /* publish-rate cap (applied) */
uint32_t ff_req = last_ff_ack; /* full_frame_req value to honor */
if (vgpu_control_read(rv, &cv)) {
desired = cv.desired_state;
draw_cursor = cv.draw_cursor;
fps = cv.target_fps ? cv.target_fps : ctx->default_fps;
vgpu_set_applied_fps(rv, fps);
vgpu_publish_ctrl_ack(rv, cv.gen);
ff_req = cv.full_frame_req;
if ((ff_req - last_ff_ack) != 0u)
force_full = 1; /* edge pending, wrap-tolerant */
}
/* --- lifecycle transitions --- */
if (desired != prev_state) {
if (desired == VGPU_CMD_RUN && prev_state != VGPU_CMD_RUN) {
vgpu_bump_run_epoch(rv);
vgpu_set_status(rv, VGPU_ST_CAPTURING);
force_full = 1; /* fresh frame on start */
} else if (desired == VGPU_CMD_PAUSE) {
vgpu_set_status(rv, VGPU_ST_PAUSED);
} else if (desired == VGPU_CMD_STOP) {
vgpu_set_status(rv, VGPU_ST_STOPPED);
}
prev_state = desired;
} else if (last_seq < 0 && desired == VGPU_CMD_RUN) {
vgpu_set_status(rv, VGPU_ST_CAPTURING);
}
if (desired != VGPU_CMD_RUN) {
/* PAUSED/STOPPED: no new frames; heartbeat still ticks. We do NOT
* ack a pending full_frame here — acking without publishing would
* be a false "honored". A pending request is honored on the next
* transition to RUN (force_full=1 there → publish + ack). */
continue;
}
/* --- compose + publish on content change OR forced full frame, but
* rate-limited to the applied fps cap (the single publish point →
* contract-level cap, independent of the capture backend). A
* force_full bypasses the cap (due=1). --- */
int cur_changed = (ctx->draw_cursor_cap && draw_cursor)
? cursor_sample(ctx) : 0;
uint64_t interval_ns = fps > 0 ? (1000000000ull / fps) : 0;
uint64_t now = now_ns();
int due = force_full || interval_ns == 0
|| (now - last_publish_ns) >= interval_ns;
EnterCriticalSection(&ctx->lock);
int64_t seq = ctx->content_seq;
uint32_t W = ctx->content_w, H = ctx->content_h;
int have = (W && H);
int content_new = have && (seq != last_seq || cur_changed || force_full);
/* take the frame ONLY when due — so we never drop the latest content;
* if not due, last_seq is left untouched and it publishes next due. */
int dirty = content_new && due;
if (dirty) {
memcpy(ctx->frame_buf, ctx->content_buf, (size_t)W * H * 4u);
last_seq = seq;
}
LeaveCriticalSection(&ctx->lock);
if (!dirty) {
/* not due, or nothing to publish. A force_full with content has
* due=1 → dirty=1, so it never lands here while have is true; thus
* no spurious ack edge. */
continue;
}
if (ctx->draw_cursor_cap && draw_cursor)
cursor_draw(ctx, ctx->frame_buf, W, H);
if (vgpu_publish_frame(rv, ctx->frame_buf, W, H, now) == 0) {
last_publish_ns = now;
if (force_full) {
vgpu_publish_full_frame_ack(rv, ff_req);
last_ff_ack = ff_req;
}
} else {
vgpu_set_error(rv, 1u); /* frame too large for slot (mode > max) */
}
}
}
+24
View File
@@ -0,0 +1,24 @@
#ifndef VGPU_PRESENT_H
#define VGPU_PRESENT_H
/* present.h — win32 present/pump lifecycle: staging arena, submit handoff, and
* the publish loop driving the OS-agnostic engine over ctx's region-view. */
#include <stdint.h>
#include "ctx.h" /* win32 vgpu_ctx + vgpu_region_t */
/* Initialize present/staging state inside ctx over an already-created region.
* Allocates the staging+cursor arena. Returns 0 on success. */
int vgpu_present_init(vgpu_ctx* ctx, vgpu_region_t* region, uint32_t default_fps);
void vgpu_present_deinit(vgpu_ctx* ctx);
/* Capture backends submit a freshly captured desktop frame (any source pitch).
* Repacked tight into ctx->content_buf, clamped to max mode. Thread-safe. */
void vgpu_present_submit(vgpu_ctx* ctx, const uint8_t* bgra,
uint32_t width, uint32_t height, uint32_t src_pitch);
/* Run the publish pump: reconcile control, tick heartbeat, compose cursor,
* publish on change / on full_frame_req. Never returns (process lifetime). */
void vgpu_present_run(vgpu_ctx* ctx);
#endif /* VGPU_PRESENT_H */
+146
View File
@@ -0,0 +1,146 @@
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <stdio.h>
#include <string.h>
#include "region.h"
#include "atomic-shim.h" /* x86-TSO ordering for contract init publish */
#define VGPU_2MB (2u * 1024u * 1024u)
/* Page-segregated init of the contract over an already-pinned region base.
* Init-ordering per contract: status=INIT, latest=NONE, backend, supported_formats,
* release-barrier; heartbeat starts later (in the run pump). */
static void region_init_contract(vgpu_region_t* r) {
vgpu_producer_t* p = r->producer;
vgpu_control_t* c = r->control;
memset(p, 0, sizeof *p);
memset(c, 0, sizeof *c);
p->status = VGPU_ST_INIT;
p->backend = VGPU_BK_NONE;
p->error_code = 0;
p->applied_fps = 0;
p->supported_formats = (1u << VGPU_FMT_BGRA8888);
p->run_epoch = 0;
p->heartbeat = 0;
p->frame_id = 0;
p->ctrl_ack = 0;
p->full_frame_ack = 0;
for (uint32_t i = 0; i < VGPU_SLOT_COUNT; i++)
p->seq[i] = 0;
/* control starts RUN: producer captures immediately; host may STOP/PAUSE */
c->ctrl_gen = 0;
c->desired_state = VGPU_CMD_RUN;
c->target_fps = 0;
c->draw_cursor = 1;
c->full_frame_req = 0;
c->consumer_tick = 0;
c->attached = 0;
/* publish latest last with a release store gating all of the above */
vgpu_sfence();
vgpu_store_release32(&p->latest, VGPU_LATEST_NONE);
}
static int adjust_lock_memory_privilege(void) {
HANDLE tok;
if (!OpenProcessToken(GetCurrentProcess(),
TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &tok))
return 0;
TOKEN_PRIVILEGES tp;
memset(&tp, 0, sizeof tp);
tp.PrivilegeCount = 1;
if (!LookupPrivilegeValueA(NULL, SE_LOCK_MEMORY_NAME, &tp.Privileges[0].Luid)) {
CloseHandle(tok);
return 0;
}
tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
int ok = AdjustTokenPrivileges(tok, FALSE, &tp, sizeof tp, NULL, NULL)
&& GetLastError() == ERROR_SUCCESS;
CloseHandle(tok);
return ok;
}
int vgpu_region_create(vgpu_region_t* out) {
memset(out, 0, sizeof *out);
const uint64_t bytes = VGPU_REGION_BYTES;
void* os_base = NULL;
uint8_t* base = NULL;
uint64_t os_total = 0;
if (adjust_lock_memory_privilege()) {
SIZE_T large_min = GetLargePageMinimum();
if (large_min && large_min <= VGPU_2MB) {
SIZE_T rounded = (SIZE_T)((bytes + VGPU_2MB - 1) & ~(uint64_t)(VGPU_2MB - 1));
void* p = VirtualAlloc(NULL, rounded,
MEM_RESERVE | MEM_COMMIT | MEM_LARGE_PAGES,
PAGE_READWRITE);
if (p) {
/* large pages are >= 2 MiB → base is already 2 MiB-aligned */
os_base = p;
base = (uint8_t*)p;
os_total = rounded;
fprintf(stderr, "region: MEM_LARGE_PAGES %llu MiB at %p\n",
(unsigned long long)(rounded / (1024 * 1024)), p);
} else {
fprintf(stderr, "region: MEM_LARGE_PAGES failed (%lu), fallback\n",
GetLastError());
}
}
} else {
fprintf(stderr, "region: SE_LOCK_MEMORY unavailable, fallback\n");
}
if (!base) {
uint64_t total = bytes + VGPU_2MB;
void* p = VirtualAlloc(NULL, (SIZE_T)total, MEM_RESERVE | MEM_COMMIT,
PAGE_READWRITE);
if (!p) {
fprintf(stderr, "region: VirtualAlloc %llu MiB failed (%lu)\n",
(unsigned long long)(total / (1024 * 1024)), GetLastError());
return 1;
}
uintptr_t addr = (uintptr_t)p;
uintptr_t aligned = (addr + VGPU_2MB - 1) & ~(uintptr_t)(VGPU_2MB - 1);
if (!VirtualLock((void*)aligned, (SIZE_T)bytes)) {
fprintf(stderr, "region: VirtualLock failed (%lu) — pages may not be pinned\n",
GetLastError());
}
os_base = p;
base = (uint8_t*)aligned;
os_total = total;
fprintf(stderr, "region: fallback VirtualAlloc+VirtualLock %llu MiB, aligned at %p\n",
(unsigned long long)(bytes / (1024 * 1024)), (void*)aligned);
}
if (((uintptr_t)base & (VGPU_2MB - 1)) != 0) {
fprintf(stderr, "region: base %p not 2 MiB aligned\n", (void*)base);
VirtualFree(os_base, 0, MEM_RELEASE);
return 1;
}
out->os_base = os_base;
out->base = base;
out->os_total = os_total;
out->producer = (vgpu_producer_t*)(base + VGPU_PRODUCER_OFFSET);
out->control = (vgpu_control_t*)(base + VGPU_CONTROL_OFFSET);
out->ring = base + VGPU_RING_OFFSET;
region_init_contract(out);
fprintf(stderr, "region: contract ready (producer=%p control=%p ring=%p)\n",
(void*)out->producer, (void*)out->control, (void*)out->ring);
return 0;
}
void vgpu_region_destroy(vgpu_region_t* r) {
if (r && r->os_base) {
VirtualUnlock(r->base, (SIZE_T)VGPU_REGION_BYTES);
VirtualFree(r->os_base, 0, MEM_RELEASE);
memset(r, 0, sizeof *r);
}
}
+28
View File
@@ -0,0 +1,28 @@
#ifndef VGPU_REGION_H
#define VGPU_REGION_H
/* region.h — win32 pinned contract region (resolves blocks for the region-view). */
#include <stdint.h>
#include "vgpu_stream.h" /* public contract: blocks, offsets, slot geometry */
/*
* One contiguous 2 MiB-aligned pinned region holding the full contract:
* producer block (page 0), control block (page 1), then SLOT_COUNT frame slots
* starting at VGPU_RING_OFFSET. Object = memory: the region owns the mapping,
* its lifetime is the mapping's lifetime. No hidden global state.
*/
typedef struct {
void* os_base; /* raw allocation base (for free) */
uint8_t* base; /* 2 MiB-aligned region base (== contract origin) */
uint64_t os_total; /* bytes reserved at os_base */
vgpu_producer_t* producer; /* base + VGPU_PRODUCER_OFFSET */
vgpu_control_t* control; /* base + VGPU_CONTROL_OFFSET */
uint8_t* ring; /* base + VGPU_RING_OFFSET */
} vgpu_region_t;
/* Returns 0 on success, non-zero on failure (region zeroed on failure). */
int vgpu_region_create(vgpu_region_t* out);
void vgpu_region_destroy(vgpu_region_t* r);
#endif /* VGPU_REGION_H */
+275
View File
@@ -0,0 +1,275 @@
/**
* \file This file contains definitions for NVFBC API.
* \copyright
*
* Copyright 1993-2016 NVIDIA Corporation. All rights reserved.
* NOTICE TO LICENSEE: This source code and/or documentation ("Licensed Deliverables")
* are subject to the applicable NVIDIA license agreement
* that governs the use of the Licensed Deliverables.
*
*/
#pragma once
#include <Windows.h>
typedef unsigned char NvU8;
typedef unsigned long NvU32;
typedef unsigned long long NvU64;
/**
* \defgroup NVFBC The NVIDIA Frame Buffer Capture API.
* \brief Defines a set of interfaces for high performance Capture of desktop content.
*/
/**
* \defgroup NVFBC_ENUMS Enums
* \ingroup NVFBC
* \brief Enumerations to be used with NVFBC API
*/
/**
* \defgroup NVFBC_STRUCTS Structs
* \ingroup NVFBC
* \brief Defines Parameter Structures to be used with NVFBC APIs.
*/
/**
* \defgroup NVFBC_ENTRYPOINTS Entrypoints
* \ingroup NVFBC
* \brief Declarations for NVFBC Entrypoint functions
*/
/**
* \ingroup NVFBC
* Macro to define the NVFBC API version corresponding to this distribution.
*/
#define NVFBC_DLL_VERSION 0x50
/**
* \ingroup NVFBC
* Macro to construct version numbers for parameter structs.
*/
#define NVFBC_STRUCT_VERSION(typeName, ver) (NvU32)(sizeof(typeName) | ((ver)<<16) | (NVFBC_DLL_VERSION << 24))
/**
* \ingroup NVFBC
* Calling Convention
*/
#define NVFBCAPI __stdcall
/**
* \ingroup NVFBC
* Indicates that there are no global overrides specified for NVFBC. To be used with NVFBC_SetGlobalFlags API
*/
#define NVFBC_GLOBAL_FLAGS_NONE 0x00000000
/**
* \ingroup NVFBC
* Indicates to NVFBC that stereo rendering is enabled. Currently unsupported. To be used with NVFBC_SetGlobalFlags API.
*/
#define NVFBC_GLOBAL_FLAGS_STEREO_BUFFER 0x00000001
/**
* \ingroup NVFBC
* Indicates that NVFBC should not request a repaint of the desktop when initiating NVFBC capture. To be used with NVFBC_SetGlobalFlags API.
*/
#define NVFBC_GLOBAL_FLAGS_NO_INITIAL_REFRESH 0x00000002
/**
* \ingroup NVFBC
* Indicates that NVFBC should not reset the graphics driver while servicing subsequent NVFBC_Enable API requests.
*/
#define NVFBC_GLOBAL_FLAGS_NO_DEVICE_RESET_TOGGLE 0x00000004
/**
* \ingroup NVFBC_ENUMS
* \brief Enumerates status codes returned by NVFBC APIs.
*/
typedef enum _NVFBCRESULT
{
NVFBC_SUCCESS = 0,
NVFBC_ERROR_GENERIC = -1, /**< Unexpected failure in NVFBC. */
NVFBC_ERROR_INVALID_PARAM = -2, /**< One or more of the paramteres passed to NvFBC are invalid [This include NULL pointers]. */
NVFBC_ERROR_INVALIDATED_SESSION = -3, /**< NvFBC session is invalid. Client needs to recreate session. */
NVFBC_ERROR_PROTECTED_CONTENT = -4, /**< Protected content detected. Capture failed. */
NVFBC_ERROR_DRIVER_FAILURE = -5, /**< GPU driver returned failure to process NvFBC command. */
NVFBC_ERROR_CUDA_FAILURE = -6, /**< CUDA driver returned failure to process NvFBC command. */
NVFBC_ERROR_UNSUPPORTED = -7, /**< API Unsupported on this version of NvFBC. */
NVFBC_ERROR_HW_ENC_FAILURE = -8, /**< HW Encoder returned failure to process NVFBC command. */
NVFBC_ERROR_INCOMPATIBLE_DRIVER = -9, /**< NVFBC is not compatible with this version of the GPU driver. */
NVFBC_ERROR_UNSUPPORTED_PLATFORM = -10, /**< NVFBC is not supported on this platform. */
NVFBC_ERROR_OUT_OF_MEMORY = -11, /**< Failed to allocate memory. */
NVFBC_ERROR_INVALID_PTR = -12, /**< A NULL pointer was passed. */
NVFBC_ERROR_INCOMPATIBLE_VERSION = -13, /**< An API was called with a parameter struct that has an incompatible version. Check dwVersion field of paramter struct. */
NVFBC_ERROR_OPT_CAPTURE_FAILURE = -14, /**< Desktop Capture failed. */
NVFBC_ERROR_INSUFFICIENT_PRIVILEGES = -15, /**< User doesn't have appropriate previlages. */
NVFBC_ERROR_INVALID_CALL = -16, /**< NVFBC APIs called in wrong sequence. */
NVFBC_ERROR_SYSTEM_ERROR = -17, /**< Win32 error. */
NVFBC_ERROR_INVALID_TARGET = -18, /**< The target adapter idx can not be used for NVFBC capture. It may not correspond to an NVIDIA GPU, or may not be attached to desktop. */
NVFBC_ERROR_DYNAMIC_DISABLE = -20, /**< NvFBC is dynamically disabled. Cannot continue to capture */
} NVFBCRESULT;
/**
* \ingroup NVFBC_ENUMS
* \brief Enumerates NVFBC states. To be used with NvFBC_Enable API
*/
typedef enum _NVFBC_STATE
{
NVFBC_STATE_DISABLE = 0, /** Disables NvFBC. */
NVFBC_STATE_ENABLE , /** Enables NvFBC. */
NVFBC_STATE_LAST , /** Sentinel value. Shouldn't be used. */
} NVFBC_STATE;
/**
* \ingroup NVFBC_STRUCTS
* \brief Defines parameters that describe the grabbed data, and provides detailed information about status of the NVFBC session.
*/
typedef struct _NvFBCFrameGrabInfo
{
DWORD dwWidth; /**< [out] Indicates the current width of captured buffer. */
DWORD dwHeight; /**< [out] Indicates the current height of captured buffer. */
DWORD dwBufferWidth; /**< [out] Indicates the current width of the pixel buffer(padded width). */
DWORD dwReserved; /**< [in] Reserved, do not use. */
BOOL bOverlayActive; /**< [out] Is set to 1 if overlay was active. */
BOOL bMustRecreate; /**< [out] Is set to 1 if the compressor must call NvBFC_Create again. */
BOOL bFirstBuffer; /**< [out] Is set to 1 is this was the first capture call, or first call after a desktop mode change.
Relevant only for XOR and diff modes supported by NVFBCToSys interface. */
BOOL bHWMouseVisible; /**< [out] Is set to 1 if HW cursor was enabled by OS at the time of the grab. */
BOOL bProtectedContent; /**< [out] Is set to 1 if protected content was active (DXVA encryption Session). */
DWORD dwDriverInternalError; /**< [out] Indicates the status code from lower layers. 0 or 0xFBCA11F9 indicates no error was returned. */
BOOL bStereoOn; /**< [out] Is set to 1 if stereo was on. */
BOOL bIGPUCapture; /**< [out] Is set to 1 if the captured frame is from iGPU. 0 if capture fails or if captured from dGPU*/
DWORD dwSourcePID; /**< [out] Indicates which process caused the last screen update that got grabbed*/
DWORD dwReserved3; /**< [in] Reserved, do not use. */
NvU32 dwReserved2[13]; /**< [in] Resereved, should be set to 0. */
} NvFBCFrameGrabInfo;
/**
* \ingroup NVFBC_STRUCTS
* \brief Deines the parameters to be used with NvFBC_GetStatusEx API
*/
typedef struct _NvFBCStatusEx
{
NvU32 dwVersion; /**< [in] Struct version. Set to NVFBC_STATUS_VER. */
NvU32 bIsCapturePossible :1; /**< [out] Indicates if NvFBC feature is enabled. */
NvU32 bCurrentlyCapturing:1; /**< [out] Indicates if NVFBC is currently capturing for the Adapter ordinal specified in dwAdapterIdx. */
NvU32 bCanCreateNow :1; /**< [out] Deprecated. Do not use. */
NvU32 bSupportMultiHead :1; /**< [out] MultiHead grab supported. */
NvU32 bSupportMultiClient:1; /**< [out] Multiple capture clients on same display adapter supported. */
NvU32 bReservedBits :27; /**< [in] Reserved, do not use. */
NvU32 dwNvFBCVersion; /**< [out] Indicates the highest NvFBC interface version supported by the loaded NVFBC library. */
NvU32 dwAdapterIdx; /**< [in] Adapter Ordinal corresponding to the display to be grabbed. IGNORED if bCapturePID is set */
void* pPrivateData; /**< [in] optional **/
NvU32 dwPrivateDataSize; /**< [in] optional **/
NvU32 dwReserved[59]; /**< [in] Reserved. Should be set to 0. */
void* pReserved[31]; /**< [in] Reserved. Should be set to NULL. */
} NvFBCStatusEx;
#define NVFBC_STATUS_VER_1 NVFBC_STRUCT_VERSION(NvFBCStatusEx, 1)
#define NVFBC_STATUS_VER_2 NVFBC_STRUCT_VERSION(NvFBCStatusEx, 2)
#define NVFBC_STATUS_VER NVFBC_STATUS_VER_2
/**
* \ingroup NVFBC_STRUCTS
* \brief Defines the parameters to be used with NvFBC_CreateEx API.
*/
typedef struct _NvFBCCreateParams
{
NvU32 dwVersion; /**< [in] Struct version. Set to NVFBC_CREATE_PARAMS_VER. */
NvU32 dwInterfaceType; /**< [in] ID of the NVFBC interface Type being requested. */
NvU32 dwMaxDisplayWidth; /**< [out] Max. display width allowed. */
NvU32 dwMaxDisplayHeight; /**< [out] Max. display height allowed. */
void* pDevice; /**< [in] Device pointer. */
void* pPrivateData; /**< [in] Private data [optional]. */
NvU32 dwPrivateDataSize; /**< [in] Size of private data. */
NvU32 dwInterfaceVersion; /**< [in] Version of the capture interface. */
void* pNvFBC; /**< [out] A pointer to the requested NVFBC object. */
NvU32 dwAdapterIdx; /**< [in] Adapter Ordinal corresponding to the display to be grabbed. If pDevice is set, this parameter is ignored. */
NvU32 dwNvFBCVersion; /**< [out] Indicates the highest NvFBC interface version supported by the loaded NVFBC library. */
void* cudaCtx; /**< [in] CUDA context created using cuD3D9CtxCreate with the D3D9 device passed as pDevice. Only used for NvFBCCuda interface.
It is mandatory to pass a valid D3D9 device if cudaCtx is passed. The call will fail otherwise.
Client must release NvFBCCuda object before destroying the cudaCtx. */
void* pPrivateData2; /**< [in] Private data [optional]. */
NvU32 dwPrivateData2Size; /**< [in] Size of private data. */
NvU32 dwReserved[55]; /**< [in] Reserved. Should be set to 0. */
void* pReserved[27]; /**< [in] Reserved. Should be set to NULL. */
}NvFBCCreateParams;
#define NVFBC_CREATE_PARAMS_VER_1 NVFBC_STRUCT_VERSION(NvFBCCreateParams, 1)
#define NVFBC_CREATE_PARAMS_VER_2 NVFBC_STRUCT_VERSION(NvFBCCreateParams, 2)
#define NVFBC_CREATE_PARAMS_VER NVFBC_CREATE_PARAMS_VER_2
/**
* \ingroup NVFBC_STRUCTS
* \brief Defines parameters for a Grab\Capture call to get HW cursor data in the NVFBCToSys capture session.
*/
typedef struct
{
NvU32 dwVersion; /**< [in]: Struct version. Set to NVFBC_MOUSE_GRAB_INFO_VER.*/
NvU32 dwWidth; /**< [out]: Width of mouse glyph captured.*/
NvU32 dwHeight; /**< [out]: Height of mouse glyph captured.*/
NvU32 dwPitch; /**< [out]: Pitch of mouse glyph captured.*/
NvU32 bIsHwCursor : 1; /**< [out]: Tells if cursor is HW cursor or SW cursor. If set to 0, ignore height, width, pitch and pBits.*/
NvU32 bReserved : 32; /**< [in]: Reserved.*/
NvU32 dwPointerFlags; /**< [out]: Maps to DXGK_POINTERFLAGS::Value.*/
NvU32 dwXHotSpot; /**< [out]: Maps to DXGKARG_SETPOINTERSHAPE::XHot.*/
NvU32 dwYHotSpot; /**< [out]: Maps to DXGKARG_SETPOINTERSHAPE::YHot.*/
NvU32 dwUpdateCounter; /**< [out]: Cursor update Counter. */
NvU32 dwBufferSize; /**< [out]: Size of the buffer contaiing the captured cursor glyph. */
void * pBits; /**< [out]: pointer to buffer containing the captured cursor glyph.*/
NvU32 dwReservedA[22]; /**< [in]: Reserved. Set to 0.*/
void * pReserved[15]; /**< [in]: Reserved. Set to 0.*/
}NVFBC_CURSOR_CAPTURE_PARAMS;
#define NVFBC_CURSOR_CAPTURE_PARAMS_VER NVFBC_STRUCT_VERSION(NVFBC_CURSOR_CAPTURE_PARAMS, 1)
/**
* \ingroup NVFBC_ENTRYPOINTS
* \brief NVFBC API to set global overrides
* \param [in] dwFlags Global overrides for NVFBC. Use ::NVFBC_GLOBAL_FLAGS value.
*/
void NVFBCAPI NvFBC_SetGlobalFlags(DWORD dwFlags);
/**
* \ingroup NVFBC_ENTRYPOINTS
* \brief NVFBC API to create an NVFBC capture session.
* Instantiates an interface identified by NvFBCCreateParams::dwInterfaceType.
* \param [inout] pCreateParams Pointer to a struct of type ::NvFBCCreateParams, typecast to void*
* \return An applicable ::NVFBCRESULT value.
*/
NVFBCRESULT NVFBCAPI NvFBC_CreateEx(void * pCreateParams);
/**
* \ingroup NVFBC_ENTRYPOINTS
* \brief NVFBC API to query Current NVFBC status.
* Queries the status for the adapter pointed to by the NvFBCStatusEx::dwAdapterIdx parameter.
* \param [inout] pCreateParams Pointer to a struct of type ::NvFBCStatusEx.
* \return An applicable ::NVFBCRESULT value.
*/
NVFBCRESULT NVFBCAPI NvFBC_GetStatusEx(NvFBCStatusEx *pNvFBCStatusEx);
/**
* \ingroup NVFBC_ENTRYPOINTS
* \brief NVFBC API to enable \ disable NVFBC feature.
* \param [in] nvFBCState Refer ::NVFBC_STATE
* \return An applicable ::NVFBCRESULT value.
*/
NVFBCRESULT NVFBCAPI NvFBC_Enable(NVFBC_STATE nvFBCState);
/**
* \ingroup NVFBC_ENTRYPOINTS
* \brief NVFBC API to query highest GRID SDK version supported by the loaded NVFBC library.
* \param [out] pVersion Pointer to a 32-bit integer to hold the supported GRID SDK version.
* \return An applicable ::NVFBCRESULT value.
*/
NVFBCRESULT NVFBCAPI NvFBC_GetSDKVersion(NvU32 * pVersion);
/**
* \cond API_PFN
*/
typedef void (NVFBCAPI * NvFBC_SetGlobalFlagsType) (DWORD dwFlags);
typedef NVFBCRESULT (NVFBCAPI * NvFBC_CreateFunctionExType) (void * pCreateParams);
typedef NVFBCRESULT (NVFBCAPI * NvFBC_GetStatusExFunctionType) (void * pNvFBCStatus);
typedef NVFBCRESULT (NVFBCAPI * NvFBC_EnableFunctionType) (NVFBC_STATE nvFBCState);
typedef NVFBCRESULT (NVFBCAPI * NvFBC_GetSDKVersionFunctionType) (NvU32 * pVersion);
/**
* \endcond API_PFN
*/
+176
View File
@@ -0,0 +1,176 @@
/**
* \file This file contains defintions for NVFBCToSys
*
* Copyright 1993-2016 NVIDIA Corporation. All rights reserved.
* NOTICE TO LICENSEE: This source code and/or documentation ("Licensed Deliverables")
* are subject to the applicable NVIDIA license agreement
* that governs the use of the Licensed Deliverables.
*
*/
#ifndef NVFBC_TO_SYS_H_
#define NVFBC_TO_SYS_H_
/**
* \defgroup NVFBC_TOSYS NVFBCToSys Interface
* \brief Interface for grabbing Desktop images and generating output in system memory.
*/
/**
* \defgroup NVFBC_TOSYS_ENUMS Enums
* \ingroup NVFBC_TOSYS
* \brief Enumerations used with NVFBCToSys interface.
*/
/**
* \defgroup NVFBC_TOSYS_STRUCTS Structs
* \ingroup NVFBC_TOSYS
* \brief Parameter Structs Defined for use with NVFBCToSys interface.
*/
/**
* \defgroup NVFBC_TOSYS_INTERFACE Object Interface
* \ingroup NVFBC_TOSYS
* \brief Interface class definition for NVFBCToSys Capture API
*/
/**
* \ingroup NVFBC_TOSYS
* \brief Macro to define the interface ID to be passed as NvFBCCreateParams::dwInterfaceType
* for creating an NVFBCToSys capture session object.
*/
#define NVFBC_TO_SYS (0x1204)
/**
* \ingroup NVFBC_TOSYS_ENUMS
* Enumerates output buffer pixel data formats supported by NVFBCToSys.
*/
typedef enum
{
NVFBC_TOSYS_ARGB = 0, /**< Output Pixels in ARGB format: 32bpp, one byte per channel. */
NVFBC_TOSYS_RGB , /**< Output Pixels in RGB format: 24bpp, one byte per channel. */
NVFBC_TOSYS_YYYYUV420p , /**< Output Pixels in YUV420 format: 12bpp,
the Y' channel at full resolution, U channel at half resolution (1 byte for four pixels), V channel at half resolution. */
NVFBC_TOSYS_RGB_PLANAR , /**< Output Pixels in planar RGB format: 24bpp,
stored sequentially in memory as complete red channel, complete green channel, complete blue channel. */
NVFBC_TOSYS_XOR , /**< Output Pixels in RGB format: 24bpp XOR'd with the prior frame. */
NVFBC_TOSYS_YUV444p , /**< Output Pixels in YUV444 planar format, i.e. separate 8-bpp Y, U, V planes with no subsampling.*/
NVFBC_TOSYS_BUF_FMT_LAST , /**< Sentinel value. Do not use.*/
} NVFBCToSysBufferFormat;
/**
* \ingroup NVFBC_TOSYS_ENUMS
* Enumerates Capture\Grab modes supported by NVFBCToSys.
*/
typedef enum
{
NVFBC_TOSYS_SOURCEMODE_FULL = 0, /**< Grab full res */
NVFBC_TOSYS_SOURCEMODE_SCALE , /**< Will convert current res to supplied resolution (dwTargetWidth and dwTargetHeight) */
NVFBC_TOSYS_SOURCEMODE_CROP , /**< Native res, crops a subwindow, of dwTargetWidth and dwTargetHeight sizes, starting at dwStartX and dwStartY */
NVFBC_TOSYS_SOURCEMODE_LAST , /**< Sentinel value. Do not use. */
}NVFBCToSysGrabMode;
/**
* \ingroup NVFBC_TOSYS_ENUMS
* \enum NVFBC_TOSYS_GRAB_FLAGS Enumerates special commands for grab\capture supported by NVFBCToSys.
*/
typedef enum
{
NVFBC_TOSYS_NOFLAGS = 0x0, /**< Default (no flags set). Grabbing will wait for a new frame or HW mouse move. */
NVFBC_TOSYS_NOWAIT = 0x1, /**< Grabbing will not wait for a new frame nor a HW cursor move. */
NVFBC_TOSYS_WAIT_WITH_TIMEOUT = 0x10, /**< Grabbing will wait for a new frame or HW mouse move with a maximum wait time of NVFBC_TOSYS_GRAB_FRAME_PARAMS::dwWaitTime millisecond*/
} NVFBC_TOSYS_GRAB_FLAGS;
/**
* \ingroup NVFBC_TOSYS_STRUCTS
* \brief Defines parameters used to configure NVFBCToSys capture session.
*/
typedef struct
{
NvU32 dwVersion; /**< [in]: Struct version. Set to NVFBC_TOSYS_SETUP_PARAMS_VER.*/
NvU32 bWithHWCursor :1; /**< [in]: The client should set this to 1 if it requires the HW cursor to be composited on the captured image.*/
NvU32 bDiffMap :1; /**< [in]: The client should set this to use the DiffMap feature.*/
NvU32 bEnableSeparateCursorCapture : 1; /**< [in]: The client should set this to 1 if it wants to enable mouse capture in separate stream.*/
NvU32 bReservedBits :29; /**< [in]: Reserved. Set to 0.*/
NVFBCToSysBufferFormat eMode; /**< [in]: Output image format.*/
NvU32 dwReserved1; /**< [in]: Reserved. Set to 0.*/
void **ppBuffer; /**< [out]: Container to hold NvFBC output buffers.*/
void **ppDiffMap; /**< [out]: Container to hold NvFBC output diffmap buffers.*/
void *hCursorCaptureEvent; /**< [out]: Client should wait for mouseEventHandle event before calling MouseGrab function. */
NvU32 dwReserved[58]; /**< [in]: Reserved. Set to 0.*/
void *pReserved[29]; /**< [in]: Reserved. Set to 0.*/
} NVFBC_TOSYS_SETUP_PARAMS_V2;
#define NVFBC_TOSYS_SETUP_PARAMS_VER2 NVFBC_STRUCT_VERSION(NVFBC_TOSYS_SETUP_PARAMS, 2)
typedef NVFBC_TOSYS_SETUP_PARAMS_V2 NVFBC_TOSYS_SETUP_PARAMS;
#define NVFBC_TOSYS_SETUP_PARAMS_VER NVFBC_TOSYS_SETUP_PARAMS_VER2
/**
* \ingroup NVFBC_TOSYS_STRUCTS
* \brief Defines parameters for a Grab\Capture call in the NVFBCToSys capture session.
* Also holds information regarding the grabbed data.
*/
typedef struct
{
NvU32 dwVersion; /**< [in]: Struct version. Set to NVFBC_TOSYS_GRAB_FRAME_PARAMS_VER.*/
NvU32 dwFlags; /**< [in]: Special grabbing requests. This should be a bit-mask of NVFBC_TOSYS_GRAB_FLAGS values.*/
NvU32 dwTargetWidth; /**< [in]: Target image width. NvFBC will scale the captured image to fit taret width and height. Used with NVFBC_TOSYS_SOURCEMODE_SCALE and NVFBC_TOSYS_SOURCEMODE_CROP. */
NvU32 dwTargetHeight; /**< [in]: Target image height. NvFBC will scale the captured image to fit taret width and height. Used with NVFBC_TOSYS_SOURCEMODE_SCALE and NVFBC_TOSYS_SOURCEMODE_CROP. */
NvU32 dwStartX; /**< [in]: x-coordinate of starting pixel for cropping. Used with NVFBC_TOSYS_SOURCEMODE_CROP. */
NvU32 dwStartY; /**< [in]: y-coordinate of starting pixel for cropping. Used with NVFBC_TOSYS_SOURCEMODE_CROP. .*/
NVFBCToSysGrabMode eGMode; /**< [in]: Frame grab mode.*/
NvU32 dwWaitTime; /**< [in]: Time limit for NvFBCToSysGrabFrame() to wait until a new frame is available or a HW mouse moves. Use with NVFBC_TOSYS_WAIT_WITH_TIMEOUT */
NvFBCFrameGrabInfo *pNvFBCFrameGrabInfo; /**< [in/out]: Frame grab information and feedback from NvFBC driver.*/
NvU32 dwReserved[56]; /**< [in]: Reserved. Set to 0.*/
void *pReserved[31]; /**< [in]: Reserved. Set to NULL.*/
} NVFBC_TOSYS_GRAB_FRAME_PARAMS_V1;
#define NVFBC_TOSYS_GRAB_FRAME_PARAMS_VER1 NVFBC_STRUCT_VERSION(NVFBC_TOSYS_GRAB_FRAME_PARAMS, 1)
typedef NVFBC_TOSYS_GRAB_FRAME_PARAMS_V1 NVFBC_TOSYS_GRAB_FRAME_PARAMS;
#define NVFBC_TOSYS_GRAB_FRAME_PARAMS_VER NVFBC_TOSYS_GRAB_FRAME_PARAMS_VER1
/**
* \ingroup NVFBC_TOSYS_INTERFACE
* Interface class definition for NVFBCToSys Capture API
*/
class INvFBCToSys_v3
{
public:
/**
* \brief Sets up NVFBC System Memory capture according to the provided parameters.
* \param [in] pParam Pointer to a struct of type ::NVFBC_TOSYS_SETUP_PARAMS.
* \return An applicable ::NVFBCRESULT value.
*/
virtual NVFBCRESULT NVFBCAPI NvFBCToSysSetUp (NVFBC_TOSYS_SETUP_PARAMS_V2 *pParam) = 0;
/**
* \brief Captures the desktop and dumps the captured data to a System memory buffer.
* If the API returns a failure, the client should check the return codes and ::NvFBCFrameGrabInfo output fields to determine if the session needs to be re-created.
* \param [inout] pParam Pointer to a struct of type ::NVFBC_TOSYS_GRAB_FRAME_PARAMS.
* \return An applicable ::NVFBCRESULT value.
*/
virtual NVFBCRESULT NVFBCAPI NvFBCToSysGrabFrame (NVFBC_TOSYS_GRAB_FRAME_PARAMS *pParam) = 0;
/**
* \brief Captures HW cursor data whenever shape of mouse is changed
* \param [inout] pParam Pointer to a struct of type ::NVFBC_CURSOR_CAPTURE_PARAMS.
* \return An applicable ::NVFBCRESULT value.
*/
virtual NVFBCRESULT NVFBCAPI NvFBCToSysCursorCapture (NVFBC_CURSOR_CAPTURE_PARAMS *pParam) = 0;
/**
* \brief A high precision implementation of Sleep().
* Can provide sub quantum (usually 16ms) sleep that does not burn CPU cycles.
* \param [in] qwMicroSeconds The number of microseconds that the thread should sleep for.
* \return An applicable ::NVFBCRESULT value.
*/
virtual NVFBCRESULT NVFBCAPI NvFBCToSysGPUBasedCPUSleep (__int64 qwMicroSeconds) = 0;
/**
* \brief Destroys the NVFBCToSys capture session.
* \return An applicable ::NVFBCRESULT value.
*/
virtual NVFBCRESULT NVFBCAPI NvFBCToSysRelease () = 0;
};
typedef INvFBCToSys_v3 NvFBCToSys;
#endif // NVFBC_TO_SYS_H_
+2
View File
@@ -0,0 +1,2 @@
/* Windows.h — case-compat shim for the vendor NvFBC header, not our API. */
#include <windows.h>