From 41682a07cbd3f578e946eb89eb1d967ed6afe8d6 Mon Sep 17 00:00:00 2001 From: Hannes Mann Date: Sat, 28 Oct 2017 01:42:25 +0200 Subject: [PATCH] Add Quality of Service (QoS) support --- Source/Core/Common/CMakeLists.txt | 1 + Source/Core/Common/Common.vcxproj | 2 + Source/Core/Common/Common.vcxproj.filters | 2 + Source/Core/Common/QoSSession.cpp | 87 +++++++++++++++++++++ Source/Core/Common/QoSSession.h | 40 ++++++++++ Source/Core/Core/Config/NetplaySettings.cpp | 2 + Source/Core/Core/Config/NetplaySettings.h | 2 + Source/Core/Core/NetPlayClient.cpp | 13 +++ Source/Core/Core/NetPlayServer.cpp | 8 +- Source/Core/Core/NetPlayServer.h | 4 + 10 files changed, 160 insertions(+), 1 deletion(-) create mode 100644 Source/Core/Common/QoSSession.cpp create mode 100644 Source/Core/Common/QoSSession.h diff --git a/Source/Core/Common/CMakeLists.txt b/Source/Core/Common/CMakeLists.txt index c4f153569d..9a56d19bdc 100644 --- a/Source/Core/Common/CMakeLists.txt +++ b/Source/Core/Common/CMakeLists.txt @@ -29,6 +29,7 @@ set(SRCS PcapFile.cpp PerformanceCounter.cpp Profiler.cpp + QoSSession.cpp SDCardUtil.cpp SettingsHandler.cpp StringUtil.cpp diff --git a/Source/Core/Common/Common.vcxproj b/Source/Core/Common/Common.vcxproj index 0198b8f1cc..a518b531f1 100644 --- a/Source/Core/Common/Common.vcxproj +++ b/Source/Core/Common/Common.vcxproj @@ -137,6 +137,7 @@ + @@ -196,6 +197,7 @@ + diff --git a/Source/Core/Common/Common.vcxproj.filters b/Source/Core/Common/Common.vcxproj.filters index b5799a167c..50ea0f619d 100644 --- a/Source/Core/Common/Common.vcxproj.filters +++ b/Source/Core/Common/Common.vcxproj.filters @@ -58,6 +58,7 @@ + @@ -282,6 +283,7 @@ + diff --git a/Source/Core/Common/QoSSession.cpp b/Source/Core/Common/QoSSession.cpp new file mode 100644 index 0000000000..992d15d273 --- /dev/null +++ b/Source/Core/Common/QoSSession.cpp @@ -0,0 +1,87 @@ +// Copyright 2017 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#include "Common/QoSSession.h" +#include "Core/ConfigManager.h" + +#if defined(_WIN32) +#include +#pragma comment(lib, "qwave") +#endif + +namespace Common +{ +#if defined(_WIN32) +QoSSession::QoSSession(ENetPeer* peer, int tos_val) : m_peer(peer) +{ + QOS_VERSION ver = {1, 0}; + + if (!QOSCreateHandle(&ver, &m_qos_handle)) + return; + + sockaddr_in sin = {}; + + sin.sin_family = AF_INET; + sin.sin_port = ENET_HOST_TO_NET_16(peer->host->address.port); + sin.sin_addr.s_addr = peer->host->address.host; + + if (QOSAddSocketToFlow(m_qos_handle, peer->host->socket, reinterpret_cast(&sin), + QOSTrafficTypeControl, QOS_NON_ADAPTIVE_FLOW, &m_qos_flow_id)) + { + // We shift the complete ToS value by 3 to get rid of the 3 bit ECN field + DWORD dscp = static_cast(tos_val >> 3); + + // Sets DSCP to the same as Linux + // This will fail if we're not admin, but we ignore it + QOSSetFlow(m_qos_handle, m_qos_flow_id, QOSSetOutgoingDSCPValue, sizeof(DWORD), &dscp, 0, + nullptr); + + m_success = true; + } +} + +QoSSession::~QoSSession() +{ + if (m_qos_handle == nullptr) + return; + + if (m_qos_flow_id != 0) + QOSRemoveSocketFromFlow(m_qos_handle, m_peer->host->socket, m_qos_flow_id, 0); + + QOSCloseHandle(m_qos_handle); +} +#else +QoSSession::QoSSession(ENetPeer* peer, int tos_val) +{ +#if defined(__linux__) + constexpr int priority = 7; + setsockopt(peer->host->socket, SOL_SOCKET, SO_PRIORITY, &priority, sizeof(priority)); +#endif + + m_success = setsockopt(peer->host->socket, IPPROTO_IP, IP_TOS, &tos_val, sizeof(tos_val)) == 0; +} + +QoSSession::~QoSSession() = default; +#endif + +QoSSession& QoSSession::operator=(QoSSession&& session) +{ + if (this != &session) + { +#if defined(_WIN32) + m_qos_handle = session.m_qos_handle; + m_qos_flow_id = session.m_qos_flow_id; + m_peer = session.m_peer; + + session.m_qos_handle = nullptr; + session.m_qos_flow_id = 0; + session.m_peer = nullptr; +#endif + m_success = session.m_success; + session.m_success = false; + } + + return *this; +} +} // namespace Common diff --git a/Source/Core/Common/QoSSession.h b/Source/Core/Common/QoSSession.h new file mode 100644 index 0000000000..0f54e1d3d7 --- /dev/null +++ b/Source/Core/Common/QoSSession.h @@ -0,0 +1,40 @@ +// Copyright 2017 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#pragma once + +#include +#include + +namespace Common +{ +class QoSSession +{ +public: + // 1 0 1 1 1 0 0 0 + // DSCP ECN + static constexpr int ef_tos = 0b10111000; + + QoSSession() = default; + QoSSession(ENetPeer* peer, int tos_val = ef_tos); + + ~QoSSession(); + + QoSSession& operator=(const QoSSession&) = delete; + QoSSession(const QoSSession&) = delete; + + QoSSession& operator=(QoSSession&& session); + QoSSession(QoSSession&& session) { *this = std::move(session); } + bool Successful() const { return m_success; } +private: +#if defined(_WIN32) + void* m_qos_handle = nullptr; + unsigned long m_qos_flow_id = 0; + + ENetPeer* m_peer = nullptr; +#endif + + bool m_success = false; +}; +} // namespace Common diff --git a/Source/Core/Core/Config/NetplaySettings.cpp b/Source/Core/Core/Config/NetplaySettings.cpp index 3b0082329d..4336c95cef 100644 --- a/Source/Core/Core/Config/NetplaySettings.cpp +++ b/Source/Core/Core/Config/NetplaySettings.cpp @@ -33,4 +33,6 @@ const ConfigInfo NETPLAY_SELECTED_HOST_GAME{ {System::Main, "NetPlay", "SelectedHostGame"}, ""}; const ConfigInfo NETPLAY_USE_UPNP{{System::Main, "NetPlay", "UseUPNP"}, false}; +const ConfigInfo NETPLAY_ENABLE_QOS{{System::Main, "NetPlay", "EnableQoS"}, true}; + } // namespace Config diff --git a/Source/Core/Core/Config/NetplaySettings.h b/Source/Core/Core/Config/NetplaySettings.h index 1978fdeb84..71af0ff6a8 100644 --- a/Source/Core/Core/Config/NetplaySettings.h +++ b/Source/Core/Core/Config/NetplaySettings.h @@ -29,4 +29,6 @@ extern const ConfigInfo NETPLAY_NICKNAME; extern const ConfigInfo NETPLAY_SELECTED_HOST_GAME; extern const ConfigInfo NETPLAY_USE_UPNP; +extern const ConfigInfo NETPLAY_ENABLE_QOS; + } // namespace Config diff --git a/Source/Core/Core/NetPlayClient.cpp b/Source/Core/Core/NetPlayClient.cpp index 465da26370..6b62fec45a 100644 --- a/Source/Core/Core/NetPlayClient.cpp +++ b/Source/Core/Core/NetPlayClient.cpp @@ -20,9 +20,11 @@ #include "Common/ENetUtil.h" #include "Common/MD5.h" #include "Common/MsgHandler.h" +#include "Common/QoSSession.h" #include "Common/StringUtil.h" #include "Common/Timer.h" #include "Common/Version.h" +#include "Core/Config/NetplaySettings.h" #include "Core/ConfigManager.h" #include "Core/HW/EXI/EXI_DeviceIPL.h" #include "Core/HW/SI/SI.h" @@ -632,6 +634,17 @@ void NetPlayClient::SendAsync(sf::Packet&& packet) // called from ---NETPLAY--- thread void NetPlayClient::ThreadFunc() { + Common::QoSSession qos_session; + if (Config::Get(Config::NETPLAY_ENABLE_QOS)) + { + qos_session = Common::QoSSession(m_server); + + if (qos_session.Successful()) + m_dialog->AppendChat("Quality of Service (QoS) was successfully enabled."); + else + m_dialog->AppendChat("Quality of Service (QoS) couldn't be enabled."); + } + while (m_do_loop.IsSet()) { ENetEvent netEvent; diff --git a/Source/Core/Core/NetPlayServer.cpp b/Source/Core/Core/NetPlayServer.cpp index 595c246ec1..e41146a543 100644 --- a/Source/Core/Core/NetPlayServer.cpp +++ b/Source/Core/Core/NetPlayServer.cpp @@ -21,10 +21,12 @@ #include "Common/StringUtil.h" #include "Common/UPnP.h" #include "Common/Version.h" +#include "Core/Config/NetplaySettings.h" #include "Core/ConfigManager.h" #include "Core/HW/Sram.h" #include "Core/NetPlayClient.h" //for NetPlayUI #include "InputCommon/GCPadStatus.h" + #if !defined(_WIN32) #include #include @@ -273,6 +275,7 @@ unsigned int NetPlayServer::OnConnect(ENetPeer* socket) Client player; player.pid = pid; player.socket = socket; + rpac >> player.revision; rpac >> player.name; @@ -343,10 +346,13 @@ unsigned int NetPlayServer::OnConnect(ENetPeer* socket) Send(player.socket, spac); } + if (Config::Get(Config::NETPLAY_ENABLE_QOS)) + player.qos_session = Common::QoSSession(player.socket); + // add client to the player list { std::lock_guard lkp(m_crit.players); - m_players.emplace(*(PlayerId*)player.socket->data, player); + m_players.emplace(*(PlayerId*)player.socket->data, std::move(player)); UpdatePadMapping(); // sync pad mappings with everyone UpdateWiimoteMapping(); } diff --git a/Source/Core/Core/NetPlayServer.h b/Source/Core/Core/NetPlayServer.h index e67aaf1266..b0f52f8000 100644 --- a/Source/Core/Core/NetPlayServer.h +++ b/Source/Core/Core/NetPlayServer.h @@ -6,12 +6,14 @@ #include #include +#include #include #include #include #include #include #include +#include "Common/QoSSession.h" #include "Common/SPSCQueue.h" #include "Common/Timer.h" #include "Common/TraversalClient.h" @@ -71,6 +73,8 @@ private: u32 ping; u32 current_game; + Common::QoSSession qos_session; + bool operator==(const Client& other) const { return this == &other; } };