Merge branch 'shadps4-emu:main' into Fix_PromoteFormatToDepth

This commit is contained in:
¥IGA 2025-01-03 17:54:35 +01:00 committed by GitHub
commit 7d02688f96
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
82 changed files with 1795 additions and 955 deletions

View File

@ -13,7 +13,11 @@ body:
**Please do not make support requests on GitHub. Our issue tracker is for tracking bugs and feature requests only.
If you have a support request or are unsure about the nature of your issue please contact us on [discord](https://discord.gg/bFJxfftGW6).**
You can also check the [Game Compatibility Repository](https://github.com/shadps4-emu/shadps4-game-compatibility) for the information about the status of the game.
This repository does not provide support for modded games. You should perform and test a clean game installation before submitting an issue.
This repository does not provide support for game patches. If you are having issues with patches please refer to [Cheats and Patches Repository](https://github.com/shadps4-emu/ps4_cheats).
Before submitting an issue please check [Game Compatibility Repository](https://github.com/shadps4-emu/shadps4-game-compatibility) for the information about the status of the game.
Please make an effort to make sure your issue isn't already reported.
@ -21,15 +25,15 @@ body:
- type: checkboxes
id: checklist
attributes:
label: Checklist
label: Checklist (we expect you to perform these steps before opening the issue)
options:
- label: I have searched for a similar issue in this repository and did not find one.
required: true
- label: I am using an official build obtained from [releases](https://github.com/shadps4-emu/shadPS4/releases) or updated one of those builds using its in-app updater.
required: true
- label: I have re-dumped the game and performed a clean install without mods.
- label: I have re-dumped the game and performed a clean install without mods and the issue is still present.
required: true
- label: I have disabled all patches and cheats.
- label: I have disabled all patches and cheats and the issue is still present.
required: true
- label: I have all the required [system modules](https://github.com/shadps4-emu/shadps4-game-compatibility?tab=readme-ov-file#informations) installed.
required: true

View File

@ -67,6 +67,7 @@ static int cursorHideTimeout = 5; // 5 seconds (default)
static bool separateupdatefolder = false;
static bool compatibilityData = false;
static bool checkCompatibilityOnStartup = false;
static std::string trophyKey;
// Gui
std::vector<std::filesystem::path> settings_install_dirs = {};
@ -91,6 +92,14 @@ std::string emulator_language = "en";
// Language
u32 m_language = 1; // english
std::string getTrophyKey() {
return trophyKey;
}
void setTrophyKey(std::string key) {
trophyKey = key;
}
bool isNeoMode() {
return isNeo;
}
@ -652,6 +661,11 @@ void load(const std::filesystem::path& path) {
m_language = toml::find_or<int>(settings, "consoleLanguage", 1);
}
if (data.contains("Keys")) {
const toml::value& keys = data.at("Keys");
trophyKey = toml::find_or<std::string>(keys, "TrophyKey", "");
}
}
void save(const std::filesystem::path& path) {
@ -712,6 +726,8 @@ void save(const std::filesystem::path& path) {
data["Debug"]["DebugDump"] = isDebugDump;
data["Debug"]["CollectShader"] = isShaderDebug;
data["Keys"]["TrophyKey"] = trophyKey;
std::vector<std::string> install_dirs;
for (const auto& dirString : settings_install_dirs) {
install_dirs.emplace_back(std::string{fmt::UTF(dirString.u8string()).data});

View File

@ -15,6 +15,9 @@ void load(const std::filesystem::path& path);
void save(const std::filesystem::path& path);
void saveMainWindow(const std::filesystem::path& path);
std::string getTrophyKey();
void setTrophyKey(std::string key);
bool isNeoMode();
bool isFullscreenMode();
bool getPlayBGM();

View File

@ -2,6 +2,7 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#include <array>
#include "crypto.h"
CryptoPP::RSA::PrivateKey Crypto::key_pkg_derived_key3_keyset_init() {
@ -137,17 +138,20 @@ void Crypto::aesCbcCfb128DecryptEntry(std::span<const CryptoPP::byte, 32> ivkey,
}
}
void Crypto::decryptEFSM(std::span<CryptoPP::byte, 16> NPcommID,
void Crypto::decryptEFSM(std::span<CryptoPP::byte, 16> trophyKey,
std::span<CryptoPP::byte, 16> NPcommID,
std::span<CryptoPP::byte, 16> efsmIv, std::span<CryptoPP::byte> ciphertext,
std::span<CryptoPP::byte> decrypted) {
std::vector<CryptoPP::byte> TrophyIV = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
// step 1: Encrypt NPcommID
CryptoPP::CBC_Mode<CryptoPP::AES>::Encryption encrypt;
std::vector<CryptoPP::byte> trophyIv(16, 0);
std::vector<CryptoPP::byte> trpKey(16);
encrypt.SetKeyWithIV(trophyKey.data(), trophyKey.size(), trophyIv.data());
encrypt.ProcessData(trpKey.data(), NPcommID.data(), 16);
// step 2: decrypt efsm.
CryptoPP::CBC_Mode<CryptoPP::AES>::Decryption decrypt;
decrypt.SetKeyWithIV(trpKey.data(), trpKey.size(), efsmIv.data());

View File

@ -32,7 +32,8 @@ public:
void aesCbcCfb128DecryptEntry(std::span<const CryptoPP::byte, 32> ivkey,
std::span<CryptoPP::byte> ciphertext,
std::span<CryptoPP::byte> decrypted);
void decryptEFSM(std::span<CryptoPP::byte, 16>, std::span<CryptoPP::byte, 16> efsmIv,
void decryptEFSM(std::span<CryptoPP::byte, 16> trophyKey,
std::span<CryptoPP::byte, 16> NPcommID, std::span<CryptoPP::byte, 16> efsmIv,
std::span<CryptoPP::byte> ciphertext, std::span<CryptoPP::byte> decrypted);
void PfsGenCryptoKey(std::span<const CryptoPP::byte, 32> ekpfs,
std::span<const CryptoPP::byte, 16> seed,

View File

@ -66,7 +66,7 @@ void RegPopup::DrawColorBuffer(const AmdGpu::Liverpool::ColorBuffer& buffer) {
"GetColorSliceSize()", buffer.GetColorSliceSize(),
"GetTilingMode()", buffer.GetTilingMode(),
"IsTiled()", buffer.IsTiled(),
"NumFormat()", buffer.NumFormat()
"NumFormat()", buffer.GetNumberFmt()
);
// clang-format on

View File

@ -1,6 +1,7 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "common/config.h"
#include "common/logging/log.h"
#include "common/path_util.h"
#include "trp.h"
@ -33,12 +34,29 @@ static void removePadding(std::vector<u8>& vec) {
}
}
static void hexToBytes(const char* hex, unsigned char* dst) {
for (size_t i = 0; hex[i] != 0; i++) {
const unsigned char value = (hex[i] < 0x3A) ? (hex[i] - 0x30) : (hex[i] - 0x37);
dst[i / 2] |= ((i % 2) == 0) ? (value << 4) : (value);
}
}
bool TRP::Extract(const std::filesystem::path& trophyPath, const std::string titleId) {
std::filesystem::path gameSysDir = trophyPath / "sce_sys/trophy/";
if (!std::filesystem::exists(gameSysDir)) {
LOG_CRITICAL(Common_Filesystem, "Game sce_sys directory doesn't exist");
return false;
}
const auto user_key_str = Config::getTrophyKey();
if (user_key_str.size() != 32) {
LOG_CRITICAL(Common_Filesystem, "Trophy decryption key is not specified");
return false;
}
std::array<CryptoPP::byte, 16> user_key{};
hexToBytes(user_key_str.c_str(), user_key.data());
for (int index = 0; const auto& it : std::filesystem::directory_iterator(gameSysDir)) {
if (it.is_regular_file()) {
GetNPcommID(trophyPath, index);
@ -97,7 +115,7 @@ bool TRP::Extract(const std::filesystem::path& trophyPath, const std::string tit
return false;
}
file.Read(ESFM);
crypto.decryptEFSM(np_comm_id, esfmIv, ESFM, XML); // decrypt
crypto.decryptEFSM(user_key, np_comm_id, esfmIv, ESFM, XML); // decrypt
removePadding(XML);
std::string xml_name = entry.entry_name;
size_t pos = xml_name.find("ESFM");

View File

@ -3,13 +3,13 @@
#include <memory>
#include <mutex>
#include <shared_mutex>
#include <stop_token>
#include <thread>
#include <magic_enum/magic_enum.hpp>
#include "common/assert.h"
#include "common/config.h"
#include "common/logging/log.h"
#include "common/polyfill_thread.h"
#include "common/thread.h"
#include "core/libraries/audio/audioout.h"
#include "core/libraries/audio/audioout_backend.h"
@ -18,7 +18,7 @@
namespace Libraries::AudioOut {
std::shared_mutex ports_mutex;
std::mutex port_open_mutex{};
std::array<PortOut, SCE_AUDIO_OUT_NUM_PORTS> ports_out{};
static std::unique_ptr<AudioOutBackend> audio;
@ -93,17 +93,20 @@ int PS4_SYSV_ABI sceAudioOutClose(s32 handle) {
return ORBIS_AUDIO_OUT_ERROR_INVALID_PORT;
}
std::scoped_lock lock(ports_mutex);
std::unique_lock open_lock{port_open_mutex};
auto& port = ports_out.at(handle - 1);
if (!port.impl) {
return ORBIS_AUDIO_OUT_ERROR_INVALID_PORT;
{
std::unique_lock lock{port.mutex};
if (!port.IsOpen()) {
return ORBIS_AUDIO_OUT_ERROR_INVALID_PORT;
}
std::free(port.output_buffer);
port.output_buffer = nullptr;
port.output_ready = false;
port.impl = nullptr;
}
// Stop outside of port lock scope to prevent deadlocks.
port.output_thread.Stop();
std::free(port.output_buffer);
port.output_buffer = nullptr;
port.output_ready = false;
port.impl = nullptr;
return ORBIS_OK;
}
@ -172,35 +175,34 @@ int PS4_SYSV_ABI sceAudioOutGetPortState(s32 handle, OrbisAudioOutPortState* sta
return ORBIS_AUDIO_OUT_ERROR_INVALID_PORT;
}
std::scoped_lock lock(ports_mutex);
const auto& port = ports_out.at(handle - 1);
if (!port.impl) {
return ORBIS_AUDIO_OUT_ERROR_INVALID_PORT;
auto& port = ports_out.at(handle - 1);
{
std::unique_lock lock{port.mutex};
if (!port.IsOpen()) {
return ORBIS_AUDIO_OUT_ERROR_INVALID_PORT;
}
switch (port.type) {
case OrbisAudioOutPort::Main:
case OrbisAudioOutPort::Bgm:
case OrbisAudioOutPort::Voice:
state->output = 1;
state->channel = port.format_info.num_channels > 2 ? 2 : port.format_info.num_channels;
break;
case OrbisAudioOutPort::Personal:
case OrbisAudioOutPort::Padspk:
state->output = 4;
state->channel = 1;
break;
case OrbisAudioOutPort::Aux:
state->output = 0;
state->channel = 0;
break;
default:
UNREACHABLE();
}
state->rerouteCounter = 0;
state->volume = 127;
}
state->rerouteCounter = 0;
state->volume = 127;
switch (port.type) {
case OrbisAudioOutPort::Main:
case OrbisAudioOutPort::Bgm:
case OrbisAudioOutPort::Voice:
state->output = 1;
state->channel = port.format_info.num_channels > 2 ? 2 : port.format_info.num_channels;
break;
case OrbisAudioOutPort::Personal:
case OrbisAudioOutPort::Padspk:
state->output = 4;
state->channel = 1;
break;
case OrbisAudioOutPort::Aux:
state->output = 0;
state->channel = 0;
break;
default:
UNREACHABLE();
}
return ORBIS_OK;
}
@ -279,15 +281,16 @@ static void AudioOutputThread(PortOut* port, const std::stop_token& stop) {
while (true) {
timer.Start();
{
std::unique_lock lock{port->output_mutex};
Common::CondvarWait(port->output_cv, lock, stop, [&] { return port->output_ready; });
if (stop.stop_requested()) {
break;
std::unique_lock lock{port->mutex};
if (port->output_cv.wait(lock, stop, [&] { return port->output_ready; })) {
port->impl->Output(port->output_buffer);
port->output_ready = false;
}
port->impl->Output(port->output_buffer);
port->output_ready = false;
}
port->output_cv.notify_one();
if (stop.stop_requested()) {
break;
}
timer.End();
}
}
@ -332,27 +335,30 @@ s32 PS4_SYSV_ABI sceAudioOutOpen(UserService::OrbisUserServiceUserId user_id,
return ORBIS_AUDIO_OUT_ERROR_INVALID_FORMAT;
}
std::scoped_lock lock{ports_mutex};
std::unique_lock open_lock{port_open_mutex};
const auto port =
std::ranges::find_if(ports_out, [&](const PortOut& p) { return p.impl == nullptr; });
std::ranges::find_if(ports_out, [&](const PortOut& p) { return !p.IsOpen(); });
if (port == ports_out.end()) {
LOG_ERROR(Lib_AudioOut, "Audio ports are full");
return ORBIS_AUDIO_OUT_ERROR_PORT_FULL;
}
port->type = port_type;
port->format_info = GetFormatInfo(format);
port->sample_rate = sample_rate;
port->buffer_frames = length;
port->volume.fill(SCE_AUDIO_OUT_VOLUME_0DB);
{
std::unique_lock port_lock(port->mutex);
port->impl = audio->Open(*port);
port->type = port_type;
port->format_info = GetFormatInfo(format);
port->sample_rate = sample_rate;
port->buffer_frames = length;
port->volume.fill(SCE_AUDIO_OUT_VOLUME_0DB);
port->output_buffer = std::malloc(port->BufferSize());
port->output_ready = false;
port->output_thread.Run(
[port](const std::stop_token& stop) { AudioOutputThread(&*port, stop); });
port->impl = audio->Open(*port);
port->output_buffer = std::malloc(port->BufferSize());
port->output_ready = false;
port->output_thread.Run(
[port](const std::stop_token& stop) { AudioOutputThread(&*port, stop); });
}
return std::distance(ports_out.begin(), port) + 1;
}
@ -367,14 +373,13 @@ s32 PS4_SYSV_ABI sceAudioOutOutput(s32 handle, void* ptr) {
}
auto& port = ports_out.at(handle - 1);
if (!port.impl) {
return ORBIS_AUDIO_OUT_ERROR_INVALID_PORT;
}
{
std::unique_lock lock{port.output_mutex};
std::unique_lock lock{port.mutex};
if (!port.IsOpen()) {
return ORBIS_AUDIO_OUT_ERROR_INVALID_PORT;
}
port.output_cv.wait(lock, [&] { return !port.output_ready; });
if (ptr != nullptr) {
if (ptr != nullptr && port.IsOpen()) {
std::memcpy(port.output_buffer, ptr, port.BufferSize());
port.output_ready = true;
}
@ -488,19 +493,19 @@ s32 PS4_SYSV_ABI sceAudioOutSetVolume(s32 handle, s32 flag, s32* vol) {
return ORBIS_AUDIO_OUT_ERROR_INVALID_PORT;
}
std::scoped_lock lock(ports_mutex);
auto& port = ports_out.at(handle - 1);
if (!port.impl) {
return ORBIS_AUDIO_OUT_ERROR_INVALID_PORT;
}
for (int i = 0; i < port.format_info.num_channels; i++, flag >>= 1u) {
if (flag & 0x1u) {
port.volume[i] = vol[i];
{
std::unique_lock lock{port.mutex};
if (!port.IsOpen()) {
return ORBIS_AUDIO_OUT_ERROR_INVALID_PORT;
}
for (int i = 0; i < port.format_info.num_channels; i++, flag >>= 1u) {
if (flag & 0x1u) {
port.volume[i] = vol[i];
}
}
port.impl->SetVolume(port.volume);
}
port.impl->SetVolume(port.volume);
return ORBIS_OK;
}

View File

@ -3,7 +3,9 @@
#pragma once
#include <condition_variable>
#include <memory>
#include <mutex>
#include "common/bit_field.h"
#include "core/libraries/kernel/threads.h"
@ -74,10 +76,10 @@ struct AudioFormatInfo {
};
struct PortOut {
std::mutex mutex;
std::unique_ptr<PortBackend> impl{};
void* output_buffer;
std::mutex output_mutex;
std::condition_variable_any output_cv;
bool output_ready;
Kernel::Thread output_thread{};
@ -88,6 +90,10 @@ struct PortOut {
u32 buffer_frames;
std::array<s32, 8> volume;
[[nodiscard]] bool IsOpen() const {
return impl != nullptr;
}
[[nodiscard]] u32 BufferSize() const {
return buffer_frames * format_info.FrameSize();
}

View File

@ -14,7 +14,7 @@ namespace Libraries::AudioOut {
class SDLPortBackend : public PortBackend {
public:
explicit SDLPortBackend(const PortOut& port)
: frame_size(port.format_info.FrameSize()), buffer_size(port.BufferSize()) {
: frame_size(port.format_info.FrameSize()), guest_buffer_size(port.BufferSize()) {
// We want the latency for delivering frames out to be as small as possible,
// so set the sample frames hint to the number of frames per buffer.
const auto samples_num_str = std::to_string(port.buffer_frames);
@ -33,7 +33,7 @@ public:
LOG_ERROR(Lib_AudioOut, "Failed to create SDL audio stream: {}", SDL_GetError());
return;
}
queue_threshold = CalculateQueueThreshold();
CalculateQueueThreshold();
if (!SDL_SetAudioStreamInputChannelMap(stream, port.format_info.channel_layout.data(),
port.format_info.num_channels)) {
LOG_ERROR(Lib_AudioOut, "Failed to configure SDL audio stream channel map: {}",
@ -71,9 +71,9 @@ public:
queue_threshold);
SDL_ClearAudioStream(stream);
// Recalculate the threshold in case this happened because of a device change.
queue_threshold = CalculateQueueThreshold();
CalculateQueueThreshold();
}
if (!SDL_PutAudioStreamData(stream, ptr, static_cast<int>(buffer_size))) {
if (!SDL_PutAudioStreamData(stream, ptr, static_cast<int>(guest_buffer_size))) {
LOG_ERROR(Lib_AudioOut, "Failed to output to SDL audio stream: {}", SDL_GetError());
}
}
@ -91,7 +91,7 @@ public:
}
private:
[[nodiscard]] u32 CalculateQueueThreshold() const {
void CalculateQueueThreshold() {
SDL_AudioSpec discard;
int sdl_buffer_frames;
if (!SDL_GetAudioDeviceFormat(SDL_GetAudioStreamDevice(stream), &discard,
@ -100,13 +100,22 @@ private:
SDL_GetError());
sdl_buffer_frames = 0;
}
return std::max<u32>(buffer_size, sdl_buffer_frames * frame_size) * 4;
const auto sdl_buffer_size = sdl_buffer_frames * frame_size;
const auto new_threshold = std::max(guest_buffer_size, sdl_buffer_size) * 4;
if (host_buffer_size != sdl_buffer_size || queue_threshold != new_threshold) {
host_buffer_size = sdl_buffer_size;
queue_threshold = new_threshold;
LOG_INFO(Lib_AudioOut,
"SDL audio buffers: guest = {} bytes, host = {} bytes, threshold = {} bytes",
guest_buffer_size, host_buffer_size, queue_threshold);
}
}
u32 frame_size;
u32 buffer_size;
u32 queue_threshold;
SDL_AudioStream* stream;
u32 guest_buffer_size;
u32 host_buffer_size{};
u32 queue_threshold{};
SDL_AudioStream* stream{};
};
std::unique_ptr<PortBackend> SDLAudioOut::Open(PortOut& port) {

View File

@ -505,6 +505,41 @@ int PS4_SYSV_ABI posix_munmap(void* addr, size_t len) {
return result;
}
static constexpr int MAX_PRT_APERTURES = 3;
static constexpr VAddr PRT_AREA_START_ADDR = 0x1000000000;
static constexpr size_t PRT_AREA_SIZE = 0xec00000000;
static std::array<std::pair<VAddr, size_t>, MAX_PRT_APERTURES> PrtApertures{};
int PS4_SYSV_ABI sceKernelSetPrtAperture(int id, VAddr address, size_t size) {
if (id < 0 || id >= MAX_PRT_APERTURES) {
return ORBIS_KERNEL_ERROR_EINVAL;
}
if (address < PRT_AREA_START_ADDR || address + size > PRT_AREA_START_ADDR + PRT_AREA_SIZE) {
return ORBIS_KERNEL_ERROR_EINVAL;
}
if (address % 4096 != 0) {
return ORBIS_KERNEL_ERROR_EINVAL;
}
LOG_WARNING(Kernel_Vmm,
"PRT aperture id = {}, address = {:#x}, size = {:#x} is set but not used", id,
address, size);
PrtApertures[id] = {address, size};
return ORBIS_OK;
}
int PS4_SYSV_ABI sceKernelGetPrtAperture(int id, VAddr* address, size_t* size) {
if (id < 0 || id >= MAX_PRT_APERTURES) {
return ORBIS_KERNEL_ERROR_EINVAL;
}
std::tie(*address, *size) = PrtApertures[id];
return ORBIS_OK;
}
void RegisterMemory(Core::Loader::SymbolsResolver* sym) {
LIB_FUNCTION("rTXw65xmLIA", "libkernel", 1, "libkernel", 1, 1, sceKernelAllocateDirectMemory);
LIB_FUNCTION("B+vc2AO2Zrc", "libkernel", 1, "libkernel", 1, 1,
@ -551,6 +586,10 @@ void RegisterMemory(Core::Loader::SymbolsResolver* sym) {
LIB_FUNCTION("BPE9s9vQQXo", "libScePosix", 1, "libkernel", 1, 1, posix_mmap);
LIB_FUNCTION("UqDGjXA5yUM", "libkernel", 1, "libkernel", 1, 1, posix_munmap);
LIB_FUNCTION("UqDGjXA5yUM", "libScePosix", 1, "libkernel", 1, 1, posix_munmap);
// PRT memory management
LIB_FUNCTION("BohYr-F7-is", "libkernel", 1, "libkernel", 1, 1, sceKernelSetPrtAperture);
LIB_FUNCTION("L0v2Go5jOuM", "libkernel", 1, "libkernel", 1, 1, sceKernelGetPrtAperture);
}
} // namespace Libraries::Kernel

View File

@ -55,6 +55,9 @@ public:
stop.request_stop();
Join();
}
thread = nullptr;
func = nullptr;
stop = std::stop_source{};
}
static void* PS4_SYSV_ABI RunWrapper(void* arg) {

View File

@ -47,6 +47,8 @@
#include "core/libraries/videodec/videodec.h"
#include "core/libraries/videodec/videodec2.h"
#include "core/libraries/videoout/video_out.h"
#include "fiber/fiber.h"
#include "jpeg/jpegenc.h"
namespace Libraries {
@ -93,6 +95,8 @@ void InitHLELibs(Core::Loader::SymbolsResolver* sym) {
Libraries::Videodec::RegisterlibSceVideodec(sym);
Libraries::RazorCpu::RegisterlibSceRazorCpu(sym);
Libraries::Move::RegisterlibSceMove(sym);
Libraries::Fiber::RegisterlibSceFiber(sym);
Libraries::JpegEnc::RegisterlibSceJpegEnc(sym);
}
} // namespace Libraries

View File

@ -972,11 +972,8 @@ int PS4_SYSV_ABI sceNpGetGamePresenceStatusA() {
}
int PS4_SYSV_ABI sceNpGetNpId(OrbisUserServiceUserId user_id, OrbisNpId* np_id) {
LOG_INFO(Lib_NpManager, "user_id {}", user_id);
const auto name = Config::getUserName();
std::memset(np_id, 0, sizeof(OrbisNpId));
name.copy(np_id->handle.data, sizeof(np_id->handle.data));
return ORBIS_OK;
LOG_DEBUG(Lib_NpManager, "user_id {}", user_id);
return ORBIS_NP_ERROR_SIGNED_OUT;
}
int PS4_SYSV_ABI sceNpGetNpReachabilityState() {
@ -986,10 +983,7 @@ int PS4_SYSV_ABI sceNpGetNpReachabilityState() {
int PS4_SYSV_ABI sceNpGetOnlineId(s32 user_id, OrbisNpOnlineId* online_id) {
LOG_DEBUG(Lib_NpManager, "user_id {}", user_id);
const auto name = Config::getUserName();
std::memset(online_id, 0, sizeof(OrbisNpOnlineId));
name.copy(online_id->data, sizeof(online_id->data));
return ORBIS_OK;
return ORBIS_NP_ERROR_SIGNED_OUT;
}
int PS4_SYSV_ABI sceNpGetParentalControlInfo() {

View File

@ -286,6 +286,7 @@ int PS4_SYSV_ABI scePadOutputReport() {
}
int PS4_SYSV_ABI scePadRead(s32 handle, OrbisPadData* pData, s32 num) {
LOG_TRACE(Lib_Pad, "called");
int connected_count = 0;
bool connected = false;
Input::State states[64];
@ -304,16 +305,15 @@ int PS4_SYSV_ABI scePadRead(s32 handle, OrbisPadData* pData, s32 num) {
pData[i].rightStick.y = states[i].axes[static_cast<int>(Input::Axis::RightY)];
pData[i].analogButtons.l2 = states[i].axes[static_cast<int>(Input::Axis::TriggerLeft)];
pData[i].analogButtons.r2 = states[i].axes[static_cast<int>(Input::Axis::TriggerRight)];
pData[i].orientation.x = 0.0f;
pData[i].orientation.y = 0.0f;
pData[i].orientation.z = 0.0f;
pData[i].orientation.w = 1.0f;
pData[i].acceleration.x = 0.0f;
pData[i].acceleration.y = 0.0f;
pData[i].acceleration.z = 0.0f;
pData[i].angularVelocity.x = 0.0f;
pData[i].angularVelocity.y = 0.0f;
pData[i].angularVelocity.z = 0.0f;
pData[i].acceleration.x = states[i].acceleration.x;
pData[i].acceleration.y = states[i].acceleration.y;
pData[i].acceleration.z = states[i].acceleration.z;
pData[i].angularVelocity.x = states[i].angularVelocity.x;
pData[i].angularVelocity.y = states[i].angularVelocity.y;
pData[i].angularVelocity.z = states[i].angularVelocity.z;
Input::GameController::CalculateOrientation(pData[i].acceleration, pData[i].angularVelocity,
1.0f / controller->accel_poll_rate,
pData[i].orientation);
pData[i].touchData.touchNum =
(states[i].touchpad[0].state ? 1 : 0) + (states[i].touchpad[1].state ? 1 : 0);
pData[i].touchData.touch[0].x = states[i].touchpad[0].x;
@ -352,6 +352,7 @@ int PS4_SYSV_ABI scePadReadHistory() {
}
int PS4_SYSV_ABI scePadReadState(s32 handle, OrbisPadData* pData) {
LOG_TRACE(Lib_Pad, "called");
if (handle == ORBIS_PAD_ERROR_DEVICE_NO_HANDLE) {
return ORBIS_PAD_ERROR_INVALID_HANDLE;
}
@ -367,16 +368,15 @@ int PS4_SYSV_ABI scePadReadState(s32 handle, OrbisPadData* pData) {
pData->rightStick.y = state.axes[static_cast<int>(Input::Axis::RightY)];
pData->analogButtons.l2 = state.axes[static_cast<int>(Input::Axis::TriggerLeft)];
pData->analogButtons.r2 = state.axes[static_cast<int>(Input::Axis::TriggerRight)];
pData->orientation.x = 0;
pData->orientation.y = 0;
pData->orientation.z = 0;
pData->orientation.w = 1;
pData->acceleration.x = 0.0f;
pData->acceleration.y = 0.0f;
pData->acceleration.z = 0.0f;
pData->angularVelocity.x = 0.0f;
pData->angularVelocity.y = 0.0f;
pData->angularVelocity.z = 0.0f;
pData->acceleration.x = state.acceleration.x;
pData->acceleration.y = state.acceleration.y;
pData->acceleration.z = state.acceleration.z;
pData->angularVelocity.x = state.angularVelocity.x;
pData->angularVelocity.y = state.angularVelocity.y;
pData->angularVelocity.z = state.angularVelocity.z;
Input::GameController::CalculateOrientation(pData->acceleration, pData->angularVelocity,
1.0f / controller->accel_poll_rate,
pData->orientation);
pData->touchData.touchNum =
(state.touchpad[0].state ? 1 : 0) + (state.touchpad[1].state ? 1 : 0);
pData->touchData.touch[0].x = state.touchpad[0].x;
@ -498,6 +498,8 @@ int PS4_SYSV_ABI scePadSetLoginUserNumber() {
int PS4_SYSV_ABI scePadSetMotionSensorState(s32 handle, bool bEnable) {
LOG_ERROR(Lib_Pad, "(STUBBED) called");
return ORBIS_OK;
// it's already handled by the SDL backend and will be on no matter what
// (assuming the controller supports it)
}
int PS4_SYSV_ABI scePadSetProcessFocus() {

View File

@ -2,6 +2,7 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#include <span>
#include <thread>
#include <vector>
#include <core/libraries/system/msgdialog_ui.h>
@ -1139,10 +1140,6 @@ Error PS4_SYSV_ABI sceSaveDataGetSaveDataMemory2(OrbisSaveDataMemoryGet2* getPar
LOG_INFO(Lib_SaveData, "called without save memory initialized");
return Error::MEMORY_NOT_READY;
}
if (SaveMemory::IsSaving()) {
LOG_TRACE(Lib_SaveData, "called while saving");
return Error::BUSY_FOR_SAVING;
}
LOG_DEBUG(Lib_SaveData, "called");
auto data = getParam->data;
if (data != nullptr) {
@ -1502,8 +1499,14 @@ Error PS4_SYSV_ABI sceSaveDataSetSaveDataMemory2(const OrbisSaveDataMemorySet2*
return Error::MEMORY_NOT_READY;
}
if (SaveMemory::IsSaving()) {
LOG_TRACE(Lib_SaveData, "called while saving");
return Error::BUSY_FOR_SAVING;
int count = 0;
while (++count < 100 && SaveMemory::IsSaving()) { // try for more 10 seconds
std::this_thread::sleep_for(chrono::milliseconds(100));
}
if (SaveMemory::IsSaving()) {
LOG_TRACE(Lib_SaveData, "called while saving");
return Error::BUSY_FOR_SAVING;
}
}
LOG_DEBUG(Lib_SaveData, "called");
auto data = setParam->data;
@ -1584,8 +1587,8 @@ Error PS4_SYSV_ABI sceSaveDataSetupSaveDataMemory2(const OrbisSaveDataMemorySetu
} else {
SaveMemory::SetIcon(nullptr, 0);
}
SaveMemory::TriggerSaveWithoutEvent();
}
SaveMemory::TriggerSaveWithoutEvent();
if (g_fw_ver >= ElfInfo::FW_45 && result != nullptr) {
result->existedMemorySize = existed_size;
}

View File

@ -10,327 +10,327 @@
namespace Libraries::Usbd {
int PS4_SYSV_ABI sceUsbdAllocTransfer() {
LOG_ERROR(Lib_Usbd, "(STUBBED)called");
LOG_ERROR(Lib_Usbd, "(STUBBED) called");
return ORBIS_OK;
}
int PS4_SYSV_ABI sceUsbdAttachKernelDriver() {
LOG_ERROR(Lib_Usbd, "(STUBBED)called");
LOG_ERROR(Lib_Usbd, "(STUBBED) called");
return ORBIS_OK;
}
int PS4_SYSV_ABI sceUsbdBulkTransfer() {
LOG_ERROR(Lib_Usbd, "(STUBBED)called");
LOG_ERROR(Lib_Usbd, "(STUBBED) called");
return ORBIS_OK;
}
int PS4_SYSV_ABI sceUsbdCancelTransfer() {
LOG_ERROR(Lib_Usbd, "(STUBBED)called");
LOG_ERROR(Lib_Usbd, "(STUBBED) called");
return ORBIS_OK;
}
int PS4_SYSV_ABI sceUsbdCheckConnected() {
LOG_ERROR(Lib_Usbd, "(STUBBED)called");
LOG_ERROR(Lib_Usbd, "(STUBBED) called");
return ORBIS_OK;
}
int PS4_SYSV_ABI sceUsbdClaimInterface() {
LOG_ERROR(Lib_Usbd, "(STUBBED)called");
LOG_ERROR(Lib_Usbd, "(STUBBED) called");
return ORBIS_OK;
}
int PS4_SYSV_ABI sceUsbdClearHalt() {
LOG_ERROR(Lib_Usbd, "(STUBBED)called");
LOG_ERROR(Lib_Usbd, "(STUBBED) called");
return ORBIS_OK;
}
int PS4_SYSV_ABI sceUsbdClose() {
LOG_ERROR(Lib_Usbd, "(STUBBED)called");
LOG_ERROR(Lib_Usbd, "(STUBBED) called");
return ORBIS_OK;
}
int PS4_SYSV_ABI sceUsbdControlTransfer() {
LOG_ERROR(Lib_Usbd, "(STUBBED)called");
LOG_ERROR(Lib_Usbd, "(STUBBED) called");
return ORBIS_OK;
}
int PS4_SYSV_ABI sceUsbdControlTransferGetData() {
LOG_ERROR(Lib_Usbd, "(STUBBED)called");
LOG_ERROR(Lib_Usbd, "(STUBBED) called");
return ORBIS_OK;
}
int PS4_SYSV_ABI sceUsbdControlTransferGetSetup() {
LOG_ERROR(Lib_Usbd, "(STUBBED)called");
LOG_ERROR(Lib_Usbd, "(STUBBED) called");
return ORBIS_OK;
}
int PS4_SYSV_ABI sceUsbdDetachKernelDriver() {
LOG_ERROR(Lib_Usbd, "(STUBBED)called");
LOG_ERROR(Lib_Usbd, "(STUBBED) called");
return ORBIS_OK;
}
int PS4_SYSV_ABI sceUsbdEventHandlerActive() {
LOG_ERROR(Lib_Usbd, "(STUBBED)called");
LOG_ERROR(Lib_Usbd, "(STUBBED) called");
return ORBIS_OK;
}
int PS4_SYSV_ABI sceUsbdEventHandlingOk() {
LOG_ERROR(Lib_Usbd, "(STUBBED)called");
LOG_ERROR(Lib_Usbd, "(STUBBED) called");
return ORBIS_OK;
}
int PS4_SYSV_ABI sceUsbdExit() {
LOG_ERROR(Lib_Usbd, "(STUBBED)called");
LOG_ERROR(Lib_Usbd, "(STUBBED) called");
return ORBIS_OK;
}
int PS4_SYSV_ABI sceUsbdFillBulkTransfer() {
LOG_ERROR(Lib_Usbd, "(STUBBED)called");
LOG_ERROR(Lib_Usbd, "(STUBBED) called");
return ORBIS_OK;
}
int PS4_SYSV_ABI sceUsbdFillControlSetup() {
LOG_ERROR(Lib_Usbd, "(STUBBED)called");
LOG_ERROR(Lib_Usbd, "(STUBBED) called");
return ORBIS_OK;
}
int PS4_SYSV_ABI sceUsbdFillControlTransfer() {
LOG_ERROR(Lib_Usbd, "(STUBBED)called");
LOG_ERROR(Lib_Usbd, "(STUBBED) called");
return ORBIS_OK;
}
int PS4_SYSV_ABI sceUsbdFillInterruptTransfer() {
LOG_ERROR(Lib_Usbd, "(STUBBED)called");
LOG_ERROR(Lib_Usbd, "(STUBBED) called");
return ORBIS_OK;
}
int PS4_SYSV_ABI sceUsbdFillIsoTransfer() {
LOG_ERROR(Lib_Usbd, "(STUBBED)called");
LOG_ERROR(Lib_Usbd, "(STUBBED) called");
return ORBIS_OK;
}
int PS4_SYSV_ABI sceUsbdFreeConfigDescriptor() {
LOG_ERROR(Lib_Usbd, "(STUBBED)called");
LOG_ERROR(Lib_Usbd, "(STUBBED) called");
return ORBIS_OK;
}
int PS4_SYSV_ABI sceUsbdFreeDeviceList() {
LOG_ERROR(Lib_Usbd, "(STUBBED)called");
LOG_ERROR(Lib_Usbd, "(STUBBED) called");
return ORBIS_OK;
}
int PS4_SYSV_ABI sceUsbdFreeTransfer() {
LOG_ERROR(Lib_Usbd, "(STUBBED)called");
LOG_ERROR(Lib_Usbd, "(STUBBED) called");
return ORBIS_OK;
}
int PS4_SYSV_ABI sceUsbdGetActiveConfigDescriptor() {
LOG_ERROR(Lib_Usbd, "(STUBBED)called");
LOG_ERROR(Lib_Usbd, "(STUBBED) called");
return ORBIS_OK;
}
int PS4_SYSV_ABI sceUsbdGetBusNumber() {
LOG_ERROR(Lib_Usbd, "(STUBBED)called");
LOG_ERROR(Lib_Usbd, "(STUBBED) called");
return ORBIS_OK;
}
int PS4_SYSV_ABI sceUsbdGetConfigDescriptor() {
LOG_ERROR(Lib_Usbd, "(STUBBED)called");
LOG_ERROR(Lib_Usbd, "(STUBBED) called");
return ORBIS_OK;
}
int PS4_SYSV_ABI sceUsbdGetConfigDescriptorByValue() {
LOG_ERROR(Lib_Usbd, "(STUBBED)called");
LOG_ERROR(Lib_Usbd, "(STUBBED) called");
return ORBIS_OK;
}
int PS4_SYSV_ABI sceUsbdGetConfiguration() {
LOG_ERROR(Lib_Usbd, "(STUBBED)called");
LOG_ERROR(Lib_Usbd, "(STUBBED) called");
return ORBIS_OK;
}
int PS4_SYSV_ABI sceUsbdGetDescriptor() {
LOG_ERROR(Lib_Usbd, "(STUBBED)called");
LOG_ERROR(Lib_Usbd, "(STUBBED) called");
return ORBIS_OK;
}
int PS4_SYSV_ABI sceUsbdGetDevice() {
LOG_ERROR(Lib_Usbd, "(STUBBED)called");
LOG_ERROR(Lib_Usbd, "(STUBBED) called");
return ORBIS_OK;
}
int PS4_SYSV_ABI sceUsbdGetDeviceAddress() {
LOG_ERROR(Lib_Usbd, "(STUBBED)called");
LOG_ERROR(Lib_Usbd, "(STUBBED) called");
return ORBIS_OK;
}
int PS4_SYSV_ABI sceUsbdGetDeviceDescriptor() {
LOG_ERROR(Lib_Usbd, "(STUBBED)called");
LOG_ERROR(Lib_Usbd, "(STUBBED) called");
return ORBIS_OK;
}
int PS4_SYSV_ABI sceUsbdGetDeviceList() {
LOG_ERROR(Lib_Usbd, "(STUBBED)called");
LOG_ERROR(Lib_Usbd, "(STUBBED) called");
return ORBIS_OK;
}
int PS4_SYSV_ABI sceUsbdGetDeviceSpeed() {
LOG_ERROR(Lib_Usbd, "(STUBBED)called");
LOG_ERROR(Lib_Usbd, "(STUBBED) called");
return ORBIS_OK;
}
int PS4_SYSV_ABI sceUsbdGetIsoPacketBuffer() {
LOG_ERROR(Lib_Usbd, "(STUBBED)called");
LOG_ERROR(Lib_Usbd, "(STUBBED) called");
return ORBIS_OK;
}
int PS4_SYSV_ABI sceUsbdGetMaxIsoPacketSize() {
LOG_ERROR(Lib_Usbd, "(STUBBED)called");
LOG_ERROR(Lib_Usbd, "(STUBBED) called");
return ORBIS_OK;
}
int PS4_SYSV_ABI sceUsbdGetMaxPacketSize() {
LOG_ERROR(Lib_Usbd, "(STUBBED)called");
LOG_ERROR(Lib_Usbd, "(STUBBED) called");
return ORBIS_OK;
}
int PS4_SYSV_ABI sceUsbdGetStringDescriptor() {
LOG_ERROR(Lib_Usbd, "(STUBBED)called");
LOG_ERROR(Lib_Usbd, "(STUBBED) called");
return ORBIS_OK;
}
int PS4_SYSV_ABI sceUsbdGetStringDescriptorAscii() {
LOG_ERROR(Lib_Usbd, "(STUBBED)called");
LOG_ERROR(Lib_Usbd, "(STUBBED) called");
return ORBIS_OK;
}
int PS4_SYSV_ABI sceUsbdHandleEvents() {
LOG_ERROR(Lib_Usbd, "(STUBBED)called");
LOG_ERROR(Lib_Usbd, "(STUBBED) called");
return ORBIS_OK;
}
int PS4_SYSV_ABI sceUsbdHandleEventsLocked() {
LOG_ERROR(Lib_Usbd, "(STUBBED)called");
LOG_ERROR(Lib_Usbd, "(STUBBED) called");
return ORBIS_OK;
}
int PS4_SYSV_ABI sceUsbdHandleEventsTimeout() {
LOG_ERROR(Lib_Usbd, "(STUBBED)called");
LOG_DEBUG(Lib_Usbd, "(STUBBED) called");
return ORBIS_OK;
}
int PS4_SYSV_ABI sceUsbdInit() {
LOG_ERROR(Lib_Usbd, "(STUBBED)called");
LOG_ERROR(Lib_Usbd, "(STUBBED) called");
return 0x80240005; // Skip
}
int PS4_SYSV_ABI sceUsbdInterruptTransfer() {
LOG_ERROR(Lib_Usbd, "(STUBBED)called");
LOG_ERROR(Lib_Usbd, "(STUBBED) called");
return ORBIS_OK;
}
int PS4_SYSV_ABI sceUsbdKernelDriverActive() {
LOG_ERROR(Lib_Usbd, "(STUBBED)called");
LOG_ERROR(Lib_Usbd, "(STUBBED) called");
return ORBIS_OK;
}
int PS4_SYSV_ABI sceUsbdLockEvents() {
LOG_ERROR(Lib_Usbd, "(STUBBED)called");
LOG_ERROR(Lib_Usbd, "(STUBBED) called");
return ORBIS_OK;
}
int PS4_SYSV_ABI sceUsbdLockEventWaiters() {
LOG_ERROR(Lib_Usbd, "(STUBBED)called");
LOG_ERROR(Lib_Usbd, "(STUBBED) called");
return ORBIS_OK;
}
int PS4_SYSV_ABI sceUsbdOpen() {
LOG_ERROR(Lib_Usbd, "(STUBBED)called");
LOG_ERROR(Lib_Usbd, "(STUBBED) called");
return ORBIS_OK;
}
int PS4_SYSV_ABI sceUsbdOpenDeviceWithVidPid() {
LOG_ERROR(Lib_Usbd, "(STUBBED)called");
LOG_ERROR(Lib_Usbd, "(STUBBED) called");
return ORBIS_OK;
}
int PS4_SYSV_ABI sceUsbdRefDevice() {
LOG_ERROR(Lib_Usbd, "(STUBBED)called");
LOG_ERROR(Lib_Usbd, "(STUBBED) called");
return ORBIS_OK;
}
int PS4_SYSV_ABI sceUsbdReleaseInterface() {
LOG_ERROR(Lib_Usbd, "(STUBBED)called");
LOG_ERROR(Lib_Usbd, "(STUBBED) called");
return ORBIS_OK;
}
int PS4_SYSV_ABI sceUsbdResetDevice() {
LOG_ERROR(Lib_Usbd, "(STUBBED)called");
LOG_ERROR(Lib_Usbd, "(STUBBED) called");
return ORBIS_OK;
}
int PS4_SYSV_ABI sceUsbdSetConfiguration() {
LOG_ERROR(Lib_Usbd, "(STUBBED)called");
LOG_ERROR(Lib_Usbd, "(STUBBED) called");
return ORBIS_OK;
}
int PS4_SYSV_ABI sceUsbdSetInterfaceAltSetting() {
LOG_ERROR(Lib_Usbd, "(STUBBED)called");
LOG_ERROR(Lib_Usbd, "(STUBBED) called");
return ORBIS_OK;
}
int PS4_SYSV_ABI sceUsbdSetIsoPacketLengths() {
LOG_ERROR(Lib_Usbd, "(STUBBED)called");
LOG_ERROR(Lib_Usbd, "(STUBBED) called");
return ORBIS_OK;
}
int PS4_SYSV_ABI sceUsbdSubmitTransfer() {
LOG_ERROR(Lib_Usbd, "(STUBBED)called");
LOG_ERROR(Lib_Usbd, "(STUBBED) called");
return ORBIS_OK;
}
int PS4_SYSV_ABI sceUsbdTryLockEvents() {
LOG_ERROR(Lib_Usbd, "(STUBBED)called");
LOG_ERROR(Lib_Usbd, "(STUBBED) called");
return ORBIS_OK;
}
int PS4_SYSV_ABI sceUsbdUnlockEvents() {
LOG_ERROR(Lib_Usbd, "(STUBBED)called");
LOG_ERROR(Lib_Usbd, "(STUBBED) called");
return ORBIS_OK;
}
int PS4_SYSV_ABI sceUsbdUnlockEventWaiters() {
LOG_ERROR(Lib_Usbd, "(STUBBED)called");
LOG_ERROR(Lib_Usbd, "(STUBBED) called");
return ORBIS_OK;
}
int PS4_SYSV_ABI sceUsbdUnrefDevice() {
LOG_ERROR(Lib_Usbd, "(STUBBED)called");
LOG_ERROR(Lib_Usbd, "(STUBBED) called");
return ORBIS_OK;
}
int PS4_SYSV_ABI sceUsbdWaitForEvent() {
LOG_ERROR(Lib_Usbd, "(STUBBED)called");
LOG_ERROR(Lib_Usbd, "(STUBBED) called");
return ORBIS_OK;
}
int PS4_SYSV_ABI Func_65F6EF33E38FFF50() {
LOG_ERROR(Lib_Usbd, "(STUBBED)called");
LOG_ERROR(Lib_Usbd, "(STUBBED) called");
return ORBIS_OK;
}
int PS4_SYSV_ABI Func_97F056BAD90AADE7() {
LOG_ERROR(Lib_Usbd, "(STUBBED)called");
LOG_ERROR(Lib_Usbd, "(STUBBED) called");
return ORBIS_OK;
}
int PS4_SYSV_ABI Func_C55104A33B35B264() {
LOG_ERROR(Lib_Usbd, "(STUBBED)called");
LOG_ERROR(Lib_Usbd, "(STUBBED) called");
return ORBIS_OK;
}
int PS4_SYSV_ABI Func_D56B43060720B1E0() {
LOG_ERROR(Lib_Usbd, "(STUBBED)called");
LOG_ERROR(Lib_Usbd, "(STUBBED) called");
return ORBIS_OK;
}

View File

@ -52,8 +52,7 @@ s32 PS4_SYSV_ABI sceVideoOutAddFlipEvent(Kernel::SceKernelEqueue eq, s32 handle,
Kernel::EqueueEvent event{};
event.event.ident = u64(OrbisVideoOutEventId::Flip);
event.event.filter = Kernel::SceKernelEvent::Filter::VideoOut;
// The library only sets EV_ADD but kernel driver forces EV_CLEAR
event.event.flags = Kernel::SceKernelEvent::Flags::Clear;
event.event.flags = Kernel::SceKernelEvent::Flags::Add;
event.event.udata = udata;
event.event.fflags = 0;
event.event.data = 0;
@ -79,8 +78,7 @@ s32 PS4_SYSV_ABI sceVideoOutAddVblankEvent(Kernel::SceKernelEqueue eq, s32 handl
Kernel::EqueueEvent event{};
event.event.ident = u64(OrbisVideoOutEventId::Vblank);
event.event.filter = Kernel::SceKernelEvent::Filter::VideoOut;
// The library only sets EV_ADD but kernel driver forces EV_CLEAR
event.event.flags = Kernel::SceKernelEvent::Flags::Clear;
event.event.flags = Kernel::SceKernelEvent::Flags::Add;
event.event.udata = udata;
event.event.fflags = 0;
event.event.data = 0;

View File

@ -282,16 +282,14 @@ void Emulator::Run(const std::filesystem::path& file) {
}
void Emulator::LoadSystemModules(const std::filesystem::path& file, std::string game_serial) {
constexpr std::array<SysModules, 13> ModulesToLoad{
constexpr std::array<SysModules, 11> ModulesToLoad{
{{"libSceNgs2.sprx", &Libraries::Ngs2::RegisterlibSceNgs2},
{"libSceFiber.sprx", &Libraries::Fiber::RegisterlibSceFiber},
{"libSceUlt.sprx", nullptr},
{"libSceJson.sprx", nullptr},
{"libSceJson2.sprx", nullptr},
{"libSceLibcInternal.sprx", &Libraries::LibcInternal::RegisterlibSceLibcInternal},
{"libSceDiscMap.sprx", &Libraries::DiscMap::RegisterlibSceDiscMap},
{"libSceRtc.sprx", &Libraries::Rtc::RegisterlibSceRtc},
{"libSceJpegEnc.sprx", &Libraries::JpegEnc::RegisterlibSceJpegEnc},
{"libSceCesCs.sprx", nullptr},
{"libSceFont.sprx", nullptr},
{"libSceFontFt.sprx", nullptr},

View File

@ -2,6 +2,7 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#include <SDL3/SDL.h>
#include "common/logging/log.h"
#include "core/libraries/kernel/time.h"
#include "core/libraries/pad/pad.h"
#include "input/controller.h"
@ -116,6 +117,103 @@ void GameController::Axis(int id, Input::Axis axis, int value) {
AddState(state);
}
void GameController::Gyro(int id, const float gyro[3]) {
std::scoped_lock lock{m_mutex};
auto state = GetLastState();
state.time = Libraries::Kernel::sceKernelGetProcessTime();
// Update the angular velocity (gyro data)
state.angularVelocity.x = gyro[0]; // X-axis
state.angularVelocity.y = gyro[1]; // Y-axis
state.angularVelocity.z = gyro[2]; // Z-axis
AddState(state);
}
void GameController::Acceleration(int id, const float acceleration[3]) {
std::scoped_lock lock{m_mutex};
auto state = GetLastState();
state.time = Libraries::Kernel::sceKernelGetProcessTime();
// Update the acceleration values
state.acceleration.x = acceleration[0]; // X-axis
state.acceleration.y = acceleration[1]; // Y-axis
state.acceleration.z = acceleration[2]; // Z-axis
AddState(state);
}
// Stolen from
// https://github.com/xioTechnologies/Open-Source-AHRS-With-x-IMU/blob/master/x-IMU%20IMU%20and%20AHRS%20Algorithms/x-IMU%20IMU%20and%20AHRS%20Algorithms/AHRS/MahonyAHRS.cs
float eInt[3] = {0.0f, 0.0f, 0.0f}; // Integral error terms
const float Kp = 50.0f; // Proportional gain
const float Ki = 1.0f; // Integral gain
Libraries::Pad::OrbisFQuaternion o = {1, 0, 0, 0};
void GameController::CalculateOrientation(Libraries::Pad::OrbisFVector3& acceleration,
Libraries::Pad::OrbisFVector3& angularVelocity,
float deltaTime,
Libraries::Pad::OrbisFQuaternion& orientation) {
float ax = acceleration.x, ay = acceleration.y, az = acceleration.z;
float gx = angularVelocity.x, gy = angularVelocity.y, gz = angularVelocity.z;
float q1 = o.w, q2 = o.x, q3 = o.y, q4 = o.z;
// Normalize accelerometer measurement
float norm = std::sqrt(ax * ax + ay * ay + az * az);
if (norm == 0.0f)
return; // Handle NaN
norm = 1.0f / norm;
ax *= norm;
ay *= norm;
az *= norm;
// Estimated direction of gravity
float vx = 2.0f * (q2 * q4 - q1 * q3);
float vy = 2.0f * (q1 * q2 + q3 * q4);
float vz = q1 * q1 - q2 * q2 - q3 * q3 + q4 * q4;
// Error is cross product between estimated direction and measured direction of gravity
float ex = (ay * vz - az * vy);
float ey = (az * vx - ax * vz);
float ez = (ax * vy - ay * vx);
if (Ki > 0.0f) {
eInt[0] += ex * deltaTime; // Accumulate integral error
eInt[1] += ey * deltaTime;
eInt[2] += ez * deltaTime;
} else {
eInt[0] = eInt[1] = eInt[2] = 0.0f; // Prevent integral wind-up
}
// Apply feedback terms
gx += Kp * ex + Ki * eInt[0];
gy += Kp * ey + Ki * eInt[1];
gz += Kp * ez + Ki * eInt[2];
//// Integrate rate of change of quaternion
// float pa = q2, pb = q3, pc = q4;
// q1 += (-q2 * gx - q3 * gy - q4 * gz) * (0.5f * deltaTime);
// q2 += (pa * gx + pb * gz - pc * gy) * (0.5f * deltaTime);
// q3 += (pb * gy - pa * gz + pc * gx) * (0.5f * deltaTime);
// q4 += (pc * gz + pa * gy - pb * gx) * (0.5f * deltaTime);
q1 += (-q2 * gx - q3 * gy - q4 * gz) * (0.5f * deltaTime);
q2 += (q1 * gx + q3 * gz - q4 * gy) * (0.5f * deltaTime);
q3 += (q1 * gy - q2 * gz + q4 * gx) * (0.5f * deltaTime);
q4 += (q1 * gz + q2 * gy - q3 * gx) * (0.5f * deltaTime);
// Normalize quaternion
norm = std::sqrt(q1 * q1 + q2 * q2 + q3 * q3 + q4 * q4);
norm = 1.0f / norm;
orientation.w = q1 * norm;
orientation.x = q2 * norm;
orientation.y = q3 * norm;
orientation.z = q4 * norm;
o.w = q1 * norm;
o.x = q2 * norm;
o.y = q3 * norm;
o.z = q4 * norm;
LOG_DEBUG(Lib_Pad, "Calculated orientation: {:.2f} {:.2f} {:.2f} {:.2f}", orientation.x,
orientation.y, orientation.z, orientation.w);
}
void GameController::SetLightBarRGB(u8 r, u8 g, u8 b) {
if (m_sdl_gamepad != nullptr) {
SDL_SetGamepadLED(m_sdl_gamepad, r, g, b);
@ -149,6 +247,18 @@ void GameController::TryOpenSDLController() {
int gamepad_count;
SDL_JoystickID* gamepads = SDL_GetGamepads(&gamepad_count);
m_sdl_gamepad = gamepad_count > 0 ? SDL_OpenGamepad(gamepads[0]) : nullptr;
if (SDL_SetGamepadSensorEnabled(m_sdl_gamepad, SDL_SENSOR_GYRO, true)) {
gyro_poll_rate = SDL_GetGamepadSensorDataRate(m_sdl_gamepad, SDL_SENSOR_GYRO);
LOG_INFO(Input, "Gyro initialized, poll rate: {}", gyro_poll_rate);
} else {
LOG_ERROR(Input, "Failed to initialize gyro controls for gamepad");
}
if (SDL_SetGamepadSensorEnabled(m_sdl_gamepad, SDL_SENSOR_ACCEL, true)) {
accel_poll_rate = SDL_GetGamepadSensorDataRate(m_sdl_gamepad, SDL_SENSOR_ACCEL);
LOG_INFO(Input, "Accel initialized, poll rate: {}", accel_poll_rate);
} else {
LOG_ERROR(Input, "Failed to initialize accel controls for gamepad");
}
SDL_free(gamepads);
SetLightBarRGB(0, 0, 255);

View File

@ -33,6 +33,9 @@ struct State {
u64 time = 0;
int axes[static_cast<int>(Axis::AxisMax)] = {128, 128, 128, 128, 0, 0};
TouchpadEntry touchpad[2] = {{false, 0, 0}, {false, 0, 0}};
Libraries::Pad::OrbisFVector3 acceleration = {0.0f, 0.0f, 0.0f};
Libraries::Pad::OrbisFVector3 angularVelocity = {0.0f, 0.0f, 0.0f};
Libraries::Pad::OrbisFQuaternion orientation = {0.0f, 0.0f, 0.0f, 1.0f};
};
inline int GetAxis(int min, int max, int value) {
@ -53,12 +56,21 @@ public:
void CheckButton(int id, Libraries::Pad::OrbisPadButtonDataOffset button, bool isPressed);
void AddState(const State& state);
void Axis(int id, Input::Axis axis, int value);
void Gyro(int id, const float gyro[3]);
void Acceleration(int id, const float acceleration[3]);
void SetLightBarRGB(u8 r, u8 g, u8 b);
bool SetVibration(u8 smallMotor, u8 largeMotor);
void SetTouchpadState(int touchIndex, bool touchDown, float x, float y);
void TryOpenSDLController();
u32 Poll();
float gyro_poll_rate;
float accel_poll_rate;
static void CalculateOrientation(Libraries::Pad::OrbisFVector3& acceleration,
Libraries::Pad::OrbisFVector3& angularVelocity,
float deltaTime,
Libraries::Pad::OrbisFQuaternion& orientation);
private:
struct StateInternal {
bool obtained = false;

View File

@ -14,6 +14,7 @@
#include <QNetworkReply>
#include <QNetworkRequest>
#include <QProcess>
#include <QProgressBar>
#include <QPushButton>
#include <QStandardPaths>
#include <QString>
@ -24,11 +25,9 @@
#include <common/path_util.h>
#include <common/scm_rev.h>
#include <common/version.h>
#include <qprogressbar.h>
#include "check_update.h"
using namespace Common::FS;
namespace fs = std::filesystem;
CheckUpdate::CheckUpdate(const bool showMessage, QWidget* parent)
: QDialog(parent), networkManager(new QNetworkAccessManager(this)) {
@ -254,7 +253,11 @@ void CheckUpdate::setupUI(const QString& downloadUrl, const QString& latestDate,
connect(noButton, &QPushButton::clicked, this, [this]() { close(); });
autoUpdateCheckBox->setChecked(Config::autoUpdate());
#if (QT_VERSION < QT_VERSION_CHECK(6, 7, 0))
connect(autoUpdateCheckBox, &QCheckBox::stateChanged, this, [](int state) {
#else
connect(autoUpdateCheckBox, &QCheckBox::checkStateChanged, this, [](Qt::CheckState state) {
#endif
const auto user_dir = Common::FS::GetUserPath(Common::FS::PathType::UserDir);
Config::setAutoUpdate(state == Qt::Checked);
Config::save(user_dir / "config.toml");

View File

@ -3,22 +3,24 @@
#include <QCompleter>
#include <QDirIterator>
#include <QFileDialog>
#include <QHoverEvent>
#include <fmt/format.h>
#include <common/version.h>
#include "common/config.h"
#include "common/version.h"
#include "qt_gui/compatibility_info.h"
#ifdef ENABLE_DISCORD_RPC
#include "common/discord_rpc_handler.h"
#include "common/singleton.h"
#endif
#ifdef ENABLE_UPDATER
#include "check_update.h"
#endif
#include <toml.hpp>
#include "background_music_player.h"
#include "common/logging/backend.h"
#include "common/logging/filter.h"
#include "common/logging/formatter.h"
#include "main_window.h"
#include "settings_dialog.h"
#include "ui_settings_dialog.h"
QStringList languageNames = {"Arabic",
@ -130,8 +132,13 @@ SettingsDialog::SettingsDialog(std::span<const QString> physical_devices,
// GENERAL TAB
{
#ifdef ENABLE_UPDATER
#if (QT_VERSION < QT_VERSION_CHECK(6, 7, 0))
connect(ui->updateCheckBox, &QCheckBox::stateChanged, this,
[](int state) { Config::setAutoUpdate(state == Qt::Checked); });
#else
connect(ui->updateCheckBox, &QCheckBox::checkStateChanged, this,
[](Qt::CheckState state) { Config::setAutoUpdate(state == Qt::Checked); });
#endif
connect(ui->updateComboBox, &QComboBox::currentTextChanged, this,
[](const QString& channel) { Config::setUpdateChannel(channel.toStdString()); });
@ -150,7 +157,12 @@ SettingsDialog::SettingsDialog(std::span<const QString> physical_devices,
emit CompatibilityChanged();
});
#if (QT_VERSION < QT_VERSION_CHECK(6, 7, 0))
connect(ui->enableCompatibilityCheckBox, &QCheckBox::stateChanged, this, [this](int state) {
#else
connect(ui->enableCompatibilityCheckBox, &QCheckBox::checkStateChanged, this,
[this](Qt::CheckState state) {
#endif
Config::setCompatibilityEnabled(state);
emit CompatibilityChanged();
});
@ -201,6 +213,8 @@ SettingsDialog::SettingsDialog(std::span<const QString> physical_devices,
ui->showSplashCheckBox->installEventFilter(this);
ui->discordRPCCheckbox->installEventFilter(this);
ui->userName->installEventFilter(this);
ui->label_Trophy->installEventFilter(this);
ui->trophyKeyLineEdit->installEventFilter(this);
ui->logTypeGroupBox->installEventFilter(this);
ui->logFilter->installEventFilter(this);
#ifdef ENABLE_UPDATER
@ -295,6 +309,9 @@ void SettingsDialog::LoadValuesFromConfig() {
QString::fromStdString(toml::find_or<std::string>(data, "General", "logFilter", "")));
ui->userNameLineEdit->setText(
QString::fromStdString(toml::find_or<std::string>(data, "General", "userName", "shadPS4")));
ui->trophyKeyLineEdit->setText(
QString::fromStdString(toml::find_or<std::string>(data, "Keys", "TrophyKey", "")));
ui->trophyKeyLineEdit->setEchoMode(QLineEdit::Password);
ui->debugDump->setChecked(toml::find_or<bool>(data, "Debug", "DebugDump", false));
ui->vkValidationCheckBox->setChecked(toml::find_or<bool>(data, "Vulkan", "validation", false));
ui->vkSyncValidationCheckBox->setChecked(
@ -358,7 +375,7 @@ void SettingsDialog::InitializeEmulatorLanguages() {
idx++;
}
connect(ui->emulatorLanguageComboBox, qOverload<int>(&QComboBox::currentIndexChanged), this,
connect(ui->emulatorLanguageComboBox, &QComboBox::currentIndexChanged, this,
&SettingsDialog::OnLanguageChanged);
}
@ -407,6 +424,10 @@ void SettingsDialog::updateNoteTextEdit(const QString& elementName) {
text = tr("discordRPCCheckbox");
} else if (elementName == "userName") {
text = tr("userName");
} else if (elementName == "label_Trophy") {
text = tr("TrophyKey");
} else if (elementName == "trophyKeyLineEdit") {
text = tr("TrophyKey");
} else if (elementName == "logTypeGroupBox") {
text = tr("logTypeGroupBox");
} else if (elementName == "logFilter") {
@ -517,6 +538,7 @@ void SettingsDialog::UpdateSettings() {
Config::setLogType(ui->logTypeComboBox->currentText().toStdString());
Config::setLogFilter(ui->logFilterLineEdit->text().toStdString());
Config::setUserName(ui->userNameLineEdit->text().toStdString());
Config::setTrophyKey(ui->trophyKeyLineEdit->text().toStdString());
Config::setCursorState(ui->hideCursorComboBox->currentIndex());
Config::setCursorHideTimeout(ui->idleTimeoutSpinBox->value());
Config::setGpuId(ui->graphicsAdapterBox->currentIndex() - 1);
@ -578,4 +600,4 @@ void SettingsDialog::ResetInstallFolders() {
}
Config::setGameInstallDirs(settings_install_dirs_config);
}
}
}

View File

@ -11,8 +11,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>950</width>
<height>780</height>
<width>970</width>
<height>670</height>
</rect>
</property>
<property name="sizePolicy">
@ -67,8 +67,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>822</width>
<height>487</height>
<width>946</width>
<height>536</height>
</rect>
</property>
<layout class="QVBoxLayout" name="generalTabVLayout" stretch="0">
@ -77,87 +77,7 @@
<property name="bottomMargin">
<number>0</number>
</property>
<item row="0" column="2">
<layout class="QVBoxLayout" name="loggerTabLayoutRight">
<item>
<widget class="QGroupBox" name="loggerGroupBox">
<property name="title">
<string>Logger</string>
</property>
<layout class="QVBoxLayout" name="loggerLayout">
<item>
<widget class="QWidget" name="LogTypeWidget" native="true">
<layout class="QVBoxLayout" name="LogTypeLayout">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QGroupBox" name="logTypeGroupBox">
<property name="title">
<string>Log Type</string>
</property>
<layout class="QVBoxLayout" name="logTypeBoxLayout">
<item>
<widget class="QComboBox" name="logTypeComboBox">
<item>
<property name="text">
<string>async</string>
</property>
</item>
<item>
<property name="text">
<string>sync</string>
</property>
</item>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<layout class="QVBoxLayout" name="vLayoutLogFilter">
<property name="spacing">
<number>6</number>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<item>
<layout class="QHBoxLayout" name="hLayoutLogFilter">
<item>
<widget class="QGroupBox" name="logFilter">
<property name="title">
<string>Log Filter</string>
</property>
<layout class="QVBoxLayout" name="logFilterLayout">
<item>
<widget class="QLineEdit" name="logFilterLineEdit"/>
</item>
</layout>
</widget>
</item>
</layout>
</item>
</layout>
</item>
</layout>
</widget>
</item>
</layout>
</item>
<item row="0" column="0">
<item row="1" column="0">
<layout class="QVBoxLayout" name="systemTabLayoutLeft">
<item>
<widget class="QGroupBox" name="SystemSettings">
@ -194,7 +114,7 @@
</item>
</layout>
</item>
<item row="0" column="1">
<item row="0" column="0">
<layout class="QVBoxLayout" name="emulatorTabLayoutMiddle">
<item>
<widget class="QGroupBox" name="emulatorSettingsGroupBox">
@ -268,10 +188,10 @@
</item>
</layout>
</item>
<item row="1" column="0">
<item row="1" column="2">
<layout class="QVBoxLayout" name="updaterTabLayoutLeft">
<property name="spacing">
<number>-1</number>
<number>6</number>
</property>
<property name="sizeConstraint">
<enum>QLayout::SizeConstraint::SetDefaultConstraint</enum>
@ -436,138 +356,8 @@
</layout>
</item>
<item row="1" column="1">
<layout class="QVBoxLayout" name="GUITabLayoutMiddle" stretch="0">
<item alignment="Qt::AlignmentFlag::AlignTop">
<widget class="QGroupBox" name="GUIgroupBox">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
<property name="title">
<string>GUI Settings</string>
</property>
<layout class="QVBoxLayout" name="GUILayout">
<property name="topMargin">
<number>1</number>
</property>
<property name="bottomMargin">
<number>11</number>
</property>
<item>
<widget class="QCheckBox" name="disableTrophycheckBox">
<property name="text">
<string>Disable Trophy Pop-ups</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="playBGMCheckBox">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Play title music</string>
</property>
</widget>
</item>
<item>
<layout class="QVBoxLayout" name="GUIMusicLayout">
<property name="topMargin">
<number>1</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<spacer name="GUIverticalSpacer_3">
<property name="orientation">
<enum>Qt::Orientation::Vertical</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Policy::Fixed</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>13</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QLabel" name="label">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="maximumSize">
<size>
<width>16777215</width>
<height>16777215</height>
</size>
</property>
<property name="text">
<string>Volume</string>
</property>
</widget>
</item>
<item>
<widget class="QSlider" name="BGMVolumeSlider">
<property name="toolTip">
<string>Set the volume of the background music.</string>
</property>
<property name="maximum">
<number>100</number>
</property>
<property name="singleStep">
<number>10</number>
</property>
<property name="pageStep">
<number>20</number>
</property>
<property name="value">
<number>50</number>
</property>
<property name="orientation">
<enum>Qt::Orientation::Horizontal</enum>
</property>
<property name="invertedAppearance">
<bool>false</bool>
</property>
<property name="invertedControls">
<bool>false</bool>
</property>
<property name="tickPosition">
<enum>QSlider::TickPosition::NoTicks</enum>
</property>
<property name="tickInterval">
<number>10</number>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
</item>
</layout>
</item>
<item row="1" column="2">
<layout class="QVBoxLayout" name="CompatTabLayoutRight" stretch="0">
<item alignment="Qt::AlignmentFlag::AlignTop">
<item>
<widget class="QGroupBox" name="CompatgroupBox">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
@ -638,6 +428,160 @@
</item>
</layout>
</item>
<item row="0" column="1">
<layout class="QVBoxLayout" name="GUITabLayoutMiddle" stretch="0">
<item>
<widget class="QGroupBox" name="GUIgroupBox">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
<property name="title">
<string>GUI Settings</string>
</property>
<layout class="QVBoxLayout" name="GUILayout">
<property name="topMargin">
<number>1</number>
</property>
<property name="bottomMargin">
<number>11</number>
</property>
<item>
<widget class="QCheckBox" name="playBGMCheckBox">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Play title music</string>
</property>
</widget>
</item>
<item>
<layout class="QVBoxLayout" name="GUIMusicLayout">
<property name="topMargin">
<number>1</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QLabel" name="label_Volume">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="maximumSize">
<size>
<width>16777215</width>
<height>16777215</height>
</size>
</property>
<property name="text">
<string>Volume</string>
</property>
</widget>
</item>
<item>
<widget class="QSlider" name="BGMVolumeSlider">
<property name="toolTip">
<string>Set the volume of the background music.</string>
</property>
<property name="maximum">
<number>100</number>
</property>
<property name="singleStep">
<number>10</number>
</property>
<property name="pageStep">
<number>20</number>
</property>
<property name="value">
<number>50</number>
</property>
<property name="orientation">
<enum>Qt::Orientation::Horizontal</enum>
</property>
<property name="invertedAppearance">
<bool>false</bool>
</property>
<property name="invertedControls">
<bool>false</bool>
</property>
<property name="tickPosition">
<enum>QSlider::TickPosition::NoTicks</enum>
</property>
<property name="tickInterval">
<number>10</number>
</property>
</widget>
</item>
<item>
<layout class="QVBoxLayout" name="vLayoutTrophy">
<property name="spacing">
<number>6</number>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<item>
<layout class="QHBoxLayout" name="hLayoutTrophy">
<item>
<widget class="QGroupBox" name="trophyGroupBox">
<property name="title">
<string>Trophy</string>
</property>
<layout class="QVBoxLayout" name="userNameLayout">
<item>
<widget class="QCheckBox" name="disableTrophycheckBox">
<property name="text">
<string>Disable Trophy Pop-ups</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label_Trophy">
<property name="text">
<string>Trophy Key</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="trophyKeyLineEdit">
<property name="minimumSize">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</item>
</layout>
</item>
</layout>
</item>
</layout>
</widget>
</item>
</layout>
</item>
</layout>
</item>
</layout>
@ -655,8 +599,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>396</width>
<height>222</height>
<width>926</width>
<height>536</height>
</rect>
</property>
<layout class="QVBoxLayout" name="inputTabVLayout" stretch="0,0">
@ -946,8 +890,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>536</width>
<height>192</height>
<width>926</width>
<height>536</height>
</rect>
</property>
<layout class="QVBoxLayout" name="graphicsTabVLayout" stretch="0,0">
@ -1197,8 +1141,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>146</width>
<height>215</height>
<width>926</width>
<height>536</height>
</rect>
</property>
<layout class="QVBoxLayout" name="pathsTabLayout" stretch="0">
@ -1211,18 +1155,25 @@
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QPushButton" name="removeFolderButton">
<property name="text">
<string>Remove</string>
<layout class="QHBoxLayout" name="horizontalLayout">
<property name="topMargin">
<number>0</number>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="addFolderButton">
<property name="text">
<string>Add...</string>
</property>
</widget>
<item>
<widget class="QPushButton" name="addFolderButton">
<property name="text">
<string>Add...</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="removeFolderButton">
<property name="text">
<string>Remove</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QListWidget" name="gameFoldersListWidget"/>
@ -1263,71 +1214,145 @@
<rect>
<x>0</x>
<y>0</y>
<width>288</width>
<height>163</height>
<width>926</width>
<height>536</height>
</rect>
</property>
<layout class="QVBoxLayout" name="debugTabVLayout" stretch="0,1">
<item>
<layout class="QHBoxLayout" name="debugTabHLayout" stretch="1">
<layout class="QHBoxLayout" name="horizontalLayout_4">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<item>
<widget class="QGroupBox" name="debugTabGroupBox">
<property name="enabled">
<bool>true</bool>
</property>
<property name="title">
<string>General</string>
</property>
<property name="alignment">
<set>Qt::AlignmentFlag::AlignLeading|Qt::AlignmentFlag::AlignLeft|Qt::AlignmentFlag::AlignTop</set>
</property>
<layout class="QVBoxLayout" name="debugTabLayout">
<item alignment="Qt::AlignmentFlag::AlignTop">
<widget class="QCheckBox" name="debugDump">
<property name="text">
<string>Enable Debug Dumping</string>
</property>
</widget>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Orientation::Vertical</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Policy::MinimumExpanding</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QCheckBox" name="vkValidationCheckBox">
<property name="text">
<string>Enable Vulkan Validation Layers</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="vkSyncValidationCheckBox">
<property name="text">
<string>Enable Vulkan Synchronization Validation</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="rdocCheckBox">
<property name="text">
<string>Enable RenderDoc Debugging</string>
</property>
</widget>
</item>
</layout>
</widget>
<layout class="QHBoxLayout" name="debugTabHLayout" stretch="1">
<item>
<widget class="QGroupBox" name="debugTabGroupBox">
<property name="enabled">
<bool>true</bool>
</property>
<property name="title">
<string>General</string>
</property>
<property name="alignment">
<set>Qt::AlignmentFlag::AlignLeading|Qt::AlignmentFlag::AlignLeft|Qt::AlignmentFlag::AlignTop</set>
</property>
<layout class="QVBoxLayout" name="debugTabLayout">
<item>
<widget class="QCheckBox" name="debugDump">
<property name="text">
<string>Enable Debug Dumping</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="vkValidationCheckBox">
<property name="text">
<string>Enable Vulkan Validation Layers</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="vkSyncValidationCheckBox">
<property name="text">
<string>Enable Vulkan Synchronization Validation</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="rdocCheckBox">
<property name="text">
<string>Enable RenderDoc Debugging</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QVBoxLayout" name="loggerTabLayoutRight">
<item>
<widget class="QGroupBox" name="loggerGroupBox">
<property name="title">
<string>Logger</string>
</property>
<layout class="QVBoxLayout" name="loggerLayout">
<item>
<widget class="QWidget" name="LogTypeWidget" native="true">
<layout class="QVBoxLayout" name="LogTypeLayout">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QGroupBox" name="logTypeGroupBox">
<property name="title">
<string>Log Type</string>
</property>
<layout class="QVBoxLayout" name="logTypeBoxLayout">
<item>
<widget class="QComboBox" name="logTypeComboBox">
<item>
<property name="text">
<string>async</string>
</property>
</item>
<item>
<property name="text">
<string>sync</string>
</property>
</item>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<layout class="QVBoxLayout" name="vLayoutLogFilter">
<property name="spacing">
<number>6</number>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<item>
<layout class="QHBoxLayout" name="hLayoutLogFilter">
<item>
<widget class="QGroupBox" name="logFilter">
<property name="title">
<string>Log Filter</string>
</property>
<layout class="QVBoxLayout" name="logFilterLayout">
<item>
<widget class="QLineEdit" name="logFilterLineEdit"/>
</item>
</layout>
</widget>
</item>
</layout>
</item>
</layout>
</item>
</layout>
</widget>
</item>
</layout>
</item>
</layout>
</item>

View File

@ -537,6 +537,16 @@
<source>Username</source>
<translation>اسم المستخدم</translation>
</message>
<message>
<location filename="../settings_dialog.ui"/>
<source>Trophy Key</source>
<translation>Trophy Key</translation>
</message>
<message>
<location filename="../settings_dialog.ui"/>
<source>Trophy</source>
<translation>Trophy</translation>
</message>
<message>
<location filename="../settings_dialog.ui" line="178"/>
<source>Logger</source>
@ -1236,6 +1246,11 @@
<source>userName</source>
<translation>اسم المستخدم:\nيضبط اسم حساب PS4، الذي قد يتم عرضه في بعض الألعاب.</translation>
</message>
<message>
<location filename="../settings_dialog.cpp"/>
<source>TrophyKey</source>
<translation>Trophy Key:\nKey used to decrypt trophies. Must be obtained from your jailbroken console.\nMust contain only hex characters.</translation>
</message>
<message>
<location filename="../settings_dialog.cpp" line="301"/>
<source>logTypeGroupBox</source>

View File

@ -537,6 +537,16 @@
<source>Username</source>
<translation>Username</translation>
</message>
<message>
<location filename="../settings_dialog.ui"/>
<source>Trophy Key</source>
<translation>Trophy Key</translation>
</message>
<message>
<location filename="../settings_dialog.ui"/>
<source>Trophy</source>
<translation>Trophy</translation>
</message>
<message>
<location filename="../settings_dialog.ui" line="178"/>
<source>Logger</source>
@ -1236,6 +1246,11 @@
<source>userName</source>
<translation>Brugernavn:\nIndstiller PS4-kontoens navn, som kan blive vist i nogle spil.</translation>
</message>
<message>
<location filename="../settings_dialog.cpp"/>
<source>TrophyKey</source>
<translation>Trophy Key:\nKey used to decrypt trophies. Must be obtained from your jailbroken console.\nMust contain only hex characters.</translation>
</message>
<message>
<location filename="../settings_dialog.cpp" line="301"/>
<source>logTypeGroupBox</source>

View File

@ -537,6 +537,16 @@
<source>Username</source>
<translation>Benutzername</translation>
</message>
<message>
<location filename="../settings_dialog.ui"/>
<source>Trophy Key</source>
<translation>Trophy Key</translation>
</message>
<message>
<location filename="../settings_dialog.ui"/>
<source>Trophy</source>
<translation>Trophy</translation>
</message>
<message>
<location filename="../settings_dialog.ui" line="178"/>
<source>Logger</source>
@ -1236,6 +1246,11 @@
<source>userName</source>
<translation>Benutzername:\nLegt den Namen des PS4-Kontos fest, der in einigen Spielen angezeigt werden kann.</translation>
</message>
<message>
<location filename="../settings_dialog.cpp"/>
<source>TrophyKey</source>
<translation>Trophy Key:\nKey used to decrypt trophies. Must be obtained from your jailbroken console.\nMust contain only hex characters.</translation>
</message>
<message>
<location filename="../settings_dialog.cpp" line="301"/>
<source>logTypeGroupBox</source>

View File

@ -537,6 +537,16 @@
<source>Username</source>
<translation>Username</translation>
</message>
<message>
<location filename="../settings_dialog.ui"/>
<source>Trophy Key</source>
<translation>Trophy Key</translation>
</message>
<message>
<location filename="../settings_dialog.ui"/>
<source>Trophy</source>
<translation>Trophy</translation>
</message>
<message>
<location filename="../settings_dialog.ui" line="178"/>
<source>Logger</source>
@ -1236,6 +1246,11 @@
<source>userName</source>
<translation>Όνομα Χρήστη:\nΟρίζει το όνομα του λογαριασμού PS4, το οποίο μπορεί να εμφανιστεί σε ορισμένα παιχνίδια.</translation>
</message>
<message>
<location filename="../settings_dialog.cpp"/>
<source>TrophyKey</source>
<translation>Trophy Key:\nKey used to decrypt trophies. Must be obtained from your jailbroken console.\nMust contain only hex characters.</translation>
</message>
<message>
<location filename="../settings_dialog.cpp" line="301"/>
<source>logTypeGroupBox</source>

View File

@ -537,6 +537,16 @@
<source>Username</source>
<translation>Username</translation>
</message>
<message>
<location filename="../settings_dialog.ui"/>
<source>Trophy Key</source>
<translation>Trophy Key</translation>
</message>
<message>
<location filename="../settings_dialog.ui"/>
<source>Trophy</source>
<translation>Trophy</translation>
</message>
<message>
<location filename="../settings_dialog.ui" line="178"/>
<source>Logger</source>
@ -1236,6 +1246,11 @@
<source>userName</source>
<translation>Username:\nSets the PS4's account username, which may be displayed by some games.</translation>
</message>
<message>
<location filename="../settings_dialog.cpp"/>
<source>TrophyKey</source>
<translation>Trophy Key:\nKey used to decrypt trophies. Must be obtained from your jailbroken console.\nMust contain only hex characters.</translation>
</message>
<message>
<location filename="../settings_dialog.cpp" line="301"/>
<source>logTypeGroupBox</source>

View File

@ -537,6 +537,16 @@
<source>Username</source>
<translation>Nombre de usuario</translation>
</message>
<message>
<location filename="../settings_dialog.ui"/>
<source>Trophy Key</source>
<translation>Trophy Key</translation>
</message>
<message>
<location filename="../settings_dialog.ui"/>
<source>Trophy</source>
<translation>Trophy</translation>
</message>
<message>
<location filename="../settings_dialog.ui" line="178"/>
<source>Logger</source>
@ -1236,6 +1246,11 @@
<source>userName</source>
<translation>Nombre de Usuario:\nEstablece el nombre de usuario de la cuenta de PS4, que puede ser mostrado por algunos juegos.</translation>
</message>
<message>
<location filename="../settings_dialog.cpp"/>
<source>TrophyKey</source>
<translation>Trophy Key:\nKey used to decrypt trophies. Must be obtained from your jailbroken console.\nMust contain only hex characters.</translation>
</message>
<message>
<location filename="../settings_dialog.cpp" line="301"/>
<source>logTypeGroupBox</source>

View File

@ -537,6 +537,16 @@
<source>Username</source>
<translation>نام کاربری</translation>
</message>
<message>
<location filename="../settings_dialog.ui"/>
<source>Trophy Key</source>
<translation>Trophy Key</translation>
</message>
<message>
<location filename="../settings_dialog.ui"/>
<source>Trophy</source>
<translation>Trophy</translation>
</message>
<message>
<location filename="../settings_dialog.ui" line="178"/>
<source>Logger</source>
@ -1236,6 +1246,11 @@
<source>userName</source>
<translation>نام کاربری:\ام کاربری حساب PS4 را تنظیم میکند که ممکن است توسط برخی بازیها نمایش داده شود.</translation>
</message>
<message>
<location filename="../settings_dialog.cpp"/>
<source>TrophyKey</source>
<translation>Trophy Key:\nKey used to decrypt trophies. Must be obtained from your jailbroken console.\nMust contain only hex characters.</translation>
</message>
<message>
<location filename="../settings_dialog.cpp" line="301"/>
<source>logTypeGroupBox</source>

View File

@ -537,6 +537,16 @@
<source>Username</source>
<translation>Username</translation>
</message>
<message>
<location filename="../settings_dialog.ui"/>
<source>Trophy Key</source>
<translation>Trophy Key</translation>
</message>
<message>
<location filename="../settings_dialog.ui"/>
<source>Trophy</source>
<translation>Trophy</translation>
</message>
<message>
<location filename="../settings_dialog.ui" line="178"/>
<source>Logger</source>
@ -1236,6 +1246,11 @@
<source>userName</source>
<translation>Käyttäjänimi:\nAsettaa PS4-tilin käyttäjänimen, joka voi näkyä joissakin peleissä.</translation>
</message>
<message>
<location filename="../settings_dialog.cpp"/>
<source>TrophyKey</source>
<translation>Trophy Key:\nKey used to decrypt trophies. Must be obtained from your jailbroken console.\nMust contain only hex characters.</translation>
</message>
<message>
<location filename="../settings_dialog.cpp" line="301"/>
<source>logTypeGroupBox</source>

View File

@ -537,6 +537,16 @@
<source>Username</source>
<translation>Nom d'utilisateur</translation>
</message>
<message>
<location filename="../settings_dialog.ui"/>
<source>Trophy Key</source>
<translation>Trophy Key</translation>
</message>
<message>
<location filename="../settings_dialog.ui"/>
<source>Trophy</source>
<translation>Trophy</translation>
</message>
<message>
<location filename="../settings_dialog.ui" line="178"/>
<source>Logger</source>
@ -1236,6 +1246,11 @@
<source>userName</source>
<translation>Nom d'utilisateur:\nDéfinit le nom d'utilisateur du compte PS4, qui peut être affiché par certains jeux.</translation>
</message>
<message>
<location filename="../settings_dialog.cpp"/>
<source>TrophyKey</source>
<translation>Trophy Key:\nKey used to decrypt trophies. Must be obtained from your jailbroken console.\nMust contain only hex characters.</translation>
</message>
<message>
<location filename="../settings_dialog.cpp" line="301"/>
<source>logTypeGroupBox</source>

View File

@ -537,6 +537,16 @@
<source>Username</source>
<translation>Felhasználónév</translation>
</message>
<message>
<location filename="../settings_dialog.ui"/>
<source>Trophy Key</source>
<translation>Trophy Key</translation>
</message>
<message>
<location filename="../settings_dialog.ui"/>
<source>Trophy</source>
<translation>Trophy</translation>
</message>
<message>
<location filename="../settings_dialog.ui" line="178"/>
<source>Logger</source>
@ -1236,6 +1246,11 @@
<source>userName</source>
<translation>Felhasználónév:\nBeállítja a PS4 fiók felhasználónevét, amelyet egyes játékok megjeleníthetnek.</translation>
</message>
<message>
<location filename="../settings_dialog.cpp"/>
<source>TrophyKey</source>
<translation>Trophy Key:\nKey used to decrypt trophies. Must be obtained from your jailbroken console.\nMust contain only hex characters.</translation>
</message>
<message>
<location filename="../settings_dialog.cpp" line="301"/>
<source>logTypeGroupBox</source>

View File

@ -537,6 +537,16 @@
<source>Username</source>
<translation>Username</translation>
</message>
<message>
<location filename="../settings_dialog.ui"/>
<source>Trophy Key</source>
<translation>Trophy Key</translation>
</message>
<message>
<location filename="../settings_dialog.ui"/>
<source>Trophy</source>
<translation>Trophy</translation>
</message>
<message>
<location filename="../settings_dialog.ui" line="178"/>
<source>Logger</source>
@ -1236,6 +1246,11 @@
<source>userName</source>
<translation>Nama Pengguna:\nMenetapkan nama pengguna akun PS4, yang mungkin ditampilkan oleh beberapa permainan.</translation>
</message>
<message>
<location filename="../settings_dialog.cpp"/>
<source>TrophyKey</source>
<translation>Trophy Key:\nKey used to decrypt trophies. Must be obtained from your jailbroken console.\nMust contain only hex characters.</translation>
</message>
<message>
<location filename="../settings_dialog.cpp" line="301"/>
<source>logTypeGroupBox</source>

View File

@ -537,6 +537,16 @@
<source>Username</source>
<translation>Nome Utente</translation>
</message>
<message>
<location filename="../settings_dialog.ui"/>
<source>Trophy Key</source>
<translation>Trophy Key</translation>
</message>
<message>
<location filename="../settings_dialog.ui"/>
<source>Trophy</source>
<translation>Trophy</translation>
</message>
<message>
<location filename="../settings_dialog.ui" line="178"/>
<source>Logger</source>
@ -1236,6 +1246,11 @@
<source>userName</source>
<translation>Nome Utente:\nImposta il nome utente dell'account PS4, che potrebbe essere visualizzato da alcuni giochi.</translation>
</message>
<message>
<location filename="../settings_dialog.cpp"/>
<source>TrophyKey</source>
<translation>Trophy Key:\nKey used to decrypt trophies. Must be obtained from your jailbroken console.\nMust contain only hex characters.</translation>
</message>
<message>
<location filename="../settings_dialog.cpp" line="301"/>
<source>logTypeGroupBox</source>

View File

@ -537,6 +537,16 @@
<source>Username</source>
<translation></translation>
</message>
<message>
<location filename="../settings_dialog.ui"/>
<source>Trophy Key</source>
<translation>Trophy Key</translation>
</message>
<message>
<location filename="../settings_dialog.ui"/>
<source>Trophy</source>
<translation>Trophy</translation>
</message>
<message>
<location filename="../settings_dialog.ui" line="178"/>
<source>Logger</source>
@ -1236,6 +1246,11 @@
<source>userName</source>
<translation>:\nPS4のアカウントユーザー名を設定します</translation>
</message>
<message>
<location filename="../settings_dialog.cpp"/>
<source>TrophyKey</source>
<translation>Trophy Key:\nKey used to decrypt trophies. Must be obtained from your jailbroken console.\nMust contain only hex characters.</translation>
</message>
<message>
<location filename="../settings_dialog.cpp" line="301"/>
<source>logTypeGroupBox</source>

View File

@ -537,6 +537,16 @@
<source>Username</source>
<translation>Username</translation>
</message>
<message>
<location filename="../settings_dialog.ui"/>
<source>Trophy Key</source>
<translation>Trophy Key</translation>
</message>
<message>
<location filename="../settings_dialog.ui"/>
<source>Trophy</source>
<translation>Trophy</translation>
</message>
<message>
<location filename="../settings_dialog.ui" line="178"/>
<source>Logger</source>
@ -1236,6 +1246,11 @@
<source>userName</source>
<translation>Username:\nSets the PS4's account username, which may be displayed by some games.</translation>
</message>
<message>
<location filename="../settings_dialog.cpp"/>
<source>TrophyKey</source>
<translation>Trophy Key:\nKey used to decrypt trophies. Must be obtained from your jailbroken console.\nMust contain only hex characters.</translation>
</message>
<message>
<location filename="../settings_dialog.cpp" line="301"/>
<source>logTypeGroupBox</source>

View File

@ -537,6 +537,16 @@
<source>Username</source>
<translation>Username</translation>
</message>
<message>
<location filename="../settings_dialog.ui"/>
<source>Trophy Key</source>
<translation>Trophy Key</translation>
</message>
<message>
<location filename="../settings_dialog.ui"/>
<source>Trophy</source>
<translation>Trophy</translation>
</message>
<message>
<location filename="../settings_dialog.ui" line="178"/>
<source>Logger</source>
@ -1236,6 +1246,11 @@
<source>userName</source>
<translation>Vartotojo vardas:\nNustato PS4 paskyros vartotojo vardą, kuris gali būti rodomas kai kuriuose žaidimuose.</translation>
</message>
<message>
<location filename="../settings_dialog.cpp"/>
<source>TrophyKey</source>
<translation>Trophy Key:\nKey used to decrypt trophies. Must be obtained from your jailbroken console.\nMust contain only hex characters.</translation>
</message>
<message>
<location filename="../settings_dialog.cpp" line="301"/>
<source>logTypeGroupBox</source>

View File

@ -537,6 +537,16 @@
<source>Username</source>
<translation>Brukernavn</translation>
</message>
<message>
<location filename="../settings_dialog.ui"/>
<source>Trophy Key</source>
<translation>Trophy Key</translation>
</message>
<message>
<location filename="../settings_dialog.ui"/>
<source>Trophy</source>
<translation>Trophy</translation>
</message>
<message>
<location filename="../settings_dialog.ui" line="178"/>
<source>Logger</source>
@ -1236,6 +1246,11 @@
<source>userName</source>
<translation>Brukernavn:\nAngir brukernavnet for PS4-kontoen, som kan vises av enkelte spill.</translation>
</message>
<message>
<location filename="../settings_dialog.cpp"/>
<source>TrophyKey</source>
<translation>Trophy Key:\nKey used to decrypt trophies. Must be obtained from your jailbroken console.\nMust contain only hex characters.</translation>
</message>
<message>
<location filename="../settings_dialog.cpp" line="301"/>
<source>logTypeGroupBox</source>

View File

@ -537,6 +537,16 @@
<source>Username</source>
<translation>Username</translation>
</message>
<message>
<location filename="../settings_dialog.ui"/>
<source>Trophy Key</source>
<translation>Trophy Key</translation>
</message>
<message>
<location filename="../settings_dialog.ui"/>
<source>Trophy</source>
<translation>Trophy</translation>
</message>
<message>
<location filename="../settings_dialog.ui" line="178"/>
<source>Logger</source>
@ -1236,6 +1246,11 @@
<source>userName</source>
<translation>Gebruikersnaam:\nStelt de gebruikersnaam van het PS4-account in, die door sommige games kan worden weergegeven.</translation>
</message>
<message>
<location filename="../settings_dialog.cpp"/>
<source>TrophyKey</source>
<translation>Trophy Key:\nKey used to decrypt trophies. Must be obtained from your jailbroken console.\nMust contain only hex characters.</translation>
</message>
<message>
<location filename="../settings_dialog.cpp" line="301"/>
<source>logTypeGroupBox</source>

View File

@ -537,6 +537,16 @@
<source>Username</source>
<translation>Nazwa użytkownika</translation>
</message>
<message>
<location filename="../settings_dialog.ui"/>
<source>Trophy Key</source>
<translation>Trophy Key</translation>
</message>
<message>
<location filename="../settings_dialog.ui"/>
<source>Trophy</source>
<translation>Trophy</translation>
</message>
<message>
<location filename="../settings_dialog.ui" line="178"/>
<source>Logger</source>
@ -1236,6 +1246,11 @@
<source>userName</source>
<translation>Nazwa użytkownika:\nUstala nazwę użytkownika konta PS4, która może być wyświetlana w niektórych grach.</translation>
</message>
<message>
<location filename="../settings_dialog.cpp"/>
<source>TrophyKey</source>
<translation>Trophy Key:\nKey used to decrypt trophies. Must be obtained from your jailbroken console.\nMust contain only hex characters.</translation>
</message>
<message>
<location filename="../settings_dialog.cpp" line="301"/>
<source>logTypeGroupBox</source>

View File

@ -537,6 +537,16 @@
<source>Username</source>
<translation>Nome de usuário</translation>
</message>
<message>
<location filename="../settings_dialog.ui"/>
<source>Trophy Key</source>
<translation>Trophy Key</translation>
</message>
<message>
<location filename="../settings_dialog.ui"/>
<source>Trophy</source>
<translation>Troféus</translation>
</message>
<message>
<location filename="../settings_dialog.ui" line="178"/>
<source>Logger</source>
@ -1236,6 +1246,11 @@
<source>userName</source>
<translation>Nome de usuário:\nDefine o nome de usuário da conta PS4 que pode ser exibido por alguns jogos.</translation>
</message>
<message>
<location filename="../settings_dialog.cpp"/>
<source>TrophyKey</source>
<translation>Trophy Key:\nKey used to decrypt trophies. Must be obtained from your jailbroken console.\nMust contain only hex characters.</translation>
</message>
<message>
<location filename="../settings_dialog.cpp" line="301"/>
<source>logTypeGroupBox</source>

View File

@ -537,6 +537,16 @@
<source>Username</source>
<translation>Username</translation>
</message>
<message>
<location filename="../settings_dialog.ui"/>
<source>Trophy Key</source>
<translation>Trophy Key</translation>
</message>
<message>
<location filename="../settings_dialog.ui"/>
<source>Trophy</source>
<translation>Trophy</translation>
</message>
<message>
<location filename="../settings_dialog.ui" line="178"/>
<source>Logger</source>
@ -1236,6 +1246,11 @@
<source>userName</source>
<translation>Nume utilizator:\nSetează numele de utilizator al contului PS4, care poate fi afișat de unele jocuri.</translation>
</message>
<message>
<location filename="../settings_dialog.cpp"/>
<source>TrophyKey</source>
<translation>Trophy Key:\nKey used to decrypt trophies. Must be obtained from your jailbroken console.\nMust contain only hex characters.</translation>
</message>
<message>
<location filename="../settings_dialog.cpp" line="301"/>
<source>logTypeGroupBox</source>

View File

@ -537,6 +537,16 @@
<source>Username</source>
<translation>Имя пользователя</translation>
</message>
<message>
<location filename="../settings_dialog.ui"/>
<source>Trophy Key</source>
<translation>Trophy Key</translation>
</message>
<message>
<location filename="../settings_dialog.ui"/>
<source>Trophy</source>
<translation>Trophy</translation>
</message>
<message>
<location filename="../settings_dialog.ui" line="178"/>
<source>Logger</source>
@ -1236,6 +1246,11 @@
<source>userName</source>
<translation>Имя пользователя:\nУстановите имя пользователя аккаунта PS4. Это может отображаться в некоторых играх.</translation>
</message>
<message>
<location filename="../settings_dialog.cpp"/>
<source>TrophyKey</source>
<translation>Trophy Key:\nKey used to decrypt trophies. Must be obtained from your jailbroken console.\nMust contain only hex characters.</translation>
</message>
<message>
<location filename="../settings_dialog.cpp" line="301"/>
<source>logTypeGroupBox</source>

View File

@ -537,6 +537,16 @@
<source>Username</source>
<translation>Përdoruesi</translation>
</message>
<message>
<location filename="../settings_dialog.ui"/>
<source>Trophy Key</source>
<translation>Trophy Key</translation>
</message>
<message>
<location filename="../settings_dialog.ui"/>
<source>Trophy</source>
<translation>Trophy</translation>
</message>
<message>
<location filename="../settings_dialog.ui" line="178"/>
<source>Logger</source>
@ -1236,6 +1246,11 @@
<source>userName</source>
<translation>Përdoruesi:\nPërcakton emrin e përdoruesit llogarisë PS4, i cili mund shfaqet nga disa lojra.</translation>
</message>
<message>
<location filename="../settings_dialog.cpp"/>
<source>TrophyKey</source>
<translation>Trophy Key:\nKey used to decrypt trophies. Must be obtained from your jailbroken console.\nMust contain only hex characters.</translation>
</message>
<message>
<location filename="../settings_dialog.cpp" line="301"/>
<source>logTypeGroupBox</source>

View File

@ -537,6 +537,16 @@
<source>Username</source>
<translation>Kullanıcı Adı</translation>
</message>
<message>
<location filename="../settings_dialog.ui"/>
<source>Trophy Key</source>
<translation>Trophy Key</translation>
</message>
<message>
<location filename="../settings_dialog.ui"/>
<source>Trophy</source>
<translation>Trophy</translation>
</message>
<message>
<location filename="../settings_dialog.ui" line="178"/>
<source>Logger</source>
@ -1236,6 +1246,11 @@
<source>userName</source>
<translation>Kullanıcı Adı:\nBazı oyunlar tarafından gösterilebilen PS4 hesabının kullanıcı adını ayarlar.</translation>
</message>
<message>
<location filename="../settings_dialog.cpp"/>
<source>TrophyKey</source>
<translation>Trophy Key:\nKey used to decrypt trophies. Must be obtained from your jailbroken console.\nMust contain only hex characters.</translation>
</message>
<message>
<location filename="../settings_dialog.cpp" line="301"/>
<source>logTypeGroupBox</source>

View File

@ -537,6 +537,16 @@
<source>Username</source>
<translation>Ім'я користувача</translation>
</message>
<message>
<location filename="../settings_dialog.ui"/>
<source>Trophy Key</source>
<translation>Trophy Key</translation>
</message>
<message>
<location filename="../settings_dialog.ui"/>
<source>Trophy</source>
<translation>Trophy</translation>
</message>
<message>
<location filename="../settings_dialog.ui" line="178"/>
<source>Logger</source>
@ -1236,6 +1246,11 @@
<source>userName</source>
<translation>Ім'я користувача:\nВстановіть ім'я користувача акаунта PS4. Це може відображатися в деяких іграх.</translation>
</message>
<message>
<location filename="../settings_dialog.cpp"/>
<source>TrophyKey</source>
<translation>Trophy Key:\nKey used to decrypt trophies. Must be obtained from your jailbroken console.\nMust contain only hex characters.</translation>
</message>
<message>
<location filename="../settings_dialog.cpp" line="301"/>
<source>logTypeGroupBox</source>

View File

@ -537,6 +537,16 @@
<source>Username</source>
<translation>Username</translation>
</message>
<message>
<location filename="../settings_dialog.ui"/>
<source>Trophy Key</source>
<translation>Trophy Key</translation>
</message>
<message>
<location filename="../settings_dialog.ui"/>
<source>Trophy</source>
<translation>Trophy</translation>
</message>
<message>
<location filename="../settings_dialog.ui" line="178"/>
<source>Logger</source>
@ -1236,6 +1246,11 @@
<source>userName</source>
<translation>Tên người dùng:\nChọn tên người dùng của tài khoản PS4, thể đưc một số trò chơi hiển thị.</translation>
</message>
<message>
<location filename="../settings_dialog.cpp"/>
<source>TrophyKey</source>
<translation>Trophy Key:\nKey used to decrypt trophies. Must be obtained from your jailbroken console.\nMust contain only hex characters.</translation>
</message>
<message>
<location filename="../settings_dialog.cpp" line="301"/>
<source>logTypeGroupBox</source>

View File

@ -537,6 +537,16 @@
<source>Username</source>
<translation></translation>
</message>
<message>
<location filename="../settings_dialog.ui"/>
<source>Trophy Key</source>
<translation>Trophy Key</translation>
</message>
<message>
<location filename="../settings_dialog.ui"/>
<source>Trophy</source>
<translation>Trophy</translation>
</message>
<message>
<location filename="../settings_dialog.ui" line="178"/>
<source>Logger</source>
@ -1236,6 +1246,11 @@
<source>userName</source>
<translation>\n设置 PS4 </translation>
</message>
<message>
<location filename="../settings_dialog.cpp"/>
<source>TrophyKey</source>
<translation>Trophy Key:\nKey used to decrypt trophies. Must be obtained from your jailbroken console.\nMust contain only hex characters.</translation>
</message>
<message>
<location filename="../settings_dialog.cpp" line="301"/>
<source>logTypeGroupBox</source>

View File

@ -537,6 +537,16 @@
<source>Username</source>
<translation>Username</translation>
</message>
<message>
<location filename="../settings_dialog.ui"/>
<source>Trophy Key</source>
<translation>Trophy Key</translation>
</message>
<message>
<location filename="../settings_dialog.ui"/>
<source>Trophy</source>
<translation>Trophy</translation>
</message>
<message>
<location filename="../settings_dialog.ui" line="178"/>
<source>Logger</source>
@ -1236,6 +1246,11 @@
<source>userName</source>
<translation>:\n設定PS4帳號的用戶名</translation>
</message>
<message>
<location filename="../settings_dialog.cpp"/>
<source>TrophyKey</source>
<translation>Trophy Key:\nKey used to decrypt trophies. Must be obtained from your jailbroken console.\nMust contain only hex characters.</translation>
</message>
<message>
<location filename="../settings_dialog.cpp" line="301"/>
<source>logTypeGroupBox</source>

View File

@ -161,6 +161,20 @@ void WindowSDL::WaitEvent() {
case SDL_EVENT_GAMEPAD_TOUCHPAD_MOTION:
OnGamepadEvent(&event);
break;
// i really would have appreciated ANY KIND OF DOCUMENTATION ON THIS
// AND IT DOESN'T EVEN USE PROPER ENUMS
case SDL_EVENT_GAMEPAD_SENSOR_UPDATE:
switch ((SDL_SensorType)event.gsensor.sensor) {
case SDL_SENSOR_GYRO:
controller->Gyro(0, event.gsensor.data);
break;
case SDL_SENSOR_ACCEL:
controller->Acceleration(0, event.gsensor.data);
break;
default:
break;
}
break;
case SDL_EVENT_QUIT:
is_open = false;
break;

View File

@ -205,7 +205,6 @@ void Translator::DS_WRITE(int bit_size, bool is_signed, bool is_pair, bool strid
addr, ir.Imm32((u32(inst.control.ds.offset1) << 8u) + u32(inst.control.ds.offset0)));
ir.WriteShared(bit_size, ir.GetVectorReg(data0), addr0);
}
emit_ds_read_barrier = true;
}
void Translator::DS_SWIZZLE_B32(const GcnInst& inst) {
@ -222,11 +221,6 @@ void Translator::DS_SWIZZLE_B32(const GcnInst& inst) {
void Translator::DS_READ(int bit_size, bool is_signed, bool is_pair, bool stride64,
const GcnInst& inst) {
if (emit_ds_read_barrier && profile.needs_lds_barriers) {
ir.Barrier();
emit_ds_read_barrier = false;
}
const IR::U32 addr{ir.GetVectorReg(IR::VectorReg(inst.src[0].code))};
IR::VectorReg dst_reg{inst.dst[0].code};
if (is_pair) {

View File

@ -308,7 +308,6 @@ private:
const RuntimeInfo& runtime_info;
const Profile& profile;
bool opcode_missing = false;
bool emit_ds_read_barrier = false;
};
void Translate(IR::Block* block, u32 block_base, std::span<const GcnInst> inst_list, Info& info,

View File

@ -8,6 +8,54 @@
namespace Shader::Optimization {
static void EmitBarrierInBlock(IR::Block* block) {
// This is inteded to insert a barrier when shared memory write and read
// occur in the same basic block. Also checks if branch depth is zero as
// we don't want to insert barrier in potentially divergent code.
bool emit_barrier_on_write = false;
bool emit_barrier_on_read = false;
const auto emit_barrier = [block](bool& emit_cond, IR::Inst& inst) {
if (emit_cond) {
IR::IREmitter ir{*block, IR::Block::InstructionList::s_iterator_to(inst)};
ir.Barrier();
emit_cond = false;
}
};
for (IR::Inst& inst : block->Instructions()) {
if (inst.GetOpcode() == IR::Opcode::LoadSharedU32 ||
inst.GetOpcode() == IR::Opcode::LoadSharedU64) {
emit_barrier(emit_barrier_on_read, inst);
emit_barrier_on_write = true;
}
if (inst.GetOpcode() == IR::Opcode::WriteSharedU32 ||
inst.GetOpcode() == IR::Opcode::WriteSharedU64) {
emit_barrier(emit_barrier_on_write, inst);
emit_barrier_on_read = true;
}
}
}
static void EmitBarrierInMergeBlock(const IR::AbstractSyntaxNode::Data& data) {
// Insert a barrier after divergent conditional blocks.
// This avoids potential softlocks and crashes when some threads
// initialize shared memory and others read from it.
const IR::U1 cond = data.if_node.cond;
const auto insert_barrier =
IR::BreadthFirstSearch(cond, [](IR::Inst* inst) -> std::optional<bool> {
if (inst->GetOpcode() == IR::Opcode::GetAttributeU32 &&
inst->Arg(0).Attribute() == IR::Attribute::LocalInvocationId) {
return true;
}
return std::nullopt;
});
if (insert_barrier) {
IR::Block* const merge = data.if_node.merge;
auto insert_point = std::ranges::find_if_not(merge->Instructions(), IR::IsPhi);
IR::IREmitter ir{*merge, insert_point};
ir.Barrier();
}
}
void SharedMemoryBarrierPass(IR::Program& program, const Profile& profile) {
if (!program.info.uses_shared || !profile.needs_lds_barriers) {
return;
@ -19,27 +67,12 @@ void SharedMemoryBarrierPass(IR::Program& program, const Profile& profile) {
--branch_depth;
continue;
}
if (node.type != Type::If) {
if (node.type == Type::If && branch_depth++ == 0) {
EmitBarrierInMergeBlock(node.data);
continue;
}
u32 curr_depth = branch_depth++;
if (curr_depth != 0) {
continue;
}
const IR::U1 cond = node.data.if_node.cond;
const auto insert_barrier =
IR::BreadthFirstSearch(cond, [](IR::Inst* inst) -> std::optional<bool> {
if (inst->GetOpcode() == IR::Opcode::GetAttributeU32 &&
inst->Arg(0).Attribute() == IR::Attribute::LocalInvocationId) {
return true;
}
return std::nullopt;
});
if (insert_barrier) {
IR::Block* const merge = node.data.if_node.merge;
auto insert_point = std::ranges::find_if_not(merge->Instructions(), IR::IsPhi);
IR::IREmitter ir{*merge, insert_point};
ir.Barrier();
if (node.type == Type::Block && branch_depth == 0) {
EmitBarrierInBlock(node.data.block);
}
}
}

View File

@ -815,7 +815,8 @@ Liverpool::Task Liverpool::ProcessCompute(std::span<const u32> acb, u32 vqid) {
}
if (rasterizer && (cs_program.dispatch_initiator & 1)) {
const auto cmd_address = reinterpret_cast<const void*>(header);
rasterizer->ScopeMarkerBegin(fmt::format("acb[{}]:{}:Dispatch", vqid, cmd_address));
rasterizer->ScopeMarkerBegin(
fmt::format("acb[{}]:{}:DispatchIndirect", vqid, cmd_address));
rasterizer->DispatchDirect();
rasterizer->ScopeMarkerEnd();
}

View File

@ -889,11 +889,11 @@ struct Liverpool {
return !info.linear_general;
}
[[nodiscard]] DataFormat DataFormat() const {
[[nodiscard]] DataFormat GetDataFmt() const {
return RemapDataFormat(info.format);
}
[[nodiscard]] NumberFormat NumFormat() const {
[[nodiscard]] NumberFormat GetNumberFmt() const {
// There is a small difference between T# and CB number types, account for it.
return RemapNumberFormat(info.number_type == NumberFormat::SnormNz
? NumberFormat::Srgb

View File

@ -79,21 +79,23 @@ inline NumberFormat RemapNumberFormat(const NumberFormat format) {
inline CompMapping RemapComponents(const DataFormat format, const CompMapping components) {
switch (format) {
case DataFormat::Format11_11_10:
return {
.r = components.b,
.g = components.g,
.b = components.r,
.a = components.a,
};
case DataFormat::Format11_11_10: {
CompMapping result;
result.r = components.b;
result.g = components.g;
result.b = components.r;
result.a = components.a;
return result;
}
case DataFormat::Format10_10_10_2:
case DataFormat::Format5_5_5_1:
return {
.r = components.a,
.g = components.b,
.b = components.g,
.a = components.r,
};
case DataFormat::Format5_5_5_1: {
CompMapping result;
result.r = components.a;
result.g = components.b;
result.b = components.g;
result.a = components.r;
return result;
}
default:
return components;
}

View File

@ -54,18 +54,10 @@ BufferCache::BufferCache(const Vulkan::Instance& instance_, Vulkan::Scheduler& s
BufferCache::~BufferCache() = default;
void BufferCache::InvalidateMemory(VAddr device_addr, u64 size) {
std::scoped_lock lk{mutex};
const bool is_tracked = IsRegionRegistered(device_addr, size);
if (!is_tracked) {
return;
}
// Mark the page as CPU modified to stop tracking writes.
SCOPE_EXIT {
if (is_tracked) {
// Mark the page as CPU modified to stop tracking writes.
memory_tracker.MarkRegionAsCpuModified(device_addr, size);
};
if (!memory_tracker.IsRegionGpuModified(device_addr, size)) {
// Page has not been modified by the GPU, nothing to do.
return;
}
}
@ -267,7 +259,16 @@ void BufferCache::InlineData(VAddr address, const void* value, u32 num_bytes, bo
const BufferId buffer_id = FindBuffer(address, num_bytes);
return &slot_buffers[buffer_id];
}();
const vk::BufferMemoryBarrier2 buf_barrier = {
const vk::BufferMemoryBarrier2 pre_barrier = {
.srcStageMask = vk::PipelineStageFlagBits2::eAllCommands,
.srcAccessMask = vk::AccessFlagBits2::eMemoryRead,
.dstStageMask = vk::PipelineStageFlagBits2::eTransfer,
.dstAccessMask = vk::AccessFlagBits2::eTransferWrite,
.buffer = buffer->Handle(),
.offset = buffer->Offset(address),
.size = num_bytes,
};
const vk::BufferMemoryBarrier2 post_barrier = {
.srcStageMask = vk::PipelineStageFlagBits2::eTransfer,
.srcAccessMask = vk::AccessFlagBits2::eTransferWrite,
.dstStageMask = vk::PipelineStageFlagBits2::eAllCommands,
@ -279,9 +280,14 @@ void BufferCache::InlineData(VAddr address, const void* value, u32 num_bytes, bo
cmdbuf.pipelineBarrier2(vk::DependencyInfo{
.dependencyFlags = vk::DependencyFlagBits::eByRegion,
.bufferMemoryBarrierCount = 1,
.pBufferMemoryBarriers = &buf_barrier,
.pBufferMemoryBarriers = &pre_barrier,
});
cmdbuf.updateBuffer(buffer->Handle(), buffer->Offset(address), num_bytes, value);
cmdbuf.pipelineBarrier2(vk::DependencyInfo{
.dependencyFlags = vk::DependencyFlagBits::eByRegion,
.bufferMemoryBarrierCount = 1,
.pBufferMemoryBarriers = &post_barrier,
});
cmdbuf.updateBuffer(buffer->Handle(), buf_barrier.offset, num_bytes, value);
}
std::pair<Buffer*, u32> BufferCache::ObtainHostUBO(std::span<const u32> data) {
@ -346,6 +352,7 @@ bool BufferCache::IsRegionRegistered(VAddr addr, size_t size) {
++page;
continue;
}
std::shared_lock lk{mutex};
Buffer& buffer = slot_buffers[buffer_id];
const VAddr buf_start_addr = buffer.CpuAddr();
const VAddr buf_end_addr = buf_start_addr + buffer.SizeBytes();
@ -472,21 +479,48 @@ void BufferCache::JoinOverlap(BufferId new_buffer_id, BufferId overlap_id,
};
scheduler.EndRendering();
const auto cmdbuf = scheduler.CommandBuffer();
static constexpr vk::MemoryBarrier READ_BARRIER{
.srcAccessMask = vk::AccessFlagBits::eMemoryWrite,
.dstAccessMask = vk::AccessFlagBits::eTransferRead | vk::AccessFlagBits::eTransferWrite,
const std::array pre_barriers = {
vk::BufferMemoryBarrier2{
.srcStageMask = vk::PipelineStageFlagBits2::eAllCommands,
.srcAccessMask = vk::AccessFlagBits2::eMemoryRead | vk::AccessFlagBits2::eMemoryWrite,
.dstStageMask = vk::PipelineStageFlagBits2::eTransfer,
.dstAccessMask = vk::AccessFlagBits2::eTransferRead,
.buffer = overlap.Handle(),
.offset = 0,
.size = overlap.SizeBytes(),
},
};
static constexpr vk::MemoryBarrier WRITE_BARRIER{
.srcAccessMask = vk::AccessFlagBits::eTransferWrite,
.dstAccessMask = vk::AccessFlagBits::eMemoryRead | vk::AccessFlagBits::eMemoryWrite,
const std::array post_barriers = {
vk::BufferMemoryBarrier2{
.srcStageMask = vk::PipelineStageFlagBits2::eTransfer,
.srcAccessMask = vk::AccessFlagBits2::eTransferRead,
.dstStageMask = vk::PipelineStageFlagBits2::eAllCommands,
.dstAccessMask = vk::AccessFlagBits2::eMemoryWrite,
.buffer = overlap.Handle(),
.offset = 0,
.size = overlap.SizeBytes(),
},
vk::BufferMemoryBarrier2{
.srcStageMask = vk::PipelineStageFlagBits2::eTransfer,
.srcAccessMask = vk::AccessFlagBits2::eTransferWrite,
.dstStageMask = vk::PipelineStageFlagBits2::eAllCommands,
.dstAccessMask = vk::AccessFlagBits2::eMemoryRead | vk::AccessFlagBits2::eMemoryWrite,
.buffer = new_buffer.Handle(),
.offset = dst_base_offset,
.size = overlap.SizeBytes(),
},
};
cmdbuf.pipelineBarrier(vk::PipelineStageFlagBits::eAllCommands,
vk::PipelineStageFlagBits::eTransfer, vk::DependencyFlagBits::eByRegion,
READ_BARRIER, {}, {});
cmdbuf.copyBuffer(overlap.buffer, new_buffer.buffer, copy);
cmdbuf.pipelineBarrier(vk::PipelineStageFlagBits::eTransfer,
vk::PipelineStageFlagBits::eAllCommands,
vk::DependencyFlagBits::eByRegion, WRITE_BARRIER, {}, {});
cmdbuf.pipelineBarrier2(vk::DependencyInfo{
.dependencyFlags = vk::DependencyFlagBits::eByRegion,
.bufferMemoryBarrierCount = 1,
.pBufferMemoryBarriers = pre_barriers.data(),
});
cmdbuf.copyBuffer(overlap.Handle(), new_buffer.Handle(), copy);
cmdbuf.pipelineBarrier2(vk::DependencyInfo{
.dependencyFlags = vk::DependencyFlagBits::eByRegion,
.bufferMemoryBarrierCount = static_cast<u32>(post_barriers.size()),
.pBufferMemoryBarriers = post_barriers.data(),
});
DeleteBuffer(overlap_id);
}
@ -496,8 +530,11 @@ BufferId BufferCache::CreateBuffer(VAddr device_addr, u32 wanted_size) {
wanted_size = static_cast<u32>(device_addr_end - device_addr);
const OverlapResult overlap = ResolveOverlaps(device_addr, wanted_size);
const u32 size = static_cast<u32>(overlap.end - overlap.begin);
const BufferId new_buffer_id = slot_buffers.insert(
instance, scheduler, MemoryUsage::DeviceLocal, overlap.begin, AllFlags, size);
const BufferId new_buffer_id = [&] {
std::scoped_lock lk{mutex};
return slot_buffers.insert(instance, scheduler, MemoryUsage::DeviceLocal, overlap.begin,
AllFlags, size);
}();
auto& new_buffer = slot_buffers[new_buffer_id];
const size_t size_bytes = new_buffer.SizeBytes();
const auto cmdbuf = scheduler.CommandBuffer();
@ -537,10 +574,8 @@ void BufferCache::ChangeRegister(BufferId buffer_id) {
void BufferCache::SynchronizeBuffer(Buffer& buffer, VAddr device_addr, u32 size,
bool is_texel_buffer) {
std::scoped_lock lk{mutex};
boost::container::small_vector<vk::BufferCopy, 4> copies;
u64 total_size_bytes = 0;
u64 largest_copy = 0;
VAddr buffer_start = buffer.CpuAddr();
memory_tracker.ForEachUploadRange(device_addr, size, [&](u64 device_addr_out, u64 range_size) {
copies.push_back(vk::BufferCopy{
@ -549,7 +584,6 @@ void BufferCache::SynchronizeBuffer(Buffer& buffer, VAddr device_addr, u32 size,
.size = range_size,
});
total_size_bytes += range_size;
largest_copy = std::max(largest_copy, range_size);
});
SCOPE_EXIT {
if (is_texel_buffer) {
@ -590,21 +624,35 @@ void BufferCache::SynchronizeBuffer(Buffer& buffer, VAddr device_addr, u32 size,
}
scheduler.EndRendering();
const auto cmdbuf = scheduler.CommandBuffer();
static constexpr vk::MemoryBarrier READ_BARRIER{
.srcAccessMask = vk::AccessFlagBits::eMemoryWrite,
.dstAccessMask = vk::AccessFlagBits::eTransferRead | vk::AccessFlagBits::eTransferWrite,
const vk::BufferMemoryBarrier2 pre_barrier = {
.srcStageMask = vk::PipelineStageFlagBits2::eAllCommands,
.srcAccessMask = vk::AccessFlagBits2::eMemoryRead,
.dstStageMask = vk::PipelineStageFlagBits2::eTransfer,
.dstAccessMask = vk::AccessFlagBits2::eTransferWrite,
.buffer = buffer.Handle(),
.offset = 0,
.size = buffer.SizeBytes(),
};
static constexpr vk::MemoryBarrier WRITE_BARRIER{
.srcAccessMask = vk::AccessFlagBits::eTransferWrite,
.dstAccessMask = vk::AccessFlagBits::eMemoryRead | vk::AccessFlagBits::eMemoryWrite,
const vk::BufferMemoryBarrier2 post_barrier = {
.srcStageMask = vk::PipelineStageFlagBits2::eTransfer,
.srcAccessMask = vk::AccessFlagBits2::eTransferWrite,
.dstStageMask = vk::PipelineStageFlagBits2::eAllCommands,
.dstAccessMask = vk::AccessFlagBits2::eMemoryRead | vk::AccessFlagBits2::eMemoryWrite,
.buffer = buffer.Handle(),
.offset = 0,
.size = buffer.SizeBytes(),
};
cmdbuf.pipelineBarrier(vk::PipelineStageFlagBits::eAllCommands,
vk::PipelineStageFlagBits::eTransfer, vk::DependencyFlagBits::eByRegion,
READ_BARRIER, {}, {});
cmdbuf.pipelineBarrier2(vk::DependencyInfo{
.dependencyFlags = vk::DependencyFlagBits::eByRegion,
.bufferMemoryBarrierCount = 1,
.pBufferMemoryBarriers = &pre_barrier,
});
cmdbuf.copyBuffer(src_buffer, buffer.buffer, copies);
cmdbuf.pipelineBarrier(vk::PipelineStageFlagBits::eTransfer,
vk::PipelineStageFlagBits::eAllCommands,
vk::DependencyFlagBits::eByRegion, WRITE_BARRIER, {}, {});
cmdbuf.pipelineBarrier2(vk::DependencyInfo{
.dependencyFlags = vk::DependencyFlagBits::eByRegion,
.bufferMemoryBarrierCount = 1,
.pBufferMemoryBarriers = &post_barrier,
});
}
bool BufferCache::SynchronizeBufferFromImage(Buffer& buffer, VAddr device_addr, u32 size) {
@ -654,10 +702,42 @@ bool BufferCache::SynchronizeBufferFromImage(Buffer& buffer, VAddr device_addr,
}
if (!copies.empty()) {
scheduler.EndRendering();
image.Transit(vk::ImageLayout::eTransferSrcOptimal, vk::AccessFlagBits2::eTransferRead, {});
const vk::BufferMemoryBarrier2 pre_barrier = {
.srcStageMask = vk::PipelineStageFlagBits2::eAllCommands,
.srcAccessMask = vk::AccessFlagBits2::eMemoryRead,
.dstStageMask = vk::PipelineStageFlagBits2::eTransfer,
.dstAccessMask = vk::AccessFlagBits2::eTransferWrite,
.buffer = buffer.Handle(),
.offset = max_offset - size,
.size = size,
};
const vk::BufferMemoryBarrier2 post_barrier = {
.srcStageMask = vk::PipelineStageFlagBits2::eTransfer,
.srcAccessMask = vk::AccessFlagBits2::eTransferWrite,
.dstStageMask = vk::PipelineStageFlagBits2::eAllCommands,
.dstAccessMask = vk::AccessFlagBits2::eMemoryRead,
.buffer = buffer.Handle(),
.offset = max_offset - size,
.size = size,
};
auto barriers = image.GetBarriers(vk::ImageLayout::eTransferSrcOptimal,
vk::AccessFlagBits2::eTransferRead,
vk::PipelineStageFlagBits2::eTransfer, {});
const auto cmdbuf = scheduler.CommandBuffer();
cmdbuf.copyImageToBuffer(image.image, vk::ImageLayout::eTransferSrcOptimal, buffer.buffer,
cmdbuf.pipelineBarrier2(vk::DependencyInfo{
.dependencyFlags = vk::DependencyFlagBits::eByRegion,
.bufferMemoryBarrierCount = 1,
.pBufferMemoryBarriers = &pre_barrier,
.imageMemoryBarrierCount = static_cast<u32>(barriers.size()),
.pImageMemoryBarriers = barriers.data(),
});
cmdbuf.copyImageToBuffer(image.image, vk::ImageLayout::eTransferSrcOptimal, buffer.Handle(),
copies);
cmdbuf.pipelineBarrier2(vk::DependencyInfo{
.dependencyFlags = vk::DependencyFlagBits::eByRegion,
.bufferMemoryBarrierCount = 1,
.pBufferMemoryBarriers = &post_barrier,
});
}
return true;
}

View File

@ -3,7 +3,7 @@
#pragma once
#include <mutex>
#include <shared_mutex>
#include <boost/container/small_vector.hpp>
#include <boost/icl/interval_map.hpp>
#include <tsl/robin_map.h>
@ -157,7 +157,7 @@ private:
StreamBuffer staging_buffer;
StreamBuffer stream_buffer;
Buffer gds_buffer;
std::mutex mutex;
std::shared_mutex mutex;
Common::SlotVector<Buffer> slot_buffers;
RangeSet gpu_modified_ranges;
vk::BufferView null_buffer_view;

View File

@ -15,13 +15,8 @@ namespace VideoCore {
class MemoryTracker {
public:
static constexpr size_t MAX_CPU_PAGE_BITS = 40;
static constexpr size_t HIGHER_PAGE_BITS = 22;
static constexpr size_t HIGHER_PAGE_SIZE = 1ULL << HIGHER_PAGE_BITS;
static constexpr size_t HIGHER_PAGE_MASK = HIGHER_PAGE_SIZE - 1ULL;
static constexpr size_t NUM_HIGH_PAGES = 1ULL << (MAX_CPU_PAGE_BITS - HIGHER_PAGE_BITS);
static constexpr size_t MANAGER_POOL_SIZE = 32;
static constexpr size_t WORDS_STACK_NEEDED = HIGHER_PAGE_SIZE / BYTES_PER_WORD;
using Manager = WordManager<WORDS_STACK_NEEDED>;
public:
explicit MemoryTracker(PageManager* tracker_) : tracker{tracker_} {}
@ -30,7 +25,7 @@ public:
/// Returns true if a region has been modified from the CPU
[[nodiscard]] bool IsRegionCpuModified(VAddr query_cpu_addr, u64 query_size) noexcept {
return IteratePages<true>(
query_cpu_addr, query_size, [](Manager* manager, u64 offset, size_t size) {
query_cpu_addr, query_size, [](RegionManager* manager, u64 offset, size_t size) {
return manager->template IsRegionModified<Type::CPU>(offset, size);
});
}
@ -38,52 +33,34 @@ public:
/// Returns true if a region has been modified from the GPU
[[nodiscard]] bool IsRegionGpuModified(VAddr query_cpu_addr, u64 query_size) noexcept {
return IteratePages<false>(
query_cpu_addr, query_size, [](Manager* manager, u64 offset, size_t size) {
query_cpu_addr, query_size, [](RegionManager* manager, u64 offset, size_t size) {
return manager->template IsRegionModified<Type::GPU>(offset, size);
});
}
/// Mark region as CPU modified, notifying the device_tracker about this change
void MarkRegionAsCpuModified(VAddr dirty_cpu_addr, u64 query_size) {
IteratePages<true>(dirty_cpu_addr, query_size,
[](Manager* manager, u64 offset, size_t size) {
manager->template ChangeRegionState<Type::CPU, true>(
manager->GetCpuAddr() + offset, size);
});
}
/// Unmark region as CPU modified, notifying the device_tracker about this change
void UnmarkRegionAsCpuModified(VAddr dirty_cpu_addr, u64 query_size) {
IteratePages<true>(dirty_cpu_addr, query_size,
[](Manager* manager, u64 offset, size_t size) {
manager->template ChangeRegionState<Type::CPU, false>(
manager->GetCpuAddr() + offset, size);
});
IteratePages<false>(dirty_cpu_addr, query_size,
[](RegionManager* manager, u64 offset, size_t size) {
manager->template ChangeRegionState<Type::CPU, true>(
manager->GetCpuAddr() + offset, size);
});
}
/// Mark region as modified from the host GPU
void MarkRegionAsGpuModified(VAddr dirty_cpu_addr, u64 query_size) noexcept {
IteratePages<true>(dirty_cpu_addr, query_size,
[](Manager* manager, u64 offset, size_t size) {
manager->template ChangeRegionState<Type::GPU, true>(
manager->GetCpuAddr() + offset, size);
});
}
/// Unmark region as modified from the host GPU
void UnmarkRegionAsGpuModified(VAddr dirty_cpu_addr, u64 query_size) noexcept {
IteratePages<true>(dirty_cpu_addr, query_size,
[](Manager* manager, u64 offset, size_t size) {
manager->template ChangeRegionState<Type::GPU, false>(
manager->GetCpuAddr() + offset, size);
});
IteratePages<false>(dirty_cpu_addr, query_size,
[](RegionManager* manager, u64 offset, size_t size) {
manager->template ChangeRegionState<Type::GPU, true>(
manager->GetCpuAddr() + offset, size);
});
}
/// Call 'func' for each CPU modified range and unmark those pages as CPU modified
template <typename Func>
void ForEachUploadRange(VAddr query_cpu_range, u64 query_size, Func&& func) {
IteratePages<true>(query_cpu_range, query_size,
[&func](Manager* manager, u64 offset, size_t size) {
[&func](RegionManager* manager, u64 offset, size_t size) {
manager->template ForEachModifiedRange<Type::CPU, true>(
manager->GetCpuAddr() + offset, size, func);
});
@ -93,7 +70,7 @@ public:
template <bool clear, typename Func>
void ForEachDownloadRange(VAddr query_cpu_range, u64 query_size, Func&& func) {
IteratePages<false>(query_cpu_range, query_size,
[&func](Manager* manager, u64 offset, size_t size) {
[&func](RegionManager* manager, u64 offset, size_t size) {
if constexpr (clear) {
manager->template ForEachModifiedRange<Type::GPU, true>(
manager->GetCpuAddr() + offset, size, func);
@ -114,7 +91,7 @@ private:
*/
template <bool create_region_on_fail, typename Func>
bool IteratePages(VAddr cpu_address, size_t size, Func&& func) {
using FuncReturn = typename std::invoke_result<Func, Manager*, u64, size_t>::type;
using FuncReturn = typename std::invoke_result<Func, RegionManager*, u64, size_t>::type;
static constexpr bool BOOL_BREAK = std::is_same_v<FuncReturn, bool>;
std::size_t remaining_size{size};
std::size_t page_index{cpu_address >> HIGHER_PAGE_BITS};
@ -155,7 +132,7 @@ private:
manager_pool.emplace_back();
auto& last_pool = manager_pool.back();
for (size_t i = 0; i < MANAGER_POOL_SIZE; i++) {
std::construct_at(&last_pool[i], tracker, 0, HIGHER_PAGE_SIZE);
std::construct_at(&last_pool[i], tracker, 0);
free_managers.push_back(&last_pool[i]);
}
}
@ -167,9 +144,9 @@ private:
}
PageManager* tracker;
std::deque<std::array<Manager, MANAGER_POOL_SIZE>> manager_pool;
std::vector<Manager*> free_managers;
std::array<Manager*, NUM_HIGH_PAGES> top_tier{};
std::deque<std::array<RegionManager, MANAGER_POOL_SIZE>> manager_pool;
std::vector<RegionManager*> free_managers;
std::array<RegionManager*, NUM_HIGH_PAGES> top_tier{};
};
} // namespace VideoCore

View File

@ -3,10 +3,12 @@
#pragma once
#include <algorithm>
#include <array>
#include <mutex>
#include <span>
#include <utility>
#include "common/div_ceil.h"
#include "common/spin_lock.h"
#include "common/types.h"
#include "video_core/page_manager.h"
@ -16,135 +18,32 @@ constexpr u64 PAGES_PER_WORD = 64;
constexpr u64 BYTES_PER_PAGE = 4_KB;
constexpr u64 BYTES_PER_WORD = PAGES_PER_WORD * BYTES_PER_PAGE;
constexpr u64 HIGHER_PAGE_BITS = 22;
constexpr u64 HIGHER_PAGE_SIZE = 1ULL << HIGHER_PAGE_BITS;
constexpr u64 HIGHER_PAGE_MASK = HIGHER_PAGE_SIZE - 1ULL;
constexpr u64 NUM_REGION_WORDS = HIGHER_PAGE_SIZE / BYTES_PER_WORD;
enum class Type {
CPU,
GPU,
Untracked,
};
/// Vector tracking modified pages tightly packed with small vector optimization
template <size_t stack_words = 1>
struct WordsArray {
/// Returns the pointer to the words state
[[nodiscard]] const u64* Pointer(bool is_short) const noexcept {
return is_short ? stack.data() : heap;
}
using WordsArray = std::array<u64, NUM_REGION_WORDS>;
/// Returns the pointer to the words state
[[nodiscard]] u64* Pointer(bool is_short) noexcept {
return is_short ? stack.data() : heap;
}
std::array<u64, stack_words> stack{}; ///< Small buffers storage
u64* heap; ///< Not-small buffers pointer to the storage
};
template <size_t stack_words = 1>
struct Words {
explicit Words() = default;
explicit Words(u64 size_bytes_) : size_bytes{size_bytes_} {
num_words = Common::DivCeil(size_bytes, BYTES_PER_WORD);
if (IsShort()) {
cpu.stack.fill(~u64{0});
gpu.stack.fill(0);
untracked.stack.fill(~u64{0});
} else {
// Share allocation between CPU and GPU pages and set their default values
u64* const alloc = new u64[num_words * 3];
cpu.heap = alloc;
gpu.heap = alloc + num_words;
untracked.heap = alloc + num_words * 2;
std::fill_n(cpu.heap, num_words, ~u64{0});
std::fill_n(gpu.heap, num_words, 0);
std::fill_n(untracked.heap, num_words, ~u64{0});
}
// Clean up tailing bits
const u64 last_word_size = size_bytes % BYTES_PER_WORD;
const u64 last_local_page = Common::DivCeil(last_word_size, BYTES_PER_PAGE);
const u64 shift = (PAGES_PER_WORD - last_local_page) % PAGES_PER_WORD;
const u64 last_word = (~u64{0} << shift) >> shift;
cpu.Pointer(IsShort())[NumWords() - 1] = last_word;
untracked.Pointer(IsShort())[NumWords() - 1] = last_word;
}
~Words() {
Release();
}
Words& operator=(Words&& rhs) noexcept {
Release();
size_bytes = rhs.size_bytes;
num_words = rhs.num_words;
cpu = rhs.cpu;
gpu = rhs.gpu;
untracked = rhs.untracked;
rhs.cpu.heap = nullptr;
return *this;
}
Words(Words&& rhs) noexcept
: size_bytes{rhs.size_bytes}, num_words{rhs.num_words}, cpu{rhs.cpu}, gpu{rhs.gpu},
untracked{rhs.untracked} {
rhs.cpu.heap = nullptr;
}
Words& operator=(const Words&) = delete;
Words(const Words&) = delete;
/// Returns true when the buffer fits in the small vector optimization
[[nodiscard]] bool IsShort() const noexcept {
return num_words <= stack_words;
}
/// Returns the number of words of the buffer
[[nodiscard]] size_t NumWords() const noexcept {
return num_words;
}
/// Release buffer resources
void Release() {
if (!IsShort()) {
// CPU written words is the base for the heap allocation
delete[] cpu.heap;
}
}
template <Type type>
std::span<u64> Span() noexcept {
if constexpr (type == Type::CPU) {
return std::span<u64>(cpu.Pointer(IsShort()), num_words);
} else if constexpr (type == Type::GPU) {
return std::span<u64>(gpu.Pointer(IsShort()), num_words);
} else if constexpr (type == Type::Untracked) {
return std::span<u64>(untracked.Pointer(IsShort()), num_words);
}
}
template <Type type>
std::span<const u64> Span() const noexcept {
if constexpr (type == Type::CPU) {
return std::span<const u64>(cpu.Pointer(IsShort()), num_words);
} else if constexpr (type == Type::GPU) {
return std::span<const u64>(gpu.Pointer(IsShort()), num_words);
} else if constexpr (type == Type::Untracked) {
return std::span<const u64>(untracked.Pointer(IsShort()), num_words);
}
}
u64 size_bytes = 0;
size_t num_words = 0;
WordsArray<stack_words> cpu;
WordsArray<stack_words> gpu;
WordsArray<stack_words> untracked;
};
template <size_t stack_words = 1>
class WordManager {
/**
* Allows tracking CPU and GPU modification of pages in a contigious 4MB virtual address region.
* Information is stored in bitsets for spacial locality and fast update of single pages.
*/
class RegionManager {
public:
explicit WordManager(PageManager* tracker_, VAddr cpu_addr_, u64 size_bytes)
: tracker{tracker_}, cpu_addr{cpu_addr_}, words{size_bytes} {}
explicit WordManager() = default;
explicit RegionManager(PageManager* tracker_, VAddr cpu_addr_)
: tracker{tracker_}, cpu_addr{cpu_addr_} {
cpu.fill(~u64{0});
gpu.fill(0);
untracked.fill(~u64{0});
}
explicit RegionManager() = default;
void SetCpuAddress(VAddr new_cpu_addr) {
cpu_addr = new_cpu_addr;
@ -175,12 +74,12 @@ public:
static constexpr bool BOOL_BREAK = std::is_same_v<FuncReturn, bool>;
const size_t start = static_cast<size_t>(std::max<s64>(static_cast<s64>(offset), 0LL));
const size_t end = static_cast<size_t>(std::max<s64>(static_cast<s64>(offset + size), 0LL));
if (start >= SizeBytes() || end <= start) {
if (start >= HIGHER_PAGE_SIZE || end <= start) {
return;
}
auto [start_word, start_page] = GetWordPage(start);
auto [end_word, end_page] = GetWordPage(end + BYTES_PER_PAGE - 1ULL);
const size_t num_words = NumWords();
constexpr size_t num_words = NUM_REGION_WORDS;
start_word = std::min(start_word, num_words);
end_word = std::min(end_word, num_words);
const size_t diff = end_word - start_word;
@ -225,21 +124,21 @@ public:
*/
template <Type type, bool enable>
void ChangeRegionState(u64 dirty_addr, u64 size) noexcept(type == Type::GPU) {
std::span<u64> state_words = words.template Span<type>();
[[maybe_unused]] std::span<u64> untracked_words = words.template Span<Type::Untracked>();
std::scoped_lock lk{lock};
std::span<u64> state_words = Span<type>();
IterateWords(dirty_addr - cpu_addr, size, [&](size_t index, u64 mask) {
if constexpr (type == Type::CPU) {
NotifyPageTracker<!enable>(index, untracked_words[index], mask);
UpdateProtection<!enable>(index, untracked[index], mask);
}
if constexpr (enable) {
state_words[index] |= mask;
if constexpr (type == Type::CPU) {
untracked_words[index] |= mask;
untracked[index] |= mask;
}
} else {
state_words[index] &= ~mask;
if constexpr (type == Type::CPU) {
untracked_words[index] &= ~mask;
untracked[index] &= ~mask;
}
}
});
@ -255,10 +154,10 @@ public:
*/
template <Type type, bool clear, typename Func>
void ForEachModifiedRange(VAddr query_cpu_range, s64 size, Func&& func) {
std::scoped_lock lk{lock};
static_assert(type != Type::Untracked);
std::span<u64> state_words = words.template Span<type>();
[[maybe_unused]] std::span<u64> untracked_words = words.template Span<Type::Untracked>();
std::span<u64> state_words = Span<type>();
const size_t offset = query_cpu_range - cpu_addr;
bool pending = false;
size_t pending_offset{};
@ -269,16 +168,16 @@ public:
};
IterateWords(offset, size, [&](size_t index, u64 mask) {
if constexpr (type == Type::GPU) {
mask &= ~untracked_words[index];
mask &= ~untracked[index];
}
const u64 word = state_words[index] & mask;
if constexpr (clear) {
if constexpr (type == Type::CPU) {
NotifyPageTracker<true>(index, untracked_words[index], mask);
UpdateProtection<true>(index, untracked[index], mask);
}
state_words[index] &= ~mask;
if constexpr (type == Type::CPU) {
untracked_words[index] &= ~mask;
untracked[index] &= ~mask;
}
}
const size_t base_offset = index * PAGES_PER_WORD;
@ -315,13 +214,11 @@ public:
[[nodiscard]] bool IsRegionModified(u64 offset, u64 size) const noexcept {
static_assert(type != Type::Untracked);
const std::span<const u64> state_words = words.template Span<type>();
[[maybe_unused]] const std::span<const u64> untracked_words =
words.template Span<Type::Untracked>();
const std::span<const u64> state_words = Span<type>();
bool result = false;
IterateWords(offset, size, [&](size_t index, u64 mask) {
if constexpr (type == Type::GPU) {
mask &= ~untracked_words[index];
mask &= ~untracked[index];
}
const u64 word = state_words[index] & mask;
if (word != 0) {
@ -333,44 +230,7 @@ public:
return result;
}
/// Returns the number of words of the manager
[[nodiscard]] size_t NumWords() const noexcept {
return words.NumWords();
}
/// Returns the size in bytes of the manager
[[nodiscard]] u64 SizeBytes() const noexcept {
return words.size_bytes;
}
/// Returns true when the buffer fits in the small vector optimization
[[nodiscard]] bool IsShort() const noexcept {
return words.IsShort();
}
private:
template <Type type>
u64* Array() noexcept {
if constexpr (type == Type::CPU) {
return words.cpu.Pointer(IsShort());
} else if constexpr (type == Type::GPU) {
return words.gpu.Pointer(IsShort());
} else if constexpr (type == Type::Untracked) {
return words.untracked.Pointer(IsShort());
}
}
template <Type type>
const u64* Array() const noexcept {
if constexpr (type == Type::CPU) {
return words.cpu.Pointer(IsShort());
} else if constexpr (type == Type::GPU) {
return words.gpu.Pointer(IsShort());
} else if constexpr (type == Type::Untracked) {
return words.untracked.Pointer(IsShort());
}
}
/**
* Notify tracker about changes in the CPU tracking state of a word in the buffer
*
@ -381,7 +241,7 @@ private:
* @tparam add_to_tracker True when the tracker should start tracking the new pages
*/
template <bool add_to_tracker>
void NotifyPageTracker(u64 word_index, u64 current_bits, u64 new_bits) const {
void UpdateProtection(u64 word_index, u64 current_bits, u64 new_bits) const {
u64 changed_bits = (add_to_tracker ? current_bits : ~current_bits) & new_bits;
VAddr addr = cpu_addr + word_index * BYTES_PER_WORD;
IteratePages(changed_bits, [&](size_t offset, size_t size) {
@ -390,9 +250,34 @@ private:
});
}
template <Type type>
std::span<u64> Span() noexcept {
if constexpr (type == Type::CPU) {
return cpu;
} else if constexpr (type == Type::GPU) {
return gpu;
} else if constexpr (type == Type::Untracked) {
return untracked;
}
}
template <Type type>
std::span<const u64> Span() const noexcept {
if constexpr (type == Type::CPU) {
return cpu;
} else if constexpr (type == Type::GPU) {
return gpu;
} else if constexpr (type == Type::Untracked) {
return untracked;
}
}
Common::SpinLock lock;
PageManager* tracker;
VAddr cpu_addr = 0;
Words<stack_words> words;
WordsArray cpu;
WordsArray gpu;
WordsArray untracked;
};
} // namespace VideoCore

View File

@ -7,6 +7,7 @@ set(SHADER_FILES
detile_m32x1.comp
detile_m32x2.comp
detile_m32x4.comp
detile_macro8x1.comp
detile_macro32x1.comp
detile_macro32x2.comp
fs_tri.vert

View File

@ -20,7 +20,7 @@ layout(push_constant) uniform image_info {
} info;
// Inverse morton LUT, small enough to fit into K$
uint rmort[16] = {
const uint rmort[16] = {
0x11011000, 0x31213020,
0x13031202, 0x33233222,
0x51415040, 0x71617060,

View File

@ -20,7 +20,7 @@ layout(push_constant) uniform image_info {
} info;
// Inverse morton LUT, small enough to fit into K$
uint rmort[16] = {
const uint rmort[16] = {
0x11011000, 0x31213020,
0x13031202, 0x33233222,
0x51415040, 0x71617060,

View File

@ -20,7 +20,7 @@ layout(push_constant) uniform image_info {
} info;
// Inverse morton LUT, small enough to fit into K$
uint rmort[16] = {
const uint rmort[16] = {
0x11011000, 0x31213020,
0x13031202, 0x33233222,
0x51415040, 0x71617060,

View File

@ -48,4 +48,4 @@ void main() {
uint dw_ofs_x = target_tile_x * 2 + col; // 2 = uints
uint dw_ofs_y = (target_tile_y * MICRO_TILE_DIM + row) * tiles_per_pitch * 2; // 2 = uints
out_data[dw_ofs_x + dw_ofs_y] = dst_tx;
}
}

View File

@ -25,7 +25,7 @@ layout(push_constant) uniform image_info {
#define TEXELS_PER_ELEMENT 2
// Inverse morton LUT, small enough to fit into K$
uint rmort[16] = {
const uint rmort[16] = {
0x11011000, 0x31213020,
0x13031202, 0x33233222,
0x51415040, 0x71617060,

View File

@ -21,46 +21,46 @@ layout(push_constant) uniform image_info {
} info;
// Each LUT is 64 bytes, so should fit into K$ given tiled slices locality
const uint lut_32bpp[][64] = {
const uint lut_32bpp[][16] = {
{
0x00, 0x01, 0x04, 0x05, 0x40, 0x41, 0x44, 0x45,
0x02, 0x03, 0x06, 0x07, 0x42, 0x43, 0x46, 0x47,
0x10, 0x11, 0x14, 0x15, 0x50, 0x51, 0x54, 0x55,
0x12, 0x13, 0x16, 0x17, 0x52, 0x53, 0x56, 0x57,
0x80, 0x81, 0x84, 0x85, 0xc0, 0xc1, 0xc4, 0xc5,
0x82, 0x83, 0x86, 0x87, 0xc2, 0xc3, 0xc6, 0xc7,
0x90, 0x91, 0x94, 0x95, 0xd0, 0xd1, 0xd4, 0xd5,
0x92, 0x93, 0x96, 0x97, 0xd2, 0xd3, 0xd6, 0xd7,
0x05040100, 0x45444140,
0x07060302, 0x47464342,
0x15141110, 0x55545150,
0x17161312, 0x57565352,
0x85848180, 0xc5c4c1c0,
0x87868382, 0xc7c6c3c2,
0x95949190, 0xd5d4d1d0,
0x97969392, 0xd7d6d3d2,
},
{
0x08, 0x09, 0x0c, 0x0d, 0x48, 0x49, 0x4c, 0x4d,
0x0a, 0x0b, 0x0e, 0x0f, 0x4a, 0x4b, 0x4e, 0x4f,
0x18, 0x19, 0x1c, 0x1d, 0x58, 0x59, 0x5c, 0x5d,
0x1a, 0x1b, 0x1e, 0x1f, 0x5a, 0x5b, 0x5e, 0x5f,
0x88, 0x89, 0x8c, 0x8d, 0xc8, 0xc9, 0xcc, 0xcd,
0x8a, 0x8b, 0x8e, 0x8f, 0xca, 0xcb, 0xce, 0xcf,
0x98, 0x99, 0x9c, 0x9d, 0xd8, 0xd9, 0xdc, 0xdd,
0x9a, 0x9b, 0x9e, 0x9f, 0xda, 0xdb, 0xde, 0xdf,
0x0d0c0908, 0x4d4c4948,
0x0f0e0b0a, 0x4f4e4b4a,
0x1d1c1918, 0x5d5c5958,
0x1f1e1b1a, 0x5f5e5b5a,
0x8d8c8988, 0xcdccc9c8,
0x8f8e8b8a, 0xcfcecbca,
0x9d9c9998, 0xdddcd9d8,
0x9f9e9b9a, 0xdfdedbda,
},
{
0x20, 0x21, 0x24, 0x25, 0x60, 0x61, 0x64, 0x65,
0x22, 0x23, 0x26, 0x27, 0x62, 0x63, 0x66, 0x67,
0x30, 0x31, 0x34, 0x35, 0x70, 0x71, 0x74, 0x75,
0x32, 0x33, 0x36, 0x37, 0x72, 0x73, 0x76, 0x77,
0xa0, 0xa1, 0xa4, 0xa5, 0xe0, 0xe1, 0xe4, 0xe5,
0xa2, 0xa3, 0xa6, 0xa7, 0xe2, 0xe3, 0xe6, 0xe7,
0xb0, 0xb1, 0xb4, 0xb5, 0xf0, 0xf1, 0xf4, 0xf5,
0xb2, 0xb3, 0xb6, 0xb7, 0xf2, 0xf3, 0xf6, 0xf7,
0x25242120, 0x65646160,
0x27262322, 0x67666362,
0x35343130, 0x75747170,
0x37363332, 0x77767372,
0xa5a4a1a0, 0xe5e4e1e0,
0xa7a6a3a2, 0xe7e6e3e2,
0xb5b4b1b0, 0xf5f4f1f0,
0xb7b6b3b2, 0xf7f6f3f2,
},
{
0x28, 0x29, 0x2c, 0x2d, 0x68, 0x69, 0x6c, 0x6d,
0x2a, 0x2b, 0x2e, 0x2f, 0x6a, 0x6b, 0x6e, 0x6f,
0x38, 0x39, 0x3c, 0x3d, 0x78, 0x79, 0x7c, 0x7d,
0x3a, 0x3b, 0x3e, 0x3f, 0x7a, 0x7b, 0x7e, 0x7f,
0xa8, 0xa9, 0xac, 0xad, 0xe8, 0xe9, 0xec, 0xed,
0xaa, 0xab, 0xae, 0xaf, 0xea, 0xeb, 0xee, 0xef,
0xb8, 0xb9, 0xbc, 0xbd, 0xf8, 0xf9, 0xfc, 0xfd,
0xba, 0xbb, 0xbe, 0xbf, 0xfa, 0xfb, 0xfe, 0xff,
0x2d2c2928, 0x6d6c6968,
0x2f2e2b2a, 0x6f6e6b6a,
0x3d3c3938, 0x7d7c7978,
0x3f3e3b3a, 0x7f7e7b7a,
0xadaca9a8, 0xedece9e8,
0xafaeabaa, 0xefeeebea,
0xbdbcb9b8, 0xfdfcf9f8,
0xbfbebbba, 0xfffefbfa,
}
};
@ -77,7 +77,9 @@ void main() {
uint col = bitfieldExtract(x, 0, 3);
uint row = bitfieldExtract(y, 0, 3);
uint lut = bitfieldExtract(z, 0, 2);
uint idx = lut_32bpp[lut][col + row * MICRO_TILE_DIM];
uint idx_dw = lut_32bpp[lut][(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 >> 2u) * info.c1 * MICRO_TILE_SZ;
uint tile_row = y / MICRO_TILE_DIM;

View File

@ -20,46 +20,46 @@ layout(push_constant) uniform image_info {
uint c1;
} info;
const uint lut_64bpp[][64] = {
const uint lut_64bpp[][16] = {
{
0x00, 0x01, 0x08, 0x09, 0x40, 0x41, 0x48, 0x49,
0x02, 0x03, 0x0a, 0x0b, 0x42, 0x43, 0x4a, 0x4b,
0x10, 0x11, 0x18, 0x19, 0x50, 0x51, 0x58, 0x59,
0x12, 0x13, 0x1a, 0x1b, 0x52, 0x53, 0x5a, 0x5b,
0x80, 0x81, 0x88, 0x89, 0xc0, 0xc1, 0xc8, 0xc9,
0x82, 0x83, 0x8a, 0x8b, 0xc2, 0xc3, 0xca, 0xcb,
0x90, 0x91, 0x98, 0x99, 0xd0, 0xd1, 0xd8, 0xd9,
0x92, 0x93, 0x9a, 0x9b, 0xd2, 0xd3, 0xda, 0xdb,
0x09080100, 0x49484140,
0x0b0a0302, 0x4a4b4342,
0x19181110, 0x59585150,
0x1b1a1312, 0x5a5b5352,
0x89888180, 0xc9c8c1c0,
0x8b8a8382, 0xcacbc3c2,
0x99989190, 0xd9d8d1d0,
0x9b9a9392, 0xdbdad3d2,
},
{
0x04, 0x05, 0x0c, 0x0d, 0x44, 0x45, 0x4c, 0x4d,
0x06, 0x07, 0x0e, 0x0f, 0x46, 0x47, 0x4e, 0x4f,
0x14, 0x15, 0x1c, 0x1d, 0x54, 0x55, 0x5c, 0x5d,
0x16, 0x17, 0x1e, 0x1f, 0x56, 0x57, 0x5e, 0x5f,
0x84, 0x85, 0x8c, 0x8d, 0xc4, 0xc5, 0xcc, 0xcd,
0x86, 0x87, 0x8e, 0x8f, 0xc6, 0xc7, 0xce, 0xcf,
0x94, 0x95, 0x9c, 0x9d, 0xd4, 0xd5, 0xdc, 0xdd,
0x96, 0x97, 0x9e, 0x9f, 0xd6, 0xd7, 0xde, 0xdf,
0x0d0c0504, 0x4d4c4544,
0x0f0e0706, 0x4f4e4746,
0x1d1c1514, 0x5d5c5554,
0x1f1e1716, 0x5f5e5756,
0x8d8c8584, 0xcdccc5c4,
0x8f8e8786, 0xcfcec7c6,
0x9d9c9594, 0xdddcd5d4,
0x9f9e9796, 0xdfded7d6,
},
{
0x20, 0x21, 0x28, 0x29, 0x60, 0x61, 0x68, 0x69,
0x22, 0x23, 0x2a, 0x2b, 0x62, 0x63, 0x6a, 0x6b,
0x30, 0x31, 0x38, 0x39, 0x70, 0x71, 0x78, 0x79,
0x32, 0x33, 0x3a, 0x3b, 0x72, 0x73, 0x7a, 0x7b,
0xa0, 0xa1, 0xa8, 0xa9, 0xe0, 0xe1, 0xe8, 0xe9,
0xa2, 0xa3, 0xaa, 0xab, 0xe2, 0xe3, 0xea, 0xeb,
0xb0, 0xb1, 0xb8, 0xb9, 0xf0, 0xf1, 0xf8, 0xf9,
0xb2, 0xb3, 0xba, 0xbb, 0xf2, 0xf3, 0xfa, 0xfb,
0x29282120, 0x69686160,
0x2b2a2322, 0x6b6a6362,
0x39383130, 0x79787170,
0x3b3a3332, 0x7b7a7372,
0xa9a8a1a0, 0xe9e8e1e0,
0xabaaa3a2, 0xebeae3e2,
0xb9b8b1b0, 0xf9f8f1f0,
0xbbbab3b2, 0xfbfaf3f2,
},
{
0x24, 0x25, 0x2c, 0x2d, 0x64, 0x65, 0x6c, 0x6d,
0x26, 0x27, 0x2e, 0x2f, 0x66, 0x67, 0x6e, 0x6f,
0x34, 0x35, 0x3c, 0x3d, 0x74, 0x75, 0x7c, 0x7d,
0x36, 0x37, 0x3e, 0x3f, 0x76, 0x77, 0x7e, 0x7f,
0xa4, 0xa5, 0xac, 0xad, 0xe4, 0xe5, 0xec, 0xed,
0xa6, 0xa7, 0xae, 0xaf, 0xe6, 0xe7, 0xee, 0xef,
0xb4, 0xb5, 0xbc, 0xbd, 0xf4, 0xf5, 0xfc, 0xfd,
0xb6, 0xb7, 0xbe, 0xbf, 0xf6, 0xf7, 0xfe, 0xff,
0x2d2c2524, 0x6d6c6564,
0x2f2e2726, 0x6f6e6766,
0x3d3c3534, 0x7d7c7574,
0x3f3e3736, 0x7f7e7776,
0xadaca5a4, 0xedece5e4,
0xafaea7a6, 0xefeee7e6,
0xbdbcb5b4, 0xfdfcf5f4,
0xbfbeb7b6, 0xfffef7f6,
},
};
@ -76,7 +76,9 @@ void main() {
uint col = bitfieldExtract(x, 0, 3);
uint row = bitfieldExtract(y, 0, 3);
uint lut = bitfieldExtract(z, 0, 2);
uint idx = lut_64bpp[lut][col + row * MICRO_TILE_DIM];
uint idx_dw = lut_64bpp[lut][(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 >> 2u) * info.c1 * MICRO_TILE_SZ;
uint tile_row = y / MICRO_TILE_DIM;
@ -85,7 +87,7 @@ void main() {
uint offs = slice_offs + tile_offs + (idx * BPP / 8);
uint p0 = in_data[(offs >> 2) + 0];
uint p1 = in_data[(offs >> 2) + 1];
uint p1 = in_data[(offs >> 2) + 1];
out_data[2 * gl_GlobalInvocationID.x + 0] = p0;
out_data[2 * gl_GlobalInvocationID.x + 1] = p1;
out_data[2 * gl_GlobalInvocationID.x + 1] = p1;
}

View File

@ -0,0 +1,101 @@
// 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_8bpp[][16] = {
{
0x05040100, 0x45444140,
0x07060302, 0x47464342,
0x0d0c0908, 0x4d4c4948,
0x0f0e0b0a, 0x4f4e4b4a,
0x85848180, 0xc5c4c1c0,
0x87868382, 0xc7c6c3c2,
0x8d8c8988, 0xcdccc9c8,
0x8f8e8b8a, 0xcfcecbca,
},
{
0x15141110, 0x55545150,
0x17161312, 0x57565352,
0x1d1c1918, 0x5d5c5958,
0x1f1e1b1a, 0x5f5e5b5a,
0x95949190, 0xd5d4d1d0,
0x97969392, 0xd7d6d3d2,
0x9d9c9998, 0xdddcd9d8,
0x9f9e9b9a, 0xdfdedbda,
},
{
0x25242120, 0x65646160,
0x27262322, 0x67666362,
0x2d2c2928, 0x6d6c6968,
0x2f2e2b2a, 0x6f6e6b6a,
0xa5a4a1a0, 0xe5e4e1e0,
0xa7a6a3a2, 0xe7e6e3e2,
0xadaca9a8, 0xedece9e8,
0xafaeabaa, 0xefeeebea,
},
{
0x35343130, 0x75747170,
0x37363332, 0x77767372,
0x3d3c3938, 0x7d7c7978,
0x3f3e3b3a, 0x7f7e7b7a,
0xb5b4b1b0, 0xf5f4f1f0,
0xb7b6b3b2, 0xf7f6f3f2,
0xbdbcb9b8, 0xfdfcf9f8,
0xbfbebbba, 0xfffefbfa,
},
};
#define MICRO_TILE_DIM (8)
#define MICRO_TILE_SZ (256)
#define TEXELS_PER_ELEMENT (1)
#define BPP (8)
shared uint scratch[16];
void main() {
uint slot = gl_LocalInvocationID.x >> 2u;
atomicAnd(scratch[slot], 0);
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 lut = bitfieldExtract(z, 0, 2);
uint idx_dw = lut_8bpp[lut][(col + row * MICRO_TILE_DIM) >> 2u];
uint byte_ofs = (gl_LocalInvocationID.x & 3u) * 8;
uint idx = bitfieldExtract(idx_dw >> byte_ofs, 0, 8);
uint slice_offs = (z >> 2u) * 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 / 8);
uint p0 = in_data[offs >> 2u];
uint byte = bitfieldExtract(p0 >> (offs * 8), 0, 8);
atomicOr(scratch[slot], byte << byte_ofs);
if (byte_ofs == 0) {
out_data[gl_GlobalInvocationID.x >> 2u] = scratch[slot];
}
}

View File

@ -39,6 +39,15 @@ public:
return &(*first_level_map[l1_page])[l2_page];
}
[[nodiscard]] const Entry* find(size_t page) const {
const size_t l1_page = page >> SecondLevelBits;
const size_t l2_page = page & (NumEntriesPerL1Page - 1);
if (!first_level_map[l1_page]) {
return nullptr;
}
return &(*first_level_map[l1_page])[l2_page];
}
[[nodiscard]] const Entry& operator[](size_t page) const {
const size_t l1_page = page >> SecondLevelBits;
const size_t l2_page = page & (NumEntriesPerL1Page - 1);

View File

@ -185,7 +185,7 @@ void PageManager::OnGpuUnmap(VAddr address, size_t size) {
void PageManager::UpdatePagesCachedCount(VAddr addr, u64 size, s32 delta) {
static constexpr u64 PageShift = 12;
std::scoped_lock lk{mutex};
std::scoped_lock lk{lock};
const u64 num_pages = ((addr + size - 1) >> PageShift) - (addr >> PageShift) + 1;
const u64 page_start = addr >> PageShift;
const u64 page_end = page_start + num_pages;

View File

@ -4,8 +4,8 @@
#pragma once
#include <memory>
#include <mutex>
#include <boost/icl/interval_map.hpp>
#include "common/spin_lock.h"
#include "common/types.h"
namespace Vulkan {
@ -35,8 +35,8 @@ private:
struct Impl;
std::unique_ptr<Impl> impl;
Vulkan::Rasterizer* rasterizer;
std::mutex mutex;
boost::icl::interval_map<VAddr, s32> cached_pages;
Common::SpinLock lock;
};
} // namespace VideoCore

View File

@ -691,16 +691,40 @@ std::span<const SurfaceFormatInfo> SurfaceFormats() {
return formats;
}
// Table 8.13 Data and Image Formats [Sea Islands Series Instruction Set Architecture]
static const size_t amd_gpu_data_format_bit_size = 6; // All values are under 64
static const size_t amd_gpu_number_format_bit_size = 4; // All values are under 16
static size_t GetSurfaceFormatTableIndex(AmdGpu::DataFormat data_format,
AmdGpu::NumberFormat num_format) {
DEBUG_ASSERT(u32(data_format) < 1 << amd_gpu_data_format_bit_size);
DEBUG_ASSERT(u32(num_format) < 1 << amd_gpu_number_format_bit_size);
size_t result = static_cast<size_t>(num_format) |
(static_cast<size_t>(data_format) << amd_gpu_number_format_bit_size);
return result;
}
static auto surface_format_table = []() constexpr {
std::array<vk::Format, 1 << amd_gpu_data_format_bit_size * 1 << amd_gpu_number_format_bit_size>
result;
for (auto& entry : result) {
entry = vk::Format::eUndefined;
}
for (const auto& supported_format : SurfaceFormats()) {
result[GetSurfaceFormatTableIndex(supported_format.data_format,
supported_format.number_format)] =
supported_format.vk_format;
}
return result;
}();
vk::Format SurfaceFormat(AmdGpu::DataFormat data_format, AmdGpu::NumberFormat num_format) {
const auto& formats = SurfaceFormats();
const auto format =
std::find_if(formats.begin(), formats.end(), [&](const SurfaceFormatInfo& format_info) {
return format_info.data_format == data_format &&
format_info.number_format == num_format;
});
ASSERT_MSG(format != formats.end(), "Unknown data_format={} and num_format={}",
static_cast<u32>(data_format), static_cast<u32>(num_format));
return format->vk_format;
vk::Format result = surface_format_table[GetSurfaceFormatTableIndex(data_format, num_format)];
bool found =
result != vk::Format::eUndefined || data_format == AmdGpu::DataFormat::FormatInvalid;
ASSERT_MSG(found, "Unknown data_format={} and num_format={}", static_cast<u32>(data_format),
static_cast<u32>(num_format));
return result;
}
static constexpr DepthFormatInfo CreateDepthFormatInfo(
@ -746,8 +770,8 @@ vk::Format DepthFormat(DepthBuffer::ZFormat z_format, DepthBuffer::StencilFormat
vk::ClearValue ColorBufferClearValue(const AmdGpu::Liverpool::ColorBuffer& color_buffer) {
const auto comp_swizzle = color_buffer.Swizzle();
const auto format = color_buffer.DataFormat();
const auto number_type = color_buffer.NumFormat();
const auto format = color_buffer.GetDataFmt();
const auto number_type = color_buffer.GetNumberFmt();
const auto& c0 = color_buffer.clear_word0;
const auto& c1 = color_buffer.clear_word1;

View File

@ -328,8 +328,8 @@ bool PipelineCache::RefreshGraphicsKey() {
}
key.color_formats[remapped_cb] =
LiverpoolToVK::SurfaceFormat(col_buf.DataFormat(), col_buf.NumFormat());
key.color_num_formats[remapped_cb] = col_buf.NumFormat();
LiverpoolToVK::SurfaceFormat(col_buf.GetDataFmt(), col_buf.GetNumberFmt());
key.color_num_formats[remapped_cb] = col_buf.GetNumberFmt();
key.color_swizzles[remapped_cb] = col_buf.Swizzle();
}

View File

@ -562,6 +562,12 @@ void Rasterizer::BindBuffers(const Shader::Info& stage, Shader::Backend::Binding
push_data.AddOffset(binding.buffer, adjust);
buffer_infos.emplace_back(vk_buffer->Handle(), offset_aligned,
vsharp.GetSize() + adjust);
if (auto barrier =
vk_buffer->GetBarrier(desc.is_written ? vk::AccessFlagBits2::eShaderWrite
: vk::AccessFlagBits2::eShaderRead,
vk::PipelineStageFlagBits2::eAllCommands)) {
buffer_barriers.emplace_back(*barrier);
}
}
set_writes.push_back({
@ -600,7 +606,7 @@ void Rasterizer::BindBuffers(const Shader::Info& stage, Shader::Backend::Binding
if (auto barrier =
vk_buffer->GetBarrier(desc.is_written ? vk::AccessFlagBits2::eShaderWrite
: vk::AccessFlagBits2::eShaderRead,
vk::PipelineStageFlagBits2::eComputeShader)) {
vk::PipelineStageFlagBits2::eAllCommands)) {
buffer_barriers.emplace_back(*barrier);
}
if (desc.is_written) {

View File

@ -265,9 +265,9 @@ ImageInfo::ImageInfo(const AmdGpu::Liverpool::ColorBuffer& buffer,
const AmdGpu::Liverpool::CbDbExtent& hint /*= {}*/) noexcept {
props.is_tiled = buffer.IsTiled();
tiling_mode = buffer.GetTilingMode();
pixel_format = LiverpoolToVK::SurfaceFormat(buffer.DataFormat(), buffer.NumFormat());
pixel_format = LiverpoolToVK::SurfaceFormat(buffer.GetDataFmt(), buffer.GetNumberFmt());
num_samples = buffer.NumSamples();
num_bits = NumBits(buffer.DataFormat());
num_bits = NumBits(buffer.GetDataFmt());
type = vk::ImageType::e2D;
size.width = hint.Valid() ? hint.width : buffer.Pitch();
size.height = hint.Valid() ? hint.height : buffer.Height();

View File

@ -76,7 +76,8 @@ ImageViewInfo::ImageViewInfo(const AmdGpu::Liverpool::ColorBuffer& col_buffer) n
range.base.layer = col_buffer.view.slice_start;
range.extent.layers = col_buffer.NumSlices() - range.base.layer;
type = range.extent.layers > 1 ? vk::ImageViewType::e2DArray : vk::ImageViewType::e2D;
format = Vulkan::LiverpoolToVK::SurfaceFormat(col_buffer.DataFormat(), col_buffer.NumFormat());
format =
Vulkan::LiverpoolToVK::SurfaceFormat(col_buffer.GetDataFmt(), col_buffer.GetNumberFmt());
}
ImageViewInfo::ImageViewInfo(const AmdGpu::Liverpool::DepthBuffer& depth_buffer,

View File

@ -542,31 +542,62 @@ void TextureCache::RefreshImage(Image& image, Vulkan::Scheduler* custom_schedule
sched_ptr->EndRendering();
const auto cmdbuf = sched_ptr->CommandBuffer();
image.Transit(vk::ImageLayout::eTransferDstOptimal, vk::AccessFlagBits2::eTransferWrite, {},
cmdbuf);
const VAddr image_addr = image.info.guest_address;
const size_t image_size = image.info.guest_size_bytes;
const auto [vk_buffer, buf_offset] =
buffer_cache.ObtainViewBuffer(image_addr, image_size, is_gpu_dirty);
// The obtained buffer may be written by a shader so we need to emit a barrier to prevent RAW
// hazard
if (auto barrier = vk_buffer->GetBarrier(vk::AccessFlagBits2::eTransferRead,
vk::PipelineStageFlagBits2::eTransfer)) {
const auto dependencies = vk::DependencyInfo{
cmdbuf.pipelineBarrier2(vk::DependencyInfo{
.dependencyFlags = vk::DependencyFlagBits::eByRegion,
.bufferMemoryBarrierCount = 1,
.pBufferMemoryBarriers = &barrier.value(),
};
cmdbuf.pipelineBarrier2(dependencies);
});
}
const auto [buffer, offset] = tile_manager.TryDetile(vk_buffer->Handle(), buf_offset, image);
const auto [buffer, offset] =
tile_manager.TryDetile(vk_buffer->Handle(), buf_offset, image.info);
for (auto& copy : image_copy) {
copy.bufferOffset += offset;
}
const vk::BufferMemoryBarrier2 pre_barrier{
.srcStageMask = vk::PipelineStageFlagBits2::eAllCommands,
.srcAccessMask = vk::AccessFlagBits2::eMemoryWrite,
.dstStageMask = vk::PipelineStageFlagBits2::eTransfer,
.dstAccessMask = vk::AccessFlagBits2::eTransferRead,
.buffer = buffer,
.offset = offset,
.size = image_size,
};
const vk::BufferMemoryBarrier2 post_barrier{
.srcStageMask = vk::PipelineStageFlagBits2::eTransfer,
.srcAccessMask = vk::AccessFlagBits2::eTransferWrite,
.dstStageMask = vk::PipelineStageFlagBits2::eAllCommands,
.dstAccessMask = vk::AccessFlagBits2::eMemoryRead | vk::AccessFlagBits2::eMemoryWrite,
.buffer = buffer,
.offset = offset,
.size = image_size,
};
const auto image_barriers =
image.GetBarriers(vk::ImageLayout::eTransferDstOptimal, vk::AccessFlagBits2::eTransferWrite,
vk::PipelineStageFlagBits2::eTransfer, {});
cmdbuf.pipelineBarrier2(vk::DependencyInfo{
.dependencyFlags = vk::DependencyFlagBits::eByRegion,
.bufferMemoryBarrierCount = 1,
.pBufferMemoryBarriers = &pre_barrier,
.imageMemoryBarrierCount = static_cast<u32>(image_barriers.size()),
.pImageMemoryBarriers = image_barriers.data(),
});
cmdbuf.copyBufferToImage(buffer, image.image, vk::ImageLayout::eTransferDstOptimal, image_copy);
cmdbuf.pipelineBarrier2(vk::DependencyInfo{
.dependencyFlags = vk::DependencyFlagBits::eByRegion,
.bufferMemoryBarrierCount = 1,
.pBufferMemoryBarriers = &post_barrier,
});
image.flags &= ~ImageFlagBits::Dirty;
}

View File

@ -4,6 +4,7 @@
#include "video_core/renderer_vulkan/vk_instance.h"
#include "video_core/renderer_vulkan/vk_scheduler.h"
#include "video_core/renderer_vulkan/vk_shader_util.h"
#include "video_core/texture_cache/image_info.h"
#include "video_core/texture_cache/image_view.h"
#include "video_core/texture_cache/tile_manager.h"
@ -14,6 +15,7 @@
#include "video_core/host_shaders/detile_m8x2_comp.h"
#include "video_core/host_shaders/detile_macro32x1_comp.h"
#include "video_core/host_shaders/detile_macro32x2_comp.h"
#include "video_core/host_shaders/detile_macro8x1_comp.h"
#include <boost/container/static_vector.hpp>
#include <magic_enum/magic_enum.hpp>
@ -32,6 +34,7 @@ static vk::Format DemoteImageFormatForDetiling(vk::Format format) {
case vk::Format::eR5G5B5A1UnormPack16:
case vk::Format::eR8G8Unorm:
case vk::Format::eR16Sfloat:
case vk::Format::eR16Uint:
case vk::Format::eR16Unorm:
case vk::Format::eD16Unorm:
return vk::Format::eR8G8Uint;
@ -85,10 +88,10 @@ static vk::Format DemoteImageFormatForDetiling(vk::Format format) {
return format;
}
const DetilerContext* TileManager::GetDetiler(const Image& image) const {
const auto format = DemoteImageFormatForDetiling(image.info.pixel_format);
const DetilerContext* TileManager::GetDetiler(const ImageInfo& info) const {
const auto format = DemoteImageFormatForDetiling(info.pixel_format);
switch (image.info.tiling_mode) {
switch (info.tiling_mode) {
case AmdGpu::TilingMode::Texture_MicroTiled:
switch (format) {
case vk::Format::eR8Uint:
@ -106,6 +109,8 @@ const DetilerContext* TileManager::GetDetiler(const Image& image) const {
}
case AmdGpu::TilingMode::Texture_Volume:
switch (format) {
case vk::Format::eR8Uint:
return &detilers[DetilerType::Macro8x1];
case vk::Format::eR32Uint:
return &detilers[DetilerType::Macro32x1];
case vk::Format::eR32G32Uint:
@ -131,8 +136,8 @@ TileManager::TileManager(const Vulkan::Instance& instance, Vulkan::Scheduler& sc
static const std::array detiler_shaders{
HostShaders::DETILE_M8X1_COMP, HostShaders::DETILE_M8X2_COMP,
HostShaders::DETILE_M32X1_COMP, HostShaders::DETILE_M32X2_COMP,
HostShaders::DETILE_M32X4_COMP, HostShaders::DETILE_MACRO32X1_COMP,
HostShaders::DETILE_MACRO32X2_COMP,
HostShaders::DETILE_M32X4_COMP, HostShaders::DETILE_MACRO8X1_COMP,
HostShaders::DETILE_MACRO32X1_COMP, HostShaders::DETILE_MACRO32X2_COMP,
};
boost::container::static_vector<vk::DescriptorSetLayoutBinding, 2> bindings{
@ -257,23 +262,23 @@ void TileManager::FreeBuffer(ScratchBuffer buffer) {
}
std::pair<vk::Buffer, u32> TileManager::TryDetile(vk::Buffer in_buffer, u32 in_offset,
Image& image) {
if (!image.info.props.is_tiled) {
const ImageInfo& info) {
if (!info.props.is_tiled) {
return {in_buffer, in_offset};
}
const auto* detiler = GetDetiler(image);
const auto* detiler = GetDetiler(info);
if (!detiler) {
if (image.info.tiling_mode != AmdGpu::TilingMode::Texture_MacroTiled &&
image.info.tiling_mode != AmdGpu::TilingMode::Display_MacroTiled &&
image.info.tiling_mode != AmdGpu::TilingMode::Depth_MacroTiled) {
if (info.tiling_mode != AmdGpu::TilingMode::Texture_MacroTiled &&
info.tiling_mode != AmdGpu::TilingMode::Display_MacroTiled &&
info.tiling_mode != AmdGpu::TilingMode::Depth_MacroTiled) {
LOG_ERROR(Render_Vulkan, "Unsupported tiled image: {} ({})",
vk::to_string(image.info.pixel_format), NameOf(image.info.tiling_mode));
vk::to_string(info.pixel_format), NameOf(info.tiling_mode));
}
return {in_buffer, in_offset};
}
const u32 image_size = image.info.guest_size_bytes;
const u32 image_size = info.guest_size_bytes;
// Prepare output buffer
auto out_buffer = AllocBuffer(image_size, true);
@ -316,22 +321,20 @@ std::pair<vk::Buffer, u32> TileManager::TryDetile(vk::Buffer in_buffer, u32 in_o
set_writes);
DetilerParams params;
params.num_levels = image.info.resources.levels;
params.pitch0 = image.info.pitch >> (image.info.props.is_block ? 2u : 0u);
params.height = image.info.size.height;
if (image.info.tiling_mode == AmdGpu::TilingMode::Texture_Volume) {
ASSERT(image.info.resources.levels == 1);
ASSERT(image.info.num_bits >= 32);
const auto tiles_per_row = image.info.pitch / 8u;
const auto tiles_per_slice = tiles_per_row * ((image.info.size.height + 7u) / 8u);
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) {
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);
params.sizes[0] = tiles_per_row;
params.sizes[1] = tiles_per_slice;
} else {
ASSERT(image.info.resources.levels <= 14);
ASSERT(info.resources.levels <= 14);
std::memset(&params.sizes, 0, sizeof(params.sizes));
for (int m = 0; m < image.info.resources.levels; ++m) {
params.sizes[m] = image.info.mips_layout[m].size * image.info.resources.layers +
for (int m = 0; m < info.resources.levels; ++m) {
params.sizes[m] = info.mips_layout[m].size * info.resources.layers +
(m > 0 ? params.sizes[m - 1] : 0);
}
}
@ -340,20 +343,9 @@ std::pair<vk::Buffer, u32> TileManager::TryDetile(vk::Buffer in_buffer, u32 in_o
&params);
ASSERT((image_size % 64) == 0);
const auto bpp = image.info.num_bits * (image.info.props.is_block ? 16u : 1u);
const auto bpp = info.num_bits * (info.props.is_block ? 16u : 1u);
const auto num_tiles = image_size / (64 * (bpp / 8));
cmdbuf.dispatch(num_tiles, 1, 1);
const vk::BufferMemoryBarrier post_barrier{
.srcAccessMask = vk::AccessFlagBits::eShaderWrite,
.dstAccessMask = vk::AccessFlagBits::eTransferRead,
.buffer = out_buffer.first,
.size = image_size,
};
cmdbuf.pipelineBarrier(vk::PipelineStageFlagBits::eComputeShader,
vk::PipelineStageFlagBits::eTransfer, vk::DependencyFlagBits::eByRegion,
{}, post_barrier, {});
return {out_buffer.first, 0};
}

View File

@ -5,11 +5,11 @@
#include "common/types.h"
#include "video_core/buffer_cache/buffer.h"
#include "video_core/texture_cache/image.h"
namespace VideoCore {
class TextureCache;
struct ImageInfo;
enum DetilerType : u32 {
Micro8x1,
@ -18,6 +18,7 @@ enum DetilerType : u32 {
Micro32x2,
Micro32x4,
Macro8x1,
Macro32x1,
Macro32x2,
@ -36,14 +37,15 @@ public:
TileManager(const Vulkan::Instance& instance, Vulkan::Scheduler& scheduler);
~TileManager();
std::pair<vk::Buffer, u32> TryDetile(vk::Buffer in_buffer, u32 in_offset, Image& image);
std::pair<vk::Buffer, u32> TryDetile(vk::Buffer in_buffer, u32 in_offset,
const ImageInfo& info);
ScratchBuffer AllocBuffer(u32 size, bool is_storage = false);
void Upload(ScratchBuffer buffer, const void* data, size_t size);
void FreeBuffer(ScratchBuffer buffer);
private:
const DetilerContext* GetDetiler(const Image& image) const;
const DetilerContext* GetDetiler(const ImageInfo& info) const;
private:
const Vulkan::Instance& instance;