diff --git a/src/cbs.cpp b/src/cbs.cpp index c06b7c4a..5ddb1538 100644 --- a/src/cbs.cpp +++ b/src/cbs.cpp @@ -16,101 +16,102 @@ extern "C" { using namespace std::literals; namespace cbs { - void - close(CodedBitstreamContext *c) { - ff_cbs_close(&c); - } +void close(CodedBitstreamContext *c) { ff_cbs_close(&c); } - using ctx_t = util::safe_ptr; +using ctx_t = util::safe_ptr; - class frag_t: public CodedBitstreamFragment { - public: +class frag_t : public CodedBitstreamFragment { + public: frag_t(frag_t &&o) { - std::copy((std::uint8_t *) &o, (std::uint8_t *) (&o + 1), (std::uint8_t *) this); + std::copy((std::uint8_t *)&o, (std::uint8_t *)(&o + 1), + (std::uint8_t *)this); - o.data = nullptr; - o.units = nullptr; + o.data = nullptr; + o.units = nullptr; }; - frag_t() { - std::fill_n((std::uint8_t *) this, sizeof(*this), 0); - } + frag_t() { std::fill_n((std::uint8_t *)this, sizeof(*this), 0); } - frag_t & - operator=(frag_t &&o) { - std::copy((std::uint8_t *) &o, (std::uint8_t *) (&o + 1), (std::uint8_t *) this); + frag_t &operator=(frag_t &&o) { + std::copy((std::uint8_t *)&o, (std::uint8_t *)(&o + 1), + (std::uint8_t *)this); - o.data = nullptr; - o.units = nullptr; + o.data = nullptr; + o.units = nullptr; - return *this; + return *this; }; ~frag_t() { - if (data || units) { - ff_cbs_fragment_free(this); - } + if (data || units) { + ff_cbs_fragment_free(this); + } } - }; +}; - util::buffer_t - write(cbs::ctx_t &cbs_ctx, std::uint8_t nal, void *uh, AVCodecID codec_id) { +util::buffer_t write(cbs::ctx_t &cbs_ctx, std::uint8_t nal, + void *uh, AVCodecID codec_id) { cbs::frag_t frag; auto err = ff_cbs_insert_unit_content(&frag, -1, nal, uh, nullptr); if (err < 0) { - char err_str[AV_ERROR_MAX_STRING_SIZE] { 0 }; - BOOST_LOG(error) << "Could not insert NAL unit SPS: "sv << av_make_error_string(err_str, AV_ERROR_MAX_STRING_SIZE, err); + char err_str[AV_ERROR_MAX_STRING_SIZE]{0}; + BOOST_LOG(error) << "Could not insert NAL unit SPS: "sv + << av_make_error_string(err_str, + AV_ERROR_MAX_STRING_SIZE, err); - return {}; + return {}; } err = ff_cbs_write_fragment_data(cbs_ctx.get(), &frag); if (err < 0) { - char err_str[AV_ERROR_MAX_STRING_SIZE] { 0 }; - BOOST_LOG(error) << "Could not write fragment data: "sv << av_make_error_string(err_str, AV_ERROR_MAX_STRING_SIZE, err); + char err_str[AV_ERROR_MAX_STRING_SIZE]{0}; + BOOST_LOG(error) << "Could not write fragment data: "sv + << av_make_error_string(err_str, + AV_ERROR_MAX_STRING_SIZE, err); - return {}; + return {}; } // frag.data_size * 8 - frag.data_bit_padding == bits in fragment - util::buffer_t data { frag.data_size }; + util::buffer_t data{frag.data_size}; std::copy_n(frag.data, frag.data_size, std::begin(data)); return data; - } +} - util::buffer_t - write(std::uint8_t nal, void *uh, AVCodecID codec_id) { +util::buffer_t write(std::uint8_t nal, void *uh, + AVCodecID codec_id) { cbs::ctx_t cbs_ctx; ff_cbs_init(&cbs_ctx, codec_id, nullptr); return write(cbs_ctx, nal, uh, codec_id); - } +} - h264_t - make_sps_h264(const AVCodecContext *avctx, const AVPacket *packet) { +h264_t make_sps_h264(const AVCodecContext *avctx, const AVPacket *packet) { cbs::ctx_t ctx; if (ff_cbs_init(&ctx, AV_CODEC_ID_H264, nullptr)) { - return {}; + return {}; } cbs::frag_t frag; int err = ff_cbs_read_packet(ctx.get(), &frag, packet); if (err < 0) { - char err_str[AV_ERROR_MAX_STRING_SIZE] { 0 }; - BOOST_LOG(error) << "Couldn't read packet: "sv << av_make_error_string(err_str, AV_ERROR_MAX_STRING_SIZE, err); + char err_str[AV_ERROR_MAX_STRING_SIZE]{0}; + BOOST_LOG(error) << "Couldn't read packet: "sv + << av_make_error_string(err_str, + AV_ERROR_MAX_STRING_SIZE, err); - return {}; + return {}; } - auto sps_p = ((CodedBitstreamH264Context *) ctx->priv_data)->active_sps; + auto sps_p = ((CodedBitstreamH264Context *)ctx->priv_data)->active_sps; // This is a very large struct that cannot safely be stored on the stack auto sps = std::make_unique(*sps_p); if (avctx->refs > 0) { - sps->max_num_ref_frames = avctx->refs; + sps->max_num_ref_frames = avctx->refs; } sps->vui_parameters_present_flag = 1; @@ -138,31 +139,32 @@ namespace cbs { cbs::ctx_t write_ctx; ff_cbs_init(&write_ctx, AV_CODEC_ID_H264, nullptr); - return h264_t { - write(write_ctx, sps->nal_unit_header.nal_unit_type, (void *) &sps->nal_unit_header, AV_CODEC_ID_H264), - write(ctx, sps_p->nal_unit_header.nal_unit_type, (void *) &sps_p->nal_unit_header, AV_CODEC_ID_H264) - }; - } + return h264_t{write(write_ctx, sps->nal_unit_header.nal_unit_type, + (void *)&sps->nal_unit_header, AV_CODEC_ID_H264), + write(ctx, sps_p->nal_unit_header.nal_unit_type, + (void *)&sps_p->nal_unit_header, AV_CODEC_ID_H264)}; +} - hevc_t - make_sps_hevc(const AVCodecContext *avctx, const AVPacket *packet) { +hevc_t make_sps_hevc(const AVCodecContext *avctx, const AVPacket *packet) { cbs::ctx_t ctx; if (ff_cbs_init(&ctx, AV_CODEC_ID_H265, nullptr)) { - return {}; + return {}; } cbs::frag_t frag; int err = ff_cbs_read_packet(ctx.get(), &frag, packet); if (err < 0) { - char err_str[AV_ERROR_MAX_STRING_SIZE] { 0 }; - BOOST_LOG(error) << "Couldn't read packet: "sv << av_make_error_string(err_str, AV_ERROR_MAX_STRING_SIZE, err); + char err_str[AV_ERROR_MAX_STRING_SIZE]{0}; + BOOST_LOG(error) << "Couldn't read packet: "sv + << av_make_error_string(err_str, + AV_ERROR_MAX_STRING_SIZE, err); - return {}; + return {}; } - auto vps_p = ((CodedBitstreamH265Context *) ctx->priv_data)->active_vps; - auto sps_p = ((CodedBitstreamH265Context *) ctx->priv_data)->active_sps; + auto vps_p = ((CodedBitstreamH265Context *)ctx->priv_data)->active_vps; + auto sps_p = ((CodedBitstreamH265Context *)ctx->priv_data)->active_sps; // These are very large structs that cannot safely be stored on the stack auto sps = std::make_unique(*sps_p); @@ -189,8 +191,10 @@ namespace cbs { vui.vui_timing_info_present_flag = vps->vps_timing_info_present_flag; vui.vui_num_units_in_tick = vps->vps_num_units_in_tick; vui.vui_time_scale = vps->vps_time_scale; - vui.vui_poc_proportional_to_timing_flag = vps->vps_poc_proportional_to_timing_flag; - vui.vui_num_ticks_poc_diff_one_minus1 = vps->vps_num_ticks_poc_diff_one_minus1; + vui.vui_poc_proportional_to_timing_flag = + vps->vps_poc_proportional_to_timing_flag; + vui.vui_num_ticks_poc_diff_one_minus1 = + vps->vps_num_ticks_poc_diff_one_minus1; vui.vui_hrd_parameters_present_flag = 0; vui.bitstream_restriction_flag = 1; @@ -204,46 +208,52 @@ namespace cbs { cbs::ctx_t write_ctx; ff_cbs_init(&write_ctx, AV_CODEC_ID_H265, nullptr); - return hevc_t { - nal_t { - write(write_ctx, vps->nal_unit_header.nal_unit_type, (void *) &vps->nal_unit_header, AV_CODEC_ID_H265), - write(ctx, vps_p->nal_unit_header.nal_unit_type, (void *) &vps_p->nal_unit_header, AV_CODEC_ID_H265), - }, + return hevc_t{ + nal_t{ + write(write_ctx, vps->nal_unit_header.nal_unit_type, + (void *)&vps->nal_unit_header, AV_CODEC_ID_H265), + write(ctx, vps_p->nal_unit_header.nal_unit_type, + (void *)&vps_p->nal_unit_header, AV_CODEC_ID_H265), + }, - nal_t { - write(write_ctx, sps->nal_unit_header.nal_unit_type, (void *) &sps->nal_unit_header, AV_CODEC_ID_H265), - write(ctx, sps_p->nal_unit_header.nal_unit_type, (void *) &sps_p->nal_unit_header, AV_CODEC_ID_H265), - }, + nal_t{ + write(write_ctx, sps->nal_unit_header.nal_unit_type, + (void *)&sps->nal_unit_header, AV_CODEC_ID_H265), + write(ctx, sps_p->nal_unit_header.nal_unit_type, + (void *)&sps_p->nal_unit_header, AV_CODEC_ID_H265), + }, }; - } +} - bool - validate_sps(const AVPacket *packet, int codec_id) { +bool validate_sps(const AVPacket *packet, int codec_id) { cbs::ctx_t ctx; - if (ff_cbs_init(&ctx, (AVCodecID) codec_id, nullptr)) { - return false; + if (ff_cbs_init(&ctx, (AVCodecID)codec_id, nullptr)) { + return false; } cbs::frag_t frag; int err = ff_cbs_read_packet(ctx.get(), &frag, packet); if (err < 0) { - char err_str[AV_ERROR_MAX_STRING_SIZE] { 0 }; - BOOST_LOG(error) << "Couldn't read packet: "sv << av_make_error_string(err_str, AV_ERROR_MAX_STRING_SIZE, err); + char err_str[AV_ERROR_MAX_STRING_SIZE]{0}; + BOOST_LOG(error) << "Couldn't read packet: "sv + << av_make_error_string(err_str, + AV_ERROR_MAX_STRING_SIZE, err); - return false; + return false; } if (codec_id == AV_CODEC_ID_H264) { - auto h264 = (CodedBitstreamH264Context *) ctx->priv_data; + auto h264 = (CodedBitstreamH264Context *)ctx->priv_data; - if (!h264->active_sps->vui_parameters_present_flag) { - return false; - } + if (!h264->active_sps->vui_parameters_present_flag) { + return false; + } - return true; + return true; } - return ((CodedBitstreamH265Context *) ctx->priv_data)->active_sps->vui_parameters_present_flag; - } + return ((CodedBitstreamH265Context *)ctx->priv_data) + ->active_sps->vui_parameters_present_flag; +} } // namespace cbs diff --git a/src/cbs.h b/src/cbs.h index 575f1e40..0fbe3803 100644 --- a/src/cbs.h +++ b/src/cbs.h @@ -11,28 +11,25 @@ struct AVCodecContext; namespace cbs { - struct nal_t { +struct nal_t { util::buffer_t _new; util::buffer_t old; - }; +}; - struct hevc_t { +struct hevc_t { nal_t vps; nal_t sps; - }; +}; - struct h264_t { +struct h264_t { nal_t sps; - }; +}; - hevc_t - make_sps_hevc(const AVCodecContext *ctx, const AVPacket *packet); - h264_t - make_sps_h264(const AVCodecContext *ctx, const AVPacket *packet); +hevc_t make_sps_hevc(const AVCodecContext *ctx, const AVPacket *packet); +h264_t make_sps_h264(const AVCodecContext *ctx, const AVPacket *packet); - /** - * Check if SPS->VUI is present - */ - bool - validate_sps(const AVPacket *packet, int codec_id); +/** + * Check if SPS->VUI is present + */ +bool validate_sps(const AVPacket *packet, int codec_id); } // namespace cbs diff --git a/src/config.cpp b/src/config.cpp index 5fe3b751..45d354ad 100644 --- a/src/config.cpp +++ b/src/config.cpp @@ -2,6 +2,8 @@ * @file src/config.cpp * @brief todo */ +#include "config.h" + #include #include #include @@ -9,15 +11,9 @@ #include #include - - - -#include "config.h" #include "main.h" -#include "utility.h" - #include "platform/common.h" - +#include "utility.h" // For NVENC legacy constants #include @@ -31,248 +27,231 @@ using namespace std::literals; boost::log::sources::severity_logger verbose(0); // Dominating output boost::log::sources::severity_logger debug(1); // Follow what is happening -boost::log::sources::severity_logger info(2); // Should be informed about +boost::log::sources::severity_logger info(2); // Should be informed about boost::log::sources::severity_logger warning(3); // Strange events -boost::log::sources::severity_logger error(4); // Recoverable errors -boost::log::sources::severity_logger fatal(5); // Unrecoverable errors +boost::log::sources::severity_logger error(4); // Recoverable errors +boost::log::sources::severity_logger fatal(5); // Unrecoverable errors #define APPS_JSON_PATH platf::appdata().string() + "/apps.json" namespace config { - namespace nv { +namespace nv { - nvenc::nvenc_two_pass - twopass_from_view(const std::string_view &preset) { - if (preset == "disabled") return nvenc::nvenc_two_pass::disabled; - if (preset == "quarter_res") return nvenc::nvenc_two_pass::quarter_resolution; - if (preset == "full_res") return nvenc::nvenc_two_pass::full_resolution; - BOOST_LOG(warning) << "config: unknown nvenc_twopass value: " << preset; - return nvenc::nvenc_two_pass::quarter_resolution; - } +nvenc::nvenc_two_pass twopass_from_view(const std::string_view &preset) { + if (preset == "disabled") return nvenc::nvenc_two_pass::disabled; + if (preset == "quarter_res") + return nvenc::nvenc_two_pass::quarter_resolution; + if (preset == "full_res") return nvenc::nvenc_two_pass::full_resolution; + BOOST_LOG(warning) << "config: unknown nvenc_twopass value: " << preset; + return nvenc::nvenc_two_pass::quarter_resolution; +} - } // namespace nv +} // namespace nv - namespace amd { - #include - #include - #include +namespace amd { +#include +#include +#include - enum class quality_av1_e : int { - speed = AMF_VIDEO_ENCODER_AV1_QUALITY_PRESET_SPEED, - quality = AMF_VIDEO_ENCODER_AV1_QUALITY_PRESET_QUALITY, - balanced = AMF_VIDEO_ENCODER_AV1_QUALITY_PRESET_BALANCED - }; +enum class quality_av1_e : int { + speed = AMF_VIDEO_ENCODER_AV1_QUALITY_PRESET_SPEED, + quality = AMF_VIDEO_ENCODER_AV1_QUALITY_PRESET_QUALITY, + balanced = AMF_VIDEO_ENCODER_AV1_QUALITY_PRESET_BALANCED +}; - enum class quality_hevc_e : int { - speed = AMF_VIDEO_ENCODER_HEVC_QUALITY_PRESET_SPEED, - quality = AMF_VIDEO_ENCODER_HEVC_QUALITY_PRESET_QUALITY, - balanced = AMF_VIDEO_ENCODER_HEVC_QUALITY_PRESET_BALANCED - }; +enum class quality_hevc_e : int { + speed = AMF_VIDEO_ENCODER_HEVC_QUALITY_PRESET_SPEED, + quality = AMF_VIDEO_ENCODER_HEVC_QUALITY_PRESET_QUALITY, + balanced = AMF_VIDEO_ENCODER_HEVC_QUALITY_PRESET_BALANCED +}; - enum class quality_h264_e : int { - speed = AMF_VIDEO_ENCODER_QUALITY_PRESET_SPEED, - quality = AMF_VIDEO_ENCODER_QUALITY_PRESET_QUALITY, - balanced = AMF_VIDEO_ENCODER_QUALITY_PRESET_BALANCED - }; +enum class quality_h264_e : int { + speed = AMF_VIDEO_ENCODER_QUALITY_PRESET_SPEED, + quality = AMF_VIDEO_ENCODER_QUALITY_PRESET_QUALITY, + balanced = AMF_VIDEO_ENCODER_QUALITY_PRESET_BALANCED +}; - enum class rc_av1_e : int { - cqp = AMF_VIDEO_ENCODER_AV1_RATE_CONTROL_METHOD_CONSTANT_QP, - vbr_latency = AMF_VIDEO_ENCODER_AV1_RATE_CONTROL_METHOD_LATENCY_CONSTRAINED_VBR, - vbr_peak = AMF_VIDEO_ENCODER_AV1_RATE_CONTROL_METHOD_PEAK_CONSTRAINED_VBR, - cbr = AMF_VIDEO_ENCODER_AV1_RATE_CONTROL_METHOD_CBR - }; +enum class rc_av1_e : int { + cqp = AMF_VIDEO_ENCODER_AV1_RATE_CONTROL_METHOD_CONSTANT_QP, + vbr_latency = + AMF_VIDEO_ENCODER_AV1_RATE_CONTROL_METHOD_LATENCY_CONSTRAINED_VBR, + vbr_peak = AMF_VIDEO_ENCODER_AV1_RATE_CONTROL_METHOD_PEAK_CONSTRAINED_VBR, + cbr = AMF_VIDEO_ENCODER_AV1_RATE_CONTROL_METHOD_CBR +}; - enum class rc_hevc_e : int { - cqp = AMF_VIDEO_ENCODER_HEVC_RATE_CONTROL_METHOD_CONSTANT_QP, - vbr_latency = AMF_VIDEO_ENCODER_HEVC_RATE_CONTROL_METHOD_LATENCY_CONSTRAINED_VBR, - vbr_peak = AMF_VIDEO_ENCODER_HEVC_RATE_CONTROL_METHOD_PEAK_CONSTRAINED_VBR, - cbr = AMF_VIDEO_ENCODER_HEVC_RATE_CONTROL_METHOD_CBR - }; +enum class rc_hevc_e : int { + cqp = AMF_VIDEO_ENCODER_HEVC_RATE_CONTROL_METHOD_CONSTANT_QP, + vbr_latency = + AMF_VIDEO_ENCODER_HEVC_RATE_CONTROL_METHOD_LATENCY_CONSTRAINED_VBR, + vbr_peak = AMF_VIDEO_ENCODER_HEVC_RATE_CONTROL_METHOD_PEAK_CONSTRAINED_VBR, + cbr = AMF_VIDEO_ENCODER_HEVC_RATE_CONTROL_METHOD_CBR +}; - enum class rc_h264_e : int { - cqp = AMF_VIDEO_ENCODER_RATE_CONTROL_METHOD_CONSTANT_QP, - vbr_latency = AMF_VIDEO_ENCODER_RATE_CONTROL_METHOD_LATENCY_CONSTRAINED_VBR, - vbr_peak = AMF_VIDEO_ENCODER_RATE_CONTROL_METHOD_PEAK_CONSTRAINED_VBR, - cbr = AMF_VIDEO_ENCODER_RATE_CONTROL_METHOD_CBR - }; +enum class rc_h264_e : int { + cqp = AMF_VIDEO_ENCODER_RATE_CONTROL_METHOD_CONSTANT_QP, + vbr_latency = AMF_VIDEO_ENCODER_RATE_CONTROL_METHOD_LATENCY_CONSTRAINED_VBR, + vbr_peak = AMF_VIDEO_ENCODER_RATE_CONTROL_METHOD_PEAK_CONSTRAINED_VBR, + cbr = AMF_VIDEO_ENCODER_RATE_CONTROL_METHOD_CBR +}; - enum class usage_av1_e : int { - transcoding = AMF_VIDEO_ENCODER_AV1_USAGE_TRANSCODING, - webcam = AMF_VIDEO_ENCODER_AV1_USAGE_WEBCAM, - lowlatency = AMF_VIDEO_ENCODER_AV1_USAGE_LOW_LATENCY, - ultralowlatency = AMF_VIDEO_ENCODER_AV1_USAGE_ULTRA_LOW_LATENCY - }; +enum class usage_av1_e : int { + transcoding = AMF_VIDEO_ENCODER_AV1_USAGE_TRANSCODING, + webcam = AMF_VIDEO_ENCODER_AV1_USAGE_WEBCAM, + lowlatency = AMF_VIDEO_ENCODER_AV1_USAGE_LOW_LATENCY, + ultralowlatency = AMF_VIDEO_ENCODER_AV1_USAGE_ULTRA_LOW_LATENCY +}; - enum class usage_hevc_e : int { - transcoding = AMF_VIDEO_ENCODER_HEVC_USAGE_TRANSCONDING, - webcam = AMF_VIDEO_ENCODER_HEVC_USAGE_WEBCAM, - lowlatency = AMF_VIDEO_ENCODER_HEVC_USAGE_LOW_LATENCY, - ultralowlatency = AMF_VIDEO_ENCODER_HEVC_USAGE_ULTRA_LOW_LATENCY - }; +enum class usage_hevc_e : int { + transcoding = AMF_VIDEO_ENCODER_HEVC_USAGE_TRANSCONDING, + webcam = AMF_VIDEO_ENCODER_HEVC_USAGE_WEBCAM, + lowlatency = AMF_VIDEO_ENCODER_HEVC_USAGE_LOW_LATENCY, + ultralowlatency = AMF_VIDEO_ENCODER_HEVC_USAGE_ULTRA_LOW_LATENCY +}; - enum class usage_h264_e : int { - transcoding = AMF_VIDEO_ENCODER_USAGE_TRANSCONDING, - webcam = AMF_VIDEO_ENCODER_USAGE_WEBCAM, - lowlatency = AMF_VIDEO_ENCODER_USAGE_LOW_LATENCY, - ultralowlatency = AMF_VIDEO_ENCODER_USAGE_ULTRA_LOW_LATENCY - }; +enum class usage_h264_e : int { + transcoding = AMF_VIDEO_ENCODER_USAGE_TRANSCONDING, + webcam = AMF_VIDEO_ENCODER_USAGE_WEBCAM, + lowlatency = AMF_VIDEO_ENCODER_USAGE_LOW_LATENCY, + ultralowlatency = AMF_VIDEO_ENCODER_USAGE_ULTRA_LOW_LATENCY +}; - enum coder_e : int { - _auto = AMF_VIDEO_ENCODER_UNDEFINED, - cabac = AMF_VIDEO_ENCODER_CABAC, - cavlc = AMF_VIDEO_ENCODER_CALV - }; +enum coder_e : int { + _auto = AMF_VIDEO_ENCODER_UNDEFINED, + cabac = AMF_VIDEO_ENCODER_CABAC, + cavlc = AMF_VIDEO_ENCODER_CALV +}; - template - std::optional - quality_from_view(const std::string_view &quality_type) { +template +std::optional quality_from_view(const std::string_view &quality_type) { #define _CONVERT_(x) \ - if (quality_type == #x##sv) return (int) T::x - _CONVERT_(quality); - _CONVERT_(speed); - _CONVERT_(balanced); + if (quality_type == #x##sv) return (int)T::x + _CONVERT_(quality); + _CONVERT_(speed); + _CONVERT_(balanced); #undef _CONVERT_ - return std::nullopt; - } + return std::nullopt; +} - template - std::optional - rc_from_view(const std::string_view &rc) { +template +std::optional rc_from_view(const std::string_view &rc) { #define _CONVERT_(x) \ - if (rc == #x##sv) return (int) T::x - _CONVERT_(cqp); - _CONVERT_(vbr_latency); - _CONVERT_(vbr_peak); - _CONVERT_(cbr); + if (rc == #x##sv) return (int)T::x + _CONVERT_(cqp); + _CONVERT_(vbr_latency); + _CONVERT_(vbr_peak); + _CONVERT_(cbr); #undef _CONVERT_ - return std::nullopt; - } + return std::nullopt; +} - template - std::optional - usage_from_view(const std::string_view &rc) { +template +std::optional usage_from_view(const std::string_view &rc) { #define _CONVERT_(x) \ - if (rc == #x##sv) return (int) T::x - _CONVERT_(transcoding); - _CONVERT_(webcam); - _CONVERT_(lowlatency); - _CONVERT_(ultralowlatency); + if (rc == #x##sv) return (int)T::x + _CONVERT_(transcoding); + _CONVERT_(webcam); + _CONVERT_(lowlatency); + _CONVERT_(ultralowlatency); #undef _CONVERT_ - return std::nullopt; - } + return std::nullopt; +} - int - coder_from_view(const std::string_view &coder) { - if (coder == "auto"sv) return _auto; - if (coder == "cabac"sv || coder == "ac"sv) return cabac; - if (coder == "cavlc"sv || coder == "vlc"sv) return cavlc; +int coder_from_view(const std::string_view &coder) { + if (coder == "auto"sv) return _auto; + if (coder == "cabac"sv || coder == "ac"sv) return cabac; + if (coder == "cavlc"sv || coder == "vlc"sv) return cavlc; - return -1; - } - } // namespace amd + return -1; +} +} // namespace amd - namespace qsv { - enum preset_e : int { - veryslow = 1, - slower = 2, - slow = 3, - medium = 4, - fast = 5, - faster = 6, - veryfast = 7 - }; +namespace qsv { +enum preset_e : int { + veryslow = 1, + slower = 2, + slow = 3, + medium = 4, + fast = 5, + faster = 6, + veryfast = 7 +}; - enum cavlc_e : int { - _auto = false, - enabled = true, - disabled = false - }; +enum cavlc_e : int { _auto = false, enabled = true, disabled = false }; - std::optional - preset_from_view(const std::string_view &preset) { +std::optional preset_from_view(const std::string_view &preset) { #define _CONVERT_(x) \ - if (preset == #x##sv) return x - _CONVERT_(veryslow); - _CONVERT_(slower); - _CONVERT_(slow); - _CONVERT_(medium); - _CONVERT_(fast); - _CONVERT_(faster); - _CONVERT_(veryfast); + if (preset == #x##sv) return x + _CONVERT_(veryslow); + _CONVERT_(slower); + _CONVERT_(slow); + _CONVERT_(medium); + _CONVERT_(fast); + _CONVERT_(faster); + _CONVERT_(veryfast); #undef _CONVERT_ - return std::nullopt; - } + return std::nullopt; +} - std::optional - coder_from_view(const std::string_view &coder) { - if (coder == "auto"sv) return _auto; - if (coder == "cabac"sv || coder == "ac"sv) return disabled; - if (coder == "cavlc"sv || coder == "vlc"sv) return enabled; - return std::nullopt; - } +std::optional coder_from_view(const std::string_view &coder) { + if (coder == "auto"sv) return _auto; + if (coder == "cabac"sv || coder == "ac"sv) return disabled; + if (coder == "cavlc"sv || coder == "vlc"sv) return enabled; + return std::nullopt; +} - } // namespace qsv +} // namespace qsv - namespace vt { +namespace vt { - enum coder_e : int { - _auto = 0, - cabac, - cavlc - }; +enum coder_e : int { _auto = 0, cabac, cavlc }; - int - coder_from_view(const std::string_view &coder) { - if (coder == "auto"sv) return _auto; - if (coder == "cabac"sv || coder == "ac"sv) return cabac; - if (coder == "cavlc"sv || coder == "vlc"sv) return cavlc; +int coder_from_view(const std::string_view &coder) { + if (coder == "auto"sv) return _auto; + if (coder == "cabac"sv || coder == "ac"sv) return cabac; + if (coder == "cavlc"sv || coder == "vlc"sv) return cavlc; - return -1; - } + return -1; +} - int - allow_software_from_view(const std::string_view &software) { - if (software == "allowed"sv || software == "forced") return 1; +int allow_software_from_view(const std::string_view &software) { + if (software == "allowed"sv || software == "forced") return 1; - return 0; - } + return 0; +} - int - force_software_from_view(const std::string_view &software) { - if (software == "forced") return 1; +int force_software_from_view(const std::string_view &software) { + if (software == "forced") return 1; - return 0; - } + return 0; +} - int - rt_from_view(const std::string_view &rt) { - if (rt == "disabled" || rt == "off" || rt == "0") return 0; +int rt_from_view(const std::string_view &rt) { + if (rt == "disabled" || rt == "off" || rt == "0") return 0; - return 1; - } + return 1; +} - } // namespace vt +} // namespace vt - namespace sw { - int - svtav1_preset_from_view(const std::string_view &preset) { +namespace sw { +int svtav1_preset_from_view(const std::string_view &preset) { #define _CONVERT_(x, y) \ - if (preset == #x##sv) return y - _CONVERT_(veryslow, 1); - _CONVERT_(slower, 2); - _CONVERT_(slow, 4); - _CONVERT_(medium, 5); - _CONVERT_(fast, 7); - _CONVERT_(faster, 9); - _CONVERT_(veryfast, 10); - _CONVERT_(superfast, 11); - _CONVERT_(ultrafast, 12); + if (preset == #x##sv) return y + _CONVERT_(veryslow, 1); + _CONVERT_(slower, 2); + _CONVERT_(slow, 4); + _CONVERT_(medium, 5); + _CONVERT_(fast, 7); + _CONVERT_(faster, 9); + _CONVERT_(veryfast, 10); + _CONVERT_(superfast, 11); + _CONVERT_(ultrafast, 12); #undef _CONVERT_ - return 11; // Default to superfast - } - } // namespace sw + return 11; // Default to superfast +} +} // namespace sw - video_t video { +video_t video{ 28, // qp 0, // hevc_mode @@ -280,202 +259,196 @@ namespace config { 1, // min_threads { - "superfast"s, // preset - "zerolatency"s, // tune - 11, // superfast - }, // software + "superfast"s, // preset + "zerolatency"s, // tune + 11, // superfast + }, // software - {}, // nv + {}, // nv true, // nv_realtime_hags - {}, // nv_legacy + {}, // nv_legacy { - qsv::medium, // preset - qsv::_auto, // cavlc - }, // qsv + qsv::medium, // preset + qsv::_auto, // cavlc + }, // qsv { - (int) amd::quality_h264_e::balanced, // quality (h264) - (int) amd::quality_hevc_e::balanced, // quality (hevc) - (int) amd::quality_av1_e::balanced, // quality (av1) - (int) amd::rc_h264_e::vbr_latency, // rate control (h264) - (int) amd::rc_hevc_e::vbr_latency, // rate control (hevc) - (int) amd::rc_av1_e::vbr_latency, // rate control (av1) - (int) amd::usage_h264_e::ultralowlatency, // usage (h264) - (int) amd::usage_hevc_e::ultralowlatency, // usage (hevc) - (int) amd::usage_av1_e::ultralowlatency, // usage (av1) - 0, // preanalysis - 1, // vbaq - (int) amd::coder_e::_auto, // coder - }, // amd + (int)amd::quality_h264_e::balanced, // quality (h264) + (int)amd::quality_hevc_e::balanced, // quality (hevc) + (int)amd::quality_av1_e::balanced, // quality (av1) + (int)amd::rc_h264_e::vbr_latency, // rate control (h264) + (int)amd::rc_hevc_e::vbr_latency, // rate control (hevc) + (int)amd::rc_av1_e::vbr_latency, // rate control (av1) + (int)amd::usage_h264_e::ultralowlatency, // usage (h264) + (int)amd::usage_hevc_e::ultralowlatency, // usage (hevc) + (int)amd::usage_av1_e::ultralowlatency, // usage (av1) + 0, // preanalysis + 1, // vbaq + (int)amd::coder_e::_auto, // coder + }, // amd { - 0, - 0, - 1, - -1, + 0, + 0, + 1, + -1, }, // vt {}, // capture {}, // encoder {}, // adapter_name {}, // output_name - }; +}; - audio_t audio { - {}, // audio_sink - {}, // virtual_sink +audio_t audio{ + {}, // audio_sink + {}, // virtual_sink true, // install_steam_drivers - }; +}; - stream_t stream { +stream_t stream{ 10s, // ping_timeout APPS_JSON_PATH, 20, // fecPercentage - 1 // channels - }; + 1 // channels +}; - - - sunshine_t sunshine { - 2, // min_log_level - 0, // flags - {}, // User file - {}, // Username - {}, // Password - {}, // Password Salt +sunshine_t sunshine{ + 2, // min_log_level + 0, // flags + {}, // User file + {}, // Username + {}, // Password + {}, // Password Salt platf::appdata().string() + "/sunshine.conf", // config file - {}, // cmd args - 47989, // Base port number - "ipv4", // Address family - platf::appdata().string() + "/sunshine.log", // log file - {}, // prep commands - }; + {}, // cmd args + 47989, // Base port number + "ipv4", // Address family + platf::appdata().string() + "/sunshine.log", // log file + {}, // prep commands +}; - bool - endline(char ch) { - return ch == '\r' || ch == '\n'; - } +bool endline(char ch) { return ch == '\r' || ch == '\n'; } - bool - space_tab(char ch) { - return ch == ' ' || ch == '\t'; - } +bool space_tab(char ch) { return ch == ' ' || ch == '\t'; } - bool - whitespace(char ch) { - return space_tab(ch) || endline(ch); - } +bool whitespace(char ch) { return space_tab(ch) || endline(ch); } - std::string - to_string(const char *begin, const char *end) { +std::string to_string(const char *begin, const char *end) { std::string result; KITTY_WHILE_LOOP(auto pos = begin, pos != end, { - auto comment = std::find(pos, end, '#'); - auto endl = std::find_if(comment, end, endline); + auto comment = std::find(pos, end, '#'); + auto endl = std::find_if(comment, end, endline); - result.append(pos, comment); + result.append(pos, comment); - pos = endl; + pos = endl; }) return result; - } +} - template - It - skip_list(It skipper, It end) { +template +It skip_list(It skipper, It end) { int stack = 1; while (skipper != end && stack) { - if (*skipper == '[') { - ++stack; - } - if (*skipper == ']') { - --stack; - } + if (*skipper == '[') { + ++stack; + } + if (*skipper == ']') { + --stack; + } - ++skipper; + ++skipper; } return skipper; - } +} - std::pair< - std::string_view::const_iterator, - std::optional>> - parse_option(std::string_view::const_iterator begin, std::string_view::const_iterator end) { +std::pair>> +parse_option(std::string_view::const_iterator begin, + std::string_view::const_iterator end) { begin = std::find_if_not(begin, end, whitespace); auto endl = std::find_if(begin, end, endline); auto endc = std::find(begin, endl, '#'); - endc = std::find_if(std::make_reverse_iterator(endc), std::make_reverse_iterator(begin), std::not_fn(whitespace)).base(); + endc = + std::find_if(std::make_reverse_iterator(endc), + std::make_reverse_iterator(begin), std::not_fn(whitespace)) + .base(); auto eq = std::find(begin, endc, '='); if (eq == endc || eq == begin) { - return std::make_pair(endl, std::nullopt); + return std::make_pair(endl, std::nullopt); } - auto end_name = std::find_if_not(std::make_reverse_iterator(eq), std::make_reverse_iterator(begin), space_tab).base(); + auto end_name = + std::find_if_not(std::make_reverse_iterator(eq), + std::make_reverse_iterator(begin), space_tab) + .base(); auto begin_val = std::find_if_not(eq + 1, endc, space_tab); if (begin_val == endl) { - return std::make_pair(endl, std::nullopt); + return std::make_pair(endl, std::nullopt); } // Lists might contain newlines if (*begin_val == '[') { - endl = skip_list(begin_val + 1, end); - if (endl == end) { - std::cout << "Warning: Config option ["sv << to_string(begin, end_name) << "] Missing ']'"sv; + endl = skip_list(begin_val + 1, end); + if (endl == end) { + std::cout << "Warning: Config option ["sv + << to_string(begin, end_name) << "] Missing ']'"sv; - return std::make_pair(endl, std::nullopt); - } + return std::make_pair(endl, std::nullopt); + } } - return std::make_pair( - endl, - std::make_pair(to_string(begin, end_name), to_string(begin_val, endl))); - } + return std::make_pair(endl, std::make_pair(to_string(begin, end_name), + to_string(begin_val, endl))); +} - void - string_f(std::unordered_map &vars, const std::string &name, std::string &input) { +void string_f(std::unordered_map &vars, + const std::string &name, std::string &input) { auto it = vars.find(name); if (it == std::end(vars)) { - return; + return; } input = std::move(it->second); vars.erase(it); - } +} - template - void - generic_f(std::unordered_map &vars, const std::string &name, T &input, F &&f) { +template +void generic_f(std::unordered_map &vars, + const std::string &name, T &input, F &&f) { std::string tmp; string_f(vars, name, tmp); if (!tmp.empty()) { - input = f(tmp); + input = f(tmp); } - } +} - void - string_restricted_f(std::unordered_map &vars, const std::string &name, std::string &input, const std::vector &allowed_vals) { +void string_restricted_f(std::unordered_map &vars, + const std::string &name, std::string &input, + const std::vector &allowed_vals) { std::string temp; string_f(vars, name, temp); for (auto &allowed_val : allowed_vals) { - if (temp == allowed_val) { - input = std::move(temp); - return; - } + if (temp == allowed_val) { + input = std::move(temp); + return; + } } - } +} - void - path_f(std::unordered_map &vars, const std::string &name, fs::path &input) { +void path_f(std::unordered_map &vars, + const std::string &name, fs::path &input) { // appdata needs to be retrieved once only static auto appdata = platf::appdata(); @@ -483,11 +456,11 @@ namespace config { string_f(vars, name, temp); if (!temp.empty()) { - input = temp; + input = temp; } if (input.is_relative()) { - input = appdata / input; + input = appdata / input; } auto dir = input; @@ -495,306 +468,309 @@ namespace config { // Ensure the directories exists if (!fs::exists(dir)) { - fs::create_directories(dir); + fs::create_directories(dir); } - } +} - void - path_f(std::unordered_map &vars, const std::string &name, std::string &input) { +void path_f(std::unordered_map &vars, + const std::string &name, std::string &input) { fs::path temp = input; path_f(vars, name, temp); input = temp.string(); - } +} - void - int_f(std::unordered_map &vars, const std::string &name, int &input) { +void int_f(std::unordered_map &vars, + const std::string &name, int &input) { auto it = vars.find(name); if (it == std::end(vars)) { - return; + return; } std::string_view val = it->second; // If value is something like: "756" instead of 756 if (val.size() >= 2 && val[0] == '"') { - val = val.substr(1, val.size() - 2); + val = val.substr(1, val.size() - 2); } // If that integer is in hexadecimal if (val.size() >= 2 && val.substr(0, 2) == "0x"sv) { - input = util::from_hex(val.substr(2)); - } - else { - input = util::from_view(val); + input = util::from_hex(val.substr(2)); + } else { + input = util::from_view(val); } vars.erase(it); - } +} - void - int_f(std::unordered_map &vars, const std::string &name, std::optional &input) { +void int_f(std::unordered_map &vars, + const std::string &name, std::optional &input) { auto it = vars.find(name); if (it == std::end(vars)) { - return; + return; } std::string_view val = it->second; // If value is something like: "756" instead of 756 if (val.size() >= 2 && val[0] == '"') { - val = val.substr(1, val.size() - 2); + val = val.substr(1, val.size() - 2); } // If that integer is in hexadecimal if (val.size() >= 2 && val.substr(0, 2) == "0x"sv) { - input = util::from_hex(val.substr(2)); - } - else { - input = util::from_view(val); + input = util::from_hex(val.substr(2)); + } else { + input = util::from_view(val); } vars.erase(it); - } +} - template - void - int_f(std::unordered_map &vars, const std::string &name, int &input, F &&f) { +template +void int_f(std::unordered_map &vars, + const std::string &name, int &input, F &&f) { std::string tmp; string_f(vars, name, tmp); if (!tmp.empty()) { - input = f(tmp); + input = f(tmp); } - } +} - template - void - int_f(std::unordered_map &vars, const std::string &name, std::optional &input, F &&f) { +template +void int_f(std::unordered_map &vars, + const std::string &name, std::optional &input, F &&f) { std::string tmp; string_f(vars, name, tmp); if (!tmp.empty()) { - input = f(tmp); + input = f(tmp); } - } +} - void - int_between_f(std::unordered_map &vars, const std::string &name, int &input, const std::pair &range) { +void int_between_f(std::unordered_map &vars, + const std::string &name, int &input, + const std::pair &range) { int temp = input; int_f(vars, name, temp); TUPLE_2D_REF(lower, upper, range); if (temp >= lower && temp <= upper) { - input = temp; + input = temp; } - } +} - bool - to_bool(std::string &boolean) { - std::for_each(std::begin(boolean), std::end(boolean), [](char ch) { return (char) std::tolower(ch); }); +bool to_bool(std::string &boolean) { + std::for_each(std::begin(boolean), std::end(boolean), + [](char ch) { return (char)std::tolower(ch); }); - return boolean == "true"sv || - boolean == "yes"sv || - boolean == "enable"sv || - boolean == "enabled"sv || - boolean == "on"sv || - (std::find(std::begin(boolean), std::end(boolean), '1') != std::end(boolean)); - } + return boolean == "true"sv || boolean == "yes"sv || boolean == "enable"sv || + boolean == "enabled"sv || boolean == "on"sv || + (std::find(std::begin(boolean), std::end(boolean), '1') != + std::end(boolean)); +} - void - bool_f(std::unordered_map &vars, const std::string &name, bool &input) { +void bool_f(std::unordered_map &vars, + const std::string &name, bool &input) { std::string tmp; string_f(vars, name, tmp); if (tmp.empty()) { - return; + return; } input = to_bool(tmp); - } +} - void - double_f(std::unordered_map &vars, const std::string &name, double &input) { +void double_f(std::unordered_map &vars, + const std::string &name, double &input) { std::string tmp; string_f(vars, name, tmp); if (tmp.empty()) { - return; + return; } char *c_str_p; auto val = std::strtod(tmp.c_str(), &c_str_p); if (c_str_p == tmp.c_str()) { - return; + return; } input = val; - } +} - void - double_between_f(std::unordered_map &vars, const std::string &name, double &input, const std::pair &range) { +void double_between_f(std::unordered_map &vars, + const std::string &name, double &input, + const std::pair &range) { double temp = input; double_f(vars, name, temp); TUPLE_2D_REF(lower, upper, range); if (temp >= lower && temp <= upper) { - input = temp; + input = temp; } - } +} - void - list_string_f(std::unordered_map &vars, const std::string &name, std::vector &input) { +void list_string_f(std::unordered_map &vars, + const std::string &name, std::vector &input) { std::string string; string_f(vars, name, string); if (string.empty()) { - return; + return; } input.clear(); auto begin = std::cbegin(string); if (*begin == '[') { - ++begin; + ++begin; } begin = std::find_if_not(begin, std::cend(string), whitespace); if (begin == std::cend(string)) { - return; + return; } auto pos = begin; while (pos < std::cend(string)) { - if (*pos == '[') { - pos = skip_list(pos + 1, std::cend(string)) + 1; - } - else if (*pos == ']') { - break; - } - else if (*pos == ',') { - input.emplace_back(begin, pos); - pos = begin = std::find_if_not(pos + 1, std::cend(string), whitespace); - } - else { - ++pos; - } + if (*pos == '[') { + pos = skip_list(pos + 1, std::cend(string)) + 1; + } else if (*pos == ']') { + break; + } else if (*pos == ',') { + input.emplace_back(begin, pos); + pos = begin = + std::find_if_not(pos + 1, std::cend(string), whitespace); + } else { + ++pos; + } } if (pos != begin) { - input.emplace_back(begin, pos); + input.emplace_back(begin, pos); } - } +} - - void - list_int_f(std::unordered_map &vars, const std::string &name, std::vector &input) { +void list_int_f(std::unordered_map &vars, + const std::string &name, std::vector &input) { std::vector list; list_string_f(vars, name, list); for (auto &el : list) { - std::string_view val = el; + std::string_view val = el; - // If value is something like: "756" instead of 756 - if (val.size() >= 2 && val[0] == '"') { - val = val.substr(1, val.size() - 2); - } + // If value is something like: "756" instead of 756 + if (val.size() >= 2 && val[0] == '"') { + val = val.substr(1, val.size() - 2); + } - int tmp; + int tmp; - // If the integer is a hexadecimal - if (val.size() >= 2 && val.substr(0, 2) == "0x"sv) { - tmp = util::from_hex(val.substr(2)); - } - else { - tmp = util::from_view(val); - } - input.emplace_back(tmp); + // If the integer is a hexadecimal + if (val.size() >= 2 && val.substr(0, 2) == "0x"sv) { + tmp = util::from_hex(val.substr(2)); + } else { + tmp = util::from_view(val); + } + input.emplace_back(tmp); } - } +} - void - map_int_int_f(std::unordered_map &vars, const std::string &name, std::unordered_map &input) { +void map_int_int_f(std::unordered_map &vars, + const std::string &name, + std::unordered_map &input) { std::vector list; list_int_f(vars, name, list); // The list needs to be a multiple of 2 if (list.size() % 2) { - std::cout << "Warning: expected "sv << name << " to have a multiple of two elements --> not "sv << list.size() << std::endl; - return; + std::cout << "Warning: expected "sv << name + << " to have a multiple of two elements --> not "sv + << list.size() << std::endl; + return; } int x = 0; while (x < list.size()) { - auto key = list[x++]; - auto val = list[x++]; + auto key = list[x++]; + auto val = list[x++]; - input.emplace(key, val); + input.emplace(key, val); } - } +} - int - apply_flags(const char *line) { +int apply_flags(const char *line) { int ret = 0; while (*line != '\0') { - switch (*line) { - case '0': - config::sunshine.flags[config::flag::PIN_STDIN].flip(); - break; - case '1': - config::sunshine.flags[config::flag::FRESH_STATE].flip(); - break; - case '2': - config::sunshine.flags[config::flag::FORCE_VIDEO_HEADER_REPLACE].flip(); - break; - case 'p': - config::sunshine.flags[config::flag::UPNP].flip(); - break; - default: - std::cout << "Warning: Unrecognized flag: ["sv << *line << ']' << std::endl; - ret = -1; - } + switch (*line) { + case '0': + config::sunshine.flags[config::flag::PIN_STDIN].flip(); + break; + case '1': + config::sunshine.flags[config::flag::FRESH_STATE].flip(); + break; + case '2': + config::sunshine.flags[config::flag::FORCE_VIDEO_HEADER_REPLACE] + .flip(); + break; + case 'p': + config::sunshine.flags[config::flag::UPNP].flip(); + break; + default: + std::cout << "Warning: Unrecognized flag: ["sv << *line << ']' + << std::endl; + ret = -1; + } - ++line; + ++line; } return ret; - } +} - void - apply_config(std::unordered_map &&vars) { +void apply_config(std::unordered_map &&vars) { if (!fs::exists(stream.file_apps.c_str())) { - fs::copy_file(SUNSHINE_ASSETS_DIR "/apps.json", stream.file_apps); + fs::copy_file(SUNSHINE_ASSETS_DIR "/apps.json", stream.file_apps); } for (auto &[name, val] : vars) { - std::cout << "["sv << name << "] -- ["sv << val << ']' << std::endl; + std::cout << "["sv << name << "] -- ["sv << val << ']' << std::endl; } int_f(vars, "qp", video.qp); int_f(vars, "min_threads", video.min_threads); - int_between_f(vars, "hevc_mode", video.hevc_mode, { 0, 3 }); - int_between_f(vars, "av1_mode", video.av1_mode, { 0, 3 }); + int_between_f(vars, "hevc_mode", video.hevc_mode, {0, 3}); + int_between_f(vars, "av1_mode", video.av1_mode, {0, 3}); string_f(vars, "sw_preset", video.sw.sw_preset); if (!video.sw.sw_preset.empty()) { - video.sw.svtav1_preset = sw::svtav1_preset_from_view(video.sw.sw_preset); + video.sw.svtav1_preset = + sw::svtav1_preset_from_view(video.sw.sw_preset); } string_f(vars, "sw_tune", video.sw.sw_tune); - int_between_f(vars, "nvenc_preset", video.nv.quality_preset, { 1, 7 }); + int_between_f(vars, "nvenc_preset", video.nv.quality_preset, {1, 7}); generic_f(vars, "nvenc_twopass", video.nv.two_pass, nv::twopass_from_view); bool_f(vars, "nvenc_h264_cavlc", video.nv.h264_cavlc); bool_f(vars, "nvenc_realtime_hags", video.nv_realtime_hags); video.nv_legacy.preset = video.nv.quality_preset + 11; - video.nv_legacy.multipass = video.nv.two_pass == nvenc::nvenc_two_pass::quarter_resolution ? NV_ENC_TWO_PASS_QUARTER_RESOLUTION : - video.nv.two_pass == nvenc::nvenc_two_pass::full_resolution ? NV_ENC_TWO_PASS_FULL_RESOLUTION : - NV_ENC_MULTI_PASS_DISABLED; - video.nv_legacy.h264_coder = video.nv.h264_cavlc ? NV_ENC_H264_ENTROPY_CODING_MODE_CAVLC : NV_ENC_H264_ENTROPY_CODING_MODE_CABAC; + video.nv_legacy.multipass = + video.nv.two_pass == nvenc::nvenc_two_pass::quarter_resolution + ? NV_ENC_TWO_PASS_QUARTER_RESOLUTION + : video.nv.two_pass == nvenc::nvenc_two_pass::full_resolution + ? NV_ENC_TWO_PASS_FULL_RESOLUTION + : NV_ENC_MULTI_PASS_DISABLED; + video.nv_legacy.h264_coder = video.nv.h264_cavlc + ? NV_ENC_H264_ENTROPY_CODING_MODE_CAVLC + : NV_ENC_H264_ENTROPY_CODING_MODE_CABAC; int_f(vars, "qsv_preset", video.qsv.qsv_preset, qsv::preset_from_view); int_f(vars, "qsv_coder", video.qsv.qsv_cavlc, qsv::coder_from_view); @@ -802,34 +778,39 @@ namespace config { std::string quality; string_f(vars, "amd_quality", quality); if (!quality.empty()) { - video.amd.amd_quality_h264 = amd::quality_from_view(quality); - video.amd.amd_quality_hevc = amd::quality_from_view(quality); - video.amd.amd_quality_av1 = amd::quality_from_view(quality); + video.amd.amd_quality_h264 = + amd::quality_from_view(quality); + video.amd.amd_quality_hevc = + amd::quality_from_view(quality); + video.amd.amd_quality_av1 = + amd::quality_from_view(quality); } std::string rc; string_f(vars, "amd_rc", rc); int_f(vars, "amd_coder", video.amd.amd_coder, amd::coder_from_view); if (!rc.empty()) { - video.amd.amd_rc_h264 = amd::rc_from_view(rc); - video.amd.amd_rc_hevc = amd::rc_from_view(rc); - video.amd.amd_rc_av1 = amd::rc_from_view(rc); + video.amd.amd_rc_h264 = amd::rc_from_view(rc); + video.amd.amd_rc_hevc = amd::rc_from_view(rc); + video.amd.amd_rc_av1 = amd::rc_from_view(rc); } std::string usage; string_f(vars, "amd_usage", usage); if (!usage.empty()) { - video.amd.amd_usage_h264 = amd::usage_from_view(rc); - video.amd.amd_usage_hevc = amd::usage_from_view(rc); - video.amd.amd_usage_av1 = amd::usage_from_view(rc); + video.amd.amd_usage_h264 = amd::usage_from_view(rc); + video.amd.amd_usage_hevc = amd::usage_from_view(rc); + video.amd.amd_usage_av1 = amd::usage_from_view(rc); } - bool_f(vars, "amd_preanalysis", (bool &) video.amd.amd_preanalysis); - bool_f(vars, "amd_vbaq", (bool &) video.amd.amd_vbaq); + bool_f(vars, "amd_preanalysis", (bool &)video.amd.amd_preanalysis); + bool_f(vars, "amd_vbaq", (bool &)video.amd.amd_vbaq); int_f(vars, "vt_coder", video.vt.vt_coder, vt::coder_from_view); - int_f(vars, "vt_software", video.vt.vt_allow_sw, vt::allow_software_from_view); - int_f(vars, "vt_software", video.vt.vt_require_sw, vt::force_software_from_view); + int_f(vars, "vt_software", video.vt.vt_allow_sw, + vt::allow_software_from_view); + int_f(vars, "vt_software", video.vt.vt_require_sw, + vt::force_software_from_view); int_f(vars, "vt_realtime", video.vt.vt_realtime, vt::rt_from_view); string_f(vars, "capture", video.capture); @@ -847,80 +828,75 @@ namespace config { bool_f(vars, "install_steam_audio_drivers", audio.install_steam_drivers); int to = -1; - int_between_f(vars, "ping_timeout", to, { -1, std::numeric_limits::max() }); + int_between_f(vars, "ping_timeout", to, + {-1, std::numeric_limits::max()}); if (to != -1) { - stream.ping_timeout = std::chrono::milliseconds(to); + stream.ping_timeout = std::chrono::milliseconds(to); } - int_between_f(vars, "channels", stream.channels, { 1, std::numeric_limits::max() }); + int_between_f(vars, "channels", stream.channels, + {1, std::numeric_limits::max()}); path_f(vars, "file_apps", stream.file_apps); - int_between_f(vars, "fec_percentage", stream.fec_percentage, { 1, 255 }); - + int_between_f(vars, "fec_percentage", stream.fec_percentage, {1, 255}); // This config option will only be used by the UI // When editing in the config file itself, use "keybindings" bool map_rightalt_to_win = false; bool_f(vars, "key_rightalt_to_key_win", map_rightalt_to_win); - int port = sunshine.port; - sunshine.port = (std::uint16_t) port; + sunshine.port = (std::uint16_t)port; - string_restricted_f(vars, "address_family", sunshine.address_family, { "ipv4"sv, "both"sv }); + string_restricted_f(vars, "address_family", sunshine.address_family, + {"ipv4"sv, "both"sv}); bool upnp = false; bool_f(vars, "upnp"s, upnp); if (upnp) { - config::sunshine.flags[config::flag::UPNP].flip(); + config::sunshine.flags[config::flag::UPNP].flip(); } std::string log_level_string; string_f(vars, "min_log_level", log_level_string); if (!log_level_string.empty()) { - if (log_level_string == "verbose"sv) { - sunshine.min_log_level = 0; - } - else if (log_level_string == "debug"sv) { - sunshine.min_log_level = 1; - } - else if (log_level_string == "info"sv) { - sunshine.min_log_level = 2; - } - else if (log_level_string == "warning"sv) { - sunshine.min_log_level = 3; - } - else if (log_level_string == "error"sv) { - sunshine.min_log_level = 4; - } - else if (log_level_string == "fatal"sv) { - sunshine.min_log_level = 5; - } - else if (log_level_string == "none"sv) { - sunshine.min_log_level = 6; - } - else { - // accept digit directly - auto val = log_level_string[0]; - if (val >= '0' && val < '7') { - sunshine.min_log_level = val - '0'; + if (log_level_string == "verbose"sv) { + sunshine.min_log_level = 0; + } else if (log_level_string == "debug"sv) { + sunshine.min_log_level = 1; + } else if (log_level_string == "info"sv) { + sunshine.min_log_level = 2; + } else if (log_level_string == "warning"sv) { + sunshine.min_log_level = 3; + } else if (log_level_string == "error"sv) { + sunshine.min_log_level = 4; + } else if (log_level_string == "fatal"sv) { + sunshine.min_log_level = 5; + } else if (log_level_string == "none"sv) { + sunshine.min_log_level = 6; + } else { + // accept digit directly + auto val = log_level_string[0]; + if (val >= '0' && val < '7') { + sunshine.min_log_level = val - '0'; + } } - } } auto it = vars.find("flags"s); if (it != std::end(vars)) { - apply_flags(it->second.c_str()); + apply_flags(it->second.c_str()); - vars.erase(it); + vars.erase(it); } if (sunshine.min_log_level <= 3) { - for (auto &[var, _] : vars) { - std::cout << "Warning: Unrecognized configurable option ["sv << var << ']' << std::endl; - } + for (auto &[var, _] : vars) { + std::cout << "Warning: Unrecognized configurable option ["sv << var + << ']' << std::endl; + } } - } +} } // namespace config diff --git a/src/config.h b/src/config.h index 4cdb6384..bd113473 100644 --- a/src/config.h +++ b/src/config.h @@ -14,7 +14,7 @@ #include "nvenc/nvenc_config.h" namespace config { - struct video_t { +struct video_t { // ffmpeg params int qp; // higher == more compression and less quality @@ -23,60 +23,60 @@ namespace config { int min_threads; // Minimum number of threads/slices for CPU encoding struct { - std::string sw_preset; - std::string sw_tune; - std::optional svtav1_preset; + std::string sw_preset; + std::string sw_tune; + std::optional svtav1_preset; } sw; nvenc::nvenc_config nv; bool nv_realtime_hags; struct { - int preset; - int multipass; - int h264_coder; + int preset; + int multipass; + int h264_coder; } nv_legacy; struct { - std::optional qsv_preset; - std::optional qsv_cavlc; + std::optional qsv_preset; + std::optional qsv_cavlc; } qsv; struct { - std::optional amd_quality_h264; - std::optional amd_quality_hevc; - std::optional amd_quality_av1; - std::optional amd_rc_h264; - std::optional amd_rc_hevc; - std::optional amd_rc_av1; - std::optional amd_usage_h264; - std::optional amd_usage_hevc; - std::optional amd_usage_av1; - std::optional amd_preanalysis; - std::optional amd_vbaq; - int amd_coder; + std::optional amd_quality_h264; + std::optional amd_quality_hevc; + std::optional amd_quality_av1; + std::optional amd_rc_h264; + std::optional amd_rc_hevc; + std::optional amd_rc_av1; + std::optional amd_usage_h264; + std::optional amd_usage_hevc; + std::optional amd_usage_av1; + std::optional amd_preanalysis; + std::optional amd_vbaq; + int amd_coder; } amd; struct { - int vt_allow_sw; - int vt_require_sw; - int vt_realtime; - int vt_coder; + int vt_allow_sw; + int vt_require_sw; + int vt_realtime; + int vt_coder; } vt; std::string capture; std::string encoder; std::string adapter_name; std::string output_name; - }; +}; - struct audio_t { +struct audio_t { std::string sink; std::string virtual_sink; bool install_steam_drivers; - }; +}; - struct stream_t { +struct stream_t { std::chrono::milliseconds ping_timeout; std::string file_apps; @@ -85,9 +85,9 @@ namespace config { // max unique instances of video and audio streams int channels; - }; +}; - struct nvhttp_t { +struct nvhttp_t { // Could be any of the following values: // pc|lan|wan std::string origin_web_ui_allowed; @@ -102,31 +102,31 @@ namespace config { std::string external_ip; std::vector resolutions; std::vector fps; - }; +}; +namespace flag { +enum flag_e : std::size_t { + PIN_STDIN = 0, // Read PIN from stdin instead of http + FRESH_STATE, // Do not load or save state + FORCE_VIDEO_HEADER_REPLACE, // force replacing headers inside video data + UPNP, // Try Universal Plug 'n Play + CONST_PIN, // Use "universal" pin + FLAG_SIZE +}; +} - - namespace flag { - enum flag_e : std::size_t { - PIN_STDIN = 0, // Read PIN from stdin instead of http - FRESH_STATE, // Do not load or save state - FORCE_VIDEO_HEADER_REPLACE, // force replacing headers inside video data - UPNP, // Try Universal Plug 'n Play - CONST_PIN, // Use "universal" pin - FLAG_SIZE - }; - } - - struct prep_cmd_t { - prep_cmd_t(std::string &&do_cmd, std::string &&undo_cmd, bool &&elevated): - do_cmd(std::move(do_cmd)), undo_cmd(std::move(undo_cmd)), elevated(std::move(elevated)) {} - explicit prep_cmd_t(std::string &&do_cmd, bool &&elevated): - do_cmd(std::move(do_cmd)), elevated(std::move(elevated)) {} +struct prep_cmd_t { + prep_cmd_t(std::string &&do_cmd, std::string &&undo_cmd, bool &&elevated) + : do_cmd(std::move(do_cmd)), + undo_cmd(std::move(undo_cmd)), + elevated(std::move(elevated)) {} + explicit prep_cmd_t(std::string &&do_cmd, bool &&elevated) + : do_cmd(std::move(do_cmd)), elevated(std::move(elevated)) {} std::string do_cmd; std::string undo_cmd; bool elevated; - }; - struct sunshine_t { +}; +struct sunshine_t { int min_log_level; std::bitset flags; std::string credentials_file; @@ -138,9 +138,9 @@ namespace config { std::string config_file; struct cmd_t { - std::string name; - int argc; - char **argv; + std::string name; + int argc; + char **argv; } cmd; std::uint16_t port; @@ -149,11 +149,11 @@ namespace config { std::string log_file; std::vector prep_cmds; - }; +}; - extern video_t video; - extern audio_t audio; - extern stream_t stream; - extern nvhttp_t nvhttp; - extern sunshine_t sunshine; +extern video_t video; +extern audio_t audio; +extern stream_t stream; +extern nvhttp_t nvhttp; +extern sunshine_t sunshine; } // namespace config diff --git a/src/dll.cpp b/src/dll.cpp index 7252c906..9f03cd78 100644 --- a/src/dll.cpp +++ b/src/dll.cpp @@ -12,161 +12,128 @@ // local includes #include "config.h" -#include "main.h" #include "dll.h" +#include "main.h" #include "platform/common.h" #include "video.h" using namespace std::literals; -struct _VideoPipeline -{ - std::chrono::steady_clock::time_point start; - video::config_t monitor; - safe::mail_t mail; +struct _VideoPipeline { + std::chrono::steady_clock::time_point start; + video::config_t monitor; + safe::mail_t mail; }; -extern VideoPipeline *__cdecl StartQueue(int video_codec, - char* display_name) -{ - static bool init = false; - if (!init) - { - // If any of the following fail, we log an error and continue event though sunshine will not function correctly. - // This allows access to the UI to fix configuration problems or view the logs. - if (platf::init()) - { - BOOST_LOG(error) << "Platform failed to initialize"sv; - return NULL; - } - else if (video::probe_encoders()) - { - BOOST_LOG(error) << "Video failed to find working encoder"sv; - return NULL; - } - init = true; - } +extern VideoPipeline *__cdecl StartQueue(int video_codec, char *display_name) { + static bool init = false; + if (!init) { + // If any of the following fail, we log an error and continue event + // though sunshine will not function correctly. This allows access to + // the UI to fix configuration problems or view the logs. + if (platf::init()) { + BOOST_LOG(error) << "Platform failed to initialize"sv; + return NULL; + } else if (video::probe_encoders()) { + BOOST_LOG(error) << "Video failed to find working encoder"sv; + return NULL; + } + init = true; + } - static VideoPipeline pipeline = {}; - pipeline.mail = std::make_shared(); - pipeline.monitor = {1920, 1080, 120, 6000, 1, 0, 1, 0, 0}; - pipeline.start = std::chrono::steady_clock::now(); + static VideoPipeline pipeline = {}; + pipeline.mail = std::make_shared(); + pipeline.monitor = {1920, 1080, 120, 6000, 1, 0, 1, 0, 0}; + pipeline.start = std::chrono::steady_clock::now(); - switch (video_codec) - { - case H265: // h265 - BOOST_LOG(info) << ("starting pipeline with h265 codec\n"); - pipeline.monitor.videoFormat = 1; - config::video.hevc_mode = 1; - config::video.av1_mode = 0; - break; - case AV1: // av1 - BOOST_LOG(info) << ("starting pipeline with av1 codec\n"); - pipeline.monitor.videoFormat = 2; - config::video.hevc_mode = 0; - config::video.av1_mode = 1; - break; - default: - BOOST_LOG(info) << ("starting pipeline with h264 codec\n"); - pipeline.monitor.videoFormat = 0; - config::video.hevc_mode = 0; - config::video.av1_mode = 0; - break; - } + switch (video_codec) { + case H265: // h265 + BOOST_LOG(info) << ("starting pipeline with h265 codec\n"); + pipeline.monitor.videoFormat = 1; + config::video.hevc_mode = 1; + config::video.av1_mode = 0; + break; + case AV1: // av1 + BOOST_LOG(info) << ("starting pipeline with av1 codec\n"); + pipeline.monitor.videoFormat = 2; + config::video.hevc_mode = 0; + config::video.av1_mode = 1; + break; + default: + BOOST_LOG(info) << ("starting pipeline with h264 codec\n"); + pipeline.monitor.videoFormat = 0; + config::video.hevc_mode = 0; + config::video.av1_mode = 0; + break; + } - auto thread = std::thread{[&](){ - video::capture( - pipeline.mail, - pipeline.monitor, - NULL); - }}; - thread.detach(); + auto thread = std::thread{ + [&]() { video::capture(pipeline.mail, pipeline.monitor, NULL); }}; + thread.detach(); - - RaiseEventS(&pipeline,CHANGE_DISPLAY,display_name); - return &pipeline; + RaiseEventS(&pipeline, CHANGE_DISPLAY, display_name); + return &pipeline; } -long long -duration_to_latency(std::chrono::steady_clock::duration duration) { - const auto duration_ns = std::chrono::duration_cast(duration).count(); - return std::clamp((duration_ns + 50) / 100, 0, std::numeric_limits::max()); +long long duration_to_latency(std::chrono::steady_clock::duration duration) { + const auto duration_ns = + std::chrono::duration_cast(duration).count(); + return std::clamp((duration_ns + 50) / 100, 0, + std::numeric_limits::max()); }; -int __cdecl -PopFromQueue(VideoPipeline *pipeline, - void *data, - int *duration) -{ - auto packet = pipeline->mail->queue(mail::video_packets)->pop(); - // if (packet->frame_timestamp) { - // *duration = duration_to_latency(*packet->frame_timestamp - pipeline->start); - // pipeline->start = *packet->frame_timestamp; - // } +int __cdecl PopFromQueue(VideoPipeline *pipeline, void *data, int *duration) { + auto packet = + pipeline->mail->queue(mail::video_packets)->pop(); + // if (packet->frame_timestamp) { + // *duration = duration_to_latency(*packet->frame_timestamp - + // pipeline->start); pipeline->start = *packet->frame_timestamp; + // } - memcpy(data, packet->data(), packet->data_size()); - int size = packet->data_size(); - return size; + memcpy(data, packet->data(), packet->data_size()); + int size = packet->data_size(); + return size; } -void __cdecl -RaiseEventS(VideoPipeline *pipeline, - EventType event, - char* value) -{ - switch (event) - { - case CHANGE_DISPLAY: // IDR FRAME - pipeline->mail->event(mail::switch_display)->raise(std::string(value)); - break; - default: - break; - } +void __cdecl RaiseEventS(VideoPipeline *pipeline, EventType event, + char *value) { + switch (event) { + case CHANGE_DISPLAY: // IDR FRAME + pipeline->mail->event(mail::switch_display) + ->raise(std::string(value)); + break; + default: + break; + } } -void __cdecl -RaiseEvent(VideoPipeline *pipeline, - EventType event, - int value) -{ - switch (event) - { - case IDR_FRAME: // IDR FRAME - pipeline->mail->event(mail::idr)->raise(true); - break; - case STOP: // IDR FRAME - pipeline->mail->event(mail::shutdown)->raise(true); - break; - case POINTER_VISIBLE: // IDR FRAME - pipeline->mail->event(mail::toggle_cursor)->raise(value != 0); - break; - case CHANGE_BITRATE: // IDR FRAME - pipeline->mail->event(mail::bitrate)->raise(value); - break; - default: - break; - } +void __cdecl RaiseEvent(VideoPipeline *pipeline, EventType event, int value) { + switch (event) { + case IDR_FRAME: // IDR FRAME + pipeline->mail->event(mail::idr)->raise(true); + break; + case STOP: // IDR FRAME + pipeline->mail->event(mail::shutdown)->raise(true); + break; + case POINTER_VISIBLE: // IDR FRAME + pipeline->mail->event(mail::toggle_cursor)->raise(value != 0); + break; + case CHANGE_BITRATE: // IDR FRAME + pipeline->mail->event(mail::bitrate)->raise(value); + break; + default: + break; + } } -void __cdecl -WaitEvent(VideoPipeline* pipeline, - EventType event) -{ - while(!PeekEvent(pipeline,event)) - std::this_thread::sleep_for(10ms); +void __cdecl WaitEvent(VideoPipeline *pipeline, EventType event) { + while (!PeekEvent(pipeline, event)) std::this_thread::sleep_for(10ms); } - -int __cdecl -PeekEvent(VideoPipeline* pipeline, - EventType event) -{ - switch (event) - { - case STOP: // IDR FRAME - return pipeline->mail->event(mail::shutdown)->peek(); - default: - return 0; - } +int __cdecl PeekEvent(VideoPipeline *pipeline, EventType event) { + switch (event) { + case STOP: // IDR FRAME + return pipeline->mail->event(mail::shutdown)->peek(); + default: + return 0; + } } - - diff --git a/src/dll.h b/src/dll.h index f4778e3d..9e603420 100644 --- a/src/dll.h +++ b/src/dll.h @@ -1,11 +1,9 @@ /* -*/ + */ extern "C" { - -typedef struct _VideoPipeline VideoPipeline; - +typedef struct _VideoPipeline VideoPipeline; typedef enum _EventType { POINTER_VISIBLE, @@ -14,54 +12,42 @@ typedef enum _EventType { IDR_FRAME, STOP -}EventType; +} EventType; typedef enum _Codec { H264 = 1, H265, AV1, -}Codec; +} Codec; __declspec(dllexport) VideoPipeline* __cdecl StartQueue(int video_codec, char* display_name); -__declspec(dllexport) int __cdecl PopFromQueue(VideoPipeline* pipeline, - void* data, - int* duration); +__declspec(dllexport) int __cdecl PopFromQueue(VideoPipeline* pipeline, + void* data, int* duration); __declspec(dllexport) void __cdecl RaiseEvent(VideoPipeline* pipeline, - EventType event, - int value); + EventType event, int value); __declspec(dllexport) void __cdecl RaiseEventS(VideoPipeline* pipeline, - EventType event, - char* value); + EventType event, char* value); -__declspec(dllexport) void __cdecl WaitEvent(VideoPipeline* pipeline, - EventType event); +__declspec(dllexport) void __cdecl WaitEvent(VideoPipeline* pipeline, + EventType event); +__declspec(dllexport) int __cdecl PeekEvent(VideoPipeline* pipeline, + EventType event); -__declspec(dllexport) int __cdecl PeekEvent(VideoPipeline* pipeline, - EventType event); +typedef VideoPipeline* (*STARTQUEUE)(int video_codec, char* display_name); -typedef VideoPipeline* (*STARTQUEUE) ( int video_codec, - char* display_name); +typedef int (*POPFROMQUEUE)(VideoPipeline* pipeline, void* data, int* duration); -typedef int (*POPFROMQUEUE) (VideoPipeline* pipeline, - void* data, - int* duration); +typedef void (*RAISEEVENT)(VideoPipeline* pipeline, EventType event, int value); -typedef void (*RAISEEVENT) (VideoPipeline* pipeline, - EventType event, - int value); +typedef void (*RAISEEVENTS)(VideoPipeline* pipeline, EventType event, + char* value); -typedef void (*RAISEEVENTS) (VideoPipeline* pipeline, - EventType event, - char* value); +typedef void (*WAITEVENT)(VideoPipeline* pipeline, EventType event); -typedef void (*WAITEVENT) (VideoPipeline* pipeline, - EventType event); - -typedef int (*PEEKEVENT) (VideoPipeline* pipeline, - EventType event); +typedef int (*PEEKEVENT)(VideoPipeline* pipeline, EventType event); } \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp index ec5e495b..c536a20b 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -4,81 +4,76 @@ */ // standard includes +#include + #include #include -#include #include "dll.h" +static HMODULE hModule; +static STARTQUEUE callstart; +static POPFROMQUEUE callpop; +static WAITEVENT callwait; +static RAISEEVENT callraise; +static RAISEEVENTS callraiseS; +static PEEKEVENT callpeek; +int initlibrary() { + char szFullPath[MAX_PATH] = {}; + GetCurrentDirectoryA(MAX_PATH, szFullPath); + strcat(szFullPath, "\\libsunshine.dll"); + hModule = LoadLibraryA(szFullPath); + if (hModule == 0) return 1; -static HMODULE hModule; -static STARTQUEUE callstart; -static POPFROMQUEUE callpop; -static WAITEVENT callwait; -static RAISEEVENT callraise; -static RAISEEVENTS callraiseS; -static PEEKEVENT callpeek; + callstart = (STARTQUEUE)GetProcAddress(hModule, "StartQueue"); + callpop = (POPFROMQUEUE)GetProcAddress(hModule, "PopFromQueue"); + callraise = (RAISEEVENT)GetProcAddress(hModule, "RaiseEvent"); + callraiseS = (RAISEEVENTS)GetProcAddress(hModule, "RaiseEventS"); + callwait = (WAITEVENT)GetProcAddress(hModule, "WaitEvent"); + callpeek = (PEEKEVENT)GetProcAddress(hModule, "PeekEvent"); -int -initlibrary() { - char szFullPath[MAX_PATH] = {}; - GetCurrentDirectoryA(MAX_PATH, szFullPath); - strcat(szFullPath, "\\libsunshine.dll"); - hModule = LoadLibraryA(szFullPath); - if(hModule == 0) - return 1; + if (callpop == 0 || callstart == 0 || callraise == 0 || callwait == 0) + return 1; - callstart = (STARTQUEUE) GetProcAddress( hModule,"StartQueue"); - callpop = (POPFROMQUEUE) GetProcAddress( hModule,"PopFromQueue"); - callraise = (RAISEEVENT) GetProcAddress( hModule,"RaiseEvent"); - callraiseS = (RAISEEVENTS) GetProcAddress( hModule,"RaiseEventS"); - callwait = (WAITEVENT) GetProcAddress( hModule,"WaitEvent"); - callpeek = (PEEKEVENT) GetProcAddress( hModule,"PeekEvent"); - - if(callpop ==0 || callstart == 0 || callraise == 0 || callwait == 0) - return 1; - - return 0; + return 0; } -int main(int argc, char *argv[]) -{ - if(initlibrary()) { - printf("failed to load libsunshine.dll"); - return 1; - } +int main(int argc, char *argv[]) { + if (initlibrary()) { + printf("failed to load libsunshine.dll"); + return 1; + } - // second encode session - for (int i =0; i < 30; i++) - { - VideoPipeline *pipeline = callstart(1,"\\\\.\\DISPLAY1"); - auto video = std::thread{[&]() { - // Video traffic is sent on this thread - int duration = 0; - void *data = malloc(100 * 1000 * 1000); + // second encode session + for (int i = 0; i < 30; i++) { + VideoPipeline *pipeline = callstart(1, "\\\\.\\DISPLAY1"); + auto video = std::thread{[&]() { + // Video traffic is sent on this thread + int duration = 0; + void *data = malloc(100 * 1000 * 1000); - int count = 0; - while (true) { - int size = callpop(pipeline, data, &duration); - if (callpeek(pipeline,STOP) || count == 1000) { - break; - } else if (count % 100 == 0) { - callraise(pipeline,CHANGE_BITRATE,2000); - } else if (count % 100 == 50) { - callraiseS(pipeline,CHANGE_DISPLAY,"\\\\.\\DISPLAY1"); - } + int count = 0; + while (true) { + int size = callpop(pipeline, data, &duration); + if (callpeek(pipeline, STOP) || count == 1000) { + break; + } else if (count % 100 == 0) { + callraise(pipeline, CHANGE_BITRATE, 2000); + } else if (count % 100 == 50) { + callraiseS(pipeline, CHANGE_DISPLAY, "\\\\.\\DISPLAY1"); + } - printf("received packet with size %d\n", size); - count++; - } + printf("received packet with size %d\n", size); + count++; + } - callraise(pipeline,STOP,0); - free(data); - }}; + callraise(pipeline, STOP, 0); + free(data); + }}; - callwait(pipeline,STOP); - video.join(); - } - return 0; + callwait(pipeline, STOP); + video.join(); + } + return 0; } \ No newline at end of file diff --git a/src/main.h b/src/main.h index 06366afb..a18c0800 100644 --- a/src/main.h +++ b/src/main.h @@ -11,40 +11,45 @@ #include // standard includes -#include -#include +#include /* assert */ +#include /* printf */ + #include #include #include +#include #include -#include /* printf */ -#include /* assert */ +#include // local includes #include "thread_safe.h" - extern boost::log::sources::severity_logger verbose; // Dominating output -extern boost::log::sources::severity_logger debug; // Follow what is happening -extern boost::log::sources::severity_logger info; // Should be informed about +extern boost::log::sources::severity_logger + debug; // Follow what is happening +extern boost::log::sources::severity_logger + info; // Should be informed about extern boost::log::sources::severity_logger warning; // Strange events -extern boost::log::sources::severity_logger error; // Recoverable errors +extern boost::log::sources::severity_logger error; // Recoverable errors extern boost::log::sources::severity_logger fatal; // Unrecoverable errors // namespaces namespace mail { -#define MAIL(x) constexpr auto x = std::string_view { #x } +#define MAIL(x) \ + constexpr auto x = std::string_view { \ +#x \ + } - //queue - MAIL(video_packets); +// queue +MAIL(video_packets); - //event - MAIL(shutdown); - MAIL(switch_display); - MAIL(toggle_cursor); - MAIL(idr); - MAIL(bitrate); - MAIL(hdr); +// event +MAIL(shutdown); +MAIL(switch_display); +MAIL(toggle_cursor); +MAIL(idr); +MAIL(bitrate); +MAIL(hdr); #undef MAIL diff --git a/src/move_by_copy.h b/src/move_by_copy.h index bac1faf9..15caed09 100644 --- a/src/move_by_copy.h +++ b/src/move_by_copy.h @@ -6,53 +6,45 @@ #include namespace move_by_copy_util { - /** - * When a copy is made, it moves the object - * This allows you to move an object when a move can't be done. - */ - template - class MoveByCopy { - public: +/** + * When a copy is made, it moves the object + * This allows you to move an object when a move can't be done. + */ +template +class MoveByCopy { + public: typedef T move_type; - private: + private: move_type _to_move; - public: - explicit MoveByCopy(move_type &&to_move): - _to_move(std::move(to_move)) {} + public: + explicit MoveByCopy(move_type &&to_move) : _to_move(std::move(to_move)) {} MoveByCopy(MoveByCopy &&other) = default; - MoveByCopy(const MoveByCopy &other) { - *this = other; + MoveByCopy(const MoveByCopy &other) { *this = other; } + + MoveByCopy &operator=(MoveByCopy &&other) = default; + + MoveByCopy &operator=(const MoveByCopy &other) { + this->_to_move = std::move(const_cast(other)._to_move); + + return *this; } - MoveByCopy & - operator=(MoveByCopy &&other) = default; + operator move_type() { return std::move(_to_move); } +}; - MoveByCopy & - operator=(const MoveByCopy &other) { - this->_to_move = std::move(const_cast(other)._to_move); - - return *this; - } - - operator move_type() { - return std::move(_to_move); - } - }; - - template - MoveByCopy - cmove(T &movable) { +template +MoveByCopy cmove(T &movable) { return MoveByCopy(std::move(movable)); - } +} - // Do NOT use this unless you are absolutely certain the object to be moved is no longer used by the caller - template - MoveByCopy - const_cmove(const T &movable) { +// Do NOT use this unless you are absolutely certain the object to be moved is +// no longer used by the caller +template +MoveByCopy const_cmove(const T &movable) { return MoveByCopy(std::move(const_cast(movable))); - } +} } // namespace move_by_copy_util diff --git a/src/nvenc/nvenc_base.cpp b/src/nvenc/nvenc_base.cpp index 21be8c4c..2b2b51d0 100644 --- a/src/nvenc/nvenc_base.cpp +++ b/src/nvenc/nvenc_base.cpp @@ -5,81 +5,78 @@ namespace { - GUID - quality_preset_guid_from_number(unsigned number) { +GUID quality_preset_guid_from_number(unsigned number) { if (number > 7) number = 7; switch (number) { - case 1: - default: - return NV_ENC_PRESET_P1_GUID; + case 1: + default: + return NV_ENC_PRESET_P1_GUID; - case 2: - return NV_ENC_PRESET_P2_GUID; + case 2: + return NV_ENC_PRESET_P2_GUID; - case 3: - return NV_ENC_PRESET_P3_GUID; + case 3: + return NV_ENC_PRESET_P3_GUID; - case 4: - return NV_ENC_PRESET_P4_GUID; + case 4: + return NV_ENC_PRESET_P4_GUID; - case 5: - return NV_ENC_PRESET_P5_GUID; + case 5: + return NV_ENC_PRESET_P5_GUID; - case 6: - return NV_ENC_PRESET_P6_GUID; + case 6: + return NV_ENC_PRESET_P6_GUID; - case 7: - return NV_ENC_PRESET_P7_GUID; + case 7: + return NV_ENC_PRESET_P7_GUID; } - }; +}; - bool - equal_guids(const GUID &guid1, const GUID &guid2) { +bool equal_guids(const GUID &guid1, const GUID &guid2) { return std::memcmp(&guid1, &guid2, sizeof(GUID)) == 0; - } +} - auto - quality_preset_string_from_guid(const GUID &guid) { +auto quality_preset_string_from_guid(const GUID &guid) { if (equal_guids(guid, NV_ENC_PRESET_P1_GUID)) { - return "P1"; + return "P1"; } if (equal_guids(guid, NV_ENC_PRESET_P2_GUID)) { - return "P2"; + return "P2"; } if (equal_guids(guid, NV_ENC_PRESET_P3_GUID)) { - return "P3"; + return "P3"; } if (equal_guids(guid, NV_ENC_PRESET_P4_GUID)) { - return "P4"; + return "P4"; } if (equal_guids(guid, NV_ENC_PRESET_P5_GUID)) { - return "P5"; + return "P5"; } if (equal_guids(guid, NV_ENC_PRESET_P6_GUID)) { - return "P6"; + return "P6"; } if (equal_guids(guid, NV_ENC_PRESET_P7_GUID)) { - return "P7"; + return "P7"; } return "Unknown"; - } +} } // namespace namespace nvenc { - nvenc_base::nvenc_base(NV_ENC_DEVICE_TYPE device_type, void *device): - device_type(device_type), - device(device) { - } +nvenc_base::nvenc_base(NV_ENC_DEVICE_TYPE device_type, void *device) + : device_type(device_type), device(device) {} - nvenc_base::~nvenc_base() { +nvenc_base::~nvenc_base() { // Use destroy_encoder() instead - } +} - bool - nvenc_base::create_encoder(const nvenc_config &config, const video::config_t &client_config, const nvenc_colorspace_t &colorspace, NV_ENC_BUFFER_FORMAT buffer_format) { +bool nvenc_base::create_encoder(const nvenc_config &config, + const video::config_t &client_config, + const nvenc_colorspace_t &colorspace, + NV_ENC_BUFFER_FORMAT buffer_format) { if (!nvenc && !init_library()) return false; if (encoder) destroy_encoder(); @@ -90,106 +87,129 @@ namespace nvenc { encoder_params.buffer_format = buffer_format; encoder_params.rfi = true; - NV_ENC_OPEN_ENCODE_SESSION_EX_PARAMS session_params = { NV_ENC_OPEN_ENCODE_SESSION_EX_PARAMS_VER }; + NV_ENC_OPEN_ENCODE_SESSION_EX_PARAMS session_params = { + NV_ENC_OPEN_ENCODE_SESSION_EX_PARAMS_VER}; session_params.device = device; session_params.deviceType = device_type; session_params.apiVersion = NVENCAPI_VERSION; - if (nvenc_failed(nvenc->nvEncOpenEncodeSessionEx(&session_params, &encoder))) { - BOOST_LOG(error) << "NvEncOpenEncodeSessionEx failed"; - return false; + if (nvenc_failed( + nvenc->nvEncOpenEncodeSessionEx(&session_params, &encoder))) { + BOOST_LOG(error) << "NvEncOpenEncodeSessionEx failed"; + return false; } uint32_t encode_guid_count = 0; - if (nvenc_failed(nvenc->nvEncGetEncodeGUIDCount(encoder, &encode_guid_count))) { - BOOST_LOG(error) << "NvEncGetEncodeGUIDCount failed: " << last_error_string; - return false; + if (nvenc_failed( + nvenc->nvEncGetEncodeGUIDCount(encoder, &encode_guid_count))) { + BOOST_LOG(error) << "NvEncGetEncodeGUIDCount failed: " + << last_error_string; + return false; }; std::vector encode_guids(encode_guid_count); - if (nvenc_failed(nvenc->nvEncGetEncodeGUIDs(encoder, encode_guids.data(), encode_guids.size(), &encode_guid_count))) { - BOOST_LOG(error) << "NvEncGetEncodeGUIDs failed: " << last_error_string; - return false; + if (nvenc_failed(nvenc->nvEncGetEncodeGUIDs(encoder, encode_guids.data(), + encode_guids.size(), + &encode_guid_count))) { + BOOST_LOG(error) << "NvEncGetEncodeGUIDs failed: " << last_error_string; + return false; } - init_params = { NV_ENC_INITIALIZE_PARAMS_VER }; + init_params = {NV_ENC_INITIALIZE_PARAMS_VER}; switch (client_config.videoFormat) { - case 0: - // H.264 - init_params.encodeGUID = NV_ENC_CODEC_H264_GUID; - break; + case 0: + // H.264 + init_params.encodeGUID = NV_ENC_CODEC_H264_GUID; + break; - case 1: - // HEVC - init_params.encodeGUID = NV_ENC_CODEC_HEVC_GUID; - break; + case 1: + // HEVC + init_params.encodeGUID = NV_ENC_CODEC_HEVC_GUID; + break; - case 2: - // AV1 - init_params.encodeGUID = NV_ENC_CODEC_AV1_GUID; - break; + case 2: + // AV1 + init_params.encodeGUID = NV_ENC_CODEC_AV1_GUID; + break; - default: - BOOST_LOG(error) << "NvEnc: unknown video format " << client_config.videoFormat; - return false; + default: + BOOST_LOG(error) + << "NvEnc: unknown video format " << client_config.videoFormat; + return false; } { - auto search_predicate = [&](const GUID &guid) { - return equal_guids(init_params.encodeGUID, guid); - }; - if (std::find_if(encode_guids.begin(), encode_guids.end(), search_predicate) == encode_guids.end()) { - BOOST_LOG(error) << "NvEnc: encoding format is not supported by the gpu"; - return false; - } + auto search_predicate = [&](const GUID &guid) { + return equal_guids(init_params.encodeGUID, guid); + }; + if (std::find_if(encode_guids.begin(), encode_guids.end(), + search_predicate) == encode_guids.end()) { + BOOST_LOG(error) + << "NvEnc: encoding format is not supported by the gpu"; + return false; + } } auto get_encoder_cap = [&](NV_ENC_CAPS cap) { - NV_ENC_CAPS_PARAM param = { NV_ENC_CAPS_PARAM_VER, cap }; - int value = 0; - nvenc->nvEncGetEncodeCaps(encoder, init_params.encodeGUID, ¶m, &value); - return value; + NV_ENC_CAPS_PARAM param = {NV_ENC_CAPS_PARAM_VER, cap}; + int value = 0; + nvenc->nvEncGetEncodeCaps(encoder, init_params.encodeGUID, ¶m, + &value); + return value; }; auto buffer_is_10bit = [&]() { - return buffer_format == NV_ENC_BUFFER_FORMAT_YUV420_10BIT || buffer_format == NV_ENC_BUFFER_FORMAT_YUV444_10BIT; + return buffer_format == NV_ENC_BUFFER_FORMAT_YUV420_10BIT || + buffer_format == NV_ENC_BUFFER_FORMAT_YUV444_10BIT; }; auto buffer_is_yuv444 = [&]() { - return buffer_format == NV_ENC_BUFFER_FORMAT_YUV444 || buffer_format == NV_ENC_BUFFER_FORMAT_YUV444_10BIT; + return buffer_format == NV_ENC_BUFFER_FORMAT_YUV444 || + buffer_format == NV_ENC_BUFFER_FORMAT_YUV444_10BIT; }; { - auto supported_width = get_encoder_cap(NV_ENC_CAPS_WIDTH_MAX); - auto supported_height = get_encoder_cap(NV_ENC_CAPS_HEIGHT_MAX); - if (encoder_params.width > supported_width || encoder_params.height > supported_height) { - BOOST_LOG(error) << "NvEnc: gpu max encode resolution " << supported_width << "x" << supported_height << ", requested " << encoder_params.width << "x" << encoder_params.height; + auto supported_width = get_encoder_cap(NV_ENC_CAPS_WIDTH_MAX); + auto supported_height = get_encoder_cap(NV_ENC_CAPS_HEIGHT_MAX); + if (encoder_params.width > supported_width || + encoder_params.height > supported_height) { + BOOST_LOG(error) + << "NvEnc: gpu max encode resolution " << supported_width << "x" + << supported_height << ", requested " << encoder_params.width + << "x" << encoder_params.height; + return false; + } + } + + if (buffer_is_10bit() && + !get_encoder_cap(NV_ENC_CAPS_SUPPORT_10BIT_ENCODE)) { + BOOST_LOG(error) << "NvEnc: gpu doesn't support 10-bit encode"; return false; - } } - if (buffer_is_10bit() && !get_encoder_cap(NV_ENC_CAPS_SUPPORT_10BIT_ENCODE)) { - BOOST_LOG(error) << "NvEnc: gpu doesn't support 10-bit encode"; - return false; + if (buffer_is_yuv444() && + !get_encoder_cap(NV_ENC_CAPS_SUPPORT_YUV444_ENCODE)) { + BOOST_LOG(error) << "NvEnc: gpu doesn't support YUV444 encode"; + return false; } - if (buffer_is_yuv444() && !get_encoder_cap(NV_ENC_CAPS_SUPPORT_YUV444_ENCODE)) { - BOOST_LOG(error) << "NvEnc: gpu doesn't support YUV444 encode"; - return false; + if (async_event_handle && + !get_encoder_cap(NV_ENC_CAPS_ASYNC_ENCODE_SUPPORT)) { + BOOST_LOG(warning) << "NvEnc: gpu doesn't support async encode"; + async_event_handle = nullptr; } - if (async_event_handle && !get_encoder_cap(NV_ENC_CAPS_ASYNC_ENCODE_SUPPORT)) { - BOOST_LOG(warning) << "NvEnc: gpu doesn't support async encode"; - async_event_handle = nullptr; - } + encoder_params.rfi = + get_encoder_cap(NV_ENC_CAPS_SUPPORT_REF_PIC_INVALIDATION); - encoder_params.rfi = get_encoder_cap(NV_ENC_CAPS_SUPPORT_REF_PIC_INVALIDATION); - - init_params.presetGUID = quality_preset_guid_from_number(config.quality_preset); + init_params.presetGUID = + quality_preset_guid_from_number(config.quality_preset); init_params.tuningInfo = NV_ENC_TUNING_INFO_ULTRA_LOW_LATENCY; init_params.enablePTD = 1; init_params.enableEncodeAsync = async_event_handle ? 1 : 0; - init_params.enableWeightedPrediction = config.weighted_prediction && get_encoder_cap(NV_ENC_CAPS_SUPPORT_WEIGHTED_PREDICTION); + init_params.enableWeightedPrediction = + config.weighted_prediction && + get_encoder_cap(NV_ENC_CAPS_SUPPORT_WEIGHTED_PREDICTION); init_params.encodeWidth = encoder_params.width; init_params.darWidth = encoder_params.width; @@ -198,10 +218,14 @@ namespace nvenc { init_params.frameRateNum = client_config.framerate; init_params.frameRateDen = 1; - NV_ENC_PRESET_CONFIG preset_config = { NV_ENC_PRESET_CONFIG_VER, { NV_ENC_CONFIG_VER } }; - if (nvenc_failed(nvenc->nvEncGetEncodePresetConfigEx(encoder, init_params.encodeGUID, init_params.presetGUID, init_params.tuningInfo, &preset_config))) { - BOOST_LOG(error) << "NvEncGetEncodePresetConfigEx failed: " << last_error_string; - return false; + NV_ENC_PRESET_CONFIG preset_config = {NV_ENC_PRESET_CONFIG_VER, + {NV_ENC_CONFIG_VER}}; + if (nvenc_failed(nvenc->nvEncGetEncodePresetConfigEx( + encoder, init_params.encodeGUID, init_params.presetGUID, + init_params.tuningInfo, &preset_config))) { + BOOST_LOG(error) << "NvEncGetEncodePresetConfigEx failed: " + << last_error_string; + return false; } NV_ENC_CONFIG enc_config = preset_config.presetCfg; @@ -213,220 +237,253 @@ namespace nvenc { enc_config.rcParams.zeroReorderDelay = 1; enc_config.rcParams.enableLookahead = 0; enc_config.rcParams.lowDelayKeyFrameScale = 1; - enc_config.rcParams.multiPass = config.two_pass == nvenc_two_pass::quarter_resolution ? NV_ENC_TWO_PASS_QUARTER_RESOLUTION : - config.two_pass == nvenc_two_pass::full_resolution ? NV_ENC_TWO_PASS_FULL_RESOLUTION : - NV_ENC_MULTI_PASS_DISABLED; + enc_config.rcParams.multiPass = + config.two_pass == nvenc_two_pass::quarter_resolution + ? NV_ENC_TWO_PASS_QUARTER_RESOLUTION + : config.two_pass == nvenc_two_pass::full_resolution + ? NV_ENC_TWO_PASS_FULL_RESOLUTION + : NV_ENC_MULTI_PASS_DISABLED; enc_config.rcParams.enableAQ = config.adaptive_quantization; enc_config.rcParams.averageBitRate = client_config.bitrate * 1000; if (get_encoder_cap(NV_ENC_CAPS_SUPPORT_CUSTOM_VBV_BUF_SIZE)) { - enc_config.rcParams.vbvBufferSize = client_config.bitrate * 1000 / client_config.framerate; + enc_config.rcParams.vbvBufferSize = + client_config.bitrate * 1000 / client_config.framerate; } auto set_h264_hevc_common_format_config = [&](auto &format_config) { - format_config.repeatSPSPPS = 1; - format_config.idrPeriod = NVENC_INFINITE_GOPLENGTH; - format_config.sliceMode = 3; - format_config.sliceModeData = client_config.slicesPerFrame; - if (buffer_is_yuv444()) { - format_config.chromaFormatIDC = 3; - } - format_config.enableFillerDataInsertion = config.insert_filler_data; + format_config.repeatSPSPPS = 1; + format_config.idrPeriod = NVENC_INFINITE_GOPLENGTH; + format_config.sliceMode = 3; + format_config.sliceModeData = client_config.slicesPerFrame; + if (buffer_is_yuv444()) { + format_config.chromaFormatIDC = 3; + } + format_config.enableFillerDataInsertion = config.insert_filler_data; }; - auto set_ref_frames = [&](uint32_t &ref_frames_option, NV_ENC_NUM_REF_FRAMES &L0_option, uint32_t ref_frames_default) { - if (client_config.numRefFrames > 0) { - ref_frames_option = client_config.numRefFrames; - } - else { - ref_frames_option = ref_frames_default; - } - if (ref_frames_option > 0 && !get_encoder_cap(NV_ENC_CAPS_SUPPORT_MULTIPLE_REF_FRAMES)) { - ref_frames_option = 1; - encoder_params.rfi = false; - } - encoder_params.ref_frames_in_dpb = ref_frames_option; - // This limits ref frames any frame can use to 1, but allows larger buffer size for fallback if some frames are invalidated through rfi - L0_option = NV_ENC_NUM_REF_FRAMES_1; + auto set_ref_frames = [&](uint32_t &ref_frames_option, + NV_ENC_NUM_REF_FRAMES &L0_option, + uint32_t ref_frames_default) { + if (client_config.numRefFrames > 0) { + ref_frames_option = client_config.numRefFrames; + } else { + ref_frames_option = ref_frames_default; + } + if (ref_frames_option > 0 && + !get_encoder_cap(NV_ENC_CAPS_SUPPORT_MULTIPLE_REF_FRAMES)) { + ref_frames_option = 1; + encoder_params.rfi = false; + } + encoder_params.ref_frames_in_dpb = ref_frames_option; + // This limits ref frames any frame can use to 1, but allows larger + // buffer size for fallback if some frames are invalidated through rfi + L0_option = NV_ENC_NUM_REF_FRAMES_1; }; auto set_minqp_if_enabled = [&](int value) { - if (config.enable_min_qp) { - enc_config.rcParams.enableMinQP = 1; - enc_config.rcParams.minQP.qpInterP = value; - enc_config.rcParams.minQP.qpIntra = value; - } + if (config.enable_min_qp) { + enc_config.rcParams.enableMinQP = 1; + enc_config.rcParams.minQP.qpInterP = value; + enc_config.rcParams.minQP.qpIntra = value; + } }; auto fill_h264_hevc_vui = [&colorspace](auto &vui_config) { - vui_config.videoSignalTypePresentFlag = 1; - vui_config.videoFormat = NV_ENC_VUI_VIDEO_FORMAT_UNSPECIFIED; - vui_config.videoFullRangeFlag = colorspace.full_range; - vui_config.colourDescriptionPresentFlag = 1; - vui_config.colourPrimaries = colorspace.primaries; - vui_config.transferCharacteristics = colorspace.tranfer_function; - vui_config.colourMatrix = colorspace.matrix; - vui_config.chromaSampleLocationFlag = 1; - vui_config.chromaSampleLocationTop = 0; - vui_config.chromaSampleLocationBot = 0; + vui_config.videoSignalTypePresentFlag = 1; + vui_config.videoFormat = NV_ENC_VUI_VIDEO_FORMAT_UNSPECIFIED; + vui_config.videoFullRangeFlag = colorspace.full_range; + vui_config.colourDescriptionPresentFlag = 1; + vui_config.colourPrimaries = colorspace.primaries; + vui_config.transferCharacteristics = colorspace.tranfer_function; + vui_config.colourMatrix = colorspace.matrix; + vui_config.chromaSampleLocationFlag = 1; + vui_config.chromaSampleLocationTop = 0; + vui_config.chromaSampleLocationBot = 0; }; switch (client_config.videoFormat) { - case 0: { - // H.264 - enc_config.profileGUID = buffer_is_yuv444() ? NV_ENC_H264_PROFILE_HIGH_444_GUID : NV_ENC_H264_PROFILE_HIGH_GUID; - auto &format_config = enc_config.encodeCodecConfig.h264Config; - set_h264_hevc_common_format_config(format_config); - if (config.h264_cavlc || !get_encoder_cap(NV_ENC_CAPS_SUPPORT_CABAC)) { - format_config.entropyCodingMode = NV_ENC_H264_ENTROPY_CODING_MODE_CAVLC; + case 0: { + // H.264 + enc_config.profileGUID = buffer_is_yuv444() + ? NV_ENC_H264_PROFILE_HIGH_444_GUID + : NV_ENC_H264_PROFILE_HIGH_GUID; + auto &format_config = enc_config.encodeCodecConfig.h264Config; + set_h264_hevc_common_format_config(format_config); + if (config.h264_cavlc || + !get_encoder_cap(NV_ENC_CAPS_SUPPORT_CABAC)) { + format_config.entropyCodingMode = + NV_ENC_H264_ENTROPY_CODING_MODE_CAVLC; + } else { + format_config.entropyCodingMode = + NV_ENC_H264_ENTROPY_CODING_MODE_CABAC; + } + set_ref_frames(format_config.maxNumRefFrames, + format_config.numRefL0, 5); + set_minqp_if_enabled(config.min_qp_h264); + fill_h264_hevc_vui(format_config.h264VUIParameters); + break; } - else { - format_config.entropyCodingMode = NV_ENC_H264_ENTROPY_CODING_MODE_CABAC; - } - set_ref_frames(format_config.maxNumRefFrames, format_config.numRefL0, 5); - set_minqp_if_enabled(config.min_qp_h264); - fill_h264_hevc_vui(format_config.h264VUIParameters); - break; - } - case 1: { - // HEVC - auto &format_config = enc_config.encodeCodecConfig.hevcConfig; - set_h264_hevc_common_format_config(format_config); - if (buffer_is_10bit()) { - format_config.pixelBitDepthMinus8 = 2; + case 1: { + // HEVC + auto &format_config = enc_config.encodeCodecConfig.hevcConfig; + set_h264_hevc_common_format_config(format_config); + if (buffer_is_10bit()) { + format_config.pixelBitDepthMinus8 = 2; + } + set_ref_frames(format_config.maxNumRefFramesInDPB, + format_config.numRefL0, 5); + set_minqp_if_enabled(config.min_qp_hevc); + fill_h264_hevc_vui(format_config.hevcVUIParameters); + break; } - set_ref_frames(format_config.maxNumRefFramesInDPB, format_config.numRefL0, 5); - set_minqp_if_enabled(config.min_qp_hevc); - fill_h264_hevc_vui(format_config.hevcVUIParameters); - break; - } - case 2: { - // AV1 - auto &format_config = enc_config.encodeCodecConfig.av1Config; - format_config.repeatSeqHdr = 1; - format_config.idrPeriod = NVENC_INFINITE_GOPLENGTH; - format_config.chromaFormatIDC = 1; // YUV444 not supported by NVENC yet - format_config.enableBitstreamPadding = config.insert_filler_data; - if (buffer_is_10bit()) { - format_config.inputPixelBitDepthMinus8 = 2; - format_config.pixelBitDepthMinus8 = 2; - } - format_config.colorPrimaries = colorspace.primaries; - format_config.transferCharacteristics = colorspace.tranfer_function; - format_config.matrixCoefficients = colorspace.matrix; - format_config.colorRange = colorspace.full_range; - format_config.chromaSamplePosition = 1; - set_ref_frames(format_config.maxNumRefFramesInDPB, format_config.numFwdRefs, 8); - set_minqp_if_enabled(config.min_qp_av1); + case 2: { + // AV1 + auto &format_config = enc_config.encodeCodecConfig.av1Config; + format_config.repeatSeqHdr = 1; + format_config.idrPeriod = NVENC_INFINITE_GOPLENGTH; + format_config.chromaFormatIDC = + 1; // YUV444 not supported by NVENC yet + format_config.enableBitstreamPadding = config.insert_filler_data; + if (buffer_is_10bit()) { + format_config.inputPixelBitDepthMinus8 = 2; + format_config.pixelBitDepthMinus8 = 2; + } + format_config.colorPrimaries = colorspace.primaries; + format_config.transferCharacteristics = colorspace.tranfer_function; + format_config.matrixCoefficients = colorspace.matrix; + format_config.colorRange = colorspace.full_range; + format_config.chromaSamplePosition = 1; + set_ref_frames(format_config.maxNumRefFramesInDPB, + format_config.numFwdRefs, 8); + set_minqp_if_enabled(config.min_qp_av1); - if (client_config.slicesPerFrame > 1) { - // NVENC only supports slice counts that are powers of two, so we'll pick powers of two - // with bias to rows due to hopefully more similar macroblocks with a row vs a column. - format_config.numTileRows = std::pow(2, std::ceil(std::log2(client_config.slicesPerFrame) / 2)); - format_config.numTileColumns = std::pow(2, std::floor(std::log2(client_config.slicesPerFrame) / 2)); + if (client_config.slicesPerFrame > 1) { + // NVENC only supports slice counts that are powers of two, so + // we'll pick powers of two with bias to rows due to hopefully + // more similar macroblocks with a row vs a column. + format_config.numTileRows = std::pow( + 2, std::ceil(std::log2(client_config.slicesPerFrame) / 2)); + format_config.numTileColumns = std::pow( + 2, std::floor(std::log2(client_config.slicesPerFrame) / 2)); + } + break; } - break; - } } init_params.encodeConfig = &enc_config; if (nvenc_failed(nvenc->nvEncInitializeEncoder(encoder, &init_params))) { - BOOST_LOG(error) << "NvEncInitializeEncoder failed: " << last_error_string; - return false; + BOOST_LOG(error) << "NvEncInitializeEncoder failed: " + << last_error_string; + return false; } if (async_event_handle) { - NV_ENC_EVENT_PARAMS event_params = { NV_ENC_EVENT_PARAMS_VER }; - event_params.completionEvent = async_event_handle; - if (nvenc_failed(nvenc->nvEncRegisterAsyncEvent(encoder, &event_params))) { - BOOST_LOG(error) << "NvEncRegisterAsyncEvent failed: " << last_error_string; - return false; - } + NV_ENC_EVENT_PARAMS event_params = {NV_ENC_EVENT_PARAMS_VER}; + event_params.completionEvent = async_event_handle; + if (nvenc_failed( + nvenc->nvEncRegisterAsyncEvent(encoder, &event_params))) { + BOOST_LOG(error) + << "NvEncRegisterAsyncEvent failed: " << last_error_string; + return false; + } } - NV_ENC_CREATE_BITSTREAM_BUFFER create_bitstream_buffer = { NV_ENC_CREATE_BITSTREAM_BUFFER_VER }; - if (nvenc_failed(nvenc->nvEncCreateBitstreamBuffer(encoder, &create_bitstream_buffer))) { - BOOST_LOG(error) << "NvEncCreateBitstreamBuffer failed: " << last_error_string; - return false; + NV_ENC_CREATE_BITSTREAM_BUFFER create_bitstream_buffer = { + NV_ENC_CREATE_BITSTREAM_BUFFER_VER}; + if (nvenc_failed(nvenc->nvEncCreateBitstreamBuffer( + encoder, &create_bitstream_buffer))) { + BOOST_LOG(error) << "NvEncCreateBitstreamBuffer failed: " + << last_error_string; + return false; } output_bitstream = create_bitstream_buffer.bitstreamBuffer; if (!create_and_register_input_buffer()) { - return false; + return false; } { - // auto f = stat_trackers::one_digit_after_decimal(); - // BOOST_LOG(debug) << "NvEnc: requested encoded frame size " << f % (client_config.bitrate / 8. / client_config.framerate) << " kB"; + // auto f = stat_trackers::one_digit_after_decimal(); + // BOOST_LOG(debug) << "NvEnc: requested encoded frame size " << f % + // (client_config.bitrate / 8. / client_config.framerate) << " kB"; } { - std::string extra; - if (init_params.enableEncodeAsync) extra += " async"; - if (buffer_is_10bit()) extra += " 10-bit"; - if (enc_config.rcParams.multiPass != NV_ENC_MULTI_PASS_DISABLED) extra += " two-pass"; - if (encoder_params.rfi) extra += " rfi"; - if (init_params.enableWeightedPrediction) extra += " weighted-prediction"; - if (enc_config.rcParams.enableAQ) extra += " adaptive-quantization"; - if (enc_config.rcParams.enableMinQP) extra += " qpmin=" + std::to_string(enc_config.rcParams.minQP.qpInterP); - if (config.insert_filler_data) extra += " filler-data"; - BOOST_LOG(info) << "NvEnc: created encoder " << quality_preset_string_from_guid(init_params.presetGUID) << extra; + std::string extra; + if (init_params.enableEncodeAsync) extra += " async"; + if (buffer_is_10bit()) extra += " 10-bit"; + if (enc_config.rcParams.multiPass != NV_ENC_MULTI_PASS_DISABLED) + extra += " two-pass"; + if (encoder_params.rfi) extra += " rfi"; + if (init_params.enableWeightedPrediction) + extra += " weighted-prediction"; + if (enc_config.rcParams.enableAQ) extra += " adaptive-quantization"; + if (enc_config.rcParams.enableMinQP) + extra += + " qpmin=" + std::to_string(enc_config.rcParams.minQP.qpInterP); + if (config.insert_filler_data) extra += " filler-data"; + BOOST_LOG(info) << "NvEnc: created encoder " + << quality_preset_string_from_guid( + init_params.presetGUID) + << extra; } encoder_state = {}; fail_guard.disable(); return true; - } +} - void - nvenc_base::destroy_encoder() { +void nvenc_base::destroy_encoder() { if (output_bitstream) { - nvenc->nvEncDestroyBitstreamBuffer(encoder, output_bitstream); - output_bitstream = nullptr; + nvenc->nvEncDestroyBitstreamBuffer(encoder, output_bitstream); + output_bitstream = nullptr; } if (encoder && async_event_handle) { - NV_ENC_EVENT_PARAMS event_params = { NV_ENC_EVENT_PARAMS_VER }; - event_params.completionEvent = async_event_handle; - nvenc->nvEncUnregisterAsyncEvent(encoder, &event_params); + NV_ENC_EVENT_PARAMS event_params = {NV_ENC_EVENT_PARAMS_VER}; + event_params.completionEvent = async_event_handle; + nvenc->nvEncUnregisterAsyncEvent(encoder, &event_params); } if (registered_input_buffer) { - nvenc->nvEncUnregisterResource(encoder, registered_input_buffer); - registered_input_buffer = nullptr; + nvenc->nvEncUnregisterResource(encoder, registered_input_buffer); + registered_input_buffer = nullptr; } if (encoder) { - nvenc->nvEncDestroyEncoder(encoder); - encoder = nullptr; + nvenc->nvEncDestroyEncoder(encoder); + encoder = nullptr; } encoder_state = {}; encoder_params = {}; - } +} - - - nvenc_encoded_frame - nvenc_base::encode_frame(uint64_t frame_index, bool force_idr) { +nvenc_encoded_frame nvenc_base::encode_frame(uint64_t frame_index, + bool force_idr) { if (!encoder) { - return {}; + return {}; } assert(registered_input_buffer); assert(output_bitstream); - NV_ENC_MAP_INPUT_RESOURCE mapped_input_buffer = { NV_ENC_MAP_INPUT_RESOURCE_VER }; + NV_ENC_MAP_INPUT_RESOURCE mapped_input_buffer = { + NV_ENC_MAP_INPUT_RESOURCE_VER}; mapped_input_buffer.registeredResource = registered_input_buffer; - if (nvenc_failed(nvenc->nvEncMapInputResource(encoder, &mapped_input_buffer))) { - BOOST_LOG(error) << "NvEncMapInputResource failed: " << last_error_string; - return {}; + if (nvenc_failed( + nvenc->nvEncMapInputResource(encoder, &mapped_input_buffer))) { + BOOST_LOG(error) << "NvEncMapInputResource failed: " + << last_error_string; + return {}; } - auto unmap_guard = util::fail_guard([&] { nvenc->nvEncUnmapInputResource(encoder, &mapped_input_buffer); }); + auto unmap_guard = util::fail_guard( + [&] { nvenc->nvEncUnmapInputResource(encoder, &mapped_input_buffer); }); - NV_ENC_PIC_PARAMS pic_params = { NV_ENC_PIC_PARAMS_VER }; + NV_ENC_PIC_PARAMS pic_params = {NV_ENC_PIC_PARAMS_VER}; pic_params.inputWidth = encoder_params.width; pic_params.inputHeight = encoder_params.height; pic_params.encodePicFlags = force_idr ? NV_ENC_PIC_FLAG_FORCEIDR : 0; @@ -438,112 +495,117 @@ namespace nvenc { pic_params.completionEvent = async_event_handle; if (nvenc_failed(nvenc->nvEncEncodePicture(encoder, &pic_params))) { - BOOST_LOG(error) << "NvEncEncodePicture failed: " << last_error_string; - return {}; + BOOST_LOG(error) << "NvEncEncodePicture failed: " << last_error_string; + return {}; } - NV_ENC_LOCK_BITSTREAM lock_bitstream = { NV_ENC_LOCK_BITSTREAM_VER }; + NV_ENC_LOCK_BITSTREAM lock_bitstream = {NV_ENC_LOCK_BITSTREAM_VER}; lock_bitstream.outputBitstream = output_bitstream; lock_bitstream.doNotWait = 0; if (async_event_handle && !wait_for_async_event(100)) { - BOOST_LOG(error) << "NvEnc: frame " << frame_index << " encode wait timeout"; - return {}; + BOOST_LOG(error) << "NvEnc: frame " << frame_index + << " encode wait timeout"; + return {}; } if (nvenc_failed(nvenc->nvEncLockBitstream(encoder, &lock_bitstream))) { - BOOST_LOG(error) << "NvEncLockBitstream failed: " << last_error_string; - return {}; + BOOST_LOG(error) << "NvEncLockBitstream failed: " << last_error_string; + return {}; } - auto data_pointer = (uint8_t *) lock_bitstream.bitstreamBufferPtr; - nvenc_encoded_frame encoded_frame { - { data_pointer, data_pointer + lock_bitstream.bitstreamSizeInBytes }, - lock_bitstream.outputTimeStamp, - lock_bitstream.pictureType == NV_ENC_PIC_TYPE_IDR, - encoder_state.rfi_needs_confirmation, + auto data_pointer = (uint8_t *)lock_bitstream.bitstreamBufferPtr; + nvenc_encoded_frame encoded_frame{ + {data_pointer, data_pointer + lock_bitstream.bitstreamSizeInBytes}, + lock_bitstream.outputTimeStamp, + lock_bitstream.pictureType == NV_ENC_PIC_TYPE_IDR, + encoder_state.rfi_needs_confirmation, }; if (encoder_state.rfi_needs_confirmation) { - // Invalidation request has been fulfilled, and video network packet will be marked as such - encoder_state.rfi_needs_confirmation = false; + // Invalidation request has been fulfilled, and video network packet + // will be marked as such + encoder_state.rfi_needs_confirmation = false; } encoder_state.last_encoded_frame_index = frame_index; if (encoded_frame.idr) { - BOOST_LOG(debug) << "NvEnc: idr frame " << encoded_frame.frame_index; + BOOST_LOG(debug) << "NvEnc: idr frame " << encoded_frame.frame_index; } - if (nvenc_failed(nvenc->nvEncUnlockBitstream(encoder, lock_bitstream.outputBitstream))) { - BOOST_LOG(error) << "NvEncUnlockBitstream failed: " << last_error_string; + if (nvenc_failed(nvenc->nvEncUnlockBitstream( + encoder, lock_bitstream.outputBitstream))) { + BOOST_LOG(error) << "NvEncUnlockBitstream failed: " + << last_error_string; } // if (config::sunshine.min_log_level <= 1) { // // Print encoded frame size stats to debug log every 20 seconds // auto callback = [&](float stat_min, float stat_max, double stat_avg) { // auto f = stat_trackers::one_digit_after_decimal(); - // BOOST_LOG(debug) << "NvEnc: encoded frame sizes (min max avg) " << f % stat_min << " " << f % stat_max << " " << f % stat_avg << " kB"; + // BOOST_LOG(debug) << "NvEnc: encoded frame sizes (min max avg) " << f + // % stat_min << " " << f % stat_max << " " << f % stat_avg << " kB"; // }; // using namespace std::literals; - // encoder_state.frame_size_tracker.collect_and_callback_on_interval(encoded_frame.data.size() / 1000., callback, 20s); + // encoder_state.frame_size_tracker.collect_and_callback_on_interval(encoded_frame.data.size() + // / 1000., callback, 20s); // } return encoded_frame; - } +} - - bool - nvenc_base::nvenc_failed(NVENCSTATUS status) { +bool nvenc_base::nvenc_failed(NVENCSTATUS status) { auto status_string = [](NVENCSTATUS status) -> std::string { - switch (status) { + switch (status) { #define nvenc_status_case(x) \ - case x: \ - return #x; - nvenc_status_case(NV_ENC_SUCCESS); - nvenc_status_case(NV_ENC_ERR_NO_ENCODE_DEVICE); - nvenc_status_case(NV_ENC_ERR_UNSUPPORTED_DEVICE); - nvenc_status_case(NV_ENC_ERR_INVALID_ENCODERDEVICE); - nvenc_status_case(NV_ENC_ERR_INVALID_DEVICE); - nvenc_status_case(NV_ENC_ERR_DEVICE_NOT_EXIST); - nvenc_status_case(NV_ENC_ERR_INVALID_PTR); - nvenc_status_case(NV_ENC_ERR_INVALID_EVENT); - nvenc_status_case(NV_ENC_ERR_INVALID_PARAM); - nvenc_status_case(NV_ENC_ERR_INVALID_CALL); - nvenc_status_case(NV_ENC_ERR_OUT_OF_MEMORY); - nvenc_status_case(NV_ENC_ERR_ENCODER_NOT_INITIALIZED); - nvenc_status_case(NV_ENC_ERR_UNSUPPORTED_PARAM); - nvenc_status_case(NV_ENC_ERR_LOCK_BUSY); - nvenc_status_case(NV_ENC_ERR_NOT_ENOUGH_BUFFER); - nvenc_status_case(NV_ENC_ERR_INVALID_VERSION); - nvenc_status_case(NV_ENC_ERR_MAP_FAILED); - nvenc_status_case(NV_ENC_ERR_NEED_MORE_INPUT); - nvenc_status_case(NV_ENC_ERR_ENCODER_BUSY); - nvenc_status_case(NV_ENC_ERR_EVENT_NOT_REGISTERD); - nvenc_status_case(NV_ENC_ERR_GENERIC); - nvenc_status_case(NV_ENC_ERR_INCOMPATIBLE_CLIENT_KEY); - nvenc_status_case(NV_ENC_ERR_UNIMPLEMENTED); - nvenc_status_case(NV_ENC_ERR_RESOURCE_REGISTER_FAILED); - nvenc_status_case(NV_ENC_ERR_RESOURCE_NOT_REGISTERED); - nvenc_status_case(NV_ENC_ERR_RESOURCE_NOT_MAPPED); - // Newer versions of sdk may add more constants, look for them the end of NVENCSTATUS enum + case x: \ + return #x; + nvenc_status_case(NV_ENC_SUCCESS); + nvenc_status_case(NV_ENC_ERR_NO_ENCODE_DEVICE); + nvenc_status_case(NV_ENC_ERR_UNSUPPORTED_DEVICE); + nvenc_status_case(NV_ENC_ERR_INVALID_ENCODERDEVICE); + nvenc_status_case(NV_ENC_ERR_INVALID_DEVICE); + nvenc_status_case(NV_ENC_ERR_DEVICE_NOT_EXIST); + nvenc_status_case(NV_ENC_ERR_INVALID_PTR); + nvenc_status_case(NV_ENC_ERR_INVALID_EVENT); + nvenc_status_case(NV_ENC_ERR_INVALID_PARAM); + nvenc_status_case(NV_ENC_ERR_INVALID_CALL); + nvenc_status_case(NV_ENC_ERR_OUT_OF_MEMORY); + nvenc_status_case(NV_ENC_ERR_ENCODER_NOT_INITIALIZED); + nvenc_status_case(NV_ENC_ERR_UNSUPPORTED_PARAM); + nvenc_status_case(NV_ENC_ERR_LOCK_BUSY); + nvenc_status_case(NV_ENC_ERR_NOT_ENOUGH_BUFFER); + nvenc_status_case(NV_ENC_ERR_INVALID_VERSION); + nvenc_status_case(NV_ENC_ERR_MAP_FAILED); + nvenc_status_case(NV_ENC_ERR_NEED_MORE_INPUT); + nvenc_status_case(NV_ENC_ERR_ENCODER_BUSY); + nvenc_status_case(NV_ENC_ERR_EVENT_NOT_REGISTERD); + nvenc_status_case(NV_ENC_ERR_GENERIC); + nvenc_status_case(NV_ENC_ERR_INCOMPATIBLE_CLIENT_KEY); + nvenc_status_case(NV_ENC_ERR_UNIMPLEMENTED); + nvenc_status_case(NV_ENC_ERR_RESOURCE_REGISTER_FAILED); + nvenc_status_case(NV_ENC_ERR_RESOURCE_NOT_REGISTERED); + nvenc_status_case(NV_ENC_ERR_RESOURCE_NOT_MAPPED); + // Newer versions of sdk may add more constants, look for them the + // end of NVENCSTATUS enum #undef nvenc_status_case - default: - return std::to_string(status); - } + default: + return std::to_string(status); + } }; last_error_string.clear(); if (status != NV_ENC_SUCCESS) { - if (nvenc && encoder) { - last_error_string = nvenc->nvEncGetLastErrorString(encoder); - if (!last_error_string.empty()) last_error_string += " "; - } - last_error_string += status_string(status); - return true; + if (nvenc && encoder) { + last_error_string = nvenc->nvEncGetLastErrorString(encoder); + if (!last_error_string.empty()) last_error_string += " "; + } + last_error_string += status_string(status); + return true; } return false; - } +} } // namespace nvenc diff --git a/src/nvenc/nvenc_base.h b/src/nvenc/nvenc_base.h index cb2a0ec5..873d77a1 100644 --- a/src/nvenc/nvenc_base.h +++ b/src/nvenc/nvenc_base.h @@ -1,46 +1,39 @@ #pragma once +#include + #include "nvenc_colorspace.h" #include "nvenc_config.h" #include "nvenc_encoded_frame.h" - #include "src/video.h" -#include - namespace nvenc { - class nvenc_base { - public: +class nvenc_base { + public: nvenc_base(NV_ENC_DEVICE_TYPE device_type, void *device); virtual ~nvenc_base(); nvenc_base(const nvenc_base &) = delete; - nvenc_base & - operator=(const nvenc_base &) = delete; + nvenc_base &operator=(const nvenc_base &) = delete; - bool - create_encoder(const nvenc_config &config, const video::config_t &client_config, const nvenc_colorspace_t &colorspace, NV_ENC_BUFFER_FORMAT buffer_format); + bool create_encoder(const nvenc_config &config, + const video::config_t &client_config, + const nvenc_colorspace_t &colorspace, + NV_ENC_BUFFER_FORMAT buffer_format); - void - destroy_encoder(); + void destroy_encoder(); + nvenc_encoded_frame encode_frame(uint64_t frame_index, bool force_idr); - nvenc_encoded_frame - encode_frame(uint64_t frame_index, bool force_idr); + protected: + virtual bool init_library() = 0; - protected: - virtual bool - init_library() = 0; + virtual bool create_and_register_input_buffer() = 0; - virtual bool - create_and_register_input_buffer() = 0; + virtual bool wait_for_async_event(uint32_t timeout_ms) { return false; } - virtual bool - wait_for_async_event(uint32_t timeout_ms) { return false; } - - bool - nvenc_failed(NVENCSTATUS status); + bool nvenc_failed(NVENCSTATUS status); const NV_ENC_DEVICE_TYPE device_type; void *const device; @@ -51,11 +44,11 @@ namespace nvenc { NV_ENC_INITIALIZE_PARAMS init_params; struct { - uint32_t width = 0; - uint32_t height = 0; - NV_ENC_BUFFER_FORMAT buffer_format = NV_ENC_BUFFER_FORMAT_UNDEFINED; - uint32_t ref_frames_in_dpb = 0; - bool rfi = false; + uint32_t width = 0; + uint32_t height = 0; + NV_ENC_BUFFER_FORMAT buffer_format = NV_ENC_BUFFER_FORMAT_UNDEFINED; + uint32_t ref_frames_in_dpb = 0; + bool rfi = false; } encoder_params; // Derived classes set these variables @@ -64,15 +57,15 @@ namespace nvenc { std::string last_error_string; - private: + private: NV_ENC_OUTPUT_PTR output_bitstream = nullptr; struct { - uint64_t last_encoded_frame_index = 0; - bool rfi_needs_confirmation = false; - std::pair last_rfi_range; - // stat_trackers::min_max_avg_tracker frame_size_tracker; + uint64_t last_encoded_frame_index = 0; + bool rfi_needs_confirmation = false; + std::pair last_rfi_range; + // stat_trackers::min_max_avg_tracker frame_size_tracker; } encoder_state; - }; +}; } // namespace nvenc diff --git a/src/nvenc/nvenc_colorspace.h b/src/nvenc/nvenc_colorspace.h index 9ebcb10f..703ec305 100644 --- a/src/nvenc/nvenc_colorspace.h +++ b/src/nvenc/nvenc_colorspace.h @@ -3,10 +3,10 @@ #include namespace nvenc { - struct nvenc_colorspace_t { +struct nvenc_colorspace_t { NV_ENC_VUI_COLOR_PRIMARIES primaries; NV_ENC_VUI_TRANSFER_CHARACTERISTIC tranfer_function; NV_ENC_VUI_MATRIX_COEFFS matrix; bool full_range; - }; +}; } // namespace nvenc diff --git a/src/nvenc/nvenc_config.h b/src/nvenc/nvenc_config.h index 632146b7..7f19fa2f 100644 --- a/src/nvenc/nvenc_config.h +++ b/src/nvenc/nvenc_config.h @@ -2,7 +2,7 @@ namespace nvenc { - enum class nvenc_two_pass { +enum class nvenc_two_pass { // Single pass, the fastest and no extra vram disabled, @@ -11,22 +11,25 @@ namespace nvenc { // Better overall statistics, slower and uses more extra vram full_resolution, - }; +}; - struct nvenc_config { +struct nvenc_config { // Quality preset from 1 to 7, higher is slower int quality_preset = 1; - // Use optional preliminary pass for better motion vectors, bitrate distribution and stricter VBV(HRD), uses CUDA cores + // Use optional preliminary pass for better motion vectors, bitrate + // distribution and stricter VBV(HRD), uses CUDA cores nvenc_two_pass two_pass = nvenc_two_pass::quarter_resolution; // Improves fades compression, uses CUDA cores bool weighted_prediction = false; - // Allocate more bitrate to flat regions since they're visually more perceptible, uses CUDA cores + // Allocate more bitrate to flat regions since they're visually more + // perceptible, uses CUDA cores bool adaptive_quantization = false; - // Don't use QP below certain value, limits peak image quality to save bitrate + // Don't use QP below certain value, limits peak image quality to save + // bitrate bool enable_min_qp = false; // Min QP value for H.264 when enable_min_qp is selected @@ -38,11 +41,13 @@ namespace nvenc { // Min QP value for AV1 when enable_min_qp is selected unsigned min_qp_av1 = 23; - // Use CAVLC entropy coding in H.264 instead of CABAC, not relevant and here for historical reasons + // Use CAVLC entropy coding in H.264 instead of CABAC, not relevant and here + // for historical reasons bool h264_cavlc = false; - // Add filler data to encoded frames to stay at target bitrate, mainly for testing + // Add filler data to encoded frames to stay at target bitrate, mainly for + // testing bool insert_filler_data = false; - }; +}; } // namespace nvenc diff --git a/src/nvenc/nvenc_d3d11.cpp b/src/nvenc/nvenc_d3d11.cpp index e86d2268..ad2d6af6 100644 --- a/src/nvenc/nvenc_d3d11.cpp +++ b/src/nvenc/nvenc_d3d11.cpp @@ -1,104 +1,106 @@ #ifdef _WIN32 - #include "nvenc_d3d11.h" +#include "nvenc_d3d11.h" - #include "nvenc_utils.h" +#include "nvenc_utils.h" namespace nvenc { - nvenc_d3d11::nvenc_d3d11(ID3D11Device *d3d_device): - nvenc_base(NV_ENC_DEVICE_TYPE_DIRECTX, d3d_device), - d3d_device(d3d_device) { - } +nvenc_d3d11::nvenc_d3d11(ID3D11Device *d3d_device) + : nvenc_base(NV_ENC_DEVICE_TYPE_DIRECTX, d3d_device), + d3d_device(d3d_device) {} - nvenc_d3d11::~nvenc_d3d11() { +nvenc_d3d11::~nvenc_d3d11() { if (encoder) destroy_encoder(); if (dll) { - FreeLibrary(dll); - dll = NULL; + FreeLibrary(dll); + dll = NULL; } - } +} - ID3D11Texture2D * - nvenc_d3d11::get_input_texture() { +ID3D11Texture2D *nvenc_d3d11::get_input_texture() { return d3d_input_texture.GetInterfacePtr(); - } +} - bool - nvenc_d3d11::init_library() { +bool nvenc_d3d11::init_library() { if (dll) return true; - #ifdef _WIN64 +#ifdef _WIN64 auto dll_name = "nvEncodeAPI64.dll"; - #else +#else auto dll_name = "nvEncodeAPI.dll"; - #endif +#endif if ((dll = LoadLibraryEx(dll_name, NULL, LOAD_LIBRARY_SEARCH_SYSTEM32))) { - if (auto create_instance = (decltype(NvEncodeAPICreateInstance) *) GetProcAddress(dll, "NvEncodeAPICreateInstance")) { - auto new_nvenc = std::make_unique(); - new_nvenc->version = NV_ENCODE_API_FUNCTION_LIST_VER; - if (nvenc_failed(create_instance(new_nvenc.get()))) { - BOOST_LOG(error) << "NvEncodeAPICreateInstance failed: " << last_error_string; + if (auto create_instance = + (decltype(NvEncodeAPICreateInstance) *)GetProcAddress( + dll, "NvEncodeAPICreateInstance")) { + auto new_nvenc = std::make_unique(); + new_nvenc->version = NV_ENCODE_API_FUNCTION_LIST_VER; + if (nvenc_failed(create_instance(new_nvenc.get()))) { + BOOST_LOG(error) << "NvEncodeAPICreateInstance failed: " + << last_error_string; + } else { + nvenc = std::move(new_nvenc); + return true; + } + } else { + BOOST_LOG(error) << "No NvEncodeAPICreateInstance in " << dll_name; } - else { - nvenc = std::move(new_nvenc); - return true; - } - } - else { - BOOST_LOG(error) << "No NvEncodeAPICreateInstance in " << dll_name; - } - } - else { - BOOST_LOG(debug) << "Couldn't load NvEnc library " << dll_name; + } else { + BOOST_LOG(debug) << "Couldn't load NvEnc library " << dll_name; } if (dll) { - FreeLibrary(dll); - dll = NULL; + FreeLibrary(dll); + dll = NULL; } return false; - } +} - bool - nvenc_d3d11::create_and_register_input_buffer() { +bool nvenc_d3d11::create_and_register_input_buffer() { if (!d3d_input_texture) { - D3D11_TEXTURE2D_DESC desc = {}; - desc.Width = encoder_params.width; - desc.Height = encoder_params.height; - desc.MipLevels = 1; - desc.ArraySize = 1; - desc.Format = dxgi_format_from_nvenc_format(encoder_params.buffer_format); - desc.SampleDesc.Count = 1; - desc.Usage = D3D11_USAGE_DEFAULT; - desc.BindFlags = D3D11_BIND_RENDER_TARGET; - if (d3d_device->CreateTexture2D(&desc, nullptr, &d3d_input_texture) != S_OK) { - BOOST_LOG(error) << "NvEnc: couldn't create input texture"; - return false; - } + D3D11_TEXTURE2D_DESC desc = {}; + desc.Width = encoder_params.width; + desc.Height = encoder_params.height; + desc.MipLevels = 1; + desc.ArraySize = 1; + desc.Format = + dxgi_format_from_nvenc_format(encoder_params.buffer_format); + desc.SampleDesc.Count = 1; + desc.Usage = D3D11_USAGE_DEFAULT; + desc.BindFlags = D3D11_BIND_RENDER_TARGET; + if (d3d_device->CreateTexture2D(&desc, nullptr, &d3d_input_texture) != + S_OK) { + BOOST_LOG(error) << "NvEnc: couldn't create input texture"; + return false; + } } if (!registered_input_buffer) { - NV_ENC_REGISTER_RESOURCE register_resource = { NV_ENC_REGISTER_RESOURCE_VER }; - register_resource.resourceType = NV_ENC_INPUT_RESOURCE_TYPE_DIRECTX; - register_resource.width = encoder_params.width; - register_resource.height = encoder_params.height; - register_resource.resourceToRegister = d3d_input_texture.GetInterfacePtr(); - register_resource.bufferFormat = encoder_params.buffer_format; - register_resource.bufferUsage = NV_ENC_INPUT_IMAGE; + NV_ENC_REGISTER_RESOURCE register_resource = { + NV_ENC_REGISTER_RESOURCE_VER}; + register_resource.resourceType = NV_ENC_INPUT_RESOURCE_TYPE_DIRECTX; + register_resource.width = encoder_params.width; + register_resource.height = encoder_params.height; + register_resource.resourceToRegister = + d3d_input_texture.GetInterfacePtr(); + register_resource.bufferFormat = encoder_params.buffer_format; + register_resource.bufferUsage = NV_ENC_INPUT_IMAGE; - if (nvenc_failed(nvenc->nvEncRegisterResource(encoder, ®ister_resource))) { - BOOST_LOG(error) << "NvEncRegisterResource failed: " << last_error_string; - return false; - } + if (nvenc_failed( + nvenc->nvEncRegisterResource(encoder, ®ister_resource))) { + BOOST_LOG(error) + << "NvEncRegisterResource failed: " << last_error_string; + return false; + } - registered_input_buffer = register_resource.registeredResource; + registered_input_buffer = register_resource.registeredResource; } return true; - } +} } // namespace nvenc #endif diff --git a/src/nvenc/nvenc_d3d11.h b/src/nvenc/nvenc_d3d11.h index ef1b8d4c..132cada8 100644 --- a/src/nvenc/nvenc_d3d11.h +++ b/src/nvenc/nvenc_d3d11.h @@ -1,35 +1,32 @@ #pragma once #ifdef _WIN32 - #include - #include +#include +#include - #include "nvenc_base.h" +#include "nvenc_base.h" namespace nvenc { - _COM_SMARTPTR_TYPEDEF(ID3D11Device, IID_ID3D11Device); - _COM_SMARTPTR_TYPEDEF(ID3D11Texture2D, IID_ID3D11Texture2D); +_COM_SMARTPTR_TYPEDEF(ID3D11Device, IID_ID3D11Device); +_COM_SMARTPTR_TYPEDEF(ID3D11Texture2D, IID_ID3D11Texture2D); - class nvenc_d3d11 final: public nvenc_base { - public: +class nvenc_d3d11 final : public nvenc_base { + public: nvenc_d3d11(ID3D11Device *d3d_device); ~nvenc_d3d11(); - ID3D11Texture2D * - get_input_texture(); + ID3D11Texture2D *get_input_texture(); - private: - bool - init_library() override; + private: + bool init_library() override; - bool - create_and_register_input_buffer() override; + bool create_and_register_input_buffer() override; HMODULE dll = NULL; const ID3D11DevicePtr d3d_device; ID3D11Texture2DPtr d3d_input_texture; - }; +}; } // namespace nvenc #endif diff --git a/src/nvenc/nvenc_encoded_frame.h b/src/nvenc/nvenc_encoded_frame.h index f60ba302..f1197af7 100644 --- a/src/nvenc/nvenc_encoded_frame.h +++ b/src/nvenc/nvenc_encoded_frame.h @@ -4,10 +4,10 @@ #include namespace nvenc { - struct nvenc_encoded_frame { +struct nvenc_encoded_frame { std::vector data; uint64_t frame_index = 0; bool idr = false; bool after_ref_frame_invalidation = false; - }; +}; } // namespace nvenc diff --git a/src/nvenc/nvenc_utils.cpp b/src/nvenc/nvenc_utils.cpp index 261e9096..4859b3b5 100644 --- a/src/nvenc/nvenc_utils.cpp +++ b/src/nvenc/nvenc_utils.cpp @@ -3,74 +3,78 @@ namespace nvenc { #ifdef _WIN32 - DXGI_FORMAT - dxgi_format_from_nvenc_format(NV_ENC_BUFFER_FORMAT format) { +DXGI_FORMAT +dxgi_format_from_nvenc_format(NV_ENC_BUFFER_FORMAT format) { switch (format) { - case NV_ENC_BUFFER_FORMAT_YUV420_10BIT: - return DXGI_FORMAT_P010; + case NV_ENC_BUFFER_FORMAT_YUV420_10BIT: + return DXGI_FORMAT_P010; - case NV_ENC_BUFFER_FORMAT_NV12: - return DXGI_FORMAT_NV12; + case NV_ENC_BUFFER_FORMAT_NV12: + return DXGI_FORMAT_NV12; - default: - return DXGI_FORMAT_UNKNOWN; + default: + return DXGI_FORMAT_UNKNOWN; } - } +} #endif - NV_ENC_BUFFER_FORMAT - nvenc_format_from_sunshine_format(platf::pix_fmt_e format) { +NV_ENC_BUFFER_FORMAT +nvenc_format_from_sunshine_format(platf::pix_fmt_e format) { switch (format) { - case platf::pix_fmt_e::nv12: - return NV_ENC_BUFFER_FORMAT_NV12; + case platf::pix_fmt_e::nv12: + return NV_ENC_BUFFER_FORMAT_NV12; - case platf::pix_fmt_e::p010: - return NV_ENC_BUFFER_FORMAT_YUV420_10BIT; + case platf::pix_fmt_e::p010: + return NV_ENC_BUFFER_FORMAT_YUV420_10BIT; - default: - return NV_ENC_BUFFER_FORMAT_UNDEFINED; + default: + return NV_ENC_BUFFER_FORMAT_UNDEFINED; } - } +} - nvenc_colorspace_t - nvenc_colorspace_from_sunshine_colorspace(const video::sunshine_colorspace_t &sunshine_colorspace) { +nvenc_colorspace_t nvenc_colorspace_from_sunshine_colorspace( + const video::sunshine_colorspace_t &sunshine_colorspace) { nvenc_colorspace_t colorspace; switch (sunshine_colorspace.colorspace) { - case video::colorspace_e::rec601: - // Rec. 601 - colorspace.primaries = NV_ENC_VUI_COLOR_PRIMARIES_SMPTE170M; - colorspace.tranfer_function = NV_ENC_VUI_TRANSFER_CHARACTERISTIC_SMPTE170M; - colorspace.matrix = NV_ENC_VUI_MATRIX_COEFFS_SMPTE170M; - break; + case video::colorspace_e::rec601: + // Rec. 601 + colorspace.primaries = NV_ENC_VUI_COLOR_PRIMARIES_SMPTE170M; + colorspace.tranfer_function = + NV_ENC_VUI_TRANSFER_CHARACTERISTIC_SMPTE170M; + colorspace.matrix = NV_ENC_VUI_MATRIX_COEFFS_SMPTE170M; + break; - case video::colorspace_e::rec709: - // Rec. 709 - colorspace.primaries = NV_ENC_VUI_COLOR_PRIMARIES_BT709; - colorspace.tranfer_function = NV_ENC_VUI_TRANSFER_CHARACTERISTIC_BT709; - colorspace.matrix = NV_ENC_VUI_MATRIX_COEFFS_BT709; - break; + case video::colorspace_e::rec709: + // Rec. 709 + colorspace.primaries = NV_ENC_VUI_COLOR_PRIMARIES_BT709; + colorspace.tranfer_function = + NV_ENC_VUI_TRANSFER_CHARACTERISTIC_BT709; + colorspace.matrix = NV_ENC_VUI_MATRIX_COEFFS_BT709; + break; - case video::colorspace_e::bt2020sdr: - // Rec. 2020 - colorspace.primaries = NV_ENC_VUI_COLOR_PRIMARIES_BT2020; - assert(sunshine_colorspace.bit_depth == 10); - colorspace.tranfer_function = NV_ENC_VUI_TRANSFER_CHARACTERISTIC_BT2020_10; - colorspace.matrix = NV_ENC_VUI_MATRIX_COEFFS_BT2020_NCL; - break; + case video::colorspace_e::bt2020sdr: + // Rec. 2020 + colorspace.primaries = NV_ENC_VUI_COLOR_PRIMARIES_BT2020; + assert(sunshine_colorspace.bit_depth == 10); + colorspace.tranfer_function = + NV_ENC_VUI_TRANSFER_CHARACTERISTIC_BT2020_10; + colorspace.matrix = NV_ENC_VUI_MATRIX_COEFFS_BT2020_NCL; + break; - case video::colorspace_e::bt2020: - // Rec. 2020 with ST 2084 perceptual quantizer - colorspace.primaries = NV_ENC_VUI_COLOR_PRIMARIES_BT2020; - assert(sunshine_colorspace.bit_depth == 10); - colorspace.tranfer_function = NV_ENC_VUI_TRANSFER_CHARACTERISTIC_SMPTE2084; - colorspace.matrix = NV_ENC_VUI_MATRIX_COEFFS_BT2020_NCL; - break; + case video::colorspace_e::bt2020: + // Rec. 2020 with ST 2084 perceptual quantizer + colorspace.primaries = NV_ENC_VUI_COLOR_PRIMARIES_BT2020; + assert(sunshine_colorspace.bit_depth == 10); + colorspace.tranfer_function = + NV_ENC_VUI_TRANSFER_CHARACTERISTIC_SMPTE2084; + colorspace.matrix = NV_ENC_VUI_MATRIX_COEFFS_BT2020_NCL; + break; } colorspace.full_range = sunshine_colorspace.full_range; return colorspace; - } +} } // namespace nvenc diff --git a/src/nvenc/nvenc_utils.h b/src/nvenc/nvenc_utils.h index 67af1037..cf70a12f 100644 --- a/src/nvenc/nvenc_utils.h +++ b/src/nvenc/nvenc_utils.h @@ -1,27 +1,26 @@ #pragma once #ifdef _WIN32 - #include +#include #endif -#include "nvenc_colorspace.h" +#include +#include "nvenc_colorspace.h" #include "src/platform/common.h" #include "src/video_colorspace.h" -#include - namespace nvenc { #ifdef _WIN32 - DXGI_FORMAT - dxgi_format_from_nvenc_format(NV_ENC_BUFFER_FORMAT format); +DXGI_FORMAT +dxgi_format_from_nvenc_format(NV_ENC_BUFFER_FORMAT format); #endif - NV_ENC_BUFFER_FORMAT - nvenc_format_from_sunshine_format(platf::pix_fmt_e format); +NV_ENC_BUFFER_FORMAT +nvenc_format_from_sunshine_format(platf::pix_fmt_e format); - nvenc_colorspace_t - nvenc_colorspace_from_sunshine_colorspace(const video::sunshine_colorspace_t &sunshine_colorspace); +nvenc_colorspace_t nvenc_colorspace_from_sunshine_colorspace( + const video::sunshine_colorspace_t &sunshine_colorspace); } // namespace nvenc diff --git a/src/platform/common.h b/src/platform/common.h index 68f77980..f69d34ce 100644 --- a/src/platform/common.h +++ b/src/platform/common.h @@ -15,225 +15,200 @@ #include "src/utility.h" #include "src/video_colorspace.h" -extern "C" { -} +extern "C" {} typedef struct _SS_HDR_METADATA { // RGB order struct { - uint16_t x; // Normalized to 50,000 - uint16_t y; // Normalized to 50,000 + uint16_t x; // Normalized to 50,000 + uint16_t y; // Normalized to 50,000 } displayPrimaries[3]; struct { - uint16_t x; // Normalized to 50,000 - uint16_t y; // Normalized to 50,000 + uint16_t x; // Normalized to 50,000 + uint16_t y; // Normalized to 50,000 } whitePoint; - uint16_t maxDisplayLuminance; // Nits - uint16_t minDisplayLuminance; // 1/10000th of a nit + uint16_t maxDisplayLuminance; // Nits + uint16_t minDisplayLuminance; // 1/10000th of a nit - // These are content-specific values which may not be available for all hosts. - uint16_t maxContentLightLevel; // Nits - uint16_t maxFrameAverageLightLevel; // Nits + // These are content-specific values which may not be available for all + // hosts. + uint16_t maxContentLightLevel; // Nits + uint16_t maxFrameAverageLightLevel; // Nits - // These are display-specific values which may not be available for all hosts. - uint16_t maxFullFrameLuminance; // Nits + // These are display-specific values which may not be available for all + // hosts. + uint16_t maxFullFrameLuminance; // Nits } SS_HDR_METADATA, *PSS_HDR_METADATA; struct sockaddr; struct AVFrame; struct AVBufferRef; struct AVHWFramesContext; - namespace video { - struct config_t; +struct config_t; } // namespace video namespace nvenc { - class nvenc_base; +class nvenc_base; } namespace platf { - // Limited by bits in activeGamepadMask - constexpr auto MAX_GAMEPADS = 16; +// Limited by bits in activeGamepadMask +constexpr auto MAX_GAMEPADS = 16; - constexpr std::uint32_t DPAD_UP = 0x0001; - constexpr std::uint32_t DPAD_DOWN = 0x0002; - constexpr std::uint32_t DPAD_LEFT = 0x0004; - constexpr std::uint32_t DPAD_RIGHT = 0x0008; - constexpr std::uint32_t START = 0x0010; - constexpr std::uint32_t BACK = 0x0020; - constexpr std::uint32_t LEFT_STICK = 0x0040; - constexpr std::uint32_t RIGHT_STICK = 0x0080; - constexpr std::uint32_t LEFT_BUTTON = 0x0100; - constexpr std::uint32_t RIGHT_BUTTON = 0x0200; - constexpr std::uint32_t HOME = 0x0400; - constexpr std::uint32_t A = 0x1000; - constexpr std::uint32_t B = 0x2000; - constexpr std::uint32_t X = 0x4000; - constexpr std::uint32_t Y = 0x8000; - constexpr std::uint32_t PADDLE1 = 0x010000; - constexpr std::uint32_t PADDLE2 = 0x020000; - constexpr std::uint32_t PADDLE3 = 0x040000; - constexpr std::uint32_t PADDLE4 = 0x080000; - constexpr std::uint32_t TOUCHPAD_BUTTON = 0x100000; - constexpr std::uint32_t MISC_BUTTON = 0x200000; +constexpr std::uint32_t DPAD_UP = 0x0001; +constexpr std::uint32_t DPAD_DOWN = 0x0002; +constexpr std::uint32_t DPAD_LEFT = 0x0004; +constexpr std::uint32_t DPAD_RIGHT = 0x0008; +constexpr std::uint32_t START = 0x0010; +constexpr std::uint32_t BACK = 0x0020; +constexpr std::uint32_t LEFT_STICK = 0x0040; +constexpr std::uint32_t RIGHT_STICK = 0x0080; +constexpr std::uint32_t LEFT_BUTTON = 0x0100; +constexpr std::uint32_t RIGHT_BUTTON = 0x0200; +constexpr std::uint32_t HOME = 0x0400; +constexpr std::uint32_t A = 0x1000; +constexpr std::uint32_t B = 0x2000; +constexpr std::uint32_t X = 0x4000; +constexpr std::uint32_t Y = 0x8000; +constexpr std::uint32_t PADDLE1 = 0x010000; +constexpr std::uint32_t PADDLE2 = 0x020000; +constexpr std::uint32_t PADDLE3 = 0x040000; +constexpr std::uint32_t PADDLE4 = 0x080000; +constexpr std::uint32_t TOUCHPAD_BUTTON = 0x100000; +constexpr std::uint32_t MISC_BUTTON = 0x200000; - enum class gamepad_feedback_e { +enum class gamepad_feedback_e { rumble, rumble_triggers, set_motion_event_state, set_rgb_led, - }; +}; - struct gamepad_feedback_msg_t { - static gamepad_feedback_msg_t - make_rumble(std::uint16_t id, std::uint16_t lowfreq, std::uint16_t highfreq) { - gamepad_feedback_msg_t msg; - msg.type = gamepad_feedback_e::rumble; - msg.id = id; - msg.data.rumble = { lowfreq, highfreq }; - return msg; +struct gamepad_feedback_msg_t { + static gamepad_feedback_msg_t make_rumble(std::uint16_t id, + std::uint16_t lowfreq, + std::uint16_t highfreq) { + gamepad_feedback_msg_t msg; + msg.type = gamepad_feedback_e::rumble; + msg.id = id; + msg.data.rumble = {lowfreq, highfreq}; + return msg; } - static gamepad_feedback_msg_t - make_rumble_triggers(std::uint16_t id, std::uint16_t left, std::uint16_t right) { - gamepad_feedback_msg_t msg; - msg.type = gamepad_feedback_e::rumble_triggers; - msg.id = id; - msg.data.rumble_triggers = { left, right }; - return msg; + static gamepad_feedback_msg_t make_rumble_triggers(std::uint16_t id, + std::uint16_t left, + std::uint16_t right) { + gamepad_feedback_msg_t msg; + msg.type = gamepad_feedback_e::rumble_triggers; + msg.id = id; + msg.data.rumble_triggers = {left, right}; + return msg; } - static gamepad_feedback_msg_t - make_motion_event_state(std::uint16_t id, std::uint8_t motion_type, std::uint16_t report_rate) { - gamepad_feedback_msg_t msg; - msg.type = gamepad_feedback_e::set_motion_event_state; - msg.id = id; - msg.data.motion_event_state.motion_type = motion_type; - msg.data.motion_event_state.report_rate = report_rate; - return msg; + static gamepad_feedback_msg_t make_motion_event_state( + std::uint16_t id, std::uint8_t motion_type, std::uint16_t report_rate) { + gamepad_feedback_msg_t msg; + msg.type = gamepad_feedback_e::set_motion_event_state; + msg.id = id; + msg.data.motion_event_state.motion_type = motion_type; + msg.data.motion_event_state.report_rate = report_rate; + return msg; } - static gamepad_feedback_msg_t - make_rgb_led(std::uint16_t id, std::uint8_t r, std::uint8_t g, std::uint8_t b) { - gamepad_feedback_msg_t msg; - msg.type = gamepad_feedback_e::set_rgb_led; - msg.id = id; - msg.data.rgb_led = { r, g, b }; - return msg; + static gamepad_feedback_msg_t make_rgb_led(std::uint16_t id, std::uint8_t r, + std::uint8_t g, std::uint8_t b) { + gamepad_feedback_msg_t msg; + msg.type = gamepad_feedback_e::set_rgb_led; + msg.id = id; + msg.data.rgb_led = {r, g, b}; + return msg; } gamepad_feedback_e type; std::uint16_t id; union { - struct { - std::uint16_t lowfreq; - std::uint16_t highfreq; - } rumble; - struct { - std::uint16_t left_trigger; - std::uint16_t right_trigger; - } rumble_triggers; - struct { - std::uint16_t report_rate; - std::uint8_t motion_type; - } motion_event_state; - struct { - std::uint8_t r; - std::uint8_t g; - std::uint8_t b; - } rgb_led; + struct { + std::uint16_t lowfreq; + std::uint16_t highfreq; + } rumble; + struct { + std::uint16_t left_trigger; + std::uint16_t right_trigger; + } rumble_triggers; + struct { + std::uint16_t report_rate; + std::uint8_t motion_type; + } motion_event_state; + struct { + std::uint8_t r; + std::uint8_t g; + std::uint8_t b; + } rgb_led; } data; - }; +}; - using feedback_queue_t = safe::mail_raw_t::queue_t; +using feedback_queue_t = safe::mail_raw_t::queue_t; - namespace speaker { - enum speaker_e { - FRONT_LEFT, - FRONT_RIGHT, - FRONT_CENTER, - LOW_FREQUENCY, - BACK_LEFT, - BACK_RIGHT, - SIDE_LEFT, - SIDE_RIGHT, - MAX_SPEAKERS, - }; +namespace speaker { +enum speaker_e { + FRONT_LEFT, + FRONT_RIGHT, + FRONT_CENTER, + LOW_FREQUENCY, + BACK_LEFT, + BACK_RIGHT, + SIDE_LEFT, + SIDE_RIGHT, + MAX_SPEAKERS, +}; - constexpr std::uint8_t map_stereo[] { - FRONT_LEFT, FRONT_RIGHT - }; - constexpr std::uint8_t map_surround51[] { - FRONT_LEFT, - FRONT_RIGHT, - FRONT_CENTER, - LOW_FREQUENCY, - BACK_LEFT, - BACK_RIGHT, - }; - constexpr std::uint8_t map_surround71[] { - FRONT_LEFT, - FRONT_RIGHT, - FRONT_CENTER, - LOW_FREQUENCY, - BACK_LEFT, - BACK_RIGHT, - SIDE_LEFT, - SIDE_RIGHT, - }; - } // namespace speaker +constexpr std::uint8_t map_stereo[]{FRONT_LEFT, FRONT_RIGHT}; +constexpr std::uint8_t map_surround51[]{ + FRONT_LEFT, FRONT_RIGHT, FRONT_CENTER, LOW_FREQUENCY, BACK_LEFT, BACK_RIGHT, +}; +constexpr std::uint8_t map_surround71[]{ + FRONT_LEFT, FRONT_RIGHT, FRONT_CENTER, LOW_FREQUENCY, + BACK_LEFT, BACK_RIGHT, SIDE_LEFT, SIDE_RIGHT, +}; +} // namespace speaker - enum class mem_type_e { - system, - vaapi, - dxgi, - cuda, - videotoolbox, - unknown - }; +enum class mem_type_e { system, vaapi, dxgi, cuda, videotoolbox, unknown }; - enum class pix_fmt_e { - yuv420p, - yuv420p10, - nv12, - p010, - unknown - }; +enum class pix_fmt_e { yuv420p, yuv420p10, nv12, p010, unknown }; - inline std::string_view - from_pix_fmt(pix_fmt_e pix_fmt) { +inline std::string_view from_pix_fmt(pix_fmt_e pix_fmt) { using namespace std::literals; -#define _CONVERT(x) \ - case pix_fmt_e::x: \ - return #x##sv +#define _CONVERT(x) \ + case pix_fmt_e::x: \ + return #x##sv switch (pix_fmt) { - _CONVERT(yuv420p); - _CONVERT(yuv420p10); - _CONVERT(nv12); - _CONVERT(p010); - _CONVERT(unknown); + _CONVERT(yuv420p); + _CONVERT(yuv420p10); + _CONVERT(nv12); + _CONVERT(p010); + _CONVERT(unknown); } #undef _CONVERT return "unknown"sv; - } +} - // Dimensions for touchscreen input - struct touch_port_t { +// Dimensions for touchscreen input +struct touch_port_t { int offset_x, offset_y; int width, height; - }; +}; - // These values must match Limelight-internal.h's SS_FF_* constants! - namespace platform_caps { - typedef uint32_t caps_t; +// These values must match Limelight-internal.h's SS_FF_* constants! +namespace platform_caps { +typedef uint32_t caps_t; - constexpr caps_t pen_touch = 0x01; // Pen and touch events - constexpr caps_t controller_touch = 0x02; // Controller touch events - }; // namespace platform_caps +constexpr caps_t pen_touch = 0x01; // Pen and touch events +constexpr caps_t controller_touch = 0x02; // Controller touch events +}; // namespace platform_caps - struct gamepad_state_t { +struct gamepad_state_t { std::uint32_t buttonFlags; std::uint8_t lt; std::uint8_t rt; @@ -241,9 +216,9 @@ namespace platf { std::int16_t lsY; std::int16_t rsX; std::int16_t rsY; - }; +}; - struct gamepad_id_t { +struct gamepad_id_t { // The global index is used when looking up gamepads in the platform's // gamepad array. It identifies gamepads uniquely among all clients. int globalIndex; @@ -252,24 +227,24 @@ namespace platf { // client. It must be used when communicating back to the client via // the input feedback queue. std::uint8_t clientRelativeIndex; - }; +}; - struct gamepad_arrival_t { +struct gamepad_arrival_t { std::uint8_t type; std::uint16_t capabilities; std::uint32_t supportedButtons; - }; +}; - struct gamepad_touch_t { +struct gamepad_touch_t { gamepad_id_t id; std::uint8_t eventType; std::uint32_t pointerId; float x; float y; float pressure; - }; +}; - struct gamepad_motion_t { +struct gamepad_motion_t { gamepad_id_t id; std::uint8_t motionType; @@ -278,15 +253,15 @@ namespace platf { float x; float y; float z; - }; +}; - struct gamepad_battery_t { +struct gamepad_battery_t { gamepad_id_t id; std::uint8_t state; std::uint8_t percentage; - }; +}; - struct touch_input_t { +struct touch_input_t { std::uint8_t eventType; std::uint16_t rotation; // Degrees (0..360) or LI_ROT_UNKNOWN std::uint32_t pointerId; @@ -295,128 +270,113 @@ namespace platf { float pressureOrDistance; // Distance for hover and pressure for contact float contactAreaMajor; float contactAreaMinor; - }; +}; - struct pen_input_t { +struct pen_input_t { std::uint8_t eventType; std::uint8_t toolType; std::uint8_t penButtons; - std::uint8_t tilt; // Degrees (0..90) or LI_TILT_UNKNOWN + std::uint8_t tilt; // Degrees (0..90) or LI_TILT_UNKNOWN std::uint16_t rotation; // Degrees (0..360) or LI_ROT_UNKNOWN float x; float y; float pressureOrDistance; // Distance for hover and pressure for contact float contactAreaMajor; float contactAreaMinor; - }; +}; - class deinit_t { - public: +class deinit_t { + public: virtual ~deinit_t() = default; - }; +}; - struct img_t: std::enable_shared_from_this { - public: +struct img_t : std::enable_shared_from_this { + public: img_t() = default; img_t(img_t &&) = delete; img_t(const img_t &) = delete; - img_t & - operator=(img_t &&) = delete; - img_t & - operator=(const img_t &) = delete; + img_t &operator=(img_t &&) = delete; + img_t &operator=(const img_t &) = delete; - std::uint8_t *data {}; - std::int32_t width {}; - std::int32_t height {}; - std::int32_t pixel_pitch {}; - std::int32_t row_pitch {}; + std::uint8_t *data{}; + std::int32_t width{}; + std::int32_t height{}; + std::int32_t pixel_pitch{}; + std::int32_t row_pitch{}; std::optional frame_timestamp; virtual ~img_t() = default; - }; +}; - struct sink_t { +struct sink_t { // Play on host PC std::string host; // On macOS and Windows, it is not possible to create a virtual sink // Therefore, it is optional struct null_t { - std::string stereo; - std::string surround51; - std::string surround71; + std::string stereo; + std::string surround51; + std::string surround71; }; std::optional null; - }; +}; - struct encode_device_t { +struct encode_device_t { virtual ~encode_device_t() = default; - virtual int - convert(platf::img_t &img) = 0; + virtual int convert(platf::img_t &img) = 0; video::sunshine_colorspace_t colorspace; - }; +}; - struct avcodec_encode_device_t: encode_device_t { - void *data {}; - AVFrame *frame {}; +struct avcodec_encode_device_t : encode_device_t { + void *data{}; + AVFrame *frame{}; - int - convert(platf::img_t &img) override { - return -1; - } + int convert(platf::img_t &img) override { return -1; } - virtual void - apply_colorspace() { - } + virtual void apply_colorspace() {} /** * implementations must take ownership of 'frame' */ - virtual int - set_frame(AVFrame *frame, AVBufferRef *hw_frames_ctx) { - BOOST_LOG(error) << "Illegal call to hwdevice_t::set_frame(). Did you forget to override it?"; - return -1; + virtual int set_frame(AVFrame *frame, AVBufferRef *hw_frames_ctx) { + BOOST_LOG(error) << "Illegal call to hwdevice_t::set_frame(). Did you " + "forget to override it?"; + return -1; }; /** - * Implementations may set parameters during initialization of the hwframes context + * Implementations may set parameters during initialization of the hwframes + * context */ - virtual void - init_hwframes(AVHWFramesContext *frames) {}; + virtual void init_hwframes(AVHWFramesContext *frames){}; /** * Implementations may make modifications required before context derivation */ - virtual int - prepare_to_derive_context(int hw_device_type) { - return 0; - }; - }; + virtual int prepare_to_derive_context(int hw_device_type) { return 0; }; +}; - struct nvenc_encode_device_t: encode_device_t { - virtual bool - init_encoder(const video::config_t &client_config, const video::sunshine_colorspace_t &colorspace) = 0; +struct nvenc_encode_device_t : encode_device_t { + virtual bool init_encoder( + const video::config_t &client_config, + const video::sunshine_colorspace_t &colorspace) = 0; nvenc::nvenc_base *nvenc = nullptr; - }; +}; - enum class capture_e : int { - ok, - reinit, - timeout, - interrupted, - error - }; +enum class capture_e : int { ok, reinit, timeout, interrupted, error }; - class display_t { - public: +class display_t { + public: /** - * When display has a new image ready or a timeout occurs, this callback will be called with the image. - * If a frame was captured, frame_captured will be true. If a timeout occurred, it will be false. + * When display has a new image ready or a timeout occurs, this callback + * will be called with the image. If a frame was captured, frame_captured + * will be true. If a timeout occurred, it will be false. * * On Break Request --> * Returns false @@ -424,7 +384,8 @@ namespace platf { * On Success --> * Returns true */ - using push_captured_image_cb_t = std::function &&img, bool frame_captured)>; + using push_captured_image_cb_t = + std::function &&img, bool frame_captured)>; /** * Use to get free image from the pool. Calls must be synchronized. @@ -434,53 +395,49 @@ namespace platf { * 'true' on success, img_out contains free image * 'false' when capture has been interrupted, img_out contains nullptr */ - using pull_free_image_cb_t = std::function &img_out)>; + using pull_free_image_cb_t = + std::function &img_out)>; - display_t() noexcept: - offset_x { 0 }, offset_y { 0 } {} + display_t() noexcept : offset_x{0}, offset_y{0} {} /** - * push_captured_image_cb --> The callback that is called with captured image, - * must be called from the same thread as capture() - * pull_free_image_cb --> Capture backends call this callback to get empty image - * from the pool. If backend uses multiple threads, calls to this - * callback must be synchronized. Calls to this callback and - * push_captured_image_cb must be synchronized as well. - * bool *cursor --> A pointer to the flag that indicates whether the cursor should be captured as well + * push_captured_image_cb --> The callback that is called with captured + * image, must be called from the same thread as capture() + * pull_free_image_cb --> Capture backends call this callback to get empty + * image from the pool. If backend uses multiple threads, calls to this + * callback must be synchronized. Calls to this + * callback and push_captured_image_cb must be synchronized as well. bool + * *cursor --> A pointer to the flag that indicates whether the cursor + * should be captured as well * * Returns either: * capture_e::ok when stopping * capture_e::error on error * capture_e::reinit when need of reinitialization */ - virtual capture_e - capture(const push_captured_image_cb_t &push_captured_image_cb, const pull_free_image_cb_t &pull_free_image_cb, bool *cursor) = 0; + virtual capture_e capture( + const push_captured_image_cb_t &push_captured_image_cb, + const pull_free_image_cb_t &pull_free_image_cb, bool *cursor) = 0; - virtual std::shared_ptr - alloc_img() = 0; + virtual std::shared_ptr alloc_img() = 0; - virtual int - dummy_img(img_t *img) = 0; + virtual int dummy_img(img_t *img) = 0; - virtual std::unique_ptr - make_avcodec_encode_device(pix_fmt_e pix_fmt) { - return nullptr; + virtual std::unique_ptr make_avcodec_encode_device( + pix_fmt_e pix_fmt) { + return nullptr; } - virtual std::unique_ptr - make_nvenc_encode_device(pix_fmt_e pix_fmt) { - return nullptr; + virtual std::unique_ptr make_nvenc_encode_device( + pix_fmt_e pix_fmt) { + return nullptr; } - virtual bool - is_hdr() { - return false; - } + virtual bool is_hdr() { return false; } - virtual bool - get_hdr_metadata(SS_HDR_METADATA &metadata) { - std::memset(&metadata, 0, sizeof(metadata)); - return false; + virtual bool get_hdr_metadata(SS_HDR_METADATA &metadata) { + std::memset(&metadata, 0, sizeof(metadata)); + return false; } /** @@ -489,9 +446,9 @@ namespace platf { * @param config The codec configuration. * @return true if supported, false otherwise. */ - virtual bool - is_codec_supported(std::string_view name, const ::video::config_t &config) { - return true; + virtual bool is_codec_supported(std::string_view name, + const ::video::config_t &config) { + return true; } virtual ~display_t() = default; @@ -501,77 +458,59 @@ namespace platf { int env_width, env_height; int width, height; - }; +}; - class mic_t { - public: - virtual capture_e - sample(std::vector &frame_buffer) = 0; +class mic_t { + public: + virtual capture_e sample(std::vector &frame_buffer) = 0; virtual ~mic_t() = default; - }; +}; - class audio_control_t { - public: - virtual int - set_sink(const std::string &sink) = 0; +class audio_control_t { + public: + virtual int set_sink(const std::string &sink) = 0; - virtual std::unique_ptr - microphone(const std::uint8_t *mapping, int channels, std::uint32_t sample_rate, std::uint32_t frame_size) = 0; + virtual std::unique_ptr microphone(const std::uint8_t *mapping, + int channels, + std::uint32_t sample_rate, + std::uint32_t frame_size) = 0; - virtual std::optional - sink_info() = 0; + virtual std::optional sink_info() = 0; virtual ~audio_control_t() = default; - }; +}; +std::filesystem::path appdata(); +std::string from_sockaddr(const sockaddr *const); +std::pair from_sockaddr_ex(const sockaddr *const); +std::unique_ptr audio_control(); - std::filesystem::path - appdata(); +/** + * display_name --> The name of the monitor that SHOULD be displayed + * If display_name is empty --> Use the first monitor that's compatible you + * can find If you require to use this parameter in a separate thread --> make a + * copy of it. + * + * config --> Stream configuration + * + * Returns display_t based on hwdevice_type + */ +std::shared_ptr display(mem_type_e hwdevice_type, + const std::string &display_name, + const video::config_t &config); +// A list of names of displays accepted as display_name with the mem_type_e +std::vector display_names(mem_type_e hwdevice_type); +enum class thread_priority_e : int { low, normal, high, critical }; +void adjust_thread_priority(thread_priority_e priority); - std::string - from_sockaddr(const sockaddr *const); - std::pair - from_sockaddr_ex(const sockaddr *const); +enum class qos_data_type_e : int { audio, video }; - std::unique_ptr - audio_control(); +typedef deinit_t client_input_t; - /** - * display_name --> The name of the monitor that SHOULD be displayed - * If display_name is empty --> Use the first monitor that's compatible you can find - * If you require to use this parameter in a separate thread --> make a copy of it. - * - * config --> Stream configuration - * - * Returns display_t based on hwdevice_type - */ - std::shared_ptr - display(mem_type_e hwdevice_type, const std::string &display_name, const video::config_t &config); - - // A list of names of displays accepted as display_name with the mem_type_e - std::vector - display_names(mem_type_e hwdevice_type); - - enum class thread_priority_e : int { - low, - normal, - high, - critical - }; - void - adjust_thread_priority(thread_priority_e priority); - - enum class qos_data_type_e : int { - audio, - video - }; - - typedef deinit_t client_input_t; - - bool init(); +bool init(); } // namespace platf diff --git a/src/platform/linux/cuda.cpp b/src/platform/linux/cuda.cpp index 0a048936..e5e9352b 100644 --- a/src/platform/linux/cuda.cpp +++ b/src/platform/linux/cuda.cpp @@ -2,11 +2,11 @@ * @file src/platform/linux/cuda.cpp * @brief todo */ -#include - #include #include +#include + extern "C" { #include #include @@ -24,158 +24,152 @@ extern "C" { #define SUNSHINE_STRINGVIEW(x) SUNSHINE_STRINGVIEW_HELPER(x) #define CU_CHECK(x, y) \ - if (check((x), SUNSHINE_STRINGVIEW(y ": "))) return -1 + if (check((x), SUNSHINE_STRINGVIEW(y ": "))) return -1 -#define CU_CHECK_IGNORE(x, y) \ - check((x), SUNSHINE_STRINGVIEW(y ": ")) +#define CU_CHECK_IGNORE(x, y) check((x), SUNSHINE_STRINGVIEW(y ": ")) using namespace std::literals; namespace cuda { - constexpr auto cudaDevAttrMaxThreadsPerBlock = (CUdevice_attribute) 1; - constexpr auto cudaDevAttrMaxThreadsPerMultiProcessor = (CUdevice_attribute) 39; +constexpr auto cudaDevAttrMaxThreadsPerBlock = (CUdevice_attribute)1; +constexpr auto cudaDevAttrMaxThreadsPerMultiProcessor = (CUdevice_attribute)39; - void - pass_error(const std::string_view &sv, const char *name, const char *description) { +void pass_error(const std::string_view &sv, const char *name, + const char *description) { BOOST_LOG(error) << sv << name << ':' << description; - } +} - void - cff(CudaFunctions *cf) { - cuda_free_functions(&cf); - } +void cff(CudaFunctions *cf) { cuda_free_functions(&cf); } - using cdf_t = util::safe_ptr; +using cdf_t = util::safe_ptr; - static cdf_t cdf; +static cdf_t cdf; - inline static int - check(CUresult result, const std::string_view &sv) { +inline static int check(CUresult result, const std::string_view &sv) { if (result != CUDA_SUCCESS) { - const char *name; - const char *description; + const char *name; + const char *description; - cdf->cuGetErrorName(result, &name); - cdf->cuGetErrorString(result, &description); + cdf->cuGetErrorName(result, &name); + cdf->cuGetErrorString(result, &description); - BOOST_LOG(error) << sv << name << ':' << description; - return -1; + BOOST_LOG(error) << sv << name << ':' << description; + return -1; } return 0; - } +} - void - freeStream(CUstream stream) { - CU_CHECK_IGNORE(cdf->cuStreamDestroy(stream), "Couldn't destroy cuda stream"); - } +void freeStream(CUstream stream) { + CU_CHECK_IGNORE(cdf->cuStreamDestroy(stream), + "Couldn't destroy cuda stream"); +} - class img_t: public platf::img_t { - public: +class img_t : public platf::img_t { + public: tex_t tex; - }; +}; - int - init() { +int init() { auto status = cuda_load_functions(&cdf, nullptr); if (status) { - BOOST_LOG(error) << "Couldn't load cuda: "sv << status; + BOOST_LOG(error) << "Couldn't load cuda: "sv << status; - return -1; + return -1; } CU_CHECK(cdf->cuInit(0), "Couldn't initialize cuda"); return 0; - } +} - class cuda_t: public platf::avcodec_encode_device_t { - public: - int - init(int in_width, int in_height) { - if (!cdf) { - BOOST_LOG(warning) << "cuda not initialized"sv; - return -1; - } - - data = (void *) 0x1; - - width = in_width; - height = in_height; - - return 0; - } - - int - set_frame(AVFrame *frame, AVBufferRef *hw_frames_ctx) override { - this->hwframe.reset(frame); - this->frame = frame; - - auto hwframe_ctx = (AVHWFramesContext *) hw_frames_ctx->data; - if (hwframe_ctx->sw_format != AV_PIX_FMT_NV12) { - BOOST_LOG(error) << "cuda::cuda_t doesn't support any format other than AV_PIX_FMT_NV12"sv; - return -1; - } - - if (!frame->buf[0]) { - if (av_hwframe_get_buffer(hw_frames_ctx, frame, 0)) { - BOOST_LOG(error) << "Couldn't get hwframe for NVENC"sv; - return -1; +class cuda_t : public platf::avcodec_encode_device_t { + public: + int init(int in_width, int in_height) { + if (!cdf) { + BOOST_LOG(warning) << "cuda not initialized"sv; + return -1; } - } - auto cuda_ctx = (AVCUDADeviceContext *) hwframe_ctx->device_ctx->hwctx; + data = (void *)0x1; - stream = make_stream(); - if (!stream) { - return -1; - } + width = in_width; + height = in_height; - cuda_ctx->stream = stream.get(); - - auto sws_opt = sws_t::make(width, height, frame->width, frame->height, width * 4); - if (!sws_opt) { - return -1; - } - - sws = std::move(*sws_opt); - - linear_interpolation = width != frame->width || height != frame->height; - - return 0; + return 0; } - void - apply_colorspace() override { - sws.apply_colorspace(colorspace); + int set_frame(AVFrame *frame, AVBufferRef *hw_frames_ctx) override { + this->hwframe.reset(frame); + this->frame = frame; - auto tex = tex_t::make(height, width * 4); - if (!tex) { - return; - } + auto hwframe_ctx = (AVHWFramesContext *)hw_frames_ctx->data; + if (hwframe_ctx->sw_format != AV_PIX_FMT_NV12) { + BOOST_LOG(error) + << "cuda::cuda_t doesn't support any format other than AV_PIX_FMT_NV12"sv; + return -1; + } - // The default green color is ugly. - // Update the background color - platf::img_t img; - img.width = width; - img.height = height; - img.pixel_pitch = 4; - img.row_pitch = img.width * img.pixel_pitch; + if (!frame->buf[0]) { + if (av_hwframe_get_buffer(hw_frames_ctx, frame, 0)) { + BOOST_LOG(error) << "Couldn't get hwframe for NVENC"sv; + return -1; + } + } - std::vector image_data; - image_data.resize(img.row_pitch * img.height); + auto cuda_ctx = (AVCUDADeviceContext *)hwframe_ctx->device_ctx->hwctx; - img.data = image_data.data(); + stream = make_stream(); + if (!stream) { + return -1; + } - if (sws.load_ram(img, tex->array)) { - return; - } + cuda_ctx->stream = stream.get(); - sws.convert(frame->data[0], frame->data[1], frame->linesize[0], frame->linesize[1], tex->texture.linear, stream.get(), { frame->width, frame->height, 0, 0 }); + auto sws_opt = + sws_t::make(width, height, frame->width, frame->height, width * 4); + if (!sws_opt) { + return -1; + } + + sws = std::move(*sws_opt); + + linear_interpolation = width != frame->width || height != frame->height; + + return 0; } - cudaTextureObject_t - tex_obj(const tex_t &tex) const { - return linear_interpolation ? tex.texture.linear : tex.texture.point; + void apply_colorspace() override { + sws.apply_colorspace(colorspace); + + auto tex = tex_t::make(height, width * 4); + if (!tex) { + return; + } + + // The default green color is ugly. + // Update the background color + platf::img_t img; + img.width = width; + img.height = height; + img.pixel_pitch = 4; + img.row_pitch = img.width * img.pixel_pitch; + + std::vector image_data; + image_data.resize(img.row_pitch * img.height); + + img.data = image_data.data(); + + if (sws.load_ram(img, tex->array)) { + return; + } + + sws.convert(frame->data[0], frame->data[1], frame->linesize[0], + frame->linesize[1], tex->texture.linear, stream.get(), + {frame->width, frame->height, 0, 0}); + } + + cudaTextureObject_t tex_obj(const tex_t &tex) const { + return linear_interpolation ? tex.texture.linear : tex.texture.point; } stream_t stream; @@ -183,299 +177,303 @@ namespace cuda { int width, height; - // When height and width don't change, it's not necessary to use linear interpolation + // When height and width don't change, it's not necessary to use linear + // interpolation bool linear_interpolation; sws_t sws; - }; +}; - class cuda_ram_t: public cuda_t { - public: - int - convert(platf::img_t &img) override { - return sws.load_ram(img, tex.array) || sws.convert(frame->data[0], frame->data[1], frame->linesize[0], frame->linesize[1], tex_obj(tex), stream.get()); +class cuda_ram_t : public cuda_t { + public: + int convert(platf::img_t &img) override { + return sws.load_ram(img, tex.array) || + sws.convert(frame->data[0], frame->data[1], frame->linesize[0], + frame->linesize[1], tex_obj(tex), stream.get()); } - int - set_frame(AVFrame *frame, AVBufferRef *hw_frames_ctx) { - if (cuda_t::set_frame(frame, hw_frames_ctx)) { - return -1; - } + int set_frame(AVFrame *frame, AVBufferRef *hw_frames_ctx) { + if (cuda_t::set_frame(frame, hw_frames_ctx)) { + return -1; + } - auto tex_opt = tex_t::make(height, width * 4); - if (!tex_opt) { - return -1; - } + auto tex_opt = tex_t::make(height, width * 4); + if (!tex_opt) { + return -1; + } - tex = std::move(*tex_opt); + tex = std::move(*tex_opt); - return 0; + return 0; } tex_t tex; - }; +}; - class cuda_vram_t: public cuda_t { - public: - int - convert(platf::img_t &img) override { - return sws.convert(frame->data[0], frame->data[1], frame->linesize[0], frame->linesize[1], tex_obj(((img_t *) &img)->tex), stream.get()); +class cuda_vram_t : public cuda_t { + public: + int convert(platf::img_t &img) override { + return sws.convert(frame->data[0], frame->data[1], frame->linesize[0], + frame->linesize[1], tex_obj(((img_t *)&img)->tex), + stream.get()); } - }; +}; - std::unique_ptr - make_avcodec_encode_device(int width, int height, bool vram) { +std::unique_ptr make_avcodec_encode_device( + int width, int height, bool vram) { if (init()) { - return nullptr; + return nullptr; } std::unique_ptr cuda; if (vram) { - cuda = std::make_unique(); - } - else { - cuda = std::make_unique(); + cuda = std::make_unique(); + } else { + cuda = std::make_unique(); } if (cuda->init(width, height)) { - return nullptr; + return nullptr; } return cuda; - } +} - namespace nvfbc { - static PNVFBCCREATEINSTANCE createInstance {}; - static NVFBC_API_FUNCTION_LIST func { NVFBC_VERSION }; +namespace nvfbc { +static PNVFBCCREATEINSTANCE createInstance{}; +static NVFBC_API_FUNCTION_LIST func{NVFBC_VERSION}; - static constexpr inline NVFBC_BOOL - nv_bool(bool b) { - return b ? NVFBC_TRUE : NVFBC_FALSE; +static constexpr inline NVFBC_BOOL nv_bool(bool b) { + return b ? NVFBC_TRUE : NVFBC_FALSE; +} + +static void *handle{nullptr}; +int init() { + static bool funcs_loaded = false; + + if (funcs_loaded) return 0; + + if (!handle) { + handle = dyn::handle({"libnvidia-fbc.so.1", "libnvidia-fbc.so"}); + if (!handle) { + return -1; + } } - static void *handle { nullptr }; - int - init() { - static bool funcs_loaded = false; + std::vector> funcs{ + {(dyn::apiproc *)&createInstance, "NvFBCCreateInstance"}, + }; - if (funcs_loaded) return 0; - - if (!handle) { - handle = dyn::handle({ "libnvidia-fbc.so.1", "libnvidia-fbc.so" }); - if (!handle) { - return -1; - } - } - - std::vector> funcs { - { (dyn::apiproc *) &createInstance, "NvFBCCreateInstance" }, - }; - - if (dyn::load(handle, funcs)) { + if (dyn::load(handle, funcs)) { dlclose(handle); handle = nullptr; return -1; - } + } - auto status = cuda::nvfbc::createInstance(&cuda::nvfbc::func); - if (status) { + auto status = cuda::nvfbc::createInstance(&cuda::nvfbc::func); + if (status) { BOOST_LOG(error) << "Unable to create NvFBC instance"sv; dlclose(handle); handle = nullptr; return -1; - } - - funcs_loaded = true; - return 0; } - class ctx_t { - public: - ctx_t(NVFBC_SESSION_HANDLE handle) { - NVFBC_BIND_CONTEXT_PARAMS params { NVFBC_BIND_CONTEXT_PARAMS_VER }; + funcs_loaded = true; + return 0; +} + +class ctx_t { + public: + ctx_t(NVFBC_SESSION_HANDLE handle) { + NVFBC_BIND_CONTEXT_PARAMS params{NVFBC_BIND_CONTEXT_PARAMS_VER}; if (func.nvFBCBindContext(handle, ¶ms)) { - BOOST_LOG(error) << "Couldn't bind NvFBC context to current thread: " << func.nvFBCGetLastErrorStr(handle); + BOOST_LOG(error) + << "Couldn't bind NvFBC context to current thread: " + << func.nvFBCGetLastErrorStr(handle); } this->handle = handle; - } + } - ~ctx_t() { - NVFBC_RELEASE_CONTEXT_PARAMS params { NVFBC_RELEASE_CONTEXT_PARAMS_VER }; + ~ctx_t() { + NVFBC_RELEASE_CONTEXT_PARAMS params{NVFBC_RELEASE_CONTEXT_PARAMS_VER}; if (func.nvFBCReleaseContext(handle, ¶ms)) { - BOOST_LOG(error) << "Couldn't release NvFBC context from current thread: " << func.nvFBCGetLastErrorStr(handle); + BOOST_LOG(error) + << "Couldn't release NvFBC context from current thread: " + << func.nvFBCGetLastErrorStr(handle); } - } + } - NVFBC_SESSION_HANDLE handle; - }; + NVFBC_SESSION_HANDLE handle; +}; - class handle_t { - enum flag_e { +class handle_t { + enum flag_e { SESSION_HANDLE, SESSION_CAPTURE, MAX_FLAGS, - }; + }; - public: - handle_t() = default; - handle_t(handle_t &&other): - handle_flags { other.handle_flags }, handle { other.handle } { + public: + handle_t() = default; + handle_t(handle_t &&other) + : handle_flags{other.handle_flags}, handle{other.handle} { other.handle_flags.reset(); - } + } - handle_t & - operator=(handle_t &&other) { + handle_t &operator=(handle_t &&other) { std::swap(handle_flags, other.handle_flags); std::swap(handle, other.handle); return *this; - } + } - static std::optional - make() { - NVFBC_CREATE_HANDLE_PARAMS params { NVFBC_CREATE_HANDLE_PARAMS_VER }; + static std::optional make() { + NVFBC_CREATE_HANDLE_PARAMS params{NVFBC_CREATE_HANDLE_PARAMS_VER}; handle_t handle; auto status = func.nvFBCCreateHandle(&handle.handle, ¶ms); if (status) { - BOOST_LOG(error) << "Failed to create session: "sv << handle.last_error(); + BOOST_LOG(error) + << "Failed to create session: "sv << handle.last_error(); - return std::nullopt; + return std::nullopt; } handle.handle_flags[SESSION_HANDLE] = true; return handle; - } + } - const char * - last_error() { - return func.nvFBCGetLastErrorStr(handle); - } + const char *last_error() { return func.nvFBCGetLastErrorStr(handle); } - std::optional - status() { - NVFBC_GET_STATUS_PARAMS params { NVFBC_GET_STATUS_PARAMS_VER }; + std::optional status() { + NVFBC_GET_STATUS_PARAMS params{NVFBC_GET_STATUS_PARAMS_VER}; auto status = func.nvFBCGetStatus(handle, ¶ms); if (status) { - BOOST_LOG(error) << "Failed to get NvFBC status: "sv << last_error(); + BOOST_LOG(error) + << "Failed to get NvFBC status: "sv << last_error(); - return std::nullopt; + return std::nullopt; } return params; - } + } - int - capture(NVFBC_CREATE_CAPTURE_SESSION_PARAMS &capture_params) { + int capture(NVFBC_CREATE_CAPTURE_SESSION_PARAMS &capture_params) { if (func.nvFBCCreateCaptureSession(handle, &capture_params)) { - BOOST_LOG(error) << "Failed to start capture session: "sv << last_error(); - return -1; + BOOST_LOG(error) + << "Failed to start capture session: "sv << last_error(); + return -1; } handle_flags[SESSION_CAPTURE] = true; - NVFBC_TOCUDA_SETUP_PARAMS setup_params { - NVFBC_TOCUDA_SETUP_PARAMS_VER, - NVFBC_BUFFER_FORMAT_BGRA, + NVFBC_TOCUDA_SETUP_PARAMS setup_params{ + NVFBC_TOCUDA_SETUP_PARAMS_VER, + NVFBC_BUFFER_FORMAT_BGRA, }; if (func.nvFBCToCudaSetUp(handle, &setup_params)) { - BOOST_LOG(error) << "Failed to setup cuda interop with nvFBC: "sv << last_error(); - return -1; + BOOST_LOG(error) << "Failed to setup cuda interop with nvFBC: "sv + << last_error(); + return -1; } return 0; - } + } - int - stop() { + int stop() { if (!handle_flags[SESSION_CAPTURE]) { - return 0; + return 0; } - NVFBC_DESTROY_CAPTURE_SESSION_PARAMS params { NVFBC_DESTROY_CAPTURE_SESSION_PARAMS_VER }; + NVFBC_DESTROY_CAPTURE_SESSION_PARAMS params{ + NVFBC_DESTROY_CAPTURE_SESSION_PARAMS_VER}; if (func.nvFBCDestroyCaptureSession(handle, ¶ms)) { - BOOST_LOG(error) << "Couldn't destroy capture session: "sv << last_error(); + BOOST_LOG(error) + << "Couldn't destroy capture session: "sv << last_error(); - return -1; + return -1; } handle_flags[SESSION_CAPTURE] = false; return 0; - } + } - int - reset() { + int reset() { if (!handle_flags[SESSION_HANDLE]) { - return 0; + return 0; } stop(); - NVFBC_DESTROY_HANDLE_PARAMS params { NVFBC_DESTROY_HANDLE_PARAMS_VER }; + NVFBC_DESTROY_HANDLE_PARAMS params{NVFBC_DESTROY_HANDLE_PARAMS_VER}; if (func.nvFBCDestroyHandle(handle, ¶ms)) { - BOOST_LOG(error) << "Couldn't destroy session handle: "sv << func.nvFBCGetLastErrorStr(handle); + BOOST_LOG(error) << "Couldn't destroy session handle: "sv + << func.nvFBCGetLastErrorStr(handle); } handle_flags[SESSION_HANDLE] = false; return 0; - } + } - ~handle_t() { - reset(); - } + ~handle_t() { reset(); } - std::bitset handle_flags; + std::bitset handle_flags; - NVFBC_SESSION_HANDLE handle; - }; + NVFBC_SESSION_HANDLE handle; +}; - class display_t: public platf::display_t { - public: - int - init(const std::string_view &display_name, const ::video::config_t &config) { +class display_t : public platf::display_t { + public: + int init(const std::string_view &display_name, + const ::video::config_t &config) { auto handle = handle_t::make(); if (!handle) { - return -1; + return -1; } - ctx_t ctx { handle->handle }; + ctx_t ctx{handle->handle}; auto status_params = handle->status(); if (!status_params) { - return -1; + return -1; } int streamedMonitor = -1; if (!display_name.empty()) { - if (status_params->bXRandRAvailable) { - auto monitor_nr = util::from_view(display_name); + if (status_params->bXRandRAvailable) { + auto monitor_nr = util::from_view(display_name); - if (monitor_nr < 0 || monitor_nr >= status_params->dwOutputNum) { - BOOST_LOG(warning) << "Can't stream monitor ["sv << monitor_nr << "], it needs to be between [0] and ["sv << status_params->dwOutputNum - 1 << "], defaulting to virtual desktop"sv; + if (monitor_nr < 0 || + monitor_nr >= status_params->dwOutputNum) { + BOOST_LOG(warning) + << "Can't stream monitor ["sv << monitor_nr + << "], it needs to be between [0] and ["sv + << status_params->dwOutputNum - 1 + << "], defaulting to virtual desktop"sv; + } else { + streamedMonitor = monitor_nr; + } + } else { + BOOST_LOG(warning) + << "XrandR not available, streaming entire virtual desktop"sv; } - else { - streamedMonitor = monitor_nr; - } - } - else { - BOOST_LOG(warning) << "XrandR not available, streaming entire virtual desktop"sv; - } } - delay = std::chrono::nanoseconds { 1s } / config.framerate; + delay = std::chrono::nanoseconds{1s} / config.framerate; - capture_params = NVFBC_CREATE_CAPTURE_SESSION_PARAMS { NVFBC_CREATE_CAPTURE_SESSION_PARAMS_VER }; + capture_params = NVFBC_CREATE_CAPTURE_SESSION_PARAMS{ + NVFBC_CREATE_CAPTURE_SESSION_PARAMS_VER}; capture_params.eCaptureType = NVFBC_CAPTURE_SHARED_CUDA; capture_params.bDisableAutoModesetRecovery = nv_bool(true); @@ -483,21 +481,20 @@ namespace cuda { capture_params.dwSamplingRateMs = 1000 /* ms */ / config.framerate; if (streamedMonitor != -1) { - auto &output = status_params->outputs[streamedMonitor]; + auto &output = status_params->outputs[streamedMonitor]; - width = output.trackedBox.w; - height = output.trackedBox.h; - offset_x = output.trackedBox.x; - offset_y = output.trackedBox.y; + width = output.trackedBox.w; + height = output.trackedBox.h; + offset_x = output.trackedBox.x; + offset_y = output.trackedBox.y; - capture_params.eTrackingType = NVFBC_TRACKING_OUTPUT; - capture_params.dwOutputId = output.dwId; - } - else { - capture_params.eTrackingType = NVFBC_TRACKING_SCREEN; + capture_params.eTrackingType = NVFBC_TRACKING_OUTPUT; + capture_params.dwOutputId = output.dwId; + } else { + capture_params.eTrackingType = NVFBC_TRACKING_SCREEN; - width = status_params->screenSize.w; - height = status_params->screenSize.h; + width = status_params->screenSize.w; + height = status_params->screenSize.h; } env_width = status_params->screenSize.w; @@ -505,183 +502,189 @@ namespace cuda { this->handle = std::move(*handle); return 0; - } + } - platf::capture_e - capture(const push_captured_image_cb_t &push_captured_image_cb, const pull_free_image_cb_t &pull_free_image_cb, bool *cursor) override { + platf::capture_e capture( + const push_captured_image_cb_t &push_captured_image_cb, + const pull_free_image_cb_t &pull_free_image_cb, bool *cursor) override { auto next_frame = std::chrono::steady_clock::now(); { - // We must create at least one texture on this thread before calling NvFBCToCudaSetUp() - // Otherwise it fails with "Unable to register an OpenGL buffer to a CUDA resource (result: 201)" message - std::shared_ptr img_dummy; - pull_free_image_cb(img_dummy); + // We must create at least one texture on this thread before calling + // NvFBCToCudaSetUp() Otherwise it fails with "Unable to register an + // OpenGL buffer to a CUDA resource (result: 201)" message + std::shared_ptr img_dummy; + pull_free_image_cb(img_dummy); } // Force display_t::capture to initialize handle_t::capture cursor_visible = !*cursor; - ctx_t ctx { handle.handle }; - auto fg = util::fail_guard([&]() { - handle.reset(); - }); + ctx_t ctx{handle.handle}; + auto fg = util::fail_guard([&]() { handle.reset(); }); while (true) { - auto now = std::chrono::steady_clock::now(); - if (next_frame > now) { - std::this_thread::sleep_for((next_frame - now) / 3 * 2); - } - while (next_frame > now) { - std::this_thread::sleep_for(1ns); - now = std::chrono::steady_clock::now(); - } - next_frame = now + delay; + auto now = std::chrono::steady_clock::now(); + if (next_frame > now) { + std::this_thread::sleep_for((next_frame - now) / 3 * 2); + } + while (next_frame > now) { + std::this_thread::sleep_for(1ns); + now = std::chrono::steady_clock::now(); + } + next_frame = now + delay; - std::shared_ptr img_out; - auto status = snapshot(pull_free_image_cb, img_out, 150ms, *cursor); - switch (status) { - case platf::capture_e::reinit: - case platf::capture_e::error: - case platf::capture_e::interrupted: - return status; - case platf::capture_e::timeout: - if (!push_captured_image_cb(std::move(img_out), false)) { - return platf::capture_e::ok; - } - break; - case platf::capture_e::ok: - if (!push_captured_image_cb(std::move(img_out), true)) { - return platf::capture_e::ok; - } - break; - default: - BOOST_LOG(error) << "Unrecognized capture status ["sv << (int) status << ']'; - return status; - } + std::shared_ptr img_out; + auto status = snapshot(pull_free_image_cb, img_out, 150ms, *cursor); + switch (status) { + case platf::capture_e::reinit: + case platf::capture_e::error: + case platf::capture_e::interrupted: + return status; + case platf::capture_e::timeout: + if (!push_captured_image_cb(std::move(img_out), false)) { + return platf::capture_e::ok; + } + break; + case platf::capture_e::ok: + if (!push_captured_image_cb(std::move(img_out), true)) { + return platf::capture_e::ok; + } + break; + default: + BOOST_LOG(error) << "Unrecognized capture status ["sv + << (int)status << ']'; + return status; + } } return platf::capture_e::ok; - } + } - // Reinitialize the capture session. - platf::capture_e - reinit(bool cursor) { + // Reinitialize the capture session. + platf::capture_e reinit(bool cursor) { if (handle.stop()) { - return platf::capture_e::error; + return platf::capture_e::error; } cursor_visible = cursor; if (cursor) { - capture_params.bPushModel = nv_bool(false); - capture_params.bWithCursor = nv_bool(true); - capture_params.bAllowDirectCapture = nv_bool(false); - } - else { - capture_params.bPushModel = nv_bool(true); - capture_params.bWithCursor = nv_bool(false); - capture_params.bAllowDirectCapture = nv_bool(true); + capture_params.bPushModel = nv_bool(false); + capture_params.bWithCursor = nv_bool(true); + capture_params.bAllowDirectCapture = nv_bool(false); + } else { + capture_params.bPushModel = nv_bool(true); + capture_params.bWithCursor = nv_bool(false); + capture_params.bAllowDirectCapture = nv_bool(true); } if (handle.capture(capture_params)) { - return platf::capture_e::error; + return platf::capture_e::error; } // If trying to capture directly, test if it actually does. if (capture_params.bAllowDirectCapture) { - CUdeviceptr device_ptr; - NVFBC_FRAME_GRAB_INFO info; + CUdeviceptr device_ptr; + NVFBC_FRAME_GRAB_INFO info; - NVFBC_TOCUDA_GRAB_FRAME_PARAMS grab { - NVFBC_TOCUDA_GRAB_FRAME_PARAMS_VER, - NVFBC_TOCUDA_GRAB_FLAGS_NOWAIT, - &device_ptr, - &info, - 0, - }; + NVFBC_TOCUDA_GRAB_FRAME_PARAMS grab{ + NVFBC_TOCUDA_GRAB_FRAME_PARAMS_VER, + NVFBC_TOCUDA_GRAB_FLAGS_NOWAIT, + &device_ptr, + &info, + 0, + }; - // Direct Capture may fail the first few times, even if it's possible - for (int x = 0; x < 3; ++x) { - if (auto status = func.nvFBCToCudaGrabFrame(handle.handle, &grab)) { - if (status == NVFBC_ERR_MUST_RECREATE) { - return platf::capture_e::reinit; - } + // Direct Capture may fail the first few times, even if it's + // possible + for (int x = 0; x < 3; ++x) { + if (auto status = + func.nvFBCToCudaGrabFrame(handle.handle, &grab)) { + if (status == NVFBC_ERR_MUST_RECREATE) { + return platf::capture_e::reinit; + } - BOOST_LOG(error) << "Couldn't capture nvFramebuffer: "sv << handle.last_error(); + BOOST_LOG(error) << "Couldn't capture nvFramebuffer: "sv + << handle.last_error(); - return platf::capture_e::error; + return platf::capture_e::error; + } + + if (info.bDirectCapture) { + break; + } + + BOOST_LOG(debug) + << "Direct capture failed attempt ["sv << x << ']'; } - if (info.bDirectCapture) { - break; + if (!info.bDirectCapture) { + BOOST_LOG(debug) + << "Direct capture failed, trying the extra copy method"sv; + // Direct capture failed + capture_params.bPushModel = nv_bool(false); + capture_params.bWithCursor = nv_bool(false); + capture_params.bAllowDirectCapture = nv_bool(false); + + if (handle.stop() || handle.capture(capture_params)) { + return platf::capture_e::error; + } } - - BOOST_LOG(debug) << "Direct capture failed attempt ["sv << x << ']'; - } - - if (!info.bDirectCapture) { - BOOST_LOG(debug) << "Direct capture failed, trying the extra copy method"sv; - // Direct capture failed - capture_params.bPushModel = nv_bool(false); - capture_params.bWithCursor = nv_bool(false); - capture_params.bAllowDirectCapture = nv_bool(false); - - if (handle.stop() || handle.capture(capture_params)) { - return platf::capture_e::error; - } - } } return platf::capture_e::ok; - } + } - platf::capture_e - snapshot(const pull_free_image_cb_t &pull_free_image_cb, std::shared_ptr &img_out, std::chrono::milliseconds timeout, bool cursor) { + platf::capture_e snapshot(const pull_free_image_cb_t &pull_free_image_cb, + std::shared_ptr &img_out, + std::chrono::milliseconds timeout, bool cursor) { if (cursor != cursor_visible) { - auto status = reinit(cursor); - if (status != platf::capture_e::ok) { - return status; - } + auto status = reinit(cursor); + if (status != platf::capture_e::ok) { + return status; + } } CUdeviceptr device_ptr; NVFBC_FRAME_GRAB_INFO info; - NVFBC_TOCUDA_GRAB_FRAME_PARAMS grab { - NVFBC_TOCUDA_GRAB_FRAME_PARAMS_VER, - NVFBC_TOCUDA_GRAB_FLAGS_NOWAIT, - &device_ptr, - &info, - (std::uint32_t) timeout.count(), + NVFBC_TOCUDA_GRAB_FRAME_PARAMS grab{ + NVFBC_TOCUDA_GRAB_FRAME_PARAMS_VER, + NVFBC_TOCUDA_GRAB_FLAGS_NOWAIT, + &device_ptr, + &info, + (std::uint32_t)timeout.count(), }; if (auto status = func.nvFBCToCudaGrabFrame(handle.handle, &grab)) { - if (status == NVFBC_ERR_MUST_RECREATE) { - return platf::capture_e::reinit; - } + if (status == NVFBC_ERR_MUST_RECREATE) { + return platf::capture_e::reinit; + } - BOOST_LOG(error) << "Couldn't capture nvFramebuffer: "sv << handle.last_error(); - return platf::capture_e::error; + BOOST_LOG(error) + << "Couldn't capture nvFramebuffer: "sv << handle.last_error(); + return platf::capture_e::error; } if (!pull_free_image_cb(img_out)) { - return platf::capture_e::interrupted; + return platf::capture_e::interrupted; } - auto img = (img_t *) img_out.get(); + auto img = (img_t *)img_out.get(); - if (img->tex.copy((std::uint8_t *) device_ptr, img->height, img->row_pitch)) { - return platf::capture_e::error; + if (img->tex.copy((std::uint8_t *)device_ptr, img->height, + img->row_pitch)) { + return platf::capture_e::error; } return platf::capture_e::ok; - } + } - std::unique_ptr - make_avcodec_encode_device(platf::pix_fmt_e pix_fmt) { + std::unique_ptr make_avcodec_encode_device( + platf::pix_fmt_e pix_fmt) { return ::cuda::make_avcodec_encode_device(width, height, true); - } + } - std::shared_ptr - alloc_img() override { + std::shared_ptr alloc_img() override { auto img = std::make_shared(); img->data = nullptr; @@ -692,82 +695,87 @@ namespace cuda { auto tex_opt = tex_t::make(height, width * img->pixel_pitch); if (!tex_opt) { - return nullptr; + return nullptr; } img->tex = std::move(*tex_opt); return img; - }; - - int - dummy_img(platf::img_t *) override { - return 0; - } - - std::chrono::nanoseconds delay; - - bool cursor_visible; - handle_t handle; - - NVFBC_CREATE_CAPTURE_SESSION_PARAMS capture_params; }; - } // namespace nvfbc + + int dummy_img(platf::img_t *) override { return 0; } + + std::chrono::nanoseconds delay; + + bool cursor_visible; + handle_t handle; + + NVFBC_CREATE_CAPTURE_SESSION_PARAMS capture_params; +}; +} // namespace nvfbc } // namespace cuda namespace platf { - std::shared_ptr - nvfbc_display(mem_type_e hwdevice_type, const std::string &display_name, const video::config_t &config) { +std::shared_ptr nvfbc_display(mem_type_e hwdevice_type, + const std::string &display_name, + const video::config_t &config) { if (hwdevice_type != mem_type_e::cuda) { - BOOST_LOG(error) << "Could not initialize nvfbc display with the given hw device type"sv; - return nullptr; + BOOST_LOG(error) + << "Could not initialize nvfbc display with the given hw device type"sv; + return nullptr; } auto display = std::make_shared(); if (display->init(display_name, config)) { - return nullptr; + return nullptr; } return display; - } +} - std::vector - nvfbc_display_names() { +std::vector nvfbc_display_names() { if (cuda::init() || cuda::nvfbc::init()) { - return {}; + return {}; } std::vector display_names; auto handle = cuda::nvfbc::handle_t::make(); if (!handle) { - return {}; + return {}; } auto status_params = handle->status(); if (!status_params) { - return {}; + return {}; } if (!status_params->bIsCapturePossible) { - BOOST_LOG(error) << "NVidia driver doesn't support NvFBC screencasting"sv; + BOOST_LOG(error) + << "NVidia driver doesn't support NvFBC screencasting"sv; } - BOOST_LOG(info) << "Found ["sv << status_params->dwOutputNum << "] outputs"sv; - BOOST_LOG(info) << "Virtual Desktop: "sv << status_params->screenSize.w << 'x' << status_params->screenSize.h; - BOOST_LOG(info) << "XrandR: "sv << (status_params->bXRandRAvailable ? "available"sv : "unavailable"sv); + BOOST_LOG(info) << "Found ["sv << status_params->dwOutputNum + << "] outputs"sv; + BOOST_LOG(info) << "Virtual Desktop: "sv << status_params->screenSize.w + << 'x' << status_params->screenSize.h; + BOOST_LOG(info) << "XrandR: "sv + << (status_params->bXRandRAvailable ? "available"sv + : "unavailable"sv); for (auto x = 0; x < status_params->dwOutputNum; ++x) { - auto &output = status_params->outputs[x]; - BOOST_LOG(info) << "-- Output --"sv; - BOOST_LOG(debug) << " ID: "sv << output.dwId; - BOOST_LOG(debug) << " Name: "sv << output.name; - BOOST_LOG(info) << " Resolution: "sv << output.trackedBox.w << 'x' << output.trackedBox.h; - BOOST_LOG(info) << " Offset: "sv << output.trackedBox.x << 'x' << output.trackedBox.y; - display_names.emplace_back(std::to_string(x)); + auto &output = status_params->outputs[x]; + BOOST_LOG(info) << "-- Output --"sv; + BOOST_LOG(debug) << " ID: "sv << output.dwId; + BOOST_LOG(debug) << " Name: "sv << output.name; + BOOST_LOG(info) << " Resolution: "sv << output.trackedBox.w << 'x' + << output.trackedBox.h; + BOOST_LOG(info) << " Offset: "sv << output.trackedBox.x << 'x' + << output.trackedBox.y; + display_names.emplace_back(std::to_string(x)); } return display_names; - } +} } // namespace platf \ No newline at end of file diff --git a/src/platform/linux/cuda.h b/src/platform/linux/cuda.h index d5b97d65..2273b647 100644 --- a/src/platform/linux/cuda.h +++ b/src/platform/linux/cuda.h @@ -6,115 +6,109 @@ #if defined(SUNSHINE_BUILD_CUDA) - #include "src/video_colorspace.h" +#include +#include +#include +#include +#include - #include - #include - #include - #include - #include +#include "src/video_colorspace.h" namespace platf { - class avcodec_encode_device_t; - class img_t; +class avcodec_encode_device_t; +class img_t; } // namespace platf namespace cuda { - namespace nvfbc { - std::vector - display_names(); - } - std::unique_ptr - make_avcodec_encode_device(int width, int height, bool vram); - int - init(); +namespace nvfbc { +std::vector display_names(); +} +std::unique_ptr make_avcodec_encode_device( + int width, int height, bool vram); +int init(); } // namespace cuda typedef struct cudaArray *cudaArray_t; - #if !defined(__CUDACC__) +#if !defined(__CUDACC__) typedef struct CUstream_st *cudaStream_t; typedef unsigned long long cudaTextureObject_t; - #else /* defined(__CUDACC__) */ +#else /* defined(__CUDACC__) */ typedef __location__(device_builtin) struct CUstream_st *cudaStream_t; typedef __location__(device_builtin) unsigned long long cudaTextureObject_t; - #endif /* !defined(__CUDACC__) */ +#endif /* !defined(__CUDACC__) */ namespace cuda { - class freeCudaPtr_t { - public: - void - operator()(void *ptr); - }; +class freeCudaPtr_t { + public: + void operator()(void *ptr); +}; - class freeCudaStream_t { - public: - void - operator()(cudaStream_t ptr); - }; +class freeCudaStream_t { + public: + void operator()(cudaStream_t ptr); +}; - using ptr_t = std::unique_ptr; - using stream_t = std::unique_ptr; +using ptr_t = std::unique_ptr; +using stream_t = std::unique_ptr; - stream_t - make_stream(int flags = 0); +stream_t make_stream(int flags = 0); - struct viewport_t { +struct viewport_t { int width, height; int offsetX, offsetY; - }; +}; - class tex_t { - public: - static std::optional - make(int height, int pitch); +class tex_t { + public: + static std::optional make(int height, int pitch); tex_t(); tex_t(tex_t &&); - tex_t & - operator=(tex_t &&other); + tex_t &operator=(tex_t &&other); ~tex_t(); - int - copy(std::uint8_t *src, int height, int pitch); + int copy(std::uint8_t *src, int height, int pitch); cudaArray_t array; struct texture { - cudaTextureObject_t point; - cudaTextureObject_t linear; + cudaTextureObject_t point; + cudaTextureObject_t linear; } texture; - }; +}; - class sws_t { - public: +class sws_t { + public: sws_t() = default; - sws_t(int in_width, int in_height, int out_width, int out_height, int pitch, int threadsPerBlock, ptr_t &&color_matrix); + sws_t(int in_width, int in_height, int out_width, int out_height, int pitch, + int threadsPerBlock, ptr_t &&color_matrix); /** - * in_width, in_height -- The width and height of the captured image in pixels - * out_width, out_height -- the width and height of the NV12 image in pixels + * in_width, in_height -- The width and height of the captured image in + * pixels out_width, out_height -- the width and height of the NV12 image in + * pixels * * pitch -- The size of a single row of pixels in bytes */ - static std::optional - make(int in_width, int in_height, int out_width, int out_height, int pitch); + static std::optional make(int in_width, int in_height, int out_width, + int out_height, int pitch); // Converts loaded image into a CUDevicePtr - int - convert(std::uint8_t *Y, std::uint8_t *UV, std::uint32_t pitchY, std::uint32_t pitchUV, cudaTextureObject_t texture, stream_t::pointer stream); - int - convert(std::uint8_t *Y, std::uint8_t *UV, std::uint32_t pitchY, std::uint32_t pitchUV, cudaTextureObject_t texture, stream_t::pointer stream, const viewport_t &viewport); + int convert(std::uint8_t *Y, std::uint8_t *UV, std::uint32_t pitchY, + std::uint32_t pitchUV, cudaTextureObject_t texture, + stream_t::pointer stream); + int convert(std::uint8_t *Y, std::uint8_t *UV, std::uint32_t pitchY, + std::uint32_t pitchUV, cudaTextureObject_t texture, + stream_t::pointer stream, const viewport_t &viewport); - void - apply_colorspace(const video::sunshine_colorspace_t &colorspace); + void apply_colorspace(const video::sunshine_colorspace_t &colorspace); - int - load_ram(platf::img_t &img, cudaArray_t array); + int load_ram(platf::img_t &img, cudaArray_t array); ptr_t color_matrix; @@ -123,7 +117,7 @@ namespace cuda { viewport_t viewport; float scale; - }; +}; } // namespace cuda #endif \ No newline at end of file diff --git a/src/platform/linux/graphics.cpp b/src/platform/linux/graphics.cpp index 448051f4..3c63ccbe 100644 --- a/src/platform/linux/graphics.cpp +++ b/src/platform/linux/graphics.cpp @@ -3,89 +3,96 @@ * @brief todo */ #include "graphics.h" -#include "src/video.h" #include +#include "src/video.h" + // I want to have as little build dependencies as possible // There aren't that many DRM_FORMAT I need to use, so define them here // // They aren't likely to change any time soon. -#define fourcc_code(a, b, c, d) ((std::uint32_t)(a) | ((std::uint32_t)(b) << 8) | \ - ((std::uint32_t)(c) << 16) | ((std::uint32_t)(d) << 24)) -#define fourcc_mod_code(vendor, val) ((((uint64_t) vendor) << 56) | ((val) &0x00ffffffffffffffULL)) +#define fourcc_code(a, b, c, d) \ + ((std::uint32_t)(a) | ((std::uint32_t)(b) << 8) | \ + ((std::uint32_t)(c) << 16) | ((std::uint32_t)(d) << 24)) +#define fourcc_mod_code(vendor, val) \ + ((((uint64_t)vendor) << 56) | ((val)&0x00ffffffffffffffULL)) #define DRM_FORMAT_R8 fourcc_code('R', '8', ' ', ' ') /* [7:0] R */ -#define DRM_FORMAT_GR88 fourcc_code('G', 'R', '8', '8') /* [15:0] G:R 8:8 little endian */ -#define DRM_FORMAT_ARGB8888 fourcc_code('A', 'R', '2', '4') /* [31:0] A:R:G:B 8:8:8:8 little endian */ -#define DRM_FORMAT_XRGB8888 fourcc_code('X', 'R', '2', '4') /* [31:0] x:R:G:B 8:8:8:8 little endian */ -#define DRM_FORMAT_XBGR8888 fourcc_code('X', 'B', '2', '4') /* [31:0] x:B:G:R 8:8:8:8 little endian */ +#define DRM_FORMAT_GR88 \ + fourcc_code('G', 'R', '8', '8') /* [15:0] G:R 8:8 little endian */ +#define DRM_FORMAT_ARGB8888 \ + fourcc_code('A', 'R', '2', '4') /* [31:0] A:R:G:B 8:8:8:8 little endian */ +#define DRM_FORMAT_XRGB8888 \ + fourcc_code('X', 'R', '2', '4') /* [31:0] x:R:G:B 8:8:8:8 little endian */ +#define DRM_FORMAT_XBGR8888 \ + fourcc_code('X', 'B', '2', '4') /* [31:0] x:B:G:R 8:8:8:8 little endian */ #define DRM_FORMAT_MOD_INVALID fourcc_mod_code(0, ((1ULL << 56) - 1)) #define SUNSHINE_SHADERS_DIR SUNSHINE_ASSETS_DIR "/shaders/opengl" using namespace std::literals; namespace gl { - GladGLContext ctx; +GladGLContext ctx; - void - drain_errors(const std::string_view &prefix) { +void drain_errors(const std::string_view &prefix) { GLenum err; while ((err = ctx.GetError()) != GL_NO_ERROR) { - BOOST_LOG(error) << "GL: "sv << prefix << ": ["sv << util::hex(err).to_string_view() << ']'; + BOOST_LOG(error) << "GL: "sv << prefix << ": ["sv + << util::hex(err).to_string_view() << ']'; } - } +} - tex_t::~tex_t() { +tex_t::~tex_t() { if (!size() == 0) { - ctx.DeleteTextures(size(), begin()); + ctx.DeleteTextures(size(), begin()); } - } +} - tex_t - tex_t::make(std::size_t count) { - tex_t textures { count }; +tex_t tex_t::make(std::size_t count) { + tex_t textures{count}; ctx.GenTextures(textures.size(), textures.begin()); - float color[] = { 0.0f, 0.0f, 0.0f, 1.0f }; + float color[] = {0.0f, 0.0f, 0.0f, 1.0f}; for (auto tex : textures) { - gl::ctx.BindTexture(GL_TEXTURE_2D, tex); - gl::ctx.TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); // x - gl::ctx.TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); // y - gl::ctx.TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - gl::ctx.TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - gl::ctx.TexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, color); + gl::ctx.BindTexture(GL_TEXTURE_2D, tex); + gl::ctx.TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, + GL_CLAMP_TO_EDGE); // x + gl::ctx.TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, + GL_CLAMP_TO_EDGE); // y + gl::ctx.TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + gl::ctx.TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + gl::ctx.TexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, color); } return textures; - } +} - frame_buf_t::~frame_buf_t() { +frame_buf_t::~frame_buf_t() { if (begin()) { - ctx.DeleteFramebuffers(size(), begin()); + ctx.DeleteFramebuffers(size(), begin()); } - } +} - frame_buf_t - frame_buf_t::make(std::size_t count) { - frame_buf_t frame_buf { count }; +frame_buf_t frame_buf_t::make(std::size_t count) { + frame_buf_t frame_buf{count}; ctx.GenFramebuffers(frame_buf.size(), frame_buf.begin()); return frame_buf; - } +} - void - frame_buf_t::copy(int id, int texture, int offset_x, int offset_y, int width, int height) { +void frame_buf_t::copy(int id, int texture, int offset_x, int offset_y, + int width, int height) { gl::ctx.BindFramebuffer(GL_FRAMEBUFFER, (*this)[id]); gl::ctx.ReadBuffer(GL_COLOR_ATTACHMENT0 + id); gl::ctx.BindTexture(GL_TEXTURE_2D, texture); - gl::ctx.CopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, offset_x, offset_y, width, height); - } + gl::ctx.CopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, offset_x, offset_y, width, + height); +} - std::string - shader_t::err_str() { +std::string shader_t::err_str() { int length; ctx.GetShaderiv(handle(), GL_INFO_LOG_LENGTH, &length); @@ -97,10 +104,10 @@ namespace gl { string.resize(length - 1); return string; - } +} - util::Either - shader_t::compile(const std::string_view &source, GLenum type) { +util::Either shader_t::compile( + const std::string_view &source, GLenum type) { shader_t shader; auto data = source.data(); @@ -114,19 +121,16 @@ namespace gl { ctx.GetShaderiv(shader.handle(), GL_COMPILE_STATUS, &status); if (!status) { - return shader.err_str(); + return shader.err_str(); } return shader; - } +} - GLuint - shader_t::handle() const { - return _shader.el; - } +GLuint shader_t::handle() const { return _shader.el; } - buffer_t - buffer_t::make(util::buffer_t &&offsets, const char *block, const std::string_view &data) { +buffer_t buffer_t::make(util::buffer_t &&offsets, const char *block, + const std::string_view &data) { buffer_t buffer; buffer._block = block; buffer._size = data.size(); @@ -134,42 +138,37 @@ namespace gl { ctx.GenBuffers(1, &buffer._buffer.el); ctx.BindBuffer(GL_UNIFORM_BUFFER, buffer.handle()); - ctx.BufferData(GL_UNIFORM_BUFFER, data.size(), (const std::uint8_t *) data.data(), GL_DYNAMIC_DRAW); + ctx.BufferData(GL_UNIFORM_BUFFER, data.size(), + (const std::uint8_t *)data.data(), GL_DYNAMIC_DRAW); return buffer; - } +} - GLuint - buffer_t::handle() const { - return _buffer.el; - } +GLuint buffer_t::handle() const { return _buffer.el; } - const char * - buffer_t::block() const { - return _block; - } +const char *buffer_t::block() const { return _block; } - void - buffer_t::update(const std::string_view &view, std::size_t offset) { +void buffer_t::update(const std::string_view &view, std::size_t offset) { ctx.BindBuffer(GL_UNIFORM_BUFFER, handle()); - ctx.BufferSubData(GL_UNIFORM_BUFFER, offset, view.size(), (const void *) view.data()); - } + ctx.BufferSubData(GL_UNIFORM_BUFFER, offset, view.size(), + (const void *)view.data()); +} - void - buffer_t::update(std::string_view *members, std::size_t count, std::size_t offset) { - util::buffer_t buffer { _size }; +void buffer_t::update(std::string_view *members, std::size_t count, + std::size_t offset) { + util::buffer_t buffer{_size}; for (int x = 0; x < count; ++x) { - auto val = members[x]; + auto val = members[x]; - std::copy_n((const std::uint8_t *) val.data(), val.size(), &buffer[_offsets[x]]); + std::copy_n((const std::uint8_t *)val.data(), val.size(), + &buffer[_offsets[x]]); } update(util::view(buffer.begin(), buffer.end()), offset); - } +} - std::string - program_t::err_str() { +std::string program_t::err_str() { int length; ctx.GetProgramiv(handle(), GL_INFO_LOG_LENGTH, &length); @@ -181,10 +180,10 @@ namespace gl { string.resize(length - 1); return string; - } +} - util::Either - program_t::link(const shader_t &vert, const shader_t &frag) { +util::Either program_t::link(const shader_t &vert, + const shader_t &frag) { program_t program; program._program.el = ctx.CreateProgram(); @@ -192,11 +191,11 @@ namespace gl { ctx.AttachShader(program.handle(), vert.handle()); ctx.AttachShader(program.handle(), frag.handle()); - // p_handle stores a copy of the program handle, since program will be moved before - // the fail guard function is called. + // p_handle stores a copy of the program handle, since program will be moved + // before the fail guard function is called. auto fg = util::fail_guard([p_handle = program.handle(), &vert, &frag]() { - ctx.DetachShader(p_handle, vert.handle()); - ctx.DetachShader(p_handle, frag.handle()); + ctx.DetachShader(p_handle, vert.handle()); + ctx.DetachShader(p_handle, frag.handle()); }); ctx.LinkProgram(program.handle()); @@ -205,26 +204,26 @@ namespace gl { ctx.GetProgramiv(program.handle(), GL_LINK_STATUS, &status); if (!status) { - return program.err_str(); + return program.err_str(); } return program; - } +} - void - program_t::bind(const buffer_t &buffer) { +void program_t::bind(const buffer_t &buffer) { ctx.UseProgram(handle()); auto i = ctx.GetUniformBlockIndex(handle(), buffer.block()); ctx.BindBufferBase(GL_UNIFORM_BUFFER, i, buffer.handle()); - } +} - std::optional - program_t::uniform(const char *block, std::pair *members, std::size_t count) { +std::optional program_t::uniform( + const char *block, std::pair *members, + std::size_t count) { auto i = ctx.GetUniformBlockIndex(handle(), block); if (i == GL_INVALID_INDEX) { - BOOST_LOG(error) << "Couldn't find index of ["sv << block << ']'; - return std::nullopt; + BOOST_LOG(error) << "Couldn't find index of ["sv << block << ']'; + return std::nullopt; } int size; @@ -232,112 +231,111 @@ namespace gl { bool error_flag = false; - util::buffer_t offsets { count }; - auto indices = (std::uint32_t *) alloca(count * sizeof(std::uint32_t)); - auto names = (const char **) alloca(count * sizeof(const char *)); + util::buffer_t offsets{count}; + auto indices = (std::uint32_t *)alloca(count * sizeof(std::uint32_t)); + auto names = (const char **)alloca(count * sizeof(const char *)); auto names_p = names; std::for_each_n(members, count, [names_p](auto &member) mutable { - *names_p++ = std::get<0>(member); + *names_p++ = std::get<0>(member); }); std::fill_n(indices, count, GL_INVALID_INDEX); ctx.GetUniformIndices(handle(), count, names, indices); for (int x = 0; x < count; ++x) { - if (indices[x] == GL_INVALID_INDEX) { - error_flag = true; + if (indices[x] == GL_INVALID_INDEX) { + error_flag = true; - BOOST_LOG(error) << "Couldn't find ["sv << block << '.' << members[x].first << ']'; - } + BOOST_LOG(error) << "Couldn't find ["sv << block << '.' + << members[x].first << ']'; + } } if (error_flag) { - return std::nullopt; + return std::nullopt; } - ctx.GetActiveUniformsiv(handle(), count, indices, GL_UNIFORM_OFFSET, offsets.begin()); - util::buffer_t buffer { (std::size_t) size }; + ctx.GetActiveUniformsiv(handle(), count, indices, GL_UNIFORM_OFFSET, + offsets.begin()); + util::buffer_t buffer{(std::size_t)size}; for (int x = 0; x < count; ++x) { - auto val = std::get<1>(members[x]); + auto val = std::get<1>(members[x]); - std::copy_n((const std::uint8_t *) val.data(), val.size(), &buffer[offsets[x]]); + std::copy_n((const std::uint8_t *)val.data(), val.size(), + &buffer[offsets[x]]); } - return buffer_t::make(std::move(offsets), block, std::string_view { (char *) buffer.begin(), buffer.size() }); - } + return buffer_t::make( + std::move(offsets), block, + std::string_view{(char *)buffer.begin(), buffer.size()}); +} - GLuint - program_t::handle() const { - return _program.el; - } +GLuint program_t::handle() const { return _program.el; } } // namespace gl namespace gbm { - device_destroy_fn device_destroy; - create_device_fn create_device; +device_destroy_fn device_destroy; +create_device_fn create_device; - int - init() { - static void *handle { nullptr }; +int init() { + static void *handle{nullptr}; static bool funcs_loaded = false; if (funcs_loaded) return 0; if (!handle) { - handle = dyn::handle({ "libgbm.so.1", "libgbm.so" }); - if (!handle) { - return -1; - } + handle = dyn::handle({"libgbm.so.1", "libgbm.so"}); + if (!handle) { + return -1; + } } - std::vector> funcs { - { (GLADapiproc *) &device_destroy, "gbm_device_destroy" }, - { (GLADapiproc *) &create_device, "gbm_create_device" }, + std::vector> funcs{ + {(GLADapiproc *)&device_destroy, "gbm_device_destroy"}, + {(GLADapiproc *)&create_device, "gbm_create_device"}, }; if (dyn::load(handle, funcs)) { - return -1; + return -1; } funcs_loaded = true; return 0; - } +} } // namespace gbm namespace egl { - constexpr auto EGL_LINUX_DMA_BUF_EXT = 0x3270; - constexpr auto EGL_LINUX_DRM_FOURCC_EXT = 0x3271; - constexpr auto EGL_DMA_BUF_PLANE0_FD_EXT = 0x3272; - constexpr auto EGL_DMA_BUF_PLANE0_OFFSET_EXT = 0x3273; - constexpr auto EGL_DMA_BUF_PLANE0_PITCH_EXT = 0x3274; - constexpr auto EGL_DMA_BUF_PLANE1_FD_EXT = 0x3275; - constexpr auto EGL_DMA_BUF_PLANE1_OFFSET_EXT = 0x3276; - constexpr auto EGL_DMA_BUF_PLANE1_PITCH_EXT = 0x3277; - constexpr auto EGL_DMA_BUF_PLANE2_FD_EXT = 0x3278; - constexpr auto EGL_DMA_BUF_PLANE2_OFFSET_EXT = 0x3279; - constexpr auto EGL_DMA_BUF_PLANE2_PITCH_EXT = 0x327A; - constexpr auto EGL_DMA_BUF_PLANE3_FD_EXT = 0x3440; - constexpr auto EGL_DMA_BUF_PLANE3_OFFSET_EXT = 0x3441; - constexpr auto EGL_DMA_BUF_PLANE3_PITCH_EXT = 0x3442; - constexpr auto EGL_DMA_BUF_PLANE0_MODIFIER_LO_EXT = 0x3443; - constexpr auto EGL_DMA_BUF_PLANE0_MODIFIER_HI_EXT = 0x3444; - constexpr auto EGL_DMA_BUF_PLANE1_MODIFIER_LO_EXT = 0x3445; - constexpr auto EGL_DMA_BUF_PLANE1_MODIFIER_HI_EXT = 0x3446; - constexpr auto EGL_DMA_BUF_PLANE2_MODIFIER_LO_EXT = 0x3447; - constexpr auto EGL_DMA_BUF_PLANE2_MODIFIER_HI_EXT = 0x3448; - constexpr auto EGL_DMA_BUF_PLANE3_MODIFIER_LO_EXT = 0x3449; - constexpr auto EGL_DMA_BUF_PLANE3_MODIFIER_HI_EXT = 0x344A; +constexpr auto EGL_LINUX_DMA_BUF_EXT = 0x3270; +constexpr auto EGL_LINUX_DRM_FOURCC_EXT = 0x3271; +constexpr auto EGL_DMA_BUF_PLANE0_FD_EXT = 0x3272; +constexpr auto EGL_DMA_BUF_PLANE0_OFFSET_EXT = 0x3273; +constexpr auto EGL_DMA_BUF_PLANE0_PITCH_EXT = 0x3274; +constexpr auto EGL_DMA_BUF_PLANE1_FD_EXT = 0x3275; +constexpr auto EGL_DMA_BUF_PLANE1_OFFSET_EXT = 0x3276; +constexpr auto EGL_DMA_BUF_PLANE1_PITCH_EXT = 0x3277; +constexpr auto EGL_DMA_BUF_PLANE2_FD_EXT = 0x3278; +constexpr auto EGL_DMA_BUF_PLANE2_OFFSET_EXT = 0x3279; +constexpr auto EGL_DMA_BUF_PLANE2_PITCH_EXT = 0x327A; +constexpr auto EGL_DMA_BUF_PLANE3_FD_EXT = 0x3440; +constexpr auto EGL_DMA_BUF_PLANE3_OFFSET_EXT = 0x3441; +constexpr auto EGL_DMA_BUF_PLANE3_PITCH_EXT = 0x3442; +constexpr auto EGL_DMA_BUF_PLANE0_MODIFIER_LO_EXT = 0x3443; +constexpr auto EGL_DMA_BUF_PLANE0_MODIFIER_HI_EXT = 0x3444; +constexpr auto EGL_DMA_BUF_PLANE1_MODIFIER_LO_EXT = 0x3445; +constexpr auto EGL_DMA_BUF_PLANE1_MODIFIER_HI_EXT = 0x3446; +constexpr auto EGL_DMA_BUF_PLANE2_MODIFIER_LO_EXT = 0x3447; +constexpr auto EGL_DMA_BUF_PLANE2_MODIFIER_HI_EXT = 0x3448; +constexpr auto EGL_DMA_BUF_PLANE3_MODIFIER_LO_EXT = 0x3449; +constexpr auto EGL_DMA_BUF_PLANE3_MODIFIER_HI_EXT = 0x344A; - bool - fail() { - return eglGetError() != EGL_SUCCESS; - } +bool fail() { return eglGetError() != EGL_SUCCESS; } - display_t - make_display(std::variant native_display) { +display_t make_display( + std::variant + native_display) { constexpr auto EGL_PLATFORM_GBM_MESA = 0x31D7; constexpr auto EGL_PLATFORM_WAYLAND_KHR = 0x31D8; constexpr auto EGL_PLATFORM_X11_KHR = 0x31D5; @@ -346,35 +344,39 @@ namespace egl { void *native_display_p; switch (native_display.index()) { - case 0: - egl_platform = EGL_PLATFORM_GBM_MESA; - native_display_p = std::get<0>(native_display); - break; - case 1: - egl_platform = EGL_PLATFORM_WAYLAND_KHR; - native_display_p = std::get<1>(native_display); - break; - case 2: - egl_platform = EGL_PLATFORM_X11_KHR; - native_display_p = std::get<2>(native_display); - break; - default: - BOOST_LOG(error) << "egl::make_display(): Index ["sv << native_display.index() << "] not implemented"sv; - return nullptr; + case 0: + egl_platform = EGL_PLATFORM_GBM_MESA; + native_display_p = std::get<0>(native_display); + break; + case 1: + egl_platform = EGL_PLATFORM_WAYLAND_KHR; + native_display_p = std::get<1>(native_display); + break; + case 2: + egl_platform = EGL_PLATFORM_X11_KHR; + native_display_p = std::get<2>(native_display); + break; + default: + BOOST_LOG(error) << "egl::make_display(): Index ["sv + << native_display.index() << "] not implemented"sv; + return nullptr; } // native_display.left() equals native_display.right() - display_t display = eglGetPlatformDisplay(egl_platform, native_display_p, nullptr); + display_t display = + eglGetPlatformDisplay(egl_platform, native_display_p, nullptr); if (fail()) { - BOOST_LOG(error) << "Couldn't open EGL display: ["sv << util::hex(eglGetError()).to_string_view() << ']'; - return nullptr; + BOOST_LOG(error) << "Couldn't open EGL display: ["sv + << util::hex(eglGetError()).to_string_view() << ']'; + return nullptr; } int major, minor; if (!eglInitialize(display.get(), &major, &minor)) { - BOOST_LOG(error) << "Couldn't initialize EGL display: ["sv << util::hex(eglGetError()).to_string_view() << ']'; - return nullptr; + BOOST_LOG(error) << "Couldn't initialize EGL display: ["sv + << util::hex(eglGetError()).to_string_view() << ']'; + return nullptr; } const char *extension_st = eglQueryString(display.get(), EGL_EXTENSIONS); @@ -382,126 +384,125 @@ namespace egl { const char *vendor = eglQueryString(display.get(), EGL_VENDOR); const char *apis = eglQueryString(display.get(), EGL_CLIENT_APIS); - BOOST_LOG(debug) << "EGL: ["sv << vendor << "]: version ["sv << version << ']'; + BOOST_LOG(debug) << "EGL: ["sv << vendor << "]: version ["sv << version + << ']'; BOOST_LOG(debug) << "API's supported: ["sv << apis << ']'; - const char *extensions[] { - "EGL_KHR_create_context", - "EGL_KHR_surfaceless_context", - "EGL_EXT_image_dma_buf_import", - "EGL_EXT_image_dma_buf_import_modifiers", + const char *extensions[]{ + "EGL_KHR_create_context", + "EGL_KHR_surfaceless_context", + "EGL_EXT_image_dma_buf_import", + "EGL_EXT_image_dma_buf_import_modifiers", }; for (auto ext : extensions) { - if (!std::strstr(extension_st, ext)) { - BOOST_LOG(error) << "Missing extension: ["sv << ext << ']'; - return nullptr; - } + if (!std::strstr(extension_st, ext)) { + BOOST_LOG(error) << "Missing extension: ["sv << ext << ']'; + return nullptr; + } } return display; - } +} - std::optional - make_ctx(display_t::pointer display) { - constexpr int conf_attr[] { - EGL_RENDERABLE_TYPE, EGL_OPENGL_BIT, EGL_NONE - }; +std::optional make_ctx(display_t::pointer display) { + constexpr int conf_attr[]{EGL_RENDERABLE_TYPE, EGL_OPENGL_BIT, EGL_NONE}; int count; EGLConfig conf; if (!eglChooseConfig(display, conf_attr, &conf, 1, &count)) { - BOOST_LOG(error) << "Couldn't set config attributes: ["sv << util::hex(eglGetError()).to_string_view() << ']'; - return std::nullopt; + BOOST_LOG(error) << "Couldn't set config attributes: ["sv + << util::hex(eglGetError()).to_string_view() << ']'; + return std::nullopt; } if (!eglBindAPI(EGL_OPENGL_API)) { - BOOST_LOG(error) << "Couldn't bind API: ["sv << util::hex(eglGetError()).to_string_view() << ']'; - return std::nullopt; + BOOST_LOG(error) << "Couldn't bind API: ["sv + << util::hex(eglGetError()).to_string_view() << ']'; + return std::nullopt; } - constexpr int attr[] { - EGL_CONTEXT_CLIENT_VERSION, 3, EGL_NONE - }; + constexpr int attr[]{EGL_CONTEXT_CLIENT_VERSION, 3, EGL_NONE}; - ctx_t ctx { display, eglCreateContext(display, conf, EGL_NO_CONTEXT, attr) }; + ctx_t ctx{display, eglCreateContext(display, conf, EGL_NO_CONTEXT, attr)}; if (fail()) { - BOOST_LOG(error) << "Couldn't create EGL context: ["sv << util::hex(eglGetError()).to_string_view() << ']'; - return std::nullopt; + BOOST_LOG(error) << "Couldn't create EGL context: ["sv + << util::hex(eglGetError()).to_string_view() << ']'; + return std::nullopt; } TUPLE_EL_REF(ctx_p, 1, ctx.el); if (!eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, ctx_p)) { - BOOST_LOG(error) << "Couldn't make current display"sv; - return std::nullopt; + BOOST_LOG(error) << "Couldn't make current display"sv; + return std::nullopt; } if (!gladLoadGLContext(&gl::ctx, eglGetProcAddress)) { - BOOST_LOG(error) << "Couldn't load OpenGL library"sv; - return std::nullopt; + BOOST_LOG(error) << "Couldn't load OpenGL library"sv; + return std::nullopt; } BOOST_LOG(debug) << "GL: vendor: "sv << gl::ctx.GetString(GL_VENDOR); BOOST_LOG(debug) << "GL: renderer: "sv << gl::ctx.GetString(GL_RENDERER); BOOST_LOG(debug) << "GL: version: "sv << gl::ctx.GetString(GL_VERSION); - BOOST_LOG(debug) << "GL: shader: "sv << gl::ctx.GetString(GL_SHADING_LANGUAGE_VERSION); + BOOST_LOG(debug) << "GL: shader: "sv + << gl::ctx.GetString(GL_SHADING_LANGUAGE_VERSION); gl::ctx.PixelStorei(GL_UNPACK_ALIGNMENT, 1); return ctx; - } +} - struct plane_attr_t { +struct plane_attr_t { EGLAttrib fd; EGLAttrib offset; EGLAttrib pitch; EGLAttrib lo; EGLAttrib hi; - }; +}; - inline plane_attr_t - get_plane(std::uint32_t plane_indice) { +inline plane_attr_t get_plane(std::uint32_t plane_indice) { switch (plane_indice) { - case 0: - return { - EGL_DMA_BUF_PLANE0_FD_EXT, - EGL_DMA_BUF_PLANE0_OFFSET_EXT, - EGL_DMA_BUF_PLANE0_PITCH_EXT, - EGL_DMA_BUF_PLANE0_MODIFIER_LO_EXT, - EGL_DMA_BUF_PLANE0_MODIFIER_HI_EXT, - }; - case 1: - return { - EGL_DMA_BUF_PLANE1_FD_EXT, - EGL_DMA_BUF_PLANE1_OFFSET_EXT, - EGL_DMA_BUF_PLANE1_PITCH_EXT, - EGL_DMA_BUF_PLANE1_MODIFIER_LO_EXT, - EGL_DMA_BUF_PLANE1_MODIFIER_HI_EXT, - }; - case 2: - return { - EGL_DMA_BUF_PLANE2_FD_EXT, - EGL_DMA_BUF_PLANE2_OFFSET_EXT, - EGL_DMA_BUF_PLANE2_PITCH_EXT, - EGL_DMA_BUF_PLANE2_MODIFIER_LO_EXT, - EGL_DMA_BUF_PLANE2_MODIFIER_HI_EXT, - }; - case 3: - return { - EGL_DMA_BUF_PLANE3_FD_EXT, - EGL_DMA_BUF_PLANE3_OFFSET_EXT, - EGL_DMA_BUF_PLANE3_PITCH_EXT, - EGL_DMA_BUF_PLANE3_MODIFIER_LO_EXT, - EGL_DMA_BUF_PLANE3_MODIFIER_HI_EXT, - }; + case 0: + return { + EGL_DMA_BUF_PLANE0_FD_EXT, + EGL_DMA_BUF_PLANE0_OFFSET_EXT, + EGL_DMA_BUF_PLANE0_PITCH_EXT, + EGL_DMA_BUF_PLANE0_MODIFIER_LO_EXT, + EGL_DMA_BUF_PLANE0_MODIFIER_HI_EXT, + }; + case 1: + return { + EGL_DMA_BUF_PLANE1_FD_EXT, + EGL_DMA_BUF_PLANE1_OFFSET_EXT, + EGL_DMA_BUF_PLANE1_PITCH_EXT, + EGL_DMA_BUF_PLANE1_MODIFIER_LO_EXT, + EGL_DMA_BUF_PLANE1_MODIFIER_HI_EXT, + }; + case 2: + return { + EGL_DMA_BUF_PLANE2_FD_EXT, + EGL_DMA_BUF_PLANE2_OFFSET_EXT, + EGL_DMA_BUF_PLANE2_PITCH_EXT, + EGL_DMA_BUF_PLANE2_MODIFIER_LO_EXT, + EGL_DMA_BUF_PLANE2_MODIFIER_HI_EXT, + }; + case 3: + return { + EGL_DMA_BUF_PLANE3_FD_EXT, + EGL_DMA_BUF_PLANE3_OFFSET_EXT, + EGL_DMA_BUF_PLANE3_PITCH_EXT, + EGL_DMA_BUF_PLANE3_MODIFIER_LO_EXT, + EGL_DMA_BUF_PLANE3_MODIFIER_HI_EXT, + }; } // Avoid warning return {}; - } +} - std::optional - import_source(display_t::pointer egl_display, const surface_descriptor_t &xrgb) { +std::optional import_source(display_t::pointer egl_display, + const surface_descriptor_t &xrgb) { EGLAttrib attribs[47]; int atti = 0; attribs[atti++] = EGL_WIDTH; @@ -512,40 +513,40 @@ namespace egl { attribs[atti++] = xrgb.fourcc; for (auto x = 0; x < 4; ++x) { - auto fd = xrgb.fds[x]; + auto fd = xrgb.fds[x]; - if (fd < 0) { - continue; - } + if (fd < 0) { + continue; + } - auto plane_attr = get_plane(x); + auto plane_attr = get_plane(x); - attribs[atti++] = plane_attr.fd; - attribs[atti++] = fd; - attribs[atti++] = plane_attr.offset; - attribs[atti++] = xrgb.offsets[x]; - attribs[atti++] = plane_attr.pitch; - attribs[atti++] = xrgb.pitches[x]; + attribs[atti++] = plane_attr.fd; + attribs[atti++] = fd; + attribs[atti++] = plane_attr.offset; + attribs[atti++] = xrgb.offsets[x]; + attribs[atti++] = plane_attr.pitch; + attribs[atti++] = xrgb.pitches[x]; - if (xrgb.modifier != DRM_FORMAT_MOD_INVALID) { - attribs[atti++] = plane_attr.lo; - attribs[atti++] = xrgb.modifier & 0xFFFFFFFF; - attribs[atti++] = plane_attr.hi; - attribs[atti++] = xrgb.modifier >> 32; - } + if (xrgb.modifier != DRM_FORMAT_MOD_INVALID) { + attribs[atti++] = plane_attr.lo; + attribs[atti++] = xrgb.modifier & 0xFFFFFFFF; + attribs[atti++] = plane_attr.hi; + attribs[atti++] = xrgb.modifier >> 32; + } } attribs[atti++] = EGL_NONE; - rgb_t rgb { - egl_display, - eglCreateImage(egl_display, EGL_NO_CONTEXT, EGL_LINUX_DMA_BUF_EXT, nullptr, attribs), - gl::tex_t::make(1) - }; + rgb_t rgb{egl_display, + eglCreateImage(egl_display, EGL_NO_CONTEXT, EGL_LINUX_DMA_BUF_EXT, + nullptr, attribs), + gl::tex_t::make(1)}; if (!rgb->xrgb8) { - BOOST_LOG(error) << "Couldn't import RGB Image: "sv << util::hex(eglGetError()).to_string_view(); + BOOST_LOG(error) << "Couldn't import RGB Image: "sv + << util::hex(eglGetError()).to_string_view(); - return std::nullopt; + return std::nullopt; } gl::ctx.BindTexture(GL_TEXTURE_2D, rgb->tex[0]); @@ -556,41 +557,38 @@ namespace egl { gl_drain_errors; return rgb; - } +} - std::optional - import_target(display_t::pointer egl_display, std::array &&fds, const surface_descriptor_t &r8, const surface_descriptor_t &gr88) { - EGLAttrib img_attr_planes[2][13] { - { EGL_LINUX_DRM_FOURCC_EXT, DRM_FORMAT_R8, - EGL_WIDTH, r8.width, - EGL_HEIGHT, r8.height, - EGL_DMA_BUF_PLANE0_FD_EXT, r8.fds[0], - EGL_DMA_BUF_PLANE0_OFFSET_EXT, r8.offsets[0], - EGL_DMA_BUF_PLANE0_PITCH_EXT, r8.pitches[0], - EGL_NONE }, +std::optional import_target( + display_t::pointer egl_display, + std::array &&fds, + const surface_descriptor_t &r8, const surface_descriptor_t &gr88) { + EGLAttrib img_attr_planes[2][13]{ + {EGL_LINUX_DRM_FOURCC_EXT, DRM_FORMAT_R8, EGL_WIDTH, r8.width, + EGL_HEIGHT, r8.height, EGL_DMA_BUF_PLANE0_FD_EXT, r8.fds[0], + EGL_DMA_BUF_PLANE0_OFFSET_EXT, r8.offsets[0], + EGL_DMA_BUF_PLANE0_PITCH_EXT, r8.pitches[0], EGL_NONE}, - { EGL_LINUX_DRM_FOURCC_EXT, DRM_FORMAT_GR88, - EGL_WIDTH, gr88.width, - EGL_HEIGHT, gr88.height, - EGL_DMA_BUF_PLANE0_FD_EXT, r8.fds[0], - EGL_DMA_BUF_PLANE0_OFFSET_EXT, gr88.offsets[0], - EGL_DMA_BUF_PLANE0_PITCH_EXT, gr88.pitches[0], - EGL_NONE }, + {EGL_LINUX_DRM_FOURCC_EXT, DRM_FORMAT_GR88, EGL_WIDTH, gr88.width, + EGL_HEIGHT, gr88.height, EGL_DMA_BUF_PLANE0_FD_EXT, r8.fds[0], + EGL_DMA_BUF_PLANE0_OFFSET_EXT, gr88.offsets[0], + EGL_DMA_BUF_PLANE0_PITCH_EXT, gr88.pitches[0], EGL_NONE}, }; - nv12_t nv12 { - egl_display, - eglCreateImage(egl_display, EGL_NO_CONTEXT, EGL_LINUX_DMA_BUF_EXT, nullptr, img_attr_planes[0]), - eglCreateImage(egl_display, EGL_NO_CONTEXT, EGL_LINUX_DMA_BUF_EXT, nullptr, img_attr_planes[1]), - gl::tex_t::make(2), - gl::frame_buf_t::make(2), - std::move(fds) - }; + nv12_t nv12{ + egl_display, + eglCreateImage(egl_display, EGL_NO_CONTEXT, EGL_LINUX_DMA_BUF_EXT, + nullptr, img_attr_planes[0]), + eglCreateImage(egl_display, EGL_NO_CONTEXT, EGL_LINUX_DMA_BUF_EXT, + nullptr, img_attr_planes[1]), + gl::tex_t::make(2), + gl::frame_buf_t::make(2), + std::move(fds)}; if (!nv12->r8 || !nv12->bg88) { - BOOST_LOG(error) << "Couldn't create KHR Image"sv; + BOOST_LOG(error) << "Couldn't create KHR Image"sv; - return std::nullopt; + return std::nullopt; } gl::ctx.BindTexture(GL_TEXTURE_2D, nv12->tex[0]); @@ -604,34 +602,33 @@ namespace egl { gl_drain_errors; return nv12; - } +} - void - sws_t::apply_colorspace(const video::sunshine_colorspace_t &colorspace) { +void sws_t::apply_colorspace(const video::sunshine_colorspace_t &colorspace) { auto color_p = video::color_vectors_from_colorspace(colorspace); - std::string_view members[] { - util::view(color_p->color_vec_y), - util::view(color_p->color_vec_u), - util::view(color_p->color_vec_v), - util::view(color_p->range_y), - util::view(color_p->range_uv), + std::string_view members[]{ + util::view(color_p->color_vec_y), util::view(color_p->color_vec_u), + util::view(color_p->color_vec_v), util::view(color_p->range_y), + util::view(color_p->range_uv), }; - color_matrix.update(members, sizeof(members) / sizeof(decltype(members[0]))); + color_matrix.update(members, + sizeof(members) / sizeof(decltype(members[0]))); program[0].bind(color_matrix); program[1].bind(color_matrix); - } +} - std::optional - sws_t::make(int in_width, int in_height, int out_width, int out_heigth, gl::tex_t &&tex) { +std::optional sws_t::make(int in_width, int in_height, int out_width, + int out_heigth, gl::tex_t &&tex) { sws_t sws; sws.serial = std::numeric_limits::max(); // Ensure aspect ratio is maintained - auto scalar = std::fminf(out_width / (float) in_width, out_heigth / (float) in_height); + auto scalar = + std::fminf(out_width / (float)in_width, out_heigth / (float)in_height); auto out_width_f = in_width * scalar; auto out_height_f = in_height * scalar; @@ -651,89 +648,97 @@ namespace egl { auto width_i = 1.0f / sws.out_width; { - const char *sources[] { - SUNSHINE_SHADERS_DIR "/ConvertUV.frag", - SUNSHINE_SHADERS_DIR "/ConvertUV.vert", - SUNSHINE_SHADERS_DIR "/ConvertY.frag", - SUNSHINE_SHADERS_DIR "/Scene.vert", - SUNSHINE_SHADERS_DIR "/Scene.frag", - }; + const char *sources[]{ + SUNSHINE_SHADERS_DIR "/ConvertUV.frag", + SUNSHINE_SHADERS_DIR "/ConvertUV.vert", + SUNSHINE_SHADERS_DIR "/ConvertY.frag", + SUNSHINE_SHADERS_DIR "/Scene.vert", + SUNSHINE_SHADERS_DIR "/Scene.frag", + }; - GLenum shader_type[2] { - GL_FRAGMENT_SHADER, - GL_VERTEX_SHADER, - }; + GLenum shader_type[2]{ + GL_FRAGMENT_SHADER, + GL_VERTEX_SHADER, + }; - constexpr auto count = sizeof(sources) / sizeof(const char *); + constexpr auto count = sizeof(sources) / sizeof(const char *); - util::Either compiled_sources[count]; + util::Either compiled_sources[count]; - bool error_flag = false; - for (int x = 0; x < count; ++x) { - auto &compiled_source = compiled_sources[x]; + bool error_flag = false; + for (int x = 0; x < count; ++x) { + auto &compiled_source = compiled_sources[x]; - compiled_source = gl::shader_t::compile(read_file(sources[x]), shader_type[x % 2]); - gl_drain_errors; + compiled_source = gl::shader_t::compile(read_file(sources[x]), + shader_type[x % 2]); + gl_drain_errors; - if (compiled_source.has_right()) { - BOOST_LOG(error) << sources[x] << ": "sv << compiled_source.right(); - error_flag = true; + if (compiled_source.has_right()) { + BOOST_LOG(error) + << sources[x] << ": "sv << compiled_source.right(); + error_flag = true; + } } - } - if (error_flag) { - return std::nullopt; - } + if (error_flag) { + return std::nullopt; + } - auto program = gl::program_t::link(compiled_sources[3].left(), compiled_sources[4].left()); - if (program.has_right()) { - BOOST_LOG(error) << "GL linker: "sv << program.right(); - return std::nullopt; - } + auto program = gl::program_t::link(compiled_sources[3].left(), + compiled_sources[4].left()); + if (program.has_right()) { + BOOST_LOG(error) << "GL linker: "sv << program.right(); + return std::nullopt; + } - // Cursor - shader - sws.program[2] = std::move(program.left()); + // Cursor - shader + sws.program[2] = std::move(program.left()); - program = gl::program_t::link(compiled_sources[1].left(), compiled_sources[0].left()); - if (program.has_right()) { - BOOST_LOG(error) << "GL linker: "sv << program.right(); - return std::nullopt; - } + program = gl::program_t::link(compiled_sources[1].left(), + compiled_sources[0].left()); + if (program.has_right()) { + BOOST_LOG(error) << "GL linker: "sv << program.right(); + return std::nullopt; + } - // UV - shader - sws.program[1] = std::move(program.left()); + // UV - shader + sws.program[1] = std::move(program.left()); - program = gl::program_t::link(compiled_sources[3].left(), compiled_sources[2].left()); - if (program.has_right()) { - BOOST_LOG(error) << "GL linker: "sv << program.right(); - return std::nullopt; - } + program = gl::program_t::link(compiled_sources[3].left(), + compiled_sources[2].left()); + if (program.has_right()) { + BOOST_LOG(error) << "GL linker: "sv << program.right(); + return std::nullopt; + } - // Y - shader - sws.program[0] = std::move(program.left()); + // Y - shader + sws.program[0] = std::move(program.left()); } - auto loc_width_i = gl::ctx.GetUniformLocation(sws.program[1].handle(), "width_i"); + auto loc_width_i = + gl::ctx.GetUniformLocation(sws.program[1].handle(), "width_i"); if (loc_width_i < 0) { - BOOST_LOG(error) << "Couldn't find uniform [width_i]"sv; - return std::nullopt; + BOOST_LOG(error) << "Couldn't find uniform [width_i]"sv; + return std::nullopt; } gl::ctx.UseProgram(sws.program[1].handle()); gl::ctx.Uniform1fv(loc_width_i, 1, &width_i); - auto color_p = video::color_vectors_from_colorspace(video::colorspace_e::rec601, false); - std::pair members[] { - std::make_pair("color_vec_y", util::view(color_p->color_vec_y)), - std::make_pair("color_vec_u", util::view(color_p->color_vec_u)), - std::make_pair("color_vec_v", util::view(color_p->color_vec_v)), - std::make_pair("range_y", util::view(color_p->range_y)), - std::make_pair("range_uv", util::view(color_p->range_uv)), + auto color_p = video::color_vectors_from_colorspace( + video::colorspace_e::rec601, false); + std::pair members[]{ + std::make_pair("color_vec_y", util::view(color_p->color_vec_y)), + std::make_pair("color_vec_u", util::view(color_p->color_vec_u)), + std::make_pair("color_vec_v", util::view(color_p->color_vec_v)), + std::make_pair("range_y", util::view(color_p->range_y)), + std::make_pair("range_uv", util::view(color_p->range_uv)), }; - auto color_matrix = sws.program[0].uniform("ColorMatrix", members, sizeof(members) / sizeof(decltype(members[0]))); + auto color_matrix = sws.program[0].uniform( + "ColorMatrix", members, sizeof(members) / sizeof(decltype(members[0]))); if (!color_matrix) { - return std::nullopt; + return std::nullopt; } sws.color_matrix = std::move(*color_matrix); @@ -751,125 +756,130 @@ namespace egl { gl_drain_errors; return sws; - } +} - int - sws_t::blank(gl::frame_buf_t &fb, int offsetX, int offsetY, int width, int height) { +int sws_t::blank(gl::frame_buf_t &fb, int offsetX, int offsetY, int width, + int height) { auto f = [&]() { - std::swap(offsetX, this->offsetX); - std::swap(offsetY, this->offsetY); - std::swap(width, this->out_width); - std::swap(height, this->out_height); + std::swap(offsetX, this->offsetX); + std::swap(offsetY, this->offsetY); + std::swap(width, this->out_width); + std::swap(height, this->out_height); }; f(); auto fg = util::fail_guard(f); return convert(fb); - } +} - std::optional - sws_t::make(int in_width, int in_height, int out_width, int out_heigth) { +std::optional sws_t::make(int in_width, int in_height, int out_width, + int out_heigth) { auto tex = gl::tex_t::make(2); gl::ctx.BindTexture(GL_TEXTURE_2D, tex[0]); gl::ctx.TexStorage2D(GL_TEXTURE_2D, 1, GL_RGBA8, in_width, in_height); return make(in_width, in_height, out_width, out_heigth, std::move(tex)); - } +} - void - sws_t::load_ram(platf::img_t &img) { +void sws_t::load_ram(platf::img_t &img) { loaded_texture = tex[0]; gl::ctx.BindTexture(GL_TEXTURE_2D, loaded_texture); - gl::ctx.TexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, img.width, img.height, GL_BGRA, GL_UNSIGNED_BYTE, img.data); - } + gl::ctx.TexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, img.width, img.height, + GL_BGRA, GL_UNSIGNED_BYTE, img.data); +} - void - sws_t::load_vram(img_descriptor_t &img, int offset_x, int offset_y, int texture) { +void sws_t::load_vram(img_descriptor_t &img, int offset_x, int offset_y, + int texture) { // When only a sub-part of the image must be encoded... - const bool copy = offset_x || offset_y || img.sd.width != in_width || img.sd.height != in_height; + const bool copy = offset_x || offset_y || img.sd.width != in_width || + img.sd.height != in_height; if (copy) { - auto framebuf = gl::frame_buf_t::make(1); - framebuf.bind(&texture, &texture + 1); + auto framebuf = gl::frame_buf_t::make(1); + framebuf.bind(&texture, &texture + 1); - loaded_texture = tex[0]; - framebuf.copy(0, loaded_texture, offset_x, offset_y, in_width, in_height); - } - else { - loaded_texture = texture; + loaded_texture = tex[0]; + framebuf.copy(0, loaded_texture, offset_x, offset_y, in_width, + in_height); + } else { + loaded_texture = texture; } if (img.data) { - GLenum attachment = GL_COLOR_ATTACHMENT0; + GLenum attachment = GL_COLOR_ATTACHMENT0; - gl::ctx.BindFramebuffer(GL_FRAMEBUFFER, cursor_framebuffer[0]); - gl::ctx.UseProgram(program[2].handle()); + gl::ctx.BindFramebuffer(GL_FRAMEBUFFER, cursor_framebuffer[0]); + gl::ctx.UseProgram(program[2].handle()); + + // When a copy has already been made... + if (!copy) { + gl::ctx.BindTexture(GL_TEXTURE_2D, texture); + gl::ctx.DrawBuffers(1, &attachment); + + gl::ctx.Viewport(0, 0, in_width, in_height); + gl::ctx.DrawArrays(GL_TRIANGLES, 0, 3); + + loaded_texture = tex[0]; + } + + gl::ctx.BindTexture(GL_TEXTURE_2D, tex[1]); + if (serial != img.serial) { + serial = img.serial; + + gl::ctx.TexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, img.width, + img.height, 0, GL_BGRA, GL_UNSIGNED_BYTE, + img.data); + } + + gl::ctx.Enable(GL_BLEND); - // When a copy has already been made... - if (!copy) { - gl::ctx.BindTexture(GL_TEXTURE_2D, texture); gl::ctx.DrawBuffers(1, &attachment); - gl::ctx.Viewport(0, 0, in_width, in_height); +#ifndef NDEBUG + auto status = gl::ctx.CheckFramebufferStatus(GL_FRAMEBUFFER); + if (status != GL_FRAMEBUFFER_COMPLETE) { + BOOST_LOG(error) + << "Pass Cursor: CheckFramebufferStatus() --> [0x"sv + << util::hex(status).to_string_view() << ']'; + return; + } +#endif + + gl::ctx.Viewport(img.x, img.y, img.width, img.height); gl::ctx.DrawArrays(GL_TRIANGLES, 0, 3); - loaded_texture = tex[0]; - } + gl::ctx.Disable(GL_BLEND); - gl::ctx.BindTexture(GL_TEXTURE_2D, tex[1]); - if (serial != img.serial) { - serial = img.serial; - - gl::ctx.TexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, img.width, img.height, 0, GL_BGRA, GL_UNSIGNED_BYTE, img.data); - } - - gl::ctx.Enable(GL_BLEND); - - gl::ctx.DrawBuffers(1, &attachment); - -#ifndef NDEBUG - auto status = gl::ctx.CheckFramebufferStatus(GL_FRAMEBUFFER); - if (status != GL_FRAMEBUFFER_COMPLETE) { - BOOST_LOG(error) << "Pass Cursor: CheckFramebufferStatus() --> [0x"sv << util::hex(status).to_string_view() << ']'; - return; - } -#endif - - gl::ctx.Viewport(img.x, img.y, img.width, img.height); - gl::ctx.DrawArrays(GL_TRIANGLES, 0, 3); - - gl::ctx.Disable(GL_BLEND); - - gl::ctx.BindTexture(GL_TEXTURE_2D, 0); - gl::ctx.BindFramebuffer(GL_FRAMEBUFFER, 0); + gl::ctx.BindTexture(GL_TEXTURE_2D, 0); + gl::ctx.BindFramebuffer(GL_FRAMEBUFFER, 0); } - } +} - int - sws_t::convert(gl::frame_buf_t &fb) { +int sws_t::convert(gl::frame_buf_t &fb) { gl::ctx.BindTexture(GL_TEXTURE_2D, loaded_texture); - GLenum attachments[] { - GL_COLOR_ATTACHMENT0, - GL_COLOR_ATTACHMENT1 - }; + GLenum attachments[]{GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1}; - for (int x = 0; x < sizeof(attachments) / sizeof(decltype(attachments[0])); ++x) { - gl::ctx.BindFramebuffer(GL_FRAMEBUFFER, fb[x]); - gl::ctx.DrawBuffers(1, &attachments[x]); + for (int x = 0; x < sizeof(attachments) / sizeof(decltype(attachments[0])); + ++x) { + gl::ctx.BindFramebuffer(GL_FRAMEBUFFER, fb[x]); + gl::ctx.DrawBuffers(1, &attachments[x]); #ifndef NDEBUG - auto status = gl::ctx.CheckFramebufferStatus(GL_FRAMEBUFFER); - if (status != GL_FRAMEBUFFER_COMPLETE) { - BOOST_LOG(error) << "Pass "sv << x << ": CheckFramebufferStatus() --> [0x"sv << util::hex(status).to_string_view() << ']'; - return -1; - } + auto status = gl::ctx.CheckFramebufferStatus(GL_FRAMEBUFFER); + if (status != GL_FRAMEBUFFER_COMPLETE) { + BOOST_LOG(error) + << "Pass "sv << x << ": CheckFramebufferStatus() --> [0x"sv + << util::hex(status).to_string_view() << ']'; + return -1; + } #endif - gl::ctx.UseProgram(program[x].handle()); - gl::ctx.Viewport(offsetX / (x + 1), offsetY / (x + 1), out_width / (x + 1), out_height / (x + 1)); - gl::ctx.DrawArrays(GL_TRIANGLES, 0, 3); + gl::ctx.UseProgram(program[x].handle()); + gl::ctx.Viewport(offsetX / (x + 1), offsetY / (x + 1), + out_width / (x + 1), out_height / (x + 1)); + gl::ctx.DrawArrays(GL_TRIANGLES, 0, 3); } gl::ctx.BindTexture(GL_TEXTURE_2D, 0); @@ -877,10 +887,7 @@ namespace egl { gl::ctx.Flush(); return 0; - } +} } // namespace egl -void -free_frame(AVFrame *frame) { - av_frame_free(&frame); -} +void free_frame(AVFrame *frame) { av_frame_free(&frame); } diff --git a/src/platform/linux/graphics.h b/src/platform/linux/graphics.h index ee72c46e..c54c8e66 100644 --- a/src/platform/linux/graphics.h +++ b/src/platform/linux/graphics.h @@ -4,12 +4,12 @@ */ #pragma once -#include -#include - #include #include +#include +#include + #include "misc.h" #include "src/main.h" #include "src/platform/common.h" @@ -19,135 +19,126 @@ #define SUNSHINE_STRINGIFY_HELPER(x) #x #define SUNSHINE_STRINGIFY(x) SUNSHINE_STRINGIFY_HELPER(x) #define gl_drain_errors_helper(x) gl::drain_errors(x) -#define gl_drain_errors gl_drain_errors_helper(__FILE__ ":" SUNSHINE_STRINGIFY(__LINE__)) +#define gl_drain_errors \ + gl_drain_errors_helper(__FILE__ ":" SUNSHINE_STRINGIFY(__LINE__)) -extern "C" int -close(int __fd); +extern "C" int close(int __fd); // X11 Display extern "C" struct _XDisplay; struct AVFrame; -void -free_frame(AVFrame *frame); +void free_frame(AVFrame *frame); using frame_t = util::safe_ptr; namespace gl { - extern GladGLContext ctx; - void - drain_errors(const std::string_view &prefix); +extern GladGLContext ctx; +void drain_errors(const std::string_view &prefix); - class tex_t: public util::buffer_t { +class tex_t : public util::buffer_t { using util::buffer_t::buffer_t; - public: + public: tex_t(tex_t &&) = default; - tex_t & - operator=(tex_t &&) = default; + tex_t &operator=(tex_t &&) = default; ~tex_t(); - static tex_t - make(std::size_t count); - }; + static tex_t make(std::size_t count); +}; - class frame_buf_t: public util::buffer_t { +class frame_buf_t : public util::buffer_t { using util::buffer_t::buffer_t; - public: + public: frame_buf_t(frame_buf_t &&) = default; - frame_buf_t & - operator=(frame_buf_t &&) = default; + frame_buf_t &operator=(frame_buf_t &&) = default; ~frame_buf_t(); - static frame_buf_t - make(std::size_t count); + static frame_buf_t make(std::size_t count); - inline void - bind(std::nullptr_t, std::nullptr_t) { - int x = 0; - for (auto fb : (*this)) { - ctx.BindFramebuffer(GL_FRAMEBUFFER, fb); - ctx.FramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + x, 0, 0); + inline void bind(std::nullptr_t, std::nullptr_t) { + int x = 0; + for (auto fb : (*this)) { + ctx.BindFramebuffer(GL_FRAMEBUFFER, fb); + ctx.FramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + x, 0, + 0); - ++x; - } - return; + ++x; + } + return; } template - void - bind(It it_begin, It it_end) { - using namespace std::literals; - if (std::distance(it_begin, it_end) > size()) { - BOOST_LOG(warning) << "To many elements to bind"sv; - return; - } + void bind(It it_begin, It it_end) { + using namespace std::literals; + if (std::distance(it_begin, it_end) > size()) { + BOOST_LOG(warning) << "To many elements to bind"sv; + return; + } - int x = 0; - std::for_each(it_begin, it_end, [&](auto tex) { - ctx.BindFramebuffer(GL_FRAMEBUFFER, (*this)[x]); - ctx.BindTexture(GL_TEXTURE_2D, tex); + int x = 0; + std::for_each(it_begin, it_end, [&](auto tex) { + ctx.BindFramebuffer(GL_FRAMEBUFFER, (*this)[x]); + ctx.BindTexture(GL_TEXTURE_2D, tex); - ctx.FramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + x, tex, 0); + ctx.FramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + x, + tex, 0); - ++x; - }); + ++x; + }); } /** * Copies a part of the framebuffer to texture */ - void - copy(int id, int texture, int offset_x, int offset_y, int width, int height); - }; + void copy(int id, int texture, int offset_x, int offset_y, int width, + int height); +}; - class shader_t { - KITTY_USING_MOVE_T(shader_internal_t, GLuint, std::numeric_limits::max(), { - if (el != std::numeric_limits::max()) { - ctx.DeleteShader(el); - } - }); +class shader_t { + KITTY_USING_MOVE_T(shader_internal_t, GLuint, + std::numeric_limits::max(), { + if (el != std::numeric_limits::max()) { + ctx.DeleteShader(el); + } + }); - public: - std::string - err_str(); + public: + std::string err_str(); - static util::Either - compile(const std::string_view &source, GLenum type); + static util::Either compile( + const std::string_view &source, GLenum type); - GLuint - handle() const; + GLuint handle() const; - private: + private: shader_internal_t _shader; - }; +}; - class buffer_t { - KITTY_USING_MOVE_T(buffer_internal_t, GLuint, std::numeric_limits::max(), { - if (el != std::numeric_limits::max()) { - ctx.DeleteBuffers(1, &el); - } - }); +class buffer_t { + KITTY_USING_MOVE_T(buffer_internal_t, GLuint, + std::numeric_limits::max(), { + if (el != std::numeric_limits::max()) { + ctx.DeleteBuffers(1, &el); + } + }); - public: - static buffer_t - make(util::buffer_t &&offsets, const char *block, const std::string_view &data); + public: + static buffer_t make(util::buffer_t &&offsets, const char *block, + const std::string_view &data); - GLuint - handle() const; + GLuint handle() const; - const char * - block() const; + const char *block() const; - void - update(const std::string_view &view, std::size_t offset = 0); - void - update(std::string_view *members, std::size_t count, std::size_t offset = 0); + void update(const std::string_view &view, std::size_t offset = 0); + void update(std::string_view *members, std::size_t count, + std::size_t offset = 0); - private: + private: const char *_block; std::size_t _size; @@ -155,62 +146,60 @@ namespace gl { util::buffer_t _offsets; buffer_internal_t _buffer; - }; +}; - class program_t { - KITTY_USING_MOVE_T(program_internal_t, GLuint, std::numeric_limits::max(), { - if (el != std::numeric_limits::max()) { - ctx.DeleteProgram(el); - } - }); +class program_t { + KITTY_USING_MOVE_T(program_internal_t, GLuint, + std::numeric_limits::max(), { + if (el != std::numeric_limits::max()) { + ctx.DeleteProgram(el); + } + }); - public: - std::string - err_str(); + public: + std::string err_str(); - static util::Either - link(const shader_t &vert, const shader_t &frag); + static util::Either link(const shader_t &vert, + const shader_t &frag); - void - bind(const buffer_t &buffer); + void bind(const buffer_t &buffer); - std::optional - uniform(const char *block, std::pair *members, std::size_t count); + std::optional uniform( + const char *block, std::pair *members, + std::size_t count); - GLuint - handle() const; + GLuint handle() const; - private: + private: program_internal_t _program; - }; +}; } // namespace gl namespace gbm { - struct device; - typedef void (*device_destroy_fn)(device *gbm); - typedef device *(*create_device_fn)(int fd); +struct device; +typedef void (*device_destroy_fn)(device *gbm); +typedef device *(*create_device_fn)(int fd); - extern device_destroy_fn device_destroy; - extern create_device_fn create_device; +extern device_destroy_fn device_destroy; +extern create_device_fn create_device; - using gbm_t = util::dyn_safe_ptr; +using gbm_t = util::dyn_safe_ptr; - int - init(); +int init(); } // namespace gbm namespace egl { - using display_t = util::dyn_safe_ptr_v2; +using display_t = util::dyn_safe_ptr_v2; - struct rgb_img_t { +struct rgb_img_t { display_t::pointer display; EGLImage xrgb8; gl::tex_t tex; - }; +}; - struct nv12_img_t { +struct nv12_img_t { display_t::pointer display; EGLImage r8; EGLImage bg88; @@ -218,37 +207,38 @@ namespace egl { gl::tex_t tex; gl::frame_buf_t buf; - // sizeof(va::DRMPRIMESurfaceDescriptor::objects) / sizeof(va::DRMPRIMESurfaceDescriptor::objects[0]); + // sizeof(va::DRMPRIMESurfaceDescriptor::objects) / + // sizeof(va::DRMPRIMESurfaceDescriptor::objects[0]); static constexpr std::size_t num_fds = 4; std::array fds; - }; +}; - KITTY_USING_MOVE_T(rgb_t, rgb_img_t, , { +KITTY_USING_MOVE_T(rgb_t, rgb_img_t, , { if (el.xrgb8) { - eglDestroyImage(el.display, el.xrgb8); + eglDestroyImage(el.display, el.xrgb8); } - }); +}); - KITTY_USING_MOVE_T(nv12_t, nv12_img_t, , { +KITTY_USING_MOVE_T(nv12_t, nv12_img_t, , { if (el.r8) { - eglDestroyImage(el.display, el.r8); + eglDestroyImage(el.display, el.r8); } if (el.bg88) { - eglDestroyImage(el.display, el.bg88); + eglDestroyImage(el.display, el.bg88); } - }); +}); - KITTY_USING_MOVE_T(ctx_t, (std::tuple), , { +KITTY_USING_MOVE_T(ctx_t, (std::tuple), , { TUPLE_2D_REF(disp, ctx, el); if (ctx) { - eglMakeCurrent(disp, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); - eglDestroyContext(disp, ctx); + eglMakeCurrent(disp, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); + eglDestroyContext(disp, ctx); } - }); +}); - struct surface_descriptor_t { +struct surface_descriptor_t { int width; int height; int fds[4]; @@ -256,79 +246,70 @@ namespace egl { std::uint64_t modifier; std::uint32_t pitches[4]; std::uint32_t offsets[4]; - }; +}; - display_t - make_display(std::variant native_display); - std::optional - make_ctx(display_t::pointer display); +display_t make_display( + std::variant + native_display); +std::optional make_ctx(display_t::pointer display); - std::optional - import_source( - display_t::pointer egl_display, - const surface_descriptor_t &xrgb); +std::optional import_source(display_t::pointer egl_display, + const surface_descriptor_t &xrgb); - std::optional - import_target( +std::optional import_target( display_t::pointer egl_display, std::array &&fds, const surface_descriptor_t &r8, const surface_descriptor_t &gr88); - class cursor_t: public platf::img_t { - public: +class cursor_t : public platf::img_t { + public: int x, y; unsigned long serial; std::vector buffer; - }; +}; - // Allow cursor and the underlying image to be kept together - class img_descriptor_t: public cursor_t { - public: - ~img_descriptor_t() { - reset(); - } +// Allow cursor and the underlying image to be kept together +class img_descriptor_t : public cursor_t { + public: + ~img_descriptor_t() { reset(); } - void - reset() { - for (auto x = 0; x < 4; ++x) { - if (sd.fds[x] >= 0) { - close(sd.fds[x]); + void reset() { + for (auto x = 0; x < 4; ++x) { + if (sd.fds[x] >= 0) { + close(sd.fds[x]); - sd.fds[x] = -1; + sd.fds[x] = -1; + } } - } } surface_descriptor_t sd; // Increment sequence when new rgb_t needs to be created std::uint64_t sequence; - }; +}; - class sws_t { - public: - static std::optional - make(int in_width, int in_height, int out_width, int out_heigth, gl::tex_t &&tex); - static std::optional - make(int in_width, int in_height, int out_width, int out_heigth); +class sws_t { + public: + static std::optional make(int in_width, int in_height, int out_width, + int out_heigth, gl::tex_t &&tex); + static std::optional make(int in_width, int in_height, int out_width, + int out_heigth); // Convert the loaded image into the first two framebuffers - int - convert(gl::frame_buf_t &fb); + int convert(gl::frame_buf_t &fb); // Make an area of the image black - int - blank(gl::frame_buf_t &fb, int offsetX, int offsetY, int width, int height); + int blank(gl::frame_buf_t &fb, int offsetX, int offsetY, int width, + int height); - void - load_ram(platf::img_t &img); - void - load_vram(img_descriptor_t &img, int offset_x, int offset_y, int texture); + void load_ram(platf::img_t &img); + void load_vram(img_descriptor_t &img, int offset_x, int offset_y, + int texture); - void - apply_colorspace(const video::sunshine_colorspace_t &colorspace); + void apply_colorspace(const video::sunshine_colorspace_t &colorspace); // The first texture is the monitor image. // The second texture is the cursor image @@ -351,8 +332,7 @@ namespace egl { // Store latest cursor for load_vram std::uint64_t serial; - }; +}; - bool - fail(); +bool fail(); } // namespace egl diff --git a/src/platform/linux/kmsgrab.cpp b/src/platform/linux/kmsgrab.cpp index bfbe3935..3e2ad0c5 100644 --- a/src/platform/linux/kmsgrab.cpp +++ b/src/platform/linux/kmsgrab.cpp @@ -29,34 +29,36 @@ namespace fs = std::filesystem; namespace platf { - namespace kms { +namespace kms { - class cap_sys_admin { - public: - cap_sys_admin() { +class cap_sys_admin { + public: + cap_sys_admin() { caps = cap_get_proc(); cap_value_t sys_admin = CAP_SYS_ADMIN; - if (cap_set_flag(caps, CAP_EFFECTIVE, 1, &sys_admin, CAP_SET) || cap_set_proc(caps)) { - BOOST_LOG(error) << "Failed to gain CAP_SYS_ADMIN"; + if (cap_set_flag(caps, CAP_EFFECTIVE, 1, &sys_admin, CAP_SET) || + cap_set_proc(caps)) { + BOOST_LOG(error) << "Failed to gain CAP_SYS_ADMIN"; } - } + } - ~cap_sys_admin() { + ~cap_sys_admin() { cap_value_t sys_admin = CAP_SYS_ADMIN; - if (cap_set_flag(caps, CAP_EFFECTIVE, 1, &sys_admin, CAP_CLEAR) || cap_set_proc(caps)) { - BOOST_LOG(error) << "Failed to drop CAP_SYS_ADMIN"; + if (cap_set_flag(caps, CAP_EFFECTIVE, 1, &sys_admin, CAP_CLEAR) || + cap_set_proc(caps)) { + BOOST_LOG(error) << "Failed to drop CAP_SYS_ADMIN"; } cap_free(caps); - } + } - cap_t caps; - }; + cap_t caps; +}; - class wrapper_fb { - public: - wrapper_fb(drmModeFB *fb): - fb { fb }, fb_id { fb->fb_id }, width { fb->width }, height { fb->height } { +class wrapper_fb { + public: + wrapper_fb(drmModeFB *fb) + : fb{fb}, fb_id{fb->fb_id}, width{fb->width}, height{fb->height} { pixel_format = DRM_FORMAT_XRGB8888; modifier = DRM_FORMAT_MOD_INVALID; std::fill_n(handles, 4, 0); @@ -64,619 +66,617 @@ namespace platf { std::fill_n(offsets, 4, 0); handles[0] = fb->handle; pitches[0] = fb->pitch; - } + } - wrapper_fb(drmModeFB2 *fb2): - fb2 { fb2 }, fb_id { fb2->fb_id }, width { fb2->width }, height { fb2->height } { + wrapper_fb(drmModeFB2 *fb2) + : fb2{fb2}, fb_id{fb2->fb_id}, width{fb2->width}, height{fb2->height} { pixel_format = fb2->pixel_format; - modifier = (fb2->flags & DRM_MODE_FB_MODIFIERS) ? fb2->modifier : DRM_FORMAT_MOD_INVALID; + modifier = (fb2->flags & DRM_MODE_FB_MODIFIERS) + ? fb2->modifier + : DRM_FORMAT_MOD_INVALID; memcpy(handles, fb2->handles, sizeof(handles)); memcpy(pitches, fb2->pitches, sizeof(pitches)); memcpy(offsets, fb2->offsets, sizeof(offsets)); - } + } - ~wrapper_fb() { + ~wrapper_fb() { if (fb) { - drmModeFreeFB(fb); + drmModeFreeFB(fb); + } else if (fb2) { + drmModeFreeFB2(fb2); } - else if (fb2) { - drmModeFreeFB2(fb2); - } - } + } - drmModeFB *fb = nullptr; - drmModeFB2 *fb2 = nullptr; - uint32_t fb_id; - uint32_t width; - uint32_t height; - uint32_t pixel_format; - uint64_t modifier; - uint32_t handles[4]; - uint32_t pitches[4]; - uint32_t offsets[4]; - }; + drmModeFB *fb = nullptr; + drmModeFB2 *fb2 = nullptr; + uint32_t fb_id; + uint32_t width; + uint32_t height; + uint32_t pixel_format; + uint64_t modifier; + uint32_t handles[4]; + uint32_t pitches[4]; + uint32_t offsets[4]; +}; - using plane_res_t = util::safe_ptr; - using encoder_t = util::safe_ptr; - using res_t = util::safe_ptr; - using plane_t = util::safe_ptr; - using fb_t = std::unique_ptr; - using crtc_t = util::safe_ptr; - using obj_prop_t = util::safe_ptr; - using prop_t = util::safe_ptr; +using plane_res_t = util::safe_ptr; +using encoder_t = util::safe_ptr; +using res_t = util::safe_ptr; +using plane_t = util::safe_ptr; +using fb_t = std::unique_ptr; +using crtc_t = util::safe_ptr; +using obj_prop_t = + util::safe_ptr; +using prop_t = util::safe_ptr; - using conn_type_count_t = std::map; +using conn_type_count_t = std::map; - static int env_width; - static int env_height; +static int env_width; +static int env_height; - std::string_view - plane_type(std::uint64_t val) { - switch (val) { +std::string_view plane_type(std::uint64_t val) { + switch (val) { case DRM_PLANE_TYPE_OVERLAY: - return "DRM_PLANE_TYPE_OVERLAY"sv; + return "DRM_PLANE_TYPE_OVERLAY"sv; case DRM_PLANE_TYPE_PRIMARY: - return "DRM_PLANE_TYPE_PRIMARY"sv; + return "DRM_PLANE_TYPE_PRIMARY"sv; case DRM_PLANE_TYPE_CURSOR: - return "DRM_PLANE_TYPE_CURSOR"sv; - } - - return "UNKNOWN"sv; + return "DRM_PLANE_TYPE_CURSOR"sv; } - struct connector_t { - // For example: HDMI-A or HDMI - std::uint32_t type; + return "UNKNOWN"sv; +} - // Equals zero if not applicable - std::uint32_t crtc_id; +struct connector_t { + // For example: HDMI-A or HDMI + std::uint32_t type; - // For example HDMI-A-{index} or HDMI-{index} - std::uint32_t index; + // Equals zero if not applicable + std::uint32_t crtc_id; - bool connected; - }; + // For example HDMI-A-{index} or HDMI-{index} + std::uint32_t index; - struct monitor_t { - std::uint32_t type; + bool connected; +}; - std::uint32_t index; +struct monitor_t { + std::uint32_t type; - platf::touch_port_t viewport; - }; + std::uint32_t index; - struct card_descriptor_t { - std::string path; + platf::touch_port_t viewport; +}; - std::map crtc_to_monitor; - }; +struct card_descriptor_t { + std::string path; - static std::vector card_descriptors; + std::map crtc_to_monitor; +}; - static std::uint32_t - from_view(const std::string_view &string) { +static std::vector card_descriptors; + +static std::uint32_t from_view(const std::string_view &string) { #define _CONVERT(x, y) \ - if (string == x) return DRM_MODE_CONNECTOR_##y + if (string == x) return DRM_MODE_CONNECTOR_##y - _CONVERT("VGA"sv, VGA); - _CONVERT("DVI-I"sv, DVII); - _CONVERT("DVI-D"sv, DVID); - _CONVERT("DVI-A"sv, DVIA); - _CONVERT("S-Video"sv, SVIDEO); - _CONVERT("LVDS"sv, LVDS); - _CONVERT("DIN"sv, 9PinDIN); - _CONVERT("DisplayPort"sv, DisplayPort); - _CONVERT("DP"sv, DisplayPort); - _CONVERT("HDMI-A"sv, HDMIA); - _CONVERT("HDMI"sv, HDMIA); - _CONVERT("HDMI-B"sv, HDMIB); - _CONVERT("eDP"sv, eDP); - _CONVERT("DSI"sv, DSI); + _CONVERT("VGA"sv, VGA); + _CONVERT("DVI-I"sv, DVII); + _CONVERT("DVI-D"sv, DVID); + _CONVERT("DVI-A"sv, DVIA); + _CONVERT("S-Video"sv, SVIDEO); + _CONVERT("LVDS"sv, LVDS); + _CONVERT("DIN"sv, 9PinDIN); + _CONVERT("DisplayPort"sv, DisplayPort); + _CONVERT("DP"sv, DisplayPort); + _CONVERT("HDMI-A"sv, HDMIA); + _CONVERT("HDMI"sv, HDMIA); + _CONVERT("HDMI-B"sv, HDMIB); + _CONVERT("eDP"sv, eDP); + _CONVERT("DSI"sv, DSI); - BOOST_LOG(error) << "Unknown Monitor connector type ["sv << string << "]: Please report this to the GitHub issue tracker"sv; - return DRM_MODE_CONNECTOR_Unknown; + BOOST_LOG(error) << "Unknown Monitor connector type ["sv << string + << "]: Please report this to the GitHub issue tracker"sv; + return DRM_MODE_CONNECTOR_Unknown; +} + +class plane_it_t + : public round_robin_util::it_wrap_t { + public: + plane_it_t(int fd, std::uint32_t *plane_p, std::uint32_t *end) + : fd{fd}, plane_p{plane_p}, end{end} { + inc(); } - class plane_it_t: public round_robin_util::it_wrap_t { - public: - plane_it_t(int fd, std::uint32_t *plane_p, std::uint32_t *end): - fd { fd }, plane_p { plane_p }, end { end } { - inc(); - } + plane_it_t(int fd, std::uint32_t *end) : fd{fd}, plane_p{end}, end{end} {} - plane_it_t(int fd, std::uint32_t *end): - fd { fd }, plane_p { end }, end { end } {} - - void - inc() { + void inc() { this->plane.reset(); for (; plane_p != end; ++plane_p) { - plane_t plane = drmModeGetPlane(fd, *plane_p); + plane_t plane = drmModeGetPlane(fd, *plane_p); - if (!plane) { - BOOST_LOG(error) << "Couldn't get drm plane ["sv << (end - plane_p) << "]: "sv << strerror(errno); - continue; - } + if (!plane) { + BOOST_LOG(error) + << "Couldn't get drm plane ["sv << (end - plane_p) + << "]: "sv << strerror(errno); + continue; + } - // If this plane is unused - if (plane->fb_id) { - this->plane = util::make_shared(plane.release()); + // If this plane is unused + if (plane->fb_id) { + this->plane = util::make_shared(plane.release()); - // One last increment - ++plane_p; - break; - } + // One last increment + ++plane_p; + break; + } } - } + } - bool - eq(const plane_it_t &other) const { - return plane_p == other.plane_p; - } + bool eq(const plane_it_t &other) const { return plane_p == other.plane_p; } - plane_t::pointer - get() { - return plane.get(); - } + plane_t::pointer get() { return plane.get(); } - int fd; - std::uint32_t *plane_p; - std::uint32_t *end; + int fd; + std::uint32_t *plane_p; + std::uint32_t *end; - util::shared_t plane; - }; + util::shared_t plane; +}; - class card_t { - public: - using connector_interal_t = util::safe_ptr; +class card_t { + public: + using connector_interal_t = + util::safe_ptr; - int - init(const char *path) { + int init(const char *path) { cap_sys_admin admin; fd.el = open(path, O_RDWR); if (fd.el < 0) { - BOOST_LOG(error) << "Couldn't open: "sv << path << ": "sv << strerror(errno); - return -1; + BOOST_LOG(error) + << "Couldn't open: "sv << path << ": "sv << strerror(errno); + return -1; } // Open the render node for this card to share with libva. // If it fails, we'll just share the primary node instead. char *rendernode_path = drmGetRenderDeviceNameFromFd(fd.el); if (rendernode_path) { - BOOST_LOG(debug) << "Opening render node: "sv << rendernode_path; - render_fd.el = open(rendernode_path, O_RDWR); - if (render_fd.el < 0) { - BOOST_LOG(warning) << "Couldn't open render node: "sv << rendernode_path << ": "sv << strerror(errno); + BOOST_LOG(debug) << "Opening render node: "sv << rendernode_path; + render_fd.el = open(rendernode_path, O_RDWR); + if (render_fd.el < 0) { + BOOST_LOG(warning) + << "Couldn't open render node: "sv << rendernode_path + << ": "sv << strerror(errno); + render_fd.el = dup(fd.el); + } + free(rendernode_path); + } else { + BOOST_LOG(warning) << "No render device name for: "sv << path; render_fd.el = dup(fd.el); - } - free(rendernode_path); - } - else { - BOOST_LOG(warning) << "No render device name for: "sv << path; - render_fd.el = dup(fd.el); } if (drmSetClientCap(fd.el, DRM_CLIENT_CAP_UNIVERSAL_PLANES, 1)) { - BOOST_LOG(error) << "Couldn't expose some/all drm planes for card: "sv << path; - return -1; + BOOST_LOG(error) + << "Couldn't expose some/all drm planes for card: "sv << path; + return -1; } if (drmSetClientCap(fd.el, DRM_CLIENT_CAP_ATOMIC, 1)) { - BOOST_LOG(warning) << "Couldn't expose some properties for card: "sv << path; + BOOST_LOG(warning) + << "Couldn't expose some properties for card: "sv << path; } plane_res.reset(drmModeGetPlaneResources(fd.el)); if (!plane_res) { - BOOST_LOG(error) << "Couldn't get drm plane resources"sv; - return -1; + BOOST_LOG(error) << "Couldn't get drm plane resources"sv; + return -1; } return 0; - } + } - fb_t - fb(plane_t::pointer plane) { + fb_t fb(plane_t::pointer plane) { cap_sys_admin admin; auto fb2 = drmModeGetFB2(fd.el, plane->fb_id); if (fb2) { - return std::make_unique(fb2); + return std::make_unique(fb2); } auto fb = drmModeGetFB(fd.el, plane->fb_id); if (fb) { - return std::make_unique(fb); + return std::make_unique(fb); } return nullptr; - } + } - crtc_t - crtc(std::uint32_t id) { - return drmModeGetCrtc(fd.el, id); - } + crtc_t crtc(std::uint32_t id) { return drmModeGetCrtc(fd.el, id); } - encoder_t - encoder(std::uint32_t id) { - return drmModeGetEncoder(fd.el, id); - } + encoder_t encoder(std::uint32_t id) { return drmModeGetEncoder(fd.el, id); } - res_t - res() { - return drmModeGetResources(fd.el); - } + res_t res() { return drmModeGetResources(fd.el); } - bool - is_cursor(std::uint32_t plane_id) { + bool is_cursor(std::uint32_t plane_id) { auto props = plane_props(plane_id); for (auto &[prop, val] : props) { - if (prop->name == "type"sv) { - if (val == DRM_PLANE_TYPE_CURSOR) { - return true; + if (prop->name == "type"sv) { + if (val == DRM_PLANE_TYPE_CURSOR) { + return true; + } else { + return false; + } } - else { - return false; - } - } } return false; - } + } - std::uint32_t - get_panel_orientation(std::uint32_t plane_id) { + std::uint32_t get_panel_orientation(std::uint32_t plane_id) { auto props = plane_props(plane_id); for (auto &[prop, val] : props) { - if (prop->name == "rotation"sv) { - return val; - } + if (prop->name == "rotation"sv) { + return val; + } } - BOOST_LOG(error) << "Failed to determine panel orientation, defaulting to landscape."; + BOOST_LOG(error) << "Failed to determine panel orientation, defaulting " + "to landscape."; return DRM_MODE_ROTATE_0; - } + } - connector_interal_t - connector(std::uint32_t id) { + connector_interal_t connector(std::uint32_t id) { return drmModeGetConnector(fd.el, id); - } + } - std::vector - monitors(conn_type_count_t &conn_type_count) { + std::vector monitors(conn_type_count_t &conn_type_count) { auto resources = res(); if (!resources) { - BOOST_LOG(error) << "Couldn't get connector resources"sv; - return {}; + BOOST_LOG(error) << "Couldn't get connector resources"sv; + return {}; } std::vector monitors; - std::for_each_n(resources->connectors, resources->count_connectors, [this, &conn_type_count, &monitors](std::uint32_t id) { - auto conn = connector(id); + std::for_each_n(resources->connectors, resources->count_connectors, + [this, &conn_type_count, &monitors](std::uint32_t id) { + auto conn = connector(id); - std::uint32_t crtc_id = 0; + std::uint32_t crtc_id = 0; - if (conn->encoder_id) { - auto enc = encoder(conn->encoder_id); - if (enc) { - crtc_id = enc->crtc_id; - } - } + if (conn->encoder_id) { + auto enc = encoder(conn->encoder_id); + if (enc) { + crtc_id = enc->crtc_id; + } + } - auto index = ++conn_type_count[conn->connector_type]; + auto index = + ++conn_type_count[conn->connector_type]; - monitors.emplace_back(connector_t { - conn->connector_type, - crtc_id, - index, - conn->connection == DRM_MODE_CONNECTED, - }); - }); + monitors.emplace_back(connector_t{ + conn->connector_type, + crtc_id, + index, + conn->connection == DRM_MODE_CONNECTED, + }); + }); return monitors; - } + } - file_t - handleFD(std::uint32_t handle) { + file_t handleFD(std::uint32_t handle) { file_t fb_fd; - auto status = drmPrimeHandleToFD(fd.el, handle, 0 /* flags */, &fb_fd.el); + auto status = + drmPrimeHandleToFD(fd.el, handle, 0 /* flags */, &fb_fd.el); if (status) { - return {}; + return {}; } return fb_fd; - } + } - std::vector> - props(std::uint32_t id, std::uint32_t type) { + std::vector> props(std::uint32_t id, + std::uint32_t type) { obj_prop_t obj_prop = drmModeObjectGetProperties(fd.el, id, type); std::vector> props; props.reserve(obj_prop->count_props); for (auto x = 0; x < obj_prop->count_props; ++x) { - props.emplace_back(drmModeGetProperty(fd.el, obj_prop->props[x]), obj_prop->prop_values[x]); + props.emplace_back(drmModeGetProperty(fd.el, obj_prop->props[x]), + obj_prop->prop_values[x]); } return props; - } - - std::vector> - plane_props(std::uint32_t id) { - return props(id, DRM_MODE_OBJECT_PLANE); - } - - std::vector> - crtc_props(std::uint32_t id) { - return props(id, DRM_MODE_OBJECT_CRTC); - } - - std::vector> - connector_props(std::uint32_t id) { - return props(id, DRM_MODE_OBJECT_CONNECTOR); - } - - plane_t - operator[](std::uint32_t index) { - return drmModeGetPlane(fd.el, plane_res->planes[index]); - } - - std::uint32_t - count() { - return plane_res->count_planes; - } - - plane_it_t - begin() const { - return plane_it_t { fd.el, plane_res->planes, plane_res->planes + plane_res->count_planes }; - } - - plane_it_t - end() const { - return plane_it_t { fd.el, plane_res->planes + plane_res->count_planes }; - } - - file_t fd; - file_t render_fd; - plane_res_t plane_res; - }; - - std::map - map_crtc_to_monitor(const std::vector &connectors) { - std::map result; - - for (auto &connector : connectors) { - result.emplace(connector.crtc_id, - monitor_t { - connector.type, - connector.index, - }); - } - - return result; } - struct kms_img_t: public img_t { - ~kms_img_t() override { + std::vector> plane_props( + std::uint32_t id) { + return props(id, DRM_MODE_OBJECT_PLANE); + } + + std::vector> crtc_props(std::uint32_t id) { + return props(id, DRM_MODE_OBJECT_CRTC); + } + + std::vector> connector_props( + std::uint32_t id) { + return props(id, DRM_MODE_OBJECT_CONNECTOR); + } + + plane_t operator[](std::uint32_t index) { + return drmModeGetPlane(fd.el, plane_res->planes[index]); + } + + std::uint32_t count() { return plane_res->count_planes; } + + plane_it_t begin() const { + return plane_it_t{fd.el, plane_res->planes, + plane_res->planes + plane_res->count_planes}; + } + + plane_it_t end() const { + return plane_it_t{fd.el, plane_res->planes + plane_res->count_planes}; + } + + file_t fd; + file_t render_fd; + plane_res_t plane_res; +}; + +std::map map_crtc_to_monitor( + const std::vector &connectors) { + std::map result; + + for (auto &connector : connectors) { + result.emplace(connector.crtc_id, monitor_t{ + connector.type, + connector.index, + }); + } + + return result; +} + +struct kms_img_t : public img_t { + ~kms_img_t() override { delete[] data; data = nullptr; - } - }; + } +}; - void - print(plane_t::pointer plane, fb_t::pointer fb, crtc_t::pointer crtc) { - if (crtc) { +void print(plane_t::pointer plane, fb_t::pointer fb, crtc_t::pointer crtc) { + if (crtc) { BOOST_LOG(debug) << "crtc("sv << crtc->x << ", "sv << crtc->y << ')'; - BOOST_LOG(debug) << "crtc("sv << crtc->width << ", "sv << crtc->height << ')'; - BOOST_LOG(debug) << "plane->possible_crtcs == "sv << plane->possible_crtcs; - } - - BOOST_LOG(debug) - << "x("sv << plane->x - << ") y("sv << plane->y - << ") crtc_x("sv << plane->crtc_x - << ") crtc_y("sv << plane->crtc_y - << ") crtc_id("sv << plane->crtc_id - << ')'; - - BOOST_LOG(debug) - << "Resolution: "sv << fb->width << 'x' << fb->height - << ": Pitch: "sv << fb->pitches[0] - << ": Offset: "sv << fb->offsets[0]; - - std::stringstream ss; - - ss << "Format ["sv; - std::for_each_n(plane->formats, plane->count_formats - 1, [&ss](auto format) { - ss << util::view(format) << ", "sv; - }); - - ss << util::view(plane->formats[plane->count_formats - 1]) << ']'; - - BOOST_LOG(debug) << ss.str(); + BOOST_LOG(debug) << "crtc("sv << crtc->width << ", "sv << crtc->height + << ')'; + BOOST_LOG(debug) << "plane->possible_crtcs == "sv + << plane->possible_crtcs; } - class display_t: public platf::display_t { - public: - display_t(mem_type_e mem_type): - platf::display_t(), mem_type { mem_type } {} + BOOST_LOG(debug) << "x("sv << plane->x << ") y("sv << plane->y + << ") crtc_x("sv << plane->crtc_x << ") crtc_y("sv + << plane->crtc_y << ") crtc_id("sv << plane->crtc_id + << ')'; - int - init(const std::string &display_name, const ::video::config_t &config) { - delay = std::chrono::nanoseconds { 1s } / config.framerate; + BOOST_LOG(debug) << "Resolution: "sv << fb->width << 'x' << fb->height + << ": Pitch: "sv << fb->pitches[0] << ": Offset: "sv + << fb->offsets[0]; + + std::stringstream ss; + + ss << "Format ["sv; + std::for_each_n(plane->formats, plane->count_formats - 1, + [&ss](auto format) { ss << util::view(format) << ", "sv; }); + + ss << util::view(plane->formats[plane->count_formats - 1]) << ']'; + + BOOST_LOG(debug) << ss.str(); +} + +class display_t : public platf::display_t { + public: + display_t(mem_type_e mem_type) : platf::display_t(), mem_type{mem_type} {} + + int init(const std::string &display_name, const ::video::config_t &config) { + delay = std::chrono::nanoseconds{1s} / config.framerate; int monitor_index = util::from_view(display_name); int monitor = 0; - fs::path card_dir { "/dev/dri"sv }; - for (auto &entry : fs::directory_iterator { card_dir }) { - auto file = entry.path().filename(); + fs::path card_dir{"/dev/dri"sv}; + for (auto &entry : fs::directory_iterator{card_dir}) { + auto file = entry.path().filename(); - auto filestring = file.generic_u8string(); - if (filestring.size() < 4 || std::string_view { filestring }.substr(0, 4) != "card"sv) { - continue; - } - - kms::card_t card; - if (card.init(entry.path().c_str())) { - return {}; - } - - auto end = std::end(card); - for (auto plane = std::begin(card); plane != end; ++plane) { - if (card.is_cursor(plane->plane_id)) { - continue; - } - - if (monitor != monitor_index) { - ++monitor; - continue; - } - - auto fb = card.fb(plane.get()); - if (!fb) { - BOOST_LOG(error) << "Couldn't get drm fb for plane ["sv << plane->fb_id << "]: "sv << strerror(errno); - return -1; - } - - if (!fb->handles[0]) { - BOOST_LOG(error) - << "Couldn't get handle for DRM Framebuffer ["sv << plane->fb_id << "]: Possibly not permitted: do [sudo setcap cap_sys_admin+p sunshine]"sv; - return -1; - } - - for (int i = 0; i < 4; ++i) { - if (!fb->handles[i]) { - break; - } - - auto fb_fd = card.handleFD(fb->handles[i]); - if (fb_fd.el < 0) { - BOOST_LOG(error) << "Couldn't get primary file descriptor for Framebuffer ["sv << fb->fb_id << "]: "sv << strerror(errno); + auto filestring = file.generic_u8string(); + if (filestring.size() < 4 || + std::string_view{filestring}.substr(0, 4) != "card"sv) { continue; - } } - BOOST_LOG(info) << "Found monitor for DRM screencasting"sv; - - // We need to find the correct /dev/dri/card{nr} to correlate the crtc_id with the monitor descriptor - auto pos = std::find_if(std::begin(card_descriptors), std::end(card_descriptors), [&](card_descriptor_t &cd) { - return cd.path == filestring; - }); - - if (pos == std::end(card_descriptors)) { - // This code path shouldn't happen, but it's there just in case. - // card_descriptors is part of the guesswork after all. - BOOST_LOG(error) << "Couldn't find ["sv << entry.path() << "]: This shouldn't have happened :/"sv; - return -1; + kms::card_t card; + if (card.init(entry.path().c_str())) { + return {}; } - // TODO: surf_sd = fb->to_sd(); + auto end = std::end(card); + for (auto plane = std::begin(card); plane != end; ++plane) { + if (card.is_cursor(plane->plane_id)) { + continue; + } - auto crct = card.crtc(plane->crtc_id); - kms::print(plane.get(), fb.get(), crct.get()); + if (monitor != monitor_index) { + ++monitor; + continue; + } - img_width = fb->width; - img_height = fb->height; - img_offset_x = crct->x; - img_offset_y = crct->y; + auto fb = card.fb(plane.get()); + if (!fb) { + BOOST_LOG(error) + << "Couldn't get drm fb for plane ["sv << plane->fb_id + << "]: "sv << strerror(errno); + return -1; + } - this->env_width = ::platf::kms::env_width; - this->env_height = ::platf::kms::env_height; + if (!fb->handles[0]) { + BOOST_LOG(error) + << "Couldn't get handle for DRM Framebuffer ["sv + << plane->fb_id + << "]: Possibly not permitted: do [sudo setcap cap_sys_admin+p sunshine]"sv; + return -1; + } - auto monitor = pos->crtc_to_monitor.find(plane->crtc_id); - if (monitor != std::end(pos->crtc_to_monitor)) { - auto &viewport = monitor->second.viewport; + for (int i = 0; i < 4; ++i) { + if (!fb->handles[i]) { + break; + } - width = viewport.width; - height = viewport.height; + auto fb_fd = card.handleFD(fb->handles[i]); + if (fb_fd.el < 0) { + BOOST_LOG(error) + << "Couldn't get primary file descriptor for Framebuffer ["sv + << fb->fb_id << "]: "sv << strerror(errno); + continue; + } + } - switch (card.get_panel_orientation(plane->plane_id)) { - case DRM_MODE_ROTATE_270: - BOOST_LOG(debug) << "Detected panel orientation at 90, swapping width and height."; - width = viewport.height; - height = viewport.width; - break; - case DRM_MODE_ROTATE_90: - case DRM_MODE_ROTATE_180: - BOOST_LOG(warning) << "Panel orientation is unsupported, screen capture may not work correctly."; - break; - } + BOOST_LOG(info) << "Found monitor for DRM screencasting"sv; - offset_x = viewport.offset_x; - offset_y = viewport.offset_y; + // We need to find the correct /dev/dri/card{nr} to correlate + // the crtc_id with the monitor descriptor + auto pos = std::find_if(std::begin(card_descriptors), + std::end(card_descriptors), + [&](card_descriptor_t &cd) { + return cd.path == filestring; + }); + + if (pos == std::end(card_descriptors)) { + // This code path shouldn't happen, but it's there just in + // case. card_descriptors is part of the guesswork after + // all. + BOOST_LOG(error) << "Couldn't find ["sv << entry.path() + << "]: This shouldn't have happened :/"sv; + return -1; + } + + // TODO: surf_sd = fb->to_sd(); + + auto crct = card.crtc(plane->crtc_id); + kms::print(plane.get(), fb.get(), crct.get()); + + img_width = fb->width; + img_height = fb->height; + img_offset_x = crct->x; + img_offset_y = crct->y; + + this->env_width = ::platf::kms::env_width; + this->env_height = ::platf::kms::env_height; + + auto monitor = pos->crtc_to_monitor.find(plane->crtc_id); + if (monitor != std::end(pos->crtc_to_monitor)) { + auto &viewport = monitor->second.viewport; + + width = viewport.width; + height = viewport.height; + + switch (card.get_panel_orientation(plane->plane_id)) { + case DRM_MODE_ROTATE_270: + BOOST_LOG(debug) + << "Detected panel orientation at 90, swapping " + "width and height."; + width = viewport.height; + height = viewport.width; + break; + case DRM_MODE_ROTATE_90: + case DRM_MODE_ROTATE_180: + BOOST_LOG(warning) + << "Panel orientation is unsupported, screen " + "capture may not work correctly."; + break; + } + + offset_x = viewport.offset_x; + offset_y = viewport.offset_y; + } + + // This code path shouldn't happen, but it's there just in case. + // crtc_to_monitor is part of the guesswork after all. + else { + BOOST_LOG(warning) + << "Couldn't find crtc_id, this shouldn't have happened :\\"sv; + width = crct->width; + height = crct->height; + offset_x = crct->x; + offset_y = crct->y; + } + + this->card = std::move(card); + + plane_id = plane->plane_id; + + goto break_loop; } - - // This code path shouldn't happen, but it's there just in case. - // crtc_to_monitor is part of the guesswork after all. - else { - BOOST_LOG(warning) << "Couldn't find crtc_id, this shouldn't have happened :\\"sv; - width = crct->width; - height = crct->height; - offset_x = crct->x; - offset_y = crct->y; - } - - this->card = std::move(card); - - plane_id = plane->plane_id; - - goto break_loop; - } } - // Neatly break from nested for loop - break_loop: + // Neatly break from nested for loop + break_loop: if (monitor != monitor_index) { - BOOST_LOG(error) << "Couldn't find monitor ["sv << monitor_index << ']'; + BOOST_LOG(error) + << "Couldn't find monitor ["sv << monitor_index << ']'; - return -1; + return -1; } cursor_opt = x11::cursor_t::make(); return 0; - } + } - inline capture_e - refresh(file_t *file, egl::surface_descriptor_t *sd) { + inline capture_e refresh(file_t *file, egl::surface_descriptor_t *sd) { plane_t plane = drmModeGetPlane(card.fd.el, plane_id); auto fb = card.fb(plane.get()); if (!fb) { - BOOST_LOG(error) << "Couldn't get drm fb for plane ["sv << plane->fb_id << "]: "sv << strerror(errno); - return capture_e::error; + BOOST_LOG(error) << "Couldn't get drm fb for plane ["sv + << plane->fb_id << "]: "sv << strerror(errno); + return capture_e::error; } if (!fb->handles[0]) { - BOOST_LOG(error) - << "Couldn't get handle for DRM Framebuffer ["sv << plane->fb_id << "]: Possibly not permitted: do [sudo setcap cap_sys_admin+p sunshine]"sv; - return capture_e::error; + BOOST_LOG(error) + << "Couldn't get handle for DRM Framebuffer ["sv << plane->fb_id + << "]: Possibly not permitted: do [sudo setcap cap_sys_admin+p sunshine]"sv; + return capture_e::error; } for (int y = 0; y < 4; ++y) { - if (!fb->handles[y]) { - // setting sd->fds[y] to a negative value indicates that sd->offsets[y] and sd->pitches[y] - // are uninitialized and contain invalid values. - sd->fds[y] = -1; - // It's not clear whether there could still be valid handles left. - // So, continue anyway. - // TODO: Is this redundant? - continue; - } + if (!fb->handles[y]) { + // setting sd->fds[y] to a negative value indicates that + // sd->offsets[y] and sd->pitches[y] are uninitialized and + // contain invalid values. + sd->fds[y] = -1; + // It's not clear whether there could still be valid handles + // left. So, continue anyway. + // TODO: Is this redundant? + continue; + } - file[y] = card.handleFD(fb->handles[y]); - if (file[y].el < 0) { - BOOST_LOG(error) << "Couldn't get primary file descriptor for Framebuffer ["sv << fb->fb_id << "]: "sv << strerror(errno); - return capture_e::error; - } + file[y] = card.handleFD(fb->handles[y]); + if (file[y].el < 0) { + BOOST_LOG(error) + << "Couldn't get primary file descriptor for Framebuffer ["sv + << fb->fb_id << "]: "sv << strerror(errno); + return capture_e::error; + } - sd->fds[y] = file[y].el; - sd->offsets[y] = fb->offsets[y]; - sd->pitches[y] = fb->pitches[y]; + sd->fds[y] = file[y].el; + sd->offsets[y] = fb->offsets[y]; + sd->pitches[y] = fb->pitches[y]; } sd->width = fb->width; @@ -684,132 +684,134 @@ namespace platf { sd->modifier = fb->modifier; sd->fourcc = fb->pixel_format; - if ( - fb->width != img_width || - fb->height != img_height) { - return capture_e::reinit; + if (fb->width != img_width || fb->height != img_height) { + return capture_e::reinit; } return capture_e::ok; - } + } - mem_type_e mem_type; + mem_type_e mem_type; - std::chrono::nanoseconds delay; + std::chrono::nanoseconds delay; - int img_width, img_height; - int img_offset_x, img_offset_y; + int img_width, img_height; + int img_offset_x, img_offset_y; - int plane_id; + int plane_id; - card_t card; + card_t card; - std::optional cursor_opt; - }; + std::optional cursor_opt; +}; - class display_ram_t: public display_t { - public: - display_ram_t(mem_type_e mem_type): - display_t(mem_type) {} +class display_ram_t : public display_t { + public: + display_ram_t(mem_type_e mem_type) : display_t(mem_type) {} - int - init(const std::string &display_name, const ::video::config_t &config) { + int init(const std::string &display_name, const ::video::config_t &config) { if (!gbm::create_device) { - BOOST_LOG(warning) << "libgbm not initialized"sv; - return -1; + BOOST_LOG(warning) << "libgbm not initialized"sv; + return -1; } if (display_t::init(display_name, config)) { - return -1; + return -1; } gbm.reset(gbm::create_device(card.fd.el)); if (!gbm) { - BOOST_LOG(error) << "Couldn't create GBM device: ["sv << util::hex(eglGetError()).to_string_view() << ']'; - return -1; + BOOST_LOG(error) + << "Couldn't create GBM device: ["sv + << util::hex(eglGetError()).to_string_view() << ']'; + return -1; } display = egl::make_display(gbm.get()); if (!display) { - return -1; + return -1; } auto ctx_opt = egl::make_ctx(display.get()); if (!ctx_opt) { - return -1; + return -1; } ctx = std::move(*ctx_opt); return 0; - } + } - capture_e - capture(const push_captured_image_cb_t &push_captured_image_cb, const pull_free_image_cb_t &pull_free_image_cb, bool *cursor) override { + capture_e capture(const push_captured_image_cb_t &push_captured_image_cb, + const pull_free_image_cb_t &pull_free_image_cb, + bool *cursor) override { auto next_frame = std::chrono::steady_clock::now(); while (true) { - auto now = std::chrono::steady_clock::now(); + auto now = std::chrono::steady_clock::now(); - if (next_frame > now) { - std::this_thread::sleep_for((next_frame - now) / 3 * 2); - } - while (next_frame > now) { - std::this_thread::sleep_for(1ns); - now = std::chrono::steady_clock::now(); - } - next_frame = now + delay; + if (next_frame > now) { + std::this_thread::sleep_for((next_frame - now) / 3 * 2); + } + while (next_frame > now) { + std::this_thread::sleep_for(1ns); + now = std::chrono::steady_clock::now(); + } + next_frame = now + delay; - std::shared_ptr img_out; - auto status = snapshot(pull_free_image_cb, img_out, 1000ms, *cursor); - switch (status) { - case platf::capture_e::reinit: - case platf::capture_e::error: - case platf::capture_e::interrupted: - return status; - case platf::capture_e::timeout: - if (!push_captured_image_cb(std::move(img_out), false)) { - return platf::capture_e::ok; - } - break; - case platf::capture_e::ok: - if (!push_captured_image_cb(std::move(img_out), true)) { - return platf::capture_e::ok; - } - break; - default: - BOOST_LOG(error) << "Unrecognized capture status ["sv << (int) status << ']'; - return status; - } + std::shared_ptr img_out; + auto status = + snapshot(pull_free_image_cb, img_out, 1000ms, *cursor); + switch (status) { + case platf::capture_e::reinit: + case platf::capture_e::error: + case platf::capture_e::interrupted: + return status; + case platf::capture_e::timeout: + if (!push_captured_image_cb(std::move(img_out), false)) { + return platf::capture_e::ok; + } + break; + case platf::capture_e::ok: + if (!push_captured_image_cb(std::move(img_out), true)) { + return platf::capture_e::ok; + } + break; + default: + BOOST_LOG(error) << "Unrecognized capture status ["sv + << (int)status << ']'; + return status; + } } return capture_e::ok; - } + } - std::unique_ptr - make_avcodec_encode_device(pix_fmt_e pix_fmt) override { + std::unique_ptr make_avcodec_encode_device( + pix_fmt_e pix_fmt) override { if (mem_type == mem_type_e::vaapi) { - return va::make_avcodec_encode_device(width, height, false); + return va::make_avcodec_encode_device(width, height, false); } return std::make_unique(); - } + } - capture_e - snapshot(const pull_free_image_cb_t &pull_free_image_cb, std::shared_ptr &img_out, std::chrono::milliseconds timeout, bool cursor) { + capture_e snapshot(const pull_free_image_cb_t &pull_free_image_cb, + std::shared_ptr &img_out, + std::chrono::milliseconds timeout, bool cursor) { file_t fb_fd[4]; egl::surface_descriptor_t sd; auto status = refresh(fb_fd, &sd); if (status != capture_e::ok) { - return status; + return status; } auto rgb_opt = egl::import_source(display.get(), sd); if (!rgb_opt) { - return capture_e::error; + return capture_e::error; } auto &rgb = *rgb_opt; @@ -822,20 +824,22 @@ namespace platf { BOOST_LOG(debug) << "width and height: w "sv << w << " h "sv << h; if (!pull_free_image_cb(img_out)) { - return platf::capture_e::interrupted; + return platf::capture_e::interrupted; } - gl::ctx.GetTextureSubImage(rgb->tex[0], 0, img_offset_x, img_offset_y, 0, width, height, 1, GL_BGRA, GL_UNSIGNED_BYTE, img_out->height * img_out->row_pitch, img_out->data); + gl::ctx.GetTextureSubImage( + rgb->tex[0], 0, img_offset_x, img_offset_y, 0, width, height, 1, + GL_BGRA, GL_UNSIGNED_BYTE, img_out->height * img_out->row_pitch, + img_out->data); if (cursor_opt && cursor) { - cursor_opt->blend(*img_out, img_offset_x, img_offset_y); + cursor_opt->blend(*img_out, img_offset_x, img_offset_y); } return capture_e::ok; - } + } - std::shared_ptr - alloc_img() override { + std::shared_ptr alloc_img() override { auto img = std::make_shared(); img->width = width; img->height = height; @@ -844,35 +848,34 @@ namespace platf { img->data = new std::uint8_t[height * img->row_pitch]; return img; - } + } - int - dummy_img(platf::img_t *img) override { - return 0; - } + int dummy_img(platf::img_t *img) override { return 0; } - gbm::gbm_t gbm; - egl::display_t display; - egl::ctx_t ctx; - }; + gbm::gbm_t gbm; + egl::display_t display; + egl::ctx_t ctx; +}; - class display_vram_t: public display_t { - public: - display_vram_t(mem_type_e mem_type): - display_t(mem_type) {} +class display_vram_t : public display_t { + public: + display_vram_t(mem_type_e mem_type) : display_t(mem_type) {} - std::unique_ptr - make_avcodec_encode_device(pix_fmt_e pix_fmt) override { + std::unique_ptr make_avcodec_encode_device( + pix_fmt_e pix_fmt) override { if (mem_type == mem_type_e::vaapi) { - return va::make_avcodec_encode_device(width, height, dup(card.render_fd.el), img_offset_x, img_offset_y, true); + return va::make_avcodec_encode_device( + width, height, dup(card.render_fd.el), img_offset_x, + img_offset_y, true); } - BOOST_LOG(error) << "Unsupported pixel format for egl::display_vram_t: "sv << platf::from_pix_fmt(pix_fmt); + BOOST_LOG(error) + << "Unsupported pixel format for egl::display_vram_t: "sv + << platf::from_pix_fmt(pix_fmt); return nullptr; - } + } - std::shared_ptr - alloc_img() override { + std::shared_ptr alloc_img() override { auto img = std::make_shared(); img->serial = std::numeric_limitsserial)>::max(); @@ -883,88 +886,93 @@ namespace platf { std::fill_n(img->sd.fds, 4, -1); return img; - } + } - int - dummy_img(platf::img_t *img) override { + int dummy_img(platf::img_t *img) override { // TODO: stop cheating and give black image if (!img) { - return -1; + return -1; }; - auto pull_dummy_img_callback = [&img](std::shared_ptr &img_out) -> bool { - img_out = img->shared_from_this(); - return true; + auto pull_dummy_img_callback = + [&img](std::shared_ptr &img_out) -> bool { + img_out = img->shared_from_this(); + return true; }; std::shared_ptr img_out; - return snapshot(pull_dummy_img_callback, img_out, 1s, false) != platf::capture_e::ok; - } + return snapshot(pull_dummy_img_callback, img_out, 1s, false) != + platf::capture_e::ok; + } - capture_e - capture(const push_captured_image_cb_t &push_captured_image_cb, const pull_free_image_cb_t &pull_free_image_cb, bool *cursor) { + capture_e capture(const push_captured_image_cb_t &push_captured_image_cb, + const pull_free_image_cb_t &pull_free_image_cb, + bool *cursor) { auto next_frame = std::chrono::steady_clock::now(); while (true) { - auto now = std::chrono::steady_clock::now(); + auto now = std::chrono::steady_clock::now(); - if (next_frame > now) { - std::this_thread::sleep_for((next_frame - now) / 3 * 2); - } - while (next_frame > now) { - std::this_thread::sleep_for(1ns); - now = std::chrono::steady_clock::now(); - } - next_frame = now + delay; + if (next_frame > now) { + std::this_thread::sleep_for((next_frame - now) / 3 * 2); + } + while (next_frame > now) { + std::this_thread::sleep_for(1ns); + now = std::chrono::steady_clock::now(); + } + next_frame = now + delay; - std::shared_ptr img_out; - auto status = snapshot(pull_free_image_cb, img_out, 1000ms, *cursor); - switch (status) { - case platf::capture_e::reinit: - case platf::capture_e::error: - case platf::capture_e::interrupted: - return status; - case platf::capture_e::timeout: - if (!push_captured_image_cb(std::move(img_out), false)) { - return platf::capture_e::ok; - } - break; - case platf::capture_e::ok: - if (!push_captured_image_cb(std::move(img_out), true)) { - return platf::capture_e::ok; - } - break; - default: - BOOST_LOG(error) << "Unrecognized capture status ["sv << (int) status << ']'; - return status; - } + std::shared_ptr img_out; + auto status = + snapshot(pull_free_image_cb, img_out, 1000ms, *cursor); + switch (status) { + case platf::capture_e::reinit: + case platf::capture_e::error: + case platf::capture_e::interrupted: + return status; + case platf::capture_e::timeout: + if (!push_captured_image_cb(std::move(img_out), false)) { + return platf::capture_e::ok; + } + break; + case platf::capture_e::ok: + if (!push_captured_image_cb(std::move(img_out), true)) { + return platf::capture_e::ok; + } + break; + default: + BOOST_LOG(error) << "Unrecognized capture status ["sv + << (int)status << ']'; + return status; + } } return capture_e::ok; - } + } - capture_e - snapshot(const pull_free_image_cb_t &pull_free_image_cb, std::shared_ptr &img_out, std::chrono::milliseconds /* timeout */, bool cursor) { + capture_e snapshot(const pull_free_image_cb_t &pull_free_image_cb, + std::shared_ptr &img_out, + std::chrono::milliseconds /* timeout */, bool cursor) { file_t fb_fd[4]; if (!pull_free_image_cb(img_out)) { - return platf::capture_e::interrupted; + return platf::capture_e::interrupted; } - auto img = (egl::img_descriptor_t *) img_out.get(); + auto img = (egl::img_descriptor_t *)img_out.get(); img->reset(); auto status = refresh(fb_fd, &img->sd); if (status != capture_e::ok) { - return status; + return status; } img->sequence = ++sequence; if (!cursor || !cursor_opt) { - img->data = nullptr; + img->data = nullptr; - for (auto x = 0; x < 4; ++x) { - fb_fd[x].release(); - } - return capture_e::ok; + for (auto x = 0; x < 4; ++x) { + fb_fd[x].release(); + } + return capture_e::ok; } cursor_opt->capture(*img); @@ -973,127 +981,135 @@ namespace platf { img->y -= offset_y; for (auto x = 0; x < 4; ++x) { - fb_fd[x].release(); + fb_fd[x].release(); } return capture_e::ok; - } + } - int - init(const std::string &display_name, const ::video::config_t &config) { + int init(const std::string &display_name, const ::video::config_t &config) { if (display_t::init(display_name, config)) { - return -1; + return -1; } if (!va::validate(card.render_fd.el)) { - BOOST_LOG(warning) << "Monitor "sv << display_name << " doesn't support hardware encoding. Reverting back to GPU -> RAM -> GPU"sv; - return -1; + BOOST_LOG(warning) + << "Monitor "sv << display_name + << " doesn't support hardware encoding. Reverting back to GPU -> RAM -> GPU"sv; + return -1; } sequence = 0; return 0; - } + } - std::uint64_t sequence; - }; + std::uint64_t sequence; +}; - } // namespace kms +} // namespace kms - std::shared_ptr - kms_display(mem_type_e hwdevice_type, const std::string &display_name, const ::video::config_t &config) { +std::shared_ptr kms_display(mem_type_e hwdevice_type, + const std::string &display_name, + const ::video::config_t &config) { if (hwdevice_type == mem_type_e::vaapi) { - auto disp = std::make_shared(hwdevice_type); + auto disp = std::make_shared(hwdevice_type); - if (!disp->init(display_name, config)) { - return disp; - } + if (!disp->init(display_name, config)) { + return disp; + } - // In the case of failure, attempt the old method for VAAPI + // In the case of failure, attempt the old method for VAAPI } auto disp = std::make_shared(hwdevice_type); if (disp->init(display_name, config)) { - return nullptr; + return nullptr; } return disp; - } +} - /** - * On Wayland, it's not possible to determine the position of the monitor on the desktop with KMS. - * Wayland does allow applications to query attached monitors on the desktop, - * however, the naming scheme is not standardized across implementations. - * - * As a result, correlating the KMS output to the wayland outputs is guess work at best. - * But, it's necessary for absolute mouse coordinates to work. - * - * This is an ugly hack :( - */ - void - correlate_to_wayland(std::vector &cds) { +/** + * On Wayland, it's not possible to determine the position of the monitor on the + * desktop with KMS. Wayland does allow applications to query attached monitors + * on the desktop, however, the naming scheme is not standardized across + * implementations. + * + * As a result, correlating the KMS output to the wayland outputs is guess work + * at best. But, it's necessary for absolute mouse coordinates to work. + * + * This is an ugly hack :( + */ +void correlate_to_wayland(std::vector &cds) { auto monitors = wl::monitors(); for (auto &monitor : monitors) { - std::string_view name = monitor->name; + std::string_view name = monitor->name; - BOOST_LOG(info) << name << ": "sv << monitor->description; + BOOST_LOG(info) << name << ": "sv << monitor->description; - // Try to convert names in the format: - // {type}-{index} - // {index} is n'th occurrence of {type} - auto index_begin = name.find_last_of('-'); + // Try to convert names in the format: + // {type}-{index} + // {index} is n'th occurrence of {type} + auto index_begin = name.find_last_of('-'); - std::uint32_t index; - if (index_begin == std::string_view::npos) { - index = 1; - } - else { - index = std::max(1, util::from_view(name.substr(index_begin + 1))); - } - - auto type = kms::from_view(name.substr(0, index_begin)); - - for (auto &card_descriptor : cds) { - for (auto &[_, monitor_descriptor] : card_descriptor.crtc_to_monitor) { - if (monitor_descriptor.index == index && monitor_descriptor.type == type) { - monitor_descriptor.viewport.offset_x = monitor->viewport.offset_x; - monitor_descriptor.viewport.offset_y = monitor->viewport.offset_y; - - // A sanity check, it's guesswork after all. - if ( - monitor_descriptor.viewport.width != monitor->viewport.width || - monitor_descriptor.viewport.height != monitor->viewport.height) { - BOOST_LOG(warning) - << "Mismatch on expected Resolution compared to actual resolution: "sv - << monitor_descriptor.viewport.width << 'x' << monitor_descriptor.viewport.height - << " vs "sv - << monitor->viewport.width << 'x' << monitor->viewport.height; - } - - goto break_for_loop; - } + std::uint32_t index; + if (index_begin == std::string_view::npos) { + index = 1; + } else { + index = std::max( + 1, util::from_view(name.substr(index_begin + 1))); + } + + auto type = kms::from_view(name.substr(0, index_begin)); + + for (auto &card_descriptor : cds) { + for (auto &[_, monitor_descriptor] : + card_descriptor.crtc_to_monitor) { + if (monitor_descriptor.index == index && + monitor_descriptor.type == type) { + monitor_descriptor.viewport.offset_x = + monitor->viewport.offset_x; + monitor_descriptor.viewport.offset_y = + monitor->viewport.offset_y; + + // A sanity check, it's guesswork after all. + if (monitor_descriptor.viewport.width != + monitor->viewport.width || + monitor_descriptor.viewport.height != + monitor->viewport.height) { + BOOST_LOG(warning) + << "Mismatch on expected Resolution compared to actual resolution: "sv + << monitor_descriptor.viewport.width << 'x' + << monitor_descriptor.viewport.height << " vs "sv + << monitor->viewport.width << 'x' + << monitor->viewport.height; + } + + goto break_for_loop; + } + } } - } break_for_loop: - BOOST_LOG(verbose) << "Reduced to name: "sv << name << ": "sv << index; + BOOST_LOG(verbose) << "Reduced to name: "sv << name << ": "sv << index; } - } +} - // A list of names of displays accepted as display_name - std::vector - kms_display_names() { +// A list of names of displays accepted as display_name +std::vector kms_display_names() { int count = 0; if (!fs::exists("/dev/dri")) { - BOOST_LOG(warning) << "Couldn't find /dev/dri, kmsgrab won't be enabled"sv; - return {}; + BOOST_LOG(warning) + << "Couldn't find /dev/dri, kmsgrab won't be enabled"sv; + return {}; } if (!gbm::create_device) { - BOOST_LOG(warning) << "libgbm not initialized"sv; - return {}; + BOOST_LOG(warning) << "libgbm not initialized"sv; + return {}; } kms::conn_type_count_t conn_type_count; @@ -1101,73 +1117,80 @@ namespace platf { std::vector cds; std::vector display_names; - fs::path card_dir { "/dev/dri"sv }; - for (auto &entry : fs::directory_iterator { card_dir }) { - auto file = entry.path().filename(); + fs::path card_dir{"/dev/dri"sv}; + for (auto &entry : fs::directory_iterator{card_dir}) { + auto file = entry.path().filename(); - auto filestring = file.generic_u8string(); - if (std::string_view { filestring }.substr(0, 4) != "card"sv) { - continue; - } - - kms::card_t card; - if (card.init(entry.path().c_str())) { - return {}; - } - - auto crtc_to_monitor = kms::map_crtc_to_monitor(card.monitors(conn_type_count)); - - auto end = std::end(card); - for (auto plane = std::begin(card); plane != end; ++plane) { - auto fb = card.fb(plane.get()); - if (!fb) { - BOOST_LOG(error) << "Couldn't get drm fb for plane ["sv << plane->fb_id << "]: "sv << strerror(errno); - continue; + auto filestring = file.generic_u8string(); + if (std::string_view{filestring}.substr(0, 4) != "card"sv) { + continue; } - if (!fb->handles[0]) { - BOOST_LOG(error) - << "Couldn't get handle for DRM Framebuffer ["sv << plane->fb_id << "]: Possibly not permitted: do [sudo setcap cap_sys_admin+p sunshine]"sv; - break; + kms::card_t card; + if (card.init(entry.path().c_str())) { + return {}; } - if (card.is_cursor(plane->plane_id)) { - continue; + auto crtc_to_monitor = + kms::map_crtc_to_monitor(card.monitors(conn_type_count)); + + auto end = std::end(card); + for (auto plane = std::begin(card); plane != end; ++plane) { + auto fb = card.fb(plane.get()); + if (!fb) { + BOOST_LOG(error) << "Couldn't get drm fb for plane ["sv + << plane->fb_id << "]: "sv << strerror(errno); + continue; + } + + if (!fb->handles[0]) { + BOOST_LOG(error) + << "Couldn't get handle for DRM Framebuffer ["sv + << plane->fb_id + << "]: Possibly not permitted: do [sudo setcap cap_sys_admin+p sunshine]"sv; + break; + } + + if (card.is_cursor(plane->plane_id)) { + continue; + } + + // This appears to return the offset of the monitor + auto crtc = card.crtc(plane->crtc_id); + if (!crtc) { + BOOST_LOG(error) + << "Couldn't get crtc info: "sv << strerror(errno); + return {}; + } + + auto it = crtc_to_monitor.find(plane->crtc_id); + if (it != std::end(crtc_to_monitor)) { + it->second.viewport = platf::touch_port_t{ + (int)crtc->x, + (int)crtc->y, + (int)crtc->width, + (int)crtc->height, + }; + } + + kms::env_width = + std::max(kms::env_width, (int)(crtc->x + crtc->width)); + kms::env_height = + std::max(kms::env_height, (int)(crtc->y + crtc->height)); + + kms::print(plane.get(), fb.get(), crtc.get()); + + display_names.emplace_back(std::to_string(count++)); } - // This appears to return the offset of the monitor - auto crtc = card.crtc(plane->crtc_id); - if (!crtc) { - BOOST_LOG(error) << "Couldn't get crtc info: "sv << strerror(errno); - return {}; - } - - auto it = crtc_to_monitor.find(plane->crtc_id); - if (it != std::end(crtc_to_monitor)) { - it->second.viewport = platf::touch_port_t { - (int) crtc->x, - (int) crtc->y, - (int) crtc->width, - (int) crtc->height, - }; - } - - kms::env_width = std::max(kms::env_width, (int) (crtc->x + crtc->width)); - kms::env_height = std::max(kms::env_height, (int) (crtc->y + crtc->height)); - - kms::print(plane.get(), fb.get(), crtc.get()); - - display_names.emplace_back(std::to_string(count++)); - } - - cds.emplace_back(kms::card_descriptor_t { - std::move(file), - std::move(crtc_to_monitor), - }); + cds.emplace_back(kms::card_descriptor_t{ + std::move(file), + std::move(crtc_to_monitor), + }); } if (!wl::init()) { - correlate_to_wayland(cds); + correlate_to_wayland(cds); } // Deduce the full virtual desktop size @@ -1175,21 +1198,30 @@ namespace platf { kms::env_height = 0; for (auto &card_descriptor : cds) { - for (auto &[_, monitor_descriptor] : card_descriptor.crtc_to_monitor) { - BOOST_LOG(debug) << "Monitor description"sv; - BOOST_LOG(debug) << "Resolution: "sv << monitor_descriptor.viewport.width << 'x' << monitor_descriptor.viewport.height; - BOOST_LOG(debug) << "Offset: "sv << monitor_descriptor.viewport.offset_x << 'x' << monitor_descriptor.viewport.offset_y; + for (auto &[_, monitor_descriptor] : card_descriptor.crtc_to_monitor) { + BOOST_LOG(debug) << "Monitor description"sv; + BOOST_LOG(debug) + << "Resolution: "sv << monitor_descriptor.viewport.width << 'x' + << monitor_descriptor.viewport.height; + BOOST_LOG(debug) + << "Offset: "sv << monitor_descriptor.viewport.offset_x << 'x' + << monitor_descriptor.viewport.offset_y; - kms::env_width = std::max(kms::env_width, (int) (monitor_descriptor.viewport.offset_x + monitor_descriptor.viewport.width)); - kms::env_height = std::max(kms::env_height, (int) (monitor_descriptor.viewport.offset_y + monitor_descriptor.viewport.height)); - } + kms::env_width = std::max( + kms::env_width, (int)(monitor_descriptor.viewport.offset_x + + monitor_descriptor.viewport.width)); + kms::env_height = std::max( + kms::env_height, (int)(monitor_descriptor.viewport.offset_y + + monitor_descriptor.viewport.height)); + } } - BOOST_LOG(debug) << "Desktop resolution: "sv << kms::env_width << 'x' << kms::env_height; + BOOST_LOG(debug) << "Desktop resolution: "sv << kms::env_width << 'x' + << kms::env_height; kms::card_descriptors = std::move(cds); return display_names; - } +} } // namespace platf diff --git a/src/platform/linux/misc.cpp b/src/platform/linux/misc.cpp index d63b3045..ed2f99f0 100644 --- a/src/platform/linux/misc.cpp +++ b/src/platform/linux/misc.cpp @@ -5,7 +5,7 @@ // Required for in6_pktinfo with glibc headers #ifndef _GNU_SOURCE - #define _GNU_SOURCE 1 +#define _GNU_SOURCE 1 #endif // standard includes @@ -13,8 +13,6 @@ // lib includes #include -#include -#include #include #include #include @@ -22,6 +20,9 @@ #include #include +#include +#include + // local includes #include "graphics.h" #include "misc.h" @@ -31,9 +32,9 @@ #include "vaapi.h" #ifdef __GNUC__ - #define SUNSHINE_GNUC_EXTENSION __extension__ +#define SUNSHINE_GNUC_EXTENSION __extension__ #else - #define SUNSHINE_GNUC_EXTENSION +#define SUNSHINE_GNUC_EXTENSION #endif using namespace std::literals; @@ -43,152 +44,151 @@ namespace bp = boost::process; window_system_e window_system; namespace dyn { - void * - handle(const std::vector &libs) { +void *handle(const std::vector &libs) { void *handle; for (auto lib : libs) { - handle = dlopen(lib, RTLD_LAZY | RTLD_LOCAL); - if (handle) { - return handle; - } + handle = dlopen(lib, RTLD_LAZY | RTLD_LOCAL); + if (handle) { + return handle; + } } std::stringstream ss; ss << "Couldn't find any of the following libraries: ["sv << libs.front(); - std::for_each(std::begin(libs) + 1, std::end(libs), [&](auto lib) { - ss << ", "sv << lib; - }); + std::for_each(std::begin(libs) + 1, std::end(libs), + [&](auto lib) { ss << ", "sv << lib; }); ss << ']'; BOOST_LOG(error) << ss.str(); return nullptr; - } +} - int - load(void *handle, const std::vector> &funcs, bool strict) { +int load(void *handle, + const std::vector> &funcs, + bool strict) { int err = 0; for (auto &func : funcs) { - TUPLE_2D_REF(fn, name, func); + TUPLE_2D_REF(fn, name, func); - *fn = SUNSHINE_GNUC_EXTENSION(apiproc) dlsym(handle, name); + *fn = SUNSHINE_GNUC_EXTENSION(apiproc) dlsym(handle, name); - if (!*fn && strict) { - BOOST_LOG(error) << "Couldn't find function: "sv << name; + if (!*fn && strict) { + BOOST_LOG(error) << "Couldn't find function: "sv << name; - err = -1; - } + err = -1; + } } return err; - } +} } // namespace dyn namespace platf { - using ifaddr_t = util::safe_ptr; +using ifaddr_t = util::safe_ptr; - ifaddr_t - get_ifaddrs() { - ifaddrs *p { nullptr }; +ifaddr_t get_ifaddrs() { + ifaddrs *p{nullptr}; getifaddrs(&p); - return ifaddr_t { p }; - } + return ifaddr_t{p}; +} - fs::path - appdata() { +fs::path appdata() { const char *homedir; if ((homedir = getenv("HOME")) == nullptr) { - homedir = getpwuid(geteuid())->pw_dir; + homedir = getpwuid(geteuid())->pw_dir; } - return fs::path { homedir } / ".config/sunshine"sv; - } + return fs::path{homedir} / ".config/sunshine"sv; +} - std::string - from_sockaddr(const sockaddr *const ip_addr) { +std::string from_sockaddr(const sockaddr *const ip_addr) { char data[INET6_ADDRSTRLEN] = {}; auto family = ip_addr->sa_family; if (family == AF_INET6) { - inet_ntop(AF_INET6, &((sockaddr_in6 *) ip_addr)->sin6_addr, data, - INET6_ADDRSTRLEN); - } - else if (family == AF_INET) { - inet_ntop(AF_INET, &((sockaddr_in *) ip_addr)->sin_addr, data, - INET_ADDRSTRLEN); + inet_ntop(AF_INET6, &((sockaddr_in6 *)ip_addr)->sin6_addr, data, + INET6_ADDRSTRLEN); + } else if (family == AF_INET) { + inet_ntop(AF_INET, &((sockaddr_in *)ip_addr)->sin_addr, data, + INET_ADDRSTRLEN); } - return std::string { data }; - } + return std::string{data}; +} - std::pair - from_sockaddr_ex(const sockaddr *const ip_addr) { +std::pair from_sockaddr_ex( + const sockaddr *const ip_addr) { char data[INET6_ADDRSTRLEN] = {}; auto family = ip_addr->sa_family; std::uint16_t port = 0; if (family == AF_INET6) { - inet_ntop(AF_INET6, &((sockaddr_in6 *) ip_addr)->sin6_addr, data, - INET6_ADDRSTRLEN); - port = ((sockaddr_in6 *) ip_addr)->sin6_port; - } - else if (family == AF_INET) { - inet_ntop(AF_INET, &((sockaddr_in *) ip_addr)->sin_addr, data, - INET_ADDRSTRLEN); - port = ((sockaddr_in *) ip_addr)->sin_port; + inet_ntop(AF_INET6, &((sockaddr_in6 *)ip_addr)->sin6_addr, data, + INET6_ADDRSTRLEN); + port = ((sockaddr_in6 *)ip_addr)->sin6_port; + } else if (family == AF_INET) { + inet_ntop(AF_INET, &((sockaddr_in *)ip_addr)->sin_addr, data, + INET_ADDRSTRLEN); + port = ((sockaddr_in *)ip_addr)->sin_port; } - return { port, std::string { data } }; - } + return {port, std::string{data}}; +} - std::string - get_mac_address(const std::string_view &address) { +std::string get_mac_address(const std::string_view &address) { auto ifaddrs = get_ifaddrs(); for (auto pos = ifaddrs.get(); pos != nullptr; pos = pos->ifa_next) { - if (pos->ifa_addr && address == from_sockaddr(pos->ifa_addr)) { - std::ifstream mac_file("/sys/class/net/"s + pos->ifa_name + "/address"); - if (mac_file.good()) { - std::string mac_address; - std::getline(mac_file, mac_address); - return mac_address; + if (pos->ifa_addr && address == from_sockaddr(pos->ifa_addr)) { + std::ifstream mac_file("/sys/class/net/"s + pos->ifa_name + + "/address"); + if (mac_file.good()) { + std::string mac_address; + std::getline(mac_file, mac_address); + return mac_address; + } } - } } BOOST_LOG(warning) << "Unable to find MAC address for "sv << address; return "00:00:00:00:00:00"s; - } +} - bp::child - run_command(bool elevated, bool interactive, const std::string &cmd, boost::filesystem::path &working_dir, const bp::environment &env, FILE *file, std::error_code &ec, bp::group *group) { +bp::child run_command(bool elevated, bool interactive, const std::string &cmd, + boost::filesystem::path &working_dir, + const bp::environment &env, FILE *file, + std::error_code &ec, bp::group *group) { if (!group) { - if (!file) { - return bp::child(cmd, env, bp::start_dir(working_dir), bp::std_out > bp::null, bp::std_err > bp::null, ec); - } - else { - return bp::child(cmd, env, bp::start_dir(working_dir), bp::std_out > file, bp::std_err > file, ec); - } + if (!file) { + return bp::child(cmd, env, bp::start_dir(working_dir), + bp::std_out > bp::null, bp::std_err > bp::null, + ec); + } else { + return bp::child(cmd, env, bp::start_dir(working_dir), + bp::std_out > file, bp::std_err > file, ec); + } + } else { + if (!file) { + return bp::child(cmd, env, bp::start_dir(working_dir), + bp::std_out > bp::null, bp::std_err > bp::null, ec, + *group); + } else { + return bp::child(cmd, env, bp::start_dir(working_dir), + bp::std_out > file, bp::std_err > file, ec, + *group); + } } - else { - if (!file) { - return bp::child(cmd, env, bp::start_dir(working_dir), bp::std_out > bp::null, bp::std_err > bp::null, ec, *group); - } - else { - return bp::child(cmd, env, bp::start_dir(working_dir), bp::std_out > file, bp::std_err > file, ec, *group); - } - } - } +} - void - adjust_thread_priority(thread_priority_e priority) { +void adjust_thread_priority(thread_priority_e priority) { // Unimplemented - } +} - struct sockaddr_in - to_sockaddr(boost::asio::ip::address_v4 address, uint16_t port) { +struct sockaddr_in to_sockaddr(boost::asio::ip::address_v4 address, + uint16_t port) { struct sockaddr_in saddr_v4 = {}; saddr_v4.sin_family = AF_INET; @@ -198,10 +198,10 @@ namespace platf { memcpy(&saddr_v4.sin_addr, addr_bytes.data(), sizeof(saddr_v4.sin_addr)); return saddr_v4; - } +} - struct sockaddr_in6 - to_sockaddr(boost::asio::ip::address_v6 address, uint16_t port) { +struct sockaddr_in6 to_sockaddr(boost::asio::ip::address_v6 address, + uint16_t port) { struct sockaddr_in6 saddr_v6 = {}; saddr_v6.sin6_family = AF_INET6; @@ -212,33 +212,34 @@ namespace platf { memcpy(&saddr_v6.sin6_addr, addr_bytes.data(), sizeof(saddr_v6.sin6_addr)); return saddr_v6; - } +} - bool - send_batch(batched_send_info_t &send_info) { - auto sockfd = (int) send_info.native_socket; +bool send_batch(batched_send_info_t &send_info) { + auto sockfd = (int)send_info.native_socket; struct msghdr msg = {}; // Convert the target address into a sockaddr struct sockaddr_in taddr_v4 = {}; struct sockaddr_in6 taddr_v6 = {}; if (send_info.target_address.is_v6()) { - taddr_v6 = to_sockaddr(send_info.target_address.to_v6(), send_info.target_port); + taddr_v6 = to_sockaddr(send_info.target_address.to_v6(), + send_info.target_port); - msg.msg_name = (struct sockaddr *) &taddr_v6; - msg.msg_namelen = sizeof(taddr_v6); - } - else { - taddr_v4 = to_sockaddr(send_info.target_address.to_v4(), send_info.target_port); + msg.msg_name = (struct sockaddr *)&taddr_v6; + msg.msg_namelen = sizeof(taddr_v6); + } else { + taddr_v4 = to_sockaddr(send_info.target_address.to_v4(), + send_info.target_port); - msg.msg_name = (struct sockaddr *) &taddr_v4; - msg.msg_namelen = sizeof(taddr_v4); + msg.msg_name = (struct sockaddr *)&taddr_v4; + msg.msg_namelen = sizeof(taddr_v4); } union { - char buf[CMSG_SPACE(sizeof(uint16_t)) + - std::max(CMSG_SPACE(sizeof(struct in_pktinfo)), CMSG_SPACE(sizeof(struct in6_pktinfo)))]; - struct cmsghdr alignment; + char buf[CMSG_SPACE(sizeof(uint16_t)) + + std::max(CMSG_SPACE(sizeof(struct in_pktinfo)), + CMSG_SPACE(sizeof(struct in6_pktinfo)))]; + struct cmsghdr alignment; } cmbuf = {}; // Must be zeroed for CMSG_NXTHDR() socklen_t cmbuflen = 0; @@ -249,171 +250,180 @@ namespace platf { // append the UDP_SEGMENT option next if applicable. auto pktinfo_cm = CMSG_FIRSTHDR(&msg); if (send_info.source_address.is_v6()) { - struct in6_pktinfo pktInfo; + struct in6_pktinfo pktInfo; - struct sockaddr_in6 saddr_v6 = to_sockaddr(send_info.source_address.to_v6(), 0); - pktInfo.ipi6_addr = saddr_v6.sin6_addr; - pktInfo.ipi6_ifindex = 0; + struct sockaddr_in6 saddr_v6 = + to_sockaddr(send_info.source_address.to_v6(), 0); + pktInfo.ipi6_addr = saddr_v6.sin6_addr; + pktInfo.ipi6_ifindex = 0; - cmbuflen += CMSG_SPACE(sizeof(pktInfo)); + cmbuflen += CMSG_SPACE(sizeof(pktInfo)); - pktinfo_cm->cmsg_level = IPPROTO_IPV6; - pktinfo_cm->cmsg_type = IPV6_PKTINFO; - pktinfo_cm->cmsg_len = CMSG_LEN(sizeof(pktInfo)); - memcpy(CMSG_DATA(pktinfo_cm), &pktInfo, sizeof(pktInfo)); - } - else { - struct in_pktinfo pktInfo; + pktinfo_cm->cmsg_level = IPPROTO_IPV6; + pktinfo_cm->cmsg_type = IPV6_PKTINFO; + pktinfo_cm->cmsg_len = CMSG_LEN(sizeof(pktInfo)); + memcpy(CMSG_DATA(pktinfo_cm), &pktInfo, sizeof(pktInfo)); + } else { + struct in_pktinfo pktInfo; - struct sockaddr_in saddr_v4 = to_sockaddr(send_info.source_address.to_v4(), 0); - pktInfo.ipi_spec_dst = saddr_v4.sin_addr; - pktInfo.ipi_ifindex = 0; + struct sockaddr_in saddr_v4 = + to_sockaddr(send_info.source_address.to_v4(), 0); + pktInfo.ipi_spec_dst = saddr_v4.sin_addr; + pktInfo.ipi_ifindex = 0; - cmbuflen += CMSG_SPACE(sizeof(pktInfo)); + cmbuflen += CMSG_SPACE(sizeof(pktInfo)); - pktinfo_cm->cmsg_level = IPPROTO_IP; - pktinfo_cm->cmsg_type = IP_PKTINFO; - pktinfo_cm->cmsg_len = CMSG_LEN(sizeof(pktInfo)); - memcpy(CMSG_DATA(pktinfo_cm), &pktInfo, sizeof(pktInfo)); + pktinfo_cm->cmsg_level = IPPROTO_IP; + pktinfo_cm->cmsg_type = IP_PKTINFO; + pktinfo_cm->cmsg_len = CMSG_LEN(sizeof(pktInfo)); + memcpy(CMSG_DATA(pktinfo_cm), &pktInfo, sizeof(pktInfo)); } #ifdef UDP_SEGMENT { - struct iovec iov = {}; + struct iovec iov = {}; - msg.msg_iov = &iov; - msg.msg_iovlen = 1; + msg.msg_iov = &iov; + msg.msg_iovlen = 1; - // UDP GSO on Linux currently only supports sending 64K or 64 segments at a time - size_t seg_index = 0; - const size_t seg_max = 65536 / 1500; - while (seg_index < send_info.block_count) { - iov.iov_base = (void *) &send_info.buffer[seg_index * send_info.block_size]; - iov.iov_len = send_info.block_size * std::min(send_info.block_count - seg_index, seg_max); + // UDP GSO on Linux currently only supports sending 64K or 64 segments + // at a time + size_t seg_index = 0; + const size_t seg_max = 65536 / 1500; + while (seg_index < send_info.block_count) { + iov.iov_base = + (void *)&send_info.buffer[seg_index * send_info.block_size]; + iov.iov_len = send_info.block_size * + std::min(send_info.block_count - seg_index, seg_max); - // We should not use GSO if the data is <= one full block size - if (iov.iov_len > send_info.block_size) { - msg.msg_controllen = cmbuflen + CMSG_SPACE(sizeof(uint16_t)); + // We should not use GSO if the data is <= one full block size + if (iov.iov_len > send_info.block_size) { + msg.msg_controllen = cmbuflen + CMSG_SPACE(sizeof(uint16_t)); - // Enable GSO to perform segmentation of our buffer for us - auto cm = CMSG_NXTHDR(&msg, pktinfo_cm); - cm->cmsg_level = SOL_UDP; - cm->cmsg_type = UDP_SEGMENT; - cm->cmsg_len = CMSG_LEN(sizeof(uint16_t)); - *((uint16_t *) CMSG_DATA(cm)) = send_info.block_size; - } - else { - msg.msg_controllen = cmbuflen; - } - - // This will fail if GSO is not available, so we will fall back to non-GSO if - // it's the first sendmsg() call. On subsequent calls, we will treat errors as - // actual failures and return to the caller. - auto bytes_sent = sendmsg(sockfd, &msg, 0); - if (bytes_sent < 0) { - // If there's no send buffer space, wait for some to be available - if (errno == EAGAIN) { - struct pollfd pfd; - - pfd.fd = sockfd; - pfd.events = POLLOUT; - - if (poll(&pfd, 1, -1) != 1) { - BOOST_LOG(warning) << "poll() failed: "sv << errno; - break; + // Enable GSO to perform segmentation of our buffer for us + auto cm = CMSG_NXTHDR(&msg, pktinfo_cm); + cm->cmsg_level = SOL_UDP; + cm->cmsg_type = UDP_SEGMENT; + cm->cmsg_len = CMSG_LEN(sizeof(uint16_t)); + *((uint16_t *)CMSG_DATA(cm)) = send_info.block_size; + } else { + msg.msg_controllen = cmbuflen; } - // Try to send again - continue; - } + // This will fail if GSO is not available, so we will fall back to + // non-GSO if it's the first sendmsg() call. On subsequent calls, we + // will treat errors as actual failures and return to the caller. + auto bytes_sent = sendmsg(sockfd, &msg, 0); + if (bytes_sent < 0) { + // If there's no send buffer space, wait for some to be + // available + if (errno == EAGAIN) { + struct pollfd pfd; - break; + pfd.fd = sockfd; + pfd.events = POLLOUT; + + if (poll(&pfd, 1, -1) != 1) { + BOOST_LOG(warning) << "poll() failed: "sv << errno; + break; + } + + // Try to send again + continue; + } + + break; + } + + seg_index += bytes_sent / send_info.block_size; } - seg_index += bytes_sent / send_info.block_size; - } - - // If we sent something, return the status and don't fall back to the non-GSO path. - if (seg_index != 0) { - return seg_index >= send_info.block_count; - } + // If we sent something, return the status and don't fall back to the + // non-GSO path. + if (seg_index != 0) { + return seg_index >= send_info.block_count; + } } #endif { - // If GSO is not supported, use sendmmsg() instead. - struct mmsghdr msgs[send_info.block_count]; - struct iovec iovs[send_info.block_count]; - for (size_t i = 0; i < send_info.block_count; i++) { - iovs[i] = {}; - iovs[i].iov_base = (void *) &send_info.buffer[i * send_info.block_size]; - iovs[i].iov_len = send_info.block_size; + // If GSO is not supported, use sendmmsg() instead. + struct mmsghdr msgs[send_info.block_count]; + struct iovec iovs[send_info.block_count]; + for (size_t i = 0; i < send_info.block_count; i++) { + iovs[i] = {}; + iovs[i].iov_base = + (void *)&send_info.buffer[i * send_info.block_size]; + iovs[i].iov_len = send_info.block_size; - msgs[i] = {}; - msgs[i].msg_hdr.msg_name = msg.msg_name; - msgs[i].msg_hdr.msg_namelen = msg.msg_namelen; - msgs[i].msg_hdr.msg_iov = &iovs[i]; - msgs[i].msg_hdr.msg_iovlen = 1; - msgs[i].msg_hdr.msg_control = cmbuf.buf; - msgs[i].msg_hdr.msg_controllen = cmbuflen; - } - - // Call sendmmsg() until all messages are sent - size_t blocks_sent = 0; - while (blocks_sent < send_info.block_count) { - int msgs_sent = sendmmsg(sockfd, &msgs[blocks_sent], send_info.block_count - blocks_sent, 0); - if (msgs_sent < 0) { - // If there's no send buffer space, wait for some to be available - if (errno == EAGAIN) { - struct pollfd pfd; - - pfd.fd = sockfd; - pfd.events = POLLOUT; - - if (poll(&pfd, 1, -1) != 1) { - BOOST_LOG(warning) << "poll() failed: "sv << errno; - break; - } - - // Try to send again - continue; - } - - BOOST_LOG(warning) << "sendmmsg() failed: "sv << errno; - return false; + msgs[i] = {}; + msgs[i].msg_hdr.msg_name = msg.msg_name; + msgs[i].msg_hdr.msg_namelen = msg.msg_namelen; + msgs[i].msg_hdr.msg_iov = &iovs[i]; + msgs[i].msg_hdr.msg_iovlen = 1; + msgs[i].msg_hdr.msg_control = cmbuf.buf; + msgs[i].msg_hdr.msg_controllen = cmbuflen; } - blocks_sent += msgs_sent; - } + // Call sendmmsg() until all messages are sent + size_t blocks_sent = 0; + while (blocks_sent < send_info.block_count) { + int msgs_sent = sendmmsg(sockfd, &msgs[blocks_sent], + send_info.block_count - blocks_sent, 0); + if (msgs_sent < 0) { + // If there's no send buffer space, wait for some to be + // available + if (errno == EAGAIN) { + struct pollfd pfd; - return true; + pfd.fd = sockfd; + pfd.events = POLLOUT; + + if (poll(&pfd, 1, -1) != 1) { + BOOST_LOG(warning) << "poll() failed: "sv << errno; + break; + } + + // Try to send again + continue; + } + + BOOST_LOG(warning) << "sendmmsg() failed: "sv << errno; + return false; + } + + blocks_sent += msgs_sent; + } + + return true; } - } +} - bool - send(send_info_t &send_info) { - auto sockfd = (int) send_info.native_socket; +bool send(send_info_t &send_info) { + auto sockfd = (int)send_info.native_socket; struct msghdr msg = {}; // Convert the target address into a sockaddr struct sockaddr_in taddr_v4 = {}; struct sockaddr_in6 taddr_v6 = {}; if (send_info.target_address.is_v6()) { - taddr_v6 = to_sockaddr(send_info.target_address.to_v6(), send_info.target_port); + taddr_v6 = to_sockaddr(send_info.target_address.to_v6(), + send_info.target_port); - msg.msg_name = (struct sockaddr *) &taddr_v6; - msg.msg_namelen = sizeof(taddr_v6); - } - else { - taddr_v4 = to_sockaddr(send_info.target_address.to_v4(), send_info.target_port); + msg.msg_name = (struct sockaddr *)&taddr_v6; + msg.msg_namelen = sizeof(taddr_v6); + } else { + taddr_v4 = to_sockaddr(send_info.target_address.to_v4(), + send_info.target_port); - msg.msg_name = (struct sockaddr *) &taddr_v4; - msg.msg_namelen = sizeof(taddr_v4); + msg.msg_name = (struct sockaddr *)&taddr_v4; + msg.msg_namelen = sizeof(taddr_v4); } union { - char buf[std::max(CMSG_SPACE(sizeof(struct in_pktinfo)), CMSG_SPACE(sizeof(struct in6_pktinfo)))]; - struct cmsghdr alignment; + char buf[std::max(CMSG_SPACE(sizeof(struct in_pktinfo)), + CMSG_SPACE(sizeof(struct in6_pktinfo)))]; + struct cmsghdr alignment; } cmbuf; socklen_t cmbuflen = 0; @@ -422,36 +432,37 @@ namespace platf { auto pktinfo_cm = CMSG_FIRSTHDR(&msg); if (send_info.source_address.is_v6()) { - struct in6_pktinfo pktInfo; + struct in6_pktinfo pktInfo; - struct sockaddr_in6 saddr_v6 = to_sockaddr(send_info.source_address.to_v6(), 0); - pktInfo.ipi6_addr = saddr_v6.sin6_addr; - pktInfo.ipi6_ifindex = 0; + struct sockaddr_in6 saddr_v6 = + to_sockaddr(send_info.source_address.to_v6(), 0); + pktInfo.ipi6_addr = saddr_v6.sin6_addr; + pktInfo.ipi6_ifindex = 0; - cmbuflen += CMSG_SPACE(sizeof(pktInfo)); + cmbuflen += CMSG_SPACE(sizeof(pktInfo)); - pktinfo_cm->cmsg_level = IPPROTO_IPV6; - pktinfo_cm->cmsg_type = IPV6_PKTINFO; - pktinfo_cm->cmsg_len = CMSG_LEN(sizeof(pktInfo)); - memcpy(CMSG_DATA(pktinfo_cm), &pktInfo, sizeof(pktInfo)); - } - else { - struct in_pktinfo pktInfo; + pktinfo_cm->cmsg_level = IPPROTO_IPV6; + pktinfo_cm->cmsg_type = IPV6_PKTINFO; + pktinfo_cm->cmsg_len = CMSG_LEN(sizeof(pktInfo)); + memcpy(CMSG_DATA(pktinfo_cm), &pktInfo, sizeof(pktInfo)); + } else { + struct in_pktinfo pktInfo; - struct sockaddr_in saddr_v4 = to_sockaddr(send_info.source_address.to_v4(), 0); - pktInfo.ipi_spec_dst = saddr_v4.sin_addr; - pktInfo.ipi_ifindex = 0; + struct sockaddr_in saddr_v4 = + to_sockaddr(send_info.source_address.to_v4(), 0); + pktInfo.ipi_spec_dst = saddr_v4.sin_addr; + pktInfo.ipi_ifindex = 0; - cmbuflen += CMSG_SPACE(sizeof(pktInfo)); + cmbuflen += CMSG_SPACE(sizeof(pktInfo)); - pktinfo_cm->cmsg_level = IPPROTO_IP; - pktinfo_cm->cmsg_type = IP_PKTINFO; - pktinfo_cm->cmsg_len = CMSG_LEN(sizeof(pktInfo)); - memcpy(CMSG_DATA(pktinfo_cm), &pktInfo, sizeof(pktInfo)); + pktinfo_cm->cmsg_level = IPPROTO_IP; + pktinfo_cm->cmsg_type = IP_PKTINFO; + pktinfo_cm->cmsg_len = CMSG_LEN(sizeof(pktInfo)); + memcpy(CMSG_DATA(pktinfo_cm), &pktInfo, sizeof(pktInfo)); } struct iovec iov = {}; - iov.iov_base = (void *) send_info.buffer; + iov.iov_base = (void *)send_info.buffer; iov.iov_len = send_info.size; msg.msg_iov = &iov; @@ -463,101 +474,95 @@ namespace platf { // If there's no send buffer space, wait for some to be available while (bytes_sent < 0 && errno == EAGAIN) { - struct pollfd pfd; + struct pollfd pfd; - pfd.fd = sockfd; - pfd.events = POLLOUT; + pfd.fd = sockfd; + pfd.events = POLLOUT; - if (poll(&pfd, 1, -1) != 1) { - BOOST_LOG(warning) << "poll() failed: "sv << errno; - break; - } + if (poll(&pfd, 1, -1) != 1) { + BOOST_LOG(warning) << "poll() failed: "sv << errno; + break; + } - // Try to send again - bytes_sent = sendmsg(sockfd, &msg, 0); + // Try to send again + bytes_sent = sendmsg(sockfd, &msg, 0); } if (bytes_sent < 0) { - BOOST_LOG(warning) << "sendmsg() failed: "sv << errno; - return false; + BOOST_LOG(warning) << "sendmsg() failed: "sv << errno; + return false; } return true; - } +} - namespace source { - enum source_e : std::size_t { +namespace source { +enum source_e : std::size_t { #ifdef SUNSHINE_BUILD_CUDA - NVFBC, + NVFBC, #endif #ifdef SUNSHINE_BUILD_WAYLAND - WAYLAND, + WAYLAND, #endif #ifdef SUNSHINE_BUILD_DRM - KMS, + KMS, #endif #ifdef SUNSHINE_BUILD_X11 - X11, + X11, #endif - MAX_FLAGS - }; - } // namespace source + MAX_FLAGS +}; +} // namespace source - static std::bitset sources; +static std::bitset sources; #ifdef SUNSHINE_BUILD_CUDA - std::vector - nvfbc_display_names(); - std::shared_ptr - nvfbc_display(mem_type_e hwdevice_type, const std::string &display_name, const video::config_t &config); +std::vector nvfbc_display_names(); +std::shared_ptr nvfbc_display(mem_type_e hwdevice_type, + const std::string &display_name, + const video::config_t &config); - bool - verify_nvfbc() { - return !nvfbc_display_names().empty(); - } +bool verify_nvfbc() { return !nvfbc_display_names().empty(); } #endif #ifdef SUNSHINE_BUILD_WAYLAND - std::vector - wl_display_names(); - std::shared_ptr - wl_display(mem_type_e hwdevice_type, const std::string &display_name, const video::config_t &config); +std::vector wl_display_names(); +std::shared_ptr wl_display(mem_type_e hwdevice_type, + const std::string &display_name, + const video::config_t &config); - bool - verify_wl() { - return window_system == window_system_e::WAYLAND && !wl_display_names().empty(); - } +bool verify_wl() { + return window_system == window_system_e::WAYLAND && + !wl_display_names().empty(); +} #endif #ifdef SUNSHINE_BUILD_DRM - std::vector - kms_display_names(); - std::shared_ptr - kms_display(mem_type_e hwdevice_type, const std::string &display_name, const video::config_t &config); +std::vector kms_display_names(); +std::shared_ptr kms_display(mem_type_e hwdevice_type, + const std::string &display_name, + const video::config_t &config); - bool - verify_kms() { - return !kms_display_names().empty(); - } +bool verify_kms() { return !kms_display_names().empty(); } #endif #ifdef SUNSHINE_BUILD_X11 - std::vector - x11_display_names(); - std::shared_ptr - x11_display(mem_type_e hwdevice_type, const std::string &display_name, const video::config_t &config); +std::vector x11_display_names(); +std::shared_ptr x11_display(mem_type_e hwdevice_type, + const std::string &display_name, + const video::config_t &config); - bool - verify_x11() { - return window_system == window_system_e::X11 && !x11_display_names().empty(); - } +bool verify_x11() { + return window_system == window_system_e::X11 && + !x11_display_names().empty(); +} #endif - std::vector - display_names(mem_type_e hwdevice_type) { +std::vector display_names(mem_type_e hwdevice_type) { #ifdef SUNSHINE_BUILD_CUDA // display using NvFBC only supports mem_type_e::cuda - if (sources[source::NVFBC] && hwdevice_type == mem_type_e::cuda) return nvfbc_display_names(); + if (sources[source::NVFBC] && hwdevice_type == mem_type_e::cuda) + return nvfbc_display_names(); #endif #ifdef SUNSHINE_BUILD_WAYLAND if (sources[source::WAYLAND]) return wl_display_names(); @@ -569,40 +574,40 @@ namespace platf { if (sources[source::X11]) return x11_display_names(); #endif return {}; - } +} - std::shared_ptr - display(mem_type_e hwdevice_type, const std::string &display_name, const video::config_t &config) { +std::shared_ptr display(mem_type_e hwdevice_type, + const std::string &display_name, + const video::config_t &config) { #ifdef SUNSHINE_BUILD_CUDA if (sources[source::NVFBC] && hwdevice_type == mem_type_e::cuda) { - BOOST_LOG(info) << "Screencasting with NvFBC"sv; - return nvfbc_display(hwdevice_type, display_name, config); + BOOST_LOG(info) << "Screencasting with NvFBC"sv; + return nvfbc_display(hwdevice_type, display_name, config); } #endif #ifdef SUNSHINE_BUILD_WAYLAND if (sources[source::WAYLAND]) { - BOOST_LOG(info) << "Screencasting with Wayland's protocol"sv; - return wl_display(hwdevice_type, display_name, config); + BOOST_LOG(info) << "Screencasting with Wayland's protocol"sv; + return wl_display(hwdevice_type, display_name, config); } #endif #ifdef SUNSHINE_BUILD_DRM if (sources[source::KMS]) { - BOOST_LOG(info) << "Screencasting with KMS"sv; - return kms_display(hwdevice_type, display_name, config); + BOOST_LOG(info) << "Screencasting with KMS"sv; + return kms_display(hwdevice_type, display_name, config); } #endif #ifdef SUNSHINE_BUILD_X11 if (sources[source::X11]) { - BOOST_LOG(info) << "Screencasting with X11"sv; - return x11_display(hwdevice_type, display_name, config); + BOOST_LOG(info) << "Screencasting with X11"sv; + return x11_display(hwdevice_type, display_name, config); } #endif return nullptr; - } +} - std::unique_ptr - init() { +std::unique_ptr init() { // These are allowed to fail. gbm::init(); va::init(); @@ -610,63 +615,64 @@ namespace platf { window_system = window_system_e::NONE; #ifdef SUNSHINE_BUILD_WAYLAND if (std::getenv("WAYLAND_DISPLAY")) { - window_system = window_system_e::WAYLAND; + window_system = window_system_e::WAYLAND; } #endif #if defined(SUNSHINE_BUILD_X11) || defined(SUNSHINE_BUILD_CUDA) if (std::getenv("DISPLAY") && window_system != window_system_e::WAYLAND) { - if (std::getenv("WAYLAND_DISPLAY")) { - BOOST_LOG(warning) << "Wayland detected, yet sunshine will use X11 for screencasting, screencasting will only work on XWayland applications"sv; - } + if (std::getenv("WAYLAND_DISPLAY")) { + BOOST_LOG(warning) + << "Wayland detected, yet sunshine will use X11 for screencasting, screencasting will only work on XWayland applications"sv; + } - window_system = window_system_e::X11; + window_system = window_system_e::X11; } #endif #ifdef SUNSHINE_BUILD_CUDA if (config::video.capture.empty() || config::video.capture == "nvfbc") { - if (verify_nvfbc()) { - sources[source::NVFBC] = true; - } + if (verify_nvfbc()) { + sources[source::NVFBC] = true; + } } #endif #ifdef SUNSHINE_BUILD_WAYLAND if (config::video.capture.empty() || config::video.capture == "wlr") { - if (verify_wl()) { - sources[source::WAYLAND] = true; - } + if (verify_wl()) { + sources[source::WAYLAND] = true; + } } #endif #ifdef SUNSHINE_BUILD_DRM if (config::video.capture.empty() || config::video.capture == "kms") { - if (verify_kms()) { - if (window_system == window_system_e::WAYLAND) { - // On Wayland, using KMS, the cursor is unreliable. - // Hide it by default - display_cursor = false; + if (verify_kms()) { + if (window_system == window_system_e::WAYLAND) { + // On Wayland, using KMS, the cursor is unreliable. + // Hide it by default + display_cursor = false; + } + sources[source::KMS] = true; } - sources[source::KMS] = true; - } } #endif #ifdef SUNSHINE_BUILD_X11 if (config::video.capture.empty() || config::video.capture == "x11") { - if (verify_x11()) { - sources[source::X11] = true; - } + if (verify_x11()) { + sources[source::X11] = true; + } } #endif if (sources.none()) { - BOOST_LOG(error) << "Unable to initialize capture method"sv; - return true; + BOOST_LOG(error) << "Unable to initialize capture method"sv; + return true; } if (!gladLoaderLoadEGL(EGL_NO_DISPLAY) || !eglGetPlatformDisplay) { - BOOST_LOG(warning) << "Couldn't load EGL library"sv; - return true; + BOOST_LOG(warning) << "Couldn't load EGL library"sv; + return true; } return false; - } +} } // namespace platf diff --git a/src/platform/linux/misc.h b/src/platform/linux/misc.h index 8541bf16..2711ecfc 100644 --- a/src/platform/linux/misc.h +++ b/src/platform/linux/misc.h @@ -5,30 +5,31 @@ #pragma once #include + #include #include "src/utility.h" KITTY_USING_MOVE_T(file_t, int, -1, { - if (el >= 0) { - close(el); - } + if (el >= 0) { + close(el); + } }); enum class window_system_e { - NONE, - X11, - WAYLAND, + NONE, + X11, + WAYLAND, }; extern window_system_e window_system; namespace dyn { - typedef void (*apiproc)(void); +typedef void (*apiproc)(void); - int - load(void *handle, const std::vector> &funcs, bool strict = true); - void * - handle(const std::vector &libs); +int load(void *handle, + const std::vector> &funcs, + bool strict = true); +void *handle(const std::vector &libs); } // namespace dyn diff --git a/src/platform/linux/vaapi.cpp b/src/platform/linux/vaapi.cpp index 18b0dff8..9c9bf909 100644 --- a/src/platform/linux/vaapi.cpp +++ b/src/platform/linux/vaapi.cpp @@ -2,22 +2,19 @@ * @file src/platform/linux/vaapi.cpp * @brief todo */ +#include + #include #include -#include - extern "C" { #include #include #if !VA_CHECK_VERSION(1, 9, 0) -// vaSyncBuffer stub allows Sunshine built against libva <2.9.0 to link against ffmpeg on libva 2.9.0 or later -VAStatus -vaSyncBuffer( - VADisplay dpy, - VABufferID buf_id, - uint64_t timeout_ns) { - return VA_STATUS_ERROR_UNIMPLEMENTED; +// vaSyncBuffer stub allows Sunshine built against libva <2.9.0 to link against +// ffmpeg on libva 2.9.0 or later +VAStatus vaSyncBuffer(VADisplay dpy, VABufferID buf_id, uint64_t timeout_ns) { + return VA_STATUS_ERROR_UNIMPLEMENTED; } #endif } @@ -35,16 +32,16 @@ using namespace std::literals; extern "C" struct AVBufferRef; namespace va { - constexpr auto SURFACE_ATTRIB_MEM_TYPE_DRM_PRIME_2 = 0x40000000; - constexpr auto EXPORT_SURFACE_WRITE_ONLY = 0x0002; - constexpr auto EXPORT_SURFACE_COMPOSED_LAYERS = 0x0008; +constexpr auto SURFACE_ATTRIB_MEM_TYPE_DRM_PRIME_2 = 0x40000000; +constexpr auto EXPORT_SURFACE_WRITE_ONLY = 0x0002; +constexpr auto EXPORT_SURFACE_COMPOSED_LAYERS = 0x0008; - using VADisplay = void *; - using VAStatus = int; - using VAGenericID = unsigned int; - using VASurfaceID = VAGenericID; +using VADisplay = void *; +using VAStatus = int; +using VAGenericID = unsigned int; +using VASurfaceID = VAGenericID; - struct DRMPRIMESurfaceDescriptor { +struct DRMPRIMESurfaceDescriptor { // VA Pixel format fourcc of the whole surface (VA_FOURCC_*). uint32_t fourcc; @@ -55,40 +52,42 @@ namespace va { uint32_t num_objects; struct { - // DRM PRIME file descriptor for this object. - // Needs to be closed manually - int fd; + // DRM PRIME file descriptor for this object. + // Needs to be closed manually + int fd; - // Total size of this object (may include regions which are not part of the surface) - uint32_t size; - // Format modifier applied to this object, not sure what that means - uint64_t drm_format_modifier; + // Total size of this object (may include regions which are not part of + // the surface) + uint32_t size; + // Format modifier applied to this object, not sure what that means + uint64_t drm_format_modifier; } objects[4]; // Number of layers making up the surface. uint32_t num_layers; struct { - // DRM format fourcc of this layer (DRM_FOURCC_*). - uint32_t drm_format; + // DRM format fourcc of this layer (DRM_FOURCC_*). + uint32_t drm_format; - // Number of planes in this layer. - uint32_t num_planes; + // Number of planes in this layer. + uint32_t num_planes; - // references objects --> DRMPRIMESurfaceDescriptor.objects[object_index[0]] - uint32_t object_index[4]; + // references objects --> + // DRMPRIMESurfaceDescriptor.objects[object_index[0]] + uint32_t object_index[4]; - // Offset within the object of each plane. - uint32_t offset[4]; + // Offset within the object of each plane. + uint32_t offset[4]; - // Pitch of each plane. - uint32_t pitch[4]; + // Pitch of each plane. + uint32_t pitch[4]; } layers[4]; - }; +}; - /** - * @brief Defined profiles - */ - enum class profile_e { +/** + * @brief Defined profiles + */ +enum class profile_e { // Profile ID used for video processing. ProfileNone = -1, MPEG2Simple = 0, @@ -129,15 +128,15 @@ namespace va { // Profile ID used for protected video playback. Protected = 35 - }; +}; - enum class entry_e { +enum class entry_e { VLD = 1, IZZ = 2, IDCT = 3, MoComp = 4, Deblocking = 5, - EncSlice = 6, /** slice level encode */ + EncSlice = 6, /** slice level encode */ EncPicture = 7, /** picture encode, JPEG, etc */ /** * For an implementation that supports a low power/high performance variant @@ -152,35 +151,39 @@ namespace va { /** * @brief FEI * - * The purpose of FEI (Flexible Encoding Infrastructure) is to allow applications to - * have more controls and trade off quality for speed with their own IPs. - * The application can optionally provide input to ENC for extra encode control - * and get the output from ENC. Application can chose to modify the ENC - * output/PAK input during encoding, but the performance impact is significant. + * The purpose of FEI (Flexible Encoding Infrastructure) is to allow + * applications to have more controls and trade off quality for speed with + * their own IPs. The application can optionally provide input to ENC for + * extra encode control and get the output from ENC. Application can chose + * to modify the ENC output/PAK input during encoding, but the performance + * impact is significant. * * On top of the existing buffers for normal encode, there will be * one extra input buffer (VAEncMiscParameterFEIFrameControl) and - * three extra output buffers (VAEncFEIMVBufferType, VAEncFEIMBModeBufferType - * and VAEncFEIDistortionBufferType) for FEI entry function. - * If separate PAK is set, two extra input buffers - * (VAEncFEIMVBufferType, VAEncFEIMBModeBufferType) are needed for PAK input. + * three extra output buffers (VAEncFEIMVBufferType, + * VAEncFEIMBModeBufferType and VAEncFEIDistortionBufferType) for FEI entry + * function. If separate PAK is set, two extra input buffers + * (VAEncFEIMVBufferType, VAEncFEIMBModeBufferType) are needed for PAK + * input. */ FEI = 11, /** * @brief Stats * - * A pre-processing function for getting some statistics and motion vectors is added, - * and some extra controls for Encode pipeline are provided. The application can - * optionally call the statistics function to get motion vectors and statistics like - * variances, distortions before calling Encode function via this entry point. + * A pre-processing function for getting some statistics and motion vectors + * is added, and some extra controls for Encode pipeline are provided. The + * application can optionally call the statistics function to get motion + * vectors and statistics like variances, distortions before calling Encode + * function via this entry point. * - * Checking whether Statistics is supported can be performed with vaQueryConfigEntrypoints(). - * If Statistics entry point is supported, then the list of returned entry-points will - * include #Stats. Supported pixel format, maximum resolution and statistics - * specific attributes can be obtained via normal attribute query. One input buffer + * Checking whether Statistics is supported can be performed with + * vaQueryConfigEntrypoints(). If Statistics entry point is supported, then + * the list of returned entry-points will include #Stats. Supported pixel + * format, maximum resolution and statistics specific attributes can be + * obtained via normal attribute query. One input buffer * (VAStatsStatisticsParameterBufferType) and one or two output buffers - * (VAStatsStatisticsBufferType, VAStatsStatisticsBottomFieldBufferType (for interlace only) - * and VAStatsMVBufferType) are needed for this entry point. + * (VAStatsStatisticsBufferType, VAStatsStatisticsBottomFieldBufferType (for + * interlace only) and VAStatsMVBufferType) are needed for this entry point. */ Stats = 12, /** @@ -195,213 +198,216 @@ namespace va { * A function for protected content to decrypt encrypted content. */ ProtectedContent = 14, - }; +}; - typedef VAStatus (*queryConfigEntrypoints_fn)(VADisplay dpy, profile_e profile, entry_e *entrypoint_list, int *num_entrypoints); - typedef int (*maxNumEntrypoints_fn)(VADisplay dpy); - typedef VADisplay (*getDisplayDRM_fn)(int fd); - typedef VAStatus (*terminate_fn)(VADisplay dpy); - typedef VAStatus (*initialize_fn)(VADisplay dpy, int *major_version, int *minor_version); - typedef const char *(*errorStr_fn)(VAStatus error_status); - typedef void (*VAMessageCallback)(void *user_context, const char *message); - typedef VAMessageCallback (*setErrorCallback_fn)(VADisplay dpy, VAMessageCallback callback, void *user_context); - typedef VAMessageCallback (*setInfoCallback_fn)(VADisplay dpy, VAMessageCallback callback, void *user_context); - typedef const char *(*queryVendorString_fn)(VADisplay dpy); - typedef VAStatus (*exportSurfaceHandle_fn)( - VADisplay dpy, VASurfaceID surface_id, - uint32_t mem_type, uint32_t flags, - void *descriptor); +typedef VAStatus (*queryConfigEntrypoints_fn)(VADisplay dpy, profile_e profile, + entry_e *entrypoint_list, + int *num_entrypoints); +typedef int (*maxNumEntrypoints_fn)(VADisplay dpy); +typedef VADisplay (*getDisplayDRM_fn)(int fd); +typedef VAStatus (*terminate_fn)(VADisplay dpy); +typedef VAStatus (*initialize_fn)(VADisplay dpy, int *major_version, + int *minor_version); +typedef const char *(*errorStr_fn)(VAStatus error_status); +typedef void (*VAMessageCallback)(void *user_context, const char *message); +typedef VAMessageCallback (*setErrorCallback_fn)(VADisplay dpy, + VAMessageCallback callback, + void *user_context); +typedef VAMessageCallback (*setInfoCallback_fn)(VADisplay dpy, + VAMessageCallback callback, + void *user_context); +typedef const char *(*queryVendorString_fn)(VADisplay dpy); +typedef VAStatus (*exportSurfaceHandle_fn)(VADisplay dpy, + VASurfaceID surface_id, + uint32_t mem_type, uint32_t flags, + void *descriptor); - static maxNumEntrypoints_fn maxNumEntrypoints; - static queryConfigEntrypoints_fn queryConfigEntrypoints; - static getDisplayDRM_fn getDisplayDRM; - static terminate_fn terminate; - static initialize_fn initialize; - static errorStr_fn errorStr; - static setErrorCallback_fn setErrorCallback; - static setInfoCallback_fn setInfoCallback; - static queryVendorString_fn queryVendorString; - static exportSurfaceHandle_fn exportSurfaceHandle; +static maxNumEntrypoints_fn maxNumEntrypoints; +static queryConfigEntrypoints_fn queryConfigEntrypoints; +static getDisplayDRM_fn getDisplayDRM; +static terminate_fn terminate; +static initialize_fn initialize; +static errorStr_fn errorStr; +static setErrorCallback_fn setErrorCallback; +static setInfoCallback_fn setInfoCallback; +static queryVendorString_fn queryVendorString; +static exportSurfaceHandle_fn exportSurfaceHandle; - using display_t = util::dyn_safe_ptr_v2; +using display_t = util::dyn_safe_ptr_v2; - int - init_main_va() { - static void *handle { nullptr }; +int init_main_va() { + static void *handle{nullptr}; static bool funcs_loaded = false; if (funcs_loaded) return 0; if (!handle) { - handle = dyn::handle({ "libva.so.2", "libva.so" }); - if (!handle) { - return -1; - } - } - - std::vector> funcs { - { (dyn::apiproc *) &maxNumEntrypoints, "vaMaxNumEntrypoints" }, - { (dyn::apiproc *) &queryConfigEntrypoints, "vaQueryConfigEntrypoints" }, - { (dyn::apiproc *) &terminate, "vaTerminate" }, - { (dyn::apiproc *) &initialize, "vaInitialize" }, - { (dyn::apiproc *) &errorStr, "vaErrorStr" }, - { (dyn::apiproc *) &setErrorCallback, "vaSetErrorCallback" }, - { (dyn::apiproc *) &setInfoCallback, "vaSetInfoCallback" }, - { (dyn::apiproc *) &queryVendorString, "vaQueryVendorString" }, - { (dyn::apiproc *) &exportSurfaceHandle, "vaExportSurfaceHandle" }, - }; - - if (dyn::load(handle, funcs)) { - return -1; - } - - funcs_loaded = true; - return 0; - } - - int - init() { - if (init_main_va()) { - return -1; - } - - static void *handle { nullptr }; - static bool funcs_loaded = false; - - if (funcs_loaded) return 0; - - if (!handle) { - handle = dyn::handle({ "libva-drm.so.2", "libva-drm.so" }); - if (!handle) { - return -1; - } - } - - std::vector> funcs { - { (dyn::apiproc *) &getDisplayDRM, "vaGetDisplayDRM" }, - }; - - if (dyn::load(handle, funcs)) { - return -1; - } - - funcs_loaded = true; - return 0; - } - - int - vaapi_init_avcodec_hardware_input_buffer(platf::avcodec_encode_device_t *encode_device, AVBufferRef **hw_device_buf); - - class va_t: public platf::avcodec_encode_device_t { - public: - int - init(int in_width, int in_height, file_t &&render_device) { - file = std::move(render_device); - - if (!va::initialize || !gbm::create_device) { - if (!va::initialize) BOOST_LOG(warning) << "libva not initialized"sv; - if (!gbm::create_device) BOOST_LOG(warning) << "libgbm not initialized"sv; - return -1; - } - - this->data = (void *) vaapi_init_avcodec_hardware_input_buffer; - - gbm.reset(gbm::create_device(file.el)); - if (!gbm) { - char string[1024]; - BOOST_LOG(error) << "Couldn't create GBM device: ["sv << strerror_r(errno, string, sizeof(string)) << ']'; - return -1; - } - - display = egl::make_display(gbm.get()); - if (!display) { - return -1; - } - - auto ctx_opt = egl::make_ctx(display.get()); - if (!ctx_opt) { - return -1; - } - - ctx = std::move(*ctx_opt); - - width = in_width; - height = in_height; - - return 0; - } - - int - set_frame(AVFrame *frame, AVBufferRef *hw_frames_ctx) override { - this->hwframe.reset(frame); - this->frame = frame; - - if (!frame->buf[0]) { - if (av_hwframe_get_buffer(hw_frames_ctx, frame, 0)) { - BOOST_LOG(error) << "Couldn't get hwframe for VAAPI"sv; - return -1; + handle = dyn::handle({"libva.so.2", "libva.so"}); + if (!handle) { + return -1; } - } - - va::DRMPRIMESurfaceDescriptor prime; - va::VASurfaceID surface = (std::uintptr_t) frame->data[3]; - - auto status = va::exportSurfaceHandle( - this->va_display, - surface, - va::SURFACE_ATTRIB_MEM_TYPE_DRM_PRIME_2, - va::EXPORT_SURFACE_WRITE_ONLY | va::EXPORT_SURFACE_COMPOSED_LAYERS, - &prime); - if (status) { - BOOST_LOG(error) << "Couldn't export va surface handle: ["sv << (int) surface << "]: "sv << va::errorStr(status); - - return -1; - } - - // Keep track of file descriptors - std::array fds; - for (int x = 0; x < prime.num_objects; ++x) { - fds[x] = prime.objects[x].fd; - } - - auto nv12_opt = egl::import_target( - display.get(), - std::move(fds), - { (int) prime.width, - (int) prime.height, - { prime.objects[prime.layers[0].object_index[0]].fd, -1, -1, -1 }, - 0, - 0, - { prime.layers[0].pitch[0] }, - { prime.layers[0].offset[0] } }, - { (int) prime.width / 2, - (int) prime.height / 2, - { prime.objects[prime.layers[0].object_index[1]].fd, -1, -1, -1 }, - 0, - 0, - { prime.layers[0].pitch[1] }, - { prime.layers[0].offset[1] } }); - - if (!nv12_opt) { - return -1; - } - - auto sws_opt = egl::sws_t::make(width, height, frame->width, frame->height); - if (!sws_opt) { - return -1; - } - - this->sws = std::move(*sws_opt); - this->nv12 = std::move(*nv12_opt); - - return 0; } - void - apply_colorspace() override { - sws.apply_colorspace(colorspace); + std::vector> funcs{ + {(dyn::apiproc *)&maxNumEntrypoints, "vaMaxNumEntrypoints"}, + {(dyn::apiproc *)&queryConfigEntrypoints, "vaQueryConfigEntrypoints"}, + {(dyn::apiproc *)&terminate, "vaTerminate"}, + {(dyn::apiproc *)&initialize, "vaInitialize"}, + {(dyn::apiproc *)&errorStr, "vaErrorStr"}, + {(dyn::apiproc *)&setErrorCallback, "vaSetErrorCallback"}, + {(dyn::apiproc *)&setInfoCallback, "vaSetInfoCallback"}, + {(dyn::apiproc *)&queryVendorString, "vaQueryVendorString"}, + {(dyn::apiproc *)&exportSurfaceHandle, "vaExportSurfaceHandle"}, + }; + + if (dyn::load(handle, funcs)) { + return -1; } + funcs_loaded = true; + return 0; +} + +int init() { + if (init_main_va()) { + return -1; + } + + static void *handle{nullptr}; + static bool funcs_loaded = false; + + if (funcs_loaded) return 0; + + if (!handle) { + handle = dyn::handle({"libva-drm.so.2", "libva-drm.so"}); + if (!handle) { + return -1; + } + } + + std::vector> funcs{ + {(dyn::apiproc *)&getDisplayDRM, "vaGetDisplayDRM"}, + }; + + if (dyn::load(handle, funcs)) { + return -1; + } + + funcs_loaded = true; + return 0; +} + +int vaapi_init_avcodec_hardware_input_buffer( + platf::avcodec_encode_device_t *encode_device, AVBufferRef **hw_device_buf); + +class va_t : public platf::avcodec_encode_device_t { + public: + int init(int in_width, int in_height, file_t &&render_device) { + file = std::move(render_device); + + if (!va::initialize || !gbm::create_device) { + if (!va::initialize) + BOOST_LOG(warning) << "libva not initialized"sv; + if (!gbm::create_device) + BOOST_LOG(warning) << "libgbm not initialized"sv; + return -1; + } + + this->data = (void *)vaapi_init_avcodec_hardware_input_buffer; + + gbm.reset(gbm::create_device(file.el)); + if (!gbm) { + char string[1024]; + BOOST_LOG(error) + << "Couldn't create GBM device: ["sv + << strerror_r(errno, string, sizeof(string)) << ']'; + return -1; + } + + display = egl::make_display(gbm.get()); + if (!display) { + return -1; + } + + auto ctx_opt = egl::make_ctx(display.get()); + if (!ctx_opt) { + return -1; + } + + ctx = std::move(*ctx_opt); + + width = in_width; + height = in_height; + + return 0; + } + + int set_frame(AVFrame *frame, AVBufferRef *hw_frames_ctx) override { + this->hwframe.reset(frame); + this->frame = frame; + + if (!frame->buf[0]) { + if (av_hwframe_get_buffer(hw_frames_ctx, frame, 0)) { + BOOST_LOG(error) << "Couldn't get hwframe for VAAPI"sv; + return -1; + } + } + + va::DRMPRIMESurfaceDescriptor prime; + va::VASurfaceID surface = (std::uintptr_t)frame->data[3]; + + auto status = va::exportSurfaceHandle( + this->va_display, surface, va::SURFACE_ATTRIB_MEM_TYPE_DRM_PRIME_2, + va::EXPORT_SURFACE_WRITE_ONLY | va::EXPORT_SURFACE_COMPOSED_LAYERS, + &prime); + if (status) { + BOOST_LOG(error) << "Couldn't export va surface handle: ["sv + << (int)surface << "]: "sv << va::errorStr(status); + + return -1; + } + + // Keep track of file descriptors + std::array fds; + for (int x = 0; x < prime.num_objects; ++x) { + fds[x] = prime.objects[x].fd; + } + + auto nv12_opt = egl::import_target( + display.get(), std::move(fds), + {(int)prime.width, + (int)prime.height, + {prime.objects[prime.layers[0].object_index[0]].fd, -1, -1, -1}, + 0, + 0, + {prime.layers[0].pitch[0]}, + {prime.layers[0].offset[0]}}, + {(int)prime.width / 2, + (int)prime.height / 2, + {prime.objects[prime.layers[0].object_index[1]].fd, -1, -1, -1}, + 0, + 0, + {prime.layers[0].pitch[1]}, + {prime.layers[0].offset[1]}}); + + if (!nv12_opt) { + return -1; + } + + auto sws_opt = + egl::sws_t::make(width, height, frame->width, frame->height); + if (!sws_opt) { + return -1; + } + + this->sws = std::move(*sws_opt); + this->nv12 = std::move(*nv12_opt); + + return 0; + } + + void apply_colorspace() override { sws.apply_colorspace(colorspace); } + va::display_t::pointer va_display; file_t file; @@ -417,85 +423,83 @@ namespace va { egl::nv12_t nv12; int width, height; - }; +}; - class va_ram_t: public va_t { - public: - int - convert(platf::img_t &img) override { - sws.load_ram(img); +class va_ram_t : public va_t { + public: + int convert(platf::img_t &img) override { + sws.load_ram(img); - sws.convert(nv12->buf); - return 0; + sws.convert(nv12->buf); + return 0; } - }; +}; - class va_vram_t: public va_t { - public: - int - convert(platf::img_t &img) override { - auto &descriptor = (egl::img_descriptor_t &) img; +class va_vram_t : public va_t { + public: + int convert(platf::img_t &img) override { + auto &descriptor = (egl::img_descriptor_t &)img; - if (descriptor.sequence > sequence) { - sequence = descriptor.sequence; + if (descriptor.sequence > sequence) { + sequence = descriptor.sequence; - rgb = egl::rgb_t {}; + rgb = egl::rgb_t{}; - auto rgb_opt = egl::import_source(display.get(), descriptor.sd); + auto rgb_opt = egl::import_source(display.get(), descriptor.sd); - if (!rgb_opt) { - return -1; + if (!rgb_opt) { + return -1; + } + + rgb = std::move(*rgb_opt); } - rgb = std::move(*rgb_opt); - } + sws.load_vram(descriptor, offset_x, offset_y, rgb->tex[0]); - sws.load_vram(descriptor, offset_x, offset_y, rgb->tex[0]); - - sws.convert(nv12->buf); - return 0; + sws.convert(nv12->buf); + return 0; } - int - init(int in_width, int in_height, file_t &&render_device, int offset_x, int offset_y) { - if (va_t::init(in_width, in_height, std::move(render_device))) { - return -1; - } + int init(int in_width, int in_height, file_t &&render_device, int offset_x, + int offset_y) { + if (va_t::init(in_width, in_height, std::move(render_device))) { + return -1; + } - sequence = 0; + sequence = 0; - this->offset_x = offset_x; - this->offset_y = offset_y; + this->offset_x = offset_x; + this->offset_y = offset_y; - return 0; + return 0; } std::uint64_t sequence; egl::rgb_t rgb; int offset_x, offset_y; - }; +}; - /** - * This is a private structure of FFmpeg, I need this to manually create - * a VAAPI hardware context - * - * xdisplay will not be used internally by FFmpeg - */ - typedef struct VAAPIDevicePriv { +/** + * This is a private structure of FFmpeg, I need this to manually create + * a VAAPI hardware context + * + * xdisplay will not be used internally by FFmpeg + */ +typedef struct VAAPIDevicePriv { union { - void *xdisplay; - int fd; + void *xdisplay; + int fd; } drm; int drm_fd; - } VAAPIDevicePriv; +} VAAPIDevicePriv; - /** - * VAAPI connection details. - * - * Allocated as AVHWDeviceContext.hwctx - */ - typedef struct AVVAAPIDeviceContext { +/** + * VAAPI connection details. + * + * Allocated as AVHWDeviceContext.hwctx + */ +typedef struct AVVAAPIDeviceContext { /** * The VADisplay handle, to be filled by the user. */ @@ -508,52 +512,54 @@ namespace va { * operations using VAAPI with the same VADisplay. */ unsigned int driver_quirks; - } AVVAAPIDeviceContext; +} AVVAAPIDeviceContext; - static void - __log(void *level, const char *msg) { - BOOST_LOG(*(boost::log::sources::severity_logger *) level) << msg; - } +static void __log(void *level, const char *msg) { + BOOST_LOG(*(boost::log::sources::severity_logger *)level) << msg; +} - static void - vaapi_hwdevice_ctx_free(AVHWDeviceContext *ctx) { - auto hwctx = (AVVAAPIDeviceContext *) ctx->hwctx; - auto priv = (VAAPIDevicePriv *) ctx->user_opaque; +static void vaapi_hwdevice_ctx_free(AVHWDeviceContext *ctx) { + auto hwctx = (AVVAAPIDeviceContext *)ctx->hwctx; + auto priv = (VAAPIDevicePriv *)ctx->user_opaque; vaTerminate(hwctx->display); close(priv->drm_fd); av_freep(&priv); - } +} - int - vaapi_init_avcodec_hardware_input_buffer(platf::avcodec_encode_device_t *base, AVBufferRef **hw_device_buf) { +int vaapi_init_avcodec_hardware_input_buffer( + platf::avcodec_encode_device_t *base, AVBufferRef **hw_device_buf) { if (!va::initialize) { - BOOST_LOG(warning) << "libva not loaded"sv; - return -1; + BOOST_LOG(warning) << "libva not loaded"sv; + return -1; } if (!va::getDisplayDRM) { - BOOST_LOG(warning) << "libva-drm not loaded"sv; - return -1; + BOOST_LOG(warning) << "libva-drm not loaded"sv; + return -1; } - auto va = (va::va_t *) base; + auto va = (va::va_t *)base; auto fd = dup(va->file.el); - auto *priv = (VAAPIDevicePriv *) av_mallocz(sizeof(VAAPIDevicePriv)); + auto *priv = (VAAPIDevicePriv *)av_mallocz(sizeof(VAAPIDevicePriv)); priv->drm_fd = fd; auto fg = util::fail_guard([fd, priv]() { - close(fd); - av_free(priv); + close(fd); + av_free(priv); }); - va::display_t display { va::getDisplayDRM(fd) }; + va::display_t display{va::getDisplayDRM(fd)}; if (!display) { - auto render_device = config::video.adapter_name.empty() ? "/dev/dri/renderD128" : config::video.adapter_name.c_str(); + auto render_device = config::video.adapter_name.empty() + ? "/dev/dri/renderD128" + : config::video.adapter_name.c_str(); - BOOST_LOG(error) << "Couldn't open a va display from DRM with device: "sv << render_device; - return -1; + BOOST_LOG(error) + << "Couldn't open a va display from DRM with device: "sv + << render_device; + return -1; } va->va_display = display.get(); @@ -564,17 +570,20 @@ namespace va { int major, minor; auto status = va::initialize(display.get(), &major, &minor); if (status) { - BOOST_LOG(error) << "Couldn't initialize va display: "sv << va::errorStr(status); - return -1; + BOOST_LOG(error) << "Couldn't initialize va display: "sv + << va::errorStr(status); + return -1; } - BOOST_LOG(debug) << "vaapi vendor: "sv << va::queryVendorString(display.get()); + BOOST_LOG(debug) << "vaapi vendor: "sv + << va::queryVendorString(display.get()); *hw_device_buf = av_hwdevice_ctx_alloc(AV_HWDEVICE_TYPE_VAAPI); - auto ctx = (AVHWDeviceContext *) (*hw_device_buf)->data; - auto hwctx = (AVVAAPIDeviceContext *) ctx->hwctx; + auto ctx = (AVHWDeviceContext *)(*hw_device_buf)->data; + auto hwctx = (AVVAAPIDeviceContext *)ctx->hwctx; - // Ownership of the VADisplay and DRM fd is now ours to manage via the free() function + // Ownership of the VADisplay and DRM fd is now ours to manage via the + // free() function hwctx->display = display.release(); ctx->user_opaque = priv; ctx->free = vaapi_hwdevice_ctx_free; @@ -582,115 +591,129 @@ namespace va { auto err = av_hwdevice_ctx_init(*hw_device_buf); if (err) { - char err_str[AV_ERROR_MAX_STRING_SIZE] { 0 }; - BOOST_LOG(error) << "Failed to create FFMpeg hardware device context: "sv << av_make_error_string(err_str, AV_ERROR_MAX_STRING_SIZE, err); + char err_str[AV_ERROR_MAX_STRING_SIZE]{0}; + BOOST_LOG(error) + << "Failed to create FFMpeg hardware device context: "sv + << av_make_error_string(err_str, AV_ERROR_MAX_STRING_SIZE, err); - return err; + return err; } return 0; - } +} - static bool - query(display_t::pointer display, profile_e profile) { +static bool query(display_t::pointer display, profile_e profile) { std::vector entrypoints; entrypoints.resize(maxNumEntrypoints(display)); int count; - auto status = queryConfigEntrypoints(display, profile, entrypoints.data(), &count); + auto status = + queryConfigEntrypoints(display, profile, entrypoints.data(), &count); if (status) { - BOOST_LOG(error) << "Couldn't query entrypoints: "sv << va::errorStr(status); - return false; + BOOST_LOG(error) << "Couldn't query entrypoints: "sv + << va::errorStr(status); + return false; } entrypoints.resize(count); for (auto entrypoint : entrypoints) { - if (entrypoint == entry_e::EncSlice || entrypoint == entry_e::EncSliceLP) { - return true; - } + if (entrypoint == entry_e::EncSlice || + entrypoint == entry_e::EncSliceLP) { + return true; + } } return false; - } +} - bool - validate(int fd) { +bool validate(int fd) { if (init()) { - return false; + return false; } - va::display_t display { va::getDisplayDRM(fd) }; + va::display_t display{va::getDisplayDRM(fd)}; if (!display) { - char string[1024]; + char string[1024]; - auto bytes = readlink(("/proc/self/fd/" + std::to_string(fd)).c_str(), string, sizeof(string)); + auto bytes = readlink(("/proc/self/fd/" + std::to_string(fd)).c_str(), + string, sizeof(string)); - std::string_view render_device { string, (std::size_t) bytes }; + std::string_view render_device{string, (std::size_t)bytes}; - BOOST_LOG(error) << "Couldn't open a va display from DRM with device: "sv << render_device; - return false; + BOOST_LOG(error) + << "Couldn't open a va display from DRM with device: "sv + << render_device; + return false; } int major, minor; auto status = initialize(display.get(), &major, &minor); if (status) { - BOOST_LOG(error) << "Couldn't initialize va display: "sv << va::errorStr(status); - return false; + BOOST_LOG(error) << "Couldn't initialize va display: "sv + << va::errorStr(status); + return false; } if (!query(display.get(), profile_e::H264Main)) { - return false; + return false; } - if (video::active_hevc_mode > 1 && !query(display.get(), profile_e::HEVCMain)) { - return false; + if (video::active_hevc_mode > 1 && + !query(display.get(), profile_e::HEVCMain)) { + return false; } - if (video::active_hevc_mode > 2 && !query(display.get(), profile_e::HEVCMain10)) { - return false; + if (video::active_hevc_mode > 2 && + !query(display.get(), profile_e::HEVCMain10)) { + return false; } return true; - } +} - std::unique_ptr - make_avcodec_encode_device(int width, int height, file_t &&card, int offset_x, int offset_y, bool vram) { +std::unique_ptr make_avcodec_encode_device( + int width, int height, file_t &&card, int offset_x, int offset_y, + bool vram) { if (vram) { - auto egl = std::make_unique(); - if (egl->init(width, height, std::move(card), offset_x, offset_y)) { - return nullptr; - } + auto egl = std::make_unique(); + if (egl->init(width, height, std::move(card), offset_x, offset_y)) { + return nullptr; + } - return egl; + return egl; } else { - auto egl = std::make_unique(); - if (egl->init(width, height, std::move(card))) { - return nullptr; - } + auto egl = std::make_unique(); + if (egl->init(width, height, std::move(card))) { + return nullptr; + } - return egl; + return egl; } - } +} - std::unique_ptr - make_avcodec_encode_device(int width, int height, int offset_x, int offset_y, bool vram) { - auto render_device = config::video.adapter_name.empty() ? "/dev/dri/renderD128" : config::video.adapter_name.c_str(); +std::unique_ptr make_avcodec_encode_device( + int width, int height, int offset_x, int offset_y, bool vram) { + auto render_device = config::video.adapter_name.empty() + ? "/dev/dri/renderD128" + : config::video.adapter_name.c_str(); file_t file = open(render_device, O_RDWR); if (file.el < 0) { - char string[1024]; - BOOST_LOG(error) << "Couldn't open "sv << render_device << ": " << strerror_r(errno, string, sizeof(string)); + char string[1024]; + BOOST_LOG(error) << "Couldn't open "sv << render_device << ": " + << strerror_r(errno, string, sizeof(string)); - return nullptr; + return nullptr; } - return make_avcodec_encode_device(width, height, std::move(file), offset_x, offset_y, vram); - } + return make_avcodec_encode_device(width, height, std::move(file), offset_x, + offset_y, vram); +} - std::unique_ptr - make_avcodec_encode_device(int width, int height, bool vram) { +std::unique_ptr make_avcodec_encode_device( + int width, int height, bool vram) { return make_avcodec_encode_device(width, height, 0, 0, vram); - } +} } // namespace va diff --git a/src/platform/linux/vaapi.h b/src/platform/linux/vaapi.h index 95760e55..f2d553db 100644 --- a/src/platform/linux/vaapi.h +++ b/src/platform/linux/vaapi.h @@ -8,27 +8,27 @@ #include "src/platform/common.h" namespace egl { - struct surface_descriptor_t; +struct surface_descriptor_t; } namespace va { - /** - * Width --> Width of the image - * Height --> Height of the image - * offset_x --> Horizontal offset of the image in the texture - * offset_y --> Vertical offset of the image in the texture - * file_t card --> The file descriptor of the render device used for encoding - */ - std::unique_ptr - make_avcodec_encode_device(int width, int height, bool vram); - std::unique_ptr - make_avcodec_encode_device(int width, int height, int offset_x, int offset_y, bool vram); - std::unique_ptr - make_avcodec_encode_device(int width, int height, file_t &&card, int offset_x, int offset_y, bool vram); +/** + * Width --> Width of the image + * Height --> Height of the image + * offset_x --> Horizontal offset of the image in the texture + * offset_y --> Vertical offset of the image in the texture + * file_t card --> The file descriptor of the render device used for encoding + */ +std::unique_ptr make_avcodec_encode_device( + int width, int height, bool vram); +std::unique_ptr make_avcodec_encode_device( + int width, int height, int offset_x, int offset_y, bool vram); +std::unique_ptr make_avcodec_encode_device( + int width, int height, file_t &&card, int offset_x, int offset_y, + bool vram); - // Ensure the render device pointed to by fd is capable of encoding h264 with the hevc_mode configured - bool - validate(int fd); +// Ensure the render device pointed to by fd is capable of encoding h264 with +// the hevc_mode configured +bool validate(int fd); - int - init(); +int init(); } // namespace va diff --git a/src/platform/linux/wayland.cpp b/src/platform/linux/wayland.cpp index d601ba95..729a6e94 100644 --- a/src/platform/linux/wayland.cpp +++ b/src/platform/linux/wayland.cpp @@ -2,6 +2,8 @@ * @file src/platform/linux/wayland.cpp * @brief todo */ +#include "wayland.h" + #include #include @@ -12,7 +14,6 @@ #include "src/platform/common.h" #include "src/round_robin.h" #include "src/utility.h" -#include "wayland.h" extern const wl_interface wl_output_interface; @@ -25,241 +26,218 @@ using namespace std::literals; namespace wl { - // Helper to call C++ method from wayland C callback - template - static auto - classCall(void *data, Params... params) -> decltype(((*reinterpret_cast(data)).*m)(params...)) { +// Helper to call C++ method from wayland C callback +template +static auto classCall(void *data, Params... params) + -> decltype(((*reinterpret_cast(data)).*m)(params...)) { return ((*reinterpret_cast(data)).*m)(params...); - } +} #define CLASS_CALL(c, m) classCall - int - display_t::init(const char *display_name) { +int display_t::init(const char *display_name) { if (!display_name) { - display_name = std::getenv("WAYLAND_DISPLAY"); + display_name = std::getenv("WAYLAND_DISPLAY"); } if (!display_name) { - BOOST_LOG(error) << "Environment variable WAYLAND_DISPLAY has not been defined"sv; - return -1; + BOOST_LOG(error) + << "Environment variable WAYLAND_DISPLAY has not been defined"sv; + return -1; } display_internal.reset(wl_display_connect(display_name)); if (!display_internal) { - BOOST_LOG(error) << "Couldn't connect to Wayland display: "sv << display_name; - return -1; + BOOST_LOG(error) << "Couldn't connect to Wayland display: "sv + << display_name; + return -1; } BOOST_LOG(info) << "Found display ["sv << display_name << ']'; return 0; - } +} - void - display_t::roundtrip() { - wl_display_roundtrip(display_internal.get()); - } +void display_t::roundtrip() { wl_display_roundtrip(display_internal.get()); } - wl_registry * - display_t::registry() { +wl_registry *display_t::registry() { return wl_display_get_registry(display_internal.get()); - } +} - inline monitor_t::monitor_t(wl_output *output): - output { output }, listener { - &CLASS_CALL(monitor_t, xdg_position), - &CLASS_CALL(monitor_t, xdg_size), - &CLASS_CALL(monitor_t, xdg_done), - &CLASS_CALL(monitor_t, xdg_name), - &CLASS_CALL(monitor_t, xdg_description) - } {} +inline monitor_t::monitor_t(wl_output *output) + : output{output}, + listener{&CLASS_CALL(monitor_t, xdg_position), + &CLASS_CALL(monitor_t, xdg_size), + &CLASS_CALL(monitor_t, xdg_done), + &CLASS_CALL(monitor_t, xdg_name), + &CLASS_CALL(monitor_t, xdg_description)} {} - inline void - monitor_t::xdg_name(zxdg_output_v1 *, const char *name) { +inline void monitor_t::xdg_name(zxdg_output_v1 *, const char *name) { this->name = name; BOOST_LOG(info) << "Name: "sv << this->name; - } +} - void - monitor_t::xdg_description(zxdg_output_v1 *, const char *description) { +void monitor_t::xdg_description(zxdg_output_v1 *, const char *description) { this->description = description; BOOST_LOG(info) << "Found monitor: "sv << this->description; - } +} - void - monitor_t::xdg_position(zxdg_output_v1 *, std::int32_t x, std::int32_t y) { +void monitor_t::xdg_position(zxdg_output_v1 *, std::int32_t x, std::int32_t y) { viewport.offset_x = x; viewport.offset_y = y; BOOST_LOG(info) << "Offset: "sv << x << 'x' << y; - } +} - void - monitor_t::xdg_size(zxdg_output_v1 *, std::int32_t width, std::int32_t height) { +void monitor_t::xdg_size(zxdg_output_v1 *, std::int32_t width, + std::int32_t height) { viewport.width = width; viewport.height = height; BOOST_LOG(info) << "Resolution: "sv << width << 'x' << height; - } +} - void - monitor_t::xdg_done(zxdg_output_v1 *) { - BOOST_LOG(info) << "All info about monitor ["sv << name << "] has been send"sv; - } +void monitor_t::xdg_done(zxdg_output_v1 *) { + BOOST_LOG(info) << "All info about monitor ["sv << name + << "] has been send"sv; +} - void - monitor_t::listen(zxdg_output_manager_v1 *output_manager) { - auto xdg_output = zxdg_output_manager_v1_get_xdg_output(output_manager, output); +void monitor_t::listen(zxdg_output_manager_v1 *output_manager) { + auto xdg_output = + zxdg_output_manager_v1_get_xdg_output(output_manager, output); zxdg_output_v1_add_listener(xdg_output, &listener, this); - } +} - interface_t::interface_t() noexcept - : - output_manager { nullptr }, - listener { - &CLASS_CALL(interface_t, add_interface), - &CLASS_CALL(interface_t, del_interface) - } {} +interface_t::interface_t() noexcept + : output_manager{nullptr}, + listener{&CLASS_CALL(interface_t, add_interface), + &CLASS_CALL(interface_t, del_interface)} {} - void - interface_t::listen(wl_registry *registry) { +void interface_t::listen(wl_registry *registry) { wl_registry_add_listener(registry, &listener, this); - } +} - void - interface_t::add_interface(wl_registry *registry, std::uint32_t id, const char *interface, std::uint32_t version) { - BOOST_LOG(debug) << "Available interface: "sv << interface << '(' << id << ") version "sv << version; +void interface_t::add_interface(wl_registry *registry, std::uint32_t id, + const char *interface, std::uint32_t version) { + BOOST_LOG(debug) << "Available interface: "sv << interface << '(' << id + << ") version "sv << version; if (!std::strcmp(interface, wl_output_interface.name)) { - BOOST_LOG(info) << "Found interface: "sv << interface << '(' << id << ") version "sv << version; - monitors.emplace_back( - std::make_unique( - (wl_output *) wl_registry_bind(registry, id, &wl_output_interface, version))); - } - else if (!std::strcmp(interface, zxdg_output_manager_v1_interface.name)) { - BOOST_LOG(info) << "Found interface: "sv << interface << '(' << id << ") version "sv << version; - output_manager = (zxdg_output_manager_v1 *) wl_registry_bind(registry, id, &zxdg_output_manager_v1_interface, version); + BOOST_LOG(info) << "Found interface: "sv << interface << '(' << id + << ") version "sv << version; + monitors.emplace_back( + std::make_unique((wl_output *)wl_registry_bind( + registry, id, &wl_output_interface, version))); + } else if (!std::strcmp(interface, zxdg_output_manager_v1_interface.name)) { + BOOST_LOG(info) << "Found interface: "sv << interface << '(' << id + << ") version "sv << version; + output_manager = (zxdg_output_manager_v1 *)wl_registry_bind( + registry, id, &zxdg_output_manager_v1_interface, version); - this->interface[XDG_OUTPUT] = true; - } - else if (!std::strcmp(interface, zwlr_export_dmabuf_manager_v1_interface.name)) { - BOOST_LOG(info) << "Found interface: "sv << interface << '(' << id << ") version "sv << version; - dmabuf_manager = (zwlr_export_dmabuf_manager_v1 *) wl_registry_bind(registry, id, &zwlr_export_dmabuf_manager_v1_interface, version); + this->interface[XDG_OUTPUT] = true; + } else if (!std::strcmp(interface, + zwlr_export_dmabuf_manager_v1_interface.name)) { + BOOST_LOG(info) << "Found interface: "sv << interface << '(' << id + << ") version "sv << version; + dmabuf_manager = (zwlr_export_dmabuf_manager_v1 *)wl_registry_bind( + registry, id, &zwlr_export_dmabuf_manager_v1_interface, version); - this->interface[WLR_EXPORT_DMABUF] = true; + this->interface[WLR_EXPORT_DMABUF] = true; } - } +} - void - interface_t::del_interface(wl_registry *registry, uint32_t id) { +void interface_t::del_interface(wl_registry *registry, uint32_t id) { BOOST_LOG(info) << "Delete: "sv << id; - } +} - dmabuf_t::dmabuf_t(): - status { READY }, frames {}, current_frame { &frames[0] }, listener { - &CLASS_CALL(dmabuf_t, frame), - &CLASS_CALL(dmabuf_t, object), - &CLASS_CALL(dmabuf_t, ready), - &CLASS_CALL(dmabuf_t, cancel) - } { - } +dmabuf_t::dmabuf_t() + : status{READY}, + frames{}, + current_frame{&frames[0]}, + listener{&CLASS_CALL(dmabuf_t, frame), &CLASS_CALL(dmabuf_t, object), + &CLASS_CALL(dmabuf_t, ready), &CLASS_CALL(dmabuf_t, cancel)} {} - void - dmabuf_t::listen(zwlr_export_dmabuf_manager_v1 *dmabuf_manager, wl_output *output, bool blend_cursor) { - auto frame = zwlr_export_dmabuf_manager_v1_capture_output(dmabuf_manager, blend_cursor, output); +void dmabuf_t::listen(zwlr_export_dmabuf_manager_v1 *dmabuf_manager, + wl_output *output, bool blend_cursor) { + auto frame = zwlr_export_dmabuf_manager_v1_capture_output( + dmabuf_manager, blend_cursor, output); zwlr_export_dmabuf_frame_v1_add_listener(frame, &listener, this); status = WAITING; - } +} - dmabuf_t::~dmabuf_t() { +dmabuf_t::~dmabuf_t() { for (auto &frame : frames) { - frame.destroy(); + frame.destroy(); } - } +} - void - dmabuf_t::frame( - zwlr_export_dmabuf_frame_v1 *frame, - std::uint32_t width, std::uint32_t height, - std::uint32_t x, std::uint32_t y, - std::uint32_t buffer_flags, std::uint32_t flags, - std::uint32_t format, - std::uint32_t high, std::uint32_t low, - std::uint32_t obj_count) { +void dmabuf_t::frame(zwlr_export_dmabuf_frame_v1 *frame, std::uint32_t width, + std::uint32_t height, std::uint32_t x, std::uint32_t y, + std::uint32_t buffer_flags, std::uint32_t flags, + std::uint32_t format, std::uint32_t high, + std::uint32_t low, std::uint32_t obj_count) { auto next_frame = get_next_frame(); next_frame->sd.fourcc = format; next_frame->sd.width = width; next_frame->sd.height = height; - next_frame->sd.modifier = (((std::uint64_t) high) << 32) | low; - } + next_frame->sd.modifier = (((std::uint64_t)high) << 32) | low; +} - void - dmabuf_t::object( - zwlr_export_dmabuf_frame_v1 *frame, - std::uint32_t index, - std::int32_t fd, - std::uint32_t size, - std::uint32_t offset, - std::uint32_t stride, - std::uint32_t plane_index) { +void dmabuf_t::object(zwlr_export_dmabuf_frame_v1 *frame, std::uint32_t index, + std::int32_t fd, std::uint32_t size, std::uint32_t offset, + std::uint32_t stride, std::uint32_t plane_index) { auto next_frame = get_next_frame(); next_frame->sd.fds[plane_index] = fd; next_frame->sd.pitches[plane_index] = stride; next_frame->sd.offsets[plane_index] = offset; - } +} - void - dmabuf_t::ready( - zwlr_export_dmabuf_frame_v1 *frame, - std::uint32_t tv_sec_hi, std::uint32_t tv_sec_lo, std::uint32_t tv_nsec) { +void dmabuf_t::ready(zwlr_export_dmabuf_frame_v1 *frame, + std::uint32_t tv_sec_hi, std::uint32_t tv_sec_lo, + std::uint32_t tv_nsec) { zwlr_export_dmabuf_frame_v1_destroy(frame); current_frame->destroy(); current_frame = get_next_frame(); status = READY; - } +} - void - dmabuf_t::cancel( - zwlr_export_dmabuf_frame_v1 *frame, - std::uint32_t reason) { +void dmabuf_t::cancel(zwlr_export_dmabuf_frame_v1 *frame, + std::uint32_t reason) { zwlr_export_dmabuf_frame_v1_destroy(frame); auto next_frame = get_next_frame(); next_frame->destroy(); status = REINIT; - } +} - void - frame_t::destroy() { +void frame_t::destroy() { for (auto x = 0; x < 4; ++x) { - if (sd.fds[x] >= 0) { - close(sd.fds[x]); + if (sd.fds[x] >= 0) { + close(sd.fds[x]); - sd.fds[x] = -1; - } + sd.fds[x] = -1; + } } - } +} - frame_t::frame_t() { +frame_t::frame_t() { // File descriptors aren't open std::fill_n(sd.fds, 4, -1); - }; +}; - std::vector> - monitors(const char *display_name) { +std::vector> monitors(const char *display_name) { display_t display; if (display.init(display_name)) { - return {}; + return {}; } interface_t interface; @@ -268,32 +246,30 @@ namespace wl { display.roundtrip(); if (!interface[interface_t::XDG_OUTPUT]) { - BOOST_LOG(error) << "Missing Wayland wire XDG_OUTPUT"sv; - return {}; + BOOST_LOG(error) << "Missing Wayland wire XDG_OUTPUT"sv; + return {}; } for (auto &monitor : interface.monitors) { - monitor->listen(interface.output_manager); + monitor->listen(interface.output_manager); } display.roundtrip(); return std::move(interface.monitors); - } +} - static bool - validate() { +static bool validate() { display_t display; return display.init() == 0; - } +} - int - init() { +int init() { static bool validated = validate(); return !validated; - } +} } // namespace wl diff --git a/src/platform/linux/wayland.h b/src/platform/linux/wayland.h index a4c3aef1..ee7afa8b 100644 --- a/src/platform/linux/wayland.h +++ b/src/platform/linux/wayland.h @@ -7,8 +7,8 @@ #include #ifdef SUNSHINE_BUILD_WAYLAND - #include - #include +#include +#include #endif #include "graphics.h" @@ -20,73 +20,54 @@ #ifdef SUNSHINE_BUILD_WAYLAND namespace wl { - using display_internal_t = util::safe_ptr; +using display_internal_t = util::safe_ptr; - class frame_t { - public: +class frame_t { + public: frame_t(); egl::surface_descriptor_t sd; - void - destroy(); - }; + void destroy(); +}; - class dmabuf_t { - public: +class dmabuf_t { + public: enum status_e { - WAITING, - READY, - REINIT, + WAITING, + READY, + REINIT, }; dmabuf_t(dmabuf_t &&) = delete; dmabuf_t(const dmabuf_t &) = delete; - dmabuf_t & - operator=(const dmabuf_t &) = delete; - dmabuf_t & - operator=(dmabuf_t &&) = delete; + dmabuf_t &operator=(const dmabuf_t &) = delete; + dmabuf_t &operator=(dmabuf_t &&) = delete; dmabuf_t(); - void - listen(zwlr_export_dmabuf_manager_v1 *dmabuf_manager, wl_output *output, bool blend_cursor = false); + void listen(zwlr_export_dmabuf_manager_v1 *dmabuf_manager, + wl_output *output, bool blend_cursor = false); ~dmabuf_t(); - void - frame( - zwlr_export_dmabuf_frame_v1 *frame, - std::uint32_t width, std::uint32_t height, - std::uint32_t x, std::uint32_t y, - std::uint32_t buffer_flags, std::uint32_t flags, - std::uint32_t format, - std::uint32_t high, std::uint32_t low, - std::uint32_t obj_count); + void frame(zwlr_export_dmabuf_frame_v1 *frame, std::uint32_t width, + std::uint32_t height, std::uint32_t x, std::uint32_t y, + std::uint32_t buffer_flags, std::uint32_t flags, + std::uint32_t format, std::uint32_t high, std::uint32_t low, + std::uint32_t obj_count); - void - object( - zwlr_export_dmabuf_frame_v1 *frame, - std::uint32_t index, - std::int32_t fd, - std::uint32_t size, - std::uint32_t offset, - std::uint32_t stride, - std::uint32_t plane_index); + void object(zwlr_export_dmabuf_frame_v1 *frame, std::uint32_t index, + std::int32_t fd, std::uint32_t size, std::uint32_t offset, + std::uint32_t stride, std::uint32_t plane_index); - void - ready( - zwlr_export_dmabuf_frame_v1 *frame, - std::uint32_t tv_sec_hi, std::uint32_t tv_sec_lo, std::uint32_t tv_nsec); + void ready(zwlr_export_dmabuf_frame_v1 *frame, std::uint32_t tv_sec_hi, + std::uint32_t tv_sec_lo, std::uint32_t tv_nsec); - void - cancel( - zwlr_export_dmabuf_frame_v1 *frame, - std::uint32_t reason); + void cancel(zwlr_export_dmabuf_frame_v1 *frame, std::uint32_t reason); - inline frame_t * - get_next_frame() { - return current_frame == &frames[0] ? &frames[1] : &frames[0]; + inline frame_t *get_next_frame() { + return current_frame == &frames[0] ? &frames[1] : &frames[0]; } status_e status; @@ -95,33 +76,25 @@ namespace wl { frame_t *current_frame; zwlr_export_dmabuf_frame_v1_listener listener; - }; +}; - class monitor_t { - public: +class monitor_t { + public: monitor_t(monitor_t &&) = delete; monitor_t(const monitor_t &) = delete; - monitor_t & - operator=(const monitor_t &) = delete; - monitor_t & - operator=(monitor_t &&) = delete; + monitor_t &operator=(const monitor_t &) = delete; + monitor_t &operator=(monitor_t &&) = delete; monitor_t(wl_output *output); - void - xdg_name(zxdg_output_v1 *, const char *name); - void - xdg_description(zxdg_output_v1 *, const char *description); - void - xdg_position(zxdg_output_v1 *, std::int32_t x, std::int32_t y); - void - xdg_size(zxdg_output_v1 *, std::int32_t width, std::int32_t height); - void - xdg_done(zxdg_output_v1 *); + void xdg_name(zxdg_output_v1 *, const char *name); + void xdg_description(zxdg_output_v1 *, const char *description); + void xdg_position(zxdg_output_v1 *, std::int32_t x, std::int32_t y); + void xdg_size(zxdg_output_v1 *, std::int32_t width, std::int32_t height); + void xdg_done(zxdg_output_v1 *); - void - listen(zxdg_output_manager_v1 *output_manager); + void listen(zxdg_output_manager_v1 *output_manager); wl_output *output; @@ -131,87 +104,74 @@ namespace wl { platf::touch_port_t viewport; zxdg_output_v1_listener listener; - }; +}; - class interface_t { +class interface_t { struct bind_t { - std::uint32_t id; - std::uint32_t version; + std::uint32_t id; + std::uint32_t version; }; - public: + public: enum interface_e { - XDG_OUTPUT, - WLR_EXPORT_DMABUF, - MAX_INTERFACES, + XDG_OUTPUT, + WLR_EXPORT_DMABUF, + MAX_INTERFACES, }; interface_t(interface_t &&) = delete; interface_t(const interface_t &) = delete; - interface_t & - operator=(const interface_t &) = delete; - interface_t & - operator=(interface_t &&) = delete; + interface_t &operator=(const interface_t &) = delete; + interface_t &operator=(interface_t &&) = delete; interface_t() noexcept; - void - listen(wl_registry *registry); + void listen(wl_registry *registry); std::vector> monitors; zwlr_export_dmabuf_manager_v1 *dmabuf_manager; zxdg_output_manager_v1 *output_manager; - bool - operator[](interface_e bit) const { - return interface[bit]; - } + bool operator[](interface_e bit) const { return interface[bit]; } - private: - void - add_interface(wl_registry *registry, std::uint32_t id, const char *interface, std::uint32_t version); - void - del_interface(wl_registry *registry, uint32_t id); + private: + void add_interface(wl_registry *registry, std::uint32_t id, + const char *interface, std::uint32_t version); + void del_interface(wl_registry *registry, uint32_t id); std::bitset interface; wl_registry_listener listener; - }; +}; - class display_t { - public: +class display_t { + public: /** * Initialize display with display_name - * If display_name == nullptr -> display_name = std::getenv("WAYLAND_DISPLAY") + * If display_name == nullptr -> display_name = + * std::getenv("WAYLAND_DISPLAY") */ - int - init(const char *display_name = nullptr); + int init(const char *display_name = nullptr); // Roundtrip with Wayland connection - void - roundtrip(); + void roundtrip(); // Get the registry associated with the display // No need to manually free the registry - wl_registry * - registry(); + wl_registry *registry(); - inline display_internal_t::pointer - get() { - return display_internal.get(); - } + inline display_internal_t::pointer get() { return display_internal.get(); } - private: + private: display_internal_t display_internal; - }; +}; - std::vector> - monitors(const char *display_name = nullptr); +std::vector> monitors( + const char *display_name = nullptr); - int - init(); +int init(); } // namespace wl #else @@ -219,20 +179,17 @@ struct wl_output; struct zxdg_output_manager_v1; namespace wl { - class monitor_t { - public: +class monitor_t { + public: monitor_t(monitor_t &&) = delete; monitor_t(const monitor_t &) = delete; - monitor_t & - operator=(const monitor_t &) = delete; - monitor_t & - operator=(monitor_t &&) = delete; + monitor_t &operator=(const monitor_t &) = delete; + monitor_t &operator=(monitor_t &&) = delete; monitor_t(wl_output *output); - void - listen(zxdg_output_manager_v1 *output_manager); + void listen(zxdg_output_manager_v1 *output_manager); wl_output *output; @@ -240,12 +197,13 @@ namespace wl { std::string description; platf::touch_port_t viewport; - }; +}; - inline std::vector> - monitors(const char *display_name = nullptr) { return {}; } +inline std::vector> monitors( + const char *display_name = nullptr) { + return {}; +} - inline int - init() { return -1; } +inline int init() { return -1; } } // namespace wl #endif diff --git a/src/platform/linux/wlgrab.cpp b/src/platform/linux/wlgrab.cpp index b57b332d..afcec141 100644 --- a/src/platform/linux/wlgrab.cpp +++ b/src/platform/linux/wlgrab.cpp @@ -2,110 +2,110 @@ * @file src/platform/linux/wlgrab.cpp * @brief todo */ -#include "src/platform/common.h" - #include "src/main.h" +#include "src/platform/common.h" #include "src/video.h" #include "vaapi.h" #include "wayland.h" using namespace std::literals; namespace wl { - static int env_width; - static int env_height; +static int env_width; +static int env_height; - struct img_t: public platf::img_t { +struct img_t : public platf::img_t { ~img_t() override { - delete[] data; - data = nullptr; + delete[] data; + data = nullptr; } - }; +}; - class wlr_t: public platf::display_t { - public: - int - init(platf::mem_type_e hwdevice_type, const std::string &display_name, const ::video::config_t &config) { - delay = std::chrono::nanoseconds { 1s } / config.framerate; - mem_type = hwdevice_type; +class wlr_t : public platf::display_t { + public: + int init(platf::mem_type_e hwdevice_type, const std::string &display_name, + const ::video::config_t &config) { + delay = std::chrono::nanoseconds{1s} / config.framerate; + mem_type = hwdevice_type; - if (display.init()) { - return -1; - } - - interface.listen(display.registry()); - - display.roundtrip(); - - if (!interface[wl::interface_t::XDG_OUTPUT]) { - BOOST_LOG(error) << "Missing Wayland wire for xdg_output"sv; - return -1; - } - - if (!interface[wl::interface_t::WLR_EXPORT_DMABUF]) { - BOOST_LOG(error) << "Missing Wayland wire for wlr-export-dmabuf"sv; - return -1; - } - - auto monitor = interface.monitors[0].get(); - - if (!display_name.empty()) { - auto streamedMonitor = util::from_view(display_name); - - if (streamedMonitor >= 0 && streamedMonitor < interface.monitors.size()) { - monitor = interface.monitors[streamedMonitor].get(); + if (display.init()) { + return -1; } - } - monitor->listen(interface.output_manager); + interface.listen(display.registry()); - display.roundtrip(); - - output = monitor->output; - - offset_x = monitor->viewport.offset_x; - offset_y = monitor->viewport.offset_y; - width = monitor->viewport.width; - height = monitor->viewport.height; - - this->env_width = ::wl::env_width; - this->env_height = ::wl::env_height; - - BOOST_LOG(info) << "Selected monitor ["sv << monitor->description << "] for streaming"sv; - BOOST_LOG(debug) << "Offset: "sv << offset_x << 'x' << offset_y; - BOOST_LOG(debug) << "Resolution: "sv << width << 'x' << height; - BOOST_LOG(debug) << "Desktop Resolution: "sv << env_width << 'x' << env_height; - - return 0; - } - - int - dummy_img(platf::img_t *img) override { - return 0; - } - - inline platf::capture_e - snapshot(const pull_free_image_cb_t &pull_free_image_cb, std::shared_ptr &img_out, std::chrono::milliseconds timeout, bool cursor) { - auto to = std::chrono::steady_clock::now() + timeout; - - dmabuf.listen(interface.dmabuf_manager, output, cursor); - do { display.roundtrip(); - if (to < std::chrono::steady_clock::now()) { - return platf::capture_e::timeout; + if (!interface[wl::interface_t::XDG_OUTPUT]) { + BOOST_LOG(error) << "Missing Wayland wire for xdg_output"sv; + return -1; } - } while (dmabuf.status == dmabuf_t::WAITING); - auto current_frame = dmabuf.current_frame; + if (!interface[wl::interface_t::WLR_EXPORT_DMABUF]) { + BOOST_LOG(error) << "Missing Wayland wire for wlr-export-dmabuf"sv; + return -1; + } - if ( - dmabuf.status == dmabuf_t::REINIT || - current_frame->sd.width != width || - current_frame->sd.height != height) { - return platf::capture_e::reinit; - } + auto monitor = interface.monitors[0].get(); - return platf::capture_e::ok; + if (!display_name.empty()) { + auto streamedMonitor = util::from_view(display_name); + + if (streamedMonitor >= 0 && + streamedMonitor < interface.monitors.size()) { + monitor = interface.monitors[streamedMonitor].get(); + } + } + + monitor->listen(interface.output_manager); + + display.roundtrip(); + + output = monitor->output; + + offset_x = monitor->viewport.offset_x; + offset_y = monitor->viewport.offset_y; + width = monitor->viewport.width; + height = monitor->viewport.height; + + this->env_width = ::wl::env_width; + this->env_height = ::wl::env_height; + + BOOST_LOG(info) << "Selected monitor ["sv << monitor->description + << "] for streaming"sv; + BOOST_LOG(debug) << "Offset: "sv << offset_x << 'x' << offset_y; + BOOST_LOG(debug) << "Resolution: "sv << width << 'x' << height; + BOOST_LOG(debug) << "Desktop Resolution: "sv << env_width << 'x' + << env_height; + + return 0; + } + + int dummy_img(platf::img_t *img) override { return 0; } + + inline platf::capture_e snapshot( + const pull_free_image_cb_t &pull_free_image_cb, + std::shared_ptr &img_out, + std::chrono::milliseconds timeout, bool cursor) { + auto to = std::chrono::steady_clock::now() + timeout; + + dmabuf.listen(interface.dmabuf_manager, output, cursor); + do { + display.roundtrip(); + + if (to < std::chrono::steady_clock::now()) { + return platf::capture_e::timeout; + } + } while (dmabuf.status == dmabuf_t::WAITING); + + auto current_frame = dmabuf.current_frame; + + if (dmabuf.status == dmabuf_t::REINIT || + current_frame->sd.width != width || + current_frame->sd.height != height) { + return platf::capture_e::reinit; + } + + return platf::capture_e::ok; } platf::mem_type_e mem_type; @@ -117,272 +117,287 @@ namespace wl { dmabuf_t dmabuf; wl_output *output; - }; +}; - class wlr_ram_t: public wlr_t { - public: - platf::capture_e - capture(const push_captured_image_cb_t &push_captured_image_cb, const pull_free_image_cb_t &pull_free_image_cb, bool *cursor) override { - auto next_frame = std::chrono::steady_clock::now(); +class wlr_ram_t : public wlr_t { + public: + platf::capture_e capture( + const push_captured_image_cb_t &push_captured_image_cb, + const pull_free_image_cb_t &pull_free_image_cb, bool *cursor) override { + auto next_frame = std::chrono::steady_clock::now(); - while (true) { - auto now = std::chrono::steady_clock::now(); + while (true) { + auto now = std::chrono::steady_clock::now(); - if (next_frame > now) { - std::this_thread::sleep_for((next_frame - now) / 3 * 2); - } - while (next_frame > now) { - now = std::chrono::steady_clock::now(); - } - next_frame = now + delay; - - std::shared_ptr img_out; - auto status = snapshot(pull_free_image_cb, img_out, 1000ms, *cursor); - switch (status) { - case platf::capture_e::reinit: - case platf::capture_e::error: - case platf::capture_e::interrupted: - return status; - case platf::capture_e::timeout: - if (!push_captured_image_cb(std::move(img_out), false)) { - return platf::capture_e::ok; + if (next_frame > now) { + std::this_thread::sleep_for((next_frame - now) / 3 * 2); } - break; - case platf::capture_e::ok: - if (!push_captured_image_cb(std::move(img_out), true)) { - return platf::capture_e::ok; + while (next_frame > now) { + now = std::chrono::steady_clock::now(); } - break; - default: - BOOST_LOG(error) << "Unrecognized capture status ["sv << (int) status << ']'; + next_frame = now + delay; + + std::shared_ptr img_out; + auto status = + snapshot(pull_free_image_cb, img_out, 1000ms, *cursor); + switch (status) { + case platf::capture_e::reinit: + case platf::capture_e::error: + case platf::capture_e::interrupted: + return status; + case platf::capture_e::timeout: + if (!push_captured_image_cb(std::move(img_out), false)) { + return platf::capture_e::ok; + } + break; + case platf::capture_e::ok: + if (!push_captured_image_cb(std::move(img_out), true)) { + return platf::capture_e::ok; + } + break; + default: + BOOST_LOG(error) << "Unrecognized capture status ["sv + << (int)status << ']'; + return status; + } + } + + return platf::capture_e::ok; + } + + platf::capture_e snapshot(const pull_free_image_cb_t &pull_free_image_cb, + std::shared_ptr &img_out, + std::chrono::milliseconds timeout, bool cursor) { + auto status = + wlr_t::snapshot(pull_free_image_cb, img_out, timeout, cursor); + if (status != platf::capture_e::ok) { return status; } - } - return platf::capture_e::ok; + auto current_frame = dmabuf.current_frame; + + auto rgb_opt = egl::import_source(egl_display.get(), current_frame->sd); + + if (!rgb_opt) { + return platf::capture_e::reinit; + } + + if (!pull_free_image_cb(img_out)) { + return platf::capture_e::interrupted; + } + + gl::ctx.BindTexture(GL_TEXTURE_2D, (*rgb_opt)->tex[0]); + + int w, h; + gl::ctx.GetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_WIDTH, &w); + gl::ctx.GetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_HEIGHT, &h); + BOOST_LOG(debug) << "width and height: w "sv << w << " h "sv << h; + + gl::ctx.GetTextureSubImage((*rgb_opt)->tex[0], 0, 0, 0, 0, width, + height, 1, GL_BGRA, GL_UNSIGNED_BYTE, + img_out->height * img_out->row_pitch, + img_out->data); + gl::ctx.BindTexture(GL_TEXTURE_2D, 0); + + return platf::capture_e::ok; } - platf::capture_e - snapshot(const pull_free_image_cb_t &pull_free_image_cb, std::shared_ptr &img_out, std::chrono::milliseconds timeout, bool cursor) { - auto status = wlr_t::snapshot(pull_free_image_cb, img_out, timeout, cursor); - if (status != platf::capture_e::ok) { - return status; - } + int init(platf::mem_type_e hwdevice_type, const std::string &display_name, + const ::video::config_t &config) { + if (wlr_t::init(hwdevice_type, display_name, config)) { + return -1; + } - auto current_frame = dmabuf.current_frame; + egl_display = egl::make_display(display.get()); + if (!egl_display) { + return -1; + } - auto rgb_opt = egl::import_source(egl_display.get(), current_frame->sd); + auto ctx_opt = egl::make_ctx(egl_display.get()); + if (!ctx_opt) { + return -1; + } - if (!rgb_opt) { - return platf::capture_e::reinit; - } + ctx = std::move(*ctx_opt); - if (!pull_free_image_cb(img_out)) { - return platf::capture_e::interrupted; - } - - gl::ctx.BindTexture(GL_TEXTURE_2D, (*rgb_opt)->tex[0]); - - int w, h; - gl::ctx.GetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_WIDTH, &w); - gl::ctx.GetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_HEIGHT, &h); - BOOST_LOG(debug) << "width and height: w "sv << w << " h "sv << h; - - gl::ctx.GetTextureSubImage((*rgb_opt)->tex[0], 0, 0, 0, 0, width, height, 1, GL_BGRA, GL_UNSIGNED_BYTE, img_out->height * img_out->row_pitch, img_out->data); - gl::ctx.BindTexture(GL_TEXTURE_2D, 0); - - return platf::capture_e::ok; + return 0; } - int - init(platf::mem_type_e hwdevice_type, const std::string &display_name, const ::video::config_t &config) { - if (wlr_t::init(hwdevice_type, display_name, config)) { - return -1; - } + std::unique_ptr make_avcodec_encode_device( + platf::pix_fmt_e pix_fmt) override { + if (mem_type == platf::mem_type_e::vaapi) { + return va::make_avcodec_encode_device(width, height, false); + } - egl_display = egl::make_display(display.get()); - if (!egl_display) { - return -1; - } - - auto ctx_opt = egl::make_ctx(egl_display.get()); - if (!ctx_opt) { - return -1; - } - - ctx = std::move(*ctx_opt); - - return 0; + return std::make_unique(); } - std::unique_ptr - make_avcodec_encode_device(platf::pix_fmt_e pix_fmt) override { - if (mem_type == platf::mem_type_e::vaapi) { - return va::make_avcodec_encode_device(width, height, false); - } + std::shared_ptr alloc_img() override { + auto img = std::make_shared(); + img->width = width; + img->height = height; + img->pixel_pitch = 4; + img->row_pitch = img->pixel_pitch * width; + img->data = new std::uint8_t[height * img->row_pitch]; - return std::make_unique(); - } - - std::shared_ptr - alloc_img() override { - auto img = std::make_shared(); - img->width = width; - img->height = height; - img->pixel_pitch = 4; - img->row_pitch = img->pixel_pitch * width; - img->data = new std::uint8_t[height * img->row_pitch]; - - return img; + return img; } egl::display_t egl_display; egl::ctx_t ctx; - }; +}; - class wlr_vram_t: public wlr_t { - public: - platf::capture_e - capture(const push_captured_image_cb_t &push_captured_image_cb, const pull_free_image_cb_t &pull_free_image_cb, bool *cursor) override { - auto next_frame = std::chrono::steady_clock::now(); +class wlr_vram_t : public wlr_t { + public: + platf::capture_e capture( + const push_captured_image_cb_t &push_captured_image_cb, + const pull_free_image_cb_t &pull_free_image_cb, bool *cursor) override { + auto next_frame = std::chrono::steady_clock::now(); - while (true) { - auto now = std::chrono::steady_clock::now(); + while (true) { + auto now = std::chrono::steady_clock::now(); - if (next_frame > now) { - std::this_thread::sleep_for((next_frame - now) / 3 * 2); + if (next_frame > now) { + std::this_thread::sleep_for((next_frame - now) / 3 * 2); + } + while (next_frame > now) { + now = std::chrono::steady_clock::now(); + } + next_frame = now + delay; + + std::shared_ptr img_out; + auto status = + snapshot(pull_free_image_cb, img_out, 1000ms, *cursor); + switch (status) { + case platf::capture_e::reinit: + case platf::capture_e::error: + case platf::capture_e::interrupted: + return status; + case platf::capture_e::timeout: + if (!push_captured_image_cb(std::move(img_out), false)) { + return platf::capture_e::ok; + } + break; + case platf::capture_e::ok: + if (!push_captured_image_cb(std::move(img_out), true)) { + return platf::capture_e::ok; + } + break; + default: + BOOST_LOG(error) << "Unrecognized capture status ["sv + << (int)status << ']'; + return status; + } } - while (next_frame > now) { - now = std::chrono::steady_clock::now(); - } - next_frame = now + delay; + return platf::capture_e::ok; + } + + platf::capture_e snapshot(const pull_free_image_cb_t &pull_free_image_cb, + std::shared_ptr &img_out, + std::chrono::milliseconds timeout, bool cursor) { + auto status = + wlr_t::snapshot(pull_free_image_cb, img_out, timeout, cursor); + if (status != platf::capture_e::ok) { + return status; + } + + if (!pull_free_image_cb(img_out)) { + return platf::capture_e::interrupted; + } + auto img = (egl::img_descriptor_t *)img_out.get(); + img->reset(); + + auto current_frame = dmabuf.current_frame; + + ++sequence; + img->sequence = sequence; + + img->sd = current_frame->sd; + + // Prevent dmabuf from closing the file descriptors. + std::fill_n(current_frame->sd.fds, 4, -1); + + return platf::capture_e::ok; + } + + std::shared_ptr alloc_img() override { + auto img = std::make_shared(); + + img->sequence = 0; + img->serial = std::numeric_limitsserial)>::max(); + img->data = nullptr; + + // File descriptors aren't open + std::fill_n(img->sd.fds, 4, -1); + + return img; + } + + std::unique_ptr make_avcodec_encode_device( + platf::pix_fmt_e pix_fmt) override { + if (mem_type == platf::mem_type_e::vaapi) { + return va::make_avcodec_encode_device(width, height, 0, 0, true); + } + + return std::make_unique(); + } + + int dummy_img(platf::img_t *img) override { + // TODO: stop cheating and give black image + if (!img) { + return -1; + }; + auto pull_dummy_img_callback = + [&img](std::shared_ptr &img_out) -> bool { + img_out = img->shared_from_this(); + return true; + }; std::shared_ptr img_out; - auto status = snapshot(pull_free_image_cb, img_out, 1000ms, *cursor); - switch (status) { - case platf::capture_e::reinit: - case platf::capture_e::error: - case platf::capture_e::interrupted: - return status; - case platf::capture_e::timeout: - if (!push_captured_image_cb(std::move(img_out), false)) { - return platf::capture_e::ok; - } - break; - case platf::capture_e::ok: - if (!push_captured_image_cb(std::move(img_out), true)) { - return platf::capture_e::ok; - } - break; - default: - BOOST_LOG(error) << "Unrecognized capture status ["sv << (int) status << ']'; - return status; - } - } - - return platf::capture_e::ok; + return snapshot(pull_dummy_img_callback, img_out, 1000ms, false) != + platf::capture_e::ok; } - platf::capture_e - snapshot(const pull_free_image_cb_t &pull_free_image_cb, std::shared_ptr &img_out, std::chrono::milliseconds timeout, bool cursor) { - auto status = wlr_t::snapshot(pull_free_image_cb, img_out, timeout, cursor); - if (status != platf::capture_e::ok) { - return status; - } - - if (!pull_free_image_cb(img_out)) { - return platf::capture_e::interrupted; - } - auto img = (egl::img_descriptor_t *) img_out.get(); - img->reset(); - - auto current_frame = dmabuf.current_frame; - - ++sequence; - img->sequence = sequence; - - img->sd = current_frame->sd; - - // Prevent dmabuf from closing the file descriptors. - std::fill_n(current_frame->sd.fds, 4, -1); - - return platf::capture_e::ok; - } - - std::shared_ptr - alloc_img() override { - auto img = std::make_shared(); - - img->sequence = 0; - img->serial = std::numeric_limitsserial)>::max(); - img->data = nullptr; - - // File descriptors aren't open - std::fill_n(img->sd.fds, 4, -1); - - return img; - } - - std::unique_ptr - make_avcodec_encode_device(platf::pix_fmt_e pix_fmt) override { - if (mem_type == platf::mem_type_e::vaapi) { - return va::make_avcodec_encode_device(width, height, 0, 0, true); - } - - return std::make_unique(); - } - - int - dummy_img(platf::img_t *img) override { - // TODO: stop cheating and give black image - if (!img) { - return -1; - }; - auto pull_dummy_img_callback = [&img](std::shared_ptr &img_out) -> bool { - img_out = img->shared_from_this(); - return true; - }; - std::shared_ptr img_out; - return snapshot(pull_dummy_img_callback, img_out, 1000ms, false) != platf::capture_e::ok; - } - - std::uint64_t sequence {}; - }; + std::uint64_t sequence{}; +}; } // namespace wl namespace platf { - std::shared_ptr - wl_display(mem_type_e hwdevice_type, const std::string &display_name, const video::config_t &config) { - if (hwdevice_type != platf::mem_type_e::system && hwdevice_type != platf::mem_type_e::vaapi && hwdevice_type != platf::mem_type_e::cuda) { - BOOST_LOG(error) << "Could not initialize display with the given hw device type."sv; - return nullptr; +std::shared_ptr wl_display(mem_type_e hwdevice_type, + const std::string &display_name, + const video::config_t &config) { + if (hwdevice_type != platf::mem_type_e::system && + hwdevice_type != platf::mem_type_e::vaapi && + hwdevice_type != platf::mem_type_e::cuda) { + BOOST_LOG(error) + << "Could not initialize display with the given hw device type."sv; + return nullptr; } if (hwdevice_type == platf::mem_type_e::vaapi) { - auto wlr = std::make_shared(); - if (wlr->init(hwdevice_type, display_name, config)) { - return nullptr; - } + auto wlr = std::make_shared(); + if (wlr->init(hwdevice_type, display_name, config)) { + return nullptr; + } - return wlr; + return wlr; } auto wlr = std::make_shared(); if (wlr->init(hwdevice_type, display_name, config)) { - return nullptr; + return nullptr; } return wlr; - } +} - std::vector - wl_display_names() { +std::vector wl_display_names() { std::vector display_names; wl::display_t display; if (display.init()) { - return {}; + return {}; } wl::interface_t interface; @@ -391,34 +406,38 @@ namespace platf { display.roundtrip(); if (!interface[wl::interface_t::XDG_OUTPUT]) { - BOOST_LOG(warning) << "Missing Wayland wire for xdg_output"sv; - return {}; + BOOST_LOG(warning) << "Missing Wayland wire for xdg_output"sv; + return {}; } if (!interface[wl::interface_t::WLR_EXPORT_DMABUF]) { - BOOST_LOG(warning) << "Missing Wayland wire for wlr-export-dmabuf"sv; - return {}; + BOOST_LOG(warning) << "Missing Wayland wire for wlr-export-dmabuf"sv; + return {}; } wl::env_width = 0; wl::env_height = 0; for (auto &monitor : interface.monitors) { - monitor->listen(interface.output_manager); + monitor->listen(interface.output_manager); } display.roundtrip(); for (int x = 0; x < interface.monitors.size(); ++x) { - auto monitor = interface.monitors[x].get(); + auto monitor = interface.monitors[x].get(); - wl::env_width = std::max(wl::env_width, (int) (monitor->viewport.offset_x + monitor->viewport.width)); - wl::env_height = std::max(wl::env_height, (int) (monitor->viewport.offset_y + monitor->viewport.height)); + wl::env_width = std::max( + wl::env_width, + (int)(monitor->viewport.offset_x + monitor->viewport.width)); + wl::env_height = std::max( + wl::env_height, + (int)(monitor->viewport.offset_y + monitor->viewport.height)); - display_names.emplace_back(std::to_string(x)); + display_names.emplace_back(std::to_string(x)); } return display_names; - } +} } // namespace platf diff --git a/src/platform/linux/x11grab.cpp b/src/platform/linux/x11grab.cpp index 6bd3018c..10845609 100644 --- a/src/platform/linux/x11grab.cpp +++ b/src/platform/linux/x11grab.cpp @@ -2,9 +2,7 @@ * @file src/platform/linux/x11grab.cpp * @brief todo */ -#include "src/platform/common.h" - -#include +#include "x11grab.h" #include #include @@ -16,326 +14,297 @@ #include #include -#include "src/config.h" -#include "src/main.h" -#include "src/task_pool.h" -#include "src/video.h" +#include #include "cuda.h" #include "graphics.h" #include "misc.h" +#include "src/config.h" +#include "src/main.h" +#include "src/platform/common.h" +#include "src/task_pool.h" +#include "src/video.h" #include "vaapi.h" -#include "x11grab.h" using namespace std::literals; namespace platf { - int - load_xcb(); - int - load_x11(); +int load_xcb(); +int load_x11(); - namespace x11 { -#define _FN(x, ret, args) \ - typedef ret(*x##_fn) args; \ - static x##_fn x +namespace x11 { +#define _FN(x, ret, args) \ + typedef ret(*x##_fn) args; \ + static x##_fn x - _FN(GetImage, XImage *, - ( - Display * display, - Drawable d, - int x, int y, - unsigned int width, unsigned int height, - unsigned long plane_mask, - int format)); +_FN(GetImage, XImage *, + (Display * display, Drawable d, int x, int y, unsigned int width, + unsigned int height, unsigned long plane_mask, int format)); - _FN(OpenDisplay, Display *, (_Xconst char *display_name)); - _FN(GetWindowAttributes, Status, - ( - Display * display, - Window w, - XWindowAttributes *window_attributes_return)); +_FN(OpenDisplay, Display *, (_Xconst char *display_name)); +_FN(GetWindowAttributes, Status, + (Display * display, Window w, XWindowAttributes *window_attributes_return)); - _FN(CloseDisplay, int, (Display * display)); - _FN(Free, int, (void *data)); - _FN(InitThreads, Status, (void) ); +_FN(CloseDisplay, int, (Display * display)); +_FN(Free, int, (void *data)); +_FN(InitThreads, Status, (void)); - namespace rr { - _FN(GetScreenResources, XRRScreenResources *, (Display * dpy, Window window)); - _FN(GetOutputInfo, XRROutputInfo *, (Display * dpy, XRRScreenResources *resources, RROutput output)); - _FN(GetCrtcInfo, XRRCrtcInfo *, (Display * dpy, XRRScreenResources *resources, RRCrtc crtc)); - _FN(FreeScreenResources, void, (XRRScreenResources * resources)); - _FN(FreeOutputInfo, void, (XRROutputInfo * outputInfo)); - _FN(FreeCrtcInfo, void, (XRRCrtcInfo * crtcInfo)); +namespace rr { +_FN(GetScreenResources, XRRScreenResources *, (Display * dpy, Window window)); +_FN(GetOutputInfo, XRROutputInfo *, + (Display * dpy, XRRScreenResources *resources, RROutput output)); +_FN(GetCrtcInfo, XRRCrtcInfo *, + (Display * dpy, XRRScreenResources *resources, RRCrtc crtc)); +_FN(FreeScreenResources, void, (XRRScreenResources * resources)); +_FN(FreeOutputInfo, void, (XRROutputInfo * outputInfo)); +_FN(FreeCrtcInfo, void, (XRRCrtcInfo * crtcInfo)); - static int - init() { - static void *handle { nullptr }; - static bool funcs_loaded = false; +static int init() { + static void *handle{nullptr}; + static bool funcs_loaded = false; - if (funcs_loaded) return 0; + if (funcs_loaded) return 0; + if (!handle) { + handle = dyn::handle({"libXrandr.so.2", "libXrandr.so"}); if (!handle) { - handle = dyn::handle({ "libXrandr.so.2", "libXrandr.so" }); - if (!handle) { return -1; - } } + } - std::vector> funcs { - { (dyn::apiproc *) &GetScreenResources, "XRRGetScreenResources" }, - { (dyn::apiproc *) &GetOutputInfo, "XRRGetOutputInfo" }, - { (dyn::apiproc *) &GetCrtcInfo, "XRRGetCrtcInfo" }, - { (dyn::apiproc *) &FreeScreenResources, "XRRFreeScreenResources" }, - { (dyn::apiproc *) &FreeOutputInfo, "XRRFreeOutputInfo" }, - { (dyn::apiproc *) &FreeCrtcInfo, "XRRFreeCrtcInfo" }, - }; + std::vector> funcs{ + {(dyn::apiproc *)&GetScreenResources, "XRRGetScreenResources"}, + {(dyn::apiproc *)&GetOutputInfo, "XRRGetOutputInfo"}, + {(dyn::apiproc *)&GetCrtcInfo, "XRRGetCrtcInfo"}, + {(dyn::apiproc *)&FreeScreenResources, "XRRFreeScreenResources"}, + {(dyn::apiproc *)&FreeOutputInfo, "XRRFreeOutputInfo"}, + {(dyn::apiproc *)&FreeCrtcInfo, "XRRFreeCrtcInfo"}, + }; - if (dyn::load(handle, funcs)) { - return -1; - } + if (dyn::load(handle, funcs)) { + return -1; + } - funcs_loaded = true; - return 0; - } + funcs_loaded = true; + return 0; +} - } // namespace rr - namespace fix { - _FN(GetCursorImage, XFixesCursorImage *, (Display * dpy)); +} // namespace rr +namespace fix { +_FN(GetCursorImage, XFixesCursorImage *, (Display * dpy)); - static int - init() { - static void *handle { nullptr }; - static bool funcs_loaded = false; +static int init() { + static void *handle{nullptr}; + static bool funcs_loaded = false; - if (funcs_loaded) return 0; + if (funcs_loaded) return 0; + if (!handle) { + handle = dyn::handle({"libXfixes.so.3", "libXfixes.so"}); if (!handle) { - handle = dyn::handle({ "libXfixes.so.3", "libXfixes.so" }); - if (!handle) { return -1; - } } - - std::vector> funcs { - { (dyn::apiproc *) &GetCursorImage, "XFixesGetCursorImage" }, - }; - - if (dyn::load(handle, funcs)) { - return -1; - } - - funcs_loaded = true; - return 0; - } - } // namespace fix - - static int - init() { - static void *handle { nullptr }; - static bool funcs_loaded = false; - - if (funcs_loaded) return 0; - - if (!handle) { - handle = dyn::handle({ "libX11.so.6", "libX11.so" }); - if (!handle) { - return -1; - } - } - - std::vector> funcs { - { (dyn::apiproc *) &GetImage, "XGetImage" }, - { (dyn::apiproc *) &OpenDisplay, "XOpenDisplay" }, - { (dyn::apiproc *) &GetWindowAttributes, "XGetWindowAttributes" }, - { (dyn::apiproc *) &Free, "XFree" }, - { (dyn::apiproc *) &CloseDisplay, "XCloseDisplay" }, - { (dyn::apiproc *) &InitThreads, "XInitThreads" }, - }; - - if (dyn::load(handle, funcs)) { - return -1; - } - - funcs_loaded = true; - return 0; - } - } // namespace x11 - - namespace xcb { - static xcb_extension_t *shm_id; - - _FN(shm_get_image_reply, xcb_shm_get_image_reply_t *, - ( - xcb_connection_t * c, - xcb_shm_get_image_cookie_t cookie, - xcb_generic_error_t **e)); - - _FN(shm_get_image_unchecked, xcb_shm_get_image_cookie_t, - ( - xcb_connection_t * c, - xcb_drawable_t drawable, - int16_t x, int16_t y, - uint16_t width, uint16_t height, - uint32_t plane_mask, - uint8_t format, - xcb_shm_seg_t shmseg, - uint32_t offset)); - - _FN(shm_attach, xcb_void_cookie_t, - (xcb_connection_t * c, - xcb_shm_seg_t shmseg, - uint32_t shmid, - uint8_t read_only)); - - _FN(get_extension_data, xcb_query_extension_reply_t *, - (xcb_connection_t * c, xcb_extension_t *ext)); - - _FN(get_setup, xcb_setup_t *, (xcb_connection_t * c)); - _FN(disconnect, void, (xcb_connection_t * c)); - _FN(connection_has_error, int, (xcb_connection_t * c)); - _FN(connect, xcb_connection_t *, (const char *displayname, int *screenp)); - _FN(setup_roots_iterator, xcb_screen_iterator_t, (const xcb_setup_t *R)); - _FN(generate_id, std::uint32_t, (xcb_connection_t * c)); - - int - init_shm() { - static void *handle { nullptr }; - static bool funcs_loaded = false; - - if (funcs_loaded) return 0; - - if (!handle) { - handle = dyn::handle({ "libxcb-shm.so.0", "libxcb-shm.so" }); - if (!handle) { - return -1; - } - } - - std::vector> funcs { - { (dyn::apiproc *) &shm_id, "xcb_shm_id" }, - { (dyn::apiproc *) &shm_get_image_reply, "xcb_shm_get_image_reply" }, - { (dyn::apiproc *) &shm_get_image_unchecked, "xcb_shm_get_image_unchecked" }, - { (dyn::apiproc *) &shm_attach, "xcb_shm_attach" }, - }; - - if (dyn::load(handle, funcs)) { - return -1; - } - - funcs_loaded = true; - return 0; } - int - init() { - static void *handle { nullptr }; - static bool funcs_loaded = false; + std::vector> funcs{ + {(dyn::apiproc *)&GetCursorImage, "XFixesGetCursorImage"}, + }; - if (funcs_loaded) return 0; - - if (!handle) { - handle = dyn::handle({ "libxcb.so.1", "libxcb.so" }); - if (!handle) { - return -1; - } - } - - std::vector> funcs { - { (dyn::apiproc *) &get_extension_data, "xcb_get_extension_data" }, - { (dyn::apiproc *) &get_setup, "xcb_get_setup" }, - { (dyn::apiproc *) &disconnect, "xcb_disconnect" }, - { (dyn::apiproc *) &connection_has_error, "xcb_connection_has_error" }, - { (dyn::apiproc *) &connect, "xcb_connect" }, - { (dyn::apiproc *) &setup_roots_iterator, "xcb_setup_roots_iterator" }, - { (dyn::apiproc *) &generate_id, "xcb_generate_id" }, - }; - - if (dyn::load(handle, funcs)) { + if (dyn::load(handle, funcs)) { return -1; - } - - funcs_loaded = true; - return 0; } + funcs_loaded = true; + return 0; +} +} // namespace fix + +static int init() { + static void *handle{nullptr}; + static bool funcs_loaded = false; + + if (funcs_loaded) return 0; + + if (!handle) { + handle = dyn::handle({"libX11.so.6", "libX11.so"}); + if (!handle) { + return -1; + } + } + + std::vector> funcs{ + {(dyn::apiproc *)&GetImage, "XGetImage"}, + {(dyn::apiproc *)&OpenDisplay, "XOpenDisplay"}, + {(dyn::apiproc *)&GetWindowAttributes, "XGetWindowAttributes"}, + {(dyn::apiproc *)&Free, "XFree"}, + {(dyn::apiproc *)&CloseDisplay, "XCloseDisplay"}, + {(dyn::apiproc *)&InitThreads, "XInitThreads"}, + }; + + if (dyn::load(handle, funcs)) { + return -1; + } + + funcs_loaded = true; + return 0; +} +} // namespace x11 + +namespace xcb { +static xcb_extension_t *shm_id; + +_FN(shm_get_image_reply, xcb_shm_get_image_reply_t *, + (xcb_connection_t * c, xcb_shm_get_image_cookie_t cookie, + xcb_generic_error_t **e)); + +_FN(shm_get_image_unchecked, xcb_shm_get_image_cookie_t, + (xcb_connection_t * c, xcb_drawable_t drawable, int16_t x, int16_t y, + uint16_t width, uint16_t height, uint32_t plane_mask, uint8_t format, + xcb_shm_seg_t shmseg, uint32_t offset)); + +_FN(shm_attach, xcb_void_cookie_t, + (xcb_connection_t * c, xcb_shm_seg_t shmseg, uint32_t shmid, + uint8_t read_only)); + +_FN(get_extension_data, xcb_query_extension_reply_t *, + (xcb_connection_t * c, xcb_extension_t *ext)); + +_FN(get_setup, xcb_setup_t *, (xcb_connection_t * c)); +_FN(disconnect, void, (xcb_connection_t * c)); +_FN(connection_has_error, int, (xcb_connection_t * c)); +_FN(connect, xcb_connection_t *, (const char *displayname, int *screenp)); +_FN(setup_roots_iterator, xcb_screen_iterator_t, (const xcb_setup_t *R)); +_FN(generate_id, std::uint32_t, (xcb_connection_t * c)); + +int init_shm() { + static void *handle{nullptr}; + static bool funcs_loaded = false; + + if (funcs_loaded) return 0; + + if (!handle) { + handle = dyn::handle({"libxcb-shm.so.0", "libxcb-shm.so"}); + if (!handle) { + return -1; + } + } + + std::vector> funcs{ + {(dyn::apiproc *)&shm_id, "xcb_shm_id"}, + {(dyn::apiproc *)&shm_get_image_reply, "xcb_shm_get_image_reply"}, + {(dyn::apiproc *)&shm_get_image_unchecked, + "xcb_shm_get_image_unchecked"}, + {(dyn::apiproc *)&shm_attach, "xcb_shm_attach"}, + }; + + if (dyn::load(handle, funcs)) { + return -1; + } + + funcs_loaded = true; + return 0; +} + +int init() { + static void *handle{nullptr}; + static bool funcs_loaded = false; + + if (funcs_loaded) return 0; + + if (!handle) { + handle = dyn::handle({"libxcb.so.1", "libxcb.so"}); + if (!handle) { + return -1; + } + } + + std::vector> funcs{ + {(dyn::apiproc *)&get_extension_data, "xcb_get_extension_data"}, + {(dyn::apiproc *)&get_setup, "xcb_get_setup"}, + {(dyn::apiproc *)&disconnect, "xcb_disconnect"}, + {(dyn::apiproc *)&connection_has_error, "xcb_connection_has_error"}, + {(dyn::apiproc *)&connect, "xcb_connect"}, + {(dyn::apiproc *)&setup_roots_iterator, "xcb_setup_roots_iterator"}, + {(dyn::apiproc *)&generate_id, "xcb_generate_id"}, + }; + + if (dyn::load(handle, funcs)) { + return -1; + } + + funcs_loaded = true; + return 0; +} + #undef _FN - } // namespace xcb +} // namespace xcb - void - freeImage(XImage *); - void - freeX(XFixesCursorImage *); +void freeImage(XImage *); +void freeX(XFixesCursorImage *); - using xcb_connect_t = util::dyn_safe_ptr; - using xcb_img_t = util::c_ptr; +using xcb_connect_t = util::dyn_safe_ptr; +using xcb_img_t = util::c_ptr; - using ximg_t = util::safe_ptr; - using xcursor_t = util::safe_ptr; +using ximg_t = util::safe_ptr; +using xcursor_t = util::safe_ptr; - using crtc_info_t = util::dyn_safe_ptr<_XRRCrtcInfo, &x11::rr::FreeCrtcInfo>; - using output_info_t = util::dyn_safe_ptr<_XRROutputInfo, &x11::rr::FreeOutputInfo>; - using screen_res_t = util::dyn_safe_ptr<_XRRScreenResources, &x11::rr::FreeScreenResources>; +using crtc_info_t = util::dyn_safe_ptr<_XRRCrtcInfo, &x11::rr::FreeCrtcInfo>; +using output_info_t = + util::dyn_safe_ptr<_XRROutputInfo, &x11::rr::FreeOutputInfo>; +using screen_res_t = + util::dyn_safe_ptr<_XRRScreenResources, &x11::rr::FreeScreenResources>; - class shm_id_t { - public: - shm_id_t(): - id { -1 } {} - shm_id_t(int id): - id { id } {} - shm_id_t(shm_id_t &&other) noexcept: - id(other.id) { - other.id = -1; - } +class shm_id_t { + public: + shm_id_t() : id{-1} {} + shm_id_t(int id) : id{id} {} + shm_id_t(shm_id_t &&other) noexcept : id(other.id) { other.id = -1; } ~shm_id_t() { - if (id != -1) { - shmctl(id, IPC_RMID, nullptr); - id = -1; - } + if (id != -1) { + shmctl(id, IPC_RMID, nullptr); + id = -1; + } } int id; - }; +}; - class shm_data_t { - public: - shm_data_t(): - data { (void *) -1 } {} - shm_data_t(void *data): - data { data } {} +class shm_data_t { + public: + shm_data_t() : data{(void *)-1} {} + shm_data_t(void *data) : data{data} {} - shm_data_t(shm_data_t &&other) noexcept: - data(other.data) { - other.data = (void *) -1; + shm_data_t(shm_data_t &&other) noexcept : data(other.data) { + other.data = (void *)-1; } ~shm_data_t() { - if ((std::uintptr_t) data != -1) { - shmdt(data); - } + if ((std::uintptr_t)data != -1) { + shmdt(data); + } } void *data; - }; +}; - struct x11_img_t: public img_t { +struct x11_img_t : public img_t { ximg_t img; - }; +}; - struct shm_img_t: public img_t { +struct shm_img_t : public img_t { ~shm_img_t() override { - delete[] data; - data = nullptr; + delete[] data; + data = nullptr; } - }; +}; - static void - blend_cursor(Display *display, img_t &img, int offsetX, int offsetY) { - xcursor_t overlay { x11::fix::GetCursorImage(display) }; +static void blend_cursor(Display *display, img_t &img, int offsetX, + int offsetY) { + xcursor_t overlay{x11::fix::GetCursorImage(display)}; if (!overlay) { - BOOST_LOG(error) << "Couldn't get cursor from XFixesGetCursorImage"sv; - return; + BOOST_LOG(error) << "Couldn't get cursor from XFixesGetCursorImage"sv; + return; } overlay->x -= overlay->xhot; @@ -344,43 +313,49 @@ namespace platf { overlay->x -= offsetX; overlay->y -= offsetY; - overlay->x = std::max((short) 0, overlay->x); - overlay->y = std::max((short) 0, overlay->y); + overlay->x = std::max((short)0, overlay->x); + overlay->y = std::max((short)0, overlay->y); - auto pixels = (int *) img.data; + auto pixels = (int *)img.data; auto screen_height = img.height; auto screen_width = img.width; - auto delta_height = std::min(overlay->height, std::max(0, screen_height - overlay->y)); - auto delta_width = std::min(overlay->width, std::max(0, screen_width - overlay->x)); + auto delta_height = std::min( + overlay->height, std::max(0, screen_height - overlay->y)); + auto delta_width = std::min( + overlay->width, std::max(0, screen_width - overlay->x)); for (auto y = 0; y < delta_height; ++y) { - auto overlay_begin = &overlay->pixels[y * overlay->width]; - auto overlay_end = &overlay->pixels[y * overlay->width + delta_width]; + auto overlay_begin = &overlay->pixels[y * overlay->width]; + auto overlay_end = &overlay->pixels[y * overlay->width + delta_width]; - auto pixels_begin = &pixels[(y + overlay->y) * (img.row_pitch / img.pixel_pitch) + overlay->x]; + auto pixels_begin = + &pixels[(y + overlay->y) * (img.row_pitch / img.pixel_pitch) + + overlay->x]; - std::for_each(overlay_begin, overlay_end, [&](long pixel) { - int *pixel_p = (int *) &pixel; + std::for_each(overlay_begin, overlay_end, [&](long pixel) { + int *pixel_p = (int *)&pixel; - auto colors_in = (uint8_t *) pixels_begin; + auto colors_in = (uint8_t *)pixels_begin; - auto alpha = (*(uint *) pixel_p) >> 24u; - if (alpha == 255) { - *pixels_begin = *pixel_p; - } - else { - auto colors_out = (uint8_t *) pixel_p; - colors_in[0] = colors_out[0] + (colors_in[0] * (255 - alpha) + 255 / 2) / 255; - colors_in[1] = colors_out[1] + (colors_in[1] * (255 - alpha) + 255 / 2) / 255; - colors_in[2] = colors_out[2] + (colors_in[2] * (255 - alpha) + 255 / 2) / 255; - } - ++pixels_begin; - }); + auto alpha = (*(uint *)pixel_p) >> 24u; + if (alpha == 255) { + *pixels_begin = *pixel_p; + } else { + auto colors_out = (uint8_t *)pixel_p; + colors_in[0] = colors_out[0] + + (colors_in[0] * (255 - alpha) + 255 / 2) / 255; + colors_in[1] = colors_out[1] + + (colors_in[1] * (255 - alpha) + 255 / 2) / 255; + colors_in[2] = colors_out[2] + + (colors_in[2] * (255 - alpha) + 255 / 2) / 255; + } + ++pixels_begin; + }); } - } +} - struct x11_attr_t: public display_t { +struct x11_attr_t : public display_t { std::chrono::nanoseconds delay; x11::xdisplay_t xdisplay; @@ -391,201 +366,217 @@ namespace platf { /** * Last X (NOT the streamed monitor!) size. - * This way we can trigger reinitialization if the dimensions changed while streaming + * This way we can trigger reinitialization if the dimensions changed while + * streaming */ // int env_width, env_height; - x11_attr_t(mem_type_e mem_type): - xdisplay { x11::OpenDisplay(nullptr) }, xwindow {}, xattr {}, mem_type { mem_type } { - x11::InitThreads(); + x11_attr_t(mem_type_e mem_type) + : xdisplay{x11::OpenDisplay(nullptr)}, + xwindow{}, + xattr{}, + mem_type{mem_type} { + x11::InitThreads(); } - int - init(const std::string &display_name, const ::video::config_t &config) { - if (!xdisplay) { - BOOST_LOG(error) << "Could not open X11 display"sv; - return -1; - } + int init(const std::string &display_name, const ::video::config_t &config) { + if (!xdisplay) { + BOOST_LOG(error) << "Could not open X11 display"sv; + return -1; + } - delay = std::chrono::nanoseconds { 1s } / config.framerate; + delay = std::chrono::nanoseconds{1s} / config.framerate; - xwindow = DefaultRootWindow(xdisplay.get()); + xwindow = DefaultRootWindow(xdisplay.get()); - refresh(); + refresh(); - int streamedMonitor = -1; - if (!display_name.empty()) { - streamedMonitor = (int) util::from_view(display_name); - } + int streamedMonitor = -1; + if (!display_name.empty()) { + streamedMonitor = (int)util::from_view(display_name); + } - if (streamedMonitor != -1) { - BOOST_LOG(info) << "Configuring selected monitor ("sv << streamedMonitor << ") to stream"sv; - screen_res_t screenr { x11::rr::GetScreenResources(xdisplay.get(), xwindow) }; - int output = screenr->noutput; + if (streamedMonitor != -1) { + BOOST_LOG(info) << "Configuring selected monitor ("sv + << streamedMonitor << ") to stream"sv; + screen_res_t screenr{ + x11::rr::GetScreenResources(xdisplay.get(), xwindow)}; + int output = screenr->noutput; - output_info_t result; - int monitor = 0; - for (int x = 0; x < output; ++x) { - output_info_t out_info { x11::rr::GetOutputInfo(xdisplay.get(), screenr.get(), screenr->outputs[x]) }; - if (out_info) { - if (monitor++ == streamedMonitor) { - result = std::move(out_info); - break; + output_info_t result; + int monitor = 0; + for (int x = 0; x < output; ++x) { + output_info_t out_info{x11::rr::GetOutputInfo( + xdisplay.get(), screenr.get(), screenr->outputs[x])}; + if (out_info) { + if (monitor++ == streamedMonitor) { + result = std::move(out_info); + break; + } + } } - } + + if (!result) { + BOOST_LOG(error) + << "Could not stream display number ["sv << streamedMonitor + << "], there are only ["sv << monitor << "] displays."sv; + return -1; + } + + if (result->crtc) { + crtc_info_t crt_info{x11::rr::GetCrtcInfo( + xdisplay.get(), screenr.get(), result->crtc)}; + BOOST_LOG(info) + << "Streaming display: "sv << result->name << " with res "sv + << crt_info->width << 'x' << crt_info->height + << " offset by "sv << crt_info->x << 'x' << crt_info->y; + + width = crt_info->width; + height = crt_info->height; + offset_x = crt_info->x; + offset_y = crt_info->y; + } else { + BOOST_LOG(warning) + << "Couldn't get requested display info, defaulting to recording entire virtual desktop"sv; + width = xattr.width; + height = xattr.height; + } + } else { + width = xattr.width; + height = xattr.height; } - if (!result) { - BOOST_LOG(error) << "Could not stream display number ["sv << streamedMonitor << "], there are only ["sv << monitor << "] displays."sv; - return -1; - } + env_width = xattr.width; + env_height = xattr.height; - if (result->crtc) { - crtc_info_t crt_info { x11::rr::GetCrtcInfo(xdisplay.get(), screenr.get(), result->crtc) }; - BOOST_LOG(info) - << "Streaming display: "sv << result->name << " with res "sv << crt_info->width << 'x' << crt_info->height << " offset by "sv << crt_info->x << 'x' << crt_info->y; - - width = crt_info->width; - height = crt_info->height; - offset_x = crt_info->x; - offset_y = crt_info->y; - } - else { - BOOST_LOG(warning) << "Couldn't get requested display info, defaulting to recording entire virtual desktop"sv; - width = xattr.width; - height = xattr.height; - } - } - else { - width = xattr.width; - height = xattr.height; - } - - env_width = xattr.width; - env_height = xattr.height; - - return 0; + return 0; } /** * Called when the display attributes should change. */ - void - refresh() { - x11::GetWindowAttributes(xdisplay.get(), xwindow, &xattr); // Update xattr's + void refresh() { + x11::GetWindowAttributes(xdisplay.get(), xwindow, + &xattr); // Update xattr's } - capture_e - capture(const push_captured_image_cb_t &push_captured_image_cb, const pull_free_image_cb_t &pull_free_image_cb, bool *cursor) override { - auto next_frame = std::chrono::steady_clock::now(); + capture_e capture(const push_captured_image_cb_t &push_captured_image_cb, + const pull_free_image_cb_t &pull_free_image_cb, + bool *cursor) override { + auto next_frame = std::chrono::steady_clock::now(); - while (true) { - auto now = std::chrono::steady_clock::now(); + while (true) { + auto now = std::chrono::steady_clock::now(); - if (next_frame > now) { - std::this_thread::sleep_for((next_frame - now) / 3 * 2); - } - while (next_frame > now) { - std::this_thread::sleep_for(1ns); - now = std::chrono::steady_clock::now(); - } - next_frame = now + delay; - - std::shared_ptr img_out; - auto status = snapshot(pull_free_image_cb, img_out, 1000ms, *cursor); - switch (status) { - case platf::capture_e::reinit: - case platf::capture_e::error: - case platf::capture_e::interrupted: - return status; - case platf::capture_e::timeout: - if (!push_captured_image_cb(std::move(img_out), false)) { - return platf::capture_e::ok; + if (next_frame > now) { + std::this_thread::sleep_for((next_frame - now) / 3 * 2); } - break; - case platf::capture_e::ok: - if (!push_captured_image_cb(std::move(img_out), true)) { - return platf::capture_e::ok; + while (next_frame > now) { + std::this_thread::sleep_for(1ns); + now = std::chrono::steady_clock::now(); + } + next_frame = now + delay; + + std::shared_ptr img_out; + auto status = + snapshot(pull_free_image_cb, img_out, 1000ms, *cursor); + switch (status) { + case platf::capture_e::reinit: + case platf::capture_e::error: + case platf::capture_e::interrupted: + return status; + case platf::capture_e::timeout: + if (!push_captured_image_cb(std::move(img_out), false)) { + return platf::capture_e::ok; + } + break; + case platf::capture_e::ok: + if (!push_captured_image_cb(std::move(img_out), true)) { + return platf::capture_e::ok; + } + break; + default: + BOOST_LOG(error) << "Unrecognized capture status ["sv + << (int)status << ']'; + return status; } - break; - default: - BOOST_LOG(error) << "Unrecognized capture status ["sv << (int) status << ']'; - return status; } - } - return capture_e::ok; + return capture_e::ok; } - capture_e - snapshot(const pull_free_image_cb_t &pull_free_image_cb, std::shared_ptr &img_out, std::chrono::milliseconds timeout, bool cursor) { - refresh(); + capture_e snapshot(const pull_free_image_cb_t &pull_free_image_cb, + std::shared_ptr &img_out, + std::chrono::milliseconds timeout, bool cursor) { + refresh(); - // The whole X server changed, so we must reinit everything - if (xattr.width != env_width || xattr.height != env_height) { - BOOST_LOG(warning) << "X dimensions changed in non-SHM mode, request reinit"sv; - return capture_e::reinit; - } + // The whole X server changed, so we must reinit everything + if (xattr.width != env_width || xattr.height != env_height) { + BOOST_LOG(warning) + << "X dimensions changed in non-SHM mode, request reinit"sv; + return capture_e::reinit; + } - if (!pull_free_image_cb(img_out)) { - return platf::capture_e::interrupted; - } - auto img = (x11_img_t *) img_out.get(); + if (!pull_free_image_cb(img_out)) { + return platf::capture_e::interrupted; + } + auto img = (x11_img_t *)img_out.get(); - XImage *x_img { x11::GetImage(xdisplay.get(), xwindow, offset_x, offset_y, width, height, AllPlanes, ZPixmap) }; + XImage *x_img{x11::GetImage(xdisplay.get(), xwindow, offset_x, offset_y, + width, height, AllPlanes, ZPixmap)}; - img->width = x_img->width; - img->height = x_img->height; - img->data = (uint8_t *) x_img->data; - img->row_pitch = x_img->bytes_per_line; - img->pixel_pitch = x_img->bits_per_pixel / 8; - img->img.reset(x_img); + img->width = x_img->width; + img->height = x_img->height; + img->data = (uint8_t *)x_img->data; + img->row_pitch = x_img->bytes_per_line; + img->pixel_pitch = x_img->bits_per_pixel / 8; + img->img.reset(x_img); - if (cursor) { - blend_cursor(xdisplay.get(), *img, offset_x, offset_y); - } + if (cursor) { + blend_cursor(xdisplay.get(), *img, offset_x, offset_y); + } - return capture_e::ok; + return capture_e::ok; } - std::shared_ptr - alloc_img() override { - return std::make_shared(); + std::shared_ptr alloc_img() override { + return std::make_shared(); } - std::unique_ptr - make_avcodec_encode_device(pix_fmt_e pix_fmt) override { - if (mem_type == mem_type_e::vaapi) { - return va::make_avcodec_encode_device(width, height, false); - } + std::unique_ptr make_avcodec_encode_device( + pix_fmt_e pix_fmt) override { + if (mem_type == mem_type_e::vaapi) { + return va::make_avcodec_encode_device(width, height, false); + } #ifdef SUNSHINE_BUILD_CUDA - if (mem_type == mem_type_e::cuda) { - return cuda::make_avcodec_encode_device(width, height, false); - } + if (mem_type == mem_type_e::cuda) { + return cuda::make_avcodec_encode_device(width, height, false); + } #endif - return std::make_unique(); + return std::make_unique(); } - int - dummy_img(img_t *img) override { - // TODO: stop cheating and give black image - if (!img) { - return -1; - }; - auto pull_dummy_img_callback = [&img](std::shared_ptr &img_out) -> bool { - img_out = img->shared_from_this(); - return true; - }; - std::shared_ptr img_out; - snapshot(pull_dummy_img_callback, img_out, 0s, true); - return 0; + int dummy_img(img_t *img) override { + // TODO: stop cheating and give black image + if (!img) { + return -1; + }; + auto pull_dummy_img_callback = + [&img](std::shared_ptr &img_out) -> bool { + img_out = img->shared_from_this(); + return true; + }; + std::shared_ptr img_out; + snapshot(pull_dummy_img_callback, img_out, 0s, true); + return 0; } - }; +}; - struct shm_attr_t: public x11_attr_t { - x11::xdisplay_t shm_xdisplay; // Prevent race condition with x11_attr_t::xdisplay +struct shm_attr_t : public x11_attr_t { + x11::xdisplay_t + shm_xdisplay; // Prevent race condition with x11_attr_t::xdisplay xcb_connect_t xcb; xcb_screen_t *display; std::uint32_t seg; @@ -596,169 +587,176 @@ namespace platf { task_pool_util::TaskPool::task_id_t refresh_task_id; - void - delayed_refresh() { - refresh(); + void delayed_refresh() { + refresh(); - refresh_task_id = task_pool.pushDelayed(&shm_attr_t::delayed_refresh, 2s, this).task_id; + refresh_task_id = + task_pool.pushDelayed(&shm_attr_t::delayed_refresh, 2s, this) + .task_id; } - shm_attr_t(mem_type_e mem_type): - x11_attr_t(mem_type), shm_xdisplay { x11::OpenDisplay(nullptr) } { - refresh_task_id = task_pool.pushDelayed(&shm_attr_t::delayed_refresh, 2s, this).task_id; + shm_attr_t(mem_type_e mem_type) + : x11_attr_t(mem_type), shm_xdisplay{x11::OpenDisplay(nullptr)} { + refresh_task_id = + task_pool.pushDelayed(&shm_attr_t::delayed_refresh, 2s, this) + .task_id; } ~shm_attr_t() override { - while (!task_pool.cancel(refresh_task_id)) - ; + while (!task_pool.cancel(refresh_task_id)) + ; } - capture_e - capture(const push_captured_image_cb_t &push_captured_image_cb, const pull_free_image_cb_t &pull_free_image_cb, bool *cursor) override { - auto next_frame = std::chrono::steady_clock::now(); + capture_e capture(const push_captured_image_cb_t &push_captured_image_cb, + const pull_free_image_cb_t &pull_free_image_cb, + bool *cursor) override { + auto next_frame = std::chrono::steady_clock::now(); - while (true) { - auto now = std::chrono::steady_clock::now(); + while (true) { + auto now = std::chrono::steady_clock::now(); - if (next_frame > now) { - std::this_thread::sleep_for((next_frame - now) / 3 * 2); - } - while (next_frame > now) { - std::this_thread::sleep_for(1ns); - now = std::chrono::steady_clock::now(); - } - next_frame = now + delay; - - std::shared_ptr img_out; - auto status = snapshot(pull_free_image_cb, img_out, 1000ms, *cursor); - switch (status) { - case platf::capture_e::reinit: - case platf::capture_e::error: - case platf::capture_e::interrupted: - return status; - case platf::capture_e::timeout: - if (!push_captured_image_cb(std::move(img_out), false)) { - return platf::capture_e::ok; + if (next_frame > now) { + std::this_thread::sleep_for((next_frame - now) / 3 * 2); } - break; - case platf::capture_e::ok: - if (!push_captured_image_cb(std::move(img_out), true)) { - return platf::capture_e::ok; + while (next_frame > now) { + std::this_thread::sleep_for(1ns); + now = std::chrono::steady_clock::now(); } - break; - default: - BOOST_LOG(error) << "Unrecognized capture status ["sv << (int) status << ']'; - return status; - } - } + next_frame = now + delay; - return capture_e::ok; - } - - capture_e - snapshot(const pull_free_image_cb_t &pull_free_image_cb, std::shared_ptr &img_out, std::chrono::milliseconds timeout, bool cursor) { - // The whole X server changed, so we must reinit everything - if (xattr.width != env_width || xattr.height != env_height) { - BOOST_LOG(warning) << "X dimensions changed in SHM mode, request reinit"sv; - return capture_e::reinit; - } - else { - auto img_cookie = xcb::shm_get_image_unchecked(xcb.get(), display->root, offset_x, offset_y, width, height, ~0, XCB_IMAGE_FORMAT_Z_PIXMAP, seg, 0); - - xcb_img_t img_reply { xcb::shm_get_image_reply(xcb.get(), img_cookie, nullptr) }; - if (!img_reply) { - BOOST_LOG(error) << "Could not get image reply"sv; - return capture_e::reinit; - } - - if (!pull_free_image_cb(img_out)) { - return platf::capture_e::interrupted; - } - - std::copy_n((std::uint8_t *) data.data, frame_size(), img_out->data); - - if (cursor) { - blend_cursor(shm_xdisplay.get(), *img_out, offset_x, offset_y); + std::shared_ptr img_out; + auto status = + snapshot(pull_free_image_cb, img_out, 1000ms, *cursor); + switch (status) { + case platf::capture_e::reinit: + case platf::capture_e::error: + case platf::capture_e::interrupted: + return status; + case platf::capture_e::timeout: + if (!push_captured_image_cb(std::move(img_out), false)) { + return platf::capture_e::ok; + } + break; + case platf::capture_e::ok: + if (!push_captured_image_cb(std::move(img_out), true)) { + return platf::capture_e::ok; + } + break; + default: + BOOST_LOG(error) << "Unrecognized capture status ["sv + << (int)status << ']'; + return status; + } } return capture_e::ok; - } } - std::shared_ptr - alloc_img() override { - auto img = std::make_shared(); - img->width = width; - img->height = height; - img->pixel_pitch = 4; - img->row_pitch = img->pixel_pitch * width; - img->data = new std::uint8_t[height * img->row_pitch]; + capture_e snapshot(const pull_free_image_cb_t &pull_free_image_cb, + std::shared_ptr &img_out, + std::chrono::milliseconds timeout, bool cursor) { + // The whole X server changed, so we must reinit everything + if (xattr.width != env_width || xattr.height != env_height) { + BOOST_LOG(warning) + << "X dimensions changed in SHM mode, request reinit"sv; + return capture_e::reinit; + } else { + auto img_cookie = xcb::shm_get_image_unchecked( + xcb.get(), display->root, offset_x, offset_y, width, height, ~0, + XCB_IMAGE_FORMAT_Z_PIXMAP, seg, 0); - return img; + xcb_img_t img_reply{ + xcb::shm_get_image_reply(xcb.get(), img_cookie, nullptr)}; + if (!img_reply) { + BOOST_LOG(error) << "Could not get image reply"sv; + return capture_e::reinit; + } + + if (!pull_free_image_cb(img_out)) { + return platf::capture_e::interrupted; + } + + std::copy_n((std::uint8_t *)data.data, frame_size(), img_out->data); + + if (cursor) { + blend_cursor(shm_xdisplay.get(), *img_out, offset_x, offset_y); + } + + return capture_e::ok; + } } - int - dummy_img(platf::img_t *img) override { - return 0; + std::shared_ptr alloc_img() override { + auto img = std::make_shared(); + img->width = width; + img->height = height; + img->pixel_pitch = 4; + img->row_pitch = img->pixel_pitch * width; + img->data = new std::uint8_t[height * img->row_pitch]; + + return img; } - int - init(const std::string &display_name, const ::video::config_t &config) { - if (x11_attr_t::init(display_name, config)) { - return 1; - } + int dummy_img(platf::img_t *img) override { return 0; } - shm_xdisplay.reset(x11::OpenDisplay(nullptr)); - xcb.reset(xcb::connect(nullptr, nullptr)); - if (xcb::connection_has_error(xcb.get())) { - return -1; - } + int init(const std::string &display_name, const ::video::config_t &config) { + if (x11_attr_t::init(display_name, config)) { + return 1; + } - if (!xcb::get_extension_data(xcb.get(), xcb::shm_id)->present) { - BOOST_LOG(error) << "Missing SHM extension"sv; + shm_xdisplay.reset(x11::OpenDisplay(nullptr)); + xcb.reset(xcb::connect(nullptr, nullptr)); + if (xcb::connection_has_error(xcb.get())) { + return -1; + } - return -1; - } + if (!xcb::get_extension_data(xcb.get(), xcb::shm_id)->present) { + BOOST_LOG(error) << "Missing SHM extension"sv; - auto iter = xcb::setup_roots_iterator(xcb::get_setup(xcb.get())); - display = iter.data; - seg = xcb::generate_id(xcb.get()); + return -1; + } - shm_id.id = shmget(IPC_PRIVATE, frame_size(), IPC_CREAT | 0777); - if (shm_id.id == -1) { - BOOST_LOG(error) << "shmget failed"sv; - return -1; - } + auto iter = xcb::setup_roots_iterator(xcb::get_setup(xcb.get())); + display = iter.data; + seg = xcb::generate_id(xcb.get()); - xcb::shm_attach(xcb.get(), seg, shm_id.id, false); - data.data = shmat(shm_id.id, nullptr, 0); + shm_id.id = shmget(IPC_PRIVATE, frame_size(), IPC_CREAT | 0777); + if (shm_id.id == -1) { + BOOST_LOG(error) << "shmget failed"sv; + return -1; + } - if ((uintptr_t) data.data == -1) { - BOOST_LOG(error) << "shmat failed"sv; + xcb::shm_attach(xcb.get(), seg, shm_id.id, false); + data.data = shmat(shm_id.id, nullptr, 0); - return -1; - } + if ((uintptr_t)data.data == -1) { + BOOST_LOG(error) << "shmat failed"sv; - return 0; + return -1; + } + + return 0; } - std::uint32_t - frame_size() { - return width * height * 4; - } - }; + std::uint32_t frame_size() { return width * height * 4; } +}; - std::shared_ptr - x11_display(platf::mem_type_e hwdevice_type, const std::string &display_name, const ::video::config_t &config) { - if (hwdevice_type != platf::mem_type_e::system && hwdevice_type != platf::mem_type_e::vaapi && hwdevice_type != platf::mem_type_e::cuda) { - BOOST_LOG(error) << "Could not initialize x11 display with the given hw device type"sv; - return nullptr; +std::shared_ptr x11_display(platf::mem_type_e hwdevice_type, + const std::string &display_name, + const ::video::config_t &config) { + if (hwdevice_type != platf::mem_type_e::system && + hwdevice_type != platf::mem_type_e::vaapi && + hwdevice_type != platf::mem_type_e::cuda) { + BOOST_LOG(error) + << "Could not initialize x11 display with the given hw device type"sv; + return nullptr; } - if (xcb::init_shm() || xcb::init() || x11::init() || x11::rr::init() || x11::fix::init()) { - BOOST_LOG(error) << "Couldn't init x11 libraries"sv; + if (xcb::init_shm() || xcb::init() || x11::init() || x11::rr::init() || + x11::fix::init()) { + BOOST_LOG(error) << "Couldn't init x11 libraries"sv; - return nullptr; + return nullptr; } // Attempt to use shared memory X11 to avoid copying the frame @@ -766,148 +764,131 @@ namespace platf { auto status = shm_disp->init(display_name, config); if (status > 0) { - // x11_attr_t::init() failed, don't bother trying again. - return nullptr; + // x11_attr_t::init() failed, don't bother trying again. + return nullptr; } if (status == 0) { - return shm_disp; + return shm_disp; } // Fallback auto x11_disp = std::make_shared(hwdevice_type); if (x11_disp->init(display_name, config)) { - return nullptr; + return nullptr; } return x11_disp; - } +} - std::vector - x11_display_names() { +std::vector x11_display_names() { if (load_x11() || load_xcb()) { - BOOST_LOG(error) << "Couldn't init x11 libraries"sv; + BOOST_LOG(error) << "Couldn't init x11 libraries"sv; - return {}; + return {}; } BOOST_LOG(info) << "Detecting monitors"sv; - x11::xdisplay_t xdisplay { x11::OpenDisplay(nullptr) }; + x11::xdisplay_t xdisplay{x11::OpenDisplay(nullptr)}; if (!xdisplay) { - return {}; + return {}; } auto xwindow = DefaultRootWindow(xdisplay.get()); - screen_res_t screenr { x11::rr::GetScreenResources(xdisplay.get(), xwindow) }; + screen_res_t screenr{x11::rr::GetScreenResources(xdisplay.get(), xwindow)}; int output = screenr->noutput; int monitor = 0; for (int x = 0; x < output; ++x) { - output_info_t out_info { x11::rr::GetOutputInfo(xdisplay.get(), screenr.get(), screenr->outputs[x]) }; - if (out_info) { - BOOST_LOG(info) << "Detected monitor "sv << monitor << ": "sv << out_info->name << ", connected: "sv << (out_info->connection == RR_Connected); - ++monitor; - } + output_info_t out_info{x11::rr::GetOutputInfo( + xdisplay.get(), screenr.get(), screenr->outputs[x])}; + if (out_info) { + BOOST_LOG(info) + << "Detected monitor "sv << monitor << ": "sv << out_info->name + << ", connected: "sv << (out_info->connection == RR_Connected); + ++monitor; + } } std::vector names; names.reserve(monitor); for (auto x = 0; x < monitor; ++x) { - names.emplace_back(std::to_string(x)); + names.emplace_back(std::to_string(x)); } return names; - } +} - void - freeImage(XImage *p) { - XDestroyImage(p); - } - void - freeX(XFixesCursorImage *p) { - x11::Free(p); - } +void freeImage(XImage *p) { XDestroyImage(p); } +void freeX(XFixesCursorImage *p) { x11::Free(p); } - int - load_xcb() { +int load_xcb() { // This will be called once only static int xcb_status = xcb::init_shm() || xcb::init(); return xcb_status; - } +} - int - load_x11() { +int load_x11() { // This will be called once only - static int x11_status = - window_system == window_system_e::NONE || - x11::init() || x11::rr::init() || x11::fix::init(); + static int x11_status = window_system == window_system_e::NONE || + x11::init() || x11::rr::init() || x11::fix::init(); return x11_status; - } +} - namespace x11 { - std::optional - cursor_t::make() { - if (load_x11()) { +namespace x11 { +std::optional cursor_t::make() { + if (load_x11()) { return std::nullopt; - } - - cursor_t cursor; - - cursor.ctx.reset((cursor_ctx_t::pointer) x11::OpenDisplay(nullptr)); - - return cursor; } - void - cursor_t::capture(egl::cursor_t &img) { - auto display = (xdisplay_t::pointer) ctx.get(); + cursor_t cursor; - xcursor_t xcursor = fix::GetCursorImage(display); + cursor.ctx.reset((cursor_ctx_t::pointer)x11::OpenDisplay(nullptr)); - if (img.serial != xcursor->cursor_serial) { + return cursor; +} + +void cursor_t::capture(egl::cursor_t &img) { + auto display = (xdisplay_t::pointer)ctx.get(); + + xcursor_t xcursor = fix::GetCursorImage(display); + + if (img.serial != xcursor->cursor_serial) { auto buf_size = xcursor->width * xcursor->height * sizeof(int); if (img.buffer.size() < buf_size) { - img.buffer.resize(buf_size); + img.buffer.resize(buf_size); } - std::transform(xcursor->pixels, xcursor->pixels + buf_size / 4, (int *) img.buffer.data(), [](long pixel) -> int { - return pixel; - }); - } - - img.data = img.buffer.data(); - img.width = xcursor->width; - img.height = xcursor->height; - img.x = xcursor->x - xcursor->xhot; - img.y = xcursor->y - xcursor->yhot; - img.pixel_pitch = 4; - img.row_pitch = img.pixel_pitch * img.width; - img.serial = xcursor->cursor_serial; + std::transform(xcursor->pixels, xcursor->pixels + buf_size / 4, + (int *)img.buffer.data(), + [](long pixel) -> int { return pixel; }); } - void - cursor_t::blend(img_t &img, int offsetX, int offsetY) { - blend_cursor((xdisplay_t::pointer) ctx.get(), img, offsetX, offsetY); - } + img.data = img.buffer.data(); + img.width = xcursor->width; + img.height = xcursor->height; + img.x = xcursor->x - xcursor->xhot; + img.y = xcursor->y - xcursor->yhot; + img.pixel_pitch = 4; + img.row_pitch = img.pixel_pitch * img.width; + img.serial = xcursor->cursor_serial; +} - xdisplay_t - make_display() { - return OpenDisplay(nullptr); - } +void cursor_t::blend(img_t &img, int offsetX, int offsetY) { + blend_cursor((xdisplay_t::pointer)ctx.get(), img, offsetX, offsetY); +} - void - freeDisplay(_XDisplay *xdisplay) { - CloseDisplay(xdisplay); - } +xdisplay_t make_display() { return OpenDisplay(nullptr); } - void - freeCursorCtx(cursor_ctx_t::pointer ctx) { - CloseDisplay((xdisplay_t::pointer) ctx); - } - } // namespace x11 +void freeDisplay(_XDisplay *xdisplay) { CloseDisplay(xdisplay); } + +void freeCursorCtx(cursor_ctx_t::pointer ctx) { + CloseDisplay((xdisplay_t::pointer)ctx); +} +} // namespace x11 } // namespace platf diff --git a/src/platform/linux/x11grab.h b/src/platform/linux/x11grab.h index 24e96f6a..539f1579 100644 --- a/src/platform/linux/x11grab.h +++ b/src/platform/linux/x11grab.h @@ -13,28 +13,24 @@ extern "C" struct _XDisplay; namespace egl { - class cursor_t; +class cursor_t; } namespace platf::x11 { #ifdef SUNSHINE_BUILD_X11 - struct cursor_ctx_raw_t; - void - freeCursorCtx(cursor_ctx_raw_t *ctx); - void - freeDisplay(_XDisplay *xdisplay); +struct cursor_ctx_raw_t; +void freeCursorCtx(cursor_ctx_raw_t *ctx); +void freeDisplay(_XDisplay *xdisplay); - using cursor_ctx_t = util::safe_ptr; - using xdisplay_t = util::safe_ptr<_XDisplay, freeDisplay>; +using cursor_ctx_t = util::safe_ptr; +using xdisplay_t = util::safe_ptr<_XDisplay, freeDisplay>; - class cursor_t { - public: - static std::optional - make(); +class cursor_t { + public: + static std::optional make(); - void - capture(egl::cursor_t &img); + void capture(egl::cursor_t &img); /** * Capture and blend the cursor into the image @@ -42,30 +38,24 @@ namespace platf::x11 { * img <-- destination image * offsetX, offsetY <--- Top left corner of the virtual screen */ - void - blend(img_t &img, int offsetX, int offsetY); + void blend(img_t &img, int offsetX, int offsetY); cursor_ctx_t ctx; - }; +}; - xdisplay_t - make_display(); +xdisplay_t make_display(); #else - // It's never something different from nullptr - util::safe_ptr<_XDisplay, std::default_delete<_XDisplay>>; +// It's never something different from nullptr +util::safe_ptr<_XDisplay, std::default_delete<_XDisplay> >; - class cursor_t { - public: - static std::optional - make() { return std::nullopt; } +class cursor_t { + public: + static std::optional make() { return std::nullopt; } - void - capture(egl::cursor_t &) {} - void - blend(img_t &, int, int) {} - }; + void capture(egl::cursor_t &) {} + void blend(img_t &, int, int) {} +}; - xdisplay_t - make_display() { return nullptr; } +xdisplay_t make_display() { return nullptr; } #endif } // namespace platf::x11 diff --git a/src/platform/windows/PolicyConfig.h b/src/platform/windows/PolicyConfig.h index 21772227..e0571995 100644 --- a/src/platform/windows/PolicyConfig.h +++ b/src/platform/windows/PolicyConfig.h @@ -11,15 +11,21 @@ #include #ifdef __MINGW32__ - #undef DEFINE_GUID - #ifdef __cplusplus - #define DEFINE_GUID(name, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8) EXTERN_C const GUID DECLSPEC_SELECTANY name = { l, w1, w2, { b1, b2, b3, b4, b5, b6, b7, b8 } } - #else - #define DEFINE_GUID(name, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8) const GUID DECLSPEC_SELECTANY name = { l, w1, w2, { b1, b2, b3, b4, b5, b6, b7, b8 } } - #endif +#undef DEFINE_GUID +#ifdef __cplusplus +#define DEFINE_GUID(name, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8) \ + EXTERN_C const GUID DECLSPEC_SELECTANY name = { \ + l, w1, w2, {b1, b2, b3, b4, b5, b6, b7, b8}} +#else +#define DEFINE_GUID(name, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8) \ + const GUID DECLSPEC_SELECTANY name = { \ + l, w1, w2, {b1, b2, b3, b4, b5, b6, b7, b8}} +#endif -DEFINE_GUID(IID_IPolicyConfig, 0xf8679f50, 0x850a, 0x41cf, 0x9c, 0x72, 0x43, 0x0f, 0x29, 0x02, 0x90, 0xc8); -DEFINE_GUID(CLSID_CPolicyConfigClient, 0x870af99c, 0x171d, 0x4f9e, 0xaf, 0x0d, 0xe6, 0x3d, 0xf4, 0x0c, 0x2b, 0xc9); +DEFINE_GUID(IID_IPolicyConfig, 0xf8679f50, 0x850a, 0x41cf, 0x9c, 0x72, 0x43, + 0x0f, 0x29, 0x02, 0x90, 0xc8); +DEFINE_GUID(CLSID_CPolicyConfigClient, 0x870af99c, 0x171d, 0x4f9e, 0xaf, 0x0d, + 0xe6, 0x3d, 0xf4, 0x0c, 0x2b, 0xc9); #endif @@ -38,66 +44,39 @@ class DECLSPEC_UUID("870af99c-171d-4f9e-af0d-e63df40c2bc9") CPolicyConfigClient; // // @compatible: Windows 7 and Later // ---------------------------------------------------------------------------- -interface IPolicyConfig: public IUnknown { -public: - virtual HRESULT - GetMixFormat( - PCWSTR, - WAVEFORMATEX **); +interface IPolicyConfig : public IUnknown { + public: + virtual HRESULT GetMixFormat(PCWSTR, WAVEFORMATEX **); - virtual HRESULT STDMETHODCALLTYPE - GetDeviceFormat( - PCWSTR, - INT, - WAVEFORMATEX **); + virtual HRESULT STDMETHODCALLTYPE GetDeviceFormat(PCWSTR, INT, + WAVEFORMATEX **); - virtual HRESULT STDMETHODCALLTYPE ResetDeviceFormat( - PCWSTR); + virtual HRESULT STDMETHODCALLTYPE ResetDeviceFormat(PCWSTR); - virtual HRESULT STDMETHODCALLTYPE - SetDeviceFormat( - PCWSTR, - WAVEFORMATEX *, - WAVEFORMATEX *); + virtual HRESULT STDMETHODCALLTYPE SetDeviceFormat(PCWSTR, WAVEFORMATEX *, + WAVEFORMATEX *); - virtual HRESULT STDMETHODCALLTYPE GetProcessingPeriod( - PCWSTR, - INT, - PINT64, - PINT64); + virtual HRESULT STDMETHODCALLTYPE GetProcessingPeriod(PCWSTR, INT, PINT64, + PINT64); - virtual HRESULT STDMETHODCALLTYPE SetProcessingPeriod( - PCWSTR, - PINT64); + virtual HRESULT STDMETHODCALLTYPE SetProcessingPeriod(PCWSTR, PINT64); - virtual HRESULT STDMETHODCALLTYPE - GetShareMode( - PCWSTR, - struct DeviceShareMode *); + virtual HRESULT STDMETHODCALLTYPE GetShareMode(PCWSTR, + struct DeviceShareMode *); - virtual HRESULT STDMETHODCALLTYPE - SetShareMode( - PCWSTR, - struct DeviceShareMode *); + virtual HRESULT STDMETHODCALLTYPE SetShareMode(PCWSTR, + struct DeviceShareMode *); - virtual HRESULT STDMETHODCALLTYPE - GetPropertyValue( - PCWSTR, - const PROPERTYKEY &, - PROPVARIANT *); + virtual HRESULT STDMETHODCALLTYPE GetPropertyValue(PCWSTR, + const PROPERTYKEY &, + PROPVARIANT *); - virtual HRESULT STDMETHODCALLTYPE - SetPropertyValue( - PCWSTR, - const PROPERTYKEY &, - PROPVARIANT *); + virtual HRESULT STDMETHODCALLTYPE SetPropertyValue(PCWSTR, + const PROPERTYKEY &, + PROPVARIANT *); - virtual HRESULT STDMETHODCALLTYPE - SetDefaultEndpoint( - PCWSTR wszDeviceId, - ERole eRole); + virtual HRESULT STDMETHODCALLTYPE SetDefaultEndpoint(PCWSTR wszDeviceId, + ERole eRole); - virtual HRESULT STDMETHODCALLTYPE SetEndpointVisibility( - PCWSTR, - INT); + virtual HRESULT STDMETHODCALLTYPE SetEndpointVisibility(PCWSTR, INT); }; diff --git a/src/platform/windows/display.h b/src/platform/windows/display.h index 2d480c59..041e44d5 100644 --- a/src/platform/windows/display.h +++ b/src/platform/windows/display.h @@ -16,123 +16,140 @@ #include "src/video.h" namespace platf::dxgi { - extern const char *format_str[]; +extern const char *format_str[]; - // Add D3D11_CREATE_DEVICE_DEBUG here to enable the D3D11 debug runtime. - // You should have a debugger like WinDbg attached to receive debug messages. - auto constexpr D3D11_CREATE_DEVICE_FLAGS = D3D11_CREATE_DEVICE_VIDEO_SUPPORT; +// Add D3D11_CREATE_DEVICE_DEBUG here to enable the D3D11 debug runtime. +// You should have a debugger like WinDbg attached to receive debug messages. +auto constexpr D3D11_CREATE_DEVICE_FLAGS = D3D11_CREATE_DEVICE_VIDEO_SUPPORT; - template - void - Release(T *dxgi) { +template +void Release(T *dxgi) { dxgi->Release(); - } +} - using factory1_t = util::safe_ptr>; - using dxgi_t = util::safe_ptr>; - using dxgi1_t = util::safe_ptr>; - using device_t = util::safe_ptr>; - using device1_t = util::safe_ptr>; - using device_ctx_t = util::safe_ptr>; - using adapter_t = util::safe_ptr>; - using output_t = util::safe_ptr>; - using output1_t = util::safe_ptr>; - using output5_t = util::safe_ptr>; - using output6_t = util::safe_ptr>; - using dup_t = util::safe_ptr>; - using texture2d_t = util::safe_ptr>; - using texture1d_t = util::safe_ptr>; - using resource_t = util::safe_ptr>; - using resource1_t = util::safe_ptr>; - using multithread_t = util::safe_ptr>; - using vs_t = util::safe_ptr>; - using ps_t = util::safe_ptr>; - using blend_t = util::safe_ptr>; - using input_layout_t = util::safe_ptr>; - using render_target_t = util::safe_ptr>; - using shader_res_t = util::safe_ptr>; - using buf_t = util::safe_ptr>; - using raster_state_t = util::safe_ptr>; - using sampler_state_t = util::safe_ptr>; - using blob_t = util::safe_ptr>; - using depth_stencil_state_t = util::safe_ptr>; - using depth_stencil_view_t = util::safe_ptr>; - using keyed_mutex_t = util::safe_ptr>; +using factory1_t = util::safe_ptr>; +using dxgi_t = util::safe_ptr>; +using dxgi1_t = util::safe_ptr>; +using device_t = util::safe_ptr>; +using device1_t = util::safe_ptr>; +using device_ctx_t = + util::safe_ptr>; +using adapter_t = util::safe_ptr>; +using output_t = util::safe_ptr>; +using output1_t = util::safe_ptr>; +using output5_t = util::safe_ptr>; +using output6_t = util::safe_ptr>; +using dup_t = + util::safe_ptr>; +using texture2d_t = util::safe_ptr>; +using texture1d_t = util::safe_ptr>; +using resource_t = util::safe_ptr>; +using resource1_t = util::safe_ptr>; +using multithread_t = + util::safe_ptr>; +using vs_t = util::safe_ptr>; +using ps_t = util::safe_ptr>; +using blend_t = util::safe_ptr>; +using input_layout_t = + util::safe_ptr>; +using render_target_t = + util::safe_ptr>; +using shader_res_t = + util::safe_ptr>; +using buf_t = util::safe_ptr>; +using raster_state_t = + util::safe_ptr>; +using sampler_state_t = + util::safe_ptr>; +using blob_t = util::safe_ptr>; +using depth_stencil_state_t = + util::safe_ptr>; +using depth_stencil_view_t = + util::safe_ptr>; +using keyed_mutex_t = util::safe_ptr>; - namespace video { - using device_t = util::safe_ptr>; - using ctx_t = util::safe_ptr>; - using processor_t = util::safe_ptr>; - using processor_out_t = util::safe_ptr>; - using processor_in_t = util::safe_ptr>; - using processor_enum_t = util::safe_ptr>; - } // namespace video +namespace video { +using device_t = util::safe_ptr>; +using ctx_t = util::safe_ptr>; +using processor_t = + util::safe_ptr>; +using processor_out_t = util::safe_ptr>; +using processor_in_t = util::safe_ptr>; +using processor_enum_t = + util::safe_ptr>; +} // namespace video - class hwdevice_t; - struct cursor_t { +class hwdevice_t; +struct cursor_t { std::vector img_data; DXGI_OUTDUPL_POINTER_SHAPE_INFO shape_info; int x, y; bool visible; - }; +}; - class gpu_cursor_t { - public: - gpu_cursor_t(): - cursor_view { 0, 0, 0, 0, 0.0f, 1.0f } {}; +class gpu_cursor_t { + public: + gpu_cursor_t() : cursor_view{0, 0, 0, 0, 0.0f, 1.0f} {}; - void - set_pos(LONG topleft_x, LONG topleft_y, LONG display_width, LONG display_height, DXGI_MODE_ROTATION display_rotation, bool visible) { - this->topleft_x = topleft_x; - this->topleft_y = topleft_y; - this->display_width = display_width; - this->display_height = display_height; - this->display_rotation = display_rotation; - this->visible = visible; - update_viewport(); + void set_pos(LONG topleft_x, LONG topleft_y, LONG display_width, + LONG display_height, DXGI_MODE_ROTATION display_rotation, + bool visible) { + this->topleft_x = topleft_x; + this->topleft_y = topleft_y; + this->display_width = display_width; + this->display_height = display_height; + this->display_rotation = display_rotation; + this->visible = visible; + update_viewport(); } - void - set_texture(LONG texture_width, LONG texture_height, texture2d_t &&texture) { - this->texture = std::move(texture); - this->texture_width = texture_width; - this->texture_height = texture_height; - update_viewport(); + void set_texture(LONG texture_width, LONG texture_height, + texture2d_t &&texture) { + this->texture = std::move(texture); + this->texture_width = texture_width; + this->texture_height = texture_height; + update_viewport(); } - void - update_viewport() { - switch (display_rotation) { - case DXGI_MODE_ROTATION_UNSPECIFIED: - case DXGI_MODE_ROTATION_IDENTITY: - cursor_view.TopLeftX = topleft_x; - cursor_view.TopLeftY = topleft_y; - cursor_view.Width = texture_width; - cursor_view.Height = texture_height; - break; + void update_viewport() { + switch (display_rotation) { + case DXGI_MODE_ROTATION_UNSPECIFIED: + case DXGI_MODE_ROTATION_IDENTITY: + cursor_view.TopLeftX = topleft_x; + cursor_view.TopLeftY = topleft_y; + cursor_view.Width = texture_width; + cursor_view.Height = texture_height; + break; - case DXGI_MODE_ROTATION_ROTATE90: - cursor_view.TopLeftX = topleft_y; - cursor_view.TopLeftY = display_width - texture_width - topleft_x; - cursor_view.Width = texture_height; - cursor_view.Height = texture_width; - break; + case DXGI_MODE_ROTATION_ROTATE90: + cursor_view.TopLeftX = topleft_y; + cursor_view.TopLeftY = + display_width - texture_width - topleft_x; + cursor_view.Width = texture_height; + cursor_view.Height = texture_width; + break; - case DXGI_MODE_ROTATION_ROTATE180: - cursor_view.TopLeftX = display_width - texture_width - topleft_x; - cursor_view.TopLeftY = display_height - texture_height - topleft_y; - cursor_view.Width = texture_width; - cursor_view.Height = texture_height; - break; + case DXGI_MODE_ROTATION_ROTATE180: + cursor_view.TopLeftX = + display_width - texture_width - topleft_x; + cursor_view.TopLeftY = + display_height - texture_height - topleft_y; + cursor_view.Width = texture_width; + cursor_view.Height = texture_height; + break; - case DXGI_MODE_ROTATION_ROTATE270: - cursor_view.TopLeftX = display_height - texture_height - topleft_y; - cursor_view.TopLeftY = topleft_x; - cursor_view.Width = texture_height; - cursor_view.Height = texture_width; - break; - } + case DXGI_MODE_ROTATION_ROTATE270: + cursor_view.TopLeftX = + display_height - texture_height - topleft_y; + cursor_view.TopLeftY = topleft_x; + cursor_view.Width = texture_height; + cursor_view.Height = texture_width; + break; + } } texture2d_t texture; @@ -151,34 +168,32 @@ namespace platf::dxgi { D3D11_VIEWPORT cursor_view; bool visible; - }; +}; - class duplication_t { - public: +class duplication_t { + public: dup_t dup; - bool has_frame {}; - std::chrono::steady_clock::time_point last_protected_content_warning_time {}; + bool has_frame{}; + std::chrono::steady_clock::time_point last_protected_content_warning_time{}; - capture_e - next_frame(DXGI_OUTDUPL_FRAME_INFO &frame_info, std::chrono::milliseconds timeout, resource_t::pointer *res_p); - capture_e - reset(dup_t::pointer dup_p = dup_t::pointer()); - capture_e - release_frame(); + capture_e next_frame(DXGI_OUTDUPL_FRAME_INFO &frame_info, + std::chrono::milliseconds timeout, + resource_t::pointer *res_p); + capture_e reset(dup_t::pointer dup_p = dup_t::pointer()); + capture_e release_frame(); ~duplication_t(); - }; +}; - class display_base_t: public display_t { - public: - int - init(const ::video::config_t &config, const std::string &display_name); +class display_base_t : public display_t { + public: + int init(const ::video::config_t &config, const std::string &display_name); - void - high_precision_sleep(std::chrono::nanoseconds duration); + void high_precision_sleep(std::chrono::nanoseconds duration); - capture_e - capture(const push_captured_image_cb_t &push_captured_image_cb, const pull_free_image_cb_t &pull_free_image_cb, bool *cursor) override; + capture_e capture(const push_captured_image_cb_t &push_captured_image_cb, + const pull_free_image_cb_t &pull_free_image_cb, + bool *cursor) override; factory1_t factory; adapter_t adapter; @@ -201,127 +216,119 @@ namespace platf::dxgi { util::safe_ptr_v2, BOOL, CloseHandle> timer; typedef enum _D3DKMT_SCHEDULINGPRIORITYCLASS { - D3DKMT_SCHEDULINGPRIORITYCLASS_IDLE, - D3DKMT_SCHEDULINGPRIORITYCLASS_BELOW_NORMAL, - D3DKMT_SCHEDULINGPRIORITYCLASS_NORMAL, - D3DKMT_SCHEDULINGPRIORITYCLASS_ABOVE_NORMAL, - D3DKMT_SCHEDULINGPRIORITYCLASS_HIGH, - D3DKMT_SCHEDULINGPRIORITYCLASS_REALTIME + D3DKMT_SCHEDULINGPRIORITYCLASS_IDLE, + D3DKMT_SCHEDULINGPRIORITYCLASS_BELOW_NORMAL, + D3DKMT_SCHEDULINGPRIORITYCLASS_NORMAL, + D3DKMT_SCHEDULINGPRIORITYCLASS_ABOVE_NORMAL, + D3DKMT_SCHEDULINGPRIORITYCLASS_HIGH, + D3DKMT_SCHEDULINGPRIORITYCLASS_REALTIME } D3DKMT_SCHEDULINGPRIORITYCLASS; typedef UINT D3DKMT_HANDLE; typedef struct _D3DKMT_OPENADAPTERFROMLUID { - LUID AdapterLuid; - D3DKMT_HANDLE hAdapter; + LUID AdapterLuid; + D3DKMT_HANDLE hAdapter; } D3DKMT_OPENADAPTERFROMLUID; typedef struct _D3DKMT_WDDM_2_7_CAPS { - union { - struct - { - UINT HwSchSupported : 1; - UINT HwSchEnabled : 1; - UINT HwSchEnabledByDefault : 1; - UINT IndependentVidPnVSyncControl : 1; - UINT Reserved : 28; + union { + struct { + UINT HwSchSupported : 1; + UINT HwSchEnabled : 1; + UINT HwSchEnabledByDefault : 1; + UINT IndependentVidPnVSyncControl : 1; + UINT Reserved : 28; + }; + UINT Value; }; - UINT Value; - }; } D3DKMT_WDDM_2_7_CAPS; typedef struct _D3DKMT_QUERYADAPTERINFO { - D3DKMT_HANDLE hAdapter; - UINT Type; - VOID *pPrivateDriverData; - UINT PrivateDriverDataSize; + D3DKMT_HANDLE hAdapter; + UINT Type; + VOID *pPrivateDriverData; + UINT PrivateDriverDataSize; } D3DKMT_QUERYADAPTERINFO; const UINT KMTQAITYPE_WDDM_2_7_CAPS = 70; typedef struct _D3DKMT_CLOSEADAPTER { - D3DKMT_HANDLE hAdapter; + D3DKMT_HANDLE hAdapter; } D3DKMT_CLOSEADAPTER; - typedef NTSTATUS(WINAPI *PD3DKMTSetProcessSchedulingPriorityClass)(HANDLE, D3DKMT_SCHEDULINGPRIORITYCLASS); - typedef NTSTATUS(WINAPI *PD3DKMTOpenAdapterFromLuid)(D3DKMT_OPENADAPTERFROMLUID *); - typedef NTSTATUS(WINAPI *PD3DKMTQueryAdapterInfo)(D3DKMT_QUERYADAPTERINFO *); + typedef NTSTATUS(WINAPI *PD3DKMTSetProcessSchedulingPriorityClass)( + HANDLE, D3DKMT_SCHEDULINGPRIORITYCLASS); + typedef NTSTATUS(WINAPI *PD3DKMTOpenAdapterFromLuid)( + D3DKMT_OPENADAPTERFROMLUID *); + typedef NTSTATUS(WINAPI *PD3DKMTQueryAdapterInfo)( + D3DKMT_QUERYADAPTERINFO *); typedef NTSTATUS(WINAPI *PD3DKMTCloseAdapter)(D3DKMT_CLOSEADAPTER *); - virtual bool - is_hdr() override; - virtual bool - get_hdr_metadata(SS_HDR_METADATA &metadata) override; + virtual bool is_hdr() override; + virtual bool get_hdr_metadata(SS_HDR_METADATA &metadata) override; - protected: - int - get_pixel_pitch() { - return (capture_format == DXGI_FORMAT_R16G16B16A16_FLOAT) ? 8 : 4; + protected: + int get_pixel_pitch() { + return (capture_format == DXGI_FORMAT_R16G16B16A16_FLOAT) ? 8 : 4; } - const char * - dxgi_format_to_string(DXGI_FORMAT format); - const char * - colorspace_to_string(DXGI_COLOR_SPACE_TYPE type); + const char *dxgi_format_to_string(DXGI_FORMAT format); + const char *colorspace_to_string(DXGI_COLOR_SPACE_TYPE type); - virtual capture_e - snapshot(const pull_free_image_cb_t &pull_free_image_cb, std::shared_ptr &img_out, std::chrono::milliseconds timeout, bool cursor_visible) = 0; - virtual int - complete_img(img_t *img, bool dummy) = 0; - virtual std::vector - get_supported_capture_formats() = 0; - }; + virtual capture_e snapshot(const pull_free_image_cb_t &pull_free_image_cb, + std::shared_ptr &img_out, + std::chrono::milliseconds timeout, + bool cursor_visible) = 0; + virtual int complete_img(img_t *img, bool dummy) = 0; + virtual std::vector get_supported_capture_formats() = 0; +}; - class display_ram_t: public display_base_t { - public: - virtual capture_e - snapshot(const pull_free_image_cb_t &pull_free_image_cb, std::shared_ptr &img_out, std::chrono::milliseconds timeout, bool cursor_visible) override; +class display_ram_t : public display_base_t { + public: + virtual capture_e snapshot(const pull_free_image_cb_t &pull_free_image_cb, + std::shared_ptr &img_out, + std::chrono::milliseconds timeout, + bool cursor_visible) override; - std::shared_ptr - alloc_img() override; - int - dummy_img(img_t *img) override; - int - complete_img(img_t *img, bool dummy) override; - std::vector - get_supported_capture_formats() override; + std::shared_ptr alloc_img() override; + int dummy_img(img_t *img) override; + int complete_img(img_t *img, bool dummy) override; + std::vector get_supported_capture_formats() override; - int - init(const ::video::config_t &config, const std::string &display_name); + int init(const ::video::config_t &config, const std::string &display_name); - std::unique_ptr - make_avcodec_encode_device(pix_fmt_e pix_fmt) override; + std::unique_ptr make_avcodec_encode_device( + pix_fmt_e pix_fmt) override; cursor_t cursor; D3D11_MAPPED_SUBRESOURCE img_info; texture2d_t texture; - }; +}; - class display_vram_t: public display_base_t, public std::enable_shared_from_this { - public: - virtual capture_e - snapshot(const pull_free_image_cb_t &pull_free_image_cb, std::shared_ptr &img_out, std::chrono::milliseconds timeout, bool cursor_visible) override; +class display_vram_t : public display_base_t, + public std::enable_shared_from_this { + public: + virtual capture_e snapshot(const pull_free_image_cb_t &pull_free_image_cb, + std::shared_ptr &img_out, + std::chrono::milliseconds timeout, + bool cursor_visible) override; - std::shared_ptr - alloc_img() override; - int - dummy_img(img_t *img_base) override; - int - complete_img(img_t *img_base, bool dummy) override; - std::vector - get_supported_capture_formats() override; + std::shared_ptr alloc_img() override; + int dummy_img(img_t *img_base) override; + int complete_img(img_t *img_base, bool dummy) override; + std::vector get_supported_capture_formats() override; - int - init(const ::video::config_t &config, const std::string &display_name); + int init(const ::video::config_t &config, const std::string &display_name); - bool - is_codec_supported(std::string_view name, const ::video::config_t &config) override; + bool is_codec_supported(std::string_view name, + const ::video::config_t &config) override; - std::unique_ptr - make_avcodec_encode_device(pix_fmt_e pix_fmt) override; + std::unique_ptr make_avcodec_encode_device( + pix_fmt_e pix_fmt) override; - std::unique_ptr - make_nvenc_encode_device(pix_fmt_e pix_fmt) override; + std::unique_ptr make_nvenc_encode_device( + pix_fmt_e pix_fmt) override; sampler_state_t sampler_linear; @@ -337,8 +344,9 @@ namespace platf::dxgi { texture2d_t old_surface_delayed_destruction; std::chrono::steady_clock::time_point old_surface_timestamp; - std::variant> last_frame_variant; + std::variant> + last_frame_variant; std::atomic next_image_id; - }; +}; } // namespace platf::dxgi diff --git a/src/platform/windows/display_base.cpp b/src/platform/windows/display_base.cpp index a8dfbaa5..4f289e16 100644 --- a/src/platform/windows/display_base.cpp +++ b/src/platform/windows/display_base.cpp @@ -2,11 +2,11 @@ * @file src/platform/windows/display_base.cpp * @brief todo */ -#include -#include #include #include +#include +#include // We have to include boost/process.hpp before display.h due to WinSock.h, // but that prevents the definition of NTSTATUS so we must define it ourself. @@ -20,239 +20,269 @@ typedef long NTSTATUS; #include "src/video.h" namespace platf { - using namespace std::literals; +using namespace std::literals; } namespace platf::dxgi { - namespace bp = boost::process; +namespace bp = boost::process; - capture_e - duplication_t::next_frame(DXGI_OUTDUPL_FRAME_INFO &frame_info, std::chrono::milliseconds timeout, resource_t::pointer *res_p) { +capture_e duplication_t::next_frame(DXGI_OUTDUPL_FRAME_INFO &frame_info, + std::chrono::milliseconds timeout, + resource_t::pointer *res_p) { auto capture_status = release_frame(); if (capture_status != capture_e::ok) { - return capture_status; + return capture_status; } auto status = dup->AcquireNextFrame(timeout.count(), &frame_info, res_p); switch (status) { - case S_OK: - // ProtectedContentMaskedOut seems to semi-randomly be TRUE or FALSE even when protected content - // is on screen the whole time, so we can't just print when it changes. Instead we'll keep track - // of the last time we printed the warning and print another if we haven't printed one recently. - if (frame_info.ProtectedContentMaskedOut && std::chrono::steady_clock::now() > last_protected_content_warning_time + 10s) { - BOOST_LOG(warning) << "Windows is currently blocking DRM-protected content from capture. You may see black regions where this content would be."sv; - last_protected_content_warning_time = std::chrono::steady_clock::now(); - } + case S_OK: + // ProtectedContentMaskedOut seems to semi-randomly be TRUE or FALSE + // even when protected content is on screen the whole time, so we + // can't just print when it changes. Instead we'll keep track of the + // last time we printed the warning and print another if we haven't + // printed one recently. + if (frame_info.ProtectedContentMaskedOut && + std::chrono::steady_clock::now() > + last_protected_content_warning_time + 10s) { + BOOST_LOG(warning) + << "Windows is currently blocking DRM-protected content from capture. You may see black regions where this content would be."sv; + last_protected_content_warning_time = + std::chrono::steady_clock::now(); + } - has_frame = true; - return capture_e::ok; - case DXGI_ERROR_WAIT_TIMEOUT: - return capture_e::timeout; - case WAIT_ABANDONED: - case DXGI_ERROR_ACCESS_LOST: - case DXGI_ERROR_ACCESS_DENIED: - return capture_e::reinit; - default: - BOOST_LOG(error) << "Couldn't acquire next frame [0x"sv << util::hex(status).to_string_view(); - return capture_e::error; + has_frame = true; + return capture_e::ok; + case DXGI_ERROR_WAIT_TIMEOUT: + return capture_e::timeout; + case WAIT_ABANDONED: + case DXGI_ERROR_ACCESS_LOST: + case DXGI_ERROR_ACCESS_DENIED: + return capture_e::reinit; + default: + BOOST_LOG(error) << "Couldn't acquire next frame [0x"sv + << util::hex(status).to_string_view(); + return capture_e::error; } - } +} - capture_e - duplication_t::reset(dup_t::pointer dup_p) { +capture_e duplication_t::reset(dup_t::pointer dup_p) { auto capture_status = release_frame(); dup.reset(dup_p); return capture_status; - } +} - capture_e - duplication_t::release_frame() { +capture_e duplication_t::release_frame() { if (!has_frame) { - return capture_e::ok; + return capture_e::ok; } auto status = dup->ReleaseFrame(); has_frame = false; switch (status) { - case S_OK: - return capture_e::ok; + case S_OK: + return capture_e::ok; - case DXGI_ERROR_INVALID_CALL: - BOOST_LOG(warning) << "Duplication frame already released"; - return capture_e::ok; + case DXGI_ERROR_INVALID_CALL: + BOOST_LOG(warning) << "Duplication frame already released"; + return capture_e::ok; - case DXGI_ERROR_ACCESS_LOST: - return capture_e::reinit; + case DXGI_ERROR_ACCESS_LOST: + return capture_e::reinit; - default: - BOOST_LOG(error) << "Error while releasing duplication frame [0x"sv << util::hex(status).to_string_view(); - return capture_e::error; + default: + BOOST_LOG(error) << "Error while releasing duplication frame [0x"sv + << util::hex(status).to_string_view(); + return capture_e::error; } - } +} - duplication_t::~duplication_t() { - release_frame(); - } +duplication_t::~duplication_t() { release_frame(); } - void - display_base_t::high_precision_sleep(std::chrono::nanoseconds duration) { +void display_base_t::high_precision_sleep(std::chrono::nanoseconds duration) { if (!timer) { - BOOST_LOG(error) << "Attempting high_precision_sleep() with uninitialized timer"; - return; + BOOST_LOG(error) + << "Attempting high_precision_sleep() with uninitialized timer"; + return; } if (duration < 0s) { - BOOST_LOG(error) << "Attempting high_precision_sleep() with negative duration"; - return; + BOOST_LOG(error) + << "Attempting high_precision_sleep() with negative duration"; + return; } if (duration > 5s) { - BOOST_LOG(error) << "Attempting high_precision_sleep() with unexpectedly large duration (>5s)"; - return; + BOOST_LOG(error) << "Attempting high_precision_sleep() with " + "unexpectedly large duration (>5s)"; + return; } LARGE_INTEGER due_time; due_time.QuadPart = duration.count() / -100; SetWaitableTimer(timer.get(), &due_time, 0, nullptr, nullptr, false); WaitForSingleObject(timer.get(), INFINITE); - } +} - capture_e - display_base_t::capture(const push_captured_image_cb_t &push_captured_image_cb, const pull_free_image_cb_t &pull_free_image_cb, bool *cursor) { +capture_e display_base_t::capture( + const push_captured_image_cb_t &push_captured_image_cb, + const pull_free_image_cb_t &pull_free_image_cb, bool *cursor) { auto adjust_client_frame_rate = [&]() -> DXGI_RATIONAL { - // Adjust capture frame interval when display refresh rate is not integral but very close to requested fps. - if (display_refresh_rate.Denominator > 1) { - DXGI_RATIONAL candidate = display_refresh_rate; - if (client_frame_rate % display_refresh_rate_rounded == 0) { - candidate.Numerator *= client_frame_rate / display_refresh_rate_rounded; + // Adjust capture frame interval when display refresh rate is not + // integral but very close to requested fps. + if (display_refresh_rate.Denominator > 1) { + DXGI_RATIONAL candidate = display_refresh_rate; + if (client_frame_rate % display_refresh_rate_rounded == 0) { + candidate.Numerator *= + client_frame_rate / display_refresh_rate_rounded; + } else if (display_refresh_rate_rounded % client_frame_rate == 0) { + candidate.Denominator *= + display_refresh_rate_rounded / client_frame_rate; + } + double candidate_rate = + (double)candidate.Numerator / candidate.Denominator; + // Can only decrease requested fps, otherwise client may start + // accumulating frames and suffer increased latency. + if (client_frame_rate > candidate_rate && + candidate_rate / client_frame_rate > 0.99) { + BOOST_LOG(info) << "Adjusted capture rate to " << candidate_rate + << "fps to better match display"; + return candidate; + } } - else if (display_refresh_rate_rounded % client_frame_rate == 0) { - candidate.Denominator *= display_refresh_rate_rounded / client_frame_rate; - } - double candidate_rate = (double) candidate.Numerator / candidate.Denominator; - // Can only decrease requested fps, otherwise client may start accumulating frames and suffer increased latency. - if (client_frame_rate > candidate_rate && candidate_rate / client_frame_rate > 0.99) { - BOOST_LOG(info) << "Adjusted capture rate to " << candidate_rate << "fps to better match display"; - return candidate; - } - } - return { (uint32_t) client_frame_rate, 1 }; + return {(uint32_t)client_frame_rate, 1}; }; DXGI_RATIONAL client_frame_rate_adjusted = adjust_client_frame_rate(); - std::optional frame_pacing_group_start; + std::optional + frame_pacing_group_start; uint32_t frame_pacing_group_frames = 0; - // Keep the display awake during capture. If the display goes to sleep during - // capture, best case is that capture stops until it powers back on. However, - // worst case it will trigger us to reinit DD, waking the display back up in - // a neverending cycle of waking and sleeping the display of an idle machine. + // Keep the display awake during capture. If the display goes to sleep + // during capture, best case is that capture stops until it powers back on. + // However, worst case it will trigger us to reinit DD, waking the display + // back up in a neverending cycle of waking and sleeping the display of an + // idle machine. SetThreadExecutionState(ES_CONTINUOUS | ES_DISPLAY_REQUIRED); - auto clear_display_required = util::fail_guard([]() { - SetThreadExecutionState(ES_CONTINUOUS); - }); + auto clear_display_required = + util::fail_guard([]() { SetThreadExecutionState(ES_CONTINUOUS); }); // stat_trackers::min_max_avg_tracker sleep_overshoot_tracker; while (true) { - // This will return false if the HDR state changes or for any number of other - // display or GPU changes. We should reinit to examine the updated state of - // the display subsystem. It is recommended to call this once per frame. - if (!factory->IsCurrent()) { - return platf::capture_e::reinit; - } - - platf::capture_e status = capture_e::ok; - std::shared_ptr img_out; - - // Try to continue frame pacing group, snapshot() is called with zero timeout after waiting for client frame interval - if (frame_pacing_group_start) { - const uint32_t seconds = (uint64_t) frame_pacing_group_frames * client_frame_rate_adjusted.Denominator / client_frame_rate_adjusted.Numerator; - const uint32_t remainder = (uint64_t) frame_pacing_group_frames * client_frame_rate_adjusted.Denominator % client_frame_rate_adjusted.Numerator; - const auto sleep_target = *frame_pacing_group_start + - std::chrono::nanoseconds(1s) * seconds + - std::chrono::nanoseconds(1s) * remainder / client_frame_rate_adjusted.Numerator; - const auto sleep_period = sleep_target - std::chrono::steady_clock::now(); - - if (sleep_period <= 0ns) { - // We missed next frame time, invalidating current frame pacing group - frame_pacing_group_start = std::nullopt; - frame_pacing_group_frames = 0; - status = capture_e::timeout; + // This will return false if the HDR state changes or for any number of + // other display or GPU changes. We should reinit to examine the updated + // state of the display subsystem. It is recommended to call this once + // per frame. + if (!factory->IsCurrent()) { + return platf::capture_e::reinit; } - else { - high_precision_sleep(sleep_period); - // if (config::sunshine.min_log_level <= 1) { - // // Print sleep overshoot stats to debug log every 20 seconds - // auto print_info = [&](double min_overshoot, double max_overshoot, double avg_overshoot) { - // auto f = stat_trackers::one_digit_after_decimal(); - // BOOST_LOG(debug) << "Sleep overshoot (min/max/avg): " << f % min_overshoot << "ms/" << f % max_overshoot << "ms/" << f % avg_overshoot << "ms"; - // }; - // std::chrono::nanoseconds overshoot_ns = std::chrono::steady_clock::now() - sleep_target; - // sleep_overshoot_tracker.collect_and_callback_on_interval(overshoot_ns.count() / 1000000., print_info, 20s); - // } + platf::capture_e status = capture_e::ok; + std::shared_ptr img_out; - status = snapshot(pull_free_image_cb, img_out, 0ms, *cursor); + // Try to continue frame pacing group, snapshot() is called with zero + // timeout after waiting for client frame interval + if (frame_pacing_group_start) { + const uint32_t seconds = (uint64_t)frame_pacing_group_frames * + client_frame_rate_adjusted.Denominator / + client_frame_rate_adjusted.Numerator; + const uint32_t remainder = (uint64_t)frame_pacing_group_frames * + client_frame_rate_adjusted.Denominator % + client_frame_rate_adjusted.Numerator; + const auto sleep_target = *frame_pacing_group_start + + std::chrono::nanoseconds(1s) * seconds + + std::chrono::nanoseconds(1s) * remainder / + client_frame_rate_adjusted.Numerator; + const auto sleep_period = + sleep_target - std::chrono::steady_clock::now(); - if (status == capture_e::ok && img_out) { - frame_pacing_group_frames += 1; - } - else { - frame_pacing_group_start = std::nullopt; - frame_pacing_group_frames = 0; - } + if (sleep_period <= 0ns) { + // We missed next frame time, invalidating current frame pacing + // group + frame_pacing_group_start = std::nullopt; + frame_pacing_group_frames = 0; + status = capture_e::timeout; + } else { + high_precision_sleep(sleep_period); + + // if (config::sunshine.min_log_level <= 1) { + // // Print sleep overshoot stats to debug log every 20 + // seconds auto print_info = [&](double min_overshoot, double + // max_overshoot, double avg_overshoot) { + // auto f = stat_trackers::one_digit_after_decimal(); + // BOOST_LOG(debug) << "Sleep overshoot (min/max/avg): " << + // f % min_overshoot << "ms/" << f % max_overshoot << "ms/" + // << f % avg_overshoot << "ms"; + // }; + // std::chrono::nanoseconds overshoot_ns = + // std::chrono::steady_clock::now() - sleep_target; + // sleep_overshoot_tracker.collect_and_callback_on_interval(overshoot_ns.count() + // / 1000000., print_info, 20s); + // } + + status = snapshot(pull_free_image_cb, img_out, 0ms, *cursor); + + if (status == capture_e::ok && img_out) { + frame_pacing_group_frames += 1; + } else { + frame_pacing_group_start = std::nullopt; + frame_pacing_group_frames = 0; + } + } } - } - // Start new frame pacing group if necessary, snapshot() is called with non-zero timeout - if (status == capture_e::timeout || (status == capture_e::ok && !frame_pacing_group_start)) { - status = snapshot(pull_free_image_cb, img_out, 1000ms, *cursor); + // Start new frame pacing group if necessary, snapshot() is called with + // non-zero timeout + if (status == capture_e::timeout || + (status == capture_e::ok && !frame_pacing_group_start)) { + status = snapshot(pull_free_image_cb, img_out, 1000ms, *cursor); - if (status == capture_e::ok && img_out) { - frame_pacing_group_start = img_out->frame_timestamp; + if (status == capture_e::ok && img_out) { + frame_pacing_group_start = img_out->frame_timestamp; - if (!frame_pacing_group_start) { - BOOST_LOG(warning) << "snapshot() provided image without timestamp"; - frame_pacing_group_start = std::chrono::steady_clock::now(); - } + if (!frame_pacing_group_start) { + BOOST_LOG(warning) + << "snapshot() provided image without timestamp"; + frame_pacing_group_start = std::chrono::steady_clock::now(); + } - frame_pacing_group_frames = 1; + frame_pacing_group_frames = 1; + } } - } - switch (status) { - case platf::capture_e::reinit: - case platf::capture_e::error: - case platf::capture_e::interrupted: - return status; - case platf::capture_e::timeout: - if (!push_captured_image_cb(std::move(img_out), false)) { - return capture_e::ok; - } - break; - case platf::capture_e::ok: - if (!push_captured_image_cb(std::move(img_out), true)) { - return capture_e::ok; - } - break; - default: - BOOST_LOG(error) << "Unrecognized capture status ["sv << (int) status << ']'; - return status; - } + switch (status) { + case platf::capture_e::reinit: + case platf::capture_e::error: + case platf::capture_e::interrupted: + return status; + case platf::capture_e::timeout: + if (!push_captured_image_cb(std::move(img_out), false)) { + return capture_e::ok; + } + break; + case platf::capture_e::ok: + if (!push_captured_image_cb(std::move(img_out), true)) { + return capture_e::ok; + } + break; + default: + BOOST_LOG(error) + << "Unrecognized capture status ["sv << (int)status << ']'; + return status; + } - status = dup.release_frame(); - if (status != platf::capture_e::ok) { - return status; - } + status = dup.release_frame(); + if (status != platf::capture_e::ok) { + return status; + } } return capture_e::ok; - } +} - bool - set_gpu_preference_on_self(int preference) { +bool set_gpu_preference_on_self(int preference) { // The GPU preferences key uses app path as the value name. WCHAR sunshine_path[MAX_PATH]; GetModuleFileNameW(NULL, sunshine_path, ARRAYSIZE(sunshine_path)); @@ -260,148 +290,139 @@ namespace platf::dxgi { WCHAR value_data[128]; swprintf_s(value_data, L"GpuPreference=%d;", preference); - auto status = RegSetKeyValueW(HKEY_CURRENT_USER, - L"Software\\Microsoft\\DirectX\\UserGpuPreferences", - sunshine_path, - REG_SZ, - value_data, - (wcslen(value_data) + 1) * sizeof(WCHAR)); + auto status = RegSetKeyValueW( + HKEY_CURRENT_USER, L"Software\\Microsoft\\DirectX\\UserGpuPreferences", + sunshine_path, REG_SZ, value_data, + (wcslen(value_data) + 1) * sizeof(WCHAR)); if (status != ERROR_SUCCESS) { - BOOST_LOG(error) << "Failed to set GPU preference: "sv << status; - return false; + BOOST_LOG(error) << "Failed to set GPU preference: "sv << status; + return false; } BOOST_LOG(info) << "Set GPU preference: "sv << preference; return true; - } +} - // On hybrid graphics systems, Windows will change the order of GPUs reported by - // DXGI in accordance with the user's GPU preference. If the selected GPU is a - // render-only device with no displays, DXGI will add virtual outputs to the - // that device to avoid confusing applications. While this works properly for most - // applications, it breaks the Desktop Duplication API because DXGI doesn't proxy - // the virtual DXGIOutput to the real GPU it is attached to. When trying to call - // DuplicateOutput() on one of these virtual outputs, it fails with DXGI_ERROR_UNSUPPORTED - // (even if you try sneaky stuff like passing the ID3D11Device for the iGPU and the - // virtual DXGIOutput from the dGPU). Because the GPU preference is once-per-process, - // we spawn a helper tool to probe for us before we set our own GPU preference. - bool - probe_for_gpu_preference(const std::string &display_name) { +// On hybrid graphics systems, Windows will change the order of GPUs reported by +// DXGI in accordance with the user's GPU preference. If the selected GPU is a +// render-only device with no displays, DXGI will add virtual outputs to the +// that device to avoid confusing applications. While this works properly for +// most applications, it breaks the Desktop Duplication API because DXGI doesn't +// proxy the virtual DXGIOutput to the real GPU it is attached to. When trying +// to call DuplicateOutput() on one of these virtual outputs, it fails with +// DXGI_ERROR_UNSUPPORTED (even if you try sneaky stuff like passing the +// ID3D11Device for the iGPU and the virtual DXGIOutput from the dGPU). Because +// the GPU preference is once-per-process, we spawn a helper tool to probe for +// us before we set our own GPU preference. +bool probe_for_gpu_preference(const std::string &display_name) { // If we've already been through here, there's nothing to do this time. static bool set_gpu_preference = false; if (set_gpu_preference) { - return true; + return true; } std::string cmd = "ddprobe.exe"; // We start at 1 because 0 is automatic selection which can be overridden by - // the GPU driver control panel options. Since ddprobe.exe can have different - // GPU driver overrides than Sunshine.exe, we want to avoid a scenario where - // autoselection might work for ddprobe.exe but not for us. + // the GPU driver control panel options. Since ddprobe.exe can have + // different GPU driver overrides than Sunshine.exe, we want to avoid a + // scenario where autoselection might work for ddprobe.exe but not for us. for (int i = 1; i < 5; i++) { - // Run the probe tool. It returns the status of DuplicateOutput(). - // - // Arg format: [GPU preference] [Display name] - HRESULT result; - try { - result = bp::system(cmd, std::to_string(i), display_name, bp::std_out > bp::null, bp::std_err > bp::null); - } - catch (bp::process_error &e) { - BOOST_LOG(error) << "Failed to start ddprobe.exe: "sv << e.what(); - return false; - } - - BOOST_LOG(info) << "ddprobe.exe ["sv << i << "] ["sv << display_name << "] returned: 0x"sv << util::hex(result).to_string_view(); - - // E_ACCESSDENIED can happen at the login screen. If we get this error, - // we know capture would have been supported, because DXGI_ERROR_UNSUPPORTED - // would have been raised first if it wasn't. - if (result == S_OK || result == E_ACCESSDENIED) { - // We found a working GPU preference, so set ourselves to use that. - if (set_gpu_preference_on_self(i)) { - set_gpu_preference = true; - return true; + // Run the probe tool. It returns the status of DuplicateOutput(). + // + // Arg format: [GPU preference] [Display name] + HRESULT result; + try { + result = bp::system(cmd, std::to_string(i), display_name, + bp::std_out > bp::null, bp::std_err > bp::null); + } catch (bp::process_error &e) { + BOOST_LOG(error) << "Failed to start ddprobe.exe: "sv << e.what(); + return false; } - else { - return false; + + BOOST_LOG(info) << "ddprobe.exe ["sv << i << "] ["sv << display_name + << "] returned: 0x"sv + << util::hex(result).to_string_view(); + + // E_ACCESSDENIED can happen at the login screen. If we get this error, + // we know capture would have been supported, because + // DXGI_ERROR_UNSUPPORTED would have been raised first if it wasn't. + if (result == S_OK || result == E_ACCESSDENIED) { + // We found a working GPU preference, so set ourselves to use that. + if (set_gpu_preference_on_self(i)) { + set_gpu_preference = true; + return true; + } else { + return false; + } + } else { + // This configuration didn't work, so continue testing others + continue; } - } - else { - // This configuration didn't work, so continue testing others - continue; - } } // If none of the manual options worked, leave the GPU preference alone return false; - } +} - bool - test_dxgi_duplication(adapter_t &adapter, output_t &output) { - D3D_FEATURE_LEVEL featureLevels[] { - D3D_FEATURE_LEVEL_11_1, - D3D_FEATURE_LEVEL_11_0, - D3D_FEATURE_LEVEL_10_1, - D3D_FEATURE_LEVEL_10_0, - D3D_FEATURE_LEVEL_9_3, - D3D_FEATURE_LEVEL_9_2, - D3D_FEATURE_LEVEL_9_1 - }; +bool test_dxgi_duplication(adapter_t &adapter, output_t &output) { + D3D_FEATURE_LEVEL featureLevels[]{ + D3D_FEATURE_LEVEL_11_1, D3D_FEATURE_LEVEL_11_0, D3D_FEATURE_LEVEL_10_1, + D3D_FEATURE_LEVEL_10_0, D3D_FEATURE_LEVEL_9_3, D3D_FEATURE_LEVEL_9_2, + D3D_FEATURE_LEVEL_9_1}; device_t device; - auto status = D3D11CreateDevice( - adapter.get(), - D3D_DRIVER_TYPE_UNKNOWN, - nullptr, - D3D11_CREATE_DEVICE_FLAGS, - featureLevels, sizeof(featureLevels) / sizeof(D3D_FEATURE_LEVEL), - D3D11_SDK_VERSION, - &device, - nullptr, - nullptr); + auto status = + D3D11CreateDevice(adapter.get(), D3D_DRIVER_TYPE_UNKNOWN, nullptr, + D3D11_CREATE_DEVICE_FLAGS, featureLevels, + sizeof(featureLevels) / sizeof(D3D_FEATURE_LEVEL), + D3D11_SDK_VERSION, &device, nullptr, nullptr); if (FAILED(status)) { - BOOST_LOG(error) << "Failed to create D3D11 device for DD test [0x"sv << util::hex(status).to_string_view() << ']'; - return false; + BOOST_LOG(error) << "Failed to create D3D11 device for DD test [0x"sv + << util::hex(status).to_string_view() << ']'; + return false; } output1_t output1; - status = output->QueryInterface(IID_IDXGIOutput1, (void **) &output1); + status = output->QueryInterface(IID_IDXGIOutput1, (void **)&output1); if (FAILED(status)) { - BOOST_LOG(error) << "Failed to query IDXGIOutput1 from the output"sv; - return false; + BOOST_LOG(error) << "Failed to query IDXGIOutput1 from the output"sv; + return false; } // Check if we can use the Desktop Duplication API on this output for (int x = 0; x < 2; ++x) { - dup_t dup; - status = output1->DuplicateOutput((IUnknown *) device.get(), &dup); - if (SUCCEEDED(status)) { - return true; - } - Sleep(200); + dup_t dup; + status = output1->DuplicateOutput((IUnknown *)device.get(), &dup); + if (SUCCEEDED(status)) { + return true; + } + Sleep(200); } - BOOST_LOG(error) << "DuplicateOutput() test failed [0x"sv << util::hex(status).to_string_view() << ']'; + BOOST_LOG(error) << "DuplicateOutput() test failed [0x"sv + << util::hex(status).to_string_view() << ']'; return false; - } +} - int - display_base_t::init(const ::video::config_t &config, const std::string &display_name) { +int display_base_t::init(const ::video::config_t &config, + const std::string &display_name) { std::once_flag windows_cpp_once_flag; std::call_once(windows_cpp_once_flag, []() { - DECLARE_HANDLE(DPI_AWARENESS_CONTEXT); + DECLARE_HANDLE(DPI_AWARENESS_CONTEXT); - typedef BOOL (*User32_SetProcessDpiAwarenessContext)(DPI_AWARENESS_CONTEXT value); + typedef BOOL (*User32_SetProcessDpiAwarenessContext)( + DPI_AWARENESS_CONTEXT value); - auto user32 = LoadLibraryA("user32.dll"); - auto f = (User32_SetProcessDpiAwarenessContext) GetProcAddress(user32, "SetProcessDpiAwarenessContext"); - if (f) { - f(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2); - } + auto user32 = LoadLibraryA("user32.dll"); + auto f = (User32_SetProcessDpiAwarenessContext)GetProcAddress( + user32, "SetProcessDpiAwarenessContext"); + if (f) { + f(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2); + } - FreeLibrary(user32); + FreeLibrary(user32); }); // Ensure we can duplicate the current display @@ -415,13 +436,15 @@ namespace platf::dxgi { // We must set the GPU preference before calling any DXGI APIs! if (!probe_for_gpu_preference(display_name)) { - BOOST_LOG(warning) << "Failed to set GPU preference. Capture may not work!"sv; + BOOST_LOG(warning) + << "Failed to set GPU preference. Capture may not work!"sv; } - status = CreateDXGIFactory1(IID_IDXGIFactory1, (void **) &factory); + status = CreateDXGIFactory1(IID_IDXGIFactory1, (void **)&factory); if (FAILED(status)) { - BOOST_LOG(error) << "Failed to create DXGIFactory1 [0x"sv << util::hex(status).to_string_view() << ']'; - return -1; + BOOST_LOG(error) << "Failed to create DXGIFactory1 [0x"sv + << util::hex(status).to_string_view() << ']'; + return -1; } std::wstring_convert, wchar_t> converter; @@ -431,108 +454,104 @@ namespace platf::dxgi { adapter_t::pointer adapter_p; for (int tries = 0; tries < 2; ++tries) { - for (int x = 0; factory->EnumAdapters1(x, &adapter_p) != DXGI_ERROR_NOT_FOUND; ++x) { - dxgi::adapter_t adapter_tmp { adapter_p }; + for (int x = 0; + factory->EnumAdapters1(x, &adapter_p) != DXGI_ERROR_NOT_FOUND; + ++x) { + dxgi::adapter_t adapter_tmp{adapter_p}; - DXGI_ADAPTER_DESC1 adapter_desc; - adapter_tmp->GetDesc1(&adapter_desc); + DXGI_ADAPTER_DESC1 adapter_desc; + adapter_tmp->GetDesc1(&adapter_desc); - if (!adapter_name.empty() && adapter_desc.Description != adapter_name) { - continue; - } - - dxgi::output_t::pointer output_p; - for (int y = 0; adapter_tmp->EnumOutputs(y, &output_p) != DXGI_ERROR_NOT_FOUND; ++y) { - dxgi::output_t output_tmp { output_p }; - - DXGI_OUTPUT_DESC desc; - output_tmp->GetDesc(&desc); - - if (!output_name.empty() && desc.DeviceName != output_name) { - continue; - } - - if (desc.AttachedToDesktop && test_dxgi_duplication(adapter_tmp, output_tmp)) { - output = std::move(output_tmp); - - offset_x = desc.DesktopCoordinates.left; - offset_y = desc.DesktopCoordinates.top; - width = desc.DesktopCoordinates.right - offset_x; - height = desc.DesktopCoordinates.bottom - offset_y; - - display_rotation = desc.Rotation; - if (display_rotation == DXGI_MODE_ROTATION_ROTATE90 || - display_rotation == DXGI_MODE_ROTATION_ROTATE270) { - width_before_rotation = height; - height_before_rotation = width; - } - else { - width_before_rotation = width; - height_before_rotation = height; + if (!adapter_name.empty() && + adapter_desc.Description != adapter_name) { + continue; } - // left and bottom may be negative, yet absolute mouse coordinates start at 0x0 - // Ensure offset starts at 0x0 - offset_x -= GetSystemMetrics(SM_XVIRTUALSCREEN); - offset_y -= GetSystemMetrics(SM_YVIRTUALSCREEN); - } + dxgi::output_t::pointer output_p; + for (int y = 0; + adapter_tmp->EnumOutputs(y, &output_p) != DXGI_ERROR_NOT_FOUND; + ++y) { + dxgi::output_t output_tmp{output_p}; + + DXGI_OUTPUT_DESC desc; + output_tmp->GetDesc(&desc); + + if (!output_name.empty() && desc.DeviceName != output_name) { + continue; + } + + if (desc.AttachedToDesktop && + test_dxgi_duplication(adapter_tmp, output_tmp)) { + output = std::move(output_tmp); + + offset_x = desc.DesktopCoordinates.left; + offset_y = desc.DesktopCoordinates.top; + width = desc.DesktopCoordinates.right - offset_x; + height = desc.DesktopCoordinates.bottom - offset_y; + + display_rotation = desc.Rotation; + if (display_rotation == DXGI_MODE_ROTATION_ROTATE90 || + display_rotation == DXGI_MODE_ROTATION_ROTATE270) { + width_before_rotation = height; + height_before_rotation = width; + } else { + width_before_rotation = width; + height_before_rotation = height; + } + + // left and bottom may be negative, yet absolute mouse + // coordinates start at 0x0 Ensure offset starts at 0x0 + offset_x -= GetSystemMetrics(SM_XVIRTUALSCREEN); + offset_y -= GetSystemMetrics(SM_YVIRTUALSCREEN); + } + } + + if (output) { + adapter = std::move(adapter_tmp); + break; + } } if (output) { - adapter = std::move(adapter_tmp); - break; + break; } - } - if (output) { - break; - } - - // If we made it here without finding an output, try to power on the display and retry. - if (tries == 0) { - SetThreadExecutionState(ES_DISPLAY_REQUIRED); - Sleep(500); - } + // If we made it here without finding an output, try to power on the + // display and retry. + if (tries == 0) { + SetThreadExecutionState(ES_DISPLAY_REQUIRED); + Sleep(500); + } } if (!output) { - BOOST_LOG(error) << "Failed to locate an output device"sv; - return -1; + BOOST_LOG(error) << "Failed to locate an output device"sv; + return -1; } - D3D_FEATURE_LEVEL featureLevels[] { - D3D_FEATURE_LEVEL_11_1, - D3D_FEATURE_LEVEL_11_0, - D3D_FEATURE_LEVEL_10_1, - D3D_FEATURE_LEVEL_10_0, - D3D_FEATURE_LEVEL_9_3, - D3D_FEATURE_LEVEL_9_2, - D3D_FEATURE_LEVEL_9_1 - }; + D3D_FEATURE_LEVEL featureLevels[]{ + D3D_FEATURE_LEVEL_11_1, D3D_FEATURE_LEVEL_11_0, D3D_FEATURE_LEVEL_10_1, + D3D_FEATURE_LEVEL_10_0, D3D_FEATURE_LEVEL_9_3, D3D_FEATURE_LEVEL_9_2, + D3D_FEATURE_LEVEL_9_1}; - status = adapter->QueryInterface(IID_IDXGIAdapter, (void **) &adapter_p); + status = adapter->QueryInterface(IID_IDXGIAdapter, (void **)&adapter_p); if (FAILED(status)) { - BOOST_LOG(error) << "Failed to query IDXGIAdapter interface"sv; - return -1; + BOOST_LOG(error) << "Failed to query IDXGIAdapter interface"sv; + return -1; } status = D3D11CreateDevice( - adapter_p, - D3D_DRIVER_TYPE_UNKNOWN, - nullptr, - D3D11_CREATE_DEVICE_FLAGS, - featureLevels, sizeof(featureLevels) / sizeof(D3D_FEATURE_LEVEL), - D3D11_SDK_VERSION, - &device, - &feature_level, - &device_ctx); + adapter_p, D3D_DRIVER_TYPE_UNKNOWN, nullptr, D3D11_CREATE_DEVICE_FLAGS, + featureLevels, sizeof(featureLevels) / sizeof(D3D_FEATURE_LEVEL), + D3D11_SDK_VERSION, &device, &feature_level, &device_ctx); adapter_p->Release(); if (FAILED(status)) { - BOOST_LOG(error) << "Failed to create D3D11 device [0x"sv << util::hex(status).to_string_view() << ']'; + BOOST_LOG(error) << "Failed to create D3D11 device [0x"sv + << util::hex(status).to_string_view() << ']'; - return -1; + return -1; } DXGI_ADAPTER_DESC adapter_desc; @@ -540,260 +559,334 @@ namespace platf::dxgi { auto description = converter.to_bytes(adapter_desc.Description); BOOST_LOG(info) - << std::endl - << "Device Description : " << description << std::endl - << "Device Vendor ID : 0x"sv << util::hex(adapter_desc.VendorId).to_string_view() << std::endl - << "Device Device ID : 0x"sv << util::hex(adapter_desc.DeviceId).to_string_view() << std::endl - << "Device Video Mem : "sv << adapter_desc.DedicatedVideoMemory / 1048576 << " MiB"sv << std::endl - << "Device Sys Mem : "sv << adapter_desc.DedicatedSystemMemory / 1048576 << " MiB"sv << std::endl - << "Share Sys Mem : "sv << adapter_desc.SharedSystemMemory / 1048576 << " MiB"sv << std::endl - << "Feature Level : 0x"sv << util::hex(feature_level).to_string_view() << std::endl - << "Capture size : "sv << width << 'x' << height << std::endl - << "Offset : "sv << offset_x << 'x' << offset_y << std::endl - << "Virtual Desktop : "sv << env_width << 'x' << env_height; + << std::endl + << "Device Description : " << description << std::endl + << "Device Vendor ID : 0x"sv + << util::hex(adapter_desc.VendorId).to_string_view() << std::endl + << "Device Device ID : 0x"sv + << util::hex(adapter_desc.DeviceId).to_string_view() << std::endl + << "Device Video Mem : "sv + << adapter_desc.DedicatedVideoMemory / 1048576 << " MiB"sv << std::endl + << "Device Sys Mem : "sv + << adapter_desc.DedicatedSystemMemory / 1048576 << " MiB"sv << std::endl + << "Share Sys Mem : "sv + << adapter_desc.SharedSystemMemory / 1048576 << " MiB"sv << std::endl + << "Feature Level : 0x"sv + << util::hex(feature_level).to_string_view() << std::endl + << "Capture size : "sv << width << 'x' << height << std::endl + << "Offset : "sv << offset_x << 'x' << offset_y << std::endl + << "Virtual Desktop : "sv << env_width << 'x' << env_height; // Bump up thread priority { - const DWORD flags = TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY; - TOKEN_PRIVILEGES tp; - HANDLE token; - LUID val; + const DWORD flags = TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY; + TOKEN_PRIVILEGES tp; + HANDLE token; + LUID val; - if (OpenProcessToken(GetCurrentProcess(), flags, &token) && - !!LookupPrivilegeValue(NULL, SE_INC_BASE_PRIORITY_NAME, &val)) { - tp.PrivilegeCount = 1; - tp.Privileges[0].Luid = val; - tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; + if (OpenProcessToken(GetCurrentProcess(), flags, &token) && + !!LookupPrivilegeValue(NULL, SE_INC_BASE_PRIORITY_NAME, &val)) { + tp.PrivilegeCount = 1; + tp.Privileges[0].Luid = val; + tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; - if (!AdjustTokenPrivileges(token, false, &tp, sizeof(tp), NULL, NULL)) { - BOOST_LOG(warning) << "Could not set privilege to increase GPU priority"; + if (!AdjustTokenPrivileges(token, false, &tp, sizeof(tp), NULL, + NULL)) { + BOOST_LOG(warning) + << "Could not set privilege to increase GPU priority"; + } } - } - CloseHandle(token); + CloseHandle(token); - HMODULE gdi32 = GetModuleHandleA("GDI32"); - if (gdi32) { - auto check_hags = [&](const LUID &adapter) -> bool { - auto d3dkmt_open_adapter = (PD3DKMTOpenAdapterFromLuid) GetProcAddress(gdi32, "D3DKMTOpenAdapterFromLuid"); - auto d3dkmt_query_adapter_info = (PD3DKMTQueryAdapterInfo) GetProcAddress(gdi32, "D3DKMTQueryAdapterInfo"); - auto d3dkmt_close_adapter = (PD3DKMTCloseAdapter) GetProcAddress(gdi32, "D3DKMTCloseAdapter"); - if (!d3dkmt_open_adapter || !d3dkmt_query_adapter_info || !d3dkmt_close_adapter) { - BOOST_LOG(error) << "Couldn't load d3dkmt functions from gdi32.dll to determine GPU HAGS status"; - return false; - } + HMODULE gdi32 = GetModuleHandleA("GDI32"); + if (gdi32) { + auto check_hags = [&](const LUID &adapter) -> bool { + auto d3dkmt_open_adapter = + (PD3DKMTOpenAdapterFromLuid)GetProcAddress( + gdi32, "D3DKMTOpenAdapterFromLuid"); + auto d3dkmt_query_adapter_info = + (PD3DKMTQueryAdapterInfo)GetProcAddress( + gdi32, "D3DKMTQueryAdapterInfo"); + auto d3dkmt_close_adapter = (PD3DKMTCloseAdapter)GetProcAddress( + gdi32, "D3DKMTCloseAdapter"); + if (!d3dkmt_open_adapter || !d3dkmt_query_adapter_info || + !d3dkmt_close_adapter) { + BOOST_LOG(error) + << "Couldn't load d3dkmt functions from gdi32.dll to " + "determine GPU HAGS status"; + return false; + } - D3DKMT_OPENADAPTERFROMLUID d3dkmt_adapter = { adapter }; - if (FAILED(d3dkmt_open_adapter(&d3dkmt_adapter))) { - BOOST_LOG(error) << "D3DKMTOpenAdapterFromLuid() failed while trying to determine GPU HAGS status"; - return false; - } + D3DKMT_OPENADAPTERFROMLUID d3dkmt_adapter = {adapter}; + if (FAILED(d3dkmt_open_adapter(&d3dkmt_adapter))) { + BOOST_LOG(error) + << "D3DKMTOpenAdapterFromLuid() failed while trying to " + "determine GPU HAGS status"; + return false; + } - bool result; + bool result; - D3DKMT_WDDM_2_7_CAPS d3dkmt_adapter_caps = {}; - D3DKMT_QUERYADAPTERINFO d3dkmt_adapter_info = {}; - d3dkmt_adapter_info.hAdapter = d3dkmt_adapter.hAdapter; - d3dkmt_adapter_info.Type = KMTQAITYPE_WDDM_2_7_CAPS; - d3dkmt_adapter_info.pPrivateDriverData = &d3dkmt_adapter_caps; - d3dkmt_adapter_info.PrivateDriverDataSize = sizeof(d3dkmt_adapter_caps); + D3DKMT_WDDM_2_7_CAPS d3dkmt_adapter_caps = {}; + D3DKMT_QUERYADAPTERINFO d3dkmt_adapter_info = {}; + d3dkmt_adapter_info.hAdapter = d3dkmt_adapter.hAdapter; + d3dkmt_adapter_info.Type = KMTQAITYPE_WDDM_2_7_CAPS; + d3dkmt_adapter_info.pPrivateDriverData = &d3dkmt_adapter_caps; + d3dkmt_adapter_info.PrivateDriverDataSize = + sizeof(d3dkmt_adapter_caps); - if (SUCCEEDED(d3dkmt_query_adapter_info(&d3dkmt_adapter_info))) { - result = d3dkmt_adapter_caps.HwSchEnabled; - } - else { - BOOST_LOG(warning) << "D3DKMTQueryAdapterInfo() failed while trying to determine GPU HAGS status"; - result = false; - } + if (SUCCEEDED( + d3dkmt_query_adapter_info(&d3dkmt_adapter_info))) { + result = d3dkmt_adapter_caps.HwSchEnabled; + } else { + BOOST_LOG(warning) + << "D3DKMTQueryAdapterInfo() failed while trying to " + "determine GPU HAGS status"; + result = false; + } - D3DKMT_CLOSEADAPTER d3dkmt_close_adapter_wrap = { d3dkmt_adapter.hAdapter }; - if (FAILED(d3dkmt_close_adapter(&d3dkmt_close_adapter_wrap))) { - BOOST_LOG(error) << "D3DKMTCloseAdapter() failed while trying to determine GPU HAGS status"; - } + D3DKMT_CLOSEADAPTER d3dkmt_close_adapter_wrap = { + d3dkmt_adapter.hAdapter}; + if (FAILED(d3dkmt_close_adapter(&d3dkmt_close_adapter_wrap))) { + BOOST_LOG(error) << "D3DKMTCloseAdapter() failed while " + "trying to determine GPU HAGS status"; + } - return result; - }; + return result; + }; - auto d3dkmt_set_process_priority = (PD3DKMTSetProcessSchedulingPriorityClass) GetProcAddress(gdi32, "D3DKMTSetProcessSchedulingPriorityClass"); - if (d3dkmt_set_process_priority) { - auto priority = D3DKMT_SCHEDULINGPRIORITYCLASS_REALTIME; - bool hags_enabled = check_hags(adapter_desc.AdapterLuid); - if (adapter_desc.VendorId == 0x10DE) { - // As of 2023.07, NVIDIA driver has unfixed bug(s) where "realtime" can cause unrecoverable encoding freeze or outright driver crash - // This issue happens more frequently with HAGS, in DX12 games or when VRAM is filled close to max capacity - // Track OBS to see if they find better workaround or NVIDIA fixes it on their end, they seem to be in communication - if (hags_enabled && !config::video.nv_realtime_hags) priority = D3DKMT_SCHEDULINGPRIORITYCLASS_HIGH; - } - BOOST_LOG(info) << "Active GPU has HAGS " << (hags_enabled ? "enabled" : "disabled"); - BOOST_LOG(info) << "Using " << (priority == D3DKMT_SCHEDULINGPRIORITYCLASS_HIGH ? "high" : "realtime") << " GPU priority"; - if (FAILED(d3dkmt_set_process_priority(GetCurrentProcess(), priority))) { - BOOST_LOG(warning) << "Failed to adjust GPU priority. Please run application as administrator for optimal performance."; - } + auto d3dkmt_set_process_priority = + (PD3DKMTSetProcessSchedulingPriorityClass)GetProcAddress( + gdi32, "D3DKMTSetProcessSchedulingPriorityClass"); + if (d3dkmt_set_process_priority) { + auto priority = D3DKMT_SCHEDULINGPRIORITYCLASS_REALTIME; + bool hags_enabled = check_hags(adapter_desc.AdapterLuid); + if (adapter_desc.VendorId == 0x10DE) { + // As of 2023.07, NVIDIA driver has unfixed bug(s) where + // "realtime" can cause unrecoverable encoding freeze or + // outright driver crash This issue happens more frequently + // with HAGS, in DX12 games or when VRAM is filled close to + // max capacity Track OBS to see if they find better + // workaround or NVIDIA fixes it on their end, they seem to + // be in communication + if (hags_enabled && !config::video.nv_realtime_hags) + priority = D3DKMT_SCHEDULINGPRIORITYCLASS_HIGH; + } + BOOST_LOG(info) << "Active GPU has HAGS " + << (hags_enabled ? "enabled" : "disabled"); + BOOST_LOG(info) + << "Using " + << (priority == D3DKMT_SCHEDULINGPRIORITYCLASS_HIGH + ? "high" + : "realtime") + << " GPU priority"; + if (FAILED(d3dkmt_set_process_priority(GetCurrentProcess(), + priority))) { + BOOST_LOG(warning) + << "Failed to adjust GPU priority. Please run " + "application as administrator for optimal " + "performance."; + } + } else { + BOOST_LOG(error) + << "Couldn't load D3DKMTSetProcessSchedulingPriorityClass " + "function from gdi32.dll to adjust GPU priority"; + } } - else { - BOOST_LOG(error) << "Couldn't load D3DKMTSetProcessSchedulingPriorityClass function from gdi32.dll to adjust GPU priority"; + + dxgi::dxgi_t dxgi; + status = device->QueryInterface(IID_IDXGIDevice, (void **)&dxgi); + if (FAILED(status)) { + BOOST_LOG(warning) + << "Failed to query DXGI interface from device [0x"sv + << util::hex(status).to_string_view() << ']'; + return -1; } - } - dxgi::dxgi_t dxgi; - status = device->QueryInterface(IID_IDXGIDevice, (void **) &dxgi); - if (FAILED(status)) { - BOOST_LOG(warning) << "Failed to query DXGI interface from device [0x"sv << util::hex(status).to_string_view() << ']'; - return -1; - } - - status = dxgi->SetGPUThreadPriority(7); - if (FAILED(status)) { - BOOST_LOG(warning) << "Failed to increase capture GPU thread priority. Please run application as administrator for optimal performance."; - } + status = dxgi->SetGPUThreadPriority(7); + if (FAILED(status)) { + BOOST_LOG(warning) + << "Failed to increase capture GPU thread priority. Please run " + "application as administrator for optimal performance."; + } } // Try to reduce latency { - dxgi::dxgi1_t dxgi {}; - status = device->QueryInterface(IID_IDXGIDevice, (void **) &dxgi); - if (FAILED(status)) { - BOOST_LOG(error) << "Failed to query DXGI interface from device [0x"sv << util::hex(status).to_string_view() << ']'; - return -1; - } + dxgi::dxgi1_t dxgi{}; + status = device->QueryInterface(IID_IDXGIDevice, (void **)&dxgi); + if (FAILED(status)) { + BOOST_LOG(error) + << "Failed to query DXGI interface from device [0x"sv + << util::hex(status).to_string_view() << ']'; + return -1; + } - status = dxgi->SetMaximumFrameLatency(1); - if (FAILED(status)) { - BOOST_LOG(warning) << "Failed to set maximum frame latency [0x"sv << util::hex(status).to_string_view() << ']'; - } + status = dxgi->SetMaximumFrameLatency(1); + if (FAILED(status)) { + BOOST_LOG(warning) << "Failed to set maximum frame latency [0x"sv + << util::hex(status).to_string_view() << ']'; + } } // FIXME: Duplicate output on RX580 in combination with DOOM (2016) --> BSOD { - // IDXGIOutput5 is optional, but can provide improved performance and wide color support - dxgi::output5_t output5 {}; - status = output->QueryInterface(IID_IDXGIOutput5, (void **) &output5); - if (SUCCEEDED(status)) { - // Ask the display implementation which formats it supports - auto supported_formats = get_supported_capture_formats(); - if (supported_formats.empty()) { - BOOST_LOG(warning) << "No compatible capture formats for this encoder"sv; - return -1; - } + // IDXGIOutput5 is optional, but can provide improved performance and + // wide color support + dxgi::output5_t output5{}; + status = output->QueryInterface(IID_IDXGIOutput5, (void **)&output5); + if (SUCCEEDED(status)) { + // Ask the display implementation which formats it supports + auto supported_formats = get_supported_capture_formats(); + if (supported_formats.empty()) { + BOOST_LOG(warning) + << "No compatible capture formats for this encoder"sv; + return -1; + } - // We try this twice, in case we still get an error on reinitialization - for (int x = 0; x < 2; ++x) { - status = output5->DuplicateOutput1((IUnknown *) device.get(), 0, supported_formats.size(), supported_formats.data(), &dup.dup); - if (SUCCEEDED(status)) { - break; - } - std::this_thread::sleep_for(200ms); - } + // We try this twice, in case we still get an error on + // reinitialization + for (int x = 0; x < 2; ++x) { + status = output5->DuplicateOutput1( + (IUnknown *)device.get(), 0, supported_formats.size(), + supported_formats.data(), &dup.dup); + if (SUCCEEDED(status)) { + break; + } + std::this_thread::sleep_for(200ms); + } - // We don't retry with DuplicateOutput() because we can hit this codepath when we're racing - // with mode changes and we don't want to accidentally fall back to suboptimal capture if - // we get unlucky and succeed below. - if (FAILED(status)) { - BOOST_LOG(warning) << "DuplicateOutput1 Failed [0x"sv << util::hex(status).to_string_view() << ']'; - return -1; - } - } - else { - BOOST_LOG(warning) << "IDXGIOutput5 is not supported by your OS. Capture performance may be reduced."sv; + // We don't retry with DuplicateOutput() because we can hit this + // codepath when we're racing with mode changes and we don't want to + // accidentally fall back to suboptimal capture if we get unlucky + // and succeed below. + if (FAILED(status)) { + BOOST_LOG(warning) << "DuplicateOutput1 Failed [0x"sv + << util::hex(status).to_string_view() << ']'; + return -1; + } + } else { + BOOST_LOG(warning) + << "IDXGIOutput5 is not supported by your OS. Capture performance may be reduced."sv; - dxgi::output1_t output1 {}; - status = output->QueryInterface(IID_IDXGIOutput1, (void **) &output1); - if (FAILED(status)) { - BOOST_LOG(error) << "Failed to query IDXGIOutput1 from the output"sv; - return -1; - } + dxgi::output1_t output1{}; + status = + output->QueryInterface(IID_IDXGIOutput1, (void **)&output1); + if (FAILED(status)) { + BOOST_LOG(error) + << "Failed to query IDXGIOutput1 from the output"sv; + return -1; + } - for (int x = 0; x < 2; ++x) { - status = output1->DuplicateOutput((IUnknown *) device.get(), &dup.dup); - if (SUCCEEDED(status)) { - break; - } - std::this_thread::sleep_for(200ms); - } + for (int x = 0; x < 2; ++x) { + status = output1->DuplicateOutput((IUnknown *)device.get(), + &dup.dup); + if (SUCCEEDED(status)) { + break; + } + std::this_thread::sleep_for(200ms); + } - if (FAILED(status)) { - BOOST_LOG(error) << "DuplicateOutput Failed [0x"sv << util::hex(status).to_string_view() << ']'; - return -1; + if (FAILED(status)) { + BOOST_LOG(error) << "DuplicateOutput Failed [0x"sv + << util::hex(status).to_string_view() << ']'; + return -1; + } } - } } DXGI_OUTDUPL_DESC dup_desc; dup.dup->GetDesc(&dup_desc); - BOOST_LOG(info) << "Desktop resolution ["sv << dup_desc.ModeDesc.Width << 'x' << dup_desc.ModeDesc.Height << ']'; - BOOST_LOG(info) << "Desktop format ["sv << dxgi_format_to_string(dup_desc.ModeDesc.Format) << ']'; + BOOST_LOG(info) << "Desktop resolution ["sv << dup_desc.ModeDesc.Width + << 'x' << dup_desc.ModeDesc.Height << ']'; + BOOST_LOG(info) << "Desktop format ["sv + << dxgi_format_to_string(dup_desc.ModeDesc.Format) << ']'; display_refresh_rate = dup_desc.ModeDesc.RefreshRate; - double display_refresh_rate_decimal = (double) display_refresh_rate.Numerator / display_refresh_rate.Denominator; - BOOST_LOG(info) << "Display refresh rate [" << display_refresh_rate_decimal << "Hz]"; + double display_refresh_rate_decimal = + (double)display_refresh_rate.Numerator / + display_refresh_rate.Denominator; + BOOST_LOG(info) << "Display refresh rate [" << display_refresh_rate_decimal + << "Hz]"; display_refresh_rate_rounded = lround(display_refresh_rate_decimal); client_frame_rate = config.framerate; BOOST_LOG(info) << "Requested frame rate [" << client_frame_rate << "fps]"; - dxgi::output6_t output6 {}; - status = output->QueryInterface(IID_IDXGIOutput6, (void **) &output6); + dxgi::output6_t output6{}; + status = output->QueryInterface(IID_IDXGIOutput6, (void **)&output6); if (SUCCEEDED(status)) { - DXGI_OUTPUT_DESC1 desc1; - output6->GetDesc1(&desc1); + DXGI_OUTPUT_DESC1 desc1; + output6->GetDesc1(&desc1); - BOOST_LOG(info) - << std::endl - << "Colorspace : "sv << colorspace_to_string(desc1.ColorSpace) << std::endl - << "Bits Per Color : "sv << desc1.BitsPerColor << std::endl - << "Red Primary : ["sv << desc1.RedPrimary[0] << ',' << desc1.RedPrimary[1] << ']' << std::endl - << "Green Primary : ["sv << desc1.GreenPrimary[0] << ',' << desc1.GreenPrimary[1] << ']' << std::endl - << "Blue Primary : ["sv << desc1.BluePrimary[0] << ',' << desc1.BluePrimary[1] << ']' << std::endl - << "White Point : ["sv << desc1.WhitePoint[0] << ',' << desc1.WhitePoint[1] << ']' << std::endl - << "Min Luminance : "sv << desc1.MinLuminance << " nits"sv << std::endl - << "Max Luminance : "sv << desc1.MaxLuminance << " nits"sv << std::endl - << "Max Full Luminance : "sv << desc1.MaxFullFrameLuminance << " nits"sv; + BOOST_LOG(info) << std::endl + << "Colorspace : "sv + << colorspace_to_string(desc1.ColorSpace) << std::endl + << "Bits Per Color : "sv << desc1.BitsPerColor + << std::endl + << "Red Primary : ["sv << desc1.RedPrimary[0] + << ',' << desc1.RedPrimary[1] << ']' << std::endl + << "Green Primary : ["sv << desc1.GreenPrimary[0] + << ',' << desc1.GreenPrimary[1] << ']' << std::endl + << "Blue Primary : ["sv << desc1.BluePrimary[0] + << ',' << desc1.BluePrimary[1] << ']' << std::endl + << "White Point : ["sv << desc1.WhitePoint[0] + << ',' << desc1.WhitePoint[1] << ']' << std::endl + << "Min Luminance : "sv << desc1.MinLuminance + << " nits"sv << std::endl + << "Max Luminance : "sv << desc1.MaxLuminance + << " nits"sv << std::endl + << "Max Full Luminance : "sv + << desc1.MaxFullFrameLuminance << " nits"sv; } - // Capture format will be determined from the first call to AcquireNextFrame() + // Capture format will be determined from the first call to + // AcquireNextFrame() capture_format = DXGI_FORMAT_UNKNOWN; // Use CREATE_WAITABLE_TIMER_HIGH_RESOLUTION if supported (Windows 10 1809+) - timer.reset(CreateWaitableTimerEx(nullptr, nullptr, CREATE_WAITABLE_TIMER_HIGH_RESOLUTION, TIMER_ALL_ACCESS)); + timer.reset(CreateWaitableTimerEx(nullptr, nullptr, + CREATE_WAITABLE_TIMER_HIGH_RESOLUTION, + TIMER_ALL_ACCESS)); if (!timer) { - timer.reset(CreateWaitableTimerEx(nullptr, nullptr, 0, TIMER_ALL_ACCESS)); - if (!timer) { - auto winerr = GetLastError(); - BOOST_LOG(error) << "Failed to create timer: "sv << winerr; - return -1; - } + timer.reset( + CreateWaitableTimerEx(nullptr, nullptr, 0, TIMER_ALL_ACCESS)); + if (!timer) { + auto winerr = GetLastError(); + BOOST_LOG(error) << "Failed to create timer: "sv << winerr; + return -1; + } } return 0; - } +} - bool - display_base_t::is_hdr() { - dxgi::output6_t output6 {}; +bool display_base_t::is_hdr() { + dxgi::output6_t output6{}; - auto status = output->QueryInterface(IID_IDXGIOutput6, (void **) &output6); + auto status = output->QueryInterface(IID_IDXGIOutput6, (void **)&output6); if (FAILED(status)) { - BOOST_LOG(warning) << "Failed to query IDXGIOutput6 from the output"sv; - return false; + BOOST_LOG(warning) << "Failed to query IDXGIOutput6 from the output"sv; + return false; } DXGI_OUTPUT_DESC1 desc1; output6->GetDesc1(&desc1); return desc1.ColorSpace == DXGI_COLOR_SPACE_RGB_FULL_G2084_NONE_P2020; - } +} - bool - display_base_t::get_hdr_metadata(SS_HDR_METADATA &metadata) { - dxgi::output6_t output6 {}; +bool display_base_t::get_hdr_metadata(SS_HDR_METADATA &metadata) { + dxgi::output6_t output6{}; std::memset(&metadata, 0, sizeof(metadata)); - auto status = output->QueryInterface(IID_IDXGIOutput6, (void **) &output6); + auto status = output->QueryInterface(IID_IDXGIOutput6, (void **)&output6); if (FAILED(status)) { - BOOST_LOG(warning) << "Failed to query IDXGIOutput6 from the output"sv; - return false; + BOOST_LOG(warning) << "Failed to query IDXGIOutput6 from the output"sv; + return false; } DXGI_OUTPUT_DESC1 desc1; @@ -829,208 +922,216 @@ namespace platf::dxgi { metadata.maxDisplayLuminance = desc1.MaxLuminance; metadata.minDisplayLuminance = desc1.MinLuminance * 10000; - // These are content-specific metadata parameters that this interface doesn't give us + // These are content-specific metadata parameters that this interface + // doesn't give us metadata.maxContentLightLevel = 0; metadata.maxFrameAverageLightLevel = 0; metadata.maxFullFrameLuminance = desc1.MaxFullFrameLuminance; return true; - } +} - const char *format_str[] = { - "DXGI_FORMAT_UNKNOWN", - "DXGI_FORMAT_R32G32B32A32_TYPELESS", - "DXGI_FORMAT_R32G32B32A32_FLOAT", - "DXGI_FORMAT_R32G32B32A32_UINT", - "DXGI_FORMAT_R32G32B32A32_SINT", - "DXGI_FORMAT_R32G32B32_TYPELESS", - "DXGI_FORMAT_R32G32B32_FLOAT", - "DXGI_FORMAT_R32G32B32_UINT", - "DXGI_FORMAT_R32G32B32_SINT", - "DXGI_FORMAT_R16G16B16A16_TYPELESS", - "DXGI_FORMAT_R16G16B16A16_FLOAT", - "DXGI_FORMAT_R16G16B16A16_UNORM", - "DXGI_FORMAT_R16G16B16A16_UINT", - "DXGI_FORMAT_R16G16B16A16_SNORM", - "DXGI_FORMAT_R16G16B16A16_SINT", - "DXGI_FORMAT_R32G32_TYPELESS", - "DXGI_FORMAT_R32G32_FLOAT", - "DXGI_FORMAT_R32G32_UINT", - "DXGI_FORMAT_R32G32_SINT", - "DXGI_FORMAT_R32G8X24_TYPELESS", - "DXGI_FORMAT_D32_FLOAT_S8X24_UINT", - "DXGI_FORMAT_R32_FLOAT_X8X24_TYPELESS", - "DXGI_FORMAT_X32_TYPELESS_G8X24_UINT", - "DXGI_FORMAT_R10G10B10A2_TYPELESS", - "DXGI_FORMAT_R10G10B10A2_UNORM", - "DXGI_FORMAT_R10G10B10A2_UINT", - "DXGI_FORMAT_R11G11B10_FLOAT", - "DXGI_FORMAT_R8G8B8A8_TYPELESS", - "DXGI_FORMAT_R8G8B8A8_UNORM", - "DXGI_FORMAT_R8G8B8A8_UNORM_SRGB", - "DXGI_FORMAT_R8G8B8A8_UINT", - "DXGI_FORMAT_R8G8B8A8_SNORM", - "DXGI_FORMAT_R8G8B8A8_SINT", - "DXGI_FORMAT_R16G16_TYPELESS", - "DXGI_FORMAT_R16G16_FLOAT", - "DXGI_FORMAT_R16G16_UNORM", - "DXGI_FORMAT_R16G16_UINT", - "DXGI_FORMAT_R16G16_SNORM", - "DXGI_FORMAT_R16G16_SINT", - "DXGI_FORMAT_R32_TYPELESS", - "DXGI_FORMAT_D32_FLOAT", - "DXGI_FORMAT_R32_FLOAT", - "DXGI_FORMAT_R32_UINT", - "DXGI_FORMAT_R32_SINT", - "DXGI_FORMAT_R24G8_TYPELESS", - "DXGI_FORMAT_D24_UNORM_S8_UINT", - "DXGI_FORMAT_R24_UNORM_X8_TYPELESS", - "DXGI_FORMAT_X24_TYPELESS_G8_UINT", - "DXGI_FORMAT_R8G8_TYPELESS", - "DXGI_FORMAT_R8G8_UNORM", - "DXGI_FORMAT_R8G8_UINT", - "DXGI_FORMAT_R8G8_SNORM", - "DXGI_FORMAT_R8G8_SINT", - "DXGI_FORMAT_R16_TYPELESS", - "DXGI_FORMAT_R16_FLOAT", - "DXGI_FORMAT_D16_UNORM", - "DXGI_FORMAT_R16_UNORM", - "DXGI_FORMAT_R16_UINT", - "DXGI_FORMAT_R16_SNORM", - "DXGI_FORMAT_R16_SINT", - "DXGI_FORMAT_R8_TYPELESS", - "DXGI_FORMAT_R8_UNORM", - "DXGI_FORMAT_R8_UINT", - "DXGI_FORMAT_R8_SNORM", - "DXGI_FORMAT_R8_SINT", - "DXGI_FORMAT_A8_UNORM", - "DXGI_FORMAT_R1_UNORM", - "DXGI_FORMAT_R9G9B9E5_SHAREDEXP", - "DXGI_FORMAT_R8G8_B8G8_UNORM", - "DXGI_FORMAT_G8R8_G8B8_UNORM", - "DXGI_FORMAT_BC1_TYPELESS", - "DXGI_FORMAT_BC1_UNORM", - "DXGI_FORMAT_BC1_UNORM_SRGB", - "DXGI_FORMAT_BC2_TYPELESS", - "DXGI_FORMAT_BC2_UNORM", - "DXGI_FORMAT_BC2_UNORM_SRGB", - "DXGI_FORMAT_BC3_TYPELESS", - "DXGI_FORMAT_BC3_UNORM", - "DXGI_FORMAT_BC3_UNORM_SRGB", - "DXGI_FORMAT_BC4_TYPELESS", - "DXGI_FORMAT_BC4_UNORM", - "DXGI_FORMAT_BC4_SNORM", - "DXGI_FORMAT_BC5_TYPELESS", - "DXGI_FORMAT_BC5_UNORM", - "DXGI_FORMAT_BC5_SNORM", - "DXGI_FORMAT_B5G6R5_UNORM", - "DXGI_FORMAT_B5G5R5A1_UNORM", - "DXGI_FORMAT_B8G8R8A8_UNORM", - "DXGI_FORMAT_B8G8R8X8_UNORM", - "DXGI_FORMAT_R10G10B10_XR_BIAS_A2_UNORM", - "DXGI_FORMAT_B8G8R8A8_TYPELESS", - "DXGI_FORMAT_B8G8R8A8_UNORM_SRGB", - "DXGI_FORMAT_B8G8R8X8_TYPELESS", - "DXGI_FORMAT_B8G8R8X8_UNORM_SRGB", - "DXGI_FORMAT_BC6H_TYPELESS", - "DXGI_FORMAT_BC6H_UF16", - "DXGI_FORMAT_BC6H_SF16", - "DXGI_FORMAT_BC7_TYPELESS", - "DXGI_FORMAT_BC7_UNORM", - "DXGI_FORMAT_BC7_UNORM_SRGB", - "DXGI_FORMAT_AYUV", - "DXGI_FORMAT_Y410", - "DXGI_FORMAT_Y416", - "DXGI_FORMAT_NV12", - "DXGI_FORMAT_P010", - "DXGI_FORMAT_P016", - "DXGI_FORMAT_420_OPAQUE", - "DXGI_FORMAT_YUY2", - "DXGI_FORMAT_Y210", - "DXGI_FORMAT_Y216", - "DXGI_FORMAT_NV11", - "DXGI_FORMAT_AI44", - "DXGI_FORMAT_IA44", - "DXGI_FORMAT_P8", - "DXGI_FORMAT_A8P8", - "DXGI_FORMAT_B4G4R4A4_UNORM", +const char *format_str[] = {"DXGI_FORMAT_UNKNOWN", + "DXGI_FORMAT_R32G32B32A32_TYPELESS", + "DXGI_FORMAT_R32G32B32A32_FLOAT", + "DXGI_FORMAT_R32G32B32A32_UINT", + "DXGI_FORMAT_R32G32B32A32_SINT", + "DXGI_FORMAT_R32G32B32_TYPELESS", + "DXGI_FORMAT_R32G32B32_FLOAT", + "DXGI_FORMAT_R32G32B32_UINT", + "DXGI_FORMAT_R32G32B32_SINT", + "DXGI_FORMAT_R16G16B16A16_TYPELESS", + "DXGI_FORMAT_R16G16B16A16_FLOAT", + "DXGI_FORMAT_R16G16B16A16_UNORM", + "DXGI_FORMAT_R16G16B16A16_UINT", + "DXGI_FORMAT_R16G16B16A16_SNORM", + "DXGI_FORMAT_R16G16B16A16_SINT", + "DXGI_FORMAT_R32G32_TYPELESS", + "DXGI_FORMAT_R32G32_FLOAT", + "DXGI_FORMAT_R32G32_UINT", + "DXGI_FORMAT_R32G32_SINT", + "DXGI_FORMAT_R32G8X24_TYPELESS", + "DXGI_FORMAT_D32_FLOAT_S8X24_UINT", + "DXGI_FORMAT_R32_FLOAT_X8X24_TYPELESS", + "DXGI_FORMAT_X32_TYPELESS_G8X24_UINT", + "DXGI_FORMAT_R10G10B10A2_TYPELESS", + "DXGI_FORMAT_R10G10B10A2_UNORM", + "DXGI_FORMAT_R10G10B10A2_UINT", + "DXGI_FORMAT_R11G11B10_FLOAT", + "DXGI_FORMAT_R8G8B8A8_TYPELESS", + "DXGI_FORMAT_R8G8B8A8_UNORM", + "DXGI_FORMAT_R8G8B8A8_UNORM_SRGB", + "DXGI_FORMAT_R8G8B8A8_UINT", + "DXGI_FORMAT_R8G8B8A8_SNORM", + "DXGI_FORMAT_R8G8B8A8_SINT", + "DXGI_FORMAT_R16G16_TYPELESS", + "DXGI_FORMAT_R16G16_FLOAT", + "DXGI_FORMAT_R16G16_UNORM", + "DXGI_FORMAT_R16G16_UINT", + "DXGI_FORMAT_R16G16_SNORM", + "DXGI_FORMAT_R16G16_SINT", + "DXGI_FORMAT_R32_TYPELESS", + "DXGI_FORMAT_D32_FLOAT", + "DXGI_FORMAT_R32_FLOAT", + "DXGI_FORMAT_R32_UINT", + "DXGI_FORMAT_R32_SINT", + "DXGI_FORMAT_R24G8_TYPELESS", + "DXGI_FORMAT_D24_UNORM_S8_UINT", + "DXGI_FORMAT_R24_UNORM_X8_TYPELESS", + "DXGI_FORMAT_X24_TYPELESS_G8_UINT", + "DXGI_FORMAT_R8G8_TYPELESS", + "DXGI_FORMAT_R8G8_UNORM", + "DXGI_FORMAT_R8G8_UINT", + "DXGI_FORMAT_R8G8_SNORM", + "DXGI_FORMAT_R8G8_SINT", + "DXGI_FORMAT_R16_TYPELESS", + "DXGI_FORMAT_R16_FLOAT", + "DXGI_FORMAT_D16_UNORM", + "DXGI_FORMAT_R16_UNORM", + "DXGI_FORMAT_R16_UINT", + "DXGI_FORMAT_R16_SNORM", + "DXGI_FORMAT_R16_SINT", + "DXGI_FORMAT_R8_TYPELESS", + "DXGI_FORMAT_R8_UNORM", + "DXGI_FORMAT_R8_UINT", + "DXGI_FORMAT_R8_SNORM", + "DXGI_FORMAT_R8_SINT", + "DXGI_FORMAT_A8_UNORM", + "DXGI_FORMAT_R1_UNORM", + "DXGI_FORMAT_R9G9B9E5_SHAREDEXP", + "DXGI_FORMAT_R8G8_B8G8_UNORM", + "DXGI_FORMAT_G8R8_G8B8_UNORM", + "DXGI_FORMAT_BC1_TYPELESS", + "DXGI_FORMAT_BC1_UNORM", + "DXGI_FORMAT_BC1_UNORM_SRGB", + "DXGI_FORMAT_BC2_TYPELESS", + "DXGI_FORMAT_BC2_UNORM", + "DXGI_FORMAT_BC2_UNORM_SRGB", + "DXGI_FORMAT_BC3_TYPELESS", + "DXGI_FORMAT_BC3_UNORM", + "DXGI_FORMAT_BC3_UNORM_SRGB", + "DXGI_FORMAT_BC4_TYPELESS", + "DXGI_FORMAT_BC4_UNORM", + "DXGI_FORMAT_BC4_SNORM", + "DXGI_FORMAT_BC5_TYPELESS", + "DXGI_FORMAT_BC5_UNORM", + "DXGI_FORMAT_BC5_SNORM", + "DXGI_FORMAT_B5G6R5_UNORM", + "DXGI_FORMAT_B5G5R5A1_UNORM", + "DXGI_FORMAT_B8G8R8A8_UNORM", + "DXGI_FORMAT_B8G8R8X8_UNORM", + "DXGI_FORMAT_R10G10B10_XR_BIAS_A2_UNORM", + "DXGI_FORMAT_B8G8R8A8_TYPELESS", + "DXGI_FORMAT_B8G8R8A8_UNORM_SRGB", + "DXGI_FORMAT_B8G8R8X8_TYPELESS", + "DXGI_FORMAT_B8G8R8X8_UNORM_SRGB", + "DXGI_FORMAT_BC6H_TYPELESS", + "DXGI_FORMAT_BC6H_UF16", + "DXGI_FORMAT_BC6H_SF16", + "DXGI_FORMAT_BC7_TYPELESS", + "DXGI_FORMAT_BC7_UNORM", + "DXGI_FORMAT_BC7_UNORM_SRGB", + "DXGI_FORMAT_AYUV", + "DXGI_FORMAT_Y410", + "DXGI_FORMAT_Y416", + "DXGI_FORMAT_NV12", + "DXGI_FORMAT_P010", + "DXGI_FORMAT_P016", + "DXGI_FORMAT_420_OPAQUE", + "DXGI_FORMAT_YUY2", + "DXGI_FORMAT_Y210", + "DXGI_FORMAT_Y216", + "DXGI_FORMAT_NV11", + "DXGI_FORMAT_AI44", + "DXGI_FORMAT_IA44", + "DXGI_FORMAT_P8", + "DXGI_FORMAT_A8P8", + "DXGI_FORMAT_B4G4R4A4_UNORM", - NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, - "DXGI_FORMAT_P208", - "DXGI_FORMAT_V208", - "DXGI_FORMAT_V408" - }; + "DXGI_FORMAT_P208", + "DXGI_FORMAT_V208", + "DXGI_FORMAT_V408"}; - const char * - display_base_t::dxgi_format_to_string(DXGI_FORMAT format) { +const char *display_base_t::dxgi_format_to_string(DXGI_FORMAT format) { return format_str[format]; - } +} - const char * - display_base_t::colorspace_to_string(DXGI_COLOR_SPACE_TYPE type) { +const char *display_base_t::colorspace_to_string(DXGI_COLOR_SPACE_TYPE type) { const char *type_str[] = { - "DXGI_COLOR_SPACE_RGB_FULL_G22_NONE_P709", - "DXGI_COLOR_SPACE_RGB_FULL_G10_NONE_P709", - "DXGI_COLOR_SPACE_RGB_STUDIO_G22_NONE_P709", - "DXGI_COLOR_SPACE_RGB_STUDIO_G22_NONE_P2020", - "DXGI_COLOR_SPACE_RESERVED", - "DXGI_COLOR_SPACE_YCBCR_FULL_G22_NONE_P709_X601", - "DXGI_COLOR_SPACE_YCBCR_STUDIO_G22_LEFT_P601", - "DXGI_COLOR_SPACE_YCBCR_FULL_G22_LEFT_P601", - "DXGI_COLOR_SPACE_YCBCR_STUDIO_G22_LEFT_P709", - "DXGI_COLOR_SPACE_YCBCR_FULL_G22_LEFT_P709", - "DXGI_COLOR_SPACE_YCBCR_STUDIO_G22_LEFT_P2020", - "DXGI_COLOR_SPACE_YCBCR_FULL_G22_LEFT_P2020", - "DXGI_COLOR_SPACE_RGB_FULL_G2084_NONE_P2020", - "DXGI_COLOR_SPACE_YCBCR_STUDIO_G2084_LEFT_P2020", - "DXGI_COLOR_SPACE_RGB_STUDIO_G2084_NONE_P2020", - "DXGI_COLOR_SPACE_YCBCR_STUDIO_G22_TOPLEFT_P2020", - "DXGI_COLOR_SPACE_YCBCR_STUDIO_G2084_TOPLEFT_P2020", - "DXGI_COLOR_SPACE_RGB_FULL_G22_NONE_P2020", - "DXGI_COLOR_SPACE_YCBCR_STUDIO_GHLG_TOPLEFT_P2020", - "DXGI_COLOR_SPACE_YCBCR_FULL_GHLG_TOPLEFT_P2020", - "DXGI_COLOR_SPACE_RGB_STUDIO_G24_NONE_P709", - "DXGI_COLOR_SPACE_RGB_STUDIO_G24_NONE_P2020", - "DXGI_COLOR_SPACE_YCBCR_STUDIO_G24_LEFT_P709", - "DXGI_COLOR_SPACE_YCBCR_STUDIO_G24_LEFT_P2020", - "DXGI_COLOR_SPACE_YCBCR_STUDIO_G24_TOPLEFT_P2020", + "DXGI_COLOR_SPACE_RGB_FULL_G22_NONE_P709", + "DXGI_COLOR_SPACE_RGB_FULL_G10_NONE_P709", + "DXGI_COLOR_SPACE_RGB_STUDIO_G22_NONE_P709", + "DXGI_COLOR_SPACE_RGB_STUDIO_G22_NONE_P2020", + "DXGI_COLOR_SPACE_RESERVED", + "DXGI_COLOR_SPACE_YCBCR_FULL_G22_NONE_P709_X601", + "DXGI_COLOR_SPACE_YCBCR_STUDIO_G22_LEFT_P601", + "DXGI_COLOR_SPACE_YCBCR_FULL_G22_LEFT_P601", + "DXGI_COLOR_SPACE_YCBCR_STUDIO_G22_LEFT_P709", + "DXGI_COLOR_SPACE_YCBCR_FULL_G22_LEFT_P709", + "DXGI_COLOR_SPACE_YCBCR_STUDIO_G22_LEFT_P2020", + "DXGI_COLOR_SPACE_YCBCR_FULL_G22_LEFT_P2020", + "DXGI_COLOR_SPACE_RGB_FULL_G2084_NONE_P2020", + "DXGI_COLOR_SPACE_YCBCR_STUDIO_G2084_LEFT_P2020", + "DXGI_COLOR_SPACE_RGB_STUDIO_G2084_NONE_P2020", + "DXGI_COLOR_SPACE_YCBCR_STUDIO_G22_TOPLEFT_P2020", + "DXGI_COLOR_SPACE_YCBCR_STUDIO_G2084_TOPLEFT_P2020", + "DXGI_COLOR_SPACE_RGB_FULL_G22_NONE_P2020", + "DXGI_COLOR_SPACE_YCBCR_STUDIO_GHLG_TOPLEFT_P2020", + "DXGI_COLOR_SPACE_YCBCR_FULL_GHLG_TOPLEFT_P2020", + "DXGI_COLOR_SPACE_RGB_STUDIO_G24_NONE_P709", + "DXGI_COLOR_SPACE_RGB_STUDIO_G24_NONE_P2020", + "DXGI_COLOR_SPACE_YCBCR_STUDIO_G24_LEFT_P709", + "DXGI_COLOR_SPACE_YCBCR_STUDIO_G24_LEFT_P2020", + "DXGI_COLOR_SPACE_YCBCR_STUDIO_G24_TOPLEFT_P2020", }; if (type < ARRAYSIZE(type_str)) { - return type_str[type]; + return type_str[type]; + } else { + return "UNKNOWN"; } - else { - return "UNKNOWN"; - } - } +} } // namespace platf::dxgi namespace platf { - std::shared_ptr - display(mem_type_e hwdevice_type, const std::string &display_name, const video::config_t &config) { +std::shared_ptr display(mem_type_e hwdevice_type, + const std::string &display_name, + const video::config_t &config) { if (hwdevice_type == mem_type_e::dxgi) { - auto disp = std::make_shared(); + auto disp = std::make_shared(); - if (!disp->init(config, display_name)) { - return disp; - } - } - else if (hwdevice_type == mem_type_e::system) { - auto disp = std::make_shared(); + if (!disp->init(config, display_name)) { + return disp; + } + } else if (hwdevice_type == mem_type_e::system) { + auto disp = std::make_shared(); - if (!disp->init(config, display_name)) { - return disp; - } + if (!disp->init(config, display_name)) { + return disp; + } } return nullptr; - } +} - std::vector - display_names(mem_type_e) { +std::vector display_names(mem_type_e) { std::vector display_names; // Ensure we can duplicate the current display @@ -1044,59 +1145,80 @@ namespace platf { // We must set the GPU preference before calling any DXGI APIs! if (!dxgi::probe_for_gpu_preference(config::video.output_name)) { - BOOST_LOG(warning) << "Failed to set GPU preference. Capture may not work!"sv; + BOOST_LOG(warning) + << "Failed to set GPU preference. Capture may not work!"sv; } dxgi::factory1_t factory; - status = CreateDXGIFactory1(IID_IDXGIFactory1, (void **) &factory); + status = CreateDXGIFactory1(IID_IDXGIFactory1, (void **)&factory); if (FAILED(status)) { - BOOST_LOG(error) << "Failed to create DXGIFactory1 [0x"sv << util::hex(status).to_string_view() << ']'; - return {}; + BOOST_LOG(error) << "Failed to create DXGIFactory1 [0x"sv + << util::hex(status).to_string_view() << ']'; + return {}; } dxgi::adapter_t adapter; - for (int x = 0; factory->EnumAdapters1(x, &adapter) != DXGI_ERROR_NOT_FOUND; ++x) { - DXGI_ADAPTER_DESC1 adapter_desc; - adapter->GetDesc1(&adapter_desc); + for (int x = 0; factory->EnumAdapters1(x, &adapter) != DXGI_ERROR_NOT_FOUND; + ++x) { + DXGI_ADAPTER_DESC1 adapter_desc; + adapter->GetDesc1(&adapter_desc); - BOOST_LOG(debug) - << std::endl - << "====== ADAPTER ====="sv << std::endl - << "Device Name : "sv << converter.to_bytes(adapter_desc.Description) << std::endl - << "Device Vendor ID : 0x"sv << util::hex(adapter_desc.VendorId).to_string_view() << std::endl - << "Device Device ID : 0x"sv << util::hex(adapter_desc.DeviceId).to_string_view() << std::endl - << "Device Video Mem : "sv << adapter_desc.DedicatedVideoMemory / 1048576 << " MiB"sv << std::endl - << "Device Sys Mem : "sv << adapter_desc.DedicatedSystemMemory / 1048576 << " MiB"sv << std::endl - << "Share Sys Mem : "sv << adapter_desc.SharedSystemMemory / 1048576 << " MiB"sv << std::endl - << std::endl - << " ====== OUTPUT ======"sv << std::endl; + BOOST_LOG(debug) << std::endl + << "====== ADAPTER ====="sv << std::endl + << "Device Name : "sv + << converter.to_bytes(adapter_desc.Description) + << std::endl + << "Device Vendor ID : 0x"sv + << util::hex(adapter_desc.VendorId).to_string_view() + << std::endl + << "Device Device ID : 0x"sv + << util::hex(adapter_desc.DeviceId).to_string_view() + << std::endl + << "Device Video Mem : "sv + << adapter_desc.DedicatedVideoMemory / 1048576 + << " MiB"sv << std::endl + << "Device Sys Mem : "sv + << adapter_desc.DedicatedSystemMemory / 1048576 + << " MiB"sv << std::endl + << "Share Sys Mem : "sv + << adapter_desc.SharedSystemMemory / 1048576 + << " MiB"sv << std::endl + << std::endl + << " ====== OUTPUT ======"sv << std::endl; - dxgi::output_t::pointer output_p {}; - for (int y = 0; adapter->EnumOutputs(y, &output_p) != DXGI_ERROR_NOT_FOUND; ++y) { - dxgi::output_t output { output_p }; + dxgi::output_t::pointer output_p{}; + for (int y = 0; + adapter->EnumOutputs(y, &output_p) != DXGI_ERROR_NOT_FOUND; ++y) { + dxgi::output_t output{output_p}; - DXGI_OUTPUT_DESC desc; - output->GetDesc(&desc); + DXGI_OUTPUT_DESC desc; + output->GetDesc(&desc); - auto device_name = converter.to_bytes(desc.DeviceName); + auto device_name = converter.to_bytes(desc.DeviceName); - auto width = desc.DesktopCoordinates.right - desc.DesktopCoordinates.left; - auto height = desc.DesktopCoordinates.bottom - desc.DesktopCoordinates.top; + auto width = + desc.DesktopCoordinates.right - desc.DesktopCoordinates.left; + auto height = + desc.DesktopCoordinates.bottom - desc.DesktopCoordinates.top; - BOOST_LOG(debug) - << " Output Name : "sv << device_name << std::endl - << " AttachedToDesktop : "sv << (desc.AttachedToDesktop ? "yes"sv : "no"sv) << std::endl - << " Resolution : "sv << width << 'x' << height << std::endl - << std::endl; + BOOST_LOG(debug) + << " Output Name : "sv << device_name << std::endl + << " AttachedToDesktop : "sv + << (desc.AttachedToDesktop ? "yes"sv : "no"sv) << std::endl + << " Resolution : "sv << width << 'x' << height + << std::endl + << std::endl; - // Don't include the display in the list if we can't actually capture it - if (desc.AttachedToDesktop && dxgi::test_dxgi_duplication(adapter, output)) { - display_names.emplace_back(std::move(device_name)); + // Don't include the display in the list if we can't actually + // capture it + if (desc.AttachedToDesktop && + dxgi::test_dxgi_duplication(adapter, output)) { + display_names.emplace_back(std::move(device_name)); + } } - } } return display_names; - } +} } // namespace platf diff --git a/src/platform/windows/display_ram.cpp b/src/platform/windows/display_ram.cpp index d29ff883..1e651989 100644 --- a/src/platform/windows/display_ram.cpp +++ b/src/platform/windows/display_ram.cpp @@ -3,24 +3,22 @@ * @brief todo */ #include "display.h" - #include "misc.h" #include "src/main.h" namespace platf { - using namespace std::literals; +using namespace std::literals; } namespace platf::dxgi { - struct img_t: public ::platf::img_t { +struct img_t : public ::platf::img_t { ~img_t() override { - delete[] data; - data = nullptr; + delete[] data; + data = nullptr; } - }; +}; - void - blend_cursor_monochrome(const cursor_t &cursor, img_t &img) { +void blend_cursor_monochrome(const cursor_t &cursor, img_t &img) { int height = cursor.shape_info.Height / 2; int width = cursor.shape_info.Width; int pitch = cursor.shape_info.Pitch; @@ -37,7 +35,7 @@ namespace platf::dxgi { auto cursor_height = height - cursor_skip_y - cursor_truncate_y; if (cursor_height > height || cursor_width > width) { - return; + return; } auto img_skip_y = std::max(0, cursor.y); @@ -45,74 +43,78 @@ namespace platf::dxgi { auto cursor_img_data = cursor.img_data.data() + cursor_skip_y * pitch; - int delta_height = std::min(cursor_height - cursor_truncate_y, std::max(0, img.height - img_skip_y)); - int delta_width = std::min(cursor_width - cursor_truncate_x, std::max(0, img.width - img_skip_x)); + int delta_height = std::min(cursor_height - cursor_truncate_y, + std::max(0, img.height - img_skip_y)); + int delta_width = std::min(cursor_width - cursor_truncate_x, + std::max(0, img.width - img_skip_x)); auto pixels_per_byte = width / pitch; auto bytes_per_row = delta_width / pixels_per_byte; - auto img_data = (int *) img.data; + auto img_data = (int *)img.data; for (int i = 0; i < delta_height; ++i) { - auto and_mask = &cursor_img_data[i * pitch]; - auto xor_mask = &cursor_img_data[(i + height) * pitch]; + auto and_mask = &cursor_img_data[i * pitch]; + auto xor_mask = &cursor_img_data[(i + height) * pitch]; - auto img_pixel_p = &img_data[(i + img_skip_y) * (img.row_pitch / img.pixel_pitch) + img_skip_x]; + auto img_pixel_p = + &img_data[(i + img_skip_y) * (img.row_pitch / img.pixel_pitch) + + img_skip_x]; - auto skip_x = cursor_skip_x; - for (int x = 0; x < bytes_per_row; ++x) { - for (auto bit = 0u; bit < 8; ++bit) { - if (skip_x > 0) { - --skip_x; + auto skip_x = cursor_skip_x; + for (int x = 0; x < bytes_per_row; ++x) { + for (auto bit = 0u; bit < 8; ++bit) { + if (skip_x > 0) { + --skip_x; - continue; - } + continue; + } - int and_ = *and_mask & (1 << (7 - bit)) ? -1 : 0; - int xor_ = *xor_mask & (1 << (7 - bit)) ? -1 : 0; + int and_ = *and_mask & (1 << (7 - bit)) ? -1 : 0; + int xor_ = *xor_mask & (1 << (7 - bit)) ? -1 : 0; - *img_pixel_p &= and_; - *img_pixel_p ^= xor_; + *img_pixel_p &= and_; + *img_pixel_p ^= xor_; - ++img_pixel_p; + ++img_pixel_p; + } + + ++and_mask; + ++xor_mask; } - - ++and_mask; - ++xor_mask; - } } - } +} - void - apply_color_alpha(int *img_pixel_p, int cursor_pixel) { - auto colors_out = (std::uint8_t *) &cursor_pixel; - auto colors_in = (std::uint8_t *) img_pixel_p; +void apply_color_alpha(int *img_pixel_p, int cursor_pixel) { + auto colors_out = (std::uint8_t *)&cursor_pixel; + auto colors_in = (std::uint8_t *)img_pixel_p; - // TODO: When use of IDXGIOutput5 is implemented, support different color formats + // TODO: When use of IDXGIOutput5 is implemented, support different color + // formats auto alpha = colors_out[3]; if (alpha == 255) { - *img_pixel_p = cursor_pixel; + *img_pixel_p = cursor_pixel; + } else { + colors_in[0] = + colors_out[0] + (colors_in[0] * (255 - alpha) + 255 / 2) / 255; + colors_in[1] = + colors_out[1] + (colors_in[1] * (255 - alpha) + 255 / 2) / 255; + colors_in[2] = + colors_out[2] + (colors_in[2] * (255 - alpha) + 255 / 2) / 255; } - else { - colors_in[0] = colors_out[0] + (colors_in[0] * (255 - alpha) + 255 / 2) / 255; - colors_in[1] = colors_out[1] + (colors_in[1] * (255 - alpha) + 255 / 2) / 255; - colors_in[2] = colors_out[2] + (colors_in[2] * (255 - alpha) + 255 / 2) / 255; - } - } +} - void - apply_color_masked(int *img_pixel_p, int cursor_pixel) { - // TODO: When use of IDXGIOutput5 is implemented, support different color formats - auto alpha = ((std::uint8_t *) &cursor_pixel)[3]; +void apply_color_masked(int *img_pixel_p, int cursor_pixel) { + // TODO: When use of IDXGIOutput5 is implemented, support different color + // formats + auto alpha = ((std::uint8_t *)&cursor_pixel)[3]; if (alpha == 0xFF) { - *img_pixel_p ^= cursor_pixel; + *img_pixel_p ^= cursor_pixel; + } else { + *img_pixel_p = cursor_pixel; } - else { - *img_pixel_p = cursor_pixel; - } - } +} - void - blend_cursor_color(const cursor_t &cursor, img_t &img, const bool masked) { +void blend_cursor_color(const cursor_t &cursor, img_t &img, const bool masked) { int height = cursor.shape_info.Height; int width = cursor.shape_info.Width; int pitch = cursor.shape_info.Pitch; @@ -132,203 +134,232 @@ namespace platf::dxgi { auto cursor_height = height - cursor_skip_y - cursor_truncate_y; if (cursor_height > height || cursor_width > width) { - return; + return; } - auto cursor_img_data = (int *) &cursor.img_data[cursor_skip_y * pitch]; + auto cursor_img_data = (int *)&cursor.img_data[cursor_skip_y * pitch]; - int delta_height = std::min(cursor_height - cursor_truncate_y, std::max(0, img.height - img_skip_y)); - int delta_width = std::min(cursor_width - cursor_truncate_x, std::max(0, img.width - img_skip_x)); + int delta_height = std::min(cursor_height - cursor_truncate_y, + std::max(0, img.height - img_skip_y)); + int delta_width = std::min(cursor_width - cursor_truncate_x, + std::max(0, img.width - img_skip_x)); - auto img_data = (int *) img.data; + auto img_data = (int *)img.data; for (int i = 0; i < delta_height; ++i) { - auto cursor_begin = &cursor_img_data[i * cursor.shape_info.Width + cursor_skip_x]; - auto cursor_end = &cursor_begin[delta_width]; + auto cursor_begin = + &cursor_img_data[i * cursor.shape_info.Width + cursor_skip_x]; + auto cursor_end = &cursor_begin[delta_width]; - auto img_pixel_p = &img_data[(i + img_skip_y) * (img.row_pitch / img.pixel_pitch) + img_skip_x]; - std::for_each(cursor_begin, cursor_end, [&](int cursor_pixel) { - if (masked) { - apply_color_masked(img_pixel_p, cursor_pixel); - } - else { - apply_color_alpha(img_pixel_p, cursor_pixel); - } - ++img_pixel_p; - }); + auto img_pixel_p = + &img_data[(i + img_skip_y) * (img.row_pitch / img.pixel_pitch) + + img_skip_x]; + std::for_each(cursor_begin, cursor_end, [&](int cursor_pixel) { + if (masked) { + apply_color_masked(img_pixel_p, cursor_pixel); + } else { + apply_color_alpha(img_pixel_p, cursor_pixel); + } + ++img_pixel_p; + }); } - } +} - void - blend_cursor(const cursor_t &cursor, img_t &img) { +void blend_cursor(const cursor_t &cursor, img_t &img) { switch (cursor.shape_info.Type) { - case DXGI_OUTDUPL_POINTER_SHAPE_TYPE_COLOR: - blend_cursor_color(cursor, img, false); - break; - case DXGI_OUTDUPL_POINTER_SHAPE_TYPE_MONOCHROME: - blend_cursor_monochrome(cursor, img); - break; - case DXGI_OUTDUPL_POINTER_SHAPE_TYPE_MASKED_COLOR: - blend_cursor_color(cursor, img, true); - break; - default: - BOOST_LOG(warning) << "Unsupported cursor format ["sv << cursor.shape_info.Type << ']'; - break; + case DXGI_OUTDUPL_POINTER_SHAPE_TYPE_COLOR: + blend_cursor_color(cursor, img, false); + break; + case DXGI_OUTDUPL_POINTER_SHAPE_TYPE_MONOCHROME: + blend_cursor_monochrome(cursor, img); + break; + case DXGI_OUTDUPL_POINTER_SHAPE_TYPE_MASKED_COLOR: + blend_cursor_color(cursor, img, true); + break; + default: + BOOST_LOG(warning) << "Unsupported cursor format ["sv + << cursor.shape_info.Type << ']'; + break; } - } +} - capture_e - display_ram_t::snapshot(const pull_free_image_cb_t &pull_free_image_cb, std::shared_ptr &img_out, std::chrono::milliseconds timeout, bool cursor_visible) { +capture_e display_ram_t::snapshot( + const pull_free_image_cb_t &pull_free_image_cb, + std::shared_ptr &img_out, std::chrono::milliseconds timeout, + bool cursor_visible) { HRESULT status; DXGI_OUTDUPL_FRAME_INFO frame_info; - resource_t::pointer res_p {}; + resource_t::pointer res_p{}; auto capture_status = dup.next_frame(frame_info, timeout, &res_p); - resource_t res { res_p }; + resource_t res{res_p}; if (capture_status != capture_e::ok) { - return capture_status; + return capture_status; } - const bool mouse_update_flag = frame_info.LastMouseUpdateTime.QuadPart != 0 || frame_info.PointerShapeBufferSize > 0; - const bool frame_update_flag = frame_info.AccumulatedFrames != 0 || frame_info.LastPresentTime.QuadPart != 0; + const bool mouse_update_flag = + frame_info.LastMouseUpdateTime.QuadPart != 0 || + frame_info.PointerShapeBufferSize > 0; + const bool frame_update_flag = frame_info.AccumulatedFrames != 0 || + frame_info.LastPresentTime.QuadPart != 0; const bool update_flag = mouse_update_flag || frame_update_flag; if (!update_flag) { - return capture_e::timeout; + return capture_e::timeout; } std::optional frame_timestamp; - if (auto qpc_displayed = std::max(frame_info.LastPresentTime.QuadPart, frame_info.LastMouseUpdateTime.QuadPart)) { - // Translate QueryPerformanceCounter() value to steady_clock time point - frame_timestamp = std::chrono::steady_clock::now() - qpc_time_difference(qpc_counter(), qpc_displayed); + if (auto qpc_displayed = + std::max(frame_info.LastPresentTime.QuadPart, + frame_info.LastMouseUpdateTime.QuadPart)) { + // Translate QueryPerformanceCounter() value to steady_clock time point + frame_timestamp = std::chrono::steady_clock::now() - + qpc_time_difference(qpc_counter(), qpc_displayed); } if (frame_info.PointerShapeBufferSize > 0) { - auto &img_data = cursor.img_data; + auto &img_data = cursor.img_data; - img_data.resize(frame_info.PointerShapeBufferSize); + img_data.resize(frame_info.PointerShapeBufferSize); - UINT dummy; - status = dup.dup->GetFramePointerShape(img_data.size(), img_data.data(), &dummy, &cursor.shape_info); - if (FAILED(status)) { - BOOST_LOG(error) << "Failed to get new pointer shape [0x"sv << util::hex(status).to_string_view() << ']'; + UINT dummy; + status = dup.dup->GetFramePointerShape(img_data.size(), img_data.data(), + &dummy, &cursor.shape_info); + if (FAILED(status)) { + BOOST_LOG(error) << "Failed to get new pointer shape [0x"sv + << util::hex(status).to_string_view() << ']'; - return capture_e::error; - } + return capture_e::error; + } } if (frame_info.LastMouseUpdateTime.QuadPart) { - cursor.x = frame_info.PointerPosition.Position.x; - cursor.y = frame_info.PointerPosition.Position.y; - cursor.visible = frame_info.PointerPosition.Visible; + cursor.x = frame_info.PointerPosition.Position.x; + cursor.y = frame_info.PointerPosition.Position.y; + cursor.visible = frame_info.PointerPosition.Visible; } if (frame_update_flag) { - { - texture2d_t src {}; - status = res->QueryInterface(IID_ID3D11Texture2D, (void **) &src); + { + texture2d_t src{}; + status = res->QueryInterface(IID_ID3D11Texture2D, (void **)&src); - if (FAILED(status)) { - BOOST_LOG(error) << "Couldn't query interface [0x"sv << util::hex(status).to_string_view() << ']'; - return capture_e::error; + if (FAILED(status)) { + BOOST_LOG(error) << "Couldn't query interface [0x"sv + << util::hex(status).to_string_view() << ']'; + return capture_e::error; + } + + D3D11_TEXTURE2D_DESC desc; + src->GetDesc(&desc); + + // If we don't know the capture format yet, grab it from this + // texture and create the staging texture + if (capture_format == DXGI_FORMAT_UNKNOWN) { + capture_format = desc.Format; + BOOST_LOG(info) << "Capture format ["sv + << dxgi_format_to_string(capture_format) << ']'; + + D3D11_TEXTURE2D_DESC t{}; + t.Width = width; + t.Height = height; + t.MipLevels = 1; + t.ArraySize = 1; + t.SampleDesc.Count = 1; + t.Usage = D3D11_USAGE_STAGING; + t.Format = capture_format; + t.CPUAccessFlags = D3D11_CPU_ACCESS_READ; + + auto status = device->CreateTexture2D(&t, nullptr, &texture); + + if (FAILED(status)) { + BOOST_LOG(error) + << "Failed to create staging texture [0x"sv + << util::hex(status).to_string_view() << ']'; + return capture_e::error; + } + } + + // It's possible for our display enumeration to race with mode + // changes and result in mismatched image pool and desktop texture + // sizes. If this happens, just reinit again. + if (desc.Width != width || desc.Height != height) { + BOOST_LOG(info) + << "Capture size changed ["sv << width << 'x' << height + << " -> "sv << desc.Width << 'x' << desc.Height << ']'; + return capture_e::reinit; + } + + // It's also possible for the capture format to change on the fly. + // If that happens, reinitialize capture to try format detection + // again and create new images. + if (capture_format != desc.Format) { + BOOST_LOG(info) + << "Capture format changed ["sv + << dxgi_format_to_string(capture_format) << " -> "sv + << dxgi_format_to_string(desc.Format) << ']'; + return capture_e::reinit; + } + + // Copy from GPU to CPU + device_ctx->CopyResource(texture.get(), src.get()); } - - D3D11_TEXTURE2D_DESC desc; - src->GetDesc(&desc); - - // If we don't know the capture format yet, grab it from this texture and create the staging texture - if (capture_format == DXGI_FORMAT_UNKNOWN) { - capture_format = desc.Format; - BOOST_LOG(info) << "Capture format ["sv << dxgi_format_to_string(capture_format) << ']'; - - D3D11_TEXTURE2D_DESC t {}; - t.Width = width; - t.Height = height; - t.MipLevels = 1; - t.ArraySize = 1; - t.SampleDesc.Count = 1; - t.Usage = D3D11_USAGE_STAGING; - t.Format = capture_format; - t.CPUAccessFlags = D3D11_CPU_ACCESS_READ; - - auto status = device->CreateTexture2D(&t, nullptr, &texture); - - if (FAILED(status)) { - BOOST_LOG(error) << "Failed to create staging texture [0x"sv << util::hex(status).to_string_view() << ']'; - return capture_e::error; - } - } - - // It's possible for our display enumeration to race with mode changes and result in - // mismatched image pool and desktop texture sizes. If this happens, just reinit again. - if (desc.Width != width || desc.Height != height) { - BOOST_LOG(info) << "Capture size changed ["sv << width << 'x' << height << " -> "sv << desc.Width << 'x' << desc.Height << ']'; - return capture_e::reinit; - } - - // It's also possible for the capture format to change on the fly. If that happens, - // reinitialize capture to try format detection again and create new images. - if (capture_format != desc.Format) { - BOOST_LOG(info) << "Capture format changed ["sv << dxgi_format_to_string(capture_format) << " -> "sv << dxgi_format_to_string(desc.Format) << ']'; - return capture_e::reinit; - } - - // Copy from GPU to CPU - device_ctx->CopyResource(texture.get(), src.get()); - } } if (!pull_free_image_cb(img_out)) { - return capture_e::interrupted; + return capture_e::interrupted; } - auto img = (img_t *) img_out.get(); + auto img = (img_t *)img_out.get(); // If we don't know the final capture format yet, encode a dummy image if (capture_format == DXGI_FORMAT_UNKNOWN) { - BOOST_LOG(debug) << "Capture format is still unknown. Encoding a blank image"sv; + BOOST_LOG(debug) + << "Capture format is still unknown. Encoding a blank image"sv; - if (dummy_img(img)) { - return capture_e::error; - } - } - else { - // Map the staging texture for CPU access (making it inaccessible for the GPU) - status = device_ctx->Map(texture.get(), 0, D3D11_MAP_READ, 0, &img_info); - if (FAILED(status)) { - BOOST_LOG(error) << "Failed to map texture [0x"sv << util::hex(status).to_string_view() << ']'; + if (dummy_img(img)) { + return capture_e::error; + } + } else { + // Map the staging texture for CPU access (making it inaccessible for + // the GPU) + status = + device_ctx->Map(texture.get(), 0, D3D11_MAP_READ, 0, &img_info); + if (FAILED(status)) { + BOOST_LOG(error) << "Failed to map texture [0x"sv + << util::hex(status).to_string_view() << ']'; - return capture_e::error; - } + return capture_e::error; + } - // Now that we know the capture format, we can finish creating the image - if (complete_img(img, false)) { + // Now that we know the capture format, we can finish creating the image + if (complete_img(img, false)) { + device_ctx->Unmap(texture.get(), 0); + img_info.pData = nullptr; + return capture_e::error; + } + + std::copy_n((std::uint8_t *)img_info.pData, height * img_info.RowPitch, + (std::uint8_t *)img->data); + + // Unmap the staging texture to allow GPU access again device_ctx->Unmap(texture.get(), 0); img_info.pData = nullptr; - return capture_e::error; - } - - std::copy_n((std::uint8_t *) img_info.pData, height * img_info.RowPitch, (std::uint8_t *) img->data); - - // Unmap the staging texture to allow GPU access again - device_ctx->Unmap(texture.get(), 0); - img_info.pData = nullptr; } if (cursor_visible && cursor.visible) { - blend_cursor(cursor, *img); + blend_cursor(cursor, *img); } if (img) { - img->frame_timestamp = frame_timestamp; + img->frame_timestamp = frame_timestamp; } return capture_e::ok; - } +} - std::shared_ptr - display_ram_t::alloc_img() { +std::shared_ptr display_ram_t::alloc_img() { auto img = std::make_shared(); // Initialize fields that are format-independent @@ -336,64 +367,62 @@ namespace platf::dxgi { img->height = height; return img; - } +} - int - display_ram_t::complete_img(platf::img_t *img, bool dummy) { +int display_ram_t::complete_img(platf::img_t *img, bool dummy) { // If this is not a dummy image, we must know the format by now if (!dummy && capture_format == DXGI_FORMAT_UNKNOWN) { - BOOST_LOG(error) << "display_ram_t::complete_img() called with unknown capture format!"; - return -1; + BOOST_LOG(error) << "display_ram_t::complete_img() called with unknown " + "capture format!"; + return -1; } img->pixel_pitch = get_pixel_pitch(); if (dummy && !img->row_pitch) { - // Assume our dummy image will have no padding - img->row_pitch = img->pixel_pitch * img->width; + // Assume our dummy image will have no padding + img->row_pitch = img->pixel_pitch * img->width; } // Reallocate the image buffer if the pitch changes if (!dummy && img->row_pitch != img_info.RowPitch) { - img->row_pitch = img_info.RowPitch; - delete img->data; - img->data = nullptr; + img->row_pitch = img_info.RowPitch; + delete img->data; + img->data = nullptr; } if (!img->data) { - img->data = new std::uint8_t[img->row_pitch * height]; + img->data = new std::uint8_t[img->row_pitch * height]; } return 0; - } +} - int - display_ram_t::dummy_img(platf::img_t *img) { +int display_ram_t::dummy_img(platf::img_t *img) { if (complete_img(img, true)) { - return -1; + return -1; } - std::fill_n((std::uint8_t *) img->data, height * img->row_pitch, 0); + std::fill_n((std::uint8_t *)img->data, height * img->row_pitch, 0); return 0; - } +} - std::vector - display_ram_t::get_supported_capture_formats() { - return { DXGI_FORMAT_B8G8R8A8_UNORM, DXGI_FORMAT_B8G8R8X8_UNORM }; - } +std::vector display_ram_t::get_supported_capture_formats() { + return {DXGI_FORMAT_B8G8R8A8_UNORM, DXGI_FORMAT_B8G8R8X8_UNORM}; +} - int - display_ram_t::init(const ::video::config_t &config, const std::string &display_name) { +int display_ram_t::init(const ::video::config_t &config, + const std::string &display_name) { if (display_base_t::init(config, display_name)) { - return -1; + return -1; } return 0; - } +} - std::unique_ptr - display_ram_t::make_avcodec_encode_device(pix_fmt_e pix_fmt) { +std::unique_ptr +display_ram_t::make_avcodec_encode_device(pix_fmt_e pix_fmt) { return std::make_unique(); - } +} } // namespace platf::dxgi diff --git a/src/platform/windows/display_vram.cpp b/src/platform/windows/display_vram.cpp index 2d31326e..5c083372 100644 --- a/src/platform/windows/display_vram.cpp +++ b/src/platform/windows/display_vram.cpp @@ -2,18 +2,21 @@ * @file src/platform/windows/display_vram.cpp * @brief todo */ -#include - -#include - #include #include +#include +#include + extern "C" { #include #include } +#include + +#include + #include "display.h" #include "misc.h" #include "src/config.h" @@ -23,98 +26,86 @@ extern "C" { #include "src/nvenc/nvenc_utils.h" #include "src/video.h" -#include - -#include - #define SUNSHINE_SHADERS_DIR "./directx" namespace platf { - using namespace std::literals; +using namespace std::literals; } -static void -free_frame(AVFrame *frame) { - av_frame_free(&frame); -} +static void free_frame(AVFrame *frame) { av_frame_free(&frame); } using frame_t = util::safe_ptr; namespace platf::dxgi { - template - buf_t - make_buffer(device_t::pointer device, const T &t) { - static_assert(sizeof(T) % 16 == 0, "Buffer needs to be aligned on a 16-byte alignment"); +template +buf_t make_buffer(device_t::pointer device, const T &t) { + static_assert(sizeof(T) % 16 == 0, + "Buffer needs to be aligned on a 16-byte alignment"); - D3D11_BUFFER_DESC buffer_desc { - sizeof(T), - D3D11_USAGE_IMMUTABLE, - D3D11_BIND_CONSTANT_BUFFER - }; + D3D11_BUFFER_DESC buffer_desc{sizeof(T), D3D11_USAGE_IMMUTABLE, + D3D11_BIND_CONSTANT_BUFFER}; - D3D11_SUBRESOURCE_DATA init_data { - &t - }; + D3D11_SUBRESOURCE_DATA init_data{&t}; buf_t::pointer buf_p; auto status = device->CreateBuffer(&buffer_desc, &init_data, &buf_p); if (status) { - BOOST_LOG(error) << "Failed to create buffer: [0x"sv << util::hex(status).to_string_view() << ']'; - return nullptr; + BOOST_LOG(error) << "Failed to create buffer: [0x"sv + << util::hex(status).to_string_view() << ']'; + return nullptr; } - return buf_t { buf_p }; - } + return buf_t{buf_p}; +} - blend_t - make_blend(device_t::pointer device, bool enable, bool invert) { - D3D11_BLEND_DESC bdesc {}; +blend_t make_blend(device_t::pointer device, bool enable, bool invert) { + D3D11_BLEND_DESC bdesc{}; auto &rt = bdesc.RenderTarget[0]; rt.BlendEnable = enable; rt.RenderTargetWriteMask = D3D11_COLOR_WRITE_ENABLE_ALL; if (enable) { - rt.BlendOp = D3D11_BLEND_OP_ADD; - rt.BlendOpAlpha = D3D11_BLEND_OP_ADD; + rt.BlendOp = D3D11_BLEND_OP_ADD; + rt.BlendOpAlpha = D3D11_BLEND_OP_ADD; - if (invert) { - // Invert colors - rt.SrcBlend = D3D11_BLEND_INV_DEST_COLOR; - rt.DestBlend = D3D11_BLEND_INV_SRC_COLOR; - } - else { - // Regular alpha blending - rt.SrcBlend = D3D11_BLEND_SRC_ALPHA; - rt.DestBlend = D3D11_BLEND_INV_SRC_ALPHA; - } + if (invert) { + // Invert colors + rt.SrcBlend = D3D11_BLEND_INV_DEST_COLOR; + rt.DestBlend = D3D11_BLEND_INV_SRC_COLOR; + } else { + // Regular alpha blending + rt.SrcBlend = D3D11_BLEND_SRC_ALPHA; + rt.DestBlend = D3D11_BLEND_INV_SRC_ALPHA; + } - rt.SrcBlendAlpha = D3D11_BLEND_ZERO; - rt.DestBlendAlpha = D3D11_BLEND_ZERO; + rt.SrcBlendAlpha = D3D11_BLEND_ZERO; + rt.DestBlendAlpha = D3D11_BLEND_ZERO; } blend_t blend; auto status = device->CreateBlendState(&bdesc, &blend); if (status) { - BOOST_LOG(error) << "Failed to create blend state: [0x"sv << util::hex(status).to_string_view() << ']'; - return nullptr; + BOOST_LOG(error) << "Failed to create blend state: [0x"sv + << util::hex(status).to_string_view() << ']'; + return nullptr; } return blend; - } +} - blob_t convert_yuv420_packed_uv_type0_ps_hlsl; - blob_t convert_yuv420_packed_uv_type0_ps_linear_hlsl; - blob_t convert_yuv420_packed_uv_type0_ps_perceptual_quantizer_hlsl; - blob_t convert_yuv420_packed_uv_type0_vs_hlsl; - blob_t convert_yuv420_planar_y_ps_hlsl; - blob_t convert_yuv420_planar_y_ps_linear_hlsl; - blob_t convert_yuv420_planar_y_ps_perceptual_quantizer_hlsl; - blob_t convert_yuv420_planar_y_vs_hlsl; - blob_t cursor_ps_hlsl; - blob_t cursor_ps_normalize_white_hlsl; - blob_t cursor_vs_hlsl; +blob_t convert_yuv420_packed_uv_type0_ps_hlsl; +blob_t convert_yuv420_packed_uv_type0_ps_linear_hlsl; +blob_t convert_yuv420_packed_uv_type0_ps_perceptual_quantizer_hlsl; +blob_t convert_yuv420_packed_uv_type0_vs_hlsl; +blob_t convert_yuv420_planar_y_ps_hlsl; +blob_t convert_yuv420_planar_y_ps_linear_hlsl; +blob_t convert_yuv420_planar_y_ps_perceptual_quantizer_hlsl; +blob_t convert_yuv420_planar_y_vs_hlsl; +blob_t cursor_ps_hlsl; +blob_t cursor_ps_normalize_white_hlsl; +blob_t cursor_vs_hlsl; - struct img_d3d_t: public platf::img_t { +struct img_d3d_t : public platf::img_t { // These objects are owned by the display_t's ID3D11Device texture2d_t capture_texture; render_target_t capture_rt; @@ -134,206 +125,219 @@ namespace platf::dxgi { DXGI_FORMAT format; virtual ~img_d3d_t() override { - if (encoder_texture_handle) { - CloseHandle(encoder_texture_handle); - } + if (encoder_texture_handle) { + CloseHandle(encoder_texture_handle); + } }; - }; +}; - struct texture_lock_helper { +struct texture_lock_helper { keyed_mutex_t _mutex; bool _locked = false; texture_lock_helper(const texture_lock_helper &) = delete; - texture_lock_helper & - operator=(const texture_lock_helper &) = delete; + texture_lock_helper &operator=(const texture_lock_helper &) = delete; texture_lock_helper(texture_lock_helper &&other) { - _mutex.reset(other._mutex.release()); - _locked = other._locked; - other._locked = false; + _mutex.reset(other._mutex.release()); + _locked = other._locked; + other._locked = false; } - texture_lock_helper & - operator=(texture_lock_helper &&other) { - if (_locked) _mutex->ReleaseSync(0); - _mutex.reset(other._mutex.release()); - _locked = other._locked; - other._locked = false; - return *this; + texture_lock_helper &operator=(texture_lock_helper &&other) { + if (_locked) _mutex->ReleaseSync(0); + _mutex.reset(other._mutex.release()); + _locked = other._locked; + other._locked = false; + return *this; } - texture_lock_helper(IDXGIKeyedMutex *mutex): - _mutex(mutex) { - if (_mutex) _mutex->AddRef(); + texture_lock_helper(IDXGIKeyedMutex *mutex) : _mutex(mutex) { + if (_mutex) _mutex->AddRef(); } ~texture_lock_helper() { - if (_locked) _mutex->ReleaseSync(0); + if (_locked) _mutex->ReleaseSync(0); } - bool - lock() { - if (_locked) return true; - HRESULT status = _mutex->AcquireSync(0, INFINITE); - if (status == S_OK) { - _locked = true; - } - else { - BOOST_LOG(error) << "Failed to acquire texture mutex [0x"sv << util::hex(status).to_string_view() << ']'; - } - return _locked; + bool lock() { + if (_locked) return true; + HRESULT status = _mutex->AcquireSync(0, INFINITE); + if (status == S_OK) { + _locked = true; + } else { + BOOST_LOG(error) << "Failed to acquire texture mutex [0x"sv + << util::hex(status).to_string_view() << ']'; + } + return _locked; } - }; +}; - util::buffer_t - make_cursor_xor_image(const util::buffer_t &img_data, DXGI_OUTDUPL_POINTER_SHAPE_INFO shape_info) { +util::buffer_t make_cursor_xor_image( + const util::buffer_t &img_data, + DXGI_OUTDUPL_POINTER_SHAPE_INFO shape_info) { constexpr std::uint32_t inverted = 0xFFFFFFFF; constexpr std::uint32_t transparent = 0; switch (shape_info.Type) { - case DXGI_OUTDUPL_POINTER_SHAPE_TYPE_COLOR: - // This type doesn't require any XOR-blending - return {}; - case DXGI_OUTDUPL_POINTER_SHAPE_TYPE_MASKED_COLOR: { - util::buffer_t cursor_img = img_data; - std::for_each((std::uint32_t *) std::begin(cursor_img), (std::uint32_t *) std::end(cursor_img), [](auto &pixel) { - auto alpha = (std::uint8_t)((pixel >> 24) & 0xFF); - if (alpha == 0xFF) { - // Pixels with 0xFF alpha will be XOR-blended as is. - } - else if (alpha == 0x00) { - // Pixels with 0x00 alpha will be blended by make_cursor_alpha_image(). - // We make them transparent for the XOR-blended cursor image. - pixel = transparent; - } - else { - // Other alpha values are illegal in masked color cursors - BOOST_LOG(warning) << "Illegal alpha value in masked color cursor: " << alpha; - } - }); - return cursor_img; - } - case DXGI_OUTDUPL_POINTER_SHAPE_TYPE_MONOCHROME: - // Monochrome is handled below - break; - default: - BOOST_LOG(error) << "Invalid cursor shape type: " << shape_info.Type; - return {}; + case DXGI_OUTDUPL_POINTER_SHAPE_TYPE_COLOR: + // This type doesn't require any XOR-blending + return {}; + case DXGI_OUTDUPL_POINTER_SHAPE_TYPE_MASKED_COLOR: { + util::buffer_t cursor_img = img_data; + std::for_each( + (std::uint32_t *)std::begin(cursor_img), + (std::uint32_t *)std::end(cursor_img), [](auto &pixel) { + auto alpha = (std::uint8_t)((pixel >> 24) & 0xFF); + if (alpha == 0xFF) { + // Pixels with 0xFF alpha will be XOR-blended as is. + } else if (alpha == 0x00) { + // Pixels with 0x00 alpha will be blended by + // make_cursor_alpha_image(). We make them transparent + // for the XOR-blended cursor image. + pixel = transparent; + } else { + // Other alpha values are illegal in masked color + // cursors + BOOST_LOG(warning) + << "Illegal alpha value in masked color cursor: " + << alpha; + } + }); + return cursor_img; + } + case DXGI_OUTDUPL_POINTER_SHAPE_TYPE_MONOCHROME: + // Monochrome is handled below + break; + default: + BOOST_LOG(error) + << "Invalid cursor shape type: " << shape_info.Type; + return {}; } shape_info.Height /= 2; - util::buffer_t cursor_img { shape_info.Width * shape_info.Height * 4 }; + util::buffer_t cursor_img{shape_info.Width * + shape_info.Height * 4}; auto bytes = shape_info.Pitch * shape_info.Height; - auto pixel_begin = (std::uint32_t *) std::begin(cursor_img); + auto pixel_begin = (std::uint32_t *)std::begin(cursor_img); auto pixel_data = pixel_begin; auto and_mask = std::begin(img_data); auto xor_mask = std::begin(img_data) + bytes; for (auto x = 0; x < bytes; ++x) { - for (auto c = 7; c >= 0; --c) { - auto bit = 1 << c; - auto color_type = ((*and_mask & bit) ? 1 : 0) + ((*xor_mask & bit) ? 2 : 0); + for (auto c = 7; c >= 0; --c) { + auto bit = 1 << c; + auto color_type = + ((*and_mask & bit) ? 1 : 0) + ((*xor_mask & bit) ? 2 : 0); - switch (color_type) { - case 0: // Opaque black (handled by alpha-blending) - case 2: // Opaque white (handled by alpha-blending) - case 1: // Color of screen (transparent) - *pixel_data = transparent; - break; - case 3: // Inverse of screen - *pixel_data = inverted; - break; + switch (color_type) { + case 0: // Opaque black (handled by alpha-blending) + case 2: // Opaque white (handled by alpha-blending) + case 1: // Color of screen (transparent) + *pixel_data = transparent; + break; + case 3: // Inverse of screen + *pixel_data = inverted; + break; + } + + ++pixel_data; } - - ++pixel_data; - } - ++and_mask; - ++xor_mask; + ++and_mask; + ++xor_mask; } return cursor_img; - } +} - util::buffer_t - make_cursor_alpha_image(const util::buffer_t &img_data, DXGI_OUTDUPL_POINTER_SHAPE_INFO shape_info) { +util::buffer_t make_cursor_alpha_image( + const util::buffer_t &img_data, + DXGI_OUTDUPL_POINTER_SHAPE_INFO shape_info) { constexpr std::uint32_t black = 0xFF000000; constexpr std::uint32_t white = 0xFFFFFFFF; constexpr std::uint32_t transparent = 0; switch (shape_info.Type) { - case DXGI_OUTDUPL_POINTER_SHAPE_TYPE_MASKED_COLOR: { - util::buffer_t cursor_img = img_data; - std::for_each((std::uint32_t *) std::begin(cursor_img), (std::uint32_t *) std::end(cursor_img), [](auto &pixel) { - auto alpha = (std::uint8_t)((pixel >> 24) & 0xFF); - if (alpha == 0xFF) { - // Pixels with 0xFF alpha will be XOR-blended by make_cursor_xor_image(). - // We make them transparent for the alpha-blended cursor image. - pixel = transparent; - } - else if (alpha == 0x00) { - // Pixels with 0x00 alpha will be blended as opaque with the alpha-blended image. - pixel |= 0xFF000000; - } - else { - // Other alpha values are illegal in masked color cursors - BOOST_LOG(warning) << "Illegal alpha value in masked color cursor: " << alpha; - } - }); - return cursor_img; - } - case DXGI_OUTDUPL_POINTER_SHAPE_TYPE_COLOR: - // Color cursors are just an ARGB bitmap which requires no processing. - return img_data; - case DXGI_OUTDUPL_POINTER_SHAPE_TYPE_MONOCHROME: - // Monochrome cursors are handled below. - break; - default: - BOOST_LOG(error) << "Invalid cursor shape type: " << shape_info.Type; - return {}; + case DXGI_OUTDUPL_POINTER_SHAPE_TYPE_MASKED_COLOR: { + util::buffer_t cursor_img = img_data; + std::for_each( + (std::uint32_t *)std::begin(cursor_img), + (std::uint32_t *)std::end(cursor_img), [](auto &pixel) { + auto alpha = (std::uint8_t)((pixel >> 24) & 0xFF); + if (alpha == 0xFF) { + // Pixels with 0xFF alpha will be XOR-blended by + // make_cursor_xor_image(). We make them transparent for + // the alpha-blended cursor image. + pixel = transparent; + } else if (alpha == 0x00) { + // Pixels with 0x00 alpha will be blended as opaque with + // the alpha-blended image. + pixel |= 0xFF000000; + } else { + // Other alpha values are illegal in masked color + // cursors + BOOST_LOG(warning) + << "Illegal alpha value in masked color cursor: " + << alpha; + } + }); + return cursor_img; + } + case DXGI_OUTDUPL_POINTER_SHAPE_TYPE_COLOR: + // Color cursors are just an ARGB bitmap which requires no + // processing. + return img_data; + case DXGI_OUTDUPL_POINTER_SHAPE_TYPE_MONOCHROME: + // Monochrome cursors are handled below. + break; + default: + BOOST_LOG(error) + << "Invalid cursor shape type: " << shape_info.Type; + return {}; } shape_info.Height /= 2; - util::buffer_t cursor_img { shape_info.Width * shape_info.Height * 4 }; + util::buffer_t cursor_img{shape_info.Width * + shape_info.Height * 4}; auto bytes = shape_info.Pitch * shape_info.Height; - auto pixel_begin = (std::uint32_t *) std::begin(cursor_img); + auto pixel_begin = (std::uint32_t *)std::begin(cursor_img); auto pixel_data = pixel_begin; auto and_mask = std::begin(img_data); auto xor_mask = std::begin(img_data) + bytes; for (auto x = 0; x < bytes; ++x) { - for (auto c = 7; c >= 0; --c) { - auto bit = 1 << c; - auto color_type = ((*and_mask & bit) ? 1 : 0) + ((*xor_mask & bit) ? 2 : 0); + for (auto c = 7; c >= 0; --c) { + auto bit = 1 << c; + auto color_type = + ((*and_mask & bit) ? 1 : 0) + ((*xor_mask & bit) ? 2 : 0); - switch (color_type) { - case 0: // Opaque black - *pixel_data = black; - break; - case 2: // Opaque white - *pixel_data = white; - break; - case 3: // Inverse of screen (handled by XOR blending) - case 1: // Color of screen (transparent) - *pixel_data = transparent; - break; + switch (color_type) { + case 0: // Opaque black + *pixel_data = black; + break; + case 2: // Opaque white + *pixel_data = white; + break; + case 3: // Inverse of screen (handled by XOR blending) + case 1: // Color of screen (transparent) + *pixel_data = transparent; + break; + } + + ++pixel_data; } - - ++pixel_data; - } - ++and_mask; - ++xor_mask; + ++and_mask; + ++xor_mask; } return cursor_img; - } +} - blob_t - compile_shader(LPCSTR file, LPCSTR entrypoint, LPCSTR shader_model) { +blob_t compile_shader(LPCSTR file, LPCSTR entrypoint, LPCSTR shader_model) { blob_t::pointer msg_p = nullptr; blob_t::pointer compiled_p; @@ -345,380 +349,458 @@ namespace platf::dxgi { std::wstring_convert, wchar_t> converter; auto wFile = converter.from_bytes(file); - auto status = D3DCompileFromFile(wFile.c_str(), nullptr, D3D_COMPILE_STANDARD_FILE_INCLUDE, entrypoint, shader_model, flags, 0, &compiled_p, &msg_p); + auto status = D3DCompileFromFile( + wFile.c_str(), nullptr, D3D_COMPILE_STANDARD_FILE_INCLUDE, entrypoint, + shader_model, flags, 0, &compiled_p, &msg_p); if (msg_p) { - BOOST_LOG(warning) << std::string_view { (const char *) msg_p->GetBufferPointer(), msg_p->GetBufferSize() - 1 }; - msg_p->Release(); + BOOST_LOG(warning) << std::string_view{ + (const char *)msg_p->GetBufferPointer(), + msg_p->GetBufferSize() - 1}; + msg_p->Release(); } if (status) { - BOOST_LOG(error) << "Couldn't compile ["sv << file << "] [0x"sv << util::hex(status).to_string_view() << ']'; - return nullptr; + BOOST_LOG(error) << "Couldn't compile ["sv << file << "] [0x"sv + << util::hex(status).to_string_view() << ']'; + return nullptr; } - return blob_t { compiled_p }; - } + return blob_t{compiled_p}; +} - blob_t - compile_pixel_shader(LPCSTR file) { +blob_t compile_pixel_shader(LPCSTR file) { return compile_shader(file, "main_ps", "ps_5_0"); - } +} - blob_t - compile_vertex_shader(LPCSTR file) { +blob_t compile_vertex_shader(LPCSTR file) { return compile_shader(file, "main_vs", "vs_5_0"); - } +} - class d3d_base_encode_device final { - public: - int - convert(platf::img_t &img_base) { - // Garbage collect mapped capture images whose weak references have expired - for (auto it = img_ctx_map.begin(); it != img_ctx_map.end();) { - if (it->second.img_weak.expired()) { - it = img_ctx_map.erase(it); +class d3d_base_encode_device final { + public: + int convert(platf::img_t &img_base) { + // Garbage collect mapped capture images whose weak references have + // expired + for (auto it = img_ctx_map.begin(); it != img_ctx_map.end();) { + if (it->second.img_weak.expired()) { + it = img_ctx_map.erase(it); + } else { + it++; + } } - else { - it++; + + auto &img = (img_d3d_t &)img_base; + auto &img_ctx = img_ctx_map[img.id]; + + // Open the shared capture texture with our ID3D11Device + if (initialize_image_context(img, img_ctx)) { + return -1; } - } - auto &img = (img_d3d_t &) img_base; - auto &img_ctx = img_ctx_map[img.id]; + // Acquire encoder mutex to synchronize with capture code + auto status = img_ctx.encoder_mutex->AcquireSync(0, INFINITE); + if (status != S_OK) { + BOOST_LOG(error) << "Failed to acquire encoder mutex [0x"sv + << util::hex(status).to_string_view() << ']'; + return -1; + } - // Open the shared capture texture with our ID3D11Device - if (initialize_image_context(img, img_ctx)) { - return -1; - } + device_ctx->OMSetRenderTargets(1, &nv12_Y_rt, nullptr); + device_ctx->VSSetShader(scene_vs.get(), nullptr, 0); + device_ctx->PSSetShader(img.format == DXGI_FORMAT_R16G16B16A16_FLOAT + ? convert_Y_fp16_ps.get() + : convert_Y_ps.get(), + nullptr, 0); + device_ctx->RSSetViewports(1, &outY_view); + device_ctx->PSSetShaderResources(0, 1, &img_ctx.encoder_input_res); + device_ctx->Draw(3, 0); - // Acquire encoder mutex to synchronize with capture code - auto status = img_ctx.encoder_mutex->AcquireSync(0, INFINITE); - if (status != S_OK) { - BOOST_LOG(error) << "Failed to acquire encoder mutex [0x"sv << util::hex(status).to_string_view() << ']'; - return -1; - } + device_ctx->OMSetRenderTargets(1, &nv12_UV_rt, nullptr); + device_ctx->VSSetShader(convert_UV_vs.get(), nullptr, 0); + device_ctx->PSSetShader(img.format == DXGI_FORMAT_R16G16B16A16_FLOAT + ? convert_UV_fp16_ps.get() + : convert_UV_ps.get(), + nullptr, 0); + device_ctx->RSSetViewports(1, &outUV_view); + device_ctx->Draw(3, 0); - device_ctx->OMSetRenderTargets(1, &nv12_Y_rt, nullptr); - device_ctx->VSSetShader(scene_vs.get(), nullptr, 0); - device_ctx->PSSetShader(img.format == DXGI_FORMAT_R16G16B16A16_FLOAT ? convert_Y_fp16_ps.get() : convert_Y_ps.get(), nullptr, 0); - device_ctx->RSSetViewports(1, &outY_view); - device_ctx->PSSetShaderResources(0, 1, &img_ctx.encoder_input_res); - device_ctx->Draw(3, 0); + // Release encoder mutex to allow capture code to reuse this image + img_ctx.encoder_mutex->ReleaseSync(0); - device_ctx->OMSetRenderTargets(1, &nv12_UV_rt, nullptr); - device_ctx->VSSetShader(convert_UV_vs.get(), nullptr, 0); - device_ctx->PSSetShader(img.format == DXGI_FORMAT_R16G16B16A16_FLOAT ? convert_UV_fp16_ps.get() : convert_UV_ps.get(), nullptr, 0); - device_ctx->RSSetViewports(1, &outUV_view); - device_ctx->Draw(3, 0); + ID3D11ShaderResourceView *emptyShaderResourceView = nullptr; + device_ctx->PSSetShaderResources(0, 1, &emptyShaderResourceView); - // Release encoder mutex to allow capture code to reuse this image - img_ctx.encoder_mutex->ReleaseSync(0); - - ID3D11ShaderResourceView *emptyShaderResourceView = nullptr; - device_ctx->PSSetShaderResources(0, 1, &emptyShaderResourceView); - - return 0; + return 0; } - void - apply_colorspace(const ::video::sunshine_colorspace_t &colorspace) { - auto color_vectors = ::video::color_vectors_from_colorspace(colorspace); + void apply_colorspace(const ::video::sunshine_colorspace_t &colorspace) { + auto color_vectors = ::video::color_vectors_from_colorspace(colorspace); - if (!color_vectors) { - BOOST_LOG(error) << "No vector data for colorspace"sv; - return; - } + if (!color_vectors) { + BOOST_LOG(error) << "No vector data for colorspace"sv; + return; + } - auto color_matrix = make_buffer(device.get(), *color_vectors); - if (!color_matrix) { - BOOST_LOG(warning) << "Failed to create color matrix"sv; - return; - } + auto color_matrix = make_buffer(device.get(), *color_vectors); + if (!color_matrix) { + BOOST_LOG(warning) << "Failed to create color matrix"sv; + return; + } - device_ctx->PSSetConstantBuffers(0, 1, &color_matrix); - this->color_matrix = std::move(color_matrix); + device_ctx->PSSetConstantBuffers(0, 1, &color_matrix); + this->color_matrix = std::move(color_matrix); } - int - init_output(ID3D11Texture2D *frame_texture, int width, int height) { - // The underlying frame pool owns the texture, so we must reference it for ourselves - frame_texture->AddRef(); - output_texture.reset(frame_texture); + int init_output(ID3D11Texture2D *frame_texture, int width, int height) { + // The underlying frame pool owns the texture, so we must reference it + // for ourselves + frame_texture->AddRef(); + output_texture.reset(frame_texture); - auto out_width = width; - auto out_height = height; + auto out_width = width; + auto out_height = height; - float in_width = display->width; - float in_height = display->height; + float in_width = display->width; + float in_height = display->height; - // Ensure aspect ratio is maintained - auto scalar = std::fminf(out_width / in_width, out_height / in_height); - auto out_width_f = in_width * scalar; - auto out_height_f = in_height * scalar; + // Ensure aspect ratio is maintained + auto scalar = std::fminf(out_width / in_width, out_height / in_height); + auto out_width_f = in_width * scalar; + auto out_height_f = in_height * scalar; - // result is always positive - auto offsetX = (out_width - out_width_f) / 2; - auto offsetY = (out_height - out_height_f) / 2; + // result is always positive + auto offsetX = (out_width - out_width_f) / 2; + auto offsetY = (out_height - out_height_f) / 2; - outY_view = D3D11_VIEWPORT { offsetX, offsetY, out_width_f, out_height_f, 0.0f, 1.0f }; - outUV_view = D3D11_VIEWPORT { offsetX / 2, offsetY / 2, out_width_f / 2, out_height_f / 2, 0.0f, 1.0f }; + outY_view = D3D11_VIEWPORT{offsetX, offsetY, out_width_f, + out_height_f, 0.0f, 1.0f}; + outUV_view = + D3D11_VIEWPORT{offsetX / 2, offsetY / 2, out_width_f / 2, + out_height_f / 2, 0.0f, 1.0f}; - float subsample_offset_in[16 / sizeof(float)] { 1.0f / (float) out_width_f, 1.0f / (float) out_height_f }; // aligned to 16-byte - subsample_offset = make_buffer(device.get(), subsample_offset_in); + float subsample_offset_in[16 / sizeof(float)]{ + 1.0f / (float)out_width_f, + 1.0f / (float)out_height_f}; // aligned to 16-byte + subsample_offset = make_buffer(device.get(), subsample_offset_in); - if (!subsample_offset) { - BOOST_LOG(error) << "Failed to create subsample offset vertex constant buffer"; - return -1; - } - device_ctx->VSSetConstantBuffers(0, 1, &subsample_offset); - - { - int32_t rotation_modifier = display->display_rotation == DXGI_MODE_ROTATION_UNSPECIFIED ? 0 : display->display_rotation - 1; - int32_t rotation_data[16 / sizeof(int32_t)] { -rotation_modifier }; // aligned to 16-byte - auto rotation = make_buffer(device.get(), rotation_data); - if (!rotation) { - BOOST_LOG(error) << "Failed to create display rotation vertex constant buffer"; - return -1; + if (!subsample_offset) { + BOOST_LOG(error) + << "Failed to create subsample offset vertex constant buffer"; + return -1; } - device_ctx->VSSetConstantBuffers(1, 1, &rotation); - } + device_ctx->VSSetConstantBuffers(0, 1, &subsample_offset); - D3D11_RENDER_TARGET_VIEW_DESC nv12_rt_desc { - format == DXGI_FORMAT_P010 ? DXGI_FORMAT_R16_UNORM : DXGI_FORMAT_R8_UNORM, - D3D11_RTV_DIMENSION_TEXTURE2D - }; + { + int32_t rotation_modifier = + display->display_rotation == DXGI_MODE_ROTATION_UNSPECIFIED + ? 0 + : display->display_rotation - 1; + int32_t rotation_data[16 / sizeof(int32_t)]{ + -rotation_modifier}; // aligned to 16-byte + auto rotation = make_buffer(device.get(), rotation_data); + if (!rotation) { + BOOST_LOG(error) << "Failed to create display rotation vertex " + "constant buffer"; + return -1; + } + device_ctx->VSSetConstantBuffers(1, 1, &rotation); + } - auto status = device->CreateRenderTargetView(output_texture.get(), &nv12_rt_desc, &nv12_Y_rt); - if (FAILED(status)) { - BOOST_LOG(error) << "Failed to create render target view [0x"sv << util::hex(status).to_string_view() << ']'; - return -1; - } + D3D11_RENDER_TARGET_VIEW_DESC nv12_rt_desc{ + format == DXGI_FORMAT_P010 ? DXGI_FORMAT_R16_UNORM + : DXGI_FORMAT_R8_UNORM, + D3D11_RTV_DIMENSION_TEXTURE2D}; - nv12_rt_desc.Format = (format == DXGI_FORMAT_P010) ? DXGI_FORMAT_R16G16_UNORM : DXGI_FORMAT_R8G8_UNORM; + auto status = device->CreateRenderTargetView(output_texture.get(), + &nv12_rt_desc, &nv12_Y_rt); + if (FAILED(status)) { + BOOST_LOG(error) << "Failed to create render target view [0x"sv + << util::hex(status).to_string_view() << ']'; + return -1; + } - status = device->CreateRenderTargetView(output_texture.get(), &nv12_rt_desc, &nv12_UV_rt); - if (FAILED(status)) { - BOOST_LOG(error) << "Failed to create render target view [0x"sv << util::hex(status).to_string_view() << ']'; - return -1; - } + nv12_rt_desc.Format = (format == DXGI_FORMAT_P010) + ? DXGI_FORMAT_R16G16_UNORM + : DXGI_FORMAT_R8G8_UNORM; - // Clear the RTVs to ensure the aspect ratio padding is black - const float y_black[] = { 0.0f, 0.0f, 0.0f, 0.0f }; - device_ctx->ClearRenderTargetView(nv12_Y_rt.get(), y_black); - const float uv_black[] = { 0.5f, 0.5f, 0.5f, 0.5f }; - device_ctx->ClearRenderTargetView(nv12_UV_rt.get(), uv_black); + status = device->CreateRenderTargetView(output_texture.get(), + &nv12_rt_desc, &nv12_UV_rt); + if (FAILED(status)) { + BOOST_LOG(error) << "Failed to create render target view [0x"sv + << util::hex(status).to_string_view() << ']'; + return -1; + } - return 0; + // Clear the RTVs to ensure the aspect ratio padding is black + const float y_black[] = {0.0f, 0.0f, 0.0f, 0.0f}; + device_ctx->ClearRenderTargetView(nv12_Y_rt.get(), y_black); + const float uv_black[] = {0.5f, 0.5f, 0.5f, 0.5f}; + device_ctx->ClearRenderTargetView(nv12_UV_rt.get(), uv_black); + + return 0; } - int - init(std::shared_ptr display, adapter_t::pointer adapter_p, pix_fmt_e pix_fmt) { - D3D_FEATURE_LEVEL featureLevels[] { - D3D_FEATURE_LEVEL_11_1, - D3D_FEATURE_LEVEL_11_0, - D3D_FEATURE_LEVEL_10_1, - D3D_FEATURE_LEVEL_10_0, - D3D_FEATURE_LEVEL_9_3, - D3D_FEATURE_LEVEL_9_2, - D3D_FEATURE_LEVEL_9_1 - }; + int init(std::shared_ptr display, + adapter_t::pointer adapter_p, pix_fmt_e pix_fmt) { + D3D_FEATURE_LEVEL featureLevels[]{ + D3D_FEATURE_LEVEL_11_1, D3D_FEATURE_LEVEL_11_0, + D3D_FEATURE_LEVEL_10_1, D3D_FEATURE_LEVEL_10_0, + D3D_FEATURE_LEVEL_9_3, D3D_FEATURE_LEVEL_9_2, + D3D_FEATURE_LEVEL_9_1}; - HRESULT status = D3D11CreateDevice( - adapter_p, - D3D_DRIVER_TYPE_UNKNOWN, - nullptr, - D3D11_CREATE_DEVICE_FLAGS, - featureLevels, sizeof(featureLevels) / sizeof(D3D_FEATURE_LEVEL), - D3D11_SDK_VERSION, - &device, - nullptr, - &device_ctx); + HRESULT status = + D3D11CreateDevice(adapter_p, D3D_DRIVER_TYPE_UNKNOWN, nullptr, + D3D11_CREATE_DEVICE_FLAGS, featureLevels, + sizeof(featureLevels) / sizeof(D3D_FEATURE_LEVEL), + D3D11_SDK_VERSION, &device, nullptr, &device_ctx); - if (FAILED(status)) { - BOOST_LOG(error) << "Failed to create encoder D3D11 device [0x"sv << util::hex(status).to_string_view() << ']'; - return -1; - } - - dxgi::dxgi_t dxgi; - status = device->QueryInterface(IID_IDXGIDevice, (void **) &dxgi); - if (FAILED(status)) { - BOOST_LOG(warning) << "Failed to query DXGI interface from device [0x"sv << util::hex(status).to_string_view() << ']'; - return -1; - } - - status = dxgi->SetGPUThreadPriority(7); - if (FAILED(status)) { - BOOST_LOG(warning) << "Failed to increase encoding GPU thread priority. Please run application as administrator for optimal performance."; - } - - format = (pix_fmt == pix_fmt_e::nv12 ? DXGI_FORMAT_NV12 : DXGI_FORMAT_P010); - status = device->CreateVertexShader(convert_yuv420_planar_y_vs_hlsl->GetBufferPointer(), convert_yuv420_planar_y_vs_hlsl->GetBufferSize(), nullptr, &scene_vs); - if (status) { - BOOST_LOG(error) << "Failed to create scene vertex shader [0x"sv << util::hex(status).to_string_view() << ']'; - return -1; - } - - status = device->CreateVertexShader(convert_yuv420_packed_uv_type0_vs_hlsl->GetBufferPointer(), convert_yuv420_packed_uv_type0_vs_hlsl->GetBufferSize(), nullptr, &convert_UV_vs); - if (status) { - BOOST_LOG(error) << "Failed to create convertUV vertex shader [0x"sv << util::hex(status).to_string_view() << ']'; - return -1; - } - - // If the display is in HDR and we're streaming HDR, we'll be converting scRGB to SMPTE 2084 PQ. - if (format == DXGI_FORMAT_P010 && display->is_hdr()) { - status = device->CreatePixelShader(convert_yuv420_planar_y_ps_perceptual_quantizer_hlsl->GetBufferPointer(), convert_yuv420_planar_y_ps_perceptual_quantizer_hlsl->GetBufferSize(), nullptr, &convert_Y_fp16_ps); - if (status) { - BOOST_LOG(error) << "Failed to create convertY pixel shader [0x"sv << util::hex(status).to_string_view() << ']'; - return -1; + if (FAILED(status)) { + BOOST_LOG(error) << "Failed to create encoder D3D11 device [0x"sv + << util::hex(status).to_string_view() << ']'; + return -1; } - status = device->CreatePixelShader(convert_yuv420_packed_uv_type0_ps_perceptual_quantizer_hlsl->GetBufferPointer(), convert_yuv420_packed_uv_type0_ps_perceptual_quantizer_hlsl->GetBufferSize(), nullptr, &convert_UV_fp16_ps); - if (status) { - BOOST_LOG(error) << "Failed to create convertUV pixel shader [0x"sv << util::hex(status).to_string_view() << ']'; - return -1; - } - } - else { - // If the display is in Advanced Color mode, the desktop format will be scRGB FP16. - // scRGB uses linear gamma, so we must use our linear to sRGB conversion shaders. - status = device->CreatePixelShader(convert_yuv420_planar_y_ps_linear_hlsl->GetBufferPointer(), convert_yuv420_planar_y_ps_linear_hlsl->GetBufferSize(), nullptr, &convert_Y_fp16_ps); - if (status) { - BOOST_LOG(error) << "Failed to create convertY pixel shader [0x"sv << util::hex(status).to_string_view() << ']'; - return -1; + dxgi::dxgi_t dxgi; + status = device->QueryInterface(IID_IDXGIDevice, (void **)&dxgi); + if (FAILED(status)) { + BOOST_LOG(warning) + << "Failed to query DXGI interface from device [0x"sv + << util::hex(status).to_string_view() << ']'; + return -1; } - status = device->CreatePixelShader(convert_yuv420_packed_uv_type0_ps_linear_hlsl->GetBufferPointer(), convert_yuv420_packed_uv_type0_ps_linear_hlsl->GetBufferSize(), nullptr, &convert_UV_fp16_ps); - if (status) { - BOOST_LOG(error) << "Failed to create convertUV pixel shader [0x"sv << util::hex(status).to_string_view() << ']'; - return -1; + status = dxgi->SetGPUThreadPriority(7); + if (FAILED(status)) { + BOOST_LOG(warning) + << "Failed to increase encoding GPU thread priority. Please " + "run application as administrator for optimal performance."; } - } - // These shaders consume standard 8-bit sRGB input - status = device->CreatePixelShader(convert_yuv420_planar_y_ps_hlsl->GetBufferPointer(), convert_yuv420_planar_y_ps_hlsl->GetBufferSize(), nullptr, &convert_Y_ps); - if (status) { - BOOST_LOG(error) << "Failed to create convertY pixel shader [0x"sv << util::hex(status).to_string_view() << ']'; - return -1; - } + format = + (pix_fmt == pix_fmt_e::nv12 ? DXGI_FORMAT_NV12 : DXGI_FORMAT_P010); + status = device->CreateVertexShader( + convert_yuv420_planar_y_vs_hlsl->GetBufferPointer(), + convert_yuv420_planar_y_vs_hlsl->GetBufferSize(), nullptr, + &scene_vs); + if (status) { + BOOST_LOG(error) << "Failed to create scene vertex shader [0x"sv + << util::hex(status).to_string_view() << ']'; + return -1; + } - status = device->CreatePixelShader(convert_yuv420_packed_uv_type0_ps_hlsl->GetBufferPointer(), convert_yuv420_packed_uv_type0_ps_hlsl->GetBufferSize(), nullptr, &convert_UV_ps); - if (status) { - BOOST_LOG(error) << "Failed to create convertUV pixel shader [0x"sv << util::hex(status).to_string_view() << ']'; - return -1; - } + status = device->CreateVertexShader( + convert_yuv420_packed_uv_type0_vs_hlsl->GetBufferPointer(), + convert_yuv420_packed_uv_type0_vs_hlsl->GetBufferSize(), nullptr, + &convert_UV_vs); + if (status) { + BOOST_LOG(error) << "Failed to create convertUV vertex shader [0x"sv + << util::hex(status).to_string_view() << ']'; + return -1; + } - auto default_color_vectors = ::video::color_vectors_from_colorspace(::video::colorspace_e::rec601, false); - if (!default_color_vectors) { - BOOST_LOG(error) << "Missing color vectors for Rec. 601"sv; - return -1; - } + // If the display is in HDR and we're streaming HDR, we'll be converting + // scRGB to SMPTE 2084 PQ. + if (format == DXGI_FORMAT_P010 && display->is_hdr()) { + status = device->CreatePixelShader( + convert_yuv420_planar_y_ps_perceptual_quantizer_hlsl + ->GetBufferPointer(), + convert_yuv420_planar_y_ps_perceptual_quantizer_hlsl + ->GetBufferSize(), + nullptr, &convert_Y_fp16_ps); + if (status) { + BOOST_LOG(error) + << "Failed to create convertY pixel shader [0x"sv + << util::hex(status).to_string_view() << ']'; + return -1; + } - color_matrix = make_buffer(device.get(), *default_color_vectors); - if (!color_matrix) { - BOOST_LOG(error) << "Failed to create color matrix buffer"sv; - return -1; - } - device_ctx->PSSetConstantBuffers(0, 1, &color_matrix); + status = device->CreatePixelShader( + convert_yuv420_packed_uv_type0_ps_perceptual_quantizer_hlsl + ->GetBufferPointer(), + convert_yuv420_packed_uv_type0_ps_perceptual_quantizer_hlsl + ->GetBufferSize(), + nullptr, &convert_UV_fp16_ps); + if (status) { + BOOST_LOG(error) + << "Failed to create convertUV pixel shader [0x"sv + << util::hex(status).to_string_view() << ']'; + return -1; + } + } else { + // If the display is in Advanced Color mode, the desktop format will + // be scRGB FP16. scRGB uses linear gamma, so we must use our linear + // to sRGB conversion shaders. + status = device->CreatePixelShader( + convert_yuv420_planar_y_ps_linear_hlsl->GetBufferPointer(), + convert_yuv420_planar_y_ps_linear_hlsl->GetBufferSize(), + nullptr, &convert_Y_fp16_ps); + if (status) { + BOOST_LOG(error) + << "Failed to create convertY pixel shader [0x"sv + << util::hex(status).to_string_view() << ']'; + return -1; + } - this->display = std::dynamic_pointer_cast(display); - if (!this->display) { - return -1; - } - display = nullptr; + status = device->CreatePixelShader( + convert_yuv420_packed_uv_type0_ps_linear_hlsl + ->GetBufferPointer(), + convert_yuv420_packed_uv_type0_ps_linear_hlsl->GetBufferSize(), + nullptr, &convert_UV_fp16_ps); + if (status) { + BOOST_LOG(error) + << "Failed to create convertUV pixel shader [0x"sv + << util::hex(status).to_string_view() << ']'; + return -1; + } + } - blend_disable = make_blend(device.get(), false, false); - if (!blend_disable) { - return -1; - } + // These shaders consume standard 8-bit sRGB input + status = device->CreatePixelShader( + convert_yuv420_planar_y_ps_hlsl->GetBufferPointer(), + convert_yuv420_planar_y_ps_hlsl->GetBufferSize(), nullptr, + &convert_Y_ps); + if (status) { + BOOST_LOG(error) << "Failed to create convertY pixel shader [0x"sv + << util::hex(status).to_string_view() << ']'; + return -1; + } - D3D11_SAMPLER_DESC sampler_desc {}; - sampler_desc.Filter = D3D11_FILTER_MIN_MAG_MIP_LINEAR; - sampler_desc.AddressU = D3D11_TEXTURE_ADDRESS_CLAMP; - sampler_desc.AddressV = D3D11_TEXTURE_ADDRESS_CLAMP; - sampler_desc.AddressW = D3D11_TEXTURE_ADDRESS_WRAP; - sampler_desc.ComparisonFunc = D3D11_COMPARISON_NEVER; - sampler_desc.MinLOD = 0; - sampler_desc.MaxLOD = D3D11_FLOAT32_MAX; + status = device->CreatePixelShader( + convert_yuv420_packed_uv_type0_ps_hlsl->GetBufferPointer(), + convert_yuv420_packed_uv_type0_ps_hlsl->GetBufferSize(), nullptr, + &convert_UV_ps); + if (status) { + BOOST_LOG(error) << "Failed to create convertUV pixel shader [0x"sv + << util::hex(status).to_string_view() << ']'; + return -1; + } - status = device->CreateSamplerState(&sampler_desc, &sampler_linear); - if (FAILED(status)) { - BOOST_LOG(error) << "Failed to create point sampler state [0x"sv << util::hex(status).to_string_view() << ']'; - return -1; - } + auto default_color_vectors = ::video::color_vectors_from_colorspace( + ::video::colorspace_e::rec601, false); + if (!default_color_vectors) { + BOOST_LOG(error) << "Missing color vectors for Rec. 601"sv; + return -1; + } - device_ctx->OMSetBlendState(blend_disable.get(), nullptr, 0xFFFFFFFFu); - device_ctx->PSSetSamplers(0, 1, &sampler_linear); - device_ctx->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP); + color_matrix = make_buffer(device.get(), *default_color_vectors); + if (!color_matrix) { + BOOST_LOG(error) << "Failed to create color matrix buffer"sv; + return -1; + } + device_ctx->PSSetConstantBuffers(0, 1, &color_matrix); - return 0; + this->display = std::dynamic_pointer_cast(display); + if (!this->display) { + return -1; + } + display = nullptr; + + blend_disable = make_blend(device.get(), false, false); + if (!blend_disable) { + return -1; + } + + D3D11_SAMPLER_DESC sampler_desc{}; + sampler_desc.Filter = D3D11_FILTER_MIN_MAG_MIP_LINEAR; + sampler_desc.AddressU = D3D11_TEXTURE_ADDRESS_CLAMP; + sampler_desc.AddressV = D3D11_TEXTURE_ADDRESS_CLAMP; + sampler_desc.AddressW = D3D11_TEXTURE_ADDRESS_WRAP; + sampler_desc.ComparisonFunc = D3D11_COMPARISON_NEVER; + sampler_desc.MinLOD = 0; + sampler_desc.MaxLOD = D3D11_FLOAT32_MAX; + + status = device->CreateSamplerState(&sampler_desc, &sampler_linear); + if (FAILED(status)) { + BOOST_LOG(error) << "Failed to create point sampler state [0x"sv + << util::hex(status).to_string_view() << ']'; + return -1; + } + + device_ctx->OMSetBlendState(blend_disable.get(), nullptr, 0xFFFFFFFFu); + device_ctx->PSSetSamplers(0, 1, &sampler_linear); + device_ctx->IASetPrimitiveTopology( + D3D11_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP); + + return 0; } struct encoder_img_ctx_t { - // Used to determine if the underlying texture changes. - // Not safe for actual use by the encoder! - texture2d_t::const_pointer capture_texture_p; + // Used to determine if the underlying texture changes. + // Not safe for actual use by the encoder! + texture2d_t::const_pointer capture_texture_p; - texture2d_t encoder_texture; - shader_res_t encoder_input_res; - keyed_mutex_t encoder_mutex; + texture2d_t encoder_texture; + shader_res_t encoder_input_res; + keyed_mutex_t encoder_mutex; - std::weak_ptr img_weak; + std::weak_ptr img_weak; - void - reset() { - capture_texture_p = nullptr; - encoder_texture.reset(); - encoder_input_res.reset(); - encoder_mutex.reset(); - img_weak.reset(); - } + void reset() { + capture_texture_p = nullptr; + encoder_texture.reset(); + encoder_input_res.reset(); + encoder_mutex.reset(); + img_weak.reset(); + } }; - int - initialize_image_context(const img_d3d_t &img, encoder_img_ctx_t &img_ctx) { - // If we've already opened the shared texture, we're done - if (img_ctx.encoder_texture && img.capture_texture.get() == img_ctx.capture_texture_p) { + int initialize_image_context(const img_d3d_t &img, + encoder_img_ctx_t &img_ctx) { + // If we've already opened the shared texture, we're done + if (img_ctx.encoder_texture && + img.capture_texture.get() == img_ctx.capture_texture_p) { + return 0; + } + + // Reset this image context in case it was used before with a different + // texture. Textures can change when transitioning from a dummy image to + // a real image. + img_ctx.reset(); + + device1_t device1; + auto status = + device->QueryInterface(__uuidof(ID3D11Device1), (void **)&device1); + if (FAILED(status)) { + BOOST_LOG(error) << "Failed to query ID3D11Device1 [0x"sv + << util::hex(status).to_string_view() << ']'; + return -1; + } + + // Open a handle to the shared texture + status = device1->OpenSharedResource1( + img.encoder_texture_handle, __uuidof(ID3D11Texture2D), + (void **)&img_ctx.encoder_texture); + if (FAILED(status)) { + BOOST_LOG(error) << "Failed to open shared image texture [0x"sv + << util::hex(status).to_string_view() << ']'; + return -1; + } + + // Get the keyed mutex to synchronize with the capture code + status = img_ctx.encoder_texture->QueryInterface( + __uuidof(IDXGIKeyedMutex), (void **)&img_ctx.encoder_mutex); + if (FAILED(status)) { + BOOST_LOG(error) << "Failed to query IDXGIKeyedMutex [0x"sv + << util::hex(status).to_string_view() << ']'; + return -1; + } + + // Create the SRV for the encoder texture + status = device->CreateShaderResourceView( + img_ctx.encoder_texture.get(), nullptr, &img_ctx.encoder_input_res); + if (FAILED(status)) { + BOOST_LOG(error) + << "Failed to create shader resource view for encoding [0x"sv + << util::hex(status).to_string_view() << ']'; + return -1; + } + + img_ctx.capture_texture_p = img.capture_texture.get(); + + img_ctx.img_weak = img.weak_from_this(); + return 0; - } - - // Reset this image context in case it was used before with a different texture. - // Textures can change when transitioning from a dummy image to a real image. - img_ctx.reset(); - - device1_t device1; - auto status = device->QueryInterface(__uuidof(ID3D11Device1), (void **) &device1); - if (FAILED(status)) { - BOOST_LOG(error) << "Failed to query ID3D11Device1 [0x"sv << util::hex(status).to_string_view() << ']'; - return -1; - } - - // Open a handle to the shared texture - status = device1->OpenSharedResource1(img.encoder_texture_handle, __uuidof(ID3D11Texture2D), (void **) &img_ctx.encoder_texture); - if (FAILED(status)) { - BOOST_LOG(error) << "Failed to open shared image texture [0x"sv << util::hex(status).to_string_view() << ']'; - return -1; - } - - // Get the keyed mutex to synchronize with the capture code - status = img_ctx.encoder_texture->QueryInterface(__uuidof(IDXGIKeyedMutex), (void **) &img_ctx.encoder_mutex); - if (FAILED(status)) { - BOOST_LOG(error) << "Failed to query IDXGIKeyedMutex [0x"sv << util::hex(status).to_string_view() << ']'; - return -1; - } - - // Create the SRV for the encoder texture - status = device->CreateShaderResourceView(img_ctx.encoder_texture.get(), nullptr, &img_ctx.encoder_input_res); - if (FAILED(status)) { - BOOST_LOG(error) << "Failed to create shader resource view for encoding [0x"sv << util::hex(status).to_string_view() << ']'; - return -1; - } - - img_ctx.capture_texture_p = img.capture_texture.get(); - - img_ctx.img_weak = img.weak_from_this(); - - return 0; } ::video::color_t *color_p; @@ -735,7 +817,8 @@ namespace platf::dxgi { // d3d_img_t::id -> encoder_img_ctx_t // These store the encoder textures for each img_t that passes through // convert(). We can't store them in the img_t itself because it is shared - // amongst multiple hwdevice_t objects (and therefore multiple ID3D11Devices). + // amongst multiple hwdevice_t objects (and therefore multiple + // ID3D11Devices). std::map img_ctx_map; std::shared_ptr display; @@ -756,162 +839,167 @@ namespace platf::dxgi { device_ctx_t device_ctx; texture2d_t output_texture; - }; +}; - class d3d_avcodec_encode_device_t: public avcodec_encode_device_t { - public: - int - init(std::shared_ptr display, adapter_t::pointer adapter_p, pix_fmt_e pix_fmt) { - int result = base.init(display, adapter_p, pix_fmt); - data = base.device.get(); - return result; +class d3d_avcodec_encode_device_t : public avcodec_encode_device_t { + public: + int init(std::shared_ptr display, + adapter_t::pointer adapter_p, pix_fmt_e pix_fmt) { + int result = base.init(display, adapter_p, pix_fmt); + data = base.device.get(); + return result; } - int - convert(platf::img_t &img_base) override { - return base.convert(img_base); + int convert(platf::img_t &img_base) override { + return base.convert(img_base); } - void - apply_colorspace() override { - base.apply_colorspace(colorspace); - } + void apply_colorspace() override { base.apply_colorspace(colorspace); } - void - init_hwframes(AVHWFramesContext *frames) override { - // We may be called with a QSV or D3D11VA context - if (frames->device_ctx->type == AV_HWDEVICE_TYPE_D3D11VA) { - auto d3d11_frames = (AVD3D11VAFramesContext *) frames->hwctx; + void init_hwframes(AVHWFramesContext *frames) override { + // We may be called with a QSV or D3D11VA context + if (frames->device_ctx->type == AV_HWDEVICE_TYPE_D3D11VA) { + auto d3d11_frames = (AVD3D11VAFramesContext *)frames->hwctx; - // The encoder requires textures with D3D11_BIND_RENDER_TARGET set - d3d11_frames->BindFlags = D3D11_BIND_RENDER_TARGET; - d3d11_frames->MiscFlags = 0; - } - - // We require a single texture - frames->initial_pool_size = 1; - } - - int - prepare_to_derive_context(int hw_device_type) override { - // QuickSync requires our device to be multithread-protected - if (hw_device_type == AV_HWDEVICE_TYPE_QSV) { - multithread_t mt; - - auto status = base.device->QueryInterface(IID_ID3D11Multithread, (void **) &mt); - if (FAILED(status)) { - BOOST_LOG(warning) << "Failed to query ID3D11Multithread interface from device [0x"sv << util::hex(status).to_string_view() << ']'; - return -1; + // The encoder requires textures with D3D11_BIND_RENDER_TARGET set + d3d11_frames->BindFlags = D3D11_BIND_RENDER_TARGET; + d3d11_frames->MiscFlags = 0; } - mt->SetMultithreadProtected(TRUE); - } - - return 0; + // We require a single texture + frames->initial_pool_size = 1; } - int - set_frame(AVFrame *frame, AVBufferRef *hw_frames_ctx) override { - this->hwframe.reset(frame); - this->frame = frame; + int prepare_to_derive_context(int hw_device_type) override { + // QuickSync requires our device to be multithread-protected + if (hw_device_type == AV_HWDEVICE_TYPE_QSV) { + multithread_t mt; - // Populate this frame with a hardware buffer if one isn't there already - if (!frame->buf[0]) { - auto err = av_hwframe_get_buffer(hw_frames_ctx, frame, 0); - if (err) { - char err_str[AV_ERROR_MAX_STRING_SIZE] { 0 }; - BOOST_LOG(error) << "Failed to get hwframe buffer: "sv << av_make_error_string(err_str, AV_ERROR_MAX_STRING_SIZE, err); - return -1; - } - } + auto status = base.device->QueryInterface(IID_ID3D11Multithread, + (void **)&mt); + if (FAILED(status)) { + BOOST_LOG(warning) + << "Failed to query ID3D11Multithread interface from device [0x"sv + << util::hex(status).to_string_view() << ']'; + return -1; + } - // If this is a frame from a derived context, we'll need to map it to D3D11 - ID3D11Texture2D *frame_texture; - if (frame->format != AV_PIX_FMT_D3D11) { - frame_t d3d11_frame { av_frame_alloc() }; - - d3d11_frame->format = AV_PIX_FMT_D3D11; - - auto err = av_hwframe_map(d3d11_frame.get(), frame, AV_HWFRAME_MAP_WRITE | AV_HWFRAME_MAP_OVERWRITE); - if (err) { - char err_str[AV_ERROR_MAX_STRING_SIZE] { 0 }; - BOOST_LOG(error) << "Failed to map D3D11 frame: "sv << av_make_error_string(err_str, AV_ERROR_MAX_STRING_SIZE, err); - return -1; + mt->SetMultithreadProtected(TRUE); } - // Get the texture from the mapped frame - frame_texture = (ID3D11Texture2D *) d3d11_frame->data[0]; - } - else { - // Otherwise, we can just use the texture inside the original frame - frame_texture = (ID3D11Texture2D *) frame->data[0]; - } - - return base.init_output(frame_texture, frame->width, frame->height); + return 0; } - private: + int set_frame(AVFrame *frame, AVBufferRef *hw_frames_ctx) override { + this->hwframe.reset(frame); + this->frame = frame; + + // Populate this frame with a hardware buffer if one isn't there already + if (!frame->buf[0]) { + auto err = av_hwframe_get_buffer(hw_frames_ctx, frame, 0); + if (err) { + char err_str[AV_ERROR_MAX_STRING_SIZE]{0}; + BOOST_LOG(error) << "Failed to get hwframe buffer: "sv + << av_make_error_string( + err_str, AV_ERROR_MAX_STRING_SIZE, err); + return -1; + } + } + + // If this is a frame from a derived context, we'll need to map it to + // D3D11 + ID3D11Texture2D *frame_texture; + if (frame->format != AV_PIX_FMT_D3D11) { + frame_t d3d11_frame{av_frame_alloc()}; + + d3d11_frame->format = AV_PIX_FMT_D3D11; + + auto err = + av_hwframe_map(d3d11_frame.get(), frame, + AV_HWFRAME_MAP_WRITE | AV_HWFRAME_MAP_OVERWRITE); + if (err) { + char err_str[AV_ERROR_MAX_STRING_SIZE]{0}; + BOOST_LOG(error) << "Failed to map D3D11 frame: "sv + << av_make_error_string( + err_str, AV_ERROR_MAX_STRING_SIZE, err); + return -1; + } + + // Get the texture from the mapped frame + frame_texture = (ID3D11Texture2D *)d3d11_frame->data[0]; + } else { + // Otherwise, we can just use the texture inside the original frame + frame_texture = (ID3D11Texture2D *)frame->data[0]; + } + + return base.init_output(frame_texture, frame->width, frame->height); + } + + private: d3d_base_encode_device base; frame_t hwframe; - }; +}; - class d3d_nvenc_encode_device_t: public nvenc_encode_device_t { - public: - bool - init_device(std::shared_ptr display, adapter_t::pointer adapter_p, pix_fmt_e pix_fmt) { - buffer_format = nvenc::nvenc_format_from_sunshine_format(pix_fmt); - if (buffer_format == NV_ENC_BUFFER_FORMAT_UNDEFINED) { - BOOST_LOG(error) << "Unexpected pixel format for NvENC ["sv << from_pix_fmt(pix_fmt) << ']'; - return false; - } +class d3d_nvenc_encode_device_t : public nvenc_encode_device_t { + public: + bool init_device(std::shared_ptr display, + adapter_t::pointer adapter_p, pix_fmt_e pix_fmt) { + buffer_format = nvenc::nvenc_format_from_sunshine_format(pix_fmt); + if (buffer_format == NV_ENC_BUFFER_FORMAT_UNDEFINED) { + BOOST_LOG(error) << "Unexpected pixel format for NvENC ["sv + << from_pix_fmt(pix_fmt) << ']'; + return false; + } - if (base.init(display, adapter_p, pix_fmt)) return false; + if (base.init(display, adapter_p, pix_fmt)) return false; - nvenc_d3d = std::make_unique(base.device.get()); - nvenc = nvenc_d3d.get(); + nvenc_d3d = std::make_unique(base.device.get()); + nvenc = nvenc_d3d.get(); - return true; + return true; } - bool - init_encoder(const ::video::config_t &client_config, const ::video::sunshine_colorspace_t &colorspace) override { - if (!nvenc_d3d) return false; + bool init_encoder( + const ::video::config_t &client_config, + const ::video::sunshine_colorspace_t &colorspace) override { + if (!nvenc_d3d) return false; - auto nvenc_colorspace = nvenc::nvenc_colorspace_from_sunshine_colorspace(colorspace); - if (!nvenc_d3d->create_encoder(config::video.nv, client_config, nvenc_colorspace, buffer_format)) return false; + auto nvenc_colorspace = + nvenc::nvenc_colorspace_from_sunshine_colorspace(colorspace); + if (!nvenc_d3d->create_encoder(config::video.nv, client_config, + nvenc_colorspace, buffer_format)) + return false; - base.apply_colorspace(colorspace); - return base.init_output(nvenc_d3d->get_input_texture(), client_config.width, client_config.height) == 0; + base.apply_colorspace(colorspace); + return base.init_output(nvenc_d3d->get_input_texture(), + client_config.width, client_config.height) == 0; } - int - convert(platf::img_t &img_base) override { - return base.convert(img_base); + int convert(platf::img_t &img_base) override { + return base.convert(img_base); } - private: + private: d3d_base_encode_device base; std::unique_ptr nvenc_d3d; NV_ENC_BUFFER_FORMAT buffer_format = NV_ENC_BUFFER_FORMAT_UNDEFINED; - }; +}; - bool - set_cursor_texture(device_t::pointer device, gpu_cursor_t &cursor, util::buffer_t &&cursor_img, DXGI_OUTDUPL_POINTER_SHAPE_INFO &shape_info) { +bool set_cursor_texture(device_t::pointer device, gpu_cursor_t &cursor, + util::buffer_t &&cursor_img, + DXGI_OUTDUPL_POINTER_SHAPE_INFO &shape_info) { // This cursor image may not be used if (cursor_img.size() == 0) { - cursor.input_res.reset(); - cursor.set_texture(0, 0, nullptr); - return true; + cursor.input_res.reset(); + cursor.set_texture(0, 0, nullptr); + return true; } - D3D11_SUBRESOURCE_DATA data { - std::begin(cursor_img), - 4 * shape_info.Width, - 0 - }; + D3D11_SUBRESOURCE_DATA data{std::begin(cursor_img), 4 * shape_info.Width, + 0}; // Create texture for cursor - D3D11_TEXTURE2D_DESC t {}; + D3D11_TEXTURE2D_DESC t{}; t.Width = shape_info.Width; t.Height = cursor_img.size() / data.SysMemPitch; t.MipLevels = 1; @@ -924,412 +1012,474 @@ namespace platf::dxgi { texture2d_t texture; auto status = device->CreateTexture2D(&t, &data, &texture); if (FAILED(status)) { - BOOST_LOG(error) << "Failed to create mouse texture [0x"sv << util::hex(status).to_string_view() << ']'; - return false; + BOOST_LOG(error) << "Failed to create mouse texture [0x"sv + << util::hex(status).to_string_view() << ']'; + return false; } // Free resources before allocating on the next line. cursor.input_res.reset(); - status = device->CreateShaderResourceView(texture.get(), nullptr, &cursor.input_res); + status = device->CreateShaderResourceView(texture.get(), nullptr, + &cursor.input_res); if (FAILED(status)) { - BOOST_LOG(error) << "Failed to create cursor shader resource view [0x"sv << util::hex(status).to_string_view() << ']'; - return false; + BOOST_LOG(error) << "Failed to create cursor shader resource view [0x"sv + << util::hex(status).to_string_view() << ']'; + return false; } cursor.set_texture(t.Width, t.Height, std::move(texture)); return true; - } +} - capture_e - display_vram_t::snapshot(const pull_free_image_cb_t &pull_free_image_cb, std::shared_ptr &img_out, std::chrono::milliseconds timeout, bool cursor_visible) { +capture_e display_vram_t::snapshot( + const pull_free_image_cb_t &pull_free_image_cb, + std::shared_ptr &img_out, std::chrono::milliseconds timeout, + bool cursor_visible) { HRESULT status; DXGI_OUTDUPL_FRAME_INFO frame_info; - resource_t::pointer res_p {}; + resource_t::pointer res_p{}; auto capture_status = dup.next_frame(frame_info, timeout, &res_p); - resource_t res { res_p }; + resource_t res{res_p}; if (capture_status != capture_e::ok) { - return capture_status; + return capture_status; } - const bool mouse_update_flag = frame_info.LastMouseUpdateTime.QuadPart != 0 || frame_info.PointerShapeBufferSize > 0; + const bool mouse_update_flag = + frame_info.LastMouseUpdateTime.QuadPart != 0 || + frame_info.PointerShapeBufferSize > 0; const bool frame_update_flag = frame_info.LastPresentTime.QuadPart != 0; const bool update_flag = mouse_update_flag || frame_update_flag; if (!update_flag) { - return capture_e::timeout; + return capture_e::timeout; } std::optional frame_timestamp; - if (auto qpc_displayed = std::max(frame_info.LastPresentTime.QuadPart, frame_info.LastMouseUpdateTime.QuadPart)) { - // Translate QueryPerformanceCounter() value to steady_clock time point - frame_timestamp = std::chrono::steady_clock::now() - qpc_time_difference(qpc_counter(), qpc_displayed); + if (auto qpc_displayed = + std::max(frame_info.LastPresentTime.QuadPart, + frame_info.LastMouseUpdateTime.QuadPart)) { + // Translate QueryPerformanceCounter() value to steady_clock time point + frame_timestamp = std::chrono::steady_clock::now() - + qpc_time_difference(qpc_counter(), qpc_displayed); } if (frame_info.PointerShapeBufferSize > 0) { - DXGI_OUTDUPL_POINTER_SHAPE_INFO shape_info {}; + DXGI_OUTDUPL_POINTER_SHAPE_INFO shape_info{}; - util::buffer_t img_data { frame_info.PointerShapeBufferSize }; + util::buffer_t img_data{ + frame_info.PointerShapeBufferSize}; - UINT dummy; - status = dup.dup->GetFramePointerShape(img_data.size(), std::begin(img_data), &dummy, &shape_info); - if (FAILED(status)) { - BOOST_LOG(error) << "Failed to get new pointer shape [0x"sv << util::hex(status).to_string_view() << ']'; + UINT dummy; + status = dup.dup->GetFramePointerShape( + img_data.size(), std::begin(img_data), &dummy, &shape_info); + if (FAILED(status)) { + BOOST_LOG(error) << "Failed to get new pointer shape [0x"sv + << util::hex(status).to_string_view() << ']'; - return capture_e::error; - } + return capture_e::error; + } - auto alpha_cursor_img = make_cursor_alpha_image(img_data, shape_info); - auto xor_cursor_img = make_cursor_xor_image(img_data, shape_info); + auto alpha_cursor_img = make_cursor_alpha_image(img_data, shape_info); + auto xor_cursor_img = make_cursor_xor_image(img_data, shape_info); - if (!set_cursor_texture(device.get(), cursor_alpha, std::move(alpha_cursor_img), shape_info) || - !set_cursor_texture(device.get(), cursor_xor, std::move(xor_cursor_img), shape_info)) { - return capture_e::error; - } + if (!set_cursor_texture(device.get(), cursor_alpha, + std::move(alpha_cursor_img), shape_info) || + !set_cursor_texture(device.get(), cursor_xor, + std::move(xor_cursor_img), shape_info)) { + return capture_e::error; + } } if (frame_info.LastMouseUpdateTime.QuadPart) { - cursor_alpha.set_pos(frame_info.PointerPosition.Position.x, frame_info.PointerPosition.Position.y, - width, height, display_rotation, frame_info.PointerPosition.Visible); + cursor_alpha.set_pos(frame_info.PointerPosition.Position.x, + frame_info.PointerPosition.Position.y, width, + height, display_rotation, + frame_info.PointerPosition.Visible); - cursor_xor.set_pos(frame_info.PointerPosition.Position.x, frame_info.PointerPosition.Position.y, - width, height, display_rotation, frame_info.PointerPosition.Visible); + cursor_xor.set_pos(frame_info.PointerPosition.Position.x, + frame_info.PointerPosition.Position.y, width, height, + display_rotation, + frame_info.PointerPosition.Visible); } - const bool blend_mouse_cursor_flag = (cursor_alpha.visible || cursor_xor.visible) && cursor_visible; + const bool blend_mouse_cursor_flag = + (cursor_alpha.visible || cursor_xor.visible) && cursor_visible; - texture2d_t src {}; + texture2d_t src{}; if (frame_update_flag) { - // Get the texture object from this frame - status = res->QueryInterface(IID_ID3D11Texture2D, (void **) &src); - if (FAILED(status)) { - BOOST_LOG(error) << "Couldn't query interface [0x"sv << util::hex(status).to_string_view() << ']'; - return capture_e::error; - } + // Get the texture object from this frame + status = res->QueryInterface(IID_ID3D11Texture2D, (void **)&src); + if (FAILED(status)) { + BOOST_LOG(error) << "Couldn't query interface [0x"sv + << util::hex(status).to_string_view() << ']'; + return capture_e::error; + } - D3D11_TEXTURE2D_DESC desc; - src->GetDesc(&desc); + D3D11_TEXTURE2D_DESC desc; + src->GetDesc(&desc); - // It's possible for our display enumeration to race with mode changes and result in - // mismatched image pool and desktop texture sizes. If this happens, just reinit again. - if (desc.Width != width_before_rotation || desc.Height != height_before_rotation) { - BOOST_LOG(info) << "Capture size changed ["sv << width << 'x' << height << " -> "sv << desc.Width << 'x' << desc.Height << ']'; - return capture_e::reinit; - } + // It's possible for our display enumeration to race with mode changes + // and result in mismatched image pool and desktop texture sizes. If + // this happens, just reinit again. + if (desc.Width != width_before_rotation || + desc.Height != height_before_rotation) { + BOOST_LOG(info) + << "Capture size changed ["sv << width << 'x' << height + << " -> "sv << desc.Width << 'x' << desc.Height << ']'; + return capture_e::reinit; + } - // If we don't know the capture format yet, grab it from this texture - if (capture_format == DXGI_FORMAT_UNKNOWN) { - capture_format = desc.Format; - BOOST_LOG(info) << "Capture format ["sv << dxgi_format_to_string(capture_format) << ']'; - } + // If we don't know the capture format yet, grab it from this texture + if (capture_format == DXGI_FORMAT_UNKNOWN) { + capture_format = desc.Format; + BOOST_LOG(info) << "Capture format ["sv + << dxgi_format_to_string(capture_format) << ']'; + } - // It's also possible for the capture format to change on the fly. If that happens, - // reinitialize capture to try format detection again and create new images. - if (capture_format != desc.Format) { - BOOST_LOG(info) << "Capture format changed ["sv << dxgi_format_to_string(capture_format) << " -> "sv << dxgi_format_to_string(desc.Format) << ']'; - return capture_e::reinit; - } + // It's also possible for the capture format to change on the fly. If + // that happens, reinitialize capture to try format detection again and + // create new images. + if (capture_format != desc.Format) { + BOOST_LOG(info) << "Capture format changed ["sv + << dxgi_format_to_string(capture_format) << " -> "sv + << dxgi_format_to_string(desc.Format) << ']'; + return capture_e::reinit; + } } enum class lfa { - nothing, - replace_surface_with_img, - replace_img_with_surface, - copy_src_to_img, - copy_src_to_surface, + nothing, + replace_surface_with_img, + replace_img_with_surface, + copy_src_to_img, + copy_src_to_surface, }; enum class ofa { - forward_last_img, - copy_last_surface_and_blend_cursor, - dummy_fallback, + forward_last_img, + copy_last_surface_and_blend_cursor, + dummy_fallback, }; auto last_frame_action = lfa::nothing; auto out_frame_action = ofa::dummy_fallback; if (capture_format == DXGI_FORMAT_UNKNOWN) { - // We don't know the final capture format yet, so we will encode a black dummy image - last_frame_action = lfa::nothing; - out_frame_action = ofa::dummy_fallback; - } - else { - if (src) { - // We got a new frame from DesktopDuplication... - if (blend_mouse_cursor_flag) { - // ...and we need to blend the mouse cursor onto it. - // Copy the frame to intermediate surface so we can blend this and future mouse cursor updates - // without new frames from DesktopDuplication. We use direct3d surface directly here and not - // an image from pull_free_image_cb mainly because it's lighter (surface sharing between - // direct3d devices produce significant memory overhead). - last_frame_action = lfa::copy_src_to_surface; - // Copy the intermediate surface to a new image from pull_free_image_cb and blend the mouse cursor onto it. - out_frame_action = ofa::copy_last_surface_and_blend_cursor; + // We don't know the final capture format yet, so we will encode a black + // dummy image + last_frame_action = lfa::nothing; + out_frame_action = ofa::dummy_fallback; + } else { + if (src) { + // We got a new frame from DesktopDuplication... + if (blend_mouse_cursor_flag) { + // ...and we need to blend the mouse cursor onto it. + // Copy the frame to intermediate surface so we can blend this + // and future mouse cursor updates without new frames from + // DesktopDuplication. We use direct3d surface directly here and + // not an image from pull_free_image_cb mainly because it's + // lighter (surface sharing between direct3d devices produce + // significant memory overhead). + last_frame_action = lfa::copy_src_to_surface; + // Copy the intermediate surface to a new image from + // pull_free_image_cb and blend the mouse cursor onto it. + out_frame_action = ofa::copy_last_surface_and_blend_cursor; + } else { + // ...and we don't need to blend the mouse cursor. + // Copy the frame to a new image from pull_free_image_cb and + // save the shared pointer to the image in case the mouse cursor + // appears without a new frame from DesktopDuplication. + last_frame_action = lfa::copy_src_to_img; + // Use saved last image shared pointer as output image evading + // copy. + out_frame_action = ofa::forward_last_img; + } + } else if (!std::holds_alternative( + last_frame_variant)) { + // We didn't get a new frame from DesktopDuplication... + if (blend_mouse_cursor_flag) { + // ...but we need to blend the mouse cursor. + if (std::holds_alternative>( + last_frame_variant)) { + // We have the shared pointer of the last image, replace it + // with intermediate surface while copying contents so we + // can blend this and future mouse cursor updates. + last_frame_action = lfa::replace_img_with_surface; + } + // Copy the intermediate surface which contains last + // DesktopDuplication frame to a new image from + // pull_free_image_cb and blend the mouse cursor onto it. + out_frame_action = ofa::copy_last_surface_and_blend_cursor; + } else { + // ...and we don't need to blend the mouse cursor. + // This happens when the mouse cursor disappears from screen, + // or there's mouse cursor on screen, but its drawing is + // disabled in sunshine. + if (std::holds_alternative(last_frame_variant)) { + // We have the intermediate surface that was used as the + // mouse cursor blending base. Replace it with an image from + // pull_free_image_cb copying contents and freeing up the + // surface memory. Save the shared pointer to the image in + // case the mouse cursor reappears. + last_frame_action = lfa::replace_surface_with_img; + } + // Use saved last image shared pointer as output image evading + // copy. + out_frame_action = ofa::forward_last_img; + } } - else { - // ...and we don't need to blend the mouse cursor. - // Copy the frame to a new image from pull_free_image_cb and save the shared pointer to the image - // in case the mouse cursor appears without a new frame from DesktopDuplication. - last_frame_action = lfa::copy_src_to_img; - // Use saved last image shared pointer as output image evading copy. - out_frame_action = ofa::forward_last_img; - } - } - else if (!std::holds_alternative(last_frame_variant)) { - // We didn't get a new frame from DesktopDuplication... - if (blend_mouse_cursor_flag) { - // ...but we need to blend the mouse cursor. - if (std::holds_alternative>(last_frame_variant)) { - // We have the shared pointer of the last image, replace it with intermediate surface - // while copying contents so we can blend this and future mouse cursor updates. - last_frame_action = lfa::replace_img_with_surface; - } - // Copy the intermediate surface which contains last DesktopDuplication frame - // to a new image from pull_free_image_cb and blend the mouse cursor onto it. - out_frame_action = ofa::copy_last_surface_and_blend_cursor; - } - else { - // ...and we don't need to blend the mouse cursor. - // This happens when the mouse cursor disappears from screen, - // or there's mouse cursor on screen, but its drawing is disabled in sunshine. - if (std::holds_alternative(last_frame_variant)) { - // We have the intermediate surface that was used as the mouse cursor blending base. - // Replace it with an image from pull_free_image_cb copying contents and freeing up the surface memory. - // Save the shared pointer to the image in case the mouse cursor reappears. - last_frame_action = lfa::replace_surface_with_img; - } - // Use saved last image shared pointer as output image evading copy. - out_frame_action = ofa::forward_last_img; - } - } } auto create_surface = [&](texture2d_t &surface) -> bool { - // Try to reuse the old surface if it hasn't been destroyed yet. - if (old_surface_delayed_destruction) { - surface.reset(old_surface_delayed_destruction.release()); + // Try to reuse the old surface if it hasn't been destroyed yet. + if (old_surface_delayed_destruction) { + surface.reset(old_surface_delayed_destruction.release()); + return true; + } + + // Otherwise create a new surface. + D3D11_TEXTURE2D_DESC t{}; + t.Width = width_before_rotation; + t.Height = height_before_rotation; + t.MipLevels = 1; + t.ArraySize = 1; + t.SampleDesc.Count = 1; + t.Usage = D3D11_USAGE_DEFAULT; + t.Format = capture_format; + t.BindFlags = 0; + status = device->CreateTexture2D(&t, nullptr, &surface); + if (FAILED(status)) { + BOOST_LOG(error) << "Failed to create frame copy texture [0x"sv + << util::hex(status).to_string_view() << ']'; + return false; + } + return true; - } - - // Otherwise create a new surface. - D3D11_TEXTURE2D_DESC t {}; - t.Width = width_before_rotation; - t.Height = height_before_rotation; - t.MipLevels = 1; - t.ArraySize = 1; - t.SampleDesc.Count = 1; - t.Usage = D3D11_USAGE_DEFAULT; - t.Format = capture_format; - t.BindFlags = 0; - status = device->CreateTexture2D(&t, nullptr, &surface); - if (FAILED(status)) { - BOOST_LOG(error) << "Failed to create frame copy texture [0x"sv << util::hex(status).to_string_view() << ']'; - return false; - } - - return true; }; - auto get_locked_d3d_img = [&](std::shared_ptr &img, bool dummy = false) -> std::tuple, texture_lock_helper> { - auto d3d_img = std::static_pointer_cast(img); + auto get_locked_d3d_img = [&](std::shared_ptr &img, + bool dummy = false) + -> std::tuple, texture_lock_helper> { + auto d3d_img = std::static_pointer_cast(img); - // Finish creating the image (if it hasn't happened already), - // also creates synchronization primitives for shared access from multiple direct3d devices. - if (complete_img(d3d_img.get(), dummy)) return { nullptr, nullptr }; + // Finish creating the image (if it hasn't happened already), + // also creates synchronization primitives for shared access from + // multiple direct3d devices. + if (complete_img(d3d_img.get(), dummy)) return {nullptr, nullptr}; - // This image is shared between capture direct3d device and encoders direct3d devices, - // we must acquire lock before doing anything to it. - texture_lock_helper lock_helper(d3d_img->capture_mutex.get()); - if (!lock_helper.lock()) { - BOOST_LOG(error) << "Failed to lock capture texture"; - return { nullptr, nullptr }; - } + // This image is shared between capture direct3d device and encoders + // direct3d devices, we must acquire lock before doing anything to it. + texture_lock_helper lock_helper(d3d_img->capture_mutex.get()); + if (!lock_helper.lock()) { + BOOST_LOG(error) << "Failed to lock capture texture"; + return {nullptr, nullptr}; + } - return { std::move(d3d_img), std::move(lock_helper) }; + return {std::move(d3d_img), std::move(lock_helper)}; }; switch (last_frame_action) { - case lfa::nothing: { - break; - } - - case lfa::replace_surface_with_img: { - auto p_surface = std::get_if(&last_frame_variant); - if (!p_surface) { - BOOST_LOG(error) << "Logical error at " << __FILE__ << ":" << __LINE__; - return capture_e::error; + case lfa::nothing: { + break; } - std::shared_ptr img; - if (!pull_free_image_cb(img)) return capture_e::interrupted; + case lfa::replace_surface_with_img: { + auto p_surface = std::get_if(&last_frame_variant); + if (!p_surface) { + BOOST_LOG(error) + << "Logical error at " << __FILE__ << ":" << __LINE__; + return capture_e::error; + } - auto [d3d_img, lock] = get_locked_d3d_img(img); - if (!d3d_img) return capture_e::error; + std::shared_ptr img; + if (!pull_free_image_cb(img)) return capture_e::interrupted; - device_ctx->CopyResource(d3d_img->capture_texture.get(), p_surface->get()); + auto [d3d_img, lock] = get_locked_d3d_img(img); + if (!d3d_img) return capture_e::error; - // We delay the destruction of intermediate surface in case the mouse cursor reappears shortly. - old_surface_delayed_destruction.reset(p_surface->release()); - old_surface_timestamp = std::chrono::steady_clock::now(); + device_ctx->CopyResource(d3d_img->capture_texture.get(), + p_surface->get()); - last_frame_variant = img; - break; - } + // We delay the destruction of intermediate surface in case the + // mouse cursor reappears shortly. + old_surface_delayed_destruction.reset(p_surface->release()); + old_surface_timestamp = std::chrono::steady_clock::now(); - case lfa::replace_img_with_surface: { - auto p_img = std::get_if>(&last_frame_variant); - if (!p_img) { - BOOST_LOG(error) << "Logical error at " << __FILE__ << ":" << __LINE__; - return capture_e::error; + last_frame_variant = img; + break; } - auto [d3d_img, lock] = get_locked_d3d_img(*p_img); - if (!d3d_img) return capture_e::error; - p_img = nullptr; - last_frame_variant = texture2d_t {}; - auto &surface = std::get(last_frame_variant); - if (!create_surface(surface)) return capture_e::error; + case lfa::replace_img_with_surface: { + auto p_img = + std::get_if>(&last_frame_variant); + if (!p_img) { + BOOST_LOG(error) + << "Logical error at " << __FILE__ << ":" << __LINE__; + return capture_e::error; + } + auto [d3d_img, lock] = get_locked_d3d_img(*p_img); + if (!d3d_img) return capture_e::error; - device_ctx->CopyResource(surface.get(), d3d_img->capture_texture.get()); - break; - } + p_img = nullptr; + last_frame_variant = texture2d_t{}; + auto &surface = std::get(last_frame_variant); + if (!create_surface(surface)) return capture_e::error; - case lfa::copy_src_to_img: { - last_frame_variant = {}; - - std::shared_ptr img; - if (!pull_free_image_cb(img)) return capture_e::interrupted; - - auto [d3d_img, lock] = get_locked_d3d_img(img); - if (!d3d_img) return capture_e::error; - - device_ctx->CopyResource(d3d_img->capture_texture.get(), src.get()); - last_frame_variant = img; - break; - } - - case lfa::copy_src_to_surface: { - auto p_surface = std::get_if(&last_frame_variant); - if (!p_surface) { - last_frame_variant = texture2d_t {}; - p_surface = std::get_if(&last_frame_variant); - if (!create_surface(*p_surface)) return capture_e::error; + device_ctx->CopyResource(surface.get(), + d3d_img->capture_texture.get()); + break; + } + + case lfa::copy_src_to_img: { + last_frame_variant = {}; + + std::shared_ptr img; + if (!pull_free_image_cb(img)) return capture_e::interrupted; + + auto [d3d_img, lock] = get_locked_d3d_img(img); + if (!d3d_img) return capture_e::error; + + device_ctx->CopyResource(d3d_img->capture_texture.get(), src.get()); + last_frame_variant = img; + break; + } + + case lfa::copy_src_to_surface: { + auto p_surface = std::get_if(&last_frame_variant); + if (!p_surface) { + last_frame_variant = texture2d_t{}; + p_surface = std::get_if(&last_frame_variant); + if (!create_surface(*p_surface)) return capture_e::error; + } + device_ctx->CopyResource(p_surface->get(), src.get()); + break; } - device_ctx->CopyResource(p_surface->get(), src.get()); - break; - } } auto blend_cursor = [&](img_d3d_t &d3d_img) { - device_ctx->VSSetShader(cursor_vs.get(), nullptr, 0); - device_ctx->PSSetShader(cursor_ps.get(), nullptr, 0); - device_ctx->OMSetRenderTargets(1, &d3d_img.capture_rt, nullptr); + device_ctx->VSSetShader(cursor_vs.get(), nullptr, 0); + device_ctx->PSSetShader(cursor_ps.get(), nullptr, 0); + device_ctx->OMSetRenderTargets(1, &d3d_img.capture_rt, nullptr); - if (cursor_alpha.texture.get()) { - // Perform an alpha blending operation - device_ctx->OMSetBlendState(blend_alpha.get(), nullptr, 0xFFFFFFFFu); + if (cursor_alpha.texture.get()) { + // Perform an alpha blending operation + device_ctx->OMSetBlendState(blend_alpha.get(), nullptr, + 0xFFFFFFFFu); - device_ctx->PSSetShaderResources(0, 1, &cursor_alpha.input_res); - device_ctx->RSSetViewports(1, &cursor_alpha.cursor_view); - device_ctx->Draw(3, 0); - } + device_ctx->PSSetShaderResources(0, 1, &cursor_alpha.input_res); + device_ctx->RSSetViewports(1, &cursor_alpha.cursor_view); + device_ctx->Draw(3, 0); + } - if (cursor_xor.texture.get()) { - // Perform an invert blending without touching alpha values - device_ctx->OMSetBlendState(blend_invert.get(), nullptr, 0x00FFFFFFu); + if (cursor_xor.texture.get()) { + // Perform an invert blending without touching alpha values + device_ctx->OMSetBlendState(blend_invert.get(), nullptr, + 0x00FFFFFFu); - device_ctx->PSSetShaderResources(0, 1, &cursor_xor.input_res); - device_ctx->RSSetViewports(1, &cursor_xor.cursor_view); - device_ctx->Draw(3, 0); - } + device_ctx->PSSetShaderResources(0, 1, &cursor_xor.input_res); + device_ctx->RSSetViewports(1, &cursor_xor.cursor_view); + device_ctx->Draw(3, 0); + } - device_ctx->OMSetBlendState(blend_disable.get(), nullptr, 0xFFFFFFFFu); + device_ctx->OMSetBlendState(blend_disable.get(), nullptr, 0xFFFFFFFFu); - ID3D11RenderTargetView *emptyRenderTarget = nullptr; - device_ctx->OMSetRenderTargets(1, &emptyRenderTarget, nullptr); - device_ctx->RSSetViewports(0, nullptr); - ID3D11ShaderResourceView *emptyShaderResourceView = nullptr; - device_ctx->PSSetShaderResources(0, 1, &emptyShaderResourceView); + ID3D11RenderTargetView *emptyRenderTarget = nullptr; + device_ctx->OMSetRenderTargets(1, &emptyRenderTarget, nullptr); + device_ctx->RSSetViewports(0, nullptr); + ID3D11ShaderResourceView *emptyShaderResourceView = nullptr; + device_ctx->PSSetShaderResources(0, 1, &emptyShaderResourceView); }; switch (out_frame_action) { - case ofa::forward_last_img: { - auto p_img = std::get_if>(&last_frame_variant); - if (!p_img) { - BOOST_LOG(error) << "Logical error at " << __FILE__ << ":" << __LINE__; - return capture_e::error; - } - img_out = *p_img; - break; - } - - case ofa::copy_last_surface_and_blend_cursor: { - auto p_surface = std::get_if(&last_frame_variant); - if (!p_surface) { - BOOST_LOG(error) << "Logical error at " << __FILE__ << ":" << __LINE__; - return capture_e::error; - } - if (!blend_mouse_cursor_flag) { - BOOST_LOG(error) << "Logical error at " << __FILE__ << ":" << __LINE__; - return capture_e::error; + case ofa::forward_last_img: { + auto p_img = + std::get_if>(&last_frame_variant); + if (!p_img) { + BOOST_LOG(error) + << "Logical error at " << __FILE__ << ":" << __LINE__; + return capture_e::error; + } + img_out = *p_img; + break; } - if (!pull_free_image_cb(img_out)) return capture_e::interrupted; + case ofa::copy_last_surface_and_blend_cursor: { + auto p_surface = std::get_if(&last_frame_variant); + if (!p_surface) { + BOOST_LOG(error) + << "Logical error at " << __FILE__ << ":" << __LINE__; + return capture_e::error; + } + if (!blend_mouse_cursor_flag) { + BOOST_LOG(error) + << "Logical error at " << __FILE__ << ":" << __LINE__; + return capture_e::error; + } - auto [d3d_img, lock] = get_locked_d3d_img(img_out); - if (!d3d_img) return capture_e::error; + if (!pull_free_image_cb(img_out)) return capture_e::interrupted; - device_ctx->CopyResource(d3d_img->capture_texture.get(), p_surface->get()); - blend_cursor(*d3d_img); - break; - } + auto [d3d_img, lock] = get_locked_d3d_img(img_out); + if (!d3d_img) return capture_e::error; - case ofa::dummy_fallback: { - if (!pull_free_image_cb(img_out)) return capture_e::interrupted; - - // Clear the image if it has been used as a dummy. - // It can have the mouse cursor blended onto it. - auto old_d3d_img = (img_d3d_t *) img_out.get(); - bool reclear_dummy = old_d3d_img->dummy && old_d3d_img->capture_texture; - - auto [d3d_img, lock] = get_locked_d3d_img(img_out, true); - if (!d3d_img) return capture_e::error; - - if (reclear_dummy) { - auto dummy_data = std::make_unique(d3d_img->row_pitch * d3d_img->height); - std::fill_n(dummy_data.get(), d3d_img->row_pitch * d3d_img->height, 0); - device_ctx->UpdateSubresource(d3d_img->capture_texture.get(), 0, nullptr, dummy_data.get(), d3d_img->row_pitch, 0); + device_ctx->CopyResource(d3d_img->capture_texture.get(), + p_surface->get()); + blend_cursor(*d3d_img); + break; } - if (blend_mouse_cursor_flag) { - blend_cursor(*d3d_img); - } + case ofa::dummy_fallback: { + if (!pull_free_image_cb(img_out)) return capture_e::interrupted; - break; - } + // Clear the image if it has been used as a dummy. + // It can have the mouse cursor blended onto it. + auto old_d3d_img = (img_d3d_t *)img_out.get(); + bool reclear_dummy = + old_d3d_img->dummy && old_d3d_img->capture_texture; + + auto [d3d_img, lock] = get_locked_d3d_img(img_out, true); + if (!d3d_img) return capture_e::error; + + if (reclear_dummy) { + auto dummy_data = std::make_unique( + d3d_img->row_pitch * d3d_img->height); + std::fill_n(dummy_data.get(), + d3d_img->row_pitch * d3d_img->height, 0); + device_ctx->UpdateSubresource(d3d_img->capture_texture.get(), 0, + nullptr, dummy_data.get(), + d3d_img->row_pitch, 0); + } + + if (blend_mouse_cursor_flag) { + blend_cursor(*d3d_img); + } + + break; + } } // Perform delayed destruction of the unused surface if the time is due. - if (old_surface_delayed_destruction && old_surface_timestamp + 10s < std::chrono::steady_clock::now()) { - old_surface_delayed_destruction.reset(); + if (old_surface_delayed_destruction && + old_surface_timestamp + 10s < std::chrono::steady_clock::now()) { + old_surface_delayed_destruction.reset(); } if (img_out) { - img_out->frame_timestamp = frame_timestamp; + img_out->frame_timestamp = frame_timestamp; } return capture_e::ok; - } +} - int - display_vram_t::init(const ::video::config_t &config, const std::string &display_name) { +int display_vram_t::init(const ::video::config_t &config, + const std::string &display_name) { if (display_base_t::init(config, display_name)) { - return -1; + return -1; } - D3D11_SAMPLER_DESC sampler_desc {}; + D3D11_SAMPLER_DESC sampler_desc{}; sampler_desc.Filter = D3D11_FILTER_MIN_MAG_MIP_LINEAR; sampler_desc.AddressU = D3D11_TEXTURE_ADDRESS_CLAMP; sampler_desc.AddressV = D3D11_TEXTURE_ADDRESS_CLAMP; @@ -1340,53 +1490,75 @@ namespace platf::dxgi { auto status = device->CreateSamplerState(&sampler_desc, &sampler_linear); if (FAILED(status)) { - BOOST_LOG(error) << "Failed to create point sampler state [0x"sv << util::hex(status).to_string_view() << ']'; - return -1; + BOOST_LOG(error) << "Failed to create point sampler state [0x"sv + << util::hex(status).to_string_view() << ']'; + return -1; } - status = device->CreateVertexShader(cursor_vs_hlsl->GetBufferPointer(), cursor_vs_hlsl->GetBufferSize(), nullptr, &cursor_vs); + status = device->CreateVertexShader(cursor_vs_hlsl->GetBufferPointer(), + cursor_vs_hlsl->GetBufferSize(), + nullptr, &cursor_vs); if (status) { - BOOST_LOG(error) << "Failed to create scene vertex shader [0x"sv << util::hex(status).to_string_view() << ']'; - return -1; + BOOST_LOG(error) << "Failed to create scene vertex shader [0x"sv + << util::hex(status).to_string_view() << ']'; + return -1; } { - int32_t rotation_modifier = display_rotation == DXGI_MODE_ROTATION_UNSPECIFIED ? 0 : display_rotation - 1; - int32_t rotation_data[16 / sizeof(int32_t)] { rotation_modifier }; // aligned to 16-byte - auto rotation = make_buffer(device.get(), rotation_data); - if (!rotation) { - BOOST_LOG(error) << "Failed to create display rotation vertex constant buffer"; - return -1; - } - device_ctx->VSSetConstantBuffers(2, 1, &rotation); + int32_t rotation_modifier = + display_rotation == DXGI_MODE_ROTATION_UNSPECIFIED + ? 0 + : display_rotation - 1; + int32_t rotation_data[16 / sizeof(int32_t)]{ + rotation_modifier}; // aligned to 16-byte + auto rotation = make_buffer(device.get(), rotation_data); + if (!rotation) { + BOOST_LOG(error) + << "Failed to create display rotation vertex constant buffer"; + return -1; + } + device_ctx->VSSetConstantBuffers(2, 1, &rotation); } if (config.dynamicRange && is_hdr()) { - // This shader will normalize scRGB white levels to a user-defined white level - status = device->CreatePixelShader(cursor_ps_normalize_white_hlsl->GetBufferPointer(), cursor_ps_normalize_white_hlsl->GetBufferSize(), nullptr, &cursor_ps); - if (status) { - BOOST_LOG(error) << "Failed to create cursor blending (normalized white) pixel shader [0x"sv << util::hex(status).to_string_view() << ']'; - return -1; - } + // This shader will normalize scRGB white levels to a user-defined white + // level + status = device->CreatePixelShader( + cursor_ps_normalize_white_hlsl->GetBufferPointer(), + cursor_ps_normalize_white_hlsl->GetBufferSize(), nullptr, + &cursor_ps); + if (status) { + BOOST_LOG(error) + << "Failed to create cursor blending (normalized white) pixel shader [0x"sv + << util::hex(status).to_string_view() << ']'; + return -1; + } - // Use a 300 nit target for the mouse cursor. We should really get - // the user's SDR white level in nits, but there is no API that - // provides that information to Win32 apps. - float white_multiplier_data[16 / sizeof(float)] { 300.0f / 80.f }; // aligned to 16-byte - auto white_multiplier = make_buffer(device.get(), white_multiplier_data); - if (!white_multiplier) { - BOOST_LOG(warning) << "Failed to create cursor blending (normalized white) white multiplier constant buffer"; - return -1; - } + // Use a 300 nit target for the mouse cursor. We should really get + // the user's SDR white level in nits, but there is no API that + // provides that information to Win32 apps. + float white_multiplier_data[16 / sizeof(float)]{ + 300.0f / 80.f}; // aligned to 16-byte + auto white_multiplier = + make_buffer(device.get(), white_multiplier_data); + if (!white_multiplier) { + BOOST_LOG(warning) + << "Failed to create cursor blending (normalized white) white " + "multiplier constant buffer"; + return -1; + } - device_ctx->PSSetConstantBuffers(1, 1, &white_multiplier); - } - else { - status = device->CreatePixelShader(cursor_ps_hlsl->GetBufferPointer(), cursor_ps_hlsl->GetBufferSize(), nullptr, &cursor_ps); - if (status) { - BOOST_LOG(error) << "Failed to create cursor blending pixel shader [0x"sv << util::hex(status).to_string_view() << ']'; - return -1; - } + device_ctx->PSSetConstantBuffers(1, 1, &white_multiplier); + } else { + status = device->CreatePixelShader(cursor_ps_hlsl->GetBufferPointer(), + cursor_ps_hlsl->GetBufferSize(), + nullptr, &cursor_ps); + if (status) { + BOOST_LOG(error) + << "Failed to create cursor blending pixel shader [0x"sv + << util::hex(status).to_string_view() << ']'; + return -1; + } } blend_alpha = make_blend(device.get(), true, false); @@ -1394,7 +1566,7 @@ namespace platf::dxgi { blend_disable = make_blend(device.get(), false, false); if (!blend_disable || !blend_alpha || !blend_invert) { - return -1; + return -1; } device_ctx->OMSetBlendState(blend_disable.get(), nullptr, 0xFFFFFFFFu); @@ -1402,10 +1574,9 @@ namespace platf::dxgi { device_ctx->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP); return 0; - } +} - std::shared_ptr - display_vram_t::alloc_img() { +std::shared_ptr display_vram_t::alloc_img() { auto img = std::make_shared(); // Initialize format-independent fields @@ -1414,22 +1585,24 @@ namespace platf::dxgi { img->id = next_image_id++; return img; - } +} - // This cannot use ID3D11DeviceContext because it can be called concurrently by the encoding thread - int - display_vram_t::complete_img(platf::img_t *img_base, bool dummy) { - auto img = (img_d3d_t *) img_base; +// This cannot use ID3D11DeviceContext because it can be called concurrently by +// the encoding thread +int display_vram_t::complete_img(platf::img_t *img_base, bool dummy) { + auto img = (img_d3d_t *)img_base; - // If this already has a capture texture and it's not switching dummy state, nothing to do + // If this already has a capture texture and it's not switching dummy state, + // nothing to do if (img->capture_texture && img->dummy == dummy) { - return 0; + return 0; } // If this is not a dummy image, we must know the format by now if (!dummy && capture_format == DXGI_FORMAT_UNKNOWN) { - BOOST_LOG(error) << "display_vram_t::complete_img() called with unknown capture format!"; - return -1; + BOOST_LOG(error) << "display_vram_t::complete_img() called with " + "unknown capture format!"; + return -1; } // Reset the image (in case this was previously a dummy) @@ -1438,17 +1611,19 @@ namespace platf::dxgi { img->capture_mutex.reset(); img->data = nullptr; if (img->encoder_texture_handle) { - CloseHandle(img->encoder_texture_handle); - img->encoder_texture_handle = NULL; + CloseHandle(img->encoder_texture_handle); + img->encoder_texture_handle = NULL; } // Initialize format-dependent fields img->pixel_pitch = get_pixel_pitch(); img->row_pitch = img->pixel_pitch * img->width; img->dummy = dummy; - img->format = (capture_format == DXGI_FORMAT_UNKNOWN) ? DXGI_FORMAT_B8G8R8A8_UNORM : capture_format; + img->format = (capture_format == DXGI_FORMAT_UNKNOWN) + ? DXGI_FORMAT_B8G8R8A8_UNORM + : capture_format; - D3D11_TEXTURE2D_DESC t {}; + D3D11_TEXTURE2D_DESC t{}; t.Width = img->width; t.Height = img->height; t.MipLevels = 1; @@ -1457,181 +1632,203 @@ namespace platf::dxgi { t.Usage = D3D11_USAGE_DEFAULT; t.Format = img->format; t.BindFlags = D3D11_BIND_SHADER_RESOURCE | D3D11_BIND_RENDER_TARGET; - t.MiscFlags = D3D11_RESOURCE_MISC_SHARED_NTHANDLE | D3D11_RESOURCE_MISC_SHARED_KEYEDMUTEX; + t.MiscFlags = D3D11_RESOURCE_MISC_SHARED_NTHANDLE | + D3D11_RESOURCE_MISC_SHARED_KEYEDMUTEX; HRESULT status; if (dummy) { - auto dummy_data = std::make_unique(img->row_pitch * img->height); - std::fill_n(dummy_data.get(), img->row_pitch * img->height, 0); - D3D11_SUBRESOURCE_DATA initial_data { - dummy_data.get(), - (UINT) img->row_pitch, - 0 - }; - status = device->CreateTexture2D(&t, &initial_data, &img->capture_texture); - } - else { - status = device->CreateTexture2D(&t, nullptr, &img->capture_texture); + auto dummy_data = + std::make_unique(img->row_pitch * img->height); + std::fill_n(dummy_data.get(), img->row_pitch * img->height, 0); + D3D11_SUBRESOURCE_DATA initial_data{dummy_data.get(), + (UINT)img->row_pitch, 0}; + status = + device->CreateTexture2D(&t, &initial_data, &img->capture_texture); + } else { + status = device->CreateTexture2D(&t, nullptr, &img->capture_texture); } if (FAILED(status)) { - BOOST_LOG(error) << "Failed to create img buf texture [0x"sv << util::hex(status).to_string_view() << ']'; - return -1; + BOOST_LOG(error) << "Failed to create img buf texture [0x"sv + << util::hex(status).to_string_view() << ']'; + return -1; } - status = device->CreateRenderTargetView(img->capture_texture.get(), nullptr, &img->capture_rt); + status = device->CreateRenderTargetView(img->capture_texture.get(), nullptr, + &img->capture_rt); if (FAILED(status)) { - BOOST_LOG(error) << "Failed to create render target view [0x"sv << util::hex(status).to_string_view() << ']'; - return -1; + BOOST_LOG(error) << "Failed to create render target view [0x"sv + << util::hex(status).to_string_view() << ']'; + return -1; } // Get the keyed mutex to synchronize with the encoding code - status = img->capture_texture->QueryInterface(__uuidof(IDXGIKeyedMutex), (void **) &img->capture_mutex); + status = img->capture_texture->QueryInterface(__uuidof(IDXGIKeyedMutex), + (void **)&img->capture_mutex); if (FAILED(status)) { - BOOST_LOG(error) << "Failed to query IDXGIKeyedMutex [0x"sv << util::hex(status).to_string_view() << ']'; - return -1; + BOOST_LOG(error) << "Failed to query IDXGIKeyedMutex [0x"sv + << util::hex(status).to_string_view() << ']'; + return -1; } resource1_t resource; - status = img->capture_texture->QueryInterface(__uuidof(IDXGIResource1), (void **) &resource); + status = img->capture_texture->QueryInterface(__uuidof(IDXGIResource1), + (void **)&resource); if (FAILED(status)) { - BOOST_LOG(error) << "Failed to query IDXGIResource1 [0x"sv << util::hex(status).to_string_view() << ']'; - return -1; + BOOST_LOG(error) << "Failed to query IDXGIResource1 [0x"sv + << util::hex(status).to_string_view() << ']'; + return -1; } // Create a handle for the encoder device to use to open this texture - status = resource->CreateSharedHandle(nullptr, DXGI_SHARED_RESOURCE_READ, nullptr, &img->encoder_texture_handle); + status = + resource->CreateSharedHandle(nullptr, DXGI_SHARED_RESOURCE_READ, + nullptr, &img->encoder_texture_handle); if (FAILED(status)) { - BOOST_LOG(error) << "Failed to create shared texture handle [0x"sv << util::hex(status).to_string_view() << ']'; - return -1; + BOOST_LOG(error) << "Failed to create shared texture handle [0x"sv + << util::hex(status).to_string_view() << ']'; + return -1; } - img->data = (std::uint8_t *) img->capture_texture.get(); + img->data = (std::uint8_t *)img->capture_texture.get(); return 0; - } +} - // This cannot use ID3D11DeviceContext because it can be called concurrently by the encoding thread - int - display_vram_t::dummy_img(platf::img_t *img_base) { +// This cannot use ID3D11DeviceContext because it can be called concurrently by +// the encoding thread +int display_vram_t::dummy_img(platf::img_t *img_base) { return complete_img(img_base, true); - } +} - std::vector - display_vram_t::get_supported_capture_formats() { +std::vector display_vram_t::get_supported_capture_formats() { return { - // scRGB FP16 is the ideal format for Wide Color Gamut and Advanced Color - // displays (both SDR and HDR). This format uses linear gamma, so we will - // use a linear->PQ shader for HDR and a linear->sRGB shader for SDR. - DXGI_FORMAT_R16G16B16A16_FLOAT, + // scRGB FP16 is the ideal format for Wide Color Gamut and Advanced + // Color + // displays (both SDR and HDR). This format uses linear gamma, so we + // will + // use a linear->PQ shader for HDR and a linear->sRGB shader for SDR. + DXGI_FORMAT_R16G16B16A16_FLOAT, - // DXGI_FORMAT_R10G10B10A2_UNORM seems like it might give us frames already - // converted to SMPTE 2084 PQ, however it seems to actually just clamp the - // scRGB FP16 values that DWM is using when the desktop format is scRGB FP16. - // - // If there is a case where the desktop format is really SMPTE 2084 PQ, it - // might make sense to support capturing it without conversion to scRGB, - // but we avoid it for now. + // DXGI_FORMAT_R10G10B10A2_UNORM seems like it might give us frames + // already + // converted to SMPTE 2084 PQ, however it seems to actually just clamp + // the + // scRGB FP16 values that DWM is using when the desktop format is scRGB + // FP16. + // + // If there is a case where the desktop format is really SMPTE 2084 PQ, + // it + // might make sense to support capturing it without conversion to scRGB, + // but we avoid it for now. - // We include the 8-bit modes too for when the display is in SDR mode, - // while the client stream is HDR-capable. These UNORM formats can - // use our normal pixel shaders that expect sRGB input. - DXGI_FORMAT_B8G8R8A8_UNORM, - DXGI_FORMAT_B8G8R8X8_UNORM, - DXGI_FORMAT_R8G8B8A8_UNORM, + // We include the 8-bit modes too for when the display is in SDR mode, + // while the client stream is HDR-capable. These UNORM formats can + // use our normal pixel shaders that expect sRGB input. + DXGI_FORMAT_B8G8R8A8_UNORM, + DXGI_FORMAT_B8G8R8X8_UNORM, + DXGI_FORMAT_R8G8B8A8_UNORM, }; - } +} - /** - * @brief Checks that a given codec is supported by the display device. - * @param name The FFmpeg codec name (or similar for non-FFmpeg codecs). - * @param config The codec configuration. - * @return true if supported, false otherwise. - */ - bool - display_vram_t::is_codec_supported(std::string_view name, const ::video::config_t &config) { +/** + * @brief Checks that a given codec is supported by the display device. + * @param name The FFmpeg codec name (or similar for non-FFmpeg codecs). + * @param config The codec configuration. + * @return true if supported, false otherwise. + */ +bool display_vram_t::is_codec_supported(std::string_view name, + const ::video::config_t &config) { DXGI_ADAPTER_DESC adapter_desc; adapter->GetDesc(&adapter_desc); if (adapter_desc.VendorId == 0x1002) { // AMD - // If it's not an AMF encoder, it's not compatible with an AMD GPU - if (!boost::algorithm::ends_with(name, "_amf")) { - return false; - } - - // Perform AMF version checks if we're using an AMD GPU. This check is placed in display_vram_t - // to avoid hitting the display_ram_t path which uses software encoding and doesn't touch AMF. - HMODULE amfrt = LoadLibraryW(AMF_DLL_NAME); - if (amfrt) { - auto unload_amfrt = util::fail_guard([amfrt]() { - FreeLibrary(amfrt); - }); - - auto fnAMFQueryVersion = (AMFQueryVersion_Fn) GetProcAddress(amfrt, AMF_QUERY_VERSION_FUNCTION_NAME); - if (fnAMFQueryVersion) { - amf_uint64 version; - auto result = fnAMFQueryVersion(&version); - if (result == AMF_OK) { - if (config.videoFormat == 2 && version < AMF_MAKE_FULL_VERSION(1, 4, 30, 0)) { - // AMF 1.4.30 adds ultra low latency mode for AV1. Don't use AV1 on earlier versions. - // This corresponds to driver version 23.5.2 (23.10.01.45) or newer. - BOOST_LOG(warning) << "AV1 encoding is disabled on AMF version "sv - << AMF_GET_MAJOR_VERSION(version) << '.' - << AMF_GET_MINOR_VERSION(version) << '.' - << AMF_GET_SUBMINOR_VERSION(version) << '.' - << AMF_GET_BUILD_VERSION(version); - BOOST_LOG(warning) << "If your AMD GPU supports AV1 encoding, update your graphics drivers!"sv; - return false; - } - else if (config.dynamicRange && version < AMF_MAKE_FULL_VERSION(1, 4, 23, 0)) { - // Older versions of the AMD AMF runtime can crash when fed P010 surfaces. - // Fail if AMF version is below 1.4.23 where HEVC Main10 encoding was introduced. - // AMF 1.4.23 corresponds to driver version 21.12.1 (21.40.11.03) or newer. - BOOST_LOG(warning) << "HDR encoding is disabled on AMF version "sv - << AMF_GET_MAJOR_VERSION(version) << '.' - << AMF_GET_MINOR_VERSION(version) << '.' - << AMF_GET_SUBMINOR_VERSION(version) << '.' - << AMF_GET_BUILD_VERSION(version); - BOOST_LOG(warning) << "If your AMD GPU supports HEVC Main10 encoding, update your graphics drivers!"sv; - return false; - } - } - else { - BOOST_LOG(warning) << "AMFQueryVersion() failed: "sv << result; - } + // If it's not an AMF encoder, it's not compatible with an AMD GPU + if (!boost::algorithm::ends_with(name, "_amf")) { + return false; } - else { - BOOST_LOG(warning) << "AMF DLL missing export: "sv << AMF_QUERY_VERSION_FUNCTION_NAME; + + // Perform AMF version checks if we're using an AMD GPU. This check is + // placed in display_vram_t to avoid hitting the display_ram_t path + // which uses software encoding and doesn't touch AMF. + HMODULE amfrt = LoadLibraryW(AMF_DLL_NAME); + if (amfrt) { + auto unload_amfrt = + util::fail_guard([amfrt]() { FreeLibrary(amfrt); }); + + auto fnAMFQueryVersion = (AMFQueryVersion_Fn)GetProcAddress( + amfrt, AMF_QUERY_VERSION_FUNCTION_NAME); + if (fnAMFQueryVersion) { + amf_uint64 version; + auto result = fnAMFQueryVersion(&version); + if (result == AMF_OK) { + if (config.videoFormat == 2 && + version < AMF_MAKE_FULL_VERSION(1, 4, 30, 0)) { + // AMF 1.4.30 adds ultra low latency mode for AV1. Don't + // use AV1 on earlier versions. This corresponds to + // driver version 23.5.2 (23.10.01.45) or newer. + BOOST_LOG(warning) + << "AV1 encoding is disabled on AMF version "sv + << AMF_GET_MAJOR_VERSION(version) << '.' + << AMF_GET_MINOR_VERSION(version) << '.' + << AMF_GET_SUBMINOR_VERSION(version) << '.' + << AMF_GET_BUILD_VERSION(version); + BOOST_LOG(warning) + << "If your AMD GPU supports AV1 encoding, update your graphics drivers!"sv; + return false; + } else if (config.dynamicRange && + version < AMF_MAKE_FULL_VERSION(1, 4, 23, 0)) { + // Older versions of the AMD AMF runtime can crash when + // fed P010 surfaces. Fail if AMF version is + // below 1.4.23 where HEVC Main10 encoding was + // introduced. AMF 1.4.23 corresponds to driver + // version 21.12.1 (21.40.11.03) or newer. + BOOST_LOG(warning) + << "HDR encoding is disabled on AMF version "sv + << AMF_GET_MAJOR_VERSION(version) << '.' + << AMF_GET_MINOR_VERSION(version) << '.' + << AMF_GET_SUBMINOR_VERSION(version) << '.' + << AMF_GET_BUILD_VERSION(version); + BOOST_LOG(warning) + << "If your AMD GPU supports HEVC Main10 encoding, update your graphics drivers!"sv; + return false; + } + } else { + BOOST_LOG(warning) + << "AMFQueryVersion() failed: "sv << result; + } + } else { + BOOST_LOG(warning) << "AMF DLL missing export: "sv + << AMF_QUERY_VERSION_FUNCTION_NAME; + } + } else { + BOOST_LOG(warning) << "Detected AMD GPU but AMF failed to load"sv; } - } - else { - BOOST_LOG(warning) << "Detected AMD GPU but AMF failed to load"sv; - } - } - else if (adapter_desc.VendorId == 0x8086) { // Intel - // If it's not a QSV encoder, it's not compatible with an Intel GPU - if (!boost::algorithm::ends_with(name, "_qsv")) { - return false; - } - } - else if (adapter_desc.VendorId == 0x10de) { // Nvidia - // If it's not an NVENC encoder, it's not compatible with an Nvidia GPU - if (!boost::algorithm::ends_with(name, "_nvenc")) { - return false; - } - } - else { - BOOST_LOG(warning) << "Unknown GPU vendor ID: " << util::hex(adapter_desc.VendorId).to_string_view(); + } else if (adapter_desc.VendorId == 0x8086) { // Intel + // If it's not a QSV encoder, it's not compatible with an Intel GPU + if (!boost::algorithm::ends_with(name, "_qsv")) { + return false; + } + } else if (adapter_desc.VendorId == 0x10de) { // Nvidia + // If it's not an NVENC encoder, it's not compatible with an Nvidia GPU + if (!boost::algorithm::ends_with(name, "_nvenc")) { + return false; + } + } else { + BOOST_LOG(warning) << "Unknown GPU vendor ID: " + << util::hex(adapter_desc.VendorId).to_string_view(); } return true; - } +} - std::unique_ptr - display_vram_t::make_avcodec_encode_device(pix_fmt_e pix_fmt) { - if (pix_fmt != platf::pix_fmt_e::nv12 && pix_fmt != platf::pix_fmt_e::p010) { - BOOST_LOG(error) << "display_vram_t doesn't support pixel format ["sv << from_pix_fmt(pix_fmt) << ']'; +std::unique_ptr +display_vram_t::make_avcodec_encode_device(pix_fmt_e pix_fmt) { + if (pix_fmt != platf::pix_fmt_e::nv12 && + pix_fmt != platf::pix_fmt_e::p010) { + BOOST_LOG(error) << "display_vram_t doesn't support pixel format ["sv + << from_pix_fmt(pix_fmt) << ']'; - return nullptr; + return nullptr; } auto device = std::make_unique(); @@ -1639,37 +1836,42 @@ namespace platf::dxgi { auto ret = device->init(shared_from_this(), adapter.get(), pix_fmt); if (ret) { - return nullptr; + return nullptr; } return device; - } +} - std::unique_ptr - display_vram_t::make_nvenc_encode_device(pix_fmt_e pix_fmt) { +std::unique_ptr display_vram_t::make_nvenc_encode_device( + pix_fmt_e pix_fmt) { auto device = std::make_unique(); if (!device->init_device(shared_from_this(), adapter.get(), pix_fmt)) { - return nullptr; + return nullptr; } return device; - } +} - int - init() { +int init() { BOOST_LOG(info) << "Compiling shaders..."sv; -#define compile_vertex_shader_helper(x) \ - if (!(x##_hlsl = compile_vertex_shader(SUNSHINE_SHADERS_DIR "/" #x ".hlsl"))) return -1; -#define compile_pixel_shader_helper(x) \ - if (!(x##_hlsl = compile_pixel_shader(SUNSHINE_SHADERS_DIR "/" #x ".hlsl"))) return -1; +#define compile_vertex_shader_helper(x) \ + if (!(x##_hlsl = \ + compile_vertex_shader(SUNSHINE_SHADERS_DIR "/" #x ".hlsl"))) \ + return -1; +#define compile_pixel_shader_helper(x) \ + if (!(x##_hlsl = \ + compile_pixel_shader(SUNSHINE_SHADERS_DIR "/" #x ".hlsl"))) \ + return -1; compile_pixel_shader_helper(convert_yuv420_packed_uv_type0_ps); compile_pixel_shader_helper(convert_yuv420_packed_uv_type0_ps_linear); - compile_pixel_shader_helper(convert_yuv420_packed_uv_type0_ps_perceptual_quantizer); + compile_pixel_shader_helper( + convert_yuv420_packed_uv_type0_ps_perceptual_quantizer); compile_vertex_shader_helper(convert_yuv420_packed_uv_type0_vs); compile_pixel_shader_helper(convert_yuv420_planar_y_ps); compile_pixel_shader_helper(convert_yuv420_planar_y_ps_linear); - compile_pixel_shader_helper(convert_yuv420_planar_y_ps_perceptual_quantizer); + compile_pixel_shader_helper( + convert_yuv420_planar_y_ps_perceptual_quantizer); compile_vertex_shader_helper(convert_yuv420_planar_y_vs); compile_pixel_shader_helper(cursor_ps); compile_pixel_shader_helper(cursor_ps_normalize_white); @@ -1681,5 +1883,5 @@ namespace platf::dxgi { #undef compile_pixel_shader_helper return 0; - } +} } // namespace platf::dxgi diff --git a/src/platform/windows/keylayout.h b/src/platform/windows/keylayout.h index 55dfa284..839bc1a9 100644 --- a/src/platform/windows/keylayout.h +++ b/src/platform/windows/keylayout.h @@ -8,264 +8,265 @@ #include namespace platf { - // Virtual Key to Scan Code mapping for the US English layout (00000409). - // GameStream uses this as the canonical key layout for scancode conversion. - constexpr std::array::max() + 1> VK_TO_SCANCODE_MAP { - 0, /* 0x00 */ - 0, /* 0x01 */ - 0, /* 0x02 */ - 70, /* 0x03 */ - 0, /* 0x04 */ - 0, /* 0x05 */ - 0, /* 0x06 */ - 0, /* 0x07 */ - 14, /* 0x08 */ - 15, /* 0x09 */ - 0, /* 0x0a */ - 0, /* 0x0b */ - 76, /* 0x0c */ - 28, /* 0x0d */ - 0, /* 0x0e */ - 0, /* 0x0f */ - 42, /* 0x10 */ - 29, /* 0x11 */ - 56, /* 0x12 */ - 0, /* 0x13 */ - 58, /* 0x14 */ - 0, /* 0x15 */ - 0, /* 0x16 */ - 0, /* 0x17 */ - 0, /* 0x18 */ - 0, /* 0x19 */ - 0, /* 0x1a */ - 1, /* 0x1b */ - 0, /* 0x1c */ - 0, /* 0x1d */ - 0, /* 0x1e */ - 0, /* 0x1f */ - 57, /* 0x20 */ - 73, /* 0x21 */ - 81, /* 0x22 */ - 79, /* 0x23 */ - 71, /* 0x24 */ - 75, /* 0x25 */ - 72, /* 0x26 */ - 77, /* 0x27 */ - 80, /* 0x28 */ - 0, /* 0x29 */ - 0, /* 0x2a */ - 0, /* 0x2b */ - 84, /* 0x2c */ - 82, /* 0x2d */ - 83, /* 0x2e */ - 99, /* 0x2f */ - 11, /* 0x30 */ - 2, /* 0x31 */ - 3, /* 0x32 */ - 4, /* 0x33 */ - 5, /* 0x34 */ - 6, /* 0x35 */ - 7, /* 0x36 */ - 8, /* 0x37 */ - 9, /* 0x38 */ - 10, /* 0x39 */ - 0, /* 0x3a */ - 0, /* 0x3b */ - 0, /* 0x3c */ - 0, /* 0x3d */ - 0, /* 0x3e */ - 0, /* 0x3f */ - 0, /* 0x40 */ - 30, /* 0x41 */ - 48, /* 0x42 */ - 46, /* 0x43 */ - 32, /* 0x44 */ - 18, /* 0x45 */ - 33, /* 0x46 */ - 34, /* 0x47 */ - 35, /* 0x48 */ - 23, /* 0x49 */ - 36, /* 0x4a */ - 37, /* 0x4b */ - 38, /* 0x4c */ - 50, /* 0x4d */ - 49, /* 0x4e */ - 24, /* 0x4f */ - 25, /* 0x50 */ - 16, /* 0x51 */ - 19, /* 0x52 */ - 31, /* 0x53 */ - 20, /* 0x54 */ - 22, /* 0x55 */ - 47, /* 0x56 */ - 17, /* 0x57 */ - 45, /* 0x58 */ - 21, /* 0x59 */ - 44, /* 0x5a */ - 91, /* 0x5b */ - 92, /* 0x5c */ - 93, /* 0x5d */ - 0, /* 0x5e */ - 95, /* 0x5f */ - 82, /* 0x60 */ - 79, /* 0x61 */ - 80, /* 0x62 */ - 81, /* 0x63 */ - 75, /* 0x64 */ - 76, /* 0x65 */ - 77, /* 0x66 */ - 71, /* 0x67 */ - 72, /* 0x68 */ - 73, /* 0x69 */ - 55, /* 0x6a */ - 78, /* 0x6b */ - 0, /* 0x6c */ - 74, /* 0x6d */ - 83, /* 0x6e */ - 53, /* 0x6f */ - 59, /* 0x70 */ - 60, /* 0x71 */ - 61, /* 0x72 */ - 62, /* 0x73 */ - 63, /* 0x74 */ - 64, /* 0x75 */ - 65, /* 0x76 */ - 66, /* 0x77 */ - 67, /* 0x78 */ - 68, /* 0x79 */ - 87, /* 0x7a */ - 88, /* 0x7b */ - 100, /* 0x7c */ - 101, /* 0x7d */ - 102, /* 0x7e */ - 103, /* 0x7f */ - 104, /* 0x80 */ - 105, /* 0x81 */ - 106, /* 0x82 */ - 107, /* 0x83 */ - 108, /* 0x84 */ - 109, /* 0x85 */ - 110, /* 0x86 */ - 118, /* 0x87 */ - 0, /* 0x88 */ - 0, /* 0x89 */ - 0, /* 0x8a */ - 0, /* 0x8b */ - 0, /* 0x8c */ - 0, /* 0x8d */ - 0, /* 0x8e */ - 0, /* 0x8f */ - 69, /* 0x90 */ - 70, /* 0x91 */ - 0, /* 0x92 */ - 0, /* 0x93 */ - 0, /* 0x94 */ - 0, /* 0x95 */ - 0, /* 0x96 */ - 0, /* 0x97 */ - 0, /* 0x98 */ - 0, /* 0x99 */ - 0, /* 0x9a */ - 0, /* 0x9b */ - 0, /* 0x9c */ - 0, /* 0x9d */ - 0, /* 0x9e */ - 0, /* 0x9f */ - 42, /* 0xa0 */ - 54, /* 0xa1 */ - 29, /* 0xa2 */ - 29, /* 0xa3 */ - 56, /* 0xa4 */ - 56, /* 0xa5 */ - 106, /* 0xa6 */ - 105, /* 0xa7 */ - 103, /* 0xa8 */ - 104, /* 0xa9 */ - 101, /* 0xaa */ - 102, /* 0xab */ - 50, /* 0xac */ - 32, /* 0xad */ - 46, /* 0xae */ - 48, /* 0xaf */ - 25, /* 0xb0 */ - 16, /* 0xb1 */ - 36, /* 0xb2 */ - 34, /* 0xb3 */ - 108, /* 0xb4 */ - 109, /* 0xb5 */ - 107, /* 0xb6 */ - 33, /* 0xb7 */ - 0, /* 0xb8 */ - 0, /* 0xb9 */ - 39, /* 0xba */ - 13, /* 0xbb */ - 51, /* 0xbc */ - 12, /* 0xbd */ - 52, /* 0xbe */ - 53, /* 0xbf */ - 41, /* 0xc0 */ - 115, /* 0xc1 */ - 126, /* 0xc2 */ - 0, /* 0xc3 */ - 0, /* 0xc4 */ - 0, /* 0xc5 */ - 0, /* 0xc6 */ - 0, /* 0xc7 */ - 0, /* 0xc8 */ - 0, /* 0xc9 */ - 0, /* 0xca */ - 0, /* 0xcb */ - 0, /* 0xcc */ - 0, /* 0xcd */ - 0, /* 0xce */ - 0, /* 0xcf */ - 0, /* 0xd0 */ - 0, /* 0xd1 */ - 0, /* 0xd2 */ - 0, /* 0xd3 */ - 0, /* 0xd4 */ - 0, /* 0xd5 */ - 0, /* 0xd6 */ - 0, /* 0xd7 */ - 0, /* 0xd8 */ - 0, /* 0xd9 */ - 0, /* 0xda */ - 26, /* 0xdb */ - 43, /* 0xdc */ - 27, /* 0xdd */ - 40, /* 0xde */ - 0, /* 0xdf */ - 0, /* 0xe0 */ - 0, /* 0xe1 */ - 86, /* 0xe2 */ - 0, /* 0xe3 */ - 0, /* 0xe4 */ - 0, /* 0xe5 */ - 0, /* 0xe6 */ - 0, /* 0xe7 */ - 0, /* 0xe8 */ - 113, /* 0xe9 */ - 92, /* 0xea */ - 123, /* 0xeb */ - 0, /* 0xec */ - 111, /* 0xed */ - 90, /* 0xee */ - 0, /* 0xef */ - 0, /* 0xf0 */ - 91, /* 0xf1 */ - 0, /* 0xf2 */ - 95, /* 0xf3 */ - 0, /* 0xf4 */ - 94, /* 0xf5 */ - 0, /* 0xf6 */ - 0, /* 0xf7 */ - 0, /* 0xf8 */ - 93, /* 0xf9 */ - 0, /* 0xfa */ - 98, /* 0xfb */ - 0, /* 0xfc */ - 0, /* 0xfd */ - 0, /* 0xfe */ - 0, /* 0xff */ - }; +// Virtual Key to Scan Code mapping for the US English layout (00000409). +// GameStream uses this as the canonical key layout for scancode conversion. +constexpr std::array::max() + 1> + VK_TO_SCANCODE_MAP{ + 0, /* 0x00 */ + 0, /* 0x01 */ + 0, /* 0x02 */ + 70, /* 0x03 */ + 0, /* 0x04 */ + 0, /* 0x05 */ + 0, /* 0x06 */ + 0, /* 0x07 */ + 14, /* 0x08 */ + 15, /* 0x09 */ + 0, /* 0x0a */ + 0, /* 0x0b */ + 76, /* 0x0c */ + 28, /* 0x0d */ + 0, /* 0x0e */ + 0, /* 0x0f */ + 42, /* 0x10 */ + 29, /* 0x11 */ + 56, /* 0x12 */ + 0, /* 0x13 */ + 58, /* 0x14 */ + 0, /* 0x15 */ + 0, /* 0x16 */ + 0, /* 0x17 */ + 0, /* 0x18 */ + 0, /* 0x19 */ + 0, /* 0x1a */ + 1, /* 0x1b */ + 0, /* 0x1c */ + 0, /* 0x1d */ + 0, /* 0x1e */ + 0, /* 0x1f */ + 57, /* 0x20 */ + 73, /* 0x21 */ + 81, /* 0x22 */ + 79, /* 0x23 */ + 71, /* 0x24 */ + 75, /* 0x25 */ + 72, /* 0x26 */ + 77, /* 0x27 */ + 80, /* 0x28 */ + 0, /* 0x29 */ + 0, /* 0x2a */ + 0, /* 0x2b */ + 84, /* 0x2c */ + 82, /* 0x2d */ + 83, /* 0x2e */ + 99, /* 0x2f */ + 11, /* 0x30 */ + 2, /* 0x31 */ + 3, /* 0x32 */ + 4, /* 0x33 */ + 5, /* 0x34 */ + 6, /* 0x35 */ + 7, /* 0x36 */ + 8, /* 0x37 */ + 9, /* 0x38 */ + 10, /* 0x39 */ + 0, /* 0x3a */ + 0, /* 0x3b */ + 0, /* 0x3c */ + 0, /* 0x3d */ + 0, /* 0x3e */ + 0, /* 0x3f */ + 0, /* 0x40 */ + 30, /* 0x41 */ + 48, /* 0x42 */ + 46, /* 0x43 */ + 32, /* 0x44 */ + 18, /* 0x45 */ + 33, /* 0x46 */ + 34, /* 0x47 */ + 35, /* 0x48 */ + 23, /* 0x49 */ + 36, /* 0x4a */ + 37, /* 0x4b */ + 38, /* 0x4c */ + 50, /* 0x4d */ + 49, /* 0x4e */ + 24, /* 0x4f */ + 25, /* 0x50 */ + 16, /* 0x51 */ + 19, /* 0x52 */ + 31, /* 0x53 */ + 20, /* 0x54 */ + 22, /* 0x55 */ + 47, /* 0x56 */ + 17, /* 0x57 */ + 45, /* 0x58 */ + 21, /* 0x59 */ + 44, /* 0x5a */ + 91, /* 0x5b */ + 92, /* 0x5c */ + 93, /* 0x5d */ + 0, /* 0x5e */ + 95, /* 0x5f */ + 82, /* 0x60 */ + 79, /* 0x61 */ + 80, /* 0x62 */ + 81, /* 0x63 */ + 75, /* 0x64 */ + 76, /* 0x65 */ + 77, /* 0x66 */ + 71, /* 0x67 */ + 72, /* 0x68 */ + 73, /* 0x69 */ + 55, /* 0x6a */ + 78, /* 0x6b */ + 0, /* 0x6c */ + 74, /* 0x6d */ + 83, /* 0x6e */ + 53, /* 0x6f */ + 59, /* 0x70 */ + 60, /* 0x71 */ + 61, /* 0x72 */ + 62, /* 0x73 */ + 63, /* 0x74 */ + 64, /* 0x75 */ + 65, /* 0x76 */ + 66, /* 0x77 */ + 67, /* 0x78 */ + 68, /* 0x79 */ + 87, /* 0x7a */ + 88, /* 0x7b */ + 100, /* 0x7c */ + 101, /* 0x7d */ + 102, /* 0x7e */ + 103, /* 0x7f */ + 104, /* 0x80 */ + 105, /* 0x81 */ + 106, /* 0x82 */ + 107, /* 0x83 */ + 108, /* 0x84 */ + 109, /* 0x85 */ + 110, /* 0x86 */ + 118, /* 0x87 */ + 0, /* 0x88 */ + 0, /* 0x89 */ + 0, /* 0x8a */ + 0, /* 0x8b */ + 0, /* 0x8c */ + 0, /* 0x8d */ + 0, /* 0x8e */ + 0, /* 0x8f */ + 69, /* 0x90 */ + 70, /* 0x91 */ + 0, /* 0x92 */ + 0, /* 0x93 */ + 0, /* 0x94 */ + 0, /* 0x95 */ + 0, /* 0x96 */ + 0, /* 0x97 */ + 0, /* 0x98 */ + 0, /* 0x99 */ + 0, /* 0x9a */ + 0, /* 0x9b */ + 0, /* 0x9c */ + 0, /* 0x9d */ + 0, /* 0x9e */ + 0, /* 0x9f */ + 42, /* 0xa0 */ + 54, /* 0xa1 */ + 29, /* 0xa2 */ + 29, /* 0xa3 */ + 56, /* 0xa4 */ + 56, /* 0xa5 */ + 106, /* 0xa6 */ + 105, /* 0xa7 */ + 103, /* 0xa8 */ + 104, /* 0xa9 */ + 101, /* 0xaa */ + 102, /* 0xab */ + 50, /* 0xac */ + 32, /* 0xad */ + 46, /* 0xae */ + 48, /* 0xaf */ + 25, /* 0xb0 */ + 16, /* 0xb1 */ + 36, /* 0xb2 */ + 34, /* 0xb3 */ + 108, /* 0xb4 */ + 109, /* 0xb5 */ + 107, /* 0xb6 */ + 33, /* 0xb7 */ + 0, /* 0xb8 */ + 0, /* 0xb9 */ + 39, /* 0xba */ + 13, /* 0xbb */ + 51, /* 0xbc */ + 12, /* 0xbd */ + 52, /* 0xbe */ + 53, /* 0xbf */ + 41, /* 0xc0 */ + 115, /* 0xc1 */ + 126, /* 0xc2 */ + 0, /* 0xc3 */ + 0, /* 0xc4 */ + 0, /* 0xc5 */ + 0, /* 0xc6 */ + 0, /* 0xc7 */ + 0, /* 0xc8 */ + 0, /* 0xc9 */ + 0, /* 0xca */ + 0, /* 0xcb */ + 0, /* 0xcc */ + 0, /* 0xcd */ + 0, /* 0xce */ + 0, /* 0xcf */ + 0, /* 0xd0 */ + 0, /* 0xd1 */ + 0, /* 0xd2 */ + 0, /* 0xd3 */ + 0, /* 0xd4 */ + 0, /* 0xd5 */ + 0, /* 0xd6 */ + 0, /* 0xd7 */ + 0, /* 0xd8 */ + 0, /* 0xd9 */ + 0, /* 0xda */ + 26, /* 0xdb */ + 43, /* 0xdc */ + 27, /* 0xdd */ + 40, /* 0xde */ + 0, /* 0xdf */ + 0, /* 0xe0 */ + 0, /* 0xe1 */ + 86, /* 0xe2 */ + 0, /* 0xe3 */ + 0, /* 0xe4 */ + 0, /* 0xe5 */ + 0, /* 0xe6 */ + 0, /* 0xe7 */ + 0, /* 0xe8 */ + 113, /* 0xe9 */ + 92, /* 0xea */ + 123, /* 0xeb */ + 0, /* 0xec */ + 111, /* 0xed */ + 90, /* 0xee */ + 0, /* 0xef */ + 0, /* 0xf0 */ + 91, /* 0xf1 */ + 0, /* 0xf2 */ + 95, /* 0xf3 */ + 0, /* 0xf4 */ + 94, /* 0xf5 */ + 0, /* 0xf6 */ + 0, /* 0xf7 */ + 0, /* 0xf8 */ + 93, /* 0xf9 */ + 0, /* 0xfa */ + 98, /* 0xfb */ + 0, /* 0xfc */ + 0, /* 0xfd */ + 0, /* 0xfe */ + 0, /* 0xff */ + }; } // namespace platf diff --git a/src/platform/windows/misc.cpp b/src/platform/windows/misc.cpp index 3e0188cd..56988a2f 100644 --- a/src/platform/windows/misc.cpp +++ b/src/platform/windows/misc.cpp @@ -8,7 +8,6 @@ #include #include - // prevent clang format from "optimizing" the header include order // clang-format off #include @@ -25,20 +24,21 @@ #include // clang-format on +#include + #include "src/main.h" #include "src/platform/common.h" #include "src/utility.h" -#include - // UDP_SEND_MSG_SIZE was added in the Windows 10 20H1 SDK #ifndef UDP_SEND_MSG_SIZE - #define UDP_SEND_MSG_SIZE 2 +#define UDP_SEND_MSG_SIZE 2 #endif // PROC_THREAD_ATTRIBUTE_JOB_LIST is currently missing from MinGW headers #ifndef PROC_THREAD_ATTRIBUTE_JOB_LIST - #define PROC_THREAD_ATTRIBUTE_JOB_LIST ProcThreadAttributeValue(13, FALSE, TRUE, FALSE) +#define PROC_THREAD_ATTRIBUTE_JOB_LIST \ + ProcThreadAttributeValue(13, FALSE, TRUE, FALSE) #endif #ifndef HAS_QOS_FLOWID @@ -51,137 +51,134 @@ typedef UINT32 *PQOS_FLOWID; #include - - using namespace std::literals; namespace platf { - static std::wstring_convert, wchar_t> converter; +static std::wstring_convert, wchar_t> + converter; - bool enabled_mouse_keys = false; - MOUSEKEYS previous_mouse_keys_state; +bool enabled_mouse_keys = false; +MOUSEKEYS previous_mouse_keys_state; - HANDLE qos_handle = nullptr; +HANDLE qos_handle = nullptr; - decltype(QOSCreateHandle) *fn_QOSCreateHandle = nullptr; - decltype(QOSAddSocketToFlow) *fn_QOSAddSocketToFlow = nullptr; - decltype(QOSRemoveSocketFromFlow) *fn_QOSRemoveSocketFromFlow = nullptr; +decltype(QOSCreateHandle) *fn_QOSCreateHandle = nullptr; +decltype(QOSAddSocketToFlow) *fn_QOSAddSocketToFlow = nullptr; +decltype(QOSRemoveSocketFromFlow) *fn_QOSRemoveSocketFromFlow = nullptr; - HANDLE wlan_handle = nullptr; +HANDLE wlan_handle = nullptr; - decltype(WlanOpenHandle) *fn_WlanOpenHandle = nullptr; - decltype(WlanCloseHandle) *fn_WlanCloseHandle = nullptr; - decltype(WlanFreeMemory) *fn_WlanFreeMemory = nullptr; - decltype(WlanEnumInterfaces) *fn_WlanEnumInterfaces = nullptr; - decltype(WlanSetInterface) *fn_WlanSetInterface = nullptr; +decltype(WlanOpenHandle) *fn_WlanOpenHandle = nullptr; +decltype(WlanCloseHandle) *fn_WlanCloseHandle = nullptr; +decltype(WlanFreeMemory) *fn_WlanFreeMemory = nullptr; +decltype(WlanEnumInterfaces) *fn_WlanEnumInterfaces = nullptr; +decltype(WlanSetInterface) *fn_WlanSetInterface = nullptr; - std::filesystem::path - appdata() { +std::filesystem::path appdata() { WCHAR sunshine_path[MAX_PATH]; GetModuleFileNameW(NULL, sunshine_path, _countof(sunshine_path)); - return std::filesystem::path { sunshine_path }.remove_filename() / L"config"sv; - } + return std::filesystem::path{sunshine_path}.remove_filename() / L"config"sv; +} - std::string - from_sockaddr(const sockaddr *const socket_address) { +std::string from_sockaddr(const sockaddr *const socket_address) { char data[INET6_ADDRSTRLEN] = {}; auto family = socket_address->sa_family; if (family == AF_INET6) { - inet_ntop(AF_INET6, &((sockaddr_in6 *) socket_address)->sin6_addr, data, INET6_ADDRSTRLEN); - } - else if (family == AF_INET) { - inet_ntop(AF_INET, &((sockaddr_in *) socket_address)->sin_addr, data, INET_ADDRSTRLEN); + inet_ntop(AF_INET6, &((sockaddr_in6 *)socket_address)->sin6_addr, data, + INET6_ADDRSTRLEN); + } else if (family == AF_INET) { + inet_ntop(AF_INET, &((sockaddr_in *)socket_address)->sin_addr, data, + INET_ADDRSTRLEN); } - return std::string { data }; - } + return std::string{data}; +} - std::pair - from_sockaddr_ex(const sockaddr *const ip_addr) { +std::pair from_sockaddr_ex( + const sockaddr *const ip_addr) { char data[INET6_ADDRSTRLEN] = {}; auto family = ip_addr->sa_family; std::uint16_t port = 0; if (family == AF_INET6) { - inet_ntop(AF_INET6, &((sockaddr_in6 *) ip_addr)->sin6_addr, data, INET6_ADDRSTRLEN); - port = ((sockaddr_in6 *) ip_addr)->sin6_port; - } - else if (family == AF_INET) { - inet_ntop(AF_INET, &((sockaddr_in *) ip_addr)->sin_addr, data, INET_ADDRSTRLEN); - port = ((sockaddr_in *) ip_addr)->sin_port; + inet_ntop(AF_INET6, &((sockaddr_in6 *)ip_addr)->sin6_addr, data, + INET6_ADDRSTRLEN); + port = ((sockaddr_in6 *)ip_addr)->sin6_port; + } else if (family == AF_INET) { + inet_ntop(AF_INET, &((sockaddr_in *)ip_addr)->sin_addr, data, + INET_ADDRSTRLEN); + port = ((sockaddr_in *)ip_addr)->sin_port; } - return { port, std::string { data } }; - } + return {port, std::string{data}}; +} - HDESK - syncThreadDesktop() { +HDESK +syncThreadDesktop() { auto hDesk = OpenInputDesktop(DF_ALLOWOTHERACCOUNTHOOK, FALSE, GENERIC_ALL); if (!hDesk) { - auto err = GetLastError(); - BOOST_LOG(error) << "Failed to Open Input Desktop [0x"sv << util::hex(err).to_string_view() << ']'; + auto err = GetLastError(); + BOOST_LOG(error) << "Failed to Open Input Desktop [0x"sv + << util::hex(err).to_string_view() << ']'; - return nullptr; + return nullptr; } if (!SetThreadDesktop(hDesk)) { - auto err = GetLastError(); - BOOST_LOG(error) << "Failed to sync desktop to thread [0x"sv << util::hex(err).to_string_view() << ']'; + auto err = GetLastError(); + BOOST_LOG(error) << "Failed to sync desktop to thread [0x"sv + << util::hex(err).to_string_view() << ']'; } CloseDesktop(hDesk); return hDesk; - } +} - void - print_status(const std::string_view &prefix, HRESULT status) { +void print_status(const std::string_view &prefix, HRESULT status) { char err_string[1024]; - DWORD bytes = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, - nullptr, - status, - MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), - err_string, - sizeof(err_string), - nullptr); + DWORD bytes = FormatMessage( + FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, nullptr, + status, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), err_string, + sizeof(err_string), nullptr); - BOOST_LOG(error) << prefix << ": "sv << std::string_view { err_string, bytes }; - } + BOOST_LOG(error) << prefix << ": "sv << std::string_view{err_string, bytes}; +} - bool - IsUserAdmin(HANDLE user_token) { +bool IsUserAdmin(HANDLE user_token) { WINBOOL ret; SID_IDENTIFIER_AUTHORITY NtAuthority = SECURITY_NT_AUTHORITY; PSID AdministratorsGroup; - ret = AllocateAndInitializeSid( - &NtAuthority, - 2, - SECURITY_BUILTIN_DOMAIN_RID, - DOMAIN_ALIAS_RID_ADMINS, - 0, 0, 0, 0, 0, 0, - &AdministratorsGroup); + ret = AllocateAndInitializeSid(&NtAuthority, 2, SECURITY_BUILTIN_DOMAIN_RID, + DOMAIN_ALIAS_RID_ADMINS, 0, 0, 0, 0, 0, 0, + &AdministratorsGroup); if (ret) { - if (!CheckTokenMembership(user_token, AdministratorsGroup, &ret)) { - ret = false; - BOOST_LOG(error) << "Failed to verify token membership for administrative access: " << GetLastError(); - } - FreeSid(AdministratorsGroup); - } - else { - BOOST_LOG(error) << "Unable to allocate SID to check administrative access: " << GetLastError(); + if (!CheckTokenMembership(user_token, AdministratorsGroup, &ret)) { + ret = false; + BOOST_LOG(error) << "Failed to verify token membership for " + "administrative access: " + << GetLastError(); + } + FreeSid(AdministratorsGroup); + } else { + BOOST_LOG(error) + << "Unable to allocate SID to check administrative access: " + << GetLastError(); } return ret; - } +} - /** - * @brief Obtain the current sessions user's primary token with elevated privileges. - * @return The user's token. If user has admin capability it will be elevated, otherwise it will be a limited token. On error, `nullptr`. - */ - HANDLE - retrieve_users_token(bool elevated) { +/** + * @brief Obtain the current sessions user's primary token with elevated + * privileges. + * @return The user's token. If user has admin capability it will be elevated, + * otherwise it will be a limited token. On error, `nullptr`. + */ +HANDLE +retrieve_users_token(bool elevated) { DWORD consoleSessionId; HANDLE userToken; TOKEN_ELEVATION_TYPE elevationType; @@ -190,65 +187,82 @@ namespace platf { // Get the session ID of the active console session consoleSessionId = WTSGetActiveConsoleSessionId(); if (0xFFFFFFFF == consoleSessionId) { - // If there is no active console session, log a warning and return null - BOOST_LOG(warning) << "There isn't an active user session, therefore it is not possible to execute commands under the users profile."; - return nullptr; + // If there is no active console session, log a warning and return null + BOOST_LOG(warning) + << "There isn't an active user session, therefore it is not " + "possible to execute commands under the users profile."; + return nullptr; } // Get the user token for the active console session if (!WTSQueryUserToken(consoleSessionId, &userToken)) { - BOOST_LOG(debug) << "QueryUserToken failed, this would prevent commands from launching under the users profile."; - return nullptr; + BOOST_LOG(debug) << "QueryUserToken failed, this would prevent " + "commands from launching under the users profile."; + return nullptr; } // We need to know if this is an elevated token or not. // Get the elevation type of the user token - // Elevation - Default: User is not an admin, UAC enabled/disabled does not matter. - // Elevation - Limited: User is an admin, has UAC enabled. - // Elevation - Full: User is an admin, has UAC disabled. - if (!GetTokenInformation(userToken, TokenElevationType, &elevationType, sizeof(TOKEN_ELEVATION_TYPE), &dwSize)) { - BOOST_LOG(debug) << "Retrieving token information failed: " << GetLastError(); - CloseHandle(userToken); - return nullptr; + // Elevation - Default: User is not an admin, UAC enabled/disabled does not + // matter. Elevation - Limited: User is an admin, has UAC enabled. Elevation + // - Full: User is an admin, has UAC disabled. + if (!GetTokenInformation(userToken, TokenElevationType, &elevationType, + sizeof(TOKEN_ELEVATION_TYPE), &dwSize)) { + BOOST_LOG(debug) << "Retrieving token information failed: " + << GetLastError(); + CloseHandle(userToken); + return nullptr; } // User is currently not an administrator - // The documentation for this scenario is conflicting, so we'll double check to see if user is actually an admin. - if (elevated && (elevationType == TokenElevationTypeDefault && !IsUserAdmin(userToken))) { - // We don't have to strip the token or do anything here, but let's give the user a warning so they're aware what is happening. - BOOST_LOG(warning) << "This command requires elevation and the current user account logged in does not have administrator rights. " - << "For security reasons Sunshine will retain the same access level as the current user and will not elevate it."; + // The documentation for this scenario is conflicting, so we'll double check + // to see if user is actually an admin. + if (elevated && (elevationType == TokenElevationTypeDefault && + !IsUserAdmin(userToken))) { + // We don't have to strip the token or do anything here, but let's give + // the user a warning so they're aware what is happening. + BOOST_LOG(warning) + << "This command requires elevation and the current user account " + "logged in does not have administrator rights. " + << "For security reasons Sunshine will retain the same access " + "level as the current user and will not elevate it."; } - // User has a limited token, this means they have UAC enabled and is an Administrator + // User has a limited token, this means they have UAC enabled and is an + // Administrator if (elevated && elevationType == TokenElevationTypeLimited) { - TOKEN_LINKED_TOKEN linkedToken; - // Retrieve the administrator token that is linked to the limited token - if (!GetTokenInformation(userToken, TokenLinkedToken, reinterpret_cast(&linkedToken), sizeof(TOKEN_LINKED_TOKEN), &dwSize)) { - // If the retrieval failed, log an error message and return null - BOOST_LOG(error) << "Retrieving linked token information failed: " << GetLastError(); + TOKEN_LINKED_TOKEN linkedToken; + // Retrieve the administrator token that is linked to the limited token + if (!GetTokenInformation(userToken, TokenLinkedToken, + reinterpret_cast(&linkedToken), + sizeof(TOKEN_LINKED_TOKEN), &dwSize)) { + // If the retrieval failed, log an error message and return null + BOOST_LOG(error) << "Retrieving linked token information failed: " + << GetLastError(); + CloseHandle(userToken); + + // There is no scenario where this should be hit, except for an + // actual error. + return nullptr; + } + + // Since we need the elevated token, we'll replace it with their + // administrative token. CloseHandle(userToken); - - // There is no scenario where this should be hit, except for an actual error. - return nullptr; - } - - // Since we need the elevated token, we'll replace it with their administrative token. - CloseHandle(userToken); - userToken = linkedToken.LinkedToken; + userToken = linkedToken.LinkedToken; } - // We don't need to do anything for TokenElevationTypeFull users here, because they're already elevated. + // We don't need to do anything for TokenElevationTypeFull users here, + // because they're already elevated. return userToken; - } +} - - /** - * @brief Check if the current process is running with system-level privileges. - * @return `true` if the current process has system-level privileges, `false` otherwise. - */ - bool - is_running_as_system() { +/** + * @brief Check if the current process is running with system-level privileges. + * @return `true` if the current process has system-level privileges, `false` + * otherwise. + */ +bool is_running_as_system() { BOOL ret; PSID SystemSid; DWORD dwSize = SECURITY_MAX_SID_SIZE; @@ -256,165 +270,161 @@ namespace platf { // Allocate memory for the SID structure SystemSid = LocalAlloc(LMEM_FIXED, dwSize); if (SystemSid == nullptr) { - BOOST_LOG(error) << "Failed to allocate memory for the SID structure: " << GetLastError(); - return false; + BOOST_LOG(error) << "Failed to allocate memory for the SID structure: " + << GetLastError(); + return false; } // Create a SID for the local system account ret = CreateWellKnownSid(WinLocalSystemSid, nullptr, SystemSid, &dwSize); if (ret) { - // Check if the current process token contains this SID - if (!CheckTokenMembership(nullptr, SystemSid, &ret)) { - BOOST_LOG(error) << "Failed to check token membership: " << GetLastError(); - ret = false; - } - } - else { - BOOST_LOG(error) << "Failed to create a SID for the local system account. This may happen if the system is out of memory or if the SID buffer is too small: " << GetLastError(); + // Check if the current process token contains this SID + if (!CheckTokenMembership(nullptr, SystemSid, &ret)) { + BOOST_LOG(error) + << "Failed to check token membership: " << GetLastError(); + ret = false; + } + } else { + BOOST_LOG(error) << "Failed to create a SID for the local system " + "account. This may happen if the system is out of " + "memory or if the SID buffer is too small: " + << GetLastError(); } // Free the memory allocated for the SID structure LocalFree(SystemSid); return ret; - } +} - // Note: This does NOT append a null terminator - void - append_string_to_environment_block(wchar_t *env_block, int &offset, const std::wstring &wstr) { - std::memcpy(&env_block[offset], wstr.data(), wstr.length() * sizeof(wchar_t)); +// Note: This does NOT append a null terminator +void append_string_to_environment_block(wchar_t *env_block, int &offset, + const std::wstring &wstr) { + std::memcpy(&env_block[offset], wstr.data(), + wstr.length() * sizeof(wchar_t)); offset += wstr.length(); - } +} - - - LPPROC_THREAD_ATTRIBUTE_LIST - allocate_proc_thread_attr_list(DWORD attribute_count) { +LPPROC_THREAD_ATTRIBUTE_LIST +allocate_proc_thread_attr_list(DWORD attribute_count) { SIZE_T size; InitializeProcThreadAttributeList(NULL, attribute_count, 0, &size); - auto list = (LPPROC_THREAD_ATTRIBUTE_LIST) HeapAlloc(GetProcessHeap(), 0, size); + auto list = + (LPPROC_THREAD_ATTRIBUTE_LIST)HeapAlloc(GetProcessHeap(), 0, size); if (list == NULL) { - return NULL; + return NULL; } if (!InitializeProcThreadAttributeList(list, attribute_count, 0, &size)) { - HeapFree(GetProcessHeap(), 0, list); - return NULL; + HeapFree(GetProcessHeap(), 0, list); + return NULL; } return list; - } +} - void - free_proc_thread_attr_list(LPPROC_THREAD_ATTRIBUTE_LIST list) { +void free_proc_thread_attr_list(LPPROC_THREAD_ATTRIBUTE_LIST list) { DeleteProcThreadAttributeList(list); HeapFree(GetProcessHeap(), 0, list); - } +} - - - /** - * @brief Impersonate the current user and invoke the callback function. - * @param user_token A handle to the user's token that was obtained from the shell. - * @param callback A function that will be executed while impersonating the user. - * @return An `std::error_code` object that will store any error that occurred during the impersonation - */ - std::error_code - impersonate_current_user(HANDLE user_token, std::function callback) { +/** + * @brief Impersonate the current user and invoke the callback function. + * @param user_token A handle to the user's token that was obtained from the + * shell. + * @param callback A function that will be executed while impersonating the + * user. + * @return An `std::error_code` object that will store any error that occurred + * during the impersonation + */ +std::error_code impersonate_current_user(HANDLE user_token, + std::function callback) { std::error_code ec; - // Impersonate the user when launching the process. This will ensure that appropriate access - // checks are done against the user token, not our SYSTEM token. It will also allow network - // shares and mapped network drives to be used as launch targets, since those credentials - // are stored per-user. + // Impersonate the user when launching the process. This will ensure that + // appropriate access checks are done against the user token, not our SYSTEM + // token. It will also allow network shares and mapped network drives to be + // used as launch targets, since those credentials are stored per-user. if (!ImpersonateLoggedOnUser(user_token)) { - auto winerror = GetLastError(); - // Log the failure of impersonating the user and its error code - BOOST_LOG(error) << "Failed to impersonate user: "sv << winerror; - ec = std::make_error_code(std::errc::permission_denied); - return ec; + auto winerror = GetLastError(); + // Log the failure of impersonating the user and its error code + BOOST_LOG(error) << "Failed to impersonate user: "sv << winerror; + ec = std::make_error_code(std::errc::permission_denied); + return ec; } // Execute the callback function while impersonating the user callback(); - // End impersonation of the logged on user. If this fails (which is extremely unlikely), - // we will be running with an unknown user token. The only safe thing to do in that case - // is terminate ourselves. + // End impersonation of the logged on user. If this fails (which is + // extremely unlikely), we will be running with an unknown user token. The + // only safe thing to do in that case is terminate ourselves. if (!RevertToSelf()) { - auto winerror = GetLastError(); - // Log the failure of reverting to self and its error code - BOOST_LOG(fatal) << "Failed to revert to self after impersonation: "sv << winerror; - std::abort(); + auto winerror = GetLastError(); + // Log the failure of reverting to self and its error code + BOOST_LOG(fatal) << "Failed to revert to self after impersonation: "sv + << winerror; + std::abort(); } return ec; - } +} - - - void - adjust_thread_priority(thread_priority_e priority) { +void adjust_thread_priority(thread_priority_e priority) { int win32_priority; switch (priority) { - case thread_priority_e::low: - win32_priority = THREAD_PRIORITY_BELOW_NORMAL; - break; - case thread_priority_e::normal: - win32_priority = THREAD_PRIORITY_NORMAL; - break; - case thread_priority_e::high: - win32_priority = THREAD_PRIORITY_ABOVE_NORMAL; - break; - case thread_priority_e::critical: - win32_priority = THREAD_PRIORITY_HIGHEST; - break; - default: - BOOST_LOG(error) << "Unknown thread priority: "sv << (int) priority; - return; + case thread_priority_e::low: + win32_priority = THREAD_PRIORITY_BELOW_NORMAL; + break; + case thread_priority_e::normal: + win32_priority = THREAD_PRIORITY_NORMAL; + break; + case thread_priority_e::high: + win32_priority = THREAD_PRIORITY_ABOVE_NORMAL; + break; + case thread_priority_e::critical: + win32_priority = THREAD_PRIORITY_HIGHEST; + break; + default: + BOOST_LOG(error) << "Unknown thread priority: "sv << (int)priority; + return; } if (!SetThreadPriority(GetCurrentThread(), win32_priority)) { - auto winerr = GetLastError(); - BOOST_LOG(warning) << "Unable to set thread priority to "sv << win32_priority << ": "sv << winerr; + auto winerr = GetLastError(); + BOOST_LOG(warning) << "Unable to set thread priority to "sv + << win32_priority << ": "sv << winerr; } - } +} - - - - - int64_t - qpc_counter() { +int64_t qpc_counter() { LARGE_INTEGER performace_counter; - if (QueryPerformanceCounter(&performace_counter)) return performace_counter.QuadPart; + if (QueryPerformanceCounter(&performace_counter)) + return performace_counter.QuadPart; return 0; - } +} - std::chrono::nanoseconds - qpc_time_difference(int64_t performance_counter1, int64_t performance_counter2) { +std::chrono::nanoseconds qpc_time_difference(int64_t performance_counter1, + int64_t performance_counter2) { auto get_frequency = []() { - LARGE_INTEGER frequency; - frequency.QuadPart = 0; - QueryPerformanceFrequency(&frequency); - return frequency.QuadPart; + LARGE_INTEGER frequency; + frequency.QuadPart = 0; + QueryPerformanceFrequency(&frequency); + return frequency.QuadPart; }; static const double frequency = get_frequency(); if (frequency) { - return std::chrono::nanoseconds((int64_t) ((performance_counter1 - performance_counter2) * frequency / std::nano::den)); + return std::chrono::nanoseconds( + (int64_t)((performance_counter1 - performance_counter2) * + frequency / std::nano::den)); } return {}; - } +} +// It's not big enough to justify it's own source file :/ +namespace dxgi { +int init(); +} - // It's not big enough to justify it's own source file :/ - namespace dxgi { - int - init(); - } - - bool - init() { - return dxgi::init() != 0; - } +bool init() { return dxgi::init() != 0; } } // namespace platf diff --git a/src/platform/windows/misc.h b/src/platform/windows/misc.h index 9228ce59..413cbe65 100644 --- a/src/platform/windows/misc.h +++ b/src/platform/windows/misc.h @@ -4,20 +4,19 @@ */ #pragma once -#include -#include #include #include +#include +#include + namespace platf { - void - print_status(const std::string_view &prefix, HRESULT status); - HDESK - syncThreadDesktop(); +void print_status(const std::string_view &prefix, HRESULT status); +HDESK +syncThreadDesktop(); - int64_t - qpc_counter(); +int64_t qpc_counter(); - std::chrono::nanoseconds - qpc_time_difference(int64_t performance_counter1, int64_t performance_counter2); +std::chrono::nanoseconds qpc_time_difference(int64_t performance_counter1, + int64_t performance_counter2); } // namespace platf diff --git a/src/sync.h b/src/sync.h index 5b64606e..76fcd183 100644 --- a/src/sync.h +++ b/src/sync.h @@ -10,92 +10,74 @@ namespace sync_util { - template - class sync_t { - public: +template +class sync_t { + public: using value_t = T; using mutex_t = M; - std::lock_guard - lock() { - return std::lock_guard { _lock }; - } + std::lock_guard lock() { return std::lock_guard{_lock}; } template - sync_t(Args &&...args): - raw { std::forward(args)... } {} + sync_t(Args &&...args) : raw{std::forward(args)...} {} - sync_t & - operator=(sync_t &&other) noexcept { - std::lock(_lock, other._lock); + sync_t &operator=(sync_t &&other) noexcept { + std::lock(_lock, other._lock); - raw = std::move(other.raw); + raw = std::move(other.raw); - _lock.unlock(); - other._lock.unlock(); + _lock.unlock(); + other._lock.unlock(); - return *this; + return *this; } - sync_t & - operator=(sync_t &other) noexcept { - std::lock(_lock, other._lock); + sync_t &operator=(sync_t &other) noexcept { + std::lock(_lock, other._lock); - raw = other.raw; + raw = other.raw; - _lock.unlock(); - other._lock.unlock(); + _lock.unlock(); + other._lock.unlock(); - return *this; + return *this; } template - sync_t & - operator=(V &&val) { - auto lg = lock(); + sync_t &operator=(V &&val) { + auto lg = lock(); - raw = val; + raw = val; - return *this; + return *this; } - sync_t & - operator=(const value_t &val) noexcept { - auto lg = lock(); + sync_t &operator=(const value_t &val) noexcept { + auto lg = lock(); - raw = val; + raw = val; - return *this; + return *this; } - sync_t & - operator=(value_t &&val) noexcept { - auto lg = lock(); + sync_t &operator=(value_t &&val) noexcept { + auto lg = lock(); - raw = std::move(val); + raw = std::move(val); - return *this; + return *this; } - value_t * - operator->() { - return &raw; - } + value_t *operator->() { return &raw; } - value_t & - operator*() { - return raw; - } + value_t &operator*() { return raw; } - const value_t & - operator*() const { - return raw; - } + const value_t &operator*() const { return raw; } value_t raw; - private: + private: mutex_t _lock; - }; +}; } // namespace sync_util diff --git a/src/thread_safe.h b/src/thread_safe.h index 8d8e68fb..72892ffa 100644 --- a/src/thread_safe.h +++ b/src/thread_safe.h @@ -4,486 +4,433 @@ */ #pragma once -#include #include #include #include #include #include #include +#include #include #include "utility.h" using namespace std::literals; namespace safe { - template - class event_t { - public: +template +class event_t { + public: using status_t = util::optional_t; template - void - raise(Args &&...args) { - std::lock_guard lg { _lock }; - if (!_continue) { - return; - } + void raise(Args &&...args) { + std::lock_guard lg{_lock}; + if (!_continue) { + return; + } - if constexpr (std::is_same_v, status_t>) { - _status = std::make_optional(std::forward(args)...); - } - else { - _status = status_t { std::forward(args)... }; - } + if constexpr (std::is_same_v, status_t>) { + _status = std::make_optional(std::forward(args)...); + } else { + _status = status_t{std::forward(args)...}; + } - _cv.notify_all(); + _cv.notify_all(); } // pop and view should not be used interchangeably - status_t - pop() { - std::unique_lock ul { _lock }; - - if (!_continue) { - return util::false_v; - } - - while (!_status) { - _cv.wait(ul); + status_t pop() { + std::unique_lock ul{_lock}; if (!_continue) { - return util::false_v; + return util::false_v; } - } - auto val = std::move(_status); - _status = util::false_v; - return val; + while (!_status) { + _cv.wait(ul); + + if (!_continue) { + return util::false_v; + } + } + + auto val = std::move(_status); + _status = util::false_v; + return val; } // pop and view should not be used interchangeably template - status_t - pop(std::chrono::duration delay) { - std::unique_lock ul { _lock }; + status_t pop(std::chrono::duration delay) { + std::unique_lock ul{_lock}; - if (!_continue) { - return util::false_v; - } - - while (!_status) { - if (!_continue || _cv.wait_for(ul, delay) == std::cv_status::timeout) { - return util::false_v; + if (!_continue) { + return util::false_v; } - } - auto val = std::move(_status); - _status = util::false_v; - return val; + while (!_status) { + if (!_continue || + _cv.wait_for(ul, delay) == std::cv_status::timeout) { + return util::false_v; + } + } + + auto val = std::move(_status); + _status = util::false_v; + return val; } // pop and view should not be used interchangeably - const status_t & - view() { - std::unique_lock ul { _lock }; - - if (!_continue) { - return util::false_v; - } - - while (!_status) { - _cv.wait(ul); + const status_t &view() { + std::unique_lock ul{_lock}; if (!_continue) { - return util::false_v; + return util::false_v; } - } - return _status; + while (!_status) { + _cv.wait(ul); + + if (!_continue) { + return util::false_v; + } + } + + return _status; } // pop and view should not be used interchangeably template - status_t - view(std::chrono::duration delay) { - std::unique_lock ul { _lock }; + status_t view(std::chrono::duration delay) { + std::unique_lock ul{_lock}; - if (!_continue) { - return util::false_v; - } - - while (!_status) { - if (!_continue || _cv.wait_for(ul, delay) == std::cv_status::timeout) { - return util::false_v; + if (!_continue) { + return util::false_v; } - } - return _status; + while (!_status) { + if (!_continue || + _cv.wait_for(ul, delay) == std::cv_status::timeout) { + return util::false_v; + } + } + + return _status; } - bool - peek() { - return _continue && (bool) _status; + bool peek() { return _continue && (bool)_status; } + + void stop() { + std::lock_guard lg{_lock}; + + _continue = false; + + _cv.notify_all(); } - void - stop() { - std::lock_guard lg { _lock }; + void reset() { + std::lock_guard lg{_lock}; - _continue = false; + _continue = true; - _cv.notify_all(); + _status = util::false_v; } - void - reset() { - std::lock_guard lg { _lock }; + [[nodiscard]] bool running() const { return _continue; } - _continue = true; - - _status = util::false_v; - } - - [[nodiscard]] bool - running() const { - return _continue; - } - - private: - bool _continue { true }; - status_t _status { util::false_v }; + private: + bool _continue{true}; + status_t _status{util::false_v}; std::condition_variable _cv; std::mutex _lock; - }; +}; - template - class alarm_raw_t { - public: +template +class alarm_raw_t { + public: using status_t = util::optional_t; - void - ring(const status_t &status) { - std::lock_guard lg(_lock); + void ring(const status_t &status) { + std::lock_guard lg(_lock); - _status = status; - _rang = true; - _cv.notify_one(); + _status = status; + _rang = true; + _cv.notify_one(); } - void - ring(status_t &&status) { - std::lock_guard lg(_lock); + void ring(status_t &&status) { + std::lock_guard lg(_lock); - _status = std::move(status); - _rang = true; - _cv.notify_one(); + _status = std::move(status); + _rang = true; + _cv.notify_one(); } template - auto - wait_for(const std::chrono::duration &rel_time) { - std::unique_lock ul(_lock); + auto wait_for(const std::chrono::duration &rel_time) { + std::unique_lock ul(_lock); - return _cv.wait_for(ul, rel_time, [this]() { return _rang; }); + return _cv.wait_for(ul, rel_time, [this]() { return _rang; }); } template - auto - wait_for(const std::chrono::duration &rel_time, Pred &&pred) { - std::unique_lock ul(_lock); + auto wait_for(const std::chrono::duration &rel_time, + Pred &&pred) { + std::unique_lock ul(_lock); - return _cv.wait_for(ul, rel_time, [this, &pred]() { return _rang || pred(); }); + return _cv.wait_for(ul, rel_time, + [this, &pred]() { return _rang || pred(); }); } template - auto - wait_until(const std::chrono::duration &rel_time) { - std::unique_lock ul(_lock); + auto wait_until(const std::chrono::duration &rel_time) { + std::unique_lock ul(_lock); - return _cv.wait_until(ul, rel_time, [this]() { return _rang; }); + return _cv.wait_until(ul, rel_time, [this]() { return _rang; }); } template - auto - wait_until(const std::chrono::duration &rel_time, Pred &&pred) { - std::unique_lock ul(_lock); + auto wait_until(const std::chrono::duration &rel_time, + Pred &&pred) { + std::unique_lock ul(_lock); - return _cv.wait_until(ul, rel_time, [this, &pred]() { return _rang || pred(); }); + return _cv.wait_until(ul, rel_time, + [this, &pred]() { return _rang || pred(); }); } - auto - wait() { - std::unique_lock ul(_lock); - _cv.wait(ul, [this]() { return _rang; }); + auto wait() { + std::unique_lock ul(_lock); + _cv.wait(ul, [this]() { return _rang; }); } template - auto - wait(Pred &&pred) { - std::unique_lock ul(_lock); - _cv.wait(ul, [this, &pred]() { return _rang || pred(); }); + auto wait(Pred &&pred) { + std::unique_lock ul(_lock); + _cv.wait(ul, [this, &pred]() { return _rang || pred(); }); } - const status_t & - status() const { - return _status; + const status_t &status() const { return _status; } + + status_t &status() { return _status; } + + void reset() { + _status = status_t{}; + _rang = false; } - status_t & - status() { - return _status; - } - - void - reset() { - _status = status_t {}; - _rang = false; - } - - private: + private: std::mutex _lock; std::condition_variable _cv; - status_t _status { util::false_v }; - bool _rang { false }; - }; + status_t _status{util::false_v}; + bool _rang{false}; +}; - template - using alarm_t = std::shared_ptr>; +template +using alarm_t = std::shared_ptr>; - template - alarm_t - make_alarm() { +template +alarm_t make_alarm() { return std::make_shared>(); - } +} - template - class queue_t { - public: +template +class queue_t { + public: using status_t = util::optional_t; - queue_t(std::uint32_t max_elements = 32): - _max_elements { max_elements } {} + queue_t(std::uint32_t max_elements = 32) : _max_elements{max_elements} {} template - void - raise(Args &&...args) { - std::lock_guard ul { _lock }; + void raise(Args &&...args) { + std::lock_guard ul{_lock}; - if (!_continue) { + if (!_continue) { + return; + } + + if (_queue.size() == _max_elements) { + _queue.clear(); + } + + _queue.emplace_back(std::forward(args)...); + + _cv.notify_all(); + } + + bool peek() { return _continue && !_queue.empty(); } + + int size() { + if (!_continue) return -1; + + return _queue.size(); + } + + void wait(int max_size) { + while (size() > max_size) { + std::this_thread::sleep_for(1ms); + } return; - } - - if (_queue.size() == _max_elements) { - _queue.clear(); - } - - _queue.emplace_back(std::forward(args)...); - - _cv.notify_all(); - } - - bool - peek() { - return _continue && !_queue.empty(); - } - - int - size() { - if(!_continue) - return -1; - - return _queue.size(); - } - - void - wait(int max_size) { - while(size() > max_size) { - std::this_thread::sleep_for(1ms); - } - return; } template - status_t - pop(std::chrono::duration delay) { - std::unique_lock ul { _lock }; - - if (!_continue) { - return util::false_v; - } - - while (_queue.empty()) { - if (!_continue || _cv.wait_for(ul, delay) == std::cv_status::timeout) { - return util::false_v; - } - } - - auto val = std::move(_queue.front()); - _queue.erase(std::begin(_queue)); - - return val; - } - - status_t - pop() { - std::unique_lock ul { _lock }; - - if (!_continue) { - return util::false_v; - } - - while (_queue.empty()) { - _cv.wait(ul); + status_t pop(std::chrono::duration delay) { + std::unique_lock ul{_lock}; if (!_continue) { - return util::false_v; + return util::false_v; } - } - auto val = std::move(_queue.front()); - _queue.erase(std::begin(_queue)); + while (_queue.empty()) { + if (!_continue || + _cv.wait_for(ul, delay) == std::cv_status::timeout) { + return util::false_v; + } + } - return val; + auto val = std::move(_queue.front()); + _queue.erase(std::begin(_queue)); + + return val; } - std::vector & - unsafe() { - return _queue; + status_t pop() { + std::unique_lock ul{_lock}; + + if (!_continue) { + return util::false_v; + } + + while (_queue.empty()) { + _cv.wait(ul); + + if (!_continue) { + return util::false_v; + } + } + + auto val = std::move(_queue.front()); + _queue.erase(std::begin(_queue)); + + return val; } - void - stop() { - std::lock_guard lg { _lock }; + std::vector &unsafe() { return _queue; } - _continue = false; + void stop() { + std::lock_guard lg{_lock}; - _cv.notify_all(); + _continue = false; + + _cv.notify_all(); } - [[nodiscard]] bool - running() const { - return _continue; - } + [[nodiscard]] bool running() const { return _continue; } - private: - bool _continue { true }; + private: + bool _continue{true}; std::uint32_t _max_elements; std::mutex _lock; std::condition_variable _cv; std::vector _queue; - }; +}; - template - class shared_t { - public: +template +class shared_t { + public: using element_type = T; using construct_f = std::function; using destruct_f = std::function; struct ptr_t { - shared_t *owner; + shared_t *owner; - ptr_t(): - owner { nullptr } {} - explicit ptr_t(shared_t *owner): - owner { owner } {} + ptr_t() : owner{nullptr} {} + explicit ptr_t(shared_t *owner) : owner{owner} {} - ptr_t(ptr_t &&ptr) noexcept: - owner { ptr.owner } { - ptr.owner = nullptr; - } + ptr_t(ptr_t &&ptr) noexcept : owner{ptr.owner} { ptr.owner = nullptr; } - ptr_t(const ptr_t &ptr) noexcept: - owner { ptr.owner } { - if (!owner) { - return; + ptr_t(const ptr_t &ptr) noexcept : owner{ptr.owner} { + if (!owner) { + return; + } + + auto tmp = ptr.owner->ref(); + tmp.owner = nullptr; } - auto tmp = ptr.owner->ref(); - tmp.owner = nullptr; - } + ptr_t &operator=(const ptr_t &ptr) noexcept { + if (!ptr.owner) { + release(); - ptr_t & - operator=(const ptr_t &ptr) noexcept { - if (!ptr.owner) { - release(); + return *this; + } - return *this; + return *this = std::move(*ptr.owner->ref()); } - return *this = std::move(*ptr.owner->ref()); - } + ptr_t &operator=(ptr_t &&ptr) noexcept { + if (owner) { + release(); + } - ptr_t & - operator=(ptr_t &&ptr) noexcept { - if (owner) { - release(); + std::swap(owner, ptr.owner); + + return *this; } - std::swap(owner, ptr.owner); - - return *this; - } - - ~ptr_t() { - if (owner) { - release(); - } - } - - operator bool() const { - return owner != nullptr; - } - - void - release() { - std::lock_guard lg { owner->_lock }; - - if (!--owner->_count) { - owner->_destruct(*get()); - (*this)->~element_type(); + ~ptr_t() { + if (owner) { + release(); + } } - owner = nullptr; - } + operator bool() const { return owner != nullptr; } - element_type * - get() const { - return reinterpret_cast(owner->_object_buf.data()); - } + void release() { + std::lock_guard lg{owner->_lock}; - element_type * - operator->() { - return reinterpret_cast(owner->_object_buf.data()); - } + if (!--owner->_count) { + owner->_destruct(*get()); + (*this)->~element_type(); + } + + owner = nullptr; + } + + element_type *get() const { + return reinterpret_cast(owner->_object_buf.data()); + } + + element_type *operator->() { + return reinterpret_cast(owner->_object_buf.data()); + } }; template - shared_t(FC &&fc, FD &&fd): - _construct { std::forward(fc) }, _destruct { std::forward(fd) } {} - [[nodiscard]] ptr_t - ref() { - std::lock_guard lg { _lock }; + shared_t(FC &&fc, FD &&fd) + : _construct{std::forward(fc)}, _destruct{std::forward(fd)} {} + [[nodiscard]] ptr_t ref() { + std::lock_guard lg{_lock}; - if (!_count) { - new (_object_buf.data()) element_type; - if (_construct(*reinterpret_cast(_object_buf.data()))) { - return ptr_t { nullptr }; + if (!_count) { + new (_object_buf.data()) element_type; + if (_construct( + *reinterpret_cast(_object_buf.data()))) { + return ptr_t{nullptr}; + } } - } - ++_count; + ++_count; - return ptr_t { this }; + return ptr_t{this}; } - private: + private: construct_f _construct; destruct_f _destruct; @@ -491,45 +438,39 @@ namespace safe { std::uint32_t _count; std::mutex _lock; - }; +}; - template - auto - make_shared(F_Construct &&fc, F_Destruct &&fd) { - return shared_t { - std::forward(fc), std::forward(fd) - }; - } +template +auto make_shared(F_Construct &&fc, F_Destruct &&fd) { + return shared_t{std::forward(fc), + std::forward(fd)}; +} - using signal_t = event_t; +using signal_t = event_t; - class mail_raw_t; - using mail_t = std::shared_ptr; +class mail_raw_t; +using mail_t = std::shared_ptr; - void - cleanup(mail_raw_t *); - template - class post_t: public T { - public: +void cleanup(mail_raw_t *); +template +class post_t : public T { + public: template - post_t(mail_t mail, Args &&...args): - T(std::forward(args)...), mail { std::move(mail) } {} + post_t(mail_t mail, Args &&...args) + : T(std::forward(args)...), mail{std::move(mail)} {} mail_t mail; - ~post_t() { - cleanup(mail.get()); - } - }; + ~post_t() { cleanup(mail.get()); } +}; - template - inline auto - lock(const std::weak_ptr &wp) { +template +inline auto lock(const std::weak_ptr &wp) { return std::reinterpret_pointer_cast(wp.lock()); - } +} - class mail_raw_t: public std::enable_shared_from_this { - public: +class mail_raw_t : public std::enable_shared_from_this { + public: template using event_t = std::shared_ptr>>; @@ -537,59 +478,58 @@ namespace safe { using queue_t = std::shared_ptr>>; template - event_t - event(const std::string_view &id) { - std::lock_guard lg { mutex }; + event_t event(const std::string_view &id) { + std::lock_guard lg{mutex}; - auto it = id_to_post.find(id); - if (it != std::end(id_to_post)) { - return lock>(it->second); - } + auto it = id_to_post.find(id); + if (it != std::end(id_to_post)) { + return lock>(it->second); + } - auto post = std::make_shared::element_type>(shared_from_this()); - id_to_post.emplace(std::pair> { std::string { id }, post }); + auto post = std::make_shared::element_type>( + shared_from_this()); + id_to_post.emplace( + std::pair>{std::string{id}, post}); - return post; + return post; } template - queue_t - queue(const std::string_view &id) { - std::lock_guard lg { mutex }; + queue_t queue(const std::string_view &id) { + std::lock_guard lg{mutex}; - auto it = id_to_post.find(id); - if (it != std::end(id_to_post)) { - return lock>(it->second); - } + auto it = id_to_post.find(id); + if (it != std::end(id_to_post)) { + return lock>(it->second); + } - auto post = std::make_shared::element_type>(shared_from_this(), 32); - id_to_post.emplace(std::pair> { std::string { id }, post }); + auto post = std::make_shared::element_type>( + shared_from_this(), 32); + id_to_post.emplace( + std::pair>{std::string{id}, post}); - return post; + return post; } - void - cleanup() { - std::lock_guard lg { mutex }; + void cleanup() { + std::lock_guard lg{mutex}; - for (auto it = std::begin(id_to_post); it != std::end(id_to_post); ++it) { - auto &weak = it->second; + for (auto it = std::begin(id_to_post); it != std::end(id_to_post); + ++it) { + auto &weak = it->second; - if (weak.expired()) { - id_to_post.erase(it); + if (weak.expired()) { + id_to_post.erase(it); - return; + return; + } } - } } std::mutex mutex; std::map, std::less<>> id_to_post; - }; +}; - inline void - cleanup(mail_raw_t *mail) { - mail->cleanup(); - } +inline void cleanup(mail_raw_t *mail) { mail->cleanup(); } } // namespace safe diff --git a/src/utility.h b/src/utility.h index 3ed32120..e08149c2 100644 --- a/src/utility.h +++ b/src/utility.h @@ -16,1023 +16,889 @@ #include #define KITTY_WHILE_LOOP(x, y, z) \ - { \ - x; \ - while (y) z \ - } + { \ + x; \ + while (y) z \ + } template struct argument_type; template struct argument_type { - typedef U type; + typedef U type; }; -#define KITTY_USING_MOVE_T(move_t, t, init_val, z) \ - class move_t { \ - public: \ - using element_type = typename argument_type::type; \ - \ - move_t(): el { init_val } {} \ - template \ - move_t(Args &&...args): el { std::forward(args)... } {} \ - move_t(const move_t &) = delete; \ - \ - move_t(move_t &&other) noexcept: el { std::move(other.el) } { \ - other.el = element_type { init_val }; \ - } \ - \ - move_t & \ - operator=(const move_t &) = delete; \ - \ - move_t & \ - operator=(move_t &&other) { \ - std::swap(el, other.el); \ - return *this; \ - } \ - element_type * \ - operator->() { return ⪙ } \ - const element_type * \ - operator->() const { return ⪙ } \ - \ - inline element_type \ - release() { \ - element_type val = std::move(el); \ - el = element_type { init_val }; \ - return val; \ - } \ - \ - ~move_t() z \ - \ - element_type el; \ - } +#define KITTY_USING_MOVE_T(move_t, t, init_val, z) \ + class move_t { \ + public: \ + using element_type = typename argument_type::type; \ + \ + move_t() : el{init_val} {} \ + template \ + move_t(Args &&...args) : el{std::forward(args)...} {} \ + move_t(const move_t &) = delete; \ + \ + move_t(move_t &&other) noexcept : el{std::move(other.el)} { \ + other.el = element_type{init_val}; \ + } \ + \ + move_t &operator=(const move_t &) = delete; \ + \ + move_t &operator=(move_t &&other) { \ + std::swap(el, other.el); \ + return *this; \ + } \ + element_type *operator->() { return ⪙ } \ + const element_type *operator->() const { return ⪙ } \ + \ + inline element_type release() { \ + element_type val = std::move(el); \ + el = element_type{init_val}; \ + return val; \ + } \ + \ + ~move_t() z \ + \ + element_type el; \ + } -#define KITTY_DECL_CONSTR(x) \ - x(x &&) noexcept = default; \ - x &operator=(x &&) noexcept = default; \ - x(); +#define KITTY_DECL_CONSTR(x) \ + x(x &&) noexcept = default; \ + x &operator=(x &&) noexcept = default; \ + x(); #define KITTY_DEFAULT_CONSTR_MOVE(x) \ - x(x &&) noexcept = default; \ - x &operator=(x &&) noexcept = default; + x(x &&) noexcept = default; \ + x &operator=(x &&) noexcept = default; #define KITTY_DEFAULT_CONSTR_MOVE_THROW(x) \ - x(x &&) = default; \ - x &operator=(x &&) = default; \ - x() = default; + x(x &&) = default; \ + x &operator=(x &&) = default; \ + x() = default; -#define KITTY_DEFAULT_CONSTR(x) \ - KITTY_DEFAULT_CONSTR_MOVE(x) \ - x(const x &) noexcept = default; \ - x &operator=(const x &) = default; +#define KITTY_DEFAULT_CONSTR(x) \ + KITTY_DEFAULT_CONSTR_MOVE(x) \ + x(const x &) noexcept = default; \ + x &operator=(const x &) = default; -#define TUPLE_2D(a, b, expr) \ - decltype(expr) a##_##b = expr; \ - auto &a = std::get<0>(a##_##b); \ - auto &b = std::get<1>(a##_##b) +#define TUPLE_2D(a, b, expr) \ + decltype(expr) a##_##b = expr; \ + auto &a = std::get<0>(a##_##b); \ + auto &b = std::get<1>(a##_##b) -#define TUPLE_2D_REF(a, b, expr) \ - auto &a##_##b = expr; \ - auto &a = std::get<0>(a##_##b); \ - auto &b = std::get<1>(a##_##b) +#define TUPLE_2D_REF(a, b, expr) \ + auto &a##_##b = expr; \ + auto &a = std::get<0>(a##_##b); \ + auto &b = std::get<1>(a##_##b) -#define TUPLE_3D(a, b, c, expr) \ - decltype(expr) a##_##b##_##c = expr; \ - auto &a = std::get<0>(a##_##b##_##c); \ - auto &b = std::get<1>(a##_##b##_##c); \ - auto &c = std::get<2>(a##_##b##_##c) +#define TUPLE_3D(a, b, c, expr) \ + decltype(expr) a##_##b##_##c = expr; \ + auto &a = std::get<0>(a##_##b##_##c); \ + auto &b = std::get<1>(a##_##b##_##c); \ + auto &c = std::get<2>(a##_##b##_##c) -#define TUPLE_3D_REF(a, b, c, expr) \ - auto &a##_##b##_##c = expr; \ - auto &a = std::get<0>(a##_##b##_##c); \ - auto &b = std::get<1>(a##_##b##_##c); \ - auto &c = std::get<2>(a##_##b##_##c) +#define TUPLE_3D_REF(a, b, c, expr) \ + auto &a##_##b##_##c = expr; \ + auto &a = std::get<0>(a##_##b##_##c); \ + auto &b = std::get<1>(a##_##b##_##c); \ + auto &c = std::get<2>(a##_##b##_##c) -#define TUPLE_EL(a, b, expr) \ - decltype(expr) a##_ = expr; \ - auto &a = std::get(a##_) +#define TUPLE_EL(a, b, expr) \ + decltype(expr) a##_ = expr; \ + auto &a = std::get(a##_) -#define TUPLE_EL_REF(a, b, expr) \ - auto &a = std::get(expr) +#define TUPLE_EL_REF(a, b, expr) auto &a = std::get(expr) namespace util { - template