Skip to content

Commit

Permalink
obs-ffmpeg: Add automatic CUDA texture sharing fallback
Browse files Browse the repository at this point in the history
  • Loading branch information
derrod committed Jan 31, 2024
1 parent 3a15ace commit 646fc7b
Show file tree
Hide file tree
Showing 3 changed files with 51 additions and 19 deletions.
1 change: 1 addition & 0 deletions plugins/obs-ffmpeg/obs-nvenc-helpers.c
Original file line number Diff line number Diff line change
Expand Up @@ -277,6 +277,7 @@ static const cuda_function cuda_functions[] = {
{offsetof(CudaFunctions, cuMemcpy2D), "cuMemcpy2D_v2"},

#ifndef _WIN32
{offsetof(CudaFunctions, cuGLGetDevices), "cuGLGetDevices_v2"},
{offsetof(CudaFunctions, cuGraphicsGLRegisterImage),
"cuGraphicsGLRegisterImage"},
{offsetof(CudaFunctions, cuGraphicsUnregisterResource),
Expand Down
67 changes: 48 additions & 19 deletions plugins/obs-ffmpeg/obs-nvenc.c
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,6 @@
/* we cannot guarantee structures haven't changed, so purposely break on
* version change to force the programmer to update or remove backward
* compatibility NVENC code. */
#if CONFIGURED_NVENC_VER != NVENCAPI_VERSION
#error NVENC version changed, update or remove NVENC compatibility code
#endif

#undef NVENCAPI_STRUCT_VERSION
#define NVENCAPI_STRUCT_VERSION(ver) \
Expand Down Expand Up @@ -1229,19 +1226,63 @@ static bool init_cuda_surfaces(struct nvenc_data *enc)
return true;
}

static bool init_cuda_ctx(struct nvenc_data *enc, obs_data_t *settings)
static bool init_cuda_ctx(struct nvenc_data *enc, obs_data_t *settings,
const bool texture)
{
int count;
int count, gpu;
CUdevice device;
const int gpu = (int)obs_data_get_int(settings, "gpu");
bool cuda_override;

/* Allow CUDA device override for texture encoders (experimental) */
if (obs_data_has_user_value(settings, "cuda_device")) {
gpu = (int)obs_data_get_int(settings, "cuda_device");
cuda_override = true;
} else {
gpu = (int)obs_data_get_int(settings, "gpu");
cuda_override = false;
}

CU_FAILED(cu->cuInit(0))
CU_FAILED(cu->cuDeviceGetCount(&count))
if (!count) {
NV_FAIL("No CUDA devices found");
return false;
}
#ifdef _WIN32
CU_FAILED(cu->cuDeviceGet(&device, gpu))
#else
if (!texture || cuda_override) {
CU_FAILED(cu->cuDeviceGet(&device, gpu))
} else {
unsigned int ctx_count = 0;
CUdevice devices[2];

obs_enter_graphics();
CUresult res = cu->cuGLGetDevices(&ctx_count, devices, 2,
CU_GL_DEVICE_LIST_ALL);
obs_leave_graphics();

if (res != CUDA_SUCCESS || !ctx_count) {
/* CUDA_ERROR_INVALID_GRAPHICS_CONTEXT should be treated
* as non-fatal fallback (probably running on iGPU). */
if (res == 219) {
info("Not running on NVIDIA GPU, falling back to non-texture encoder");
} else {
error("Failed to get a CUDA device for the current OpenGL context: %d",
res);
}
return false;
}

/* Documentation indicates this should only ever happen with SLI, i.e. never for OBS. */
if (ctx_count > 1) {
warn("Got more than one CUDA devices for OpenGL context, this is untested.");
}

device = devices[0];
debug("Loading up CUDA on device %u", device);
}
#endif
CU_FAILED(cu->cuCtxCreate(&enc->cu_ctx, 0, device))
CU_FAILED(cu->cuCtxPopCurrent(NULL))

Expand Down Expand Up @@ -1388,7 +1429,7 @@ static void *nvenc_create_internal(enum codec_type codec, obs_data_t *settings,
#ifdef _WIN32
!texture &&
#endif
!init_cuda_ctx(enc, settings)) {
!init_cuda_ctx(enc, settings, texture)) {
goto fail;
}
if (get_nvenc_ver() == COMPATIBILITY_VERSION) {
Expand Down Expand Up @@ -2292,12 +2333,8 @@ struct obs_encoder_info h264_nvenc_soft_info = {
.id = "h264_fallback_nvenc",
.codec = "h264",
.type = OBS_ENCODER_VIDEO,
#ifdef _WIN32
.caps = OBS_ENCODER_CAP_DYN_BITRATE | OBS_ENCODER_CAP_ROI |
OBS_ENCODER_CAP_INTERNAL,
#else
.caps = OBS_ENCODER_CAP_DYN_BITRATE | OBS_ENCODER_CAP_ROI,
#endif
.get_name = h264_nvenc_soft_get_name,
.create = h264_nvenc_soft_create,
.destroy = nvenc_destroy,
Expand All @@ -2315,12 +2352,8 @@ struct obs_encoder_info hevc_nvenc_soft_info = {
.id = "hevc_fallback_nvenc",
.codec = "hevc",
.type = OBS_ENCODER_VIDEO,
#ifdef _WIN32
.caps = OBS_ENCODER_CAP_DYN_BITRATE | OBS_ENCODER_CAP_ROI |
OBS_ENCODER_CAP_INTERNAL,
#else
.caps = OBS_ENCODER_CAP_DYN_BITRATE | OBS_ENCODER_CAP_ROI,
#endif
.get_name = hevc_nvenc_soft_get_name,
.create = hevc_nvenc_soft_create,
.destroy = nvenc_destroy,
Expand All @@ -2338,12 +2371,8 @@ struct obs_encoder_info av1_nvenc_soft_info = {
.id = "av1_fallback_nvenc",
.codec = "av1",
.type = OBS_ENCODER_VIDEO,
#ifdef _WIN32
.caps = OBS_ENCODER_CAP_DYN_BITRATE | OBS_ENCODER_CAP_ROI |
OBS_ENCODER_CAP_INTERNAL,
#else
.caps = OBS_ENCODER_CAP_DYN_BITRATE | OBS_ENCODER_CAP_ROI,
#endif
.get_name = av1_nvenc_soft_get_name,
.create = av1_nvenc_soft_create,
.destroy = nvenc_destroy,
Expand Down
2 changes: 2 additions & 0 deletions plugins/obs-ffmpeg/obs-nvenc.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ typedef struct CudaFunctions {
tcuMemcpy2D_v2 *cuMemcpy2D;

#ifndef _WIN32
tcuGLGetDevices_v2 *cuGLGetDevices;
tcuGraphicsGLRegisterImage *cuGraphicsGLRegisterImage;
tcuGraphicsUnregisterResource *cuGraphicsUnregisterResource;
tcuGraphicsMapResources *cuGraphicsMapResources;
Expand All @@ -47,6 +48,7 @@ extern const char *nv_error_name(NVENCSTATUS err);
extern NV_ENCODE_API_FUNCTION_LIST nv;
extern NV_CREATE_INSTANCE_FUNC nv_create_instance;
extern CudaFunctions *cu;
extern int cuTexDeviceIdx;
extern uint32_t get_nvenc_ver(void);
extern bool init_nvenc(obs_encoder_t *encoder);
extern bool init_cuda(obs_encoder_t *encoder);
Expand Down

0 comments on commit 646fc7b

Please sign in to comment.