From 5e40248fdf5941afcf7bd233cbefc6d270b667d7 Mon Sep 17 00:00:00 2001 From: ReenigneArcher <42013603+ReenigneArcher@users.noreply.github.com> Date: Thu, 15 Dec 2022 09:31:45 -0500 Subject: [PATCH 01/88] update ffmpeg submodules (#565) --- third-party/ffmpeg-linux-aarch64 | 2 +- third-party/ffmpeg-linux-x86_64 | 2 +- third-party/ffmpeg-macos-x86_64 | 2 +- third-party/ffmpeg-windows-x86_64 | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/third-party/ffmpeg-linux-aarch64 b/third-party/ffmpeg-linux-aarch64 index a6c2a00dd83..d73b78136b9 160000 --- a/third-party/ffmpeg-linux-aarch64 +++ b/third-party/ffmpeg-linux-aarch64 @@ -1 +1 @@ -Subproject commit a6c2a00dd834337de6781b589d820ba81a1dbcbc +Subproject commit d73b78136b91da93cd60eeb3c0dea121a4f7d2b7 diff --git a/third-party/ffmpeg-linux-x86_64 b/third-party/ffmpeg-linux-x86_64 index 672397dfcd2..d4f0e85a7e6 160000 --- a/third-party/ffmpeg-linux-x86_64 +++ b/third-party/ffmpeg-linux-x86_64 @@ -1 +1 @@ -Subproject commit 672397dfcd25616aec33ff50ac3a28c39dbee6e7 +Subproject commit d4f0e85a7e67af5033b731c0c60de841d5b76076 diff --git a/third-party/ffmpeg-macos-x86_64 b/third-party/ffmpeg-macos-x86_64 index 40b2c2b2abc..de823e0779b 160000 --- a/third-party/ffmpeg-macos-x86_64 +++ b/third-party/ffmpeg-macos-x86_64 @@ -1 +1 @@ -Subproject commit 40b2c2b2abc3b8a4ee19dd8ee38ec4d12bcf3cd6 +Subproject commit de823e0779b7606bdd8420f92ac7381b1fb205e0 diff --git a/third-party/ffmpeg-windows-x86_64 b/third-party/ffmpeg-windows-x86_64 index 862c4dfc234..495ac7502e4 160000 --- a/third-party/ffmpeg-windows-x86_64 +++ b/third-party/ffmpeg-windows-x86_64 @@ -1 +1 @@ -Subproject commit 862c4dfc2346ba0bad6bf478ad346bcf1ff30325 +Subproject commit 495ac7502e4e81bbb24ed7fe71bb459f71b6673c From a4a5d30603c743f9b6d17014f207c07a660f98e6 Mon Sep 17 00:00:00 2001 From: Conn O'Griofa Date: Thu, 15 Dec 2022 16:23:59 +0000 Subject: [PATCH 02/88] AMD encoder improvements (#480) --- src/video.cpp | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/src/video.cpp b/src/video.cpp index bec77c81b9d..e847b629f4e 100644 --- a/src/video.cpp +++ b/src/video.cpp @@ -455,21 +455,29 @@ static encoder_t amdvce { AV_PIX_FMT_NV12, AV_PIX_FMT_P010, { { + { "enforce_hrd"s, true }, + { "gops_per_idr"s, 1 }, { "header_insertion_mode"s, "idr"s }, - { "gops_per_idr"s, 30 }, - { "usage"s, "ultralowlatency"s }, + { "qmax"s, 51 }, + { "qmin"s, 0 }, { "quality"s, &config::video.amd.quality }, { "rc"s, &config::video.amd.rc_hevc }, + { "usage"s, "ultralowlatency"s }, + { "vbaq"s, true }, }, std::make_optional({ "qp_p"s, &config::video.qp }), "hevc_amf"s, }, { { - { "usage"s, "ultralowlatency"s }, + { "enforce_hrd"s, true }, + { "log_to_dbg"s, "1"s }, + { "qmax"s, 51 }, + { "qmin"s, 0 }, { "quality"s, &config::video.amd.quality }, { "rc"s, &config::video.amd.rc_h264 }, - { "log_to_dbg"s, "1"s }, + { "usage"s, "ultralowlatency"s }, + { "vbaq"s, true }, }, std::make_optional({ "qp_p"s, &config::video.qp }), "h264_amf"s, From cb406bce06dc66942c5712ecca368ff69312cdbc Mon Sep 17 00:00:00 2001 From: Conn O'Griofa Date: Thu, 15 Dec 2022 18:12:25 +0000 Subject: [PATCH 03/88] Video: revert software bitrate change & use more conservative bufsize (#482) --- src/video.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/video.cpp b/src/video.cpp index e847b629f4e..30dda46d7eb 100644 --- a/src/video.cpp +++ b/src/video.cpp @@ -978,9 +978,9 @@ std::optional make_session(const encoder_t &encoder, const config_t & } if(video_format[encoder_t::CBR]) { - auto bitrate = config.bitrate * (hardware ? 1000 : 800); // software bitrate overshoots by ~20% + auto bitrate = config.bitrate * 1000; ctx->rc_max_rate = bitrate; - ctx->rc_buffer_size = bitrate / 10; + ctx->rc_buffer_size = bitrate / ((config.framerate * 10) / 15); ctx->bit_rate = bitrate; ctx->rc_min_rate = bitrate; } From 3fd38b3a2e00394bc14a89a00b054c09b41ab423 Mon Sep 17 00:00:00 2001 From: Conn O'Griofa Date: Thu, 15 Dec 2022 19:18:11 +0000 Subject: [PATCH 04/88] video: reduce nvenc delay/async_depth to 0 (#507) --- src/video.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/video.cpp b/src/video.cpp index 30dda46d7eb..36e2a731db3 100644 --- a/src/video.cpp +++ b/src/video.cpp @@ -418,6 +418,7 @@ static encoder_t nvenc { AV_PIX_FMT_NV12, AV_PIX_FMT_P010, { { + { "delay"s, 0 }, { "forced-idr"s, 1 }, { "zerolatency"s, 1 }, { "preset"s, &config::video.nv.preset }, @@ -428,6 +429,7 @@ static encoder_t nvenc { }, { { + { "delay"s, 0 }, { "forced-idr"s, 1 }, { "zerolatency"s, 1 }, { "preset"s, &config::video.nv.preset }, From 5e6a42abb21388043c0dd786e96512326f4afc88 Mon Sep 17 00:00:00 2001 From: Cameron Gutman Date: Thu, 15 Dec 2022 14:32:31 -0600 Subject: [PATCH 05/88] Migrate to upstream Simple-Web-Server submodule (#517) --- .gitmodules | 2 +- src/confighttp.cpp | 29 +++---- src/nvhttp.cpp | 138 +++++++++++++++++++++++----------- third-party/Simple-Web-Server | 2 +- 4 files changed, 106 insertions(+), 65 deletions(-) diff --git a/.gitmodules b/.gitmodules index 75dd21c9e9c..80f505eba19 100644 --- a/.gitmodules +++ b/.gitmodules @@ -3,7 +3,7 @@ url = https://github.com/moonlight-stream/moonlight-common-c.git [submodule "Simple-Web-Server"] path = third-party/Simple-Web-Server - url = https://github.com/loki-47-6F-64/Simple-Web-Server.git + url = https://gitlab.com/eidheim/Simple-Web-Server.git [submodule "ViGEmClient"] path = third-party/ViGEmClient url = https://github.com/ViGEm/ViGEmClient diff --git a/src/confighttp.cpp b/src/confighttp.cpp index 6cf6b12348a..d6b60c0f2ad 100644 --- a/src/confighttp.cpp +++ b/src/confighttp.cpp @@ -68,7 +68,7 @@ void print_req(const req_https_t &request) { } void send_unauthorized(resp_https_t response, req_https_t request) { - auto address = request->remote_endpoint_address(); + auto address = request->remote_endpoint().address().to_string(); BOOST_LOG(info) << "Web UI: ["sv << address << "] -- not authorized"sv; const SimpleWeb::CaseInsensitiveMultimap headers { { "WWW-Authenticate", R"(Basic realm="Sunshine Gamestream Host", charset="UTF-8")" } @@ -77,7 +77,7 @@ void send_unauthorized(resp_https_t response, req_https_t request) { } void send_redirect(resp_https_t response, req_https_t request, const char *path) { - auto address = request->remote_endpoint_address(); + auto address = request->remote_endpoint().address().to_string(); BOOST_LOG(info) << "Web UI: ["sv << address << "] -- not authorized"sv; const SimpleWeb::CaseInsensitiveMultimap headers { { "Location", path } @@ -86,7 +86,7 @@ void send_redirect(resp_https_t response, req_https_t request, const char *path) } bool authenticate(resp_https_t response, req_https_t request) { - auto address = request->remote_endpoint_address(); + auto address = request->remote_endpoint().address().to_string(); auto ip_type = net::from_address(address); if(ip_type > http::origin_web_ui_allowed) { @@ -636,11 +636,8 @@ void start() { auto port_https = map_port(PORT_HTTPS); - auto ctx = std::make_shared(boost::asio::ssl::context::tls); - ctx->use_certificate_chain_file(config::nvhttp.cert); - ctx->use_private_key_file(config::nvhttp.pkey, boost::asio::ssl::context::pem); - https_server_t server { ctx, 0 }; - server.default_resource = not_found; + https_server_t server { config::nvhttp.cert, config::nvhttp.pkey }; + server.default_resource["GET"] = not_found; server.resource["^/$"]["GET"] = getIndexPage; server.resource["^/pin$"]["GET"] = getPinPage; server.resource["^/apps$"]["GET"] = getAppsPage; @@ -666,19 +663,11 @@ void start() { server.config.address = "0.0.0.0"s; server.config.port = port_https; - try { - server.bind(); - BOOST_LOG(info) << "Configuration UI available at [https://localhost:"sv << port_https << "]"; - } - catch(boost::system::system_error &err) { - BOOST_LOG(fatal) << "Couldn't bind http server to ports ["sv << port_https << "]: "sv << err.what(); - - shutdown_event->raise(true); - return; - } auto accept_and_run = [&](auto *server) { try { - server->accept_and_run(); + server->start([](unsigned short port) { + BOOST_LOG(info) << "Configuration UI available at [https://localhost:"sv << port << "]"; + }); } catch(boost::system::system_error &err) { // It's possible the exception gets thrown after calling server->stop() from a different thread @@ -686,7 +675,7 @@ void start() { return; } - BOOST_LOG(fatal) << "Couldn't start Configuration HTTPS server to port ["sv << port_https << "]: "sv << err.what(); + BOOST_LOG(fatal) << "Couldn't start Configuration HTTPS server on port ["sv << port_https << "]: "sv << err.what(); shutdown_event->raise(true); return; } diff --git a/src/nvhttp.cpp b/src/nvhttp.cpp index 47067d2654d..9ffda1b4e70 100644 --- a/src/nvhttp.cpp +++ b/src/nvhttp.cpp @@ -37,7 +37,69 @@ constexpr auto GFE_VERSION = "3.23.0.74"; namespace fs = std::filesystem; namespace pt = boost::property_tree; -using https_server_t = SimpleWeb::Server; +class SunshineHttpsServer : public SimpleWeb::Server { +public: + SunshineHttpsServer(const std::string &certification_file, const std::string &private_key_file) + : SimpleWeb::Server::Server(certification_file, private_key_file) {} + + std::function verify; + std::function, std::shared_ptr)> on_verify_failed; + +protected: + void after_bind() override { + SimpleWeb::Server::after_bind(); + + if(verify) { + context.set_verify_mode(boost::asio::ssl::verify_peer | boost::asio::ssl::verify_fail_if_no_peer_cert | boost::asio::ssl::verify_client_once); + context.set_verify_callback([](int verified, boost::asio::ssl::verify_context &ctx) { + // To respond with an error message, a connection must be established + return 1; + }); + } + } + + // This is Server::accept() with SSL validation support added + void accept() override { + auto connection = create_connection(*io_service, context); + + acceptor->async_accept(connection->socket->lowest_layer(), [this, connection](const SimpleWeb::error_code &ec) { + auto lock = connection->handler_runner->continue_lock(); + if(!lock) + return; + + if(ec != SimpleWeb::error::operation_aborted) + this->accept(); + + auto session = std::make_shared(config.max_request_streambuf_size, connection); + + if(!ec) { + boost::asio::ip::tcp::no_delay option(true); + SimpleWeb::error_code ec; + session->connection->socket->lowest_layer().set_option(option, ec); + + session->connection->set_timeout(config.timeout_request); + session->connection->socket->async_handshake(boost::asio::ssl::stream_base::server, [this, session](const SimpleWeb::error_code &ec) { + session->connection->cancel_timeout(); + auto lock = session->connection->handler_runner->continue_lock(); + if(!lock) + return; + if(!ec) { + if(verify && !verify(session->connection->socket->native_handle())) + this->write(session, on_verify_failed); + else + this->read(session); + } + else if(this->on_error) + this->on_error(session->request, ec); + }); + } + else if(this->on_error) + this->on_error(session->request, ec); + }); + } +}; + +using https_server_t = SunshineHttpsServer; using http_server_t = SimpleWeb::Server; struct conf_intern_t { @@ -86,6 +148,14 @@ enum class op_e { REMOVE }; +std::string get_arg(const args_t &args, const char *name) { + auto it = args.find(name); + if(it == std::end(args)) { + throw std::out_of_range(name); + } + return it->second; +} + void save_state() { pt::ptree root; @@ -188,8 +258,8 @@ stream::launch_session_t make_launch_session(bool host_audio, const args_t &args stream::launch_session_t launch_session; launch_session.host_audio = host_audio; - launch_session.gcm_key = util::from_hex(args.at("rikey"s), true); - uint32_t prepend_iv = util::endian::big(util::from_view(args.at("rikeyid"s))); + launch_session.gcm_key = util::from_hex(get_arg(args, "rikey"), true); + uint32_t prepend_iv = util::endian::big(util::from_view(get_arg(args, "rikeyid"))); auto prepend_iv_p = (uint8_t *)&prepend_iv; auto next = std::copy(prepend_iv_p, prepend_iv_p + sizeof(prepend_iv), std::begin(launch_session.iv)); @@ -217,7 +287,7 @@ void getservercert(pair_session_t &sess, pt::ptree &tree, const std::string &pin tree.put("root..status_code", 200); } void serverchallengeresp(pair_session_t &sess, pt::ptree &tree, const args_t &args) { - auto encrypted_response = util::from_hex_vec(args.at("serverchallengeresp"s), true); + auto encrypted_response = util::from_hex_vec(get_arg(args, "serverchallengeresp"), true); std::vector decrypted; crypto::cipher::ecb_t cipher(*sess.cipher_key, false); @@ -237,7 +307,7 @@ void serverchallengeresp(pair_session_t &sess, pt::ptree &tree, const args_t &ar } void clientchallenge(pair_session_t &sess, pt::ptree &tree, const args_t &args) { - auto challenge = util::from_hex_vec(args.at("clientchallenge"s), true); + auto challenge = util::from_hex_vec(get_arg(args, "clientchallenge"), true); crypto::cipher::ecb_t cipher(*sess.cipher_key, false); @@ -274,7 +344,7 @@ void clientchallenge(pair_session_t &sess, pt::ptree &tree, const args_t &args) void clientpairingsecret(std::shared_ptr> &add_cert, pair_session_t &sess, pt::ptree &tree, const args_t &args) { auto &client = sess.client; - auto pairingsecret = util::from_hex_vec(args.at("clientpairingsecret"), true); + auto pairingsecret = util::from_hex_vec(get_arg(args, "clientpairingsecret"), true); std::string_view secret { pairingsecret.data(), 16 }; std::string_view sign { pairingsecret.data() + secret.size(), crypto::digest_size }; @@ -391,7 +461,7 @@ void pair(std::shared_ptr> &add_cert, std::shared_ return; } - auto uniqID { std::move(args.at("uniqueid"s)) }; + auto uniqID { std::move(get_arg(args, "uniqueid")) }; auto sess_it = map_id_sess.find(uniqID); args_t::const_iterator it; @@ -400,12 +470,12 @@ void pair(std::shared_ptr> &add_cert, std::shared_ pair_session_t sess; sess.client.uniqueID = std::move(uniqID); - sess.client.cert = util::from_hex_vec(args.at("clientcert"s), true); + sess.client.cert = util::from_hex_vec(get_arg(args, "clientcert"), true); BOOST_LOG(debug) << sess.client.cert; auto ptr = map_id_sess.emplace(sess.client.uniqueID, std::move(sess)).first; - ptr->second.async_insert_pin.salt = std::move(args.at("salt"s)); + ptr->second.async_insert_pin.salt = std::move(get_arg(args, "salt")); if(config::sunshine.flags[config::flag::PIN_STDIN]) { std::string pin; @@ -477,7 +547,7 @@ void pin(std::shared_ptr::Response> response, response->close_connection_after_response = true; - auto address = request->remote_endpoint_address(); + auto address = request->remote_endpoint().address().to_string(); auto ip_type = net::from_address(address); if(ip_type > http::origin_pin_allowed) { BOOST_LOG(info) << "/pin: ["sv << address << "] -- denied"sv; @@ -513,6 +583,8 @@ void serverinfo(std::shared_ptr::Response> res } } + auto local_endpoint = request->local_endpoint(); + pt::ptree tree; tree.put("root..status_code", 200); @@ -523,9 +595,9 @@ void serverinfo(std::shared_ptr::Response> res tree.put("root.uniqueid", http::unique_id); tree.put("root.HttpsPort", map_port(PORT_HTTPS)); tree.put("root.ExternalPort", map_port(PORT_HTTP)); - tree.put("root.mac", platf::get_mac_address(request->local_endpoint_address())); + tree.put("root.mac", platf::get_mac_address(local_endpoint.address().to_string())); tree.put("root.MaxLumaPixelsHEVC", config::video.hevc_mode > 1 ? "1869449984" : "0"); - tree.put("root.LocalIP", request->local_endpoint_address()); + tree.put("root.LocalIP", local_endpoint.address().to_string()); if(config::video.hevc_mode == 3) { tree.put("root.ServerCodecModeSupport", "3843"); @@ -598,7 +670,7 @@ void applist(resp_https_t response, req_https_t request) { return; } - auto clientID = args.at("uniqueid"s); + auto clientID = get_arg(args, "uniqueid"); auto client = map_id_client.find(clientID); if(client == std::end(map_id_client)) { @@ -655,7 +727,7 @@ void launch(bool &host_audio, resp_https_t response, req_https_t request) { return; } - auto appid = util::from_view(args.at("appid")) - 1; + auto appid = util::from_view(get_arg(args, "appid")) - 1; auto current_appid = proc::proc.running(); if(current_appid != -1) { @@ -675,11 +747,11 @@ void launch(bool &host_audio, resp_https_t response, req_https_t request) { } } - host_audio = util::from_view(args.at("localAudioPlayMode")); + host_audio = util::from_view(get_arg(args, "localAudioPlayMode")); stream::launch_session_raise(make_launch_session(host_audio, args)); tree.put("root..status_code", 200); - tree.put("root.sessionUrl0", "rtsp://"s + request->local_endpoint_address() + ':' + std::to_string(map_port(stream::RTSP_SETUP_PORT))); + tree.put("root.sessionUrl0", "rtsp://"s + request->local_endpoint().address().to_string() + ':' + std::to_string(map_port(stream::RTSP_SETUP_PORT))); tree.put("root.gamesession", 1); } @@ -726,7 +798,7 @@ void resume(bool &host_audio, resp_https_t response, req_https_t request) { stream::launch_session_raise(make_launch_session(host_audio, args)); tree.put("root..status_code", 200); - tree.put("root.sessionUrl0", "rtsp://"s + request->local_endpoint_address() + ':' + std::to_string(map_port(stream::RTSP_SETUP_PORT))); + tree.put("root.sessionUrl0", "rtsp://"s + request->local_endpoint().address().to_string() + ':' + std::to_string(map_port(stream::RTSP_SETUP_PORT))); tree.put("root.resume", 1); } @@ -764,7 +836,7 @@ void appasset(resp_https_t response, req_https_t request) { print_req(request); auto args = request->parse_query_string(); - auto app_image = proc::proc.get_app_image(util::from_view(args.at("appid"))); + auto app_image = proc::proc.get_app_image(util::from_view(get_arg(args, "appid"))); std::ifstream in(app_image, std::ios::binary); SimpleWeb::CaseInsensitiveMultimap headers; @@ -788,10 +860,6 @@ void start() { conf_intern.pkey = read_file(config::nvhttp.pkey.c_str()); conf_intern.servercert = read_file(config::nvhttp.cert.c_str()); - auto ctx = std::make_shared(boost::asio::ssl::context::tls); - ctx->use_certificate_chain_file(config::nvhttp.cert); - ctx->use_private_key_file(config::nvhttp.pkey, boost::asio::ssl::context::pem); - crypto::cert_chain_t cert_chain; for(auto &[_, client] : map_id_client) { for(auto &cert : client.certs) { @@ -801,16 +869,11 @@ void start() { auto add_cert = std::make_shared>(30); - ctx->set_verify_callback([](int verified, boost::asio::ssl::verify_context &ctx) { - // To respond with an error message, a connection must be established - return 1; - }); - // /resume doesn't get the parameter "localAudioPlayMode" // /launch will store it in host_audio bool host_audio {}; - https_server_t https_server { ctx, boost::asio::ssl::verify_peer | boost::asio::ssl::verify_fail_if_no_peer_cert | boost::asio::ssl::verify_client_once }; + https_server_t https_server { config::nvhttp.cert, config::nvhttp.pkey }; http_server_t http_server; // Verify certificates after establishing connection @@ -870,7 +933,7 @@ void start() { tree.put("root..status_message"s, "The client is not authorized. Certificate verification failed."s); }; - https_server.default_resource = not_found; + https_server.default_resource["GET"] = not_found; https_server.resource["^/serverinfo$"]["GET"] = serverinfo; https_server.resource["^/pair$"]["GET"] = [&add_cert](auto resp, auto req) { pair(add_cert, resp, req); }; https_server.resource["^/applist$"]["GET"] = applist; @@ -884,7 +947,7 @@ void start() { https_server.config.address = "0.0.0.0"s; https_server.config.port = port_https; - http_server.default_resource = not_found; + http_server.default_resource["GET"] = not_found; http_server.resource["^/serverinfo$"]["GET"] = serverinfo; http_server.resource["^/pair$"]["GET"] = [&add_cert](auto resp, auto req) { pair(add_cert, resp, req); }; http_server.resource["^/pin/([0-9]+)$"]["GET"] = pin; @@ -893,20 +956,9 @@ void start() { http_server.config.address = "0.0.0.0"s; http_server.config.port = port_http; - try { - https_server.bind(); - http_server.bind(); - } - catch(boost::system::system_error &err) { - BOOST_LOG(fatal) << "Couldn't bind http server to ports ["sv << port_http << ", "sv << port_http << "]: "sv << err.what(); - - shutdown_event->raise(true); - return; - } - auto accept_and_run = [&](auto *http_server) { try { - http_server->accept_and_run(); + http_server->start(); } catch(boost::system::system_error &err) { // It's possible the exception gets thrown after calling http_server->stop() from a different thread @@ -914,7 +966,7 @@ void start() { return; } - BOOST_LOG(fatal) << "Couldn't start http server to ports ["sv << port_https << ", "sv << port_https << "]: "sv << err.what(); + BOOST_LOG(fatal) << "Couldn't start http server on ports ["sv << port_https << ", "sv << port_https << "]: "sv << err.what(); shutdown_event->raise(true); return; } diff --git a/third-party/Simple-Web-Server b/third-party/Simple-Web-Server index 3ae451038ff..2f29926dbbc 160000 --- a/third-party/Simple-Web-Server +++ b/third-party/Simple-Web-Server @@ -1 +1 @@ -Subproject commit 3ae451038ff151b9abeb0a945c8c9241653420c7 +Subproject commit 2f29926dbbcd8a0425064d98c24f37ac50bd0b5b From a1d07ff0e0089330a3039932bb58e4ebd31a72f0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 15 Dec 2022 16:07:24 -0500 Subject: [PATCH 06/88] Bump third-party/TPCircularBuffer from `bce9170` to `8833b3a` (#555) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- third-party/TPCircularBuffer | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/third-party/TPCircularBuffer b/third-party/TPCircularBuffer index bce9170d9e8..8833b3a73fa 160000 --- a/third-party/TPCircularBuffer +++ b/third-party/TPCircularBuffer @@ -1 +1 @@ -Subproject commit bce9170d9e8e566fb33d56136ec6e4b97f91ed2d +Subproject commit 8833b3a73fab6530cc51e2063a85cced01714cfb From 63c105bfb1427bf3afe2245bb48fc80ac4f3bb73 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 15 Dec 2022 16:43:57 -0500 Subject: [PATCH 07/88] Bump third-party/moonlight-common-c from `8169a31` to `9da6329` (#559) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- third-party/moonlight-common-c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/third-party/moonlight-common-c b/third-party/moonlight-common-c index 8169a31ecc0..9da63294964 160000 --- a/third-party/moonlight-common-c +++ b/third-party/moonlight-common-c @@ -1 +1 @@ -Subproject commit 8169a31ecc07a5726fea3f7b809e6fbff312c724 +Subproject commit 9da632949649e8b6ea30887fdcbdc12c7c540b38 From a54830cf39013a1e3d6e11adae990d08fe71e726 Mon Sep 17 00:00:00 2001 From: Conn O'Griofa Date: Fri, 16 Dec 2022 01:37:55 +0000 Subject: [PATCH 08/88] video: vaapi: reduce async_depth to 1 (#545) --- src/video.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/video.cpp b/src/video.cpp index 36e2a731db3..70d7a6c4bcd 100644 --- a/src/video.cpp +++ b/src/video.cpp @@ -531,6 +531,7 @@ static encoder_t vaapi { AV_PIX_FMT_NV12, AV_PIX_FMT_YUV420P10, { { + { "async_depth"s, 1 }, { "sei"s, 0 }, { "idr_interval"s, std::numeric_limits::max() }, }, @@ -539,6 +540,7 @@ static encoder_t vaapi { }, { { + { "async_depth"s, 1 }, { "sei"s, 0 }, { "idr_interval"s, std::numeric_limits::max() }, }, From f4edce318cbaba8402418940e39a7ce3f0b92a42 Mon Sep 17 00:00:00 2001 From: Cameron Gutman Date: Thu, 15 Dec 2022 20:21:57 -0600 Subject: [PATCH 09/88] Avoid using functions deprecated in OpenSSL 3.0 (#515) --- src/crypto.cpp | 21 ++++++--------------- src/crypto.h | 1 + 2 files changed, 7 insertions(+), 15 deletions(-) diff --git a/src/crypto.cpp b/src/crypto.cpp index 45b65abcff7..c547b944aa2 100644 --- a/src/crypto.cpp +++ b/src/crypto.cpp @@ -4,8 +4,6 @@ #include namespace crypto { -using big_num_t = util::safe_ptr; -// using rsa_t = util::safe_ptr; using asn1_string_t = util::safe_ptr; cert_chain_t::cert_chain_t() : _certs {}, _cert_ctx { X509_STORE_CTX_new() } {} @@ -315,12 +313,7 @@ aes_t gen_aes_key(const std::array &salt, const std::string_view &p sha256_t hash(const std::string_view &plaintext) { sha256_t hsh; - - SHA256_CTX sha256; - SHA256_Init(&sha256); - SHA256_Update(&sha256, plaintext.data(), plaintext.size()); - SHA256_Final(hsh.data(), &sha256); - + EVP_Digest(plaintext.data(), plaintext.size(), hsh.data(), nullptr, EVP_sha256(), nullptr); return hsh; } @@ -409,14 +402,12 @@ std::vector sign(const pkey_t &pkey, const std::string_view &data, cons creds_t gen_creds(const std::string_view &cn, std::uint32_t key_bits) { x509_t x509 { X509_new() }; - pkey_t pkey { EVP_PKEY_new() }; - - big_num_t big_num { BN_new() }; - BN_set_word(big_num.get(), RSA_F4); + pkey_ctx_t ctx { EVP_PKEY_CTX_new_id(EVP_PKEY_RSA, nullptr) }; + pkey_t pkey; - auto rsa = RSA_new(); - RSA_generate_key_ex(rsa, key_bits, big_num.get(), nullptr); - EVP_PKEY_assign_RSA(pkey.get(), rsa); + EVP_PKEY_keygen_init(ctx.get()); + EVP_PKEY_CTX_set_rsa_keygen_bits(ctx.get(), key_bits); + EVP_PKEY_keygen(ctx.get(), &pkey); X509_set_version(x509.get(), 2); ASN1_INTEGER_set(X509_get_serialNumber(x509.get()), 0); diff --git a/src/crypto.h b/src/crypto.h index 46259d1a7b7..b2b1ac122cd 100644 --- a/src/crypto.h +++ b/src/crypto.h @@ -30,6 +30,7 @@ using cipher_ctx_t = util::safe_ptr; using md_ctx_t = util::safe_ptr; using bio_t = util::safe_ptr; using pkey_t = util::safe_ptr; +using pkey_ctx_t = util::safe_ptr; sha256_t hash(const std::string_view &plaintext); From 66f46ab3419fe94d265be3a2fab3a2f367c04ef7 Mon Sep 17 00:00:00 2001 From: Cameron Gutman Date: Fri, 16 Dec 2022 13:53:34 -0600 Subject: [PATCH 10/88] Send valid frame type value in frame header (#516) --- src/stream.cpp | 29 +++++++++++++++++++++++++++-- 1 file changed, 27 insertions(+), 2 deletions(-) diff --git a/src/stream.cpp b/src/stream.cpp index 7a357c129cc..240ca7e4162 100644 --- a/src/stream.cpp +++ b/src/stream.cpp @@ -65,6 +65,28 @@ enum class socket_e : int { #pragma pack(push, 1) +struct video_short_frame_header_t { + uint8_t *payload() { + return (uint8_t *)(this + 1); + } + + std::uint8_t headerType; // Always 0x01 for short headers + std::uint8_t unknown[2]; + + // Currently known values: + // 1 = Normal P-frame + // 2 = IDR-frame + // 4 = P-frame with intra-refresh blocks + // 5 = P-frame after reference frame invalidation + std::uint8_t frameType; + + std::uint8_t unknown2[4]; +}; + +static_assert( + sizeof(video_short_frame_header_t) == 8, + "Short frame header must be 8 bytes"); + struct video_packet_raw_t { uint8_t *payload() { return (uint8_t *)(this + 1); @@ -896,8 +918,11 @@ void videoBroadcastThread(udp::socket &sock) { std::string_view payload { (char *)av_packet->data, (size_t)av_packet->size }; std::vector payload_new; - auto nv_packet_header = "\0017charss"sv; - std::copy(std::begin(nv_packet_header), std::end(nv_packet_header), std::back_inserter(payload_new)); + video_short_frame_header_t frame_header = {}; + frame_header.headerType = 0x01; // Short header type + frame_header.frameType = (av_packet->flags & AV_PKT_FLAG_KEY) ? 2 : 1; + + std::copy_n((uint8_t *)&frame_header, sizeof(frame_header), std::back_inserter(payload_new)); std::copy(std::begin(payload), std::end(payload), std::back_inserter(payload_new)); payload = { (char *)payload_new.data(), payload_new.size() }; From 633f0035c2290c9ea3c6f7419834570abdf6deef Mon Sep 17 00:00:00 2001 From: Cameron Gutman Date: Fri, 16 Dec 2022 14:56:44 -0600 Subject: [PATCH 11/88] Send a valid PTS in the RTP video header (#529) --- src/stream.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/stream.cpp b/src/stream.cpp index 240ca7e4162..4d530998f84 100644 --- a/src/stream.cpp +++ b/src/stream.cpp @@ -905,6 +905,7 @@ void recvThread(broadcast_ctx_t &ctx) { void videoBroadcastThread(udp::socket &sock) { auto shutdown_event = mail::man->event(mail::broadcast_shutdown); auto packets = mail::man->queue(mail::video_packets); + auto timebase = boost::posix_time::microsec_clock::universal_time(); while(auto packet = packets->pop()) { if(shutdown_event->peek()) { @@ -1017,6 +1018,10 @@ void videoBroadcastThread(udp::socket &sock) { for(auto x = 0; x < shards.size(); ++x) { auto *inspect = (video_packet_raw_t *)shards.data(x); + // RTP video timestamps use a 90 KHz clock + auto now = boost::posix_time::microsec_clock::universal_time(); + auto timestamp = (now - timebase).total_microseconds() / (1000 / 90); + inspect->packet.fecInfo = (x << 12 | shards.data_shards << 22 | @@ -1024,12 +1029,11 @@ void videoBroadcastThread(udp::socket &sock) { inspect->rtp.header = 0x80 | FLAG_EXTENSION; inspect->rtp.sequenceNumber = util::endian::big(lowseq + x); + inspect->rtp.timestamp = util::endian::big(timestamp); inspect->packet.multiFecBlocks = (blockIndex << 4) | lastBlockIndex; inspect->packet.frameIndex = av_packet->pts; - } - for(auto x = 0; x < shards.size(); ++x) { sock.send_to(asio::buffer(shards[x]), session->video.peer); } From 223e87f30be16fe831b5b9fb1eff9a05979ffbee Mon Sep 17 00:00:00 2001 From: ReenigneArcher <42013603+ReenigneArcher@users.noreply.github.com> Date: Fri, 16 Dec 2022 17:27:28 -0500 Subject: [PATCH 12/88] set submodule branches (#570) --- .gitmodules | 12 +++++++++--- third-party/nv-codec-headers | 2 +- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/.gitmodules b/.gitmodules index 80f505eba19..1469a6c3713 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,21 +1,27 @@ [submodule "moonlight-common-c"] path = third-party/moonlight-common-c url = https://github.com/moonlight-stream/moonlight-common-c.git + branch = master [submodule "Simple-Web-Server"] path = third-party/Simple-Web-Server url = https://gitlab.com/eidheim/Simple-Web-Server.git + branch = master [submodule "ViGEmClient"] path = third-party/ViGEmClient url = https://github.com/ViGEm/ViGEmClient -[submodule "third-party/miniupnp"] + branch = master +[submodule "miniupnp"] path = third-party/miniupnp url = https://github.com/miniupnp/miniupnp -[submodule "third-party/nv-codec-headers"] + branch = master +[submodule "nv-codec-headers"] path = third-party/nv-codec-headers url = https://github.com/FFmpeg/nv-codec-headers -[submodule "third-party/TPCircularBuffer"] + branch = sdk/11.1 +[submodule "TPCircularBuffer"] path = third-party/TPCircularBuffer url = https://github.com/michaeltyson/TPCircularBuffer + branch = master [submodule "ffmpeg-windows-x86_64"] path = third-party/ffmpeg-windows-x86_64 url = https://github.com/LizardByte/build-deps diff --git a/third-party/nv-codec-headers b/third-party/nv-codec-headers index b641a195edb..b550d4042f1 160000 --- a/third-party/nv-codec-headers +++ b/third-party/nv-codec-headers @@ -1 +1 @@ -Subproject commit b641a195edbe3ac9788e681e22c2e2fad8aacddb +Subproject commit b550d4042f1ac0990efa1fa9f0f0c08fb6b24446 From 66f1ac664ae6b3a72a9e0665d7f17e5ab0a32667 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 16 Dec 2022 18:48:09 -0500 Subject: [PATCH 13/88] Bump third-party/miniupnp from `6f848ae` to `207cf44` (#557) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: ReenigneArcher <42013603+ReenigneArcher@users.noreply.github.com> --- CMakeLists.txt | 2 +- src/upnp.cpp | 4 ++-- third-party/miniupnp | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index fad60ef5e16..035ae69b9ae 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -58,7 +58,7 @@ set(UPNPC_BUILD_TESTS OFF CACHE BOOL "Don't build tests for miniupnpc") set(UPNPC_BUILD_SAMPLE OFF CACHE BOOL "Don't build samples for miniupnpc") set(UPNPC_NO_INSTALL ON CACHE BOOL "Don't install any libraries build for miniupnpc") add_subdirectory(third-party/miniupnp/miniupnpc) -include_directories(third-party/miniupnp) +include_directories(third-party/miniupnp/miniupnpc/include) find_package(Threads REQUIRED) find_package(OpenSSL REQUIRED) diff --git a/src/upnp.cpp b/src/upnp.cpp index a5a99e86fe1..03aedd51606 100644 --- a/src/upnp.cpp +++ b/src/upnp.cpp @@ -1,5 +1,5 @@ -#include -#include +#include +#include #include "config.h" #include "confighttp.h" diff --git a/third-party/miniupnp b/third-party/miniupnp index 6f848ae0821..207cf440a22 160000 --- a/third-party/miniupnp +++ b/third-party/miniupnp @@ -1 +1 @@ -Subproject commit 6f848ae0821f1dd1be393edb52115f36812d3c2c +Subproject commit 207cf440a22c075cb55fb067a850be4f9c204e6e From 302bf58631da4e3a6e416f9779c41bec382b5b6d Mon Sep 17 00:00:00 2001 From: Conn O'Griofa Date: Sat, 17 Dec 2022 20:16:35 +0000 Subject: [PATCH 14/88] CMakeLists: win32: fix build against boost 1.81.0-1+ (#576) --- CMakeLists.txt | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 035ae69b9ae..05086627492 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -78,9 +78,6 @@ if(WIN32) set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -static") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${CURL_STATIC_LDFLAGS} ${CURL_STATIC_CFLAGS}") - # Restrict Boost WinAPI version to work around 1.80 linker errors - ADD_DEFINITIONS(-DBOOST_USE_WINAPI_VERSION=BOOST_WINAPI_VERSION_WIN7) - add_compile_definitions(SUNSHINE_PLATFORM="windows") add_subdirectory(tools) # This is temporary, only tools for Windows are needed, for now @@ -121,6 +118,7 @@ if(WIN32) d3d11 dxgi D3DCompiler setupapi dwmapi + synchronization.lib ${CURL_STATIC_LIBRARIES} ) From 7a23d6aa814667e60192ddf633e4301950ca66e0 Mon Sep 17 00:00:00 2001 From: Brad Richardson Date: Tue, 20 Dec 2022 14:38:50 -0500 Subject: [PATCH 15/88] macOS arm64 (#591) --- .gitmodules | 4 ++++ CMakeLists.txt | 6 +++++- docs/source/building/macos.rst | 2 +- packaging/macos/Portfile | 1 + third-party/ffmpeg-macos-aarch64 | 1 + 5 files changed, 12 insertions(+), 2 deletions(-) create mode 160000 third-party/ffmpeg-macos-aarch64 diff --git a/.gitmodules b/.gitmodules index 1469a6c3713..bf47edeefbb 100644 --- a/.gitmodules +++ b/.gitmodules @@ -38,3 +38,7 @@ path = third-party/ffmpeg-linux-aarch64 url = https://github.com/LizardByte/build-deps branch = ffmpeg-linux-aarch64 +[submodule "ffmpeg-macos-aarch64"] + path = third-party/ffmpeg-macos-aarch64 + url = https://github.com/LizardByte/build-deps + branch = ffmpeg-macos-aarch64 diff --git a/CMakeLists.txt b/CMakeLists.txt index 05086627492..f48fdaeb8a6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -375,7 +375,11 @@ if(WIN32) set(FFMPEG_PREPARED_BINARIES "${CMAKE_CURRENT_SOURCE_DIR}/third-party/ffmpeg-windows-x86_64") set(FFMPEG_PLATFORM_LIBRARIES mfplat ole32 strmiids mfuuid) elseif(APPLE) - set(FFMPEG_PREPARED_BINARIES "${CMAKE_CURRENT_SOURCE_DIR}/third-party/ffmpeg-macos-x86_64") + if (CMAKE_SYSTEM_PROCESSOR STREQUAL "arm64") + set(FFMPEG_PREPARED_BINARIES "${CMAKE_CURRENT_SOURCE_DIR}/third-party/ffmpeg-macos-aarch64") + else() + set(FFMPEG_PREPARED_BINARIES "${CMAKE_CURRENT_SOURCE_DIR}/third-party/ffmpeg-macos-x86_64") + endif() else() if (CMAKE_SYSTEM_PROCESSOR STREQUAL "aarch64") set(FFMPEG_PREPARED_BINARIES "${CMAKE_CURRENT_SOURCE_DIR}/third-party/ffmpeg-linux-aarch64") diff --git a/docs/source/building/macos.rst b/docs/source/building/macos.rst index 447a0fd6702..5a6fb5c668c 100644 --- a/docs/source/building/macos.rst +++ b/docs/source/building/macos.rst @@ -12,7 +12,7 @@ MacPorts Install Requirements .. code-block:: bash - sudo port install boost cmake libopus npm9 + sudo port install avahi boost180 cmake curl libopus npm9 pkgconfig Homebrew """""""" diff --git a/packaging/macos/Portfile b/packaging/macos/Portfile index 6cd0f685577..246c5521406 100644 --- a/packaging/macos/Portfile +++ b/packaging/macos/Portfile @@ -32,6 +32,7 @@ post-fetch { } depends_lib port:avahi \ + port:boost180 \ port:curl \ port:libopus \ port:npm9 \ diff --git a/third-party/ffmpeg-macos-aarch64 b/third-party/ffmpeg-macos-aarch64 new file mode 160000 index 00000000000..9103674a57a --- /dev/null +++ b/third-party/ffmpeg-macos-aarch64 @@ -0,0 +1 @@ +Subproject commit 9103674a57ab04f5109d4e10812fbf9f22685468 From dfb8d9d87fca9817fc4d42bd137735efc3272a36 Mon Sep 17 00:00:00 2001 From: Cameron Gutman Date: Tue, 20 Dec 2022 22:25:08 -0600 Subject: [PATCH 16/88] Fix PATH environment variable handling on Windows PATH needs to be matched case-insensitively on Windows or we will just clobber the pre-existing PATH --- src/process.cpp | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/src/process.cpp b/src/process.cpp index 2b6c1507119..8783db8c113 100644 --- a/src/process.cpp +++ b/src/process.cpp @@ -279,8 +279,21 @@ std::string parse_env_val(bp::native_environment &env, const std::string_view &v ss.write(pos, (dollar - pos)); auto var_begin = next + 1; auto var_end = find_match(next, std::end(val_raw)); + auto var_name = std::string { var_begin, var_end }; + +#ifdef _WIN32 + // Windows treats environment variable names in a case-insensitive manner, + // so we look for a case-insensitive match here. This is critical for + // correctly appending to PATH on Windows. + auto itr = std::find_if(env.cbegin(), env.cend(), + [&](const auto &e) { return boost::iequals(e.get_name(), var_name); }); + if(itr != env.cend()) { + // Use an existing case-insensitive match + var_name = itr->get_name(); + } +#endif - ss << env[std::string { var_begin, var_end }].to_string(); + ss << env[var_name].to_string(); pos = var_end + 1; next = var_end; From 6c5a323b4a2e8efdb60484aabb8e7d72ddc7c567 Mon Sep 17 00:00:00 2001 From: Cameron Gutman Date: Tue, 20 Dec 2022 22:26:35 -0600 Subject: [PATCH 17/88] Use the proper environment variable for the Program Files (x86) folder --- src_assets/windows/assets/apps.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src_assets/windows/assets/apps.json b/src_assets/windows/assets/apps.json index de4847c344f..0b106ae81c8 100644 --- a/src_assets/windows/assets/apps.json +++ b/src_assets/windows/assets/apps.json @@ -1,6 +1,6 @@ { "env": { - "PATH": "$(PATH);C:\\Program Files (x86)\\Steam" + "PATH": "$(PATH);$(ProgramFiles(x86))\\Steam" }, "apps": [ { From 8a99187562c1d535d401ac61691256731e99749a Mon Sep 17 00:00:00 2001 From: Cameron Gutman Date: Wed, 21 Dec 2022 15:41:48 -0600 Subject: [PATCH 18/88] Fix SunshineSvc hanging if an error occurs during startup (#598) --- tools/sunshinesvc.cpp | 88 +++++++++++++++++++++++++------------------ 1 file changed, 51 insertions(+), 37 deletions(-) diff --git a/tools/sunshinesvc.cpp b/tools/sunshinesvc.cpp index 13a6b51498a..3a7d26739df 100644 --- a/tools/sunshinesvc.cpp +++ b/tools/sunshinesvc.cpp @@ -71,38 +71,53 @@ HANDLE OpenLogFileHandle() { // Overwrite the old sunshine.log return CreateFileW(log_file_name, - GENERIC_WRITE, - FILE_SHARE_READ, - &security_attributes, - CREATE_ALWAYS, - 0, - NULL); + GENERIC_WRITE, + FILE_SHARE_READ, + &security_attributes, + CREATE_ALWAYS, + 0, + NULL); } VOID WINAPI ServiceMain(DWORD dwArgc, LPTSTR *lpszArgv) { - stop_event = CreateEventA(NULL, TRUE, FALSE, NULL); - if(stop_event == NULL) { - return; - } - service_status_handle = RegisterServiceCtrlHandlerEx(SERVICE_NAME, HandlerEx, NULL); if(service_status_handle == NULL) { + // Nothing we can really do here but terminate ourselves + ExitProcess(GetLastError()); return; } - auto log_file_handle = OpenLogFileHandle(); - if (log_file_handle == INVALID_HANDLE_VALUE) { - return; - } - - // Tell SCM we're running + // Tell SCM we're starting service_status.dwServiceType = SERVICE_WIN32_OWN_PROCESS; service_status.dwServiceSpecificExitCode = 0; service_status.dwWin32ExitCode = NO_ERROR; service_status.dwWaitHint = 0; - service_status.dwControlsAccepted = SERVICE_ACCEPT_STOP; + service_status.dwControlsAccepted = 0; service_status.dwCheckPoint = 0; - service_status.dwCurrentState = SERVICE_RUNNING; + service_status.dwCurrentState = SERVICE_START_PENDING; + SetServiceStatus(service_status_handle, &service_status); + + stop_event = CreateEventA(NULL, TRUE, FALSE, NULL); + if(stop_event == NULL) { + // Tell SCM we failed to start + service_status.dwWin32ExitCode = GetLastError(); + service_status.dwCurrentState = SERVICE_STOPPED; + SetServiceStatus(service_status_handle, &service_status); + return; + } + + auto log_file_handle = OpenLogFileHandle(); + if(log_file_handle == INVALID_HANDLE_VALUE) { + // Tell SCM we failed to start + service_status.dwWin32ExitCode = GetLastError(); + service_status.dwCurrentState = SERVICE_STOPPED; + SetServiceStatus(service_status_handle, &service_status); + return; + } + + // Tell SCM we're running (and stoppable now) + service_status.dwControlsAccepted = SERVICE_ACCEPT_STOP; + service_status.dwCurrentState = SERVICE_RUNNING; SetServiceStatus(service_status_handle, &service_status); // Loop every 3 seconds until the stop event is set or Sunshine.exe is running @@ -113,25 +128,25 @@ VOID WINAPI ServiceMain(DWORD dwArgc, LPTSTR *lpszArgv) { } STARTUPINFOW startup_info = {}; - startup_info.cb = sizeof(startup_info); - startup_info.lpDesktop = (LPWSTR)L"winsta0\\default"; - startup_info.dwFlags = STARTF_USESTDHANDLES; - startup_info.hStdInput = INVALID_HANDLE_VALUE; - startup_info.hStdOutput = log_file_handle; - startup_info.hStdError = log_file_handle; + startup_info.cb = sizeof(startup_info); + startup_info.lpDesktop = (LPWSTR)L"winsta0\\default"; + startup_info.dwFlags = STARTF_USESTDHANDLES; + startup_info.hStdInput = INVALID_HANDLE_VALUE; + startup_info.hStdOutput = log_file_handle; + startup_info.hStdError = log_file_handle; PROCESS_INFORMATION process_info; if(!CreateProcessAsUserW(console_token, - L"Sunshine.exe", - NULL, - NULL, - NULL, - TRUE, - ABOVE_NORMAL_PRIORITY_CLASS | CREATE_UNICODE_ENVIRONMENT | CREATE_NO_WINDOW, - NULL, - NULL, - &startup_info, - &process_info)) { + L"Sunshine.exe", + NULL, + NULL, + NULL, + TRUE, + ABOVE_NORMAL_PRIORITY_CLASS | CREATE_UNICODE_ENVIRONMENT | CREATE_NO_WINDOW, + NULL, + NULL, + &startup_info, + &process_info)) { CloseHandle(console_token); continue; } @@ -162,8 +177,7 @@ VOID WINAPI ServiceMain(DWORD dwArgc, LPTSTR *lpszArgv) { SetServiceStatus(service_status_handle, &service_status); } -int main(int argc, char* argv[]) -{ +int main(int argc, char *argv[]) { static const SERVICE_TABLE_ENTRY service_table[] = { { (LPSTR)SERVICE_NAME, ServiceMain }, { NULL, NULL } From 2b1514b547abbb7e2c175ae692d62c56db1feb5b Mon Sep 17 00:00:00 2001 From: LizardByte-bot <108553330+LizardByte-bot@users.noreply.github.com> Date: Wed, 21 Dec 2022 23:04:58 -0500 Subject: [PATCH 19/88] Bump ffmpeg (#603) --- third-party/ffmpeg-linux-aarch64 | 2 +- third-party/ffmpeg-linux-x86_64 | 2 +- third-party/ffmpeg-macos-x86_64 | 2 +- third-party/ffmpeg-windows-x86_64 | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/third-party/ffmpeg-linux-aarch64 b/third-party/ffmpeg-linux-aarch64 index d73b78136b9..fe670da0818 160000 --- a/third-party/ffmpeg-linux-aarch64 +++ b/third-party/ffmpeg-linux-aarch64 @@ -1 +1 @@ -Subproject commit d73b78136b91da93cd60eeb3c0dea121a4f7d2b7 +Subproject commit fe670da0818a248eaf1c08eaa556a8285c1d97f7 diff --git a/third-party/ffmpeg-linux-x86_64 b/third-party/ffmpeg-linux-x86_64 index d4f0e85a7e6..c56edb68061 160000 --- a/third-party/ffmpeg-linux-x86_64 +++ b/third-party/ffmpeg-linux-x86_64 @@ -1 +1 @@ -Subproject commit d4f0e85a7e67af5033b731c0c60de841d5b76076 +Subproject commit c56edb680615dd84ed93cc531476659150aa2477 diff --git a/third-party/ffmpeg-macos-x86_64 b/third-party/ffmpeg-macos-x86_64 index de823e0779b..0b7d3bee1aa 160000 --- a/third-party/ffmpeg-macos-x86_64 +++ b/third-party/ffmpeg-macos-x86_64 @@ -1 +1 @@ -Subproject commit de823e0779b7606bdd8420f92ac7381b1fb205e0 +Subproject commit 0b7d3bee1aa7a18af1023a6e35b71ba9a47e1907 diff --git a/third-party/ffmpeg-windows-x86_64 b/third-party/ffmpeg-windows-x86_64 index 495ac7502e4..e4d141c6a08 160000 --- a/third-party/ffmpeg-windows-x86_64 +++ b/third-party/ffmpeg-windows-x86_64 @@ -1 +1 @@ -Subproject commit 495ac7502e4e81bbb24ed7fe71bb459f71b6673c +Subproject commit e4d141c6a08047e28025acade471baa2b412a873 From 1041f87a5d55b1cb3610d683254bf6150cb5d206 Mon Sep 17 00:00:00 2001 From: Cameron Gutman Date: Thu, 22 Dec 2022 12:09:45 -0600 Subject: [PATCH 20/88] Spawn Sunshine.exe in a job object, so it is terminated if SunshineSvc.exe dies (#602) --- tools/sunshinesvc.cpp | 104 ++++++++++++++++++++++++++++++++++++++---- 1 file changed, 94 insertions(+), 10 deletions(-) diff --git a/tools/sunshinesvc.cpp b/tools/sunshinesvc.cpp index 3a7d26739df..c92c9ebbb4b 100644 --- a/tools/sunshinesvc.cpp +++ b/tools/sunshinesvc.cpp @@ -2,6 +2,11 @@ #include #include +// 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) +#endif + SERVICE_STATUS_HANDLE service_status_handle; SERVICE_STATUS service_status; HANDLE stop_event; @@ -29,6 +34,47 @@ DWORD WINAPI HandlerEx(DWORD dwControl, DWORD dwEventType, LPVOID lpEventData, L } } +HANDLE CreateJobObjectForChildProcess() { + HANDLE job_handle = CreateJobObjectW(NULL, NULL); + if(!job_handle) { + return NULL; + } + + JOBOBJECT_EXTENDED_LIMIT_INFORMATION job_limit_info = {}; + + // Kill Sunshine.exe when the final job object handle is closed (which will happen if we terminate unexpectedly). + // This ensures we don't leave an orphaned Sunshine.exe running with an inherited handle to our log file. + job_limit_info.BasicLimitInformation.LimitFlags |= JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE; + + // Allow Sunshine.exe to use CREATE_BREAKAWAY_FROM_JOB when spawning processes to ensure they can to live beyond + // the lifetime of SunshineSvc.exe. This avoids unexpected user data loss if we crash or are killed. + job_limit_info.BasicLimitInformation.LimitFlags |= JOB_OBJECT_LIMIT_BREAKAWAY_OK; + + if(!SetInformationJobObject(job_handle, JobObjectExtendedLimitInformation, &job_limit_info, sizeof(job_limit_info))) { + CloseHandle(job_handle); + return NULL; + } + + return job_handle; +} + +LPPROC_THREAD_ATTRIBUTE_LIST AllocateProcThreadAttributeList(DWORD attribute_count) { + SIZE_T size; + InitializeProcThreadAttributeList(NULL, attribute_count, 0, &size); + + auto list = (LPPROC_THREAD_ATTRIBUTE_LIST)HeapAlloc(GetProcessHeap(), 0, size); + if(list == NULL) { + return NULL; + } + + if(!InitializeProcThreadAttributeList(list, attribute_count, 0, &size)) { + HeapFree(GetProcessHeap(), 0, list); + return NULL; + } + + return list; +} + HANDLE DuplicateTokenForConsoleSession() { auto console_session_id = WTSGetActiveConsoleSessionId(); if(console_session_id == 0xFFFFFFFF) { @@ -115,6 +161,52 @@ VOID WINAPI ServiceMain(DWORD dwArgc, LPTSTR *lpszArgv) { return; } + auto job_handle = CreateJobObjectForChildProcess(); + if(job_handle == NULL) { + // Tell SCM we failed to start + service_status.dwWin32ExitCode = GetLastError(); + service_status.dwCurrentState = SERVICE_STOPPED; + SetServiceStatus(service_status_handle, &service_status); + return; + } + + // We can use a single STARTUPINFOEXW for all the processes that we launch + STARTUPINFOEXW startup_info = {}; + startup_info.StartupInfo.cb = sizeof(startup_info); + startup_info.StartupInfo.lpDesktop = (LPWSTR)L"winsta0\\default"; + startup_info.StartupInfo.dwFlags = STARTF_USESTDHANDLES; + startup_info.StartupInfo.hStdInput = NULL; + startup_info.StartupInfo.hStdOutput = log_file_handle; + startup_info.StartupInfo.hStdError = log_file_handle; + + // Allocate an attribute list with space for 2 entries + startup_info.lpAttributeList = AllocateProcThreadAttributeList(2); + if(startup_info.lpAttributeList == NULL) { + // Tell SCM we failed to start + service_status.dwWin32ExitCode = GetLastError(); + service_status.dwCurrentState = SERVICE_STOPPED; + SetServiceStatus(service_status_handle, &service_status); + return; + } + + // Only allow Sunshine.exe to inherit the log file handle, not all inheritable handles + UpdateProcThreadAttribute(startup_info.lpAttributeList, + 0, + PROC_THREAD_ATTRIBUTE_HANDLE_LIST, + &log_file_handle, + sizeof(log_file_handle), + NULL, + NULL); + + // Start Sunshine.exe inside our job object + UpdateProcThreadAttribute(startup_info.lpAttributeList, + 0, + PROC_THREAD_ATTRIBUTE_JOB_LIST, + &job_handle, + sizeof(job_handle), + NULL, + NULL); + // Tell SCM we're running (and stoppable now) service_status.dwControlsAccepted = SERVICE_ACCEPT_STOP; service_status.dwCurrentState = SERVICE_RUNNING; @@ -127,14 +219,6 @@ VOID WINAPI ServiceMain(DWORD dwArgc, LPTSTR *lpszArgv) { continue; } - STARTUPINFOW startup_info = {}; - startup_info.cb = sizeof(startup_info); - startup_info.lpDesktop = (LPWSTR)L"winsta0\\default"; - startup_info.dwFlags = STARTF_USESTDHANDLES; - startup_info.hStdInput = INVALID_HANDLE_VALUE; - startup_info.hStdOutput = log_file_handle; - startup_info.hStdError = log_file_handle; - PROCESS_INFORMATION process_info; if(!CreateProcessAsUserW(console_token, L"Sunshine.exe", @@ -142,10 +226,10 @@ VOID WINAPI ServiceMain(DWORD dwArgc, LPTSTR *lpszArgv) { NULL, NULL, TRUE, - ABOVE_NORMAL_PRIORITY_CLASS | CREATE_UNICODE_ENVIRONMENT | CREATE_NO_WINDOW, + ABOVE_NORMAL_PRIORITY_CLASS | CREATE_UNICODE_ENVIRONMENT | CREATE_NO_WINDOW | EXTENDED_STARTUPINFO_PRESENT, NULL, NULL, - &startup_info, + (LPSTARTUPINFOW)&startup_info, &process_info)) { CloseHandle(console_token); continue; From bb092c07230fd2b673bbaa69ad390fa3ba12d337 Mon Sep 17 00:00:00 2001 From: Cameron Gutman Date: Thu, 22 Dec 2022 19:48:31 -0600 Subject: [PATCH 21/88] Fix streaming with non-B8G8R8A8 desktop mode (#609) --- src/platform/windows/display_base.cpp | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/src/platform/windows/display_base.cpp b/src/platform/windows/display_base.cpp index e6951cce47e..53b329cc763 100644 --- a/src/platform/windows/display_base.cpp +++ b/src/platform/windows/display_base.cpp @@ -318,9 +318,17 @@ int display_base_t::init(int framerate, const std::string &display_name) { DXGI_OUTDUPL_DESC dup_desc; dup.dup->GetDesc(&dup_desc); - format = dup_desc.ModeDesc.Format; - - BOOST_LOG(debug) << "Source format ["sv << format_str[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 << format_str[dup_desc.ModeDesc.Format] << ']'; + + // For IDXGIOutput1::DuplicateOutput(), the format of the desktop image we receive from AcquireNextFrame() is + // converted to DXGI_FORMAT_B8G8R8A8_UNORM, even if the current mode (as returned in dup_desc) differs. + // See https://learn.microsoft.com/en-us/windows/win32/direct3ddxgi/desktop-dup-api for details. + // + // TODO: When we implement IDXGIOutput5, we will need to actually call AcquireNextFrame(), then call GetDesc() + // on the the texture we receive to determine which format in our list that it has decided to use. + format = DXGI_FORMAT_B8G8R8A8_UNORM; + BOOST_LOG(info) << "Capture format ["sv << format_str[format] << ']'; return 0; } From 1e037db7bdae5b404ca939a90146f84e9644e6fb Mon Sep 17 00:00:00 2001 From: Cameron Gutman Date: Thu, 22 Dec 2022 20:58:39 -0600 Subject: [PATCH 22/88] Fix pixel pitch handling for DXGI_FORMAT_R16G16B16A16_FLOAT (#605) --- src/platform/windows/display.h | 5 +++++ src/platform/windows/display_ram.cpp | 2 +- src/platform/windows/display_vram.cpp | 9 +++++---- 3 files changed, 11 insertions(+), 5 deletions(-) diff --git a/src/platform/windows/display.h b/src/platform/windows/display.h index 52faddb7179..a60e9587eea 100644 --- a/src/platform/windows/display.h +++ b/src/platform/windows/display.h @@ -131,6 +131,11 @@ class display_base_t : public display_t { } D3DKMT_SCHEDULINGPRIORITYCLASS; typedef NTSTATUS WINAPI (*PD3DKMTSetProcessSchedulingPriorityClass)(HANDLE, D3DKMT_SCHEDULINGPRIORITYCLASS); + +protected: + int get_pixel_pitch() { + return (format == DXGI_FORMAT_R16G16B16A16_FLOAT) ? 8 : 4; + } }; class display_ram_t : public display_base_t { diff --git a/src/platform/windows/display_ram.cpp b/src/platform/windows/display_ram.cpp index 596d4880ff3..33072c32791 100644 --- a/src/platform/windows/display_ram.cpp +++ b/src/platform/windows/display_ram.cpp @@ -280,7 +280,7 @@ capture_e display_ram_t::snapshot(::platf::img_t *img_base, std::chrono::millise std::shared_ptr display_ram_t::alloc_img() { auto img = std::make_shared(); - img->pixel_pitch = 4; + img->pixel_pitch = get_pixel_pitch(); img->row_pitch = img_info.RowPitch; img->width = width; img->height = height; diff --git a/src/platform/windows/display_vram.cpp b/src/platform/windows/display_vram.cpp index 3cf7c97789b..89b84406ab4 100644 --- a/src/platform/windows/display_vram.cpp +++ b/src/platform/windows/display_vram.cpp @@ -757,7 +757,7 @@ int display_vram_t::init(int framerate, const std::string &display_name) { std::shared_ptr display_vram_t::alloc_img() { auto img = std::make_shared(); - img->pixel_pitch = 4; + img->pixel_pitch = get_pixel_pitch(); img->row_pitch = img->pixel_pitch * width; img->width = width; img->height = height; @@ -802,13 +802,14 @@ int display_vram_t::dummy_img(platf::img_t *img_base) { return 0; } - img->row_pitch = width * 4; - auto dummy_data = std::make_unique(width * height); + img->pixel_pitch = get_pixel_pitch(); + img->row_pitch = img->pixel_pitch * width; + auto dummy_data = std::make_unique(img->row_pitch * height); D3D11_SUBRESOURCE_DATA data { dummy_data.get(), (UINT)img->row_pitch }; - std::fill_n(dummy_data.get(), width * height, 0); + std::fill_n(dummy_data.get(), img->row_pitch * height, 0); D3D11_TEXTURE2D_DESC t {}; t.Width = width; From 5adbd2988c17589a3df777341c5c20ad4e77354b Mon Sep 17 00:00:00 2001 From: LizardByte-bot <108553330+LizardByte-bot@users.noreply.github.com> Date: Thu, 22 Dec 2022 22:47:24 -0500 Subject: [PATCH 23/88] ci: update global workflows (#613) --- .github/workflows/automerge.yml | 2 +- .github/workflows/autoupdate-labeler.yml | 12 ++---------- .github/workflows/autoupdate.yml | 10 ++++++++-- .github/workflows/issues-stale.yml | 4 ++-- 4 files changed, 13 insertions(+), 15 deletions(-) diff --git a/.github/workflows/automerge.yml b/.github/workflows/automerge.yml index c91ac7d4ad1..0ba556810af 100644 --- a/.github/workflows/automerge.yml +++ b/.github/workflows/automerge.yml @@ -51,7 +51,7 @@ jobs: BASE_BRANCHES: nightly GITHUB_TOKEN: ${{ secrets.GH_BOT_TOKEN }} GITHUB_LOGIN: ${{ secrets.GH_BOT_NAME }} - MERGE_LABELS: "" + MERGE_LABELS: "!dependencies" MERGE_METHOD: "squash" MERGE_COMMIT_MESSAGE: "{pullRequest.title} (#{pullRequest.number})" MERGE_DELETE_BRANCH: true diff --git a/.github/workflows/autoupdate-labeler.yml b/.github/workflows/autoupdate-labeler.yml index 92b00988c89..5e426ad7b33 100644 --- a/.github/workflows/autoupdate-labeler.yml +++ b/.github/workflows/autoupdate-labeler.yml @@ -10,11 +10,8 @@ on: types: - edited - opened + - reopened - synchronize - pull_request_review: - types: - - edited - - submitted jobs: label_pr: @@ -40,11 +37,7 @@ jobs: steps.org_member.outputs.result == 'true' && contains(github.event.pull_request.labels.*.name, 'autoupdate') == false && contains(github.event.pull_request.body, - fromJSON('"\n- [x] I want maintainers to keep my branch updated"')) == true && - ( - (github.event_name == 'pull_request_review' && github.event.review.state == 'approved') || - (github.event_name == 'pull_request') - ) + fromJSON('"\n- [x] I want maintainers to keep my branch updated"')) == true uses: actions/github-script@v6 with: @@ -60,7 +53,6 @@ jobs: - name: Unlabel autoupdate if: >- contains(github.event.pull_request.labels.*.name, 'autoupdate') && - github.event_name == 'pull_request' && ( (github.event.action == 'synchronize' && steps.org_member.outputs.result == 'false') || (contains(github.event.pull_request.body, diff --git a/.github/workflows/autoupdate.yml b/.github/workflows/autoupdate.yml index 4522182f9a3..83afbc3a77e 100644 --- a/.github/workflows/autoupdate.yml +++ b/.github/workflows/autoupdate.yml @@ -36,11 +36,17 @@ jobs: dependabot-rebase: name: Dependabot Rebase if: >- - startsWith(github.repository, 'LizardByte/') && - contains(github.event.pull_request.labels.*.name, 'central_dependency') == false + startsWith(github.repository, 'LizardByte/') runs-on: ubuntu-latest steps: + - name: check labels + id: label + run: | + echo "central_dep=${{ contains(github.event.pull_request.labels.*.name, 'central_dependency') }}" \ + >> $GITHUB_OUTPUT + - name: rebase + if: ${{ steps.label.outputs.central_dep == false }} uses: "bbeesley/gha-auto-dependabot-rebase@v1.2.0" env: GITHUB_TOKEN: ${{ secrets.GH_BOT_TOKEN }} diff --git a/.github/workflows/issues-stale.yml b/.github/workflows/issues-stale.yml index 586545e8a7a..460eb848f1e 100644 --- a/.github/workflows/issues-stale.yml +++ b/.github/workflows/issues-stale.yml @@ -16,7 +16,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Stale - uses: actions/stale@v6 + uses: actions/stale@v7 with: close-issue-message: > This issue was closed because it has been stalled for 10 days with no activity. @@ -38,7 +38,7 @@ jobs: repo-token: ${{ secrets.GH_BOT_TOKEN }} - name: Invalid Template - uses: actions/stale@v6 + uses: actions/stale@v7 with: close-issue-message: > This issue was closed because the the template was not completed after 5 days. From c3ec53c0ae141763b146d01eee392adffff61a54 Mon Sep 17 00:00:00 2001 From: Conn O'Griofa Date: Tue, 27 Dec 2022 04:20:46 +0000 Subject: [PATCH 24/88] CMake: win32: further fix for boost linker incompatibility (#632) --- CMakeLists.txt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index f48fdaeb8a6..95718bed29a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -78,6 +78,10 @@ if(WIN32) set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -static") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${CURL_STATIC_LDFLAGS} ${CURL_STATIC_CFLAGS}") + # WORKAROUND: /mingw64/include/_mingw.h r186 defines _WIN32_WINNT=0x601, but boost + # is built against version 0x603 (BOOST_WINAPI_VERSION_WINBLUE) from r157 header. + ADD_DEFINITIONS(-DBOOST_USE_WINAPI_VERSION=BOOST_WINAPI_VERSION_WINBLUE) + add_compile_definitions(SUNSHINE_PLATFORM="windows") add_subdirectory(tools) # This is temporary, only tools for Windows are needed, for now From cbb5ec3f29155937ea581949bc2032b32a6494c2 Mon Sep 17 00:00:00 2001 From: Brad Richardson Date: Tue, 27 Dec 2022 08:11:01 -0500 Subject: [PATCH 25/88] Remove duplicate boost port (#630) --- packaging/macos/Portfile | 1 - 1 file changed, 1 deletion(-) diff --git a/packaging/macos/Portfile b/packaging/macos/Portfile index 246c5521406..6cd0f685577 100644 --- a/packaging/macos/Portfile +++ b/packaging/macos/Portfile @@ -32,7 +32,6 @@ post-fetch { } depends_lib port:avahi \ - port:boost180 \ port:curl \ port:libopus \ port:npm9 \ From 95437d15f3ed63ab00e7bbe52db0ce1b00f337ff Mon Sep 17 00:00:00 2001 From: Brad Richardson Date: Tue, 27 Dec 2022 09:13:54 -0500 Subject: [PATCH 26/88] FFmpeg 5 and pre-built CBS (#509) --- CMakeLists.txt | 10 +- docs/source/about/advanced_usage.rst | 2 +- src/cbs.cpp | 2 +- src/config.cpp | 8 +- src_assets/common/assets/web/config.html | 2 +- third-party/cbs/CMakeLists.txt | 69 - third-party/cbs/bytestream.h | 351 ---- third-party/cbs/cbs.c | 1050 ---------- third-party/cbs/cbs_av1.c | 1323 ------------ third-party/cbs/cbs_av1_syntax_template.c | 2063 ------------------- third-party/cbs/cbs_h2645.c | 1632 --------------- third-party/cbs/cbs_h264_syntax_template.c | 1175 ----------- third-party/cbs/cbs_h265_syntax_template.c | 2038 ------------------ third-party/cbs/cbs_internal.h | 220 -- third-party/cbs/cbs_jpeg.c | 482 ----- third-party/cbs/cbs_jpeg_syntax_template.c | 189 -- third-party/cbs/cbs_mpeg2.c | 469 ----- third-party/cbs/cbs_mpeg2_syntax_template.c | 413 ---- third-party/cbs/cbs_sei.c | 355 ---- third-party/cbs/cbs_sei_syntax_template.c | 310 --- third-party/cbs/cbs_vp9.c | 675 ------ third-party/cbs/cbs_vp9_syntax_template.c | 422 ---- third-party/cbs/config.h | 27 - third-party/cbs/defs.h | 51 - third-party/cbs/get_bits.h | 831 -------- third-party/cbs/h2645_parse.c | 535 ----- third-party/cbs/h264_ps.h | 173 -- third-party/cbs/h264_sei.h | 202 -- third-party/cbs/hevc_sei.h | 142 -- third-party/cbs/include/cbs/av1.h | 171 -- third-party/cbs/include/cbs/cbs.h | 448 ---- third-party/cbs/include/cbs/cbs_av1.h | 464 ----- third-party/cbs/include/cbs/cbs_bsf.h | 131 -- third-party/cbs/include/cbs/cbs_h264.h | 406 ---- third-party/cbs/include/cbs/cbs_h2645.h | 36 - third-party/cbs/include/cbs/cbs_h265.h | 679 ------ third-party/cbs/include/cbs/cbs_jpeg.h | 123 -- third-party/cbs/include/cbs/cbs_mpeg2.h | 231 --- third-party/cbs/include/cbs/cbs_sei.h | 200 -- third-party/cbs/include/cbs/cbs_vp9.h | 213 -- third-party/cbs/include/cbs/h264.h | 113 - third-party/cbs/include/cbs/h2645_parse.h | 152 -- third-party/cbs/include/cbs/hevc.h | 160 -- third-party/cbs/include/cbs/sei.h | 140 -- third-party/cbs/include/cbs/video_levels.h | 112 - third-party/cbs/intmath.h | 152 -- third-party/cbs/mathops.h | 243 --- third-party/cbs/put_bits.h | 406 ---- third-party/cbs/video_levels.c | 349 ---- third-party/cbs/vlc.h | 140 -- third-party/ffmpeg-linux-aarch64 | 2 +- third-party/ffmpeg-linux-x86_64 | 2 +- third-party/ffmpeg-macos-aarch64 | 2 +- third-party/ffmpeg-macos-x86_64 | 2 +- third-party/ffmpeg-windows-x86_64 | 2 +- 55 files changed, 14 insertions(+), 20286 deletions(-) delete mode 100644 third-party/cbs/CMakeLists.txt delete mode 100644 third-party/cbs/bytestream.h delete mode 100644 third-party/cbs/cbs.c delete mode 100644 third-party/cbs/cbs_av1.c delete mode 100644 third-party/cbs/cbs_av1_syntax_template.c delete mode 100644 third-party/cbs/cbs_h2645.c delete mode 100644 third-party/cbs/cbs_h264_syntax_template.c delete mode 100644 third-party/cbs/cbs_h265_syntax_template.c delete mode 100644 third-party/cbs/cbs_internal.h delete mode 100644 third-party/cbs/cbs_jpeg.c delete mode 100644 third-party/cbs/cbs_jpeg_syntax_template.c delete mode 100644 third-party/cbs/cbs_mpeg2.c delete mode 100644 third-party/cbs/cbs_mpeg2_syntax_template.c delete mode 100644 third-party/cbs/cbs_sei.c delete mode 100644 third-party/cbs/cbs_sei_syntax_template.c delete mode 100644 third-party/cbs/cbs_vp9.c delete mode 100644 third-party/cbs/cbs_vp9_syntax_template.c delete mode 100644 third-party/cbs/config.h delete mode 100644 third-party/cbs/defs.h delete mode 100644 third-party/cbs/get_bits.h delete mode 100644 third-party/cbs/h2645_parse.c delete mode 100644 third-party/cbs/h264_ps.h delete mode 100644 third-party/cbs/h264_sei.h delete mode 100644 third-party/cbs/hevc_sei.h delete mode 100644 third-party/cbs/include/cbs/av1.h delete mode 100644 third-party/cbs/include/cbs/cbs.h delete mode 100644 third-party/cbs/include/cbs/cbs_av1.h delete mode 100644 third-party/cbs/include/cbs/cbs_bsf.h delete mode 100644 third-party/cbs/include/cbs/cbs_h264.h delete mode 100644 third-party/cbs/include/cbs/cbs_h2645.h delete mode 100644 third-party/cbs/include/cbs/cbs_h265.h delete mode 100644 third-party/cbs/include/cbs/cbs_jpeg.h delete mode 100644 third-party/cbs/include/cbs/cbs_mpeg2.h delete mode 100644 third-party/cbs/include/cbs/cbs_sei.h delete mode 100644 third-party/cbs/include/cbs/cbs_vp9.h delete mode 100644 third-party/cbs/include/cbs/h264.h delete mode 100644 third-party/cbs/include/cbs/h2645_parse.h delete mode 100644 third-party/cbs/include/cbs/hevc.h delete mode 100644 third-party/cbs/include/cbs/sei.h delete mode 100644 third-party/cbs/include/cbs/video_levels.h delete mode 100644 third-party/cbs/intmath.h delete mode 100644 third-party/cbs/mathops.h delete mode 100644 third-party/cbs/put_bits.h delete mode 100644 third-party/cbs/video_levels.c delete mode 100644 third-party/cbs/vlc.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 95718bed29a..27c25d64305 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -401,6 +401,8 @@ endif() set(FFMPEG_LIBRARIES ${FFMPEG_PREPARED_BINARIES}/lib/libavcodec.a ${FFMPEG_PREPARED_BINARIES}/lib/libavutil.a + ${FFMPEG_PREPARED_BINARIES}/lib/libcbs.a + ${FFMPEG_PREPARED_BINARIES}/lib/libSvtAv1Enc.a ${FFMPEG_PREPARED_BINARIES}/lib/libswscale.a ${FFMPEG_PREPARED_BINARIES}/lib/libx264.a ${FFMPEG_PREPARED_BINARIES}/lib/libx265.a @@ -410,15 +412,12 @@ set(FFMPEG_LIBRARIES include_directories( ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/third-party - ${CMAKE_CURRENT_SOURCE_DIR}/third-party/cbs/include ${CMAKE_CURRENT_SOURCE_DIR}/third-party/moonlight-common-c/enet/include ${CMAKE_CURRENT_SOURCE_DIR}/third-party/moonlight-common-c/reedsolomon ${FFMPEG_INCLUDE_DIRS} ${PLATFORM_INCLUDE_DIRS} ) -add_subdirectory(third-party/cbs) - string(TOUPPER "x${CMAKE_BUILD_TYPE}" BUILD_TYPE) if("${BUILD_TYPE}" STREQUAL "XDEBUG") list(APPEND SUNSHINE_COMPILE_OPTIONS -O0 -ggdb3) @@ -446,13 +445,8 @@ else() endif() list(APPEND SUNSHINE_DEFINITIONS SUNSHINE_ASSETS_DIR="${SUNSHINE_ASSETS_DIR_DEF}") - -list(APPEND CBS_EXTERNAL_LIBRARIES - cbs) - list(APPEND SUNSHINE_EXTERNAL_LIBRARIES libminiupnpc-static - ${CBS_EXTERNAL_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT} enet opus diff --git a/docs/source/about/advanced_usage.rst b/docs/source/about/advanced_usage.rst index 2ab43a856c6..4f1455c36a5 100644 --- a/docs/source/about/advanced_usage.rst +++ b/docs/source/about/advanced_usage.rst @@ -958,7 +958,7 @@ amd_rc Value Description =========== =========== auto let ffmpeg decide - constqp constant QP mode + cqp constant QP mode cbr constant bitrate vbr_latency variable bitrate, latency constrained vbr_peak variable bitrate, peak constrained diff --git a/src/cbs.cpp b/src/cbs.cpp index d50bd1951fb..6844d91aa57 100644 --- a/src/cbs.cpp +++ b/src/cbs.cpp @@ -1,7 +1,7 @@ extern "C" { #include #include -#include +#include #include #include } diff --git a/src/config.cpp b/src/config.cpp index b7f483ac6f3..00e4768120a 100644 --- a/src/config.cpp +++ b/src/config.cpp @@ -103,14 +103,14 @@ enum quality_e : int { }; enum class rc_hevc_e : int { - constqp, /**< Constant QP mode */ + cqp, /**< Constant QP mode */ vbr_latency, /**< Latency Constrained Variable Bitrate */ vbr_peak, /**< Peak Constrained Variable Bitrate */ cbr, /**< Constant bitrate mode */ }; enum class rc_h264_e : int { - constqp, /**< Constant QP mode */ + cqp, /**< Constant QP mode */ cbr, /**< Constant bitrate mode */ vbr_peak, /**< Peak Constrained Variable Bitrate */ vbr_latency, /**< Latency Constrained Variable Bitrate */ @@ -135,7 +135,7 @@ std::optional quality_from_view(const std::string_view &quality) { std::optional rc_h264_from_view(const std::string_view &rc) { #define _CONVERT_(x) \ if(rc == #x##sv) return (int)rc_h264_e::x - _CONVERT_(constqp); + _CONVERT_(cqp); _CONVERT_(vbr_latency); _CONVERT_(vbr_peak); _CONVERT_(cbr); @@ -146,7 +146,7 @@ std::optional rc_h264_from_view(const std::string_view &rc) { std::optional rc_hevc_from_view(const std::string_view &rc) { #define _CONVERT_(x) \ if(rc == #x##sv) return (int)rc_hevc_e::x - _CONVERT_(constqp); + _CONVERT_(cqp); _CONVERT_(vbr_latency); _CONVERT_(vbr_peak); _CONVERT_(cbr); diff --git a/src_assets/common/assets/web/config.html b/src_assets/common/assets/web/config.html index 01954e76e7b..95d17f0da07 100644 --- a/src_assets/common/assets/web/config.html +++ b/src_assets/common/assets/web/config.html @@ -700,7 +700,7 @@

Configuration

- - - - - - - + + + + + + + + + +
+ +
- +
- +
@@ -685,33 +685,31 @@

Configuration

- +
- +
- +
@@ -851,12 +849,13 @@

Configuration

this.config.origin_web_manager_allowed || "lan"; this.config.hevc_mode = this.config.hevc_mode || 0; this.config.encoder = this.config.encoder || ""; - this.config.nv_preset = this.config.nv_preset || "default"; - this.config.nv_rc = this.config.nv_rc || "auto"; + this.config.nv_preset = this.config.nv_preset || "p4"; + this.config.nv_tune = this.config.nv_tune || "ull"; this.config.nv_coder = this.config.nv_coder || "auto"; + this.config.nv_rc = this.config.nv_rc || "cbr"; this.config.amd_coder = this.config.amd_coder || "auto" - this.config.amd_quality = this.config.amd_quality || "default"; - this.config.amd_rc = this.config.amd_rc || "auto"; + this.config.amd_quality = this.config.amd_quality || "balanced"; + this.config.amd_rc = this.config.amd_rc || "vbr_latency"; this.config.vt_coder = this.config.vt_coder || "auto"; this.config.vt_software = this.config.vt_software || "auto"; this.config.vt_realtime = this.config.vt_realtime || "enabled"; From c7fe8f65bdf4ebf6b103d1d6a90d4a08c8a30285 Mon Sep 17 00:00:00 2001 From: Cameron Gutman Date: Wed, 28 Dec 2022 07:53:58 -0600 Subject: [PATCH 32/88] windows: Fix audio when host is not using 48 KHz sample rate (#640) --- src/platform/windows/audio.cpp | 404 +++++---------------------------- 1 file changed, 56 insertions(+), 348 deletions(-) diff --git a/src/platform/windows/audio.cpp b/src/platform/windows/audio.cpp index b630f73e85d..72360f53aff 100644 --- a/src/platform/windows/audio.cpp +++ b/src/platform/windows/audio.cpp @@ -81,262 +81,10 @@ class prop_var_t { PROPVARIANT prop; }; -class audio_pipe_t { -public: - static constexpr auto stereo = 2; - static constexpr auto channels51 = 6; - static constexpr auto channels71 = 8; - - using samples_t = std::vector; - using buf_t = util::buffer_t; - - virtual void to_stereo(samples_t &out, const buf_t &in) = 0; - virtual void to_51(samples_t &out, const buf_t &in) = 0; - virtual void to_71(samples_t &out, const buf_t &in) = 0; -}; - -class mono_t : public audio_pipe_t { -public: - void to_stereo(samples_t &out, const buf_t &in) override { - auto sample_in_pos = std::begin(in); - auto sample_end = std::begin(out) + out.size(); - - for(auto sample_out_p = std::begin(out); sample_out_p != sample_end;) { - *sample_out_p++ = *sample_in_pos * 7 / 10; - *sample_out_p++ = *sample_in_pos++ * 7 / 10; - } - } - - void to_51(samples_t &out, const buf_t &in) override { - using namespace speaker; - - auto sample_in_pos = std::begin(in); - auto sample_end = std::begin(out) + out.size(); - - for(auto sample_out_p = std::begin(out); sample_out_p != sample_end; sample_out_p += channels51) { - int left = *sample_in_pos++; - - auto fl = (left * 7 / 10); - - sample_out_p[FRONT_LEFT] = fl; - sample_out_p[FRONT_RIGHT] = fl; - sample_out_p[FRONT_CENTER] = fl * 6; - sample_out_p[LOW_FREQUENCY] = fl / 10; - sample_out_p[BACK_LEFT] = left * 4 / 10; - sample_out_p[BACK_RIGHT] = left * 4 / 10; - } - } - - void to_71(samples_t &out, const buf_t &in) override { - using namespace speaker; - - auto sample_in_pos = std::begin(in); - auto sample_end = std::begin(out) + out.size(); - - for(auto sample_out_p = std::begin(out); sample_out_p != sample_end; sample_out_p += channels71) { - int left = *sample_in_pos++; - - auto fl = (left * 7 / 10); - - sample_out_p[FRONT_LEFT] = fl; - sample_out_p[FRONT_RIGHT] = fl; - sample_out_p[FRONT_CENTER] = fl * 6; - sample_out_p[LOW_FREQUENCY] = fl / 10; - sample_out_p[BACK_LEFT] = left * 4 / 10; - sample_out_p[BACK_RIGHT] = left * 4 / 10; - sample_out_p[SIDE_LEFT] = left * 5 / 10; - sample_out_p[SIDE_RIGHT] = left * 5 / 10; - } - } -}; - -class stereo_t : public audio_pipe_t { -public: - void to_stereo(samples_t &out, const buf_t &in) override { - std::copy_n(std::begin(in), out.size(), std::begin(out)); - } - - void to_51(samples_t &out, const buf_t &in) override { - using namespace speaker; - - auto sample_in_pos = std::begin(in); - auto sample_end = std::begin(out) + out.size(); - - for(auto sample_out_p = std::begin(out); sample_out_p != sample_end; sample_out_p += channels51) { - int left = sample_in_pos[speaker::FRONT_LEFT]; - int right = sample_in_pos[speaker::FRONT_RIGHT]; - - sample_in_pos += 2; - - auto fl = (left * 7 / 10); - auto fr = (right * 7 / 10); - - auto mix = (fl + fr) / 2; - - sample_out_p[FRONT_LEFT] = fl; - sample_out_p[FRONT_RIGHT] = fr; - sample_out_p[FRONT_CENTER] = mix; - sample_out_p[LOW_FREQUENCY] = mix / 2; - sample_out_p[BACK_LEFT] = left * 4 / 10; - sample_out_p[BACK_RIGHT] = right * 4 / 10; - } - } - - void to_71(samples_t &out, const buf_t &in) override { - using namespace speaker; - - auto sample_in_pos = std::begin(in); - auto sample_end = std::begin(out) + out.size(); - - for(auto sample_out_p = std::begin(out); sample_out_p != sample_end; sample_out_p += channels71) { - int left = sample_in_pos[speaker::FRONT_LEFT]; - int right = sample_in_pos[speaker::FRONT_RIGHT]; - - sample_in_pos += 2; - - auto fl = (left * 7 / 10); - auto fr = (right * 7 / 10); - - auto mix = (fl + fr) / 2; - - sample_out_p[FRONT_LEFT] = fl; - sample_out_p[FRONT_RIGHT] = fr; - sample_out_p[FRONT_CENTER] = mix; - sample_out_p[LOW_FREQUENCY] = mix / 2; - sample_out_p[BACK_LEFT] = left * 4 / 10; - sample_out_p[BACK_RIGHT] = right * 4 / 10; - sample_out_p[SIDE_LEFT] = left * 5 / 10; - sample_out_p[SIDE_RIGHT] = right * 5 / 10; - } - } -}; - -class surr51_t : public audio_pipe_t { -public: - void to_stereo(samples_t &out, const buf_t &in) { - using namespace speaker; - - auto sample_in_pos = std::begin(in); - auto sample_end = std::begin(out) + out.size(); - - for(auto sample_out_p = std::begin(out); sample_out_p != sample_end; sample_out_p += stereo) { - int left {}, right {}; - - left += sample_in_pos[FRONT_LEFT]; - left += sample_in_pos[FRONT_CENTER] * 9 / 10; - left += sample_in_pos[LOW_FREQUENCY] * 3 / 10; - left += sample_in_pos[BACK_LEFT] * 7 / 10; - left += sample_in_pos[BACK_RIGHT] * 3 / 10; - - right += sample_in_pos[FRONT_RIGHT]; - right += sample_in_pos[FRONT_CENTER] * 9 / 10; - right += sample_in_pos[LOW_FREQUENCY] * 3 / 10; - right += sample_in_pos[BACK_LEFT] * 3 / 10; - right += sample_in_pos[BACK_RIGHT] * 7 / 10; - - sample_out_p[0] = left; - sample_out_p[1] = right; - - sample_in_pos += channels51; - } - } - - void to_51(samples_t &out, const buf_t &in) override { - std::copy_n(std::begin(in), out.size(), std::begin(out)); - } - - void to_71(samples_t &out, const buf_t &in) override { - using namespace speaker; - - auto sample_in_pos = std::begin(in); - auto sample_end = std::begin(out) + out.size(); - - for(auto sample_out_p = std::begin(out); sample_out_p != sample_end; sample_out_p += channels71) { - int fl = sample_in_pos[FRONT_LEFT]; - int fr = sample_in_pos[FRONT_RIGHT]; - int bl = sample_in_pos[BACK_LEFT]; - int br = sample_in_pos[BACK_RIGHT]; - - auto mix_l = (fl + bl) / 2; - auto mix_r = (bl + br) / 2; - - sample_out_p[FRONT_LEFT] = fl; - sample_out_p[FRONT_RIGHT] = fr; - sample_out_p[FRONT_CENTER] = sample_in_pos[FRONT_CENTER]; - sample_out_p[LOW_FREQUENCY] = sample_in_pos[LOW_FREQUENCY]; - sample_out_p[BACK_LEFT] = bl; - sample_out_p[BACK_RIGHT] = br; - sample_out_p[SIDE_LEFT] = mix_l; - sample_out_p[SIDE_RIGHT] = mix_r; - - sample_in_pos += channels51; - } - } -}; - -class surr71_t : public audio_pipe_t { -public: - void to_stereo(samples_t &out, const buf_t &in) { - using namespace speaker; - - auto sample_in_pos = std::begin(in); - auto sample_end = std::begin(out) + out.size(); - - for(auto sample_out_p = std::begin(out); sample_out_p != sample_end; sample_out_p += stereo) { - int left {}, right {}; - - left += sample_in_pos[FRONT_LEFT]; - left += sample_in_pos[FRONT_CENTER] * 9 / 10; - left += sample_in_pos[LOW_FREQUENCY] * 3 / 10; - left += sample_in_pos[BACK_LEFT] * 7 / 10; - left += sample_in_pos[BACK_RIGHT] * 3 / 10; - left += sample_in_pos[SIDE_LEFT]; - - right += sample_in_pos[FRONT_RIGHT]; - right += sample_in_pos[FRONT_CENTER] * 9 / 10; - right += sample_in_pos[LOW_FREQUENCY] * 3 / 10; - right += sample_in_pos[BACK_LEFT] * 3 / 10; - right += sample_in_pos[BACK_RIGHT] * 7 / 10; - right += sample_in_pos[SIDE_RIGHT]; - - sample_out_p[0] = left; - sample_out_p[1] = right; - - sample_in_pos += channels71; - } - } - - void to_51(samples_t &out, const buf_t &in) override { - using namespace speaker; - - auto sample_in_pos = std::begin(in); - auto sample_end = std::begin(out) + out.size(); - - for(auto sample_out_p = std::begin(out); sample_out_p != sample_end; sample_out_p += channels51) { - auto sl = (int)sample_out_p[SIDE_LEFT] * 3 / 10; - auto sr = (int)sample_out_p[SIDE_RIGHT] * 3 / 10; - - sample_out_p[FRONT_LEFT] = sample_in_pos[FRONT_LEFT] + sl; - sample_out_p[FRONT_RIGHT] = sample_in_pos[FRONT_RIGHT] + sr; - sample_out_p[FRONT_CENTER] = sample_in_pos[FRONT_CENTER]; - sample_out_p[LOW_FREQUENCY] = sample_in_pos[LOW_FREQUENCY]; - sample_out_p[BACK_LEFT] = sample_in_pos[BACK_LEFT] + sl; - sample_out_p[BACK_RIGHT] = sample_in_pos[BACK_RIGHT] + sr; - - sample_in_pos += channels71; - } - } - - void to_71(samples_t &out, const buf_t &in) override { - std::copy_n(std::begin(in), out.size(), std::begin(out)); - } -}; - static std::wstring_convert, wchar_t> converter; struct format_t { enum type_e : int { none, - mono, stereo, surr51, surr71, @@ -346,12 +94,6 @@ struct format_t { int channels; int channel_mask; } formats[] { - { - format_t::mono, - "Mono"sv, - 1, - SPEAKER_FRONT_CENTER, - }, { format_t::stereo, "Stereo"sv, @@ -396,43 +138,53 @@ static format_t surround_51_side_speakers { SPEAKER_SIDE_RIGHT, }; -void set_wave_format(audio::wave_format_t &wave_format, const format_t &format) { - wave_format->nChannels = format.channels; - wave_format->nBlockAlign = wave_format->nChannels * wave_format->wBitsPerSample / 8; - wave_format->nAvgBytesPerSec = wave_format->nSamplesPerSec * wave_format->nBlockAlign; +WAVEFORMATEXTENSIBLE create_wave_format(const format_t &format) { + WAVEFORMATEXTENSIBLE wave_format; - if(wave_format->wFormatTag == WAVE_FORMAT_EXTENSIBLE) { - ((PWAVEFORMATEXTENSIBLE)wave_format.get())->dwChannelMask = format.channel_mask; - } + wave_format.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE; + wave_format.Format.nChannels = format.channels; + wave_format.Format.nSamplesPerSec = SAMPLE_RATE; + wave_format.Format.wBitsPerSample = 16; + wave_format.Format.nBlockAlign = wave_format.Format.nChannels * wave_format.Format.wBitsPerSample / 8; + wave_format.Format.nAvgBytesPerSec = wave_format.Format.nSamplesPerSec * wave_format.Format.nBlockAlign; + wave_format.Format.cbSize = sizeof(wave_format); + + wave_format.Samples.wValidBitsPerSample = 16; + wave_format.dwChannelMask = format.channel_mask; + wave_format.SubFormat = KSDATAFORMAT_SUBTYPE_PCM; + + return wave_format; } -int init_wave_format(audio::wave_format_t &wave_format, DWORD sample_rate) { +int set_wave_format(audio::wave_format_t &wave_format, const format_t &format) { + wave_format->nSamplesPerSec = SAMPLE_RATE; wave_format->wBitsPerSample = 16; - wave_format->nSamplesPerSec = sample_rate; + switch(wave_format->wFormatTag) { case WAVE_FORMAT_PCM: break; case WAVE_FORMAT_IEEE_FLOAT: break; case WAVE_FORMAT_EXTENSIBLE: { - auto wave_ex = (PWAVEFORMATEXTENSIBLE)wave_format.get(); - if(IsEqualGUID(KSDATAFORMAT_SUBTYPE_IEEE_FLOAT, wave_ex->SubFormat)) { - wave_ex->Samples.wValidBitsPerSample = 16; - wave_ex->SubFormat = KSDATAFORMAT_SUBTYPE_PCM; - break; - } - - BOOST_LOG(error) << "Unsupported Sub Format for WAVE_FORMAT_EXTENSIBLE: [0x"sv << util::hex(wave_ex->SubFormat).to_string_view() << ']'; + auto wave_ex = (PWAVEFORMATEXTENSIBLE)wave_format.get(); + wave_ex->Samples.wValidBitsPerSample = 16; + wave_ex->dwChannelMask = format.channel_mask; + wave_ex->SubFormat = KSDATAFORMAT_SUBTYPE_PCM; + break; } default: BOOST_LOG(error) << "Unsupported Wave Format: [0x"sv << util::hex(wave_format->wFormatTag).to_string_view() << ']'; return -1; }; + wave_format->nChannels = format.channels; + wave_format->nBlockAlign = wave_format->nChannels * wave_format->wBitsPerSample / 8; + wave_format->nAvgBytesPerSec = wave_format->nSamplesPerSec * wave_format->nBlockAlign; + return 0; } -audio_client_t make_audio_client(device_t &device, const format_t &format, int sample_rate) { +audio_client_t make_audio_client(device_t &device, const format_t &format) { audio_client_t audio_client; auto status = device->Activate( IID_IAudioClient, @@ -446,24 +198,14 @@ audio_client_t make_audio_client(device_t &device, const format_t &format, int s return nullptr; } - wave_format_t wave_format; - status = audio_client->GetMixFormat(&wave_format); - if(FAILED(status)) { - BOOST_LOG(error) << "Couldn't acquire Wave Format [0x"sv << util::hex(status).to_string_view() << ']'; - - return nullptr; - } - - if(init_wave_format(wave_format, sample_rate)) { - return nullptr; - } - set_wave_format(wave_format, format); + WAVEFORMATEXTENSIBLE wave_format = create_wave_format(format); status = audio_client->Initialize( AUDCLNT_SHAREMODE_SHARED, - AUDCLNT_STREAMFLAGS_LOOPBACK | AUDCLNT_STREAMFLAGS_EVENTCALLBACK, + AUDCLNT_STREAMFLAGS_LOOPBACK | AUDCLNT_STREAMFLAGS_EVENTCALLBACK | + AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM | AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY, // Enable automatic resampling to 48 KHz 0, 0, - wave_format.get(), + (LPWAVEFORMATEX)&wave_format, nullptr); if(status) { @@ -478,19 +220,21 @@ const wchar_t *no_null(const wchar_t *str) { return str ? str : L"Unknown"; } -format_t::type_e validate_device(device_t &device, int sample_rate) { +bool validate_device(device_t &device) { + bool valid = false; + + // Check for any valid format for(const auto &format : formats) { - // Ensure WaveFromat is compatible - auto audio_client = make_audio_client(device, format, sample_rate); + auto audio_client = make_audio_client(device, format); BOOST_LOG(debug) << format.name << ": "sv << (!audio_client ? "unsupported"sv : "supported"sv); if(audio_client) { - return format.type; + valid = true; } } - return format_t::none; + return valid; } device_t default_device(device_enum_t &device_enum) { @@ -514,32 +258,20 @@ device_t default_device(device_enum_t &device_enum) { class mic_wasapi_t : public mic_t { public: capture_e sample(std::vector &sample_out) override { - auto sample_size = sample_out.size() / channels_out * channels_in; + auto sample_size = sample_out.size(); + + // Refill the sample buffer if needed while(sample_buf_pos - std::begin(sample_buf) < sample_size) { - //FIXME: Use IAudioClient3 instead of IAudioClient, that would allows for adjusting the latency of the audio samples auto capture_result = _fill_buffer(); - if(capture_result != capture_e::ok) { return capture_result; } } - switch(channels_out) { - case 2: - pipe->to_stereo(sample_out, sample_buf); - break; - case 6: - pipe->to_51(sample_out, sample_buf); - break; - case 8: - pipe->to_71(sample_out, sample_buf); - break; - default: - BOOST_LOG(error) << "converting to ["sv << channels_out << "] channels is not supported"sv; - return capture_e::error; - } + // Fill the output buffer with samples + std::copy_n(std::begin(sample_buf), sample_size, std::begin(sample_out)); - // The excess samples should be in front of the queue + // Move any excess samples to the front of the buffer std::move(&sample_buf[sample_size], sample_buf_pos, std::begin(sample_buf)); sample_buf_pos -= sample_size; @@ -576,31 +308,17 @@ class mic_wasapi_t : public mic_t { } for(auto &format : formats) { + if(format.channels != channels_out) { + BOOST_LOG(debug) << "Skipping audio format ["sv << format.name << "] with channel count ["sv << format.channels << " != "sv << channels_out << ']'; + continue; + } + BOOST_LOG(debug) << "Trying audio format ["sv << format.name << ']'; - audio_client = make_audio_client(device, format, sample_rate); + audio_client = make_audio_client(device, format); if(audio_client) { BOOST_LOG(debug) << "Found audio format ["sv << format.name << ']'; - channels_in = format.channels; - this->channels_out = channels_out; - - switch(channels_in) { - case 1: - pipe = std::make_unique(); - break; - case 2: - pipe = std::make_unique(); - break; - case 6: - pipe = std::make_unique(); - break; - case 8: - pipe = std::make_unique(); - break; - default: - BOOST_LOG(error) << "converting from ["sv << channels_in << "] channels is not supported"sv; - return -1; - } + channels = channels_out; break; } } @@ -623,7 +341,7 @@ class mic_wasapi_t : public mic_t { } // *2 --> needs to fit double - sample_buf = util::buffer_t { std::max(frames, frame_size) * 2 * channels_in }; + sample_buf = util::buffer_t { std::max(frames, frame_size) * 2 * channels_out }; sample_buf_pos = std::begin(sample_buf); status = audio_client->GetService(IID_IAudioCaptureClient, (void **)&audio_capture); @@ -705,7 +423,7 @@ class mic_wasapi_t : public mic_t { } sample_aligned.uninitialized = std::end(sample_buf) - sample_buf_pos; - auto n = std::min(sample_aligned.uninitialized, block_aligned.audio_sample_size * channels_in); + auto n = std::min(sample_aligned.uninitialized, block_aligned.audio_sample_size * channels); if(buffer_flags & AUDCLNT_BUFFERFLAGS_SILENT) { std::fill_n(sample_buf_pos, n, 0); @@ -742,13 +460,7 @@ class mic_wasapi_t : public mic_t { util::buffer_t sample_buf; std::int16_t *sample_buf_pos; - - // out --> our audio output - int channels_out; - // in --> our wasapi input - int channels_in; - - std::unique_ptr pipe; + int channels; }; class audio_control_t : public ::platf::audio_control_t { @@ -798,8 +510,7 @@ class audio_control_t : public ::platf::audio_control_t { audio::device_t device; collection->Item(x, &device); - auto type = validate_device(device, SAMPLE_RATE); - if(type == format_t::none) { + if(!validate_device(device)) { continue; } @@ -897,9 +608,6 @@ class audio_control_t : public ::platf::audio_control_t { return std::nullopt; } - if(init_wave_format(wave_format, SAMPLE_RATE)) { - return std::nullopt; - } set_wave_format(wave_format, formats[(int)type - 1]); WAVEFORMATEXTENSIBLE p {}; From f4a48f44e40aa2def3677ffaf14d45f5e4066370 Mon Sep 17 00:00:00 2001 From: Cameron Gutman Date: Wed, 28 Dec 2022 08:30:51 -0600 Subject: [PATCH 33/88] Rework audio bitrate and quality handling (#642) --- src/audio.cpp | 41 ++++++++++++++++++++--------------------- src/audio.h | 2 ++ src/rtsp.cpp | 13 +++++++++++++ 3 files changed, 35 insertions(+), 21 deletions(-) diff --git a/src/audio.cpp b/src/audio.cpp index c5b6a976de1..0512bf2af67 100644 --- a/src/audio.cpp +++ b/src/audio.cpp @@ -39,6 +39,15 @@ opus_stream_config_t stream_configs[MAX_STREAM_CONFIG] { 1, 1, platf::speaker::map_stereo, + 96000, + }, + { + SAMPLE_RATE, + 2, + 1, + 1, + platf::speaker::map_stereo, + 512000, }, { SAMPLE_RATE, @@ -46,6 +55,7 @@ opus_stream_config_t stream_configs[MAX_STREAM_CONFIG] { 4, 2, platf::speaker::map_surround51, + 256000, }, { SAMPLE_RATE, @@ -53,6 +63,7 @@ opus_stream_config_t stream_configs[MAX_STREAM_CONFIG] { 6, 0, platf::speaker::map_surround51, + 1536000, }, { SAMPLE_RATE, @@ -60,6 +71,7 @@ opus_stream_config_t stream_configs[MAX_STREAM_CONFIG] { 5, 3, platf::speaker::map_surround71, + 450000, }, { SAMPLE_RATE, @@ -67,6 +79,7 @@ opus_stream_config_t stream_configs[MAX_STREAM_CONFIG] { 8, 0, platf::speaker::map_surround71, + 2048000, }, }; @@ -74,9 +87,7 @@ auto control_shared = safe::make_shared(start_audio_control, stop_a void encodeThread(sample_queue_t samples, config_t config, void *channel_data) { auto packets = mail::man->queue(mail::audio_packets); - - // FIXME: Pick correct opus_stream_config_t based on config.channels - auto stream = &stream_configs[map_stream(config.channels, config.flags[config_t::HIGH_QUALITY])]; + auto stream = &stream_configs[map_stream(config.channels, config.flags[config_t::HIGH_QUALITY])]; opus_t opus { opus_multistream_encoder_create( stream->sampleRate, @@ -84,17 +95,15 @@ void encodeThread(sample_queue_t samples, config_t config, void *channel_data) { stream->streams, stream->coupledStreams, stream->mapping, - OPUS_APPLICATION_AUDIO, + OPUS_APPLICATION_RESTRICTED_LOWDELAY, nullptr) }; - // For some reason, audio is crackling when the encoder is set to constant bitstream. - // We simulate a constant bitstream with OPUS_SET_BITRATE(OPUS_BITRATE_MAX) --> - // which tries to occupy as much space as possible in the packet - opus_multistream_encoder_ctl(opus.get(), OPUS_SET_BITRATE(OPUS_BITRATE_MAX)); + opus_multistream_encoder_ctl(opus.get(), OPUS_SET_BITRATE(stream->bitrate)); + opus_multistream_encoder_ctl(opus.get(), OPUS_SET_VBR(0)); auto frame_size = config.packetDuration * stream->sampleRate / 1000; while(auto sample = samples->pop()) { - buffer_t packet { 1400 }; // 1KB + buffer_t packet { 1400 }; int bytes = opus_multistream_encode(opus.get(), sample->data(), frame_size, std::begin(packet), packet.size()); if(bytes < 0) { @@ -104,14 +113,6 @@ void encodeThread(sample_queue_t samples, config_t config, void *channel_data) { return; } - // Even with OPUS_SET_BITRATE(OPUS_BITRATE_MAX), silent packets are smaller than the rest - // Drop silent packets to ensure Moonlight won't complain - // A packet size of 128 seems a reasonable enough threshold - if(bytes < 128) { - BOOST_LOG(verbose) << "Dropped silent packet"sv; - continue; - } - packet.fake_resize(bytes); packets->raise(channel_data, std::move(packet)); } @@ -119,9 +120,7 @@ void encodeThread(sample_queue_t samples, config_t config, void *channel_data) { void capture(safe::mail_t mail, config_t config, void *channel_data) { auto shutdown_event = mail->event(mail::shutdown); - - // FIXME: Pick correct opus_stream_config_t based on config.channels - auto stream = &stream_configs[map_stream(config.channels, config.flags[config_t::HIGH_QUALITY])]; + auto stream = &stream_configs[map_stream(config.channels, config.flags[config_t::HIGH_QUALITY])]; auto ref = control_shared.ref(); if(!ref) { @@ -225,7 +224,7 @@ int map_stream(int channels, bool quality) { int shift = quality ? 1 : 0; switch(channels) { case 2: - return STEREO; + return STEREO + shift; case 6: return SURROUND51 + shift; case 8: diff --git a/src/audio.h b/src/audio.h index 67f3af13989..17d3ebb4218 100644 --- a/src/audio.h +++ b/src/audio.h @@ -6,6 +6,7 @@ namespace audio { enum stream_config_e : int { STEREO, + HIGH_STEREO, SURROUND51, HIGH_SURROUND51, SURROUND71, @@ -19,6 +20,7 @@ struct opus_stream_config_t { int streams; int coupledStreams; const std::uint8_t *mapping; + int bitrate; }; extern opus_stream_config_t stream_configs[MAX_STREAM_CONFIG]; diff --git a/src/rtsp.cpp b/src/rtsp.cpp index a2ffa143629..9f2a7bbd961 100644 --- a/src/rtsp.cpp +++ b/src/rtsp.cpp @@ -646,6 +646,19 @@ void cmd_announce(rtsp_server_t *server, tcp::socket &sock, msg_t &&req) { return; } + // When using stereo audio, the audio quality is (strangely) indicated by whether the Host field + // in the RTSP message matches a local interface's IP address. Fortunately, Moonlight always sends + // 0.0.0.0 when it wants low quality, so it is easy to check without enumerating interfaces. + if(config.audio.channels == 2) { + for(auto option = req->options; option != nullptr; option = option->next) { + if("Host"sv == option->option) { + std::string_view content { option->content }; + BOOST_LOG(debug) << "Found Host: "sv << content; + config.audio.flags[audio::config_t::HIGH_QUALITY] = (content.find("0.0.0.0"sv) == std::string::npos); + } + } + } + if(config.monitor.videoFormat != 0 && config::video.hevc_mode == 1) { BOOST_LOG(warning) << "HEVC is disabled, yet the client requested HEVC"sv; From ad20572dde4b721ce9b10dfa7a7c390c39d8581c Mon Sep 17 00:00:00 2001 From: LizardByte-bot <108553330+LizardByte-bot@users.noreply.github.com> Date: Wed, 28 Dec 2022 10:48:29 -0500 Subject: [PATCH 34/88] ci: update global workflows (#643) --- .github/workflows/autoupdate.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/autoupdate.yml b/.github/workflows/autoupdate.yml index 83afbc3a77e..b1ebbb6b966 100644 --- a/.github/workflows/autoupdate.yml +++ b/.github/workflows/autoupdate.yml @@ -46,7 +46,7 @@ jobs: >> $GITHUB_OUTPUT - name: rebase - if: ${{ steps.label.outputs.central_dep == false }} + if: ${{ steps.label.outputs.central_dep == 'false' }} uses: "bbeesley/gha-auto-dependabot-rebase@v1.2.0" env: GITHUB_TOKEN: ${{ secrets.GH_BOT_TOKEN }} From a996902a335e61a226c55579890d5d3497e89461 Mon Sep 17 00:00:00 2001 From: Cameron Gutman Date: Wed, 28 Dec 2022 13:03:41 -0600 Subject: [PATCH 35/88] Generate certificates with unique serial numbers (#645) --- src/crypto.cpp | 7 ++++++- src/crypto.h | 1 + 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src/crypto.cpp b/src/crypto.cpp index c547b944aa2..c0de077295a 100644 --- a/src/crypto.cpp +++ b/src/crypto.cpp @@ -410,7 +410,12 @@ creds_t gen_creds(const std::string_view &cn, std::uint32_t key_bits) { EVP_PKEY_keygen(ctx.get(), &pkey); X509_set_version(x509.get(), 2); - ASN1_INTEGER_set(X509_get_serialNumber(x509.get()), 0); + + // Generate a real serial number to avoid SEC_ERROR_REUSED_ISSUER_AND_SERIAL with Firefox + bignum_t serial { BN_new() }; + BN_rand(serial.get(), 159, BN_RAND_TOP_ANY, BN_RAND_BOTTOM_ANY); // 159 bits to fit in 20 bytes in DER format + BN_set_negative(serial.get(), 0); // Serial numbers must be positive + BN_to_ASN1_INTEGER(serial.get(), X509_get_serialNumber(x509.get())); constexpr auto year = 60 * 60 * 24 * 365; #if OPENSSL_VERSION_NUMBER < 0x10100000L diff --git a/src/crypto.h b/src/crypto.h index b2b1ac122cd..9f2061c69af 100644 --- a/src/crypto.h +++ b/src/crypto.h @@ -31,6 +31,7 @@ using md_ctx_t = util::safe_ptr; using bio_t = util::safe_ptr; using pkey_t = util::safe_ptr; using pkey_ctx_t = util::safe_ptr; +using bignum_t = util::safe_ptr; sha256_t hash(const std::string_view &plaintext); From 27919697a7388b953bca9fc26df52bc47dfe17d2 Mon Sep 17 00:00:00 2001 From: Conn O'Griofa Date: Wed, 28 Dec 2022 20:39:05 +0000 Subject: [PATCH 36/88] CMake: NSIS: improve user upgrade experience (#587) --- CMakeLists.txt | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index c7ec75fcf99..9bb0244c585 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -528,7 +528,6 @@ if(WIN32) # see options at: https://cmake.org/cmake/help/latest/cpack_gen/nsis.h set(CPACK_PACKAGE_ICON "${CMAKE_CURRENT_SOURCE_DIR}\\\\sunshine.ico") set(CPACK_NSIS_INSTALLED_ICON_NAME "${PROJECT__DIR}\\\\${PROJECT_EXE}") set(CPACK_PACKAGE_INSTALL_DIRECTORY "${CPACK_PACKAGE_NAME}") # The name of the directory that will be created in C:/Program files/ - string(APPEND CPACK_NSIS_DEFINES "\n RequestExecutionLevel admin") # TODO: Not sure if this is needed but it took me a while to figure out where to put this option so I'm leaving it here # Extra install commands # Sets permissions on the installed folder so that we can write in it @@ -546,7 +545,7 @@ if(WIN32) # see options at: https://cmake.org/cmake/help/latest/cpack_gen/nsis.h "${CPACK_NSIS_EXTRA_UNINSTALL_COMMANDS} ExecWait '\\\"$INSTDIR\\\\scripts\\\\delete-firewall-rule.bat\\\"' ExecWait '\\\"$INSTDIR\\\\scripts\\\\uninstall-service.bat\\\"' - MessageBox MB_YESNO|MB_ICONQUESTION 'Do you want to completely remove the directory $INSTDIR and all of its contents?' IDNO NoDelete + MessageBox MB_YESNO|MB_ICONQUESTION 'Do you want to remove $INSTDIR (this includes the configuration, cover images, and settings)?' /SD IDNO IDNO NoDelete RMDir /r \\\"$INSTDIR\\\" ; skipped if no NoDelete: ") @@ -554,7 +553,6 @@ if(WIN32) # see options at: https://cmake.org/cmake/help/latest/cpack_gen/nsis.h # Adding an option for the start menu and PATH set(CPACK_NSIS_MODIFY_PATH "OFF") # TODO: it asks to add it to the PATH but is not working https://gitlab.kitware.com/cmake/cmake/-/issues/15635 set(CPACK_NSIS_EXECUTABLES_DIRECTORY ".") - set(CPACK_NSIS_MUI_FINISHPAGE_RUN "${CMAKE_PROJECT_NAME}.exe") set(CPACK_NSIS_INSTALLED_ICON_NAME "${CMAKE_PROJECT_NAME}.exe") # This will be shown on the installed apps Windows settings set(CPACK_NSIS_CREATE_ICONS_EXTRA "${CPACK_NSIS_CREATE_ICONS_EXTRA} @@ -566,7 +564,7 @@ if(WIN32) # see options at: https://cmake.org/cmake/help/latest/cpack_gen/nsis.h ") # Checking for previous installed versions - # set(CPACK_NSIS_ENABLE_UNINSTALL_BEFORE_INSTALL "ON") # TODO: doesn't work on my machine when Sunshine is already installed + set(CPACK_NSIS_ENABLE_UNINSTALL_BEFORE_INSTALL "ON") set(CPACK_NSIS_HELP_LINK "https://docs.lizardbyte.dev/projects/sunshine/en/latest/about/installation.html") set(CPACK_NSIS_URL_INFO_ABOUT "${CMAKE_PROJECT_HOMEPAGE_URL}") From 88a450bf1f781d5cbad47b56960686b05ac0ba33 Mon Sep 17 00:00:00 2001 From: ReenigneArcher <42013603+ReenigneArcher@users.noreply.github.com> Date: Wed, 28 Dec 2022 16:30:32 -0500 Subject: [PATCH 37/88] update docs (#574) --- .github/workflows/CI.yml | 3 +- CMakeLists.txt | 33 ++++-- DOCKER_README.md | 14 +-- README.rst | 103 +++++++++++++---- docs/source/about/advanced_usage.rst | 15 +++ docs/source/about/installation.rst | 33 +----- docs/source/about/third_party_packages.rst | 3 - docs/source/about/usage.rst | 107 +++++++++++++----- docs/source/building/linux.rst | 88 +------------- docs/source/building/macos.rst | 3 +- docs/source/building/windows.rst | 24 ++-- docs/source/contributing/testing.rst | 4 +- docs/source/gamestream/gamestream.rst | 21 ++++ docs/source/legal/legal.rst | 21 ++++ docs/source/toc.rst | 12 ++ docs/source/troubleshooting/general.rst | 7 ++ docs/source/troubleshooting/linux.rst | 3 + docs/source/troubleshooting/macos.rst | 3 + docs/source/troubleshooting/windows.rst | 6 +- src_assets/common/assets/web/index.html | 2 +- .../windows/misc/service/install-service.bat | 3 + tools/CMakeLists.txt | 2 +- 22 files changed, 310 insertions(+), 200 deletions(-) create mode 100644 docs/source/gamestream/gamestream.rst create mode 100644 docs/source/legal/legal.rst diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index 9298ce6338b..7d7c1fb4a8f 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -886,7 +886,6 @@ jobs: mingw-w64-x86_64-openssl mingw-w64-x86_64-opus mingw-w64-x86_64-toolchain - mingw-w64-x86_64-x265 nasm yasm @@ -903,7 +902,7 @@ jobs: -DSUNSHINE_ASSETS_DIR=assets \ -G "MinGW Makefiles" \ .. - mingw32-make -j2 + mingw32-make -j$(nproc) - name: Package Windows shell: msys2 {0} diff --git a/CMakeLists.txt b/CMakeLists.txt index 9bb0244c585..606339dbb10 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,13 +1,14 @@ cmake_minimum_required(VERSION 3.0) project(Sunshine VERSION 0.16.0 - DESCRIPTION "Sunshine is a Gamestream host for Moonlight." + DESCRIPTION "Sunshine is a self-hosted game stream host for Moonlight." HOMEPAGE_URL "https://app.lizardbyte.dev" ) -set(PROJECT_LONG_DESCRIPTION "Sunshine is a self hosted, low latency, cloud gaming solution with support for AMD, \ -Intel, and Nvidia GPUs. It is an open source implementation of NVIDIA's GameStream, as used by the NVIDIA Shield. \ -Connect to Sunshine from any Moonlight client, available for nearly any device imaginable.") +set(PROJECT_LONG_DESCRIPTION "Offering low latency, cloud gaming server capabilities with support for AMD, Intel, \ +and Nvidia GPUs for hardware encoding. Software encoding is also available. You can connect to Sunshine from any \ +Moonlight client on a variety of devices. A web UI is provided to allow configuration, and client pairing, from \ +your favorite web browser. Pair from the local server or any mobile device.") option(SUNSHINE_CONFIGURE_APPIMAGE "Configuration specific for AppImage." OFF) option(SUNSHINE_CONFIGURE_AUR "Configure files required for AUR." OFF) @@ -246,7 +247,7 @@ else() if(WAYLAND_FOUND) add_compile_definitions(SUNSHINE_BUILD_WAYLAND) macro(genWayland FILENAME) - make_directory(${CMAKE_BINARY_DIR}/generated-src) + file(MAKE_DIRECTORY ${CMAKE_BINARY_DIR}/generated-src) message("wayland-scanner private-code ${CMAKE_SOURCE_DIR}/third-party/wayland-protocols/${FILENAME}.xml ${CMAKE_BINARY_DIR}/generated-src/${FILENAME}.c") message("wayland-scanner client-header ${CMAKE_SOURCE_DIR}/third-party/wayland-protocols/${FILENAME}.xml ${CMAKE_BINARY_DIR}/generated-src/${FILENAME}.h") @@ -533,11 +534,15 @@ if(WIN32) # see options at: https://cmake.org/cmake/help/latest/cpack_gen/nsis.h # Sets permissions on the installed folder so that we can write in it # Install service SET(CPACK_NSIS_EXTRA_INSTALL_COMMANDS - "${CPACK_NSIS_EXTRA_INSTALL_COMMANDS} - ExecWait 'icacls \\\"$INSTDIR\\\" /grant:r Users:\\\(OI\\\)\\\(CI\\\)\\\(F\\\)' - ExecWait '\\\"$INSTDIR\\\\scripts\\\\add-firewall-rule.bat\\\"' - ExecWait '\\\"$INSTDIR\\\\scripts\\\\install-service.bat\\\"' - ") + "${CPACK_NSIS_EXTRA_INSTALL_COMMANDS} + ExecWait '\\\"$SYSDIR\\\\cmd.exe\\\" /c \\\"start https://sunshinestream.readthedocs.io/\\\"' + ExecWait 'icacls \\\"$INSTDIR\\\" /grant:r Users:\\\(OI\\\)\\\(CI\\\)\\\(F\\\)' + ExecWait '\\\"$INSTDIR\\\\scripts\\\\add-firewall-rule.bat\\\"' + ExecWait '\\\"$INSTDIR\\\\scripts\\\\install-service.bat\\\"' + MessageBox MB_YESNO|MB_ICONQUESTION 'Do you want to add/update ViGEmBus (virtual controller support)?' IDNO NoController + ExecWait '\\\"$SYSDIR\\\\cmd.exe\\\" /c \\\"start https://github.com/ViGEm/ViGEmBus/releases/latest\\\"' ; skipped if no + NoController: + ") # Extra uninstall commands # Uninstall service @@ -566,10 +571,16 @@ if(WIN32) # see options at: https://cmake.org/cmake/help/latest/cpack_gen/nsis.h # Checking for previous installed versions set(CPACK_NSIS_ENABLE_UNINSTALL_BEFORE_INSTALL "ON") - set(CPACK_NSIS_HELP_LINK "https://docs.lizardbyte.dev/projects/sunshine/en/latest/about/installation.html") + set(CPACK_NSIS_HELP_LINK "https://sunshinestream.readthedocs.io/about/installation.html") set(CPACK_NSIS_URL_INFO_ABOUT "${CMAKE_PROJECT_HOMEPAGE_URL}") set(CPACK_NSIS_CONTACT "${CMAKE_PROJECT_HOMEPAGE_URL}/support") + set(CPACK_NSIS_MENU_LINKS + "https://sunshinestream.readthedocs.io" "Sunshine documentation" + "https://app.lizardbyte.dev" "LizardByte Web Site" + "https://app.lizardbyte.dev/support" "LizardByte Support" + ) + # Setting components groups and dependencies # sunshine binary set(CPACK_COMPONENT_APPLICATION_DISPLAY_NAME "${CMAKE_PROJECT_NAME}") diff --git a/DOCKER_README.md b/DOCKER_README.md index 222cd163f02..71338e0a612 100644 --- a/DOCKER_README.md +++ b/DOCKER_README.md @@ -71,13 +71,13 @@ port `47990` (e.g. `http://:47990`). The internal port must be `47990`, (e.g. `-p 8080:47990`). All the ports listed in the `docker run` and `docker-compose` examples are required. -| Parameter | Function | Example Value | Required | -|-----------------------------|---------------------------|--------------------|----------| -| `-p :47990` | Web UI Port | `47990` | True | -| `-v :/config` | Volume mapping | `/home/sunshine` | True | -| `-e PUID=` | User ID | `1001` | False | -| `-e PGID=` | Group ID | `1001` | False | -| `-e TZ=` | Lookup TZ value [here][1] | `America/New_York` | False | +| Parameter | Function | Example Value | Required | +|-----------------------------|----------------------|--------------------|----------| +| `-p :47990` | Web UI Port | `47990` | True | +| `-v :/config` | Volume mapping | `/home/sunshine` | True | +| `-e PUID=` | User ID | `1001` | False | +| `-e PGID=` | Group ID | `1001` | False | +| `-e TZ=` | Lookup [TZ value][1] | `America/New_York` | False | [1]: https://en.wikipedia.org/wiki/List_of_tz_database_time_zones diff --git a/README.rst b/README.rst index f7853dee23a..c00277110d5 100644 --- a/README.rst +++ b/README.rst @@ -4,34 +4,91 @@ LizardByte has the full documentation hosted on `Read the Docs ` for additional information. Linux ----- -First, follow the instructions for your preferred package type below. - -Then start sunshine with the following command, unless a start command is listed in the specified package. - -.. code-block:: bash - - sunshine +Follow the instructions for your preferred package type below. AppImage ^^^^^^^^ -.. image:: https://img.shields.io/github/issues/lizardbyte/sunshine/pkg:appimage?logo=github&style=for-the-badge - :alt: GitHub issues by-label - According to AppImageLint the supported distro matrix of the AppImage is below. - [✖] Debian oldstable (buster) @@ -82,9 +73,6 @@ Uninstall: Debian Package ^^^^^^^^^^^^^^ -.. image:: https://img.shields.io/github/issues/lizardbyte/sunshine/pkg:deb?logo=github&style=for-the-badge - :alt: GitHub issues by-label - #. Download ``sunshine-{ubuntu-version}.deb`` and run the following code. .. code-block:: bash @@ -103,9 +91,6 @@ Uninstall: Flatpak Package ^^^^^^^^^^^^^^^ -.. image:: https://img.shields.io/github/issues/lizardbyte/sunshine/pkg:flatpak?logo=github&style=for-the-badge - :alt: GitHub issues by-label - #. Install `Flatpak `_ as required. #. Download ``sunshine_{arch}.flatpak`` and run the following code. @@ -145,9 +130,6 @@ Uninstall: RPM Package ^^^^^^^^^^^ -.. image:: https://img.shields.io/github/issues/lizardbyte/sunshine/pkg:rpm?logo=github&style=for-the-badge - :alt: GitHub issues by-label - #. Add `rpmfusion` repositories by running the following code. .. code-block:: bash @@ -170,12 +152,11 @@ Uninstall: macOS ----- -.. image:: https://img.shields.io/github/issues/lizardbyte/sunshine/os:macos?logo=github&style=for-the-badge - :alt: GitHub issues by-label +Sunshine on macOS is experimental. Gamepads do not work. Other features may not work as expected. pkg ^^^ -.. Warning:: The `pkg` does not include runtime dependencies and should be considered experimental. +.. Warning:: The `pkg` does not include runtime dependencies. #. Download the ``sunshine.pkg`` file and install it as normal. @@ -218,16 +199,14 @@ Uninstall: Windows ------- -.. image:: https://img.shields.io/github/issues/lizardbyte/sunshine/os:windows:10?logo=github&style=for-the-badge - :alt: GitHub issues by-label - -.. image:: https://img.shields.io/github/issues/lizardbyte/sunshine/os:windows:11?logo=github&style=for-the-badge - :alt: GitHub issues by-label Installer ^^^^^^^^^ #. Download and install ``sunshine-windows.exe`` +.. Attention:: You should carefully select or unselect the options you want to install. Do not blindly install or enable + features. + To uninstall, find Sunshine in the list `here `_ and select "Uninstall" from the overflow menu. Different versions of Windows may provide slightly different steps for uninstall. diff --git a/docs/source/about/third_party_packages.rst b/docs/source/about/third_party_packages.rst index ad8979dc246..40a6bc70771 100644 --- a/docs/source/about/third_party_packages.rst +++ b/docs/source/about/third_party_packages.rst @@ -52,6 +52,3 @@ Legacy GitHub Repo .. image:: https://img.shields.io/github/release-date/loki-47-6F-64/sunshine?style=for-the-badge&logo=github :alt: GitHub Release Date - -.. image:: https://img.shields.io/github/downloads/loki-47-6F-64/sunshine/total?style=for-the-badge&logo=github - :alt: GitHub Releases diff --git a/docs/source/about/usage.rst b/docs/source/about/usage.rst index 7d59b715122..f24ba0119a2 100644 --- a/docs/source/about/usage.rst +++ b/docs/source/about/usage.rst @@ -1,23 +1,35 @@ Usage ===== #. See the `setup`_ section for your specific OS. -#. Run ``sunshine /sunshine.conf``. +#. If you did not install the service, then start sunshine with the following command, unless a start command is listed + in the specified package :ref:`installation ` instructions. - .. Note:: You do not need to specify a config file. If no config file is entered the default location will be used. + .. Note:: A service is a process that runs in the background. Running multiple instances of Sunshine is not + advised. - .. Attention:: The configuration file specified will be created if it doesn't exist. + **Basic usage** + .. code-block:: bash + + sunshine + + **Specify config file** + .. code-block:: bash + + sunshine /sunshine.conf + + .. Note:: You do not need to specify a config file. If no config file is entered the default location will be used. - .. Tip:: If using the Linux AppImage, replace ``sunshine`` with ``./sunshine.AppImage`` + .. Attention:: The configuration file specified will be created if it doesn't exist. #. Configure Sunshine in the web ui The web ui is available on `https://localhost:47990 `_ by default. You may replace `localhost` with your internal ip address. - .. Attention:: Ignore any warning given by your browser about "insecure website". + .. Attention:: Ignore any warning given by your browser about "insecure website". This is due to the SSL certificate + being self signed. - .. Caution:: If running for the first time, make sure to note the username and password Sunshine showed to you, - since you cannot get back later! + .. Caution:: If running for the first time, make sure to note the username and password that you created. **Add games and applications.** This can be configured in the web ui. @@ -26,8 +38,6 @@ Usage list of applications that are started just before running a stream. This is the directory within the GitHub repo. - .. Attention:: Application list is not fully supported on macOS - #. In Moonlight, you may need to add the PC manually. #. When Moonlight request you insert the correct pin on sunshine: @@ -76,7 +86,7 @@ Sunshine needs access to `uinput` to create mouse and gamepad events. .. code-block:: [Unit] - Description=Sunshine Gamestream Server for Moonlight + Description=Sunshine self-hosted game stream host for Moonlight. [Service] ExecStart= @@ -118,7 +128,7 @@ Sunshine needs access to `uinput` to create mouse and gamepad events. sudo setcap cap_sys_admin+p $(readlink -f $(which sunshine)) - **Disable** + **Disable (for Xorg/X11)** .. code-block:: bash sudo setcap -r $(readlink -f $(which sunshine)) @@ -149,6 +159,32 @@ Windows ^^^^^^^ For gamepad support, install `ViGEmBus `_ +Sunshine firewall + **Add rule** + .. code-block:: batch + + cd /d "C:\Program Files\Sunshine\scripts" + add-firewall-rule.bat + + **Remove rule** + .. code-block:: batch + + cd /d "C:\Program Files\Sunshine\scripts" + remove-firewall-rule.bat + +Sunshine service + **Enable** + .. code-block:: batch + + cd /d "C:\Program Files\Sunshine\scripts" + install-service.bat + + **Disable** + .. code-block:: batch + + cd /d "C:\Program Files\Sunshine\scripts" + uninstall-service.bat + Shortcuts --------- All shortcuts start with ``CTRL + ALT + SHIFT``, just like Moonlight @@ -159,34 +195,43 @@ All shortcuts start with ``CTRL + ALT + SHIFT``, just like Moonlight Application List ---------------- - Applications should be configured via the web UI. -- A basic understanding of working directories and commands is recommended. +- A basic understanding of working directories and commands is required. - You can use Environment variables in place of values - ``$(HOME)`` will be replaced by the value of ``$HOME`` - ``$$`` will be replaced by ``$``, e.g. ``$$(HOME)`` will be become ``$(HOME)`` - ``env`` - Adds or overwrites Environment variables for the commands/applications run by Sunshine - ``"Variable name":"Variable value"`` - ``apps`` - The list of applications +- Advanced users may want to edit the application list manually. The format is ``json``. - Example application: .. code-block:: json { - "name":"An App", - "cmd":"command to open app", - "prep-cmd":[ - { - "do":"some-command", - "undo":"undo-that-command" - } - ], - "detached":[ - "some-command", - "another-command" - ] + "cmd": "command to open app", + "detached": [ + "some-command", + "another-command" + ], + "image-path": "/full-path/to/png-image", + "name": "An App", + "output": "/full-path/to/command-log-file", + "prep-cmd": [ + { + "do": "some-command", + "undo": "undo-that-command" + } + ], + "working-dir": "/full-path/to/working-directory" } + - ``cmd`` - The main application + - ``detached`` - A list of commands to be run and forgotten about + + - If not specified, a process is started that sleeps indefinitely + + - ``image-path`` - The full path to the cover art image to use. - ``name`` - The name of the application/game - ``output`` - The file where the output of the command is stored - - ``detached`` - A list of commands to be run and forgotten about - ``prep-cmd`` - A list of commands to be run before/after the application - If any of the prep-commands fail, starting the application is aborted @@ -199,9 +244,7 @@ Application List - This should not fail considering it is supposed to undo the ``do`` commands - If it fails, Sunshine is terminated - - ``cmd`` - The main application - - - If not specified, a process is started that sleeps indefinitely + - ``working-dir`` - The working directory to use. If not specified, Sunshine will use the application directory. Considerations -------------- @@ -214,3 +257,11 @@ Considerations - In addition to the apps listed, one app "Desktop" is hardcoded into Sunshine. It does not start an application, instead it simply starts a stream. - For the Linux flatpak you must prepend commands with ``flatpak-spawn --host``. + +Tutorials +--------- +Tutorial videos are available `here `_. + +.. admonition:: Community! + + Tutorials are community generated. Want to contribute? Reach out to us on our discord server. diff --git a/docs/source/building/linux.rst b/docs/source/building/linux.rst index 0b1c6ffe75f..2ccdd7c025b 100644 --- a/docs/source/building/linux.rst +++ b/docs/source/building/linux.rst @@ -41,7 +41,7 @@ Install Requirements nvidia-cuda-dev \ # Cuda, NvFBC nvidia-cuda-toolkit # Cuda, NvFBC -Fedora 35 +Fedora 36 ^^^^^^^^^ End of Life: TBD @@ -80,73 +80,6 @@ Install Requirements pulseaudio-libs-devel \ rpm-build # if you want to build an RPM binary package -Ubuntu 18.04 -^^^^^^^^^^^^ -End of Life: April 2028 - -Install Repositories - .. code-block:: bash - - sudo apt update && sudo apt install \ - software-properties-common \ - && add-apt-repository ppa:savoury1/boost-defaults-1.71 && \ - add-apt-repository ppa:ubuntu-toolchain-r/test && \ - -Install Requirements - .. code-block:: bash - - sudo apt install \ - build-essential \ - cmake \ - gcc-10 \ - g++-10 \ - libavdevice-dev \ - libboost-filesystem1.71-dev \ - libboost-log1.71-dev \ - libboost-regex1.71-dev \ - libboost-thread1.71-dev \ - libboost-program-options1.71-dev \ - libcap-dev \ # KMS - libdrm-dev \ # KMS - libevdev-dev \ - libnuma-dev \ - libopus-dev \ - libpulse-dev \ - libssl-dev \ - libva-dev \ - libvdpau-dev \ - libwayland-dev \ # Wayland - libx11-dev \ # X11 - libxcb-shm0-dev \ # X11 - libxcb-xfixes0-dev \ # X11 - libxcb1-dev \ # X11 - libxfixes-dev \ # X11 - libxrandr-dev \ # X11 - libxtst-dev \ # X11 - nodejs \ - npm \ - wget - -Update gcc alias - .. code-block:: bash - - update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-10 100 --slave /usr/bin/g++ g++ /usr/bin/g++-10 - -Install CuDA - .. code-block:: bash - - wget https://developer.download.nvidia.com/compute/cuda/11.4.2/local_installers/cuda_11.4.2_470.57.02_linux.run --progress=bar:force:noscroll -q --show-progress -O ./cuda.run && chmod a+x ./cuda.run - ./cuda.run --silent --toolkit --toolkitpath=/usr --no-opengl-libs --no-man-page --no-drm && rm ./cuda.run - -Install CMake - .. code-block:: bash - - wget https://cmake.org/files/v3.22/cmake-3.22.2-linux-x86_64.sh - mkdir /opt/cmake - sh /cmake-3.22.2-linux-x86_64.sh --prefix=/opt/cmake --skip-license - ln -s /opt/cmake/bin/cmake /usr/local/bin/cmake - cmake --version - Ubuntu 20.04 ^^^^^^^^^^^^ End of Life: April 2030 @@ -241,19 +174,10 @@ Build ----- .. Attention:: Ensure you are in the build directory created during the clone step earlier before continuing. -Debian based OSes - .. code-block:: bash - - cmake -DCMAKE_C_COMPILER=gcc-10 -DCMAKE_CXX_COMPILER=g++-10 .. - -Red Hat based OSes - .. code-block:: bash - - cmake -DCMAKE_C_COMPILER=gcc -DCMAKE_CXX_COMPILER=g++ .. +.. code-block:: bash -Finally - .. code-block:: bash + cmake .. + make -j ${nproc} - make -j ${nproc} - cpack -G DEB # optionally, create a deb package - cpack -G RPM # optionally, create a rpm package + cpack -G DEB # optionally, create a deb package + cpack -G RPM # optionally, create a rpm package diff --git a/docs/source/building/macos.rst b/docs/source/building/macos.rst index 5834424155a..5ff3e74e980 100644 --- a/docs/source/building/macos.rst +++ b/docs/source/building/macos.rst @@ -43,5 +43,4 @@ Build cpack -G DragNDrop # optionally, create a macOS dmg package If cmake fails complaining to find Boost, try to set the path explicitly. - ``cmake -DBOOST_ROOT=[boost path] ..``, e.g., ``cmake -DBOOST_ROOT=/opt/local/libexec/boost/1.76 ..`` - + ``cmake -DBOOST_ROOT=[boost path] ..``, e.g., ``cmake -DBOOST_ROOT=/opt/local/libexec/boost/1.80 ..`` diff --git a/docs/source/building/windows.rst b/docs/source/building/windows.rst index 70ca0c2ebbc..82b0dfe6c3d 100644 --- a/docs/source/building/windows.rst +++ b/docs/source/building/windows.rst @@ -3,14 +3,20 @@ Windows Requirements ------------ -First you need to install `MSYS2 `_, then startup "MSYS2 MinGW 64-bit" and install the -following packages using: +First you need to install `MSYS2 `_, then startup "MSYS2 MinGW 64-bit" and execute the following +codes. -.. code-block:: bash +Update all packages: + .. code-block:: bash + + pacman -Suy - pacman -S mingw-w64-x86_64-binutils mingw-w64-x86_64-openssl mingw-w64-x86_64-cmake \ - mingw-w64-x86_64-toolchain mingw-w64-x86_64-opus mingw-w64-x86_64-x265 mingw-w64-x86_64-boost \ - git mingw-w64-x86_64-make cmake make gcc +Install dependencies: + .. code-block:: bash + + pacman -S base-devel cmake diffutils gcc git make mingw-w64-x86_64-binutils \ + mingw-w64-x86_64-boost mingw-w64-x86_64-cmake mingw-w64-x86_64-curl \ + mingw-w64-x86_64-openssl mingw-w64-x86_64-opus mingw-w64-x86_64-toolchain npm dependencies ---------------- @@ -27,10 +33,8 @@ Build .. code-block:: bash - cmake -G"Unix Makefiles" .. - cmake -G"MinGW Makefiles" .. # alternatively - - mingw32-make + cmake -G "MinGW Makefiles" .. + mingw32-make -j$(nproc) cpack -G NSIS # optionally, create a windows installer cpack -G ZIP # optionally, create a windows standalone package diff --git a/docs/source/contributing/testing.rst b/docs/source/contributing/testing.rst index 512aed1ceed..0d6d7afadd5 100644 --- a/docs/source/contributing/testing.rst +++ b/docs/source/contributing/testing.rst @@ -7,11 +7,9 @@ Source code is tested against the `.clang-format` file for linting errors. The w format testing is `.github/workflows/cpp-clang-format-lint.yml`. Test clang-format locally. - .. Todo:: This documentation needs to be improved. - .. code-block:: bash - clang-format ... + find ./ -iname *.cpp -o -iname *.h -iname *.m -iname *.mm | xargs clang-format -i Sphinx ------ diff --git a/docs/source/gamestream/gamestream.rst b/docs/source/gamestream/gamestream.rst new file mode 100644 index 00000000000..c5c8d68dcce --- /dev/null +++ b/docs/source/gamestream/gamestream.rst @@ -0,0 +1,21 @@ +GameStream +========== +Nvidia announced that their GameStream service for Nvidia Games clients will be discontinued in February 2023. +Luckily, Sunshine performance is now on par with Nvidia GameStream. Many users have even reported that Sunshine +outperforms GameStream, so rest assured that Sunshine will be equally performant moving forward. + +Migration +--------- +We have developed a simple migration tool to help you migrate your GameStream games and apps to Sunshine automatically. +Please check out our `GSMS `_ project if you're interested in an automated +migration option. At the time of writing this GSMS offers the ability to migrate your custom games and apps. The +working directory, command, and image are all set in Sunshine's ``apps.json`` file. The box-art image is also copied +to a specified directory. + +Limitations +----------- +Sunshine does have some limitations, as compared to Nvidia GameStream. + +- HDR support is limited and currently HDR is converted to SDR. +- Automatic game/application list. +- Changing game settings automatically, to optimize streaming. diff --git a/docs/source/legal/legal.rst b/docs/source/legal/legal.rst new file mode 100644 index 00000000000..13bc3660140 --- /dev/null +++ b/docs/source/legal/legal.rst @@ -0,0 +1,21 @@ +Legal +===== +.. Attention:: This documentation is for informational purposes only and is not intended as legal advice. If you have + any legal questions or concerns about using Sunshine, we recommend consulting with a lawyer. + +Sunshine is licensed under the GPL-3.0 license, which allows for free use and modification of the software. +The full text of the license can be reviewed `here `_. + +Commercial Use +-------------- +Sunshine can be used in commercial applications without any limitations. This means that businesses and organizations +can use Sunshine to create and sell products or services without needing to seek permission or pay a fee. + +However, it is important to note that the GPL-3.0 license does not grant any rights to distribute or sell the encoders +contained within Sunshine. If you plan to sell access to Sunshine as part of their distribution, you are responsible +for obtaining the necessary licenses to do so. This may include obtaining a license from the +Motion Picture Experts Group (MPEG-LA) and/or any other necessary licensing requirements. + +In summary, while Sunshine is free to use, it is the user's responsibility to ensure compliance with all applicable +licensing requirements when redistributing the software as part of a commercial offering. If you have any questions or +concerns about using Sunshine in a commercial setting, we recommend consulting with a lawyer. diff --git a/docs/source/toc.rst b/docs/source/toc.rst index efafa92bff1..eff935bb890 100644 --- a/docs/source/toc.rst +++ b/docs/source/toc.rst @@ -9,6 +9,12 @@ about/usage about/advanced_usage +.. toctree:: + :maxdepth: 2 + :caption: GameStream + + gamestream/gamestream + .. toctree:: :maxdepth: 2 :caption: Troubleshooting @@ -34,3 +40,9 @@ contributing/contributing contributing/localization contributing/testing + +.. toctree:: + :maxdepth: 2 + :caption: Legal + + legal/legal diff --git a/docs/source/troubleshooting/general.rst b/docs/source/troubleshooting/general.rst index 2bbb93c9f44..3b6d6aad241 100644 --- a/docs/source/troubleshooting/general.rst +++ b/docs/source/troubleshooting/general.rst @@ -1,13 +1,20 @@ General ======= + +Forgotten Credentials +--------------------- If you forgot your credentials to the web UI, try this. .. code-block:: bash sunshine --creds +Web UI Access +------------- Can't access the web UI? #. Check firewall rules. +Nvidia issues +------------- NvFBC, NvENC, or general issues with Nvidia graphics card. - Consumer grade Nvidia cards are software limited to a specific number of encodes. See `Video Encode and Decode GPU Support Matrix `_ diff --git a/docs/source/troubleshooting/linux.rst b/docs/source/troubleshooting/linux.rst index 0c8e3f979ba..5c2d7c8a37c 100644 --- a/docs/source/troubleshooting/linux.rst +++ b/docs/source/troubleshooting/linux.rst @@ -1,5 +1,8 @@ Linux ===== + +KMS Streaming fails +------------------- If screencasting fails with KMS, you may need to run the following to force unprivileged screencasting. .. code-block:: bash diff --git a/docs/source/troubleshooting/macos.rst b/docs/source/troubleshooting/macos.rst index 70a0d60c22f..551f6945f4a 100644 --- a/docs/source/troubleshooting/macos.rst +++ b/docs/source/troubleshooting/macos.rst @@ -1,5 +1,8 @@ macOS ===== + +Dynamic session lookup failed +----------------------------- If you get this error: `Dynamic session lookup supported but failed: launchd did not provide a socket path, verify that org.freedesktop.dbus-session.plist is loaded!` diff --git a/docs/source/troubleshooting/windows.rst b/docs/source/troubleshooting/windows.rst index 15053519cb1..31a1a6bafbe 100644 --- a/docs/source/troubleshooting/windows.rst +++ b/docs/source/troubleshooting/windows.rst @@ -1,4 +1,6 @@ Windows ======= -No gamepad is detected. - #. Verify that you've installed `ViGEmBus `_. + +No gamepad detected +------------------- +#. Verify that you've installed `ViGEmBus `_. diff --git a/src_assets/common/assets/web/index.html b/src_assets/common/assets/web/index.html index 55b41947ba9..a5d8650532f 100644 --- a/src_assets/common/assets/web/index.html +++ b/src_assets/common/assets/web/index.html @@ -1,6 +1,6 @@

Hello, Sunshine!

-

Sunshine is a Gamestream host for Moonlight

+

Sunshine is a self-hosted game stream host for Moonlight.

diff --git a/src_assets/windows/misc/service/install-service.bat b/src_assets/windows/misc/service/install-service.bat index af27fec15d1..576563bc730 100644 --- a/src_assets/windows/misc/service/install-service.bat +++ b/src_assets/windows/misc/service/install-service.bat @@ -23,5 +23,8 @@ if %ERRORLEVEL%==0 ( rem Run the sc command to create/reconfigure the service sc %SC_CMD% %SERVICE_NAME% binPath= %SERVICE_BIN% start= %SERVICE_START_TYPE% +rem Set the description of the service +sc description %SERVICE_NAME% "Sunshine is a self-hosted game stream host for Moonlight." + rem Start the new service net start %SERVICE_NAME% diff --git a/tools/CMakeLists.txt b/tools/CMakeLists.txt index 887b4267443..a66bbb2cb80 100644 --- a/tools/CMakeLists.txt +++ b/tools/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 2.8) +cmake_minimum_required(VERSION 3.0) project(sunshine_tools) From dc5571ba987adba9fc34ab39196893cba111a70c Mon Sep 17 00:00:00 2001 From: ReenigneArcher <42013603+ReenigneArcher@users.noreply.github.com> Date: Thu, 29 Dec 2022 08:24:13 -0500 Subject: [PATCH 38/88] add cmake-lint action (#646) --- ...cpp-clang-format-lint.yml => cpp-lint.yml} | 35 +- CMakeLists.txt | 1101 +++++++++-------- cmake/FindFFMPEG.cmake | 144 --- cmake/FindLIBDRM.cmake | 2 +- cmake/FindWayland.cmake | 88 +- 5 files changed, 660 insertions(+), 710 deletions(-) rename .github/workflows/{cpp-clang-format-lint.yml => cpp-lint.yml} (60%) delete mode 100644 cmake/FindFFMPEG.cmake diff --git a/.github/workflows/cpp-clang-format-lint.yml b/.github/workflows/cpp-lint.yml similarity index 60% rename from .github/workflows/cpp-clang-format-lint.yml rename to .github/workflows/cpp-lint.yml index b0b87f9eaaa..77ee726d605 100644 --- a/.github/workflows/cpp-clang-format-lint.yml +++ b/.github/workflows/cpp-lint.yml @@ -3,7 +3,7 @@ # Don't make changes to this file in this repo as they will be overwritten with changes made to the same file in # the above-mentioned repo. -name: Clang Format Lint +name: C++ Lint on: pull_request: @@ -33,7 +33,7 @@ jobs: outputs: src: ${{ steps.check.outputs.src }} - lint: + clang-format: name: Clang Format Lint needs: [check_src] if: ${{ needs.check_src.outputs.src == 'true' }} @@ -58,3 +58,34 @@ jobs: with: name: clang-format-fixes path: src/ + + cmake-lint: + name: CMake Lint + runs-on: ubuntu-latest + + steps: + - name: Checkout + uses: actions/checkout@v3 + + - name: Set up Python + uses: actions/setup-python@v4 + with: + python-version: '3.11' + + - name: Install dependencies + run: | + python -m pip install --upgrade pip setuptools cmakelang + + - name: Find cmake files + id: cmake_files + run: | + cmake_files=$(find . -type f -iname "CMakeLists.txt" -o -iname "*.cmake") + + echo "found cmake files: ${cmake_files}" + + # do not quote to keep this as a single line + echo cmake_files=${cmake_files} >> $GITHUB_OUTPUT + + - name: Test with cmake-lint + run: | + cmake-lint --line-width 120 --tab-size 4 ${{ steps.cmake_files.outputs.cmake_files }} diff --git a/CMakeLists.txt b/CMakeLists.txt index 606339dbb10..ef731b519d7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,9 +1,8 @@ cmake_minimum_required(VERSION 3.0) project(Sunshine VERSION 0.16.0 - DESCRIPTION "Sunshine is a self-hosted game stream host for Moonlight." - HOMEPAGE_URL "https://app.lizardbyte.dev" - ) + DESCRIPTION "Sunshine is a self-hosted game stream host for Moonlight." + HOMEPAGE_URL "https://app.lizardbyte.dev") set(PROJECT_LONG_DESCRIPTION "Offering low latency, cloud gaming server capabilities with support for AMD, Intel, \ and Nvidia GPUs for hardware encoding. Software encoding is also available. You can connect to Sunshine from any \ @@ -18,37 +17,38 @@ option(SUNSHINE_CONFIGURE_PORTFILE "Configure macOS Portfile." OFF) option(SUNSHINE_CONFIGURE_ONLY "Configure special files only, then exit." OFF) if(${SUNSHINE_CONFIGURE_APPIMAGE}) - configure_file(packaging/linux/sunshine.desktop sunshine.desktop @ONLY) + configure_file(packaging/linux/sunshine.desktop sunshine.desktop @ONLY) elseif(${SUNSHINE_CONFIGURE_AUR}) - configure_file(packaging/linux/aur/PKGBUILD PKGBUILD @ONLY) + configure_file(packaging/linux/aur/PKGBUILD PKGBUILD @ONLY) elseif(${SUNSHINE_CONFIGURE_FLATPAK_MAN}) - configure_file(packaging/linux/flatpak/dev.lizardbyte.sunshine.yml dev.lizardbyte.sunshine.yml @ONLY) + configure_file(packaging/linux/flatpak/dev.lizardbyte.sunshine.yml dev.lizardbyte.sunshine.yml @ONLY) elseif(${SUNSHINE_CONFIGURE_PORTFILE}) - configure_file(packaging/macos/Portfile Portfile @ONLY) + configure_file(packaging/macos/Portfile Portfile @ONLY) endif() # return if configure only is set if(${SUNSHINE_CONFIGURE_ONLY}) - return() + return() endif() set(CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake) set(SUNSHINE_SOURCE_ASSETS_DIR "${CMAKE_CURRENT_SOURCE_DIR}/src_assets") if(APPLE) - macro(ADD_FRAMEWORK fwname appname) - find_library(FRAMEWORK_${fwname} - NAMES ${fwname} - PATHS ${CMAKE_OSX_SYSROOT}/System/Library - PATH_SUFFIXES Frameworks - NO_DEFAULT_PATH) - if( ${FRAMEWORK_${fwname}} STREQUAL FRAMEWORK_${fwname}-NOTFOUND) - MESSAGE(ERROR ": Framework ${fwname} not found") - else() - TARGET_LINK_LIBRARIES(${appname} "${FRAMEWORK_${fwname}}/${fwname}") - MESSAGE(STATUS "Framework ${fwname} found at ${FRAMEWORK_${fwname}}") - endif() - endmacro(ADD_FRAMEWORK) + # ADD_FRAMEWORK: args = `fwname`, `appname` + macro(ADD_FRAMEWORK fwname appname) + find_library(FRAMEWORK_${fwname} + NAMES ${fwname} + PATHS ${CMAKE_OSX_SYSROOT}/System/Library + PATH_SUFFIXES Frameworks + NO_DEFAULT_PATH) + if( ${FRAMEWORK_${fwname}} STREQUAL FRAMEWORK_${fwname}-NOTFOUND) + MESSAGE(ERROR ": Framework ${fwname} not found") + else() + TARGET_LINK_LIBRARIES(${appname} "${FRAMEWORK_${fwname}}/${fwname}") + MESSAGE(STATUS "Framework ${fwname} found at ${FRAMEWORK_${fwname}}") + endif() + endmacro(ADD_FRAMEWORK) endif() add_subdirectory(third-party/moonlight-common-c/enet) @@ -67,413 +67,420 @@ find_package(PkgConfig REQUIRED) pkg_check_modules (CURL REQUIRED libcurl) if(NOT APPLE) - set(Boost_USE_STATIC_LIBS ON) + set(Boost_USE_STATIC_LIBS ON) # cmake-lint: disable=C0103 endif() find_package(Boost COMPONENTS log filesystem program_options REQUIRED) list(APPEND SUNSHINE_COMPILE_OPTIONS -Wall -Wno-missing-braces -Wno-maybe-uninitialized -Wno-sign-compare) if(WIN32) - enable_language(RC) - set(CMAKE_RC_COMPILER windres) - set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -static") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${CURL_STATIC_LDFLAGS} ${CURL_STATIC_CFLAGS}") - - # WORKAROUND: /mingw64/include/_mingw.h r186 defines _WIN32_WINNT=0x601, but boost - # is built against version 0x603 (BOOST_WINAPI_VERSION_WINBLUE) from r157 header. - ADD_DEFINITIONS(-DBOOST_USE_WINAPI_VERSION=BOOST_WINAPI_VERSION_WINBLUE) - - add_compile_definitions(SUNSHINE_PLATFORM="windows") - add_subdirectory(tools) # This is temporary, only tools for Windows are needed, for now - - include_directories(third-party/ViGEmClient/include) - - if(NOT DEFINED SUNSHINE_ICON_PATH) - set(SUNSHINE_ICON_PATH "${CMAKE_CURRENT_SOURCE_DIR}/sunshine.ico") - endif() - configure_file(src/platform/windows/windows.rs.in windows.rc @ONLY) - set(PLATFORM_TARGET_FILES - "${CMAKE_CURRENT_BINARY_DIR}/windows.rc" - src/platform/windows/publish.cpp - src/platform/windows/misc.h - src/platform/windows/misc.cpp - src/platform/windows/input.cpp - src/platform/windows/display.h - src/platform/windows/display_base.cpp - src/platform/windows/display_vram.cpp - src/platform/windows/display_ram.cpp - src/platform/windows/audio.cpp - third-party/ViGEmClient/src/ViGEmClient.cpp - third-party/ViGEmClient/include/ViGEm/Client.h - third-party/ViGEmClient/include/ViGEm/Common.h - third-party/ViGEmClient/include/ViGEm/Util.h - third-party/ViGEmClient/include/ViGEm/km/BusShared.h) - - set(OPENSSL_LIBRARIES - libssl.a - libcrypto.a) - - list(PREPEND PLATFORM_LIBRARIES - libstdc++.a - libwinpthread.a - libssp.a - ksuser - wsock32 - ws2_32 - d3d11 dxgi D3DCompiler - setupapi - dwmapi - userenv - synchronization.lib - ${CURL_STATIC_LIBRARIES} - ) - - set_source_files_properties(third-party/ViGEmClient/src/ViGEmClient.cpp PROPERTIES COMPILE_DEFINITIONS "UNICODE=1;ERROR_INVALID_DEVICE_OBJECT_PARAMETER=650") - set_source_files_properties(third-party/ViGEmClient/src/ViGEmClient.cpp PROPERTIES COMPILE_FLAGS "-Wno-unknown-pragmas -Wno-misleading-indentation -Wno-class-memaccess") + enable_language(RC) + set(CMAKE_RC_COMPILER windres) + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -static") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${CURL_STATIC_LDFLAGS} ${CURL_STATIC_CFLAGS}") + + # WORKAROUND: /mingw64/include/_mingw.h r186 defines _WIN32_WINNT=0x601, but boost + # is built against version 0x603 (BOOST_WINAPI_VERSION_WINBLUE) from r157 header. + ADD_DEFINITIONS(-DBOOST_USE_WINAPI_VERSION=BOOST_WINAPI_VERSION_WINBLUE) + + add_compile_definitions(SUNSHINE_PLATFORM="windows") + add_subdirectory(tools) # This is temporary, only tools for Windows are needed, for now + + include_directories(third-party/ViGEmClient/include) + + if(NOT DEFINED SUNSHINE_ICON_PATH) + set(SUNSHINE_ICON_PATH "${CMAKE_CURRENT_SOURCE_DIR}/sunshine.ico") + endif() + configure_file(src/platform/windows/windows.rs.in windows.rc @ONLY) + set(PLATFORM_TARGET_FILES + "${CMAKE_CURRENT_BINARY_DIR}/windows.rc" + src/platform/windows/publish.cpp + src/platform/windows/misc.h + src/platform/windows/misc.cpp + src/platform/windows/input.cpp + src/platform/windows/display.h + src/platform/windows/display_base.cpp + src/platform/windows/display_vram.cpp + src/platform/windows/display_ram.cpp + src/platform/windows/audio.cpp + third-party/ViGEmClient/src/ViGEmClient.cpp + third-party/ViGEmClient/include/ViGEm/Client.h + third-party/ViGEmClient/include/ViGEm/Common.h + third-party/ViGEmClient/include/ViGEm/Util.h + third-party/ViGEmClient/include/ViGEm/km/BusShared.h) + + set(OPENSSL_LIBRARIES + libssl.a + libcrypto.a) + + list(PREPEND PLATFORM_LIBRARIES + libstdc++.a + libwinpthread.a + libssp.a + ksuser + wsock32 + ws2_32 + d3d11 dxgi D3DCompiler + setupapi + dwmapi + userenv + synchronization.lib + ${CURL_STATIC_LIBRARIES}) + + set_source_files_properties(third-party/ViGEmClient/src/ViGEmClient.cpp + PROPERTIES COMPILE_DEFINITIONS "UNICODE=1;ERROR_INVALID_DEVICE_OBJECT_PARAMETER=650") + set_source_files_properties(third-party/ViGEmClient/src/ViGEmClient.cpp + PROPERTIES COMPILE_FLAGS "-Wno-unknown-pragmas -Wno-misleading-indentation -Wno-class-memaccess") elseif(APPLE) - add_compile_definitions(SUNSHINE_PLATFORM="macos") - - option(SUNSHINE_MACOS_PACKAGE "Should only be used when creating a MACOS package/dmg." OFF) - - link_directories(/opt/local/lib) - link_directories(/usr/local/lib) - ADD_DEFINITIONS(-DBOOST_LOG_DYN_LINK) - - FIND_LIBRARY(APP_SERVICES_LIBRARY ApplicationServices ) - FIND_LIBRARY(AV_FOUNDATION_LIBRARY AVFoundation ) - FIND_LIBRARY(CORE_MEDIA_LIBRARY CoreMedia ) - FIND_LIBRARY(CORE_VIDEO_LIBRARY CoreVideo ) - FIND_LIBRARY(VIDEO_TOOLBOX_LIBRARY VideoToolbox ) - FIND_LIBRARY(FOUNDATION_LIBRARY Foundation ) - list(APPEND SUNSHINE_EXTERNAL_LIBRARIES - ${APP_SERVICES_LIBRARY} - ${AV_FOUNDATION_LIBRARY} - ${CORE_MEDIA_LIBRARY} - ${CORE_VIDEO_LIBRARY} - ${VIDEO_TOOLBOX_LIBRARY} - ${FOUNDATION_LIBRARY}) - - set(PLATFORM_INCLUDE_DIRS - ${Boost_INCLUDE_DIR}) - - set(APPLE_PLIST_FILE ${SUNSHINE_SOURCE_ASSETS_DIR}/macos/assets/Info.plist) - - set(PLATFORM_TARGET_FILES - src/platform/macos/av_audio.h - src/platform/macos/av_audio.m - src/platform/macos/av_img_t.h - src/platform/macos/av_video.h - src/platform/macos/av_video.m - src/platform/macos/display.mm - src/platform/macos/input.cpp - src/platform/macos/microphone.mm - src/platform/macos/misc.cpp - src/platform/macos/misc.h - src/platform/macos/nv12_zero_device.cpp - src/platform/macos/nv12_zero_device.h - src/platform/macos/publish.cpp - third-party/TPCircularBuffer/TPCircularBuffer.c - third-party/TPCircularBuffer/TPCircularBuffer.h - ${APPLE_PLIST_FILE}) + add_compile_definitions(SUNSHINE_PLATFORM="macos") + + option(SUNSHINE_MACOS_PACKAGE "Should only be used when creating a MACOS package/dmg." OFF) + + link_directories(/opt/local/lib) + link_directories(/usr/local/lib) + ADD_DEFINITIONS(-DBOOST_LOG_DYN_LINK) + + FIND_LIBRARY(APP_SERVICES_LIBRARY ApplicationServices ) + FIND_LIBRARY(AV_FOUNDATION_LIBRARY AVFoundation ) + FIND_LIBRARY(CORE_MEDIA_LIBRARY CoreMedia ) + FIND_LIBRARY(CORE_VIDEO_LIBRARY CoreVideo ) + FIND_LIBRARY(VIDEO_TOOLBOX_LIBRARY VideoToolbox ) + FIND_LIBRARY(FOUNDATION_LIBRARY Foundation ) + list(APPEND SUNSHINE_EXTERNAL_LIBRARIES + ${APP_SERVICES_LIBRARY} + ${AV_FOUNDATION_LIBRARY} + ${CORE_MEDIA_LIBRARY} + ${CORE_VIDEO_LIBRARY} + ${VIDEO_TOOLBOX_LIBRARY} + ${FOUNDATION_LIBRARY}) + + set(PLATFORM_INCLUDE_DIRS + ${Boost_INCLUDE_DIR}) + + set(APPLE_PLIST_FILE ${SUNSHINE_SOURCE_ASSETS_DIR}/macos/assets/Info.plist) + + set(PLATFORM_TARGET_FILES + src/platform/macos/av_audio.h + src/platform/macos/av_audio.m + src/platform/macos/av_img_t.h + src/platform/macos/av_video.h + src/platform/macos/av_video.m + src/platform/macos/display.mm + src/platform/macos/input.cpp + src/platform/macos/microphone.mm + src/platform/macos/misc.cpp + src/platform/macos/misc.h + src/platform/macos/nv12_zero_device.cpp + src/platform/macos/nv12_zero_device.h + src/platform/macos/publish.cpp + third-party/TPCircularBuffer/TPCircularBuffer.c + third-party/TPCircularBuffer/TPCircularBuffer.h + ${APPLE_PLIST_FILE}) else() - add_compile_definitions(SUNSHINE_PLATFORM="linux") - - option(SUNSHINE_ENABLE_DRM "Enable KMS grab if available" ON) - option(SUNSHINE_ENABLE_X11 "Enable X11 grab if available" ON) - option(SUNSHINE_ENABLE_WAYLAND "Enable building wayland specific code" ON) - option(SUNSHINE_ENABLE_CUDA "Enable cuda specific code" ON) - - if(${SUNSHINE_ENABLE_X11}) - find_package(X11) - else() - set(X11_FOUND OFF) - endif() - - set(CUDA_FOUND OFF) - if(${SUNSHINE_ENABLE_CUDA}) - include(CheckLanguage) - check_language(CUDA) - - if(CMAKE_CUDA_COMPILER) - if(NOT DEFINED CMAKE_CUDA_ARCHITECTURES) - set(CMAKE_CUDA_ARCHITECTURES 35) - endif() - - set(CUDA_FOUND ON) - enable_language(CUDA) - endif() - endif() - if(${SUNSHINE_ENABLE_DRM}) - find_package(LIBDRM) - find_package(LIBCAP) - else() - set(LIBDRM_FOUND OFF) - set(LIBCAP_FOUND OFF) - endif() - if(${SUNSHINE_ENABLE_WAYLAND}) - find_package(Wayland) - else() - set(WAYLAND_FOUND OFF) - endif() - - if(X11_FOUND) - add_compile_definitions(SUNSHINE_BUILD_X11) - include_directories(${X11_INCLUDE_DIR}) - list(APPEND PLATFORM_TARGET_FILES src/platform/linux/x11grab.cpp) - endif() - - if(CUDA_FOUND) - include_directories(third-party/nvfbc) - list(APPEND PLATFORM_TARGET_FILES - src/platform/linux/cuda.cu - src/platform/linux/cuda.cpp - third-party/nvfbc/NvFBC.h) - - add_compile_definitions(SUNSHINE_BUILD_CUDA) - endif() - - if(LIBDRM_FOUND AND LIBCAP_FOUND) - add_compile_definitions(SUNSHINE_BUILD_DRM) - include_directories(${LIBDRM_INCLUDE_DIRS} ${LIBCAP_INCLUDE_DIRS}) - list(APPEND PLATFORM_LIBRARIES ${LIBDRM_LIBRARIES} ${LIBCAP_LIBRARIES}) - list(APPEND PLATFORM_TARGET_FILES src/platform/linux/kmsgrab.cpp) - list(APPEND SUNSHINE_DEFINITIONS EGL_NO_X11=1) - elseif(LIBDRM_FOUND) - message(WARNING "Found libdrm, yet there is no libcap") - elseif(LIBDRM_FOUND) - message(WARNING "Found libcap, yet there is no libdrm") - endif() - - if(WAYLAND_FOUND) - add_compile_definitions(SUNSHINE_BUILD_WAYLAND) - macro(genWayland FILENAME) - file(MAKE_DIRECTORY ${CMAKE_BINARY_DIR}/generated-src) - - message("wayland-scanner private-code ${CMAKE_SOURCE_DIR}/third-party/wayland-protocols/${FILENAME}.xml ${CMAKE_BINARY_DIR}/generated-src/${FILENAME}.c") - message("wayland-scanner client-header ${CMAKE_SOURCE_DIR}/third-party/wayland-protocols/${FILENAME}.xml ${CMAKE_BINARY_DIR}/generated-src/${FILENAME}.h") - execute_process( - COMMAND wayland-scanner private-code ${CMAKE_SOURCE_DIR}/third-party/wayland-protocols/${FILENAME}.xml ${CMAKE_BINARY_DIR}/generated-src/${FILENAME}.c - COMMAND wayland-scanner client-header ${CMAKE_SOURCE_DIR}/third-party/wayland-protocols/${FILENAME}.xml ${CMAKE_BINARY_DIR}/generated-src/${FILENAME}.h - - RESULT_VARIABLE EXIT_INT - ) - - if(NOT ${EXIT_INT} EQUAL 0) - message(FATAL_ERROR "wayland-scanner failed") - endif() - - list(APPEND PLATFORM_TARGET_FILES - ${CMAKE_BINARY_DIR}/generated-src/${FILENAME}.c - ${CMAKE_BINARY_DIR}/generated-src/${FILENAME}.h - ) - endmacro() - - genWayland(xdg-output-unstable-v1) - genWayland(wlr-export-dmabuf-unstable-v1) - - include_directories( - ${WAYLAND_INCLUDE_DIRS} - ${CMAKE_BINARY_DIR}/generated-src - ) - - list(APPEND PLATFORM_LIBRARIES ${WAYLAND_LIBRARIES}) - list(APPEND PLATFORM_TARGET_FILES - src/platform/linux/wlgrab.cpp - src/platform/linux/wayland.cpp) - endif() - if(NOT ${X11_FOUND} AND NOT (${LIBDRM_FOUND} AND ${LIBCAP_FOUND}) AND NOT ${WAYLAND_FOUND} AND NOT ${}) - message(FATAL_ERROR "Couldn't find either x11, wayland, cuda or (libdrm and libcap)") - endif() - - list(APPEND PLATFORM_TARGET_FILES - src/platform/linux/publish.cpp - src/platform/linux/vaapi.h - src/platform/linux/vaapi.cpp - src/platform/linux/cuda.h - src/platform/linux/graphics.h - src/platform/linux/graphics.cpp - src/platform/linux/misc.h - src/platform/linux/misc.cpp - src/platform/linux/audio.cpp - src/platform/linux/input.cpp - src/platform/linux/x11grab.h - src/platform/linux/wayland.h - third-party/glad/src/egl.c - third-party/glad/src/gl.c - third-party/glad/include/EGL/eglplatform.h - third-party/glad/include/KHR/khrplatform.h - third-party/glad/include/glad/gl.h - third-party/glad/include/glad/egl.h) - - list(APPEND PLATFORM_LIBRARIES - dl - evdev - numa - pulse - pulse-simple - ) - - include_directories( - /usr/include/libevdev-1.0 - third-party/nv-codec-headers/include - third-party/glad/include) - - if(NOT DEFINED SUNSHINE_EXECUTABLE_PATH) - set(SUNSHINE_EXECUTABLE_PATH "sunshine") - endif() - configure_file(sunshine.service.in sunshine.service @ONLY) + add_compile_definitions(SUNSHINE_PLATFORM="linux") + + option(SUNSHINE_ENABLE_DRM "Enable KMS grab if available" ON) + option(SUNSHINE_ENABLE_X11 "Enable X11 grab if available" ON) + option(SUNSHINE_ENABLE_WAYLAND "Enable building wayland specific code" ON) + option(SUNSHINE_ENABLE_CUDA "Enable cuda specific code" ON) + + if(${SUNSHINE_ENABLE_X11}) + find_package(X11) + else() + set(X11_FOUND OFF) + endif() + + set(CUDA_FOUND OFF) + if(${SUNSHINE_ENABLE_CUDA}) + include(CheckLanguage) + check_language(CUDA) + + if(CMAKE_CUDA_COMPILER) + if(NOT DEFINED CMAKE_CUDA_ARCHITECTURES) + set(CMAKE_CUDA_ARCHITECTURES 35) + endif() + + set(CUDA_FOUND ON) + enable_language(CUDA) + endif() + endif() + if(${SUNSHINE_ENABLE_DRM}) + find_package(LIBDRM) + find_package(LIBCAP) + else() + set(LIBDRM_FOUND OFF) + set(LIBCAP_FOUND OFF) + endif() + if(${SUNSHINE_ENABLE_WAYLAND}) + find_package(Wayland) + else() + set(WAYLAND_FOUND OFF) + endif() + + if(X11_FOUND) + add_compile_definitions(SUNSHINE_BUILD_X11) + include_directories(${X11_INCLUDE_DIR}) + list(APPEND PLATFORM_TARGET_FILES src/platform/linux/x11grab.cpp) + endif() + + if(CUDA_FOUND) + include_directories(third-party/nvfbc) + list(APPEND PLATFORM_TARGET_FILES + src/platform/linux/cuda.cu + src/platform/linux/cuda.cpp + third-party/nvfbc/NvFBC.h) + + add_compile_definitions(SUNSHINE_BUILD_CUDA) + endif() + + if(LIBDRM_FOUND AND LIBCAP_FOUND) + add_compile_definitions(SUNSHINE_BUILD_DRM) + include_directories(${LIBDRM_INCLUDE_DIRS} ${LIBCAP_INCLUDE_DIRS}) + list(APPEND PLATFORM_LIBRARIES ${LIBDRM_LIBRARIES} ${LIBCAP_LIBRARIES}) + list(APPEND PLATFORM_TARGET_FILES src/platform/linux/kmsgrab.cpp) + list(APPEND SUNSHINE_DEFINITIONS EGL_NO_X11=1) + elseif(LIBDRM_FOUND) + message(WARNING "Found libdrm, yet there is no libcap") + elseif(LIBDRM_FOUND) + message(WARNING "Found libcap, yet there is no libdrm") + endif() + + if(WAYLAND_FOUND) + add_compile_definitions(SUNSHINE_BUILD_WAYLAND) + # GEN_WAYLAND: args = `filename` + macro(GEN_WAYLAND filename) + file(MAKE_DIRECTORY ${CMAKE_BINARY_DIR}/generated-src) + + message("wayland-scanner private-code \ +${CMAKE_SOURCE_DIR}/third-party/wayland-protocols/${filename}.xml \ +${CMAKE_BINARY_DIR}/generated-src/${filename}.c") + message("wayland-scanner client-header \ +${CMAKE_SOURCE_DIR}/third-party/wayland-protocols/${filename}.xml \ +${CMAKE_BINARY_DIR}/generated-src/${filename}.h") + execute_process( + COMMAND wayland-scanner private-code + ${CMAKE_SOURCE_DIR}/third-party/wayland-protocols/${filename}.xml + ${CMAKE_BINARY_DIR}/generated-src/${filename}.c + COMMAND wayland-scanner client-header + ${CMAKE_SOURCE_DIR}/third-party/wayland-protocols/${filename}.xml + ${CMAKE_BINARY_DIR}/generated-src/${filename}.h + + RESULT_VARIABLE EXIT_INT + ) + + if(NOT ${EXIT_INT} EQUAL 0) + message(FATAL_ERROR "wayland-scanner failed") + endif() + + list(APPEND PLATFORM_TARGET_FILES + ${CMAKE_BINARY_DIR}/generated-src/${filename}.c + ${CMAKE_BINARY_DIR}/generated-src/${filename}.h) + endmacro() + + GEN_WAYLAND(xdg-output-unstable-v1) + GEN_WAYLAND(wlr-export-dmabuf-unstable-v1) + + include_directories( + ${WAYLAND_INCLUDE_DIRS} + ${CMAKE_BINARY_DIR}/generated-src + ) + + list(APPEND PLATFORM_LIBRARIES ${WAYLAND_LIBRARIES}) + list(APPEND PLATFORM_TARGET_FILES + src/platform/linux/wlgrab.cpp + src/platform/linux/wayland.cpp) + endif() + if(NOT ${X11_FOUND} AND NOT (${LIBDRM_FOUND} AND ${LIBCAP_FOUND}) AND NOT ${WAYLAND_FOUND} AND NOT ${}) + message(FATAL_ERROR "Couldn't find either x11, wayland, cuda or (libdrm and libcap)") + endif() + + list(APPEND PLATFORM_TARGET_FILES + src/platform/linux/publish.cpp + src/platform/linux/vaapi.h + src/platform/linux/vaapi.cpp + src/platform/linux/cuda.h + src/platform/linux/graphics.h + src/platform/linux/graphics.cpp + src/platform/linux/misc.h + src/platform/linux/misc.cpp + src/platform/linux/audio.cpp + src/platform/linux/input.cpp + src/platform/linux/x11grab.h + src/platform/linux/wayland.h + third-party/glad/src/egl.c + third-party/glad/src/gl.c + third-party/glad/include/EGL/eglplatform.h + third-party/glad/include/KHR/khrplatform.h + third-party/glad/include/glad/gl.h + third-party/glad/include/glad/egl.h) + + list(APPEND PLATFORM_LIBRARIES + dl + evdev + numa + pulse + pulse-simple) + + include_directories( + /usr/include/libevdev-1.0 + third-party/nv-codec-headers/include + third-party/glad/include) + + if(NOT DEFINED SUNSHINE_EXECUTABLE_PATH) + set(SUNSHINE_EXECUTABLE_PATH "sunshine") + endif() + configure_file(sunshine.service.in sunshine.service @ONLY) endif() configure_file(version.h.in version.h @ONLY) include_directories(${CMAKE_CURRENT_BINARY_DIR}) set(SUNSHINE_TARGET_FILES - third-party/moonlight-common-c/reedsolomon/rs.c - third-party/moonlight-common-c/reedsolomon/rs.h - third-party/moonlight-common-c/src/Input.h - third-party/moonlight-common-c/src/Rtsp.h - third-party/moonlight-common-c/src/RtspParser.c - third-party/moonlight-common-c/src/Video.h - src/upnp.cpp - src/upnp.h - src/cbs.cpp - src/utility.h - src/uuid.h - src/config.h - src/config.cpp - src/main.cpp - src/main.h - src/crypto.cpp - src/crypto.h - src/nvhttp.cpp - src/nvhttp.h - src/httpcommon.cpp - src/httpcommon.h - src/confighttp.cpp - src/confighttp.h - src/rtsp.cpp - src/rtsp.h - src/stream.cpp - src/stream.h - src/video.cpp - src/video.h - src/input.cpp - src/input.h - src/audio.cpp - src/audio.h - src/platform/common.h - src/process.cpp - src/process.h - src/network.cpp - src/network.h - src/move_by_copy.h - src/task_pool.h - src/thread_pool.h - src/thread_safe.h - src/sync.h - src/round_robin.h - ${PLATFORM_TARGET_FILES}) + third-party/moonlight-common-c/reedsolomon/rs.c + third-party/moonlight-common-c/reedsolomon/rs.h + third-party/moonlight-common-c/src/Input.h + third-party/moonlight-common-c/src/Rtsp.h + third-party/moonlight-common-c/src/RtspParser.c + third-party/moonlight-common-c/src/Video.h + src/upnp.cpp + src/upnp.h + src/cbs.cpp + src/utility.h + src/uuid.h + src/config.h + src/config.cpp + src/main.cpp + src/main.h + src/crypto.cpp + src/crypto.h + src/nvhttp.cpp + src/nvhttp.h + src/httpcommon.cpp + src/httpcommon.h + src/confighttp.cpp + src/confighttp.h + src/rtsp.cpp + src/rtsp.h + src/stream.cpp + src/stream.h + src/video.cpp + src/video.h + src/input.cpp + src/input.h + src/audio.cpp + src/audio.h + src/platform/common.h + src/process.cpp + src/process.h + src/network.cpp + src/network.h + src/move_by_copy.h + src/task_pool.h + src/thread_pool.h + src/thread_safe.h + src/sync.h + src/round_robin.h + ${PLATFORM_TARGET_FILES}) set_source_files_properties(src/upnp.cpp PROPERTIES COMPILE_FLAGS -Wno-pedantic) # Pre-compiled binaries if(WIN32) - set(FFMPEG_PREPARED_BINARIES "${CMAKE_CURRENT_SOURCE_DIR}/third-party/ffmpeg-windows-x86_64") - set(FFMPEG_PLATFORM_LIBRARIES mfplat ole32 strmiids mfuuid) + set(FFMPEG_PREPARED_BINARIES "${CMAKE_CURRENT_SOURCE_DIR}/third-party/ffmpeg-windows-x86_64") + set(FFMPEG_PLATFORM_LIBRARIES mfplat ole32 strmiids mfuuid) elseif(APPLE) - if (CMAKE_SYSTEM_PROCESSOR STREQUAL "arm64") - set(FFMPEG_PREPARED_BINARIES "${CMAKE_CURRENT_SOURCE_DIR}/third-party/ffmpeg-macos-aarch64") - else() - set(FFMPEG_PREPARED_BINARIES "${CMAKE_CURRENT_SOURCE_DIR}/third-party/ffmpeg-macos-x86_64") - endif() + if (CMAKE_SYSTEM_PROCESSOR STREQUAL "arm64") + set(FFMPEG_PREPARED_BINARIES "${CMAKE_CURRENT_SOURCE_DIR}/third-party/ffmpeg-macos-aarch64") + else() + set(FFMPEG_PREPARED_BINARIES "${CMAKE_CURRENT_SOURCE_DIR}/third-party/ffmpeg-macos-x86_64") + endif() else() - if (CMAKE_SYSTEM_PROCESSOR STREQUAL "aarch64") - set(FFMPEG_PREPARED_BINARIES "${CMAKE_CURRENT_SOURCE_DIR}/third-party/ffmpeg-linux-aarch64") - else() - set(FFMPEG_PREPARED_BINARIES "${CMAKE_CURRENT_SOURCE_DIR}/third-party/ffmpeg-linux-x86_64") - endif() - set(FFMPEG_PLATFORM_LIBRARIES va va-drm va-x11 vdpau X11) + if (CMAKE_SYSTEM_PROCESSOR STREQUAL "aarch64") + set(FFMPEG_PREPARED_BINARIES "${CMAKE_CURRENT_SOURCE_DIR}/third-party/ffmpeg-linux-aarch64") + else() + set(FFMPEG_PREPARED_BINARIES "${CMAKE_CURRENT_SOURCE_DIR}/third-party/ffmpeg-linux-x86_64") + endif() + set(FFMPEG_PLATFORM_LIBRARIES va va-drm va-x11 vdpau X11) endif() set(FFMPEG_INCLUDE_DIRS - ${FFMPEG_PREPARED_BINARIES}/include) + ${FFMPEG_PREPARED_BINARIES}/include) if(EXISTS ${FFMPEG_PREPARED_BINARIES}/lib/libhdr10plus.a) - set(HDR10_PLUS_LIBRARY - ${FFMPEG_PREPARED_BINARIES}/lib/libhdr10plus.a) + set(HDR10_PLUS_LIBRARY + ${FFMPEG_PREPARED_BINARIES}/lib/libhdr10plus.a) endif() set(FFMPEG_LIBRARIES - ${FFMPEG_PREPARED_BINARIES}/lib/libavcodec.a - ${FFMPEG_PREPARED_BINARIES}/lib/libavutil.a - ${FFMPEG_PREPARED_BINARIES}/lib/libcbs.a - ${FFMPEG_PREPARED_BINARIES}/lib/libSvtAv1Enc.a - ${FFMPEG_PREPARED_BINARIES}/lib/libswscale.a - ${FFMPEG_PREPARED_BINARIES}/lib/libx264.a - ${FFMPEG_PREPARED_BINARIES}/lib/libx265.a - ${HDR10_PLUS_LIBRARY} - ${FFMPEG_PLATFORM_LIBRARIES}) + ${FFMPEG_PREPARED_BINARIES}/lib/libavcodec.a + ${FFMPEG_PREPARED_BINARIES}/lib/libavutil.a + ${FFMPEG_PREPARED_BINARIES}/lib/libcbs.a + ${FFMPEG_PREPARED_BINARIES}/lib/libSvtAv1Enc.a + ${FFMPEG_PREPARED_BINARIES}/lib/libswscale.a + ${FFMPEG_PREPARED_BINARIES}/lib/libx264.a + ${FFMPEG_PREPARED_BINARIES}/lib/libx265.a + ${HDR10_PLUS_LIBRARY} + ${FFMPEG_PLATFORM_LIBRARIES}) include_directories( - ${CMAKE_CURRENT_SOURCE_DIR} - ${CMAKE_CURRENT_SOURCE_DIR}/third-party - ${CMAKE_CURRENT_SOURCE_DIR}/third-party/moonlight-common-c/enet/include - ${CMAKE_CURRENT_SOURCE_DIR}/third-party/moonlight-common-c/reedsolomon - ${FFMPEG_INCLUDE_DIRS} - ${PLATFORM_INCLUDE_DIRS} + ${CMAKE_CURRENT_SOURCE_DIR} + ${CMAKE_CURRENT_SOURCE_DIR}/third-party + ${CMAKE_CURRENT_SOURCE_DIR}/third-party/moonlight-common-c/enet/include + ${CMAKE_CURRENT_SOURCE_DIR}/third-party/moonlight-common-c/reedsolomon + ${FFMPEG_INCLUDE_DIRS} + ${PLATFORM_INCLUDE_DIRS} ) string(TOUPPER "x${CMAKE_BUILD_TYPE}" BUILD_TYPE) if("${BUILD_TYPE}" STREQUAL "XDEBUG") - list(APPEND SUNSHINE_COMPILE_OPTIONS -O0 -ggdb3) - if(WIN32) - set_source_files_properties(src/nvhttp.cpp PROPERTIES COMPILE_FLAGS -O2) - endif() + list(APPEND SUNSHINE_COMPILE_OPTIONS -O0 -ggdb3) + if(WIN32) + set_source_files_properties(src/nvhttp.cpp PROPERTIES COMPILE_FLAGS -O2) + endif() else() - add_definitions(-DNDEBUG) - list(APPEND SUNSHINE_COMPILE_OPTIONS -O3) + add_definitions(-DNDEBUG) + list(APPEND SUNSHINE_COMPILE_OPTIONS -O3) endif() # setup assets directory if(NOT SUNSHINE_ASSETS_DIR) - set(SUNSHINE_ASSETS_DIR "assets") + set(SUNSHINE_ASSETS_DIR "assets") endif() if(UNIX) - set(SUNSHINE_ASSETS_DIR "${CMAKE_INSTALL_PREFIX}/${SUNSHINE_ASSETS_DIR}") + set(SUNSHINE_ASSETS_DIR "${CMAKE_INSTALL_PREFIX}/${SUNSHINE_ASSETS_DIR}") endif() # use relative assets path for AppImage... maybe for all unix if(${SUNSHINE_CONFIGURE_APPIMAGE}) - string(REPLACE "${CMAKE_INSTALL_PREFIX}" ".${CMAKE_INSTALL_PREFIX}" SUNSHINE_ASSETS_DIR_DEF ${SUNSHINE_ASSETS_DIR}) + string(REPLACE "${CMAKE_INSTALL_PREFIX}" ".${CMAKE_INSTALL_PREFIX}" SUNSHINE_ASSETS_DIR_DEF ${SUNSHINE_ASSETS_DIR}) else() - set(SUNSHINE_ASSETS_DIR_DEF "${SUNSHINE_ASSETS_DIR}") + set(SUNSHINE_ASSETS_DIR_DEF "${SUNSHINE_ASSETS_DIR}") endif() list(APPEND SUNSHINE_DEFINITIONS SUNSHINE_ASSETS_DIR="${SUNSHINE_ASSETS_DIR_DEF}") list(APPEND SUNSHINE_EXTERNAL_LIBRARIES - libminiupnpc-static - ${CMAKE_THREAD_LIBS_INIT} - enet - opus - ${FFMPEG_LIBRARIES} - ${Boost_LIBRARIES} - ${OPENSSL_LIBRARIES} - ${CURL_LIBRARIES} - ${PLATFORM_LIBRARIES}) + libminiupnpc-static + ${CMAKE_THREAD_LIBS_INIT} + enet + opus + ${FFMPEG_LIBRARIES} + ${Boost_LIBRARIES} + ${OPENSSL_LIBRARIES} + ${CURL_LIBRARIES} + ${PLATFORM_LIBRARIES}) if(NOT WIN32) - list(APPEND SUNSHINE_EXTERNAL_LIBRARIES Boost::log) + list(APPEND SUNSHINE_EXTERNAL_LIBRARIES Boost::log) endif() add_executable(sunshine ${SUNSHINE_TARGET_FILES}) if(WIN32) - set_target_properties(sunshine PROPERTIES LINK_SEARCH_START_STATIC 1) + set_target_properties(sunshine PROPERTIES LINK_SEARCH_START_STATIC 1) endif() target_link_libraries(sunshine ${SUNSHINE_EXTERNAL_LIBRARIES} ${EXTRA_LIBS}) target_compile_definitions(sunshine PUBLIC ${SUNSHINE_DEFINITIONS}) set_target_properties(sunshine PROPERTIES CXX_STANDARD 17 - VERSION ${PROJECT_VERSION} - SOVERSION ${PROJECT_VERSION_MAJOR} - ) + VERSION ${PROJECT_VERSION} + SOVERSION ${PROJECT_VERSION_MAJOR}) if(NOT DEFINED CMAKE_CUDA_STANDARD) set(CMAKE_CUDA_STANDARD 17) @@ -481,14 +488,14 @@ if(NOT DEFINED CMAKE_CUDA_STANDARD) endif() if(APPLE) - target_link_options(sunshine PRIVATE LINKER:-sectcreate,__TEXT,__info_plist,${APPLE_PLIST_FILE}) + target_link_options(sunshine PRIVATE LINKER:-sectcreate,__TEXT,__info_plist,${APPLE_PLIST_FILE}) endif() foreach(flag IN LISTS SUNSHINE_COMPILE_OPTIONS) - list(APPEND SUNSHINE_COMPILE_OPTIONS_CUDA "$<$:--compiler-options=${flag}>") + list(APPEND SUNSHINE_COMPILE_OPTIONS_CUDA "$<$:--compiler-options=${flag}>") endforeach() -target_compile_options(sunshine PRIVATE $<$:${SUNSHINE_COMPILE_OPTIONS}>;$<$:${SUNSHINE_COMPILE_OPTIONS_CUDA};-std=c++17>) +target_compile_options(sunshine PRIVATE $<$:${SUNSHINE_COMPILE_OPTIONS}>;$<$:${SUNSHINE_COMPILE_OPTIONS_CUDA};-std=c++17>) # cmake-lint: disable=C0301 # CPACK / Packaging @@ -506,172 +513,226 @@ set(CPACK_PACKAGE_FILE_NAME "${CMAKE_PROJECT_NAME}") set(CPACK_STRIP_FILES YES) # install npm modules -install(DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/node_modules" DESTINATION "${SUNSHINE_ASSETS_DIR}/web") +install(DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/node_modules" + DESTINATION "${SUNSHINE_ASSETS_DIR}/web") # Platform specific options if(WIN32) # see options at: https://cmake.org/cmake/help/latest/cpack_gen/nsis.html - install(TARGETS sunshine RUNTIME DESTINATION "." COMPONENT application) - - # Adding tools - install(TARGETS dxgi-info RUNTIME DESTINATION "tools" COMPONENT dxgi) - install(TARGETS audio-info RUNTIME DESTINATION "tools" COMPONENT audio) - install(TARGETS sunshinesvc RUNTIME DESTINATION "tools" COMPONENT sunshinesvc) - - # scripts - install(DIRECTORY "${SUNSHINE_SOURCE_ASSETS_DIR}/windows/misc/firewall/" DESTINATION "scripts" COMPONENT firewall) - install(DIRECTORY "${SUNSHINE_SOURCE_ASSETS_DIR}/windows/misc/service/" DESTINATION "scripts" COMPONENT service) - - # Sunshine assets - install(DIRECTORY "${SUNSHINE_SOURCE_ASSETS_DIR}/common/assets/" DESTINATION "${SUNSHINE_ASSETS_DIR}" COMPONENT assets) - install(DIRECTORY "${SUNSHINE_SOURCE_ASSETS_DIR}/windows/assets/" DESTINATION "${SUNSHINE_ASSETS_DIR}" COMPONENT assets) - - # set(CPACK_NSIS_MUI_HEADERIMAGE "") # TODO: image should be 150x57 bmp - set(CPACK_PACKAGE_ICON "${CMAKE_CURRENT_SOURCE_DIR}\\\\sunshine.ico") - set(CPACK_NSIS_INSTALLED_ICON_NAME "${PROJECT__DIR}\\\\${PROJECT_EXE}") - set(CPACK_PACKAGE_INSTALL_DIRECTORY "${CPACK_PACKAGE_NAME}") # The name of the directory that will be created in C:/Program files/ - - # Extra install commands - # Sets permissions on the installed folder so that we can write in it - # Install service - SET(CPACK_NSIS_EXTRA_INSTALL_COMMANDS - "${CPACK_NSIS_EXTRA_INSTALL_COMMANDS} + install(TARGETS sunshine RUNTIME DESTINATION "." COMPONENT application) + + # Adding tools + install(TARGETS dxgi-info RUNTIME DESTINATION "tools" COMPONENT dxgi) + install(TARGETS audio-info RUNTIME DESTINATION "tools" COMPONENT audio) + install(TARGETS sunshinesvc RUNTIME DESTINATION "tools" COMPONENT sunshinesvc) + + # scripts + install(DIRECTORY "${SUNSHINE_SOURCE_ASSETS_DIR}/windows/misc/firewall/" + DESTINATION "scripts" + COMPONENT firewall) + install(DIRECTORY "${SUNSHINE_SOURCE_ASSETS_DIR}/windows/misc/service/" + DESTINATION "scripts" + COMPONENT service) + + # Sunshine assets + install(DIRECTORY "${SUNSHINE_SOURCE_ASSETS_DIR}/common/assets/" + DESTINATION "${SUNSHINE_ASSETS_DIR}" + COMPONENT assets) + install(DIRECTORY "${SUNSHINE_SOURCE_ASSETS_DIR}/windows/assets/" + DESTINATION "${SUNSHINE_ASSETS_DIR}" + COMPONENT assets) + + # set(CPACK_NSIS_MUI_HEADERIMAGE "") # TODO: image should be 150x57 bmp + set(CPACK_PACKAGE_ICON "${CMAKE_CURRENT_SOURCE_DIR}\\\\sunshine.ico") + set(CPACK_NSIS_INSTALLED_ICON_NAME "${PROJECT__DIR}\\\\${PROJECT_EXE}") + # The name of the directory that will be created in C:/Program files/ + set(CPACK_PACKAGE_INSTALL_DIRECTORY "${CPACK_PACKAGE_NAME}") + + # Extra install commands + # Sets permissions on the installed folder so that we can write in it + # Install service + SET(CPACK_NSIS_EXTRA_INSTALL_COMMANDS + "${CPACK_NSIS_EXTRA_INSTALL_COMMANDS} ExecWait '\\\"$SYSDIR\\\\cmd.exe\\\" /c \\\"start https://sunshinestream.readthedocs.io/\\\"' ExecWait 'icacls \\\"$INSTDIR\\\" /grant:r Users:\\\(OI\\\)\\\(CI\\\)\\\(F\\\)' ExecWait '\\\"$INSTDIR\\\\scripts\\\\add-firewall-rule.bat\\\"' ExecWait '\\\"$INSTDIR\\\\scripts\\\\install-service.bat\\\"' - MessageBox MB_YESNO|MB_ICONQUESTION 'Do you want to add/update ViGEmBus (virtual controller support)?' IDNO NoController - ExecWait '\\\"$SYSDIR\\\\cmd.exe\\\" /c \\\"start https://github.com/ViGEm/ViGEmBus/releases/latest\\\"' ; skipped if no + MessageBox MB_YESNO|MB_ICONQUESTION 'Do you want to add/update ViGEmBus (virtual controller support)?' \ + IDNO NoController + ExecWait '\\\"$SYSDIR\\\\cmd.exe\\\" /c \\\"start https://github.com/ViGEm/ViGEmBus/releases/latest\\\"'; \ + skipped if no NoController: ") - # Extra uninstall commands - # Uninstall service - set(CPACK_NSIS_EXTRA_UNINSTALL_COMMANDS - "${CPACK_NSIS_EXTRA_UNINSTALL_COMMANDS} - ExecWait '\\\"$INSTDIR\\\\scripts\\\\delete-firewall-rule.bat\\\"' - ExecWait '\\\"$INSTDIR\\\\scripts\\\\uninstall-service.bat\\\"' - MessageBox MB_YESNO|MB_ICONQUESTION 'Do you want to remove $INSTDIR (this includes the configuration, cover images, and settings)?' /SD IDNO IDNO NoDelete - RMDir /r \\\"$INSTDIR\\\" ; skipped if no - NoDelete: - ") - - # Adding an option for the start menu and PATH - set(CPACK_NSIS_MODIFY_PATH "OFF") # TODO: it asks to add it to the PATH but is not working https://gitlab.kitware.com/cmake/cmake/-/issues/15635 - set(CPACK_NSIS_EXECUTABLES_DIRECTORY ".") - set(CPACK_NSIS_INSTALLED_ICON_NAME "${CMAKE_PROJECT_NAME}.exe") # This will be shown on the installed apps Windows settings - set(CPACK_NSIS_CREATE_ICONS_EXTRA - "${CPACK_NSIS_CREATE_ICONS_EXTRA} - CreateShortCut '\$SMPROGRAMS\\\\$STARTMENU_FOLDER\\\\${CMAKE_PROJECT_NAME}.lnk' '\$INSTDIR\\\\${CMAKE_PROJECT_NAME}.exe' - ") - set(CPACK_NSIS_DELETE_ICONS_EXTRA - "${CPACK_NSIS_DELETE_ICONS_EXTRA} - Delete '\$SMPROGRAMS\\\\$MUI_TEMP\\\\${CMAKE_PROJECT_NAME}.lnk' - ") - - # Checking for previous installed versions - set(CPACK_NSIS_ENABLE_UNINSTALL_BEFORE_INSTALL "ON") - - set(CPACK_NSIS_HELP_LINK "https://sunshinestream.readthedocs.io/about/installation.html") - set(CPACK_NSIS_URL_INFO_ABOUT "${CMAKE_PROJECT_HOMEPAGE_URL}") - set(CPACK_NSIS_CONTACT "${CMAKE_PROJECT_HOMEPAGE_URL}/support") - - set(CPACK_NSIS_MENU_LINKS - "https://sunshinestream.readthedocs.io" "Sunshine documentation" - "https://app.lizardbyte.dev" "LizardByte Web Site" - "https://app.lizardbyte.dev/support" "LizardByte Support" - ) - - # Setting components groups and dependencies - # sunshine binary - set(CPACK_COMPONENT_APPLICATION_DISPLAY_NAME "${CMAKE_PROJECT_NAME}") - set(CPACK_COMPONENT_APPLICATION_DESCRIPTION "${CMAKE_PROJECT_NAME} main application.") - set(CPACK_COMPONENT_APPLICATION_GROUP "core") - set(CPACK_COMPONENT_APPLICATION_REQUIRED true) - set(CPACK_COMPONENT_APPLICATION_DEPENDS assets) - - # assets - set(CPACK_COMPONENT_ASSETS_DISPLAY_NAME "assets") - set(CPACK_COMPONENT_ASSETS_DESCRIPTION "Shaders, default box art, and web ui.") - set(CPACK_COMPONENT_ASSETS_GROUP "core") - set(CPACK_COMPONENT_ASSETS_REQUIRED true) - - # audio tool - set(CPACK_COMPONENT_AUDIO_DISPLAY_NAME "audio-info") - set(CPACK_COMPONENT_AUDIO_DESCRIPTION "CLI tool providing information about sound devices.") - set(CPACK_COMPONENT_AUDIO_GROUP "tools") - - # display tool - set(CPACK_COMPONENT_DXGI_DISPLAY_NAME "dxgi-info") - set(CPACK_COMPONENT_DXGI_DESCRIPTION "CLI tool providing information about graphics cards and displays.") - set(CPACK_COMPONENT_DXGI_GROUP "tools") - - # service - set(CPACK_COMPONENT_SUNSHINESVC_DISPLAY_NAME "sunshinesvc") - set(CPACK_COMPONENT_SUNSHINESVC_DESCRIPTION "CLI tool providing ability to enable/disable the Sunshine service.") - set(CPACK_COMPONENT_SUNSHINESVC_GROUP "tools") - - # service scripts - set(CPACK_COMPONENT_SERVICE_DISPLAY_NAME "service-scripts") - set(CPACK_COMPONENT_SERVICE_DESCRIPTION "Scripts to enable/disable the service.") - set(CPACK_COMPONENT_SERVICE_GROUP "scripts") - set(CPACK_COMPONENT_SERVICE_DEPENDS sunshinesvc) - - # firewall scripts - set(CPACK_COMPONENT_FIREWALL_DISPLAY_NAME "firewall-scripts") - set(CPACK_COMPONENT_FIREWALL_DESCRIPTION "Scripts to enable or disable firewall rules.") - set(CPACK_COMPONENT_FIREWALL_GROUP "scripts") + # Extra uninstall commands + # Uninstall service + set(CPACK_NSIS_EXTRA_UNINSTALL_COMMANDS + "${CPACK_NSIS_EXTRA_UNINSTALL_COMMANDS} + ExecWait '\\\"$INSTDIR\\\\scripts\\\\delete-firewall-rule.bat\\\"' + ExecWait '\\\"$INSTDIR\\\\scripts\\\\uninstall-service.bat\\\"' + MessageBox MB_YESNO|MB_ICONQUESTION \ + 'Do you want to remove $INSTDIR (this includes the configuration, cover images, and settings)?' \ + /SD IDNO IDNO NoDelete + RMDir /r \\\"$INSTDIR\\\"; skipped if no + NoDelete: + ") + + # Adding an option for the start menu and PATH + # TODO: it asks to add it to the PATH but is not working https://gitlab.kitware.com/cmake/cmake/-/issues/15635 + set(CPACK_NSIS_MODIFY_PATH "OFF") + set(CPACK_NSIS_EXECUTABLES_DIRECTORY ".") + # This will be shown on the installed apps Windows settings + set(CPACK_NSIS_INSTALLED_ICON_NAME "${CMAKE_PROJECT_NAME}.exe") + set(CPACK_NSIS_CREATE_ICONS_EXTRA + "${CPACK_NSIS_CREATE_ICONS_EXTRA} + CreateShortCut '\$SMPROGRAMS\\\\$STARTMENU_FOLDER\\\\${CMAKE_PROJECT_NAME}.lnk' \ + '\$INSTDIR\\\\${CMAKE_PROJECT_NAME}.exe' + ") + set(CPACK_NSIS_DELETE_ICONS_EXTRA + "${CPACK_NSIS_DELETE_ICONS_EXTRA} + Delete '\$SMPROGRAMS\\\\$MUI_TEMP\\\\${CMAKE_PROJECT_NAME}.lnk' + ") + + # Checking for previous installed versions + set(CPACK_NSIS_ENABLE_UNINSTALL_BEFORE_INSTALL "ON") + + set(CPACK_NSIS_HELP_LINK "https://sunshinestream.readthedocs.io/about/installation.html") + set(CPACK_NSIS_URL_INFO_ABOUT "${CMAKE_PROJECT_HOMEPAGE_URL}") + set(CPACK_NSIS_CONTACT "${CMAKE_PROJECT_HOMEPAGE_URL}/support") + + set(CPACK_NSIS_MENU_LINKS + "https://sunshinestream.readthedocs.io" "Sunshine documentation" + "https://app.lizardbyte.dev" "LizardByte Web Site" + "https://app.lizardbyte.dev/support" "LizardByte Support") + + # Setting components groups and dependencies + # sunshine binary + set(CPACK_COMPONENT_APPLICATION_DISPLAY_NAME "${CMAKE_PROJECT_NAME}") + set(CPACK_COMPONENT_APPLICATION_DESCRIPTION "${CMAKE_PROJECT_NAME} main application.") + set(CPACK_COMPONENT_APPLICATION_GROUP "core") + set(CPACK_COMPONENT_APPLICATION_REQUIRED true) + set(CPACK_COMPONENT_APPLICATION_DEPENDS assets) + + # assets + set(CPACK_COMPONENT_ASSETS_DISPLAY_NAME "assets") + set(CPACK_COMPONENT_ASSETS_DESCRIPTION "Shaders, default box art, and web ui.") + set(CPACK_COMPONENT_ASSETS_GROUP "core") + set(CPACK_COMPONENT_ASSETS_REQUIRED true) + + # audio tool + set(CPACK_COMPONENT_AUDIO_DISPLAY_NAME "audio-info") + set(CPACK_COMPONENT_AUDIO_DESCRIPTION "CLI tool providing information about sound devices.") + set(CPACK_COMPONENT_AUDIO_GROUP "tools") + + # display tool + set(CPACK_COMPONENT_DXGI_DISPLAY_NAME "dxgi-info") + set(CPACK_COMPONENT_DXGI_DESCRIPTION "CLI tool providing information about graphics cards and displays.") + set(CPACK_COMPONENT_DXGI_GROUP "tools") + + # service + set(CPACK_COMPONENT_SUNSHINESVC_DISPLAY_NAME "sunshinesvc") + set(CPACK_COMPONENT_SUNSHINESVC_DESCRIPTION "CLI tool providing ability to enable/disable the Sunshine service.") + set(CPACK_COMPONENT_SUNSHINESVC_GROUP "tools") + + # service scripts + set(CPACK_COMPONENT_SERVICE_DISPLAY_NAME "service-scripts") + set(CPACK_COMPONENT_SERVICE_DESCRIPTION "Scripts to enable/disable the service.") + set(CPACK_COMPONENT_SERVICE_GROUP "scripts") + set(CPACK_COMPONENT_SERVICE_DEPENDS sunshinesvc) + + # firewall scripts + set(CPACK_COMPONENT_FIREWALL_DISPLAY_NAME "firewall-scripts") + set(CPACK_COMPONENT_FIREWALL_DESCRIPTION "Scripts to enable or disable firewall rules.") + set(CPACK_COMPONENT_FIREWALL_GROUP "scripts") endif() if(APPLE) - # TODO: bundle doesn't produce a valid .app use cpack -G DragNDrop - set(CPACK_BUNDLE_NAME "${CMAKE_PROJECT_NAME}") - set(CPACK_BUNDLE_PLIST "${APPLE_PLIST_FILE}") - set(CPACK_BUNDLE_ICON "${PROJECT_SOURCE_DIR}/sunshine.icns") - # set(CPACK_BUNDLE_STARTUP_COMMAND "${INSTALL_RUNTIME_DIR}/sunshine") + # TODO: bundle doesn't produce a valid .app use cpack -G DragNDrop + set(CPACK_BUNDLE_NAME "${CMAKE_PROJECT_NAME}") + set(CPACK_BUNDLE_PLIST "${APPLE_PLIST_FILE}") + set(CPACK_BUNDLE_ICON "${PROJECT_SOURCE_DIR}/sunshine.icns") + # set(CPACK_BUNDLE_STARTUP_COMMAND "${INSTALL_RUNTIME_DIR}/sunshine") endif() if(APPLE AND SUNSHINE_MACOS_PACKAGE) # TODO - set(prefix "${CMAKE_PROJECT_NAME}.app/Contents") - set(INSTALL_RUNTIME_DIR "${prefix}/MacOS") + set(MAC_PREFIX "${CMAKE_PROJECT_NAME}.app/Contents") + set(INSTALL_RUNTIME_DIR "${MAC_PREFIX}/MacOS") - install(DIRECTORY "${SUNSHINE_SOURCE_ASSETS_DIR}/common/assets/" DESTINATION "${SUNSHINE_ASSETS_DIR}") - install(DIRECTORY "${SUNSHINE_SOURCE_ASSETS_DIR}/macos/assets/" DESTINATION "${SUNSHINE_ASSETS_DIR}") + install(DIRECTORY "${SUNSHINE_SOURCE_ASSETS_DIR}/common/assets/" + DESTINATION "${SUNSHINE_ASSETS_DIR}") + install(DIRECTORY "${SUNSHINE_SOURCE_ASSETS_DIR}/macos/assets/" + DESTINATION "${SUNSHINE_ASSETS_DIR}") - install(TARGETS sunshine - BUNDLE DESTINATION . COMPONENT Runtime - RUNTIME DESTINATION ${INSTALL_RUNTIME_DIR} COMPONENT Runtime) + install(TARGETS sunshine + BUNDLE DESTINATION . COMPONENT Runtime + RUNTIME DESTINATION ${INSTALL_RUNTIME_DIR} COMPONENT Runtime) elseif(UNIX) - # Installation destination dir - set(CPACK_SET_DESTDIR true) - if(NOT CMAKE_INSTALL_PREFIX) - set(CMAKE_INSTALL_PREFIX "/usr/share/sunshine") - endif() - - install(TARGETS sunshine RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}") - - install(DIRECTORY "${SUNSHINE_SOURCE_ASSETS_DIR}/common/assets/" DESTINATION "${SUNSHINE_ASSETS_DIR}") - - if(APPLE) - install(DIRECTORY "${SUNSHINE_SOURCE_ASSETS_DIR}/macos/assets/" DESTINATION "${SUNSHINE_ASSETS_DIR}") - install(FILES "${SUNSHINE_SOURCE_ASSETS_DIR}/macos/misc/uninstall_pkg.sh" DESTINATION "${SUNSHINE_ASSETS_DIR}") - else() - install(DIRECTORY "${SUNSHINE_SOURCE_ASSETS_DIR}/linux/assets/" DESTINATION "${SUNSHINE_ASSETS_DIR}") - if(${SUNSHINE_CONFIGURE_APPIMAGE} OR ${SUNSHINE_CONFIGURE_FLATPAK}) - install(FILES "${SUNSHINE_SOURCE_ASSETS_DIR}/linux/misc/85-sunshine.rules" DESTINATION "${SUNSHINE_ASSETS_DIR}/udev/rules.d") - install(FILES "${CMAKE_CURRENT_BINARY_DIR}/sunshine.service" DESTINATION "${SUNSHINE_ASSETS_DIR}/systemd/user") - else() - install(FILES "${SUNSHINE_SOURCE_ASSETS_DIR}/linux/misc/85-sunshine.rules" DESTINATION "${CMAKE_INSTALL_PREFIX}/lib/udev/rules.d") - install(FILES "${CMAKE_CURRENT_BINARY_DIR}/sunshine.service" DESTINATION "${CMAKE_INSTALL_PREFIX}/lib/systemd/user") - endif() - - # Post install - set(CPACK_DEBIAN_PACKAGE_CONTROL_EXTRA "${SUNSHINE_SOURCE_ASSETS_DIR}/linux/misc/postinst") - set(CPACK_RPM_POST_INSTALL_SCRIPT_FILE "${SUNSHINE_SOURCE_ASSETS_DIR}/linux/misc/postinst") - - # Dependencies - set(CPACK_DEB_COMPONENT_INSTALL ON) - set(CPACK_DEBIAN_PACKAGE_DEPENDS "openssl, libboost-thread1.67.0 | libboost-thread1.71.0 | libboost-thread1.74.0, libboost-filesystem1.67.0 | libboost-filesystem1.71.0 | libboost-filesystem1.74.0, libboost-log1.67.0 | libboost-log1.71.0 | libboost-log1.74.0, libboost-program-options1.67.0 | libboost-program-options1.71.0 | libboost-program-options1.74.0, libcurl4, libpulse0, libopus0, libxcb-shm0, libxcb-xfixes0, libxtst6, libevdev2, libdrm2, libcap2") - set(CPACK_RPM_PACKAGE_REQUIRES "openssl >= 1.1, boost-thread >= 1.67.0, boost-filesystem >= 1.67.0, boost-log >= 1.67.0, boost-program-options >= 1.67.0, libcurl >= 7.0, pulseaudio-libs >= 10.0, libopusenc >= 0.2.1, libxcb >= 1.13, libXtst >= 1.2.3, libevdev >= 1.5.6, libdrm >= 2.4.97, libcap >= 2.22") - set(CPACK_DEBIAN_PACKAGE_SHLIBDEPS OFF) # This should automatically figure out dependencies, doesn't work with the current config - endif() + # Installation destination dir + set(CPACK_SET_DESTDIR true) + if(NOT CMAKE_INSTALL_PREFIX) + set(CMAKE_INSTALL_PREFIX "/usr/share/sunshine") + endif() + + install(TARGETS sunshine RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}") + + install(DIRECTORY "${SUNSHINE_SOURCE_ASSETS_DIR}/common/assets/" + DESTINATION "${SUNSHINE_ASSETS_DIR}") + + if(APPLE) + install(DIRECTORY "${SUNSHINE_SOURCE_ASSETS_DIR}/macos/assets/" + DESTINATION "${SUNSHINE_ASSETS_DIR}") + install(FILES "${SUNSHINE_SOURCE_ASSETS_DIR}/macos/misc/uninstall_pkg.sh" + DESTINATION "${SUNSHINE_ASSETS_DIR}") + else() + install(DIRECTORY "${SUNSHINE_SOURCE_ASSETS_DIR}/linux/assets/" + DESTINATION "${SUNSHINE_ASSETS_DIR}") + if(${SUNSHINE_CONFIGURE_APPIMAGE} OR ${SUNSHINE_CONFIGURE_FLATPAK}) + install(FILES "${SUNSHINE_SOURCE_ASSETS_DIR}/linux/misc/85-sunshine.rules" + DESTINATION "${SUNSHINE_ASSETS_DIR}/udev/rules.d") + install(FILES "${CMAKE_CURRENT_BINARY_DIR}/sunshine.service" + DESTINATION "${SUNSHINE_ASSETS_DIR}/systemd/user") + else() + install(FILES "${SUNSHINE_SOURCE_ASSETS_DIR}/linux/misc/85-sunshine.rules" + DESTINATION "${CMAKE_INSTALL_PREFIX}/lib/udev/rules.d") + install(FILES "${CMAKE_CURRENT_BINARY_DIR}/sunshine.service" + DESTINATION "${CMAKE_INSTALL_PREFIX}/lib/systemd/user") + endif() + + # Post install + set(CPACK_DEBIAN_PACKAGE_CONTROL_EXTRA "${SUNSHINE_SOURCE_ASSETS_DIR}/linux/misc/postinst") + set(CPACK_RPM_POST_INSTALL_SCRIPT_FILE "${SUNSHINE_SOURCE_ASSETS_DIR}/linux/misc/postinst") + + # Dependencies + set(CPACK_DEB_COMPONENT_INSTALL ON) + set(CPACK_DEBIAN_PACKAGE_DEPENDS "\ + libboost-filesystem1.67.0 | libboost-filesystem1.71.0 | libboost-filesystem1.74.0, \ + libboost-log1.67.0 | libboost-log1.71.0 | libboost-log1.74.0, \ + libboost-program-options1.67.0 | libboost-program-options1.71.0 | libboost-program-options1.74.0, \ + libboost-thread1.67.0 | libboost-thread1.71.0 | libboost-thread1.74.0, \ + libcap2, \ + libcurl4, \ + libdrm2, \ + libevdev2, \ + libopus0, \ + libpulse0, \ + libxcb-shm0, \ + libxcb-xfixes0, \ + libxtst6, \ + openssl") + set(CPACK_RPM_PACKAGE_REQUIRES "\ + boost-filesystem >= 1.67.0, \ + boost-log >= 1.67.0, \ + boost-program-options >= 1.67.0, \ + boost-thread >= 1.67.0, \ + libcap >= 2.22, \ + libcurl >= 7.0, \ + libdrm >= 2.4.97, \ + libevdev >= 1.5.6, \ + libopusenc >= 0.2.1, \ + libxcb >= 1.13, \ + libXtst >= 1.2.3, \ + openssl >= 1.1, \ + pulseaudio-libs >= 10.0") + # This should automatically figure out dependencies, doesn't work with the current config + set(CPACK_DEBIAN_PACKAGE_SHLIBDEPS OFF) + endif() endif() include(CPack) diff --git a/cmake/FindFFMPEG.cmake b/cmake/FindFFMPEG.cmake deleted file mode 100644 index 515d23ea327..00000000000 --- a/cmake/FindFFMPEG.cmake +++ /dev/null @@ -1,144 +0,0 @@ -# - Try to find FFMPEG -# Once done this will define -# FFMPEG_FOUND - System has FFMPEG -# FFMPEG_INCLUDE_DIRS - The FFMPEG include directories -# FFMPEG_LIBRARIES - The libraries needed to use FFMPEG -# FFMPEG_LIBRARY_DIRS - The directory to find FFMPEG libraries -# -# written by Roy Shilkrot 2013 http://www.morethantechnical.com/ -# - -find_package(PkgConfig) - - -MACRO(FFMPEG_FIND varname shortname headername) - - IF(NOT WIN32) - PKG_CHECK_MODULES(PC_${varname} ${shortname}) - - FIND_PATH(${varname}_INCLUDE_DIR "${shortname}/${headername}" - HINTS ${PC_${varname}_INCLUDEDIR} ${PC_${varname}_INCLUDE_DIRS} - NO_DEFAULT_PATH - ) - ELSE() - FIND_PATH(${varname}_INCLUDE_DIR "${shortname}/${headername}") - ENDIF() - - IF(${varname}_INCLUDE_DIR STREQUAL "${varname}_INCLUDE_DIR-NOTFOUND") - message(STATUS "look for newer strcture") - IF(NOT WIN32) - PKG_CHECK_MODULES(PC_${varname} "lib${shortname}") - - FIND_PATH(${varname}_INCLUDE_DIR "lib${shortname}/${headername}" - HINTS ${PC_${varname}_INCLUDEDIR} ${PC_${varname}_INCLUDE_DIRS} - NO_DEFAULT_PATH - ) - ELSE() - FIND_PATH(${varname}_INCLUDE_DIR "lib${shortname}/${headername}") - IF(${${varname}_INCLUDE_DIR} STREQUAL "${varname}_INCLUDE_DIR-NOTFOUND") - #Desperate times call for desperate measures - MESSAGE(STATUS "globbing...") - FILE(GLOB_RECURSE ${varname}_INCLUDE_DIR "/ffmpeg*/${headername}") - MESSAGE(STATUS "found: ${${varname}_INCLUDE_DIR}") - IF(${varname}_INCLUDE_DIR) - GET_FILENAME_COMPONENT(${varname}_INCLUDE_DIR "${${varname}_INCLUDE_DIR}" PATH) - GET_FILENAME_COMPONENT(${varname}_INCLUDE_DIR "${${varname}_INCLUDE_DIR}" PATH) - ELSE() - SET(${varname}_INCLUDE_DIR "${varname}_INCLUDE_DIR-NOTFOUND") - ENDIF() - ENDIF() - ENDIF() - ENDIF() - - - IF(${${varname}_INCLUDE_DIR} STREQUAL "${varname}_INCLUDE_DIR-NOTFOUND") - MESSAGE(STATUS "Can't find includes for ${shortname}...") - ELSE() - MESSAGE(STATUS "Found ${shortname} include dirs: ${${varname}_INCLUDE_DIR}") - - #GET_DIRECTORY_PROPERTY(FFMPEG_PARENT DIRECTORY ${${varname}_INCLUDE_DIR} PARENT_DIRECTORY) - GET_FILENAME_COMPONENT(FFMPEG_PARENT ${${varname}_INCLUDE_DIR} PATH) - MESSAGE(STATUS "Using FFMpeg dir parent as hint: ${FFMPEG_PARENT}") - - IF(NOT WIN32) - FIND_LIBRARY(${varname}_LIBRARIES NAMES ${shortname} - HINTS ${PC_${varname}_LIBDIR} ${PC_${varname}_LIBRARY_DIR} ${FFMPEG_PARENT}) - ELSE() - FIND_PATH(${varname}_LIBRARIES "${shortname}.dll.a" HINTS ${FFMPEG_PARENT}) - # FILE(GLOB_RECURSE ${varname}_LIBRARIES "${FFMPEG_PARENT}/*${shortname}.lib") - # GLOBing is very bad... but windows sux, this is the only thing that works - ENDIF() - - IF(${varname}_LIBRARIES STREQUAL "${varname}_LIBRARIES-NOTFOUND") - MESSAGE(STATUS "look for newer structure for library") - FIND_LIBRARY(${varname}_LIBRARIES NAMES lib${shortname} - HINTS ${PC_${varname}_LIBDIR} ${PC_${varname}_LIBRARY_DIR} ${FFMPEG_PARENT}) - ENDIF() - - - IF(${varname}_LIBRARIES STREQUAL "${varname}_LIBRARIES-NOTFOUND") - MESSAGE(STATUS "Can't find lib for ${shortname}...") - ELSE() - MESSAGE(STATUS "Found ${shortname} libs: ${${varname}_LIBRARIES}") - ENDIF() - - - IF(NOT ${varname}_INCLUDE_DIR STREQUAL "${varname}_INCLUDE_DIR-NOTFOUND" - AND NOT ${varname}_LIBRARIES STREQUAL ${varname}_LIBRARIES-NOTFOUND) - - MESSAGE(STATUS "found ${shortname}: include ${${varname}_INCLUDE_DIR} lib ${${varname}_LIBRARIES}") - SET(FFMPEG_${varname}_FOUND 1) - SET(FFMPEG_${varname}_INCLUDE_DIRS ${${varname}_INCLUDE_DIR}) - SET(FFMPEG_${varname}_LIBS ${${varname}_LIBRARIES}) - ELSE() - MESSAGE(STATUS "Can't find ${shortname}") - ENDIF() - - ENDIF() - -ENDMACRO(FFMPEG_FIND) - -FFMPEG_FIND(LIBAVFORMAT avformat avformat.h) -FFMPEG_FIND(LIBAVDEVICE avdevice avdevice.h) -FFMPEG_FIND(LIBAVCODEC avcodec avcodec.h) -FFMPEG_FIND(LIBAVUTIL avutil avutil.h) -FFMPEG_FIND(LIBSWSCALE swscale swscale.h) - -SET(FFMPEG_FOUND "NO") -IF (FFMPEG_LIBAVFORMAT_FOUND AND - FFMPEG_LIBAVDEVICE_FOUND AND - FFMPEG_LIBAVCODEC_FOUND AND - FFMPEG_LIBAVUTIL_FOUND AND - FFMPEG_LIBSWSCALE_FOUND - ) - - - SET(FFMPEG_FOUND "YES") - - SET(FFMPEG_INCLUDE_DIRS ${FFMPEG_LIBAVFORMAT_INCLUDE_DIRS}) - - SET(FFMPEG_LIBRARY_DIRS ${FFMPEG_LIBAVFORMAT_LIBRARY_DIRS}) - - SET(FFMPEG_LIBRARIES - ${FFMPEG_LIBAVFORMAT_LIBS} - ${FFMPEG_LIBAVDEVICE_LIBS} - ${FFMPEG_LIBAVCODEC_LIBS} - ${FFMPEG_LIBAVUTIL_LIBS} - ${FFMPEG_LIBSWSCALE_LIBS} - ) - -ELSE () - - MESSAGE(STATUS "Could not find FFMPEG") - -ENDIF() - -message(STATUS ${FFMPEG_LIBRARIES} ${FFMPEG_LIBAVFORMAT_LIBRARIES}) - -include(FindPackageHandleStandardArgs) -# handle the QUIETLY and REQUIRED arguments and set FFMPEG_FOUND to TRUE -# if all listed variables are TRUE -find_package_handle_standard_args(FFMPEG DEFAULT_MSG - FFMPEG_LIBRARIES FFMPEG_INCLUDE_DIRS) - -mark_as_advanced(FFMPEG_INCLUDE_DIRS FFMPEG_LIBRARY_DIRS FFMPEG_LIBRARIES) diff --git a/cmake/FindLIBDRM.cmake b/cmake/FindLIBDRM.cmake index fd0548c1a1e..6e050018460 100644 --- a/cmake/FindLIBDRM.cmake +++ b/cmake/FindLIBDRM.cmake @@ -18,4 +18,4 @@ find_library(LIBDRM_LIBRARIES NAMES libdrm.so PATHS ${PC_LIBDRM_LIBDIR} ${PC_LIB mark_as_advanced(LIBDRM_INCLUDE_DIRS LIBDRM_LIBRARIES) include(FindPackageHandleStandardArgs) -find_package_handle_standard_args(LIBDRM REQUIRED_VARS LIBDRM_LIBRARIES LIBDRM_INCLUDE_DIRS) \ No newline at end of file +find_package_handle_standard_args(LIBDRM REQUIRED_VARS LIBDRM_LIBRARIES LIBDRM_INCLUDE_DIRS) diff --git a/cmake/FindWayland.cmake b/cmake/FindWayland.cmake index 377f0545cda..cf66d83abaf 100644 --- a/cmake/FindWayland.cmake +++ b/cmake/FindWayland.cmake @@ -22,57 +22,59 @@ IF (NOT WIN32) - # Use pkg-config to get the directories and then use these values - # in the find_path() and find_library() calls - find_package(PkgConfig) - PKG_CHECK_MODULES(PKG_WAYLAND QUIET wayland-client wayland-server wayland-egl wayland-cursor) + # Use pkg-config to get the directories and then use these values + # in the find_path() and find_library() calls + find_package(PkgConfig) + PKG_CHECK_MODULES(PKG_WAYLAND QUIET wayland-client wayland-server wayland-egl wayland-cursor) - set(WAYLAND_DEFINITIONS ${PKG_WAYLAND_CFLAGS}) + set(WAYLAND_DEFINITIONS ${PKG_WAYLAND_CFLAGS}) - find_path(WAYLAND_CLIENT_INCLUDE_DIRS NAMES wayland-client.h HINTS ${PKG_WAYLAND_INCLUDE_DIRS}) - find_library(WAYLAND_CLIENT_LIBRARIES NAMES wayland-client HINTS ${PKG_WAYLAND_LIBRARY_DIRS}) - if(WAYLAND_CLIENT_INCLUDE_DIRS AND WAYLAND_CLIENT_LIBRARIES) - set(Wayland_Client_FOUND TRUE) - else() - set(Wayland_Client_FOUND FALSE) - endif() - mark_as_advanced(WAYLAND_CLIENT_INCLUDE_DIRS WAYLAND_CLIENT_LIBRARIES) + find_path(WAYLAND_CLIENT_INCLUDE_DIRS NAMES wayland-client.h HINTS ${PKG_WAYLAND_INCLUDE_DIRS}) + find_library(WAYLAND_CLIENT_LIBRARIES NAMES wayland-client HINTS ${PKG_WAYLAND_LIBRARY_DIRS}) + if(WAYLAND_CLIENT_INCLUDE_DIRS AND WAYLAND_CLIENT_LIBRARIES) + set(Wayland_Client_FOUND TRUE) # cmake-lint: disable=C0103 + else() + set(Wayland_Client_FOUND FALSE) # cmake-lint: disable=C0103 + endif() + mark_as_advanced(WAYLAND_CLIENT_INCLUDE_DIRS WAYLAND_CLIENT_LIBRARIES) - find_path(WAYLAND_CURSOR_INCLUDE_DIRS NAMES wayland-cursor.h HINTS ${PKG_WAYLAND_INCLUDE_DIRS}) - find_library(WAYLAND_CURSOR_LIBRARIES NAMES wayland-cursor HINTS ${PKG_WAYLAND_LIBRARY_DIRS}) - if(WAYLAND_CURSOR_INCLUDE_DIRS AND WAYLAND_CURSOR_LIBRARIES) - set(Wayland_Cursor_FOUND TRUE) - else() - set(Wayland_Cursor_FOUND FALSE) - endif() - mark_as_advanced(WAYLAND_CURSOR_INCLUDE_DIRS WAYLAND_CURSOR_LIBRARIES) + find_path(WAYLAND_CURSOR_INCLUDE_DIRS NAMES wayland-cursor.h HINTS ${PKG_WAYLAND_INCLUDE_DIRS}) + find_library(WAYLAND_CURSOR_LIBRARIES NAMES wayland-cursor HINTS ${PKG_WAYLAND_LIBRARY_DIRS}) + if(WAYLAND_CURSOR_INCLUDE_DIRS AND WAYLAND_CURSOR_LIBRARIES) + set(Wayland_Cursor_FOUND TRUE) # cmake-lint: disable=C0103 + else() + set(Wayland_Cursor_FOUND FALSE) # cmake-lint: disable=C0103 + endif() + mark_as_advanced(WAYLAND_CURSOR_INCLUDE_DIRS WAYLAND_CURSOR_LIBRARIES) - find_path(WAYLAND_EGL_INCLUDE_DIRS NAMES wayland-egl.h HINTS ${PKG_WAYLAND_INCLUDE_DIRS}) - find_library(WAYLAND_EGL_LIBRARIES NAMES wayland-egl HINTS ${PKG_WAYLAND_LIBRARY_DIRS}) - if(WAYLAND_EGL_INCLUDE_DIRS AND WAYLAND_EGL_LIBRARIES) - set(Wayland_EGL_FOUND TRUE) - else() - set(Wayland_EGL_FOUND FALSE) - endif() - mark_as_advanced(WAYLAND_EGL_INCLUDE_DIRS WAYLAND_EGL_LIBRARIES) + find_path(WAYLAND_EGL_INCLUDE_DIRS NAMES wayland-egl.h HINTS ${PKG_WAYLAND_INCLUDE_DIRS}) + find_library(WAYLAND_EGL_LIBRARIES NAMES wayland-egl HINTS ${PKG_WAYLAND_LIBRARY_DIRS}) + if(WAYLAND_EGL_INCLUDE_DIRS AND WAYLAND_EGL_LIBRARIES) + set(Wayland_EGL_FOUND TRUE) # cmake-lint: disable=C0103 + else() + set(Wayland_EGL_FOUND FALSE) # cmake-lint: disable=C0103 + endif() + mark_as_advanced(WAYLAND_EGL_INCLUDE_DIRS WAYLAND_EGL_LIBRARIES) - find_path(WAYLAND_SERVER_INCLUDE_DIRS NAMES wayland-server.h HINTS ${PKG_WAYLAND_INCLUDE_DIRS}) - find_library(WAYLAND_SERVER_LIBRARIES NAMES wayland-server HINTS ${PKG_WAYLAND_LIBRARY_DIRS}) - if(WAYLAND_SERVER_INCLUDE_DIRS AND WAYLAND_SERVER_LIBRARIES) - set(Wayland_Server_FOUND TRUE) - else() - set(Wayland_Server_FOUND FALSE) - endif() - mark_as_advanced(WAYLAND_SERVER_INCLUDE_DIRS WAYLAND_SERVER_LIBRARIES) + find_path(WAYLAND_SERVER_INCLUDE_DIRS NAMES wayland-server.h HINTS ${PKG_WAYLAND_INCLUDE_DIRS}) + find_library(WAYLAND_SERVER_LIBRARIES NAMES wayland-server HINTS ${PKG_WAYLAND_LIBRARY_DIRS}) + if(WAYLAND_SERVER_INCLUDE_DIRS AND WAYLAND_SERVER_LIBRARIES) + set(Wayland_Server_FOUND TRUE) # cmake-lint: disable=C0103 + else() + set(Wayland_Server_FOUND FALSE) # cmake-lint: disable=C0103 + endif() + mark_as_advanced(WAYLAND_SERVER_INCLUDE_DIRS WAYLAND_SERVER_LIBRARIES) - set(WAYLAND_INCLUDE_DIRS ${WAYLAND_CLIENT_INCLUDE_DIRS} ${WAYLAND_SERVER_INCLUDE_DIRS} ${WAYLAND_EGL_INCLUDE_DIRS} ${WAYLAND_CURSOR_INCLUDE_DIRS}) - set(WAYLAND_LIBRARIES ${WAYLAND_CLIENT_LIBRARIES} ${WAYLAND_SERVER_LIBRARIES} ${WAYLAND_EGL_LIBRARIES} ${WAYLAND_CURSOR_LIBRARIES}) - mark_as_advanced(WAYLAND_INCLUDE_DIRS WAYLAND_LIBRARIES) + set(WAYLAND_INCLUDE_DIRS ${WAYLAND_CLIENT_INCLUDE_DIRS} ${WAYLAND_SERVER_INCLUDE_DIRS} + ${WAYLAND_EGL_INCLUDE_DIRS} ${WAYLAND_CURSOR_INCLUDE_DIRS}) + set(WAYLAND_LIBRARIES ${WAYLAND_CLIENT_LIBRARIES} ${WAYLAND_SERVER_LIBRARIES} + ${WAYLAND_EGL_LIBRARIES} ${WAYLAND_CURSOR_LIBRARIES}) + mark_as_advanced(WAYLAND_INCLUDE_DIRS WAYLAND_LIBRARIES) - list(REMOVE_DUPLICATES WAYLAND_INCLUDE_DIRS) + list(REMOVE_DUPLICATES WAYLAND_INCLUDE_DIRS) - include(FindPackageHandleStandardArgs) + include(FindPackageHandleStandardArgs) - find_package_handle_standard_args(Wayland REQUIRED_VARS WAYLAND_LIBRARIES WAYLAND_INCLUDE_DIRS HANDLE_COMPONENTS) + find_package_handle_standard_args(Wayland REQUIRED_VARS WAYLAND_LIBRARIES WAYLAND_INCLUDE_DIRS HANDLE_COMPONENTS) ENDIF () From 8ad7af86c0ad130395019c540884ca0da12a7ed2 Mon Sep 17 00:00:00 2001 From: Cameron Gutman Date: Thu, 29 Dec 2022 08:32:23 -0600 Subject: [PATCH 39/88] Graceful termination on shutdown, logoff, and service stop (#647) --- src/main.cpp | 70 ++++++++++++++++++++++++ tools/sunshinesvc.cpp | 124 +++++++++++++++++++++++++++++++++--------- 2 files changed, 168 insertions(+), 26 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index 58271f83d02..2411640e737 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -136,16 +136,86 @@ std::map
+ +
+ + +
+ The file where the current logs of Sunshine are stored. +
+
-
@@ -39,16 +35,28 @@

Unpair All Clients

Error while unpairing
-
+ +
+
+

Logs

+
+
+

See the logs uploaded by Sunshine

+ +
+
+
+ {{actualLogs}} +
+
+
+
+ + From 17cd230c55c9298261ec836c99abf398aba8ca96 Mon Sep 17 00:00:00 2001 From: LizardByte-bot <108553330+LizardByte-bot@users.noreply.github.com> Date: Sat, 31 Dec 2022 22:38:46 -0500 Subject: [PATCH 51/88] ci: update global workflows (#661) --- .github/workflows/auto-create-pr.yml | 3 +++ .github/workflows/automerge.yml | 9 ++++++--- .github/workflows/autoupdate-labeler.yml | 3 ++- .github/workflows/autoupdate.yml | 10 ++-------- .github/workflows/issues-stale.yml | 2 ++ .github/workflows/issues.yml | 2 ++ .github/workflows/pull-requests.yml | 6 ++++++ .github/workflows/yaml-lint.yml | 6 ++++++ 8 files changed, 29 insertions(+), 12 deletions(-) diff --git a/.github/workflows/auto-create-pr.yml b/.github/workflows/auto-create-pr.yml index 67045e0ea9a..811747c6517 100644 --- a/.github/workflows/auto-create-pr.yml +++ b/.github/workflows/auto-create-pr.yml @@ -3,6 +3,9 @@ # Don't make changes to this file in this repo as they will be overwritten with changes made to the same file in # the above-mentioned repo. +# This workflow creates a PR automatically when anything is merged/pushed into the `nightly` branch. The PR is created +# against the `master` (default) branch. + name: Auto create PR on: diff --git a/.github/workflows/automerge.yml b/.github/workflows/automerge.yml index 0ba556810af..4774eb21317 100644 --- a/.github/workflows/automerge.yml +++ b/.github/workflows/automerge.yml @@ -3,6 +3,8 @@ # Don't make changes to this file in this repo as they will be overwritten with changes made to the same file in # the above-mentioned repo. +# This workflow will, first, automatically approve PRs created by @LizardByte-bot. Then it will automerge relevant PRs. + name: Automerge PR on: @@ -11,6 +13,10 @@ on: - opened - synchronize +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + jobs: autoapprove: if: >- @@ -40,9 +46,6 @@ jobs: if: startsWith(github.repository, 'LizardByte/') needs: [autoapprove] runs-on: ubuntu-latest - concurrency: - group: automerge-${{ github.ref }} - cancel-in-progress: true steps: - name: Automerging diff --git a/.github/workflows/autoupdate-labeler.yml b/.github/workflows/autoupdate-labeler.yml index 5e426ad7b33..974c9fa7fd1 100644 --- a/.github/workflows/autoupdate-labeler.yml +++ b/.github/workflows/autoupdate-labeler.yml @@ -3,6 +3,8 @@ # Don't make changes to this file in this repo as they will be overwritten with changes made to the same file in # the above-mentioned repo. +# Label PRs with `autoupdate` if various conditions are met, otherwise, remove the label. + name: Label PR autoupdate on: @@ -38,7 +40,6 @@ jobs: contains(github.event.pull_request.labels.*.name, 'autoupdate') == false && contains(github.event.pull_request.body, fromJSON('"\n- [x] I want maintainers to keep my branch updated"')) == true - uses: actions/github-script@v6 with: github-token: ${{ secrets.GH_BOT_TOKEN }} diff --git a/.github/workflows/autoupdate.yml b/.github/workflows/autoupdate.yml index b1ebbb6b966..65d80dc3fb3 100644 --- a/.github/workflows/autoupdate.yml +++ b/.github/workflows/autoupdate.yml @@ -7,8 +7,9 @@ # - automerge # - autoupdate-labeler -# It uses GitHub Action that auto-updates pull requests branches, when changes are pushed to their destination branch. +# It uses an action that auto-updates pull requests branches, when changes are pushed to their destination branch. # Auto-updating to the latest destination branch works only in the context of upstream repo and not forks. +# Dependabot PRs are updated by an action that comments `@depdenabot rebase` on dependabot PRs. name: autoupdate @@ -39,14 +40,7 @@ jobs: startsWith(github.repository, 'LizardByte/') runs-on: ubuntu-latest steps: - - name: check labels - id: label - run: | - echo "central_dep=${{ contains(github.event.pull_request.labels.*.name, 'central_dependency') }}" \ - >> $GITHUB_OUTPUT - - name: rebase - if: ${{ steps.label.outputs.central_dep == 'false' }} uses: "bbeesley/gha-auto-dependabot-rebase@v1.2.0" env: GITHUB_TOKEN: ${{ secrets.GH_BOT_TOKEN }} diff --git a/.github/workflows/issues-stale.yml b/.github/workflows/issues-stale.yml index 460eb848f1e..cc0e3ae86ef 100644 --- a/.github/workflows/issues-stale.yml +++ b/.github/workflows/issues-stale.yml @@ -3,6 +3,8 @@ # Don't make changes to this file in this repo as they will be overwritten with changes made to the same file in # the above-mentioned repo. +# Manage stale issues and PRs. + name: Stale Issues / PRs on: diff --git a/.github/workflows/issues.yml b/.github/workflows/issues.yml index db25703d1dd..d7a1025cdce 100644 --- a/.github/workflows/issues.yml +++ b/.github/workflows/issues.yml @@ -3,6 +3,8 @@ # Don't make changes to this file in this repo as they will be overwritten with changes made to the same file in # the above-mentioned repo. +# Label and un-label actions using `../label-actions.yml`. + name: Issues on: diff --git a/.github/workflows/pull-requests.yml b/.github/workflows/pull-requests.yml index 1fa3c071c6c..a1d9279b1d3 100644 --- a/.github/workflows/pull-requests.yml +++ b/.github/workflows/pull-requests.yml @@ -3,12 +3,18 @@ # Don't make changes to this file in this repo as they will be overwritten with changes made to the same file in # the above-mentioned repo. +# Ensure PRs are made against `nightly` branch. + name: Pull Requests on: pull_request_target: types: [opened, synchronize, edited, reopened] +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + jobs: check-pull-request: name: Check Pull Request diff --git a/.github/workflows/yaml-lint.yml b/.github/workflows/yaml-lint.yml index a8c97e3a09c..6327d5d6326 100644 --- a/.github/workflows/yaml-lint.yml +++ b/.github/workflows/yaml-lint.yml @@ -3,6 +3,8 @@ # Don't make changes to this file in this repo as they will be overwritten with changes made to the same file in # the above-mentioned repo. +# Lint yaml files. + name: yaml lint on: @@ -10,6 +12,10 @@ on: branches: [master, nightly] types: [opened, synchronize, reopened] +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + jobs: yaml-lint: runs-on: ubuntu-latest From e5dedbbe46f97e52422b25005af9dbbb7396bc73 Mon Sep 17 00:00:00 2001 From: LizardByte-bot <108553330+LizardByte-bot@users.noreply.github.com> Date: Sun, 1 Jan 2023 00:09:29 -0500 Subject: [PATCH 52/88] ci: update global python (#662) --- .github/workflows/python-flake8.yml | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/.github/workflows/python-flake8.yml b/.github/workflows/python-flake8.yml index 463fb8a2033..19bcdb9d8a2 100644 --- a/.github/workflows/python-flake8.yml +++ b/.github/workflows/python-flake8.yml @@ -3,6 +3,8 @@ # Don't make changes to this file in this repo as they will be overwritten with changes made to the same file in # the above-mentioned repo. +# Lint python files with flake8. + name: flake8 on: @@ -10,6 +12,10 @@ on: branches: [master, nightly] types: [opened, synchronize, reopened] +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + jobs: flake8: runs-on: ubuntu-latest @@ -24,7 +30,8 @@ jobs: - name: Install dependencies run: | - python -m pip install --upgrade pip setuptools flake8 + # pin flake8 before v6.0.0 due to removal of support for type comments (required for Python 2.7 type hints) + python -m pip install --upgrade pip setuptools "flake8<6" - name: Test with flake8 run: | From 9ae46e1325092b4332d005b953f17adaa0cd74ab Mon Sep 17 00:00:00 2001 From: LizardByte-bot <108553330+LizardByte-bot@users.noreply.github.com> Date: Sun, 1 Jan 2023 00:35:39 -0500 Subject: [PATCH 53/88] ci: update global docker (#663) --- .github/workflows/ci-docker.yml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/.github/workflows/ci-docker.yml b/.github/workflows/ci-docker.yml index f1e94794151..8ff84aaac90 100644 --- a/.github/workflows/ci-docker.yml +++ b/.github/workflows/ci-docker.yml @@ -3,6 +3,10 @@ # Don't make changes to this file in this repo as they will be overwritten with changes made to the same file in # the above-mentioned repo. +# This workflow is intended to work with all our organization Docker projects. Docker platforms/architectures should be +# listed in a file named `.docker_platforms`, comma separated list with no spaces. A readme named `DOCKER_README.md` +# will be used to update the description on Docker hub. + name: CI Docker on: @@ -13,6 +17,10 @@ on: branches: [master, nightly] workflow_dispatch: +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + jobs: check_dockerfile: name: Check Dockerfile From 97b1790d0cfd247ed47ef5284f44baa2f177c3d6 Mon Sep 17 00:00:00 2001 From: LizardByte-bot <108553330+LizardByte-bot@users.noreply.github.com> Date: Sun, 1 Jan 2023 09:36:24 -0500 Subject: [PATCH 54/88] ci: update release notifier (#665) --- .github/workflows/release-notifier.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/release-notifier.yml b/.github/workflows/release-notifier.yml index bea36033c35..ed7b3ef20ff 100644 --- a/.github/workflows/release-notifier.yml +++ b/.github/workflows/release-notifier.yml @@ -3,6 +3,8 @@ # Don't make changes to this file in this repo as they will be overwritten with changes made to the same file in # the above-mentioned repo. +# Send release notification to various platforms. + name: Release Notifications on: From cfe712910099dd1b40687b1b8d3f8db92f5e3495 Mon Sep 17 00:00:00 2001 From: ReenigneArcher <42013603+ReenigneArcher@users.noreply.github.com> Date: Sun, 1 Jan 2023 23:59:53 -0500 Subject: [PATCH 55/88] windows: pin libpsl version (#669) --- .github/workflows/CI.yml | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index e6b229d9f5b..e4a8ef1df46 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -885,14 +885,26 @@ jobs: mingw-w64-x86_64-binutils mingw-w64-x86_64-boost mingw-w64-x86_64-cmake - mingw-w64-x86_64-curl mingw-w64-x86_64-nsis mingw-w64-x86_64-openssl mingw-w64-x86_64-opus mingw-w64-x86_64-toolchain nasm + wget yasm + - name: pin libpsl + # libpsl is pinned until https://github.com/msys2/MINGW-packages/issues/14882 is resolved + # wget above is only necessary for this step + shell: msys2 {0} + run: | + # manually download and install libpsl + wget https://repo.msys2.org/mingw/mingw64/mingw-w64-x86_64-libpsl-0.21.1-4-any.pkg.tar.zst + pacman --noconfirm -U mingw-w64-x86_64-libpsl-0.21.1-4-any.pkg.tar.zst + + # install curl + pacman --noconfirm -S mingw-w64-x86_64-curl + - name: Install npm packages run: | npm install From a7a9df3b9dec8bb92af343fb199e27b02181509c Mon Sep 17 00:00:00 2001 From: LizardByte-bot <108553330+LizardByte-bot@users.noreply.github.com> Date: Mon, 2 Jan 2023 10:12:06 -0500 Subject: [PATCH 56/88] ci: update global workflows (#673) --- .github/workflows/pull-requests.yml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/.github/workflows/pull-requests.yml b/.github/workflows/pull-requests.yml index a1d9279b1d3..58243872498 100644 --- a/.github/workflows/pull-requests.yml +++ b/.github/workflows/pull-requests.yml @@ -11,9 +11,7 @@ on: pull_request_target: types: [opened, synchronize, edited, reopened] -concurrency: - group: ${{ github.workflow }}-${{ github.ref }} - cancel-in-progress: true +# no concurrency for pull_request_target events jobs: check-pull-request: From f9963ed39bf5f1c7255ab66f8f81d4b38a2a20eb Mon Sep 17 00:00:00 2001 From: ReenigneArcher <42013603+ReenigneArcher@users.noreply.github.com> Date: Mon, 2 Jan 2023 11:17:02 -0500 Subject: [PATCH 57/88] update clang-format (#666) --- .github/workflows/cpp-lint.yml | 45 +- src/crypto.h | 12 +- src/main.h | 6 +- src/platform/common.h | 6 +- src/platform/linux/graphics.h | 4 +- src/platform/linux/wayland.h | 8 +- src/platform/macos/display.mm | 2 +- src/platform/macos/microphone.mm | 2 +- src/stream.cpp | 2 +- src/utility.h | 24 +- src/video.cpp | 4 +- third-party/nvfbc/NvFBC.h | 667 ++++++----- third-party/nvfbc/helper_math.h | 1819 +++++++++++++----------------- 13 files changed, 1147 insertions(+), 1454 deletions(-) diff --git a/.github/workflows/cpp-lint.yml b/.github/workflows/cpp-lint.yml index 77ee726d605..fb1bf642dea 100644 --- a/.github/workflows/cpp-lint.yml +++ b/.github/workflows/cpp-lint.yml @@ -3,6 +3,8 @@ # Don't make changes to this file in this repo as they will be overwritten with changes made to the same file in # the above-mentioned repo. +# Lint c++ source files and cmake files. + name: C++ Lint on: @@ -10,45 +12,36 @@ on: branches: [master, nightly] types: [opened, synchronize, reopened] +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + jobs: - check_src: - name: Check src + clang-format: + name: Clang Format Lint runs-on: ubuntu-latest + steps: - name: Checkout uses: actions/checkout@v3 - - name: Check - id: check + - name: Find cpp files + id: cpp_files run: | - if [ -d "./src" ] - then - FOUND=true - else - FOUND=false - fi - - echo "src=${FOUND}" >> $GITHUB_OUTPUT - - outputs: - src: ${{ steps.check.outputs.src }} + cpp_files=$(find . -type f -iname "*.cpp" -o -iname "*.h" -o -iname "*.m" -o -iname "*.mm") - clang-format: - name: Clang Format Lint - needs: [check_src] - if: ${{ needs.check_src.outputs.src == 'true' }} - runs-on: ubuntu-latest + echo "found cpp files: ${cpp_files}" - steps: - - name: Checkout - uses: actions/checkout@v3 + # do not quote to keep this as a single line + echo cpp_files=${cpp_files} >> $GITHUB_OUTPUT - name: Clang format lint + if: ${{ steps.cpp_files.outputs.cpp_files }} uses: DoozyX/clang-format-lint-action@v0.15 with: - source: './src' + source: ${{ steps.cpp_files.outputs.cpp_files }} extensions: 'cpp,h,m,mm' - clangFormatVersion: 13 + clangFormatVersion: 15 style: file inplace: false @@ -57,7 +50,7 @@ jobs: uses: actions/upload-artifact@v3 with: name: clang-format-fixes - path: src/ + path: ${{ steps.cpp_files.outputs.cpp_files }} cmake-lint: name: CMake Lint diff --git a/src/crypto.h b/src/crypto.h index 9f2061c69af..8a2547b1bf1 100644 --- a/src/crypto.h +++ b/src/crypto.h @@ -84,8 +84,8 @@ class cipher_t { class ecb_t : public cipher_t { public: - ecb_t() = default; - ecb_t(ecb_t &&) noexcept = default; + ecb_t() = default; + ecb_t(ecb_t &&) noexcept = default; ecb_t &operator=(ecb_t &&) noexcept = default; ecb_t(const aes_t &key, bool padding = true); @@ -96,8 +96,8 @@ class ecb_t : public cipher_t { class gcm_t : public cipher_t { public: - gcm_t() = default; - gcm_t(gcm_t &&) noexcept = default; + gcm_t() = default; + gcm_t(gcm_t &&) noexcept = default; gcm_t &operator=(gcm_t &&) noexcept = default; gcm_t(const crypto::aes_t &key, bool padding = true); @@ -115,8 +115,8 @@ class gcm_t : public cipher_t { class cbc_t : public cipher_t { public: - cbc_t() = default; - cbc_t(cbc_t &&) noexcept = default; + cbc_t() = default; + cbc_t(cbc_t &&) noexcept = default; cbc_t &operator=(cbc_t &&) noexcept = default; cbc_t(const crypto::aes_t &key, bool padding = true); diff --git a/src/main.h b/src/main.h index 3fec1f83c00..6169fb9846a 100644 --- a/src/main.h +++ b/src/main.h @@ -31,8 +31,10 @@ int write_file(const char *path, const std::string_view &contents); std::uint16_t map_port(int port); namespace mail { -#define MAIL(x) \ - constexpr auto x = std::string_view { #x } +#define MAIL(x) \ + constexpr auto x = std::string_view { \ +#x \ + } extern safe::mail_t man; diff --git a/src/platform/common.h b/src/platform/common.h index 6f4af7e9094..79e55cda343 100644 --- a/src/platform/common.h +++ b/src/platform/common.h @@ -158,9 +158,9 @@ struct img_t { public: img_t() = default; - img_t(img_t &&) = delete; - img_t(const img_t &) = delete; - img_t &operator=(img_t &&) = delete; + img_t(img_t &&) = delete; + img_t(const img_t &) = delete; + img_t &operator=(img_t &&) = delete; img_t &operator=(const img_t &) = delete; std::uint8_t *data {}; diff --git a/src/platform/linux/graphics.h b/src/platform/linux/graphics.h index c566b48fc4f..dd53a7c826b 100644 --- a/src/platform/linux/graphics.h +++ b/src/platform/linux/graphics.h @@ -35,7 +35,7 @@ class tex_t : public util::buffer_t { using util::buffer_t::buffer_t; public: - tex_t(tex_t &&) = default; + tex_t(tex_t &&) = default; tex_t &operator=(tex_t &&) = default; ~tex_t(); @@ -47,7 +47,7 @@ class frame_buf_t : public util::buffer_t { using util::buffer_t::buffer_t; public: - frame_buf_t(frame_buf_t &&) = default; + frame_buf_t(frame_buf_t &&) = default; frame_buf_t &operator=(frame_buf_t &&) = default; ~frame_buf_t(); diff --git a/src/platform/linux/wayland.h b/src/platform/linux/wayland.h index d6e63843bcb..673ddc2ac15 100644 --- a/src/platform/linux/wayland.h +++ b/src/platform/linux/wayland.h @@ -39,7 +39,7 @@ class dmabuf_t { dmabuf_t(const dmabuf_t &) = delete; dmabuf_t &operator=(const dmabuf_t &) = delete; - dmabuf_t &operator=(dmabuf_t &&) = delete; + dmabuf_t &operator=(dmabuf_t &&) = delete; dmabuf_t(); @@ -91,7 +91,7 @@ class monitor_t { monitor_t(const monitor_t &) = delete; monitor_t &operator=(const monitor_t &) = delete; - monitor_t &operator=(monitor_t &&) = delete; + monitor_t &operator=(monitor_t &&) = delete; monitor_t(wl_output *output); @@ -130,7 +130,7 @@ class interface_t { interface_t(const interface_t &) = delete; interface_t &operator=(const interface_t &) = delete; - interface_t &operator=(interface_t &&) = delete; + interface_t &operator=(interface_t &&) = delete; interface_t() noexcept; @@ -193,7 +193,7 @@ class monitor_t { monitor_t(const monitor_t &) = delete; monitor_t &operator=(const monitor_t &) = delete; - monitor_t &operator=(monitor_t &&) = delete; + monitor_t &operator=(monitor_t &&) = delete; monitor_t(wl_output *output); diff --git a/src/platform/macos/display.mm b/src/platform/macos/display.mm index 1601e3ecd83..8845fbacbfc 100644 --- a/src/platform/macos/display.mm +++ b/src/platform/macos/display.mm @@ -193,4 +193,4 @@ static void setPixelFormat(void *display, OSType pixelFormat) { return display_names; } -} +} // namespace platf diff --git a/src/platform/macos/microphone.mm b/src/platform/macos/microphone.mm index cfe9562da17..6e367e9e5f5 100644 --- a/src/platform/macos/microphone.mm +++ b/src/platform/macos/microphone.mm @@ -84,4 +84,4 @@ int set_sink(const std::string &sink) override { std::unique_ptr audio_control() { return std::make_unique(); } -} +} // namespace platf diff --git a/src/stream.cpp b/src/stream.cpp index d263cff440a..e998ade5bde 100644 --- a/src/stream.cpp +++ b/src/stream.cpp @@ -142,7 +142,7 @@ typedef struct control_encrypted_t { return (uint8_t *)(this + 1); } // encrypted control_header_v2 and payload data follow -} * control_encrypted_p; +} *control_encrypted_p; struct audio_fec_packet_raw_t { uint8_t *payload() { diff --git a/src/utility.h b/src/utility.h index 59776bf1d20..c4946dfdf3c 100644 --- a/src/utility.h +++ b/src/utility.h @@ -21,7 +21,9 @@ template struct argument_type; template -struct argument_type { typedef U type; }; +struct argument_type { + typedef U type; +}; #define KITTY_USING_MOVE_T(move_t, t, init_val, z) \ class move_t { \ @@ -58,22 +60,22 @@ struct argument_type { typedef U type; }; } #define KITTY_DECL_CONSTR(x) \ - x(x &&) noexcept = default; \ + x(x &&) noexcept = default; \ x &operator=(x &&) noexcept = default; \ x(); -#define KITTY_DEFAULT_CONSTR_MOVE(x) \ - x(x &&) noexcept = default; \ +#define KITTY_DEFAULT_CONSTR_MOVE(x) \ + x(x &&) noexcept = default; \ x &operator=(x &&) noexcept = default; #define KITTY_DEFAULT_CONSTR_MOVE_THROW(x) \ - x(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; \ +#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) \ @@ -133,7 +135,9 @@ template using either_t = typename __either::type; template -struct overloaded : Ts... { using Ts::operator()...; }; +struct overloaded : Ts... { + using Ts::operator()...; +}; template overloaded(Ts...) -> overloaded; @@ -453,7 +457,7 @@ class uniq_ptr { constexpr uniq_ptr() noexcept : _p { nullptr } {} constexpr uniq_ptr(std::nullptr_t) noexcept : _p { nullptr } {} - uniq_ptr(const uniq_ptr &other) noexcept = delete; + uniq_ptr(const uniq_ptr &other) noexcept = delete; uniq_ptr &operator=(const uniq_ptr &other) noexcept = delete; template diff --git a/src/video.cpp b/src/video.cpp index 5d353a385e5..2bdb6719b2c 100644 --- a/src/video.cpp +++ b/src/video.cpp @@ -1334,7 +1334,7 @@ void captureThreadSync() { ctx.shutdown_event->raise(true); ctx.join_event->raise(true); } - }); + }); while(encode_run_sync(synced_session_ctxs, ctx) == encode_e::reinit) {} } @@ -1350,7 +1350,7 @@ void capture_async( auto lg = util::fail_guard([&]() { images->stop(); shutdown_event->raise(true); - }); + }); auto ref = capture_thread_async.ref(); if(!ref) { diff --git a/third-party/nvfbc/NvFBC.h b/third-party/nvfbc/NvFBC.h index 8990eeab0f7..8735dec33e9 100644 --- a/third-party/nvfbc/NvFBC.h +++ b/third-party/nvfbc/NvFBC.h @@ -265,74 +265,73 @@ extern "C" { /*! * NvFBC API version. */ -#define NVFBC_VERSION (uint32_t) (NVFBC_VERSION_MINOR | (NVFBC_VERSION_MAJOR << 8)) +#define NVFBC_VERSION (uint32_t)(NVFBC_VERSION_MINOR | (NVFBC_VERSION_MAJOR << 8)) /*! * Creates a version number for structure parameters. */ #define NVFBC_STRUCT_VERSION(typeName, ver) \ - (uint32_t) (sizeof(typeName) | ((ver) << 16) | (NVFBC_VERSION << 24)) + (uint32_t)(sizeof(typeName) | ((ver) << 16) | (NVFBC_VERSION << 24)) /*! * Defines error codes. * * \see NvFBCGetLastErrorStr */ -typedef enum _NVFBCSTATUS -{ - /*! +typedef enum _NVFBCSTATUS { + /*! * This indicates that the API call returned with no errors. */ - NVFBC_SUCCESS = 0, - /*! + NVFBC_SUCCESS = 0, + /*! * This indicates that the API version between the client and the library * is not compatible. */ - NVFBC_ERR_API_VERSION = 1, - /*! + NVFBC_ERR_API_VERSION = 1, + /*! * An internal error occurred. */ - NVFBC_ERR_INTERNAL = 2, - /*! + NVFBC_ERR_INTERNAL = 2, + /*! * This indicates that one or more of the parameter passed to the API call * is invalid. */ - NVFBC_ERR_INVALID_PARAM = 3, - /*! + NVFBC_ERR_INVALID_PARAM = 3, + /*! * This indicates that one or more of the pointers passed to the API call * is invalid. */ - NVFBC_ERR_INVALID_PTR = 4, - /*! + NVFBC_ERR_INVALID_PTR = 4, + /*! * This indicates that the handle passed to the API call to identify the * client is invalid. */ - NVFBC_ERR_INVALID_HANDLE = 5, - /*! + NVFBC_ERR_INVALID_HANDLE = 5, + /*! * This indicates that the maximum number of threaded clients of the same * process has been reached. The limit is 10 threads per process. * There is no limit on the number of process. */ - NVFBC_ERR_MAX_CLIENTS = 6, - /*! + NVFBC_ERR_MAX_CLIENTS = 6, + /*! * This indicates that the requested feature is not currently supported * by the library. */ - NVFBC_ERR_UNSUPPORTED = 7, - /*! + NVFBC_ERR_UNSUPPORTED = 7, + /*! * This indicates that the API call failed because it was unable to allocate * enough memory to perform the requested operation. */ - NVFBC_ERR_OUT_OF_MEMORY = 8, - /*! + NVFBC_ERR_OUT_OF_MEMORY = 8, + /*! * This indicates that the API call was not expected. This happens when * API calls are performed in a wrong order, such as trying to capture * a frame prior to creating a new capture session; or trying to set up * a capture to video memory although a capture session to system memory * was created. */ - NVFBC_ERR_BAD_REQUEST = 9, - /*! + NVFBC_ERR_BAD_REQUEST = 9, + /*! * This indicates an X error, most likely meaning that the X server has * been terminated. When this error is returned, the only resort is to * create another FBC handle using NvFBCCreateHandle(). @@ -346,54 +345,53 @@ typedef enum _NVFBCSTATUS * the OpenGL driver, close the forked process running the capture, or * restart the application. */ - NVFBC_ERR_X = 10, - /*! + NVFBC_ERR_X = 10, + /*! * This indicates a GLX error. */ - NVFBC_ERR_GLX = 11, - /*! + NVFBC_ERR_GLX = 11, + /*! * This indicates an OpenGL error. */ - NVFBC_ERR_GL = 12, - /*! + NVFBC_ERR_GL = 12, + /*! * This indicates a CUDA error. */ - NVFBC_ERR_CUDA = 13, - /*! + NVFBC_ERR_CUDA = 13, + /*! * This indicates a HW encoder error. */ - NVFBC_ERR_ENCODER = 14, - /*! + NVFBC_ERR_ENCODER = 14, + /*! * This indicates an NvFBC context error. */ - NVFBC_ERR_CONTEXT = 15, - /*! + NVFBC_ERR_CONTEXT = 15, + /*! * This indicates that the application must recreate the capture session. * * This error can be returned if a modeset event occurred while capturing * frames, and NVFBC_CREATE_HANDLE_PARAMS::bDisableAutoModesetRecovery * was set to NVFBC_TRUE. */ - NVFBC_ERR_MUST_RECREATE = 16, - /*! + NVFBC_ERR_MUST_RECREATE = 16, + /*! * This indicates a Vulkan error. */ - NVFBC_ERR_VULKAN = 17, + NVFBC_ERR_VULKAN = 17, } NVFBCSTATUS; /*! * Defines boolean values. */ -typedef enum _NVFBC_BOOL -{ - /*! +typedef enum _NVFBC_BOOL { + /*! * False value. */ - NVFBC_FALSE = 0, - /*! + NVFBC_FALSE = 0, + /*! * True value. */ - NVFBC_TRUE, + NVFBC_TRUE, } NVFBC_BOOL; /*! @@ -404,26 +402,25 @@ typedef enum _NVFBC_BOOL /*! * Capture type. */ -typedef enum _NVFBC_CAPTURE_TYPE -{ - /*! +typedef enum _NVFBC_CAPTURE_TYPE { + /*! * Capture frames to a buffer in system memory. */ - NVFBC_CAPTURE_TO_SYS = 0, - /*! + NVFBC_CAPTURE_TO_SYS = 0, + /*! * Capture frames to a CUDA device in video memory. * * Specifying this will dlopen() libcuda.so.1 and fail if not available. */ - NVFBC_CAPTURE_SHARED_CUDA, - /*! + NVFBC_CAPTURE_SHARED_CUDA, + /*! * Retired. Do not use. */ - /* NVFBC_CAPTURE_TO_HW_ENCODER, */ - /*! + /* NVFBC_CAPTURE_TO_HW_ENCODER, */ + /*! * Capture frames to an OpenGL buffer in video memory. */ - NVFBC_CAPTURE_TO_GL = 3, + NVFBC_CAPTURE_TO_GL = 3, } NVFBC_CAPTURE_TYPE; /*! @@ -439,9 +436,8 @@ typedef enum _NVFBC_CAPTURE_TYPE * output captures the region of the X screen that the RandR CRTC is sending to * the RandR output. */ -typedef enum -{ - /*! +typedef enum { + /*! * By default, NvFBC tries to track a connected primary output. If none is * found, then it tries to track the first connected output. If none is * found then it tracks the entire X screen. @@ -451,8 +447,8 @@ typedef enum * * This default behavior might be subject to changes in the future. */ - NVFBC_TRACKING_DEFAULT = 0, - /*! + NVFBC_TRACKING_DEFAULT = 0, + /*! * Track an RandR output specified by its ID in the appropriate field. * * The list of connected outputs can be queried via NvFBCGetStatus(). @@ -461,45 +457,44 @@ typedef enum * If the XRandR extension is not available, setting this option returns an * error. */ - NVFBC_TRACKING_OUTPUT, - /*! + NVFBC_TRACKING_OUTPUT, + /*! * Track the entire X screen. */ - NVFBC_TRACKING_SCREEN, + NVFBC_TRACKING_SCREEN, } NVFBC_TRACKING_TYPE; /*! * Buffer format. */ -typedef enum _NVFBC_BUFFER_FORMAT -{ - /*! +typedef enum _NVFBC_BUFFER_FORMAT { + /*! * Data will be converted to ARGB8888 byte-order format. 32 bpp. */ - NVFBC_BUFFER_FORMAT_ARGB = 0, - /*! + NVFBC_BUFFER_FORMAT_ARGB = 0, + /*! * Data will be converted to RGB888 byte-order format. 24 bpp. */ - NVFBC_BUFFER_FORMAT_RGB, - /*! + NVFBC_BUFFER_FORMAT_RGB, + /*! * Data will be converted to NV12 format using HDTV weights * according to ITU-R BT.709. 12 bpp. */ - NVFBC_BUFFER_FORMAT_NV12, - /*! + NVFBC_BUFFER_FORMAT_NV12, + /*! * Data will be converted to YUV 444 planar format using HDTV weights * according to ITU-R BT.709. 24 bpp */ - NVFBC_BUFFER_FORMAT_YUV444P, - /*! + NVFBC_BUFFER_FORMAT_YUV444P, + /*! * Data will be converted to RGBA8888 byte-order format. 32 bpp. */ - NVFBC_BUFFER_FORMAT_RGBA, - /*! + NVFBC_BUFFER_FORMAT_RGBA, + /*! * Native format. No pixel conversion needed. * BGRA8888 byte-order format. 32 bpp. */ - NVFBC_BUFFER_FORMAT_BGRA, + NVFBC_BUFFER_FORMAT_BGRA, } NVFBC_BUFFER_FORMAT; #define NVFBC_BUFFER_FORMAT_YUV420P NVFBC_BUFFER_FORMAT_NV12 @@ -519,65 +514,62 @@ typedef uint64_t NVFBC_SESSION_HANDLE; * 800x600+100+50 effectively captures a region of 800x600+2020+50 relative to * the X screen. */ -typedef struct _NVFBC_BOX -{ - /*! +typedef struct _NVFBC_BOX { + /*! * [in] X offset of the box. */ - uint32_t x; - /*! + uint32_t x; + /*! * [in] Y offset of the box. */ - uint32_t y; - /*! + uint32_t y; + /*! * [in] Width of the box. */ - uint32_t w; - /*! + uint32_t w; + /*! * [in] Height of the box. */ - uint32_t h; + uint32_t h; } NVFBC_BOX; /*! * Size used to describe the size of a frame. */ -typedef struct _NVFBC_SIZE -{ - /*! +typedef struct _NVFBC_SIZE { + /*! * [in] Width. */ - uint32_t w; - /*! + uint32_t w; + /*! * [in] Height. */ - uint32_t h; + uint32_t h; } NVFBC_SIZE; /*! * Describes information about a captured frame. */ -typedef struct _NVFBC_FRAME_GRAB_INFO -{ - /*! +typedef struct _NVFBC_FRAME_GRAB_INFO { + /*! * [out] Width of the captured frame. */ - uint32_t dwWidth; - /*! + uint32_t dwWidth; + /*! * [out] Height of the captured frame. */ - uint32_t dwHeight; - /*! + uint32_t dwHeight; + /*! * [out] Size of the frame in bytes. */ - uint32_t dwByteSize; - /*! + uint32_t dwByteSize; + /*! * [out] Incremental ID of the current frame. * * This can be used to identify a frame. */ - uint32_t dwCurrentFrame; - /*! + uint32_t dwCurrentFrame; + /*! * [out] Whether the captured frame is a new frame. * * When using non blocking calls it is possible to capture a frame @@ -607,8 +599,8 @@ typedef struct _NVFBC_FRAME_GRAB_INFO * are damaged * - Using NvFBC's diffmaps to find out if the frame changed */ - NVFBC_BOOL bIsNewFrame; - /*! + NVFBC_BOOL bIsNewFrame; + /*! * [out] Frame timestamp * * Time in micro seconds when the display server started rendering the @@ -619,48 +611,47 @@ typedef struct _NVFBC_FRAME_GRAB_INFO * will reflect the time when the old frame was rendered by the display * server. */ - uint64_t ulTimestampUs; - /* + uint64_t ulTimestampUs; + /* * [out] Number of frames generated since the last capture. * * This can help applications tell whether they missed frames or there * were no frames generated by the server since the last capture. */ - uint32_t dwMissedFrames; - /* + uint32_t dwMissedFrames; + /* * [out] Whether the captured frame required post processing. * * See the 'Post Processing' section. */ - NVFBC_BOOL bRequiredPostProcessing; - /* + NVFBC_BOOL bRequiredPostProcessing; + /* * [out] Whether this frame was obtained via direct capture. * * See NVFBC_CREATE_CAPTURE_SESSION_PARAMS::bAllowDirectCapture. */ - NVFBC_BOOL bDirectCapture; + NVFBC_BOOL bDirectCapture; } NVFBC_FRAME_GRAB_INFO; /*! * Defines parameters for the CreateHandle() API call. */ -typedef struct _NVFBC_CREATE_HANDLE_PARAMS -{ - /*! +typedef struct _NVFBC_CREATE_HANDLE_PARAMS { + /*! * [in] Must be set to NVFBC_CREATE_HANDLE_PARAMS_VER */ - uint32_t dwVersion; - /*! + uint32_t dwVersion; + /*! * [in] Application specific private information passed to the NvFBC * session. */ - const void *privateData; - /*! + const void *privateData; + /*! * [in] Size of the application specific private information passed to the * NvFBC session. */ - uint32_t privateDataSize; - /*! + uint32_t privateDataSize; + /*! * [in] Whether NvFBC should not create and manage its own graphics context * * NvFBC internally uses OpenGL to perfom graphics operations on the @@ -671,8 +662,8 @@ typedef struct _NVFBC_CREATE_HANDLE_PARAMS * be the application's responsibility to make sure that a context is * current on the thread calling into the NvFBC API. */ - NVFBC_BOOL bExternallyManagedContext; - /*! + NVFBC_BOOL bExternallyManagedContext; + /*! * [in] GLX context * * GLX context that NvFBC should use internally to create pixmaps and @@ -681,8 +672,8 @@ typedef struct _NVFBC_CREATE_HANDLE_PARAMS * Note: NvFBC expects a context created against a GLX_RGBA_TYPE render * type. */ - void *glxCtx; - /*! + void *glxCtx; + /*! * [in] GLX framebuffer configuration * * Framebuffer configuration that was used to create the GLX context, and @@ -694,7 +685,7 @@ typedef struct _NVFBC_CREATE_HANDLE_PARAMS * GLX_BIND_TO_TEXTURE_RGBA_EXT, 1 * GLX_BIND_TO_TEXTURE_TARGETS_EXT, GLX_TEXTURE_2D_BIT_EXT */ - void *glxFBConfig; + void *glxFBConfig; } NVFBC_CREATE_HANDLE_PARAMS; /*! @@ -705,12 +696,11 @@ typedef struct _NVFBC_CREATE_HANDLE_PARAMS /*! * Defines parameters for the ::NvFBCDestroyHandle() API call. */ -typedef struct _NVFBC_DESTROY_HANDLE_PARAMS -{ - /*! +typedef struct _NVFBC_DESTROY_HANDLE_PARAMS { + /*! * [in] Must be set to NVFBC_DESTROY_HANDLE_PARAMS_VER */ - uint32_t dwVersion; + uint32_t dwVersion; } NVFBC_DESTROY_HANDLE_PARAMS; /*! @@ -736,60 +726,58 @@ typedef struct _NVFBC_DESTROY_HANDLE_PARAMS * * \see Requirements */ -typedef struct _NVFBC_OUTPUT -{ - /*! +typedef struct _NVFBC_OUTPUT { + /*! * Identifier of the RandR output. */ - uint32_t dwId; - /*! + uint32_t dwId; + /*! * Name of the RandR output, as reported by tools such as xrandr(1). * * Example: "DVI-I-0" */ - char name[NVFBC_OUTPUT_NAME_LEN]; - /*! + char name[NVFBC_OUTPUT_NAME_LEN]; + /*! * Region of the X screen tracked by the RandR CRTC driving this RandR * output. */ - NVFBC_BOX trackedBox; + NVFBC_BOX trackedBox; } NVFBC_RANDR_OUTPUT_INFO; /*! * Defines parameters for the ::NvFBCGetStatus() API call. */ -typedef struct _NVFBC_GET_STATUS_PARAMS -{ - /*! +typedef struct _NVFBC_GET_STATUS_PARAMS { + /*! * [in] Must be set to NVFBC_GET_STATUS_PARAMS_VER */ - uint32_t dwVersion; - /*! + uint32_t dwVersion; + /*! * [out] Whether or not framebuffer capture is supported by the graphics * driver. */ - NVFBC_BOOL bIsCapturePossible; - /*! + NVFBC_BOOL bIsCapturePossible; + /*! * [out] Whether or not there is already a capture session on this system. */ - NVFBC_BOOL bCurrentlyCapturing; - /*! + NVFBC_BOOL bCurrentlyCapturing; + /*! * [out] Whether or not it is possible to create a capture session on this * system. */ - NVFBC_BOOL bCanCreateNow; - /*! + NVFBC_BOOL bCanCreateNow; + /*! * [out] Size of the X screen (framebuffer). */ - NVFBC_SIZE screenSize; - /*! + NVFBC_SIZE screenSize; + /*! * [out] Whether the XRandR extension is available. * * If this extension is not available then it is not possible to have * information about RandR outputs. */ - NVFBC_BOOL bXRandRAvailable; - /*! + NVFBC_BOOL bXRandRAvailable; + /*! * [out] Array of outputs connected to the X screen. * * An application can track a specific output by specifying its ID when @@ -797,20 +785,20 @@ typedef struct _NVFBC_GET_STATUS_PARAMS * * Only if XRandR is available. */ - NVFBC_RANDR_OUTPUT_INFO outputs[NVFBC_OUTPUT_MAX]; - /*! + NVFBC_RANDR_OUTPUT_INFO outputs[NVFBC_OUTPUT_MAX]; + /*! * [out] Number of outputs connected to the X screen. * * This must be used to parse the array of connected outputs. * * Only if XRandR is available. */ - uint32_t dwOutputNum; - /*! + uint32_t dwOutputNum; + /*! * [out] Version of the NvFBC library running on this system. */ - uint32_t dwNvFBCVersion; - /*! + uint32_t dwNvFBCVersion; + /*! * [out] Whether the X server is currently in modeset. * * When the X server is in modeset, it must give up all its video @@ -819,7 +807,7 @@ typedef struct _NVFBC_GET_STATUS_PARAMS * * Note that VT-switches are considered modesets. */ - NVFBC_BOOL bInModeset; + NVFBC_BOOL bInModeset; } NVFBC_GET_STATUS_PARAMS; /*! @@ -830,53 +818,52 @@ typedef struct _NVFBC_GET_STATUS_PARAMS /*! * Defines parameters for the ::NvFBCCreateCaptureSession() API call. */ -typedef struct _NVFBC_CREATE_CAPTURE_SESSION_PARAMS -{ - /*! +typedef struct _NVFBC_CREATE_CAPTURE_SESSION_PARAMS { + /*! * [in] Must be set to NVFBC_CREATE_CAPTURE_SESSION_PARAMS_VER */ - uint32_t dwVersion; - /*! + uint32_t dwVersion; + /*! * [in] Desired capture type. * * Note that when specyfing ::NVFBC_CAPTURE_SHARED_CUDA NvFBC will try to * dlopen() the corresponding libraries. This means that NvFBC can run on * a system without the CUDA library since it does not link against them. */ - NVFBC_CAPTURE_TYPE eCaptureType; - /*! + NVFBC_CAPTURE_TYPE eCaptureType; + /*! * [in] What region of the framebuffer should be tracked. */ - NVFBC_TRACKING_TYPE eTrackingType; - /*! + NVFBC_TRACKING_TYPE eTrackingType; + /*! * [in] ID of the output to track if eTrackingType is set to * ::NVFBC_TRACKING_OUTPUT. */ - uint32_t dwOutputId; - /*! + uint32_t dwOutputId; + /*! * [in] Crop the tracked region. * * The coordinates are relative to the tracked region. * * It can be set to 0 to capture the entire tracked region. */ - NVFBC_BOX captureBox; - /*! + NVFBC_BOX captureBox; + /*! * [in] Desired size of the captured frame. * * This parameter allow to scale the captured frame. * * It can be set to 0 to disable frame resizing. */ - NVFBC_SIZE frameSize; - /*! + NVFBC_SIZE frameSize; + /*! * [in] Whether the mouse cursor should be composited to the frame. * * Disabling the cursor will not generate new frames when only the cursor * is moved. */ - NVFBC_BOOL bWithCursor; - /*! + NVFBC_BOOL bWithCursor; + /*! * [in] Whether NvFBC should not attempt to recover from modesets. * * NvFBC is able to detect when a modeset event occured and can automatically @@ -895,8 +882,8 @@ typedef struct _NVFBC_CREATE_CAPTURE_SESSION_PARAMS * Note that during modeset recovery, NvFBC will try to re-create the * capture session every second until it succeeds. */ - NVFBC_BOOL bDisableAutoModesetRecovery; - /*! + NVFBC_BOOL bDisableAutoModesetRecovery; + /*! * [in] Whether NvFBC should round the requested frameSize. * * When disabled, NvFBC will not attempt to round the requested resolution. @@ -918,8 +905,8 @@ typedef struct _NVFBC_CREATE_CAPTURE_SESSION_PARAMS * NVFBC_FRAME_GRAB_INFO::dwWidth and NVFBC_FRAME_GRAB_INFO::dwHeight should * always be used for getting information about captured frames. */ - NVFBC_BOOL bRoundFrameSize; - /*! + NVFBC_BOOL bRoundFrameSize; + /*! * [in] Rate in ms at which the display server generates new frames * * This controls the frequency at which the display server will generate @@ -930,8 +917,8 @@ typedef struct _NVFBC_CREATE_CAPTURE_SESSION_PARAMS * * The default value is 16ms (~ 60 Hz). */ - uint32_t dwSamplingRateMs; - /*! + uint32_t dwSamplingRateMs; + /*! * [in] Enable push model for frame capture * * When set to NVFBC_TRUE, the display server will generate frames whenever @@ -947,8 +934,8 @@ typedef struct _NVFBC_CREATE_CAPTURE_SESSION_PARAMS * Note that applications running at high frame rates will increase CPU and * GPU loads. */ - NVFBC_BOOL bPushModel; - /*! + NVFBC_BOOL bPushModel; + /*! * [in] Allow direct capture * * Direct capture allows NvFBC to attach itself to a fullscreen graphics @@ -987,7 +974,7 @@ typedef struct _NVFBC_CREATE_CAPTURE_SESSION_PARAMS * An application can know whether a given frame was obtained through * direct capture by checking NVFBC_FRAME_GRAB_INFO::bDirectCapture. */ - NVFBC_BOOL bAllowDirectCapture; + NVFBC_BOOL bAllowDirectCapture; } NVFBC_CREATE_CAPTURE_SESSION_PARAMS; /*! @@ -998,12 +985,11 @@ typedef struct _NVFBC_CREATE_CAPTURE_SESSION_PARAMS /*! * Defines parameters for the ::NvFBCDestroyCaptureSession() API call. */ -typedef struct _NVFBC_DESTROY_CAPTURE_SESSION_PARAMS -{ - /*! +typedef struct _NVFBC_DESTROY_CAPTURE_SESSION_PARAMS { + /*! * [in] Must be set to NVFBC_DESTROY_CAPTURE_SESSION_PARAMS_VER */ - uint32_t dwVersion; + uint32_t dwVersion; } NVFBC_DESTROY_CAPTURE_SESSION_PARAMS; /*! @@ -1014,12 +1000,11 @@ typedef struct _NVFBC_DESTROY_CAPTURE_SESSION_PARAMS /*! * Defines parameters for the ::NvFBCBindContext() API call. */ -typedef struct _NVFBC_BIND_CONTEXT_PARAMS -{ - /*! +typedef struct _NVFBC_BIND_CONTEXT_PARAMS { + /*! * [in] Must be set to NVFBC_BIND_CONTEXT_PARAMS_VER */ - uint32_t dwVersion; + uint32_t dwVersion; } NVFBC_BIND_CONTEXT_PARAMS; /*! @@ -1030,12 +1015,11 @@ typedef struct _NVFBC_BIND_CONTEXT_PARAMS /*! * Defines parameters for the ::NvFBCReleaseContext() API call. */ -typedef struct _NVFBC_RELEASE_CONTEXT_PARAMS -{ - /*! +typedef struct _NVFBC_RELEASE_CONTEXT_PARAMS { + /*! * [in] Must be set to NVFBC_RELEASE_CONTEXT_PARAMS_VER */ - uint32_t dwVersion; + uint32_t dwVersion; } NVFBC_RELEASE_CONTEXT_PARAMS; /*! @@ -1046,9 +1030,8 @@ typedef struct _NVFBC_RELEASE_CONTEXT_PARAMS /*! * Defines flags that can be used when capturing to system memory. */ -typedef enum -{ - /*! +typedef enum { + /*! * Default, capturing waits for a new frame or mouse move. * * The default behavior of blocking grabs is to wait for a new frame until @@ -1056,16 +1039,16 @@ typedef enum * ready that the client hasn't seen. * \see NVFBC_TOSYS_GRAB_FLAGS_NOWAIT_IF_NEW_FRAME_READY */ - NVFBC_TOSYS_GRAB_FLAGS_NOFLAGS = 0, - /*! + NVFBC_TOSYS_GRAB_FLAGS_NOFLAGS = 0, + /*! * Capturing does not wait for a new frame nor a mouse move. * * It is therefore possible to capture the same frame multiple times. * When this occurs, the dwCurrentFrame parameter of the * NVFBC_FRAME_GRAB_INFO structure is not incremented. */ - NVFBC_TOSYS_GRAB_FLAGS_NOWAIT = (1 << 0), - /*! + NVFBC_TOSYS_GRAB_FLAGS_NOWAIT = (1 << 0), + /*! * Forces the destination buffer to be refreshed even if the frame has not * changed since previous capture. * @@ -1075,29 +1058,28 @@ typedef enum * Setting that flag will prevent this behavior. This can be useful e.g., * if the application has modified the buffer in the meantime. */ - NVFBC_TOSYS_GRAB_FLAGS_FORCE_REFRESH = (1 << 1), - /*! + NVFBC_TOSYS_GRAB_FLAGS_FORCE_REFRESH = (1 << 1), + /*! * Similar to NVFBC_TOSYS_GRAB_FLAGS_NOFLAGS, except that the capture will * not wait if there is already a frame available that the client has * never seen yet. */ - NVFBC_TOSYS_GRAB_FLAGS_NOWAIT_IF_NEW_FRAME_READY = (1 << 2), + NVFBC_TOSYS_GRAB_FLAGS_NOWAIT_IF_NEW_FRAME_READY = (1 << 2), } NVFBC_TOSYS_GRAB_FLAGS; /*! * Defines parameters for the ::NvFBCToSysSetUp() API call. */ -typedef struct _NVFBC_TOSYS_SETUP_PARAMS -{ - /*! +typedef struct _NVFBC_TOSYS_SETUP_PARAMS { + /*! * [in] Must be set to NVFBC_TOSYS_SETUP_PARAMS_VER */ - uint32_t dwVersion; - /*! + uint32_t dwVersion; + /*! * [in] Desired buffer format. */ - NVFBC_BUFFER_FORMAT eBufferFormat; - /*! + NVFBC_BUFFER_FORMAT eBufferFormat; + /*! * [out] Pointer to a pointer to a buffer in system memory. * * This buffer contains the pixel value of the requested format. Refer to @@ -1111,12 +1093,12 @@ typedef struct _NVFBC_TOSYS_SETUP_PARAMS * size is returned in the dwByteSize field of the * ::NVFBC_FRAME_GRAB_INFO structure. */ - void **ppBuffer; - /*! + void **ppBuffer; + /*! * [in] Whether differential maps should be generated. */ - NVFBC_BOOL bWithDiffMap; - /*! + NVFBC_BOOL bWithDiffMap; + /*! * [out] Pointer to a pointer to a buffer in system memory. * * This buffer contains the differential map of two frames. It must be read @@ -1135,8 +1117,8 @@ typedef struct _NVFBC_TOSYS_SETUP_PARAMS * This option is not compatible with the ::NVFBC_BUFFER_FORMAT_YUV420P and * ::NVFBC_BUFFER_FORMAT_YUV444P buffer formats. */ - void **ppDiffMap; - /*! + void **ppDiffMap; + /*! * [in] Scaling factor of the differential maps. * * For example, a scaling factor of 16 means that one pixel of the diffmap @@ -1149,13 +1131,13 @@ typedef struct _NVFBC_TOSYS_SETUP_PARAMS * The default scaling factor is 1. A dwDiffMapScalingFactor of 0 will be * set to 1. */ - uint32_t dwDiffMapScalingFactor; - /*! + uint32_t dwDiffMapScalingFactor; + /*! * [out] Size of the differential map. * * Only set if bWithDiffMap is set to NVFBC_TRUE. */ - NVFBC_SIZE diffMapSize; + NVFBC_SIZE diffMapSize; } NVFBC_TOSYS_SETUP_PARAMS; /*! @@ -1166,23 +1148,22 @@ typedef struct _NVFBC_TOSYS_SETUP_PARAMS /*! * Defines parameters for the ::NvFBCToSysGrabFrame() API call. */ -typedef struct _NVFBC_TOSYS_GRAB_FRAME_PARAMS -{ - /*! +typedef struct _NVFBC_TOSYS_GRAB_FRAME_PARAMS { + /*! * [in] Must be set to NVFBC_TOSYS_GRAB_FRAME_PARAMS_VER */ - uint32_t dwVersion; - /*! + uint32_t dwVersion; + /*! * [in] Flags defining the behavior of this frame capture. */ - uint32_t dwFlags; - /*! + uint32_t dwFlags; + /*! * [out] Information about the captured frame. * * Can be NULL. */ - NVFBC_FRAME_GRAB_INFO *pFrameGrabInfo; - /*! + NVFBC_FRAME_GRAB_INFO *pFrameGrabInfo; + /*! * [in] Wait timeout in milliseconds. * * When capturing frames with the NVFBC_TOSYS_GRAB_FLAGS_NOFLAGS or @@ -1207,7 +1188,7 @@ typedef struct _NVFBC_TOSYS_GRAB_FRAME_PARAMS * * Set to 0 to disable timeouts. */ - uint32_t dwTimeoutMs; + uint32_t dwTimeoutMs; } NVFBC_TOSYS_GRAB_FRAME_PARAMS; /*! @@ -1218,9 +1199,8 @@ typedef struct _NVFBC_TOSYS_GRAB_FRAME_PARAMS /*! * Defines flags that can be used when capturing to a CUDA buffer in video memory. */ -typedef enum -{ - /*! +typedef enum { + /*! * Default, capturing waits for a new frame or mouse move. * * The default behavior of blocking grabs is to wait for a new frame until @@ -1228,16 +1208,16 @@ typedef enum * ready that the client hasn't seen. * \see NVFBC_TOCUDA_GRAB_FLAGS_NOWAIT_IF_NEW_FRAME_READY */ - NVFBC_TOCUDA_GRAB_FLAGS_NOFLAGS = 0, - /*! + NVFBC_TOCUDA_GRAB_FLAGS_NOFLAGS = 0, + /*! * Capturing does not wait for a new frame nor a mouse move. * * It is therefore possible to capture the same frame multiple times. * When this occurs, the dwCurrentFrame parameter of the * NVFBC_FRAME_GRAB_INFO structure is not incremented. */ - NVFBC_TOCUDA_GRAB_FLAGS_NOWAIT = (1 << 0), - /*! + NVFBC_TOCUDA_GRAB_FLAGS_NOWAIT = (1 << 0), + /*! * [in] Forces the destination buffer to be refreshed even if the frame * has not changed since previous capture. * @@ -1247,28 +1227,27 @@ typedef enum * Setting that flag will prevent this behavior. This can be useful e.g., * if the application has modified the buffer in the meantime. */ - NVFBC_TOCUDA_GRAB_FLAGS_FORCE_REFRESH = (1 << 1), - /*! + NVFBC_TOCUDA_GRAB_FLAGS_FORCE_REFRESH = (1 << 1), + /*! * Similar to NVFBC_TOCUDA_GRAB_FLAGS_NOFLAGS, except that the capture will * not wait if there is already a frame available that the client has * never seen yet. */ - NVFBC_TOCUDA_GRAB_FLAGS_NOWAIT_IF_NEW_FRAME_READY = (1 << 2), + NVFBC_TOCUDA_GRAB_FLAGS_NOWAIT_IF_NEW_FRAME_READY = (1 << 2), } NVFBC_TOCUDA_FLAGS; /*! * Defines parameters for the ::NvFBCToCudaSetUp() API call. */ -typedef struct _NVFBC_TOCUDA_SETUP_PARAMS -{ - /*! +typedef struct _NVFBC_TOCUDA_SETUP_PARAMS { + /*! * [in] Must be set to NVFBC_TOCUDA_SETUP_PARAMS_VER */ - uint32_t dwVersion; - /*! + uint32_t dwVersion; + /*! * [in] Desired buffer format. */ - NVFBC_BUFFER_FORMAT eBufferFormat; + NVFBC_BUFFER_FORMAT eBufferFormat; } NVFBC_TOCUDA_SETUP_PARAMS; /*! @@ -1279,17 +1258,16 @@ typedef struct _NVFBC_TOCUDA_SETUP_PARAMS /*! * Defines parameters for the ::NvFBCToCudaGrabFrame() API call. */ -typedef struct _NVFBC_TOCUDA_GRAB_FRAME_PARAMS -{ - /*! +typedef struct _NVFBC_TOCUDA_GRAB_FRAME_PARAMS { + /*! * [in] Must be set to NVFBC_TOCUDA_GRAB_FRAME_PARAMS_VER. */ - uint32_t dwVersion; - /*! + uint32_t dwVersion; + /*! * [in] Flags defining the behavior of this frame capture. */ - uint32_t dwFlags; - /*! + uint32_t dwFlags; + /*! * [out] Pointer to a ::CUdeviceptr * * The application does not need to allocate memory for this CUDA device. @@ -1302,14 +1280,14 @@ typedef struct _NVFBC_TOCUDA_GRAB_FRAME_PARAMS * a new frame. If the application wants to do so, it must copy the CUDA * device using ::cuMemcpyDtoD or ::cuMemcpyDtoH beforehand. */ - void *pCUDADeviceBuffer; - /*! + void *pCUDADeviceBuffer; + /*! * [out] Information about the captured frame. * * Can be NULL. */ - NVFBC_FRAME_GRAB_INFO *pFrameGrabInfo; - /*! + NVFBC_FRAME_GRAB_INFO *pFrameGrabInfo; + /*! * [in] Wait timeout in milliseconds. * * When capturing frames with the NVFBC_TOCUDA_GRAB_FLAGS_NOFLAGS or @@ -1334,7 +1312,7 @@ typedef struct _NVFBC_TOCUDA_GRAB_FRAME_PARAMS * * Set to 0 to disable timeouts. */ - uint32_t dwTimeoutMs; + uint32_t dwTimeoutMs; } NVFBC_TOCUDA_GRAB_FRAME_PARAMS; /*! @@ -1345,9 +1323,8 @@ typedef struct _NVFBC_TOCUDA_GRAB_FRAME_PARAMS /*! * Defines flags that can be used when capturing to an OpenGL buffer in video memory. */ -typedef enum -{ - /*! +typedef enum { + /*! * Default, capturing waits for a new frame or mouse move. * * The default behavior of blocking grabs is to wait for a new frame until @@ -1355,16 +1332,16 @@ typedef enum * ready that the client hasn't seen. * \see NVFBC_TOGL_GRAB_FLAGS_NOWAIT_IF_NEW_FRAME_READY */ - NVFBC_TOGL_GRAB_FLAGS_NOFLAGS = 0, - /*! + NVFBC_TOGL_GRAB_FLAGS_NOFLAGS = 0, + /*! * Capturing does not wait for a new frame nor a mouse move. * * It is therefore possible to capture the same frame multiple times. * When this occurs, the dwCurrentFrame parameter of the * NVFBC_FRAME_GRAB_INFO structure is not incremented. */ - NVFBC_TOGL_GRAB_FLAGS_NOWAIT = (1 << 0), - /*! + NVFBC_TOGL_GRAB_FLAGS_NOWAIT = (1 << 0), + /*! * [in] Forces the destination buffer to be refreshed even if the frame * has not changed since previous capture. * @@ -1374,13 +1351,13 @@ typedef enum * Setting that flag will prevent this behavior. This can be useful e.g., * if the application has modified the buffer in the meantime. */ - NVFBC_TOGL_GRAB_FLAGS_FORCE_REFRESH = (1 << 1), - /*! + NVFBC_TOGL_GRAB_FLAGS_FORCE_REFRESH = (1 << 1), + /*! * Similar to NVFBC_TOGL_GRAB_FLAGS_NOFLAGS, except that the capture will * not wait if there is already a frame available that the client has * never seen yet. */ - NVFBC_TOGL_GRAB_FLAGS_NOWAIT_IF_NEW_FRAME_READY = (1 << 2), + NVFBC_TOGL_GRAB_FLAGS_NOWAIT_IF_NEW_FRAME_READY = (1 << 2), } NVFBC_TOGL_FLAGS; /*! @@ -1391,33 +1368,32 @@ typedef enum /*! * Defines parameters for the ::NvFBCToGLSetUp() API call. */ -typedef struct _NVFBC_TOGL_SETUP_PARAMS -{ - /*! +typedef struct _NVFBC_TOGL_SETUP_PARAMS { + /*! * [in] Must be set to NVFBC_TOGL_SETUP_PARAMS_VER */ - uint32_t dwVersion; - /*! + uint32_t dwVersion; + /*! * [in] Desired buffer format. */ - NVFBC_BUFFER_FORMAT eBufferFormat; - /*! + NVFBC_BUFFER_FORMAT eBufferFormat; + /*! * [in] Whether differential maps should be generated. */ - NVFBC_BOOL bWithDiffMap; - /*! + NVFBC_BOOL bWithDiffMap; + /*! * [out] Pointer to a pointer to a buffer in system memory. * * \see NVFBC_TOSYS_SETUP_PARAMS::ppDiffMap */ - void **ppDiffMap; - /*! + void **ppDiffMap; + /*! * [in] Scaling factor of the differential maps. * * \see NVFBC_TOSYS_SETUP_PARAMS::dwDiffMapScalingFactor */ - uint32_t dwDiffMapScalingFactor; - /*! + uint32_t dwDiffMapScalingFactor; + /*! * [out] List of GL textures that will store the captured frames. * * This array is 0 terminated. The number of textures varies depending on @@ -1429,25 +1405,25 @@ typedef struct _NVFBC_TOGL_SETUP_PARAMS * After each frame capture, the texture holding the current frame will be * returned in NVFBC_TOGL_GRAB_FRAME_PARAMS::dwTexture. */ - uint32_t dwTextures[NVFBC_TOGL_TEXTURES_MAX]; - /*! + uint32_t dwTextures[NVFBC_TOGL_TEXTURES_MAX]; + /*! * [out] GL target to which the texture should be bound. */ - uint32_t dwTexTarget; - /*! + uint32_t dwTexTarget; + /*! * [out] GL format of the textures. */ - uint32_t dwTexFormat; - /*! + uint32_t dwTexFormat; + /*! * [out] GL type of the textures. */ - uint32_t dwTexType; - /*! + uint32_t dwTexType; + /*! * [out] Size of the differential map. * * Only set if bWithDiffMap is set to NVFBC_TRUE. */ - NVFBC_SIZE diffMapSize; + NVFBC_SIZE diffMapSize; } NVFBC_TOGL_SETUP_PARAMS; /*! @@ -1458,29 +1434,28 @@ typedef struct _NVFBC_TOGL_SETUP_PARAMS /*! * Defines parameters for the ::NvFBCToGLGrabFrame() API call. */ -typedef struct _NVFBC_TOGL_GRAB_FRAME_PARAMS -{ - /*! +typedef struct _NVFBC_TOGL_GRAB_FRAME_PARAMS { + /*! * [in] Must be set to NVFBC_TOGL_GRAB_FRAME_PARAMS_VER. */ - uint32_t dwVersion; - /*! + uint32_t dwVersion; + /*! * [in] Flags defining the behavior of this frame capture. */ - uint32_t dwFlags; - /*! + uint32_t dwFlags; + /*! * [out] Index of the texture storing the current frame. * * This is an index in the NVFBC_TOGL_SETUP_PARAMS::dwTextures array. */ - uint32_t dwTextureIndex; - /*! + uint32_t dwTextureIndex; + /*! * [out] Information about the captured frame. * * Can be NULL. */ - NVFBC_FRAME_GRAB_INFO *pFrameGrabInfo; - /*! + NVFBC_FRAME_GRAB_INFO *pFrameGrabInfo; + /*! * [in] Wait timeout in milliseconds. * * When capturing frames with the NVFBC_TOGL_GRAB_FLAGS_NOFLAGS or @@ -1505,7 +1480,7 @@ typedef struct _NVFBC_TOGL_GRAB_FRAME_PARAMS * * Set to 0 to disable timeouts. */ - uint32_t dwTimeoutMs; + uint32_t dwTimeoutMs; } NVFBC_TOGL_GRAB_FRAME_PARAMS; /*! @@ -1550,7 +1525,7 @@ typedef struct _NVFBC_TOGL_GRAB_FRAME_PARAMS * A NULL terminated error message, or an empty string. Its maximum length * is NVFBC_ERROR_STR_LEN. */ -const char* NVFBCAPI NvFBCGetLastErrorStr(const NVFBC_SESSION_HANDLE sessionHandle); +const char *NVFBCAPI NvFBCGetLastErrorStr(const NVFBC_SESSION_HANDLE sessionHandle); /*! * \brief Allocates a new handle for an NvFBC client. @@ -1925,20 +1900,20 @@ NVFBCSTATUS NVFBCAPI NvFBCToGLGrabFrame(const NVFBC_SESSION_HANDLE sessionHandle * * Defines API function pointers */ -typedef const char* (NVFBCAPI* PNVFBCGETLASTERRORSTR)(const NVFBC_SESSION_HANDLE sessionHandle); -typedef NVFBCSTATUS (NVFBCAPI* PNVFBCCREATEHANDLE)(NVFBC_SESSION_HANDLE *pSessionHandle, NVFBC_CREATE_HANDLE_PARAMS *pParams); -typedef NVFBCSTATUS (NVFBCAPI* PNVFBCDESTROYHANDLE)(const NVFBC_SESSION_HANDLE sessionHandle, NVFBC_DESTROY_HANDLE_PARAMS *pParams); -typedef NVFBCSTATUS (NVFBCAPI* PNVFBCBINDCONTEXT)(const NVFBC_SESSION_HANDLE sessionHandle, NVFBC_BIND_CONTEXT_PARAMS *pParams); -typedef NVFBCSTATUS (NVFBCAPI* PNVFBCRELEASECONTEXT)(const NVFBC_SESSION_HANDLE sessionHandle, NVFBC_RELEASE_CONTEXT_PARAMS *pParams); -typedef NVFBCSTATUS (NVFBCAPI* PNVFBCGETSTATUS)(const NVFBC_SESSION_HANDLE sessionHandle, NVFBC_GET_STATUS_PARAMS *pParams); -typedef NVFBCSTATUS (NVFBCAPI* PNVFBCCREATECAPTURESESSION)(const NVFBC_SESSION_HANDLE sessionHandle, NVFBC_CREATE_CAPTURE_SESSION_PARAMS *pParams); -typedef NVFBCSTATUS (NVFBCAPI* PNVFBCDESTROYCAPTURESESSION)(const NVFBC_SESSION_HANDLE sessionHandle, NVFBC_DESTROY_CAPTURE_SESSION_PARAMS *pParams); -typedef NVFBCSTATUS (NVFBCAPI* PNVFBCTOSYSSETUP)(const NVFBC_SESSION_HANDLE sessionHandle, NVFBC_TOSYS_SETUP_PARAMS *pParams); -typedef NVFBCSTATUS (NVFBCAPI* PNVFBCTOSYSGRABFRAME)(const NVFBC_SESSION_HANDLE sessionHandle, NVFBC_TOSYS_GRAB_FRAME_PARAMS *pParams); -typedef NVFBCSTATUS (NVFBCAPI* PNVFBCTOCUDASETUP)(const NVFBC_SESSION_HANDLE sessionHandle, NVFBC_TOCUDA_SETUP_PARAMS *pParams); -typedef NVFBCSTATUS (NVFBCAPI* PNVFBCTOCUDAGRABFRAME)(const NVFBC_SESSION_HANDLE sessionHandle, NVFBC_TOCUDA_GRAB_FRAME_PARAMS *pParams); -typedef NVFBCSTATUS (NVFBCAPI* PNVFBCTOGLSETUP)(const NVFBC_SESSION_HANDLE sessionHandle, NVFBC_TOGL_SETUP_PARAMS *pParams); -typedef NVFBCSTATUS (NVFBCAPI* PNVFBCTOGLGRABFRAME)(const NVFBC_SESSION_HANDLE sessionHandle, NVFBC_TOGL_GRAB_FRAME_PARAMS *pParams); +typedef const char *(NVFBCAPI *PNVFBCGETLASTERRORSTR)(const NVFBC_SESSION_HANDLE sessionHandle); +typedef NVFBCSTATUS(NVFBCAPI *PNVFBCCREATEHANDLE)(NVFBC_SESSION_HANDLE *pSessionHandle, NVFBC_CREATE_HANDLE_PARAMS *pParams); +typedef NVFBCSTATUS(NVFBCAPI *PNVFBCDESTROYHANDLE)(const NVFBC_SESSION_HANDLE sessionHandle, NVFBC_DESTROY_HANDLE_PARAMS *pParams); +typedef NVFBCSTATUS(NVFBCAPI *PNVFBCBINDCONTEXT)(const NVFBC_SESSION_HANDLE sessionHandle, NVFBC_BIND_CONTEXT_PARAMS *pParams); +typedef NVFBCSTATUS(NVFBCAPI *PNVFBCRELEASECONTEXT)(const NVFBC_SESSION_HANDLE sessionHandle, NVFBC_RELEASE_CONTEXT_PARAMS *pParams); +typedef NVFBCSTATUS(NVFBCAPI *PNVFBCGETSTATUS)(const NVFBC_SESSION_HANDLE sessionHandle, NVFBC_GET_STATUS_PARAMS *pParams); +typedef NVFBCSTATUS(NVFBCAPI *PNVFBCCREATECAPTURESESSION)(const NVFBC_SESSION_HANDLE sessionHandle, NVFBC_CREATE_CAPTURE_SESSION_PARAMS *pParams); +typedef NVFBCSTATUS(NVFBCAPI *PNVFBCDESTROYCAPTURESESSION)(const NVFBC_SESSION_HANDLE sessionHandle, NVFBC_DESTROY_CAPTURE_SESSION_PARAMS *pParams); +typedef NVFBCSTATUS(NVFBCAPI *PNVFBCTOSYSSETUP)(const NVFBC_SESSION_HANDLE sessionHandle, NVFBC_TOSYS_SETUP_PARAMS *pParams); +typedef NVFBCSTATUS(NVFBCAPI *PNVFBCTOSYSGRABFRAME)(const NVFBC_SESSION_HANDLE sessionHandle, NVFBC_TOSYS_GRAB_FRAME_PARAMS *pParams); +typedef NVFBCSTATUS(NVFBCAPI *PNVFBCTOCUDASETUP)(const NVFBC_SESSION_HANDLE sessionHandle, NVFBC_TOCUDA_SETUP_PARAMS *pParams); +typedef NVFBCSTATUS(NVFBCAPI *PNVFBCTOCUDAGRABFRAME)(const NVFBC_SESSION_HANDLE sessionHandle, NVFBC_TOCUDA_GRAB_FRAME_PARAMS *pParams); +typedef NVFBCSTATUS(NVFBCAPI *PNVFBCTOGLSETUP)(const NVFBC_SESSION_HANDLE sessionHandle, NVFBC_TOGL_SETUP_PARAMS *pParams); +typedef NVFBCSTATUS(NVFBCAPI *PNVFBCTOGLGRABFRAME)(const NVFBC_SESSION_HANDLE sessionHandle, NVFBC_TOGL_GRAB_FRAME_PARAMS *pParams); /// \endcond @@ -1951,28 +1926,28 @@ typedef NVFBCSTATUS (NVFBCAPI* PNVFBCTOGLGRABFRAME)(const NVFBC_SESSION_HANDLE s */ typedef struct { - uint32_t dwVersion; //!< [in] Must be set to NVFBC_VERSION. - PNVFBCGETLASTERRORSTR nvFBCGetLastErrorStr; //!< [out] Pointer to ::NvFBCGetLastErrorStr(). - PNVFBCCREATEHANDLE nvFBCCreateHandle; //!< [out] Pointer to ::NvFBCCreateHandle(). - PNVFBCDESTROYHANDLE nvFBCDestroyHandle; //!< [out] Pointer to ::NvFBCDestroyHandle(). - PNVFBCGETSTATUS nvFBCGetStatus; //!< [out] Pointer to ::NvFBCGetStatus(). - PNVFBCCREATECAPTURESESSION nvFBCCreateCaptureSession; //!< [out] Pointer to ::NvFBCCreateCaptureSession(). - PNVFBCDESTROYCAPTURESESSION nvFBCDestroyCaptureSession; //!< [out] Pointer to ::NvFBCDestroyCaptureSession(). - PNVFBCTOSYSSETUP nvFBCToSysSetUp; //!< [out] Pointer to ::NvFBCToSysSetUp(). - PNVFBCTOSYSGRABFRAME nvFBCToSysGrabFrame; //!< [out] Pointer to ::NvFBCToSysGrabFrame(). - PNVFBCTOCUDASETUP nvFBCToCudaSetUp; //!< [out] Pointer to ::NvFBCToCudaSetUp(). - PNVFBCTOCUDAGRABFRAME nvFBCToCudaGrabFrame; //!< [out] Pointer to ::NvFBCToCudaGrabFrame(). - void* pad1; //!< [out] Retired. Do not use. - void* pad2; //!< [out] Retired. Do not use. - void* pad3; //!< [out] Retired. Do not use. - PNVFBCBINDCONTEXT nvFBCBindContext; //!< [out] Pointer to ::NvFBCBindContext(). - PNVFBCRELEASECONTEXT nvFBCReleaseContext; //!< [out] Pointer to ::NvFBCReleaseContext(). - void* pad4; //!< [out] Retired. Do not use. - void* pad5; //!< [out] Retired. Do not use. - void* pad6; //!< [out] Retired. Do not use. - void* pad7; //!< [out] Retired. Do not use. - PNVFBCTOGLSETUP nvFBCToGLSetUp; //!< [out] Pointer to ::nvFBCToGLSetup(). - PNVFBCTOGLGRABFRAME nvFBCToGLGrabFrame; //!< [out] Pointer to ::nvFBCToGLGrabFrame(). + uint32_t dwVersion; //!< [in] Must be set to NVFBC_VERSION. + PNVFBCGETLASTERRORSTR nvFBCGetLastErrorStr; //!< [out] Pointer to ::NvFBCGetLastErrorStr(). + PNVFBCCREATEHANDLE nvFBCCreateHandle; //!< [out] Pointer to ::NvFBCCreateHandle(). + PNVFBCDESTROYHANDLE nvFBCDestroyHandle; //!< [out] Pointer to ::NvFBCDestroyHandle(). + PNVFBCGETSTATUS nvFBCGetStatus; //!< [out] Pointer to ::NvFBCGetStatus(). + PNVFBCCREATECAPTURESESSION nvFBCCreateCaptureSession; //!< [out] Pointer to ::NvFBCCreateCaptureSession(). + PNVFBCDESTROYCAPTURESESSION nvFBCDestroyCaptureSession; //!< [out] Pointer to ::NvFBCDestroyCaptureSession(). + PNVFBCTOSYSSETUP nvFBCToSysSetUp; //!< [out] Pointer to ::NvFBCToSysSetUp(). + PNVFBCTOSYSGRABFRAME nvFBCToSysGrabFrame; //!< [out] Pointer to ::NvFBCToSysGrabFrame(). + PNVFBCTOCUDASETUP nvFBCToCudaSetUp; //!< [out] Pointer to ::NvFBCToCudaSetUp(). + PNVFBCTOCUDAGRABFRAME nvFBCToCudaGrabFrame; //!< [out] Pointer to ::NvFBCToCudaGrabFrame(). + void *pad1; //!< [out] Retired. Do not use. + void *pad2; //!< [out] Retired. Do not use. + void *pad3; //!< [out] Retired. Do not use. + PNVFBCBINDCONTEXT nvFBCBindContext; //!< [out] Pointer to ::NvFBCBindContext(). + PNVFBCRELEASECONTEXT nvFBCReleaseContext; //!< [out] Pointer to ::NvFBCReleaseContext(). + void *pad4; //!< [out] Retired. Do not use. + void *pad5; //!< [out] Retired. Do not use. + void *pad6; //!< [out] Retired. Do not use. + void *pad7; //!< [out] Retired. Do not use. + PNVFBCTOGLSETUP nvFBCToGLSetUp; //!< [out] Pointer to ::nvFBCToGLSetup(). + PNVFBCTOGLGRABFRAME nvFBCToGLGrabFrame; //!< [out] Pointer to ::nvFBCToGLGrabFrame(). } NVFBC_API_FUNCTION_LIST; /*! @@ -1997,7 +1972,7 @@ NVFBCSTATUS NVFBCAPI NvFBCCreateInstance(NVFBC_API_FUNCTION_LIST *pFunctionList) * * Defines function pointer for the ::NvFBCCreateInstance() API call. */ -typedef NVFBCSTATUS (NVFBCAPI* PNVFBCCREATEINSTANCE)(NVFBC_API_FUNCTION_LIST *pFunctionList); +typedef NVFBCSTATUS(NVFBCAPI *PNVFBCCREATEINSTANCE)(NVFBC_API_FUNCTION_LIST *pFunctionList); #ifdef __cplusplus } diff --git a/third-party/nvfbc/helper_math.h b/third-party/nvfbc/helper_math.h index d17b024e20f..f27b5bff09f 100644 --- a/third-party/nvfbc/helper_math.h +++ b/third-party/nvfbc/helper_math.h @@ -55,29 +55,24 @@ typedef unsigned short ushort; // host implementations of CUDA functions //////////////////////////////////////////////////////////////////////////////// -inline float fminf(float a, float b) -{ - return a < b ? a : b; +inline float fminf(float a, float b) { + return a < b ? a : b; } -inline float fmaxf(float a, float b) -{ - return a > b ? a : b; +inline float fmaxf(float a, float b) { + return a > b ? a : b; } -inline int max(int a, int b) -{ - return a > b ? a : b; +inline int max(int a, int b) { + return a > b ? a : b; } -inline int min(int a, int b) -{ - return a < b ? a : b; +inline int min(int a, int b) { + return a < b ? a : b; } -inline float rsqrtf(float x) -{ - return 1.0f / sqrtf(x); +inline float rsqrtf(float x) { + return 1.0f / sqrtf(x); } #endif @@ -85,1057 +80,843 @@ inline float rsqrtf(float x) // constructors //////////////////////////////////////////////////////////////////////////////// -inline __host__ __device__ float2 make_float2(float s) -{ - return make_float2(s, s); +inline __host__ __device__ float2 make_float2(float s) { + return make_float2(s, s); } -inline __host__ __device__ float2 make_float2(float3 a) -{ - return make_float2(a.x, a.y); +inline __host__ __device__ float2 make_float2(float3 a) { + return make_float2(a.x, a.y); } -inline __host__ __device__ float2 make_float2(int2 a) -{ - return make_float2(float(a.x), float(a.y)); +inline __host__ __device__ float2 make_float2(int2 a) { + return make_float2(float(a.x), float(a.y)); } -inline __host__ __device__ float2 make_float2(uint2 a) -{ - return make_float2(float(a.x), float(a.y)); +inline __host__ __device__ float2 make_float2(uint2 a) { + return make_float2(float(a.x), float(a.y)); } -inline __host__ __device__ int2 make_int2(int s) -{ - return make_int2(s, s); +inline __host__ __device__ int2 make_int2(int s) { + return make_int2(s, s); } -inline __host__ __device__ int2 make_int2(int3 a) -{ - return make_int2(a.x, a.y); +inline __host__ __device__ int2 make_int2(int3 a) { + return make_int2(a.x, a.y); } -inline __host__ __device__ int2 make_int2(uint2 a) -{ - return make_int2(int(a.x), int(a.y)); +inline __host__ __device__ int2 make_int2(uint2 a) { + return make_int2(int(a.x), int(a.y)); } -inline __host__ __device__ int2 make_int2(float2 a) -{ - return make_int2(int(a.x), int(a.y)); +inline __host__ __device__ int2 make_int2(float2 a) { + return make_int2(int(a.x), int(a.y)); } -inline __host__ __device__ uint2 make_uint2(uint s) -{ - return make_uint2(s, s); +inline __host__ __device__ uint2 make_uint2(uint s) { + return make_uint2(s, s); } -inline __host__ __device__ uint2 make_uint2(uint3 a) -{ - return make_uint2(a.x, a.y); +inline __host__ __device__ uint2 make_uint2(uint3 a) { + return make_uint2(a.x, a.y); } -inline __host__ __device__ uint2 make_uint2(int2 a) -{ - return make_uint2(uint(a.x), uint(a.y)); +inline __host__ __device__ uint2 make_uint2(int2 a) { + return make_uint2(uint(a.x), uint(a.y)); } -inline __host__ __device__ float3 make_float3(float s) -{ - return make_float3(s, s, s); +inline __host__ __device__ float3 make_float3(float s) { + return make_float3(s, s, s); } -inline __host__ __device__ float3 make_float3(float2 a) -{ - return make_float3(a.x, a.y, 0.0f); +inline __host__ __device__ float3 make_float3(float2 a) { + return make_float3(a.x, a.y, 0.0f); } -inline __host__ __device__ float3 make_float3(float2 a, float s) -{ - return make_float3(a.x, a.y, s); +inline __host__ __device__ float3 make_float3(float2 a, float s) { + return make_float3(a.x, a.y, s); } -inline __host__ __device__ float3 make_float3(float4 a) -{ - return make_float3(a.x, a.y, a.z); +inline __host__ __device__ float3 make_float3(float4 a) { + return make_float3(a.x, a.y, a.z); } -inline __host__ __device__ float3 make_float3(int3 a) -{ - return make_float3(float(a.x), float(a.y), float(a.z)); +inline __host__ __device__ float3 make_float3(int3 a) { + return make_float3(float(a.x), float(a.y), float(a.z)); } -inline __host__ __device__ float3 make_float3(uint3 a) -{ - return make_float3(float(a.x), float(a.y), float(a.z)); +inline __host__ __device__ float3 make_float3(uint3 a) { + return make_float3(float(a.x), float(a.y), float(a.z)); } -inline __host__ __device__ int3 make_int3(int s) -{ - return make_int3(s, s, s); +inline __host__ __device__ int3 make_int3(int s) { + return make_int3(s, s, s); } -inline __host__ __device__ int3 make_int3(int2 a) -{ - return make_int3(a.x, a.y, 0); +inline __host__ __device__ int3 make_int3(int2 a) { + return make_int3(a.x, a.y, 0); } -inline __host__ __device__ int3 make_int3(int2 a, int s) -{ - return make_int3(a.x, a.y, s); +inline __host__ __device__ int3 make_int3(int2 a, int s) { + return make_int3(a.x, a.y, s); } -inline __host__ __device__ int3 make_int3(uint3 a) -{ - return make_int3(int(a.x), int(a.y), int(a.z)); +inline __host__ __device__ int3 make_int3(uint3 a) { + return make_int3(int(a.x), int(a.y), int(a.z)); } -inline __host__ __device__ int3 make_int3(float3 a) -{ - return make_int3(int(a.x), int(a.y), int(a.z)); +inline __host__ __device__ int3 make_int3(float3 a) { + return make_int3(int(a.x), int(a.y), int(a.z)); } -inline __host__ __device__ uint3 make_uint3(uint s) -{ - return make_uint3(s, s, s); +inline __host__ __device__ uint3 make_uint3(uint s) { + return make_uint3(s, s, s); } -inline __host__ __device__ uint3 make_uint3(uint2 a) -{ - return make_uint3(a.x, a.y, 0); +inline __host__ __device__ uint3 make_uint3(uint2 a) { + return make_uint3(a.x, a.y, 0); } -inline __host__ __device__ uint3 make_uint3(uint2 a, uint s) -{ - return make_uint3(a.x, a.y, s); +inline __host__ __device__ uint3 make_uint3(uint2 a, uint s) { + return make_uint3(a.x, a.y, s); } -inline __host__ __device__ uint3 make_uint3(uint4 a) -{ - return make_uint3(a.x, a.y, a.z); +inline __host__ __device__ uint3 make_uint3(uint4 a) { + return make_uint3(a.x, a.y, a.z); } -inline __host__ __device__ uint3 make_uint3(int3 a) -{ - return make_uint3(uint(a.x), uint(a.y), uint(a.z)); +inline __host__ __device__ uint3 make_uint3(int3 a) { + return make_uint3(uint(a.x), uint(a.y), uint(a.z)); } -inline __host__ __device__ float4 make_float4(float s) -{ - return make_float4(s, s, s, s); +inline __host__ __device__ float4 make_float4(float s) { + return make_float4(s, s, s, s); } -inline __host__ __device__ float4 make_float4(float3 a) -{ - return make_float4(a.x, a.y, a.z, 0.0f); +inline __host__ __device__ float4 make_float4(float3 a) { + return make_float4(a.x, a.y, a.z, 0.0f); } -inline __host__ __device__ float4 make_float4(float3 a, float w) -{ - return make_float4(a.x, a.y, a.z, w); +inline __host__ __device__ float4 make_float4(float3 a, float w) { + return make_float4(a.x, a.y, a.z, w); } -inline __host__ __device__ float4 make_float4(int4 a) -{ - return make_float4(float(a.x), float(a.y), float(a.z), float(a.w)); +inline __host__ __device__ float4 make_float4(int4 a) { + return make_float4(float(a.x), float(a.y), float(a.z), float(a.w)); } -inline __host__ __device__ float4 make_float4(uint4 a) -{ - return make_float4(float(a.x), float(a.y), float(a.z), float(a.w)); +inline __host__ __device__ float4 make_float4(uint4 a) { + return make_float4(float(a.x), float(a.y), float(a.z), float(a.w)); } -inline __host__ __device__ int4 make_int4(int s) -{ - return make_int4(s, s, s, s); +inline __host__ __device__ int4 make_int4(int s) { + return make_int4(s, s, s, s); } -inline __host__ __device__ int4 make_int4(int3 a) -{ - return make_int4(a.x, a.y, a.z, 0); +inline __host__ __device__ int4 make_int4(int3 a) { + return make_int4(a.x, a.y, a.z, 0); } -inline __host__ __device__ int4 make_int4(int3 a, int w) -{ - return make_int4(a.x, a.y, a.z, w); +inline __host__ __device__ int4 make_int4(int3 a, int w) { + return make_int4(a.x, a.y, a.z, w); } -inline __host__ __device__ int4 make_int4(uint4 a) -{ - return make_int4(int(a.x), int(a.y), int(a.z), int(a.w)); +inline __host__ __device__ int4 make_int4(uint4 a) { + return make_int4(int(a.x), int(a.y), int(a.z), int(a.w)); } -inline __host__ __device__ int4 make_int4(float4 a) -{ - return make_int4(int(a.x), int(a.y), int(a.z), int(a.w)); +inline __host__ __device__ int4 make_int4(float4 a) { + return make_int4(int(a.x), int(a.y), int(a.z), int(a.w)); } -inline __host__ __device__ uint4 make_uint4(uint s) -{ - return make_uint4(s, s, s, s); +inline __host__ __device__ uint4 make_uint4(uint s) { + return make_uint4(s, s, s, s); } -inline __host__ __device__ uint4 make_uint4(uint3 a) -{ - return make_uint4(a.x, a.y, a.z, 0); +inline __host__ __device__ uint4 make_uint4(uint3 a) { + return make_uint4(a.x, a.y, a.z, 0); } -inline __host__ __device__ uint4 make_uint4(uint3 a, uint w) -{ - return make_uint4(a.x, a.y, a.z, w); +inline __host__ __device__ uint4 make_uint4(uint3 a, uint w) { + return make_uint4(a.x, a.y, a.z, w); } -inline __host__ __device__ uint4 make_uint4(int4 a) -{ - return make_uint4(uint(a.x), uint(a.y), uint(a.z), uint(a.w)); +inline __host__ __device__ uint4 make_uint4(int4 a) { + return make_uint4(uint(a.x), uint(a.y), uint(a.z), uint(a.w)); } //////////////////////////////////////////////////////////////////////////////// // negate //////////////////////////////////////////////////////////////////////////////// -inline __host__ __device__ float2 operator-(float2 &a) -{ - return make_float2(-a.x, -a.y); +inline __host__ __device__ float2 operator-(float2 &a) { + return make_float2(-a.x, -a.y); } -inline __host__ __device__ int2 operator-(int2 &a) -{ - return make_int2(-a.x, -a.y); +inline __host__ __device__ int2 operator-(int2 &a) { + return make_int2(-a.x, -a.y); } -inline __host__ __device__ float3 operator-(float3 &a) -{ - return make_float3(-a.x, -a.y, -a.z); +inline __host__ __device__ float3 operator-(float3 &a) { + return make_float3(-a.x, -a.y, -a.z); } -inline __host__ __device__ int3 operator-(int3 &a) -{ - return make_int3(-a.x, -a.y, -a.z); +inline __host__ __device__ int3 operator-(int3 &a) { + return make_int3(-a.x, -a.y, -a.z); } -inline __host__ __device__ float4 operator-(float4 &a) -{ - return make_float4(-a.x, -a.y, -a.z, -a.w); +inline __host__ __device__ float4 operator-(float4 &a) { + return make_float4(-a.x, -a.y, -a.z, -a.w); } -inline __host__ __device__ int4 operator-(int4 &a) -{ - return make_int4(-a.x, -a.y, -a.z, -a.w); +inline __host__ __device__ int4 operator-(int4 &a) { + return make_int4(-a.x, -a.y, -a.z, -a.w); } //////////////////////////////////////////////////////////////////////////////// // addition //////////////////////////////////////////////////////////////////////////////// -inline __host__ __device__ float2 operator+(float2 a, float2 b) -{ - return make_float2(a.x + b.x, a.y + b.y); +inline __host__ __device__ float2 operator+(float2 a, float2 b) { + return make_float2(a.x + b.x, a.y + b.y); } -inline __host__ __device__ void operator+=(float2 &a, float2 b) -{ - a.x += b.x; - a.y += b.y; +inline __host__ __device__ void operator+=(float2 &a, float2 b) { + a.x += b.x; + a.y += b.y; } -inline __host__ __device__ float2 operator+(float2 a, float b) -{ - return make_float2(a.x + b, a.y + b); +inline __host__ __device__ float2 operator+(float2 a, float b) { + return make_float2(a.x + b, a.y + b); } -inline __host__ __device__ float2 operator+(float b, float2 a) -{ - return make_float2(a.x + b, a.y + b); +inline __host__ __device__ float2 operator+(float b, float2 a) { + return make_float2(a.x + b, a.y + b); } -inline __host__ __device__ void operator+=(float2 &a, float b) -{ - a.x += b; - a.y += b; +inline __host__ __device__ void operator+=(float2 &a, float b) { + a.x += b; + a.y += b; } -inline __host__ __device__ int2 operator+(int2 a, int2 b) -{ - return make_int2(a.x + b.x, a.y + b.y); +inline __host__ __device__ int2 operator+(int2 a, int2 b) { + return make_int2(a.x + b.x, a.y + b.y); } -inline __host__ __device__ void operator+=(int2 &a, int2 b) -{ - a.x += b.x; - a.y += b.y; +inline __host__ __device__ void operator+=(int2 &a, int2 b) { + a.x += b.x; + a.y += b.y; } -inline __host__ __device__ int2 operator+(int2 a, int b) -{ - return make_int2(a.x + b, a.y + b); +inline __host__ __device__ int2 operator+(int2 a, int b) { + return make_int2(a.x + b, a.y + b); } -inline __host__ __device__ int2 operator+(int b, int2 a) -{ - return make_int2(a.x + b, a.y + b); +inline __host__ __device__ int2 operator+(int b, int2 a) { + return make_int2(a.x + b, a.y + b); } -inline __host__ __device__ void operator+=(int2 &a, int b) -{ - a.x += b; - a.y += b; +inline __host__ __device__ void operator+=(int2 &a, int b) { + a.x += b; + a.y += b; } -inline __host__ __device__ uint2 operator+(uint2 a, uint2 b) -{ - return make_uint2(a.x + b.x, a.y + b.y); +inline __host__ __device__ uint2 operator+(uint2 a, uint2 b) { + return make_uint2(a.x + b.x, a.y + b.y); } -inline __host__ __device__ void operator+=(uint2 &a, uint2 b) -{ - a.x += b.x; - a.y += b.y; +inline __host__ __device__ void operator+=(uint2 &a, uint2 b) { + a.x += b.x; + a.y += b.y; } -inline __host__ __device__ uint2 operator+(uint2 a, uint b) -{ - return make_uint2(a.x + b, a.y + b); +inline __host__ __device__ uint2 operator+(uint2 a, uint b) { + return make_uint2(a.x + b, a.y + b); } -inline __host__ __device__ uint2 operator+(uint b, uint2 a) -{ - return make_uint2(a.x + b, a.y + b); +inline __host__ __device__ uint2 operator+(uint b, uint2 a) { + return make_uint2(a.x + b, a.y + b); } -inline __host__ __device__ void operator+=(uint2 &a, uint b) -{ - a.x += b; - a.y += b; +inline __host__ __device__ void operator+=(uint2 &a, uint b) { + a.x += b; + a.y += b; } -inline __host__ __device__ float3 operator+(float3 a, float3 b) -{ - return make_float3(a.x + b.x, a.y + b.y, a.z + b.z); +inline __host__ __device__ float3 operator+(float3 a, float3 b) { + return make_float3(a.x + b.x, a.y + b.y, a.z + b.z); } -inline __host__ __device__ void operator+=(float3 &a, float3 b) -{ - a.x += b.x; - a.y += b.y; - a.z += b.z; +inline __host__ __device__ void operator+=(float3 &a, float3 b) { + a.x += b.x; + a.y += b.y; + a.z += b.z; } -inline __host__ __device__ float3 operator+(float3 a, float b) -{ - return make_float3(a.x + b, a.y + b, a.z + b); +inline __host__ __device__ float3 operator+(float3 a, float b) { + return make_float3(a.x + b, a.y + b, a.z + b); } -inline __host__ __device__ void operator+=(float3 &a, float b) -{ - a.x += b; - a.y += b; - a.z += b; +inline __host__ __device__ void operator+=(float3 &a, float b) { + a.x += b; + a.y += b; + a.z += b; } -inline __host__ __device__ int3 operator+(int3 a, int3 b) -{ - return make_int3(a.x + b.x, a.y + b.y, a.z + b.z); +inline __host__ __device__ int3 operator+(int3 a, int3 b) { + return make_int3(a.x + b.x, a.y + b.y, a.z + b.z); } -inline __host__ __device__ void operator+=(int3 &a, int3 b) -{ - a.x += b.x; - a.y += b.y; - a.z += b.z; +inline __host__ __device__ void operator+=(int3 &a, int3 b) { + a.x += b.x; + a.y += b.y; + a.z += b.z; } -inline __host__ __device__ int3 operator+(int3 a, int b) -{ - return make_int3(a.x + b, a.y + b, a.z + b); +inline __host__ __device__ int3 operator+(int3 a, int b) { + return make_int3(a.x + b, a.y + b, a.z + b); } -inline __host__ __device__ void operator+=(int3 &a, int b) -{ - a.x += b; - a.y += b; - a.z += b; +inline __host__ __device__ void operator+=(int3 &a, int b) { + a.x += b; + a.y += b; + a.z += b; } -inline __host__ __device__ uint3 operator+(uint3 a, uint3 b) -{ - return make_uint3(a.x + b.x, a.y + b.y, a.z + b.z); +inline __host__ __device__ uint3 operator+(uint3 a, uint3 b) { + return make_uint3(a.x + b.x, a.y + b.y, a.z + b.z); } -inline __host__ __device__ void operator+=(uint3 &a, uint3 b) -{ - a.x += b.x; - a.y += b.y; - a.z += b.z; +inline __host__ __device__ void operator+=(uint3 &a, uint3 b) { + a.x += b.x; + a.y += b.y; + a.z += b.z; } -inline __host__ __device__ uint3 operator+(uint3 a, uint b) -{ - return make_uint3(a.x + b, a.y + b, a.z + b); -} -inline __host__ __device__ void operator+=(uint3 &a, uint b) -{ - a.x += b; - a.y += b; - a.z += b; +inline __host__ __device__ uint3 operator+(uint3 a, uint b) { + return make_uint3(a.x + b, a.y + b, a.z + b); +} +inline __host__ __device__ void operator+=(uint3 &a, uint b) { + a.x += b; + a.y += b; + a.z += b; +} + +inline __host__ __device__ int3 operator+(int b, int3 a) { + return make_int3(a.x + b, a.y + b, a.z + b); +} +inline __host__ __device__ uint3 operator+(uint b, uint3 a) { + return make_uint3(a.x + b, a.y + b, a.z + b); +} +inline __host__ __device__ float3 operator+(float b, float3 a) { + return make_float3(a.x + b, a.y + b, a.z + b); } -inline __host__ __device__ int3 operator+(int b, int3 a) -{ - return make_int3(a.x + b, a.y + b, a.z + b); +inline __host__ __device__ float4 operator+(float4 a, float4 b) { + return make_float4(a.x + b.x, a.y + b.y, a.z + b.z, a.w + b.w); } -inline __host__ __device__ uint3 operator+(uint b, uint3 a) -{ - return make_uint3(a.x + b, a.y + b, a.z + b); +inline __host__ __device__ void operator+=(float4 &a, float4 b) { + a.x += b.x; + a.y += b.y; + a.z += b.z; + a.w += b.w; } -inline __host__ __device__ float3 operator+(float b, float3 a) -{ - return make_float3(a.x + b, a.y + b, a.z + b); +inline __host__ __device__ float4 operator+(float4 a, float b) { + return make_float4(a.x + b, a.y + b, a.z + b, a.w + b); +} +inline __host__ __device__ float4 operator+(float b, float4 a) { + return make_float4(a.x + b, a.y + b, a.z + b, a.w + b); +} +inline __host__ __device__ void operator+=(float4 &a, float b) { + a.x += b; + a.y += b; + a.z += b; + a.w += b; } -inline __host__ __device__ float4 operator+(float4 a, float4 b) -{ - return make_float4(a.x + b.x, a.y + b.y, a.z + b.z, a.w + b.w); +inline __host__ __device__ int4 operator+(int4 a, int4 b) { + return make_int4(a.x + b.x, a.y + b.y, a.z + b.z, a.w + b.w); +} +inline __host__ __device__ void operator+=(int4 &a, int4 b) { + a.x += b.x; + a.y += b.y; + a.z += b.z; + a.w += b.w; +} +inline __host__ __device__ int4 operator+(int4 a, int b) { + return make_int4(a.x + b, a.y + b, a.z + b, a.w + b); +} +inline __host__ __device__ int4 operator+(int b, int4 a) { + return make_int4(a.x + b, a.y + b, a.z + b, a.w + b); } -inline __host__ __device__ void operator+=(float4 &a, float4 b) -{ - a.x += b.x; - a.y += b.y; - a.z += b.z; - a.w += b.w; +inline __host__ __device__ void operator+=(int4 &a, int b) { + a.x += b; + a.y += b; + a.z += b; + a.w += b; } -inline __host__ __device__ float4 operator+(float4 a, float b) -{ - return make_float4(a.x + b, a.y + b, a.z + b, a.w + b); + +inline __host__ __device__ uint4 operator+(uint4 a, uint4 b) { + return make_uint4(a.x + b.x, a.y + b.y, a.z + b.z, a.w + b.w); +} +inline __host__ __device__ void operator+=(uint4 &a, uint4 b) { + a.x += b.x; + a.y += b.y; + a.z += b.z; + a.w += b.w; } -inline __host__ __device__ float4 operator+(float b, float4 a) -{ - return make_float4(a.x + b, a.y + b, a.z + b, a.w + b); +inline __host__ __device__ uint4 operator+(uint4 a, uint b) { + return make_uint4(a.x + b, a.y + b, a.z + b, a.w + b); } -inline __host__ __device__ void operator+=(float4 &a, float b) -{ - a.x += b; - a.y += b; - a.z += b; - a.w += b; -} - -inline __host__ __device__ int4 operator+(int4 a, int4 b) -{ - return make_int4(a.x + b.x, a.y + b.y, a.z + b.z, a.w + b.w); -} -inline __host__ __device__ void operator+=(int4 &a, int4 b) -{ - a.x += b.x; - a.y += b.y; - a.z += b.z; - a.w += b.w; -} -inline __host__ __device__ int4 operator+(int4 a, int b) -{ - return make_int4(a.x + b, a.y + b, a.z + b, a.w + b); -} -inline __host__ __device__ int4 operator+(int b, int4 a) -{ - return make_int4(a.x + b, a.y + b, a.z + b, a.w + b); -} -inline __host__ __device__ void operator+=(int4 &a, int b) -{ - a.x += b; - a.y += b; - a.z += b; - a.w += b; -} - -inline __host__ __device__ uint4 operator+(uint4 a, uint4 b) -{ - return make_uint4(a.x + b.x, a.y + b.y, a.z + b.z, a.w + b.w); -} -inline __host__ __device__ void operator+=(uint4 &a, uint4 b) -{ - a.x += b.x; - a.y += b.y; - a.z += b.z; - a.w += b.w; +inline __host__ __device__ uint4 operator+(uint b, uint4 a) { + return make_uint4(a.x + b, a.y + b, a.z + b, a.w + b); } -inline __host__ __device__ uint4 operator+(uint4 a, uint b) -{ - return make_uint4(a.x + b, a.y + b, a.z + b, a.w + b); -} -inline __host__ __device__ uint4 operator+(uint b, uint4 a) -{ - return make_uint4(a.x + b, a.y + b, a.z + b, a.w + b); -} -inline __host__ __device__ void operator+=(uint4 &a, uint b) -{ - a.x += b; - a.y += b; - a.z += b; - a.w += b; +inline __host__ __device__ void operator+=(uint4 &a, uint b) { + a.x += b; + a.y += b; + a.z += b; + a.w += b; } //////////////////////////////////////////////////////////////////////////////// // subtract //////////////////////////////////////////////////////////////////////////////// -inline __host__ __device__ float2 operator-(float2 a, float2 b) -{ - return make_float2(a.x - b.x, a.y - b.y); +inline __host__ __device__ float2 operator-(float2 a, float2 b) { + return make_float2(a.x - b.x, a.y - b.y); +} +inline __host__ __device__ void operator-=(float2 &a, float2 b) { + a.x -= b.x; + a.y -= b.y; +} +inline __host__ __device__ float2 operator-(float2 a, float b) { + return make_float2(a.x - b, a.y - b); +} +inline __host__ __device__ float2 operator-(float b, float2 a) { + return make_float2(b - a.x, b - a.y); +} +inline __host__ __device__ void operator-=(float2 &a, float b) { + a.x -= b; + a.y -= b; +} + +inline __host__ __device__ int2 operator-(int2 a, int2 b) { + return make_int2(a.x - b.x, a.y - b.y); +} +inline __host__ __device__ void operator-=(int2 &a, int2 b) { + a.x -= b.x; + a.y -= b.y; +} +inline __host__ __device__ int2 operator-(int2 a, int b) { + return make_int2(a.x - b, a.y - b); +} +inline __host__ __device__ int2 operator-(int b, int2 a) { + return make_int2(b - a.x, b - a.y); +} +inline __host__ __device__ void operator-=(int2 &a, int b) { + a.x -= b; + a.y -= b; +} + +inline __host__ __device__ uint2 operator-(uint2 a, uint2 b) { + return make_uint2(a.x - b.x, a.y - b.y); +} +inline __host__ __device__ void operator-=(uint2 &a, uint2 b) { + a.x -= b.x; + a.y -= b.y; +} +inline __host__ __device__ uint2 operator-(uint2 a, uint b) { + return make_uint2(a.x - b, a.y - b); +} +inline __host__ __device__ uint2 operator-(uint b, uint2 a) { + return make_uint2(b - a.x, b - a.y); +} +inline __host__ __device__ void operator-=(uint2 &a, uint b) { + a.x -= b; + a.y -= b; +} + +inline __host__ __device__ float3 operator-(float3 a, float3 b) { + return make_float3(a.x - b.x, a.y - b.y, a.z - b.z); } -inline __host__ __device__ void operator-=(float2 &a, float2 b) -{ - a.x -= b.x; - a.y -= b.y; +inline __host__ __device__ void operator-=(float3 &a, float3 b) { + a.x -= b.x; + a.y -= b.y; + a.z -= b.z; } -inline __host__ __device__ float2 operator-(float2 a, float b) -{ - return make_float2(a.x - b, a.y - b); +inline __host__ __device__ float3 operator-(float3 a, float b) { + return make_float3(a.x - b, a.y - b, a.z - b); } -inline __host__ __device__ float2 operator-(float b, float2 a) -{ - return make_float2(b - a.x, b - a.y); +inline __host__ __device__ float3 operator-(float b, float3 a) { + return make_float3(b - a.x, b - a.y, b - a.z); } -inline __host__ __device__ void operator-=(float2 &a, float b) -{ - a.x -= b; - a.y -= b; +inline __host__ __device__ void operator-=(float3 &a, float b) { + a.x -= b; + a.y -= b; + a.z -= b; } -inline __host__ __device__ int2 operator-(int2 a, int2 b) -{ - return make_int2(a.x - b.x, a.y - b.y); +inline __host__ __device__ int3 operator-(int3 a, int3 b) { + return make_int3(a.x - b.x, a.y - b.y, a.z - b.z); } -inline __host__ __device__ void operator-=(int2 &a, int2 b) -{ - a.x -= b.x; - a.y -= b.y; +inline __host__ __device__ void operator-=(int3 &a, int3 b) { + a.x -= b.x; + a.y -= b.y; + a.z -= b.z; } -inline __host__ __device__ int2 operator-(int2 a, int b) -{ - return make_int2(a.x - b, a.y - b); +inline __host__ __device__ int3 operator-(int3 a, int b) { + return make_int3(a.x - b, a.y - b, a.z - b); } -inline __host__ __device__ int2 operator-(int b, int2 a) -{ - return make_int2(b - a.x, b - a.y); +inline __host__ __device__ int3 operator-(int b, int3 a) { + return make_int3(b - a.x, b - a.y, b - a.z); } -inline __host__ __device__ void operator-=(int2 &a, int b) -{ - a.x -= b; - a.y -= b; +inline __host__ __device__ void operator-=(int3 &a, int b) { + a.x -= b; + a.y -= b; + a.z -= b; } -inline __host__ __device__ uint2 operator-(uint2 a, uint2 b) -{ - return make_uint2(a.x - b.x, a.y - b.y); +inline __host__ __device__ uint3 operator-(uint3 a, uint3 b) { + return make_uint3(a.x - b.x, a.y - b.y, a.z - b.z); } -inline __host__ __device__ void operator-=(uint2 &a, uint2 b) -{ - a.x -= b.x; - a.y -= b.y; +inline __host__ __device__ void operator-=(uint3 &a, uint3 b) { + a.x -= b.x; + a.y -= b.y; + a.z -= b.z; } -inline __host__ __device__ uint2 operator-(uint2 a, uint b) -{ - return make_uint2(a.x - b, a.y - b); +inline __host__ __device__ uint3 operator-(uint3 a, uint b) { + return make_uint3(a.x - b, a.y - b, a.z - b); } -inline __host__ __device__ uint2 operator-(uint b, uint2 a) -{ - return make_uint2(b - a.x, b - a.y); -} -inline __host__ __device__ void operator-=(uint2 &a, uint b) -{ - a.x -= b; - a.y -= b; +inline __host__ __device__ uint3 operator-(uint b, uint3 a) { + return make_uint3(b - a.x, b - a.y, b - a.z); +} +inline __host__ __device__ void operator-=(uint3 &a, uint b) { + a.x -= b; + a.y -= b; + a.z -= b; +} + +inline __host__ __device__ float4 operator-(float4 a, float4 b) { + return make_float4(a.x - b.x, a.y - b.y, a.z - b.z, a.w - b.w); +} +inline __host__ __device__ void operator-=(float4 &a, float4 b) { + a.x -= b.x; + a.y -= b.y; + a.z -= b.z; + a.w -= b.w; +} +inline __host__ __device__ float4 operator-(float4 a, float b) { + return make_float4(a.x - b, a.y - b, a.z - b, a.w - b); +} +inline __host__ __device__ void operator-=(float4 &a, float b) { + a.x -= b; + a.y -= b; + a.z -= b; + a.w -= b; } -inline __host__ __device__ float3 operator-(float3 a, float3 b) -{ - return make_float3(a.x - b.x, a.y - b.y, a.z - b.z); -} -inline __host__ __device__ void operator-=(float3 &a, float3 b) -{ - a.x -= b.x; - a.y -= b.y; - a.z -= b.z; -} -inline __host__ __device__ float3 operator-(float3 a, float b) -{ - return make_float3(a.x - b, a.y - b, a.z - b); -} -inline __host__ __device__ float3 operator-(float b, float3 a) -{ - return make_float3(b - a.x, b - a.y, b - a.z); -} -inline __host__ __device__ void operator-=(float3 &a, float b) -{ - a.x -= b; - a.y -= b; - a.z -= b; +inline __host__ __device__ int4 operator-(int4 a, int4 b) { + return make_int4(a.x - b.x, a.y - b.y, a.z - b.z, a.w - b.w); +} +inline __host__ __device__ void operator-=(int4 &a, int4 b) { + a.x -= b.x; + a.y -= b.y; + a.z -= b.z; + a.w -= b.w; +} +inline __host__ __device__ int4 operator-(int4 a, int b) { + return make_int4(a.x - b, a.y - b, a.z - b, a.w - b); +} +inline __host__ __device__ int4 operator-(int b, int4 a) { + return make_int4(b - a.x, b - a.y, b - a.z, b - a.w); +} +inline __host__ __device__ void operator-=(int4 &a, int b) { + a.x -= b; + a.y -= b; + a.z -= b; + a.w -= b; } -inline __host__ __device__ int3 operator-(int3 a, int3 b) -{ - return make_int3(a.x - b.x, a.y - b.y, a.z - b.z); +inline __host__ __device__ uint4 operator-(uint4 a, uint4 b) { + return make_uint4(a.x - b.x, a.y - b.y, a.z - b.z, a.w - b.w); } -inline __host__ __device__ void operator-=(int3 &a, int3 b) -{ - a.x -= b.x; - a.y -= b.y; - a.z -= b.z; -} -inline __host__ __device__ int3 operator-(int3 a, int b) -{ - return make_int3(a.x - b, a.y - b, a.z - b); -} -inline __host__ __device__ int3 operator-(int b, int3 a) -{ - return make_int3(b - a.x, b - a.y, b - a.z); -} -inline __host__ __device__ void operator-=(int3 &a, int b) -{ - a.x -= b; - a.y -= b; - a.z -= b; -} - -inline __host__ __device__ uint3 operator-(uint3 a, uint3 b) -{ - return make_uint3(a.x - b.x, a.y - b.y, a.z - b.z); -} -inline __host__ __device__ void operator-=(uint3 &a, uint3 b) -{ - a.x -= b.x; - a.y -= b.y; - a.z -= b.z; -} -inline __host__ __device__ uint3 operator-(uint3 a, uint b) -{ - return make_uint3(a.x - b, a.y - b, a.z - b); -} -inline __host__ __device__ uint3 operator-(uint b, uint3 a) -{ - return make_uint3(b - a.x, b - a.y, b - a.z); -} -inline __host__ __device__ void operator-=(uint3 &a, uint b) -{ - a.x -= b; - a.y -= b; - a.z -= b; -} - -inline __host__ __device__ float4 operator-(float4 a, float4 b) -{ - return make_float4(a.x - b.x, a.y - b.y, a.z - b.z, a.w - b.w); -} -inline __host__ __device__ void operator-=(float4 &a, float4 b) -{ - a.x -= b.x; - a.y -= b.y; - a.z -= b.z; - a.w -= b.w; -} -inline __host__ __device__ float4 operator-(float4 a, float b) -{ - return make_float4(a.x - b, a.y - b, a.z - b, a.w - b); -} -inline __host__ __device__ void operator-=(float4 &a, float b) -{ - a.x -= b; - a.y -= b; - a.z -= b; - a.w -= b; -} - -inline __host__ __device__ int4 operator-(int4 a, int4 b) -{ - return make_int4(a.x - b.x, a.y - b.y, a.z - b.z, a.w - b.w); -} -inline __host__ __device__ void operator-=(int4 &a, int4 b) -{ - a.x -= b.x; - a.y -= b.y; - a.z -= b.z; - a.w -= b.w; -} -inline __host__ __device__ int4 operator-(int4 a, int b) -{ - return make_int4(a.x - b, a.y - b, a.z - b, a.w - b); -} -inline __host__ __device__ int4 operator-(int b, int4 a) -{ - return make_int4(b - a.x, b - a.y, b - a.z, b - a.w); -} -inline __host__ __device__ void operator-=(int4 &a, int b) -{ - a.x -= b; - a.y -= b; - a.z -= b; - a.w -= b; -} - -inline __host__ __device__ uint4 operator-(uint4 a, uint4 b) -{ - return make_uint4(a.x - b.x, a.y - b.y, a.z - b.z, a.w - b.w); -} -inline __host__ __device__ void operator-=(uint4 &a, uint4 b) -{ - a.x -= b.x; - a.y -= b.y; - a.z -= b.z; - a.w -= b.w; -} -inline __host__ __device__ uint4 operator-(uint4 a, uint b) -{ - return make_uint4(a.x - b, a.y - b, a.z - b, a.w - b); -} -inline __host__ __device__ uint4 operator-(uint b, uint4 a) -{ - return make_uint4(b - a.x, b - a.y, b - a.z, b - a.w); -} -inline __host__ __device__ void operator-=(uint4 &a, uint b) -{ - a.x -= b; - a.y -= b; - a.z -= b; - a.w -= b; +inline __host__ __device__ void operator-=(uint4 &a, uint4 b) { + a.x -= b.x; + a.y -= b.y; + a.z -= b.z; + a.w -= b.w; +} +inline __host__ __device__ uint4 operator-(uint4 a, uint b) { + return make_uint4(a.x - b, a.y - b, a.z - b, a.w - b); +} +inline __host__ __device__ uint4 operator-(uint b, uint4 a) { + return make_uint4(b - a.x, b - a.y, b - a.z, b - a.w); +} +inline __host__ __device__ void operator-=(uint4 &a, uint b) { + a.x -= b; + a.y -= b; + a.z -= b; + a.w -= b; } //////////////////////////////////////////////////////////////////////////////// // multiply //////////////////////////////////////////////////////////////////////////////// -inline __host__ __device__ float2 operator*(float2 a, float2 b) -{ - return make_float2(a.x * b.x, a.y * b.y); +inline __host__ __device__ float2 operator*(float2 a, float2 b) { + return make_float2(a.x * b.x, a.y * b.y); +} +inline __host__ __device__ void operator*=(float2 &a, float2 b) { + a.x *= b.x; + a.y *= b.y; +} +inline __host__ __device__ float2 operator*(float2 a, float b) { + return make_float2(a.x * b, a.y * b); +} +inline __host__ __device__ float2 operator*(float b, float2 a) { + return make_float2(b * a.x, b * a.y); +} +inline __host__ __device__ void operator*=(float2 &a, float b) { + a.x *= b; + a.y *= b; +} + +inline __host__ __device__ int2 operator*(int2 a, int2 b) { + return make_int2(a.x * b.x, a.y * b.y); +} +inline __host__ __device__ void operator*=(int2 &a, int2 b) { + a.x *= b.x; + a.y *= b.y; +} +inline __host__ __device__ int2 operator*(int2 a, int b) { + return make_int2(a.x * b, a.y * b); +} +inline __host__ __device__ int2 operator*(int b, int2 a) { + return make_int2(b * a.x, b * a.y); +} +inline __host__ __device__ void operator*=(int2 &a, int b) { + a.x *= b; + a.y *= b; +} + +inline __host__ __device__ uint2 operator*(uint2 a, uint2 b) { + return make_uint2(a.x * b.x, a.y * b.y); +} +inline __host__ __device__ void operator*=(uint2 &a, uint2 b) { + a.x *= b.x; + a.y *= b.y; +} +inline __host__ __device__ uint2 operator*(uint2 a, uint b) { + return make_uint2(a.x * b, a.y * b); +} +inline __host__ __device__ uint2 operator*(uint b, uint2 a) { + return make_uint2(b * a.x, b * a.y); +} +inline __host__ __device__ void operator*=(uint2 &a, uint b) { + a.x *= b; + a.y *= b; +} + +inline __host__ __device__ float3 operator*(float3 a, float3 b) { + return make_float3(a.x * b.x, a.y * b.y, a.z * b.z); +} +inline __host__ __device__ void operator*=(float3 &a, float3 b) { + a.x *= b.x; + a.y *= b.y; + a.z *= b.z; +} +inline __host__ __device__ float3 operator*(float3 a, float b) { + return make_float3(a.x * b, a.y * b, a.z * b); +} +inline __host__ __device__ float3 operator*(float b, float3 a) { + return make_float3(b * a.x, b * a.y, b * a.z); +} +inline __host__ __device__ void operator*=(float3 &a, float b) { + a.x *= b; + a.y *= b; + a.z *= b; +} + +inline __host__ __device__ int3 operator*(int3 a, int3 b) { + return make_int3(a.x * b.x, a.y * b.y, a.z * b.z); +} +inline __host__ __device__ void operator*=(int3 &a, int3 b) { + a.x *= b.x; + a.y *= b.y; + a.z *= b.z; +} +inline __host__ __device__ int3 operator*(int3 a, int b) { + return make_int3(a.x * b, a.y * b, a.z * b); +} +inline __host__ __device__ int3 operator*(int b, int3 a) { + return make_int3(b * a.x, b * a.y, b * a.z); +} +inline __host__ __device__ void operator*=(int3 &a, int b) { + a.x *= b; + a.y *= b; + a.z *= b; +} + +inline __host__ __device__ uint3 operator*(uint3 a, uint3 b) { + return make_uint3(a.x * b.x, a.y * b.y, a.z * b.z); } -inline __host__ __device__ void operator*=(float2 &a, float2 b) -{ - a.x *= b.x; - a.y *= b.y; +inline __host__ __device__ void operator*=(uint3 &a, uint3 b) { + a.x *= b.x; + a.y *= b.y; + a.z *= b.z; } -inline __host__ __device__ float2 operator*(float2 a, float b) -{ - return make_float2(a.x * b, a.y * b); +inline __host__ __device__ uint3 operator*(uint3 a, uint b) { + return make_uint3(a.x * b, a.y * b, a.z * b); } -inline __host__ __device__ float2 operator*(float b, float2 a) -{ - return make_float2(b * a.x, b * a.y); +inline __host__ __device__ uint3 operator*(uint b, uint3 a) { + return make_uint3(b * a.x, b * a.y, b * a.z); } -inline __host__ __device__ void operator*=(float2 &a, float b) -{ - a.x *= b; - a.y *= b; +inline __host__ __device__ void operator*=(uint3 &a, uint b) { + a.x *= b; + a.y *= b; + a.z *= b; } -inline __host__ __device__ int2 operator*(int2 a, int2 b) -{ - return make_int2(a.x * b.x, a.y * b.y); +inline __host__ __device__ float4 operator*(float4 a, float4 b) { + return make_float4(a.x * b.x, a.y * b.y, a.z * b.z, a.w * b.w); } -inline __host__ __device__ void operator*=(int2 &a, int2 b) -{ - a.x *= b.x; - a.y *= b.y; +inline __host__ __device__ void operator*=(float4 &a, float4 b) { + a.x *= b.x; + a.y *= b.y; + a.z *= b.z; + a.w *= b.w; } -inline __host__ __device__ int2 operator*(int2 a, int b) -{ - return make_int2(a.x * b, a.y * b); +inline __host__ __device__ float4 operator*(float4 a, float b) { + return make_float4(a.x * b, a.y * b, a.z * b, a.w * b); } -inline __host__ __device__ int2 operator*(int b, int2 a) -{ - return make_int2(b * a.x, b * a.y); +inline __host__ __device__ float4 operator*(float b, float4 a) { + return make_float4(b * a.x, b * a.y, b * a.z, b * a.w); } -inline __host__ __device__ void operator*=(int2 &a, int b) -{ - a.x *= b; - a.y *= b; +inline __host__ __device__ void operator*=(float4 &a, float b) { + a.x *= b; + a.y *= b; + a.z *= b; + a.w *= b; } -inline __host__ __device__ uint2 operator*(uint2 a, uint2 b) -{ - return make_uint2(a.x * b.x, a.y * b.y); +inline __host__ __device__ int4 operator*(int4 a, int4 b) { + return make_int4(a.x * b.x, a.y * b.y, a.z * b.z, a.w * b.w); +} +inline __host__ __device__ void operator*=(int4 &a, int4 b) { + a.x *= b.x; + a.y *= b.y; + a.z *= b.z; + a.w *= b.w; +} +inline __host__ __device__ int4 operator*(int4 a, int b) { + return make_int4(a.x * b, a.y * b, a.z * b, a.w * b); } -inline __host__ __device__ void operator*=(uint2 &a, uint2 b) -{ - a.x *= b.x; - a.y *= b.y; +inline __host__ __device__ int4 operator*(int b, int4 a) { + return make_int4(b * a.x, b * a.y, b * a.z, b * a.w); } -inline __host__ __device__ uint2 operator*(uint2 a, uint b) -{ - return make_uint2(a.x * b, a.y * b); +inline __host__ __device__ void operator*=(int4 &a, int b) { + a.x *= b; + a.y *= b; + a.z *= b; + a.w *= b; } -inline __host__ __device__ uint2 operator*(uint b, uint2 a) -{ - return make_uint2(b * a.x, b * a.y); + +inline __host__ __device__ uint4 operator*(uint4 a, uint4 b) { + return make_uint4(a.x * b.x, a.y * b.y, a.z * b.z, a.w * b.w); +} +inline __host__ __device__ void operator*=(uint4 &a, uint4 b) { + a.x *= b.x; + a.y *= b.y; + a.z *= b.z; + a.w *= b.w; } -inline __host__ __device__ void operator*=(uint2 &a, uint b) -{ - a.x *= b; - a.y *= b; -} - -inline __host__ __device__ float3 operator*(float3 a, float3 b) -{ - return make_float3(a.x * b.x, a.y * b.y, a.z * b.z); +inline __host__ __device__ uint4 operator*(uint4 a, uint b) { + return make_uint4(a.x * b, a.y * b, a.z * b, a.w * b); } -inline __host__ __device__ void operator*=(float3 &a, float3 b) -{ - a.x *= b.x; - a.y *= b.y; - a.z *= b.z; -} -inline __host__ __device__ float3 operator*(float3 a, float b) -{ - return make_float3(a.x * b, a.y * b, a.z * b); -} -inline __host__ __device__ float3 operator*(float b, float3 a) -{ - return make_float3(b * a.x, b * a.y, b * a.z); -} -inline __host__ __device__ void operator*=(float3 &a, float b) -{ - a.x *= b; - a.y *= b; - a.z *= b; -} - -inline __host__ __device__ int3 operator*(int3 a, int3 b) -{ - return make_int3(a.x * b.x, a.y * b.y, a.z * b.z); +inline __host__ __device__ uint4 operator*(uint b, uint4 a) { + return make_uint4(b * a.x, b * a.y, b * a.z, b * a.w); } -inline __host__ __device__ void operator*=(int3 &a, int3 b) -{ - a.x *= b.x; - a.y *= b.y; - a.z *= b.z; -} -inline __host__ __device__ int3 operator*(int3 a, int b) -{ - return make_int3(a.x * b, a.y * b, a.z * b); -} -inline __host__ __device__ int3 operator*(int b, int3 a) -{ - return make_int3(b * a.x, b * a.y, b * a.z); -} -inline __host__ __device__ void operator*=(int3 &a, int b) -{ - a.x *= b; - a.y *= b; - a.z *= b; -} - -inline __host__ __device__ uint3 operator*(uint3 a, uint3 b) -{ - return make_uint3(a.x * b.x, a.y * b.y, a.z * b.z); -} -inline __host__ __device__ void operator*=(uint3 &a, uint3 b) -{ - a.x *= b.x; - a.y *= b.y; - a.z *= b.z; -} -inline __host__ __device__ uint3 operator*(uint3 a, uint b) -{ - return make_uint3(a.x * b, a.y * b, a.z * b); -} -inline __host__ __device__ uint3 operator*(uint b, uint3 a) -{ - return make_uint3(b * a.x, b * a.y, b * a.z); -} -inline __host__ __device__ void operator*=(uint3 &a, uint b) -{ - a.x *= b; - a.y *= b; - a.z *= b; -} - -inline __host__ __device__ float4 operator*(float4 a, float4 b) -{ - return make_float4(a.x * b.x, a.y * b.y, a.z * b.z, a.w * b.w); -} -inline __host__ __device__ void operator*=(float4 &a, float4 b) -{ - a.x *= b.x; - a.y *= b.y; - a.z *= b.z; - a.w *= b.w; -} -inline __host__ __device__ float4 operator*(float4 a, float b) -{ - return make_float4(a.x * b, a.y * b, a.z * b, a.w * b); -} -inline __host__ __device__ float4 operator*(float b, float4 a) -{ - return make_float4(b * a.x, b * a.y, b * a.z, b * a.w); -} -inline __host__ __device__ void operator*=(float4 &a, float b) -{ - a.x *= b; - a.y *= b; - a.z *= b; - a.w *= b; -} - -inline __host__ __device__ int4 operator*(int4 a, int4 b) -{ - return make_int4(a.x * b.x, a.y * b.y, a.z * b.z, a.w * b.w); -} -inline __host__ __device__ void operator*=(int4 &a, int4 b) -{ - a.x *= b.x; - a.y *= b.y; - a.z *= b.z; - a.w *= b.w; -} -inline __host__ __device__ int4 operator*(int4 a, int b) -{ - return make_int4(a.x * b, a.y * b, a.z * b, a.w * b); -} -inline __host__ __device__ int4 operator*(int b, int4 a) -{ - return make_int4(b * a.x, b * a.y, b * a.z, b * a.w); -} -inline __host__ __device__ void operator*=(int4 &a, int b) -{ - a.x *= b; - a.y *= b; - a.z *= b; - a.w *= b; -} - -inline __host__ __device__ uint4 operator*(uint4 a, uint4 b) -{ - return make_uint4(a.x * b.x, a.y * b.y, a.z * b.z, a.w * b.w); -} -inline __host__ __device__ void operator*=(uint4 &a, uint4 b) -{ - a.x *= b.x; - a.y *= b.y; - a.z *= b.z; - a.w *= b.w; -} -inline __host__ __device__ uint4 operator*(uint4 a, uint b) -{ - return make_uint4(a.x * b, a.y * b, a.z * b, a.w * b); -} -inline __host__ __device__ uint4 operator*(uint b, uint4 a) -{ - return make_uint4(b * a.x, b * a.y, b * a.z, b * a.w); -} -inline __host__ __device__ void operator*=(uint4 &a, uint b) -{ - a.x *= b; - a.y *= b; - a.z *= b; - a.w *= b; +inline __host__ __device__ void operator*=(uint4 &a, uint b) { + a.x *= b; + a.y *= b; + a.z *= b; + a.w *= b; } //////////////////////////////////////////////////////////////////////////////// // divide //////////////////////////////////////////////////////////////////////////////// -inline __host__ __device__ float2 operator/(float2 a, float2 b) -{ - return make_float2(a.x / b.x, a.y / b.y); +inline __host__ __device__ float2 operator/(float2 a, float2 b) { + return make_float2(a.x / b.x, a.y / b.y); } -inline __host__ __device__ void operator/=(float2 &a, float2 b) -{ - a.x /= b.x; - a.y /= b.y; +inline __host__ __device__ void operator/=(float2 &a, float2 b) { + a.x /= b.x; + a.y /= b.y; } -inline __host__ __device__ float2 operator/(float2 a, float b) -{ - return make_float2(a.x / b, a.y / b); +inline __host__ __device__ float2 operator/(float2 a, float b) { + return make_float2(a.x / b, a.y / b); } -inline __host__ __device__ void operator/=(float2 &a, float b) -{ - a.x /= b; - a.y /= b; +inline __host__ __device__ void operator/=(float2 &a, float b) { + a.x /= b; + a.y /= b; } -inline __host__ __device__ float2 operator/(float b, float2 a) -{ - return make_float2(b / a.x, b / a.y); +inline __host__ __device__ float2 operator/(float b, float2 a) { + return make_float2(b / a.x, b / a.y); } -inline __host__ __device__ float3 operator/(float3 a, float3 b) -{ - return make_float3(a.x / b.x, a.y / b.y, a.z / b.z); +inline __host__ __device__ float3 operator/(float3 a, float3 b) { + return make_float3(a.x / b.x, a.y / b.y, a.z / b.z); } -inline __host__ __device__ void operator/=(float3 &a, float3 b) -{ - a.x /= b.x; - a.y /= b.y; - a.z /= b.z; +inline __host__ __device__ void operator/=(float3 &a, float3 b) { + a.x /= b.x; + a.y /= b.y; + a.z /= b.z; } -inline __host__ __device__ float3 operator/(float3 a, float b) -{ - return make_float3(a.x / b, a.y / b, a.z / b); +inline __host__ __device__ float3 operator/(float3 a, float b) { + return make_float3(a.x / b, a.y / b, a.z / b); } -inline __host__ __device__ void operator/=(float3 &a, float b) -{ - a.x /= b; - a.y /= b; - a.z /= b; +inline __host__ __device__ void operator/=(float3 &a, float b) { + a.x /= b; + a.y /= b; + a.z /= b; } -inline __host__ __device__ float3 operator/(float b, float3 a) -{ - return make_float3(b / a.x, b / a.y, b / a.z); +inline __host__ __device__ float3 operator/(float b, float3 a) { + return make_float3(b / a.x, b / a.y, b / a.z); } -inline __host__ __device__ float4 operator/(float4 a, float4 b) -{ - return make_float4(a.x / b.x, a.y / b.y, a.z / b.z, a.w / b.w); +inline __host__ __device__ float4 operator/(float4 a, float4 b) { + return make_float4(a.x / b.x, a.y / b.y, a.z / b.z, a.w / b.w); } -inline __host__ __device__ void operator/=(float4 &a, float4 b) -{ - a.x /= b.x; - a.y /= b.y; - a.z /= b.z; - a.w /= b.w; +inline __host__ __device__ void operator/=(float4 &a, float4 b) { + a.x /= b.x; + a.y /= b.y; + a.z /= b.z; + a.w /= b.w; } -inline __host__ __device__ float4 operator/(float4 a, float b) -{ - return make_float4(a.x / b, a.y / b, a.z / b, a.w / b); +inline __host__ __device__ float4 operator/(float4 a, float b) { + return make_float4(a.x / b, a.y / b, a.z / b, a.w / b); } -inline __host__ __device__ void operator/=(float4 &a, float b) -{ - a.x /= b; - a.y /= b; - a.z /= b; - a.w /= b; +inline __host__ __device__ void operator/=(float4 &a, float b) { + a.x /= b; + a.y /= b; + a.z /= b; + a.w /= b; } -inline __host__ __device__ float4 operator/(float b, float4 a) -{ - return make_float4(b / a.x, b / a.y, b / a.z, b / a.w); +inline __host__ __device__ float4 operator/(float b, float4 a) { + return make_float4(b / a.x, b / a.y, b / a.z, b / a.w); } //////////////////////////////////////////////////////////////////////////////// // min //////////////////////////////////////////////////////////////////////////////// -inline __host__ __device__ float2 fminf(float2 a, float2 b) -{ - return make_float2(fminf(a.x,b.x), fminf(a.y,b.y)); +inline __host__ __device__ float2 fminf(float2 a, float2 b) { + return make_float2(fminf(a.x, b.x), fminf(a.y, b.y)); } -inline __host__ __device__ float3 fminf(float3 a, float3 b) -{ - return make_float3(fminf(a.x,b.x), fminf(a.y,b.y), fminf(a.z,b.z)); +inline __host__ __device__ float3 fminf(float3 a, float3 b) { + return make_float3(fminf(a.x, b.x), fminf(a.y, b.y), fminf(a.z, b.z)); } -inline __host__ __device__ float4 fminf(float4 a, float4 b) -{ - return make_float4(fminf(a.x,b.x), fminf(a.y,b.y), fminf(a.z,b.z), fminf(a.w,b.w)); +inline __host__ __device__ float4 fminf(float4 a, float4 b) { + return make_float4(fminf(a.x, b.x), fminf(a.y, b.y), fminf(a.z, b.z), fminf(a.w, b.w)); } -inline __host__ __device__ int2 min(int2 a, int2 b) -{ - return make_int2(min(a.x,b.x), min(a.y,b.y)); +inline __host__ __device__ int2 min(int2 a, int2 b) { + return make_int2(min(a.x, b.x), min(a.y, b.y)); } -inline __host__ __device__ int3 min(int3 a, int3 b) -{ - return make_int3(min(a.x,b.x), min(a.y,b.y), min(a.z,b.z)); +inline __host__ __device__ int3 min(int3 a, int3 b) { + return make_int3(min(a.x, b.x), min(a.y, b.y), min(a.z, b.z)); } -inline __host__ __device__ int4 min(int4 a, int4 b) -{ - return make_int4(min(a.x,b.x), min(a.y,b.y), min(a.z,b.z), min(a.w,b.w)); +inline __host__ __device__ int4 min(int4 a, int4 b) { + return make_int4(min(a.x, b.x), min(a.y, b.y), min(a.z, b.z), min(a.w, b.w)); } -inline __host__ __device__ uint2 min(uint2 a, uint2 b) -{ - return make_uint2(min(a.x,b.x), min(a.y,b.y)); +inline __host__ __device__ uint2 min(uint2 a, uint2 b) { + return make_uint2(min(a.x, b.x), min(a.y, b.y)); } -inline __host__ __device__ uint3 min(uint3 a, uint3 b) -{ - return make_uint3(min(a.x,b.x), min(a.y,b.y), min(a.z,b.z)); +inline __host__ __device__ uint3 min(uint3 a, uint3 b) { + return make_uint3(min(a.x, b.x), min(a.y, b.y), min(a.z, b.z)); } -inline __host__ __device__ uint4 min(uint4 a, uint4 b) -{ - return make_uint4(min(a.x,b.x), min(a.y,b.y), min(a.z,b.z), min(a.w,b.w)); +inline __host__ __device__ uint4 min(uint4 a, uint4 b) { + return make_uint4(min(a.x, b.x), min(a.y, b.y), min(a.z, b.z), min(a.w, b.w)); } //////////////////////////////////////////////////////////////////////////////// // max //////////////////////////////////////////////////////////////////////////////// -inline __host__ __device__ float2 fmaxf(float2 a, float2 b) -{ - return make_float2(fmaxf(a.x,b.x), fmaxf(a.y,b.y)); +inline __host__ __device__ float2 fmaxf(float2 a, float2 b) { + return make_float2(fmaxf(a.x, b.x), fmaxf(a.y, b.y)); } -inline __host__ __device__ float3 fmaxf(float3 a, float3 b) -{ - return make_float3(fmaxf(a.x,b.x), fmaxf(a.y,b.y), fmaxf(a.z,b.z)); +inline __host__ __device__ float3 fmaxf(float3 a, float3 b) { + return make_float3(fmaxf(a.x, b.x), fmaxf(a.y, b.y), fmaxf(a.z, b.z)); } -inline __host__ __device__ float4 fmaxf(float4 a, float4 b) -{ - return make_float4(fmaxf(a.x,b.x), fmaxf(a.y,b.y), fmaxf(a.z,b.z), fmaxf(a.w,b.w)); +inline __host__ __device__ float4 fmaxf(float4 a, float4 b) { + return make_float4(fmaxf(a.x, b.x), fmaxf(a.y, b.y), fmaxf(a.z, b.z), fmaxf(a.w, b.w)); } -inline __host__ __device__ int2 max(int2 a, int2 b) -{ - return make_int2(max(a.x,b.x), max(a.y,b.y)); +inline __host__ __device__ int2 max(int2 a, int2 b) { + return make_int2(max(a.x, b.x), max(a.y, b.y)); } -inline __host__ __device__ int3 max(int3 a, int3 b) -{ - return make_int3(max(a.x,b.x), max(a.y,b.y), max(a.z,b.z)); +inline __host__ __device__ int3 max(int3 a, int3 b) { + return make_int3(max(a.x, b.x), max(a.y, b.y), max(a.z, b.z)); } -inline __host__ __device__ int4 max(int4 a, int4 b) -{ - return make_int4(max(a.x,b.x), max(a.y,b.y), max(a.z,b.z), max(a.w,b.w)); +inline __host__ __device__ int4 max(int4 a, int4 b) { + return make_int4(max(a.x, b.x), max(a.y, b.y), max(a.z, b.z), max(a.w, b.w)); } -inline __host__ __device__ uint2 max(uint2 a, uint2 b) -{ - return make_uint2(max(a.x,b.x), max(a.y,b.y)); +inline __host__ __device__ uint2 max(uint2 a, uint2 b) { + return make_uint2(max(a.x, b.x), max(a.y, b.y)); } -inline __host__ __device__ uint3 max(uint3 a, uint3 b) -{ - return make_uint3(max(a.x,b.x), max(a.y,b.y), max(a.z,b.z)); +inline __host__ __device__ uint3 max(uint3 a, uint3 b) { + return make_uint3(max(a.x, b.x), max(a.y, b.y), max(a.z, b.z)); } -inline __host__ __device__ uint4 max(uint4 a, uint4 b) -{ - return make_uint4(max(a.x,b.x), max(a.y,b.y), max(a.z,b.z), max(a.w,b.w)); +inline __host__ __device__ uint4 max(uint4 a, uint4 b) { + return make_uint4(max(a.x, b.x), max(a.y, b.y), max(a.z, b.z), max(a.w, b.w)); } //////////////////////////////////////////////////////////////////////////////// @@ -1143,21 +924,17 @@ inline __host__ __device__ uint4 max(uint4 a, uint4 b) // - linear interpolation between a and b, based on value t in [0, 1] range //////////////////////////////////////////////////////////////////////////////// -inline __device__ __host__ float lerp(float a, float b, float t) -{ - return a + t*(b-a); +inline __device__ __host__ float lerp(float a, float b, float t) { + return a + t * (b - a); } -inline __device__ __host__ float2 lerp(float2 a, float2 b, float t) -{ - return a + t*(b-a); +inline __device__ __host__ float2 lerp(float2 a, float2 b, float t) { + return a + t * (b - a); } -inline __device__ __host__ float3 lerp(float3 a, float3 b, float t) -{ - return a + t*(b-a); +inline __device__ __host__ float3 lerp(float3 a, float3 b, float t) { + return a + t * (b - a); } -inline __device__ __host__ float4 lerp(float4 a, float4 b, float t) -{ - return a + t*(b-a); +inline __device__ __host__ float4 lerp(float4 a, float4 b, float t) { + return a + t * (b - a); } //////////////////////////////////////////////////////////////////////////////// @@ -1165,257 +942,205 @@ inline __device__ __host__ float4 lerp(float4 a, float4 b, float t) // - clamp the value v to be in the range [a, b] //////////////////////////////////////////////////////////////////////////////// -inline __device__ __host__ float clamp(float f, float a, float b) -{ - return fmaxf(a, fminf(f, b)); +inline __device__ __host__ float clamp(float f, float a, float b) { + return fmaxf(a, fminf(f, b)); } -inline __device__ __host__ int clamp(int f, int a, int b) -{ - return max(a, min(f, b)); +inline __device__ __host__ int clamp(int f, int a, int b) { + return max(a, min(f, b)); } -inline __device__ __host__ uint clamp(uint f, uint a, uint b) -{ - return max(a, min(f, b)); +inline __device__ __host__ uint clamp(uint f, uint a, uint b) { + return max(a, min(f, b)); } -inline __device__ __host__ float2 clamp(float2 v, float a, float b) -{ - return make_float2(clamp(v.x, a, b), clamp(v.y, a, b)); +inline __device__ __host__ float2 clamp(float2 v, float a, float b) { + return make_float2(clamp(v.x, a, b), clamp(v.y, a, b)); } -inline __device__ __host__ float2 clamp(float2 v, float2 a, float2 b) -{ - return make_float2(clamp(v.x, a.x, b.x), clamp(v.y, a.y, b.y)); +inline __device__ __host__ float2 clamp(float2 v, float2 a, float2 b) { + return make_float2(clamp(v.x, a.x, b.x), clamp(v.y, a.y, b.y)); } -inline __device__ __host__ float3 clamp(float3 v, float a, float b) -{ - return make_float3(clamp(v.x, a, b), clamp(v.y, a, b), clamp(v.z, a, b)); +inline __device__ __host__ float3 clamp(float3 v, float a, float b) { + return make_float3(clamp(v.x, a, b), clamp(v.y, a, b), clamp(v.z, a, b)); } -inline __device__ __host__ float3 clamp(float3 v, float3 a, float3 b) -{ - return make_float3(clamp(v.x, a.x, b.x), clamp(v.y, a.y, b.y), clamp(v.z, a.z, b.z)); +inline __device__ __host__ float3 clamp(float3 v, float3 a, float3 b) { + return make_float3(clamp(v.x, a.x, b.x), clamp(v.y, a.y, b.y), clamp(v.z, a.z, b.z)); } -inline __device__ __host__ float4 clamp(float4 v, float a, float b) -{ - return make_float4(clamp(v.x, a, b), clamp(v.y, a, b), clamp(v.z, a, b), clamp(v.w, a, b)); +inline __device__ __host__ float4 clamp(float4 v, float a, float b) { + return make_float4(clamp(v.x, a, b), clamp(v.y, a, b), clamp(v.z, a, b), clamp(v.w, a, b)); } -inline __device__ __host__ float4 clamp(float4 v, float4 a, float4 b) -{ - return make_float4(clamp(v.x, a.x, b.x), clamp(v.y, a.y, b.y), clamp(v.z, a.z, b.z), clamp(v.w, a.w, b.w)); +inline __device__ __host__ float4 clamp(float4 v, float4 a, float4 b) { + return make_float4(clamp(v.x, a.x, b.x), clamp(v.y, a.y, b.y), clamp(v.z, a.z, b.z), clamp(v.w, a.w, b.w)); } -inline __device__ __host__ int2 clamp(int2 v, int a, int b) -{ - return make_int2(clamp(v.x, a, b), clamp(v.y, a, b)); +inline __device__ __host__ int2 clamp(int2 v, int a, int b) { + return make_int2(clamp(v.x, a, b), clamp(v.y, a, b)); } -inline __device__ __host__ int2 clamp(int2 v, int2 a, int2 b) -{ - return make_int2(clamp(v.x, a.x, b.x), clamp(v.y, a.y, b.y)); +inline __device__ __host__ int2 clamp(int2 v, int2 a, int2 b) { + return make_int2(clamp(v.x, a.x, b.x), clamp(v.y, a.y, b.y)); } -inline __device__ __host__ int3 clamp(int3 v, int a, int b) -{ - return make_int3(clamp(v.x, a, b), clamp(v.y, a, b), clamp(v.z, a, b)); +inline __device__ __host__ int3 clamp(int3 v, int a, int b) { + return make_int3(clamp(v.x, a, b), clamp(v.y, a, b), clamp(v.z, a, b)); } -inline __device__ __host__ int3 clamp(int3 v, int3 a, int3 b) -{ - return make_int3(clamp(v.x, a.x, b.x), clamp(v.y, a.y, b.y), clamp(v.z, a.z, b.z)); +inline __device__ __host__ int3 clamp(int3 v, int3 a, int3 b) { + return make_int3(clamp(v.x, a.x, b.x), clamp(v.y, a.y, b.y), clamp(v.z, a.z, b.z)); } -inline __device__ __host__ int4 clamp(int4 v, int a, int b) -{ - return make_int4(clamp(v.x, a, b), clamp(v.y, a, b), clamp(v.z, a, b), clamp(v.w, a, b)); +inline __device__ __host__ int4 clamp(int4 v, int a, int b) { + return make_int4(clamp(v.x, a, b), clamp(v.y, a, b), clamp(v.z, a, b), clamp(v.w, a, b)); } -inline __device__ __host__ int4 clamp(int4 v, int4 a, int4 b) -{ - return make_int4(clamp(v.x, a.x, b.x), clamp(v.y, a.y, b.y), clamp(v.z, a.z, b.z), clamp(v.w, a.w, b.w)); +inline __device__ __host__ int4 clamp(int4 v, int4 a, int4 b) { + return make_int4(clamp(v.x, a.x, b.x), clamp(v.y, a.y, b.y), clamp(v.z, a.z, b.z), clamp(v.w, a.w, b.w)); } -inline __device__ __host__ uint2 clamp(uint2 v, uint a, uint b) -{ - return make_uint2(clamp(v.x, a, b), clamp(v.y, a, b)); +inline __device__ __host__ uint2 clamp(uint2 v, uint a, uint b) { + return make_uint2(clamp(v.x, a, b), clamp(v.y, a, b)); } -inline __device__ __host__ uint2 clamp(uint2 v, uint2 a, uint2 b) -{ - return make_uint2(clamp(v.x, a.x, b.x), clamp(v.y, a.y, b.y)); +inline __device__ __host__ uint2 clamp(uint2 v, uint2 a, uint2 b) { + return make_uint2(clamp(v.x, a.x, b.x), clamp(v.y, a.y, b.y)); } -inline __device__ __host__ uint3 clamp(uint3 v, uint a, uint b) -{ - return make_uint3(clamp(v.x, a, b), clamp(v.y, a, b), clamp(v.z, a, b)); +inline __device__ __host__ uint3 clamp(uint3 v, uint a, uint b) { + return make_uint3(clamp(v.x, a, b), clamp(v.y, a, b), clamp(v.z, a, b)); } -inline __device__ __host__ uint3 clamp(uint3 v, uint3 a, uint3 b) -{ - return make_uint3(clamp(v.x, a.x, b.x), clamp(v.y, a.y, b.y), clamp(v.z, a.z, b.z)); +inline __device__ __host__ uint3 clamp(uint3 v, uint3 a, uint3 b) { + return make_uint3(clamp(v.x, a.x, b.x), clamp(v.y, a.y, b.y), clamp(v.z, a.z, b.z)); } -inline __device__ __host__ uint4 clamp(uint4 v, uint a, uint b) -{ - return make_uint4(clamp(v.x, a, b), clamp(v.y, a, b), clamp(v.z, a, b), clamp(v.w, a, b)); +inline __device__ __host__ uint4 clamp(uint4 v, uint a, uint b) { + return make_uint4(clamp(v.x, a, b), clamp(v.y, a, b), clamp(v.z, a, b), clamp(v.w, a, b)); } -inline __device__ __host__ uint4 clamp(uint4 v, uint4 a, uint4 b) -{ - return make_uint4(clamp(v.x, a.x, b.x), clamp(v.y, a.y, b.y), clamp(v.z, a.z, b.z), clamp(v.w, a.w, b.w)); +inline __device__ __host__ uint4 clamp(uint4 v, uint4 a, uint4 b) { + return make_uint4(clamp(v.x, a.x, b.x), clamp(v.y, a.y, b.y), clamp(v.z, a.z, b.z), clamp(v.w, a.w, b.w)); } //////////////////////////////////////////////////////////////////////////////// // dot product //////////////////////////////////////////////////////////////////////////////// -inline __host__ __device__ float dot(float2 a, float2 b) -{ - return a.x * b.x + a.y * b.y; +inline __host__ __device__ float dot(float2 a, float2 b) { + return a.x * b.x + a.y * b.y; } -inline __host__ __device__ float dot(float3 a, float3 b) -{ - return a.x * b.x + a.y * b.y + a.z * b.z; +inline __host__ __device__ float dot(float3 a, float3 b) { + return a.x * b.x + a.y * b.y + a.z * b.z; } -inline __host__ __device__ float dot(float4 a, float4 b) -{ - return a.x * b.x + a.y * b.y + a.z * b.z + a.w * b.w; +inline __host__ __device__ float dot(float4 a, float4 b) { + return a.x * b.x + a.y * b.y + a.z * b.z + a.w * b.w; } -inline __host__ __device__ int dot(int2 a, int2 b) -{ - return a.x * b.x + a.y * b.y; +inline __host__ __device__ int dot(int2 a, int2 b) { + return a.x * b.x + a.y * b.y; } -inline __host__ __device__ int dot(int3 a, int3 b) -{ - return a.x * b.x + a.y * b.y + a.z * b.z; +inline __host__ __device__ int dot(int3 a, int3 b) { + return a.x * b.x + a.y * b.y + a.z * b.z; } -inline __host__ __device__ int dot(int4 a, int4 b) -{ - return a.x * b.x + a.y * b.y + a.z * b.z + a.w * b.w; +inline __host__ __device__ int dot(int4 a, int4 b) { + return a.x * b.x + a.y * b.y + a.z * b.z + a.w * b.w; } -inline __host__ __device__ uint dot(uint2 a, uint2 b) -{ - return a.x * b.x + a.y * b.y; +inline __host__ __device__ uint dot(uint2 a, uint2 b) { + return a.x * b.x + a.y * b.y; } -inline __host__ __device__ uint dot(uint3 a, uint3 b) -{ - return a.x * b.x + a.y * b.y + a.z * b.z; +inline __host__ __device__ uint dot(uint3 a, uint3 b) { + return a.x * b.x + a.y * b.y + a.z * b.z; } -inline __host__ __device__ uint dot(uint4 a, uint4 b) -{ - return a.x * b.x + a.y * b.y + a.z * b.z + a.w * b.w; +inline __host__ __device__ uint dot(uint4 a, uint4 b) { + return a.x * b.x + a.y * b.y + a.z * b.z + a.w * b.w; } //////////////////////////////////////////////////////////////////////////////// // length //////////////////////////////////////////////////////////////////////////////// -inline __host__ __device__ float length(float2 v) -{ - return sqrtf(dot(v, v)); +inline __host__ __device__ float length(float2 v) { + return sqrtf(dot(v, v)); } -inline __host__ __device__ float length(float3 v) -{ - return sqrtf(dot(v, v)); +inline __host__ __device__ float length(float3 v) { + return sqrtf(dot(v, v)); } -inline __host__ __device__ float length(float4 v) -{ - return sqrtf(dot(v, v)); +inline __host__ __device__ float length(float4 v) { + return sqrtf(dot(v, v)); } //////////////////////////////////////////////////////////////////////////////// // normalize //////////////////////////////////////////////////////////////////////////////// -inline __host__ __device__ float2 normalize(float2 v) -{ - float invLen = rsqrtf(dot(v, v)); - return v * invLen; +inline __host__ __device__ float2 normalize(float2 v) { + float invLen = rsqrtf(dot(v, v)); + return v * invLen; } -inline __host__ __device__ float3 normalize(float3 v) -{ - float invLen = rsqrtf(dot(v, v)); - return v * invLen; +inline __host__ __device__ float3 normalize(float3 v) { + float invLen = rsqrtf(dot(v, v)); + return v * invLen; } -inline __host__ __device__ float4 normalize(float4 v) -{ - float invLen = rsqrtf(dot(v, v)); - return v * invLen; +inline __host__ __device__ float4 normalize(float4 v) { + float invLen = rsqrtf(dot(v, v)); + return v * invLen; } //////////////////////////////////////////////////////////////////////////////// // floor //////////////////////////////////////////////////////////////////////////////// -inline __host__ __device__ float2 floorf(float2 v) -{ - return make_float2(floorf(v.x), floorf(v.y)); +inline __host__ __device__ float2 floorf(float2 v) { + return make_float2(floorf(v.x), floorf(v.y)); } -inline __host__ __device__ float3 floorf(float3 v) -{ - return make_float3(floorf(v.x), floorf(v.y), floorf(v.z)); +inline __host__ __device__ float3 floorf(float3 v) { + return make_float3(floorf(v.x), floorf(v.y), floorf(v.z)); } -inline __host__ __device__ float4 floorf(float4 v) -{ - return make_float4(floorf(v.x), floorf(v.y), floorf(v.z), floorf(v.w)); +inline __host__ __device__ float4 floorf(float4 v) { + return make_float4(floorf(v.x), floorf(v.y), floorf(v.z), floorf(v.w)); } //////////////////////////////////////////////////////////////////////////////// // frac - returns the fractional portion of a scalar or each vector component //////////////////////////////////////////////////////////////////////////////// -inline __host__ __device__ float fracf(float v) -{ - return v - floorf(v); +inline __host__ __device__ float fracf(float v) { + return v - floorf(v); } -inline __host__ __device__ float2 fracf(float2 v) -{ - return make_float2(fracf(v.x), fracf(v.y)); +inline __host__ __device__ float2 fracf(float2 v) { + return make_float2(fracf(v.x), fracf(v.y)); } -inline __host__ __device__ float3 fracf(float3 v) -{ - return make_float3(fracf(v.x), fracf(v.y), fracf(v.z)); +inline __host__ __device__ float3 fracf(float3 v) { + return make_float3(fracf(v.x), fracf(v.y), fracf(v.z)); } -inline __host__ __device__ float4 fracf(float4 v) -{ - return make_float4(fracf(v.x), fracf(v.y), fracf(v.z), fracf(v.w)); +inline __host__ __device__ float4 fracf(float4 v) { + return make_float4(fracf(v.x), fracf(v.y), fracf(v.z), fracf(v.w)); } //////////////////////////////////////////////////////////////////////////////// // fmod //////////////////////////////////////////////////////////////////////////////// -inline __host__ __device__ float2 fmodf(float2 a, float2 b) -{ - return make_float2(fmodf(a.x, b.x), fmodf(a.y, b.y)); +inline __host__ __device__ float2 fmodf(float2 a, float2 b) { + return make_float2(fmodf(a.x, b.x), fmodf(a.y, b.y)); } -inline __host__ __device__ float3 fmodf(float3 a, float3 b) -{ - return make_float3(fmodf(a.x, b.x), fmodf(a.y, b.y), fmodf(a.z, b.z)); +inline __host__ __device__ float3 fmodf(float3 a, float3 b) { + return make_float3(fmodf(a.x, b.x), fmodf(a.y, b.y), fmodf(a.z, b.z)); } -inline __host__ __device__ float4 fmodf(float4 a, float4 b) -{ - return make_float4(fmodf(a.x, b.x), fmodf(a.y, b.y), fmodf(a.z, b.z), fmodf(a.w, b.w)); +inline __host__ __device__ float4 fmodf(float4 a, float4 b) { + return make_float4(fmodf(a.x, b.x), fmodf(a.y, b.y), fmodf(a.z, b.z), fmodf(a.w, b.w)); } //////////////////////////////////////////////////////////////////////////////// // absolute value //////////////////////////////////////////////////////////////////////////////// -inline __host__ __device__ float2 fabs(float2 v) -{ - return make_float2(fabs(v.x), fabs(v.y)); +inline __host__ __device__ float2 fabs(float2 v) { + return make_float2(fabs(v.x), fabs(v.y)); } -inline __host__ __device__ float3 fabs(float3 v) -{ - return make_float3(fabs(v.x), fabs(v.y), fabs(v.z)); +inline __host__ __device__ float3 fabs(float3 v) { + return make_float3(fabs(v.x), fabs(v.y), fabs(v.z)); } -inline __host__ __device__ float4 fabs(float4 v) -{ - return make_float4(fabs(v.x), fabs(v.y), fabs(v.z), fabs(v.w)); +inline __host__ __device__ float4 fabs(float4 v) { + return make_float4(fabs(v.x), fabs(v.y), fabs(v.z), fabs(v.w)); } -inline __host__ __device__ int2 abs(int2 v) -{ - return make_int2(abs(v.x), abs(v.y)); +inline __host__ __device__ int2 abs(int2 v) { + return make_int2(abs(v.x), abs(v.y)); } -inline __host__ __device__ int3 abs(int3 v) -{ - return make_int3(abs(v.x), abs(v.y), abs(v.z)); +inline __host__ __device__ int3 abs(int3 v) { + return make_int3(abs(v.x), abs(v.y), abs(v.z)); } -inline __host__ __device__ int4 abs(int4 v) -{ - return make_int4(abs(v.x), abs(v.y), abs(v.z), abs(v.w)); +inline __host__ __device__ int4 abs(int4 v) { + return make_int4(abs(v.x), abs(v.y), abs(v.z), abs(v.w)); } //////////////////////////////////////////////////////////////////////////////// @@ -1424,18 +1149,16 @@ inline __host__ __device__ int4 abs(int4 v) // - N should be normalized, reflected vector's length is equal to length of I //////////////////////////////////////////////////////////////////////////////// -inline __host__ __device__ float3 reflect(float3 i, float3 n) -{ - return i - 2.0f * n * dot(n,i); +inline __host__ __device__ float3 reflect(float3 i, float3 n) { + return i - 2.0f * n * dot(n, i); } //////////////////////////////////////////////////////////////////////////////// // cross product //////////////////////////////////////////////////////////////////////////////// -inline __host__ __device__ float3 cross(float3 a, float3 b) -{ - return make_float3(a.y*b.z - a.z*b.y, a.z*b.x - a.x*b.z, a.x*b.y - a.y*b.x); +inline __host__ __device__ float3 cross(float3 a, float3 b) { + return make_float3(a.y * b.z - a.z * b.y, a.z * b.x - a.x * b.z, a.x * b.y - a.y * b.x); } //////////////////////////////////////////////////////////////////////////////// @@ -1445,25 +1168,21 @@ inline __host__ __device__ float3 cross(float3 a, float3 b) // - otherwise returns smooth interpolation between 0 and 1 based on x //////////////////////////////////////////////////////////////////////////////// -inline __device__ __host__ float smoothstep(float a, float b, float x) -{ - float y = clamp((x - a) / (b - a), 0.0f, 1.0f); - return (y*y*(3.0f - (2.0f*y))); -} -inline __device__ __host__ float2 smoothstep(float2 a, float2 b, float2 x) -{ - float2 y = clamp((x - a) / (b - a), 0.0f, 1.0f); - return (y*y*(make_float2(3.0f) - (make_float2(2.0f)*y))); -} -inline __device__ __host__ float3 smoothstep(float3 a, float3 b, float3 x) -{ - float3 y = clamp((x - a) / (b - a), 0.0f, 1.0f); - return (y*y*(make_float3(3.0f) - (make_float3(2.0f)*y))); -} -inline __device__ __host__ float4 smoothstep(float4 a, float4 b, float4 x) -{ - float4 y = clamp((x - a) / (b - a), 0.0f, 1.0f); - return (y*y*(make_float4(3.0f) - (make_float4(2.0f)*y))); +inline __device__ __host__ float smoothstep(float a, float b, float x) { + float y = clamp((x - a) / (b - a), 0.0f, 1.0f); + return (y * y * (3.0f - (2.0f * y))); +} +inline __device__ __host__ float2 smoothstep(float2 a, float2 b, float2 x) { + float2 y = clamp((x - a) / (b - a), 0.0f, 1.0f); + return (y * y * (make_float2(3.0f) - (make_float2(2.0f) * y))); +} +inline __device__ __host__ float3 smoothstep(float3 a, float3 b, float3 x) { + float3 y = clamp((x - a) / (b - a), 0.0f, 1.0f); + return (y * y * (make_float3(3.0f) - (make_float3(2.0f) * y))); +} +inline __device__ __host__ float4 smoothstep(float4 a, float4 b, float4 x) { + float4 y = clamp((x - a) / (b - a), 0.0f, 1.0f); + return (y * y * (make_float4(3.0f) - (make_float4(2.0f) * y))); } #endif From 76ffa2a0b56f44c9fea970cf54c8d93ea3f8d396 Mon Sep 17 00:00:00 2001 From: Cameron Gutman Date: Mon, 2 Jan 2023 11:11:45 -0600 Subject: [PATCH 58/88] Fix unexpected format switching in display_vram (#667) --- src/platform/windows/display.h | 2 ++ src/platform/windows/display_base.cpp | 30 ++++++++--------- src/platform/windows/display_vram.cpp | 48 ++++++++++++++++++++++----- 3 files changed, 56 insertions(+), 24 deletions(-) diff --git a/src/platform/windows/display.h b/src/platform/windows/display.h index e4d43b336ef..ecc0bc2d779 100644 --- a/src/platform/windows/display.h +++ b/src/platform/windows/display.h @@ -185,6 +185,8 @@ class display_vram_t : public display_base_t, public std::enable_shared_from_thi vs_t scene_vs; gpu_cursor_t cursor; + + texture2d_t last_frame_copy; }; } // namespace platf::dxgi diff --git a/src/platform/windows/display_base.cpp b/src/platform/windows/display_base.cpp index bc10b5a5932..ff8845894f7 100644 --- a/src/platform/windows/display_base.cpp +++ b/src/platform/windows/display_base.cpp @@ -292,22 +292,10 @@ int display_base_t::init(int framerate, const std::string &display_name) { //FIXME: Duplicate output on RX580 in combination with DOOM (2016) --> BSOD { - dxgi::output1_t output1 {}; - dxgi::output5_t output5 {}; - // IDXGIOutput5 is optional, but can provide improved performance and wide color support + dxgi::output5_t output5 {}; status = output->QueryInterface(IID_IDXGIOutput5, (void **)&output5); - if(FAILED(status)) { - BOOST_LOG(warning) << "Failed to query IDXGIOutput5 from the output"sv; - } - - status = output->QueryInterface(IID_IDXGIOutput1, (void **)&output1); - if(FAILED(status)) { - BOOST_LOG(error) << "Failed to query IDXGIOutput1 from the output"sv; - return -1; - } - - if(output5) { + if(SUCCEEDED(status)) { // Ask the display implementation which formats it supports auto supported_formats = get_supported_sdr_capture_formats(); if(supported_formats.empty()) { @@ -324,12 +312,24 @@ int display_base_t::init(int framerate, const std::string &display_name) { 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; + + 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; + } - if(!output5 || FAILED(status)) { for(int x = 0; x < 2; ++x) { status = output1->DuplicateOutput((IUnknown *)device.get(), &dup.dup); if(SUCCEEDED(status)) { diff --git a/src/platform/windows/display_vram.cpp b/src/platform/windows/display_vram.cpp index ae84bfb0efd..7c9c8674abc 100644 --- a/src/platform/windows/display_vram.cpp +++ b/src/platform/windows/display_vram.cpp @@ -628,7 +628,7 @@ capture_e display_vram_t::snapshot(platf::img_t *img_base, std::chrono::millisec cursor.set_pos(frame_info.PointerPosition.Position.x, frame_info.PointerPosition.Position.y, frame_info.PointerPosition.Visible); } - if(capture_format != DXGI_FORMAT_UNKNOWN || frame_update_flag) { + if(frame_update_flag) { texture2d_t src {}; // Get the texture object from this frame @@ -641,12 +641,6 @@ capture_e display_vram_t::snapshot(platf::img_t *img_base, std::chrono::millisec D3D11_TEXTURE2D_DESC desc; src->GetDesc(&desc); - // 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 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) { @@ -654,6 +648,29 @@ capture_e display_vram_t::snapshot(platf::img_t *img_base, std::chrono::millisec 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) << ']'; + + D3D11_TEXTURE2D_DESC t {}; + t.Width = width; + t.Height = height; + t.MipLevels = 1; + t.ArraySize = 1; + t.SampleDesc.Count = 1; + t.Usage = D3D11_USAGE_DEFAULT; + t.Format = capture_format; + t.BindFlags = 0; + + // Create a texture to store the most recent copy of the desktop + auto status = device->CreateTexture2D(&t, nullptr, &last_frame_copy); + if(FAILED(status)) { + BOOST_LOG(error) << "Failed to create frame copy texture [0x"sv << util::hex(status).to_string_view() << ']'; + return capture_e::error; + } + } + // 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) { @@ -666,10 +683,11 @@ capture_e display_vram_t::snapshot(platf::img_t *img_base, std::chrono::millisec return capture_e::error; } - // Copy the texture into this image + // Copy the texture into this image and the staging texture device_ctx->CopyResource(img->texture.get(), src.get()); + device_ctx->CopyResource(last_frame_copy.get(), src.get()); } - else { + else if(capture_format == DXGI_FORMAT_UNKNOWN) { // We don't know the final capture format yet, so we will encode a dummy image BOOST_LOG(debug) << "Capture format is still unknown. Encoding a blank image"sv; @@ -677,6 +695,18 @@ capture_e display_vram_t::snapshot(platf::img_t *img_base, std::chrono::millisec return capture_e::error; } } + else { + // We must know the capture format in this path or we would have hit the above unknown format case + if(complete_img(img, false)) { + return capture_e::error; + } + + // We have a previously captured frame to reuse. We can't just grab the src texture from + // the call to AcquireNextFrame() because that won't be valid. It seems to return a texture + // in the unmodified desktop format (rather than the formats we passed to DuplicateOutput1()) + // if called in that case. + device_ctx->CopyResource(img->texture.get(), last_frame_copy.get()); + } if(cursor.visible && cursor_visible) { D3D11_VIEWPORT view { From 0439d7a83a0deac7578043cfdf8fddadd4b46799 Mon Sep 17 00:00:00 2001 From: Cameron Gutman Date: Mon, 2 Jan 2023 14:56:58 -0600 Subject: [PATCH 59/88] Use separate encoding and capture devices to enable parallel encoding and capture (#668) --- src/platform/windows/display.h | 7 + src/platform/windows/display_base.cpp | 7 +- src/platform/windows/display_vram.cpp | 368 +++++++++++++++++++------- src/video.cpp | 11 +- 4 files changed, 291 insertions(+), 102 deletions(-) diff --git a/src/platform/windows/display.h b/src/platform/windows/display.h index ecc0bc2d779..0f73874587e 100644 --- a/src/platform/windows/display.h +++ b/src/platform/windows/display.h @@ -18,6 +18,10 @@ namespace platf::dxgi { 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; + template void Release(T *dxgi) { dxgi->Release(); @@ -27,6 +31,7 @@ using factory1_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>; @@ -36,6 +41,7 @@ using dup_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>; @@ -49,6 +55,7 @@ using sampler_state_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>; diff --git a/src/platform/windows/display_base.cpp b/src/platform/windows/display_base.cpp index ff8845894f7..9f8c2705957 100644 --- a/src/platform/windows/display_base.cpp +++ b/src/platform/windows/display_base.cpp @@ -186,7 +186,7 @@ int display_base_t::init(int framerate, const std::string &display_name) { adapter_p, D3D_DRIVER_TYPE_UNKNOWN, nullptr, - D3D11_CREATE_DEVICE_VIDEO_SUPPORT, + D3D11_CREATE_DEVICE_FLAGS, featureLevels, sizeof(featureLevels) / sizeof(D3D_FEATURE_LEVEL), D3D11_SDK_VERSION, &device, @@ -272,7 +272,10 @@ int display_base_t::init(int framerate, const std::string &display_name) { return -1; } - dxgi->SetGPUThreadPriority(7); + 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 diff --git a/src/platform/windows/display_vram.cpp b/src/platform/windows/display_vram.cpp index 7c9c8674abc..75e77b6e23a 100644 --- a/src/platform/windows/display_vram.cpp +++ b/src/platform/windows/display_vram.cpp @@ -88,13 +88,26 @@ blob_t scene_ps_hlsl; struct img_d3d_t : public platf::img_t { std::shared_ptr display; - shader_res_t input_res; - render_target_t scene_rt; + // These objects are owned by the display_t's ID3D11Device + texture2d_t capture_texture; + render_target_t capture_rt; + keyed_mutex_t capture_mutex; + + // These objects are owned by the hwdevice_t's ID3D11Device + texture2d_t encoder_texture; + shader_res_t encoder_input_res; + keyed_mutex_t encoder_mutex; + + // This is the shared handle used by hwdevice_t to open capture_texture + HANDLE encoder_texture_handle = {}; - texture2d_t texture; bool dummy = false; - ~img_d3d_t() override = default; + virtual ~img_d3d_t() override { + if(encoder_texture_handle) { + CloseHandle(encoder_texture_handle); + } + }; }; util::buffer_t make_cursor_image(util::buffer_t &&img_data, DXGI_OUTDUPL_POINTER_SHAPE_INFO shape_info) { @@ -216,55 +229,65 @@ blob_t compile_vertex_shader(LPCSTR file) { return compile_shader(file, "main_vs", "vs_5_0"); } -int init_rt(device_t::pointer device, shader_res_t &shader_res, render_target_t &render_target, int width, int height, texture2d_t::pointer tex) { - auto status = device->CreateShaderResourceView(tex, nullptr, &shader_res); - if(status) { - BOOST_LOG(error) << "Failed to create shader resource view for luma [0x"sv << util::hex(status).to_string_view() << ']'; - return -1; - } - - status = device->CreateRenderTargetView(tex, nullptr, &render_target); - if(status) { - BOOST_LOG(error) << "Failed to create render target view [0x"sv << util::hex(status).to_string_view() << ']'; - return -1; - } - - return 0; -} - class hwdevice_t : public platf::hwdevice_t { public: int convert(platf::img_t &img_base) override { - auto &img = (img_d3d_t &)img_base; + auto &img = (img_d3d_t &)img_base; + auto back_d3d_img = (img_d3d_t *)back_img.get(); + + // Open the shared capture texture with our ID3D11Device + if(share_img(&img_base)) { + return -1; + } + + // Acquire encoder mutex to synchronize with capture code + auto status = img.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_p->IASetInputLayout(input_layout.get()); + // Even though this image will never have racing updates, we must acquire the + // keyed mutex for PSSetShaderResources() to succeed. + status = back_d3d_img->encoder_mutex->AcquireSync(0, INFINITE); + if(status != S_OK) { + img.encoder_mutex->ReleaseSync(0); + BOOST_LOG(error) << "Failed to acquire back_d3d_img mutex [0x"sv << util::hex(status).to_string_view() << ']'; + return -1; + } + + device_ctx->IASetInputLayout(input_layout.get()); _init_view_port(this->img.width, this->img.height); - device_ctx_p->OMSetRenderTargets(1, &nv12_Y_rt, nullptr); - device_ctx_p->VSSetShader(scene_vs.get(), nullptr, 0); - device_ctx_p->PSSetShader(convert_Y_ps.get(), nullptr, 0); - device_ctx_p->PSSetShaderResources(0, 1, &((img_d3d_t *)back_img.get())->input_res); - device_ctx_p->Draw(3, 0); + device_ctx->OMSetRenderTargets(1, &nv12_Y_rt, nullptr); + device_ctx->VSSetShader(scene_vs.get(), nullptr, 0); + device_ctx->PSSetShader(convert_Y_ps.get(), nullptr, 0); + device_ctx->PSSetShaderResources(0, 1, &back_d3d_img->encoder_input_res); + device_ctx->Draw(3, 0); - device_ctx_p->RSSetViewports(1, &outY_view); - device_ctx_p->PSSetShaderResources(0, 1, &img.input_res); - device_ctx_p->Draw(3, 0); + device_ctx->RSSetViewports(1, &outY_view); + device_ctx->PSSetShaderResources(0, 1, &img.encoder_input_res); + device_ctx->Draw(3, 0); // Artifacts start appearing on the rendered image if Sunshine doesn't flush // before rendering on the UV part of the image. - device_ctx_p->Flush(); + device_ctx->Flush(); _init_view_port(this->img.width / 2, this->img.height / 2); - device_ctx_p->OMSetRenderTargets(1, &nv12_UV_rt, nullptr); - device_ctx_p->VSSetShader(convert_UV_vs.get(), nullptr, 0); - device_ctx_p->PSSetShader(convert_UV_ps.get(), nullptr, 0); - device_ctx_p->PSSetShaderResources(0, 1, &((img_d3d_t *)back_img.get())->input_res); - device_ctx_p->Draw(3, 0); + device_ctx->OMSetRenderTargets(1, &nv12_UV_rt, nullptr); + device_ctx->VSSetShader(convert_UV_vs.get(), nullptr, 0); + device_ctx->PSSetShader(convert_UV_ps.get(), nullptr, 0); + device_ctx->PSSetShaderResources(0, 1, &back_d3d_img->encoder_input_res); + device_ctx->Draw(3, 0); - device_ctx_p->RSSetViewports(1, &outUV_view); - device_ctx_p->PSSetShaderResources(0, 1, &img.input_res); - device_ctx_p->Draw(3, 0); - device_ctx_p->Flush(); + device_ctx->RSSetViewports(1, &outUV_view); + device_ctx->PSSetShaderResources(0, 1, &img.encoder_input_res); + device_ctx->Draw(3, 0); + device_ctx->Flush(); + + // Release encoder mutexes to allow capture code to reuse this image + back_d3d_img->encoder_mutex->ReleaseSync(0); + img.encoder_mutex->ReleaseSync(0); return 0; } @@ -294,8 +317,8 @@ class hwdevice_t : public platf::hwdevice_t { return; } - device_ctx_p->VSSetConstantBuffers(0, 1, &info_scene); - device_ctx_p->PSSetConstantBuffers(0, 1, &color_matrix); + device_ctx->VSSetConstantBuffers(0, 1, &info_scene); + device_ctx->PSSetConstantBuffers(0, 1, &color_matrix); this->color_matrix = std::move(color_matrix); } @@ -303,8 +326,6 @@ class hwdevice_t : public platf::hwdevice_t { this->hwframe.reset(frame); this->frame = frame; - auto device_p = (device_t::pointer)data; - auto out_width = frame->width; auto out_height = frame->height; @@ -333,7 +354,7 @@ class hwdevice_t : public platf::hwdevice_t { t.Format = format; t.BindFlags = D3D11_BIND_RENDER_TARGET; - auto status = device_p->CreateTexture2D(&t, nullptr, &img.texture); + auto status = device->CreateTexture2D(&t, nullptr, &img.encoder_texture); if(FAILED(status)) { BOOST_LOG(error) << "Failed to create render target texture [0x"sv << util::hex(status).to_string_view() << ']'; return -1; @@ -341,12 +362,12 @@ class hwdevice_t : public platf::hwdevice_t { img.width = out_width; img.height = out_height; - img.data = (std::uint8_t *)img.texture.get(); + img.data = (std::uint8_t *)img.encoder_texture.get(); img.row_pitch = out_width * 4; img.pixel_pitch = 4; float info_in[16 / sizeof(float)] { 1.0f / (float)out_width_f }; //aligned to 16-byte - info_scene = make_buffer(device_p, info_in); + info_scene = make_buffer(device.get(), info_in); if(!info_scene) { BOOST_LOG(error) << "Failed to create info scene buffer"sv; @@ -358,7 +379,7 @@ class hwdevice_t : public platf::hwdevice_t { D3D11_RTV_DIMENSION_TEXTURE2D }; - status = device_p->CreateRenderTargetView(img.texture.get(), &nv12_rt_desc, &nv12_Y_rt); + status = device->CreateRenderTargetView(img.encoder_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; @@ -366,7 +387,7 @@ class hwdevice_t : public platf::hwdevice_t { nv12_rt_desc.Format = (format == DXGI_FORMAT_P010) ? DXGI_FORMAT_R16G16_UNORM : DXGI_FORMAT_R8G8_UNORM; - status = device_p->CreateRenderTargetView(img.texture.get(), &nv12_rt_desc, &nv12_UV_rt); + status = device->CreateRenderTargetView(img.encoder_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; @@ -393,48 +414,81 @@ class hwdevice_t : public platf::hwdevice_t { } int init( - std::shared_ptr display, device_t::pointer device_p, device_ctx_t::pointer device_ctx_p, + std::shared_ptr display, adapter_t::pointer adapter_p, pix_fmt_e pix_fmt) { - HRESULT status; + 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); + + 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; + } - device_p->AddRef(); - data = device_p; + 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."; + } - this->device_ctx_p = device_ctx_p; + data = device.get(); format = (pix_fmt == pix_fmt_e::nv12 ? DXGI_FORMAT_NV12 : DXGI_FORMAT_P010); - status = device_p->CreateVertexShader(scene_vs_hlsl->GetBufferPointer(), scene_vs_hlsl->GetBufferSize(), nullptr, &scene_vs); + status = device->CreateVertexShader(scene_vs_hlsl->GetBufferPointer(), scene_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_p->CreatePixelShader(convert_Y_ps_hlsl->GetBufferPointer(), convert_Y_ps_hlsl->GetBufferSize(), nullptr, &convert_Y_ps); + status = device->CreatePixelShader(convert_Y_ps_hlsl->GetBufferPointer(), convert_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; } - status = device_p->CreatePixelShader(convert_UV_ps_hlsl->GetBufferPointer(), convert_UV_ps_hlsl->GetBufferSize(), nullptr, &convert_UV_ps); + status = device->CreatePixelShader(convert_UV_ps_hlsl->GetBufferPointer(), convert_UV_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_p->CreateVertexShader(convert_UV_vs_hlsl->GetBufferPointer(), convert_UV_vs_hlsl->GetBufferSize(), nullptr, &convert_UV_vs); + status = device->CreateVertexShader(convert_UV_vs_hlsl->GetBufferPointer(), convert_UV_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; } - status = device_p->CreatePixelShader(scene_ps_hlsl->GetBufferPointer(), scene_ps_hlsl->GetBufferSize(), nullptr, &scene_ps); + status = device->CreatePixelShader(scene_ps_hlsl->GetBufferPointer(), scene_ps_hlsl->GetBufferSize(), nullptr, &scene_ps); if(status) { BOOST_LOG(error) << "Failed to create scene pixel shader [0x"sv << util::hex(status).to_string_view() << ']'; return -1; } - color_matrix = make_buffer(device_p, ::video::colors[0]); + color_matrix = make_buffer(device.get(), ::video::colors[0]); if(!color_matrix) { BOOST_LOG(error) << "Failed to create color matrix buffer"sv; return -1; @@ -444,7 +498,7 @@ class hwdevice_t : public platf::hwdevice_t { "SV_Position", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0 }; - status = device_p->CreateInputLayout( + status = device->CreateInputLayout( &layout_desc, 1, convert_UV_vs_hlsl->GetBufferPointer(), convert_UV_vs_hlsl->GetBufferSize(), &input_layout); @@ -454,22 +508,40 @@ class hwdevice_t : public platf::hwdevice_t { // Color the background black, so that the padding for keeping the aspect ratio // is black back_img = img.display->alloc_img(); - if(img.display->dummy_img(back_img.get())) { + if(img.display->dummy_img(back_img.get()) || share_img(back_img.get())) { BOOST_LOG(warning) << "Couldn't create an image to set background color to black"sv; return -1; } - device_ctx_p->IASetInputLayout(input_layout.get()); - device_ctx_p->PSSetConstantBuffers(0, 1, &color_matrix); - device_ctx_p->VSSetConstantBuffers(0, 1, &info_scene); + blend_disable = make_blend(device.get(), false); + if(!blend_disable) { + return -1; + } - return 0; - } + 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; - ~hwdevice_t() override { - if(data) { - ((ID3D11Device *)data)->Release(); + 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->IASetInputLayout(input_layout.get()); + device_ctx->PSSetConstantBuffers(0, 1, &color_matrix); + device_ctx->VSSetConstantBuffers(0, 1, &info_scene); + + device_ctx->OMSetBlendState(blend_disable.get(), nullptr, 0xFFFFFFFFu); + device_ctx->PSSetSamplers(0, 1, &sampler_linear); + device_ctx->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP); + + return 0; } private: @@ -480,13 +552,52 @@ class hwdevice_t : public platf::hwdevice_t { 0.0f, 1.0f }; - device_ctx_p->RSSetViewports(1, &view); + device_ctx->RSSetViewports(1, &view); } void _init_view_port(float width, float height) { _init_view_port(0.0f, 0.0f, width, height); } + int share_img(platf::img_t *img_base) { + auto img = (img_d3d_t *)img_base; + + // If we've already opened the shared texture, we're done + if(img->encoder_texture) { + return 0; + } + + 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->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->encoder_texture->QueryInterface(__uuidof(IDXGIKeyedMutex), (void **)&img->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->encoder_texture.get(), nullptr, &img->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; + } + + return 0; + } + public: frame_t hwframe; @@ -497,6 +608,9 @@ class hwdevice_t : public platf::hwdevice_t { input_layout_t input_layout; + blend_t blend_disable; + sampler_state_t sampler_linear; + render_target_t nv12_Y_rt; render_target_t nv12_UV_rt; @@ -518,7 +632,8 @@ class hwdevice_t : public platf::hwdevice_t { DXGI_FORMAT format; - device_ctx_t::pointer device_ctx_p; + device_t device; + device_ctx_t device_ctx; }; capture_e display_vram_t::capture(snapshot_cb_t &&snapshot_cb, std::shared_ptr<::platf::img_t> img, bool *cursor) { @@ -664,7 +779,7 @@ capture_e display_vram_t::snapshot(platf::img_t *img_base, std::chrono::millisec t.BindFlags = 0; // Create a texture to store the most recent copy of the desktop - auto status = device->CreateTexture2D(&t, nullptr, &last_frame_copy); + status = device->CreateTexture2D(&t, nullptr, &last_frame_copy); if(FAILED(status)) { BOOST_LOG(error) << "Failed to create frame copy texture [0x"sv << util::hex(status).to_string_view() << ']'; return capture_e::error; @@ -683,17 +798,38 @@ capture_e display_vram_t::snapshot(platf::img_t *img_base, std::chrono::millisec return capture_e::error; } - // Copy the texture into this image and the staging texture - device_ctx->CopyResource(img->texture.get(), src.get()); + // Copy the texture to use for cursor-only updates device_ctx->CopyResource(last_frame_copy.get(), src.get()); + + // Copy into the capture texture on the image with the mutex held + status = img->capture_mutex->AcquireSync(0, INFINITE); + if(status != S_OK) { + BOOST_LOG(error) << "Failed to acquire capture mutex [0x"sv << util::hex(status).to_string_view() << ']'; + return capture_e::error; + } + device_ctx->CopyResource(img->capture_texture.get(), src.get()); } else if(capture_format == DXGI_FORMAT_UNKNOWN) { // We don't know the final capture format yet, so we will encode a dummy image BOOST_LOG(debug) << "Capture format is still unknown. Encoding a blank image"sv; - if(dummy_img(img)) { + // Finish creating the image as a dummy (if it hasn't happened already) + if(complete_img(img, true)) { + return capture_e::error; + } + + auto dummy_data = std::make_unique(img->row_pitch * img->height); + std::fill_n(dummy_data.get(), img->row_pitch * img->height, 0); + + status = img->capture_mutex->AcquireSync(0, INFINITE); + if(status != S_OK) { + BOOST_LOG(error) << "Failed to acquire capture mutex [0x"sv << util::hex(status).to_string_view() << ']'; return capture_e::error; } + + // Populate the image with dummy data. This is required because these images could be reused + // after rendering (in which case they would have a cursor already rendered into them). + device_ctx->UpdateSubresource(img->capture_texture.get(), 0, nullptr, dummy_data.get(), img->row_pitch, 0); } else { // We must know the capture format in this path or we would have hit the above unknown format case @@ -705,7 +841,12 @@ capture_e display_vram_t::snapshot(platf::img_t *img_base, std::chrono::millisec // the call to AcquireNextFrame() because that won't be valid. It seems to return a texture // in the unmodified desktop format (rather than the formats we passed to DuplicateOutput1()) // if called in that case. - device_ctx->CopyResource(img->texture.get(), last_frame_copy.get()); + status = img->capture_mutex->AcquireSync(0, INFINITE); + if(status != S_OK) { + BOOST_LOG(error) << "Failed to acquire capture mutex [0x"sv << util::hex(status).to_string_view() << ']'; + return capture_e::error; + } + device_ctx->CopyResource(img->capture_texture.get(), last_frame_copy.get()); } if(cursor.visible && cursor_visible) { @@ -719,13 +860,16 @@ capture_e display_vram_t::snapshot(platf::img_t *img_base, std::chrono::millisec device_ctx->PSSetShader(scene_ps.get(), nullptr, 0); device_ctx->RSSetViewports(1, &view); device_ctx->PSSetShaderResources(0, 1, &cursor.input_res); - device_ctx->OMSetRenderTargets(1, &img->scene_rt, nullptr); + device_ctx->OMSetRenderTargets(1, &img->capture_rt, nullptr); device_ctx->OMSetBlendState(blend_enable.get(), nullptr, 0xFFFFFFFFu); device_ctx->RSSetViewports(1, &cursor.cursor_view); device_ctx->Draw(3, 0); device_ctx->OMSetBlendState(blend_disable.get(), nullptr, 0xFFFFFFFFu); } + // Release the mutex to allow encoding of this frame + img->capture_mutex->ReleaseSync(0); + return capture_e::ok; } @@ -786,11 +930,12 @@ std::shared_ptr display_vram_t::alloc_img() { 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; - // If this already has a texture and it's not switching dummy state, nothing to do - if(img->texture && img->dummy == dummy) { + // 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; } @@ -801,10 +946,17 @@ int display_vram_t::complete_img(platf::img_t *img_base, bool dummy) { } // Reset the image (in case this was previously a dummy) - img->texture.reset(); - img->input_res.reset(); - img->scene_rt.reset(); + img->capture_texture.reset(); + img->capture_rt.reset(); + img->capture_mutex.reset(); + img->encoder_texture.reset(); + img->encoder_input_res.reset(); + img->encoder_mutex.reset(); img->data = nullptr; + if(img->encoder_texture_handle) { + CloseHandle(img->encoder_texture_handle); + img->encoder_texture_handle = NULL; + } // Initialize format-dependent fields img->pixel_pitch = get_pixel_pitch(); @@ -820,36 +972,59 @@ int display_vram_t::complete_img(platf::img_t *img_base, bool dummy) { t.Usage = D3D11_USAGE_DEFAULT; t.Format = (capture_format == DXGI_FORMAT_UNKNOWN) ? DXGI_FORMAT_B8G8R8A8_UNORM : capture_format; t.BindFlags = D3D11_BIND_SHADER_RESOURCE | D3D11_BIND_RENDER_TARGET; + t.MiscFlags = D3D11_RESOURCE_MISC_SHARED_NTHANDLE | D3D11_RESOURCE_MISC_SHARED_KEYEDMUTEX; + + 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 + }; - auto status = device->CreateTexture2D(&t, nullptr, &img->texture); + auto status = device->CreateTexture2D(&t, &initial_data, &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; } - if(init_rt(device.get(), img->input_res, img->scene_rt, img->width, img->height, img->texture.get())) { + 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; } - img->data = (std::uint8_t *)img->texture.get(); - - return 0; -} + // Get the keyed mutex to synchronize with the encoding code + 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; + } -int display_vram_t::dummy_img(platf::img_t *img_base) { - auto img = (img_d3d_t *)img_base; + resource1_t 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; + } - if(complete_img(img, true)) { + // 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); + if(FAILED(status)) { + BOOST_LOG(error) << "Failed to create shared texture handle [0x"sv << util::hex(status).to_string_view() << ']'; return -1; } - auto dummy_data = std::make_unique(img->row_pitch * img->height); - std::fill_n(dummy_data.get(), img->row_pitch * img->height, 0); + img->data = (std::uint8_t *)img->capture_texture.get(); - device_ctx->UpdateSubresource(img->texture.get(), 0, nullptr, dummy_data.get(), img->row_pitch, 0); 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) { + return complete_img(img_base, true); +} + std::vector display_vram_t::get_supported_sdr_capture_formats() { return std::vector { DXGI_FORMAT_B8G8R8A8_UNORM, DXGI_FORMAT_R8G8B8A8_UNORM }; } @@ -865,8 +1040,7 @@ std::shared_ptr display_vram_t::make_hwdevice(pix_fmt_e pix_f auto ret = hwdevice->init( shared_from_this(), - device.get(), - device_ctx.get(), + adapter.get(), pix_fmt); if(ret) { diff --git a/src/video.cpp b/src/video.cpp index 2bdb6719b2c..9529ce1f524 100644 --- a/src/video.cpp +++ b/src/video.cpp @@ -441,11 +441,10 @@ static encoder_t nvenc { std::make_optional({ "qp"s, &config::video.qp }), "h264_nvenc"s, }, + PARALLEL_ENCODING, #ifdef _WIN32 - DEFAULT, dxgi_make_hwdevice_ctx #else - PARALLEL_ENCODING, cuda_make_hwdevice_ctx #endif }; @@ -486,7 +485,7 @@ static encoder_t amdvce { std::make_optional({ "qp_p"s, &config::video.qp }), "h264_amf"s, }, - DEFAULT, + PARALLEL_ENCODING, dxgi_make_hwdevice_ctx }; #endif @@ -1409,6 +1408,12 @@ void capture_async( std::move(hwdevice), ref->reinit_event, *ref->encoder_p, channel_data); + + // Free images that weren't consumed by the encoder before it quit. + // This is critical to allow the display_t to be freed correctly. + while(images->peek()) { + images->pop(); + } } } From 952e142ffa54bb7564cd1f0745840abf899ff9f2 Mon Sep 17 00:00:00 2001 From: ReenigneArcher <42013603+ReenigneArcher@users.noreply.github.com> Date: Mon, 2 Jan 2023 17:24:39 -0500 Subject: [PATCH 60/88] fix aur publish trigger event (#680) --- .github/workflows/CI.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index e4a8ef1df46..f9821dce247 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -189,7 +189,7 @@ jobs: elif [[ ${{ github.ref == 'refs/heads/nightly' }} ]]; then sub_version=".r${commit}" - echo "aur_publish=true" >> $GITHUB_ENV + echo "aur_publish=false" >> $GITHUB_ENV fi else echo "This is a PR event" From 12efe9629752bec8ec68318a0ec4f803c62c036b Mon Sep 17 00:00:00 2001 From: ReenigneArcher <42013603+ReenigneArcher@users.noreply.github.com> Date: Mon, 2 Jan 2023 18:18:29 -0500 Subject: [PATCH 61/88] fix docs badges for epub (#679) --- README.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.rst b/README.rst index c00277110d5..7a7cf7f545f 100644 --- a/README.rst +++ b/README.rst @@ -84,11 +84,11 @@ System Requirements Integrations ------------ -.. image:: https://img.shields.io/github/actions/workflow/status/lizardbyte/sunshine/CI.yml?branch=master&label=CI%20build&logo=github&style=for-the-badge +.. image:: https://img.shields.io/github/actions/workflow/status/lizardbyte/sunshine/CI.yml.svg?branch=master&label=CI%20build&logo=github&style=for-the-badge :alt: GitHub Workflow Status (CI) :target: https://github.com/LizardByte/Sunshine/actions/workflows/CI.yml?query=branch%3Amaster -.. image:: https://img.shields.io/github/actions/workflow/status/lizardbyte/sunshine/localize.yml?branch=nightly&label=localize%20build&logo=github&style=for-the-badge +.. image:: https://img.shields.io/github/actions/workflow/status/lizardbyte/sunshine/localize.yml.svg?branch=nightly&label=localize%20build&logo=github&style=for-the-badge :alt: GitHub Workflow Status (localize) :target: https://github.com/LizardByte/Sunshine/actions/workflows/localize.yml?query=branch%3Anightly @@ -101,7 +101,7 @@ Integrations :target: https://crowdin.com/project/sunshinestream Support ---------- +------- Our support methods are listed in our `LizardByte Docs `_. From 052297a1a59ad0c50c41ee0e0c095f560071915e Mon Sep 17 00:00:00 2001 From: Brad Richardson Date: Mon, 2 Jan 2023 21:02:10 -0500 Subject: [PATCH 62/88] Update app id on edit (#670) Co-authored-by: ReenigneArcher <42013603+ReenigneArcher@users.noreply.github.com> --- CMakeLists.txt | 1 + src/confighttp.cpp | 15 +++++++++++++++ src/main.cpp | 1 + src/nvhttp.cpp | 17 ++++++++--------- src/process.cpp | 37 +++++++++++++++++++++++++------------ src/process.h | 7 +++++-- src/rand.h | 23 +++++++++++++++++++++++ src/stream.cpp | 2 +- 8 files changed, 79 insertions(+), 24 deletions(-) create mode 100644 src/rand.h diff --git a/CMakeLists.txt b/CMakeLists.txt index ef731b519d7..b192e00828a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -375,6 +375,7 @@ set(SUNSHINE_TARGET_FILES src/network.cpp src/network.h src/move_by_copy.h + src/rand.h src/task_pool.h src/thread_pool.h src/thread_safe.h diff --git a/src/confighttp.cpp b/src/confighttp.cpp index 8df67249bbb..e58f53ae97a 100644 --- a/src/confighttp.cpp +++ b/src/confighttp.cpp @@ -6,6 +6,7 @@ #include "process.h" #include +#include #include #include @@ -29,6 +30,7 @@ #include "network.h" #include "nvhttp.h" #include "platform/common.h" +#include "rand.h" #include "rtsp.h" #include "utility.h" #include "uuid.h" @@ -301,6 +303,11 @@ void saveApp(resp_https_t response, req_https_t request) { response->write(data.str()); }); + std::set ids; + for(auto const &app : proc::proc.get_apps()) { + ids.insert(app.id); + } + pt::ptree inputTree, fileTree; BOOST_LOG(fatal) << config::stream.file_apps; @@ -309,6 +316,14 @@ void saveApp(resp_https_t response, req_https_t request) { pt::read_json(ss, inputTree); pt::read_json(config::stream.file_apps, fileTree); + // Moonlight checks the id of an item to determine if an item was changed + // Needs to be a 32-bit positive integer due to client limitations, "0" indicates no app + auto id = util::generate_int32(1, std::numeric_limits::max()); + while(ids.count(std::to_string(id)) > 0) { + id = util::generate_int32(1, std::numeric_limits::max()); + } + inputTree.put("id", id); + if(inputTree.get_child("prep-cmd").empty()) { inputTree.erase("prep-cmd"); } diff --git a/src/main.cpp b/src/main.cpp index 886149e8321..639688aaa15 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -357,6 +357,7 @@ int main(int argc, char *argv[]) { std::string read_file(const char *path) { if(!std::filesystem::exists(path)) { + BOOST_LOG(debug) << "Missing file: " << path; return {}; } diff --git a/src/nvhttp.cpp b/src/nvhttp.cpp index 9ffda1b4e70..444e5ed7ef6 100644 --- a/src/nvhttp.cpp +++ b/src/nvhttp.cpp @@ -640,8 +640,8 @@ void serverinfo(std::shared_ptr::Response> res } auto current_appid = proc::proc.running(); tree.put("root.PairStatus", pair_status); - tree.put("root.currentgame", current_appid >= 0 ? current_appid + 1 : 0); - tree.put("root.state", current_appid >= 0 ? "SUNSHINE_SERVER_BUSY" : "SUNSHINE_SERVER_FREE"); + tree.put("root.currentgame", current_appid); + tree.put("root.state", current_appid > 0 ? "SUNSHINE_SERVER_BUSY" : "SUNSHINE_SERVER_FREE"); std::ostringstream data; @@ -683,13 +683,12 @@ void applist(resp_https_t response, req_https_t request) { apps.put(".status_code", 200); - int x = 0; for(auto &proc : proc::proc.get_apps()) { pt::ptree app; app.put("IsHdrSupported"s, config::video.hevc_mode == 3 ? 1 : 0); app.put("AppTitle"s, proc.name); - app.put("ID"s, ++x); + app.put("ID", proc.id); apps.push_back(std::make_pair("App", std::move(app))); } @@ -727,17 +726,17 @@ void launch(bool &host_audio, resp_https_t response, req_https_t request) { return; } - auto appid = util::from_view(get_arg(args, "appid")) - 1; + auto appid = util::from_view(get_arg(args, "appid")); auto current_appid = proc::proc.running(); - if(current_appid != -1) { + if(current_appid > 0) { tree.put("root.resume", 0); tree.put("root..status_code", 400); return; } - if(appid >= 0) { + if(appid > 0) { auto err = proc::proc.execute(appid); if(err) { tree.put("root..status_code", err); @@ -777,7 +776,7 @@ void resume(bool &host_audio, resp_https_t response, req_https_t request) { } auto current_appid = proc::proc.running(); - if(current_appid == -1) { + if(current_appid == 0) { tree.put("root.resume", 0); tree.put("root..status_code", 503); @@ -826,7 +825,7 @@ void cancel(resp_https_t response, req_https_t request) { tree.put("root.cancel", 1); tree.put("root..status_code", 200); - if(proc::proc.running() != -1) { + if(proc::proc.running() > 0) { proc::proc.terminate(); } } diff --git a/src/process.cpp b/src/process.cpp index 46af3e83a18..457333dac13 100644 --- a/src/process.cpp +++ b/src/process.cpp @@ -84,14 +84,17 @@ int proc_t::execute(int app_id) { // Ensure starting from a clean slate terminate(); - if(app_id < 0 || app_id >= _apps.size()) { - BOOST_LOG(error) << "Couldn't find app with ID ["sv << app_id << ']'; + auto iter = std::find_if(_apps.begin(), _apps.end(), [&app_id](const auto app) { + return app.id == std::to_string(app_id); + }); + if(iter == _apps.end()) { + BOOST_LOG(error) << "Couldn't find app with ID ["sv << app_id << ']'; return 404; } _app_id = app_id; - auto &proc = _apps[app_id]; + auto &proc = *iter; _undo_begin = std::begin(proc.prep_cmds); _undo_it = _undo_begin; @@ -182,7 +185,7 @@ int proc_t::running() { terminate(); } - return -1; + return 0; } void proc_t::terminate() { @@ -230,16 +233,15 @@ std::vector &proc_t::get_apps() { // Returns default image if image configuration is not set. // Returns http content-type header compatible image type. std::string proc_t::get_app_image(int app_id) { - auto app_index = app_id - 1; - if(app_index < 0 || app_index >= _apps.size()) { - BOOST_LOG(error) << "Couldn't find app with ID ["sv << app_id << ']'; - return SUNSHINE_ASSETS_DIR "/box.png"; - } + auto default_image = SUNSHINE_ASSETS_DIR "/box.png"; + + auto iter = std::find_if(_apps.begin(), _apps.end(), [&app_id](const auto app) { + return app.id == std::to_string(app_id); + }); + auto app_image_path = iter == _apps.end() ? std::string() : iter->image_path; - auto default_image = SUNSHINE_ASSETS_DIR "/box.png"; - auto app_image_path = _apps[app_index].image_path; if(app_image_path.empty()) { - // image is empty, return default box image + BOOST_LOG(warning) << "Couldn't find app image for ID ["sv << app_id << ']'; return default_image; } @@ -368,6 +370,7 @@ std::optional parse(const std::string &file_name) { this_env[name] = parse_env_val(this_env, val.get_value()); } + int app_index = 1; // Start at 1, 0 indicates no app running std::vector apps; for(auto &[_, app_node] : apps_node) { proc::ctx_t ctx; @@ -379,6 +382,7 @@ std::optional parse(const std::string &file_name) { auto cmd = app_node.get_optional("cmd"s); auto image_path = app_node.get_optional("image-path"s); auto working_dir = app_node.get_optional("working-dir"s); + auto id = app_node.get_optional("id"s); std::vector prep_cmds; if(prep_nodes_opt) { @@ -424,6 +428,15 @@ std::optional parse(const std::string &file_name) { ctx.image_path = parse_env_val(this_env, *image_path); } + if(id) { + ctx.id = parse_env_val(this_env, *id); + } + else { + ctx.id = std::to_string(app_index); + } + // Always increment index to avoid order shuffling in moonlight + app_index++; + ctx.name = std::move(name); ctx.prep_cmds = std::move(prep_cmds); ctx.detached = std::move(detached); diff --git a/src/process.h b/src/process.h index eab324a7887..fa6ecc01933 100644 --- a/src/process.h +++ b/src/process.h @@ -54,6 +54,7 @@ struct ctx_t { std::string working_dir; std::string output; std::string image_path; + std::string id; }; class proc_t { @@ -62,14 +63,14 @@ class proc_t { proc_t( boost::process::environment &&env, - std::vector &&apps) : _app_id(-1), + std::vector &&apps) : _app_id(0), _env(std::move(env)), _apps(std::move(apps)) {} int execute(int app_id); /** - * @return _app_id if a process is running, otherwise returns -1 + * @return _app_id if a process is running, otherwise returns 0 */ int running(); @@ -96,6 +97,8 @@ class proc_t { file_t _pipe; std::vector::const_iterator _undo_it; std::vector::const_iterator _undo_begin; + + int app_index_from_id(int app_id); }; void refresh(const std::string &file_name); diff --git a/src/rand.h b/src/rand.h new file mode 100644 index 00000000000..62273baef13 --- /dev/null +++ b/src/rand.h @@ -0,0 +1,23 @@ +#ifndef SUNSHINE_RAND_H +#define SUNSHINE_RAND_H + +#include + +namespace util { + +static int32_t generate_int32(std::default_random_engine &engine, int32_t min, int32_t max) { + std::uniform_int_distribution dist(min, max); + + return dist(engine); +} + +static int32_t generate_int32(int32_t min, int32_t max) { + std::random_device r; + + std::default_random_engine engine { r() }; + + return util::generate_int32(engine, min, max); +} + +} // namespace util +#endif // SUNSHINE_RAND_H diff --git a/src/stream.cpp b/src/stream.cpp index e998ade5bde..b30175ba533 100644 --- a/src/stream.cpp +++ b/src/stream.cpp @@ -776,7 +776,7 @@ void controlBroadcastThread(control_server_t *server) { }) } - if(proc::proc.running() == -1) { + if(proc::proc.running() == 0) { BOOST_LOG(debug) << "Process terminated"sv; break; From 30a790ba4029ab209b6eb3e82aa2dd5ab3e5316f Mon Sep 17 00:00:00 2001 From: Tony Langhammer Date: Tue, 3 Jan 2023 22:09:07 +0100 Subject: [PATCH 63/88] Fix UPnP IGD detection (#689) --- src/upnp.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/upnp.cpp b/src/upnp.cpp index 03aedd51606..36c0692126c 100644 --- a/src/upnp.cpp +++ b/src/upnp.cpp @@ -82,6 +82,8 @@ static std::string_view status_string(int status) { case 1: return "Valid IGD device found"sv; case 2: + return "Valid IGD device found, but it isn't connected"sv; + case 3: return "A UPnP device has been found, but it wasn't recognized as an IGD"sv; } @@ -109,7 +111,7 @@ std::unique_ptr start() { IGDdatas data; auto status = UPNP_GetValidIGD(device.get(), &urls.el, &data, lan_addr.data(), lan_addr.size()); - if(status != 1) { + if(status != 1 && status != 2) { BOOST_LOG(error) << status_string(status); return nullptr; } From e4104262573ffd0c1385b454557375c7182bc0f3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 3 Jan 2023 19:07:03 -0500 Subject: [PATCH 64/88] Bump bootstrap from 5.0.0 to 5.2.3 (#638) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 6180112a62d..c9d0a9c7e90 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "dependencies": { "@fortawesome/fontawesome-free": "6.2.0", - "bootstrap": "5.0.0", + "bootstrap": "5.2.3", "vue": "2.6.12" } } From 997e8c6e5a48d128a55bf3de04f7e654729029ec Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 3 Jan 2023 19:50:59 -0500 Subject: [PATCH 65/88] Bump @fortawesome/fontawesome-free from 6.2.0 to 6.2.1 (#639) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index c9d0a9c7e90..a826e89f375 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "dependencies": { - "@fortawesome/fontawesome-free": "6.2.0", + "@fortawesome/fontawesome-free": "6.2.1", "bootstrap": "5.2.3", "vue": "2.6.12" } From cc688c78453b2f7939b705ca01ac82fb3ac73166 Mon Sep 17 00:00:00 2001 From: Cameron Gutman Date: Tue, 3 Jan 2023 21:05:00 -0600 Subject: [PATCH 66/88] Implement process and thread priority adjustments (#691) --- src/audio.cpp | 6 ++++ src/platform/common.h | 12 ++++++++ src/platform/linux/misc.cpp | 12 ++++++++ src/platform/macos/misc.cpp | 12 ++++++++ src/platform/windows/misc.cpp | 52 +++++++++++++++++++++++++++++++++++ src/stream.cpp | 20 ++++++++++++++ src/video.cpp | 14 +++++++++- tools/sunshinesvc.cpp | 2 +- 8 files changed, 128 insertions(+), 2 deletions(-) diff --git a/src/audio.cpp b/src/audio.cpp index 0512bf2af67..153da68649c 100644 --- a/src/audio.cpp +++ b/src/audio.cpp @@ -89,6 +89,9 @@ void encodeThread(sample_queue_t samples, config_t config, void *channel_data) { auto packets = mail::man->queue(mail::audio_packets); auto stream = &stream_configs[map_stream(config.channels, config.flags[config_t::HIGH_QUALITY])]; + // Encoding takes place on this thread + platf::adjust_thread_priority(platf::thread_priority_e::high); + opus_t opus { opus_multistream_encoder_create( stream->sampleRate, stream->channelCount, @@ -173,6 +176,9 @@ void capture(safe::mail_t mail, config_t config, void *channel_data) { } } + // Capture takes place on this thread + platf::adjust_thread_priority(platf::thread_priority_e::critical); + auto samples = std::make_shared(30); std::thread thread { encodeThread, samples, config, channel_data }; diff --git a/src/platform/common.h b/src/platform/common.h index 79e55cda343..c6bdfad8661 100644 --- a/src/platform/common.h +++ b/src/platform/common.h @@ -306,6 +306,18 @@ std::vector display_names(mem_type_e hwdevice_type); boost::process::child run_unprivileged(const std::string &cmd, boost::filesystem::path &working_dir, boost::process::environment &env, FILE *file, std::error_code &ec); +enum class thread_priority_e : int { + low, + normal, + high, + critical +}; +void adjust_thread_priority(thread_priority_e priority); + +// Allow OS-specific actions to be taken to prepare for streaming +void streaming_will_start(); +void streaming_will_stop(); + input_t input(); void move_mouse(input_t &input, int deltaX, int deltaY); void abs_mouse(input_t &input, const touch_port_t &touch_port, float x, float y); diff --git a/src/platform/linux/misc.cpp b/src/platform/linux/misc.cpp index 1fbf9c5b4a3..1a4c30d6d6f 100644 --- a/src/platform/linux/misc.cpp +++ b/src/platform/linux/misc.cpp @@ -153,6 +153,18 @@ bp::child run_unprivileged(const std::string &cmd, boost::filesystem::path &work } } +void adjust_thread_priority(thread_priority_e priority) { + // Unimplemented +} + +void streaming_will_start() { + // Nothing to do +} + +void streaming_will_stop() { + // Nothing to do +} + namespace source { enum source_e : std::size_t { #ifdef SUNSHINE_BUILD_CUDA diff --git a/src/platform/macos/misc.cpp b/src/platform/macos/misc.cpp index 64a7ef4207a..0bfef608e15 100644 --- a/src/platform/macos/misc.cpp +++ b/src/platform/macos/misc.cpp @@ -131,6 +131,18 @@ bp::child run_unprivileged(const std::string &cmd, boost::filesystem::path &work } } +void adjust_thread_priority(thread_priority_e priority) { + // Unimplemented +} + +void streaming_will_start() { + // Nothing to do +} + +void streaming_will_stop() { + // Nothing to do +} + } // namespace platf namespace dyn { diff --git a/src/platform/windows/misc.cpp b/src/platform/windows/misc.cpp index a2417d2e4f7..4f9d211c708 100644 --- a/src/platform/windows/misc.cpp +++ b/src/platform/windows/misc.cpp @@ -13,9 +13,12 @@ #include #include #include +#include +#include // clang-format on #include "src/main.h" +#include "src/platform/common.h" #include "src/utility.h" namespace bp = boost::process; @@ -470,4 +473,53 @@ bp::child run_unprivileged(const std::string &cmd, boost::filesystem::path &work } } +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; + } + + if(!SetThreadPriority(GetCurrentThread(), win32_priority)) { + auto winerr = GetLastError(); + BOOST_LOG(warning) << "Unable to set thread priority to "sv << win32_priority << ": "sv << winerr; + } +} + +void streaming_will_start() { + // Enable MMCSS scheduling for DWM + DwmEnableMMCSS(true); + + // Reduce timer period to 1ms + timeBeginPeriod(1); + + // Promote ourselves to high priority class + SetPriorityClass(GetCurrentProcess(), HIGH_PRIORITY_CLASS); +} + +void streaming_will_stop() { + // Demote ourselves back to normal priority class + SetPriorityClass(GetCurrentProcess(), NORMAL_PRIORITY_CLASS); + + // End our 1ms timer request + timeEndPeriod(1); + + // Disable MMCSS scheduling for DWM + DwmEnableMMCSS(false); +} + } // namespace platf \ No newline at end of file diff --git a/src/stream.cpp b/src/stream.cpp index b30175ba533..f567c4cf6ec 100644 --- a/src/stream.cpp +++ b/src/stream.cpp @@ -740,6 +740,8 @@ void controlBroadcastThread(control_server_t *server) { input::passthrough(session->input, std::move(plaintext)); }); + // This thread handles latency-sensitive control messages + platf::adjust_thread_priority(platf::thread_priority_e::critical); auto shutdown_event = mail::man->event(mail::broadcast_shutdown); while(!shutdown_event->peek()) { @@ -905,6 +907,9 @@ void videoBroadcastThread(udp::socket &sock) { auto packets = mail::man->queue(mail::video_packets); auto timebase = boost::posix_time::microsec_clock::universal_time(); + // Video traffic is sent on this thread + platf::adjust_thread_priority(platf::thread_priority_e::high); + while(auto packet = packets->pop()) { if(shutdown_event->peek()) { break; @@ -1079,6 +1084,9 @@ void audioBroadcastThread(udp::socket &sock) { audio_packet->rtp.packetType = 97; audio_packet->rtp.ssrc = 0; + // Audio traffic is sent on this thread + platf::adjust_thread_priority(platf::thread_priority_e::high); + while(auto packet = packets->pop()) { if(shutdown_event->peek()) { break; @@ -1333,6 +1341,8 @@ void audioThread(session_t *session) { } namespace session { +std::atomic_uint running_sessions; + state_e state(session_t &session) { return session.state.load(std::memory_order_relaxed); } @@ -1394,6 +1404,11 @@ void join(session_t &session) { } } + // If this is the last session, invoke the platform callbacks + if(--running_sessions == 0) { + platf::streaming_will_stop(); + } + BOOST_LOG(debug) << "Session ended"sv; } @@ -1432,6 +1447,11 @@ int start(session_t &session, const std::string &addr_string) { session.state.store(state_e::RUNNING, std::memory_order_relaxed); + // If this is the first session, invoke the platform callbacks + if(++running_sessions == 1) { + platf::streaming_will_start(); + } + return 0; } diff --git a/src/video.cpp b/src/video.cpp index 9529ce1f524..ab8cd25f186 100644 --- a/src/video.cpp +++ b/src/video.cpp @@ -673,6 +673,9 @@ void captureThread( } } + // Capture takes place on this thread + platf::adjust_thread_priority(platf::thread_priority_e::critical); + while(capture_ctx_queue->running()) { bool artificial_reinit = false; @@ -703,7 +706,10 @@ void captureThread( } auto &next_img = *round_robin++; - while(next_img.use_count() > 1) {} + while(next_img.use_count() > 1) { + // Sleep a bit to avoid starving the encoder threads + std::this_thread::sleep_for(2ms); + } return next_img; }, @@ -1335,6 +1341,9 @@ void captureThreadSync() { } }); + // Encoding and capture takes place on this thread + platf::adjust_thread_priority(platf::thread_priority_e::high); + while(encode_run_sync(synced_session_ctxs, ctx) == encode_e::reinit) {} } @@ -1367,6 +1376,9 @@ void capture_async( auto touch_port_event = mail->event(mail::touch_port); + // Encoding takes place on this thread + platf::adjust_thread_priority(platf::thread_priority_e::high); + while(!shutdown_event->peek() && images->running()) { // Wait for the main capture event when the display is being reinitialized if(ref->reinit_event.peek()) { diff --git a/tools/sunshinesvc.cpp b/tools/sunshinesvc.cpp index 9089be307b2..13836e5f040 100644 --- a/tools/sunshinesvc.cpp +++ b/tools/sunshinesvc.cpp @@ -271,7 +271,7 @@ VOID WINAPI ServiceMain(DWORD dwArgc, LPTSTR *lpszArgv) { NULL, NULL, TRUE, - ABOVE_NORMAL_PRIORITY_CLASS | CREATE_UNICODE_ENVIRONMENT | CREATE_NO_WINDOW | EXTENDED_STARTUPINFO_PRESENT, + CREATE_UNICODE_ENVIRONMENT | CREATE_NO_WINDOW | EXTENDED_STARTUPINFO_PRESENT, NULL, NULL, (LPSTARTUPINFOW)&startup_info, From a3922d9eef0b6ff9258b517fc24a2a6658b308ee Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 3 Jan 2023 22:41:25 -0500 Subject: [PATCH 67/88] Bump third-party/moonlight-common-c from `9da6329` to `ef9ad52` (#690) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- third-party/moonlight-common-c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/third-party/moonlight-common-c b/third-party/moonlight-common-c index 9da63294964..ef9ad529a49 160000 --- a/third-party/moonlight-common-c +++ b/third-party/moonlight-common-c @@ -1 +1 @@ -Subproject commit 9da632949649e8b6ea30887fdcbdc12c7c540b38 +Subproject commit ef9ad529a493d699724d84a189cc1899afcc2d72 From ff883058e680bfb7f81335f0bc4f87a329320fab Mon Sep 17 00:00:00 2001 From: Jackson Garner Date: Wed, 4 Jan 2023 07:13:09 -0700 Subject: [PATCH 68/88] Fix wayland capture on nvidia (#649) --- src/platform/linux/wlgrab.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/platform/linux/wlgrab.cpp b/src/platform/linux/wlgrab.cpp index 31d5cfa062b..486ed8e4026 100644 --- a/src/platform/linux/wlgrab.cpp +++ b/src/platform/linux/wlgrab.cpp @@ -162,6 +162,12 @@ class wlr_ram_t : public wlr_t { } 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_WIDTH, &h); + BOOST_LOG(debug) << "width and height: w "sv << w << ' h ' << h; + gl::ctx.GetTextureSubImage((*rgb_opt)->tex[0], 0, 0, 0, 0, width, height, 1, GL_BGRA, GL_UNSIGNED_BYTE, img_out_base->height * img_out_base->row_pitch, img_out_base->data); gl::ctx.BindTexture(GL_TEXTURE_2D, 0); @@ -366,4 +372,4 @@ std::vector wl_display_names() { return display_names; } -} // namespace platf \ No newline at end of file +} // namespace platf From 1c1a7fa8c035e0a29456287aee839c4aa9ae7a42 Mon Sep 17 00:00:00 2001 From: Cameron Gutman Date: Wed, 4 Jan 2023 09:08:33 -0600 Subject: [PATCH 69/88] Fix 7.1 surround channel mapping (#694) --- src/platform/common.h | 1 - 1 file changed, 1 deletion(-) diff --git a/src/platform/common.h b/src/platform/common.h index c6bdfad8661..4a778269798 100644 --- a/src/platform/common.h +++ b/src/platform/common.h @@ -92,7 +92,6 @@ constexpr std::uint8_t map_surround71[] { FRONT_RIGHT, FRONT_CENTER, LOW_FREQUENCY, - LOW_FREQUENCY, BACK_LEFT, BACK_RIGHT, SIDE_LEFT, From 76896eba885b2fcffaa21a64b8caca08094df846 Mon Sep 17 00:00:00 2001 From: Cameron Gutman Date: Wed, 4 Jan 2023 10:18:28 -0600 Subject: [PATCH 70/88] Fix NVENC profile values not applying (#695) --- src/video.cpp | 64 ++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 48 insertions(+), 16 deletions(-) diff --git a/src/video.cpp b/src/video.cpp index ab8cd25f186..cd1db64acde 100644 --- a/src/video.cpp +++ b/src/video.cpp @@ -286,12 +286,6 @@ struct encoder_t { option_t(std::string &&name, decltype(value) &&value) : name { std::move(name) }, value { std::move(value) } {} }; - struct { - int h264_high; - int hevc_main; - int hevc_main_10; - } profile; - AVHWDeviceType dev_type; AVPixelFormat dev_pix_fmt; @@ -299,7 +293,9 @@ struct encoder_t { AVPixelFormat dynamic_pix_fmt; struct { - std::vector options; + std::vector common_options; + std::vector sdr_options; + std::vector hdr_options; std::optional qp; std::string name; @@ -407,7 +403,6 @@ auto capture_thread_sync = safe::make_shared(start_c static encoder_t nvenc { "nvenc"sv, - { (int)nv::profile_h264_e::high, (int)nv::profile_hevc_e::main, (int)nv::profile_hevc_e::main_10 }, #ifdef _WIN32 AV_HWDEVICE_TYPE_D3D11VA, AV_PIX_FMT_D3D11, @@ -417,6 +412,7 @@ static encoder_t nvenc { #endif AV_PIX_FMT_NV12, AV_PIX_FMT_P010, { + // Common options { { "delay"s, 0 }, { "forced-idr"s, 1 }, @@ -425,6 +421,14 @@ static encoder_t nvenc { { "tune"s, &config::video.nv.tune }, { "rc"s, &config::video.nv.rc }, }, + // SDR-specific options + { + { "profile"s, (int)nv::profile_hevc_e::main }, + }, + // HDR-specific options + { + { "profile"s, (int)nv::profile_hevc_e::main_10 }, + }, std::nullopt, "hevc_nvenc"s, }, @@ -438,6 +442,11 @@ static encoder_t nvenc { { "rc"s, &config::video.nv.rc }, { "coder"s, &config::video.nv.coder }, }, + // SDR-specific options + { + { "profile"s, (int)nv::profile_h264_e::high }, + }, + {}, // HDR-specific options std::make_optional({ "qp"s, &config::video.qp }), "h264_nvenc"s, }, @@ -452,11 +461,11 @@ static encoder_t nvenc { #ifdef _WIN32 static encoder_t amdvce { "amdvce"sv, - { FF_PROFILE_H264_HIGH, FF_PROFILE_HEVC_MAIN }, AV_HWDEVICE_TYPE_D3D11VA, AV_PIX_FMT_D3D11, AV_PIX_FMT_NV12, AV_PIX_FMT_P010, { + // Common options { { "enforce_hrd"s, true }, { "gops_per_idr"s, 1 }, @@ -468,10 +477,13 @@ static encoder_t amdvce { { "usage"s, "ultralowlatency"s }, { "vbaq"s, true }, }, + {}, // SDR-specific options + {}, // HDR-specific options std::make_optional({ "qp_p"s, &config::video.qp }), "hevc_amf"s, }, { + // Common options { { "enforce_hrd"s, true }, { "log_to_dbg"s, "1"s }, @@ -482,6 +494,8 @@ static encoder_t amdvce { { "usage"s, "ultralowlatency"s }, { "vbaq"s, true }, }, + {}, // SDR-specific options + {}, // HDR-specific options std::make_optional({ "qp_p"s, &config::video.qp }), "h264_amf"s, }, @@ -492,7 +506,6 @@ static encoder_t amdvce { static encoder_t software { "software"sv, - { FF_PROFILE_H264_HIGH, FF_PROFILE_HEVC_MAIN, FF_PROFILE_HEVC_MAIN_10 }, AV_HWDEVICE_TYPE_NONE, AV_PIX_FMT_NONE, AV_PIX_FMT_YUV420P, AV_PIX_FMT_YUV420P10, @@ -507,14 +520,19 @@ static encoder_t software { { "preset"s, &config::video.sw.preset }, { "tune"s, &config::video.sw.tune }, }, + {}, // SDR-specific options + {}, // HDR-specific options std::make_optional("qp"s, &config::video.qp), "libx265"s, }, { + // Common options { { "preset"s, &config::video.sw.preset }, { "tune"s, &config::video.sw.tune }, }, + {}, // SDR-specific options + {}, // HDR-specific options std::make_optional("qp"s, &config::video.qp), "libx264"s, }, @@ -526,25 +544,30 @@ static encoder_t software { #ifdef __linux__ static encoder_t vaapi { "vaapi"sv, - { FF_PROFILE_H264_HIGH, FF_PROFILE_HEVC_MAIN, FF_PROFILE_HEVC_MAIN_10 }, AV_HWDEVICE_TYPE_VAAPI, AV_PIX_FMT_VAAPI, AV_PIX_FMT_NV12, AV_PIX_FMT_YUV420P10, { + // Common options { { "async_depth"s, 1 }, { "sei"s, 0 }, { "idr_interval"s, std::numeric_limits::max() }, }, + {}, // SDR-specific options + {}, // HDR-specific options std::make_optional("qp"s, &config::video.qp), "hevc_vaapi"s, }, { + // Common options { { "async_depth"s, 1 }, { "sei"s, 0 }, { "idr_interval"s, std::numeric_limits::max() }, }, + {}, // SDR-specific options + {}, // HDR-specific options std::make_optional("qp"s, &config::video.qp), "h264_vaapi"s, }, @@ -557,25 +580,30 @@ static encoder_t vaapi { #ifdef __APPLE__ static encoder_t videotoolbox { "videotoolbox"sv, - { FF_PROFILE_H264_HIGH, FF_PROFILE_HEVC_MAIN, FF_PROFILE_HEVC_MAIN_10 }, AV_HWDEVICE_TYPE_NONE, AV_PIX_FMT_VIDEOTOOLBOX, AV_PIX_FMT_NV12, AV_PIX_FMT_NV12, { + // Common options { { "allow_sw"s, &config::video.vt.allow_sw }, { "require_sw"s, &config::video.vt.require_sw }, { "realtime"s, &config::video.vt.realtime }, }, + {}, // SDR-specific options + {}, // HDR-specific options std::nullopt, "hevc_videotoolbox"s, }, { + // Common options { { "allow_sw"s, &config::video.vt.allow_sw }, { "require_sw"s, &config::video.vt.require_sw }, { "realtime"s, &config::video.vt.realtime }, }, + {}, // SDR-specific options + {}, // HDR-specific options std::nullopt, "h264_videotoolbox"s, }, @@ -865,13 +893,13 @@ std::optional make_session(const encoder_t &encoder, const config_t & ctx->framerate = AVRational { config.framerate, 1 }; if(config.videoFormat == 0) { - ctx->profile = encoder.profile.h264_high; + ctx->profile = FF_PROFILE_H264_HIGH; } else if(config.dynamicRange == 0) { - ctx->profile = encoder.profile.hevc_main; + ctx->profile = FF_PROFILE_HEVC_MAIN; } else { - ctx->profile = encoder.profile.hevc_main_10; + ctx->profile = FF_PROFILE_HEVC_MAIN_10; } // B-frames delay decoder output, so never use them @@ -984,7 +1012,11 @@ std::optional make_session(const encoder_t &encoder, const config_t & option.value); }; - for(auto &option : video_format.options) { + // Apply common options, then format-specific overrides + for(auto &option : video_format.common_options) { + handle_option(option); + } + for(auto &option : (config.dynamicRange ? video_format.hdr_options : video_format.sdr_options)) { handle_option(option); } From c72aeef67bbcc31406525eda46e6586f8cb769d7 Mon Sep 17 00:00:00 2001 From: Cameron Gutman Date: Wed, 4 Jan 2023 10:50:36 -0600 Subject: [PATCH 71/88] Only increase rc_buffer_size when software encoding with more than one slice (#692) --- src/video.cpp | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/src/video.cpp b/src/video.cpp index cd1db64acde..89646dda5b5 100644 --- a/src/video.cpp +++ b/src/video.cpp @@ -1021,11 +1021,19 @@ std::optional make_session(const encoder_t &encoder, const config_t & } if(video_format[encoder_t::CBR]) { - auto bitrate = config.bitrate * 1000; - ctx->rc_max_rate = bitrate; - ctx->rc_buffer_size = bitrate / ((config.framerate * 10) / 15); - ctx->bit_rate = bitrate; - ctx->rc_min_rate = bitrate; + auto bitrate = config.bitrate * 1000; + ctx->rc_max_rate = bitrate; + ctx->bit_rate = bitrate; + ctx->rc_min_rate = bitrate; + + if(!hardware && ctx->slices > 1) { + // Use a larger rc_buffer_size for software encoding when slices are enabled, + // because libx264 can severely degrade quality if the buffer is too small. + ctx->rc_buffer_size = bitrate / ((config.framerate * 10) / 15); + } + else { + ctx->rc_buffer_size = bitrate / config.framerate; + } } else if(video_format.qp) { handle_option(*video_format.qp); From 1a929cc37ba38db647154d66a42134ef0603f2df Mon Sep 17 00:00:00 2001 From: Dmitry Khlestkov Date: Wed, 4 Jan 2023 20:24:20 +0300 Subject: [PATCH 72/88] Fix origin_web_ui_allowed binding (#697) --- src_assets/common/assets/web/config.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src_assets/common/assets/web/config.html b/src_assets/common/assets/web/config.html index 99e71a9241a..849ccac626a 100644 --- a/src_assets/common/assets/web/config.html +++ b/src_assets/common/assets/web/config.html @@ -860,7 +860,7 @@

Configuration

this.config.origin_pin_allowed = this.config.origin_pin_allowed || "pc"; this.config.origin_web_ui_allowed = - this.config.origin_web_manager_allowed || "lan"; + this.config.origin_web_ui_allowed || "lan"; this.config.hevc_mode = this.config.hevc_mode || 0; this.config.encoder = this.config.encoder || ""; this.config.nv_preset = this.config.nv_preset || "p4"; From 08cb5fc2f2375d6ea84abf850ed2e2e694faed02 Mon Sep 17 00:00:00 2001 From: Cameron Gutman Date: Wed, 4 Jan 2023 17:44:23 -0600 Subject: [PATCH 73/88] Minor UI fixes (#696) --- docs/source/about/advanced_usage.rst | 4 +-- src_assets/common/assets/web/config.html | 40 ++++++++++++++---------- 2 files changed, 25 insertions(+), 19 deletions(-) diff --git a/docs/source/about/advanced_usage.rst b/docs/source/about/advanced_usage.rst index ab46db3a27f..f208772889d 100644 --- a/docs/source/about/advanced_usage.rst +++ b/docs/source/about/advanced_usage.rst @@ -667,7 +667,7 @@ qp ^^ **Description** - Quantitization Parameter. Some devices don't support Constant Bit Rate. For those devices, QP is used instead. + Quantization Parameter. Some devices don't support Constant Bit Rate. For those devices, QP is used instead. .. Warning:: Higher value means more compression, but less quality. @@ -683,7 +683,7 @@ min_threads ^^^^^^^^^^^ **Description** - Minimum number of threads used by ffmpeg to encode the video. + Minimum number of threads used for software encoding. .. Note:: Increasing the value slightly reduces encoding efficiency, but the tradeoff is usually worth it to gain the use of more CPU cores for encoding. The ideal value is the lowest value that can reliably encode at your diff --git a/src_assets/common/assets/web/config.html b/src_assets/common/assets/web/config.html index 849ccac626a..bb961c624d8 100644 --- a/src_assets/common/assets/web/config.html +++ b/src_assets/common/assets/web/config.html @@ -461,7 +461,7 @@

Configuration

- + Configuration v-model="config.qp" />
- Quantitization Parameter
+ Quantization Parameter
Some devices may not support Constant Bit Rate.
For those devices, QP is used instead.
Higher value means more compression, but less quality
@@ -479,7 +479,7 @@

Configuration

Minimum Software Encoding Thread Count Configuration v-model="config.min_threads" />
- Minimum number of threads used by ffmpeg to encode the video.
Increasing the value slightly reduces encoding efficiency, but the tradeoff is usually
worth it to gain the use of more CPU cores for encoding. The ideal value is the lowest
value that can reliably encode at your desired streaming settings on your hardware. @@ -523,9 +522,10 @@

Configuration

@@ -727,6 +727,7 @@

Configuration

+
Configuration id: "advanced", name: "Advanced", }, - { - id: "sw", - name: "Software Encoder", - }, { id: "nv", - name: "NVENC Encoder", + name: "NVIDIA NVENC Encoder", }, { id: "amd", - name: "AMF Encoder", + name: "AMD AMF Encoder", }, { id: "va-api", - name: "VA-API encoder", + name: "VA-API Encoder", }, { id: "vt", - name: "VideoToolbox encoder", + name: "VideoToolbox Encoder", + }, + { + id: "sw", + name: "Software Encoder", }, ], }; @@ -839,12 +840,17 @@

Configuration

var app = document.getElementById("app"); if (this.platform == "windows") { this.tabs = this.tabs.filter((el) => { - return el.id !== "va-api"; + return el.id !== "va-api" && el.id !== "vt"; }); } if (this.platform == "linux") { this.tabs = this.tabs.filter((el) => { - return el.id !== "amd"; + return el.id !== "amd" && el.id !== "vt"; + }); + } + if (this.platform == "macos") { + this.tabs = this.tabs.filter((el) => { + return el.id !== "amd" && el.id !== "nv" && el.id !== "va-api"; }); } From f1c225fccc66f2c929076d2bc212805a55674172 Mon Sep 17 00:00:00 2001 From: Cameron Gutman Date: Thu, 5 Jan 2023 08:05:25 -0600 Subject: [PATCH 74/88] Terminate Sunshine if a session doesn't terminate for more than 10 seconds (#707) --- src/stream.cpp | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/stream.cpp b/src/stream.cpp index f567c4cf6ec..6b470ddb3c3 100644 --- a/src/stream.cpp +++ b/src/stream.cpp @@ -1359,6 +1359,20 @@ void stop(session_t &session) { } void join(session_t &session) { + // Current Nvidia drivers have a bug where NVENC can deadlock the encoder thread with hardware-accelerated + // GPU scheduling enabled. If this happens, we will terminate ourselves and the service can restart. + // The alternative is that Sunshine can never start another session until it's manually restarted. + auto task = []() { + BOOST_LOG(fatal) << "Hang detected! Session failed to terminate in 10 seconds."sv; + log_flush(); + std::abort(); + }; + auto force_kill = task_pool.pushDelayed(task, 10s).task_id; + auto fg = util::fail_guard([&force_kill]() { + // Cancel the kill task if we manage to return from this function + task_pool.cancel(force_kill); + }); + BOOST_LOG(debug) << "Waiting for video to end..."sv; session.videoThread.join(); BOOST_LOG(debug) << "Waiting for audio to end..."sv; From 00aa23b3424701bbae1fa73315a22ea3d10231ed Mon Sep 17 00:00:00 2001 From: Cameron Gutman Date: Thu, 5 Jan 2023 10:21:38 -0600 Subject: [PATCH 75/88] Fix hang on stream termination if no frames can be captured (#709) --- src/platform/common.h | 5 +++-- src/platform/linux/cuda.cpp | 6 +++--- src/platform/linux/kmsgrab.cpp | 12 ++++++------ src/platform/linux/wlgrab.cpp | 10 ++++++---- src/platform/linux/x11grab.cpp | 12 ++++++------ src/platform/macos/display.mm | 3 ++- src/platform/windows/display_ram.cpp | 5 +++-- src/platform/windows/display_vram.cpp | 5 +++-- src/video.cpp | 11 +++++++---- 9 files changed, 39 insertions(+), 30 deletions(-) diff --git a/src/platform/common.h b/src/platform/common.h index 4a778269798..732b56bb8f3 100644 --- a/src/platform/common.h +++ b/src/platform/common.h @@ -216,7 +216,8 @@ enum class capture_e : int { class display_t { public: /** - * When display has a new image ready, this callback will be called with the new image. + * 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 nullptr @@ -225,7 +226,7 @@ class display_t { * Returns the image object that should be filled next. * This may or may not be the image send with the callback */ - using snapshot_cb_t = std::function(std::shared_ptr &img)>; + using snapshot_cb_t = std::function(std::shared_ptr &img, bool frame_captured)>; display_t() noexcept : offset_x { 0 }, offset_y { 0 } {} diff --git a/src/platform/linux/cuda.cpp b/src/platform/linux/cuda.cpp index faef891d828..963d7c35982 100644 --- a/src/platform/linux/cuda.cpp +++ b/src/platform/linux/cuda.cpp @@ -505,10 +505,10 @@ class display_t : public platf::display_t { case platf::capture_e::error: return status; case platf::capture_e::timeout: - std::this_thread::sleep_for(1ms); - continue; + img = snapshot_cb(img, false); + break; case platf::capture_e::ok: - img = snapshot_cb(img); + img = snapshot_cb(img, true); break; default: BOOST_LOG(error) << "Unrecognized capture status ["sv << (int)status << ']'; diff --git a/src/platform/linux/kmsgrab.cpp b/src/platform/linux/kmsgrab.cpp index 8a756716cdd..2c84baeaf2e 100644 --- a/src/platform/linux/kmsgrab.cpp +++ b/src/platform/linux/kmsgrab.cpp @@ -684,10 +684,10 @@ class display_ram_t : public display_t { case platf::capture_e::error: return status; case platf::capture_e::timeout: - std::this_thread::sleep_for(1ms); - continue; + img = snapshot_cb(img, false); + break; case platf::capture_e::ok: - img = snapshot_cb(img); + img = snapshot_cb(img, true); break; default: BOOST_LOG(error) << "Unrecognized capture status ["sv << (int)status << ']'; @@ -805,10 +805,10 @@ class display_vram_t : public display_t { case platf::capture_e::error: return status; case platf::capture_e::timeout: - std::this_thread::sleep_for(1ms); - continue; + img = snapshot_cb(img, false); + break; case platf::capture_e::ok: - img = snapshot_cb(img); + img = snapshot_cb(img, true); break; default: BOOST_LOG(error) << "Unrecognized capture status ["sv << (int)status << ']'; diff --git a/src/platform/linux/wlgrab.cpp b/src/platform/linux/wlgrab.cpp index 486ed8e4026..20d8957236f 100644 --- a/src/platform/linux/wlgrab.cpp +++ b/src/platform/linux/wlgrab.cpp @@ -134,9 +134,10 @@ class wlr_ram_t : public wlr_t { case platf::capture_e::error: return status; case platf::capture_e::timeout: - continue; + img = snapshot_cb(img, false); + break; case platf::capture_e::ok: - img = snapshot_cb(img); + img = snapshot_cb(img, true); break; default: BOOST_LOG(error) << "Unrecognized capture status ["sv << (int)status << ']'; @@ -239,9 +240,10 @@ class wlr_vram_t : public wlr_t { case platf::capture_e::error: return status; case platf::capture_e::timeout: - continue; + img = snapshot_cb(img, false); + break; case platf::capture_e::ok: - img = snapshot_cb(img); + img = snapshot_cb(img, true); break; default: BOOST_LOG(error) << "Unrecognized capture status ["sv << (int)status << ']'; diff --git a/src/platform/linux/x11grab.cpp b/src/platform/linux/x11grab.cpp index c024f4d8f40..1f8f5b73a1c 100644 --- a/src/platform/linux/x11grab.cpp +++ b/src/platform/linux/x11grab.cpp @@ -476,10 +476,10 @@ struct x11_attr_t : public display_t { case platf::capture_e::error: return status; case platf::capture_e::timeout: - std::this_thread::sleep_for(1ms); - continue; + img = snapshot_cb(img, false); + break; case platf::capture_e::ok: - img = snapshot_cb(img); + img = snapshot_cb(img, true); break; default: BOOST_LOG(error) << "Unrecognized capture status ["sv << (int)status << ']'; @@ -587,10 +587,10 @@ struct shm_attr_t : public x11_attr_t { case platf::capture_e::error: return status; case platf::capture_e::timeout: - std::this_thread::sleep_for(1ms); - continue; + img = snapshot_cb(img, false); + break; case platf::capture_e::ok: - img = snapshot_cb(img); + img = snapshot_cb(img, true); break; default: BOOST_LOG(error) << "Unrecognized capture status ["sv << (int)status << ']'; diff --git a/src/platform/macos/display.mm b/src/platform/macos/display.mm index 8845fbacbfc..a53ac0e686c 100644 --- a/src/platform/macos/display.mm +++ b/src/platform/macos/display.mm @@ -60,11 +60,12 @@ capture_e capture(snapshot_cb_t &&snapshot_cb, std::shared_ptr img, bool img_next->row_pitch = CVPixelBufferGetBytesPerRow(pixelBuffer); img_next->pixel_pitch = img_next->row_pitch / img_next->width; - img_next = snapshot_cb(img_next); + img_next = snapshot_cb(img_next, true); return img_next != nullptr; }]; + // FIXME: We should time out if an image isn't returned for a while dispatch_semaphore_wait(signal, DISPATCH_TIME_FOREVER); return capture_e::ok; diff --git a/src/platform/windows/display_ram.cpp b/src/platform/windows/display_ram.cpp index b5c026dbb6d..c5ce6dc3280 100644 --- a/src/platform/windows/display_ram.cpp +++ b/src/platform/windows/display_ram.cpp @@ -181,10 +181,11 @@ capture_e display_ram_t::capture(snapshot_cb_t &&snapshot_cb, std::shared_ptr<:: case platf::capture_e::error: return status; case platf::capture_e::timeout: + img = snapshot_cb(img, false); std::this_thread::sleep_for(1ms); - continue; + break; case platf::capture_e::ok: - img = snapshot_cb(img); + img = snapshot_cb(img, true); break; default: BOOST_LOG(error) << "Unrecognized capture status ["sv << (int)status << ']'; diff --git a/src/platform/windows/display_vram.cpp b/src/platform/windows/display_vram.cpp index 75e77b6e23a..354942b477e 100644 --- a/src/platform/windows/display_vram.cpp +++ b/src/platform/windows/display_vram.cpp @@ -652,10 +652,11 @@ capture_e display_vram_t::capture(snapshot_cb_t &&snapshot_cb, std::shared_ptr<: case platf::capture_e::error: return status; case platf::capture_e::timeout: + img = snapshot_cb(img, false); std::this_thread::sleep_for(1ms); - continue; + break; case platf::capture_e::ok: - img = snapshot_cb(img); + img = snapshot_cb(img, true); break; default: BOOST_LOG(error) << "Unrecognized capture status ["sv << (int)status << ']'; diff --git a/src/video.cpp b/src/video.cpp index 89646dda5b5..8896216750b 100644 --- a/src/video.cpp +++ b/src/video.cpp @@ -707,7 +707,7 @@ void captureThread( while(capture_ctx_queue->running()) { bool artificial_reinit = false; - auto status = disp->capture([&](std::shared_ptr &img) -> std::shared_ptr { + auto status = disp->capture([&](std::shared_ptr &img, bool frame_captured) -> std::shared_ptr { KITTY_WHILE_LOOP(auto capture_ctx = std::begin(capture_ctxs), capture_ctx != std::end(capture_ctxs), { if(!capture_ctx->images->running()) { capture_ctx = capture_ctxs.erase(capture_ctx); @@ -715,7 +715,10 @@ void captureThread( continue; } - capture_ctx->images->raise(img); + if(frame_captured) { + capture_ctx->images->raise(img); + } + ++capture_ctx; }) @@ -1274,7 +1277,7 @@ encode_e encode_run_sync( auto ec = platf::capture_e::ok; while(encode_session_ctx_queue.running()) { - auto snapshot_cb = [&](std::shared_ptr &img) -> std::shared_ptr { + auto snapshot_cb = [&](std::shared_ptr &img, bool frame_captured) -> std::shared_ptr { while(encode_session_ctx_queue.peek()) { auto encode_session_ctx = encode_session_ctx_queue.pop(); if(!encode_session_ctx) { @@ -1318,7 +1321,7 @@ encode_e encode_run_sync( ctx->idr_events->pop(); } - if(pos->session.device->convert(*img)) { + if(frame_captured && pos->session.device->convert(*img)) { BOOST_LOG(error) << "Could not convert image"sv; ctx->shutdown_event->raise(true); From 5980e520b94b8081bba52b27c9b9941d763dddb1 Mon Sep 17 00:00:00 2001 From: Cameron Gutman Date: Thu, 5 Jan 2023 11:28:40 -0600 Subject: [PATCH 76/88] Improve robustness of encoder selection (#710) --- src/video.cpp | 98 ++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 78 insertions(+), 20 deletions(-) diff --git a/src/video.cpp b/src/video.cpp index 8896216750b..020ab77c331 100644 --- a/src/video.cpp +++ b/src/video.cpp @@ -1029,9 +1029,11 @@ std::optional make_session(const encoder_t &encoder, const config_t & ctx->bit_rate = bitrate; ctx->rc_min_rate = bitrate; - if(!hardware && ctx->slices > 1) { + if(!hardware && (ctx->slices > 1 || config.videoFormat != 0)) { // Use a larger rc_buffer_size for software encoding when slices are enabled, // because libx264 can severely degrade quality if the buffer is too small. + // libx265 encounters this issue more frequently, so always scale the + // buffer by 1.5x for software HEVC encoding. ctx->rc_buffer_size = bitrate / ((config.framerate * 10) / 15); } else { @@ -1694,36 +1696,92 @@ bool validate_encoder(encoder_t &encoder) { } int init() { - BOOST_LOG(info) << "// Testing for available encoders, this may generate errors. You can safely ignore those errors. //"sv; + bool encoder_found = false; + if(!config::video.encoder.empty()) { + // If there is a specific encoder specified, use it if it passes validation + KITTY_WHILE_LOOP(auto pos = std::begin(encoders), pos != std::end(encoders), { + auto encoder = *pos; + + if(encoder.name == config::video.encoder) { + // Remove the encoder from the list entirely if it fails validation + if(!validate_encoder(encoder)) { + pos = encoders.erase(pos); + break; + } - KITTY_WHILE_LOOP(auto pos = std::begin(encoders), pos != std::end(encoders), { - if( - (!config::video.encoder.empty() && pos->name != config::video.encoder) || - !validate_encoder(*pos) || - (config::video.hevc_mode == 3 && !pos->hevc[encoder_t::DYNAMIC_RANGE])) { - pos = encoders.erase(pos); + // If we can't satisfy both the encoder and HDR requirement, prefer the encoder over HDR support + if(config::video.hevc_mode == 3 && !encoder.hevc[encoder_t::DYNAMIC_RANGE]) { + BOOST_LOG(warning) << "Encoder ["sv << config::video.encoder << "] does not support HDR on this system"sv; + config::video.hevc_mode = 0; + } - continue; + encoders.clear(); + encoders.emplace_back(encoder); + encoder_found = true; + break; + } + + pos++; + }); + + if(!encoder_found) { + BOOST_LOG(error) << "Couldn't find any working encoder matching ["sv << config::video.encoder << ']'; + config::video.encoder.clear(); } + } - break; - }) + BOOST_LOG(info) << "// Testing for available encoders, this may generate errors. You can safely ignore those errors. //"sv; - BOOST_LOG(info); - BOOST_LOG(info) << "// Ignore any errors mentioned above, they are not relevant. //"sv; - BOOST_LOG(info); + // If we haven't found an encoder yet but we want one with HDR support, search for that now. + if(!encoder_found && config::video.hevc_mode == 3) { + KITTY_WHILE_LOOP(auto pos = std::begin(encoders), pos != std::end(encoders), { + auto encoder = *pos; - if(encoders.empty()) { - if(config::video.encoder.empty()) { - BOOST_LOG(fatal) << "Couldn't find any encoder"sv; - } - else { - BOOST_LOG(fatal) << "Couldn't find any encoder matching ["sv << config::video.encoder << ']'; + // Remove the encoder from the list entirely if it fails validation + if(!validate_encoder(encoder)) { + pos = encoders.erase(pos); + continue; + } + + // Skip it if it doesn't support HDR + if(!encoder.hevc[encoder_t::DYNAMIC_RANGE]) { + pos++; + continue; + } + + encoders.clear(); + encoders.emplace_back(encoder); + encoder_found = true; + break; + }); + + if(!encoder_found) { + BOOST_LOG(error) << "Couldn't find any working HDR-capable encoder"sv; } + } + + // If no encoder was specified or the specified encoder was unusable, keep trying + // the remaining encoders until we find one that passes validation. + if(!encoder_found) { + KITTY_WHILE_LOOP(auto pos = std::begin(encoders), pos != std::end(encoders), { + if(!validate_encoder(*pos)) { + pos = encoders.erase(pos); + continue; + } + + break; + }); + } + if(encoders.empty()) { + BOOST_LOG(fatal) << "Couldn't find any working encoder"sv; return -1; } + BOOST_LOG(info); + BOOST_LOG(info) << "// Ignore any errors mentioned above, they are not relevant. //"sv; + BOOST_LOG(info); + auto &encoder = encoders.front(); BOOST_LOG(debug) << "------ h264 ------"sv; From 65574a02d4966a2ac0867e46fb31af359cdaef3b Mon Sep 17 00:00:00 2001 From: Cameron Gutman Date: Thu, 5 Jan 2023 13:26:54 -0600 Subject: [PATCH 77/88] Add an Apply button to the Web UI when running as a Win32 Service (#700) --- src/confighttp.cpp | 33 ++++++++++++ src/platform/common.h | 3 ++ src/platform/linux/misc.cpp | 10 ++++ src/platform/macos/misc.cpp | 10 ++++ src/platform/windows/misc.cpp | 13 +++++ src_assets/common/assets/web/config.html | 43 +++++++++++++--- .../common/assets/web/troubleshooting.html | 51 +++++++++++++++++-- 7 files changed, 153 insertions(+), 10 deletions(-) diff --git a/src/confighttp.cpp b/src/confighttp.cpp index e58f53ae97a..79997435d6f 100644 --- a/src/confighttp.cpp +++ b/src/confighttp.cpp @@ -494,6 +494,7 @@ void getConfig(resp_https_t response, req_https_t request) { outputTree.put("status", "true"); outputTree.put("platform", SUNSHINE_PLATFORM); + outputTree.put("restart_supported", platf::restart_supported()); auto vars = config::parse_config(read_file(config::sunshine.config_file.c_str())); @@ -537,6 +538,37 @@ void saveConfig(resp_https_t response, req_https_t request) { } } +void restart(resp_https_t response, req_https_t request) { + if(!authenticate(response, request)) return; + + print_req(request); + + std::stringstream ss; + std::stringstream configStream; + ss << request->content.rdbuf(); + pt::ptree outputTree; + auto g = util::fail_guard([&]() { + std::ostringstream data; + + pt::write_json(data, outputTree); + response->write(data.str()); + }); + + if(!platf::restart_supported()) { + outputTree.put("status", false); + outputTree.put("error", "Restart is not currently supported on this platform"); + return; + } + + if(!platf::restart()) { + outputTree.put("status", false); + outputTree.put("error", "Restart failed"); + return; + } + + outputTree.put("status", true); +} + void savePassword(resp_https_t response, req_https_t request) { if(!config::sunshine.username.empty() && !authenticate(response, request)) return; @@ -678,6 +710,7 @@ void start() { server.resource["^/api/apps$"]["POST"] = saveApp; server.resource["^/api/config$"]["GET"] = getConfig; server.resource["^/api/config$"]["POST"] = saveConfig; + server.resource["^/api/restart$"]["POST"] = restart; server.resource["^/api/password$"]["POST"] = savePassword; server.resource["^/api/apps/([0-9]+)$"]["DELETE"] = deleteApp; server.resource["^/api/clients/unpair$"]["POST"] = unpairAll; diff --git a/src/platform/common.h b/src/platform/common.h index 732b56bb8f3..fe074c5b0c0 100644 --- a/src/platform/common.h +++ b/src/platform/common.h @@ -318,6 +318,9 @@ void adjust_thread_priority(thread_priority_e priority); void streaming_will_start(); void streaming_will_stop(); +bool restart_supported(); +bool restart(); + input_t input(); void move_mouse(input_t &input, int deltaX, int deltaY); void abs_mouse(input_t &input, const touch_port_t &touch_port, float x, float y); diff --git a/src/platform/linux/misc.cpp b/src/platform/linux/misc.cpp index 1a4c30d6d6f..35e68b96c3a 100644 --- a/src/platform/linux/misc.cpp +++ b/src/platform/linux/misc.cpp @@ -165,6 +165,16 @@ void streaming_will_stop() { // Nothing to do } +bool restart_supported() { + // Restart not supported yet + return false; +} + +bool restart() { + // Restart not supported yet + return false; +} + namespace source { enum source_e : std::size_t { #ifdef SUNSHINE_BUILD_CUDA diff --git a/src/platform/macos/misc.cpp b/src/platform/macos/misc.cpp index 0bfef608e15..eb99de78d2b 100644 --- a/src/platform/macos/misc.cpp +++ b/src/platform/macos/misc.cpp @@ -143,6 +143,16 @@ void streaming_will_stop() { // Nothing to do } +bool restart_supported() { + // Restart not supported yet + return false; +} + +bool restart() { + // Restart not supported yet + return false; +} + } // namespace platf namespace dyn { diff --git a/src/platform/windows/misc.cpp b/src/platform/windows/misc.cpp index 4f9d211c708..88e714d330d 100644 --- a/src/platform/windows/misc.cpp +++ b/src/platform/windows/misc.cpp @@ -1,3 +1,4 @@ +#include #include #include #include @@ -522,4 +523,16 @@ void streaming_will_stop() { DwmEnableMMCSS(false); } +bool restart_supported() { + // Restart is supported if we're running from the service + return (GetConsoleWindow() == NULL); +} + +bool restart() { + // Raise SIGINT to trigger the graceful exit logic. The service will + // restart us in a few seconds. + std::raise(SIGINT); + return true; +} + } // namespace platf \ No newline at end of file diff --git a/src_assets/common/assets/web/config.html b/src_assets/common/assets/web/config.html index bb961c624d8..096233dc6a4 100644 --- a/src_assets/common/assets/web/config.html +++ b/src_assets/common/assets/web/config.html @@ -765,11 +765,18 @@

Configuration

-
- Success! Restart Sunshine to apply changes +
+ Success! Click 'Apply' to restart Sunshine and apply changes. This will terminate any running sessions. +
+
+ Success! Restart Sunshine to apply changes. +
+
+ Success! Sunshine is restarting to apply changes.
+
@@ -779,7 +786,9 @@

Configuration

data() { return { platform: "", - success: false, + restart_supported: false, + saved: false, + restarted: false, config: null, fps: [], resolutions: [], @@ -836,6 +845,7 @@

Configuration

.then((r) => { this.config = r; this.platform = this.config.platform; + this.restart_supported = (this.config.restart_supported === "true"); var app = document.getElementById("app"); if (this.platform == "windows") { @@ -856,6 +866,7 @@

Configuration

delete this.config.status; delete this.config.platform; + delete this.config.restart_supported; //Populate default values if not present in config this.config.key_rightalt_to_key_win = this.config.key_rightalt_to_key_win || "disabled"; @@ -895,8 +906,7 @@

Configuration

}); }, methods: { - save() { - this.success = false; + serialize() { let nl = this.config === "windows" ? "\r\n" : "\n"; this.config.resolutions = "[" + @@ -906,12 +916,31 @@

Configuration

nl + "]"; this.config.fps = JSON.stringify(this.fps); - + }, + save() { + this.saved = this.restarted = false; + this.serialize(); + fetch("/api/config", { + method: "POST", + body: JSON.stringify(this.config), + }).then((r) => { + if (r.status == 200) this.saved = true; + }); + }, + apply() { + this.saved = this.restarted = false; + this.serialize(); fetch("/api/config", { method: "POST", body: JSON.stringify(this.config), }).then((r) => { - if (r.status == 200) this.success = true; + if (r.status == 200) { + fetch("/api/restart", { + method: "POST", + }).then((r) => { + if (r.status == 200) this.restarted = true; + }); + } }); }, }, diff --git a/src_assets/common/assets/web/troubleshooting.html b/src_assets/common/assets/web/troubleshooting.html index 513319d0a57..f517348ed9c 100644 --- a/src_assets/common/assets/web/troubleshooting.html +++ b/src_assets/common/assets/web/troubleshooting.html @@ -7,10 +7,10 @@

Force Close


If Moonlight complains about an app currently running, force closing the - app should fix the issue + app should fix the issue.

- Application Closed Successfuly! + Application Closed Successfully!
Error while closing Appplication @@ -22,6 +22,28 @@

Force Close

+ +
+
+

Restart Sunshine

+
+

+ If Sunshine isn't working properly, you can try restarting it. + This will terminate any running sessions. +

+
+ Sunshine is restarting +
+
+ Error restarting Sunshine +
+
+ +
+
+
@@ -68,6 +90,9 @@

Logs

closeAppStatus: null, unpairAllPressed: false, unpairAllStatus: null, + restartSupported: false, + restartPressed: false, + restartStatus: null, logs: 'Loading...', logFilter: null, logInterval: null, @@ -86,6 +111,11 @@

Logs

this.refreshLogs(); }, 5000); this.refreshLogs(); + fetch("/api/config") + .then((r) => r.json()) + .then((r) => { + this.restartSupported = (r.restart_supported === "true"); + }); }, beforeDestroy(){ clearInterval(this.logInterval); @@ -124,7 +154,22 @@

Logs

}, copyLogs(){ navigator.clipboard.writeText(this.actualLogs); - } + }, + restart() { + this.restartPressed = true; + fetch("/api/restart", { + method: "POST", + }).then((r) => { + this.restartPressed = false; + + // We won't get a response in the success case + this.restartStatus = r.status.toString() !== "false"; + + setTimeout(() => { + this.restartStatus = null; + }, 5000); + }); + }, }, }); From effa98f76a3f1401e98e994e40f959a8df463173 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 5 Jan 2023 19:59:41 -0500 Subject: [PATCH 78/88] Bump sphinx from 6.0.0 to 6.1.1 (#713) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- docs/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/requirements.txt b/docs/requirements.txt index aaf8d78fb54..82012203a23 100644 --- a/docs/requirements.txt +++ b/docs/requirements.txt @@ -1,4 +1,4 @@ furo==2022.12.7 m2r2==0.3.3 -Sphinx==6.0.0 +Sphinx==6.1.1 sphinx-copybutton==0.5.1 From 9b6d0b7a06ad46395bd5c1652fc56fe53c37546f Mon Sep 17 00:00:00 2001 From: Brad Richardson Date: Sat, 7 Jan 2023 09:42:40 -0500 Subject: [PATCH 79/88] Generated app id with hashed input data (#715) --- CMakeLists.txt | 1 - src/confighttp.cpp | 14 ---- src/process.cpp | 168 +++++++++++++++++++++++++++++++++------------ src/process.h | 9 ++- src/rand.h | 23 ------- 5 files changed, 132 insertions(+), 83 deletions(-) delete mode 100644 src/rand.h diff --git a/CMakeLists.txt b/CMakeLists.txt index b192e00828a..ef731b519d7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -375,7 +375,6 @@ set(SUNSHINE_TARGET_FILES src/network.cpp src/network.h src/move_by_copy.h - src/rand.h src/task_pool.h src/thread_pool.h src/thread_safe.h diff --git a/src/confighttp.cpp b/src/confighttp.cpp index 79997435d6f..1276c5973f9 100644 --- a/src/confighttp.cpp +++ b/src/confighttp.cpp @@ -30,7 +30,6 @@ #include "network.h" #include "nvhttp.h" #include "platform/common.h" -#include "rand.h" #include "rtsp.h" #include "utility.h" #include "uuid.h" @@ -303,11 +302,6 @@ void saveApp(resp_https_t response, req_https_t request) { response->write(data.str()); }); - std::set ids; - for(auto const &app : proc::proc.get_apps()) { - ids.insert(app.id); - } - pt::ptree inputTree, fileTree; BOOST_LOG(fatal) << config::stream.file_apps; @@ -316,14 +310,6 @@ void saveApp(resp_https_t response, req_https_t request) { pt::read_json(ss, inputTree); pt::read_json(config::stream.file_apps, fileTree); - // Moonlight checks the id of an item to determine if an item was changed - // Needs to be a 32-bit positive integer due to client limitations, "0" indicates no app - auto id = util::generate_int32(1, std::numeric_limits::max()); - while(ids.count(std::to_string(id)) > 0) { - id = util::generate_int32(1, std::numeric_limits::max()); - } - inputTree.put("id", id); - if(inputTree.get_child("prep-cmd").empty()) { inputTree.erase("prep-cmd"); } diff --git a/src/process.cpp b/src/process.cpp index 457333dac13..21e633aee7e 100644 --- a/src/process.cpp +++ b/src/process.cpp @@ -9,11 +9,16 @@ #include #include +#include #include #include #include #include +#include +#include + +#include "crypto.h" #include "main.h" #include "platform/common.h" #include "utility.h" @@ -23,6 +28,8 @@ #include #endif +#define DEFAULT_APP_IMAGE_PATH SUNSHINE_ASSETS_DIR "/box.png" + namespace proc { using namespace std::literals; namespace bp = boost::process; @@ -233,47 +240,12 @@ std::vector &proc_t::get_apps() { // Returns default image if image configuration is not set. // Returns http content-type header compatible image type. std::string proc_t::get_app_image(int app_id) { - auto default_image = SUNSHINE_ASSETS_DIR "/box.png"; - auto iter = std::find_if(_apps.begin(), _apps.end(), [&app_id](const auto app) { return app.id == std::to_string(app_id); }); auto app_image_path = iter == _apps.end() ? std::string() : iter->image_path; - if(app_image_path.empty()) { - BOOST_LOG(warning) << "Couldn't find app image for ID ["sv << app_id << ']'; - return default_image; - } - - // get the image extension and convert it to lowercase - auto image_extension = std::filesystem::path(app_image_path).extension().string(); - boost::to_lower(image_extension); - - // return the default box image if extension is not "png" - if(image_extension != ".png") { - return default_image; - } - - // check if image is in assets directory - auto full_image_path = std::filesystem::path(SUNSHINE_ASSETS_DIR) / app_image_path; - if(std::filesystem::exists(full_image_path)) { - return full_image_path.string(); - } - else if(app_image_path == "./assets/steam.png") { - // handle old default steam image definition - return SUNSHINE_ASSETS_DIR "/steam.png"; - } - - // check if specified image exists - std::error_code code; - if(!std::filesystem::exists(app_image_path, code)) { - // return default box image if image does not exist - return default_image; - } - - // image is a png, and not in assets directory - // return only "content-type" http header compatible image type - return app_image_path; + return validate_app_image_path(app_image_path); } proc_t::~proc_t() { @@ -355,6 +327,114 @@ std::string parse_env_val(bp::native_environment &env, const std::string_view &v return ss.str(); } +std::string validate_app_image_path(std::string app_image_path) { + if(app_image_path.empty()) { + return DEFAULT_APP_IMAGE_PATH; + } + + // get the image extension and convert it to lowercase + auto image_extension = std::filesystem::path(app_image_path).extension().string(); + boost::to_lower(image_extension); + + // return the default box image if extension is not "png" + if(image_extension != ".png") { + return DEFAULT_APP_IMAGE_PATH; + } + + // check if image is in assets directory + auto full_image_path = std::filesystem::path(SUNSHINE_ASSETS_DIR) / app_image_path; + if(std::filesystem::exists(full_image_path)) { + return full_image_path.string(); + } + else if(app_image_path == "./assets/steam.png") { + // handle old default steam image definition + return SUNSHINE_ASSETS_DIR "/steam.png"; + } + + // check if specified image exists + std::error_code code; + if(!std::filesystem::exists(app_image_path, code)) { + // return default box image if image does not exist + BOOST_LOG(warning) << "Couldn't find app image at path ["sv << app_image_path << ']'; + return DEFAULT_APP_IMAGE_PATH; + } + + // image is a png, and not in assets directory + // return only "content-type" http header compatible image type + return app_image_path; +} + +std::optional calculate_sha256(const std::string &filename) { + crypto::md_ctx_t ctx { EVP_MD_CTX_create() }; + if(!ctx) { + return std::nullopt; + } + + if(!EVP_DigestInit_ex(ctx.get(), EVP_sha256(), nullptr)) { + return std::nullopt; + } + + // Read file and update calculated SHA + char buf[1024 * 16]; + std::ifstream file(filename, std::ifstream::binary); + while(file.good()) { + file.read(buf, sizeof(buf)); + if(!EVP_DigestUpdate(ctx.get(), buf, file.gcount())) { + return std::nullopt; + } + } + file.close(); + + unsigned char result[SHA256_DIGEST_LENGTH]; + if(!EVP_DigestFinal_ex(ctx.get(), result, nullptr)) { + return std::nullopt; + } + + // Transform byte-array to string + std::stringstream ss; + ss << std::hex << std::setfill('0'); + for(const auto &byte : result) { + ss << std::setw(2) << (int)byte; + } + return ss.str(); +} + +uint32_t calculate_crc32(const std::string &input) { + boost::crc_32_type result; + result.process_bytes(input.data(), input.length()); + return result.checksum(); +} + +std::tuple calculate_app_id(const std::string &app_name, std::string app_image_path, int index) { + // Generate id by hashing name with image data if present + std::vector to_hash; + to_hash.push_back(app_name); + auto file_path = validate_app_image_path(app_image_path); + if(file_path != DEFAULT_APP_IMAGE_PATH) { + auto file_hash = calculate_sha256(file_path); + if(file_hash) { + to_hash.push_back(file_hash.value()); + } + else { + // Fallback to just hashing image path + to_hash.push_back(file_path); + } + } + + // Create combined strings for hash + std::stringstream ss; + for_each(to_hash.begin(), to_hash.end(), [&ss](const std::string &s) { ss << s; }); + auto input_no_index = ss.str(); + ss << index; + auto input_with_index = ss.str(); + + // CRC32 then truncate to signed 32-bit range due to client limitations + auto id_no_index = std::to_string(abs((int32_t)calculate_crc32(input_no_index))); + auto id_with_index = std::to_string(abs((int32_t)calculate_crc32(input_with_index))); + + return std::make_tuple(id_no_index, id_with_index); +} + std::optional parse(const std::string &file_name) { pt::ptree tree; @@ -370,8 +450,9 @@ std::optional parse(const std::string &file_name) { this_env[name] = parse_env_val(this_env, val.get_value()); } - int app_index = 1; // Start at 1, 0 indicates no app running + std::set ids; std::vector apps; + int i = 0; for(auto &[_, app_node] : apps_node) { proc::ctx_t ctx; @@ -382,7 +463,6 @@ std::optional parse(const std::string &file_name) { auto cmd = app_node.get_optional("cmd"s); auto image_path = app_node.get_optional("image-path"s); auto working_dir = app_node.get_optional("working-dir"s); - auto id = app_node.get_optional("id"s); std::vector prep_cmds; if(prep_nodes_opt) { @@ -428,14 +508,16 @@ std::optional parse(const std::string &file_name) { ctx.image_path = parse_env_val(this_env, *image_path); } - if(id) { - ctx.id = parse_env_val(this_env, *id); + auto possible_ids = calculate_app_id(name, ctx.image_path, i++); + if(ids.count(std::get<0>(possible_ids)) == 0) { + // Avoid using index to generate id if possible + ctx.id = std::get<0>(possible_ids); } else { - ctx.id = std::to_string(app_index); + // Fallback to include index on collision + ctx.id = std::get<1>(possible_ids); } - // Always increment index to avoid order shuffling in moonlight - app_index++; + ids.insert(ctx.id); ctx.name = std::move(name); ctx.prep_cmds = std::move(prep_cmds); diff --git a/src/process.h b/src/process.h index fa6ecc01933..e4bd0170e37 100644 --- a/src/process.h +++ b/src/process.h @@ -97,10 +97,15 @@ class proc_t { file_t _pipe; std::vector::const_iterator _undo_it; std::vector::const_iterator _undo_begin; - - int app_index_from_id(int app_id); }; +/** + * Calculate a stable id based on name and image data + * @return tuple of id calculated without index (for use if no collision) and one with +*/ +std::tuple calculate_app_id(const std::string &app_name, std::string app_image_path, int index); + +std::string validate_app_image_path(std::string app_image_path); void refresh(const std::string &file_name); std::optional parse(const std::string &file_name); diff --git a/src/rand.h b/src/rand.h deleted file mode 100644 index 62273baef13..00000000000 --- a/src/rand.h +++ /dev/null @@ -1,23 +0,0 @@ -#ifndef SUNSHINE_RAND_H -#define SUNSHINE_RAND_H - -#include - -namespace util { - -static int32_t generate_int32(std::default_random_engine &engine, int32_t min, int32_t max) { - std::uniform_int_distribution dist(min, max); - - return dist(engine); -} - -static int32_t generate_int32(int32_t min, int32_t max) { - std::random_device r; - - std::default_random_engine engine { r() }; - - return util::generate_int32(engine, min, max); -} - -} // namespace util -#endif // SUNSHINE_RAND_H From c95f54f874841d9f772780856c4c64150ef015bc Mon Sep 17 00:00:00 2001 From: Cameron Gutman Date: Sat, 7 Jan 2023 12:34:06 -0600 Subject: [PATCH 80/88] Migrate Windows config files into specific config directory (#716) --- CMakeLists.txt | 11 ++++- docs/source/about/advanced_usage.rst | 2 +- src/platform/windows/misc.cpp | 4 +- src_assets/linux/assets/apps.json | 1 - src_assets/macos/assets/apps.json | 1 - src_assets/windows/assets/apps.json | 1 - .../windows/misc/migration/migrate-config.bat | 46 +++++++++++++++++++ 7 files changed, 59 insertions(+), 7 deletions(-) create mode 100644 src_assets/windows/misc/migration/migrate-config.bat diff --git a/CMakeLists.txt b/CMakeLists.txt index ef731b519d7..54303f01eb4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -532,6 +532,9 @@ if(WIN32) # see options at: https://cmake.org/cmake/help/latest/cpack_gen/nsis.h install(DIRECTORY "${SUNSHINE_SOURCE_ASSETS_DIR}/windows/misc/service/" DESTINATION "scripts" COMPONENT service) + install(DIRECTORY "${SUNSHINE_SOURCE_ASSETS_DIR}/windows/misc/migration/" + DESTINATION "scripts" + COMPONENT assets) # Sunshine assets install(DIRECTORY "${SUNSHINE_SOURCE_ASSETS_DIR}/common/assets/" @@ -548,12 +551,16 @@ if(WIN32) # see options at: https://cmake.org/cmake/help/latest/cpack_gen/nsis.h set(CPACK_PACKAGE_INSTALL_DIRECTORY "${CPACK_PACKAGE_NAME}") # Extra install commands - # Sets permissions on the installed folder so that we can write in it + # Restores permissions on the install directory + # Migrates config files from the root into the new config folder + # Sets permissions on the config folder so that we can write in it # Install service SET(CPACK_NSIS_EXTRA_INSTALL_COMMANDS "${CPACK_NSIS_EXTRA_INSTALL_COMMANDS} ExecWait '\\\"$SYSDIR\\\\cmd.exe\\\" /c \\\"start https://sunshinestream.readthedocs.io/\\\"' - ExecWait 'icacls \\\"$INSTDIR\\\" /grant:r Users:\\\(OI\\\)\\\(CI\\\)\\\(F\\\)' + ExecWait 'icacls \\\"$INSTDIR\\\" /reset' + ExecWait '\\\"$INSTDIR\\\\scripts\\\\migrate-config.bat\\\"' + ExecWait 'icacls \\\"$INSTDIR\\\\config\\\" /grant:r Users:\\\(OI\\\)\\\(CI\\\)\\\(F\\\)' ExecWait '\\\"$INSTDIR\\\\scripts\\\\add-firewall-rule.bat\\\"' ExecWait '\\\"$INSTDIR\\\\scripts\\\\install-service.bat\\\"' MessageBox MB_YESNO|MB_ICONQUESTION 'Do you want to add/update ViGEmBus (virtual controller support)?' \ diff --git a/docs/source/about/advanced_usage.rst b/docs/source/about/advanced_usage.rst index f208772889d..03e46983dcf 100644 --- a/docs/source/about/advanced_usage.rst +++ b/docs/source/about/advanced_usage.rst @@ -33,7 +33,7 @@ location by modifying the configuration file. Docker /config/ Linux ~/.config/sunshine/ macOS ~/.config/sunshine/ - Windows ./config/ + Windows %ProgramFiles%\\Sunshine\\config ========= =========== **Example** diff --git a/src/platform/windows/misc.cpp b/src/platform/windows/misc.cpp index 88e714d330d..a9d31b8f162 100644 --- a/src/platform/windows/misc.cpp +++ b/src/platform/windows/misc.cpp @@ -29,7 +29,9 @@ namespace platf { using adapteraddrs_t = util::c_ptr; std::filesystem::path appdata() { - return L"."sv; + WCHAR sunshine_path[MAX_PATH]; + GetModuleFileNameW(NULL, sunshine_path, _countof(sunshine_path)); + return std::filesystem::path { sunshine_path }.remove_filename() / L"config"sv; } std::string from_sockaddr(const sockaddr *const socket_address) { diff --git a/src_assets/linux/assets/apps.json b/src_assets/linux/assets/apps.json index a5211fa573b..5331dcf8dc1 100644 --- a/src_assets/linux/assets/apps.json +++ b/src_assets/linux/assets/apps.json @@ -19,7 +19,6 @@ }, { "name": "Steam BigPicture", - "output": "steam.txt", "detached": [ "setsid steam steam://open/bigpicture" ], diff --git a/src_assets/macos/assets/apps.json b/src_assets/macos/assets/apps.json index 94b2cde13fc..88559542964 100644 --- a/src_assets/macos/assets/apps.json +++ b/src_assets/macos/assets/apps.json @@ -9,7 +9,6 @@ }, { "name": "Steam BigPicture", - "output": "steam.txt", "detached": [ "open steam://open/bigpicture" ], diff --git a/src_assets/windows/assets/apps.json b/src_assets/windows/assets/apps.json index 1f27805aa1d..08d2386d31d 100644 --- a/src_assets/windows/assets/apps.json +++ b/src_assets/windows/assets/apps.json @@ -9,7 +9,6 @@ }, { "name": "Steam BigPicture", - "output": "steam.txt", "detached": [ "steam steam://open/bigpicture" ], diff --git a/src_assets/windows/misc/migration/migrate-config.bat b/src_assets/windows/misc/migration/migrate-config.bat new file mode 100644 index 00000000000..313b472e8af --- /dev/null +++ b/src_assets/windows/misc/migration/migrate-config.bat @@ -0,0 +1,46 @@ +@echo off + +rem Get sunshine root directory +for %%I in ("%~dp0\..") do set "OLD_DIR=%%~fI" + +rem Create the config directory if it didn't already exist +set "NEW_DIR=%OLD_DIR%\config" +if not exist "%NEW_DIR%\" mkdir "%NEW_DIR%" + +rem Migrate all files that aren't already present in the config dir +if exist "%OLD_DIR%\apps.json" ( + if not exist "%NEW_DIR%\apps.json" ( + move "%OLD_DIR%\apps.json" "%NEW_DIR%\apps.json" + ) +) +if exist "%OLD_DIR%\sunshine.conf" ( + if not exist "%NEW_DIR%\sunshine.conf" ( + move "%OLD_DIR%\sunshine.conf" "%NEW_DIR%\sunshine.conf" + ) +) +if exist "%OLD_DIR%\sunshine_state.json" ( + if not exist "%NEW_DIR%\sunshine_state.json" ( + move "%OLD_DIR%\sunshine_state.json" "%NEW_DIR%\sunshine_state.json" + ) +) + +rem Migrate the credentials directory +if exist "%OLD_DIR%\credentials\" ( + if not exist "%NEW_DIR%\credentials\" ( + move "%OLD_DIR%\credentials" "%NEW_DIR%\" + ) +) + +rem Migrate the covers directory +if exist "%OLD_DIR%\covers\" ( + if not exist "%NEW_DIR%\covers\" ( + move "%OLD_DIR%\covers" "%NEW_DIR%\" + + rem Fix apps.json image path values that point at the old covers directory + powershell -c "(Get-Content '%NEW_DIR%\apps.json').replace('.\/covers\/', '.\/config\/covers\/') | Set-Content '%NEW_DIR%\apps.json'" + ) +) + +rem Remove log files +del "%OLD_DIR%\*.txt" +del "%OLD_DIR%\*.log" From e2fce257b53626b4f21cfc7132a1e94f781a24e1 Mon Sep 17 00:00:00 2001 From: Cameron Gutman Date: Sat, 7 Jan 2023 13:25:02 -0600 Subject: [PATCH 81/88] Fix Windows masked cursor blending with GPU encoders (#720) --- src/platform/windows/display.h | 6 +- src/platform/windows/display_vram.cpp | 276 +++++++++++++++++--------- src/utility.h | 3 + 3 files changed, 192 insertions(+), 93 deletions(-) diff --git a/src/platform/windows/display.h b/src/platform/windows/display.h index 0f73874587e..a788df51221 100644 --- a/src/platform/windows/display.h +++ b/src/platform/windows/display.h @@ -185,13 +185,15 @@ class display_vram_t : public display_base_t, public std::enable_shared_from_thi sampler_state_t sampler_linear; - blend_t blend_enable; + blend_t blend_alpha; + blend_t blend_invert; blend_t blend_disable; ps_t scene_ps; vs_t scene_vs; - gpu_cursor_t cursor; + gpu_cursor_t cursor_alpha; + gpu_cursor_t cursor_xor; texture2d_t last_frame_copy; }; diff --git a/src/platform/windows/display_vram.cpp b/src/platform/windows/display_vram.cpp index 354942b477e..fb9bef7d7b1 100644 --- a/src/platform/windows/display_vram.cpp +++ b/src/platform/windows/display_vram.cpp @@ -52,7 +52,7 @@ buf_t make_buffer(device_t::pointer device, const T &t) { return buf_t { buf_p }; } -blend_t make_blend(device_t::pointer device, bool enable) { +blend_t make_blend(device_t::pointer device, bool enable, bool invert) { D3D11_BLEND_DESC bdesc {}; auto &rt = bdesc.RenderTarget[0]; rt.BlendEnable = enable; @@ -62,8 +62,16 @@ blend_t make_blend(device_t::pointer device, bool enable) { rt.BlendOp = D3D11_BLEND_OP_ADD; rt.BlendOpAlpha = D3D11_BLEND_OP_ADD; - 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; @@ -110,22 +118,111 @@ struct img_d3d_t : public platf::img_t { }; }; -util::buffer_t make_cursor_image(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 {}; + } + + shape_info.Height /= 2; + + 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_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); + + 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; + } + ++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) { 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: - std::for_each((std::uint32_t *)std::begin(img_data), (std::uint32_t *)std::end(img_data), [](auto &pixel) { - if(pixel & 0xFF000000) { + 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: - return std::move(img_data); - default: + // 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; @@ -144,46 +241,17 @@ util::buffer_t make_cursor_image(util::buffer_t &&im auto color_type = ((*and_mask & bit) ? 1 : 0) + ((*xor_mask & bit) ? 2 : 0); switch(color_type) { - case 0: //black + case 0: // Opaque black *pixel_data = black; break; - case 2: //white + case 2: // Opaque white *pixel_data = white; break; - case 1: //transparent - { + case 3: // Inverse of screen (handled by XOR blending) + case 1: // Color of screen (transparent) *pixel_data = transparent; - break; } - case 3: //inverse - { - auto top_p = pixel_data - shape_info.Width; - auto left_p = pixel_data - 1; - auto right_p = pixel_data + 1; - auto bottom_p = pixel_data + shape_info.Width; - - // Get the x coordinate of the pixel - auto column = (pixel_data - pixel_begin) % shape_info.Width != 0; - - if(top_p >= pixel_begin && *top_p == transparent) { - *top_p = black; - } - - if(column != 0 && left_p >= pixel_begin && *left_p == transparent) { - *left_p = black; - } - - if(bottom_p < (std::uint32_t *)std::end(cursor_img)) { - *bottom_p = black; - } - - if(column != shape_info.Width - 1) { - *right_p = black; - } - *pixel_data = white; - } - } ++pixel_data; } @@ -513,7 +581,7 @@ class hwdevice_t : public platf::hwdevice_t { return -1; } - blend_disable = make_blend(device.get(), false); + blend_disable = make_blend(device.get(), false, false); if(!blend_disable) { return -1; } @@ -667,6 +735,50 @@ capture_e display_vram_t::capture(snapshot_cb_t &&snapshot_cb, std::shared_ptr<: return capture_e::ok; } +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; + } + + D3D11_SUBRESOURCE_DATA data { + std::begin(cursor_img), + 4 * shape_info.Width, + 0 + }; + + // Create texture for cursor + D3D11_TEXTURE2D_DESC t {}; + t.Width = shape_info.Width; + t.Height = cursor_img.size() / data.SysMemPitch; + t.MipLevels = 1; + t.ArraySize = 1; + t.SampleDesc.Count = 1; + t.Usage = D3D11_USAGE_IMMUTABLE; + t.Format = DXGI_FORMAT_B8G8R8A8_UNORM; + t.BindFlags = D3D11_BIND_SHADER_RESOURCE; + + 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; + } + + // Free resources before allocating on the next line. + cursor.input_res.reset(); + 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; + } + + cursor.set_texture(t.Width, t.Height, std::move(texture)); + return true; +} + capture_e display_vram_t::snapshot(platf::img_t *img_base, std::chrono::milliseconds timeout, bool cursor_visible) { auto img = (img_d3d_t *)img_base; @@ -703,45 +815,18 @@ capture_e display_vram_t::snapshot(platf::img_t *img_base, std::chrono::millisec return capture_e::error; } - auto cursor_img = make_cursor_image(std::move(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); - D3D11_SUBRESOURCE_DATA data { - std::begin(cursor_img), - 4 * shape_info.Width, - 0 - }; - - // Create texture for cursor - D3D11_TEXTURE2D_DESC t {}; - t.Width = shape_info.Width; - t.Height = cursor_img.size() / data.SysMemPitch; - t.MipLevels = 1; - t.ArraySize = 1; - t.SampleDesc.Count = 1; - t.Usage = D3D11_USAGE_DEFAULT; - t.Format = DXGI_FORMAT_B8G8R8A8_UNORM; - t.BindFlags = D3D11_BIND_SHADER_RESOURCE; - - 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() << ']'; + 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; } - - // Free resources before allocating on the next line. - cursor.input_res.reset(); - 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 capture_e::error; - } - - cursor.set_texture(t.Width, t.Height, std::move(texture)); } if(frame_info.LastMouseUpdateTime.QuadPart) { - cursor.set_pos(frame_info.PointerPosition.Position.x, frame_info.PointerPosition.Position.y, frame_info.PointerPosition.Visible); + cursor_alpha.set_pos(frame_info.PointerPosition.Position.x, frame_info.PointerPosition.Position.y, frame_info.PointerPosition.Visible); + cursor_xor.set_pos(frame_info.PointerPosition.Position.x, frame_info.PointerPosition.Position.y, frame_info.PointerPosition.Visible); } if(frame_update_flag) { @@ -850,21 +935,29 @@ capture_e display_vram_t::snapshot(platf::img_t *img_base, std::chrono::millisec device_ctx->CopyResource(img->capture_texture.get(), last_frame_copy.get()); } - if(cursor.visible && cursor_visible) { - D3D11_VIEWPORT view { - 0.0f, 0.0f, - (float)width, (float)height, - 0.0f, 1.0f - }; - + if((cursor_alpha.visible || cursor_xor.visible) && cursor_visible) { device_ctx->VSSetShader(scene_vs.get(), nullptr, 0); device_ctx->PSSetShader(scene_ps.get(), nullptr, 0); - device_ctx->RSSetViewports(1, &view); - device_ctx->PSSetShaderResources(0, 1, &cursor.input_res); device_ctx->OMSetRenderTargets(1, &img->capture_rt, nullptr); - device_ctx->OMSetBlendState(blend_enable.get(), nullptr, 0xFFFFFFFFu); - device_ctx->RSSetViewports(1, &cursor.cursor_view); - device_ctx->Draw(3, 0); + + 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); + } + + 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->OMSetBlendState(blend_disable.get(), nullptr, 0xFFFFFFFFu); } @@ -906,10 +999,11 @@ int display_vram_t::init(int framerate, const std::string &display_name) { return -1; } - blend_enable = make_blend(device.get(), true); - blend_disable = make_blend(device.get(), false); + blend_alpha = make_blend(device.get(), true, false); + blend_invert = make_blend(device.get(), true, true); + blend_disable = make_blend(device.get(), false, false); - if(!blend_disable || !blend_enable) { + if(!blend_disable || !blend_alpha || !blend_invert) { return -1; } diff --git a/src/utility.h b/src/utility.h index c4946dfdf3c..dcd52c2c9df 100644 --- a/src/utility.h +++ b/src/utility.h @@ -726,6 +726,9 @@ class buffer_t { buffer_t(buffer_t &&o) noexcept : _els { o._els }, _buf { std::move(o._buf) } { o._els = 0; } + buffer_t(const buffer_t &o) : _els { o._els }, _buf { std::make_unique(_els) } { + std::copy(o.begin(), o.end(), begin()); + } buffer_t &operator=(buffer_t &&o) noexcept { std::swap(_els, o._els); std::swap(_buf, o._buf); From 7770b5f708675854674e8372af9227ee91ce5882 Mon Sep 17 00:00:00 2001 From: Cameron Gutman Date: Sat, 7 Jan 2023 14:11:14 -0600 Subject: [PATCH 82/88] Enable Mouse Keys while streaming for systems with no mouse plugged in (#721) --- src/platform/windows/misc.cpp | 41 +++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/src/platform/windows/misc.cpp b/src/platform/windows/misc.cpp index a9d31b8f162..9b6867daf23 100644 --- a/src/platform/windows/misc.cpp +++ b/src/platform/windows/misc.cpp @@ -28,6 +28,9 @@ using namespace std::literals; namespace platf { using adapteraddrs_t = util::c_ptr; +bool enabled_mouse_keys = false; +MOUSEKEYS previous_mouse_keys_state; + std::filesystem::path appdata() { WCHAR sunshine_path[MAX_PATH]; GetModuleFileNameW(NULL, sunshine_path, _countof(sunshine_path)); @@ -512,6 +515,35 @@ void streaming_will_start() { // Promote ourselves to high priority class SetPriorityClass(GetCurrentProcess(), HIGH_PRIORITY_CLASS); + + // If there is no mouse connected, enable Mouse Keys to force the cursor to appear + if(!GetSystemMetrics(SM_MOUSEPRESENT)) { + BOOST_LOG(info) << "A mouse was not detected. Sunshine will enable Mouse Keys while streaming to force the mouse cursor to appear."; + + // Get the current state of Mouse Keys so we can restore it when streaming is over + previous_mouse_keys_state.cbSize = sizeof(previous_mouse_keys_state); + if(SystemParametersInfoW(SPI_GETMOUSEKEYS, 0, &previous_mouse_keys_state, 0)) { + MOUSEKEYS new_mouse_keys_state = {}; + + // Enable Mouse Keys + new_mouse_keys_state.cbSize = sizeof(new_mouse_keys_state); + new_mouse_keys_state.dwFlags = MKF_MOUSEKEYSON | MKF_AVAILABLE; + new_mouse_keys_state.iMaxSpeed = 10; + new_mouse_keys_state.iTimeToMaxSpeed = 1000; + if(SystemParametersInfoW(SPI_SETMOUSEKEYS, 0, &new_mouse_keys_state, 0)) { + // Remember to restore the previous settings when we stop streaming + enabled_mouse_keys = true; + } + else { + auto winerr = GetLastError(); + BOOST_LOG(warning) << "Unable to enable Mouse Keys: "sv << winerr; + } + } + else { + auto winerr = GetLastError(); + BOOST_LOG(warning) << "Unable to get current state of Mouse Keys: "sv << winerr; + } + } } void streaming_will_stop() { @@ -523,6 +555,15 @@ void streaming_will_stop() { // Disable MMCSS scheduling for DWM DwmEnableMMCSS(false); + + // Restore Mouse Keys back to the previous settings if we turned it on + if(enabled_mouse_keys) { + enabled_mouse_keys = false; + if(!SystemParametersInfoW(SPI_SETMOUSEKEYS, 0, &previous_mouse_keys_state, 0)) { + auto winerr = GetLastError(); + BOOST_LOG(warning) << "Unable to restore original state of Mouse Keys: "sv << winerr; + } + } } bool restart_supported() { From 77d3a7e2abc7032d8c4a4bfb96855cded7afa2a3 Mon Sep 17 00:00:00 2001 From: Cameron Gutman Date: Sat, 7 Jan 2023 21:28:47 -0600 Subject: [PATCH 83/88] Color conversion fixes and BT.2020 support (#723) --- src/platform/linux/cuda.cu | 4 +++- src/platform/linux/graphics.cpp | 2 ++ src/platform/windows/display_vram.cpp | 2 ++ src/video.cpp | 18 ++++++++++-------- src/video.h | 2 +- .../linux/assets/shaders/opengl/ConvertUV.frag | 2 +- .../assets/shaders/directx/ConvertUVPS.hlsl | 2 +- .../assets/shaders/directx/ConvertYPS.hlsl | 2 +- 8 files changed, 21 insertions(+), 13 deletions(-) diff --git a/src/platform/linux/cuda.cu b/src/platform/linux/cuda.cu index 1c6169b38ae..d4b24e4aa6c 100644 --- a/src/platform/linux/cuda.cu +++ b/src/platform/linux/cuda.cu @@ -143,7 +143,7 @@ inline __device__ float2 calcUV(float3 pixel, const video::color_t *const color_ float v = dot(pixel, make_float3(vec_v)) + vec_v.w; u = u * color_matrix->range_uv.x + color_matrix->range_uv.y; - v = (v * color_matrix->range_uv.x + color_matrix->range_uv.y) * 224.0f / 256.0f + 0.0625f; + v = v * color_matrix->range_uv.x + color_matrix->range_uv.y; return make_float2(u, v); } @@ -322,6 +322,8 @@ void sws_t::set_colorspace(std::uint32_t colorspace, std::uint32_t color_range) color_p = &video::colors[2]; break; case 9: // SWS_CS_BT2020 + color_p = &video::colors[4]; + break; default: color_p = &video::colors[0]; }; diff --git a/src/platform/linux/graphics.cpp b/src/platform/linux/graphics.cpp index 59b855221a3..1dc25b07262 100644 --- a/src/platform/linux/graphics.cpp +++ b/src/platform/linux/graphics.cpp @@ -588,6 +588,8 @@ void sws_t::set_colorspace(std::uint32_t colorspace, std::uint32_t color_range) color_p = &video::colors[2]; break; case 9: // SWS_CS_BT2020 + color_p = &video::colors[4]; + break; default: BOOST_LOG(warning) << "Colorspace: ["sv << colorspace << "] not yet supported: switching to default"sv; color_p = &video::colors[0]; diff --git a/src/platform/windows/display_vram.cpp b/src/platform/windows/display_vram.cpp index fb9bef7d7b1..4c0e8fa4a99 100644 --- a/src/platform/windows/display_vram.cpp +++ b/src/platform/windows/display_vram.cpp @@ -369,6 +369,8 @@ class hwdevice_t : public platf::hwdevice_t { color_p = &::video::colors[2]; break; case 9: // SWS_CS_BT2020 + color_p = &::video::colors[4]; + break; default: BOOST_LOG(warning) << "Colorspace: ["sv << colorspace << "] not yet supported: switching to default"sv; color_p = &::video::colors[0]; diff --git a/src/video.cpp b/src/video.cpp index 020ab77c331..7392fafc1d2 100644 --- a/src/video.cpp +++ b/src/video.cpp @@ -1966,7 +1966,7 @@ platf::pix_fmt_e map_pix_fmt(AVPixelFormat fmt) { return platf::pix_fmt_e::unknown; } -color_t make_color_matrix(float Cr, float Cb, float U_max, float V_max, float add_Y, float add_UV, const float2 &range_Y, const float2 &range_UV) { +color_t make_color_matrix(float Cr, float Cb, const float2 &range_Y, const float2 &range_UV) { float Cg = 1.0f - Cr - Cb; float Cr_i = 1.0f - Cr; @@ -1978,18 +1978,20 @@ color_t make_color_matrix(float Cr, float Cb, float U_max, float V_max, float ad float scale_y = (range_Y[1] - range_Y[0]) / 256.0f; float scale_uv = (range_UV[1] - range_UV[0]) / 256.0f; return { - { Cr, Cg, Cb, add_Y }, - { -(Cr * U_max / Cb_i), -(Cg * U_max / Cb_i), U_max, add_UV }, - { V_max, -(Cg * V_max / Cr_i), -(Cb * V_max / Cr_i), add_UV }, + { Cr, Cg, Cb, 0.0f }, + { -(Cr * 0.5f / Cb_i), -(Cg * 0.5f / Cb_i), 0.5f, 0.5f }, + { 0.5f, -(Cg * 0.5f / Cr_i), -(Cb * 0.5f / Cr_i), 0.5f }, { scale_y, shift_y }, { scale_uv, shift_uv }, }; } color_t colors[] { - make_color_matrix(0.299f, 0.114f, 0.436f, 0.615f, 0.0625, 0.5f, { 16.0f, 235.0f }, { 16.0f, 240.0f }), // BT601 MPEG - make_color_matrix(0.299f, 0.114f, 0.5f, 0.5f, 0.0f, 0.5f, { 0.0f, 255.0f }, { 0.0f, 255.0f }), // BT601 JPEG - make_color_matrix(0.2126f, 0.0722f, 0.436f, 0.615f, 0.0625, 0.5f, { 16.0f, 235.0f }, { 16.0f, 240.0f }), // BT701 MPEG - make_color_matrix(0.2126f, 0.0722f, 0.5f, 0.5f, 0.0f, 0.5f, { 0.0f, 255.0f }, { 0.0f, 255.0f }), // BT701 JPEG + make_color_matrix(0.299f, 0.114f, { 16.0f, 235.0f }, { 16.0f, 240.0f }), // BT601 MPEG + make_color_matrix(0.299f, 0.114f, { 0.0f, 255.0f }, { 0.0f, 255.0f }), // BT601 JPEG + make_color_matrix(0.2126f, 0.0722f, { 16.0f, 235.0f }, { 16.0f, 240.0f }), // BT709 MPEG + make_color_matrix(0.2126f, 0.0722f, { 0.0f, 255.0f }, { 0.0f, 255.0f }), // BT709 JPEG + make_color_matrix(0.2627f, 0.0593f, { 16.0f, 235.0f }, { 16.0f, 240.0f }), // BT2020 MPEG + make_color_matrix(0.2627f, 0.0593f, { 0.0f, 255.0f }, { 0.0f, 255.0f }), // BT2020 JPEG }; } // namespace video diff --git a/src/video.h b/src/video.h index 6b8d6e57077..3d99f855e35 100644 --- a/src/video.h +++ b/src/video.h @@ -72,7 +72,7 @@ struct __attribute__((__aligned__(16))) color_t { float2 range_uv; }; -extern color_t colors[4]; +extern color_t colors[6]; void capture( safe::mail_t mail, diff --git a/src_assets/linux/assets/shaders/opengl/ConvertUV.frag b/src_assets/linux/assets/shaders/opengl/ConvertUV.frag index 4bd082870f5..699535e3981 100644 --- a/src_assets/linux/assets/shaders/opengl/ConvertUV.frag +++ b/src_assets/linux/assets/shaders/opengl/ConvertUV.frag @@ -31,5 +31,5 @@ void main() { u = u * range_uv.x + range_uv.y; v = v * range_uv.x + range_uv.y; - color = vec2(u, v * 224.0f / 256.0f + 0.0625); + color = vec2(u, v); } \ No newline at end of file diff --git a/src_assets/windows/assets/shaders/directx/ConvertUVPS.hlsl b/src_assets/windows/assets/shaders/directx/ConvertUVPS.hlsl index f9bf69df062..93d45762dbf 100644 --- a/src_assets/windows/assets/shaders/directx/ConvertUVPS.hlsl +++ b/src_assets/windows/assets/shaders/directx/ConvertUVPS.hlsl @@ -29,5 +29,5 @@ float2 main_ps(FragTexWide input) : SV_Target u = u * range_uv.x + range_uv.y; v = v * range_uv.x + range_uv.y; - return float2(u, v * 224.0f/256.0f + 0.0625); + return float2(u, v); } \ No newline at end of file diff --git a/src_assets/windows/assets/shaders/directx/ConvertYPS.hlsl b/src_assets/windows/assets/shaders/directx/ConvertYPS.hlsl index c38d19c82ae..0abb89a86eb 100644 --- a/src_assets/windows/assets/shaders/directx/ConvertYPS.hlsl +++ b/src_assets/windows/assets/shaders/directx/ConvertYPS.hlsl @@ -19,7 +19,7 @@ struct PS_INPUT float main_ps(PS_INPUT frag_in) : SV_Target { float3 rgb = image.Sample(def_sampler, frag_in.tex, 0).rgb; - float y = dot(color_vec_y.xyz, rgb); + float y = dot(color_vec_y.xyz, rgb) + color_vec_y.w; return y * range_y.x + range_y.y; } \ No newline at end of file From 66070626481bb7e55bef45f473c3a6adba920ca4 Mon Sep 17 00:00:00 2001 From: Cameron Gutman Date: Sun, 8 Jan 2023 08:04:07 -0600 Subject: [PATCH 84/88] Fix divisor for YUV range (#728) --- src/video.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/video.cpp b/src/video.cpp index 7392fafc1d2..1fd50e7e248 100644 --- a/src/video.cpp +++ b/src/video.cpp @@ -1972,11 +1972,11 @@ color_t make_color_matrix(float Cr, float Cb, const float2 &range_Y, const float float Cr_i = 1.0f - Cr; float Cb_i = 1.0f - Cb; - float shift_y = range_Y[0] / 256.0f; - float shift_uv = range_UV[0] / 256.0f; + float shift_y = range_Y[0] / 255.0f; + float shift_uv = range_UV[0] / 255.0f; - float scale_y = (range_Y[1] - range_Y[0]) / 256.0f; - float scale_uv = (range_UV[1] - range_UV[0]) / 256.0f; + float scale_y = (range_Y[1] - range_Y[0]) / 255.0f; + float scale_uv = (range_UV[1] - range_UV[0]) / 255.0f; return { { Cr, Cg, Cb, 0.0f }, { -(Cr * 0.5f / Cb_i), -(Cg * 0.5f / Cb_i), 0.5f, 0.5f }, From a26877a91749c385c731e8c0fd288a7b36c54f35 Mon Sep 17 00:00:00 2001 From: ReenigneArcher <42013603+ReenigneArcher@users.noreply.github.com> Date: Sun, 8 Jan 2023 10:05:29 -0500 Subject: [PATCH 85/88] fix submodule names (#725) --- .gitmodules | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/.gitmodules b/.gitmodules index bf47edeefbb..b79222a2dbe 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,44 +1,44 @@ -[submodule "moonlight-common-c"] +[submodule "third-party/moonlight-common-c"] path = third-party/moonlight-common-c url = https://github.com/moonlight-stream/moonlight-common-c.git branch = master -[submodule "Simple-Web-Server"] +[submodule "third-party/Simple-Web-Server"] path = third-party/Simple-Web-Server url = https://gitlab.com/eidheim/Simple-Web-Server.git branch = master -[submodule "ViGEmClient"] +[submodule "third-party/ViGEmClient"] path = third-party/ViGEmClient url = https://github.com/ViGEm/ViGEmClient branch = master -[submodule "miniupnp"] +[submodule "third-party/miniupnp"] path = third-party/miniupnp url = https://github.com/miniupnp/miniupnp branch = master -[submodule "nv-codec-headers"] +[submodule "third-party/nv-codec-headers"] path = third-party/nv-codec-headers url = https://github.com/FFmpeg/nv-codec-headers branch = sdk/11.1 -[submodule "TPCircularBuffer"] +[submodule "third-party/TPCircularBuffer"] path = third-party/TPCircularBuffer url = https://github.com/michaeltyson/TPCircularBuffer branch = master -[submodule "ffmpeg-windows-x86_64"] +[submodule "third-party/ffmpeg-windows-x86_64"] path = third-party/ffmpeg-windows-x86_64 url = https://github.com/LizardByte/build-deps branch = ffmpeg-windows-x86_64 -[submodule "ffmpeg-macos-x86_64"] +[submodule "third-party/ffmpeg-macos-x86_64"] path = third-party/ffmpeg-macos-x86_64 url = https://github.com/LizardByte/build-deps branch = ffmpeg-macos-x86_64 -[submodule "ffmpeg-linux-x86_64"] +[submodule "third-party/ffmpeg-linux-x86_64"] path = third-party/ffmpeg-linux-x86_64 url = https://github.com/LizardByte/build-deps branch = ffmpeg-linux-x86_64 -[submodule "ffmpeg-linux-aarch64"] +[submodule "third-party/ffmpeg-linux-aarch64"] path = third-party/ffmpeg-linux-aarch64 url = https://github.com/LizardByte/build-deps branch = ffmpeg-linux-aarch64 -[submodule "ffmpeg-macos-aarch64"] +[submodule "third-party/ffmpeg-macos-aarch64"] path = third-party/ffmpeg-macos-aarch64 url = https://github.com/LizardByte/build-deps branch = ffmpeg-macos-aarch64 From d51afbe19dcf0c190b79bb4a31a0eb51919b7ee8 Mon Sep 17 00:00:00 2001 From: Conn O'Griofa Date: Mon, 9 Jan 2023 00:06:56 +0000 Subject: [PATCH 86/88] CMake: win32: fix API version, disable boost-cmake detection (#732) --- CMakeLists.txt | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 54303f01eb4..e1690e74a91 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -69,6 +69,11 @@ pkg_check_modules (CURL REQUIRED libcurl) if(NOT APPLE) set(Boost_USE_STATIC_LIBS ON) # cmake-lint: disable=C0103 endif() +if(WIN32) + # workaround to prevent link errors against icudata, icui18n + set(Boost_NO_BOOST_CMAKE ON) # cmake-lint: disable=C0103 +endif() + find_package(Boost COMPONENTS log filesystem program_options REQUIRED) list(APPEND SUNSHINE_COMPILE_OPTIONS -Wall -Wno-missing-braces -Wno-maybe-uninitialized -Wno-sign-compare) @@ -79,10 +84,6 @@ if(WIN32) set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -static") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${CURL_STATIC_LDFLAGS} ${CURL_STATIC_CFLAGS}") - # WORKAROUND: /mingw64/include/_mingw.h r186 defines _WIN32_WINNT=0x601, but boost - # is built against version 0x603 (BOOST_WINAPI_VERSION_WINBLUE) from r157 header. - ADD_DEFINITIONS(-DBOOST_USE_WINAPI_VERSION=BOOST_WINAPI_VERSION_WINBLUE) - add_compile_definitions(SUNSHINE_PLATFORM="windows") add_subdirectory(tools) # This is temporary, only tools for Windows are needed, for now From 23ef23159ac332485a2436b1d43b7e2227ca51ec Mon Sep 17 00:00:00 2001 From: ReenigneArcher <42013603+ReenigneArcher@users.noreply.github.com> Date: Sun, 8 Jan 2023 19:41:33 -0500 Subject: [PATCH 87/88] revert windows build libpsl version lock (#729) --- .github/workflows/CI.yml | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index f9821dce247..970191bc1a8 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -885,6 +885,7 @@ jobs: mingw-w64-x86_64-binutils mingw-w64-x86_64-boost mingw-w64-x86_64-cmake + mingw-w64-x86_64-curl mingw-w64-x86_64-nsis mingw-w64-x86_64-openssl mingw-w64-x86_64-opus @@ -893,18 +894,6 @@ jobs: wget yasm - - name: pin libpsl - # libpsl is pinned until https://github.com/msys2/MINGW-packages/issues/14882 is resolved - # wget above is only necessary for this step - shell: msys2 {0} - run: | - # manually download and install libpsl - wget https://repo.msys2.org/mingw/mingw64/mingw-w64-x86_64-libpsl-0.21.1-4-any.pkg.tar.zst - pacman --noconfirm -U mingw-w64-x86_64-libpsl-0.21.1-4-any.pkg.tar.zst - - # install curl - pacman --noconfirm -S mingw-w64-x86_64-curl - - name: Install npm packages run: | npm install From 009975dc7bc242515908d03e2e079cb0aeb4ff70 Mon Sep 17 00:00:00 2001 From: ReenigneArcher <42013603+ReenigneArcher@users.noreply.github.com> Date: Sun, 8 Jan 2023 20:10:13 -0500 Subject: [PATCH 88/88] prepare v0.17.0 (#708) --- CHANGELOG.md | 85 ++++++++++++++++++++++++++++++++++++++++++++++++-- CMakeLists.txt | 2 +- 2 files changed, 83 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7e2a05cef2d..d2521b68ef8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,59 @@ # Changelog +## [0.17.0] - 2023-01-08 +If you are running Sunshine as a service on Windows, we are strongly urging you to update to v0.17.0 as soon as +possible. Older Windows versions of Sunshine had a security flaw in which the binary was located in a user-writable +location which is problematic when running as a service or on a multi-user system. Additionally, when running Sunshine +as a service, games and applications were launched as SYSTEM. This could lead to issues with save files and other game +settings. In v0.17.0, games now run under your user account without elevated privileges. + +### Breaking +- (Apps) Removed automatic desktop entry (Re-add by adding an empty application named "Desktop" with no commands, "desktop.png" can be added as the image.) +- (Windows) Improved user upgrade experience (Suggest to manually uninstall existing Sunshine version before this upgrade. Do NOT select to remove everything, if prompted. Make a backup of config files before uninstall.) +- (Windows) Move config files to specific directory (files will be migrated automatically if using Windows installer) +- (Dependencies) Fix npm path (breaking change for package maintainers) +### Added +- (macOS) Added initial support for arm64 on macOS through Macports portfile +- (Input) Added support for foreign keyboard input +- (Misc) Logs inside the WebUI and log to file +- (UI/Windows) Added an Apply button to configuration page when running as a service +- (Input/Windows) Enable Mouse Keys while streaming for systems with no physical mouse +### Fixed +- (Video) Improved capture performance +- (Audio) Improved audio bitrate and quality handling +- (Apps/Windows) Fixed PATH environment variable handling +- (Apps/Windows) Use the proper environment variable for the Program Files (x86) folder +- (Service/Windows) Fix SunshineSvc hanging if an error occurs during startup +- (Service/Windows) Spawn Sunshine.exe in a job object, so it is terminated if SunshineSvc.exe dies +- (Video) windows/vram: fix fringing in NV12 colour conversion +- (Apps/Windows) Launch games under the correct user account +- (Video) nvenc, amdvce: rework all user presets/options +- (Network) Generate certificates with unique serial numbers +- (Service/Windows) Graceful termination on shutdown, logoff, and service stop +- (Apps/Windows) Fix launching apps when Sunshine is running as admin +- (Misc) Remove/fix calls to std::abort() +- (Misc) Remove prompt to press enter after Sunshine exits +- (Misc) Make log priority consistent for execution messages +- (Apps) Applications in Moonlight clients are now updated automatically after editing +- (Video/Linux) Fix wayland capture on nvidia +- (Audio) Fix 7.1 surround channel mapping +- (Video) Fix NVENC profile values not applying +- (Network) Fix origin_web_ui_allowed binding +- (Service/Windows) Self terminate/restart service if process hangs for 10 seconds +- (Input/Windows) Fix Windows masked cursor blending with GPU encoders +- (Video) Color conversion fixes and BT.2020 support +### Dependencies +- Bump ffmpeg from 4.4 to 5.1 +- ffmpeg_patches: add amfenc delay/buffering fix +- CBS moved to ffmpeg submodules +- Migrate to upstream Simple-Web-Server submodule +- Bump third-party/TPCircularBuffer from `bce9170` to `8833b3a` +- Bump third-party/moonlight-common-c from `8169a31` to `ef9ad52` +- Bump third-party/miniupnp from `6f848ae` to `207cf44` +- Bump third-party/ViGEmClient from `f719a1d` to `9e842ba` +- Bump bootstrap from 5.0.0 to 5.2.3 +- Bump @fortawesome/fontawesome-free from 6.2.0 to 6.2.1 + ## [0.16.0] - 2022-12-13 ### Added - Add cover finder @@ -61,7 +115,7 @@ - (Documentation) Added Sphinx documentation available at https://sunshinestream.readthedocs.io/en/latest/ - (Development) Initial support for Localization - (Linux) Add rpm package as release asset -- (MacOS) Add Portfile as release asset +- (macOS) Add Portfile as release asset - (Windows) Add DwmFlush() call to improve capture - (Windows) Add Windows installer ### Fixed @@ -70,13 +124,13 @@ - (Linux) Fixed rumble events causing game to freeze - (Linux) Improved Pulse/Pipewire compatibility - (Linux) Moved to single deb package -- (MacOS) Fixed missing TPCircularBuffer submodule +- (macOS) Fixed missing TPCircularBuffer submodule - (Stream) Properly catch exceptions in stream broadcast handlers - (Stream/Video) AVPacket fix ## [0.13.0] - 2022-02-27 ### Added -- (MacOS) Initial support for MacOS (#40) +- (macOS) Initial support for macOS (#40) ## [0.12.0] - 2022-02-13 ### Added @@ -193,3 +247,28 @@ ## [0.1.0] - 2020-01-27 ### Added - The first official release for Sunshine! + +[0.1.0]: https://github.com/LizardByte/Sunshine/releases/tag/v0.1.0 +[0.1.1]: https://github.com/LizardByte/Sunshine/releases/tag/v0.1.1 +[0.2.0]: https://github.com/LizardByte/Sunshine/releases/tag/v0.2.0 +[0.3.0]: https://github.com/LizardByte/Sunshine/releases/tag/v0.3.0 +[0.3.1]: https://github.com/LizardByte/Sunshine/releases/tag/v0.3.1 +[0.4.0]: https://github.com/LizardByte/Sunshine/releases/tag/v0.4.0 +[0.5.0]: https://github.com/LizardByte/Sunshine/releases/tag/0.5.0 +[0.6.0]: https://github.com/LizardByte/Sunshine/releases/tag/v0.6.0 +[0.7.0]: https://github.com/LizardByte/Sunshine/releases/tag/v0.7.0 +[0.7.1]: https://github.com/LizardByte/Sunshine/releases/tag/v0.7.1 +[0.7.7]: https://github.com/LizardByte/Sunshine/releases/tag/v0.7.7 +[0.8.0]: https://github.com/LizardByte/Sunshine/releases/tag/v0.8.0 +[0.9.0]: https://github.com/LizardByte/Sunshine/releases/tag/v0.9.0 +[0.10.0]: https://github.com/LizardByte/Sunshine/releases/tag/v0.10.0 +[0.10.1]: https://github.com/LizardByte/Sunshine/releases/tag/v0.10.1 +[0.11.0]: https://github.com/LizardByte/Sunshine/releases/tag/v0.11.0 +[0.11.1]: https://github.com/LizardByte/Sunshine/releases/tag/v0.11.1 +[0.12.0]: https://github.com/LizardByte/Sunshine/releases/tag/v0.12.0 +[0.13.0]: https://github.com/LizardByte/Sunshine/releases/tag/v0.13.0 +[0.14.0]: https://github.com/LizardByte/Sunshine/releases/tag/v0.14.0 +[0.14.1]: https://github.com/LizardByte/Sunshine/releases/tag/v0.14.1 +[0.15.0]: https://github.com/LizardByte/Sunshine/releases/tag/v0.15.0 +[0.16.0]: https://github.com/LizardByte/Sunshine/releases/tag/v0.16.0 +[0.17.0]: https://github.com/LizardByte/Sunshine/releases/tag/v0.17.0 diff --git a/CMakeLists.txt b/CMakeLists.txt index e1690e74a91..b1970b1d224 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,6 +1,6 @@ cmake_minimum_required(VERSION 3.0) -project(Sunshine VERSION 0.16.0 +project(Sunshine VERSION 0.17.0 DESCRIPTION "Sunshine is a self-hosted game stream host for Moonlight." HOMEPAGE_URL "https://app.lizardbyte.dev")