Merge branch 'RPCS3:master' into wiimote-to-guncon3

This commit is contained in:
Frog Business 2026-02-09 14:43:57 +03:00 committed by GitHub
commit 234a1283c5
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
74 changed files with 3589 additions and 501 deletions

View file

@ -134,7 +134,7 @@ jobs:
runs-on: macos-14
env:
CCACHE_DIR: /tmp/ccache_dir
QT_VER: '6.10.1'
QT_VER: '6.10.2'
QT_VER_MAIN: '6'
LLVM_COMPILER_VER: '21'
RELEASE_MESSAGE: ../GitHubReleaseMessage.txt
@ -213,9 +213,9 @@ jobs:
env:
COMPILER: msvc
QT_VER_MAIN: '6'
QT_VER: '6.10.1'
QT_VER: '6.10.2'
QT_VER_MSVC: 'msvc2022'
QT_DATE: '202511161843'
QT_DATE: '202601261212'
LLVM_VER: '19.1.7'
VULKAN_VER: '1.3.268.0'
VULKAN_SDK_SHA: '8459ef49bd06b697115ddd3d97c9aec729e849cd775f5be70897718a9b3b9db5'

@ -1 +1 @@
Subproject commit 4e3f57d50f552841550a36eabbb3fbcecacb7750
Subproject commit 02f2b4f4699f0ef9111a6534f093b53732df4452

View file

@ -20,26 +20,26 @@ The following tools are required to build RPCS3 on Windows 10 or later:
with standalone **CMake** tool.
- [Python 3.6+](https://www.python.org/downloads/) (add to PATH)
- [Qt 6.10.1](https://www.qt.io/download-qt-installer) In case you can't download from the official installer, you can use [Another Qt installer](https://github.com/miurahr/aqtinstall) (In that case you will need to manually add the "qtmultimedia" module when installing Qt)
- [Qt 6.10.2](https://www.qt.io/download-qt-installer) In case you can't download from the official installer, you can use [Another Qt installer](https://github.com/miurahr/aqtinstall) (In that case you will need to manually add the "qtmultimedia" module when installing Qt)
- [Vulkan SDK 1.3.268.0](https://vulkan.lunarg.com/sdk/home) (see "Install the SDK" [here](https://vulkan.lunarg.com/doc/sdk/latest/windows/getting_started.html)) for now future SDKs don't work. You need precisely 1.3.268.0.
The `sln` solution available only on **Visual Studio** is the preferred building solution. It easily allows to build the **RPCS3** application in `Release` and `Debug` mode.
In order to build **RPCS3** with the `sln` solution (with **Visual Studio**), **Qt** libs need to be detected. To detect the libs:
- add and set the `QTDIR` environment variable, e.g. `<QtInstallFolder>\6.10.1\msvc2022_64\`
- add and set the `QTDIR` environment variable, e.g. `<QtInstallFolder>\6.10.2\msvc2022_64\`
- or use the [Visual Studio Qt Plugin](https://marketplace.visualstudio.com/items?itemName=TheQtCompany.QtVisualStudioTools2022)
**NOTE:** If you have issues with the **Visual Studio Qt Plugin**, you may want to uninstall it and install the [Legacy Qt Plugin](https://marketplace.visualstudio.com/items?itemName=TheQtCompany.LEGACYQtVisualStudioTools2022) instead.
In order to build **RPCS3** with the `CMake` solution (with both **Visual Studio** and standalone **CMake** tool):
- add and set the `Qt6_ROOT` environment variable to the **Qt** libs path, e.g. `<QtInstallFolder>\6.10.1\msvc2022_64\`
- add and set the `Qt6_ROOT` environment variable to the **Qt** libs path, e.g. `<QtInstallFolder>\6.10.2\msvc2022_64\`
### Linux
These are the essentials tools to build RPCS3 on Linux. Some of them can be installed through your favorite package manager:
- Clang 17+ or GCC 13+
- [CMake 3.28.0+](https://www.cmake.org/download/)
- [Qt 6.10.1](https://www.qt.io/download-qt-installer)
- [Qt 6.10.2](https://www.qt.io/download-qt-installer)
- [Vulkan SDK 1.3.268.0](https://vulkan.lunarg.com/sdk/home) (See "Install the SDK" [here](https://vulkan.lunarg.com/doc/sdk/latest/linux/getting_started.html)) for now future SDKs don't work. You need precisely 1.3.268.0.
- [SDL3](https://github.com/libsdl-org/SDL/releases) (for the FAudio backend)
@ -123,7 +123,7 @@ Start **Visual Studio**, click on `Open a project or solution` and select the `r
##### Configuring the Qt Plugin (if used)
1) go to `Extensions->Qt VS Tools->Qt Versions`
2) add the path to your Qt installation with compiler e.g. `<QtInstallFolder>\6.10.1\msvc2022_64`, version will fill in automatically
2) add the path to your Qt installation with compiler e.g. `<QtInstallFolder>\6.10.2\msvc2022_64`, version will fill in automatically
3) go to `Extensions->Qt VS Tools->Options->Legacy Project Format`. (Only available in the **Legacy Qt Plugin**)
4) set `Build: Run pre-build setup` to `true`. (Only available in the **Legacy Qt Plugin**)

View file

@ -188,7 +188,7 @@ namespace fmt
result.append(separator);
}
return result.append(source.back());
return result.append(*end);
}
template <typename T>

View file

@ -2228,6 +2228,12 @@ error_code cellFontStatic()
return CELL_OK;
}
error_code cellFontsetUSleep()
{
cellFont.todo("cellFontsetUSleep()");
return CELL_OK;
}
DECLARE(ppu_module_manager::cellFont)("cellFont", []()
{
@ -2315,4 +2321,5 @@ DECLARE(ppu_module_manager::cellFont)("cellFont", []()
REG_FUNC(cellFont, cellFontGraphicsGetLineRGBA);
REG_FUNC(cellFont, cellFontControl);
REG_FUNC(cellFont, cellFontStatic);
REG_FUNC(cellFont, cellFontsetUSleep);
});

View file

@ -1034,6 +1034,18 @@ s32 cellFsUnregisterL10nCallbacks()
return CELL_OK;
}
s32 cellFsGetDirent()
{
cellFs.todo("cellFsGetDirent()");
return CELL_OK;
}
s32 cellFsGetDirentCount()
{
cellFs.todo("cellFsGetDirentCount()");
return CELL_OK;
}
DECLARE(ppu_module_manager::cellFs)("sys_fs", []()
{
REG_FUNC(sys_fs, cellFsAccess);
@ -1065,6 +1077,8 @@ DECLARE(ppu_module_manager::cellFs)("sys_fs", []()
REG_FUNC(sys_fs, cellFsGetBlockSize);
REG_FUNC(sys_fs, cellFsGetBlockSize2);
REG_FUNC(sys_fs, cellFsGetDirectoryEntries);
REG_FUNC(sys_fs, cellFsGetDirent);
REG_FUNC(sys_fs, cellFsGetDirentCount);
REG_FUNC(sys_fs, cellFsGetFreeSize);
REG_FUNC(sys_fs, cellFsGetPath);
REG_FUNC(sys_fs, cellFsLink);

View file

@ -2514,6 +2514,18 @@ error_code cellSaveDataUserFixedDelete(ppu_thread& ppu, u32 userId, PSetList set
return savedata_op(ppu, SAVEDATA_OP_FIXED_DELETE, 0, vm::null, 1, setList, setBuf, vm::null, funcFixed, vm::null, vm::null, container, 6, userdata, userId, funcDone);
}
error_code cellSaveDataGetEnableOverlay()
{
cellSaveData.todo("cellSaveDataGetEnableOverlay()");
// auto& manager = g_fxo->get<savedata_manager>();
// manager.enable_overlay;
// TODO
return CELL_OK;
}
void cellSaveDataEnableOverlay(s32 enable)
{
cellSaveData.notice("cellSaveDataEnableOverlay(enable=%d)", enable);
@ -2521,7 +2533,6 @@ void cellSaveDataEnableOverlay(s32 enable)
manager.enable_overlay = enable != 0;
}
// Functions (Extensions)
error_code cellSaveDataListDelete(ppu_thread& ppu, PSetList setList, PSetBuf setBuf, PFuncList funcList, PFuncDone funcDone, u32 container, vm::ptr<void> userdata)
{
@ -2678,6 +2689,7 @@ void cellSysutil_SaveData_init()
REG_VAR(cellSysutil, g_savedata_context).flag(MFF_HIDDEN);
// libsysutil functions:
REG_FUNC(cellSysutil, cellSaveDataGetEnableOverlay);
REG_FUNC(cellSysutil, cellSaveDataEnableOverlay);
REG_FUNC(cellSysutil, cellSaveDataDelete2);

View file

@ -1438,6 +1438,13 @@ s32 cellSpursInitializeWithAttribute2(ppu_thread& ppu, vm::ptr<CellSpurs> spurs,
attr->swlIsPreem);
}
// Initialise SPURS
s32 cellSpursInitializeForSpuSharing()
{
cellSpurs.todo("cellSpursInitializeForSpuSharing()");
return CELL_OK;
}
/// Initialise SPURS attribute
s32 _cellSpursAttributeInitialize(vm::ptr<CellSpursAttribute> attr, u32 revision, u32 sdkVersion, u32 nSpus, s32 spuPriority, s32 ppuPriority, b8 exitIfNoWork)
{
@ -5390,6 +5397,7 @@ DECLARE(ppu_module_manager::cellSpurs)("cellSpurs", [](ppu_static_module* _this)
REG_FUNC(cellSpurs, cellSpursInitialize);
REG_FUNC(cellSpurs, cellSpursInitializeWithAttribute);
REG_FUNC(cellSpurs, cellSpursInitializeWithAttribute2);
REG_FUNC(cellSpurs, cellSpursInitializeForSpuSharing);
REG_FUNC(cellSpurs, cellSpursFinalize);
REG_FUNC(cellSpurs, _cellSpursAttributeInitialize);
REG_FUNC(cellSpurs, cellSpursAttributeSetMemoryContainerForSpuThread);

View file

@ -406,6 +406,30 @@ error_code cellSysmoduleIsLoadedEx()
return CELL_OK;
}
error_code cellSysmoduleLoadModuleFile()
{
UNIMPLEMENTED_FUNC(cellSysmodule);
return CELL_OK;
}
error_code cellSysmoduleUnloadModuleFile()
{
UNIMPLEMENTED_FUNC(cellSysmodule);
return CELL_OK;
}
error_code cellSysmoduleSetDebugmode()
{
UNIMPLEMENTED_FUNC(cellSysmodule);
return CELL_OK;
}
error_code cellSysmoduleSetInternalmode()
{
UNIMPLEMENTED_FUNC(cellSysmodule);
return CELL_OK;
}
DECLARE(ppu_module_manager::cellSysmodule)("cellSysmodule", []()
{
REG_FUNC(cellSysmodule, cellSysmoduleInitialize);
@ -421,4 +445,8 @@ DECLARE(ppu_module_manager::cellSysmodule)("cellSysmodule", []()
REG_FUNC(cellSysmodule, cellSysmoduleUnloadModuleEx);
REG_FUNC(cellSysmodule, cellSysmoduleLoadModuleEx);
REG_FUNC(cellSysmodule, cellSysmoduleIsLoadedEx);
REG_FUNC(cellSysmodule, cellSysmoduleLoadModuleFile);
REG_FUNC(cellSysmodule, cellSysmoduleUnloadModuleFile);
REG_FUNC(cellSysmodule, cellSysmoduleSetDebugmode);
REG_FUNC(cellSysmodule, cellSysmoduleSetInternalmode);
});

View file

@ -1118,10 +1118,35 @@ error_code sceNpBasicSetPresenceDetails2(vm::cptr<SceNpBasicPresenceDetails2> pr
return CELL_OK;
}
error_code sceNpBasicSendMessage(vm::cptr<SceNpId> to, vm::cptr<void> data, u32 size)
{
sceNp.warning("sceNpBasicSendMessage(to=*0x%x, data=*0x%x, size=%d)", to, data, size);
u64 sys_time_get_system_time();
error_code acquire_time_slot(u64* time_array, usz array_size, u64 slot_duration)
{
static shared_mutex mutex;
std::lock_guard lock(mutex);
const u64 current_time = sys_time_get_system_time();
for (usz index = 0; index < array_size; index++)
{
if (time_array[index] == 0)
{
time_array[index] = current_time;
return CELL_OK;
}
if (current_time > (time_array[index] + slot_duration))
{
time_array[index] = current_time;
return CELL_OK;
}
}
return SCE_NP_BASIC_ERROR_BUSY;
}
error_code _sceNpBasicSendMessage(vm::cptr<SceNpId> to, vm::cptr<void> data, u32 size, bool rate_limited)
{
auto& nph = g_fxo->get<named_thread<np::np_handler>>();
if (!nph.is_NP_init)
@ -1144,6 +1169,23 @@ error_code sceNpBasicSendMessage(vm::cptr<SceNpId> to, vm::cptr<void> data, u32
return SCE_NP_BASIC_ERROR_EXCEEDS_MAX;
}
if (rate_limited)
{
struct sceNpBasicSendMessage_time_slots
{
sceNpBasicSendMessage_time_slots() = default;
sceNpBasicSendMessage_time_slots(sceNpBasicSendMessage_time_slots&&) = delete;
std::array<u64, 20> data{};
};
auto& time_slots = g_fxo->get<sceNpBasicSendMessage_time_slots>();
if (auto error = acquire_time_slot(time_slots.data.data(), time_slots.data.size(), 60000000); error != CELL_OK)
{
return error;
}
}
if (nph.get_psn_status() != SCE_NP_MANAGER_STATUS_ONLINE)
{
return not_an_error(SCE_NP_BASIC_ERROR_NOT_CONNECTED);
@ -1164,6 +1206,19 @@ error_code sceNpBasicSendMessage(vm::cptr<SceNpId> to, vm::cptr<void> data, u32
return CELL_OK;
}
error_code sceNpBasicSendMessage(vm::cptr<SceNpId> to, vm::cptr<void> data, u32 size)
{
sceNp.warning("sceNpBasicSendMessage(to=*0x%x, data=*0x%x, size=%d)", to, data, size);
return _sceNpBasicSendMessage(to, data, size, false);
}
// This function is sceNpBasicSendMessage + a rate limiter that will return SCE_NP_BASIC_ERROR_BUSY if it too many messages have been sent
error_code sceNpBasicLimited_0xEB42E2E6(vm::cptr<SceNpId> to, vm::cptr<void> data, u32 size)
{
sceNp.warning("sceNpBasicLimited_0xEB42E2E6(to=*0x%x, data=*0x%x, size=%d)", to, data, size);
return _sceNpBasicSendMessage(to, data, size, true);
}
error_code sceNpBasicSendMessageGui(ppu_thread& ppu, vm::cptr<SceNpBasicMessageDetails> msg, sys_memory_container_t containerId)
{
sceNp.warning("sceNpBasicSendMessageGui(msg=*0x%x, containerId=%d)", msg, containerId);
@ -7289,6 +7344,11 @@ s32 _Z32_sce_np_sysutil_cxml_prepare_docPN16sysutil_cxmlutil11FixedMemoryERN4cxm
return CELL_OK;
}
DECLARE(ppu_module_manager::sceNpBasicLimited)
("sceNpBasicLimited", []() {
ppu_module_manager::register_static_function<&sceNpBasicLimited_0xEB42E2E6>("sceNpBasicLimited", ppu_select_name("sceNpBasicLimited", "sceNpBasicLimited_0xEB42E2E6"), BIND_FUNC_WITH_BLR(sceNpBasicLimited_0xEB42E2E6, "sceNpBasicLimited"), 0xEB42E2E6);
});
DECLARE(ppu_module_manager::sceNp)
("sceNp", []() {
REG_FUNC(sceNp, sceNpInit);

View file

@ -616,9 +616,9 @@ error_code sceNpMatching2CreateServerContext(
return CELL_OK;
}
error_code sceNpMatching2GetMemoryInfo(vm::ptr<SceNpMatching2MemoryInfo> memInfo) // TODO
error_code sceNpMatching2GetMemoryInfo(vm::ptr<SceNpMatching2MemoryInfo> memInfo)
{
sceNp2.todo("sceNpMatching2GetMemoryInfo(memInfo=*0x%x)", memInfo);
sceNp2.warning("sceNpMatching2GetMemoryInfo(memInfo=*0x%x)", memInfo);
auto& nph = g_fxo->get<named_thread<np::np_handler>>();
@ -627,6 +627,14 @@ error_code sceNpMatching2GetMemoryInfo(vm::ptr<SceNpMatching2MemoryInfo> memInfo
return SCE_NP_MATCHING2_ERROR_NOT_INITIALIZED;
}
if (!memInfo)
{
return SCE_NP_MATCHING2_ERROR_INVALID_ARGUMENT;
}
SceNpMatching2MemoryInfo mem_info = nph.get_memory_info();
*memInfo = mem_info;
return CELL_OK;
}
@ -790,7 +798,7 @@ error_code sceNpMatching2JoinLobby(
error_code sceNpMatching2GetRoomMemberDataExternalList(SceNpMatching2ContextId ctxId, vm::cptr<SceNpMatching2GetRoomMemberDataExternalListRequest> reqParam,
vm::cptr<SceNpMatching2RequestOptParam> optParam, vm::ptr<SceNpMatching2RequestId> assignedReqId)
{
sceNp2.todo("sceNpMatching2GetRoomMemberDataExternalList(ctxId=%d, reqParam=*0x%x, optParam=*0x%x, assignedReqId=*0x%x)", ctxId, reqParam, optParam, assignedReqId);
sceNp2.warning("sceNpMatching2GetRoomMemberDataExternalList(ctxId=%d, reqParam=*0x%x, optParam=*0x%x, assignedReqId=*0x%x)", ctxId, reqParam, optParam, assignedReqId);
auto& nph = g_fxo->get<named_thread<np::np_handler>>();
if (auto res = generic_match2_error_check(nph, ctxId, reqParam); res != CELL_OK)
@ -798,6 +806,13 @@ error_code sceNpMatching2GetRoomMemberDataExternalList(SceNpMatching2ContextId c
return res;
}
const u32 request_id = nph.get_room_member_data_external_list(ctxId, optParam, reqParam.get_ptr());
if (assignedReqId)
{
*assignedReqId = request_id;
}
return CELL_OK;
}
@ -1056,7 +1071,7 @@ error_code sceNpMatching2GetRoomMemberDataInternalLocal(SceNpMatching2ContextId
error_code sceNpMatching2GetCbQueueInfo(SceNpMatching2ContextId ctxId, vm::ptr<SceNpMatching2CbQueueInfo> queueInfo)
{
sceNp2.todo("sceNpMatching2GetCbQueueInfo(ctxId=%d, queueInfo=*0x%x)", ctxId, queueInfo);
sceNp2.warning("sceNpMatching2GetCbQueueInfo(ctxId=%d, queueInfo=*0x%x)", ctxId, queueInfo);
auto& nph = g_fxo->get<named_thread<np::np_handler>>();
@ -1065,6 +1080,17 @@ error_code sceNpMatching2GetCbQueueInfo(SceNpMatching2ContextId ctxId, vm::ptr<S
return SCE_NP_MATCHING2_ERROR_NOT_INITIALIZED;
}
if (!queueInfo)
{
return SCE_NP_MATCHING2_ERROR_INVALID_ARGUMENT;
}
*queueInfo = {};
// TODO: check the values returned on real hardware
queueInfo->requestCbQueueLen = 255;
queueInfo->sessionEventCbQueueLen = 255;
queueInfo->sessionMsgCbQueueLen = 255;
return CELL_OK;
}
@ -1326,7 +1352,7 @@ error_code sceNpMatching2CreateContext(
error_code sceNpMatching2GetSignalingOptParamLocal(SceNpMatching2ContextId ctxId, SceNpMatching2RoomId roomId, vm::ptr<SceNpMatching2SignalingOptParam> signalingOptParam)
{
sceNp2.todo("sceNpMatching2GetSignalingOptParamLocal(ctxId=%d, roomId=%d, signalingOptParam=*0x%x)", ctxId, roomId, signalingOptParam);
sceNp2.warning("sceNpMatching2GetSignalingOptParamLocal(ctxId=%d, roomId=%d, signalingOptParam=*0x%x)", ctxId, roomId, signalingOptParam);
auto& nph = g_fxo->get<named_thread<np::np_handler>>();
@ -1335,6 +1361,30 @@ error_code sceNpMatching2GetSignalingOptParamLocal(SceNpMatching2ContextId ctxId
return SCE_NP_MATCHING2_ERROR_NOT_INITIALIZED;
}
if (!ctxId)
{
return SCE_NP_MATCHING2_ERROR_INVALID_CONTEXT_ID;
}
if (!roomId)
{
return SCE_NP_MATCHING2_ERROR_INVALID_ROOM_ID;
}
if (!check_match2_context(ctxId))
{
return SCE_NP_MATCHING2_ERROR_CONTEXT_NOT_FOUND;
}
const auto [error, signaling_opt_param] = nph.local_get_signaling_opt_param(roomId);
if (error)
{
return error;
}
*signalingOptParam = *signaling_opt_param;
return CELL_OK;
}

View file

@ -1529,6 +1529,11 @@ error_code sceNpTrophyGetTrophyIcon(u32 context, u32 handle, s32 trophyId, vm::p
return CELL_OK;
}
error_code sceNpTrophyNetworkSync()
{
UNIMPLEMENTED_FUNC(sceNpTrophy);
return CELL_OK;
}
DECLARE(ppu_module_manager::sceNpTrophy)("sceNpTrophy", []()
{
@ -1553,4 +1558,5 @@ DECLARE(ppu_module_manager::sceNpTrophy)("sceNpTrophy", []()
REG_FUNC(sceNpTrophy, sceNpTrophyGetTrophyDetails);
REG_FUNC(sceNpTrophy, sceNpTrophyGetTrophyInfo);
REG_FUNC(sceNpTrophy, sceNpTrophyGetGameIcon);
REG_FUNC(sceNpTrophy, sceNpTrophyNetworkSync);
});

View file

@ -65,7 +65,7 @@ struct sce_np_util_manager
error_code sceNpUtilBandwidthTestInitStart([[maybe_unused]] ppu_thread& ppu, u32 prio, u32 stack)
{
sceNpUtil.todo("sceNpUtilBandwidthTestInitStart(prio=%d, stack=%d)", prio, stack);
sceNpUtil.warning("sceNpUtilBandwidthTestInitStart(prio=%d, stack=%d)", prio, stack);
auto& util_manager = g_fxo->get<sce_np_util_manager>();
std::lock_guard lock(util_manager.mtx);

View file

@ -314,6 +314,7 @@ static void ppu_initialize_modules(ppu_linkage_info* link, utils::serial* ar = n
&ppu_module_manager::libsnd3,
&ppu_module_manager::libsynth2,
&ppu_module_manager::sceNp,
&ppu_module_manager::sceNpBasicLimited,
&ppu_module_manager::sceNp2,
&ppu_module_manager::sceNpClans,
&ppu_module_manager::sceNpCommerce2,

View file

@ -283,6 +283,7 @@ public:
static const ppu_static_module libsnd3;
static const ppu_static_module libsynth2;
static const ppu_static_module sceNp;
static const ppu_static_module sceNpBasicLimited;
static const ppu_static_module sceNp2;
static const ppu_static_module sceNpClans;
static const ppu_static_module sceNpCommerce2;

View file

@ -33,12 +33,12 @@ void MouseHandlerBase::save(utils::serial& ar)
bool MouseHandlerBase::is_time_for_update(double elapsed_time_ms)
{
steady_clock::time_point now = steady_clock::now();
const double elapsed_ms = (now - last_update).count() / 1'000'000.;
const steady_clock::time_point now = steady_clock::now();
const double elapsed_ms = (now - m_last_update).count() / 1'000'000.;
if (elapsed_ms > elapsed_time_ms)
{
last_update = now;
m_last_update = now;
return true;
}
return false;

View file

@ -128,7 +128,7 @@ class MouseHandlerBase
protected:
MouseInfo m_info{};
std::vector<Mouse> m_mice;
steady_clock::time_point last_update{};
steady_clock::time_point m_last_update{};
bool is_time_for_update(double elapsed_time_ms = 10.0); // 4-10 ms, let's use 10 for now

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -1,4 +1,3 @@
// Protocol Buffers definition for np2_structs
syntax = "proto3";
@ -16,71 +15,61 @@ message uint16 {
uint32 value = 1;
}
// SignalingAddr
message SignalingAddr {
bytes ip = 1;
uint16 port = 2;
}
// MatchingSignalingInfo
message MatchingSignalingInfo {
string npid = 1;
SignalingAddr addr = 2;
}
// Matching2SignalingInfo
message Matching2SignalingInfo {
uint16 member_id = 1;
SignalingAddr addr = 2;
}
// BinAttr
message BinAttr {
uint16 id = 1;
bytes data = 2;
}
// IntAttr
message IntAttr {
uint16 id = 1;
uint32 num = 2;
}
// RoomMemberBinAttrInternal
message RoomMemberBinAttrInternal {
uint64 updateDate = 1;
BinAttr data = 2;
}
// BinAttrInternal
message BinAttrInternal {
uint64 updateDate = 1;
uint16 updateMemberId = 2;
BinAttr data = 3;
}
// OptParam
message OptParam {
uint8 type = 1;
uint8 flag = 2;
uint16 hubMemberId = 3;
}
// GroupConfig
message GroupConfig {
uint32 slotNum = 1;
bytes label = 2;
bool withPassword = 3;
}
// UserInfo
message UserInfo {
string npId = 1;
string onlineName = 2;
string avatarUrl = 3;
}
// RoomMemberDataInternal
message RoomMemberDataInternal {
UserInfo userInfo = 1;
uint64 joinDate = 2;
@ -92,7 +81,6 @@ message RoomMemberDataInternal {
repeated RoomMemberBinAttrInternal roomMemberBinAttrInternal = 8;
}
// RoomGroup
message RoomGroup {
uint8 groupId = 1;
bool withPassword = 2;
@ -101,7 +89,6 @@ message RoomGroup {
uint32 curGroupMemberNum = 5;
}
// RoomDataInternal
message RoomDataInternal {
uint16 serverId = 1;
uint32 worldId = 2;
@ -116,7 +103,6 @@ message RoomDataInternal {
repeated BinAttrInternal roomBinAttrInternal = 11;
}
// RoomDataExternal
message RoomDataExternal {
uint16 serverId = 1;
uint32 worldId = 2;
@ -137,31 +123,26 @@ message RoomDataExternal {
repeated BinAttr roomBinAttrExternal = 17;
}
// IntSearchFilter
message IntSearchFilter {
uint8 searchOperator = 1;
IntAttr attr = 2;
}
// BinSearchFilter
message BinSearchFilter {
uint8 searchOperator = 1;
BinAttr attr = 2;
}
// PresenceOptionData
message PresenceOptionData {
bytes data = 1;
uint32 len = 2;
}
// RoomGroupPasswordConfig
message RoomGroupPasswordConfig {
uint8 groupId = 1;
bool withPassword = 2;
}
// SearchRoomRequest
message SearchRoomRequest {
int32 option = 1;
uint32 worldId = 2;
@ -175,14 +156,12 @@ message SearchRoomRequest {
repeated uint16 attrId = 10;
}
// SearchRoomResponse
message SearchRoomResponse {
uint32 startIndex = 1;
uint32 total = 2;
repeated RoomDataExternal rooms = 3;
}
// CreateJoinRoomRequest
message CreateJoinRoomRequest {
uint32 worldId = 1;
uint64 lobbyId = 2;
@ -203,7 +182,12 @@ message CreateJoinRoomRequest {
OptParam sigOptParam = 17;
}
// JoinRoomRequest
// Reply on creating a room
message CreateRoomResponse {
RoomDataInternal internal = 1;
OptParam opt_param = 2;
}
message JoinRoomRequest {
uint64 roomId = 1;
bytes roomPassword = 2;
@ -213,30 +197,26 @@ message JoinRoomRequest {
uint8 teamId = 6;
}
// JoinRoomResponse
message JoinRoomResponse {
RoomDataInternal room_data = 1;
repeated Matching2SignalingInfo signaling_data = 2;
OptParam opt_param = 3;
}
// LeaveRoomRequest
message LeaveRoomRequest {
uint64 roomId = 1;
PresenceOptionData optData = 2;
}
// GetRoomDataExternalListRequest
message GetRoomDataExternalListRequest {
repeated uint64 roomIds = 1;
repeated uint16 attrIds = 2;
}
// GetRoomDataExternalListResponse
message GetRoomDataExternalListResponse {
repeated RoomDataExternal rooms = 1;
}
// SetRoomDataExternalRequest
message SetRoomDataExternalRequest {
uint64 roomId = 1;
repeated IntAttr roomSearchableIntAttrExternal = 2;
@ -244,7 +224,6 @@ message SetRoomDataExternalRequest {
repeated BinAttr roomBinAttrExternal = 4;
}
// SetRoomDataInternalRequest
message SetRoomDataInternalRequest {
uint64 roomId = 1;
uint32 flagFilter = 2;
@ -255,14 +234,12 @@ message SetRoomDataInternalRequest {
repeated uint16 ownerPrivilegeRank = 7;
}
// GetRoomMemberDataInternalRequest
message GetRoomMemberDataInternalRequest {
uint64 roomId = 1;
uint16 memberId = 2;
repeated uint16 attrId = 3;
}
// SetRoomMemberDataInternalRequest
message SetRoomMemberDataInternalRequest {
uint64 roomId = 1;
uint16 memberId = 2;
@ -270,40 +247,34 @@ message SetRoomMemberDataInternalRequest {
repeated BinAttr roomMemberBinAttrInternal = 4;
}
// SetUserInfo
message SetUserInfo {
uint16 serverId = 1;
repeated BinAttr userBinAttr = 2;
}
// GetRoomDataInternalRequest
message GetRoomDataInternalRequest {
uint64 roomId = 1;
repeated uint16 attrId = 2;
}
// RoomMemberUpdateInfo
message RoomMemberUpdateInfo {
RoomMemberDataInternal roomMemberDataInternal = 1;
uint8 eventCause = 2;
PresenceOptionData optData = 3;
}
// NotificationUserJoinedRoom
message NotificationUserJoinedRoom {
uint64 room_id = 1;
RoomMemberUpdateInfo update_info = 2;
SignalingAddr signaling = 3;
}
// RoomUpdateInfo
message RoomUpdateInfo {
uint8 eventCause = 1;
int32 errorCode = 2;
PresenceOptionData optData = 3;
}
// RoomDataInternalUpdateInfo
message RoomDataInternalUpdateInfo {
RoomDataInternal newRoomDataInternal = 1;
uint32 prevFlagAttr = 2;
@ -312,7 +283,6 @@ message RoomDataInternalUpdateInfo {
repeated uint16 newRoomBinAttrInternal = 5;
}
// RoomMemberDataInternalUpdateInfo
message RoomMemberDataInternalUpdateInfo {
RoomMemberDataInternal newRoomMemberDataInternal = 1;
uint32 prevFlagAttr = 2;
@ -320,7 +290,6 @@ message RoomMemberDataInternalUpdateInfo {
repeated uint16 newRoomMemberBinAttrInternal = 4;
}
// GetPingInfoResponse
message GetPingInfoResponse {
uint16 serverId = 1;
uint32 worldId = 2;
@ -328,7 +297,6 @@ message GetPingInfoResponse {
uint32 rtt = 4;
}
// SendRoomMessageRequest
message SendRoomMessageRequest {
uint64 roomId = 1;
uint8 castType = 2;
@ -337,7 +305,6 @@ message SendRoomMessageRequest {
uint8 option = 5;
}
// RoomMessageInfo
message RoomMessageInfo {
bool filtered = 1;
uint8 castType = 2;
@ -346,7 +313,6 @@ message RoomMessageInfo {
bytes msg = 5;
}
// MessageDetails
message MessageDetails {
string communicationId = 1;
uint64 msgId = 2;
@ -358,13 +324,11 @@ message MessageDetails {
bytes data = 8;
}
// SendMessageRequest
message SendMessageRequest {
bytes message = 1;
repeated string npids = 2;
}
// BoardInfo
message BoardInfo {
uint32 rankLimit = 1;
uint32 updateMode = 2;
@ -373,7 +337,6 @@ message BoardInfo {
uint32 uploadSizeLimit = 5;
}
// RecordScoreRequest
message RecordScoreRequest {
uint32 boardId = 1;
int32 pcId = 2;
@ -382,7 +345,6 @@ message RecordScoreRequest {
bytes data = 5;
}
// GetScoreRangeRequest
message GetScoreRangeRequest {
uint32 boardId = 1;
uint32 startRank = 2;
@ -391,13 +353,11 @@ message GetScoreRangeRequest {
bool withGameInfo = 5;
}
// ScoreNpIdPcId
message ScoreNpIdPcId {
string npid = 1;
int32 pcId = 2;
}
// GetScoreNpIdRequest
message GetScoreNpIdRequest {
uint32 boardId = 1;
repeated ScoreNpIdPcId npids = 2;
@ -405,7 +365,6 @@ message GetScoreNpIdRequest {
bool withGameInfo = 4;
}
// GetScoreFriendsRequest
message GetScoreFriendsRequest {
uint32 boardId = 1;
bool include_self = 2;
@ -414,7 +373,6 @@ message GetScoreFriendsRequest {
bool withGameInfo = 5;
}
// ScoreRankData
message ScoreRankData {
string npId = 1;
string onlineName = 2;
@ -425,12 +383,10 @@ message ScoreRankData {
uint64 recordDate = 7;
}
// ScoreInfo
message ScoreInfo {
bytes data = 1;
}
// GetScoreResponse
message GetScoreResponse {
repeated ScoreRankData rankArray = 1;
repeated string commentArray = 2;
@ -439,27 +395,23 @@ message GetScoreResponse {
uint32 totalRecord = 5;
}
// RecordScoreGameDataRequest
message RecordScoreGameDataRequest {
uint32 boardId = 1;
int32 pcId = 2;
int64 score = 3;
}
// GetScoreGameDataRequest
message GetScoreGameDataRequest {
uint32 boardId = 1;
string npId = 2;
int32 pcId = 3;
}
// TusUser
message TusUser {
bool vuser = 1;
string npid = 2;
}
// TusVariable
message TusVariable {
string ownerId = 1;
bool hasData = 2;
@ -469,31 +421,26 @@ message TusVariable {
int64 oldVariable = 6;
}
// TusVarResponse
message TusVarResponse {
repeated TusVariable vars = 1;
}
// TusSetMultiSlotVariableRequest
message TusSetMultiSlotVariableRequest {
TusUser user = 1;
repeated int32 slotIdArray = 2;
repeated int64 variableArray = 3;
}
// TusGetMultiSlotVariableRequest
message TusGetMultiSlotVariableRequest {
TusUser user = 1;
repeated int32 slotIdArray = 2;
}
// TusGetMultiUserVariableRequest
message TusGetMultiUserVariableRequest {
repeated TusUser users = 1;
int32 slotId = 2;
}
// TusGetFriendsVariableRequest
message TusGetFriendsVariableRequest {
int32 slotId = 1;
bool includeSelf = 2;
@ -501,7 +448,6 @@ message TusGetFriendsVariableRequest {
uint32 arrayNum = 4;
}
// TusAddAndGetVariableRequest
message TusAddAndGetVariableRequest {
TusUser user = 1;
int32 slotId = 2;
@ -510,7 +456,6 @@ message TusAddAndGetVariableRequest {
string isLastChangedAuthorId = 5;
}
// TusTryAndSetVariableRequest
message TusTryAndSetVariableRequest {
TusUser user = 1;
int32 slotId = 2;
@ -521,13 +466,11 @@ message TusTryAndSetVariableRequest {
repeated int64 compareValue = 7;
}
// TusDeleteMultiSlotVariableRequest
message TusDeleteMultiSlotVariableRequest {
TusUser user = 1;
repeated int32 slotIdArray = 2;
}
// TusSetDataRequest
message TusSetDataRequest {
TusUser user = 1;
int32 slotId = 2;
@ -537,7 +480,6 @@ message TusSetDataRequest {
string isLastChangedAuthorId = 6;
}
// TusDataStatus
message TusDataStatus {
string ownerId = 1;
bool hasData = 2;
@ -546,36 +488,30 @@ message TusDataStatus {
bytes info = 5;
}
// TusData
message TusData {
TusDataStatus status = 1;
bytes data = 2;
}
// TusDataStatusResponse
message TusDataStatusResponse {
repeated TusDataStatus status = 1;
}
// TusGetDataRequest
message TusGetDataRequest {
TusUser user = 1;
int32 slotId = 2;
}
// TusGetMultiSlotDataStatusRequest
message TusGetMultiSlotDataStatusRequest {
TusUser user = 1;
repeated int32 slotIdArray = 2;
}
// TusGetMultiUserDataStatusRequest
message TusGetMultiUserDataStatusRequest {
repeated TusUser users = 1;
int32 slotId = 2;
}
// TusGetFriendsDataStatusRequest
message TusGetFriendsDataStatusRequest {
int32 slotId = 1;
bool includeSelf = 2;
@ -583,13 +519,11 @@ message TusGetFriendsDataStatusRequest {
uint32 arrayNum = 4;
}
// TusDeleteMultiSlotDataRequest
message TusDeleteMultiSlotDataRequest {
TusUser user = 1;
repeated int32 slotIdArray = 2;
}
// SetPresenceRequest
message SetPresenceRequest {
string title = 1;
string status = 2;
@ -597,7 +531,6 @@ message SetPresenceRequest {
bytes data = 4;
}
// MatchingSearchCondition
message MatchingSearchCondition {
uint32 attr_type = 1;
uint32 attr_id = 2;
@ -605,7 +538,6 @@ message MatchingSearchCondition {
uint32 comp_value = 4;
}
// MatchingAttr
message MatchingAttr {
uint32 attr_type = 1;
uint32 attr_id = 2;
@ -613,7 +545,6 @@ message MatchingAttr {
bytes data = 4;
}
// CreateRoomGUIRequest
message CreateRoomGUIRequest {
uint32 total_slots = 1;
uint32 private_slots = 2;
@ -622,13 +553,11 @@ message CreateRoomGUIRequest {
repeated MatchingAttr game_attrs = 5;
}
// GUIUserInfo
message GUIUserInfo {
UserInfo info = 1;
bool owner = 2;
}
// MatchingRoomStatus
message MatchingRoomStatus {
bytes id = 1;
repeated GUIUserInfo members = 2;
@ -636,7 +565,6 @@ message MatchingRoomStatus {
bytes opt = 4;
}
// GetRoomListGUIRequest
message GetRoomListGUIRequest {
uint32 range_start = 1;
uint32 range_max = 2;
@ -644,44 +572,48 @@ message GetRoomListGUIRequest {
repeated MatchingAttr attrs = 4;
}
// MatchingRoom
message MatchingRoom {
bytes id = 1;
repeated MatchingAttr attr = 2;
}
// MatchingRoomList
message MatchingRoomList {
uint32 start = 1;
uint32 total = 2;
repeated MatchingRoom rooms = 3;
}
// MatchingGuiRoomId
message MatchingGuiRoomId {
bytes id = 1;
}
// SetRoomSearchFlagGUI
message SetRoomSearchFlagGUI {
bytes roomid = 1;
bool stealth = 2;
}
// QuickMatchGUIRequest
message QuickMatchGUIRequest {
repeated MatchingSearchCondition conds = 1;
uint32 available_num = 2;
}
// SearchJoinRoomGUIRequest
message SearchJoinRoomGUIRequest {
repeated MatchingSearchCondition conds = 1;
repeated MatchingAttr attrs = 2;
}
// MatchingSearchJoinRoomInfo
message MatchingSearchJoinRoomInfo {
MatchingRoomStatus room = 1;
repeated MatchingAttr attr = 2;
}
message RoomMemberDataExternal {
UserInfo userInfo = 1;
uint64 joinDate = 2;
uint8 role = 3;
}
message GetRoomMemberDataExternalListResponse
{
repeated RoomMemberDataExternal members = 1;
}

View file

@ -39,6 +39,11 @@ namespace np
m_allocs.clear();
}
std::tuple<u32, u32, u32> get_stats() const
{
return {m_size, m_size - m_avail, m_max_usage};
}
u32 allocate(u32 size)
{
std::lock_guard lock(m_mutex);
@ -82,6 +87,12 @@ namespace np
m_allocs.emplace(last_free, alloc_size);
m_avail -= alloc_size;
const u32 usage = m_size - m_avail;
if (usage > m_max_usage)
{
m_max_usage = usage;
}
memset((static_cast<u8*>(m_pool.get_ptr())) + last_free, 0, alloc_size);
np_mem_allocator.trace("Allocation off:%d size:%d psize:%d, pavail:%d", last_free, alloc_size, m_size, m_avail);
@ -121,6 +132,7 @@ namespace np
vm::ptr<void> m_pool{};
u32 m_size = 0;
u32 m_avail = 0;
u32 m_max_usage = 0;
std::map<u32, u32> m_allocs{}; // offset/size
};
} // namespace np

View file

@ -120,6 +120,11 @@ namespace np
rooms[room_id].password = password;
}
void cache_manager::update_opt_param(SceNpMatching2RoomId room_id, const SceNpMatching2SignalingOptParam* sce_opt_param)
{
rooms[room_id].opt_param = *sce_opt_param;
}
std::pair<error_code, std::optional<SceNpMatching2RoomSlotInfo>> cache_manager::get_slots(SceNpMatching2RoomId room_id)
{
std::lock_guard lock(mutex);
@ -223,6 +228,18 @@ namespace np
return {CELL_OK, rooms[room_id].password};
}
std::pair<error_code, std::optional<SceNpMatching2SignalingOptParam>> cache_manager::get_opt_param(SceNpMatching2RoomId room_id)
{
std::lock_guard lock(mutex);
if (!rooms.contains(room_id))
{
return {SCE_NP_MATCHING2_ERROR_ROOM_NOT_FOUND, {}};
}
return {CELL_OK, rooms[room_id].opt_param};
}
error_code cache_manager::get_member_and_attrs(SceNpMatching2RoomId room_id, SceNpMatching2RoomMemberId member_id, const std::vector<SceNpMatching2AttributeId>& binattrs_list, SceNpMatching2RoomMemberDataInternal* ptr_member, u32 addr_data, u32 size_data, bool include_onlinename, bool include_avatarurl)
{
std::lock_guard lock(mutex);

View file

@ -58,6 +58,8 @@ namespace np
std::map<SceNpMatching2RoomGroupId, SceNpMatching2RoomGroup> groups;
std::map<SceNpMatching2RoomMemberId, member_cache> members;
SceNpMatching2SignalingOptParam opt_param{};
bool owner = false;
};
@ -70,10 +72,12 @@ namespace np
bool add_member(SceNpMatching2RoomId room_id, const SceNpMatching2RoomMemberDataInternal* sce_roommemberdata);
bool del_member(SceNpMatching2RoomId room_id, SceNpMatching2RoomMemberId member_id);
void update_password(SceNpMatching2RoomId room_id, const std::optional<SceNpMatching2SessionPassword>& password);
void update_opt_param(SceNpMatching2RoomId room_id, const SceNpMatching2SignalingOptParam* sce_opt_param);
std::pair<error_code, std::optional<SceNpMatching2RoomSlotInfo>> get_slots(SceNpMatching2RoomId room_id);
std::pair<error_code, std::vector<SceNpMatching2RoomMemberId>> get_memberids(u64 room_id, s32 sort_method);
std::pair<error_code, std::optional<SceNpMatching2SessionPassword>> get_password(SceNpMatching2RoomId room_id);
std::pair<error_code, std::optional<SceNpMatching2SignalingOptParam>> get_opt_param(SceNpMatching2RoomId room_id);
error_code get_member_and_attrs(SceNpMatching2RoomId room_id, SceNpMatching2RoomMemberId member_id, const std::vector<SceNpMatching2AttributeId>& binattrs_list, SceNpMatching2RoomMemberDataInternal* ptr_member, u32 addr_data, u32 size_data, bool include_onlinename, bool include_avatarurl);
std::pair<error_code, std::optional<SceNpId>> get_npid(u64 room_id, u16 member_id);
std::optional<u16> get_memberid(u64 room_id, const SceNpId& npid);

View file

@ -17,7 +17,12 @@ generic_async_transaction_context::generic_async_transaction_context(const SceNp
generic_async_transaction_context::~generic_async_transaction_context()
{
if (thread.joinable())
thread.join();
{
if (std::this_thread::get_id() == thread.get_id())
thread.detach();
else
thread.join();
}
}
std::optional<s32> generic_async_transaction_context::get_transaction_status()
@ -41,7 +46,7 @@ error_code generic_async_transaction_context::wait_for_completion()
return *result;
}
completion_cond.wait(lock);
completion_cond.wait(lock, [this] { return result.has_value(); });
return *result;
}

View file

@ -1085,6 +1085,9 @@ namespace np
void np_handler::send_message(const message_data& msg_data, const std::set<std::string>& npids)
{
rpcn_log.notice("Sending message to \"%s\":", fmt::merge(npids, "\",\""));
msg_data.print();
get_rpcn()->send_message(msg_data, npids);
}
@ -1124,6 +1127,7 @@ namespace np
case rpcn::CommandType::LeaveRoom: reply_leave_room(req_id, error, reply_data); break;
case rpcn::CommandType::SearchRoom: reply_search_room(req_id, error, reply_data); break;
case rpcn::CommandType::GetRoomDataExternalList: reply_get_roomdata_external_list(req_id, error, reply_data); break;
case rpcn::CommandType::GetRoomMemberDataExternalList: reply_get_room_member_data_external_list(req_id, error, reply_data); break;
case rpcn::CommandType::SetRoomDataExternal: reply_set_roomdata_external(req_id, error); break;
case rpcn::CommandType::GetRoomDataInternal: reply_get_roomdata_internal(req_id, error, reply_data); break;
case rpcn::CommandType::SetRoomDataInternal: reply_set_roomdata_internal(req_id, error); break;
@ -1672,6 +1676,11 @@ namespace np
return np_cache.get_memberids(room_id, sort_method);
}
std::pair<error_code, std::optional<SceNpMatching2SignalingOptParam>> np_handler::local_get_signaling_opt_param(SceNpMatching2RoomId room_id)
{
return np_cache.get_opt_param(room_id);
}
error_code np_handler::local_get_room_member_data(SceNpMatching2RoomId room_id, SceNpMatching2RoomMemberId member_id, const std::vector<SceNpMatching2AttributeId>& binattrs_list, SceNpMatching2RoomMemberDataInternal* ptr_member, u32 addr_data, u32 size_data, u32 ctx_id)
{
auto [include_onlinename, include_avatarurl] = get_match2_context_options(ctx_id);
@ -1759,4 +1768,16 @@ namespace np
}
}
SceNpMatching2MemoryInfo np_handler::get_memory_info() const
{
auto [m_size, m_usage, m_max_usage] = np_memory.get_stats();
SceNpMatching2MemoryInfo mem_info{};
mem_info.totalMemSize = m_size;
mem_info.curMemUsage = m_usage;
mem_info.maxMemUsage = m_max_usage;
return mem_info;
}
} // namespace np

View file

@ -177,6 +177,7 @@ namespace np
u32 leave_room(SceNpMatching2ContextId ctx_id, vm::cptr<SceNpMatching2RequestOptParam> optParam, const SceNpMatching2LeaveRoomRequest* req);
u32 search_room(SceNpMatching2ContextId ctx_id, vm::cptr<SceNpMatching2RequestOptParam> optParam, const SceNpMatching2SearchRoomRequest* req);
u32 get_roomdata_external_list(SceNpMatching2ContextId ctx_id, vm::cptr<SceNpMatching2RequestOptParam> optParam, const SceNpMatching2GetRoomDataExternalListRequest* req);
u32 get_room_member_data_external_list(SceNpMatching2ContextId ctx_id, vm::cptr<SceNpMatching2RequestOptParam> optParam, const SceNpMatching2GetRoomMemberDataExternalListRequest* req);
u32 set_roomdata_external(SceNpMatching2ContextId ctx_id, vm::cptr<SceNpMatching2RequestOptParam> optParam, const SceNpMatching2SetRoomDataExternalRequest* req);
u32 get_roomdata_internal(SceNpMatching2ContextId ctx_id, vm::cptr<SceNpMatching2RequestOptParam> optParam, const SceNpMatching2GetRoomDataInternalRequest* req);
u32 set_roomdata_internal(SceNpMatching2ContextId ctx_id, vm::cptr<SceNpMatching2RequestOptParam> optParam, const SceNpMatching2SetRoomDataInternalRequest* req);
@ -235,6 +236,7 @@ namespace np
std::pair<error_code, std::optional<SceNpMatching2RoomSlotInfo>> local_get_room_slots(SceNpMatching2RoomId room_id);
std::pair<error_code, std::optional<SceNpMatching2SessionPassword>> local_get_room_password(SceNpMatching2RoomId room_id);
std::pair<error_code, std::vector<SceNpMatching2RoomMemberId>> local_get_room_memberids(SceNpMatching2RoomId room_id, s32 sort_method);
std::pair<error_code, std::optional<SceNpMatching2SignalingOptParam>> local_get_signaling_opt_param(SceNpMatching2RoomId room_id);
error_code local_get_room_member_data(SceNpMatching2RoomId room_id, SceNpMatching2RoomMemberId member_id, const std::vector<SceNpMatching2AttributeId>& binattrs_list, SceNpMatching2RoomMemberDataInternal* ptr_member, u32 addr_data, u32 size_data, u32 ctx_id);
// Local GUI functions
@ -261,6 +263,7 @@ namespace np
u32 add_players_to_history(const SceNpId* npids, const char* description, u32 count);
u32 get_players_history_count(u32 options);
bool get_player_history_entry(u32 options, u32 index, SceNpId* npid);
SceNpMatching2MemoryInfo get_memory_info() const;
error_code abort_request(u32 req_id);
// For signaling
@ -321,6 +324,7 @@ namespace np
void reply_leave_room(u32 req_id, rpcn::ErrorType error, vec_stream& reply);
void reply_search_room(u32 req_id, rpcn::ErrorType error, vec_stream& reply);
void reply_get_roomdata_external_list(u32 req_id, rpcn::ErrorType error, vec_stream& reply);
void reply_get_room_member_data_external_list(u32 req_id, rpcn::ErrorType error, vec_stream& reply);
void reply_set_roomdata_external(u32 req_id, rpcn::ErrorType error);
void reply_get_roomdata_internal(u32 req_id, rpcn::ErrorType error, vec_stream& reply);
void reply_set_roomdata_internal(u32 req_id, rpcn::ErrorType error);

View file

@ -8,6 +8,8 @@
#include <WS2tcpip.h>
#endif
LOG_CHANNEL(rpcn_log, "rpcn");
namespace np
{
std::string ip_to_string(u32 ip_addr)
@ -34,6 +36,24 @@ namespace np
return fmt::format("%s_%02d", com_id_data, communicationId.num);
}
std::optional<SceNpCommunicationId> string_to_communication_id(std::string_view str)
{
SceNpCommunicationId id{};
const auto split_id = fmt::split_sv(str, {"_"});
if (split_id.size() != 2 || split_id[0].length() != 9 || split_id[1].length() != 2 || !std::isdigit(split_id[1][0]) || !std::isdigit(split_id[1][1]))
{
rpcn_log.error("Tried to parse an invalid communication_id!");
return std::nullopt;
}
strcpy_trunc(id.data, split_id[0]);
id.num = std::stoi(std::string(split_id[1]));
return id;
}
void strings_to_userinfo(std::string_view npid, std::string_view online_name, std::string_view avatar_url, SceNpUserInfo& user_info)
{
memset(&user_info, 0, sizeof(user_info));
@ -81,12 +101,6 @@ namespace np
strcpy_trunc(avatar_url.data, str);
}
void string_to_communication_id(std::string_view str, SceNpCommunicationId& comm_id)
{
memset(&comm_id, 0, sizeof(comm_id));
strcpy_trunc(comm_id.data, str);
}
bool is_valid_npid(const SceNpId& npid)
{
if (!std::all_of(npid.handle.data, npid.handle.data + 16, [](char c) { return std::isalnum(c) || c == '-' || c == '_' || c == 0; } )

View file

@ -10,11 +10,11 @@ namespace np
std::string ether_to_string(std::array<u8, 6>& ether);
bool validate_communication_id(const SceNpCommunicationId& com_id);
std::string communication_id_to_string(const SceNpCommunicationId& communicationId);
std::optional<SceNpCommunicationId> string_to_communication_id(std::string_view str);
void string_to_npid(std::string_view str, SceNpId& npid);
void string_to_online_name(std::string_view str, SceNpOnlineName& online_name);
void string_to_avatar_url(std::string_view str, SceNpAvatarUrl& avatar_url);
void string_to_communication_id(std::string_view str, SceNpCommunicationId& comm_id);
void strings_to_userinfo(std::string_view npid, std::string_view online_name, std::string_view avatar_url, SceNpUserInfo& user_info);
template <typename T>

View file

@ -200,8 +200,11 @@ namespace np
return;
}
const auto resp = reply.get_protobuf<np2_structs::RoomDataInternal>();
const auto resp = reply.get_protobuf<np2_structs::CreateRoomResponse>();
ensure(!reply.is_error(), "Malformed reply to CreateRoom command");
ensure(resp->has_internal());
const auto& resp_internal = resp->internal();
const u32 event_key = get_event_key();
auto [include_onlinename, include_avatarurl] = get_match2_context_options(cb_info_opt->ctx_id);
@ -209,13 +212,19 @@ namespace np
auto& edata = allocate_req_result(event_key, SCE_NP_MATCHING2_EVENT_DATA_MAX_SIZE_CreateJoinRoom, sizeof(SceNpMatching2CreateJoinRoomResponse));
auto* room_resp = reinterpret_cast<SceNpMatching2CreateJoinRoomResponse*>(edata.data());
auto* room_info = edata.allocate<SceNpMatching2RoomDataInternal>(sizeof(SceNpMatching2RoomDataInternal), room_resp->roomDataInternal);
RoomDataInternal_to_SceNpMatching2RoomDataInternal(edata, *resp, room_info, npid, include_onlinename, include_avatarurl);
RoomDataInternal_to_SceNpMatching2RoomDataInternal(edata, resp_internal, room_info, npid, include_onlinename, include_avatarurl);
np_memory.shrink_allocation(edata.addr(), edata.size());
const auto resp_opt_param = resp->opt_param();
SceNpMatching2SignalingOptParam opt_param{};
OptParam_to_SceNpMatching2SignalingOptParam(resp_opt_param, &opt_param);
np_cache.insert_room(room_info);
np_cache.update_password(room_resp->roomDataInternal->roomId, cached_cj_password);
np_cache.update_password(room_info->roomId, cached_cj_password);
np_cache.update_opt_param(room_info->roomId, &opt_param);
extra_nps::print_SceNpMatching2CreateJoinRoomResponse(room_resp);
extra_nps::print_SceNpMatching2SignalingOptParam(&opt_param);
cb_info_opt->queue_callback(req_id, event_key, 0, edata.size());
}
@ -275,36 +284,38 @@ namespace np
RoomDataInternal_to_SceNpMatching2RoomDataInternal(edata, resp->room_data(), room_info, npid, include_onlinename, include_avatarurl);
np_memory.shrink_allocation(edata.addr(), edata.size());
const u64 room_id = resp->room_data().roomid();
const auto resp_opt_param = resp->opt_param();
SceNpMatching2SignalingOptParam opt_param{};
OptParam_to_SceNpMatching2SignalingOptParam(resp_opt_param, &opt_param);
np_cache.insert_room(room_info);
np_cache.update_opt_param(room_id, &opt_param);
extra_nps::print_SceNpMatching2RoomDataInternal(room_info);
extra_nps::print_SceNpMatching2SignalingOptParam(&opt_param);
// We initiate signaling if necessary
if (resp->signaling_data_size() > 0)
for (int i = 0; i < resp->signaling_data_size(); i++)
{
const u64 room_id = resp->room_data().roomid();
const auto& signaling_info = resp->signaling_data(i);
ensure(signaling_info.has_addr());
for (int i = 0; i < resp->signaling_data_size(); i++)
{
const auto& signaling_info = resp->signaling_data(i);
ensure(signaling_info.has_addr());
const u32 addr_p2p = register_ip(signaling_info.addr().ip());
const u16 port_p2p = signaling_info.addr().port().value();
const u32 addr_p2p = register_ip(signaling_info.addr().ip());
const u16 port_p2p = signaling_info.addr().port().value();
const u16 member_id = signaling_info.member_id().value();
const auto [npid_res, npid_p2p] = np_cache.get_npid(room_id, member_id);
const u16 member_id = signaling_info.member_id().value();
const auto [npid_res, npid_p2p] = np_cache.get_npid(room_id, member_id);
if (npid_res != CELL_OK)
continue;
if (npid_res != CELL_OK)
continue;
rpcn_log.notice("JoinRoomResult told to connect to member(%d=%s) of room(%d): %s:%d", member_id, reinterpret_cast<const char*>(npid_p2p->handle.data), room_id, ip_to_string(addr_p2p), port_p2p);
rpcn_log.notice("JoinRoomResult told to connect to member(%d=%s) of room(%d): %s:%d", member_id, reinterpret_cast<const char*>(npid_p2p->handle.data), room_id, ip_to_string(addr_p2p), port_p2p);
// Attempt Signaling
auto& sigh = g_fxo->get<named_thread<signaling_handler>>();
const u32 conn_id = sigh.init_sig2(*npid_p2p, room_id, member_id);
sigh.start_sig(conn_id, addr_p2p, port_p2p);
}
// Attempt Signaling
auto& sigh = g_fxo->get<named_thread<signaling_handler>>();
const u32 conn_id = sigh.init_sig2(*npid_p2p, room_id, member_id);
sigh.start_sig(conn_id, addr_p2p, port_p2p);
}
cb_info_opt->queue_callback(req_id, event_key, 0, edata.size());
@ -434,6 +445,50 @@ namespace np
cb_info_opt->queue_callback(req_id, event_key, 0, edata.size());
}
u32 np_handler::get_room_member_data_external_list(SceNpMatching2ContextId ctx_id, vm::cptr<SceNpMatching2RequestOptParam> optParam, const SceNpMatching2GetRoomMemberDataExternalListRequest* req)
{
const u32 req_id = generate_callback_info(ctx_id, optParam, SCE_NP_MATCHING2_REQUEST_EVENT_GetRoomDataExternalList, true);
if (!get_rpcn()->get_room_member_data_external_list(req_id, get_match2_context(ctx_id)->communicationId, req->roomId))
{
rpcn_log.error("Disconnecting from RPCN!");
is_psn_active = false;
}
return req_id;
}
void np_handler::reply_get_room_member_data_external_list(u32 req_id, rpcn::ErrorType error, vec_stream& reply)
{
auto cb_info_opt = take_pending_request(req_id);
if (!cb_info_opt)
return;
ensure(error == rpcn::ErrorType::NoError, "Unexpected error in GetRoomMemberDataExternalList reply");
if (error == rpcn::ErrorType::RoomMissing)
{
cb_info_opt->queue_callback(req_id, 0, SCE_NP_MATCHING2_SERVER_ERROR_NO_SUCH_ROOM, 0);
return;
}
const auto resp = reply.get_protobuf<np2_structs::GetRoomMemberDataExternalListResponse>();
ensure(!reply.is_error(), "Malformed reply to GetRoomMemberDataExternalList command");
const u32 event_key = get_event_key();
auto [include_onlinename, include_avatarurl] = get_match2_context_options(cb_info_opt->ctx_id);
auto& edata = allocate_req_result(event_key, SCE_NP_MATCHING2_EVENT_DATA_MAX_SIZE_GetRoomMemberDataExternalList, sizeof(SceNpMatching2GetRoomMemberDataExternalListResponse));
auto* sce_get_room_member_ext_resp = reinterpret_cast<SceNpMatching2GetRoomMemberDataExternalListResponse*>(edata.data());
GetRoomMemberDataExternalListResponse_to_SceNpMatching2GetRoomMemberDataExternalListResponse(edata, *resp, sce_get_room_member_ext_resp, include_onlinename, include_avatarurl);
np_memory.shrink_allocation(edata.addr(), edata.size());
extra_nps::print_SceNpMatching2GetRoomMemberDataExternalListResponse(sce_get_room_member_ext_resp);
cb_info_opt->queue_callback(req_id, event_key, 0, edata.size());
}
u32 np_handler::set_roomdata_external(SceNpMatching2ContextId ctx_id, vm::cptr<SceNpMatching2RequestOptParam> optParam, const SceNpMatching2SetRoomDataExternalRequest* req)
{
const u32 req_id = generate_callback_info(ctx_id, optParam, SCE_NP_MATCHING2_REQUEST_EVENT_SetRoomDataExternal, false);
@ -896,13 +951,16 @@ namespace np
{
thread_base::set_name("NP Trans Worker");
auto res = trans_ctx->wake_cond.wait_for(lock, std::chrono::microseconds(trans_ctx->timeout));
bool has_value = trans_ctx->wake_cond.wait_for(lock, std::chrono::microseconds(trans_ctx->timeout), [&]
{
return trans_ctx->result.has_value();
});
{
std::lock_guard lock_threads(this->mutex_async_transactions);
this->async_transactions.erase(req_id);
}
if (res == std::cv_status::timeout)
if (!has_value)
{
trans_ctx->result = SCE_NP_COMMUNITY_ERROR_TIMEOUT;
return;

View file

@ -195,6 +195,15 @@ namespace extra_nps
}
}
void print_SceNpMatching2RoomMemberDataExternal(const SceNpMatching2RoomMemberDataExternal* member)
{
sceNp2.warning("SceNpMatching2RoomMemberDataExternal:");
sceNp2.warning("next: *0x%x", member->next);
print_SceNpUserInfo2(&member->userInfo);
sceNp2.warning("joinDate: %lld", member->joinDate.tick);
sceNp2.warning("role: %d", member->role);
}
void print_SceNpMatching2RoomMemberDataInternal(const SceNpMatching2RoomMemberDataInternal* member)
{
sceNp2.warning("SceNpMatching2RoomMemberDataInternal:");
@ -397,6 +406,21 @@ namespace extra_nps
}
}
void print_SceNpMatching2GetRoomMemberDataExternalListResponse(const SceNpMatching2GetRoomMemberDataExternalListResponse* resp)
{
sceNp2.warning("SceNpMatching2GetRoomMemberDataExternalListResponse:");
sceNp2.warning("roomMemberDataExternalNum: %d", resp->roomMemberDataExternalNum);
vm::bptr<SceNpMatching2RoomMemberDataExternal> cur_member = resp->roomMemberDataExternal;
for (u32 i = 0; i < resp->roomMemberDataExternalNum && cur_member; i++)
{
sceNp2.warning("roomMemberDataExternal[%d]:", i);
print_SceNpMatching2RoomMemberDataExternal(cur_member.get_ptr());
cur_member = cur_member->next;
}
}
void print_SceNpMatching2GetLobbyInfoListRequest(const SceNpMatching2GetLobbyInfoListRequest* resp)
{
sceNp2.warning("SceNpMatching2GetLobbyInfoListRequest:");

View file

@ -11,6 +11,7 @@ namespace extra_nps
void print_SceNpMatching2RangeFilter(const SceNpMatching2RangeFilter* filt);
void print_SceNpMatching2RoomDataInternal(const SceNpMatching2RoomDataInternal* room);
void print_SceNpMatching2RoomDataExternal(const SceNpMatching2RoomDataExternal* room);
void print_SceNpMatching2RoomMemberDataExternal(const SceNpMatching2RoomMemberDataExternal* room);
void print_SceNpMatching2RoomMemberDataInternal(const SceNpMatching2RoomMemberDataInternal* member);
void print_SceNpMatching2CreateJoinRoomRequest(const SceNpMatching2CreateJoinRoomRequest* req);
@ -24,6 +25,7 @@ namespace extra_nps
void print_SceNpMatching2SetRoomMemberDataInternalRequest(const SceNpMatching2SetRoomMemberDataInternalRequest* req);
void print_SceNpMatching2GetRoomDataExternalListRequest(const SceNpMatching2GetRoomDataExternalListRequest* req);
void print_SceNpMatching2GetRoomDataExternalListResponse(const SceNpMatching2GetRoomDataExternalListResponse* resp);
void print_SceNpMatching2GetRoomMemberDataExternalListResponse(const SceNpMatching2GetRoomMemberDataExternalListResponse* resp);
void print_SceNpMatching2GetLobbyInfoListRequest(const SceNpMatching2GetLobbyInfoListRequest* resp);

View file

@ -171,6 +171,15 @@ namespace np
}
}
void RoomMemberDataExternal_to_SceNpMatching2RoomMemberDataExternal(event_data& edata, const np2_structs::RoomMemberDataExternal& member, SceNpMatching2RoomMemberDataExternal* member_info, bool include_onlinename, bool include_avatarurl)
{
ensure(member.has_userinfo());
UserInfo_to_SceNpUserInfo2(edata, member.userinfo(), &member_info->userInfo, include_onlinename, include_avatarurl);
member_info->joinDate.tick = member.joindate();
member_info->role = member.role().value();
}
void SearchRoomResponse_to_SceNpMatching2SearchRoomResponse(event_data& edata, const np2_structs::SearchRoomResponse& resp, SceNpMatching2SearchRoomResponse* search_resp)
{
search_resp->range.size = resp.rooms_size();
@ -181,8 +190,7 @@ namespace np
for (int i = 0; i < resp.rooms_size(); i++)
{
const auto& pb_room = resp.rooms(i);
SceNpMatching2RoomDataExternal* cur_room;
cur_room = edata.allocate<SceNpMatching2RoomDataExternal>(sizeof(SceNpMatching2RoomDataExternal), (i > 0) ? prev_room->next : search_resp->roomDataExternal);
SceNpMatching2RoomDataExternal* cur_room = edata.allocate<SceNpMatching2RoomDataExternal>(sizeof(SceNpMatching2RoomDataExternal), (i > 0) ? prev_room->next : search_resp->roomDataExternal);
RoomDataExternal_to_SceNpMatching2RoomDataExternal(edata, pb_room, cur_room, true, true);
prev_room = cur_room;
}
@ -196,15 +204,28 @@ namespace np
for (int i = 0; i < resp.rooms_size(); i++)
{
const auto& pb_room = resp.rooms(i);
SceNpMatching2RoomDataExternal* cur_room;
cur_room = edata.allocate<SceNpMatching2RoomDataExternal>(sizeof(SceNpMatching2RoomDataExternal), (i > 0) ? prev_room->next : get_resp->roomDataExternal);
SceNpMatching2RoomDataExternal* cur_room = edata.allocate<SceNpMatching2RoomDataExternal>(sizeof(SceNpMatching2RoomDataExternal), (i > 0) ? prev_room->next : get_resp->roomDataExternal);
RoomDataExternal_to_SceNpMatching2RoomDataExternal(edata, pb_room, cur_room, include_onlinename, include_avatarurl);
prev_room = cur_room;
}
}
void GetRoomMemberDataExternalListResponse_to_SceNpMatching2GetRoomMemberDataExternalListResponse(event_data& edata, const np2_structs::GetRoomMemberDataExternalListResponse& resp, SceNpMatching2GetRoomMemberDataExternalListResponse* get_resp, bool include_onlinename, bool include_avatarurl)
{
get_resp->roomMemberDataExternalNum = resp.members_size();
SceNpMatching2RoomMemberDataExternal* prev_member = nullptr;
for (int i = 0; i < resp.members_size(); i++)
{
const auto& pb_member = resp.members(i);
SceNpMatching2RoomMemberDataExternal* cur_member = edata.allocate<SceNpMatching2RoomMemberDataExternal>(sizeof(SceNpMatching2RoomMemberDataExternal), (i > 0) ? prev_member->next : get_resp->roomMemberDataExternal);
RoomMemberDataExternal_to_SceNpMatching2RoomMemberDataExternal(edata, pb_member, cur_member, include_onlinename, include_avatarurl);
prev_member = cur_member;
}
}
u16 RoomDataInternal_to_SceNpMatching2RoomDataInternal(event_data& edata, const np2_structs::RoomDataInternal& resp, SceNpMatching2RoomDataInternal* room_info, const SceNpId& npid, bool include_onlinename, bool include_avatarurl)
{
u16 member_id = 0;
@ -632,4 +653,11 @@ namespace np
MatchingAttr_to_SceNpMatchingAttr(edata, resp.attr(), room_info->attr);
}
void OptParam_to_SceNpMatching2SignalingOptParam(const np2_structs::OptParam& resp, SceNpMatching2SignalingOptParam* opt_param)
{
opt_param->type = resp.type().value();
opt_param->flag = resp.flag().value();
opt_param->hubMemberId = resp.hubmemberid().value();
}
} // namespace np

View file

@ -16,8 +16,10 @@ namespace np
void UserInfo_to_SceNpUserInfo(const np2_structs::UserInfo& user, SceNpUserInfo* user_info);
void UserInfo_to_SceNpUserInfo2(event_data& edata, const np2_structs::UserInfo& user, SceNpUserInfo2* user_info, bool include_onlinename, bool include_avatarurl);
void RoomDataExternal_to_SceNpMatching2RoomDataExternal(event_data& edata, const np2_structs::RoomDataExternal& room, SceNpMatching2RoomDataExternal* room_info, bool include_onlinename, bool include_avatarurl);
void RoomMemberDataExternal_to_SceNpMatching2RoomMemberDataExternal(event_data& edata, const np2_structs::RoomMemberDataExternal& member, SceNpMatching2RoomMemberDataExternal* member_info, bool include_onlinename, bool include_avatarurl);
void SearchRoomResponse_to_SceNpMatching2SearchRoomResponse(event_data& edata, const np2_structs::SearchRoomResponse& resp, SceNpMatching2SearchRoomResponse* search_resp);
void GetRoomDataExternalListResponse_to_SceNpMatching2GetRoomDataExternalListResponse(event_data& edata, const np2_structs::GetRoomDataExternalListResponse& resp, SceNpMatching2GetRoomDataExternalListResponse* get_resp, bool include_onlinename, bool include_avatarurl);
void GetRoomMemberDataExternalListResponse_to_SceNpMatching2GetRoomMemberDataExternalListResponse(event_data& edata, const np2_structs::GetRoomMemberDataExternalListResponse& resp, SceNpMatching2GetRoomMemberDataExternalListResponse* get_resp, bool include_onlinename, bool include_avatarurl);
u16 RoomDataInternal_to_SceNpMatching2RoomDataInternal(event_data& edata, const np2_structs::RoomDataInternal& resp, SceNpMatching2RoomDataInternal* room_resp, const SceNpId& npid, bool include_onlinename, bool include_avatarurl);
void RoomMemberDataInternal_to_SceNpMatching2RoomMemberDataInternal(event_data& edata, const np2_structs::RoomMemberDataInternal& member_data, const SceNpMatching2RoomDataInternal* room_info, SceNpMatching2RoomMemberDataInternal* sce_member_data, bool include_onlinename, bool include_avatarurl);
void RoomMemberUpdateInfo_to_SceNpMatching2RoomMemberUpdateInfo(event_data& edata, const np2_structs::RoomMemberUpdateInfo& resp, SceNpMatching2RoomMemberUpdateInfo* room_info, bool include_onlinename, bool include_avatarurl);
@ -32,4 +34,5 @@ namespace np
void MatchingRoomList_to_SceNpMatchingRoomList(event_data& edata, const np2_structs::MatchingRoomList& resp, SceNpMatchingRoomList* room_list);
void MatchingSearchJoinRoomInfo_to_SceNpMatchingSearchJoinRoomInfo(event_data& edata, const np2_structs::MatchingSearchJoinRoomInfo& resp, SceNpMatchingSearchJoinRoomInfo* room_info);
void MatchingAttr_to_SceNpMatchingAttr(event_data& edata, const google::protobuf::RepeatedPtrField<np2_structs::MatchingAttr>& attr_list, vm::bptr<SceNpMatchingAttr>& first_attr);
void OptParam_to_SceNpMatching2SignalingOptParam(const np2_structs::OptParam& resp, SceNpMatching2SignalingOptParam* opt_param);
} // namespace np

View file

@ -117,6 +117,7 @@ void fmt_class_string<rpcn::CommandType>::format(std::string& out, u64 arg)
case rpcn::CommandType::LeaveRoom: return "LeaveRoom";
case rpcn::CommandType::SearchRoom: return "SearchRoom";
case rpcn::CommandType::GetRoomDataExternalList: return "GetRoomDataExternalList";
case rpcn::CommandType::GetRoomMemberDataExternalList: return "GetRoomMemberDataExternalList";
case rpcn::CommandType::SetRoomDataExternal: return "SetRoomDataExternal";
case rpcn::CommandType::GetRoomDataInternal: return "GetRoomDataInternal";
case rpcn::CommandType::SetRoomDataInternal: return "SetRoomDataInternal";
@ -256,7 +257,7 @@ namespace rpcn
rpcn_log.notice("online: %s, pr_com_id: %s, pr_title: %s, pr_status: %s, pr_comment: %s, pr_data: %s", online ? "true" : "false", pr_com_id.data, pr_title, pr_status, pr_comment, fmt::buf_to_hexstring(pr_data.data(), pr_data.size()));
}
constexpr u32 RPCN_PROTOCOL_VERSION = 29;
constexpr u32 RPCN_PROTOCOL_VERSION = 30;
constexpr usz RPCN_HEADER_SIZE = 15;
const char* error_to_explanation(rpcn::ErrorType error)
@ -1466,7 +1467,7 @@ namespace rpcn
return error;
}
bool rpcn_client::add_friend(const std::string& friend_username)
std::optional<ErrorType> rpcn_client::add_friend(const std::string& friend_username)
{
std::vector<u8> data;
std::copy(friend_username.begin(), friend_username.end(), std::back_inserter(data));
@ -1477,19 +1478,18 @@ namespace rpcn
std::vector<u8> packet_data;
if (!forge_send_reply(CommandType::AddFriend, req_id, data, packet_data))
{
return false;
return std::nullopt;
}
vec_stream reply(packet_data);
auto error = static_cast<ErrorType>(reply.get<u8>());
const auto error = static_cast<ErrorType>(reply.get<u8>());
if (error != rpcn::ErrorType::NoError)
{
return false;
}
if (error == ErrorType::NoError)
rpcn_log.success("add_friend(\"%s\") succeeded", friend_username);
else
rpcn_log.error("add_friend(\"%s\") failed with error: %s", error);
rpcn_log.success("You have successfully added \"%s\" as a friend", friend_username);
return true;
return error;
}
bool rpcn_client::remove_friend(const std::string& friend_username)
@ -1915,6 +1915,16 @@ namespace rpcn
return forge_request_with_com_id(serialized, communication_id, CommandType::GetRoomDataExternalList, req_id);
}
bool rpcn_client::get_room_member_data_external_list(u32 req_id, const SceNpCommunicationId& communication_id, u64 room_id)
{
std::vector<u8> data(COMMUNICATION_ID_SIZE + sizeof(u64));
rpcn_client::write_communication_id(communication_id, data);
write_to_ptr<le_t<u64>>(data, COMMUNICATION_ID_SIZE, room_id);
return forge_send(CommandType::GetRoomMemberDataExternalList, req_id, data);
}
bool rpcn_client::set_roomdata_external(u32 req_id, const SceNpCommunicationId& communication_id, const SceNpMatching2SetRoomDataExternalRequest* req)
{
np2_structs::SetRoomDataExternalRequest pb_req;
@ -2171,7 +2181,7 @@ namespace rpcn
bool rpcn_client::send_message(const message_data& msg_data, const std::set<std::string>& npids)
{
np2_structs::MessageDetails pb_message;
pb_message.set_communicationid(static_cast<const char*>(msg_data.commId.data));
pb_message.set_communicationid(np::communication_id_to_string(msg_data.commId));
pb_message.set_msgid(msg_data.msgId);
pb_message.mutable_maintype()->set_value(msg_data.mainType);
pb_message.mutable_subtype()->set_value(msg_data.subType);
@ -3122,11 +3132,13 @@ namespace rpcn
if (sdata.is_error())
{
rpcn_log.error("Error parsing MessageReceived notification");
return;
}
if (pb_mdata->communicationid().empty() || pb_mdata->communicationid().size() > 9 ||
pb_mdata->subject().empty() || pb_mdata->body().empty())
const auto communication_id = np::string_to_communication_id(pb_mdata->communicationid());
if (!communication_id)
{
rpcn_log.warning("Discarded invalid message!");
return;
@ -3140,9 +3152,12 @@ namespace rpcn
.subject = pb_mdata->subject(),
.body = pb_mdata->body()};
strcpy_trunc(mdata.commId.data, pb_mdata->communicationid());
mdata.commId = *communication_id;
mdata.data.assign(pb_mdata->data().begin(), pb_mdata->data().end());
rpcn_log.notice("Received message from %s:", sender);
mdata.print();
// Save the message and call callbacks
{
std::lock_guard lock(mutex_messages);

View file

@ -293,7 +293,7 @@ namespace rpcn
ErrorType send_reset_token(std::string_view npid, std::string_view email);
ErrorType reset_password(std::string_view npid, std::string_view token, std::string_view password);
ErrorType delete_account();
bool add_friend(const std::string& friend_username);
std::optional<ErrorType> add_friend(const std::string& friend_username);
bool remove_friend(const std::string& friend_username);
u32 get_num_friends();
@ -329,6 +329,7 @@ namespace rpcn
bool leave_room(u32 req_id, const SceNpCommunicationId& communication_id, const SceNpMatching2LeaveRoomRequest* req);
bool search_room(u32 req_id, const SceNpCommunicationId& communication_id, const SceNpMatching2SearchRoomRequest* req);
bool get_roomdata_external_list(u32 req_id, const SceNpCommunicationId& communication_id, const SceNpMatching2GetRoomDataExternalListRequest* req);
bool get_room_member_data_external_list(u32 req_id, const SceNpCommunicationId& communication_id, u64 room_id);
bool set_roomdata_external(u32 req_id, const SceNpCommunicationId& communication_id, const SceNpMatching2SetRoomDataExternalRequest* req);
bool get_roomdata_internal(u32 req_id, const SceNpCommunicationId& communication_id, const SceNpMatching2GetRoomDataInternalRequest* req);
bool set_roomdata_internal(u32 req_id, const SceNpCommunicationId& communication_id, const SceNpMatching2SetRoomDataInternalRequest* req);

View file

@ -10,7 +10,7 @@ struct cfg_rpcn : cfg::node
cfg::string password{this, "Password", ""};
cfg::string token{this, "Token", ""};
cfg::string hosts{this, "Hosts", "Official RPCN Server|np.rpcs3.net"};
cfg::_bool ipv6_support{this, "IPv6 support", true};
cfg::_bool ipv6_support{this, "Experimental IPv6 support", false};
void load();
void save() const;

View file

@ -68,6 +68,7 @@ namespace rpcn
GetRoomInfoGUI,
QuickMatchGUI,
SearchJoinRoomGUI,
GetRoomMemberDataExternalList,
};
enum class NotificationType : u16

View file

@ -1351,7 +1351,7 @@ void GLGSRender::notify_tile_unbound(u32 tile)
}
}
bool GLGSRender::release_GCM_label(u32 address, u32 args)
bool GLGSRender::release_GCM_label(u32 type, u32 address, u32 args)
{
if (!backend_config.supports_host_gpu_labels)
{
@ -1360,7 +1360,7 @@ bool GLGSRender::release_GCM_label(u32 address, u32 args)
auto host_ctx = ensure(m_host_dma_ctrl->host_ctx());
if (host_ctx->texture_loads_completed())
if (type == NV4097_TEXTURE_READ_SEMAPHORE_RELEASE && host_ctx->texture_loads_completed())
{
// We're about to poll waiting for GPU state, ensure the context is still valid.
gl::check_state();

View file

@ -206,7 +206,7 @@ public:
void discard_occlusion_query(rsx::reports::occlusion_query_info* query) override;
// DMA
bool release_GCM_label(u32 address, u32 data) override;
bool release_GCM_label(u32 type, u32 address, u32 data) override;
void enqueue_host_context_write(u32 offset, u32 size, const void* data);
void on_guest_texture_read();

View file

@ -4,6 +4,7 @@
#include "Emu/RSX/RSXThread.h"
#include "Emu/RSX/Core/RSXReservationLock.hpp"
#include "Emu/RSX/Common/tiled_dma_copy.hpp"
#include "Emu/RSX/Host/MM.h"
#include "context_accessors.define.h"
@ -581,9 +582,11 @@ namespace rsx
const u16 out_h = REGS(ctx)->blit_engine_output_height();
// Lock here. RSX cannot execute any locking operations from this point, including ZCULL read barriers
const u32 read_length = src.pitch * src.height;
const u32 write_length = dst.pitch * dst.clip_height;
auto res = ::rsx::reservation_lock<true>(
dst.rsx_address, dst.pitch * dst.clip_height,
src.rsx_address, src.pitch * src.height);
dst.rsx_address, write_length,
src.rsx_address, read_length);
if (!g_cfg.video.force_cpu_blit_processing &&
(dst.dma == CELL_GCM_CONTEXT_DMA_MEMORY_FRAME_BUFFER || src.dma == CELL_GCM_CONTEXT_DMA_MEMORY_FRAME_BUFFER) &&
@ -593,6 +596,14 @@ namespace rsx
return;
}
// Conservative MM flush
rsx::simple_array<utils::address_range64> flush_mm_ranges =
{
utils::address_range64::start_length(reinterpret_cast<u64>(dst.pixels), write_length),
utils::address_range64::start_length(reinterpret_cast<u64>(src.pixels), read_length)
};
rsx::mm_flush(flush_mm_ranges);
std::vector<u8> mirror_tmp;
bool src_is_temp = false;
@ -619,7 +630,7 @@ namespace rsx
const bool interpolate = in_inter == blit_engine::transfer_interpolator::foh;
auto real_dst = dst.pixels;
const auto tiled_region = RSX(ctx)->get_tiled_memory_region(utils::address_range32::start_length(dst.rsx_address, dst.pitch * dst.clip_height));
const auto tiled_region = RSX(ctx)->get_tiled_memory_region(utils::address_range32::start_length(dst.rsx_address, write_length));
std::vector<u8> tmp;
if (tiled_region)

View file

@ -86,7 +86,7 @@ namespace rsx
RSX(ctx)->performance_counters.idle_time += (get_system_time() - start);
}
void semaphore_release(context* ctx, u32 /*reg*/, u32 arg)
void semaphore_release(context* ctx, u32 reg, u32 arg)
{
const u32 offset = REGS(ctx)->semaphore_offset_406e();
@ -122,7 +122,7 @@ namespace rsx
arg = 1;
}
util::write_gcm_label<false, true>(ctx, addr, arg);
util::write_gcm_label<false, true>(ctx, reg, addr, arg);
}
}
}

View file

@ -690,7 +690,7 @@ namespace rsx
});
}
void texture_read_semaphore_release(context* ctx, u32 /*reg*/, u32 arg)
void texture_read_semaphore_release(context* ctx, u32 reg, u32 arg)
{
// Pipeline barrier seems to be equivalent to a SHADER_READ stage barrier.
// Ideally the GPU only needs to have cached all textures declared up to this point before writing the label.
@ -715,15 +715,15 @@ namespace rsx
if (g_cfg.video.strict_rendering_mode) [[ unlikely ]]
{
util::write_gcm_label<true, true>(ctx, addr, arg);
util::write_gcm_label<true, true>(ctx, reg, addr, arg);
}
else
{
util::write_gcm_label<true, false>(ctx, addr, arg);
util::write_gcm_label<true, false>(ctx, reg, addr, arg);
}
}
void back_end_write_semaphore_release(context* ctx, u32 /*reg*/, u32 arg)
void back_end_write_semaphore_release(context* ctx, u32 reg, u32 arg)
{
// Full pipeline barrier. GPU must flush pipeline before writing the label
@ -744,7 +744,7 @@ namespace rsx
}
const u32 val = (arg & 0xff00ff00) | ((arg & 0xff) << 16) | ((arg >> 16) & 0xff);
util::write_gcm_label<true, true>(ctx, addr, val);
util::write_gcm_label<true, true>(ctx, reg, addr, val);
}
void sync(context* ctx, u32, u32)

View file

@ -13,13 +13,13 @@ namespace rsx
namespace util
{
template <bool FlushDMA, bool FlushPipe>
static void write_gcm_label(context* ctx, u32 address, u32 data)
static void write_gcm_label(context* ctx, u32 type, u32 address, u32 data)
{
const bool is_flip_sema = (address == (RSX(ctx)->label_addr + 0x10) || address == (RSX(ctx)->device_addr + 0x30));
if (!is_flip_sema)
{
// First, queue the GPU work. If it flushes the queue for us, the following routines will be faster.
const bool handled = RSX(ctx)->get_backend_config().supports_host_gpu_labels && RSX(ctx)->release_GCM_label(address, data);
const bool handled = RSX(ctx)->get_backend_config().supports_host_gpu_labels && RSX(ctx)->release_GCM_label(type, address, data);
if (vm::_ref<RsxSemaphore>(address) == data)
{

View file

@ -380,7 +380,7 @@ namespace rsx
flags32_t read_barrier(u32 memory_address, u32 memory_range, bool unconditional);
virtual void write_barrier(u32 /*memory_address*/, u32 /*memory_range*/) {}
virtual void sync_hint(FIFO::interrupt_hint hint, reports::sync_hint_payload_t payload);
virtual bool release_GCM_label(u32 /*address*/, u32 /*value*/) { return false; }
virtual bool release_GCM_label(u32 /*type*/, u32 /*address*/, u32 /*value*/) { return false; }
protected:

View file

@ -74,16 +74,20 @@ namespace vk
// This was used in a cyclic ref before, but is missing a barrier
// No need for a full stall, use a custom barrier instead
VkPipelineStageFlags src_stage;
VkAccessFlags src_access;
VkAccessFlags src_access, dst_access;
if (raw->aspect() == VK_IMAGE_ASPECT_COLOR_BIT)
{
src_stage = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
src_access = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
dst_access = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_READ_BIT;
dst_stage |= VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
}
else
{
src_stage = VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT;
src_access = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;
dst_access = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT;
dst_stage |= VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT;
}
vk::insert_image_memory_barrier(
@ -91,7 +95,7 @@ namespace vk
raw->value,
raw->current_layout, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
src_stage, dst_stage,
src_access, VK_ACCESS_SHADER_READ_BIT,
src_access, dst_access,
{ raw->aspect(), 0, 1, 0, 1 });
raw->current_layout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
@ -139,6 +143,17 @@ VkRenderPass VKGSRender::get_render_pass()
return m_cached_renderpass;
}
void VKGSRender::invalidate_render_pass()
{
// Regenerate renderpass key for the next draw call
if (const auto key = vk::get_renderpass_key(m_fbo_images, m_current_renderpass_key);
key != m_current_renderpass_key)
{
m_current_renderpass_key = key;
m_cached_renderpass = VK_NULL_HANDLE;
}
}
void VKGSRender::update_draw_state()
{
m_profiler.start();
@ -510,12 +525,7 @@ void VKGSRender::load_texture_env()
if (check_for_cyclic_refs)
{
// Regenerate renderpass key
if (const auto key = vk::get_renderpass_key(m_fbo_images, m_current_renderpass_key);
key != m_current_renderpass_key)
{
m_current_renderpass_key = key;
m_cached_renderpass = VK_NULL_HANDLE;
}
invalidate_render_pass();
}
if (backend_config.supports_asynchronous_compute)
@ -1065,6 +1075,9 @@ void VKGSRender::end()
// Since we're ending the subpass, might as well restore DCC/HiZ for extra performance
ds->change_layout(*m_current_command_buffer, VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL);
ds->reset_surface_counters();
// Regenerate render pass key
invalidate_render_pass();
}
}

View file

@ -505,9 +505,6 @@ VKGSRender::VKGSRender(utils::serial* ar) noexcept : GSRender(ar)
m_occlusion_query_manager->set_control_flags(VK_QUERY_CONTROL_PRECISE_BIT, 0);
}
VkSemaphoreCreateInfo semaphore_info = {};
semaphore_info.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;
// VRAM allocation
// This first set is bound persistently, so grow notifications are enabled.
m_attrib_ring_info.create(VK_BUFFER_USAGE_UNIFORM_TEXEL_BUFFER_BIT, VK_ATTRIB_RING_BUFFER_SIZE_M * 0x100000, vk::heap_pool_default, "attrib buffer", 0x400000, VK_TRUE);
@ -570,10 +567,13 @@ VKGSRender::VKGSRender(utils::serial* ar) noexcept : GSRender(ar)
rsx_log.warning("Current driver may crash due to memory limitations (%uk)", m_texbuffer_view_size / 1024);
}
for (auto &ctx : frame_context_storage)
m_max_async_frames = m_swapchain->get_swap_image_count();
m_frame_context_storage.resize(m_max_async_frames);
m_current_frame = &m_frame_context_storage[0];
for (auto& ctx : m_frame_context_storage)
{
vkCreateSemaphore((*m_device), &semaphore_info, nullptr, &ctx.present_wait_semaphore);
vkCreateSemaphore((*m_device), &semaphore_info, nullptr, &ctx.acquire_signal_semaphore);
ctx.init(*m_device);
}
const auto& memory_map = m_device->get_memory_mapping();
@ -612,8 +612,6 @@ VKGSRender::VKGSRender(utils::serial* ar) noexcept : GSRender(ar)
}
m_current_frame = &frame_context_storage[0];
m_texture_cache.initialize((*m_device), m_device->get_graphics_queue(),
m_texture_upload_buffer_ring_info);
@ -830,16 +828,17 @@ VKGSRender::~VKGSRender()
if (m_current_frame == &m_aux_frame_context)
{
// Return resources back to the owner
m_current_frame = &frame_context_storage[m_current_queue_index];
m_current_frame = &m_frame_context_storage[m_current_queue_index];
m_current_frame->grab_resources(m_aux_frame_context);
}
// NOTE: aux_context uses descriptor pools borrowed from the main queues and any allocations will be automatically freed when pool is destroyed
for (auto &ctx : frame_context_storage)
// CPU frame contexts
for (auto &ctx : m_frame_context_storage)
{
vkDestroySemaphore((*m_device), ctx.present_wait_semaphore, nullptr);
vkDestroySemaphore((*m_device), ctx.acquire_signal_semaphore, nullptr);
ctx.destroy(*m_device);
}
m_current_frame = nullptr;
m_frame_context_storage.clear();
// Textures
m_rtts.destroy();
@ -1542,7 +1541,7 @@ std::pair<volatile vk::host_data_t*, VkBuffer> VKGSRender::map_host_object_data(
return { m_host_dma_ctrl->host_ctx(), m_host_object_data->value };
}
bool VKGSRender::release_GCM_label(u32 address, u32 args)
bool VKGSRender::release_GCM_label(u32 type, u32 address, u32 args)
{
if (!backend_config.supports_host_gpu_labels)
{
@ -1551,7 +1550,7 @@ bool VKGSRender::release_GCM_label(u32 address, u32 args)
auto host_ctx = ensure(m_host_dma_ctrl->host_ctx());
if (host_ctx->texture_loads_completed())
if (type == NV4097_TEXTURE_READ_SEMAPHORE_RELEASE && host_ctx->texture_loads_completed())
{
// All texture loads already seen by the host GPU
// Wait for all previously submitted labels to be flushed
@ -1573,13 +1572,10 @@ bool VKGSRender::release_GCM_label(u32 address, u32 args)
const auto release_event_id = host_ctx->on_label_acquire();
vk::insert_global_memory_barrier(*m_current_command_buffer);
if (host_ctx->has_unflushed_texture_loads())
{
if (vk::is_renderpass_open(*m_current_command_buffer))
{
vk::end_renderpass(*m_current_command_buffer);
}
vkCmdUpdateBuffer(*m_current_command_buffer, mapping.second->value, mapping.first, 4, &write_data);
flush_command_queue();
}

View file

@ -159,8 +159,9 @@ private:
u64 m_texture_parameters_dynamic_offset = 0;
u64 m_stipple_array_dynamic_offset = 0;
std::array<vk::frame_context_t, VK_MAX_ASYNC_FRAMES> frame_context_storage;
//Temp frame context to use if the real frame queue is overburdened. Only used for storage
std::vector<vk::frame_context_t> m_frame_context_storage;
u32 m_max_async_frames = 0u;
// Temp frame context to use if the real frame queue is overburdened. Only used for storage
vk::frame_context_t m_aux_frame_context;
u32 m_current_queue_index = 0;
@ -220,13 +221,14 @@ private:
void frame_context_cleanup(vk::frame_context_t *ctx);
void advance_queued_frames();
void present(vk::frame_context_t *ctx);
void reinitialize_swapchain();
bool reinitialize_swapchain();
vk::viewable_image* get_present_source(vk::present_surface_info* info, const rsx::avconf& avconfig);
void begin_render_pass();
void close_render_pass();
VkRenderPass get_render_pass();
void invalidate_render_pass();
void update_draw_state();
void check_present_status();
@ -252,7 +254,7 @@ public:
// Sync
void write_barrier(u32 address, u32 range) override;
void sync_hint(rsx::FIFO::interrupt_hint hint, rsx::reports::sync_hint_payload_t payload) override;
bool release_GCM_label(u32 address, u32 data) override;
bool release_GCM_label(u32 type, u32 address, u32 data) override;
void begin_occlusion_query(rsx::reports::occlusion_query_info* query) override;
void end_occlusion_query(rsx::reports::occlusion_query_info* query) override;

View file

@ -23,7 +23,6 @@
#define VK_INDEX_RING_BUFFER_SIZE_M 16
#define VK_MAX_ASYNC_CB_COUNT 512
#define VK_MAX_ASYNC_FRAMES 2
#define FRAME_PRESENT_TIMEOUT 10000000ull // 10 seconds
#define GENERAL_WAIT_TIMEOUT 2000000ull // 2 seconds
@ -186,6 +185,20 @@ namespace vk
data_heap_manager::managed_heap_snapshot_t heap_snapshot;
u64 last_frame_sync_time = 0;
void init(VkDevice dev)
{
VkSemaphoreCreateInfo semaphore_info = {};
semaphore_info.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;
vkCreateSemaphore(dev, &semaphore_info, nullptr, &present_wait_semaphore);
vkCreateSemaphore(dev, &semaphore_info, nullptr, &acquire_signal_semaphore);
}
void destroy(VkDevice dev)
{
vkDestroySemaphore(dev, present_wait_semaphore, nullptr);
vkDestroySemaphore(dev, acquire_signal_semaphore, nullptr);
}
// Copy shareable information
void grab_resources(frame_context_t& other)
{

View file

@ -90,6 +90,7 @@ namespace vk
g_drv_no_primitive_restart = false;
g_drv_sanitize_fp_values = false;
g_drv_disable_fence_reset = false;
g_drv_strict_query_scopes = !!g_cfg.video.strict_rendering_mode;
g_drv_emulate_cond_render = (g_cfg.video.relaxed_zcull_sync && !g_render_device->get_conditional_render_support());
g_num_processed_frames = 0;
g_num_total_frames = 0;

View file

@ -33,7 +33,7 @@ namespace
}
}
void VKGSRender::reinitialize_swapchain()
bool VKGSRender::reinitialize_swapchain()
{
m_swapchain_dims.width = m_frame->client_width();
m_swapchain_dims.height = m_frame->client_height();
@ -44,7 +44,7 @@ void VKGSRender::reinitialize_swapchain()
if (m_swapchain_dims.width == 0 || m_swapchain_dims.height == 0)
{
swapchain_unavailable = true;
return;
return false;
}
// NOTE: This operation will create a hard sync point
@ -52,7 +52,7 @@ void VKGSRender::reinitialize_swapchain()
m_current_command_buffer->reset();
m_current_command_buffer->begin();
for (auto &ctx : frame_context_storage)
for (auto &ctx : m_frame_context_storage)
{
if (ctx.present_image == umax)
continue;
@ -61,20 +61,55 @@ void VKGSRender::reinitialize_swapchain()
frame_context_cleanup(&ctx);
}
// NOTE: frame_context_cleanup alters the queued_frames structure.
while (!m_queued_frames.empty())
{
auto& frame = m_queued_frames.front();
if (!frame->swap_command_buffer)
{
// Drop it
m_queued_frames.pop_front();
continue;
}
frame_context_cleanup(frame);
}
ensure(m_queued_frames.empty());
// Discard the current upscaling pipeline if any
m_upscaler.reset();
// Drain all the queues
vkDeviceWaitIdle(*m_device);
// Reset frame context storage
for (auto& ctx : m_frame_context_storage)
{
ctx.destroy(*m_device);
}
m_current_frame = nullptr;
m_max_async_frames = 0;
m_current_queue_index = 0;
m_frame_context_storage.clear();
// Rebuild swapchain. Old swapchain destruction is handled by the init_swapchain call
if (!m_swapchain->init(m_swapchain_dims.width, m_swapchain_dims.height))
{
rsx_log.warning("Swapchain initialization failed. Request ignored [%dx%d]", m_swapchain_dims.width, m_swapchain_dims.height);
swapchain_unavailable = true;
return;
return false;
}
// Re-initialize CPU frame contexts
m_max_async_frames = m_swapchain->get_swap_image_count();
m_frame_context_storage.resize(m_max_async_frames);
for (auto& ctx : m_frame_context_storage)
{
ctx.init(*m_device);
}
m_current_queue_index = 0;
m_current_frame = &m_frame_context_storage[0];
// Prepare new swapchain images for use
for (u32 i = 0; i < m_swapchain->get_swap_image_count(); ++i)
{
@ -100,6 +135,7 @@ void VKGSRender::reinitialize_swapchain()
swapchain_unavailable = false;
should_reinitialize_swapchain = false;
return true;
}
void VKGSRender::present(vk::frame_context_t *ctx)
@ -158,10 +194,10 @@ void VKGSRender::advance_queued_frames()
m_current_frame->tag_frame_end();
m_queued_frames.push_back(m_current_frame);
ensure(m_queued_frames.size() <= VK_MAX_ASYNC_FRAMES);
ensure(m_queued_frames.size() <= m_max_async_frames);
m_current_queue_index = (m_current_queue_index + 1) % VK_MAX_ASYNC_FRAMES;
m_current_frame = &frame_context_storage[m_current_queue_index];
m_current_queue_index = (m_current_queue_index + 1) % m_max_async_frames;
m_current_frame = &m_frame_context_storage[m_current_queue_index];
m_current_frame->flags |= frame_context_state::dirty;
vk::advance_frame_counter();
@ -391,14 +427,35 @@ void VKGSRender::flip(const rsx::display_flip_info_t& info)
if (swapchain_unavailable || should_reinitialize_swapchain)
{
reinitialize_swapchain();
// Reinitializing the swapchain is a failable operation. However, not all failures are fatal (e.g minimized window).
// In the worst case, we can have the driver refuse to create the swapchain while we already deleted the previous one.
// In such scenarios, we have to retry a few times before giving up as we cannot proceed without a swapchain.
for (int i = 0; i < 10; ++i)
{
if (reinitialize_swapchain() || m_current_frame)
{
// If m_current_frame exists, then the initialization failure is non-fatal. Proceed as usual.
break;
}
if (Emu.IsStopped())
{
m_frame->flip(m_context);
rsx::thread::flip(info);
return;
}
std::this_thread::sleep_for(100ms);
}
}
m_profiler.start();
ensure(m_current_frame, "Invalid swapchain setup. Resizing the game window failed.");
if (m_current_frame == &m_aux_frame_context)
{
m_current_frame = &frame_context_storage[m_current_queue_index];
m_current_frame = &m_frame_context_storage[m_current_queue_index];
if (m_current_frame->swap_command_buffer)
{
// Its possible this flip request is triggered by overlays and the flip queue is in undefined state
@ -520,7 +577,7 @@ void VKGSRender::flip(const rsx::display_flip_info_t& info)
ensure(m_current_frame->present_image == umax);
ensure(m_current_frame->swap_command_buffer == nullptr);
u64 timeout = m_swapchain->get_swap_image_count() <= VK_MAX_ASYNC_FRAMES? 0ull: 100000000ull;
u64 timeout = m_swapchain->get_swap_image_count() <= 2? 0ull: 100000000ull;
while (VkResult status = m_swapchain->acquire_next_swapchain_image(m_current_frame->acquire_signal_semaphore, timeout, &m_current_frame->present_image))
{
switch (status)
@ -547,6 +604,7 @@ void VKGSRender::flip(const rsx::display_flip_info_t& info)
rsx_log.warning("vkAcquireNextImageKHR failed with VK_ERROR_OUT_OF_DATE_KHR. Flip request ignored until surface is recreated.");
swapchain_unavailable = true;
reinitialize_swapchain();
ensure(m_current_frame, "Could not reinitialize swapchain after VK_ERROR_OUT_OF_DATE_KHR signal!");
continue;
default:
vk::die_with_error(status);
@ -585,6 +643,109 @@ void VKGSRender::flip(const rsx::display_flip_info_t& info)
vk::framebuffer_holder* direct_fbo = nullptr;
rsx::simple_array<vk::viewable_image*> calibration_src;
const bool has_overlay = (m_overlay_manager && m_overlay_manager->has_visible());
const bool user_asked_for_screenshot = g_user_asked_for_screenshot.exchange(false);
const bool user_is_recording = (g_recording_mode != recording_mode::stopped && m_frame->can_consume_frame());
const bool need_media_capture = user_asked_for_screenshot || user_is_recording;
const auto render_overlays = [&](vk::framebuffer_holder* fbo, const areau& area)
{
if (!has_overlay) return;
// Lock to avoid modification during run-update chain
auto ui_renderer = vk::get_overlay_pass<vk::ui_overlay_renderer>();
std::lock_guard lock(*m_overlay_manager);
for (const auto& view : m_overlay_manager->get_views())
{
ui_renderer->run(*m_current_command_buffer, area, fbo, single_target_pass, m_texture_upload_buffer_ring_info, *view.get());
}
};
// WARNING: We have to do this here. We cannot touch the acquired image on the CB and then do a hard sync on it before it is submitted to the presentation engine.
// That introduces a WRITE_AFTER_PRESENT (from the previous present) when we later try to present on a different CB
if (image_to_flip && need_media_capture)
{
const usz sshot_size = buffer_height * buffer_width * 4;
vk::buffer sshot_vkbuf(*m_device, utils::align(sshot_size, 0x100000), m_device->get_memory_mapping().host_visible_coherent,
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, VK_BUFFER_USAGE_TRANSFER_DST_BIT, 0, VMM_ALLOCATION_POOL_UNDEFINED);
VkBufferImageCopy copy_info{};
copy_info.bufferOffset = 0;
copy_info.bufferRowLength = 0;
copy_info.bufferImageHeight = 0;
copy_info.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
copy_info.imageSubresource.baseArrayLayer = 0;
copy_info.imageSubresource.layerCount = 1;
copy_info.imageSubresource.mipLevel = 0;
copy_info.imageOffset.x = 0;
copy_info.imageOffset.y = 0;
copy_info.imageOffset.z = 0;
copy_info.imageExtent.width = buffer_width;
copy_info.imageExtent.height = buffer_height;
copy_info.imageExtent.depth = 1;
vk::image* image_to_copy = image_to_flip;
if (g_cfg.video.record_with_overlays && has_overlay)
{
const auto key = vk::get_renderpass_key(m_swapchain->get_surface_format());
single_target_pass = vk::get_renderpass(*m_device, key);
ensure(single_target_pass != VK_NULL_HANDLE);
if (!m_overlay_recording_img ||
m_overlay_recording_img->type() != image_to_flip->type() ||
m_overlay_recording_img->format() != image_to_flip->format() ||
m_overlay_recording_img->width() != image_to_flip->width() ||
m_overlay_recording_img->height() != image_to_flip->height() ||
m_overlay_recording_img->layers() != image_to_flip->layers())
{
m_overlay_recording_img = std::make_unique<vk::image>(*m_device, m_device->get_memory_mapping().device_local, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT,
image_to_flip->type(), image_to_flip->format(), image_to_flip->width(), image_to_flip->height(), 1, 1, image_to_flip->layers(), VK_SAMPLE_COUNT_1_BIT,
VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_TILING_OPTIMAL, VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_SAMPLED_BIT,
0, VMM_ALLOCATION_POOL_UNDEFINED);
}
m_overlay_recording_img->change_layout(*m_current_command_buffer, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);
image_to_flip->push_layout(*m_current_command_buffer, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL);
const areai rect = areai(0, 0, buffer_width, buffer_height);
vk::copy_image(*m_current_command_buffer, image_to_flip, m_overlay_recording_img.get(), rect, rect, 1);
image_to_flip->pop_layout(*m_current_command_buffer);
m_overlay_recording_img->change_layout(*m_current_command_buffer, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL);
vk::framebuffer_holder* sshot_fbo = vk::get_framebuffer(*m_device, buffer_width, buffer_height, VK_FALSE, single_target_pass, { m_overlay_recording_img.get() });
sshot_fbo->add_ref();
render_overlays(sshot_fbo, areau(rect));
sshot_fbo->release();
image_to_copy = m_overlay_recording_img.get();
}
image_to_copy->push_layout(*m_current_command_buffer, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL);
vk::copy_image_to_buffer(*m_current_command_buffer, image_to_copy, &sshot_vkbuf, copy_info);
image_to_copy->pop_layout(*m_current_command_buffer);
flush_command_queue(true);
const auto src = sshot_vkbuf.map(0, sshot_size);
std::vector<u8> sshot_frame(sshot_size);
memcpy(sshot_frame.data(), src, sshot_size);
sshot_vkbuf.unmap();
const bool is_bgra = image_to_copy->format() == VK_FORMAT_B8G8R8A8_UNORM;
if (user_asked_for_screenshot)
{
m_frame->take_screenshot(std::move(sshot_frame), buffer_width, buffer_height, is_bgra);
}
else
{
m_frame->present_frame(std::move(sshot_frame), buffer_width * 4, buffer_width, buffer_height, is_bgra);
}
}
if (!image_to_flip || aspect_ratio.x1 || aspect_ratio.y1)
{
// Clear the window background to black
@ -592,6 +753,19 @@ void VKGSRender::flip(const rsx::display_flip_info_t& info)
vk::change_image_layout(*m_current_command_buffer, target_image, present_layout, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, subresource_range);
vkCmdClearColorImage(*m_current_command_buffer, target_image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, &clear_black, 1, &subresource_range);
// Prevent WAW on transfer writes
vk::insert_image_memory_barrier(
*m_current_command_buffer,
target_image,
target_layout,
target_layout,
VK_PIPELINE_STAGE_TRANSFER_BIT,
VK_PIPELINE_STAGE_TRANSFER_BIT,
VK_ACCESS_TRANSFER_WRITE_BIT,
VK_ACCESS_TRANSFER_WRITE_BIT,
subresource_range
);
target_layout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
}
@ -615,21 +789,6 @@ void VKGSRender::flip(const rsx::display_flip_info_t& info)
}
}
const bool has_overlay = (m_overlay_manager && m_overlay_manager->has_visible());
const auto render_overlays = [&](vk::framebuffer_holder* fbo, const areau& area)
{
if (!has_overlay) return;
// Lock to avoid modification during run-update chain
auto ui_renderer = vk::get_overlay_pass<vk::ui_overlay_renderer>();
std::lock_guard lock(*m_overlay_manager);
for (const auto& view : m_overlay_manager->get_views())
{
ui_renderer->run(*m_current_command_buffer, area, fbo, single_target_pass, m_texture_upload_buffer_ring_info, *view.get());
}
};
if (image_to_flip)
{
const bool use_full_rgb_range_output = g_cfg.video.full_rgb_range_output.get();
@ -693,90 +852,6 @@ void VKGSRender::flip(const rsx::display_flip_info_t& info)
m_upscaler->scale_output(*m_current_command_buffer, image_to_flip, target_image, target_layout, rgn, UPSCALE_AND_COMMIT | UPSCALE_DEFAULT_VIEW);
}
const bool user_asked_for_screenshot = g_user_asked_for_screenshot.exchange(false);
if (user_asked_for_screenshot || (g_recording_mode != recording_mode::stopped && m_frame->can_consume_frame()))
{
const usz sshot_size = buffer_height * buffer_width * 4;
vk::buffer sshot_vkbuf(*m_device, utils::align(sshot_size, 0x100000), m_device->get_memory_mapping().host_visible_coherent,
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, VK_BUFFER_USAGE_TRANSFER_DST_BIT, 0, VMM_ALLOCATION_POOL_UNDEFINED);
VkBufferImageCopy copy_info {};
copy_info.bufferOffset = 0;
copy_info.bufferRowLength = 0;
copy_info.bufferImageHeight = 0;
copy_info.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
copy_info.imageSubresource.baseArrayLayer = 0;
copy_info.imageSubresource.layerCount = 1;
copy_info.imageSubresource.mipLevel = 0;
copy_info.imageOffset.x = 0;
copy_info.imageOffset.y = 0;
copy_info.imageOffset.z = 0;
copy_info.imageExtent.width = buffer_width;
copy_info.imageExtent.height = buffer_height;
copy_info.imageExtent.depth = 1;
vk::image* image_to_copy = image_to_flip;
if (g_cfg.video.record_with_overlays && has_overlay)
{
const auto key = vk::get_renderpass_key(m_swapchain->get_surface_format());
single_target_pass = vk::get_renderpass(*m_device, key);
ensure(single_target_pass != VK_NULL_HANDLE);
if (!m_overlay_recording_img ||
m_overlay_recording_img->type() != image_to_flip->type() ||
m_overlay_recording_img->format() != image_to_flip->format() ||
m_overlay_recording_img->width() != image_to_flip->width() ||
m_overlay_recording_img->height() != image_to_flip->height() ||
m_overlay_recording_img->layers() != image_to_flip->layers())
{
m_overlay_recording_img = std::make_unique<vk::image>(*m_device, m_device->get_memory_mapping().device_local, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT,
image_to_flip->type(), image_to_flip->format(), image_to_flip->width(), image_to_flip->height(), 1, 1, image_to_flip->layers(), VK_SAMPLE_COUNT_1_BIT,
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_TILING_OPTIMAL, VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_SAMPLED_BIT,
0, VMM_ALLOCATION_POOL_UNDEFINED);
}
m_overlay_recording_img->change_layout(*m_current_command_buffer, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);
image_to_flip->push_layout(*m_current_command_buffer, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL);
const areai rect = areai(0, 0, buffer_width, buffer_height);
vk::copy_image(*m_current_command_buffer, image_to_flip, m_overlay_recording_img.get(), rect, rect, 1);
image_to_flip->pop_layout(*m_current_command_buffer);
m_overlay_recording_img->change_layout(*m_current_command_buffer, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL);
vk::framebuffer_holder* sshot_fbo = vk::get_framebuffer(*m_device, buffer_width, buffer_height, VK_FALSE, single_target_pass, { m_overlay_recording_img.get() });
sshot_fbo->add_ref();
render_overlays(sshot_fbo, areau(rect));
sshot_fbo->release();
image_to_copy = m_overlay_recording_img.get();
}
image_to_copy->push_layout(*m_current_command_buffer, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL);
vk::copy_image_to_buffer(*m_current_command_buffer, image_to_copy, &sshot_vkbuf, copy_info);
image_to_copy->pop_layout(*m_current_command_buffer);
flush_command_queue(true);
const auto src = sshot_vkbuf.map(0, sshot_size);
std::vector<u8> sshot_frame(sshot_size);
memcpy(sshot_frame.data(), src, sshot_size);
sshot_vkbuf.unmap();
const bool is_bgra = image_to_copy->format() == VK_FORMAT_B8G8R8A8_UNORM;
if (user_asked_for_screenshot)
{
m_frame->take_screenshot(std::move(sshot_frame), buffer_width, buffer_height, is_bgra);
}
else
{
m_frame->present_frame(std::move(sshot_frame), buffer_width * 4, buffer_width, buffer_height, is_bgra);
}
}
}
if (g_cfg.video.debug_overlay || has_overlay)
@ -790,7 +865,7 @@ void VKGSRender::flip(const rsx::display_flip_info_t& info)
barrier.oldLayout = target_layout;
barrier.image = target_image;
barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
barrier.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
barrier.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | VK_ACCESS_COLOR_ATTACHMENT_READ_BIT;
barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
barrier.subresourceRange = subresource_range;

View file

@ -1183,6 +1183,13 @@ namespace vk
{
ensure(scratch_buf);
// WAW hazard - complete previous work before executing any transfers
insert_buffer_memory_barrier(
cmd2, scratch_buf->value, 0, scratch_offset,
VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT,
VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT,
VK_ACCESS_TRANSFER_WRITE_BIT);
if (upload_commands.size() > 1)
{
auto range_ptr = buffer_copies.data();
@ -1197,8 +1204,11 @@ namespace vk
vkCmdCopyBuffer(cmd2, upload_buffer->value, scratch_buf->value, static_cast<u32>(buffer_copies.size()), buffer_copies.data());
}
insert_buffer_memory_barrier(cmd2, scratch_buf->value, 0, scratch_offset, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT,
VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT);
insert_buffer_memory_barrier(
cmd2, scratch_buf->value, 0, scratch_offset,
VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT,
VK_ACCESS_TRANSFER_WRITE_BIT,
VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT);
}
// Swap and deswizzle if requested

View file

@ -94,19 +94,25 @@ namespace vk
vk::end_renderpass(cmd);
}
VkAccessFlags src_access;
VkPipelineStageFlags src_stage;
VkAccessFlags src_access, dst_access;
VkPipelineStageFlags src_stage, dst_stage;
if (range.aspectMask == VK_IMAGE_ASPECT_COLOR_BIT)
{
src_access = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
dst_access = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_READ_BIT;
src_stage = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
dst_stage = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
}
else
{
src_access = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;
dst_access = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT;
src_stage = VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT;
dst_stage = VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT;
}
dst_stage |= VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | VK_PIPELINE_STAGE_VERTEX_SHADER_BIT;
VkImageMemoryBarrier barrier = {};
barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
barrier.newLayout = new_layout;
@ -116,9 +122,9 @@ namespace vk
barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
barrier.subresourceRange = range;
barrier.srcAccessMask = src_access;
barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
barrier.dstAccessMask = dst_access;
vkCmdPipelineBarrier(cmd, src_stage, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | VK_PIPELINE_STAGE_VERTEX_SHADER_BIT, 0, 0, nullptr, 0, nullptr, 1, &barrier);
vkCmdPipelineBarrier(cmd, src_stage, dst_stage, 0, 0, nullptr, 0, nullptr, 1, &barrier);
}
void insert_texture_barrier(const vk::command_buffer& cmd, vk::image* image, VkImageLayout new_layout, bool preserve_renderpass)

View file

@ -102,7 +102,7 @@ namespace vk
multidraw_support.max_batch_size = 65536;
optional_features_support.barycentric_coords = !!shader_barycentric_info.fragmentShaderBarycentric;
optional_features_support.framebuffer_loops = !!fbo_loops_info.attachmentFeedbackLoopLayout && get_driver_vendor() != driver_vendor::AMD;
optional_features_support.framebuffer_loops = !!fbo_loops_info.attachmentFeedbackLoopLayout;
optional_features_support.extended_device_fault = !!device_fault_info.deviceFault;
features = features2.features;

View file

@ -207,6 +207,11 @@ namespace vk
return m_format_class;
}
std::string image::debug_name() const
{
return m_debug_name;
}
void image::push_layout(const command_buffer& cmd, VkImageLayout layout)
{
ensure(current_queue_family == VK_QUEUE_FAMILY_IGNORED || current_queue_family == cmd.get_queue_family());
@ -304,6 +309,8 @@ namespace vk
_vkSetDebugUtilsObjectNameEXT(m_device, &name_info);
}
m_debug_name = name;
}
image_view::image_view(VkDevice dev, VkImage image, VkImageViewType view_type, VkFormat format, VkComponentMapping mapping, VkImageSubresourceRange range)
@ -405,6 +412,14 @@ namespace vk
// Restore requested mapping
info.components = mapping;
#endif
if (m_resource)
{
if (const auto name = m_resource->debug_name(); !name.empty())
{
set_debug_name(fmt::format("%p (%p) %s", value, m_resource->value, name));
}
}
}
viewable_image* viewable_image::clone()
@ -488,4 +503,18 @@ namespace vk
views.clear();
}
}
void image_view::set_debug_name(std::string_view name)
{
if (g_render_device->get_debug_utils_support())
{
VkDebugUtilsObjectNameInfoEXT name_info{};
name_info.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_OBJECT_NAME_INFO_EXT;
name_info.objectType = VK_OBJECT_TYPE_IMAGE_VIEW;
name_info.objectHandle = reinterpret_cast<u64>(value);
name_info.pObjectName = name.data();
_vkSetDebugUtilsObjectNameEXT(m_device, &name_info);
}
}
}

View file

@ -33,6 +33,7 @@ namespace vk
VkImageAspectFlags m_storage_aspect = 0;
rsx::format_class m_format_class = RSX_FORMAT_CLASS_UNDEFINED;
std::string m_debug_name;
void validate(const vk::render_device& dev, const VkImageCreateInfo& info) const;
@ -83,6 +84,7 @@ namespace vk
VkSharingMode sharing_mode() const;
VkImageAspectFlags aspect() const;
rsx::format_class format_class() const;
std::string debug_name() const;
// Pipeline management
void push_layout(const command_buffer& cmd, VkImageLayout layout);
@ -127,6 +129,7 @@ namespace vk
vk::image* m_resource = nullptr;
void create_impl();
void set_debug_name(std::string_view name);
};
class viewable_image : public image

View file

@ -58,6 +58,8 @@ namespace vk
VkPipelineStageFlags src_stage = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
VkPipelineStageFlags dst_stage = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
const bool is_color_surface = !!(range.aspectMask & VK_IMAGE_ASPECT_COLOR_BIT);
switch (+new_layout)
{
case VK_IMAGE_LAYOUT_GENERAL:
@ -76,6 +78,7 @@ namespace vk
VK_PIPELINE_STAGE_TRANSFER_BIT |
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT |
VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT |
VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT |
VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT
};
break;
@ -89,17 +92,30 @@ namespace vk
dst_stage = VK_PIPELINE_STAGE_TRANSFER_BIT;
break;
case VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL:
barrier.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
barrier.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | VK_ACCESS_COLOR_ATTACHMENT_READ_BIT;
dst_stage = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
break;
case VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL:
barrier.dstAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;
dst_stage = VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT;
barrier.dstAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT;
dst_stage = VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT;
break;
case VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL:
barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_INPUT_ATTACHMENT_READ_BIT;
dst_stage = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | VK_PIPELINE_STAGE_VERTEX_SHADER_BIT;
break;
case VK_IMAGE_LAYOUT_ATTACHMENT_FEEDBACK_LOOP_OPTIMAL_EXT:
barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_INPUT_ATTACHMENT_READ_BIT;
dst_stage = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
dst_stage = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | VK_PIPELINE_STAGE_VERTEX_SHADER_BIT;
if (is_color_surface)
{
barrier.dstAccessMask |= VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
dst_stage |= VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
}
else
{
barrier.dstAccessMask |= VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;
dst_stage |= VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT;
}
break;
case VK_IMAGE_LAYOUT_UNDEFINED:
case VK_IMAGE_LAYOUT_PREINITIALIZED:
@ -170,12 +186,25 @@ namespace vk
src_stage = VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT;
break;
case VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL:
barrier.srcAccessMask = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_INPUT_ATTACHMENT_READ_BIT;
src_stage = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | VK_PIPELINE_STAGE_VERTEX_SHADER_BIT;
break;
case VK_IMAGE_LAYOUT_ATTACHMENT_FEEDBACK_LOOP_OPTIMAL_EXT:
barrier.srcAccessMask = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_INPUT_ATTACHMENT_READ_BIT;
src_stage = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
if (is_color_surface)
{
barrier.srcAccessMask |= VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
src_stage |= VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
}
else
{
barrier.srcAccessMask |= VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;
src_stage |= VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT;
}
break;
default:
break; //TODO Investigate what happens here
break;
}
barrier.srcAccessMask &= src_access_mask_bits;

View file

@ -252,7 +252,7 @@ void init_fxo_for_exec(utils::serial* ar, bool full = false)
// Reserved area
if (!load_and_check_reserved(*ar, advance))
{
sys_log.error("Potential failure to load savestate: padding buyes are not 0. %s", *ar);
sys_log.error("Potential failure to load savestate: padding bytes are not 0. %s", *ar);
}
}
}
@ -310,7 +310,7 @@ static void fixup_settings(const psf::registry* _psf)
if (g_cfg.net.net_active == np_internet_status::disabled && g_cfg.net.psn_status != np_psn_status::disabled)
{
sys_log.warning("Net status was set to disconnected so psn status was disabled");
sys_log.warning("Net status was set to disconnected so PSN status was disabled.");
g_cfg.net.psn_status.set(np_psn_status::disabled);
}
}
@ -3393,7 +3393,7 @@ void Emulator::Kill(bool allow_autoexit, bool savestate, savestate_stage* save_s
bool is_being_held_longer = false;
for (int i = 0; !*join_ended && thread_ctrl::state() != thread_state::aborting;)
for (int i = 0; !*join_ended && thread_ctrl::state() != thread_state::aborting; i++)
{
if (g_watchdog_hold_ctr)
{
@ -4642,7 +4642,7 @@ game_boot_result Emulator::InsertDisc(const std::string& path)
else
{
// TODO: find out where other discs are mounted
sys_log.todo("Mounting non-ps2/ps3 disc in dev_bdvd. Is this correct? (path='%s')", disc_root);
sys_log.todo("Mounting non-PS2/PS3 disc in dev_bdvd. Is this correct? (path='%s')", disc_root);
ensure(vfs::mount("/dev_bdvd", disc_root));
}

View file

@ -0,0 +1,146 @@
#include "mouse_gyro_handler.h"
#include <QEvent>
#include <QMouseEvent>
#include <QWheelEvent>
#include <QWindow>
#include <algorithm>
void mouse_gyro_handler::clear()
{
active = false;
reset = false;
gyro_x = DEFAULT_MOTION_X;
gyro_y = DEFAULT_MOTION_Y;
gyro_z = DEFAULT_MOTION_Z;
}
bool mouse_gyro_handler::toggle_enabled()
{
enabled = !enabled;
clear();
return enabled;
}
void mouse_gyro_handler::set_gyro_active()
{
active = true;
}
void mouse_gyro_handler::set_gyro_reset()
{
active = false;
reset = true;
}
void mouse_gyro_handler::set_gyro_xz(s32 off_x, s32 off_y)
{
if (!active)
return;
gyro_x = static_cast<u16>(std::clamp(off_x, 0, DEFAULT_MOTION_X * 2 - 1));
gyro_z = static_cast<u16>(std::clamp(off_y, 0, DEFAULT_MOTION_Z * 2 - 1));
}
void mouse_gyro_handler::set_gyro_y(s32 steps)
{
if (!active)
return;
gyro_y = static_cast<u16>(std::clamp(gyro_y + steps, 0, DEFAULT_MOTION_Y * 2 - 1));
}
void mouse_gyro_handler::handle_event(QEvent* ev, const QWindow& win)
{
if (!enabled)
return;
// Mouse-based motion input.
// Captures mouse events while the game window is focused.
// Updates motion sensor values via mouse position and mouse wheel while RMB is held.
// Intentionally independent of chosen pad configuration.
switch (ev->type())
{
case QEvent::MouseButtonPress:
{
auto* e = static_cast<QMouseEvent*>(ev);
if (e->button() == Qt::RightButton)
{
// Enable mouse-driven gyro emulation while RMB is held.
set_gyro_active();
}
break;
}
case QEvent::MouseButtonRelease:
{
auto* e = static_cast<QMouseEvent*>(ev);
if (e->button() == Qt::RightButton)
{
// Disable gyro emulation and request a one-shot motion reset.
set_gyro_reset();
}
break;
}
case QEvent::MouseMove:
{
auto* e = static_cast<QMouseEvent*>(ev);
// Track cursor offset from window center.
const QPoint center(win.width() / 2, win.height() / 2);
const QPoint cur = e->position().toPoint();
const s32 off_x = cur.x() - center.x() + DEFAULT_MOTION_X;
const s32 off_y = cur.y() - center.y() + DEFAULT_MOTION_Z;
// Determine motion from relative mouse position while gyro emulation is active.
set_gyro_xz(off_x, off_y);
break;
}
case QEvent::Wheel:
{
auto* e = static_cast<QWheelEvent*>(ev);
// Track mouse wheel steps.
const s32 steps = e->angleDelta().y() / 120;
// Accumulate mouse wheel steps while gyro emulation is active.
set_gyro_y(steps);
break;
}
default:
{
break;
}
}
}
void mouse_gyro_handler::apply_gyro(const std::shared_ptr<Pad>& pad)
{
if (!enabled)
return;
if (!pad || !pad->is_connected())
return;
// Inject mouse-based motion sensor values into pad sensors for gyro emulation.
// The Qt frontend maps cursor offset and wheel input to absolute motion values while RMB is held.
if (reset)
{
// RMB released → reset motion
pad->m_sensors[0].m_value = DEFAULT_MOTION_X;
pad->m_sensors[1].m_value = DEFAULT_MOTION_Y;
pad->m_sensors[2].m_value = DEFAULT_MOTION_Z;
clear();
}
else
{
// RMB held → accumulate motion
// Axes have been chosen as tested in Sly 4 minigames. Top-down view motion uses X/Z axes.
pad->m_sensors[0].m_value = gyro_x; // Mouse X → Motion X
pad->m_sensors[1].m_value = gyro_y; // Mouse Wheel → Motion Y
pad->m_sensors[2].m_value = gyro_z; // Mouse Y → Motion Z
}
}

View file

@ -0,0 +1,33 @@
#pragma once
#include "util/types.hpp"
#include "util/atomic.hpp"
#include "Emu/Io/pad_types.h"
class QEvent;
class QWindow;
// Mouse-based motion sensor emulation state.
class mouse_gyro_handler
{
private:
atomic_t<bool> enabled = false; // Whether mouse-based gyro emulation mode has been enabled by using the associated hotkey
atomic_t<bool> active = false; // Whether right mouse button is currently held (gyro active)
atomic_t<bool> reset = false; // One-shot reset request on right mouse button release
atomic_t<s32> gyro_x = DEFAULT_MOTION_X; // Accumulated from mouse X position relative to center
atomic_t<s32> gyro_y = DEFAULT_MOTION_Y; // Accumulated from mouse wheel delta
atomic_t<s32> gyro_z = DEFAULT_MOTION_Z; // Accumulated from mouse Y position relative to center
void set_gyro_active();
void set_gyro_reset();
void set_gyro_xz(s32 off_x, s32 off_y);
void set_gyro_y(s32 steps);
public:
void clear();
bool toggle_enabled();
void handle_event(QEvent* ev, const QWindow& win);
void apply_gyro(const std::shared_ptr<Pad>& pad);
};

View file

@ -81,6 +81,9 @@ void pad_thread::Init()
{
std::lock_guard lock(pad::g_pad_mutex);
// Reset mouse-based gyro state
m_mouse_gyro.clear();
// Cache old settings if possible
std::array<pad_setting, CELL_PAD_MAX_PORT_NUM> pad_settings;
for (u32 i = 0; i < CELL_PAD_MAX_PORT_NUM; i++) // max 7 pads
@ -606,6 +609,10 @@ void pad_thread::operator()()
if (Emu.IsRunning())
{
update_pad_states();
// Apply mouse-based gyro emulation.
// Intentionally bound to Player 1 only.
m_mouse_gyro.apply_gyro(m_pads[0]);
}
m_info.now_connect = connected_devices + num_ldd_pad;

View file

@ -5,6 +5,7 @@
#include "Emu/Io/pad_types.h"
#include "Emu/Io/pad_config.h"
#include "Emu/Io/pad_config_types.h"
#include "Input/mouse_gyro_handler.h"
#include "Utilities/mutex.h"
#include <map>
@ -41,6 +42,8 @@ public:
static auto constexpr thread_name = "Pad Thread"sv;
mouse_gyro_handler& get_mouse_gyro() { return m_mouse_gyro; }
protected:
void Init();
void InitLddPad(u32 handle, const u32* port_status);
@ -67,6 +70,8 @@ private:
bool m_resume_emulation_flag = false;
bool m_ps_button_pressed = false;
atomic_t<bool> m_home_menu_open = false;
mouse_gyro_handler m_mouse_gyro;
};
namespace pad

View file

@ -194,6 +194,7 @@
<ClCompile Include="Input\dualsense_pad_handler.cpp" />
<ClCompile Include="Input\gui_pad_thread.cpp" />
<ClCompile Include="Input\hid_pad_handler.cpp" />
<ClCompile Include="Input\mouse_gyro_handler.cpp" />
<ClCompile Include="Input\ps_move_calibration.cpp" />
<ClCompile Include="Input\ps_move_config.cpp" />
<ClCompile Include="Input\ps_move_tracker.cpp" />
@ -1079,6 +1080,7 @@
<AdditionalInputs Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">$(QTDIR)\bin\moc.exe;%(FullPath)</AdditionalInputs>
<AdditionalInputs Condition="'$(Configuration)|$(Platform)'=='Release|x64'">$(QTDIR)\bin\moc.exe;%(FullPath)</AdditionalInputs>
</CustomBuild>
<ClInclude Include="Input\mouse_gyro_handler.h" />
<ClInclude Include="Input\ps_move_calibration.h" />
<ClInclude Include="Input\ps_move_config.h" />
<ClInclude Include="Input\ps_move_tracker.h" />

View file

@ -1272,6 +1272,9 @@
<ClCompile Include="QTGeneratedFiles\Release\moc_game_list_context_menu.cpp">
<Filter>Generated Files\Release</Filter>
</ClCompile>
<ClCompile Include="Input\mouse_gyro_handler.cpp">
<Filter>Io</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="Input\ds4_pad_handler.h">
@ -1511,6 +1514,9 @@
<ClInclude Include="Input\sdl_camera_video_sink.h">
<Filter>Io\camera</Filter>
</ClInclude>
<ClInclude Include="Input\mouse_gyro_handler.h">
<Filter>Io</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClInclude Include="resource.h">

View file

@ -159,6 +159,7 @@ add_library(rpcs3_ui STATIC
../Input/hid_pad_handler.cpp
../Input/keyboard_pad_handler.cpp
../Input/mm_joystick_handler.cpp
../Input/mouse_gyro_handler.cpp
../Input/pad_thread.cpp
../Input/product_info.cpp
../Input/ps_move_calibration.cpp

View file

@ -99,7 +99,7 @@ void downloader::start(const std::string& url, bool follow_location, bool show_p
// The downloader's signals are expected to be disconnected and customized before start is called.
// Therefore we need to (re)connect its signal(s) here and not in the constructor.
connect(this, &downloader::signal_buffer_update, this, &downloader::handle_buffer_update);
connect(this, &downloader::signal_buffer_update, this, &downloader::handle_buffer_update, static_cast<Qt::ConnectionType>(Qt::QueuedConnection | Qt::UniqueConnection));
if (show_progress_dialog)
{
@ -169,7 +169,7 @@ usz downloader::update_buffer(char* data, usz size)
const auto old_size = m_curl_buf.size();
const auto new_size = old_size + size;
m_curl_buf.resize(static_cast<int>(new_size));
memcpy(m_curl_buf.data() + old_size, data, size);
std::memcpy(m_curl_buf.data() + old_size, data, size);
int max = 0;
@ -197,6 +197,5 @@ void downloader::handle_buffer_update(int size, int max) const
{
m_progress_dialog->SetRange(0, max > 0 ? max : m_progress_dialog->maximum());
m_progress_dialog->SetValue(size);
QApplication::processEvents();
}
}

View file

@ -19,6 +19,7 @@
#include "Emu/RSX/Overlays/overlay_message.h"
#include "Emu/Io/interception.h"
#include "Emu/Io/recording_config.h"
#include "Input/pad_thread.h"
#include <QApplication>
#include <QDateTime>
@ -402,6 +403,15 @@ void gs_frame::handle_shortcut(gui::shortcuts::shortcut shortcut_key, const QKey
audio::change_volume(-5);
break;
}
case gui::shortcuts::shortcut::gw_toggle_mouse_gyro:
{
if (auto* pad_thr = pad::get_pad_thread(true))
{
const bool mouse_gyro_enabled = pad_thr->get_mouse_gyro().toggle_enabled();
gui_log.notice("Mouse-based gyro emulation %s", mouse_gyro_enabled ? "enabled" : "disabled");
}
break;
}
default:
{
break;
@ -1216,6 +1226,16 @@ bool gs_frame::event(QEvent* ev)
// This will make the cursor visible again if it was hidden by the mouse idle timeout
handle_cursor(visibility(), false, false, true);
}
// Handle events for mouse-based gyro emulation.
if (Emu.IsRunning())
{
if (auto* pad_thr = pad::get_pad_thread(true))
{
pad_thr->get_mouse_gyro().handle_event(ev, *this);
}
}
return QWindow::event(ev);
}

View file

@ -28,7 +28,8 @@ LOG_CHANNEL(gui_log, "GUI");
log_viewer::log_viewer(std::shared_ptr<gui_settings> gui_settings)
: m_gui_settings(std::move(gui_settings))
{
setWindowTitle(tr("Log Viewer"));
update_title();
setObjectName("log_viewer");
setAttribute(Qt::WA_DeleteOnClose);
setAttribute(Qt::WA_StyledBackground);
@ -59,15 +60,33 @@ log_viewer::log_viewer(std::shared_ptr<gui_settings> gui_settings)
connect(m_log_text, &QWidget::customContextMenuRequested, this, &log_viewer::show_context_menu);
}
void log_viewer::update_title()
{
QString suffix;
if (!m_filter_term.isEmpty())
{
suffix = tr(" | Filter '%0'").arg(m_filter_term);
}
if (!m_exclude_term.isEmpty())
{
suffix += tr(" | Exclude '%0'").arg(m_exclude_term);
}
setWindowTitle(tr("Log Viewer%0").arg(suffix));
}
void log_viewer::show_context_menu(const QPoint& pos)
{
QMenu menu;
QAction* clear = new QAction(tr("&Clear"));
QAction* copy = new QAction(tr("&Copy"));
QAction* open = new QAction(tr("&Open log file"));
QAction* save = new QAction(tr("&Save filtered log"));
QAction* filter = new QAction(tr("&Filter log"));
QAction* config = new QAction(tr("&Check config"));
QAction* clear = new QAction(tr("&Clear"));
QAction* copy = new QAction(tr("&Copy"));
QAction* open = new QAction(tr("&Open log file"));
QAction* save = new QAction(tr("&Save filtered log"));
QAction* filter = new QAction(tr("&Filter log%0").arg(m_filter_term.isEmpty() ? "" : QString(" (%0)").arg(m_filter_term)));
QAction* exclude = new QAction(tr("&Exclude%0").arg(m_exclude_term.isEmpty() ? "" : QString(" (%0)").arg(m_exclude_term)));
QAction* config = new QAction(tr("&Check config"));
QAction* timestamps = new QAction(tr("&Show Timestamps"));
timestamps->setCheckable(true);
@ -91,7 +110,7 @@ void log_viewer::show_context_menu(const QPoint& pos)
QAction* trace_act = new QAction(tr("Trace"), log_level_acts);
log_level_acts->setExclusive(false);
auto init_action = [this](QAction* act, logs::level logLevel)
const auto init_action = [this](QAction* act, logs::level logLevel)
{
act->setCheckable(true);
act->setChecked(m_log_levels.test(static_cast<u32>(logLevel)));
@ -120,6 +139,7 @@ void log_viewer::show_context_menu(const QPoint& pos)
menu.addAction(open);
menu.addAction(config);
menu.addAction(filter);
menu.addAction(exclude);
menu.addAction(save);
menu.addSeparator();
menu.addAction(timestamps);
@ -187,7 +207,22 @@ void log_viewer::show_context_menu(const QPoint& pos)
connect(filter, &QAction::triggered, this, [this]()
{
m_filter_term = QInputDialog::getText(this, tr("Filter log"), tr("Enter text"), QLineEdit::EchoMode::Normal, m_filter_term);
bool ok = false;
QString filter_term = QInputDialog::getText(this, tr("Filter log"), tr("Enter text"), QLineEdit::EchoMode::Normal, m_filter_term, &ok);
if (!ok) return;
m_filter_term = std::move(filter_term);
update_title();
filter_log();
});
connect(exclude, &QAction::triggered, this, [this]()
{
bool ok = false;
QString exclude_term = QInputDialog::getText(this, tr("Exclude"), tr("Enter text (comma separated)"), QLineEdit::EchoMode::Normal, m_exclude_term, &ok);
if (!ok) return;
m_exclude_term = std::move(exclude_term);
m_exclude_terms = m_exclude_term.split(',', Qt::SkipEmptyParts);
update_title();
filter_log();
});
@ -309,7 +344,7 @@ void log_viewer::filter_log()
if (!m_log_levels.test(static_cast<u32>(logs::level::notice))) excluded_log_levels.push_back("·! ");
if (!m_log_levels.test(static_cast<u32>(logs::level::trace))) excluded_log_levels.push_back("·T ");
if (m_filter_term.isEmpty() && excluded_log_levels.empty() && m_show_timestamps && m_show_threads && !m_last_actions_only)
if (m_filter_term.isEmpty() && m_exclude_terms.isEmpty() && excluded_log_levels.empty() && m_show_timestamps && m_show_threads && !m_last_actions_only)
{
set_text_and_keep_position(m_full_log);
return;
@ -322,44 +357,49 @@ void log_viewer::filter_log()
const auto add_line = [this, &result, &excluded_log_levels, &timestamp_regexp, &thread_regexp](QString& line)
{
bool exclude_line = false;
for (const QString& log_level_prefix : excluded_log_levels)
if (!line.isEmpty())
{
if (line.startsWith(log_level_prefix))
for (QStringView log_level_prefix : excluded_log_levels)
{
exclude_line = true;
break;
if (line.startsWith(log_level_prefix))
{
return;
}
}
for (QStringView term : m_exclude_terms)
{
if (line.contains(term))
{
return;
}
}
}
if (exclude_line)
if (!m_filter_term.isEmpty() && !line.contains(m_filter_term))
{
return;
}
if (m_filter_term.isEmpty() || line.contains(m_filter_term))
if (line.isEmpty())
{
if (line.isEmpty())
{
result += "\n";
return;
}
result += "\n";
return;
}
if (!m_show_timestamps)
{
line.remove(timestamp_regexp);
}
if (!m_show_timestamps)
{
line.remove(timestamp_regexp);
}
if (!m_show_threads)
{
line.remove(thread_regexp);
}
if (!m_show_threads)
{
line.remove(thread_regexp);
}
if (!line.isEmpty())
{
result += line + "\n";
}
if (!line.isEmpty())
{
result += line + "\n";
}
};

View file

@ -23,6 +23,7 @@ private Q_SLOTS:
void show_context_menu(const QPoint& pos);
private:
void update_title();
void set_text_and_keep_position(const QString& text);
void filter_log();
bool is_valid_file(const QMimeData& md, bool save = false);
@ -30,6 +31,8 @@ private:
std::shared_ptr<gui_settings> m_gui_settings;
QString m_path_last;
QString m_filter_term;
QString m_exclude_term;
QStringList m_exclude_terms;
QString m_full_log;
QPlainTextEdit* m_log_text;
LogHighlighter* m_log_highlighter;

View file

@ -185,8 +185,8 @@ rpcn_account_dialog::rpcn_account_dialog(QWidget* parent)
QPushButton* btn_test = new QPushButton(tr("Test Account"));
QLabel* label_npid = new QLabel();
QCheckBox* checkbox_disable_ipv6 = new QCheckBox(tr("Disable IPv6"));
checkbox_disable_ipv6->setCheckState(g_cfg_rpcn.get_ipv6_support() ? Qt::Unchecked : Qt::Checked);
// QCheckBox* checkbox_disable_ipv6 = new QCheckBox(tr("Enable IPv6(Experimental)"));
// checkbox_disable_ipv6->setCheckState(g_cfg_rpcn.get_ipv6_support() ? Qt::Checked : Qt::Unchecked);
const auto update_npid_label = [label_npid]()
{
@ -206,7 +206,7 @@ rpcn_account_dialog::rpcn_account_dialog(QWidget* parent)
grp_buttons->setLayout(vbox_buttons);
vbox_global->addWidget(grp_buttons);
vbox_global->addWidget(checkbox_disable_ipv6);
// vbox_global->addWidget(checkbox_disable_ipv6);
setLayout(vbox_global);
@ -359,11 +359,11 @@ rpcn_account_dialog::rpcn_account_dialog(QWidget* parent)
QMessageBox::information(this, tr("RPCN Account Valid!"), tr("Your account is valid!"), QMessageBox::Ok);
});
connect(checkbox_disable_ipv6, &QCheckBox::checkStateChanged, this, [this](Qt::CheckState state)
{
g_cfg_rpcn.set_ipv6_support(state == Qt::Unchecked);
g_cfg_rpcn.save();
});
// connect(checkbox_disable_ipv6, &QCheckBox::checkStateChanged, this, [this](Qt::CheckState state)
// {
// g_cfg_rpcn.set_ipv6_support(state == Qt::Checked);
// g_cfg_rpcn.save();
// });
}
void rpcn_account_dialog::refresh_combobox()
@ -1262,13 +1262,10 @@ rpcn_friends_dialog::rpcn_friends_dialog(QWidget* parent)
connect(accept_request_action, &QAction::triggered, this, [this, str_sel_friend]()
{
if (!m_rpcn->add_friend(str_sel_friend))
{
QMessageBox::critical(this, tr("Error adding a friend!"), tr("An error occurred while trying to add a friend!"), QMessageBox::Ok);
}
else
if (add_friend_with_error_dialog(str_sel_friend))
{
QMessageBox::information(this, tr("Friend added!"), tr("You've successfully added a friend!"), QMessageBox::Ok);
return;
}
});
@ -1304,11 +1301,8 @@ rpcn_friends_dialog::rpcn_friends_dialog(QWidget* parent)
connect(send_friend_request_action, &QAction::triggered, this, [this, str_sel_friend]()
{
if (!m_rpcn->add_friend(str_sel_friend))
{
QMessageBox::critical(this, tr("Error sending a friend request!"), tr("An error occurred while trying to send a friend request!"), QMessageBox::Ok);
if (!add_friend_with_error_dialog(str_sel_friend))
return;
}
QString qstr_friend = QString::fromStdString(str_sel_friend);
add_update_list(m_lst_requests, qstr_friend, m_icon_request_sent, QVariant(false));
@ -1341,11 +1335,7 @@ rpcn_friends_dialog::rpcn_friends_dialog(QWidget* parent)
QMessageBox::critical(this, tr("Error validating username!"), tr("The username you entered is invalid!"), QMessageBox::Ok);
}
if (!m_rpcn->add_friend(str_friend_username))
{
QMessageBox::critical(this, tr("Error adding friend!"), tr("An error occurred while adding a friend!"), QMessageBox::Ok);
}
else
if (add_friend_with_error_dialog(str_friend_username))
{
add_update_list(m_lst_requests, QString::fromStdString(str_friend_username), m_icon_request_sent, QVariant(false));
QMessageBox::information(this, tr("Friend added!"), tr("Friend was successfully added!"), QMessageBox::Ok);
@ -1360,6 +1350,42 @@ rpcn_friends_dialog::~rpcn_friends_dialog()
m_rpcn->remove_friend_cb(friend_callback, this);
}
bool rpcn_friends_dialog::add_friend_with_error_dialog(const std::string& friend_username)
{
QString err_msg;
const auto opt_error = m_rpcn->add_friend(friend_username);
if (opt_error.has_value())
{
const auto error = opt_error.value();
if (error != rpcn::ErrorType::NoError)
{
switch (error)
{
case rpcn::ErrorType::NotFound: err_msg = tr("The specified username does not exist."); break;
case rpcn::ErrorType::InvalidInput: err_msg = tr("You cannot add yourself as a friend."); break;
case rpcn::ErrorType::Blocked: err_msg = tr("You or the other user have the other blocked."); break;
case rpcn::ErrorType::AlreadyFriend: err_msg = tr("You are already friends with this user."); break;
case rpcn::ErrorType::DbFail: err_msg = tr("A database error occurred. Please try again later."); break;
default: err_msg = tr("An unexpected error occurred."); break;
}
}
}
else
{
err_msg = tr("Failed to send the friend request.");
}
if (!err_msg.isEmpty())
{
QMessageBox::critical(this, tr("Friend Request Failed"), err_msg, QMessageBox::Ok);
return false;
}
return true;
}
bool rpcn_friends_dialog::is_ok() const
{
return m_rpcn_ok;

View file

@ -121,6 +121,7 @@ public:
private:
void add_update_list(QListWidget* list, const QString& name, const QIcon& icon, const QVariant& data);
void remove_list(QListWidget* list, const QString& name);
bool add_friend_with_error_dialog(const std::string& friend_username);
private Q_SLOTS:
void add_update_friend(const QString& name, bool status);

View file

@ -37,6 +37,7 @@ void fmt_class_string<shortcut>::format(std::string& out, u64 arg)
case shortcut::gw_mute_unmute: return "gw_mute_unmute";
case shortcut::gw_volume_up: return "gw_volume_up";
case shortcut::gw_volume_down: return "gw_volume_down";
case shortcut::gw_toggle_mouse_gyro: return "gw_toggle_mouse_gyro";
case shortcut::count: return "count";
}
@ -88,6 +89,7 @@ shortcut_settings::shortcut_settings()
{ shortcut::gw_mute_unmute, shortcut_info{ "gw_mute_unmute", tr("Mute/Unmute Audio"), "Ctrl+Shift+M", shortcut_handler_id::game_window, false } },
{ shortcut::gw_volume_up, shortcut_info{ "gw_volume_up", tr("Volume Up"), "Ctrl+Shift++", shortcut_handler_id::game_window, true } },
{ shortcut::gw_volume_down, shortcut_info{ "gw_volume_down", tr("Volume Down"), "Ctrl+Shift+-", shortcut_handler_id::game_window, true } },
{ shortcut::gw_toggle_mouse_gyro, shortcut_info{ "gw_toggle_mouse_gyro", tr("Toggle Mouse-based Gyro"), "Ctrl+G", shortcut_handler_id::game_window, false } },
})
{
}

View file

@ -46,6 +46,7 @@ namespace gui
gw_mute_unmute,
gw_volume_up,
gw_volume_down,
gw_toggle_mouse_gyro,
count
};

View file

@ -175,7 +175,7 @@ namespace utils
inline void pause()
{
#if defined(ARCH_ARM64)
__asm__ volatile("yield");
__asm__ volatile("isb" ::: "memory");
#elif defined(ARCH_X64)
_mm_pause();
#else

View file

@ -575,34 +575,28 @@ namespace utils
return;
}
// Prepare resampler
av.swr = swr_alloc();
if (!av.swr)
{
media_log.error("audio_decoder: Failed to allocate resampler for stream #%u in file '%s'", stream_index, path);
has_error = true;
return;
}
const int dst_channels = 2;
const AVChannelLayout dst_channel_layout = AV_CHANNEL_LAYOUT_STEREO;
const AVSampleFormat dst_format = AV_SAMPLE_FMT_FLT;
int set_err = 0;
if ((set_err = av_opt_set_int(av.swr, "in_channel_count", stream->codecpar->ch_layout.nb_channels, 0)) ||
(set_err = av_opt_set_int(av.swr, "out_channel_count", dst_channels, 0)) ||
(set_err = av_opt_set_chlayout(av.swr, "in_channel_layout", &stream->codecpar->ch_layout, 0)) ||
(set_err = av_opt_set_chlayout(av.swr, "out_channel_layout", &dst_channel_layout, 0)) ||
(set_err = av_opt_set_int(av.swr, "in_sample_rate", stream->codecpar->sample_rate, 0)) ||
(set_err = av_opt_set_int(av.swr, "out_sample_rate", sample_rate, 0)) ||
(set_err = av_opt_set_sample_fmt(av.swr, "in_sample_fmt", static_cast<AVSampleFormat>(stream->codecpar->format), 0)) ||
(set_err = av_opt_set_sample_fmt(av.swr, "out_sample_fmt", dst_format, 0)))
const int set_err = swr_alloc_set_opts2(&av.swr, &dst_channel_layout, dst_format,
sample_rate, &stream->codecpar->ch_layout,
static_cast<AVSampleFormat>(stream->codecpar->format),
stream->codecpar->sample_rate, 0, nullptr);
if (set_err < 0)
{
media_log.error("audio_decoder: Failed to set resampler options: Error: %d='%s'", set_err, av_error_to_string(set_err));
has_error = true;
return;
}
if (!av.swr)
{
media_log.error("audio_decoder: Failed to allocate resampler for stream #%u in file '%s'", stream_index, path);
has_error = true;
return;
}
if (int err = swr_init(av.swr); err < 0 || !swr_is_initialized(av.swr))
{
media_log.error("audio_decoder: Resampler has not been properly initialized: %d='%s'", err, av_error_to_string(err));