mirror of
https://github.com/shadps4-emu/shadPS4.git
synced 2026-04-13 13:11:28 -06:00
Merge branch 'main' into ime-fixes-again
This commit is contained in:
commit
1016a6faa7
@ -587,6 +587,8 @@ set(NP_LIBS src/core/libraries/np/np_error.h
|
||||
src/core/libraries/np/np_score.h
|
||||
src/core/libraries/np/np_trophy.cpp
|
||||
src/core/libraries/np/np_trophy.h
|
||||
src/core/libraries/np/np_tus.cpp
|
||||
src/core/libraries/np/np_tus.h
|
||||
src/core/libraries/np/trophy_ui.cpp
|
||||
src/core/libraries/np/trophy_ui.h
|
||||
src/core/libraries/np/np_web_api.cpp
|
||||
@ -601,6 +603,8 @@ set(NP_LIBS src/core/libraries/np/np_error.h
|
||||
src/core/libraries/np/np_profile_dialog.h
|
||||
src/core/libraries/np/np_sns_facebook_dialog.cpp
|
||||
src/core/libraries/np/np_sns_facebook_dialog.h
|
||||
src/core/libraries/np/np_partner.cpp
|
||||
src/core/libraries/np/np_partner.h
|
||||
)
|
||||
|
||||
set(ZLIB_LIB src/core/libraries/zlib/zlib.cpp
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
<!--
|
||||
SPDX-FileCopyrightText: 2024 shadPS4 Emulator Project
|
||||
SPDX-FileCopyrightText: 2026 shadPS4 Emulator Project
|
||||
SPDX-License-Identifier: GPL-2.0-or-later
|
||||
-->
|
||||
|
||||
@ -58,6 +58,11 @@ This project began for fun. Given our limited free time, it may take some time b
|
||||
|
||||
# Building
|
||||
|
||||
## Docker
|
||||
|
||||
For building shadPS4 in a containerized environment using Docker and VSCode, check the instructions here:
|
||||
[**Docker Build Instructions**](https://github.com/shadps4-emu/shadPS4/blob/main/documents/building-docker.md)
|
||||
|
||||
## Windows
|
||||
|
||||
Check the build instructions for [**Windows**](https://github.com/shadps4-emu/shadPS4/blob/main/documents/building-windows.md).
|
||||
|
||||
51
documents/Docker Builder/.devcontainer/devcontainer.json
Normal file
51
documents/Docker Builder/.devcontainer/devcontainer.json
Normal file
@ -0,0 +1,51 @@
|
||||
// SPDX-FileCopyrightText: 2026 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
{
|
||||
"name": "shadPS4-dev",
|
||||
"dockerComposeFile": [
|
||||
"../docker-compose.yml"
|
||||
],
|
||||
"containerEnv": {
|
||||
"GITHUB_TOKEN": "${localEnv:GITHUB_TOKEN}",
|
||||
"GITHUB_USER": "${localEnv:GITHUB_USER}"
|
||||
},
|
||||
"service": "shadps4",
|
||||
"workspaceFolder": "/workspaces/shadPS4",
|
||||
"remoteUser": "root",
|
||||
"shutdownAction": "none",
|
||||
"customizations": {
|
||||
"vscode": {
|
||||
"extensions": [
|
||||
"llvm-vs-code-extensions.vscode-clangd",
|
||||
"ms-vscode.cmake-tools",
|
||||
"xaver.clang-format"
|
||||
],
|
||||
"settings": {
|
||||
"clangd.arguments": [
|
||||
"--background-index",
|
||||
"--clang-tidy",
|
||||
"--completion-style=detailed",
|
||||
"--header-insertion=never",
|
||||
"--compile-commands-dir=/workspaces/shadPS4/Build/x64-Clang-Release"
|
||||
],
|
||||
"C_Cpp.intelliSenseEngine": "Disabled"
|
||||
}
|
||||
}
|
||||
},
|
||||
"settings": {
|
||||
"cmake.configureOnOpen": false,
|
||||
"cmake.generator": "Unix Makefiles",
|
||||
"cmake.environment": {
|
||||
"CC": "clang",
|
||||
"CXX": "clang++"
|
||||
},
|
||||
"cmake.configureEnvironment": {
|
||||
"CMAKE_CXX_STANDARD": "23",
|
||||
"CMAKE_CXX_STANDARD_REQUIRED": "ON",
|
||||
"CMAKE_EXPORT_COMPILE_COMMANDS": "ON"
|
||||
},
|
||||
"editor.formatOnSave": true,
|
||||
"clang-format.executable": "clang-format-19"
|
||||
}
|
||||
}
|
||||
45
documents/Docker Builder/.docker/Dockerfile
Normal file
45
documents/Docker Builder/.docker/Dockerfile
Normal file
@ -0,0 +1,45 @@
|
||||
# SPDX-FileCopyrightText: 2026 shadPS4 Emulator Project
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
FROM archlinux:latest
|
||||
|
||||
RUN pacman-key --init && \
|
||||
pacman-key --populate archlinux && \
|
||||
pacman -Syu --noconfirm
|
||||
|
||||
RUN pacman -S --noconfirm \
|
||||
base-devel \
|
||||
clang \
|
||||
clang19 \
|
||||
ninja \
|
||||
git \
|
||||
ca-certificates \
|
||||
wget \
|
||||
alsa-lib \
|
||||
libpulse \
|
||||
openal \
|
||||
openssl \
|
||||
zlib \
|
||||
libedit \
|
||||
systemd-libs \
|
||||
libevdev \
|
||||
sdl2 \
|
||||
jack \
|
||||
sndio \
|
||||
libxtst \
|
||||
vulkan-headers \
|
||||
vulkan-validation-layers \
|
||||
libpng \
|
||||
clang-tools-extra \
|
||||
cmake \
|
||||
libx11 \
|
||||
libxrandr \
|
||||
libxcursor \
|
||||
libxi \
|
||||
libxinerama \
|
||||
libxss \
|
||||
&& pacman -Scc --noconfirm
|
||||
|
||||
RUN ln -sf /usr/lib/llvm19/bin/clang-format /usr/bin/clang-format-19
|
||||
|
||||
WORKDIR /workspaces/shadPS4
|
||||
10
documents/Docker Builder/docker-compose.yml
Normal file
10
documents/Docker Builder/docker-compose.yml
Normal file
@ -0,0 +1,10 @@
|
||||
# SPDX-FileCopyrightText: 2026 shadPS4 Emulator Project
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
services:
|
||||
shadps4:
|
||||
build:
|
||||
context: ./.docker
|
||||
volumes:
|
||||
- ./emu:/workspaces/shadPS4:cached
|
||||
tty: true
|
||||
100
documents/building-docker.md
Normal file
100
documents/building-docker.md
Normal file
@ -0,0 +1,100 @@
|
||||
<!--
|
||||
SPDX-FileCopyrightText: 2026 shadPS4 Emulator Project
|
||||
SPDX-License-Identifier: GPL-2.0-or-later
|
||||
-->
|
||||
|
||||
# Building shadPS4 with Docker and VSCode Support
|
||||
|
||||
This guide explains how to build **shadPS4** using Docker while keeping full compatibility with **VSCode** development.
|
||||
|
||||
---
|
||||
|
||||
## Prerequisites
|
||||
|
||||
Before starting, ensure you have:
|
||||
|
||||
- **Docker Engine** or **Docker Desktop** installed
|
||||
[Installation Guide](https://docs.docker.com/engine/install/)
|
||||
|
||||
- **Git** installed on your system.
|
||||
|
||||
---
|
||||
|
||||
## Step 1: Prepare the Docker Environment
|
||||
|
||||
Inside the container (or on your host if mounting volumes):
|
||||
|
||||
1. Navigate to the repository folder containing the Docker Builder folder:
|
||||
|
||||
```bash
|
||||
cd <path-to-repo>
|
||||
```
|
||||
|
||||
2. Start the Docker container:
|
||||
|
||||
```bash
|
||||
docker compose up -d
|
||||
```
|
||||
|
||||
This will spin up a container with all the necessary build dependencies, including Clang, CMake, SDL2, Vulkan, and more.
|
||||
|
||||
## Step 2: Clone shadPS4 Source
|
||||
|
||||
```bash
|
||||
mkdir emu
|
||||
cd emu
|
||||
git clone --recursive https://github.com/shadps4-emu/shadPS4.git .
|
||||
|
||||
or your fork link.
|
||||
```
|
||||
|
||||
3. Initialize submodules:
|
||||
|
||||
```bash
|
||||
git submodule update --init --recursive
|
||||
```
|
||||
|
||||
## Step 3: Build with CMake Tools (GUI)
|
||||
|
||||
Generate build with CMake Tools.
|
||||
|
||||
1. Go `CMake Tools > Configure > '>'`
|
||||
2. And `Build > '>'`
|
||||
|
||||
Compiled executable in `Build` folder.
|
||||
|
||||
## Alternative Step 3: Build with CMake
|
||||
|
||||
Generate the build directory and configure the project using Clang:
|
||||
|
||||
```bash
|
||||
cmake -S . -B build/ -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++
|
||||
```
|
||||
|
||||
Then build the project:
|
||||
|
||||
```bash
|
||||
cmake --build ./build --parallel $(nproc)
|
||||
```
|
||||
|
||||
* Tip: To enable debug builds, add -DCMAKE_BUILD_TYPE=Debug to the CMake command.
|
||||
|
||||
---
|
||||
|
||||
After a successful build, the executable is located at:
|
||||
|
||||
```bash
|
||||
./build/shadps4
|
||||
```
|
||||
|
||||
## Step 4: VSCode Integration
|
||||
|
||||
1. Open the repository in VSCode.
|
||||
2. The CMake Tools extension should automatically detect the build directory inside the container or on your host.
|
||||
3. You can configure build options, build, and debug directly from the VSCode interface without extra manual setup.
|
||||
|
||||
# Notes
|
||||
|
||||
* The Docker environment contains all dependencies, so you don’t need to install anything manually.
|
||||
* Using Clang inside Docker ensures consistent builds across Linux and macOS runners.
|
||||
* GitHub Actions are recommended for cross-platform builds, including Windows .exe output, which is not trivial to produce locally without Visual Studio or clang-cl.
|
||||
2
externals/MoltenVK
vendored
2
externals/MoltenVK
vendored
@ -1 +1 @@
|
||||
Subproject commit f168dec05998ab0ca09a400bab6831a95c0bdb2e
|
||||
Subproject commit f79c6c5690d3ee06ec3a00d11a8b1bab4aa1d030
|
||||
@ -109,10 +109,12 @@ bool ParseFilterRule(Filter& instance, Iterator begin, Iterator end) {
|
||||
SUB(Lib, NpManager) \
|
||||
SUB(Lib, NpScore) \
|
||||
SUB(Lib, NpTrophy) \
|
||||
SUB(Lib, NpTus) \
|
||||
SUB(Lib, NpWebApi) \
|
||||
SUB(Lib, NpWebApi2) \
|
||||
SUB(Lib, NpProfileDialog) \
|
||||
SUB(Lib, NpSnsFacebookDialog) \
|
||||
SUB(Lib, NpPartner) \
|
||||
SUB(Lib, Screenshot) \
|
||||
SUB(Lib, LibCInternal) \
|
||||
SUB(Lib, AppContent) \
|
||||
|
||||
@ -76,6 +76,7 @@ enum class Class : u8 {
|
||||
Lib_NpManager, ///< The LibSceNpManager implementation
|
||||
Lib_NpScore, ///< The LibSceNpScore implementation
|
||||
Lib_NpTrophy, ///< The LibSceNpTrophy implementation
|
||||
Lib_NpTus, ///< The LibSceNpTus implementation
|
||||
Lib_NpWebApi, ///< The LibSceWebApi implementation
|
||||
Lib_NpWebApi2, ///< The LibSceWebApi2 implementation
|
||||
Lib_NpProfileDialog, ///< The LibSceNpProfileDialog implementation
|
||||
@ -110,6 +111,7 @@ enum class Class : u8 {
|
||||
Lib_Mouse, ///< The LibSceMouse implementation
|
||||
Lib_WebBrowserDialog, ///< The LibSceWebBrowserDialog implementation
|
||||
Lib_NpParty, ///< The LibSceNpParty implementation
|
||||
Lib_NpPartner, ///< The LibSceNpPartner implementation
|
||||
Lib_Zlib, ///< The LibSceZlib implementation.
|
||||
Lib_Hmd, ///< The LibSceHmd implementation.
|
||||
Lib_HmdSetupDialog, ///< The LibSceHmdSetupDialog implementation.
|
||||
|
||||
@ -17,6 +17,15 @@ public:
|
||||
writer_active = true;
|
||||
}
|
||||
|
||||
bool try_lock() {
|
||||
std::lock_guard<std::mutex> lock(mtx);
|
||||
if (writer_active || readers > 0) {
|
||||
return false;
|
||||
}
|
||||
writer_active = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
void unlock() {
|
||||
std::lock_guard<std::mutex> lock(mtx);
|
||||
writer_active = false;
|
||||
|
||||
@ -174,6 +174,9 @@ bool AccurateSleep(const std::chrono::nanoseconds duration, std::chrono::nanosec
|
||||
|
||||
// Sets the debugger-visible name of the current thread.
|
||||
void SetCurrentThreadName(const char* name) {
|
||||
if (Libraries::Kernel::g_curthread) {
|
||||
Libraries::Kernel::g_curthread->name = std::string{name};
|
||||
}
|
||||
SetThreadDescription(GetCurrentThread(), UTF8ToUTF16W(name).data());
|
||||
}
|
||||
|
||||
@ -186,6 +189,9 @@ void SetThreadName(void* thread, const char* name) {
|
||||
// MinGW with the POSIX threading model does not support pthread_setname_np
|
||||
#if !defined(_WIN32) || defined(_MSC_VER)
|
||||
void SetCurrentThreadName(const char* name) {
|
||||
if (Libraries::Kernel::g_curthread) {
|
||||
Libraries::Kernel::g_curthread->name = std::string{name};
|
||||
}
|
||||
#ifdef __APPLE__
|
||||
pthread_setname_np(name);
|
||||
#elif defined(__Bitrig__) || defined(__DragonFly__) || defined(__FreeBSD__) || defined(__OpenBSD__)
|
||||
@ -212,6 +218,9 @@ void SetThreadName(void* thread, const char* name) {
|
||||
|
||||
#if defined(_WIN32)
|
||||
void SetCurrentThreadName(const char*) {
|
||||
if (Libraries::Kernel::g_curthread) {
|
||||
Libraries::Kernel::g_curthread->name = std::string{name};
|
||||
}
|
||||
// Do Nothing on MinGW
|
||||
}
|
||||
|
||||
|
||||
@ -709,7 +709,7 @@ struct AddressSpace::Impl {
|
||||
return ret;
|
||||
}
|
||||
|
||||
void Unmap(VAddr virtual_addr, u64 size, bool) {
|
||||
void Unmap(VAddr virtual_addr, u64 size) {
|
||||
// Check to see if we are adjacent to any regions.
|
||||
VAddr start_address = virtual_addr;
|
||||
VAddr end_address = start_address + size;
|
||||
@ -792,12 +792,8 @@ void* AddressSpace::MapFile(VAddr virtual_addr, u64 size, u64 offset, u32 prot,
|
||||
#endif
|
||||
}
|
||||
|
||||
void AddressSpace::Unmap(VAddr virtual_addr, u64 size, bool has_backing) {
|
||||
#ifdef _WIN32
|
||||
void AddressSpace::Unmap(VAddr virtual_addr, u64 size) {
|
||||
impl->Unmap(virtual_addr, size);
|
||||
#else
|
||||
impl->Unmap(virtual_addr, size, has_backing);
|
||||
#endif
|
||||
}
|
||||
|
||||
void AddressSpace::Protect(VAddr virtual_addr, u64 size, MemoryPermission perms) {
|
||||
|
||||
@ -79,8 +79,9 @@ public:
|
||||
void* MapFile(VAddr virtual_addr, u64 size, u64 offset, u32 prot, uintptr_t fd);
|
||||
|
||||
/// Unmaps specified virtual memory area.
|
||||
void Unmap(VAddr virtual_addr, u64 size, bool has_backing);
|
||||
void Unmap(VAddr virtual_addr, u64 size);
|
||||
|
||||
/// Protects requested region.
|
||||
void Protect(VAddr virtual_addr, u64 size, MemoryPermission perms);
|
||||
|
||||
// Returns an interval set containing all usable regions.
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 shadPS4 Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2025-2026 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "common/assert.h"
|
||||
@ -325,6 +325,7 @@ PthreadT PS4_SYSV_ABI posix_pthread_self() {
|
||||
}
|
||||
|
||||
void PS4_SYSV_ABI posix_pthread_set_name_np(PthreadT thread, const char* name) {
|
||||
LOG_INFO(Kernel_Pthread, "called, new name: {}", name);
|
||||
Common::SetCurrentThreadName(name);
|
||||
}
|
||||
|
||||
|
||||
@ -35,11 +35,13 @@
|
||||
#include "core/libraries/np/np_commerce.h"
|
||||
#include "core/libraries/np/np_common.h"
|
||||
#include "core/libraries/np/np_manager.h"
|
||||
#include "core/libraries/np/np_partner.h"
|
||||
#include "core/libraries/np/np_party.h"
|
||||
#include "core/libraries/np/np_profile_dialog.h"
|
||||
#include "core/libraries/np/np_score.h"
|
||||
#include "core/libraries/np/np_sns_facebook_dialog.h"
|
||||
#include "core/libraries/np/np_trophy.h"
|
||||
#include "core/libraries/np/np_tus.h"
|
||||
#include "core/libraries/np/np_web_api.h"
|
||||
#include "core/libraries/np/np_web_api2.h"
|
||||
#include "core/libraries/pad/pad.h"
|
||||
@ -105,6 +107,8 @@ void InitHLELibs(Core::Loader::SymbolsResolver* sym) {
|
||||
Libraries::Np::NpSnsFacebookDialog::RegisterLib(sym);
|
||||
Libraries::Np::NpAuth::RegisterLib(sym);
|
||||
Libraries::Np::NpParty::RegisterLib(sym);
|
||||
Libraries::Np::NpPartner::RegisterLib(sym);
|
||||
Libraries::Np::NpTus::RegisterLib(sym);
|
||||
Libraries::ScreenShot::RegisterLib(sym);
|
||||
Libraries::AppContent::RegisterLib(sym);
|
||||
Libraries::PngDec::RegisterLib(sym);
|
||||
|
||||
83
src/core/libraries/np/np_partner.cpp
Normal file
83
src/core/libraries/np/np_partner.cpp
Normal file
@ -0,0 +1,83 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <mutex>
|
||||
#include "common/logging/log.h"
|
||||
#include "core/libraries/error_codes.h"
|
||||
#include "core/libraries/libs.h"
|
||||
#include "core/libraries/np/np_partner.h"
|
||||
#include "core/libraries/np/np_partner_error.h"
|
||||
#include "core/libraries/system/userservice.h"
|
||||
|
||||
namespace Libraries::Np::NpPartner {
|
||||
|
||||
static bool g_library_init = false;
|
||||
std::mutex g_library_mutex{};
|
||||
|
||||
/**
|
||||
* Terminates the library
|
||||
*/
|
||||
s32 PS4_SYSV_ABI Func_A4CC5784DA33517F() {
|
||||
LOG_ERROR(Lib_NpPartner, "(STUBBED) called");
|
||||
if (!g_library_init) {
|
||||
return ORBIS_NP_PARTNER_ERROR_NOT_INITIALIZED;
|
||||
}
|
||||
std::scoped_lock lk{g_library_mutex};
|
||||
g_library_init = false;
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* Aborts requests started by Func_F8E9DB52CD425743
|
||||
*/
|
||||
s32 PS4_SYSV_ABI Func_A507D84D91F39CC7() {
|
||||
LOG_ERROR(Lib_NpPartner, "(STUBBED) called");
|
||||
if (!g_library_init) {
|
||||
return ORBIS_NP_PARTNER_ERROR_NOT_INITIALIZED;
|
||||
}
|
||||
// Request logic is unimplemented, so this does nothing.
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the library
|
||||
*/
|
||||
s32 PS4_SYSV_ABI Func_EC2C48E74FF19429() {
|
||||
LOG_ERROR(Lib_NpPartner, "(STUBBED) called");
|
||||
g_library_init = true;
|
||||
// Also retrieves and sends compiled SDK version to server.
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an NP request to determine if the user has a subscription to EA's services.
|
||||
*/
|
||||
s32 PS4_SYSV_ABI Func_F8E9DB52CD425743(UserService::OrbisUserServiceUserId user_id, bool* result) {
|
||||
LOG_ERROR(Lib_NpPartner, "(STUBBED) called");
|
||||
if (!g_library_init) {
|
||||
return ORBIS_NP_PARTNER_ERROR_NOT_INITIALIZED;
|
||||
}
|
||||
if (result == nullptr) {
|
||||
return ORBIS_NP_PARTNER_ERROR_INVALID_ARGUMENT;
|
||||
}
|
||||
std::scoped_lock lk{g_library_mutex};
|
||||
// In the real library, this creates and sends a request that checks for EA subscription,
|
||||
// then waits for the request to return a response, and returns that response.
|
||||
// NP signed out likely returns an error, but I haven't figured out the error code yet.
|
||||
// For now, stub having no subscription.
|
||||
*result = false;
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
void RegisterLib(Core::Loader::SymbolsResolver* sym) {
|
||||
LIB_FUNCTION("pMxXhNozUX8", "libSceNpPartner001", 1, "libSceNpPartner001",
|
||||
Func_A4CC5784DA33517F);
|
||||
LIB_FUNCTION("pQfYTZHznMc", "libSceNpPartner001", 1, "libSceNpPartner001",
|
||||
Func_A507D84D91F39CC7);
|
||||
LIB_FUNCTION("7CxI50-xlCk", "libSceNpPartner001", 1, "libSceNpPartner001",
|
||||
Func_EC2C48E74FF19429);
|
||||
LIB_FUNCTION("+OnbUs1CV0M", "libSceNpPartner001", 1, "libSceNpPartner001",
|
||||
Func_F8E9DB52CD425743);
|
||||
};
|
||||
|
||||
} // namespace Libraries::Np::NpPartner
|
||||
15
src/core/libraries/np/np_partner.h
Normal file
15
src/core/libraries/np/np_partner.h
Normal file
@ -0,0 +1,15 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common/types.h"
|
||||
|
||||
namespace Core::Loader {
|
||||
class SymbolsResolver;
|
||||
}
|
||||
|
||||
namespace Libraries::Np::NpPartner {
|
||||
|
||||
void RegisterLib(Core::Loader::SymbolsResolver* sym);
|
||||
} // namespace Libraries::Np::NpPartner
|
||||
9
src/core/libraries/np/np_partner_error.h
Normal file
9
src/core/libraries/np/np_partner_error.h
Normal file
@ -0,0 +1,9 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "core/libraries/error_codes.h"
|
||||
|
||||
constexpr int ORBIS_NP_PARTNER_ERROR_NOT_INITIALIZED = 0x819d0001;
|
||||
constexpr int ORBIS_NP_PARTNER_ERROR_INVALID_ARGUMENT = 0x819d0002;
|
||||
1002
src/core/libraries/np/np_tus.cpp
Normal file
1002
src/core/libraries/np/np_tus.cpp
Normal file
File diff suppressed because it is too large
Load Diff
158
src/core/libraries/np/np_tus.h
Normal file
158
src/core/libraries/np/np_tus.h
Normal file
@ -0,0 +1,158 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024-2026 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common/types.h"
|
||||
|
||||
namespace Core::Loader {
|
||||
class SymbolsResolver;
|
||||
}
|
||||
|
||||
namespace Libraries::Np::NpTus {
|
||||
|
||||
s32 PS4_SYSV_ABI sceNpTssCreateNpTitleCtx();
|
||||
s32 PS4_SYSV_ABI sceNpTusAddAndGetVariable();
|
||||
s32 PS4_SYSV_ABI sceNpTusAddAndGetVariableAsync();
|
||||
s32 PS4_SYSV_ABI sceNpTusAddAndGetVariableVUser();
|
||||
s32 PS4_SYSV_ABI sceNpTusAddAndGetVariableVUserAsync();
|
||||
s32 PS4_SYSV_ABI sceNpTusCreateNpTitleCtx();
|
||||
s32 PS4_SYSV_ABI sceNpTusDeleteMultiSlotData();
|
||||
s32 PS4_SYSV_ABI sceNpTusDeleteMultiSlotDataAsync();
|
||||
s32 PS4_SYSV_ABI sceNpTusDeleteMultiSlotVariable();
|
||||
s32 PS4_SYSV_ABI sceNpTusDeleteMultiSlotVariableAsync();
|
||||
s32 PS4_SYSV_ABI sceNpTusGetData();
|
||||
s32 PS4_SYSV_ABI sceNpTusGetDataAsync();
|
||||
s32 PS4_SYSV_ABI sceNpTusGetDataVUser();
|
||||
s32 PS4_SYSV_ABI sceNpTusGetDataVUserAsync();
|
||||
s32 PS4_SYSV_ABI sceNpTusGetFriendsDataStatus();
|
||||
s32 PS4_SYSV_ABI sceNpTusGetFriendsDataStatusAsync();
|
||||
s32 PS4_SYSV_ABI sceNpTusGetFriendsVariable();
|
||||
s32 PS4_SYSV_ABI sceNpTusGetFriendsVariableAsync();
|
||||
s32 PS4_SYSV_ABI sceNpTusGetMultiSlotDataStatus();
|
||||
s32 PS4_SYSV_ABI sceNpTusGetMultiSlotDataStatusAsync();
|
||||
s32 PS4_SYSV_ABI sceNpTusGetMultiSlotDataStatusVUser();
|
||||
s32 PS4_SYSV_ABI sceNpTusGetMultiSlotDataStatusVUserAsync();
|
||||
s32 PS4_SYSV_ABI sceNpTusGetMultiSlotVariable();
|
||||
s32 PS4_SYSV_ABI sceNpTusGetMultiSlotVariableAsync();
|
||||
s32 PS4_SYSV_ABI sceNpTusGetMultiSlotVariableVUser();
|
||||
s32 PS4_SYSV_ABI sceNpTusGetMultiSlotVariableVUserAsync();
|
||||
s32 PS4_SYSV_ABI sceNpTusGetMultiUserDataStatus();
|
||||
s32 PS4_SYSV_ABI sceNpTusGetMultiUserDataStatusAsync();
|
||||
s32 PS4_SYSV_ABI sceNpTusGetMultiUserDataStatusVUser();
|
||||
s32 PS4_SYSV_ABI sceNpTusGetMultiUserDataStatusVUserAsync();
|
||||
s32 PS4_SYSV_ABI sceNpTusGetMultiUserVariable();
|
||||
s32 PS4_SYSV_ABI sceNpTusGetMultiUserVariableAsync();
|
||||
s32 PS4_SYSV_ABI sceNpTusGetMultiUserVariableVUser();
|
||||
s32 PS4_SYSV_ABI sceNpTusGetMultiUserVariableVUserAsync();
|
||||
s32 PS4_SYSV_ABI sceNpTusSetData();
|
||||
s32 PS4_SYSV_ABI sceNpTusSetDataAsync();
|
||||
s32 PS4_SYSV_ABI sceNpTusSetDataVUser();
|
||||
s32 PS4_SYSV_ABI sceNpTusSetDataVUserAsync();
|
||||
s32 PS4_SYSV_ABI sceNpTusSetMultiSlotVariable();
|
||||
s32 PS4_SYSV_ABI sceNpTusSetMultiSlotVariableAsync();
|
||||
s32 PS4_SYSV_ABI sceNpTusTryAndSetVariable();
|
||||
s32 PS4_SYSV_ABI sceNpTusTryAndSetVariableAsync();
|
||||
s32 PS4_SYSV_ABI sceNpTusTryAndSetVariableVUser();
|
||||
s32 PS4_SYSV_ABI sceNpTusTryAndSetVariableVUserAsync();
|
||||
s32 PS4_SYSV_ABI sceNpTssCreateNpTitleCtxA();
|
||||
s32 PS4_SYSV_ABI sceNpTssGetData();
|
||||
s32 PS4_SYSV_ABI sceNpTssGetDataAsync();
|
||||
s32 PS4_SYSV_ABI sceNpTssGetSmallStorage();
|
||||
s32 PS4_SYSV_ABI sceNpTssGetSmallStorageAsync();
|
||||
s32 PS4_SYSV_ABI sceNpTssGetStorage();
|
||||
s32 PS4_SYSV_ABI sceNpTssGetStorageAsync();
|
||||
s32 PS4_SYSV_ABI sceNpTusAbortRequest();
|
||||
s32 PS4_SYSV_ABI sceNpTusAddAndGetVariableA();
|
||||
s32 PS4_SYSV_ABI sceNpTusAddAndGetVariableAAsync();
|
||||
s32 PS4_SYSV_ABI sceNpTusAddAndGetVariableAVUser();
|
||||
s32 PS4_SYSV_ABI sceNpTusAddAndGetVariableAVUserAsync();
|
||||
s32 PS4_SYSV_ABI sceNpTusAddAndGetVariableForCrossSave();
|
||||
s32 PS4_SYSV_ABI sceNpTusAddAndGetVariableForCrossSaveAsync();
|
||||
s32 PS4_SYSV_ABI sceNpTusAddAndGetVariableForCrossSaveVUser();
|
||||
s32 PS4_SYSV_ABI sceNpTusAddAndGetVariableForCrossSaveVUserAsync();
|
||||
s32 PS4_SYSV_ABI sceNpTusChangeModeForOtherSaveDataOwners();
|
||||
s32 PS4_SYSV_ABI sceNpTusCreateNpTitleCtxA();
|
||||
s32 PS4_SYSV_ABI sceNpTusCreateRequest();
|
||||
s32 PS4_SYSV_ABI sceNpTusCreateTitleCtx();
|
||||
s32 PS4_SYSV_ABI sceNpTusDeleteMultiSlotDataA();
|
||||
s32 PS4_SYSV_ABI sceNpTusDeleteMultiSlotDataAAsync();
|
||||
s32 PS4_SYSV_ABI sceNpTusDeleteMultiSlotDataVUser();
|
||||
s32 PS4_SYSV_ABI sceNpTusDeleteMultiSlotDataVUserAsync();
|
||||
s32 PS4_SYSV_ABI sceNpTusDeleteMultiSlotVariableA();
|
||||
s32 PS4_SYSV_ABI sceNpTusDeleteMultiSlotVariableAAsync();
|
||||
s32 PS4_SYSV_ABI sceNpTusDeleteMultiSlotVariableVUser();
|
||||
s32 PS4_SYSV_ABI sceNpTusDeleteMultiSlotVariableVUserAsync();
|
||||
s32 PS4_SYSV_ABI sceNpTusDeleteNpTitleCtx();
|
||||
s32 PS4_SYSV_ABI sceNpTusDeleteRequest();
|
||||
s32 PS4_SYSV_ABI sceNpTusGetDataA();
|
||||
s32 PS4_SYSV_ABI sceNpTusGetDataAAsync();
|
||||
s32 PS4_SYSV_ABI sceNpTusGetDataAVUser();
|
||||
s32 PS4_SYSV_ABI sceNpTusGetDataAVUserAsync();
|
||||
s32 PS4_SYSV_ABI sceNpTusGetDataForCrossSave();
|
||||
s32 PS4_SYSV_ABI sceNpTusGetDataForCrossSaveAsync();
|
||||
s32 PS4_SYSV_ABI sceNpTusGetDataForCrossSaveVUser();
|
||||
s32 PS4_SYSV_ABI sceNpTusGetDataForCrossSaveVUserAsync();
|
||||
s32 PS4_SYSV_ABI sceNpTusGetFriendsDataStatusA();
|
||||
s32 PS4_SYSV_ABI sceNpTusGetFriendsDataStatusAAsync();
|
||||
s32 PS4_SYSV_ABI sceNpTusGetFriendsDataStatusForCrossSave();
|
||||
s32 PS4_SYSV_ABI sceNpTusGetFriendsDataStatusForCrossSaveAsync();
|
||||
s32 PS4_SYSV_ABI sceNpTusGetFriendsVariableA();
|
||||
s32 PS4_SYSV_ABI sceNpTusGetFriendsVariableAAsync();
|
||||
s32 PS4_SYSV_ABI sceNpTusGetFriendsVariableForCrossSave();
|
||||
s32 PS4_SYSV_ABI sceNpTusGetFriendsVariableForCrossSaveAsync();
|
||||
s32 PS4_SYSV_ABI sceNpTusGetMultiSlotDataStatusA();
|
||||
s32 PS4_SYSV_ABI sceNpTusGetMultiSlotDataStatusAAsync();
|
||||
s32 PS4_SYSV_ABI sceNpTusGetMultiSlotDataStatusAVUser();
|
||||
s32 PS4_SYSV_ABI sceNpTusGetMultiSlotDataStatusAVUserAsync();
|
||||
s32 PS4_SYSV_ABI sceNpTusGetMultiSlotDataStatusForCrossSave();
|
||||
s32 PS4_SYSV_ABI sceNpTusGetMultiSlotDataStatusForCrossSaveAsync();
|
||||
s32 PS4_SYSV_ABI sceNpTusGetMultiSlotDataStatusForCrossSaveVUser();
|
||||
s32 PS4_SYSV_ABI sceNpTusGetMultiSlotDataStatusForCrossSaveVUserAsync();
|
||||
s32 PS4_SYSV_ABI sceNpTusGetMultiSlotVariableA();
|
||||
s32 PS4_SYSV_ABI sceNpTusGetMultiSlotVariableAAsync();
|
||||
s32 PS4_SYSV_ABI sceNpTusGetMultiSlotVariableAVUser();
|
||||
s32 PS4_SYSV_ABI sceNpTusGetMultiSlotVariableAVUserAsync();
|
||||
s32 PS4_SYSV_ABI sceNpTusGetMultiSlotVariableForCrossSave();
|
||||
s32 PS4_SYSV_ABI sceNpTusGetMultiSlotVariableForCrossSaveAsync();
|
||||
s32 PS4_SYSV_ABI sceNpTusGetMultiSlotVariableForCrossSaveVUser();
|
||||
s32 PS4_SYSV_ABI sceNpTusGetMultiSlotVariableForCrossSaveVUserAsync();
|
||||
s32 PS4_SYSV_ABI sceNpTusGetMultiUserDataStatusA();
|
||||
s32 PS4_SYSV_ABI sceNpTusGetMultiUserDataStatusAAsync();
|
||||
s32 PS4_SYSV_ABI sceNpTusGetMultiUserDataStatusAVUser();
|
||||
s32 PS4_SYSV_ABI sceNpTusGetMultiUserDataStatusAVUserAsync();
|
||||
s32 PS4_SYSV_ABI sceNpTusGetMultiUserDataStatusForCrossSave();
|
||||
s32 PS4_SYSV_ABI sceNpTusGetMultiUserDataStatusForCrossSaveAsync();
|
||||
s32 PS4_SYSV_ABI sceNpTusGetMultiUserDataStatusForCrossSaveVUser();
|
||||
s32 PS4_SYSV_ABI sceNpTusGetMultiUserDataStatusForCrossSaveVUserAsync();
|
||||
s32 PS4_SYSV_ABI sceNpTusGetMultiUserVariableA();
|
||||
s32 PS4_SYSV_ABI sceNpTusGetMultiUserVariableAAsync();
|
||||
s32 PS4_SYSV_ABI sceNpTusGetMultiUserVariableAVUser();
|
||||
s32 PS4_SYSV_ABI sceNpTusGetMultiUserVariableAVUserAsync();
|
||||
s32 PS4_SYSV_ABI sceNpTusGetMultiUserVariableForCrossSave();
|
||||
s32 PS4_SYSV_ABI sceNpTusGetMultiUserVariableForCrossSaveAsync();
|
||||
s32 PS4_SYSV_ABI sceNpTusGetMultiUserVariableForCrossSaveVUser();
|
||||
s32 PS4_SYSV_ABI sceNpTusGetMultiUserVariableForCrossSaveVUserAsync();
|
||||
s32 PS4_SYSV_ABI sceNpTusPollAsync();
|
||||
s32 PS4_SYSV_ABI sceNpTusSetDataA();
|
||||
s32 PS4_SYSV_ABI sceNpTusSetDataAAsync();
|
||||
s32 PS4_SYSV_ABI sceNpTusSetDataAVUser();
|
||||
s32 PS4_SYSV_ABI sceNpTusSetDataAVUserAsync();
|
||||
s32 PS4_SYSV_ABI sceNpTusSetMultiSlotVariableA();
|
||||
s32 PS4_SYSV_ABI sceNpTusSetMultiSlotVariableAAsync();
|
||||
s32 PS4_SYSV_ABI sceNpTusSetMultiSlotVariableVUser();
|
||||
s32 PS4_SYSV_ABI sceNpTusSetMultiSlotVariableVUserAsync();
|
||||
s32 PS4_SYSV_ABI sceNpTusSetThreadParam();
|
||||
s32 PS4_SYSV_ABI sceNpTusSetTimeout();
|
||||
s32 PS4_SYSV_ABI sceNpTusTryAndSetVariableA();
|
||||
s32 PS4_SYSV_ABI sceNpTusTryAndSetVariableAAsync();
|
||||
s32 PS4_SYSV_ABI sceNpTusTryAndSetVariableAVUser();
|
||||
s32 PS4_SYSV_ABI sceNpTusTryAndSetVariableAVUserAsync();
|
||||
s32 PS4_SYSV_ABI sceNpTusTryAndSetVariableForCrossSave();
|
||||
s32 PS4_SYSV_ABI sceNpTusTryAndSetVariableForCrossSaveAsync();
|
||||
s32 PS4_SYSV_ABI sceNpTusTryAndSetVariableForCrossSaveVUser();
|
||||
s32 PS4_SYSV_ABI sceNpTusTryAndSetVariableForCrossSaveVUserAsync();
|
||||
s32 PS4_SYSV_ABI sceNpTusWaitAsync();
|
||||
|
||||
void RegisterLib(Core::Loader::SymbolsResolver* sym);
|
||||
} // namespace Libraries::Np::NpTus
|
||||
@ -1,4 +1,4 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 shadPS4 Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2025-2026 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "common/alignment.h"
|
||||
@ -105,7 +105,7 @@ void Linker::Execute(const std::vector<std::string>& args) {
|
||||
memory->SetupMemoryRegions(fmem_size, use_extended_mem1, use_extended_mem2);
|
||||
|
||||
main_thread.Run([this, module, &args](std::stop_token) {
|
||||
Common::SetCurrentThreadName("GAME_MainThread");
|
||||
Common::SetCurrentThreadName("Game:Main");
|
||||
if (auto& ipc = IPC::Instance()) {
|
||||
ipc.WaitForStart();
|
||||
}
|
||||
|
||||
@ -79,6 +79,7 @@ u64 MemoryManager::ClampRangeSize(VAddr virtual_addr, u64 size) {
|
||||
return size;
|
||||
}
|
||||
|
||||
std::shared_lock lk{mutex};
|
||||
ASSERT_MSG(IsValidMapping(virtual_addr), "Attempted to access invalid address {:#x}",
|
||||
virtual_addr);
|
||||
|
||||
@ -117,6 +118,7 @@ void MemoryManager::SetPrtArea(u32 id, VAddr address, u64 size) {
|
||||
}
|
||||
|
||||
void MemoryManager::CopySparseMemory(VAddr virtual_addr, u8* dest, u64 size) {
|
||||
std::shared_lock lk{mutex};
|
||||
ASSERT_MSG(IsValidMapping(virtual_addr), "Attempted to access invalid address {:#x}",
|
||||
virtual_addr);
|
||||
|
||||
@ -137,6 +139,7 @@ void MemoryManager::CopySparseMemory(VAddr virtual_addr, u8* dest, u64 size) {
|
||||
|
||||
bool MemoryManager::TryWriteBacking(void* address, const void* data, u64 size) {
|
||||
const VAddr virtual_addr = std::bit_cast<VAddr>(address);
|
||||
std::shared_lock lk{mutex};
|
||||
ASSERT_MSG(IsValidMapping(virtual_addr, size), "Attempted to access invalid address {:#x}",
|
||||
virtual_addr);
|
||||
|
||||
@ -263,9 +266,7 @@ s32 MemoryManager::Free(PAddr phys_addr, u64 size, bool is_checked) {
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
// Lock mutex
|
||||
std::scoped_lock lk{mutex};
|
||||
|
||||
std::scoped_lock lk{unmap_mutex};
|
||||
// If this is a checked free, then all direct memory in range must be allocated.
|
||||
std::vector<std::pair<PAddr, u64>> free_list;
|
||||
u64 remaining_size = size;
|
||||
@ -316,6 +317,17 @@ s32 MemoryManager::Free(PAddr phys_addr, u64 size, bool is_checked) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Early unmap from GPU to avoid deadlocking.
|
||||
for (auto& [addr, unmap_size] : remove_list) {
|
||||
if (IsValidGpuMapping(addr, unmap_size)) {
|
||||
rasterizer->UnmapMemory(addr, unmap_size);
|
||||
}
|
||||
}
|
||||
|
||||
// Acquire writer lock
|
||||
std::scoped_lock lk2{mutex};
|
||||
|
||||
for (const auto& [addr, size] : remove_list) {
|
||||
LOG_INFO(Kernel_Vmm, "Unmapping direct mapping {:#x} with size {:#x}", addr, size);
|
||||
UnmapMemoryImpl(addr, size);
|
||||
@ -337,7 +349,7 @@ s32 MemoryManager::Free(PAddr phys_addr, u64 size, bool is_checked) {
|
||||
}
|
||||
|
||||
s32 MemoryManager::PoolCommit(VAddr virtual_addr, u64 size, MemoryProt prot, s32 mtype) {
|
||||
std::scoped_lock lk{mutex};
|
||||
std::scoped_lock lk{mutex, unmap_mutex};
|
||||
ASSERT_MSG(IsValidMapping(virtual_addr, size), "Attempted to access invalid address {:#x}",
|
||||
virtual_addr);
|
||||
|
||||
@ -429,54 +441,31 @@ s32 MemoryManager::PoolCommit(VAddr virtual_addr, u64 size, MemoryProt prot, s32
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
std::pair<s32, MemoryManager::VMAHandle> MemoryManager::CreateArea(
|
||||
VAddr virtual_addr, u64 size, MemoryProt prot, MemoryMapFlags flags, VMAType type,
|
||||
std::string_view name, u64 alignment) {
|
||||
|
||||
// Limit the minimum address to SystemManagedVirtualBase to prevent hardware-specific issues.
|
||||
VAddr mapped_addr = (virtual_addr == 0) ? impl.SystemManagedVirtualBase() : virtual_addr;
|
||||
|
||||
// Fixed mapping means the virtual address must exactly match the provided one.
|
||||
// On a PS4, the Fixed flag is ignored if address 0 is provided.
|
||||
if (True(flags & MemoryMapFlags::Fixed) && virtual_addr != 0) {
|
||||
ASSERT_MSG(IsValidMapping(mapped_addr, size), "Attempted to access invalid address {:#x}",
|
||||
mapped_addr);
|
||||
auto vma = FindVMA(mapped_addr)->second;
|
||||
// There's a possible edge case where we're mapping to a partially reserved range.
|
||||
// To account for this, unmap any reserved areas within this mapping range first.
|
||||
auto unmap_addr = mapped_addr;
|
||||
MemoryManager::VMAHandle MemoryManager::CreateArea(VAddr virtual_addr, u64 size, MemoryProt prot,
|
||||
MemoryMapFlags flags, VMAType type,
|
||||
std::string_view name, u64 alignment) {
|
||||
// Locate the VMA representing the requested region
|
||||
auto vma = FindVMA(virtual_addr)->second;
|
||||
if (True(flags & MemoryMapFlags::Fixed)) {
|
||||
// If fixed is specified, map directly to the region of virtual_addr + size.
|
||||
// Callers should check to ensure the NoOverwrite flag is handled appropriately beforehand.
|
||||
auto unmap_addr = virtual_addr;
|
||||
auto unmap_size = size;
|
||||
|
||||
// If flag NoOverwrite is provided, don't overwrite mapped VMAs.
|
||||
// When it isn't provided, VMAs can be overwritten regardless of if they're mapped.
|
||||
while ((False(flags & MemoryMapFlags::NoOverwrite) || vma.IsFree()) &&
|
||||
unmap_addr < mapped_addr + size) {
|
||||
while (unmap_size > 0) {
|
||||
auto unmapped = UnmapBytesFromEntry(unmap_addr, vma, unmap_size);
|
||||
unmap_addr += unmapped;
|
||||
unmap_size -= unmapped;
|
||||
vma = FindVMA(unmap_addr)->second;
|
||||
}
|
||||
|
||||
vma = FindVMA(mapped_addr)->second;
|
||||
auto remaining_size = vma.base + vma.size - mapped_addr;
|
||||
if (!vma.IsFree() || remaining_size < size) {
|
||||
LOG_ERROR(Kernel_Vmm, "Unable to map {:#x} bytes at address {:#x}", size, mapped_addr);
|
||||
return {ORBIS_KERNEL_ERROR_ENOMEM, vma_map.end()};
|
||||
}
|
||||
} else {
|
||||
// When MemoryMapFlags::Fixed is not specified, and mapped_addr is 0,
|
||||
// search from address 0x200000000 instead.
|
||||
alignment = alignment > 0 ? alignment : 16_KB;
|
||||
mapped_addr = virtual_addr == 0 ? 0x200000000 : mapped_addr;
|
||||
mapped_addr = SearchFree(mapped_addr, size, alignment);
|
||||
if (mapped_addr == -1) {
|
||||
// No suitable memory areas to map to
|
||||
return {ORBIS_KERNEL_ERROR_ENOMEM, vma_map.end()};
|
||||
}
|
||||
}
|
||||
vma = FindVMA(virtual_addr)->second;
|
||||
|
||||
// By this point, vma should be free and ready to map.
|
||||
// Caller performs address searches for non-fixed mappings before this.
|
||||
ASSERT_MSG(vma.IsFree(), "VMA to map is not free");
|
||||
|
||||
// Create a memory area representing this mapping.
|
||||
const auto new_vma_handle = CarveVMA(mapped_addr, size);
|
||||
const auto new_vma_handle = CarveVMA(virtual_addr, size);
|
||||
auto& new_vma = new_vma_handle->second;
|
||||
const bool is_exec = True(prot & MemoryProt::CpuExec);
|
||||
if (True(prot & MemoryProt::CpuWrite)) {
|
||||
@ -484,12 +473,13 @@ std::pair<s32, MemoryManager::VMAHandle> MemoryManager::CreateArea(
|
||||
prot |= MemoryProt::CpuRead;
|
||||
}
|
||||
|
||||
// Update VMA appropriately.
|
||||
new_vma.disallow_merge = True(flags & MemoryMapFlags::NoCoalesce);
|
||||
new_vma.prot = prot;
|
||||
new_vma.name = name;
|
||||
new_vma.type = type;
|
||||
new_vma.phys_areas.clear();
|
||||
return {ORBIS_OK, new_vma_handle};
|
||||
return new_vma_handle;
|
||||
}
|
||||
|
||||
s32 MemoryManager::MapMemory(void** out_addr, VAddr virtual_addr, u64 size, MemoryProt prot,
|
||||
@ -504,8 +494,7 @@ s32 MemoryManager::MapMemory(void** out_addr, VAddr virtual_addr, u64 size, Memo
|
||||
total_flexible_size - flexible_usage, size);
|
||||
return ORBIS_KERNEL_ERROR_EINVAL;
|
||||
}
|
||||
|
||||
std::scoped_lock lk{mutex};
|
||||
std::scoped_lock lk{unmap_mutex};
|
||||
|
||||
PhysHandle dmem_area;
|
||||
// Validate the requested physical address range
|
||||
@ -538,12 +527,37 @@ s32 MemoryManager::MapMemory(void** out_addr, VAddr virtual_addr, u64 size, Memo
|
||||
}
|
||||
}
|
||||
|
||||
auto [result, new_vma_handle] =
|
||||
CreateArea(virtual_addr, size, prot, flags, type, name, alignment);
|
||||
if (result != ORBIS_OK) {
|
||||
return result;
|
||||
if (True(flags & MemoryMapFlags::Fixed) && True(flags & MemoryMapFlags::NoOverwrite)) {
|
||||
// Perform necessary error checking for Fixed & NoOverwrite case
|
||||
ASSERT_MSG(IsValidMapping(virtual_addr, size), "Attempted to access invalid address {:#x}",
|
||||
virtual_addr);
|
||||
auto vma = FindVMA(virtual_addr)->second;
|
||||
auto remaining_size = vma.base + vma.size - virtual_addr;
|
||||
if (!vma.IsFree() || remaining_size < size) {
|
||||
LOG_ERROR(Kernel_Vmm, "Unable to map {:#x} bytes at address {:#x}", size, virtual_addr);
|
||||
return ORBIS_KERNEL_ERROR_ENOMEM;
|
||||
}
|
||||
} else if (False(flags & MemoryMapFlags::Fixed)) {
|
||||
// Find a free virtual addr to map
|
||||
alignment = alignment > 0 ? alignment : 16_KB;
|
||||
virtual_addr = virtual_addr == 0 ? DEFAULT_MAPPING_BASE : virtual_addr;
|
||||
virtual_addr = SearchFree(virtual_addr, size, alignment);
|
||||
if (virtual_addr == -1) {
|
||||
// No suitable memory areas to map to
|
||||
return ORBIS_KERNEL_ERROR_ENOMEM;
|
||||
}
|
||||
}
|
||||
|
||||
// Perform early GPU unmap to avoid potential deadlocks
|
||||
if (IsValidGpuMapping(virtual_addr, size)) {
|
||||
rasterizer->UnmapMemory(virtual_addr, size);
|
||||
}
|
||||
|
||||
// Acquire writer lock.
|
||||
std::scoped_lock lk2{mutex};
|
||||
|
||||
// Create VMA representing this mapping.
|
||||
auto new_vma_handle = CreateArea(virtual_addr, size, prot, flags, type, name, alignment);
|
||||
auto& new_vma = new_vma_handle->second;
|
||||
auto mapped_addr = new_vma.base;
|
||||
bool is_exec = True(prot & MemoryProt::CpuExec);
|
||||
@ -590,7 +604,7 @@ s32 MemoryManager::MapMemory(void** out_addr, VAddr virtual_addr, u64 size, Memo
|
||||
// Map the physical memory for this direct memory mapping.
|
||||
auto phys_addr_to_search = phys_addr;
|
||||
u64 remaining_size = size;
|
||||
dmem_area = FindDmemArea(phys_addr);
|
||||
auto dmem_area = FindDmemArea(phys_addr);
|
||||
while (dmem_area != dmem_map.end() && remaining_size > 0) {
|
||||
// Carve a new dmem area in place of this one with the appropriate type.
|
||||
// Ensure the carved area only covers the current dmem area.
|
||||
@ -638,14 +652,15 @@ s32 MemoryManager::MapMemory(void** out_addr, VAddr virtual_addr, u64 size, Memo
|
||||
rasterizer->MapMemory(mapped_addr, size);
|
||||
}
|
||||
}
|
||||
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
s32 MemoryManager::MapFile(void** out_addr, VAddr virtual_addr, u64 size, MemoryProt prot,
|
||||
MemoryMapFlags flags, s32 fd, s64 phys_addr) {
|
||||
std::scoped_lock lk{mutex};
|
||||
uintptr_t handle = 0;
|
||||
std::scoped_lock lk{unmap_mutex};
|
||||
// Get the file to map
|
||||
|
||||
auto* h = Common::Singleton<Core::FileSys::HandleTable>::Instance();
|
||||
auto file = h->GetFile(fd);
|
||||
if (file == nullptr) {
|
||||
@ -663,12 +678,13 @@ s32 MemoryManager::MapFile(void** out_addr, VAddr virtual_addr, u64 size, Memory
|
||||
prot |= MemoryProt::CpuRead;
|
||||
}
|
||||
|
||||
const auto handle = file->f.GetFileMapping();
|
||||
handle = file->f.GetFileMapping();
|
||||
|
||||
if (False(file->f.GetAccessMode() & Common::FS::FileAccessMode::Write) ||
|
||||
False(file->f.GetAccessMode() & Common::FS::FileAccessMode::Append)) {
|
||||
// If the file does not have write access, ensure prot does not contain write permissions.
|
||||
// On real hardware, these mappings succeed, but the memory cannot be written to.
|
||||
// If the file does not have write access, ensure prot does not contain write
|
||||
// permissions. On real hardware, these mappings succeed, but the memory cannot be
|
||||
// written to.
|
||||
prot &= ~MemoryProt::CpuWrite;
|
||||
}
|
||||
|
||||
@ -682,13 +698,38 @@ s32 MemoryManager::MapFile(void** out_addr, VAddr virtual_addr, u64 size, Memory
|
||||
prot &= ~MemoryProt::CpuExec;
|
||||
}
|
||||
|
||||
auto [result, new_vma_handle] =
|
||||
CreateArea(virtual_addr, size, prot, flags, VMAType::File, "anon", 0);
|
||||
if (result != ORBIS_OK) {
|
||||
return result;
|
||||
if (True(flags & MemoryMapFlags::Fixed) && False(flags & MemoryMapFlags::NoOverwrite)) {
|
||||
ASSERT_MSG(IsValidMapping(virtual_addr, size), "Attempted to access invalid address {:#x}",
|
||||
virtual_addr);
|
||||
auto vma = FindVMA(virtual_addr)->second;
|
||||
|
||||
auto remaining_size = vma.base + vma.size - virtual_addr;
|
||||
if (!vma.IsFree() || remaining_size < size) {
|
||||
LOG_ERROR(Kernel_Vmm, "Unable to map {:#x} bytes at address {:#x}", size, virtual_addr);
|
||||
return ORBIS_KERNEL_ERROR_ENOMEM;
|
||||
}
|
||||
} else if (False(flags & MemoryMapFlags::Fixed)) {
|
||||
virtual_addr = virtual_addr == 0 ? DEFAULT_MAPPING_BASE : virtual_addr;
|
||||
virtual_addr = SearchFree(virtual_addr, size, 16_KB);
|
||||
if (virtual_addr == -1) {
|
||||
// No suitable memory areas to map to
|
||||
return ORBIS_KERNEL_ERROR_ENOMEM;
|
||||
}
|
||||
}
|
||||
|
||||
// Perform early GPU unmap to avoid potential deadlocks
|
||||
if (IsValidGpuMapping(virtual_addr, size)) {
|
||||
rasterizer->UnmapMemory(virtual_addr, size);
|
||||
}
|
||||
|
||||
// Aquire writer lock
|
||||
std::scoped_lock lk2{mutex};
|
||||
|
||||
// Update VMA map and map to address space.
|
||||
auto new_vma_handle = CreateArea(virtual_addr, size, prot, flags, VMAType::File, "anon", 0);
|
||||
|
||||
auto& new_vma = new_vma_handle->second;
|
||||
new_vma.fd = fd;
|
||||
auto mapped_addr = new_vma.base;
|
||||
bool is_exec = True(prot & MemoryProt::CpuExec);
|
||||
|
||||
@ -699,7 +740,7 @@ s32 MemoryManager::MapFile(void** out_addr, VAddr virtual_addr, u64 size, Memory
|
||||
}
|
||||
|
||||
s32 MemoryManager::PoolDecommit(VAddr virtual_addr, u64 size) {
|
||||
std::scoped_lock lk{mutex};
|
||||
std::scoped_lock lk{unmap_mutex};
|
||||
ASSERT_MSG(IsValidMapping(virtual_addr, size), "Attempted to access invalid address {:#x}",
|
||||
virtual_addr);
|
||||
|
||||
@ -713,6 +754,14 @@ s32 MemoryManager::PoolDecommit(VAddr virtual_addr, u64 size) {
|
||||
it++;
|
||||
}
|
||||
|
||||
// Perform early GPU unmap to avoid potential deadlocks
|
||||
if (IsValidGpuMapping(virtual_addr, size)) {
|
||||
rasterizer->UnmapMemory(virtual_addr, size);
|
||||
}
|
||||
|
||||
// Aquire writer mutex
|
||||
std::scoped_lock lk2{mutex};
|
||||
|
||||
// Loop through all vmas in the area, unmap them.
|
||||
u64 remaining_size = size;
|
||||
VAddr current_addr = virtual_addr;
|
||||
@ -721,13 +770,7 @@ s32 MemoryManager::PoolDecommit(VAddr virtual_addr, u64 size) {
|
||||
const auto& vma_base = handle->second;
|
||||
const auto start_in_vma = current_addr - vma_base.base;
|
||||
const auto size_in_vma = std::min<u64>(remaining_size, vma_base.size - start_in_vma);
|
||||
|
||||
if (vma_base.type == VMAType::Pooled) {
|
||||
// We always map PoolCommitted memory to GPU, so unmap when decomitting.
|
||||
if (IsValidGpuMapping(current_addr, size_in_vma)) {
|
||||
rasterizer->UnmapMemory(current_addr, size_in_vma);
|
||||
}
|
||||
|
||||
// Track how much pooled memory is decommitted
|
||||
pool_budget += size_in_vma;
|
||||
|
||||
@ -772,7 +815,7 @@ s32 MemoryManager::PoolDecommit(VAddr virtual_addr, u64 size) {
|
||||
}
|
||||
|
||||
// Unmap from address space
|
||||
impl.Unmap(virtual_addr, size, true);
|
||||
impl.Unmap(virtual_addr, size);
|
||||
// Tracy memory tracking breaks from merging memory areas. Disabled for now.
|
||||
// TRACK_FREE(virtual_addr, "VMEM");
|
||||
|
||||
@ -783,29 +826,32 @@ s32 MemoryManager::UnmapMemory(VAddr virtual_addr, u64 size) {
|
||||
if (size == 0) {
|
||||
return ORBIS_OK;
|
||||
}
|
||||
std::scoped_lock lk{mutex};
|
||||
|
||||
std::scoped_lock lk{unmap_mutex};
|
||||
// Align address and size appropriately
|
||||
virtual_addr = Common::AlignDown(virtual_addr, 16_KB);
|
||||
size = Common::AlignUp(size, 16_KB);
|
||||
ASSERT_MSG(IsValidMapping(virtual_addr, size), "Attempted to access invalid address {:#x}",
|
||||
virtual_addr);
|
||||
u64 bytes_unmapped = UnmapMemoryImpl(virtual_addr, size);
|
||||
return bytes_unmapped;
|
||||
|
||||
// If the requested range has GPU access, unmap from GPU.
|
||||
if (IsValidGpuMapping(virtual_addr, size)) {
|
||||
rasterizer->UnmapMemory(virtual_addr, size);
|
||||
}
|
||||
|
||||
// Acquire writer lock.
|
||||
std::scoped_lock lk2{mutex};
|
||||
return UnmapMemoryImpl(virtual_addr, size);
|
||||
}
|
||||
|
||||
u64 MemoryManager::UnmapBytesFromEntry(VAddr virtual_addr, VirtualMemoryArea vma_base, u64 size) {
|
||||
const auto start_in_vma = virtual_addr - vma_base.base;
|
||||
const auto size_in_vma = std::min<u64>(vma_base.size - start_in_vma, size);
|
||||
const auto vma_type = vma_base.type;
|
||||
const bool has_backing = HasPhysicalBacking(vma_base) || vma_base.type == VMAType::File;
|
||||
const bool readonly_file =
|
||||
vma_base.prot == MemoryProt::CpuRead && vma_base.type == VMAType::File;
|
||||
const bool is_exec = True(vma_base.prot & MemoryProt::CpuExec);
|
||||
|
||||
if (vma_base.type == VMAType::Free || vma_base.type == VMAType::Pooled) {
|
||||
return size_in_vma;
|
||||
}
|
||||
|
||||
PAddr phys_base = 0;
|
||||
VAddr current_addr = virtual_addr;
|
||||
if (vma_base.phys_areas.size() > 0) {
|
||||
u64 size_to_free = size_in_vma;
|
||||
@ -860,14 +906,9 @@ u64 MemoryManager::UnmapBytesFromEntry(VAddr virtual_addr, VirtualMemoryArea vma
|
||||
|
||||
if (vma_type != VMAType::Reserved && vma_type != VMAType::PoolReserved) {
|
||||
// Unmap the memory region.
|
||||
impl.Unmap(virtual_addr, size_in_vma, has_backing);
|
||||
impl.Unmap(virtual_addr, size_in_vma);
|
||||
// Tracy memory tracking breaks from merging memory areas. Disabled for now.
|
||||
// TRACK_FREE(virtual_addr, "VMEM");
|
||||
|
||||
// If this mapping has GPU access, unmap from GPU.
|
||||
if (IsValidGpuMapping(virtual_addr, size)) {
|
||||
rasterizer->UnmapMemory(virtual_addr, size);
|
||||
}
|
||||
}
|
||||
return size_in_vma;
|
||||
}
|
||||
@ -983,7 +1024,7 @@ s32 MemoryManager::Protect(VAddr addr, u64 size, MemoryProt prot) {
|
||||
}
|
||||
|
||||
// Ensure the range to modify is valid
|
||||
std::scoped_lock lk{mutex};
|
||||
std::scoped_lock lk{mutex, unmap_mutex};
|
||||
ASSERT_MSG(IsValidMapping(addr, size), "Attempted to access invalid address {:#x}", addr);
|
||||
|
||||
// Appropriately restrict flags.
|
||||
@ -1141,7 +1182,7 @@ s32 MemoryManager::DirectQueryAvailable(PAddr search_start, PAddr search_end, u6
|
||||
}
|
||||
|
||||
s32 MemoryManager::SetDirectMemoryType(VAddr addr, u64 size, s32 memory_type) {
|
||||
std::scoped_lock lk{mutex};
|
||||
std::scoped_lock lk{mutex, unmap_mutex};
|
||||
|
||||
ASSERT_MSG(IsValidMapping(addr, size), "Attempted to access invalid address {:#x}", addr);
|
||||
|
||||
@ -1188,7 +1229,7 @@ s32 MemoryManager::SetDirectMemoryType(VAddr addr, u64 size, s32 memory_type) {
|
||||
}
|
||||
|
||||
void MemoryManager::NameVirtualRange(VAddr virtual_addr, u64 size, std::string_view name) {
|
||||
std::scoped_lock lk{mutex};
|
||||
std::scoped_lock lk{mutex, unmap_mutex};
|
||||
|
||||
// Sizes are aligned up to the nearest 16_KB
|
||||
u64 aligned_size = Common::AlignUp(size, 16_KB);
|
||||
@ -1246,7 +1287,6 @@ s32 MemoryManager::IsStack(VAddr addr, void** start, void** end) {
|
||||
ASSERT_MSG(IsValidMapping(addr), "Attempted to access invalid address {:#x}", addr);
|
||||
const auto& vma = FindVMA(addr)->second;
|
||||
if (vma.IsFree()) {
|
||||
mutex.unlock_shared();
|
||||
return ORBIS_KERNEL_ERROR_EACCES;
|
||||
}
|
||||
|
||||
|
||||
@ -28,6 +28,8 @@ class MemoryMapViewer;
|
||||
|
||||
namespace Core {
|
||||
|
||||
constexpr u64 DEFAULT_MAPPING_BASE = 0x200000000;
|
||||
|
||||
enum class MemoryProt : u32 {
|
||||
NoAccess = 0,
|
||||
CpuRead = 1,
|
||||
@ -304,10 +306,8 @@ private:
|
||||
vma.type == VMAType::Pooled;
|
||||
}
|
||||
|
||||
std::pair<s32, MemoryManager::VMAHandle> CreateArea(VAddr virtual_addr, u64 size,
|
||||
MemoryProt prot, MemoryMapFlags flags,
|
||||
VMAType type, std::string_view name,
|
||||
u64 alignment);
|
||||
VMAHandle CreateArea(VAddr virtual_addr, u64 size, MemoryProt prot, MemoryMapFlags flags,
|
||||
VMAType type, std::string_view name, u64 alignment);
|
||||
|
||||
VAddr SearchFree(VAddr virtual_addr, u64 size, u32 alignment);
|
||||
|
||||
@ -333,6 +333,7 @@ private:
|
||||
PhysMap fmem_map;
|
||||
VMAMap vma_map;
|
||||
Common::SharedFirstMutex mutex{};
|
||||
std::mutex unmap_mutex{};
|
||||
u64 total_direct_size{};
|
||||
u64 total_flexible_size{};
|
||||
u64 flexible_usage{};
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 shadPS4 Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2025-2026 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <filesystem>
|
||||
@ -96,7 +96,7 @@ s32 ReadCompiledSdkVersion(const std::filesystem::path& file) {
|
||||
|
||||
void Emulator::Run(std::filesystem::path file, std::vector<std::string> args,
|
||||
std::optional<std::filesystem::path> p_game_folder) {
|
||||
Common::SetCurrentThreadName("Main Thread");
|
||||
Common::SetCurrentThreadName("shadPS4:Main");
|
||||
if (waitForDebuggerBeforeRun) {
|
||||
Debugger::WaitForDebuggerAttach();
|
||||
}
|
||||
|
||||
@ -219,6 +219,7 @@ public:
|
||||
void V_NOT_B32(const GcnInst& inst);
|
||||
void V_BFREV_B32(const GcnInst& inst);
|
||||
void V_FFBH_U32(const GcnInst& inst);
|
||||
void V_FFBH_I32(const GcnInst& inst);
|
||||
void V_FFBL_B32(const GcnInst& inst);
|
||||
void V_FREXP_EXP_I32_F64(const GcnInst& inst);
|
||||
void V_FREXP_MANT_F64(const GcnInst& inst);
|
||||
@ -231,6 +232,7 @@ public:
|
||||
|
||||
// VOPC
|
||||
void V_CMP_F32(ConditionOp op, bool set_exec, const GcnInst& inst);
|
||||
void V_CMP_F64(ConditionOp op, bool set_exec, const GcnInst& inst);
|
||||
void V_CMP_U32(ConditionOp op, bool is_signed, bool set_exec, const GcnInst& inst);
|
||||
void V_CMP_U64(ConditionOp op, bool is_signed, bool set_exec, const GcnInst& inst);
|
||||
void V_CMP_CLASS_F32(const GcnInst& inst);
|
||||
@ -259,6 +261,7 @@ public:
|
||||
void V_CVT_PK_I16_I32(const GcnInst& inst);
|
||||
void V_CVT_PK_U8_F32(const GcnInst& inst);
|
||||
void V_LSHL_B64(const GcnInst& inst);
|
||||
void V_LSHR_B64(const GcnInst& inst);
|
||||
void V_ALIGNBIT_B32(const GcnInst& inst);
|
||||
void V_ALIGNBYTE_B32(const GcnInst& inst);
|
||||
void V_MUL_F64(const GcnInst& inst);
|
||||
|
||||
@ -188,6 +188,8 @@ void Translator::EmitVectorAlu(const GcnInst& inst) {
|
||||
return V_FFBH_U32(inst);
|
||||
case Opcode::V_FFBL_B32:
|
||||
return V_FFBL_B32(inst);
|
||||
case Opcode::V_FFBH_I32:
|
||||
return V_FFBH_I32(inst);
|
||||
case Opcode::V_FREXP_EXP_I32_F64:
|
||||
return V_FREXP_EXP_I32_F64(inst);
|
||||
case Opcode::V_FREXP_MANT_F64:
|
||||
@ -264,6 +266,34 @@ void Translator::EmitVectorAlu(const GcnInst& inst) {
|
||||
case Opcode::V_CMPX_TRU_F32:
|
||||
return V_CMP_F32(ConditionOp::TRU, true, inst);
|
||||
|
||||
// V_CMP_{OP16}_F64
|
||||
case Opcode::V_CMP_F_F64:
|
||||
return V_CMP_F64(ConditionOp::F, false, inst);
|
||||
case Opcode::V_CMP_LT_F64:
|
||||
return V_CMP_F64(ConditionOp::LT, false, inst);
|
||||
case Opcode::V_CMP_EQ_F64:
|
||||
return V_CMP_F64(ConditionOp::EQ, false, inst);
|
||||
case Opcode::V_CMP_LE_F64:
|
||||
return V_CMP_F64(ConditionOp::LE, false, inst);
|
||||
case Opcode::V_CMP_GT_F64:
|
||||
return V_CMP_F64(ConditionOp::GT, false, inst);
|
||||
case Opcode::V_CMP_LG_F64:
|
||||
return V_CMP_F64(ConditionOp::LG, false, inst);
|
||||
case Opcode::V_CMP_GE_F64:
|
||||
return V_CMP_F64(ConditionOp::GE, false, inst);
|
||||
case Opcode::V_CMP_U_F64:
|
||||
return V_CMP_F64(ConditionOp::U, false, inst);
|
||||
case Opcode::V_CMP_NGE_F64:
|
||||
return V_CMP_F64(ConditionOp::LT, false, inst);
|
||||
case Opcode::V_CMP_NGT_F64:
|
||||
return V_CMP_F64(ConditionOp::LE, false, inst);
|
||||
case Opcode::V_CMP_NLE_F64:
|
||||
return V_CMP_F64(ConditionOp::GT, false, inst);
|
||||
case Opcode::V_CMP_NEQ_F64:
|
||||
return V_CMP_F64(ConditionOp::LG, false, inst);
|
||||
case Opcode::V_CMP_NLT_F64:
|
||||
return V_CMP_F64(ConditionOp::GE, false, inst);
|
||||
|
||||
// V_CMP_{OP8}_I32
|
||||
case Opcode::V_CMP_LT_I32:
|
||||
return V_CMP_U32(ConditionOp::LT, true, false, inst);
|
||||
@ -394,6 +424,8 @@ void Translator::EmitVectorAlu(const GcnInst& inst) {
|
||||
return V_CVT_PK_U8_F32(inst);
|
||||
case Opcode::V_LSHL_B64:
|
||||
return V_LSHL_B64(inst);
|
||||
case Opcode::V_LSHR_B64:
|
||||
return V_LSHR_B64(inst);
|
||||
case Opcode::V_ADD_F64:
|
||||
return V_ADD_F64(inst);
|
||||
case Opcode::V_ALIGNBIT_B32:
|
||||
@ -918,6 +950,19 @@ void Translator::V_FFBL_B32(const GcnInst& inst) {
|
||||
SetDst(inst.dst[0], ir.FindILsb(src0));
|
||||
}
|
||||
|
||||
void Translator::V_FFBH_I32(const GcnInst& inst) {
|
||||
const IR::U32 src0{GetSrc(inst.src[0])};
|
||||
// Gcn wants the MSB position counting from the left, but SPIR-V counts from the rightmost (LSB)
|
||||
// position
|
||||
const IR::U32 msb_pos = ir.FindSMsb(src0);
|
||||
const IR::U32 pos_from_left = ir.ISub(ir.Imm32(31), msb_pos);
|
||||
// Select 0xFFFFFFFF if src0 was 0 or -1
|
||||
const IR::U32 minusOne = ir.Imm32(~0U);
|
||||
const IR::U1 cond =
|
||||
ir.LogicalAnd(ir.INotEqual(src0, ir.Imm32(0)), ir.INotEqual(src0, minusOne));
|
||||
SetDst(inst.dst[0], IR::U32{ir.Select(cond, pos_from_left, minusOne)});
|
||||
}
|
||||
|
||||
void Translator::V_FREXP_EXP_I32_F64(const GcnInst& inst) {
|
||||
const IR::F64 src0{GetSrc64<IR::F64>(inst.src[0])};
|
||||
SetDst(inst.dst[0], ir.FPFrexpExp(src0));
|
||||
@ -1011,6 +1056,47 @@ void Translator::V_CMP_F32(ConditionOp op, bool set_exec, const GcnInst& inst) {
|
||||
}
|
||||
}
|
||||
|
||||
void Translator::V_CMP_F64(ConditionOp op, bool set_exec, const GcnInst& inst) {
|
||||
const IR::F64 src0{GetSrc64<IR::F64>(inst.src[0])};
|
||||
const IR::F64 src1{GetSrc64<IR::F64>(inst.src[1])};
|
||||
const IR::U1 result = [&] {
|
||||
switch (op) {
|
||||
case ConditionOp::F:
|
||||
return ir.Imm1(false);
|
||||
case ConditionOp::EQ:
|
||||
return ir.FPEqual(src0, src1);
|
||||
case ConditionOp::LG:
|
||||
return ir.FPNotEqual(src0, src1);
|
||||
case ConditionOp::GT:
|
||||
return ir.FPGreaterThan(src0, src1);
|
||||
case ConditionOp::LT:
|
||||
return ir.FPLessThan(src0, src1);
|
||||
case ConditionOp::LE:
|
||||
return ir.FPLessThanEqual(src0, src1);
|
||||
case ConditionOp::GE:
|
||||
return ir.FPGreaterThanEqual(src0, src1);
|
||||
case ConditionOp::U:
|
||||
return ir.LogicalOr(ir.FPIsNan(src0), ir.FPIsNan(src1));
|
||||
default:
|
||||
UNREACHABLE();
|
||||
}
|
||||
}();
|
||||
if (set_exec) {
|
||||
ir.SetExec(result);
|
||||
}
|
||||
|
||||
switch (inst.dst[1].field) {
|
||||
case OperandField::VccLo:
|
||||
ir.SetVcc(result);
|
||||
break;
|
||||
case OperandField::ScalarGPR:
|
||||
ir.SetThreadBitScalarReg(IR::ScalarReg(inst.dst[1].code), result);
|
||||
break;
|
||||
default:
|
||||
UNREACHABLE();
|
||||
}
|
||||
}
|
||||
|
||||
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])};
|
||||
@ -1357,6 +1443,12 @@ void Translator::V_LSHL_B64(const GcnInst& inst) {
|
||||
SetDst64(inst.dst[0], ir.ShiftLeftLogical(src0, ir.BitwiseAnd(src1, ir.Imm64(u64(0x3F)))));
|
||||
}
|
||||
|
||||
void Translator::V_LSHR_B64(const GcnInst& inst) {
|
||||
const IR::U64 src0{GetSrc64(inst.src[0])};
|
||||
const IR::U64 src1{GetSrc64(inst.src[1])};
|
||||
SetDst64(inst.dst[0], ir.ShiftRightLogical(src0, ir.BitwiseAnd(src1, ir.Imm64(u64(0x3F)))));
|
||||
}
|
||||
|
||||
void Translator::V_ALIGNBIT_B32(const GcnInst& inst) {
|
||||
const IR::U32 src0{GetSrc(inst.src[0])};
|
||||
const IR::U32 src1{GetSrc(inst.src[1])};
|
||||
|
||||
Loading…
Reference in New Issue
Block a user