win-capture: Fix D3D leaks on swap chain release

For game capture, hook DXGI release function to release D3D objects if
the related swap chain is also being destroyed.

An added bonus is that the game capture hook will handle swap chain
recreation for applications that don't use ResizeBuffers.
master
jpark37 2020-09-09 22:35:11 -07:00 committed by Jim
parent 19cb3b9463
commit dad861b036
6 changed files with 47 additions and 4 deletions

View File

@ -102,7 +102,8 @@ static inline void dxgi_free(dxgi_info &info)
DestroyWindow(info.hwnd); DestroyWindow(info.hwnd);
} }
void get_dxgi_offsets(struct dxgi_offsets *offsets) void get_dxgi_offsets(struct dxgi_offsets *offsets,
struct dxgi_offsets2 *offsets2)
{ {
dxgi_info info = {}; dxgi_info info = {};
bool success = dxgi_init(info); bool success = dxgi_init(info);
@ -120,6 +121,8 @@ void get_dxgi_offsets(struct dxgi_offsets *offsets)
vtable_offset(info.module, swap1, 22); vtable_offset(info.module, swap1, 22);
swap1->Release(); swap1->Release();
} }
offsets2->release = vtable_offset(info.module, info.swap, 2);
} }
dxgi_free(info); dxgi_free(info);

View File

@ -8,6 +8,7 @@ int main(int argc, char *argv[])
struct d3d8_offsets d3d8 = {0}; struct d3d8_offsets d3d8 = {0};
struct d3d9_offsets d3d9 = {0}; struct d3d9_offsets d3d9 = {0};
struct dxgi_offsets dxgi = {0}; struct dxgi_offsets dxgi = {0};
struct dxgi_offsets2 dxgi2 = {0};
WNDCLASSA wc = {0}; WNDCLASSA wc = {0};
wc.style = CS_OWNDC; wc.style = CS_OWNDC;
@ -24,7 +25,7 @@ int main(int argc, char *argv[])
get_d3d9_offsets(&d3d9); get_d3d9_offsets(&d3d9);
get_d3d8_offsets(&d3d8); get_d3d8_offsets(&d3d8);
get_dxgi_offsets(&dxgi); get_dxgi_offsets(&dxgi, &dxgi2);
printf("[d3d8]\n"); printf("[d3d8]\n");
printf("present=0x%" PRIx32 "\n", d3d8.present); printf("present=0x%" PRIx32 "\n", d3d8.present);
@ -38,6 +39,7 @@ int main(int argc, char *argv[])
printf("present=0x%" PRIx32 "\n", dxgi.present); printf("present=0x%" PRIx32 "\n", dxgi.present);
printf("present1=0x%" PRIx32 "\n", dxgi.present1); printf("present1=0x%" PRIx32 "\n", dxgi.present1);
printf("resize=0x%" PRIx32 "\n", dxgi.resize); printf("resize=0x%" PRIx32 "\n", dxgi.resize);
printf("release=0x%" PRIx32 "\n", dxgi2.release);
(void)argc; (void)argc;
(void)argv; (void)argv;

View File

@ -21,7 +21,8 @@ static inline uint32_t vtable_offset(HMODULE module, void *cls,
return (uint32_t)(vtable[offset] - (uintptr_t)module); return (uint32_t)(vtable[offset] - (uintptr_t)module);
} }
extern void get_dxgi_offsets(struct dxgi_offsets *offsets); extern void get_dxgi_offsets(struct dxgi_offsets *offsets,
struct dxgi_offsets2 *offsets2);
extern void get_d3d9_offsets(struct d3d9_offsets *offsets); extern void get_d3d9_offsets(struct d3d9_offsets *offsets);
extern void get_d3d8_offsets(struct d3d8_offsets *offsets); extern void get_d3d8_offsets(struct d3d8_offsets *offsets);

View File

@ -1,5 +1,6 @@
#pragma once #pragma once
#include <assert.h>
#include <stdint.h> #include <stdint.h>
#include <stdbool.h> #include <stdbool.h>
#include <string.h> #include <string.h>
@ -46,6 +47,10 @@ struct dxgi_offsets {
uint32_t present1; uint32_t present1;
}; };
struct dxgi_offsets2 {
uint32_t release;
};
struct ddraw_offsets { struct ddraw_offsets {
uint32_t surface_create; uint32_t surface_create;
uint32_t surface_restore; uint32_t surface_restore;
@ -77,6 +82,7 @@ struct graphics_offsets {
struct d3d9_offsets d3d9; struct d3d9_offsets d3d9;
struct dxgi_offsets dxgi; struct dxgi_offsets dxgi;
struct ddraw_offsets ddraw; struct ddraw_offsets ddraw;
struct dxgi_offsets2 dxgi2;
}; };
struct hook_info { struct hook_info {
@ -106,8 +112,9 @@ struct hook_info {
/* hook addresses */ /* hook addresses */
struct graphics_offsets offsets; struct graphics_offsets offsets;
uint32_t reserved[128]; uint32_t reserved[127];
}; };
static_assert(sizeof(struct hook_info) == 648, "ABI compatibility");
#pragma pack(pop) #pragma pack(pop)

View File

@ -10,6 +10,7 @@
#include <d3d12.h> #include <d3d12.h>
#endif #endif
typedef ULONG(STDMETHODCALLTYPE *release_t)(IUnknown *);
typedef HRESULT(STDMETHODCALLTYPE *resize_buffers_t)(IDXGISwapChain *, UINT, typedef HRESULT(STDMETHODCALLTYPE *resize_buffers_t)(IDXGISwapChain *, UINT,
UINT, UINT, DXGI_FORMAT, UINT, UINT, DXGI_FORMAT,
UINT); UINT);
@ -17,6 +18,7 @@ typedef HRESULT(STDMETHODCALLTYPE *present_t)(IDXGISwapChain *, UINT, UINT);
typedef HRESULT(STDMETHODCALLTYPE *present1_t)(IDXGISwapChain1 *, UINT, UINT, typedef HRESULT(STDMETHODCALLTYPE *present1_t)(IDXGISwapChain1 *, UINT, UINT,
const DXGI_PRESENT_PARAMETERS *); const DXGI_PRESENT_PARAMETERS *);
static struct func_hook release;
static struct func_hook resize_buffers; static struct func_hook resize_buffers;
static struct func_hook present; static struct func_hook present;
static struct func_hook present1; static struct func_hook present1;
@ -80,6 +82,23 @@ static bool setup_dxgi(IDXGISwapChain *swap)
return false; return false;
} }
static ULONG STDMETHODCALLTYPE hook_release(IUnknown *unknown)
{
unhook(&release);
release_t call = (release_t)release.call_addr;
ULONG refs = call(unknown);
rehook(&release);
if (unknown == data.swap && refs == 0) {
data.free();
data.swap = nullptr;
data.free = nullptr;
data.capture = nullptr;
}
return refs;
}
static bool resize_buffers_called = false; static bool resize_buffers_called = false;
static HRESULT STDMETHODCALLTYPE hook_resize_buffers(IDXGISwapChain *swap, static HRESULT STDMETHODCALLTYPE hook_resize_buffers(IDXGISwapChain *swap,
@ -223,6 +242,7 @@ bool hook_dxgi(void)
void *present_addr; void *present_addr;
void *resize_addr; void *resize_addr;
void *present1_addr = nullptr; void *present1_addr = nullptr;
void *release_addr = nullptr;
if (!dxgi_module) { if (!dxgi_module) {
return false; return false;
@ -237,6 +257,9 @@ bool hook_dxgi(void)
if (global_hook_info->offsets.dxgi.present1) if (global_hook_info->offsets.dxgi.present1)
present1_addr = get_offset_addr( present1_addr = get_offset_addr(
dxgi_module, global_hook_info->offsets.dxgi.present1); dxgi_module, global_hook_info->offsets.dxgi.present1);
if (global_hook_info->offsets.dxgi2.release)
release_addr = get_offset_addr(
dxgi_module, global_hook_info->offsets.dxgi2.release);
hook_init(&present, present_addr, (void *)hook_present, hook_init(&present, present_addr, (void *)hook_present,
"IDXGISwapChain::Present"); "IDXGISwapChain::Present");
@ -245,11 +268,16 @@ bool hook_dxgi(void)
if (present1_addr) if (present1_addr)
hook_init(&present1, present1_addr, (void *)hook_present1, hook_init(&present1, present1_addr, (void *)hook_present1,
"IDXGISwapChain1::Present1"); "IDXGISwapChain1::Present1");
if (release_addr)
hook_init(&release, release_addr, (void *)hook_release,
"IDXGISwapChain::Release");
rehook(&resize_buffers); rehook(&resize_buffers);
rehook(&present); rehook(&present);
if (present1_addr) if (present1_addr)
rehook(&present1); rehook(&present1);
if (release_addr)
rehook(&release);
hlog("Hooked DXGI"); hlog("Hooked DXGI");
return true; return true;

View File

@ -40,6 +40,8 @@ static inline bool load_offsets_from_string(struct graphics_offsets *offsets,
(uint32_t)config_get_uint(config, "dxgi", "present1"); (uint32_t)config_get_uint(config, "dxgi", "present1");
offsets->dxgi.resize = offsets->dxgi.resize =
(uint32_t)config_get_uint(config, "dxgi", "resize"); (uint32_t)config_get_uint(config, "dxgi", "resize");
offsets->dxgi2.release =
(uint32_t)config_get_uint(config, "dxgi", "release");
config_close(config); config_close(config);
return true; return true;