dxgi-capture.cpp 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336
  1. #include <d3d10_1.h>
  2. #include <d3d11.h>
  3. #include <dxgi1_2.h>
  4. #include <d3dcompiler.h>
  5. #include "d3d1x_shaders.hpp"
  6. #include "graphics-hook.h"
  7. #include "../funchook.h"
  8. #if COMPILE_D3D12_HOOK
  9. #include <d3d12.h>
  10. #endif
  11. typedef HRESULT (STDMETHODCALLTYPE *resize_buffers_t)(IDXGISwapChain*, UINT,
  12. UINT, UINT, DXGI_FORMAT, UINT);
  13. typedef HRESULT (STDMETHODCALLTYPE *present_t)(IDXGISwapChain*, UINT, UINT);
  14. typedef HRESULT (STDMETHODCALLTYPE *present1_t)(IDXGISwapChain1*, UINT, UINT,
  15. const DXGI_PRESENT_PARAMETERS *);
  16. static struct func_hook resize_buffers;
  17. static struct func_hook present;
  18. static struct func_hook present1;
  19. struct dxgi_swap_data {
  20. IDXGISwapChain *swap;
  21. void (*capture)(void*, void*, bool);
  22. void (*free)(void);
  23. };
  24. static struct dxgi_swap_data data = {};
  25. static bool setup_dxgi(IDXGISwapChain *swap)
  26. {
  27. IUnknown *device;
  28. HRESULT hr;
  29. hr = swap->GetDevice(__uuidof(ID3D11Device), (void**)&device);
  30. if (SUCCEEDED(hr)) {
  31. ID3D11Device *d3d11 = reinterpret_cast<ID3D11Device*>(device);
  32. D3D_FEATURE_LEVEL level = d3d11->GetFeatureLevel();
  33. device->Release();
  34. if (level >= D3D_FEATURE_LEVEL_11_0) {
  35. data.swap = swap;
  36. data.capture = d3d11_capture;
  37. data.free = d3d11_free;
  38. return true;
  39. }
  40. }
  41. hr = swap->GetDevice(__uuidof(ID3D10Device), (void**)&device);
  42. if (SUCCEEDED(hr)) {
  43. data.swap = swap;
  44. data.capture = d3d10_capture;
  45. data.free = d3d10_free;
  46. device->Release();
  47. return true;
  48. }
  49. hr = swap->GetDevice(__uuidof(ID3D11Device), (void**)&device);
  50. if (SUCCEEDED(hr)) {
  51. data.swap = swap;
  52. data.capture = d3d11_capture;
  53. data.free = d3d11_free;
  54. device->Release();
  55. return true;
  56. }
  57. #if COMPILE_D3D12_HOOK
  58. hr = swap->GetDevice(__uuidof(ID3D12Device), (void**)&device);
  59. if (SUCCEEDED(hr)) {
  60. data.swap = swap;
  61. data.capture = d3d12_capture;
  62. data.free = d3d12_free;
  63. device->Release();
  64. return true;
  65. }
  66. #endif
  67. return false;
  68. }
  69. static bool resize_buffers_called = false;
  70. static HRESULT STDMETHODCALLTYPE hook_resize_buffers(IDXGISwapChain *swap,
  71. UINT buffer_count, UINT width, UINT height, DXGI_FORMAT format,
  72. UINT flags)
  73. {
  74. HRESULT hr;
  75. if (!!data.free)
  76. data.free();
  77. data.swap = nullptr;
  78. data.free = nullptr;
  79. data.capture = nullptr;
  80. unhook(&resize_buffers);
  81. resize_buffers_t call = (resize_buffers_t)resize_buffers.call_addr;
  82. hr = call(swap, buffer_count, width, height, format, flags);
  83. rehook(&resize_buffers);
  84. resize_buffers_called = true;
  85. return hr;
  86. }
  87. static inline IUnknown *get_dxgi_backbuffer(IDXGISwapChain *swap)
  88. {
  89. IDXGIResource *res = nullptr;
  90. HRESULT hr;
  91. hr = swap->GetBuffer(0, __uuidof(IUnknown), (void**)&res);
  92. if (FAILED(hr))
  93. hlog_hr("get_dxgi_backbuffer: GetBuffer failed", hr);
  94. return res;
  95. }
  96. static HRESULT STDMETHODCALLTYPE hook_present(IDXGISwapChain *swap,
  97. UINT sync_interval, UINT flags)
  98. {
  99. IUnknown *backbuffer = nullptr;
  100. bool capture_overlay = global_hook_info->capture_overlay;
  101. bool test_draw = (flags & DXGI_PRESENT_TEST) != 0;
  102. bool capture;
  103. HRESULT hr;
  104. if (!data.swap && !capture_active()) {
  105. setup_dxgi(swap);
  106. }
  107. capture = !test_draw && swap == data.swap && !!data.capture;
  108. if (capture && !capture_overlay) {
  109. backbuffer = get_dxgi_backbuffer(swap);
  110. if (!!backbuffer) {
  111. data.capture(swap, backbuffer, capture_overlay);
  112. backbuffer->Release();
  113. }
  114. }
  115. unhook(&present);
  116. present_t call = (present_t)present.call_addr;
  117. hr = call(swap, sync_interval, flags);
  118. rehook(&present);
  119. if (capture && capture_overlay) {
  120. /*
  121. * It seems that the first call to Present after ResizeBuffers
  122. * will cause the backbuffer to be invalidated, so do not
  123. * perform the post-overlay capture if ResizeBuffers has
  124. * recently been called. (The backbuffer returned by
  125. * get_dxgi_backbuffer *will* be invalid otherwise)
  126. */
  127. if (resize_buffers_called) {
  128. resize_buffers_called = false;
  129. } else {
  130. backbuffer = get_dxgi_backbuffer(swap);
  131. if (!!backbuffer) {
  132. data.capture(swap, backbuffer, capture_overlay);
  133. backbuffer->Release();
  134. }
  135. }
  136. }
  137. return hr;
  138. }
  139. static HRESULT STDMETHODCALLTYPE hook_present1(IDXGISwapChain1 *swap,
  140. UINT sync_interval, UINT flags,
  141. const DXGI_PRESENT_PARAMETERS *params)
  142. {
  143. IUnknown *backbuffer = nullptr;
  144. bool capture_overlay = global_hook_info->capture_overlay;
  145. bool test_draw = (flags & DXGI_PRESENT_TEST) != 0;
  146. bool capture;
  147. HRESULT hr;
  148. if (!data.swap && !capture_active()) {
  149. setup_dxgi(swap);
  150. }
  151. capture = !test_draw && swap == data.swap && !!data.capture;
  152. if (capture && !capture_overlay) {
  153. backbuffer = get_dxgi_backbuffer(swap);
  154. if (!!backbuffer) {
  155. DXGI_SWAP_CHAIN_DESC1 desc;
  156. swap->GetDesc1(&desc);
  157. data.capture(swap, backbuffer, capture_overlay);
  158. backbuffer->Release();
  159. }
  160. }
  161. unhook(&present1);
  162. present1_t call = (present1_t)present1.call_addr;
  163. hr = call(swap, sync_interval, flags, params);
  164. rehook(&present1);
  165. if (capture && capture_overlay) {
  166. if (resize_buffers_called) {
  167. resize_buffers_called = false;
  168. } else {
  169. backbuffer = get_dxgi_backbuffer(swap);
  170. if (!!backbuffer) {
  171. data.capture(swap, backbuffer, capture_overlay);
  172. backbuffer->Release();
  173. }
  174. }
  175. }
  176. return hr;
  177. }
  178. static pD3DCompile get_compiler(void)
  179. {
  180. pD3DCompile compile = nullptr;
  181. char d3dcompiler[40] = {};
  182. int ver = 49;
  183. while (ver > 30) {
  184. sprintf_s(d3dcompiler, 40, "D3DCompiler_%02d.dll", ver);
  185. HMODULE module = LoadLibraryA(d3dcompiler);
  186. if (module) {
  187. compile = (pD3DCompile)GetProcAddress(module,
  188. "D3DCompile");
  189. if (compile) {
  190. break;
  191. }
  192. }
  193. ver--;
  194. }
  195. return compile;
  196. }
  197. static uint8_t vertex_shader_data[1024];
  198. static uint8_t pixel_shader_data[1024];
  199. static size_t vertex_shader_size = 0;
  200. static size_t pixel_shader_size = 0;
  201. bool hook_dxgi(void)
  202. {
  203. pD3DCompile compile;
  204. ID3D10Blob *blob;
  205. HMODULE dxgi_module = get_system_module("dxgi.dll");
  206. HRESULT hr;
  207. void *present_addr;
  208. void *resize_addr;
  209. void *present1_addr = nullptr;
  210. if (!dxgi_module) {
  211. return false;
  212. }
  213. compile = get_compiler();
  214. if (!compile) {
  215. hlog("hook_dxgi: failed to find d3d compiler library");
  216. return true;
  217. }
  218. /* ---------------------- */
  219. hr = compile(vertex_shader_string, sizeof(vertex_shader_string),
  220. "vertex_shader_string", nullptr, nullptr, "main",
  221. "vs_4_0", D3D10_SHADER_OPTIMIZATION_LEVEL1, 0, &blob,
  222. nullptr);
  223. if (FAILED(hr)) {
  224. hlog_hr("hook_dxgi: failed to compile vertex shader", hr);
  225. return true;
  226. }
  227. vertex_shader_size = (size_t)blob->GetBufferSize();
  228. memcpy(vertex_shader_data, blob->GetBufferPointer(),
  229. blob->GetBufferSize());
  230. blob->Release();
  231. /* ---------------------- */
  232. hr = compile(pixel_shader_string, sizeof(pixel_shader_string),
  233. "pixel_shader_string", nullptr, nullptr, "main",
  234. "ps_4_0", D3D10_SHADER_OPTIMIZATION_LEVEL1, 0, &blob,
  235. nullptr);
  236. if (FAILED(hr)) {
  237. hlog_hr("hook_dxgi: failed to compile pixel shader", hr);
  238. return true;
  239. }
  240. pixel_shader_size = (size_t)blob->GetBufferSize();
  241. memcpy(pixel_shader_data, blob->GetBufferPointer(),
  242. blob->GetBufferSize());
  243. blob->Release();
  244. /* ---------------------- */
  245. present_addr = get_offset_addr(dxgi_module,
  246. global_hook_info->offsets.dxgi.present);
  247. resize_addr = get_offset_addr(dxgi_module,
  248. global_hook_info->offsets.dxgi.resize);
  249. if (global_hook_info->offsets.dxgi.present1)
  250. present1_addr = get_offset_addr(dxgi_module,
  251. global_hook_info->offsets.dxgi.present1);
  252. hook_init(&present, present_addr, (void*)hook_present,
  253. "IDXGISwapChain::Present");
  254. hook_init(&resize_buffers, resize_addr, (void*)hook_resize_buffers,
  255. "IDXGISwapChain::ResizeBuffers");
  256. if (present1_addr)
  257. hook_init(&present1, present1_addr, (void*)hook_present1,
  258. "IDXGISwapChain1::Present1");
  259. rehook(&resize_buffers);
  260. rehook(&present);
  261. if (present1_addr)
  262. rehook(&present1);
  263. hlog("Hooked DXGI");
  264. return true;
  265. }
  266. uint8_t *get_d3d1x_vertex_shader(size_t *size)
  267. {
  268. *size = vertex_shader_size;
  269. return vertex_shader_data;
  270. }
  271. uint8_t *get_d3d1x_pixel_shader(size_t *size)
  272. {
  273. *size = pixel_shader_size;
  274. return pixel_shader_data;
  275. }