nvenc-d3d11.c 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264
  1. #include "nvenc-internal.h"
  2. #include "nvenc-helpers.h"
  3. /*
  4. * NVENC implementation using Direct3D 11 context and textures
  5. */
  6. /* ------------------------------------------------------------------------- */
  7. /* D3D11 Context/Device management */
  8. static HANDLE get_lib(struct nvenc_data *enc, const char *lib)
  9. {
  10. HMODULE mod = GetModuleHandleA(lib);
  11. if (mod)
  12. return mod;
  13. mod = LoadLibraryA(lib);
  14. if (!mod)
  15. error("Failed to load %s", lib);
  16. return mod;
  17. }
  18. typedef HRESULT(WINAPI *CREATEDXGIFACTORY1PROC)(REFIID, void **);
  19. bool d3d11_init(struct nvenc_data *enc, obs_data_t *settings)
  20. {
  21. HMODULE dxgi = get_lib(enc, "DXGI.dll");
  22. HMODULE d3d11 = get_lib(enc, "D3D11.dll");
  23. CREATEDXGIFACTORY1PROC create_dxgi;
  24. PFN_D3D11_CREATE_DEVICE create_device;
  25. IDXGIFactory1 *factory;
  26. IDXGIAdapter *adapter;
  27. ID3D11Device *device;
  28. ID3D11DeviceContext *context;
  29. HRESULT hr;
  30. if (!dxgi || !d3d11) {
  31. return false;
  32. }
  33. create_dxgi = (CREATEDXGIFACTORY1PROC)GetProcAddress(dxgi, "CreateDXGIFactory1");
  34. create_device = (PFN_D3D11_CREATE_DEVICE)GetProcAddress(d3d11, "D3D11CreateDevice");
  35. if (!create_dxgi || !create_device) {
  36. error("Failed to load D3D11/DXGI procedures");
  37. return false;
  38. }
  39. hr = create_dxgi(&IID_IDXGIFactory1, &factory);
  40. if (FAILED(hr)) {
  41. error_hr("CreateDXGIFactory1 failed");
  42. return false;
  43. }
  44. hr = factory->lpVtbl->EnumAdapters(factory, 0, &adapter);
  45. factory->lpVtbl->Release(factory);
  46. if (FAILED(hr)) {
  47. error_hr("EnumAdapters failed");
  48. return false;
  49. }
  50. hr = create_device(adapter, D3D_DRIVER_TYPE_UNKNOWN, NULL, 0, NULL, 0, D3D11_SDK_VERSION, &device, NULL,
  51. &context);
  52. adapter->lpVtbl->Release(adapter);
  53. if (FAILED(hr)) {
  54. error_hr("D3D11CreateDevice failed");
  55. return false;
  56. }
  57. enc->device = device;
  58. enc->context = context;
  59. return true;
  60. }
  61. void d3d11_free(struct nvenc_data *enc)
  62. {
  63. for (size_t i = 0; i < enc->input_textures.num; i++) {
  64. ID3D11Texture2D *tex = enc->input_textures.array[i].tex;
  65. IDXGIKeyedMutex *km = enc->input_textures.array[i].km;
  66. tex->lpVtbl->Release(tex);
  67. km->lpVtbl->Release(km);
  68. }
  69. if (enc->context) {
  70. enc->context->lpVtbl->Release(enc->context);
  71. }
  72. if (enc->device) {
  73. enc->device->lpVtbl->Release(enc->device);
  74. }
  75. }
  76. /* ------------------------------------------------------------------------- */
  77. /* D3D11 Surface management */
  78. static bool d3d11_texture_init(struct nvenc_data *enc, struct nv_texture *nvtex)
  79. {
  80. const bool p010 = obs_encoder_video_tex_active(enc->encoder, VIDEO_FORMAT_P010);
  81. D3D11_TEXTURE2D_DESC desc = {0};
  82. desc.Width = enc->cx;
  83. desc.Height = enc->cy;
  84. desc.MipLevels = 1;
  85. desc.ArraySize = 1;
  86. desc.Format = p010 ? DXGI_FORMAT_P010 : DXGI_FORMAT_NV12;
  87. desc.SampleDesc.Count = 1;
  88. desc.BindFlags = D3D11_BIND_RENDER_TARGET;
  89. ID3D11Device *const device = enc->device;
  90. ID3D11Texture2D *tex;
  91. HRESULT hr = device->lpVtbl->CreateTexture2D(device, &desc, NULL, &tex);
  92. if (FAILED(hr)) {
  93. error_hr("Failed to create texture");
  94. return false;
  95. }
  96. tex->lpVtbl->SetEvictionPriority(tex, DXGI_RESOURCE_PRIORITY_MAXIMUM);
  97. NV_ENC_REGISTER_RESOURCE res = {NV_ENC_REGISTER_RESOURCE_VER};
  98. res.resourceType = NV_ENC_INPUT_RESOURCE_TYPE_DIRECTX;
  99. res.resourceToRegister = tex;
  100. res.width = enc->cx;
  101. res.height = enc->cy;
  102. res.bufferFormat = p010 ? NV_ENC_BUFFER_FORMAT_YUV420_10BIT : NV_ENC_BUFFER_FORMAT_NV12;
  103. if (NV_FAILED(nv.nvEncRegisterResource(enc->session, &res))) {
  104. tex->lpVtbl->Release(tex);
  105. return false;
  106. }
  107. nvtex->res = res.registeredResource;
  108. nvtex->tex = tex;
  109. nvtex->mapped_res = NULL;
  110. return true;
  111. }
  112. bool d3d11_init_textures(struct nvenc_data *enc)
  113. {
  114. //blog(LOG_DEBUG, "buf count: %d", enc->buf_count);
  115. da_reserve(enc->textures, enc->buf_count);
  116. for (uint32_t i = 0; i < enc->buf_count; i++) {
  117. struct nv_texture texture;
  118. if (!d3d11_texture_init(enc, &texture)) {
  119. return false;
  120. }
  121. da_push_back(enc->textures, &texture);
  122. }
  123. return true;
  124. }
  125. static void d3d11_texture_free(struct nvenc_data *enc, struct nv_texture *nvtex)
  126. {
  127. if (nvtex->res) {
  128. if (nvtex->mapped_res) {
  129. nv.nvEncUnmapInputResource(enc->session, nvtex->mapped_res);
  130. }
  131. nv.nvEncUnregisterResource(enc->session, nvtex->res);
  132. nvtex->tex->lpVtbl->Release(nvtex->tex);
  133. }
  134. }
  135. void d3d11_free_textures(struct nvenc_data *enc)
  136. {
  137. for (size_t i = 0; i < enc->textures.num; i++) {
  138. d3d11_texture_free(enc, &enc->textures.array[i]);
  139. }
  140. }
  141. /* ------------------------------------------------------------------------- */
  142. /* Actual encoding stuff */
  143. static ID3D11Texture2D *get_tex_from_handle(struct nvenc_data *enc, uint32_t handle, IDXGIKeyedMutex **km_out)
  144. {
  145. ID3D11Device *device = enc->device;
  146. IDXGIKeyedMutex *km;
  147. ID3D11Texture2D *input_tex;
  148. HRESULT hr;
  149. for (size_t i = 0; i < enc->input_textures.num; i++) {
  150. struct handle_tex *ht = &enc->input_textures.array[i];
  151. if (ht->handle == handle) {
  152. *km_out = ht->km;
  153. return ht->tex;
  154. }
  155. }
  156. hr = device->lpVtbl->OpenSharedResource(device, (HANDLE)(uintptr_t)handle, &IID_ID3D11Texture2D, &input_tex);
  157. if (FAILED(hr)) {
  158. error_hr("OpenSharedResource failed");
  159. return NULL;
  160. }
  161. hr = input_tex->lpVtbl->QueryInterface(input_tex, &IID_IDXGIKeyedMutex, &km);
  162. if (FAILED(hr)) {
  163. error_hr("QueryInterface(IDXGIKeyedMutex) failed");
  164. input_tex->lpVtbl->Release(input_tex);
  165. return NULL;
  166. }
  167. input_tex->lpVtbl->SetEvictionPriority(input_tex, DXGI_RESOURCE_PRIORITY_MAXIMUM);
  168. *km_out = km;
  169. struct handle_tex new_ht = {handle, input_tex, km};
  170. da_push_back(enc->input_textures, &new_ht);
  171. return input_tex;
  172. }
  173. bool d3d11_encode(void *data, struct encoder_texture *texture, int64_t pts, uint64_t lock_key, uint64_t *next_key,
  174. struct encoder_packet *packet, bool *received_packet)
  175. {
  176. struct nvenc_data *enc = data;
  177. ID3D11DeviceContext *context = enc->context;
  178. ID3D11Texture2D *input_tex;
  179. ID3D11Texture2D *output_tex;
  180. IDXGIKeyedMutex *km;
  181. struct nv_texture *nvtex;
  182. struct nv_bitstream *bs;
  183. if (texture->handle == GS_INVALID_HANDLE) {
  184. error("Encode failed: bad texture handle");
  185. *next_key = lock_key;
  186. return false;
  187. }
  188. bs = &enc->bitstreams.array[enc->next_bitstream];
  189. nvtex = &enc->textures.array[enc->next_bitstream];
  190. input_tex = get_tex_from_handle(enc, texture->handle, &km);
  191. output_tex = nvtex->tex;
  192. if (!input_tex) {
  193. *next_key = lock_key;
  194. return false;
  195. }
  196. deque_push_back(&enc->dts_list, &pts, sizeof(pts));
  197. /* ------------------------------------ */
  198. /* copy to output tex */
  199. km->lpVtbl->AcquireSync(km, lock_key, INFINITE);
  200. context->lpVtbl->CopyResource(context, (ID3D11Resource *)output_tex, (ID3D11Resource *)input_tex);
  201. km->lpVtbl->ReleaseSync(km, *next_key);
  202. /* ------------------------------------ */
  203. /* map output tex so nvenc can use it */
  204. NV_ENC_MAP_INPUT_RESOURCE map = {NV_ENC_MAP_INPUT_RESOURCE_VER};
  205. map.registeredResource = nvtex->res;
  206. if (NV_FAILED(nv.nvEncMapInputResource(enc->session, &map))) {
  207. return false;
  208. }
  209. nvtex->mapped_res = map.mappedResource;
  210. /* ------------------------------------ */
  211. /* do actual encode call */
  212. return nvenc_encode_base(enc, bs, nvtex->mapped_res, pts, packet, received_packet);
  213. }