mirror of
https://github.com/shadps4-emu/shadPS4.git
synced 2025-07-24 19:14:40 +00:00
Merge branch 'shadps4-emu:main' into walker-acces-violation
This commit is contained in:
commit
ffdbcb0a01
36
.github/workflows/build.yml
vendored
36
.github/workflows/build.yml
vendored
@ -76,18 +76,13 @@ jobs:
|
||||
${{ env.cache-name }}-
|
||||
|
||||
- name: Cache CMake Build
|
||||
uses: hendrikmuhs/ccache-action@v1.2.17
|
||||
uses: hendrikmuhs/ccache-action@v1.2.18
|
||||
env:
|
||||
cache-name: ${{ runner.os }}-sdl-cache-cmake-build
|
||||
with:
|
||||
append-timestamp: false
|
||||
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
|
||||
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
|
||||
uses: jurplel/install-qt-action@v4
|
||||
with:
|
||||
version: 6.9.0
|
||||
version: 6.9.1
|
||||
host: windows
|
||||
target: desktop
|
||||
arch: win64_msvc2022_64
|
||||
@ -130,18 +125,13 @@ jobs:
|
||||
${{ env.cache-name }}-
|
||||
|
||||
- name: Cache CMake Build
|
||||
uses: hendrikmuhs/ccache-action@v1.2.17
|
||||
uses: hendrikmuhs/ccache-action@v1.2.18
|
||||
env:
|
||||
cache-name: ${{ runner.os }}-qt-cache-cmake-build
|
||||
with:
|
||||
append-timestamp: false
|
||||
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
|
||||
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 }}-
|
||||
|
||||
- name: Cache CMake Build
|
||||
uses: hendrikmuhs/ccache-action@v1.2.17
|
||||
uses: hendrikmuhs/ccache-action@v1.2.18
|
||||
env:
|
||||
cache-name: ${{runner.os}}-sdl-cache-cmake-build
|
||||
with:
|
||||
@ -228,7 +218,7 @@ jobs:
|
||||
- name: Setup Qt
|
||||
uses: jurplel/install-qt-action@v4
|
||||
with:
|
||||
version: 6.9.0
|
||||
version: 6.9.1
|
||||
host: mac
|
||||
target: desktop
|
||||
arch: clang_64
|
||||
@ -247,7 +237,7 @@ jobs:
|
||||
${{ env.cache-name }}-
|
||||
|
||||
- name: Cache CMake Build
|
||||
uses: hendrikmuhs/ccache-action@v1.2.17
|
||||
uses: hendrikmuhs/ccache-action@v1.2.18
|
||||
env:
|
||||
cache-name: ${{runner.os}}-qt-cache-cmake-build
|
||||
with:
|
||||
@ -301,7 +291,7 @@ jobs:
|
||||
${{ env.cache-name }}-
|
||||
|
||||
- name: Cache CMake Build
|
||||
uses: hendrikmuhs/ccache-action@v1.2.17
|
||||
uses: hendrikmuhs/ccache-action@v1.2.18
|
||||
env:
|
||||
cache-name: ${{ runner.os }}-sdl-cache-cmake-build
|
||||
with:
|
||||
@ -362,7 +352,7 @@ jobs:
|
||||
${{ env.cache-name }}-
|
||||
|
||||
- name: Cache CMake Build
|
||||
uses: hendrikmuhs/ccache-action@v1.2.17
|
||||
uses: hendrikmuhs/ccache-action@v1.2.18
|
||||
env:
|
||||
cache-name: ${{ runner.os }}-qt-cache-cmake-build
|
||||
with:
|
||||
@ -409,7 +399,7 @@ jobs:
|
||||
${{ env.cache-name }}-
|
||||
|
||||
- name: Cache CMake Build
|
||||
uses: hendrikmuhs/ccache-action@v1.2.17
|
||||
uses: hendrikmuhs/ccache-action@v1.2.18
|
||||
env:
|
||||
cache-name: ${{ runner.os }}-sdl-gcc-cache-cmake-build
|
||||
with:
|
||||
@ -445,7 +435,7 @@ jobs:
|
||||
${{ env.cache-name }}-
|
||||
|
||||
- name: Cache CMake Build
|
||||
uses: hendrikmuhs/ccache-action@v1.2.17
|
||||
uses: hendrikmuhs/ccache-action@v1.2.18
|
||||
env:
|
||||
cache-name: ${{ runner.os }}-qt-gcc-cache-cmake-build
|
||||
with:
|
||||
@ -494,7 +484,7 @@ jobs:
|
||||
with:
|
||||
token: ${{ secrets.SHADPS4_TOKEN_REPO }}
|
||||
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
|
||||
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 }})"
|
||||
@ -530,14 +520,14 @@ jobs:
|
||||
|
||||
# Check if release already exists and get ID
|
||||
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
|
||||
echo "Creating release in $REPO for $filename"
|
||||
release_id=$(curl -s -X POST -H "Authorization: token $GITHUB_TOKEN" \
|
||||
-H "Accept: application/vnd.github.v3+json" \
|
||||
-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 }}",
|
||||
"draft": false,
|
||||
"prerelease": true,
|
||||
|
@ -296,6 +296,8 @@ set(AJM_LIB src/core/libraries/ajm/ajm.cpp
|
||||
|
||||
set(AUDIO_LIB src/core/libraries/audio/audioin.cpp
|
||||
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.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
|
||||
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
|
||||
)
|
||||
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.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/page_manager.cpp
|
||||
src/video_core/page_manager.h
|
||||
|
@ -29,7 +29,7 @@ sudo dnf install clang git cmake libatomic alsa-lib-devel \
|
||||
openssl-devel libevdev-devel libudev-devel libXext-devel \
|
||||
qt6-qtbase-devel qt6-qtbase-private-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
|
||||
|
@ -69,7 +69,7 @@ static bool vkGuestMarkers = false;
|
||||
static bool rdocEnable = false;
|
||||
static bool isFpsColor = true;
|
||||
static bool isSeparateLogFilesEnabled = false;
|
||||
static s16 cursorState = HideCursorState::Idle;
|
||||
static int cursorState = HideCursorState::Idle;
|
||||
static int cursorHideTimeout = 5; // 5 seconds (default)
|
||||
static double trophyNotificationDuration = 6.0;
|
||||
static bool useUnifiedInputConfig = true;
|
||||
@ -78,6 +78,7 @@ static int controllerCustomColorRGB[3] = {0, 0, 255};
|
||||
static bool compatibilityData = false;
|
||||
static bool checkCompatibilityOnStartup = false;
|
||||
static std::string trophyKey;
|
||||
static bool isPSNSignedIn = false;
|
||||
|
||||
// Gui
|
||||
static bool load_game_size = true;
|
||||
@ -730,6 +731,14 @@ void setShowBackgroundImage(bool show) {
|
||||
showBackgroundImage = show;
|
||||
}
|
||||
|
||||
bool getPSNSignedIn() {
|
||||
return isPSNSignedIn;
|
||||
}
|
||||
|
||||
void setPSNSignedIn(bool sign) {
|
||||
isPSNSignedIn = sign;
|
||||
}
|
||||
|
||||
void load(const std::filesystem::path& path) {
|
||||
// If the configuration file does not exist, create it and return
|
||||
std::error_code error;
|
||||
@ -754,6 +763,7 @@ void load(const std::filesystem::path& path) {
|
||||
|
||||
isNeo = toml::find_or<bool>(general, "isPS4Pro", 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);
|
||||
isTrophyPopupDisabled = toml::find_or<bool>(general, "isTrophyPopupDisabled", false);
|
||||
trophyNotificationDuration =
|
||||
@ -953,6 +963,7 @@ void save(const std::filesystem::path& path) {
|
||||
|
||||
data["General"]["isPS4Pro"] = isNeo;
|
||||
data["General"]["isDevKit"] = isDevKit;
|
||||
data["General"]["isPSNSignedIn"] = isPSNSignedIn;
|
||||
data["General"]["isTrophyPopupDisabled"] = isTrophyPopupDisabled;
|
||||
data["General"]["trophyNotificationDuration"] = trophyNotificationDuration;
|
||||
data["General"]["playBGM"] = playBGM;
|
||||
@ -1098,6 +1109,7 @@ void setDefaultValues() {
|
||||
isHDRAllowed = false;
|
||||
isNeo = false;
|
||||
isDevKit = false;
|
||||
isPSNSignedIn = false;
|
||||
isFullscreen = false;
|
||||
isTrophyPopupDisabled = false;
|
||||
playBGM = false;
|
||||
|
@ -14,7 +14,7 @@ struct GameInstallDir {
|
||||
bool enabled;
|
||||
};
|
||||
|
||||
enum HideCursorState : s16 { Never, Idle, Always };
|
||||
enum HideCursorState : int { Never, Idle, Always };
|
||||
|
||||
void load(const std::filesystem::path& path);
|
||||
void save(const std::filesystem::path& path);
|
||||
@ -39,6 +39,7 @@ bool getCompatibilityEnabled();
|
||||
bool getCheckCompatibilityOnStartup();
|
||||
int getBackgroundImageOpacity();
|
||||
bool getShowBackgroundImage();
|
||||
bool getPSNSignedIn();
|
||||
|
||||
std::string getLogFilter();
|
||||
std::string getLogType();
|
||||
@ -111,6 +112,7 @@ void setCompatibilityEnabled(bool use);
|
||||
void setCheckCompatibilityOnStartup(bool use);
|
||||
void setBackgroundImageOpacity(int opacity);
|
||||
void setShowBackgroundImage(bool show);
|
||||
void setPSNSignedIn(bool sign);
|
||||
|
||||
void setCursorState(s16 cursorState);
|
||||
void setCursorHideTimeout(int newcursorHideTimeout);
|
||||
|
@ -140,6 +140,8 @@ bool ParseFilterRule(Filter& instance, Iterator begin, Iterator end) {
|
||||
SUB(Lib, SigninDialog) \
|
||||
SUB(Lib, Camera) \
|
||||
SUB(Lib, CompanionHttpd) \
|
||||
SUB(Lib, CompanionUtil) \
|
||||
SUB(Lib, Voice) \
|
||||
CLS(Frontend) \
|
||||
CLS(Render) \
|
||||
SUB(Render, Vulkan) \
|
||||
|
@ -98,6 +98,7 @@ enum class Class : u8 {
|
||||
Lib_Fiber, ///< The LibSceFiber implementation.
|
||||
Lib_Vdec2, ///< The LibSceVideodec2 implementation.
|
||||
Lib_Videodec, ///< The LibSceVideodec implementation.
|
||||
Lib_Voice, ///< The LibSceVoice implementation.
|
||||
Lib_RazorCpu, ///< The LibRazorCpu implementation.
|
||||
Lib_Mouse, ///< The LibSceMouse implementation
|
||||
Lib_WebBrowserDialog, ///< The LibSceWebBrowserDialog implementation
|
||||
@ -107,6 +108,7 @@ enum class Class : u8 {
|
||||
Lib_SigninDialog, ///< The LibSigninDialog implementation.
|
||||
Lib_Camera, ///< The LibCamera implementation.
|
||||
Lib_CompanionHttpd, ///< The LibCompanionHttpd implementation.
|
||||
Lib_CompanionUtil, ///< The LibCompanionUtil implementation.
|
||||
Frontend, ///< Emulator UI
|
||||
Render, ///< Video Core
|
||||
Render_Vulkan, ///< Vulkan backend
|
||||
|
@ -1,6 +1,8 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "common/scm_rev.h"
|
||||
|
||||
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_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
|
||||
|
||||
|
@ -3,6 +3,8 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace Common {
|
||||
|
||||
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_date[];
|
||||
|
||||
const std::string GetRemoteNameFromLink();
|
||||
|
||||
} // namespace Common
|
||||
|
@ -88,7 +88,8 @@ static bool FilterTcbAccess(const ZydisDecodedOperand* operands) {
|
||||
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]);
|
||||
|
||||
#if defined(_WIN32)
|
||||
@ -126,7 +127,8 @@ static bool FilterNoSSE4a(const ZydisDecodedOperand*) {
|
||||
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 &&
|
||||
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 &&
|
||||
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 InstructionGenerator = void (*)(const ZydisDecodedOperand*, Xbyak::CodeGenerator&);
|
||||
using InstructionGenerator = void (*)(void*, const ZydisDecodedOperand*, Xbyak::CodeGenerator&);
|
||||
struct PatchInfo {
|
||||
/// Filter for more granular patch conditions past just the instruction mnemonic.
|
||||
PatchFilter filter;
|
||||
@ -400,6 +439,8 @@ static const std::unordered_map<ZydisMnemonic, PatchInfo> Patches = {
|
||||
// SSE4a
|
||||
{ZYDIS_MNEMONIC_EXTRQ, {FilterNoSSE4a, GenerateEXTRQ, true}},
|
||||
{ZYDIS_MNEMONIC_INSERTQ, {FilterNoSSE4a, GenerateINSERTQ, true}},
|
||||
{ZYDIS_MNEMONIC_MOVNTSS, {FilterNoSSE4a, ReplaceMOVNTSS, false}},
|
||||
{ZYDIS_MNEMONIC_MOVNTSD, {FilterNoSSE4a, ReplaceMOVNTSD, false}},
|
||||
|
||||
#if defined(_WIN32)
|
||||
// Windows needs a trampoline.
|
||||
@ -477,7 +518,7 @@ static std::pair<bool, u64> TryPatch(u8* code, PatchModule* module) {
|
||||
auto& trampoline_gen = module->trampoline_gen;
|
||||
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.
|
||||
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.
|
||||
patch_gen.jmp(trampoline_ptr, Xbyak::CodeGenerator::LabelType::T_NEAR);
|
||||
} else {
|
||||
patch_info.generator(operands, patch_gen);
|
||||
patch_info.generator(code, operands, patch_gen);
|
||||
}
|
||||
|
||||
const auto patch_size = patch_gen.getCurr() - code;
|
||||
|
@ -3,6 +3,8 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common/types.h"
|
||||
|
||||
// companion_httpd error codes
|
||||
constexpr int ORBIS_COMPANION_HTTPD_ERROR_UNKNOWN = 0x80E40001;
|
||||
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_NOT_CONNECTED = 0x80E4000D;
|
||||
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;
|
72
src/core/libraries/companion/companion_util.cpp
Normal file
72
src/core/libraries/companion/companion_util.cpp
Normal 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
|
33
src/core/libraries/companion/companion_util.h
Normal file
33
src/core/libraries/companion/companion_util.h
Normal 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
|
@ -98,6 +98,11 @@ bool EqueueInternal::RemoveEvent(u64 id, s16 filter) {
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
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 (filter == SceKernelEvent::Filter::VideoOut) {
|
||||
event.TriggerDisplay(trigger_data);
|
||||
} else if (filter == SceKernelEvent::Filter::User) {
|
||||
event.TriggerUser(trigger_data);
|
||||
} else {
|
||||
event.Trigger(trigger_data);
|
||||
}
|
||||
@ -267,27 +274,15 @@ int PS4_SYSV_ABI sceKernelWaitEqueue(SceKernelEqueue eq, SceKernelEvent* ev, int
|
||||
return ORBIS_KERNEL_ERROR_EINVAL;
|
||||
}
|
||||
|
||||
// When the timeout is nullptr, we wait indefinitely
|
||||
if (eq->HasSmallTimer()) {
|
||||
if (timo == nullptr) {
|
||||
*out = eq->WaitForSmallTimer(ev, num, 0);
|
||||
} 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->WaitForSmallTimer(ev, num, *timo);
|
||||
}
|
||||
if (timo == nullptr) {
|
||||
// When the timeout is nullptr, we wait indefinitely
|
||||
*out = eq->WaitForEvents(ev, num, 0);
|
||||
} 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 {
|
||||
if (timo == nullptr) {
|
||||
*out = eq->WaitForEvents(ev, num, 0);
|
||||
} 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);
|
||||
}
|
||||
// Wait for up to the specified timeout value
|
||||
*out = eq->WaitForEvents(ev, num, *timo);
|
||||
}
|
||||
|
||||
if (*out == 0) {
|
||||
|
@ -98,6 +98,12 @@ struct EqueueEvent {
|
||||
event.data = reinterpret_cast<uintptr_t>(data);
|
||||
}
|
||||
|
||||
void TriggerUser(void* data) {
|
||||
is_triggered = true;
|
||||
event.fflags++;
|
||||
event.udata = data;
|
||||
}
|
||||
|
||||
void TriggerDisplay(void* data) {
|
||||
is_triggered = true;
|
||||
if (data != nullptr) {
|
||||
|
@ -273,6 +273,10 @@ void RegisterKernel(Core::Loader::SymbolsResolver* sym) {
|
||||
Libraries::Net::sceNetInetNtop); // TODO fix it to sys_ ...
|
||||
LIB_FUNCTION("4n51s0zEf0c", "libScePosix", 1, "libkernel", 1, 1,
|
||||
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
|
||||
|
@ -8,7 +8,6 @@
|
||||
#include "common/logging/log.h"
|
||||
#include "common/scope_exit.h"
|
||||
#include "common/singleton.h"
|
||||
#include "core/file_sys/fs.h"
|
||||
#include "core/libraries/kernel/kernel.h"
|
||||
#include "core/libraries/kernel/memory.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 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) {
|
||||
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;
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI sceKernelMapNamedFlexibleMemory(void** addr_in_out, std::size_t len, int prot,
|
||||
int flags, const char* name) {
|
||||
|
||||
s32 PS4_SYSV_ABI sceKernelMapNamedFlexibleMemory(void** addr_in_out, u64 len, s32 prot, s32 flags,
|
||||
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)) {
|
||||
LOG_ERROR(Kernel_Vmm, "len is 0 or not 16kb multiple");
|
||||
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 auto mem_prot = static_cast<Core::MemoryProt>(prot);
|
||||
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();
|
||||
return memory->MapMemory(addr_in_out, in_addr, len, mem_prot, map_flags,
|
||||
Core::VMAType::Flexible, name);
|
||||
const auto ret = memory->MapMemory(addr_in_out, in_addr, len, mem_prot, map_flags,
|
||||
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,
|
||||
int flags) {
|
||||
s32 PS4_SYSV_ABI sceKernelMapFlexibleMemory(void** addr_in_out, u64 len, s32 prot, s32 flags) {
|
||||
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);
|
||||
}
|
||||
|
||||
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::MemoryProt protection_flags = static_cast<Core::MemoryProt>(prot);
|
||||
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::MemoryProt protection_flags = static_cast<Core::MemoryProt>(prot);
|
||||
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;
|
||||
}
|
||||
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,
|
||||
entries[i].operation, entries[i].length, result);
|
||||
break;
|
||||
@ -359,7 +369,7 @@ s32 PS4_SYSV_ABI sceKernelBatchMap2(OrbisKernelBatchMapEntry* entries, int numEn
|
||||
break;
|
||||
}
|
||||
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);
|
||||
LOG_INFO(Kernel_Vmm, "entry = {}, operation = {}, len = {:#x}, result = {}", i,
|
||||
entries[i].operation, entries[i].length, result);
|
||||
@ -380,7 +390,7 @@ s32 PS4_SYSV_ABI sceKernelBatchMap2(OrbisKernelBatchMapEntry* entries, int numEn
|
||||
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) {
|
||||
LOG_ERROR(Kernel_Vmm, "name is invalid!");
|
||||
return ORBIS_KERNEL_ERROR_EFAULT;
|
||||
@ -396,8 +406,8 @@ s32 PS4_SYSV_ABI sceKernelSetVirtualRangeName(const void* addr, size_t len, cons
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI sceKernelMemoryPoolExpand(u64 searchStart, u64 searchEnd, size_t len,
|
||||
size_t alignment, u64* physAddrOut) {
|
||||
s32 PS4_SYSV_ABI sceKernelMemoryPoolExpand(u64 searchStart, u64 searchEnd, u64 len, u64 alignment,
|
||||
u64* physAddrOut) {
|
||||
if (searchStart < 0 || searchEnd <= searchStart) {
|
||||
LOG_ERROR(Kernel_Vmm, "Provided address range is invalid!");
|
||||
return ORBIS_KERNEL_ERROR_EINVAL;
|
||||
@ -439,10 +449,10 @@ s32 PS4_SYSV_ABI sceKernelMemoryPoolExpand(u64 searchStart, u64 searchEnd, size_
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI sceKernelMemoryPoolReserve(void* addrIn, size_t len, size_t alignment, int flags,
|
||||
void** addrOut) {
|
||||
LOG_INFO(Kernel_Vmm, "addrIn = {}, len = {:#x}, alignment = {:#x}, flags = {:#x}",
|
||||
fmt::ptr(addrIn), len, alignment, flags);
|
||||
s32 PS4_SYSV_ABI sceKernelMemoryPoolReserve(void* addr_in, u64 len, u64 alignment, s32 flags,
|
||||
void** addr_out) {
|
||||
LOG_INFO(Kernel_Vmm, "addr_in = {}, len = {:#x}, alignment = {:#x}, flags = {:#x}",
|
||||
fmt::ptr(addr_in), len, alignment, flags);
|
||||
|
||||
if (len == 0 || !Common::Is2MBAligned(len)) {
|
||||
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();
|
||||
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);
|
||||
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) {
|
||||
LOG_ERROR(Kernel_Vmm, "Address is invalid!");
|
||||
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);
|
||||
}
|
||||
|
||||
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) {
|
||||
LOG_ERROR(Kernel_Vmm, "Address is invalid!");
|
||||
return ORBIS_KERNEL_ERROR_EINVAL;
|
||||
@ -523,12 +535,12 @@ s32 PS4_SYSV_ABI sceKernelMemoryPoolBatch(const OrbisKernelMemoryPoolBatchEntry*
|
||||
break;
|
||||
}
|
||||
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);
|
||||
break;
|
||||
}
|
||||
case OrbisKernelMemoryPoolOpcode::TypeProtect: {
|
||||
result = sceKernelMTypeProtect(
|
||||
result = sceKernelMtypeprotect(
|
||||
entry.type_protect_params.addr, entry.type_protect_params.len,
|
||||
entry.type_protect_params.type, entry.type_protect_params.prot);
|
||||
break;
|
||||
@ -553,30 +565,48 @@ s32 PS4_SYSV_ABI sceKernelMemoryPoolBatch(const OrbisKernelMemoryPoolBatchEntry*
|
||||
return result;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceKernelMmap(void* addr, u64 len, int prot, int flags, int fd, size_t offset,
|
||||
void** res) {
|
||||
LOG_INFO(Kernel_Vmm, "called addr = {}, len = {}, prot = {}, flags = {}, fd = {}, offset = {}",
|
||||
fmt::ptr(addr), len, prot, flags, fd, offset);
|
||||
auto* h = Common::Singleton<Core::FileSys::HandleTable>::Instance();
|
||||
void* PS4_SYSV_ABI posix_mmap(void* addr, u64 len, s32 prot, s32 flags, s32 fd, s64 phys_addr) {
|
||||
LOG_INFO(Kernel_Vmm,
|
||||
"called addr = {}, len = {}, prot = {}, flags = {}, fd = {}, phys_addr = {}",
|
||||
fmt::ptr(addr), len, prot, flags, fd, phys_addr);
|
||||
|
||||
void* addr_out;
|
||||
auto* memory = Core::Memory::Instance();
|
||||
const auto mem_prot = static_cast<Core::MemoryProt>(prot);
|
||||
const auto mem_flags = static_cast<Core::MemoryMapFlags>(flags);
|
||||
|
||||
s32 result = ORBIS_OK;
|
||||
if (fd == -1) {
|
||||
return memory->MapMemory(res, std::bit_cast<VAddr>(addr), len, mem_prot, mem_flags,
|
||||
Core::VMAType::Flexible);
|
||||
result = memory->MapMemory(&addr_out, std::bit_cast<VAddr>(addr), len, mem_prot, mem_flags,
|
||||
Core::VMAType::Flexible);
|
||||
} else {
|
||||
const uintptr_t handle = h->GetFile(fd)->f.GetFileMapping();
|
||||
return memory->MapFile(res, std::bit_cast<VAddr>(addr), len, mem_prot, mem_flags, handle,
|
||||
offset);
|
||||
result = memory->MapFile(&addr_out, std::bit_cast<VAddr>(addr), len, mem_prot, mem_flags,
|
||||
fd, phys_addr);
|
||||
}
|
||||
|
||||
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) {
|
||||
void* ptr;
|
||||
LOG_INFO(Kernel_Vmm, "posix mmap redirect to sceKernelMmap");
|
||||
int result = sceKernelMmap(addr, len, prot, flags, fd, offset, &ptr);
|
||||
ASSERT(result == 0);
|
||||
return ptr;
|
||||
s32 PS4_SYSV_ABI sceKernelMmap(void* addr, u64 len, s32 prot, s32 flags, s32 fd, s64 phys_addr,
|
||||
void** res) {
|
||||
void* addr_out = posix_mmap(addr, len, prot, flags, fd, phys_addr);
|
||||
|
||||
if (addr_out == reinterpret_cast<void*>(-1)) {
|
||||
// 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) {
|
||||
@ -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,
|
||||
address, size);
|
||||
|
||||
auto* memory = Core::Memory::Instance();
|
||||
memory->SetPrtArea(id, address, size);
|
||||
|
||||
PrtApertures[id] = {address, size};
|
||||
return ORBIS_OK;
|
||||
}
|
||||
@ -678,8 +711,9 @@ void RegisterMemory(Core::Loader::SymbolsResolver* sym) {
|
||||
LIB_FUNCTION("n1-v6FgU7MQ", "libkernel", 1, "libkernel", 1, 1,
|
||||
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
|
||||
LIB_FUNCTION("qCSfqDILlns", "libkernel", 1, "libkernel", 1, 1, sceKernelMemoryPoolExpand);
|
||||
|
@ -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,
|
||||
size_t infoSize);
|
||||
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,
|
||||
int flags, const char* name);
|
||||
s32 PS4_SYSV_ABI sceKernelMapFlexibleMemory(void** addr_in_out, std::size_t len, int prot,
|
||||
int flags);
|
||||
s32 PS4_SYSV_ABI sceKernelMapNamedFlexibleMemory(void** addr_in_out, u64 len, s32 prot, s32 flags,
|
||||
const char* name);
|
||||
s32 PS4_SYSV_ABI sceKernelMapFlexibleMemory(void** addr_in_out, u64 len, s32 prot, s32 flags);
|
||||
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,
|
||||
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,
|
||||
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,
|
||||
size_t alignment, u64* physAddrOut);
|
||||
s32 PS4_SYSV_ABI sceKernelMemoryPoolReserve(void* addrIn, size_t len, size_t alignment, int flags,
|
||||
void** addrOut);
|
||||
s32 PS4_SYSV_ABI sceKernelMemoryPoolCommit(void* addr, size_t len, int type, int prot, int flags);
|
||||
s32 PS4_SYSV_ABI sceKernelMemoryPoolDecommit(void* addr, size_t len, int flags);
|
||||
s32 PS4_SYSV_ABI sceKernelMemoryPoolExpand(u64 searchStart, u64 searchEnd, u64 len, u64 alignment,
|
||||
u64* physAddrOut);
|
||||
s32 PS4_SYSV_ABI sceKernelMemoryPoolReserve(void* addr_in, u64 len, u64 alignment, s32 flags,
|
||||
void** addr_out);
|
||||
s32 PS4_SYSV_ABI sceKernelMemoryPoolCommit(void* addr, u64 len, s32 type, s32 prot, s32 flags);
|
||||
s32 PS4_SYSV_ABI sceKernelMemoryPoolDecommit(void* addr, u64 len, s32 flags);
|
||||
s32 PS4_SYSV_ABI sceKernelMemoryPoolBatch(const OrbisKernelMemoryPoolBatchEntry* entries, s32 count,
|
||||
s32* num_processed, s32 flags);
|
||||
|
||||
|
@ -426,6 +426,7 @@ void RegisterMutex(Core::Loader::SymbolsResolver* sym) {
|
||||
// Posix
|
||||
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("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("ltCfaGr2JGE", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_mutex_destroy);
|
||||
LIB_FUNCTION("dQHWEsJtoE4", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_mutexattr_init);
|
||||
|
@ -576,8 +576,19 @@ int PS4_SYSV_ABI posix_pthread_getaffinity_np(PthreadT thread, size_t cpusetsize
|
||||
if (thread == nullptr || cpusetp == nullptr) {
|
||||
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;
|
||||
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,
|
||||
@ -585,11 +596,23 @@ int PS4_SYSV_ABI posix_pthread_setaffinity_np(PthreadT thread, size_t cpusetsize
|
||||
if (thread == nullptr || cpusetp == nullptr) {
|
||||
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 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) {
|
||||
|
@ -306,6 +306,8 @@ void RegisterThreadAttr(Core::Loader::SymbolsResolver* sym) {
|
||||
posix_pthread_attr_getdetachstate);
|
||||
LIB_FUNCTION("JKyG3SWyA10", "libScePosix", 1, "libkernel", 1, 1,
|
||||
posix_pthread_attr_setguardsize);
|
||||
LIB_FUNCTION("qlk9pSLsUmM", "libScePosix", 1, "libkernel", 1, 1,
|
||||
posix_pthread_attr_getschedparam);
|
||||
|
||||
// Orbis
|
||||
LIB_FUNCTION("4+h9EzwKF4I", "libkernel", 1, "libkernel", 1, 1,
|
||||
|
@ -10,6 +10,7 @@
|
||||
#include "core/libraries/avplayer/avplayer.h"
|
||||
#include "core/libraries/camera/camera.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/game_live_streaming/gamelivestreaming.h"
|
||||
#include "core/libraries/gnmdriver/gnmdriver.h"
|
||||
@ -59,6 +60,7 @@
|
||||
#include "core/libraries/videodec/videodec.h"
|
||||
#include "core/libraries/videodec/videodec2.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/zlib/zlib_sce.h"
|
||||
#include "fiber/fiber.h"
|
||||
@ -126,6 +128,8 @@ void InitHLELibs(Core::Loader::SymbolsResolver* sym) {
|
||||
Libraries::SigninDialog::RegisterlibSceSigninDialog(sym);
|
||||
Libraries::Camera::RegisterlibSceCamera(sym);
|
||||
Libraries::CompanionHttpd::RegisterlibSceCompanionHttpd(sym);
|
||||
Libraries::CompanionUtil::RegisterlibSceCompanionUtil(sym);
|
||||
Libraries::Voice::RegisterlibSceVoice(sym);
|
||||
}
|
||||
|
||||
} // namespace Libraries
|
||||
|
@ -955,16 +955,148 @@ u16 PS4_SYSV_ABI sceNetHtons(u16 host16) {
|
||||
return htons(host16);
|
||||
}
|
||||
|
||||
const char* PS4_SYSV_ABI sceNetInetNtop(int af, const void* src, char* dst, u32 size) {
|
||||
#ifdef WIN32
|
||||
const char* res = InetNtopA(af, src, dst, size);
|
||||
#else
|
||||
const char* res = inet_ntop(af, src, dst, size);
|
||||
#endif
|
||||
if (res == nullptr) {
|
||||
UNREACHABLE();
|
||||
// there isn't a strlcpy function in windows so implement one
|
||||
u64 strlcpy(char* dst, const char* src, u64 size) {
|
||||
u64 src_len = strlen(src);
|
||||
|
||||
if (size > 0) {
|
||||
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() {
|
||||
|
@ -20,6 +20,10 @@ class SymbolsResolver;
|
||||
|
||||
namespace Libraries::Net {
|
||||
|
||||
enum OrbisNetFamily : u32 {
|
||||
ORBIS_NET_AF_INET = 2,
|
||||
ORBIS_NET_AF_INET6 = 28,
|
||||
};
|
||||
enum OrbisNetSocketType : u32 {
|
||||
ORBIS_NET_SOCK_STREAM = 1,
|
||||
ORBIS_NET_SOCK_DGRAM = 2,
|
||||
|
@ -1,6 +1,7 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "common/config.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "core/libraries/error_codes.h"
|
||||
#include "core/libraries/libs.h"
|
||||
@ -10,6 +11,8 @@
|
||||
|
||||
namespace Libraries::NpManager {
|
||||
|
||||
#define SIGNEDIN_STATUS (Config::getPSNSignedIn() ? ORBIS_OK : ORBIS_NP_ERROR_SIGNED_OUT)
|
||||
|
||||
int PS4_SYSV_ABI Func_EF4378573542A508() {
|
||||
LOG_ERROR(Lib_NpManager, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
@ -921,9 +924,16 @@ int PS4_SYSV_ABI sceNpGetAccountCountry() {
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceNpGetAccountCountryA() {
|
||||
LOG_ERROR(Lib_NpManager, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
int PS4_SYSV_ABI sceNpGetAccountCountryA(OrbisUserServiceUserId user_id,
|
||||
OrbisNpCountryCode* country_code) {
|
||||
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() {
|
||||
@ -941,8 +951,8 @@ int PS4_SYSV_ABI sceNpGetAccountId(OrbisNpOnlineId* online_id, u64* account_id)
|
||||
if (online_id == nullptr || account_id == nullptr) {
|
||||
return ORBIS_NP_ERROR_INVALID_ARGUMENT;
|
||||
}
|
||||
*account_id = 0;
|
||||
return ORBIS_NP_ERROR_SIGNED_OUT;
|
||||
*account_id = 0xFEEDFACE;
|
||||
return SIGNEDIN_STATUS;
|
||||
}
|
||||
|
||||
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) {
|
||||
return ORBIS_NP_ERROR_INVALID_ARGUMENT;
|
||||
}
|
||||
*account_id = 0;
|
||||
return ORBIS_NP_ERROR_SIGNED_OUT;
|
||||
*account_id = 0xFEEDFACE;
|
||||
return SIGNEDIN_STATUS;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceNpGetAccountLanguage() {
|
||||
@ -984,7 +994,9 @@ int PS4_SYSV_ABI sceNpGetNpId(OrbisUserServiceUserId user_id, OrbisNpId* np_id)
|
||||
if (np_id == nullptr) {
|
||||
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() {
|
||||
@ -997,7 +1009,9 @@ int PS4_SYSV_ABI sceNpGetOnlineId(OrbisUserServiceUserId user_id, OrbisNpOnlineI
|
||||
if (online_id == nullptr) {
|
||||
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() {
|
||||
@ -1014,8 +1028,8 @@ int PS4_SYSV_ABI sceNpGetState(OrbisUserServiceUserId user_id, OrbisNpState* sta
|
||||
if (state == nullptr) {
|
||||
return ORBIS_NP_ERROR_INVALID_ARGUMENT;
|
||||
}
|
||||
*state = OrbisNpState::SignedOut;
|
||||
LOG_DEBUG(Lib_NpManager, "Signed out");
|
||||
*state = Config::getPSNSignedIn() ? OrbisNpState::SignedIn : OrbisNpState::SignedOut;
|
||||
LOG_DEBUG(Lib_NpManager, "Signed {}", Config::getPSNSignedIn() ? "in" : "out");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
|
@ -32,6 +32,12 @@ struct OrbisNpId {
|
||||
u8 reserved[8];
|
||||
};
|
||||
|
||||
struct OrbisNpCountryCode {
|
||||
char country_code[2];
|
||||
char end;
|
||||
char pad;
|
||||
};
|
||||
|
||||
int PS4_SYSV_ABI Func_EF4378573542A508();
|
||||
int PS4_SYSV_ABI _sceNpIpcCreateMemoryFromKernel();
|
||||
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 sceNpGetAccountAge();
|
||||
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 sceNpGetAccountDateOfBirthA();
|
||||
int PS4_SYSV_ABI sceNpGetAccountId(OrbisNpOnlineId* online_id, u64* account_id);
|
||||
|
@ -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.z = states[i].angularVelocity.z;
|
||||
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();
|
||||
if (gyro_poll_rate != 0.0f) {
|
||||
GameController::CalculateOrientation(pData[i].acceleration,
|
||||
pData[i].angularVelocity,
|
||||
1.0f / gyro_poll_rate, pData[i].orientation);
|
||||
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,
|
||||
deltaTime, lastOrientation, outputOrientation);
|
||||
pData[i].orientation = outputOrientation;
|
||||
controller->SetLastOrientation(outputOrientation);
|
||||
}
|
||||
}
|
||||
|
||||
pData[i].touchData.touchNum =
|
||||
(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].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].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].timestamp = states[i].time;
|
||||
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.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.y = state.axes[static_cast<int>(Input::Axis::RightY)];
|
||||
pData->analogButtons.l2 = state.axes[static_cast<int>(Input::Axis::TriggerLeft)];
|
||||
pData->analogButtons.r2 = state.axes[static_cast<int>(Input::Axis::TriggerRight)];
|
||||
pData->acceleration.x = state.acceleration.x;
|
||||
pData->acceleration.y = state.acceleration.y;
|
||||
pData->acceleration.z = state.acceleration.z;
|
||||
pData->acceleration.x = state.acceleration.x * 0.098;
|
||||
pData->acceleration.y = state.acceleration.y * 0.098;
|
||||
pData->acceleration.z = state.acceleration.z * 0.098;
|
||||
pData->angularVelocity.x = state.angularVelocity.x;
|
||||
pData->angularVelocity.y = state.angularVelocity.y;
|
||||
pData->angularVelocity.z = state.angularVelocity.z;
|
||||
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();
|
||||
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,
|
||||
1.0f / gyro_poll_rate, pData->orientation);
|
||||
deltaTime, lastOrientation, outputOrientation);
|
||||
pData->orientation = outputOrientation;
|
||||
controller->SetLastOrientation(outputOrientation);
|
||||
}
|
||||
}
|
||||
pData->touchData.touchNum =
|
||||
(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].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].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->connected = true; // isConnected; //TODO fix me proper
|
||||
pData->connectedCount = 1; // connectedCount;
|
||||
|
@ -282,7 +282,12 @@ s32 PS4_SYSV_ABI sceVideoOutGetVblankStatus(int handle, SceVideoOutVblankStatus*
|
||||
|
||||
s32 PS4_SYSV_ABI sceVideoOutGetResolutionStatus(s32 handle, SceVideoOutResolutionStatus* status) {
|
||||
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;
|
||||
}
|
||||
|
||||
|
203
src/core/libraries/voice/voice.cpp
Normal file
203
src/core/libraries/voice/voice.cpp
Normal 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
|
56
src/core/libraries/voice/voice.h
Normal file
56
src/core/libraries/voice/voice.h
Normal 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
|
@ -51,7 +51,7 @@ void ZlibTaskThread(const std::stop_token& stop) {
|
||||
if (!task_queue_cv.wait(lock, stop, [&] { return !task_queue.empty(); })) {
|
||||
break;
|
||||
}
|
||||
task = task_queue.back();
|
||||
task = task_queue.front();
|
||||
task_queue.pop();
|
||||
}
|
||||
|
||||
@ -136,7 +136,7 @@ s32 PS4_SYSV_ABI sceZlibWaitForDone(u64* request_id, const u32* timeout) {
|
||||
} else {
|
||||
done_queue_cv.wait(lock, pred);
|
||||
}
|
||||
*request_id = done_queue.back();
|
||||
*request_id = done_queue.front();
|
||||
done_queue.pop();
|
||||
}
|
||||
return ORBIS_OK;
|
||||
|
@ -117,6 +117,18 @@ void Linker::Execute(const std::vector<std::string> args) {
|
||||
Common::SetCurrentThreadName("GAME_MainThread");
|
||||
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.
|
||||
EntryParams params{};
|
||||
params.argc = 1;
|
||||
@ -320,21 +332,22 @@ bool Linker::Resolve(const std::string& name, Loader::SymbolType sym_type, Modul
|
||||
sr.type = sym_type;
|
||||
|
||||
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) {
|
||||
*return_info = *record;
|
||||
|
||||
Core::Devtools::Widget::ModuleList::AddModule(sr.library);
|
||||
|
||||
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());
|
||||
if (aeronid) {
|
||||
return_info->name = aeronid->name;
|
||||
|
@ -5,6 +5,7 @@
|
||||
#include "common/assert.h"
|
||||
#include "common/config.h"
|
||||
#include "common/debug.h"
|
||||
#include "core/file_sys/fs.h"
|
||||
#include "core/libraries/kernel/memory.h"
|
||||
#include "core/libraries/kernel/orbis_error.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) {
|
||||
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.
|
||||
if (size < MinSizeToClamp) {
|
||||
return size;
|
||||
@ -94,6 +95,46 @@ u64 MemoryManager::ClampRangeSize(VAddr virtual_addr, u64 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) {
|
||||
const VAddr virtual_addr = std::bit_cast<VAddr>(address);
|
||||
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);
|
||||
}
|
||||
|
||||
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) {
|
||||
std::scoped_lock lk{mutex};
|
||||
|
||||
@ -351,7 +308,7 @@ int MemoryManager::PoolCommit(VAddr virtual_addr, size_t size, MemoryProt prot)
|
||||
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,
|
||||
bool is_exec, PAddr phys_addr, u64 alignment) {
|
||||
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;
|
||||
|
||||
// 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;
|
||||
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.
|
||||
// To account for this, unmap any reserved areas within this mapping range first.
|
||||
auto unmap_addr = mapped_addr;
|
||||
auto unmap_size = size;
|
||||
|
||||
// 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.
|
||||
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);
|
||||
unmap_addr += 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;
|
||||
remaining_size = vma.base + vma.size - mapped_addr;
|
||||
auto remaining_size = vma.base + vma.size - mapped_addr;
|
||||
if (vma.IsMapped() || remaining_size < size) {
|
||||
LOG_ERROR(Kernel_Vmm, "Unable to map {:#x} bytes at address {:#x}", size, mapped_addr);
|
||||
return ORBIS_KERNEL_ERROR_ENOMEM;
|
||||
}
|
||||
}
|
||||
|
||||
// Find the first free area starting with provided virtual address.
|
||||
if (False(flags & MemoryMapFlags::Fixed)) {
|
||||
// Provided address needs to be aligned before we can map.
|
||||
} else {
|
||||
// When MemoryMapFlags::Fixed is not specified, and mapped_addr is 0,
|
||||
// search from address 0x200000000 instead.
|
||||
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) {
|
||||
// No suitable memory areas to map to
|
||||
return ORBIS_KERNEL_ERROR_ENOMEM;
|
||||
}
|
||||
}
|
||||
|
||||
// Perform the mapping.
|
||||
*out_addr = impl.Map(mapped_addr, size, alignment, phys_addr, is_exec);
|
||||
TRACK_ALLOC(*out_addr, size, "VMEM");
|
||||
// Create a memory area representing this mapping.
|
||||
const auto new_vma_handle = CarveVMA(mapped_addr, size);
|
||||
auto& new_vma = new_vma_handle->second;
|
||||
|
||||
auto& new_vma = CarveVMA(mapped_addr, size)->second;
|
||||
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 is Flexible, we need to track how much flexible memory is used here.
|
||||
if (type == VMAType::Flexible) {
|
||||
flexible_usage += size;
|
||||
}
|
||||
|
||||
if (IsValidGpuMapping(mapped_addr, size)) {
|
||||
rasterizer->MapMemory(mapped_addr, size);
|
||||
new_vma.disallow_merge = True(flags & MemoryMapFlags::NoCoalesce);
|
||||
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;
|
||||
}
|
||||
|
||||
int MemoryManager::MapFile(void** out_addr, VAddr virtual_addr, size_t size, MemoryProt prot,
|
||||
MemoryMapFlags flags, uintptr_t fd, size_t offset) {
|
||||
s32 MemoryManager::MapFile(void** out_addr, VAddr virtual_addr, u64 size, MemoryProt prot,
|
||||
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;
|
||||
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);
|
||||
}
|
||||
|
||||
// Map the file.
|
||||
impl.MapFile(mapped_addr, size_aligned, offset, std::bit_cast<u32>(prot), fd);
|
||||
// Get the file to map
|
||||
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
|
||||
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) {
|
||||
// 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
|
||||
pool_budget += size;
|
||||
}
|
||||
|
||||
if (IsValidGpuMapping(virtual_addr, size)) {
|
||||
rasterizer->UnmapMemory(virtual_addr, size);
|
||||
}
|
||||
|
||||
// Mark region as free and attempt to coalesce it with neighbours.
|
||||
const auto new_it = CarveVMA(virtual_addr, size);
|
||||
auto& vma = new_it->second;
|
||||
@ -528,18 +512,16 @@ u64 MemoryManager::UnmapBytesFromEntry(VAddr virtual_addr, VirtualMemoryArea vma
|
||||
const auto adjusted_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 auto prot = vma_base.prot;
|
||||
|
||||
if (type == VMAType::Free) {
|
||||
return adjusted_size;
|
||||
}
|
||||
|
||||
if (type == VMAType::Flexible) {
|
||||
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.
|
||||
const auto new_it = CarveVMA(virtual_addr, adjusted_size);
|
||||
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;
|
||||
bool readonly_file = post_merge_vma.prot == MemoryProt::CpuRead && type == VMAType::File;
|
||||
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.
|
||||
impl.Unmap(vma_base_addr, vma_base_size, start_in_vma, start_in_vma + adjusted_size,
|
||||
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;
|
||||
|
||||
if (vma_base.type == VMAType::Free) {
|
||||
LOG_ERROR(Kernel_Vmm, "Cannot change protection on free memory region");
|
||||
return ORBIS_KERNEL_ERROR_EINVAL;
|
||||
}
|
||||
|
||||
// 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;
|
||||
// On PS4, protecting freed memory does nothing.
|
||||
return adjusted_size;
|
||||
}
|
||||
|
||||
// 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) {
|
||||
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_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& vma_base = it->second;
|
||||
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;
|
||||
}
|
||||
protected_bytes += result;
|
||||
} while (protected_bytes < aligned_size);
|
||||
}
|
||||
|
||||
return ORBIS_OK;
|
||||
}
|
||||
@ -798,12 +787,31 @@ s32 MemoryManager::SetDirectMemoryType(s64 phys_addr, s32 memory_type) {
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
void MemoryManager::NameVirtualRange(VAddr virtual_addr, size_t size, std::string_view name) {
|
||||
auto it = FindVMA(virtual_addr);
|
||||
void MemoryManager::NameVirtualRange(VAddr virtual_addr, u64 size, std::string_view name) {
|
||||
// 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),
|
||||
"Range provided is not fully contained in vma");
|
||||
it->second.name = name;
|
||||
auto it = FindVMA(aligned_addr);
|
||||
s64 remaining_size = aligned_size;
|
||||
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 {
|
||||
@ -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",
|
||||
virtual_addr);
|
||||
|
||||
// Align up the virtual_addr first.
|
||||
virtual_addr = Common::AlignUp(virtual_addr, alignment);
|
||||
auto it = FindVMA(virtual_addr);
|
||||
|
||||
// If the VMA is free and contains the requested mapping we are done.
|
||||
|
@ -172,6 +172,10 @@ public:
|
||||
|
||||
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);
|
||||
|
||||
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);
|
||||
|
||||
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 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",
|
||||
bool is_exec = false, PAddr phys_addr = -1, u64 alignment = 0);
|
||||
|
||||
int MapFile(void** out_addr, VAddr virtual_addr, size_t size, MemoryProt prot,
|
||||
MemoryMapFlags flags, uintptr_t fd, size_t offset);
|
||||
s32 MapFile(void** out_addr, VAddr virtual_addr, u64 size, MemoryProt prot,
|
||||
MemoryMapFlags flags, s32 fd, s64 phys_addr);
|
||||
|
||||
s32 PoolDecommit(VAddr virtual_addr, size_t size);
|
||||
|
||||
@ -221,7 +219,7 @@ public:
|
||||
|
||||
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;
|
||||
|
||||
@ -281,6 +279,18 @@ private:
|
||||
size_t pool_budget{};
|
||||
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;
|
||||
};
|
||||
|
||||
|
@ -1,6 +1,7 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <filesystem>
|
||||
#include <set>
|
||||
#include <fmt/core.h>
|
||||
|
||||
@ -62,8 +63,13 @@ Emulator::~Emulator() {
|
||||
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();
|
||||
|
||||
auto game_folder = file.parent_path();
|
||||
if (const auto game_folder_name = game_folder.filename().string();
|
||||
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::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, "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 window_title = "";
|
||||
std::string remote_url(Common::g_scm_remote_url);
|
||||
std::string remote_host;
|
||||
try {
|
||||
if (*remote_url.rbegin() == '/') {
|
||||
remote_url.pop_back();
|
||||
}
|
||||
remote_host = remote_url.substr(19, remote_url.rfind('/') - 19);
|
||||
} catch (...) {
|
||||
remote_host = "unknown";
|
||||
}
|
||||
std::string remote_host = Common::GetRemoteNameFromLink();
|
||||
if (Common::g_is_release) {
|
||||
if (remote_host == "shadps4-emu" || remote_url.length() == 0) {
|
||||
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
|
||||
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
|
||||
LoadSystemModules(game_info.game_serial);
|
||||
|
@ -25,7 +25,7 @@ public:
|
||||
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);
|
||||
|
||||
private:
|
||||
|
@ -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
|
||||
|
||||
#include <SDL3/SDL.h>
|
||||
@ -165,69 +165,37 @@ void GameController::Acceleration(int id, const float acceleration[3]) {
|
||||
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,
|
||||
Libraries::Pad::OrbisFVector3& angularVelocity,
|
||||
float deltaTime,
|
||||
Libraries::Pad::OrbisFQuaternion& lastOrientation,
|
||||
Libraries::Pad::OrbisFQuaternion& orientation) {
|
||||
float ax = acceleration.x, ay = acceleration.y, az = acceleration.z;
|
||||
float gx = angularVelocity.x, gy = angularVelocity.y, gz = angularVelocity.z;
|
||||
Libraries::Pad::OrbisFQuaternion q = lastOrientation;
|
||||
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ω = {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
|
||||
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;
|
||||
Libraries::Pad::OrbisFQuaternion qDot = {0.5f * qω.x, 0.5f * qω.y, 0.5f * qω.z, 0.5f * qω.w};
|
||||
|
||||
// Estimated direction of gravity
|
||||
float vx = 2.0f * (q2 * q4 - q1 * q3);
|
||||
float vy = 2.0f * (q1 * q2 + q3 * q4);
|
||||
float vz = q1 * q1 - q2 * q2 - q3 * q3 + q4 * q4;
|
||||
q.x += qDot.x * deltaTime;
|
||||
q.y += qDot.y * deltaTime;
|
||||
q.z += qDot.z * deltaTime;
|
||||
q.w += qDot.w * deltaTime;
|
||||
|
||||
// Error is cross product between estimated direction and measured direction of gravity
|
||||
float ex = (ay * vz - az * vy);
|
||||
float ey = (az * vx - ax * vz);
|
||||
float ez = (ax * vy - ay * vx);
|
||||
if (Ki > 0.0f) {
|
||||
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
|
||||
}
|
||||
float norm = std::sqrt(q.x * q.x + q.y * q.y + q.z * q.z + q.w * q.w);
|
||||
q.x /= norm;
|
||||
q.y /= norm;
|
||||
q.z /= norm;
|
||||
q.w /= norm;
|
||||
|
||||
// Apply feedback terms
|
||||
gx += Kp * ex + Ki * eInt[0];
|
||||
gy += Kp * ey + Ki * eInt[1];
|
||||
gz += Kp * ez + Ki * eInt[2];
|
||||
|
||||
//// 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;
|
||||
orientation.x = q.x;
|
||||
orientation.y = q.y;
|
||||
orientation.z = q.z;
|
||||
orientation.w = q.w;
|
||||
LOG_DEBUG(Lib_Pad, "Calculated orientation: {:.2f} {:.2f} {:.2f} {:.2f}", orientation.x,
|
||||
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) {
|
||||
std::scoped_lock _{m_mutex};
|
||||
m_engine = std::move(engine);
|
||||
|
@ -23,6 +23,7 @@ enum class Axis {
|
||||
};
|
||||
|
||||
struct TouchpadEntry {
|
||||
u8 ID = 0;
|
||||
bool state{};
|
||||
u16 x{};
|
||||
u16 y{};
|
||||
@ -82,9 +83,23 @@ public:
|
||||
Engine* GetEngine();
|
||||
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,
|
||||
Libraries::Pad::OrbisFVector3& angularVelocity,
|
||||
float deltaTime,
|
||||
Libraries::Pad::OrbisFQuaternion& lastOrientation,
|
||||
Libraries::Pad::OrbisFQuaternion& orientation);
|
||||
|
||||
private:
|
||||
@ -98,8 +113,15 @@ private:
|
||||
int m_connected_count = 0;
|
||||
u32 m_states_num = 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<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;
|
||||
};
|
||||
|
@ -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();
|
||||
|
||||
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);
|
||||
latestDate = dateTime.isValid() ? dateTime.toString("yyyy-MM-dd HH:mm:ss") : "Unknown date";
|
||||
|
||||
if (latestRev == currentRev.left(7)) {
|
||||
if (latestRev == currentRev) {
|
||||
if (showMessage) {
|
||||
QMessageBox::information(this, tr("Auto Updater"),
|
||||
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>(%4)</td>"
|
||||
"</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);
|
||||
layout->addWidget(updateLabel);
|
||||
|
@ -56,15 +56,7 @@ bool MainWindow::Init() {
|
||||
setMinimumSize(720, 405);
|
||||
std::string window_title = "";
|
||||
std::string remote_url(Common::g_scm_remote_url);
|
||||
std::string remote_host;
|
||||
try {
|
||||
if (*remote_url.rbegin() == '/') {
|
||||
remote_url.pop_back();
|
||||
}
|
||||
remote_host = remote_url.substr(19, remote_url.rfind('/') - 19);
|
||||
} catch (...) {
|
||||
remote_host = "unknown";
|
||||
}
|
||||
std::string remote_host = Common::GetRemoteNameFromLink();
|
||||
if (Common::g_is_release) {
|
||||
if (remote_host == "shadps4-emu" || remote_url.length() == 0) {
|
||||
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->refreshButton, &QPushButton::clicked, this, &MainWindow::RefreshGameTable);
|
||||
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->fullscreenButton, &QPushButton::clicked, this, &MainWindow::toggleFullscreen);
|
||||
|
||||
connect(ui->sizeSlider, &QSlider::valueChanged, this, [this](int value) {
|
||||
if (isTableList) {
|
||||
m_game_list_frame->icon_size =
|
||||
36 + value; // 36 is the minimum icon size to use due to text disappearing.
|
||||
m_game_list_frame->ResizeIcons(36 + value);
|
||||
Config::setIconSize(36 + value);
|
||||
48 + value; // 48 is the minimum icon size to use due to text disappearing.
|
||||
m_game_list_frame->ResizeIcons(48 + value);
|
||||
Config::setIconSize(48 + value);
|
||||
Config::setSliderPosition(value);
|
||||
} else {
|
||||
m_game_grid_frame->icon_size = 69 + value;
|
||||
|
@ -29,7 +29,6 @@ class MainWindow : public QMainWindow {
|
||||
Q_OBJECT
|
||||
signals:
|
||||
void WindowResized(QResizeEvent* event);
|
||||
void ExtractionFinished();
|
||||
|
||||
public:
|
||||
explicit MainWindow(QWidget* parent = nullptr);
|
||||
|
@ -26,7 +26,7 @@
|
||||
</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>
|
||||
<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>
|
||||
<source>No Image Available</source>
|
||||
@ -2048,7 +2048,7 @@
|
||||
</message>
|
||||
<message>
|
||||
<source> * Unsupported Vulkan Version</source>
|
||||
<translation type="unfinished"> * Unsupported Vulkan Version</translation>
|
||||
<translation> * Versión de Vulkan no soportada</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
|
@ -2048,7 +2048,7 @@
|
||||
</message>
|
||||
<message>
|
||||
<source> * Unsupported Vulkan Version</source>
|
||||
<translation type="unfinished"> * Unsupported Vulkan Version</translation>
|
||||
<translation> * Versão do Vulkan não suportada</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
|
@ -138,7 +138,7 @@
|
||||
</message>
|
||||
<message>
|
||||
<source>File Exists</source>
|
||||
<translation>Dosya mevcut</translation>
|
||||
<translation>Dosya Mevcut</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>File already exists. Do you want to replace it?</source>
|
||||
@ -1221,7 +1221,7 @@
|
||||
</message>
|
||||
<message>
|
||||
<source>Exit shadPS4</source>
|
||||
<translation>shadPS4'ten Çık</translation>
|
||||
<translation>shadPS4 Çıkış</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Exit the application.</source>
|
||||
@ -1381,7 +1381,7 @@
|
||||
</message>
|
||||
<message>
|
||||
<source>Game Boot</source>
|
||||
<translation>Oyun Başlatma</translation>
|
||||
<translation>Oyun Başlat</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Only one file can be selected!</source>
|
||||
@ -2048,7 +2048,7 @@
|
||||
</message>
|
||||
<message>
|
||||
<source> * Unsupported Vulkan Version</source>
|
||||
<translation type="unfinished"> * Unsupported Vulkan Version</translation>
|
||||
<translation> * Desteklenmeyen Vulkan Sürümü</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
|
@ -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 (Sirit::Module::*atomic_func)(Id, Id, Id, Id, Id)) {
|
||||
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);
|
||||
}
|
||||
|
||||
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) {
|
||||
return ImageAtomicU32(ctx, inst, handle, coords, value, &Sirit::Module::OpAtomicIAdd);
|
||||
}
|
||||
|
@ -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 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 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 EmitGetAttributeU32(EmitContext& ctx, IR::Attribute attr, 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 EmitFindSMsb32(EmitContext& ctx, Id value);
|
||||
Id EmitFindUMsb32(EmitContext& ctx, Id value);
|
||||
Id EmitFindUMsb64(EmitContext& ctx, Id value);
|
||||
Id EmitFindILsb32(EmitContext& ctx, Id value);
|
||||
Id EmitFindILsb64(EmitContext& ctx, Id value);
|
||||
Id EmitSMin32(EmitContext& ctx, Id a, Id b);
|
||||
|
@ -229,6 +229,20 @@ Id EmitFindUMsb32(EmitContext& ctx, Id 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) {
|
||||
return ctx.OpFindILsb(ctx.U32[1], value);
|
||||
}
|
||||
|
@ -307,7 +307,9 @@ void EmitContext::DefineInterpolatedAttribs() {
|
||||
const Id p2{OpCompositeExtract(F32[4], p_array, 2U)};
|
||||
const Id p10{OpFSub(F32[4], p1, 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_z{OpCompositeExtract(F32[1], bary_coord, 2)};
|
||||
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);
|
||||
}
|
||||
if (profile.needs_manual_interpolation) {
|
||||
gl_bary_coord_id =
|
||||
DefineVariable(F32[3], spv::BuiltIn::BaryCoordKHR, spv::StorageClass::Input);
|
||||
if (info.has_perspective_interp) {
|
||||
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++) {
|
||||
const auto& input = runtime_info.fs_info.inputs[i];
|
||||
@ -435,9 +443,12 @@ void EmitContext::DefineInputs() {
|
||||
} else {
|
||||
attr_id = DefineInput(type, semantic);
|
||||
Name(attr_id, fmt::format("fs_in_attr{}", semantic));
|
||||
}
|
||||
if (input.is_flat) {
|
||||
Decorate(attr_id, spv::Decoration::Flat);
|
||||
|
||||
if (input.is_flat) {
|
||||
Decorate(attr_id, spv::Decoration::Flat);
|
||||
} else if (IsLinear(info.interp_qualifiers[i])) {
|
||||
Decorate(attr_id, spv::Decoration::NoPerspective);
|
||||
}
|
||||
}
|
||||
input_params[semantic] =
|
||||
GetAttributeInfo(AmdGpu::NumberFormat::Float, attr_id, num_components, false);
|
||||
@ -634,7 +645,8 @@ void EmitContext::DefineOutputs() {
|
||||
}
|
||||
break;
|
||||
}
|
||||
case LogicalStage::Fragment:
|
||||
case LogicalStage::Fragment: {
|
||||
u32 num_render_targets = 0;
|
||||
for (u32 i = 0; i < IR::NumRenderTargets; i++) {
|
||||
const IR::Attribute mrt{IR::Attribute::RenderTarget0 + i};
|
||||
if (!info.stores.GetAny(mrt)) {
|
||||
@ -643,11 +655,21 @@ void EmitContext::DefineOutputs() {
|
||||
const u32 num_components = info.stores.NumComponents(mrt);
|
||||
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 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));
|
||||
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;
|
||||
}
|
||||
case LogicalStage::Geometry: {
|
||||
output_position = DefineVariable(F32[4], spv::BuiltIn::Position, spv::StorageClass::Output);
|
||||
|
||||
|
@ -293,8 +293,8 @@ public:
|
||||
|
||||
Id shared_memory_u32_type{};
|
||||
|
||||
Id interpolate_func{};
|
||||
Id gl_bary_coord_id{};
|
||||
Id bary_coord_persp_id{};
|
||||
Id bary_coord_linear_id{};
|
||||
|
||||
struct TextureDefinition {
|
||||
const VectorIds* data_types;
|
||||
|
@ -67,6 +67,9 @@ CopyShaderData ParseCopyShader(std::span<const u32> code) {
|
||||
|
||||
if (last_attr != IR::Attribute::Position0) {
|
||||
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;
|
||||
|
@ -3,8 +3,8 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <map>
|
||||
#include <span>
|
||||
#include <unordered_map>
|
||||
|
||||
#include "common/types.h"
|
||||
#include "shader_recompiler/ir/attribute.h"
|
||||
@ -12,8 +12,9 @@
|
||||
namespace Shader {
|
||||
|
||||
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 output_vertices{0};
|
||||
};
|
||||
|
||||
CopyShaderData ParseCopyShader(std::span<const u32> code);
|
||||
|
@ -1032,7 +1032,6 @@ void GcnDecodeContext::decodeInstructionMIMG(uint64_t hexInstruction) {
|
||||
|
||||
m_instruction.control.mimg = *reinterpret_cast<InstControlMIMG*>(&hexInstruction);
|
||||
m_instruction.control.mimg.mod = getMimgModifier(m_instruction.opcode);
|
||||
ASSERT(m_instruction.control.mimg.r128 == 0);
|
||||
}
|
||||
|
||||
void GcnDecodeContext::decodeInstructionDS(uint64_t hexInstruction) {
|
||||
|
@ -605,11 +605,12 @@ public:
|
||||
Info& info_, const RuntimeInfo& runtime_info_, const Profile& profile_)
|
||||
: stmt_pool{stmt_pool_}, inst_pool{inst_pool_}, block_pool{block_pool_},
|
||||
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);
|
||||
|
||||
IR::Block& first_block{*syntax_list.front().data.block};
|
||||
Translator{&first_block, info, runtime_info, profile}.EmitPrologue();
|
||||
IR::Block* first_block = syntax_list.front().data.block;
|
||||
translator.EmitPrologue(first_block);
|
||||
}
|
||||
|
||||
private:
|
||||
@ -637,8 +638,8 @@ private:
|
||||
current_block->has_multiple_predecessors = stmt.block->num_predecessors > 1;
|
||||
const u32 start = stmt.block->begin_index;
|
||||
const u32 size = stmt.block->end_index - start + 1;
|
||||
Translate(current_block, stmt.block->begin, inst_list.subspan(start, size),
|
||||
info, runtime_info, profile);
|
||||
translator.Translate(current_block, stmt.block->begin,
|
||||
inst_list.subspan(start, size));
|
||||
}
|
||||
break;
|
||||
}
|
||||
@ -820,6 +821,7 @@ private:
|
||||
Info& info;
|
||||
const RuntimeInfo& runtime_info;
|
||||
const Profile& profile;
|
||||
Translator translator;
|
||||
};
|
||||
} // Anonymous namespace
|
||||
|
||||
|
@ -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) {
|
||||
const u32 color_buffer_idx =
|
||||
u32 color_buffer_idx =
|
||||
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];
|
||||
|
||||
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) {
|
||||
const u32 color_buffer_idx =
|
||||
u32 color_buffer_idx =
|
||||
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 swizzled_comp = SwizzleMrtComponent(color_buffer, comp);
|
||||
|
||||
|
@ -114,6 +114,8 @@ void Translator::EmitScalarAlu(const GcnInst& inst) {
|
||||
return S_FF1_I32_B64(inst);
|
||||
case Opcode::S_FLBIT_I32_B32:
|
||||
return S_FLBIT_I32_B32(inst);
|
||||
case Opcode::S_FLBIT_I32_B64:
|
||||
return S_FLBIT_I32_B64(inst);
|
||||
case Opcode::S_BITSET0_B32:
|
||||
return S_BITSET_B32(inst, 0);
|
||||
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))});
|
||||
}
|
||||
|
||||
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) {
|
||||
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))};
|
||||
|
@ -21,16 +21,60 @@
|
||||
|
||||
namespace Shader::Gcn {
|
||||
|
||||
static u32 next_vgpr_num;
|
||||
static std::unordered_map<u32, IR::VectorReg> vgpr_map;
|
||||
|
||||
Translator::Translator(IR::Block* block_, Info& info_, const RuntimeInfo& runtime_info_,
|
||||
const Profile& profile_)
|
||||
: 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;
|
||||
Translator::Translator(Info& info_, const RuntimeInfo& runtime_info_, const Profile& profile_)
|
||||
: info{info_}, runtime_info{runtime_info_}, profile{profile_},
|
||||
next_vgpr_num{runtime_info.num_allocated_vgprs} {
|
||||
if (info.l_stage == LogicalStage::Fragment) {
|
||||
dst_frag_vreg = GatherInterpQualifiers();
|
||||
}
|
||||
}
|
||||
|
||||
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.SetExec(ir.Imm1(true));
|
||||
|
||||
@ -60,39 +104,7 @@ void Translator::EmitPrologue() {
|
||||
}
|
||||
break;
|
||||
case LogicalStage::Fragment:
|
||||
dst_vreg = IR::VectorReg::V0;
|
||||
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;
|
||||
}
|
||||
dst_vreg = dst_frag_vreg;
|
||||
if (runtime_info.fs_info.addr_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));
|
||||
@ -380,7 +392,7 @@ T Translator::GetSrc64(const InstOperand& operand) {
|
||||
break;
|
||||
case OperandField::VccLo:
|
||||
if constexpr (is_float) {
|
||||
UNREACHABLE();
|
||||
value = ir.PackDouble2x32(ir.CompositeConstruct(ir.GetVccLo(), ir.GetVccHi()));
|
||||
} else {
|
||||
value = ir.PackUint2x32(ir.CompositeConstruct(ir.GetVccLo(), ir.GetVccHi()));
|
||||
}
|
||||
@ -543,6 +555,26 @@ void Translator::LogMissingOpcode(const GcnInst& inst) {
|
||||
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) {
|
||||
// Emit instructions for each 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
|
||||
|
@ -4,6 +4,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <span>
|
||||
#include <unordered_map>
|
||||
#include "shader_recompiler/frontend/instruction.h"
|
||||
#include "shader_recompiler/info.h"
|
||||
#include "shader_recompiler/ir/basic_block.h"
|
||||
@ -53,15 +54,17 @@ enum class NegateMode : u32 {
|
||||
Result,
|
||||
};
|
||||
|
||||
static constexpr size_t MaxInterpVgpr = 16;
|
||||
|
||||
class Translator {
|
||||
public:
|
||||
explicit Translator(IR::Block* block_, Info& info, const RuntimeInfo& runtime_info,
|
||||
const Profile& profile);
|
||||
explicit Translator(Info& info, const RuntimeInfo& runtime_info, const Profile& profile);
|
||||
|
||||
void Translate(IR::Block* block, u32 pc, std::span<const GcnInst> inst_list);
|
||||
void TranslateInstruction(const GcnInst& inst, u32 pc);
|
||||
|
||||
// Instruction categories
|
||||
void EmitPrologue();
|
||||
void EmitPrologue(IR::Block* first_block);
|
||||
void EmitFetch(const GcnInst& inst);
|
||||
void EmitExport(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_B64(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_GETPC_B64(u32 pc, 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_CVT_I32_F64(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_U32(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_LOG_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_RSQ_F32(const GcnInst& inst);
|
||||
void V_SQRT_F32(const GcnInst& inst);
|
||||
@ -323,16 +329,18 @@ private:
|
||||
void LogMissingOpcode(const GcnInst& inst);
|
||||
|
||||
IR::VectorReg GetScratchVgpr(u32 offset);
|
||||
IR::VectorReg GatherInterpQualifiers();
|
||||
|
||||
private:
|
||||
IR::IREmitter ir;
|
||||
Info& info;
|
||||
const RuntimeInfo& runtime_info;
|
||||
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;
|
||||
};
|
||||
|
||||
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
|
||||
|
@ -110,6 +110,8 @@ void Translator::EmitVectorAlu(const GcnInst& inst) {
|
||||
return V_CVT_I32_F64(inst);
|
||||
case Opcode::V_CVT_F64_I32:
|
||||
return V_CVT_F64_I32(inst);
|
||||
case Opcode::V_CVT_F64_U32:
|
||||
return V_CVT_F64_U32(inst);
|
||||
case Opcode::V_CVT_F32_I32:
|
||||
return V_CVT_F32_I32(inst);
|
||||
case Opcode::V_CVT_F32_U32:
|
||||
@ -156,6 +158,8 @@ void Translator::EmitVectorAlu(const GcnInst& inst) {
|
||||
return V_LOG_F32(inst);
|
||||
case Opcode::V_RCP_F32:
|
||||
return V_RCP_F32(inst);
|
||||
case Opcode::V_RCP_LEGACY_F32:
|
||||
return V_RCP_LEGACY_F32(inst);
|
||||
case Opcode::V_RCP_F64:
|
||||
return V_RCP_F64(inst);
|
||||
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));
|
||||
}
|
||||
|
||||
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) {
|
||||
const IR::U32 src0{GetSrc(inst.src[0])};
|
||||
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));
|
||||
}
|
||||
|
||||
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) {
|
||||
const IR::F64 src0{GetSrc64<IR::F64>(inst.src[0])};
|
||||
SetDst64(inst.dst[0], ir.FPRecip(src0));
|
||||
|
@ -22,13 +22,14 @@ void Translator::EmitVectorInterpolation(const GcnInst& inst) {
|
||||
// VINTRP
|
||||
|
||||
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};
|
||||
SetDst(inst.dst[0], ir.GetAttribute(attrib, inst.control.vintrp.chan));
|
||||
}
|
||||
|
||||
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};
|
||||
SetDst(inst.dst[0], ir.GetAttribute(attrib, inst.control.vintrp.chan));
|
||||
}
|
||||
|
@ -70,6 +70,8 @@ void Translator::EmitVectorMemory(const GcnInst& inst) {
|
||||
return BUFFER_ATOMIC(AtomicOp::Add, inst);
|
||||
case Opcode::BUFFER_ATOMIC_SWAP:
|
||||
return BUFFER_ATOMIC(AtomicOp::Swap, inst);
|
||||
case Opcode::BUFFER_ATOMIC_CMPSWAP:
|
||||
return BUFFER_ATOMIC(AtomicOp::CmpSwap, inst);
|
||||
case Opcode::BUFFER_ATOMIC_SMIN:
|
||||
return BUFFER_ATOMIC(AtomicOp::Smin, inst);
|
||||
case Opcode::BUFFER_ATOMIC_UMIN:
|
||||
@ -152,6 +154,7 @@ void Translator::EmitVectorMemory(const GcnInst& inst) {
|
||||
|
||||
// Image gather operations
|
||||
case Opcode::IMAGE_GATHER4:
|
||||
case Opcode::IMAGE_GATHER4_L:
|
||||
case Opcode::IMAGE_GATHER4_LZ:
|
||||
case Opcode::IMAGE_GATHER4_C:
|
||||
case Opcode::IMAGE_GATHER4_O:
|
||||
@ -330,6 +333,10 @@ void Translator::BUFFER_ATOMIC(AtomicOp op, const GcnInst& inst) {
|
||||
switch (op) {
|
||||
case AtomicOp::Swap:
|
||||
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:
|
||||
return ir.BufferAtomicIAdd(handle, address, vdata_val, buffer_info);
|
||||
case AtomicOp::Smin:
|
||||
@ -377,6 +384,7 @@ void Translator::IMAGE_LOAD(bool has_mip, const GcnInst& inst) {
|
||||
IR::TextureInstInfo info{};
|
||||
info.has_lod.Assign(has_mip);
|
||||
info.is_array.Assign(mimg.da);
|
||||
info.is_r128.Assign(mimg.r128);
|
||||
const IR::Value texel = ir.ImageRead(handle, body, {}, {}, info);
|
||||
|
||||
for (u32 i = 0; i < 4; i++) {
|
||||
@ -426,6 +434,7 @@ void Translator::IMAGE_GET_RESINFO(const GcnInst& inst) {
|
||||
|
||||
IR::TextureInstInfo info{};
|
||||
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);
|
||||
|
||||
@ -451,6 +460,7 @@ void Translator::IMAGE_ATOMIC(AtomicOp op, const GcnInst& inst) {
|
||||
|
||||
IR::TextureInstInfo info{};
|
||||
info.is_array.Assign(mimg.da);
|
||||
info.is_r128.Assign(mimg.r128);
|
||||
|
||||
const IR::Value value = ir.GetVectorReg(val_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.is_array.Assign(mimg.da);
|
||||
info.is_unnormalized.Assign(mimg.unrm);
|
||||
info.is_r128.Assign(mimg.r128);
|
||||
|
||||
if (gather) {
|
||||
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{};
|
||||
info.is_array.Assign(mimg.da);
|
||||
info.is_r128.Assign(mimg.r128);
|
||||
|
||||
const IR::Value handle = ir.GetScalarReg(tsharp_reg);
|
||||
const IR::Value body = ir.CompositeConstruct(
|
||||
|
@ -84,6 +84,7 @@ struct ImageResource {
|
||||
bool is_atomic{};
|
||||
bool is_array{};
|
||||
bool is_written{};
|
||||
bool is_r128{};
|
||||
|
||||
[[nodiscard]] constexpr AmdGpu::Image GetSharp(const Info& info) const noexcept;
|
||||
};
|
||||
@ -192,6 +193,8 @@ struct Info {
|
||||
PersistentSrtInfo srt_info;
|
||||
std::vector<u32> flattened_ud_buf;
|
||||
|
||||
std::array<IR::Interpolation, 32> interp_qualifiers{};
|
||||
|
||||
IR::ScalarReg tess_consts_ptr_base = IR::ScalarReg::Max;
|
||||
s32 tess_consts_dword_offset = -1;
|
||||
|
||||
@ -205,6 +208,8 @@ struct Info {
|
||||
bool has_discard{};
|
||||
bool has_image_gather{};
|
||||
bool has_image_query{};
|
||||
bool has_perspective_interp{};
|
||||
bool has_linear_interp{};
|
||||
bool uses_atomic_float_min_max{};
|
||||
bool uses_lane_id{};
|
||||
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 {
|
||||
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()) {
|
||||
// Fall back to null image if unbound.
|
||||
return AmdGpu::Image::Null();
|
||||
|
@ -83,6 +83,16 @@ enum class Attribute : u64 {
|
||||
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 NumRenderTargets = 8;
|
||||
constexpr size_t NumParams = 32;
|
||||
@ -104,6 +114,15 @@ constexpr bool IsMrt(Attribute attribute) noexcept {
|
||||
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]] constexpr Attribute operator+(Attribute attr, int num) {
|
||||
|
@ -2,7 +2,6 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <array>
|
||||
#include <bit>
|
||||
#include <source_location>
|
||||
#include <boost/container/small_vector.hpp>
|
||||
#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);
|
||||
}
|
||||
|
||||
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) {
|
||||
return Inst<U32>(Opcode::DataAppend, counter, Imm32(0));
|
||||
}
|
||||
@ -1546,8 +1550,15 @@ U32 IREmitter::FindSMsb(const U32& value) {
|
||||
return Inst<U32>(Opcode::FindSMsb32, value);
|
||||
}
|
||||
|
||||
U32 IREmitter::FindUMsb(const U32& value) {
|
||||
return Inst<U32>(Opcode::FindUMsb32, value);
|
||||
U32 IREmitter::FindUMsb(const U32U64& 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) {
|
||||
|
@ -6,7 +6,6 @@
|
||||
#include <cstring>
|
||||
#include <type_traits>
|
||||
|
||||
#include "shader_recompiler/info.h"
|
||||
#include "shader_recompiler/ir/attribute.h"
|
||||
#include "shader_recompiler/ir/basic_block.h"
|
||||
#include "shader_recompiler/ir/condition.h"
|
||||
@ -17,6 +16,7 @@ namespace Shader::IR {
|
||||
|
||||
class IREmitter {
|
||||
public:
|
||||
explicit IREmitter() = default;
|
||||
explicit IREmitter(Block& block_) : block{&block_}, insertion_point{block->end()} {}
|
||||
explicit IREmitter(Block& block_, Block::iterator insertion_point_)
|
||||
: block{&block_}, insertion_point{insertion_point_} {}
|
||||
@ -150,6 +150,9 @@ public:
|
||||
const Value& value, BufferInstInfo info);
|
||||
[[nodiscard]] Value BufferAtomicSwap(const Value& handle, const Value& address,
|
||||
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 DataConsume(const U32& counter);
|
||||
@ -266,7 +269,7 @@ public:
|
||||
[[nodiscard]] U32 BitwiseNot(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 SMin(const U32& a, const U32& b);
|
||||
[[nodiscard]] U32 UMin(const U32& a, const U32& b);
|
||||
|
@ -126,6 +126,7 @@ OPCODE(BufferAtomicAnd32, U32, Opaq
|
||||
OPCODE(BufferAtomicOr32, U32, Opaque, Opaque, U32, )
|
||||
OPCODE(BufferAtomicXor32, U32, Opaque, Opaque, U32, )
|
||||
OPCODE(BufferAtomicSwap32, U32, Opaque, Opaque, U32, )
|
||||
OPCODE(BufferAtomicCmpSwap32, U32, Opaque, Opaque, U32, U32, )
|
||||
|
||||
// Vector utility
|
||||
OPCODE(CompositeConstructU32x2, U32x2, U32, U32, )
|
||||
@ -349,6 +350,7 @@ OPCODE(BitwiseNot32, U32, U32,
|
||||
|
||||
OPCODE(FindSMsb32, U32, U32, )
|
||||
OPCODE(FindUMsb32, U32, U32, )
|
||||
OPCODE(FindUMsb64, U32, U64, )
|
||||
OPCODE(FindILsb32, U32, U32, )
|
||||
OPCODE(FindILsb64, U32, U64, )
|
||||
OPCODE(SMin32, U32, U32, U32, )
|
||||
|
@ -15,7 +15,7 @@ struct FormatInfo {
|
||||
AmdGpu::NumberFormat num_format;
|
||||
AmdGpu::CompMapping swizzle;
|
||||
AmdGpu::NumberConversion num_conversion;
|
||||
int num_components;
|
||||
u32 num_components;
|
||||
};
|
||||
|
||||
static bool IsBufferFormatLoad(const IR::Inst& inst) {
|
||||
|
@ -1,6 +1,7 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <unordered_map>
|
||||
#include "shader_recompiler/ir/program.h"
|
||||
|
||||
namespace Shader::Optimization {
|
||||
|
@ -411,6 +411,7 @@ void PatchImageSharp(IR::Block& block, IR::Inst& inst, Info& info, Descriptors&
|
||||
.is_atomic = IsImageAtomicInstruction(inst),
|
||||
.is_array = bool(inst_info.is_array),
|
||||
.is_written = is_written,
|
||||
.is_r128 = bool(inst_info.is_r128),
|
||||
});
|
||||
|
||||
IR::IREmitter ir{block, IR::Block::InstructionList::s_iterator_to(inst)};
|
||||
|
@ -91,6 +91,19 @@ void RingAccessElimination(const IR::Program& program, const RuntimeInfo& runtim
|
||||
const auto& gs_info = runtime_info.gs_info;
|
||||
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) {
|
||||
const auto opcode = inst.GetOpcode();
|
||||
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 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 vc_read_ofs = (((offset / comp_ofs) * comp_ofs) % output_size) * 16u;
|
||||
|
@ -7,7 +7,7 @@
|
||||
#include "common/bit_field.h"
|
||||
#include "common/enum.h"
|
||||
#include "common/types.h"
|
||||
#include "video_core/amdgpu/types.h"
|
||||
#include "video_core/amdgpu/pixel_format.h"
|
||||
|
||||
namespace Shader::IR {
|
||||
|
||||
@ -44,6 +44,7 @@ union TextureInstInfo {
|
||||
BitField<9, 1, u32> is_array;
|
||||
BitField<10, 1, u32> is_unnormalized;
|
||||
BitField<11, 1, u32> is_gather;
|
||||
BitField<12, 1, u32> is_r128;
|
||||
};
|
||||
|
||||
union BufferInstInfo {
|
||||
|
@ -149,6 +149,7 @@ struct GeometryRuntimeInfo {
|
||||
u32 out_vertex_data_size{};
|
||||
AmdGpu::PrimitiveType in_primitive;
|
||||
GsOutputPrimTypes out_primitive;
|
||||
AmdGpu::Liverpool::GsMode::Mode mode;
|
||||
std::span<const u32> vs_copy;
|
||||
u64 vs_copy_hash;
|
||||
|
||||
@ -196,11 +197,13 @@ struct FragmentRuntimeInfo {
|
||||
u32 num_inputs;
|
||||
std::array<PsInput, 32> inputs;
|
||||
std::array<PsColorBuffer, MaxColorBuffers> color_buffers;
|
||||
bool dual_source_blending;
|
||||
|
||||
bool operator==(const FragmentRuntimeInfo& other) const noexcept {
|
||||
return std::ranges::equal(color_buffers, other.color_buffers) &&
|
||||
en_flags.raw == other.en_flags.raw && addr_flags.raw == other.addr_flags.raw &&
|
||||
num_inputs == other.num_inputs &&
|
||||
dual_source_blending == other.dual_source_blending &&
|
||||
std::ranges::equal(inputs.begin(), inputs.begin() + num_inputs, other.inputs.begin(),
|
||||
other.inputs.begin() + num_inputs);
|
||||
}
|
||||
|
@ -228,9 +228,12 @@ Liverpool::Task Liverpool::ProcessGraphics(std::span<const u32> dcb, std::span<c
|
||||
const u32 type = header->type;
|
||||
|
||||
switch (type) {
|
||||
default:
|
||||
UNREACHABLE_MSG("Wrong PM4 type {}", type);
|
||||
break;
|
||||
case 0:
|
||||
case 1:
|
||||
UNREACHABLE_MSG("Unsupported PM4 type {}", type);
|
||||
UNREACHABLE_MSG("Unimplemented PM4 type 0, base reg: {}, size: {}",
|
||||
header->type0.base.Value(), header->type0.NumWords());
|
||||
break;
|
||||
case 2:
|
||||
// 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;
|
||||
}
|
||||
case PM4ItOpcode::SetPredication: {
|
||||
LOG_WARNING(Render_Vulkan, "Unimplemented IT_SET_PREDICATION");
|
||||
LOG_WARNING(Render, "Unimplemented IT_SET_PREDICATION");
|
||||
break;
|
||||
}
|
||||
case PM4ItOpcode::IndexType: {
|
||||
@ -586,8 +589,7 @@ Liverpool::Task Liverpool::ProcessGraphics(std::span<const u32> dcb, std::span<c
|
||||
}
|
||||
case PM4ItOpcode::EventWrite: {
|
||||
const auto* event = reinterpret_cast<const PM4CmdEventWrite*>(header);
|
||||
LOG_DEBUG(Render_Vulkan,
|
||||
"Encountered EventWrite: event_type = {}, event_index = {}",
|
||||
LOG_DEBUG(Render, "Encountered EventWrite: event_type = {}, event_index = {}",
|
||||
magic_enum::enum_name(event->event_type.Value()),
|
||||
magic_enum::enum_name(event->event_index.Value()));
|
||||
if (event->event_type.Value() == EventType::SoVgtStreamoutFlush) {
|
||||
@ -673,6 +675,16 @@ Liverpool::Task Liverpool::ProcessGraphics(std::span<const u32> dcb, std::span<c
|
||||
}
|
||||
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: {
|
||||
const auto* mem_semaphore = reinterpret_cast<const PM4CmdMemSemaphore*>(header);
|
||||
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");
|
||||
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:
|
||||
UNREACHABLE_MSG("Unknown PM4 type 3 opcode {:#x} with count {}",
|
||||
static_cast<u32>(opcode), count);
|
||||
@ -804,6 +829,19 @@ Liverpool::Task Liverpool::ProcessCompute(const u32* acb, u32 acb_dwords, u32 vq
|
||||
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) {
|
||||
// No other types of packets were spotted so far
|
||||
UNREACHABLE_MSG("Invalid PM4 type {}", header->type.Value());
|
||||
|
@ -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 {
|
||||
BitField<0, 8, u32> base_addr_hi;
|
||||
u32 base_addr_lo;
|
||||
@ -904,7 +914,7 @@ struct Liverpool {
|
||||
}
|
||||
|
||||
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 =
|
||||
num_bytes_per_element * (slice.tile_max + 1) * 64u * NumSamples();
|
||||
return slice_size;
|
||||
@ -1169,8 +1179,16 @@ struct Liverpool {
|
||||
};
|
||||
|
||||
union GsMode {
|
||||
enum class Mode : u32 {
|
||||
Off = 0,
|
||||
ScenarioA = 1,
|
||||
ScenarioB = 2,
|
||||
ScenarioG = 3,
|
||||
ScenarioC = 4,
|
||||
};
|
||||
|
||||
u32 raw;
|
||||
BitField<0, 3, u32> mode;
|
||||
BitField<0, 3, Mode> mode;
|
||||
BitField<3, 2, u32> cut_mode;
|
||||
BitField<22, 2, u32> onchip;
|
||||
};
|
||||
@ -1299,7 +1317,9 @@ struct Liverpool {
|
||||
Scissor screen_scissor;
|
||||
INSERT_PADDING_WORDS(0xA010 - 0xA00C - 2);
|
||||
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;
|
||||
ViewportScissor window_scissor;
|
||||
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(depth_buffer.z_info) == 0xA010);
|
||||
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_scissor) == 0xA081);
|
||||
static_assert(GFX6_3D_REG_INDEX(color_target_mask) == 0xA08E);
|
||||
|
@ -111,136 +111,106 @@ std::string_view NameOf(NumberFormat fmt) {
|
||||
}
|
||||
}
|
||||
|
||||
int NumComponents(DataFormat format) {
|
||||
constexpr std::array num_components_per_element = {
|
||||
0, 1, 1, 2, 1, 2, 3, 3, 4, 4, 4, 2, 4, 3, 4, -1, 3, 4, 4, 4, 2,
|
||||
2, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, 3, 3, 3, 4, 4, 4, 1, 2, 3, 4,
|
||||
-1, -1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 1, 1};
|
||||
|
||||
const u32 index = static_cast<u32>(format);
|
||||
if (index >= num_components_per_element.size()) {
|
||||
return 0;
|
||||
}
|
||||
return num_components_per_element[index];
|
||||
}
|
||||
|
||||
int NumBits(DataFormat format) {
|
||||
const std::array num_bits_per_element = {
|
||||
0, 8, 16, 16, 32, 32, 32, 32, 32, 32, 32, 64, 64, 96, 128, -1, 16, 16, 16, 16, 32,
|
||||
32, 64, -1, -1, -1, -1, -1, -1, -1, -1, -1, 16, 16, 32, 4, 8, 8, 4, 8, 8, 8,
|
||||
-1, -1, 8, 8, 8, 8, 8, 8, 16, 16, 32, 32, 32, 64, 64, 8, 16, 1, 1};
|
||||
|
||||
const u32 index = static_cast<u32>(format);
|
||||
if (index >= num_bits_per_element.size()) {
|
||||
return 0;
|
||||
}
|
||||
return num_bits_per_element[index];
|
||||
}
|
||||
|
||||
static constexpr std::array component_bits = {
|
||||
std::array{0, 0, 0, 0}, // 0 FormatInvalid
|
||||
std::array{8, 0, 0, 0}, // 1 Format8
|
||||
std::array{16, 0, 0, 0}, // 2 Format16
|
||||
std::array{8, 8, 0, 0}, // 3 Format8_8
|
||||
std::array{32, 0, 0, 0}, // 4 Format32
|
||||
std::array{16, 16, 0, 0}, // 5 Format16_16
|
||||
std::array{11, 11, 10, 0}, // 6 Format10_11_11
|
||||
std::array{10, 11, 11, 0}, // 7 Format11_11_10
|
||||
std::array{2, 10, 10, 10}, // 8 Format10_10_10_2
|
||||
std::array{10, 10, 10, 2}, // 9 Format2_10_10_10
|
||||
std::array{8, 8, 8, 8}, // 10 Format8_8_8_8
|
||||
std::array{32, 32, 0, 0}, // 11 Format32_32
|
||||
std::array{16, 16, 16, 16}, // 12 Format16_16_16_16
|
||||
std::array{32, 32, 32, 0}, // 13 Format32_32_32
|
||||
std::array{32, 32, 32, 32}, // 14 Format32_32_32_32
|
||||
std::array{0, 0, 0, 0}, // 15
|
||||
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
|
||||
static constexpr std::array NUM_COMPONENTS = {
|
||||
0, // 0 FormatInvalid
|
||||
1, // 1 Format8
|
||||
1, // 2 Format16
|
||||
2, // 3 Format8_8
|
||||
1, // 4 Format32
|
||||
2, // 5 Format16_16
|
||||
3, // 6 Format10_11_11
|
||||
3, // 7 Format11_11_10
|
||||
4, // 8 Format10_10_10_2
|
||||
4, // 9 Format2_10_10_10
|
||||
4, // 10 Format8_8_8_8
|
||||
2, // 11 Format32_32
|
||||
4, // 12 Format16_16_16_16
|
||||
3, // 13 Format32_32_32
|
||||
4, // 14 Format32_32_32_32
|
||||
0, // 15
|
||||
3, // 16 Format5_6_5
|
||||
4, // 17 Format1_5_5_5
|
||||
4, // 18 Format5_5_5_1
|
||||
4, // 19 Format4_4_4_4
|
||||
2, // 20 Format8_24
|
||||
2, // 21 Format24_8
|
||||
2, // 22 FormatX24_8_32
|
||||
0, // 23
|
||||
0, // 24
|
||||
0, // 25
|
||||
0, // 26
|
||||
0, // 27
|
||||
0, // 28
|
||||
0, // 29
|
||||
0, // 30
|
||||
0, // 31
|
||||
3, // 32 FormatGB_GR
|
||||
3, // 33 FormatBG_RG
|
||||
4, // 34 Format5_9_9_9
|
||||
4, // 35 FormatBc1
|
||||
4, // 36 FormatBc2
|
||||
4, // 37 FormatBc3
|
||||
1, // 38 FormatBc4
|
||||
2, // 39 FormatBc5
|
||||
3, // 40 FormatBc6
|
||||
4, // 41 FormatBc7
|
||||
};
|
||||
|
||||
u32 ComponentBits(DataFormat format, u32 comp) {
|
||||
u32 NumComponents(DataFormat format) {
|
||||
const u32 index = static_cast<u32>(format);
|
||||
if (index >= component_bits.size() || comp >= 4) {
|
||||
return 0;
|
||||
}
|
||||
return component_bits[index][comp];
|
||||
ASSERT_MSG(index < NUM_COMPONENTS.size(), "Invalid data format = {}", format);
|
||||
return NUM_COMPONENTS[index];
|
||||
}
|
||||
|
||||
static constexpr std::array component_offset = {
|
||||
std::array{-1, -1, -1, -1}, // 0 FormatInvalid
|
||||
std::array{0, -1, -1, -1}, // 1 Format8
|
||||
std::array{0, -1, -1, -1}, // 2 Format16
|
||||
std::array{0, 8, -1, -1}, // 3 Format8_8
|
||||
std::array{0, -1, -1, -1}, // 4 Format32
|
||||
std::array{0, 16, -1, -1}, // 5 Format16_16
|
||||
std::array{0, 11, 22, -1}, // 6 Format10_11_11
|
||||
std::array{0, 10, 21, -1}, // 7 Format11_11_10
|
||||
std::array{0, 2, 12, 22}, // 8 Format10_10_10_2
|
||||
std::array{0, 10, 20, 30}, // 9 Format2_10_10_10
|
||||
std::array{0, 8, 16, 24}, // 10 Format8_8_8_8
|
||||
std::array{0, 32, -1, -1}, // 11 Format32_32
|
||||
std::array{0, 16, 32, 48}, // 12 Format16_16_16_16
|
||||
std::array{0, 32, 64, -1}, // 13 Format32_32_32
|
||||
std::array{0, 32, 64, 96}, // 14 Format32_32_32_32
|
||||
std::array{-1, -1, -1, -1}, // 15
|
||||
std::array{0, 5, 11, -1}, // 16 Format5_6_5
|
||||
std::array{0, 5, 10, 15}, // 17 Format1_5_5_5
|
||||
std::array{0, 1, 6, 11}, // 18 Format5_5_5_1
|
||||
std::array{0, 4, 8, 12}, // 19 Format4_4_4_4
|
||||
std::array{0, 24, -1, -1}, // 20 Format8_24
|
||||
std::array{0, 8, -1, -1}, // 21 Format24_8
|
||||
std::array{0, 8, -1, -1}, // 22 FormatX24_8_32
|
||||
std::array{-1, -1, -1, -1}, // 23
|
||||
std::array{-1, -1, -1, -1}, // 24
|
||||
std::array{-1, -1, -1, -1}, // 25
|
||||
std::array{-1, -1, -1, -1}, // 26
|
||||
std::array{-1, -1, -1, -1}, // 27
|
||||
std::array{-1, -1, -1, -1}, // 28
|
||||
std::array{-1, -1, -1, -1}, // 29
|
||||
std::array{-1, -1, -1, -1}, // 30
|
||||
std::array{-1, -1, -1, -1}, // 31
|
||||
std::array{-1, -1, -1, -1}, // 32 FormatGB_GR
|
||||
std::array{-1, -1, -1, -1}, // 33 FormatBG_RG
|
||||
std::array{-1, -1, -1, -1}, // 34 Format5_9_9_9
|
||||
std::array{-1, -1, -1, -1}, // 35 FormatBc1
|
||||
std::array{-1, -1, -1, -1}, // 36 FormatBc2
|
||||
std::array{-1, -1, -1, -1}, // 37 FormatBc3
|
||||
std::array{-1, -1, -1, -1}, // 38 FormatBc4
|
||||
std::array{-1, -1, -1, -1}, // 39 FormatBc5
|
||||
std::array{-1, -1, -1, -1}, // 40 FormatBc6
|
||||
std::array{-1, -1, -1, -1}, // 41 FormatBc7
|
||||
static constexpr std::array BITS_PER_BLOCK = {
|
||||
0, // 0 FormatInvalid
|
||||
8, // 1 Format8
|
||||
16, // 2 Format16
|
||||
16, // 3 Format8_8
|
||||
32, // 4 Format32
|
||||
32, // 5 Format16_16
|
||||
32, // 6 Format10_11_11
|
||||
32, // 7 Format11_11_10
|
||||
32, // 8 Format10_10_10_2
|
||||
32, // 9 Format2_10_10_10
|
||||
32, // 10 Format8_8_8_8
|
||||
64, // 11 Format32_32
|
||||
64, // 12 Format16_16_16_16
|
||||
96, // 13 Format32_32_32
|
||||
128, // 14 Format32_32_32_32
|
||||
0, // 15
|
||||
16, // 16 Format5_6_5
|
||||
16, // 17 Format1_5_5_5
|
||||
16, // 18 Format5_5_5_1
|
||||
16, // 19 Format4_4_4_4
|
||||
32, // 20 Format8_24
|
||||
32, // 21 Format24_8
|
||||
64, // 22 FormatX24_8_32
|
||||
0, // 23
|
||||
0, // 24
|
||||
0, // 25
|
||||
0, // 26
|
||||
0, // 27
|
||||
0, // 28
|
||||
0, // 29
|
||||
0, // 30
|
||||
0, // 31
|
||||
16, // 32 FormatGB_GR
|
||||
16, // 33 FormatBG_RG
|
||||
32, // 34 Format5_9_9_9
|
||||
64, // 35 FormatBc1
|
||||
128, // 36 FormatBc2
|
||||
128, // 37 FormatBc3
|
||||
64, // 38 FormatBc4
|
||||
128, // 39 FormatBc5
|
||||
128, // 40 FormatBc6
|
||||
128, // 41 FormatBc7
|
||||
};
|
||||
|
||||
s32 ComponentOffset(DataFormat format, u32 comp) {
|
||||
u32 NumBitsPerBlock(DataFormat format) {
|
||||
const u32 index = static_cast<u32>(format);
|
||||
if (index >= component_offset.size() || comp >= 4) {
|
||||
return -1;
|
||||
}
|
||||
return component_offset[index][comp];
|
||||
ASSERT_MSG(index < BITS_PER_BLOCK.size(), "Invalid data format = {}", format);
|
||||
return BITS_PER_BLOCK[index];
|
||||
}
|
||||
|
||||
} // namespace AmdGpu
|
||||
|
@ -5,39 +5,313 @@
|
||||
|
||||
#include <string_view>
|
||||
#include <fmt/format.h>
|
||||
#include "common/assert.h"
|
||||
#include "common/types.h"
|
||||
#include "video_core/amdgpu/types.h"
|
||||
|
||||
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,
|
||||
Sint,
|
||||
Uint,
|
||||
};
|
||||
|
||||
[[nodiscard]] constexpr NumberClass GetNumberClass(const NumberFormat nfmt) {
|
||||
switch (nfmt) {
|
||||
case NumberFormat::Sint:
|
||||
return Sint;
|
||||
case NumberFormat::Uint:
|
||||
return Uint;
|
||||
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,
|
||||
};
|
||||
|
||||
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:
|
||||
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;
|
||||
}
|
||||
|
||||
[[nodiscard]] std::string_view NameOf(DataFormat fmt);
|
||||
[[nodiscard]] std::string_view NameOf(NumberFormat fmt);
|
||||
std::string_view NameOf(DataFormat fmt);
|
||||
std::string_view NameOf(NumberFormat fmt);
|
||||
|
||||
int NumComponents(DataFormat format);
|
||||
int NumBits(DataFormat format);
|
||||
u32 ComponentBits(DataFormat format, u32 comp);
|
||||
s32 ComponentOffset(DataFormat format, u32 comp);
|
||||
u32 NumComponents(DataFormat format);
|
||||
u32 NumBitsPerBlock(DataFormat format);
|
||||
|
||||
} // namespace AmdGpu
|
||||
|
||||
|
@ -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 {
|
||||
PM4Type3Header header;
|
||||
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
|
||||
|
@ -6,7 +6,6 @@
|
||||
#include "common/alignment.h"
|
||||
#include "common/assert.h"
|
||||
#include "common/bit_field.h"
|
||||
#include "common/types.h"
|
||||
#include "video_core/amdgpu/pixel_format.h"
|
||||
|
||||
namespace AmdGpu {
|
||||
|
@ -5,7 +5,6 @@
|
||||
|
||||
#include <string_view>
|
||||
#include <fmt/format.h>
|
||||
#include "common/assert.h"
|
||||
#include "common/types.h"
|
||||
|
||||
namespace AmdGpu {
|
||||
@ -114,281 +113,6 @@ enum class GsOutputPrimitiveType : u32 {
|
||||
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
|
||||
|
||||
template <>
|
||||
|
@ -6,6 +6,7 @@
|
||||
#include "common/debug.h"
|
||||
#include "common/scope_exit.h"
|
||||
#include "common/types.h"
|
||||
#include "core/memory.h"
|
||||
#include "video_core/amdgpu/liverpool.h"
|
||||
#include "video_core/buffer_cache/buffer_cache.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_,
|
||||
TextureCache& texture_cache_, PageManager& tracker_)
|
||||
: 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},
|
||||
stream_buffer{instance, scheduler, MemoryUsage::Stream, UboStreamBufferSize},
|
||||
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) {
|
||||
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);
|
||||
return;
|
||||
}
|
||||
@ -365,7 +366,9 @@ std::pair<Buffer*, u32> BufferCache::ObtainViewBuffer(VAddr gpu_addr, u32 size,
|
||||
return ObtainBuffer(gpu_addr, size, false, false);
|
||||
}
|
||||
// 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};
|
||||
}
|
||||
|
||||
@ -798,24 +801,45 @@ void BufferCache::SynchronizeBuffer(Buffer& buffer, VAddr device_addr, u32 size,
|
||||
}
|
||||
|
||||
bool BufferCache::SynchronizeBufferFromImage(Buffer& buffer, VAddr device_addr, u32 size) {
|
||||
static constexpr FindFlags find_flags =
|
||||
FindFlags::NoCreate | FindFlags::RelaxDim | FindFlags::RelaxFmt | FindFlags::RelaxSize;
|
||||
TextureCache::BaseDesc desc{};
|
||||
desc.info.guest_address = device_addr;
|
||||
desc.info.guest_size = size;
|
||||
const ImageId image_id = texture_cache.FindImage(desc, find_flags);
|
||||
if (!image_id) {
|
||||
boost::container::small_vector<ImageId, 6> image_ids;
|
||||
texture_cache.ForEachImageInRegion(device_addr, size, [&](ImageId image_id, Image& image) {
|
||||
if (image.info.guest_address != device_addr) {
|
||||
return;
|
||||
}
|
||||
// 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;
|
||||
}
|
||||
image_ids.push_back(image_id);
|
||||
});
|
||||
if (image_ids.empty()) {
|
||||
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);
|
||||
// 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,
|
||||
"Texel buffer aliases image subresources {:x} : {:x}", device_addr,
|
||||
image.info.guest_address);
|
||||
|
@ -17,6 +17,10 @@ namespace AmdGpu {
|
||||
struct Liverpool;
|
||||
}
|
||||
|
||||
namespace Core {
|
||||
class MemoryManager;
|
||||
}
|
||||
|
||||
namespace Shader {
|
||||
namespace Gcn {
|
||||
struct FetchShaderData;
|
||||
@ -183,6 +187,7 @@ private:
|
||||
Vulkan::Scheduler& scheduler;
|
||||
Vulkan::Rasterizer& rasterizer;
|
||||
AmdGpu::Liverpool* liverpool;
|
||||
Core::MemoryManager* memory;
|
||||
TextureCache& texture_cache;
|
||||
PageManager& tracker;
|
||||
StreamBuffer staging_buffer;
|
||||
|
@ -16,7 +16,7 @@ layout(push_constant) uniform image_info {
|
||||
uint num_levels;
|
||||
uint pitch;
|
||||
uint height;
|
||||
uint sizes[14];
|
||||
uint sizes[16];
|
||||
} info;
|
||||
|
||||
// Inverse morton LUT, small enough to fit into K$
|
||||
|
@ -18,7 +18,7 @@ layout(push_constant) uniform image_info {
|
||||
uint num_levels;
|
||||
uint pitch;
|
||||
uint height;
|
||||
uint sizes[14];
|
||||
uint sizes[16];
|
||||
} info;
|
||||
|
||||
#define MICRO_TILE_DIM 8
|
||||
|
@ -16,7 +16,7 @@ layout(push_constant) uniform image_info {
|
||||
uint num_levels;
|
||||
uint pitch;
|
||||
uint height;
|
||||
uint sizes[14];
|
||||
uint sizes[16];
|
||||
} info;
|
||||
|
||||
// Inverse morton LUT, small enough to fit into K$
|
||||
|
@ -16,7 +16,7 @@ layout(push_constant) uniform image_info {
|
||||
uint num_levels;
|
||||
uint pitch;
|
||||
uint height;
|
||||
uint sizes[14];
|
||||
uint sizes[16];
|
||||
} info;
|
||||
|
||||
// Inverse morton LUT, small enough to fit into K$
|
||||
|
@ -19,7 +19,7 @@ layout(push_constant) uniform image_info {
|
||||
uint num_levels;
|
||||
uint pitch;
|
||||
uint height;
|
||||
uint sizes[14];
|
||||
uint sizes[16];
|
||||
} info;
|
||||
|
||||
#define MICRO_TILE_DIM 8
|
||||
|
@ -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) {
|
||||
using BlendFunc = Liverpool::BlendControl::BlendFunc;
|
||||
switch (func) {
|
||||
|
@ -30,6 +30,8 @@ vk::FrontFace FrontFace(Liverpool::FrontFace mode);
|
||||
|
||||
vk::BlendFactor BlendFactor(Liverpool::BlendControl::BlendFactor factor);
|
||||
|
||||
bool IsDualSourceBlendFactor(Liverpool::BlendControl::BlendFactor factor);
|
||||
|
||||
vk::BlendOp BlendOp(Liverpool::BlendControl::BlendFunc func);
|
||||
|
||||
vk::SamplerAddressMode ClampMode(AmdGpu::ClampMode mode);
|
||||
|
@ -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.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);
|
||||
gs_info.vs_copy = params_vc.code;
|
||||
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;
|
||||
const auto& ps_inputs = regs.ps_inputs;
|
||||
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++) {
|
||||
info.fs_info.inputs[i] = {
|
||||
.param_index = u8(ps_inputs[i].input_offset.Value()),
|
||||
|
@ -716,7 +716,7 @@ void Rasterizer::BindTextures(const Shader::Info& stage, Shader::Backend::Bindin
|
||||
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);
|
||||
set_writes.push_back({
|
||||
.dstSet = VK_NULL_HANDLE,
|
||||
|
220
src/video_core/texture_cache/host_compatibility.cpp
Normal file
220
src/video_core/texture_cache/host_compatibility.cpp
Normal 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
|
@ -6,387 +6,11 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <unordered_map>
|
||||
#include "video_core/renderer_vulkan/vk_common.h"
|
||||
|
||||
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
|
||||
};
|
||||
|
||||
/**
|
||||
* @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/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},
|
||||
};
|
||||
/// Returns true if the two formats are compatible according to Vulkan's format compatibility rules
|
||||
bool IsVulkanFormatCompatible(vk::Format base, vk::Format view);
|
||||
|
||||
/**
|
||||
* @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
|
||||
|
@ -14,62 +14,6 @@ namespace VideoCore {
|
||||
|
||||
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) {
|
||||
vk::ImageUsageFlags usage = vk::ImageUsageFlagBits::eTransferSrc |
|
||||
vk::ImageUsageFlagBits::eTransferDst |
|
||||
@ -161,6 +105,10 @@ Image::Image(const Vulkan::Instance& instance_, Vulkan::Scheduler& scheduler_,
|
||||
if (info.props.is_volume) {
|
||||
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);
|
||||
format_features = FormatFeatureFlags(usage_flags);
|
||||
@ -372,9 +320,9 @@ void Image::CopyImage(const Image& image) {
|
||||
|
||||
boost::container::small_vector<vk::ImageCopy, 14> image_copy{};
|
||||
for (u32 m = 0; m < image.info.resources.levels; ++m) {
|
||||
const auto mip_w = std::max(info.size.width >> m, 1u);
|
||||
const auto mip_h = std::max(info.size.height >> m, 1u);
|
||||
const auto mip_d = std::max(info.size.depth >> m, 1u);
|
||||
const auto mip_w = std::max(image.info.size.width >> m, 1u);
|
||||
const auto mip_h = std::max(image.info.size.height >> m, 1u);
|
||||
const auto mip_d = std::max(image.info.size.depth >> m, 1u);
|
||||
|
||||
image_copy.emplace_back(vk::ImageCopy{
|
||||
.srcSubresource{
|
||||
|
@ -81,7 +81,7 @@ ImageInfo::ImageInfo(const AmdGpu::Liverpool::ColorBuffer& buffer,
|
||||
tiling_mode = buffer.GetTilingMode();
|
||||
pixel_format = LiverpoolToVK::SurfaceFormat(buffer.GetDataFmt(), buffer.GetNumberFmt());
|
||||
num_samples = buffer.NumSamples();
|
||||
num_bits = NumBits(buffer.GetDataFmt());
|
||||
num_bits = NumBitsPerBlock(buffer.GetDataFmt());
|
||||
type = vk::ImageType::e2D;
|
||||
size.width = hint.Valid() ? hint.width : buffer.Pitch();
|
||||
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.layers = image.NumLayers();
|
||||
num_samples = image.NumSamples();
|
||||
num_bits = NumBits(image.GetDataFmt());
|
||||
num_bits = NumBitsPerBlock(image.GetDataFmt());
|
||||
|
||||
guest_address = image.Address();
|
||||
|
||||
@ -152,6 +152,80 @@ ImageInfo::ImageInfo(const AmdGpu::Image& image, const Shader::ImageResource& de
|
||||
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() {
|
||||
mips_layout.clear();
|
||||
MipInfo mip_info{};
|
||||
@ -163,7 +237,6 @@ void ImageInfo::UpdateSize() {
|
||||
if (props.is_block) {
|
||||
mip_w = (mip_w + 3) / 4;
|
||||
mip_h = (mip_h + 3) / 4;
|
||||
bpp *= 16;
|
||||
}
|
||||
mip_w = std::max(mip_w, 1u);
|
||||
mip_h = std::max(mip_h, 1u);
|
||||
|
@ -25,6 +25,11 @@ struct ImageInfo {
|
||||
bool IsTiled() const {
|
||||
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 IsPacked() const;
|
||||
bool IsDepthStencil() const;
|
||||
@ -33,24 +38,8 @@ struct ImageInfo {
|
||||
s32 MipOf(const ImageInfo& info) const;
|
||||
s32 SliceOf(const ImageInfo& info, s32 mip) const;
|
||||
|
||||
/// Verifies if images are compatible for subresource merging.
|
||||
bool IsCompatible(const ImageInfo& info) 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;
|
||||
}
|
||||
bool IsCompatible(const ImageInfo& info) const;
|
||||
bool IsTilingCompatible(u32 lhs, u32 rhs) const;
|
||||
|
||||
void UpdateSize();
|
||||
|
||||
|
@ -9,7 +9,8 @@
|
||||
|
||||
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) {
|
||||
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 =
|
||||
anisotropyEnable ? std::clamp(sampler.MaxAniso(), 1.0f, instance.MaxSamplerAnisotropy())
|
||||
: 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 = {
|
||||
.pNext = customColor ? &*customColor : nullptr,
|
||||
.magFilter = LiverpoolToVK::Filter(sampler.xy_mag_filter),
|
||||
.minFilter = LiverpoolToVK::Filter(sampler.xy_min_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),
|
||||
.minLod = sampler.MinLod(),
|
||||
.maxLod = sampler.MaxLod(),
|
||||
.borderColor = LiverpoolToVK::BorderColor(sampler.border_color_type),
|
||||
.borderColor = borderColor,
|
||||
.unnormalizedCoordinates = false, // Handled in shader due to Vulkan limitations.
|
||||
};
|
||||
auto [sampler_result, smplr] = instance.GetDevice().createSamplerUnique(sampler_ci);
|
||||
|
@ -14,7 +14,8 @@ namespace VideoCore {
|
||||
|
||||
class Sampler {
|
||||
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(const Sampler&) = delete;
|
||||
|
@ -199,7 +199,8 @@ std::tuple<ImageId, int, int> TextureCache::ResolveOverlap(const ImageInfo& imag
|
||||
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.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.
|
||||
if (safe_to_delete) {
|
||||
FreeImage(cache_image_id);
|
||||
@ -211,15 +212,19 @@ std::tuple<ImageId, int, int> TextureCache::ResolveOverlap(const ImageInfo& imag
|
||||
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 ||
|
||||
image_info.guest_size <= tex_cache_image.info.guest_size) {
|
||||
auto result_id = merged_image_id ? merged_image_id : cache_image_id;
|
||||
const auto& result_image = slot_images[result_id];
|
||||
return {
|
||||
IsVulkanFormatCompatible(image_info.pixel_format, result_image.info.pixel_format)
|
||||
? result_id
|
||||
: ImageId{},
|
||||
-1, -1};
|
||||
const bool is_compatible =
|
||||
IsVulkanFormatCompatible(result_image.info.pixel_format, image_info.pixel_format);
|
||||
return {is_compatible ? result_id : ImageId{}, -1, -1};
|
||||
}
|
||||
|
||||
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];
|
||||
|
||||
src_image.Transit(vk::ImageLayout::eTransferSrcOptimal, vk::AccessFlagBits2::eTransferRead, {});
|
||||
RefreshImage(new_image);
|
||||
new_image.CopyImage(src_image);
|
||||
|
||||
if (src_image.binding.is_bound || src_image.binding.is_target) {
|
||||
@ -339,7 +345,7 @@ ImageId TextureCache::FindImage(BaseDesc& desc, FindFlags flags) {
|
||||
continue;
|
||||
}
|
||||
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}))) {
|
||||
continue;
|
||||
}
|
||||
@ -511,9 +517,9 @@ void TextureCache::RefreshImage(Image& image, Vulkan::Scheduler* custom_schedule
|
||||
// So this calculation should be very uncommon and reasonably fast
|
||||
// For now we'll just check up to 64 first pixels
|
||||
const auto addr = std::bit_cast<u8*>(image.info.guest_address);
|
||||
const auto w = std::min(image.info.size.width, u32(8));
|
||||
const auto h = std::min(image.info.size.height, u32(8));
|
||||
const auto size = w * h * image.info.num_bits / 8;
|
||||
const u32 w = std::min(image.info.size.width, u32(8));
|
||||
const u32 h = std::min(image.info.size.height, u32(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);
|
||||
if (image.hash == hash) {
|
||||
image.flags &= ~ImageFlagBits::MaybeCpuDirty;
|
||||
@ -636,9 +642,11 @@ void TextureCache::RefreshImage(Image& image, Vulkan::Scheduler* custom_schedule
|
||||
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 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();
|
||||
}
|
||||
|
||||
|
@ -139,7 +139,9 @@ public:
|
||||
void RefreshImage(Image& image, Vulkan::Scheduler* custom_scheduler = nullptr);
|
||||
|
||||
/// 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.
|
||||
[[nodiscard]] Image& GetImage(ImageId id) {
|
||||
|
@ -25,10 +25,9 @@
|
||||
namespace VideoCore {
|
||||
|
||||
const DetilerContext* TileManager::GetDetiler(const ImageInfo& info) const {
|
||||
const auto bpp = info.num_bits * (info.props.is_block ? 16 : 1);
|
||||
switch (info.tiling_mode) {
|
||||
case AmdGpu::TilingMode::Texture_MicroTiled:
|
||||
switch (bpp) {
|
||||
switch (info.num_bits) {
|
||||
case 8:
|
||||
return &detilers[DetilerType::Micro8];
|
||||
case 16:
|
||||
@ -43,7 +42,7 @@ const DetilerContext* TileManager::GetDetiler(const ImageInfo& info) const {
|
||||
return nullptr;
|
||||
}
|
||||
case AmdGpu::TilingMode::Texture_Volume:
|
||||
switch (bpp) {
|
||||
switch (info.num_bits) {
|
||||
case 8:
|
||||
return &detilers[DetilerType::Macro8];
|
||||
case 32:
|
||||
@ -55,7 +54,7 @@ const DetilerContext* TileManager::GetDetiler(const ImageInfo& info) const {
|
||||
}
|
||||
break;
|
||||
case AmdGpu::TilingMode::Display_MicroTiled:
|
||||
switch (bpp) {
|
||||
switch (info.num_bits) {
|
||||
case 64:
|
||||
return &detilers[DetilerType::Display_Micro64];
|
||||
default:
|
||||
@ -71,7 +70,7 @@ struct DetilerParams {
|
||||
u32 num_levels;
|
||||
u32 pitch0;
|
||||
u32 height;
|
||||
u32 sizes[14];
|
||||
std::array<u32, 16> sizes;
|
||||
};
|
||||
|
||||
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;
|
||||
if (info.tiling_mode == AmdGpu::TilingMode::Texture_Volume ||
|
||||
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_slice = tiles_per_row * ((info.size.height + 7u) / 8u);
|
||||
params.sizes[0] = tiles_per_row;
|
||||
params.sizes[1] = tiles_per_slice;
|
||||
} else {
|
||||
ASSERT(info.resources.levels <= 14);
|
||||
ASSERT(info.resources.levels <= params.sizes.size());
|
||||
std::memset(¶ms.sizes, 0, sizeof(params.sizes));
|
||||
for (int m = 0; m < info.resources.levels; ++m) {
|
||||
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
|
||||
¶ms);
|
||||
|
||||
ASSERT((image_size % 64) == 0);
|
||||
const auto bpp = info.num_bits * (info.props.is_block ? 16u : 1u);
|
||||
const auto num_tiles = image_size / (64 * (bpp / 8));
|
||||
const auto num_tiles = image_size / (64 * (info.num_bits / 8));
|
||||
cmdbuf.dispatch(num_tiles, 1, 1);
|
||||
return {out_buffer.first, 0};
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user