d3d9-capture.cpp 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954
  1. #include <d3d9.h>
  2. #include <d3d11.h>
  3. #include <dxgi.h>
  4. #include "graphics-hook.h"
  5. #include "../funchook.h"
  6. #include "d3d9-patches.hpp"
  7. typedef HRESULT(STDMETHODCALLTYPE *present_t)(IDirect3DDevice9 *, CONST RECT *,
  8. CONST RECT *, HWND,
  9. CONST RGNDATA *);
  10. typedef HRESULT(STDMETHODCALLTYPE *present_ex_t)(IDirect3DDevice9 *,
  11. CONST RECT *, CONST RECT *,
  12. HWND, CONST RGNDATA *, DWORD);
  13. typedef HRESULT(STDMETHODCALLTYPE *present_swap_t)(IDirect3DSwapChain9 *,
  14. CONST RECT *, CONST RECT *,
  15. HWND, CONST RGNDATA *,
  16. DWORD);
  17. typedef HRESULT(STDMETHODCALLTYPE *reset_t)(IDirect3DDevice9 *,
  18. D3DPRESENT_PARAMETERS *);
  19. typedef HRESULT(STDMETHODCALLTYPE *reset_ex_t)(IDirect3DDevice9 *,
  20. D3DPRESENT_PARAMETERS *,
  21. D3DDISPLAYMODEEX *);
  22. typedef HRESULT(WINAPI *createfactory1_t)(REFIID, void **);
  23. static struct func_hook present;
  24. static struct func_hook present_ex;
  25. static struct func_hook present_swap;
  26. static struct func_hook reset;
  27. static struct func_hook reset_ex;
  28. struct d3d9_data {
  29. HMODULE d3d9;
  30. IDirect3DDevice9 *device; /* do not release */
  31. uint32_t cx;
  32. uint32_t cy;
  33. D3DFORMAT d3d9_format;
  34. DXGI_FORMAT dxgi_format;
  35. bool using_shtex;
  36. bool using_scale;
  37. /* shared texture */
  38. IDirect3DSurface9 *d3d9_copytex;
  39. ID3D11Device *d3d11_device;
  40. ID3D11DeviceContext *d3d11_context;
  41. ID3D11Resource *d3d11_tex;
  42. struct shtex_data *shtex_info;
  43. HANDLE handle;
  44. int patch;
  45. /* shared memory */
  46. IDirect3DSurface9 *copy_surfaces[NUM_BUFFERS];
  47. IDirect3DSurface9 *render_targets[NUM_BUFFERS];
  48. IDirect3DQuery9 *queries[NUM_BUFFERS];
  49. struct shmem_data *shmem_info;
  50. bool texture_mapped[NUM_BUFFERS];
  51. volatile bool issued_queries[NUM_BUFFERS];
  52. uint32_t pitch;
  53. int cur_tex;
  54. int copy_wait;
  55. };
  56. static struct d3d9_data data = {};
  57. static void d3d9_free()
  58. {
  59. capture_free();
  60. if (data.using_shtex) {
  61. if (data.d3d11_tex)
  62. data.d3d11_tex->Release();
  63. if (data.d3d11_context)
  64. data.d3d11_context->Release();
  65. if (data.d3d11_device)
  66. data.d3d11_device->Release();
  67. if (data.d3d9_copytex)
  68. data.d3d9_copytex->Release();
  69. } else {
  70. for (size_t i = 0; i < NUM_BUFFERS; i++) {
  71. if (data.copy_surfaces[i]) {
  72. if (data.texture_mapped[i])
  73. data.copy_surfaces[i]->UnlockRect();
  74. data.copy_surfaces[i]->Release();
  75. }
  76. if (data.render_targets[i])
  77. data.render_targets[i]->Release();
  78. if (data.queries[i])
  79. data.queries[i]->Release();
  80. }
  81. }
  82. memset(&data, 0, sizeof(data));
  83. hlog("----------------- d3d9 capture freed -----------------");
  84. }
  85. static DXGI_FORMAT d3d9_to_dxgi_format(D3DFORMAT format)
  86. {
  87. switch ((unsigned long)format) {
  88. case D3DFMT_A2B10G10R10:
  89. return DXGI_FORMAT_R10G10B10A2_UNORM;
  90. case D3DFMT_A8R8G8B8:
  91. return DXGI_FORMAT_B8G8R8A8_UNORM;
  92. case D3DFMT_X8R8G8B8:
  93. return DXGI_FORMAT_B8G8R8X8_UNORM;
  94. }
  95. return DXGI_FORMAT_UNKNOWN;
  96. }
  97. const static D3D_FEATURE_LEVEL feature_levels[] = {
  98. D3D_FEATURE_LEVEL_11_0,
  99. D3D_FEATURE_LEVEL_10_1,
  100. D3D_FEATURE_LEVEL_10_0,
  101. D3D_FEATURE_LEVEL_9_3,
  102. };
  103. static inline bool shex_init_d3d11()
  104. {
  105. PFN_D3D11_CREATE_DEVICE create_device;
  106. createfactory1_t create_factory;
  107. D3D_FEATURE_LEVEL level_used;
  108. IDXGIFactory *factory;
  109. IDXGIAdapter *adapter;
  110. HMODULE d3d11;
  111. HMODULE dxgi;
  112. HRESULT hr;
  113. d3d11 = load_system_library("d3d11.dll");
  114. if (!d3d11) {
  115. hlog("d3d9_init: Failed to load D3D11");
  116. return false;
  117. }
  118. dxgi = load_system_library("dxgi.dll");
  119. if (!dxgi) {
  120. hlog("d3d9_init: Failed to load DXGI");
  121. return false;
  122. }
  123. create_factory =
  124. (createfactory1_t)GetProcAddress(dxgi, "CreateDXGIFactory1");
  125. if (!create_factory) {
  126. hlog("d3d9_init: Failed to get CreateDXGIFactory1 address");
  127. return false;
  128. }
  129. create_device = (PFN_D3D11_CREATE_DEVICE)GetProcAddress(
  130. d3d11, "D3D11CreateDevice");
  131. if (!create_device) {
  132. hlog("d3d9_init: Failed to get D3D11CreateDevice address");
  133. return false;
  134. }
  135. hr = create_factory(__uuidof(IDXGIFactory1), (void **)&factory);
  136. if (FAILED(hr)) {
  137. hlog_hr("d3d9_init: Failed to create factory object", hr);
  138. return false;
  139. }
  140. hr = factory->EnumAdapters(0, &adapter);
  141. factory->Release();
  142. if (FAILED(hr)) {
  143. hlog_hr("d3d9_init: Failed to get adapter", hr);
  144. return false;
  145. }
  146. hr = create_device(adapter, D3D_DRIVER_TYPE_UNKNOWN, nullptr, 0,
  147. feature_levels,
  148. sizeof(feature_levels) / sizeof(D3D_FEATURE_LEVEL),
  149. D3D11_SDK_VERSION, &data.d3d11_device, &level_used,
  150. &data.d3d11_context);
  151. adapter->Release();
  152. if (FAILED(hr)) {
  153. hlog_hr("d3d9_init: Failed to create D3D11 device", hr);
  154. return false;
  155. }
  156. return true;
  157. }
  158. static inline bool d3d9_shtex_init_shtex()
  159. {
  160. IDXGIResource *res;
  161. HRESULT hr;
  162. D3D11_TEXTURE2D_DESC desc = {};
  163. desc.Width = data.cx;
  164. desc.Height = data.cy;
  165. desc.Format = data.dxgi_format;
  166. desc.MipLevels = 1;
  167. desc.ArraySize = 1;
  168. desc.SampleDesc.Count = 1;
  169. desc.Usage = D3D11_USAGE_DEFAULT;
  170. desc.MiscFlags = D3D11_RESOURCE_MISC_SHARED;
  171. desc.BindFlags = D3D11_BIND_RENDER_TARGET | D3D11_BIND_SHADER_RESOURCE;
  172. hr = data.d3d11_device->CreateTexture2D(
  173. &desc, nullptr, (ID3D11Texture2D **)&data.d3d11_tex);
  174. if (FAILED(hr)) {
  175. hlog_hr("d3d9_shtex_init_shtex: Failed to create D3D11 texture",
  176. hr);
  177. return false;
  178. }
  179. hr = data.d3d11_tex->QueryInterface(__uuidof(IDXGIResource),
  180. (void **)&res);
  181. if (FAILED(hr)) {
  182. hlog_hr("d3d9_shtex_init_shtex: Failed to query IDXGIResource",
  183. hr);
  184. return false;
  185. }
  186. hr = res->GetSharedHandle(&data.handle);
  187. res->Release();
  188. if (FAILED(hr)) {
  189. hlog_hr("d3d9_shtex_init_shtex: Failed to get shared handle",
  190. hr);
  191. return false;
  192. }
  193. return true;
  194. }
  195. static inline bool d3d9_shtex_init_copytex()
  196. {
  197. struct d3d9_offsets offsets = global_hook_info->offsets.d3d9;
  198. uint8_t *patch_addr = nullptr;
  199. BOOL *p_is_d3d9 = nullptr;
  200. uint8_t saved_data[MAX_PATCH_SIZE];
  201. size_t patch_size = 0;
  202. BOOL was_d3d9ex = false;
  203. IDirect3DTexture9 *tex;
  204. DWORD protect_val;
  205. HRESULT hr;
  206. if (offsets.d3d9_clsoff && offsets.is_d3d9ex_clsoff) {
  207. uint8_t *device_ptr = (uint8_t *)(data.device);
  208. uint8_t *d3d9_ptr =
  209. *(uint8_t **)(device_ptr + offsets.d3d9_clsoff);
  210. p_is_d3d9 = (BOOL *)(d3d9_ptr + offsets.is_d3d9ex_clsoff);
  211. } else {
  212. patch_addr = get_d3d9_patch_addr(data.d3d9, data.patch);
  213. }
  214. if (p_is_d3d9) {
  215. was_d3d9ex = *p_is_d3d9;
  216. *p_is_d3d9 = true;
  217. } else if (patch_addr) {
  218. patch_size = patch[data.patch].size;
  219. VirtualProtect(patch_addr, patch_size, PAGE_EXECUTE_READWRITE,
  220. &protect_val);
  221. memcpy(saved_data, patch_addr, patch_size);
  222. memcpy(patch_addr, patch[data.patch].data, patch_size);
  223. }
  224. hr = data.device->CreateTexture(data.cx, data.cy, 1,
  225. D3DUSAGE_RENDERTARGET, data.d3d9_format,
  226. D3DPOOL_DEFAULT, &tex, &data.handle);
  227. if (p_is_d3d9) {
  228. *p_is_d3d9 = was_d3d9ex;
  229. } else if (patch_addr && patch_size) {
  230. memcpy(patch_addr, saved_data, patch_size);
  231. VirtualProtect(patch_addr, patch_size, protect_val,
  232. &protect_val);
  233. }
  234. if (FAILED(hr)) {
  235. hlog_hr("d3d9_shtex_init_copytex: Failed to create shared texture",
  236. hr);
  237. return false;
  238. }
  239. hr = tex->GetSurfaceLevel(0, &data.d3d9_copytex);
  240. tex->Release();
  241. if (FAILED(hr)) {
  242. hlog_hr("d3d9_shtex_init_copytex: Failed to get surface level",
  243. hr);
  244. return false;
  245. }
  246. return true;
  247. }
  248. static bool d3d9_shtex_init(uint32_t cx, uint32_t cy, HWND window)
  249. {
  250. data.using_shtex = true;
  251. if (!shex_init_d3d11()) {
  252. return false;
  253. }
  254. if (!d3d9_shtex_init_shtex()) {
  255. return false;
  256. }
  257. if (!d3d9_shtex_init_copytex()) {
  258. return false;
  259. }
  260. if (!capture_init_shtex(&data.shtex_info, window, cx, cy, data.cx,
  261. data.cy, data.dxgi_format, false,
  262. (uintptr_t)data.handle)) {
  263. return false;
  264. }
  265. hlog("d3d9 shared texture capture successful");
  266. return true;
  267. }
  268. static bool d3d9_shmem_init_buffers(size_t buffer)
  269. {
  270. HRESULT hr;
  271. hr = data.device->CreateOffscreenPlainSurface(
  272. data.cx, data.cy, data.d3d9_format, D3DPOOL_SYSTEMMEM,
  273. &data.copy_surfaces[buffer], nullptr);
  274. if (FAILED(hr)) {
  275. hlog_hr("d3d9_shmem_init_buffers: Failed to create surface",
  276. hr);
  277. return false;
  278. }
  279. if (buffer == 0) {
  280. D3DLOCKED_RECT rect;
  281. hr = data.copy_surfaces[buffer]->LockRect(&rect, nullptr,
  282. D3DLOCK_READONLY);
  283. if (FAILED(hr)) {
  284. hlog_hr("d3d9_shmem_init_buffers: Failed to lock "
  285. "buffer",
  286. hr);
  287. return false;
  288. }
  289. data.pitch = rect.Pitch;
  290. data.copy_surfaces[buffer]->UnlockRect();
  291. }
  292. hr = data.device->CreateRenderTarget(data.cx, data.cy, data.d3d9_format,
  293. D3DMULTISAMPLE_NONE, 0, false,
  294. &data.render_targets[buffer],
  295. nullptr);
  296. if (FAILED(hr)) {
  297. hlog_hr("d3d9_shmem_init_buffers: Failed to create render "
  298. "target",
  299. hr);
  300. return false;
  301. }
  302. hr = data.device->CreateQuery(D3DQUERYTYPE_EVENT,
  303. &data.queries[buffer]);
  304. if (FAILED(hr)) {
  305. hlog_hr("d3d9_shmem_init_buffers: Failed to create query", hr);
  306. return false;
  307. }
  308. return true;
  309. }
  310. static bool d3d9_shmem_init(uint32_t cx, uint32_t cy, HWND window)
  311. {
  312. data.using_shtex = false;
  313. for (size_t i = 0; i < NUM_BUFFERS; i++) {
  314. if (!d3d9_shmem_init_buffers(i)) {
  315. return false;
  316. }
  317. }
  318. if (!capture_init_shmem(&data.shmem_info, window, cx, cy, data.cx,
  319. data.cy, data.pitch, data.dxgi_format, false)) {
  320. return false;
  321. }
  322. hlog("d3d9 memory capture successful");
  323. return true;
  324. }
  325. static bool d3d9_get_swap_desc(D3DPRESENT_PARAMETERS &pp)
  326. {
  327. IDirect3DSwapChain9 *swap = nullptr;
  328. HRESULT hr;
  329. hr = data.device->GetSwapChain(0, &swap);
  330. if (FAILED(hr)) {
  331. hlog_hr("d3d9_get_swap_desc: Failed to get swap chain", hr);
  332. return false;
  333. }
  334. hr = swap->GetPresentParameters(&pp);
  335. swap->Release();
  336. if (FAILED(hr)) {
  337. hlog_hr("d3d9_get_swap_desc: Failed to get "
  338. "presentation parameters",
  339. hr);
  340. return false;
  341. }
  342. return true;
  343. }
  344. static bool d3d9_init_format_backbuffer(uint32_t &cx, uint32_t &cy,
  345. HWND &window)
  346. {
  347. IDirect3DSurface9 *back_buffer = nullptr;
  348. D3DPRESENT_PARAMETERS pp;
  349. D3DSURFACE_DESC desc;
  350. HRESULT hr;
  351. if (!d3d9_get_swap_desc(pp)) {
  352. return false;
  353. }
  354. hr = data.device->GetRenderTarget(0, &back_buffer);
  355. if (FAILED(hr)) {
  356. return false;
  357. }
  358. hr = back_buffer->GetDesc(&desc);
  359. back_buffer->Release();
  360. if (FAILED(hr)) {
  361. hlog_hr("d3d9_init_format_backbuffer: Failed to get "
  362. "backbuffer descriptor",
  363. hr);
  364. return false;
  365. }
  366. data.d3d9_format = desc.Format;
  367. data.dxgi_format = d3d9_to_dxgi_format(desc.Format);
  368. data.using_scale = global_hook_info->use_scale;
  369. window = pp.hDeviceWindow;
  370. cx = desc.Width;
  371. cy = desc.Height;
  372. if (data.using_scale) {
  373. data.cx = global_hook_info->cx;
  374. data.cy = global_hook_info->cy;
  375. } else {
  376. data.cx = desc.Width;
  377. data.cy = desc.Height;
  378. }
  379. return true;
  380. }
  381. static bool d3d9_init_format_swapchain(uint32_t &cx, uint32_t &cy, HWND &window)
  382. {
  383. D3DPRESENT_PARAMETERS pp;
  384. if (!d3d9_get_swap_desc(pp)) {
  385. return false;
  386. }
  387. data.dxgi_format = d3d9_to_dxgi_format(pp.BackBufferFormat);
  388. data.d3d9_format = pp.BackBufferFormat;
  389. data.using_scale = global_hook_info->use_scale;
  390. window = pp.hDeviceWindow;
  391. cx = pp.BackBufferWidth;
  392. cy = pp.BackBufferHeight;
  393. if (data.using_scale) {
  394. data.cx = global_hook_info->cx;
  395. data.cy = global_hook_info->cy;
  396. } else {
  397. data.cx = pp.BackBufferWidth;
  398. data.cy = pp.BackBufferHeight;
  399. }
  400. return true;
  401. }
  402. static void d3d9_init(IDirect3DDevice9 *device)
  403. {
  404. IDirect3DDevice9Ex *d3d9ex = nullptr;
  405. bool has_d3d9ex_bool_offset =
  406. global_hook_info->offsets.d3d9.d3d9_clsoff &&
  407. global_hook_info->offsets.d3d9.is_d3d9ex_clsoff;
  408. bool success;
  409. uint32_t cx = 0;
  410. uint32_t cy = 0;
  411. HWND window = nullptr;
  412. HRESULT hr;
  413. data.d3d9 = get_system_module("d3d9.dll");
  414. data.device = device;
  415. hr = device->QueryInterface(__uuidof(IDirect3DDevice9Ex),
  416. (void **)&d3d9ex);
  417. if (SUCCEEDED(hr)) {
  418. d3d9ex->Release();
  419. data.patch = -1;
  420. } else if (!has_d3d9ex_bool_offset) {
  421. data.patch = get_d3d9_patch(data.d3d9);
  422. } else {
  423. data.patch = -1;
  424. }
  425. if (!d3d9_init_format_backbuffer(cx, cy, window)) {
  426. if (!d3d9_init_format_swapchain(cx, cy, window)) {
  427. return;
  428. }
  429. }
  430. if (global_hook_info->force_shmem ||
  431. (!d3d9ex && data.patch == -1 && !has_d3d9ex_bool_offset)) {
  432. success = d3d9_shmem_init(cx, cy, window);
  433. } else {
  434. success = d3d9_shtex_init(cx, cy, window);
  435. }
  436. if (!success)
  437. d3d9_free();
  438. }
  439. static inline HRESULT get_backbuffer(IDirect3DDevice9 *device,
  440. IDirect3DSurface9 **surface)
  441. {
  442. static bool use_backbuffer = false;
  443. static bool checked_exceptions = false;
  444. if (!checked_exceptions) {
  445. if (_strcmpi(get_process_name(), "hotd_ng.exe") == 0)
  446. use_backbuffer = true;
  447. checked_exceptions = true;
  448. }
  449. if (use_backbuffer) {
  450. return device->GetBackBuffer(0, 0, D3DBACKBUFFER_TYPE_MONO,
  451. surface);
  452. } else {
  453. return device->GetRenderTarget(0, surface);
  454. }
  455. }
  456. static inline void d3d9_shtex_capture(IDirect3DSurface9 *backbuffer)
  457. {
  458. D3DTEXTUREFILTERTYPE filter;
  459. HRESULT hr;
  460. filter = data.using_scale ? D3DTEXF_LINEAR : D3DTEXF_NONE;
  461. hr = data.device->StretchRect(backbuffer, nullptr, data.d3d9_copytex,
  462. nullptr, filter);
  463. if (FAILED(hr))
  464. hlog_hr("d3d9_shtex_capture: StretchRect failed", hr);
  465. }
  466. static inline void d3d9_shmem_capture_queue_copy()
  467. {
  468. for (int i = 0; i < NUM_BUFFERS; i++) {
  469. IDirect3DSurface9 *target = data.copy_surfaces[i];
  470. D3DLOCKED_RECT rect;
  471. HRESULT hr;
  472. if (!data.issued_queries[i]) {
  473. continue;
  474. }
  475. if (data.queries[i]->GetData(0, 0, 0) != S_OK) {
  476. continue;
  477. }
  478. data.issued_queries[i] = false;
  479. hr = target->LockRect(&rect, nullptr, D3DLOCK_READONLY);
  480. if (SUCCEEDED(hr)) {
  481. data.texture_mapped[i] = true;
  482. shmem_copy_data(i, rect.pBits);
  483. }
  484. break;
  485. }
  486. }
  487. static inline void d3d9_shmem_capture(IDirect3DSurface9 *backbuffer)
  488. {
  489. D3DTEXTUREFILTERTYPE filter;
  490. IDirect3DSurface9 *copy;
  491. int next_tex;
  492. HRESULT hr;
  493. d3d9_shmem_capture_queue_copy();
  494. next_tex = (data.cur_tex == NUM_BUFFERS - 1) ? 0 : data.cur_tex + 1;
  495. filter = data.using_scale ? D3DTEXF_LINEAR : D3DTEXF_NONE;
  496. copy = data.render_targets[data.cur_tex];
  497. hr = data.device->StretchRect(backbuffer, nullptr, copy, nullptr,
  498. filter);
  499. if (FAILED(hr)) {
  500. hlog_hr("d3d9_shmem_capture: StretchRect failed", hr);
  501. return;
  502. }
  503. if (data.copy_wait < NUM_BUFFERS - 1) {
  504. data.copy_wait++;
  505. } else {
  506. IDirect3DSurface9 *src = data.render_targets[next_tex];
  507. IDirect3DSurface9 *dst = data.copy_surfaces[next_tex];
  508. if (shmem_texture_data_lock(next_tex)) {
  509. dst->UnlockRect();
  510. data.texture_mapped[next_tex] = false;
  511. shmem_texture_data_unlock(next_tex);
  512. }
  513. hr = data.device->GetRenderTargetData(src, dst);
  514. if (FAILED(hr)) {
  515. hlog_hr("d3d9_shmem_capture: GetRenderTargetData "
  516. "failed",
  517. hr);
  518. }
  519. data.queries[next_tex]->Issue(D3DISSUE_END);
  520. data.issued_queries[next_tex] = true;
  521. }
  522. data.cur_tex = next_tex;
  523. }
  524. static void d3d9_capture(IDirect3DDevice9 *device,
  525. IDirect3DSurface9 *backbuffer)
  526. {
  527. if (capture_should_stop()) {
  528. d3d9_free();
  529. }
  530. if (capture_should_init()) {
  531. d3d9_init(device);
  532. }
  533. if (capture_ready()) {
  534. if (data.device != device) {
  535. d3d9_free();
  536. return;
  537. }
  538. if (data.using_shtex)
  539. d3d9_shtex_capture(backbuffer);
  540. else
  541. d3d9_shmem_capture(backbuffer);
  542. }
  543. }
  544. /* this is used just in case Present calls PresentEx or vise versa. */
  545. static int present_recurse = 0;
  546. static inline void present_begin(IDirect3DDevice9 *device,
  547. IDirect3DSurface9 *&backbuffer)
  548. {
  549. HRESULT hr;
  550. if (!present_recurse) {
  551. hr = get_backbuffer(device, &backbuffer);
  552. if (FAILED(hr)) {
  553. hlog_hr("d3d9_shmem_capture: Failed to get "
  554. "backbuffer",
  555. hr);
  556. }
  557. if (!global_hook_info->capture_overlay) {
  558. d3d9_capture(device, backbuffer);
  559. }
  560. }
  561. present_recurse++;
  562. }
  563. static inline void present_end(IDirect3DDevice9 *device,
  564. IDirect3DSurface9 *backbuffer)
  565. {
  566. present_recurse--;
  567. if (!present_recurse) {
  568. if (global_hook_info->capture_overlay) {
  569. if (!present_recurse)
  570. d3d9_capture(device, backbuffer);
  571. }
  572. if (backbuffer)
  573. backbuffer->Release();
  574. }
  575. }
  576. static bool hooked_reset = false;
  577. static void setup_reset_hooks(IDirect3DDevice9 *device);
  578. static HRESULT STDMETHODCALLTYPE hook_present(IDirect3DDevice9 *device,
  579. CONST RECT *src_rect,
  580. CONST RECT *dst_rect,
  581. HWND override_window,
  582. CONST RGNDATA *dirty_region)
  583. {
  584. IDirect3DSurface9 *backbuffer = nullptr;
  585. HRESULT hr;
  586. if (!hooked_reset)
  587. setup_reset_hooks(device);
  588. present_begin(device, backbuffer);
  589. unhook(&present);
  590. present_t call = (present_t)present.call_addr;
  591. hr = call(device, src_rect, dst_rect, override_window, dirty_region);
  592. rehook(&present);
  593. present_end(device, backbuffer);
  594. return hr;
  595. }
  596. static HRESULT STDMETHODCALLTYPE hook_present_ex(
  597. IDirect3DDevice9 *device, CONST RECT *src_rect, CONST RECT *dst_rect,
  598. HWND override_window, CONST RGNDATA *dirty_region, DWORD flags)
  599. {
  600. IDirect3DSurface9 *backbuffer = nullptr;
  601. HRESULT hr;
  602. if (!hooked_reset)
  603. setup_reset_hooks(device);
  604. present_begin(device, backbuffer);
  605. unhook(&present_ex);
  606. present_ex_t call = (present_ex_t)present_ex.call_addr;
  607. hr = call(device, src_rect, dst_rect, override_window, dirty_region,
  608. flags);
  609. rehook(&present_ex);
  610. present_end(device, backbuffer);
  611. return hr;
  612. }
  613. static HRESULT STDMETHODCALLTYPE hook_present_swap(
  614. IDirect3DSwapChain9 *swap, CONST RECT *src_rect, CONST RECT *dst_rect,
  615. HWND override_window, CONST RGNDATA *dirty_region, DWORD flags)
  616. {
  617. IDirect3DSurface9 *backbuffer = nullptr;
  618. IDirect3DDevice9 *device = nullptr;
  619. HRESULT hr;
  620. if (!present_recurse) {
  621. hr = swap->GetDevice(&device);
  622. if (SUCCEEDED(hr)) {
  623. device->Release();
  624. }
  625. }
  626. if (device) {
  627. if (!hooked_reset)
  628. setup_reset_hooks(device);
  629. present_begin(device, backbuffer);
  630. }
  631. unhook(&present_swap);
  632. present_swap_t call = (present_swap_t)present_swap.call_addr;
  633. hr = call(swap, src_rect, dst_rect, override_window, dirty_region,
  634. flags);
  635. rehook(&present_swap);
  636. if (device)
  637. present_end(device, backbuffer);
  638. return hr;
  639. }
  640. static HRESULT STDMETHODCALLTYPE hook_reset(IDirect3DDevice9 *device,
  641. D3DPRESENT_PARAMETERS *params)
  642. {
  643. HRESULT hr;
  644. if (capture_active())
  645. d3d9_free();
  646. unhook(&reset);
  647. reset_t call = (reset_t)reset.call_addr;
  648. hr = call(device, params);
  649. rehook(&reset);
  650. return hr;
  651. }
  652. static HRESULT STDMETHODCALLTYPE hook_reset_ex(IDirect3DDevice9 *device,
  653. D3DPRESENT_PARAMETERS *params,
  654. D3DDISPLAYMODEEX *dmex)
  655. {
  656. HRESULT hr;
  657. if (capture_active())
  658. d3d9_free();
  659. unhook(&reset_ex);
  660. reset_ex_t call = (reset_ex_t)reset_ex.call_addr;
  661. hr = call(device, params, dmex);
  662. rehook(&reset_ex);
  663. return hr;
  664. }
  665. static void setup_reset_hooks(IDirect3DDevice9 *device)
  666. {
  667. IDirect3DDevice9Ex *d3d9ex = nullptr;
  668. uintptr_t *vtable = *(uintptr_t **)device;
  669. HRESULT hr;
  670. hook_init(&reset, (void *)vtable[16], (void *)hook_reset,
  671. "IDirect3DDevice9::Reset");
  672. rehook(&reset);
  673. hr = device->QueryInterface(__uuidof(IDirect3DDevice9Ex),
  674. (void **)&d3d9ex);
  675. if (SUCCEEDED(hr)) {
  676. hook_init(&reset_ex, (void *)vtable[132], (void *)hook_reset_ex,
  677. "IDirect3DDevice9Ex::ResetEx");
  678. rehook(&reset_ex);
  679. d3d9ex->Release();
  680. }
  681. hooked_reset = true;
  682. }
  683. typedef HRESULT(WINAPI *d3d9create_ex_t)(UINT, IDirect3D9Ex **);
  684. static bool manually_get_d3d9_addrs(HMODULE d3d9_module, void **present_addr,
  685. void **present_ex_addr,
  686. void **present_swap_addr)
  687. {
  688. d3d9create_ex_t create_ex;
  689. D3DPRESENT_PARAMETERS pp;
  690. HRESULT hr;
  691. IDirect3DDevice9Ex *device;
  692. IDirect3D9Ex *d3d9ex;
  693. hlog("D3D9 values invalid, manually obtaining");
  694. create_ex = (d3d9create_ex_t)GetProcAddress(d3d9_module,
  695. "Direct3DCreate9Ex");
  696. if (!create_ex) {
  697. hlog("Failed to load Direct3DCreate9Ex");
  698. return false;
  699. }
  700. if (FAILED(create_ex(D3D_SDK_VERSION, &d3d9ex))) {
  701. hlog("Failed to create D3D9 context");
  702. return false;
  703. }
  704. memset(&pp, 0, sizeof(pp));
  705. pp.Windowed = 1;
  706. pp.SwapEffect = D3DSWAPEFFECT_FLIP;
  707. pp.BackBufferFormat = D3DFMT_A8R8G8B8;
  708. pp.BackBufferCount = 1;
  709. pp.hDeviceWindow = (HWND)dummy_window;
  710. pp.PresentationInterval = D3DPRESENT_INTERVAL_IMMEDIATE;
  711. hr = d3d9ex->CreateDeviceEx(
  712. D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, dummy_window,
  713. D3DCREATE_HARDWARE_VERTEXPROCESSING | D3DCREATE_NOWINDOWCHANGES,
  714. &pp, NULL, &device);
  715. d3d9ex->Release();
  716. if (SUCCEEDED(hr)) {
  717. uintptr_t *vtable = *(uintptr_t **)device;
  718. IDirect3DSwapChain9 *swap;
  719. *present_addr = (void *)vtable[17];
  720. *present_ex_addr = (void *)vtable[121];
  721. hr = device->GetSwapChain(0, &swap);
  722. if (SUCCEEDED(hr)) {
  723. vtable = *(uintptr_t **)swap;
  724. *present_swap_addr = (void *)vtable[3];
  725. swap->Release();
  726. }
  727. device->Release();
  728. } else {
  729. hlog("Failed to create D3D9 device");
  730. return false;
  731. }
  732. return true;
  733. }
  734. bool hook_d3d9(void)
  735. {
  736. HMODULE d3d9_module = get_system_module("d3d9.dll");
  737. uint32_t d3d9_size;
  738. void *present_addr = nullptr;
  739. void *present_ex_addr = nullptr;
  740. void *present_swap_addr = nullptr;
  741. if (!d3d9_module) {
  742. return false;
  743. }
  744. d3d9_size = module_size(d3d9_module);
  745. if (global_hook_info->offsets.d3d9.present < d3d9_size &&
  746. global_hook_info->offsets.d3d9.present_ex < d3d9_size &&
  747. global_hook_info->offsets.d3d9.present_swap < d3d9_size) {
  748. present_addr = get_offset_addr(
  749. d3d9_module, global_hook_info->offsets.d3d9.present);
  750. present_ex_addr = get_offset_addr(
  751. d3d9_module, global_hook_info->offsets.d3d9.present_ex);
  752. present_swap_addr = get_offset_addr(
  753. d3d9_module,
  754. global_hook_info->offsets.d3d9.present_swap);
  755. } else {
  756. if (!dummy_window) {
  757. return false;
  758. }
  759. if (!manually_get_d3d9_addrs(d3d9_module, &present_addr,
  760. &present_ex_addr,
  761. &present_swap_addr)) {
  762. hlog("Failed to get D3D9 values");
  763. return true;
  764. }
  765. }
  766. if (!present_addr && !present_ex_addr && !present_swap_addr) {
  767. hlog("Invalid D3D9 values");
  768. return true;
  769. }
  770. if (present_swap_addr) {
  771. hook_init(&present_swap, present_swap_addr,
  772. (void *)hook_present_swap,
  773. "IDirect3DSwapChain9::Present");
  774. rehook(&present_swap);
  775. }
  776. if (present_ex_addr) {
  777. hook_init(&present_ex, present_ex_addr, (void *)hook_present_ex,
  778. "IDirect3DDevice9Ex::PresentEx");
  779. rehook(&present_ex);
  780. }
  781. if (present_addr) {
  782. hook_init(&present, present_addr, (void *)hook_present,
  783. "IDirect3DDevice9::Present");
  784. rehook(&present);
  785. }
  786. hlog("Hooked D3D9");
  787. return true;
  788. }