kokole dd6dd4e104 win-capture: Fix D3D12 third party overlay capture
Doesn't make sense to grab the oldest frame when not capturing overlays,
because the overlay was already rendered in it. This will grab the
latest frame (before the overlay is rendered).
2021-08-17 08:24:57 -07:00

365 lines
7.8 KiB
C++

#include <d3d10.h>
#include <dxgi.h>
#include "dxgi-helpers.hpp"
#include "graphics-hook.h"
#include "../funchook.h"
struct d3d10_data {
ID3D10Device *device; /* do not release */
uint32_t cx;
uint32_t cy;
DXGI_FORMAT format;
bool using_shtex;
bool multisampled;
ID3D10Texture2D *scale_tex;
ID3D10ShaderResourceView *scale_resource;
ID3D10VertexShader *vertex_shader;
ID3D10InputLayout *vertex_layout;
ID3D10PixelShader *pixel_shader;
ID3D10SamplerState *sampler_state;
ID3D10BlendState *blend_state;
ID3D10DepthStencilState *zstencil_state;
ID3D10RasterizerState *raster_state;
ID3D10Buffer *vertex_buffer;
union {
/* shared texture */
struct {
struct shtex_data *shtex_info;
ID3D10Texture2D *texture;
HANDLE handle;
};
/* shared memory */
struct {
struct shmem_data *shmem_info;
ID3D10Texture2D *copy_surfaces[NUM_BUFFERS];
bool texture_ready[NUM_BUFFERS];
bool texture_mapped[NUM_BUFFERS];
uint32_t pitch;
int cur_tex;
int copy_wait;
};
};
};
static struct d3d10_data data = {};
void d3d10_free(void)
{
if (data.scale_tex)
data.scale_tex->Release();
if (data.scale_resource)
data.scale_resource->Release();
if (data.vertex_shader)
data.vertex_shader->Release();
if (data.vertex_layout)
data.vertex_layout->Release();
if (data.pixel_shader)
data.pixel_shader->Release();
if (data.sampler_state)
data.sampler_state->Release();
if (data.blend_state)
data.blend_state->Release();
if (data.zstencil_state)
data.zstencil_state->Release();
if (data.raster_state)
data.raster_state->Release();
if (data.vertex_buffer)
data.vertex_buffer->Release();
capture_free();
if (data.using_shtex) {
if (data.texture)
data.texture->Release();
} else {
for (size_t i = 0; i < NUM_BUFFERS; i++) {
if (data.copy_surfaces[i]) {
if (data.texture_mapped[i])
data.copy_surfaces[i]->Unmap(0);
data.copy_surfaces[i]->Release();
}
}
}
memset(&data, 0, sizeof(data));
hlog("----------------- d3d10 capture freed ----------------");
}
static bool create_d3d10_stage_surface(ID3D10Texture2D **tex)
{
HRESULT hr;
D3D10_TEXTURE2D_DESC desc = {};
desc.Width = data.cx;
desc.Height = data.cy;
desc.Format = data.format;
desc.MipLevels = 1;
desc.ArraySize = 1;
desc.SampleDesc.Count = 1;
desc.Usage = D3D10_USAGE_STAGING;
desc.CPUAccessFlags = D3D10_CPU_ACCESS_READ;
hr = data.device->CreateTexture2D(&desc, nullptr, tex);
if (FAILED(hr)) {
hlog_hr("create_d3d10_stage_surface: failed to create texture",
hr);
return false;
}
return true;
}
static bool create_d3d10_tex(uint32_t cx, uint32_t cy, ID3D10Texture2D **tex,
HANDLE *handle)
{
HRESULT hr;
D3D10_TEXTURE2D_DESC desc = {};
desc.Width = cx;
desc.Height = cy;
desc.MipLevels = 1;
desc.ArraySize = 1;
desc.Format = apply_dxgi_format_typeless(
data.format, global_hook_info->allow_srgb_alias);
desc.BindFlags = D3D10_BIND_SHADER_RESOURCE;
desc.SampleDesc.Count = 1;
desc.Usage = D3D10_USAGE_DEFAULT;
desc.MiscFlags = D3D10_RESOURCE_MISC_SHARED;
hr = data.device->CreateTexture2D(&desc, nullptr, tex);
if (FAILED(hr)) {
hlog_hr("create_d3d10_tex: failed to create texture", hr);
return false;
}
if (!!handle) {
IDXGIResource *dxgi_res;
hr = (*tex)->QueryInterface(__uuidof(IDXGIResource),
(void **)&dxgi_res);
if (FAILED(hr)) {
hlog_hr("create_d3d10_tex: failed to query "
"IDXGIResource interface from texture",
hr);
return false;
}
hr = dxgi_res->GetSharedHandle(handle);
dxgi_res->Release();
if (FAILED(hr)) {
hlog_hr("create_d3d10_tex: failed to get shared handle",
hr);
return false;
}
}
return true;
}
static inline bool d3d10_init_format(IDXGISwapChain *swap, HWND &window)
{
DXGI_SWAP_CHAIN_DESC desc;
HRESULT hr;
hr = swap->GetDesc(&desc);
if (FAILED(hr)) {
hlog_hr("d3d10_init_format: swap->GetDesc failed", hr);
return false;
}
data.format = strip_dxgi_format_srgb(desc.BufferDesc.Format);
data.multisampled = desc.SampleDesc.Count > 1;
window = desc.OutputWindow;
data.cx = desc.BufferDesc.Width;
data.cy = desc.BufferDesc.Height;
return true;
}
static bool d3d10_shmem_init_buffers(size_t idx)
{
bool success;
success = create_d3d10_stage_surface(&data.copy_surfaces[idx]);
if (!success) {
hlog("d3d10_shmem_init_buffers: failed to create copy surface");
return false;
}
if (idx == 0) {
D3D10_MAPPED_TEXTURE2D map = {};
HRESULT hr;
hr = data.copy_surfaces[idx]->Map(0, D3D10_MAP_READ, 0, &map);
if (FAILED(hr)) {
hlog_hr("d3d10_shmem_init_buffers: failed to get "
"pitch",
hr);
return false;
}
data.pitch = map.RowPitch;
data.copy_surfaces[idx]->Unmap(0);
}
return true;
}
static bool d3d10_shmem_init(HWND window)
{
data.using_shtex = false;
for (size_t i = 0; i < NUM_BUFFERS; i++) {
if (!d3d10_shmem_init_buffers(i)) {
return false;
}
}
if (!capture_init_shmem(&data.shmem_info, window, data.cx, data.cy,
data.pitch, data.format, false)) {
return false;
}
hlog("d3d10 memory capture successful");
return true;
}
static bool d3d10_shtex_init(HWND window)
{
bool success;
data.using_shtex = true;
success =
create_d3d10_tex(data.cx, data.cy, &data.texture, &data.handle);
if (!success) {
hlog("d3d10_shtex_init: failed to create texture");
return false;
}
if (!capture_init_shtex(&data.shtex_info, window, data.cx, data.cy,
data.format, false, (uintptr_t)data.handle)) {
return false;
}
hlog("d3d10 shared texture capture successful");
return true;
}
static void d3d10_init(IDXGISwapChain *swap)
{
HWND window;
HRESULT hr;
hr = swap->GetDevice(__uuidof(ID3D10Device), (void **)&data.device);
if (FAILED(hr)) {
hlog_hr("d3d10_init: failed to get device from swap", hr);
return;
}
/* remove the unneeded extra reference */
data.device->Release();
if (!d3d10_init_format(swap, window)) {
return;
}
const bool success = global_hook_info->force_shmem
? d3d10_shmem_init(window)
: d3d10_shtex_init(window);
if (!success)
d3d10_free();
}
static inline void d3d10_copy_texture(ID3D10Resource *dst, ID3D10Resource *src)
{
if (data.multisampled) {
data.device->ResolveSubresource(dst, 0, src, 0, data.format);
} else {
data.device->CopyResource(dst, src);
}
}
static inline void d3d10_shtex_capture(ID3D10Resource *backbuffer)
{
d3d10_copy_texture(data.texture, backbuffer);
}
static void d3d10_shmem_capture_copy(int i)
{
D3D10_MAPPED_TEXTURE2D map;
HRESULT hr;
if (data.texture_ready[i]) {
data.texture_ready[i] = false;
hr = data.copy_surfaces[i]->Map(0, D3D10_MAP_READ, 0, &map);
if (SUCCEEDED(hr)) {
data.texture_mapped[i] = true;
shmem_copy_data(i, map.pData);
}
}
}
static inline void d3d10_shmem_capture(ID3D10Resource *backbuffer)
{
int next_tex;
next_tex = (data.cur_tex + 1) % NUM_BUFFERS;
d3d10_shmem_capture_copy(next_tex);
if (data.copy_wait < NUM_BUFFERS - 1) {
data.copy_wait++;
} else {
if (shmem_texture_data_lock(data.cur_tex)) {
data.copy_surfaces[data.cur_tex]->Unmap(0);
data.texture_mapped[data.cur_tex] = false;
shmem_texture_data_unlock(data.cur_tex);
}
d3d10_copy_texture(data.copy_surfaces[data.cur_tex],
backbuffer);
data.texture_ready[data.cur_tex] = true;
}
data.cur_tex = next_tex;
}
void d3d10_capture(void *swap_ptr, void *backbuffer_ptr)
{
IDXGIResource *dxgi_backbuffer = (IDXGIResource *)backbuffer_ptr;
IDXGISwapChain *swap = (IDXGISwapChain *)swap_ptr;
HRESULT hr;
if (capture_should_stop()) {
d3d10_free();
}
if (capture_should_init()) {
d3d10_init(swap);
}
if (capture_ready()) {
ID3D10Resource *backbuffer;
hr = dxgi_backbuffer->QueryInterface(__uuidof(ID3D10Resource),
(void **)&backbuffer);
if (FAILED(hr)) {
hlog_hr("d3d10_shtex_capture: failed to get "
"backbuffer",
hr);
return;
}
if (data.using_shtex)
d3d10_shtex_capture(backbuffer);
else
d3d10_shmem_capture(backbuffer);
backbuffer->Release();
}
}