Merge branch 'shadps4-emu:main' into walker-acces-violation

This commit is contained in:
Lander Gallastegi 2025-06-09 12:00:53 +02:00 committed by GitHub
commit ffdbcb0a01
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
99 changed files with 2570 additions and 1440 deletions

View File

@ -76,18 +76,13 @@ jobs:
${{ env.cache-name }}- ${{ env.cache-name }}-
- name: Cache CMake Build - name: Cache CMake Build
uses: hendrikmuhs/ccache-action@v1.2.17 uses: hendrikmuhs/ccache-action@v1.2.18
env: env:
cache-name: ${{ runner.os }}-sdl-cache-cmake-build cache-name: ${{ runner.os }}-sdl-cache-cmake-build
with: with:
append-timestamp: false append-timestamp: false
key: ${{ env.cache-name }}-${{ hashFiles('**/CMakeLists.txt', 'cmake/**') }} key: ${{ env.cache-name }}-${{ hashFiles('**/CMakeLists.txt', 'cmake/**') }}
- name: Setup VS Environment
uses: ilammy/msvc-dev-cmd@v1.13.0
with:
arch: amd64
- name: Configure CMake - name: Configure CMake
run: cmake --fresh -G Ninja -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DCMAKE_INTERPROCEDURAL_OPTIMIZATION_RELEASE=ON -DCMAKE_C_COMPILER=clang-cl -DCMAKE_CXX_COMPILER=clang-cl -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache run: cmake --fresh -G Ninja -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DCMAKE_INTERPROCEDURAL_OPTIMIZATION_RELEASE=ON -DCMAKE_C_COMPILER=clang-cl -DCMAKE_CXX_COMPILER=clang-cl -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache
@ -111,7 +106,7 @@ jobs:
- name: Setup Qt - name: Setup Qt
uses: jurplel/install-qt-action@v4 uses: jurplel/install-qt-action@v4
with: with:
version: 6.9.0 version: 6.9.1
host: windows host: windows
target: desktop target: desktop
arch: win64_msvc2022_64 arch: win64_msvc2022_64
@ -130,18 +125,13 @@ jobs:
${{ env.cache-name }}- ${{ env.cache-name }}-
- name: Cache CMake Build - name: Cache CMake Build
uses: hendrikmuhs/ccache-action@v1.2.17 uses: hendrikmuhs/ccache-action@v1.2.18
env: env:
cache-name: ${{ runner.os }}-qt-cache-cmake-build cache-name: ${{ runner.os }}-qt-cache-cmake-build
with: with:
append-timestamp: false append-timestamp: false
key: ${{ env.cache-name }}-${{ hashFiles('**/CMakeLists.txt', 'cmake/**') }} key: ${{ env.cache-name }}-${{ hashFiles('**/CMakeLists.txt', 'cmake/**') }}
- name: Setup VS Environment
uses: ilammy/msvc-dev-cmd@v1.13.0
with:
arch: amd64
- name: Configure CMake - name: Configure CMake
run: cmake --fresh -G Ninja -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DENABLE_QT_GUI=ON -DENABLE_UPDATER=ON -DCMAKE_INTERPROCEDURAL_OPTIMIZATION_RELEASE=ON -DCMAKE_C_COMPILER=clang-cl -DCMAKE_CXX_COMPILER=clang-cl -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache run: cmake --fresh -G Ninja -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DENABLE_QT_GUI=ON -DENABLE_UPDATER=ON -DCMAKE_INTERPROCEDURAL_OPTIMIZATION_RELEASE=ON -DCMAKE_C_COMPILER=clang-cl -DCMAKE_CXX_COMPILER=clang-cl -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache
@ -186,7 +176,7 @@ jobs:
${{ env.cache-name }}- ${{ env.cache-name }}-
- name: Cache CMake Build - name: Cache CMake Build
uses: hendrikmuhs/ccache-action@v1.2.17 uses: hendrikmuhs/ccache-action@v1.2.18
env: env:
cache-name: ${{runner.os}}-sdl-cache-cmake-build cache-name: ${{runner.os}}-sdl-cache-cmake-build
with: with:
@ -228,7 +218,7 @@ jobs:
- name: Setup Qt - name: Setup Qt
uses: jurplel/install-qt-action@v4 uses: jurplel/install-qt-action@v4
with: with:
version: 6.9.0 version: 6.9.1
host: mac host: mac
target: desktop target: desktop
arch: clang_64 arch: clang_64
@ -247,7 +237,7 @@ jobs:
${{ env.cache-name }}- ${{ env.cache-name }}-
- name: Cache CMake Build - name: Cache CMake Build
uses: hendrikmuhs/ccache-action@v1.2.17 uses: hendrikmuhs/ccache-action@v1.2.18
env: env:
cache-name: ${{runner.os}}-qt-cache-cmake-build cache-name: ${{runner.os}}-qt-cache-cmake-build
with: with:
@ -301,7 +291,7 @@ jobs:
${{ env.cache-name }}- ${{ env.cache-name }}-
- name: Cache CMake Build - name: Cache CMake Build
uses: hendrikmuhs/ccache-action@v1.2.17 uses: hendrikmuhs/ccache-action@v1.2.18
env: env:
cache-name: ${{ runner.os }}-sdl-cache-cmake-build cache-name: ${{ runner.os }}-sdl-cache-cmake-build
with: with:
@ -362,7 +352,7 @@ jobs:
${{ env.cache-name }}- ${{ env.cache-name }}-
- name: Cache CMake Build - name: Cache CMake Build
uses: hendrikmuhs/ccache-action@v1.2.17 uses: hendrikmuhs/ccache-action@v1.2.18
env: env:
cache-name: ${{ runner.os }}-qt-cache-cmake-build cache-name: ${{ runner.os }}-qt-cache-cmake-build
with: with:
@ -409,7 +399,7 @@ jobs:
${{ env.cache-name }}- ${{ env.cache-name }}-
- name: Cache CMake Build - name: Cache CMake Build
uses: hendrikmuhs/ccache-action@v1.2.17 uses: hendrikmuhs/ccache-action@v1.2.18
env: env:
cache-name: ${{ runner.os }}-sdl-gcc-cache-cmake-build cache-name: ${{ runner.os }}-sdl-gcc-cache-cmake-build
with: with:
@ -445,7 +435,7 @@ jobs:
${{ env.cache-name }}- ${{ env.cache-name }}-
- name: Cache CMake Build - name: Cache CMake Build
uses: hendrikmuhs/ccache-action@v1.2.17 uses: hendrikmuhs/ccache-action@v1.2.18
env: env:
cache-name: ${{ runner.os }}-qt-gcc-cache-cmake-build cache-name: ${{ runner.os }}-qt-gcc-cache-cmake-build
with: with:
@ -494,7 +484,7 @@ jobs:
with: with:
token: ${{ secrets.SHADPS4_TOKEN_REPO }} token: ${{ secrets.SHADPS4_TOKEN_REPO }}
name: "Pre-release-shadPS4-${{ needs.get-info.outputs.date }}-${{ needs.get-info.outputs.shorthash }}" name: "Pre-release-shadPS4-${{ needs.get-info.outputs.date }}-${{ needs.get-info.outputs.shorthash }}"
tag: "Pre-release-shadPS4-${{ needs.get-info.outputs.date }}-${{ needs.get-info.outputs.shorthash }}" tag: "Pre-release-shadPS4-${{ needs.get-info.outputs.date }}-${{ needs.get-info.outputs.fullhash }}"
draft: false draft: false
prerelease: true prerelease: true
body: "Full Changelog: [${{ env.last_release_tag }}...${{ needs.get-info.outputs.shorthash }}](https://github.com/shadps4-emu/shadPS4/compare/${{ env.last_release_tag }}...${{ needs.get-info.outputs.fullhash }})" body: "Full Changelog: [${{ env.last_release_tag }}...${{ needs.get-info.outputs.shorthash }}](https://github.com/shadps4-emu/shadPS4/compare/${{ env.last_release_tag }}...${{ needs.get-info.outputs.fullhash }})"
@ -530,14 +520,14 @@ jobs:
# Check if release already exists and get ID # Check if release already exists and get ID
release_id=$(curl -s -H "Authorization: token $GITHUB_TOKEN" \ release_id=$(curl -s -H "Authorization: token $GITHUB_TOKEN" \
"https://api.github.com/repos/$REPO/releases/tags/Pre-release-shadPS4-${{ needs.get-info.outputs.date }}-${{ needs.get-info.outputs.shorthash }}" | jq -r '.id') "https://api.github.com/repos/$REPO/releases/tags/Pre-release-shadPS4-${{ needs.get-info.outputs.date }}-${{ needs.get-info.outputs.fullhash }}" | jq -r '.id')
if [[ "$release_id" == "null" ]]; then if [[ "$release_id" == "null" ]]; then
echo "Creating release in $REPO for $filename" echo "Creating release in $REPO for $filename"
release_id=$(curl -s -X POST -H "Authorization: token $GITHUB_TOKEN" \ release_id=$(curl -s -X POST -H "Authorization: token $GITHUB_TOKEN" \
-H "Accept: application/vnd.github.v3+json" \ -H "Accept: application/vnd.github.v3+json" \
-d '{ -d '{
"tag_name": "Pre-release-shadPS4-${{ needs.get-info.outputs.date }}-${{ needs.get-info.outputs.shorthash }}", "tag_name": "Pre-release-shadPS4-${{ needs.get-info.outputs.date }}-${{ needs.get-info.outputs.fullhash }}",
"name": "Pre-release-shadPS4-${{ needs.get-info.outputs.date }}-${{ needs.get-info.outputs.shorthash }}", "name": "Pre-release-shadPS4-${{ needs.get-info.outputs.date }}-${{ needs.get-info.outputs.shorthash }}",
"draft": false, "draft": false,
"prerelease": true, "prerelease": true,

View File

@ -296,6 +296,8 @@ set(AJM_LIB src/core/libraries/ajm/ajm.cpp
set(AUDIO_LIB src/core/libraries/audio/audioin.cpp set(AUDIO_LIB src/core/libraries/audio/audioin.cpp
src/core/libraries/audio/audioin.h src/core/libraries/audio/audioin.h
src/core/libraries/voice/voice.cpp
src/core/libraries/voice/voice.h
src/core/libraries/audio/audioout.cpp src/core/libraries/audio/audioout.cpp
src/core/libraries/audio/audioout.h src/core/libraries/audio/audioout.h
src/core/libraries/audio/audioout_backend.h src/core/libraries/audio/audioout_backend.h
@ -603,6 +605,8 @@ set(CAMERA_LIBS src/core/libraries/camera/camera.cpp
set(COMPANION_LIBS src/core/libraries/companion/companion_httpd.cpp set(COMPANION_LIBS src/core/libraries/companion/companion_httpd.cpp
src/core/libraries/companion/companion_httpd.h src/core/libraries/companion/companion_httpd.h
src/core/libraries/companion/companion_util.cpp
src/core/libraries/companion/companion_util.h
src/core/libraries/companion/companion_error.h src/core/libraries/companion/companion_error.h
) )
set(DEV_TOOLS src/core/devtools/layer.cpp set(DEV_TOOLS src/core/devtools/layer.cpp
@ -960,6 +964,7 @@ set(VIDEO_CORE src/video_core/amdgpu/liverpool.cpp
src/video_core/texture_cache/tile_manager.cpp src/video_core/texture_cache/tile_manager.cpp
src/video_core/texture_cache/tile_manager.h src/video_core/texture_cache/tile_manager.h
src/video_core/texture_cache/types.h src/video_core/texture_cache/types.h
src/video_core/texture_cache/host_compatibility.cpp
src/video_core/texture_cache/host_compatibility.h src/video_core/texture_cache/host_compatibility.h
src/video_core/page_manager.cpp src/video_core/page_manager.cpp
src/video_core/page_manager.h src/video_core/page_manager.h

View File

@ -29,7 +29,7 @@ sudo dnf install clang git cmake libatomic alsa-lib-devel \
openssl-devel libevdev-devel libudev-devel libXext-devel \ openssl-devel libevdev-devel libudev-devel libXext-devel \
qt6-qtbase-devel qt6-qtbase-private-devel \ qt6-qtbase-devel qt6-qtbase-private-devel \
qt6-qtmultimedia-devel qt6-qtsvg-devel qt6-qttools-devel \ qt6-qtmultimedia-devel qt6-qtsvg-devel qt6-qttools-devel \
vulkan-devel vulkan-validation-layers libpng-devel vulkan-devel vulkan-validation-layers libpng-devel libuuid-devel
``` ```
#### Arch Linux #### Arch Linux

View File

@ -69,7 +69,7 @@ static bool vkGuestMarkers = false;
static bool rdocEnable = false; static bool rdocEnable = false;
static bool isFpsColor = true; static bool isFpsColor = true;
static bool isSeparateLogFilesEnabled = false; static bool isSeparateLogFilesEnabled = false;
static s16 cursorState = HideCursorState::Idle; static int cursorState = HideCursorState::Idle;
static int cursorHideTimeout = 5; // 5 seconds (default) static int cursorHideTimeout = 5; // 5 seconds (default)
static double trophyNotificationDuration = 6.0; static double trophyNotificationDuration = 6.0;
static bool useUnifiedInputConfig = true; static bool useUnifiedInputConfig = true;
@ -78,6 +78,7 @@ static int controllerCustomColorRGB[3] = {0, 0, 255};
static bool compatibilityData = false; static bool compatibilityData = false;
static bool checkCompatibilityOnStartup = false; static bool checkCompatibilityOnStartup = false;
static std::string trophyKey; static std::string trophyKey;
static bool isPSNSignedIn = false;
// Gui // Gui
static bool load_game_size = true; static bool load_game_size = true;
@ -730,6 +731,14 @@ void setShowBackgroundImage(bool show) {
showBackgroundImage = show; showBackgroundImage = show;
} }
bool getPSNSignedIn() {
return isPSNSignedIn;
}
void setPSNSignedIn(bool sign) {
isPSNSignedIn = sign;
}
void load(const std::filesystem::path& path) { void load(const std::filesystem::path& path) {
// If the configuration file does not exist, create it and return // If the configuration file does not exist, create it and return
std::error_code error; std::error_code error;
@ -754,6 +763,7 @@ void load(const std::filesystem::path& path) {
isNeo = toml::find_or<bool>(general, "isPS4Pro", false); isNeo = toml::find_or<bool>(general, "isPS4Pro", false);
isDevKit = toml::find_or<bool>(general, "isDevKit", false); isDevKit = toml::find_or<bool>(general, "isDevKit", false);
isPSNSignedIn = toml::find_or<bool>(general, "isPSNSignedIn", false);
playBGM = toml::find_or<bool>(general, "playBGM", false); playBGM = toml::find_or<bool>(general, "playBGM", false);
isTrophyPopupDisabled = toml::find_or<bool>(general, "isTrophyPopupDisabled", false); isTrophyPopupDisabled = toml::find_or<bool>(general, "isTrophyPopupDisabled", false);
trophyNotificationDuration = trophyNotificationDuration =
@ -953,6 +963,7 @@ void save(const std::filesystem::path& path) {
data["General"]["isPS4Pro"] = isNeo; data["General"]["isPS4Pro"] = isNeo;
data["General"]["isDevKit"] = isDevKit; data["General"]["isDevKit"] = isDevKit;
data["General"]["isPSNSignedIn"] = isPSNSignedIn;
data["General"]["isTrophyPopupDisabled"] = isTrophyPopupDisabled; data["General"]["isTrophyPopupDisabled"] = isTrophyPopupDisabled;
data["General"]["trophyNotificationDuration"] = trophyNotificationDuration; data["General"]["trophyNotificationDuration"] = trophyNotificationDuration;
data["General"]["playBGM"] = playBGM; data["General"]["playBGM"] = playBGM;
@ -1098,6 +1109,7 @@ void setDefaultValues() {
isHDRAllowed = false; isHDRAllowed = false;
isNeo = false; isNeo = false;
isDevKit = false; isDevKit = false;
isPSNSignedIn = false;
isFullscreen = false; isFullscreen = false;
isTrophyPopupDisabled = false; isTrophyPopupDisabled = false;
playBGM = false; playBGM = false;

View File

@ -14,7 +14,7 @@ struct GameInstallDir {
bool enabled; bool enabled;
}; };
enum HideCursorState : s16 { Never, Idle, Always }; enum HideCursorState : int { Never, Idle, Always };
void load(const std::filesystem::path& path); void load(const std::filesystem::path& path);
void save(const std::filesystem::path& path); void save(const std::filesystem::path& path);
@ -39,6 +39,7 @@ bool getCompatibilityEnabled();
bool getCheckCompatibilityOnStartup(); bool getCheckCompatibilityOnStartup();
int getBackgroundImageOpacity(); int getBackgroundImageOpacity();
bool getShowBackgroundImage(); bool getShowBackgroundImage();
bool getPSNSignedIn();
std::string getLogFilter(); std::string getLogFilter();
std::string getLogType(); std::string getLogType();
@ -111,6 +112,7 @@ void setCompatibilityEnabled(bool use);
void setCheckCompatibilityOnStartup(bool use); void setCheckCompatibilityOnStartup(bool use);
void setBackgroundImageOpacity(int opacity); void setBackgroundImageOpacity(int opacity);
void setShowBackgroundImage(bool show); void setShowBackgroundImage(bool show);
void setPSNSignedIn(bool sign);
void setCursorState(s16 cursorState); void setCursorState(s16 cursorState);
void setCursorHideTimeout(int newcursorHideTimeout); void setCursorHideTimeout(int newcursorHideTimeout);

View File

@ -140,6 +140,8 @@ bool ParseFilterRule(Filter& instance, Iterator begin, Iterator end) {
SUB(Lib, SigninDialog) \ SUB(Lib, SigninDialog) \
SUB(Lib, Camera) \ SUB(Lib, Camera) \
SUB(Lib, CompanionHttpd) \ SUB(Lib, CompanionHttpd) \
SUB(Lib, CompanionUtil) \
SUB(Lib, Voice) \
CLS(Frontend) \ CLS(Frontend) \
CLS(Render) \ CLS(Render) \
SUB(Render, Vulkan) \ SUB(Render, Vulkan) \

View File

@ -98,6 +98,7 @@ enum class Class : u8 {
Lib_Fiber, ///< The LibSceFiber implementation. Lib_Fiber, ///< The LibSceFiber implementation.
Lib_Vdec2, ///< The LibSceVideodec2 implementation. Lib_Vdec2, ///< The LibSceVideodec2 implementation.
Lib_Videodec, ///< The LibSceVideodec implementation. Lib_Videodec, ///< The LibSceVideodec implementation.
Lib_Voice, ///< The LibSceVoice implementation.
Lib_RazorCpu, ///< The LibRazorCpu implementation. Lib_RazorCpu, ///< The LibRazorCpu implementation.
Lib_Mouse, ///< The LibSceMouse implementation Lib_Mouse, ///< The LibSceMouse implementation
Lib_WebBrowserDialog, ///< The LibSceWebBrowserDialog implementation Lib_WebBrowserDialog, ///< The LibSceWebBrowserDialog implementation
@ -107,6 +108,7 @@ enum class Class : u8 {
Lib_SigninDialog, ///< The LibSigninDialog implementation. Lib_SigninDialog, ///< The LibSigninDialog implementation.
Lib_Camera, ///< The LibCamera implementation. Lib_Camera, ///< The LibCamera implementation.
Lib_CompanionHttpd, ///< The LibCompanionHttpd implementation. Lib_CompanionHttpd, ///< The LibCompanionHttpd implementation.
Lib_CompanionUtil, ///< The LibCompanionUtil implementation.
Frontend, ///< Emulator UI Frontend, ///< Emulator UI
Render, ///< Video Core Render, ///< Video Core
Render_Vulkan, ///< Vulkan backend Render_Vulkan, ///< Vulkan backend

View File

@ -1,6 +1,8 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project // SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
#include <string>
#include "common/scm_rev.h" #include "common/scm_rev.h"
namespace Common { namespace Common {
@ -15,5 +17,26 @@ constexpr char g_scm_remote_name[] = "@GIT_REMOTE_NAME@";
constexpr char g_scm_remote_url[] = "@GIT_REMOTE_URL@"; constexpr char g_scm_remote_url[] = "@GIT_REMOTE_URL@";
constexpr char g_scm_date[] = "@BUILD_DATE@"; constexpr char g_scm_date[] = "@BUILD_DATE@";
const std::string GetRemoteNameFromLink() {
std::string remote_url(Common::g_scm_remote_url);
std::string remote_host;
try {
if (remote_url.starts_with("http")) {
if (*remote_url.rbegin() == '/') {
remote_url.pop_back();
}
remote_host = remote_url.substr(19, remote_url.rfind('/') - 19);
} else if (remote_url.starts_with("git@")) {
auto after_comma_pos = remote_url.find(':') + 1, slash_pos = remote_url.find('/');
remote_host = remote_url.substr(after_comma_pos, slash_pos - after_comma_pos);
} else {
remote_host = "unknown";
}
} catch (...) {
remote_host = "unknown";
}
return remote_host;
}
} // namespace } // namespace

View File

@ -3,6 +3,8 @@
#pragma once #pragma once
#include <string>
namespace Common { namespace Common {
extern const char g_version[]; extern const char g_version[];
@ -15,4 +17,6 @@ extern const char g_scm_remote_name[];
extern const char g_scm_remote_url[]; extern const char g_scm_remote_url[];
extern const char g_scm_date[]; extern const char g_scm_date[];
const std::string GetRemoteNameFromLink();
} // namespace Common } // namespace Common

View File

@ -88,7 +88,8 @@ static bool FilterTcbAccess(const ZydisDecodedOperand* operands) {
dst_op.reg.value <= ZYDIS_REGISTER_R15; dst_op.reg.value <= ZYDIS_REGISTER_R15;
} }
static void GenerateTcbAccess(const ZydisDecodedOperand* operands, Xbyak::CodeGenerator& c) { static void GenerateTcbAccess(void* /* address */, const ZydisDecodedOperand* operands,
Xbyak::CodeGenerator& c) {
const auto dst = ZydisToXbyakRegisterOperand(operands[0]); const auto dst = ZydisToXbyakRegisterOperand(operands[0]);
#if defined(_WIN32) #if defined(_WIN32)
@ -126,7 +127,8 @@ static bool FilterNoSSE4a(const ZydisDecodedOperand*) {
return !cpu.has(Cpu::tSSE4a); return !cpu.has(Cpu::tSSE4a);
} }
static void GenerateEXTRQ(const ZydisDecodedOperand* operands, Xbyak::CodeGenerator& c) { static void GenerateEXTRQ(void* /* address */, const ZydisDecodedOperand* operands,
Xbyak::CodeGenerator& c) {
bool immediateForm = operands[1].type == ZYDIS_OPERAND_TYPE_IMMEDIATE && bool immediateForm = operands[1].type == ZYDIS_OPERAND_TYPE_IMMEDIATE &&
operands[2].type == ZYDIS_OPERAND_TYPE_IMMEDIATE; operands[2].type == ZYDIS_OPERAND_TYPE_IMMEDIATE;
@ -245,7 +247,8 @@ static void GenerateEXTRQ(const ZydisDecodedOperand* operands, Xbyak::CodeGenera
} }
} }
static void GenerateINSERTQ(const ZydisDecodedOperand* operands, Xbyak::CodeGenerator& c) { static void GenerateINSERTQ(void* /* address */, const ZydisDecodedOperand* operands,
Xbyak::CodeGenerator& c) {
bool immediateForm = operands[2].type == ZYDIS_OPERAND_TYPE_IMMEDIATE && bool immediateForm = operands[2].type == ZYDIS_OPERAND_TYPE_IMMEDIATE &&
operands[3].type == ZYDIS_OPERAND_TYPE_IMMEDIATE; operands[3].type == ZYDIS_OPERAND_TYPE_IMMEDIATE;
@ -383,8 +386,44 @@ static void GenerateINSERTQ(const ZydisDecodedOperand* operands, Xbyak::CodeGene
} }
} }
static void ReplaceMOVNT(void* address, u8 rep_prefix) {
// Find the opcode byte
// There can be any amount of prefixes but the instruction can't be more than 15 bytes
// And we know for sure this is a MOVNTSS/MOVNTSD
bool found = false;
bool rep_prefix_found = false;
int index = 0;
u8* ptr = reinterpret_cast<u8*>(address);
for (int i = 0; i < 15; i++) {
if (ptr[i] == rep_prefix) {
rep_prefix_found = true;
} else if (ptr[i] == 0x2B) {
index = i;
found = true;
break;
}
}
// Some sanity checks
ASSERT(found);
ASSERT(index >= 2);
ASSERT(ptr[index - 1] == 0x0F);
ASSERT(rep_prefix_found);
// This turns the MOVNTSS/MOVNTSD to a MOVSS/MOVSD m, xmm
ptr[index] = 0x11;
}
static void ReplaceMOVNTSS(void* address, const ZydisDecodedOperand*, Xbyak::CodeGenerator&) {
ReplaceMOVNT(address, 0xF3);
}
static void ReplaceMOVNTSD(void* address, const ZydisDecodedOperand*, Xbyak::CodeGenerator&) {
ReplaceMOVNT(address, 0xF2);
}
using PatchFilter = bool (*)(const ZydisDecodedOperand*); using PatchFilter = bool (*)(const ZydisDecodedOperand*);
using InstructionGenerator = void (*)(const ZydisDecodedOperand*, Xbyak::CodeGenerator&); using InstructionGenerator = void (*)(void*, const ZydisDecodedOperand*, Xbyak::CodeGenerator&);
struct PatchInfo { struct PatchInfo {
/// Filter for more granular patch conditions past just the instruction mnemonic. /// Filter for more granular patch conditions past just the instruction mnemonic.
PatchFilter filter; PatchFilter filter;
@ -400,6 +439,8 @@ static const std::unordered_map<ZydisMnemonic, PatchInfo> Patches = {
// SSE4a // SSE4a
{ZYDIS_MNEMONIC_EXTRQ, {FilterNoSSE4a, GenerateEXTRQ, true}}, {ZYDIS_MNEMONIC_EXTRQ, {FilterNoSSE4a, GenerateEXTRQ, true}},
{ZYDIS_MNEMONIC_INSERTQ, {FilterNoSSE4a, GenerateINSERTQ, true}}, {ZYDIS_MNEMONIC_INSERTQ, {FilterNoSSE4a, GenerateINSERTQ, true}},
{ZYDIS_MNEMONIC_MOVNTSS, {FilterNoSSE4a, ReplaceMOVNTSS, false}},
{ZYDIS_MNEMONIC_MOVNTSD, {FilterNoSSE4a, ReplaceMOVNTSD, false}},
#if defined(_WIN32) #if defined(_WIN32)
// Windows needs a trampoline. // Windows needs a trampoline.
@ -477,7 +518,7 @@ static std::pair<bool, u64> TryPatch(u8* code, PatchModule* module) {
auto& trampoline_gen = module->trampoline_gen; auto& trampoline_gen = module->trampoline_gen;
const auto trampoline_ptr = trampoline_gen.getCurr(); const auto trampoline_ptr = trampoline_gen.getCurr();
patch_info.generator(operands, trampoline_gen); patch_info.generator(code, operands, trampoline_gen);
// Return to the following instruction at the end of the trampoline. // Return to the following instruction at the end of the trampoline.
trampoline_gen.jmp(code + instruction.length); trampoline_gen.jmp(code + instruction.length);
@ -485,7 +526,7 @@ static std::pair<bool, u64> TryPatch(u8* code, PatchModule* module) {
// Replace instruction with near jump to the trampoline. // Replace instruction with near jump to the trampoline.
patch_gen.jmp(trampoline_ptr, Xbyak::CodeGenerator::LabelType::T_NEAR); patch_gen.jmp(trampoline_ptr, Xbyak::CodeGenerator::LabelType::T_NEAR);
} else { } else {
patch_info.generator(operands, patch_gen); patch_info.generator(code, operands, patch_gen);
} }
const auto patch_size = patch_gen.getCurr() - code; const auto patch_size = patch_gen.getCurr() - code;

View File

@ -3,6 +3,8 @@
#pragma once #pragma once
#include "common/types.h"
// companion_httpd error codes // companion_httpd error codes
constexpr int ORBIS_COMPANION_HTTPD_ERROR_UNKNOWN = 0x80E40001; constexpr int ORBIS_COMPANION_HTTPD_ERROR_UNKNOWN = 0x80E40001;
constexpr int ORBIS_COMPANION_HTTPD_ERROR_FATAL = 0x80E40002; constexpr int ORBIS_COMPANION_HTTPD_ERROR_FATAL = 0x80E40002;
@ -18,3 +20,8 @@ constexpr int ORBIS_COMPANION_HTTPD_ERROR_NOT_STARTED = 0x80E4000B;
constexpr int ORBIS_COMPANION_HTTPD_ERROR_ALREADY_REGISTERED = 0x80E4000; constexpr int ORBIS_COMPANION_HTTPD_ERROR_ALREADY_REGISTERED = 0x80E4000;
constexpr int ORBIS_COMPANION_HTTPD_ERROR_NOT_CONNECTED = 0x80E4000D; constexpr int ORBIS_COMPANION_HTTPD_ERROR_NOT_CONNECTED = 0x80E4000D;
constexpr int ORBIS_COMPANION_HTTPD_ERROR_USER_NOT_FOUND = 0x80E4000E; constexpr int ORBIS_COMPANION_HTTPD_ERROR_USER_NOT_FOUND = 0x80E4000E;
// companion_util error codes
constexpr u32 ORBIS_COMPANION_UTIL_INVALID_ARGUMENT = 0x80AD0004;
constexpr u32 ORBIS_COMPANION_UTIL_INVALID_POINTER = 0x80AD0006;
constexpr u32 ORBIS_COMPANION_UTIL_NO_EVENT = 0x80AD0008;

View File

@ -0,0 +1,72 @@
// SPDX-FileCopyrightText: Copyright 2025 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "common/logging/log.h"
#include "companion_error.h"
#include "core/libraries/companion/companion_util.h"
#include "core/libraries/error_codes.h"
#include "core/libraries/libs.h"
namespace Libraries::CompanionUtil {
u32 PS4_SYSV_ABI getEvent(sceCompanionUtilContext* ctx, sceCompanionUtilEvent* outEvent,
s32 param_3) {
if (outEvent == 0) {
return ORBIS_COMPANION_UTIL_INVALID_ARGUMENT;
}
if (ctx == nullptr) {
return ORBIS_COMPANION_UTIL_INVALID_POINTER;
}
uint8_t* base = ctx->blob;
int flag = *reinterpret_cast<int*>(base + 0x178);
if (flag == 0) {
return ORBIS_COMPANION_UTIL_NO_EVENT;
}
return ORBIS_COMPANION_UTIL_OK;
}
s32 PS4_SYSV_ABI sceCompanionUtilGetEvent(sceCompanionUtilEvent* outEvent) {
sceCompanionUtilContext* ctx = nullptr;
u32 ret = getEvent(ctx, outEvent, 1);
LOG_DEBUG(Lib_CompanionUtil, "(STUBBED) called ret: {}", ret);
return ret;
}
s32 PS4_SYSV_ABI sceCompanionUtilGetRemoteOskEvent() {
LOG_ERROR(Lib_CompanionUtil, "(STUBBED) called");
return ORBIS_OK;
}
s32 PS4_SYSV_ABI sceCompanionUtilInitialize() {
LOG_ERROR(Lib_CompanionUtil, "(STUBBED) called");
return ORBIS_OK;
}
s32 PS4_SYSV_ABI sceCompanionUtilOptParamInitialize() {
LOG_ERROR(Lib_CompanionUtil, "(STUBBED) called");
return ORBIS_OK;
}
s32 PS4_SYSV_ABI sceCompanionUtilTerminate() {
LOG_ERROR(Lib_CompanionUtil, "(STUBBED) called");
return ORBIS_OK;
}
void RegisterlibSceCompanionUtil(Core::Loader::SymbolsResolver* sym) {
LIB_FUNCTION("cE5Msy11WhU", "libSceCompanionUtil", 1, "libSceCompanionUtil", 1, 1,
sceCompanionUtilGetEvent);
LIB_FUNCTION("MaVrz79mT5o", "libSceCompanionUtil", 1, "libSceCompanionUtil", 1, 1,
sceCompanionUtilGetRemoteOskEvent);
LIB_FUNCTION("xb1xlIhf0QY", "libSceCompanionUtil", 1, "libSceCompanionUtil", 1, 1,
sceCompanionUtilInitialize);
LIB_FUNCTION("IPN-FRSrafk", "libSceCompanionUtil", 1, "libSceCompanionUtil", 1, 1,
sceCompanionUtilOptParamInitialize);
LIB_FUNCTION("H1fYQd5lFAI", "libSceCompanionUtil", 1, "libSceCompanionUtil", 1, 1,
sceCompanionUtilTerminate);
};
} // namespace Libraries::CompanionUtil

View File

@ -0,0 +1,33 @@
// SPDX-FileCopyrightText: Copyright 2025 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "common/types.h"
namespace Core::Loader {
class SymbolsResolver;
}
namespace Libraries::CompanionUtil {
constexpr u32 ORBIS_COMPANION_UTIL_OK = 0;
struct sceCompanionUtilEvent {
std::uint8_t blob[0x104]{}; /// 0x104 bytes of data, dont know what it is exactly
};
struct sceCompanionUtilContext {
std::uint8_t blob[0x27B]{}; /// 0x27B bytes of data, dont know what it is exactly
};
u32 PS4_SYSV_ABI getEvent(sceCompanionUtilContext* ctx, sceCompanionUtilEvent* outEvent,
s32 param_3);
s32 PS4_SYSV_ABI sceCompanionUtilGetEvent(sceCompanionUtilEvent* outEvent);
s32 PS4_SYSV_ABI sceCompanionUtilGetRemoteOskEvent();
s32 PS4_SYSV_ABI sceCompanionUtilInitialize();
s32 PS4_SYSV_ABI sceCompanionUtilOptParamInitialize();
s32 PS4_SYSV_ABI sceCompanionUtilTerminate();
void RegisterlibSceCompanionUtil(Core::Loader::SymbolsResolver* sym);
} // namespace Libraries::CompanionUtil

View File

@ -98,6 +98,11 @@ bool EqueueInternal::RemoveEvent(u64 id, s16 filter) {
} }
int EqueueInternal::WaitForEvents(SceKernelEvent* ev, int num, u32 micros) { int EqueueInternal::WaitForEvents(SceKernelEvent* ev, int num, u32 micros) {
if (HasSmallTimer()) {
// If a small timer is set, just wait for it to expire.
return WaitForSmallTimer(ev, num, micros);
}
int count = 0; int count = 0;
const auto predicate = [&] { const auto predicate = [&] {
@ -140,6 +145,8 @@ bool EqueueInternal::TriggerEvent(u64 ident, s16 filter, void* trigger_data) {
if (event.event.ident == ident && event.event.filter == filter) { if (event.event.ident == ident && event.event.filter == filter) {
if (filter == SceKernelEvent::Filter::VideoOut) { if (filter == SceKernelEvent::Filter::VideoOut) {
event.TriggerDisplay(trigger_data); event.TriggerDisplay(trigger_data);
} else if (filter == SceKernelEvent::Filter::User) {
event.TriggerUser(trigger_data);
} else { } else {
event.Trigger(trigger_data); event.Trigger(trigger_data);
} }
@ -267,27 +274,15 @@ int PS4_SYSV_ABI sceKernelWaitEqueue(SceKernelEqueue eq, SceKernelEvent* ev, int
return ORBIS_KERNEL_ERROR_EINVAL; return ORBIS_KERNEL_ERROR_EINVAL;
} }
// When the timeout is nullptr, we wait indefinitely if (timo == nullptr) {
if (eq->HasSmallTimer()) { // When the timeout is nullptr, we wait indefinitely
if (timo == nullptr) { *out = eq->WaitForEvents(ev, num, 0);
*out = eq->WaitForSmallTimer(ev, num, 0); } else if (*timo == 0) {
} else if (*timo == 0) { // Only events that have already arrived at the time of this function call can be received
// Only events that have already arrived at the time of this function call can be *out = eq->GetTriggeredEvents(ev, num);
// received
*out = eq->GetTriggeredEvents(ev, num);
} else {
*out = eq->WaitForSmallTimer(ev, num, *timo);
}
} else { } else {
if (timo == nullptr) { // Wait for up to the specified timeout value
*out = eq->WaitForEvents(ev, num, 0); *out = eq->WaitForEvents(ev, num, *timo);
} else if (*timo == 0) {
// Only events that have already arrived at the time of this function call can be
// received
*out = eq->GetTriggeredEvents(ev, num);
} else {
*out = eq->WaitForEvents(ev, num, *timo);
}
} }
if (*out == 0) { if (*out == 0) {

View File

@ -98,6 +98,12 @@ struct EqueueEvent {
event.data = reinterpret_cast<uintptr_t>(data); event.data = reinterpret_cast<uintptr_t>(data);
} }
void TriggerUser(void* data) {
is_triggered = true;
event.fflags++;
event.udata = data;
}
void TriggerDisplay(void* data) { void TriggerDisplay(void* data) {
is_triggered = true; is_triggered = true;
if (data != nullptr) { if (data != nullptr) {

View File

@ -273,6 +273,10 @@ void RegisterKernel(Core::Loader::SymbolsResolver* sym) {
Libraries::Net::sceNetInetNtop); // TODO fix it to sys_ ... Libraries::Net::sceNetInetNtop); // TODO fix it to sys_ ...
LIB_FUNCTION("4n51s0zEf0c", "libScePosix", 1, "libkernel", 1, 1, LIB_FUNCTION("4n51s0zEf0c", "libScePosix", 1, "libkernel", 1, 1,
Libraries::Net::sceNetInetPton); // TODO fix it to sys_ ... Libraries::Net::sceNetInetPton); // TODO fix it to sys_ ...
LIB_FUNCTION("XVL8So3QJUk", "libScePosix", 1, "libkernel", 1, 1, Libraries::Net::sys_connect);
LIB_FUNCTION("3e+4Iv7IJ8U", "libScePosix", 1, "libkernel", 1, 1, Libraries::Net::sys_accept);
LIB_FUNCTION("aNeavPDNKzA", "libScePosix", 1, "libkernel", 1, 1, Libraries::Net::sys_sendmsg);
LIB_FUNCTION("pxnCmagrtao", "libScePosix", 1, "libkernel", 1, 1, Libraries::Net::sys_listen);
} }
} // namespace Libraries::Kernel } // namespace Libraries::Kernel

View File

@ -8,7 +8,6 @@
#include "common/logging/log.h" #include "common/logging/log.h"
#include "common/scope_exit.h" #include "common/scope_exit.h"
#include "common/singleton.h" #include "common/singleton.h"
#include "core/file_sys/fs.h"
#include "core/libraries/kernel/kernel.h" #include "core/libraries/kernel/kernel.h"
#include "core/libraries/kernel/memory.h" #include "core/libraries/kernel/memory.h"
#include "core/libraries/kernel/orbis_error.h" #include "core/libraries/kernel/orbis_error.h"
@ -152,7 +151,8 @@ s32 PS4_SYSV_ABI sceKernelReserveVirtualRange(void** addr, u64 len, int flags, u
const VAddr in_addr = reinterpret_cast<VAddr>(*addr); const VAddr in_addr = reinterpret_cast<VAddr>(*addr);
const auto map_flags = static_cast<Core::MemoryMapFlags>(flags); const auto map_flags = static_cast<Core::MemoryMapFlags>(flags);
s32 result = memory->Reserve(addr, in_addr, len, map_flags, alignment); s32 result = memory->MapMemory(addr, in_addr, len, Core::MemoryProt::NoAccess, map_flags,
Core::VMAType::Reserved, "anon", false, -1, alignment);
if (result == 0) { if (result == 0) {
LOG_INFO(Kernel_Vmm, "out_addr = {}", fmt::ptr(*addr)); LOG_INFO(Kernel_Vmm, "out_addr = {}", fmt::ptr(*addr));
} }
@ -222,9 +222,10 @@ s32 PS4_SYSV_ABI sceKernelMapDirectMemory2(void** addr, u64 len, s32 type, s32 p
return ret; return ret;
} }
s32 PS4_SYSV_ABI sceKernelMapNamedFlexibleMemory(void** addr_in_out, std::size_t len, int prot, s32 PS4_SYSV_ABI sceKernelMapNamedFlexibleMemory(void** addr_in_out, u64 len, s32 prot, s32 flags,
int flags, const char* name) { const char* name) {
LOG_INFO(Kernel_Vmm, "in_addr = {}, len = {:#x}, prot = {:#x}, flags = {:#x}, name = '{}'",
fmt::ptr(*addr_in_out), len, prot, flags, name);
if (len == 0 || !Common::Is16KBAligned(len)) { if (len == 0 || !Common::Is16KBAligned(len)) {
LOG_ERROR(Kernel_Vmm, "len is 0 or not 16kb multiple"); LOG_ERROR(Kernel_Vmm, "len is 0 or not 16kb multiple");
return ORBIS_KERNEL_ERROR_EINVAL; return ORBIS_KERNEL_ERROR_EINVAL;
@ -243,18 +244,14 @@ s32 PS4_SYSV_ABI sceKernelMapNamedFlexibleMemory(void** addr_in_out, std::size_t
const VAddr in_addr = reinterpret_cast<VAddr>(*addr_in_out); const VAddr in_addr = reinterpret_cast<VAddr>(*addr_in_out);
const auto mem_prot = static_cast<Core::MemoryProt>(prot); const auto mem_prot = static_cast<Core::MemoryProt>(prot);
const auto map_flags = static_cast<Core::MemoryMapFlags>(flags); const auto map_flags = static_cast<Core::MemoryMapFlags>(flags);
SCOPE_EXIT {
LOG_INFO(Kernel_Vmm,
"in_addr = {:#x}, out_addr = {}, len = {:#x}, prot = {:#x}, flags = {:#x}",
in_addr, fmt::ptr(*addr_in_out), len, prot, flags);
};
auto* memory = Core::Memory::Instance(); auto* memory = Core::Memory::Instance();
return memory->MapMemory(addr_in_out, in_addr, len, mem_prot, map_flags, const auto ret = memory->MapMemory(addr_in_out, in_addr, len, mem_prot, map_flags,
Core::VMAType::Flexible, name); Core::VMAType::Flexible, name);
LOG_INFO(Kernel_Vmm, "out_addr = {}", fmt::ptr(*addr_in_out));
return ret;
} }
s32 PS4_SYSV_ABI sceKernelMapFlexibleMemory(void** addr_in_out, std::size_t len, int prot, s32 PS4_SYSV_ABI sceKernelMapFlexibleMemory(void** addr_in_out, u64 len, s32 prot, s32 flags) {
int flags) {
return sceKernelMapNamedFlexibleMemory(addr_in_out, len, prot, flags, "anon"); return sceKernelMapNamedFlexibleMemory(addr_in_out, len, prot, flags, "anon");
} }
@ -263,13 +260,26 @@ int PS4_SYSV_ABI sceKernelQueryMemoryProtection(void* addr, void** start, void**
return memory->QueryProtection(std::bit_cast<VAddr>(addr), start, end, prot); return memory->QueryProtection(std::bit_cast<VAddr>(addr), start, end, prot);
} }
int PS4_SYSV_ABI sceKernelMProtect(const void* addr, size_t size, int prot) { s32 PS4_SYSV_ABI sceKernelMprotect(const void* addr, u64 size, s32 prot) {
LOG_INFO(Kernel_Vmm, "called addr = {}, size = {:#x}, prot = {:#x}", fmt::ptr(addr), size,
prot);
Core::MemoryManager* memory_manager = Core::Memory::Instance(); Core::MemoryManager* memory_manager = Core::Memory::Instance();
Core::MemoryProt protection_flags = static_cast<Core::MemoryProt>(prot); Core::MemoryProt protection_flags = static_cast<Core::MemoryProt>(prot);
return memory_manager->Protect(std::bit_cast<VAddr>(addr), size, protection_flags); return memory_manager->Protect(std::bit_cast<VAddr>(addr), size, protection_flags);
} }
int PS4_SYSV_ABI sceKernelMTypeProtect(const void* addr, size_t size, int mtype, int prot) { s32 PS4_SYSV_ABI posix_mprotect(const void* addr, u64 size, s32 prot) {
s32 result = sceKernelMprotect(addr, size, prot);
if (result < 0) {
ErrSceToPosix(result);
return -1;
}
return result;
}
s32 PS4_SYSV_ABI sceKernelMtypeprotect(const void* addr, u64 size, s32 mtype, s32 prot) {
LOG_INFO(Kernel_Vmm, "called addr = {}, size = {:#x}, prot = {:#x}", fmt::ptr(addr), size,
prot);
Core::MemoryManager* memory_manager = Core::Memory::Instance(); Core::MemoryManager* memory_manager = Core::Memory::Instance();
Core::MemoryProt protection_flags = static_cast<Core::MemoryProt>(prot); Core::MemoryProt protection_flags = static_cast<Core::MemoryProt>(prot);
return memory_manager->Protect(std::bit_cast<VAddr>(addr), size, protection_flags); return memory_manager->Protect(std::bit_cast<VAddr>(addr), size, protection_flags);
@ -344,7 +354,7 @@ s32 PS4_SYSV_ABI sceKernelBatchMap2(OrbisKernelBatchMapEntry* entries, int numEn
break; break;
} }
case MemoryOpTypes::ORBIS_KERNEL_MAP_OP_PROTECT: { case MemoryOpTypes::ORBIS_KERNEL_MAP_OP_PROTECT: {
result = sceKernelMProtect(entries[i].start, entries[i].length, entries[i].protection); result = sceKernelMprotect(entries[i].start, entries[i].length, entries[i].protection);
LOG_INFO(Kernel_Vmm, "entry = {}, operation = {}, len = {:#x}, result = {}", i, LOG_INFO(Kernel_Vmm, "entry = {}, operation = {}, len = {:#x}, result = {}", i,
entries[i].operation, entries[i].length, result); entries[i].operation, entries[i].length, result);
break; break;
@ -359,7 +369,7 @@ s32 PS4_SYSV_ABI sceKernelBatchMap2(OrbisKernelBatchMapEntry* entries, int numEn
break; break;
} }
case MemoryOpTypes::ORBIS_KERNEL_MAP_OP_TYPE_PROTECT: { case MemoryOpTypes::ORBIS_KERNEL_MAP_OP_TYPE_PROTECT: {
result = sceKernelMTypeProtect(entries[i].start, entries[i].length, entries[i].type, result = sceKernelMtypeprotect(entries[i].start, entries[i].length, entries[i].type,
entries[i].protection); entries[i].protection);
LOG_INFO(Kernel_Vmm, "entry = {}, operation = {}, len = {:#x}, result = {}", i, LOG_INFO(Kernel_Vmm, "entry = {}, operation = {}, len = {:#x}, result = {}", i,
entries[i].operation, entries[i].length, result); entries[i].operation, entries[i].length, result);
@ -380,7 +390,7 @@ s32 PS4_SYSV_ABI sceKernelBatchMap2(OrbisKernelBatchMapEntry* entries, int numEn
return result; return result;
} }
s32 PS4_SYSV_ABI sceKernelSetVirtualRangeName(const void* addr, size_t len, const char* name) { s32 PS4_SYSV_ABI sceKernelSetVirtualRangeName(const void* addr, u64 len, const char* name) {
if (name == nullptr) { if (name == nullptr) {
LOG_ERROR(Kernel_Vmm, "name is invalid!"); LOG_ERROR(Kernel_Vmm, "name is invalid!");
return ORBIS_KERNEL_ERROR_EFAULT; return ORBIS_KERNEL_ERROR_EFAULT;
@ -396,8 +406,8 @@ s32 PS4_SYSV_ABI sceKernelSetVirtualRangeName(const void* addr, size_t len, cons
return ORBIS_OK; return ORBIS_OK;
} }
s32 PS4_SYSV_ABI sceKernelMemoryPoolExpand(u64 searchStart, u64 searchEnd, size_t len, s32 PS4_SYSV_ABI sceKernelMemoryPoolExpand(u64 searchStart, u64 searchEnd, u64 len, u64 alignment,
size_t alignment, u64* physAddrOut) { u64* physAddrOut) {
if (searchStart < 0 || searchEnd <= searchStart) { if (searchStart < 0 || searchEnd <= searchStart) {
LOG_ERROR(Kernel_Vmm, "Provided address range is invalid!"); LOG_ERROR(Kernel_Vmm, "Provided address range is invalid!");
return ORBIS_KERNEL_ERROR_EINVAL; return ORBIS_KERNEL_ERROR_EINVAL;
@ -439,10 +449,10 @@ s32 PS4_SYSV_ABI sceKernelMemoryPoolExpand(u64 searchStart, u64 searchEnd, size_
return ORBIS_OK; return ORBIS_OK;
} }
s32 PS4_SYSV_ABI sceKernelMemoryPoolReserve(void* addrIn, size_t len, size_t alignment, int flags, s32 PS4_SYSV_ABI sceKernelMemoryPoolReserve(void* addr_in, u64 len, u64 alignment, s32 flags,
void** addrOut) { void** addr_out) {
LOG_INFO(Kernel_Vmm, "addrIn = {}, len = {:#x}, alignment = {:#x}, flags = {:#x}", LOG_INFO(Kernel_Vmm, "addr_in = {}, len = {:#x}, alignment = {:#x}, flags = {:#x}",
fmt::ptr(addrIn), len, alignment, flags); fmt::ptr(addr_in), len, alignment, flags);
if (len == 0 || !Common::Is2MBAligned(len)) { if (len == 0 || !Common::Is2MBAligned(len)) {
LOG_ERROR(Kernel_Vmm, "Map size is either zero or not 2MB aligned!"); LOG_ERROR(Kernel_Vmm, "Map size is either zero or not 2MB aligned!");
@ -456,14 +466,16 @@ s32 PS4_SYSV_ABI sceKernelMemoryPoolReserve(void* addrIn, size_t len, size_t ali
} }
auto* memory = Core::Memory::Instance(); auto* memory = Core::Memory::Instance();
const VAddr in_addr = reinterpret_cast<VAddr>(addrIn); const VAddr in_addr = reinterpret_cast<VAddr>(addr_in);
const auto map_flags = static_cast<Core::MemoryMapFlags>(flags); const auto map_flags = static_cast<Core::MemoryMapFlags>(flags);
memory->PoolReserve(addrOut, in_addr, len, map_flags, alignment); u64 map_alignment = alignment == 0 ? 2_MB : alignment;
return ORBIS_OK; return memory->MapMemory(addr_out, std::bit_cast<VAddr>(addr_in), len,
Core::MemoryProt::NoAccess, map_flags, Core::VMAType::PoolReserved,
"anon", false, -1, map_alignment);
} }
s32 PS4_SYSV_ABI sceKernelMemoryPoolCommit(void* addr, size_t len, int type, int prot, int flags) { s32 PS4_SYSV_ABI sceKernelMemoryPoolCommit(void* addr, u64 len, s32 type, s32 prot, s32 flags) {
if (addr == nullptr) { if (addr == nullptr) {
LOG_ERROR(Kernel_Vmm, "Address is invalid!"); LOG_ERROR(Kernel_Vmm, "Address is invalid!");
return ORBIS_KERNEL_ERROR_EINVAL; return ORBIS_KERNEL_ERROR_EINVAL;
@ -482,7 +494,7 @@ s32 PS4_SYSV_ABI sceKernelMemoryPoolCommit(void* addr, size_t len, int type, int
return memory->PoolCommit(in_addr, len, mem_prot); return memory->PoolCommit(in_addr, len, mem_prot);
} }
s32 PS4_SYSV_ABI sceKernelMemoryPoolDecommit(void* addr, size_t len, int flags) { s32 PS4_SYSV_ABI sceKernelMemoryPoolDecommit(void* addr, u64 len, s32 flags) {
if (addr == nullptr) { if (addr == nullptr) {
LOG_ERROR(Kernel_Vmm, "Address is invalid!"); LOG_ERROR(Kernel_Vmm, "Address is invalid!");
return ORBIS_KERNEL_ERROR_EINVAL; return ORBIS_KERNEL_ERROR_EINVAL;
@ -523,12 +535,12 @@ s32 PS4_SYSV_ABI sceKernelMemoryPoolBatch(const OrbisKernelMemoryPoolBatchEntry*
break; break;
} }
case OrbisKernelMemoryPoolOpcode::Protect: { case OrbisKernelMemoryPoolOpcode::Protect: {
result = sceKernelMProtect(entry.protect_params.addr, entry.protect_params.len, result = sceKernelMprotect(entry.protect_params.addr, entry.protect_params.len,
entry.protect_params.prot); entry.protect_params.prot);
break; break;
} }
case OrbisKernelMemoryPoolOpcode::TypeProtect: { case OrbisKernelMemoryPoolOpcode::TypeProtect: {
result = sceKernelMTypeProtect( result = sceKernelMtypeprotect(
entry.type_protect_params.addr, entry.type_protect_params.len, entry.type_protect_params.addr, entry.type_protect_params.len,
entry.type_protect_params.type, entry.type_protect_params.prot); entry.type_protect_params.type, entry.type_protect_params.prot);
break; break;
@ -553,30 +565,48 @@ s32 PS4_SYSV_ABI sceKernelMemoryPoolBatch(const OrbisKernelMemoryPoolBatchEntry*
return result; return result;
} }
int PS4_SYSV_ABI sceKernelMmap(void* addr, u64 len, int prot, int flags, int fd, size_t offset, void* PS4_SYSV_ABI posix_mmap(void* addr, u64 len, s32 prot, s32 flags, s32 fd, s64 phys_addr) {
void** res) { LOG_INFO(Kernel_Vmm,
LOG_INFO(Kernel_Vmm, "called addr = {}, len = {}, prot = {}, flags = {}, fd = {}, offset = {}", "called addr = {}, len = {}, prot = {}, flags = {}, fd = {}, phys_addr = {}",
fmt::ptr(addr), len, prot, flags, fd, offset); fmt::ptr(addr), len, prot, flags, fd, phys_addr);
auto* h = Common::Singleton<Core::FileSys::HandleTable>::Instance();
void* addr_out;
auto* memory = Core::Memory::Instance(); auto* memory = Core::Memory::Instance();
const auto mem_prot = static_cast<Core::MemoryProt>(prot); const auto mem_prot = static_cast<Core::MemoryProt>(prot);
const auto mem_flags = static_cast<Core::MemoryMapFlags>(flags); const auto mem_flags = static_cast<Core::MemoryMapFlags>(flags);
s32 result = ORBIS_OK;
if (fd == -1) { if (fd == -1) {
return memory->MapMemory(res, std::bit_cast<VAddr>(addr), len, mem_prot, mem_flags, result = memory->MapMemory(&addr_out, std::bit_cast<VAddr>(addr), len, mem_prot, mem_flags,
Core::VMAType::Flexible); Core::VMAType::Flexible);
} else { } else {
const uintptr_t handle = h->GetFile(fd)->f.GetFileMapping(); result = memory->MapFile(&addr_out, std::bit_cast<VAddr>(addr), len, mem_prot, mem_flags,
return memory->MapFile(res, std::bit_cast<VAddr>(addr), len, mem_prot, mem_flags, handle, fd, phys_addr);
offset);
} }
if (result != ORBIS_OK) {
// If the memory mappings fail, mmap sets errno to the appropriate error code,
// then returns (void*)-1;
ErrSceToPosix(result);
return reinterpret_cast<void*>(-1);
}
return addr_out;
} }
void* PS4_SYSV_ABI posix_mmap(void* addr, u64 len, int prot, int flags, int fd, u64 offset) { s32 PS4_SYSV_ABI sceKernelMmap(void* addr, u64 len, s32 prot, s32 flags, s32 fd, s64 phys_addr,
void* ptr; void** res) {
LOG_INFO(Kernel_Vmm, "posix mmap redirect to sceKernelMmap"); void* addr_out = posix_mmap(addr, len, prot, flags, fd, phys_addr);
int result = sceKernelMmap(addr, len, prot, flags, fd, offset, &ptr);
ASSERT(result == 0); if (addr_out == reinterpret_cast<void*>(-1)) {
return ptr; // posix_mmap failed, calculate and return the appropriate kernel error code using errno.
LOG_ERROR(Kernel_Fs, "error = {}", *__Error());
return ErrnoToSceKernelError(*__Error());
}
// Set the outputted address
*res = addr_out;
return ORBIS_OK;
} }
s32 PS4_SYSV_ABI sceKernelConfiguredFlexibleMemorySize(u64* sizeOut) { s32 PS4_SYSV_ABI sceKernelConfiguredFlexibleMemorySize(u64* sizeOut) {
@ -630,6 +660,9 @@ int PS4_SYSV_ABI sceKernelSetPrtAperture(int id, VAddr address, size_t size) {
"PRT aperture id = {}, address = {:#x}, size = {:#x} is set but not used", id, "PRT aperture id = {}, address = {:#x}, size = {:#x} is set but not used", id,
address, size); address, size);
auto* memory = Core::Memory::Instance();
memory->SetPrtArea(id, address, size);
PrtApertures[id] = {address, size}; PrtApertures[id] = {address, size};
return ORBIS_OK; return ORBIS_OK;
} }
@ -678,8 +711,9 @@ void RegisterMemory(Core::Loader::SymbolsResolver* sym) {
LIB_FUNCTION("n1-v6FgU7MQ", "libkernel", 1, "libkernel", 1, 1, LIB_FUNCTION("n1-v6FgU7MQ", "libkernel", 1, "libkernel", 1, 1,
sceKernelConfiguredFlexibleMemorySize); sceKernelConfiguredFlexibleMemorySize);
LIB_FUNCTION("9bfdLIyuwCY", "libkernel", 1, "libkernel", 1, 1, sceKernelMTypeProtect); LIB_FUNCTION("vSMAm3cxYTY", "libkernel", 1, "libkernel", 1, 1, sceKernelMprotect);
LIB_FUNCTION("vSMAm3cxYTY", "libkernel", 1, "libkernel", 1, 1, sceKernelMProtect); LIB_FUNCTION("YQOfxL4QfeU", "libScePosix", 1, "libkernel", 1, 1, posix_mprotect);
LIB_FUNCTION("9bfdLIyuwCY", "libkernel", 1, "libkernel", 1, 1, sceKernelMtypeprotect);
// Memory pool // Memory pool
LIB_FUNCTION("qCSfqDILlns", "libkernel", 1, "libkernel", 1, 1, sceKernelMemoryPoolExpand); LIB_FUNCTION("qCSfqDILlns", "libkernel", 1, "libkernel", 1, 1, sceKernelMemoryPoolExpand);

View File

@ -141,15 +141,14 @@ s32 PS4_SYSV_ABI sceKernelAvailableDirectMemorySize(u64 searchStart, u64 searchE
s32 PS4_SYSV_ABI sceKernelVirtualQuery(const void* addr, int flags, OrbisVirtualQueryInfo* info, s32 PS4_SYSV_ABI sceKernelVirtualQuery(const void* addr, int flags, OrbisVirtualQueryInfo* info,
size_t infoSize); size_t infoSize);
s32 PS4_SYSV_ABI sceKernelReserveVirtualRange(void** addr, u64 len, int flags, u64 alignment); s32 PS4_SYSV_ABI sceKernelReserveVirtualRange(void** addr, u64 len, int flags, u64 alignment);
s32 PS4_SYSV_ABI sceKernelMapNamedFlexibleMemory(void** addrInOut, std::size_t len, int prot, s32 PS4_SYSV_ABI sceKernelMapNamedFlexibleMemory(void** addr_in_out, u64 len, s32 prot, s32 flags,
int flags, const char* name); const char* name);
s32 PS4_SYSV_ABI sceKernelMapFlexibleMemory(void** addr_in_out, std::size_t len, int prot, s32 PS4_SYSV_ABI sceKernelMapFlexibleMemory(void** addr_in_out, u64 len, s32 prot, s32 flags);
int flags);
int PS4_SYSV_ABI sceKernelQueryMemoryProtection(void* addr, void** start, void** end, u32* prot); int PS4_SYSV_ABI sceKernelQueryMemoryProtection(void* addr, void** start, void** end, u32* prot);
int PS4_SYSV_ABI sceKernelMProtect(const void* addr, size_t size, int prot); s32 PS4_SYSV_ABI sceKernelMprotect(const void* addr, u64 size, s32 prot);
int PS4_SYSV_ABI sceKernelMTypeProtect(const void* addr, size_t size, int mtype, int prot); s32 PS4_SYSV_ABI sceKernelMtypeprotect(const void* addr, u64 size, s32 mtype, s32 prot);
int PS4_SYSV_ABI sceKernelDirectMemoryQuery(u64 offset, int flags, OrbisQueryInfo* query_info, int PS4_SYSV_ABI sceKernelDirectMemoryQuery(u64 offset, int flags, OrbisQueryInfo* query_info,
size_t infoSize); size_t infoSize);
@ -165,14 +164,14 @@ s32 PS4_SYSV_ABI sceKernelBatchMap(OrbisKernelBatchMapEntry* entries, int numEnt
s32 PS4_SYSV_ABI sceKernelBatchMap2(OrbisKernelBatchMapEntry* entries, int numEntries, s32 PS4_SYSV_ABI sceKernelBatchMap2(OrbisKernelBatchMapEntry* entries, int numEntries,
int* numEntriesOut, int flags); int* numEntriesOut, int flags);
s32 PS4_SYSV_ABI sceKernelSetVirtualRangeName(const void* addr, size_t len, const char* name); s32 PS4_SYSV_ABI sceKernelSetVirtualRangeName(const void* addr, u64 len, const char* name);
s32 PS4_SYSV_ABI sceKernelMemoryPoolExpand(u64 searchStart, u64 searchEnd, size_t len, s32 PS4_SYSV_ABI sceKernelMemoryPoolExpand(u64 searchStart, u64 searchEnd, u64 len, u64 alignment,
size_t alignment, u64* physAddrOut); u64* physAddrOut);
s32 PS4_SYSV_ABI sceKernelMemoryPoolReserve(void* addrIn, size_t len, size_t alignment, int flags, s32 PS4_SYSV_ABI sceKernelMemoryPoolReserve(void* addr_in, u64 len, u64 alignment, s32 flags,
void** addrOut); void** addr_out);
s32 PS4_SYSV_ABI sceKernelMemoryPoolCommit(void* addr, size_t len, int type, int prot, int flags); s32 PS4_SYSV_ABI sceKernelMemoryPoolCommit(void* addr, u64 len, s32 type, s32 prot, s32 flags);
s32 PS4_SYSV_ABI sceKernelMemoryPoolDecommit(void* addr, size_t len, int flags); s32 PS4_SYSV_ABI sceKernelMemoryPoolDecommit(void* addr, u64 len, s32 flags);
s32 PS4_SYSV_ABI sceKernelMemoryPoolBatch(const OrbisKernelMemoryPoolBatchEntry* entries, s32 count, s32 PS4_SYSV_ABI sceKernelMemoryPoolBatch(const OrbisKernelMemoryPoolBatchEntry* entries, s32 count,
s32* num_processed, s32 flags); s32* num_processed, s32 flags);

View File

@ -426,6 +426,7 @@ void RegisterMutex(Core::Loader::SymbolsResolver* sym) {
// Posix // Posix
LIB_FUNCTION("ttHNfU+qDBU", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_mutex_init); LIB_FUNCTION("ttHNfU+qDBU", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_mutex_init);
LIB_FUNCTION("7H0iTOciTLo", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_mutex_lock); LIB_FUNCTION("7H0iTOciTLo", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_mutex_lock);
LIB_FUNCTION("Io9+nTKXZtA", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_mutex_timedlock);
LIB_FUNCTION("2Z+PpY6CaJg", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_mutex_unlock); LIB_FUNCTION("2Z+PpY6CaJg", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_mutex_unlock);
LIB_FUNCTION("ltCfaGr2JGE", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_mutex_destroy); LIB_FUNCTION("ltCfaGr2JGE", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_mutex_destroy);
LIB_FUNCTION("dQHWEsJtoE4", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_mutexattr_init); LIB_FUNCTION("dQHWEsJtoE4", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_mutexattr_init);

View File

@ -576,8 +576,19 @@ int PS4_SYSV_ABI posix_pthread_getaffinity_np(PthreadT thread, size_t cpusetsize
if (thread == nullptr || cpusetp == nullptr) { if (thread == nullptr || cpusetp == nullptr) {
return POSIX_EINVAL; return POSIX_EINVAL;
} }
auto* thread_state = ThrState::Instance();
if (thread == g_curthread) {
g_curthread->lock.lock();
} else if (auto ret = thread_state->FindThread(thread, /*include dead*/ 0); ret != 0) {
return ret;
}
auto* attr_ptr = &thread->attr; auto* attr_ptr = &thread->attr;
return posix_pthread_attr_getaffinity_np(&attr_ptr, cpusetsize, cpusetp); auto ret = posix_pthread_attr_getaffinity_np(&attr_ptr, cpusetsize, cpusetp);
thread->lock.unlock();
return ret;
} }
int PS4_SYSV_ABI posix_pthread_setaffinity_np(PthreadT thread, size_t cpusetsize, int PS4_SYSV_ABI posix_pthread_setaffinity_np(PthreadT thread, size_t cpusetsize,
@ -585,11 +596,23 @@ int PS4_SYSV_ABI posix_pthread_setaffinity_np(PthreadT thread, size_t cpusetsize
if (thread == nullptr || cpusetp == nullptr) { if (thread == nullptr || cpusetp == nullptr) {
return POSIX_EINVAL; return POSIX_EINVAL;
} }
auto* attr_ptr = &thread->attr;
if (const auto ret = posix_pthread_attr_setaffinity_np(&attr_ptr, cpusetsize, cpusetp)) { auto* thread_state = ThrState::Instance();
if (thread == g_curthread) {
g_curthread->lock.lock();
} else if (auto ret = thread_state->FindThread(thread, /*include dead*/ 0); ret != 0) {
return ret; return ret;
} }
return thread->SetAffinity(thread->attr.cpuset);
auto* attr_ptr = &thread->attr;
auto ret = posix_pthread_attr_setaffinity_np(&attr_ptr, cpusetsize, cpusetp);
if (ret == ORBIS_OK) {
ret = thread->SetAffinity(thread->attr.cpuset);
}
thread->lock.unlock();
return ret;
} }
int PS4_SYSV_ABI scePthreadGetaffinity(PthreadT thread, u64* mask) { int PS4_SYSV_ABI scePthreadGetaffinity(PthreadT thread, u64* mask) {

View File

@ -306,6 +306,8 @@ void RegisterThreadAttr(Core::Loader::SymbolsResolver* sym) {
posix_pthread_attr_getdetachstate); posix_pthread_attr_getdetachstate);
LIB_FUNCTION("JKyG3SWyA10", "libScePosix", 1, "libkernel", 1, 1, LIB_FUNCTION("JKyG3SWyA10", "libScePosix", 1, "libkernel", 1, 1,
posix_pthread_attr_setguardsize); posix_pthread_attr_setguardsize);
LIB_FUNCTION("qlk9pSLsUmM", "libScePosix", 1, "libkernel", 1, 1,
posix_pthread_attr_getschedparam);
// Orbis // Orbis
LIB_FUNCTION("4+h9EzwKF4I", "libkernel", 1, "libkernel", 1, 1, LIB_FUNCTION("4+h9EzwKF4I", "libkernel", 1, "libkernel", 1, 1,

View File

@ -10,6 +10,7 @@
#include "core/libraries/avplayer/avplayer.h" #include "core/libraries/avplayer/avplayer.h"
#include "core/libraries/camera/camera.h" #include "core/libraries/camera/camera.h"
#include "core/libraries/companion/companion_httpd.h" #include "core/libraries/companion/companion_httpd.h"
#include "core/libraries/companion/companion_util.h"
#include "core/libraries/disc_map/disc_map.h" #include "core/libraries/disc_map/disc_map.h"
#include "core/libraries/game_live_streaming/gamelivestreaming.h" #include "core/libraries/game_live_streaming/gamelivestreaming.h"
#include "core/libraries/gnmdriver/gnmdriver.h" #include "core/libraries/gnmdriver/gnmdriver.h"
@ -59,6 +60,7 @@
#include "core/libraries/videodec/videodec.h" #include "core/libraries/videodec/videodec.h"
#include "core/libraries/videodec/videodec2.h" #include "core/libraries/videodec/videodec2.h"
#include "core/libraries/videoout/video_out.h" #include "core/libraries/videoout/video_out.h"
#include "core/libraries/voice/voice.h"
#include "core/libraries/web_browser_dialog/webbrowserdialog.h" #include "core/libraries/web_browser_dialog/webbrowserdialog.h"
#include "core/libraries/zlib/zlib_sce.h" #include "core/libraries/zlib/zlib_sce.h"
#include "fiber/fiber.h" #include "fiber/fiber.h"
@ -126,6 +128,8 @@ void InitHLELibs(Core::Loader::SymbolsResolver* sym) {
Libraries::SigninDialog::RegisterlibSceSigninDialog(sym); Libraries::SigninDialog::RegisterlibSceSigninDialog(sym);
Libraries::Camera::RegisterlibSceCamera(sym); Libraries::Camera::RegisterlibSceCamera(sym);
Libraries::CompanionHttpd::RegisterlibSceCompanionHttpd(sym); Libraries::CompanionHttpd::RegisterlibSceCompanionHttpd(sym);
Libraries::CompanionUtil::RegisterlibSceCompanionUtil(sym);
Libraries::Voice::RegisterlibSceVoice(sym);
} }
} // namespace Libraries } // namespace Libraries

View File

@ -955,16 +955,148 @@ u16 PS4_SYSV_ABI sceNetHtons(u16 host16) {
return htons(host16); return htons(host16);
} }
const char* PS4_SYSV_ABI sceNetInetNtop(int af, const void* src, char* dst, u32 size) {
#ifdef WIN32 #ifdef WIN32
const char* res = InetNtopA(af, src, dst, size); // there isn't a strlcpy function in windows so implement one
#else u64 strlcpy(char* dst, const char* src, u64 size) {
const char* res = inet_ntop(af, src, dst, size); u64 src_len = strlen(src);
#endif
if (res == nullptr) { if (size > 0) {
UNREACHABLE(); u64 copy_len = (src_len >= size) ? (size - 1) : src_len;
memcpy(dst, src, copy_len);
dst[copy_len] = '\0';
} }
return dst;
return src_len;
}
#endif
const char* freebsd_inet_ntop4(const char* src, char* dst, u64 size) {
static const char fmt[] = "%u.%u.%u.%u";
char tmp[sizeof "255.255.255.255"];
int l;
l = snprintf(tmp, sizeof(tmp), fmt, src[0], src[1], src[2], src[3]);
if (l <= 0 || (socklen_t)l >= size) {
return nullptr;
}
strlcpy(dst, tmp, size);
return (dst);
}
const char* freebsd_inet_ntop6(const char* src, char* dst, u64 size) {
/*
* Note that int32_t and int16_t need only be "at least" large enough
* to contain a value of the specified size. On some systems, like
* Crays, there is no such thing as an integer variable with 16 bits.
* Keep this in mind if you think this function should have been coded
* to use pointer overlays. All the world's not a VAX.
*/
char tmp[sizeof "ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255"], *tp;
struct {
int base, len;
} best, cur;
#define NS_IN6ADDRSZ 16
#define NS_INT16SZ 2
u_int words[NS_IN6ADDRSZ / NS_INT16SZ];
int i;
/*
* Preprocess:
* Copy the input (bytewise) array into a wordwise array.
* Find the longest run of 0x00's in src[] for :: shorthanding.
*/
memset(words, '\0', sizeof words);
for (i = 0; i < NS_IN6ADDRSZ; i++)
words[i / 2] |= (src[i] << ((1 - (i % 2)) << 3));
best.base = -1;
best.len = 0;
cur.base = -1;
cur.len = 0;
for (i = 0; i < (NS_IN6ADDRSZ / NS_INT16SZ); i++) {
if (words[i] == 0) {
if (cur.base == -1)
cur.base = i, cur.len = 1;
else
cur.len++;
} else {
if (cur.base != -1) {
if (best.base == -1 || cur.len > best.len)
best = cur;
cur.base = -1;
}
}
}
if (cur.base != -1) {
if (best.base == -1 || cur.len > best.len)
best = cur;
}
if (best.base != -1 && best.len < 2)
best.base = -1;
/*
* Format the result.
*/
tp = tmp;
for (i = 0; i < (NS_IN6ADDRSZ / NS_INT16SZ); i++) {
/* Are we inside the best run of 0x00's? */
if (best.base != -1 && i >= best.base && i < (best.base + best.len)) {
if (i == best.base)
*tp++ = ':';
continue;
}
/* Are we following an initial run of 0x00s or any real hex? */
if (i != 0)
*tp++ = ':';
/* Is this address an encapsulated IPv4? */
if (i == 6 && best.base == 0 &&
(best.len == 6 || (best.len == 7 && words[7] != 0x0001) ||
(best.len == 5 && words[5] == 0xffff))) {
if (!freebsd_inet_ntop4(src + 12, tp, sizeof tmp - (tp - tmp)))
return nullptr;
tp += strlen(tp);
break;
}
tp += sprintf(tp, "%x", words[i]);
}
/* Was it a trailing run of 0x00's? */
if (best.base != -1 && (best.base + best.len) == (NS_IN6ADDRSZ / NS_INT16SZ))
*tp++ = ':';
*tp++ = '\0';
/*
* Check for overflow, copy, and we're done.
*/
if ((u64)(tp - tmp) > size) {
return nullptr;
}
strcpy(dst, tmp);
return (dst);
}
const char* PS4_SYSV_ABI sceNetInetNtop(int af, const void* src, char* dst, u32 size) {
if (!(src && dst)) {
*sceNetErrnoLoc() = ORBIS_NET_ENOSPC;
LOG_ERROR(Lib_Net, "returned ORBIS_NET_ENOSPC");
return nullptr;
}
const char* returnvalue = nullptr;
switch (af) {
case ORBIS_NET_AF_INET:
returnvalue = freebsd_inet_ntop4((const char*)src, dst, size);
break;
case ORBIS_NET_AF_INET6:
returnvalue = freebsd_inet_ntop6((const char*)src, dst, size);
break;
default:
*sceNetErrnoLoc() = ORBIS_NET_EAFNOSUPPORT;
LOG_ERROR(Lib_Net, "returned ORBIS_NET_EAFNOSUPPORT");
return nullptr;
}
if (returnvalue == nullptr) {
*sceNetErrnoLoc() = ORBIS_NET_ENOSPC;
LOG_ERROR(Lib_Net, "returned ORBIS_NET_ENOSPC");
}
return returnvalue;
} }
int PS4_SYSV_ABI sceNetInetNtopWithScopeId() { int PS4_SYSV_ABI sceNetInetNtopWithScopeId() {

View File

@ -20,6 +20,10 @@ class SymbolsResolver;
namespace Libraries::Net { namespace Libraries::Net {
enum OrbisNetFamily : u32 {
ORBIS_NET_AF_INET = 2,
ORBIS_NET_AF_INET6 = 28,
};
enum OrbisNetSocketType : u32 { enum OrbisNetSocketType : u32 {
ORBIS_NET_SOCK_STREAM = 1, ORBIS_NET_SOCK_STREAM = 1,
ORBIS_NET_SOCK_DGRAM = 2, ORBIS_NET_SOCK_DGRAM = 2,

View File

@ -1,6 +1,7 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project // SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
#include "common/config.h"
#include "common/logging/log.h" #include "common/logging/log.h"
#include "core/libraries/error_codes.h" #include "core/libraries/error_codes.h"
#include "core/libraries/libs.h" #include "core/libraries/libs.h"
@ -10,6 +11,8 @@
namespace Libraries::NpManager { namespace Libraries::NpManager {
#define SIGNEDIN_STATUS (Config::getPSNSignedIn() ? ORBIS_OK : ORBIS_NP_ERROR_SIGNED_OUT)
int PS4_SYSV_ABI Func_EF4378573542A508() { int PS4_SYSV_ABI Func_EF4378573542A508() {
LOG_ERROR(Lib_NpManager, "(STUBBED) called"); LOG_ERROR(Lib_NpManager, "(STUBBED) called");
return ORBIS_OK; return ORBIS_OK;
@ -921,9 +924,16 @@ int PS4_SYSV_ABI sceNpGetAccountCountry() {
return ORBIS_OK; return ORBIS_OK;
} }
int PS4_SYSV_ABI sceNpGetAccountCountryA() { int PS4_SYSV_ABI sceNpGetAccountCountryA(OrbisUserServiceUserId user_id,
LOG_ERROR(Lib_NpManager, "(STUBBED) called"); OrbisNpCountryCode* country_code) {
return ORBIS_OK; LOG_INFO(Lib_NpManager, "(STUBBED) called, user_id = {}", user_id);
if (country_code == nullptr) {
return ORBIS_NP_ERROR_INVALID_ARGUMENT;
}
::memset(country_code, 0, sizeof(OrbisNpCountryCode));
// TODO: get NP country code from config
::memcpy(country_code->country_code, "us", 2);
return SIGNEDIN_STATUS;
} }
int PS4_SYSV_ABI sceNpGetAccountDateOfBirth() { int PS4_SYSV_ABI sceNpGetAccountDateOfBirth() {
@ -941,8 +951,8 @@ int PS4_SYSV_ABI sceNpGetAccountId(OrbisNpOnlineId* online_id, u64* account_id)
if (online_id == nullptr || account_id == nullptr) { if (online_id == nullptr || account_id == nullptr) {
return ORBIS_NP_ERROR_INVALID_ARGUMENT; return ORBIS_NP_ERROR_INVALID_ARGUMENT;
} }
*account_id = 0; *account_id = 0xFEEDFACE;
return ORBIS_NP_ERROR_SIGNED_OUT; return SIGNEDIN_STATUS;
} }
int PS4_SYSV_ABI sceNpGetAccountIdA(OrbisUserServiceUserId user_id, u64* account_id) { int PS4_SYSV_ABI sceNpGetAccountIdA(OrbisUserServiceUserId user_id, u64* account_id) {
@ -950,8 +960,8 @@ int PS4_SYSV_ABI sceNpGetAccountIdA(OrbisUserServiceUserId user_id, u64* account
if (account_id == nullptr) { if (account_id == nullptr) {
return ORBIS_NP_ERROR_INVALID_ARGUMENT; return ORBIS_NP_ERROR_INVALID_ARGUMENT;
} }
*account_id = 0; *account_id = 0xFEEDFACE;
return ORBIS_NP_ERROR_SIGNED_OUT; return SIGNEDIN_STATUS;
} }
int PS4_SYSV_ABI sceNpGetAccountLanguage() { int PS4_SYSV_ABI sceNpGetAccountLanguage() {
@ -984,7 +994,9 @@ int PS4_SYSV_ABI sceNpGetNpId(OrbisUserServiceUserId user_id, OrbisNpId* np_id)
if (np_id == nullptr) { if (np_id == nullptr) {
return ORBIS_NP_ERROR_INVALID_ARGUMENT; return ORBIS_NP_ERROR_INVALID_ARGUMENT;
} }
return ORBIS_NP_ERROR_SIGNED_OUT; memset(np_id, 0, sizeof(OrbisNpId));
strncpy(np_id->handle.data, Config::getUserName().c_str(), sizeof(np_id->handle.data));
return SIGNEDIN_STATUS;
} }
int PS4_SYSV_ABI sceNpGetNpReachabilityState() { int PS4_SYSV_ABI sceNpGetNpReachabilityState() {
@ -997,7 +1009,9 @@ int PS4_SYSV_ABI sceNpGetOnlineId(OrbisUserServiceUserId user_id, OrbisNpOnlineI
if (online_id == nullptr) { if (online_id == nullptr) {
return ORBIS_NP_ERROR_INVALID_ARGUMENT; return ORBIS_NP_ERROR_INVALID_ARGUMENT;
} }
return ORBIS_NP_ERROR_SIGNED_OUT; memset(online_id, 0, sizeof(OrbisNpOnlineId));
strncpy(online_id->data, Config::getUserName().c_str(), sizeof(online_id->data));
return SIGNEDIN_STATUS;
} }
int PS4_SYSV_ABI sceNpGetParentalControlInfo() { int PS4_SYSV_ABI sceNpGetParentalControlInfo() {
@ -1014,8 +1028,8 @@ int PS4_SYSV_ABI sceNpGetState(OrbisUserServiceUserId user_id, OrbisNpState* sta
if (state == nullptr) { if (state == nullptr) {
return ORBIS_NP_ERROR_INVALID_ARGUMENT; return ORBIS_NP_ERROR_INVALID_ARGUMENT;
} }
*state = OrbisNpState::SignedOut; *state = Config::getPSNSignedIn() ? OrbisNpState::SignedIn : OrbisNpState::SignedOut;
LOG_DEBUG(Lib_NpManager, "Signed out"); LOG_DEBUG(Lib_NpManager, "Signed {}", Config::getPSNSignedIn() ? "in" : "out");
return ORBIS_OK; return ORBIS_OK;
} }

View File

@ -32,6 +32,12 @@ struct OrbisNpId {
u8 reserved[8]; u8 reserved[8];
}; };
struct OrbisNpCountryCode {
char country_code[2];
char end;
char pad;
};
int PS4_SYSV_ABI Func_EF4378573542A508(); int PS4_SYSV_ABI Func_EF4378573542A508();
int PS4_SYSV_ABI _sceNpIpcCreateMemoryFromKernel(); int PS4_SYSV_ABI _sceNpIpcCreateMemoryFromKernel();
int PS4_SYSV_ABI _sceNpIpcCreateMemoryFromPool(); int PS4_SYSV_ABI _sceNpIpcCreateMemoryFromPool();
@ -215,7 +221,8 @@ int PS4_SYSV_ABI sceNpCreateRequest();
int PS4_SYSV_ABI sceNpDeleteRequest(int reqId); int PS4_SYSV_ABI sceNpDeleteRequest(int reqId);
int PS4_SYSV_ABI sceNpGetAccountAge(); int PS4_SYSV_ABI sceNpGetAccountAge();
int PS4_SYSV_ABI sceNpGetAccountCountry(); int PS4_SYSV_ABI sceNpGetAccountCountry();
int PS4_SYSV_ABI sceNpGetAccountCountryA(); int PS4_SYSV_ABI sceNpGetAccountCountryA(OrbisUserServiceUserId user_id,
OrbisNpCountryCode* country_code);
int PS4_SYSV_ABI sceNpGetAccountDateOfBirth(); int PS4_SYSV_ABI sceNpGetAccountDateOfBirth();
int PS4_SYSV_ABI sceNpGetAccountDateOfBirthA(); int PS4_SYSV_ABI sceNpGetAccountDateOfBirthA();
int PS4_SYSV_ABI sceNpGetAccountId(OrbisNpOnlineId* online_id, u64* account_id); int PS4_SYSV_ABI sceNpGetAccountId(OrbisNpOnlineId* online_id, u64* account_id);

View File

@ -316,22 +316,79 @@ int PS4_SYSV_ABI scePadRead(s32 handle, OrbisPadData* pData, s32 num) {
pData[i].angularVelocity.y = states[i].angularVelocity.y; pData[i].angularVelocity.y = states[i].angularVelocity.y;
pData[i].angularVelocity.z = states[i].angularVelocity.z; pData[i].angularVelocity.z = states[i].angularVelocity.z;
pData[i].orientation = {0.0f, 0.0f, 0.0f, 1.0f}; pData[i].orientation = {0.0f, 0.0f, 0.0f, 1.0f};
if (engine) { pData[i].acceleration.x = states[i].acceleration.x * 0.098;
pData[i].acceleration.y = states[i].acceleration.y * 0.098;
pData[i].acceleration.z = states[i].acceleration.z * 0.098;
pData[i].angularVelocity.x = states[i].angularVelocity.x;
pData[i].angularVelocity.y = states[i].angularVelocity.y;
pData[i].angularVelocity.z = states[i].angularVelocity.z;
if (engine && handle == 1) {
const auto gyro_poll_rate = engine->GetAccelPollRate(); const auto gyro_poll_rate = engine->GetAccelPollRate();
if (gyro_poll_rate != 0.0f) { if (gyro_poll_rate != 0.0f) {
GameController::CalculateOrientation(pData[i].acceleration, auto now = std::chrono::steady_clock::now();
pData[i].angularVelocity, float deltaTime = std::chrono::duration_cast<std::chrono::microseconds>(
1.0f / gyro_poll_rate, pData[i].orientation); now - controller->GetLastUpdate())
.count() /
1000000.0f;
controller->SetLastUpdate(now);
Libraries::Pad::OrbisFQuaternion lastOrientation = controller->GetLastOrientation();
Libraries::Pad::OrbisFQuaternion outputOrientation = {0.0f, 0.0f, 0.0f, 1.0f};
GameController::CalculateOrientation(pData->acceleration, pData->angularVelocity,
deltaTime, lastOrientation, outputOrientation);
pData[i].orientation = outputOrientation;
controller->SetLastOrientation(outputOrientation);
} }
} }
pData[i].touchData.touchNum = pData[i].touchData.touchNum =
(states[i].touchpad[0].state ? 1 : 0) + (states[i].touchpad[1].state ? 1 : 0); (states[i].touchpad[0].state ? 1 : 0) + (states[i].touchpad[1].state ? 1 : 0);
if (handle == 1) {
if (controller->GetTouchCount() >= 127) {
controller->SetTouchCount(0);
}
if (controller->GetSecondaryTouchCount() >= 127) {
controller->SetSecondaryTouchCount(0);
}
if (pData->touchData.touchNum == 1 && controller->GetPreviousTouchNum() == 0) {
controller->SetTouchCount(controller->GetTouchCount() + 1);
controller->SetSecondaryTouchCount(controller->GetTouchCount());
} else if (pData->touchData.touchNum == 2 && controller->GetPreviousTouchNum() == 1) {
controller->SetSecondaryTouchCount(controller->GetSecondaryTouchCount() + 1);
} else if (pData->touchData.touchNum == 0 && controller->GetPreviousTouchNum() > 0) {
if (controller->GetTouchCount() < controller->GetSecondaryTouchCount()) {
controller->SetTouchCount(controller->GetSecondaryTouchCount());
} else {
if (controller->WasSecondaryTouchReset()) {
controller->SetTouchCount(controller->GetSecondaryTouchCount());
controller->UnsetSecondaryTouchResetBool();
}
}
}
controller->SetPreviousTouchNum(pData->touchData.touchNum);
if (pData->touchData.touchNum == 1) {
states[i].touchpad[0].ID = controller->GetTouchCount();
states[i].touchpad[1].ID = 0;
} else if (pData->touchData.touchNum == 2) {
states[i].touchpad[0].ID = controller->GetTouchCount();
states[i].touchpad[1].ID = controller->GetSecondaryTouchCount();
}
} else {
states[i].touchpad[0].ID = 1;
states[i].touchpad[1].ID = 2;
}
pData[i].touchData.touch[0].x = states[i].touchpad[0].x; pData[i].touchData.touch[0].x = states[i].touchpad[0].x;
pData[i].touchData.touch[0].y = states[i].touchpad[0].y; pData[i].touchData.touch[0].y = states[i].touchpad[0].y;
pData[i].touchData.touch[0].id = 1; pData[i].touchData.touch[0].id = states[i].touchpad[0].ID;
pData[i].touchData.touch[1].x = states[i].touchpad[1].x; pData[i].touchData.touch[1].x = states[i].touchpad[1].x;
pData[i].touchData.touch[1].y = states[i].touchpad[1].y; pData[i].touchData.touch[1].y = states[i].touchpad[1].y;
pData[i].touchData.touch[1].id = 2; pData[i].touchData.touch[1].id = states[i].touchpad[1].ID;
pData[i].connected = connected; pData[i].connected = connected;
pData[i].timestamp = states[i].time; pData[i].timestamp = states[i].time;
pData[i].connectedCount = connected_count; pData[i].connectedCount = connected_count;
@ -376,31 +433,85 @@ int PS4_SYSV_ABI scePadReadState(s32 handle, OrbisPadData* pData) {
pData->leftStick.x = state.axes[static_cast<int>(Input::Axis::LeftX)]; pData->leftStick.x = state.axes[static_cast<int>(Input::Axis::LeftX)];
pData->leftStick.y = state.axes[static_cast<int>(Input::Axis::LeftY)]; pData->leftStick.y = state.axes[static_cast<int>(Input::Axis::LeftY)];
pData->rightStick.x = state.axes[static_cast<int>(Input::Axis::RightX)]; pData->rightStick.x = state.axes[static_cast<int>(Input::Axis::RightX)];
pData->rightStick.x = state.axes[static_cast<int>(Input::Axis::RightX)];
pData->rightStick.y = state.axes[static_cast<int>(Input::Axis::RightY)]; pData->rightStick.y = state.axes[static_cast<int>(Input::Axis::RightY)];
pData->analogButtons.l2 = state.axes[static_cast<int>(Input::Axis::TriggerLeft)]; pData->analogButtons.l2 = state.axes[static_cast<int>(Input::Axis::TriggerLeft)];
pData->analogButtons.r2 = state.axes[static_cast<int>(Input::Axis::TriggerRight)]; pData->analogButtons.r2 = state.axes[static_cast<int>(Input::Axis::TriggerRight)];
pData->acceleration.x = state.acceleration.x; pData->acceleration.x = state.acceleration.x * 0.098;
pData->acceleration.y = state.acceleration.y; pData->acceleration.y = state.acceleration.y * 0.098;
pData->acceleration.z = state.acceleration.z; pData->acceleration.z = state.acceleration.z * 0.098;
pData->angularVelocity.x = state.angularVelocity.x; pData->angularVelocity.x = state.angularVelocity.x;
pData->angularVelocity.y = state.angularVelocity.y; pData->angularVelocity.y = state.angularVelocity.y;
pData->angularVelocity.z = state.angularVelocity.z; pData->angularVelocity.z = state.angularVelocity.z;
pData->orientation = {0.0f, 0.0f, 0.0f, 1.0f}; pData->orientation = {0.0f, 0.0f, 0.0f, 1.0f};
if (engine) {
// Only do this on handle 1 for now
if (engine && handle == 1) {
const auto gyro_poll_rate = engine->GetAccelPollRate(); const auto gyro_poll_rate = engine->GetAccelPollRate();
if (gyro_poll_rate != 0.0f) { if (gyro_poll_rate != 0.0f) {
auto now = std::chrono::steady_clock::now();
float deltaTime = std::chrono::duration_cast<std::chrono::microseconds>(
now - controller->GetLastUpdate())
.count() /
1000000.0f;
controller->SetLastUpdate(now);
Libraries::Pad::OrbisFQuaternion lastOrientation = controller->GetLastOrientation();
Libraries::Pad::OrbisFQuaternion outputOrientation = {0.0f, 0.0f, 0.0f, 1.0f};
GameController::CalculateOrientation(pData->acceleration, pData->angularVelocity, GameController::CalculateOrientation(pData->acceleration, pData->angularVelocity,
1.0f / gyro_poll_rate, pData->orientation); deltaTime, lastOrientation, outputOrientation);
pData->orientation = outputOrientation;
controller->SetLastOrientation(outputOrientation);
} }
} }
pData->touchData.touchNum = pData->touchData.touchNum =
(state.touchpad[0].state ? 1 : 0) + (state.touchpad[1].state ? 1 : 0); (state.touchpad[0].state ? 1 : 0) + (state.touchpad[1].state ? 1 : 0);
// Only do this on handle 1 for now
if (handle == 1) {
if (controller->GetTouchCount() >= 127) {
controller->SetTouchCount(0);
}
if (controller->GetSecondaryTouchCount() >= 127) {
controller->SetSecondaryTouchCount(0);
}
if (pData->touchData.touchNum == 1 && controller->GetPreviousTouchNum() == 0) {
controller->SetTouchCount(controller->GetTouchCount() + 1);
controller->SetSecondaryTouchCount(controller->GetTouchCount());
} else if (pData->touchData.touchNum == 2 && controller->GetPreviousTouchNum() == 1) {
controller->SetSecondaryTouchCount(controller->GetSecondaryTouchCount() + 1);
} else if (pData->touchData.touchNum == 0 && controller->GetPreviousTouchNum() > 0) {
if (controller->GetTouchCount() < controller->GetSecondaryTouchCount()) {
controller->SetTouchCount(controller->GetSecondaryTouchCount());
} else {
if (controller->WasSecondaryTouchReset()) {
controller->SetTouchCount(controller->GetSecondaryTouchCount());
controller->UnsetSecondaryTouchResetBool();
}
}
}
controller->SetPreviousTouchNum(pData->touchData.touchNum);
if (pData->touchData.touchNum == 1) {
state.touchpad[0].ID = controller->GetTouchCount();
state.touchpad[1].ID = 0;
} else if (pData->touchData.touchNum == 2) {
state.touchpad[0].ID = controller->GetTouchCount();
state.touchpad[1].ID = controller->GetSecondaryTouchCount();
}
} else {
state.touchpad[0].ID = 1;
state.touchpad[1].ID = 2;
}
pData->touchData.touch[0].x = state.touchpad[0].x; pData->touchData.touch[0].x = state.touchpad[0].x;
pData->touchData.touch[0].y = state.touchpad[0].y; pData->touchData.touch[0].y = state.touchpad[0].y;
pData->touchData.touch[0].id = 1; pData->touchData.touch[0].id = state.touchpad[0].ID;
pData->touchData.touch[1].x = state.touchpad[1].x; pData->touchData.touch[1].x = state.touchpad[1].x;
pData->touchData.touch[1].y = state.touchpad[1].y; pData->touchData.touch[1].y = state.touchpad[1].y;
pData->touchData.touch[1].id = 2; pData->touchData.touch[1].id = state.touchpad[1].ID;
pData->timestamp = state.time; pData->timestamp = state.time;
pData->connected = true; // isConnected; //TODO fix me proper pData->connected = true; // isConnected; //TODO fix me proper
pData->connectedCount = 1; // connectedCount; pData->connectedCount = 1; // connectedCount;

View File

@ -282,7 +282,12 @@ s32 PS4_SYSV_ABI sceVideoOutGetVblankStatus(int handle, SceVideoOutVblankStatus*
s32 PS4_SYSV_ABI sceVideoOutGetResolutionStatus(s32 handle, SceVideoOutResolutionStatus* status) { s32 PS4_SYSV_ABI sceVideoOutGetResolutionStatus(s32 handle, SceVideoOutResolutionStatus* status) {
LOG_INFO(Lib_VideoOut, "called"); LOG_INFO(Lib_VideoOut, "called");
*status = driver->GetPort(handle)->resolution; auto* port = driver->GetPort(handle);
if (!port || !port->is_open) {
return ORBIS_VIDEO_OUT_ERROR_INVALID_HANDLE;
}
*status = port->resolution;
return ORBIS_OK; return ORBIS_OK;
} }

View File

@ -0,0 +1,203 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "common/logging/log.h"
#include "core/libraries/error_codes.h"
#include "core/libraries/libs.h"
#include "core/libraries/voice/voice.h"
namespace Libraries::Voice {
s32 PS4_SYSV_ABI sceVoiceConnectIPortToOPort() {
LOG_ERROR(Lib_Voice, "(STUBBED) called");
return ORBIS_OK;
}
s32 PS4_SYSV_ABI sceVoiceCreatePort() {
LOG_ERROR(Lib_Voice, "(STUBBED) called");
return ORBIS_OK;
}
s32 PS4_SYSV_ABI sceVoiceDeletePort() {
LOG_ERROR(Lib_Voice, "(STUBBED) called");
return ORBIS_OK;
}
s32 PS4_SYSV_ABI sceVoiceDisconnectIPortFromOPort() {
LOG_ERROR(Lib_Voice, "(STUBBED) called");
return ORBIS_OK;
}
s32 PS4_SYSV_ABI sceVoiceEnd() {
LOG_ERROR(Lib_Voice, "(STUBBED) called");
return ORBIS_OK;
}
s32 PS4_SYSV_ABI sceVoiceGetBitRate(u32 port_id, u32* bitrate) {
LOG_ERROR(Lib_Voice, "(STUBBED) called");
*bitrate = 48000;
return ORBIS_OK;
}
s32 PS4_SYSV_ABI sceVoiceGetMuteFlag() {
LOG_ERROR(Lib_Voice, "(STUBBED) called");
return ORBIS_OK;
}
s32 PS4_SYSV_ABI sceVoiceGetPortAttr() {
LOG_ERROR(Lib_Voice, "(STUBBED) called");
return ORBIS_OK;
}
s32 PS4_SYSV_ABI sceVoiceGetPortInfo(u32 port_id, OrbisVoicePortInfo* info) {
LOG_ERROR(Lib_Voice, "(STUBBED) called");
info->port_type = 0;
info->state = 3;
info->byte_count = 0;
info->frame_size = 1;
info->edge_count = 0;
info->reserved = 0;
return ORBIS_OK;
}
s32 PS4_SYSV_ABI sceVoiceGetResourceInfo() {
LOG_ERROR(Lib_Voice, "(STUBBED) called");
return ORBIS_OK;
}
s32 PS4_SYSV_ABI sceVoiceGetVolume() {
LOG_ERROR(Lib_Voice, "(STUBBED) called");
return ORBIS_OK;
}
s32 PS4_SYSV_ABI sceVoiceInit() {
LOG_ERROR(Lib_Voice, "(STUBBED) called");
return ORBIS_OK;
}
s32 PS4_SYSV_ABI sceVoiceInitHQ() {
LOG_ERROR(Lib_Voice, "(STUBBED) called");
return ORBIS_OK;
}
s32 PS4_SYSV_ABI sceVoicePausePort() {
LOG_ERROR(Lib_Voice, "(STUBBED) called");
return ORBIS_OK;
}
s32 PS4_SYSV_ABI sceVoicePausePortAll() {
LOG_ERROR(Lib_Voice, "(STUBBED) called");
return ORBIS_OK;
}
s32 PS4_SYSV_ABI sceVoiceReadFromOPort() {
LOG_ERROR(Lib_Voice, "(STUBBED) called");
return ORBIS_OK;
}
s32 PS4_SYSV_ABI sceVoiceResetPort() {
LOG_ERROR(Lib_Voice, "(STUBBED) called");
return ORBIS_OK;
}
s32 PS4_SYSV_ABI sceVoiceResumePort() {
LOG_ERROR(Lib_Voice, "(STUBBED) called");
return ORBIS_OK;
}
s32 PS4_SYSV_ABI sceVoiceResumePortAll() {
LOG_ERROR(Lib_Voice, "(STUBBED) called");
return ORBIS_OK;
}
s32 PS4_SYSV_ABI sceVoiceSetBitRate() {
LOG_ERROR(Lib_Voice, "(STUBBED) called");
return ORBIS_OK;
}
s32 PS4_SYSV_ABI sceVoiceSetMuteFlag() {
LOG_ERROR(Lib_Voice, "(STUBBED) called");
return ORBIS_OK;
}
s32 PS4_SYSV_ABI sceVoiceSetMuteFlagAll() {
LOG_ERROR(Lib_Voice, "(STUBBED) called");
return ORBIS_OK;
}
s32 PS4_SYSV_ABI sceVoiceSetThreadsParams() {
LOG_ERROR(Lib_Voice, "(STUBBED) called");
return ORBIS_OK;
}
s32 PS4_SYSV_ABI sceVoiceSetVolume() {
LOG_ERROR(Lib_Voice, "(STUBBED) called");
return ORBIS_OK;
}
s32 PS4_SYSV_ABI sceVoiceStart() {
LOG_ERROR(Lib_Voice, "(STUBBED) called");
return ORBIS_OK;
}
s32 PS4_SYSV_ABI sceVoiceStop() {
LOG_ERROR(Lib_Voice, "(STUBBED) called");
return ORBIS_OK;
}
s32 PS4_SYSV_ABI sceVoiceUpdatePort() {
LOG_ERROR(Lib_Voice, "(STUBBED) called");
return ORBIS_OK;
}
s32 PS4_SYSV_ABI sceVoiceVADAdjustment() {
LOG_ERROR(Lib_Voice, "(STUBBED) called");
return ORBIS_OK;
}
s32 PS4_SYSV_ABI sceVoiceVADSetVersion() {
LOG_ERROR(Lib_Voice, "(STUBBED) called");
return ORBIS_OK;
}
s32 PS4_SYSV_ABI sceVoiceWriteToIPort() {
LOG_ERROR(Lib_Voice, "(STUBBED) called");
return ORBIS_OK;
}
void RegisterlibSceVoice(Core::Loader::SymbolsResolver* sym) {
LIB_FUNCTION("oV9GAdJ23Gw", "libSceVoice", 1, "libSceVoice", 0, 0, sceVoiceConnectIPortToOPort);
LIB_FUNCTION("nXpje5yNpaE", "libSceVoice", 1, "libSceVoice", 0, 0, sceVoiceCreatePort);
LIB_FUNCTION("b7kJI+nx2hg", "libSceVoice", 1, "libSceVoice", 0, 0, sceVoiceDeletePort);
LIB_FUNCTION("ajVj3QG2um4", "libSceVoice", 1, "libSceVoice", 0, 0,
sceVoiceDisconnectIPortFromOPort);
LIB_FUNCTION("Oo0S5PH7FIQ", "libSceVoice", 1, "libSceVoice", 0, 0, sceVoiceEnd);
LIB_FUNCTION("cJLufzou6bc", "libSceVoice", 1, "libSceVoice", 0, 0, sceVoiceGetBitRate);
LIB_FUNCTION("Pc4z1QjForU", "libSceVoice", 1, "libSceVoice", 0, 0, sceVoiceGetMuteFlag);
LIB_FUNCTION("elcxZTEfHZM", "libSceVoice", 1, "libSceVoice", 0, 0, sceVoiceGetPortAttr);
LIB_FUNCTION("CrLqDwWLoXM", "libSceVoice", 1, "libSceVoice", 0, 0, sceVoiceGetPortInfo);
LIB_FUNCTION("Z6QV6j7igvE", "libSceVoice", 1, "libSceVoice", 0, 0, sceVoiceGetResourceInfo);
LIB_FUNCTION("jjkCjneOYSs", "libSceVoice", 1, "libSceVoice", 0, 0, sceVoiceGetVolume);
LIB_FUNCTION("9TrhuGzberQ", "libSceVoice", 1, "libSceVoice", 0, 0, sceVoiceInit);
LIB_FUNCTION("IPHvnM5+g04", "libSceVoice", 1, "libSceVoice", 0, 0, sceVoiceInitHQ);
LIB_FUNCTION("x0slGBQW+wY", "libSceVoice", 1, "libSceVoice", 0, 0, sceVoicePausePort);
LIB_FUNCTION("Dinob0yMRl8", "libSceVoice", 1, "libSceVoice", 0, 0, sceVoicePausePortAll);
LIB_FUNCTION("cQ6DGsQEjV4", "libSceVoice", 1, "libSceVoice", 0, 0, sceVoiceReadFromOPort);
LIB_FUNCTION("udAxvCePkUs", "libSceVoice", 1, "libSceVoice", 0, 0, sceVoiceResetPort);
LIB_FUNCTION("gAgN+HkiEzY", "libSceVoice", 1, "libSceVoice", 0, 0, sceVoiceResumePort);
LIB_FUNCTION("jbkJFmOZ9U0", "libSceVoice", 1, "libSceVoice", 0, 0, sceVoiceResumePortAll);
LIB_FUNCTION("TexwmOHQsDg", "libSceVoice", 1, "libSceVoice", 0, 0, sceVoiceSetBitRate);
LIB_FUNCTION("gwUynkEgNFY", "libSceVoice", 1, "libSceVoice", 0, 0, sceVoiceSetMuteFlag);
LIB_FUNCTION("oUha0S-Ij9Q", "libSceVoice", 1, "libSceVoice", 0, 0, sceVoiceSetMuteFlagAll);
LIB_FUNCTION("clyKUyi3RYU", "libSceVoice", 1, "libSceVoice", 0, 0, sceVoiceSetThreadsParams);
LIB_FUNCTION("QBFoAIjJoXQ", "libSceVoice", 1, "libSceVoice", 0, 0, sceVoiceSetVolume);
LIB_FUNCTION("54phPH2LZls", "libSceVoice", 1, "libSceVoice", 0, 0, sceVoiceStart);
LIB_FUNCTION("Ao2YNSA7-Qo", "libSceVoice", 1, "libSceVoice", 0, 0, sceVoiceStop);
LIB_FUNCTION("jSZNP7xJrcw", "libSceVoice", 1, "libSceVoice", 0, 0, sceVoiceUpdatePort);
LIB_FUNCTION("hg9T73LlRiU", "libSceVoice", 1, "libSceVoice", 0, 0, sceVoiceVADAdjustment);
LIB_FUNCTION("wFeAxEeEi-8", "libSceVoice", 1, "libSceVoice", 0, 0, sceVoiceVADSetVersion);
LIB_FUNCTION("YeJl6yDlhW0", "libSceVoice", 1, "libSceVoice", 0, 0, sceVoiceWriteToIPort);
};
} // namespace Libraries::Voice

View File

@ -0,0 +1,56 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "common/types.h"
namespace Core::Loader {
class SymbolsResolver;
}
namespace Libraries::Voice {
struct OrbisVoicePortInfo {
s32 port_type;
s32 state;
u32* edge;
u32 byte_count;
u32 frame_size;
u16 edge_count;
u16 reserved;
};
s32 PS4_SYSV_ABI sceVoiceConnectIPortToOPort();
s32 PS4_SYSV_ABI sceVoiceCreatePort();
s32 PS4_SYSV_ABI sceVoiceDeletePort();
s32 PS4_SYSV_ABI sceVoiceDisconnectIPortFromOPort();
s32 PS4_SYSV_ABI sceVoiceEnd();
s32 PS4_SYSV_ABI sceVoiceGetBitRate(u32 port_id, u32* bitrate);
s32 PS4_SYSV_ABI sceVoiceGetMuteFlag();
s32 PS4_SYSV_ABI sceVoiceGetPortAttr();
s32 PS4_SYSV_ABI sceVoiceGetPortInfo(u32 port_id, OrbisVoicePortInfo* info);
s32 PS4_SYSV_ABI sceVoiceGetResourceInfo();
s32 PS4_SYSV_ABI sceVoiceGetVolume();
s32 PS4_SYSV_ABI sceVoiceInit();
s32 PS4_SYSV_ABI sceVoiceInitHQ();
s32 PS4_SYSV_ABI sceVoicePausePort();
s32 PS4_SYSV_ABI sceVoicePausePortAll();
s32 PS4_SYSV_ABI sceVoiceReadFromOPort();
s32 PS4_SYSV_ABI sceVoiceResetPort();
s32 PS4_SYSV_ABI sceVoiceResumePort();
s32 PS4_SYSV_ABI sceVoiceResumePortAll();
s32 PS4_SYSV_ABI sceVoiceSetBitRate();
s32 PS4_SYSV_ABI sceVoiceSetMuteFlag();
s32 PS4_SYSV_ABI sceVoiceSetMuteFlagAll();
s32 PS4_SYSV_ABI sceVoiceSetThreadsParams();
s32 PS4_SYSV_ABI sceVoiceSetVolume();
s32 PS4_SYSV_ABI sceVoiceStart();
s32 PS4_SYSV_ABI sceVoiceStop();
s32 PS4_SYSV_ABI sceVoiceUpdatePort();
s32 PS4_SYSV_ABI sceVoiceVADAdjustment();
s32 PS4_SYSV_ABI sceVoiceVADSetVersion();
s32 PS4_SYSV_ABI sceVoiceWriteToIPort();
void RegisterlibSceVoice(Core::Loader::SymbolsResolver* sym);
} // namespace Libraries::Voice

View File

@ -51,7 +51,7 @@ void ZlibTaskThread(const std::stop_token& stop) {
if (!task_queue_cv.wait(lock, stop, [&] { return !task_queue.empty(); })) { if (!task_queue_cv.wait(lock, stop, [&] { return !task_queue.empty(); })) {
break; break;
} }
task = task_queue.back(); task = task_queue.front();
task_queue.pop(); task_queue.pop();
} }
@ -136,7 +136,7 @@ s32 PS4_SYSV_ABI sceZlibWaitForDone(u64* request_id, const u32* timeout) {
} else { } else {
done_queue_cv.wait(lock, pred); done_queue_cv.wait(lock, pred);
} }
*request_id = done_queue.back(); *request_id = done_queue.front();
done_queue.pop(); done_queue.pop();
} }
return ORBIS_OK; return ORBIS_OK;

View File

@ -117,6 +117,18 @@ void Linker::Execute(const std::vector<std::string> args) {
Common::SetCurrentThreadName("GAME_MainThread"); Common::SetCurrentThreadName("GAME_MainThread");
LoadSharedLibraries(); LoadSharedLibraries();
// Simulate libSceGnmDriver initialization, which maps a chunk of direct memory.
// Some games fail without accurately emulating this behavior.
s64 phys_addr{};
s32 result = Libraries::Kernel::sceKernelAllocateDirectMemory(
0, Libraries::Kernel::sceKernelGetDirectMemorySize(), 0x10000, 0x10000, 3, &phys_addr);
if (result == 0) {
void* addr{reinterpret_cast<void*>(0xfe0000000)};
result = Libraries::Kernel::sceKernelMapNamedDirectMemory(
&addr, 0x10000, 0x13, 0, phys_addr, 0x10000, "SceGnmDriver");
}
ASSERT_MSG(result == 0, "Unable to emulate libSceGnmDriver initialization");
// Start main module. // Start main module.
EntryParams params{}; EntryParams params{};
params.argc = 1; params.argc = 1;
@ -320,21 +332,22 @@ bool Linker::Resolve(const std::string& name, Loader::SymbolType sym_type, Modul
sr.type = sym_type; sr.type = sym_type;
const auto* record = m_hle_symbols.FindSymbol(sr); const auto* record = m_hle_symbols.FindSymbol(sr);
if (!record) {
// Check if it an export function
const auto* p = FindExportedModule(*module, *library);
if (p && p->export_sym.GetSize() > 0) {
record = p->export_sym.FindSymbol(sr);
}
}
if (record) { if (record) {
*return_info = *record; *return_info = *record;
Core::Devtools::Widget::ModuleList::AddModule(sr.library); Core::Devtools::Widget::ModuleList::AddModule(sr.library);
return true; return true;
} }
// Check if it an export function
const auto* p = FindExportedModule(*module, *library);
if (p && p->export_sym.GetSize() > 0) {
record = p->export_sym.FindSymbol(sr);
if (record) {
*return_info = *record;
return true;
}
}
const auto aeronid = AeroLib::FindByNid(sr.name.c_str()); const auto aeronid = AeroLib::FindByNid(sr.name.c_str());
if (aeronid) { if (aeronid) {
return_info->name = aeronid->name; return_info->name = aeronid->name;

View File

@ -5,6 +5,7 @@
#include "common/assert.h" #include "common/assert.h"
#include "common/config.h" #include "common/config.h"
#include "common/debug.h" #include "common/debug.h"
#include "core/file_sys/fs.h"
#include "core/libraries/kernel/memory.h" #include "core/libraries/kernel/memory.h"
#include "core/libraries/kernel/orbis_error.h" #include "core/libraries/kernel/orbis_error.h"
#include "core/libraries/kernel/process.h" #include "core/libraries/kernel/process.h"
@ -67,7 +68,7 @@ void MemoryManager::SetupMemoryRegions(u64 flexible_size, bool use_extended_mem1
} }
u64 MemoryManager::ClampRangeSize(VAddr virtual_addr, u64 size) { u64 MemoryManager::ClampRangeSize(VAddr virtual_addr, u64 size) {
static constexpr u64 MinSizeToClamp = 512_MB; static constexpr u64 MinSizeToClamp = 2_MB;
// Dont bother with clamping if the size is small so we dont pay a map lookup on every buffer. // Dont bother with clamping if the size is small so we dont pay a map lookup on every buffer.
if (size < MinSizeToClamp) { if (size < MinSizeToClamp) {
return size; return size;
@ -94,6 +95,46 @@ u64 MemoryManager::ClampRangeSize(VAddr virtual_addr, u64 size) {
return clamped_size; return clamped_size;
} }
void MemoryManager::SetPrtArea(u32 id, VAddr address, u64 size) {
PrtArea& area = prt_areas[id];
if (area.mapped) {
rasterizer->UnmapMemory(area.start, area.end - area.start);
}
area.start = address;
area.end = address + size;
area.mapped = true;
// Pretend the entire PRT area is mapped to avoid GPU tracking errors.
// The caches will use CopySparseMemory to fetch data which avoids unmapped areas.
rasterizer->MapMemory(address, size);
}
void MemoryManager::CopySparseMemory(VAddr virtual_addr, u8* dest, u64 size) {
const bool is_sparse = std::ranges::any_of(
prt_areas, [&](const PrtArea& area) { return area.Overlaps(virtual_addr, size); });
if (!is_sparse) {
std::memcpy(dest, std::bit_cast<const u8*>(virtual_addr), size);
return;
}
auto vma = FindVMA(virtual_addr);
ASSERT_MSG(vma->second.Contains(virtual_addr, 0),
"Attempted to access invalid GPU address {:#x}", virtual_addr);
while (size) {
u64 copy_size = std::min<u64>(vma->second.size - (virtual_addr - vma->first), size);
if (vma->second.IsFree()) {
std::memset(dest, 0, copy_size);
} else {
std::memcpy(dest, std::bit_cast<const u8*>(virtual_addr), copy_size);
}
size -= copy_size;
virtual_addr += copy_size;
dest += copy_size;
++vma;
}
}
bool MemoryManager::TryWriteBacking(void* address, const void* data, u32 num_bytes) { bool MemoryManager::TryWriteBacking(void* address, const void* data, u32 num_bytes) {
const VAddr virtual_addr = std::bit_cast<VAddr>(address); const VAddr virtual_addr = std::bit_cast<VAddr>(address);
const auto& vma = FindVMA(virtual_addr)->second; const auto& vma = FindVMA(virtual_addr)->second;
@ -214,90 +255,6 @@ void MemoryManager::Free(PAddr phys_addr, size_t size) {
MergeAdjacent(dmem_map, dmem_area); MergeAdjacent(dmem_map, dmem_area);
} }
int MemoryManager::PoolReserve(void** out_addr, VAddr virtual_addr, size_t size,
MemoryMapFlags flags, u64 alignment) {
std::scoped_lock lk{mutex};
alignment = alignment > 0 ? alignment : 2_MB;
VAddr min_address = Common::AlignUp(impl.SystemManagedVirtualBase(), alignment);
VAddr mapped_addr = Common::AlignUp(virtual_addr, alignment);
// Fixed mapping means the virtual address must exactly match the provided one.
if (True(flags & MemoryMapFlags::Fixed)) {
// Make sure we're mapping to a valid address
mapped_addr = mapped_addr > min_address ? mapped_addr : min_address;
auto vma = FindVMA(mapped_addr)->second;
size_t remaining_size = vma.base + vma.size - mapped_addr;
// If the VMA is mapped or there's not enough space, unmap the region first.
if (vma.IsMapped() || remaining_size < size) {
UnmapMemoryImpl(mapped_addr, size);
vma = FindVMA(mapped_addr)->second;
}
}
if (False(flags & MemoryMapFlags::Fixed)) {
// When MemoryMapFlags::Fixed is not specified, and mapped_addr is 0,
// search from address 0x200000000 instead.
mapped_addr = mapped_addr == 0 ? 0x200000000 : mapped_addr;
mapped_addr = SearchFree(mapped_addr, size, alignment);
if (mapped_addr == -1) {
// No suitable memory areas to map to
return ORBIS_KERNEL_ERROR_ENOMEM;
}
}
// Add virtual memory area
const auto new_vma_handle = CarveVMA(mapped_addr, size);
auto& new_vma = new_vma_handle->second;
new_vma.disallow_merge = True(flags & MemoryMapFlags::NoCoalesce);
new_vma.prot = MemoryProt::NoAccess;
new_vma.name = "anon";
new_vma.type = VMAType::PoolReserved;
*out_addr = std::bit_cast<void*>(mapped_addr);
return ORBIS_OK;
}
int MemoryManager::Reserve(void** out_addr, VAddr virtual_addr, size_t size, MemoryMapFlags flags,
u64 alignment) {
std::scoped_lock lk{mutex};
virtual_addr = (virtual_addr == 0) ? impl.SystemManagedVirtualBase() : virtual_addr;
alignment = alignment > 0 ? alignment : 16_KB;
VAddr mapped_addr = alignment > 0 ? Common::AlignUp(virtual_addr, alignment) : virtual_addr;
// Fixed mapping means the virtual address must exactly match the provided one.
if (True(flags & MemoryMapFlags::Fixed)) {
auto vma = FindVMA(mapped_addr)->second;
size_t remaining_size = vma.base + vma.size - mapped_addr;
// If the VMA is mapped or there's not enough space, unmap the region first.
if (vma.IsMapped() || remaining_size < size) {
UnmapMemoryImpl(mapped_addr, size);
vma = FindVMA(mapped_addr)->second;
}
}
// Find the first free area starting with provided virtual address.
if (False(flags & MemoryMapFlags::Fixed)) {
mapped_addr = SearchFree(mapped_addr, size, alignment);
if (mapped_addr == -1) {
// No suitable memory areas to map to
return ORBIS_KERNEL_ERROR_ENOMEM;
}
}
// Add virtual memory area
const auto new_vma_handle = CarveVMA(mapped_addr, size);
auto& new_vma = new_vma_handle->second;
new_vma.disallow_merge = True(flags & MemoryMapFlags::NoCoalesce);
new_vma.prot = MemoryProt::NoAccess;
new_vma.name = "anon";
new_vma.type = VMAType::Reserved;
MergeAdjacent(vma_map, new_vma_handle);
*out_addr = std::bit_cast<void*>(mapped_addr);
return ORBIS_OK;
}
int MemoryManager::PoolCommit(VAddr virtual_addr, size_t size, MemoryProt prot) { int MemoryManager::PoolCommit(VAddr virtual_addr, size_t size, MemoryProt prot) {
std::scoped_lock lk{mutex}; std::scoped_lock lk{mutex};
@ -351,7 +308,7 @@ int MemoryManager::PoolCommit(VAddr virtual_addr, size_t size, MemoryProt prot)
return ORBIS_OK; return ORBIS_OK;
} }
int MemoryManager::MapMemory(void** out_addr, VAddr virtual_addr, size_t size, MemoryProt prot, s32 MemoryManager::MapMemory(void** out_addr, VAddr virtual_addr, u64 size, MemoryProt prot,
MemoryMapFlags flags, VMAType type, std::string_view name, MemoryMapFlags flags, VMAType type, std::string_view name,
bool is_exec, PAddr phys_addr, u64 alignment) { bool is_exec, PAddr phys_addr, u64 alignment) {
std::scoped_lock lk{mutex}; std::scoped_lock lk{mutex};
@ -366,17 +323,18 @@ int MemoryManager::MapMemory(void** out_addr, VAddr virtual_addr, size_t size, M
VAddr mapped_addr = (virtual_addr == 0) ? impl.SystemManagedVirtualBase() : virtual_addr; VAddr mapped_addr = (virtual_addr == 0) ? impl.SystemManagedVirtualBase() : virtual_addr;
// Fixed mapping means the virtual address must exactly match the provided one. // Fixed mapping means the virtual address must exactly match the provided one.
if (True(flags & MemoryMapFlags::Fixed)) { // On a PS4, the Fixed flag is ignored if address 0 is provided.
if (True(flags & MemoryMapFlags::Fixed) && virtual_addr != 0) {
auto vma = FindVMA(mapped_addr)->second; auto vma = FindVMA(mapped_addr)->second;
size_t remaining_size = vma.base + vma.size - mapped_addr;
// There's a possible edge case where we're mapping to a partially reserved range. // There's a possible edge case where we're mapping to a partially reserved range.
// To account for this, unmap any reserved areas within this mapping range first. // To account for this, unmap any reserved areas within this mapping range first.
auto unmap_addr = mapped_addr; auto unmap_addr = mapped_addr;
auto unmap_size = size; auto unmap_size = size;
// If flag NoOverwrite is provided, don't overwrite mapped VMAs. // If flag NoOverwrite is provided, don't overwrite mapped VMAs.
// When it isn't provided, VMAs can be overwritten regardless of if they're mapped. // When it isn't provided, VMAs can be overwritten regardless of if they're mapped.
while ((False(flags & MemoryMapFlags::NoOverwrite) || !vma.IsMapped()) && while ((False(flags & MemoryMapFlags::NoOverwrite) || !vma.IsMapped()) &&
unmap_addr < mapped_addr + size && remaining_size < size) { unmap_addr < mapped_addr + size) {
auto unmapped = UnmapBytesFromEntry(unmap_addr, vma, unmap_size); auto unmapped = UnmapBytesFromEntry(unmap_addr, vma, unmap_size);
unmap_addr += unmapped; unmap_addr += unmapped;
unmap_size -= unmapped; unmap_size -= unmapped;
@ -384,51 +342,65 @@ int MemoryManager::MapMemory(void** out_addr, VAddr virtual_addr, size_t size, M
} }
vma = FindVMA(mapped_addr)->second; vma = FindVMA(mapped_addr)->second;
remaining_size = vma.base + vma.size - mapped_addr; auto remaining_size = vma.base + vma.size - mapped_addr;
if (vma.IsMapped() || remaining_size < size) { if (vma.IsMapped() || remaining_size < size) {
LOG_ERROR(Kernel_Vmm, "Unable to map {:#x} bytes at address {:#x}", size, mapped_addr); LOG_ERROR(Kernel_Vmm, "Unable to map {:#x} bytes at address {:#x}", size, mapped_addr);
return ORBIS_KERNEL_ERROR_ENOMEM; return ORBIS_KERNEL_ERROR_ENOMEM;
} }
} } else {
// When MemoryMapFlags::Fixed is not specified, and mapped_addr is 0,
// Find the first free area starting with provided virtual address. // search from address 0x200000000 instead.
if (False(flags & MemoryMapFlags::Fixed)) {
// Provided address needs to be aligned before we can map.
alignment = alignment > 0 ? alignment : 16_KB; alignment = alignment > 0 ? alignment : 16_KB;
mapped_addr = SearchFree(Common::AlignUp(mapped_addr, alignment), size, alignment); mapped_addr = virtual_addr == 0 ? 0x200000000 : mapped_addr;
mapped_addr = SearchFree(mapped_addr, size, alignment);
if (mapped_addr == -1) { if (mapped_addr == -1) {
// No suitable memory areas to map to // No suitable memory areas to map to
return ORBIS_KERNEL_ERROR_ENOMEM; return ORBIS_KERNEL_ERROR_ENOMEM;
} }
} }
// Perform the mapping. // Create a memory area representing this mapping.
*out_addr = impl.Map(mapped_addr, size, alignment, phys_addr, is_exec); const auto new_vma_handle = CarveVMA(mapped_addr, size);
TRACK_ALLOC(*out_addr, size, "VMEM"); auto& new_vma = new_vma_handle->second;
auto& new_vma = CarveVMA(mapped_addr, size)->second; // If type is Flexible, we need to track how much flexible memory is used here.
new_vma.disallow_merge = True(flags & MemoryMapFlags::NoCoalesce);
new_vma.prot = prot;
new_vma.name = name;
new_vma.type = type;
new_vma.is_exec = is_exec;
if (type == VMAType::Direct) {
new_vma.phys_base = phys_addr;
}
if (type == VMAType::Flexible) { if (type == VMAType::Flexible) {
flexible_usage += size; flexible_usage += size;
} }
if (IsValidGpuMapping(mapped_addr, size)) { new_vma.disallow_merge = True(flags & MemoryMapFlags::NoCoalesce);
rasterizer->MapMemory(mapped_addr, size); new_vma.prot = prot;
new_vma.name = name;
new_vma.type = type;
new_vma.phys_base = phys_addr == -1 ? 0 : phys_addr;
new_vma.is_exec = is_exec;
if (type == VMAType::Reserved) {
// Technically this should be done for direct and flexible mappings too,
// But some Windows-specific limitations make that hard to accomplish.
MergeAdjacent(vma_map, new_vma_handle);
} }
if (type == VMAType::Reserved || type == VMAType::PoolReserved) {
// For Reserved/PoolReserved mappings, we don't perform any address space allocations.
// Just set out_addr to mapped_addr instead.
*out_addr = std::bit_cast<void*>(mapped_addr);
} else {
// If this is not a reservation, then map to GPU and address space
if (IsValidGpuMapping(mapped_addr, size)) {
rasterizer->MapMemory(mapped_addr, size);
}
*out_addr = impl.Map(mapped_addr, size, alignment, phys_addr, is_exec);
}
TRACK_ALLOC(*out_addr, size, "VMEM");
return ORBIS_OK; return ORBIS_OK;
} }
int MemoryManager::MapFile(void** out_addr, VAddr virtual_addr, size_t size, MemoryProt prot, s32 MemoryManager::MapFile(void** out_addr, VAddr virtual_addr, u64 size, MemoryProt prot,
MemoryMapFlags flags, uintptr_t fd, size_t offset) { MemoryMapFlags flags, s32 fd, s64 phys_addr) {
auto* h = Common::Singleton<Core::FileSys::HandleTable>::Instance();
VAddr mapped_addr = (virtual_addr == 0) ? impl.SystemManagedVirtualBase() : virtual_addr; VAddr mapped_addr = (virtual_addr == 0) ? impl.SystemManagedVirtualBase() : virtual_addr;
const size_t size_aligned = Common::AlignUp(size, 16_KB); const size_t size_aligned = Common::AlignUp(size, 16_KB);
@ -449,8 +421,19 @@ int MemoryManager::MapFile(void** out_addr, VAddr virtual_addr, size_t size, Mem
vma.base, vma.base + vma.size, virtual_addr, virtual_addr + size); vma.base, vma.base + vma.size, virtual_addr, virtual_addr + size);
} }
// Map the file. // Get the file to map
impl.MapFile(mapped_addr, size_aligned, offset, std::bit_cast<u32>(prot), fd); auto file = h->GetFile(fd);
if (file == nullptr) {
return ORBIS_KERNEL_ERROR_EBADF;
}
const auto handle = file->f.GetFileMapping();
impl.MapFile(mapped_addr, size_aligned, phys_addr, std::bit_cast<u32>(prot), handle);
if (prot >= MemoryProt::GpuRead) {
ASSERT_MSG(false, "Files cannot be mapped to GPU memory");
}
// Add virtual memory area // Add virtual memory area
auto& new_vma = CarveVMA(mapped_addr, size_aligned)->second; auto& new_vma = CarveVMA(mapped_addr, size_aligned)->second;
@ -485,14 +468,15 @@ s32 MemoryManager::PoolDecommit(VAddr virtual_addr, size_t size) {
} }
if (type == VMAType::Pooled) { if (type == VMAType::Pooled) {
// We always map PoolCommitted memory to GPU, so unmap when decomitting.
if (IsValidGpuMapping(virtual_addr, size)) {
rasterizer->UnmapMemory(virtual_addr, size);
}
// Track how much pooled memory is decommitted // Track how much pooled memory is decommitted
pool_budget += size; pool_budget += size;
} }
if (IsValidGpuMapping(virtual_addr, size)) {
rasterizer->UnmapMemory(virtual_addr, size);
}
// Mark region as free and attempt to coalesce it with neighbours. // Mark region as free and attempt to coalesce it with neighbours.
const auto new_it = CarveVMA(virtual_addr, size); const auto new_it = CarveVMA(virtual_addr, size);
auto& vma = new_it->second; auto& vma = new_it->second;
@ -528,18 +512,16 @@ u64 MemoryManager::UnmapBytesFromEntry(VAddr virtual_addr, VirtualMemoryArea vma
const auto adjusted_size = const auto adjusted_size =
vma_base_size - start_in_vma < size ? vma_base_size - start_in_vma : size; vma_base_size - start_in_vma < size ? vma_base_size - start_in_vma : size;
const bool has_backing = type == VMAType::Direct || type == VMAType::File; const bool has_backing = type == VMAType::Direct || type == VMAType::File;
const auto prot = vma_base.prot;
if (type == VMAType::Free) { if (type == VMAType::Free) {
return adjusted_size; return adjusted_size;
} }
if (type == VMAType::Flexible) { if (type == VMAType::Flexible) {
flexible_usage -= adjusted_size; flexible_usage -= adjusted_size;
} }
if (IsValidGpuMapping(virtual_addr, adjusted_size)) {
rasterizer->UnmapMemory(virtual_addr, adjusted_size);
}
// Mark region as free and attempt to coalesce it with neighbours. // Mark region as free and attempt to coalesce it with neighbours.
const auto new_it = CarveVMA(virtual_addr, adjusted_size); const auto new_it = CarveVMA(virtual_addr, adjusted_size);
auto& vma = new_it->second; auto& vma = new_it->second;
@ -552,6 +534,11 @@ u64 MemoryManager::UnmapBytesFromEntry(VAddr virtual_addr, VirtualMemoryArea vma
auto& post_merge_vma = post_merge_it->second; auto& post_merge_vma = post_merge_it->second;
bool readonly_file = post_merge_vma.prot == MemoryProt::CpuRead && type == VMAType::File; bool readonly_file = post_merge_vma.prot == MemoryProt::CpuRead && type == VMAType::File;
if (type != VMAType::Reserved && type != VMAType::PoolReserved) { if (type != VMAType::Reserved && type != VMAType::PoolReserved) {
// If this mapping has GPU access, unmap from GPU.
if (IsValidGpuMapping(virtual_addr, size)) {
rasterizer->UnmapMemory(virtual_addr, size);
}
// Unmap the memory region. // Unmap the memory region.
impl.Unmap(vma_base_addr, vma_base_size, start_in_vma, start_in_vma + adjusted_size, impl.Unmap(vma_base_addr, vma_base_size, start_in_vma, start_in_vma + adjusted_size,
phys_base, is_exec, has_backing, readonly_file); phys_base, is_exec, has_backing, readonly_file);
@ -605,20 +592,8 @@ s64 MemoryManager::ProtectBytes(VAddr addr, VirtualMemoryArea vma_base, size_t s
vma_base.size - start_in_vma < size ? vma_base.size - start_in_vma : size; vma_base.size - start_in_vma < size ? vma_base.size - start_in_vma : size;
if (vma_base.type == VMAType::Free) { if (vma_base.type == VMAType::Free) {
LOG_ERROR(Kernel_Vmm, "Cannot change protection on free memory region"); // On PS4, protecting freed memory does nothing.
return ORBIS_KERNEL_ERROR_EINVAL; return adjusted_size;
}
// Validate protection flags
constexpr static MemoryProt valid_flags = MemoryProt::NoAccess | MemoryProt::CpuRead |
MemoryProt::CpuReadWrite | MemoryProt::GpuRead |
MemoryProt::GpuWrite | MemoryProt::GpuReadWrite;
MemoryProt invalid_flags = prot & ~valid_flags;
if (u32(invalid_flags) != 0 && u32(invalid_flags) != u32(MemoryProt::NoAccess)) {
LOG_ERROR(Kernel_Vmm, "Invalid protection flags: prot = {:#x}, invalid flags = {:#x}",
u32(prot), u32(invalid_flags));
return ORBIS_KERNEL_ERROR_EINVAL;
} }
// Change protection // Change protection
@ -650,11 +625,25 @@ s64 MemoryManager::ProtectBytes(VAddr addr, VirtualMemoryArea vma_base, size_t s
s32 MemoryManager::Protect(VAddr addr, size_t size, MemoryProt prot) { s32 MemoryManager::Protect(VAddr addr, size_t size, MemoryProt prot) {
std::scoped_lock lk{mutex}; std::scoped_lock lk{mutex};
s64 protected_bytes = 0;
// Validate protection flags
constexpr static MemoryProt valid_flags = MemoryProt::NoAccess | MemoryProt::CpuRead |
MemoryProt::CpuReadWrite | MemoryProt::GpuRead |
MemoryProt::GpuWrite | MemoryProt::GpuReadWrite;
MemoryProt invalid_flags = prot & ~valid_flags;
if (invalid_flags != MemoryProt::NoAccess) {
LOG_ERROR(Kernel_Vmm, "Invalid protection flags");
return ORBIS_KERNEL_ERROR_EINVAL;
}
// Align addr and size to the nearest page boundary.
auto aligned_addr = Common::AlignDown(addr, 16_KB); auto aligned_addr = Common::AlignDown(addr, 16_KB);
auto aligned_size = Common::AlignUp(size + addr - aligned_addr, 16_KB); auto aligned_size = Common::AlignUp(size + addr - aligned_addr, 16_KB);
do {
// Protect all VMAs between aligned_addr and aligned_addr + aligned_size.
s64 protected_bytes = 0;
while (protected_bytes < aligned_size) {
auto it = FindVMA(aligned_addr + protected_bytes); auto it = FindVMA(aligned_addr + protected_bytes);
auto& vma_base = it->second; auto& vma_base = it->second;
ASSERT_MSG(vma_base.Contains(addr + protected_bytes, 0), "Address {:#x} is out of bounds", ASSERT_MSG(vma_base.Contains(addr + protected_bytes, 0), "Address {:#x} is out of bounds",
@ -667,7 +656,7 @@ s32 MemoryManager::Protect(VAddr addr, size_t size, MemoryProt prot) {
return result; return result;
} }
protected_bytes += result; protected_bytes += result;
} while (protected_bytes < aligned_size); }
return ORBIS_OK; return ORBIS_OK;
} }
@ -798,12 +787,31 @@ s32 MemoryManager::SetDirectMemoryType(s64 phys_addr, s32 memory_type) {
return ORBIS_OK; return ORBIS_OK;
} }
void MemoryManager::NameVirtualRange(VAddr virtual_addr, size_t size, std::string_view name) { void MemoryManager::NameVirtualRange(VAddr virtual_addr, u64 size, std::string_view name) {
auto it = FindVMA(virtual_addr); // Sizes are aligned up to the nearest 16_KB
auto aligned_size = Common::AlignUp(size, 16_KB);
// Addresses are aligned down to the nearest 16_KB
auto aligned_addr = Common::AlignDown(virtual_addr, 16_KB);
ASSERT_MSG(it->second.Contains(virtual_addr, size), auto it = FindVMA(aligned_addr);
"Range provided is not fully contained in vma"); s64 remaining_size = aligned_size;
it->second.name = name; auto current_addr = aligned_addr;
while (remaining_size > 0) {
// Nothing needs to be done to free VMAs
if (!it->second.IsFree()) {
if (remaining_size < it->second.size) {
// We should split VMAs here, but this could cause trouble for Windows.
// Instead log a warning and name the whole VMA.
// it = CarveVMA(current_addr, remaining_size);
LOG_WARNING(Kernel_Vmm, "Trying to partially name a range");
}
auto& vma = it->second;
vma.name = name;
}
remaining_size -= it->second.size;
current_addr += it->second.size;
it = FindVMA(current_addr);
}
} }
void MemoryManager::InvalidateMemory(const VAddr addr, const u64 size) const { void MemoryManager::InvalidateMemory(const VAddr addr, const u64 size) const {
@ -824,6 +832,8 @@ VAddr MemoryManager::SearchFree(VAddr virtual_addr, size_t size, u32 alignment)
ASSERT_MSG(virtual_addr <= max_search_address, "Input address {:#x} is out of bounds", ASSERT_MSG(virtual_addr <= max_search_address, "Input address {:#x} is out of bounds",
virtual_addr); virtual_addr);
// Align up the virtual_addr first.
virtual_addr = Common::AlignUp(virtual_addr, alignment);
auto it = FindVMA(virtual_addr); auto it = FindVMA(virtual_addr);
// If the VMA is free and contains the requested mapping we are done. // If the VMA is free and contains the requested mapping we are done.

View File

@ -172,6 +172,10 @@ public:
u64 ClampRangeSize(VAddr virtual_addr, u64 size); u64 ClampRangeSize(VAddr virtual_addr, u64 size);
void SetPrtArea(u32 id, VAddr address, u64 size);
void CopySparseMemory(VAddr source, u8* dest, u64 size);
bool TryWriteBacking(void* address, const void* data, u32 num_bytes); bool TryWriteBacking(void* address, const void* data, u32 num_bytes);
void SetupMemoryRegions(u64 flexible_size, bool use_extended_mem1, bool use_extended_mem2); void SetupMemoryRegions(u64 flexible_size, bool use_extended_mem1, bool use_extended_mem2);
@ -183,20 +187,14 @@ public:
void Free(PAddr phys_addr, size_t size); void Free(PAddr phys_addr, size_t size);
int PoolReserve(void** out_addr, VAddr virtual_addr, size_t size, MemoryMapFlags flags,
u64 alignment = 0);
int Reserve(void** out_addr, VAddr virtual_addr, size_t size, MemoryMapFlags flags,
u64 alignment = 0);
int PoolCommit(VAddr virtual_addr, size_t size, MemoryProt prot); int PoolCommit(VAddr virtual_addr, size_t size, MemoryProt prot);
int MapMemory(void** out_addr, VAddr virtual_addr, size_t size, MemoryProt prot, s32 MapMemory(void** out_addr, VAddr virtual_addr, u64 size, MemoryProt prot,
MemoryMapFlags flags, VMAType type, std::string_view name = "anon", MemoryMapFlags flags, VMAType type, std::string_view name = "anon",
bool is_exec = false, PAddr phys_addr = -1, u64 alignment = 0); bool is_exec = false, PAddr phys_addr = -1, u64 alignment = 0);
int MapFile(void** out_addr, VAddr virtual_addr, size_t size, MemoryProt prot, s32 MapFile(void** out_addr, VAddr virtual_addr, u64 size, MemoryProt prot,
MemoryMapFlags flags, uintptr_t fd, size_t offset); MemoryMapFlags flags, s32 fd, s64 phys_addr);
s32 PoolDecommit(VAddr virtual_addr, size_t size); s32 PoolDecommit(VAddr virtual_addr, size_t size);
@ -221,7 +219,7 @@ public:
s32 SetDirectMemoryType(s64 phys_addr, s32 memory_type); s32 SetDirectMemoryType(s64 phys_addr, s32 memory_type);
void NameVirtualRange(VAddr virtual_addr, size_t size, std::string_view name); void NameVirtualRange(VAddr virtual_addr, u64 size, std::string_view name);
void InvalidateMemory(VAddr addr, u64 size) const; void InvalidateMemory(VAddr addr, u64 size) const;
@ -281,6 +279,18 @@ private:
size_t pool_budget{}; size_t pool_budget{};
Vulkan::Rasterizer* rasterizer{}; Vulkan::Rasterizer* rasterizer{};
struct PrtArea {
VAddr start;
VAddr end;
bool mapped;
bool Overlaps(VAddr test_address, u64 test_size) const {
const VAddr overlap_end = test_address + test_size;
return start < overlap_end && test_address < end;
}
};
std::array<PrtArea, 3> prt_areas{};
friend class ::Core::Devtools::Widget::MemoryMapViewer; friend class ::Core::Devtools::Widget::MemoryMapViewer;
}; };

View File

@ -1,6 +1,7 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project // SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
#include <filesystem>
#include <set> #include <set>
#include <fmt/core.h> #include <fmt/core.h>
@ -62,8 +63,13 @@ Emulator::~Emulator() {
Config::saveMainWindow(config_dir / "config.toml"); Config::saveMainWindow(config_dir / "config.toml");
} }
void Emulator::Run(const std::filesystem::path& file, const std::vector<std::string> args) { void Emulator::Run(std::filesystem::path file, const std::vector<std::string> args) {
if (std::filesystem::is_directory(file)) {
file /= "eboot.bin";
}
const auto eboot_name = file.filename().string(); const auto eboot_name = file.filename().string();
auto game_folder = file.parent_path(); auto game_folder = file.parent_path();
if (const auto game_folder_name = game_folder.filename().string(); if (const auto game_folder_name = game_folder.filename().string();
game_folder_name.ends_with("-UPDATE") || game_folder_name.ends_with("-patch")) { game_folder_name.ends_with("-UPDATE") || game_folder_name.ends_with("-patch")) {
@ -114,6 +120,11 @@ void Emulator::Run(const std::filesystem::path& file, const std::vector<std::str
Common::Log::Initialize(); Common::Log::Initialize();
} }
Common::Log::Start(); Common::Log::Start();
if (!std::filesystem::exists(file)) {
LOG_CRITICAL(Loader, "eboot.bin does not exist: {}",
std::filesystem::absolute(file).string());
std::quick_exit(0);
}
LOG_INFO(Loader, "Starting shadps4 emulator v{} ", Common::g_version); LOG_INFO(Loader, "Starting shadps4 emulator v{} ", Common::g_version);
LOG_INFO(Loader, "Revision {}", Common::g_scm_rev); LOG_INFO(Loader, "Revision {}", Common::g_scm_rev);
@ -194,15 +205,7 @@ void Emulator::Run(const std::filesystem::path& file, const std::vector<std::str
std::string game_title = fmt::format("{} - {} <{}>", id, title, app_version); std::string game_title = fmt::format("{} - {} <{}>", id, title, app_version);
std::string window_title = ""; std::string window_title = "";
std::string remote_url(Common::g_scm_remote_url); std::string remote_url(Common::g_scm_remote_url);
std::string remote_host; std::string remote_host = Common::GetRemoteNameFromLink();
try {
if (*remote_url.rbegin() == '/') {
remote_url.pop_back();
}
remote_host = remote_url.substr(19, remote_url.rfind('/') - 19);
} catch (...) {
remote_host = "unknown";
}
if (Common::g_is_release) { if (Common::g_is_release) {
if (remote_host == "shadps4-emu" || remote_url.length() == 0) { if (remote_host == "shadps4-emu" || remote_url.length() == 0) {
window_title = fmt::format("shadPS4 v{} | {}", Common::g_version, game_title); window_title = fmt::format("shadPS4 v{} | {}", Common::g_version, game_title);
@ -258,7 +261,11 @@ void Emulator::Run(const std::filesystem::path& file, const std::vector<std::str
// Load the module with the linker // Load the module with the linker
const auto eboot_path = mnt->GetHostPath("/app0/" + eboot_name); const auto eboot_path = mnt->GetHostPath("/app0/" + eboot_name);
linker->LoadModule(eboot_path); if (linker->LoadModule(eboot_path) == -1) {
LOG_CRITICAL(Loader, "Failed to load game's eboot.bin: {}",
std::filesystem::absolute(eboot_path).string());
std::quick_exit(0);
}
// check if we have system modules to load // check if we have system modules to load
LoadSystemModules(game_info.game_serial); LoadSystemModules(game_info.game_serial);

View File

@ -25,7 +25,7 @@ public:
Emulator(); Emulator();
~Emulator(); ~Emulator();
void Run(const std::filesystem::path& file, const std::vector<std::string> args = {}); void Run(std::filesystem::path file, const std::vector<std::string> args = {});
void UpdatePlayTime(const std::string& serial); void UpdatePlayTime(const std::string& serial);
private: private:

View File

@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project // SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
#include <SDL3/SDL.h> #include <SDL3/SDL.h>
@ -165,69 +165,37 @@ void GameController::Acceleration(int id, const float acceleration[3]) {
AddState(state); AddState(state);
} }
// Stolen from
// https://github.com/xioTechnologies/Open-Source-AHRS-With-x-IMU/blob/master/x-IMU%20IMU%20and%20AHRS%20Algorithms/x-IMU%20IMU%20and%20AHRS%20Algorithms/AHRS/MahonyAHRS.cs
float eInt[3] = {0.0f, 0.0f, 0.0f}; // Integral error terms
const float Kp = 50.0f; // Proportional gain
const float Ki = 1.0f; // Integral gain
Libraries::Pad::OrbisFQuaternion o = {1, 0, 0, 0};
void GameController::CalculateOrientation(Libraries::Pad::OrbisFVector3& acceleration, void GameController::CalculateOrientation(Libraries::Pad::OrbisFVector3& acceleration,
Libraries::Pad::OrbisFVector3& angularVelocity, Libraries::Pad::OrbisFVector3& angularVelocity,
float deltaTime, float deltaTime,
Libraries::Pad::OrbisFQuaternion& lastOrientation,
Libraries::Pad::OrbisFQuaternion& orientation) { Libraries::Pad::OrbisFQuaternion& orientation) {
float ax = acceleration.x, ay = acceleration.y, az = acceleration.z; Libraries::Pad::OrbisFQuaternion q = lastOrientation;
float gx = angularVelocity.x, gy = angularVelocity.y, gz = angularVelocity.z; Libraries::Pad::OrbisFQuaternion ω = {angularVelocity.x, angularVelocity.y, angularVelocity.z,
0.0f};
float q1 = o.w, q2 = o.x, q3 = o.y, q4 = o.z; Libraries::Pad::OrbisFQuaternion = {q.w * ω.x + q.x * ω.w + q.y * ω.z - q.z * ω.y,
q.w * ω.y + q.y * ω.w + q.z * ω.x - q.x * ω.z,
q.w * ω.z + q.z * ω.w + q.x * ω.y - q.y * ω.x,
q.w * ω.w - q.x * ω.x - q.y * ω.y - q.z * ω.z};
// Normalize accelerometer measurement Libraries::Pad::OrbisFQuaternion qDot = {0.5f * .x, 0.5f * .y, 0.5f * .z, 0.5f * .w};
float norm = std::sqrt(ax * ax + ay * ay + az * az);
if (norm == 0.0f || deltaTime == 0.0f)
return; // Handle NaN
norm = 1.0f / norm;
ax *= norm;
ay *= norm;
az *= norm;
// Estimated direction of gravity q.x += qDot.x * deltaTime;
float vx = 2.0f * (q2 * q4 - q1 * q3); q.y += qDot.y * deltaTime;
float vy = 2.0f * (q1 * q2 + q3 * q4); q.z += qDot.z * deltaTime;
float vz = q1 * q1 - q2 * q2 - q3 * q3 + q4 * q4; q.w += qDot.w * deltaTime;
// Error is cross product between estimated direction and measured direction of gravity float norm = std::sqrt(q.x * q.x + q.y * q.y + q.z * q.z + q.w * q.w);
float ex = (ay * vz - az * vy); q.x /= norm;
float ey = (az * vx - ax * vz); q.y /= norm;
float ez = (ax * vy - ay * vx); q.z /= norm;
if (Ki > 0.0f) { q.w /= norm;
eInt[0] += ex * deltaTime; // Accumulate integral error
eInt[1] += ey * deltaTime;
eInt[2] += ez * deltaTime;
} else {
eInt[0] = eInt[1] = eInt[2] = 0.0f; // Prevent integral wind-up
}
// Apply feedback terms orientation.x = q.x;
gx += Kp * ex + Ki * eInt[0]; orientation.y = q.y;
gy += Kp * ey + Ki * eInt[1]; orientation.z = q.z;
gz += Kp * ez + Ki * eInt[2]; orientation.w = q.w;
//// Integrate rate of change of quaternion
q1 += (-q2 * gx - q3 * gy - q4 * gz) * (0.5f * deltaTime);
q2 += (q1 * gx + q3 * gz - q4 * gy) * (0.5f * deltaTime);
q3 += (q1 * gy - q2 * gz + q4 * gx) * (0.5f * deltaTime);
q4 += (q1 * gz + q2 * gy - q3 * gx) * (0.5f * deltaTime);
// Normalize quaternion
norm = std::sqrt(q1 * q1 + q2 * q2 + q3 * q3 + q4 * q4);
norm = 1.0f / norm;
orientation.w = q1 * norm;
orientation.x = q2 * norm;
orientation.y = q3 * norm;
orientation.z = q4 * norm;
o.w = q1 * norm;
o.x = q2 * norm;
o.y = q3 * norm;
o.z = q4 * norm;
LOG_DEBUG(Lib_Pad, "Calculated orientation: {:.2f} {:.2f} {:.2f} {:.2f}", orientation.x, LOG_DEBUG(Lib_Pad, "Calculated orientation: {:.2f} {:.2f} {:.2f} {:.2f}", orientation.x,
orientation.y, orientation.z, orientation.w); orientation.y, orientation.z, orientation.w);
} }
@ -260,6 +228,69 @@ void GameController::SetTouchpadState(int touchIndex, bool touchDown, float x, f
} }
} }
u8 GameController::GetTouchCount() {
std::scoped_lock lock{m_mutex};
return m_touch_count;
}
void GameController::SetTouchCount(u8 touchCount) {
std::scoped_lock lock{m_mutex};
m_touch_count = touchCount;
}
u8 GameController::GetSecondaryTouchCount() {
std::scoped_lock lock{m_mutex};
return m_secondary_touch_count;
}
void GameController::SetSecondaryTouchCount(u8 touchCount) {
std::scoped_lock lock{m_mutex};
m_secondary_touch_count = touchCount;
if (touchCount == 0) {
m_was_secondary_reset = true;
}
}
u8 GameController::GetPreviousTouchNum() {
std::scoped_lock lock{m_mutex};
return m_previous_touchnum;
}
void GameController::SetPreviousTouchNum(u8 touchNum) {
std::scoped_lock lock{m_mutex};
m_previous_touchnum = touchNum;
}
bool GameController::WasSecondaryTouchReset() {
std::scoped_lock lock{m_mutex};
return m_was_secondary_reset;
}
void GameController::UnsetSecondaryTouchResetBool() {
std::scoped_lock lock{m_mutex};
m_was_secondary_reset = false;
}
void GameController::SetLastOrientation(Libraries::Pad::OrbisFQuaternion& orientation) {
std::scoped_lock lock{m_mutex};
m_orientation = orientation;
}
Libraries::Pad::OrbisFQuaternion GameController::GetLastOrientation() {
std::scoped_lock lock{m_mutex};
return m_orientation;
}
std::chrono::steady_clock::time_point GameController::GetLastUpdate() {
std::scoped_lock lock{m_mutex};
return m_last_update;
}
void GameController::SetLastUpdate(std::chrono::steady_clock::time_point lastUpdate) {
std::scoped_lock lock{m_mutex};
m_last_update = lastUpdate;
}
void GameController::SetEngine(std::unique_ptr<Engine> engine) { void GameController::SetEngine(std::unique_ptr<Engine> engine) {
std::scoped_lock _{m_mutex}; std::scoped_lock _{m_mutex};
m_engine = std::move(engine); m_engine = std::move(engine);

View File

@ -23,6 +23,7 @@ enum class Axis {
}; };
struct TouchpadEntry { struct TouchpadEntry {
u8 ID = 0;
bool state{}; bool state{};
u16 x{}; u16 x{};
u16 y{}; u16 y{};
@ -82,9 +83,23 @@ public:
Engine* GetEngine(); Engine* GetEngine();
u32 Poll(); u32 Poll();
u8 GetTouchCount();
void SetTouchCount(u8 touchCount);
u8 GetSecondaryTouchCount();
void SetSecondaryTouchCount(u8 touchCount);
u8 GetPreviousTouchNum();
void SetPreviousTouchNum(u8 touchNum);
bool WasSecondaryTouchReset();
void UnsetSecondaryTouchResetBool();
void SetLastOrientation(Libraries::Pad::OrbisFQuaternion& orientation);
Libraries::Pad::OrbisFQuaternion GetLastOrientation();
std::chrono::steady_clock::time_point GetLastUpdate();
void SetLastUpdate(std::chrono::steady_clock::time_point lastUpdate);
static void CalculateOrientation(Libraries::Pad::OrbisFVector3& acceleration, static void CalculateOrientation(Libraries::Pad::OrbisFVector3& acceleration,
Libraries::Pad::OrbisFVector3& angularVelocity, Libraries::Pad::OrbisFVector3& angularVelocity,
float deltaTime, float deltaTime,
Libraries::Pad::OrbisFQuaternion& lastOrientation,
Libraries::Pad::OrbisFQuaternion& orientation); Libraries::Pad::OrbisFQuaternion& orientation);
private: private:
@ -98,8 +113,15 @@ private:
int m_connected_count = 0; int m_connected_count = 0;
u32 m_states_num = 0; u32 m_states_num = 0;
u32 m_first_state = 0; u32 m_first_state = 0;
u8 m_touch_count = 0;
u8 m_secondary_touch_count = 0;
u8 m_previous_touch_count = 0;
u8 m_previous_touchnum = 0;
bool m_was_secondary_reset = false;
std::array<State, MAX_STATES> m_states; std::array<State, MAX_STATES> m_states;
std::array<StateInternal, MAX_STATES> m_private; std::array<StateInternal, MAX_STATES> m_private;
std::chrono::steady_clock::time_point m_last_update = {};
Libraries::Pad::OrbisFQuaternion m_orientation = {0.0f, 0.0f, 0.0f, 1.0f};
std::unique_ptr<Engine> m_engine = nullptr; std::unique_ptr<Engine> m_engine = nullptr;
}; };

View File

@ -137,7 +137,7 @@ tr("The Auto Updater allows up to 60 update checks per hour.\\nYou have reached
} }
} }
latestRev = latestVersion.right(7); latestRev = latestVersion.right(40);
latestDate = jsonObj["published_at"].toString(); latestDate = jsonObj["published_at"].toString();
QJsonArray assets = jsonObj["assets"].toArray(); QJsonArray assets = jsonObj["assets"].toArray();
@ -167,7 +167,7 @@ tr("The Auto Updater allows up to 60 update checks per hour.\\nYou have reached
QDateTime dateTime = QDateTime::fromString(latestDate, Qt::ISODate); QDateTime dateTime = QDateTime::fromString(latestDate, Qt::ISODate);
latestDate = dateTime.isValid() ? dateTime.toString("yyyy-MM-dd HH:mm:ss") : "Unknown date"; latestDate = dateTime.isValid() ? dateTime.toString("yyyy-MM-dd HH:mm:ss") : "Unknown date";
if (latestRev == currentRev.left(7)) { if (latestRev == currentRev) {
if (showMessage) { if (showMessage) {
QMessageBox::information(this, tr("Auto Updater"), QMessageBox::information(this, tr("Auto Updater"),
tr("Your version is already up to date!")); tr("Your version is already up to date!"));
@ -215,7 +215,7 @@ void CheckUpdate::setupUI(const QString& downloadUrl, const QString& latestDate,
"<td>%3</td>" "<td>%3</td>"
"<td>(%4)</td>" "<td>(%4)</td>"
"</tr></table></p>") "</tr></table></p>")
.arg(currentRev.left(7), currentDate, latestRev, latestDate); .arg(currentRev.left(7), currentDate, latestRev.left(7), latestDate);
QLabel* updateLabel = new QLabel(updateText, this); QLabel* updateLabel = new QLabel(updateText, this);
layout->addWidget(updateLabel); layout->addWidget(updateLabel);

View File

@ -56,15 +56,7 @@ bool MainWindow::Init() {
setMinimumSize(720, 405); setMinimumSize(720, 405);
std::string window_title = ""; std::string window_title = "";
std::string remote_url(Common::g_scm_remote_url); std::string remote_url(Common::g_scm_remote_url);
std::string remote_host; std::string remote_host = Common::GetRemoteNameFromLink();
try {
if (*remote_url.rbegin() == '/') {
remote_url.pop_back();
}
remote_host = remote_url.substr(19, remote_url.rfind('/') - 19);
} catch (...) {
remote_host = "unknown";
}
if (Common::g_is_release) { if (Common::g_is_release) {
if (remote_host == "shadps4-emu" || remote_url.length() == 0) { if (remote_host == "shadps4-emu" || remote_url.length() == 0) {
window_title = fmt::format("shadPS4 v{}", Common::g_version); window_title = fmt::format("shadPS4 v{}", Common::g_version);
@ -380,16 +372,15 @@ void MainWindow::CreateConnects() {
connect(ui->refreshGameListAct, &QAction::triggered, this, &MainWindow::RefreshGameTable); connect(ui->refreshGameListAct, &QAction::triggered, this, &MainWindow::RefreshGameTable);
connect(ui->refreshButton, &QPushButton::clicked, this, &MainWindow::RefreshGameTable); connect(ui->refreshButton, &QPushButton::clicked, this, &MainWindow::RefreshGameTable);
connect(ui->showGameListAct, &QAction::triggered, this, &MainWindow::ShowGameList); connect(ui->showGameListAct, &QAction::triggered, this, &MainWindow::ShowGameList);
connect(this, &MainWindow::ExtractionFinished, this, &MainWindow::RefreshGameTable);
connect(ui->toggleLabelsAct, &QAction::toggled, this, &MainWindow::toggleLabelsUnderIcons); connect(ui->toggleLabelsAct, &QAction::toggled, this, &MainWindow::toggleLabelsUnderIcons);
connect(ui->fullscreenButton, &QPushButton::clicked, this, &MainWindow::toggleFullscreen); connect(ui->fullscreenButton, &QPushButton::clicked, this, &MainWindow::toggleFullscreen);
connect(ui->sizeSlider, &QSlider::valueChanged, this, [this](int value) { connect(ui->sizeSlider, &QSlider::valueChanged, this, [this](int value) {
if (isTableList) { if (isTableList) {
m_game_list_frame->icon_size = m_game_list_frame->icon_size =
36 + value; // 36 is the minimum icon size to use due to text disappearing. 48 + value; // 48 is the minimum icon size to use due to text disappearing.
m_game_list_frame->ResizeIcons(36 + value); m_game_list_frame->ResizeIcons(48 + value);
Config::setIconSize(36 + value); Config::setIconSize(48 + value);
Config::setSliderPosition(value); Config::setSliderPosition(value);
} else { } else {
m_game_grid_frame->icon_size = 69 + value; m_game_grid_frame->icon_size = 69 + value;

View File

@ -29,7 +29,6 @@ class MainWindow : public QMainWindow {
Q_OBJECT Q_OBJECT
signals: signals:
void WindowResized(QResizeEvent* event); void WindowResized(QResizeEvent* event);
void ExtractionFinished();
public: public:
explicit MainWindow(QWidget* parent = nullptr); explicit MainWindow(QWidget* parent = nullptr);

View File

@ -26,7 +26,7 @@
</message> </message>
<message> <message>
<source>Cheats/Patches are experimental.\nUse with caution.\n\nDownload cheats individually by selecting the repository and clicking the download button.\nIn the Patches tab, you can download all patches at once, choose which ones you want to use, and save your selection.\n\nSince we do not develop the Cheats/Patches,\nplease report issues to the cheat author.\n\nCreated a new cheat? Visit:\n</source> <source>Cheats/Patches are experimental.\nUse with caution.\n\nDownload cheats individually by selecting the repository and clicking the download button.\nIn the Patches tab, you can download all patches at once, choose which ones you want to use, and save your selection.\n\nSince we do not develop the Cheats/Patches,\nplease report issues to the cheat author.\n\nCreated a new cheat? Visit:\n</source>
<translation>Los cheats/patches son experimentales.\nÚselos con precaución.\n\nDescargue los cheats individualmente seleccionando el repositorio y haciendo clic en el botón de descarga.\nEn la pestaña Patches, puede descargar todos los patches a la vez, elegir cuáles desea usar y guardar la selección.\n\nComo no desarrollamos los Cheats/Patches,\npor favor informe los problemas al autor del cheat.\n\n¿Creaste un nuevo cheat? Visita:\n</translation> <translation>Los trucos/parches son experimentales.\nÚselos con precaución.\n\nPuede descargar cada truco seleccionando el repositorio y haciendo clic en el botón de descarga.\nEn la pestaña Parches podrá descargar todos los parches a la vez, elegir cuáles desea usar y guardar la selección.\n\nComo no desarrollamos los trucos/parches,\ndebe informar de cualquier problema a sus autores correspondientes.\n\n¿Creaste un truco nuevo? Visita:\n</translation>
</message> </message>
<message> <message>
<source>No Image Available</source> <source>No Image Available</source>
@ -2048,7 +2048,7 @@
</message> </message>
<message> <message>
<source> * Unsupported Vulkan Version</source> <source> * Unsupported Vulkan Version</source>
<translation type="unfinished"> * Unsupported Vulkan Version</translation> <translation> * Versión de Vulkan no soportada</translation>
</message> </message>
</context> </context>
<context> <context>

View File

@ -2048,7 +2048,7 @@
</message> </message>
<message> <message>
<source> * Unsupported Vulkan Version</source> <source> * Unsupported Vulkan Version</source>
<translation type="unfinished"> * Unsupported Vulkan Version</translation> <translation> * Versão do Vulkan não suportada</translation>
</message> </message>
</context> </context>
<context> <context>

View File

@ -138,7 +138,7 @@
</message> </message>
<message> <message>
<source>File Exists</source> <source>File Exists</source>
<translation>Dosya mevcut</translation> <translation>Dosya Mevcut</translation>
</message> </message>
<message> <message>
<source>File already exists. Do you want to replace it?</source> <source>File already exists. Do you want to replace it?</source>
@ -1221,7 +1221,7 @@
</message> </message>
<message> <message>
<source>Exit shadPS4</source> <source>Exit shadPS4</source>
<translation>shadPS4&apos;ten Çık</translation> <translation>shadPS4 Çıkış</translation>
</message> </message>
<message> <message>
<source>Exit the application.</source> <source>Exit the application.</source>
@ -1381,7 +1381,7 @@
</message> </message>
<message> <message>
<source>Game Boot</source> <source>Game Boot</source>
<translation>Oyun Başlatma</translation> <translation>Oyun Başlat</translation>
</message> </message>
<message> <message>
<source>Only one file can be selected!</source> <source>Only one file can be selected!</source>
@ -2048,7 +2048,7 @@
</message> </message>
<message> <message>
<source> * Unsupported Vulkan Version</source> <source> * Unsupported Vulkan Version</source>
<translation type="unfinished"> * Unsupported Vulkan Version</translation> <translation> * Desteklenmeyen Vulkan Sürümü</translation>
</message> </message>
</context> </context>
<context> <context>

View File

@ -68,6 +68,22 @@ Id BufferAtomicU32(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address, Id
}); });
} }
Id BufferAtomicU32CmpSwap(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address, Id value,
Id cmp_value,
Id (Sirit::Module::*atomic_func)(Id, Id, Id, Id, Id, Id, Id)) {
const auto& buffer = ctx.buffers[handle];
if (Sirit::ValidId(buffer.offset)) {
address = ctx.OpIAdd(ctx.U32[1], address, buffer.offset);
}
const Id index = ctx.OpShiftRightLogical(ctx.U32[1], address, ctx.ConstU32(2u));
const auto [id, pointer_type] = buffer[EmitContext::PointerType::U32];
const Id ptr = ctx.OpAccessChain(pointer_type, id, ctx.u32_zero_value, index);
const auto [scope, semantics]{AtomicArgs(ctx)};
return BufferAtomicU32BoundsCheck(ctx, index, buffer.size_dwords, [&] {
return (ctx.*atomic_func)(ctx.U32[1], ptr, scope, semantics, semantics, value, cmp_value);
});
}
Id ImageAtomicU32(EmitContext& ctx, IR::Inst* inst, u32 handle, Id coords, Id value, Id ImageAtomicU32(EmitContext& ctx, IR::Inst* inst, u32 handle, Id coords, Id value,
Id (Sirit::Module::*atomic_func)(Id, Id, Id, Id, Id)) { Id (Sirit::Module::*atomic_func)(Id, Id, Id, Id, Id)) {
const auto& texture = ctx.images[handle & 0xFFFF]; const auto& texture = ctx.images[handle & 0xFFFF];
@ -175,6 +191,12 @@ Id EmitBufferAtomicSwap32(EmitContext& ctx, IR::Inst* inst, u32 handle, Id addre
return BufferAtomicU32(ctx, inst, handle, address, value, &Sirit::Module::OpAtomicExchange); return BufferAtomicU32(ctx, inst, handle, address, value, &Sirit::Module::OpAtomicExchange);
} }
Id EmitBufferAtomicCmpSwap32(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address, Id value,
Id cmp_value) {
return BufferAtomicU32CmpSwap(ctx, inst, handle, address, value, cmp_value,
&Sirit::Module::OpAtomicCompareExchange);
}
Id EmitImageAtomicIAdd32(EmitContext& ctx, IR::Inst* inst, u32 handle, Id coords, Id value) { Id EmitImageAtomicIAdd32(EmitContext& ctx, IR::Inst* inst, u32 handle, Id coords, Id value) {
return ImageAtomicU32(ctx, inst, handle, coords, value, &Sirit::Module::OpAtomicIAdd); return ImageAtomicU32(ctx, inst, handle, coords, value, &Sirit::Module::OpAtomicIAdd);
} }

View File

@ -96,6 +96,8 @@ Id EmitBufferAtomicAnd32(EmitContext& ctx, IR::Inst* inst, u32 handle, Id addres
Id EmitBufferAtomicOr32(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address, Id value); Id EmitBufferAtomicOr32(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address, Id value);
Id EmitBufferAtomicXor32(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address, Id value); Id EmitBufferAtomicXor32(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address, Id value);
Id EmitBufferAtomicSwap32(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address, Id value); Id EmitBufferAtomicSwap32(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address, Id value);
Id EmitBufferAtomicCmpSwap32(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address, Id value,
Id cmp_value);
Id EmitGetAttribute(EmitContext& ctx, IR::Attribute attr, u32 comp, Id index); Id EmitGetAttribute(EmitContext& ctx, IR::Attribute attr, u32 comp, Id index);
Id EmitGetAttributeU32(EmitContext& ctx, IR::Attribute attr, u32 comp); Id EmitGetAttributeU32(EmitContext& ctx, IR::Attribute attr, u32 comp);
void EmitSetAttribute(EmitContext& ctx, IR::Attribute attr, Id value, u32 comp); void EmitSetAttribute(EmitContext& ctx, IR::Attribute attr, Id value, u32 comp);
@ -372,6 +374,7 @@ Id EmitBitCount64(EmitContext& ctx, Id value);
Id EmitBitwiseNot32(EmitContext& ctx, Id value); Id EmitBitwiseNot32(EmitContext& ctx, Id value);
Id EmitFindSMsb32(EmitContext& ctx, Id value); Id EmitFindSMsb32(EmitContext& ctx, Id value);
Id EmitFindUMsb32(EmitContext& ctx, Id value); Id EmitFindUMsb32(EmitContext& ctx, Id value);
Id EmitFindUMsb64(EmitContext& ctx, Id value);
Id EmitFindILsb32(EmitContext& ctx, Id value); Id EmitFindILsb32(EmitContext& ctx, Id value);
Id EmitFindILsb64(EmitContext& ctx, Id value); Id EmitFindILsb64(EmitContext& ctx, Id value);
Id EmitSMin32(EmitContext& ctx, Id a, Id b); Id EmitSMin32(EmitContext& ctx, Id a, Id b);

View File

@ -229,6 +229,20 @@ Id EmitFindUMsb32(EmitContext& ctx, Id value) {
return ctx.OpFindUMsb(ctx.U32[1], value); return ctx.OpFindUMsb(ctx.U32[1], value);
} }
Id EmitFindUMsb64(EmitContext& ctx, Id value) {
// Vulkan restricts some bitwise operations to 32-bit only, so decompose into
// two 32-bit values and select the correct result.
const Id unpacked{ctx.OpBitcast(ctx.U32[2], value)};
const Id hi{ctx.OpCompositeExtract(ctx.U32[1], unpacked, 1U)};
const Id lo{ctx.OpCompositeExtract(ctx.U32[1], unpacked, 0U)};
const Id hi_msb{ctx.OpFindUMsb(ctx.U32[1], hi)};
const Id lo_msb{ctx.OpFindUMsb(ctx.U32[1], lo)};
const Id found_hi{ctx.OpINotEqual(ctx.U1[1], hi_msb, ctx.ConstU32(u32(-1)))};
const Id shifted_hi{ctx.OpIAdd(ctx.U32[1], hi_msb, ctx.ConstU32(32u))};
// value == 0 case is checked in IREmitter
return ctx.OpSelect(ctx.U32[1], found_hi, shifted_hi, lo_msb);
}
Id EmitFindILsb32(EmitContext& ctx, Id value) { Id EmitFindILsb32(EmitContext& ctx, Id value) {
return ctx.OpFindILsb(ctx.U32[1], value); return ctx.OpFindILsb(ctx.U32[1], value);
} }

View File

@ -307,7 +307,9 @@ void EmitContext::DefineInterpolatedAttribs() {
const Id p2{OpCompositeExtract(F32[4], p_array, 2U)}; const Id p2{OpCompositeExtract(F32[4], p_array, 2U)};
const Id p10{OpFSub(F32[4], p1, p0)}; const Id p10{OpFSub(F32[4], p1, p0)};
const Id p20{OpFSub(F32[4], p2, p0)}; const Id p20{OpFSub(F32[4], p2, p0)};
const Id bary_coord{OpLoad(F32[3], gl_bary_coord_id)}; const Id bary_coord{OpLoad(F32[3], IsLinear(info.interp_qualifiers[i])
? bary_coord_linear_id
: bary_coord_persp_id)};
const Id bary_coord_y{OpCompositeExtract(F32[1], bary_coord, 1)}; const Id bary_coord_y{OpCompositeExtract(F32[1], bary_coord, 1)};
const Id bary_coord_z{OpCompositeExtract(F32[1], bary_coord, 2)}; const Id bary_coord_z{OpCompositeExtract(F32[1], bary_coord, 2)};
const Id p10_y{OpVectorTimesScalar(F32[4], p10, bary_coord_y)}; const Id p10_y{OpVectorTimesScalar(F32[4], p10, bary_coord_y)};
@ -411,8 +413,14 @@ void EmitContext::DefineInputs() {
DefineVariable(U1[1], spv::BuiltIn::FrontFacing, spv::StorageClass::Input); DefineVariable(U1[1], spv::BuiltIn::FrontFacing, spv::StorageClass::Input);
} }
if (profile.needs_manual_interpolation) { if (profile.needs_manual_interpolation) {
gl_bary_coord_id = if (info.has_perspective_interp) {
DefineVariable(F32[3], spv::BuiltIn::BaryCoordKHR, spv::StorageClass::Input); bary_coord_persp_id =
DefineVariable(F32[3], spv::BuiltIn::BaryCoordKHR, spv::StorageClass::Input);
}
if (info.has_linear_interp) {
bary_coord_linear_id = DefineVariable(F32[3], spv::BuiltIn::BaryCoordNoPerspKHR,
spv::StorageClass::Input);
}
} }
for (s32 i = 0; i < runtime_info.fs_info.num_inputs; i++) { for (s32 i = 0; i < runtime_info.fs_info.num_inputs; i++) {
const auto& input = runtime_info.fs_info.inputs[i]; const auto& input = runtime_info.fs_info.inputs[i];
@ -435,9 +443,12 @@ void EmitContext::DefineInputs() {
} else { } else {
attr_id = DefineInput(type, semantic); attr_id = DefineInput(type, semantic);
Name(attr_id, fmt::format("fs_in_attr{}", semantic)); Name(attr_id, fmt::format("fs_in_attr{}", semantic));
}
if (input.is_flat) { if (input.is_flat) {
Decorate(attr_id, spv::Decoration::Flat); Decorate(attr_id, spv::Decoration::Flat);
} else if (IsLinear(info.interp_qualifiers[i])) {
Decorate(attr_id, spv::Decoration::NoPerspective);
}
} }
input_params[semantic] = input_params[semantic] =
GetAttributeInfo(AmdGpu::NumberFormat::Float, attr_id, num_components, false); GetAttributeInfo(AmdGpu::NumberFormat::Float, attr_id, num_components, false);
@ -634,7 +645,8 @@ void EmitContext::DefineOutputs() {
} }
break; break;
} }
case LogicalStage::Fragment: case LogicalStage::Fragment: {
u32 num_render_targets = 0;
for (u32 i = 0; i < IR::NumRenderTargets; i++) { for (u32 i = 0; i < IR::NumRenderTargets; i++) {
const IR::Attribute mrt{IR::Attribute::RenderTarget0 + i}; const IR::Attribute mrt{IR::Attribute::RenderTarget0 + i};
if (!info.stores.GetAny(mrt)) { if (!info.stores.GetAny(mrt)) {
@ -643,11 +655,21 @@ void EmitContext::DefineOutputs() {
const u32 num_components = info.stores.NumComponents(mrt); const u32 num_components = info.stores.NumComponents(mrt);
const AmdGpu::NumberFormat num_format{runtime_info.fs_info.color_buffers[i].num_format}; const AmdGpu::NumberFormat num_format{runtime_info.fs_info.color_buffers[i].num_format};
const Id type{GetAttributeType(*this, num_format)[num_components]}; const Id type{GetAttributeType(*this, num_format)[num_components]};
const Id id{DefineOutput(type, i)}; Id id;
if (runtime_info.fs_info.dual_source_blending) {
id = DefineOutput(type, 0);
Decorate(id, spv::Decoration::Index, i);
} else {
id = DefineOutput(type, i);
}
Name(id, fmt::format("frag_color{}", i)); Name(id, fmt::format("frag_color{}", i));
frag_outputs[i] = GetAttributeInfo(num_format, id, num_components, true); frag_outputs[i] = GetAttributeInfo(num_format, id, num_components, true);
++num_render_targets;
} }
ASSERT_MSG(!runtime_info.fs_info.dual_source_blending || num_render_targets == 2,
"Dual source blending enabled, there must be exactly two MRT exports");
break; break;
}
case LogicalStage::Geometry: { case LogicalStage::Geometry: {
output_position = DefineVariable(F32[4], spv::BuiltIn::Position, spv::StorageClass::Output); output_position = DefineVariable(F32[4], spv::BuiltIn::Position, spv::StorageClass::Output);

View File

@ -293,8 +293,8 @@ public:
Id shared_memory_u32_type{}; Id shared_memory_u32_type{};
Id interpolate_func{}; Id bary_coord_persp_id{};
Id gl_bary_coord_id{}; Id bary_coord_linear_id{};
struct TextureDefinition { struct TextureDefinition {
const VectorIds* data_types; const VectorIds* data_types;

View File

@ -67,6 +67,9 @@ CopyShaderData ParseCopyShader(std::span<const u32> code) {
if (last_attr != IR::Attribute::Position0) { if (last_attr != IR::Attribute::Position0) {
data.num_attrs = static_cast<u32>(last_attr) - static_cast<u32>(IR::Attribute::Param0) + 1; data.num_attrs = static_cast<u32>(last_attr) - static_cast<u32>(IR::Attribute::Param0) + 1;
const auto it = data.attr_map.begin();
const u32 comp_stride = std::next(it)->first - it->first;
data.output_vertices = comp_stride / 64;
} }
return data; return data;

View File

@ -3,8 +3,8 @@
#pragma once #pragma once
#include <map>
#include <span> #include <span>
#include <unordered_map>
#include "common/types.h" #include "common/types.h"
#include "shader_recompiler/ir/attribute.h" #include "shader_recompiler/ir/attribute.h"
@ -12,8 +12,9 @@
namespace Shader { namespace Shader {
struct CopyShaderData { struct CopyShaderData {
std::unordered_map<u32, std::pair<Shader::IR::Attribute, u32>> attr_map; std::map<u32, std::pair<Shader::IR::Attribute, u32>> attr_map;
u32 num_attrs{0}; u32 num_attrs{0};
u32 output_vertices{0};
}; };
CopyShaderData ParseCopyShader(std::span<const u32> code); CopyShaderData ParseCopyShader(std::span<const u32> code);

View File

@ -1032,7 +1032,6 @@ void GcnDecodeContext::decodeInstructionMIMG(uint64_t hexInstruction) {
m_instruction.control.mimg = *reinterpret_cast<InstControlMIMG*>(&hexInstruction); m_instruction.control.mimg = *reinterpret_cast<InstControlMIMG*>(&hexInstruction);
m_instruction.control.mimg.mod = getMimgModifier(m_instruction.opcode); m_instruction.control.mimg.mod = getMimgModifier(m_instruction.opcode);
ASSERT(m_instruction.control.mimg.r128 == 0);
} }
void GcnDecodeContext::decodeInstructionDS(uint64_t hexInstruction) { void GcnDecodeContext::decodeInstructionDS(uint64_t hexInstruction) {

View File

@ -605,11 +605,12 @@ public:
Info& info_, const RuntimeInfo& runtime_info_, const Profile& profile_) Info& info_, const RuntimeInfo& runtime_info_, const Profile& profile_)
: stmt_pool{stmt_pool_}, inst_pool{inst_pool_}, block_pool{block_pool_}, : stmt_pool{stmt_pool_}, inst_pool{inst_pool_}, block_pool{block_pool_},
syntax_list{syntax_list_}, inst_list{inst_list_}, info{info_}, syntax_list{syntax_list_}, inst_list{inst_list_}, info{info_},
runtime_info{runtime_info_}, profile{profile_} { runtime_info{runtime_info_}, profile{profile_},
translator{info_, runtime_info_, profile_} {
Visit(root_stmt, nullptr, nullptr); Visit(root_stmt, nullptr, nullptr);
IR::Block& first_block{*syntax_list.front().data.block}; IR::Block* first_block = syntax_list.front().data.block;
Translator{&first_block, info, runtime_info, profile}.EmitPrologue(); translator.EmitPrologue(first_block);
} }
private: private:
@ -637,8 +638,8 @@ private:
current_block->has_multiple_predecessors = stmt.block->num_predecessors > 1; current_block->has_multiple_predecessors = stmt.block->num_predecessors > 1;
const u32 start = stmt.block->begin_index; const u32 start = stmt.block->begin_index;
const u32 size = stmt.block->end_index - start + 1; const u32 size = stmt.block->end_index - start + 1;
Translate(current_block, stmt.block->begin, inst_list.subspan(start, size), translator.Translate(current_block, stmt.block->begin,
info, runtime_info, profile); inst_list.subspan(start, size));
} }
break; break;
} }
@ -820,6 +821,7 @@ private:
Info& info; Info& info;
const RuntimeInfo& runtime_info; const RuntimeInfo& runtime_info;
const Profile& profile; const Profile& profile;
Translator translator;
}; };
} // Anonymous namespace } // Anonymous namespace

View File

@ -26,8 +26,11 @@ void Translator::ExportMrtValue(IR::Attribute attribute, u32 comp, const IR::F32
} }
void Translator::ExportMrtCompressed(IR::Attribute attribute, u32 idx, const IR::U32& value) { void Translator::ExportMrtCompressed(IR::Attribute attribute, u32 idx, const IR::U32& value) {
const u32 color_buffer_idx = u32 color_buffer_idx =
static_cast<u32>(attribute) - static_cast<u32>(IR::Attribute::RenderTarget0); static_cast<u32>(attribute) - static_cast<u32>(IR::Attribute::RenderTarget0);
if (runtime_info.fs_info.dual_source_blending && attribute == IR::Attribute::RenderTarget1) {
color_buffer_idx = 0;
}
const auto color_buffer = runtime_info.fs_info.color_buffers[color_buffer_idx]; const auto color_buffer = runtime_info.fs_info.color_buffers[color_buffer_idx];
AmdGpu::NumberFormat num_format; AmdGpu::NumberFormat num_format;
@ -68,8 +71,11 @@ void Translator::ExportMrtCompressed(IR::Attribute attribute, u32 idx, const IR:
} }
void Translator::ExportMrtUncompressed(IR::Attribute attribute, u32 comp, const IR::F32& value) { void Translator::ExportMrtUncompressed(IR::Attribute attribute, u32 comp, const IR::F32& value) {
const u32 color_buffer_idx = u32 color_buffer_idx =
static_cast<u32>(attribute) - static_cast<u32>(IR::Attribute::RenderTarget0); static_cast<u32>(attribute) - static_cast<u32>(IR::Attribute::RenderTarget0);
if (runtime_info.fs_info.dual_source_blending && attribute == IR::Attribute::RenderTarget1) {
color_buffer_idx = 0;
}
const auto color_buffer = runtime_info.fs_info.color_buffers[color_buffer_idx]; const auto color_buffer = runtime_info.fs_info.color_buffers[color_buffer_idx];
const auto swizzled_comp = SwizzleMrtComponent(color_buffer, comp); const auto swizzled_comp = SwizzleMrtComponent(color_buffer, comp);

View File

@ -114,6 +114,8 @@ void Translator::EmitScalarAlu(const GcnInst& inst) {
return S_FF1_I32_B64(inst); return S_FF1_I32_B64(inst);
case Opcode::S_FLBIT_I32_B32: case Opcode::S_FLBIT_I32_B32:
return S_FLBIT_I32_B32(inst); return S_FLBIT_I32_B32(inst);
case Opcode::S_FLBIT_I32_B64:
return S_FLBIT_I32_B64(inst);
case Opcode::S_BITSET0_B32: case Opcode::S_BITSET0_B32:
return S_BITSET_B32(inst, 0); return S_BITSET_B32(inst, 0);
case Opcode::S_BITSET1_B32: case Opcode::S_BITSET1_B32:
@ -686,6 +688,17 @@ void Translator::S_FLBIT_I32_B32(const GcnInst& inst) {
SetDst(inst.dst[0], IR::U32{ir.Select(cond, pos_from_left, ir.Imm32(~0U))}); SetDst(inst.dst[0], IR::U32{ir.Select(cond, pos_from_left, ir.Imm32(~0U))});
} }
void Translator::S_FLBIT_I32_B64(const GcnInst& inst) {
const IR::U64 src0{GetSrc64(inst.src[0])};
// Gcn wants the MSB position counting from the left, but SPIR-V counts from the rightmost (LSB)
// position
const IR::U32 msb_pos = ir.FindUMsb(src0);
const IR::U32 pos_from_left = ir.ISub(ir.Imm32(63), msb_pos);
// Select 0xFFFFFFFF if src0 was 0
const IR::U1 cond = ir.INotEqual(src0, ir.Imm64(u64(0u)));
SetDst(inst.dst[0], IR::U32{ir.Select(cond, pos_from_left, ir.Imm32(~0U))});
}
void Translator::S_BITSET_B32(const GcnInst& inst, u32 bit_value) { void Translator::S_BITSET_B32(const GcnInst& inst, u32 bit_value) {
const IR::U32 old_value{GetSrc(inst.dst[0])}; const IR::U32 old_value{GetSrc(inst.dst[0])};
const IR::U32 offset{ir.BitFieldExtract(GetSrc(inst.src[0]), ir.Imm32(0U), ir.Imm32(5U))}; const IR::U32 offset{ir.BitFieldExtract(GetSrc(inst.src[0]), ir.Imm32(0U), ir.Imm32(5U))};

View File

@ -21,16 +21,60 @@
namespace Shader::Gcn { namespace Shader::Gcn {
static u32 next_vgpr_num; Translator::Translator(Info& info_, const RuntimeInfo& runtime_info_, const Profile& profile_)
static std::unordered_map<u32, IR::VectorReg> vgpr_map; : info{info_}, runtime_info{runtime_info_}, profile{profile_},
next_vgpr_num{runtime_info.num_allocated_vgprs} {
Translator::Translator(IR::Block* block_, Info& info_, const RuntimeInfo& runtime_info_, if (info.l_stage == LogicalStage::Fragment) {
const Profile& profile_) dst_frag_vreg = GatherInterpQualifiers();
: ir{*block_, block_->begin()}, info{info_}, runtime_info{runtime_info_}, profile{profile_} { }
next_vgpr_num = vgpr_map.empty() ? runtime_info.num_allocated_vgprs : next_vgpr_num;
} }
void Translator::EmitPrologue() { IR::VectorReg Translator::GatherInterpQualifiers() {
u32 dst_vreg{};
if (runtime_info.fs_info.addr_flags.persp_sample_ena) {
vgpr_to_interp[dst_vreg++] = IR::Interpolation::PerspectiveSample; // I
vgpr_to_interp[dst_vreg++] = IR::Interpolation::PerspectiveSample; // J
info.has_perspective_interp = true;
}
if (runtime_info.fs_info.addr_flags.persp_center_ena) {
vgpr_to_interp[dst_vreg++] = IR::Interpolation::PerspectiveCenter; // I
vgpr_to_interp[dst_vreg++] = IR::Interpolation::PerspectiveCenter; // J
info.has_perspective_interp = true;
}
if (runtime_info.fs_info.addr_flags.persp_centroid_ena) {
vgpr_to_interp[dst_vreg++] = IR::Interpolation::PerspectiveCentroid; // I
vgpr_to_interp[dst_vreg++] = IR::Interpolation::PerspectiveCentroid; // J
info.has_perspective_interp = true;
}
if (runtime_info.fs_info.addr_flags.persp_pull_model_ena) {
++dst_vreg; // I/W
++dst_vreg; // J/W
++dst_vreg; // 1/W
}
if (runtime_info.fs_info.addr_flags.linear_sample_ena) {
vgpr_to_interp[dst_vreg++] = IR::Interpolation::LinearSample; // I
vgpr_to_interp[dst_vreg++] = IR::Interpolation::LinearSample; // J
info.has_linear_interp = true;
}
if (runtime_info.fs_info.addr_flags.linear_center_ena) {
vgpr_to_interp[dst_vreg++] = IR::Interpolation::LinearCenter; // I
vgpr_to_interp[dst_vreg++] = IR::Interpolation::LinearCenter; // J
info.has_linear_interp = true;
}
if (runtime_info.fs_info.addr_flags.linear_centroid_ena) {
vgpr_to_interp[dst_vreg++] = IR::Interpolation::LinearCentroid; // I
vgpr_to_interp[dst_vreg++] = IR::Interpolation::LinearCentroid; // J
info.has_linear_interp = true;
}
if (runtime_info.fs_info.addr_flags.line_stipple_tex_ena) {
++dst_vreg;
}
return IR::VectorReg(dst_vreg);
}
void Translator::EmitPrologue(IR::Block* first_block) {
ir = IR::IREmitter(*first_block, first_block->begin());
ir.Prologue(); ir.Prologue();
ir.SetExec(ir.Imm1(true)); ir.SetExec(ir.Imm1(true));
@ -60,39 +104,7 @@ void Translator::EmitPrologue() {
} }
break; break;
case LogicalStage::Fragment: case LogicalStage::Fragment:
dst_vreg = IR::VectorReg::V0; dst_vreg = dst_frag_vreg;
if (runtime_info.fs_info.addr_flags.persp_sample_ena) {
++dst_vreg; // I
++dst_vreg; // J
}
if (runtime_info.fs_info.addr_flags.persp_center_ena) {
++dst_vreg; // I
++dst_vreg; // J
}
if (runtime_info.fs_info.addr_flags.persp_centroid_ena) {
++dst_vreg; // I
++dst_vreg; // J
}
if (runtime_info.fs_info.addr_flags.persp_pull_model_ena) {
++dst_vreg; // I/W
++dst_vreg; // J/W
++dst_vreg; // 1/W
}
if (runtime_info.fs_info.addr_flags.linear_sample_ena) {
++dst_vreg; // I
++dst_vreg; // J
}
if (runtime_info.fs_info.addr_flags.linear_center_ena) {
++dst_vreg; // I
++dst_vreg; // J
}
if (runtime_info.fs_info.addr_flags.linear_centroid_ena) {
++dst_vreg; // I
++dst_vreg; // J
}
if (runtime_info.fs_info.addr_flags.line_stipple_tex_ena) {
++dst_vreg;
}
if (runtime_info.fs_info.addr_flags.pos_x_float_ena) { if (runtime_info.fs_info.addr_flags.pos_x_float_ena) {
if (runtime_info.fs_info.en_flags.pos_x_float_ena) { if (runtime_info.fs_info.en_flags.pos_x_float_ena) {
ir.SetVectorReg(dst_vreg++, ir.GetAttribute(IR::Attribute::FragCoord, 0)); ir.SetVectorReg(dst_vreg++, ir.GetAttribute(IR::Attribute::FragCoord, 0));
@ -380,7 +392,7 @@ T Translator::GetSrc64(const InstOperand& operand) {
break; break;
case OperandField::VccLo: case OperandField::VccLo:
if constexpr (is_float) { if constexpr (is_float) {
UNREACHABLE(); value = ir.PackDouble2x32(ir.CompositeConstruct(ir.GetVccLo(), ir.GetVccHi()));
} else { } else {
value = ir.PackUint2x32(ir.CompositeConstruct(ir.GetVccLo(), ir.GetVccHi())); value = ir.PackUint2x32(ir.CompositeConstruct(ir.GetVccLo(), ir.GetVccHi()));
} }
@ -543,6 +555,26 @@ void Translator::LogMissingOpcode(const GcnInst& inst) {
info.translation_failed = true; info.translation_failed = true;
} }
void Translator::Translate(IR::Block* block, u32 pc, std::span<const GcnInst> inst_list) {
if (inst_list.empty()) {
return;
}
ir = IR::IREmitter{*block, block->begin()};
for (const auto& inst : inst_list) {
pc += inst.length;
// Special case for emitting fetch shader.
if (inst.opcode == Opcode::S_SWAPPC_B64) {
ASSERT(info.stage == Stage::Vertex || info.stage == Stage::Export ||
info.stage == Stage::Local);
EmitFetch(inst);
continue;
}
TranslateInstruction(inst, pc);
}
}
void Translator::TranslateInstruction(const GcnInst& inst, const u32 pc) { void Translator::TranslateInstruction(const GcnInst& inst, const u32 pc) {
// Emit instructions for each category. // Emit instructions for each category.
switch (inst.category) { switch (inst.category) {
@ -577,25 +609,4 @@ void Translator::TranslateInstruction(const GcnInst& inst, const u32 pc) {
} }
} }
void Translate(IR::Block* block, u32 pc, std::span<const GcnInst> inst_list, Info& info,
const RuntimeInfo& runtime_info, const Profile& profile) {
if (inst_list.empty()) {
return;
}
Translator translator{block, info, runtime_info, profile};
for (const auto& inst : inst_list) {
pc += inst.length;
// Special case for emitting fetch shader.
if (inst.opcode == Opcode::S_SWAPPC_B64) {
ASSERT(info.stage == Stage::Vertex || info.stage == Stage::Export ||
info.stage == Stage::Local);
translator.EmitFetch(inst);
continue;
}
translator.TranslateInstruction(inst, pc);
}
}
} // namespace Shader::Gcn } // namespace Shader::Gcn

View File

@ -4,6 +4,7 @@
#pragma once #pragma once
#include <span> #include <span>
#include <unordered_map>
#include "shader_recompiler/frontend/instruction.h" #include "shader_recompiler/frontend/instruction.h"
#include "shader_recompiler/info.h" #include "shader_recompiler/info.h"
#include "shader_recompiler/ir/basic_block.h" #include "shader_recompiler/ir/basic_block.h"
@ -53,15 +54,17 @@ enum class NegateMode : u32 {
Result, Result,
}; };
static constexpr size_t MaxInterpVgpr = 16;
class Translator { class Translator {
public: public:
explicit Translator(IR::Block* block_, Info& info, const RuntimeInfo& runtime_info, explicit Translator(Info& info, const RuntimeInfo& runtime_info, const Profile& profile);
const Profile& profile);
void Translate(IR::Block* block, u32 pc, std::span<const GcnInst> inst_list);
void TranslateInstruction(const GcnInst& inst, u32 pc); void TranslateInstruction(const GcnInst& inst, u32 pc);
// Instruction categories // Instruction categories
void EmitPrologue(); void EmitPrologue(IR::Block* first_block);
void EmitFetch(const GcnInst& inst); void EmitFetch(const GcnInst& inst);
void EmitExport(const GcnInst& inst); void EmitExport(const GcnInst& inst);
void EmitFlowControl(u32 pc, const GcnInst& inst); void EmitFlowControl(u32 pc, const GcnInst& inst);
@ -121,6 +124,7 @@ public:
void S_FF1_I32_B32(const GcnInst& inst); void S_FF1_I32_B32(const GcnInst& inst);
void S_FF1_I32_B64(const GcnInst& inst); void S_FF1_I32_B64(const GcnInst& inst);
void S_FLBIT_I32_B32(const GcnInst& inst); void S_FLBIT_I32_B32(const GcnInst& inst);
void S_FLBIT_I32_B64(const GcnInst& inst);
void S_BITSET_B32(const GcnInst& inst, u32 bit_value); void S_BITSET_B32(const GcnInst& inst, u32 bit_value);
void S_GETPC_B64(u32 pc, const GcnInst& inst); void S_GETPC_B64(u32 pc, const GcnInst& inst);
void S_SAVEEXEC_B64(NegateMode negate, bool is_or, const GcnInst& inst); void S_SAVEEXEC_B64(NegateMode negate, bool is_or, const GcnInst& inst);
@ -183,6 +187,7 @@ public:
void V_READFIRSTLANE_B32(const GcnInst& inst); void V_READFIRSTLANE_B32(const GcnInst& inst);
void V_CVT_I32_F64(const GcnInst& inst); void V_CVT_I32_F64(const GcnInst& inst);
void V_CVT_F64_I32(const GcnInst& inst); void V_CVT_F64_I32(const GcnInst& inst);
void V_CVT_F64_U32(const GcnInst& inst);
void V_CVT_F32_I32(const GcnInst& inst); void V_CVT_F32_I32(const GcnInst& inst);
void V_CVT_F32_U32(const GcnInst& inst); void V_CVT_F32_U32(const GcnInst& inst);
void V_CVT_U32_F32(const GcnInst& inst); void V_CVT_U32_F32(const GcnInst& inst);
@ -203,6 +208,7 @@ public:
void V_EXP_F32(const GcnInst& inst); void V_EXP_F32(const GcnInst& inst);
void V_LOG_F32(const GcnInst& inst); void V_LOG_F32(const GcnInst& inst);
void V_RCP_F32(const GcnInst& inst); void V_RCP_F32(const GcnInst& inst);
void V_RCP_LEGACY_F32(const GcnInst& inst);
void V_RCP_F64(const GcnInst& inst); void V_RCP_F64(const GcnInst& inst);
void V_RSQ_F32(const GcnInst& inst); void V_RSQ_F32(const GcnInst& inst);
void V_SQRT_F32(const GcnInst& inst); void V_SQRT_F32(const GcnInst& inst);
@ -323,16 +329,18 @@ private:
void LogMissingOpcode(const GcnInst& inst); void LogMissingOpcode(const GcnInst& inst);
IR::VectorReg GetScratchVgpr(u32 offset); IR::VectorReg GetScratchVgpr(u32 offset);
IR::VectorReg GatherInterpQualifiers();
private: private:
IR::IREmitter ir; IR::IREmitter ir;
Info& info; Info& info;
const RuntimeInfo& runtime_info; const RuntimeInfo& runtime_info;
const Profile& profile; const Profile& profile;
u32 next_vgpr_num;
std::unordered_map<u32, IR::VectorReg> vgpr_map;
std::array<IR::Interpolation, MaxInterpVgpr> vgpr_to_interp{};
IR::VectorReg dst_frag_vreg{};
bool opcode_missing = false; bool opcode_missing = false;
}; };
void Translate(IR::Block* block, u32 block_base, std::span<const GcnInst> inst_list, Info& info,
const RuntimeInfo& runtime_info, const Profile& profile);
} // namespace Shader::Gcn } // namespace Shader::Gcn

View File

@ -110,6 +110,8 @@ void Translator::EmitVectorAlu(const GcnInst& inst) {
return V_CVT_I32_F64(inst); return V_CVT_I32_F64(inst);
case Opcode::V_CVT_F64_I32: case Opcode::V_CVT_F64_I32:
return V_CVT_F64_I32(inst); return V_CVT_F64_I32(inst);
case Opcode::V_CVT_F64_U32:
return V_CVT_F64_U32(inst);
case Opcode::V_CVT_F32_I32: case Opcode::V_CVT_F32_I32:
return V_CVT_F32_I32(inst); return V_CVT_F32_I32(inst);
case Opcode::V_CVT_F32_U32: case Opcode::V_CVT_F32_U32:
@ -156,6 +158,8 @@ void Translator::EmitVectorAlu(const GcnInst& inst) {
return V_LOG_F32(inst); return V_LOG_F32(inst);
case Opcode::V_RCP_F32: case Opcode::V_RCP_F32:
return V_RCP_F32(inst); return V_RCP_F32(inst);
case Opcode::V_RCP_LEGACY_F32:
return V_RCP_LEGACY_F32(inst);
case Opcode::V_RCP_F64: case Opcode::V_RCP_F64:
return V_RCP_F64(inst); return V_RCP_F64(inst);
case Opcode::V_RCP_IFLAG_F32: case Opcode::V_RCP_IFLAG_F32:
@ -684,6 +688,11 @@ void Translator::V_CVT_F64_I32(const GcnInst& inst) {
SetDst64(inst.dst[0], ir.ConvertSToF(64, 32, src0)); SetDst64(inst.dst[0], ir.ConvertSToF(64, 32, src0));
} }
void Translator::V_CVT_F64_U32(const GcnInst& inst) {
const IR::U32 src0{GetSrc(inst.src[0])};
SetDst64(inst.dst[0], ir.ConvertUToF(64, 32, src0));
}
void Translator::V_CVT_F32_I32(const GcnInst& inst) { void Translator::V_CVT_F32_I32(const GcnInst& inst) {
const IR::U32 src0{GetSrc(inst.src[0])}; const IR::U32 src0{GetSrc(inst.src[0])};
SetDst(inst.dst[0], ir.ConvertSToF(32, 32, src0)); SetDst(inst.dst[0], ir.ConvertSToF(32, 32, src0));
@ -791,6 +800,20 @@ void Translator::V_RCP_F32(const GcnInst& inst) {
SetDst(inst.dst[0], ir.FPRecip(src0)); SetDst(inst.dst[0], ir.FPRecip(src0));
} }
void Translator::V_RCP_LEGACY_F32(const GcnInst& inst) {
const IR::F32 src0{GetSrc<IR::F32>(inst.src[0])};
const auto result = ir.FPRecip(src0);
const auto inf = ir.FPIsInf(result);
const auto raw_result = ir.ConvertFToU(32, result);
const auto sign_bit = ir.ShiftRightLogical(raw_result, ir.Imm32(31u));
const auto sign_bit_set = ir.INotEqual(sign_bit, ir.Imm32(0u));
const IR::F32 inf_result{ir.Select(sign_bit_set, ir.Imm32(-0.0f), ir.Imm32(0.0f))};
const IR::F32 val{ir.Select(inf, inf_result, result)};
SetDst(inst.dst[0], val);
}
void Translator::V_RCP_F64(const GcnInst& inst) { void Translator::V_RCP_F64(const GcnInst& inst) {
const IR::F64 src0{GetSrc64<IR::F64>(inst.src[0])}; const IR::F64 src0{GetSrc64<IR::F64>(inst.src[0])};
SetDst64(inst.dst[0], ir.FPRecip(src0)); SetDst64(inst.dst[0], ir.FPRecip(src0));

View File

@ -22,13 +22,14 @@ void Translator::EmitVectorInterpolation(const GcnInst& inst) {
// VINTRP // VINTRP
void Translator::V_INTERP_P2_F32(const GcnInst& inst) { void Translator::V_INTERP_P2_F32(const GcnInst& inst) {
auto& attr = runtime_info.fs_info.inputs.at(inst.control.vintrp.attr); const auto& attr = runtime_info.fs_info.inputs.at(inst.control.vintrp.attr);
info.interp_qualifiers[attr.param_index] = vgpr_to_interp[inst.src[0].code];
const IR::Attribute attrib{IR::Attribute::Param0 + attr.param_index}; const IR::Attribute attrib{IR::Attribute::Param0 + attr.param_index};
SetDst(inst.dst[0], ir.GetAttribute(attrib, inst.control.vintrp.chan)); SetDst(inst.dst[0], ir.GetAttribute(attrib, inst.control.vintrp.chan));
} }
void Translator::V_INTERP_MOV_F32(const GcnInst& inst) { void Translator::V_INTERP_MOV_F32(const GcnInst& inst) {
auto& attr = runtime_info.fs_info.inputs.at(inst.control.vintrp.attr); const auto& attr = runtime_info.fs_info.inputs.at(inst.control.vintrp.attr);
const IR::Attribute attrib{IR::Attribute::Param0 + attr.param_index}; const IR::Attribute attrib{IR::Attribute::Param0 + attr.param_index};
SetDst(inst.dst[0], ir.GetAttribute(attrib, inst.control.vintrp.chan)); SetDst(inst.dst[0], ir.GetAttribute(attrib, inst.control.vintrp.chan));
} }

View File

@ -70,6 +70,8 @@ void Translator::EmitVectorMemory(const GcnInst& inst) {
return BUFFER_ATOMIC(AtomicOp::Add, inst); return BUFFER_ATOMIC(AtomicOp::Add, inst);
case Opcode::BUFFER_ATOMIC_SWAP: case Opcode::BUFFER_ATOMIC_SWAP:
return BUFFER_ATOMIC(AtomicOp::Swap, inst); return BUFFER_ATOMIC(AtomicOp::Swap, inst);
case Opcode::BUFFER_ATOMIC_CMPSWAP:
return BUFFER_ATOMIC(AtomicOp::CmpSwap, inst);
case Opcode::BUFFER_ATOMIC_SMIN: case Opcode::BUFFER_ATOMIC_SMIN:
return BUFFER_ATOMIC(AtomicOp::Smin, inst); return BUFFER_ATOMIC(AtomicOp::Smin, inst);
case Opcode::BUFFER_ATOMIC_UMIN: case Opcode::BUFFER_ATOMIC_UMIN:
@ -152,6 +154,7 @@ void Translator::EmitVectorMemory(const GcnInst& inst) {
// Image gather operations // Image gather operations
case Opcode::IMAGE_GATHER4: case Opcode::IMAGE_GATHER4:
case Opcode::IMAGE_GATHER4_L:
case Opcode::IMAGE_GATHER4_LZ: case Opcode::IMAGE_GATHER4_LZ:
case Opcode::IMAGE_GATHER4_C: case Opcode::IMAGE_GATHER4_C:
case Opcode::IMAGE_GATHER4_O: case Opcode::IMAGE_GATHER4_O:
@ -330,6 +333,10 @@ void Translator::BUFFER_ATOMIC(AtomicOp op, const GcnInst& inst) {
switch (op) { switch (op) {
case AtomicOp::Swap: case AtomicOp::Swap:
return ir.BufferAtomicSwap(handle, address, vdata_val, buffer_info); return ir.BufferAtomicSwap(handle, address, vdata_val, buffer_info);
case AtomicOp::CmpSwap: {
const IR::Value cmp_val = ir.GetVectorReg(vdata + 1);
return ir.BufferAtomicCmpSwap(handle, address, vdata_val, cmp_val, buffer_info);
}
case AtomicOp::Add: case AtomicOp::Add:
return ir.BufferAtomicIAdd(handle, address, vdata_val, buffer_info); return ir.BufferAtomicIAdd(handle, address, vdata_val, buffer_info);
case AtomicOp::Smin: case AtomicOp::Smin:
@ -377,6 +384,7 @@ void Translator::IMAGE_LOAD(bool has_mip, const GcnInst& inst) {
IR::TextureInstInfo info{}; IR::TextureInstInfo info{};
info.has_lod.Assign(has_mip); info.has_lod.Assign(has_mip);
info.is_array.Assign(mimg.da); info.is_array.Assign(mimg.da);
info.is_r128.Assign(mimg.r128);
const IR::Value texel = ir.ImageRead(handle, body, {}, {}, info); const IR::Value texel = ir.ImageRead(handle, body, {}, {}, info);
for (u32 i = 0; i < 4; i++) { for (u32 i = 0; i < 4; i++) {
@ -426,6 +434,7 @@ void Translator::IMAGE_GET_RESINFO(const GcnInst& inst) {
IR::TextureInstInfo info{}; IR::TextureInstInfo info{};
info.is_array.Assign(mimg.da); info.is_array.Assign(mimg.da);
info.is_r128.Assign(mimg.r128);
const IR::Value size = ir.ImageQueryDimension(tsharp, lod, ir.Imm1(has_mips), info); const IR::Value size = ir.ImageQueryDimension(tsharp, lod, ir.Imm1(has_mips), info);
@ -451,6 +460,7 @@ void Translator::IMAGE_ATOMIC(AtomicOp op, const GcnInst& inst) {
IR::TextureInstInfo info{}; IR::TextureInstInfo info{};
info.is_array.Assign(mimg.da); info.is_array.Assign(mimg.da);
info.is_r128.Assign(mimg.r128);
const IR::Value value = ir.GetVectorReg(val_reg); const IR::Value value = ir.GetVectorReg(val_reg);
const IR::Value handle = ir.GetScalarReg(tsharp_reg); const IR::Value handle = ir.GetScalarReg(tsharp_reg);
@ -509,6 +519,7 @@ IR::Value EmitImageSample(IR::IREmitter& ir, const GcnInst& inst, const IR::Scal
info.has_lod.Assign(flags.any(MimgModifier::Lod)); info.has_lod.Assign(flags.any(MimgModifier::Lod));
info.is_array.Assign(mimg.da); info.is_array.Assign(mimg.da);
info.is_unnormalized.Assign(mimg.unrm); info.is_unnormalized.Assign(mimg.unrm);
info.is_r128.Assign(mimg.r128);
if (gather) { if (gather) {
info.gather_comp.Assign(std::bit_width(mimg.dmask) - 1); info.gather_comp.Assign(std::bit_width(mimg.dmask) - 1);
@ -617,6 +628,7 @@ void Translator::IMAGE_GET_LOD(const GcnInst& inst) {
IR::TextureInstInfo info{}; IR::TextureInstInfo info{};
info.is_array.Assign(mimg.da); info.is_array.Assign(mimg.da);
info.is_r128.Assign(mimg.r128);
const IR::Value handle = ir.GetScalarReg(tsharp_reg); const IR::Value handle = ir.GetScalarReg(tsharp_reg);
const IR::Value body = ir.CompositeConstruct( const IR::Value body = ir.CompositeConstruct(

View File

@ -84,6 +84,7 @@ struct ImageResource {
bool is_atomic{}; bool is_atomic{};
bool is_array{}; bool is_array{};
bool is_written{}; bool is_written{};
bool is_r128{};
[[nodiscard]] constexpr AmdGpu::Image GetSharp(const Info& info) const noexcept; [[nodiscard]] constexpr AmdGpu::Image GetSharp(const Info& info) const noexcept;
}; };
@ -192,6 +193,8 @@ struct Info {
PersistentSrtInfo srt_info; PersistentSrtInfo srt_info;
std::vector<u32> flattened_ud_buf; std::vector<u32> flattened_ud_buf;
std::array<IR::Interpolation, 32> interp_qualifiers{};
IR::ScalarReg tess_consts_ptr_base = IR::ScalarReg::Max; IR::ScalarReg tess_consts_ptr_base = IR::ScalarReg::Max;
s32 tess_consts_dword_offset = -1; s32 tess_consts_dword_offset = -1;
@ -205,6 +208,8 @@ struct Info {
bool has_discard{}; bool has_discard{};
bool has_image_gather{}; bool has_image_gather{};
bool has_image_query{}; bool has_image_query{};
bool has_perspective_interp{};
bool has_linear_interp{};
bool uses_atomic_float_min_max{}; bool uses_atomic_float_min_max{};
bool uses_lane_id{}; bool uses_lane_id{};
bool uses_group_quad{}; bool uses_group_quad{};
@ -293,7 +298,13 @@ constexpr AmdGpu::Buffer BufferResource::GetSharp(const Info& info) const noexce
} }
constexpr AmdGpu::Image ImageResource::GetSharp(const Info& info) const noexcept { constexpr AmdGpu::Image ImageResource::GetSharp(const Info& info) const noexcept {
const auto image = info.ReadUdSharp<AmdGpu::Image>(sharp_idx); AmdGpu::Image image{0};
if (!is_r128) {
image = info.ReadUdSharp<AmdGpu::Image>(sharp_idx);
} else {
AmdGpu::Buffer buf = info.ReadUdSharp<AmdGpu::Buffer>(sharp_idx);
memcpy(&image, &buf, sizeof(buf));
}
if (!image.Valid()) { if (!image.Valid()) {
// Fall back to null image if unbound. // Fall back to null image if unbound.
return AmdGpu::Image::Null(); return AmdGpu::Image::Null();

View File

@ -83,6 +83,16 @@ enum class Attribute : u64 {
Max, Max,
}; };
enum class Interpolation {
Invalid = 0,
PerspectiveSample = 1,
PerspectiveCenter = 2,
PerspectiveCentroid = 3,
LinearSample = 4,
LinearCenter = 5,
LinearCentroid = 6,
};
constexpr size_t NumAttributes = static_cast<size_t>(Attribute::Max); constexpr size_t NumAttributes = static_cast<size_t>(Attribute::Max);
constexpr size_t NumRenderTargets = 8; constexpr size_t NumRenderTargets = 8;
constexpr size_t NumParams = 32; constexpr size_t NumParams = 32;
@ -104,6 +114,15 @@ constexpr bool IsMrt(Attribute attribute) noexcept {
return attribute >= Attribute::RenderTarget0 && attribute <= Attribute::RenderTarget7; return attribute >= Attribute::RenderTarget0 && attribute <= Attribute::RenderTarget7;
} }
constexpr bool IsLinear(Interpolation interp) noexcept {
return interp >= Interpolation::LinearSample && interp <= Interpolation::LinearCentroid;
}
constexpr bool IsPerspective(Interpolation interp) noexcept {
return interp >= Interpolation::PerspectiveSample &&
interp <= Interpolation::PerspectiveCentroid;
}
[[nodiscard]] std::string NameOf(Attribute attribute); [[nodiscard]] std::string NameOf(Attribute attribute);
[[nodiscard]] constexpr Attribute operator+(Attribute attr, int num) { [[nodiscard]] constexpr Attribute operator+(Attribute attr, int num) {

View File

@ -2,7 +2,6 @@
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
#include <array> #include <array>
#include <bit>
#include <source_location> #include <source_location>
#include <boost/container/small_vector.hpp> #include <boost/container/small_vector.hpp>
#include "common/assert.h" #include "common/assert.h"
@ -513,6 +512,11 @@ Value IREmitter::BufferAtomicSwap(const Value& handle, const Value& address, con
return Inst(Opcode::BufferAtomicSwap32, Flags{info}, handle, address, value); return Inst(Opcode::BufferAtomicSwap32, Flags{info}, handle, address, value);
} }
Value IREmitter::BufferAtomicCmpSwap(const Value& handle, const Value& address, const Value& vdata,
const Value& cmp_value, BufferInstInfo info) {
return Inst(Opcode::BufferAtomicCmpSwap32, Flags{info}, handle, address, vdata, cmp_value);
}
U32 IREmitter::DataAppend(const U32& counter) { U32 IREmitter::DataAppend(const U32& counter) {
return Inst<U32>(Opcode::DataAppend, counter, Imm32(0)); return Inst<U32>(Opcode::DataAppend, counter, Imm32(0));
} }
@ -1546,8 +1550,15 @@ U32 IREmitter::FindSMsb(const U32& value) {
return Inst<U32>(Opcode::FindSMsb32, value); return Inst<U32>(Opcode::FindSMsb32, value);
} }
U32 IREmitter::FindUMsb(const U32& value) { U32 IREmitter::FindUMsb(const U32U64& value) {
return Inst<U32>(Opcode::FindUMsb32, value); switch (value.Type()) {
case Type::U32:
return Inst<U32>(Opcode::FindUMsb32, value);
case Type::U64:
return Inst<U32>(Opcode::FindUMsb64, value);
default:
ThrowInvalidType(value.Type());
}
} }
U32 IREmitter::FindILsb(const U32U64& value) { U32 IREmitter::FindILsb(const U32U64& value) {

View File

@ -6,7 +6,6 @@
#include <cstring> #include <cstring>
#include <type_traits> #include <type_traits>
#include "shader_recompiler/info.h"
#include "shader_recompiler/ir/attribute.h" #include "shader_recompiler/ir/attribute.h"
#include "shader_recompiler/ir/basic_block.h" #include "shader_recompiler/ir/basic_block.h"
#include "shader_recompiler/ir/condition.h" #include "shader_recompiler/ir/condition.h"
@ -17,6 +16,7 @@ namespace Shader::IR {
class IREmitter { class IREmitter {
public: public:
explicit IREmitter() = default;
explicit IREmitter(Block& block_) : block{&block_}, insertion_point{block->end()} {} explicit IREmitter(Block& block_) : block{&block_}, insertion_point{block->end()} {}
explicit IREmitter(Block& block_, Block::iterator insertion_point_) explicit IREmitter(Block& block_, Block::iterator insertion_point_)
: block{&block_}, insertion_point{insertion_point_} {} : block{&block_}, insertion_point{insertion_point_} {}
@ -150,6 +150,9 @@ public:
const Value& value, BufferInstInfo info); const Value& value, BufferInstInfo info);
[[nodiscard]] Value BufferAtomicSwap(const Value& handle, const Value& address, [[nodiscard]] Value BufferAtomicSwap(const Value& handle, const Value& address,
const Value& value, BufferInstInfo info); const Value& value, BufferInstInfo info);
[[nodiscard]] Value BufferAtomicCmpSwap(const Value& handle, const Value& address,
const Value& value, const Value& cmp_value,
BufferInstInfo info);
[[nodiscard]] U32 DataAppend(const U32& counter); [[nodiscard]] U32 DataAppend(const U32& counter);
[[nodiscard]] U32 DataConsume(const U32& counter); [[nodiscard]] U32 DataConsume(const U32& counter);
@ -266,7 +269,7 @@ public:
[[nodiscard]] U32 BitwiseNot(const U32& value); [[nodiscard]] U32 BitwiseNot(const U32& value);
[[nodiscard]] U32 FindSMsb(const U32& value); [[nodiscard]] U32 FindSMsb(const U32& value);
[[nodiscard]] U32 FindUMsb(const U32& value); [[nodiscard]] U32 FindUMsb(const U32U64& value);
[[nodiscard]] U32 FindILsb(const U32U64& value); [[nodiscard]] U32 FindILsb(const U32U64& value);
[[nodiscard]] U32 SMin(const U32& a, const U32& b); [[nodiscard]] U32 SMin(const U32& a, const U32& b);
[[nodiscard]] U32 UMin(const U32& a, const U32& b); [[nodiscard]] U32 UMin(const U32& a, const U32& b);

View File

@ -126,6 +126,7 @@ OPCODE(BufferAtomicAnd32, U32, Opaq
OPCODE(BufferAtomicOr32, U32, Opaque, Opaque, U32, ) OPCODE(BufferAtomicOr32, U32, Opaque, Opaque, U32, )
OPCODE(BufferAtomicXor32, U32, Opaque, Opaque, U32, ) OPCODE(BufferAtomicXor32, U32, Opaque, Opaque, U32, )
OPCODE(BufferAtomicSwap32, U32, Opaque, Opaque, U32, ) OPCODE(BufferAtomicSwap32, U32, Opaque, Opaque, U32, )
OPCODE(BufferAtomicCmpSwap32, U32, Opaque, Opaque, U32, U32, )
// Vector utility // Vector utility
OPCODE(CompositeConstructU32x2, U32x2, U32, U32, ) OPCODE(CompositeConstructU32x2, U32x2, U32, U32, )
@ -349,6 +350,7 @@ OPCODE(BitwiseNot32, U32, U32,
OPCODE(FindSMsb32, U32, U32, ) OPCODE(FindSMsb32, U32, U32, )
OPCODE(FindUMsb32, U32, U32, ) OPCODE(FindUMsb32, U32, U32, )
OPCODE(FindUMsb64, U32, U64, )
OPCODE(FindILsb32, U32, U32, ) OPCODE(FindILsb32, U32, U32, )
OPCODE(FindILsb64, U32, U64, ) OPCODE(FindILsb64, U32, U64, )
OPCODE(SMin32, U32, U32, U32, ) OPCODE(SMin32, U32, U32, U32, )

View File

@ -15,7 +15,7 @@ struct FormatInfo {
AmdGpu::NumberFormat num_format; AmdGpu::NumberFormat num_format;
AmdGpu::CompMapping swizzle; AmdGpu::CompMapping swizzle;
AmdGpu::NumberConversion num_conversion; AmdGpu::NumberConversion num_conversion;
int num_components; u32 num_components;
}; };
static bool IsBufferFormatLoad(const IR::Inst& inst) { static bool IsBufferFormatLoad(const IR::Inst& inst) {

View File

@ -1,6 +1,7 @@
// SPDX-FileCopyrightText: Copyright 2025 shadPS4 Emulator Project // SPDX-FileCopyrightText: Copyright 2025 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
#include <unordered_map>
#include "shader_recompiler/ir/program.h" #include "shader_recompiler/ir/program.h"
namespace Shader::Optimization { namespace Shader::Optimization {

View File

@ -411,6 +411,7 @@ void PatchImageSharp(IR::Block& block, IR::Inst& inst, Info& info, Descriptors&
.is_atomic = IsImageAtomicInstruction(inst), .is_atomic = IsImageAtomicInstruction(inst),
.is_array = bool(inst_info.is_array), .is_array = bool(inst_info.is_array),
.is_written = is_written, .is_written = is_written,
.is_r128 = bool(inst_info.is_r128),
}); });
IR::IREmitter ir{block, IR::Block::InstructionList::s_iterator_to(inst)}; IR::IREmitter ir{block, IR::Block::InstructionList::s_iterator_to(inst)};

View File

@ -91,6 +91,19 @@ void RingAccessElimination(const IR::Program& program, const RuntimeInfo& runtim
const auto& gs_info = runtime_info.gs_info; const auto& gs_info = runtime_info.gs_info;
info.gs_copy_data = Shader::ParseCopyShader(gs_info.vs_copy); info.gs_copy_data = Shader::ParseCopyShader(gs_info.vs_copy);
u32 output_vertices = gs_info.output_vertices;
if (info.gs_copy_data.output_vertices &&
info.gs_copy_data.output_vertices != output_vertices) {
ASSERT_MSG(output_vertices > info.gs_copy_data.output_vertices &&
gs_info.mode == AmdGpu::Liverpool::GsMode::Mode::ScenarioG,
"Invalid geometry shader vertex configuration scenario = {}, max_vert_out = "
"{}, output_vertices = {}",
u32(gs_info.mode), output_vertices, info.gs_copy_data.output_vertices);
LOG_WARNING(Render_Vulkan, "MAX_VERT_OUT {} is larger than actual output vertices {}",
output_vertices, info.gs_copy_data.output_vertices);
output_vertices = info.gs_copy_data.output_vertices;
}
ForEachInstruction([&](IR::IREmitter& ir, IR::Inst& inst) { ForEachInstruction([&](IR::IREmitter& ir, IR::Inst& inst) {
const auto opcode = inst.GetOpcode(); const auto opcode = inst.GetOpcode();
switch (opcode) { switch (opcode) {
@ -122,7 +135,7 @@ void RingAccessElimination(const IR::Program& program, const RuntimeInfo& runtim
const auto offset = inst.Flags<IR::BufferInstInfo>().inst_offset.Value(); const auto offset = inst.Flags<IR::BufferInstInfo>().inst_offset.Value();
const auto data = ir.BitCast<IR::F32>(IR::U32{inst.Arg(2)}); const auto data = ir.BitCast<IR::F32>(IR::U32{inst.Arg(2)});
const auto comp_ofs = gs_info.output_vertices * 4u; const auto comp_ofs = output_vertices * 4u;
const auto output_size = comp_ofs * gs_info.out_vertex_data_size; const auto output_size = comp_ofs * gs_info.out_vertex_data_size;
const auto vc_read_ofs = (((offset / comp_ofs) * comp_ofs) % output_size) * 16u; const auto vc_read_ofs = (((offset / comp_ofs) * comp_ofs) % output_size) * 16u;

View File

@ -7,7 +7,7 @@
#include "common/bit_field.h" #include "common/bit_field.h"
#include "common/enum.h" #include "common/enum.h"
#include "common/types.h" #include "common/types.h"
#include "video_core/amdgpu/types.h" #include "video_core/amdgpu/pixel_format.h"
namespace Shader::IR { namespace Shader::IR {
@ -44,6 +44,7 @@ union TextureInstInfo {
BitField<9, 1, u32> is_array; BitField<9, 1, u32> is_array;
BitField<10, 1, u32> is_unnormalized; BitField<10, 1, u32> is_unnormalized;
BitField<11, 1, u32> is_gather; BitField<11, 1, u32> is_gather;
BitField<12, 1, u32> is_r128;
}; };
union BufferInstInfo { union BufferInstInfo {

View File

@ -149,6 +149,7 @@ struct GeometryRuntimeInfo {
u32 out_vertex_data_size{}; u32 out_vertex_data_size{};
AmdGpu::PrimitiveType in_primitive; AmdGpu::PrimitiveType in_primitive;
GsOutputPrimTypes out_primitive; GsOutputPrimTypes out_primitive;
AmdGpu::Liverpool::GsMode::Mode mode;
std::span<const u32> vs_copy; std::span<const u32> vs_copy;
u64 vs_copy_hash; u64 vs_copy_hash;
@ -196,11 +197,13 @@ struct FragmentRuntimeInfo {
u32 num_inputs; u32 num_inputs;
std::array<PsInput, 32> inputs; std::array<PsInput, 32> inputs;
std::array<PsColorBuffer, MaxColorBuffers> color_buffers; std::array<PsColorBuffer, MaxColorBuffers> color_buffers;
bool dual_source_blending;
bool operator==(const FragmentRuntimeInfo& other) const noexcept { bool operator==(const FragmentRuntimeInfo& other) const noexcept {
return std::ranges::equal(color_buffers, other.color_buffers) && return std::ranges::equal(color_buffers, other.color_buffers) &&
en_flags.raw == other.en_flags.raw && addr_flags.raw == other.addr_flags.raw && en_flags.raw == other.en_flags.raw && addr_flags.raw == other.addr_flags.raw &&
num_inputs == other.num_inputs && num_inputs == other.num_inputs &&
dual_source_blending == other.dual_source_blending &&
std::ranges::equal(inputs.begin(), inputs.begin() + num_inputs, other.inputs.begin(), std::ranges::equal(inputs.begin(), inputs.begin() + num_inputs, other.inputs.begin(),
other.inputs.begin() + num_inputs); other.inputs.begin() + num_inputs);
} }

View File

@ -228,9 +228,12 @@ Liverpool::Task Liverpool::ProcessGraphics(std::span<const u32> dcb, std::span<c
const u32 type = header->type; const u32 type = header->type;
switch (type) { switch (type) {
default:
UNREACHABLE_MSG("Wrong PM4 type {}", type);
break;
case 0: case 0:
case 1: UNREACHABLE_MSG("Unimplemented PM4 type 0, base reg: {}, size: {}",
UNREACHABLE_MSG("Unsupported PM4 type {}", type); header->type0.base.Value(), header->type0.NumWords());
break; break;
case 2: case 2:
// Type-2 packet are used for padding purposes // Type-2 packet are used for padding purposes
@ -394,7 +397,7 @@ Liverpool::Task Liverpool::ProcessGraphics(std::span<const u32> dcb, std::span<c
break; break;
} }
case PM4ItOpcode::SetPredication: { case PM4ItOpcode::SetPredication: {
LOG_WARNING(Render_Vulkan, "Unimplemented IT_SET_PREDICATION"); LOG_WARNING(Render, "Unimplemented IT_SET_PREDICATION");
break; break;
} }
case PM4ItOpcode::IndexType: { case PM4ItOpcode::IndexType: {
@ -586,8 +589,7 @@ Liverpool::Task Liverpool::ProcessGraphics(std::span<const u32> dcb, std::span<c
} }
case PM4ItOpcode::EventWrite: { case PM4ItOpcode::EventWrite: {
const auto* event = reinterpret_cast<const PM4CmdEventWrite*>(header); const auto* event = reinterpret_cast<const PM4CmdEventWrite*>(header);
LOG_DEBUG(Render_Vulkan, LOG_DEBUG(Render, "Encountered EventWrite: event_type = {}, event_index = {}",
"Encountered EventWrite: event_type = {}, event_index = {}",
magic_enum::enum_name(event->event_type.Value()), magic_enum::enum_name(event->event_type.Value()),
magic_enum::enum_name(event->event_index.Value())); magic_enum::enum_name(event->event_index.Value()));
if (event->event_type.Value() == EventType::SoVgtStreamoutFlush) { if (event->event_type.Value() == EventType::SoVgtStreamoutFlush) {
@ -673,6 +675,16 @@ Liverpool::Task Liverpool::ProcessGraphics(std::span<const u32> dcb, std::span<c
} }
break; break;
} }
case PM4ItOpcode::CopyData: {
const auto* copy_data = reinterpret_cast<const PM4CmdCopyData*>(header);
LOG_WARNING(Render,
"unhandled IT_COPY_DATA src_sel = {}, dst_sel = {}, "
"count_sel = {}, wr_confirm = {}, engine_sel = {}",
u32(copy_data->src_sel.Value()), u32(copy_data->dst_sel.Value()),
copy_data->count_sel.Value(), copy_data->wr_confirm.Value(),
u32(copy_data->engine_sel.Value()));
break;
}
case PM4ItOpcode::MemSemaphore: { case PM4ItOpcode::MemSemaphore: {
const auto* mem_semaphore = reinterpret_cast<const PM4CmdMemSemaphore*>(header); const auto* mem_semaphore = reinterpret_cast<const PM4CmdMemSemaphore*>(header);
if (mem_semaphore->IsSignaling()) { if (mem_semaphore->IsSignaling()) {
@ -756,6 +768,19 @@ Liverpool::Task Liverpool::ProcessGraphics(std::span<const u32> dcb, std::span<c
LOG_WARNING(Render_Vulkan, "Unimplemented IT_GET_LOD_STATS"); LOG_WARNING(Render_Vulkan, "Unimplemented IT_GET_LOD_STATS");
break; break;
} }
case PM4ItOpcode::CondExec: {
const auto* cond_exec = reinterpret_cast<const PM4CmdCondExec*>(header);
if (cond_exec->command.Value() != 0) {
LOG_WARNING(Render, "IT_COND_EXEC used a reserved command");
}
const auto skip = *cond_exec->Address() == false;
if (skip) {
dcb = NextPacket(dcb,
header->type3.NumWords() + 1 + cond_exec->exec_count.Value());
continue;
}
break;
}
default: default:
UNREACHABLE_MSG("Unknown PM4 type 3 opcode {:#x} with count {}", UNREACHABLE_MSG("Unknown PM4 type 3 opcode {:#x} with count {}",
static_cast<u32>(opcode), count); static_cast<u32>(opcode), count);
@ -804,6 +829,19 @@ Liverpool::Task Liverpool::ProcessCompute(const u32* acb, u32 acb_dwords, u32 vq
break; break;
} }
if (header->type == 2) {
// Type-2 packet are used for padding purposes
next_dw_off = 1;
acb += next_dw_off;
acb_dwords -= next_dw_off;
if constexpr (!is_indirect) {
*queue.read_addr += next_dw_off;
*queue.read_addr %= queue.ring_size_dw;
}
continue;
}
if (header->type != 3) { if (header->type != 3) {
// No other types of packets were spotted so far // No other types of packets were spotted so far
UNREACHABLE_MSG("Invalid PM4 type {}", header->type.Value()); UNREACHABLE_MSG("Invalid PM4 type {}", header->type.Value());

View File

@ -608,6 +608,16 @@ struct Liverpool {
} }
}; };
struct BorderColorBufferBase {
u32 base_addr_lo;
BitField<0, 8, u32> base_addr_hi;
template <typename T = VAddr>
T Address() const {
return std::bit_cast<T>(u64(base_addr_hi) << 40 | u64(base_addr_lo) << 8);
}
};
struct IndexBufferBase { struct IndexBufferBase {
BitField<0, 8, u32> base_addr_hi; BitField<0, 8, u32> base_addr_hi;
u32 base_addr_lo; u32 base_addr_lo;
@ -904,7 +914,7 @@ struct Liverpool {
} }
size_t GetColorSliceSize() const { size_t GetColorSliceSize() const {
const auto num_bytes_per_element = NumBits(info.format) / 8u; const auto num_bytes_per_element = NumBitsPerBlock(info.format) / 8u;
const auto slice_size = const auto slice_size =
num_bytes_per_element * (slice.tile_max + 1) * 64u * NumSamples(); num_bytes_per_element * (slice.tile_max + 1) * 64u * NumSamples();
return slice_size; return slice_size;
@ -1169,8 +1179,16 @@ struct Liverpool {
}; };
union GsMode { union GsMode {
enum class Mode : u32 {
Off = 0,
ScenarioA = 1,
ScenarioB = 2,
ScenarioG = 3,
ScenarioC = 4,
};
u32 raw; u32 raw;
BitField<0, 3, u32> mode; BitField<0, 3, Mode> mode;
BitField<3, 2, u32> cut_mode; BitField<3, 2, u32> cut_mode;
BitField<22, 2, u32> onchip; BitField<22, 2, u32> onchip;
}; };
@ -1299,7 +1317,9 @@ struct Liverpool {
Scissor screen_scissor; Scissor screen_scissor;
INSERT_PADDING_WORDS(0xA010 - 0xA00C - 2); INSERT_PADDING_WORDS(0xA010 - 0xA00C - 2);
DepthBuffer depth_buffer; DepthBuffer depth_buffer;
INSERT_PADDING_WORDS(0xA080 - 0xA018); INSERT_PADDING_WORDS(8);
BorderColorBufferBase ta_bc_base;
INSERT_PADDING_WORDS(0xA080 - 0xA020 - 2);
WindowOffset window_offset; WindowOffset window_offset;
ViewportScissor window_scissor; ViewportScissor window_scissor;
INSERT_PADDING_WORDS(0xA08E - 0xA081 - 2); INSERT_PADDING_WORDS(0xA08E - 0xA081 - 2);
@ -1626,6 +1646,7 @@ static_assert(GFX6_3D_REG_INDEX(depth_htile_data_base) == 0xA005);
static_assert(GFX6_3D_REG_INDEX(screen_scissor) == 0xA00C); static_assert(GFX6_3D_REG_INDEX(screen_scissor) == 0xA00C);
static_assert(GFX6_3D_REG_INDEX(depth_buffer.z_info) == 0xA010); static_assert(GFX6_3D_REG_INDEX(depth_buffer.z_info) == 0xA010);
static_assert(GFX6_3D_REG_INDEX(depth_buffer.depth_slice) == 0xA017); static_assert(GFX6_3D_REG_INDEX(depth_buffer.depth_slice) == 0xA017);
static_assert(GFX6_3D_REG_INDEX(ta_bc_base) == 0xA020);
static_assert(GFX6_3D_REG_INDEX(window_offset) == 0xA080); static_assert(GFX6_3D_REG_INDEX(window_offset) == 0xA080);
static_assert(GFX6_3D_REG_INDEX(window_scissor) == 0xA081); static_assert(GFX6_3D_REG_INDEX(window_scissor) == 0xA081);
static_assert(GFX6_3D_REG_INDEX(color_target_mask) == 0xA08E); static_assert(GFX6_3D_REG_INDEX(color_target_mask) == 0xA08E);

View File

@ -111,136 +111,106 @@ std::string_view NameOf(NumberFormat fmt) {
} }
} }
int NumComponents(DataFormat format) { static constexpr std::array NUM_COMPONENTS = {
constexpr std::array num_components_per_element = { 0, // 0 FormatInvalid
0, 1, 1, 2, 1, 2, 3, 3, 4, 4, 4, 2, 4, 3, 4, -1, 3, 4, 4, 4, 2, 1, // 1 Format8
2, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, 3, 3, 3, 4, 4, 4, 1, 2, 3, 4, 1, // 2 Format16
-1, -1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 1, 1}; 2, // 3 Format8_8
1, // 4 Format32
const u32 index = static_cast<u32>(format); 2, // 5 Format16_16
if (index >= num_components_per_element.size()) { 3, // 6 Format10_11_11
return 0; 3, // 7 Format11_11_10
} 4, // 8 Format10_10_10_2
return num_components_per_element[index]; 4, // 9 Format2_10_10_10
} 4, // 10 Format8_8_8_8
2, // 11 Format32_32
int NumBits(DataFormat format) { 4, // 12 Format16_16_16_16
const std::array num_bits_per_element = { 3, // 13 Format32_32_32
0, 8, 16, 16, 32, 32, 32, 32, 32, 32, 32, 64, 64, 96, 128, -1, 16, 16, 16, 16, 32, 4, // 14 Format32_32_32_32
32, 64, -1, -1, -1, -1, -1, -1, -1, -1, -1, 16, 16, 32, 4, 8, 8, 4, 8, 8, 8, 0, // 15
-1, -1, 8, 8, 8, 8, 8, 8, 16, 16, 32, 32, 32, 64, 64, 8, 16, 1, 1}; 3, // 16 Format5_6_5
4, // 17 Format1_5_5_5
const u32 index = static_cast<u32>(format); 4, // 18 Format5_5_5_1
if (index >= num_bits_per_element.size()) { 4, // 19 Format4_4_4_4
return 0; 2, // 20 Format8_24
} 2, // 21 Format24_8
return num_bits_per_element[index]; 2, // 22 FormatX24_8_32
} 0, // 23
0, // 24
static constexpr std::array component_bits = { 0, // 25
std::array{0, 0, 0, 0}, // 0 FormatInvalid 0, // 26
std::array{8, 0, 0, 0}, // 1 Format8 0, // 27
std::array{16, 0, 0, 0}, // 2 Format16 0, // 28
std::array{8, 8, 0, 0}, // 3 Format8_8 0, // 29
std::array{32, 0, 0, 0}, // 4 Format32 0, // 30
std::array{16, 16, 0, 0}, // 5 Format16_16 0, // 31
std::array{11, 11, 10, 0}, // 6 Format10_11_11 3, // 32 FormatGB_GR
std::array{10, 11, 11, 0}, // 7 Format11_11_10 3, // 33 FormatBG_RG
std::array{2, 10, 10, 10}, // 8 Format10_10_10_2 4, // 34 Format5_9_9_9
std::array{10, 10, 10, 2}, // 9 Format2_10_10_10 4, // 35 FormatBc1
std::array{8, 8, 8, 8}, // 10 Format8_8_8_8 4, // 36 FormatBc2
std::array{32, 32, 0, 0}, // 11 Format32_32 4, // 37 FormatBc3
std::array{16, 16, 16, 16}, // 12 Format16_16_16_16 1, // 38 FormatBc4
std::array{32, 32, 32, 0}, // 13 Format32_32_32 2, // 39 FormatBc5
std::array{32, 32, 32, 32}, // 14 Format32_32_32_32 3, // 40 FormatBc6
std::array{0, 0, 0, 0}, // 15 4, // 41 FormatBc7
std::array{5, 6, 5, 0}, // 16 Format5_6_5
std::array{5, 5, 5, 1}, // 17 Format1_5_5_5
std::array{1, 5, 5, 5}, // 18 Format5_5_5_1
std::array{4, 4, 4, 4}, // 19 Format4_4_4_4
std::array{24, 8, 0, 0}, // 20 Format8_24
std::array{8, 24, 0, 0}, // 21 Format24_8
std::array{8, 24, 0, 0}, // 22 FormatX24_8_32
std::array{0, 0, 0, 0}, // 23
std::array{0, 0, 0, 0}, // 24
std::array{0, 0, 0, 0}, // 25
std::array{0, 0, 0, 0}, // 26
std::array{0, 0, 0, 0}, // 27
std::array{0, 0, 0, 0}, // 28
std::array{0, 0, 0, 0}, // 29
std::array{0, 0, 0, 0}, // 30
std::array{0, 0, 0, 0}, // 31
std::array{0, 0, 0, 0}, // 32 FormatGB_GR
std::array{0, 0, 0, 0}, // 33 FormatBG_RG
std::array{0, 0, 0, 0}, // 34 Format5_9_9_9
std::array{0, 0, 0, 0}, // 35 FormatBc1
std::array{0, 0, 0, 0}, // 36 FormatBc2
std::array{0, 0, 0, 0}, // 37 FormatBc3
std::array{0, 0, 0, 0}, // 38 FormatBc4
std::array{0, 0, 0, 0}, // 39 FormatBc5
std::array{0, 0, 0, 0}, // 40 FormatBc6
std::array{0, 0, 0, 0}, // 41 FormatBc7
}; };
u32 ComponentBits(DataFormat format, u32 comp) { u32 NumComponents(DataFormat format) {
const u32 index = static_cast<u32>(format); const u32 index = static_cast<u32>(format);
if (index >= component_bits.size() || comp >= 4) { ASSERT_MSG(index < NUM_COMPONENTS.size(), "Invalid data format = {}", format);
return 0; return NUM_COMPONENTS[index];
}
return component_bits[index][comp];
} }
static constexpr std::array component_offset = { static constexpr std::array BITS_PER_BLOCK = {
std::array{-1, -1, -1, -1}, // 0 FormatInvalid 0, // 0 FormatInvalid
std::array{0, -1, -1, -1}, // 1 Format8 8, // 1 Format8
std::array{0, -1, -1, -1}, // 2 Format16 16, // 2 Format16
std::array{0, 8, -1, -1}, // 3 Format8_8 16, // 3 Format8_8
std::array{0, -1, -1, -1}, // 4 Format32 32, // 4 Format32
std::array{0, 16, -1, -1}, // 5 Format16_16 32, // 5 Format16_16
std::array{0, 11, 22, -1}, // 6 Format10_11_11 32, // 6 Format10_11_11
std::array{0, 10, 21, -1}, // 7 Format11_11_10 32, // 7 Format11_11_10
std::array{0, 2, 12, 22}, // 8 Format10_10_10_2 32, // 8 Format10_10_10_2
std::array{0, 10, 20, 30}, // 9 Format2_10_10_10 32, // 9 Format2_10_10_10
std::array{0, 8, 16, 24}, // 10 Format8_8_8_8 32, // 10 Format8_8_8_8
std::array{0, 32, -1, -1}, // 11 Format32_32 64, // 11 Format32_32
std::array{0, 16, 32, 48}, // 12 Format16_16_16_16 64, // 12 Format16_16_16_16
std::array{0, 32, 64, -1}, // 13 Format32_32_32 96, // 13 Format32_32_32
std::array{0, 32, 64, 96}, // 14 Format32_32_32_32 128, // 14 Format32_32_32_32
std::array{-1, -1, -1, -1}, // 15 0, // 15
std::array{0, 5, 11, -1}, // 16 Format5_6_5 16, // 16 Format5_6_5
std::array{0, 5, 10, 15}, // 17 Format1_5_5_5 16, // 17 Format1_5_5_5
std::array{0, 1, 6, 11}, // 18 Format5_5_5_1 16, // 18 Format5_5_5_1
std::array{0, 4, 8, 12}, // 19 Format4_4_4_4 16, // 19 Format4_4_4_4
std::array{0, 24, -1, -1}, // 20 Format8_24 32, // 20 Format8_24
std::array{0, 8, -1, -1}, // 21 Format24_8 32, // 21 Format24_8
std::array{0, 8, -1, -1}, // 22 FormatX24_8_32 64, // 22 FormatX24_8_32
std::array{-1, -1, -1, -1}, // 23 0, // 23
std::array{-1, -1, -1, -1}, // 24 0, // 24
std::array{-1, -1, -1, -1}, // 25 0, // 25
std::array{-1, -1, -1, -1}, // 26 0, // 26
std::array{-1, -1, -1, -1}, // 27 0, // 27
std::array{-1, -1, -1, -1}, // 28 0, // 28
std::array{-1, -1, -1, -1}, // 29 0, // 29
std::array{-1, -1, -1, -1}, // 30 0, // 30
std::array{-1, -1, -1, -1}, // 31 0, // 31
std::array{-1, -1, -1, -1}, // 32 FormatGB_GR 16, // 32 FormatGB_GR
std::array{-1, -1, -1, -1}, // 33 FormatBG_RG 16, // 33 FormatBG_RG
std::array{-1, -1, -1, -1}, // 34 Format5_9_9_9 32, // 34 Format5_9_9_9
std::array{-1, -1, -1, -1}, // 35 FormatBc1 64, // 35 FormatBc1
std::array{-1, -1, -1, -1}, // 36 FormatBc2 128, // 36 FormatBc2
std::array{-1, -1, -1, -1}, // 37 FormatBc3 128, // 37 FormatBc3
std::array{-1, -1, -1, -1}, // 38 FormatBc4 64, // 38 FormatBc4
std::array{-1, -1, -1, -1}, // 39 FormatBc5 128, // 39 FormatBc5
std::array{-1, -1, -1, -1}, // 40 FormatBc6 128, // 40 FormatBc6
std::array{-1, -1, -1, -1}, // 41 FormatBc7 128, // 41 FormatBc7
}; };
s32 ComponentOffset(DataFormat format, u32 comp) { u32 NumBitsPerBlock(DataFormat format) {
const u32 index = static_cast<u32>(format); const u32 index = static_cast<u32>(format);
if (index >= component_offset.size() || comp >= 4) { ASSERT_MSG(index < BITS_PER_BLOCK.size(), "Invalid data format = {}", format);
return -1; return BITS_PER_BLOCK[index];
}
return component_offset[index][comp];
} }
} // namespace AmdGpu } // namespace AmdGpu

View File

@ -5,39 +5,313 @@
#include <string_view> #include <string_view>
#include <fmt/format.h> #include <fmt/format.h>
#include "common/assert.h"
#include "common/types.h" #include "common/types.h"
#include "video_core/amdgpu/types.h"
namespace AmdGpu { namespace AmdGpu {
enum NumberClass { // Table 8.13 Data and Image Formats [Sea Islands Series Instruction Set Architecture]
enum class DataFormat : u32 {
FormatInvalid = 0,
Format8 = 1,
Format16 = 2,
Format8_8 = 3,
Format32 = 4,
Format16_16 = 5,
Format10_11_11 = 6,
Format11_11_10 = 7,
Format10_10_10_2 = 8,
Format2_10_10_10 = 9,
Format8_8_8_8 = 10,
Format32_32 = 11,
Format16_16_16_16 = 12,
Format32_32_32 = 13,
Format32_32_32_32 = 14,
Format5_6_5 = 16,
Format1_5_5_5 = 17,
Format5_5_5_1 = 18,
Format4_4_4_4 = 19,
Format8_24 = 20,
Format24_8 = 21,
FormatX24_8_32 = 22,
FormatGB_GR = 32,
FormatBG_RG = 33,
Format5_9_9_9 = 34,
FormatBc1 = 35,
FormatBc2 = 36,
FormatBc3 = 37,
FormatBc4 = 38,
FormatBc5 = 39,
FormatBc6 = 40,
FormatBc7 = 41,
FormatFmask8_1 = 47,
FormatFmask8_2 = 48,
FormatFmask8_4 = 49,
FormatFmask16_1 = 50,
FormatFmask16_2 = 51,
FormatFmask32_2 = 52,
FormatFmask32_4 = 53,
FormatFmask32_8 = 54,
FormatFmask64_4 = 55,
FormatFmask64_8 = 56,
Format4_4 = 57,
Format6_5_5 = 58,
Format1 = 59,
Format1_Reversed = 60,
Format32_As_8 = 61,
Format32_As_8_8 = 62,
Format32_As_32_32_32_32 = 63,
};
enum class NumberFormat : u32 {
Unorm = 0,
Snorm = 1,
Uscaled = 2,
Sscaled = 3,
Uint = 4,
Sint = 5,
SnormNz = 6,
Float = 7,
Srgb = 9,
Ubnorm = 10,
UbnormNz = 11,
Ubint = 12,
Ubscaled = 13,
};
enum class NumberClass {
Float, Float,
Sint, Sint,
Uint, Uint,
}; };
[[nodiscard]] constexpr NumberClass GetNumberClass(const NumberFormat nfmt) { enum class CompSwizzle : u8 {
switch (nfmt) { Zero = 0,
case NumberFormat::Sint: One = 1,
return Sint; Red = 4,
case NumberFormat::Uint: Green = 5,
return Uint; Blue = 6,
Alpha = 7,
};
enum class NumberConversion : u32 {
None = 0,
UintToUscaled = 1,
SintToSscaled = 2,
UnormToUbnorm = 3,
Sint8ToSnormNz = 4,
Sint16ToSnormNz = 5,
Uint32ToUnorm = 6,
};
struct CompMapping {
CompSwizzle r;
CompSwizzle g;
CompSwizzle b;
CompSwizzle a;
auto operator<=>(const CompMapping& other) const = default;
template <typename T>
[[nodiscard]] std::array<T, 4> Apply(const std::array<T, 4>& data) const {
return {
ApplySingle(data, r),
ApplySingle(data, g),
ApplySingle(data, b),
ApplySingle(data, a),
};
}
[[nodiscard]] CompMapping Inverse() const {
CompMapping result{};
InverseSingle(result.r, CompSwizzle::Red);
InverseSingle(result.g, CompSwizzle::Green);
InverseSingle(result.b, CompSwizzle::Blue);
InverseSingle(result.a, CompSwizzle::Alpha);
return result;
}
private:
template <typename T>
T ApplySingle(const std::array<T, 4>& data, const CompSwizzle swizzle) const {
switch (swizzle) {
case CompSwizzle::Zero:
return T(0);
case CompSwizzle::One:
return T(1);
case CompSwizzle::Red:
return data[0];
case CompSwizzle::Green:
return data[1];
case CompSwizzle::Blue:
return data[2];
case CompSwizzle::Alpha:
return data[3];
default:
UNREACHABLE();
}
}
void InverseSingle(CompSwizzle& dst, const CompSwizzle target) const {
if (r == target) {
dst = CompSwizzle::Red;
} else if (g == target) {
dst = CompSwizzle::Green;
} else if (b == target) {
dst = CompSwizzle::Blue;
} else if (a == target) {
dst = CompSwizzle::Alpha;
} else {
dst = CompSwizzle::Zero;
}
}
};
static constexpr CompMapping IdentityMapping = {
.r = CompSwizzle::Red,
.g = CompSwizzle::Green,
.b = CompSwizzle::Blue,
.a = CompSwizzle::Alpha,
};
constexpr DataFormat RemapDataFormat(const DataFormat format) {
switch (format) {
case DataFormat::Format11_11_10:
return DataFormat::Format10_11_11;
case DataFormat::Format10_10_10_2:
return DataFormat::Format2_10_10_10;
case DataFormat::Format5_5_5_1:
return DataFormat::Format1_5_5_5;
default: default:
return Float; return format;
} }
} }
[[nodiscard]] constexpr bool IsInteger(const NumberFormat nfmt) { constexpr NumberFormat RemapNumberFormat(const NumberFormat format, const DataFormat data_format) {
switch (format) {
case NumberFormat::Unorm: {
switch (data_format) {
case DataFormat::Format32:
case DataFormat::Format32_32:
case DataFormat::Format32_32_32:
case DataFormat::Format32_32_32_32:
return NumberFormat::Uint;
default:
return format;
}
}
case NumberFormat::Uscaled:
return NumberFormat::Uint;
case NumberFormat::Sscaled:
case NumberFormat::SnormNz:
return NumberFormat::Sint;
case NumberFormat::Ubnorm:
return NumberFormat::Unorm;
case NumberFormat::Float:
if (data_format == DataFormat::Format8) {
// Games may ask for 8-bit float when they want to access the stencil component
// of a depth-stencil image. Change to unsigned int to match the stencil format.
// This is also the closest approximation to pass the bits through unconverted.
return NumberFormat::Uint;
}
[[fallthrough]];
default:
return format;
}
}
constexpr CompMapping RemapSwizzle(const DataFormat format, const CompMapping swizzle) {
switch (format) {
case DataFormat::Format1_5_5_5:
case DataFormat::Format11_11_10: {
CompMapping result;
result.r = swizzle.b;
result.g = swizzle.g;
result.b = swizzle.r;
result.a = swizzle.a;
return result;
}
case DataFormat::Format10_10_10_2: {
CompMapping result;
result.r = swizzle.a;
result.g = swizzle.b;
result.b = swizzle.g;
result.a = swizzle.r;
return result;
}
case DataFormat::Format4_4_4_4: {
// Remap to a more supported component order.
CompMapping result;
result.r = swizzle.g;
result.g = swizzle.b;
result.b = swizzle.a;
result.a = swizzle.r;
return result;
}
default:
return swizzle;
}
}
constexpr NumberConversion MapNumberConversion(const NumberFormat num_fmt,
const DataFormat data_fmt) {
switch (num_fmt) {
case NumberFormat::Unorm: {
switch (data_fmt) {
case DataFormat::Format32:
case DataFormat::Format32_32:
case DataFormat::Format32_32_32:
case DataFormat::Format32_32_32_32:
return NumberConversion::Uint32ToUnorm;
default:
return NumberConversion::None;
}
}
case NumberFormat::Uscaled:
return NumberConversion::UintToUscaled;
case NumberFormat::Sscaled:
return NumberConversion::SintToSscaled;
case NumberFormat::Ubnorm:
return NumberConversion::UnormToUbnorm;
case NumberFormat::SnormNz: {
switch (data_fmt) {
case DataFormat::Format8:
case DataFormat::Format8_8:
case DataFormat::Format8_8_8_8:
return NumberConversion::Sint8ToSnormNz;
case DataFormat::Format16:
case DataFormat::Format16_16:
case DataFormat::Format16_16_16_16:
return NumberConversion::Sint16ToSnormNz;
default:
UNREACHABLE_MSG("data_fmt = {}", u32(data_fmt));
}
}
default:
return NumberConversion::None;
}
}
constexpr NumberClass GetNumberClass(const NumberFormat nfmt) {
switch (nfmt) {
case NumberFormat::Sint:
return NumberClass::Sint;
case NumberFormat::Uint:
return NumberClass::Uint;
default:
return NumberClass::Float;
}
}
constexpr bool IsInteger(const NumberFormat nfmt) {
return nfmt == AmdGpu::NumberFormat::Sint || nfmt == AmdGpu::NumberFormat::Uint; return nfmt == AmdGpu::NumberFormat::Sint || nfmt == AmdGpu::NumberFormat::Uint;
} }
[[nodiscard]] std::string_view NameOf(DataFormat fmt); std::string_view NameOf(DataFormat fmt);
[[nodiscard]] std::string_view NameOf(NumberFormat fmt); std::string_view NameOf(NumberFormat fmt);
int NumComponents(DataFormat format); u32 NumComponents(DataFormat format);
int NumBits(DataFormat format); u32 NumBitsPerBlock(DataFormat format);
u32 ComponentBits(DataFormat format, u32 comp);
s32 ComponentOffset(DataFormat format, u32 comp);
} // namespace AmdGpu } // namespace AmdGpu

View File

@ -554,6 +554,61 @@ struct PM4DmaData {
} }
}; };
enum class CopyDataSrc : u32 {
MappedRegister = 0,
Memory = 1,
TCL2 = 2,
Gds = 3,
// Reserved = 4,
Immediate = 5,
Atomic = 6,
GdsAtomic0 = 7,
GdsAtomic1 = 8,
GpuClock = 9,
};
enum class CopyDataDst : u32 {
MappedRegister = 0,
MemorySync = 1,
TCL2 = 2,
Gds = 3,
// Reserved = 4,
MemoryAsync = 5,
};
enum class CopyDataEngine : u32 {
Me = 0,
Pfp = 1,
Ce = 2,
// Reserved = 3
};
struct PM4CmdCopyData {
PM4Type3Header header;
union {
BitField<0, 4, CopyDataSrc> src_sel;
BitField<8, 4, CopyDataDst> dst_sel;
BitField<16, 1, u32> count_sel;
BitField<20, 1, u32> wr_confirm;
BitField<30, 2, CopyDataEngine> engine_sel;
u32 control;
};
u32 src_addr_lo;
u32 src_addr_hi;
u32 dst_addr_lo;
u32 dst_addr_hi;
template <typename T>
T SrcAddress() const {
return std::bit_cast<T>(src_addr_lo | u64(src_addr_hi) << 32);
}
template <typename T>
T DstAddress() const {
return std::bit_cast<T>(dst_addr_lo | u64(dst_addr_hi) << 32);
}
};
struct PM4CmdRewind { struct PM4CmdRewind {
PM4Type3Header header; PM4Type3Header header;
union { union {
@ -1104,4 +1159,25 @@ struct PM4CmdMemSemaphore {
} }
}; };
struct PM4CmdCondExec {
PM4Type3Header header;
union {
BitField<2, 30, u32> bool_addr_lo; ///< low 32 address bits for the block in memory from
///< where the CP will fetch the condition
};
union {
BitField<0, 16, u32> bool_addr_hi; ///< high address bits for the condition
BitField<28, 4, u32> command;
};
union {
BitField<0, 14, u32> exec_count; ///< Number of DWords that the CP will skip
///< if bool pointed to is zero
};
bool* Address() const {
return std::bit_cast<bool*>(u64(bool_addr_hi.Value()) << 32 | u64(bool_addr_lo.Value())
<< 2);
}
};
} // namespace AmdGpu } // namespace AmdGpu

View File

@ -6,7 +6,6 @@
#include "common/alignment.h" #include "common/alignment.h"
#include "common/assert.h" #include "common/assert.h"
#include "common/bit_field.h" #include "common/bit_field.h"
#include "common/types.h"
#include "video_core/amdgpu/pixel_format.h" #include "video_core/amdgpu/pixel_format.h"
namespace AmdGpu { namespace AmdGpu {

View File

@ -5,7 +5,6 @@
#include <string_view> #include <string_view>
#include <fmt/format.h> #include <fmt/format.h>
#include "common/assert.h"
#include "common/types.h" #include "common/types.h"
namespace AmdGpu { namespace AmdGpu {
@ -114,281 +113,6 @@ enum class GsOutputPrimitiveType : u32 {
TriangleStrip = 2, TriangleStrip = 2,
}; };
// Table 8.13 Data and Image Formats [Sea Islands Series Instruction Set Architecture]
enum class DataFormat : u32 {
FormatInvalid = 0,
Format8 = 1,
Format16 = 2,
Format8_8 = 3,
Format32 = 4,
Format16_16 = 5,
Format10_11_11 = 6,
Format11_11_10 = 7,
Format10_10_10_2 = 8,
Format2_10_10_10 = 9,
Format8_8_8_8 = 10,
Format32_32 = 11,
Format16_16_16_16 = 12,
Format32_32_32 = 13,
Format32_32_32_32 = 14,
Format5_6_5 = 16,
Format1_5_5_5 = 17,
Format5_5_5_1 = 18,
Format4_4_4_4 = 19,
Format8_24 = 20,
Format24_8 = 21,
FormatX24_8_32 = 22,
FormatGB_GR = 32,
FormatBG_RG = 33,
Format5_9_9_9 = 34,
FormatBc1 = 35,
FormatBc2 = 36,
FormatBc3 = 37,
FormatBc4 = 38,
FormatBc5 = 39,
FormatBc6 = 40,
FormatBc7 = 41,
FormatFmask8_1 = 47,
FormatFmask8_2 = 48,
FormatFmask8_4 = 49,
FormatFmask16_1 = 50,
FormatFmask16_2 = 51,
FormatFmask32_2 = 52,
FormatFmask32_4 = 53,
FormatFmask32_8 = 54,
FormatFmask64_4 = 55,
FormatFmask64_8 = 56,
Format4_4 = 57,
Format6_5_5 = 58,
Format1 = 59,
Format1_Reversed = 60,
Format32_As_8 = 61,
Format32_As_8_8 = 62,
Format32_As_32_32_32_32 = 63,
};
enum class NumberFormat : u32 {
Unorm = 0,
Snorm = 1,
Uscaled = 2,
Sscaled = 3,
Uint = 4,
Sint = 5,
SnormNz = 6,
Float = 7,
Srgb = 9,
Ubnorm = 10,
UbnormNz = 11,
Ubint = 12,
Ubscaled = 13,
};
enum class CompSwizzle : u8 {
Zero = 0,
One = 1,
Red = 4,
Green = 5,
Blue = 6,
Alpha = 7,
};
enum class NumberConversion : u32 {
None = 0,
UintToUscaled = 1,
SintToSscaled = 2,
UnormToUbnorm = 3,
Sint8ToSnormNz = 4,
Sint16ToSnormNz = 5,
Uint32ToUnorm = 6,
};
struct CompMapping {
CompSwizzle r;
CompSwizzle g;
CompSwizzle b;
CompSwizzle a;
auto operator<=>(const CompMapping& other) const = default;
template <typename T>
[[nodiscard]] std::array<T, 4> Apply(const std::array<T, 4>& data) const {
return {
ApplySingle(data, r),
ApplySingle(data, g),
ApplySingle(data, b),
ApplySingle(data, a),
};
}
[[nodiscard]] CompMapping Inverse() const {
CompMapping result{};
InverseSingle(result.r, CompSwizzle::Red);
InverseSingle(result.g, CompSwizzle::Green);
InverseSingle(result.b, CompSwizzle::Blue);
InverseSingle(result.a, CompSwizzle::Alpha);
return result;
}
private:
template <typename T>
T ApplySingle(const std::array<T, 4>& data, const CompSwizzle swizzle) const {
switch (swizzle) {
case CompSwizzle::Zero:
return T(0);
case CompSwizzle::One:
return T(1);
case CompSwizzle::Red:
return data[0];
case CompSwizzle::Green:
return data[1];
case CompSwizzle::Blue:
return data[2];
case CompSwizzle::Alpha:
return data[3];
default:
UNREACHABLE();
}
}
void InverseSingle(CompSwizzle& dst, const CompSwizzle target) const {
if (r == target) {
dst = CompSwizzle::Red;
} else if (g == target) {
dst = CompSwizzle::Green;
} else if (b == target) {
dst = CompSwizzle::Blue;
} else if (a == target) {
dst = CompSwizzle::Alpha;
} else {
dst = CompSwizzle::Zero;
}
}
};
static constexpr CompMapping IdentityMapping = {
.r = CompSwizzle::Red,
.g = CompSwizzle::Green,
.b = CompSwizzle::Blue,
.a = CompSwizzle::Alpha,
};
inline DataFormat RemapDataFormat(const DataFormat format) {
switch (format) {
case DataFormat::Format11_11_10:
return DataFormat::Format10_11_11;
case DataFormat::Format10_10_10_2:
return DataFormat::Format2_10_10_10;
case DataFormat::Format5_5_5_1:
return DataFormat::Format1_5_5_5;
default:
return format;
}
}
inline NumberFormat RemapNumberFormat(const NumberFormat format, const DataFormat data_format) {
switch (format) {
case NumberFormat::Unorm: {
switch (data_format) {
case DataFormat::Format32:
case DataFormat::Format32_32:
case DataFormat::Format32_32_32:
case DataFormat::Format32_32_32_32:
return NumberFormat::Uint;
default:
return format;
}
}
case NumberFormat::Uscaled:
return NumberFormat::Uint;
case NumberFormat::Sscaled:
case NumberFormat::SnormNz:
return NumberFormat::Sint;
case NumberFormat::Ubnorm:
return NumberFormat::Unorm;
case NumberFormat::Float:
if (data_format == DataFormat::Format8) {
// Games may ask for 8-bit float when they want to access the stencil component
// of a depth-stencil image. Change to unsigned int to match the stencil format.
// This is also the closest approximation to pass the bits through unconverted.
return NumberFormat::Uint;
}
[[fallthrough]];
default:
return format;
}
}
inline CompMapping RemapSwizzle(const DataFormat format, const CompMapping swizzle) {
switch (format) {
case DataFormat::Format1_5_5_5:
case DataFormat::Format11_11_10: {
CompMapping result;
result.r = swizzle.b;
result.g = swizzle.g;
result.b = swizzle.r;
result.a = swizzle.a;
return result;
}
case DataFormat::Format10_10_10_2: {
CompMapping result;
result.r = swizzle.a;
result.g = swizzle.b;
result.b = swizzle.g;
result.a = swizzle.r;
return result;
}
case DataFormat::Format4_4_4_4: {
// Remap to a more supported component order.
CompMapping result;
result.r = swizzle.g;
result.g = swizzle.b;
result.b = swizzle.a;
result.a = swizzle.r;
return result;
}
default:
return swizzle;
}
}
inline NumberConversion MapNumberConversion(const NumberFormat num_fmt, const DataFormat data_fmt) {
switch (num_fmt) {
case NumberFormat::Unorm: {
switch (data_fmt) {
case DataFormat::Format32:
case DataFormat::Format32_32:
case DataFormat::Format32_32_32:
case DataFormat::Format32_32_32_32:
return NumberConversion::Uint32ToUnorm;
default:
return NumberConversion::None;
}
}
case NumberFormat::Uscaled:
return NumberConversion::UintToUscaled;
case NumberFormat::Sscaled:
return NumberConversion::SintToSscaled;
case NumberFormat::Ubnorm:
return NumberConversion::UnormToUbnorm;
case NumberFormat::SnormNz: {
switch (data_fmt) {
case DataFormat::Format8:
case DataFormat::Format8_8:
case DataFormat::Format8_8_8_8:
return NumberConversion::Sint8ToSnormNz;
case DataFormat::Format16:
case DataFormat::Format16_16:
case DataFormat::Format16_16_16_16:
return NumberConversion::Sint16ToSnormNz;
default:
UNREACHABLE_MSG("data_fmt = {}", u32(data_fmt));
}
}
default:
return NumberConversion::None;
}
}
} // namespace AmdGpu } // namespace AmdGpu
template <> template <>

View File

@ -6,6 +6,7 @@
#include "common/debug.h" #include "common/debug.h"
#include "common/scope_exit.h" #include "common/scope_exit.h"
#include "common/types.h" #include "common/types.h"
#include "core/memory.h"
#include "video_core/amdgpu/liverpool.h" #include "video_core/amdgpu/liverpool.h"
#include "video_core/buffer_cache/buffer_cache.h" #include "video_core/buffer_cache/buffer_cache.h"
#include "video_core/host_shaders/fault_buffer_process_comp.h" #include "video_core/host_shaders/fault_buffer_process_comp.h"
@ -28,7 +29,7 @@ BufferCache::BufferCache(const Vulkan::Instance& instance_, Vulkan::Scheduler& s
Vulkan::Rasterizer& rasterizer_, AmdGpu::Liverpool* liverpool_, Vulkan::Rasterizer& rasterizer_, AmdGpu::Liverpool* liverpool_,
TextureCache& texture_cache_, PageManager& tracker_) TextureCache& texture_cache_, PageManager& tracker_)
: instance{instance_}, scheduler{scheduler_}, rasterizer{rasterizer_}, liverpool{liverpool_}, : instance{instance_}, scheduler{scheduler_}, rasterizer{rasterizer_}, liverpool{liverpool_},
texture_cache{texture_cache_}, tracker{tracker_}, memory{Core::Memory::Instance()}, texture_cache{texture_cache_}, tracker{tracker_},
staging_buffer{instance, scheduler, MemoryUsage::Upload, StagingBufferSize}, staging_buffer{instance, scheduler, MemoryUsage::Upload, StagingBufferSize},
stream_buffer{instance, scheduler, MemoryUsage::Stream, UboStreamBufferSize}, stream_buffer{instance, scheduler, MemoryUsage::Stream, UboStreamBufferSize},
download_buffer(instance, scheduler, MemoryUsage::Download, DownloadBufferSize), download_buffer(instance, scheduler, MemoryUsage::Download, DownloadBufferSize),
@ -293,7 +294,7 @@ void BufferCache::BindIndexBuffer(u32 index_offset) {
void BufferCache::InlineData(VAddr address, const void* value, u32 num_bytes, bool is_gds) { void BufferCache::InlineData(VAddr address, const void* value, u32 num_bytes, bool is_gds) {
ASSERT_MSG(address % 4 == 0, "GDS offset must be dword aligned"); ASSERT_MSG(address % 4 == 0, "GDS offset must be dword aligned");
if (!is_gds && !IsRegionRegistered(address, num_bytes)) { if (!is_gds && !IsRegionGpuModified(address, num_bytes)) {
memcpy(std::bit_cast<void*>(address), value, num_bytes); memcpy(std::bit_cast<void*>(address), value, num_bytes);
return; return;
} }
@ -365,7 +366,9 @@ std::pair<Buffer*, u32> BufferCache::ObtainViewBuffer(VAddr gpu_addr, u32 size,
return ObtainBuffer(gpu_addr, size, false, false); return ObtainBuffer(gpu_addr, size, false, false);
} }
// In all other cases, just do a CPU copy to the staging buffer. // In all other cases, just do a CPU copy to the staging buffer.
const u32 offset = staging_buffer.Copy(gpu_addr, size, 16); const auto [data, offset] = staging_buffer.Map(size, 16);
memory->CopySparseMemory(gpu_addr, data, size);
staging_buffer.Commit();
return {&staging_buffer, offset}; return {&staging_buffer, offset};
} }
@ -798,24 +801,45 @@ void BufferCache::SynchronizeBuffer(Buffer& buffer, VAddr device_addr, u32 size,
} }
bool BufferCache::SynchronizeBufferFromImage(Buffer& buffer, VAddr device_addr, u32 size) { bool BufferCache::SynchronizeBufferFromImage(Buffer& buffer, VAddr device_addr, u32 size) {
static constexpr FindFlags find_flags = boost::container::small_vector<ImageId, 6> image_ids;
FindFlags::NoCreate | FindFlags::RelaxDim | FindFlags::RelaxFmt | FindFlags::RelaxSize; texture_cache.ForEachImageInRegion(device_addr, size, [&](ImageId image_id, Image& image) {
TextureCache::BaseDesc desc{}; if (image.info.guest_address != device_addr) {
desc.info.guest_address = device_addr; return;
desc.info.guest_size = size; }
const ImageId image_id = texture_cache.FindImage(desc, find_flags); // Only perform sync if image is:
if (!image_id) { // - GPU modified; otherwise there are no changes to synchronize.
// - Not CPU dirty; otherwise we could overwrite CPU changes with stale GPU changes.
// - Not GPU dirty; otherwise we could overwrite GPU changes with stale image data.
if (False(image.flags & ImageFlagBits::GpuModified) ||
True(image.flags & ImageFlagBits::Dirty)) {
return;
}
image_ids.push_back(image_id);
});
if (image_ids.empty()) {
return false; return false;
} }
ImageId image_id{};
if (image_ids.size() == 1) {
// Sometimes image size might not exactly match with requested buffer size
// If we only found 1 candidate image use it without too many questions.
image_id = image_ids[0];
} else {
for (s32 i = 0; i < image_ids.size(); ++i) {
Image& image = texture_cache.GetImage(image_ids[i]);
if (image.info.guest_size == size) {
image_id = image_ids[i];
break;
}
}
if (!image_id) {
LOG_WARNING(Render_Vulkan,
"Failed to find exact image match for copy addr={:#x}, size={:#x}",
device_addr, size);
return false;
}
}
Image& image = texture_cache.GetImage(image_id); Image& image = texture_cache.GetImage(image_id);
// Only perform sync if image is:
// - GPU modified; otherwise there are no changes to synchronize.
// - Not CPU dirty; otherwise we could overwrite CPU changes with stale GPU changes.
// - Not GPU dirty; otherwise we could overwrite GPU changes with stale image data.
if (False(image.flags & ImageFlagBits::GpuModified) ||
True(image.flags & ImageFlagBits::Dirty)) {
return false;
}
ASSERT_MSG(device_addr == image.info.guest_address, ASSERT_MSG(device_addr == image.info.guest_address,
"Texel buffer aliases image subresources {:x} : {:x}", device_addr, "Texel buffer aliases image subresources {:x} : {:x}", device_addr,
image.info.guest_address); image.info.guest_address);

View File

@ -17,6 +17,10 @@ namespace AmdGpu {
struct Liverpool; struct Liverpool;
} }
namespace Core {
class MemoryManager;
}
namespace Shader { namespace Shader {
namespace Gcn { namespace Gcn {
struct FetchShaderData; struct FetchShaderData;
@ -183,6 +187,7 @@ private:
Vulkan::Scheduler& scheduler; Vulkan::Scheduler& scheduler;
Vulkan::Rasterizer& rasterizer; Vulkan::Rasterizer& rasterizer;
AmdGpu::Liverpool* liverpool; AmdGpu::Liverpool* liverpool;
Core::MemoryManager* memory;
TextureCache& texture_cache; TextureCache& texture_cache;
PageManager& tracker; PageManager& tracker;
StreamBuffer staging_buffer; StreamBuffer staging_buffer;

View File

@ -16,7 +16,7 @@ layout(push_constant) uniform image_info {
uint num_levels; uint num_levels;
uint pitch; uint pitch;
uint height; uint height;
uint sizes[14]; uint sizes[16];
} info; } info;
// Inverse morton LUT, small enough to fit into K$ // Inverse morton LUT, small enough to fit into K$

View File

@ -18,7 +18,7 @@ layout(push_constant) uniform image_info {
uint num_levels; uint num_levels;
uint pitch; uint pitch;
uint height; uint height;
uint sizes[14]; uint sizes[16];
} info; } info;
#define MICRO_TILE_DIM 8 #define MICRO_TILE_DIM 8

View File

@ -16,7 +16,7 @@ layout(push_constant) uniform image_info {
uint num_levels; uint num_levels;
uint pitch; uint pitch;
uint height; uint height;
uint sizes[14]; uint sizes[16];
} info; } info;
// Inverse morton LUT, small enough to fit into K$ // Inverse morton LUT, small enough to fit into K$

View File

@ -16,7 +16,7 @@ layout(push_constant) uniform image_info {
uint num_levels; uint num_levels;
uint pitch; uint pitch;
uint height; uint height;
uint sizes[14]; uint sizes[16];
} info; } info;
// Inverse morton LUT, small enough to fit into K$ // Inverse morton LUT, small enough to fit into K$

View File

@ -19,7 +19,7 @@ layout(push_constant) uniform image_info {
uint num_levels; uint num_levels;
uint pitch; uint pitch;
uint height; uint height;
uint sizes[14]; uint sizes[16];
} info; } info;
#define MICRO_TILE_DIM 8 #define MICRO_TILE_DIM 8

View File

@ -214,6 +214,19 @@ vk::BlendFactor BlendFactor(Liverpool::BlendControl::BlendFactor factor) {
} }
} }
bool IsDualSourceBlendFactor(Liverpool::BlendControl::BlendFactor factor) {
using BlendFactor = Liverpool::BlendControl::BlendFactor;
switch (factor) {
case BlendFactor::Src1Color:
case BlendFactor::Src1Alpha:
case BlendFactor::InvSrc1Color:
case BlendFactor::InvSrc1Alpha:
return true;
default:
return false;
}
}
vk::BlendOp BlendOp(Liverpool::BlendControl::BlendFunc func) { vk::BlendOp BlendOp(Liverpool::BlendControl::BlendFunc func) {
using BlendFunc = Liverpool::BlendControl::BlendFunc; using BlendFunc = Liverpool::BlendControl::BlendFunc;
switch (func) { switch (func) {

View File

@ -30,6 +30,8 @@ vk::FrontFace FrontFace(Liverpool::FrontFace mode);
vk::BlendFactor BlendFactor(Liverpool::BlendControl::BlendFactor factor); vk::BlendFactor BlendFactor(Liverpool::BlendControl::BlendFactor factor);
bool IsDualSourceBlendFactor(Liverpool::BlendControl::BlendFactor factor);
vk::BlendOp BlendOp(Liverpool::BlendControl::BlendFunc func); vk::BlendOp BlendOp(Liverpool::BlendControl::BlendFunc func);
vk::SamplerAddressMode ClampMode(AmdGpu::ClampMode mode); vk::SamplerAddressMode ClampMode(AmdGpu::ClampMode mode);

View File

@ -146,6 +146,7 @@ const Shader::RuntimeInfo& PipelineCache::BuildRuntimeInfo(Stage stage, LogicalS
} }
gs_info.in_vertex_data_size = regs.vgt_esgs_ring_itemsize; gs_info.in_vertex_data_size = regs.vgt_esgs_ring_itemsize;
gs_info.out_vertex_data_size = regs.vgt_gs_vert_itemsize[0]; gs_info.out_vertex_data_size = regs.vgt_gs_vert_itemsize[0];
gs_info.mode = regs.vgt_gs_mode.mode;
const auto params_vc = Liverpool::GetParams(regs.vs_program); const auto params_vc = Liverpool::GetParams(regs.vs_program);
gs_info.vs_copy = params_vc.code; gs_info.vs_copy = params_vc.code;
gs_info.vs_copy_hash = params_vc.hash; gs_info.vs_copy_hash = params_vc.hash;
@ -158,6 +159,15 @@ const Shader::RuntimeInfo& PipelineCache::BuildRuntimeInfo(Stage stage, LogicalS
info.fs_info.addr_flags = regs.ps_input_addr; info.fs_info.addr_flags = regs.ps_input_addr;
const auto& ps_inputs = regs.ps_inputs; const auto& ps_inputs = regs.ps_inputs;
info.fs_info.num_inputs = regs.num_interp; info.fs_info.num_inputs = regs.num_interp;
const auto& cb0_blend = regs.blend_control[0];
info.fs_info.dual_source_blending =
LiverpoolToVK::IsDualSourceBlendFactor(cb0_blend.color_dst_factor) ||
LiverpoolToVK::IsDualSourceBlendFactor(cb0_blend.color_src_factor);
if (cb0_blend.separate_alpha_blend) {
info.fs_info.dual_source_blending |=
LiverpoolToVK::IsDualSourceBlendFactor(cb0_blend.alpha_dst_factor) ||
LiverpoolToVK::IsDualSourceBlendFactor(cb0_blend.alpha_src_factor);
}
for (u32 i = 0; i < regs.num_interp; i++) { for (u32 i = 0; i < regs.num_interp; i++) {
info.fs_info.inputs[i] = { info.fs_info.inputs[i] = {
.param_index = u8(ps_inputs[i].input_offset.Value()), .param_index = u8(ps_inputs[i].input_offset.Value()),

View File

@ -716,7 +716,7 @@ void Rasterizer::BindTextures(const Shader::Info& stage, Shader::Backend::Bindin
ssharp.max_aniso.Assign(AmdGpu::AnisoRatio::One); ssharp.max_aniso.Assign(AmdGpu::AnisoRatio::One);
} }
} }
const auto vk_sampler = texture_cache.GetSampler(ssharp); const auto vk_sampler = texture_cache.GetSampler(ssharp, liverpool->regs.ta_bc_base);
image_infos.emplace_back(vk_sampler, VK_NULL_HANDLE, vk::ImageLayout::eGeneral); image_infos.emplace_back(vk_sampler, VK_NULL_HANDLE, vk::ImageLayout::eGeneral);
set_writes.push_back({ set_writes.push_back({
.dstSet = VK_NULL_HANDLE, .dstSet = VK_NULL_HANDLE,

View File

@ -0,0 +1,220 @@
// SPDX-License-Identifier: GPL-2.0-or-later
// Copyright © 2023 Skyline Team and Contributors (https://github.com/skyline-emu/)
// Copyright © 2015-2023 The Khronos Group Inc.
// Copyright © 2015-2023 Valve Corporation
// Copyright © 2015-2023 LunarG, Inc.
#include <unordered_map>
#include "common/enum.h"
#include "video_core/texture_cache/host_compatibility.h"
namespace VideoCore {
/**
* @brief All classes of format compatibility according to the Vulkan specification
* @url
* https://github.com/KhronosGroup/Vulkan-ValidationLayers/blob/d37c676f/layers/generated/vk_format_utils.h#L47-L131
*/
enum class CompatibilityClass {
NONE = 0,
_128BIT = 1 << 0,
_16BIT = 1 << 1,
_192BIT = 1 << 2,
_24BIT = 1 << 3,
_256BIT = 1 << 4,
_32BIT = 1 << 5,
_48BIT = 1 << 6,
_64BIT = 1 << 7,
_8BIT = 1 << 8,
_96BIT = 1 << 9,
BC1_RGB = 1 << 10,
BC1_RGBA = 1 << 11,
BC2 = 1 << 12,
BC3 = 1 << 13,
BC4 = 1 << 14,
BC5 = 1 << 15,
BC6H = 1 << 16,
BC7 = 1 << 17,
D16 = 1 << 18,
D16S8 = 1 << 19,
D24 = 1 << 20,
D24S8 = 1 << 21,
D32 = 1 << 22,
D32S8 = 1 << 23,
S8 = 1 << 24,
};
DECLARE_ENUM_FLAG_OPERATORS(CompatibilityClass)
/**
* @brief The format compatibility class according to the Vulkan specification
* @url
* https://registry.khronos.org/vulkan/specs/1.3-extensions/html/vkspec.html#formats-compatibility-classes
* @url
* https://github.com/KhronosGroup/Vulkan-ValidationLayers/blob/d37c676f/layers/generated/vk_format_utils.cpp#L70-L812
*/
static const std::unordered_map<vk::Format, CompatibilityClass> FORMAT_TABLE = {
{vk::Format::eA1R5G5B5UnormPack16, CompatibilityClass::_16BIT},
{vk::Format::eA2B10G10R10SintPack32, CompatibilityClass::_32BIT},
{vk::Format::eA2B10G10R10SnormPack32, CompatibilityClass::_32BIT},
{vk::Format::eA2B10G10R10SscaledPack32, CompatibilityClass::_32BIT},
{vk::Format::eA2B10G10R10UintPack32, CompatibilityClass::_32BIT},
{vk::Format::eA2B10G10R10UnormPack32, CompatibilityClass::_32BIT},
{vk::Format::eA2B10G10R10UscaledPack32, CompatibilityClass::_32BIT},
{vk::Format::eA2R10G10B10SintPack32, CompatibilityClass::_32BIT},
{vk::Format::eA2R10G10B10SnormPack32, CompatibilityClass::_32BIT},
{vk::Format::eA2R10G10B10SscaledPack32, CompatibilityClass::_32BIT},
{vk::Format::eA2R10G10B10UintPack32, CompatibilityClass::_32BIT},
{vk::Format::eA2R10G10B10UnormPack32, CompatibilityClass::_32BIT},
{vk::Format::eA2R10G10B10UscaledPack32, CompatibilityClass::_32BIT},
{vk::Format::eA4B4G4R4UnormPack16, CompatibilityClass::_16BIT},
{vk::Format::eA4R4G4B4UnormPack16, CompatibilityClass::_16BIT},
{vk::Format::eA8B8G8R8SintPack32, CompatibilityClass::_32BIT},
{vk::Format::eA8B8G8R8SnormPack32, CompatibilityClass::_32BIT},
{vk::Format::eA8B8G8R8SrgbPack32, CompatibilityClass::_32BIT},
{vk::Format::eA8B8G8R8SscaledPack32, CompatibilityClass::_32BIT},
{vk::Format::eA8B8G8R8UintPack32, CompatibilityClass::_32BIT},
{vk::Format::eA8B8G8R8UnormPack32, CompatibilityClass::_32BIT},
{vk::Format::eA8B8G8R8UscaledPack32, CompatibilityClass::_32BIT},
{vk::Format::eB10G11R11UfloatPack32, CompatibilityClass::_32BIT},
{vk::Format::eB4G4R4A4UnormPack16, CompatibilityClass::_16BIT},
{vk::Format::eB5G5R5A1UnormPack16, CompatibilityClass::_16BIT},
{vk::Format::eB5G6R5UnormPack16, CompatibilityClass::_16BIT},
{vk::Format::eB8G8R8A8Sint, CompatibilityClass::_32BIT},
{vk::Format::eB8G8R8A8Snorm, CompatibilityClass::_32BIT},
{vk::Format::eB8G8R8A8Srgb, CompatibilityClass::_32BIT},
{vk::Format::eB8G8R8A8Sscaled, CompatibilityClass::_32BIT},
{vk::Format::eB8G8R8A8Uint, CompatibilityClass::_32BIT},
{vk::Format::eB8G8R8A8Unorm, CompatibilityClass::_32BIT},
{vk::Format::eB8G8R8A8Uscaled, CompatibilityClass::_32BIT},
{vk::Format::eB8G8R8Sint, CompatibilityClass::_24BIT},
{vk::Format::eB8G8R8Snorm, CompatibilityClass::_24BIT},
{vk::Format::eB8G8R8Srgb, CompatibilityClass::_24BIT},
{vk::Format::eB8G8R8Sscaled, CompatibilityClass::_24BIT},
{vk::Format::eB8G8R8Uint, CompatibilityClass::_24BIT},
{vk::Format::eB8G8R8Unorm, CompatibilityClass::_24BIT},
{vk::Format::eB8G8R8Uscaled, CompatibilityClass::_24BIT},
{vk::Format::eBc1RgbaSrgbBlock, CompatibilityClass::BC1_RGBA | CompatibilityClass::_64BIT},
{vk::Format::eBc1RgbaUnormBlock, CompatibilityClass::BC1_RGBA | CompatibilityClass::_64BIT},
{vk::Format::eBc1RgbSrgbBlock, CompatibilityClass::BC1_RGB | CompatibilityClass::_64BIT},
{vk::Format::eBc1RgbUnormBlock, CompatibilityClass::BC1_RGB | CompatibilityClass::_64BIT},
{vk::Format::eBc2SrgbBlock, CompatibilityClass::BC2 | CompatibilityClass::_128BIT},
{vk::Format::eBc2UnormBlock, CompatibilityClass::BC2 | CompatibilityClass::_128BIT},
{vk::Format::eBc3SrgbBlock, CompatibilityClass::BC3 | CompatibilityClass::_128BIT},
{vk::Format::eBc3UnormBlock, CompatibilityClass::BC3 | CompatibilityClass::_128BIT},
{vk::Format::eBc4SnormBlock, CompatibilityClass::BC4 | CompatibilityClass::_64BIT},
{vk::Format::eBc4UnormBlock, CompatibilityClass::BC4 | CompatibilityClass::_64BIT},
{vk::Format::eBc5SnormBlock, CompatibilityClass::BC5 | CompatibilityClass::_128BIT},
{vk::Format::eBc5UnormBlock, CompatibilityClass::BC5 | CompatibilityClass::_128BIT},
{vk::Format::eBc6HSfloatBlock, CompatibilityClass::BC6H | CompatibilityClass::_128BIT},
{vk::Format::eBc6HUfloatBlock, CompatibilityClass::BC6H | CompatibilityClass::_128BIT},
{vk::Format::eBc7SrgbBlock, CompatibilityClass::BC7 | CompatibilityClass::_128BIT},
{vk::Format::eBc7UnormBlock, CompatibilityClass::BC7 | CompatibilityClass::_128BIT},
{vk::Format::eD16Unorm, CompatibilityClass::D16},
{vk::Format::eD16UnormS8Uint, CompatibilityClass::D16S8},
{vk::Format::eD24UnormS8Uint, CompatibilityClass::D24S8},
{vk::Format::eD32Sfloat, CompatibilityClass::D32},
{vk::Format::eD32SfloatS8Uint, CompatibilityClass::D32S8},
{vk::Format::eE5B9G9R9UfloatPack32, CompatibilityClass::_32BIT},
{vk::Format::eR10X6G10X6Unorm2Pack16, CompatibilityClass::_32BIT},
{vk::Format::eR10X6UnormPack16, CompatibilityClass::_16BIT},
{vk::Format::eR12X4G12X4Unorm2Pack16, CompatibilityClass::_32BIT},
{vk::Format::eR12X4UnormPack16, CompatibilityClass::_16BIT},
{vk::Format::eR16G16B16A16Sfloat, CompatibilityClass::_64BIT},
{vk::Format::eR16G16B16A16Sint, CompatibilityClass::_64BIT},
{vk::Format::eR16G16B16A16Snorm, CompatibilityClass::_64BIT},
{vk::Format::eR16G16B16A16Sscaled, CompatibilityClass::_64BIT},
{vk::Format::eR16G16B16A16Uint, CompatibilityClass::_64BIT},
{vk::Format::eR16G16B16A16Unorm, CompatibilityClass::_64BIT},
{vk::Format::eR16G16B16A16Uscaled, CompatibilityClass::_64BIT},
{vk::Format::eR16G16B16Sfloat, CompatibilityClass::_48BIT},
{vk::Format::eR16G16B16Sint, CompatibilityClass::_48BIT},
{vk::Format::eR16G16B16Snorm, CompatibilityClass::_48BIT},
{vk::Format::eR16G16B16Sscaled, CompatibilityClass::_48BIT},
{vk::Format::eR16G16B16Uint, CompatibilityClass::_48BIT},
{vk::Format::eR16G16B16Unorm, CompatibilityClass::_48BIT},
{vk::Format::eR16G16B16Uscaled, CompatibilityClass::_48BIT},
{vk::Format::eR16G16Sfloat, CompatibilityClass::_32BIT},
{vk::Format::eR16G16Sint, CompatibilityClass::_32BIT},
{vk::Format::eR16G16Snorm, CompatibilityClass::_32BIT},
{vk::Format::eR16G16Sscaled, CompatibilityClass::_32BIT},
{vk::Format::eR16G16Uint, CompatibilityClass::_32BIT},
{vk::Format::eR16G16Unorm, CompatibilityClass::_32BIT},
{vk::Format::eR16G16Uscaled, CompatibilityClass::_32BIT},
{vk::Format::eR16Sfloat, CompatibilityClass::_16BIT},
{vk::Format::eR16Sint, CompatibilityClass::_16BIT},
{vk::Format::eR16Snorm, CompatibilityClass::_16BIT},
{vk::Format::eR16Sscaled, CompatibilityClass::_16BIT},
{vk::Format::eR16Uint, CompatibilityClass::_16BIT},
{vk::Format::eR16Unorm, CompatibilityClass::_16BIT},
{vk::Format::eR16Uscaled, CompatibilityClass::_16BIT},
{vk::Format::eR32G32B32A32Sfloat, CompatibilityClass::_128BIT},
{vk::Format::eR32G32B32A32Sint, CompatibilityClass::_128BIT},
{vk::Format::eR32G32B32A32Uint, CompatibilityClass::_128BIT},
{vk::Format::eR32G32B32Sfloat, CompatibilityClass::_96BIT},
{vk::Format::eR32G32B32Sint, CompatibilityClass::_96BIT},
{vk::Format::eR32G32B32Uint, CompatibilityClass::_96BIT},
{vk::Format::eR32G32Sfloat, CompatibilityClass::_64BIT},
{vk::Format::eR32G32Sint, CompatibilityClass::_64BIT},
{vk::Format::eR32G32Uint, CompatibilityClass::_64BIT},
{vk::Format::eR32Sfloat, CompatibilityClass::_32BIT},
{vk::Format::eR32Sint, CompatibilityClass::_32BIT},
{vk::Format::eR32Uint, CompatibilityClass::_32BIT},
{vk::Format::eR4G4B4A4UnormPack16, CompatibilityClass::_16BIT},
{vk::Format::eR4G4UnormPack8, CompatibilityClass::_8BIT},
{vk::Format::eR5G5B5A1UnormPack16, CompatibilityClass::_16BIT},
{vk::Format::eR5G6B5UnormPack16, CompatibilityClass::_16BIT},
{vk::Format::eR64G64B64A64Sfloat, CompatibilityClass::_256BIT},
{vk::Format::eR64G64B64A64Sint, CompatibilityClass::_256BIT},
{vk::Format::eR64G64B64A64Uint, CompatibilityClass::_256BIT},
{vk::Format::eR64G64B64Sfloat, CompatibilityClass::_192BIT},
{vk::Format::eR64G64B64Sint, CompatibilityClass::_192BIT},
{vk::Format::eR64G64B64Uint, CompatibilityClass::_192BIT},
{vk::Format::eR64G64Sfloat, CompatibilityClass::_128BIT},
{vk::Format::eR64G64Sint, CompatibilityClass::_128BIT},
{vk::Format::eR64G64Uint, CompatibilityClass::_128BIT},
{vk::Format::eR64Sfloat, CompatibilityClass::_64BIT},
{vk::Format::eR64Sint, CompatibilityClass::_64BIT},
{vk::Format::eR64Uint, CompatibilityClass::_64BIT},
{vk::Format::eR8G8B8A8Sint, CompatibilityClass::_32BIT},
{vk::Format::eR8G8B8A8Snorm, CompatibilityClass::_32BIT},
{vk::Format::eR8G8B8A8Srgb, CompatibilityClass::_32BIT},
{vk::Format::eR8G8B8A8Sscaled, CompatibilityClass::_32BIT},
{vk::Format::eR8G8B8A8Uint, CompatibilityClass::_32BIT},
{vk::Format::eR8G8B8A8Unorm, CompatibilityClass::_32BIT},
{vk::Format::eR8G8B8A8Uscaled, CompatibilityClass::_32BIT},
{vk::Format::eR8G8B8Sint, CompatibilityClass::_24BIT},
{vk::Format::eR8G8B8Snorm, CompatibilityClass::_24BIT},
{vk::Format::eR8G8B8Srgb, CompatibilityClass::_24BIT},
{vk::Format::eR8G8B8Sscaled, CompatibilityClass::_24BIT},
{vk::Format::eR8G8B8Uint, CompatibilityClass::_24BIT},
{vk::Format::eR8G8B8Unorm, CompatibilityClass::_24BIT},
{vk::Format::eR8G8B8Uscaled, CompatibilityClass::_24BIT},
{vk::Format::eR8G8Sint, CompatibilityClass::_16BIT},
{vk::Format::eR8G8Snorm, CompatibilityClass::_16BIT},
{vk::Format::eR8G8Srgb, CompatibilityClass::_16BIT},
{vk::Format::eR8G8Sscaled, CompatibilityClass::_16BIT},
{vk::Format::eR8G8Uint, CompatibilityClass::_16BIT},
{vk::Format::eR8G8Unorm, CompatibilityClass::_16BIT},
{vk::Format::eR8G8Uscaled, CompatibilityClass::_16BIT},
{vk::Format::eR8Sint, CompatibilityClass::_8BIT},
{vk::Format::eR8Snorm, CompatibilityClass::_8BIT},
{vk::Format::eR8Srgb, CompatibilityClass::_8BIT},
{vk::Format::eR8Sscaled, CompatibilityClass::_8BIT},
{vk::Format::eR8Uint, CompatibilityClass::_8BIT},
{vk::Format::eR8Unorm, CompatibilityClass::_8BIT},
{vk::Format::eR8Uscaled, CompatibilityClass::_8BIT},
{vk::Format::eS8Uint, CompatibilityClass::S8},
{vk::Format::eX8D24UnormPack32, CompatibilityClass::D24},
{vk::Format::eUndefined, CompatibilityClass::NONE},
};
bool IsVulkanFormatCompatible(vk::Format base, vk::Format view) {
if (base == view) {
return true;
}
const auto base_comp = FORMAT_TABLE.at(base);
const auto view_comp = FORMAT_TABLE.at(view);
return (base_comp & view_comp) == view_comp;
}
} // namespace VideoCore

View File

@ -6,387 +6,11 @@
#pragma once #pragma once
#include <unordered_map>
#include "video_core/renderer_vulkan/vk_common.h" #include "video_core/renderer_vulkan/vk_common.h"
namespace VideoCore { namespace VideoCore {
/**
* @brief All classes of format compatibility according to the Vulkan specification
* @url
* https://github.com/KhronosGroup/Vulkan-ValidationLayers/blob/d37c676f75f545a3e5a98d7dfb89864391a1db1e/layers/generated/vk_format_utils.h#L47-L131
* @note This is copied directly from Vulkan Validation Layers and doesn't follow the Skyline naming
* conventions
*/
enum class FORMAT_COMPATIBILITY_CLASS {
NONE = 0,
_10BIT_2PLANE_420,
_10BIT_2PLANE_422,
_10BIT_2PLANE_444,
_10BIT_3PLANE_420,
_10BIT_3PLANE_422,
_10BIT_3PLANE_444,
_12BIT_2PLANE_420,
_12BIT_2PLANE_422,
_12BIT_2PLANE_444,
_12BIT_3PLANE_420,
_12BIT_3PLANE_422,
_12BIT_3PLANE_444,
_128BIT,
_16BIT,
_16BIT_2PLANE_420,
_16BIT_2PLANE_422,
_16BIT_2PLANE_444,
_16BIT_3PLANE_420,
_16BIT_3PLANE_422,
_16BIT_3PLANE_444,
_192BIT,
_24BIT,
_256BIT,
_32BIT,
_32BIT_B8G8R8G8,
_32BIT_G8B8G8R8,
_48BIT,
_64BIT,
_64BIT_B10G10R10G10,
_64BIT_B12G12R12G12,
_64BIT_B16G16R16G16,
_64BIT_G10B10G10R10,
_64BIT_G12B12G12R12,
_64BIT_G16B16G16R16,
_64BIT_R10G10B10A10,
_64BIT_R12G12B12A12,
_8BIT,
_8BIT_2PLANE_420,
_8BIT_2PLANE_422,
_8BIT_2PLANE_444,
_8BIT_3PLANE_420,
_8BIT_3PLANE_422,
_8BIT_3PLANE_444,
_96BIT,
ASTC_10X10,
ASTC_10X5,
ASTC_10X6,
ASTC_10X8,
ASTC_12X10,
ASTC_12X12,
ASTC_4X4,
ASTC_5X4,
ASTC_5X5,
ASTC_6X5,
ASTC_6X6,
ASTC_8X5,
ASTC_8X6,
ASTC_8X8,
BC1_RGB,
BC1_RGBA,
BC2,
BC3,
BC4,
BC5,
BC6H,
BC7,
D16,
D16S8,
D24,
D24S8,
D32,
D32S8,
EAC_R,
EAC_RG,
ETC2_EAC_RGBA,
ETC2_RGB,
ETC2_RGBA,
PVRTC1_2BPP,
PVRTC1_4BPP,
PVRTC2_2BPP,
PVRTC2_4BPP,
S8
};
/** /// Returns true if the two formats are compatible according to Vulkan's format compatibility rules
* @brief The format compatibility class according to the Vulkan specification bool IsVulkanFormatCompatible(vk::Format base, vk::Format view);
* @url
* https://registry.khronos.org/vulkan/specs/1.3-extensions/html/vkspec.html#formats-compatibility-classes
* @url
* https://github.com/KhronosGroup/Vulkan-ValidationLayers/blob/d37c676f75f545a3e5a98d7dfb89864391a1db1e/layers/generated/vk_format_utils.cpp#L70-L812
* @note This is copied directly from Vulkan Validation Layers and doesn't follow the Skyline naming
* conventions
*/
static const std::unordered_map<VkFormat, FORMAT_COMPATIBILITY_CLASS> vkFormatClassTable{
{VK_FORMAT_A1R5G5B5_UNORM_PACK16, FORMAT_COMPATIBILITY_CLASS::_16BIT},
{VK_FORMAT_A2B10G10R10_SINT_PACK32, FORMAT_COMPATIBILITY_CLASS::_32BIT},
{VK_FORMAT_A2B10G10R10_SNORM_PACK32, FORMAT_COMPATIBILITY_CLASS::_32BIT},
{VK_FORMAT_A2B10G10R10_SSCALED_PACK32, FORMAT_COMPATIBILITY_CLASS::_32BIT},
{VK_FORMAT_A2B10G10R10_UINT_PACK32, FORMAT_COMPATIBILITY_CLASS::_32BIT},
{VK_FORMAT_A2B10G10R10_UNORM_PACK32, FORMAT_COMPATIBILITY_CLASS::_32BIT},
{VK_FORMAT_A2B10G10R10_USCALED_PACK32, FORMAT_COMPATIBILITY_CLASS::_32BIT},
{VK_FORMAT_A2R10G10B10_SINT_PACK32, FORMAT_COMPATIBILITY_CLASS::_32BIT},
{VK_FORMAT_A2R10G10B10_SNORM_PACK32, FORMAT_COMPATIBILITY_CLASS::_32BIT},
{VK_FORMAT_A2R10G10B10_SSCALED_PACK32, FORMAT_COMPATIBILITY_CLASS::_32BIT},
{VK_FORMAT_A2R10G10B10_UINT_PACK32, FORMAT_COMPATIBILITY_CLASS::_32BIT},
{VK_FORMAT_A2R10G10B10_UNORM_PACK32, FORMAT_COMPATIBILITY_CLASS::_32BIT},
{VK_FORMAT_A2R10G10B10_USCALED_PACK32, FORMAT_COMPATIBILITY_CLASS::_32BIT},
{VK_FORMAT_A4B4G4R4_UNORM_PACK16_EXT, FORMAT_COMPATIBILITY_CLASS::_16BIT},
{VK_FORMAT_A4R4G4B4_UNORM_PACK16_EXT, FORMAT_COMPATIBILITY_CLASS::_16BIT},
{VK_FORMAT_A8B8G8R8_SINT_PACK32, FORMAT_COMPATIBILITY_CLASS::_32BIT},
{VK_FORMAT_A8B8G8R8_SNORM_PACK32, FORMAT_COMPATIBILITY_CLASS::_32BIT},
{VK_FORMAT_A8B8G8R8_SRGB_PACK32, FORMAT_COMPATIBILITY_CLASS::_32BIT},
{VK_FORMAT_A8B8G8R8_SSCALED_PACK32, FORMAT_COMPATIBILITY_CLASS::_32BIT},
{VK_FORMAT_A8B8G8R8_UINT_PACK32, FORMAT_COMPATIBILITY_CLASS::_32BIT},
{VK_FORMAT_A8B8G8R8_UNORM_PACK32, FORMAT_COMPATIBILITY_CLASS::_32BIT},
{VK_FORMAT_A8B8G8R8_USCALED_PACK32, FORMAT_COMPATIBILITY_CLASS::_32BIT},
{VK_FORMAT_ASTC_10x10_SFLOAT_BLOCK_EXT, FORMAT_COMPATIBILITY_CLASS::ASTC_10X10},
{VK_FORMAT_ASTC_10x10_SRGB_BLOCK, FORMAT_COMPATIBILITY_CLASS::ASTC_10X10},
{VK_FORMAT_ASTC_10x10_UNORM_BLOCK, FORMAT_COMPATIBILITY_CLASS::ASTC_10X10},
{VK_FORMAT_ASTC_10x5_SFLOAT_BLOCK_EXT, FORMAT_COMPATIBILITY_CLASS::ASTC_10X5},
{VK_FORMAT_ASTC_10x5_SRGB_BLOCK, FORMAT_COMPATIBILITY_CLASS::ASTC_10X5},
{VK_FORMAT_ASTC_10x5_UNORM_BLOCK, FORMAT_COMPATIBILITY_CLASS::ASTC_10X5},
{VK_FORMAT_ASTC_10x6_SFLOAT_BLOCK_EXT, FORMAT_COMPATIBILITY_CLASS::ASTC_10X6},
{VK_FORMAT_ASTC_10x6_SRGB_BLOCK, FORMAT_COMPATIBILITY_CLASS::ASTC_10X6},
{VK_FORMAT_ASTC_10x6_UNORM_BLOCK, FORMAT_COMPATIBILITY_CLASS::ASTC_10X6},
{VK_FORMAT_ASTC_10x8_SFLOAT_BLOCK_EXT, FORMAT_COMPATIBILITY_CLASS::ASTC_10X8},
{VK_FORMAT_ASTC_10x8_SRGB_BLOCK, FORMAT_COMPATIBILITY_CLASS::ASTC_10X8},
{VK_FORMAT_ASTC_10x8_UNORM_BLOCK, FORMAT_COMPATIBILITY_CLASS::ASTC_10X8},
{VK_FORMAT_ASTC_12x10_SFLOAT_BLOCK_EXT, FORMAT_COMPATIBILITY_CLASS::ASTC_12X10},
{VK_FORMAT_ASTC_12x10_SRGB_BLOCK, FORMAT_COMPATIBILITY_CLASS::ASTC_12X10},
{VK_FORMAT_ASTC_12x10_UNORM_BLOCK, FORMAT_COMPATIBILITY_CLASS::ASTC_12X10},
{VK_FORMAT_ASTC_12x12_SFLOAT_BLOCK_EXT, FORMAT_COMPATIBILITY_CLASS::ASTC_12X12},
{VK_FORMAT_ASTC_12x12_SRGB_BLOCK, FORMAT_COMPATIBILITY_CLASS::ASTC_12X12},
{VK_FORMAT_ASTC_12x12_UNORM_BLOCK, FORMAT_COMPATIBILITY_CLASS::ASTC_12X12},
{VK_FORMAT_ASTC_4x4_SFLOAT_BLOCK_EXT, FORMAT_COMPATIBILITY_CLASS::ASTC_4X4},
{VK_FORMAT_ASTC_4x4_SRGB_BLOCK, FORMAT_COMPATIBILITY_CLASS::ASTC_4X4},
{VK_FORMAT_ASTC_4x4_UNORM_BLOCK, FORMAT_COMPATIBILITY_CLASS::ASTC_4X4},
{VK_FORMAT_ASTC_5x4_SFLOAT_BLOCK_EXT, FORMAT_COMPATIBILITY_CLASS::ASTC_5X4},
{VK_FORMAT_ASTC_5x4_SRGB_BLOCK, FORMAT_COMPATIBILITY_CLASS::ASTC_5X4},
{VK_FORMAT_ASTC_5x4_UNORM_BLOCK, FORMAT_COMPATIBILITY_CLASS::ASTC_5X4},
{VK_FORMAT_ASTC_5x5_SFLOAT_BLOCK_EXT, FORMAT_COMPATIBILITY_CLASS::ASTC_5X5},
{VK_FORMAT_ASTC_5x5_SRGB_BLOCK, FORMAT_COMPATIBILITY_CLASS::ASTC_5X5},
{VK_FORMAT_ASTC_5x5_UNORM_BLOCK, FORMAT_COMPATIBILITY_CLASS::ASTC_5X5},
{VK_FORMAT_ASTC_6x5_SFLOAT_BLOCK_EXT, FORMAT_COMPATIBILITY_CLASS::ASTC_6X5},
{VK_FORMAT_ASTC_6x5_SRGB_BLOCK, FORMAT_COMPATIBILITY_CLASS::ASTC_6X5},
{VK_FORMAT_ASTC_6x5_UNORM_BLOCK, FORMAT_COMPATIBILITY_CLASS::ASTC_6X5},
{VK_FORMAT_ASTC_6x6_SFLOAT_BLOCK_EXT, FORMAT_COMPATIBILITY_CLASS::ASTC_6X6},
{VK_FORMAT_ASTC_6x6_SRGB_BLOCK, FORMAT_COMPATIBILITY_CLASS::ASTC_6X6},
{VK_FORMAT_ASTC_6x6_UNORM_BLOCK, FORMAT_COMPATIBILITY_CLASS::ASTC_6X6},
{VK_FORMAT_ASTC_8x5_SFLOAT_BLOCK_EXT, FORMAT_COMPATIBILITY_CLASS::ASTC_8X5},
{VK_FORMAT_ASTC_8x5_SRGB_BLOCK, FORMAT_COMPATIBILITY_CLASS::ASTC_8X5},
{VK_FORMAT_ASTC_8x5_UNORM_BLOCK, FORMAT_COMPATIBILITY_CLASS::ASTC_8X5},
{VK_FORMAT_ASTC_8x6_SFLOAT_BLOCK_EXT, FORMAT_COMPATIBILITY_CLASS::ASTC_8X6},
{VK_FORMAT_ASTC_8x6_SRGB_BLOCK, FORMAT_COMPATIBILITY_CLASS::ASTC_8X6},
{VK_FORMAT_ASTC_8x6_UNORM_BLOCK, FORMAT_COMPATIBILITY_CLASS::ASTC_8X6},
{VK_FORMAT_ASTC_8x8_SFLOAT_BLOCK_EXT, FORMAT_COMPATIBILITY_CLASS::ASTC_8X8},
{VK_FORMAT_ASTC_8x8_SRGB_BLOCK, FORMAT_COMPATIBILITY_CLASS::ASTC_8X8},
{VK_FORMAT_ASTC_8x8_UNORM_BLOCK, FORMAT_COMPATIBILITY_CLASS::ASTC_8X8},
{VK_FORMAT_B10G11R11_UFLOAT_PACK32, FORMAT_COMPATIBILITY_CLASS::_32BIT},
{VK_FORMAT_B10X6G10X6R10X6G10X6_422_UNORM_4PACK16,
FORMAT_COMPATIBILITY_CLASS::_64BIT_B10G10R10G10},
{VK_FORMAT_B12X4G12X4R12X4G12X4_422_UNORM_4PACK16,
FORMAT_COMPATIBILITY_CLASS::_64BIT_B12G12R12G12},
{VK_FORMAT_B16G16R16G16_422_UNORM, FORMAT_COMPATIBILITY_CLASS::_64BIT_B16G16R16G16},
{VK_FORMAT_B4G4R4A4_UNORM_PACK16, FORMAT_COMPATIBILITY_CLASS::_16BIT},
{VK_FORMAT_B5G5R5A1_UNORM_PACK16, FORMAT_COMPATIBILITY_CLASS::_16BIT},
{VK_FORMAT_B5G6R5_UNORM_PACK16, FORMAT_COMPATIBILITY_CLASS::_16BIT},
{VK_FORMAT_B8G8R8A8_SINT, FORMAT_COMPATIBILITY_CLASS::_32BIT},
{VK_FORMAT_B8G8R8A8_SNORM, FORMAT_COMPATIBILITY_CLASS::_32BIT},
{VK_FORMAT_B8G8R8A8_SRGB, FORMAT_COMPATIBILITY_CLASS::_32BIT},
{VK_FORMAT_B8G8R8A8_SSCALED, FORMAT_COMPATIBILITY_CLASS::_32BIT},
{VK_FORMAT_B8G8R8A8_UINT, FORMAT_COMPATIBILITY_CLASS::_32BIT},
{VK_FORMAT_B8G8R8A8_UNORM, FORMAT_COMPATIBILITY_CLASS::_32BIT},
{VK_FORMAT_B8G8R8A8_USCALED, FORMAT_COMPATIBILITY_CLASS::_32BIT},
{VK_FORMAT_B8G8R8G8_422_UNORM, FORMAT_COMPATIBILITY_CLASS::_32BIT_B8G8R8G8},
{VK_FORMAT_B8G8R8_SINT, FORMAT_COMPATIBILITY_CLASS::_24BIT},
{VK_FORMAT_B8G8R8_SNORM, FORMAT_COMPATIBILITY_CLASS::_24BIT},
{VK_FORMAT_B8G8R8_SRGB, FORMAT_COMPATIBILITY_CLASS::_24BIT},
{VK_FORMAT_B8G8R8_SSCALED, FORMAT_COMPATIBILITY_CLASS::_24BIT},
{VK_FORMAT_B8G8R8_UINT, FORMAT_COMPATIBILITY_CLASS::_24BIT},
{VK_FORMAT_B8G8R8_UNORM, FORMAT_COMPATIBILITY_CLASS::_24BIT},
{VK_FORMAT_B8G8R8_USCALED, FORMAT_COMPATIBILITY_CLASS::_24BIT},
{VK_FORMAT_BC1_RGBA_SRGB_BLOCK, FORMAT_COMPATIBILITY_CLASS::BC1_RGBA},
{VK_FORMAT_BC1_RGBA_UNORM_BLOCK, FORMAT_COMPATIBILITY_CLASS::BC1_RGBA},
{VK_FORMAT_BC1_RGB_SRGB_BLOCK, FORMAT_COMPATIBILITY_CLASS::BC1_RGB},
{VK_FORMAT_BC1_RGB_UNORM_BLOCK, FORMAT_COMPATIBILITY_CLASS::BC1_RGB},
{VK_FORMAT_BC2_SRGB_BLOCK, FORMAT_COMPATIBILITY_CLASS::BC2},
{VK_FORMAT_BC2_UNORM_BLOCK, FORMAT_COMPATIBILITY_CLASS::BC2},
{VK_FORMAT_BC3_SRGB_BLOCK, FORMAT_COMPATIBILITY_CLASS::BC3},
{VK_FORMAT_BC3_UNORM_BLOCK, FORMAT_COMPATIBILITY_CLASS::BC3},
{VK_FORMAT_BC4_SNORM_BLOCK, FORMAT_COMPATIBILITY_CLASS::BC4},
{VK_FORMAT_BC4_UNORM_BLOCK, FORMAT_COMPATIBILITY_CLASS::BC4},
{VK_FORMAT_BC5_SNORM_BLOCK, FORMAT_COMPATIBILITY_CLASS::BC5},
{VK_FORMAT_BC5_UNORM_BLOCK, FORMAT_COMPATIBILITY_CLASS::BC5},
{VK_FORMAT_BC6H_SFLOAT_BLOCK, FORMAT_COMPATIBILITY_CLASS::BC6H},
{VK_FORMAT_BC6H_UFLOAT_BLOCK, FORMAT_COMPATIBILITY_CLASS::BC6H},
{VK_FORMAT_BC7_SRGB_BLOCK, FORMAT_COMPATIBILITY_CLASS::BC7},
{VK_FORMAT_BC7_UNORM_BLOCK, FORMAT_COMPATIBILITY_CLASS::BC7},
{VK_FORMAT_D16_UNORM, FORMAT_COMPATIBILITY_CLASS::D16},
{VK_FORMAT_D16_UNORM_S8_UINT, FORMAT_COMPATIBILITY_CLASS::D16S8},
{VK_FORMAT_D24_UNORM_S8_UINT, FORMAT_COMPATIBILITY_CLASS::D24S8},
{VK_FORMAT_D32_SFLOAT, FORMAT_COMPATIBILITY_CLASS::D32},
{VK_FORMAT_D32_SFLOAT_S8_UINT, FORMAT_COMPATIBILITY_CLASS::D32S8},
{VK_FORMAT_E5B9G9R9_UFLOAT_PACK32, FORMAT_COMPATIBILITY_CLASS::_32BIT},
{VK_FORMAT_EAC_R11G11_SNORM_BLOCK, FORMAT_COMPATIBILITY_CLASS::EAC_RG},
{VK_FORMAT_EAC_R11G11_UNORM_BLOCK, FORMAT_COMPATIBILITY_CLASS::EAC_RG},
{VK_FORMAT_EAC_R11_SNORM_BLOCK, FORMAT_COMPATIBILITY_CLASS::EAC_R},
{VK_FORMAT_EAC_R11_UNORM_BLOCK, FORMAT_COMPATIBILITY_CLASS::EAC_R},
{VK_FORMAT_ETC2_R8G8B8A1_SRGB_BLOCK, FORMAT_COMPATIBILITY_CLASS::ETC2_RGBA},
{VK_FORMAT_ETC2_R8G8B8A1_UNORM_BLOCK, FORMAT_COMPATIBILITY_CLASS::ETC2_RGBA},
{VK_FORMAT_ETC2_R8G8B8A8_SRGB_BLOCK, FORMAT_COMPATIBILITY_CLASS::ETC2_EAC_RGBA},
{VK_FORMAT_ETC2_R8G8B8A8_UNORM_BLOCK, FORMAT_COMPATIBILITY_CLASS::ETC2_EAC_RGBA},
{VK_FORMAT_ETC2_R8G8B8_SRGB_BLOCK, FORMAT_COMPATIBILITY_CLASS::ETC2_RGB},
{VK_FORMAT_ETC2_R8G8B8_UNORM_BLOCK, FORMAT_COMPATIBILITY_CLASS::ETC2_RGB},
{VK_FORMAT_G10X6B10X6G10X6R10X6_422_UNORM_4PACK16,
FORMAT_COMPATIBILITY_CLASS::_64BIT_G10B10G10R10},
{VK_FORMAT_G10X6_B10X6R10X6_2PLANE_420_UNORM_3PACK16,
FORMAT_COMPATIBILITY_CLASS::_10BIT_2PLANE_420},
{VK_FORMAT_G10X6_B10X6R10X6_2PLANE_422_UNORM_3PACK16,
FORMAT_COMPATIBILITY_CLASS::_10BIT_2PLANE_422},
{VK_FORMAT_G10X6_B10X6R10X6_2PLANE_444_UNORM_3PACK16_EXT,
FORMAT_COMPATIBILITY_CLASS::_10BIT_2PLANE_444},
{VK_FORMAT_G10X6_B10X6_R10X6_3PLANE_420_UNORM_3PACK16,
FORMAT_COMPATIBILITY_CLASS::_10BIT_3PLANE_420},
{VK_FORMAT_G10X6_B10X6_R10X6_3PLANE_422_UNORM_3PACK16,
FORMAT_COMPATIBILITY_CLASS::_10BIT_3PLANE_422},
{VK_FORMAT_G10X6_B10X6_R10X6_3PLANE_444_UNORM_3PACK16,
FORMAT_COMPATIBILITY_CLASS::_10BIT_3PLANE_444},
{VK_FORMAT_G12X4B12X4G12X4R12X4_422_UNORM_4PACK16,
FORMAT_COMPATIBILITY_CLASS::_64BIT_G12B12G12R12},
{VK_FORMAT_G12X4_B12X4R12X4_2PLANE_420_UNORM_3PACK16,
FORMAT_COMPATIBILITY_CLASS::_12BIT_2PLANE_420},
{VK_FORMAT_G12X4_B12X4R12X4_2PLANE_422_UNORM_3PACK16,
FORMAT_COMPATIBILITY_CLASS::_12BIT_2PLANE_422},
{VK_FORMAT_G12X4_B12X4R12X4_2PLANE_444_UNORM_3PACK16_EXT,
FORMAT_COMPATIBILITY_CLASS::_12BIT_2PLANE_444},
{VK_FORMAT_G12X4_B12X4_R12X4_3PLANE_420_UNORM_3PACK16,
FORMAT_COMPATIBILITY_CLASS::_12BIT_3PLANE_420},
{VK_FORMAT_G12X4_B12X4_R12X4_3PLANE_422_UNORM_3PACK16,
FORMAT_COMPATIBILITY_CLASS::_12BIT_3PLANE_422},
{VK_FORMAT_G12X4_B12X4_R12X4_3PLANE_444_UNORM_3PACK16,
FORMAT_COMPATIBILITY_CLASS::_12BIT_3PLANE_444},
{VK_FORMAT_G16B16G16R16_422_UNORM, FORMAT_COMPATIBILITY_CLASS::_64BIT_G16B16G16R16},
{VK_FORMAT_G16_B16R16_2PLANE_420_UNORM, FORMAT_COMPATIBILITY_CLASS::_16BIT_2PLANE_420},
{VK_FORMAT_G16_B16R16_2PLANE_422_UNORM, FORMAT_COMPATIBILITY_CLASS::_16BIT_2PLANE_422},
{VK_FORMAT_G16_B16R16_2PLANE_444_UNORM_EXT, FORMAT_COMPATIBILITY_CLASS::_16BIT_2PLANE_444},
{VK_FORMAT_G16_B16_R16_3PLANE_420_UNORM, FORMAT_COMPATIBILITY_CLASS::_16BIT_3PLANE_420},
{VK_FORMAT_G16_B16_R16_3PLANE_422_UNORM, FORMAT_COMPATIBILITY_CLASS::_16BIT_3PLANE_422},
{VK_FORMAT_G16_B16_R16_3PLANE_444_UNORM, FORMAT_COMPATIBILITY_CLASS::_16BIT_3PLANE_444},
{VK_FORMAT_G8B8G8R8_422_UNORM, FORMAT_COMPATIBILITY_CLASS::_32BIT_G8B8G8R8},
{VK_FORMAT_G8_B8R8_2PLANE_420_UNORM, FORMAT_COMPATIBILITY_CLASS::_8BIT_2PLANE_420},
{VK_FORMAT_G8_B8R8_2PLANE_422_UNORM, FORMAT_COMPATIBILITY_CLASS::_8BIT_2PLANE_422},
{VK_FORMAT_G8_B8R8_2PLANE_444_UNORM_EXT, FORMAT_COMPATIBILITY_CLASS::_8BIT_2PLANE_444},
{VK_FORMAT_G8_B8_R8_3PLANE_420_UNORM, FORMAT_COMPATIBILITY_CLASS::_8BIT_3PLANE_420},
{VK_FORMAT_G8_B8_R8_3PLANE_422_UNORM, FORMAT_COMPATIBILITY_CLASS::_8BIT_3PLANE_422},
{VK_FORMAT_G8_B8_R8_3PLANE_444_UNORM, FORMAT_COMPATIBILITY_CLASS::_8BIT_3PLANE_444},
{VK_FORMAT_PVRTC1_2BPP_SRGB_BLOCK_IMG, FORMAT_COMPATIBILITY_CLASS::PVRTC1_2BPP},
{VK_FORMAT_PVRTC1_2BPP_UNORM_BLOCK_IMG, FORMAT_COMPATIBILITY_CLASS::PVRTC1_2BPP},
{VK_FORMAT_PVRTC1_4BPP_SRGB_BLOCK_IMG, FORMAT_COMPATIBILITY_CLASS::PVRTC1_4BPP},
{VK_FORMAT_PVRTC1_4BPP_UNORM_BLOCK_IMG, FORMAT_COMPATIBILITY_CLASS::PVRTC1_4BPP},
{VK_FORMAT_PVRTC2_2BPP_SRGB_BLOCK_IMG, FORMAT_COMPATIBILITY_CLASS::PVRTC2_2BPP},
{VK_FORMAT_PVRTC2_2BPP_UNORM_BLOCK_IMG, FORMAT_COMPATIBILITY_CLASS::PVRTC2_2BPP},
{VK_FORMAT_PVRTC2_4BPP_SRGB_BLOCK_IMG, FORMAT_COMPATIBILITY_CLASS::PVRTC2_4BPP},
{VK_FORMAT_PVRTC2_4BPP_UNORM_BLOCK_IMG, FORMAT_COMPATIBILITY_CLASS::PVRTC2_4BPP},
{VK_FORMAT_R10X6G10X6B10X6A10X6_UNORM_4PACK16, FORMAT_COMPATIBILITY_CLASS::_64BIT_R10G10B10A10},
{VK_FORMAT_R10X6G10X6_UNORM_2PACK16, FORMAT_COMPATIBILITY_CLASS::_32BIT},
{VK_FORMAT_R10X6_UNORM_PACK16, FORMAT_COMPATIBILITY_CLASS::_16BIT},
{VK_FORMAT_R12X4G12X4B12X4A12X4_UNORM_4PACK16, FORMAT_COMPATIBILITY_CLASS::_64BIT_R12G12B12A12},
{VK_FORMAT_R12X4G12X4_UNORM_2PACK16, FORMAT_COMPATIBILITY_CLASS::_32BIT},
{VK_FORMAT_R12X4_UNORM_PACK16, FORMAT_COMPATIBILITY_CLASS::_16BIT},
{VK_FORMAT_R16G16B16A16_SFLOAT, FORMAT_COMPATIBILITY_CLASS::_64BIT},
{VK_FORMAT_R16G16B16A16_SINT, FORMAT_COMPATIBILITY_CLASS::_64BIT},
{VK_FORMAT_R16G16B16A16_SNORM, FORMAT_COMPATIBILITY_CLASS::_64BIT},
{VK_FORMAT_R16G16B16A16_SSCALED, FORMAT_COMPATIBILITY_CLASS::_64BIT},
{VK_FORMAT_R16G16B16A16_UINT, FORMAT_COMPATIBILITY_CLASS::_64BIT},
{VK_FORMAT_R16G16B16A16_UNORM, FORMAT_COMPATIBILITY_CLASS::_64BIT},
{VK_FORMAT_R16G16B16A16_USCALED, FORMAT_COMPATIBILITY_CLASS::_64BIT},
{VK_FORMAT_R16G16B16_SFLOAT, FORMAT_COMPATIBILITY_CLASS::_48BIT},
{VK_FORMAT_R16G16B16_SINT, FORMAT_COMPATIBILITY_CLASS::_48BIT},
{VK_FORMAT_R16G16B16_SNORM, FORMAT_COMPATIBILITY_CLASS::_48BIT},
{VK_FORMAT_R16G16B16_SSCALED, FORMAT_COMPATIBILITY_CLASS::_48BIT},
{VK_FORMAT_R16G16B16_UINT, FORMAT_COMPATIBILITY_CLASS::_48BIT},
{VK_FORMAT_R16G16B16_UNORM, FORMAT_COMPATIBILITY_CLASS::_48BIT},
{VK_FORMAT_R16G16B16_USCALED, FORMAT_COMPATIBILITY_CLASS::_48BIT},
{VK_FORMAT_R16G16_SFLOAT, FORMAT_COMPATIBILITY_CLASS::_32BIT},
{VK_FORMAT_R16G16_SINT, FORMAT_COMPATIBILITY_CLASS::_32BIT},
{VK_FORMAT_R16G16_SNORM, FORMAT_COMPATIBILITY_CLASS::_32BIT},
{VK_FORMAT_R16G16_SSCALED, FORMAT_COMPATIBILITY_CLASS::_32BIT},
{VK_FORMAT_R16G16_UINT, FORMAT_COMPATIBILITY_CLASS::_32BIT},
{VK_FORMAT_R16G16_UNORM, FORMAT_COMPATIBILITY_CLASS::_32BIT},
{VK_FORMAT_R16G16_USCALED, FORMAT_COMPATIBILITY_CLASS::_32BIT},
{VK_FORMAT_R16_SFLOAT, FORMAT_COMPATIBILITY_CLASS::_16BIT},
{VK_FORMAT_R16_SINT, FORMAT_COMPATIBILITY_CLASS::_16BIT},
{VK_FORMAT_R16_SNORM, FORMAT_COMPATIBILITY_CLASS::_16BIT},
{VK_FORMAT_R16_SSCALED, FORMAT_COMPATIBILITY_CLASS::_16BIT},
{VK_FORMAT_R16_UINT, FORMAT_COMPATIBILITY_CLASS::_16BIT},
{VK_FORMAT_R16_UNORM, FORMAT_COMPATIBILITY_CLASS::_16BIT},
{VK_FORMAT_R16_USCALED, FORMAT_COMPATIBILITY_CLASS::_16BIT},
{VK_FORMAT_R32G32B32A32_SFLOAT, FORMAT_COMPATIBILITY_CLASS::_128BIT},
{VK_FORMAT_R32G32B32A32_SINT, FORMAT_COMPATIBILITY_CLASS::_128BIT},
{VK_FORMAT_R32G32B32A32_UINT, FORMAT_COMPATIBILITY_CLASS::_128BIT},
{VK_FORMAT_R32G32B32_SFLOAT, FORMAT_COMPATIBILITY_CLASS::_96BIT},
{VK_FORMAT_R32G32B32_SINT, FORMAT_COMPATIBILITY_CLASS::_96BIT},
{VK_FORMAT_R32G32B32_UINT, FORMAT_COMPATIBILITY_CLASS::_96BIT},
{VK_FORMAT_R32G32_SFLOAT, FORMAT_COMPATIBILITY_CLASS::_64BIT},
{VK_FORMAT_R32G32_SINT, FORMAT_COMPATIBILITY_CLASS::_64BIT},
{VK_FORMAT_R32G32_UINT, FORMAT_COMPATIBILITY_CLASS::_64BIT},
{VK_FORMAT_R32_SFLOAT, FORMAT_COMPATIBILITY_CLASS::_32BIT},
{VK_FORMAT_R32_SINT, FORMAT_COMPATIBILITY_CLASS::_32BIT},
{VK_FORMAT_R32_UINT, FORMAT_COMPATIBILITY_CLASS::_32BIT},
{VK_FORMAT_R4G4B4A4_UNORM_PACK16, FORMAT_COMPATIBILITY_CLASS::_16BIT},
{VK_FORMAT_R4G4_UNORM_PACK8, FORMAT_COMPATIBILITY_CLASS::_8BIT},
{VK_FORMAT_R5G5B5A1_UNORM_PACK16, FORMAT_COMPATIBILITY_CLASS::_16BIT},
{VK_FORMAT_R5G6B5_UNORM_PACK16, FORMAT_COMPATIBILITY_CLASS::_16BIT},
{VK_FORMAT_R64G64B64A64_SFLOAT, FORMAT_COMPATIBILITY_CLASS::_256BIT},
{VK_FORMAT_R64G64B64A64_SINT, FORMAT_COMPATIBILITY_CLASS::_256BIT},
{VK_FORMAT_R64G64B64A64_UINT, FORMAT_COMPATIBILITY_CLASS::_256BIT},
{VK_FORMAT_R64G64B64_SFLOAT, FORMAT_COMPATIBILITY_CLASS::_192BIT},
{VK_FORMAT_R64G64B64_SINT, FORMAT_COMPATIBILITY_CLASS::_192BIT},
{VK_FORMAT_R64G64B64_UINT, FORMAT_COMPATIBILITY_CLASS::_192BIT},
{VK_FORMAT_R64G64_SFLOAT, FORMAT_COMPATIBILITY_CLASS::_128BIT},
{VK_FORMAT_R64G64_SINT, FORMAT_COMPATIBILITY_CLASS::_128BIT},
{VK_FORMAT_R64G64_UINT, FORMAT_COMPATIBILITY_CLASS::_128BIT},
{VK_FORMAT_R64_SFLOAT, FORMAT_COMPATIBILITY_CLASS::_64BIT},
{VK_FORMAT_R64_SINT, FORMAT_COMPATIBILITY_CLASS::_64BIT},
{VK_FORMAT_R64_UINT, FORMAT_COMPATIBILITY_CLASS::_64BIT},
{VK_FORMAT_R8G8B8A8_SINT, FORMAT_COMPATIBILITY_CLASS::_32BIT},
{VK_FORMAT_R8G8B8A8_SNORM, FORMAT_COMPATIBILITY_CLASS::_32BIT},
{VK_FORMAT_R8G8B8A8_SRGB, FORMAT_COMPATIBILITY_CLASS::_32BIT},
{VK_FORMAT_R8G8B8A8_SSCALED, FORMAT_COMPATIBILITY_CLASS::_32BIT},
{VK_FORMAT_R8G8B8A8_UINT, FORMAT_COMPATIBILITY_CLASS::_32BIT},
{VK_FORMAT_R8G8B8A8_UNORM, FORMAT_COMPATIBILITY_CLASS::_32BIT},
{VK_FORMAT_R8G8B8A8_USCALED, FORMAT_COMPATIBILITY_CLASS::_32BIT},
{VK_FORMAT_R8G8B8_SINT, FORMAT_COMPATIBILITY_CLASS::_24BIT},
{VK_FORMAT_R8G8B8_SNORM, FORMAT_COMPATIBILITY_CLASS::_24BIT},
{VK_FORMAT_R8G8B8_SRGB, FORMAT_COMPATIBILITY_CLASS::_24BIT},
{VK_FORMAT_R8G8B8_SSCALED, FORMAT_COMPATIBILITY_CLASS::_24BIT},
{VK_FORMAT_R8G8B8_UINT, FORMAT_COMPATIBILITY_CLASS::_24BIT},
{VK_FORMAT_R8G8B8_UNORM, FORMAT_COMPATIBILITY_CLASS::_24BIT},
{VK_FORMAT_R8G8B8_USCALED, FORMAT_COMPATIBILITY_CLASS::_24BIT},
{VK_FORMAT_R8G8_SINT, FORMAT_COMPATIBILITY_CLASS::_16BIT},
{VK_FORMAT_R8G8_SNORM, FORMAT_COMPATIBILITY_CLASS::_16BIT},
{VK_FORMAT_R8G8_SRGB, FORMAT_COMPATIBILITY_CLASS::_16BIT},
{VK_FORMAT_R8G8_SSCALED, FORMAT_COMPATIBILITY_CLASS::_16BIT},
{VK_FORMAT_R8G8_UINT, FORMAT_COMPATIBILITY_CLASS::_16BIT},
{VK_FORMAT_R8G8_UNORM, FORMAT_COMPATIBILITY_CLASS::_16BIT},
{VK_FORMAT_R8G8_USCALED, FORMAT_COMPATIBILITY_CLASS::_16BIT},
{VK_FORMAT_R8_SINT, FORMAT_COMPATIBILITY_CLASS::_8BIT},
{VK_FORMAT_R8_SNORM, FORMAT_COMPATIBILITY_CLASS::_8BIT},
{VK_FORMAT_R8_SRGB, FORMAT_COMPATIBILITY_CLASS::_8BIT},
{VK_FORMAT_R8_SSCALED, FORMAT_COMPATIBILITY_CLASS::_8BIT},
{VK_FORMAT_R8_UINT, FORMAT_COMPATIBILITY_CLASS::_8BIT},
{VK_FORMAT_R8_UNORM, FORMAT_COMPATIBILITY_CLASS::_8BIT},
{VK_FORMAT_R8_USCALED, FORMAT_COMPATIBILITY_CLASS::_8BIT},
{VK_FORMAT_S8_UINT, FORMAT_COMPATIBILITY_CLASS::S8},
{VK_FORMAT_X8_D24_UNORM_PACK32, FORMAT_COMPATIBILITY_CLASS::D24},
{VK_FORMAT_UNDEFINED, FORMAT_COMPATIBILITY_CLASS::NONE},
};
/**
* @return If the two formats are compatible according to Vulkan's format compatibility rules
* @url
* https://registry.khronos.org/vulkan/specs/1.3-extensions/html/vkspec.html#formats-compatibility
*/
static bool IsVulkanFormatCompatible(vk::Format lhs, vk::Format rhs) {
if (lhs == rhs) {
return true;
}
return vkFormatClassTable.at(VkFormat(lhs)) == vkFormatClassTable.at(VkFormat(rhs));
}
} // namespace VideoCore } // namespace VideoCore

View File

@ -14,62 +14,6 @@ namespace VideoCore {
using namespace Vulkan; using namespace Vulkan;
bool ImageInfo::IsBlockCoded() const {
switch (pixel_format) {
case vk::Format::eBc1RgbaSrgbBlock:
case vk::Format::eBc1RgbaUnormBlock:
case vk::Format::eBc1RgbSrgbBlock:
case vk::Format::eBc1RgbUnormBlock:
case vk::Format::eBc2SrgbBlock:
case vk::Format::eBc2UnormBlock:
case vk::Format::eBc3SrgbBlock:
case vk::Format::eBc3UnormBlock:
case vk::Format::eBc4SnormBlock:
case vk::Format::eBc4UnormBlock:
case vk::Format::eBc5SnormBlock:
case vk::Format::eBc5UnormBlock:
case vk::Format::eBc6HSfloatBlock:
case vk::Format::eBc6HUfloatBlock:
case vk::Format::eBc7SrgbBlock:
case vk::Format::eBc7UnormBlock:
return true;
default:
return false;
}
}
bool ImageInfo::IsPacked() const {
switch (pixel_format) {
case vk::Format::eB5G5R5A1UnormPack16:
[[fallthrough]];
case vk::Format::eB5G6R5UnormPack16:
return true;
default:
return false;
}
}
bool ImageInfo::IsDepthStencil() const {
switch (pixel_format) {
case vk::Format::eD16Unorm:
case vk::Format::eD16UnormS8Uint:
case vk::Format::eD32Sfloat:
case vk::Format::eD32SfloatS8Uint:
return true;
default:
return false;
}
}
bool ImageInfo::HasStencil() const {
if (pixel_format == vk::Format::eD32SfloatS8Uint ||
pixel_format == vk::Format::eD24UnormS8Uint ||
pixel_format == vk::Format::eD16UnormS8Uint) {
return true;
}
return false;
}
static vk::ImageUsageFlags ImageUsageFlags(const ImageInfo& info) { static vk::ImageUsageFlags ImageUsageFlags(const ImageInfo& info) {
vk::ImageUsageFlags usage = vk::ImageUsageFlagBits::eTransferSrc | vk::ImageUsageFlags usage = vk::ImageUsageFlagBits::eTransferSrc |
vk::ImageUsageFlagBits::eTransferDst | vk::ImageUsageFlagBits::eTransferDst |
@ -161,6 +105,10 @@ Image::Image(const Vulkan::Instance& instance_, Vulkan::Scheduler& scheduler_,
if (info.props.is_volume) { if (info.props.is_volume) {
flags |= vk::ImageCreateFlagBits::e2DArrayCompatible; flags |= vk::ImageCreateFlagBits::e2DArrayCompatible;
} }
// Not supported by MoltenVK.
if (info.props.is_block && instance->GetDriverID() != vk::DriverId::eMoltenvk) {
flags |= vk::ImageCreateFlagBits::eBlockTexelViewCompatible;
}
usage_flags = ImageUsageFlags(info); usage_flags = ImageUsageFlags(info);
format_features = FormatFeatureFlags(usage_flags); format_features = FormatFeatureFlags(usage_flags);
@ -372,9 +320,9 @@ void Image::CopyImage(const Image& image) {
boost::container::small_vector<vk::ImageCopy, 14> image_copy{}; boost::container::small_vector<vk::ImageCopy, 14> image_copy{};
for (u32 m = 0; m < image.info.resources.levels; ++m) { for (u32 m = 0; m < image.info.resources.levels; ++m) {
const auto mip_w = std::max(info.size.width >> m, 1u); const auto mip_w = std::max(image.info.size.width >> m, 1u);
const auto mip_h = std::max(info.size.height >> m, 1u); const auto mip_h = std::max(image.info.size.height >> m, 1u);
const auto mip_d = std::max(info.size.depth >> m, 1u); const auto mip_d = std::max(image.info.size.depth >> m, 1u);
image_copy.emplace_back(vk::ImageCopy{ image_copy.emplace_back(vk::ImageCopy{
.srcSubresource{ .srcSubresource{

View File

@ -81,7 +81,7 @@ ImageInfo::ImageInfo(const AmdGpu::Liverpool::ColorBuffer& buffer,
tiling_mode = buffer.GetTilingMode(); tiling_mode = buffer.GetTilingMode();
pixel_format = LiverpoolToVK::SurfaceFormat(buffer.GetDataFmt(), buffer.GetNumberFmt()); pixel_format = LiverpoolToVK::SurfaceFormat(buffer.GetDataFmt(), buffer.GetNumberFmt());
num_samples = buffer.NumSamples(); num_samples = buffer.NumSamples();
num_bits = NumBits(buffer.GetDataFmt()); num_bits = NumBitsPerBlock(buffer.GetDataFmt());
type = vk::ImageType::e2D; type = vk::ImageType::e2D;
size.width = hint.Valid() ? hint.width : buffer.Pitch(); size.width = hint.Valid() ? hint.width : buffer.Pitch();
size.height = hint.Valid() ? hint.height : buffer.Height(); size.height = hint.Valid() ? hint.height : buffer.Height();
@ -142,7 +142,7 @@ ImageInfo::ImageInfo(const AmdGpu::Image& image, const Shader::ImageResource& de
resources.levels = image.NumLevels(); resources.levels = image.NumLevels();
resources.layers = image.NumLayers(); resources.layers = image.NumLayers();
num_samples = image.NumSamples(); num_samples = image.NumSamples();
num_bits = NumBits(image.GetDataFmt()); num_bits = NumBitsPerBlock(image.GetDataFmt());
guest_address = image.Address(); guest_address = image.Address();
@ -152,6 +152,80 @@ ImageInfo::ImageInfo(const AmdGpu::Image& image, const Shader::ImageResource& de
UpdateSize(); UpdateSize();
} }
bool ImageInfo::IsBlockCoded() const {
switch (pixel_format) {
case vk::Format::eBc1RgbaSrgbBlock:
case vk::Format::eBc1RgbaUnormBlock:
case vk::Format::eBc1RgbSrgbBlock:
case vk::Format::eBc1RgbUnormBlock:
case vk::Format::eBc2SrgbBlock:
case vk::Format::eBc2UnormBlock:
case vk::Format::eBc3SrgbBlock:
case vk::Format::eBc3UnormBlock:
case vk::Format::eBc4SnormBlock:
case vk::Format::eBc4UnormBlock:
case vk::Format::eBc5SnormBlock:
case vk::Format::eBc5UnormBlock:
case vk::Format::eBc6HSfloatBlock:
case vk::Format::eBc6HUfloatBlock:
case vk::Format::eBc7SrgbBlock:
case vk::Format::eBc7UnormBlock:
return true;
default:
return false;
}
}
bool ImageInfo::IsPacked() const {
switch (pixel_format) {
case vk::Format::eB5G5R5A1UnormPack16:
[[fallthrough]];
case vk::Format::eB5G6R5UnormPack16:
return true;
default:
return false;
}
}
bool ImageInfo::IsDepthStencil() const {
switch (pixel_format) {
case vk::Format::eD16Unorm:
case vk::Format::eD16UnormS8Uint:
case vk::Format::eD32Sfloat:
case vk::Format::eD32SfloatS8Uint:
return true;
default:
return false;
}
}
bool ImageInfo::HasStencil() const {
if (pixel_format == vk::Format::eD32SfloatS8Uint ||
pixel_format == vk::Format::eD24UnormS8Uint ||
pixel_format == vk::Format::eD16UnormS8Uint) {
return true;
}
return false;
}
bool ImageInfo::IsCompatible(const ImageInfo& info) const {
return (pixel_format == info.pixel_format && num_samples == info.num_samples &&
num_bits == info.num_bits);
}
bool ImageInfo::IsTilingCompatible(u32 lhs, u32 rhs) const {
if (lhs == rhs) {
return true;
}
if (lhs == 0x0e && rhs == 0x0d) {
return true;
}
if (lhs == 0x0d && rhs == 0x0e) {
return true;
}
return false;
}
void ImageInfo::UpdateSize() { void ImageInfo::UpdateSize() {
mips_layout.clear(); mips_layout.clear();
MipInfo mip_info{}; MipInfo mip_info{};
@ -163,7 +237,6 @@ void ImageInfo::UpdateSize() {
if (props.is_block) { if (props.is_block) {
mip_w = (mip_w + 3) / 4; mip_w = (mip_w + 3) / 4;
mip_h = (mip_h + 3) / 4; mip_h = (mip_h + 3) / 4;
bpp *= 16;
} }
mip_w = std::max(mip_w, 1u); mip_w = std::max(mip_w, 1u);
mip_h = std::max(mip_h, 1u); mip_h = std::max(mip_h, 1u);

View File

@ -25,6 +25,11 @@ struct ImageInfo {
bool IsTiled() const { bool IsTiled() const {
return tiling_mode != AmdGpu::TilingMode::Display_Linear; return tiling_mode != AmdGpu::TilingMode::Display_Linear;
} }
Extent3D BlockDim() const {
const u32 shift = props.is_block ? 2 : 0;
return Extent3D{size.width >> shift, size.height >> shift, size.depth};
}
bool IsBlockCoded() const; bool IsBlockCoded() const;
bool IsPacked() const; bool IsPacked() const;
bool IsDepthStencil() const; bool IsDepthStencil() const;
@ -33,24 +38,8 @@ struct ImageInfo {
s32 MipOf(const ImageInfo& info) const; s32 MipOf(const ImageInfo& info) const;
s32 SliceOf(const ImageInfo& info, s32 mip) const; s32 SliceOf(const ImageInfo& info, s32 mip) const;
/// Verifies if images are compatible for subresource merging. bool IsCompatible(const ImageInfo& info) const;
bool IsCompatible(const ImageInfo& info) const { bool IsTilingCompatible(u32 lhs, u32 rhs) const;
return (pixel_format == info.pixel_format && num_samples == info.num_samples &&
num_bits == info.num_bits);
}
bool IsTilingCompatible(u32 lhs, u32 rhs) const {
if (lhs == rhs) {
return true;
}
if (lhs == 0x0e && rhs == 0x0d) {
return true;
}
if (lhs == 0x0d && rhs == 0x0e) {
return true;
}
return false;
}
void UpdateSize(); void UpdateSize();

View File

@ -9,7 +9,8 @@
namespace VideoCore { namespace VideoCore {
Sampler::Sampler(const Vulkan::Instance& instance, const AmdGpu::Sampler& sampler) { Sampler::Sampler(const Vulkan::Instance& instance, const AmdGpu::Sampler& sampler,
const AmdGpu::Liverpool::BorderColorBufferBase& border_color_base) {
if (sampler.force_degamma) { if (sampler.force_degamma) {
LOG_WARNING(Render_Vulkan, "Texture requires gamma correction"); LOG_WARNING(Render_Vulkan, "Texture requires gamma correction");
} }
@ -20,7 +21,33 @@ Sampler::Sampler(const Vulkan::Instance& instance, const AmdGpu::Sampler& sample
const float maxAnisotropy = const float maxAnisotropy =
anisotropyEnable ? std::clamp(sampler.MaxAniso(), 1.0f, instance.MaxSamplerAnisotropy()) anisotropyEnable ? std::clamp(sampler.MaxAniso(), 1.0f, instance.MaxSamplerAnisotropy())
: 1.0f; : 1.0f;
auto borderColor = LiverpoolToVK::BorderColor(sampler.border_color_type);
if (!instance.IsCustomBorderColorSupported()) {
LOG_WARNING(Render_Vulkan, "Custom border color is not supported, falling back to black");
borderColor = vk::BorderColor::eFloatOpaqueBlack;
}
const auto customColor = [&]() -> std::optional<vk::SamplerCustomBorderColorCreateInfoEXT> {
if (borderColor == vk::BorderColor::eFloatCustomEXT) {
const auto borderColorIndex = sampler.border_color_ptr.Value();
const auto borderColorBuffer = border_color_base.Address<std::array<float, 4>*>();
const auto customBorderColorArray = borderColorBuffer[borderColorIndex];
const vk::SamplerCustomBorderColorCreateInfoEXT ret{
.customBorderColor =
vk::ClearColorValue{
.float32 = customBorderColorArray,
},
.format = vk::Format::eR32G32B32A32Sfloat,
};
return ret;
} else {
return std::nullopt;
}
}();
const vk::SamplerCreateInfo sampler_ci = { const vk::SamplerCreateInfo sampler_ci = {
.pNext = customColor ? &*customColor : nullptr,
.magFilter = LiverpoolToVK::Filter(sampler.xy_mag_filter), .magFilter = LiverpoolToVK::Filter(sampler.xy_mag_filter),
.minFilter = LiverpoolToVK::Filter(sampler.xy_min_filter), .minFilter = LiverpoolToVK::Filter(sampler.xy_min_filter),
.mipmapMode = LiverpoolToVK::MipFilter(sampler.mip_filter), .mipmapMode = LiverpoolToVK::MipFilter(sampler.mip_filter),
@ -34,7 +61,7 @@ Sampler::Sampler(const Vulkan::Instance& instance, const AmdGpu::Sampler& sample
.compareOp = LiverpoolToVK::DepthCompare(sampler.depth_compare_func), .compareOp = LiverpoolToVK::DepthCompare(sampler.depth_compare_func),
.minLod = sampler.MinLod(), .minLod = sampler.MinLod(),
.maxLod = sampler.MaxLod(), .maxLod = sampler.MaxLod(),
.borderColor = LiverpoolToVK::BorderColor(sampler.border_color_type), .borderColor = borderColor,
.unnormalizedCoordinates = false, // Handled in shader due to Vulkan limitations. .unnormalizedCoordinates = false, // Handled in shader due to Vulkan limitations.
}; };
auto [sampler_result, smplr] = instance.GetDevice().createSamplerUnique(sampler_ci); auto [sampler_result, smplr] = instance.GetDevice().createSamplerUnique(sampler_ci);

View File

@ -14,7 +14,8 @@ namespace VideoCore {
class Sampler { class Sampler {
public: public:
explicit Sampler(const Vulkan::Instance& instance, const AmdGpu::Sampler& sampler); explicit Sampler(const Vulkan::Instance& instance, const AmdGpu::Sampler& sampler,
const AmdGpu::Liverpool::BorderColorBufferBase& border_color_base);
~Sampler(); ~Sampler();
Sampler(const Sampler&) = delete; Sampler(const Sampler&) = delete;

View File

@ -199,7 +199,8 @@ std::tuple<ImageId, int, int> TextureCache::ResolveOverlap(const ImageInfo& imag
scheduler.CurrentTick() - tex_cache_image.tick_accessed_last > NumFramesBeforeRemoval; scheduler.CurrentTick() - tex_cache_image.tick_accessed_last > NumFramesBeforeRemoval;
if (image_info.guest_address == tex_cache_image.info.guest_address) { // Equal address if (image_info.guest_address == tex_cache_image.info.guest_address) { // Equal address
if (image_info.size != tex_cache_image.info.size) { if (image_info.BlockDim() != tex_cache_image.info.BlockDim() ||
image_info.num_bits != tex_cache_image.info.num_bits) {
// Very likely this kind of overlap is caused by allocation from a pool. // Very likely this kind of overlap is caused by allocation from a pool.
if (safe_to_delete) { if (safe_to_delete) {
FreeImage(cache_image_id); FreeImage(cache_image_id);
@ -211,15 +212,19 @@ std::tuple<ImageId, int, int> TextureCache::ResolveOverlap(const ImageInfo& imag
return {depth_image_id, -1, -1}; return {depth_image_id, -1, -1};
} }
if (image_info.IsBlockCoded() && !tex_cache_image.info.IsBlockCoded()) {
// Compressed view of uncompressed image with same block size.
// We need to recreate the image with compressed format and copy.
return {ExpandImage(image_info, cache_image_id), -1, -1};
}
if (image_info.pixel_format != tex_cache_image.info.pixel_format || if (image_info.pixel_format != tex_cache_image.info.pixel_format ||
image_info.guest_size <= tex_cache_image.info.guest_size) { image_info.guest_size <= tex_cache_image.info.guest_size) {
auto result_id = merged_image_id ? merged_image_id : cache_image_id; auto result_id = merged_image_id ? merged_image_id : cache_image_id;
const auto& result_image = slot_images[result_id]; const auto& result_image = slot_images[result_id];
return { const bool is_compatible =
IsVulkanFormatCompatible(image_info.pixel_format, result_image.info.pixel_format) IsVulkanFormatCompatible(result_image.info.pixel_format, image_info.pixel_format);
? result_id return {is_compatible ? result_id : ImageId{}, -1, -1};
: ImageId{},
-1, -1};
} }
if (image_info.type == tex_cache_image.info.type && if (image_info.type == tex_cache_image.info.type &&
@ -299,6 +304,7 @@ ImageId TextureCache::ExpandImage(const ImageInfo& info, ImageId image_id) {
auto& new_image = slot_images[new_image_id]; auto& new_image = slot_images[new_image_id];
src_image.Transit(vk::ImageLayout::eTransferSrcOptimal, vk::AccessFlagBits2::eTransferRead, {}); src_image.Transit(vk::ImageLayout::eTransferSrcOptimal, vk::AccessFlagBits2::eTransferRead, {});
RefreshImage(new_image);
new_image.CopyImage(src_image); new_image.CopyImage(src_image);
if (src_image.binding.is_bound || src_image.binding.is_target) { if (src_image.binding.is_bound || src_image.binding.is_target) {
@ -339,7 +345,7 @@ ImageId TextureCache::FindImage(BaseDesc& desc, FindFlags flags) {
continue; continue;
} }
if (False(flags & FindFlags::RelaxFmt) && if (False(flags & FindFlags::RelaxFmt) &&
(!IsVulkanFormatCompatible(info.pixel_format, cache_image.info.pixel_format) || (!IsVulkanFormatCompatible(cache_image.info.pixel_format, info.pixel_format) ||
(cache_image.info.type != info.type && info.size != Extent3D{1, 1, 1}))) { (cache_image.info.type != info.type && info.size != Extent3D{1, 1, 1}))) {
continue; continue;
} }
@ -511,9 +517,9 @@ void TextureCache::RefreshImage(Image& image, Vulkan::Scheduler* custom_schedule
// So this calculation should be very uncommon and reasonably fast // So this calculation should be very uncommon and reasonably fast
// For now we'll just check up to 64 first pixels // For now we'll just check up to 64 first pixels
const auto addr = std::bit_cast<u8*>(image.info.guest_address); const auto addr = std::bit_cast<u8*>(image.info.guest_address);
const auto w = std::min(image.info.size.width, u32(8)); const u32 w = std::min(image.info.size.width, u32(8));
const auto h = std::min(image.info.size.height, u32(8)); const u32 h = std::min(image.info.size.height, u32(8));
const auto size = w * h * image.info.num_bits / 8; const u32 size = w * h * image.info.num_bits >> (3 + image.info.props.is_block ? 4 : 0);
const u64 hash = XXH3_64bits(addr, size); const u64 hash = XXH3_64bits(addr, size);
if (image.hash == hash) { if (image.hash == hash) {
image.flags &= ~ImageFlagBits::MaybeCpuDirty; image.flags &= ~ImageFlagBits::MaybeCpuDirty;
@ -636,9 +642,11 @@ void TextureCache::RefreshImage(Image& image, Vulkan::Scheduler* custom_schedule
image.flags &= ~ImageFlagBits::Dirty; image.flags &= ~ImageFlagBits::Dirty;
} }
vk::Sampler TextureCache::GetSampler(const AmdGpu::Sampler& sampler) { vk::Sampler TextureCache::GetSampler(
const AmdGpu::Sampler& sampler,
const AmdGpu::Liverpool::BorderColorBufferBase& border_color_base) {
const u64 hash = XXH3_64bits(&sampler, sizeof(sampler)); const u64 hash = XXH3_64bits(&sampler, sizeof(sampler));
const auto [it, new_sampler] = samplers.try_emplace(hash, instance, sampler); const auto [it, new_sampler] = samplers.try_emplace(hash, instance, sampler, border_color_base);
return it->second.Handle(); return it->second.Handle();
} }

View File

@ -139,7 +139,9 @@ public:
void RefreshImage(Image& image, Vulkan::Scheduler* custom_scheduler = nullptr); void RefreshImage(Image& image, Vulkan::Scheduler* custom_scheduler = nullptr);
/// Retrieves the sampler that matches the provided S# descriptor. /// Retrieves the sampler that matches the provided S# descriptor.
[[nodiscard]] vk::Sampler GetSampler(const AmdGpu::Sampler& sampler); [[nodiscard]] vk::Sampler GetSampler(
const AmdGpu::Sampler& sampler,
const AmdGpu::Liverpool::BorderColorBufferBase& border_color_base);
/// Retrieves the image with the specified id. /// Retrieves the image with the specified id.
[[nodiscard]] Image& GetImage(ImageId id) { [[nodiscard]] Image& GetImage(ImageId id) {

View File

@ -25,10 +25,9 @@
namespace VideoCore { namespace VideoCore {
const DetilerContext* TileManager::GetDetiler(const ImageInfo& info) const { const DetilerContext* TileManager::GetDetiler(const ImageInfo& info) const {
const auto bpp = info.num_bits * (info.props.is_block ? 16 : 1);
switch (info.tiling_mode) { switch (info.tiling_mode) {
case AmdGpu::TilingMode::Texture_MicroTiled: case AmdGpu::TilingMode::Texture_MicroTiled:
switch (bpp) { switch (info.num_bits) {
case 8: case 8:
return &detilers[DetilerType::Micro8]; return &detilers[DetilerType::Micro8];
case 16: case 16:
@ -43,7 +42,7 @@ const DetilerContext* TileManager::GetDetiler(const ImageInfo& info) const {
return nullptr; return nullptr;
} }
case AmdGpu::TilingMode::Texture_Volume: case AmdGpu::TilingMode::Texture_Volume:
switch (bpp) { switch (info.num_bits) {
case 8: case 8:
return &detilers[DetilerType::Macro8]; return &detilers[DetilerType::Macro8];
case 32: case 32:
@ -55,7 +54,7 @@ const DetilerContext* TileManager::GetDetiler(const ImageInfo& info) const {
} }
break; break;
case AmdGpu::TilingMode::Display_MicroTiled: case AmdGpu::TilingMode::Display_MicroTiled:
switch (bpp) { switch (info.num_bits) {
case 64: case 64:
return &detilers[DetilerType::Display_Micro64]; return &detilers[DetilerType::Display_Micro64];
default: default:
@ -71,7 +70,7 @@ struct DetilerParams {
u32 num_levels; u32 num_levels;
u32 pitch0; u32 pitch0;
u32 height; u32 height;
u32 sizes[14]; std::array<u32, 16> sizes;
}; };
TileManager::TileManager(const Vulkan::Instance& instance, Vulkan::Scheduler& scheduler) TileManager::TileManager(const Vulkan::Instance& instance, Vulkan::Scheduler& scheduler)
@ -270,13 +269,16 @@ std::pair<vk::Buffer, u32> TileManager::TryDetile(vk::Buffer in_buffer, u32 in_o
params.height = info.size.height; params.height = info.size.height;
if (info.tiling_mode == AmdGpu::TilingMode::Texture_Volume || if (info.tiling_mode == AmdGpu::TilingMode::Texture_Volume ||
info.tiling_mode == AmdGpu::TilingMode::Display_MicroTiled) { info.tiling_mode == AmdGpu::TilingMode::Display_MicroTiled) {
ASSERT(info.resources.levels == 1); if (info.resources.levels != 1) {
LOG_ERROR(Render_Vulkan, "Unexpected mipmaps for volume and display tilings {}",
info.resources.levels);
}
const auto tiles_per_row = info.pitch / 8u; const auto tiles_per_row = info.pitch / 8u;
const auto tiles_per_slice = tiles_per_row * ((info.size.height + 7u) / 8u); const auto tiles_per_slice = tiles_per_row * ((info.size.height + 7u) / 8u);
params.sizes[0] = tiles_per_row; params.sizes[0] = tiles_per_row;
params.sizes[1] = tiles_per_slice; params.sizes[1] = tiles_per_slice;
} else { } else {
ASSERT(info.resources.levels <= 14); ASSERT(info.resources.levels <= params.sizes.size());
std::memset(&params.sizes, 0, sizeof(params.sizes)); std::memset(&params.sizes, 0, sizeof(params.sizes));
for (int m = 0; m < info.resources.levels; ++m) { for (int m = 0; m < info.resources.levels; ++m) {
params.sizes[m] = info.mips_layout[m].size + (m > 0 ? params.sizes[m - 1] : 0); params.sizes[m] = info.mips_layout[m].size + (m > 0 ? params.sizes[m - 1] : 0);
@ -287,8 +289,7 @@ std::pair<vk::Buffer, u32> TileManager::TryDetile(vk::Buffer in_buffer, u32 in_o
&params); &params);
ASSERT((image_size % 64) == 0); ASSERT((image_size % 64) == 0);
const auto bpp = info.num_bits * (info.props.is_block ? 16u : 1u); const auto num_tiles = image_size / (64 * (info.num_bits / 8));
const auto num_tiles = image_size / (64 * (bpp / 8));
cmdbuf.dispatch(num_tiles, 1, 1); cmdbuf.dispatch(num_tiles, 1, 1);
return {out_buffer.first, 0}; return {out_buffer.first, 0};
} }