Skip to content

Commit 35bad4e

Browse files
codeafridiReenigneArcher
authored andcommitted
harden vulkan encoder fallback and KMS monitor failure handling
1 parent 7822c1f commit 35bad4e

2 files changed

Lines changed: 72 additions & 30 deletions

File tree

src/platform/linux/kmsgrab.cpp

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -611,6 +611,11 @@ namespace platf {
611611
int init(const std::string &display_name, const ::video::config_t &config) {
612612
delay = std::chrono::nanoseconds {1s} / config.framerate;
613613

614+
if (kms::card_descriptors.empty()) {
615+
BOOST_LOG(error) << "No KMS monitor descriptors are available; aborting monitor lookup for ["sv << display_name << ']';
616+
return -1;
617+
}
618+
614619
int monitor_index = util::from_view(display_name);
615620
int monitor = 0;
616621

@@ -777,7 +782,7 @@ namespace platf {
777782
}
778783
}
779784

780-
BOOST_LOG(error) << "Couldn't find monitor ["sv << monitor_index << ']';
785+
BOOST_LOG(error) << "Couldn't find monitor ["sv << monitor_index << "] in "sv << monitor << " enumerated KMS monitor(s)"sv;
781786
return -1;
782787

783788
// Neatly break from nested for loop
@@ -1616,6 +1621,12 @@ namespace platf {
16161621
std::vector<std::string> kms_display_names(mem_type_e hwdevice_type) {
16171622
int count = 0;
16181623

1624+
kms::env_width = 0;
1625+
kms::env_height = 0;
1626+
kms::env_logical_width = 0;
1627+
kms::env_logical_height = 0;
1628+
kms::card_descriptors.clear();
1629+
16191630
if (!fs::exists("/dev/dri")) {
16201631
BOOST_LOG(warning) << "Couldn't find /dev/dri, kmsgrab won't be enabled"sv;
16211632
return {};
@@ -1728,13 +1739,14 @@ namespace platf {
17281739
correlate_to_wayland(cds);
17291740
}
17301741

1731-
// Deduce the full virtual desktop size
1732-
kms::env_width = 0;
1733-
kms::env_height = 0;
1742+
BOOST_LOG(debug) << "Enumerated "sv << display_names.size() << " KMS monitor(s)"sv;
17341743

1735-
kms::env_logical_width = 0;
1736-
kms::env_logical_height = 0;
1744+
if (display_names.empty()) {
1745+
BOOST_LOG(error) << "No KMS monitors were found during enumeration"sv;
1746+
return {};
1747+
}
17371748

1749+
// Deduce the full virtual desktop size
17381750
for (auto &card_descriptor : cds) {
17391751
for (auto &[_, monitor_descriptor] : card_descriptor.crtc_to_monitor) {
17401752
BOOST_LOG(debug) << "Monitor description"sv;

src/video.cpp

Lines changed: 54 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1181,6 +1181,8 @@ namespace video {
11811181
int active_av1_mode;
11821182
bool last_encoder_probe_supported_ref_frames_invalidation = false;
11831183
std::array<bool, 3> last_encoder_probe_supported_yuv444_for_codec = {};
1184+
constexpr auto no_display_available_msg = "Unable to start capture because no display is available"sv;
1185+
constexpr auto no_encoder_selected_msg = "No encoder selected; aborting capture instead of using invalid encoder state"sv;
11841186

11851187
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) {
11861188
// We try this twice, in case we still get an error on reinitialization
@@ -1214,16 +1216,18 @@ namespace video {
12141216
}
12151217

12161218
// Refresh the display names
1217-
auto old_display_names = std::move(display_names);
1219+
auto had_display_names = !display_names.empty();
12181220
display_names = platf::display_names(dev_type);
12191221

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);
1222+
// If we now have no displays, fail instead of reusing stale display names.
1223+
if (display_names.empty()) {
1224+
if (!had_display_names && !output_name.empty()) {
1225+
display_names.emplace_back(output_name);
1226+
} else {
1227+
BOOST_LOG(error) << (had_display_names ? "No displays were found after reenumeration!"sv : "No displays were found during enumeration!"sv);
1228+
current_display_index = -1;
1229+
return;
1230+
}
12271231
}
12281232

12291233
// We now have a new display name list, so reset the index back to 0
@@ -1284,6 +1288,10 @@ namespace video {
12841288
std::vector<std::string> display_names;
12851289
int display_p = -1;
12861290
refresh_displays(encoder.platform_formats->dev_type, display_names, display_p);
1291+
if (display_p < 0 || display_names.empty()) {
1292+
BOOST_LOG(error) << no_display_available_msg;
1293+
return;
1294+
}
12871295
auto disp = platf::display(encoder.platform_formats->dev_type, display_names[display_p], capture_ctxs.front().config);
12881296
if (!disp) {
12891297
return;
@@ -1897,27 +1905,34 @@ namespace video {
18971905
// Allow the encoding device a final opportunity to set/unset or override any options
18981906
encode_device->init_codec_options(ctx.get(), &options);
18991907

1900-
if (auto status = avcodec_open2(ctx.get(), codec, &options)) {
1901-
char err_str[AV_ERROR_MAX_STRING_SIZE] {0};
1908+
auto status = avcodec_open2(ctx.get(), codec, &options);
1909+
if (!status) {
1910+
// Successfully opened the codec
1911+
break;
1912+
}
19021913

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);
1914+
char err_str[AV_ERROR_MAX_STRING_SIZE] {0};
1915+
if (!video_format.fallback_options.empty() && retries == 0) {
1916+
BOOST_LOG(info)
1917+
<< "Retrying with fallback configuration options for ["sv << video_format.name << "] after error: "sv
1918+
<< av_make_error_string(err_str, AV_ERROR_MAX_STRING_SIZE, status);
19071919

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);
1920+
continue;
1921+
}
19141922

1915-
return nullptr;
1916-
}
1923+
BOOST_LOG(error)
1924+
<< "Could not open codec ["sv
1925+
<< video_format.name << "]: "sv
1926+
<< av_make_error_string(err_str, AV_ERROR_MAX_STRING_SIZE, status);
1927+
1928+
if (encoder.name == "vulkan"sv) {
1929+
BOOST_LOG(error)
1930+
<< "Vulkan encoder setup failed for ["sv << video_format.name
1931+
<< "]; this GPU or driver does not expose the required Vulkan video encode capability. "
1932+
<< "Sunshine will discard this encoder and continue probing fallback encoders."sv;
19171933
}
19181934

1919-
// Successfully opened the codec
1920-
break;
1935+
return nullptr;
19211936
}
19221937

19231938
avcodec_frame_t frame {av_frame_alloc()};
@@ -2286,6 +2301,10 @@ namespace video {
22862301
while (encode_session_ctx_queue.running()) {
22872302
// Refresh display names since a display removal might have caused the reinitialization
22882303
refresh_displays(encoder.platform_formats->dev_type, display_names, display_p);
2304+
if (display_p < 0 || display_names.empty()) {
2305+
BOOST_LOG(error) << no_display_available_msg;
2306+
return encode_e::error;
2307+
}
22892308

22902309
// Process any pending display switch with the new list of displays
22912310
if (switch_display_event->peek()) {
@@ -2491,6 +2510,11 @@ namespace video {
24912510
display = ref->display_wp->lock();
24922511
}
24932512

2513+
if (!chosen_encoder) {
2514+
BOOST_LOG(error) << no_encoder_selected_msg;
2515+
return;
2516+
}
2517+
24942518
auto &encoder = *chosen_encoder;
24952519

24962520
auto encode_device = make_encode_device(*display, encoder, config);
@@ -2533,6 +2557,11 @@ namespace video {
25332557
) {
25342558
auto idr_events = mail->event<bool>(mail::idr);
25352559

2560+
if (!chosen_encoder) {
2561+
BOOST_LOG(error) << no_encoder_selected_msg;
2562+
return;
2563+
}
2564+
25362565
idr_events->raise(true);
25372566
if (chosen_encoder->flags & PARALLEL_ENCODING) {
25382567
capture_async(std::move(mail), config, channel_data);
@@ -3013,6 +3042,7 @@ namespace video {
30133042

30143043
if (encode_device && encode_device->data) {
30153044
if (((vulkan_init_avcodec_hardware_input_buffer_fn) encode_device->data)(encode_device, &hw_device_buf)) {
3045+
BOOST_LOG(error) << "Failed to create a Vulkan device from the capture backend; aborting Vulkan encoder setup"sv;
30163046
return -1;
30173047
}
30183048
return hw_device_buf;

0 commit comments

Comments
 (0)