@@ -31,6 +31,12 @@ extern "C" {
3131#include " sync.h"
3232#include " video.h"
3333
34+ #ifdef SUNSHINE_BUILD_VULKAN
35+ #if defined(__linux__) || defined(linux) || defined(__linux) || defined(__FreeBSD__)
36+ #include " platform/linux/vulkan_encode.h"
37+ #endif
38+ #endif
39+
3440#ifdef _WIN32
3541extern " C" {
3642 #include < libavutil/hwcontext_d3d11va.h>
@@ -1181,6 +1187,8 @@ namespace video {
11811187 int active_av1_mode;
11821188 bool last_encoder_probe_supported_ref_frames_invalidation = false ;
11831189 std::array<bool , 3 > last_encoder_probe_supported_yuv444_for_codec = {};
1190+ constexpr auto no_display_available_msg = " Unable to start capture because no display is available" sv;
1191+ constexpr auto no_encoder_selected_msg = " No encoder selected; aborting capture instead of using invalid encoder state" sv;
11841192
11851193 void reset_display (std::shared_ptr<platf::display_t > &disp, const platf::mem_type_e &type, const std::string &display_name, const config_t &config) {
11861194 // We try this twice, in case we still get an error on reinitialization
@@ -1214,16 +1222,18 @@ namespace video {
12141222 }
12151223
12161224 // Refresh the display names
1217- auto old_display_names = std::move ( display_names);
1225+ auto had_display_names = ! display_names. empty ( );
12181226 display_names = platf::display_names (dev_type);
12191227
1220- // If we now have no displays, let's put the old display array back and fail
1221- if (display_names.empty () && !old_display_names.empty ()) {
1222- BOOST_LOG (error) << " No displays were found after reenumeration!" sv;
1223- display_names = std::move (old_display_names);
1224- return ;
1225- } else if (display_names.empty ()) {
1226- display_names.emplace_back (output_name);
1228+ // If we now have no displays, fail instead of reusing stale display names.
1229+ if (display_names.empty ()) {
1230+ if (!had_display_names && !output_name.empty ()) {
1231+ display_names.emplace_back (output_name);
1232+ } else {
1233+ BOOST_LOG (error) << (had_display_names ? " No displays were found after reenumeration!" sv : " No displays were found during enumeration!" sv);
1234+ current_display_index = -1 ;
1235+ return ;
1236+ }
12271237 }
12281238
12291239 // We now have a new display name list, so reset the index back to 0
@@ -1284,6 +1294,10 @@ namespace video {
12841294 std::vector<std::string> display_names;
12851295 int display_p = -1 ;
12861296 refresh_displays (encoder.platform_formats ->dev_type , display_names, display_p);
1297+ if (display_p < 0 || display_names.empty ()) {
1298+ BOOST_LOG (error) << no_display_available_msg;
1299+ return ;
1300+ }
12871301 auto disp = platf::display (encoder.platform_formats ->dev_type , display_names[display_p], capture_ctxs.front ().config );
12881302 if (!disp) {
12891303 return ;
@@ -1897,27 +1911,34 @@ namespace video {
18971911 // Allow the encoding device a final opportunity to set/unset or override any options
18981912 encode_device->init_codec_options (ctx.get (), &options);
18991913
1900- if (auto status = avcodec_open2 (ctx.get (), codec, &options)) {
1901- char err_str[AV_ERROR_MAX_STRING_SIZE ] {0 };
1914+ auto status = avcodec_open2 (ctx.get (), codec, &options);
1915+ if (!status) {
1916+ // Successfully opened the codec
1917+ break ;
1918+ }
19021919
1903- if (!video_format.fallback_options .empty () && retries == 0 ) {
1904- BOOST_LOG (info)
1905- << " Retrying with fallback configuration options for [" sv << video_format.name << " ] after error: " sv
1906- << av_make_error_string (err_str, AV_ERROR_MAX_STRING_SIZE , status);
1920+ char err_str[AV_ERROR_MAX_STRING_SIZE ] {0 };
1921+ if (!video_format.fallback_options .empty () && retries == 0 ) {
1922+ BOOST_LOG (info)
1923+ << " Retrying with fallback configuration options for [" sv << video_format.name << " ] after error: " sv
1924+ << av_make_error_string (err_str, AV_ERROR_MAX_STRING_SIZE , status);
19071925
1908- continue ;
1909- } else {
1910- BOOST_LOG (error)
1911- << " Could not open codec [" sv
1912- << video_format.name << " ]: " sv
1913- << av_make_error_string (err_str, AV_ERROR_MAX_STRING_SIZE , status);
1926+ continue ;
1927+ }
19141928
1915- return nullptr ;
1916- }
1929+ BOOST_LOG (error)
1930+ << " Could not open codec [" sv
1931+ << video_format.name << " ]: " sv
1932+ << av_make_error_string (err_str, AV_ERROR_MAX_STRING_SIZE , status);
1933+
1934+ if (encoder.name == " vulkan" sv) {
1935+ BOOST_LOG (error)
1936+ << " Vulkan encoder setup failed for [" sv << video_format.name
1937+ << " ]; this GPU or driver does not expose the required Vulkan video encode capability. "
1938+ << " Sunshine will discard this encoder and continue probing fallback encoders." sv;
19171939 }
19181940
1919- // Successfully opened the codec
1920- break ;
1941+ return nullptr ;
19211942 }
19221943
19231944 avcodec_frame_t frame {av_frame_alloc ()};
@@ -2286,6 +2307,10 @@ namespace video {
22862307 while (encode_session_ctx_queue.running ()) {
22872308 // Refresh display names since a display removal might have caused the reinitialization
22882309 refresh_displays (encoder.platform_formats ->dev_type , display_names, display_p);
2310+ if (display_p < 0 || display_names.empty ()) {
2311+ BOOST_LOG (error) << no_display_available_msg;
2312+ return encode_e::error;
2313+ }
22892314
22902315 // Process any pending display switch with the new list of displays
22912316 if (switch_display_event->peek ()) {
@@ -2491,6 +2516,11 @@ namespace video {
24912516 display = ref->display_wp ->lock ();
24922517 }
24932518
2519+ if (!chosen_encoder) {
2520+ BOOST_LOG (error) << no_encoder_selected_msg;
2521+ return ;
2522+ }
2523+
24942524 auto &encoder = *chosen_encoder;
24952525
24962526 auto encode_device = make_encode_device (*display, encoder, config);
@@ -2533,6 +2563,11 @@ namespace video {
25332563 ) {
25342564 auto idr_events = mail->event <bool >(mail::idr);
25352565
2566+ if (!chosen_encoder) {
2567+ BOOST_LOG (error) << no_encoder_selected_msg;
2568+ return ;
2569+ }
2570+
25362571 idr_events->raise (true );
25372572 if (chosen_encoder->flags & PARALLEL_ENCODING ) {
25382573 capture_async (std::move (mail), config, channel_data);
@@ -2628,6 +2663,14 @@ namespace video {
26282663 encoder.hevc .capabilities .set ();
26292664 encoder.av1 .capabilities .set ();
26302665
2666+ #ifdef SUNSHINE_BUILD_VULKAN
2667+ if (encoder.name == " vulkan" sv && !vk::validate ()) {
2668+ fg.disable ();
2669+ BOOST_LOG (info) << " Encoder [" sv << encoder.name << " ] is not supported on this GPU" sv;
2670+ return false ;
2671+ }
2672+ #endif
2673+
26312674 // First, test encoder viability
26322675 config_t config_max_ref_frames {1920 , 1080 , 60 , 6000 , 1000 , 1 , 1 , 1 , 0 , 0 , 0 };
26332676 config_t config_autoselect {1920 , 1080 , 60 , 6000 , 1000 , 1 , 0 , 1 , 0 , 0 , 0 };
@@ -3013,6 +3056,7 @@ namespace video {
30133056
30143057 if (encode_device && encode_device->data ) {
30153058 if (((vulkan_init_avcodec_hardware_input_buffer_fn) encode_device->data )(encode_device, &hw_device_buf)) {
3059+ BOOST_LOG (error) << " Failed to create a Vulkan device from the capture backend; aborting Vulkan encoder setup" sv;
30163060 return -1 ;
30173061 }
30183062 return hw_device_buf;
0 commit comments