#include "nvenc-internal.h" #include "nvenc-helpers.h" /* * NVENC implementation using Direct3D 11 context and textures */ /* ------------------------------------------------------------------------- */ /* D3D11 Context/Device management */ static HANDLE get_lib(struct nvenc_data *enc, const char *lib) { HMODULE mod = GetModuleHandleA(lib); if (mod) return mod; mod = LoadLibraryA(lib); if (!mod) error("Failed to load %s", lib); return mod; } typedef HRESULT(WINAPI *CREATEDXGIFACTORY1PROC)(REFIID, void **); bool d3d11_init(struct nvenc_data *enc, obs_data_t *settings) { HMODULE dxgi = get_lib(enc, "DXGI.dll"); HMODULE d3d11 = get_lib(enc, "D3D11.dll"); CREATEDXGIFACTORY1PROC create_dxgi; PFN_D3D11_CREATE_DEVICE create_device; IDXGIFactory1 *factory; IDXGIAdapter *adapter; ID3D11Device *device; ID3D11DeviceContext *context; HRESULT hr; if (!dxgi || !d3d11) { return false; } create_dxgi = (CREATEDXGIFACTORY1PROC)GetProcAddress(dxgi, "CreateDXGIFactory1"); create_device = (PFN_D3D11_CREATE_DEVICE)GetProcAddress(d3d11, "D3D11CreateDevice"); if (!create_dxgi || !create_device) { error("Failed to load D3D11/DXGI procedures"); return false; } hr = create_dxgi(&IID_IDXGIFactory1, &factory); if (FAILED(hr)) { error_hr("CreateDXGIFactory1 failed"); return false; } hr = factory->lpVtbl->EnumAdapters(factory, 0, &adapter); factory->lpVtbl->Release(factory); if (FAILED(hr)) { error_hr("EnumAdapters failed"); return false; } hr = create_device(adapter, D3D_DRIVER_TYPE_UNKNOWN, NULL, 0, NULL, 0, D3D11_SDK_VERSION, &device, NULL, &context); adapter->lpVtbl->Release(adapter); if (FAILED(hr)) { error_hr("D3D11CreateDevice failed"); return false; } enc->device = device; enc->context = context; return true; } void d3d11_free(struct nvenc_data *enc) { for (size_t i = 0; i < enc->input_textures.num; i++) { ID3D11Texture2D *tex = enc->input_textures.array[i].tex; IDXGIKeyedMutex *km = enc->input_textures.array[i].km; tex->lpVtbl->Release(tex); km->lpVtbl->Release(km); } if (enc->context) { enc->context->lpVtbl->Release(enc->context); } if (enc->device) { enc->device->lpVtbl->Release(enc->device); } } /* ------------------------------------------------------------------------- */ /* D3D11 Surface management */ static bool d3d11_texture_init(struct nvenc_data *enc, struct nv_texture *nvtex) { const bool p010 = obs_encoder_video_tex_active(enc->encoder, VIDEO_FORMAT_P010); D3D11_TEXTURE2D_DESC desc = {0}; desc.Width = enc->cx; desc.Height = enc->cy; desc.MipLevels = 1; desc.ArraySize = 1; desc.Format = p010 ? DXGI_FORMAT_P010 : DXGI_FORMAT_NV12; desc.SampleDesc.Count = 1; desc.BindFlags = D3D11_BIND_RENDER_TARGET; ID3D11Device *const device = enc->device; ID3D11Texture2D *tex; HRESULT hr = device->lpVtbl->CreateTexture2D(device, &desc, NULL, &tex); if (FAILED(hr)) { error_hr("Failed to create texture"); return false; } tex->lpVtbl->SetEvictionPriority(tex, DXGI_RESOURCE_PRIORITY_MAXIMUM); NV_ENC_REGISTER_RESOURCE res = {NV_ENC_REGISTER_RESOURCE_VER}; res.resourceType = NV_ENC_INPUT_RESOURCE_TYPE_DIRECTX; res.resourceToRegister = tex; res.width = enc->cx; res.height = enc->cy; res.bufferFormat = p010 ? NV_ENC_BUFFER_FORMAT_YUV420_10BIT : NV_ENC_BUFFER_FORMAT_NV12; if (NV_FAILED(nv.nvEncRegisterResource(enc->session, &res))) { tex->lpVtbl->Release(tex); return false; } nvtex->res = res.registeredResource; nvtex->tex = tex; nvtex->mapped_res = NULL; return true; } bool d3d11_init_textures(struct nvenc_data *enc) { //blog(LOG_DEBUG, "buf count: %d", enc->buf_count); da_reserve(enc->textures, enc->buf_count); for (uint32_t i = 0; i < enc->buf_count; i++) { struct nv_texture texture; if (!d3d11_texture_init(enc, &texture)) { return false; } da_push_back(enc->textures, &texture); } return true; } static void d3d11_texture_free(struct nvenc_data *enc, struct nv_texture *nvtex) { if (nvtex->res) { if (nvtex->mapped_res) { nv.nvEncUnmapInputResource(enc->session, nvtex->mapped_res); } nv.nvEncUnregisterResource(enc->session, nvtex->res); nvtex->tex->lpVtbl->Release(nvtex->tex); } } void d3d11_free_textures(struct nvenc_data *enc) { for (size_t i = 0; i < enc->textures.num; i++) { d3d11_texture_free(enc, &enc->textures.array[i]); } } /* ------------------------------------------------------------------------- */ /* Actual encoding stuff */ static ID3D11Texture2D *get_tex_from_handle(struct nvenc_data *enc, uint32_t handle, IDXGIKeyedMutex **km_out) { ID3D11Device *device = enc->device; IDXGIKeyedMutex *km; ID3D11Texture2D *input_tex; HRESULT hr; for (size_t i = 0; i < enc->input_textures.num; i++) { struct handle_tex *ht = &enc->input_textures.array[i]; if (ht->handle == handle) { *km_out = ht->km; return ht->tex; } } hr = device->lpVtbl->OpenSharedResource(device, (HANDLE)(uintptr_t)handle, &IID_ID3D11Texture2D, &input_tex); if (FAILED(hr)) { error_hr("OpenSharedResource failed"); return NULL; } hr = input_tex->lpVtbl->QueryInterface(input_tex, &IID_IDXGIKeyedMutex, &km); if (FAILED(hr)) { error_hr("QueryInterface(IDXGIKeyedMutex) failed"); input_tex->lpVtbl->Release(input_tex); return NULL; } input_tex->lpVtbl->SetEvictionPriority(input_tex, DXGI_RESOURCE_PRIORITY_MAXIMUM); *km_out = km; struct handle_tex new_ht = {handle, input_tex, km}; da_push_back(enc->input_textures, &new_ht); return input_tex; } bool d3d11_encode(void *data, struct encoder_texture *texture, int64_t pts, uint64_t lock_key, uint64_t *next_key, struct encoder_packet *packet, bool *received_packet) { struct nvenc_data *enc = data; ID3D11DeviceContext *context = enc->context; ID3D11Texture2D *input_tex; ID3D11Texture2D *output_tex; IDXGIKeyedMutex *km; struct nv_texture *nvtex; struct nv_bitstream *bs; if (texture->handle == GS_INVALID_HANDLE) { error("Encode failed: bad texture handle"); *next_key = lock_key; return false; } bs = &enc->bitstreams.array[enc->next_bitstream]; nvtex = &enc->textures.array[enc->next_bitstream]; input_tex = get_tex_from_handle(enc, texture->handle, &km); output_tex = nvtex->tex; if (!input_tex) { *next_key = lock_key; return false; } deque_push_back(&enc->dts_list, &pts, sizeof(pts)); /* ------------------------------------ */ /* copy to output tex */ km->lpVtbl->AcquireSync(km, lock_key, INFINITE); context->lpVtbl->CopyResource(context, (ID3D11Resource *)output_tex, (ID3D11Resource *)input_tex); km->lpVtbl->ReleaseSync(km, *next_key); /* ------------------------------------ */ /* map output tex so nvenc can use it */ NV_ENC_MAP_INPUT_RESOURCE map = {NV_ENC_MAP_INPUT_RESOURCE_VER}; map.registeredResource = nvtex->res; if (NV_FAILED(nv.nvEncMapInputResource(enc->session, &map))) { return false; } nvtex->mapped_res = map.mappedResource; /* ------------------------------------ */ /* do actual encode call */ return nvenc_encode_base(enc, bs, nvtex->mapped_res, pts, packet, received_packet); }