mirror of
https://github.com/thinkonmay/sunshine-sdk.git
synced 2026-08-07 04:54:35 +00:00
loosen dependency on boost
This commit is contained in:
parent
c01b3a4c25
commit
0d9dbcabfe
@ -46,8 +46,6 @@ set(SUNSHINE_TARGET_FILES
|
||||
src/move_by_copy.h
|
||||
src/thread_safe.h
|
||||
src/sync.h
|
||||
src/stat_trackers.h
|
||||
src/stat_trackers.cpp
|
||||
${PLATFORM_TARGET_FILES})
|
||||
|
||||
|
||||
|
||||
@ -6,10 +6,7 @@ enable_language(RC)
|
||||
set(CMAKE_RC_COMPILER windres)
|
||||
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -static")
|
||||
|
||||
# curl
|
||||
add_definitions(-DCURL_STATICLIB)
|
||||
include_directories(SYSTEM ${CURL_STATIC_INCLUDE_DIRS})
|
||||
link_directories(${CURL_STATIC_LIBRARY_DIRS})
|
||||
|
||||
|
||||
# extra tools/binaries for audio/display devices
|
||||
add_subdirectory(tools) # todo - this is temporary, only tools for Windows are needed, for now
|
||||
|
||||
@ -51,10 +51,8 @@ set(FFMPEG_LIBRARIES
|
||||
${FFMPEG_PLATFORM_LIBRARIES})
|
||||
|
||||
# common dependencies
|
||||
find_package(OpenSSL REQUIRED)
|
||||
find_package(PkgConfig REQUIRED)
|
||||
find_package(Threads REQUIRED)
|
||||
pkg_check_modules(CURL REQUIRED libcurl)
|
||||
|
||||
# platform specific dependencies
|
||||
if(WIN32)
|
||||
|
||||
@ -1,6 +1,4 @@
|
||||
# windows specific dependencies
|
||||
|
||||
set(Boost_USE_STATIC_LIBS ON) # cmake-lint: disable=C0103
|
||||
# Boost >= 1.82.0 is required for boost::json::value::set_at_pointer() support
|
||||
# todo - are we actually using json? I think this was attempted to be used in a PR, but we ended up not using json
|
||||
find_package(Boost 1.82.0 COMPONENTS locale log filesystem program_options json REQUIRED)
|
||||
|
||||
@ -1,7 +1,8 @@
|
||||
# common target definitions
|
||||
# this file will also load platform specific macros
|
||||
|
||||
add_library(sunshine SHARED ${SUNSHINE_TARGET_FILES})
|
||||
# add_library(sunshine SHARED ${SUNSHINE_TARGET_FILES})
|
||||
add_executable(sunshine ${SUNSHINE_TARGET_FILES})
|
||||
|
||||
# platform specific target definitions
|
||||
if(WIN32)
|
||||
|
||||
10
src/cbs.cpp
10
src/cbs.cpp
@ -59,7 +59,7 @@ namespace cbs {
|
||||
auto err = ff_cbs_insert_unit_content(&frag, -1, nal, uh, nullptr);
|
||||
if (err < 0) {
|
||||
char err_str[AV_ERROR_MAX_STRING_SIZE] { 0 };
|
||||
BOOST_LOG(error) << "Could not insert NAL unit SPS: "sv << av_make_error_string(err_str, AV_ERROR_MAX_STRING_SIZE, err);
|
||||
// BOOST_LOG(error) << "Could not insert NAL unit SPS: "sv << av_make_error_string(err_str, AV_ERROR_MAX_STRING_SIZE, err);
|
||||
|
||||
return {};
|
||||
}
|
||||
@ -67,7 +67,7 @@ namespace cbs {
|
||||
err = ff_cbs_write_fragment_data(cbs_ctx.get(), &frag);
|
||||
if (err < 0) {
|
||||
char err_str[AV_ERROR_MAX_STRING_SIZE] { 0 };
|
||||
BOOST_LOG(error) << "Could not write fragment data: "sv << av_make_error_string(err_str, AV_ERROR_MAX_STRING_SIZE, err);
|
||||
// BOOST_LOG(error) << "Could not write fragment data: "sv << av_make_error_string(err_str, AV_ERROR_MAX_STRING_SIZE, err);
|
||||
|
||||
return {};
|
||||
}
|
||||
@ -99,7 +99,7 @@ namespace cbs {
|
||||
int err = ff_cbs_read_packet(ctx.get(), &frag, packet);
|
||||
if (err < 0) {
|
||||
char err_str[AV_ERROR_MAX_STRING_SIZE] { 0 };
|
||||
BOOST_LOG(error) << "Couldn't read packet: "sv << av_make_error_string(err_str, AV_ERROR_MAX_STRING_SIZE, err);
|
||||
// BOOST_LOG(error) << "Couldn't read packet: "sv << av_make_error_string(err_str, AV_ERROR_MAX_STRING_SIZE, err);
|
||||
|
||||
return {};
|
||||
}
|
||||
@ -156,7 +156,7 @@ namespace cbs {
|
||||
int err = ff_cbs_read_packet(ctx.get(), &frag, packet);
|
||||
if (err < 0) {
|
||||
char err_str[AV_ERROR_MAX_STRING_SIZE] { 0 };
|
||||
BOOST_LOG(error) << "Couldn't read packet: "sv << av_make_error_string(err_str, AV_ERROR_MAX_STRING_SIZE, err);
|
||||
// BOOST_LOG(error) << "Couldn't read packet: "sv << av_make_error_string(err_str, AV_ERROR_MAX_STRING_SIZE, err);
|
||||
|
||||
return {};
|
||||
}
|
||||
@ -229,7 +229,7 @@ namespace cbs {
|
||||
int err = ff_cbs_read_packet(ctx.get(), &frag, packet);
|
||||
if (err < 0) {
|
||||
char err_str[AV_ERROR_MAX_STRING_SIZE] { 0 };
|
||||
BOOST_LOG(error) << "Couldn't read packet: "sv << av_make_error_string(err_str, AV_ERROR_MAX_STRING_SIZE, err);
|
||||
// BOOST_LOG(error) << "Couldn't read packet: "sv << av_make_error_string(err_str, AV_ERROR_MAX_STRING_SIZE, err);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -9,10 +9,8 @@
|
||||
#include <iostream>
|
||||
#include <unordered_map>
|
||||
|
||||
#include <boost/asio.hpp>
|
||||
#include <boost/filesystem.hpp>
|
||||
#include <boost/property_tree/json_parser.hpp>
|
||||
#include <boost/property_tree/ptree.hpp>
|
||||
|
||||
|
||||
|
||||
#include "config.h"
|
||||
#include "main.h"
|
||||
@ -46,7 +44,7 @@ namespace config {
|
||||
if (preset == "disabled") return nvenc::nvenc_two_pass::disabled;
|
||||
if (preset == "quarter_res") return nvenc::nvenc_two_pass::quarter_resolution;
|
||||
if (preset == "full_res") return nvenc::nvenc_two_pass::full_resolution;
|
||||
BOOST_LOG(warning) << "config: unknown nvenc_twopass value: " << preset;
|
||||
// BOOST_LOG(warning) << "config: unknown nvenc_twopass value: " << preset;
|
||||
return nvenc::nvenc_two_pass::quarter_resolution;
|
||||
}
|
||||
|
||||
@ -730,32 +728,6 @@ namespace config {
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
list_prep_cmd_f(std::unordered_map<std::string, std::string> &vars, const std::string &name, std::vector<prep_cmd_t> &input) {
|
||||
std::string string;
|
||||
string_f(vars, name, string);
|
||||
|
||||
std::stringstream jsonStream;
|
||||
|
||||
// check if string is empty, i.e. when the value doesn't exist in the config file
|
||||
if (string.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// We need to add a wrapping object to make it valid JSON, otherwise ptree cannot parse it.
|
||||
jsonStream << "{\"prep_cmd\":" << string << "}";
|
||||
|
||||
boost::property_tree::ptree jsonTree;
|
||||
boost::property_tree::read_json(jsonStream, jsonTree);
|
||||
|
||||
for (auto &[_, prep_cmd] : jsonTree.get_child("prep_cmd"s)) {
|
||||
auto do_cmd = prep_cmd.get_optional<std::string>("do"s);
|
||||
auto undo_cmd = prep_cmd.get_optional<std::string>("undo"s);
|
||||
auto elevated = prep_cmd.get_optional<bool>("elevated"s);
|
||||
|
||||
input.emplace_back(do_cmd.value_or(""), undo_cmd.value_or(""), elevated.value_or(false));
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
list_int_f(std::unordered_map<std::string, std::string> &vars, const std::string &name, std::vector<int> &input) {
|
||||
@ -910,8 +882,6 @@ namespace config {
|
||||
// Must be run after "file_state"
|
||||
path_f(vars, "credentials_file", config::sunshine.credentials_file);
|
||||
|
||||
list_prep_cmd_f(vars, "global_prep_cmd", config::sunshine.prep_cmds);
|
||||
|
||||
string_f(vars, "audio_sink", audio.sink);
|
||||
string_f(vars, "virtual_sink", audio.virtual_sink);
|
||||
bool_f(vars, "install_steam_audio_drivers", audio.install_steam_drivers);
|
||||
|
||||
179
src/main.cpp
179
src/main.cpp
@ -10,12 +10,6 @@
|
||||
#include <iostream>
|
||||
#include <thread>
|
||||
|
||||
// lib includes
|
||||
#include <boost/log/attributes/clock.hpp>
|
||||
#include <boost/log/common.hpp>
|
||||
#include <boost/log/expressions.hpp>
|
||||
#include <boost/log/sinks.hpp>
|
||||
#include <boost/log/sources/severity_logger.hpp>
|
||||
|
||||
// local includes
|
||||
#include "config.h"
|
||||
@ -24,80 +18,16 @@
|
||||
#include "platform/common.h"
|
||||
#include "video.h"
|
||||
|
||||
extern "C" {
|
||||
#include <libavutil/log.h>
|
||||
#ifdef _WIN32
|
||||
#include <iphlpapi.h>
|
||||
#include "platform/windows/nvprefs/nvprefs_interface.h"
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
safe::mail_t mail::man;
|
||||
|
||||
using namespace std::literals;
|
||||
namespace bl = boost::log;
|
||||
|
||||
#ifdef _WIN32
|
||||
// Define global singleton used for NVIDIA control panel modifications
|
||||
nvprefs::nvprefs_interface nvprefs_instance;
|
||||
#endif
|
||||
|
||||
bl::sources::severity_logger<int> verbose(0); // Dominating output
|
||||
bl::sources::severity_logger<int> debug(1); // Follow what is happening
|
||||
bl::sources::severity_logger<int> info(2); // Should be informed about
|
||||
bl::sources::severity_logger<int> warning(3); // Strange events
|
||||
bl::sources::severity_logger<int> error(4); // Recoverable errors
|
||||
bl::sources::severity_logger<int> fatal(5); // Unrecoverable errors
|
||||
|
||||
bool display_cursor = true;
|
||||
|
||||
using text_sink = bl::sinks::asynchronous_sink<bl::sinks::text_ostream_backend>;
|
||||
boost::shared_ptr<text_sink> sink;
|
||||
|
||||
struct NoDelete {
|
||||
void
|
||||
operator()(void *) {}
|
||||
};
|
||||
|
||||
BOOST_LOG_ATTRIBUTE_KEYWORD(severity, "Severity", int)
|
||||
|
||||
|
||||
#ifdef _WIN32
|
||||
namespace restore_nvprefs_undo {
|
||||
int
|
||||
entry(const char *name, int argc, char *argv[]) {
|
||||
// Restore global NVIDIA control panel settings to the undo file
|
||||
// left by improper termination of sunshine.exe, if it exists.
|
||||
// This entry point is typically called by the uninstaller.
|
||||
if (nvprefs_instance.load()) {
|
||||
nvprefs_instance.restore_from_and_delete_undo_file_if_exists();
|
||||
nvprefs_instance.unload();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
} // namespace restore_nvprefs_undo
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
void
|
||||
log_flush() {
|
||||
}
|
||||
|
||||
|
||||
|
||||
DeInit(){
|
||||
#ifdef WIN32
|
||||
// Restore global NVIDIA control panel settings
|
||||
if (nvprefs_instance.owning_undo_file() && nvprefs_instance.load()) {
|
||||
nvprefs_instance.restore_global_profile();
|
||||
nvprefs_instance.unload();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
static video::config_t monitor = { 1920, 1080, 60, 6000, 1, 0, 1, 0, 0 };
|
||||
|
||||
static std::shared_ptr<safe::mail_raw_t> privatemail;
|
||||
static std::chrono::steady_clock::time_point start;
|
||||
extern int __cdecl
|
||||
@ -134,109 +64,9 @@ Init(
|
||||
|
||||
|
||||
|
||||
#ifdef _WIN32
|
||||
// Switch default C standard library locale to UTF-8 on Windows 10 1803+
|
||||
setlocale(LC_ALL, ".UTF-8");
|
||||
#endif
|
||||
|
||||
// Use UTF-8 conversion for the default C++ locale (used by boost::log)
|
||||
std::locale::global(std::locale(std::locale(), new std::codecvt_utf8<wchar_t>));
|
||||
mail::man = std::make_shared<safe::mail_raw_t>();
|
||||
|
||||
|
||||
if (config::sunshine.min_log_level >= 1) {
|
||||
av_log_set_level(AV_LOG_QUIET);
|
||||
} else {
|
||||
av_log_set_level(AV_LOG_DEBUG);
|
||||
}
|
||||
|
||||
av_log_set_callback([](void *ptr, int level, const char *fmt, va_list vl) {
|
||||
static int print_prefix = 1;
|
||||
char buffer[1024];
|
||||
|
||||
av_log_format_line(ptr, level, fmt, vl, buffer, sizeof(buffer), &print_prefix);
|
||||
if (level <= AV_LOG_FATAL) {
|
||||
BOOST_LOG(fatal) << buffer;
|
||||
}
|
||||
else if (level <= AV_LOG_ERROR) {
|
||||
BOOST_LOG(error) << buffer;
|
||||
}
|
||||
else if (level <= AV_LOG_WARNING) {
|
||||
BOOST_LOG(warning) << buffer;
|
||||
}
|
||||
else if (level <= AV_LOG_INFO) {
|
||||
BOOST_LOG(info) << buffer;
|
||||
}
|
||||
else if (level <= AV_LOG_VERBOSE) {
|
||||
// AV_LOG_VERBOSE is less verbose than AV_LOG_DEBUG
|
||||
BOOST_LOG(debug) << buffer;
|
||||
}
|
||||
else {
|
||||
BOOST_LOG(verbose) << buffer;
|
||||
}
|
||||
});
|
||||
|
||||
sink = boost::make_shared<text_sink>();
|
||||
|
||||
boost::shared_ptr<std::ostream> stream { &std::cout, NoDelete {} };
|
||||
sink->locked_backend()->add_stream(stream);
|
||||
sink->locked_backend()->add_stream(boost::make_shared<std::ofstream>(config::sunshine.log_file));
|
||||
sink->set_filter(severity >= config::sunshine.min_log_level);
|
||||
|
||||
sink->set_formatter([message = "Message"s, severity = "Severity"s](const bl::record_view &view, bl::formatting_ostream &os) {
|
||||
constexpr int DATE_BUFFER_SIZE = 21 + 2 + 1; // Full string plus ": \0"
|
||||
|
||||
auto log_level = view.attribute_values()[severity].extract<int>().get();
|
||||
|
||||
std::string_view log_type;
|
||||
switch (log_level) {
|
||||
case 0:
|
||||
log_type = "Verbose: "sv;
|
||||
break;
|
||||
case 1:
|
||||
log_type = "Debug: "sv;
|
||||
break;
|
||||
case 2:
|
||||
log_type = "Info: "sv;
|
||||
break;
|
||||
case 3:
|
||||
log_type = "Warning: "sv;
|
||||
break;
|
||||
case 4:
|
||||
log_type = "Error: "sv;
|
||||
break;
|
||||
case 5:
|
||||
log_type = "Fatal: "sv;
|
||||
break;
|
||||
};
|
||||
|
||||
char _date[DATE_BUFFER_SIZE];
|
||||
std::time_t t = std::time(nullptr);
|
||||
strftime(_date, DATE_BUFFER_SIZE, "[%Y:%m:%d:%H:%M:%S]: ", std::localtime(&t));
|
||||
|
||||
os << _date << log_type << view.attribute_values()[message].extract<std::string>();
|
||||
});
|
||||
|
||||
// Flush after each log record to ensure log file contents on disk isn't stale.
|
||||
// This is particularly important when running from a Windows service.
|
||||
sink->locked_backend()->auto_flush(true);
|
||||
|
||||
bl::core::get()->add_sink(sink);
|
||||
auto fg = util::fail_guard([&sink](){ sink->flush(); });
|
||||
|
||||
#ifdef WIN32
|
||||
// Modify relevant NVIDIA control panel settings if the system has corresponding gpu
|
||||
if (nvprefs_instance.load()) {
|
||||
// Restore global settings to the undo file left by improper termination of sunshine.exe
|
||||
nvprefs_instance.restore_from_and_delete_undo_file_if_exists();
|
||||
// Modify application settings for sunshine.exe
|
||||
nvprefs_instance.modify_application_profile();
|
||||
// Modify global settings, undo file is produced in the process to restore after improper termination
|
||||
nvprefs_instance.modify_global_profile();
|
||||
// Unload dynamic library to survive driver reinstallation
|
||||
nvprefs_instance.unload();
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
@ -245,13 +75,13 @@ Init(
|
||||
// This allows access to the UI to fix configuration problems or view the logs.
|
||||
auto deinit_guard = platf::init();
|
||||
if (!deinit_guard) {
|
||||
BOOST_LOG(error) << "Platform failed to initialize"sv;
|
||||
// BOOST_LOG(error) << "Platform failed to initialize"sv;
|
||||
return 1;
|
||||
} else if (video::probe_encoders()) {
|
||||
BOOST_LOG(error) << "Video failed to find working encoder"sv;
|
||||
// BOOST_LOG(error) << "Video failed to find working encoder"sv;
|
||||
return 1;
|
||||
} else {
|
||||
BOOST_LOG(error) << "Hello from thinkmay."sv;
|
||||
// BOOST_LOG(error) << "Hello from thinkmay."sv;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -259,7 +89,6 @@ Init(
|
||||
|
||||
// Create signal handler after logging has been initialized
|
||||
mail::man->event<bool>(mail::shutdown)->view();
|
||||
DeInit();
|
||||
}
|
||||
|
||||
|
||||
|
||||
16
src/main.h
16
src/main.h
@ -9,21 +9,19 @@
|
||||
// standard includes
|
||||
#include <filesystem>
|
||||
#include <string_view>
|
||||
|
||||
// lib includes
|
||||
#include <boost/log/common.hpp>
|
||||
#include <bitset>
|
||||
#include <climits>
|
||||
#include <cstring>
|
||||
#include <iostream>
|
||||
#include <stdio.h> /* printf */
|
||||
#include <assert.h> /* assert */
|
||||
|
||||
// local includes
|
||||
#include "thread_safe.h"
|
||||
|
||||
extern bool display_cursor;
|
||||
|
||||
extern boost::log::sources::severity_logger<int> verbose;
|
||||
extern boost::log::sources::severity_logger<int> debug;
|
||||
extern boost::log::sources::severity_logger<int> info;
|
||||
extern boost::log::sources::severity_logger<int> warning;
|
||||
extern boost::log::sources::severity_logger<int> error;
|
||||
extern boost::log::sources::severity_logger<int> fatal;
|
||||
|
||||
|
||||
|
||||
// namespaces
|
||||
|
||||
@ -95,19 +95,19 @@ namespace nvenc {
|
||||
session_params.deviceType = device_type;
|
||||
session_params.apiVersion = NVENCAPI_VERSION;
|
||||
if (nvenc_failed(nvenc->nvEncOpenEncodeSessionEx(&session_params, &encoder))) {
|
||||
BOOST_LOG(error) << "NvEncOpenEncodeSessionEx failed";
|
||||
// BOOST_LOG(error) << "NvEncOpenEncodeSessionEx failed";
|
||||
return false;
|
||||
}
|
||||
|
||||
uint32_t encode_guid_count = 0;
|
||||
if (nvenc_failed(nvenc->nvEncGetEncodeGUIDCount(encoder, &encode_guid_count))) {
|
||||
BOOST_LOG(error) << "NvEncGetEncodeGUIDCount failed: " << last_error_string;
|
||||
// BOOST_LOG(error) << "NvEncGetEncodeGUIDCount failed: " << last_error_string;
|
||||
return false;
|
||||
};
|
||||
|
||||
std::vector<GUID> encode_guids(encode_guid_count);
|
||||
if (nvenc_failed(nvenc->nvEncGetEncodeGUIDs(encoder, encode_guids.data(), encode_guids.size(), &encode_guid_count))) {
|
||||
BOOST_LOG(error) << "NvEncGetEncodeGUIDs failed: " << last_error_string;
|
||||
// BOOST_LOG(error) << "NvEncGetEncodeGUIDs failed: " << last_error_string;
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -130,7 +130,7 @@ namespace nvenc {
|
||||
break;
|
||||
|
||||
default:
|
||||
BOOST_LOG(error) << "NvEnc: unknown video format " << client_config.videoFormat;
|
||||
// BOOST_LOG(error) << "NvEnc: unknown video format " << client_config.videoFormat;
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -139,7 +139,7 @@ namespace nvenc {
|
||||
return equal_guids(init_params.encodeGUID, guid);
|
||||
};
|
||||
if (std::find_if(encode_guids.begin(), encode_guids.end(), search_predicate) == encode_guids.end()) {
|
||||
BOOST_LOG(error) << "NvEnc: encoding format is not supported by the gpu";
|
||||
// BOOST_LOG(error) << "NvEnc: encoding format is not supported by the gpu";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@ -163,23 +163,23 @@ namespace nvenc {
|
||||
auto supported_width = get_encoder_cap(NV_ENC_CAPS_WIDTH_MAX);
|
||||
auto supported_height = get_encoder_cap(NV_ENC_CAPS_HEIGHT_MAX);
|
||||
if (encoder_params.width > supported_width || encoder_params.height > supported_height) {
|
||||
BOOST_LOG(error) << "NvEnc: gpu max encode resolution " << supported_width << "x" << supported_height << ", requested " << encoder_params.width << "x" << encoder_params.height;
|
||||
// BOOST_LOG(error) << "NvEnc: gpu max encode resolution " << supported_width << "x" << supported_height << ", requested " << encoder_params.width << "x" << encoder_params.height;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (buffer_is_10bit() && !get_encoder_cap(NV_ENC_CAPS_SUPPORT_10BIT_ENCODE)) {
|
||||
BOOST_LOG(error) << "NvEnc: gpu doesn't support 10-bit encode";
|
||||
// BOOST_LOG(error) << "NvEnc: gpu doesn't support 10-bit encode";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (buffer_is_yuv444() && !get_encoder_cap(NV_ENC_CAPS_SUPPORT_YUV444_ENCODE)) {
|
||||
BOOST_LOG(error) << "NvEnc: gpu doesn't support YUV444 encode";
|
||||
// BOOST_LOG(error) << "NvEnc: gpu doesn't support YUV444 encode";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (async_event_handle && !get_encoder_cap(NV_ENC_CAPS_ASYNC_ENCODE_SUPPORT)) {
|
||||
BOOST_LOG(warning) << "NvEnc: gpu doesn't support async encode";
|
||||
// BOOST_LOG(warning) << "NvEnc: gpu doesn't support async encode";
|
||||
async_event_handle = nullptr;
|
||||
}
|
||||
|
||||
@ -200,7 +200,7 @@ namespace nvenc {
|
||||
|
||||
NV_ENC_PRESET_CONFIG preset_config = { NV_ENC_PRESET_CONFIG_VER, { NV_ENC_CONFIG_VER } };
|
||||
if (nvenc_failed(nvenc->nvEncGetEncodePresetConfigEx(encoder, init_params.encodeGUID, init_params.presetGUID, init_params.tuningInfo, &preset_config))) {
|
||||
BOOST_LOG(error) << "NvEncGetEncodePresetConfigEx failed: " << last_error_string;
|
||||
// BOOST_LOG(error) << "NvEncGetEncodePresetConfigEx failed: " << last_error_string;
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -335,7 +335,7 @@ namespace nvenc {
|
||||
init_params.encodeConfig = &enc_config;
|
||||
|
||||
if (nvenc_failed(nvenc->nvEncInitializeEncoder(encoder, &init_params))) {
|
||||
BOOST_LOG(error) << "NvEncInitializeEncoder failed: " << last_error_string;
|
||||
// BOOST_LOG(error) << "NvEncInitializeEncoder failed: " << last_error_string;
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -343,14 +343,14 @@ namespace nvenc {
|
||||
NV_ENC_EVENT_PARAMS event_params = { NV_ENC_EVENT_PARAMS_VER };
|
||||
event_params.completionEvent = async_event_handle;
|
||||
if (nvenc_failed(nvenc->nvEncRegisterAsyncEvent(encoder, &event_params))) {
|
||||
BOOST_LOG(error) << "NvEncRegisterAsyncEvent failed: " << last_error_string;
|
||||
// BOOST_LOG(error) << "NvEncRegisterAsyncEvent failed: " << last_error_string;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
NV_ENC_CREATE_BITSTREAM_BUFFER create_bitstream_buffer = { NV_ENC_CREATE_BITSTREAM_BUFFER_VER };
|
||||
if (nvenc_failed(nvenc->nvEncCreateBitstreamBuffer(encoder, &create_bitstream_buffer))) {
|
||||
BOOST_LOG(error) << "NvEncCreateBitstreamBuffer failed: " << last_error_string;
|
||||
// BOOST_LOG(error) << "NvEncCreateBitstreamBuffer failed: " << last_error_string;
|
||||
return false;
|
||||
}
|
||||
output_bitstream = create_bitstream_buffer.bitstreamBuffer;
|
||||
@ -360,8 +360,8 @@ namespace nvenc {
|
||||
}
|
||||
|
||||
{
|
||||
auto f = stat_trackers::one_digit_after_decimal();
|
||||
BOOST_LOG(debug) << "NvEnc: requested encoded frame size " << f % (client_config.bitrate / 8. / client_config.framerate) << " kB";
|
||||
// auto f = stat_trackers::one_digit_after_decimal();
|
||||
// BOOST_LOG(debug) << "NvEnc: requested encoded frame size " << f % (client_config.bitrate / 8. / client_config.framerate) << " kB";
|
||||
}
|
||||
|
||||
{
|
||||
@ -374,7 +374,7 @@ namespace nvenc {
|
||||
if (enc_config.rcParams.enableAQ) extra += " adaptive-quantization";
|
||||
if (enc_config.rcParams.enableMinQP) extra += " qpmin=" + std::to_string(enc_config.rcParams.minQP.qpInterP);
|
||||
if (config.insert_filler_data) extra += " filler-data";
|
||||
BOOST_LOG(info) << "NvEnc: created encoder " << quality_preset_string_from_guid(init_params.presetGUID) << extra;
|
||||
// BOOST_LOG(info) << "NvEnc: created encoder " << quality_preset_string_from_guid(init_params.presetGUID) << extra;
|
||||
}
|
||||
|
||||
encoder_state = {};
|
||||
@ -419,7 +419,7 @@ namespace nvenc {
|
||||
mapped_input_buffer.registeredResource = registered_input_buffer;
|
||||
|
||||
if (nvenc_failed(nvenc->nvEncMapInputResource(encoder, &mapped_input_buffer))) {
|
||||
BOOST_LOG(error) << "NvEncMapInputResource failed: " << last_error_string;
|
||||
// BOOST_LOG(error) << "NvEncMapInputResource failed: " << last_error_string;
|
||||
return {};
|
||||
}
|
||||
auto unmap_guard = util::fail_guard([&] { nvenc->nvEncUnmapInputResource(encoder, &mapped_input_buffer); });
|
||||
@ -436,7 +436,7 @@ namespace nvenc {
|
||||
pic_params.completionEvent = async_event_handle;
|
||||
|
||||
if (nvenc_failed(nvenc->nvEncEncodePicture(encoder, &pic_params))) {
|
||||
BOOST_LOG(error) << "NvEncEncodePicture failed: " << last_error_string;
|
||||
// BOOST_LOG(error) << "NvEncEncodePicture failed: " << last_error_string;
|
||||
return {};
|
||||
}
|
||||
|
||||
@ -445,12 +445,12 @@ namespace nvenc {
|
||||
lock_bitstream.doNotWait = 0;
|
||||
|
||||
if (async_event_handle && !wait_for_async_event(100)) {
|
||||
BOOST_LOG(error) << "NvEnc: frame " << frame_index << " encode wait timeout";
|
||||
// BOOST_LOG(error) << "NvEnc: frame " << frame_index << " encode wait timeout";
|
||||
return {};
|
||||
}
|
||||
|
||||
if (nvenc_failed(nvenc->nvEncLockBitstream(encoder, &lock_bitstream))) {
|
||||
BOOST_LOG(error) << "NvEncLockBitstream failed: " << last_error_string;
|
||||
// BOOST_LOG(error) << "NvEncLockBitstream failed: " << last_error_string;
|
||||
return {};
|
||||
}
|
||||
|
||||
@ -470,22 +470,22 @@ namespace nvenc {
|
||||
encoder_state.last_encoded_frame_index = frame_index;
|
||||
|
||||
if (encoded_frame.idr) {
|
||||
BOOST_LOG(debug) << "NvEnc: idr frame " << encoded_frame.frame_index;
|
||||
// BOOST_LOG(debug) << "NvEnc: idr frame " << encoded_frame.frame_index;
|
||||
}
|
||||
|
||||
if (nvenc_failed(nvenc->nvEncUnlockBitstream(encoder, lock_bitstream.outputBitstream))) {
|
||||
BOOST_LOG(error) << "NvEncUnlockBitstream failed: " << last_error_string;
|
||||
// BOOST_LOG(error) << "NvEncUnlockBitstream failed: " << last_error_string;
|
||||
}
|
||||
|
||||
if (config::sunshine.min_log_level <= 1) {
|
||||
// Print encoded frame size stats to debug log every 20 seconds
|
||||
auto callback = [&](float stat_min, float stat_max, double stat_avg) {
|
||||
auto f = stat_trackers::one_digit_after_decimal();
|
||||
BOOST_LOG(debug) << "NvEnc: encoded frame sizes (min max avg) " << f % stat_min << " " << f % stat_max << " " << f % stat_avg << " kB";
|
||||
};
|
||||
using namespace std::literals;
|
||||
encoder_state.frame_size_tracker.collect_and_callback_on_interval(encoded_frame.data.size() / 1000., callback, 20s);
|
||||
}
|
||||
// if (config::sunshine.min_log_level <= 1) {
|
||||
// // Print encoded frame size stats to debug log every 20 seconds
|
||||
// auto callback = [&](float stat_min, float stat_max, double stat_avg) {
|
||||
// auto f = stat_trackers::one_digit_after_decimal();
|
||||
// // BOOST_LOG(debug) << "NvEnc: encoded frame sizes (min max avg) " << f % stat_min << " " << f % stat_max << " " << f % stat_avg << " kB";
|
||||
// };
|
||||
// using namespace std::literals;
|
||||
// encoder_state.frame_size_tracker.collect_and_callback_on_interval(encoded_frame.data.size() / 1000., callback, 20s);
|
||||
// }
|
||||
|
||||
return encoded_frame;
|
||||
}
|
||||
@ -496,30 +496,30 @@ namespace nvenc {
|
||||
|
||||
if (first_frame >= encoder_state.last_rfi_range.first &&
|
||||
last_frame <= encoder_state.last_rfi_range.second) {
|
||||
BOOST_LOG(debug) << "NvEnc: rfi request " << first_frame << "-" << last_frame << " already done";
|
||||
// BOOST_LOG(debug) << "NvEnc: rfi request " << first_frame << "-" << last_frame << " already done";
|
||||
return true;
|
||||
}
|
||||
|
||||
encoder_state.rfi_needs_confirmation = true;
|
||||
|
||||
if (last_frame < first_frame) {
|
||||
BOOST_LOG(error) << "NvEnc: invaid rfi request " << first_frame << "-" << last_frame << ", generating IDR";
|
||||
// BOOST_LOG(error) << "NvEnc: invaid rfi request " << first_frame << "-" << last_frame << ", generating IDR";
|
||||
return false;
|
||||
}
|
||||
|
||||
BOOST_LOG(debug) << "NvEnc: rfi request " << first_frame << "-" << last_frame << " expanding to last encoded frame " << encoder_state.last_encoded_frame_index;
|
||||
// BOOST_LOG(debug) << "NvEnc: rfi request " << first_frame << "-" << last_frame << " expanding to last encoded frame " << encoder_state.last_encoded_frame_index;
|
||||
last_frame = encoder_state.last_encoded_frame_index;
|
||||
|
||||
encoder_state.last_rfi_range = { first_frame, last_frame };
|
||||
|
||||
if (last_frame - first_frame + 1 >= encoder_params.ref_frames_in_dpb) {
|
||||
BOOST_LOG(debug) << "NvEnc: rfi request too large, generating IDR";
|
||||
// BOOST_LOG(debug) << "NvEnc: rfi request too large, generating IDR";
|
||||
return false;
|
||||
}
|
||||
|
||||
for (auto i = first_frame; i <= last_frame; i++) {
|
||||
if (nvenc_failed(nvenc->nvEncInvalidateRefFrames(encoder, i))) {
|
||||
BOOST_LOG(error) << "NvEncInvalidateRefFrames " << i << " failed: " << last_error_string;
|
||||
// BOOST_LOG(error) << "NvEncInvalidateRefFrames " << i << " failed: " << last_error_string;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@ -4,7 +4,6 @@
|
||||
#include "nvenc_config.h"
|
||||
#include "nvenc_encoded_frame.h"
|
||||
|
||||
#include "src/stat_trackers.h"
|
||||
#include "src/video.h"
|
||||
|
||||
#include <ffnvcodec/nvEncodeAPI.h>
|
||||
@ -73,7 +72,7 @@ namespace nvenc {
|
||||
uint64_t last_encoded_frame_index = 0;
|
||||
bool rfi_needs_confirmation = false;
|
||||
std::pair<uint64_t, uint64_t> last_rfi_range;
|
||||
stat_trackers::min_max_avg_tracker<float> frame_size_tracker;
|
||||
// stat_trackers::min_max_avg_tracker<float> frame_size_tracker;
|
||||
} encoder_state;
|
||||
};
|
||||
|
||||
|
||||
@ -39,7 +39,7 @@ namespace nvenc {
|
||||
auto new_nvenc = std::make_unique<NV_ENCODE_API_FUNCTION_LIST>();
|
||||
new_nvenc->version = NV_ENCODE_API_FUNCTION_LIST_VER;
|
||||
if (nvenc_failed(create_instance(new_nvenc.get()))) {
|
||||
BOOST_LOG(error) << "NvEncodeAPICreateInstance failed: " << last_error_string;
|
||||
// BOOST_LOG(error) << "NvEncodeAPICreateInstance failed: " << last_error_string;
|
||||
}
|
||||
else {
|
||||
nvenc = std::move(new_nvenc);
|
||||
@ -47,11 +47,11 @@ namespace nvenc {
|
||||
}
|
||||
}
|
||||
else {
|
||||
BOOST_LOG(error) << "No NvEncodeAPICreateInstance in " << dll_name;
|
||||
// BOOST_LOG(error) << "No NvEncodeAPICreateInstance in " << dll_name;
|
||||
}
|
||||
}
|
||||
else {
|
||||
BOOST_LOG(debug) << "Couldn't load NvEnc library " << dll_name;
|
||||
// BOOST_LOG(debug) << "Couldn't load NvEnc library " << dll_name;
|
||||
}
|
||||
|
||||
if (dll) {
|
||||
@ -75,7 +75,7 @@ namespace nvenc {
|
||||
desc.Usage = D3D11_USAGE_DEFAULT;
|
||||
desc.BindFlags = D3D11_BIND_RENDER_TARGET;
|
||||
if (d3d_device->CreateTexture2D(&desc, nullptr, &d3d_input_texture) != S_OK) {
|
||||
BOOST_LOG(error) << "NvEnc: couldn't create input texture";
|
||||
// BOOST_LOG(error) << "NvEnc: couldn't create input texture";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@ -90,7 +90,7 @@ namespace nvenc {
|
||||
register_resource.bufferUsage = NV_ENC_INPUT_IMAGE;
|
||||
|
||||
if (nvenc_failed(nvenc->nvEncRegisterResource(encoder, ®ister_resource))) {
|
||||
BOOST_LOG(error) << "NvEncRegisterResource failed: " << last_error_string;
|
||||
// BOOST_LOG(error) << "NvEncRegisterResource failed: " << last_error_string;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@ -44,25 +44,7 @@ struct AVFrame;
|
||||
struct AVBufferRef;
|
||||
struct AVHWFramesContext;
|
||||
|
||||
// Forward declarations of boost classes to avoid having to include boost headers
|
||||
// here, which results in issues with Windows.h and WinSock2.h include order.
|
||||
namespace boost {
|
||||
namespace asio {
|
||||
namespace ip {
|
||||
class address;
|
||||
} // namespace ip
|
||||
} // namespace asio
|
||||
namespace filesystem {
|
||||
class path;
|
||||
}
|
||||
namespace process {
|
||||
class child;
|
||||
class group;
|
||||
template <typename Char>
|
||||
class basic_environment;
|
||||
typedef basic_environment<char> environment;
|
||||
} // namespace process
|
||||
} // namespace boost
|
||||
|
||||
namespace video {
|
||||
struct config_t;
|
||||
} // namespace video
|
||||
@ -396,7 +378,7 @@ namespace platf {
|
||||
*/
|
||||
virtual int
|
||||
set_frame(AVFrame *frame, AVBufferRef *hw_frames_ctx) {
|
||||
BOOST_LOG(error) << "Illegal call to hwdevice_t::set_frame(). Did you forget to override it?";
|
||||
// BOOST_LOG(error) << "Illegal call to hwdevice_t::set_frame(). Did you forget to override it?";
|
||||
return -1;
|
||||
};
|
||||
|
||||
@ -575,9 +557,6 @@ namespace platf {
|
||||
std::vector<std::string>
|
||||
display_names(mem_type_e hwdevice_type);
|
||||
|
||||
boost::process::child
|
||||
run_command(bool elevated, bool interactive, const std::string &cmd, boost::filesystem::path &working_dir, const boost::process::environment &env, FILE *file, std::error_code &ec, boost::process::group *group);
|
||||
|
||||
enum class thread_priority_e : int {
|
||||
low,
|
||||
normal,
|
||||
@ -587,33 +566,6 @@ namespace platf {
|
||||
void
|
||||
adjust_thread_priority(thread_priority_e priority);
|
||||
|
||||
|
||||
|
||||
struct batched_send_info_t {
|
||||
const char *buffer;
|
||||
size_t block_size;
|
||||
size_t block_count;
|
||||
|
||||
std::uintptr_t native_socket;
|
||||
boost::asio::ip::address &target_address;
|
||||
uint16_t target_port;
|
||||
boost::asio::ip::address &source_address;
|
||||
};
|
||||
bool
|
||||
send_batch(batched_send_info_t &send_info);
|
||||
|
||||
struct send_info_t {
|
||||
const char *buffer;
|
||||
size_t size;
|
||||
|
||||
std::uintptr_t native_socket;
|
||||
boost::asio::ip::address &target_address;
|
||||
uint16_t target_port;
|
||||
boost::asio::ip::address &source_address;
|
||||
};
|
||||
bool
|
||||
send(send_info_t &send_info);
|
||||
|
||||
enum class qos_data_type_e : int {
|
||||
audio,
|
||||
video
|
||||
|
||||
@ -85,7 +85,7 @@ namespace platf {
|
||||
return device;
|
||||
}
|
||||
else {
|
||||
BOOST_LOG(error) << "Unsupported Pixel Format."sv;
|
||||
// BOOST_LOG(error) << "Unsupported Pixel Format."sv;
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
@ -136,7 +136,7 @@ namespace platf {
|
||||
std::shared_ptr<display_t>
|
||||
display(platf::mem_type_e hwdevice_type, const std::string &display_name, const video::config_t &config) {
|
||||
if (hwdevice_type != platf::mem_type_e::system && hwdevice_type != platf::mem_type_e::videotoolbox) {
|
||||
BOOST_LOG(error) << "Could not initialize display with the given hw device type."sv;
|
||||
// BOOST_LOG(error) << "Could not initialize display with the given hw device type."sv;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
@ -158,7 +158,7 @@ namespace platf {
|
||||
display->av_capture = [[AVVideo alloc] initWithDisplay:display->display_id frameRate:config.framerate];
|
||||
|
||||
if (!display->av_capture) {
|
||||
BOOST_LOG(error) << "Video setup failed."sv;
|
||||
// BOOST_LOG(error) << "Video setup failed."sv;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
@ -21,12 +21,9 @@
|
||||
#include "src/main.h"
|
||||
#include "src/platform/common.h"
|
||||
|
||||
#include <boost/asio/ip/address.hpp>
|
||||
#include <boost/process.hpp>
|
||||
|
||||
using namespace std::literals;
|
||||
namespace fs = std::filesystem;
|
||||
namespace bp = boost::process;
|
||||
|
||||
namespace platf {
|
||||
|
||||
@ -61,8 +58,8 @@ namespace platf {
|
||||
// Double check that these weakly-linked symbols have been loaded:
|
||||
CGPreflightScreenCaptureAccess != nullptr && CGRequestScreenCaptureAccess != nullptr &&
|
||||
!CGPreflightScreenCaptureAccess()) {
|
||||
BOOST_LOG(error) << "No screen capture permission!"sv;
|
||||
BOOST_LOG(error) << "Please activate it in 'System Preferences' -> 'Privacy' -> 'Screen Recording'"sv;
|
||||
// BOOST_LOG(error) << "No screen capture permission!"sv;
|
||||
// BOOST_LOG(error) << "Please activate it in 'System Preferences' -> 'Privacy' -> 'Screen Recording'"sv;
|
||||
CGRequestScreenCaptureAccess();
|
||||
return nullptr;
|
||||
}
|
||||
@ -128,162 +125,12 @@ namespace platf {
|
||||
return { port, std::string { data } };
|
||||
}
|
||||
|
||||
bp::child
|
||||
run_command(bool elevated, bool interactive, const std::string &cmd, boost::filesystem::path &working_dir, const bp::environment &env, FILE *file, std::error_code &ec, bp::group *group) {
|
||||
if (!group) {
|
||||
if (!file) {
|
||||
return bp::child(cmd, env, bp::start_dir(working_dir), bp::std_out > bp::null, bp::std_err > bp::null, ec);
|
||||
}
|
||||
else {
|
||||
return bp::child(cmd, env, bp::start_dir(working_dir), bp::std_out > file, bp::std_err > file, ec);
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (!file) {
|
||||
return bp::child(cmd, env, bp::start_dir(working_dir), bp::std_out > bp::null, bp::std_err > bp::null, ec, *group);
|
||||
}
|
||||
else {
|
||||
return bp::child(cmd, env, bp::start_dir(working_dir), bp::std_out > file, bp::std_err > file, ec, *group);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
void
|
||||
adjust_thread_priority(thread_priority_e priority) {
|
||||
// Unimplemented
|
||||
}
|
||||
|
||||
struct sockaddr_in
|
||||
to_sockaddr(boost::asio::ip::address_v4 address, uint16_t port) {
|
||||
struct sockaddr_in saddr_v4 = {};
|
||||
|
||||
saddr_v4.sin_family = AF_INET;
|
||||
saddr_v4.sin_port = htons(port);
|
||||
|
||||
auto addr_bytes = address.to_bytes();
|
||||
memcpy(&saddr_v4.sin_addr, addr_bytes.data(), sizeof(saddr_v4.sin_addr));
|
||||
|
||||
return saddr_v4;
|
||||
}
|
||||
|
||||
struct sockaddr_in6
|
||||
to_sockaddr(boost::asio::ip::address_v6 address, uint16_t port) {
|
||||
struct sockaddr_in6 saddr_v6 = {};
|
||||
|
||||
saddr_v6.sin6_family = AF_INET6;
|
||||
saddr_v6.sin6_port = htons(port);
|
||||
saddr_v6.sin6_scope_id = address.scope_id();
|
||||
|
||||
auto addr_bytes = address.to_bytes();
|
||||
memcpy(&saddr_v6.sin6_addr, addr_bytes.data(), sizeof(saddr_v6.sin6_addr));
|
||||
|
||||
return saddr_v6;
|
||||
}
|
||||
|
||||
bool
|
||||
send_batch(batched_send_info_t &send_info) {
|
||||
// Fall back to unbatched send calls
|
||||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
send(send_info_t &send_info) {
|
||||
auto sockfd = (int) send_info.native_socket;
|
||||
struct msghdr msg = {};
|
||||
|
||||
// Convert the target address into a sockaddr
|
||||
struct sockaddr_in taddr_v4 = {};
|
||||
struct sockaddr_in6 taddr_v6 = {};
|
||||
if (send_info.target_address.is_v6()) {
|
||||
taddr_v6 = to_sockaddr(send_info.target_address.to_v6(), send_info.target_port);
|
||||
|
||||
msg.msg_name = (struct sockaddr *) &taddr_v6;
|
||||
msg.msg_namelen = sizeof(taddr_v6);
|
||||
}
|
||||
else {
|
||||
taddr_v4 = to_sockaddr(send_info.target_address.to_v4(), send_info.target_port);
|
||||
|
||||
msg.msg_name = (struct sockaddr *) &taddr_v4;
|
||||
msg.msg_namelen = sizeof(taddr_v4);
|
||||
}
|
||||
|
||||
union {
|
||||
char buf[std::max(CMSG_SPACE(sizeof(struct in_pktinfo)), CMSG_SPACE(sizeof(struct in6_pktinfo)))];
|
||||
struct cmsghdr alignment;
|
||||
} cmbuf;
|
||||
socklen_t cmbuflen = 0;
|
||||
|
||||
msg.msg_control = cmbuf.buf;
|
||||
msg.msg_controllen = sizeof(cmbuf.buf);
|
||||
|
||||
auto pktinfo_cm = CMSG_FIRSTHDR(&msg);
|
||||
if (send_info.source_address.is_v6()) {
|
||||
struct in6_pktinfo pktInfo;
|
||||
|
||||
struct sockaddr_in6 saddr_v6 = to_sockaddr(send_info.source_address.to_v6(), 0);
|
||||
pktInfo.ipi6_addr = saddr_v6.sin6_addr;
|
||||
pktInfo.ipi6_ifindex = 0;
|
||||
|
||||
cmbuflen += CMSG_SPACE(sizeof(pktInfo));
|
||||
|
||||
pktinfo_cm->cmsg_level = IPPROTO_IPV6;
|
||||
pktinfo_cm->cmsg_type = IPV6_PKTINFO;
|
||||
pktinfo_cm->cmsg_len = CMSG_LEN(sizeof(pktInfo));
|
||||
memcpy(CMSG_DATA(pktinfo_cm), &pktInfo, sizeof(pktInfo));
|
||||
}
|
||||
else {
|
||||
struct in_pktinfo pktInfo;
|
||||
|
||||
struct sockaddr_in saddr_v4 = to_sockaddr(send_info.source_address.to_v4(), 0);
|
||||
pktInfo.ipi_spec_dst = saddr_v4.sin_addr;
|
||||
pktInfo.ipi_ifindex = 0;
|
||||
|
||||
cmbuflen += CMSG_SPACE(sizeof(pktInfo));
|
||||
|
||||
pktinfo_cm->cmsg_level = IPPROTO_IP;
|
||||
pktinfo_cm->cmsg_type = IP_PKTINFO;
|
||||
pktinfo_cm->cmsg_len = CMSG_LEN(sizeof(pktInfo));
|
||||
memcpy(CMSG_DATA(pktinfo_cm), &pktInfo, sizeof(pktInfo));
|
||||
}
|
||||
|
||||
struct iovec iov = {};
|
||||
iov.iov_base = (void *) send_info.buffer;
|
||||
iov.iov_len = send_info.size;
|
||||
|
||||
msg.msg_iov = &iov;
|
||||
msg.msg_iovlen = 1;
|
||||
|
||||
msg.msg_controllen = cmbuflen;
|
||||
|
||||
auto bytes_sent = sendmsg(sockfd, &msg, 0);
|
||||
|
||||
// If there's no send buffer space, wait for some to be available
|
||||
while (bytes_sent < 0 && errno == EAGAIN) {
|
||||
struct pollfd pfd;
|
||||
|
||||
pfd.fd = sockfd;
|
||||
pfd.events = POLLOUT;
|
||||
|
||||
if (poll(&pfd, 1, -1) != 1) {
|
||||
BOOST_LOG(warning) << "poll() failed: "sv << errno;
|
||||
break;
|
||||
}
|
||||
|
||||
// Try to send again
|
||||
bytes_sent = sendmsg(sockfd, &msg, 0);
|
||||
}
|
||||
|
||||
if (bytes_sent < 0) {
|
||||
BOOST_LOG(warning) << "sendmsg() failed: "sv << errno;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
} // namespace platf
|
||||
|
||||
namespace dyn {
|
||||
@ -306,7 +153,7 @@ namespace dyn {
|
||||
|
||||
ss << ']';
|
||||
|
||||
BOOST_LOG(error) << ss.str();
|
||||
// BOOST_LOG(error) << ss.str();
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
@ -320,7 +167,7 @@ namespace dyn {
|
||||
*fn = (void (*)()) dlsym(handle, name);
|
||||
|
||||
if (!*fn && strict) {
|
||||
BOOST_LOG(error) << "Couldn't find function: "sv << name;
|
||||
// BOOST_LOG(error) << "Couldn't find function: "sv << name;
|
||||
|
||||
err = -1;
|
||||
}
|
||||
|
||||
@ -17,7 +17,6 @@ typedef long NTSTATUS;
|
||||
#include "src/config.h"
|
||||
#include "src/main.h"
|
||||
#include "src/platform/common.h"
|
||||
#include "src/stat_trackers.h"
|
||||
#include "src/video.h"
|
||||
|
||||
namespace platf {
|
||||
@ -41,7 +40,7 @@ namespace platf::dxgi {
|
||||
// is on screen the whole time, so we can't just print when it changes. Instead we'll keep track
|
||||
// of the last time we printed the warning and print another if we haven't printed one recently.
|
||||
if (frame_info.ProtectedContentMaskedOut && std::chrono::steady_clock::now() > last_protected_content_warning_time + 10s) {
|
||||
BOOST_LOG(warning) << "Windows is currently blocking DRM-protected content from capture. You may see black regions where this content would be."sv;
|
||||
// BOOST_LOG(warning) << "Windows is currently blocking DRM-protected content from capture. You may see black regions where this content would be."sv;
|
||||
last_protected_content_warning_time = std::chrono::steady_clock::now();
|
||||
}
|
||||
|
||||
@ -54,7 +53,7 @@ namespace platf::dxgi {
|
||||
case DXGI_ERROR_ACCESS_DENIED:
|
||||
return capture_e::reinit;
|
||||
default:
|
||||
BOOST_LOG(error) << "Couldn't acquire next frame [0x"sv << util::hex(status).to_string_view();
|
||||
// BOOST_LOG(error) << "Couldn't acquire next frame [0x"sv << util::hex(status).to_string_view();
|
||||
return capture_e::error;
|
||||
}
|
||||
}
|
||||
@ -81,14 +80,14 @@ namespace platf::dxgi {
|
||||
return capture_e::ok;
|
||||
|
||||
case DXGI_ERROR_INVALID_CALL:
|
||||
BOOST_LOG(warning) << "Duplication frame already released";
|
||||
// BOOST_LOG(warning) << "Duplication frame already released";
|
||||
return capture_e::ok;
|
||||
|
||||
case DXGI_ERROR_ACCESS_LOST:
|
||||
return capture_e::reinit;
|
||||
|
||||
default:
|
||||
BOOST_LOG(error) << "Error while releasing duplication frame [0x"sv << util::hex(status).to_string_view();
|
||||
// BOOST_LOG(error) << "Error while releasing duplication frame [0x"sv << util::hex(status).to_string_view();
|
||||
return capture_e::error;
|
||||
}
|
||||
}
|
||||
@ -100,15 +99,15 @@ namespace platf::dxgi {
|
||||
void
|
||||
display_base_t::high_precision_sleep(std::chrono::nanoseconds duration) {
|
||||
if (!timer) {
|
||||
BOOST_LOG(error) << "Attempting high_precision_sleep() with uninitialized timer";
|
||||
// BOOST_LOG(error) << "Attempting high_precision_sleep() with uninitialized timer";
|
||||
return;
|
||||
}
|
||||
if (duration < 0s) {
|
||||
BOOST_LOG(error) << "Attempting high_precision_sleep() with negative duration";
|
||||
// BOOST_LOG(error) << "Attempting high_precision_sleep() with negative duration";
|
||||
return;
|
||||
}
|
||||
if (duration > 5s) {
|
||||
BOOST_LOG(error) << "Attempting high_precision_sleep() with unexpectedly large duration (>5s)";
|
||||
// BOOST_LOG(error) << "Attempting high_precision_sleep() with unexpectedly large duration (>5s)";
|
||||
return;
|
||||
}
|
||||
|
||||
@ -133,7 +132,7 @@ namespace platf::dxgi {
|
||||
double candidate_rate = (double) candidate.Numerator / candidate.Denominator;
|
||||
// Can only decrease requested fps, otherwise client may start accumulating frames and suffer increased latency.
|
||||
if (client_frame_rate > candidate_rate && candidate_rate / client_frame_rate > 0.99) {
|
||||
BOOST_LOG(info) << "Adjusted capture rate to " << candidate_rate << "fps to better match display";
|
||||
// BOOST_LOG(info) << "Adjusted capture rate to " << candidate_rate << "fps to better match display";
|
||||
return candidate;
|
||||
}
|
||||
}
|
||||
@ -154,7 +153,7 @@ namespace platf::dxgi {
|
||||
SetThreadExecutionState(ES_CONTINUOUS);
|
||||
});
|
||||
|
||||
stat_trackers::min_max_avg_tracker<double> sleep_overshoot_tracker;
|
||||
// stat_trackers::min_max_avg_tracker<double> sleep_overshoot_tracker;
|
||||
|
||||
while (true) {
|
||||
// This will return false if the HDR state changes or for any number of other
|
||||
@ -185,15 +184,15 @@ namespace platf::dxgi {
|
||||
else {
|
||||
high_precision_sleep(sleep_period);
|
||||
|
||||
if (config::sunshine.min_log_level <= 1) {
|
||||
// Print sleep overshoot stats to debug log every 20 seconds
|
||||
auto print_info = [&](double min_overshoot, double max_overshoot, double avg_overshoot) {
|
||||
auto f = stat_trackers::one_digit_after_decimal();
|
||||
BOOST_LOG(debug) << "Sleep overshoot (min/max/avg): " << f % min_overshoot << "ms/" << f % max_overshoot << "ms/" << f % avg_overshoot << "ms";
|
||||
};
|
||||
std::chrono::nanoseconds overshoot_ns = std::chrono::steady_clock::now() - sleep_target;
|
||||
sleep_overshoot_tracker.collect_and_callback_on_interval(overshoot_ns.count() / 1000000., print_info, 20s);
|
||||
}
|
||||
// if (config::sunshine.min_log_level <= 1) {
|
||||
// // Print sleep overshoot stats to debug log every 20 seconds
|
||||
// auto print_info = [&](double min_overshoot, double max_overshoot, double avg_overshoot) {
|
||||
// auto f = stat_trackers::one_digit_after_decimal();
|
||||
// // BOOST_LOG(debug) << "Sleep overshoot (min/max/avg): " << f % min_overshoot << "ms/" << f % max_overshoot << "ms/" << f % avg_overshoot << "ms";
|
||||
// };
|
||||
// std::chrono::nanoseconds overshoot_ns = std::chrono::steady_clock::now() - sleep_target;
|
||||
// sleep_overshoot_tracker.collect_and_callback_on_interval(overshoot_ns.count() / 1000000., print_info, 20s);
|
||||
// }
|
||||
|
||||
status = snapshot(pull_free_image_cb, img_out, 0ms, *cursor);
|
||||
|
||||
@ -215,7 +214,7 @@ namespace platf::dxgi {
|
||||
frame_pacing_group_start = img_out->frame_timestamp;
|
||||
|
||||
if (!frame_pacing_group_start) {
|
||||
BOOST_LOG(warning) << "snapshot() provided image without timestamp";
|
||||
// BOOST_LOG(warning) << "snapshot() provided image without timestamp";
|
||||
frame_pacing_group_start = std::chrono::steady_clock::now();
|
||||
}
|
||||
|
||||
@ -239,7 +238,7 @@ namespace platf::dxgi {
|
||||
}
|
||||
break;
|
||||
default:
|
||||
BOOST_LOG(error) << "Unrecognized capture status ["sv << (int) status << ']';
|
||||
// BOOST_LOG(error) << "Unrecognized capture status ["sv << (int) status << ']';
|
||||
return status;
|
||||
}
|
||||
|
||||
@ -268,11 +267,11 @@ namespace platf::dxgi {
|
||||
value_data,
|
||||
(wcslen(value_data) + 1) * sizeof(WCHAR));
|
||||
if (status != ERROR_SUCCESS) {
|
||||
BOOST_LOG(error) << "Failed to set GPU preference: "sv << status;
|
||||
// BOOST_LOG(error) << "Failed to set GPU preference: "sv << status;
|
||||
return false;
|
||||
}
|
||||
|
||||
BOOST_LOG(info) << "Set GPU preference: "sv << preference;
|
||||
// BOOST_LOG(info) << "Set GPU preference: "sv << preference;
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -309,11 +308,11 @@ namespace platf::dxgi {
|
||||
result = bp::system(cmd, std::to_string(i), display_name, bp::std_out > bp::null, bp::std_err > bp::null);
|
||||
}
|
||||
catch (bp::process_error &e) {
|
||||
BOOST_LOG(error) << "Failed to start ddprobe.exe: "sv << e.what();
|
||||
// BOOST_LOG(error) << "Failed to start ddprobe.exe: "sv << e.what();
|
||||
return false;
|
||||
}
|
||||
|
||||
BOOST_LOG(info) << "ddprobe.exe ["sv << i << "] ["sv << display_name << "] returned: 0x"sv << util::hex(result).to_string_view();
|
||||
// BOOST_LOG(info) << "ddprobe.exe ["sv << i << "] ["sv << display_name << "] returned: 0x"sv << util::hex(result).to_string_view();
|
||||
|
||||
// E_ACCESSDENIED can happen at the login screen. If we get this error,
|
||||
// we know capture would have been supported, because DXGI_ERROR_UNSUPPORTED
|
||||
@ -362,14 +361,14 @@ namespace platf::dxgi {
|
||||
nullptr,
|
||||
nullptr);
|
||||
if (FAILED(status)) {
|
||||
BOOST_LOG(error) << "Failed to create D3D11 device for DD test [0x"sv << util::hex(status).to_string_view() << ']';
|
||||
// BOOST_LOG(error) << "Failed to create D3D11 device for DD test [0x"sv << util::hex(status).to_string_view() << ']';
|
||||
return false;
|
||||
}
|
||||
|
||||
output1_t output1;
|
||||
status = output->QueryInterface(IID_IDXGIOutput1, (void **) &output1);
|
||||
if (FAILED(status)) {
|
||||
BOOST_LOG(error) << "Failed to query IDXGIOutput1 from the output"sv;
|
||||
// BOOST_LOG(error) << "Failed to query IDXGIOutput1 from the output"sv;
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -383,7 +382,7 @@ namespace platf::dxgi {
|
||||
Sleep(200);
|
||||
}
|
||||
|
||||
BOOST_LOG(error) << "DuplicateOutput() test failed [0x"sv << util::hex(status).to_string_view() << ']';
|
||||
// BOOST_LOG(error) << "DuplicateOutput() test failed [0x"sv << util::hex(status).to_string_view() << ']';
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -416,12 +415,12 @@ namespace platf::dxgi {
|
||||
|
||||
// We must set the GPU preference before calling any DXGI APIs!
|
||||
if (!probe_for_gpu_preference(display_name)) {
|
||||
BOOST_LOG(warning) << "Failed to set GPU preference. Capture may not work!"sv;
|
||||
// BOOST_LOG(warning) << "Failed to set GPU preference. Capture may not work!"sv;
|
||||
}
|
||||
|
||||
status = CreateDXGIFactory1(IID_IDXGIFactory1, (void **) &factory);
|
||||
if (FAILED(status)) {
|
||||
BOOST_LOG(error) << "Failed to create DXGIFactory1 [0x"sv << util::hex(status).to_string_view() << ']';
|
||||
// BOOST_LOG(error) << "Failed to create DXGIFactory1 [0x"sv << util::hex(status).to_string_view() << ']';
|
||||
return -1;
|
||||
}
|
||||
|
||||
@ -497,7 +496,7 @@ namespace platf::dxgi {
|
||||
}
|
||||
|
||||
if (!output) {
|
||||
BOOST_LOG(error) << "Failed to locate an output device"sv;
|
||||
// BOOST_LOG(error) << "Failed to locate an output device"sv;
|
||||
return -1;
|
||||
}
|
||||
|
||||
@ -513,7 +512,7 @@ namespace platf::dxgi {
|
||||
|
||||
status = adapter->QueryInterface(IID_IDXGIAdapter, (void **) &adapter_p);
|
||||
if (FAILED(status)) {
|
||||
BOOST_LOG(error) << "Failed to query IDXGIAdapter interface"sv;
|
||||
// BOOST_LOG(error) << "Failed to query IDXGIAdapter interface"sv;
|
||||
return -1;
|
||||
}
|
||||
|
||||
@ -531,7 +530,7 @@ namespace platf::dxgi {
|
||||
adapter_p->Release();
|
||||
|
||||
if (FAILED(status)) {
|
||||
BOOST_LOG(error) << "Failed to create D3D11 device [0x"sv << util::hex(status).to_string_view() << ']';
|
||||
// BOOST_LOG(error) << "Failed to create D3D11 device [0x"sv << util::hex(status).to_string_view() << ']';
|
||||
|
||||
return -1;
|
||||
}
|
||||
@ -540,18 +539,18 @@ namespace platf::dxgi {
|
||||
adapter->GetDesc(&adapter_desc);
|
||||
|
||||
auto description = converter.to_bytes(adapter_desc.Description);
|
||||
BOOST_LOG(info)
|
||||
<< std::endl
|
||||
<< "Device Description : " << description << std::endl
|
||||
<< "Device Vendor ID : 0x"sv << util::hex(adapter_desc.VendorId).to_string_view() << std::endl
|
||||
<< "Device Device ID : 0x"sv << util::hex(adapter_desc.DeviceId).to_string_view() << std::endl
|
||||
<< "Device Video Mem : "sv << adapter_desc.DedicatedVideoMemory / 1048576 << " MiB"sv << std::endl
|
||||
<< "Device Sys Mem : "sv << adapter_desc.DedicatedSystemMemory / 1048576 << " MiB"sv << std::endl
|
||||
<< "Share Sys Mem : "sv << adapter_desc.SharedSystemMemory / 1048576 << " MiB"sv << std::endl
|
||||
<< "Feature Level : 0x"sv << util::hex(feature_level).to_string_view() << std::endl
|
||||
<< "Capture size : "sv << width << 'x' << height << std::endl
|
||||
<< "Offset : "sv << offset_x << 'x' << offset_y << std::endl
|
||||
<< "Virtual Desktop : "sv << env_width << 'x' << env_height;
|
||||
// BOOST_LOG(info)
|
||||
// << std::endl
|
||||
// << "Device Description : " << description << std::endl
|
||||
// << "Device Vendor ID : 0x"sv << util::hex(adapter_desc.VendorId).to_string_view() << std::endl
|
||||
// << "Device Device ID : 0x"sv << util::hex(adapter_desc.DeviceId).to_string_view() << std::endl
|
||||
// << "Device Video Mem : "sv << adapter_desc.DedicatedVideoMemory / 1048576 << " MiB"sv << std::endl
|
||||
// << "Device Sys Mem : "sv << adapter_desc.DedicatedSystemMemory / 1048576 << " MiB"sv << std::endl
|
||||
// << "Share Sys Mem : "sv << adapter_desc.SharedSystemMemory / 1048576 << " MiB"sv << std::endl
|
||||
// << "Feature Level : 0x"sv << util::hex(feature_level).to_string_view() << std::endl
|
||||
// << "Capture size : "sv << width << 'x' << height << std::endl
|
||||
// << "Offset : "sv << offset_x << 'x' << offset_y << std::endl
|
||||
// << "Virtual Desktop : "sv << env_width << 'x' << env_height;
|
||||
|
||||
// Bump up thread priority
|
||||
{
|
||||
@ -567,7 +566,7 @@ namespace platf::dxgi {
|
||||
tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
|
||||
|
||||
if (!AdjustTokenPrivileges(token, false, &tp, sizeof(tp), NULL, NULL)) {
|
||||
BOOST_LOG(warning) << "Could not set privilege to increase GPU priority";
|
||||
// BOOST_LOG(warning) << "Could not set privilege to increase GPU priority";
|
||||
}
|
||||
}
|
||||
|
||||
@ -580,13 +579,13 @@ namespace platf::dxgi {
|
||||
auto d3dkmt_query_adapter_info = (PD3DKMTQueryAdapterInfo) GetProcAddress(gdi32, "D3DKMTQueryAdapterInfo");
|
||||
auto d3dkmt_close_adapter = (PD3DKMTCloseAdapter) GetProcAddress(gdi32, "D3DKMTCloseAdapter");
|
||||
if (!d3dkmt_open_adapter || !d3dkmt_query_adapter_info || !d3dkmt_close_adapter) {
|
||||
BOOST_LOG(error) << "Couldn't load d3dkmt functions from gdi32.dll to determine GPU HAGS status";
|
||||
// BOOST_LOG(error) << "Couldn't load d3dkmt functions from gdi32.dll to determine GPU HAGS status";
|
||||
return false;
|
||||
}
|
||||
|
||||
D3DKMT_OPENADAPTERFROMLUID d3dkmt_adapter = { adapter };
|
||||
if (FAILED(d3dkmt_open_adapter(&d3dkmt_adapter))) {
|
||||
BOOST_LOG(error) << "D3DKMTOpenAdapterFromLuid() failed while trying to determine GPU HAGS status";
|
||||
// BOOST_LOG(error) << "D3DKMTOpenAdapterFromLuid() failed while trying to determine GPU HAGS status";
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -603,13 +602,13 @@ namespace platf::dxgi {
|
||||
result = d3dkmt_adapter_caps.HwSchEnabled;
|
||||
}
|
||||
else {
|
||||
BOOST_LOG(warning) << "D3DKMTQueryAdapterInfo() failed while trying to determine GPU HAGS status";
|
||||
// BOOST_LOG(warning) << "D3DKMTQueryAdapterInfo() failed while trying to determine GPU HAGS status";
|
||||
result = false;
|
||||
}
|
||||
|
||||
D3DKMT_CLOSEADAPTER d3dkmt_close_adapter_wrap = { d3dkmt_adapter.hAdapter };
|
||||
if (FAILED(d3dkmt_close_adapter(&d3dkmt_close_adapter_wrap))) {
|
||||
BOOST_LOG(error) << "D3DKMTCloseAdapter() failed while trying to determine GPU HAGS status";
|
||||
// BOOST_LOG(error) << "D3DKMTCloseAdapter() failed while trying to determine GPU HAGS status";
|
||||
}
|
||||
|
||||
return result;
|
||||
@ -625,27 +624,27 @@ namespace platf::dxgi {
|
||||
// Track OBS to see if they find better workaround or NVIDIA fixes it on their end, they seem to be in communication
|
||||
if (hags_enabled && !config::video.nv_realtime_hags) priority = D3DKMT_SCHEDULINGPRIORITYCLASS_HIGH;
|
||||
}
|
||||
BOOST_LOG(info) << "Active GPU has HAGS " << (hags_enabled ? "enabled" : "disabled");
|
||||
BOOST_LOG(info) << "Using " << (priority == D3DKMT_SCHEDULINGPRIORITYCLASS_HIGH ? "high" : "realtime") << " GPU priority";
|
||||
// BOOST_LOG(info) << "Active GPU has HAGS " << (hags_enabled ? "enabled" : "disabled");
|
||||
// BOOST_LOG(info) << "Using " << (priority == D3DKMT_SCHEDULINGPRIORITYCLASS_HIGH ? "high" : "realtime") << " GPU priority";
|
||||
if (FAILED(d3dkmt_set_process_priority(GetCurrentProcess(), priority))) {
|
||||
BOOST_LOG(warning) << "Failed to adjust GPU priority. Please run application as administrator for optimal performance.";
|
||||
// BOOST_LOG(warning) << "Failed to adjust GPU priority. Please run application as administrator for optimal performance.";
|
||||
}
|
||||
}
|
||||
else {
|
||||
BOOST_LOG(error) << "Couldn't load D3DKMTSetProcessSchedulingPriorityClass function from gdi32.dll to adjust GPU priority";
|
||||
// BOOST_LOG(error) << "Couldn't load D3DKMTSetProcessSchedulingPriorityClass function from gdi32.dll to adjust GPU priority";
|
||||
}
|
||||
}
|
||||
|
||||
dxgi::dxgi_t dxgi;
|
||||
status = device->QueryInterface(IID_IDXGIDevice, (void **) &dxgi);
|
||||
if (FAILED(status)) {
|
||||
BOOST_LOG(warning) << "Failed to query DXGI interface from device [0x"sv << util::hex(status).to_string_view() << ']';
|
||||
// BOOST_LOG(warning) << "Failed to query DXGI interface from device [0x"sv << util::hex(status).to_string_view() << ']';
|
||||
return -1;
|
||||
}
|
||||
|
||||
status = dxgi->SetGPUThreadPriority(7);
|
||||
if (FAILED(status)) {
|
||||
BOOST_LOG(warning) << "Failed to increase capture GPU thread priority. Please run application as administrator for optimal performance.";
|
||||
// BOOST_LOG(warning) << "Failed to increase capture GPU thread priority. Please run application as administrator for optimal performance.";
|
||||
}
|
||||
}
|
||||
|
||||
@ -654,13 +653,13 @@ namespace platf::dxgi {
|
||||
dxgi::dxgi1_t dxgi {};
|
||||
status = device->QueryInterface(IID_IDXGIDevice, (void **) &dxgi);
|
||||
if (FAILED(status)) {
|
||||
BOOST_LOG(error) << "Failed to query DXGI interface from device [0x"sv << util::hex(status).to_string_view() << ']';
|
||||
// BOOST_LOG(error) << "Failed to query DXGI interface from device [0x"sv << util::hex(status).to_string_view() << ']';
|
||||
return -1;
|
||||
}
|
||||
|
||||
status = dxgi->SetMaximumFrameLatency(1);
|
||||
if (FAILED(status)) {
|
||||
BOOST_LOG(warning) << "Failed to set maximum frame latency [0x"sv << util::hex(status).to_string_view() << ']';
|
||||
// BOOST_LOG(warning) << "Failed to set maximum frame latency [0x"sv << util::hex(status).to_string_view() << ']';
|
||||
}
|
||||
}
|
||||
|
||||
@ -673,7 +672,7 @@ namespace platf::dxgi {
|
||||
// Ask the display implementation which formats it supports
|
||||
auto supported_formats = get_supported_capture_formats();
|
||||
if (supported_formats.empty()) {
|
||||
BOOST_LOG(warning) << "No compatible capture formats for this encoder"sv;
|
||||
// BOOST_LOG(warning) << "No compatible capture formats for this encoder"sv;
|
||||
return -1;
|
||||
}
|
||||
|
||||
@ -690,17 +689,17 @@ namespace platf::dxgi {
|
||||
// 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() << ']';
|
||||
// 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;
|
||||
// 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;
|
||||
// BOOST_LOG(error) << "Failed to query IDXGIOutput1 from the output"sv;
|
||||
return -1;
|
||||
}
|
||||
|
||||
@ -713,7 +712,7 @@ namespace platf::dxgi {
|
||||
}
|
||||
|
||||
if (FAILED(status)) {
|
||||
BOOST_LOG(error) << "DuplicateOutput Failed [0x"sv << util::hex(status).to_string_view() << ']';
|
||||
// BOOST_LOG(error) << "DuplicateOutput Failed [0x"sv << util::hex(status).to_string_view() << ']';
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
@ -722,16 +721,16 @@ namespace platf::dxgi {
|
||||
DXGI_OUTDUPL_DESC dup_desc;
|
||||
dup.dup->GetDesc(&dup_desc);
|
||||
|
||||
BOOST_LOG(info) << "Desktop resolution ["sv << dup_desc.ModeDesc.Width << 'x' << dup_desc.ModeDesc.Height << ']';
|
||||
BOOST_LOG(info) << "Desktop format ["sv << dxgi_format_to_string(dup_desc.ModeDesc.Format) << ']';
|
||||
// BOOST_LOG(info) << "Desktop resolution ["sv << dup_desc.ModeDesc.Width << 'x' << dup_desc.ModeDesc.Height << ']';
|
||||
// BOOST_LOG(info) << "Desktop format ["sv << dxgi_format_to_string(dup_desc.ModeDesc.Format) << ']';
|
||||
|
||||
display_refresh_rate = dup_desc.ModeDesc.RefreshRate;
|
||||
double display_refresh_rate_decimal = (double) display_refresh_rate.Numerator / display_refresh_rate.Denominator;
|
||||
BOOST_LOG(info) << "Display refresh rate [" << display_refresh_rate_decimal << "Hz]";
|
||||
// BOOST_LOG(info) << "Display refresh rate [" << display_refresh_rate_decimal << "Hz]";
|
||||
display_refresh_rate_rounded = lround(display_refresh_rate_decimal);
|
||||
|
||||
client_frame_rate = config.framerate;
|
||||
BOOST_LOG(info) << "Requested frame rate [" << client_frame_rate << "fps]";
|
||||
// BOOST_LOG(info) << "Requested frame rate [" << client_frame_rate << "fps]";
|
||||
|
||||
dxgi::output6_t output6 {};
|
||||
status = output->QueryInterface(IID_IDXGIOutput6, (void **) &output6);
|
||||
@ -739,17 +738,17 @@ namespace platf::dxgi {
|
||||
DXGI_OUTPUT_DESC1 desc1;
|
||||
output6->GetDesc1(&desc1);
|
||||
|
||||
BOOST_LOG(info)
|
||||
<< std::endl
|
||||
<< "Colorspace : "sv << colorspace_to_string(desc1.ColorSpace) << std::endl
|
||||
<< "Bits Per Color : "sv << desc1.BitsPerColor << std::endl
|
||||
<< "Red Primary : ["sv << desc1.RedPrimary[0] << ',' << desc1.RedPrimary[1] << ']' << std::endl
|
||||
<< "Green Primary : ["sv << desc1.GreenPrimary[0] << ',' << desc1.GreenPrimary[1] << ']' << std::endl
|
||||
<< "Blue Primary : ["sv << desc1.BluePrimary[0] << ',' << desc1.BluePrimary[1] << ']' << std::endl
|
||||
<< "White Point : ["sv << desc1.WhitePoint[0] << ',' << desc1.WhitePoint[1] << ']' << std::endl
|
||||
<< "Min Luminance : "sv << desc1.MinLuminance << " nits"sv << std::endl
|
||||
<< "Max Luminance : "sv << desc1.MaxLuminance << " nits"sv << std::endl
|
||||
<< "Max Full Luminance : "sv << desc1.MaxFullFrameLuminance << " nits"sv;
|
||||
// BOOST_LOG(info)
|
||||
// << std::endl
|
||||
// << "Colorspace : "sv << colorspace_to_string(desc1.ColorSpace) << std::endl
|
||||
// << "Bits Per Color : "sv << desc1.BitsPerColor << std::endl
|
||||
// << "Red Primary : ["sv << desc1.RedPrimary[0] << ',' << desc1.RedPrimary[1] << ']' << std::endl
|
||||
// << "Green Primary : ["sv << desc1.GreenPrimary[0] << ',' << desc1.GreenPrimary[1] << ']' << std::endl
|
||||
// << "Blue Primary : ["sv << desc1.BluePrimary[0] << ',' << desc1.BluePrimary[1] << ']' << std::endl
|
||||
// << "White Point : ["sv << desc1.WhitePoint[0] << ',' << desc1.WhitePoint[1] << ']' << std::endl
|
||||
// << "Min Luminance : "sv << desc1.MinLuminance << " nits"sv << std::endl
|
||||
// << "Max Luminance : "sv << desc1.MaxLuminance << " nits"sv << std::endl
|
||||
// << "Max Full Luminance : "sv << desc1.MaxFullFrameLuminance << " nits"sv;
|
||||
}
|
||||
|
||||
// Capture format will be determined from the first call to AcquireNextFrame()
|
||||
@ -761,7 +760,7 @@ namespace platf::dxgi {
|
||||
timer.reset(CreateWaitableTimerEx(nullptr, nullptr, 0, TIMER_ALL_ACCESS));
|
||||
if (!timer) {
|
||||
auto winerr = GetLastError();
|
||||
BOOST_LOG(error) << "Failed to create timer: "sv << winerr;
|
||||
// BOOST_LOG(error) << "Failed to create timer: "sv << winerr;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
@ -775,7 +774,7 @@ namespace platf::dxgi {
|
||||
|
||||
auto status = output->QueryInterface(IID_IDXGIOutput6, (void **) &output6);
|
||||
if (FAILED(status)) {
|
||||
BOOST_LOG(warning) << "Failed to query IDXGIOutput6 from the output"sv;
|
||||
// BOOST_LOG(warning) << "Failed to query IDXGIOutput6 from the output"sv;
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -793,7 +792,7 @@ namespace platf::dxgi {
|
||||
|
||||
auto status = output->QueryInterface(IID_IDXGIOutput6, (void **) &output6);
|
||||
if (FAILED(status)) {
|
||||
BOOST_LOG(warning) << "Failed to query IDXGIOutput6 from the output"sv;
|
||||
// BOOST_LOG(warning) << "Failed to query IDXGIOutput6 from the output"sv;
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -1036,19 +1035,19 @@ namespace platf {
|
||||
|
||||
HRESULT status;
|
||||
|
||||
BOOST_LOG(debug) << "Detecting monitors..."sv;
|
||||
// BOOST_LOG(debug) << "Detecting monitors..."sv;
|
||||
|
||||
std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>, wchar_t> converter;
|
||||
|
||||
// We must set the GPU preference before calling any DXGI APIs!
|
||||
if (!dxgi::probe_for_gpu_preference(config::video.output_name)) {
|
||||
BOOST_LOG(warning) << "Failed to set GPU preference. Capture may not work!"sv;
|
||||
// BOOST_LOG(warning) << "Failed to set GPU preference. Capture may not work!"sv;
|
||||
}
|
||||
|
||||
dxgi::factory1_t factory;
|
||||
status = CreateDXGIFactory1(IID_IDXGIFactory1, (void **) &factory);
|
||||
if (FAILED(status)) {
|
||||
BOOST_LOG(error) << "Failed to create DXGIFactory1 [0x"sv << util::hex(status).to_string_view() << ']';
|
||||
// BOOST_LOG(error) << "Failed to create DXGIFactory1 [0x"sv << util::hex(status).to_string_view() << ']';
|
||||
return {};
|
||||
}
|
||||
|
||||
@ -1057,17 +1056,17 @@ namespace platf {
|
||||
DXGI_ADAPTER_DESC1 adapter_desc;
|
||||
adapter->GetDesc1(&adapter_desc);
|
||||
|
||||
BOOST_LOG(debug)
|
||||
<< std::endl
|
||||
<< "====== ADAPTER ====="sv << std::endl
|
||||
<< "Device Name : "sv << converter.to_bytes(adapter_desc.Description) << std::endl
|
||||
<< "Device Vendor ID : 0x"sv << util::hex(adapter_desc.VendorId).to_string_view() << std::endl
|
||||
<< "Device Device ID : 0x"sv << util::hex(adapter_desc.DeviceId).to_string_view() << std::endl
|
||||
<< "Device Video Mem : "sv << adapter_desc.DedicatedVideoMemory / 1048576 << " MiB"sv << std::endl
|
||||
<< "Device Sys Mem : "sv << adapter_desc.DedicatedSystemMemory / 1048576 << " MiB"sv << std::endl
|
||||
<< "Share Sys Mem : "sv << adapter_desc.SharedSystemMemory / 1048576 << " MiB"sv << std::endl
|
||||
<< std::endl
|
||||
<< " ====== OUTPUT ======"sv << std::endl;
|
||||
// BOOST_LOG(debug)
|
||||
// << std::endl
|
||||
// << "====== ADAPTER ====="sv << std::endl
|
||||
// << "Device Name : "sv << converter.to_bytes(adapter_desc.Description) << std::endl
|
||||
// << "Device Vendor ID : 0x"sv << util::hex(adapter_desc.VendorId).to_string_view() << std::endl
|
||||
// << "Device Device ID : 0x"sv << util::hex(adapter_desc.DeviceId).to_string_view() << std::endl
|
||||
// << "Device Video Mem : "sv << adapter_desc.DedicatedVideoMemory / 1048576 << " MiB"sv << std::endl
|
||||
// << "Device Sys Mem : "sv << adapter_desc.DedicatedSystemMemory / 1048576 << " MiB"sv << std::endl
|
||||
// << "Share Sys Mem : "sv << adapter_desc.SharedSystemMemory / 1048576 << " MiB"sv << std::endl
|
||||
// << std::endl
|
||||
// << " ====== OUTPUT ======"sv << std::endl;
|
||||
|
||||
dxgi::output_t::pointer output_p {};
|
||||
for (int y = 0; adapter->EnumOutputs(y, &output_p) != DXGI_ERROR_NOT_FOUND; ++y) {
|
||||
@ -1081,11 +1080,11 @@ namespace platf {
|
||||
auto width = desc.DesktopCoordinates.right - desc.DesktopCoordinates.left;
|
||||
auto height = desc.DesktopCoordinates.bottom - desc.DesktopCoordinates.top;
|
||||
|
||||
BOOST_LOG(debug)
|
||||
<< " Output Name : "sv << device_name << std::endl
|
||||
<< " AttachedToDesktop : "sv << (desc.AttachedToDesktop ? "yes"sv : "no"sv) << std::endl
|
||||
<< " Resolution : "sv << width << 'x' << height << std::endl
|
||||
<< std::endl;
|
||||
// BOOST_LOG(debug)
|
||||
// << " Output Name : "sv << device_name << std::endl
|
||||
// << " AttachedToDesktop : "sv << (desc.AttachedToDesktop ? "yes"sv : "no"sv) << std::endl
|
||||
// << " Resolution : "sv << width << 'x' << height << std::endl
|
||||
// << std::endl;
|
||||
|
||||
// Don't include the display in the list if we can't actually capture it
|
||||
if (desc.AttachedToDesktop && dxgi::test_dxgi_duplication(adapter, output)) {
|
||||
|
||||
@ -172,7 +172,8 @@ namespace platf::dxgi {
|
||||
blend_cursor_color(cursor, img, true);
|
||||
break;
|
||||
default:
|
||||
BOOST_LOG(warning) << "Unsupported cursor format ["sv << cursor.shape_info.Type << ']';
|
||||
// BOOST_LOG(warning) << "Unsupported cursor format ["sv << cursor.shape_info.Type << ']';
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@ -212,7 +213,7 @@ namespace platf::dxgi {
|
||||
UINT dummy;
|
||||
status = dup.dup->GetFramePointerShape(img_data.size(), img_data.data(), &dummy, &cursor.shape_info);
|
||||
if (FAILED(status)) {
|
||||
BOOST_LOG(error) << "Failed to get new pointer shape [0x"sv << util::hex(status).to_string_view() << ']';
|
||||
// BOOST_LOG(error) << "Failed to get new pointer shape [0x"sv << util::hex(status).to_string_view() << ']';
|
||||
|
||||
return capture_e::error;
|
||||
}
|
||||
@ -230,7 +231,7 @@ namespace platf::dxgi {
|
||||
status = res->QueryInterface(IID_ID3D11Texture2D, (void **) &src);
|
||||
|
||||
if (FAILED(status)) {
|
||||
BOOST_LOG(error) << "Couldn't query interface [0x"sv << util::hex(status).to_string_view() << ']';
|
||||
// BOOST_LOG(error) << "Couldn't query interface [0x"sv << util::hex(status).to_string_view() << ']';
|
||||
return capture_e::error;
|
||||
}
|
||||
|
||||
@ -240,7 +241,7 @@ namespace platf::dxgi {
|
||||
// If we don't know the capture format yet, grab it from this texture and create the staging texture
|
||||
if (capture_format == DXGI_FORMAT_UNKNOWN) {
|
||||
capture_format = desc.Format;
|
||||
BOOST_LOG(info) << "Capture format ["sv << dxgi_format_to_string(capture_format) << ']';
|
||||
// BOOST_LOG(info) << "Capture format ["sv << dxgi_format_to_string(capture_format) << ']';
|
||||
|
||||
D3D11_TEXTURE2D_DESC t {};
|
||||
t.Width = width;
|
||||
@ -255,7 +256,7 @@ namespace platf::dxgi {
|
||||
auto status = device->CreateTexture2D(&t, nullptr, &texture);
|
||||
|
||||
if (FAILED(status)) {
|
||||
BOOST_LOG(error) << "Failed to create staging texture [0x"sv << util::hex(status).to_string_view() << ']';
|
||||
// BOOST_LOG(error) << "Failed to create staging texture [0x"sv << util::hex(status).to_string_view() << ']';
|
||||
return capture_e::error;
|
||||
}
|
||||
}
|
||||
@ -263,14 +264,14 @@ namespace platf::dxgi {
|
||||
// It's possible for our display enumeration to race with mode changes and result in
|
||||
// mismatched image pool and desktop texture sizes. If this happens, just reinit again.
|
||||
if (desc.Width != width || desc.Height != height) {
|
||||
BOOST_LOG(info) << "Capture size changed ["sv << width << 'x' << height << " -> "sv << desc.Width << 'x' << desc.Height << ']';
|
||||
// BOOST_LOG(info) << "Capture size changed ["sv << width << 'x' << height << " -> "sv << desc.Width << 'x' << desc.Height << ']';
|
||||
return capture_e::reinit;
|
||||
}
|
||||
|
||||
// It's also possible for the capture format to change on the fly. If that happens,
|
||||
// reinitialize capture to try format detection again and create new images.
|
||||
if (capture_format != desc.Format) {
|
||||
BOOST_LOG(info) << "Capture format changed ["sv << dxgi_format_to_string(capture_format) << " -> "sv << dxgi_format_to_string(desc.Format) << ']';
|
||||
// BOOST_LOG(info) << "Capture format changed ["sv << dxgi_format_to_string(capture_format) << " -> "sv << dxgi_format_to_string(desc.Format) << ']';
|
||||
return capture_e::reinit;
|
||||
}
|
||||
|
||||
@ -286,7 +287,7 @@ namespace platf::dxgi {
|
||||
|
||||
// If we don't know the final capture format yet, encode a dummy image
|
||||
if (capture_format == DXGI_FORMAT_UNKNOWN) {
|
||||
BOOST_LOG(debug) << "Capture format is still unknown. Encoding a blank image"sv;
|
||||
// BOOST_LOG(debug) << "Capture format is still unknown. Encoding a blank image"sv;
|
||||
|
||||
if (dummy_img(img)) {
|
||||
return capture_e::error;
|
||||
@ -296,7 +297,7 @@ namespace platf::dxgi {
|
||||
// Map the staging texture for CPU access (making it inaccessible for the GPU)
|
||||
status = device_ctx->Map(texture.get(), 0, D3D11_MAP_READ, 0, &img_info);
|
||||
if (FAILED(status)) {
|
||||
BOOST_LOG(error) << "Failed to map texture [0x"sv << util::hex(status).to_string_view() << ']';
|
||||
// BOOST_LOG(error) << "Failed to map texture [0x"sv << util::hex(status).to_string_view() << ']';
|
||||
|
||||
return capture_e::error;
|
||||
}
|
||||
@ -341,7 +342,7 @@ namespace platf::dxgi {
|
||||
display_ram_t::complete_img(platf::img_t *img, bool dummy) {
|
||||
// If this is not a dummy image, we must know the format by now
|
||||
if (!dummy && capture_format == DXGI_FORMAT_UNKNOWN) {
|
||||
BOOST_LOG(error) << "display_ram_t::complete_img() called with unknown capture format!";
|
||||
// BOOST_LOG(error) << "display_ram_t::complete_img() called with unknown capture format!";
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
@ -59,7 +59,7 @@ namespace platf::dxgi {
|
||||
buf_t::pointer buf_p;
|
||||
auto status = device->CreateBuffer(&buffer_desc, &init_data, &buf_p);
|
||||
if (status) {
|
||||
BOOST_LOG(error) << "Failed to create buffer: [0x"sv << util::hex(status).to_string_view() << ']';
|
||||
// BOOST_LOG(error) << "Failed to create buffer: [0x"sv << util::hex(status).to_string_view() << ']';
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
@ -95,7 +95,7 @@ namespace platf::dxgi {
|
||||
blend_t blend;
|
||||
auto status = device->CreateBlendState(&bdesc, &blend);
|
||||
if (status) {
|
||||
BOOST_LOG(error) << "Failed to create blend state: [0x"sv << util::hex(status).to_string_view() << ']';
|
||||
// BOOST_LOG(error) << "Failed to create blend state: [0x"sv << util::hex(status).to_string_view() << ']';
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
@ -180,7 +180,7 @@ namespace platf::dxgi {
|
||||
_locked = true;
|
||||
}
|
||||
else {
|
||||
BOOST_LOG(error) << "Failed to acquire texture mutex [0x"sv << util::hex(status).to_string_view() << ']';
|
||||
// BOOST_LOG(error) << "Failed to acquire texture mutex [0x"sv << util::hex(status).to_string_view() << ']';
|
||||
}
|
||||
return _locked;
|
||||
}
|
||||
@ -209,7 +209,7 @@ namespace platf::dxgi {
|
||||
}
|
||||
else {
|
||||
// Other alpha values are illegal in masked color cursors
|
||||
BOOST_LOG(warning) << "Illegal alpha value in masked color cursor: " << alpha;
|
||||
// BOOST_LOG(warning) << "Illegal alpha value in masked color cursor: " << alpha;
|
||||
}
|
||||
});
|
||||
return cursor_img;
|
||||
@ -218,7 +218,7 @@ namespace platf::dxgi {
|
||||
// Monochrome is handled below
|
||||
break;
|
||||
default:
|
||||
BOOST_LOG(error) << "Invalid cursor shape type: " << shape_info.Type;
|
||||
// BOOST_LOG(error) << "Invalid cursor shape type: " << shape_info.Type;
|
||||
return {};
|
||||
}
|
||||
|
||||
@ -279,7 +279,7 @@ namespace platf::dxgi {
|
||||
}
|
||||
else {
|
||||
// Other alpha values are illegal in masked color cursors
|
||||
BOOST_LOG(warning) << "Illegal alpha value in masked color cursor: " << alpha;
|
||||
// BOOST_LOG(warning) << "Illegal alpha value in masked color cursor: " << alpha;
|
||||
}
|
||||
});
|
||||
return cursor_img;
|
||||
@ -291,7 +291,7 @@ namespace platf::dxgi {
|
||||
// Monochrome cursors are handled below.
|
||||
break;
|
||||
default:
|
||||
BOOST_LOG(error) << "Invalid cursor shape type: " << shape_info.Type;
|
||||
// BOOST_LOG(error) << "Invalid cursor shape type: " << shape_info.Type;
|
||||
return {};
|
||||
}
|
||||
|
||||
@ -348,12 +348,12 @@ namespace platf::dxgi {
|
||||
auto status = D3DCompileFromFile(wFile.c_str(), nullptr, D3D_COMPILE_STANDARD_FILE_INCLUDE, entrypoint, shader_model, flags, 0, &compiled_p, &msg_p);
|
||||
|
||||
if (msg_p) {
|
||||
BOOST_LOG(warning) << std::string_view { (const char *) msg_p->GetBufferPointer(), msg_p->GetBufferSize() - 1 };
|
||||
// BOOST_LOG(warning) << std::string_view { (const char *) msg_p->GetBufferPointer(), msg_p->GetBufferSize() - 1 };
|
||||
msg_p->Release();
|
||||
}
|
||||
|
||||
if (status) {
|
||||
BOOST_LOG(error) << "Couldn't compile ["sv << file << "] [0x"sv << util::hex(status).to_string_view() << ']';
|
||||
// BOOST_LOG(error) << "Couldn't compile ["sv << file << "] [0x"sv << util::hex(status).to_string_view() << ']';
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
@ -395,7 +395,7 @@ namespace platf::dxgi {
|
||||
// Acquire encoder mutex to synchronize with capture code
|
||||
auto status = img_ctx.encoder_mutex->AcquireSync(0, INFINITE);
|
||||
if (status != S_OK) {
|
||||
BOOST_LOG(error) << "Failed to acquire encoder mutex [0x"sv << util::hex(status).to_string_view() << ']';
|
||||
// BOOST_LOG(error) << "Failed to acquire encoder mutex [0x"sv << util::hex(status).to_string_view() << ']';
|
||||
return -1;
|
||||
}
|
||||
|
||||
@ -426,13 +426,13 @@ namespace platf::dxgi {
|
||||
auto color_vectors = ::video::color_vectors_from_colorspace(colorspace);
|
||||
|
||||
if (!color_vectors) {
|
||||
BOOST_LOG(error) << "No vector data for colorspace"sv;
|
||||
// BOOST_LOG(error) << "No vector data for colorspace"sv;
|
||||
return;
|
||||
}
|
||||
|
||||
auto color_matrix = make_buffer(device.get(), *color_vectors);
|
||||
if (!color_matrix) {
|
||||
BOOST_LOG(warning) << "Failed to create color matrix"sv;
|
||||
// BOOST_LOG(warning) << "Failed to create color matrix"sv;
|
||||
return;
|
||||
}
|
||||
|
||||
@ -468,7 +468,7 @@ namespace platf::dxgi {
|
||||
subsample_offset = make_buffer(device.get(), subsample_offset_in);
|
||||
|
||||
if (!subsample_offset) {
|
||||
BOOST_LOG(error) << "Failed to create subsample offset vertex constant buffer";
|
||||
// BOOST_LOG(error) << "Failed to create subsample offset vertex constant buffer";
|
||||
return -1;
|
||||
}
|
||||
device_ctx->VSSetConstantBuffers(0, 1, &subsample_offset);
|
||||
@ -478,7 +478,7 @@ namespace platf::dxgi {
|
||||
int32_t rotation_data[16 / sizeof(int32_t)] { -rotation_modifier }; // aligned to 16-byte
|
||||
auto rotation = make_buffer(device.get(), rotation_data);
|
||||
if (!rotation) {
|
||||
BOOST_LOG(error) << "Failed to create display rotation vertex constant buffer";
|
||||
// BOOST_LOG(error) << "Failed to create display rotation vertex constant buffer";
|
||||
return -1;
|
||||
}
|
||||
device_ctx->VSSetConstantBuffers(1, 1, &rotation);
|
||||
@ -491,7 +491,7 @@ namespace platf::dxgi {
|
||||
|
||||
auto status = device->CreateRenderTargetView(output_texture.get(), &nv12_rt_desc, &nv12_Y_rt);
|
||||
if (FAILED(status)) {
|
||||
BOOST_LOG(error) << "Failed to create render target view [0x"sv << util::hex(status).to_string_view() << ']';
|
||||
// BOOST_LOG(error) << "Failed to create render target view [0x"sv << util::hex(status).to_string_view() << ']';
|
||||
return -1;
|
||||
}
|
||||
|
||||
@ -499,7 +499,7 @@ namespace platf::dxgi {
|
||||
|
||||
status = device->CreateRenderTargetView(output_texture.get(), &nv12_rt_desc, &nv12_UV_rt);
|
||||
if (FAILED(status)) {
|
||||
BOOST_LOG(error) << "Failed to create render target view [0x"sv << util::hex(status).to_string_view() << ']';
|
||||
// BOOST_LOG(error) << "Failed to create render target view [0x"sv << util::hex(status).to_string_view() << ']';
|
||||
return -1;
|
||||
}
|
||||
|
||||
@ -536,32 +536,32 @@ namespace platf::dxgi {
|
||||
&device_ctx);
|
||||
|
||||
if (FAILED(status)) {
|
||||
BOOST_LOG(error) << "Failed to create encoder D3D11 device [0x"sv << util::hex(status).to_string_view() << ']';
|
||||
// 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() << ']';
|
||||
// BOOST_LOG(warning) << "Failed to query DXGI interface from device [0x"sv << util::hex(status).to_string_view() << ']';
|
||||
return -1;
|
||||
}
|
||||
|
||||
status = dxgi->SetGPUThreadPriority(7);
|
||||
if (FAILED(status)) {
|
||||
BOOST_LOG(warning) << "Failed to increase encoding GPU thread priority. Please run application as administrator for optimal performance.";
|
||||
// BOOST_LOG(warning) << "Failed to increase encoding GPU thread priority. Please run application as administrator for optimal performance.";
|
||||
}
|
||||
|
||||
format = (pix_fmt == pix_fmt_e::nv12 ? DXGI_FORMAT_NV12 : DXGI_FORMAT_P010);
|
||||
status = device->CreateVertexShader(convert_yuv420_planar_y_vs_hlsl->GetBufferPointer(), convert_yuv420_planar_y_vs_hlsl->GetBufferSize(), nullptr, &scene_vs);
|
||||
if (status) {
|
||||
BOOST_LOG(error) << "Failed to create scene vertex shader [0x"sv << util::hex(status).to_string_view() << ']';
|
||||
// BOOST_LOG(error) << "Failed to create scene vertex shader [0x"sv << util::hex(status).to_string_view() << ']';
|
||||
return -1;
|
||||
}
|
||||
|
||||
status = device->CreateVertexShader(convert_yuv420_packed_uv_type0_vs_hlsl->GetBufferPointer(), convert_yuv420_packed_uv_type0_vs_hlsl->GetBufferSize(), nullptr, &convert_UV_vs);
|
||||
if (status) {
|
||||
BOOST_LOG(error) << "Failed to create convertUV vertex shader [0x"sv << util::hex(status).to_string_view() << ']';
|
||||
// BOOST_LOG(error) << "Failed to create convertUV vertex shader [0x"sv << util::hex(status).to_string_view() << ']';
|
||||
return -1;
|
||||
}
|
||||
|
||||
@ -569,13 +569,13 @@ namespace platf::dxgi {
|
||||
if (format == DXGI_FORMAT_P010 && display->is_hdr()) {
|
||||
status = device->CreatePixelShader(convert_yuv420_planar_y_ps_perceptual_quantizer_hlsl->GetBufferPointer(), convert_yuv420_planar_y_ps_perceptual_quantizer_hlsl->GetBufferSize(), nullptr, &convert_Y_fp16_ps);
|
||||
if (status) {
|
||||
BOOST_LOG(error) << "Failed to create convertY pixel shader [0x"sv << util::hex(status).to_string_view() << ']';
|
||||
// BOOST_LOG(error) << "Failed to create convertY pixel shader [0x"sv << util::hex(status).to_string_view() << ']';
|
||||
return -1;
|
||||
}
|
||||
|
||||
status = device->CreatePixelShader(convert_yuv420_packed_uv_type0_ps_perceptual_quantizer_hlsl->GetBufferPointer(), convert_yuv420_packed_uv_type0_ps_perceptual_quantizer_hlsl->GetBufferSize(), nullptr, &convert_UV_fp16_ps);
|
||||
if (status) {
|
||||
BOOST_LOG(error) << "Failed to create convertUV pixel shader [0x"sv << util::hex(status).to_string_view() << ']';
|
||||
// BOOST_LOG(error) << "Failed to create convertUV pixel shader [0x"sv << util::hex(status).to_string_view() << ']';
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
@ -584,13 +584,13 @@ namespace platf::dxgi {
|
||||
// scRGB uses linear gamma, so we must use our linear to sRGB conversion shaders.
|
||||
status = device->CreatePixelShader(convert_yuv420_planar_y_ps_linear_hlsl->GetBufferPointer(), convert_yuv420_planar_y_ps_linear_hlsl->GetBufferSize(), nullptr, &convert_Y_fp16_ps);
|
||||
if (status) {
|
||||
BOOST_LOG(error) << "Failed to create convertY pixel shader [0x"sv << util::hex(status).to_string_view() << ']';
|
||||
// BOOST_LOG(error) << "Failed to create convertY pixel shader [0x"sv << util::hex(status).to_string_view() << ']';
|
||||
return -1;
|
||||
}
|
||||
|
||||
status = device->CreatePixelShader(convert_yuv420_packed_uv_type0_ps_linear_hlsl->GetBufferPointer(), convert_yuv420_packed_uv_type0_ps_linear_hlsl->GetBufferSize(), nullptr, &convert_UV_fp16_ps);
|
||||
if (status) {
|
||||
BOOST_LOG(error) << "Failed to create convertUV pixel shader [0x"sv << util::hex(status).to_string_view() << ']';
|
||||
// BOOST_LOG(error) << "Failed to create convertUV pixel shader [0x"sv << util::hex(status).to_string_view() << ']';
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
@ -598,25 +598,25 @@ namespace platf::dxgi {
|
||||
// These shaders consume standard 8-bit sRGB input
|
||||
status = device->CreatePixelShader(convert_yuv420_planar_y_ps_hlsl->GetBufferPointer(), convert_yuv420_planar_y_ps_hlsl->GetBufferSize(), nullptr, &convert_Y_ps);
|
||||
if (status) {
|
||||
BOOST_LOG(error) << "Failed to create convertY pixel shader [0x"sv << util::hex(status).to_string_view() << ']';
|
||||
// BOOST_LOG(error) << "Failed to create convertY pixel shader [0x"sv << util::hex(status).to_string_view() << ']';
|
||||
return -1;
|
||||
}
|
||||
|
||||
status = device->CreatePixelShader(convert_yuv420_packed_uv_type0_ps_hlsl->GetBufferPointer(), convert_yuv420_packed_uv_type0_ps_hlsl->GetBufferSize(), nullptr, &convert_UV_ps);
|
||||
if (status) {
|
||||
BOOST_LOG(error) << "Failed to create convertUV pixel shader [0x"sv << util::hex(status).to_string_view() << ']';
|
||||
// BOOST_LOG(error) << "Failed to create convertUV pixel shader [0x"sv << util::hex(status).to_string_view() << ']';
|
||||
return -1;
|
||||
}
|
||||
|
||||
auto default_color_vectors = ::video::color_vectors_from_colorspace(::video::colorspace_e::rec601, false);
|
||||
if (!default_color_vectors) {
|
||||
BOOST_LOG(error) << "Missing color vectors for Rec. 601"sv;
|
||||
// BOOST_LOG(error) << "Missing color vectors for Rec. 601"sv;
|
||||
return -1;
|
||||
}
|
||||
|
||||
color_matrix = make_buffer(device.get(), *default_color_vectors);
|
||||
if (!color_matrix) {
|
||||
BOOST_LOG(error) << "Failed to create color matrix buffer"sv;
|
||||
// BOOST_LOG(error) << "Failed to create color matrix buffer"sv;
|
||||
return -1;
|
||||
}
|
||||
device_ctx->PSSetConstantBuffers(0, 1, &color_matrix);
|
||||
@ -643,7 +643,7 @@ namespace platf::dxgi {
|
||||
|
||||
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() << ']';
|
||||
// BOOST_LOG(error) << "Failed to create point sampler state [0x"sv << util::hex(status).to_string_view() << ']';
|
||||
return -1;
|
||||
}
|
||||
|
||||
@ -689,28 +689,28 @@ namespace platf::dxgi {
|
||||
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() << ']';
|
||||
// BOOST_LOG(error) << "Failed to query ID3D11Device1 [0x"sv << util::hex(status).to_string_view() << ']';
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Open a handle to the shared texture
|
||||
status = device1->OpenSharedResource1(img.encoder_texture_handle, __uuidof(ID3D11Texture2D), (void **) &img_ctx.encoder_texture);
|
||||
if (FAILED(status)) {
|
||||
BOOST_LOG(error) << "Failed to open shared image texture [0x"sv << util::hex(status).to_string_view() << ']';
|
||||
// BOOST_LOG(error) << "Failed to open shared image texture [0x"sv << util::hex(status).to_string_view() << ']';
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Get the keyed mutex to synchronize with the capture code
|
||||
status = img_ctx.encoder_texture->QueryInterface(__uuidof(IDXGIKeyedMutex), (void **) &img_ctx.encoder_mutex);
|
||||
if (FAILED(status)) {
|
||||
BOOST_LOG(error) << "Failed to query IDXGIKeyedMutex [0x"sv << util::hex(status).to_string_view() << ']';
|
||||
// BOOST_LOG(error) << "Failed to query IDXGIKeyedMutex [0x"sv << util::hex(status).to_string_view() << ']';
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Create the SRV for the encoder texture
|
||||
status = device->CreateShaderResourceView(img_ctx.encoder_texture.get(), nullptr, &img_ctx.encoder_input_res);
|
||||
if (FAILED(status)) {
|
||||
BOOST_LOG(error) << "Failed to create shader resource view for encoding [0x"sv << util::hex(status).to_string_view() << ']';
|
||||
// BOOST_LOG(error) << "Failed to create shader resource view for encoding [0x"sv << util::hex(status).to_string_view() << ']';
|
||||
return -1;
|
||||
}
|
||||
|
||||
@ -800,7 +800,7 @@ namespace platf::dxgi {
|
||||
|
||||
auto status = base.device->QueryInterface(IID_ID3D11Multithread, (void **) &mt);
|
||||
if (FAILED(status)) {
|
||||
BOOST_LOG(warning) << "Failed to query ID3D11Multithread interface from device [0x"sv << util::hex(status).to_string_view() << ']';
|
||||
// BOOST_LOG(warning) << "Failed to query ID3D11Multithread interface from device [0x"sv << util::hex(status).to_string_view() << ']';
|
||||
return -1;
|
||||
}
|
||||
|
||||
@ -820,7 +820,7 @@ namespace platf::dxgi {
|
||||
auto err = av_hwframe_get_buffer(hw_frames_ctx, frame, 0);
|
||||
if (err) {
|
||||
char err_str[AV_ERROR_MAX_STRING_SIZE] { 0 };
|
||||
BOOST_LOG(error) << "Failed to get hwframe buffer: "sv << av_make_error_string(err_str, AV_ERROR_MAX_STRING_SIZE, err);
|
||||
// BOOST_LOG(error) << "Failed to get hwframe buffer: "sv << av_make_error_string(err_str, AV_ERROR_MAX_STRING_SIZE, err);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
@ -835,7 +835,7 @@ namespace platf::dxgi {
|
||||
auto err = av_hwframe_map(d3d11_frame.get(), frame, AV_HWFRAME_MAP_WRITE | AV_HWFRAME_MAP_OVERWRITE);
|
||||
if (err) {
|
||||
char err_str[AV_ERROR_MAX_STRING_SIZE] { 0 };
|
||||
BOOST_LOG(error) << "Failed to map D3D11 frame: "sv << av_make_error_string(err_str, AV_ERROR_MAX_STRING_SIZE, err);
|
||||
// BOOST_LOG(error) << "Failed to map D3D11 frame: "sv << av_make_error_string(err_str, AV_ERROR_MAX_STRING_SIZE, err);
|
||||
return -1;
|
||||
}
|
||||
|
||||
@ -861,7 +861,7 @@ namespace platf::dxgi {
|
||||
init_device(std::shared_ptr<platf::display_t> display, adapter_t::pointer adapter_p, pix_fmt_e pix_fmt) {
|
||||
buffer_format = nvenc::nvenc_format_from_sunshine_format(pix_fmt);
|
||||
if (buffer_format == NV_ENC_BUFFER_FORMAT_UNDEFINED) {
|
||||
BOOST_LOG(error) << "Unexpected pixel format for NvENC ["sv << from_pix_fmt(pix_fmt) << ']';
|
||||
// BOOST_LOG(error) << "Unexpected pixel format for NvENC ["sv << from_pix_fmt(pix_fmt) << ']';
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -924,7 +924,7 @@ namespace platf::dxgi {
|
||||
texture2d_t texture;
|
||||
auto status = device->CreateTexture2D(&t, &data, &texture);
|
||||
if (FAILED(status)) {
|
||||
BOOST_LOG(error) << "Failed to create mouse texture [0x"sv << util::hex(status).to_string_view() << ']';
|
||||
// BOOST_LOG(error) << "Failed to create mouse texture [0x"sv << util::hex(status).to_string_view() << ']';
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -932,7 +932,7 @@ namespace platf::dxgi {
|
||||
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() << ']';
|
||||
// BOOST_LOG(error) << "Failed to create cursor shader resource view [0x"sv << util::hex(status).to_string_view() << ']';
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -976,7 +976,7 @@ namespace platf::dxgi {
|
||||
UINT dummy;
|
||||
status = dup.dup->GetFramePointerShape(img_data.size(), std::begin(img_data), &dummy, &shape_info);
|
||||
if (FAILED(status)) {
|
||||
BOOST_LOG(error) << "Failed to get new pointer shape [0x"sv << util::hex(status).to_string_view() << ']';
|
||||
// BOOST_LOG(error) << "Failed to get new pointer shape [0x"sv << util::hex(status).to_string_view() << ']';
|
||||
|
||||
return capture_e::error;
|
||||
}
|
||||
@ -1005,7 +1005,7 @@ namespace platf::dxgi {
|
||||
// Get the texture object from this frame
|
||||
status = res->QueryInterface(IID_ID3D11Texture2D, (void **) &src);
|
||||
if (FAILED(status)) {
|
||||
BOOST_LOG(error) << "Couldn't query interface [0x"sv << util::hex(status).to_string_view() << ']';
|
||||
// BOOST_LOG(error) << "Couldn't query interface [0x"sv << util::hex(status).to_string_view() << ']';
|
||||
return capture_e::error;
|
||||
}
|
||||
|
||||
@ -1015,20 +1015,20 @@ namespace platf::dxgi {
|
||||
// It's possible for our display enumeration to race with mode changes and result in
|
||||
// mismatched image pool and desktop texture sizes. If this happens, just reinit again.
|
||||
if (desc.Width != width_before_rotation || desc.Height != height_before_rotation) {
|
||||
BOOST_LOG(info) << "Capture size changed ["sv << width << 'x' << height << " -> "sv << desc.Width << 'x' << desc.Height << ']';
|
||||
// BOOST_LOG(info) << "Capture size changed ["sv << width << 'x' << height << " -> "sv << desc.Width << 'x' << desc.Height << ']';
|
||||
return capture_e::reinit;
|
||||
}
|
||||
|
||||
// If we don't know the capture format yet, grab it from this texture
|
||||
if (capture_format == DXGI_FORMAT_UNKNOWN) {
|
||||
capture_format = desc.Format;
|
||||
BOOST_LOG(info) << "Capture format ["sv << dxgi_format_to_string(capture_format) << ']';
|
||||
// BOOST_LOG(info) << "Capture format ["sv << dxgi_format_to_string(capture_format) << ']';
|
||||
}
|
||||
|
||||
// It's also possible for the capture format to change on the fly. If that happens,
|
||||
// reinitialize capture to try format detection again and create new images.
|
||||
if (capture_format != desc.Format) {
|
||||
BOOST_LOG(info) << "Capture format changed ["sv << dxgi_format_to_string(capture_format) << " -> "sv << dxgi_format_to_string(desc.Format) << ']';
|
||||
// BOOST_LOG(info) << "Capture format changed ["sv << dxgi_format_to_string(capture_format) << " -> "sv << dxgi_format_to_string(desc.Format) << ']';
|
||||
return capture_e::reinit;
|
||||
}
|
||||
}
|
||||
@ -1125,7 +1125,7 @@ namespace platf::dxgi {
|
||||
t.BindFlags = 0;
|
||||
status = device->CreateTexture2D(&t, nullptr, &surface);
|
||||
if (FAILED(status)) {
|
||||
BOOST_LOG(error) << "Failed to create frame copy texture [0x"sv << util::hex(status).to_string_view() << ']';
|
||||
// BOOST_LOG(error) << "Failed to create frame copy texture [0x"sv << util::hex(status).to_string_view() << ']';
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -1143,7 +1143,7 @@ namespace platf::dxgi {
|
||||
// we must acquire lock before doing anything to it.
|
||||
texture_lock_helper lock_helper(d3d_img->capture_mutex.get());
|
||||
if (!lock_helper.lock()) {
|
||||
BOOST_LOG(error) << "Failed to lock capture texture";
|
||||
// BOOST_LOG(error) << "Failed to lock capture texture";
|
||||
return { nullptr, nullptr };
|
||||
}
|
||||
|
||||
@ -1158,7 +1158,7 @@ namespace platf::dxgi {
|
||||
case lfa::replace_surface_with_img: {
|
||||
auto p_surface = std::get_if<texture2d_t>(&last_frame_variant);
|
||||
if (!p_surface) {
|
||||
BOOST_LOG(error) << "Logical error at " << __FILE__ << ":" << __LINE__;
|
||||
// BOOST_LOG(error) << "Logical error at " << __FILE__ << ":" << __LINE__;
|
||||
return capture_e::error;
|
||||
}
|
||||
|
||||
@ -1181,7 +1181,7 @@ namespace platf::dxgi {
|
||||
case lfa::replace_img_with_surface: {
|
||||
auto p_img = std::get_if<std::shared_ptr<platf::img_t>>(&last_frame_variant);
|
||||
if (!p_img) {
|
||||
BOOST_LOG(error) << "Logical error at " << __FILE__ << ":" << __LINE__;
|
||||
// BOOST_LOG(error) << "Logical error at " << __FILE__ << ":" << __LINE__;
|
||||
return capture_e::error;
|
||||
}
|
||||
auto [d3d_img, lock] = get_locked_d3d_img(*p_img);
|
||||
@ -1258,7 +1258,7 @@ namespace platf::dxgi {
|
||||
case ofa::forward_last_img: {
|
||||
auto p_img = std::get_if<std::shared_ptr<platf::img_t>>(&last_frame_variant);
|
||||
if (!p_img) {
|
||||
BOOST_LOG(error) << "Logical error at " << __FILE__ << ":" << __LINE__;
|
||||
// BOOST_LOG(error) << "Logical error at " << __FILE__ << ":" << __LINE__;
|
||||
return capture_e::error;
|
||||
}
|
||||
img_out = *p_img;
|
||||
@ -1268,11 +1268,11 @@ namespace platf::dxgi {
|
||||
case ofa::copy_last_surface_and_blend_cursor: {
|
||||
auto p_surface = std::get_if<texture2d_t>(&last_frame_variant);
|
||||
if (!p_surface) {
|
||||
BOOST_LOG(error) << "Logical error at " << __FILE__ << ":" << __LINE__;
|
||||
// BOOST_LOG(error) << "Logical error at " << __FILE__ << ":" << __LINE__;
|
||||
return capture_e::error;
|
||||
}
|
||||
if (!blend_mouse_cursor_flag) {
|
||||
BOOST_LOG(error) << "Logical error at " << __FILE__ << ":" << __LINE__;
|
||||
// BOOST_LOG(error) << "Logical error at " << __FILE__ << ":" << __LINE__;
|
||||
return capture_e::error;
|
||||
}
|
||||
|
||||
@ -1340,13 +1340,13 @@ namespace platf::dxgi {
|
||||
|
||||
auto status = device->CreateSamplerState(&sampler_desc, &sampler_linear);
|
||||
if (FAILED(status)) {
|
||||
BOOST_LOG(error) << "Failed to create point sampler state [0x"sv << util::hex(status).to_string_view() << ']';
|
||||
// BOOST_LOG(error) << "Failed to create point sampler state [0x"sv << util::hex(status).to_string_view() << ']';
|
||||
return -1;
|
||||
}
|
||||
|
||||
status = device->CreateVertexShader(cursor_vs_hlsl->GetBufferPointer(), cursor_vs_hlsl->GetBufferSize(), nullptr, &cursor_vs);
|
||||
if (status) {
|
||||
BOOST_LOG(error) << "Failed to create scene vertex shader [0x"sv << util::hex(status).to_string_view() << ']';
|
||||
// BOOST_LOG(error) << "Failed to create scene vertex shader [0x"sv << util::hex(status).to_string_view() << ']';
|
||||
return -1;
|
||||
}
|
||||
|
||||
@ -1355,7 +1355,7 @@ namespace platf::dxgi {
|
||||
int32_t rotation_data[16 / sizeof(int32_t)] { rotation_modifier }; // aligned to 16-byte
|
||||
auto rotation = make_buffer(device.get(), rotation_data);
|
||||
if (!rotation) {
|
||||
BOOST_LOG(error) << "Failed to create display rotation vertex constant buffer";
|
||||
// BOOST_LOG(error) << "Failed to create display rotation vertex constant buffer";
|
||||
return -1;
|
||||
}
|
||||
device_ctx->VSSetConstantBuffers(2, 1, &rotation);
|
||||
@ -1365,7 +1365,7 @@ namespace platf::dxgi {
|
||||
// This shader will normalize scRGB white levels to a user-defined white level
|
||||
status = device->CreatePixelShader(cursor_ps_normalize_white_hlsl->GetBufferPointer(), cursor_ps_normalize_white_hlsl->GetBufferSize(), nullptr, &cursor_ps);
|
||||
if (status) {
|
||||
BOOST_LOG(error) << "Failed to create cursor blending (normalized white) pixel shader [0x"sv << util::hex(status).to_string_view() << ']';
|
||||
// BOOST_LOG(error) << "Failed to create cursor blending (normalized white) pixel shader [0x"sv << util::hex(status).to_string_view() << ']';
|
||||
return -1;
|
||||
}
|
||||
|
||||
@ -1375,7 +1375,7 @@ namespace platf::dxgi {
|
||||
float white_multiplier_data[16 / sizeof(float)] { 300.0f / 80.f }; // aligned to 16-byte
|
||||
auto white_multiplier = make_buffer(device.get(), white_multiplier_data);
|
||||
if (!white_multiplier) {
|
||||
BOOST_LOG(warning) << "Failed to create cursor blending (normalized white) white multiplier constant buffer";
|
||||
// BOOST_LOG(warning) << "Failed to create cursor blending (normalized white) white multiplier constant buffer";
|
||||
return -1;
|
||||
}
|
||||
|
||||
@ -1384,7 +1384,7 @@ namespace platf::dxgi {
|
||||
else {
|
||||
status = device->CreatePixelShader(cursor_ps_hlsl->GetBufferPointer(), cursor_ps_hlsl->GetBufferSize(), nullptr, &cursor_ps);
|
||||
if (status) {
|
||||
BOOST_LOG(error) << "Failed to create cursor blending pixel shader [0x"sv << util::hex(status).to_string_view() << ']';
|
||||
// BOOST_LOG(error) << "Failed to create cursor blending pixel shader [0x"sv << util::hex(status).to_string_view() << ']';
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
@ -1428,7 +1428,7 @@ namespace platf::dxgi {
|
||||
|
||||
// If this is not a dummy image, we must know the format by now
|
||||
if (!dummy && capture_format == DXGI_FORMAT_UNKNOWN) {
|
||||
BOOST_LOG(error) << "display_vram_t::complete_img() called with unknown capture format!";
|
||||
// BOOST_LOG(error) << "display_vram_t::complete_img() called with unknown capture format!";
|
||||
return -1;
|
||||
}
|
||||
|
||||
@ -1474,34 +1474,34 @@ namespace platf::dxgi {
|
||||
status = device->CreateTexture2D(&t, nullptr, &img->capture_texture);
|
||||
}
|
||||
if (FAILED(status)) {
|
||||
BOOST_LOG(error) << "Failed to create img buf texture [0x"sv << util::hex(status).to_string_view() << ']';
|
||||
// BOOST_LOG(error) << "Failed to create img buf texture [0x"sv << util::hex(status).to_string_view() << ']';
|
||||
return -1;
|
||||
}
|
||||
|
||||
status = device->CreateRenderTargetView(img->capture_texture.get(), nullptr, &img->capture_rt);
|
||||
if (FAILED(status)) {
|
||||
BOOST_LOG(error) << "Failed to create render target view [0x"sv << util::hex(status).to_string_view() << ']';
|
||||
// BOOST_LOG(error) << "Failed to create render target view [0x"sv << util::hex(status).to_string_view() << ']';
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Get the keyed mutex to synchronize with the encoding code
|
||||
status = img->capture_texture->QueryInterface(__uuidof(IDXGIKeyedMutex), (void **) &img->capture_mutex);
|
||||
if (FAILED(status)) {
|
||||
BOOST_LOG(error) << "Failed to query IDXGIKeyedMutex [0x"sv << util::hex(status).to_string_view() << ']';
|
||||
// BOOST_LOG(error) << "Failed to query IDXGIKeyedMutex [0x"sv << util::hex(status).to_string_view() << ']';
|
||||
return -1;
|
||||
}
|
||||
|
||||
resource1_t resource;
|
||||
status = img->capture_texture->QueryInterface(__uuidof(IDXGIResource1), (void **) &resource);
|
||||
if (FAILED(status)) {
|
||||
BOOST_LOG(error) << "Failed to query IDXGIResource1 [0x"sv << util::hex(status).to_string_view() << ']';
|
||||
// BOOST_LOG(error) << "Failed to query IDXGIResource1 [0x"sv << util::hex(status).to_string_view() << ']';
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Create a handle for the encoder device to use to open this texture
|
||||
status = resource->CreateSharedHandle(nullptr, DXGI_SHARED_RESOURCE_READ, nullptr, &img->encoder_texture_handle);
|
||||
if (FAILED(status)) {
|
||||
BOOST_LOG(error) << "Failed to create shared texture handle [0x"sv << util::hex(status).to_string_view() << ']';
|
||||
// BOOST_LOG(error) << "Failed to create shared texture handle [0x"sv << util::hex(status).to_string_view() << ']';
|
||||
return -1;
|
||||
}
|
||||
|
||||
@ -1574,37 +1574,37 @@ namespace platf::dxgi {
|
||||
if (config.videoFormat == 2 && version < AMF_MAKE_FULL_VERSION(1, 4, 30, 0)) {
|
||||
// AMF 1.4.30 adds ultra low latency mode for AV1. Don't use AV1 on earlier versions.
|
||||
// This corresponds to driver version 23.5.2 (23.10.01.45) or newer.
|
||||
BOOST_LOG(warning) << "AV1 encoding is disabled on AMF version "sv
|
||||
<< AMF_GET_MAJOR_VERSION(version) << '.'
|
||||
<< AMF_GET_MINOR_VERSION(version) << '.'
|
||||
<< AMF_GET_SUBMINOR_VERSION(version) << '.'
|
||||
<< AMF_GET_BUILD_VERSION(version);
|
||||
BOOST_LOG(warning) << "If your AMD GPU supports AV1 encoding, update your graphics drivers!"sv;
|
||||
// BOOST_LOG(warning) << "AV1 encoding is disabled on AMF version "sv
|
||||
// << AMF_GET_MAJOR_VERSION(version) << '.'
|
||||
// << AMF_GET_MINOR_VERSION(version) << '.'
|
||||
// << AMF_GET_SUBMINOR_VERSION(version) << '.'
|
||||
// << AMF_GET_BUILD_VERSION(version);
|
||||
// BOOST_LOG(warning) << "If your AMD GPU supports AV1 encoding, update your graphics drivers!"sv;
|
||||
return false;
|
||||
}
|
||||
else if (config.dynamicRange && version < AMF_MAKE_FULL_VERSION(1, 4, 23, 0)) {
|
||||
// Older versions of the AMD AMF runtime can crash when fed P010 surfaces.
|
||||
// Fail if AMF version is below 1.4.23 where HEVC Main10 encoding was introduced.
|
||||
// AMF 1.4.23 corresponds to driver version 21.12.1 (21.40.11.03) or newer.
|
||||
BOOST_LOG(warning) << "HDR encoding is disabled on AMF version "sv
|
||||
<< AMF_GET_MAJOR_VERSION(version) << '.'
|
||||
<< AMF_GET_MINOR_VERSION(version) << '.'
|
||||
<< AMF_GET_SUBMINOR_VERSION(version) << '.'
|
||||
<< AMF_GET_BUILD_VERSION(version);
|
||||
BOOST_LOG(warning) << "If your AMD GPU supports HEVC Main10 encoding, update your graphics drivers!"sv;
|
||||
// BOOST_LOG(warning) << "HDR encoding is disabled on AMF version "sv
|
||||
// << AMF_GET_MAJOR_VERSION(version) << '.'
|
||||
// << AMF_GET_MINOR_VERSION(version) << '.'
|
||||
// << AMF_GET_SUBMINOR_VERSION(version) << '.'
|
||||
// << AMF_GET_BUILD_VERSION(version);
|
||||
// BOOST_LOG(warning) << "If your AMD GPU supports HEVC Main10 encoding, update your graphics drivers!"sv;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else {
|
||||
BOOST_LOG(warning) << "AMFQueryVersion() failed: "sv << result;
|
||||
// BOOST_LOG(warning) << "AMFQueryVersion() failed: "sv << result;
|
||||
}
|
||||
}
|
||||
else {
|
||||
BOOST_LOG(warning) << "AMF DLL missing export: "sv << AMF_QUERY_VERSION_FUNCTION_NAME;
|
||||
// BOOST_LOG(warning) << "AMF DLL missing export: "sv << AMF_QUERY_VERSION_FUNCTION_NAME;
|
||||
}
|
||||
}
|
||||
else {
|
||||
BOOST_LOG(warning) << "Detected AMD GPU but AMF failed to load"sv;
|
||||
// BOOST_LOG(warning) << "Detected AMD GPU but AMF failed to load"sv;
|
||||
}
|
||||
}
|
||||
else if (adapter_desc.VendorId == 0x8086) { // Intel
|
||||
@ -1620,7 +1620,7 @@ namespace platf::dxgi {
|
||||
}
|
||||
}
|
||||
else {
|
||||
BOOST_LOG(warning) << "Unknown GPU vendor ID: " << util::hex(adapter_desc.VendorId).to_string_view();
|
||||
// BOOST_LOG(warning) << "Unknown GPU vendor ID: " << util::hex(adapter_desc.VendorId).to_string_view();
|
||||
}
|
||||
|
||||
return true;
|
||||
@ -1629,7 +1629,7 @@ namespace platf::dxgi {
|
||||
std::unique_ptr<avcodec_encode_device_t>
|
||||
display_vram_t::make_avcodec_encode_device(pix_fmt_e pix_fmt) {
|
||||
if (pix_fmt != platf::pix_fmt_e::nv12 && pix_fmt != platf::pix_fmt_e::p010) {
|
||||
BOOST_LOG(error) << "display_vram_t doesn't support pixel format ["sv << from_pix_fmt(pix_fmt) << ']';
|
||||
// BOOST_LOG(error) << "display_vram_t doesn't support pixel format ["sv << from_pix_fmt(pix_fmt) << ']';
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
@ -1656,7 +1656,7 @@ namespace platf::dxgi {
|
||||
|
||||
int
|
||||
init() {
|
||||
BOOST_LOG(info) << "Compiling shaders..."sv;
|
||||
// BOOST_LOG(info) << "Compiling shaders..."sv;
|
||||
|
||||
#define compile_vertex_shader_helper(x) \
|
||||
if (!(x##_hlsl = compile_vertex_shader(SUNSHINE_SHADERS_DIR "/" #x ".hlsl"))) return -1;
|
||||
@ -1675,7 +1675,7 @@ namespace platf::dxgi {
|
||||
compile_pixel_shader_helper(cursor_ps_normalize_white);
|
||||
compile_vertex_shader_helper(cursor_vs);
|
||||
|
||||
BOOST_LOG(info) << "Compiled shaders"sv;
|
||||
// BOOST_LOG(info) << "Compiled shaders"sv;
|
||||
|
||||
#undef compile_vertex_shader_helper
|
||||
#undef compile_pixel_shader_helper
|
||||
|
||||
@ -9,7 +9,6 @@
|
||||
#include <sstream>
|
||||
|
||||
#include <boost/algorithm/string.hpp>
|
||||
#include <boost/asio/ip/address.hpp>
|
||||
#include <boost/process.hpp>
|
||||
|
||||
// prevent clang format from "optimizing" the header include order
|
||||
@ -33,7 +32,6 @@
|
||||
#include "src/utility.h"
|
||||
#include <iterator>
|
||||
|
||||
#include "nvprefs/nvprefs_interface.h"
|
||||
|
||||
// UDP_SEND_MSG_SIZE was added in the Windows 10 20H1 SDK
|
||||
#ifndef UDP_SEND_MSG_SIZE
|
||||
@ -126,14 +124,14 @@ namespace platf {
|
||||
auto hDesk = OpenInputDesktop(DF_ALLOWOTHERACCOUNTHOOK, FALSE, GENERIC_ALL);
|
||||
if (!hDesk) {
|
||||
auto err = GetLastError();
|
||||
BOOST_LOG(error) << "Failed to Open Input Desktop [0x"sv << util::hex(err).to_string_view() << ']';
|
||||
// BOOST_LOG(error) << "Failed to Open Input Desktop [0x"sv << util::hex(err).to_string_view() << ']';
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (!SetThreadDesktop(hDesk)) {
|
||||
auto err = GetLastError();
|
||||
BOOST_LOG(error) << "Failed to sync desktop to thread [0x"sv << util::hex(err).to_string_view() << ']';
|
||||
// BOOST_LOG(error) << "Failed to sync desktop to thread [0x"sv << util::hex(err).to_string_view() << ']';
|
||||
}
|
||||
|
||||
CloseDesktop(hDesk);
|
||||
@ -153,7 +151,7 @@ namespace platf {
|
||||
sizeof(err_string),
|
||||
nullptr);
|
||||
|
||||
BOOST_LOG(error) << prefix << ": "sv << std::string_view { err_string, bytes };
|
||||
// BOOST_LOG(error) << prefix << ": "sv << std::string_view { err_string, bytes };
|
||||
}
|
||||
|
||||
bool
|
||||
@ -171,12 +169,12 @@ namespace platf {
|
||||
if (ret) {
|
||||
if (!CheckTokenMembership(user_token, AdministratorsGroup, &ret)) {
|
||||
ret = false;
|
||||
BOOST_LOG(error) << "Failed to verify token membership for administrative access: " << GetLastError();
|
||||
// BOOST_LOG(error) << "Failed to verify token membership for administrative access: " << GetLastError();
|
||||
}
|
||||
FreeSid(AdministratorsGroup);
|
||||
}
|
||||
else {
|
||||
BOOST_LOG(error) << "Unable to allocate SID to check administrative access: " << GetLastError();
|
||||
// BOOST_LOG(error) << "Unable to allocate SID to check administrative access: " << GetLastError();
|
||||
}
|
||||
|
||||
return ret;
|
||||
@ -197,13 +195,13 @@ namespace platf {
|
||||
consoleSessionId = WTSGetActiveConsoleSessionId();
|
||||
if (0xFFFFFFFF == consoleSessionId) {
|
||||
// If there is no active console session, log a warning and return null
|
||||
BOOST_LOG(warning) << "There isn't an active user session, therefore it is not possible to execute commands under the users profile.";
|
||||
// BOOST_LOG(warning) << "There isn't an active user session, therefore it is not possible to execute commands under the users profile.";
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Get the user token for the active console session
|
||||
if (!WTSQueryUserToken(consoleSessionId, &userToken)) {
|
||||
BOOST_LOG(debug) << "QueryUserToken failed, this would prevent commands from launching under the users profile.";
|
||||
// BOOST_LOG(debug) << "QueryUserToken failed, this would prevent commands from launching under the users profile.";
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
@ -213,7 +211,7 @@ namespace platf {
|
||||
// Elevation - Limited: User is an admin, has UAC enabled.
|
||||
// Elevation - Full: User is an admin, has UAC disabled.
|
||||
if (!GetTokenInformation(userToken, TokenElevationType, &elevationType, sizeof(TOKEN_ELEVATION_TYPE), &dwSize)) {
|
||||
BOOST_LOG(debug) << "Retrieving token information failed: " << GetLastError();
|
||||
// BOOST_LOG(debug) << "Retrieving token information failed: " << GetLastError();
|
||||
CloseHandle(userToken);
|
||||
return nullptr;
|
||||
}
|
||||
@ -222,8 +220,8 @@ namespace platf {
|
||||
// The documentation for this scenario is conflicting, so we'll double check to see if user is actually an admin.
|
||||
if (elevated && (elevationType == TokenElevationTypeDefault && !IsUserAdmin(userToken))) {
|
||||
// We don't have to strip the token or do anything here, but let's give the user a warning so they're aware what is happening.
|
||||
BOOST_LOG(warning) << "This command requires elevation and the current user account logged in does not have administrator rights. "
|
||||
<< "For security reasons Sunshine will retain the same access level as the current user and will not elevate it.";
|
||||
// BOOST_LOG(warning) << "This command requires elevation and the current user account logged in does not have administrator rights. "
|
||||
// << "For security reasons Sunshine will retain the same access level as the current user and will not elevate it.";
|
||||
}
|
||||
|
||||
// User has a limited token, this means they have UAC enabled and is an Administrator
|
||||
@ -232,7 +230,7 @@ namespace platf {
|
||||
// Retrieve the administrator token that is linked to the limited token
|
||||
if (!GetTokenInformation(userToken, TokenLinkedToken, reinterpret_cast<void *>(&linkedToken), sizeof(TOKEN_LINKED_TOKEN), &dwSize)) {
|
||||
// If the retrieval failed, log an error message and return null
|
||||
BOOST_LOG(error) << "Retrieving linked token information failed: " << GetLastError();
|
||||
// BOOST_LOG(error) << "Retrieving linked token information failed: " << GetLastError();
|
||||
CloseHandle(userToken);
|
||||
|
||||
// There is no scenario where this should be hit, except for an actual error.
|
||||
@ -298,7 +296,7 @@ namespace platf {
|
||||
// Allocate memory for the SID structure
|
||||
SystemSid = LocalAlloc(LMEM_FIXED, dwSize);
|
||||
if (SystemSid == nullptr) {
|
||||
BOOST_LOG(error) << "Failed to allocate memory for the SID structure: " << GetLastError();
|
||||
// BOOST_LOG(error) << "Failed to allocate memory for the SID structure: " << GetLastError();
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -307,12 +305,12 @@ namespace platf {
|
||||
if (ret) {
|
||||
// Check if the current process token contains this SID
|
||||
if (!CheckTokenMembership(nullptr, SystemSid, &ret)) {
|
||||
BOOST_LOG(error) << "Failed to check token membership: " << GetLastError();
|
||||
// BOOST_LOG(error) << "Failed to check token membership: " << GetLastError();
|
||||
ret = false;
|
||||
}
|
||||
}
|
||||
else {
|
||||
BOOST_LOG(error) << "Failed to create a SID for the local system account. This may happen if the system is out of memory or if the SID buffer is too small: " << GetLastError();
|
||||
// BOOST_LOG(error) << "Failed to create a SID for the local system account. This may happen if the system is out of memory or if the SID buffer is too small: " << GetLastError();
|
||||
}
|
||||
|
||||
// Free the memory allocated for the SID structure
|
||||
@ -407,12 +405,12 @@ namespace platf {
|
||||
if (process_launched) {
|
||||
// If the launch was successful, create a new bp::child object representing the new process
|
||||
auto child = bp::child((bp::pid_t) process_info.dwProcessId);
|
||||
BOOST_LOG(info) << cmd << " running with PID "sv << child.id();
|
||||
// BOOST_LOG(info) << cmd << " running with PID "sv << child.id();
|
||||
return child;
|
||||
}
|
||||
else {
|
||||
auto winerror = GetLastError();
|
||||
BOOST_LOG(error) << "Failed to launch process: "sv << winerror;
|
||||
// BOOST_LOG(error) << "Failed to launch process: "sv << winerror;
|
||||
ec = std::make_error_code(std::errc::invalid_argument);
|
||||
// We must NOT attach the failed process here, since this case can potentially be induced by ACL
|
||||
// manipulation (denying yourself execute permission) to cause an escalation of privilege.
|
||||
@ -437,7 +435,7 @@ namespace platf {
|
||||
if (!ImpersonateLoggedOnUser(user_token)) {
|
||||
auto winerror = GetLastError();
|
||||
// Log the failure of impersonating the user and its error code
|
||||
BOOST_LOG(error) << "Failed to impersonate user: "sv << winerror;
|
||||
// BOOST_LOG(error) << "Failed to impersonate user: "sv << winerror;
|
||||
ec = std::make_error_code(std::errc::permission_denied);
|
||||
return ec;
|
||||
}
|
||||
@ -451,7 +449,7 @@ namespace platf {
|
||||
if (!RevertToSelf()) {
|
||||
auto winerror = GetLastError();
|
||||
// Log the failure of reverting to self and its error code
|
||||
BOOST_LOG(fatal) << "Failed to revert to self after impersonation: "sv << winerror;
|
||||
// BOOST_LOG(fatal) << "Failed to revert to self after impersonation: "sv << winerror;
|
||||
std::abort();
|
||||
}
|
||||
|
||||
@ -520,124 +518,6 @@ namespace platf {
|
||||
return startup_info;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Run a command on the users profile.
|
||||
*
|
||||
* Launches a child process as the user, using the current user's environment and a specific working directory.
|
||||
*
|
||||
* @param elevated Specify whether to elevate the process.
|
||||
* @param interactive Specify whether this will run in a window or hidden.
|
||||
* @param cmd The command to run.
|
||||
* @param working_dir The working directory for the new process.
|
||||
* @param env The environment variables to use for the new process.
|
||||
* @param file A file object to redirect the child process's output to (may be `nullptr`).
|
||||
* @param ec An error code, set to indicate any errors that occur during the launch process.
|
||||
* @param group A pointer to a `bp::group` object to which the new process should belong (may be `nullptr`).
|
||||
* @return A `bp::child` object representing the new process, or an empty `bp::child` object if the launch fails.
|
||||
*/
|
||||
bp::child
|
||||
run_command(bool elevated, bool interactive, const std::string &cmd, boost::filesystem::path &working_dir, const bp::environment &env, FILE *file, std::error_code &ec, bp::group *group) {
|
||||
BOOL ret;
|
||||
// Convert cmd, env, and working_dir to the appropriate character sets for Win32 APIs
|
||||
std::wstring wcmd = converter.from_bytes(cmd);
|
||||
std::wstring start_dir = converter.from_bytes(working_dir.string());
|
||||
|
||||
HANDLE job = group ? group->native_handle() : nullptr;
|
||||
STARTUPINFOEXW startup_info = create_startup_info(file, job ? &job : nullptr, ec);
|
||||
PROCESS_INFORMATION process_info;
|
||||
|
||||
// Clone the environment to create a local copy. Boost.Process (bp) shares the environment with all spawned processes.
|
||||
// Since we're going to modify the 'env' variable by merging user-specific environment variables into it,
|
||||
// we make a clone to prevent side effects to the shared environment.
|
||||
bp::environment cloned_env = env;
|
||||
|
||||
if (ec) {
|
||||
// In the event that startup_info failed, return a blank child process.
|
||||
return bp::child();
|
||||
}
|
||||
|
||||
// Use RAII to ensure the attribute list is freed when we're done with it
|
||||
auto attr_list_free = util::fail_guard([list = startup_info.lpAttributeList]() {
|
||||
free_proc_thread_attr_list(list);
|
||||
});
|
||||
|
||||
DWORD creation_flags = EXTENDED_STARTUPINFO_PRESENT | CREATE_UNICODE_ENVIRONMENT | CREATE_BREAKAWAY_FROM_JOB;
|
||||
|
||||
// Create a new console for interactive processes and use no console for non-interactive processes
|
||||
creation_flags |= interactive ? CREATE_NEW_CONSOLE : CREATE_NO_WINDOW;
|
||||
|
||||
if (is_running_as_system()) {
|
||||
// Duplicate the current user's token
|
||||
HANDLE user_token = retrieve_users_token(elevated);
|
||||
if (!user_token) {
|
||||
// Fail the launch rather than risking launching with Sunshine's permissions unmodified.
|
||||
ec = std::make_error_code(std::errc::permission_denied);
|
||||
return bp::child();
|
||||
}
|
||||
|
||||
// Use RAII to ensure the shell token is closed when we're done with it
|
||||
auto token_close = util::fail_guard([user_token]() {
|
||||
CloseHandle(user_token);
|
||||
});
|
||||
|
||||
// Populate env with user-specific environment variables
|
||||
if (!merge_user_environment_block(cloned_env, user_token)) {
|
||||
ec = std::make_error_code(std::errc::not_enough_memory);
|
||||
return bp::child();
|
||||
}
|
||||
|
||||
// Open the process as the current user account, elevation is handled in the token itself.
|
||||
ec = impersonate_current_user(user_token, [&]() {
|
||||
std::wstring env_block = create_environment_block(cloned_env);
|
||||
ret = CreateProcessAsUserW(user_token,
|
||||
NULL,
|
||||
(LPWSTR) wcmd.c_str(),
|
||||
NULL,
|
||||
NULL,
|
||||
!!(startup_info.StartupInfo.dwFlags & STARTF_USESTDHANDLES),
|
||||
creation_flags,
|
||||
env_block.data(),
|
||||
start_dir.empty() ? NULL : start_dir.c_str(),
|
||||
(LPSTARTUPINFOW) &startup_info,
|
||||
&process_info);
|
||||
});
|
||||
}
|
||||
// Otherwise, launch the process using CreateProcessW()
|
||||
// This will inherit the elevation of whatever the user launched Sunshine with.
|
||||
else {
|
||||
// Open our current token to resolve environment variables
|
||||
HANDLE process_token;
|
||||
if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY | TOKEN_DUPLICATE, &process_token)) {
|
||||
ec = std::make_error_code(std::errc::permission_denied);
|
||||
return bp::child();
|
||||
}
|
||||
auto token_close = util::fail_guard([process_token]() {
|
||||
CloseHandle(process_token);
|
||||
});
|
||||
|
||||
// Populate env with user-specific environment variables
|
||||
if (!merge_user_environment_block(cloned_env, process_token)) {
|
||||
ec = std::make_error_code(std::errc::not_enough_memory);
|
||||
return bp::child();
|
||||
}
|
||||
|
||||
std::wstring env_block = create_environment_block(cloned_env);
|
||||
ret = CreateProcessW(NULL,
|
||||
(LPWSTR) wcmd.c_str(),
|
||||
NULL,
|
||||
NULL,
|
||||
!!(startup_info.StartupInfo.dwFlags & STARTF_USESTDHANDLES),
|
||||
creation_flags,
|
||||
env_block.data(),
|
||||
start_dir.empty() ? NULL : start_dir.c_str(),
|
||||
(LPSTARTUPINFOW) &startup_info,
|
||||
&process_info);
|
||||
}
|
||||
|
||||
// Use the results of the launch to create a bp::child object
|
||||
return create_boost_child_from_results(ret, cmd, ec, process_info);
|
||||
}
|
||||
|
||||
void
|
||||
adjust_thread_priority(thread_priority_e priority) {
|
||||
int win32_priority;
|
||||
@ -656,206 +536,20 @@ namespace platf {
|
||||
win32_priority = THREAD_PRIORITY_HIGHEST;
|
||||
break;
|
||||
default:
|
||||
BOOST_LOG(error) << "Unknown thread priority: "sv << (int) priority;
|
||||
// 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;
|
||||
// BOOST_LOG(warning) << "Unable to set thread priority to "sv << win32_priority << ": "sv << winerr;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
SOCKADDR_IN
|
||||
to_sockaddr(boost::asio::ip::address_v4 address, uint16_t port) {
|
||||
SOCKADDR_IN saddr_v4 = {};
|
||||
|
||||
saddr_v4.sin_family = AF_INET;
|
||||
saddr_v4.sin_port = htons(port);
|
||||
|
||||
auto addr_bytes = address.to_bytes();
|
||||
memcpy(&saddr_v4.sin_addr, addr_bytes.data(), sizeof(saddr_v4.sin_addr));
|
||||
|
||||
return saddr_v4;
|
||||
}
|
||||
|
||||
SOCKADDR_IN6
|
||||
to_sockaddr(boost::asio::ip::address_v6 address, uint16_t port) {
|
||||
SOCKADDR_IN6 saddr_v6 = {};
|
||||
|
||||
saddr_v6.sin6_family = AF_INET6;
|
||||
saddr_v6.sin6_port = htons(port);
|
||||
saddr_v6.sin6_scope_id = address.scope_id();
|
||||
|
||||
auto addr_bytes = address.to_bytes();
|
||||
memcpy(&saddr_v6.sin6_addr, addr_bytes.data(), sizeof(saddr_v6.sin6_addr));
|
||||
|
||||
return saddr_v6;
|
||||
}
|
||||
|
||||
// Use UDP segmentation offload if it is supported by the OS. If the NIC is capable, this will use
|
||||
// hardware acceleration to reduce CPU usage. Support for USO was introduced in Windows 10 20H1.
|
||||
bool
|
||||
send_batch(batched_send_info_t &send_info) {
|
||||
WSAMSG msg;
|
||||
|
||||
// Convert the target address into a SOCKADDR
|
||||
SOCKADDR_IN taddr_v4;
|
||||
SOCKADDR_IN6 taddr_v6;
|
||||
if (send_info.target_address.is_v6()) {
|
||||
taddr_v6 = to_sockaddr(send_info.target_address.to_v6(), send_info.target_port);
|
||||
|
||||
msg.name = (PSOCKADDR) &taddr_v6;
|
||||
msg.namelen = sizeof(taddr_v6);
|
||||
}
|
||||
else {
|
||||
taddr_v4 = to_sockaddr(send_info.target_address.to_v4(), send_info.target_port);
|
||||
|
||||
msg.name = (PSOCKADDR) &taddr_v4;
|
||||
msg.namelen = sizeof(taddr_v4);
|
||||
}
|
||||
|
||||
WSABUF buf;
|
||||
buf.buf = (char *) send_info.buffer;
|
||||
buf.len = send_info.block_size * send_info.block_count;
|
||||
|
||||
msg.lpBuffers = &buf;
|
||||
msg.dwBufferCount = 1;
|
||||
msg.dwFlags = 0;
|
||||
|
||||
// At most, one DWORD option and one PKTINFO option
|
||||
char cmbuf[WSA_CMSG_SPACE(sizeof(DWORD)) +
|
||||
std::max(WSA_CMSG_SPACE(sizeof(IN6_PKTINFO)), WSA_CMSG_SPACE(sizeof(IN_PKTINFO)))] = {};
|
||||
ULONG cmbuflen = 0;
|
||||
|
||||
msg.Control.buf = cmbuf;
|
||||
msg.Control.len = sizeof(cmbuf);
|
||||
|
||||
auto cm = WSA_CMSG_FIRSTHDR(&msg);
|
||||
if (send_info.source_address.is_v6()) {
|
||||
IN6_PKTINFO pktInfo;
|
||||
|
||||
SOCKADDR_IN6 saddr_v6 = to_sockaddr(send_info.source_address.to_v6(), 0);
|
||||
pktInfo.ipi6_addr = saddr_v6.sin6_addr;
|
||||
pktInfo.ipi6_ifindex = 0;
|
||||
|
||||
cmbuflen += WSA_CMSG_SPACE(sizeof(pktInfo));
|
||||
|
||||
cm->cmsg_level = IPPROTO_IPV6;
|
||||
cm->cmsg_type = IPV6_PKTINFO;
|
||||
cm->cmsg_len = WSA_CMSG_LEN(sizeof(pktInfo));
|
||||
memcpy(WSA_CMSG_DATA(cm), &pktInfo, sizeof(pktInfo));
|
||||
}
|
||||
else {
|
||||
IN_PKTINFO pktInfo;
|
||||
|
||||
SOCKADDR_IN saddr_v4 = to_sockaddr(send_info.source_address.to_v4(), 0);
|
||||
pktInfo.ipi_addr = saddr_v4.sin_addr;
|
||||
pktInfo.ipi_ifindex = 0;
|
||||
|
||||
cmbuflen += WSA_CMSG_SPACE(sizeof(pktInfo));
|
||||
|
||||
cm->cmsg_level = IPPROTO_IP;
|
||||
cm->cmsg_type = IP_PKTINFO;
|
||||
cm->cmsg_len = WSA_CMSG_LEN(sizeof(pktInfo));
|
||||
memcpy(WSA_CMSG_DATA(cm), &pktInfo, sizeof(pktInfo));
|
||||
}
|
||||
|
||||
if (send_info.block_count > 1) {
|
||||
cmbuflen += WSA_CMSG_SPACE(sizeof(DWORD));
|
||||
|
||||
cm = WSA_CMSG_NXTHDR(&msg, cm);
|
||||
cm->cmsg_level = IPPROTO_UDP;
|
||||
cm->cmsg_type = UDP_SEND_MSG_SIZE;
|
||||
cm->cmsg_len = WSA_CMSG_LEN(sizeof(DWORD));
|
||||
*((DWORD *) WSA_CMSG_DATA(cm)) = send_info.block_size;
|
||||
}
|
||||
|
||||
msg.Control.len = cmbuflen;
|
||||
|
||||
// If USO is not supported, this will fail and the caller will fall back to unbatched sends.
|
||||
DWORD bytes_sent;
|
||||
return WSASendMsg((SOCKET) send_info.native_socket, &msg, 1, &bytes_sent, nullptr, nullptr) != SOCKET_ERROR;
|
||||
}
|
||||
|
||||
bool
|
||||
send(send_info_t &send_info) {
|
||||
WSAMSG msg;
|
||||
|
||||
// Convert the target address into a SOCKADDR
|
||||
SOCKADDR_IN taddr_v4;
|
||||
SOCKADDR_IN6 taddr_v6;
|
||||
if (send_info.target_address.is_v6()) {
|
||||
taddr_v6 = to_sockaddr(send_info.target_address.to_v6(), send_info.target_port);
|
||||
|
||||
msg.name = (PSOCKADDR) &taddr_v6;
|
||||
msg.namelen = sizeof(taddr_v6);
|
||||
}
|
||||
else {
|
||||
taddr_v4 = to_sockaddr(send_info.target_address.to_v4(), send_info.target_port);
|
||||
|
||||
msg.name = (PSOCKADDR) &taddr_v4;
|
||||
msg.namelen = sizeof(taddr_v4);
|
||||
}
|
||||
|
||||
WSABUF buf;
|
||||
buf.buf = (char *) send_info.buffer;
|
||||
buf.len = send_info.size;
|
||||
|
||||
msg.lpBuffers = &buf;
|
||||
msg.dwBufferCount = 1;
|
||||
msg.dwFlags = 0;
|
||||
|
||||
char cmbuf[std::max(WSA_CMSG_SPACE(sizeof(IN6_PKTINFO)), WSA_CMSG_SPACE(sizeof(IN_PKTINFO)))] = {};
|
||||
ULONG cmbuflen = 0;
|
||||
|
||||
msg.Control.buf = cmbuf;
|
||||
msg.Control.len = sizeof(cmbuf);
|
||||
|
||||
auto cm = WSA_CMSG_FIRSTHDR(&msg);
|
||||
if (send_info.source_address.is_v6()) {
|
||||
IN6_PKTINFO pktInfo;
|
||||
|
||||
SOCKADDR_IN6 saddr_v6 = to_sockaddr(send_info.source_address.to_v6(), 0);
|
||||
pktInfo.ipi6_addr = saddr_v6.sin6_addr;
|
||||
pktInfo.ipi6_ifindex = 0;
|
||||
|
||||
cmbuflen += WSA_CMSG_SPACE(sizeof(pktInfo));
|
||||
|
||||
cm->cmsg_level = IPPROTO_IPV6;
|
||||
cm->cmsg_type = IPV6_PKTINFO;
|
||||
cm->cmsg_len = WSA_CMSG_LEN(sizeof(pktInfo));
|
||||
memcpy(WSA_CMSG_DATA(cm), &pktInfo, sizeof(pktInfo));
|
||||
}
|
||||
else {
|
||||
IN_PKTINFO pktInfo;
|
||||
|
||||
SOCKADDR_IN saddr_v4 = to_sockaddr(send_info.source_address.to_v4(), 0);
|
||||
pktInfo.ipi_addr = saddr_v4.sin_addr;
|
||||
pktInfo.ipi_ifindex = 0;
|
||||
|
||||
cmbuflen += WSA_CMSG_SPACE(sizeof(pktInfo));
|
||||
|
||||
cm->cmsg_level = IPPROTO_IP;
|
||||
cm->cmsg_type = IP_PKTINFO;
|
||||
cm->cmsg_len = WSA_CMSG_LEN(sizeof(pktInfo));
|
||||
memcpy(WSA_CMSG_DATA(cm), &pktInfo, sizeof(pktInfo));
|
||||
}
|
||||
|
||||
msg.Control.len = cmbuflen;
|
||||
|
||||
DWORD bytes_sent;
|
||||
if (WSASendMsg((SOCKET) send_info.native_socket, &msg, 1, &bytes_sent, nullptr, nullptr) == SOCKET_ERROR) {
|
||||
auto winerr = WSAGetLastError();
|
||||
BOOST_LOG(warning) << "WSASendMsg() failed: "sv << winerr;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
int64_t
|
||||
qpc_counter() {
|
||||
LARGE_INTEGER performace_counter;
|
||||
|
||||
@ -1,289 +0,0 @@
|
||||
#include "nvprefs_common.h"
|
||||
|
||||
#include "driver_settings.h"
|
||||
|
||||
namespace {
|
||||
|
||||
const auto sunshine_application_profile_name = L"SunshineStream";
|
||||
const auto sunshine_application_path = L"sunshine.exe";
|
||||
|
||||
void
|
||||
nvapi_error_message(NvAPI_Status status) {
|
||||
NvAPI_ShortString message = {};
|
||||
NvAPI_GetErrorMessage(status, message);
|
||||
nvprefs::error_message(std::string("NvAPI error: ") + message);
|
||||
}
|
||||
|
||||
void
|
||||
fill_nvapi_string(NvAPI_UnicodeString &dest, const wchar_t *src) {
|
||||
static_assert(sizeof(NvU16) == sizeof(wchar_t));
|
||||
memcpy_s(dest, NVAPI_UNICODE_STRING_MAX * sizeof(NvU16), src, (wcslen(src) + 1) * sizeof(wchar_t));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace nvprefs {
|
||||
|
||||
driver_settings_t::~driver_settings_t() {
|
||||
if (session_handle) {
|
||||
NvAPI_DRS_DestroySession(session_handle);
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
driver_settings_t::init() {
|
||||
if (session_handle) return true;
|
||||
|
||||
NvAPI_Status status;
|
||||
|
||||
status = NvAPI_Initialize();
|
||||
if (status != NVAPI_OK) {
|
||||
info_message("NvAPI_Initialize() failed, ignore if you don't have NVIDIA video card");
|
||||
return false;
|
||||
}
|
||||
|
||||
status = NvAPI_DRS_CreateSession(&session_handle);
|
||||
if (status != NVAPI_OK) {
|
||||
nvapi_error_message(status);
|
||||
error_message("NvAPI_DRS_CreateSession() failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
return load_settings();
|
||||
}
|
||||
|
||||
void
|
||||
driver_settings_t::destroy() {
|
||||
if (session_handle) {
|
||||
NvAPI_DRS_DestroySession(session_handle);
|
||||
session_handle = 0;
|
||||
}
|
||||
NvAPI_Unload();
|
||||
}
|
||||
|
||||
bool
|
||||
driver_settings_t::load_settings() {
|
||||
if (!session_handle) return false;
|
||||
|
||||
NvAPI_Status status = NvAPI_DRS_LoadSettings(session_handle);
|
||||
if (status != NVAPI_OK) {
|
||||
nvapi_error_message(status);
|
||||
error_message("NvAPI_DRS_LoadSettings() failed");
|
||||
destroy();
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
driver_settings_t::save_settings() {
|
||||
if (!session_handle) return false;
|
||||
|
||||
NvAPI_Status status = NvAPI_DRS_SaveSettings(session_handle);
|
||||
if (status != NVAPI_OK) {
|
||||
nvapi_error_message(status);
|
||||
error_message("NvAPI_DRS_SaveSettings() failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
driver_settings_t::restore_global_profile_to_undo(const undo_data_t &undo_data) {
|
||||
if (!session_handle) return false;
|
||||
|
||||
auto [opengl_swapchain_saved, opengl_swapchain_our_value, opengl_swapchain_undo_value] = undo_data.get_opengl_swapchain();
|
||||
|
||||
if (opengl_swapchain_saved) {
|
||||
NvAPI_Status status;
|
||||
|
||||
NvDRSProfileHandle profile_handle = 0;
|
||||
status = NvAPI_DRS_GetBaseProfile(session_handle, &profile_handle);
|
||||
if (status != NVAPI_OK) {
|
||||
nvapi_error_message(status);
|
||||
error_message("NvAPI_DRS_GetBaseProfile() failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
NVDRS_SETTING setting = {};
|
||||
setting.version = NVDRS_SETTING_VER;
|
||||
status = NvAPI_DRS_GetSetting(session_handle, profile_handle, OGL_CPL_PREFER_DXPRESENT_ID, &setting);
|
||||
|
||||
if (status == NVAPI_OK && setting.settingLocation == NVDRS_CURRENT_PROFILE_LOCATION && setting.u32CurrentValue == opengl_swapchain_our_value) {
|
||||
if (opengl_swapchain_undo_value) {
|
||||
setting = {};
|
||||
setting.version = NVDRS_SETTING_VER1;
|
||||
setting.settingId = OGL_CPL_PREFER_DXPRESENT_ID;
|
||||
setting.settingType = NVDRS_DWORD_TYPE;
|
||||
setting.settingLocation = NVDRS_CURRENT_PROFILE_LOCATION;
|
||||
setting.u32CurrentValue = *opengl_swapchain_undo_value;
|
||||
|
||||
status = NvAPI_DRS_SetSetting(session_handle, profile_handle, &setting);
|
||||
|
||||
if (status != NVAPI_OK) {
|
||||
nvapi_error_message(status);
|
||||
error_message("NvAPI_DRS_SetSetting() OGL_CPL_PREFER_DXPRESENT failed");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else {
|
||||
status = NvAPI_DRS_DeleteProfileSetting(session_handle, profile_handle, OGL_CPL_PREFER_DXPRESENT_ID);
|
||||
|
||||
if (status != NVAPI_OK && status != NVAPI_SETTING_NOT_FOUND) {
|
||||
nvapi_error_message(status);
|
||||
error_message("NvAPI_DRS_DeleteProfileSetting() OGL_CPL_PREFER_DXPRESENT failed");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
info_message("Restored OGL_CPL_PREFER_DXPRESENT for base profile");
|
||||
}
|
||||
else if (status == NVAPI_OK || status == NVAPI_SETTING_NOT_FOUND) {
|
||||
info_message("OGL_CPL_PREFER_DXPRESENT has been changed from our value in base profile, not restoring");
|
||||
}
|
||||
else {
|
||||
error_message("NvAPI_DRS_GetSetting() OGL_CPL_PREFER_DXPRESENT failed");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
driver_settings_t::check_and_modify_global_profile(std::optional<undo_data_t> &undo_data) {
|
||||
if (!session_handle) return false;
|
||||
|
||||
undo_data.reset();
|
||||
NvAPI_Status status;
|
||||
|
||||
NvDRSProfileHandle profile_handle = 0;
|
||||
status = NvAPI_DRS_GetBaseProfile(session_handle, &profile_handle);
|
||||
if (status != NVAPI_OK) {
|
||||
nvapi_error_message(status);
|
||||
error_message("NvAPI_DRS_GetBaseProfile() failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
NVDRS_SETTING setting = {};
|
||||
setting.version = NVDRS_SETTING_VER;
|
||||
status = NvAPI_DRS_GetSetting(session_handle, profile_handle, OGL_CPL_PREFER_DXPRESENT_ID, &setting);
|
||||
|
||||
// Remember current OpenGL/Vulkan DXGI swapchain setting and change it if needed
|
||||
if (status == NVAPI_SETTING_NOT_FOUND || (status == NVAPI_OK && setting.u32CurrentValue != OGL_CPL_PREFER_DXPRESENT_PREFER_ENABLED)) {
|
||||
undo_data = undo_data_t();
|
||||
if (status == NVAPI_OK) {
|
||||
undo_data->set_opengl_swapchain(OGL_CPL_PREFER_DXPRESENT_PREFER_ENABLED, setting.u32CurrentValue);
|
||||
}
|
||||
else {
|
||||
undo_data->set_opengl_swapchain(OGL_CPL_PREFER_DXPRESENT_PREFER_ENABLED, std::nullopt);
|
||||
}
|
||||
|
||||
setting = {};
|
||||
setting.version = NVDRS_SETTING_VER1;
|
||||
setting.settingId = OGL_CPL_PREFER_DXPRESENT_ID;
|
||||
setting.settingType = NVDRS_DWORD_TYPE;
|
||||
setting.settingLocation = NVDRS_CURRENT_PROFILE_LOCATION;
|
||||
setting.u32CurrentValue = OGL_CPL_PREFER_DXPRESENT_PREFER_ENABLED;
|
||||
|
||||
status = NvAPI_DRS_SetSetting(session_handle, profile_handle, &setting);
|
||||
if (status != NVAPI_OK) {
|
||||
nvapi_error_message(status);
|
||||
error_message("NvAPI_DRS_SetSetting() OGL_CPL_PREFER_DXPRESENT failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
info_message("Changed OGL_CPL_PREFER_DXPRESENT to OGL_CPL_PREFER_DXPRESENT_PREFER_ENABLED for base profile");
|
||||
}
|
||||
else if (status != NVAPI_OK) {
|
||||
nvapi_error_message(status);
|
||||
error_message("NvAPI_DRS_GetSetting() OGL_CPL_PREFER_DXPRESENT failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
driver_settings_t::check_and_modify_application_profile(bool &modified) {
|
||||
if (!session_handle) return false;
|
||||
|
||||
modified = false;
|
||||
NvAPI_Status status;
|
||||
|
||||
NvAPI_UnicodeString profile_name = {};
|
||||
fill_nvapi_string(profile_name, sunshine_application_profile_name);
|
||||
|
||||
NvDRSProfileHandle profile_handle = 0;
|
||||
status = NvAPI_DRS_FindProfileByName(session_handle, profile_name, &profile_handle);
|
||||
|
||||
if (status != NVAPI_OK) {
|
||||
// Create application profile if missing
|
||||
NVDRS_PROFILE profile = {};
|
||||
profile.version = NVDRS_PROFILE_VER1;
|
||||
fill_nvapi_string(profile.profileName, sunshine_application_profile_name);
|
||||
status = NvAPI_DRS_CreateProfile(session_handle, &profile, &profile_handle);
|
||||
if (status != NVAPI_OK) {
|
||||
nvapi_error_message(status);
|
||||
error_message("NvAPI_DRS_CreateProfile() failed");
|
||||
return false;
|
||||
}
|
||||
modified = true;
|
||||
}
|
||||
|
||||
NvAPI_UnicodeString sunshine_path = {};
|
||||
fill_nvapi_string(sunshine_path, sunshine_application_path);
|
||||
|
||||
NVDRS_APPLICATION application = {};
|
||||
application.version = NVDRS_APPLICATION_VER_V1;
|
||||
status = NvAPI_DRS_GetApplicationInfo(session_handle, profile_handle, sunshine_path, &application);
|
||||
|
||||
if (status != NVAPI_OK) {
|
||||
// Add application to application profile if missing
|
||||
application.version = NVDRS_APPLICATION_VER_V1;
|
||||
application.isPredefined = 0;
|
||||
fill_nvapi_string(application.appName, sunshine_application_path);
|
||||
fill_nvapi_string(application.userFriendlyName, sunshine_application_path);
|
||||
fill_nvapi_string(application.launcher, L"");
|
||||
|
||||
status = NvAPI_DRS_CreateApplication(session_handle, profile_handle, &application);
|
||||
if (status != NVAPI_OK) {
|
||||
nvapi_error_message(status);
|
||||
error_message("NvAPI_DRS_CreateApplication() failed");
|
||||
return false;
|
||||
}
|
||||
modified = true;
|
||||
}
|
||||
|
||||
NVDRS_SETTING setting = {};
|
||||
setting.version = NVDRS_SETTING_VER1;
|
||||
status = NvAPI_DRS_GetSetting(session_handle, profile_handle, PREFERRED_PSTATE_ID, &setting);
|
||||
|
||||
if (status != NVAPI_OK ||
|
||||
setting.settingLocation != NVDRS_CURRENT_PROFILE_LOCATION ||
|
||||
setting.u32CurrentValue != PREFERRED_PSTATE_PREFER_MAX) {
|
||||
// Set power setting if needed
|
||||
setting = {};
|
||||
setting.version = NVDRS_SETTING_VER1;
|
||||
setting.settingId = PREFERRED_PSTATE_ID;
|
||||
setting.settingType = NVDRS_DWORD_TYPE;
|
||||
setting.settingLocation = NVDRS_CURRENT_PROFILE_LOCATION;
|
||||
setting.u32CurrentValue = PREFERRED_PSTATE_PREFER_MAX;
|
||||
|
||||
status = NvAPI_DRS_SetSetting(session_handle, profile_handle, &setting);
|
||||
if (status != NVAPI_OK) {
|
||||
nvapi_error_message(status);
|
||||
error_message("NvAPI_DRS_SetSetting() PREFERRED_PSTATE failed");
|
||||
return false;
|
||||
}
|
||||
modified = true;
|
||||
|
||||
info_message(std::wstring(L"Changed PREFERRED_PSTATE to PREFERRED_PSTATE_PREFER_MAX for ") + sunshine_application_path);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace nvprefs
|
||||
@ -1,36 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "undo_data.h"
|
||||
|
||||
namespace nvprefs {
|
||||
|
||||
class driver_settings_t {
|
||||
public:
|
||||
~driver_settings_t();
|
||||
|
||||
bool
|
||||
init();
|
||||
|
||||
void
|
||||
destroy();
|
||||
|
||||
bool
|
||||
load_settings();
|
||||
|
||||
bool
|
||||
save_settings();
|
||||
|
||||
bool
|
||||
restore_global_profile_to_undo(const undo_data_t &undo_data);
|
||||
|
||||
bool
|
||||
check_and_modify_global_profile(std::optional<undo_data_t> &undo_data);
|
||||
|
||||
bool
|
||||
check_and_modify_application_profile(bool &modified);
|
||||
|
||||
private:
|
||||
NvDRSSessionHandle session_handle = 0;
|
||||
};
|
||||
|
||||
} // namespace nvprefs
|
||||
@ -1,127 +0,0 @@
|
||||
#include "nvprefs_common.h"
|
||||
|
||||
#include <nvapi_interface.h>
|
||||
|
||||
namespace {
|
||||
|
||||
std::map<const char *, void *> interfaces;
|
||||
HMODULE dll = NULL;
|
||||
|
||||
template <typename Func, typename... Args>
|
||||
NvAPI_Status
|
||||
call_interface(const char *name, Args... args) {
|
||||
auto func = (Func *) interfaces[name];
|
||||
|
||||
if (!func) {
|
||||
return interfaces.empty() ? NVAPI_API_NOT_INITIALIZED : NVAPI_NOT_SUPPORTED;
|
||||
}
|
||||
|
||||
return func(args...);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
#undef NVAPI_INTERFACE
|
||||
#define NVAPI_INTERFACE NvAPI_Status __cdecl
|
||||
|
||||
extern void *__cdecl nvapi_QueryInterface(NvU32 id);
|
||||
|
||||
NVAPI_INTERFACE
|
||||
NvAPI_Initialize() {
|
||||
if (dll) return NVAPI_OK;
|
||||
|
||||
#ifdef _WIN64
|
||||
auto dll_name = "nvapi64.dll";
|
||||
#else
|
||||
auto dll_name = "nvapi.dll";
|
||||
#endif
|
||||
|
||||
if ((dll = LoadLibraryEx(dll_name, NULL, LOAD_LIBRARY_SEARCH_SYSTEM32))) {
|
||||
if (auto query_interface = (decltype(nvapi_QueryInterface) *) GetProcAddress(dll, "nvapi_QueryInterface")) {
|
||||
for (const auto &item : nvapi_interface_table) {
|
||||
interfaces[item.func] = query_interface(item.id);
|
||||
}
|
||||
return NVAPI_OK;
|
||||
}
|
||||
}
|
||||
|
||||
NvAPI_Unload();
|
||||
return NVAPI_LIBRARY_NOT_FOUND;
|
||||
}
|
||||
|
||||
NVAPI_INTERFACE
|
||||
NvAPI_Unload() {
|
||||
if (dll) {
|
||||
interfaces.clear();
|
||||
FreeLibrary(dll);
|
||||
dll = NULL;
|
||||
}
|
||||
return NVAPI_OK;
|
||||
}
|
||||
|
||||
NVAPI_INTERFACE
|
||||
NvAPI_GetErrorMessage(NvAPI_Status nr, NvAPI_ShortString szDesc) {
|
||||
return call_interface<decltype(NvAPI_GetErrorMessage)>("NvAPI_GetErrorMessage", nr, szDesc);
|
||||
}
|
||||
|
||||
// This is only a subset of NvAPI_DRS_* functions, more can be added if needed
|
||||
|
||||
NVAPI_INTERFACE
|
||||
NvAPI_DRS_CreateSession(NvDRSSessionHandle *phSession) {
|
||||
return call_interface<decltype(NvAPI_DRS_CreateSession)>("NvAPI_DRS_CreateSession", phSession);
|
||||
}
|
||||
|
||||
NVAPI_INTERFACE
|
||||
NvAPI_DRS_DestroySession(NvDRSSessionHandle hSession) {
|
||||
return call_interface<decltype(NvAPI_DRS_DestroySession)>("NvAPI_DRS_DestroySession", hSession);
|
||||
}
|
||||
|
||||
NVAPI_INTERFACE
|
||||
NvAPI_DRS_LoadSettings(NvDRSSessionHandle hSession) {
|
||||
return call_interface<decltype(NvAPI_DRS_LoadSettings)>("NvAPI_DRS_LoadSettings", hSession);
|
||||
}
|
||||
|
||||
NVAPI_INTERFACE
|
||||
NvAPI_DRS_SaveSettings(NvDRSSessionHandle hSession) {
|
||||
return call_interface<decltype(NvAPI_DRS_SaveSettings)>("NvAPI_DRS_SaveSettings", hSession);
|
||||
}
|
||||
|
||||
NVAPI_INTERFACE
|
||||
NvAPI_DRS_CreateProfile(NvDRSSessionHandle hSession, NVDRS_PROFILE *pProfileInfo, NvDRSProfileHandle *phProfile) {
|
||||
return call_interface<decltype(NvAPI_DRS_CreateProfile)>("NvAPI_DRS_CreateProfile", hSession, pProfileInfo, phProfile);
|
||||
}
|
||||
|
||||
NVAPI_INTERFACE
|
||||
NvAPI_DRS_FindProfileByName(NvDRSSessionHandle hSession, NvAPI_UnicodeString profileName, NvDRSProfileHandle *phProfile) {
|
||||
return call_interface<decltype(NvAPI_DRS_FindProfileByName)>("NvAPI_DRS_FindProfileByName", hSession, profileName, phProfile);
|
||||
}
|
||||
|
||||
NVAPI_INTERFACE
|
||||
NvAPI_DRS_CreateApplication(NvDRSSessionHandle hSession, NvDRSProfileHandle hProfile, NVDRS_APPLICATION *pApplication) {
|
||||
return call_interface<decltype(NvAPI_DRS_CreateApplication)>("NvAPI_DRS_CreateApplication", hSession, hProfile, pApplication);
|
||||
}
|
||||
|
||||
NVAPI_INTERFACE
|
||||
NvAPI_DRS_GetApplicationInfo(NvDRSSessionHandle hSession, NvDRSProfileHandle hProfile, NvAPI_UnicodeString appName, NVDRS_APPLICATION *pApplication) {
|
||||
return call_interface<decltype(NvAPI_DRS_GetApplicationInfo)>("NvAPI_DRS_GetApplicationInfo", hSession, hProfile, appName, pApplication);
|
||||
}
|
||||
|
||||
NVAPI_INTERFACE
|
||||
NvAPI_DRS_SetSetting(NvDRSSessionHandle hSession, NvDRSProfileHandle hProfile, NVDRS_SETTING *pSetting) {
|
||||
return call_interface<decltype(NvAPI_DRS_SetSetting)>("NvAPI_DRS_SetSetting", hSession, hProfile, pSetting);
|
||||
}
|
||||
|
||||
NVAPI_INTERFACE
|
||||
NvAPI_DRS_GetSetting(NvDRSSessionHandle hSession, NvDRSProfileHandle hProfile, NvU32 settingId, NVDRS_SETTING *pSetting) {
|
||||
return call_interface<decltype(NvAPI_DRS_GetSetting)>("NvAPI_DRS_GetSetting", hSession, hProfile, settingId, pSetting);
|
||||
}
|
||||
|
||||
NVAPI_INTERFACE
|
||||
NvAPI_DRS_DeleteProfileSetting(NvDRSSessionHandle hSession, NvDRSProfileHandle hProfile, NvU32 settingId) {
|
||||
return call_interface<decltype(NvAPI_DRS_DeleteProfileSetting)>("NvAPI_DRS_DeleteProfileSetting", hSession, hProfile, settingId);
|
||||
}
|
||||
|
||||
NVAPI_INTERFACE
|
||||
NvAPI_DRS_GetBaseProfile(NvDRSSessionHandle hSession, NvDRSProfileHandle *phProfile) {
|
||||
return call_interface<decltype(NvAPI_DRS_GetBaseProfile)>("NvAPI_DRS_GetBaseProfile", hSession, phProfile);
|
||||
}
|
||||
@ -1,25 +0,0 @@
|
||||
#include "nvprefs_common.h"
|
||||
|
||||
namespace nvprefs {
|
||||
|
||||
void
|
||||
info_message(const std::wstring &message) {
|
||||
BOOST_LOG(info) << "nvprefs: " << message;
|
||||
}
|
||||
|
||||
void
|
||||
info_message(const std::string &message) {
|
||||
BOOST_LOG(info) << "nvprefs: " << message;
|
||||
}
|
||||
|
||||
void
|
||||
error_message(const std::wstring &message) {
|
||||
BOOST_LOG(error) << "nvprefs: " << message;
|
||||
}
|
||||
|
||||
void
|
||||
error_message(const std::string &message) {
|
||||
BOOST_LOG(error) << "nvprefs: " << message;
|
||||
}
|
||||
|
||||
} // namespace nvprefs
|
||||
@ -1,70 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
// sunshine utility header for generic smart pointers
|
||||
#include "src/utility.h"
|
||||
|
||||
// sunshine boost::log severity levels
|
||||
#include "src/main.h"
|
||||
|
||||
// standard library headers
|
||||
#include <filesystem>
|
||||
#include <iostream>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
#include <vector>
|
||||
|
||||
// winapi headers
|
||||
// disable clang-format header reordering
|
||||
// clang-format off
|
||||
#include <windows.h>
|
||||
#include <aclapi.h>
|
||||
// clang-format on
|
||||
|
||||
// nvapi headers
|
||||
// disable clang-format header reordering
|
||||
// clang-format off
|
||||
#include <nvapi.h>
|
||||
#include <NvApiDriverSettings.h>
|
||||
// clang-format on
|
||||
|
||||
// boost headers
|
||||
#include <boost/json.hpp>
|
||||
|
||||
namespace nvprefs {
|
||||
|
||||
struct safe_handle: public util::safe_ptr_v2<void, BOOL, CloseHandle> {
|
||||
using util::safe_ptr_v2<void, BOOL, CloseHandle>::safe_ptr_v2;
|
||||
explicit operator bool() const {
|
||||
auto handle = get();
|
||||
return handle != NULL && handle != INVALID_HANDLE_VALUE;
|
||||
}
|
||||
};
|
||||
|
||||
struct safe_hlocal_deleter {
|
||||
void
|
||||
operator()(void *p) {
|
||||
LocalFree(p);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
using safe_hlocal = util::uniq_ptr<std::remove_pointer_t<T>, safe_hlocal_deleter>;
|
||||
|
||||
using safe_sid = util::safe_ptr_v2<void, PVOID, FreeSid>;
|
||||
|
||||
void
|
||||
info_message(const std::wstring &message);
|
||||
|
||||
void
|
||||
info_message(const std::string &message);
|
||||
|
||||
void
|
||||
error_message(const std::wstring &message);
|
||||
|
||||
void
|
||||
error_message(const std::string &message);
|
||||
|
||||
} // namespace nvprefs
|
||||
@ -1,225 +0,0 @@
|
||||
#include "nvprefs_common.h"
|
||||
|
||||
#include "nvprefs_interface.h"
|
||||
|
||||
#include "driver_settings.h"
|
||||
#include "undo_data.h"
|
||||
#include "undo_file.h"
|
||||
|
||||
namespace {
|
||||
|
||||
const auto sunshine_program_data_folder = "Sunshine";
|
||||
const auto nvprefs_undo_file_name = "nvprefs_undo.json";
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace nvprefs {
|
||||
|
||||
struct nvprefs_interface::impl {
|
||||
bool loaded = false;
|
||||
driver_settings_t driver_settings;
|
||||
std::filesystem::path undo_folder_path;
|
||||
std::filesystem::path undo_file_path;
|
||||
std::optional<undo_data_t> undo_data;
|
||||
std::optional<undo_file_t> undo_file;
|
||||
};
|
||||
|
||||
nvprefs_interface::nvprefs_interface():
|
||||
pimpl(new impl()) {
|
||||
}
|
||||
|
||||
nvprefs_interface::~nvprefs_interface() {
|
||||
if (owning_undo_file() && load()) {
|
||||
restore_global_profile();
|
||||
}
|
||||
unload();
|
||||
}
|
||||
|
||||
bool
|
||||
nvprefs_interface::load() {
|
||||
if (!pimpl->loaded) {
|
||||
// Check %ProgramData% variable, need it for storing undo file
|
||||
wchar_t program_data_env[MAX_PATH];
|
||||
auto get_env_result = GetEnvironmentVariableW(L"ProgramData", program_data_env, MAX_PATH);
|
||||
if (get_env_result == 0 || get_env_result >= MAX_PATH || !std::filesystem::is_directory(program_data_env)) {
|
||||
error_message("Missing or malformed %ProgramData% environment variable");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Prepare undo file path variables
|
||||
pimpl->undo_folder_path = std::filesystem::path(program_data_env) / sunshine_program_data_folder;
|
||||
pimpl->undo_file_path = pimpl->undo_folder_path / nvprefs_undo_file_name;
|
||||
|
||||
// Dynamically load nvapi library and load driver settings
|
||||
pimpl->loaded = pimpl->driver_settings.init();
|
||||
}
|
||||
|
||||
return pimpl->loaded;
|
||||
}
|
||||
|
||||
void
|
||||
nvprefs_interface::unload() {
|
||||
if (pimpl->loaded) {
|
||||
// Unload dynamically loaded nvapi library
|
||||
pimpl->driver_settings.destroy();
|
||||
pimpl->loaded = false;
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
nvprefs_interface::restore_from_and_delete_undo_file_if_exists() {
|
||||
if (!pimpl->loaded) return false;
|
||||
|
||||
// Check for undo file from previous improper termination
|
||||
bool access_denied = false;
|
||||
if (auto undo_file = undo_file_t::open_existing_file(pimpl->undo_file_path, access_denied)) {
|
||||
// Try to restore from the undo file
|
||||
info_message("Opened undo file from previous improper termination");
|
||||
if (auto undo_data = undo_file->read_undo_data()) {
|
||||
if (pimpl->driver_settings.restore_global_profile_to_undo(*undo_data) && pimpl->driver_settings.save_settings()) {
|
||||
info_message("Restored global profile settings from undo file - deleting the file");
|
||||
}
|
||||
else {
|
||||
error_message("Failed to restore global profile settings from undo file, deleting the file anyway");
|
||||
}
|
||||
}
|
||||
else {
|
||||
error_message("Coulnd't read undo file, deleting the file anyway");
|
||||
}
|
||||
|
||||
if (!undo_file->delete_file()) {
|
||||
error_message("Couldn't delete undo file");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if (access_denied) {
|
||||
error_message("Couldn't open undo file from previous improper termination, or confirm that there's no such file");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
nvprefs_interface::modify_application_profile() {
|
||||
if (!pimpl->loaded) return false;
|
||||
|
||||
// Modify and save sunshine.exe application profile settings, if needed
|
||||
bool modified = false;
|
||||
if (!pimpl->driver_settings.check_and_modify_application_profile(modified)) {
|
||||
error_message("Failed to modify application profile settings");
|
||||
return false;
|
||||
}
|
||||
else if (modified) {
|
||||
if (pimpl->driver_settings.save_settings()) {
|
||||
info_message("Modified application profile settings");
|
||||
}
|
||||
else {
|
||||
error_message("Couldn't save application profile settings");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else {
|
||||
info_message("No need to modify application profile settings");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
nvprefs_interface::modify_global_profile() {
|
||||
if (!pimpl->loaded) return false;
|
||||
|
||||
// Modify but not save global profile settings, if needed
|
||||
std::optional<undo_data_t> undo_data;
|
||||
if (!pimpl->driver_settings.check_and_modify_global_profile(undo_data)) {
|
||||
error_message("Couldn't modify global profile settings");
|
||||
return false;
|
||||
}
|
||||
else if (!undo_data) {
|
||||
info_message("No need to modify global profile settings");
|
||||
return true;
|
||||
}
|
||||
|
||||
auto make_undo_and_commit = [&]() -> bool {
|
||||
// Create and lock undo file if it hasn't been done yet
|
||||
if (!pimpl->undo_file) {
|
||||
// Prepare Sunshine folder in ProgramData if it doesn't exist
|
||||
if (!CreateDirectoryW(pimpl->undo_folder_path.c_str(), nullptr) && GetLastError() != ERROR_ALREADY_EXISTS) {
|
||||
error_message("Couldn't create undo folder");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Create undo file to handle improper termination of nvprefs.exe
|
||||
pimpl->undo_file = undo_file_t::create_new_file(pimpl->undo_file_path);
|
||||
if (!pimpl->undo_file) {
|
||||
error_message("Couldn't create undo file");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
assert(undo_data);
|
||||
if (pimpl->undo_data) {
|
||||
// Merge undo data if settings has been modified externally since our last modification
|
||||
pimpl->undo_data->merge(*undo_data);
|
||||
}
|
||||
else {
|
||||
pimpl->undo_data = undo_data;
|
||||
}
|
||||
|
||||
// Write undo data to undo file
|
||||
if (!pimpl->undo_file->write_undo_data(*pimpl->undo_data)) {
|
||||
error_message("Couldn't write to undo file - deleting the file");
|
||||
if (!pimpl->undo_file->delete_file()) {
|
||||
error_message("Couldn't delete undo file");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Save global profile settings
|
||||
if (!pimpl->driver_settings.save_settings()) {
|
||||
error_message("Couldn't save global profile settings");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
if (!make_undo_and_commit()) {
|
||||
// Revert settings modifications
|
||||
pimpl->driver_settings.load_settings();
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
nvprefs_interface::owning_undo_file() {
|
||||
return pimpl->undo_file.has_value();
|
||||
}
|
||||
|
||||
bool
|
||||
nvprefs_interface::restore_global_profile() {
|
||||
if (!pimpl->loaded || !pimpl->undo_data || !pimpl->undo_file) return false;
|
||||
|
||||
// Restore global profile settings with undo data
|
||||
if (pimpl->driver_settings.restore_global_profile_to_undo(*pimpl->undo_data) &&
|
||||
pimpl->driver_settings.save_settings()) {
|
||||
// Global profile settings sucessfully restored, can delete undo file
|
||||
if (!pimpl->undo_file->delete_file()) {
|
||||
error_message("Couldn't delete undo file");
|
||||
return false;
|
||||
}
|
||||
pimpl->undo_data = std::nullopt;
|
||||
pimpl->undo_file = std::nullopt;
|
||||
}
|
||||
else {
|
||||
error_message("Couldn't restore global profile settings");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace nvprefs
|
||||
@ -1,36 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
namespace nvprefs {
|
||||
|
||||
class nvprefs_interface {
|
||||
public:
|
||||
nvprefs_interface();
|
||||
~nvprefs_interface();
|
||||
|
||||
bool
|
||||
load();
|
||||
|
||||
void
|
||||
unload();
|
||||
|
||||
bool
|
||||
restore_from_and_delete_undo_file_if_exists();
|
||||
|
||||
bool
|
||||
modify_application_profile();
|
||||
|
||||
bool
|
||||
modify_global_profile();
|
||||
|
||||
bool
|
||||
owning_undo_file();
|
||||
|
||||
bool
|
||||
restore_global_profile();
|
||||
|
||||
private:
|
||||
struct impl;
|
||||
std::unique_ptr<impl> pimpl;
|
||||
};
|
||||
|
||||
} // namespace nvprefs
|
||||
@ -1,71 +0,0 @@
|
||||
#include "nvprefs_common.h"
|
||||
|
||||
#include "undo_data.h"
|
||||
|
||||
namespace {
|
||||
|
||||
const auto opengl_swapchain_our_value_key = "/opengl_swapchain/our_value";
|
||||
const auto opengl_swapchain_undo_value_key = "/opengl_swapchain/undo_value";
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace nvprefs {
|
||||
|
||||
void
|
||||
undo_data_t::set_opengl_swapchain(uint32_t our_value, std::optional<uint32_t> undo_value) {
|
||||
data.set_at_pointer(opengl_swapchain_our_value_key, our_value);
|
||||
if (undo_value) {
|
||||
data.set_at_pointer(opengl_swapchain_undo_value_key, *undo_value);
|
||||
}
|
||||
else {
|
||||
data.set_at_pointer(opengl_swapchain_undo_value_key, nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
std::tuple<bool, uint32_t, std::optional<uint32_t>>
|
||||
undo_data_t::get_opengl_swapchain() const {
|
||||
auto get_value = [this](const auto &key) -> std::tuple<bool, std::optional<uint32_t>> {
|
||||
try {
|
||||
auto value = data.at_pointer(key);
|
||||
if (value.is_null()) {
|
||||
return { true, std::nullopt };
|
||||
}
|
||||
else if (value.is_number()) {
|
||||
return { true, value.template to_number<uint32_t>() };
|
||||
}
|
||||
}
|
||||
catch (...) {
|
||||
}
|
||||
error_message(std::string("Couldn't find ") + key + " element");
|
||||
return { false, std::nullopt };
|
||||
};
|
||||
|
||||
auto [our_value_present, our_value] = get_value(opengl_swapchain_our_value_key);
|
||||
auto [undo_value_present, undo_value] = get_value(opengl_swapchain_undo_value_key);
|
||||
|
||||
if (!our_value_present || !undo_value_present || !our_value) {
|
||||
return { false, 0, std::nullopt };
|
||||
}
|
||||
|
||||
return { true, *our_value, undo_value };
|
||||
}
|
||||
|
||||
std::string
|
||||
undo_data_t::write() const {
|
||||
return boost::json::serialize(data);
|
||||
}
|
||||
|
||||
void
|
||||
undo_data_t::read(const std::vector<char> &buffer) {
|
||||
data = boost::json::parse(std::string_view(buffer.data(), buffer.size()));
|
||||
}
|
||||
|
||||
void
|
||||
undo_data_t::merge(const undo_data_t &newer_data) {
|
||||
auto [opengl_swapchain_saved, opengl_swapchain_our_value, opengl_swapchain_undo_value] = newer_data.get_opengl_swapchain();
|
||||
if (opengl_swapchain_saved) {
|
||||
set_opengl_swapchain(opengl_swapchain_our_value, opengl_swapchain_undo_value);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace nvprefs
|
||||
@ -1,32 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
namespace nvprefs {
|
||||
|
||||
class undo_data_t {
|
||||
public:
|
||||
void
|
||||
set_opengl_swapchain(uint32_t our_value, std::optional<uint32_t> undo_value);
|
||||
|
||||
std::tuple<bool, uint32_t, std::optional<uint32_t>>
|
||||
get_opengl_swapchain() const;
|
||||
|
||||
void
|
||||
write(std::ostream &stream) const;
|
||||
|
||||
std::string
|
||||
write() const;
|
||||
|
||||
void
|
||||
read(std::istream &stream);
|
||||
|
||||
void
|
||||
read(const std::vector<char> &buffer);
|
||||
|
||||
void
|
||||
merge(const undo_data_t &newer_data);
|
||||
|
||||
private:
|
||||
boost::json::value data;
|
||||
};
|
||||
|
||||
} // namespace nvprefs
|
||||
@ -1,154 +0,0 @@
|
||||
#include "nvprefs_common.h"
|
||||
|
||||
#include "undo_file.h"
|
||||
|
||||
namespace {
|
||||
|
||||
using namespace nvprefs;
|
||||
|
||||
DWORD
|
||||
relax_permissions(HANDLE file_handle) {
|
||||
PACL old_dacl = nullptr;
|
||||
|
||||
safe_hlocal<PSECURITY_DESCRIPTOR> sd;
|
||||
DWORD status = GetSecurityInfo(file_handle, SE_FILE_OBJECT, DACL_SECURITY_INFORMATION, nullptr, nullptr, &old_dacl, nullptr, &sd);
|
||||
if (status != ERROR_SUCCESS) return status;
|
||||
|
||||
safe_sid users_sid;
|
||||
SID_IDENTIFIER_AUTHORITY nt_authorithy = SECURITY_NT_AUTHORITY;
|
||||
if (!AllocateAndInitializeSid(&nt_authorithy, 2, SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_USERS, 0, 0, 0, 0, 0, 0, &users_sid)) {
|
||||
return GetLastError();
|
||||
}
|
||||
|
||||
EXPLICIT_ACCESS ea = {};
|
||||
ea.grfAccessPermissions = GENERIC_READ | GENERIC_WRITE | DELETE;
|
||||
ea.grfAccessMode = GRANT_ACCESS;
|
||||
ea.grfInheritance = NO_INHERITANCE;
|
||||
ea.Trustee.TrusteeForm = TRUSTEE_IS_SID;
|
||||
ea.Trustee.ptstrName = (LPTSTR) users_sid.get();
|
||||
|
||||
safe_hlocal<PACL> new_dacl;
|
||||
status = SetEntriesInAcl(1, &ea, old_dacl, &new_dacl);
|
||||
if (status != ERROR_SUCCESS) return status;
|
||||
|
||||
status = SetSecurityInfo(file_handle, SE_FILE_OBJECT, DACL_SECURITY_INFORMATION, nullptr, nullptr, new_dacl.get(), nullptr);
|
||||
if (status != ERROR_SUCCESS) return status;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace nvprefs {
|
||||
|
||||
std::optional<undo_file_t>
|
||||
undo_file_t::open_existing_file(std::filesystem::path file_path, bool &access_denied) {
|
||||
undo_file_t file;
|
||||
file.file_handle.reset(CreateFileW(file_path.c_str(), GENERIC_READ | DELETE, 0, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL));
|
||||
if (file.file_handle) {
|
||||
access_denied = false;
|
||||
return file;
|
||||
}
|
||||
else {
|
||||
auto last_error = GetLastError();
|
||||
access_denied = (last_error != ERROR_FILE_NOT_FOUND && last_error != ERROR_PATH_NOT_FOUND);
|
||||
return std::nullopt;
|
||||
}
|
||||
}
|
||||
|
||||
std::optional<undo_file_t>
|
||||
undo_file_t::create_new_file(std::filesystem::path file_path) {
|
||||
undo_file_t file;
|
||||
file.file_handle.reset(CreateFileW(file_path.c_str(), GENERIC_WRITE | STANDARD_RIGHTS_ALL, 0, nullptr, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL));
|
||||
|
||||
if (file.file_handle) {
|
||||
// give GENERIC_READ, GENERIC_WRITE and DELETE permissions to Users group
|
||||
if (relax_permissions(file.file_handle.get()) != 0) {
|
||||
error_message("Failed to relax permissions on undo file");
|
||||
}
|
||||
return file;
|
||||
}
|
||||
else {
|
||||
return std::nullopt;
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
undo_file_t::delete_file() {
|
||||
if (!file_handle) return false;
|
||||
|
||||
FILE_DISPOSITION_INFO delete_file_info = { TRUE };
|
||||
if (SetFileInformationByHandle(file_handle.get(), FileDispositionInfo, &delete_file_info, sizeof(delete_file_info))) {
|
||||
file_handle.reset();
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
undo_file_t::write_undo_data(const undo_data_t &undo_data) {
|
||||
if (!file_handle) return false;
|
||||
|
||||
std::string buffer;
|
||||
try {
|
||||
buffer = undo_data.write();
|
||||
}
|
||||
catch (...) {
|
||||
error_message("Couldn't serialize undo data");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!SetFilePointerEx(file_handle.get(), {}, nullptr, FILE_BEGIN) || !SetEndOfFile(file_handle.get())) {
|
||||
error_message("Couldn't clear undo file");
|
||||
return false;
|
||||
}
|
||||
|
||||
DWORD bytes_written = 0;
|
||||
if (!WriteFile(file_handle.get(), buffer.data(), buffer.size(), &bytes_written, nullptr) || bytes_written != buffer.size()) {
|
||||
error_message("Couldn't write undo file");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!FlushFileBuffers(file_handle.get())) {
|
||||
error_message("Failed to flush undo file");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
std::optional<undo_data_t>
|
||||
undo_file_t::read_undo_data() {
|
||||
if (!file_handle) return std::nullopt;
|
||||
|
||||
LARGE_INTEGER file_size;
|
||||
if (!GetFileSizeEx(file_handle.get(), &file_size)) {
|
||||
error_message("Couldn't get undo file size");
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
if ((size_t) file_size.QuadPart > 1024) {
|
||||
error_message("Undo file size is unexpectedly large, aborting");
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::vector<char> buffer(file_size.QuadPart);
|
||||
DWORD bytes_read = 0;
|
||||
if (!ReadFile(file_handle.get(), buffer.data(), buffer.size(), &bytes_read, nullptr) || bytes_read != buffer.size()) {
|
||||
error_message("Couldn't read undo file");
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
undo_data_t undo_data;
|
||||
try {
|
||||
undo_data.read(buffer);
|
||||
}
|
||||
catch (...) {
|
||||
error_message("Couldn't parse undo file");
|
||||
return std::nullopt;
|
||||
}
|
||||
return undo_data;
|
||||
}
|
||||
|
||||
} // namespace nvprefs
|
||||
@ -1,29 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "undo_data.h"
|
||||
|
||||
namespace nvprefs {
|
||||
|
||||
class undo_file_t {
|
||||
public:
|
||||
static std::optional<undo_file_t>
|
||||
open_existing_file(std::filesystem::path file_path, bool &access_denied);
|
||||
|
||||
static std::optional<undo_file_t>
|
||||
create_new_file(std::filesystem::path file_path);
|
||||
|
||||
bool
|
||||
delete_file();
|
||||
|
||||
bool
|
||||
write_undo_data(const undo_data_t &undo_data);
|
||||
|
||||
std::optional<undo_data_t>
|
||||
read_undo_data();
|
||||
|
||||
private:
|
||||
undo_file_t() = default;
|
||||
safe_handle file_handle;
|
||||
};
|
||||
|
||||
} // namespace nvprefs
|
||||
@ -1,14 +0,0 @@
|
||||
/**
|
||||
* @file src/stat_trackers.cpp
|
||||
* @brief todo
|
||||
*/
|
||||
#include "stat_trackers.h"
|
||||
|
||||
namespace stat_trackers {
|
||||
|
||||
boost::format
|
||||
one_digit_after_decimal() {
|
||||
return boost::format("%1$.1f");
|
||||
}
|
||||
|
||||
} // namespace stat_trackers
|
||||
@ -1,50 +0,0 @@
|
||||
/**
|
||||
* @file src/stat_trackers.h
|
||||
* @brief todo
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <chrono>
|
||||
#include <functional>
|
||||
#include <limits>
|
||||
|
||||
#include <boost/format.hpp>
|
||||
|
||||
namespace stat_trackers {
|
||||
|
||||
boost::format
|
||||
one_digit_after_decimal();
|
||||
|
||||
template <typename T>
|
||||
class min_max_avg_tracker {
|
||||
public:
|
||||
using callback_function = std::function<void(T stat_min, T stat_max, double stat_avg)>;
|
||||
|
||||
void
|
||||
collect_and_callback_on_interval(T stat, const callback_function &callback, std::chrono::seconds interval_in_seconds) {
|
||||
if (std::chrono::steady_clock::now() > data.last_callback_time + interval_in_seconds) {
|
||||
callback(data.stat_min, data.stat_max, data.stat_total / data.calls);
|
||||
data = {};
|
||||
}
|
||||
data.stat_min = std::min(data.stat_min, stat);
|
||||
data.stat_max = std::max(data.stat_max, stat);
|
||||
data.stat_total += stat;
|
||||
data.calls += 1;
|
||||
}
|
||||
|
||||
void
|
||||
reset() {
|
||||
data = {};
|
||||
}
|
||||
|
||||
private:
|
||||
struct {
|
||||
std::chrono::steady_clock::steady_clock::time_point last_callback_time = std::chrono::steady_clock::now();
|
||||
T stat_min = std::numeric_limits<T>::max();
|
||||
T stat_max = std::numeric_limits<T>::min();
|
||||
double stat_total = 0;
|
||||
uint32_t calls = 0;
|
||||
} data;
|
||||
};
|
||||
|
||||
} // namespace stat_trackers
|
||||
138
src/video.cpp
138
src/video.cpp
@ -124,7 +124,7 @@ namespace video {
|
||||
|
||||
int ret = sws_scale(sws.get(), (std::uint8_t *const *) &img.data, linesizes, 0, img.height, data, sw_frame->linesize);
|
||||
if (ret <= 0) {
|
||||
BOOST_LOG(error) << "Couldn't convert image to required format and/or size"sv;
|
||||
// BOOST_LOG(error) << "Couldn't convert image to required format and/or size"sv;
|
||||
|
||||
return -1;
|
||||
}
|
||||
@ -135,7 +135,7 @@ namespace video {
|
||||
auto status = av_hwframe_transfer_data(frame, sw_frame.get(), 0);
|
||||
if (status < 0) {
|
||||
char string[AV_ERROR_MAX_STRING_SIZE];
|
||||
BOOST_LOG(error) << "Failed to transfer image data to hardware frame: "sv << av_make_error_string(string, AV_ERROR_MAX_STRING_SIZE, status);
|
||||
// BOOST_LOG(error) << "Failed to transfer image data to hardware frame: "sv << av_make_error_string(string, AV_ERROR_MAX_STRING_SIZE, status);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
@ -203,7 +203,7 @@ namespace video {
|
||||
auto data = img.begin();
|
||||
int ret = sws_scale(sws.get(), (std::uint8_t *const *) &data, linesizes, 0, height, frame->data, frame->linesize);
|
||||
if (ret <= 0) {
|
||||
BOOST_LOG(error) << "Couldn't convert image to required format and/or size"sv;
|
||||
// BOOST_LOG(error) << "Couldn't convert image to required format and/or size"sv;
|
||||
|
||||
return -1;
|
||||
}
|
||||
@ -454,7 +454,7 @@ namespace video {
|
||||
|
||||
void
|
||||
invalidate_ref_frames(int64_t first_frame, int64_t last_frame) override {
|
||||
BOOST_LOG(error) << "Encoder doesn't support reference frame invalidation";
|
||||
// BOOST_LOG(error) << "Encoder doesn't support reference frame invalidation";
|
||||
request_idr_frame();
|
||||
}
|
||||
|
||||
@ -1264,7 +1264,7 @@ namespace video {
|
||||
case platf::capture_e::interrupted:
|
||||
return;
|
||||
default:
|
||||
BOOST_LOG(error) << "Unrecognized capture status ["sv << (int) status << ']';
|
||||
// BOOST_LOG(error) << "Unrecognized capture status ["sv << (int) status << ']';
|
||||
return;
|
||||
}
|
||||
}
|
||||
@ -1284,7 +1284,7 @@ namespace video {
|
||||
auto ret = avcodec_send_frame(ctx.get(), frame);
|
||||
if (ret < 0) {
|
||||
char err_str[AV_ERROR_MAX_STRING_SIZE] { 0 };
|
||||
BOOST_LOG(error) << "Could not send a frame for encoding: "sv << av_make_error_string(err_str, AV_ERROR_MAX_STRING_SIZE, ret);
|
||||
// BOOST_LOG(error) << "Could not send a frame for encoding: "sv << av_make_error_string(err_str, AV_ERROR_MAX_STRING_SIZE, ret);
|
||||
|
||||
return -1;
|
||||
}
|
||||
@ -1302,7 +1302,7 @@ namespace video {
|
||||
}
|
||||
|
||||
if ((frame->flags & AV_FRAME_FLAG_KEY) && !(av_packet->flags & AV_PKT_FLAG_KEY)) {
|
||||
BOOST_LOG(error) << "Encoder did not produce IDR frame when requested!"sv;
|
||||
// BOOST_LOG(error) << "Encoder did not produce IDR frame when requested!"sv;
|
||||
}
|
||||
|
||||
if (session.inject) {
|
||||
@ -1347,12 +1347,12 @@ namespace video {
|
||||
encode_nvenc(int64_t frame_nr, nvenc_encode_session_t &session, safe::mail_raw_t::queue_t<packet_t> &packets, void *channel_data, std::optional<std::chrono::steady_clock::time_point> frame_timestamp) {
|
||||
auto encoded_frame = session.encode_frame(frame_nr);
|
||||
if (encoded_frame.data.empty()) {
|
||||
BOOST_LOG(error) << "NvENC returned empty packet";
|
||||
// BOOST_LOG(error) << "NvENC returned empty packet";
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (frame_nr != encoded_frame.frame_index) {
|
||||
BOOST_LOG(error) << "NvENC frame index mismatch " << frame_nr << " " << encoded_frame.frame_index;
|
||||
// BOOST_LOG(error) << "NvENC frame index mismatch " << frame_nr << " " << encoded_frame.frame_index;
|
||||
}
|
||||
|
||||
auto packet = std::make_unique<packet_raw_generic>(std::move(encoded_frame.data), encoded_frame.frame_index, encoded_frame.idr);
|
||||
@ -1390,18 +1390,18 @@ namespace video {
|
||||
config.videoFormat == 1 ? encoder.hevc :
|
||||
encoder.av1;
|
||||
if (!video_format[encoder_t::PASSED] || !disp->is_codec_supported(video_format.name, config)) {
|
||||
BOOST_LOG(error) << encoder.name << ": "sv << video_format.name << " mode not supported"sv;
|
||||
// BOOST_LOG(error) << encoder.name << ": "sv << video_format.name << " mode not supported"sv;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (config.dynamicRange && !video_format[encoder_t::DYNAMIC_RANGE]) {
|
||||
BOOST_LOG(error) << video_format.name << ": dynamic range not supported"sv;
|
||||
// BOOST_LOG(error) << video_format.name << ": dynamic range not supported"sv;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto codec = avcodec_find_encoder_by_name(video_format.name.c_str());
|
||||
if (!codec) {
|
||||
BOOST_LOG(error) << "Couldn't open ["sv << video_format.name << ']';
|
||||
// BOOST_LOG(error) << "Couldn't open ["sv << video_format.name << ']';
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
@ -1443,7 +1443,7 @@ namespace video {
|
||||
ctx->refs = config.numRefFrames;
|
||||
}
|
||||
else {
|
||||
BOOST_LOG(warning) << "Client requested reference frame limit, but encoder doesn't support it!"sv;
|
||||
// BOOST_LOG(warning) << "Client requested reference frame limit, but encoder doesn't support it!"sv;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1487,7 +1487,7 @@ namespace video {
|
||||
auto err = av_hwdevice_ctx_create_derived(&derived_context, platform_formats->avcodec_derived_dev_type, encoding_stream_context.get(), 0);
|
||||
if (err) {
|
||||
char err_str[AV_ERROR_MAX_STRING_SIZE] { 0 };
|
||||
BOOST_LOG(error) << "Failed to derive device context: "sv << av_make_error_string(err_str, AV_ERROR_MAX_STRING_SIZE, err);
|
||||
// BOOST_LOG(error) << "Failed to derive device context: "sv << av_make_error_string(err_str, AV_ERROR_MAX_STRING_SIZE, err);
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
@ -1589,16 +1589,16 @@ namespace video {
|
||||
handle_option(*video_format.qp);
|
||||
}
|
||||
else {
|
||||
BOOST_LOG(error) << "Couldn't set video quality: encoder "sv << encoder.name << " doesn't support qp"sv;
|
||||
// BOOST_LOG(error) << "Couldn't set video quality: encoder "sv << encoder.name << " doesn't support qp"sv;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (auto status = avcodec_open2(ctx.get(), codec, &options)) {
|
||||
char err_str[AV_ERROR_MAX_STRING_SIZE] { 0 };
|
||||
BOOST_LOG(error)
|
||||
<< "Could not open codec ["sv
|
||||
<< video_format.name << "]: "sv
|
||||
<< av_make_error_string(err_str, AV_ERROR_MAX_STRING_SIZE, status);
|
||||
// BOOST_LOG(error)
|
||||
// << "Could not open codec ["sv
|
||||
// << video_format.name << "]: "sv
|
||||
// << av_make_error_string(err_str, AV_ERROR_MAX_STRING_SIZE, status);
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
@ -1643,7 +1643,7 @@ namespace video {
|
||||
}
|
||||
}
|
||||
else {
|
||||
BOOST_LOG(error) << "Couldn't get display hdr metadata when colorspace selection indicates it should have one";
|
||||
// BOOST_LOG(error) << "Couldn't get display hdr metadata when colorspace selection indicates it should have one";
|
||||
}
|
||||
}
|
||||
|
||||
@ -1693,23 +1693,23 @@ namespace video {
|
||||
if (encode_device) {
|
||||
switch (encode_device->colorspace.colorspace) {
|
||||
case colorspace_e::bt2020:
|
||||
BOOST_LOG(info) << "HDR color coding [Rec. 2020 + SMPTE 2084 PQ]"sv;
|
||||
// BOOST_LOG(info) << "HDR color coding [Rec. 2020 + SMPTE 2084 PQ]"sv;
|
||||
break;
|
||||
|
||||
case colorspace_e::rec601:
|
||||
BOOST_LOG(info) << "SDR color coding [Rec. 601]"sv;
|
||||
// BOOST_LOG(info) << "SDR color coding [Rec. 601]"sv;
|
||||
break;
|
||||
|
||||
case colorspace_e::rec709:
|
||||
BOOST_LOG(info) << "SDR color coding [Rec. 709]"sv;
|
||||
// BOOST_LOG(info) << "SDR color coding [Rec. 709]"sv;
|
||||
break;
|
||||
|
||||
case colorspace_e::bt2020sdr:
|
||||
BOOST_LOG(info) << "SDR color coding [Rec. 2020]"sv;
|
||||
// BOOST_LOG(info) << "SDR color coding [Rec. 2020]"sv;
|
||||
break;
|
||||
}
|
||||
BOOST_LOG(info) << "Color depth: " << encode_device->colorspace.bit_depth << "-bit";
|
||||
BOOST_LOG(info) << "Color range: ["sv << (encode_device->colorspace.full_range ? "JPEG"sv : "MPEG"sv) << ']';
|
||||
// BOOST_LOG(info) << "Color depth: " << encode_device->colorspace.bit_depth << "-bit";
|
||||
// BOOST_LOG(info) << "Color range: ["sv << (encode_device->colorspace.full_range ? "JPEG"sv : "MPEG"sv) << ']';
|
||||
}
|
||||
|
||||
if (dynamic_cast<platf::avcodec_encode_device_t *>(encode_device.get())) {
|
||||
@ -1785,7 +1785,7 @@ namespace video {
|
||||
if (auto img = images->pop(100ms)) {
|
||||
frame_timestamp = img->frame_timestamp;
|
||||
if (session->convert(*img)) {
|
||||
BOOST_LOG(error) << "Could not convert image"sv;
|
||||
// BOOST_LOG(error) << "Could not convert image"sv;
|
||||
return;
|
||||
}
|
||||
}
|
||||
@ -1795,7 +1795,7 @@ namespace video {
|
||||
}
|
||||
|
||||
if (encode(frame_nr++, *session, packets, channel_data, frame_timestamp)) {
|
||||
BOOST_LOG(error) << "Could not encode video packet"sv;
|
||||
// BOOST_LOG(error) << "Could not encode video packet"sv;
|
||||
return;
|
||||
}
|
||||
|
||||
@ -1843,7 +1843,7 @@ namespace video {
|
||||
hdr_info->enabled = true;
|
||||
}
|
||||
else {
|
||||
BOOST_LOG(error) << "Couldn't get display hdr metadata when colorspace selection indicates it should have one";
|
||||
// BOOST_LOG(error) << "Couldn't get display hdr metadata when colorspace selection indicates it should have one";
|
||||
}
|
||||
}
|
||||
ctx.hdr_events->raise(std::move(hdr_info));
|
||||
@ -1855,7 +1855,7 @@ namespace video {
|
||||
|
||||
// Load the initial image to prepare for encoding
|
||||
if (session->convert(img)) {
|
||||
BOOST_LOG(error) << "Could not convert initial image"sv;
|
||||
// BOOST_LOG(error) << "Could not convert initial image"sv;
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
@ -1963,13 +1963,13 @@ namespace video {
|
||||
}
|
||||
|
||||
if (ctx->idr_events->peek()) {
|
||||
BOOST_LOG(info) << "IDR frame generated"sv;
|
||||
// BOOST_LOG(info) << "IDR frame generated"sv;
|
||||
pos->session->request_idr_frame();
|
||||
ctx->idr_events->pop();
|
||||
}
|
||||
|
||||
if (frame_captured && pos->session->convert(*img)) {
|
||||
BOOST_LOG(error) << "Could not convert image"sv;
|
||||
// BOOST_LOG(error) << "Could not convert image"sv;
|
||||
ctx->shutdown_event->raise(true);
|
||||
|
||||
continue;
|
||||
@ -1981,7 +1981,7 @@ namespace video {
|
||||
}
|
||||
|
||||
if (encode(ctx->frame_nr++, *pos->session, ctx->packets, ctx->channel_data, frame_timestamp)) {
|
||||
BOOST_LOG(error) << "Could not encode video packet"sv;
|
||||
// BOOST_LOG(error) << "Could not encode video packet"sv;
|
||||
ctx->shutdown_event->raise(true);
|
||||
|
||||
continue;
|
||||
@ -2112,7 +2112,7 @@ namespace video {
|
||||
hdr_info->enabled = true;
|
||||
}
|
||||
else {
|
||||
BOOST_LOG(error) << "Couldn't get display hdr metadata when colorspace selection indicates it should have one";
|
||||
// BOOST_LOG(error) << "Couldn't get display hdr metadata when colorspace selection indicates it should have one";
|
||||
}
|
||||
}
|
||||
hdr_event->raise(std::move(hdr_info));
|
||||
@ -2197,7 +2197,7 @@ namespace video {
|
||||
|
||||
auto packet = packets->pop();
|
||||
if (!packet->is_idr()) {
|
||||
BOOST_LOG(error) << "First packet type is not an IDR frame"sv;
|
||||
// BOOST_LOG(error) << "First packet type is not an IDR frame"sv;
|
||||
|
||||
return -1;
|
||||
}
|
||||
@ -2224,9 +2224,9 @@ namespace video {
|
||||
validate_encoder(encoder_t &encoder, bool expect_failure) {
|
||||
std::shared_ptr<platf::display_t> disp;
|
||||
|
||||
BOOST_LOG(info) << "Trying encoder ["sv << encoder.name << ']';
|
||||
// BOOST_LOG(info) << "Trying encoder ["sv << encoder.name << ']';
|
||||
auto fg = util::fail_guard([&]() {
|
||||
BOOST_LOG(info) << "Encoder ["sv << encoder.name << "] failed"sv;
|
||||
// BOOST_LOG(info) << "Encoder ["sv << encoder.name << "] failed"sv;
|
||||
});
|
||||
|
||||
auto test_hevc = active_hevc_mode >= 2 || (active_hevc_mode == 0 && !(encoder.flags & H264_ONLY));
|
||||
@ -2247,7 +2247,7 @@ namespace video {
|
||||
}
|
||||
if (!disp->is_codec_supported(encoder.h264.name, config_autoselect)) {
|
||||
fg.disable();
|
||||
BOOST_LOG(info) << "Encoder ["sv << encoder.name << "] is not supported on this GPU"sv;
|
||||
// BOOST_LOG(info) << "Encoder ["sv << encoder.name << "] is not supported on this GPU"sv;
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -2304,7 +2304,7 @@ namespace video {
|
||||
encoder.hevc[encoder_t::PASSED] = max_ref_frames_hevc >= 0 || autoselect_hevc >= 0;
|
||||
}
|
||||
else {
|
||||
BOOST_LOG(info) << "Encoder ["sv << encoder.hevc.name << "] is not supported on this GPU"sv;
|
||||
// BOOST_LOG(info) << "Encoder ["sv << encoder.hevc.name << "] is not supported on this GPU"sv;
|
||||
encoder.hevc.capabilities.reset();
|
||||
}
|
||||
}
|
||||
@ -2336,7 +2336,7 @@ namespace video {
|
||||
encoder.av1[encoder_t::PASSED] = max_ref_frames_av1 >= 0 || autoselect_av1 >= 0;
|
||||
}
|
||||
else {
|
||||
BOOST_LOG(info) << "Encoder ["sv << encoder.av1.name << "] is not supported on this GPU"sv;
|
||||
// BOOST_LOG(info) << "Encoder ["sv << encoder.av1.name << "] is not supported on this GPU"sv;
|
||||
encoder.av1.capabilities.reset();
|
||||
}
|
||||
}
|
||||
@ -2374,10 +2374,10 @@ namespace video {
|
||||
encoder.hevc[encoder_t::VUI_PARAMETERS] = encoder.hevc[encoder_t::VUI_PARAMETERS] && !config::sunshine.flags[config::flag::FORCE_VIDEO_HEADER_REPLACE];
|
||||
|
||||
if (!encoder.h264[encoder_t::VUI_PARAMETERS]) {
|
||||
BOOST_LOG(warning) << encoder.name << ": h264 missing sps->vui parameters"sv;
|
||||
// BOOST_LOG(warning) << encoder.name << ": h264 missing sps->vui parameters"sv;
|
||||
}
|
||||
if (encoder.hevc[encoder_t::PASSED] && !encoder.hevc[encoder_t::VUI_PARAMETERS]) {
|
||||
BOOST_LOG(warning) << encoder.name << ": hevc missing sps->vui parameters"sv;
|
||||
// BOOST_LOG(warning) << encoder.name << ": hevc missing sps->vui parameters"sv;
|
||||
}
|
||||
|
||||
fg.disable();
|
||||
@ -2405,20 +2405,20 @@ namespace video {
|
||||
auto adjust_encoder_constraints = [&](encoder_t *encoder) {
|
||||
// If we can't satisfy both the encoder and codec requirement, prefer the encoder over codec support
|
||||
if (active_hevc_mode == 3 && !encoder->hevc[encoder_t::DYNAMIC_RANGE]) {
|
||||
BOOST_LOG(warning) << "Encoder ["sv << encoder->name << "] does not support HEVC Main10 on this system"sv;
|
||||
// BOOST_LOG(warning) << "Encoder ["sv << encoder->name << "] does not support HEVC Main10 on this system"sv;
|
||||
active_hevc_mode = 0;
|
||||
}
|
||||
else if (active_hevc_mode == 2 && !encoder->hevc[encoder_t::PASSED]) {
|
||||
BOOST_LOG(warning) << "Encoder ["sv << encoder->name << "] does not support HEVC on this system"sv;
|
||||
// BOOST_LOG(warning) << "Encoder ["sv << encoder->name << "] does not support HEVC on this system"sv;
|
||||
active_hevc_mode = 0;
|
||||
}
|
||||
|
||||
if (active_av1_mode == 3 && !encoder->av1[encoder_t::DYNAMIC_RANGE]) {
|
||||
BOOST_LOG(warning) << "Encoder ["sv << encoder->name << "] does not support AV1 Main10 on this system"sv;
|
||||
// BOOST_LOG(warning) << "Encoder ["sv << encoder->name << "] does not support AV1 Main10 on this system"sv;
|
||||
active_av1_mode = 0;
|
||||
}
|
||||
else if (active_av1_mode == 2 && !encoder->av1[encoder_t::PASSED]) {
|
||||
BOOST_LOG(warning) << "Encoder ["sv << encoder->name << "] does not support AV1 on this system"sv;
|
||||
// BOOST_LOG(warning) << "Encoder ["sv << encoder->name << "] does not support AV1 on this system"sv;
|
||||
active_av1_mode = 0;
|
||||
}
|
||||
};
|
||||
@ -2446,11 +2446,11 @@ namespace video {
|
||||
});
|
||||
|
||||
if (chosen_encoder == nullptr) {
|
||||
BOOST_LOG(error) << "Couldn't find any working encoder matching ["sv << config::video.encoder << ']';
|
||||
// BOOST_LOG(error) << "Couldn't find any working encoder matching ["sv << config::video.encoder << ']';
|
||||
}
|
||||
}
|
||||
|
||||
BOOST_LOG(info) << "// Testing for available encoders, this may generate errors. You can safely ignore those errors. //"sv;
|
||||
// BOOST_LOG(info) << "// Testing for available encoders, this may generate errors. You can safely ignore those errors. //"sv;
|
||||
|
||||
// If we haven't found an encoder yet, but we want one with specific codec support, search for that now.
|
||||
if (chosen_encoder == nullptr && (active_hevc_mode >= 2 || active_av1_mode >= 2)) {
|
||||
@ -2482,7 +2482,7 @@ namespace video {
|
||||
});
|
||||
|
||||
if (chosen_encoder == nullptr) {
|
||||
BOOST_LOG(error) << "Couldn't find any working encoder that meets HEVC/AV1 requirements"sv;
|
||||
// BOOST_LOG(error) << "Couldn't find any working encoder that meets HEVC/AV1 requirements"sv;
|
||||
}
|
||||
}
|
||||
|
||||
@ -2509,46 +2509,46 @@ namespace video {
|
||||
}
|
||||
|
||||
if (chosen_encoder == nullptr) {
|
||||
BOOST_LOG(fatal) << "Couldn't find any working encoder"sv;
|
||||
// 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);
|
||||
// BOOST_LOG(info);
|
||||
// BOOST_LOG(info) << "// Ignore any errors mentioned above, they are not relevant. //"sv;
|
||||
// BOOST_LOG(info);
|
||||
|
||||
auto &encoder = *chosen_encoder;
|
||||
|
||||
last_encoder_probe_supported_ref_frames_invalidation = (encoder.flags & REF_FRAMES_INVALIDATION);
|
||||
|
||||
BOOST_LOG(debug) << "------ h264 ------"sv;
|
||||
// BOOST_LOG(debug) << "------ h264 ------"sv;
|
||||
for (int x = 0; x < encoder_t::MAX_FLAGS; ++x) {
|
||||
auto flag = (encoder_t::flag_e) x;
|
||||
BOOST_LOG(debug) << encoder_t::from_flag(flag) << (encoder.h264[flag] ? ": supported"sv : ": unsupported"sv);
|
||||
// BOOST_LOG(debug) << encoder_t::from_flag(flag) << (encoder.h264[flag] ? ": supported"sv : ": unsupported"sv);
|
||||
}
|
||||
BOOST_LOG(debug) << "-------------------"sv;
|
||||
BOOST_LOG(info) << "Found H.264 encoder: "sv << encoder.h264.name << " ["sv << encoder.name << ']';
|
||||
// BOOST_LOG(debug) << "-------------------"sv;
|
||||
// BOOST_LOG(info) << "Found H.264 encoder: "sv << encoder.h264.name << " ["sv << encoder.name << ']';
|
||||
|
||||
if (encoder.hevc[encoder_t::PASSED]) {
|
||||
BOOST_LOG(debug) << "------ hevc ------"sv;
|
||||
// BOOST_LOG(debug) << "------ hevc ------"sv;
|
||||
for (int x = 0; x < encoder_t::MAX_FLAGS; ++x) {
|
||||
auto flag = (encoder_t::flag_e) x;
|
||||
BOOST_LOG(debug) << encoder_t::from_flag(flag) << (encoder.hevc[flag] ? ": supported"sv : ": unsupported"sv);
|
||||
// BOOST_LOG(debug) << encoder_t::from_flag(flag) << (encoder.hevc[flag] ? ": supported"sv : ": unsupported"sv);
|
||||
}
|
||||
BOOST_LOG(debug) << "-------------------"sv;
|
||||
// BOOST_LOG(debug) << "-------------------"sv;
|
||||
|
||||
BOOST_LOG(info) << "Found HEVC encoder: "sv << encoder.hevc.name << " ["sv << encoder.name << ']';
|
||||
// BOOST_LOG(info) << "Found HEVC encoder: "sv << encoder.hevc.name << " ["sv << encoder.name << ']';
|
||||
}
|
||||
|
||||
if (encoder.av1[encoder_t::PASSED]) {
|
||||
BOOST_LOG(debug) << "------ av1 ------"sv;
|
||||
// BOOST_LOG(debug) << "------ av1 ------"sv;
|
||||
for (int x = 0; x < encoder_t::MAX_FLAGS; ++x) {
|
||||
auto flag = (encoder_t::flag_e) x;
|
||||
BOOST_LOG(debug) << encoder_t::from_flag(flag) << (encoder.av1[flag] ? ": supported"sv : ": unsupported"sv);
|
||||
// BOOST_LOG(debug) << encoder_t::from_flag(flag) << (encoder.av1[flag] ? ": supported"sv : ": unsupported"sv);
|
||||
}
|
||||
BOOST_LOG(debug) << "-------------------"sv;
|
||||
// BOOST_LOG(debug) << "-------------------"sv;
|
||||
|
||||
BOOST_LOG(info) << "Found AV1 encoder: "sv << encoder.av1.name << " ["sv << encoder.name << ']';
|
||||
// BOOST_LOG(info) << "Found AV1 encoder: "sv << encoder.av1.name << " ["sv << encoder.name << ']';
|
||||
}
|
||||
|
||||
if (active_hevc_mode == 0) {
|
||||
@ -2583,7 +2583,7 @@ namespace video {
|
||||
auto status = av_hwdevice_ctx_create(&hw_device_buf, AV_HWDEVICE_TYPE_VAAPI, render_device, nullptr, 0);
|
||||
if (status < 0) {
|
||||
char string[AV_ERROR_MAX_STRING_SIZE];
|
||||
BOOST_LOG(error) << "Failed to create a VAAPI device: "sv << av_make_error_string(string, AV_ERROR_MAX_STRING_SIZE, status);
|
||||
// BOOST_LOG(error) << "Failed to create a VAAPI device: "sv << av_make_error_string(string, AV_ERROR_MAX_STRING_SIZE, status);
|
||||
return -1;
|
||||
}
|
||||
|
||||
@ -2597,7 +2597,7 @@ namespace video {
|
||||
auto status = av_hwdevice_ctx_create(&hw_device_buf, AV_HWDEVICE_TYPE_CUDA, nullptr, nullptr, 1 /* AV_CUDA_USE_PRIMARY_CONTEXT */);
|
||||
if (status < 0) {
|
||||
char string[AV_ERROR_MAX_STRING_SIZE];
|
||||
BOOST_LOG(error) << "Failed to create a CUDA device: "sv << av_make_error_string(string, AV_ERROR_MAX_STRING_SIZE, status);
|
||||
// BOOST_LOG(error) << "Failed to create a CUDA device: "sv << av_make_error_string(string, AV_ERROR_MAX_STRING_SIZE, status);
|
||||
return -1;
|
||||
}
|
||||
|
||||
@ -2611,7 +2611,7 @@ namespace video {
|
||||
auto status = av_hwdevice_ctx_create(&hw_device_buf, AV_HWDEVICE_TYPE_VIDEOTOOLBOX, nullptr, nullptr, 0);
|
||||
if (status < 0) {
|
||||
char string[AV_ERROR_MAX_STRING_SIZE];
|
||||
BOOST_LOG(error) << "Failed to create a VideoToolbox device: "sv << av_make_error_string(string, AV_ERROR_MAX_STRING_SIZE, status);
|
||||
// BOOST_LOG(error) << "Failed to create a VideoToolbox device: "sv << av_make_error_string(string, AV_ERROR_MAX_STRING_SIZE, status);
|
||||
return -1;
|
||||
}
|
||||
|
||||
@ -2644,7 +2644,7 @@ namespace video {
|
||||
auto err = av_hwdevice_ctx_init(ctx_buf.get());
|
||||
if (err) {
|
||||
char err_str[AV_ERROR_MAX_STRING_SIZE] { 0 };
|
||||
BOOST_LOG(error) << "Failed to create FFMpeg hardware device context: "sv << av_make_error_string(err_str, AV_ERROR_MAX_STRING_SIZE, err);
|
||||
// BOOST_LOG(error) << "Failed to create FFMpeg hardware device context: "sv << av_make_error_string(err_str, AV_ERROR_MAX_STRING_SIZE, err);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
@ -42,7 +42,7 @@ namespace video {
|
||||
break;
|
||||
|
||||
default:
|
||||
BOOST_LOG(error) << "Unknown video colorspace in csc, falling back to Rec. 709";
|
||||
// BOOST_LOG(error) << "Unknown video colorspace in csc, falling back to Rec. 709";
|
||||
colorspace.colorspace = colorspace_e::rec709;
|
||||
break;
|
||||
}
|
||||
@ -60,13 +60,13 @@ namespace video {
|
||||
break;
|
||||
|
||||
default:
|
||||
BOOST_LOG(error) << "Unknown dynamicRange value, falling back to 10-bit color depth";
|
||||
// BOOST_LOG(error) << "Unknown dynamicRange value, falling back to 10-bit color depth";
|
||||
colorspace.bit_depth = 10;
|
||||
break;
|
||||
}
|
||||
|
||||
if (colorspace.colorspace == colorspace_e::bt2020sdr && colorspace.bit_depth != 10) {
|
||||
BOOST_LOG(error) << "BT.2020 SDR colorspace expects 10-bit color depth, falling back to Rec. 709";
|
||||
// BOOST_LOG(error) << "BT.2020 SDR colorspace expects 10-bit color depth, falling back to Rec. 709";
|
||||
colorspace.colorspace = colorspace_e::rec709;
|
||||
}
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user