mirror of
https://github.com/RPCS3/rpcs3.git
synced 2026-03-11 07:56:15 +01:00
Merge branch 'master' into windows-clang
This commit is contained in:
commit
3e902af71b
|
|
@ -1,7 +1,7 @@
|
|||
#!/bin/sh -ex
|
||||
|
||||
# Resource/dependency URLs
|
||||
CCACHE_URL="https://github.com/ccache/ccache/releases/download/v4.11.2/ccache-4.11.2-windows-x86_64.zip"
|
||||
CCACHE_URL="https://github.com/ccache/ccache/releases/download/v4.12.3/ccache-4.12.3-windows-x86_64.zip"
|
||||
|
||||
DEP_URLS=" \
|
||||
$CCACHE_URL"
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ QT_SVG_URL="${QT_HOST}${QT_PREFIX}${QT_PREFIX_2}qtsvg${QT_SUFFIX}"
|
|||
QT_TRANSLATIONS_URL="${QT_HOST}${QT_PREFIX}${QT_PREFIX_2}qttranslations${QT_SUFFIX}"
|
||||
LLVMLIBS_URL="https://github.com/RPCS3/llvm-mirror/releases/download/custom-build-win-${LLVM_VER}/llvmlibs_mt.7z"
|
||||
VULKAN_SDK_URL="https://www.dropbox.com/scl/fi/sjjh0fc4ld281pjbl2xzu/VulkanSDK-${VULKAN_VER}-Installer.exe?rlkey=f6wzc0lvms5vwkt2z3qabfv9d&dl=1"
|
||||
CCACHE_URL="https://github.com/ccache/ccache/releases/download/v4.11.2/ccache-4.11.2-windows-x86_64.zip"
|
||||
CCACHE_URL="https://github.com/ccache/ccache/releases/download/v4.12.3/ccache-4.12.3-windows-x86_64.zip"
|
||||
|
||||
DEP_URLS=" \
|
||||
$QT_BASE_URL \
|
||||
|
|
|
|||
2
.github/workflows/llvm.yml
vendored
2
.github/workflows/llvm.yml
vendored
|
|
@ -20,7 +20,7 @@ jobs:
|
|||
runs-on: windows-2025
|
||||
env:
|
||||
COMPILER: msvc
|
||||
CCACHE_SHA: '1f39f3ad5aae3fe915e99ad1302633bc8f6718e58fa7c0de2b0ba7e080f0f08c'
|
||||
CCACHE_SHA: '859141059ac950e1e8cd042c66f842f26b9e3a62a1669a69fe6ba180cb58bbdf'
|
||||
CCACHE_BIN_DIR: 'C:\ccache_bin'
|
||||
CCACHE_DIR: 'C:\ccache'
|
||||
CCACHE_INODECACHE: 'true'
|
||||
|
|
|
|||
16
.github/workflows/rpcs3.yml
vendored
16
.github/workflows/rpcs3.yml
vendored
|
|
@ -33,23 +33,23 @@ jobs:
|
|||
matrix:
|
||||
include:
|
||||
- os: ubuntu-24.04
|
||||
docker_img: "rpcs3/rpcs3-ci-jammy:1.7"
|
||||
docker_img: "rpcs3/rpcs3-ci-jammy:1.8"
|
||||
build_sh: "/rpcs3/.ci/build-linux.sh"
|
||||
compiler: clang
|
||||
UPLOAD_COMMIT_HASH: d812f1254a1157c80fd402f94446310560f54e5f
|
||||
UPLOAD_REPO_FULL_NAME: "rpcs3/rpcs3-binaries-linux"
|
||||
- os: ubuntu-24.04
|
||||
docker_img: "rpcs3/rpcs3-ci-jammy:1.7"
|
||||
docker_img: "rpcs3/rpcs3-ci-jammy:1.8"
|
||||
build_sh: "/rpcs3/.ci/build-linux.sh"
|
||||
compiler: gcc
|
||||
- os: ubuntu-24.04-arm
|
||||
docker_img: "rpcs3/rpcs3-ci-jammy-aarch64:1.7"
|
||||
docker_img: "rpcs3/rpcs3-ci-jammy-aarch64:1.8"
|
||||
build_sh: "/rpcs3/.ci/build-linux-aarch64.sh"
|
||||
compiler: clang
|
||||
UPLOAD_COMMIT_HASH: a1d35836e8d45bfc6f63c26f0a3e5d46ef622fe1
|
||||
UPLOAD_REPO_FULL_NAME: "rpcs3/rpcs3-binaries-linux-arm64"
|
||||
- os: ubuntu-24.04-arm
|
||||
docker_img: "rpcs3/rpcs3-ci-jammy-aarch64:1.7"
|
||||
docker_img: "rpcs3/rpcs3-ci-jammy-aarch64:1.8"
|
||||
build_sh: "/rpcs3/.ci/build-linux-aarch64.sh"
|
||||
compiler: gcc
|
||||
name: RPCS3 Linux ${{ matrix.os }} ${{ matrix.compiler }}
|
||||
|
|
@ -137,7 +137,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
|
||||
|
|
@ -216,13 +216,13 @@ 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'
|
||||
CCACHE_SHA: '1f39f3ad5aae3fe915e99ad1302633bc8f6718e58fa7c0de2b0ba7e080f0f08c'
|
||||
CCACHE_SHA: '859141059ac950e1e8cd042c66f842f26b9e3a62a1669a69fe6ba180cb58bbdf'
|
||||
CCACHE_BIN_DIR: 'C:\ccache_bin'
|
||||
CCACHE_DIR: 'C:\ccache'
|
||||
CCACHE_INODECACHE: 'true'
|
||||
|
|
|
|||
2
3rdparty/7zip/7zip
vendored
2
3rdparty/7zip/7zip
vendored
|
|
@ -1 +1 @@
|
|||
Subproject commit 5e96a8279489832924056b1fa82f29d5837c9469
|
||||
Subproject commit 839151eaaad24771892afaae6bac690e31e58384
|
||||
2
3rdparty/FAudio
vendored
2
3rdparty/FAudio
vendored
|
|
@ -1 +1 @@
|
|||
Subproject commit 633bdb772a593104414b4b103ec752567d57c3c1
|
||||
Subproject commit e67d761ead486de3e69fa11705456bf94df734ca
|
||||
125
3rdparty/GL/glext.h
vendored
125
3rdparty/GL/glext.h
vendored
|
|
@ -6,7 +6,7 @@ extern "C" {
|
|||
#endif
|
||||
|
||||
/*
|
||||
** Copyright 2013-2020 The Khronos Group Inc.
|
||||
** Copyright 2013-2026 The Khronos Group Inc.
|
||||
** SPDX-License-Identifier: MIT
|
||||
**
|
||||
** This header is generated from the Khronos OpenGL / OpenGL ES XML
|
||||
|
|
@ -32,7 +32,7 @@ extern "C" {
|
|||
#define GLAPI extern
|
||||
#endif
|
||||
|
||||
#define GL_GLEXT_VERSION 20250203
|
||||
#define GL_GLEXT_VERSION 20260126
|
||||
|
||||
#include <KHR/khrplatform.h>
|
||||
|
||||
|
|
@ -7358,6 +7358,47 @@ GLAPI void APIENTRY glFogCoordPointerEXT (GLenum type, GLsizei stride, const voi
|
|||
#endif
|
||||
#endif /* GL_EXT_fog_coord */
|
||||
|
||||
#ifndef GL_EXT_fragment_shading_rate
|
||||
#define GL_EXT_fragment_shading_rate 1
|
||||
#define GL_SHADING_RATE_1X1_PIXELS_EXT 0x96A6
|
||||
#define GL_SHADING_RATE_1X2_PIXELS_EXT 0x96A7
|
||||
#define GL_SHADING_RATE_2X1_PIXELS_EXT 0x96A8
|
||||
#define GL_SHADING_RATE_2X2_PIXELS_EXT 0x96A9
|
||||
#define GL_SHADING_RATE_1X4_PIXELS_EXT 0x96AA
|
||||
#define GL_SHADING_RATE_4X1_PIXELS_EXT 0x96AB
|
||||
#define GL_SHADING_RATE_4X2_PIXELS_EXT 0x96AC
|
||||
#define GL_SHADING_RATE_2X4_PIXELS_EXT 0x96AD
|
||||
#define GL_SHADING_RATE_4X4_PIXELS_EXT 0x96AE
|
||||
#define GL_SHADING_RATE_EXT 0x96D0
|
||||
#define GL_SHADING_RATE_ATTACHMENT_EXT 0x96D1
|
||||
#define GL_FRAGMENT_SHADING_RATE_COMBINER_OP_KEEP_EXT 0x96D2
|
||||
#define GL_FRAGMENT_SHADING_RATE_COMBINER_OP_REPLACE_EXT 0x96D3
|
||||
#define GL_FRAGMENT_SHADING_RATE_COMBINER_OP_MIN_EXT 0x96D4
|
||||
#define GL_FRAGMENT_SHADING_RATE_COMBINER_OP_MAX_EXT 0x96D5
|
||||
#define GL_FRAGMENT_SHADING_RATE_COMBINER_OP_MUL_EXT 0x96D6
|
||||
#define GL_MIN_FRAGMENT_SHADING_RATE_ATTACHMENT_TEXEL_WIDTH_EXT 0x96D7
|
||||
#define GL_MAX_FRAGMENT_SHADING_RATE_ATTACHMENT_TEXEL_WIDTH_EXT 0x96D8
|
||||
#define GL_MIN_FRAGMENT_SHADING_RATE_ATTACHMENT_TEXEL_HEIGHT_EXT 0x96D9
|
||||
#define GL_MAX_FRAGMENT_SHADING_RATE_ATTACHMENT_TEXEL_HEIGHT_EXT 0x96DA
|
||||
#define GL_MAX_FRAGMENT_SHADING_RATE_ATTACHMENT_TEXEL_ASPECT_RATIO_EXT 0x96DB
|
||||
#define GL_MAX_FRAGMENT_SHADING_RATE_ATTACHMENT_LAYERS_EXT 0x96DC
|
||||
#define GL_FRAGMENT_SHADING_RATE_WITH_SHADER_DEPTH_STENCIL_WRITES_SUPPORTED_EXT 0x96DD
|
||||
#define GL_FRAGMENT_SHADING_RATE_WITH_SAMPLE_MASK_SUPPORTED_EXT 0x96DE
|
||||
#define GL_FRAGMENT_SHADING_RATE_ATTACHMENT_WITH_DEFAULT_FRAMEBUFFER_SUPPORTED_EXT 0x96DF
|
||||
#define GL_FRAGMENT_SHADING_RATE_NON_TRIVIAL_COMBINERS_SUPPORTED_EXT 0x8F6F
|
||||
#define GL_FRAGMENT_SHADING_RATE_PRIMITIVE_RATE_WITH_MULTI_VIEWPORT_SUPPORTED_EXT 0x9780
|
||||
typedef void (APIENTRYP PFNGLGETFRAGMENTSHADINGRATESEXTPROC) (GLsizei samples, GLsizei maxCount, GLsizei *count, GLenum *shadingRates);
|
||||
typedef void (APIENTRYP PFNGLSHADINGRATEEXTPROC) (GLenum rate);
|
||||
typedef void (APIENTRYP PFNGLSHADINGRATECOMBINEROPSEXTPROC) (GLenum combinerOp0, GLenum combinerOp1);
|
||||
typedef void (APIENTRYP PFNGLFRAMEBUFFERSHADINGRATEEXTPROC) (GLenum target, GLenum attachment, GLuint texture, GLint baseLayer, GLsizei numLayers, GLsizei texelWidth, GLsizei texelHeight);
|
||||
#ifdef GL_GLEXT_PROTOTYPES
|
||||
GLAPI void APIENTRY glGetFragmentShadingRatesEXT (GLsizei samples, GLsizei maxCount, GLsizei *count, GLenum *shadingRates);
|
||||
GLAPI void APIENTRY glShadingRateEXT (GLenum rate);
|
||||
GLAPI void APIENTRY glShadingRateCombinerOpsEXT (GLenum combinerOp0, GLenum combinerOp1);
|
||||
GLAPI void APIENTRY glFramebufferShadingRateEXT (GLenum target, GLenum attachment, GLuint texture, GLint baseLayer, GLsizei numLayers, GLsizei texelWidth, GLsizei texelHeight);
|
||||
#endif
|
||||
#endif /* GL_EXT_fragment_shading_rate */
|
||||
|
||||
#ifndef GL_EXT_framebuffer_blit
|
||||
#define GL_EXT_framebuffer_blit 1
|
||||
#define GL_READ_FRAMEBUFFER_EXT 0x8CA8
|
||||
|
|
@ -7816,6 +7857,86 @@ GLAPI void APIENTRY glImportMemoryWin32NameEXT (GLuint memory, GLuint64 size, GL
|
|||
#endif
|
||||
#endif /* GL_EXT_memory_object_win32 */
|
||||
|
||||
#ifndef GL_EXT_mesh_shader
|
||||
#define GL_EXT_mesh_shader 1
|
||||
#define GL_MESH_SHADER_EXT 0x9559
|
||||
#define GL_TASK_SHADER_EXT 0x955A
|
||||
#define GL_MAX_MESH_UNIFORM_BLOCKS_EXT 0x8E60
|
||||
#define GL_MAX_MESH_TEXTURE_IMAGE_UNITS_EXT 0x8E61
|
||||
#define GL_MAX_MESH_IMAGE_UNIFORMS_EXT 0x8E62
|
||||
#define GL_MAX_MESH_UNIFORM_COMPONENTS_EXT 0x8E63
|
||||
#define GL_MAX_MESH_ATOMIC_COUNTER_BUFFERS_EXT 0x8E64
|
||||
#define GL_MAX_MESH_ATOMIC_COUNTERS_EXT 0x8E65
|
||||
#define GL_MAX_MESH_SHADER_STORAGE_BLOCKS_EXT 0x8E66
|
||||
#define GL_MAX_COMBINED_MESH_UNIFORM_COMPONENTS_EXT 0x8E67
|
||||
#define GL_MAX_TASK_UNIFORM_BLOCKS_EXT 0x8E68
|
||||
#define GL_MAX_TASK_TEXTURE_IMAGE_UNITS_EXT 0x8E69
|
||||
#define GL_MAX_TASK_IMAGE_UNIFORMS_EXT 0x8E6A
|
||||
#define GL_MAX_TASK_UNIFORM_COMPONENTS_EXT 0x8E6B
|
||||
#define GL_MAX_TASK_ATOMIC_COUNTER_BUFFERS_EXT 0x8E6C
|
||||
#define GL_MAX_TASK_ATOMIC_COUNTERS_EXT 0x8E6D
|
||||
#define GL_MAX_TASK_SHADER_STORAGE_BLOCKS_EXT 0x8E6E
|
||||
#define GL_MAX_COMBINED_TASK_UNIFORM_COMPONENTS_EXT 0x8E6F
|
||||
#define GL_MAX_TASK_WORK_GROUP_TOTAL_COUNT_EXT 0x9740
|
||||
#define GL_MAX_MESH_WORK_GROUP_TOTAL_COUNT_EXT 0x9741
|
||||
#define GL_MAX_MESH_WORK_GROUP_INVOCATIONS_EXT 0x9757
|
||||
#define GL_MAX_TASK_WORK_GROUP_INVOCATIONS_EXT 0x9759
|
||||
#define GL_MAX_TASK_PAYLOAD_SIZE_EXT 0x9742
|
||||
#define GL_MAX_TASK_SHARED_MEMORY_SIZE_EXT 0x9743
|
||||
#define GL_MAX_MESH_SHARED_MEMORY_SIZE_EXT 0x9744
|
||||
#define GL_MAX_TASK_PAYLOAD_AND_SHARED_MEMORY_SIZE_EXT 0x9745
|
||||
#define GL_MAX_MESH_PAYLOAD_AND_SHARED_MEMORY_SIZE_EXT 0x9746
|
||||
#define GL_MAX_MESH_OUTPUT_MEMORY_SIZE_EXT 0x9747
|
||||
#define GL_MAX_MESH_PAYLOAD_AND_OUTPUT_MEMORY_SIZE_EXT 0x9748
|
||||
#define GL_MAX_MESH_OUTPUT_VERTICES_EXT 0x9538
|
||||
#define GL_MAX_MESH_OUTPUT_PRIMITIVES_EXT 0x9756
|
||||
#define GL_MAX_MESH_OUTPUT_COMPONENTS_EXT 0x9749
|
||||
#define GL_MAX_MESH_OUTPUT_LAYERS_EXT 0x974A
|
||||
#define GL_MAX_MESH_MULTIVIEW_VIEW_COUNT_EXT 0x9557
|
||||
#define GL_MESH_OUTPUT_PER_VERTEX_GRANULARITY_EXT 0x92DF
|
||||
#define GL_MESH_OUTPUT_PER_PRIMITIVE_GRANULARITY_EXT 0x9543
|
||||
#define GL_MAX_PREFERRED_TASK_WORK_GROUP_INVOCATIONS_EXT 0x974B
|
||||
#define GL_MAX_PREFERRED_MESH_WORK_GROUP_INVOCATIONS_EXT 0x974C
|
||||
#define GL_MESH_PREFERS_LOCAL_INVOCATION_VERTEX_OUTPUT_EXT 0x974D
|
||||
#define GL_MESH_PREFERS_LOCAL_INVOCATION_PRIMITIVE_OUTPUT_EXT 0x974E
|
||||
#define GL_MESH_PREFERS_COMPACT_VERTEX_OUTPUT_EXT 0x974F
|
||||
#define GL_MESH_PREFERS_COMPACT_PRIMITIVE_OUTPUT_EXT 0x9750
|
||||
#define GL_MAX_TASK_WORK_GROUP_COUNT_EXT 0x9751
|
||||
#define GL_MAX_MESH_WORK_GROUP_COUNT_EXT 0x9752
|
||||
#define GL_MAX_MESH_WORK_GROUP_SIZE_EXT 0x9758
|
||||
#define GL_MAX_TASK_WORK_GROUP_SIZE_EXT 0x975A
|
||||
#define GL_MESH_WORK_GROUP_SIZE_EXT 0x953E
|
||||
#define GL_TASK_WORK_GROUP_SIZE_EXT 0x953F
|
||||
#define GL_MESH_VERTICES_OUT_EXT 0x9579
|
||||
#define GL_MESH_PRIMITIVES_OUT_EXT 0x957A
|
||||
#define GL_MESH_OUTPUT_TYPE_EXT 0x957B
|
||||
#define GL_UNIFORM_BLOCK_REFERENCED_BY_MESH_SHADER_EXT 0x959C
|
||||
#define GL_UNIFORM_BLOCK_REFERENCED_BY_TASK_SHADER_EXT 0x959D
|
||||
#define GL_REFERENCED_BY_MESH_SHADER_EXT 0x95A0
|
||||
#define GL_REFERENCED_BY_TASK_SHADER_EXT 0x95A1
|
||||
#define GL_TASK_SHADER_INVOCATIONS_EXT 0x9753
|
||||
#define GL_MESH_SHADER_INVOCATIONS_EXT 0x9754
|
||||
#define GL_MESH_PRIMITIVES_GENERATED_EXT 0x9755
|
||||
#define GL_MESH_SHADER_BIT_EXT 0x00000040
|
||||
#define GL_TASK_SHADER_BIT_EXT 0x00000080
|
||||
#define GL_MESH_SUBROUTINE_EXT 0x957C
|
||||
#define GL_TASK_SUBROUTINE_EXT 0x957D
|
||||
#define GL_MESH_SUBROUTINE_UNIFORM_EXT 0x957E
|
||||
#define GL_TASK_SUBROUTINE_UNIFORM_EXT 0x957F
|
||||
#define GL_ATOMIC_COUNTER_BUFFER_REFERENCED_BY_MESH_SHADER_EXT 0x959E
|
||||
#define GL_ATOMIC_COUNTER_BUFFER_REFERENCED_BY_TASK_SHADER_EXT 0x959F
|
||||
typedef void (APIENTRYP PFNGLDRAWMESHTASKSEXTPROC) (GLuint num_groups_x, GLuint num_groups_y, GLuint num_groups_z);
|
||||
typedef void (APIENTRYP PFNGLDRAWMESHTASKSINDIRECTEXTPROC) (GLintptr indirect);
|
||||
typedef void (APIENTRYP PFNGLMULTIDRAWMESHTASKSINDIRECTEXTPROC) (GLintptr indirect, GLsizei drawcount, GLsizei stride);
|
||||
typedef void (APIENTRYP PFNGLMULTIDRAWMESHTASKSINDIRECTCOUNTEXTPROC) (GLintptr indirect, GLintptr drawcount, GLsizei maxdrawcount, GLsizei stride);
|
||||
#ifdef GL_GLEXT_PROTOTYPES
|
||||
GLAPI void APIENTRY glDrawMeshTasksEXT (GLuint num_groups_x, GLuint num_groups_y, GLuint num_groups_z);
|
||||
GLAPI void APIENTRY glDrawMeshTasksIndirectEXT (GLintptr indirect);
|
||||
GLAPI void APIENTRY glMultiDrawMeshTasksIndirectEXT (GLintptr indirect, GLsizei drawcount, GLsizei stride);
|
||||
GLAPI void APIENTRY glMultiDrawMeshTasksIndirectCountEXT (GLintptr indirect, GLintptr drawcount, GLsizei maxdrawcount, GLsizei stride);
|
||||
#endif
|
||||
#endif /* GL_EXT_mesh_shader */
|
||||
|
||||
#ifndef GL_EXT_misc_attribute
|
||||
#define GL_EXT_misc_attribute 1
|
||||
#endif /* GL_EXT_misc_attribute */
|
||||
|
|
|
|||
2
3rdparty/cubeb/cubeb
vendored
2
3rdparty/cubeb/cubeb
vendored
|
|
@ -1 +1 @@
|
|||
Subproject commit e495bee4cd630c9f99907a764e16edba37a4b564
|
||||
Subproject commit 484857522c73318c06f18ba0a3e17525fa98c608
|
||||
2
3rdparty/libpng/libpng
vendored
2
3rdparty/libpng/libpng
vendored
|
|
@ -1 +1 @@
|
|||
Subproject commit 4e3f57d50f552841550a36eabbb3fbcecacb7750
|
||||
Subproject commit c3e304954a9cfd154bc0dfbfea2b01cd61d6546d
|
||||
4
3rdparty/protobuf/CMakeLists.txt
vendored
4
3rdparty/protobuf/CMakeLists.txt
vendored
|
|
@ -2,8 +2,8 @@ add_library(3rdparty_protobuf INTERFACE)
|
|||
if (USE_SYSTEM_PROTOBUF)
|
||||
pkg_check_modules(PROTOBUF REQUIRED IMPORTED_TARGET protobuf>=33.0.0)
|
||||
target_link_libraries(3rdparty_protobuf INTERFACE PkgConfig::PROTOBUF)
|
||||
set(PROTOBUF_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../../rpcs3/Emu/NP/generated/")
|
||||
execute_process(COMMAND protoc --cpp_out="${PROTOBUF_DIR}" --proto_path="${PROTOBUF_DIR}" np2_structs.proto RESULT_VARIABLE PROTOBUF_CMD_ERROR)
|
||||
set(PROTOBUF_DIR "${CMAKE_SOURCE_DIR}/rpcs3/Emu/NP/generated")
|
||||
execute_process(COMMAND protoc --cpp_out=${PROTOBUF_DIR} --proto_path=${PROTOBUF_DIR} np2_structs.proto RESULT_VARIABLE PROTOBUF_CMD_ERROR)
|
||||
if(PROTOBUF_CMD_ERROR AND NOT PROTOBUF_CMD_ERROR EQUAL 0)
|
||||
message(FATAL_ERROR "protoc failed to regenerate protobuf files.")
|
||||
endif()
|
||||
|
|
|
|||
2
3rdparty/yaml-cpp/yaml-cpp
vendored
2
3rdparty/yaml-cpp/yaml-cpp
vendored
|
|
@ -1 +1 @@
|
|||
Subproject commit 456c68f452da09d8ca84b375faa2b1397713eaba
|
||||
Subproject commit 05c44fcd18074836e21e1eda9fc02b3a4a1529b5
|
||||
1
3rdparty/yaml-cpp/yaml-cpp.vcxproj
vendored
1
3rdparty/yaml-cpp/yaml-cpp.vcxproj
vendored
|
|
@ -76,6 +76,7 @@
|
|||
<ClCompile Include="yaml-cpp\src\exceptions.cpp" />
|
||||
<ClCompile Include="yaml-cpp\src\exp.cpp">
|
||||
</ClCompile>
|
||||
<ClCompile Include="yaml-cpp\src\fptostring.cpp" />
|
||||
<ClCompile Include="yaml-cpp\src\memory.cpp">
|
||||
</ClCompile>
|
||||
<ClCompile Include="yaml-cpp\src\node.cpp">
|
||||
|
|
|
|||
3
3rdparty/yaml-cpp/yaml-cpp.vcxproj.filters
vendored
3
3rdparty/yaml-cpp/yaml-cpp.vcxproj.filters
vendored
|
|
@ -94,5 +94,8 @@
|
|||
<ClCompile Include="yaml-cpp\src\depthguard.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="yaml-cpp\src\fptostring.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
12
BUILDING.md
12
BUILDING.md
|
|
@ -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)
|
||||
|
||||
|
|
@ -95,7 +95,7 @@ sudo apt-get install cmake
|
|||
|
||||
#### Fedora
|
||||
|
||||
sudo dnf install alsa-lib-devel cmake ninja-build glew glew-devel libatomic libevdev-devel libudev-devel openal-devel qt6-qtbase-devel qt6-qtbase-private-devel vulkan-devel pipewire-jack-audio-connection-kit-devel qt6-qtmultimedia-devel qt6-qtsvg-devel llvm-devel
|
||||
sudo dnf install alsa-lib-devel cmake ninja-build glew glew-devel libatomic libevdev-devel libudev-devel openal-soft-devel qt6-qtbase-devel qt6-qtbase-private-devel vulkan-devel pipewire-jack-audio-connection-kit-devel qt6-qtmultimedia-devel qt6-qtsvg-devel llvm-devel libcurl-devel
|
||||
|
||||
#### OpenSUSE
|
||||
|
||||
|
|
@ -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**)
|
||||
|
||||
|
|
|
|||
|
|
@ -13,12 +13,12 @@ set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
|
|||
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
|
||||
|
||||
if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
|
||||
if(CMAKE_CXX_COMPILER_VERSION VERSION_LESS 11)
|
||||
message(FATAL_ERROR "RPCS3 requires at least gcc-11.")
|
||||
if(CMAKE_CXX_COMPILER_VERSION VERSION_LESS 13)
|
||||
message(FATAL_ERROR "RPCS3 requires at least gcc-13.")
|
||||
endif()
|
||||
elseif(CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
|
||||
if(CMAKE_CXX_COMPILER_VERSION VERSION_LESS 12.0)
|
||||
message(FATAL_ERROR "RPCS3 requires at least clang-12.0.")
|
||||
if(CMAKE_CXX_COMPILER_VERSION VERSION_LESS 19.0)
|
||||
message(FATAL_ERROR "RPCS3 requires at least clang-19.0.")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
|
|
|
|||
|
|
@ -398,12 +398,11 @@ namespace fs
|
|||
class windows_file final : public file_base
|
||||
{
|
||||
HANDLE m_handle;
|
||||
atomic_t<u64> m_pos;
|
||||
atomic_t<u64> m_pos {0};
|
||||
|
||||
public:
|
||||
windows_file(HANDLE handle)
|
||||
: m_handle(handle)
|
||||
, m_pos(0)
|
||||
{
|
||||
}
|
||||
|
||||
|
|
@ -417,10 +416,10 @@ namespace fs
|
|||
|
||||
stat_t get_stat() override
|
||||
{
|
||||
FILE_BASIC_INFO basic_info;
|
||||
FILE_BASIC_INFO basic_info {};
|
||||
ensure(GetFileInformationByHandleEx(m_handle, FileBasicInfo, &basic_info, sizeof(FILE_BASIC_INFO))); // "file::stat"
|
||||
|
||||
stat_t info;
|
||||
stat_t info {};
|
||||
info.is_directory = (basic_info.FileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0;
|
||||
info.is_writable = (basic_info.FileAttributes & FILE_ATTRIBUTE_READONLY) == 0;
|
||||
info.size = this->size();
|
||||
|
|
@ -441,7 +440,7 @@ namespace fs
|
|||
|
||||
bool trunc(u64 length) override
|
||||
{
|
||||
FILE_END_OF_FILE_INFO _eof;
|
||||
FILE_END_OF_FILE_INFO _eof {};
|
||||
_eof.EndOfFile.QuadPart = length;
|
||||
|
||||
if (!SetFileInformationByHandle(m_handle, FileEndOfFileInfo, &_eof, sizeof(_eof)))
|
||||
|
|
@ -563,6 +562,7 @@ namespace fs
|
|||
|
||||
u64 size() override
|
||||
{
|
||||
// NOTE: this can fail if we access a mounted empty drive (e.g. after unmounting an iso).
|
||||
LARGE_INTEGER size;
|
||||
ensure(GetFileSizeEx(m_handle, &size)); // "file::size"
|
||||
|
||||
|
|
@ -579,7 +579,7 @@ namespace fs
|
|||
file_id id{"windows_file"};
|
||||
id.data.resize(sizeof(FILE_ID_INFO));
|
||||
|
||||
FILE_ID_INFO info;
|
||||
FILE_ID_INFO info {};
|
||||
|
||||
if (!GetFileInformationByHandleEx(m_handle, FileIdInfo, &info, sizeof(info)))
|
||||
{
|
||||
|
|
@ -625,7 +625,7 @@ namespace fs
|
|||
struct ::stat file_info;
|
||||
ensure(::fstat(m_fd, &file_info) == 0); // "file::stat"
|
||||
|
||||
stat_t info;
|
||||
stat_t info {};
|
||||
info.is_directory = S_ISDIR(file_info.st_mode);
|
||||
info.is_writable = file_info.st_mode & 0200; // HACK: approximation
|
||||
info.size = file_info.st_size;
|
||||
|
|
@ -1656,6 +1656,16 @@ fs::file::file(const std::string& path, bs_t<open_mode> mode)
|
|||
return;
|
||||
}
|
||||
|
||||
// Check if the handle is actually valid.
|
||||
// This can fail on empty mounted drives (e.g. with ERROR_NOT_READY or ERROR_INVALID_FUNCTION).
|
||||
BY_HANDLE_FILE_INFORMATION info;
|
||||
if (!GetFileInformationByHandle(handle, &info))
|
||||
{
|
||||
CloseHandle(handle);
|
||||
g_tls_error = to_error(GetLastError());
|
||||
return;
|
||||
}
|
||||
|
||||
m_file = std::make_unique<windows_file>(handle);
|
||||
#else
|
||||
int flags = O_CLOEXEC; // Ensures all files are closed on execl for auto updater
|
||||
|
|
|
|||
|
|
@ -66,13 +66,13 @@ namespace fs
|
|||
// File attributes (TODO)
|
||||
struct stat_t
|
||||
{
|
||||
bool is_directory;
|
||||
bool is_symlink;
|
||||
bool is_writable;
|
||||
u64 size;
|
||||
s64 atime;
|
||||
s64 mtime;
|
||||
s64 ctime;
|
||||
bool is_directory = false;
|
||||
bool is_symlink = false;
|
||||
bool is_writable = false;
|
||||
u64 size = 0;
|
||||
s64 atime = 0;
|
||||
s64 mtime = 0;
|
||||
s64 ctime = 0;
|
||||
|
||||
using enable_bitcopy = std::true_type;
|
||||
|
||||
|
|
|
|||
|
|
@ -14,6 +14,10 @@
|
|||
#define CAN_OVERCOMMIT
|
||||
#endif
|
||||
|
||||
#if defined(__APPLE__)
|
||||
#include <mutex>
|
||||
#endif
|
||||
|
||||
LOG_CHANNEL(jit_log, "JIT");
|
||||
|
||||
void jit_announce(uptr func, usz size, std::string_view name)
|
||||
|
|
|
|||
|
|
@ -688,6 +688,30 @@ jit_compiler::jit_compiler(const std::unordered_map<std::string, u64>& _link, co
|
|||
mem = std::make_unique<MemoryManager1>(std::move(symbols_cement));
|
||||
}
|
||||
|
||||
std::vector<std::string> attributes;
|
||||
|
||||
#if defined(ARCH_ARM64)
|
||||
if (utils::has_sha3())
|
||||
attributes.push_back("+sha3");
|
||||
else
|
||||
attributes.push_back("-sha3");
|
||||
|
||||
if (utils::has_dotprod())
|
||||
attributes.push_back("+dotprod");
|
||||
else
|
||||
attributes.push_back("-dotprod");
|
||||
|
||||
if (utils::has_sve())
|
||||
attributes.push_back("+sve");
|
||||
else
|
||||
attributes.push_back("-sve");
|
||||
|
||||
if (utils::has_sve2())
|
||||
attributes.push_back("+sve2");
|
||||
else
|
||||
attributes.push_back("-sve2");
|
||||
#endif
|
||||
|
||||
{
|
||||
m_engine.reset(llvm::EngineBuilder(std::move(null_mod))
|
||||
.setErrorStr(&result)
|
||||
|
|
@ -699,6 +723,7 @@ jit_compiler::jit_compiler(const std::unordered_map<std::string, u64>& _link, co
|
|||
//.setCodeModel(llvm::CodeModel::Large)
|
||||
#endif
|
||||
.setRelocationModel(llvm::Reloc::Model::PIC_)
|
||||
.setMAttrs(attributes)
|
||||
.setMCPU(m_cpu)
|
||||
.create());
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,7 +8,6 @@
|
|||
#include "Emu/RSX/RSXThread.h"
|
||||
#include "Thread.h"
|
||||
#include "Utilities/JIT.h"
|
||||
#include <thread>
|
||||
#include <cfenv>
|
||||
|
||||
#ifdef ARCH_ARM64
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@
|
|||
#include "util/atomic.hpp"
|
||||
#include "util/shared_ptr.hpp"
|
||||
|
||||
#include <thread>
|
||||
#include <string>
|
||||
|
||||
// Hardware core layout
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@
|
|||
<ItemDefinitionGroup>
|
||||
<ClCompile>
|
||||
<LanguageStandard Condition = "'$(VisualStudioVersion.Substring(0,2))'<'17'">stdcpplatest</LanguageStandard>
|
||||
<LanguageStandard Condition = "'$(VisualStudioVersion.Substring(0,2))'>='17'">stdcpp20</LanguageStandard>
|
||||
<LanguageStandard Condition = "'$(VisualStudioVersion.Substring(0,2))'>='17'">stdcpp23</LanguageStandard>
|
||||
<PreprocessorDefinitions>_SILENCE_CXX17_ITERATOR_BASE_CLASS_DEPRECATION_WARNING=1;_HAS_EXCEPTIONS=0;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<ExceptionHandling>false</ExceptionHandling>
|
||||
<AdditionalOptions>-d2FH4- %(AdditionalOptions)</AdditionalOptions>
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ include(${CMAKE_CURRENT_SOURCE_DIR}/git-version.cmake)
|
|||
include(ConfigureCompiler)
|
||||
include(CheckFunctionExists)
|
||||
|
||||
set(CMAKE_CXX_STANDARD 20)
|
||||
set(CMAKE_CXX_STANDARD 23)
|
||||
|
||||
if(UNIX AND NOT APPLE AND NOT ANDROID)
|
||||
add_compile_definitions(DATADIR="${CMAKE_INSTALL_FULL_DATADIR}/rpcs3")
|
||||
|
|
|
|||
|
|
@ -634,7 +634,7 @@ u32 microphone_device::capture_audio()
|
|||
if (ALCenum err = alcGetError(micdevice.device); err != ALC_NO_ERROR)
|
||||
{
|
||||
cellMic.error("Error getting number of captured samples of device '%s' (error=%s)", micdevice.name, fmt::alc_error{micdevice.device, err});
|
||||
return CELL_MICIN_ERROR_FATAL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
num_samples = std::min<u32>(num_samples, samples_in);
|
||||
|
|
|
|||
|
|
@ -238,7 +238,7 @@ public:
|
|||
if (over_size > Size)
|
||||
{
|
||||
m_tail += (over_size - Size);
|
||||
if (m_tail > Size)
|
||||
if (m_tail >= Size)
|
||||
m_tail -= Size;
|
||||
|
||||
m_used = Size;
|
||||
|
|
|
|||
|
|
@ -876,39 +876,42 @@ static NEVER_INLINE error_code savedata_op(ppu_thread& ppu, u32 operation, u32 v
|
|||
|
||||
// Sort the entries
|
||||
{
|
||||
const u32 order = setList->sortOrder;
|
||||
const u32 type = setList->sortType;
|
||||
|
||||
std::sort(save_entries.begin(), save_entries.end(), [order, type](const SaveDataEntry& entry1, const SaveDataEntry& entry2) -> bool
|
||||
auto comp = [type](const SaveDataEntry& entry1, const SaveDataEntry& entry2) -> bool
|
||||
{
|
||||
const bool mtime_lower = entry1.mtime < entry2.mtime;
|
||||
const bool mtime_equal = entry1.mtime == entry2.mtime;
|
||||
const bool subtitle_lower = entry1.subtitle < entry2.subtitle;
|
||||
const bool subtitle_equal = entry1.subtitle == entry2.subtitle;
|
||||
const bool revert_order = order == CELL_SAVEDATA_SORTORDER_DESCENT;
|
||||
|
||||
if (type == CELL_SAVEDATA_SORTTYPE_MODIFIEDTIME)
|
||||
{
|
||||
if (mtime_equal)
|
||||
{
|
||||
return subtitle_lower != revert_order;
|
||||
return subtitle_lower;
|
||||
}
|
||||
|
||||
return mtime_lower != revert_order;
|
||||
return mtime_lower;
|
||||
}
|
||||
else if (type == CELL_SAVEDATA_SORTTYPE_SUBTITLE)
|
||||
{
|
||||
if (subtitle_equal)
|
||||
{
|
||||
return mtime_lower != revert_order;
|
||||
return mtime_lower;
|
||||
}
|
||||
|
||||
return subtitle_lower != revert_order;
|
||||
return subtitle_lower;
|
||||
}
|
||||
|
||||
ensure(false);
|
||||
return true;
|
||||
});
|
||||
};
|
||||
|
||||
if (setList->sortOrder == CELL_SAVEDATA_SORTORDER_ASCENT)
|
||||
std::sort(save_entries.begin(), save_entries.end(), comp);
|
||||
else
|
||||
std::sort(save_entries.rbegin(), save_entries.rend(), comp);
|
||||
}
|
||||
|
||||
// Fill the listGet->dirList array
|
||||
|
|
|
|||
|
|
@ -1462,7 +1462,12 @@ error_code cellVdecGetPicItem(ppu_thread& ppu, u32 handle, vm::pptr<CellVdecPicI
|
|||
struct all_info_t
|
||||
{
|
||||
CellVdecPicItem picItem;
|
||||
std::aligned_union_t<0, CellVdecAvcInfo, CellVdecDivxInfo, CellVdecMpeg2Info> picInfo;
|
||||
union
|
||||
{
|
||||
CellVdecAvcInfo avcInfo;
|
||||
CellVdecDivxInfo divxInfo;
|
||||
CellVdecMpeg2Info mpeg2Info;
|
||||
} picInfo;
|
||||
};
|
||||
|
||||
AVFrame* frame{};
|
||||
|
|
|
|||
|
|
@ -1199,7 +1199,7 @@ error_code _sceNpBasicSendMessage(vm::cptr<SceNpId> to, vm::cptr<void> data, u32
|
|||
.msgFeatures = {},
|
||||
.data = std::vector<u8>(static_cast<const u8*>(data.get_ptr()), static_cast<const u8*>(data.get_ptr()) + size)};
|
||||
std::set<std::string> npids;
|
||||
npids.insert(std::string(to->handle.data));
|
||||
npids.insert(np::npid_to_string(*to));
|
||||
|
||||
nph.send_message(msg_data, npids);
|
||||
|
||||
|
|
@ -1228,7 +1228,7 @@ error_code sceNpBasicSendMessageGui(ppu_thread& ppu, vm::cptr<SceNpBasicMessageD
|
|||
sceNp.notice("sceNpBasicSendMessageGui: msgId: %d, mainType: %d, subType: %d, msgFeatures: %d, count: %d, npids: *0x%x", msg->msgId, msg->mainType, msg->subType, msg->msgFeatures, msg->count, msg->npids);
|
||||
for (u32 i = 0; i < msg->count && msg->npids; i++)
|
||||
{
|
||||
sceNp.trace("sceNpBasicSendMessageGui: NpId[%d] = %s", i, static_cast<char*>(&msg->npids[i].handle.data[0]));
|
||||
sceNp.trace("sceNpBasicSendMessageGui: NpId[%d] = %s", i, np::npid_to_string(msg->npids[i]));
|
||||
}
|
||||
sceNp.notice("sceNpBasicSendMessageGui: subject: %s", msg->subject);
|
||||
sceNp.notice("sceNpBasicSendMessageGui: body: %s", msg->body);
|
||||
|
|
@ -1398,7 +1398,7 @@ error_code sceNpBasicSendMessageGui(ppu_thread& ppu, vm::cptr<SceNpBasicMessageD
|
|||
{
|
||||
for (u32 i = 0; i < msg->count; i++)
|
||||
{
|
||||
npids.insert(std::string(msg->npids[i].handle.data));
|
||||
npids.insert(np::npid_to_string(msg->npids[i]));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -7144,7 +7144,7 @@ error_code sceNpUtilCanonicalizeNpIdForPsp(vm::ptr<SceNpId> npId)
|
|||
|
||||
error_code sceNpUtilCmpNpId(vm::ptr<SceNpId> id1, vm::ptr<SceNpId> id2)
|
||||
{
|
||||
sceNp.trace("sceNpUtilCmpNpId(id1=*0x%x(%s), id2=*0x%x(%s))", id1, id1 ? id1->handle.data : "", id2, id2 ? id2->handle.data : "");
|
||||
sceNp.trace("sceNpUtilCmpNpId(id1=*0x%x(%s), id2=*0x%x(%s))", id1, id1 ? np::npid_to_string(*id1) : std::string(), id2, id2 ? np::npid_to_string(*id2) : std::string());
|
||||
|
||||
if (!id1 || !id2)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -992,7 +992,7 @@ error_code sys_net_bnet_sendto(ppu_thread& ppu, s32 s, vm::cptr<void> buf, u32 l
|
|||
fmt::throw_exception("sys_net_bnet_sendto(s=%d): unknown flags (0x%x)", flags);
|
||||
}
|
||||
|
||||
if (addr && addrlen < 8)
|
||||
if (addr && addrlen < sizeof(sys_net_sockaddr))
|
||||
{
|
||||
sys_net.error("sys_net_bnet_sendto(s=%d): bad addrlen (%u)", s, addrlen);
|
||||
return -SYS_NET_EINVAL;
|
||||
|
|
|
|||
|
|
@ -107,7 +107,7 @@ enum lv2_ip_option : s32
|
|||
SYS_NET_IP_MULTICAST_LOOP = 11,
|
||||
SYS_NET_IP_ADD_MEMBERSHIP = 12,
|
||||
SYS_NET_IP_DROP_MEMBERSHIP = 13,
|
||||
SYS_NET_IP_TTLCHK = 23,
|
||||
SYS_NET_IP_TTLCHK = 23, // This is probably the equivalent of IP_MINTTL on FreeBSD
|
||||
SYS_NET_IP_MAXTTL = 24,
|
||||
SYS_NET_IP_DONTFRAG = 26
|
||||
};
|
||||
|
|
|
|||
|
|
@ -551,12 +551,14 @@ std::tuple<s32, lv2_socket::sockopt_data, u32> lv2_socket_native::getsockopt(s32
|
|||
}
|
||||
case SYS_NET_IP_TTLCHK:
|
||||
{
|
||||
sys_net.error("sys_net_bnet_getsockopt(IPPROTO_IP, SYS_NET_IP_TTLCHK): stubbed option");
|
||||
out_val._int = min_ttl;
|
||||
out_len = sizeof(s32);
|
||||
return {CELL_OK, out_val, out_len};
|
||||
}
|
||||
case SYS_NET_IP_MAXTTL:
|
||||
{
|
||||
sys_net.error("sys_net_bnet_getsockopt(IPPROTO_IP, SYS_NET_IP_MAXTTL): stubbed option");
|
||||
out_val._int = max_ttl;
|
||||
out_len = sizeof(s32);
|
||||
return {CELL_OK, out_val, out_len};
|
||||
}
|
||||
case SYS_NET_IP_DONTFRAG:
|
||||
|
|
@ -834,13 +836,13 @@ s32 lv2_socket_native::setsockopt(s32 level, s32 optname, const std::vector<u8>&
|
|||
}
|
||||
case SYS_NET_IP_TTLCHK:
|
||||
{
|
||||
sys_net.error("sys_net_bnet_setsockopt(s=%d, IPPROTO_IP): Stubbed option (0x%x) (SYS_NET_IP_TTLCHK)", lv2_id, optname);
|
||||
break;
|
||||
min_ttl = native_int;
|
||||
return {};
|
||||
}
|
||||
case SYS_NET_IP_MAXTTL:
|
||||
{
|
||||
sys_net.error("sys_net_bnet_setsockopt(s=%d, IPPROTO_IP): Stubbed option (0x%x) (SYS_NET_IP_MAXTTL)", lv2_id, optname);
|
||||
break;
|
||||
max_ttl = native_int;
|
||||
return {};
|
||||
}
|
||||
case SYS_NET_IP_DONTFRAG:
|
||||
{
|
||||
|
|
@ -910,7 +912,7 @@ std::optional<std::tuple<s32, std::vector<u8>, sys_net_sockaddr>> lv2_socket_nat
|
|||
{
|
||||
auto& nph = g_fxo->get<named_thread<np::np_handler>>();
|
||||
const auto packet = dnshook.get_dns_packet(lv2_id);
|
||||
ensure(packet.size() < len);
|
||||
ensure(packet.size() <= len);
|
||||
memcpy(res_buf.data(), packet.data(), packet.size());
|
||||
native_addr.ss_family = AF_INET;
|
||||
(reinterpret_cast<::sockaddr_in*>(&native_addr))->sin_port = std::bit_cast<u16, be_t<u16>>(53); // htons(53)
|
||||
|
|
@ -1069,18 +1071,20 @@ std::optional<s32> lv2_socket_native::sendmsg(s32 flags, const sys_net_msghdr& m
|
|||
return {-SYS_NET_ECONNRESET};
|
||||
}
|
||||
|
||||
std::vector<u8> buf_copy;
|
||||
for (int i = 0; i < msg.msg_iovlen; i++)
|
||||
{
|
||||
auto iov_base = msg.msg_iov[i].iov_base;
|
||||
const u32 len = msg.msg_iov[i].iov_len;
|
||||
const std::vector<u8> buf_copy(vm::_ptr<const char>(iov_base.addr()), vm::_ptr<const char>(iov_base.addr()) + len);
|
||||
const auto* src = vm::_ptr<const char>(iov_base.addr());
|
||||
buf_copy.insert(buf_copy.end(), src, src + len);
|
||||
}
|
||||
|
||||
native_result = ::send(native_socket, reinterpret_cast<const char*>(buf_copy.data()), ::narrow<int>(buf_copy.size()), native_flags);
|
||||
native_result = ::send(native_socket, reinterpret_cast<const char*>(buf_copy.data()), ::narrow<int>(buf_copy.size()), native_flags);
|
||||
|
||||
if (native_result >= 0)
|
||||
{
|
||||
return {native_result};
|
||||
}
|
||||
if (native_result >= 0)
|
||||
{
|
||||
return {native_result};
|
||||
}
|
||||
|
||||
result = get_last_error(!so_nbio && (flags & SYS_NET_MSG_DONTWAIT) == 0);
|
||||
|
|
@ -1232,16 +1236,16 @@ bool lv2_socket_native::is_socket_connected()
|
|||
return false;
|
||||
}
|
||||
|
||||
fd_set readfds, writefds;
|
||||
struct timeval timeout{0, 0}; // Zero timeout
|
||||
pollfd pfd{};
|
||||
pfd.fd = native_socket;
|
||||
pfd.events = POLLIN | POLLOUT;
|
||||
|
||||
FD_ZERO(&readfds);
|
||||
FD_ZERO(&writefds);
|
||||
FD_SET(native_socket, &readfds);
|
||||
FD_SET(native_socket, &writefds);
|
||||
|
||||
// Use select to check for readability and writability
|
||||
const int result = ::select(1, &readfds, &writefds, NULL, &timeout);
|
||||
// Use poll to check for readability and writability
|
||||
#ifdef _WIN32
|
||||
const int result = WSAPoll(&pfd, 1, 0);
|
||||
#else
|
||||
const int result = ::poll(&pfd, 1, 0);
|
||||
#endif
|
||||
|
||||
if (result < 0)
|
||||
{
|
||||
|
|
@ -1250,5 +1254,5 @@ bool lv2_socket_native::is_socket_connected()
|
|||
}
|
||||
|
||||
// Socket is connected if it's readable or writable
|
||||
return FD_ISSET(native_socket, &readfds) || FD_ISSET(native_socket, &writefds);
|
||||
return (pfd.revents & (POLLIN | POLLOUT)) != 0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -70,6 +70,10 @@ private:
|
|||
s32 so_reuseaddr = 0;
|
||||
s32 so_reuseport = 0;
|
||||
#endif
|
||||
// Those values come from FreeBSD
|
||||
s32 min_ttl = 1;
|
||||
s32 max_ttl = 64;
|
||||
|
||||
u16 bound_port = 0;
|
||||
bool feign_tcp_conn_failure = false; // Savestate load related
|
||||
};
|
||||
|
|
|
|||
|
|
@ -112,7 +112,6 @@ public:
|
|||
|
||||
// reply is late, increases rtt
|
||||
auto& msg = it->second;
|
||||
const auto addr = msg.dst_addr.sin_addr.s_addr;
|
||||
rtt_info rtt = rtts[msg.sock_id];
|
||||
// Only increases rtt once per loop(in case a big number of packets are sent at once)
|
||||
if (!rtt_increased.count(msg.sock_id))
|
||||
|
|
@ -120,7 +119,7 @@ public:
|
|||
rtt.num_retries += 1;
|
||||
// Increases current rtt by 10%
|
||||
rtt.rtt_time += (rtt.rtt_time / 10);
|
||||
rtts[addr] = rtt;
|
||||
rtts[msg.sock_id] = rtt;
|
||||
|
||||
rtt_increased.emplace(msg.sock_id);
|
||||
}
|
||||
|
|
@ -625,7 +624,7 @@ std::tuple<bool, s32, shared_ptr<lv2_socket>, sys_net_sockaddr> lv2_socket_p2ps:
|
|||
sys_net_sockaddr ps3_addr{};
|
||||
auto* paddr = reinterpret_cast<sys_net_sockaddr_in_p2p*>(&ps3_addr);
|
||||
|
||||
lv2_socket_p2ps* sock_client = reinterpret_cast<lv2_socket_p2ps*>(idm::check_unlocked<lv2_socket>(p2ps_client));
|
||||
auto sock_client = static_cast<shared_ptr<lv2_socket_p2ps>>(idm::get_unlocked<lv2_socket>(p2ps_client));
|
||||
{
|
||||
std::lock_guard lock(sock_client->mutex);
|
||||
paddr->sin_family = SYS_NET_AF_INET;
|
||||
|
|
|
|||
|
|
@ -249,8 +249,9 @@ bool nt_p2p_port::recv_data()
|
|||
|
||||
auto& bound_sockets = ::at32(bound_p2p_vports, dst_vport);
|
||||
|
||||
for (const auto sock_id : bound_sockets)
|
||||
for (auto it = bound_sockets.begin(); it != bound_sockets.end();)
|
||||
{
|
||||
s32 sock_id = *it;
|
||||
const auto sock = idm::check<lv2_socket>(sock_id, [&](lv2_socket& sock)
|
||||
{
|
||||
ensure(sock.get_type() == SYS_NET_SOCK_DGRAM_P2P);
|
||||
|
|
@ -262,12 +263,17 @@ bool nt_p2p_port::recv_data()
|
|||
if (!sock)
|
||||
{
|
||||
sys_net.error("Socket %d found in bound_p2p_vports didn't exist!", sock_id);
|
||||
bound_sockets.erase(sock_id);
|
||||
it = bound_sockets.erase(it);
|
||||
if (bound_sockets.empty())
|
||||
{
|
||||
bound_p2p_vports.erase(dst_vport);
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
it++;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
|
|
|
|||
|
|
@ -118,6 +118,7 @@ static int clock_gettime(int clk_id, struct timespec* tp)
|
|||
|
||||
#ifndef _WIN32
|
||||
|
||||
#include <exception>
|
||||
#include <sys/time.h>
|
||||
|
||||
static struct timespec start_time = []()
|
||||
|
|
|
|||
|
|
@ -556,6 +556,8 @@ usb_handler_thread::usb_handler_thread()
|
|||
|
||||
switch (g_cfg.audio.microphone_type)
|
||||
{
|
||||
case microphone_handler::null:
|
||||
break;
|
||||
case microphone_handler::standard:
|
||||
usb_devices.push_back(std::make_shared<usb_device_mic>(0, get_new_location(), MicType::Logitech));
|
||||
break;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -464,7 +464,7 @@ namespace clan
|
|||
clan.append_child("ticket").text().set(ticket.c_str());
|
||||
clan.append_child("id").text().set(clan_id);
|
||||
|
||||
const std::string jid_str = fmt::format(JID_FORMAT, np_id.handle.data);
|
||||
const std::string jid_str = fmt::format(JID_FORMAT, np::npid_to_string(np_id));
|
||||
clan.append_child("jid").text().set(jid_str.c_str());
|
||||
|
||||
pugi::xml_document response = pugi::xml_document();
|
||||
|
|
@ -656,7 +656,7 @@ namespace clan
|
|||
clan.append_child("ticket").text().set(ticket.c_str());
|
||||
clan.append_child("id").text().set(clan_id);
|
||||
|
||||
const std::string jid_str = fmt::format(JID_FORMAT, np_id.handle.data);
|
||||
const std::string jid_str = fmt::format(JID_FORMAT, np::npid_to_string(np_id));
|
||||
clan.append_child("jid").text().set(jid_str.c_str());
|
||||
|
||||
pugi::xml_document response = pugi::xml_document();
|
||||
|
|
@ -674,7 +674,7 @@ namespace clan
|
|||
clan.append_child("ticket").text().set(ticket.c_str());
|
||||
clan.append_child("id").text().set(clan_id);
|
||||
|
||||
const std::string jid_str = fmt::format(JID_FORMAT, np_id.handle.data);
|
||||
const std::string jid_str = fmt::format(JID_FORMAT, np::npid_to_string(np_id));
|
||||
clan.append_child("jid").text().set(jid_str.c_str());
|
||||
|
||||
pugi::xml_document response = pugi::xml_document();
|
||||
|
|
@ -832,7 +832,7 @@ namespace clan
|
|||
clan.append_child("ticket").text().set(ticket.c_str());
|
||||
clan.append_child("id").text().set(clan_id);
|
||||
|
||||
const std::string jid_str = fmt::format(JID_FORMAT, np_id.handle.data);
|
||||
const std::string jid_str = fmt::format(JID_FORMAT, np::npid_to_string(np_id));
|
||||
clan.append_child("jid").text().set(jid_str.c_str());
|
||||
|
||||
pugi::xml_document response = pugi::xml_document();
|
||||
|
|
@ -850,7 +850,7 @@ namespace clan
|
|||
clan.append_child("ticket").text().set(ticket.c_str());
|
||||
clan.append_child("id").text().set(clan_id);
|
||||
|
||||
const std::string jid_str = fmt::format(JID_FORMAT, np_id.handle.data);
|
||||
const std::string jid_str = fmt::format(JID_FORMAT, np::npid_to_string(np_id));
|
||||
clan.append_child("jid").text().set(jid_str.c_str());
|
||||
|
||||
pugi::xml_document response = pugi::xml_document();
|
||||
|
|
@ -868,7 +868,7 @@ namespace clan
|
|||
clan.append_child("ticket").text().set(ticket.c_str());
|
||||
clan.append_child("id").text().set(clan_id);
|
||||
|
||||
const std::string jid_str = fmt::format(JID_FORMAT, np_id.handle.data);
|
||||
const std::string jid_str = fmt::format(JID_FORMAT, np::npid_to_string(np_id));
|
||||
clan.append_child("jid").text().set(jid_str.c_str());
|
||||
|
||||
pugi::xml_document response = pugi::xml_document();
|
||||
|
|
@ -902,7 +902,7 @@ namespace clan
|
|||
clan.append_child("id").text().set(clan_id);
|
||||
|
||||
pugi::xml_node role = clan.append_child("onlinename");
|
||||
role.text().set(nph.get_npid().handle.data);
|
||||
role.text().set(np::npid_to_string(nph.get_npid()).c_str());
|
||||
|
||||
pugi::xml_node description = clan.append_child("description");
|
||||
description.text().set(info.description);
|
||||
|
|
@ -990,7 +990,7 @@ namespace clan
|
|||
clan.append_child("ticket").text().set(ticket.c_str());
|
||||
clan.append_child("id").text().set(clan_id);
|
||||
|
||||
const std::string jid_str = fmt::format(JID_FORMAT, np_id.handle.data);
|
||||
const std::string jid_str = fmt::format(JID_FORMAT, np::npid_to_string(np_id));
|
||||
clan.append_child("jid").text().set(jid_str.c_str());
|
||||
|
||||
pugi::xml_document response = pugi::xml_document();
|
||||
|
|
@ -1008,7 +1008,7 @@ namespace clan
|
|||
clan.append_child("ticket").text().set(ticket.c_str());
|
||||
clan.append_child("id").text().set(clan_id);
|
||||
|
||||
const std::string jid_str = fmt::format(JID_FORMAT, np_id.handle.data);
|
||||
const std::string jid_str = fmt::format(JID_FORMAT, np::npid_to_string(np_id));
|
||||
clan.append_child("jid").text().set(jid_str.c_str());
|
||||
|
||||
pugi::xml_node role_node = clan.append_child("role");
|
||||
|
|
|
|||
|
|
@ -377,7 +377,7 @@ namespace np
|
|||
|
||||
if (!rooms.contains(room_id))
|
||||
{
|
||||
np_cache.error("np_cache::get_memberid cache miss room_id: room_id(%d)/npid(%s)", room_id, static_cast<const char*>(npid.handle.data));
|
||||
np_cache.error("np_cache::get_memberid cache miss room_id: room_id(%d)/npid(%s)", room_id, np::npid_to_string(npid));
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
|
|
@ -389,7 +389,7 @@ namespace np
|
|||
return id;
|
||||
}
|
||||
|
||||
np_cache.error("np_cache::get_memberid cache miss member_id: room_id(%d)/npid(%s)", room_id, static_cast<const char*>(npid.handle.data));
|
||||
np_cache.error("np_cache::get_memberid cache miss member_id: room_id(%d)/npid(%s)", room_id, np::npid_to_string(npid));
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -46,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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1024,7 +1024,7 @@ namespace np
|
|||
}
|
||||
}
|
||||
|
||||
nph_log.notice("basic_event: event:%d, from:%s(%s), size:%d", *event, static_cast<char*>(from->userId.handle.data), static_cast<char*>(from->name.data), *size);
|
||||
nph_log.notice("basic_event: event:%d, from:%s(%s), size:%d", *event, np::npid_to_string(from->userId), static_cast<char*>(from->name.data), *size);
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
|
@ -1361,7 +1361,7 @@ namespace np
|
|||
|
||||
player_history& np_handler::get_player_and_set_timestamp(const SceNpId& npid, u64 timestamp)
|
||||
{
|
||||
std::string npid_str = std::string(npid.handle.data);
|
||||
std::string npid_str = np::npid_to_string(npid);
|
||||
|
||||
if (!players_history.contains(npid_str))
|
||||
{
|
||||
|
|
@ -1641,7 +1641,7 @@ namespace np
|
|||
return SCE_NP_BASIC_ERROR_NOT_CONNECTED;
|
||||
}
|
||||
|
||||
auto friend_infos = rpcn->get_friend_presence_by_npid(std::string(npid.handle.data));
|
||||
auto friend_infos = rpcn->get_friend_presence_by_npid(np::npid_to_string(npid));
|
||||
if (!friend_infos)
|
||||
{
|
||||
return SCE_NP_BASIC_ERROR_INVALID_ARGUMENT;
|
||||
|
|
|
|||
|
|
@ -89,6 +89,13 @@ namespace np
|
|||
// npid->reserved[0] = 1;
|
||||
}
|
||||
|
||||
std::string npid_to_string(const SceNpId& npid)
|
||||
{
|
||||
char npid_str[17]{};
|
||||
std::memcpy(npid_str, npid.handle.data, 16);
|
||||
return std::string(npid_str);
|
||||
}
|
||||
|
||||
void string_to_online_name(std::string_view str, SceNpOnlineName& online_name)
|
||||
{
|
||||
memset(&online_name, 0, sizeof(online_name));
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ namespace np
|
|||
std::optional<SceNpCommunicationId> string_to_communication_id(std::string_view str);
|
||||
|
||||
void string_to_npid(std::string_view str, SceNpId& npid);
|
||||
std::string npid_to_string(const 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 strings_to_userinfo(std::string_view npid, std::string_view online_name, std::string_view avatar_url, SceNpUserInfo& user_info);
|
||||
|
|
|
|||
|
|
@ -41,7 +41,7 @@ namespace np
|
|||
return;
|
||||
}
|
||||
|
||||
rpcn_log.notice("Received notification that user %s(%d) joined the room(%d)", notif_data->roomMemberDataInternal->userInfo.npId.handle.data, notif_data->roomMemberDataInternal->memberId, room_id);
|
||||
rpcn_log.notice("Received notification that user %s(%d) joined the room(%d)", np::npid_to_string(notif_data->roomMemberDataInternal->userInfo.npId), notif_data->roomMemberDataInternal->memberId, room_id);
|
||||
extra_nps::print_SceNpMatching2RoomMemberDataInternal(notif_data->roomMemberDataInternal.get_ptr());
|
||||
|
||||
// We initiate signaling if necessary
|
||||
|
|
@ -54,7 +54,7 @@ namespace np
|
|||
const u16 member_id = notif_data->roomMemberDataInternal->memberId;
|
||||
const SceNpId& npid = notif_data->roomMemberDataInternal->userInfo.npId;
|
||||
|
||||
rpcn_log.notice("Join notification told to connect to member(%d=%s) of room(%d): %s:%d", member_id, reinterpret_cast<const char*>(npid.handle.data), room_id, ip_to_string(addr_p2p), port_p2p);
|
||||
rpcn_log.notice("Join notification told to connect to member(%d=%s) of room(%d): %s:%d", member_id, np::npid_to_string(npid), room_id, ip_to_string(addr_p2p), port_p2p);
|
||||
|
||||
// Attempt Signaling
|
||||
auto& sigh = g_fxo->get<named_thread<signaling_handler>>();
|
||||
|
|
@ -98,7 +98,7 @@ namespace np
|
|||
return;
|
||||
}
|
||||
|
||||
rpcn_log.notice("Received notification that user %s(%d) left the room(%d)", notif_data->roomMemberDataInternal->userInfo.npId.handle.data, notif_data->roomMemberDataInternal->memberId, room_id);
|
||||
rpcn_log.notice("Received notification that user %s(%d) left the room(%d)", np::npid_to_string(notif_data->roomMemberDataInternal->userInfo.npId), notif_data->roomMemberDataInternal->memberId, room_id);
|
||||
extra_nps::print_SceNpMatching2RoomMemberDataInternal(notif_data->roomMemberDataInternal.get_ptr());
|
||||
|
||||
if (room_event_cb)
|
||||
|
|
@ -204,7 +204,7 @@ namespace np
|
|||
return;
|
||||
}
|
||||
|
||||
rpcn_log.notice("Received notification that user's %s(%d) room (%d) data was updated", notif_data->newRoomMemberDataInternal->userInfo.npId.handle.data, notif_data->newRoomMemberDataInternal->memberId, room_id);
|
||||
rpcn_log.notice("Received notification that user's %s(%d) room (%d) data was updated", np::npid_to_string(notif_data->newRoomMemberDataInternal->userInfo.npId), notif_data->newRoomMemberDataInternal->memberId, room_id);
|
||||
extra_nps::print_SceNpMatching2RoomMemberDataInternal(notif_data->newRoomMemberDataInternal.get_ptr());
|
||||
|
||||
if (room_event_cb)
|
||||
|
|
|
|||
|
|
@ -310,7 +310,7 @@ namespace np
|
|||
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, np::npid_to_string(*npid_p2p), room_id, ip_to_string(addr_p2p), port_p2p);
|
||||
|
||||
// Attempt Signaling
|
||||
auto& sigh = g_fxo->get<named_thread<signaling_handler>>();
|
||||
|
|
@ -951,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;
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
#include "stdafx.h"
|
||||
#include <span>
|
||||
#include "np_structs_extra.h"
|
||||
#include "np_helpers.h"
|
||||
|
||||
LOG_CHANNEL(sceNp);
|
||||
LOG_CHANNEL(sceNp2);
|
||||
|
|
@ -13,7 +14,7 @@ namespace extra_nps
|
|||
void print_SceNpUserInfo2(const SceNpUserInfo2* user)
|
||||
{
|
||||
sceNp2.warning("SceNpUserInfo2:");
|
||||
sceNp2.warning("npid: %s", static_cast<const char*>(user->npId.handle.data));
|
||||
sceNp2.warning("npid: %s", np::npid_to_string(user->npId));
|
||||
sceNp2.warning("onlineName: *0x%x(%s)", user->onlineName, user->onlineName ? static_cast<const char*>(user->onlineName->data) : "");
|
||||
sceNp2.warning("avatarUrl: *0x%x(%s)", user->avatarUrl, user->avatarUrl ? static_cast<const char*>(user->avatarUrl->data) : "");
|
||||
}
|
||||
|
|
@ -208,7 +209,7 @@ namespace extra_nps
|
|||
{
|
||||
sceNp2.warning("SceNpMatching2RoomMemberDataInternal:");
|
||||
sceNp2.warning("next: *0x%x", member->next);
|
||||
sceNp2.warning("npId: %s", member->userInfo.npId.handle.data);
|
||||
sceNp2.warning("npId: %s", np::npid_to_string(member->userInfo.npId));
|
||||
sceNp2.warning("onlineName: %s", member->userInfo.onlineName ? member->userInfo.onlineName->data : "");
|
||||
sceNp2.warning("avatarUrl: %s", member->userInfo.avatarUrl ? member->userInfo.avatarUrl->data : "");
|
||||
sceNp2.warning("joinDate: %lld", member->joinDate.tick);
|
||||
|
|
@ -460,7 +461,7 @@ namespace extra_nps
|
|||
void print_SceNpScoreRankData(const SceNpScoreRankData* data)
|
||||
{
|
||||
sceNp.warning("sceNpScoreRankData:");
|
||||
sceNp.warning("npId: %s", static_cast<const char*>(data->npId.handle.data));
|
||||
sceNp.warning("npId: %s", np::npid_to_string(data->npId));
|
||||
sceNp.warning("onlineName: %s", static_cast<const char*>(data->onlineName.data));
|
||||
sceNp.warning("pcId: %d", data->pcId);
|
||||
sceNp.warning("serialRank: %d", data->serialRank);
|
||||
|
|
@ -474,7 +475,7 @@ namespace extra_nps
|
|||
void print_SceNpScoreRankData_deprecated(const SceNpScoreRankData_deprecated* data)
|
||||
{
|
||||
sceNp.warning("sceNpScoreRankData_deprecated:");
|
||||
sceNp.warning("npId: %s", static_cast<const char*>(data->npId.handle.data));
|
||||
sceNp.warning("npId: %s", np::npid_to_string(data->npId));
|
||||
sceNp.warning("onlineName: %s", static_cast<const char*>(data->onlineName.data));
|
||||
sceNp.warning("serialRank: %d", data->serialRank);
|
||||
sceNp.warning("rank: %d", data->rank);
|
||||
|
|
@ -542,7 +543,7 @@ namespace extra_nps
|
|||
|
||||
void print_SceNpUserInfo(const SceNpUserInfo* data)
|
||||
{
|
||||
sceNp.warning("userId: %s", data->userId.handle.data);
|
||||
sceNp.warning("userId: %s", np::npid_to_string(data->userId));
|
||||
sceNp.warning("name: %s", data->name.data);
|
||||
sceNp.warning("icon: %s", data->icon.data);
|
||||
}
|
||||
|
|
@ -576,7 +577,7 @@ namespace extra_nps
|
|||
|
||||
if (data->kick_actor)
|
||||
{
|
||||
sceNp.warning("kick_actor: %s", data->kick_actor->handle.data);
|
||||
sceNp.warning("kick_actor: %s", np::npid_to_string(*data->kick_actor));
|
||||
}
|
||||
|
||||
sceNp.warning("opt: 0x%x", data->kick_actor);
|
||||
|
|
|
|||
|
|
@ -263,7 +263,7 @@ namespace np
|
|||
for (u32 i = 0; i < room_info->memberList.membersNum; i++)
|
||||
{
|
||||
SceNpMatching2RoomMemberDataInternal* sce_member = &room_info->memberList.members[i];
|
||||
if (strcmp(sce_member->userInfo.npId.handle.data, npid.handle.data) == 0)
|
||||
if (strncmp(sce_member->userInfo.npId.handle.data, npid.handle.data, 16) == 0)
|
||||
{
|
||||
room_info->memberList.me = room_info->memberList.members + i;
|
||||
edata.add_relocation<SceNpMatching2RoomMemberDataInternal>(room_info->memberList.me);
|
||||
|
|
|
|||
|
|
@ -896,7 +896,7 @@ namespace rpcn
|
|||
return error_and_disconnect("Failed to send all the bytes");
|
||||
}
|
||||
|
||||
res = 0;
|
||||
continue;
|
||||
}
|
||||
n_sent += res;
|
||||
}
|
||||
|
|
@ -1055,6 +1055,8 @@ namespace rpcn
|
|||
found = found->ai_next;
|
||||
}
|
||||
|
||||
freeaddrinfo(addr_info);
|
||||
|
||||
if (!found_ipv4)
|
||||
{
|
||||
rpcn_log.error("connect: Failed to find IPv4 for %s", host);
|
||||
|
|
@ -1156,7 +1158,7 @@ namespace rpcn
|
|||
if (!connected || terminate)
|
||||
{
|
||||
state = rpcn_state::failure_other;
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (received_version != RPCN_PROTOCOL_VERSION)
|
||||
|
|
@ -1467,7 +1469,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));
|
||||
|
|
@ -1478,19 +1480,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)
|
||||
|
|
@ -1755,7 +1756,7 @@ namespace rpcn
|
|||
{
|
||||
continue;
|
||||
}
|
||||
pb_req.add_alloweduser(req->allowedUser[i].handle.data);
|
||||
pb_req.add_alloweduser(np::npid_to_string(req->allowedUser[i]));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1767,7 +1768,7 @@ namespace rpcn
|
|||
{
|
||||
continue;
|
||||
}
|
||||
pb_req.add_blockeduser(req->blockedUser[i].handle.data);
|
||||
pb_req.add_blockeduser(np::npid_to_string(req->blockedUser[i]));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -2266,7 +2267,7 @@ namespace rpcn
|
|||
for (usz i = 0; i < npids.size(); i++)
|
||||
{
|
||||
auto* npid_entry = pb_req.add_npids();
|
||||
npid_entry->set_npid(static_cast<const char*>(npids[i].first.handle.data));
|
||||
npid_entry->set_npid(np::npid_to_string(npids[i].first));
|
||||
npid_entry->set_pcid(npids[i].second);
|
||||
}
|
||||
|
||||
|
|
@ -2317,7 +2318,7 @@ namespace rpcn
|
|||
{
|
||||
np2_structs::GetScoreGameDataRequest pb_req;
|
||||
pb_req.set_boardid(board_id);
|
||||
pb_req.set_npid(reinterpret_cast<const char*>(npid.handle.data));
|
||||
pb_req.set_npid(np::npid_to_string(npid));
|
||||
pb_req.set_pcid(pc_id);
|
||||
|
||||
std::string serialized;
|
||||
|
|
@ -2413,7 +2414,7 @@ namespace rpcn
|
|||
|
||||
if (option->isLastChangedAuthorId)
|
||||
{
|
||||
pb_req.set_islastchangedauthorid(option->isLastChangedAuthorId->handle.data);
|
||||
pb_req.set_islastchangedauthorid(np::npid_to_string(*option->isLastChangedAuthorId));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -2442,7 +2443,7 @@ namespace rpcn
|
|||
|
||||
if (option->isLastChangedAuthorId)
|
||||
{
|
||||
pb_req.set_islastchangedauthorid(option->isLastChangedAuthorId->handle.data);
|
||||
pb_req.set_islastchangedauthorid(np::npid_to_string(*option->isLastChangedAuthorId));
|
||||
}
|
||||
|
||||
if (option->compareValue)
|
||||
|
|
@ -2498,7 +2499,7 @@ namespace rpcn
|
|||
|
||||
if (option->isLastChangedAuthorId)
|
||||
{
|
||||
pb_req.set_islastchangedauthorid(option->isLastChangedAuthorId->handle.data);
|
||||
pb_req.set_islastchangedauthorid(np::npid_to_string(*option->isLastChangedAuthorId));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -79,6 +79,14 @@ public:
|
|||
res.push_back(vec[i]);
|
||||
i++;
|
||||
}
|
||||
|
||||
// Make sure we hit terminating 0
|
||||
if (i >= vec.size())
|
||||
{
|
||||
error = true;
|
||||
return {};
|
||||
}
|
||||
|
||||
i++;
|
||||
|
||||
if (!empty && res.empty())
|
||||
|
|
@ -293,7 +301,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();
|
||||
|
|
|
|||
|
|
@ -256,9 +256,7 @@ void signaling_handler::process_incoming_messages()
|
|||
addr.s_addr = op_addr;
|
||||
char ip_str[16];
|
||||
inet_ntop(AF_INET, &addr, ip_str, sizeof(ip_str));
|
||||
std::string_view npid(sp->npid.handle.data);
|
||||
|
||||
sign_log.trace("SP %s from %s:%d(npid: %s)", sp->command, ip_str, op_port, npid);
|
||||
sign_log.trace("SP %s from %s:%d(npid: %s)", sp->command, ip_str, op_port, np::npid_to_string(sp->npid));
|
||||
}
|
||||
|
||||
bool reply = false, schedule_repeat = false;
|
||||
|
|
@ -426,9 +424,10 @@ void signaling_handler::operator()()
|
|||
if (sig.sig_info->time_last_msg_recvd < now - 60s && cmd != signal_info)
|
||||
{
|
||||
// We had no connection to opponent for 60 seconds, consider the connection dead
|
||||
auto retire_info = sig.sig_info;
|
||||
sign_log.notice("Timeout disconnection");
|
||||
update_si_status(sig.sig_info, SCE_NP_SIGNALING_CONN_STATUS_INACTIVE, SCE_NP_SIGNALING_ERROR_TIMEOUT);
|
||||
retire_packet(sig.sig_info, signal_ping); // Retire ping packet if necessary
|
||||
update_si_status(retire_info, SCE_NP_SIGNALING_CONN_STATUS_INACTIVE, SCE_NP_SIGNALING_ERROR_TIMEOUT);
|
||||
retire_packet(retire_info, signal_ping); // Retire ping packet if necessary
|
||||
break; // qpackets has been emptied of all packets for this user so we're requeuing
|
||||
}
|
||||
|
||||
|
|
@ -674,9 +673,7 @@ std::shared_ptr<signaling_info> signaling_handler::get_signaling_ptr(const signa
|
|||
{
|
||||
u32 conn_id;
|
||||
|
||||
char npid_buf[17]{};
|
||||
memcpy(npid_buf, sp->npid.handle.data, 16);
|
||||
std::string npid(npid_buf);
|
||||
std::string npid = np::npid_to_string(sp->npid);
|
||||
|
||||
if (!npid_to_conn_id.contains(npid))
|
||||
return nullptr;
|
||||
|
|
@ -784,7 +781,7 @@ void signaling_handler::send_information_packets(u32 addr, u16 port, const SceNp
|
|||
|
||||
u32 signaling_handler::get_always_conn_id(const SceNpId& npid)
|
||||
{
|
||||
std::string npid_str(reinterpret_cast<const char*>(npid.handle.data));
|
||||
std::string npid_str = np::npid_to_string(npid);
|
||||
if (npid_to_conn_id.contains(npid_str))
|
||||
return ::at32(npid_to_conn_id, npid_str);
|
||||
|
||||
|
|
@ -810,9 +807,8 @@ u32 signaling_handler::init_sig1(const SceNpId& npid)
|
|||
sig_peers[conn_id]->conn_status = SCE_NP_SIGNALING_CONN_STATUS_PENDING;
|
||||
|
||||
// Request peer infos from RPCN
|
||||
std::string npid_str(reinterpret_cast<const char*>(npid.handle.data));
|
||||
auto& nph = g_fxo->get<named_thread<np::np_handler>>();
|
||||
nph.req_sign_infos(npid_str, conn_id);
|
||||
nph.req_sign_infos(np::npid_to_string(npid), conn_id);
|
||||
}
|
||||
|
||||
return conn_id;
|
||||
|
|
@ -839,7 +835,7 @@ std::optional<u32> signaling_handler::get_conn_id_from_npid(const SceNpId& npid)
|
|||
{
|
||||
std::lock_guard lock(data_mutex);
|
||||
|
||||
std::string npid_str(reinterpret_cast<const char*>(npid.handle.data));
|
||||
std::string npid_str = np::npid_to_string(npid);
|
||||
if (npid_to_conn_id.contains(npid_str))
|
||||
return ::at32(npid_to_conn_id, npid_str);
|
||||
|
||||
|
|
|
|||
|
|
@ -1200,27 +1200,59 @@ namespace rsx
|
|||
fmt::throw_exception("Unknown format 0x%x", texture_format);
|
||||
}
|
||||
|
||||
bool is_int8_remapped_format(u32 format)
|
||||
rsx::flags32_t get_format_features(u32 texture_format)
|
||||
{
|
||||
switch (format)
|
||||
switch (texture_format)
|
||||
{
|
||||
case CELL_GCM_TEXTURE_B8:
|
||||
case CELL_GCM_TEXTURE_A1R5G5B5:
|
||||
case CELL_GCM_TEXTURE_A4R4G4B4:
|
||||
case CELL_GCM_TEXTURE_R5G6B5:
|
||||
case CELL_GCM_TEXTURE_A8R8G8B8:
|
||||
case CELL_GCM_TEXTURE_COMPRESSED_DXT1:
|
||||
case CELL_GCM_TEXTURE_COMPRESSED_DXT23:
|
||||
case CELL_GCM_TEXTURE_COMPRESSED_DXT45:
|
||||
case CELL_GCM_TEXTURE_G8B8:
|
||||
case CELL_GCM_TEXTURE_COMPRESSED_B8R8_G8R8:
|
||||
case CELL_GCM_TEXTURE_COMPRESSED_R8B8_R8G8:
|
||||
case CELL_GCM_TEXTURE_R6G5B5:
|
||||
case CELL_GCM_TEXTURE_R5G5B5A1:
|
||||
case CELL_GCM_TEXTURE_D1R5G5B5:
|
||||
case CELL_GCM_TEXTURE_D8R8G8B8:
|
||||
// Base texture formats - everything is supported
|
||||
return RSX_FORMAT_FEATURE_SIGNED_COMPONENTS | RSX_FORMAT_FEATURE_GAMMA_CORRECTION | RSX_FORMAT_FEATURE_BIASED_NORMALIZATION;
|
||||
|
||||
case CELL_GCM_TEXTURE_DEPTH24_D8:
|
||||
case CELL_GCM_TEXTURE_DEPTH24_D8_FLOAT:
|
||||
case CELL_GCM_TEXTURE_DEPTH16:
|
||||
case CELL_GCM_TEXTURE_DEPTH16_FLOAT:
|
||||
// Depth textures will hang the hardware if BX2 or GAMMA is active. ARGB8_SIGNED has no impact.
|
||||
// UNSIGNED_REMAP=BIASED works on all formats including the float variants.
|
||||
return RSX_FORMAT_FEATURE_BIASED_NORMALIZATION;
|
||||
|
||||
case CELL_GCM_TEXTURE_X16:
|
||||
// X16 - GAMMA causes hangs. ARGB8_SIGNED is ignored. UNSIGNED_REMAP=BIASED works.
|
||||
return RSX_FORMAT_FEATURE_BIASED_NORMALIZATION | RSX_FORMAT_FEATURE_16BIT_CHANNELS;
|
||||
case CELL_GCM_TEXTURE_Y16_X16:
|
||||
// X16 | Y16 - GAMMA causes hangs. ARGB8_SIGNED works. UNSIGNED_REMAP=BIASED also works.
|
||||
return RSX_FORMAT_FEATURE_SIGNED_COMPONENTS | RSX_FORMAT_FEATURE_BIASED_NORMALIZATION | RSX_FORMAT_FEATURE_16BIT_CHANNELS;
|
||||
|
||||
case CELL_GCM_TEXTURE_COMPRESSED_HILO8:
|
||||
// GAMMA causes GPU hangs. ARGB8_SIGNED is ignored. UNSIGNED_REMAP=BIASED works.
|
||||
return RSX_FORMAT_FEATURE_BIASED_NORMALIZATION;
|
||||
|
||||
case CELL_GCM_TEXTURE_COMPRESSED_HILO_S8:
|
||||
// GAMMA causes hangs. Other flags ignored.
|
||||
return 0;
|
||||
|
||||
case CELL_GCM_TEXTURE_W16_Z16_Y16_X16_FLOAT:
|
||||
case CELL_GCM_TEXTURE_W32_Z32_Y32_X32_FLOAT:
|
||||
case CELL_GCM_TEXTURE_X32_FLOAT:
|
||||
case CELL_GCM_TEXTURE_Y16_X16_FLOAT:
|
||||
// NOTE: Special data formats (XY, HILO, DEPTH) are not RGB formats
|
||||
return false;
|
||||
default:
|
||||
return true;
|
||||
// Floating point textures. Nothing works.
|
||||
return 0;
|
||||
}
|
||||
fmt::throw_exception("Unknown format 0x%x", texture_format);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -9,6 +9,8 @@
|
|||
|
||||
namespace rsx
|
||||
{
|
||||
using flags32_t = u32;
|
||||
|
||||
enum texture_upload_context : u32
|
||||
{
|
||||
shader_read = 1,
|
||||
|
|
@ -125,6 +127,32 @@ namespace rsx
|
|||
|
||||
using namespace format_class_;
|
||||
|
||||
enum format_features : u8
|
||||
{
|
||||
RSX_FORMAT_FEATURE_SIGNED_COMPONENTS = (1 << 0),
|
||||
RSX_FORMAT_FEATURE_BIASED_NORMALIZATION = (1 << 1),
|
||||
RSX_FORMAT_FEATURE_GAMMA_CORRECTION = (1 << 2),
|
||||
RSX_FORMAT_FEATURE_16BIT_CHANNELS = (1 << 3), // Complements RSX_FORMAT_FEATURE_SIGNED_COMPONENTS
|
||||
};
|
||||
|
||||
using enum format_features;
|
||||
|
||||
struct texture_format_ex
|
||||
{
|
||||
texture_format_ex() = default;
|
||||
texture_format_ex(u32 bits)
|
||||
: format_bits(bits)
|
||||
{}
|
||||
|
||||
bool valid() const { return format_bits != 0; }
|
||||
u32 format() const { return format_bits & ~(CELL_GCM_TEXTURE_LN | CELL_GCM_TEXTURE_UN); }
|
||||
|
||||
//private:
|
||||
u32 format_bits = 0;
|
||||
u32 features = 0;
|
||||
u32 texel_remap_control = 0;
|
||||
};
|
||||
|
||||
// Sampled image descriptor
|
||||
class sampled_image_descriptor_base
|
||||
{
|
||||
|
|
@ -167,6 +195,7 @@ namespace rsx
|
|||
u64 surface_cache_tag = 0;
|
||||
|
||||
texcoord_xform_t texcoord_xform;
|
||||
texture_format_ex format_ex;
|
||||
};
|
||||
|
||||
struct typeless_xfer
|
||||
|
|
@ -257,7 +286,12 @@ namespace rsx
|
|||
u8 get_format_sample_count(rsx::surface_antialiasing antialias);
|
||||
u32 get_max_depth_value(rsx::surface_depth_format2 format);
|
||||
bool is_depth_stencil_format(rsx::surface_depth_format2 format);
|
||||
bool is_int8_remapped_format(u32 format); // Returns true if the format is treated as INT8 by the RSX remapper.
|
||||
|
||||
/**
|
||||
* Format feature support. There is not simple format to determine what is supported here, results are from hw tests
|
||||
* Returns a bitmask of supported features.
|
||||
*/
|
||||
rsx::flags32_t get_format_features(u32 texture_format);
|
||||
|
||||
/**
|
||||
* Returns number of texel rows encoded in one pitch-length line of bytes
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
#include "GLGSRender.h"
|
||||
#include "../rsx_methods.h"
|
||||
#include "../Common/BufferUtils.h"
|
||||
#include "../Program/GLSLCommon.h"
|
||||
|
||||
#include "Emu/RSX/NV47/HW/context_accessors.define.h"
|
||||
|
||||
|
|
@ -315,6 +316,8 @@ void GLGSRender::load_texture_env()
|
|||
|
||||
if (sampler_state->validate())
|
||||
{
|
||||
sampler_state->format_ex = tex.format_ex();
|
||||
|
||||
if (m_textures_dirty[i])
|
||||
{
|
||||
m_fs_sampler_states[i].apply(tex, fs_sampler_state[i].get());
|
||||
|
|
@ -324,12 +327,17 @@ void GLGSRender::load_texture_env()
|
|||
m_graphics_state |= rsx::fragment_program_state_dirty;
|
||||
}
|
||||
|
||||
if (const auto texture_format = tex.format() & ~(CELL_GCM_TEXTURE_UN | CELL_GCM_TEXTURE_LN);
|
||||
sampler_state->format_class != rsx::classify_format(texture_format) &&
|
||||
(texture_format == CELL_GCM_TEXTURE_A8R8G8B8 || texture_format == CELL_GCM_TEXTURE_D8R8G8B8))
|
||||
const auto texture_format = sampler_state->format_ex.format();
|
||||
// Depth format redirected to BGRA8 resample stage. Do not filter to avoid bits leaking.
|
||||
// If accurate graphics are desired, force a bitcast to COLOR as a workaround.
|
||||
const bool is_depth_reconstructed = sampler_state->format_class != rsx::classify_format(texture_format) &&
|
||||
(texture_format == CELL_GCM_TEXTURE_A8R8G8B8 || texture_format == CELL_GCM_TEXTURE_D8R8G8B8);
|
||||
// SNORM conversion required in shader. Do not interpolate to avoid introducing discontinuities due to how negative numbers work
|
||||
const bool is_snorm = (sampler_state->format_ex.texel_remap_control & rsx::texture_control_bits::SEXT_MASK) != 0;
|
||||
|
||||
if (is_depth_reconstructed || is_snorm)
|
||||
{
|
||||
// Depth format redirected to BGRA8 resample stage. Do not filter to avoid bits leaking.
|
||||
// If accurate graphics are desired, force a bitcast to COLOR as a workaround.
|
||||
m_fs_sampler_states[i].set_parameteri(GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||||
m_fs_sampler_states[i].set_parameteri(GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1192,7 +1192,7 @@ bool FragmentProgramDecompiler::handle_tex_srb(u32 opcode)
|
|||
if (dst.exp_tex)
|
||||
{
|
||||
properties.has_exp_tex_op = true;
|
||||
AddCode("_enable_texture_expand();");
|
||||
AddCode("_enable_texture_expand($_i);");
|
||||
}
|
||||
|
||||
// Shadow proj
|
||||
|
|
|
|||
|
|
@ -337,21 +337,21 @@ namespace glsl
|
|||
// Declare special texture control flags
|
||||
program_common::define_glsl_constants<rsx::texture_control_bits>(OS,
|
||||
{
|
||||
{ "GAMMA_R_BIT " , rsx::texture_control_bits::GAMMA_R },
|
||||
{ "GAMMA_G_BIT " , rsx::texture_control_bits::GAMMA_G },
|
||||
{ "GAMMA_B_BIT " , rsx::texture_control_bits::GAMMA_B },
|
||||
{ "GAMMA_A_BIT " , rsx::texture_control_bits::GAMMA_A },
|
||||
{ "EXPAND_R_BIT" , rsx::texture_control_bits::EXPAND_R },
|
||||
{ "EXPAND_G_BIT" , rsx::texture_control_bits::EXPAND_G },
|
||||
{ "EXPAND_B_BIT" , rsx::texture_control_bits::EXPAND_B },
|
||||
{ "EXPAND_A_BIT" , rsx::texture_control_bits::EXPAND_A },
|
||||
{ "SEXT_R_BIT" , rsx::texture_control_bits::SEXT_R },
|
||||
{ "SEXT_G_BIT" , rsx::texture_control_bits::SEXT_G },
|
||||
{ "SEXT_B_BIT" , rsx::texture_control_bits::SEXT_B },
|
||||
{ "SEXT_A_BIT" , rsx::texture_control_bits::SEXT_A },
|
||||
{ "WRAP_S_BIT", rsx::texture_control_bits::WRAP_S },
|
||||
{ "WRAP_T_BIT", rsx::texture_control_bits::WRAP_T },
|
||||
{ "WRAP_R_BIT", rsx::texture_control_bits::WRAP_R },
|
||||
{ "GAMMA_R_BIT ", rsx::texture_control_bits::GAMMA_R },
|
||||
{ "GAMMA_G_BIT ", rsx::texture_control_bits::GAMMA_G },
|
||||
{ "GAMMA_B_BIT ", rsx::texture_control_bits::GAMMA_B },
|
||||
{ "GAMMA_A_BIT ", rsx::texture_control_bits::GAMMA_A },
|
||||
{ "EXPAND_R_BIT", rsx::texture_control_bits::EXPAND_R },
|
||||
{ "EXPAND_G_BIT", rsx::texture_control_bits::EXPAND_G },
|
||||
{ "EXPAND_B_BIT", rsx::texture_control_bits::EXPAND_B },
|
||||
{ "EXPAND_A_BIT", rsx::texture_control_bits::EXPAND_A },
|
||||
{ "SEXT_R_BIT", rsx::texture_control_bits::SEXT_R },
|
||||
{ "SEXT_G_BIT", rsx::texture_control_bits::SEXT_G },
|
||||
{ "SEXT_B_BIT", rsx::texture_control_bits::SEXT_B },
|
||||
{ "SEXT_A_BIT", rsx::texture_control_bits::SEXT_A },
|
||||
{ "WRAP_S_BIT", rsx::texture_control_bits::WRAP_S },
|
||||
{ "WRAP_T_BIT", rsx::texture_control_bits::WRAP_T },
|
||||
{ "WRAP_R_BIT", rsx::texture_control_bits::WRAP_R },
|
||||
|
||||
{ "ALPHAKILL ", rsx::texture_control_bits::ALPHAKILL },
|
||||
{ "RENORMALIZE ", rsx::texture_control_bits::RENORMALIZE },
|
||||
|
|
@ -360,7 +360,12 @@ namespace glsl
|
|||
{ "FILTERED_MAG_BIT", rsx::texture_control_bits::FILTERED_MAG },
|
||||
{ "FILTERED_MIN_BIT", rsx::texture_control_bits::FILTERED_MIN },
|
||||
{ "INT_COORDS_BIT ", rsx::texture_control_bits::UNNORMALIZED_COORDS },
|
||||
{ "CLAMP_COORDS_BIT", rsx::texture_control_bits::CLAMP_TEXCOORDS_BIT }
|
||||
{ "CLAMP_COORDS_BIT", rsx::texture_control_bits::CLAMP_TEXCOORDS_BIT },
|
||||
|
||||
{ "FORMAT_FEATURE_SIGNED_BIT", rsx::texture_control_bits::FF_SIGNED_BIT },
|
||||
{ "FORMAT_FEATURE_GAMMA_BIT", rsx::texture_control_bits::FF_GAMMA_BIT },
|
||||
{ "FORMAT_FEATURE_BIASED_RENORMALIZATION_BIT", rsx::texture_control_bits::FF_BIASED_RENORM_BIT },
|
||||
{ "FORMAT_FEATURE_16BIT_CHANNELS_BIT", rsx::texture_control_bits::FF_16BIT_CHANNELS_BIT }
|
||||
});
|
||||
|
||||
if (props.require_texture_expand)
|
||||
|
|
|
|||
|
|
@ -37,12 +37,17 @@ namespace rsx
|
|||
WRAP_S,
|
||||
WRAP_T,
|
||||
WRAP_R,
|
||||
FF_SIGNED_BIT,
|
||||
FF_BIASED_RENORM_BIT,
|
||||
FF_GAMMA_BIT,
|
||||
FF_16BIT_CHANNELS_BIT,
|
||||
|
||||
GAMMA_CTRL_MASK = (1 << GAMMA_R) | (1 << GAMMA_G) | (1 << GAMMA_B) | (1 << GAMMA_A),
|
||||
EXPAND_MASK = (1 << EXPAND_R) | (1 << EXPAND_G) | (1 << EXPAND_B) | (1 << EXPAND_A),
|
||||
EXPAND_OFFSET = EXPAND_A,
|
||||
SEXT_MASK = (1 << SEXT_R) | (1 << SEXT_G) | (1 << SEXT_B) | (1 << SEXT_A),
|
||||
SEXT_OFFSET = SEXT_A
|
||||
SEXT_OFFSET = SEXT_A,
|
||||
FORMAT_FEATURES_OFFSET = FF_SIGNED_BIT,
|
||||
};
|
||||
|
||||
enum ROP_control_bits : u32
|
||||
|
|
|
|||
|
|
@ -20,13 +20,34 @@ R"(
|
|||
#define SEXT_MASK (SEXT_R_MASK | SEXT_G_MASK | SEXT_B_MASK | SEXT_A_MASK)
|
||||
#define FILTERED_MASK (FILTERED_MAG_BIT | FILTERED_MIN_BIT)
|
||||
|
||||
#define FORMAT_FEATURE_SIGNED (1 << FORMAT_FEATURE_SIGNED_BIT)
|
||||
#define FORMAT_FEATURE_GAMMA (1 << FORMAT_FEATURE_GAMMA_BIT)
|
||||
#define FORMAT_FEATURE_BIASED_RENORMALIZATION (1 << FORMAT_FEATURE_BIASED_RENORMALIZATION_BIT)
|
||||
#define FORMAT_FEATURE_16BIT_CHANNELS (1 << FORMAT_FEATURE_16BIT_CHANNELS_BIT)
|
||||
#define FORMAT_FEATURE_MASK (FORMAT_FEATURE_SIGNED | FORMAT_FEATURE_GAMMA | FORMAT_FEATURE_BIASED_RENORMALIZATION | FORMAT_FEATURE_16BIT_CHANNELS)
|
||||
|
||||
#ifdef _ENABLE_TEXTURE_EXPAND
|
||||
// NOTE: BX2 expansion overrides GAMMA correction
|
||||
uint _texture_flag_override = 0;
|
||||
#define _enable_texture_expand() _texture_flag_override = SIGN_EXPAND_MASK
|
||||
#define _disable_texture_expand() _texture_flag_override = 0
|
||||
#define TEX_FLAGS(index) (TEX_PARAM(index).flags | _texture_flag_override)
|
||||
uint _texture_flag_erase = 0;
|
||||
bool _texture_bx2_active = false;
|
||||
#define _enable_texture_expand(index) \
|
||||
do { \
|
||||
if (_test_bit(TEX_PARAM(index).flags, FORMAT_FEATURE_BIASED_RENORMALIZATION_BIT)) { \
|
||||
_texture_flag_override = SIGN_EXPAND_MASK & (_get_bits(TEX_PARAM(index).remap, 16, 4) << EXPAND_A_BIT); \
|
||||
_texture_flag_erase = GAMMA_CTRL_MASK; \
|
||||
_texture_bx2_active = true; \
|
||||
} \
|
||||
} while (false)
|
||||
#define _disable_texture_expand() \
|
||||
do { \
|
||||
_texture_flag_override = 0; \
|
||||
_texture_flag_erase = 0; \
|
||||
_texture_bx2_active = false; \
|
||||
} while (false)
|
||||
#define TEX_FLAGS(index) ((TEX_PARAM(index).flags & ~(_texture_flag_erase)) | _texture_flag_override)
|
||||
#else
|
||||
#define TEX_FLAGS(index) TEX_PARAM(index).flags
|
||||
#define TEX_FLAGS(index) (TEX_PARAM(index).flags)
|
||||
#endif
|
||||
|
||||
#define TEX_NAME(index) tex##index
|
||||
|
|
@ -175,15 +196,24 @@ vec4 _texcoord_xform_shadow(const in vec4 coord4, const in sampler_info params)
|
|||
vec4 _sext_unorm8x4(const in vec4 x)
|
||||
{
|
||||
// TODO: Handle clamped sign-extension
|
||||
const vec4 bits = floor(fma(x, vec4(255.f), vec4(0.5f)));
|
||||
const bvec4 sign_check = lessThan(bits, vec4(128.f));
|
||||
const vec4 ret = _select(bits - 256.f, bits, sign_check);
|
||||
return ret / 127.f;
|
||||
const uint shift = 32 - 8; // sext 8-bit value into 32-bit container
|
||||
const uvec4 ubits = uvec4(floor(fma(x, vec4(255.f), vec4(0.5f))));
|
||||
const ivec4 ibits = ivec4(ubits << shift);
|
||||
return (ibits >> shift) / 127.f;
|
||||
}
|
||||
|
||||
vec4 _sext_unorm16x4(const in vec4 x)
|
||||
{
|
||||
// TODO: Handle clamped sign-extension
|
||||
const uint shift = 32 - 16; // sext 16-bit value into 32-bit container
|
||||
const uvec4 ubits = uvec4(floor(fma(x, vec4(65535.f), vec4(0.5f))));
|
||||
const ivec4 ibits = ivec4(ubits << shift);
|
||||
return (ibits >> shift) / 32767.f;
|
||||
}
|
||||
|
||||
vec4 _process_texel(in vec4 rgba, const in uint control_bits)
|
||||
{
|
||||
if (control_bits == 0)
|
||||
if ((control_bits & ~FORMAT_FEATURE_MASK) == 0u)
|
||||
{
|
||||
return rgba;
|
||||
}
|
||||
|
|
@ -210,31 +240,46 @@ vec4 _process_texel(in vec4 rgba, const in uint control_bits)
|
|||
uvec4 mask;
|
||||
vec4 convert;
|
||||
|
||||
uint op_mask = control_bits & uint(SIGN_EXPAND_MASK);
|
||||
if (op_mask != 0u)
|
||||
{
|
||||
// Expand to signed normalized by decompressing the signal
|
||||
mask = uvec4(op_mask) & uvec4(EXPAND_R_MASK, EXPAND_G_MASK, EXPAND_B_MASK, EXPAND_A_MASK);
|
||||
convert = (rgba * 2.f - 1.f);
|
||||
rgba = _select(rgba, convert, notEqual(mask, uvec4(0)));
|
||||
}
|
||||
uint op_mask = control_bits & uint(SEXT_MASK);
|
||||
uint ch_mask = 0xFu;
|
||||
|
||||
op_mask = control_bits & uint(SEXT_MASK);
|
||||
if (op_mask != 0u)
|
||||
{
|
||||
// Sign-extend the input signal
|
||||
mask = uvec4(op_mask) & uvec4(SEXT_R_MASK, SEXT_G_MASK, SEXT_B_MASK, SEXT_A_MASK);
|
||||
convert = _sext_unorm8x4(rgba);
|
||||
if (_test_bit(control_bits, FORMAT_FEATURE_16BIT_CHANNELS_BIT))
|
||||
convert = _sext_unorm16x4(rgba);
|
||||
else
|
||||
convert = _sext_unorm8x4(rgba);
|
||||
rgba = _select(rgba, convert, notEqual(mask, uvec4(0)));
|
||||
ch_mask &= ~(op_mask >> SEXT_A_BIT);
|
||||
}
|
||||
|
||||
op_mask = control_bits & uint(GAMMA_CTRL_MASK);
|
||||
op_mask = control_bits & uint(GAMMA_CTRL_MASK) & (ch_mask << GAMMA_A_BIT);
|
||||
if (op_mask != 0u)
|
||||
{
|
||||
// Gamma correction
|
||||
mask = uvec4(op_mask) & uvec4(GAMMA_R_MASK, GAMMA_G_MASK, GAMMA_B_MASK, GAMMA_A_MASK);
|
||||
convert = srgb_to_linear(rgba);
|
||||
return _select(rgba, convert, notEqual(mask, uvec4(0)));
|
||||
rgba = _select(rgba, convert, notEqual(mask, uvec4(0)));
|
||||
ch_mask &= ~(op_mask >> GAMMA_A_BIT);
|
||||
}
|
||||
|
||||
op_mask = control_bits & uint(SIGN_EXPAND_MASK) & (ch_mask << EXPAND_A_BIT);
|
||||
if (op_mask != 0u)
|
||||
{
|
||||
// Expand to signed normalized by decompressing the signal
|
||||
mask = uvec4(op_mask) & uvec4(EXPAND_R_MASK, EXPAND_G_MASK, EXPAND_B_MASK, EXPAND_A_MASK);
|
||||
#ifdef _ENABLE_TEXTURE_EXPAND
|
||||
if (_texture_bx2_active)
|
||||
convert = (rgba * 2.f - 1.f);
|
||||
else
|
||||
#endif
|
||||
if (_test_bit(control_bits, FORMAT_FEATURE_16BIT_CHANNELS_BIT))
|
||||
convert = (floor(fma(rgba, vec4(65535.f), vec4(0.5f))) - 32768.f) / 32767.f;
|
||||
else
|
||||
convert = (floor(fma(rgba, vec4(255.f), vec4(0.5f))) - 128.f) / 127.f;
|
||||
rgba = _select(rgba, convert, notEqual(mask, uvec4(0)));
|
||||
}
|
||||
|
||||
return rgba;
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ namespace glsl
|
|||
glsl_compute_program = 2,
|
||||
|
||||
// Meta
|
||||
glsl_invalid_program = 0xff
|
||||
glsl_invalid_program = 7
|
||||
};
|
||||
|
||||
enum glsl_rules : unsigned char
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
#include "stdafx.h"
|
||||
#include "ProgramStateCache.h"
|
||||
#include "FragmentProgramDecompiler.h"
|
||||
#include "Emu/system_config.h"
|
||||
#include "Emu/RSX/Core/RSXDriverState.h"
|
||||
#include "util/sysinfo.hpp"
|
||||
|
|
@ -637,58 +638,51 @@ fragment_program_utils::fragment_program_metadata fragment_program_utils::analys
|
|||
while (true)
|
||||
{
|
||||
const auto inst = v128::loadu(instBuffer, index);
|
||||
const auto d0 = OPDEST::from_be32(inst._u32[0]);
|
||||
const auto opcode = static_cast<rsx::assembler::FP_opcode>(d0.opcode);
|
||||
|
||||
// Check for opcode high bit which indicates a branch instructions (opcode 0x40...0x45)
|
||||
if (inst._u32[2] & (1 << 23))
|
||||
switch (opcode)
|
||||
{
|
||||
case RSX_FP_OPCODE_TEX:
|
||||
case RSX_FP_OPCODE_TEXBEM:
|
||||
case RSX_FP_OPCODE_TXP:
|
||||
case RSX_FP_OPCODE_TXPBEM:
|
||||
case RSX_FP_OPCODE_TXD:
|
||||
case RSX_FP_OPCODE_TXB:
|
||||
case RSX_FP_OPCODE_TXL:
|
||||
result.referenced_textures_mask |= (1 << d0.tex_num);
|
||||
result.has_tex_bx2_conv |= !!d0.exp_tex;
|
||||
break;
|
||||
case RSX_FP_OPCODE_PK4:
|
||||
case RSX_FP_OPCODE_UP4:
|
||||
case RSX_FP_OPCODE_PK2:
|
||||
case RSX_FP_OPCODE_UP2:
|
||||
case RSX_FP_OPCODE_PKB:
|
||||
case RSX_FP_OPCODE_UPB:
|
||||
case RSX_FP_OPCODE_PK16:
|
||||
case RSX_FP_OPCODE_UP16:
|
||||
case RSX_FP_OPCODE_PKG:
|
||||
case RSX_FP_OPCODE_UPG:
|
||||
result.has_pack_instructions = true;
|
||||
break;
|
||||
case RSX_FP_OPCODE_BRK:
|
||||
case RSX_FP_OPCODE_CAL:
|
||||
case RSX_FP_OPCODE_IFE:
|
||||
case RSX_FP_OPCODE_LOOP:
|
||||
case RSX_FP_OPCODE_REP:
|
||||
case RSX_FP_OPCODE_RET:
|
||||
// NOTE: Jump instructions are not yet proved to work outside of loops and if/else blocks
|
||||
// Otherwise we would need to follow the execution chain
|
||||
result.has_branch_instructions = true;
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
const u32 opcode = (inst._u32[0] >> 16) & 0x3F;
|
||||
if (opcode)
|
||||
{
|
||||
switch (opcode)
|
||||
{
|
||||
case RSX_FP_OPCODE_TEX:
|
||||
case RSX_FP_OPCODE_TEXBEM:
|
||||
case RSX_FP_OPCODE_TXP:
|
||||
case RSX_FP_OPCODE_TXPBEM:
|
||||
case RSX_FP_OPCODE_TXD:
|
||||
case RSX_FP_OPCODE_TXB:
|
||||
case RSX_FP_OPCODE_TXL:
|
||||
{
|
||||
//Bits 17-20 of word 1, swapped within u16 sections
|
||||
//Bits 16-23 are swapped into the upper 8 bits (24-31)
|
||||
const u32 tex_num = (inst._u32[0] >> 25) & 15;
|
||||
result.referenced_textures_mask |= (1 << tex_num);
|
||||
break;
|
||||
}
|
||||
case RSX_FP_OPCODE_PK4:
|
||||
case RSX_FP_OPCODE_UP4:
|
||||
case RSX_FP_OPCODE_PK2:
|
||||
case RSX_FP_OPCODE_UP2:
|
||||
case RSX_FP_OPCODE_PKB:
|
||||
case RSX_FP_OPCODE_UPB:
|
||||
case RSX_FP_OPCODE_PK16:
|
||||
case RSX_FP_OPCODE_UP16:
|
||||
case RSX_FP_OPCODE_PKG:
|
||||
case RSX_FP_OPCODE_UPG:
|
||||
{
|
||||
result.has_pack_instructions = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (is_any_src_constant(inst))
|
||||
{
|
||||
//Instruction references constant, skip one slot occupied by data
|
||||
index++;
|
||||
result.program_constants_buffer_length += 16;
|
||||
}
|
||||
if (rsx::assembler::FP::get_operand_count(opcode) > 0 &&
|
||||
is_any_src_constant(inst))
|
||||
{
|
||||
// Instruction references constant, skip one slot occupied by data
|
||||
index++;
|
||||
result.program_constants_buffer_length += 16;
|
||||
}
|
||||
|
||||
index++;
|
||||
|
|
|
|||
|
|
@ -59,6 +59,7 @@ namespace program_hash_util
|
|||
|
||||
bool has_pack_instructions;
|
||||
bool has_branch_instructions;
|
||||
bool has_tex_bx2_conv;
|
||||
bool is_nop_shader; // Does this affect Z-pass testing???
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -54,6 +54,14 @@ union OPDEST
|
|||
u32 : 9;
|
||||
u32 write_mask : 4;
|
||||
};
|
||||
|
||||
static OPDEST from_be32(u32 be_word)
|
||||
{
|
||||
const u32 _hex =
|
||||
((be_word & 0x00FF00FF) << 8) |
|
||||
((be_word & 0xFF00FF00) >> 8);
|
||||
return OPDEST{ .HEX = _hex };
|
||||
}
|
||||
};
|
||||
|
||||
union SRC0
|
||||
|
|
|
|||
|
|
@ -2,8 +2,24 @@
|
|||
#include "RSXTexture.h"
|
||||
|
||||
#include "rsx_utils.h"
|
||||
#include "Common/TextureUtils.h"
|
||||
#include "Program/GLSLCommon.h"
|
||||
|
||||
#include "Emu/system_config.h"
|
||||
#include "util/simd.hpp"
|
||||
|
||||
#if !defined(_MSC_VER)
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wold-style-cast"
|
||||
#endif
|
||||
|
||||
#if defined(ARCH_ARM64)
|
||||
#if !defined(_MSC_VER)
|
||||
#pragma GCC diagnostic ignored "-Wstrict-aliasing"
|
||||
#endif
|
||||
#undef FORCE_INLINE
|
||||
#include "Emu/CPU/sse2neon.h"
|
||||
#endif
|
||||
|
||||
namespace rsx
|
||||
{
|
||||
|
|
@ -49,6 +65,65 @@ namespace rsx
|
|||
return ((registers[NV4097_SET_TEXTURE_FORMAT + (m_index * 8)] >> 8) & 0xff);
|
||||
}
|
||||
|
||||
texture_format_ex fragment_texture::format_ex() const
|
||||
{
|
||||
const auto format_bits = format();
|
||||
const auto base_format = format_bits & ~(CELL_GCM_TEXTURE_UN | CELL_GCM_TEXTURE_LN);
|
||||
const auto format_features = rsx::get_format_features(base_format);
|
||||
if (format_features == 0)
|
||||
{
|
||||
return { format_bits };
|
||||
}
|
||||
|
||||
// NOTE: The unsigned_remap=bias flag being set flags the texture as being compressed normal (2n-1 / BX2) (UE3)
|
||||
// NOTE: The ARGB8_signed flag means to reinterpret the raw bytes as signed. This is different than unsigned_remap=bias which does range decompression.
|
||||
// This is a separate method of setting the format to signed mode without doing so per-channel
|
||||
// Precedence = SNORM > GAMMA > UNSIGNED_REMAP/BX2
|
||||
// Games using mixed flags: (See Resistance 3 for GAMMA/BX2 relationship, UE3 for BX2 effect)
|
||||
u32 argb_signed_ = 0;
|
||||
u32 unsigned_remap_ = 0;
|
||||
u32 gamma_ = 0;
|
||||
|
||||
if (format_features & RSX_FORMAT_FEATURE_SIGNED_COMPONENTS)
|
||||
{
|
||||
// Tests show this is applied pre-readout. It's just a property of the incoming bytes and is therefore subject to remap.
|
||||
argb_signed_ = decoded_remap().shuffle_mask_bits(argb_signed());
|
||||
}
|
||||
|
||||
if (format_features & RSX_FORMAT_FEATURE_GAMMA_CORRECTION)
|
||||
{
|
||||
// Tests show this is applied post-readout. It's a property of the final value stored in the register and is not remapped.
|
||||
// NOTE: GAMMA correction has no algorithmic effect on constants (0 and 1) so we need not mask it out for correctness.
|
||||
gamma_ = gamma() & ~(argb_signed_);
|
||||
}
|
||||
|
||||
if (format_features & RSX_FORMAT_FEATURE_BIASED_NORMALIZATION)
|
||||
{
|
||||
// The renormalization flag applies to all channels. It is weaker than the other flags.
|
||||
// This applies on input and is subject to remap overrides
|
||||
if (unsigned_remap() == CELL_GCM_TEXTURE_UNSIGNED_REMAP_BIASED)
|
||||
{
|
||||
unsigned_remap_ = decoded_remap().shuffle_mask_bits(0xFu) & ~(argb_signed_ | gamma_);
|
||||
}
|
||||
}
|
||||
|
||||
u32 format_convert = gamma_;
|
||||
|
||||
// The options are mutually exclusive
|
||||
ensure((argb_signed_ & gamma_) == 0);
|
||||
ensure((argb_signed_ & unsigned_remap_) == 0);
|
||||
ensure((gamma_ & unsigned_remap_) == 0);
|
||||
|
||||
// NOTE: Hardware tests show that remapping bypasses the channel swizzles completely
|
||||
format_convert |= (argb_signed_ << texture_control_bits::SEXT_OFFSET);
|
||||
format_convert |= (unsigned_remap_ << texture_control_bits::EXPAND_OFFSET);
|
||||
|
||||
texture_format_ex result { format_bits };
|
||||
result.features = format_features;
|
||||
result.texel_remap_control = format_convert;
|
||||
return result;
|
||||
}
|
||||
|
||||
bool fragment_texture::is_compressed_format() const
|
||||
{
|
||||
int texture_format = format() & ~(CELL_GCM_TEXTURE_LN | CELL_GCM_TEXTURE_UN);
|
||||
|
|
@ -291,7 +366,68 @@ namespace rsx
|
|||
|
||||
u32 fragment_texture::border_color() const
|
||||
{
|
||||
return registers[NV4097_SET_TEXTURE_BORDER_COLOR + (m_index * 8)];
|
||||
const u32 raw = registers[NV4097_SET_TEXTURE_BORDER_COLOR + (m_index * 8)];
|
||||
const u32 sext = argb_signed();
|
||||
|
||||
if (!sext) [[ likely ]]
|
||||
{
|
||||
return raw;
|
||||
}
|
||||
|
||||
// Border color is broken on PS3. The SNORM behavior is completely broken and behaves like BIASED renormalization instead.
|
||||
// To solve the mismatch, we need to first do a bit expansion on the value then store it as sign extended. The second part is a natural part of numbers on a binary system, so we only need to do the former.
|
||||
// Note that the input color is in BE order (BGRA) so we reverse the mask to match.
|
||||
static constexpr u32 expand4_lut[16] =
|
||||
{
|
||||
0x00000000u, // 0000
|
||||
0xFF000000u, // 0001
|
||||
0x00FF0000u, // 0010
|
||||
0xFFFF0000u, // 0011
|
||||
0x0000FF00u, // 0100
|
||||
0xFF00FF00u, // 0101
|
||||
0x00FFFF00u, // 0110
|
||||
0xFFFFFF00u, // 0111
|
||||
0x000000FFu, // 1000
|
||||
0xFF0000FFu, // 1001
|
||||
0x00FF00FFu, // 1010
|
||||
0xFFFF00FFu, // 1011
|
||||
0x0000FFFFu, // 1100
|
||||
0xFF00FFFFu, // 1101
|
||||
0x00FFFFFFu, // 1110
|
||||
0xFFFFFFFFu // 1111
|
||||
};
|
||||
|
||||
// Bit pattern expand
|
||||
const u32 mask = expand4_lut[sext];
|
||||
|
||||
// Now we perform the compensation operation
|
||||
// BIAS operation = (V - 128 / 127)
|
||||
|
||||
// Load
|
||||
const __m128i _0 = _mm_setzero_si128();
|
||||
const __m128i _128 = _mm_set1_epi32(128);
|
||||
|
||||
// Explode the bytes.
|
||||
__m128i v = _mm_cvtsi32_si128(raw);
|
||||
v = _mm_unpacklo_epi8(v, _0);
|
||||
v = _mm_unpacklo_epi16(v, _0);
|
||||
|
||||
// Conversion: x = (y - 128)
|
||||
v = _mm_sub_epi32(v, _128);
|
||||
|
||||
// Convert to signed encoding (reverse sext)
|
||||
v = _mm_slli_epi32(v, 24);
|
||||
v = _mm_srli_epi32(v, 24);
|
||||
|
||||
// Pack down
|
||||
v = _mm_packs_epi32(v, _0);
|
||||
v = _mm_packus_epi16(v, _0);
|
||||
|
||||
// Read
|
||||
const u32 conv = _mm_cvtsi128_si32(v);
|
||||
|
||||
// Merge
|
||||
return (conv & mask) | (raw & ~mask);
|
||||
}
|
||||
|
||||
color4f fragment_texture::remapped_border_color() const
|
||||
|
|
|
|||
|
|
@ -4,6 +4,8 @@
|
|||
|
||||
namespace rsx
|
||||
{
|
||||
struct texture_format_ex;
|
||||
|
||||
class fragment_texture
|
||||
{
|
||||
protected:
|
||||
|
|
@ -33,6 +35,7 @@ namespace rsx
|
|||
// cubemap as a separate dimension.
|
||||
rsx::texture_dimension_extended get_extended_texture_dimension() const;
|
||||
u8 format() const;
|
||||
texture_format_ex format_ex() const;
|
||||
bool is_compressed_format() const;
|
||||
u16 mipmap() const;
|
||||
|
||||
|
|
|
|||
|
|
@ -2321,55 +2321,17 @@ namespace rsx
|
|||
}
|
||||
}
|
||||
|
||||
if (rsx::is_int8_remapped_format(format))
|
||||
if (const auto& format_ex = sampler_descriptors[i]->format_ex; format_ex.features != 0)
|
||||
{
|
||||
// Special operations applied to 8-bit formats such as gamma correction and sign conversion
|
||||
// NOTE: The unsigned_remap=bias flag being set flags the texture as being compressed normal (2n-1 / BX2) (UE3)
|
||||
// NOTE: The ARGB8_signed flag means to reinterpret the raw bytes as signed. This is different than unsigned_remap=bias which does range decompression.
|
||||
// This is a separate method of setting the format to signed mode without doing so per-channel
|
||||
// Precedence = SNORM > GAMMA > UNSIGNED_REMAP (See Resistance 3 for GAMMA/BX2 relationship, UE3 for BX2 effect)
|
||||
texture_control |= format_ex.texel_remap_control;
|
||||
texture_control |= format_ex.features << texture_control_bits::FORMAT_FEATURES_OFFSET;
|
||||
|
||||
const u32 argb8_signed = tex.argb_signed(); // _SNROM
|
||||
const u32 gamma = tex.gamma() & ~argb8_signed; // _SRGB
|
||||
const u32 unsigned_remap = (tex.unsigned_remap() == CELL_GCM_TEXTURE_UNSIGNED_REMAP_NORMAL)? 0u : (~(gamma | argb8_signed) & 0xF); // _BX2
|
||||
u32 argb8_convert = gamma;
|
||||
|
||||
// The options are mutually exclusive
|
||||
ensure((argb8_signed & gamma) == 0);
|
||||
ensure((argb8_signed & unsigned_remap) == 0);
|
||||
ensure((gamma & unsigned_remap) == 0);
|
||||
|
||||
// Helper function to apply a per-channel mask based on an input mask
|
||||
const auto apply_sign_convert_mask = [&](u32 mask, u32 bit_offset)
|
||||
if (current_fp_metadata.has_tex_bx2_conv)
|
||||
{
|
||||
// TODO: Use actual remap mask to account for 0 and 1 overrides in default mapping
|
||||
// TODO: Replace this clusterfuck of texture control with matrix transformation
|
||||
const auto remap_ctrl = (tex.remap() >> 8) & 0xAA;
|
||||
if (remap_ctrl == 0xAA)
|
||||
{
|
||||
argb8_convert |= (mask & 0xFu) << bit_offset;
|
||||
return;
|
||||
}
|
||||
|
||||
if ((remap_ctrl & 0x03) == 0x02) argb8_convert |= (mask & 0x1u) << bit_offset;
|
||||
if ((remap_ctrl & 0x0C) == 0x08) argb8_convert |= (mask & 0x2u) << bit_offset;
|
||||
if ((remap_ctrl & 0x30) == 0x20) argb8_convert |= (mask & 0x4u) << bit_offset;
|
||||
if ((remap_ctrl & 0xC0) == 0x80) argb8_convert |= (mask & 0x8u) << bit_offset;
|
||||
};
|
||||
|
||||
if (argb8_signed)
|
||||
{
|
||||
// Apply integer sign extension from uint8 to sint8 and renormalize
|
||||
apply_sign_convert_mask(argb8_signed, texture_control_bits::SEXT_OFFSET);
|
||||
const u32 remap_hi = tex.decoded_remap().shuffle_mask_bits(0xFu);
|
||||
current_fragment_program.texture_params[i].remap &= ~(0xFu << 16u);
|
||||
current_fragment_program.texture_params[i].remap |= (remap_hi << 16u);
|
||||
}
|
||||
|
||||
if (unsigned_remap)
|
||||
{
|
||||
// Apply sign expansion, compressed normal-map style (2n - 1)
|
||||
apply_sign_convert_mask(unsigned_remap, texture_control_bits::EXPAND_OFFSET);
|
||||
}
|
||||
|
||||
texture_control |= argb8_convert;
|
||||
}
|
||||
|
||||
current_fragment_program.texture_params[i].control = texture_control;
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
#include "stdafx.h"
|
||||
#include "../Common/BufferUtils.h"
|
||||
#include "../Program/GLSLCommon.h"
|
||||
#include "../rsx_methods.h"
|
||||
|
||||
#include "VKAsyncScheduler.h"
|
||||
|
|
@ -275,7 +276,7 @@ void VKGSRender::load_texture_env()
|
|||
|
||||
auto get_border_color = [&](const rsx::Texture auto& tex)
|
||||
{
|
||||
return m_device->get_custom_border_color_support().require_border_color_remap
|
||||
return m_device->get_custom_border_color_support().require_border_color_remap
|
||||
? tex.remapped_border_color()
|
||||
: rsx::decode_border_color(tex.border_color());
|
||||
};
|
||||
|
|
@ -307,6 +308,8 @@ void VKGSRender::load_texture_env()
|
|||
|
||||
if (sampler_state->validate())
|
||||
{
|
||||
sampler_state->format_ex = tex.format_ex();
|
||||
|
||||
if (sampler_state->is_cyclic_reference)
|
||||
{
|
||||
check_for_cyclic_refs |= true;
|
||||
|
|
@ -324,7 +327,7 @@ void VKGSRender::load_texture_env()
|
|||
f32 min_lod = 0.f, max_lod = 0.f;
|
||||
f32 lod_bias = 0.f;
|
||||
|
||||
const u32 texture_format = tex.format() & ~(CELL_GCM_TEXTURE_UN | CELL_GCM_TEXTURE_LN);
|
||||
const u32 texture_format = sampler_state->format_ex.format();
|
||||
VkBool32 compare_enabled = VK_FALSE;
|
||||
VkCompareOp depth_compare_mode = VK_COMPARE_OP_NEVER;
|
||||
|
||||
|
|
@ -350,7 +353,8 @@ void VKGSRender::load_texture_env()
|
|||
if (sampler_state->format_class == RSX_FORMAT_CLASS_COLOR) [[likely]]
|
||||
{
|
||||
// Most PS3-like formats can be linearly filtered without problem
|
||||
can_sample_linear = true;
|
||||
// Exclude textures that require SNORM conversion however
|
||||
can_sample_linear = (sampler_state->format_ex.texel_remap_control & rsx::texture_control_bits::SEXT_MASK) == 0;
|
||||
}
|
||||
else if (sampler_state->format_class != rsx::classify_format(texture_format) &&
|
||||
(texture_format == CELL_GCM_TEXTURE_A8R8G8B8 || texture_format == CELL_GCM_TEXTURE_D8R8G8B8))
|
||||
|
|
|
|||
|
|
@ -1541,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)
|
||||
{
|
||||
|
|
@ -1550,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
|
||||
|
|
@ -1572,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();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -221,7 +221,7 @@ 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);
|
||||
|
||||
|
|
@ -254,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;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -97,7 +97,7 @@ void VKGSRender::reinitialize_swapchain()
|
|||
{
|
||||
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
|
||||
|
|
@ -135,6 +135,7 @@ void VKGSRender::reinitialize_swapchain()
|
|||
|
||||
swapchain_unavailable = false;
|
||||
should_reinitialize_swapchain = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
void VKGSRender::present(vk::frame_context_t *ctx)
|
||||
|
|
@ -426,11 +427,32 @@ 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 = &m_frame_context_storage[m_current_queue_index];
|
||||
|
|
@ -582,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);
|
||||
|
|
|
|||
|
|
@ -182,16 +182,8 @@ namespace vk
|
|||
return found == m_generic_sampler_pool.end() ? nullptr : found->second.get();
|
||||
}
|
||||
|
||||
const auto block = m_custom_color_sampler_pool.equal_range(key.base_key);
|
||||
for (auto it = block.first; it != block.second; ++it)
|
||||
{
|
||||
if (it->second->key.border_color_key == key.border_color_key)
|
||||
{
|
||||
return it->second.get();
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
const auto found = m_custom_color_sampler_pool.find(key);
|
||||
return found == m_custom_color_sampler_pool.end() ? nullptr : found->second.get();
|
||||
}
|
||||
|
||||
cached_sampler_object_t* sampler_pool_t::emplace(const sampler_pool_key_t& key, std::unique_ptr<cached_sampler_object_t>& object)
|
||||
|
|
@ -204,7 +196,7 @@ namespace vk
|
|||
return iterator->second.get();
|
||||
}
|
||||
|
||||
const auto [iterator, _unused] = m_custom_color_sampler_pool.emplace(key.base_key, std::move(object));
|
||||
const auto [iterator, _unused] = m_custom_color_sampler_pool.emplace(key, std::move(object));
|
||||
return iterator->second.get();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -64,6 +64,22 @@ namespace vk
|
|||
{
|
||||
u64 base_key;
|
||||
u64 border_color_key;
|
||||
|
||||
bool operator == (const sampler_pool_key_t& that) const
|
||||
{
|
||||
return this->base_key == that.base_key &&
|
||||
this->border_color_key == that.border_color_key;
|
||||
}
|
||||
};
|
||||
|
||||
struct sampler_pool_key_hash
|
||||
{
|
||||
size_t operator()(const vk::sampler_pool_key_t& k) const noexcept
|
||||
{
|
||||
usz result = k.base_key;
|
||||
result ^= k.border_color_key + 0x9e3779b97f4a7c15ULL + (result << 6) + (result >> 2);
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
struct cached_sampler_object_t : public vk::sampler, public rsx::ref_counted
|
||||
|
|
@ -75,7 +91,7 @@ namespace vk
|
|||
class sampler_pool_t
|
||||
{
|
||||
std::unordered_map<u64, std::unique_ptr<cached_sampler_object_t>> m_generic_sampler_pool;
|
||||
std::unordered_map<u64, std::unique_ptr<cached_sampler_object_t>> m_custom_color_sampler_pool;
|
||||
std::unordered_map<sampler_pool_key_t, std::unique_ptr<cached_sampler_object_t>, sampler_pool_key_hash> m_custom_color_sampler_pool;
|
||||
|
||||
public:
|
||||
|
||||
|
|
|
|||
|
|
@ -39,6 +39,34 @@ namespace rsx
|
|||
return remapped;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Remap color channel bits based on a remap vector. The output is a normalized selector of each color channel with spread.
|
||||
* The input bits are an action selector. e.g a mask of channels that need to be interpreted as SNORM or BX2
|
||||
* The output is a final mask on which post-sampling channels the operation applies to.
|
||||
* Examples:
|
||||
* - If we have remap as [ 1 R R R ] and mask of R (0010) then we get 1110. Remapper spreads 'R' action to all channels where it should apply.
|
||||
*/
|
||||
u32 shuffle_mask_bits(u32 bits) const
|
||||
{
|
||||
if (!bits || encoded == RSX_TEXTURE_REMAP_IDENTITY) [[likely]]
|
||||
{
|
||||
return bits;
|
||||
}
|
||||
|
||||
u32 result = 0;
|
||||
for (u8 channel = 0; channel < 4; ++channel)
|
||||
{
|
||||
if (control_map[channel] != CELL_GCM_TEXTURE_REMAP_REMAP || // Channel not read from input
|
||||
(bits & (1u << channel_map[channel])) == 0) // Input channel is not enabled in the mask
|
||||
{
|
||||
continue;
|
||||
}
|
||||
result |= (1u << channel);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
requires std::is_integral_v<T> || std::is_floating_point_v<T>
|
||||
std::array<T, 4> remap(const std::array<T, 4>& components) const
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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));
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -248,7 +248,7 @@ struct cfg_root : cfg::node
|
|||
cfg::string audio_device{ this, "Audio Device", "@@@default@@@", true };
|
||||
cfg::_int<0, 200> volume{ this, "Master Volume", 100, true };
|
||||
cfg::_bool enable_buffering{ this, "Enable Buffering", true, true };
|
||||
cfg::_int <4, 250> desired_buffer_duration{ this, "Desired Audio Buffer Duration", 100, true };
|
||||
cfg::_int <4, 250> desired_buffer_duration{ this, "Desired Audio Buffer Duration", 34, true };
|
||||
cfg::_bool enable_time_stretching{ this, "Enable Time Stretching", false, true };
|
||||
cfg::_bool disable_sampling_skip{ this, "Disable Sampling Skip", false, true };
|
||||
cfg::_int<0, 100> time_stretching_threshold{ this, "Time Stretching Threshold", 75, true };
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
#include "util/types.hpp"
|
||||
#include <string>
|
||||
#include <set>
|
||||
#include <vector>
|
||||
|
||||
enum class game_content_type
|
||||
{
|
||||
|
|
|
|||
146
rpcs3/Input/mouse_gyro_handler.cpp
Normal file
146
rpcs3/Input/mouse_gyro_handler.cpp
Normal 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
|
||||
}
|
||||
}
|
||||
33
rpcs3/Input/mouse_gyro_handler.h
Normal file
33
rpcs3/Input/mouse_gyro_handler.h
Normal 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);
|
||||
};
|
||||
|
|
@ -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;
|
||||
|
|
@ -624,15 +631,18 @@ void pad_thread::operator()()
|
|||
if (!pad->is_connected())
|
||||
continue;
|
||||
|
||||
for (const auto& button : pad->m_buttons)
|
||||
for (const Button& button : pad->m_buttons)
|
||||
{
|
||||
if (button.m_pressed && (
|
||||
button.m_outKeyCode == CELL_PAD_CTRL_CROSS ||
|
||||
button.m_outKeyCode == CELL_PAD_CTRL_CIRCLE ||
|
||||
button.m_outKeyCode == CELL_PAD_CTRL_TRIANGLE ||
|
||||
button.m_outKeyCode == CELL_PAD_CTRL_SQUARE ||
|
||||
button.m_outKeyCode == CELL_PAD_CTRL_START ||
|
||||
button.m_outKeyCode == CELL_PAD_CTRL_SELECT))
|
||||
(button.m_offset == CELL_PAD_BTN_OFFSET_DIGITAL1 && (
|
||||
button.m_outKeyCode == CELL_PAD_CTRL_START ||
|
||||
button.m_outKeyCode == CELL_PAD_CTRL_SELECT)) ||
|
||||
(button.m_offset == CELL_PAD_BTN_OFFSET_DIGITAL2 && (
|
||||
button.m_outKeyCode == CELL_PAD_CTRL_CROSS ||
|
||||
button.m_outKeyCode == CELL_PAD_CTRL_CIRCLE ||
|
||||
button.m_outKeyCode == CELL_PAD_CTRL_TRIANGLE ||
|
||||
button.m_outKeyCode == CELL_PAD_CTRL_SQUARE))
|
||||
))
|
||||
{
|
||||
any_button_pressed = true;
|
||||
break;
|
||||
|
|
@ -669,7 +679,7 @@ void pad_thread::operator()()
|
|||
break;
|
||||
}
|
||||
|
||||
for (const auto& button : pad->m_buttons)
|
||||
for (const Button& button : pad->m_buttons)
|
||||
{
|
||||
if (button.m_offset == CELL_PAD_BTN_OFFSET_DIGITAL1 && button.m_outKeyCode == CELL_PAD_CTRL_PS && button.m_pressed)
|
||||
{
|
||||
|
|
@ -728,7 +738,7 @@ void pad_thread::operator()()
|
|||
if (!pad->is_connected())
|
||||
continue;
|
||||
|
||||
for (const auto& button : pad->m_buttons)
|
||||
for (const Button& button : pad->m_buttons)
|
||||
{
|
||||
if (button.m_offset == CELL_PAD_BTN_OFFSET_DIGITAL1 && button.m_outKeyCode == CELL_PAD_CTRL_START && button.m_pressed)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -13,16 +13,16 @@ void unload_iso();
|
|||
|
||||
struct iso_extent_info
|
||||
{
|
||||
u64 start;
|
||||
u64 size;
|
||||
u64 start = 0;
|
||||
u64 size = 0;
|
||||
};
|
||||
|
||||
struct iso_fs_metadata
|
||||
{
|
||||
std::string name;
|
||||
s64 time;
|
||||
bool is_directory;
|
||||
bool has_multiple_extents;
|
||||
s64 time = 0;
|
||||
bool is_directory = false;
|
||||
bool has_multiple_extents = false;
|
||||
std::vector<iso_extent_info> extents;
|
||||
|
||||
u64 size() const;
|
||||
|
|
@ -30,7 +30,7 @@ struct iso_fs_metadata
|
|||
|
||||
struct iso_fs_node
|
||||
{
|
||||
iso_fs_metadata metadata;
|
||||
iso_fs_metadata metadata {};
|
||||
std::vector<std::unique_ptr<iso_fs_node>> children;
|
||||
};
|
||||
|
||||
|
|
@ -38,7 +38,7 @@ class iso_file : public fs::file_base
|
|||
{
|
||||
private:
|
||||
fs::file m_file;
|
||||
iso_fs_metadata m_meta;
|
||||
iso_fs_metadata m_meta {};
|
||||
u64 m_pos = 0;
|
||||
|
||||
std::pair<u64, iso_extent_info> get_extent_pos(u64 pos) const;
|
||||
|
|
@ -80,7 +80,7 @@ class iso_archive
|
|||
{
|
||||
private:
|
||||
std::string m_path;
|
||||
iso_fs_node m_root;
|
||||
iso_fs_node m_root {};
|
||||
fs::file m_file;
|
||||
|
||||
public:
|
||||
|
|
|
|||
|
|
@ -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" />
|
||||
|
|
|
|||
|
|
@ -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">
|
||||
|
|
|
|||
|
|
@ -157,6 +157,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
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,8 +25,41 @@ namespace
|
|||
{
|
||||
static NEVER_INLINE void emit_data(YAML::Emitter& out, const YAML::Node& node)
|
||||
{
|
||||
// TODO
|
||||
out << node;
|
||||
if (!node || node.IsNull())
|
||||
{
|
||||
// I chose to output a null when nothing is present so that recursive YAML Value calls can be matched to a null value instead of nothing
|
||||
out << YAML::Null;
|
||||
return;
|
||||
}
|
||||
if (node.IsMap())
|
||||
{
|
||||
std::vector<std::string> keys;
|
||||
keys.reserve(node.size());
|
||||
// generate vector of strings to be sorted using the as function from YAML documentation
|
||||
for (const auto& pair : node)
|
||||
{
|
||||
keys.push_back(pair.first.as<std::string>());
|
||||
}
|
||||
std::sort(keys.begin(), keys.end());
|
||||
// recursively generate sorted maps
|
||||
// alternative implementations could have stops at specified recursion levels or maybe just the first two levels would be sorted
|
||||
out << YAML::BeginMap;
|
||||
for (const std::string& key : keys)
|
||||
{
|
||||
out << YAML::Key << key;
|
||||
out << YAML::Value;
|
||||
emit_data(out, node[key]);
|
||||
}
|
||||
out << YAML::EndMap;
|
||||
}
|
||||
// alternatively: an else statement could be used however I wanted to follow a similar format to the += operator so the YAML Undefined class can be ignored
|
||||
else if (node.IsScalar() || node.IsSequence())
|
||||
{
|
||||
out << node;
|
||||
}
|
||||
// this exists to preserve the same functionality as before where Undefined nodes would still be output, can be removed or consolidated with the else if branch
|
||||
else
|
||||
out << node;
|
||||
}
|
||||
|
||||
// Incrementally load YAML
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -163,11 +163,6 @@ log_frame::log_frame(std::shared_ptr<gui_settings> _gui_settings, QWidget* paren
|
|||
CreateAndConnectActions();
|
||||
LoadSettings();
|
||||
|
||||
if (m_ansi_tty)
|
||||
{
|
||||
m_tty_ansi_highlighter = new AnsiHighlighter(m_tty->document());
|
||||
}
|
||||
|
||||
m_timer = new QTimer(this);
|
||||
connect(m_timer, &QTimer::timeout, this, &log_frame::UpdateUI);
|
||||
}
|
||||
|
|
@ -225,7 +220,7 @@ void log_frame::CreateAndConnectActions()
|
|||
// I, for one, welcome our lambda overlord
|
||||
// It's either this or a signal mapper
|
||||
// Then, probably making a list of these actions so that it's easier to iterate to generate the mapper.
|
||||
auto l_initAct = [this](QAction* act, logs::level logLevel)
|
||||
const auto l_initAct = [this](QAction* act, logs::level logLevel)
|
||||
{
|
||||
act->setCheckable(true);
|
||||
|
||||
|
|
@ -298,7 +293,7 @@ void log_frame::CreateAndConnectActions()
|
|||
|
||||
if (m_ansi_tty && !m_tty_ansi_highlighter)
|
||||
{
|
||||
m_tty_ansi_highlighter = new AnsiHighlighter(m_tty->document());
|
||||
m_tty_ansi_highlighter = new AnsiHighlighter(m_tty);
|
||||
}
|
||||
else if (!m_ansi_tty && m_tty_ansi_highlighter)
|
||||
{
|
||||
|
|
@ -607,6 +602,12 @@ void log_frame::RepaintTextColors()
|
|||
html.replace(old_style, new_style);
|
||||
|
||||
m_log->document()->setHtml(html);
|
||||
|
||||
if (m_tty_ansi_highlighter)
|
||||
{
|
||||
m_tty_ansi_highlighter->update_colors(m_tty);
|
||||
m_tty_ansi_highlighter->rehighlight();
|
||||
}
|
||||
}
|
||||
|
||||
void log_frame::UpdateUI()
|
||||
|
|
|
|||
|
|
@ -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, ×tamp_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";
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -173,21 +173,45 @@ namespace gui
|
|||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
QColor get_foreground_color()
|
||||
|
||||
QColor get_foreground_color(QWidget* widget)
|
||||
{
|
||||
if (widget)
|
||||
{
|
||||
widget->ensurePolished();
|
||||
return widget->palette().color(QPalette::ColorRole::WindowText);
|
||||
}
|
||||
|
||||
QLabel dummy_color;
|
||||
dummy_color.ensurePolished();
|
||||
return dummy_color.palette().color(QPalette::ColorRole::WindowText);
|
||||
}
|
||||
|
||||
QColor get_background_color()
|
||||
QColor get_background_color(QWidget* widget)
|
||||
{
|
||||
if (widget)
|
||||
{
|
||||
widget->ensurePolished();
|
||||
return widget->palette().color(QPalette::ColorRole::Window);
|
||||
}
|
||||
|
||||
QLabel dummy_color;
|
||||
dummy_color.ensurePolished();
|
||||
return dummy_color.palette().color(QPalette::ColorRole::Window);
|
||||
}
|
||||
|
||||
QColor adjust_color_for_background(const QColor& fg, const QColor& bg)
|
||||
{
|
||||
const int diff = fg.lightness() - bg.lightness();
|
||||
|
||||
if (std::abs(diff) >= 40)
|
||||
{
|
||||
return fg;
|
||||
}
|
||||
|
||||
return (bg.lightness() < 128) ? fg.lighter(180) : fg.darker(180);
|
||||
}
|
||||
|
||||
QColor get_label_color(const QString& object_name, const QColor& fallback_light, const QColor& fallback_dark, QPalette::ColorRole color_role)
|
||||
{
|
||||
if (!gui::custom_stylesheet_active || !gui::stylesheet.contains(object_name))
|
||||
|
|
|
|||
|
|
@ -68,11 +68,14 @@ namespace gui
|
|||
// Returns a list of all base names of files in dir whose complete file names contain one of the given name_filters
|
||||
QStringList get_dir_entries(const QDir& dir, const QStringList& name_filters, bool full_path = false);
|
||||
|
||||
// Returns the foreground color of QLabel with respect to the current light/dark mode.
|
||||
QColor get_foreground_color();
|
||||
// Returns the foreground color of QLabel or the given widget with respect to the current light/dark mode.
|
||||
QColor get_foreground_color(QWidget* widget = nullptr);
|
||||
|
||||
// Returns the background color of QLabel with respect to the current light/dark mode.
|
||||
QColor get_background_color();
|
||||
// Returns the background color of QLabel or the given widget with respect to the current light/dark mode.
|
||||
QColor get_background_color(QWidget* widget = nullptr);
|
||||
|
||||
// Returns an adjusted color with better contrast, depending on the background.
|
||||
QColor adjust_color_for_background(const QColor& fg, const QColor& bg);
|
||||
|
||||
// Returns the color specified by its color_role for the QLabels with object_name
|
||||
QColor get_label_color(const QString& object_name, const QColor& fallback_light, const QColor& fallback_dark, QPalette::ColorRole color_role = QPalette::WindowText);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue