fix(desktop): drop game capture injection and pin dll search (#2069)

This commit is contained in:
Hampus authored and GitHub committed 2026-08-29 14:26:28 +02:00
1 parent 9ad026b8ce
commit 9d0be1ebd1
43 files changed
+158 -12089

No files matched your search

@@ -22,8 +22,6 @@ function makeFakeBinding() {
const calls = [];
const frameSinkHandleCalls = [];
const priorityCalls = [];
const vulkanCalls = [];
const hookAvailable = {value: false};
const natives = [];
const diagnostics = {
state: 1,
@@ -38,9 +36,7 @@ function makeFakeBinding() {
droppedFrameCounter: 0,
lastPresentTimestampUs: 123456,
lastError: 0,
requestedInjectionMethod: 'auto',
injectionMethod: 'remote-thread',
activeStrategy: 'game-hook',
activeStrategy: 'wgc',
lastFallbackReason: '',
startOptions: {
colorRange: 'full',
@@ -88,27 +84,13 @@ function makeFakeBinding() {
frameSinkHandleCalls.push(handle);
}
start(
sourceId,
sourceKind,
width,
height,
frameRate,
hookDllPath,
hookDllPathX86,
injectionMethod,
captureId,
captureOptions,
) {
start(sourceId, sourceKind, width, height, frameRate, captureId, captureOptions) {
calls.push({
sourceId,
sourceKind,
width,
height,
frameRate,
hookDllPath,
hookDllPathX86,
injectionMethod,
captureId,
captureOptions,
});
@@ -190,19 +172,10 @@ function makeFakeBinding() {
restoreGpuSchedulingPriority: (processId) => {
priorityCalls.push({type: 'restore', processId});
},
isGameCaptureHookAvailable: () => hookAvailable.value,
registerVulkanLayerManifest: (manifestPath) => {
vulkanCalls.push({type: 'register', manifestPath});
},
unregisterVulkanLayerManifest: (manifestPath) => {
vulkanCalls.push({type: 'unregister', manifestPath});
},
},
calls,
frameSinkHandleCalls,
priorityCalls,
vulkanCalls,
hookAvailable,
natives,
diagnostics,
encoderDiagnostics,
@@ -258,74 +231,7 @@ describe('win-game-capture loader wrapper -- binding-absent fallback path', () =
);
});
describe('win-game-capture loader wrapper -- arch path resolvers (platform-portable)', () => {
test('resolveGameHookPath() is null or the host-arch hook path', () => {
const r = winGameCapture.resolveGameHookPath();
assert.ok(
r === null || (typeof r === 'string' && /fluxer-game-hook\.win32-(x64|ia32|arm64)-msvc\.dll$/.test(r)),
`unexpected resolveGameHookPath(): ${r}`,
);
});
test('resolveGameHookPathX86() is null or the ia32 hook path', () => {
const r = winGameCapture.resolveGameHookPathX86();
assert.ok(
r === null || (typeof r === 'string' && r.endsWith('fluxer-game-hook.win32-ia32-msvc.dll')),
`unexpected resolveGameHookPathX86(): ${r}`,
);
});
test('resolveVulkanLayerManifestPath() is null or the host-arch layer manifest path', () => {
const r = winGameCapture.resolveVulkanLayerManifestPath();
assert.ok(
r === null || (typeof r === 'string' && /fluxer-vulkan-layer\.win32-(x64|ia32|arm64)-msvc\.json$/.test(r)),
`unexpected resolveVulkanLayerManifestPath(): ${r}`,
);
});
});
describe('win-game-capture loader wrapper -- injected fake binding', () => {
test('isGameCaptureHookAvailable() follows the native hook flag', {skip: injectionSkip}, () => {
const {binding} = makeFakeBinding();
winGameCapture.__setBindingForTests(binding);
assert.equal(winGameCapture.isGameCaptureHookAvailable(), false);
});
test(
'isGameCaptureHookAvailable() is false when the native binding predates the hook flag',
{skip: injectionSkip},
() => {
const {binding} = makeFakeBinding();
binding.isGameCaptureHookAvailable = undefined;
winGameCapture.__setBindingForTests(binding);
assert.equal(winGameCapture.isGameCaptureHookAvailable(), false);
},
);
test(
'registerVulkanLayerManifest() never touches the registry while hook capture is unavailable',
{skip: injectionSkip},
() => {
const {binding, vulkanCalls} = makeFakeBinding();
winGameCapture.__setBindingForTests(binding);
assert.equal(winGameCapture.registerVulkanLayerManifest(), false);
assert.deepEqual(vulkanCalls, [], 'the native registration entry point must not be called');
},
);
test(
'registerVulkanLayerManifest() still refuses when the hook DLL is missing for this host',
{skip: injectionSkip || (winGameCapture.resolveGameHookPath() !== null && 'host ships a game capture hook DLL')},
() => {
const {binding, hookAvailable, vulkanCalls} = makeFakeBinding();
hookAvailable.value = true;
winGameCapture.__setBindingForTests(binding);
assert.equal(winGameCapture.isGameCaptureHookAvailable(), false);
assert.equal(winGameCapture.registerVulkanLayerManifest(), false);
assert.deepEqual(vulkanCalls, []);
},
);
test(
'listSources() forwards sanitized screen/window sources from the native binding',
{skip: injectionSkip},
@@ -355,7 +261,7 @@ describe('win-game-capture loader wrapper -- injected fake binding', () => {
);
test(
'start() forwards sourceId/kind/dims/frameRate and BOTH hook paths (6th + 7th args)',
'start() forwards sourceId/kind/dims/frameRate/captureId and the capture options',
{skip: injectionSkip},
async () => {
const {binding, calls} = makeFakeBinding();
@@ -366,8 +272,6 @@ describe('win-game-capture loader wrapper -- injected fake binding', () => {
width: 1600,
height: 900,
frameRate: 60,
hookDllPath: 'C:/hooks/fluxer-game-hook.win32-x64-msvc.dll',
hookDllPathX86: 'C:/hooks/fluxer-game-hook.win32-ia32-msvc.dll',
captureId: 'capture-1',
colorRange: 'full',
colorSpace: 'rec709',
@@ -383,9 +287,6 @@ describe('win-game-capture loader wrapper -- injected fake binding', () => {
width: 1600,
height: 900,
frameRate: 60,
hookDllPath: 'C:/hooks/fluxer-game-hook.win32-x64-msvc.dll',
hookDllPathX86: 'C:/hooks/fluxer-game-hook.win32-ia32-msvc.dll',
injectionMethod: undefined,
captureId: 'capture-1',
captureOptions: {
colorRange: 'full',
@@ -401,40 +302,7 @@ describe('win-game-capture loader wrapper -- injected fake binding', () => {
},
);
test(
'start() forwards the injectionMethod option as the 8th arg for game capture',
{skip: injectionSkip},
async () => {
const {binding, calls} = makeFakeBinding();
winGameCapture.__setBindingForTests(binding);
const capture = new winGameCapture.ScreenCapture({
sourceId: '555',
sourceKind: 'game',
hookDllPath: 'C:/hooks/fluxer-game-hook.win32-x64-msvc.dll',
hookDllPathX86: 'C:/hooks/fluxer-game-hook.win32-ia32-msvc.dll',
injectionMethod: 'set-windows-hook',
});
capture.on('error', () => {});
await capture.start();
assert.equal(calls.length, 1);
assert.equal(calls[0].injectionMethod, 'set-windows-hook');
},
);
test('window sourceKind does not forward the injectionMethod', {skip: injectionSkip}, async () => {
const {binding, calls} = makeFakeBinding();
winGameCapture.__setBindingForTests(binding);
const capture = new winGameCapture.ScreenCapture({
sourceId: '42',
sourceKind: 'window',
injectionMethod: 'set-windows-hook',
});
capture.on('error', () => {});
await capture.start();
assert.equal(calls[0].injectionMethod, undefined);
});
test('window sourceKind does not forward hook paths', {skip: injectionSkip}, async () => {
test('native start() takes no hook or injection arguments', {skip: injectionSkip}, async () => {
const {binding, calls} = makeFakeBinding();
winGameCapture.__setBindingForTests(binding);
const capture = new winGameCapture.ScreenCapture({
@@ -445,27 +313,17 @@ describe('win-game-capture loader wrapper -- injected fake binding', () => {
});
capture.on('error', () => {});
await capture.start();
assert.equal(calls.length, 1);
assert.deepEqual(Object.keys(calls[0]), [
'sourceId',
'sourceKind',
'width',
'height',
'frameRate',
'captureId',
'captureOptions',
]);
assert.equal(calls[0].sourceKind, 'window');
assert.equal(calls[0].hookDllPath, undefined);
assert.equal(calls[0].hookDllPathX86, undefined);
});
test('screen sourceKind does not forward hook paths or the injectionMethod', {skip: injectionSkip}, async () => {
const {binding, calls} = makeFakeBinding();
winGameCapture.__setBindingForTests(binding);
const capture = new winGameCapture.ScreenCapture({
sourceId: 'screen:0:0',
sourceKind: 'screen',
hookDllPath: 'C:/hooks/primary.dll',
hookDllPathX86: 'C:/hooks/x86.dll',
injectionMethod: 'set-windows-hook',
});
capture.on('error', () => {});
await capture.start();
assert.equal(calls[0].sourceKind, 'screen');
assert.equal(calls[0].hookDllPath, undefined);
assert.equal(calls[0].hookDllPathX86, undefined);
assert.equal(calls[0].injectionMethod, undefined);
});
test('getDiagnostics() exposes native start option state', {skip: injectionSkip}, () => {
@@ -580,8 +438,6 @@ describe('win-game-capture loader wrapper -- injected fake binding', () => {
const capture = new winGameCapture.ScreenCapture({
sourceId: '7',
sourceKind: 'game',
hookDllPath: '',
hookDllPathX86: '',
});
capture.on('error', () => {});
await capture.start();
@@ -739,8 +595,7 @@ describe('win-game-capture loader wrapper -- injected fake binding', () => {
test('getDiagnostics() surfaces the activeStrategy + lastFallbackReason fields', {skip: injectionSkip}, () => {
const {binding, diagnostics} = makeFakeBinding();
diagnostics.activeStrategy = 'dxgi-duplication';
diagnostics.lastFallbackReason =
'game-hook capture could not inject its capture hook; switching to dxgi-duplication capture';
diagnostics.lastFallbackReason = 'wgc capture stopped delivering frames; switching to dxgi-duplication capture';
winGameCapture.__setBindingForTests(binding);
const capture = new winGameCapture.ScreenCapture({sourceId: '1'});
const snapshot = capture.getDiagnostics();
@@ -788,7 +643,7 @@ describe('win-game-capture loader wrapper -- injected fake binding', () => {
capture.on('error', (err) => errors.push(err));
natives[0].lifecycleCallback([
'error',
'fallback: game-hook -> dxgi-duplication (game-hook capture could not inject its capture hook) [next-strategy=dxgi-duplication]',
'fallback: wgc -> dxgi-duplication (wgc capture stopped delivering frames) [next-strategy=dxgi-duplication]',
]);
assert.equal(errors.length, 1);
assert.ok(errors[0] instanceof Error);
@@ -824,7 +679,7 @@ describe('win-game-capture loader wrapper -- parseFallbackRecommendation', () =>
test('extracts the recommended strategy from a transition error message', () => {
assert.equal(
winGameCapture.parseFallbackRecommendation(
'fallback: game-hook -> window-gdi (reason) [next-strategy=window-gdi]',
'fallback: dxgi-duplication -> window-gdi (reason) [next-strategy=window-gdi]',
),
'window-gdi',
);
@@ -853,7 +708,7 @@ describe('win-game-capture loader wrapper -- parseFallbackRecommendation', () =>
assert.equal(winGameCapture.parseFallbackRecommendation('[next-strategy=wgc]'), 'wgc');
assert.equal(
winGameCapture.parseFallbackRecommendation(
'fallback: game-hook -> wgc (game-hook capture could not inject its capture hook) [next-strategy=wgc]',
'fallback: dxgi-duplication -> wgc (dxgi-duplication capture stopped delivering frames) [next-strategy=wgc]',
),
'wgc',
);