static bool d3d8_shmem_init_buffer(IDirect3DDevice8 *device, int idx) { HRESULT hr; hr = device->CreateImageSurface(data.cx, data.cy, data.d3d8_format, &data.copy_surfaces[idx]); if (FAILED(hr)) { hlog_hr("d3d8_shmem_init_buffer: Failed to create surface", hr); return false; } if (idx == 0) { D3DLOCKED_RECT rect; hr = data.copy_surfaces[0]->LockRect(&rect, nullptr, D3DLOCK_READONLY); if (FAILED(hr)) { hlog_hr("d3d8_shmem_init_buffer: Failed to lock buffer", hr); return false; } data.pitch = rect.Pitch; data.copy_surfaces[0]->UnlockRect(); } return true; }
static bool d3d8_init_format_backbuffer(IDirect3DDevice8 *device) { IDirect3DSurface8 *backbuffer; D3DSURFACE_DESC desc; HRESULT hr; if (!d3d8_get_window_handle(device)) return false; backbuffer = d3d8_get_backbuffer(device); if (!backbuffer) return false; hr = backbuffer->GetDesc(&desc); backbuffer->Release(); if (FAILED(hr)) { hlog_hr("d3d8_init_format_backbuffer: Failed to get " "backbuffer descriptor", hr); return false; } data.d3d8_format = desc.Format; data.dxgi_format = d3d8_to_dxgi_format(desc.Format); data.cx = desc.Width; data.cy = desc.Height; return true; }
static bool d3d9_shmem_init_buffers(size_t buffer) { HRESULT hr; hr = data.device->CreateOffscreenPlainSurface(data.cx, data.cy, data.d3d9_format, D3DPOOL_SYSTEMMEM, &data.copy_surfaces[buffer], nullptr); if (FAILED(hr)) { hlog_hr("d3d9_shmem_init_buffers: Failed to create surface", hr); return false; } if (buffer == 0) { D3DLOCKED_RECT rect; hr = data.copy_surfaces[buffer]->LockRect(&rect, nullptr, D3DLOCK_READONLY); if (FAILED(hr)) { hlog_hr("d3d9_shmem_init_buffers: Failed to lock " "buffer", hr); return false; } data.pitch = rect.Pitch; data.copy_surfaces[buffer]->UnlockRect(); } hr = data.device->CreateRenderTarget(data.cx, data.cy, data.d3d9_format, D3DMULTISAMPLE_NONE, 0, false, &data.render_targets[buffer], nullptr); if (FAILED(hr)) { hlog_hr("d3d9_shmem_init_buffers: Failed to create render " "target", hr); return false; } hr = data.device->CreateQuery(D3DQUERYTYPE_EVENT, &data.queries[buffer]); if (FAILED(hr)) { hlog_hr("d3d9_shmem_init_buffers: Failed to create query", hr); return false; } return true; }
static inline void d3d9_shmem_capture(IDirect3DSurface9 *backbuffer) { D3DTEXTUREFILTERTYPE filter; IDirect3DSurface9 *copy; int next_tex; HRESULT hr; d3d9_shmem_capture_queue_copy(); next_tex = (data.cur_tex == NUM_BUFFERS - 1) ? 0 : data.cur_tex + 1; filter = data.using_scale ? D3DTEXF_LINEAR : D3DTEXF_NONE; copy = data.render_targets[data.cur_tex]; hr = data.device->StretchRect(backbuffer, nullptr, copy, nullptr, filter); if (FAILED(hr)) { hlog_hr("d3d9_shmem_capture: StretchRect failed", hr); return; } if (data.copy_wait < NUM_BUFFERS - 1) { data.copy_wait++; } else { IDirect3DSurface9 *src = data.render_targets[next_tex]; IDirect3DSurface9 *dst = data.copy_surfaces[next_tex]; if (shmem_texture_data_lock(next_tex)) { dst->UnlockRect(); data.texture_mapped[next_tex] = false; shmem_texture_data_unlock(next_tex); } hr = data.device->GetRenderTargetData(src, dst); if (FAILED(hr)) { hlog_hr("d3d9_shmem_capture: GetRenderTargetData " "failed", hr); } data.queries[next_tex]->Issue(D3DISSUE_END); data.issued_queries[next_tex] = true; } data.cur_tex = next_tex; }
static inline bool d3d9_shtex_init_shtex() { IDXGIResource *res; HRESULT hr; D3D11_TEXTURE2D_DESC desc = {}; desc.Width = data.cx; desc.Height = data.cy; desc.Format = data.dxgi_format; desc.MipLevels = 1; desc.ArraySize = 1; desc.SampleDesc.Count = 1; desc.Usage = D3D11_USAGE_DEFAULT; desc.MiscFlags = D3D11_RESOURCE_MISC_SHARED; desc.BindFlags = D3D11_BIND_RENDER_TARGET | D3D11_BIND_SHADER_RESOURCE; hr = data.d3d11_device->CreateTexture2D(&desc, nullptr, (ID3D11Texture2D**)&data.d3d11_tex); if (FAILED(hr)) { hlog_hr("d3d9_shtex_init_shtex: Failed to create D3D11 texture", hr); return false; } hr = data.d3d11_tex->QueryInterface(__uuidof(IDXGIResource), (void**)&res); if (FAILED(hr)) { hlog_hr("d3d9_shtex_init_shtex: Failed to query IDXGIResource", hr); return false; } hr = res->GetSharedHandle(&data.handle); res->Release(); if (FAILED(hr)) { hlog_hr("d3d9_shtex_init_shtex: Failed to get shared handle", hr); return false; } return true; }
static inline bool gl_shtex_init_d3d11_tex(void) { IDXGIResource *dxgi_res; HRESULT hr; D3D11_TEXTURE2D_DESC desc = {0}; desc.Width = data.cx; desc.Height = data.cy; desc.MipLevels = 1; desc.ArraySize = 1; desc.Format = DXGI_FORMAT_B8G8R8A8_UNORM; desc.SampleDesc.Count = 1; desc.Usage = D3D11_USAGE_DEFAULT; desc.MiscFlags = D3D11_RESOURCE_MISC_SHARED; desc.BindFlags = D3D11_BIND_RENDER_TARGET | D3D11_BIND_SHADER_RESOURCE; hr = ID3D11Device_CreateTexture2D(data.d3d11_device, &desc, NULL, &data.d3d11_tex); if (FAILED(hr)) { hlog_hr("gl_shtex_init_d3d11_tex: failed to create texture", hr); return false; } hr = ID3D11Device_QueryInterface(data.d3d11_tex, &GUID_IDXGIResource, (void**)&dxgi_res); if (FAILED(hr)) { hlog_hr("gl_shtex_init_d3d11_tex: failed to get IDXGIResource", hr); return false; } hr = IDXGIResource_GetSharedHandle(dxgi_res, &data.handle); IDXGIResource_Release(dxgi_res); if (FAILED(hr)) { hlog_hr("gl_shtex_init_d3d11_tex: failed to get shared handle", hr); return false; } return true; }
static inline bool d3d9_shtex_init_copytex() { uint8_t *patch_addr = get_d3d9_patch_addr(data.d3d9, data.patch); uint8_t saved_data[MAX_PATCH_SIZE]; size_t patch_size = 0; IDirect3DTexture9 *tex; DWORD protect_val; HRESULT hr; if (patch_addr) { patch_size = patch[data.patch].size; VirtualProtect(patch_addr, patch_size, PAGE_EXECUTE_READWRITE, &protect_val); memcpy(saved_data, patch_addr, patch_size); memcpy(patch_addr, patch[data.patch].data, patch_size); } hr = data.device->CreateTexture(data.cx, data.cy, 1, D3DUSAGE_RENDERTARGET, data.d3d9_format, D3DPOOL_DEFAULT, &tex, &data.handle); if (patch_addr && patch_size) { memcpy(patch_addr, saved_data, patch_size); VirtualProtect(patch_addr, patch_size, protect_val, &protect_val); } if (FAILED(hr)) { hlog_hr("d3d9_shtex_init_copytex: Failed to create shared texture", hr); return false; } hr = tex->GetSurfaceLevel(0, &data.d3d9_copytex); tex->Release(); if (FAILED(hr)) { hlog_hr("d3d9_shtex_init_copytex: Failed to get surface level", hr); return false; } return true; }
static inline IUnknown *get_dxgi_backbuffer(IDXGISwapChain *swap) { IDXGIResource *res = nullptr; HRESULT hr; hr = swap->GetBuffer(0, __uuidof(IUnknown), (void**)&res); if (FAILED(hr)) hlog_hr("get_dxgi_backbuffer: GetBuffer failed", hr); return res; }
static inline void d3d9_shtex_capture(IDirect3DSurface9 *backbuffer) { D3DTEXTUREFILTERTYPE filter; HRESULT hr; filter = data.using_scale ? D3DTEXF_LINEAR : D3DTEXF_NONE; hr = data.device->StretchRect(backbuffer, nullptr, data.d3d9_copytex, nullptr, filter); if (FAILED(hr)) hlog_hr("d3d9_shtex_capture: StretchRect failed", hr); }
static bool d3d9_get_swap_desc(D3DPRESENT_PARAMETERS &pp) { IDirect3DSwapChain9 *swap = nullptr; HRESULT hr; hr = data.device->GetSwapChain(0, &swap); if (FAILED(hr)) { hlog_hr("d3d9_get_swap_desc: Failed to get swap chain", hr); return false; } hr = swap->GetPresentParameters(&pp); swap->Release(); if (FAILED(hr)) { hlog_hr("d3d9_get_swap_desc: Failed to get " "presentation parameters", hr); return false; } return true; }
static IDirect3DSurface8 *d3d8_get_backbuffer(IDirect3DDevice8 *device) { IDirect3DSurface8 *backbuffer; HRESULT hr; hr = device->GetRenderTarget(&backbuffer); if (FAILED(hr)) { hlog_hr("d3d8_get_backbuffer: Failed to get backbuffer", hr); backbuffer = nullptr; } return backbuffer; }
static bool d3d8_get_window_handle(IDirect3DDevice8 *device) { D3DDEVICE_CREATION_PARAMETERS parameters; HRESULT hr; hr = device->GetCreationParameters(¶meters); if (FAILED(hr)) { hlog_hr("d3d8_get_window_handle: Failed to get " "device creation parameters", hr); return false; } data.window = parameters.hFocusWindow; return true; }
static bool d3d9_init_format_backbuffer(uint32_t &cx, uint32_t &cy, HWND &window) { IDirect3DSurface9 *back_buffer = nullptr; D3DPRESENT_PARAMETERS pp; D3DSURFACE_DESC desc; HRESULT hr; if (!d3d9_get_swap_desc(pp)) { return false; } hr = data.device->GetRenderTarget(0, &back_buffer); if (FAILED(hr)) { return false; } hr = back_buffer->GetDesc(&desc); back_buffer->Release(); if (FAILED(hr)) { hlog_hr("d3d9_init_format_backbuffer: Failed to get " "backbuffer descriptor", hr); return false; } data.d3d9_format = desc.Format; data.dxgi_format = d3d9_to_dxgi_format(desc.Format); data.using_scale = global_hook_info->use_scale; window = pp.hDeviceWindow; cx = desc.Width; cy = desc.Height; if (data.using_scale) { data.cx = global_hook_info->cx; data.cy = global_hook_info->cy; } else { data.cx = desc.Width; data.cy = desc.Height; } return true; }
static inline void present_begin(IDirect3DDevice9 *device, IDirect3DSurface9 *&backbuffer) { HRESULT hr; if (!present_recurse) { hr = get_backbuffer(device, &backbuffer); if (FAILED(hr)) { hlog_hr("d3d9_shmem_capture: Failed to get " "backbuffer", hr); } if (!global_hook_info->capture_overlay) { d3d9_capture(device, backbuffer); } } present_recurse++; }
static inline bool shex_init_d3d11() { PFN_D3D11_CREATE_DEVICE create_device; createfactory1_t create_factory; D3D_FEATURE_LEVEL level_used; IDXGIFactory *factory; IDXGIAdapter *adapter; HMODULE d3d11; HMODULE dxgi; HRESULT hr; d3d11 = load_system_library("d3d11.dll"); if (!d3d11) { hlog("d3d9_init: Failed to load D3D11"); return false; } dxgi = load_system_library("dxgi.dll"); if (!dxgi) { hlog("d3d9_init: Failed to load DXGI"); return false; } create_factory = (createfactory1_t)GetProcAddress(dxgi, "CreateDXGIFactory1"); if (!create_factory) { hlog("d3d9_init: Failed to get CreateDXGIFactory1 address"); return false; } create_device = (PFN_D3D11_CREATE_DEVICE)GetProcAddress(d3d11, "D3D11CreateDevice"); if (!create_device) { hlog("d3d9_init: Failed to get D3D11CreateDevice address"); return false; } hr = create_factory(__uuidof(IDXGIFactory1), (void**)&factory); if (FAILED(hr)) { hlog_hr("d3d9_init: Failed to create factory object", hr); return false; } hr = factory->EnumAdapters(0, &adapter); factory->Release(); if (FAILED(hr)) { hlog_hr("d3d9_init: Failed to get adapter", hr); return false; } hr = create_device(adapter, D3D_DRIVER_TYPE_UNKNOWN, nullptr, 0, feature_levels, sizeof(feature_levels) / sizeof(D3D_FEATURE_LEVEL), D3D11_SDK_VERSION, &data.d3d11_device, &level_used, &data.d3d11_context); adapter->Release(); if (FAILED(hr)) { hlog_hr("d3d9_init: Failed to create D3D11 device", hr); return false; } return true; }
static inline bool gl_shtex_init_d3d11(void) { D3D_FEATURE_LEVEL level_used; IDXGIFactory1 *factory; IDXGIAdapter *adapter; HRESULT hr; HMODULE d3d11 = load_system_library("d3d11.dll"); if (!d3d11) { hlog("gl_shtex_init_d3d11: failed to load D3D11.dll: %d", GetLastError()); return false; } HMODULE dxgi = load_system_library("dxgi.dll"); if (!dxgi) { hlog("gl_shtex_init_d3d11: failed to load DXGI.dll: %d", GetLastError()); return false; } DXGI_SWAP_CHAIN_DESC desc = {0}; desc.BufferCount = 2; desc.BufferDesc.Format = DXGI_FORMAT_B8G8R8A8_UNORM; desc.BufferDesc.Width = 2; desc.BufferDesc.Height = 2; desc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; desc.SampleDesc.Count = 1; desc.Windowed = true; desc.OutputWindow = data.hwnd; create_dxgi_factory1_t create_factory = (void*)GetProcAddress(dxgi, "CreateDXGIFactory1"); if (!create_factory) { hlog("gl_shtex_init_d3d11: failed to load CreateDXGIFactory1 " "procedure: %d", GetLastError()); return false; } PFN_D3D11_CREATE_DEVICE_AND_SWAP_CHAIN create = (void*)GetProcAddress( d3d11, "D3D11CreateDeviceAndSwapChain"); if (!create) { hlog("gl_shtex_init_d3d11: failed to load " "D3D11CreateDeviceAndSwapChain procedure: %d", GetLastError()); return false; } hr = create_factory(&GUID_IDXGIFactory1, (void**)&factory); if (FAILED(hr)) { hlog_hr("gl_shtex_init_d3d11: failed to create factory", hr); return false; } hr = IDXGIFactory1_EnumAdapters1(factory, 0, (IDXGIAdapter1**)&adapter); IDXGIFactory1_Release(factory); if (FAILED(hr)) { hlog_hr("gl_shtex_init_d3d11: failed to create adapter", hr); return false; } hr = create(adapter, D3D_DRIVER_TYPE_UNKNOWN, NULL, 0, feature_levels, sizeof(feature_levels) / sizeof(D3D_FEATURE_LEVEL), D3D11_SDK_VERSION, &desc, &data.dxgi_swap, &data.d3d11_device, &level_used, &data.d3d11_context); IDXGIAdapter_Release(adapter); if (FAILED(hr)) { hlog_hr("gl_shtex_init_d3d11: failed to create device", hr); return false; } return true; }
static DWORD CALLBACK copy_thread(LPVOID unused) { uint32_t pitch = thread_data.pitch; uint32_t cy = thread_data.cy; HANDLE events[2] = {NULL, NULL}; int shmem_id = 0; if (!duplicate_handle(&events[0], thread_data.copy_event)) { hlog_hr("copy_thread: Failed to duplicate copy event: %d", GetLastError()); return 0; } if (!duplicate_handle(&events[1], thread_data.stop_event)) { hlog_hr("copy_thread: Failed to duplicate stop event: %d", GetLastError()); goto finish; } for (;;) { int copy_tex; void *cur_data; DWORD ret = WaitForMultipleObjects(2, events, false, INFINITE); if (ret != WAIT_OBJECT_0) { break; } EnterCriticalSection(&thread_data.data_mutex); copy_tex = thread_data.cur_tex; cur_data = thread_data.cur_data; LeaveCriticalSection(&thread_data.data_mutex); if (copy_tex < NUM_BUFFERS && !!cur_data) { EnterCriticalSection(&thread_data.mutexes[copy_tex]); int lock_id = try_lock_shmem_tex(shmem_id); if (lock_id != -1) { memcpy(thread_data.shmem_textures[lock_id], cur_data, pitch * cy); unlock_shmem_tex(lock_id); ((struct shmem_data*)shmem_info)->last_tex = lock_id; shmem_id = lock_id == 0 ? 1 : 0; } LeaveCriticalSection(&thread_data.mutexes[copy_tex]); } } finish: for (size_t i = 0; i < 2; i++) { if (events[i]) { CloseHandle(events[i]); } } (void)unused; return 0; }
bool hook_dxgi(void) { pD3DCompile compile; ID3D10Blob *blob; HMODULE dxgi_module = get_system_module("dxgi.dll"); HRESULT hr; void *present_addr; void *resize_addr; if (!dxgi_module) { return false; } compile = get_compiler(); if (!compile) { hlog("hook_dxgi: failed to find d3d compiler library"); return true; } /* ---------------------- */ hr = compile(vertex_shader_string, sizeof(vertex_shader_string), "vertex_shader_string", nullptr, nullptr, "main", "vs_4_0", D3D10_SHADER_OPTIMIZATION_LEVEL1, 0, &blob, nullptr); if (FAILED(hr)) { hlog_hr("hook_dxgi: failed to compile vertex shader", hr); return true; } vertex_shader_size = (size_t)blob->GetBufferSize(); memcpy(vertex_shader_data, blob->GetBufferPointer(), blob->GetBufferSize()); blob->Release(); /* ---------------------- */ hr = compile(pixel_shader_string, sizeof(pixel_shader_string), "pixel_shader_string", nullptr, nullptr, "main", "ps_4_0", D3D10_SHADER_OPTIMIZATION_LEVEL1, 0, &blob, nullptr); if (FAILED(hr)) { hlog_hr("hook_dxgi: failed to compile pixel shader", hr); return true; } pixel_shader_size = (size_t)blob->GetBufferSize(); memcpy(pixel_shader_data, blob->GetBufferPointer(), blob->GetBufferSize()); blob->Release(); /* ---------------------- */ present_addr = get_offset_addr(dxgi_module, global_hook_info->offsets.dxgi.present); resize_addr = get_offset_addr(dxgi_module, global_hook_info->offsets.dxgi.resize); hook_init(&present, present_addr, (void*)hook_present, "IDXGISwapChain::Present"); hook_init(&resize_buffers, resize_addr, (void*)hook_resize_buffers, "IDXGISwapChain::ResizeBuffers"); rehook(&resize_buffers); rehook(&present); hlog("Hooked DXGI"); return true; }