2014-11-10 15:04:40 -08:00
|
|
|
#include <d3d10_1.h>
|
|
|
|
|
#include <d3d11.h>
|
2017-06-19 15:45:00 -07:00
|
|
|
#include <dxgi1_2.h>
|
2014-11-10 15:04:40 -08:00
|
|
|
#include <d3dcompiler.h>
|
2021-05-15 20:29:25 -07:00
|
|
|
#include <inttypes.h>
|
2014-11-10 15:04:40 -08:00
|
|
|
|
|
|
|
|
#include "graphics-hook.h"
|
2021-05-18 00:17:52 -07:00
|
|
|
|
|
|
|
|
#include <detours.h>
|
2014-11-10 15:04:40 -08:00
|
|
|
|
2016-10-29 08:25:08 -07:00
|
|
|
#if COMPILE_D3D12_HOOK
|
|
|
|
|
#include <d3d12.h>
|
|
|
|
|
#endif
|
|
|
|
|
|
2020-09-09 22:35:11 -07:00
|
|
|
typedef ULONG(STDMETHODCALLTYPE *release_t)(IUnknown *);
|
2015-02-14 07:34:55 -08:00
|
|
|
typedef HRESULT(STDMETHODCALLTYPE *resize_buffers_t)(IDXGISwapChain *, UINT,
|
|
|
|
|
UINT, UINT, DXGI_FORMAT,
|
|
|
|
|
UINT);
|
|
|
|
|
typedef HRESULT(STDMETHODCALLTYPE *present_t)(IDXGISwapChain *, UINT, UINT);
|
2017-06-19 15:45:00 -07:00
|
|
|
typedef HRESULT(STDMETHODCALLTYPE *present1_t)(IDXGISwapChain1 *, UINT, UINT,
|
|
|
|
|
const DXGI_PRESENT_PARAMETERS *);
|
2014-11-10 15:04:40 -08:00
|
|
|
|
2021-05-18 00:17:52 -07:00
|
|
|
release_t RealRelease = nullptr;
|
|
|
|
|
resize_buffers_t RealResizeBuffers = nullptr;
|
|
|
|
|
present_t RealPresent = nullptr;
|
|
|
|
|
present1_t RealPresent1 = nullptr;
|
2014-11-10 15:04:40 -08:00
|
|
|
|
2022-07-29 21:04:15 -07:00
|
|
|
thread_local int dxgi_presenting = 0;
|
2021-05-16 01:09:30 -07:00
|
|
|
struct ID3D12CommandQueue *dxgi_possible_swap_queues[8]{};
|
|
|
|
|
size_t dxgi_possible_swap_queue_count;
|
2021-01-11 02:12:25 -08:00
|
|
|
bool dxgi_present_attempted = false;
|
|
|
|
|
|
2014-11-10 15:04:40 -08:00
|
|
|
struct dxgi_swap_data {
|
|
|
|
|
IDXGISwapChain *swap;
|
2021-06-23 16:34:37 +02:00
|
|
|
void (*capture)(void *, void *);
|
2014-11-10 15:04:40 -08:00
|
|
|
void (*free)(void);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
static struct dxgi_swap_data data = {};
|
2021-05-15 20:16:04 -07:00
|
|
|
static int swap_chain_mismatch_count = 0;
|
|
|
|
|
constexpr int swap_chain_mismtach_limit = 16;
|
2014-11-10 15:04:40 -08:00
|
|
|
|
|
|
|
|
static bool setup_dxgi(IDXGISwapChain *swap)
|
|
|
|
|
{
|
|
|
|
|
IUnknown *device;
|
|
|
|
|
HRESULT hr;
|
|
|
|
|
|
win-capture/graphics-hook: Fix D3D10/D3D11 detection
When hooking DXGI-based graphics programs in the hook, the first hook
point is IDXGISwapChain::Present. To be able to initiate a capture, a
pointer to the device context that created the swap chain is required,
which can be retrieved via IDXGISwapChain::GetDevice. Determining
whether the device context was D3D10 or D3D11 has always been somewhat
of an issue due to D3D10 and D3D11 being nearly identical, as well as
their interoperability/interchangeability. The GetDevice function would
first be called with the UUID of ID3D10Device, then if that failed,
the UUID of ID3D11Device.
However, with certain specific D3D11 games, GetDevice would for some
unknown reason succeed with the UUID of ID3D10Device, which would cause
capture to fail. (Conversely, attempting to call GetDevice with the
UUID of ID3D11Device on a device that's actually ID3D10Device would
always succeed, so reversing the order of the test was not an option).
There were originally three known D3D11 games that would erroneously
succeed when querying a D3D10 device interface: Call of Duty: Ghosts,
Just Cause 3, and theHunter: Call of the Wild. All other known D3D11
games would work correctly. Because it was only these three games, a
hack was originally implemented in the form of an executable exception
list that would force them to capture D3D11.
Unfortunately, Oculus games are now failing under the same circumstance
as well, so a simple hack will no longer work. To fix this, a more
reliable method of detecting which context it is had to be discovered:
simply check the feature level using ID3D11Device::GetFeatureLevel. If
the feature level is D3D_FEATURE_LEVEL_11_0 or D3D_FEATURE_LEVEL_11_1,
the device is definitely a D3D11 device. Otherwise, continue the tests
as they were before. Successfully tested with many D3D10 games, many
D3D11 games, and especially those three D3D11 games that previously were
detected as D3D10 erroneously.
2017-06-14 20:38:33 -07:00
|
|
|
hr = swap->GetDevice(__uuidof(ID3D11Device), (void **)&device);
|
|
|
|
|
if (SUCCEEDED(hr)) {
|
2021-05-15 20:07:38 -07:00
|
|
|
ID3D11Device *d3d11 = static_cast<ID3D11Device *>(device);
|
win-capture/graphics-hook: Fix D3D10/D3D11 detection
When hooking DXGI-based graphics programs in the hook, the first hook
point is IDXGISwapChain::Present. To be able to initiate a capture, a
pointer to the device context that created the swap chain is required,
which can be retrieved via IDXGISwapChain::GetDevice. Determining
whether the device context was D3D10 or D3D11 has always been somewhat
of an issue due to D3D10 and D3D11 being nearly identical, as well as
their interoperability/interchangeability. The GetDevice function would
first be called with the UUID of ID3D10Device, then if that failed,
the UUID of ID3D11Device.
However, with certain specific D3D11 games, GetDevice would for some
unknown reason succeed with the UUID of ID3D10Device, which would cause
capture to fail. (Conversely, attempting to call GetDevice with the
UUID of ID3D11Device on a device that's actually ID3D10Device would
always succeed, so reversing the order of the test was not an option).
There were originally three known D3D11 games that would erroneously
succeed when querying a D3D10 device interface: Call of Duty: Ghosts,
Just Cause 3, and theHunter: Call of the Wild. All other known D3D11
games would work correctly. Because it was only these three games, a
hack was originally implemented in the form of an executable exception
list that would force them to capture D3D11.
Unfortunately, Oculus games are now failing under the same circumstance
as well, so a simple hack will no longer work. To fix this, a more
reliable method of detecting which context it is had to be discovered:
simply check the feature level using ID3D11Device::GetFeatureLevel. If
the feature level is D3D_FEATURE_LEVEL_11_0 or D3D_FEATURE_LEVEL_11_1,
the device is definitely a D3D11 device. Otherwise, continue the tests
as they were before. Successfully tested with many D3D10 games, many
D3D11 games, and especially those three D3D11 games that previously were
detected as D3D10 erroneously.
2017-06-14 20:38:33 -07:00
|
|
|
D3D_FEATURE_LEVEL level = d3d11->GetFeatureLevel();
|
|
|
|
|
device->Release();
|
2014-11-10 15:04:40 -08:00
|
|
|
|
win-capture/graphics-hook: Fix D3D10/D3D11 detection
When hooking DXGI-based graphics programs in the hook, the first hook
point is IDXGISwapChain::Present. To be able to initiate a capture, a
pointer to the device context that created the swap chain is required,
which can be retrieved via IDXGISwapChain::GetDevice. Determining
whether the device context was D3D10 or D3D11 has always been somewhat
of an issue due to D3D10 and D3D11 being nearly identical, as well as
their interoperability/interchangeability. The GetDevice function would
first be called with the UUID of ID3D10Device, then if that failed,
the UUID of ID3D11Device.
However, with certain specific D3D11 games, GetDevice would for some
unknown reason succeed with the UUID of ID3D10Device, which would cause
capture to fail. (Conversely, attempting to call GetDevice with the
UUID of ID3D11Device on a device that's actually ID3D10Device would
always succeed, so reversing the order of the test was not an option).
There were originally three known D3D11 games that would erroneously
succeed when querying a D3D10 device interface: Call of Duty: Ghosts,
Just Cause 3, and theHunter: Call of the Wild. All other known D3D11
games would work correctly. Because it was only these three games, a
hack was originally implemented in the form of an executable exception
list that would force them to capture D3D11.
Unfortunately, Oculus games are now failing under the same circumstance
as well, so a simple hack will no longer work. To fix this, a more
reliable method of detecting which context it is had to be discovered:
simply check the feature level using ID3D11Device::GetFeatureLevel. If
the feature level is D3D_FEATURE_LEVEL_11_0 or D3D_FEATURE_LEVEL_11_1,
the device is definitely a D3D11 device. Otherwise, continue the tests
as they were before. Successfully tested with many D3D10 games, many
D3D11 games, and especially those three D3D11 games that previously were
detected as D3D10 erroneously.
2017-06-14 20:38:33 -07:00
|
|
|
if (level >= D3D_FEATURE_LEVEL_11_0) {
|
2021-05-15 20:29:25 -07:00
|
|
|
hlog("Found D3D11 11.0 device on swap chain");
|
|
|
|
|
|
2014-11-10 15:04:40 -08:00
|
|
|
data.swap = swap;
|
win-capture/graphics-hook: Fix D3D10/D3D11 detection
When hooking DXGI-based graphics programs in the hook, the first hook
point is IDXGISwapChain::Present. To be able to initiate a capture, a
pointer to the device context that created the swap chain is required,
which can be retrieved via IDXGISwapChain::GetDevice. Determining
whether the device context was D3D10 or D3D11 has always been somewhat
of an issue due to D3D10 and D3D11 being nearly identical, as well as
their interoperability/interchangeability. The GetDevice function would
first be called with the UUID of ID3D10Device, then if that failed,
the UUID of ID3D11Device.
However, with certain specific D3D11 games, GetDevice would for some
unknown reason succeed with the UUID of ID3D10Device, which would cause
capture to fail. (Conversely, attempting to call GetDevice with the
UUID of ID3D11Device on a device that's actually ID3D10Device would
always succeed, so reversing the order of the test was not an option).
There were originally three known D3D11 games that would erroneously
succeed when querying a D3D10 device interface: Call of Duty: Ghosts,
Just Cause 3, and theHunter: Call of the Wild. All other known D3D11
games would work correctly. Because it was only these three games, a
hack was originally implemented in the form of an executable exception
list that would force them to capture D3D11.
Unfortunately, Oculus games are now failing under the same circumstance
as well, so a simple hack will no longer work. To fix this, a more
reliable method of detecting which context it is had to be discovered:
simply check the feature level using ID3D11Device::GetFeatureLevel. If
the feature level is D3D_FEATURE_LEVEL_11_0 or D3D_FEATURE_LEVEL_11_1,
the device is definitely a D3D11 device. Otherwise, continue the tests
as they were before. Successfully tested with many D3D10 games, many
D3D11 games, and especially those three D3D11 games that previously were
detected as D3D10 erroneously.
2017-06-14 20:38:33 -07:00
|
|
|
data.capture = d3d11_capture;
|
|
|
|
|
data.free = d3d11_free;
|
2014-11-10 15:04:40 -08:00
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
win-capture/graphics-hook: Fix D3D10/D3D11 detection
When hooking DXGI-based graphics programs in the hook, the first hook
point is IDXGISwapChain::Present. To be able to initiate a capture, a
pointer to the device context that created the swap chain is required,
which can be retrieved via IDXGISwapChain::GetDevice. Determining
whether the device context was D3D10 or D3D11 has always been somewhat
of an issue due to D3D10 and D3D11 being nearly identical, as well as
their interoperability/interchangeability. The GetDevice function would
first be called with the UUID of ID3D10Device, then if that failed,
the UUID of ID3D11Device.
However, with certain specific D3D11 games, GetDevice would for some
unknown reason succeed with the UUID of ID3D10Device, which would cause
capture to fail. (Conversely, attempting to call GetDevice with the
UUID of ID3D11Device on a device that's actually ID3D10Device would
always succeed, so reversing the order of the test was not an option).
There were originally three known D3D11 games that would erroneously
succeed when querying a D3D10 device interface: Call of Duty: Ghosts,
Just Cause 3, and theHunter: Call of the Wild. All other known D3D11
games would work correctly. Because it was only these three games, a
hack was originally implemented in the form of an executable exception
list that would force them to capture D3D11.
Unfortunately, Oculus games are now failing under the same circumstance
as well, so a simple hack will no longer work. To fix this, a more
reliable method of detecting which context it is had to be discovered:
simply check the feature level using ID3D11Device::GetFeatureLevel. If
the feature level is D3D_FEATURE_LEVEL_11_0 or D3D_FEATURE_LEVEL_11_1,
the device is definitely a D3D11 device. Otherwise, continue the tests
as they were before. Successfully tested with many D3D10 games, many
D3D11 games, and especially those three D3D11 games that previously were
detected as D3D10 erroneously.
2017-06-14 20:38:33 -07:00
|
|
|
hr = swap->GetDevice(__uuidof(ID3D10Device), (void **)&device);
|
|
|
|
|
if (SUCCEEDED(hr)) {
|
2021-05-15 20:07:38 -07:00
|
|
|
device->Release();
|
|
|
|
|
|
2021-05-15 20:29:25 -07:00
|
|
|
hlog("Found D3D10 device on swap chain");
|
|
|
|
|
|
win-capture/graphics-hook: Fix D3D10/D3D11 detection
When hooking DXGI-based graphics programs in the hook, the first hook
point is IDXGISwapChain::Present. To be able to initiate a capture, a
pointer to the device context that created the swap chain is required,
which can be retrieved via IDXGISwapChain::GetDevice. Determining
whether the device context was D3D10 or D3D11 has always been somewhat
of an issue due to D3D10 and D3D11 being nearly identical, as well as
their interoperability/interchangeability. The GetDevice function would
first be called with the UUID of ID3D10Device, then if that failed,
the UUID of ID3D11Device.
However, with certain specific D3D11 games, GetDevice would for some
unknown reason succeed with the UUID of ID3D10Device, which would cause
capture to fail. (Conversely, attempting to call GetDevice with the
UUID of ID3D11Device on a device that's actually ID3D10Device would
always succeed, so reversing the order of the test was not an option).
There were originally three known D3D11 games that would erroneously
succeed when querying a D3D10 device interface: Call of Duty: Ghosts,
Just Cause 3, and theHunter: Call of the Wild. All other known D3D11
games would work correctly. Because it was only these three games, a
hack was originally implemented in the form of an executable exception
list that would force them to capture D3D11.
Unfortunately, Oculus games are now failing under the same circumstance
as well, so a simple hack will no longer work. To fix this, a more
reliable method of detecting which context it is had to be discovered:
simply check the feature level using ID3D11Device::GetFeatureLevel. If
the feature level is D3D_FEATURE_LEVEL_11_0 or D3D_FEATURE_LEVEL_11_1,
the device is definitely a D3D11 device. Otherwise, continue the tests
as they were before. Successfully tested with many D3D10 games, many
D3D11 games, and especially those three D3D11 games that previously were
detected as D3D10 erroneously.
2017-06-14 20:38:33 -07:00
|
|
|
data.swap = swap;
|
|
|
|
|
data.capture = d3d10_capture;
|
|
|
|
|
data.free = d3d10_free;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2014-11-10 15:04:40 -08:00
|
|
|
hr = swap->GetDevice(__uuidof(ID3D11Device), (void **)&device);
|
|
|
|
|
if (SUCCEEDED(hr)) {
|
2021-05-15 20:07:38 -07:00
|
|
|
device->Release();
|
|
|
|
|
|
2021-05-15 20:29:25 -07:00
|
|
|
hlog("Found D3D11 device on swap chain");
|
|
|
|
|
|
2014-11-10 15:04:40 -08:00
|
|
|
data.swap = swap;
|
|
|
|
|
data.capture = d3d11_capture;
|
|
|
|
|
data.free = d3d11_free;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2016-10-29 08:25:08 -07:00
|
|
|
#if COMPILE_D3D12_HOOK
|
|
|
|
|
hr = swap->GetDevice(__uuidof(ID3D12Device), (void **)&device);
|
|
|
|
|
if (SUCCEEDED(hr)) {
|
2021-05-15 20:07:38 -07:00
|
|
|
device->Release();
|
|
|
|
|
|
2021-05-15 20:29:25 -07:00
|
|
|
hlog("Found D3D12 device on swap chain: swap=0x%" PRIX64
|
2021-05-18 12:20:00 -07:00
|
|
|
", device=0x%" PRIX64,
|
|
|
|
|
(uint64_t)(uintptr_t)swap, (uint64_t)(uintptr_t)device);
|
2021-05-16 01:09:30 -07:00
|
|
|
for (size_t i = 0; i < dxgi_possible_swap_queue_count; ++i) {
|
2021-05-18 12:20:00 -07:00
|
|
|
hlog(" queue=0x%" PRIX64,
|
2021-05-16 01:09:30 -07:00
|
|
|
(uint64_t)(uintptr_t)dxgi_possible_swap_queues[i]);
|
|
|
|
|
}
|
2021-05-15 20:29:25 -07:00
|
|
|
|
2021-05-18 12:20:00 -07:00
|
|
|
if (dxgi_possible_swap_queue_count > 0) {
|
2021-01-11 02:12:25 -08:00
|
|
|
data.swap = swap;
|
|
|
|
|
data.capture = d3d12_capture;
|
|
|
|
|
data.free = d3d12_free;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
2016-10-29 08:25:08 -07:00
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
|
2021-05-15 20:29:25 -07:00
|
|
|
hlog_verbose("Failed to setup DXGI");
|
2014-11-10 15:04:40 -08:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2020-09-09 22:35:11 -07:00
|
|
|
static ULONG STDMETHODCALLTYPE hook_release(IUnknown *unknown)
|
|
|
|
|
{
|
2021-05-18 00:17:52 -07:00
|
|
|
const ULONG refs = RealRelease(unknown);
|
2020-09-09 22:35:11 -07:00
|
|
|
|
2021-05-15 20:29:25 -07:00
|
|
|
hlog_verbose("Release callback: Refs=%lu", refs);
|
2020-09-09 22:35:11 -07:00
|
|
|
if (unknown == data.swap && refs == 0) {
|
2021-05-15 20:29:25 -07:00
|
|
|
hlog_verbose("No more refs, so reset capture");
|
|
|
|
|
|
2020-09-09 22:35:11 -07:00
|
|
|
data.swap = nullptr;
|
|
|
|
|
data.capture = nullptr;
|
2021-05-16 01:09:30 -07:00
|
|
|
memset(dxgi_possible_swap_queues, 0,
|
|
|
|
|
sizeof(dxgi_possible_swap_queues));
|
|
|
|
|
dxgi_possible_swap_queue_count = 0;
|
2021-01-11 02:12:25 -08:00
|
|
|
dxgi_present_attempted = false;
|
2021-05-15 20:10:11 -07:00
|
|
|
|
|
|
|
|
data.free();
|
|
|
|
|
data.free = nullptr;
|
2020-09-09 22:35:11 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return refs;
|
|
|
|
|
}
|
|
|
|
|
|
2015-02-14 07:35:53 -08:00
|
|
|
static bool resize_buffers_called = false;
|
|
|
|
|
|
2014-11-10 15:04:40 -08:00
|
|
|
static HRESULT STDMETHODCALLTYPE hook_resize_buffers(IDXGISwapChain *swap,
|
|
|
|
|
UINT buffer_count,
|
|
|
|
|
UINT width, UINT height,
|
|
|
|
|
DXGI_FORMAT format,
|
|
|
|
|
UINT flags)
|
|
|
|
|
{
|
2021-05-15 20:29:25 -07:00
|
|
|
hlog_verbose("ResizeBuffers callback");
|
|
|
|
|
|
2014-11-10 15:04:40 -08:00
|
|
|
data.swap = nullptr;
|
|
|
|
|
data.capture = nullptr;
|
2021-05-16 01:09:30 -07:00
|
|
|
memset(dxgi_possible_swap_queues, 0, sizeof(dxgi_possible_swap_queues));
|
|
|
|
|
dxgi_possible_swap_queue_count = 0;
|
2021-01-11 02:12:25 -08:00
|
|
|
dxgi_present_attempted = false;
|
2014-11-10 15:04:40 -08:00
|
|
|
|
2021-05-15 20:10:11 -07:00
|
|
|
if (data.free)
|
|
|
|
|
data.free();
|
|
|
|
|
data.free = nullptr;
|
|
|
|
|
|
2021-05-18 00:17:52 -07:00
|
|
|
const HRESULT hr = RealResizeBuffers(swap, buffer_count, width, height,
|
|
|
|
|
format, flags);
|
2014-11-10 15:04:40 -08:00
|
|
|
|
2015-02-14 07:35:53 -08:00
|
|
|
resize_buffers_called = true;
|
|
|
|
|
|
2014-11-10 15:04:40 -08:00
|
|
|
return hr;
|
|
|
|
|
}
|
|
|
|
|
|
2016-10-29 08:23:44 -07:00
|
|
|
static inline IUnknown *get_dxgi_backbuffer(IDXGISwapChain *swap)
|
2014-11-10 15:04:40 -08:00
|
|
|
{
|
2021-05-15 20:15:17 -07:00
|
|
|
IUnknown *res = nullptr;
|
2014-11-10 15:04:40 -08:00
|
|
|
|
2021-05-15 20:15:17 -07:00
|
|
|
const HRESULT hr = swap->GetBuffer(0, IID_PPV_ARGS(&res));
|
2014-11-10 15:04:40 -08:00
|
|
|
if (FAILED(hr))
|
|
|
|
|
hlog_hr("get_dxgi_backbuffer: GetBuffer failed", hr);
|
|
|
|
|
|
|
|
|
|
return res;
|
|
|
|
|
}
|
|
|
|
|
|
2021-05-15 20:16:04 -07:00
|
|
|
static void update_mismatch_count(bool match)
|
|
|
|
|
{
|
|
|
|
|
if (match) {
|
|
|
|
|
swap_chain_mismatch_count = 0;
|
|
|
|
|
} else {
|
|
|
|
|
++swap_chain_mismatch_count;
|
|
|
|
|
|
|
|
|
|
if (swap_chain_mismatch_count == swap_chain_mismtach_limit) {
|
|
|
|
|
data.swap = nullptr;
|
|
|
|
|
data.capture = nullptr;
|
2021-05-16 01:09:30 -07:00
|
|
|
memset(dxgi_possible_swap_queues, 0,
|
|
|
|
|
sizeof(dxgi_possible_swap_queues));
|
|
|
|
|
dxgi_possible_swap_queue_count = 0;
|
2021-05-15 20:16:04 -07:00
|
|
|
dxgi_present_attempted = false;
|
|
|
|
|
|
|
|
|
|
data.free();
|
|
|
|
|
data.free = nullptr;
|
|
|
|
|
|
|
|
|
|
swap_chain_mismatch_count = 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2014-11-10 15:04:40 -08:00
|
|
|
static HRESULT STDMETHODCALLTYPE hook_present(IDXGISwapChain *swap,
|
|
|
|
|
UINT sync_interval, UINT flags)
|
|
|
|
|
{
|
2022-07-10 20:13:44 -03:00
|
|
|
if (should_passthrough()) {
|
|
|
|
|
dxgi_presenting = true;
|
|
|
|
|
const HRESULT hr = RealPresent(swap, sync_interval, flags);
|
|
|
|
|
dxgi_presenting = false;
|
|
|
|
|
return hr;
|
|
|
|
|
}
|
|
|
|
|
|
2021-05-15 20:15:17 -07:00
|
|
|
const bool capture_overlay = global_hook_info->capture_overlay;
|
|
|
|
|
const bool test_draw = (flags & DXGI_PRESENT_TEST) != 0;
|
2014-11-10 15:04:40 -08:00
|
|
|
|
2021-05-15 20:16:04 -07:00
|
|
|
if (data.swap) {
|
|
|
|
|
update_mismatch_count(swap == data.swap);
|
|
|
|
|
}
|
|
|
|
|
|
2014-11-10 15:04:40 -08:00
|
|
|
if (!data.swap && !capture_active()) {
|
|
|
|
|
setup_dxgi(swap);
|
|
|
|
|
}
|
|
|
|
|
|
2021-05-15 20:29:25 -07:00
|
|
|
hlog_verbose(
|
|
|
|
|
"Present callback: sync_interval=%u, flags=%u, current_swap=0x%" PRIX64
|
|
|
|
|
", expected_swap=0x%" PRIX64,
|
|
|
|
|
sync_interval, flags, swap, data.swap);
|
2021-05-15 20:15:17 -07:00
|
|
|
const bool capture = !test_draw && swap == data.swap && data.capture;
|
2015-02-14 07:35:53 -08:00
|
|
|
if (capture && !capture_overlay) {
|
2021-05-15 20:15:17 -07:00
|
|
|
IUnknown *backbuffer = get_dxgi_backbuffer(swap);
|
2014-11-10 15:04:40 -08:00
|
|
|
|
2021-05-15 20:15:17 -07:00
|
|
|
if (backbuffer) {
|
2021-06-23 16:34:37 +02:00
|
|
|
data.capture(swap, backbuffer);
|
2015-02-14 07:35:53 -08:00
|
|
|
backbuffer->Release();
|
|
|
|
|
}
|
2014-11-10 15:04:40 -08:00
|
|
|
}
|
|
|
|
|
|
2022-07-29 21:04:15 -07:00
|
|
|
++dxgi_presenting;
|
2021-05-18 00:17:52 -07:00
|
|
|
const HRESULT hr = RealPresent(swap, sync_interval, flags);
|
2022-07-29 21:04:15 -07:00
|
|
|
--dxgi_presenting;
|
2021-01-11 02:12:25 -08:00
|
|
|
dxgi_present_attempted = true;
|
2014-11-10 15:04:40 -08:00
|
|
|
|
2015-02-14 07:35:53 -08:00
|
|
|
if (capture && capture_overlay) {
|
|
|
|
|
/*
|
|
|
|
|
* It seems that the first call to Present after ResizeBuffers
|
|
|
|
|
* will cause the backbuffer to be invalidated, so do not
|
|
|
|
|
* perform the post-overlay capture if ResizeBuffers has
|
|
|
|
|
* recently been called. (The backbuffer returned by
|
|
|
|
|
* get_dxgi_backbuffer *will* be invalid otherwise)
|
|
|
|
|
*/
|
|
|
|
|
if (resize_buffers_called) {
|
|
|
|
|
resize_buffers_called = false;
|
|
|
|
|
} else {
|
2021-05-15 20:15:17 -07:00
|
|
|
IUnknown *backbuffer = get_dxgi_backbuffer(swap);
|
2015-02-14 07:35:53 -08:00
|
|
|
|
2021-05-15 20:15:17 -07:00
|
|
|
if (backbuffer) {
|
2021-06-23 16:34:37 +02:00
|
|
|
data.capture(swap, backbuffer);
|
2015-02-14 07:35:53 -08:00
|
|
|
backbuffer->Release();
|
|
|
|
|
}
|
|
|
|
|
}
|
2014-11-10 15:04:40 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return hr;
|
|
|
|
|
}
|
|
|
|
|
|
2017-06-19 15:45:00 -07:00
|
|
|
static HRESULT STDMETHODCALLTYPE
|
|
|
|
|
hook_present1(IDXGISwapChain1 *swap, UINT sync_interval, UINT flags,
|
|
|
|
|
const DXGI_PRESENT_PARAMETERS *params)
|
|
|
|
|
{
|
2022-07-10 20:13:44 -03:00
|
|
|
if (should_passthrough()) {
|
|
|
|
|
dxgi_presenting = true;
|
|
|
|
|
const HRESULT hr =
|
|
|
|
|
RealPresent1(swap, sync_interval, flags, params);
|
|
|
|
|
dxgi_presenting = false;
|
|
|
|
|
return hr;
|
|
|
|
|
}
|
|
|
|
|
|
2021-05-15 20:15:17 -07:00
|
|
|
const bool capture_overlay = global_hook_info->capture_overlay;
|
|
|
|
|
const bool test_draw = (flags & DXGI_PRESENT_TEST) != 0;
|
2017-06-19 15:45:00 -07:00
|
|
|
|
2021-05-15 20:16:04 -07:00
|
|
|
if (data.swap) {
|
|
|
|
|
update_mismatch_count(swap == data.swap);
|
|
|
|
|
}
|
|
|
|
|
|
2017-06-19 15:45:00 -07:00
|
|
|
if (!data.swap && !capture_active()) {
|
|
|
|
|
setup_dxgi(swap);
|
|
|
|
|
}
|
|
|
|
|
|
2021-05-15 20:29:25 -07:00
|
|
|
hlog_verbose(
|
|
|
|
|
"Present1 callback: sync_interval=%u, flags=%u, current_swap=0x%" PRIX64
|
|
|
|
|
", expected_swap=0x%" PRIX64,
|
|
|
|
|
sync_interval, flags, swap, data.swap);
|
|
|
|
|
const bool capture = !test_draw && swap == data.swap && !!data.capture;
|
2017-06-19 15:45:00 -07:00
|
|
|
if (capture && !capture_overlay) {
|
2021-05-15 20:15:17 -07:00
|
|
|
IUnknown *backbuffer = get_dxgi_backbuffer(swap);
|
2017-06-19 15:45:00 -07:00
|
|
|
|
2021-05-15 20:15:17 -07:00
|
|
|
if (backbuffer) {
|
2021-06-23 16:34:37 +02:00
|
|
|
data.capture(swap, backbuffer);
|
2017-06-19 15:45:00 -07:00
|
|
|
backbuffer->Release();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-07-29 21:04:15 -07:00
|
|
|
++dxgi_presenting;
|
2021-05-18 00:17:52 -07:00
|
|
|
const HRESULT hr = RealPresent1(swap, sync_interval, flags, params);
|
2022-07-29 21:04:15 -07:00
|
|
|
--dxgi_presenting;
|
2021-01-11 02:12:25 -08:00
|
|
|
dxgi_present_attempted = true;
|
2017-06-19 15:45:00 -07:00
|
|
|
|
|
|
|
|
if (capture && capture_overlay) {
|
|
|
|
|
if (resize_buffers_called) {
|
|
|
|
|
resize_buffers_called = false;
|
|
|
|
|
} else {
|
2021-05-15 20:15:17 -07:00
|
|
|
IUnknown *backbuffer = get_dxgi_backbuffer(swap);
|
2017-06-19 15:45:00 -07:00
|
|
|
|
2021-05-15 20:15:17 -07:00
|
|
|
if (backbuffer) {
|
2021-06-23 16:34:37 +02:00
|
|
|
data.capture(swap, backbuffer);
|
2017-06-19 15:45:00 -07:00
|
|
|
backbuffer->Release();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return hr;
|
|
|
|
|
}
|
|
|
|
|
|
2014-11-10 15:04:40 -08:00
|
|
|
bool hook_dxgi(void)
|
|
|
|
|
{
|
|
|
|
|
HMODULE dxgi_module = get_system_module("dxgi.dll");
|
|
|
|
|
if (!dxgi_module) {
|
2021-05-15 20:29:25 -07:00
|
|
|
hlog_verbose("Failed to find dxgi.dll. Skipping hook attempt.");
|
2014-11-10 15:04:40 -08:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* ---------------------- */
|
|
|
|
|
|
2021-05-15 20:15:17 -07:00
|
|
|
void *present_addr = get_offset_addr(
|
|
|
|
|
dxgi_module, global_hook_info->offsets.dxgi.present);
|
|
|
|
|
void *resize_addr = get_offset_addr(
|
|
|
|
|
dxgi_module, global_hook_info->offsets.dxgi.resize);
|
|
|
|
|
void *present1_addr = nullptr;
|
2017-06-19 15:45:00 -07:00
|
|
|
if (global_hook_info->offsets.dxgi.present1)
|
|
|
|
|
present1_addr = get_offset_addr(
|
|
|
|
|
dxgi_module, global_hook_info->offsets.dxgi.present1);
|
2021-05-15 20:15:17 -07:00
|
|
|
void *release_addr = nullptr;
|
2020-09-09 22:35:11 -07:00
|
|
|
if (global_hook_info->offsets.dxgi2.release)
|
|
|
|
|
release_addr = get_offset_addr(
|
|
|
|
|
dxgi_module, global_hook_info->offsets.dxgi2.release);
|
2014-11-10 15:04:40 -08:00
|
|
|
|
2021-05-18 00:17:52 -07:00
|
|
|
DetourTransactionBegin();
|
|
|
|
|
|
|
|
|
|
RealPresent = (present_t)present_addr;
|
|
|
|
|
DetourAttach(&(PVOID &)RealPresent, hook_present);
|
|
|
|
|
|
|
|
|
|
RealResizeBuffers = (resize_buffers_t)resize_addr;
|
|
|
|
|
DetourAttach(&(PVOID &)RealResizeBuffers, hook_resize_buffers);
|
|
|
|
|
|
2021-05-15 20:29:25 -07:00
|
|
|
if (present1_addr) {
|
2021-05-18 00:17:52 -07:00
|
|
|
RealPresent1 = (present1_t)present1_addr;
|
|
|
|
|
DetourAttach(&(PVOID &)RealPresent1, hook_present1);
|
2021-05-15 20:29:25 -07:00
|
|
|
}
|
2021-05-18 00:17:52 -07:00
|
|
|
|
2021-05-15 20:29:25 -07:00
|
|
|
if (release_addr) {
|
2021-05-18 00:17:52 -07:00
|
|
|
RealRelease = (release_t)release_addr;
|
|
|
|
|
DetourAttach(&(PVOID &)RealRelease, hook_release);
|
2021-05-15 20:29:25 -07:00
|
|
|
}
|
2014-11-10 15:04:40 -08:00
|
|
|
|
2021-05-18 00:17:52 -07:00
|
|
|
const LONG error = DetourTransactionCommit();
|
|
|
|
|
const bool success = error == NO_ERROR;
|
|
|
|
|
if (success) {
|
|
|
|
|
hlog("Hooked IDXGISwapChain::Present");
|
|
|
|
|
hlog("Hooked IDXGISwapChain::ResizeBuffers");
|
|
|
|
|
if (RealPresent1)
|
|
|
|
|
hlog("Hooked IDXGISwapChain1::Present1");
|
|
|
|
|
if (RealRelease)
|
|
|
|
|
hlog("Hooked IDXGISwapChain::Release");
|
|
|
|
|
hlog("Hooked DXGI");
|
|
|
|
|
} else {
|
|
|
|
|
RealPresent = nullptr;
|
|
|
|
|
RealResizeBuffers = nullptr;
|
|
|
|
|
RealPresent1 = nullptr;
|
|
|
|
|
RealRelease = nullptr;
|
|
|
|
|
hlog("Failed to attach Detours hook: %ld", error);
|
|
|
|
|
}
|
2014-11-10 15:04:40 -08:00
|
|
|
|
2021-05-18 00:17:52 -07:00
|
|
|
return success;
|
2014-11-10 15:04:40 -08:00
|
|
|
}
|