0
0
mirror of https://github.com/obsproject/obs-studio.git synced 2024-09-20 04:42:18 +02:00
obs-studio/plugins/obs-nvenc/nvenc-d3d11.c
2024-08-08 22:52:40 +02:00

279 lines
7.1 KiB
C

#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_p010_tex_active();
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);
}