Prechádzať zdrojové kódy

obs-nvenc: Add SDK 13.0 features

derrod 1 rok pred
rodič
commit
86f98beaf1

+ 1 - 0
plugins/obs-nvenc/data/locale/en-US.ini

@@ -50,6 +50,7 @@ SplitEncode.Auto="Auto"
 SplitEncode.Disabled="Disabled"
 SplitEncode.Enabled="Two-way split"
 SplitEncode.ThreeWay="Three-way split"
+SplitEncode.FourWay="Four-way split"
 
 Opts="Custom Encoder Options"
 Opts.TT="Space-separated list of options to apply to the rate control and codec settings,\nbased their names in the nvEncodeAPI header.\ne.g. \"lookaheadDepth=16 aqStrength=4\""

+ 2 - 0
plugins/obs-nvenc/nvenc-helpers.c

@@ -281,6 +281,8 @@ static void read_codec_caps(config_t *config, enum codec_type codec, const char
 	caps->temporal_aq = config_get_bool(config, section, "temporal_aq");
 	caps->ten_bit = config_get_bool(config, section, "10bit");
 	caps->four_four_four = config_get_bool(config, section, "yuv_444");
+	caps->four_two_two = config_get_bool(config, section, "yuv_422");
+	caps->uhq = config_get_bool(config, section, "uhq");
 }
 
 static bool nvenc_check(void)

+ 8 - 3
plugins/obs-nvenc/nvenc-helpers.h

@@ -18,6 +18,10 @@
 #define NVENC_12_2_OR_LATER
 #endif
 
+#if NVENCAPI_MAJOR_VERSION >= 13
+#define NVENC_13_0_OR_LATER
+#endif
+
 enum codec_type {
 	CODEC_H264,
 	CODEC_HEVC,
@@ -46,18 +50,19 @@ struct encoder_caps {
 	int max_width;
 	int max_height;
 
-	/* These don't seem to work correctly, thanks NVIDIA. */
-	int temporal_filter;
-	int lookahead_level;
+	int temporal_filter; /* Broken prior to the 551.21 driver. */
+	int lookahead_level; /* Broken prior to the 570.20 driver. */
 
 	bool dyn_bitrate;
 	bool lookahead;
 	bool lossless;
 	bool temporal_aq;
+	bool uhq;
 
 	/* Yeah... */
 	bool ten_bit;
 	bool four_four_four;
+	bool four_two_two;
 };
 
 typedef NVENCSTATUS(NVENCAPI *NV_CREATE_INSTANCE_FUNC)(NV_ENCODE_API_FUNCTION_LIST *);

+ 5 - 0
plugins/obs-nvenc/nvenc-internal.h

@@ -108,6 +108,11 @@ struct nvenc_data {
 	size_t roi_map_size;
 	uint32_t roi_increment;
 
+#ifdef NVENC_13_0_OR_LATER
+	CONTENT_LIGHT_LEVEL *cll;
+	MASTERING_DISPLAY_INFO *mdi;
+#endif
+
 	struct nvenc_properties props;
 
 	CUcontext cu_ctx;

+ 6 - 0
plugins/obs-nvenc/nvenc-opts-parser.c

@@ -129,6 +129,9 @@ static bool apply_h264_opt(struct obs_option *opt, NV_ENC_CONFIG_H264 *nv_conf)
 
 	APPLY_INT_OPT(idrPeriod, uint32_t, PRIu32)
 	APPLY_INT_OPT(useBFramesAsRef, NV_ENC_BFRAME_REF_MODE, PRIu32)
+#ifdef NVENC_13_0_OR_LATER
+	APPLY_INT_OPT(tfLevel, NV_ENC_TEMPORAL_FILTER_LEVEL, PRIu32)
+#endif
 
 	APPLY_BIT_OPT(enableFillerDataInsertion, 1)
 
@@ -162,6 +165,9 @@ static bool apply_av1_opt(struct obs_option *opt, NV_ENC_CONFIG_AV1 *nv_conf)
 	APPLY_INT_OPT(numTileRows, uint32_t, PRIu32)
 	APPLY_INT_OPT(idrPeriod, uint32_t, PRIu32)
 	APPLY_INT_OPT(useBFramesAsRef, NV_ENC_BFRAME_REF_MODE, PRIu32)
+#ifdef NVENC_13_0_OR_LATER
+	APPLY_INT_OPT(tfLevel, NV_ENC_TEMPORAL_FILTER_LEVEL, PRIu32)
+#endif
 
 	APPLY_BIT_OPT(enableBitstreamPadding, 1)
 

+ 13 - 6
plugins/obs-nvenc/nvenc-properties.c

@@ -157,13 +157,10 @@ obs_properties_t *nvenc_properties_internal(enum codec_type codec)
 				    OBS_COMBO_FORMAT_STRING);
 
 #define add_tune(val) obs_property_list_add_string(p, obs_module_text("Tuning." val), val)
-#ifdef NVENC_12_2_OR_LATER
-	/* The UHQ tune is only supported on Turing or later.
-	 * It uses the temporal filtering feature, so we can use its
-	 * availability as an indicator that we are on a supported GPU. */
-	if (codec == CODEC_HEVC && caps->temporal_filter)
+	/* The UHQ tune is only supported on Turing or later. */
+	if (caps->uhq)
 		add_tune("uhq");
-#endif
+
 	add_tune("hq");
 	add_tune("ll");
 	add_tune("ull");
@@ -189,6 +186,10 @@ obs_properties_t *nvenc_properties_internal(enum codec_type codec)
 	} else if (codec == CODEC_AV1) {
 		add_profile("main");
 	} else {
+#ifdef NVENC_13_0_OR_LATER
+		if (caps->ten_bit)
+			add_profile("high10");
+#endif
 		add_profile("high");
 		add_profile("main");
 		add_profile("baseline");
@@ -244,6 +245,12 @@ obs_properties_t *nvenc_properties_internal(enum codec_type codec)
 			obs_property_list_add_int(p, obs_module_text("SplitEncode.ThreeWay"),
 						  NV_ENC_SPLIT_THREE_FORCED_MODE);
 		}
+#ifdef NVENC_13_0_OR_LATER
+		if (caps->engines > 3) {
+			obs_property_list_add_int(p, obs_module_text("SplitEncode.FourWay"),
+						  NV_ENC_SPLIT_FOUR_FORCED_MODE);
+		}
+#endif
 	}
 #endif
 

+ 82 - 1
plugins/obs-nvenc/nvenc.c

@@ -214,6 +214,11 @@ static bool is_10_bit(const struct nvenc_data *enc)
 				: obs_encoder_video_tex_active(enc->encoder, VIDEO_FORMAT_P010);
 }
 
+static bool is_hdr(const enum video_colorspace space)
+{
+	return space == VIDEO_CS_2100_HLG || space == VIDEO_CS_2100_PQ;
+}
+
 static bool init_encoder_base(struct nvenc_data *enc, obs_data_t *settings)
 {
 	UNUSED_PARAMETER(settings);
@@ -481,6 +486,13 @@ static bool init_encoder_h264(struct nvenc_data *enc, obs_data_t *settings)
 	if (enc->in_format == VIDEO_FORMAT_I444) {
 		config->profileGUID = NV_ENC_H264_PROFILE_HIGH_444_GUID;
 		h264_config->chromaFormatIDC = 3;
+#ifdef NVENC_13_0_OR_LATER
+	} else if (astrcmpi(enc->props.profile, "high10") == 0) {
+		config->profileGUID = NV_ENC_H264_PROFILE_HIGH_10_GUID;
+	} else if (is_10_bit(enc)) {
+		warn("Forcing high10 for P010");
+		config->profileGUID = NV_ENC_H264_PROFILE_HIGH_10_GUID;
+#endif
 	} else if (astrcmpi(enc->props.profile, "main") == 0) {
 		config->profileGUID = NV_ENC_H264_PROFILE_MAIN_GUID;
 	} else if (astrcmpi(enc->props.profile, "baseline") == 0) {
@@ -489,6 +501,14 @@ static bool init_encoder_h264(struct nvenc_data *enc, obs_data_t *settings)
 		config->profileGUID = NV_ENC_H264_PROFILE_HIGH_GUID;
 	}
 
+#ifdef NVENC_13_0_OR_LATER
+	/* Note: Only supported on Blackwell! */
+	h264_config->inputBitDepth = is_10_bit(enc) ? NV_ENC_BIT_DEPTH_10 : NV_ENC_BIT_DEPTH_8;
+	h264_config->outputBitDepth = memcmp(&config->profileGUID, &NV_ENC_H264_PROFILE_HIGH_10_GUID, sizeof(GUID)) == 0
+					      ? NV_ENC_BIT_DEPTH_10
+					      : NV_ENC_BIT_DEPTH_8;
+#endif
+
 	if (!apply_user_args(enc)) {
 		obs_encoder_set_last_error(enc->encoder, obs_module_text("Opts.Invalid"));
 		return false;
@@ -587,7 +607,7 @@ static bool init_encoder_hevc(struct nvenc_data *enc, obs_data_t *settings)
 		config->profileGUID = NV_ENC_HEVC_PROFILE_MAIN10_GUID;
 		profile_is_10bpc = true;
 	} else if (is_10_bit(enc)) {
-		blog(LOG_WARNING, "[obs-nvenc] Forcing main10 for P010");
+		warn("Forcing main10 for P010");
 		config->profileGUID = NV_ENC_HEVC_PROFILE_MAIN10_GUID;
 		profile_is_10bpc = true;
 	} else {
@@ -601,6 +621,13 @@ static bool init_encoder_hevc(struct nvenc_data *enc, obs_data_t *settings)
 	hevc_config->outputBitDepth = profile_is_10bpc ? NV_ENC_BIT_DEPTH_10 : NV_ENC_BIT_DEPTH_8;
 #endif
 
+#ifdef NVENC_13_0_OR_LATER
+	if (is_10_bit(enc) && is_hdr(voi->colorspace)) {
+		hevc_config->outputMasteringDisplay = 1;
+		hevc_config->outputMaxCll = 1;
+	}
+#endif
+
 	if (!apply_user_args(enc)) {
 		obs_encoder_set_last_error(enc->encoder, obs_module_text("Opts.Invalid"));
 		return false;
@@ -688,6 +715,13 @@ static bool init_encoder_av1(struct nvenc_data *enc, obs_data_t *settings)
 	av1_config->numBwdRefs = 1;
 	av1_config->repeatSeqHdr = 1;
 
+#ifdef NVENC_13_0_OR_LATER
+	if (is_10_bit(enc) && is_hdr(voi->colorspace)) {
+		av1_config->outputMasteringDisplay = 1;
+		av1_config->outputMaxCll = 1;
+	}
+#endif
+
 	if (!apply_user_args(enc)) {
 		obs_encoder_set_last_error(enc->encoder, obs_module_text("Opts.Invalid"));
 		return false;
@@ -774,6 +808,31 @@ static bool init_encoder(struct nvenc_data *enc, enum codec_type codec, obs_data
 		}
 	}
 
+#ifdef NVENC_13_0_OR_LATER
+	const bool pq = voi->colorspace == VIDEO_CS_2100_PQ;
+	const bool hlg = voi->colorspace == VIDEO_CS_2100_HLG;
+	if (pq || hlg) {
+		enc->cll = bzalloc(sizeof(CONTENT_LIGHT_LEVEL));
+		enc->mdi = bzalloc(sizeof(MASTERING_DISPLAY_INFO));
+		const uint16_t hdr_nominal_peak_level = pq ? (uint16_t)obs_get_video_hdr_nominal_peak_level()
+							   : (hlg ? 1000 : 0);
+		/* Currently these are hardcoded across all encoders. */
+		enc->mdi->r.x = 13250;
+		enc->mdi->r.y = 34500;
+		enc->mdi->g.x = 7500;
+		enc->mdi->g.y = 3000;
+		enc->mdi->b.x = 34000;
+		enc->mdi->b.y = 16000;
+		enc->mdi->whitePoint.x = 15635;
+		enc->mdi->whitePoint.y = 16450;
+		enc->mdi->maxLuma = hdr_nominal_peak_level * 10000;
+		enc->mdi->minLuma = 0;
+
+		enc->cll->maxContentLightLevel = hdr_nominal_peak_level;
+		enc->cll->maxPicAverageLightLevel = hdr_nominal_peak_level;
+	}
+#endif
+
 	switch (enc->codec) {
 	case CODEC_HEVC:
 		return init_encoder_hevc(enc, settings);
@@ -986,6 +1045,13 @@ static void nvenc_destroy(void *data)
 	bfree(enc->sei);
 	bfree(enc->roi_map);
 
+#ifdef NVENC_13_0_OR_LATER
+	if (enc->mdi)
+		bfree(enc->mdi);
+	if (enc->cll)
+		bfree(enc->cll);
+#endif
+
 	deque_free(&enc->dts_list);
 
 	da_free(enc->surfaces);
@@ -1225,6 +1291,21 @@ bool nvenc_encode_base(struct nvenc_data *enc, struct nv_bitstream *bs, void *pi
 					   : NV_ENC_BUFFER_FORMAT_NV12;
 	}
 
+#ifdef NVENC_13_0_OR_LATER
+	if (enc->cll) {
+		if (enc->codec == CODEC_AV1)
+			params.codecPicParams.av1PicParams.pMaxCll = enc->cll;
+		else if (enc->codec == CODEC_HEVC)
+			params.codecPicParams.hevcPicParams.pMaxCll = enc->cll;
+	}
+	if (enc->mdi) {
+		if (enc->codec == CODEC_AV1)
+			params.codecPicParams.av1PicParams.pMasteringDisplay = enc->mdi;
+		else if (enc->codec == CODEC_HEVC)
+			params.codecPicParams.hevcPicParams.pMasteringDisplay = enc->mdi;
+	}
+#endif
+
 	/* Add ROI map if enabled */
 	if (obs_encoder_has_roi(enc->encoder))
 		add_roi(enc, &params);