Merge branch 'shadps4-emu:main' into equeue-fix

This commit is contained in:
kalaposfos13 2025-01-12 20:57:00 +01:00 committed by GitHub
commit cf0e6af007
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
94 changed files with 10314 additions and 441 deletions

View File

@ -376,6 +376,78 @@ jobs:
name: shadps4-linux-qt-${{ needs.get-info.outputs.date }}-${{ needs.get-info.outputs.shorthash }}
path: Shadps4-qt.AppImage
linux-sdl-gcc:
runs-on: ubuntu-24.04
needs: get-info
steps:
- uses: actions/checkout@v4
with:
submodules: recursive
- name: Install dependencies
run: sudo apt-get update && sudo apt install -y libx11-dev libxext-dev libwayland-dev libdecor-0-dev libxkbcommon-dev libglfw3-dev libgles2-mesa-dev libfuse2 gcc-14 build-essential libasound2-dev libpulse-dev libopenal-dev libudev-dev
- name: Cache CMake Configuration
uses: actions/cache@v4
env:
cache-name: ${{ runner.os }}-sdl-cache-cmake-configuration
with:
path: |
${{github.workspace}}/build
key: ${{ env.cache-name }}-${{ hashFiles('**/CMakeLists.txt', 'cmake/**') }}
restore-keys: |
${{ env.cache-name }}-
- name: Cache CMake Build
uses: hendrikmuhs/ccache-action@v1.2.14
env:
cache-name: ${{ runner.os }}-sdl-cache-cmake-build
with:
append-timestamp: false
key: ${{ env.cache-name }}-${{ hashFiles('**/CMakeLists.txt', 'cmake/**') }}
- name: Configure CMake
run: cmake --fresh -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DCMAKE_INTERPROCEDURAL_OPTIMIZATION_RELEASE=ON -DCMAKE_C_COMPILER=gcc-14 -DCMAKE_CXX_COMPILER=g++-14 -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache
- name: Build
run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}} --parallel $(nproc)
linux-qt-gcc:
runs-on: ubuntu-24.04
needs: get-info
steps:
- uses: actions/checkout@v4
with:
submodules: recursive
- name: Install dependencies
run: sudo apt-get update && sudo apt install -y libx11-dev libxext-dev libwayland-dev libdecor-0-dev libxkbcommon-dev libglfw3-dev libgles2-mesa-dev libfuse2 gcc-14 build-essential qt6-base-dev qt6-tools-dev qt6-multimedia-dev libasound2-dev libpulse-dev libopenal-dev libudev-dev
- name: Cache CMake Configuration
uses: actions/cache@v4
env:
cache-name: ${{ runner.os }}-qt-cache-cmake-configuration
with:
path: |
${{github.workspace}}/build
key: ${{ env.cache-name }}-${{ hashFiles('**/CMakeLists.txt', 'cmake/**') }}
restore-keys: |
${{ env.cache-name }}-
- name: Cache CMake Build
uses: hendrikmuhs/ccache-action@v1.2.14
env:
cache-name: ${{ runner.os }}-qt-cache-cmake-build
with:
append-timestamp: false
key: ${{ env.cache-name }}-${{ hashFiles('**/CMakeLists.txt', 'cmake/**') }}
- name: Configure CMake
run: cmake --fresh -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DCMAKE_INTERPROCEDURAL_OPTIMIZATION_RELEASE=ON -DCMAKE_C_COMPILER=gcc-14 -DCMAKE_CXX_COMPILER=g++-14 -DENABLE_QT_GUI=ON -DENABLE_UPDATER=ON -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache
- name: Build
run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}} --parallel $(nproc)
pre-release:
if: github.ref == 'refs/heads/main' && github.repository == 'shadps4-emu/shadPS4' && github.event_name == 'push'
needs: [get-info, windows-sdl, windows-qt, macos-sdl, macos-qt, linux-sdl, linux-qt]

View File

@ -263,6 +263,8 @@ set(NETWORK_LIBS src/core/libraries/network/http.cpp
src/core/libraries/network/net.h
src/core/libraries/network/ssl.cpp
src/core/libraries/network/ssl.h
src/core/libraries/network/ssl2.cpp
src/core/libraries/network/ssl2.h
)
set(AVPLAYER_LIB src/core/libraries/avplayer/avplayer_common.cpp
@ -415,7 +417,9 @@ set(VDEC_LIB src/core/libraries/videodec/videodec2_impl.cpp
src/core/libraries/videodec/videodec_impl.h
)
set(NP_LIBS src/core/libraries/np_manager/np_manager.cpp
set(NP_LIBS src/core/libraries/np_common/np_common.cpp
src/core/libraries/np_common/np_common.h
src/core/libraries/np_manager/np_manager.cpp
src/core/libraries/np_manager/np_manager.h
src/core/libraries/np_score/np_score.cpp
src/core/libraries/np_score/np_score.h

View File

@ -62,7 +62,6 @@
<url>https://github.com/shadps4-emu/shadPS4/releases/tag/v0.0.1</url>
</release>
</releases>
</releases>
<content_rating type="oars-1.1"/>
<supports>
<control translate="no">keyboard</control>

View File

@ -213,9 +213,7 @@ endif()
# Discord RPC
if (ENABLE_DISCORD_RPC)
set(BUILD_EXAMPLES OFF)
add_subdirectory(discord-rpc)
target_include_directories(discord-rpc INTERFACE discord-rpc/include)
endif()
# GCN Headers

@ -1 +1 @@
Subproject commit 4ec218155d73bcb8022f8f7ca72305d801f84beb
Subproject commit 51b09d426a4a1bcfa6ee6d4894e57d669f4a2e65

2
externals/sirit vendored

@ -1 +1 @@
Subproject commit 1e74f4ef8d2a0e3221a4de51977663f342b53c35
Subproject commit 26ad5a9d0fe13260b0d7d6c64419d01a196b2e32

View File

@ -111,7 +111,7 @@ public:
return raw_firmware_ver;
}
[[nodiscard]] const PSFAttributes& PSFAttributes() const {
[[nodiscard]] const PSFAttributes& GetPSFAttributes() const {
ASSERT(initialized);
return psf_attributes;
}

View File

@ -96,8 +96,10 @@ bool ParseFilterRule(Filter& instance, Iterator begin, Iterator end) {
SUB(Lib, SaveDataDialog) \
SUB(Lib, Http) \
SUB(Lib, Ssl) \
SUB(Lib, Ssl2) \
SUB(Lib, SysModule) \
SUB(Lib, Move) \
SUB(Lib, NpCommon) \
SUB(Lib, NpManager) \
SUB(Lib, NpScore) \
SUB(Lib, NpTrophy) \

View File

@ -63,8 +63,10 @@ enum class Class : u8 {
Lib_SaveData, ///< The LibSceSaveData implementation.
Lib_SaveDataDialog, ///< The LibSceSaveDataDialog implementation.
Lib_Ssl, ///< The LibSceSsl implementation.
Lib_Ssl2, ///< The LibSceSsl2 implementation.
Lib_Http, ///< The LibSceHttp implementation.
Lib_SysModule, ///< The LibSceSysModule implementation
Lib_NpCommon, ///< The LibSceNpCommon implementation
Lib_NpManager, ///< The LibSceNpManager implementation
Lib_NpScore, ///< The LibSceNpScore implementation
Lib_NpTrophy, ///< The LibSceNpTrophy implementation

View File

@ -40,7 +40,8 @@ void MntPoints::UnmountAll() {
m_mnt_pairs.clear();
}
std::filesystem::path MntPoints::GetHostPath(std::string_view path, bool* is_read_only) {
std::filesystem::path MntPoints::GetHostPath(std::string_view path, bool* is_read_only,
bool force_base_path) {
// Evil games like Turok2 pass double slashes e.g /app0//game.kpf
std::string corrected_path(path);
size_t pos = corrected_path.find("//");
@ -72,7 +73,7 @@ std::filesystem::path MntPoints::GetHostPath(std::string_view path, bool* is_rea
patch_path /= rel_path;
if ((corrected_path.starts_with("/app0") || corrected_path.starts_with("/hostapp")) &&
std::filesystem::exists(patch_path)) {
!force_base_path && std::filesystem::exists(patch_path)) {
return patch_path;
}
@ -132,9 +133,11 @@ std::filesystem::path MntPoints::GetHostPath(std::string_view path, bool* is_rea
return std::optional<std::filesystem::path>(current_path);
};
if (!force_base_path) {
if (const auto path = search(patch_path)) {
return *path;
}
}
if (const auto path = search(host_path)) {
return *path;
}
@ -144,6 +147,39 @@ std::filesystem::path MntPoints::GetHostPath(std::string_view path, bool* is_rea
return host_path;
}
// TODO: Does not handle mount points inside mount points.
void MntPoints::IterateDirectory(std::string_view guest_directory,
const IterateDirectoryCallback& callback) {
const auto base_path = GetHostPath(guest_directory, nullptr, true);
const auto patch_path = GetHostPath(guest_directory, nullptr, false);
// Only need to consider patch path if it exists and does not resolve to the same as base.
const auto apply_patch = base_path != patch_path && std::filesystem::exists(patch_path);
// Pass 1: Any files that existed in the base directory, using patch directory if needed.
if (std::filesystem::exists(base_path)) {
for (const auto& entry : std::filesystem::directory_iterator(base_path)) {
if (apply_patch) {
const auto patch_entry_path = patch_path / entry.path().filename();
if (std::filesystem::exists(patch_entry_path)) {
callback(patch_entry_path, !std::filesystem::is_directory(patch_entry_path));
continue;
}
}
callback(entry.path(), !entry.is_directory());
}
}
// Pass 2: Any files that exist only in the patch directory.
if (apply_patch) {
for (const auto& entry : std::filesystem::directory_iterator(patch_path)) {
const auto base_entry_path = base_path / entry.path().filename();
if (!std::filesystem::exists(base_entry_path)) {
callback(entry.path(), !entry.is_directory());
}
}
}
}
int HandleTable::CreateHandle() {
std::scoped_lock lock{m_mutex};

View File

@ -36,7 +36,11 @@ public:
void UnmountAll();
std::filesystem::path GetHostPath(std::string_view guest_directory,
bool* is_read_only = nullptr);
bool* is_read_only = nullptr, bool force_base_path = false);
using IterateDirectoryCallback =
std::function<void(const std::filesystem::path& host_path, bool is_file)>;
void IterateDirectory(std::string_view guest_directory,
const IterateDirectoryCallback& callback);
const MntPair* GetMountFromHostPath(const std::string& host_path) {
std::scoped_lock lock{m_mutex};

View File

@ -46,17 +46,6 @@ static std::map<std::string, FactoryDevice> available_device = {
namespace Libraries::Kernel {
auto GetDirectoryEntries(const std::filesystem::path& path) {
std::vector<Core::FileSys::DirEntry> files;
for (const auto& entry : std::filesystem::directory_iterator(path)) {
auto& dir_entry = files.emplace_back();
dir_entry.name = entry.path().filename().string();
dir_entry.isFile = !std::filesystem::is_directory(entry.path().string());
}
return files;
}
int PS4_SYSV_ABI sceKernelOpen(const char* raw_path, int flags, u16 mode) {
LOG_INFO(Kernel_Fs, "path = {} flags = {:#x} mode = {}", raw_path, flags, mode);
auto* h = Common::Singleton<Core::FileSys::HandleTable>::Instance();
@ -115,7 +104,12 @@ int PS4_SYSV_ABI sceKernelOpen(const char* raw_path, int flags, u16 mode) {
if (create) {
return handle; // dir already exists
} else {
file->dirents = GetDirectoryEntries(file->m_host_name);
mnt->IterateDirectory(file->m_guest_name,
[&file](const auto& ent_path, const auto ent_is_file) {
auto& dir_entry = file->dirents.emplace_back();
dir_entry.name = ent_path.filename().string();
dir_entry.isFile = ent_is_file;
});
file->dirents_index = 0;
}
}
@ -695,66 +689,12 @@ static int GetDents(int fd, char* buf, int nbytes, s64* basep) {
return sizeof(OrbisKernelDirent);
}
static int HandleSeparateUpdateDents(int fd, char* buf, int nbytes, s64* basep) {
int dir_entries = 0;
auto* h = Common::Singleton<Core::FileSys::HandleTable>::Instance();
auto* mnt = Common::Singleton<Core::FileSys::MntPoints>::Instance();
auto* file = h->GetFile(fd);
auto update_dir_name = std::string{fmt::UTF(file->m_host_name.u8string()).data};
auto mount = mnt->GetMountFromHostPath(update_dir_name);
auto suffix = std::string{fmt::UTF(mount->host_path.u8string()).data};
size_t pos = update_dir_name.find("-UPDATE");
if (pos != std::string::npos) {
update_dir_name.erase(pos, 7);
auto guest_name = mount->mount + "/" + update_dir_name.substr(suffix.size() + 1);
int descriptor;
auto existent_folder = h->GetFile(update_dir_name);
if (!existent_folder) {
u32 handle = h->CreateHandle();
auto* new_file = h->GetFile(handle);
new_file->type = Core::FileSys::FileType::Directory;
new_file->m_guest_name = guest_name;
new_file->m_host_name = update_dir_name;
if (!std::filesystem::is_directory(new_file->m_host_name)) {
h->DeleteHandle(handle);
return dir_entries;
} else {
new_file->dirents = GetDirectoryEntries(new_file->m_host_name);
new_file->dirents_index = 0;
}
new_file->is_opened = true;
descriptor = h->GetFileDescriptor(new_file);
} else {
descriptor = h->GetFileDescriptor(existent_folder);
}
dir_entries = GetDents(descriptor, buf, nbytes, basep);
if (dir_entries == ORBIS_OK && existent_folder) {
existent_folder->dirents_index = 0;
file->dirents_index = 0;
}
}
return dir_entries;
}
int PS4_SYSV_ABI sceKernelGetdents(int fd, char* buf, int nbytes) {
int a = GetDents(fd, buf, nbytes, nullptr);
if (a == ORBIS_OK) {
return HandleSeparateUpdateDents(fd, buf, nbytes, nullptr);
}
return a;
return GetDents(fd, buf, nbytes, nullptr);
}
int PS4_SYSV_ABI sceKernelGetdirentries(int fd, char* buf, int nbytes, s64* basep) {
int a = GetDents(fd, buf, nbytes, basep);
if (a == ORBIS_OK) {
return HandleSeparateUpdateDents(fd, buf, nbytes, basep);
}
return a;
return GetDents(fd, buf, nbytes, basep);
}
s64 PS4_SYSV_ABI sceKernelPwrite(int d, void* buf, size_t nbytes, s64 offset) {

View File

@ -15,7 +15,7 @@ namespace Libraries::Kernel {
int PS4_SYSV_ABI sceKernelIsNeoMode() {
LOG_DEBUG(Kernel_Sce, "called");
return Config::isNeoModeConsole() &&
Common::ElfInfo::Instance().PSFAttributes().support_neo_mode;
Common::ElfInfo::Instance().GetPSFAttributes().support_neo_mode;
}
int PS4_SYSV_ABI sceKernelGetCompiledSdkVersion(int* ver) {

View File

@ -386,6 +386,9 @@ int PS4_SYSV_ABI posix_sched_get_priority_min() {
}
int PS4_SYSV_ABI posix_pthread_rename_np(PthreadT thread, const char* name) {
if (thread == nullptr) {
return POSIX_EINVAL;
}
LOG_INFO(Kernel_Pthread, "name = {}", name);
Common::SetThreadName(reinterpret_cast<void*>(thread->native_thr.GetHandle()), name);
thread->name = name;

View File

@ -24,6 +24,8 @@
#include "core/libraries/network/net.h"
#include "core/libraries/network/netctl.h"
#include "core/libraries/network/ssl.h"
#include "core/libraries/network/ssl2.h"
#include "core/libraries/np_common/np_common.h"
#include "core/libraries/np_manager/np_manager.h"
#include "core/libraries/np_score/np_score.h"
#include "core/libraries/np_trophy/np_trophy.h"
@ -69,9 +71,11 @@ void InitHLELibs(Core::Loader::SymbolsResolver* sym) {
Libraries::SaveData::RegisterlibSceSaveData(sym);
Libraries::SaveData::Dialog::RegisterlibSceSaveDataDialog(sym);
Libraries::Ssl::RegisterlibSceSsl(sym);
Libraries::Ssl2::RegisterlibSceSsl2(sym);
Libraries::SysModule::RegisterlibSceSysmodule(sym);
Libraries::Posix::Registerlibsceposix(sym);
Libraries::AudioIn::RegisterlibSceAudioIn(sym);
Libraries::NpCommon::RegisterlibSceNpCommon(sym);
Libraries::NpManager::RegisterlibSceNpManager(sym);
Libraries::NpScore::RegisterlibSceNpScore(sym);
Libraries::NpTrophy::RegisterlibSceNpTrophy(sym);

View File

@ -0,0 +1,353 @@
// 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/network/ssl2.h"
namespace Libraries::Ssl2 {
int PS4_SYSV_ABI CA_MGMT_extractKeyBlobEx() {
LOG_ERROR(Lib_Ssl2, "(STUBBED) called");
return ORBIS_OK;
}
int PS4_SYSV_ABI CA_MGMT_extractPublicKeyInfo() {
LOG_ERROR(Lib_Ssl2, "(STUBBED) called");
return ORBIS_OK;
}
int PS4_SYSV_ABI CA_MGMT_freeKeyBlob() {
LOG_ERROR(Lib_Ssl2, "(STUBBED) called");
return ORBIS_OK;
}
int PS4_SYSV_ABI CRYPTO_initAsymmetricKey() {
LOG_ERROR(Lib_Ssl2, "(STUBBED) called");
return ORBIS_OK;
}
int PS4_SYSV_ABI CRYPTO_uninitAsymmetricKey() {
LOG_ERROR(Lib_Ssl2, "(STUBBED) called");
return ORBIS_OK;
}
int PS4_SYSV_ABI RSA_verifySignature() {
LOG_ERROR(Lib_Ssl2, "(STUBBED) called");
return ORBIS_OK;
}
int PS4_SYSV_ABI sceSslCheckRecvPending() {
LOG_ERROR(Lib_Ssl2, "(STUBBED) called");
return ORBIS_OK;
}
int PS4_SYSV_ABI sceSslClose() {
LOG_ERROR(Lib_Ssl2, "(STUBBED) called");
return ORBIS_OK;
}
int PS4_SYSV_ABI sceSslConnect() {
LOG_ERROR(Lib_Ssl2, "(STUBBED) called");
return ORBIS_OK;
}
int PS4_SYSV_ABI sceSslCreateConnection() {
LOG_ERROR(Lib_Ssl2, "(STUBBED) called");
return ORBIS_OK;
}
int PS4_SYSV_ABI sceSslCreateSslConnection() {
LOG_ERROR(Lib_Ssl2, "(STUBBED) called");
return ORBIS_OK;
}
int PS4_SYSV_ABI sceSslDeleteConnection() {
LOG_ERROR(Lib_Ssl2, "(STUBBED) called");
return ORBIS_OK;
}
int PS4_SYSV_ABI sceSslDeleteSslConnection() {
LOG_ERROR(Lib_Ssl2, "(STUBBED) called");
return ORBIS_OK;
}
int PS4_SYSV_ABI sceSslDisableOption() {
LOG_ERROR(Lib_Ssl2, "(STUBBED) called");
return ORBIS_OK;
}
int PS4_SYSV_ABI sceSslDisableOptionInternal() {
LOG_ERROR(Lib_Ssl2, "(STUBBED) called");
return ORBIS_OK;
}
int PS4_SYSV_ABI sceSslDisableOptionInternalInsecure() {
LOG_ERROR(Lib_Ssl2, "(STUBBED) called");
return ORBIS_OK;
}
int PS4_SYSV_ABI sceSslDisableVerifyOption() {
LOG_ERROR(Lib_Ssl2, "(STUBBED) called");
return ORBIS_OK;
}
int PS4_SYSV_ABI sceSslEnableOption() {
LOG_ERROR(Lib_Ssl2, "(STUBBED) called");
return ORBIS_OK;
}
int PS4_SYSV_ABI sceSslEnableOptionInternal() {
LOG_ERROR(Lib_Ssl2, "(STUBBED) called");
return ORBIS_OK;
}
int PS4_SYSV_ABI sceSslEnableVerifyOption() {
LOG_ERROR(Lib_Ssl2, "(STUBBED) called");
return ORBIS_OK;
}
int PS4_SYSV_ABI sceSslFreeCaCerts() {
LOG_ERROR(Lib_Ssl2, "(STUBBED) called");
return ORBIS_OK;
}
int PS4_SYSV_ABI sceSslFreeCaList() {
LOG_ERROR(Lib_Ssl2, "(STUBBED) called");
return ORBIS_OK;
}
int PS4_SYSV_ABI sceSslFreeSslCertName() {
LOG_ERROR(Lib_Ssl2, "(STUBBED) called");
return ORBIS_OK;
}
int PS4_SYSV_ABI sceSslGetAlpnSelected() {
LOG_ERROR(Lib_Ssl2, "(STUBBED) called");
return ORBIS_OK;
}
int PS4_SYSV_ABI sceSslGetCaCerts() {
LOG_ERROR(Lib_Ssl2, "(STUBBED) called");
return ORBIS_OK;
}
int PS4_SYSV_ABI sceSslGetCaList() {
LOG_ERROR(Lib_Ssl2, "(STUBBED) called");
return ORBIS_OK;
}
int PS4_SYSV_ABI sceSslGetFingerprint() {
LOG_ERROR(Lib_Ssl2, "(STUBBED) called");
return ORBIS_OK;
}
int PS4_SYSV_ABI sceSslGetIssuerName() {
LOG_ERROR(Lib_Ssl2, "(STUBBED) called");
return ORBIS_OK;
}
int PS4_SYSV_ABI sceSslGetMemoryPoolStats() {
LOG_ERROR(Lib_Ssl2, "(STUBBED) called");
return ORBIS_OK;
}
int PS4_SYSV_ABI sceSslGetNameEntryCount() {
LOG_ERROR(Lib_Ssl2, "(STUBBED) called");
return ORBIS_OK;
}
int PS4_SYSV_ABI sceSslGetNameEntryInfo() {
LOG_ERROR(Lib_Ssl2, "(STUBBED) called");
return ORBIS_OK;
}
int PS4_SYSV_ABI sceSslGetNanoSSLModuleId() {
LOG_ERROR(Lib_Ssl2, "(STUBBED) called");
return ORBIS_OK;
}
int PS4_SYSV_ABI sceSslGetNotAfter() {
LOG_ERROR(Lib_Ssl2, "(STUBBED) called");
return ORBIS_OK;
}
int PS4_SYSV_ABI sceSslGetNotBefore() {
LOG_ERROR(Lib_Ssl2, "(STUBBED) called");
return ORBIS_OK;
}
int PS4_SYSV_ABI sceSslGetPeerCert() {
LOG_ERROR(Lib_Ssl2, "(STUBBED) called");
return ORBIS_OK;
}
int PS4_SYSV_ABI sceSslGetPem() {
LOG_ERROR(Lib_Ssl2, "(STUBBED) called");
return ORBIS_OK;
}
int PS4_SYSV_ABI sceSslGetSerialNumber() {
LOG_ERROR(Lib_Ssl2, "(STUBBED) called");
return ORBIS_OK;
}
int PS4_SYSV_ABI sceSslGetSslError() {
LOG_ERROR(Lib_Ssl2, "(STUBBED) called");
return ORBIS_OK;
}
int PS4_SYSV_ABI sceSslGetSubjectName() {
LOG_ERROR(Lib_Ssl2, "(STUBBED) called");
return ORBIS_OK;
}
int PS4_SYSV_ABI sceSslInit(std::size_t poolSize) {
LOG_ERROR(Lib_Ssl2, "(DUMMY) called poolSize = {}", poolSize);
// return a value >1
static int id = 0;
return ++id;
}
int PS4_SYSV_ABI sceSslLoadCert() {
LOG_ERROR(Lib_Ssl2, "(STUBBED) called");
return ORBIS_OK;
}
int PS4_SYSV_ABI sceSslLoadRootCACert() {
LOG_ERROR(Lib_Ssl2, "(STUBBED) called");
return ORBIS_OK;
}
int PS4_SYSV_ABI sceSslRead() {
LOG_ERROR(Lib_Ssl2, "(STUBBED) called");
return ORBIS_OK;
}
int PS4_SYSV_ABI sceSslRecv() {
LOG_ERROR(Lib_Ssl2, "(STUBBED) called");
return ORBIS_OK;
}
int PS4_SYSV_ABI sceSslReuseConnection() {
LOG_ERROR(Lib_Ssl2, "(STUBBED) called");
return ORBIS_OK;
}
int PS4_SYSV_ABI sceSslSend() {
LOG_ERROR(Lib_Ssl2, "(STUBBED) called");
return ORBIS_OK;
}
int PS4_SYSV_ABI sceSslSetAlpn() {
LOG_ERROR(Lib_Ssl2, "(STUBBED) called");
return ORBIS_OK;
}
int PS4_SYSV_ABI sceSslSetMinSslVersion() {
LOG_ERROR(Lib_Ssl2, "(STUBBED) called");
return ORBIS_OK;
}
int PS4_SYSV_ABI sceSslSetSslVersion() {
LOG_ERROR(Lib_Ssl2, "(STUBBED) called");
return ORBIS_OK;
}
int PS4_SYSV_ABI sceSslSetVerifyCallback() {
LOG_ERROR(Lib_Ssl2, "(STUBBED) called");
return ORBIS_OK;
}
int PS4_SYSV_ABI sceSslTerm() {
LOG_ERROR(Lib_Ssl2, "(STUBBED) called");
return ORBIS_OK;
}
int PS4_SYSV_ABI sceSslUnloadCert() {
LOG_ERROR(Lib_Ssl2, "(STUBBED) called");
return ORBIS_OK;
}
int PS4_SYSV_ABI sceSslWrite() {
LOG_ERROR(Lib_Ssl2, "(STUBBED) called");
return ORBIS_OK;
}
int PS4_SYSV_ABI VLONG_freeVlongQueue() {
LOG_ERROR(Lib_Ssl2, "(STUBBED) called");
return ORBIS_OK;
}
int PS4_SYSV_ABI Func_22E76E60BC0587D7() {
LOG_ERROR(Lib_Ssl2, "(STUBBED) called");
return ORBIS_OK;
}
int PS4_SYSV_ABI Func_28F8791A771D39C7() {
LOG_ERROR(Lib_Ssl2, "(STUBBED) called");
return ORBIS_OK;
}
void RegisterlibSceSsl2(Core::Loader::SymbolsResolver* sym) {
LIB_FUNCTION("Md+HYkCBZB4", "libSceSsl", 1, "libSceSsl", 2, 1, CA_MGMT_extractKeyBlobEx);
LIB_FUNCTION("9bKYzKP6kYU", "libSceSsl", 1, "libSceSsl", 2, 1, CA_MGMT_extractPublicKeyInfo);
LIB_FUNCTION("ipLIammTj2Q", "libSceSsl", 1, "libSceSsl", 2, 1, CA_MGMT_freeKeyBlob);
LIB_FUNCTION("PRWr3-ytpdg", "libSceSsl", 1, "libSceSsl", 2, 1, CRYPTO_initAsymmetricKey);
LIB_FUNCTION("cW7VCIMCh9A", "libSceSsl", 1, "libSceSsl", 2, 1, CRYPTO_uninitAsymmetricKey);
LIB_FUNCTION("pBwtarKd7eg", "libSceSsl", 1, "libSceSsl", 2, 1, RSA_verifySignature);
LIB_FUNCTION("1VM0h1JrUfA", "libSceSsl", 1, "libSceSsl", 2, 1, sceSslCheckRecvPending);
LIB_FUNCTION("viRXSHZYd0c", "libSceSsl", 1, "libSceSsl", 2, 1, sceSslClose);
LIB_FUNCTION("zXvd6iNyfgc", "libSceSsl", 1, "libSceSsl", 2, 1, sceSslConnect);
LIB_FUNCTION("tuscfitnhEo", "libSceSsl", 1, "libSceSsl", 2, 1, sceSslCreateConnection);
LIB_FUNCTION("P14ATpXc4J8", "libSceSsl", 1, "libSceSsl", 2, 1, sceSslCreateSslConnection);
LIB_FUNCTION("HJ1n138CQ2g", "libSceSsl", 1, "libSceSsl", 2, 1, sceSslDeleteConnection);
LIB_FUNCTION("hwrHV6Pprk4", "libSceSsl", 1, "libSceSsl", 2, 1, sceSslDeleteSslConnection);
LIB_FUNCTION("iLKz4+ukLqk", "libSceSsl", 1, "libSceSsl", 2, 1, sceSslDisableOption);
LIB_FUNCTION("-WqxBRAUVM4", "libSceSsl", 1, "libSceSsl", 2, 1, sceSslDisableOptionInternal);
LIB_FUNCTION("w1+L-27nYas", "libSceSsl", 1, "libSceSsl", 2, 1,
sceSslDisableOptionInternalInsecure);
LIB_FUNCTION("PwsHbErG+e8", "libSceSsl", 1, "libSceSsl", 2, 1, sceSslDisableVerifyOption);
LIB_FUNCTION("m-zPyAsIpco", "libSceSsl", 1, "libSceSsl", 2, 1, sceSslEnableOption);
LIB_FUNCTION("g-zCwUKstEQ", "libSceSsl", 1, "libSceSsl", 2, 1, sceSslEnableOptionInternal);
LIB_FUNCTION("po1X86mgHDU", "libSceSsl", 1, "libSceSsl", 2, 1, sceSslEnableVerifyOption);
LIB_FUNCTION("qIvLs0gYxi0", "libSceSsl", 1, "libSceSsl", 2, 1, sceSslFreeCaCerts);
LIB_FUNCTION("+DzXseDVkeI", "libSceSsl", 1, "libSceSsl", 2, 1, sceSslFreeCaList);
LIB_FUNCTION("RwXD8grHZHM", "libSceSsl", 1, "libSceSsl", 2, 1, sceSslFreeSslCertName);
LIB_FUNCTION("4O7+bRkRUe8", "libSceSsl", 1, "libSceSsl", 2, 1, sceSslGetAlpnSelected);
LIB_FUNCTION("TDfQqO-gMbY", "libSceSsl", 1, "libSceSsl", 2, 1, sceSslGetCaCerts);
LIB_FUNCTION("qOn+wm28wmA", "libSceSsl", 1, "libSceSsl", 2, 1, sceSslGetCaList);
LIB_FUNCTION("brRtwGBu4A8", "libSceSsl", 1, "libSceSsl", 2, 1, sceSslGetFingerprint);
LIB_FUNCTION("7whYpYfHP74", "libSceSsl", 1, "libSceSsl", 2, 1, sceSslGetIssuerName);
LIB_FUNCTION("-PoIzr3PEk0", "libSceSsl", 1, "libSceSsl", 2, 1, sceSslGetMemoryPoolStats);
LIB_FUNCTION("R1ePzopYPYM", "libSceSsl", 1, "libSceSsl", 2, 1, sceSslGetNameEntryCount);
LIB_FUNCTION("7RBSTKGrmDA", "libSceSsl", 1, "libSceSsl", 2, 1, sceSslGetNameEntryInfo);
LIB_FUNCTION("AzUipl-DpIw", "libSceSsl", 1, "libSceSsl", 2, 1, sceSslGetNanoSSLModuleId);
LIB_FUNCTION("xHpt6+2pGYk", "libSceSsl", 1, "libSceSsl", 2, 1, sceSslGetNotAfter);
LIB_FUNCTION("Eo0S65Jy28Q", "libSceSsl", 1, "libSceSsl", 2, 1, sceSslGetNotBefore);
LIB_FUNCTION("-TbZc8pwPNc", "libSceSsl", 1, "libSceSsl", 2, 1, sceSslGetPeerCert);
LIB_FUNCTION("kLB5aGoUJXg", "libSceSsl", 1, "libSceSsl", 2, 1, sceSslGetPem);
LIB_FUNCTION("DOwXL+FQMEY", "libSceSsl", 1, "libSceSsl", 2, 1, sceSslGetSerialNumber);
LIB_FUNCTION("0XcZknp7-Wc", "libSceSsl", 1, "libSceSsl", 2, 1, sceSslGetSslError);
LIB_FUNCTION("dQReuBX9sD8", "libSceSsl", 1, "libSceSsl", 2, 1, sceSslGetSubjectName);
LIB_FUNCTION("hdpVEUDFW3s", "libSceSsl", 1, "libSceSsl", 2, 1, sceSslInit);
LIB_FUNCTION("Ab7+DH+gYyM", "libSceSsl", 1, "libSceSsl", 2, 1, sceSslLoadCert);
LIB_FUNCTION("3-643mGVFJo", "libSceSsl", 1, "libSceSsl", 2, 1, sceSslLoadRootCACert);
LIB_FUNCTION("jltWpVKtetg", "libSceSsl", 1, "libSceSsl", 2, 1, sceSslRead);
LIB_FUNCTION("hi0veU3L2pU", "libSceSsl", 1, "libSceSsl", 2, 1, sceSslRecv);
LIB_FUNCTION("50R2xYaYZwE", "libSceSsl", 1, "libSceSsl", 2, 1, sceSslReuseConnection);
LIB_FUNCTION("p5bM5PPufFY", "libSceSsl", 1, "libSceSsl", 2, 1, sceSslSend);
LIB_FUNCTION("TL86glUrmUw", "libSceSsl", 1, "libSceSsl", 2, 1, sceSslSetAlpn);
LIB_FUNCTION("QWSxBzf6lAg", "libSceSsl", 1, "libSceSsl", 2, 1, sceSslSetMinSslVersion);
LIB_FUNCTION("bKaEtQnoUuQ", "libSceSsl", 1, "libSceSsl", 2, 1, sceSslSetSslVersion);
LIB_FUNCTION("E4a-ahM57QQ", "libSceSsl", 1, "libSceSsl", 2, 1, sceSslSetVerifyCallback);
LIB_FUNCTION("0K1yQ6Lv-Yc", "libSceSsl", 1, "libSceSsl", 2, 1, sceSslTerm);
LIB_FUNCTION("UQ+3Qu7v3cA", "libSceSsl", 1, "libSceSsl", 2, 1, sceSslUnloadCert);
LIB_FUNCTION("iNjkt9Poblw", "libSceSsl", 1, "libSceSsl", 2, 1, sceSslWrite);
LIB_FUNCTION("wcVuyTUr5ys", "libSceSsl", 1, "libSceSsl", 2, 1, VLONG_freeVlongQueue);
LIB_FUNCTION("IuduYLwFh9c", "libSceSsl", 1, "libSceSsl", 2, 1, Func_22E76E60BC0587D7);
LIB_FUNCTION("KPh5GncdOcc", "libSceSsl", 1, "libSceSsl", 2, 1, Func_28F8791A771D39C7);
};
} // namespace Libraries::Ssl2

View File

@ -0,0 +1,14 @@
// 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::Ssl2 {
void RegisterlibSceSsl2(Core::Loader::SymbolsResolver* sym);
} // namespace Libraries::Ssl2

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,9 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "core/libraries/error_codes.h"
constexpr int ORBIS_NP_ERROR_INVALID_ARGUMENT = 0x80550003;
constexpr int ORBIS_NP_UTIL_ERROR_NOT_MATCH = 0x80550609;

View File

@ -1,7 +1,6 @@
// 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"

View File

@ -171,10 +171,11 @@ int MemoryManager::PoolReserve(void** out_addr, VAddr virtual_addr, size_t size,
// Fixed mapping means the virtual address must exactly match the provided one.
if (True(flags & MemoryMapFlags::Fixed)) {
const auto& vma = FindVMA(mapped_addr)->second;
auto& vma = FindVMA(mapped_addr)->second;
// If the VMA is mapped, unmap the region first.
if (vma.IsMapped()) {
UnmapMemoryImpl(mapped_addr, size);
vma = FindVMA(mapped_addr)->second;
}
const size_t remaining_size = vma.base + vma.size - mapped_addr;
ASSERT_MSG(vma.type == VMAType::Free && remaining_size >= size);
@ -208,10 +209,11 @@ int MemoryManager::Reserve(void** out_addr, VAddr virtual_addr, size_t size, Mem
// Fixed mapping means the virtual address must exactly match the provided one.
if (True(flags & MemoryMapFlags::Fixed)) {
const auto& vma = FindVMA(mapped_addr)->second;
auto& vma = FindVMA(mapped_addr)->second;
// If the VMA is mapped, unmap the region first.
if (vma.IsMapped()) {
UnmapMemoryImpl(mapped_addr, size);
vma = FindVMA(mapped_addr)->second;
}
const size_t remaining_size = vma.base + vma.size - mapped_addr;
ASSERT_MSG(vma.type == VMAType::Free && remaining_size >= size);
@ -393,14 +395,18 @@ s32 MemoryManager::UnmapMemoryImpl(VAddr virtual_addr, size_t size) {
ASSERT_MSG(vma_base.Contains(virtual_addr, size),
"Existing mapping does not contain requested unmap range");
const auto type = vma_base.type;
if (type == VMAType::Free) {
return ORBIS_OK;
}
const auto vma_base_addr = vma_base.base;
const auto vma_base_size = vma_base.size;
const auto phys_base = vma_base.phys_base;
const bool is_exec = vma_base.is_exec;
const auto start_in_vma = virtual_addr - vma_base_addr;
const auto type = vma_base.type;
const bool has_backing = type == VMAType::Direct || type == VMAType::File;
if (type == VMAType::Direct) {
if (type == VMAType::Direct || type == VMAType::Pooled) {
rasterizer->UnmapMemory(virtual_addr, size);
}
if (type == VMAType::Flexible) {
@ -418,10 +424,12 @@ s32 MemoryManager::UnmapMemoryImpl(VAddr virtual_addr, size_t size) {
MergeAdjacent(vma_map, new_it);
bool readonly_file = vma.prot == MemoryProt::CpuRead && type == VMAType::File;
if (type != VMAType::Reserved && type != VMAType::PoolReserved) {
// Unmap the memory region.
impl.Unmap(vma_base_addr, vma_base_size, start_in_vma, start_in_vma + size, phys_base, is_exec,
has_backing, readonly_file);
impl.Unmap(vma_base_addr, vma_base_size, start_in_vma, start_in_vma + size, phys_base,
is_exec, has_backing, readonly_file);
TRACK_FREE(virtual_addr, "VMEM");
}
return ORBIS_OK;
}

View File

@ -217,41 +217,15 @@ void Emulator::Run(const std::filesystem::path& file) {
linker->LoadModule(eboot_path);
// check if we have system modules to load
LoadSystemModules(eboot_path, game_info.game_serial);
LoadSystemModules(game_info.game_serial);
// Load all prx from game's sce_module folder
std::vector<std::filesystem::path> modules_to_load;
std::filesystem::path game_module_folder = file.parent_path() / "sce_module";
if (std::filesystem::is_directory(game_module_folder)) {
for (const auto& entry : std::filesystem::directory_iterator(game_module_folder)) {
if (entry.is_regular_file()) {
modules_to_load.push_back(entry.path());
mnt->IterateDirectory("/app0/sce_module", [this](const auto& path, const auto is_file) {
if (is_file) {
LOG_INFO(Loader, "Loading {}", fmt::UTF(path.u8string()));
linker->LoadModule(path);
}
}
}
// Load all prx from separate update's sce_module folder
std::filesystem::path game_patch_folder = game_folder;
game_patch_folder += "-UPDATE";
std::filesystem::path update_module_folder = game_patch_folder / "sce_module";
if (std::filesystem::is_directory(update_module_folder)) {
for (const auto& entry : std::filesystem::directory_iterator(update_module_folder)) {
auto it = std::find_if(modules_to_load.begin(), modules_to_load.end(),
[&entry](const std::filesystem::path& p) {
return p.filename() == entry.path().filename();
});
if (it != modules_to_load.end()) {
*it = entry.path();
} else {
modules_to_load.push_back(entry.path());
}
}
}
for (const auto& module_path : modules_to_load) {
LOG_INFO(Loader, "Loading {}", fmt::UTF(module_path.u8string()));
linker->LoadModule(module_path);
}
#ifdef ENABLE_DISCORD_RPC
// Discord RPC
@ -278,7 +252,7 @@ void Emulator::Run(const std::filesystem::path& file) {
std::exit(0);
}
void Emulator::LoadSystemModules(const std::filesystem::path& file, std::string game_serial) {
void Emulator::LoadSystemModules(const std::string& game_serial) {
constexpr std::array<SysModules, 11> ModulesToLoad{
{{"libSceNgs2.sprx", &Libraries::Ngs2::RegisterlibSceNgs2},
{"libSceUlt.sprx", nullptr},

View File

@ -29,7 +29,7 @@ public:
void UpdatePlayTime(const std::string& serial);
private:
void LoadSystemModules(const std::filesystem::path& file, std::string game_serial);
void LoadSystemModules(const std::string& game_serial);
Core::MemoryManager* memory;
Input::GameController* controller;

View File

@ -283,7 +283,7 @@ public:
#ifdef Q_OS_WIN
if (createShortcutWin(linkPath, ebootPath, icoPath, exePath)) {
#else
if (createShortcutLinux(linkPath, ebootPath, iconPath)) {
if (createShortcutLinux(linkPath, m_games[itemID].name, ebootPath, iconPath)) {
#endif
QMessageBox::information(
nullptr, tr("Shortcut creation"),
@ -301,7 +301,7 @@ public:
#ifdef Q_OS_WIN
if (createShortcutWin(linkPath, ebootPath, iconPath, exePath)) {
#else
if (createShortcutLinux(linkPath, ebootPath, iconPath)) {
if (createShortcutLinux(linkPath, m_games[itemID].name, ebootPath, iconPath)) {
#endif
QMessageBox::information(
nullptr, tr("Shortcut creation"),
@ -510,8 +510,8 @@ private:
return SUCCEEDED(hres);
}
#else
bool createShortcutLinux(const QString& linkPath, const QString& targetPath,
const QString& iconPath) {
bool createShortcutLinux(const QString& linkPath, const std::string& name,
const QString& targetPath, const QString& iconPath) {
QFile shortcutFile(linkPath);
if (!shortcutFile.open(QIODevice::WriteOnly | QIODevice::Text)) {
QMessageBox::critical(nullptr, "Error",
@ -522,7 +522,7 @@ private:
QTextStream out(&shortcutFile);
out << "[Desktop Entry]\n";
out << "Version=1.0\n";
out << "Name=" << QFileInfo(linkPath).baseName() << "\n";
out << "Name=" << QString::fromStdString(name) << "\n";
out << "Exec=" << QCoreApplication::applicationFilePath() << " \"" << targetPath << "\"\n";
out << "Icon=" << iconPath << "\n";
out << "Terminal=false\n";

View File

@ -47,6 +47,9 @@ PKGViewer::PKGViewer(std::shared_ptr<GameInfoClass> game_info_get, QWidget* pare
connect(treeWidget, &QTreeWidget::customContextMenuRequested, this,
[=, this](const QPoint& pos) {
if (treeWidget->selectedItems().isEmpty()) {
return;
}
m_gui_context_menus.RequestGameMenuPKGViewer(pos, m_full_pkg_list, treeWidget,
InstallDragDropPkg);
});

View File

@ -17,6 +17,7 @@
#ifdef ENABLE_UPDATER
#include "check_update.h"
#endif
#include <QDesktopServices>
#include <toml.hpp>
#include "background_music_player.h"
#include "common/logging/backend.h"
@ -203,6 +204,16 @@ SettingsDialog::SettingsDialog(std::span<const QString> physical_devices,
});
}
// DEBUG TAB
{
connect(ui->OpenLogLocationButton, &QPushButton::clicked, this, []() {
QString userPath;
Common::FS::PathToQString(userPath,
Common::FS::GetUserPath(Common::FS::PathType::UserDir));
QDesktopServices::openUrl(QUrl::fromLocalFile(userPath + "/log"));
});
}
// Descriptions
{
// General

View File

@ -1394,6 +1394,13 @@
</item>
</layout>
</item>
<item>
<widget class="QPushButton" name="OpenLogLocationButton">
<property name="text">
<string>Open Log Location</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>

View File

@ -576,6 +576,10 @@
<source>Log Filter</source>
<translation>مرشح السجل</translation>
</message>
<message>
<source>Open Log Location</source>
<translation>افتح موقع السجل</translation>
</message>
<message>
<source>Input</source>
<translation>إدخال</translation>

View File

@ -576,6 +576,10 @@
<source>Log Filter</source>
<translation>Log Filter</translation>
</message>
<message>
<source>Open Log Location</source>
<translation>Åbn logplacering</translation>
</message>
<message>
<source>Input</source>
<translation>Indtastning</translation>

View File

@ -576,6 +576,10 @@
<source>Log Filter</source>
<translation>Log-Filter</translation>
</message>
<message>
<source>Open Log Location</source>
<translation>Protokollspeicherort öffnen</translation>
</message>
<message>
<source>Input</source>
<translation>Eingabe</translation>

View File

@ -576,6 +576,10 @@
<source>Log Filter</source>
<translation>Log Filter</translation>
</message>
<message>
<source>Open Log Location</source>
<translation>Άνοιγμα τοποθεσίας αρχείου καταγραφής</translation>
</message>
<message>
<source>Input</source>
<translation>Είσοδος</translation>

View File

@ -576,6 +576,10 @@
<source>Log Filter</source>
<translation>Log Filter</translation>
</message>
<message>
<source>Open Log Location</source>
<translation>Open Log Location</translation>
</message>
<message>
<source>Input</source>
<translation>Input</translation>

View File

@ -576,6 +576,10 @@
<source>Log Filter</source>
<translation>Filtro de registro</translation>
</message>
<message>
<source>Open Log Location</source>
<translation>Abrir ubicación del registro</translation>
</message>
<message>
<source>Input</source>
<translation>Entrada</translation>

View File

@ -576,6 +576,10 @@
<source>Log Filter</source>
<translation>Log فیلتر</translation>
</message>
<message>
<source>Open Log Location</source>
<translation>باز کردن مکان گزارش</translation>
</message>
<message>
<source>Input</source>
<translation>ورودی</translation>

View File

@ -576,6 +576,10 @@
<source>Log Filter</source>
<translation>Lokisuodatin</translation>
</message>
<message>
<source>Open Log Location</source>
<translation>Avaa lokin sijainti</translation>
</message>
<message>
<source>Input</source>
<translation>Syöttö</translation>

View File

@ -576,6 +576,10 @@
<source>Log Filter</source>
<translation>Filtre</translation>
</message>
<message>
<source>Open Log Location</source>
<translation>Ouvrir l'emplacement du journal</translation>
</message>
<message>
<source>Input</source>
<translation>Entrée</translation>

View File

@ -576,6 +576,10 @@
<source>Log Filter</source>
<translation>Naplózási Filter</translation>
</message>
<message>
<source>Open Log Location</source>
<translation>Napló helyének megnyitása</translation>
</message>
<message>
<source>Input</source>
<translation>Bemenet</translation>

View File

@ -576,6 +576,10 @@
<source>Log Filter</source>
<translation>Log Filter</translation>
</message>
<message>
<source>Open Log Location</source>
<translation>Buka Lokasi Log</translation>
</message>
<message>
<source>Input</source>
<translation>Masukan</translation>

View File

@ -576,6 +576,10 @@
<source>Log Filter</source>
<translation>Filtro Log</translation>
</message>
<message>
<source>Open Log Location</source>
<translation>Apri posizione del registro</translation>
</message>
<message>
<source>Input</source>
<translation>Input</translation>

View File

@ -576,6 +576,10 @@
<source>Log Filter</source>
<translation></translation>
</message>
<message>
<source>Open Log Location</source>
<translation></translation>
</message>
<message>
<source>Input</source>
<translation></translation>

View File

@ -576,6 +576,10 @@
<source>Log Filter</source>
<translation>Log Filter</translation>
</message>
<message>
<source>Open Log Location</source>
<translation> </translation>
</message>
<message>
<source>Input</source>
<translation>Input</translation>

View File

@ -576,6 +576,10 @@
<source>Log Filter</source>
<translation>Log Filter</translation>
</message>
<message>
<source>Open Log Location</source>
<translation>Atidaryti žurnalo vietą</translation>
</message>
<message>
<source>Input</source>
<translation>Įvestis</translation>

View File

@ -576,6 +576,10 @@
<source>Log Filter</source>
<translation>Logg filter</translation>
</message>
<message>
<source>Open Log Location</source>
<translation>Åpne loggplassering</translation>
</message>
<message>
<source>Input</source>
<translation>Inndata</translation>

View File

@ -576,6 +576,10 @@
<source>Log Filter</source>
<translation>Log Filter</translation>
</message>
<message>
<source>Open Log Location</source>
<translation>Loglocatie openen</translation>
</message>
<message>
<source>Input</source>
<translation>Invoer</translation>

View File

@ -576,6 +576,10 @@
<source>Log Filter</source>
<translation>Filtrowanie dziennika</translation>
</message>
<message>
<source>Open Log Location</source>
<translation>Otwórz lokalizację dziennika</translation>
</message>
<message>
<source>Input</source>
<translation>Wejście</translation>

View File

@ -576,6 +576,10 @@
<source>Log Filter</source>
<translation>Filtro do Registro</translation>
</message>
<message>
<source>Open Log Location</source>
<translation>Abrir local do log</translation>
</message>
<message>
<source>Input</source>
<translation>Entradas</translation>

View File

@ -576,6 +576,10 @@
<source>Log Filter</source>
<translation>Log Filter</translation>
</message>
<message>
<source>Open Log Location</source>
<translation>Deschide locația jurnalului</translation>
</message>
<message>
<source>Input</source>
<translation>Introducere</translation>

View File

@ -576,6 +576,10 @@
<source>Log Filter</source>
<translation>Фильтр логов</translation>
</message>
<message>
<source>Open Log Location</source>
<translation>Открыть местоположение журнала</translation>
</message>
<message>
<source>Input</source>
<translation>Ввод</translation>

View File

@ -576,6 +576,10 @@
<source>Log Filter</source>
<translation>Filtri i Ditarit</translation>
</message>
<message>
<source>Open Log Location</source>
<translation>Hap vendndodhjen e regjistrit</translation>
</message>
<message>
<source>Input</source>
<translation>Hyrja</translation>

View File

@ -1064,6 +1064,10 @@
<source>Log Filter</source>
<translation>Loggfilter</translation>
</message>
<message>
<source>Open Log Location</source>
<translation>Öppna loggplats</translation>
</message>
<message>
<source>Input</source>
<translation>Inmatning</translation>

View File

@ -576,6 +576,10 @@
<source>Log Filter</source>
<translation>Kayıt Filtresi</translation>
</message>
<message>
<source>Open Log Location</source>
<translation>Günlük Konumunu </translation>
</message>
<message>
<source>Input</source>
<translation>Girdi</translation>

View File

@ -576,6 +576,10 @@
<source>Log Filter</source>
<translation>Фільтр логів</translation>
</message>
<message>
<source>Open Log Location</source>
<translation>Відкрити місце розташування журналу</translation>
</message>
<message>
<source>Input</source>
<translation>Введення</translation>

View File

@ -576,6 +576,10 @@
<source>Log Filter</source>
<translation>Log Filter</translation>
</message>
<message>
<source>Open Log Location</source>
<translation>Mở vị trí nhật </translation>
</message>
<message>
<source>Input</source>
<translation>Đu vào</translation>

View File

@ -558,11 +558,11 @@
</message>
<message>
<source>Trophy Key</source>
<translation>Trophy Key</translation>
<translation></translation>
</message>
<message>
<source>Trophy</source>
<translation>Trophy</translation>
<translation></translation>
</message>
<message>
<source>Logger</source>
@ -576,6 +576,10 @@
<source>Log Filter</source>
<translation></translation>
</message>
<message>
<source>Open Log Location</source>
<translation></translation>
</message>
<message>
<source>Input</source>
<translation></translation>
@ -778,7 +782,7 @@
</message>
<message>
<source>TrophyKey</source>
<translation>Trophy Key:\nKey used to decrypt trophies. Must be obtained from your jailbroken console.\nMust contain only hex characters.</translation>
<translation>\n用于解密奖杯的密钥\n仅包含十六进制字符</translation>
</message>
<message>
<source>logTypeGroupBox</source>

View File

@ -576,6 +576,10 @@
<source>Log Filter</source>
<translation>Log Filter</translation>
</message>
<message>
<source>Open Log Location</source>
<translation></translation>
</message>
<message>
<source>Input</source>
<translation></translation>

View File

@ -172,20 +172,18 @@ Id EmitImageQueryDimensions(EmitContext& ctx, IR::Inst* inst, u32 handle, Id lod
const auto& texture = ctx.images[handle & 0xFFFF];
const Id image = ctx.OpLoad(texture.image_type, texture.id);
const auto sharp = ctx.info.images[handle & 0xFFFF].GetSharp(ctx.info);
const auto type = sharp.GetBoundType();
const Id zero = ctx.u32_zero_value;
const auto mips{[&] { return has_mips ? ctx.OpImageQueryLevels(ctx.U32[1], image) : zero; }};
const bool uses_lod{type != AmdGpu::ImageType::Color2DMsaa && !texture.is_storage};
const bool uses_lod{texture.view_type != AmdGpu::ImageType::Color2DMsaa && !texture.is_storage};
const auto query{[&](Id type) {
return uses_lod ? ctx.OpImageQuerySizeLod(type, image, lod)
: ctx.OpImageQuerySize(type, image);
}};
switch (type) {
switch (texture.view_type) {
case AmdGpu::ImageType::Color1D:
return ctx.OpCompositeConstruct(ctx.U32[4], query(ctx.U32[1]), zero, zero, mips());
case AmdGpu::ImageType::Color1DArray:
case AmdGpu::ImageType::Color2D:
case AmdGpu::ImageType::Cube:
case AmdGpu::ImageType::Color2DMsaa:
return ctx.OpCompositeConstruct(ctx.U32[4], query(ctx.U32[2]), zero, mips());
case AmdGpu::ImageType::Color2DArray:
@ -257,4 +255,12 @@ void EmitImageWrite(EmitContext& ctx, IR::Inst* inst, u32 handle, Id coords, Id
ctx.OpImageWrite(image, coords, texel, operands.mask, operands.operands);
}
Id EmitCubeFaceIndex(EmitContext& ctx, IR::Inst* inst, Id cube_coords) {
if (ctx.profile.supports_native_cube_calc) {
return ctx.OpCubeFaceIndexAMD(ctx.F32[1], cube_coords);
} else {
UNREACHABLE_MSG("SPIR-V Instruction");
}
}
} // namespace Shader::Backend::SPIRV

View File

@ -439,6 +439,7 @@ Id EmitImageAtomicAnd32(EmitContext& ctx, IR::Inst* inst, u32 handle, Id coords,
Id EmitImageAtomicOr32(EmitContext& ctx, IR::Inst* inst, u32 handle, Id coords, Id value);
Id EmitImageAtomicXor32(EmitContext& ctx, IR::Inst* inst, u32 handle, Id coords, Id value);
Id EmitImageAtomicExchange32(EmitContext& ctx, IR::Inst* inst, u32 handle, Id coords, Id value);
Id EmitCubeFaceIndex(EmitContext& ctx, IR::Inst* inst, Id cube_coords);
Id EmitLaneId(EmitContext& ctx);
Id EmitWarpId(EmitContext& ctx);
Id EmitQuadShuffle(EmitContext& ctx, Id value, Id index);

View File

@ -773,8 +773,8 @@ spv::ImageFormat GetFormat(const AmdGpu::Image& image) {
Id ImageType(EmitContext& ctx, const ImageResource& desc, Id sampled_type) {
const auto image = desc.GetSharp(ctx.info);
const auto format = desc.is_atomic ? GetFormat(image) : spv::ImageFormat::Unknown;
const auto type = image.GetBoundType();
const u32 sampled = desc.IsStorage(image) ? 2 : 1;
const auto type = image.GetViewType(desc.is_array);
const u32 sampled = desc.is_written ? 2 : 1;
switch (type) {
case AmdGpu::ImageType::Color1D:
return ctx.TypeImage(sampled_type, spv::Dim::Dim1D, false, false, false, sampled, format);
@ -788,9 +788,6 @@ Id ImageType(EmitContext& ctx, const ImageResource& desc, Id sampled_type) {
return ctx.TypeImage(sampled_type, spv::Dim::Dim2D, false, false, true, sampled, format);
case AmdGpu::ImageType::Color3D:
return ctx.TypeImage(sampled_type, spv::Dim::Dim3D, false, false, false, sampled, format);
case AmdGpu::ImageType::Cube:
return ctx.TypeImage(sampled_type, spv::Dim::Cube, false, desc.is_array, false, sampled,
format);
default:
break;
}
@ -802,7 +799,7 @@ void EmitContext::DefineImagesAndSamplers() {
const auto sharp = image_desc.GetSharp(info);
const auto nfmt = sharp.GetNumberFmt();
const bool is_integer = AmdGpu::IsInteger(nfmt);
const bool is_storage = image_desc.IsStorage(sharp);
const bool is_storage = image_desc.is_written;
const VectorIds& data_types = GetAttributeType(*this, nfmt);
const Id sampled_type = data_types[1];
const Id image_type{ImageType(*this, image_desc, sampled_type)};
@ -817,6 +814,7 @@ void EmitContext::DefineImagesAndSamplers() {
.sampled_type = is_storage ? sampled_type : TypeSampledImage(image_type),
.pointer_type = pointer_type,
.image_type = image_type,
.view_type = sharp.GetViewType(image_desc.is_array),
.is_integer = is_integer,
.is_storage = is_storage,
});

View File

@ -222,6 +222,7 @@ public:
Id sampled_type;
Id pointer_type;
Id image_type;
AmdGpu::ImageType view_type;
bool is_integer = false;
bool is_storage = false;
};

View File

@ -47,13 +47,26 @@ static IR::Condition MakeCondition(const GcnInst& inst) {
}
}
static bool IgnoresExecMask(Opcode opcode) {
switch (opcode) {
case Opcode::V_WRITELANE_B32:
static bool IgnoresExecMask(const GcnInst& inst) {
// EXEC mask does not affect scalar instructions or branches.
switch (inst.category) {
case InstCategory::ScalarALU:
case InstCategory::ScalarMemory:
case InstCategory::FlowControl:
return true;
default:
return false;
break;
}
// Read/Write Lane instructions are not affected either.
switch (inst.opcode) {
case Opcode::V_READLANE_B32:
case Opcode::V_WRITELANE_B32:
case Opcode::V_READFIRSTLANE_B32:
return true;
default:
break;
}
return false;
}
static constexpr size_t LabelReserveSize = 32;
@ -147,8 +160,7 @@ void CFG::EmitDivergenceLabels() {
// If all instructions in the scope ignore exec masking, we shouldn't insert a
// scope.
const auto start = inst_list.begin() + curr_begin + 1;
if (!std::ranges::all_of(start, inst_list.begin() + index, IgnoresExecMask,
&GcnInst::opcode)) {
if (!std::ranges::all_of(start, inst_list.begin() + index, IgnoresExecMask)) {
// Add a label to the instruction right after the open scope call.
// It is the start of a new basic block.
const auto& save_inst = inst_list[curr_begin];

View File

@ -301,6 +301,9 @@ private:
IR::U32 VMovRelSHelper(u32 src_vgprno, const IR::U32 m0);
void VMovRelDHelper(u32 dst_vgprno, const IR::U32 src_val, const IR::U32 m0);
IR::F32 SelectCubeResult(const IR::F32& x, const IR::F32& y, const IR::F32& z,
const IR::F32& x_res, const IR::F32& y_res, const IR::F32& z_res);
void LogMissingOpcode(const GcnInst& inst);
private:

View File

@ -3,6 +3,7 @@
#include "shader_recompiler/frontend/opcodes.h"
#include "shader_recompiler/frontend/translate/translate.h"
#include "shader_recompiler/profile.h"
namespace Shader::Gcn {
@ -1042,20 +1043,81 @@ void Translator::V_MAD_U32_U24(const GcnInst& inst) {
V_MAD_I32_I24(inst, false);
}
IR::F32 Translator::SelectCubeResult(const IR::F32& x, const IR::F32& y, const IR::F32& z,
const IR::F32& x_res, const IR::F32& y_res,
const IR::F32& z_res) {
const auto abs_x = ir.FPAbs(x);
const auto abs_y = ir.FPAbs(y);
const auto abs_z = ir.FPAbs(z);
const auto z_face_cond{
ir.LogicalAnd(ir.FPGreaterThanEqual(abs_z, abs_x), ir.FPGreaterThanEqual(abs_z, abs_y))};
const auto y_face_cond{ir.FPGreaterThanEqual(abs_y, abs_x)};
return IR::F32{ir.Select(z_face_cond, z_res, ir.Select(y_face_cond, y_res, x_res))};
}
void Translator::V_CUBEID_F32(const GcnInst& inst) {
SetDst(inst.dst[0], GetSrc<IR::F32>(inst.src[2]));
const auto x = GetSrc<IR::F32>(inst.src[0]);
const auto y = GetSrc<IR::F32>(inst.src[1]);
const auto z = GetSrc<IR::F32>(inst.src[2]);
IR::F32 result;
if (profile.supports_native_cube_calc) {
result = ir.CubeFaceIndex(ir.CompositeConstruct(x, y, z));
} else {
const auto x_neg_cond{ir.FPLessThan(x, ir.Imm32(0.f))};
const auto y_neg_cond{ir.FPLessThan(y, ir.Imm32(0.f))};
const auto z_neg_cond{ir.FPLessThan(z, ir.Imm32(0.f))};
const IR::F32 x_face{ir.Select(x_neg_cond, ir.Imm32(1.f), ir.Imm32(0.f))};
const IR::F32 y_face{ir.Select(y_neg_cond, ir.Imm32(3.f), ir.Imm32(2.f))};
const IR::F32 z_face{ir.Select(z_neg_cond, ir.Imm32(5.f), ir.Imm32(4.f))};
result = SelectCubeResult(x, y, z, x_face, y_face, z_face);
}
SetDst(inst.dst[0], result);
}
void Translator::V_CUBESC_F32(const GcnInst& inst) {
SetDst(inst.dst[0], GetSrc<IR::F32>(inst.src[0]));
const auto x = GetSrc<IR::F32>(inst.src[0]);
const auto y = GetSrc<IR::F32>(inst.src[1]);
const auto z = GetSrc<IR::F32>(inst.src[2]);
const auto x_neg_cond{ir.FPLessThan(x, ir.Imm32(0.f))};
const auto z_neg_cond{ir.FPLessThan(z, ir.Imm32(0.f))};
const IR::F32 x_sc{ir.Select(x_neg_cond, z, ir.FPNeg(z))};
const IR::F32 y_sc{x};
const IR::F32 z_sc{ir.Select(z_neg_cond, ir.FPNeg(x), x)};
const auto result{SelectCubeResult(x, y, z, x_sc, y_sc, z_sc)};
SetDst(inst.dst[0], result);
}
void Translator::V_CUBETC_F32(const GcnInst& inst) {
SetDst(inst.dst[0], GetSrc<IR::F32>(inst.src[1]));
const auto x = GetSrc<IR::F32>(inst.src[0]);
const auto y = GetSrc<IR::F32>(inst.src[1]);
const auto z = GetSrc<IR::F32>(inst.src[2]);
const auto y_neg_cond{ir.FPLessThan(y, ir.Imm32(0.f))};
const IR::F32 x_z_tc{ir.FPNeg(y)};
const IR::F32 y_tc{ir.Select(y_neg_cond, ir.FPNeg(z), z)};
const auto result{SelectCubeResult(x, y, z, x_z_tc, y_tc, x_z_tc)};
SetDst(inst.dst[0], result);
}
void Translator::V_CUBEMA_F32(const GcnInst& inst) {
SetDst(inst.dst[0], ir.Imm32(1.f));
const auto x = GetSrc<IR::F32>(inst.src[0]);
const auto y = GetSrc<IR::F32>(inst.src[1]);
const auto z = GetSrc<IR::F32>(inst.src[2]);
const auto two{ir.Imm32(2.f)};
const IR::F32 x_major_axis{ir.FPMul(x, two)};
const IR::F32 y_major_axis{ir.FPMul(y, two)};
const IR::F32 z_major_axis{ir.FPMul(z, two)};
const auto result{SelectCubeResult(x, y, z, x_major_axis, y_major_axis, z_major_axis)};
SetDst(inst.dst[0], result);
}
void Translator::V_BFE_U32(bool is_signed, const GcnInst& inst) {

View File

@ -418,6 +418,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);
const IR::Value texel = ir.ImageRead(handle, body, {}, {}, info);
for (u32 i = 0; i < 4; i++) {
@ -442,6 +443,7 @@ void Translator::IMAGE_STORE(bool has_mip, const GcnInst& inst) {
IR::TextureInstInfo info{};
info.has_lod.Assign(has_mip);
info.is_array.Assign(mimg.da);
boost::container::static_vector<IR::F32, 4> comps;
for (u32 i = 0; i < 4; i++) {
@ -456,13 +458,18 @@ void Translator::IMAGE_STORE(bool has_mip, const GcnInst& inst) {
}
void Translator::IMAGE_GET_RESINFO(const GcnInst& inst) {
const auto& mimg = inst.control.mimg;
IR::VectorReg dst_reg{inst.dst[0].code};
const IR::ScalarReg tsharp_reg{inst.src[2].code * 4};
const auto flags = ImageResFlags(inst.control.mimg.dmask);
const bool has_mips = flags.test(ImageResComponent::MipCount);
const IR::U32 lod = ir.GetVectorReg(IR::VectorReg(inst.src[0].code));
const IR::Value tsharp = ir.GetScalarReg(tsharp_reg);
const IR::Value size = ir.ImageQueryDimension(tsharp, lod, ir.Imm1(has_mips));
IR::TextureInstInfo info{};
info.is_array.Assign(mimg.da);
const IR::Value size = ir.ImageQueryDimension(tsharp, lod, ir.Imm1(has_mips), info);
if (flags.test(ImageResComponent::Width)) {
ir.SetVectorReg(dst_reg++, IR::U32{ir.CompositeExtract(size, 0)});
@ -484,6 +491,9 @@ void Translator::IMAGE_ATOMIC(AtomicOp op, const GcnInst& inst) {
IR::VectorReg addr_reg{inst.src[0].code};
const IR::ScalarReg tsharp_reg{inst.src[2].code * 4};
IR::TextureInstInfo info{};
info.is_array.Assign(mimg.da);
const IR::Value value = ir.GetVectorReg(val_reg);
const IR::Value handle = ir.GetScalarReg(tsharp_reg);
const IR::Value body =
@ -494,25 +504,25 @@ void Translator::IMAGE_ATOMIC(AtomicOp op, const GcnInst& inst) {
case AtomicOp::Swap:
return ir.ImageAtomicExchange(handle, body, value, {});
case AtomicOp::Add:
return ir.ImageAtomicIAdd(handle, body, value, {});
return ir.ImageAtomicIAdd(handle, body, value, info);
case AtomicOp::Smin:
return ir.ImageAtomicIMin(handle, body, value, true, {});
return ir.ImageAtomicIMin(handle, body, value, true, info);
case AtomicOp::Umin:
return ir.ImageAtomicUMin(handle, body, value, {});
return ir.ImageAtomicUMin(handle, body, value, info);
case AtomicOp::Smax:
return ir.ImageAtomicIMax(handle, body, value, true, {});
return ir.ImageAtomicIMax(handle, body, value, true, info);
case AtomicOp::Umax:
return ir.ImageAtomicUMax(handle, body, value, {});
return ir.ImageAtomicUMax(handle, body, value, info);
case AtomicOp::And:
return ir.ImageAtomicAnd(handle, body, value, {});
return ir.ImageAtomicAnd(handle, body, value, info);
case AtomicOp::Or:
return ir.ImageAtomicOr(handle, body, value, {});
return ir.ImageAtomicOr(handle, body, value, info);
case AtomicOp::Xor:
return ir.ImageAtomicXor(handle, body, value, {});
return ir.ImageAtomicXor(handle, body, value, info);
case AtomicOp::Inc:
return ir.ImageAtomicInc(handle, body, value, {});
return ir.ImageAtomicInc(handle, body, value, info);
case AtomicOp::Dec:
return ir.ImageAtomicDec(handle, body, value, {});
return ir.ImageAtomicDec(handle, body, value, info);
default:
UNREACHABLE();
}
@ -643,11 +653,14 @@ void Translator::IMAGE_GET_LOD(const GcnInst& inst) {
IR::VectorReg addr_reg{inst.src[0].code};
const IR::ScalarReg tsharp_reg{inst.src[2].code * 4};
IR::TextureInstInfo info{};
info.is_array.Assign(mimg.da);
const IR::Value handle = ir.GetScalarReg(tsharp_reg);
const IR::Value body = ir.CompositeConstruct(
ir.GetVectorReg<IR::F32>(addr_reg), ir.GetVectorReg<IR::F32>(addr_reg + 1),
ir.GetVectorReg<IR::F32>(addr_reg + 2), ir.GetVectorReg<IR::F32>(addr_reg + 3));
const IR::Value lod = ir.ImageQueryLod(handle, body, {});
const IR::Value lod = ir.ImageQueryLod(handle, body, info);
ir.SetVectorReg(dst_reg++, IR::F32{ir.CompositeExtract(lod, 0)});
ir.SetVectorReg(dst_reg++, IR::F32{ir.CompositeExtract(lod, 1)});
}

View File

@ -70,14 +70,8 @@ struct ImageResource {
bool is_depth{};
bool is_atomic{};
bool is_array{};
bool is_read{};
bool is_written{};
[[nodiscard]] bool IsStorage(const AmdGpu::Image& image) const noexcept {
// Need cube as storage when used with ImageRead.
return is_written || (is_read && image.GetBoundType() == AmdGpu::ImageType::Cube);
}
[[nodiscard]] constexpr AmdGpu::Image GetSharp(const Info& info) const noexcept;
};
using ImageResourceList = boost::container::small_vector<ImageResource, 16>;

View File

@ -1732,11 +1732,6 @@ Value IREmitter::ImageGatherDref(const Value& handle, const Value& coords, const
return Inst(Opcode::ImageGatherDref, Flags{info}, handle, coords, offset, dref);
}
Value IREmitter::ImageQueryDimension(const Value& handle, const IR::U32& lod,
const IR::U1& skip_mips) {
return Inst(Opcode::ImageQueryDimensions, handle, lod, skip_mips);
}
Value IREmitter::ImageQueryDimension(const Value& handle, const IR::U32& lod,
const IR::U1& skip_mips, TextureInstInfo info) {
return Inst(Opcode::ImageQueryDimensions, Flags{info}, handle, lod, skip_mips);
@ -1763,6 +1758,10 @@ void IREmitter::ImageWrite(const Value& handle, const Value& coords, const U32&
Inst(Opcode::ImageWrite, Flags{info}, handle, coords, lod, multisampling, color);
}
[[nodiscard]] F32 IREmitter::CubeFaceIndex(const Value& cube_coords) {
return Inst<F32>(Opcode::CubeFaceIndex, cube_coords);
}
// Debug print maps to SPIRV's NonSemantic DebugPrintf instruction
// Renderdoc will hook in its own implementation of the SPIRV instruction
// Renderdoc accepts format specifiers, e.g. %u, listed here:

View File

@ -324,8 +324,6 @@ public:
const F32& dref, const F32& lod,
const Value& offset, TextureInstInfo info);
[[nodiscard]] Value ImageQueryDimension(const Value& handle, const U32& lod,
const U1& skip_mips);
[[nodiscard]] Value ImageQueryDimension(const Value& handle, const U32& lod,
const U1& skip_mips, TextureInstInfo info);
@ -344,6 +342,8 @@ public:
void ImageWrite(const Value& handle, const Value& coords, const U32& lod,
const U32& multisampling, const Value& color, TextureInstInfo info);
[[nodiscard]] F32 CubeFaceIndex(const Value& cube_coords);
void EmitVertex();
void EmitPrimitive();

View File

@ -374,6 +374,9 @@ OPCODE(ImageAtomicOr32, U32, Opaq
OPCODE(ImageAtomicXor32, U32, Opaque, Opaque, U32, )
OPCODE(ImageAtomicExchange32, U32, Opaque, Opaque, U32, )
// Cube operations - optional, usable if profile.supports_native_cube_calc
OPCODE(CubeFaceIndex, F32, F32x3, )
// Warp operations
OPCODE(LaneId, U32, )
OPCODE(WarpId, U32, )

View File

@ -161,10 +161,9 @@ public:
u32 Add(const ImageResource& desc) {
const u32 index{Add(image_resources, desc, [&desc](const auto& existing) {
return desc.sharp_idx == existing.sharp_idx;
return desc.sharp_idx == existing.sharp_idx && desc.is_array == existing.is_array;
})};
auto& image = image_resources[index];
image.is_read |= desc.is_read;
image.is_written |= desc.is_written;
return index;
}
@ -361,7 +360,6 @@ void PatchImageSharp(IR::Block& block, IR::Inst& inst, Info& info, Descriptors&
image = AmdGpu::Image::Null();
}
ASSERT(image.GetType() != AmdGpu::ImageType::Invalid);
const bool is_read = inst.GetOpcode() == IR::Opcode::ImageRead;
const bool is_written = inst.GetOpcode() == IR::Opcode::ImageWrite;
// Patch image instruction if image is FMask.
@ -402,7 +400,6 @@ void PatchImageSharp(IR::Block& block, IR::Inst& inst, Info& info, Descriptors&
.is_depth = bool(inst_info.is_depth),
.is_atomic = IsImageAtomicInstruction(inst),
.is_array = bool(inst_info.is_array),
.is_read = is_read,
.is_written = is_written,
});
@ -560,40 +557,15 @@ void PatchTextureBufferArgs(IR::Block& block, IR::Inst& inst, Info& info) {
}
}
IR::Value PatchCubeCoord(IR::IREmitter& ir, const IR::Value& s, const IR::Value& t,
const IR::Value& z, bool is_written, bool is_array) {
// When cubemap is written with imageStore it is treated like 2DArray.
if (is_written) {
return ir.CompositeConstruct(s, t, z);
}
ASSERT(s.Type() == IR::Type::F32); // in case of fetched image need to adjust the code below
// We need to fix x and y coordinate,
// because the s and t coordinate will be scaled and plus 1.5 by v_madak_f32.
// We already force the scale value to be 1.0 when handling v_cubema_f32,
// here we subtract 1.5 to recover the original value.
const IR::Value x = ir.FPSub(IR::F32{s}, ir.Imm32(1.5f));
const IR::Value y = ir.FPSub(IR::F32{t}, ir.Imm32(1.5f));
if (is_array) {
const IR::U32 array_index = ir.ConvertFToU(32, IR::F32{z});
const IR::U32 face_id = ir.BitwiseAnd(array_index, ir.Imm32(7u));
const IR::U32 slice_id = ir.ShiftRightLogical(array_index, ir.Imm32(3u));
return ir.CompositeConstruct(x, y, ir.ConvertIToF(32, 32, false, face_id),
ir.ConvertIToF(32, 32, false, slice_id));
} else {
return ir.CompositeConstruct(x, y, z);
}
}
void PatchImageSampleArgs(IR::Block& block, IR::Inst& inst, Info& info,
const AmdGpu::Image& image) {
const ImageResource& image_res, const AmdGpu::Image& image) {
const auto handle = inst.Arg(0);
const auto sampler_res = info.samplers[(handle.U32() >> 16) & 0xFFFF];
auto sampler = sampler_res.GetSharp(info);
IR::IREmitter ir{block, IR::Block::InstructionList::s_iterator_to(inst)};
const auto inst_info = inst.Flags<IR::TextureInstInfo>();
const auto view_type = image.GetViewType(image_res.is_array);
IR::Inst* body1 = inst.Arg(1).InstRecursive();
IR::Inst* body2 = inst.Arg(2).InstRecursive();
@ -640,7 +612,7 @@ void PatchImageSampleArgs(IR::Block& block, IR::Inst& inst, Info& info,
return ir.BitFieldExtract(IR::U32{arg}, ir.Imm32(off), ir.Imm32(6), true);
};
switch (image.GetType()) {
switch (view_type) {
case AmdGpu::ImageType::Color1D:
case AmdGpu::ImageType::Color1DArray:
return read(0);
@ -649,7 +621,6 @@ void PatchImageSampleArgs(IR::Block& block, IR::Inst& inst, Info& info,
case AmdGpu::ImageType::Color2DMsaa:
return ir.CompositeConstruct(read(0), read(8));
case AmdGpu::ImageType::Color3D:
case AmdGpu::ImageType::Cube:
return ir.CompositeConstruct(read(0), read(8), read(16));
default:
UNREACHABLE();
@ -661,7 +632,7 @@ void PatchImageSampleArgs(IR::Block& block, IR::Inst& inst, Info& info,
if (!inst_info.has_derivatives) {
return {};
}
switch (image.GetType()) {
switch (view_type) {
case AmdGpu::ImageType::Color1D:
case AmdGpu::ImageType::Color1DArray:
// du/dx, du/dy
@ -675,7 +646,6 @@ void PatchImageSampleArgs(IR::Block& block, IR::Inst& inst, Info& info,
return {ir.CompositeConstruct(get_addr_reg(addr_reg - 4), get_addr_reg(addr_reg - 3)),
ir.CompositeConstruct(get_addr_reg(addr_reg - 2), get_addr_reg(addr_reg - 1))};
case AmdGpu::ImageType::Color3D:
case AmdGpu::ImageType::Cube:
// (du/dx, dv/dx, dw/dx), (du/dy, dv/dy, dw/dy)
addr_reg = addr_reg + 6;
return {ir.CompositeConstruct(get_addr_reg(addr_reg - 6), get_addr_reg(addr_reg - 5),
@ -691,7 +661,8 @@ void PatchImageSampleArgs(IR::Block& block, IR::Inst& inst, Info& info,
// Query dimensions of image if needed for normalization.
// We can't use the image sharp because it could be bound to a different image later.
const auto dimensions =
unnormalized ? ir.ImageQueryDimension(handle, ir.Imm32(0u), ir.Imm1(false)) : IR::Value{};
unnormalized ? ir.ImageQueryDimension(handle, ir.Imm32(0u), ir.Imm1(false), inst_info)
: IR::Value{};
const auto get_coord = [&](u32 coord_idx, u32 dim_idx) -> IR::Value {
const auto coord = get_addr_reg(coord_idx);
if (unnormalized) {
@ -705,7 +676,7 @@ void PatchImageSampleArgs(IR::Block& block, IR::Inst& inst, Info& info,
// Now we can load body components as noted in Table 8.9 Image Opcodes with Sampler
const IR::Value coords = [&] -> IR::Value {
switch (image.GetType()) {
switch (view_type) {
case AmdGpu::ImageType::Color1D: // x
addr_reg = addr_reg + 1;
return get_coord(addr_reg - 1, 0);
@ -724,10 +695,6 @@ void PatchImageSampleArgs(IR::Block& block, IR::Inst& inst, Info& info,
addr_reg = addr_reg + 3;
return ir.CompositeConstruct(get_coord(addr_reg - 3, 0), get_coord(addr_reg - 2, 1),
get_coord(addr_reg - 1, 2));
case AmdGpu::ImageType::Cube: // x, y, face
addr_reg = addr_reg + 3;
return PatchCubeCoord(ir, get_coord(addr_reg - 3, 0), get_coord(addr_reg - 2, 1),
get_addr_reg(addr_reg - 1), false, inst_info.is_array);
default:
UNREACHABLE();
}
@ -779,17 +746,18 @@ void PatchImageArgs(IR::Block& block, IR::Inst& inst, Info& info) {
// Sample instructions must be handled separately using address register data.
if (inst.GetOpcode() == IR::Opcode::ImageSampleRaw) {
PatchImageSampleArgs(block, inst, info, image);
PatchImageSampleArgs(block, inst, info, image_res, image);
return;
}
IR::IREmitter ir{block, IR::Block::InstructionList::s_iterator_to(inst)};
const auto inst_info = inst.Flags<IR::TextureInstInfo>();
const auto view_type = image.GetViewType(image_res.is_array);
// Now that we know the image type, adjust texture coordinate vector.
IR::Inst* body = inst.Arg(1).InstRecursive();
const auto [coords, arg] = [&] -> std::pair<IR::Value, IR::Value> {
switch (image.GetType()) {
switch (view_type) {
case AmdGpu::ImageType::Color1D: // x, [lod]
return {body->Arg(0), body->Arg(1)};
case AmdGpu::ImageType::Color1DArray: // x, slice, [lod]
@ -805,22 +773,18 @@ void PatchImageArgs(IR::Block& block, IR::Inst& inst, Info& info) {
[[fallthrough]];
case AmdGpu::ImageType::Color3D: // x, y, z, [lod]
return {ir.CompositeConstruct(body->Arg(0), body->Arg(1), body->Arg(2)), body->Arg(3)};
case AmdGpu::ImageType::Cube: // x, y, face, [lod]
return {PatchCubeCoord(ir, body->Arg(0), body->Arg(1), body->Arg(2),
inst.GetOpcode() == IR::Opcode::ImageWrite, inst_info.is_array),
body->Arg(3)};
default:
UNREACHABLE_MSG("Unknown image type {}", image.GetType());
UNREACHABLE_MSG("Unknown image type {}", view_type);
}
}();
const auto has_ms = image.GetType() == AmdGpu::ImageType::Color2DMsaa ||
image.GetType() == AmdGpu::ImageType::Color2DMsaaArray;
const auto has_ms = view_type == AmdGpu::ImageType::Color2DMsaa ||
view_type == AmdGpu::ImageType::Color2DMsaaArray;
ASSERT(!inst_info.has_lod || !has_ms);
const auto lod = inst_info.has_lod ? IR::U32{arg} : IR::U32{};
const auto ms = has_ms ? IR::U32{arg} : IR::U32{};
const auto is_storage = image_res.IsStorage(image);
const auto is_storage = image_res.is_written;
if (inst.GetOpcode() == IR::Opcode::ImageRead) {
auto texel = ir.ImageRead(handle, coords, lod, ms, inst_info);
if (is_storage) {

View File

@ -5,7 +5,7 @@
namespace Shader::Optimization {
void Visit(Info& info, IR::Inst& inst) {
void Visit(Info& info, const IR::Inst& inst) {
switch (inst.GetOpcode()) {
case IR::Opcode::GetAttribute:
case IR::Opcode::GetAttributeU32:

View File

@ -24,6 +24,7 @@ struct Profile {
bool support_explicit_workgroup_layout{};
bool support_legacy_vertex_attributes{};
bool supports_image_load_store_lod{};
bool supports_native_cube_calc{};
bool has_broken_spirv_clamp{};
bool lower_left_origin_mode{};
bool needs_manual_interpolation{};

View File

@ -113,9 +113,9 @@ struct StageSpecialization {
});
ForEachSharp(binding, images, info->images,
[](auto& spec, const auto& desc, AmdGpu::Image sharp) {
spec.type = sharp.GetBoundType();
spec.type = sharp.GetViewType(desc.is_array);
spec.is_integer = AmdGpu::IsInteger(sharp.GetNumberFmt());
spec.is_storage = desc.IsStorage(sharp);
spec.is_storage = desc.is_written;
if (spec.is_storage) {
spec.dst_select = sharp.DstSelect();
}

View File

@ -899,7 +899,8 @@ struct Liverpool {
// There is a small difference between T# and CB number types, account for it.
return RemapNumberFormat(info.number_type == NumberFormat::SnormNz
? NumberFormat::Srgb
: info.number_type.Value());
: info.number_type.Value(),
info.format);
}
[[nodiscard]] NumberConversion GetNumberConversion() const {

View File

@ -54,7 +54,7 @@ struct Buffer {
}
NumberFormat GetNumberFmt() const noexcept {
return RemapNumberFormat(NumberFormat(num_format));
return RemapNumberFormat(NumberFormat(num_format), DataFormat(data_format));
}
DataFormat GetDataFmt() const noexcept {
@ -119,6 +119,7 @@ constexpr std::string_view NameOf(ImageType type) {
enum class TilingMode : u32 {
Depth_MacroTiled = 0u,
Display_Linear = 0x8u,
Display_MicroTiled = 0x9u,
Display_MacroTiled = 0xAu,
Texture_MicroTiled = 0xDu,
Texture_MacroTiled = 0xEu,
@ -131,6 +132,8 @@ constexpr std::string_view NameOf(TilingMode type) {
return "Depth_MacroTiled";
case TilingMode::Display_Linear:
return "Display_Linear";
case TilingMode::Display_MicroTiled:
return "Display_MicroTiled";
case TilingMode::Display_MacroTiled:
return "Display_MacroTiled";
case TilingMode::Texture_MicroTiled:
@ -226,15 +229,15 @@ struct Image {
return pitch + 1;
}
u32 NumLayers(bool is_array) const {
u32 slices = GetType() == ImageType::Color3D ? 1 : depth + 1;
if (GetType() == ImageType::Cube) {
if (is_array) {
slices = last_array + 1;
ASSERT(slices % 6 == 0);
} else {
slices = 6;
}
[[nodiscard]] u32 NumLayers() const noexcept {
// Depth is the number of layers for Array images.
u32 slices = depth + 1;
if (GetType() == ImageType::Color3D) {
// Depth is the actual texture depth for 3D images.
slices = 1;
} else if (IsCube()) {
// Depth is the number of full cubes for Cube images.
slices *= 6;
}
if (pow2pad) {
slices = std::bit_ceil(slices);
@ -256,8 +259,12 @@ struct Image {
return 1;
}
bool IsCube() const noexcept {
return static_cast<ImageType>(type) == ImageType::Cube;
}
ImageType GetType() const noexcept {
return static_cast<ImageType>(type);
return IsCube() ? ImageType::Color2DArray : static_cast<ImageType>(type);
}
DataFormat GetDataFmt() const noexcept {
@ -265,7 +272,7 @@ struct Image {
}
NumberFormat GetNumberFmt() const noexcept {
return RemapNumberFormat(NumberFormat(num_format));
return RemapNumberFormat(NumberFormat(num_format), DataFormat(data_format));
}
NumberConversion GetNumberConversion() const noexcept {
@ -289,13 +296,48 @@ struct Image {
GetDataFmt() <= DataFormat::FormatFmask64_8;
}
bool IsPartialCubemap() const {
const auto viewed_slice = last_array - base_array + 1;
return GetType() == ImageType::Cube && viewed_slice < 6;
[[nodiscard]] ImageType GetViewType(const bool is_array) const noexcept {
const auto base_type = GetType();
if (IsCube()) {
// Cube needs to remain array type regardless of instruction array specifier.
return base_type;
}
if (base_type == ImageType::Color1DArray && !is_array) {
return ImageType::Color1D;
}
if (base_type == ImageType::Color2DArray && !is_array) {
return ImageType::Color2D;
}
if (base_type == ImageType::Color2DMsaaArray && !is_array) {
return ImageType::Color2DMsaa;
}
return base_type;
}
ImageType GetBoundType() const noexcept {
return IsPartialCubemap() ? ImageType::Color2DArray : GetType();
[[nodiscard]] u32 NumViewLevels(const bool is_array) const noexcept {
switch (GetViewType(is_array)) {
case ImageType::Color2DMsaa:
case ImageType::Color2DMsaaArray:
return 1;
default:
// Constrain to actual number of available levels.
const auto max_level = std::min<u32>(last_level + 1, NumLevels());
return max_level > base_level ? max_level - base_level : 1;
}
}
[[nodiscard]] u32 NumViewLayers(const bool is_array) const noexcept {
switch (GetViewType(is_array)) {
case ImageType::Color1D:
case ImageType::Color2D:
case ImageType::Color2DMsaa:
case ImageType::Color3D:
return 1;
default:
// Constrain to actual number of available layers.
const auto max_array = std::min<u32>(last_array + 1, NumLayers());
return max_array > base_array ? max_array - base_array : 1;
}
}
};
static_assert(sizeof(Image) == 32); // 256bits

View File

@ -252,7 +252,7 @@ inline DataFormat RemapDataFormat(const DataFormat format) {
}
}
inline NumberFormat RemapNumberFormat(const NumberFormat format) {
inline NumberFormat RemapNumberFormat(const NumberFormat format, const DataFormat data_format) {
switch (format) {
case NumberFormat::Uscaled:
return NumberFormat::Uint;
@ -260,6 +260,14 @@ inline NumberFormat RemapNumberFormat(const NumberFormat format) {
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;
}

View File

@ -10,13 +10,13 @@
#include "video_core/amdgpu/liverpool.h"
#include "video_core/buffer_cache/buffer_cache.h"
#include "video_core/renderer_vulkan/liverpool_to_vk.h"
#include "video_core/renderer_vulkan/vk_graphics_pipeline.h"
#include "video_core/renderer_vulkan/vk_instance.h"
#include "video_core/renderer_vulkan/vk_scheduler.h"
#include "video_core/texture_cache/texture_cache.h"
namespace VideoCore {
static constexpr size_t NumVertexBuffers = 32;
static constexpr size_t GdsBufferSize = 64_KB;
static constexpr size_t StagingBufferSize = 1_GB;
static constexpr size_t UboStreamBufferSize = 64_MB;
@ -89,35 +89,22 @@ void BufferCache::DownloadBufferMemory(Buffer& buffer, VAddr device_addr, u64 si
}
}
bool BufferCache::BindVertexBuffers(
const Shader::Info& vs_info, const std::optional<Shader::Gcn::FetchShaderData>& fetch_shader) {
boost::container::small_vector<vk::VertexInputAttributeDescription2EXT, 16> attributes;
boost::container::small_vector<vk::VertexInputBindingDescription2EXT, 16> bindings;
SCOPE_EXIT {
void BufferCache::BindVertexBuffers(const Vulkan::GraphicsPipeline& pipeline) {
Vulkan::VertexInputs<vk::VertexInputAttributeDescription2EXT> attributes;
Vulkan::VertexInputs<vk::VertexInputBindingDescription2EXT> bindings;
Vulkan::VertexInputs<AmdGpu::Buffer> guest_buffers;
pipeline.GetVertexInputs(attributes, bindings, guest_buffers);
if (instance.IsVertexInputDynamicState()) {
// Update current vertex inputs.
const auto cmdbuf = scheduler.CommandBuffer();
cmdbuf.setVertexInputEXT(bindings, attributes);
} else if (bindings.empty()) {
// Required to call bindVertexBuffers2EXT at least once in the current command buffer
// with non-null strides without a non-dynamic stride pipeline in between. Thus even
// when nothing is bound we still need to make a dummy call. Non-null strides in turn
// requires a count greater than 0.
const auto cmdbuf = scheduler.CommandBuffer();
const std::array null_buffers = {GetBuffer(NULL_BUFFER_ID).buffer.buffer};
constexpr std::array null_offsets = {static_cast<vk::DeviceSize>(0)};
cmdbuf.bindVertexBuffers2EXT(0, null_buffers, null_offsets, null_offsets, null_offsets);
}
};
if (!fetch_shader || fetch_shader->attributes.empty()) {
return false;
}
std::array<vk::Buffer, NumVertexBuffers> host_buffers;
std::array<vk::DeviceSize, NumVertexBuffers> host_offsets;
std::array<vk::DeviceSize, NumVertexBuffers> host_sizes;
std::array<vk::DeviceSize, NumVertexBuffers> host_strides;
boost::container::static_vector<AmdGpu::Buffer, NumVertexBuffers> guest_buffers;
if (bindings.empty()) {
// If there are no bindings, there is nothing further to do.
return;
}
struct BufferRange {
VAddr base_address;
@ -125,51 +112,26 @@ bool BufferCache::BindVertexBuffers(
vk::Buffer vk_buffer;
u64 offset;
size_t GetSize() const {
[[nodiscard]] size_t GetSize() const {
return end_address - base_address;
}
};
// Calculate buffers memory overlaps
bool has_step_rate = false;
boost::container::static_vector<BufferRange, NumVertexBuffers> ranges{};
for (const auto& attrib : fetch_shader->attributes) {
if (attrib.UsesStepRates()) {
has_step_rate = true;
continue;
}
const auto& buffer = attrib.GetSharp(vs_info);
if (buffer.GetSize() == 0) {
continue;
}
guest_buffers.emplace_back(buffer);
// Build list of ranges covering the requested buffers
Vulkan::VertexInputs<BufferRange> ranges{};
for (const auto& buffer : guest_buffers) {
if (buffer.GetSize() > 0) {
ranges.emplace_back(buffer.base_address, buffer.base_address + buffer.GetSize());
attributes.push_back({
.location = attrib.semantic,
.binding = attrib.semantic,
.format =
Vulkan::LiverpoolToVK::SurfaceFormat(buffer.GetDataFmt(), buffer.GetNumberFmt()),
.offset = 0,
});
bindings.push_back({
.binding = attrib.semantic,
.stride = buffer.GetStride(),
.inputRate = attrib.GetStepRate() == Shader::Gcn::VertexAttribute::InstanceIdType::None
? vk::VertexInputRate::eVertex
: vk::VertexInputRate::eInstance,
.divisor = 1,
});
}
if (ranges.empty()) {
return false;
}
// Merge connecting ranges together
Vulkan::VertexInputs<BufferRange> ranges_merged{};
if (!ranges.empty()) {
std::ranges::sort(ranges, [](const BufferRange& lhv, const BufferRange& rhv) {
return lhv.base_address < rhv.base_address;
});
boost::container::static_vector<BufferRange, NumVertexBuffers> ranges_merged{ranges[0]};
ranges_merged.emplace_back(ranges[0]);
for (auto range : ranges) {
auto& prev_range = ranges_merged.back();
if (prev_range.end_address < range.base_address) {
@ -178,8 +140,9 @@ bool BufferCache::BindVertexBuffers(
prev_range.end_address = std::max(prev_range.end_address, range.end_address);
}
}
}
// Map buffers
// Map buffers for merged ranges
for (auto& range : ranges_merged) {
const auto [buffer, offset] = ObtainBuffer(range.base_address, range.GetSize(), false);
range.vk_buffer = buffer->buffer;
@ -187,23 +150,33 @@ bool BufferCache::BindVertexBuffers(
}
// Bind vertex buffers
const size_t num_buffers = guest_buffers.size();
for (u32 i = 0; i < num_buffers; ++i) {
const auto& buffer = guest_buffers[i];
const auto host_buffer = std::ranges::find_if(ranges_merged, [&](const BufferRange& range) {
return (buffer.base_address >= range.base_address &&
buffer.base_address < range.end_address);
Vulkan::VertexInputs<vk::Buffer> host_buffers;
Vulkan::VertexInputs<vk::DeviceSize> host_offsets;
Vulkan::VertexInputs<vk::DeviceSize> host_sizes;
Vulkan::VertexInputs<vk::DeviceSize> host_strides;
const auto null_buffer =
instance.IsNullDescriptorSupported() ? VK_NULL_HANDLE : GetBuffer(NULL_BUFFER_ID).Handle();
for (const auto& buffer : guest_buffers) {
if (buffer.GetSize() > 0) {
const auto host_buffer_info =
std::ranges::find_if(ranges_merged, [&](const BufferRange& range) {
return buffer.base_address >= range.base_address &&
buffer.base_address < range.end_address;
});
ASSERT(host_buffer != ranges_merged.cend());
host_buffers[i] = host_buffer->vk_buffer;
host_offsets[i] = host_buffer->offset + buffer.base_address - host_buffer->base_address;
host_sizes[i] = buffer.GetSize();
host_strides[i] = buffer.GetStride();
ASSERT(host_buffer_info != ranges_merged.cend());
host_buffers.emplace_back(host_buffer_info->vk_buffer);
host_offsets.push_back(host_buffer_info->offset + buffer.base_address -
host_buffer_info->base_address);
} else {
host_buffers.emplace_back(null_buffer);
host_offsets.push_back(0);
}
host_sizes.push_back(buffer.GetSize());
host_strides.push_back(buffer.GetStride());
}
if (num_buffers > 0) {
const auto cmdbuf = scheduler.CommandBuffer();
const auto num_buffers = guest_buffers.size();
if (instance.IsVertexInputDynamicState()) {
cmdbuf.bindVertexBuffers(0, num_buffers, host_buffers.data(), host_offsets.data());
} else {
@ -212,9 +185,6 @@ bool BufferCache::BindVertexBuffers(
}
}
return has_step_rate;
}
void BufferCache::BindIndexBuffer(u32 index_offset) {
const auto& regs = liverpool->regs;

View File

@ -5,8 +5,6 @@
#include <shared_mutex>
#include <boost/container/small_vector.hpp>
#include <boost/icl/interval_map.hpp>
#include <tsl/robin_map.h>
#include "common/div_ceil.h"
#include "common/slot_vector.h"
#include "common/types.h"
@ -26,6 +24,10 @@ struct FetchShaderData;
struct Info;
} // namespace Shader
namespace Vulkan {
class GraphicsPipeline;
}
namespace VideoCore {
using BufferId = Common::SlotId;
@ -75,8 +77,7 @@ public:
void InvalidateMemory(VAddr device_addr, u64 size);
/// Binds host vertex buffers for the current draw.
bool BindVertexBuffers(const Shader::Info& vs_info,
const std::optional<Shader::Gcn::FetchShaderData>& fetch_shader);
void BindVertexBuffers(const Vulkan::GraphicsPipeline& pipeline);
/// Bind host index buffer for the current draw.
void BindIndexBuffer(u32 index_offset);

View File

@ -2,6 +2,7 @@
# SPDX-License-Identifier: GPL-2.0-or-later
set(SHADER_FILES
detilers/display_micro_64bpp.comp
detilers/macro_32bpp.comp
detilers/macro_64bpp.comp
detilers/macro_8bpp.comp

View File

@ -0,0 +1,60 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#version 450
layout (local_size_x = 64, local_size_y = 1, local_size_z = 1) in;
layout(std430, binding = 0) buffer input_buf {
uint in_data[];
};
layout(std430, binding = 1) buffer output_buf {
uint out_data[];
};
layout(push_constant) uniform image_info {
uint num_levels;
uint pitch;
uint height;
uint c0;
uint c1;
} info;
const uint lut_64bpp[16] = {
0x05040100, 0x0d0c0908,
0x07060302, 0x0f0e0b0a,
0x15141110, 0x1d1c1918,
0x17161312, 0x1f1e1b1a,
0x25242120, 0x2d2c2928,
0x27262322, 0x2f2e2b2a,
0x35343130, 0x3d3c3938,
0x37363332, 0x3f3e3b3a,
};
#define MICRO_TILE_DIM (8)
#define MICRO_TILE_SZ (512)
#define TEXELS_PER_ELEMENT (1)
#define BPP (64)
void main() {
uint x = gl_GlobalInvocationID.x % info.pitch;
uint y = (gl_GlobalInvocationID.x / info.pitch) % info.height;
uint z = gl_GlobalInvocationID.x / (info.pitch * info.height);
uint col = bitfieldExtract(x, 0, 3);
uint row = bitfieldExtract(y, 0, 3);
uint idx_dw = lut_64bpp[(col + row * MICRO_TILE_DIM) >> 2u];
uint byte_ofs = gl_LocalInvocationID.x & 3u;
uint idx = bitfieldExtract(idx_dw >> (8 * byte_ofs), 0, 8);
uint slice_offs = z * info.c1 * MICRO_TILE_SZ;
uint tile_row = y / MICRO_TILE_DIM;
uint tile_column = x / MICRO_TILE_DIM;
uint tile_offs = ((tile_row * info.c0) + tile_column) * MICRO_TILE_SZ;
uint offs = slice_offs + tile_offs + ((idx * BPP) / 8u);
uint p0 = in_data[(offs >> 2) + 0];
uint p1 = in_data[(offs >> 2) + 1];
out_data[2 * gl_GlobalInvocationID.x + 0] = p0;
out_data[2 * gl_GlobalInvocationID.x + 1] = p1;
}

View File

@ -59,8 +59,7 @@ ComputePipeline::ComputePipeline(const Instance& instance_, Scheduler& scheduler
for (const auto& image : info->images) {
bindings.push_back({
.binding = binding++,
.descriptorType = image.IsStorage(image.GetSharp(*info))
? vk::DescriptorType::eStorageImage
.descriptorType = image.is_written ? vk::DescriptorType::eStorageImage
: vk::DescriptorType::eSampledImage,
.descriptorCount = 1,
.stageFlags = vk::ShaderStageFlagBits::eCompute,

View File

@ -57,35 +57,11 @@ GraphicsPipeline::GraphicsPipeline(
pipeline_layout = std::move(layout);
SetObjectName(device, *pipeline_layout, "Graphics PipelineLayout {}", debug_str);
boost::container::static_vector<vk::VertexInputBindingDescription, 32> vertex_bindings;
boost::container::static_vector<vk::VertexInputAttributeDescription, 32> vertex_attributes;
if (fetch_shader && !instance.IsVertexInputDynamicState()) {
const auto& vs_info = GetStage(Shader::LogicalStage::Vertex);
for (const auto& attrib : fetch_shader->attributes) {
if (attrib.UsesStepRates()) {
// Skip attribute binding as the data will be pulled by shader
continue;
}
const auto buffer = attrib.GetSharp(vs_info);
if (buffer.GetSize() == 0) {
continue;
}
vertex_attributes.push_back({
.location = attrib.semantic,
.binding = attrib.semantic,
.format = LiverpoolToVK::SurfaceFormat(buffer.GetDataFmt(), buffer.GetNumberFmt()),
.offset = 0,
});
vertex_bindings.push_back({
.binding = attrib.semantic,
.stride = buffer.GetStride(),
.inputRate =
attrib.GetStepRate() == Shader::Gcn::VertexAttribute::InstanceIdType::None
? vk::VertexInputRate::eVertex
: vk::VertexInputRate::eInstance,
});
}
VertexInputs<vk::VertexInputAttributeDescription> vertex_attributes;
VertexInputs<vk::VertexInputBindingDescription> vertex_bindings;
VertexInputs<AmdGpu::Buffer> guest_buffers;
if (!instance.IsVertexInputDynamicState()) {
GetVertexInputs(vertex_attributes, vertex_bindings, guest_buffers);
}
const vk::PipelineVertexInputStateCreateInfo vertex_input_info = {
@ -161,7 +137,7 @@ GraphicsPipeline::GraphicsPipeline(
}
if (instance.IsVertexInputDynamicState()) {
dynamic_states.push_back(vk::DynamicState::eVertexInputEXT);
} else {
} else if (!vertex_bindings.empty()) {
dynamic_states.push_back(vk::DynamicState::eVertexInputBindingStrideEXT);
}
@ -329,6 +305,51 @@ GraphicsPipeline::GraphicsPipeline(
GraphicsPipeline::~GraphicsPipeline() = default;
template <typename Attribute, typename Binding>
void GraphicsPipeline::GetVertexInputs(VertexInputs<Attribute>& attributes,
VertexInputs<Binding>& bindings,
VertexInputs<AmdGpu::Buffer>& guest_buffers) const {
if (!fetch_shader || fetch_shader->attributes.empty()) {
return;
}
const auto& vs_info = GetStage(Shader::LogicalStage::Vertex);
for (const auto& attrib : fetch_shader->attributes) {
if (attrib.UsesStepRates()) {
// Skip attribute binding as the data will be pulled by shader.
continue;
}
const auto& buffer = attrib.GetSharp(vs_info);
attributes.push_back(Attribute{
.location = attrib.semantic,
.binding = attrib.semantic,
.format = LiverpoolToVK::SurfaceFormat(buffer.GetDataFmt(), buffer.GetNumberFmt()),
.offset = 0,
});
bindings.push_back(Binding{
.binding = attrib.semantic,
.stride = buffer.GetStride(),
.inputRate = attrib.GetStepRate() == Shader::Gcn::VertexAttribute::InstanceIdType::None
? vk::VertexInputRate::eVertex
: vk::VertexInputRate::eInstance,
});
if constexpr (std::is_same_v<Binding, vk::VertexInputBindingDescription2EXT>) {
bindings.back().divisor = 1;
}
guest_buffers.emplace_back(buffer);
}
}
// Declare templated GetVertexInputs for necessary types.
template void GraphicsPipeline::GetVertexInputs(
VertexInputs<vk::VertexInputAttributeDescription>& attributes,
VertexInputs<vk::VertexInputBindingDescription>& bindings,
VertexInputs<AmdGpu::Buffer>& guest_buffers) const;
template void GraphicsPipeline::GetVertexInputs(
VertexInputs<vk::VertexInputAttributeDescription2EXT>& attributes,
VertexInputs<vk::VertexInputBindingDescription2EXT>& bindings,
VertexInputs<AmdGpu::Buffer>& guest_buffers) const;
void GraphicsPipeline::BuildDescSetLayout() {
boost::container::small_vector<vk::DescriptorSetLayoutBinding, 32> bindings;
u32 binding{};
@ -367,8 +388,7 @@ void GraphicsPipeline::BuildDescSetLayout() {
for (const auto& image : stage->images) {
bindings.push_back({
.binding = binding++,
.descriptorType = image.IsStorage(image.GetSharp(*stage))
? vk::DescriptorType::eStorageImage
.descriptorType = image.is_written ? vk::DescriptorType::eStorageImage
: vk::DescriptorType::eSampledImage,
.descriptorCount = 1,
.stageFlags = gp_stage_flags,

View File

@ -3,6 +3,7 @@
#pragma once
#include <boost/container/static_vector.hpp>
#include <xxhash.h>
#include "common/types.h"
@ -27,6 +28,9 @@ class DescriptorHeap;
using Liverpool = AmdGpu::Liverpool;
template <typename T>
using VertexInputs = boost::container::static_vector<T, MaxVertexBufferCount>;
struct GraphicsPipelineKey {
std::array<size_t, MaxShaderStages> stage_hashes;
u32 num_color_attachments;
@ -100,6 +104,11 @@ public:
key.prim_type == AmdGpu::PrimitiveType::QuadList;
}
/// Gets the attributes and bindings for vertex inputs.
template <typename Attribute, typename Binding>
void GetVertexInputs(VertexInputs<Attribute>& attributes, VertexInputs<Binding>& bindings,
VertexInputs<AmdGpu::Buffer>& guest_buffers) const;
private:
void BuildDescSetLayout();

View File

@ -271,6 +271,7 @@ bool Instance::CreateDevice() {
maintenance5 = add_extension(VK_KHR_MAINTENANCE_5_EXTENSION_NAME);
legacy_vertex_attributes = add_extension(VK_EXT_LEGACY_VERTEX_ATTRIBUTES_EXTENSION_NAME);
image_load_store_lod = add_extension(VK_AMD_SHADER_IMAGE_LOAD_STORE_LOD_EXTENSION_NAME);
amd_gcn_shader = add_extension(VK_AMD_GCN_SHADER_EXTENSION_NAME);
// These extensions are promoted by Vulkan 1.3, but for greater compatibility we use Vulkan 1.2
// with extensions.

View File

@ -159,6 +159,11 @@ public:
return image_load_store_lod;
}
/// Returns true when VK_AMD_gcn_shader is supported.
bool IsAmdGcnShaderSupported() const {
return amd_gcn_shader;
}
/// Returns true when geometry shaders are supported by the device
bool IsGeometryStageSupported() const {
return features.geometryShader;
@ -334,6 +339,7 @@ private:
bool list_restart{};
bool legacy_vertex_attributes{};
bool image_load_store_lod{};
bool amd_gcn_shader{};
u64 min_imported_host_pointer_alignment{};
u32 subgroup_size{};
bool tooling_info{};

View File

@ -204,6 +204,7 @@ PipelineCache::PipelineCache(const Instance& instance_, Scheduler& scheduler_,
.support_explicit_workgroup_layout = true,
.support_legacy_vertex_attributes = instance_.IsLegacyVertexAttributesSupported(),
.supports_image_load_store_lod = instance_.IsImageLoadStoreLodSupported(),
.supports_native_cube_calc = instance_.IsAmdGcnShaderSupported(),
.needs_manual_interpolation = instance.IsFragmentShaderBarycentricSupported() &&
instance.GetDriverID() == vk::DriverId::eNvidiaProprietary,
.needs_lds_barriers = instance.GetDriverID() == vk::DriverId::eNvidiaProprietary ||
@ -419,17 +420,17 @@ bool PipelineCache::RefreshGraphicsKey() {
}
}
const auto vs_info = infos[static_cast<u32>(Shader::LogicalStage::Vertex)];
const auto* vs_info = infos[static_cast<u32>(Shader::LogicalStage::Vertex)];
if (vs_info && fetch_shader && !instance.IsVertexInputDynamicState()) {
// Without vertex input dynamic state, the pipeline needs to specialize on format.
// Stride will still be handled outside the pipeline using dynamic state.
u32 vertex_binding = 0;
for (const auto& attrib : fetch_shader->attributes) {
if (attrib.UsesStepRates()) {
// Skip attribute binding as the data will be pulled by shader.
continue;
}
const auto& buffer = attrib.GetSharp(*vs_info);
if (buffer.GetSize() == 0) {
continue;
}
ASSERT(vertex_binding < MaxVertexBufferCount);
key.vertex_buffer_formats[vertex_binding++] =
Vulkan::LiverpoolToVK::SurfaceFormat(buffer.GetDataFmt(), buffer.GetNumberFmt());

View File

@ -248,9 +248,7 @@ void Rasterizer::Draw(bool is_indexed, u32 index_offset) {
return;
}
const auto& vs_info = pipeline->GetStage(Shader::LogicalStage::Vertex);
const auto& fetch_shader = pipeline->GetFetchShader();
buffer_cache.BindVertexBuffers(vs_info, fetch_shader);
buffer_cache.BindVertexBuffers(*pipeline);
if (is_indexed) {
buffer_cache.BindIndexBuffer(index_offset);
}
@ -258,6 +256,8 @@ void Rasterizer::Draw(bool is_indexed, u32 index_offset) {
BeginRendering(*pipeline, state);
UpdateDynamicState(*pipeline);
const auto& vs_info = pipeline->GetStage(Shader::LogicalStage::Vertex);
const auto& fetch_shader = pipeline->GetFetchShader();
const auto [vertex_offset, instance_offset] = GetDrawOffsets(regs, vs_info, fetch_shader);
const auto cmdbuf = scheduler.CommandBuffer();
@ -292,9 +292,7 @@ void Rasterizer::DrawIndirect(bool is_indexed, VAddr arg_address, u32 offset, u3
return;
}
const auto& vs_info = pipeline->GetStage(Shader::LogicalStage::Vertex);
const auto& fetch_shader = pipeline->GetFetchShader();
buffer_cache.BindVertexBuffers(vs_info, fetch_shader);
buffer_cache.BindVertexBuffers(*pipeline);
if (is_indexed) {
buffer_cache.BindIndexBuffer(0);
}
@ -661,7 +659,7 @@ void Rasterizer::BindTextures(const Shader::Info& stage, Shader::Backend::Bindin
if (image->binding.is_bound) {
// The image is already bound. In case if it is about to be used as storage we need
// to force general layout on it.
image->binding.force_general |= image_desc.IsStorage(tsharp);
image->binding.force_general |= image_desc.is_written;
}
if (image->binding.is_target) {
// The image is already bound as target. Since we read and output to it need to force

View File

@ -153,13 +153,7 @@ Image::Image(const Vulkan::Instance& instance_, Vulkan::Scheduler& scheduler_,
// the texture cache should re-create the resource with the usage requested
vk::ImageCreateFlags flags{vk::ImageCreateFlagBits::eMutableFormat |
vk::ImageCreateFlagBits::eExtendedUsage};
const bool can_be_cube =
(info.type == vk::ImageType::e2D) &&
((info.props.is_pow2 ? (info.resources.layers % 8) : (info.resources.layers % 6)) == 0) &&
(info.size.width == info.size.height);
if (info.props.is_cube || can_be_cube) {
flags |= vk::ImageCreateFlagBits::eCubeCompatible;
} else if (info.props.is_volume) {
if (info.props.is_volume) {
flags |= vk::ImageCreateFlagBits::e2DArrayCompatible;
}

View File

@ -37,7 +37,6 @@ static vk::ImageType ConvertImageType(AmdGpu::ImageType type) noexcept {
return vk::ImageType::e1D;
case AmdGpu::ImageType::Color2D:
case AmdGpu::ImageType::Color2DMsaa:
case AmdGpu::ImageType::Cube:
case AmdGpu::ImageType::Color2DArray:
return vk::ImageType::e2D;
case AmdGpu::ImageType::Color3D:
@ -130,7 +129,6 @@ ImageInfo::ImageInfo(const AmdGpu::Image& image, const Shader::ImageResource& de
}
type = ConvertImageType(image.GetType());
props.is_tiled = image.IsTiled();
props.is_cube = image.GetType() == AmdGpu::ImageType::Cube;
props.is_volume = image.GetType() == AmdGpu::ImageType::Color3D;
props.is_pow2 = image.pow2pad;
props.is_block = IsBlockCoded();
@ -139,7 +137,7 @@ ImageInfo::ImageInfo(const AmdGpu::Image& image, const Shader::ImageResource& de
size.depth = props.is_volume ? image.depth + 1 : 1;
pitch = image.Pitch();
resources.levels = image.NumLevels();
resources.layers = image.NumLayers(desc.is_array);
resources.layers = image.NumLayers();
num_samples = image.NumSamples();
num_bits = NumBits(image.GetDataFmt());
@ -184,6 +182,7 @@ void ImageInfo::UpdateSize() {
case AmdGpu::TilingMode::Texture_Volume:
mip_d += (-mip_d) & 3u;
[[fallthrough]];
case AmdGpu::TilingMode::Display_MicroTiled:
case AmdGpu::TilingMode::Texture_MicroTiled: {
std::tie(mip_info.pitch, mip_info.size) =
ImageSizeMicroTiled(mip_w, mip_h, bpp, num_samples);

View File

@ -61,7 +61,6 @@ struct ImageInfo {
} meta_info{};
struct {
u32 is_cube : 1;
u32 is_volume : 1;
u32 is_tiled : 1;
u32 is_pow2 : 1;

View File

@ -20,8 +20,6 @@ vk::ImageViewType ConvertImageViewType(AmdGpu::ImageType type) {
case AmdGpu::ImageType::Color2D:
case AmdGpu::ImageType::Color2DMsaa:
return vk::ImageViewType::e2D;
case AmdGpu::ImageType::Cube:
return vk::ImageViewType::eCube;
case AmdGpu::ImageType::Color2DArray:
return vk::ImageViewType::e2DArray;
case AmdGpu::ImageType::Color3D:
@ -32,7 +30,7 @@ vk::ImageViewType ConvertImageViewType(AmdGpu::ImageType type) {
}
ImageViewInfo::ImageViewInfo(const AmdGpu::Image& image, const Shader::ImageResource& desc) noexcept
: is_storage{desc.IsStorage(image)} {
: is_storage{desc.is_written} {
const auto dfmt = image.GetDataFmt();
auto nfmt = image.GetNumberFmt();
if (is_storage && nfmt == AmdGpu::NumberFormat::Srgb) {
@ -42,30 +40,12 @@ ImageViewInfo::ImageViewInfo(const AmdGpu::Image& image, const Shader::ImageReso
if (desc.is_depth) {
format = Vulkan::LiverpoolToVK::PromoteFormatToDepth(format);
}
range.base.level = image.base_level;
range.base.layer = image.base_array;
if (image.GetType() == AmdGpu::ImageType::Color2DMsaa ||
image.GetType() == AmdGpu::ImageType::Color2DMsaaArray) {
range.extent.levels = 1;
} else {
range.extent.levels = image.last_level - image.base_level + 1;
}
range.extent.layers = image.last_array - image.base_array + 1;
type = ConvertImageViewType(image.GetBoundType());
// Adjust view type for arrays
if (type == vk::ImageViewType::eCube) {
if (desc.is_array) {
type = vk::ImageViewType::eCubeArray;
} else {
// Some games try to bind an array of cubemaps while shader reads only single one.
range.extent.layers = std::min(range.extent.layers, 6u);
}
}
if (type == vk::ImageViewType::e3D && range.extent.layers > 1) {
// Some games pass incorrect layer count for 3D textures so we need to fixup it.
range.extent.layers = 1;
}
range.extent.levels = image.NumViewLevels(desc.is_array);
range.extent.layers = image.NumViewLayers(desc.is_array);
type = ConvertImageViewType(image.GetViewType(desc.is_array));
if (!is_storage) {
mapping = Vulkan::LiverpoolToVK::ComponentMapping(image.DstSelect());

View File

@ -469,9 +469,6 @@ ImageView& TextureCache::FindDepthTarget(BaseDesc& desc) {
}
void TextureCache::RefreshImage(Image& image, Vulkan::Scheduler* custom_scheduler /*= nullptr*/) {
RENDERER_TRACE;
TRACE_HINT(fmt::format("{:x}:{:x}", image.info.guest_address, image.info.guest_size));
if (False(image.flags & ImageFlagBits::Dirty)) {
return;
}
@ -480,6 +477,9 @@ void TextureCache::RefreshImage(Image& image, Vulkan::Scheduler* custom_schedule
return;
}
RENDERER_TRACE;
TRACE_HINT(fmt::format("{:x}:{:x}", image.info.guest_address, image.info.guest_size));
if (True(image.flags & ImageFlagBits::MaybeCpuDirty) &&
False(image.flags & ImageFlagBits::CpuDirty)) {
// The image size should be less than page size to be considered MaybeCpuDirty

View File

@ -65,7 +65,7 @@ public:
struct TextureDesc : public BaseDesc {
TextureDesc() = default;
TextureDesc(const AmdGpu::Image& image, const Shader::ImageResource& desc)
: BaseDesc{desc.IsStorage(image) ? BindingType::Storage : BindingType::Texture,
: BaseDesc{desc.is_written ? BindingType::Storage : BindingType::Texture,
ImageInfo{image, desc}, ImageViewInfo{image, desc}} {}
};

View File

@ -8,6 +8,7 @@
#include "video_core/texture_cache/image_view.h"
#include "video_core/texture_cache/tile_manager.h"
#include "video_core/host_shaders/detilers/display_micro_64bpp_comp.h"
#include "video_core/host_shaders/detilers/macro_32bpp_comp.h"
#include "video_core/host_shaders/detilers/macro_64bpp_comp.h"
#include "video_core/host_shaders/detilers/macro_8bpp_comp.h"
@ -53,6 +54,14 @@ const DetilerContext* TileManager::GetDetiler(const ImageInfo& info) const {
return nullptr;
}
break;
case AmdGpu::TilingMode::Display_MicroTiled:
switch (bpp) {
case 64:
return &detilers[DetilerType::Display_Micro64];
default:
return nullptr;
}
break;
default:
return nullptr;
}
@ -72,6 +81,7 @@ TileManager::TileManager(const Vulkan::Instance& instance, Vulkan::Scheduler& sc
HostShaders::MICRO_32BPP_COMP, HostShaders::MICRO_64BPP_COMP,
HostShaders::MICRO_128BPP_COMP, HostShaders::MACRO_8BPP_COMP,
HostShaders::MACRO_32BPP_COMP, HostShaders::MACRO_64BPP_COMP,
HostShaders::DISPLAY_MICRO_64BPP_COMP,
};
boost::container::static_vector<vk::DescriptorSetLayoutBinding, 2> bindings{
@ -258,7 +268,8 @@ std::pair<vk::Buffer, u32> TileManager::TryDetile(vk::Buffer in_buffer, u32 in_o
params.num_levels = info.resources.levels;
params.pitch0 = info.pitch >> (info.props.is_block ? 2u : 0u);
params.height = info.size.height;
if (info.tiling_mode == AmdGpu::TilingMode::Texture_Volume) {
if (info.tiling_mode == AmdGpu::TilingMode::Texture_Volume ||
info.tiling_mode == AmdGpu::TilingMode::Display_MicroTiled) {
ASSERT(info.resources.levels == 1);
const auto tiles_per_row = info.pitch / 8u;
const auto tiles_per_slice = tiles_per_row * ((info.size.height + 7u) / 8u);

View File

@ -22,6 +22,8 @@ enum DetilerType : u32 {
Macro32,
Macro64,
Display_Micro64,
Max
};