mirror of
https://github.com/shadps4-emu/shadPS4.git
synced 2025-06-04 09:43:16 +00:00
''
This commit is contained in:
commit
d9da4a9d2b
158 changed files with 4404 additions and 1662 deletions
|
@ -4,8 +4,8 @@
|
|||
#include <SDL3/SDL_audio.h>
|
||||
#include <SDL3/SDL_init.h>
|
||||
#include <SDL3/SDL_timer.h>
|
||||
#include <common/assert.h>
|
||||
#include <core/libraries/error_codes.h>
|
||||
#include "common/assert.h"
|
||||
#include "core/libraries/error_codes.h"
|
||||
#include "sdl_audio.h"
|
||||
|
||||
namespace Audio {
|
||||
|
|
|
@ -15,15 +15,17 @@ static u32 screenWidth = 1280;
|
|||
static u32 screenHeight = 720;
|
||||
static s32 gpuId = -1; // Vulkan physical device index. Set to negative for auto select
|
||||
static std::string logFilter;
|
||||
static std::string logType = "sync";
|
||||
static std::string logType = "async";
|
||||
static bool isDebugDump = false;
|
||||
static bool isLibc = true;
|
||||
static bool isShowSplash = false;
|
||||
static bool isNullGpu = false;
|
||||
static bool shouldDumpShaders = false;
|
||||
static bool shouldDumpPM4 = false;
|
||||
static u32 vblankDivider = 1;
|
||||
static bool vkValidation = false;
|
||||
static bool vkValidationSync = false;
|
||||
static bool rdocEnable = false;
|
||||
// Gui
|
||||
std::string settings_install_dir = "";
|
||||
u32 main_window_geometry_x = 400;
|
||||
|
@ -94,6 +96,14 @@ bool dumpPM4() {
|
|||
return shouldDumpPM4;
|
||||
}
|
||||
|
||||
bool isRdocEnabled() {
|
||||
return rdocEnable;
|
||||
}
|
||||
|
||||
u32 vblankDiv() {
|
||||
return vblankDivider;
|
||||
}
|
||||
|
||||
bool vkValidationEnabled() {
|
||||
return vkValidation;
|
||||
}
|
||||
|
@ -220,7 +230,7 @@ void load(const std::filesystem::path& path) {
|
|||
auto general = generalResult.unwrap();
|
||||
|
||||
isNeo = toml::find_or<toml::boolean>(general, "isPS4Pro", false);
|
||||
isFullscreen = toml::find_or<toml::boolean>(general, "Fullscreen", true);
|
||||
isFullscreen = toml::find_or<toml::boolean>(general, "Fullscreen", false);
|
||||
logFilter = toml::find_or<toml::string>(general, "logFilter", "");
|
||||
logType = toml::find_or<toml::string>(general, "logType", "sync");
|
||||
isShowSplash = toml::find_or<toml::boolean>(general, "showSplash", true);
|
||||
|
@ -233,10 +243,10 @@ void load(const std::filesystem::path& path) {
|
|||
|
||||
screenWidth = toml::find_or<toml::integer>(gpu, "screenWidth", screenWidth);
|
||||
screenHeight = toml::find_or<toml::integer>(gpu, "screenHeight", screenHeight);
|
||||
gpuId = toml::find_or<toml::integer>(gpu, "gpuId", 0);
|
||||
isNullGpu = toml::find_or<toml::boolean>(gpu, "nullGpu", false);
|
||||
shouldDumpShaders = toml::find_or<toml::boolean>(gpu, "dumpShaders", false);
|
||||
shouldDumpPM4 = toml::find_or<toml::boolean>(gpu, "dumpPM4", false);
|
||||
vblankDivider = toml::find_or<toml::integer>(gpu, "vblankDivider", 1);
|
||||
}
|
||||
}
|
||||
if (data.contains("Vulkan")) {
|
||||
|
@ -244,8 +254,10 @@ void load(const std::filesystem::path& path) {
|
|||
if (vkResult.is_ok()) {
|
||||
auto vk = vkResult.unwrap();
|
||||
|
||||
gpuId = toml::find_or<toml::integer>(vk, "gpuId", 0);
|
||||
vkValidation = toml::find_or<toml::boolean>(vk, "validation", true);
|
||||
vkValidationSync = toml::find_or<toml::boolean>(vk, "validation_sync", true);
|
||||
rdocEnable = toml::find_or<toml::boolean>(vk, "rdocEnable", false);
|
||||
}
|
||||
}
|
||||
if (data.contains("Debug")) {
|
||||
|
@ -312,14 +324,16 @@ void save(const std::filesystem::path& path) {
|
|||
data["General"]["logFilter"] = logFilter;
|
||||
data["General"]["logType"] = logType;
|
||||
data["General"]["showSplash"] = isShowSplash;
|
||||
data["GPU"]["gpuId"] = gpuId;
|
||||
data["GPU"]["screenWidth"] = screenWidth;
|
||||
data["GPU"]["screenHeight"] = screenHeight;
|
||||
data["GPU"]["nullGpu"] = isNullGpu;
|
||||
data["GPU"]["dumpShaders"] = shouldDumpShaders;
|
||||
data["GPU"]["dumpPM4"] = shouldDumpPM4;
|
||||
data["GPU"]["vblankDivider"] = vblankDivider;
|
||||
data["Vulkan"]["gpuId"] = gpuId;
|
||||
data["Vulkan"]["validation"] = vkValidation;
|
||||
data["Vulkan"]["validation_sync"] = vkValidationSync;
|
||||
data["Vulkan"]["rdocEnable"] = rdocEnable;
|
||||
data["Debug"]["DebugDump"] = isDebugDump;
|
||||
data["LLE"]["libc"] = isLibc;
|
||||
data["GUI"]["theme"] = mw_themes;
|
||||
|
|
|
@ -26,6 +26,8 @@ bool showSplash();
|
|||
bool nullGpu();
|
||||
bool dumpShaders();
|
||||
bool dumpPM4();
|
||||
bool isRdocEnabled();
|
||||
u32 vblankDiv();
|
||||
|
||||
bool vkValidationEnabled();
|
||||
bool vkValidationSyncEnabled();
|
||||
|
|
|
@ -207,8 +207,8 @@ public:
|
|||
message_queue.EmplaceWait(entry);
|
||||
} else {
|
||||
ForEachBackend([&entry](auto& backend) { backend.Write(entry); });
|
||||
std::fflush(stdout);
|
||||
}
|
||||
std::fflush(stdout);
|
||||
}
|
||||
|
||||
private:
|
||||
|
|
|
@ -73,6 +73,7 @@ enum class Class : u8 {
|
|||
Lib_DiscMap, ///< The LibSceDiscMap implementation.
|
||||
Lib_Png, ///< The LibScePng implementation.
|
||||
Lib_PlayGo, ///< The LibScePlayGo implementation.
|
||||
Lib_Random, ///< The libSceRandom implementation.
|
||||
Lib_Usbd, ///< The LibSceUsbd implementation.
|
||||
Lib_Ajm, ///< The LibSceAjm implementation.
|
||||
Lib_ErrorDialog, ///< The LibSceErrorDialog implementation.
|
||||
|
|
|
@ -72,6 +72,8 @@ static auto UserPaths = [] {
|
|||
create_path(PathType::GameDataDir, user_dir / GAMEDATA_DIR);
|
||||
create_path(PathType::TempDataDir, user_dir / TEMPDATA_DIR);
|
||||
create_path(PathType::SysModuleDir, user_dir / SYSMODULES_DIR);
|
||||
create_path(PathType::DownloadDir, user_dir / DOWNLOAD_DIR);
|
||||
create_path(PathType::CapturesDir, user_dir / CAPTURES_DIR);
|
||||
|
||||
return paths;
|
||||
}();
|
||||
|
|
|
@ -18,6 +18,8 @@ enum class PathType {
|
|||
TempDataDir, // Where game temp data is stored.
|
||||
GameDataDir, // Where game data is stored.
|
||||
SysModuleDir, // Where system modules are stored.
|
||||
DownloadDir, // Where downloads/temp files are stored.
|
||||
CapturesDir, // Where rdoc captures are stored.
|
||||
};
|
||||
|
||||
constexpr auto PORTABLE_DIR = "user";
|
||||
|
@ -31,6 +33,8 @@ constexpr auto SAVEDATA_DIR = "savedata";
|
|||
constexpr auto GAMEDATA_DIR = "data";
|
||||
constexpr auto TEMPDATA_DIR = "temp";
|
||||
constexpr auto SYSMODULES_DIR = "sys_modules";
|
||||
constexpr auto DOWNLOAD_DIR = "download";
|
||||
constexpr auto CAPTURES_DIR = "captures";
|
||||
|
||||
// Filenames
|
||||
constexpr auto LOG_FILE = "shad_log.txt";
|
||||
|
|
17
src/common/scm_rev.cpp.in
Normal file
17
src/common/scm_rev.cpp.in
Normal file
|
@ -0,0 +1,17 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "common/scm_rev.h"
|
||||
|
||||
#define GIT_REV "@GIT_REV@"
|
||||
#define GIT_BRANCH "@GIT_BRANCH@"
|
||||
#define GIT_DESC "@GIT_DESC@"
|
||||
|
||||
namespace Common {
|
||||
|
||||
const char g_scm_rev[] = GIT_REV;
|
||||
const char g_scm_branch[] = GIT_BRANCH;
|
||||
const char g_scm_desc[] = GIT_DESC;
|
||||
|
||||
} // namespace
|
||||
|
12
src/common/scm_rev.h
Normal file
12
src/common/scm_rev.h
Normal file
|
@ -0,0 +1,12 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace Common {
|
||||
|
||||
extern const char g_scm_rev[];
|
||||
extern const char g_scm_branch[];
|
||||
extern const char g_scm_desc[];
|
||||
|
||||
} // namespace Common
|
|
@ -9,5 +9,6 @@
|
|||
namespace Common {
|
||||
|
||||
constexpr char VERSION[] = "0.1.1 WIP";
|
||||
constexpr bool isRelease = false;
|
||||
|
||||
} // namespace Common
|
||||
|
|
|
@ -34,10 +34,7 @@ constexpr VAddr USER_MAX = 0xFBFFFFFFFFULL;
|
|||
|
||||
static constexpr size_t SystemManagedSize = SYSTEM_MANAGED_MAX - SYSTEM_MANAGED_MIN + 1;
|
||||
static constexpr size_t SystemReservedSize = SYSTEM_RESERVED_MAX - SYSTEM_RESERVED_MIN + 1;
|
||||
// User area size is normally larger than this. However games are unlikely to map to high
|
||||
// regions of that area, so by default we allocate a smaller virtual address space (about 1/4th).
|
||||
// to save space on page tables.
|
||||
static constexpr size_t UserSize = 1ULL << 39;
|
||||
static constexpr size_t UserSize = 1ULL << 40;
|
||||
|
||||
/**
|
||||
* Represents the user virtual address space backed by a dmem memory block
|
||||
|
|
|
@ -285,20 +285,24 @@ static void GenerateTcbAccess(const ZydisDecodedOperand* operands, Xbyak::CodeGe
|
|||
const auto slot = GetTcbKey();
|
||||
|
||||
#if defined(_WIN32)
|
||||
// The following logic is based on the wine implementation of TlsGetValue
|
||||
// https://github.com/wine-mirror/wine/blob/a27b9551/dlls/kernelbase/thread.c#L719
|
||||
// The following logic is based on the Kernel32.dll asm of TlsGetValue
|
||||
static constexpr u32 TlsSlotsOffset = 0x1480;
|
||||
static constexpr u32 TlsExpansionSlotsOffset = 0x1780;
|
||||
static constexpr u32 TlsMinimumAvailable = 64;
|
||||
|
||||
const u32 teb_offset = slot < TlsMinimumAvailable ? TlsSlotsOffset : TlsExpansionSlotsOffset;
|
||||
const u32 tls_index = slot < TlsMinimumAvailable ? slot : slot - TlsMinimumAvailable;
|
||||
|
||||
// Load the pointer to the table of TLS slots.
|
||||
c.putSeg(gs);
|
||||
c.mov(dst, ptr[reinterpret_cast<void*>(teb_offset)]);
|
||||
// Load the pointer to our buffer.
|
||||
c.mov(dst, qword[dst + tls_index * sizeof(LPVOID)]);
|
||||
if (slot < TlsMinimumAvailable) {
|
||||
// Load the pointer to TLS slots.
|
||||
c.mov(dst, ptr[reinterpret_cast<void*>(TlsSlotsOffset + slot * sizeof(LPVOID))]);
|
||||
} else {
|
||||
const u32 tls_index = slot - TlsMinimumAvailable;
|
||||
|
||||
// Load the pointer to the table of TLS expansion slots.
|
||||
c.mov(dst, ptr[reinterpret_cast<void*>(TlsExpansionSlotsOffset)]);
|
||||
// Load the pointer to our buffer.
|
||||
c.mov(dst, qword[dst + tls_index * sizeof(LPVOID)]);
|
||||
}
|
||||
#elif defined(__APPLE__)
|
||||
// The following logic is based on the Darwin implementation of _os_tsd_get_direct, used by
|
||||
// pthread_getspecific https://github.com/apple/darwin-xnu/blob/main/libsyscall/os/tsd.h#L89-L96
|
||||
|
|
16
src/core/file_format/playgo_chunk.cpp
Normal file
16
src/core/file_format/playgo_chunk.cpp
Normal file
|
@ -0,0 +1,16 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "common/io_file.h"
|
||||
|
||||
#include "playgo_chunk.h"
|
||||
|
||||
bool PlaygoChunk::Open(const std::filesystem::path& filepath) {
|
||||
Common::FS::IOFile file(filepath, Common::FS::FileAccessMode::Read);
|
||||
if (!file.IsOpen()) {
|
||||
return false;
|
||||
}
|
||||
file.Read(playgoHeader);
|
||||
|
||||
return true;
|
||||
}
|
31
src/core/file_format/playgo_chunk.h
Normal file
31
src/core/file_format/playgo_chunk.h
Normal file
|
@ -0,0 +1,31 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
#include <filesystem>
|
||||
#include "common/types.h"
|
||||
|
||||
struct PlaygoHeader {
|
||||
u32 magic;
|
||||
|
||||
u16 version_major;
|
||||
u16 version_minor;
|
||||
u16 image_count;
|
||||
u16 chunk_count;
|
||||
u16 mchunk_count;
|
||||
u16 scenario_count;
|
||||
// TODO fill the rest
|
||||
};
|
||||
class PlaygoChunk {
|
||||
public:
|
||||
PlaygoChunk() = default;
|
||||
~PlaygoChunk() = default;
|
||||
|
||||
bool Open(const std::filesystem::path& filepath);
|
||||
PlaygoHeader GetPlaygoHeader() {
|
||||
return playgoHeader;
|
||||
}
|
||||
|
||||
private:
|
||||
PlaygoHeader playgoHeader;
|
||||
};
|
|
@ -26,23 +26,27 @@ void MntPoints::UnmountAll() {
|
|||
}
|
||||
|
||||
std::filesystem::path MntPoints::GetHostPath(const std::string& guest_directory) {
|
||||
const MntPair* mount = GetMount(guest_directory);
|
||||
// Evil games like Turok2 pass double slashes e.g /app0//game.kpf
|
||||
auto corrected_path = guest_directory;
|
||||
size_t pos = corrected_path.find("//");
|
||||
while (pos != std::string::npos) {
|
||||
corrected_path.replace(pos, 2, "/");
|
||||
pos = corrected_path.find("//", pos + 1);
|
||||
}
|
||||
|
||||
const MntPair* mount = GetMount(corrected_path);
|
||||
if (!mount) {
|
||||
return guest_directory;
|
||||
return "";
|
||||
}
|
||||
|
||||
// Nothing to do if getting the mount itself.
|
||||
if (guest_directory == mount->mount) {
|
||||
if (corrected_path == mount->mount) {
|
||||
return mount->host_path;
|
||||
}
|
||||
|
||||
// Remove device (e.g /app0) from path to retrieve relative path.
|
||||
u32 pos = mount->mount.size() + 1;
|
||||
// Evil games like Turok2 pass double slashes e.g /app0//game.kpf
|
||||
if (guest_directory[pos] == '/') {
|
||||
pos++;
|
||||
}
|
||||
const auto rel_path = std::string_view(guest_directory).substr(pos);
|
||||
pos = mount->mount.size() + 1;
|
||||
const auto rel_path = std::string_view(corrected_path).substr(pos);
|
||||
const auto host_path = mount->host_path / rel_path;
|
||||
if (!NeedsCaseInsensiveSearch) {
|
||||
return host_path;
|
||||
|
@ -66,7 +70,7 @@ std::filesystem::path MntPoints::GetHostPath(const std::string& guest_directory)
|
|||
// exist in filesystem but in different case.
|
||||
auto guest_path = current_path;
|
||||
while (!path_parts.empty()) {
|
||||
const auto& part = path_parts.back();
|
||||
const auto part = path_parts.back();
|
||||
const auto add_match = [&](const auto& host_part) {
|
||||
current_path /= host_part;
|
||||
guest_path /= part;
|
||||
|
|
|
@ -198,13 +198,9 @@ int PS4_SYSV_ABI sceAppContentTemporaryDataMount() {
|
|||
|
||||
int PS4_SYSV_ABI sceAppContentTemporaryDataMount2(OrbisAppContentTemporaryDataOption option,
|
||||
OrbisAppContentMountPoint* mountPoint) {
|
||||
if (std::string_view(mountPoint->data).empty()) // causing issues with save_data.
|
||||
if (mountPoint == nullptr)
|
||||
return ORBIS_APP_CONTENT_ERROR_PARAMETER;
|
||||
auto* param_sfo = Common::Singleton<PSF>::Instance();
|
||||
std::string id(param_sfo->GetString("CONTENT_ID"), 7, 9);
|
||||
const auto& mount_dir = Common::FS::GetUserPath(Common::FS::PathType::TempDataDir) / id;
|
||||
auto* mnt = Common::Singleton<Core::FileSys::MntPoints>::Instance();
|
||||
mnt->Mount(mount_dir, mountPoint->data);
|
||||
strncpy(mountPoint->data, "/temp0", 16);
|
||||
LOG_INFO(Lib_AppContent, "sceAppContentTemporaryDataMount2: option = {}, mountPoint = {}",
|
||||
option, mountPoint->data);
|
||||
return ORBIS_OK;
|
||||
|
|
|
@ -233,6 +233,9 @@ constexpr int SCE_KERNEL_ERROR_ESDKVERSION = 0x80020063;
|
|||
constexpr int SCE_KERNEL_ERROR_ESTART = 0x80020064;
|
||||
constexpr int SCE_KERNEL_ERROR_ESTOP = 0x80020065;
|
||||
|
||||
// libSceRandom error codes
|
||||
constexpr int SCE_RANDOM_ERROR_INVALID = 0x817C0016;
|
||||
|
||||
// videoOut
|
||||
constexpr int SCE_VIDEO_OUT_ERROR_INVALID_VALUE = 0x80290001; // invalid argument
|
||||
constexpr int SCE_VIDEO_OUT_ERROR_INVALID_ADDRESS = 0x80290002; // invalid addresses
|
||||
|
|
|
@ -20,13 +20,12 @@
|
|||
|
||||
extern Frontend::WindowSDL* g_window;
|
||||
std::unique_ptr<Vulkan::RendererVulkan> renderer;
|
||||
std::unique_ptr<AmdGpu::Liverpool> liverpool;
|
||||
|
||||
namespace Libraries::GnmDriver {
|
||||
|
||||
using namespace AmdGpu;
|
||||
|
||||
static std::unique_ptr<AmdGpu::Liverpool> liverpool;
|
||||
|
||||
enum GnmEventIdents : u64 {
|
||||
Compute0RelMem = 0x00,
|
||||
Compute1RelMem = 0x01,
|
||||
|
@ -958,7 +957,7 @@ int PS4_SYSV_ABI sceGnmGetGpuBlockStatus() {
|
|||
}
|
||||
|
||||
int PS4_SYSV_ABI sceGnmGetGpuCoreClockFrequency() {
|
||||
LOG_ERROR(Lib_GnmDriver, "(STUBBED) called");
|
||||
LOG_DEBUG(Lib_GnmDriver, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
|
@ -2131,6 +2130,7 @@ int PS4_SYSV_ABI sceGnmSubmitDone() {
|
|||
if (!liverpool->IsGpuIdle()) {
|
||||
submission_lock = true;
|
||||
}
|
||||
liverpool->SubmitDone();
|
||||
send_init_packet = true;
|
||||
++frames_submitted;
|
||||
return ORBIS_OK;
|
||||
|
|
|
@ -78,9 +78,7 @@ bool EqueueInternal::TriggerEvent(u64 ident, s16 filter, void* trigger_data) {
|
|||
std::scoped_lock lock{m_mutex};
|
||||
|
||||
for (auto& event : m_events) {
|
||||
ASSERT_MSG(event.event.filter == filter,
|
||||
"Event to trigger doesn't match to queue events");
|
||||
if (event.event.ident == ident) {
|
||||
if ((event.event.ident == ident) && (event.event.filter == filter)) {
|
||||
event.Trigger(trigger_data);
|
||||
has_found = true;
|
||||
}
|
||||
|
|
|
@ -53,6 +53,9 @@ int PS4_SYSV_ABI sceKernelOpen(const char* path, int flags, u16 mode) {
|
|||
if (std::string_view{path} == "/dev/stdout") {
|
||||
return 2002;
|
||||
}
|
||||
if (std::string_view{path} == "/dev/urandom") {
|
||||
return 2003;
|
||||
}
|
||||
u32 handle = h->CreateHandle();
|
||||
auto* file = h->GetFile(handle);
|
||||
if (directory) {
|
||||
|
@ -113,6 +116,9 @@ int PS4_SYSV_ABI sceKernelClose(int d) {
|
|||
if (d < 3) { // d probably hold an error code
|
||||
return ORBIS_KERNEL_ERROR_EPERM;
|
||||
}
|
||||
if (d == 2003) { // dev/urandom case
|
||||
return SCE_OK;
|
||||
}
|
||||
auto* h = Common::Singleton<Core::FileSys::HandleTable>::Instance();
|
||||
auto* file = h->GetFile(d);
|
||||
if (file == nullptr) {
|
||||
|
@ -223,6 +229,13 @@ s64 PS4_SYSV_ABI posix_lseek(int d, s64 offset, int whence) {
|
|||
}
|
||||
|
||||
s64 PS4_SYSV_ABI sceKernelRead(int d, void* buf, size_t nbytes) {
|
||||
if (d == 2003) // dev urandom case
|
||||
{
|
||||
auto rbuf = static_cast<char*>(buf);
|
||||
for (size_t i = 0; i < nbytes; i++)
|
||||
rbuf[i] = std::rand() & 0xFF;
|
||||
return nbytes;
|
||||
}
|
||||
auto* h = Common::Singleton<Core::FileSys::HandleTable>::Instance();
|
||||
auto* file = h->GetFile(d);
|
||||
if (file == nullptr) {
|
||||
|
@ -459,7 +472,30 @@ s64 PS4_SYSV_ABI sceKernelPwrite(int d, void* buf, size_t nbytes, s64 offset) {
|
|||
return file->f.WriteRaw<u8>(buf, nbytes);
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI sceKernelRename(const char* from, const char* to) {
|
||||
auto* mnt = Common::Singleton<Core::FileSys::MntPoints>::Instance();
|
||||
const auto src_path = mnt->GetHostPath(from);
|
||||
if (!std::filesystem::exists(src_path)) {
|
||||
return ORBIS_KERNEL_ERROR_ENOENT;
|
||||
}
|
||||
const auto dst_path = mnt->GetHostPath(to);
|
||||
const bool src_is_dir = std::filesystem::is_directory(src_path);
|
||||
const bool dst_is_dir = std::filesystem::is_directory(dst_path);
|
||||
if (src_is_dir && !dst_is_dir) {
|
||||
return ORBIS_KERNEL_ERROR_ENOTDIR;
|
||||
}
|
||||
if (!src_is_dir && dst_is_dir) {
|
||||
return ORBIS_KERNEL_ERROR_EISDIR;
|
||||
}
|
||||
if (dst_is_dir && !std::filesystem::is_empty(dst_path)) {
|
||||
return ORBIS_KERNEL_ERROR_ENOTEMPTY;
|
||||
}
|
||||
std::filesystem::copy(src_path, dst_path, std::filesystem::copy_options::overwrite_existing);
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
void fileSystemSymbolsRegister(Core::Loader::SymbolsResolver* sym) {
|
||||
std::srand(std::time(nullptr));
|
||||
LIB_FUNCTION("1G3lF1Gg1k8", "libkernel", 1, "libkernel", 1, 1, sceKernelOpen);
|
||||
LIB_FUNCTION("wuCroIGjt2g", "libScePosix", 1, "libkernel", 1, 1, posix_open);
|
||||
LIB_FUNCTION("UK2Tl2DWUns", "libkernel", 1, "libkernel", 1, 1, sceKernelClose);
|
||||
|
@ -479,6 +515,7 @@ void fileSystemSymbolsRegister(Core::Loader::SymbolsResolver* sym) {
|
|||
LIB_FUNCTION("kBwCPsYX-m4", "libkernel", 1, "libkernel", 1, 1, sceKernelFStat);
|
||||
LIB_FUNCTION("mqQMh1zPPT8", "libScePosix", 1, "libkernel", 1, 1, posix_fstat);
|
||||
LIB_FUNCTION("VW3TVZiM4-E", "libkernel", 1, "libkernel", 1, 1, sceKernelFtruncate);
|
||||
LIB_FUNCTION("52NcYU9+lEo", "libkernel", 1, "libkernel", 1, 1, sceKernelRename);
|
||||
|
||||
LIB_FUNCTION("E6ao34wPw+U", "libScePosix", 1, "libkernel", 1, 1, posix_stat);
|
||||
LIB_FUNCTION("+r3rMFwItV4", "libkernel", 1, "libkernel", 1, 1, sceKernelPread);
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
#include <boost/asio/io_context.hpp>
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/debug.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/polyfill_thread.h"
|
||||
#include "common/singleton.h"
|
||||
|
@ -84,6 +85,9 @@ static PS4_SYSV_ABI void stack_chk_fail() {
|
|||
|
||||
int PS4_SYSV_ABI sceKernelMunmap(void* addr, size_t len) {
|
||||
LOG_INFO(Kernel_Vmm, "addr = {}, len = {:#x}", fmt::ptr(addr), len);
|
||||
if (len == 0) {
|
||||
return ORBIS_OK;
|
||||
}
|
||||
auto* memory = Core::Memory::Instance();
|
||||
memory->UnmapMemory(std::bit_cast<VAddr>(addr), len);
|
||||
return SCE_OK;
|
||||
|
@ -407,6 +411,7 @@ void LibKernel_Register(Core::Loader::SymbolsResolver* sym) {
|
|||
|
||||
LIB_FUNCTION("2SKEx6bSq-4", "libkernel", 1, "libkernel", 1, 1, sceKernelBatchMap);
|
||||
LIB_FUNCTION("kBJzF8x4SyE", "libkernel", 1, "libkernel", 1, 1, sceKernelBatchMap2);
|
||||
LIB_FUNCTION("DGMG3JshrZU", "libkernel", 1, "libkernel", 1, 1, sceKernelSetVirtualRangeName);
|
||||
|
||||
// equeue
|
||||
LIB_FUNCTION("D0OdFMjp46I", "libkernel", 1, "libkernel", 1, 1, sceKernelCreateEqueue);
|
||||
|
|
|
@ -246,7 +246,8 @@ int PS4_SYSV_ABI sceKernelGetDirectMemoryType(u64 addr, int* directMemoryTypeOut
|
|||
|
||||
s32 PS4_SYSV_ABI sceKernelBatchMap(OrbisKernelBatchMapEntry* entries, int numEntries,
|
||||
int* numEntriesOut) {
|
||||
return sceKernelBatchMap2(entries, numEntries, numEntriesOut, 0x10); // 0x10 : Fixed / 0x410
|
||||
return sceKernelBatchMap2(entries, numEntries, numEntriesOut,
|
||||
MemoryFlags::SCE_KERNEL_MAP_FIXED); // 0x10, 0x410?
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceKernelMunmap(void* addr, size_t len);
|
||||
|
@ -261,7 +262,7 @@ s32 PS4_SYSV_ABI sceKernelBatchMap2(OrbisKernelBatchMapEntry* entries, int numEn
|
|||
break; // break and assign a value to numEntriesOut.
|
||||
}
|
||||
|
||||
if (entries[i].operation == 0) { // MAP_DIRECT
|
||||
if (entries[i].operation == MemoryOpTypes::ORBIS_KERNEL_MAP_OP_MAP_DIRECT) {
|
||||
result = sceKernelMapNamedDirectMemory(&entries[i].start, entries[i].length,
|
||||
entries[i].protection, flags,
|
||||
static_cast<s64>(entries[i].offset), 0, "");
|
||||
|
@ -274,13 +275,18 @@ s32 PS4_SYSV_ABI sceKernelBatchMap2(OrbisKernelBatchMapEntry* entries, int numEn
|
|||
|
||||
if (result == 0)
|
||||
processed++;
|
||||
<<<<<<< HEAD
|
||||
} else if (entries[i].operation == 1) { // UNMAP
|
||||
=======
|
||||
} else if (entries[i].operation == MemoryOpTypes::ORBIS_KERNEL_MAP_OP_UNMAP) {
|
||||
>>>>>>> cdff4af38da1d832e35d8c057d698f38c64b2932
|
||||
result = sceKernelMunmap(entries[i].start, entries[i].length);
|
||||
LOG_INFO(Kernel_Vmm, "BatchMap: entry = {}, operation = {}, len = {:#x}, result = {}",
|
||||
i, entries[i].operation, entries[i].length, result);
|
||||
|
||||
if (result == 0)
|
||||
processed++;
|
||||
<<<<<<< HEAD
|
||||
} else if (entries[i].operation == 4) { // MPROTECT
|
||||
result =
|
||||
sceKernelMProtect(entries[i].start, entries[i].length, entries[i].protection);
|
||||
|
@ -296,6 +302,19 @@ s32 PS4_SYSV_ABI sceKernelBatchMap2(OrbisKernelBatchMapEntry* entries, int numEn
|
|||
}
|
||||
}
|
||||
else {
|
||||
=======
|
||||
} else if (entries[i].operation == MemoryOpTypes::ORBIS_KERNEL_MAP_OP_MAP_FLEXIBLE) {
|
||||
result = sceKernelMapNamedFlexibleMemory(&entries[i].start, entries[i].length,
|
||||
entries[i].protection, flags, "");
|
||||
LOG_INFO(Kernel_Vmm,
|
||||
"BatchMap: entry = {}, operation = {}, len = {:#x}, type = {}, "
|
||||
"result = {}",
|
||||
i, entries[i].operation, entries[i].length, (u8)entries[i].type, result);
|
||||
|
||||
if (result == 0)
|
||||
processed++;
|
||||
} else {
|
||||
>>>>>>> cdff4af38da1d832e35d8c057d698f38c64b2932
|
||||
UNREACHABLE_MSG("called: Unimplemented Operation = {}", entries[i].operation);
|
||||
}
|
||||
}
|
||||
|
@ -305,4 +324,19 @@ s32 PS4_SYSV_ABI sceKernelBatchMap2(OrbisKernelBatchMapEntry* entries, int numEn
|
|||
return result;
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI sceKernelSetVirtualRangeName(const void* addr, size_t len, const char* name) {
|
||||
static constexpr size_t MaxNameSize = 32;
|
||||
if (std::strlen(name) > MaxNameSize) {
|
||||
LOG_ERROR(Kernel_Vmm, "name exceeds 32 bytes!");
|
||||
return ORBIS_KERNEL_ERROR_ENAMETOOLONG;
|
||||
}
|
||||
|
||||
if (name == nullptr) {
|
||||
LOG_ERROR(Kernel_Vmm, "name is invalid!");
|
||||
return ORBIS_KERNEL_ERROR_EFAULT;
|
||||
}
|
||||
auto* memory = Core::Memory::Instance();
|
||||
memory->NameVirtualRange(std::bit_cast<VAddr>(addr), len, name);
|
||||
return ORBIS_OK;
|
||||
}
|
||||
} // namespace Libraries::Kernel
|
||||
|
|
|
@ -31,6 +31,14 @@ enum MemoryProtection : u32 {
|
|||
SCE_KERNEL_PROT_GPU_RW = 0x30 // Permit reads/writes from the GPU
|
||||
};
|
||||
|
||||
enum MemoryOpTypes : u32 {
|
||||
ORBIS_KERNEL_MAP_OP_MAP_DIRECT = 0,
|
||||
ORBIS_KERNEL_MAP_OP_UNMAP = 1,
|
||||
ORBIS_KERNEL_MAP_OP_PROTECT = 2,
|
||||
ORBIS_KERNEL_MAP_OP_MAP_FLEXIBLE = 3,
|
||||
ORBIS_KERNEL_MAP_OP_TYPE_PROTECT = 4
|
||||
};
|
||||
|
||||
struct OrbisQueryInfo {
|
||||
uintptr_t start;
|
||||
uintptr_t end;
|
||||
|
@ -100,4 +108,6 @@ s32 PS4_SYSV_ABI sceKernelBatchMap(OrbisKernelBatchMapEntry* entries, int numEnt
|
|||
s32 PS4_SYSV_ABI sceKernelBatchMap2(OrbisKernelBatchMapEntry* entries, int numEntries,
|
||||
int* numEntriesOut, int flags);
|
||||
|
||||
s32 PS4_SYSV_ABI sceKernelSetVirtualRangeName(const void* addr, size_t len, const char* name);
|
||||
|
||||
} // namespace Libraries::Kernel
|
||||
|
|
|
@ -394,6 +394,18 @@ int PS4_SYSV_ABI scePthreadSetaffinity(ScePthread thread, const /*SceKernelCpuma
|
|||
return result;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI scePthreadGetaffinity(ScePthread thread, /*SceKernelCpumask*/ u64* mask) {
|
||||
LOG_INFO(Kernel_Pthread, "called");
|
||||
|
||||
if (thread == nullptr) {
|
||||
return SCE_KERNEL_ERROR_ESRCH;
|
||||
}
|
||||
|
||||
auto result = scePthreadAttrGetaffinity(&thread->attr, mask);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
ScePthreadMutex* createMutex(ScePthreadMutex* addr) {
|
||||
if (addr == nullptr || *addr != nullptr) {
|
||||
return addr;
|
||||
|
@ -427,11 +439,7 @@ int PS4_SYSV_ABI scePthreadMutexInit(ScePthreadMutex* mutex, const ScePthreadMut
|
|||
|
||||
int result = pthread_mutex_init(&(*mutex)->pth_mutex, &(*attr)->pth_mutex_attr);
|
||||
|
||||
static auto mutex_loc = MUTEX_LOCATION("mutex");
|
||||
(*mutex)->tracy_lock = std::make_unique<tracy::LockableCtx>(&mutex_loc);
|
||||
|
||||
if (name != nullptr) {
|
||||
(*mutex)->tracy_lock->CustomName(name, std::strlen(name));
|
||||
LOG_INFO(Kernel_Pthread, "name={}, result={}", name, result);
|
||||
}
|
||||
|
||||
|
@ -543,15 +551,11 @@ int PS4_SYSV_ABI scePthreadMutexLock(ScePthreadMutex* mutex) {
|
|||
return SCE_KERNEL_ERROR_EINVAL;
|
||||
}
|
||||
|
||||
(*mutex)->tracy_lock->BeforeLock();
|
||||
|
||||
int result = pthread_mutex_lock(&(*mutex)->pth_mutex);
|
||||
if (result != 0) {
|
||||
LOG_TRACE(Kernel_Pthread, "Locked name={}, result={}", (*mutex)->name, result);
|
||||
}
|
||||
|
||||
(*mutex)->tracy_lock->AfterLock();
|
||||
|
||||
switch (result) {
|
||||
case 0:
|
||||
return SCE_OK;
|
||||
|
@ -577,8 +581,6 @@ int PS4_SYSV_ABI scePthreadMutexUnlock(ScePthreadMutex* mutex) {
|
|||
LOG_TRACE(Kernel_Pthread, "Unlocking name={}, result={}", (*mutex)->name, result);
|
||||
}
|
||||
|
||||
(*mutex)->tracy_lock->AfterUnlock();
|
||||
|
||||
switch (result) {
|
||||
case 0:
|
||||
return SCE_OK;
|
||||
|
@ -1183,8 +1185,6 @@ int PS4_SYSV_ABI scePthreadMutexTrylock(ScePthreadMutex* mutex) {
|
|||
LOG_TRACE(Kernel_Pthread, "name={}, result={}", (*mutex)->name, result);
|
||||
}
|
||||
|
||||
(*mutex)->tracy_lock->AfterTryLock(result == 0);
|
||||
|
||||
switch (result) {
|
||||
case 0:
|
||||
return ORBIS_OK;
|
||||
|
@ -1243,6 +1243,40 @@ int PS4_SYSV_ABI posix_pthread_attr_destroy(ScePthreadAttr* attr) {
|
|||
return result;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI posix_pthread_attr_setschedparam(ScePthreadAttr* attr,
|
||||
const SceKernelSchedParam* param) {
|
||||
int result = scePthreadAttrSetschedparam(attr, param);
|
||||
if (result < 0) {
|
||||
int rt = result > SCE_KERNEL_ERROR_UNKNOWN && result <= SCE_KERNEL_ERROR_ESTOP
|
||||
? result + -SCE_KERNEL_ERROR_UNKNOWN
|
||||
: POSIX_EOTHER;
|
||||
return rt;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI posix_pthread_attr_setinheritsched(ScePthreadAttr* attr, int inheritSched) {
|
||||
int result = scePthreadAttrSetinheritsched(attr, inheritSched);
|
||||
if (result < 0) {
|
||||
int rt = result > SCE_KERNEL_ERROR_UNKNOWN && result <= SCE_KERNEL_ERROR_ESTOP
|
||||
? result + -SCE_KERNEL_ERROR_UNKNOWN
|
||||
: POSIX_EOTHER;
|
||||
return rt;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI posix_pthread_setprio(ScePthread thread, int prio) {
|
||||
int result = scePthreadSetprio(thread, prio);
|
||||
if (result < 0) {
|
||||
int rt = result > SCE_KERNEL_ERROR_UNKNOWN && result <= SCE_KERNEL_ERROR_ESTOP
|
||||
? result + -SCE_KERNEL_ERROR_UNKNOWN
|
||||
: POSIX_EOTHER;
|
||||
return rt;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI posix_pthread_attr_setdetachstate(ScePthreadAttr* attr, int detachstate) {
|
||||
// LOG_INFO(Kernel_Pthread, "posix pthread_mutexattr_init redirect to scePthreadMutexattrInit");
|
||||
int result = scePthreadAttrSetdetachstate(attr, detachstate);
|
||||
|
@ -1336,14 +1370,56 @@ int PS4_SYSV_ABI posix_sem_wait(sem_t* sem) {
|
|||
return sem_wait(sem);
|
||||
}
|
||||
|
||||
#ifndef HAVE_SEM_TIMEDWAIT
|
||||
int sem_timedwait(sem_t* sem, const struct timespec* abstime) {
|
||||
int rc;
|
||||
while ((rc = sem_trywait(sem)) == EAGAIN) {
|
||||
struct timespec curr_time;
|
||||
clock_gettime(CLOCK_REALTIME, &curr_time);
|
||||
|
||||
s64 remaining_ns = 0;
|
||||
remaining_ns +=
|
||||
(static_cast<s64>(abstime->tv_sec) - static_cast<s64>(curr_time.tv_sec)) * 1000000000L;
|
||||
remaining_ns += static_cast<s64>(abstime->tv_nsec) - static_cast<s64>(curr_time.tv_nsec);
|
||||
|
||||
if (remaining_ns <= 0) {
|
||||
return ETIMEDOUT;
|
||||
}
|
||||
|
||||
struct timespec sleep_time;
|
||||
sleep_time.tv_sec = 0;
|
||||
if (remaining_ns < 5000000L) {
|
||||
sleep_time.tv_nsec = remaining_ns;
|
||||
} else {
|
||||
sleep_time.tv_nsec = 5000000;
|
||||
}
|
||||
|
||||
nanosleep(&sleep_time, nullptr);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
#endif
|
||||
|
||||
int PS4_SYSV_ABI posix_sem_timedwait(sem_t* sem, const timespec* t) {
|
||||
return sem_timedwait(sem, t);
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI posix_sem_post(sem_t* sem) {
|
||||
return sem_post(sem);
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI posix_sem_destroy(sem_t* sem) {
|
||||
return sem_destroy(sem);
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI posix_sem_getvalue(sem_t* sem, int* sval) {
|
||||
return sem_getvalue(sem, sval);
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI posix_pthread_attr_getstacksize(const pthread_attr_t* attr, size_t* size) {
|
||||
return pthread_attr_getstacksize(attr, size);
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI scePthreadGetschedparam(ScePthread thread, int* policy,
|
||||
SceKernelSchedParam* param) {
|
||||
return pthread_getschedparam(thread->pth, policy, param);
|
||||
|
@ -1403,6 +1479,26 @@ int PS4_SYSV_ABI posix_pthread_condattr_setclock(ScePthreadCondattr* attr, clock
|
|||
return SCE_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI posix_pthread_getschedparam(ScePthread thread, int* policy,
|
||||
SceKernelSchedParam* param) {
|
||||
return scePthreadGetschedparam(thread, policy, param);
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI posix_pthread_setschedparam(ScePthread thread, int policy,
|
||||
const SceKernelSchedParam* param) {
|
||||
return scePthreadSetschedparam(thread, policy, param);
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI posix_pthread_attr_getschedpolicy(const ScePthreadAttr* attr, int* policy) {
|
||||
return scePthreadAttrGetschedpolicy(attr, policy);
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI scePthreadRename(ScePthread thread, const char* name) {
|
||||
thread->name = name;
|
||||
LOG_INFO(Kernel_Pthread, "scePthreadRename: name = {}", thread->name);
|
||||
return SCE_OK;
|
||||
}
|
||||
|
||||
void pthreadSymbolsRegister(Core::Loader::SymbolsResolver* sym) {
|
||||
LIB_FUNCTION("lZzFeSxPl08", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_setcancelstate);
|
||||
LIB_FUNCTION("0TyVk4MSLt0", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_cond_init);
|
||||
|
@ -1427,6 +1523,7 @@ void pthreadSymbolsRegister(Core::Loader::SymbolsResolver* sym) {
|
|||
LIB_FUNCTION("EI-5-jlq2dE", "libkernel", 1, "libkernel", 1, 1, scePthreadGetthreadid);
|
||||
LIB_FUNCTION("1tKyG7RlMJo", "libkernel", 1, "libkernel", 1, 1, scePthreadGetprio);
|
||||
LIB_FUNCTION("W0Hpm2X0uPE", "libkernel", 1, "libkernel", 1, 1, scePthreadSetprio);
|
||||
LIB_FUNCTION("GBUY7ywdULE", "libkernel", 1, "libkernel", 1, 1, scePthreadRename);
|
||||
|
||||
LIB_FUNCTION("aI+OeCz8xrQ", "libkernel", 1, "libkernel", 1, 1, scePthreadSelf);
|
||||
LIB_FUNCTION("EotR8a3ASf4", "libkernel", 1, "libkernel", 1, 1, posix_pthread_self);
|
||||
|
@ -1442,6 +1539,7 @@ void pthreadSymbolsRegister(Core::Loader::SymbolsResolver* sym) {
|
|||
LIB_FUNCTION("OxhIB8LB-PQ", "libkernel", 1, "libkernel", 1, 1, posix_pthread_create);
|
||||
LIB_FUNCTION("OxhIB8LB-PQ", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_create);
|
||||
LIB_FUNCTION("bt3CTBKmGyI", "libkernel", 1, "libkernel", 1, 1, scePthreadSetaffinity);
|
||||
LIB_FUNCTION("rcrVFJsQWRY", "libkernel", 1, "libkernel", 1, 1, scePthreadGetaffinity);
|
||||
LIB_FUNCTION("6UgtwV+0zb4", "libkernel", 1, "libkernel", 1, 1, scePthreadCreate);
|
||||
LIB_FUNCTION("T72hz6ffq08", "libkernel", 1, "libkernel", 1, 1, scePthreadYield);
|
||||
LIB_FUNCTION("B5GmVDKwpn0", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_yield);
|
||||
|
@ -1498,6 +1596,8 @@ void pthreadSymbolsRegister(Core::Loader::SymbolsResolver* sym) {
|
|||
LIB_FUNCTION("EjllaAqAPZo", "libScePosix", 1, "libkernel", 1, 1,
|
||||
posix_pthread_condattr_setclock);
|
||||
LIB_FUNCTION("Z4QosVuAsA0", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_once);
|
||||
LIB_FUNCTION("RtLRV-pBTTY", "libScePosix", 1, "libkernel", 1, 1,
|
||||
posix_pthread_attr_getschedpolicy);
|
||||
|
||||
// openorbis weird functions
|
||||
LIB_FUNCTION("7H0iTOciTLo", "libkernel", 1, "libkernel", 1, 1, posix_pthread_mutex_lock);
|
||||
|
@ -1507,15 +1607,26 @@ void pthreadSymbolsRegister(Core::Loader::SymbolsResolver* sym) {
|
|||
LIB_FUNCTION("E+tyo3lp5Lw", "libScePosix", 1, "libkernel", 1, 1,
|
||||
posix_pthread_attr_setdetachstate);
|
||||
LIB_FUNCTION("zHchY8ft5pk", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_attr_destroy);
|
||||
LIB_FUNCTION("euKRgm0Vn2M", "libScePosix", 1, "libkernel", 1, 1,
|
||||
posix_pthread_attr_setschedparam);
|
||||
LIB_FUNCTION("7ZlAakEf0Qg", "libScePosix", 1, "libkernel", 1, 1,
|
||||
posix_pthread_attr_setinheritsched);
|
||||
LIB_FUNCTION("a2P9wYGeZvc", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_setprio);
|
||||
LIB_FUNCTION("Jmi+9w9u0E4", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_create_name_np);
|
||||
LIB_FUNCTION("OxhIB8LB-PQ", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_create);
|
||||
LIB_FUNCTION("+U1R4WtXvoc", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_detach);
|
||||
LIB_FUNCTION("CBNtXOoef-E", "libScePosix", 1, "libkernel", 1, 1, posix_sched_get_priority_max);
|
||||
LIB_FUNCTION("m0iS6jNsXds", "libScePosix", 1, "libkernel", 1, 1, posix_sched_get_priority_min);
|
||||
LIB_FUNCTION("FIs3-UQT9sg", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_getschedparam);
|
||||
LIB_FUNCTION("Xs9hdiD7sAA", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_setschedparam);
|
||||
LIB_FUNCTION("pDuPEf3m4fI", "libScePosix", 1, "libkernel", 1, 1, posix_sem_init);
|
||||
LIB_FUNCTION("YCV5dGGBcCo", "libScePosix", 1, "libkernel", 1, 1, posix_sem_wait);
|
||||
LIB_FUNCTION("w5IHyvahg-o", "libScePosix", 1, "libkernel", 1, 1, posix_sem_timedwait);
|
||||
LIB_FUNCTION("IKP8typ0QUk", "libScePosix", 1, "libkernel", 1, 1, posix_sem_post);
|
||||
LIB_FUNCTION("cDW233RAwWo", "libScePosix", 1, "libkernel", 1, 1, posix_sem_destroy);
|
||||
LIB_FUNCTION("Bq+LRV-N6Hk", "libScePosix", 1, "libkernel", 1, 1, posix_sem_getvalue);
|
||||
LIB_FUNCTION("0qOtCR-ZHck", "libScePosix", 1, "libkernel", 1, 1,
|
||||
posix_pthread_attr_getstacksize);
|
||||
// libs
|
||||
RwlockSymbolsRegister(sym);
|
||||
SemaphoreSymbolsRegister(sym);
|
||||
|
|
|
@ -9,7 +9,6 @@
|
|||
#include <vector>
|
||||
#include <pthread.h>
|
||||
#include <sched.h>
|
||||
#include "common/debug.h"
|
||||
#include "common/types.h"
|
||||
|
||||
namespace Core::Loader {
|
||||
|
@ -74,7 +73,6 @@ struct PthreadMutexInternal {
|
|||
u8 reserved[256];
|
||||
std::string name;
|
||||
pthread_mutex_t pth_mutex;
|
||||
std::unique_ptr<tracy::LockableCtx> tracy_lock;
|
||||
};
|
||||
|
||||
struct PthreadMutexattrInternal {
|
||||
|
@ -169,9 +167,12 @@ ScePthread PS4_SYSV_ABI scePthreadSelf();
|
|||
int PS4_SYSV_ABI scePthreadAttrSetaffinity(ScePthreadAttr* pattr,
|
||||
const /*SceKernelCpumask*/ u64 mask);
|
||||
int PS4_SYSV_ABI scePthreadSetaffinity(ScePthread thread, const /*SceKernelCpumask*/ u64 mask);
|
||||
int PS4_SYSV_ABI scePthreadGetaffinity(ScePthread thread, /*SceKernelCpumask*/ u64* mask);
|
||||
int PS4_SYSV_ABI scePthreadCreate(ScePthread* thread, const ScePthreadAttr* attr,
|
||||
PthreadEntryFunc start_routine, void* arg, const char* name);
|
||||
|
||||
int PS4_SYSV_ABI scePthreadSetprio(ScePthread thread, int prio);
|
||||
|
||||
/***
|
||||
* Mutex calls
|
||||
*/
|
||||
|
|
|
@ -41,7 +41,6 @@ public:
|
|||
AddWaiter(waiter);
|
||||
|
||||
// Perform the wait.
|
||||
std::exchange(lk, std::unique_lock{waiter.mutex});
|
||||
return waiter.Wait(lk, timeout);
|
||||
}
|
||||
|
||||
|
@ -59,10 +58,9 @@ public:
|
|||
it++;
|
||||
continue;
|
||||
}
|
||||
std::scoped_lock lk2{waiter.mutex};
|
||||
it = wait_list.erase(it);
|
||||
token_count -= waiter.need_count;
|
||||
waiter.cv.notify_one();
|
||||
it = wait_list.erase(it);
|
||||
}
|
||||
|
||||
return true;
|
||||
|
@ -84,7 +82,6 @@ public:
|
|||
|
||||
public:
|
||||
struct WaitingThread : public ListBaseHook {
|
||||
std::mutex mutex;
|
||||
std::string name;
|
||||
std::condition_variable cv;
|
||||
u32 priority;
|
||||
|
|
|
@ -214,6 +214,22 @@ int PS4_SYSV_ABI posix_clock_getres(u32 clock_id, OrbisKernelTimespec* res) {
|
|||
return SCE_KERNEL_ERROR_EINVAL;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceKernelConvertLocaltimeToUtc(time_t param_1, int64_t param_2, time_t* seconds,
|
||||
OrbisKernelTimezone* timezone, int* dst_seconds) {
|
||||
LOG_INFO(Kernel, "called");
|
||||
if (timezone) {
|
||||
sceKernelGettimezone(timezone);
|
||||
param_1 -= (timezone->tz_minuteswest + timezone->tz_dsttime) * 60;
|
||||
if (seconds)
|
||||
*seconds = param_1;
|
||||
if (dst_seconds)
|
||||
*dst_seconds = timezone->tz_dsttime * 60;
|
||||
} else {
|
||||
return SCE_KERNEL_ERROR_EINVAL;
|
||||
}
|
||||
return SCE_OK;
|
||||
}
|
||||
|
||||
void timeSymbolsRegister(Core::Loader::SymbolsResolver* sym) {
|
||||
clock = std::make_unique<Common::NativeClock>();
|
||||
initial_ptc = clock->GetUptime();
|
||||
|
@ -239,6 +255,7 @@ void timeSymbolsRegister(Core::Loader::SymbolsResolver* sym) {
|
|||
LIB_FUNCTION("lLMT9vJAck0", "libkernel", 1, "libkernel", 1, 1, posix_clock_gettime);
|
||||
LIB_FUNCTION("lLMT9vJAck0", "libScePosix", 1, "libkernel", 1, 1, posix_clock_gettime);
|
||||
LIB_FUNCTION("smIj7eqzZE8", "libScePosix", 1, "libkernel", 1, 1, posix_clock_getres);
|
||||
LIB_FUNCTION("0NTHN1NKONI", "libkernel", 1, "libkernel", 1, 1, sceKernelConvertLocaltimeToUtc);
|
||||
}
|
||||
|
||||
} // namespace Libraries::Kernel
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
#include "core/libraries/np_trophy/np_trophy.h"
|
||||
#include "core/libraries/pad/pad.h"
|
||||
#include "core/libraries/playgo/playgo.h"
|
||||
#include "core/libraries/random/random.h"
|
||||
#include "core/libraries/rtc/rtc.h"
|
||||
#include "core/libraries/save_data/savedata.h"
|
||||
#include "core/libraries/screenshot/screenshot.h"
|
||||
|
@ -43,8 +44,8 @@ namespace Libraries {
|
|||
void InitHLELibs(Core::Loader::SymbolsResolver* sym) {
|
||||
LOG_INFO(Lib_Kernel, "Initializing HLE libraries");
|
||||
Libraries::Kernel::LibKernel_Register(sym);
|
||||
Libraries::VideoOut::RegisterLib(sym);
|
||||
Libraries::GnmDriver::RegisterlibSceGnmDriver(sym);
|
||||
Libraries::VideoOut::RegisterLib(sym);
|
||||
if (!Config::isLleLibc()) {
|
||||
Libraries::LibC::libcSymbolsRegister(sym);
|
||||
}
|
||||
|
@ -71,6 +72,7 @@ void InitHLELibs(Core::Loader::SymbolsResolver* sym) {
|
|||
Libraries::AppContent::RegisterlibSceAppContent(sym);
|
||||
Libraries::PngDec::RegisterlibScePngDec(sym);
|
||||
Libraries::PlayGo::RegisterlibScePlayGo(sym);
|
||||
Libraries::Random::RegisterlibSceRandom(sym);
|
||||
Libraries::Usbd::RegisterlibSceUsbd(sym);
|
||||
Libraries::Pad::RegisterlibScePad(sym);
|
||||
Libraries::Ajm::RegisterlibSceAjm(sym);
|
||||
|
|
|
@ -559,7 +559,7 @@ int PS4_SYSV_ABI sceNetEpollDestroy() {
|
|||
}
|
||||
|
||||
int PS4_SYSV_ABI sceNetEpollWait() {
|
||||
LOG_ERROR(Lib_Net, "(STUBBED) called");
|
||||
LOG_TRACE(Lib_Net, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
|
|
|
@ -79,7 +79,7 @@ int PS4_SYSV_ABI sceNetCtlUnregisterCallbackV6() {
|
|||
}
|
||||
|
||||
int PS4_SYSV_ABI sceNetCtlCheckCallback() {
|
||||
LOG_ERROR(Lib_NetCtl, "(STUBBED) called");
|
||||
LOG_TRACE(Lib_NetCtl, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
|
|
|
@ -870,7 +870,7 @@ int PS4_SYSV_ABI sceNpAsmTerminate() {
|
|||
}
|
||||
|
||||
int PS4_SYSV_ABI sceNpCheckCallback() {
|
||||
LOG_ERROR(Lib_NpManager, "(STUBBED) called");
|
||||
LOG_TRACE(Lib_NpManager, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
|
@ -3510,4 +3510,4 @@ void RegisterlibSceNpManager(Core::Loader::SymbolsResolver* sym) {
|
|||
sceNpUnregisterStateCallbackForToolkit);
|
||||
};
|
||||
|
||||
} // namespace Libraries::NpManager
|
||||
} // namespace Libraries::NpManager
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <core/file_format/playgo_chunk.h>
|
||||
#include "common/logging/log.h"
|
||||
#include "common/singleton.h"
|
||||
#include "core/libraries/error_codes.h"
|
||||
|
@ -50,9 +51,16 @@ s32 PS4_SYSV_ABI scePlayGoGetLocus(OrbisPlayGoHandle handle, const OrbisPlayGoCh
|
|||
uint32_t numberOfEntries, OrbisPlayGoLocus* outLoci) {
|
||||
LOG_ERROR(Lib_PlayGo, "(STUBBED)called handle = {}, chunkIds = {}, numberOfEntries = {}",
|
||||
handle, *chunkIds, numberOfEntries);
|
||||
// assign all now so that scePlayGoGetLocus is not called again for every single entry
|
||||
std::fill(outLoci, outLoci + numberOfEntries,
|
||||
OrbisPlayGoLocusValue::ORBIS_PLAYGO_LOCUS_LOCAL_FAST);
|
||||
|
||||
auto* playgo = Common::Singleton<PlaygoChunk>::Instance();
|
||||
|
||||
for (uint32_t i = 0; i < numberOfEntries; i++) {
|
||||
if (chunkIds[i] <= playgo->GetPlaygoHeader().mchunk_count) {
|
||||
outLoci[i] = OrbisPlayGoLocusValue::ORBIS_PLAYGO_LOCUS_LOCAL_FAST;
|
||||
} else {
|
||||
return ORBIS_PLAYGO_ERROR_BAD_CHUNK_ID;
|
||||
}
|
||||
}
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
|
@ -68,7 +76,7 @@ s32 PS4_SYSV_ABI scePlayGoGetProgress(OrbisPlayGoHandle handle, const OrbisPlayG
|
|||
s32 PS4_SYSV_ABI scePlayGoGetToDoList(OrbisPlayGoHandle handle, OrbisPlayGoToDo* outTodoList,
|
||||
u32 numberOfEntries, u32* outEntries) {
|
||||
LOG_ERROR(Lib_PlayGo, "(STUBBED)called");
|
||||
if (handle != shadMagic)
|
||||
if (handle != 1)
|
||||
return ORBIS_PLAYGO_ERROR_BAD_HANDLE;
|
||||
if (outTodoList == nullptr)
|
||||
return ORBIS_PLAYGO_ERROR_BAD_POINTER;
|
||||
|
@ -86,7 +94,7 @@ s32 PS4_SYSV_ABI scePlayGoInitialize(OrbisPlayGoInitParams* param) {
|
|||
}
|
||||
|
||||
s32 PS4_SYSV_ABI scePlayGoOpen(OrbisPlayGoHandle* outHandle, const void* param) {
|
||||
*outHandle = shadMagic;
|
||||
*outHandle = 1;
|
||||
LOG_INFO(Lib_PlayGo, "(STUBBED)called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
|
27
src/core/libraries/random/random.cpp
Normal file
27
src/core/libraries/random/random.cpp
Normal file
|
@ -0,0 +1,27 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "common/logging/log.h"
|
||||
#include "core/libraries/error_codes.h"
|
||||
#include "core/libraries/libs.h"
|
||||
#include "random.h"
|
||||
|
||||
namespace Libraries::Random {
|
||||
|
||||
s32 PS4_SYSV_ABI sceRandomGetRandomNumber(u8* buf, size_t size) {
|
||||
LOG_TRACE(Lib_Random, "called");
|
||||
if (size > SCE_RANDOM_MAX_SIZE) {
|
||||
return SCE_RANDOM_ERROR_INVALID;
|
||||
}
|
||||
|
||||
for (auto i = 0; i < size; ++i) {
|
||||
buf[i] = std::rand() & 0xFF;
|
||||
}
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
void RegisterlibSceRandom(Core::Loader::SymbolsResolver* sym) {
|
||||
LIB_FUNCTION("PI7jIZj4pcE", "libSceRandom", 1, "libSceRandom", 1, 1, sceRandomGetRandomNumber);
|
||||
};
|
||||
|
||||
} // namespace Libraries::Random
|
17
src/core/libraries/random/random.h
Normal file
17
src/core/libraries/random/random.h
Normal file
|
@ -0,0 +1,17 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
#include "common/types.h"
|
||||
|
||||
namespace Core::Loader {
|
||||
class SymbolsResolver;
|
||||
}
|
||||
|
||||
namespace Libraries::Random {
|
||||
constexpr int32_t SCE_RANDOM_MAX_SIZE = 64;
|
||||
|
||||
s32 PS4_SYSV_ABI sceRandomGetRandomNumber(u8* buf, size_t size);
|
||||
|
||||
void RegisterlibSceRandom(Core::Loader::SymbolsResolver* sym);
|
||||
} // namespace Libraries::Random
|
|
@ -15,7 +15,7 @@
|
|||
#include "error_codes.h"
|
||||
|
||||
namespace Libraries::SaveData {
|
||||
|
||||
bool is_rw_mode = false;
|
||||
static constexpr std::string_view g_mount_point = "/savedata0"; // temp mount point (todo)
|
||||
std::string game_serial;
|
||||
|
||||
|
@ -180,27 +180,31 @@ int PS4_SYSV_ABI sceSaveDataDirNameSearch(const OrbisSaveDataDirNameSearchCond*
|
|||
OrbisSaveDataDirNameSearchResult* result) {
|
||||
if (cond == nullptr)
|
||||
return ORBIS_SAVE_DATA_ERROR_PARAMETER;
|
||||
LOG_ERROR(Lib_SaveData, "TODO sceSaveDataDirNameSearch: Add params");
|
||||
LOG_INFO(Lib_SaveData, "called");
|
||||
const auto& mount_dir = Common::FS::GetUserPath(Common::FS::PathType::SaveDataDir) /
|
||||
std::to_string(cond->userId) / game_serial;
|
||||
if (!mount_dir.empty() && std::filesystem::exists(mount_dir)) {
|
||||
if (cond->dirName == nullptr) { // look for all dirs if no dir is provided.
|
||||
if (cond->dirName == nullptr || std::string_view(cond->dirName->data)
|
||||
.empty()) { // look for all dirs if no dir is provided.
|
||||
for (int i = 0; const auto& entry : std::filesystem::directory_iterator(mount_dir)) {
|
||||
if (std::filesystem::is_directory(entry.path())) {
|
||||
i++;
|
||||
result->dirNamesNum = 0; // why is it 1024? is it max?
|
||||
if (std::filesystem::is_directory(entry.path()) &&
|
||||
entry.path().filename().string() != "sdmemory") {
|
||||
// sceSaveDataDirNameSearch does not search for dataMemory1/2 dirs.
|
||||
// copy dir name to be used by sceSaveDataMount in read mode.
|
||||
strncpy(result->dirNames[i].data, entry.path().filename().string().c_str(), 32);
|
||||
result->hitNum = i + 1;
|
||||
result->dirNamesNum = i + 1; // to confirm
|
||||
result->setNum = i + 1; // to confirm
|
||||
i++;
|
||||
result->hitNum = i;
|
||||
result->dirNamesNum = i;
|
||||
result->setNum = i;
|
||||
}
|
||||
}
|
||||
} else { // Need a game to test.
|
||||
LOG_ERROR(Lib_SaveData, "Check Me. sceSaveDataDirNameSearch: dirName = {}",
|
||||
cond->dirName->data);
|
||||
strncpy(result->dirNames[0].data, cond->dirName->data, 32);
|
||||
result->hitNum = 1;
|
||||
result->dirNamesNum = 1; // to confirm
|
||||
result->setNum = 1; // to confirm
|
||||
result->dirNamesNum = 1;
|
||||
result->setNum = 1;
|
||||
}
|
||||
} else {
|
||||
result->hitNum = 0;
|
||||
|
@ -303,8 +307,51 @@ int PS4_SYSV_ABI sceSaveDataGetMountInfo(const OrbisSaveDataMountPoint* mountPoi
|
|||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceSaveDataGetParam() {
|
||||
LOG_ERROR(Lib_SaveData, "(STUBBED) called");
|
||||
int PS4_SYSV_ABI sceSaveDataGetParam(const OrbisSaveDataMountPoint* mountPoint,
|
||||
const OrbisSaveDataParamType paramType, void* paramBuf,
|
||||
const size_t paramBufSize, size_t* gotSize) {
|
||||
|
||||
if (mountPoint == nullptr)
|
||||
return ORBIS_SAVE_DATA_ERROR_PARAMETER;
|
||||
|
||||
auto* mnt = Common::Singleton<Core::FileSys::MntPoints>::Instance();
|
||||
const auto mount_dir = mnt->GetHostPath(mountPoint->data);
|
||||
Common::FS::IOFile file(mount_dir / "param.txt", Common::FS::FileAccessMode::Read);
|
||||
OrbisSaveDataParam params;
|
||||
file.Read(params);
|
||||
|
||||
LOG_INFO(Lib_SaveData, "called");
|
||||
|
||||
switch (paramType) {
|
||||
case ORBIS_SAVE_DATA_PARAM_TYPE_ALL: {
|
||||
memcpy(paramBuf, ¶ms, sizeof(OrbisSaveDataParam));
|
||||
*gotSize = sizeof(OrbisSaveDataParam);
|
||||
} break;
|
||||
case ORBIS_SAVE_DATA_PARAM_TYPE_TITLE: {
|
||||
std::memcpy(paramBuf, ¶ms.title, ORBIS_SAVE_DATA_TITLE_MAXSIZE);
|
||||
*gotSize = ORBIS_SAVE_DATA_TITLE_MAXSIZE;
|
||||
} break;
|
||||
case ORBIS_SAVE_DATA_PARAM_TYPE_SUB_TITLE: {
|
||||
std::memcpy(paramBuf, ¶ms.subTitle, ORBIS_SAVE_DATA_SUBTITLE_MAXSIZE);
|
||||
*gotSize = ORBIS_SAVE_DATA_SUBTITLE_MAXSIZE;
|
||||
} break;
|
||||
case ORBIS_SAVE_DATA_PARAM_TYPE_DETAIL: {
|
||||
std::memcpy(paramBuf, ¶ms.detail, ORBIS_SAVE_DATA_DETAIL_MAXSIZE);
|
||||
*gotSize = ORBIS_SAVE_DATA_DETAIL_MAXSIZE;
|
||||
} break;
|
||||
case ORBIS_SAVE_DATA_PARAM_TYPE_USER_PARAM: {
|
||||
std::memcpy(paramBuf, ¶ms.userParam, sizeof(u32));
|
||||
*gotSize = sizeof(u32);
|
||||
} break;
|
||||
case ORBIS_SAVE_DATA_PARAM_TYPE_MTIME: {
|
||||
std::memcpy(paramBuf, ¶ms.mtime, sizeof(time_t));
|
||||
*gotSize = sizeof(time_t);
|
||||
} break;
|
||||
default: {
|
||||
UNREACHABLE_MSG("Unknown Param = {}", paramType);
|
||||
} break;
|
||||
}
|
||||
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
|
@ -321,7 +368,7 @@ int PS4_SYSV_ABI sceSaveDataGetSaveDataCount() {
|
|||
int PS4_SYSV_ABI sceSaveDataGetSaveDataMemory(const u32 userId, void* buf, const size_t bufSize,
|
||||
const int64_t offset) {
|
||||
const auto& mount_dir = Common::FS::GetUserPath(Common::FS::PathType::SaveDataDir) /
|
||||
std::to_string(userId) / game_serial / "save_mem1.sav";
|
||||
std::to_string(userId) / game_serial / "sdmemory/save_mem1.sav";
|
||||
|
||||
Common::FS::IOFile file(mount_dir, Common::FS::FileAccessMode::Read);
|
||||
if (!file.IsOpen()) {
|
||||
|
@ -336,7 +383,7 @@ int PS4_SYSV_ABI sceSaveDataGetSaveDataMemory(const u32 userId, void* buf, const
|
|||
|
||||
int PS4_SYSV_ABI sceSaveDataGetSaveDataMemory2(OrbisSaveDataMemoryGet2* getParam) {
|
||||
const auto& mount_dir = Common::FS::GetUserPath(Common::FS::PathType::SaveDataDir) /
|
||||
std::to_string(getParam->userId) / game_serial;
|
||||
std::to_string(getParam->userId) / game_serial / "sdmemory";
|
||||
if (getParam == nullptr)
|
||||
return ORBIS_SAVE_DATA_ERROR_PARAMETER;
|
||||
if (getParam->data != nullptr) {
|
||||
|
@ -443,6 +490,7 @@ s32 saveDataMount(u32 user_id, char* dir_name, u32 mount_mode,
|
|||
case ORBIS_SAVE_DATA_MOUNT_MODE_RDWR:
|
||||
case ORBIS_SAVE_DATA_MOUNT_MODE_RDWR | ORBIS_SAVE_DATA_MOUNT_MODE_DESTRUCT_OFF:
|
||||
case ORBIS_SAVE_DATA_MOUNT_MODE_RDONLY | ORBIS_SAVE_DATA_MOUNT_MODE_DESTRUCT_OFF: {
|
||||
is_rw_mode = (mount_mode == ORBIS_SAVE_DATA_MOUNT_MODE_RDWR) ? true : false;
|
||||
if (!std::filesystem::exists(mount_dir)) {
|
||||
return ORBIS_SAVE_DATA_ERROR_NOT_FOUND;
|
||||
}
|
||||
|
@ -460,10 +508,6 @@ s32 saveDataMount(u32 user_id, char* dir_name, u32 mount_mode,
|
|||
case ORBIS_SAVE_DATA_MOUNT_MODE_CREATE | ORBIS_SAVE_DATA_MOUNT_MODE_DESTRUCT_OFF |
|
||||
ORBIS_SAVE_DATA_MOUNT_MODE_COPY_ICON: {
|
||||
if (std::filesystem::exists(mount_dir)) {
|
||||
g_mount_point.copy(mount_result->mount_point.data, 16);
|
||||
mnt->Mount(mount_dir, mount_result->mount_point.data);
|
||||
mount_result->required_blocks = 0;
|
||||
mount_result->mount_status = 0;
|
||||
return ORBIS_SAVE_DATA_ERROR_EXISTS;
|
||||
}
|
||||
if (std::filesystem::create_directories(mount_dir)) {
|
||||
|
@ -483,7 +527,7 @@ s32 saveDataMount(u32 user_id, char* dir_name, u32 mount_mode,
|
|||
mount_result->mount_status = 1;
|
||||
} break;
|
||||
default:
|
||||
UNREACHABLE();
|
||||
UNREACHABLE_MSG("Unknown mount mode = {}", mount_mode);
|
||||
}
|
||||
mount_result->required_blocks = 0;
|
||||
|
||||
|
@ -583,15 +627,46 @@ int PS4_SYSV_ABI sceSaveDataSetEventInfo() {
|
|||
int PS4_SYSV_ABI sceSaveDataSetParam(const OrbisSaveDataMountPoint* mountPoint,
|
||||
OrbisSaveDataParamType paramType, const void* paramBuf,
|
||||
size_t paramBufSize) {
|
||||
auto* mnt = Common::Singleton<Core::FileSys::MntPoints>::Instance();
|
||||
const auto mount_dir = mnt->GetHostPath(mountPoint->data);
|
||||
LOG_INFO(Lib_SaveData, "called = {}, mountPoint->data = {}", mount_dir.string(),
|
||||
mountPoint->data);
|
||||
if (paramBuf == nullptr)
|
||||
return ORBIS_SAVE_DATA_ERROR_PARAMETER;
|
||||
|
||||
if (paramBuf != nullptr) {
|
||||
Common::FS::IOFile file(mount_dir / "param.txt", Common::FS::FileAccessMode::Write);
|
||||
file.WriteRaw<u8>(paramBuf, paramBufSize);
|
||||
auto* mnt = Common::Singleton<Core::FileSys::MntPoints>::Instance();
|
||||
const auto mount_dir = mnt->GetHostPath(mountPoint->data) / "param.txt";
|
||||
OrbisSaveDataParam params;
|
||||
if (std::filesystem::exists(mount_dir)) {
|
||||
Common::FS::IOFile file(mount_dir, Common::FS::FileAccessMode::Read);
|
||||
file.ReadRaw<u8>(¶ms, sizeof(OrbisSaveDataParam));
|
||||
}
|
||||
|
||||
LOG_INFO(Lib_SaveData, "called");
|
||||
|
||||
switch (paramType) {
|
||||
case ORBIS_SAVE_DATA_PARAM_TYPE_ALL: {
|
||||
memcpy(¶ms, paramBuf, sizeof(OrbisSaveDataParam));
|
||||
} break;
|
||||
case ORBIS_SAVE_DATA_PARAM_TYPE_TITLE: {
|
||||
strncpy(params.title, static_cast<const char*>(paramBuf), paramBufSize);
|
||||
} break;
|
||||
case ORBIS_SAVE_DATA_PARAM_TYPE_SUB_TITLE: {
|
||||
strncpy(params.subTitle, static_cast<const char*>(paramBuf), paramBufSize);
|
||||
} break;
|
||||
case ORBIS_SAVE_DATA_PARAM_TYPE_DETAIL: {
|
||||
strncpy(params.detail, static_cast<const char*>(paramBuf), paramBufSize);
|
||||
} break;
|
||||
case ORBIS_SAVE_DATA_PARAM_TYPE_USER_PARAM: {
|
||||
params.userParam = *(static_cast<const u32*>(paramBuf));
|
||||
} break;
|
||||
case ORBIS_SAVE_DATA_PARAM_TYPE_MTIME: {
|
||||
params.mtime = *(static_cast<const time_t*>(paramBuf));
|
||||
} break;
|
||||
default: {
|
||||
UNREACHABLE_MSG("Unknown Param = {}", paramType);
|
||||
}
|
||||
}
|
||||
|
||||
Common::FS::IOFile file(mount_dir, Common::FS::FileAccessMode::Write);
|
||||
file.WriteRaw<u8>(¶ms, sizeof(OrbisSaveDataParam));
|
||||
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
|
@ -604,11 +679,11 @@ int PS4_SYSV_ABI sceSaveDataSetSaveDataMemory(const u32 userId, const void* buf,
|
|||
const size_t bufSize, const int64_t offset) {
|
||||
LOG_INFO(Lib_SaveData, "called");
|
||||
const auto& mount_dir = Common::FS::GetUserPath(Common::FS::PathType::SaveDataDir) /
|
||||
std::to_string(userId) / game_serial / "save_mem1.sav";
|
||||
std::to_string(userId) / game_serial / "sdmemory/save_mem1.sav";
|
||||
|
||||
Common::FS::IOFile file(mount_dir, Common::FS::FileAccessMode::Write);
|
||||
file.Seek(offset);
|
||||
file.WriteRaw<u8>((void*)buf, bufSize);
|
||||
file.WriteRaw<u8>(buf, bufSize);
|
||||
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
@ -616,13 +691,13 @@ int PS4_SYSV_ABI sceSaveDataSetSaveDataMemory(const u32 userId, const void* buf,
|
|||
int PS4_SYSV_ABI sceSaveDataSetSaveDataMemory2(const OrbisSaveDataMemorySet2* setParam) {
|
||||
LOG_INFO(Lib_SaveData, "called: dataNum = {}, slotId= {}", setParam->dataNum, setParam->slotId);
|
||||
const auto& mount_dir = Common::FS::GetUserPath(Common::FS::PathType::SaveDataDir) /
|
||||
std::to_string(setParam->userId) / game_serial;
|
||||
std::to_string(setParam->userId) / game_serial / "sdmemory";
|
||||
if (setParam->data != nullptr) {
|
||||
Common::FS::IOFile file(mount_dir / "save_mem2.sav", Common::FS::FileAccessMode::Write);
|
||||
if (!file.IsOpen())
|
||||
return -1;
|
||||
file.Seek(setParam->data->offset);
|
||||
file.WriteRaw<u8>((void*)setParam->data->buf, setParam->data->bufSize);
|
||||
file.WriteRaw<u8>(setParam->data->buf, setParam->data->bufSize);
|
||||
}
|
||||
|
||||
if (setParam->param != nullptr) {
|
||||
|
@ -632,7 +707,7 @@ int PS4_SYSV_ABI sceSaveDataSetSaveDataMemory2(const OrbisSaveDataMemorySet2* se
|
|||
|
||||
if (setParam->icon != nullptr) {
|
||||
Common::FS::IOFile file(mount_dir / "save_icon.png", Common::FS::FileAccessMode::Write);
|
||||
file.WriteRaw<u8>((void*)setParam->icon->buf, setParam->icon->bufSize);
|
||||
file.WriteRaw<u8>(setParam->icon->buf, setParam->icon->bufSize);
|
||||
}
|
||||
|
||||
return ORBIS_OK;
|
||||
|
@ -644,7 +719,7 @@ int PS4_SYSV_ABI sceSaveDataSetupSaveDataMemory(u32 userId, size_t memorySize,
|
|||
LOG_INFO(Lib_SaveData, "called:userId = {}, memorySize = {}", userId, memorySize);
|
||||
|
||||
const auto& mount_dir = Common::FS::GetUserPath(Common::FS::PathType::SaveDataDir) /
|
||||
std::to_string(userId) / game_serial;
|
||||
std::to_string(userId) / game_serial / "sdmemory";
|
||||
|
||||
if (std::filesystem::exists(mount_dir)) {
|
||||
return ORBIS_SAVE_DATA_ERROR_EXISTS;
|
||||
|
@ -663,7 +738,7 @@ int PS4_SYSV_ABI sceSaveDataSetupSaveDataMemory2(const OrbisSaveDataMemorySetup2
|
|||
LOG_INFO(Lib_SaveData, "called");
|
||||
// if (setupParam->option == 1) { // check this later.
|
||||
const auto& mount_dir = Common::FS::GetUserPath(Common::FS::PathType::SaveDataDir) /
|
||||
std::to_string(setupParam->userId) / game_serial;
|
||||
std::to_string(setupParam->userId) / game_serial / "sdmemory";
|
||||
if (std::filesystem::exists(mount_dir) &&
|
||||
std::filesystem::exists(mount_dir / "save_mem2.sav")) {
|
||||
Common::FS::IOFile file(mount_dir / "save_mem2.sav", Common::FS::FileAccessMode::Read);
|
||||
|
@ -717,16 +792,17 @@ int PS4_SYSV_ABI sceSaveDataTransferringMount() {
|
|||
}
|
||||
|
||||
s32 PS4_SYSV_ABI sceSaveDataUmount(const OrbisSaveDataMountPoint* mountPoint) {
|
||||
LOG_INFO(Lib_SaveData, "mountPoint = {}", std::string(mountPoint->data));
|
||||
if (std::string(mountPoint->data).empty()) {
|
||||
return ORBIS_SAVE_DATA_ERROR_NOT_MOUNTED;
|
||||
}
|
||||
const auto& mount_dir = Common::FS::GetUserPath(Common::FS::PathType::SaveDataDir) /
|
||||
std::to_string(1) / game_serial / mountPoint->data;
|
||||
auto* mnt = Common::Singleton<Core::FileSys::MntPoints>::Instance();
|
||||
|
||||
const auto& guest_path = mnt->GetHostPath(mountPoint->data);
|
||||
if (guest_path.empty())
|
||||
return ORBIS_SAVE_DATA_ERROR_NOT_MOUNTED;
|
||||
mnt->Unmount(mount_dir, mountPoint->data);
|
||||
LOG_INFO(Lib_SaveData, "mountPoint = {}", std::string(mountPoint->data));
|
||||
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
|
@ -736,23 +812,33 @@ int PS4_SYSV_ABI sceSaveDataUmountSys() {
|
|||
}
|
||||
|
||||
int PS4_SYSV_ABI sceSaveDataUmountWithBackup(const OrbisSaveDataMountPoint* mountPoint) {
|
||||
LOG_ERROR(Lib_SaveData, "called = {}", std::string(mountPoint->data));
|
||||
LOG_INFO(Lib_SaveData, "called mount = {}, is_rw_mode = {}", std::string(mountPoint->data),
|
||||
is_rw_mode);
|
||||
auto* mnt = Common::Singleton<Core::FileSys::MntPoints>::Instance();
|
||||
const auto mount_dir = mnt->GetHostPath(mountPoint->data);
|
||||
if (!std::filesystem::exists(mount_dir)) {
|
||||
return ORBIS_SAVE_DATA_ERROR_NOT_FOUND;
|
||||
}
|
||||
// leave disabled for now. and just unmount.
|
||||
|
||||
std::filesystem::create_directories(mount_dir.parent_path() / "backup");
|
||||
/* if (is_rw_mode) { // backup is done only when mount mode is ReadWrite.
|
||||
auto backup_path = mount_dir;
|
||||
std::string save_data_dir = (mount_dir.string() + "_backup");
|
||||
backup_path.replace_filename(save_data_dir);
|
||||
|
||||
for (const auto& entry : std::filesystem::recursive_directory_iterator(mount_dir)) {
|
||||
const auto& path = entry.path();
|
||||
const auto target_path = mount_dir.parent_path() / "backup";
|
||||
if (std::filesystem::is_regular_file(path)) {
|
||||
std::filesystem::copy(path, target_path,
|
||||
std::filesystem::copy_options::overwrite_existing);
|
||||
std::filesystem::create_directories(backup_path);
|
||||
|
||||
for (const auto& entry : std::filesystem::recursive_directory_iterator(mount_dir)) {
|
||||
const auto& path = entry.path();
|
||||
if (std::filesystem::is_regular_file(path)) {
|
||||
std::filesystem::copy(path, save_data_dir,
|
||||
std::filesystem::copy_options::overwrite_existing);
|
||||
}
|
||||
}
|
||||
}
|
||||
}*/
|
||||
const auto& guest_path = mnt->GetHostPath(mountPoint->data);
|
||||
if (guest_path.empty())
|
||||
return ORBIS_SAVE_DATA_ERROR_NOT_MOUNTED;
|
||||
|
||||
mnt->Unmount(mount_dir, mountPoint->data);
|
||||
return ORBIS_OK;
|
||||
|
|
|
@ -242,6 +242,13 @@ struct OrbisSaveDataMemorySync {
|
|||
u8 reserved[28];
|
||||
};
|
||||
|
||||
constexpr int ORBIS_SAVE_DATA_PARAM_TYPE_ALL = 0;
|
||||
constexpr int ORBIS_SAVE_DATA_PARAM_TYPE_TITLE = 1;
|
||||
constexpr int ORBIS_SAVE_DATA_PARAM_TYPE_SUB_TITLE = 2;
|
||||
constexpr int ORBIS_SAVE_DATA_PARAM_TYPE_DETAIL = 3;
|
||||
constexpr int ORBIS_SAVE_DATA_PARAM_TYPE_USER_PARAM = 4;
|
||||
constexpr int ORBIS_SAVE_DATA_PARAM_TYPE_MTIME = 5;
|
||||
|
||||
int PS4_SYSV_ABI sceSaveDataAbort();
|
||||
int PS4_SYSV_ABI sceSaveDataBackup();
|
||||
int PS4_SYSV_ABI sceSaveDataBindPsnAccount();
|
||||
|
@ -291,7 +298,9 @@ int PS4_SYSV_ABI sceSaveDataGetFormat();
|
|||
int PS4_SYSV_ABI sceSaveDataGetMountedSaveDataCount();
|
||||
int PS4_SYSV_ABI sceSaveDataGetMountInfo(const OrbisSaveDataMountPoint* mountPoint,
|
||||
OrbisSaveDataMountInfo* info);
|
||||
int PS4_SYSV_ABI sceSaveDataGetParam();
|
||||
int PS4_SYSV_ABI sceSaveDataGetParam(const OrbisSaveDataMountPoint* mountPoint,
|
||||
const OrbisSaveDataParamType paramType, void* paramBuf,
|
||||
const size_t paramBufSize, size_t* gotSize);
|
||||
int PS4_SYSV_ABI sceSaveDataGetProgress();
|
||||
int PS4_SYSV_ABI sceSaveDataGetSaveDataCount();
|
||||
int PS4_SYSV_ABI sceSaveDataGetSaveDataMemory(const u32 userId, void* buf, const size_t bufSize,
|
||||
|
|
|
@ -3,14 +3,16 @@
|
|||
|
||||
#include <pthread.h>
|
||||
#include "common/assert.h"
|
||||
#include "common/config.h"
|
||||
#include "common/debug.h"
|
||||
#include "common/thread.h"
|
||||
#include "core/libraries/error_codes.h"
|
||||
#include "core/libraries/kernel/time_management.h"
|
||||
#include "core/libraries/videoout/driver.h"
|
||||
#include "core/platform.h"
|
||||
|
||||
#include "video_core/renderer_vulkan/renderer_vulkan.h"
|
||||
|
||||
extern std::unique_ptr<Vulkan::RendererVulkan> renderer;
|
||||
extern std::unique_ptr<AmdGpu::Liverpool> liverpool;
|
||||
|
||||
namespace Libraries::VideoOut {
|
||||
|
||||
|
@ -41,20 +43,18 @@ VideoOutDriver::VideoOutDriver(u32 width, u32 height) {
|
|||
main_port.resolution.fullHeight = height;
|
||||
main_port.resolution.paneWidth = width;
|
||||
main_port.resolution.paneHeight = height;
|
||||
present_thread = std::jthread([&](std::stop_token token) { PresentThread(token); });
|
||||
}
|
||||
|
||||
VideoOutDriver::~VideoOutDriver() = default;
|
||||
|
||||
int VideoOutDriver::Open(const ServiceThreadParams* params) {
|
||||
std::scoped_lock lock{mutex};
|
||||
|
||||
if (main_port.is_open) {
|
||||
return ORBIS_VIDEO_OUT_ERROR_RESOURCE_BUSY;
|
||||
}
|
||||
|
||||
int handle = 1;
|
||||
main_port.is_open = true;
|
||||
return handle;
|
||||
liverpool->SetVoPort(&main_port);
|
||||
return 1;
|
||||
}
|
||||
|
||||
void VideoOutDriver::Close(s32 handle) {
|
||||
|
@ -158,31 +158,22 @@ int VideoOutDriver::UnregisterBuffers(VideoOutPort* port, s32 attributeIndex) {
|
|||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
void VideoOutDriver::Flip(std::chrono::microseconds timeout) {
|
||||
Request req;
|
||||
{
|
||||
std::unique_lock lock{mutex};
|
||||
submit_cond.wait_for(lock, timeout, [&] { return !requests.empty(); });
|
||||
if (requests.empty()) {
|
||||
renderer->ShowSplash();
|
||||
return;
|
||||
}
|
||||
|
||||
// Retrieve the request.
|
||||
req = requests.front();
|
||||
requests.pop();
|
||||
std::chrono::microseconds VideoOutDriver::Flip(const Request& req) {
|
||||
if (!req) {
|
||||
return std::chrono::microseconds{0};
|
||||
}
|
||||
|
||||
const auto start = std::chrono::high_resolution_clock::now();
|
||||
|
||||
// Whatever the game is rendering show splash if it is active
|
||||
if (!renderer->ShowSplash(req.frame)) {
|
||||
// Present the frame.
|
||||
renderer->Present(req.frame);
|
||||
}
|
||||
|
||||
std::scoped_lock lock{mutex};
|
||||
|
||||
// Update flip status.
|
||||
auto& flip_status = req.port->flip_status;
|
||||
auto* port = req.port;
|
||||
auto& flip_status = port->flip_status;
|
||||
flip_status.count++;
|
||||
flip_status.processTime = Libraries::Kernel::sceKernelGetProcessTime();
|
||||
flip_status.tsc = Libraries::Kernel::sceKernelReadTsc();
|
||||
|
@ -192,7 +183,7 @@ void VideoOutDriver::Flip(std::chrono::microseconds timeout) {
|
|||
flip_status.flipPendingNum = static_cast<int>(requests.size());
|
||||
|
||||
// Trigger flip events for the port.
|
||||
for (auto& event : req.port->flip_events) {
|
||||
for (auto& event : port->flip_events) {
|
||||
if (event != nullptr) {
|
||||
event->TriggerEvent(SCE_VIDEO_OUT_EVENT_FLIP, Kernel::SceKernelEvent::Filter::VideoOut,
|
||||
reinterpret_cast<void*>(req.flip_arg));
|
||||
|
@ -201,21 +192,23 @@ void VideoOutDriver::Flip(std::chrono::microseconds timeout) {
|
|||
|
||||
// Reset flip label
|
||||
if (req.index != -1) {
|
||||
req.port->buffer_labels[req.index] = 0;
|
||||
port->buffer_labels[req.index] = 0;
|
||||
port->SignalVoLabel();
|
||||
}
|
||||
|
||||
const auto end = std::chrono::high_resolution_clock::now();
|
||||
return std::chrono::duration_cast<std::chrono::microseconds>(end - start);
|
||||
}
|
||||
|
||||
bool VideoOutDriver::SubmitFlip(VideoOutPort* port, s32 index, s64 flip_arg,
|
||||
bool is_eop /*= false*/) {
|
||||
std::scoped_lock lock{mutex};
|
||||
|
||||
Vulkan::Frame* frame;
|
||||
if (index == -1) {
|
||||
frame = renderer->PrepareBlankFrame();
|
||||
} else {
|
||||
const auto& buffer = port->buffer_slots[index];
|
||||
const auto& group = port->groups[buffer.group_index];
|
||||
frame = renderer->PrepareFrame(group, buffer.address_left);
|
||||
frame = renderer->PrepareFrame(group, buffer.address_left, is_eop);
|
||||
}
|
||||
|
||||
if (index != -1 && requests.size() >= port->NumRegisteredBuffers()) {
|
||||
|
@ -223,6 +216,7 @@ bool VideoOutDriver::SubmitFlip(VideoOutPort* port, s32 index, s64 flip_arg,
|
|||
return false;
|
||||
}
|
||||
|
||||
std::scoped_lock lock{mutex};
|
||||
requests.push({
|
||||
.frame = frame,
|
||||
.port = port,
|
||||
|
@ -234,24 +228,53 @@ bool VideoOutDriver::SubmitFlip(VideoOutPort* port, s32 index, s64 flip_arg,
|
|||
|
||||
port->flip_status.flipPendingNum = static_cast<int>(requests.size());
|
||||
port->flip_status.gcQueueNum = 0;
|
||||
submit_cond.notify_one();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void VideoOutDriver::Vblank() {
|
||||
std::scoped_lock lock{mutex};
|
||||
void VideoOutDriver::PresentThread(std::stop_token token) {
|
||||
static constexpr std::chrono::milliseconds VblankPeriod{16};
|
||||
Common::SetCurrentThreadName("PresentThread");
|
||||
|
||||
auto& vblank_status = main_port.vblank_status;
|
||||
vblank_status.count++;
|
||||
vblank_status.processTime = Libraries::Kernel::sceKernelGetProcessTime();
|
||||
vblank_status.tsc = Libraries::Kernel::sceKernelReadTsc();
|
||||
const auto receive_request = [this] -> Request {
|
||||
std::scoped_lock lk{mutex};
|
||||
if (!requests.empty()) {
|
||||
const auto request = requests.front();
|
||||
requests.pop();
|
||||
return request;
|
||||
}
|
||||
return {};
|
||||
};
|
||||
|
||||
// Trigger flip events for the port.
|
||||
for (auto& event : main_port.vblank_events) {
|
||||
if (event != nullptr) {
|
||||
event->TriggerEvent(SCE_VIDEO_OUT_EVENT_VBLANK,
|
||||
Kernel::SceKernelEvent::Filter::VideoOut, nullptr);
|
||||
auto vblank_period = VblankPeriod / Config::vblankDiv();
|
||||
auto delay = std::chrono::microseconds{0};
|
||||
while (!token.stop_requested()) {
|
||||
// Sleep for most of the vblank duration.
|
||||
std::this_thread::sleep_for(vblank_period - delay);
|
||||
|
||||
// Check if it's time to take a request.
|
||||
auto& vblank_status = main_port.vblank_status;
|
||||
if (vblank_status.count % (main_port.flip_rate + 1) == 0) {
|
||||
const auto request = receive_request();
|
||||
delay = Flip(request);
|
||||
FRAME_END;
|
||||
}
|
||||
|
||||
{
|
||||
// Needs lock here as can be concurrently read by `sceVideoOutGetVblankStatus`
|
||||
std::unique_lock lock{main_port.vo_mutex};
|
||||
vblank_status.count++;
|
||||
vblank_status.processTime = Libraries::Kernel::sceKernelGetProcessTime();
|
||||
vblank_status.tsc = Libraries::Kernel::sceKernelReadTsc();
|
||||
main_port.vblank_cv.notify_all();
|
||||
}
|
||||
|
||||
// Trigger flip events for the port.
|
||||
for (auto& event : main_port.vblank_events) {
|
||||
if (event != nullptr) {
|
||||
event->TriggerEvent(SCE_VIDEO_OUT_EVENT_VBLANK,
|
||||
Kernel::SceKernelEvent::Filter::VideoOut, nullptr);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,10 +3,13 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include "common/debug.h"
|
||||
#include "common/polyfill_thread.h"
|
||||
#include "core/libraries/videoout/video_out.h"
|
||||
|
||||
#include <condition_variable>
|
||||
#include <mutex>
|
||||
#include <queue>
|
||||
#include "core/libraries/videoout/video_out.h"
|
||||
|
||||
namespace Vulkan {
|
||||
struct Frame;
|
||||
|
@ -25,6 +28,9 @@ struct VideoOutPort {
|
|||
SceVideoOutVblankStatus vblank_status;
|
||||
std::vector<Kernel::SceKernelEqueue> flip_events;
|
||||
std::vector<Kernel::SceKernelEqueue> vblank_events;
|
||||
std::mutex vo_mutex;
|
||||
std::condition_variable vo_cv;
|
||||
std::condition_variable vblank_cv;
|
||||
int flip_rate = 0;
|
||||
|
||||
s32 FindFreeGroup() const {
|
||||
|
@ -35,6 +41,22 @@ struct VideoOutPort {
|
|||
return index;
|
||||
}
|
||||
|
||||
bool IsVoLabel(const u64* address) const {
|
||||
const u64* start = &buffer_labels[0];
|
||||
const u64* end = &buffer_labels[MaxDisplayBuffers - 1];
|
||||
return address >= start && address <= end;
|
||||
}
|
||||
|
||||
void WaitVoLabel(auto&& pred) {
|
||||
std::unique_lock lk{vo_mutex};
|
||||
vo_cv.wait(lk, pred);
|
||||
}
|
||||
|
||||
void SignalVoLabel() {
|
||||
std::scoped_lock lk{vo_mutex};
|
||||
vo_cv.notify_one();
|
||||
}
|
||||
|
||||
[[nodiscard]] int NumRegisteredBuffers() const {
|
||||
return std::count_if(buffer_slots.cbegin(), buffer_slots.cend(),
|
||||
[](auto& buffer) { return buffer.group_index != -1; });
|
||||
|
@ -63,11 +85,8 @@ public:
|
|||
const BufferAttribute* attribute);
|
||||
int UnregisterBuffers(VideoOutPort* port, s32 attributeIndex);
|
||||
|
||||
void Flip(std::chrono::microseconds timeout);
|
||||
bool SubmitFlip(VideoOutPort* port, s32 index, s64 flip_arg, bool is_eop = false);
|
||||
|
||||
void Vblank();
|
||||
|
||||
private:
|
||||
struct Request {
|
||||
Vulkan::Frame* frame;
|
||||
|
@ -76,14 +95,19 @@ private:
|
|||
s64 flip_arg;
|
||||
u64 submit_tsc;
|
||||
bool eop;
|
||||
|
||||
operator bool() const noexcept {
|
||||
return frame != nullptr;
|
||||
}
|
||||
};
|
||||
|
||||
std::chrono::microseconds Flip(const Request& req);
|
||||
void PresentThread(std::stop_token token);
|
||||
|
||||
std::mutex mutex;
|
||||
VideoOutPort main_port{};
|
||||
std::condition_variable_any submit_cond;
|
||||
std::condition_variable done_cond;
|
||||
std::jthread present_thread;
|
||||
std::queue<Request> requests;
|
||||
bool is_neo{};
|
||||
};
|
||||
|
||||
} // namespace Libraries::VideoOut
|
||||
|
|
|
@ -183,6 +183,7 @@ s32 PS4_SYSV_ABI sceVideoOutGetVblankStatus(int handle, SceVideoOutVblankStatus*
|
|||
return ORBIS_VIDEO_OUT_ERROR_INVALID_HANDLE;
|
||||
}
|
||||
|
||||
std::unique_lock lock{port->vo_mutex};
|
||||
*status = port->vblank_status;
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
@ -229,14 +230,6 @@ s32 PS4_SYSV_ABI sceVideoOutUnregisterBuffers(s32 handle, s32 attributeIndex) {
|
|||
return driver->UnregisterBuffers(port, attributeIndex);
|
||||
}
|
||||
|
||||
void Flip(std::chrono::microseconds micros) {
|
||||
return driver->Flip(micros);
|
||||
}
|
||||
|
||||
void Vblank() {
|
||||
return driver->Vblank();
|
||||
}
|
||||
|
||||
void sceVideoOutGetBufferLabelAddress(s32 handle, uintptr_t* label_addr) {
|
||||
auto* port = driver->GetPort(handle);
|
||||
ASSERT(port);
|
||||
|
@ -266,6 +259,18 @@ s32 PS4_SYSV_ABI sceVideoOutGetDeviceCapabilityInfo(
|
|||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI sceVideoOutWaitVblank(s32 handle) {
|
||||
auto* port = driver->GetPort(handle);
|
||||
if (!port) {
|
||||
return ORBIS_VIDEO_OUT_ERROR_INVALID_HANDLE;
|
||||
}
|
||||
|
||||
std::unique_lock lock{port->vo_mutex};
|
||||
const auto prev_counter = port->vblank_status.count;
|
||||
port->vblank_cv.wait(lock, [&]() { return prev_counter != port->vblank_status.count; });
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
void RegisterLib(Core::Loader::SymbolsResolver* sym) {
|
||||
driver = std::make_unique<VideoOutDriver>(Config::getScreenWidth(), Config::getScreenHeight());
|
||||
|
||||
|
@ -294,6 +299,7 @@ void RegisterLib(Core::Loader::SymbolsResolver* sym) {
|
|||
sceVideoOutGetVblankStatus);
|
||||
LIB_FUNCTION("kGVLc3htQE8", "libSceVideoOut", 1, "libSceVideoOut", 0, 0,
|
||||
sceVideoOutGetDeviceCapabilityInfo);
|
||||
LIB_FUNCTION("j6RaAUlaLv0", "libSceVideoOut", 1, "libSceVideoOut", 0, 0, sceVideoOutWaitVblank);
|
||||
|
||||
// openOrbis appears to have libSceVideoOut_v1 module libSceVideoOut_v1.1
|
||||
LIB_FUNCTION("Up36PTk687E", "libSceVideoOut", 1, "libSceVideoOut", 1, 1, sceVideoOutOpen);
|
||||
|
|
|
@ -92,11 +92,12 @@ void PS4_SYSV_ABI sceVideoOutSetBufferAttribute(BufferAttribute* attribute, Pixe
|
|||
u32 tilingMode, u32 aspectRatio, u32 width,
|
||||
u32 height, u32 pitchInPixel);
|
||||
s32 PS4_SYSV_ABI sceVideoOutAddFlipEvent(Kernel::SceKernelEqueue eq, s32 handle, void* udata);
|
||||
s32 PS4_SYSV_ABI sceVideoOutAddVBlankEvent(Kernel::SceKernelEqueue eq, s32 handle, void* udata);
|
||||
s32 PS4_SYSV_ABI sceVideoOutAddVblankEvent(Kernel::SceKernelEqueue eq, s32 handle, void* udata);
|
||||
s32 PS4_SYSV_ABI sceVideoOutRegisterBuffers(s32 handle, s32 startIndex, void* const* addresses,
|
||||
s32 bufferNum, const BufferAttribute* attribute);
|
||||
s32 PS4_SYSV_ABI sceVideoOutSetFlipRate(s32 handle, s32 rate);
|
||||
s32 PS4_SYSV_ABI sceVideoOutIsFlipPending(s32 handle);
|
||||
s32 PS4_SYSV_ABI sceVideoOutWaitVblank(s32 handle);
|
||||
s32 PS4_SYSV_ABI sceVideoOutSubmitFlip(s32 handle, s32 bufferIndex, s32 flipMode, s64 flipArg);
|
||||
s32 PS4_SYSV_ABI sceVideoOutGetFlipStatus(s32 handle, FlipStatus* status);
|
||||
s32 PS4_SYSV_ABI sceVideoOutGetResolutionStatus(s32 handle, SceVideoOutResolutionStatus* status);
|
||||
|
@ -104,9 +105,6 @@ s32 PS4_SYSV_ABI sceVideoOutOpen(SceUserServiceUserId userId, s32 busType, s32 i
|
|||
const void* param);
|
||||
s32 PS4_SYSV_ABI sceVideoOutClose(s32 handle);
|
||||
|
||||
void Flip(std::chrono::microseconds micros);
|
||||
void Vblank();
|
||||
|
||||
// Internal system functions
|
||||
void sceVideoOutGetBufferLabelAddress(s32 handle, uintptr_t* label_addr);
|
||||
s32 sceVideoOutSubmitEopFlip(s32 handle, u32 buf_id, u32 mode, u32 arg, void** unk);
|
||||
|
|
|
@ -193,7 +193,7 @@ int MemoryManager::MapFile(void** out_addr, VAddr virtual_addr, size_t size, Mem
|
|||
|
||||
// Find first free area to map the file.
|
||||
if (False(flags & MemoryMapFlags::Fixed)) {
|
||||
mapped_addr = SearchFree(mapped_addr, size_aligned);
|
||||
mapped_addr = SearchFree(mapped_addr, size_aligned, 1);
|
||||
}
|
||||
|
||||
if (True(flags & MemoryMapFlags::Fixed)) {
|
||||
|
@ -240,6 +240,7 @@ void MemoryManager::UnmapMemory(VAddr virtual_addr, size_t size) {
|
|||
vma.prot = MemoryProt::NoAccess;
|
||||
vma.phys_base = 0;
|
||||
vma.disallow_merge = false;
|
||||
vma.name = "";
|
||||
MergeAdjacent(vma_map, new_it);
|
||||
|
||||
// Unmap the memory region.
|
||||
|
@ -334,6 +335,7 @@ int MemoryManager::VirtualQuery(VAddr addr, int flags,
|
|||
info->is_flexible.Assign(vma.type == VMAType::Flexible);
|
||||
info->is_direct.Assign(vma.type == VMAType::Direct);
|
||||
info->is_commited.Assign(vma.type != VMAType::Free);
|
||||
vma.name.copy(info->name.data(), std::min(info->name.size(), vma.name.size()));
|
||||
if (vma.type == VMAType::Direct) {
|
||||
const auto dmem_it = FindDmemArea(vma.phys_base);
|
||||
ASSERT(dmem_it != dmem_map.end());
|
||||
|
@ -392,8 +394,23 @@ std::pair<vk::Buffer, size_t> MemoryManager::GetVulkanBuffer(VAddr addr) {
|
|||
return std::make_pair(*it->second.buffer, addr - it->first);
|
||||
}
|
||||
|
||||
VAddr MemoryManager::SearchFree(VAddr virtual_addr, size_t size, u32 alignment) {
|
||||
void MemoryManager::NameVirtualRange(VAddr virtual_addr, size_t size, std::string_view name) {
|
||||
auto it = FindVMA(virtual_addr);
|
||||
|
||||
ASSERT_MSG(it->second.Contains(virtual_addr, size),
|
||||
"Range provided is not fully containted in vma");
|
||||
it->second.name = name;
|
||||
}
|
||||
VAddr MemoryManager::SearchFree(VAddr virtual_addr, size_t size, u32 alignment) {
|
||||
// If the requested address is below the mapped range, start search from the lowest address
|
||||
auto min_search_address = impl.SystemManagedVirtualBase();
|
||||
if (virtual_addr < min_search_address) {
|
||||
virtual_addr = min_search_address;
|
||||
}
|
||||
|
||||
auto it = FindVMA(virtual_addr);
|
||||
ASSERT_MSG(it != vma_map.end(), "Specified mapping address was not found!");
|
||||
|
||||
// If the VMA is free and contains the requested mapping we are done.
|
||||
if (it->second.IsFree() && it->second.Contains(virtual_addr, size)) {
|
||||
return virtual_addr;
|
||||
|
|
|
@ -179,6 +179,8 @@ public:
|
|||
int GetDirectMemoryType(PAddr addr, int* directMemoryTypeOut, void** directMemoryStartOut,
|
||||
void** directMemoryEndOut);
|
||||
|
||||
void NameVirtualRange(VAddr virtual_addr, size_t size, std::string_view name);
|
||||
|
||||
private:
|
||||
VMAHandle FindVMA(VAddr target) {
|
||||
return std::prev(vma_map.upper_bound(target));
|
||||
|
|
|
@ -1,29 +1,33 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <common/logging/log.h>
|
||||
#include <core/file_format/psf.h>
|
||||
#include <core/file_format/splash.h>
|
||||
#include <core/libraries/disc_map/disc_map.h>
|
||||
#include <core/libraries/libc/libc.h>
|
||||
#include <core/libraries/libc_internal/libc_internal.h>
|
||||
#include <core/libraries/rtc/rtc.h>
|
||||
#include <core/libraries/videoout/video_out.h>
|
||||
#include <fmt/core.h>
|
||||
|
||||
#include "common/config.h"
|
||||
#include "common/debug.h"
|
||||
#include "common/logging/backend.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/ntapi.h"
|
||||
#include "common/path_util.h"
|
||||
#include "common/polyfill_thread.h"
|
||||
#include "common/singleton.h"
|
||||
#include "common/version.h"
|
||||
#include "core/file_format/playgo_chunk.h"
|
||||
#include "core/file_format/psf.h"
|
||||
#include "core/file_format/splash.h"
|
||||
#include "core/file_sys/fs.h"
|
||||
#include "core/libraries/disc_map/disc_map.h"
|
||||
#include "core/libraries/kernel/thread_management.h"
|
||||
#include "core/libraries/libc/libc.h"
|
||||
#include "core/libraries/libc_internal/libc_internal.h"
|
||||
#include "core/libraries/libs.h"
|
||||
#include "core/libraries/rtc/rtc.h"
|
||||
#include "core/libraries/videoout/video_out.h"
|
||||
#include "core/linker.h"
|
||||
#include "core/memory.h"
|
||||
#include "emulator.h"
|
||||
#include "src/common/scm_rev.h"
|
||||
#include "video_core/renderdoc.h"
|
||||
|
||||
Frontend::WindowSDL* g_window = nullptr;
|
||||
|
||||
|
@ -46,14 +50,17 @@ Emulator::Emulator() {
|
|||
Common::Log::Initialize();
|
||||
Common::Log::Start();
|
||||
LOG_INFO(Loader, "Starting shadps4 emulator v{} ", Common::VERSION);
|
||||
LOG_INFO(Loader, "Revision {}", Common::g_scm_rev);
|
||||
LOG_INFO(Loader, "Branch {}", Common::g_scm_branch);
|
||||
LOG_INFO(Loader, "Description {}", Common::g_scm_desc);
|
||||
|
||||
// Defer until after logging is initialized.
|
||||
memory = Core::Memory::Instance();
|
||||
controller = Common::Singleton<Input::GameController>::Instance();
|
||||
linker = Common::Singleton<Core::Linker>::Instance();
|
||||
window = std::make_unique<Frontend::WindowSDL>(WindowWidth, WindowHeight, controller);
|
||||
|
||||
g_window = window.get();
|
||||
// Load renderdoc module.
|
||||
VideoCore::LoadRenderDoc();
|
||||
}
|
||||
|
||||
Emulator::~Emulator() {
|
||||
|
@ -68,6 +75,8 @@ void Emulator::Run(const std::filesystem::path& file) {
|
|||
|
||||
// Loading param.sfo file if exists
|
||||
std::string id;
|
||||
std::string title;
|
||||
std::string app_version;
|
||||
std::filesystem::path sce_sys_folder = file.parent_path() / "sce_sys";
|
||||
if (std::filesystem::is_directory(sce_sys_folder)) {
|
||||
for (const auto& entry : std::filesystem::directory_iterator(sce_sys_folder)) {
|
||||
|
@ -75,11 +84,14 @@ void Emulator::Run(const std::filesystem::path& file) {
|
|||
auto* param_sfo = Common::Singleton<PSF>::Instance();
|
||||
param_sfo->open(sce_sys_folder.string() + "/param.sfo", {});
|
||||
id = std::string(param_sfo->GetString("CONTENT_ID"), 7, 9);
|
||||
std::string title(param_sfo->GetString("TITLE"));
|
||||
title = param_sfo->GetString("TITLE");
|
||||
LOG_INFO(Loader, "Game id: {} Title: {}", id, title);
|
||||
u32 fw_version = param_sfo->GetInteger("SYSTEM_VER");
|
||||
std::string app_version = param_sfo->GetString("APP_VER");
|
||||
app_version = param_sfo->GetString("APP_VER");
|
||||
LOG_INFO(Loader, "Fw: {:#x} App Version: {}", fw_version, app_version);
|
||||
} else if (entry.path().filename() == "playgo-chunk.dat") {
|
||||
auto* playgo = Common::Singleton<PlaygoChunk>::Instance();
|
||||
playgo->Open(sce_sys_folder.string() + "/playgo-chunk.dat");
|
||||
} else if (entry.path().filename() == "pic0.png" ||
|
||||
entry.path().filename() == "pic1.png") {
|
||||
auto* splash = Common::Singleton<Splash>::Instance();
|
||||
|
@ -93,6 +105,19 @@ void Emulator::Run(const std::filesystem::path& file) {
|
|||
}
|
||||
}
|
||||
|
||||
std::string game_title = fmt::format("{} - {} <{}>", id, title, app_version);
|
||||
std::string window_title = "";
|
||||
if (Common::isRelease) {
|
||||
window_title = fmt::format("shadPS4 v{} | {}", Common::VERSION, game_title);
|
||||
} else {
|
||||
window_title =
|
||||
fmt::format("shadPS4 v{} {} | {}", Common::VERSION, Common::g_scm_desc, game_title);
|
||||
}
|
||||
window =
|
||||
std::make_unique<Frontend::WindowSDL>(WindowWidth, WindowHeight, controller, window_title);
|
||||
|
||||
g_window = window.get();
|
||||
|
||||
const auto& mount_data_dir = Common::FS::GetUserPath(Common::FS::PathType::GameDataDir) / id;
|
||||
if (!std::filesystem::exists(mount_data_dir)) {
|
||||
std::filesystem::create_directory(mount_data_dir);
|
||||
|
@ -104,6 +129,19 @@ void Emulator::Run(const std::filesystem::path& file) {
|
|||
}
|
||||
mnt->Mount(mount_temp_dir, "/temp0"); // called in app_content ==> stat/mkdir
|
||||
|
||||
const auto& mount_download_dir =
|
||||
Common::FS::GetUserPath(Common::FS::PathType::DownloadDir) / id;
|
||||
if (!std::filesystem::exists(mount_download_dir)) {
|
||||
std::filesystem::create_directory(mount_download_dir);
|
||||
}
|
||||
mnt->Mount(mount_download_dir, "/download0");
|
||||
|
||||
const auto& mount_captures_dir = Common::FS::GetUserPath(Common::FS::PathType::CapturesDir);
|
||||
if (!std::filesystem::exists(mount_captures_dir)) {
|
||||
std::filesystem::create_directory(mount_captures_dir);
|
||||
}
|
||||
VideoCore::SetOutputDir(mount_captures_dir.generic_string(), id);
|
||||
|
||||
// Initialize kernel and library facilities.
|
||||
Libraries::Kernel::init_pthreads();
|
||||
Libraries::InitHLELibs(&linker->GetHLESymbols());
|
||||
|
@ -136,14 +174,8 @@ void Emulator::Run(const std::filesystem::path& file) {
|
|||
std::jthread mainthread =
|
||||
std::jthread([this](std::stop_token stop_token) { linker->Execute(); });
|
||||
|
||||
// Begin main window loop until the application exits
|
||||
static constexpr std::chrono::milliseconds FlipPeriod{16};
|
||||
|
||||
while (window->isOpen()) {
|
||||
window->waitEvent();
|
||||
Libraries::VideoOut::Flip(FlipPeriod);
|
||||
Libraries::VideoOut::Vblank();
|
||||
FRAME_END;
|
||||
}
|
||||
|
||||
std::exit(0);
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
#include <filesystem>
|
||||
#include <thread>
|
||||
|
||||
#include <common/singleton.h>
|
||||
#include "common/singleton.h"
|
||||
#include "core/linker.h"
|
||||
#include "input/controller.h"
|
||||
#include "sdl_window.h"
|
||||
|
|
BIN
src/images/shadPS4.icns
Normal file
BIN
src/images/shadPS4.icns
Normal file
Binary file not shown.
Binary file not shown.
Before Width: | Height: | Size: 264 KiB After Width: | Height: | Size: 73 KiB |
|
@ -4,6 +4,7 @@
|
|||
#include "core/libraries/kernel/time_management.h"
|
||||
#include "core/libraries/pad/pad.h"
|
||||
#include "input/controller.h"
|
||||
|
||||
namespace Input {
|
||||
|
||||
GameController::GameController() {
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <emulator.h>
|
||||
#include <fmt/core.h>
|
||||
#include "emulator.h"
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
if (argc == 1) {
|
||||
|
|
|
@ -2,7 +2,9 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <QHeaderView>
|
||||
|
||||
#include "elf_viewer.h"
|
||||
|
||||
ElfViewer::ElfViewer(QWidget* parent) : QTableWidget(parent) {
|
||||
dir_list_std = Config::getElfViewer();
|
||||
for (const auto& str : dir_list_std) {
|
||||
|
|
|
@ -14,8 +14,9 @@
|
|||
#include <QTreeView>
|
||||
#include <QVBoxLayout>
|
||||
#include <QWidget>
|
||||
|
||||
#include "core/loader/elf.h"
|
||||
#include "game_list_frame.h"
|
||||
#include "src/core/loader/elf.h"
|
||||
|
||||
class ElfViewer : public QTableWidget {
|
||||
Q_OBJECT
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
#include <QVBoxLayout>
|
||||
#include <QWidget>
|
||||
#include <QtConcurrent/QtConcurrent>
|
||||
|
||||
#include "common/config.h"
|
||||
#include "game_info.h"
|
||||
#include "game_list_utils.h"
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
#include <thread>
|
||||
#include <QProgressDialog>
|
||||
#include <QtConcurrent/QtConcurrent>
|
||||
|
||||
#include "game_info.h"
|
||||
|
||||
GameInfoClass::GameInfoClass() = default;
|
||||
|
@ -42,4 +43,4 @@ void GameInfoClass::GetGameInfo(QWidget* parent) {
|
|||
&QProgressDialog::setValue);
|
||||
|
||||
dialog.exec();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
#include <QObject>
|
||||
#include <QPixmap>
|
||||
#include <QtConcurrent/QtConcurrent>
|
||||
|
||||
#include "common/config.h"
|
||||
#include "core/file_format/psf.h"
|
||||
#include "game_list_utils.h"
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "game_install_dialog.h"
|
||||
|
||||
#include <QDialogButtonBox>
|
||||
#include <QDir>
|
||||
#include <QFileDialog>
|
||||
|
@ -14,6 +12,8 @@
|
|||
#include <QPushButton>
|
||||
#include <QVBoxLayout>
|
||||
|
||||
#include "game_install_dialog.h"
|
||||
|
||||
GameInstallDialog::GameInstallDialog() : m_gamesDirectory(nullptr) {
|
||||
auto layout = new QVBoxLayout(this);
|
||||
|
||||
|
@ -21,8 +21,8 @@ GameInstallDialog::GameInstallDialog() : m_gamesDirectory(nullptr) {
|
|||
layout->addStretch();
|
||||
layout->addWidget(SetupDialogActions());
|
||||
|
||||
setWindowTitle("Shadps4 - Choose directory");
|
||||
setWindowIcon(QIcon(":/images/shadps4.ico"));
|
||||
setWindowTitle("shadPS4 - Choose directory");
|
||||
setWindowIcon(QIcon(":images/shadps4.ico"));
|
||||
}
|
||||
|
||||
GameInstallDialog::~GameInstallDialog() {}
|
||||
|
@ -47,7 +47,7 @@ QWidget* GameInstallDialog::SetupGamesDirectory() {
|
|||
layout->addWidget(m_gamesDirectory);
|
||||
|
||||
// Browse button.
|
||||
auto browse = new QPushButton("...");
|
||||
auto browse = new QPushButton("Browse");
|
||||
|
||||
connect(browse, &QPushButton::clicked, this, &GameInstallDialog::Browse);
|
||||
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
#pragma once
|
||||
|
||||
#include <QDialog>
|
||||
|
||||
#include "common/config.h"
|
||||
#include "common/path_util.h"
|
||||
|
||||
|
|
|
@ -190,17 +190,17 @@ void GameListFrame::SetRegionFlag(int row, int column, QString itemStr) {
|
|||
QTableWidgetItem* item = new QTableWidgetItem();
|
||||
QImage scaledPixmap;
|
||||
if (itemStr == "Japan") {
|
||||
scaledPixmap = QImage(":/images/flag_jp.png");
|
||||
scaledPixmap = QImage(":images/flag_jp.png");
|
||||
} else if (itemStr == "Europe") {
|
||||
scaledPixmap = QImage(":/images/flag_eu.png");
|
||||
scaledPixmap = QImage(":images/flag_eu.png");
|
||||
} else if (itemStr == "USA") {
|
||||
scaledPixmap = QImage(":/images/flag_us.png");
|
||||
scaledPixmap = QImage(":images/flag_us.png");
|
||||
} else if (itemStr == "Asia") {
|
||||
scaledPixmap = QImage(":/images/flag_china.png");
|
||||
scaledPixmap = QImage(":images/flag_china.png");
|
||||
} else if (itemStr == "World") {
|
||||
scaledPixmap = QImage(":/images/flag_world.png");
|
||||
scaledPixmap = QImage(":images/flag_world.png");
|
||||
} else {
|
||||
scaledPixmap = QImage(":/images/flag_unk.png");
|
||||
scaledPixmap = QImage(":images/flag_unk.png");
|
||||
}
|
||||
QWidget* widget = new QWidget(this);
|
||||
QVBoxLayout* layout = new QVBoxLayout(widget);
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
#include <QVBoxLayout>
|
||||
#include <QWidget>
|
||||
#include <QtConcurrent/QtConcurrent>
|
||||
|
||||
#include "game_info.h"
|
||||
#include "game_list_utils.h"
|
||||
#include "gui_context_menus.h"
|
||||
|
|
|
@ -165,12 +165,12 @@ public:
|
|||
if (createShortcutLinux(linkPath, ebootPath, iconPath)) {
|
||||
#endif
|
||||
QMessageBox::information(
|
||||
nullptr, "Shortcut Creation",
|
||||
QString("Shortcut created successfully:\n %1").arg(linkPath));
|
||||
nullptr, "Shortcut creation",
|
||||
QString("Shortcut created successfully!\n %1").arg(linkPath));
|
||||
} else {
|
||||
QMessageBox::critical(
|
||||
nullptr, "Error",
|
||||
QString("Error creating shortcut:\n %1").arg(linkPath));
|
||||
QString("Error creating shortcut!\n %1").arg(linkPath));
|
||||
}
|
||||
} else {
|
||||
QMessageBox::critical(nullptr, "Error", "Failed to convert icon.");
|
||||
|
@ -183,11 +183,11 @@ public:
|
|||
if (createShortcutLinux(linkPath, ebootPath, iconPath)) {
|
||||
#endif
|
||||
QMessageBox::information(
|
||||
nullptr, "Shortcut Creation",
|
||||
QString("Shortcut created successfully:\n %1").arg(linkPath));
|
||||
nullptr, "Shortcut creation",
|
||||
QString("Shortcut created successfully!\n %1").arg(linkPath));
|
||||
} else {
|
||||
QMessageBox::critical(nullptr, "Error",
|
||||
QString("Error creating shortcut:\n %1").arg(linkPath));
|
||||
QString("Error creating shortcut!\n %1").arg(linkPath));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -308,7 +308,7 @@ private:
|
|||
QFile shortcutFile(linkPath);
|
||||
if (!shortcutFile.open(QIODevice::WriteOnly | QIODevice::Text)) {
|
||||
QMessageBox::critical(nullptr, "Error",
|
||||
QString("Error creating shortcut:\n %1").arg(linkPath));
|
||||
QString("Error creating shortcut!\n %1").arg(linkPath));
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
@ -2,15 +2,14 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <QtWidgets/QApplication>
|
||||
#include <fmt/core.h>
|
||||
|
||||
#include "common/config.h"
|
||||
#include "core/file_sys/fs.h"
|
||||
#include "emulator.h"
|
||||
#include "qt_gui/game_install_dialog.h"
|
||||
#include "qt_gui/main_window.h"
|
||||
|
||||
#include <emulator.h>
|
||||
#include <fmt/core.h>
|
||||
|
||||
// Custom message handler to ignore Qt logs
|
||||
void customMessageHandler(QtMsgType, const QMessageLogContext&, const QString&) {}
|
||||
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
#include <QProgressDialog>
|
||||
#include <QStatusBar>
|
||||
#include <QtConcurrent>
|
||||
|
||||
#include "common/io_file.h"
|
||||
#include "common/version.h"
|
||||
#include "core/file_format/pkg.h"
|
||||
|
|
|
@ -9,13 +9,14 @@
|
|||
#include <QMainWindow>
|
||||
#include <QMimeData>
|
||||
#include <QScopedPointer>
|
||||
#include <emulator.h>
|
||||
#include <fmt/core.h>
|
||||
|
||||
#include "common/config.h"
|
||||
#include "common/path_util.h"
|
||||
#include "core/file_format/psf.h"
|
||||
#include "core/file_sys/fs.h"
|
||||
#include "elf_viewer.h"
|
||||
#include "emulator.h"
|
||||
#include "game_grid_frame.h"
|
||||
#include "game_info.h"
|
||||
#include "game_list_frame.h"
|
||||
|
|
|
@ -7,25 +7,6 @@ void WindowThemes::SetWindowTheme(Theme theme, QLineEdit* mw_searchbar) {
|
|||
QPalette themePalette;
|
||||
|
||||
switch (theme) {
|
||||
case Theme::Light:
|
||||
mw_searchbar->setStyleSheet("background-color: #ffffff; /* Light gray background */"
|
||||
"color: #000000; /* Black text */"
|
||||
"padding: 5px;");
|
||||
themePalette.setColor(QPalette::Window, QColor(240, 240, 240)); // Light gray
|
||||
themePalette.setColor(QPalette::WindowText, Qt::black); // Black
|
||||
themePalette.setColor(QPalette::Base, QColor(230, 230, 230, 80)); // Grayish
|
||||
themePalette.setColor(QPalette::ToolTipBase, Qt::black); // Black
|
||||
themePalette.setColor(QPalette::ToolTipText, Qt::black); // Black
|
||||
themePalette.setColor(QPalette::Text, Qt::black); // Black
|
||||
themePalette.setColor(QPalette::Button, QColor(240, 240, 240)); // Light gray
|
||||
themePalette.setColor(QPalette::ButtonText, Qt::black); // Black
|
||||
themePalette.setColor(QPalette::BrightText, Qt::red); // Red
|
||||
themePalette.setColor(QPalette::Link, QColor(42, 130, 218)); // Blue
|
||||
themePalette.setColor(QPalette::Highlight, QColor(42, 130, 218)); // Blue
|
||||
themePalette.setColor(QPalette::HighlightedText, Qt::white); // White
|
||||
qApp->setPalette(themePalette);
|
||||
break;
|
||||
|
||||
case Theme::Dark:
|
||||
mw_searchbar->setStyleSheet("background-color: #1e1e1e; /* Dark background */"
|
||||
"color: #ffffff; /* White text */"
|
||||
|
@ -48,6 +29,25 @@ void WindowThemes::SetWindowTheme(Theme theme, QLineEdit* mw_searchbar) {
|
|||
qApp->setPalette(themePalette);
|
||||
break;
|
||||
|
||||
case Theme::Light:
|
||||
mw_searchbar->setStyleSheet("background-color: #ffffff; /* Light gray background */"
|
||||
"color: #000000; /* Black text */"
|
||||
"padding: 5px;");
|
||||
themePalette.setColor(QPalette::Window, QColor(240, 240, 240)); // Light gray
|
||||
themePalette.setColor(QPalette::WindowText, Qt::black); // Black
|
||||
themePalette.setColor(QPalette::Base, QColor(230, 230, 230, 80)); // Grayish
|
||||
themePalette.setColor(QPalette::ToolTipBase, Qt::black); // Black
|
||||
themePalette.setColor(QPalette::ToolTipText, Qt::black); // Black
|
||||
themePalette.setColor(QPalette::Text, Qt::black); // Black
|
||||
themePalette.setColor(QPalette::Button, QColor(240, 240, 240)); // Light gray
|
||||
themePalette.setColor(QPalette::ButtonText, Qt::black); // Black
|
||||
themePalette.setColor(QPalette::BrightText, Qt::red); // Red
|
||||
themePalette.setColor(QPalette::Link, QColor(42, 130, 218)); // Blue
|
||||
themePalette.setColor(QPalette::Highlight, QColor(42, 130, 218)); // Blue
|
||||
themePalette.setColor(QPalette::HighlightedText, Qt::white); // White
|
||||
qApp->setPalette(themePalette);
|
||||
break;
|
||||
|
||||
case Theme::Green:
|
||||
mw_searchbar->setStyleSheet("background-color: #354535; /* Dark green background */"
|
||||
"color: #ffffff; /* White text */"
|
||||
|
|
|
@ -2,13 +2,14 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QApplication>
|
||||
#include <QLineEdit>
|
||||
#include <QWidget>
|
||||
|
||||
enum class Theme : int {
|
||||
Light,
|
||||
Dark,
|
||||
Light,
|
||||
Green,
|
||||
Blue,
|
||||
Violet,
|
||||
|
|
|
@ -44,8 +44,8 @@ public:
|
|||
QAction* gameInstallPathAct;
|
||||
QAction* dumpGameListAct;
|
||||
QAction* pkgViewerAct;
|
||||
QAction* setThemeLight;
|
||||
QAction* setThemeDark;
|
||||
QAction* setThemeLight;
|
||||
QAction* setThemeGreen;
|
||||
QAction* setThemeBlue;
|
||||
QAction* setThemeViolet;
|
||||
|
@ -76,7 +76,7 @@ public:
|
|||
MainWindow->setObjectName("MainWindow");
|
||||
// MainWindow->resize(1280, 720);
|
||||
QIcon icon;
|
||||
icon.addFile(QString::fromUtf8(":/images/shadps4.ico"), QSize(), QIcon::Normal, QIcon::Off);
|
||||
icon.addFile(QString::fromUtf8(":images/shadps4.ico"), QSize(), QIcon::Normal, QIcon::Off);
|
||||
MainWindow->setWindowIcon(icon);
|
||||
QSizePolicy sizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
|
||||
sizePolicy.setHorizontalStretch(0);
|
||||
|
@ -136,13 +136,13 @@ public:
|
|||
pkgViewerAct->setObjectName("pkgViewer");
|
||||
pkgViewerAct->setObjectName("pkgViewer");
|
||||
pkgViewerAct->setIcon(QIcon(":images/file_icon.png"));
|
||||
setThemeLight = new QAction(MainWindow);
|
||||
setThemeLight->setObjectName("setThemeLight");
|
||||
setThemeLight->setCheckable(true);
|
||||
setThemeLight->setChecked(true);
|
||||
setThemeDark = new QAction(MainWindow);
|
||||
setThemeDark->setObjectName("setThemeDark");
|
||||
setThemeDark->setCheckable(true);
|
||||
setThemeDark->setChecked(true);
|
||||
setThemeLight = new QAction(MainWindow);
|
||||
setThemeLight->setObjectName("setThemeLight");
|
||||
setThemeLight->setCheckable(true);
|
||||
setThemeGreen = new QAction(MainWindow);
|
||||
setThemeGreen->setObjectName("setThemeGreen");
|
||||
setThemeGreen->setCheckable(true);
|
||||
|
@ -285,7 +285,7 @@ public:
|
|||
} // setupUi
|
||||
|
||||
void retranslateUi(QMainWindow* MainWindow) {
|
||||
MainWindow->setWindowTitle(QCoreApplication::translate("MainWindow", "Shadps4", nullptr));
|
||||
MainWindow->setWindowTitle(QCoreApplication::translate("MainWindow", "shadPS4", nullptr));
|
||||
addElfFolderAct->setText(
|
||||
QCoreApplication::translate("MainWindow", "Open/Add Elf Folder", nullptr));
|
||||
bootInstallPkgAct->setText(
|
||||
|
@ -332,8 +332,8 @@ public:
|
|||
menuSettings->setTitle(QCoreApplication::translate("MainWindow", "Settings", nullptr));
|
||||
menuUtils->setTitle(QCoreApplication::translate("MainWindow", "Utils", nullptr));
|
||||
menuThemes->setTitle(QCoreApplication::translate("MainWindow", "Themes", nullptr));
|
||||
setThemeLight->setText(QCoreApplication::translate("MainWindow", "Light", nullptr));
|
||||
setThemeDark->setText(QCoreApplication::translate("MainWindow", "Dark", nullptr));
|
||||
setThemeLight->setText(QCoreApplication::translate("MainWindow", "Light", nullptr));
|
||||
setThemeGreen->setText(QCoreApplication::translate("MainWindow", "Green", nullptr));
|
||||
setThemeBlue->setText(QCoreApplication::translate("MainWindow", "Blue", nullptr));
|
||||
setThemeViolet->setText(QCoreApplication::translate("MainWindow", "Violet", nullptr));
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
|
||||
#include <QHeaderView>
|
||||
#include <QWidget>
|
||||
|
||||
#include "pkg_viewer.h"
|
||||
|
||||
PKGViewer::PKGViewer(std::shared_ptr<GameInfoClass> game_info_get, QWidget* parent,
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
#include <QTreeWidget>
|
||||
#include <QTreeWidgetItem>
|
||||
#include <QtConcurrent/QtConcurrent>
|
||||
|
||||
#include "common/io_file.h"
|
||||
#include "core/file_format/pkg.h"
|
||||
#include "core/file_format/pkg_type.h"
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
#include <QVBoxLayout>
|
||||
#include <QWidget>
|
||||
#include <QXmlStreamReader>
|
||||
|
||||
#include "common/types.h"
|
||||
#include "core/file_format/trp.h"
|
||||
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
#include "core/libraries/pad/pad.h"
|
||||
#include "input/controller.h"
|
||||
#include "sdl_window.h"
|
||||
#include "video_core/renderdoc.h"
|
||||
|
||||
#ifdef __APPLE__
|
||||
#include <SDL3/SDL_metal.h>
|
||||
|
@ -18,16 +19,17 @@
|
|||
|
||||
namespace Frontend {
|
||||
|
||||
WindowSDL::WindowSDL(s32 width_, s32 height_, Input::GameController* controller_)
|
||||
WindowSDL::WindowSDL(s32 width_, s32 height_, Input::GameController* controller_,
|
||||
std::string_view window_title)
|
||||
: width{width_}, height{height_}, controller{controller_} {
|
||||
if (SDL_Init(SDL_INIT_VIDEO) < 0) {
|
||||
UNREACHABLE_MSG("Failed to initialize SDL video subsystem: {}", SDL_GetError());
|
||||
}
|
||||
SDL_InitSubSystem(SDL_INIT_AUDIO);
|
||||
|
||||
const std::string title = "shadPS4 v" + std::string(Common::VERSION);
|
||||
SDL_PropertiesID props = SDL_CreateProperties();
|
||||
SDL_SetStringProperty(props, SDL_PROP_WINDOW_CREATE_TITLE_STRING, title.c_str());
|
||||
SDL_SetStringProperty(props, SDL_PROP_WINDOW_CREATE_TITLE_STRING,
|
||||
std::string(window_title).c_str());
|
||||
SDL_SetNumberProperty(props, SDL_PROP_WINDOW_CREATE_X_NUMBER, SDL_WINDOWPOS_CENTERED);
|
||||
SDL_SetNumberProperty(props, SDL_PROP_WINDOW_CREATE_Y_NUMBER, SDL_WINDOWPOS_CENTERED);
|
||||
SDL_SetNumberProperty(props, SDL_PROP_WINDOW_CREATE_WIDTH_NUMBER, width);
|
||||
|
@ -71,7 +73,7 @@ void WindowSDL::waitEvent() {
|
|||
// Called on main thread
|
||||
SDL_Event event;
|
||||
|
||||
if (!SDL_PollEvent(&event)) {
|
||||
if (!SDL_WaitEvent(&event)) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -179,6 +181,11 @@ void WindowSDL::onKeyPress(const SDL_Event* event) {
|
|||
ax = Input::GetAxis(-0x80, 0x80, axisvalue);
|
||||
break;
|
||||
case SDLK_S:
|
||||
if (event->key.mod == SDL_KMOD_LCTRL) {
|
||||
// Trigger rdoc capture
|
||||
VideoCore::TriggerCapture();
|
||||
break;
|
||||
}
|
||||
axis = Input::Axis::LeftY;
|
||||
if (event->type == SDL_EVENT_KEY_DOWN) {
|
||||
axisvalue += 127;
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include "common/types.h"
|
||||
|
||||
struct SDL_Window;
|
||||
|
@ -40,7 +41,8 @@ struct WindowSystemInfo {
|
|||
|
||||
class WindowSDL {
|
||||
public:
|
||||
explicit WindowSDL(s32 width, s32 height, Input::GameController* controller);
|
||||
explicit WindowSDL(s32 width, s32 height, Input::GameController* controller,
|
||||
std::string_view window_title);
|
||||
~WindowSDL();
|
||||
|
||||
s32 getWidth() const {
|
||||
|
|
|
@ -183,6 +183,7 @@ void DefineEntryPoint(const IR::Program& program, EmitContext& ctx, Id main) {
|
|||
ctx.AddCapability(spv::Capability::Float16);
|
||||
ctx.AddCapability(spv::Capability::Int16);
|
||||
}
|
||||
ctx.AddCapability(spv::Capability::Int64);
|
||||
if (info.has_storage_images) {
|
||||
ctx.AddCapability(spv::Capability::StorageImageExtendedFormats);
|
||||
}
|
||||
|
@ -204,8 +205,8 @@ void DefineEntryPoint(const IR::Program& program, EmitContext& ctx, Id main) {
|
|||
} else {
|
||||
ctx.AddExecutionMode(main, spv::ExecutionMode::OriginUpperLeft);
|
||||
}
|
||||
ctx.AddCapability(spv::Capability::GroupNonUniform);
|
||||
if (info.uses_group_quad) {
|
||||
ctx.AddCapability(spv::Capability::GroupNonUniform);
|
||||
ctx.AddCapability(spv::Capability::GroupNonUniformQuad);
|
||||
}
|
||||
if (info.has_discard) {
|
||||
|
@ -217,9 +218,9 @@ void DefineEntryPoint(const IR::Program& program, EmitContext& ctx, Id main) {
|
|||
if (info.has_image_query) {
|
||||
ctx.AddCapability(spv::Capability::ImageQuery);
|
||||
}
|
||||
// if (program.info.stores_frag_depth) {
|
||||
// ctx.AddExecutionMode(main, spv::ExecutionMode::DepthReplacing);
|
||||
// }
|
||||
if (info.stores.Get(IR::Attribute::Depth)) {
|
||||
ctx.AddExecutionMode(main, spv::ExecutionMode::DepthReplacing);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
throw NotImplementedException("Stage {}", u32(program.info.stage));
|
||||
|
|
|
@ -6,8 +6,8 @@
|
|||
|
||||
namespace Shader::Backend::SPIRV {
|
||||
|
||||
void EmitBitCastU16F16(EmitContext&) {
|
||||
UNREACHABLE_MSG("SPIR-V Instruction");
|
||||
Id EmitBitCastU16F16(EmitContext& ctx, Id value) {
|
||||
return ctx.OpBitcast(ctx.U16, value);
|
||||
}
|
||||
|
||||
Id EmitBitCastU32F32(EmitContext& ctx, Id value) {
|
||||
|
|
|
@ -120,6 +120,7 @@ void EmitGetGotoVariable(EmitContext&) {
|
|||
}
|
||||
|
||||
Id EmitReadConst(EmitContext& ctx) {
|
||||
return ctx.u32_zero_value;
|
||||
UNREACHABLE_MSG("Unreachable instruction");
|
||||
}
|
||||
|
||||
|
@ -149,6 +150,9 @@ Id EmitGetAttribute(EmitContext& ctx, IR::Attribute attr, u32 comp) {
|
|||
// Attribute is disabled or varying component is not written
|
||||
return ctx.ConstF32(comp == 3 ? 1.0f : 0.0f);
|
||||
}
|
||||
if (param.is_default) {
|
||||
return ctx.OpCompositeExtract(param.component_type, param.id, comp);
|
||||
}
|
||||
|
||||
if (param.num_components > 1) {
|
||||
const Id pointer{
|
||||
|
@ -208,7 +212,7 @@ Id EmitGetAttributeU32(EmitContext& ctx, IR::Attribute attr, u32 comp) {
|
|||
|
||||
void EmitSetAttribute(EmitContext& ctx, IR::Attribute attr, Id value, u32 element) {
|
||||
const Id pointer{OutputAttrPointer(ctx, attr, element)};
|
||||
ctx.OpStore(pointer, value);
|
||||
ctx.OpStore(pointer, ctx.OpBitcast(ctx.F32[1], value));
|
||||
}
|
||||
|
||||
Id EmitLoadBufferU32(EmitContext& ctx, IR::Inst* inst, u32 handle, Id address) {
|
||||
|
|
|
@ -259,4 +259,8 @@ Id EmitConvertU16U32(EmitContext& ctx, Id value) {
|
|||
return ctx.OpUConvert(ctx.U16, value);
|
||||
}
|
||||
|
||||
Id EmitConvertU32U16(EmitContext& ctx, Id value) {
|
||||
return ctx.OpUConvert(ctx.U32[1], value);
|
||||
}
|
||||
|
||||
} // namespace Shader::Backend::SPIRV
|
||||
|
|
|
@ -385,4 +385,8 @@ Id EmitFPIsInf64(EmitContext& ctx, Id value) {
|
|||
return ctx.OpIsInf(ctx.U1[1], value);
|
||||
}
|
||||
|
||||
void EmitFPCmpClass32(EmitContext&) {
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
} // namespace Shader::Backend::SPIRV
|
||||
|
|
|
@ -70,7 +70,6 @@ Id EmitImageGather(EmitContext& ctx, IR::Inst* inst, u32 handle, Id coords, Id o
|
|||
const u32 comp = inst->Flags<IR::TextureInstInfo>().gather_comp.Value();
|
||||
ImageOperands operands;
|
||||
operands.Add(spv::ImageOperandsMask::Offset, offset);
|
||||
operands.Add(spv::ImageOperandsMask::Lod, ctx.ConstF32(0.f));
|
||||
return ctx.OpImageGather(ctx.F32[4], sampled_image, coords, ctx.ConstU32(comp), operands.mask,
|
||||
operands.operands);
|
||||
}
|
||||
|
@ -106,8 +105,7 @@ Id EmitImageQueryDimensions(EmitContext& ctx, IR::Inst* inst, u32 handle, Id lod
|
|||
const auto type = ctx.info.images[handle & 0xFFFF].type;
|
||||
const Id zero = ctx.u32_zero_value;
|
||||
const auto mips{[&] { return skip_mips ? zero : ctx.OpImageQueryLevels(ctx.U32[1], image); }};
|
||||
const bool uses_lod{type != AmdGpu::ImageType::Color2DMsaa &&
|
||||
type != AmdGpu::ImageType::Buffer};
|
||||
const bool uses_lod{type != AmdGpu::ImageType::Color2DMsaa};
|
||||
const auto query{[&](Id type) {
|
||||
return uses_lod ? ctx.OpImageQuerySizeLod(type, image, lod)
|
||||
: ctx.OpImageQuerySize(type, image);
|
||||
|
|
|
@ -42,6 +42,7 @@ void EmitSetVcc(EmitContext& ctx);
|
|||
void EmitSetSccLo(EmitContext& ctx);
|
||||
void EmitSetVccLo(EmitContext& ctx);
|
||||
void EmitSetVccHi(EmitContext& ctx);
|
||||
void EmitFPCmpClass32(EmitContext& ctx);
|
||||
void EmitPrologue(EmitContext& ctx);
|
||||
void EmitEpilogue(EmitContext& ctx);
|
||||
void EmitDiscard(EmitContext& ctx);
|
||||
|
@ -148,7 +149,7 @@ Id EmitSelectU64(EmitContext& ctx, Id cond, Id true_value, Id false_value);
|
|||
Id EmitSelectF16(EmitContext& ctx, Id cond, Id true_value, Id false_value);
|
||||
Id EmitSelectF32(EmitContext& ctx, Id cond, Id true_value, Id false_value);
|
||||
Id EmitSelectF64(EmitContext& ctx, Id cond, Id true_value, Id false_value);
|
||||
void EmitBitCastU16F16(EmitContext& ctx);
|
||||
Id EmitBitCastU16F16(EmitContext& ctx, Id value);
|
||||
Id EmitBitCastU32F32(EmitContext& ctx, Id value);
|
||||
void EmitBitCastU64F64(EmitContext& ctx);
|
||||
Id EmitBitCastF16U16(EmitContext& ctx, Id value);
|
||||
|
@ -258,6 +259,7 @@ Id EmitISub64(EmitContext& ctx, Id a, Id b);
|
|||
Id EmitSMulExt(EmitContext& ctx, Id a, Id b);
|
||||
Id EmitUMulExt(EmitContext& ctx, Id a, Id b);
|
||||
Id EmitIMul32(EmitContext& ctx, Id a, Id b);
|
||||
Id EmitIMul64(EmitContext& ctx, Id a, Id b);
|
||||
Id EmitSDiv32(EmitContext& ctx, Id a, Id b);
|
||||
Id EmitUDiv32(EmitContext& ctx, Id a, Id b);
|
||||
Id EmitINeg32(EmitContext& ctx, Id value);
|
||||
|
@ -271,6 +273,7 @@ Id EmitShiftRightArithmetic32(EmitContext& ctx, Id base, Id shift);
|
|||
Id EmitShiftRightArithmetic64(EmitContext& ctx, Id base, Id shift);
|
||||
Id EmitBitwiseAnd32(EmitContext& ctx, IR::Inst* inst, Id a, Id b);
|
||||
Id EmitBitwiseOr32(EmitContext& ctx, IR::Inst* inst, Id a, Id b);
|
||||
Id EmitBitwiseOr64(EmitContext& ctx, IR::Inst* inst, Id a, Id b);
|
||||
Id EmitBitwiseXor32(EmitContext& ctx, IR::Inst* inst, Id a, Id b);
|
||||
Id EmitBitFieldInsert(EmitContext& ctx, Id base, Id insert, Id offset, Id count);
|
||||
Id EmitBitFieldSExtract(EmitContext& ctx, IR::Inst* inst, Id base, Id offset, Id count);
|
||||
|
@ -280,14 +283,17 @@ Id EmitBitCount32(EmitContext& ctx, Id value);
|
|||
Id EmitBitwiseNot32(EmitContext& ctx, Id value);
|
||||
Id EmitFindSMsb32(EmitContext& ctx, Id value);
|
||||
Id EmitFindUMsb32(EmitContext& ctx, Id value);
|
||||
Id EmitFindILsb32(EmitContext& ctx, Id value);
|
||||
Id EmitSMin32(EmitContext& ctx, Id a, Id b);
|
||||
Id EmitUMin32(EmitContext& ctx, Id a, Id b);
|
||||
Id EmitSMax32(EmitContext& ctx, Id a, Id b);
|
||||
Id EmitUMax32(EmitContext& ctx, Id a, Id b);
|
||||
Id EmitSClamp32(EmitContext& ctx, IR::Inst* inst, Id value, Id min, Id max);
|
||||
Id EmitUClamp32(EmitContext& ctx, IR::Inst* inst, Id value, Id min, Id max);
|
||||
Id EmitSLessThan(EmitContext& ctx, Id lhs, Id rhs);
|
||||
Id EmitULessThan(EmitContext& ctx, Id lhs, Id rhs);
|
||||
Id EmitSLessThan32(EmitContext& ctx, Id lhs, Id rhs);
|
||||
Id EmitSLessThan64(EmitContext& ctx, Id lhs, Id rhs);
|
||||
Id EmitULessThan32(EmitContext& ctx, Id lhs, Id rhs);
|
||||
Id EmitULessThan64(EmitContext& ctx, Id lhs, Id rhs);
|
||||
Id EmitIEqual(EmitContext& ctx, Id lhs, Id rhs);
|
||||
Id EmitSLessThanEqual(EmitContext& ctx, Id lhs, Id rhs);
|
||||
Id EmitULessThanEqual(EmitContext& ctx, Id lhs, Id rhs);
|
||||
|
@ -349,6 +355,7 @@ Id EmitConvertF64U16(EmitContext& ctx, Id value);
|
|||
Id EmitConvertF64U32(EmitContext& ctx, Id value);
|
||||
Id EmitConvertF64U64(EmitContext& ctx, Id value);
|
||||
Id EmitConvertU16U32(EmitContext& ctx, Id value);
|
||||
Id EmitConvertU32U16(EmitContext& ctx, Id value);
|
||||
|
||||
Id EmitImageSampleImplicitLod(EmitContext& ctx, IR::Inst* inst, u32 handle, Id coords, Id bias_lc,
|
||||
Id offset);
|
||||
|
@ -383,6 +390,7 @@ Id EmitImageAtomicXor32(EmitContext& ctx, IR::Inst* inst, u32 handle, Id coords,
|
|||
Id EmitImageAtomicExchange32(EmitContext& ctx, IR::Inst* inst, u32 handle, Id coords, Id value);
|
||||
|
||||
Id EmitLaneId(EmitContext& ctx);
|
||||
Id EmitWarpId(EmitContext& ctx);
|
||||
Id EmitQuadShuffle(EmitContext& ctx, Id value, Id index);
|
||||
|
||||
} // namespace Shader::Backend::SPIRV
|
||||
|
|
|
@ -84,6 +84,10 @@ Id EmitIMul32(EmitContext& ctx, Id a, Id b) {
|
|||
return ctx.OpIMul(ctx.U32[1], a, b);
|
||||
}
|
||||
|
||||
Id EmitIMul64(EmitContext& ctx, Id a, Id b) {
|
||||
return ctx.OpIMul(ctx.U64, a, b);
|
||||
}
|
||||
|
||||
Id EmitSDiv32(EmitContext& ctx, Id a, Id b) {
|
||||
return ctx.OpSDiv(ctx.U32[1], a, b);
|
||||
}
|
||||
|
@ -142,6 +146,13 @@ Id EmitBitwiseOr32(EmitContext& ctx, IR::Inst* inst, Id a, Id b) {
|
|||
return result;
|
||||
}
|
||||
|
||||
Id EmitBitwiseOr64(EmitContext& ctx, IR::Inst* inst, Id a, Id b) {
|
||||
const Id result{ctx.OpBitwiseOr(ctx.U64, a, b)};
|
||||
SetZeroFlag(ctx, inst, result);
|
||||
SetSignFlag(ctx, inst, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
Id EmitBitwiseXor32(EmitContext& ctx, IR::Inst* inst, Id a, Id b) {
|
||||
const Id result{ctx.OpBitwiseXor(ctx.U32[1], a, b)};
|
||||
SetZeroFlag(ctx, inst, result);
|
||||
|
@ -187,6 +198,10 @@ Id EmitFindUMsb32(EmitContext& ctx, Id value) {
|
|||
return ctx.OpFindUMsb(ctx.U32[1], value);
|
||||
}
|
||||
|
||||
Id EmitFindILsb32(EmitContext& ctx, Id value) {
|
||||
return ctx.OpFindILsb(ctx.U32[1], value);
|
||||
}
|
||||
|
||||
Id EmitSMin32(EmitContext& ctx, Id a, Id b) {
|
||||
return ctx.OpSMin(ctx.U32[1], a, b);
|
||||
}
|
||||
|
@ -231,11 +246,19 @@ Id EmitUClamp32(EmitContext& ctx, IR::Inst* inst, Id value, Id min, Id max) {
|
|||
return result;
|
||||
}
|
||||
|
||||
Id EmitSLessThan(EmitContext& ctx, Id lhs, Id rhs) {
|
||||
Id EmitSLessThan32(EmitContext& ctx, Id lhs, Id rhs) {
|
||||
return ctx.OpSLessThan(ctx.U1[1], lhs, rhs);
|
||||
}
|
||||
|
||||
Id EmitULessThan(EmitContext& ctx, Id lhs, Id rhs) {
|
||||
Id EmitSLessThan64(EmitContext& ctx, Id lhs, Id rhs) {
|
||||
return ctx.OpSLessThan(ctx.U1[1], lhs, rhs);
|
||||
}
|
||||
|
||||
Id EmitULessThan32(EmitContext& ctx, Id lhs, Id rhs) {
|
||||
return ctx.OpULessThan(ctx.U1[1], lhs, rhs);
|
||||
}
|
||||
|
||||
Id EmitULessThan64(EmitContext& ctx, Id lhs, Id rhs) {
|
||||
return ctx.OpULessThan(ctx.U1[1], lhs, rhs);
|
||||
}
|
||||
|
||||
|
|
|
@ -10,6 +10,10 @@ Id SubgroupScope(EmitContext& ctx) {
|
|||
return ctx.ConstU32(static_cast<u32>(spv::Scope::Subgroup));
|
||||
}
|
||||
|
||||
Id EmitWarpId(EmitContext& ctx) {
|
||||
return ctx.OpLoad(ctx.U32[1], ctx.subgroup_id);
|
||||
}
|
||||
|
||||
Id EmitLaneId(EmitContext& ctx) {
|
||||
return ctx.OpLoad(ctx.U32[1], ctx.subgroup_local_invocation_id);
|
||||
}
|
||||
|
|
|
@ -49,7 +49,7 @@ EmitContext::EmitContext(const Profile& profile_, IR::Program& program, u32& bin
|
|||
DefineInterfaces(program);
|
||||
DefineBuffers(info);
|
||||
DefineImagesAndSamplers(info);
|
||||
DefineSharedMemory(info);
|
||||
DefineSharedMemory();
|
||||
}
|
||||
|
||||
EmitContext::~EmitContext() = default;
|
||||
|
@ -86,6 +86,7 @@ void EmitContext::DefineArithmeticTypes() {
|
|||
F32[1] = Name(TypeFloat(32), "f32_id");
|
||||
S32[1] = Name(TypeSInt(32), "i32_id");
|
||||
U32[1] = Name(TypeUInt(32), "u32_id");
|
||||
U64 = Name(TypeUInt(64), "u64_id");
|
||||
|
||||
for (u32 i = 2; i <= 4; i++) {
|
||||
if (info.uses_fp16) {
|
||||
|
@ -126,6 +127,7 @@ Id GetAttributeType(EmitContext& ctx, AmdGpu::NumberFormat fmt) {
|
|||
case AmdGpu::NumberFormat::Float:
|
||||
case AmdGpu::NumberFormat::Unorm:
|
||||
case AmdGpu::NumberFormat::Snorm:
|
||||
case AmdGpu::NumberFormat::SnormNz:
|
||||
return ctx.F32[4];
|
||||
case AmdGpu::NumberFormat::Sint:
|
||||
return ctx.S32[4];
|
||||
|
@ -146,6 +148,7 @@ EmitContext::SpirvAttribute EmitContext::GetAttributeInfo(AmdGpu::NumberFormat f
|
|||
case AmdGpu::NumberFormat::Float:
|
||||
case AmdGpu::NumberFormat::Unorm:
|
||||
case AmdGpu::NumberFormat::Snorm:
|
||||
case AmdGpu::NumberFormat::SnormNz:
|
||||
return {id, input_f32, F32[1], 4};
|
||||
case AmdGpu::NumberFormat::Uint:
|
||||
return {id, input_u32, U32[1], 4};
|
||||
|
@ -204,7 +207,9 @@ void EmitContext::DefineInputs(const Info& info) {
|
|||
: 1;
|
||||
// Note that we pass index rather than Id
|
||||
input_params[input.binding] = {
|
||||
rate_idx, input_u32, U32[1], input.num_components, input.instance_data_buf,
|
||||
rate_idx, input_u32,
|
||||
U32[1], input.num_components,
|
||||
false, input.instance_data_buf,
|
||||
};
|
||||
} else {
|
||||
Id id{DefineInput(type, input.binding)};
|
||||
|
@ -220,19 +225,18 @@ void EmitContext::DefineInputs(const Info& info) {
|
|||
break;
|
||||
}
|
||||
case Stage::Fragment:
|
||||
if (info.uses_group_quad) {
|
||||
subgroup_local_invocation_id = DefineVariable(
|
||||
U32[1], spv::BuiltIn::SubgroupLocalInvocationId, spv::StorageClass::Input);
|
||||
Decorate(subgroup_local_invocation_id, spv::Decoration::Flat);
|
||||
}
|
||||
subgroup_id = DefineVariable(U32[1], spv::BuiltIn::SubgroupId, spv::StorageClass::Input);
|
||||
subgroup_local_invocation_id = DefineVariable(
|
||||
U32[1], spv::BuiltIn::SubgroupLocalInvocationId, spv::StorageClass::Input);
|
||||
Decorate(subgroup_local_invocation_id, spv::Decoration::Flat);
|
||||
frag_coord = DefineVariable(F32[4], spv::BuiltIn::FragCoord, spv::StorageClass::Input);
|
||||
frag_depth = DefineVariable(F32[1], spv::BuiltIn::FragDepth, spv::StorageClass::Output);
|
||||
front_facing = DefineVariable(U1[1], spv::BuiltIn::FrontFacing, spv::StorageClass::Input);
|
||||
for (const auto& input : info.ps_inputs) {
|
||||
const u32 semantic = input.param_index;
|
||||
if (input.is_default) {
|
||||
input_params[semantic] = {MakeDefaultValue(*this, input.default_value), input_f32,
|
||||
F32[1]};
|
||||
input_params[semantic] = {MakeDefaultValue(*this, input.default_value), F32[1],
|
||||
F32[1], 4, true};
|
||||
continue;
|
||||
}
|
||||
const IR::Attribute param{IR::Attribute::Param0 + input.param_index};
|
||||
|
@ -392,7 +396,16 @@ spv::ImageFormat GetFormat(const AmdGpu::Image& image) {
|
|||
image.GetNumberFmt() == AmdGpu::NumberFormat::Uint) {
|
||||
return spv::ImageFormat::Rgba8ui;
|
||||
}
|
||||
UNREACHABLE();
|
||||
if (image.GetDataFmt() == AmdGpu::DataFormat::Format10_11_11 &&
|
||||
image.GetNumberFmt() == AmdGpu::NumberFormat::Float) {
|
||||
return spv::ImageFormat::R11fG11fB10f;
|
||||
}
|
||||
if (image.GetDataFmt() == AmdGpu::DataFormat::Format32_32_32_32 &&
|
||||
image.GetNumberFmt() == AmdGpu::NumberFormat::Float) {
|
||||
return spv::ImageFormat::Rgba32f;
|
||||
}
|
||||
UNREACHABLE_MSG("Unknown storage format data_format={}, num_format={}", image.GetDataFmt(),
|
||||
image.GetNumberFmt());
|
||||
}
|
||||
|
||||
Id ImageType(EmitContext& ctx, const ImageResource& desc, Id sampled_type) {
|
||||
|
@ -412,8 +425,6 @@ Id ImageType(EmitContext& ctx, const ImageResource& desc, Id sampled_type) {
|
|||
return ctx.TypeImage(sampled_type, spv::Dim::Dim3D, false, false, false, sampled, format);
|
||||
case AmdGpu::ImageType::Cube:
|
||||
return ctx.TypeImage(sampled_type, spv::Dim::Cube, false, false, false, sampled, format);
|
||||
case AmdGpu::ImageType::Buffer:
|
||||
throw NotImplementedException("Image buffer");
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
@ -471,10 +482,14 @@ void EmitContext::DefineImagesAndSamplers(const Info& info) {
|
|||
}
|
||||
}
|
||||
|
||||
void EmitContext::DefineSharedMemory(const Info& info) {
|
||||
if (info.shared_memory_size == 0) {
|
||||
void EmitContext::DefineSharedMemory() {
|
||||
static constexpr size_t DefaultSharedMemSize = 16_KB;
|
||||
if (!info.uses_shared) {
|
||||
return;
|
||||
}
|
||||
if (info.shared_memory_size == 0) {
|
||||
info.shared_memory_size = DefaultSharedMemSize;
|
||||
}
|
||||
const auto make{[&](Id element_type, u32 element_size) {
|
||||
const u32 num_elements{Common::DivCeil(info.shared_memory_size, element_size)};
|
||||
const Id array_type{TypeArray(element_type, ConstU32(num_elements))};
|
||||
|
|
|
@ -180,6 +180,7 @@ public:
|
|||
|
||||
Id workgroup_id{};
|
||||
Id local_invocation_id{};
|
||||
Id subgroup_id{};
|
||||
Id subgroup_local_invocation_id{};
|
||||
Id image_u32{};
|
||||
|
||||
|
@ -219,6 +220,7 @@ public:
|
|||
Id pointer_type;
|
||||
Id component_type;
|
||||
u32 num_components;
|
||||
bool is_default{};
|
||||
s32 buffer_handle{-1};
|
||||
};
|
||||
std::array<SpirvAttribute, 32> input_params{};
|
||||
|
@ -231,7 +233,7 @@ private:
|
|||
void DefineOutputs(const Info& info);
|
||||
void DefineBuffers(const Info& info);
|
||||
void DefineImagesAndSamplers(const Info& info);
|
||||
void DefineSharedMemory(const Info& info);
|
||||
void DefineSharedMemory();
|
||||
|
||||
SpirvAttribute GetAttributeInfo(AmdGpu::NumberFormat fmt, Id id);
|
||||
};
|
||||
|
|
|
@ -1479,7 +1479,7 @@ constexpr std::array<InstFormat, 455> InstructionFormatVOP3 = {{
|
|||
{InstClass::VectorFpGraph32, InstCategory::VectorALU, 3, 1, ScalarType::Float32,
|
||||
ScalarType::Float32},
|
||||
// 337 = V_MIN3_F32
|
||||
{InstClass::VectorIntArith32, InstCategory::VectorALU, 3, 1, ScalarType::Float32,
|
||||
{InstClass::VectorFpArith32, InstCategory::VectorALU, 3, 1, ScalarType::Float32,
|
||||
ScalarType::Float32},
|
||||
// 338 = V_MIN3_I32
|
||||
{InstClass::VectorIntArith32, InstCategory::VectorALU, 3, 1, ScalarType::Sint32,
|
||||
|
@ -1488,7 +1488,7 @@ constexpr std::array<InstFormat, 455> InstructionFormatVOP3 = {{
|
|||
{InstClass::VectorIntArith32, InstCategory::VectorALU, 3, 1, ScalarType::Uint32,
|
||||
ScalarType::Uint32},
|
||||
// 340 = V_MAX3_F32
|
||||
{InstClass::VectorIntArith32, InstCategory::VectorALU, 3, 1, ScalarType::Float32,
|
||||
{InstClass::VectorFpArith32, InstCategory::VectorALU, 3, 1, ScalarType::Float32,
|
||||
ScalarType::Float32},
|
||||
// 341 = V_MAX3_I32
|
||||
{InstClass::VectorIntArith32, InstCategory::VectorALU, 3, 1, ScalarType::Sint32,
|
||||
|
@ -1497,7 +1497,7 @@ constexpr std::array<InstFormat, 455> InstructionFormatVOP3 = {{
|
|||
{InstClass::VectorIntArith32, InstCategory::VectorALU, 3, 1, ScalarType::Uint32,
|
||||
ScalarType::Uint32},
|
||||
// 343 = V_MED3_F32
|
||||
{InstClass::VectorIntArith32, InstCategory::VectorALU, 3, 1, ScalarType::Float32,
|
||||
{InstClass::VectorFpArith32, InstCategory::VectorALU, 3, 1, ScalarType::Float32,
|
||||
ScalarType::Float32},
|
||||
// 344 = V_MED3_I32
|
||||
{InstClass::VectorIntArith32, InstCategory::VectorALU, 3, 1, ScalarType::Sint32,
|
||||
|
@ -2779,11 +2779,9 @@ constexpr std::array<InstFormat, 256> InstructionFormatDS = {{
|
|||
// 60 = DS_READ_U16
|
||||
{InstClass::DsIdxRd, InstCategory::DataShare, 3, 1, ScalarType::Uint32, ScalarType::Uint32},
|
||||
// 61 = DS_CONSUME
|
||||
{InstClass::DsAppendCon, InstCategory::DataShare, 3, 1, ScalarType::Undefined,
|
||||
ScalarType::Undefined},
|
||||
{InstClass::DsAppendCon, InstCategory::DataShare, 3, 1, ScalarType::Uint32, ScalarType::Uint32},
|
||||
// 62 = DS_APPEND
|
||||
{InstClass::DsAppendCon, InstCategory::DataShare, 3, 1, ScalarType::Undefined,
|
||||
ScalarType::Undefined},
|
||||
{InstClass::DsAppendCon, InstCategory::DataShare, 3, 1, ScalarType::Uint32, ScalarType::Uint32},
|
||||
// 63 = DS_ORDERED_COUNT
|
||||
{InstClass::GdsOrdCnt, InstCategory::DataShare, 3, 1, ScalarType::Undefined,
|
||||
ScalarType::Undefined},
|
||||
|
|
|
@ -76,11 +76,11 @@ struct SMRD {
|
|||
};
|
||||
|
||||
struct InstControlSOPK {
|
||||
BitField<0, 16, u32> simm;
|
||||
s16 simm;
|
||||
};
|
||||
|
||||
struct InstControlSOPP {
|
||||
BitField<0, 16, u32> simm;
|
||||
s16 simm;
|
||||
};
|
||||
|
||||
struct InstControlVOP3 {
|
||||
|
|
|
@ -2392,10 +2392,10 @@ enum class OperandField : u32 {
|
|||
ConstFloatPos_4_0,
|
||||
ConstFloatNeg_4_0,
|
||||
VccZ = 251,
|
||||
ExecZ,
|
||||
Scc,
|
||||
LdsDirect,
|
||||
LiteralConst,
|
||||
ExecZ = 252,
|
||||
Scc = 253,
|
||||
LdsDirect = 254,
|
||||
LiteralConst = 255,
|
||||
VectorGPR,
|
||||
|
||||
Undefined = 0xFFFFFFFF,
|
||||
|
|
|
@ -600,13 +600,13 @@ public:
|
|||
TranslatePass(ObjectPool<IR::Inst>& inst_pool_, ObjectPool<IR::Block>& block_pool_,
|
||||
ObjectPool<Statement>& stmt_pool_, Statement& root_stmt,
|
||||
IR::AbstractSyntaxList& syntax_list_, std::span<const GcnInst> inst_list_,
|
||||
Info& info_)
|
||||
Info& info_, const Profile& profile_)
|
||||
: stmt_pool{stmt_pool_}, inst_pool{inst_pool_}, block_pool{block_pool_},
|
||||
syntax_list{syntax_list_}, inst_list{inst_list_}, info{info_} {
|
||||
syntax_list{syntax_list_}, inst_list{inst_list_}, info{info_}, profile{profile_} {
|
||||
Visit(root_stmt, nullptr, nullptr);
|
||||
|
||||
IR::Block& first_block{*syntax_list.front().data.block};
|
||||
Translator{&first_block, info}.EmitPrologue();
|
||||
Translator{&first_block, info, profile}.EmitPrologue();
|
||||
}
|
||||
|
||||
private:
|
||||
|
@ -635,7 +635,7 @@ private:
|
|||
const u32 start = stmt.block->begin_index;
|
||||
const u32 size = stmt.block->end_index - start + 1;
|
||||
Translate(current_block, stmt.block->begin, inst_list.subspan(start, size),
|
||||
info);
|
||||
info, profile);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
@ -815,16 +815,18 @@ private:
|
|||
const Block dummy_flow_block{.is_dummy = true};
|
||||
std::span<const GcnInst> inst_list;
|
||||
Info& info;
|
||||
const Profile& profile;
|
||||
};
|
||||
} // Anonymous namespace
|
||||
|
||||
IR::AbstractSyntaxList BuildASL(ObjectPool<IR::Inst>& inst_pool, ObjectPool<IR::Block>& block_pool,
|
||||
CFG& cfg, Info& info) {
|
||||
CFG& cfg, Info& info, const Profile& profile) {
|
||||
ObjectPool<Statement> stmt_pool{64};
|
||||
GotoPass goto_pass{cfg, stmt_pool};
|
||||
Statement& root{goto_pass.RootStatement()};
|
||||
IR::AbstractSyntaxList syntax_list;
|
||||
TranslatePass{inst_pool, block_pool, stmt_pool, root, syntax_list, cfg.inst_list, info};
|
||||
TranslatePass{inst_pool, block_pool, stmt_pool, root,
|
||||
syntax_list, cfg.inst_list, info, profile};
|
||||
ASSERT_MSG(!info.translation_failed, "Shader translation has failed");
|
||||
return syntax_list;
|
||||
}
|
||||
|
|
|
@ -11,12 +11,13 @@
|
|||
|
||||
namespace Shader {
|
||||
struct Info;
|
||||
}
|
||||
struct Profile;
|
||||
} // namespace Shader
|
||||
|
||||
namespace Shader::Gcn {
|
||||
|
||||
[[nodiscard]] IR::AbstractSyntaxList BuildASL(ObjectPool<IR::Inst>& inst_pool,
|
||||
ObjectPool<IR::Block>& block_pool, CFG& cfg,
|
||||
Info& info);
|
||||
Info& info, const Profile& profile);
|
||||
|
||||
} // namespace Shader::Gcn
|
||||
|
|
|
@ -5,6 +5,31 @@
|
|||
|
||||
namespace Shader::Gcn {
|
||||
|
||||
void Translator::EmitDataShare(const GcnInst& inst) {
|
||||
switch (inst.opcode) {
|
||||
case Opcode::DS_SWIZZLE_B32:
|
||||
return DS_SWIZZLE_B32(inst);
|
||||
case Opcode::DS_READ_B32:
|
||||
return DS_READ(32, false, false, inst);
|
||||
case Opcode::DS_READ_B64:
|
||||
return DS_READ(64, false, false, inst);
|
||||
case Opcode::DS_READ2_B32:
|
||||
return DS_READ(32, false, true, inst);
|
||||
case Opcode::DS_READ2_B64:
|
||||
return DS_READ(64, false, true, inst);
|
||||
case Opcode::DS_WRITE_B32:
|
||||
return DS_WRITE(32, false, false, inst);
|
||||
case Opcode::DS_WRITE_B64:
|
||||
return DS_WRITE(64, false, false, inst);
|
||||
case Opcode::DS_WRITE2_B32:
|
||||
return DS_WRITE(32, false, true, inst);
|
||||
case Opcode::DS_WRITE2_B64:
|
||||
return DS_WRITE(64, false, true, inst);
|
||||
default:
|
||||
LogMissingOpcode(inst);
|
||||
}
|
||||
}
|
||||
|
||||
void Translator::DS_SWIZZLE_B32(const GcnInst& inst) {
|
||||
const u8 offset0 = inst.control.ds.offset0;
|
||||
const u8 offset1 = inst.control.ds.offset1;
|
||||
|
@ -20,14 +45,25 @@ void Translator::DS_SWIZZLE_B32(const GcnInst& inst) {
|
|||
|
||||
void Translator::DS_READ(int bit_size, bool is_signed, bool is_pair, const GcnInst& inst) {
|
||||
const IR::U32 addr{ir.GetVectorReg(IR::VectorReg(inst.src[0].code))};
|
||||
const IR::VectorReg dst_reg{inst.dst[0].code};
|
||||
IR::VectorReg dst_reg{inst.dst[0].code};
|
||||
if (is_pair) {
|
||||
// Pair loads are either 32 or 64-bit. We assume 32-bit for now.
|
||||
ASSERT(bit_size == 32);
|
||||
// Pair loads are either 32 or 64-bit
|
||||
const IR::U32 addr0 = ir.IAdd(addr, ir.Imm32(u32(inst.control.ds.offset0)));
|
||||
ir.SetVectorReg(dst_reg, IR::U32{ir.LoadShared(32, is_signed, addr0)});
|
||||
const IR::Value data0 = ir.LoadShared(bit_size, is_signed, addr0);
|
||||
if (bit_size == 32) {
|
||||
ir.SetVectorReg(dst_reg++, IR::U32{data0});
|
||||
} else {
|
||||
ir.SetVectorReg(dst_reg++, IR::U32{ir.CompositeExtract(data0, 0)});
|
||||
ir.SetVectorReg(dst_reg++, IR::U32{ir.CompositeExtract(data0, 1)});
|
||||
}
|
||||
const IR::U32 addr1 = ir.IAdd(addr, ir.Imm32(u32(inst.control.ds.offset1)));
|
||||
ir.SetVectorReg(dst_reg + 1, IR::U32{ir.LoadShared(32, is_signed, addr1)});
|
||||
const IR::Value data1 = ir.LoadShared(bit_size, is_signed, addr1);
|
||||
if (bit_size == 32) {
|
||||
ir.SetVectorReg(dst_reg++, IR::U32{data1});
|
||||
} else {
|
||||
ir.SetVectorReg(dst_reg++, IR::U32{ir.CompositeExtract(data1, 0)});
|
||||
ir.SetVectorReg(dst_reg++, IR::U32{ir.CompositeExtract(data1, 1)});
|
||||
}
|
||||
} else if (bit_size == 64) {
|
||||
const IR::Value data = ir.LoadShared(bit_size, is_signed, addr);
|
||||
ir.SetVectorReg(dst_reg, IR::U32{ir.CompositeExtract(data, 0)});
|
||||
|
@ -43,11 +79,22 @@ void Translator::DS_WRITE(int bit_size, bool is_signed, bool is_pair, const GcnI
|
|||
const IR::VectorReg data0{inst.src[1].code};
|
||||
const IR::VectorReg data1{inst.src[2].code};
|
||||
if (is_pair) {
|
||||
ASSERT(bit_size == 32);
|
||||
const IR::U32 addr0 = ir.IAdd(addr, ir.Imm32(u32(inst.control.ds.offset0)));
|
||||
ir.WriteShared(32, ir.GetVectorReg(data0), addr0);
|
||||
if (bit_size == 32) {
|
||||
ir.WriteShared(32, ir.GetVectorReg(data0), addr0);
|
||||
} else {
|
||||
ir.WriteShared(
|
||||
64, ir.CompositeConstruct(ir.GetVectorReg(data0), ir.GetVectorReg(data0 + 1)),
|
||||
addr0);
|
||||
}
|
||||
const IR::U32 addr1 = ir.IAdd(addr, ir.Imm32(u32(inst.control.ds.offset1)));
|
||||
ir.WriteShared(32, ir.GetVectorReg(data1), addr1);
|
||||
if (bit_size == 32) {
|
||||
ir.WriteShared(32, ir.GetVectorReg(data1), addr1);
|
||||
} else {
|
||||
ir.WriteShared(
|
||||
64, ir.CompositeConstruct(ir.GetVectorReg(data1), ir.GetVectorReg(data1 + 1)),
|
||||
addr1);
|
||||
}
|
||||
} else if (bit_size == 64) {
|
||||
const IR::Value data =
|
||||
ir.CompositeConstruct(ir.GetVectorReg(data0), ir.GetVectorReg(data0 + 1));
|
||||
|
@ -62,7 +109,18 @@ void Translator::S_BARRIER() {
|
|||
}
|
||||
|
||||
void Translator::V_READFIRSTLANE_B32(const GcnInst& inst) {
|
||||
UNREACHABLE();
|
||||
ASSERT(info.stage != Stage::Compute);
|
||||
SetDst(inst.dst[0], GetSrc(inst.src[0]));
|
||||
}
|
||||
|
||||
void Translator::V_READLANE_B32(const GcnInst& inst) {
|
||||
ASSERT(info.stage != Stage::Compute);
|
||||
SetDst(inst.dst[0], GetSrc(inst.src[0]));
|
||||
}
|
||||
|
||||
void Translator::V_WRITELANE_B32(const GcnInst& inst) {
|
||||
ASSERT(info.stage != Stage::Compute);
|
||||
SetDst(inst.dst[0], GetSrc(inst.src[0]));
|
||||
}
|
||||
|
||||
} // namespace Shader::Gcn
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
|
||||
namespace Shader::Gcn {
|
||||
|
||||
void Translator::EXP(const GcnInst& inst) {
|
||||
void Translator::EmitExport(const GcnInst& inst) {
|
||||
if (ir.block->has_multiple_predecessors && info.stage == Stage::Fragment) {
|
||||
LOG_WARNING(Render_Recompiler, "An ambiguous export appeared in translation");
|
||||
ir.Discard(ir.LogicalNot(ir.GetExec()));
|
||||
|
|
|
@ -5,8 +5,102 @@
|
|||
|
||||
namespace Shader::Gcn {
|
||||
|
||||
void Translator::EmitScalarAlu(const GcnInst& inst) {
|
||||
switch (inst.opcode) {
|
||||
case Opcode::S_MOVK_I32:
|
||||
return S_MOVK(inst);
|
||||
case Opcode::S_MOV_B32:
|
||||
return S_MOV(inst);
|
||||
case Opcode::S_MUL_I32:
|
||||
return S_MUL_I32(inst);
|
||||
case Opcode::S_AND_SAVEEXEC_B64:
|
||||
return S_AND_SAVEEXEC_B64(inst);
|
||||
case Opcode::S_MOV_B64:
|
||||
return S_MOV_B64(inst);
|
||||
case Opcode::S_CMP_LT_U32:
|
||||
return S_CMP(ConditionOp::LT, false, inst);
|
||||
case Opcode::S_CMP_LE_U32:
|
||||
return S_CMP(ConditionOp::LE, false, inst);
|
||||
case Opcode::S_CMP_LG_U32:
|
||||
return S_CMP(ConditionOp::LG, false, inst);
|
||||
case Opcode::S_CMP_LT_I32:
|
||||
return S_CMP(ConditionOp::LT, true, inst);
|
||||
case Opcode::S_CMP_LG_I32:
|
||||
return S_CMP(ConditionOp::LG, true, inst);
|
||||
case Opcode::S_CMP_GT_I32:
|
||||
return S_CMP(ConditionOp::GT, true, inst);
|
||||
case Opcode::S_CMP_GE_I32:
|
||||
return S_CMP(ConditionOp::GE, true, inst);
|
||||
case Opcode::S_CMP_EQ_I32:
|
||||
return S_CMP(ConditionOp::EQ, true, inst);
|
||||
case Opcode::S_CMP_EQ_U32:
|
||||
return S_CMP(ConditionOp::EQ, false, inst);
|
||||
case Opcode::S_CMP_GE_U32:
|
||||
return S_CMP(ConditionOp::GE, false, inst);
|
||||
case Opcode::S_CMP_GT_U32:
|
||||
return S_CMP(ConditionOp::GT, false, inst);
|
||||
case Opcode::S_OR_B64:
|
||||
return S_OR_B64(NegateMode::None, false, inst);
|
||||
case Opcode::S_NOR_B64:
|
||||
return S_OR_B64(NegateMode::Result, false, inst);
|
||||
case Opcode::S_XOR_B64:
|
||||
return S_OR_B64(NegateMode::None, true, inst);
|
||||
case Opcode::S_ORN2_B64:
|
||||
return S_OR_B64(NegateMode::Src1, false, inst);
|
||||
case Opcode::S_AND_B64:
|
||||
return S_AND_B64(NegateMode::None, inst);
|
||||
case Opcode::S_NAND_B64:
|
||||
return S_AND_B64(NegateMode::Result, inst);
|
||||
case Opcode::S_ANDN2_B64:
|
||||
return S_AND_B64(NegateMode::Src1, inst);
|
||||
case Opcode::S_NOT_B64:
|
||||
return S_NOT_B64(inst);
|
||||
case Opcode::S_ADD_I32:
|
||||
return S_ADD_I32(inst);
|
||||
case Opcode::S_AND_B32:
|
||||
return S_AND_B32(inst);
|
||||
case Opcode::S_ASHR_I32:
|
||||
return S_ASHR_I32(inst);
|
||||
case Opcode::S_OR_B32:
|
||||
return S_OR_B32(inst);
|
||||
case Opcode::S_LSHL_B32:
|
||||
return S_LSHL_B32(inst);
|
||||
case Opcode::S_LSHR_B32:
|
||||
return S_LSHR_B32(inst);
|
||||
case Opcode::S_CSELECT_B32:
|
||||
return S_CSELECT_B32(inst);
|
||||
case Opcode::S_CSELECT_B64:
|
||||
return S_CSELECT_B64(inst);
|
||||
case Opcode::S_BFE_U32:
|
||||
return S_BFE_U32(inst);
|
||||
case Opcode::S_BFM_B32:
|
||||
return S_BFM_B32(inst);
|
||||
case Opcode::S_BREV_B32:
|
||||
return S_BREV_B32(inst);
|
||||
case Opcode::S_ADD_U32:
|
||||
return S_ADD_U32(inst);
|
||||
case Opcode::S_ADDC_U32:
|
||||
return S_ADDC_U32(inst);
|
||||
case Opcode::S_ADDK_I32:
|
||||
return S_ADDK_I32(inst);
|
||||
case Opcode::S_MULK_I32:
|
||||
return S_MULK_I32(inst);
|
||||
case Opcode::S_SUB_U32:
|
||||
case Opcode::S_SUB_I32:
|
||||
return S_SUB_U32(inst);
|
||||
case Opcode::S_MIN_U32:
|
||||
return S_MIN_U32(inst);
|
||||
case Opcode::S_MAX_U32:
|
||||
return S_MAX_U32(inst);
|
||||
case Opcode::S_WQM_B64:
|
||||
break;
|
||||
default:
|
||||
LogMissingOpcode(inst);
|
||||
}
|
||||
}
|
||||
|
||||
void Translator::S_MOVK(const GcnInst& inst) {
|
||||
const auto simm16 = inst.control.sopk.simm.Value();
|
||||
const auto simm16 = inst.control.sopk.simm;
|
||||
if (simm16 & (1 << 15)) {
|
||||
// TODO: need to verify the case of imm sign extension
|
||||
UNREACHABLE();
|
||||
|
@ -14,6 +108,16 @@ void Translator::S_MOVK(const GcnInst& inst) {
|
|||
SetDst(inst.dst[0], ir.Imm32(simm16));
|
||||
}
|
||||
|
||||
void Translator::S_ADDK_I32(const GcnInst& inst) {
|
||||
const s32 simm16 = inst.control.sopk.simm;
|
||||
SetDst(inst.dst[0], ir.IAdd(GetSrc(inst.dst[0]), ir.Imm32(simm16)));
|
||||
}
|
||||
|
||||
void Translator::S_MULK_I32(const GcnInst& inst) {
|
||||
const s32 simm16 = inst.control.sopk.simm;
|
||||
SetDst(inst.dst[0], ir.IMul(GetSrc(inst.dst[0]), ir.Imm32(simm16)));
|
||||
}
|
||||
|
||||
void Translator::S_MOV(const GcnInst& inst) {
|
||||
SetDst(inst.dst[0], GetSrc(inst.src[0]));
|
||||
}
|
||||
|
@ -62,15 +166,10 @@ void Translator::S_AND_SAVEEXEC_B64(const GcnInst& inst) {
|
|||
}
|
||||
}();
|
||||
|
||||
// Mark destination SPGR as an EXEC context. This means we will use 1-bit
|
||||
// IR instruction whenever it's loaded.
|
||||
switch (inst.dst[0].field) {
|
||||
case OperandField::ScalarGPR: {
|
||||
const u32 reg = inst.dst[0].code;
|
||||
exec_contexts[reg] = true;
|
||||
ir.SetThreadBitScalarReg(IR::ScalarReg(reg), exec);
|
||||
case OperandField::ScalarGPR:
|
||||
ir.SetThreadBitScalarReg(IR::ScalarReg(inst.dst[0].code), exec);
|
||||
break;
|
||||
}
|
||||
case OperandField::VccLo:
|
||||
ir.SetVcc(exec);
|
||||
break;
|
||||
|
@ -79,27 +178,37 @@ void Translator::S_AND_SAVEEXEC_B64(const GcnInst& inst) {
|
|||
}
|
||||
|
||||
// Update EXEC.
|
||||
ir.SetExec(ir.LogicalAnd(exec, src));
|
||||
const IR::U1 result = ir.LogicalAnd(exec, src);
|
||||
ir.SetExec(result);
|
||||
ir.SetScc(result);
|
||||
}
|
||||
|
||||
void Translator::S_MOV_B64(const GcnInst& inst) {
|
||||
// TODO: Using VCC as EXEC context.
|
||||
if (inst.src[0].field == OperandField::VccLo || inst.dst[0].field == OperandField::VccLo) {
|
||||
return;
|
||||
}
|
||||
if (inst.dst[0].field == OperandField::ScalarGPR && inst.src[0].field == OperandField::ExecLo) {
|
||||
// Exec context push
|
||||
exec_contexts[inst.dst[0].code] = true;
|
||||
ir.SetThreadBitScalarReg(IR::ScalarReg(inst.dst[0].code), ir.GetExec());
|
||||
} else if (inst.dst[0].field == OperandField::ExecLo &&
|
||||
inst.src[0].field == OperandField::ScalarGPR) {
|
||||
// Exec context pop
|
||||
exec_contexts[inst.src[0].code] = false;
|
||||
ir.SetExec(ir.GetThreadBitScalarReg(IR::ScalarReg(inst.src[0].code)));
|
||||
} else if (inst.dst[0].field == OperandField::ExecLo &&
|
||||
inst.src[0].field == OperandField::ConstZero) {
|
||||
ir.SetExec(ir.Imm1(false));
|
||||
} else {
|
||||
const IR::U1 src = [&] {
|
||||
switch (inst.src[0].field) {
|
||||
case OperandField::VccLo:
|
||||
return ir.GetVcc();
|
||||
case OperandField::ExecLo:
|
||||
return ir.GetExec();
|
||||
case OperandField::ScalarGPR:
|
||||
return ir.GetThreadBitScalarReg(IR::ScalarReg(inst.src[0].code));
|
||||
case OperandField::ConstZero:
|
||||
return ir.Imm1(false);
|
||||
default:
|
||||
UNREACHABLE();
|
||||
}
|
||||
}();
|
||||
switch (inst.dst[0].field) {
|
||||
case OperandField::ScalarGPR:
|
||||
ir.SetThreadBitScalarReg(IR::ScalarReg(inst.dst[0].code), src);
|
||||
break;
|
||||
case OperandField::ExecLo:
|
||||
ir.SetExec(src);
|
||||
break;
|
||||
case OperandField::VccLo:
|
||||
ir.SetVcc(src);
|
||||
break;
|
||||
default:
|
||||
UNREACHABLE();
|
||||
}
|
||||
}
|
||||
|
@ -338,4 +447,20 @@ void Translator::S_ADDC_U32(const GcnInst& inst) {
|
|||
SetDst(inst.dst[0], ir.IAdd(ir.IAdd(src0, src1), ir.GetSccLo()));
|
||||
}
|
||||
|
||||
void Translator::S_MAX_U32(const GcnInst& inst) {
|
||||
const IR::U32 src0{GetSrc(inst.src[0])};
|
||||
const IR::U32 src1{GetSrc(inst.src[1])};
|
||||
const IR::U32 result = ir.UMax(src0, src1);
|
||||
SetDst(inst.dst[0], result);
|
||||
ir.SetScc(ir.IEqual(result, src0));
|
||||
}
|
||||
|
||||
void Translator::S_MIN_U32(const GcnInst& inst) {
|
||||
const IR::U32 src0{GetSrc(inst.src[0])};
|
||||
const IR::U32 src1{GetSrc(inst.src[1])};
|
||||
const IR::U32 result = ir.UMin(src0, src1);
|
||||
SetDst(inst.dst[0], result);
|
||||
ir.SetScc(ir.IEqual(result, src0));
|
||||
}
|
||||
|
||||
} // namespace Shader::Gcn
|
||||
|
|
|
@ -7,6 +7,29 @@ namespace Shader::Gcn {
|
|||
|
||||
static constexpr u32 SQ_SRC_LITERAL = 0xFF;
|
||||
|
||||
void Translator::EmitScalarMemory(const GcnInst& inst) {
|
||||
switch (inst.opcode) {
|
||||
case Opcode::S_LOAD_DWORDX4:
|
||||
return S_LOAD_DWORD(4, inst);
|
||||
case Opcode::S_LOAD_DWORDX8:
|
||||
return S_LOAD_DWORD(8, inst);
|
||||
case Opcode::S_LOAD_DWORDX16:
|
||||
return S_LOAD_DWORD(16, inst);
|
||||
case Opcode::S_BUFFER_LOAD_DWORD:
|
||||
return S_BUFFER_LOAD_DWORD(1, inst);
|
||||
case Opcode::S_BUFFER_LOAD_DWORDX2:
|
||||
return S_BUFFER_LOAD_DWORD(2, inst);
|
||||
case Opcode::S_BUFFER_LOAD_DWORDX4:
|
||||
return S_BUFFER_LOAD_DWORD(4, inst);
|
||||
case Opcode::S_BUFFER_LOAD_DWORDX8:
|
||||
return S_BUFFER_LOAD_DWORD(8, inst);
|
||||
case Opcode::S_BUFFER_LOAD_DWORDX16:
|
||||
return S_BUFFER_LOAD_DWORD(16, inst);
|
||||
default:
|
||||
LogMissingOpcode(inst);
|
||||
}
|
||||
}
|
||||
|
||||
void Translator::S_LOAD_DWORD(int num_dwords, const GcnInst& inst) {
|
||||
const auto& smrd = inst.control.smrd;
|
||||
const u32 dword_offset = [&] -> u32 {
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -11,7 +11,8 @@
|
|||
|
||||
namespace Shader {
|
||||
struct Info;
|
||||
}
|
||||
struct Profile;
|
||||
} // namespace Shader
|
||||
|
||||
namespace Shader::Gcn {
|
||||
|
||||
|
@ -24,6 +25,7 @@ enum class ConditionOp : u32 {
|
|||
LT,
|
||||
LE,
|
||||
TRU,
|
||||
U,
|
||||
};
|
||||
|
||||
enum class AtomicOp : u32 {
|
||||
|
@ -53,10 +55,19 @@ enum class NegateMode : u32 {
|
|||
|
||||
class Translator {
|
||||
public:
|
||||
explicit Translator(IR::Block* block_, Info& info);
|
||||
explicit Translator(IR::Block* block_, Info& info, const Profile& profile);
|
||||
|
||||
// Instruction categories
|
||||
void EmitPrologue();
|
||||
void EmitFetch(const GcnInst& inst);
|
||||
void EmitDataShare(const GcnInst& inst);
|
||||
void EmitVectorInterpolation(const GcnInst& inst);
|
||||
void EmitScalarMemory(const GcnInst& inst);
|
||||
void EmitVectorMemory(const GcnInst& inst);
|
||||
void EmitExport(const GcnInst& inst);
|
||||
void EmitFlowControl(u32 pc, const GcnInst& inst);
|
||||
void EmitScalarAlu(const GcnInst& inst);
|
||||
void EmitVectorAlu(const GcnInst& inst);
|
||||
|
||||
// Scalar ALU
|
||||
void S_MOVK(const GcnInst& inst);
|
||||
|
@ -83,6 +94,10 @@ public:
|
|||
void S_SUB_U32(const GcnInst& inst);
|
||||
void S_GETPC_B64(u32 pc, const GcnInst& inst);
|
||||
void S_ADDC_U32(const GcnInst& inst);
|
||||
void S_MULK_I32(const GcnInst& inst);
|
||||
void S_ADDK_I32(const GcnInst& inst);
|
||||
void S_MAX_U32(const GcnInst& inst);
|
||||
void S_MIN_U32(const GcnInst& inst);
|
||||
|
||||
// Scalar Memory
|
||||
void S_LOAD_DWORD(int num_dwords, const GcnInst& inst);
|
||||
|
@ -94,12 +109,15 @@ public:
|
|||
void V_MAC_F32(const GcnInst& inst);
|
||||
void V_CVT_PKRTZ_F16_F32(const GcnInst& inst);
|
||||
void V_CVT_F32_F16(const GcnInst& inst);
|
||||
void V_CVT_F16_F32(const GcnInst& inst);
|
||||
void V_MUL_F32(const GcnInst& inst);
|
||||
void V_CNDMASK_B32(const GcnInst& inst);
|
||||
void V_OR_B32(bool is_xor, const GcnInst& inst);
|
||||
void V_AND_B32(const GcnInst& inst);
|
||||
void V_LSHLREV_B32(const GcnInst& inst);
|
||||
void V_LSHL_B32(const GcnInst& inst);
|
||||
void V_ADD_I32(const GcnInst& inst);
|
||||
void V_ADDC_U32(const GcnInst& inst);
|
||||
void V_CVT_F32_I32(const GcnInst& inst);
|
||||
void V_CVT_F32_U32(const GcnInst& inst);
|
||||
void V_MAD_F32(const GcnInst& inst);
|
||||
|
@ -121,6 +139,7 @@ public:
|
|||
void V_SQRT_F32(const GcnInst& inst);
|
||||
void V_MIN_F32(const GcnInst& inst, bool is_legacy = false);
|
||||
void V_MIN3_F32(const GcnInst& inst);
|
||||
void V_MIN3_I32(const GcnInst& inst);
|
||||
void V_MADMK_F32(const GcnInst& inst);
|
||||
void V_CUBEMA_F32(const GcnInst& inst);
|
||||
void V_CUBESC_F32(const GcnInst& inst);
|
||||
|
@ -129,6 +148,7 @@ public:
|
|||
void V_CVT_U32_F32(const GcnInst& inst);
|
||||
void V_SUBREV_F32(const GcnInst& inst);
|
||||
void V_SUBREV_I32(const GcnInst& inst);
|
||||
void V_MAD_U64_U32(const GcnInst& inst);
|
||||
void V_CMP_U32(ConditionOp op, bool is_signed, bool set_exec, const GcnInst& inst);
|
||||
void V_LSHRREV_B32(const GcnInst& inst);
|
||||
void V_MUL_HI_U32(bool is_signed, const GcnInst& inst);
|
||||
|
@ -144,6 +164,7 @@ public:
|
|||
void V_BCNT_U32_B32(const GcnInst& inst);
|
||||
void V_COS_F32(const GcnInst& inst);
|
||||
void V_MAX3_F32(const GcnInst& inst);
|
||||
void V_MAX3_U32(const GcnInst& inst);
|
||||
void V_CVT_I32_F32(const GcnInst& inst);
|
||||
void V_MIN_I32(const GcnInst& inst);
|
||||
void V_MUL_LO_U32(const GcnInst& inst);
|
||||
|
@ -158,6 +179,8 @@ public:
|
|||
void V_LDEXP_F32(const GcnInst& inst);
|
||||
void V_CVT_FLR_I32_F32(const GcnInst& inst);
|
||||
void V_CMP_CLASS_F32(const GcnInst& inst);
|
||||
void V_FFBL_B32(const GcnInst& inst);
|
||||
void V_MBCNT_U32_B32(bool is_low, const GcnInst& inst);
|
||||
|
||||
// Vector Memory
|
||||
void BUFFER_LOAD_FORMAT(u32 num_dwords, bool is_typed, bool is_format, const GcnInst& inst);
|
||||
|
@ -165,12 +188,15 @@ public:
|
|||
|
||||
// Vector interpolation
|
||||
void V_INTERP_P2_F32(const GcnInst& inst);
|
||||
void V_INTERP_MOV_F32(const GcnInst& inst);
|
||||
|
||||
// Data share
|
||||
void DS_SWIZZLE_B32(const GcnInst& inst);
|
||||
void DS_READ(int bit_size, bool is_signed, bool is_pair, const GcnInst& inst);
|
||||
void DS_WRITE(int bit_size, bool is_signed, bool is_pair, const GcnInst& inst);
|
||||
void V_READFIRSTLANE_B32(const GcnInst& inst);
|
||||
void V_READLANE_B32(const GcnInst& inst);
|
||||
void V_WRITELANE_B32(const GcnInst& inst);
|
||||
void S_BARRIER();
|
||||
|
||||
// MIMG
|
||||
|
@ -182,19 +208,25 @@ public:
|
|||
void IMAGE_GET_LOD(const GcnInst& inst);
|
||||
void IMAGE_ATOMIC(AtomicOp op, const GcnInst& inst);
|
||||
|
||||
// Export
|
||||
void EXP(const GcnInst& inst);
|
||||
|
||||
private:
|
||||
IR::U32F32 GetSrc(const InstOperand& operand, bool flt_zero = false);
|
||||
template <typename T = IR::U32F32>
|
||||
[[nodiscard]] T GetSrc(const InstOperand& operand, bool flt_zero = false);
|
||||
template <typename T = IR::U64F64>
|
||||
[[nodiscard]] T GetSrc64(const InstOperand& operand, bool flt_zero = false);
|
||||
void SetDst(const InstOperand& operand, const IR::U32F32& value);
|
||||
void SetDst64(const InstOperand& operand, const IR::U64F64& value_raw);
|
||||
|
||||
void LogMissingOpcode(const GcnInst& inst);
|
||||
|
||||
private:
|
||||
IR::IREmitter ir;
|
||||
Info& info;
|
||||
static std::array<bool, IR::NumScalarRegs> exec_contexts;
|
||||
const Profile& profile;
|
||||
IR::U32 m0_value;
|
||||
bool opcode_missing = false;
|
||||
};
|
||||
|
||||
void Translate(IR::Block* block, u32 block_base, std::span<const GcnInst> inst_list, Info& info);
|
||||
void Translate(IR::Block* block, u32 block_base, std::span<const GcnInst> inst_list, Info& info,
|
||||
const Profile& profile);
|
||||
|
||||
} // namespace Shader::Gcn
|
||||
|
|
|
@ -2,9 +2,311 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "shader_recompiler/frontend/translate/translate.h"
|
||||
#include "shader_recompiler/profile.h"
|
||||
|
||||
namespace Shader::Gcn {
|
||||
|
||||
void Translator::EmitVectorAlu(const GcnInst& inst) {
|
||||
switch (inst.opcode) {
|
||||
case Opcode::V_LSHLREV_B32:
|
||||
return V_LSHLREV_B32(inst);
|
||||
case Opcode::V_LSHL_B32:
|
||||
return V_LSHL_B32(inst);
|
||||
case Opcode::V_BFREV_B32:
|
||||
return V_BFREV_B32(inst);
|
||||
case Opcode::V_BFE_U32:
|
||||
return V_BFE_U32(false, inst);
|
||||
case Opcode::V_BFE_I32:
|
||||
return V_BFE_U32(true, inst);
|
||||
case Opcode::V_BFI_B32:
|
||||
return V_BFI_B32(inst);
|
||||
case Opcode::V_LSHR_B32:
|
||||
return V_LSHR_B32(inst);
|
||||
case Opcode::V_ASHRREV_I32:
|
||||
return V_ASHRREV_I32(inst);
|
||||
case Opcode::V_LSHRREV_B32:
|
||||
return V_LSHRREV_B32(inst);
|
||||
case Opcode::V_NOT_B32:
|
||||
return V_NOT_B32(inst);
|
||||
case Opcode::V_AND_B32:
|
||||
return V_AND_B32(inst);
|
||||
case Opcode::V_OR_B32:
|
||||
return V_OR_B32(false, inst);
|
||||
case Opcode::V_XOR_B32:
|
||||
return V_OR_B32(true, inst);
|
||||
case Opcode::V_FFBL_B32:
|
||||
return V_FFBL_B32(inst);
|
||||
|
||||
case Opcode::V_MOV_B32:
|
||||
return V_MOV(inst);
|
||||
case Opcode::V_ADD_I32:
|
||||
return V_ADD_I32(inst);
|
||||
case Opcode::V_ADDC_U32:
|
||||
return V_ADDC_U32(inst);
|
||||
case Opcode::V_CVT_F32_I32:
|
||||
return V_CVT_F32_I32(inst);
|
||||
case Opcode::V_CVT_F32_U32:
|
||||
return V_CVT_F32_U32(inst);
|
||||
case Opcode::V_CVT_PKRTZ_F16_F32:
|
||||
return V_CVT_PKRTZ_F16_F32(inst);
|
||||
case Opcode::V_CVT_F32_F16:
|
||||
return V_CVT_F32_F16(inst);
|
||||
case Opcode::V_CVT_F16_F32:
|
||||
return V_CVT_F16_F32(inst);
|
||||
case Opcode::V_CVT_F32_UBYTE0:
|
||||
return V_CVT_F32_UBYTE(0, inst);
|
||||
case Opcode::V_CVT_F32_UBYTE1:
|
||||
return V_CVT_F32_UBYTE(1, inst);
|
||||
case Opcode::V_CVT_F32_UBYTE2:
|
||||
return V_CVT_F32_UBYTE(2, inst);
|
||||
case Opcode::V_CVT_F32_UBYTE3:
|
||||
return V_CVT_F32_UBYTE(3, inst);
|
||||
case Opcode::V_CVT_OFF_F32_I4:
|
||||
return V_CVT_OFF_F32_I4(inst);
|
||||
case Opcode::V_MAD_U64_U32:
|
||||
return V_MAD_U64_U32(inst);
|
||||
case Opcode::V_CMP_GE_I32:
|
||||
return V_CMP_U32(ConditionOp::GE, true, false, inst);
|
||||
case Opcode::V_CMP_EQ_I32:
|
||||
return V_CMP_U32(ConditionOp::EQ, true, false, inst);
|
||||
case Opcode::V_CMP_LE_I32:
|
||||
return V_CMP_U32(ConditionOp::LE, true, false, inst);
|
||||
case Opcode::V_CMP_NE_I32:
|
||||
return V_CMP_U32(ConditionOp::LG, true, false, inst);
|
||||
case Opcode::V_CMP_NE_U32:
|
||||
return V_CMP_U32(ConditionOp::LG, false, false, inst);
|
||||
case Opcode::V_CMP_EQ_U32:
|
||||
return V_CMP_U32(ConditionOp::EQ, false, false, inst);
|
||||
case Opcode::V_CMP_F_U32:
|
||||
return V_CMP_U32(ConditionOp::F, false, false, inst);
|
||||
case Opcode::V_CMP_LT_U32:
|
||||
return V_CMP_U32(ConditionOp::LT, false, false, inst);
|
||||
case Opcode::V_CMP_GT_U32:
|
||||
return V_CMP_U32(ConditionOp::GT, false, false, inst);
|
||||
case Opcode::V_CMP_GE_U32:
|
||||
return V_CMP_U32(ConditionOp::GE, false, false, inst);
|
||||
case Opcode::V_CMP_TRU_U32:
|
||||
return V_CMP_U32(ConditionOp::TRU, false, false, inst);
|
||||
case Opcode::V_CMP_NEQ_F32:
|
||||
return V_CMP_F32(ConditionOp::LG, false, inst);
|
||||
case Opcode::V_CMP_F_F32:
|
||||
return V_CMP_F32(ConditionOp::F, false, inst);
|
||||
case Opcode::V_CMP_LT_F32:
|
||||
return V_CMP_F32(ConditionOp::LT, false, inst);
|
||||
case Opcode::V_CMP_EQ_F32:
|
||||
return V_CMP_F32(ConditionOp::EQ, false, inst);
|
||||
case Opcode::V_CMP_LE_F32:
|
||||
return V_CMP_F32(ConditionOp::LE, false, inst);
|
||||
case Opcode::V_CMP_GT_F32:
|
||||
return V_CMP_F32(ConditionOp::GT, false, inst);
|
||||
case Opcode::V_CMP_LG_F32:
|
||||
return V_CMP_F32(ConditionOp::LG, false, inst);
|
||||
case Opcode::V_CMP_GE_F32:
|
||||
return V_CMP_F32(ConditionOp::GE, false, inst);
|
||||
case Opcode::V_CMP_NLE_F32:
|
||||
return V_CMP_F32(ConditionOp::GT, false, inst);
|
||||
case Opcode::V_CMP_NLT_F32:
|
||||
return V_CMP_F32(ConditionOp::GE, false, inst);
|
||||
case Opcode::V_CMP_NGT_F32:
|
||||
return V_CMP_F32(ConditionOp::LE, false, inst);
|
||||
case Opcode::V_CMP_NGE_F32:
|
||||
return V_CMP_F32(ConditionOp::LT, false, inst);
|
||||
case Opcode::V_CMP_U_F32:
|
||||
return V_CMP_F32(ConditionOp::U, false, inst);
|
||||
case Opcode::V_CNDMASK_B32:
|
||||
return V_CNDMASK_B32(inst);
|
||||
case Opcode::V_MAX_I32:
|
||||
return V_MAX_U32(true, inst);
|
||||
case Opcode::V_MAX_U32:
|
||||
return V_MAX_U32(false, inst);
|
||||
case Opcode::V_MIN_I32:
|
||||
return V_MIN_I32(inst);
|
||||
case Opcode::V_CUBEMA_F32:
|
||||
return V_CUBEMA_F32(inst);
|
||||
case Opcode::V_CUBESC_F32:
|
||||
return V_CUBESC_F32(inst);
|
||||
case Opcode::V_CUBETC_F32:
|
||||
return V_CUBETC_F32(inst);
|
||||
case Opcode::V_CUBEID_F32:
|
||||
return V_CUBEID_F32(inst);
|
||||
case Opcode::V_CVT_U32_F32:
|
||||
return V_CVT_U32_F32(inst);
|
||||
case Opcode::V_CVT_I32_F32:
|
||||
return V_CVT_I32_F32(inst);
|
||||
case Opcode::V_CVT_FLR_I32_F32:
|
||||
return V_CVT_FLR_I32_F32(inst);
|
||||
case Opcode::V_SUBREV_I32:
|
||||
return V_SUBREV_I32(inst);
|
||||
case Opcode::V_MUL_HI_U32:
|
||||
return V_MUL_HI_U32(false, inst);
|
||||
case Opcode::V_MUL_LO_I32:
|
||||
return V_MUL_LO_U32(inst);
|
||||
case Opcode::V_SAD_U32:
|
||||
return V_SAD_U32(inst);
|
||||
case Opcode::V_SUB_I32:
|
||||
return V_SUB_I32(inst);
|
||||
case Opcode::V_MAD_I32_I24:
|
||||
return V_MAD_I32_I24(inst);
|
||||
case Opcode::V_MUL_I32_I24:
|
||||
case Opcode::V_MUL_U32_U24:
|
||||
return V_MUL_I32_I24(inst);
|
||||
case Opcode::V_MAD_U32_U24:
|
||||
return V_MAD_U32_U24(inst);
|
||||
case Opcode::V_BCNT_U32_B32:
|
||||
return V_BCNT_U32_B32(inst);
|
||||
case Opcode::V_MUL_LO_U32:
|
||||
return V_MUL_LO_U32(inst);
|
||||
case Opcode::V_MIN_U32:
|
||||
return V_MIN_U32(inst);
|
||||
case Opcode::V_CMP_NE_U64:
|
||||
return V_CMP_NE_U64(inst);
|
||||
case Opcode::V_READFIRSTLANE_B32:
|
||||
return V_READFIRSTLANE_B32(inst);
|
||||
case Opcode::V_READLANE_B32:
|
||||
return V_READLANE_B32(inst);
|
||||
case Opcode::V_WRITELANE_B32:
|
||||
return V_WRITELANE_B32(inst);
|
||||
|
||||
case Opcode::V_MAD_F32:
|
||||
return V_MAD_F32(inst);
|
||||
case Opcode::V_MAC_F32:
|
||||
return V_MAC_F32(inst);
|
||||
case Opcode::V_MUL_F32:
|
||||
return V_MUL_F32(inst);
|
||||
case Opcode::V_RCP_F32:
|
||||
return V_RCP_F32(inst);
|
||||
case Opcode::V_LDEXP_F32:
|
||||
return V_LDEXP_F32(inst);
|
||||
case Opcode::V_FRACT_F32:
|
||||
return V_FRACT_F32(inst);
|
||||
case Opcode::V_ADD_F32:
|
||||
return V_ADD_F32(inst);
|
||||
case Opcode::V_MED3_F32:
|
||||
return V_MED3_F32(inst);
|
||||
case Opcode::V_FLOOR_F32:
|
||||
return V_FLOOR_F32(inst);
|
||||
case Opcode::V_SUB_F32:
|
||||
return V_SUB_F32(inst);
|
||||
case Opcode::V_FMA_F32:
|
||||
case Opcode::V_MADAK_F32:
|
||||
return V_FMA_F32(inst);
|
||||
case Opcode::V_MAX_F32:
|
||||
return V_MAX_F32(inst);
|
||||
case Opcode::V_RSQ_F32:
|
||||
return V_RSQ_F32(inst);
|
||||
case Opcode::V_SIN_F32:
|
||||
return V_SIN_F32(inst);
|
||||
case Opcode::V_COS_F32:
|
||||
return V_COS_F32(inst);
|
||||
case Opcode::V_LOG_F32:
|
||||
return V_LOG_F32(inst);
|
||||
case Opcode::V_EXP_F32:
|
||||
return V_EXP_F32(inst);
|
||||
case Opcode::V_SQRT_F32:
|
||||
return V_SQRT_F32(inst);
|
||||
case Opcode::V_MIN_F32:
|
||||
return V_MIN_F32(inst, false);
|
||||
case Opcode::V_MIN3_F32:
|
||||
return V_MIN3_F32(inst);
|
||||
case Opcode::V_MIN3_I32:
|
||||
return V_MIN3_I32(inst);
|
||||
case Opcode::V_MIN_LEGACY_F32:
|
||||
return V_MIN_F32(inst, true);
|
||||
case Opcode::V_MADMK_F32:
|
||||
return V_MADMK_F32(inst);
|
||||
case Opcode::V_SUBREV_F32:
|
||||
return V_SUBREV_F32(inst);
|
||||
case Opcode::V_RNDNE_F32:
|
||||
return V_RNDNE_F32(inst);
|
||||
case Opcode::V_MAX3_F32:
|
||||
return V_MAX3_F32(inst);
|
||||
case Opcode::V_MAX3_U32:
|
||||
return V_MAX3_U32(inst);
|
||||
case Opcode::V_TRUNC_F32:
|
||||
return V_TRUNC_F32(inst);
|
||||
case Opcode::V_CEIL_F32:
|
||||
return V_CEIL_F32(inst);
|
||||
case Opcode::V_MUL_LEGACY_F32:
|
||||
return V_MUL_F32(inst);
|
||||
case Opcode::V_MAC_LEGACY_F32:
|
||||
return V_MAC_F32(inst);
|
||||
case Opcode::V_MAD_LEGACY_F32:
|
||||
return V_MAD_F32(inst);
|
||||
case Opcode::V_MAX_LEGACY_F32:
|
||||
return V_MAX_F32(inst, true);
|
||||
case Opcode::V_RSQ_LEGACY_F32:
|
||||
case Opcode::V_RSQ_CLAMP_F32:
|
||||
return V_RSQ_F32(inst);
|
||||
case Opcode::V_RCP_IFLAG_F32:
|
||||
return V_RCP_F32(inst);
|
||||
|
||||
case Opcode::V_CMPX_F_F32:
|
||||
return V_CMP_F32(ConditionOp::F, true, inst);
|
||||
case Opcode::V_CMPX_LT_F32:
|
||||
return V_CMP_F32(ConditionOp::LT, true, inst);
|
||||
case Opcode::V_CMPX_EQ_F32:
|
||||
return V_CMP_F32(ConditionOp::EQ, true, inst);
|
||||
case Opcode::V_CMPX_LE_F32:
|
||||
return V_CMP_F32(ConditionOp::LE, true, inst);
|
||||
case Opcode::V_CMPX_GT_F32:
|
||||
return V_CMP_F32(ConditionOp::GT, true, inst);
|
||||
case Opcode::V_CMPX_LG_F32:
|
||||
return V_CMP_F32(ConditionOp::LG, true, inst);
|
||||
case Opcode::V_CMPX_GE_F32:
|
||||
return V_CMP_F32(ConditionOp::GE, true, inst);
|
||||
case Opcode::V_CMPX_NGE_F32:
|
||||
return V_CMP_F32(ConditionOp::LT, true, inst);
|
||||
case Opcode::V_CMPX_NLG_F32:
|
||||
return V_CMP_F32(ConditionOp::EQ, true, inst);
|
||||
case Opcode::V_CMPX_NGT_F32:
|
||||
return V_CMP_F32(ConditionOp::LE, true, inst);
|
||||
case Opcode::V_CMPX_NLE_F32:
|
||||
return V_CMP_F32(ConditionOp::GT, true, inst);
|
||||
case Opcode::V_CMPX_NEQ_F32:
|
||||
return V_CMP_F32(ConditionOp::LG, true, inst);
|
||||
case Opcode::V_CMPX_NLT_F32:
|
||||
return V_CMP_F32(ConditionOp::GE, true, inst);
|
||||
case Opcode::V_CMPX_TRU_F32:
|
||||
return V_CMP_F32(ConditionOp::TRU, true, inst);
|
||||
case Opcode::V_CMP_CLASS_F32:
|
||||
return V_CMP_CLASS_F32(inst);
|
||||
|
||||
case Opcode::V_CMP_LE_U32:
|
||||
return V_CMP_U32(ConditionOp::LE, false, false, inst);
|
||||
case Opcode::V_CMP_GT_I32:
|
||||
return V_CMP_U32(ConditionOp::GT, true, false, inst);
|
||||
case Opcode::V_CMP_LT_I32:
|
||||
return V_CMP_U32(ConditionOp::LT, true, false, inst);
|
||||
case Opcode::V_CMPX_LT_I32:
|
||||
return V_CMP_U32(ConditionOp::LT, true, true, inst);
|
||||
case Opcode::V_CMPX_F_U32:
|
||||
return V_CMP_U32(ConditionOp::F, false, true, inst);
|
||||
case Opcode::V_CMPX_LT_U32:
|
||||
return V_CMP_U32(ConditionOp::LT, false, true, inst);
|
||||
case Opcode::V_CMPX_EQ_U32:
|
||||
return V_CMP_U32(ConditionOp::EQ, false, true, inst);
|
||||
case Opcode::V_CMPX_LE_U32:
|
||||
return V_CMP_U32(ConditionOp::LE, false, true, inst);
|
||||
case Opcode::V_CMPX_GT_U32:
|
||||
return V_CMP_U32(ConditionOp::GT, false, true, inst);
|
||||
case Opcode::V_CMPX_NE_U32:
|
||||
return V_CMP_U32(ConditionOp::LG, false, true, inst);
|
||||
case Opcode::V_CMPX_GE_U32:
|
||||
return V_CMP_U32(ConditionOp::GE, false, true, inst);
|
||||
case Opcode::V_CMPX_TRU_U32:
|
||||
return V_CMP_U32(ConditionOp::TRU, false, true, inst);
|
||||
case Opcode::V_CMPX_LG_I32:
|
||||
return V_CMP_U32(ConditionOp::LG, true, true, inst);
|
||||
|
||||
case Opcode::V_MBCNT_LO_U32_B32:
|
||||
return V_MBCNT_U32_B32(true, inst);
|
||||
case Opcode::V_MBCNT_HI_U32_B32:
|
||||
return V_MBCNT_U32_B32(false, inst);
|
||||
default:
|
||||
LogMissingOpcode(inst);
|
||||
}
|
||||
}
|
||||
|
||||
void Translator::V_MOV(const GcnInst& inst) {
|
||||
SetDst(inst.dst[0], GetSrc(inst.src[0]));
|
||||
}
|
||||
|
@ -32,6 +334,12 @@ void Translator::V_CVT_F32_F16(const GcnInst& inst) {
|
|||
SetDst(inst.dst[0], ir.FPConvert(32, ir.BitCast<IR::F16>(src0l)));
|
||||
}
|
||||
|
||||
void Translator::V_CVT_F16_F32(const GcnInst& inst) {
|
||||
const IR::F32 src0 = GetSrc(inst.src[0], true);
|
||||
const IR::F16 src0fp16 = ir.FPConvert(16, src0);
|
||||
SetDst(inst.dst[0], ir.UConvert(32, ir.BitCast<IR::U16>(src0fp16)));
|
||||
}
|
||||
|
||||
void Translator::V_MUL_F32(const GcnInst& inst) {
|
||||
SetDst(inst.dst[0], ir.FPMul(GetSrc(inst.src[0], true), GetSrc(inst.src[1], true)));
|
||||
}
|
||||
|
@ -67,7 +375,8 @@ void Translator::V_OR_B32(bool is_xor, const GcnInst& inst) {
|
|||
const IR::U32 src0{GetSrc(inst.src[0])};
|
||||
const IR::U32 src1{ir.GetVectorReg(IR::VectorReg(inst.src[1].code))};
|
||||
const IR::VectorReg dst_reg{inst.dst[0].code};
|
||||
ir.SetVectorReg(dst_reg, is_xor ? ir.BitwiseXor(src0, src1) : ir.BitwiseOr(src0, src1));
|
||||
ir.SetVectorReg(dst_reg,
|
||||
is_xor ? ir.BitwiseXor(src0, src1) : IR::U32(ir.BitwiseOr(src0, src1)));
|
||||
}
|
||||
|
||||
void Translator::V_AND_B32(const GcnInst& inst) {
|
||||
|
@ -84,6 +393,12 @@ void Translator::V_LSHLREV_B32(const GcnInst& inst) {
|
|||
ir.SetVectorReg(dst_reg, ir.ShiftLeftLogical(src1, ir.BitwiseAnd(src0, ir.Imm32(0x1F))));
|
||||
}
|
||||
|
||||
void Translator::V_LSHL_B32(const GcnInst& inst) {
|
||||
const IR::U32 src0{GetSrc(inst.src[0])};
|
||||
const IR::U32 src1{GetSrc(inst.src[1])};
|
||||
SetDst(inst.dst[0], ir.ShiftLeftLogical(src0, ir.BitwiseAnd(src1, ir.Imm32(0x1F))));
|
||||
}
|
||||
|
||||
void Translator::V_ADD_I32(const GcnInst& inst) {
|
||||
const IR::U32 src0{GetSrc(inst.src[0])};
|
||||
const IR::U32 src1{ir.GetVectorReg(IR::VectorReg(inst.src[1].code))};
|
||||
|
@ -92,6 +407,30 @@ void Translator::V_ADD_I32(const GcnInst& inst) {
|
|||
// TODO: Carry
|
||||
}
|
||||
|
||||
void Translator::V_ADDC_U32(const GcnInst& inst) {
|
||||
|
||||
const auto src0 = GetSrc<IR::U32>(inst.src[0]);
|
||||
const auto src1 = GetSrc<IR::U32>(inst.src[1]);
|
||||
|
||||
IR::U32 scarry;
|
||||
if (inst.src_count == 3) { // VOP3
|
||||
IR::U1 thread_bit{ir.GetThreadBitScalarReg(IR::ScalarReg(inst.src[2].code))};
|
||||
scarry = IR::U32{ir.Select(thread_bit, ir.Imm32(1), ir.Imm32(0))};
|
||||
} else { // VOP2
|
||||
scarry = ir.GetVccLo();
|
||||
}
|
||||
|
||||
const IR::U32 result = ir.IAdd(ir.IAdd(src0, src1), scarry);
|
||||
|
||||
const IR::VectorReg dst_reg{inst.dst[0].code};
|
||||
ir.SetVectorReg(dst_reg, result);
|
||||
|
||||
const IR::U1 less_src0 = ir.ILessThan(result, src0, false);
|
||||
const IR::U1 less_src1 = ir.ILessThan(result, src1, false);
|
||||
const IR::U1 did_overflow = ir.LogicalOr(less_src0, less_src1);
|
||||
ir.SetVcc(did_overflow);
|
||||
}
|
||||
|
||||
void Translator::V_CVT_F32_I32(const GcnInst& inst) {
|
||||
const IR::U32 src0{GetSrc(inst.src[0])};
|
||||
const IR::VectorReg dst_reg{inst.dst[0].code};
|
||||
|
@ -183,6 +522,8 @@ void Translator::V_CMP_F32(ConditionOp op, bool set_exec, const GcnInst& inst) {
|
|||
return ir.FPLessThanEqual(src0, src1);
|
||||
case ConditionOp::GE:
|
||||
return ir.FPGreaterThanEqual(src0, src1);
|
||||
case ConditionOp::U:
|
||||
return ir.LogicalNot(ir.LogicalAnd(ir.FPIsNan(src0), ir.FPIsNan(src1)));
|
||||
default:
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
@ -253,6 +594,13 @@ void Translator::V_MIN3_F32(const GcnInst& inst) {
|
|||
SetDst(inst.dst[0], ir.FPMin(src0, ir.FPMin(src1, src2)));
|
||||
}
|
||||
|
||||
void Translator::V_MIN3_I32(const GcnInst& inst) {
|
||||
const IR::U32 src0{GetSrc(inst.src[0])};
|
||||
const IR::U32 src1{GetSrc(inst.src[1])};
|
||||
const IR::U32 src2{GetSrc(inst.src[2])};
|
||||
SetDst(inst.dst[0], ir.SMin(src0, ir.SMin(src1, src2)));
|
||||
}
|
||||
|
||||
void Translator::V_MADMK_F32(const GcnInst& inst) {
|
||||
const IR::F32 src0{GetSrc(inst.src[0], true)};
|
||||
const IR::F32 src1{GetSrc(inst.src[1], true)};
|
||||
|
@ -294,6 +642,24 @@ void Translator::V_SUBREV_I32(const GcnInst& inst) {
|
|||
// TODO: Carry-out
|
||||
}
|
||||
|
||||
void Translator::V_MAD_U64_U32(const GcnInst& inst) {
|
||||
const auto src0 = GetSrc<IR::U32>(inst.src[0]);
|
||||
const auto src1 = GetSrc<IR::U32>(inst.src[1]);
|
||||
const auto src2 = GetSrc64<IR::U64>(inst.src[2]);
|
||||
|
||||
// const IR::U64 mul_result = ir.UConvert(64, ir.IMul(src0, src1));
|
||||
const IR::U64 mul_result =
|
||||
ir.PackUint2x32(ir.CompositeConstruct(ir.IMul(src0, src1), ir.Imm32(0U)));
|
||||
const IR::U64 sum_result = ir.IAdd(mul_result, src2);
|
||||
|
||||
SetDst64(inst.dst[0], sum_result);
|
||||
|
||||
const IR::U1 less_src0 = ir.ILessThan(sum_result, mul_result, false);
|
||||
const IR::U1 less_src1 = ir.ILessThan(sum_result, src2, false);
|
||||
const IR::U1 did_overflow = ir.LogicalOr(less_src0, less_src1);
|
||||
ir.SetVcc(did_overflow);
|
||||
}
|
||||
|
||||
void Translator::V_CMP_U32(ConditionOp op, bool is_signed, bool set_exec, const GcnInst& inst) {
|
||||
const IR::U32 src0{GetSrc(inst.src[0])};
|
||||
const IR::U32 src1{GetSrc(inst.src[1])};
|
||||
|
@ -421,6 +787,13 @@ void Translator::V_MAX3_F32(const GcnInst& inst) {
|
|||
SetDst(inst.dst[0], ir.FPMax(src0, ir.FPMax(src1, src2)));
|
||||
}
|
||||
|
||||
void Translator::V_MAX3_U32(const GcnInst& inst) {
|
||||
const IR::U32 src0{GetSrc(inst.src[0])};
|
||||
const IR::U32 src1{GetSrc(inst.src[1])};
|
||||
const IR::U32 src2{GetSrc(inst.src[2])};
|
||||
SetDst(inst.dst[0], ir.UMax(src0, ir.UMax(src1, src2)));
|
||||
}
|
||||
|
||||
void Translator::V_CVT_I32_F32(const GcnInst& inst) {
|
||||
const IR::F32 src0{GetSrc(inst.src[0], true)};
|
||||
SetDst(inst.dst[0], ir.ConvertFToS(32, src0));
|
||||
|
@ -519,38 +892,58 @@ void Translator::V_CVT_FLR_I32_F32(const GcnInst& inst) {
|
|||
}
|
||||
|
||||
void Translator::V_CMP_CLASS_F32(const GcnInst& inst) {
|
||||
constexpr u32 SIGNALING_NAN = 1 << 0;
|
||||
constexpr u32 QUIET_NAN = 1 << 1;
|
||||
constexpr u32 NEGATIVE_INFINITY = 1 << 2;
|
||||
constexpr u32 NEGATIVE_NORMAL = 1 << 3;
|
||||
constexpr u32 NEGATIVE_DENORM = 1 << 4;
|
||||
constexpr u32 NEGATIVE_ZERO = 1 << 5;
|
||||
constexpr u32 POSITIVE_ZERO = 1 << 6;
|
||||
constexpr u32 POSITIVE_DENORM = 1 << 7;
|
||||
constexpr u32 POSITIVE_NORMAL = 1 << 8;
|
||||
constexpr u32 POSITIVE_INFINITY = 1 << 9;
|
||||
|
||||
const IR::F32F64 src0{GetSrc(inst.src[0])};
|
||||
const IR::U32 src1{GetSrc(inst.src[1])};
|
||||
IR::U1 value;
|
||||
if (src1.IsImmediate()) {
|
||||
const u32 class_mask = src1.U32();
|
||||
IR::U1 value;
|
||||
if ((class_mask & (SIGNALING_NAN | QUIET_NAN)) == (SIGNALING_NAN | QUIET_NAN)) {
|
||||
const auto class_mask = static_cast<IR::FloatClassFunc>(src1.U32());
|
||||
if ((class_mask & IR::FloatClassFunc::NaN) == IR::FloatClassFunc::NaN) {
|
||||
value = ir.FPIsNan(src0);
|
||||
} else if ((class_mask & (POSITIVE_INFINITY | NEGATIVE_INFINITY)) ==
|
||||
(POSITIVE_INFINITY | NEGATIVE_INFINITY)) {
|
||||
} else if ((class_mask & IR::FloatClassFunc::Infinity) == IR::FloatClassFunc::Infinity) {
|
||||
value = ir.FPIsInf(src0);
|
||||
} else {
|
||||
UNREACHABLE();
|
||||
}
|
||||
if (inst.dst[1].field == OperandField::VccLo) {
|
||||
return ir.SetVcc(value);
|
||||
} else {
|
||||
UNREACHABLE();
|
||||
}
|
||||
} else {
|
||||
// We don't know the type yet, delay its resolution.
|
||||
value = ir.FPCmpClass32(src0, src1);
|
||||
}
|
||||
|
||||
switch (inst.dst[1].field) {
|
||||
case OperandField::VccLo:
|
||||
return ir.SetVcc(value);
|
||||
default:
|
||||
UNREACHABLE();
|
||||
}
|
||||
}
|
||||
|
||||
void Translator::V_FFBL_B32(const GcnInst& inst) {
|
||||
const IR::U32 src0{GetSrc(inst.src[0])};
|
||||
SetDst(inst.dst[0], ir.FindILsb(src0));
|
||||
}
|
||||
|
||||
void Translator::V_MBCNT_U32_B32(bool is_low, const GcnInst& inst) {
|
||||
const IR::U32 src0{GetSrc(inst.src[0])};
|
||||
const IR::U32 src1{GetSrc(inst.src[1])};
|
||||
const IR::U32 lane_id = ir.LaneId();
|
||||
|
||||
const auto [warp_half, mask_shift] = [&]() -> std::pair<IR::U32, IR::U32> {
|
||||
if (profile.subgroup_size == 32) {
|
||||
const IR::U32 warp_half = ir.BitwiseAnd(ir.WarpId(), ir.Imm32(1));
|
||||
return std::make_pair(warp_half, lane_id);
|
||||
}
|
||||
const IR::U32 warp_half = ir.ShiftRightLogical(lane_id, ir.Imm32(5));
|
||||
const IR::U32 mask_shift = ir.BitwiseAnd(lane_id, ir.Imm32(0x1F));
|
||||
return std::make_pair(warp_half, mask_shift);
|
||||
}();
|
||||
|
||||
const IR::U32 thread_mask = ir.ISub(ir.ShiftLeftLogical(ir.Imm32(1), mask_shift), ir.Imm32(1));
|
||||
const IR::U1 is_odd_warp = ir.INotEqual(warp_half, ir.Imm32(0));
|
||||
const IR::U32 mask = IR::U32{ir.Select(is_odd_warp, is_low ? ir.Imm32(~0U) : thread_mask,
|
||||
is_low ? thread_mask : ir.Imm32(0))};
|
||||
const IR::U32 masked_value = ir.BitwiseAnd(src0, mask);
|
||||
const IR::U32 result = ir.IAdd(src1, ir.BitCount(masked_value));
|
||||
SetDst(inst.dst[0], result);
|
||||
}
|
||||
|
||||
} // namespace Shader::Gcn
|
||||
|
|
|
@ -12,4 +12,24 @@ void Translator::V_INTERP_P2_F32(const GcnInst& inst) {
|
|||
ir.SetVectorReg(dst_reg, ir.GetAttribute(attrib, inst.control.vintrp.chan));
|
||||
}
|
||||
|
||||
void Translator::V_INTERP_MOV_F32(const GcnInst& inst) {
|
||||
const IR::VectorReg dst_reg{inst.dst[0].code};
|
||||
auto& attr = info.ps_inputs.at(inst.control.vintrp.attr);
|
||||
const IR::Attribute attrib{IR::Attribute::Param0 + attr.param_index};
|
||||
ir.SetVectorReg(dst_reg, ir.GetAttribute(attrib, inst.control.vintrp.chan));
|
||||
}
|
||||
|
||||
void Translator::EmitVectorInterpolation(const GcnInst& inst) {
|
||||
switch (inst.opcode) {
|
||||
case Opcode::V_INTERP_P1_F32:
|
||||
return;
|
||||
case Opcode::V_INTERP_P2_F32:
|
||||
return V_INTERP_P2_F32(inst);
|
||||
case Opcode::V_INTERP_MOV_F32:
|
||||
return V_INTERP_MOV_F32(inst);
|
||||
default:
|
||||
LogMissingOpcode(inst);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Shader::Gcn
|
||||
|
|
|
@ -5,9 +5,96 @@
|
|||
|
||||
namespace Shader::Gcn {
|
||||
|
||||
void Translator::EmitVectorMemory(const GcnInst& inst) {
|
||||
switch (inst.opcode) {
|
||||
case Opcode::IMAGE_SAMPLE_LZ_O:
|
||||
case Opcode::IMAGE_SAMPLE_O:
|
||||
case Opcode::IMAGE_SAMPLE_C:
|
||||
case Opcode::IMAGE_SAMPLE_C_LZ:
|
||||
case Opcode::IMAGE_SAMPLE_LZ:
|
||||
case Opcode::IMAGE_SAMPLE:
|
||||
case Opcode::IMAGE_SAMPLE_L:
|
||||
case Opcode::IMAGE_SAMPLE_C_O:
|
||||
case Opcode::IMAGE_SAMPLE_B:
|
||||
case Opcode::IMAGE_SAMPLE_C_LZ_O:
|
||||
return IMAGE_SAMPLE(inst);
|
||||
case Opcode::IMAGE_GATHER4_C:
|
||||
case Opcode::IMAGE_GATHER4_LZ:
|
||||
case Opcode::IMAGE_GATHER4_LZ_O:
|
||||
return IMAGE_GATHER(inst);
|
||||
case Opcode::IMAGE_ATOMIC_ADD:
|
||||
return IMAGE_ATOMIC(AtomicOp::Add, inst);
|
||||
case Opcode::IMAGE_ATOMIC_AND:
|
||||
return IMAGE_ATOMIC(AtomicOp::And, inst);
|
||||
case Opcode::IMAGE_ATOMIC_OR:
|
||||
return IMAGE_ATOMIC(AtomicOp::Or, inst);
|
||||
case Opcode::IMAGE_ATOMIC_XOR:
|
||||
return IMAGE_ATOMIC(AtomicOp::Xor, inst);
|
||||
case Opcode::IMAGE_ATOMIC_UMAX:
|
||||
return IMAGE_ATOMIC(AtomicOp::Umax, inst);
|
||||
case Opcode::IMAGE_ATOMIC_SMAX:
|
||||
return IMAGE_ATOMIC(AtomicOp::Smax, inst);
|
||||
case Opcode::IMAGE_ATOMIC_UMIN:
|
||||
return IMAGE_ATOMIC(AtomicOp::Umin, inst);
|
||||
case Opcode::IMAGE_ATOMIC_SMIN:
|
||||
return IMAGE_ATOMIC(AtomicOp::Smin, inst);
|
||||
case Opcode::IMAGE_ATOMIC_INC:
|
||||
return IMAGE_ATOMIC(AtomicOp::Inc, inst);
|
||||
case Opcode::IMAGE_ATOMIC_DEC:
|
||||
return IMAGE_ATOMIC(AtomicOp::Dec, inst);
|
||||
case Opcode::IMAGE_GET_LOD:
|
||||
return IMAGE_GET_LOD(inst);
|
||||
case Opcode::IMAGE_STORE:
|
||||
return IMAGE_STORE(inst);
|
||||
case Opcode::IMAGE_LOAD_MIP:
|
||||
return IMAGE_LOAD(true, inst);
|
||||
case Opcode::IMAGE_LOAD:
|
||||
return IMAGE_LOAD(false, inst);
|
||||
case Opcode::IMAGE_GET_RESINFO:
|
||||
return IMAGE_GET_RESINFO(inst);
|
||||
|
||||
case Opcode::TBUFFER_LOAD_FORMAT_X:
|
||||
return BUFFER_LOAD_FORMAT(1, true, true, inst);
|
||||
case Opcode::TBUFFER_LOAD_FORMAT_XY:
|
||||
return BUFFER_LOAD_FORMAT(2, true, true, inst);
|
||||
case Opcode::TBUFFER_LOAD_FORMAT_XYZ:
|
||||
return BUFFER_LOAD_FORMAT(3, true, true, inst);
|
||||
case Opcode::TBUFFER_LOAD_FORMAT_XYZW:
|
||||
return BUFFER_LOAD_FORMAT(4, true, true, inst);
|
||||
case Opcode::BUFFER_LOAD_FORMAT_X:
|
||||
return BUFFER_LOAD_FORMAT(1, false, true, inst);
|
||||
case Opcode::BUFFER_LOAD_FORMAT_XY:
|
||||
return BUFFER_LOAD_FORMAT(2, false, true, inst);
|
||||
case Opcode::BUFFER_LOAD_FORMAT_XYZ:
|
||||
return BUFFER_LOAD_FORMAT(3, false, true, inst);
|
||||
case Opcode::BUFFER_LOAD_FORMAT_XYZW:
|
||||
return BUFFER_LOAD_FORMAT(4, false, true, inst);
|
||||
case Opcode::BUFFER_LOAD_DWORD:
|
||||
return BUFFER_LOAD_FORMAT(1, false, false, inst);
|
||||
case Opcode::BUFFER_LOAD_DWORDX2:
|
||||
return BUFFER_LOAD_FORMAT(2, false, false, inst);
|
||||
case Opcode::BUFFER_LOAD_DWORDX3:
|
||||
return BUFFER_LOAD_FORMAT(3, false, false, inst);
|
||||
case Opcode::BUFFER_LOAD_DWORDX4:
|
||||
return BUFFER_LOAD_FORMAT(4, false, false, inst);
|
||||
case Opcode::BUFFER_STORE_FORMAT_X:
|
||||
case Opcode::BUFFER_STORE_DWORD:
|
||||
return BUFFER_STORE_FORMAT(1, false, inst);
|
||||
case Opcode::BUFFER_STORE_DWORDX2:
|
||||
return BUFFER_STORE_FORMAT(2, false, inst);
|
||||
case Opcode::BUFFER_STORE_DWORDX3:
|
||||
return BUFFER_STORE_FORMAT(3, false, inst);
|
||||
case Opcode::BUFFER_STORE_FORMAT_XYZW:
|
||||
case Opcode::BUFFER_STORE_DWORDX4:
|
||||
return BUFFER_STORE_FORMAT(4, false, inst);
|
||||
default:
|
||||
LogMissingOpcode(inst);
|
||||
}
|
||||
}
|
||||
|
||||
void Translator::IMAGE_GET_RESINFO(const GcnInst& inst) {
|
||||
IR::VectorReg dst_reg{inst.dst[0].code};
|
||||
const IR::ScalarReg tsharp_reg{inst.src[2].code};
|
||||
const IR::ScalarReg tsharp_reg{inst.src[2].code * 4};
|
||||
const auto flags = ImageResFlags(inst.control.mimg.dmask);
|
||||
const bool has_mips = flags.test(ImageResComponent::MipCount);
|
||||
const IR::U32 lod = ir.GetVectorReg(IR::VectorReg(inst.src[0].code));
|
||||
|
@ -157,7 +244,7 @@ void Translator::IMAGE_GATHER(const GcnInst& inst) {
|
|||
info.has_bias.Assign(flags.test(MimgModifier::LodBias));
|
||||
info.has_lod_clamp.Assign(flags.test(MimgModifier::LodClamp));
|
||||
info.force_level0.Assign(flags.test(MimgModifier::Level0));
|
||||
info.explicit_lod.Assign(explicit_lod);
|
||||
// info.explicit_lod.Assign(explicit_lod);
|
||||
info.gather_comp.Assign(std::bit_width(mimg.dmask) - 1);
|
||||
|
||||
// Issue IR instruction, leaving unknown fields blank to patch later.
|
||||
|
|
|
@ -12,16 +12,16 @@
|
|||
namespace Shader::IR {
|
||||
|
||||
template <typename Pred>
|
||||
auto BreadthFirstSearch(const Value& value, Pred&& pred)
|
||||
-> std::invoke_result_t<Pred, const Inst*> {
|
||||
if (value.IsImmediate()) {
|
||||
// Nothing to do with immediates
|
||||
return std::nullopt;
|
||||
auto BreadthFirstSearch(const Inst* inst, Pred&& pred) -> std::invoke_result_t<Pred, const Inst*> {
|
||||
// Most often case the instruction is the desired already.
|
||||
if (const std::optional result = pred(inst)) {
|
||||
return result;
|
||||
}
|
||||
|
||||
// Breadth-first search visiting the right most arguments first
|
||||
boost::container::small_vector<const Inst*, 2> visited;
|
||||
std::queue<const Inst*> queue;
|
||||
queue.push(value.InstRecursive());
|
||||
queue.push(inst);
|
||||
|
||||
while (!queue.empty()) {
|
||||
// Pop one instruction from the queue
|
||||
|
@ -49,4 +49,14 @@ auto BreadthFirstSearch(const Value& value, Pred&& pred)
|
|||
return std::nullopt;
|
||||
}
|
||||
|
||||
template <typename Pred>
|
||||
auto BreadthFirstSearch(const Value& value, Pred&& pred)
|
||||
-> std::invoke_result_t<Pred, const Inst*> {
|
||||
if (value.IsImmediate()) {
|
||||
// Nothing to do with immediates
|
||||
return std::nullopt;
|
||||
}
|
||||
return BreadthFirstSearch(value.InstRecursive(), pred);
|
||||
}
|
||||
|
||||
} // namespace Shader::IR
|
||||
|
|
|
@ -2,14 +2,19 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <bit>
|
||||
#include <source_location>
|
||||
#include "shader_recompiler/exception.h"
|
||||
#include "shader_recompiler/ir/ir_emitter.h"
|
||||
#include "shader_recompiler/ir/value.h"
|
||||
|
||||
namespace Shader::IR {
|
||||
namespace {
|
||||
[[noreturn]] void ThrowInvalidType(Type type) {
|
||||
UNREACHABLE_MSG("Invalid type {}", u32(type));
|
||||
[[noreturn]] void ThrowInvalidType(Type type,
|
||||
std::source_location loc = std::source_location::current()) {
|
||||
const std::string functionName = loc.function_name();
|
||||
const int lineNumber = loc.line();
|
||||
UNREACHABLE_MSG("Invalid type = {}, functionName = {}, line = {}", u32(type), functionName,
|
||||
lineNumber);
|
||||
}
|
||||
|
||||
Value MakeLodClampPair(IREmitter& ir, const F32& bias_lod, const F32& lod_clamp) {
|
||||
|
@ -273,7 +278,7 @@ Value IREmitter::LoadShared(int bit_size, bool is_signed, const U32& offset) {
|
|||
case 32:
|
||||
return Inst<U32>(Opcode::LoadSharedU32, offset);
|
||||
case 64:
|
||||
return Inst<U64>(Opcode::LoadSharedU64, offset);
|
||||
return Inst(Opcode::LoadSharedU64, offset);
|
||||
case 128:
|
||||
return Inst(Opcode::LoadSharedU128, offset);
|
||||
default:
|
||||
|
@ -368,6 +373,10 @@ U32 IREmitter::LaneId() {
|
|||
return Inst<U32>(Opcode::LaneId);
|
||||
}
|
||||
|
||||
U32 IREmitter::WarpId() {
|
||||
return Inst<U32>(Opcode::WarpId);
|
||||
}
|
||||
|
||||
U32 IREmitter::QuadShuffle(const U32& value, const U32& index) {
|
||||
return Inst<U32>(Opcode::QuadShuffle, value, index);
|
||||
}
|
||||
|
@ -871,6 +880,10 @@ U1 IREmitter::FPIsInf(const F32F64& value) {
|
|||
}
|
||||
}
|
||||
|
||||
U1 IREmitter::FPCmpClass32(const F32& value, const U32& op) {
|
||||
return Inst<U1>(Opcode::FPCmpClass32, value, op);
|
||||
}
|
||||
|
||||
U1 IREmitter::FPOrdered(const F32F64& lhs, const F32F64& rhs) {
|
||||
if (lhs.Type() != rhs.Type()) {
|
||||
UNREACHABLE_MSG("Mismatching types {} and {}", lhs.Type(), rhs.Type());
|
||||
|
@ -964,8 +977,18 @@ IR::Value IREmitter::IMulExt(const U32& a, const U32& b, bool is_signed) {
|
|||
return Inst(is_signed ? Opcode::SMulExt : Opcode::UMulExt, a, b);
|
||||
}
|
||||
|
||||
U32 IREmitter::IMul(const U32& a, const U32& b) {
|
||||
return Inst<U32>(Opcode::IMul32, a, b);
|
||||
U32U64 IREmitter::IMul(const U32U64& a, const U32U64& b) {
|
||||
if (a.Type() != b.Type()) {
|
||||
UNREACHABLE_MSG("Mismatching types {} and {}", a.Type(), b.Type());
|
||||
}
|
||||
switch (a.Type()) {
|
||||
case Type::U32:
|
||||
return Inst<U32>(Opcode::IMul32, a, b);
|
||||
case Type::U64:
|
||||
return Inst<U64>(Opcode::IMul64, a, b);
|
||||
default:
|
||||
ThrowInvalidType(a.Type());
|
||||
}
|
||||
}
|
||||
|
||||
U32 IREmitter::IDiv(const U32& a, const U32& b, bool is_signed) {
|
||||
|
@ -1024,8 +1047,18 @@ U32 IREmitter::BitwiseAnd(const U32& a, const U32& b) {
|
|||
return Inst<U32>(Opcode::BitwiseAnd32, a, b);
|
||||
}
|
||||
|
||||
U32 IREmitter::BitwiseOr(const U32& a, const U32& b) {
|
||||
return Inst<U32>(Opcode::BitwiseOr32, a, b);
|
||||
U32U64 IREmitter::BitwiseOr(const U32U64& a, const U32U64& b) {
|
||||
if (a.Type() != b.Type()) {
|
||||
UNREACHABLE_MSG("Mismatching types {} and {}", a.Type(), b.Type());
|
||||
}
|
||||
switch (a.Type()) {
|
||||
case Type::U32:
|
||||
return Inst<U32>(Opcode::BitwiseOr32, a, b);
|
||||
case Type::U64:
|
||||
return Inst<U64>(Opcode::BitwiseOr64, a, b);
|
||||
default:
|
||||
ThrowInvalidType(a.Type());
|
||||
}
|
||||
}
|
||||
|
||||
U32 IREmitter::BitwiseXor(const U32& a, const U32& b) {
|
||||
|
@ -1063,6 +1096,10 @@ U32 IREmitter::FindUMsb(const U32& value) {
|
|||
return Inst<U32>(Opcode::FindUMsb32, value);
|
||||
}
|
||||
|
||||
U32 IREmitter::FindILsb(const U32& value) {
|
||||
return Inst<U32>(Opcode::FindILsb32, value);
|
||||
}
|
||||
|
||||
U32 IREmitter::SMin(const U32& a, const U32& b) {
|
||||
return Inst<U32>(Opcode::SMin32, a, b);
|
||||
}
|
||||
|
@ -1095,8 +1132,18 @@ U32 IREmitter::UClamp(const U32& value, const U32& min, const U32& max) {
|
|||
return Inst<U32>(Opcode::UClamp32, value, min, max);
|
||||
}
|
||||
|
||||
U1 IREmitter::ILessThan(const U32& lhs, const U32& rhs, bool is_signed) {
|
||||
return Inst<U1>(is_signed ? Opcode::SLessThan : Opcode::ULessThan, lhs, rhs);
|
||||
U1 IREmitter::ILessThan(const U32U64& lhs, const U32U64& rhs, bool is_signed) {
|
||||
if (lhs.Type() != rhs.Type()) {
|
||||
UNREACHABLE_MSG("Mismatching types {} and {}", lhs.Type(), rhs.Type());
|
||||
}
|
||||
switch (lhs.Type()) {
|
||||
case Type::U32:
|
||||
return Inst<U1>(is_signed ? Opcode::SLessThan32 : Opcode::ULessThan32, lhs, rhs);
|
||||
case Type::U64:
|
||||
return Inst<U1>(is_signed ? Opcode::SLessThan64 : Opcode::ULessThan64, lhs, rhs);
|
||||
default:
|
||||
ThrowInvalidType(lhs.Type());
|
||||
}
|
||||
}
|
||||
|
||||
U1 IREmitter::IEqual(const U32U64& lhs, const U32U64& rhs) {
|
||||
|
@ -1155,8 +1202,9 @@ U32U64 IREmitter::ConvertFToS(size_t bitsize, const F32F64& value) {
|
|||
ThrowInvalidType(value.Type());
|
||||
}
|
||||
default:
|
||||
UNREACHABLE_MSG("Invalid destination bitsize {}", bitsize);
|
||||
break;
|
||||
}
|
||||
throw NotImplementedException("Invalid destination bitsize {}", bitsize);
|
||||
}
|
||||
|
||||
U32U64 IREmitter::ConvertFToU(size_t bitsize, const F32F64& value) {
|
||||
|
@ -1183,13 +1231,17 @@ F32F64 IREmitter::ConvertSToF(size_t dest_bitsize, size_t src_bitsize, const Val
|
|||
switch (src_bitsize) {
|
||||
case 32:
|
||||
return Inst<F32>(Opcode::ConvertF32S32, value);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 64:
|
||||
switch (src_bitsize) {
|
||||
case 32:
|
||||
return Inst<F64>(Opcode::ConvertF64S32, value);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
UNREACHABLE_MSG("Invalid bit size combination dst={} src={}", dest_bitsize, src_bitsize);
|
||||
|
@ -1203,13 +1255,17 @@ F32F64 IREmitter::ConvertUToF(size_t dest_bitsize, size_t src_bitsize, const Val
|
|||
return Inst<F32>(Opcode::ConvertF32U16, value);
|
||||
case 32:
|
||||
return Inst<F32>(Opcode::ConvertF32U32, value);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 64:
|
||||
switch (src_bitsize) {
|
||||
case 32:
|
||||
return Inst<F64>(Opcode::ConvertF64U32, value);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
UNREACHABLE_MSG("Invalid bit size combination dst={} src={}", dest_bitsize, src_bitsize);
|
||||
|
@ -1227,7 +1283,16 @@ U16U32U64 IREmitter::UConvert(size_t result_bitsize, const U16U32U64& value) {
|
|||
switch (value.Type()) {
|
||||
case Type::U32:
|
||||
return Inst<U16>(Opcode::ConvertU16U32, value);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
case 32:
|
||||
switch (value.Type()) {
|
||||
case Type::U16:
|
||||
return Inst<U32>(Opcode::ConvertU32U16, value);
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
throw NotImplementedException("Conversion from {} to {} bits", value.Type(), result_bitsize);
|
||||
}
|
||||
|
@ -1238,13 +1303,17 @@ F16F32F64 IREmitter::FPConvert(size_t result_bitsize, const F16F32F64& value) {
|
|||
switch (value.Type()) {
|
||||
case Type::F32:
|
||||
return Inst<F16>(Opcode::ConvertF16F32, value);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 32:
|
||||
switch (value.Type()) {
|
||||
case Type::F16:
|
||||
return Inst<F32>(Opcode::ConvertF32F16, value);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
throw NotImplementedException("Conversion from {} to {} bits", value.Type(), result_bitsize);
|
||||
|
|
|
@ -95,6 +95,7 @@ public:
|
|||
BufferInstInfo info);
|
||||
|
||||
[[nodiscard]] U32 LaneId();
|
||||
[[nodiscard]] U32 WarpId();
|
||||
[[nodiscard]] U32 QuadShuffle(const U32& value, const U32& index);
|
||||
|
||||
[[nodiscard]] Value CompositeConstruct(const Value& e1, const Value& e2);
|
||||
|
@ -150,6 +151,7 @@ public:
|
|||
[[nodiscard]] U1 FPGreaterThan(const F32F64& lhs, const F32F64& rhs, bool ordered = true);
|
||||
[[nodiscard]] U1 FPIsNan(const F32F64& value);
|
||||
[[nodiscard]] U1 FPIsInf(const F32F64& value);
|
||||
[[nodiscard]] U1 FPCmpClass32(const F32& value, const U32& op);
|
||||
[[nodiscard]] U1 FPOrdered(const F32F64& lhs, const F32F64& rhs);
|
||||
[[nodiscard]] U1 FPUnordered(const F32F64& lhs, const F32F64& rhs);
|
||||
[[nodiscard]] F32F64 FPMax(const F32F64& lhs, const F32F64& rhs, bool is_legacy = false);
|
||||
|
@ -159,7 +161,7 @@ public:
|
|||
[[nodiscard]] Value IAddCary(const U32& a, const U32& b);
|
||||
[[nodiscard]] U32U64 ISub(const U32U64& a, const U32U64& b);
|
||||
[[nodiscard]] Value IMulExt(const U32& a, const U32& b, bool is_signed = false);
|
||||
[[nodiscard]] U32 IMul(const U32& a, const U32& b);
|
||||
[[nodiscard]] U32U64 IMul(const U32U64& a, const U32U64& b);
|
||||
[[nodiscard]] U32 IDiv(const U32& a, const U32& b, bool is_signed = false);
|
||||
[[nodiscard]] U32U64 INeg(const U32U64& value);
|
||||
[[nodiscard]] U32 IAbs(const U32& value);
|
||||
|
@ -167,7 +169,7 @@ public:
|
|||
[[nodiscard]] U32U64 ShiftRightLogical(const U32U64& base, const U32& shift);
|
||||
[[nodiscard]] U32U64 ShiftRightArithmetic(const U32U64& base, const U32& shift);
|
||||
[[nodiscard]] U32 BitwiseAnd(const U32& a, const U32& b);
|
||||
[[nodiscard]] U32 BitwiseOr(const U32& a, const U32& b);
|
||||
[[nodiscard]] U32U64 BitwiseOr(const U32U64& a, const U32U64& b);
|
||||
[[nodiscard]] U32 BitwiseXor(const U32& a, const U32& b);
|
||||
[[nodiscard]] U32 BitFieldInsert(const U32& base, const U32& insert, const U32& offset,
|
||||
const U32& count);
|
||||
|
@ -179,6 +181,7 @@ public:
|
|||
|
||||
[[nodiscard]] U32 FindSMsb(const U32& value);
|
||||
[[nodiscard]] U32 FindUMsb(const U32& value);
|
||||
[[nodiscard]] U32 FindILsb(const U32& value);
|
||||
[[nodiscard]] U32 SMin(const U32& a, const U32& b);
|
||||
[[nodiscard]] U32 UMin(const U32& a, const U32& b);
|
||||
[[nodiscard]] U32 IMin(const U32& a, const U32& b, bool is_signed);
|
||||
|
@ -188,7 +191,7 @@ public:
|
|||
[[nodiscard]] U32 SClamp(const U32& value, const U32& min, const U32& max);
|
||||
[[nodiscard]] U32 UClamp(const U32& value, const U32& min, const U32& max);
|
||||
|
||||
[[nodiscard]] U1 ILessThan(const U32& lhs, const U32& rhs, bool is_signed);
|
||||
[[nodiscard]] U1 ILessThan(const U32U64& lhs, const U32U64& rhs, bool is_signed);
|
||||
[[nodiscard]] U1 IEqual(const U32U64& lhs, const U32U64& rhs);
|
||||
[[nodiscard]] U1 ILessThanEqual(const U32& lhs, const U32& rhs, bool is_signed);
|
||||
[[nodiscard]] U1 IGreaterThan(const U32& lhs, const U32& rhs, bool is_signed);
|
||||
|
|
|
@ -219,6 +219,7 @@ OPCODE(FPIsNan32, U1, F32,
|
|||
OPCODE(FPIsNan64, U1, F64, )
|
||||
OPCODE(FPIsInf32, U1, F32, )
|
||||
OPCODE(FPIsInf64, U1, F64, )
|
||||
OPCODE(FPCmpClass32, U1, F32, U32 )
|
||||
|
||||
// Integer operations
|
||||
OPCODE(IAdd32, U32, U32, U32, )
|
||||
|
@ -227,6 +228,7 @@ OPCODE(IAddCary32, U32x2, U32,
|
|||
OPCODE(ISub32, U32, U32, U32, )
|
||||
OPCODE(ISub64, U64, U64, U64, )
|
||||
OPCODE(IMul32, U32, U32, U32, )
|
||||
OPCODE(IMul64, U64, U64, U64, )
|
||||
OPCODE(SMulExt, U32x2, U32, U32, )
|
||||
OPCODE(UMulExt, U32x2, U32, U32, )
|
||||
OPCODE(SDiv32, U32, U32, U32, )
|
||||
|
@ -242,6 +244,7 @@ OPCODE(ShiftRightArithmetic32, U32, U32,
|
|||
OPCODE(ShiftRightArithmetic64, U64, U64, U32, )
|
||||
OPCODE(BitwiseAnd32, U32, U32, U32, )
|
||||
OPCODE(BitwiseOr32, U32, U32, U32, )
|
||||
OPCODE(BitwiseOr64, U64, U64, U64, )
|
||||
OPCODE(BitwiseXor32, U32, U32, U32, )
|
||||
OPCODE(BitFieldInsert, U32, U32, U32, U32, U32, )
|
||||
OPCODE(BitFieldSExtract, U32, U32, U32, U32, )
|
||||
|
@ -252,14 +255,17 @@ OPCODE(BitwiseNot32, U32, U32,
|
|||
|
||||
OPCODE(FindSMsb32, U32, U32, )
|
||||
OPCODE(FindUMsb32, U32, U32, )
|
||||
OPCODE(FindILsb32, U32, U32, )
|
||||
OPCODE(SMin32, U32, U32, U32, )
|
||||
OPCODE(UMin32, U32, U32, U32, )
|
||||
OPCODE(SMax32, U32, U32, U32, )
|
||||
OPCODE(UMax32, U32, U32, U32, )
|
||||
OPCODE(SClamp32, U32, U32, U32, U32, )
|
||||
OPCODE(UClamp32, U32, U32, U32, U32, )
|
||||
OPCODE(SLessThan, U1, U32, U32, )
|
||||
OPCODE(ULessThan, U1, U32, U32, )
|
||||
OPCODE(SLessThan32, U1, U32, U32, )
|
||||
OPCODE(SLessThan64, U1, U64, U64, )
|
||||
OPCODE(ULessThan32, U1, U32, U32, )
|
||||
OPCODE(ULessThan64, U1, U64, U64, )
|
||||
OPCODE(IEqual, U1, U32, U32, )
|
||||
OPCODE(SLessThanEqual, U1, U32, U32, )
|
||||
OPCODE(ULessThanEqual, U1, U32, U32, )
|
||||
|
@ -289,6 +295,7 @@ OPCODE(ConvertF64S32, F64, U32,
|
|||
OPCODE(ConvertF64U32, F64, U32, )
|
||||
OPCODE(ConvertF32U16, F32, U16, )
|
||||
OPCODE(ConvertU16U32, U16, U32, )
|
||||
OPCODE(ConvertU32U16, U32, U16, )
|
||||
|
||||
// Image operations
|
||||
OPCODE(ImageSampleImplicitLod, F32x4, Opaque, Opaque, Opaque, Opaque, )
|
||||
|
@ -319,4 +326,5 @@ OPCODE(ImageAtomicExchange32, U32, Opaq
|
|||
|
||||
// Warp operations
|
||||
OPCODE(LaneId, U32, )
|
||||
OPCODE(WarpId, U32, )
|
||||
OPCODE(QuadShuffle, U32, U32, U32 )
|
||||
|
|
|
@ -21,6 +21,8 @@ template <typename T>
|
|||
return value.F32();
|
||||
} else if constexpr (std::is_same_v<T, u64>) {
|
||||
return value.U64();
|
||||
} else if constexpr (std::is_same_v<T, s64>) {
|
||||
return static_cast<s64>(value.U64());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -236,6 +238,18 @@ void FoldBooleanConvert(IR::Inst& inst) {
|
|||
}
|
||||
}
|
||||
|
||||
void FoldCmpClass(IR::Inst& inst) {
|
||||
ASSERT_MSG(inst.Arg(1).IsImmediate(), "Unable to resolve compare operation");
|
||||
const auto class_mask = static_cast<IR::FloatClassFunc>(inst.Arg(1).U32());
|
||||
if ((class_mask & IR::FloatClassFunc::NaN) == IR::FloatClassFunc::NaN) {
|
||||
inst.ReplaceOpcode(IR::Opcode::FPIsNan32);
|
||||
} else if ((class_mask & IR::FloatClassFunc::Infinity) == IR::FloatClassFunc::Infinity) {
|
||||
inst.ReplaceOpcode(IR::Opcode::FPIsInf32);
|
||||
} else {
|
||||
UNREACHABLE();
|
||||
}
|
||||
}
|
||||
|
||||
void ConstantPropagation(IR::Block& block, IR::Inst& inst) {
|
||||
switch (inst.GetOpcode()) {
|
||||
case IR::Opcode::IAdd32:
|
||||
|
@ -249,6 +263,9 @@ void ConstantPropagation(IR::Block& block, IR::Inst& inst) {
|
|||
case IR::Opcode::IMul32:
|
||||
FoldWhenAllImmediates(inst, [](u32 a, u32 b) { return a * b; });
|
||||
return;
|
||||
case IR::Opcode::FPCmpClass32:
|
||||
FoldCmpClass(inst);
|
||||
return;
|
||||
case IR::Opcode::ShiftRightArithmetic32:
|
||||
FoldWhenAllImmediates(inst, [](s32 a, s32 b) { return static_cast<u32>(a >> b); });
|
||||
return;
|
||||
|
@ -281,12 +298,18 @@ void ConstantPropagation(IR::Block& block, IR::Inst& inst) {
|
|||
return FoldLogicalOr(inst);
|
||||
case IR::Opcode::LogicalNot:
|
||||
return FoldLogicalNot(inst);
|
||||
case IR::Opcode::SLessThan:
|
||||
case IR::Opcode::SLessThan32:
|
||||
FoldWhenAllImmediates(inst, [](s32 a, s32 b) { return a < b; });
|
||||
return;
|
||||
case IR::Opcode::ULessThan:
|
||||
case IR::Opcode::SLessThan64:
|
||||
FoldWhenAllImmediates(inst, [](s64 a, s64 b) { return a < b; });
|
||||
return;
|
||||
case IR::Opcode::ULessThan32:
|
||||
FoldWhenAllImmediates(inst, [](u32 a, u32 b) { return a < b; });
|
||||
return;
|
||||
case IR::Opcode::ULessThan64:
|
||||
FoldWhenAllImmediates(inst, [](u64 a, u64 b) { return a < b; });
|
||||
return;
|
||||
case IR::Opcode::SLessThanEqual:
|
||||
FoldWhenAllImmediates(inst, [](s32 a, s32 b) { return a <= b; });
|
||||
return;
|
||||
|
|
|
@ -2,7 +2,6 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <algorithm>
|
||||
#include <deque>
|
||||
#include <boost/container/small_vector.hpp>
|
||||
#include "shader_recompiler/ir/basic_block.h"
|
||||
#include "shader_recompiler/ir/breadth_first_search.h"
|
||||
|
@ -273,9 +272,18 @@ std::pair<const IR::Inst*, bool> TryDisableAnisoLod0(const IR::Inst* inst) {
|
|||
}
|
||||
|
||||
SharpLocation TrackSharp(const IR::Inst* inst) {
|
||||
while (inst->GetOpcode() == IR::Opcode::Phi) {
|
||||
inst = inst->Arg(0).InstRecursive();
|
||||
}
|
||||
// Search until we find a potential sharp source.
|
||||
const auto pred0 = [](const IR::Inst* inst) -> std::optional<const IR::Inst*> {
|
||||
if (inst->GetOpcode() == IR::Opcode::GetUserData ||
|
||||
inst->GetOpcode() == IR::Opcode::ReadConst) {
|
||||
return inst;
|
||||
}
|
||||
return std::nullopt;
|
||||
};
|
||||
const auto result = IR::BreadthFirstSearch(inst, pred0);
|
||||
ASSERT_MSG(result, "Unable to track sharp source");
|
||||
inst = result.value();
|
||||
// If its from user data not much else to do.
|
||||
if (inst->GetOpcode() == IR::Opcode::GetUserData) {
|
||||
return SharpLocation{
|
||||
.sgpr_base = u32(IR::ScalarReg::Max),
|
||||
|
@ -289,14 +297,14 @@ SharpLocation TrackSharp(const IR::Inst* inst) {
|
|||
const IR::Inst* spgpr_base = inst->Arg(0).InstRecursive();
|
||||
|
||||
// Retrieve SGPR pair that holds sbase
|
||||
const auto pred = [](const IR::Inst* inst) -> std::optional<IR::ScalarReg> {
|
||||
const auto pred1 = [](const IR::Inst* inst) -> std::optional<IR::ScalarReg> {
|
||||
if (inst->GetOpcode() == IR::Opcode::GetUserData) {
|
||||
return inst->Arg(0).ScalarReg();
|
||||
}
|
||||
return std::nullopt;
|
||||
};
|
||||
const auto base0 = IR::BreadthFirstSearch(spgpr_base->Arg(0), pred);
|
||||
const auto base1 = IR::BreadthFirstSearch(spgpr_base->Arg(1), pred);
|
||||
const auto base0 = IR::BreadthFirstSearch(spgpr_base->Arg(0), pred1);
|
||||
const auto base1 = IR::BreadthFirstSearch(spgpr_base->Arg(1), pred1);
|
||||
ASSERT_MSG(base0 && base1, "Nested resource loads not supported");
|
||||
|
||||
// Return retrieved location.
|
||||
|
@ -456,36 +464,26 @@ IR::Value PatchCubeCoord(IR::IREmitter& ir, const IR::Value& s, const IR::Value&
|
|||
}
|
||||
|
||||
void PatchImageInstruction(IR::Block& block, IR::Inst& inst, Info& info, Descriptors& descriptors) {
|
||||
std::deque<IR::Inst*> insts{&inst};
|
||||
const auto& pred = [](auto opcode) -> bool {
|
||||
return (opcode == IR::Opcode::CompositeConstructU32x2 || // IMAGE_SAMPLE (image+sampler)
|
||||
opcode == IR::Opcode::ReadConst || // IMAGE_LOAD (image only)
|
||||
opcode == IR::Opcode::GetUserData);
|
||||
const auto pred = [](const IR::Inst* inst) -> std::optional<const IR::Inst*> {
|
||||
const auto opcode = inst->GetOpcode();
|
||||
if (opcode == IR::Opcode::CompositeConstructU32x2 || // IMAGE_SAMPLE (image+sampler)
|
||||
opcode == IR::Opcode::ReadConst || // IMAGE_LOAD (image only)
|
||||
opcode == IR::Opcode::GetUserData) {
|
||||
return inst;
|
||||
}
|
||||
return std::nullopt;
|
||||
};
|
||||
|
||||
IR::Inst* producer{};
|
||||
while (!insts.empty() && (producer = insts.front(), !pred(producer->GetOpcode()))) {
|
||||
for (auto arg_idx = 0u; arg_idx < producer->NumArgs(); ++arg_idx) {
|
||||
const auto arg = producer->Arg(arg_idx);
|
||||
if (arg.TryInstRecursive()) {
|
||||
insts.push_back(arg.InstRecursive());
|
||||
}
|
||||
}
|
||||
insts.pop_front();
|
||||
}
|
||||
ASSERT(pred(producer->GetOpcode()));
|
||||
auto [tsharp_handle, ssharp_handle] = [&] -> std::pair<IR::Inst*, IR::Inst*> {
|
||||
if (producer->GetOpcode() == IR::Opcode::CompositeConstructU32x2) {
|
||||
return std::make_pair(producer->Arg(0).InstRecursive(),
|
||||
producer->Arg(1).InstRecursive());
|
||||
}
|
||||
return std::make_pair(producer, nullptr);
|
||||
}();
|
||||
const auto result = IR::BreadthFirstSearch(&inst, pred);
|
||||
ASSERT_MSG(result, "Unable to find image sharp source");
|
||||
const IR::Inst* producer = result.value();
|
||||
const bool has_sampler = producer->GetOpcode() == IR::Opcode::CompositeConstructU32x2;
|
||||
const auto tsharp_handle = has_sampler ? producer->Arg(0).InstRecursive() : producer;
|
||||
|
||||
// Read image sharp.
|
||||
const auto tsharp = TrackSharp(tsharp_handle);
|
||||
const auto image = info.ReadUd<AmdGpu::Image>(tsharp.sgpr_base, tsharp.dword_offset);
|
||||
const auto inst_info = inst.Flags<IR::TextureInstInfo>();
|
||||
ASSERT(image.GetType() != AmdGpu::ImageType::Invalid);
|
||||
u32 image_binding = descriptors.Add(ImageResource{
|
||||
.sgpr_base = tsharp.sgpr_base,
|
||||
.dword_offset = tsharp.dword_offset,
|
||||
|
@ -496,17 +494,32 @@ void PatchImageInstruction(IR::Block& block, IR::Inst& inst, Info& info, Descrip
|
|||
});
|
||||
|
||||
// Read sampler sharp. This doesn't exist for IMAGE_LOAD/IMAGE_STORE instructions
|
||||
if (ssharp_handle) {
|
||||
const u32 sampler_binding = [&] {
|
||||
if (!has_sampler) {
|
||||
return 0U;
|
||||
}
|
||||
const IR::Value& handle = producer->Arg(1);
|
||||
// Inline sampler resource.
|
||||
if (handle.IsImmediate()) {
|
||||
LOG_WARNING(Render_Vulkan, "Inline sampler detected");
|
||||
return descriptors.Add(SamplerResource{
|
||||
.sgpr_base = std::numeric_limits<u32>::max(),
|
||||
.dword_offset = 0,
|
||||
.inline_sampler = AmdGpu::Sampler{.raw0 = handle.U32()},
|
||||
});
|
||||
}
|
||||
// Normal sampler resource.
|
||||
const auto ssharp_handle = handle.InstRecursive();
|
||||
const auto& [ssharp_ud, disable_aniso] = TryDisableAnisoLod0(ssharp_handle);
|
||||
const auto ssharp = TrackSharp(ssharp_ud);
|
||||
const u32 sampler_binding = descriptors.Add(SamplerResource{
|
||||
return descriptors.Add(SamplerResource{
|
||||
.sgpr_base = ssharp.sgpr_base,
|
||||
.dword_offset = ssharp.dword_offset,
|
||||
.associated_image = image_binding,
|
||||
.disable_aniso = disable_aniso,
|
||||
});
|
||||
image_binding |= (sampler_binding << 16);
|
||||
}
|
||||
}();
|
||||
image_binding |= (sampler_binding << 16);
|
||||
|
||||
// Patch image handle
|
||||
IR::IREmitter ir{block, IR::Block::InstructionList::s_iterator_to(inst)};
|
||||
|
@ -607,7 +620,7 @@ void ResourceTrackingPass(IR::Program& program) {
|
|||
// Iterate resource instructions and patch them after finding the sharp.
|
||||
auto& info = program.info;
|
||||
Descriptors descriptors{info.buffers, info.images, info.samplers};
|
||||
for (IR::Block* const block : program.post_order_blocks) {
|
||||
for (IR::Block* const block : program.blocks) {
|
||||
for (IR::Inst& inst : block->Instructions()) {
|
||||
if (IsBufferInstruction(inst)) {
|
||||
PatchBufferInstruction(*block, inst, info, descriptors);
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue