mirror of
https://github.com/shadps4-emu/shadPS4.git
synced 2026-04-03 03:18:09 -06:00
Merge branch 'main' into ime-fixes-again
This commit is contained in:
commit
eaf5a0488b
@ -227,6 +227,8 @@ find_package(fmt 10.2.0 CONFIG)
|
||||
find_package(glslang 15 CONFIG)
|
||||
find_package(half 1.12.0 MODULE)
|
||||
find_package(magic_enum 0.9.7 CONFIG)
|
||||
find_package(miniz 3.1 CONFIG)
|
||||
find_package(nlohmann_json 3.12 CONFIG)
|
||||
find_package(PNG 1.6 MODULE)
|
||||
find_package(OpenAL CONFIG)
|
||||
find_package(RenderDoc 1.6.0 MODULE)
|
||||
@ -1118,7 +1120,7 @@ create_target_directory_groups(shadps4)
|
||||
|
||||
target_link_libraries(shadps4 PRIVATE magic_enum::magic_enum fmt::fmt toml11::toml11 tsl::robin_map xbyak::xbyak Tracy::TracyClient RenderDoc::API FFmpeg::ffmpeg Dear_ImGui gcn half::half ZLIB::ZLIB PNG::PNG)
|
||||
target_link_libraries(shadps4 PRIVATE Boost::headers GPUOpen::VulkanMemoryAllocator LibAtrac9 sirit Vulkan::Headers xxHash::xxhash Zydis::Zydis glslang::glslang SDL3::SDL3 SDL3_mixer::SDL3_mixer pugixml::pugixml)
|
||||
target_link_libraries(shadps4 PRIVATE stb::headers libusb::usb lfreist-hwinfo::hwinfo nlohmann_json::nlohmann_json miniz fdk-aac CLI11::CLI11 OpenAL::OpenAL Cpp_Httplib)
|
||||
target_link_libraries(shadps4 PRIVATE stb::headers libusb::usb lfreist-hwinfo::hwinfo nlohmann_json::nlohmann_json miniz::miniz fdk-aac CLI11::CLI11 OpenAL::OpenAL Cpp_Httplib)
|
||||
|
||||
target_compile_definitions(shadps4 PRIVATE IMGUI_USER_CONFIG="imgui/imgui_config.h")
|
||||
target_compile_definitions(Dear_ImGui PRIVATE IMGUI_USER_CONFIG="${PROJECT_SOURCE_DIR}/src/imgui/imgui_config.h")
|
||||
|
||||
@ -73,6 +73,8 @@ You can configure the emulator by editing the `config.toml` file found in the `u
|
||||
- Examples:
|
||||
- If the log is being spammed with messages coming from Lib.Pad, you can use `Lib.Pad:Critical` to only log critical-level messages.
|
||||
- If you'd like to mute everything, but still want to receive messages from Vulkan rendering: `*:Critical Render.Vulkan:Info`
|
||||
- `isIdenticalLogGrouped`: Group same logs in one line with a counter (`true`/`false`)
|
||||
- By default, the emulator will not rewrite the same line, and instead add a counter.
|
||||
|
||||
- `Fullscreen`: Display the game in a full screen borderless window.
|
||||
|
||||
|
||||
7
externals/CMakeLists.txt
vendored
7
externals/CMakeLists.txt
vendored
@ -259,16 +259,19 @@ if (WIN32)
|
||||
add_subdirectory(ext-wepoll)
|
||||
endif()
|
||||
|
||||
if (NOT TARGET fdk-aac)
|
||||
add_subdirectory(aacdec)
|
||||
endif()
|
||||
|
||||
#nlohmann json
|
||||
if (NOT TARGET nlohmann_json::nlohmann_json)
|
||||
set(JSON_BuildTests OFF CACHE INTERNAL "")
|
||||
add_subdirectory(json)
|
||||
endif()
|
||||
|
||||
# miniz
|
||||
if (NOT TARGET miniz::miniz)
|
||||
add_subdirectory(miniz)
|
||||
add_library(miniz::miniz ALIAS miniz)
|
||||
endif()
|
||||
|
||||
# cli11
|
||||
if (NOT TARGET CLI11::CLI11)
|
||||
|
||||
@ -141,6 +141,7 @@ static ConfigEntry<bool> isTrophyPopupDisabled(false);
|
||||
static ConfigEntry<double> trophyNotificationDuration(6.0);
|
||||
static ConfigEntry<string> logFilter("");
|
||||
static ConfigEntry<string> logType("sync");
|
||||
static ConfigEntry<bool> isIdenticalLogGrouped(true);
|
||||
static ConfigEntry<string> userName("shadPS4");
|
||||
static ConfigEntry<bool> isShowSplash(false);
|
||||
static ConfigEntry<string> isSideTrophy("right");
|
||||
@ -397,6 +398,10 @@ string getLogType() {
|
||||
return logType.get();
|
||||
}
|
||||
|
||||
bool groupIdenticalLogs() {
|
||||
return isIdenticalLogGrouped.get();
|
||||
}
|
||||
|
||||
string getUserName() {
|
||||
return userName.get();
|
||||
}
|
||||
@ -696,6 +701,10 @@ void setLogType(const string& type, bool is_game_specific) {
|
||||
logType.set(type, is_game_specific);
|
||||
}
|
||||
|
||||
void setIdenticalLogGrouped(bool enable, bool is_game_specific) {
|
||||
isIdenticalLogGrouped.set(enable, is_game_specific);
|
||||
}
|
||||
|
||||
void setLogFilter(const string& type, bool is_game_specific) {
|
||||
logFilter.set(type, is_game_specific);
|
||||
}
|
||||
@ -911,6 +920,7 @@ void load(const std::filesystem::path& path, bool is_game_specific) {
|
||||
enableDiscordRPC = toml::find_or<bool>(general, "enableDiscordRPC", enableDiscordRPC);
|
||||
logFilter.setFromToml(general, "logFilter", is_game_specific);
|
||||
logType.setFromToml(general, "logType", is_game_specific);
|
||||
isIdenticalLogGrouped.setFromToml(general, "isIdenticalLogGrouped", is_game_specific);
|
||||
userName.setFromToml(general, "userName", is_game_specific);
|
||||
isShowSplash.setFromToml(general, "showSplash", is_game_specific);
|
||||
isSideTrophy.setFromToml(general, "sideTrophy", is_game_specific);
|
||||
@ -1101,6 +1111,7 @@ void save(const std::filesystem::path& path, bool is_game_specific) {
|
||||
is_game_specific);
|
||||
logFilter.setTomlValue(data, "General", "logFilter", is_game_specific);
|
||||
logType.setTomlValue(data, "General", "logType", is_game_specific);
|
||||
isIdenticalLogGrouped.setTomlValue(data, "General", "isIdenticalLogGrouped", is_game_specific);
|
||||
userName.setTomlValue(data, "General", "userName", is_game_specific);
|
||||
isShowSplash.setTomlValue(data, "General", "showSplash", is_game_specific);
|
||||
isSideTrophy.setTomlValue(data, "General", "sideTrophy", is_game_specific);
|
||||
@ -1247,6 +1258,7 @@ void setDefaultValues(bool is_game_specific) {
|
||||
trophyNotificationDuration.set(6.0, is_game_specific);
|
||||
logFilter.set("", is_game_specific);
|
||||
logType.set("sync", is_game_specific);
|
||||
isIdenticalLogGrouped.set("isIdenticalLogGrouped", is_game_specific);
|
||||
userName.set("shadPS4", is_game_specific);
|
||||
isShowSplash.set(false, is_game_specific);
|
||||
isSideTrophy.set("right", is_game_specific);
|
||||
|
||||
@ -101,6 +101,8 @@ void setPipelineCacheEnabled(bool enable, bool is_game_specific = false);
|
||||
void setPipelineCacheArchived(bool enable, bool is_game_specific = false);
|
||||
std::string getLogType();
|
||||
void setLogType(const std::string& type, bool is_game_specific = false);
|
||||
bool groupIdenticalLogs();
|
||||
void setGroupIdenticalLogs(bool enable, bool is_game_specific = false);
|
||||
std::string getLogFilter();
|
||||
void setLogFilter(const std::string& type, bool is_game_specific = false);
|
||||
double getTrophyNotificationDuration();
|
||||
|
||||
@ -70,7 +70,7 @@ public:
|
||||
~FileBackend() = default;
|
||||
|
||||
void Write(const Entry& entry) {
|
||||
if (!enabled) {
|
||||
if (!enabled && entry.log_level != Level::Critical) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -209,41 +209,62 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
std::unique_lock entry_loc(_mutex);
|
||||
|
||||
if (_last_entry.message == message) {
|
||||
++_last_entry.counter;
|
||||
return;
|
||||
}
|
||||
|
||||
if (_last_entry.counter >= 2) {
|
||||
_last_entry.message += " x" + std::to_string(_last_entry.counter);
|
||||
}
|
||||
|
||||
if (_last_entry.counter >= 1) {
|
||||
if (Config::getLogType() == "async") {
|
||||
message_queue.EmplaceWait(_last_entry);
|
||||
} else {
|
||||
ForEachBackend([this](auto& backend) { backend.Write(this->_last_entry); });
|
||||
std::fflush(stdout);
|
||||
}
|
||||
}
|
||||
|
||||
using std::chrono::duration_cast;
|
||||
using std::chrono::microseconds;
|
||||
using std::chrono::steady_clock;
|
||||
|
||||
this->_last_entry = {
|
||||
.timestamp = duration_cast<microseconds>(steady_clock::now() - time_origin),
|
||||
.log_class = log_class,
|
||||
.log_level = log_level,
|
||||
.filename = filename,
|
||||
.line_num = line_num,
|
||||
.function = function,
|
||||
.message = message,
|
||||
.thread = Common::GetCurrentThreadName(),
|
||||
.counter = 1,
|
||||
};
|
||||
if (Config::groupIdenticalLogs()) {
|
||||
std::unique_lock entry_loc(_mutex);
|
||||
|
||||
if (_last_entry.message == message) {
|
||||
++_last_entry.counter;
|
||||
return;
|
||||
}
|
||||
|
||||
if (_last_entry.counter >= 2) {
|
||||
_last_entry.message += " x" + std::to_string(_last_entry.counter);
|
||||
}
|
||||
|
||||
if (_last_entry.counter >= 1) {
|
||||
if (Config::getLogType() == "async") {
|
||||
message_queue.EmplaceWait(_last_entry);
|
||||
} else {
|
||||
ForEachBackend([this](auto& backend) { backend.Write(this->_last_entry); });
|
||||
std::fflush(stdout);
|
||||
}
|
||||
}
|
||||
|
||||
this->_last_entry = {
|
||||
.timestamp = duration_cast<microseconds>(steady_clock::now() - time_origin),
|
||||
.log_class = log_class,
|
||||
.log_level = log_level,
|
||||
.filename = filename,
|
||||
.line_num = line_num,
|
||||
.function = function,
|
||||
.message = message,
|
||||
.thread = Common::GetCurrentThreadName(),
|
||||
.counter = 1,
|
||||
};
|
||||
} else {
|
||||
const Entry entry = {
|
||||
.timestamp = duration_cast<microseconds>(steady_clock::now() - time_origin),
|
||||
.log_class = log_class,
|
||||
.log_level = log_level,
|
||||
.filename = filename,
|
||||
.line_num = line_num,
|
||||
.function = function,
|
||||
.message = message,
|
||||
.thread = Common::GetCurrentThreadName(),
|
||||
.counter = 1,
|
||||
};
|
||||
|
||||
if (Config::getLogType() == "async") {
|
||||
message_queue.EmplaceWait(entry);
|
||||
} else {
|
||||
ForEachBackend([&entry](auto& backend) { backend.Write(entry); });
|
||||
std::fflush(stdout);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
@ -275,21 +296,23 @@ private:
|
||||
}
|
||||
|
||||
void StopBackendThread() {
|
||||
// log last message
|
||||
if (_last_entry.counter >= 2) {
|
||||
_last_entry.message += " x" + std::to_string(_last_entry.counter);
|
||||
}
|
||||
|
||||
if (_last_entry.counter >= 1) {
|
||||
if (Config::getLogType() == "async") {
|
||||
message_queue.EmplaceWait(_last_entry);
|
||||
} else {
|
||||
ForEachBackend([this](auto& backend) { backend.Write(this->_last_entry); });
|
||||
std::fflush(stdout);
|
||||
if (Config::groupIdenticalLogs()) {
|
||||
// log last message
|
||||
if (_last_entry.counter >= 2) {
|
||||
_last_entry.message += " x" + std::to_string(_last_entry.counter);
|
||||
}
|
||||
}
|
||||
|
||||
this->_last_entry = {};
|
||||
if (_last_entry.counter >= 1) {
|
||||
if (Config::getLogType() == "async") {
|
||||
message_queue.EmplaceWait(_last_entry);
|
||||
} else {
|
||||
ForEachBackend([this](auto& backend) { backend.Write(this->_last_entry); });
|
||||
std::fflush(stdout);
|
||||
}
|
||||
}
|
||||
|
||||
this->_last_entry = {};
|
||||
}
|
||||
|
||||
backend_thread.request_stop();
|
||||
if (backend_thread.joinable()) {
|
||||
|
||||
@ -6,6 +6,7 @@
|
||||
#include "common/assert.h"
|
||||
#include "common/native_clock.h"
|
||||
#include "common/singleton.h"
|
||||
#include "core/signals.h"
|
||||
#include "debug_state.h"
|
||||
#include "devtools/widget/common.h"
|
||||
#include "libraries/kernel/time.h"
|
||||
@ -33,7 +34,7 @@ static void PauseThread(ThreadID id) {
|
||||
SuspendThread(handle);
|
||||
CloseHandle(handle);
|
||||
#else
|
||||
pthread_kill(id, SIGUSR1);
|
||||
pthread_kill(id, SIGSLEEP);
|
||||
#endif
|
||||
}
|
||||
|
||||
@ -43,7 +44,7 @@ static void ResumeThread(ThreadID id) {
|
||||
ResumeThread(handle);
|
||||
CloseHandle(handle);
|
||||
#else
|
||||
pthread_kill(id, SIGUSR1);
|
||||
pthread_kill(id, SIGSLEEP);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
@ -55,7 +55,7 @@ std::filesystem::path MntPoints::GetHostPath(std::string_view path, bool* is_rea
|
||||
if (path.length() > 255)
|
||||
return "";
|
||||
|
||||
const MntPair* mount = GetMount(corrected_path);
|
||||
const std::optional<MntPair> mount = GetMount(corrected_path);
|
||||
if (!mount) {
|
||||
return "";
|
||||
}
|
||||
|
||||
@ -5,6 +5,7 @@
|
||||
|
||||
#include <atomic>
|
||||
#include <mutex>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <tsl/robin_map.h>
|
||||
@ -58,14 +59,17 @@ public:
|
||||
return it == m_mnt_pairs.end() ? nullptr : &*it;
|
||||
}
|
||||
|
||||
const MntPair* GetMount(const std::string& guest_path) {
|
||||
const std::optional<MntPair> GetMount(const std::string& guest_path) {
|
||||
std::scoped_lock lock{m_mutex};
|
||||
const auto it = std::ranges::find_if(m_mnt_pairs, [&](const auto& mount) {
|
||||
// When doing starts-with check, add a trailing slash to make sure we don't match
|
||||
// against only part of the mount path.
|
||||
return guest_path == mount.mount || guest_path.starts_with(mount.mount + "/");
|
||||
});
|
||||
return it == m_mnt_pairs.end() ? nullptr : &*it;
|
||||
if (it == m_mnt_pairs.end()) {
|
||||
return std::nullopt;
|
||||
}
|
||||
return *it;
|
||||
}
|
||||
|
||||
private:
|
||||
@ -81,7 +85,8 @@ enum class FileType {
|
||||
Device,
|
||||
Socket,
|
||||
Epoll,
|
||||
Resolver
|
||||
Resolver,
|
||||
Equeue
|
||||
};
|
||||
|
||||
struct File {
|
||||
|
||||
@ -174,12 +174,18 @@ void IPC::InputLoop() {
|
||||
} else if (cmd == "USB_LOAD_FIGURE") {
|
||||
const auto ref = Libraries::Usbd::usb_backend->GetImplRef();
|
||||
if (ref) {
|
||||
ref->LoadFigure(next_str(), next_u64(), next_u64());
|
||||
std::string file_name = next_str();
|
||||
const u8 pad = next_u64();
|
||||
const u8 slot = next_u64();
|
||||
ref->LoadFigure(file_name, pad, slot);
|
||||
}
|
||||
} else if (cmd == "USB_REMOVE_FIGURE") {
|
||||
const auto ref = Libraries::Usbd::usb_backend->GetImplRef();
|
||||
if (ref) {
|
||||
ref->RemoveFigure(next_u64(), next_u64(), next_u64() != 0);
|
||||
const u8 pad = next_u64();
|
||||
const u8 slot = next_u64();
|
||||
bool full_remove = next_u64() != 0;
|
||||
ref->RemoveFigure(pad, slot, full_remove);
|
||||
}
|
||||
} else if (cmd == "USB_MOVE_FIGURE") {
|
||||
const auto ref = Libraries::Usbd::usb_backend->GetImplRef();
|
||||
|
||||
@ -123,21 +123,23 @@ static inline bool IsValidEventType(Platform::InterruptId id) {
|
||||
static_cast<u32>(id) == static_cast<u32>(Platform::InterruptId::GfxEop);
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI sceGnmAddEqEvent(SceKernelEqueue eq, u64 id, void* udata) {
|
||||
s32 PS4_SYSV_ABI sceGnmAddEqEvent(OrbisKernelEqueue eq, u64 id, void* udata) {
|
||||
LOG_TRACE(Lib_GnmDriver, "called");
|
||||
|
||||
if (!eq) {
|
||||
auto equeue = GetEqueue(eq);
|
||||
if (!equeue) {
|
||||
return ORBIS_KERNEL_ERROR_EBADF;
|
||||
}
|
||||
|
||||
EqueueEvent kernel_event{};
|
||||
kernel_event.event.ident = id;
|
||||
kernel_event.event.filter = SceKernelEvent::Filter::GraphicsCore;
|
||||
kernel_event.event.flags = SceKernelEvent::Flags::Add;
|
||||
kernel_event.event.filter = OrbisKernelEvent::Filter::GraphicsCore;
|
||||
kernel_event.event.flags = OrbisKernelEvent::Flags::Add;
|
||||
kernel_event.event.fflags = 0;
|
||||
kernel_event.event.data = id;
|
||||
kernel_event.event.udata = udata;
|
||||
eq->AddEvent(kernel_event);
|
||||
|
||||
equeue->AddEvent(kernel_event);
|
||||
|
||||
Platform::IrqC::Instance()->Register(
|
||||
static_cast<Platform::InterruptId>(id),
|
||||
@ -149,10 +151,11 @@ s32 PS4_SYSV_ABI sceGnmAddEqEvent(SceKernelEqueue eq, u64 id, void* udata) {
|
||||
return;
|
||||
|
||||
// Event data is expected to be an event type as per sceGnmGetEqEventType.
|
||||
eq->TriggerEvent(static_cast<GnmEventType>(id), SceKernelEvent::Filter::GraphicsCore,
|
||||
reinterpret_cast<void*>(id));
|
||||
equeue->TriggerEvent(static_cast<GnmEventType>(id),
|
||||
OrbisKernelEvent::Filter::GraphicsCore,
|
||||
reinterpret_cast<void*>(id));
|
||||
},
|
||||
eq);
|
||||
equeue);
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
@ -267,16 +270,17 @@ int PS4_SYSV_ABI sceGnmDebugHardwareStatus() {
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI sceGnmDeleteEqEvent(SceKernelEqueue eq, u64 id) {
|
||||
s32 PS4_SYSV_ABI sceGnmDeleteEqEvent(OrbisKernelEqueue eq, u64 id) {
|
||||
LOG_TRACE(Lib_GnmDriver, "called");
|
||||
|
||||
if (!eq) {
|
||||
auto equeue = GetEqueue(eq);
|
||||
if (!equeue) {
|
||||
return ORBIS_KERNEL_ERROR_EBADF;
|
||||
}
|
||||
|
||||
eq->RemoveEvent(id, SceKernelEvent::Filter::GraphicsCore);
|
||||
equeue->RemoveEvent(id, OrbisKernelEvent::Filter::GraphicsCore);
|
||||
|
||||
Platform::IrqC::Instance()->Unregister(static_cast<Platform::InterruptId>(id), eq);
|
||||
Platform::IrqC::Instance()->Unregister(static_cast<Platform::InterruptId>(id), equeue);
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
@ -895,7 +899,7 @@ int PS4_SYSV_ABI sceGnmGetDebugTimestamp() {
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceGnmGetEqEventType(const SceKernelEvent* ev) {
|
||||
int PS4_SYSV_ABI sceGnmGetEqEventType(const OrbisKernelEvent* ev) {
|
||||
LOG_TRACE(Lib_GnmDriver, "called");
|
||||
return sceKernelGetEventData(ev);
|
||||
}
|
||||
@ -2052,7 +2056,7 @@ int PS4_SYSV_ABI sceGnmSqttWaitForEvent() {
|
||||
}
|
||||
|
||||
static inline s32 PatchFlipRequest(u32* cmdbuf, u32 size, u32 vo_handle, u32 buf_idx, u32 flip_mode,
|
||||
u32 flip_arg, void* unk) {
|
||||
s64 flip_arg, void* unk) {
|
||||
// check for `prepareFlip` packet
|
||||
cmdbuf += size - 64;
|
||||
ASSERT_MSG(cmdbuf[0] == 0xc03e1000, "Can't find `prepareFlip` packet");
|
||||
@ -2138,7 +2142,7 @@ static inline s32 PatchFlipRequest(u32* cmdbuf, u32 size, u32 vo_handle, u32 buf
|
||||
s32 PS4_SYSV_ABI sceGnmSubmitAndFlipCommandBuffers(u32 count, u32* dcb_gpu_addrs[],
|
||||
u32* dcb_sizes_in_bytes, u32* ccb_gpu_addrs[],
|
||||
u32* ccb_sizes_in_bytes, u32 vo_handle,
|
||||
u32 buf_idx, u32 flip_mode, u32 flip_arg) {
|
||||
u32 buf_idx, u32 flip_mode, s64 flip_arg) {
|
||||
return sceGnmSubmitAndFlipCommandBuffersForWorkload(
|
||||
count, count, dcb_gpu_addrs, dcb_sizes_in_bytes, ccb_gpu_addrs, ccb_sizes_in_bytes,
|
||||
vo_handle, buf_idx, flip_mode, flip_arg);
|
||||
@ -2146,7 +2150,7 @@ s32 PS4_SYSV_ABI sceGnmSubmitAndFlipCommandBuffers(u32 count, u32* dcb_gpu_addrs
|
||||
|
||||
s32 PS4_SYSV_ABI sceGnmSubmitAndFlipCommandBuffersForWorkload(
|
||||
u32 workload, u32 count, u32* dcb_gpu_addrs[], u32* dcb_sizes_in_bytes, u32* ccb_gpu_addrs[],
|
||||
u32* ccb_sizes_in_bytes, u32 vo_handle, u32 buf_idx, u32 flip_mode, u32 flip_arg) {
|
||||
u32* ccb_sizes_in_bytes, u32 vo_handle, u32 buf_idx, u32 flip_mode, s64 flip_arg) {
|
||||
LOG_DEBUG(Lib_GnmDriver, "called [buf = {}]", buf_idx);
|
||||
|
||||
auto* cmdbuf = dcb_gpu_addrs[count - 1];
|
||||
|
||||
@ -14,7 +14,7 @@ namespace Libraries::GnmDriver {
|
||||
|
||||
using namespace Kernel;
|
||||
|
||||
s32 PS4_SYSV_ABI sceGnmAddEqEvent(SceKernelEqueue eq, u64 id, void* udata);
|
||||
s32 PS4_SYSV_ABI sceGnmAddEqEvent(OrbisKernelEqueue eq, u64 id, void* udata);
|
||||
int PS4_SYSV_ABI sceGnmAreSubmitsAllowed();
|
||||
int PS4_SYSV_ABI sceGnmBeginWorkload(u32 workload_stream, u64* workload);
|
||||
s32 PS4_SYSV_ABI sceGnmComputeWaitOnAddress(u32* cmdbuf, u32 size, uintptr_t addr, u32 mask,
|
||||
@ -31,7 +31,7 @@ int PS4_SYSV_ABI sceGnmDebuggerSetAddressWatch();
|
||||
int PS4_SYSV_ABI sceGnmDebuggerWriteGds();
|
||||
int PS4_SYSV_ABI sceGnmDebuggerWriteSqIndirectRegister();
|
||||
int PS4_SYSV_ABI sceGnmDebugHardwareStatus();
|
||||
s32 PS4_SYSV_ABI sceGnmDeleteEqEvent(SceKernelEqueue eq, u64 id);
|
||||
s32 PS4_SYSV_ABI sceGnmDeleteEqEvent(OrbisKernelEqueue eq, u64 id);
|
||||
int PS4_SYSV_ABI sceGnmDestroyWorkloadStream();
|
||||
void PS4_SYSV_ABI sceGnmDingDong(u32 gnm_vqid, u32 next_offs_dw);
|
||||
void PS4_SYSV_ABI sceGnmDingDongForWorkload(u32 gnm_vqid, u32 next_offs_dw, u64 workload_id);
|
||||
@ -87,7 +87,7 @@ int PS4_SYSV_ABI sceGnmGetCoredumpMode();
|
||||
int PS4_SYSV_ABI sceGnmGetCoredumpProtectionFaultTimestamp();
|
||||
int PS4_SYSV_ABI sceGnmGetDbgGcHandle();
|
||||
int PS4_SYSV_ABI sceGnmGetDebugTimestamp();
|
||||
int PS4_SYSV_ABI sceGnmGetEqEventType(const SceKernelEvent* ev);
|
||||
int PS4_SYSV_ABI sceGnmGetEqEventType(const OrbisKernelEvent* ev);
|
||||
int PS4_SYSV_ABI sceGnmGetEqTimeStamp();
|
||||
int PS4_SYSV_ABI sceGnmGetGpuBlockStatus();
|
||||
u32 PS4_SYSV_ABI sceGnmGetGpuCoreClockFrequency();
|
||||
@ -211,10 +211,10 @@ int PS4_SYSV_ABI sceGnmSqttWaitForEvent();
|
||||
s32 PS4_SYSV_ABI sceGnmSubmitAndFlipCommandBuffers(u32 count, u32* dcb_gpu_addrs[],
|
||||
u32* dcb_sizes_in_bytes, u32* ccb_gpu_addrs[],
|
||||
u32* ccb_sizes_in_bytes, u32 vo_handle,
|
||||
u32 buf_idx, u32 flip_mode, u32 flip_arg);
|
||||
u32 buf_idx, u32 flip_mode, s64 flip_arg);
|
||||
int PS4_SYSV_ABI sceGnmSubmitAndFlipCommandBuffersForWorkload(
|
||||
u32 workload, u32 count, u32* dcb_gpu_addrs[], u32* dcb_sizes_in_bytes, u32* ccb_gpu_addrs[],
|
||||
u32* ccb_sizes_in_bytes, u32 vo_handle, u32 buf_idx, u32 flip_mode, u32 flip_arg);
|
||||
u32* ccb_sizes_in_bytes, u32 vo_handle, u32 buf_idx, u32 flip_mode, s64 flip_arg);
|
||||
s32 PS4_SYSV_ABI sceGnmSubmitCommandBuffers(u32 count, const u32* dcb_gpu_addrs[],
|
||||
u32* dcb_sizes_in_bytes, const u32* ccb_gpu_addrs[],
|
||||
u32* ccb_sizes_in_bytes);
|
||||
|
||||
@ -2,12 +2,18 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <thread>
|
||||
#include <magic_enum/magic_enum.hpp>
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/debug.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/singleton.h"
|
||||
#include "core/file_sys/fs.h"
|
||||
#include "core/libraries/kernel/equeue.h"
|
||||
#include "core/libraries/kernel/kernel.h"
|
||||
#include "core/libraries/kernel/orbis_error.h"
|
||||
#include "core/libraries/kernel/posix_error.h"
|
||||
#include "core/libraries/kernel/time.h"
|
||||
#include "core/libraries/libs.h"
|
||||
|
||||
namespace Libraries::Kernel {
|
||||
@ -15,35 +21,104 @@ namespace Libraries::Kernel {
|
||||
extern boost::asio::io_context io_context;
|
||||
extern void KernelSignalRequest();
|
||||
|
||||
static constexpr auto HrTimerSpinlockThresholdUs = 1200u;
|
||||
static std::unordered_map<s32, EqueueInternal*> kqueues;
|
||||
static constexpr auto HrTimerSpinlockThresholdNs = 1200000u;
|
||||
|
||||
EqueueInternal* GetEqueue(OrbisKernelEqueue eq) {
|
||||
if (!kqueues.contains(eq)) {
|
||||
return nullptr;
|
||||
}
|
||||
return kqueues[eq];
|
||||
}
|
||||
|
||||
static void HrTimerCallback(OrbisKernelEqueue eq, const OrbisKernelEvent& kevent) {
|
||||
if (kqueues.contains(eq)) {
|
||||
kqueues[eq]->TriggerEvent(kevent.ident, OrbisKernelEvent::Filter::HrTimer, kevent.udata);
|
||||
}
|
||||
}
|
||||
|
||||
static void TimerCallback(OrbisKernelEqueue eq, const OrbisKernelEvent& kevent) {
|
||||
if (kqueues.contains(eq) && kqueues[eq]->EventExists(kevent.ident, kevent.filter)) {
|
||||
kqueues[eq]->TriggerEvent(kevent.ident, OrbisKernelEvent::Filter::Timer, kevent.udata);
|
||||
if (!(kevent.flags & OrbisKernelEvent::Flags::OneShot)) {
|
||||
// Reschedule the event for its next period.
|
||||
kqueues[eq]->ScheduleEvent(kevent.ident, kevent.filter, TimerCallback);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Events are uniquely identified by id and filter.
|
||||
|
||||
bool EqueueInternal::AddEvent(EqueueEvent& event) {
|
||||
std::scoped_lock lock{m_mutex};
|
||||
{
|
||||
std::scoped_lock lock{m_mutex};
|
||||
|
||||
event.time_added = std::chrono::steady_clock::now();
|
||||
if (event.event.filter == SceKernelEvent::Filter::Timer ||
|
||||
event.event.filter == SceKernelEvent::Filter::HrTimer) {
|
||||
// HrTimer events are offset by the threshold of time at the end that we spinlock for
|
||||
// greater accuracy.
|
||||
const auto offset =
|
||||
event.event.filter == SceKernelEvent::Filter::HrTimer ? HrTimerSpinlockThresholdUs : 0u;
|
||||
event.timer_interval = std::chrono::microseconds(event.event.data - offset);
|
||||
// Calculate timer interval
|
||||
event.time_added = std::chrono::steady_clock::now();
|
||||
if (event.event.filter == OrbisKernelEvent::Filter::Timer) {
|
||||
// Set timer interval, this is stored in milliseconds for timers.
|
||||
event.timer_interval = std::chrono::milliseconds(event.event.data);
|
||||
} else if (event.event.filter == OrbisKernelEvent::Filter::HrTimer) {
|
||||
// Retrieve inputted time, this is stored in the bintime format.
|
||||
OrbisKernelBintime* time = reinterpret_cast<OrbisKernelBintime*>(event.event.data);
|
||||
|
||||
// Convert the bintime format to a timespec.
|
||||
OrbisKernelTimespec ts;
|
||||
ts.tv_sec = time->sec;
|
||||
ts.tv_nsec = (1000000000 * (time->frac >> 32)) >> 32;
|
||||
|
||||
// Then use the timespec to set the timer interval.
|
||||
event.timer_interval = std::chrono::nanoseconds(ts.tv_nsec + ts.tv_sec * 1000000000);
|
||||
}
|
||||
|
||||
// First, check if there's already an event with the same id and filter.
|
||||
u64 id = event.event.ident;
|
||||
OrbisKernelEvent::Filter filter = event.event.filter;
|
||||
const auto& find_it = std::ranges::find_if(m_events, [id, filter](auto& ev) {
|
||||
return ev.event.ident == id && ev.event.filter == filter;
|
||||
});
|
||||
// If there is a duplicate event, we need to update that instead.
|
||||
if (find_it != m_events.cend()) {
|
||||
// Specifically, update user data and timer_interval.
|
||||
// Trigger status and event data should remain intact.
|
||||
auto& old_event = *find_it;
|
||||
old_event.timer_interval = event.timer_interval;
|
||||
old_event.event.udata = event.event.udata;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Clear input data from event.
|
||||
event.event.data = 0;
|
||||
|
||||
// Remove add flag from event
|
||||
event.event.flags &= ~OrbisKernelEvent::Flags::Add;
|
||||
|
||||
// Clear flag is appended to most event types internally.
|
||||
if (event.event.filter != OrbisKernelEvent::Filter::User) {
|
||||
event.event.flags |= OrbisKernelEvent::Flags::Clear;
|
||||
}
|
||||
|
||||
const auto& it = std::ranges::find(m_events, event);
|
||||
if (it != m_events.cend()) {
|
||||
*it = std::move(event);
|
||||
} else {
|
||||
m_events.emplace_back(std::move(event));
|
||||
}
|
||||
}
|
||||
|
||||
const auto& it = std::ranges::find(m_events, event);
|
||||
if (it != m_events.cend()) {
|
||||
*it = std::move(event);
|
||||
} else {
|
||||
m_events.emplace_back(std::move(event));
|
||||
// Schedule callbacks for timer events
|
||||
if (event.event.filter == OrbisKernelEvent::Timer) {
|
||||
return this->ScheduleEvent(event.event.ident, OrbisKernelEvent::Filter::Timer,
|
||||
TimerCallback);
|
||||
} else if (event.event.filter == OrbisKernelEvent::HrTimer) {
|
||||
return this->ScheduleEvent(event.event.ident, OrbisKernelEvent::Filter::HrTimer,
|
||||
HrTimerCallback);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool EqueueInternal::ScheduleEvent(u64 id, s16 filter,
|
||||
void (*callback)(SceKernelEqueue, const SceKernelEvent&)) {
|
||||
void (*callback)(OrbisKernelEqueue, const OrbisKernelEvent&)) {
|
||||
std::scoped_lock lock{m_mutex};
|
||||
|
||||
const auto& it = std::ranges::find_if(m_events, [id, filter](auto& ev) {
|
||||
@ -54,8 +129,8 @@ bool EqueueInternal::ScheduleEvent(u64 id, s16 filter,
|
||||
}
|
||||
|
||||
const auto& event = *it;
|
||||
ASSERT(event.event.filter == SceKernelEvent::Filter::Timer ||
|
||||
event.event.filter == SceKernelEvent::Filter::HrTimer);
|
||||
ASSERT(event.event.filter == OrbisKernelEvent::Filter::Timer ||
|
||||
event.event.filter == OrbisKernelEvent::Filter::HrTimer);
|
||||
|
||||
if (!it->timer) {
|
||||
it->timer = std::make_unique<boost::asio::steady_timer>(io_context, event.timer_interval);
|
||||
@ -76,7 +151,7 @@ bool EqueueInternal::ScheduleEvent(u64 id, s16 filter,
|
||||
}
|
||||
return;
|
||||
}
|
||||
callback(this, event_data);
|
||||
callback(this->m_handle, event_data);
|
||||
});
|
||||
KernelSignalRequest();
|
||||
|
||||
@ -97,7 +172,7 @@ bool EqueueInternal::RemoveEvent(u64 id, s16 filter) {
|
||||
return has_found;
|
||||
}
|
||||
|
||||
int EqueueInternal::WaitForEvents(SceKernelEvent* ev, int num, const SceKernelUseconds* timo) {
|
||||
int EqueueInternal::WaitForEvents(OrbisKernelEvent* ev, int num, const OrbisKernelUseconds* timo) {
|
||||
if (timo != nullptr && *timo == 0) {
|
||||
// Effectively acts as a poll; only events that have already
|
||||
// arrived at the time of this function call can be received
|
||||
@ -127,15 +202,6 @@ int EqueueInternal::WaitForEvents(SceKernelEvent* ev, int num, const SceKernelUs
|
||||
m_cond.wait_for(lock, std::chrono::microseconds(micros), predicate);
|
||||
}
|
||||
|
||||
if (HasSmallTimer()) {
|
||||
if (count > 0) {
|
||||
const auto time_waited = std::chrono::duration_cast<std::chrono::microseconds>(
|
||||
std::chrono::steady_clock::now() - m_events[0].time_added)
|
||||
.count();
|
||||
count = WaitForSmallTimer(ev, num, std::max(0l, long(micros - time_waited)));
|
||||
}
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
@ -145,10 +211,13 @@ bool EqueueInternal::TriggerEvent(u64 ident, s16 filter, void* trigger_data) {
|
||||
std::scoped_lock lock{m_mutex};
|
||||
for (auto& event : m_events) {
|
||||
if (event.event.ident == ident && event.event.filter == filter) {
|
||||
if (filter == SceKernelEvent::Filter::VideoOut) {
|
||||
if (filter == OrbisKernelEvent::Filter::VideoOut) {
|
||||
event.TriggerDisplay(trigger_data);
|
||||
} else if (filter == SceKernelEvent::Filter::User) {
|
||||
} else if (filter == OrbisKernelEvent::Filter::User) {
|
||||
event.TriggerUser(trigger_data);
|
||||
} else if (filter == OrbisKernelEvent::Filter::Timer ||
|
||||
filter == OrbisKernelEvent::Filter::HrTimer) {
|
||||
event.TriggerTimer();
|
||||
} else {
|
||||
event.Trigger(trigger_data);
|
||||
}
|
||||
@ -160,19 +229,15 @@ bool EqueueInternal::TriggerEvent(u64 ident, s16 filter, void* trigger_data) {
|
||||
return has_found;
|
||||
}
|
||||
|
||||
int EqueueInternal::GetTriggeredEvents(SceKernelEvent* ev, int num) {
|
||||
int EqueueInternal::GetTriggeredEvents(OrbisKernelEvent* ev, int num) {
|
||||
int count = 0;
|
||||
for (auto it = m_events.begin(); it != m_events.end();) {
|
||||
if (it->IsTriggered()) {
|
||||
ev[count++] = it->event;
|
||||
|
||||
// Event should not trigger again
|
||||
it->ResetTriggerState();
|
||||
|
||||
if (it->event.flags & SceKernelEvent::Flags::Clear) {
|
||||
if (it->event.flags & OrbisKernelEvent::Flags::Clear) {
|
||||
it->Clear();
|
||||
}
|
||||
if (it->event.flags & SceKernelEvent::Flags::OneShot) {
|
||||
if (it->event.flags & OrbisKernelEvent::Flags::OneShot) {
|
||||
it = m_events.erase(it);
|
||||
} else {
|
||||
++it;
|
||||
@ -190,10 +255,17 @@ int EqueueInternal::GetTriggeredEvents(SceKernelEvent* ev, int num) {
|
||||
}
|
||||
|
||||
bool EqueueInternal::AddSmallTimer(EqueueEvent& ev) {
|
||||
// Retrieve inputted time, this is stored in the bintime format
|
||||
OrbisKernelBintime* time = reinterpret_cast<OrbisKernelBintime*>(ev.event.data);
|
||||
OrbisKernelTimespec ts;
|
||||
ts.tv_sec = time->sec;
|
||||
ts.tv_nsec = ((1000000000 * (time->frac >> 32)) >> 32);
|
||||
|
||||
// Create the small timer
|
||||
SmallTimer st;
|
||||
st.event = ev.event;
|
||||
st.added = std::chrono::steady_clock::now();
|
||||
st.interval = std::chrono::microseconds{ev.event.data};
|
||||
st.interval = std::chrono::nanoseconds(ts.tv_nsec + ts.tv_sec * 1000000000);
|
||||
{
|
||||
std::scoped_lock lock{m_mutex};
|
||||
m_small_timers[st.event.ident] = std::move(st);
|
||||
@ -201,7 +273,7 @@ bool EqueueInternal::AddSmallTimer(EqueueEvent& ev) {
|
||||
return true;
|
||||
}
|
||||
|
||||
int EqueueInternal::WaitForSmallTimer(SceKernelEvent* ev, int num, u32 micros) {
|
||||
int EqueueInternal::WaitForSmallTimer(OrbisKernelEvent* ev, int num, u32 micros) {
|
||||
ASSERT(num >= 1);
|
||||
|
||||
auto curr_clock = std::chrono::steady_clock::now();
|
||||
@ -242,18 +314,119 @@ bool EqueueInternal::EventExists(u64 id, s16 filter) {
|
||||
return it != m_events.cend();
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceKernelCreateEqueue(SceKernelEqueue* eq, const char* name) {
|
||||
s32 PS4_SYSV_ABI posix_kqueue() {
|
||||
// Reserve a file handle for the kqueue
|
||||
auto* handles = Common::Singleton<Core::FileSys::HandleTable>::Instance();
|
||||
s32 kqueue_handle = handles->CreateHandle();
|
||||
auto* kqueue_file = handles->GetFile(kqueue_handle);
|
||||
kqueue_file->type = Core::FileSys::FileType::Equeue;
|
||||
|
||||
// Plenty of equeue logic uses names to identify queues.
|
||||
// Create a unique name for the queue we create.
|
||||
char name[32];
|
||||
memset(name, 0, sizeof(name));
|
||||
snprintf(name, sizeof(name), "kqueue%i", kqueue_handle);
|
||||
|
||||
// Create the queue
|
||||
kqueues[kqueue_handle] = new EqueueInternal(kqueue_handle, name);
|
||||
LOG_INFO(Kernel_Event, "kqueue created with name {}", name);
|
||||
|
||||
// Return handle.
|
||||
return kqueue_handle;
|
||||
}
|
||||
|
||||
// Helper method to detect supported filters.
|
||||
// We don't want to allow adding events we don't handle properly.
|
||||
bool SupportedEqueueFilter(OrbisKernelEvent::Filter filter) {
|
||||
return filter == OrbisKernelEvent::Filter::GraphicsCore ||
|
||||
filter == OrbisKernelEvent::Filter::HrTimer ||
|
||||
filter == OrbisKernelEvent::Filter::Timer || filter == OrbisKernelEvent::Filter::User ||
|
||||
filter == OrbisKernelEvent::Filter::VideoOut;
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI posix_kevent(s32 handle, OrbisKernelEvent* changelist, u64 nchanges,
|
||||
OrbisKernelEvent* eventlist, u64 nevents,
|
||||
OrbisKernelTimespec* timeout) {
|
||||
LOG_INFO(Kernel_Event, "called, eq = {}, nchanges = {}, nevents = {}", handle, nchanges,
|
||||
nevents);
|
||||
|
||||
// Get the equeue
|
||||
if (!kqueues.contains(handle)) {
|
||||
*__Error() = POSIX_EBADF;
|
||||
return ORBIS_FAIL;
|
||||
}
|
||||
auto equeue = kqueues[handle];
|
||||
|
||||
// First step is to apply all changes in changelist.
|
||||
for (u64 i = 0; i < nchanges; i++) {
|
||||
auto event = changelist[i];
|
||||
if (!SupportedEqueueFilter(event.filter)) {
|
||||
LOG_ERROR(Kernel_Event, "Unsupported event filter {}",
|
||||
magic_enum::enum_name(event.filter));
|
||||
continue;
|
||||
}
|
||||
|
||||
// Check the event flags to determine the appropriate action
|
||||
if (event.flags & OrbisKernelEvent::Flags::Add) {
|
||||
// The caller is requesting to add an event.
|
||||
EqueueEvent internal_event{};
|
||||
internal_event.event = event;
|
||||
if (!equeue->AddEvent(internal_event)) {
|
||||
// Failed to add event, return error.
|
||||
*__Error() = POSIX_ENOMEM;
|
||||
return ORBIS_FAIL;
|
||||
}
|
||||
}
|
||||
|
||||
if (event.flags & OrbisKernelEvent::Flags::Delete) {
|
||||
// The caller is requesting to remove an event.
|
||||
if (!equeue->RemoveEvent(event.ident, event.filter)) {
|
||||
// Failed to remove event, return error.
|
||||
*__Error() = POSIX_ENOENT;
|
||||
return ORBIS_FAIL;
|
||||
}
|
||||
}
|
||||
|
||||
if (event.filter == OrbisKernelEvent::Filter::User && event.fflags == 0x1000000) {
|
||||
// For user events, this fflags value indicates we need to trigger the event.
|
||||
if (!equeue->TriggerEvent(event.ident, OrbisKernelEvent::Filter::User, event.udata)) {
|
||||
*__Error() = POSIX_ENOENT;
|
||||
return ORBIS_FAIL;
|
||||
}
|
||||
} else if (event.fflags != 0) {
|
||||
// The title is using filter-specific flags. Right now, these are unhandled.
|
||||
LOG_ERROR(Kernel_Event, "Unhandled fflags {:#x} for event filter {}", event.fflags,
|
||||
magic_enum::enum_name(event.filter));
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// Now we need to wait on the event list.
|
||||
s32 count = 0;
|
||||
if (nevents > 0) {
|
||||
if (timeout != nullptr) {
|
||||
OrbisKernelUseconds micros = (timeout->tv_sec * 1000000) + (timeout->tv_nsec / 1000);
|
||||
count = equeue->WaitForEvents(eventlist, nevents, µs);
|
||||
} else {
|
||||
count = equeue->WaitForEvents(eventlist, nevents, nullptr);
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceKernelCreateEqueue(OrbisKernelEqueue* eq, const char* name) {
|
||||
if (eq == nullptr) {
|
||||
LOG_ERROR(Kernel_Event, "Event queue is null!");
|
||||
return ORBIS_KERNEL_ERROR_EINVAL;
|
||||
}
|
||||
|
||||
if (name == nullptr) {
|
||||
LOG_ERROR(Kernel_Event, "Event queue name is null!");
|
||||
return ORBIS_KERNEL_ERROR_EINVAL;
|
||||
}
|
||||
|
||||
// Maximum is 32 including null terminator
|
||||
static constexpr size_t MaxEventQueueNameSize = 32;
|
||||
static constexpr u64 MaxEventQueueNameSize = 32;
|
||||
if (std::strlen(name) > MaxEventQueueNameSize) {
|
||||
LOG_ERROR(Kernel_Event, "Event queue name exceeds 32 bytes!");
|
||||
return ORBIS_KERNEL_ERROR_ENAMETOOLONG;
|
||||
@ -261,29 +434,42 @@ int PS4_SYSV_ABI sceKernelCreateEqueue(SceKernelEqueue* eq, const char* name) {
|
||||
|
||||
LOG_INFO(Kernel_Event, "name = {}", name);
|
||||
|
||||
*eq = new EqueueInternal(name);
|
||||
// Reserve a file handle for the kqueue
|
||||
auto* handles = Common::Singleton<Core::FileSys::HandleTable>::Instance();
|
||||
OrbisKernelEqueue kqueue_handle = handles->CreateHandle();
|
||||
auto* kqueue_file = handles->GetFile(kqueue_handle);
|
||||
kqueue_file->type = Core::FileSys::FileType::Equeue;
|
||||
|
||||
// Create the equeue
|
||||
kqueues[kqueue_handle] = new EqueueInternal(kqueue_handle, name);
|
||||
*eq = kqueue_handle;
|
||||
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceKernelDeleteEqueue(SceKernelEqueue eq) {
|
||||
if (eq == nullptr) {
|
||||
int PS4_SYSV_ABI sceKernelDeleteEqueue(OrbisKernelEqueue eq) {
|
||||
if (!kqueues.contains(eq)) {
|
||||
return ORBIS_KERNEL_ERROR_EBADF;
|
||||
}
|
||||
|
||||
delete eq;
|
||||
auto* handles = Common::Singleton<Core::FileSys::HandleTable>::Instance();
|
||||
handles->DeleteHandle(eq);
|
||||
kqueues.erase(eq);
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceKernelWaitEqueue(SceKernelEqueue eq, SceKernelEvent* ev, int num, int* out,
|
||||
SceKernelUseconds* timo) {
|
||||
int PS4_SYSV_ABI sceKernelWaitEqueue(OrbisKernelEqueue eq, OrbisKernelEvent* ev, int num, int* out,
|
||||
OrbisKernelUseconds* timo) {
|
||||
HLE_TRACE;
|
||||
TRACE_HINT(eq->GetName());
|
||||
LOG_TRACE(Kernel_Event, "equeue = {} num = {}", eq->GetName(), num);
|
||||
|
||||
if (eq == nullptr) {
|
||||
if (!kqueues.contains(eq)) {
|
||||
return ORBIS_KERNEL_ERROR_EBADF;
|
||||
}
|
||||
|
||||
auto& equeue = kqueues[eq];
|
||||
|
||||
TRACE_HINT(equeue->GetName());
|
||||
LOG_TRACE(Kernel_Event, "equeue = {} num = {}", equeue->GetName(), num);
|
||||
|
||||
if (ev == nullptr) {
|
||||
return ORBIS_KERNEL_ERROR_EFAULT;
|
||||
}
|
||||
@ -293,7 +479,7 @@ int PS4_SYSV_ABI sceKernelWaitEqueue(SceKernelEqueue eq, SceKernelEvent* ev, int
|
||||
return ORBIS_KERNEL_ERROR_EINVAL;
|
||||
}
|
||||
|
||||
*out = eq->WaitForEvents(ev, num, timo);
|
||||
*out = equeue->WaitForEvents(ev, num, timo);
|
||||
|
||||
if (*out == 0) {
|
||||
return ORBIS_KERNEL_ERROR_ETIMEDOUT;
|
||||
@ -302,31 +488,22 @@ int PS4_SYSV_ABI sceKernelWaitEqueue(SceKernelEqueue eq, SceKernelEvent* ev, int
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
static void HrTimerCallback(SceKernelEqueue eq, const SceKernelEvent& kevent) {
|
||||
static EqueueEvent event;
|
||||
event.event = kevent;
|
||||
event.event.data = HrTimerSpinlockThresholdUs;
|
||||
eq->AddSmallTimer(event);
|
||||
eq->TriggerEvent(kevent.ident, SceKernelEvent::Filter::HrTimer, kevent.udata);
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI sceKernelAddHRTimerEvent(SceKernelEqueue eq, int id, timespec* ts, void* udata) {
|
||||
if (eq == nullptr) {
|
||||
s32 PS4_SYSV_ABI sceKernelAddHRTimerEvent(OrbisKernelEqueue eq, int id, OrbisKernelTimespec* ts,
|
||||
void* udata) {
|
||||
if (!kqueues.contains(eq)) {
|
||||
return ORBIS_KERNEL_ERROR_EBADF;
|
||||
}
|
||||
|
||||
if (ts->tv_sec > 100 || ts->tv_nsec < 100'000) {
|
||||
return ORBIS_KERNEL_ERROR_EINVAL;
|
||||
}
|
||||
ASSERT(ts->tv_nsec > 1000); // assume 1us resolution
|
||||
const auto total_us = ts->tv_sec * 1000'000 + ts->tv_nsec / 1000;
|
||||
const auto total_ns = ts->tv_sec * 1000000000 + ts->tv_nsec;
|
||||
|
||||
EqueueEvent event{};
|
||||
event.event.ident = id;
|
||||
event.event.filter = SceKernelEvent::Filter::HrTimer;
|
||||
event.event.flags = SceKernelEvent::Flags::Add | SceKernelEvent::Flags::OneShot;
|
||||
event.event.filter = OrbisKernelEvent::Filter::HrTimer;
|
||||
event.event.flags = OrbisKernelEvent::Flags::Add | OrbisKernelEvent::Flags::OneShot;
|
||||
event.event.fflags = 0;
|
||||
event.event.data = total_us;
|
||||
// Data is stored as the address of a OrbisKernelBintime struct.
|
||||
OrbisKernelBintime time{ts->tv_sec, ts->tv_nsec * 0x44b82fa09};
|
||||
event.event.data = reinterpret_cast<u64>(&time);
|
||||
event.event.udata = udata;
|
||||
|
||||
// HR timers cannot be implemented within the existing event queue architecture due to the
|
||||
@ -336,158 +513,137 @@ s32 PS4_SYSV_ABI sceKernelAddHRTimerEvent(SceKernelEqueue eq, int id, timespec*
|
||||
// `HrTimerSpinlockThresholdUs`) and fall back to boost asio timers if the time to tick is
|
||||
// large. Even for large delays, we truncate a small portion to complete the wait
|
||||
// using the spinlock, prioritizing precision.
|
||||
|
||||
if (eq->EventExists(event.event.ident, event.event.filter)) {
|
||||
eq->RemoveEvent(id, SceKernelEvent::Filter::HrTimer);
|
||||
auto& equeue = kqueues[eq];
|
||||
if (total_ns < HrTimerSpinlockThresholdNs) {
|
||||
return equeue->AddSmallTimer(event) ? ORBIS_OK : ORBIS_KERNEL_ERROR_ENOMEM;
|
||||
}
|
||||
|
||||
if (total_us < HrTimerSpinlockThresholdUs) {
|
||||
return eq->AddSmallTimer(event) ? ORBIS_OK : ORBIS_KERNEL_ERROR_ENOMEM;
|
||||
}
|
||||
|
||||
if (!eq->AddEvent(event) ||
|
||||
!eq->ScheduleEvent(id, SceKernelEvent::Filter::HrTimer, HrTimerCallback)) {
|
||||
if (!equeue->AddEvent(event)) {
|
||||
return ORBIS_KERNEL_ERROR_ENOMEM;
|
||||
}
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceKernelDeleteHRTimerEvent(SceKernelEqueue eq, int id) {
|
||||
if (eq == nullptr) {
|
||||
int PS4_SYSV_ABI sceKernelDeleteHRTimerEvent(OrbisKernelEqueue eq, int id) {
|
||||
if (!kqueues.contains(eq)) {
|
||||
return ORBIS_KERNEL_ERROR_EBADF;
|
||||
}
|
||||
|
||||
if (eq->HasSmallTimer()) {
|
||||
return eq->RemoveSmallTimer(id) ? ORBIS_OK : ORBIS_KERNEL_ERROR_ENOENT;
|
||||
auto& equeue = kqueues[eq];
|
||||
if (equeue->HasSmallTimer()) {
|
||||
return equeue->RemoveSmallTimer(id) ? ORBIS_OK : ORBIS_KERNEL_ERROR_ENOENT;
|
||||
} else {
|
||||
return eq->RemoveEvent(id, SceKernelEvent::Filter::HrTimer) ? ORBIS_OK
|
||||
: ORBIS_KERNEL_ERROR_ENOENT;
|
||||
return equeue->RemoveEvent(id, OrbisKernelEvent::Filter::HrTimer)
|
||||
? ORBIS_OK
|
||||
: ORBIS_KERNEL_ERROR_ENOENT;
|
||||
}
|
||||
}
|
||||
|
||||
static void TimerCallback(SceKernelEqueue eq, const SceKernelEvent& kevent) {
|
||||
if (eq->EventExists(kevent.ident, kevent.filter)) {
|
||||
eq->TriggerEvent(kevent.ident, SceKernelEvent::Filter::Timer, kevent.udata);
|
||||
|
||||
if (!(kevent.flags & SceKernelEvent::Flags::OneShot)) {
|
||||
// Reschedule the event for its next period.
|
||||
eq->ScheduleEvent(kevent.ident, kevent.filter, TimerCallback);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceKernelAddTimerEvent(SceKernelEqueue eq, int id, SceKernelUseconds usec,
|
||||
int PS4_SYSV_ABI sceKernelAddTimerEvent(OrbisKernelEqueue eq, int id, OrbisKernelUseconds usec,
|
||||
void* udata) {
|
||||
if (eq == nullptr) {
|
||||
if (!kqueues.contains(eq)) {
|
||||
return ORBIS_KERNEL_ERROR_EBADF;
|
||||
}
|
||||
|
||||
EqueueEvent event{};
|
||||
event.event.ident = static_cast<u64>(id);
|
||||
event.event.filter = SceKernelEvent::Filter::Timer;
|
||||
event.event.flags = SceKernelEvent::Flags::Add;
|
||||
event.event.filter = OrbisKernelEvent::Filter::Timer;
|
||||
event.event.flags = OrbisKernelEvent::Flags::Add;
|
||||
event.event.fflags = 0;
|
||||
event.event.data = usec;
|
||||
event.event.data = usec / 1000;
|
||||
event.event.udata = udata;
|
||||
|
||||
if (eq->EventExists(event.event.ident, event.event.filter)) {
|
||||
eq->RemoveEvent(id, SceKernelEvent::Filter::Timer);
|
||||
LOG_DEBUG(Kernel_Event,
|
||||
"Timer event already exists, removing it: queue name={}, queue id={}",
|
||||
eq->GetName(), event.event.ident);
|
||||
}
|
||||
|
||||
LOG_DEBUG(Kernel_Event, "Added timing event: queue name={}, queue id={}, usec={}, pointer={:x}",
|
||||
eq->GetName(), event.event.ident, usec, reinterpret_cast<uintptr_t>(udata));
|
||||
|
||||
if (!eq->AddEvent(event) ||
|
||||
!eq->ScheduleEvent(id, SceKernelEvent::Filter::Timer, TimerCallback)) {
|
||||
auto& equeue = kqueues[eq];
|
||||
if (!equeue->AddEvent(event)) {
|
||||
return ORBIS_KERNEL_ERROR_ENOMEM;
|
||||
}
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceKernelDeleteTimerEvent(SceKernelEqueue eq, int id) {
|
||||
if (eq == nullptr) {
|
||||
int PS4_SYSV_ABI sceKernelDeleteTimerEvent(OrbisKernelEqueue eq, int id) {
|
||||
if (!kqueues.contains(eq)) {
|
||||
return ORBIS_KERNEL_ERROR_EBADF;
|
||||
}
|
||||
|
||||
return eq->RemoveEvent(id, SceKernelEvent::Filter::Timer) ? ORBIS_OK
|
||||
: ORBIS_KERNEL_ERROR_ENOENT;
|
||||
return kqueues[eq]->RemoveEvent(id, OrbisKernelEvent::Filter::Timer)
|
||||
? ORBIS_OK
|
||||
: ORBIS_KERNEL_ERROR_ENOENT;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceKernelAddUserEvent(SceKernelEqueue eq, int id) {
|
||||
if (eq == nullptr) {
|
||||
int PS4_SYSV_ABI sceKernelAddUserEvent(OrbisKernelEqueue eq, int id) {
|
||||
if (!kqueues.contains(eq)) {
|
||||
return ORBIS_KERNEL_ERROR_EBADF;
|
||||
}
|
||||
|
||||
EqueueEvent event{};
|
||||
event.event.ident = id;
|
||||
event.event.filter = SceKernelEvent::Filter::User;
|
||||
event.event.filter = OrbisKernelEvent::Filter::User;
|
||||
event.event.udata = 0;
|
||||
event.event.flags = SceKernelEvent::Flags::Add;
|
||||
event.event.flags = OrbisKernelEvent::Flags::Add;
|
||||
event.event.fflags = 0;
|
||||
event.event.data = 0;
|
||||
|
||||
return eq->AddEvent(event) ? ORBIS_OK : ORBIS_KERNEL_ERROR_ENOMEM;
|
||||
return kqueues[eq]->AddEvent(event) ? ORBIS_OK : ORBIS_KERNEL_ERROR_ENOMEM;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceKernelAddUserEventEdge(SceKernelEqueue eq, int id) {
|
||||
if (eq == nullptr) {
|
||||
int PS4_SYSV_ABI sceKernelAddUserEventEdge(OrbisKernelEqueue eq, int id) {
|
||||
if (!kqueues.contains(eq)) {
|
||||
return ORBIS_KERNEL_ERROR_EBADF;
|
||||
}
|
||||
|
||||
EqueueEvent event{};
|
||||
event.event.ident = id;
|
||||
event.event.filter = SceKernelEvent::Filter::User;
|
||||
event.event.filter = OrbisKernelEvent::Filter::User;
|
||||
event.event.udata = 0;
|
||||
event.event.flags = SceKernelEvent::Flags::Add | SceKernelEvent::Flags::Clear;
|
||||
event.event.flags = OrbisKernelEvent::Flags::Add | OrbisKernelEvent::Flags::Clear;
|
||||
event.event.fflags = 0;
|
||||
event.event.data = 0;
|
||||
|
||||
return eq->AddEvent(event) ? ORBIS_OK : ORBIS_KERNEL_ERROR_ENOMEM;
|
||||
return kqueues[eq]->AddEvent(event) ? ORBIS_OK : ORBIS_KERNEL_ERROR_ENOMEM;
|
||||
}
|
||||
|
||||
void* PS4_SYSV_ABI sceKernelGetEventUserData(const SceKernelEvent* ev) {
|
||||
void* PS4_SYSV_ABI sceKernelGetEventUserData(const OrbisKernelEvent* ev) {
|
||||
ASSERT(ev);
|
||||
return ev->udata;
|
||||
}
|
||||
|
||||
u64 PS4_SYSV_ABI sceKernelGetEventId(const SceKernelEvent* ev) {
|
||||
u64 PS4_SYSV_ABI sceKernelGetEventId(const OrbisKernelEvent* ev) {
|
||||
return ev->ident;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceKernelTriggerUserEvent(SceKernelEqueue eq, int id, void* udata) {
|
||||
if (eq == nullptr) {
|
||||
int PS4_SYSV_ABI sceKernelTriggerUserEvent(OrbisKernelEqueue eq, int id, void* udata) {
|
||||
if (!kqueues.contains(eq)) {
|
||||
return ORBIS_KERNEL_ERROR_EBADF;
|
||||
}
|
||||
|
||||
if (!eq->TriggerEvent(id, SceKernelEvent::Filter::User, udata)) {
|
||||
if (!kqueues[eq]->TriggerEvent(id, OrbisKernelEvent::Filter::User, udata)) {
|
||||
return ORBIS_KERNEL_ERROR_ENOENT;
|
||||
}
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceKernelDeleteUserEvent(SceKernelEqueue eq, int id) {
|
||||
if (eq == nullptr) {
|
||||
int PS4_SYSV_ABI sceKernelDeleteUserEvent(OrbisKernelEqueue eq, int id) {
|
||||
if (!kqueues.contains(eq)) {
|
||||
return ORBIS_KERNEL_ERROR_EBADF;
|
||||
}
|
||||
|
||||
if (!eq->RemoveEvent(id, SceKernelEvent::Filter::User)) {
|
||||
if (!kqueues[eq]->RemoveEvent(id, OrbisKernelEvent::Filter::User)) {
|
||||
return ORBIS_KERNEL_ERROR_ENOENT;
|
||||
}
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceKernelGetEventFilter(const SceKernelEvent* ev) {
|
||||
int PS4_SYSV_ABI sceKernelGetEventFilter(const OrbisKernelEvent* ev) {
|
||||
return ev->filter;
|
||||
}
|
||||
|
||||
u64 PS4_SYSV_ABI sceKernelGetEventData(const SceKernelEvent* ev) {
|
||||
u64 PS4_SYSV_ABI sceKernelGetEventData(const OrbisKernelEvent* ev) {
|
||||
return ev->data;
|
||||
}
|
||||
|
||||
void RegisterEventQueue(Core::Loader::SymbolsResolver* sym) {
|
||||
LIB_FUNCTION("nh2IFMgKTv8", "libScePosix", 1, "libkernel", posix_kqueue);
|
||||
LIB_FUNCTION("RW-GEfpnsqg", "libScePosix", 1, "libkernel", posix_kevent);
|
||||
LIB_FUNCTION("D0OdFMjp46I", "libkernel", 1, "libkernel", sceKernelCreateEqueue);
|
||||
LIB_FUNCTION("jpFjmgAC5AE", "libkernel", 1, "libkernel", sceKernelDeleteEqueue);
|
||||
LIB_FUNCTION("fzyMKs9kim0", "libkernel", 1, "libkernel", sceKernelWaitEqueue);
|
||||
|
||||
@ -22,10 +22,15 @@ namespace Libraries::Kernel {
|
||||
class EqueueInternal;
|
||||
struct EqueueEvent;
|
||||
|
||||
using SceKernelUseconds = u32;
|
||||
using SceKernelEqueue = EqueueInternal*;
|
||||
struct OrbisKernelBintime {
|
||||
s64 sec;
|
||||
s64 frac;
|
||||
};
|
||||
|
||||
struct SceKernelEvent {
|
||||
using OrbisKernelUseconds = u32;
|
||||
using OrbisKernelEqueue = s64;
|
||||
|
||||
struct OrbisKernelEvent {
|
||||
enum Filter : s16 {
|
||||
None = 0,
|
||||
Read = -1,
|
||||
@ -78,37 +83,37 @@ struct OrbisVideoOutEventData {
|
||||
};
|
||||
|
||||
struct EqueueEvent {
|
||||
SceKernelEvent event;
|
||||
OrbisKernelEvent event;
|
||||
void* data = nullptr;
|
||||
std::chrono::steady_clock::time_point time_added;
|
||||
std::chrono::microseconds timer_interval;
|
||||
std::chrono::nanoseconds timer_interval;
|
||||
std::unique_ptr<boost::asio::steady_timer> timer;
|
||||
|
||||
void ResetTriggerState() {
|
||||
is_triggered = false;
|
||||
}
|
||||
|
||||
void Clear() {
|
||||
is_triggered = false;
|
||||
event.fflags = 0;
|
||||
event.data = 0;
|
||||
}
|
||||
|
||||
void Trigger(void* data) {
|
||||
is_triggered = true;
|
||||
event.fflags++;
|
||||
event.data = reinterpret_cast<uintptr_t>(data);
|
||||
}
|
||||
|
||||
void TriggerUser(void* data) {
|
||||
is_triggered = true;
|
||||
event.fflags++;
|
||||
event.udata = data;
|
||||
}
|
||||
|
||||
void TriggerTimer() {
|
||||
is_triggered = true;
|
||||
event.data++;
|
||||
}
|
||||
|
||||
void TriggerDisplay(void* data) {
|
||||
is_triggered = true;
|
||||
if (data != nullptr) {
|
||||
auto event_data = static_cast<OrbisVideoOutEventData>(event.data);
|
||||
auto event_data = std::bit_cast<OrbisVideoOutEventData>(event.data);
|
||||
auto event_hint_raw = reinterpret_cast<u64>(data);
|
||||
auto event_hint = static_cast<OrbisVideoOutEventHint>(event_hint_raw);
|
||||
if (event_hint.event_id == event.ident && event.ident != 0xfe) {
|
||||
@ -137,13 +142,14 @@ private:
|
||||
|
||||
class EqueueInternal {
|
||||
struct SmallTimer {
|
||||
SceKernelEvent event;
|
||||
OrbisKernelEvent event;
|
||||
std::chrono::steady_clock::time_point added;
|
||||
std::chrono::microseconds interval;
|
||||
std::chrono::nanoseconds interval;
|
||||
};
|
||||
|
||||
public:
|
||||
explicit EqueueInternal(std::string_view name) : m_name(name) {}
|
||||
explicit EqueueInternal(OrbisKernelEqueue handle, std::string_view name)
|
||||
: m_handle(handle), m_name(name) {}
|
||||
|
||||
std::string_view GetName() const {
|
||||
return m_name;
|
||||
@ -151,11 +157,11 @@ public:
|
||||
|
||||
bool AddEvent(EqueueEvent& event);
|
||||
bool ScheduleEvent(u64 id, s16 filter,
|
||||
void (*callback)(SceKernelEqueue, const SceKernelEvent&));
|
||||
void (*callback)(OrbisKernelEqueue, const OrbisKernelEvent&));
|
||||
bool RemoveEvent(u64 id, s16 filter);
|
||||
int WaitForEvents(SceKernelEvent* ev, int num, const SceKernelUseconds* timo);
|
||||
int WaitForEvents(OrbisKernelEvent* ev, int num, const OrbisKernelUseconds* timo);
|
||||
bool TriggerEvent(u64 ident, s16 filter, void* trigger_data);
|
||||
int GetTriggeredEvents(SceKernelEvent* ev, int num);
|
||||
int GetTriggeredEvents(OrbisKernelEvent* ev, int num);
|
||||
|
||||
bool AddSmallTimer(EqueueEvent& event);
|
||||
bool HasSmallTimer() {
|
||||
@ -170,11 +176,12 @@ public:
|
||||
return false;
|
||||
}
|
||||
|
||||
int WaitForSmallTimer(SceKernelEvent* ev, int num, u32 micros);
|
||||
int WaitForSmallTimer(OrbisKernelEvent* ev, int num, u32 micros);
|
||||
|
||||
bool EventExists(u64 id, s16 filter);
|
||||
|
||||
private:
|
||||
OrbisKernelEqueue m_handle;
|
||||
std::string m_name;
|
||||
std::mutex m_mutex;
|
||||
std::vector<EqueueEvent> m_events;
|
||||
@ -182,7 +189,8 @@ private:
|
||||
std::unordered_map<u64, SmallTimer> m_small_timers;
|
||||
};
|
||||
|
||||
u64 PS4_SYSV_ABI sceKernelGetEventData(const SceKernelEvent* ev);
|
||||
EqueueInternal* GetEqueue(OrbisKernelEqueue eq);
|
||||
u64 PS4_SYSV_ABI sceKernelGetEventData(const OrbisKernelEvent* ev);
|
||||
|
||||
void RegisterEventQueue(Core::Loader::SymbolsResolver* sym);
|
||||
|
||||
|
||||
@ -792,7 +792,8 @@ s32 PS4_SYSV_ABI fstat(s32 fd, OrbisKernelStat* sb) {
|
||||
return file->socket->fstat(sb);
|
||||
}
|
||||
case Core::FileSys::FileType::Epoll:
|
||||
case Core::FileSys::FileType::Resolver: {
|
||||
case Core::FileSys::FileType::Resolver:
|
||||
case Core::FileSys::FileType::Equeue: {
|
||||
LOG_ERROR(Kernel_Fs, "(STUBBED) file type {}", magic_enum::enum_name(file->type.load()));
|
||||
break;
|
||||
}
|
||||
|
||||
@ -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/config.h"
|
||||
@ -275,6 +275,10 @@ s32 PS4_SYSV_ABI sceKernelGetModuleList2(s32* handles, u64 num_array, u64* out_c
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
u32 PS4_SYSV_ABI posix_getuid() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI exit(s32 status) {
|
||||
UNREACHABLE_MSG("Exiting with status code {}", status);
|
||||
return 0;
|
||||
@ -299,6 +303,7 @@ void RegisterProcess(Core::Loader::SymbolsResolver* sym) {
|
||||
LIB_FUNCTION("HZO7xOos4xc", "libkernel", 1, "libkernel", sceKernelGetModuleInfoInternal);
|
||||
LIB_FUNCTION("IuxnUuXk6Bg", "libkernel", 1, "libkernel", sceKernelGetModuleList);
|
||||
LIB_FUNCTION("ZzzC3ZGVAkc", "libkernel", 1, "libkernel", sceKernelGetModuleList2);
|
||||
LIB_FUNCTION("kg4x8Prhfxw", "libkernel", 1, "libkernel", posix_getuid);
|
||||
LIB_FUNCTION("6Z83sYWFlA8", "libkernel", 1, "libkernel", exit);
|
||||
}
|
||||
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2024-2026 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "common/assert.h"
|
||||
@ -6,6 +6,7 @@
|
||||
#include "core/libraries/kernel/threads/exception.h"
|
||||
#include "core/libraries/kernel/threads/pthread.h"
|
||||
#include "core/libraries/libs.h"
|
||||
#include "core/signals.h"
|
||||
|
||||
#ifdef _WIN64
|
||||
#include "common/ntapi.h"
|
||||
@ -15,11 +16,156 @@
|
||||
|
||||
namespace Libraries::Kernel {
|
||||
|
||||
static std::array<SceKernelExceptionHandler, 32> Handlers{};
|
||||
#ifdef _WIN32
|
||||
|
||||
// Windows doesn't have native versions of these, and we don't need to use them either.
|
||||
static s32 NativeToOrbisSignal(s32 s) {
|
||||
return s;
|
||||
}
|
||||
|
||||
static s32 OrbisToNativeSignal(s32 s) {
|
||||
return s;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
static s32 NativeToOrbisSignal(s32 s) {
|
||||
switch (s) {
|
||||
case SIGHUP:
|
||||
return POSIX_SIGHUP;
|
||||
case SIGINT:
|
||||
return POSIX_SIGINT;
|
||||
case SIGQUIT:
|
||||
return POSIX_SIGQUIT;
|
||||
case SIGILL:
|
||||
return POSIX_SIGILL;
|
||||
case SIGTRAP:
|
||||
return POSIX_SIGTRAP;
|
||||
case SIGABRT:
|
||||
return POSIX_SIGABRT;
|
||||
case SIGFPE:
|
||||
return POSIX_SIGFPE;
|
||||
case SIGKILL:
|
||||
return POSIX_SIGKILL;
|
||||
case SIGBUS:
|
||||
return POSIX_SIGBUS;
|
||||
case SIGSEGV:
|
||||
return POSIX_SIGSEGV;
|
||||
case SIGSYS:
|
||||
return POSIX_SIGSYS;
|
||||
case SIGPIPE:
|
||||
return POSIX_SIGPIPE;
|
||||
case SIGALRM:
|
||||
return POSIX_SIGALRM;
|
||||
case SIGTERM:
|
||||
return POSIX_SIGTERM;
|
||||
case SIGURG:
|
||||
return POSIX_SIGURG;
|
||||
case SIGSTOP:
|
||||
return POSIX_SIGSTOP;
|
||||
case SIGTSTP:
|
||||
return POSIX_SIGTSTP;
|
||||
case SIGCONT:
|
||||
return POSIX_SIGCONT;
|
||||
case SIGCHLD:
|
||||
return POSIX_SIGCHLD;
|
||||
case SIGTTIN:
|
||||
return POSIX_SIGTTIN;
|
||||
case SIGTTOU:
|
||||
return POSIX_SIGTTOU;
|
||||
case SIGIO:
|
||||
return POSIX_SIGIO;
|
||||
case SIGXCPU:
|
||||
return POSIX_SIGXCPU;
|
||||
case SIGXFSZ:
|
||||
return POSIX_SIGXFSZ;
|
||||
case SIGVTALRM:
|
||||
return POSIX_SIGVTALRM;
|
||||
case SIGPROF:
|
||||
return POSIX_SIGPROF;
|
||||
case SIGWINCH:
|
||||
return POSIX_SIGWINCH;
|
||||
case SIGUSR1:
|
||||
return POSIX_SIGUSR1;
|
||||
case SIGUSR2:
|
||||
return POSIX_SIGUSR2;
|
||||
default:
|
||||
UNREACHABLE_MSG("Unknown signal {}", s);
|
||||
}
|
||||
}
|
||||
|
||||
static s32 OrbisToNativeSignal(s32 s) {
|
||||
switch (s) {
|
||||
case POSIX_SIGHUP:
|
||||
return SIGHUP;
|
||||
case POSIX_SIGINT:
|
||||
return SIGINT;
|
||||
case POSIX_SIGQUIT:
|
||||
return SIGQUIT;
|
||||
case POSIX_SIGILL:
|
||||
return SIGILL;
|
||||
case POSIX_SIGTRAP:
|
||||
return SIGTRAP;
|
||||
case POSIX_SIGABRT:
|
||||
return SIGABRT;
|
||||
case POSIX_SIGFPE:
|
||||
return SIGFPE;
|
||||
case POSIX_SIGKILL:
|
||||
return SIGKILL;
|
||||
case POSIX_SIGBUS:
|
||||
return SIGBUS;
|
||||
case POSIX_SIGSEGV:
|
||||
return SIGSEGV;
|
||||
case POSIX_SIGSYS:
|
||||
return SIGSYS;
|
||||
case POSIX_SIGPIPE:
|
||||
return SIGPIPE;
|
||||
case POSIX_SIGALRM:
|
||||
return SIGALRM;
|
||||
case POSIX_SIGTERM:
|
||||
return SIGTERM;
|
||||
case POSIX_SIGURG:
|
||||
return SIGURG;
|
||||
case POSIX_SIGSTOP:
|
||||
return SIGSTOP;
|
||||
case POSIX_SIGTSTP:
|
||||
return SIGTSTP;
|
||||
case POSIX_SIGCONT:
|
||||
return SIGCONT;
|
||||
case POSIX_SIGCHLD:
|
||||
return SIGCHLD;
|
||||
case POSIX_SIGTTIN:
|
||||
return SIGTTIN;
|
||||
case POSIX_SIGTTOU:
|
||||
return SIGTTOU;
|
||||
case POSIX_SIGIO:
|
||||
return SIGIO;
|
||||
case POSIX_SIGXCPU:
|
||||
return SIGXCPU;
|
||||
case POSIX_SIGXFSZ:
|
||||
return SIGXFSZ;
|
||||
case POSIX_SIGVTALRM:
|
||||
return SIGVTALRM;
|
||||
case POSIX_SIGPROF:
|
||||
return SIGPROF;
|
||||
case POSIX_SIGWINCH:
|
||||
return SIGWINCH;
|
||||
case POSIX_SIGUSR1:
|
||||
return SIGUSR1;
|
||||
case POSIX_SIGUSR2:
|
||||
return SIGUSR2;
|
||||
default:
|
||||
UNREACHABLE_MSG("Unknown signal {}", s);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
std::array<SceKernelExceptionHandler, 32> Handlers{};
|
||||
|
||||
#ifndef _WIN64
|
||||
void SigactionHandler(int signum, siginfo_t* inf, ucontext_t* raw_context) {
|
||||
const auto handler = Handlers[POSIX_SIGUSR1];
|
||||
void SigactionHandler(int native_signum, siginfo_t* inf, ucontext_t* raw_context) {
|
||||
const auto handler = Handlers[native_signum];
|
||||
if (handler) {
|
||||
auto ctx = Ucontext{};
|
||||
#ifdef __APPLE__
|
||||
@ -42,6 +188,8 @@ void SigactionHandler(int signum, siginfo_t* inf, ucontext_t* raw_context) {
|
||||
ctx.uc_mcontext.mc_rsp = regs.__rsp;
|
||||
ctx.uc_mcontext.mc_fs = regs.__fs;
|
||||
ctx.uc_mcontext.mc_gs = regs.__gs;
|
||||
ctx.uc_mcontext.mc_rip = regs.__rip;
|
||||
ctx.uc_mcontext.mc_addr = reinterpret_cast<uint64_t>(inf->si_addr);
|
||||
#else
|
||||
const auto& regs = raw_context->uc_mcontext.gregs;
|
||||
ctx.uc_mcontext.mc_r8 = regs[REG_R8];
|
||||
@ -62,15 +210,18 @@ void SigactionHandler(int signum, siginfo_t* inf, ucontext_t* raw_context) {
|
||||
ctx.uc_mcontext.mc_rsp = regs[REG_RSP];
|
||||
ctx.uc_mcontext.mc_fs = (regs[REG_CSGSFS] >> 32) & 0xFFFF;
|
||||
ctx.uc_mcontext.mc_gs = (regs[REG_CSGSFS] >> 16) & 0xFFFF;
|
||||
ctx.uc_mcontext.mc_rip = (regs[REG_RIP]);
|
||||
ctx.uc_mcontext.mc_addr = reinterpret_cast<uint64_t>(inf->si_addr);
|
||||
#endif
|
||||
handler(POSIX_SIGUSR1, &ctx);
|
||||
handler(NativeToOrbisSignal(native_signum), &ctx);
|
||||
}
|
||||
}
|
||||
#else
|
||||
void ExceptionHandler(void* arg1, void* arg2, void* arg3, PCONTEXT context) {
|
||||
const char* thrName = (char*)arg1;
|
||||
int native_signum = reinterpret_cast<uintptr_t>(arg2);
|
||||
LOG_INFO(Lib_Kernel, "Exception raised successfully on thread '{}'", thrName);
|
||||
const auto handler = Handlers[POSIX_SIGUSR1];
|
||||
const auto handler = Handlers[native_signum];
|
||||
if (handler) {
|
||||
auto ctx = Ucontext{};
|
||||
ctx.uc_mcontext.mc_r8 = context->R8;
|
||||
@ -91,49 +242,71 @@ void ExceptionHandler(void* arg1, void* arg2, void* arg3, PCONTEXT context) {
|
||||
ctx.uc_mcontext.mc_rsp = context->Rsp;
|
||||
ctx.uc_mcontext.mc_fs = context->SegFs;
|
||||
ctx.uc_mcontext.mc_gs = context->SegGs;
|
||||
handler(POSIX_SIGUSR1, &ctx);
|
||||
handler(NativeToOrbisSignal(native_signum), &ctx);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
int PS4_SYSV_ABI sceKernelInstallExceptionHandler(s32 signum, SceKernelExceptionHandler handler) {
|
||||
if (signum != POSIX_SIGUSR1) {
|
||||
LOG_ERROR(Lib_Kernel, "Installing non-supported exception handler for signal {}", signum);
|
||||
return 0;
|
||||
if (signum > POSIX_SIGUSR2) {
|
||||
return ORBIS_KERNEL_ERROR_EINVAL;
|
||||
}
|
||||
ASSERT_MSG(!Handlers[POSIX_SIGUSR1], "Invalid parameters");
|
||||
Handlers[POSIX_SIGUSR1] = handler;
|
||||
LOG_INFO(Lib_Kernel, "Installing signal handler for {}", signum);
|
||||
int const native_signum = OrbisToNativeSignal(signum);
|
||||
#ifdef __APPLE__
|
||||
ASSERT_MSG(native_signum != SIGVTALRM, "SIGVTALRM is HLE-reserved on macOS!");
|
||||
#endif
|
||||
ASSERT_MSG(!Handlers[native_signum], "Invalid parameters");
|
||||
Handlers[native_signum] = handler;
|
||||
#ifndef _WIN64
|
||||
if (native_signum == SIGSEGV || native_signum == SIGBUS || native_signum == SIGILL) {
|
||||
return ORBIS_OK; // These are handled in Core::SignalHandler
|
||||
}
|
||||
struct sigaction act = {};
|
||||
act.sa_flags = SA_SIGINFO | SA_RESTART;
|
||||
act.sa_sigaction = reinterpret_cast<decltype(act.sa_sigaction)>(SigactionHandler);
|
||||
sigaction(SIGUSR2, &act, nullptr);
|
||||
sigemptyset(&act.sa_mask);
|
||||
sigaction(native_signum, &act, nullptr);
|
||||
#endif
|
||||
return 0;
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceKernelRemoveExceptionHandler(s32 signum) {
|
||||
if (signum != POSIX_SIGUSR1) {
|
||||
LOG_ERROR(Lib_Kernel, "Installing non-supported exception handler for signal {}", signum);
|
||||
return 0;
|
||||
if (signum > POSIX_SIGUSR2) {
|
||||
return ORBIS_KERNEL_ERROR_EINVAL;
|
||||
}
|
||||
ASSERT_MSG(Handlers[POSIX_SIGUSR1], "Invalid parameters");
|
||||
Handlers[POSIX_SIGUSR1] = nullptr;
|
||||
int const native_signum = OrbisToNativeSignal(signum);
|
||||
ASSERT_MSG(Handlers[native_signum], "Invalid parameters");
|
||||
Handlers[native_signum] = nullptr;
|
||||
#ifndef _WIN64
|
||||
struct sigaction act = {};
|
||||
act.sa_flags = SA_SIGINFO | SA_RESTART;
|
||||
act.sa_sigaction = nullptr;
|
||||
sigaction(SIGUSR2, &act, nullptr);
|
||||
if (native_signum == SIGSEGV || native_signum == SIGBUS || native_signum == SIGILL) {
|
||||
struct sigaction action{};
|
||||
action.sa_sigaction = Core::SignalHandler;
|
||||
action.sa_flags = SA_SIGINFO | SA_ONSTACK;
|
||||
sigemptyset(&action.sa_mask);
|
||||
|
||||
ASSERT_MSG(sigaction(native_signum, &action, nullptr) == 0,
|
||||
"Failed to reinstate original signal handler for signal {}", native_signum);
|
||||
} else {
|
||||
struct sigaction act = {};
|
||||
act.sa_flags = SA_SIGINFO | SA_RESTART;
|
||||
act.sa_sigaction = nullptr;
|
||||
sigemptyset(&act.sa_mask);
|
||||
sigaction(native_signum, &act, nullptr);
|
||||
}
|
||||
#endif
|
||||
return 0;
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceKernelRaiseException(PthreadT thread, int signum) {
|
||||
if (signum != POSIX_SIGUSR1) {
|
||||
return ORBIS_KERNEL_ERROR_EINVAL;
|
||||
}
|
||||
LOG_WARNING(Lib_Kernel, "Raising exception on thread '{}'", thread->name);
|
||||
ASSERT_MSG(signum == POSIX_SIGUSR1, "Attempting to raise non user defined signal!");
|
||||
int const native_signum = OrbisToNativeSignal(signum);
|
||||
#ifndef _WIN64
|
||||
const auto pthr = reinterpret_cast<pthread_t>(thread->native_thr.GetHandle());
|
||||
const auto ret = pthread_kill(pthr, SIGUSR2);
|
||||
const auto ret = pthread_kill(pthr, native_signum);
|
||||
if (ret != 0) {
|
||||
LOG_ERROR(Kernel, "Failed to send exception signal to thread '{}': {}", thread->name,
|
||||
strerror(ret));
|
||||
@ -143,10 +316,11 @@ int PS4_SYSV_ABI sceKernelRaiseException(PthreadT thread, int signum) {
|
||||
option.UserApcFlags = QueueUserApcFlagsSpecialUserApc;
|
||||
|
||||
u64 res = NtQueueApcThreadEx(reinterpret_cast<HANDLE>(thread->native_thr.GetHandle()), option,
|
||||
ExceptionHandler, (void*)thread->name.c_str(), nullptr, nullptr);
|
||||
ExceptionHandler, (void*)thread->name.c_str(),
|
||||
(void*)native_signum, nullptr);
|
||||
ASSERT(res == 0);
|
||||
#endif
|
||||
return 0;
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI sceKernelDebugRaiseException(s32 error, s64 unk) {
|
||||
@ -154,7 +328,7 @@ s32 PS4_SYSV_ABI sceKernelDebugRaiseException(s32 error, s64 unk) {
|
||||
return ORBIS_KERNEL_ERROR_EINVAL;
|
||||
}
|
||||
UNREACHABLE_MSG("error {:#x}", error);
|
||||
return 0;
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI sceKernelDebugRaiseExceptionOnReleaseMode(s32 error, s64 unk) {
|
||||
@ -162,7 +336,7 @@ s32 PS4_SYSV_ABI sceKernelDebugRaiseExceptionOnReleaseMode(s32 error, s64 unk) {
|
||||
return ORBIS_KERNEL_ERROR_EINVAL;
|
||||
}
|
||||
UNREACHABLE_MSG("error {:#x}", error);
|
||||
return 0;
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
void RegisterException(Core::Loader::SymbolsResolver* sym) {
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2024-2026 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
@ -13,8 +13,39 @@ namespace Libraries::Kernel {
|
||||
|
||||
using SceKernelExceptionHandler = PS4_SYSV_ABI void (*)(int, void*);
|
||||
|
||||
constexpr int POSIX_SIGSEGV = 11;
|
||||
constexpr int POSIX_SIGUSR1 = 30;
|
||||
constexpr s32 POSIX_SIGHUP = 1;
|
||||
constexpr s32 POSIX_SIGINT = 2;
|
||||
constexpr s32 POSIX_SIGQUIT = 3;
|
||||
constexpr s32 POSIX_SIGILL = 4;
|
||||
constexpr s32 POSIX_SIGTRAP = 5;
|
||||
constexpr s32 POSIX_SIGABRT = 6;
|
||||
constexpr s32 POSIX_SIGEMT = 7;
|
||||
constexpr s32 POSIX_SIGFPE = 8;
|
||||
constexpr s32 POSIX_SIGKILL = 9;
|
||||
constexpr s32 POSIX_SIGBUS = 10;
|
||||
constexpr s32 POSIX_SIGSEGV = 11;
|
||||
constexpr s32 POSIX_SIGSYS = 12;
|
||||
constexpr s32 POSIX_SIGPIPE = 13;
|
||||
constexpr s32 POSIX_SIGALRM = 14;
|
||||
constexpr s32 POSIX_SIGTERM = 15;
|
||||
constexpr s32 POSIX_SIGURG = 16;
|
||||
constexpr s32 POSIX_SIGSTOP = 17;
|
||||
constexpr s32 POSIX_SIGTSTP = 18;
|
||||
constexpr s32 POSIX_SIGCONT = 19;
|
||||
constexpr s32 POSIX_SIGCHLD = 20;
|
||||
constexpr s32 POSIX_SIGTTIN = 21;
|
||||
constexpr s32 POSIX_SIGTTOU = 22;
|
||||
constexpr s32 POSIX_SIGIO = 23;
|
||||
constexpr s32 POSIX_SIGXCPU = 24;
|
||||
constexpr s32 POSIX_SIGXFSZ = 25;
|
||||
constexpr s32 POSIX_SIGVTALRM = 26;
|
||||
constexpr s32 POSIX_SIGPROF = 27;
|
||||
constexpr s32 POSIX_SIGWINCH = 28;
|
||||
constexpr s32 POSIX_SIGINFO = 29;
|
||||
constexpr s32 POSIX_SIGUSR1 = 30;
|
||||
constexpr s32 POSIX_SIGUSR2 = 31;
|
||||
constexpr s32 POSIX_SIGTHR = 32;
|
||||
constexpr s32 POSIX_SIGLIBRT = 33;
|
||||
|
||||
struct Mcontext {
|
||||
u64 mc_onstack;
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2024-2026 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <thread>
|
||||
@ -39,7 +39,7 @@ static constexpr PthreadMutexAttr PthreadMutexattrAdaptiveDefault = {
|
||||
|
||||
using CallocFun = void* (*)(size_t, size_t);
|
||||
|
||||
static int MutexInit(PthreadMutexT* mutex, const PthreadMutexAttr* mutex_attr, const char* name) {
|
||||
static s32 MutexInit(PthreadMutexT* mutex, const PthreadMutexAttr* mutex_attr, const char* name) {
|
||||
const PthreadMutexAttr* attr;
|
||||
if (mutex_attr == nullptr) {
|
||||
attr = &PthreadMutexattrDefault;
|
||||
@ -60,7 +60,7 @@ static int MutexInit(PthreadMutexT* mutex, const PthreadMutexAttr* mutex_attr, c
|
||||
if (name) {
|
||||
pmutex->name = name;
|
||||
} else {
|
||||
static int MutexId = 0;
|
||||
static s32 MutexId = 0;
|
||||
pmutex->name = fmt::format("Mutex{}", MutexId++);
|
||||
}
|
||||
|
||||
@ -79,7 +79,7 @@ static int MutexInit(PthreadMutexT* mutex, const PthreadMutexAttr* mutex_attr, c
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int InitStatic(Pthread* thread, PthreadMutexT* mutex) {
|
||||
static s32 InitStatic(Pthread* thread, PthreadMutexT* mutex) {
|
||||
std::scoped_lock lk{MutxStaticLock};
|
||||
|
||||
if (*mutex == THR_MUTEX_INITIALIZER) {
|
||||
@ -90,17 +90,17 @@ static int InitStatic(Pthread* thread, PthreadMutexT* mutex) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI posix_pthread_mutex_init(PthreadMutexT* mutex,
|
||||
s32 PS4_SYSV_ABI posix_pthread_mutex_init(PthreadMutexT* mutex,
|
||||
const PthreadMutexAttrT* mutex_attr) {
|
||||
return MutexInit(mutex, mutex_attr ? *mutex_attr : nullptr, nullptr);
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI scePthreadMutexInit(PthreadMutexT* mutex, const PthreadMutexAttrT* mutex_attr,
|
||||
s32 PS4_SYSV_ABI scePthreadMutexInit(PthreadMutexT* mutex, const PthreadMutexAttrT* mutex_attr,
|
||||
const char* name) {
|
||||
return MutexInit(mutex, mutex_attr ? *mutex_attr : nullptr, name);
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI posix_pthread_mutex_destroy(PthreadMutexT* mutex) {
|
||||
s32 PS4_SYSV_ABI posix_pthread_mutex_destroy(PthreadMutexT* mutex) {
|
||||
PthreadMutexT m = *mutex;
|
||||
if (m < THR_MUTEX_DESTROYED) {
|
||||
return 0;
|
||||
@ -116,7 +116,7 @@ int PS4_SYSV_ABI posix_pthread_mutex_destroy(PthreadMutexT* mutex) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int PthreadMutex::SelfTryLock() {
|
||||
s32 PthreadMutex::SelfTryLock() {
|
||||
switch (Type()) {
|
||||
case PthreadMutexType::ErrorCheck:
|
||||
case PthreadMutexType::Normal:
|
||||
@ -135,7 +135,7 @@ int PthreadMutex::SelfTryLock() {
|
||||
}
|
||||
}
|
||||
|
||||
int PthreadMutex::SelfLock(const OrbisKernelTimespec* abstime, u64 usec) {
|
||||
s32 PthreadMutex::SelfLock(const OrbisKernelTimespec* abstime, u64 usec) {
|
||||
const auto DoSleep = [&] {
|
||||
if (abstime == THR_RELTIME) {
|
||||
std::this_thread::sleep_for(std::chrono::microseconds(usec));
|
||||
@ -185,7 +185,7 @@ int PthreadMutex::SelfLock(const OrbisKernelTimespec* abstime, u64 usec) {
|
||||
}
|
||||
}
|
||||
|
||||
int PthreadMutex::Lock(const OrbisKernelTimespec* abstime, u64 usec) {
|
||||
s32 PthreadMutex::Lock(const OrbisKernelTimespec* abstime, u64 usec) {
|
||||
Pthread* curthread = g_curthread;
|
||||
if (m_owner == curthread) {
|
||||
return SelfLock(abstime, usec);
|
||||
@ -198,7 +198,7 @@ int PthreadMutex::Lock(const OrbisKernelTimespec* abstime, u64 usec) {
|
||||
* faster than entering the kernel
|
||||
*/
|
||||
if (m_protocol == PthreadMutexProt::None) [[likely]] {
|
||||
int count = m_spinloops;
|
||||
s32 count = m_spinloops;
|
||||
while (count--) {
|
||||
if (m_lock.try_lock()) {
|
||||
m_owner = curthread;
|
||||
@ -217,7 +217,7 @@ int PthreadMutex::Lock(const OrbisKernelTimespec* abstime, u64 usec) {
|
||||
}
|
||||
}
|
||||
|
||||
int ret = 0;
|
||||
s32 ret = 0;
|
||||
if (abstime == nullptr) {
|
||||
m_lock.lock();
|
||||
} else if (abstime != THR_RELTIME && (abstime->tv_nsec < 0 || abstime->tv_nsec >= 1000000000))
|
||||
@ -236,40 +236,40 @@ int PthreadMutex::Lock(const OrbisKernelTimespec* abstime, u64 usec) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
int PthreadMutex::TryLock() {
|
||||
s32 PthreadMutex::TryLock() {
|
||||
Pthread* curthread = g_curthread;
|
||||
if (m_owner == curthread) {
|
||||
return SelfTryLock();
|
||||
}
|
||||
const int ret = m_lock.try_lock() ? 0 : POSIX_EBUSY;
|
||||
const s32 ret = m_lock.try_lock() ? 0 : POSIX_EBUSY;
|
||||
if (ret == 0) {
|
||||
m_owner = curthread;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI posix_pthread_mutex_trylock(PthreadMutexT* mutex) {
|
||||
s32 PS4_SYSV_ABI posix_pthread_mutex_trylock(PthreadMutexT* mutex) {
|
||||
CHECK_AND_INIT_MUTEX
|
||||
return (*mutex)->TryLock();
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI posix_pthread_mutex_lock(PthreadMutexT* mutex) {
|
||||
s32 PS4_SYSV_ABI posix_pthread_mutex_lock(PthreadMutexT* mutex) {
|
||||
CHECK_AND_INIT_MUTEX
|
||||
return (*mutex)->Lock(nullptr);
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI posix_pthread_mutex_timedlock(PthreadMutexT* mutex,
|
||||
s32 PS4_SYSV_ABI posix_pthread_mutex_timedlock(PthreadMutexT* mutex,
|
||||
const OrbisKernelTimespec* abstime) {
|
||||
CHECK_AND_INIT_MUTEX
|
||||
return (*mutex)->Lock(abstime);
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI posix_pthread_mutex_reltimedlock_np(PthreadMutexT* mutex, u64 usec) {
|
||||
s32 PS4_SYSV_ABI posix_pthread_mutex_reltimedlock_np(PthreadMutexT* mutex, u64 usec) {
|
||||
CHECK_AND_INIT_MUTEX
|
||||
return (*mutex)->Lock(THR_RELTIME, usec);
|
||||
}
|
||||
|
||||
int PthreadMutex::Unlock() {
|
||||
s32 PthreadMutex::Unlock() {
|
||||
Pthread* curthread = g_curthread;
|
||||
/*
|
||||
* Check if the running thread is not the owner of the mutex.
|
||||
@ -294,7 +294,7 @@ int PthreadMutex::Unlock() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI posix_pthread_mutex_unlock(PthreadMutexT* mutex) {
|
||||
s32 PS4_SYSV_ABI posix_pthread_mutex_unlock(PthreadMutexT* mutex) {
|
||||
PthreadMutex* mp = *mutex;
|
||||
if (mp <= THR_MUTEX_DESTROYED) [[unlikely]] {
|
||||
if (mp == THR_MUTEX_DESTROYED) {
|
||||
@ -305,29 +305,29 @@ int PS4_SYSV_ABI posix_pthread_mutex_unlock(PthreadMutexT* mutex) {
|
||||
return mp->Unlock();
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI posix_pthread_mutex_getspinloops_np(PthreadMutexT* mutex, int* count) {
|
||||
s32 PS4_SYSV_ABI posix_pthread_mutex_getspinloops_np(PthreadMutexT* mutex, int* count) {
|
||||
CHECK_AND_INIT_MUTEX
|
||||
*count = (*mutex)->m_spinloops;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI posix_pthread_mutex_setspinloops_np(PthreadMutexT* mutex, int count) {
|
||||
s32 PS4_SYSV_ABI posix_pthread_mutex_setspinloops_np(PthreadMutexT* mutex, s32 count) {
|
||||
CHECK_AND_INIT_MUTEX(*mutex)->m_spinloops = count;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI posix_pthread_mutex_getyieldloops_np(PthreadMutexT* mutex, int* count) {
|
||||
s32 PS4_SYSV_ABI posix_pthread_mutex_getyieldloops_np(PthreadMutexT* mutex, int* count) {
|
||||
CHECK_AND_INIT_MUTEX
|
||||
*count = (*mutex)->m_yieldloops;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI posix_pthread_mutex_setyieldloops_np(PthreadMutexT* mutex, int count) {
|
||||
s32 PS4_SYSV_ABI posix_pthread_mutex_setyieldloops_np(PthreadMutexT* mutex, s32 count) {
|
||||
CHECK_AND_INIT_MUTEX(*mutex)->m_yieldloops = count;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI posix_pthread_mutex_isowned_np(PthreadMutexT* mutex) {
|
||||
s32 PS4_SYSV_ABI posix_pthread_mutex_isowned_np(PthreadMutexT* mutex) {
|
||||
PthreadMutex* m = *mutex;
|
||||
if (m <= THR_MUTEX_DESTROYED) {
|
||||
return 0;
|
||||
@ -335,7 +335,7 @@ int PS4_SYSV_ABI posix_pthread_mutex_isowned_np(PthreadMutexT* mutex) {
|
||||
return m->m_owner == g_curthread;
|
||||
}
|
||||
|
||||
int PthreadMutex::IsOwned(Pthread* curthread) const {
|
||||
s32 PthreadMutex::IsOwned(Pthread* curthread) const {
|
||||
if (this <= THR_MUTEX_DESTROYED) [[unlikely]] {
|
||||
if (this == THR_MUTEX_DESTROYED) {
|
||||
return POSIX_EINVAL;
|
||||
@ -348,7 +348,7 @@ int PthreadMutex::IsOwned(Pthread* curthread) const {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI posix_pthread_mutexattr_init(PthreadMutexAttrT* attr) {
|
||||
s32 PS4_SYSV_ABI posix_pthread_mutexattr_init(PthreadMutexAttrT* attr) {
|
||||
auto pattr = new (std::nothrow) PthreadMutexAttr{};
|
||||
if (pattr == nullptr) {
|
||||
return POSIX_ENOMEM;
|
||||
@ -358,7 +358,7 @@ int PS4_SYSV_ABI posix_pthread_mutexattr_init(PthreadMutexAttrT* attr) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI posix_pthread_mutexattr_setkind_np(PthreadMutexAttrT* attr,
|
||||
s32 PS4_SYSV_ABI posix_pthread_mutexattr_setkind_np(PthreadMutexAttrT* attr,
|
||||
PthreadMutexType kind) {
|
||||
if (attr == nullptr || *attr == nullptr) {
|
||||
*__Error() = POSIX_EINVAL;
|
||||
@ -368,7 +368,7 @@ int PS4_SYSV_ABI posix_pthread_mutexattr_setkind_np(PthreadMutexAttrT* attr,
|
||||
return 0;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI posix_pthread_mutexattr_getkind_np(PthreadMutexAttrT attr) {
|
||||
s32 PS4_SYSV_ABI posix_pthread_mutexattr_getkind_np(PthreadMutexAttrT attr) {
|
||||
if (attr == nullptr) {
|
||||
*__Error() = POSIX_EINVAL;
|
||||
return -1;
|
||||
@ -376,7 +376,7 @@ int PS4_SYSV_ABI posix_pthread_mutexattr_getkind_np(PthreadMutexAttrT attr) {
|
||||
return static_cast<int>(attr->m_type);
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI posix_pthread_mutexattr_settype(PthreadMutexAttrT* attr, PthreadMutexType type) {
|
||||
s32 PS4_SYSV_ABI posix_pthread_mutexattr_settype(PthreadMutexAttrT* attr, PthreadMutexType type) {
|
||||
if (attr == nullptr || *attr == nullptr || type < PthreadMutexType::ErrorCheck ||
|
||||
type >= PthreadMutexType::Max) {
|
||||
return POSIX_EINVAL;
|
||||
@ -385,7 +385,7 @@ int PS4_SYSV_ABI posix_pthread_mutexattr_settype(PthreadMutexAttrT* attr, Pthrea
|
||||
return 0;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI posix_pthread_mutexattr_gettype(PthreadMutexAttrT* attr, PthreadMutexType* type) {
|
||||
s32 PS4_SYSV_ABI posix_pthread_mutexattr_gettype(PthreadMutexAttrT* attr, PthreadMutexType* type) {
|
||||
if (attr == nullptr || *attr == nullptr || (*attr)->m_type >= PthreadMutexType::Max) {
|
||||
return POSIX_EINVAL;
|
||||
}
|
||||
@ -393,7 +393,7 @@ int PS4_SYSV_ABI posix_pthread_mutexattr_gettype(PthreadMutexAttrT* attr, Pthrea
|
||||
return 0;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI posix_pthread_mutexattr_destroy(PthreadMutexAttrT* attr) {
|
||||
s32 PS4_SYSV_ABI posix_pthread_mutexattr_destroy(PthreadMutexAttrT* attr) {
|
||||
if (attr == nullptr || *attr == nullptr) {
|
||||
return POSIX_EINVAL;
|
||||
}
|
||||
@ -402,7 +402,7 @@ int PS4_SYSV_ABI posix_pthread_mutexattr_destroy(PthreadMutexAttrT* attr) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI posix_pthread_mutexattr_getprotocol(PthreadMutexAttrT* mattr,
|
||||
s32 PS4_SYSV_ABI posix_pthread_mutexattr_getprotocol(PthreadMutexAttrT* mattr,
|
||||
PthreadMutexProt* protocol) {
|
||||
if (mattr == nullptr || *mattr == nullptr) {
|
||||
return POSIX_EINVAL;
|
||||
@ -411,7 +411,7 @@ int PS4_SYSV_ABI posix_pthread_mutexattr_getprotocol(PthreadMutexAttrT* mattr,
|
||||
return 0;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI posix_pthread_mutexattr_setprotocol(PthreadMutexAttrT* mattr,
|
||||
s32 PS4_SYSV_ABI posix_pthread_mutexattr_setprotocol(PthreadMutexAttrT* mattr,
|
||||
PthreadMutexProt protocol) {
|
||||
if (mattr == nullptr || *mattr == nullptr || (protocol < PthreadMutexProt::None) ||
|
||||
(protocol > PthreadMutexProt::Protect)) {
|
||||
@ -422,6 +422,15 @@ int PS4_SYSV_ABI posix_pthread_mutexattr_setprotocol(PthreadMutexAttrT* mattr,
|
||||
return 0;
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI posix_pthread_mutexattr_setpshared(PthreadMutexAttrT* attr, s32 pshared) {
|
||||
constexpr s32 POSIX_PTHREAD_PROCESS_PRIVATE = 0;
|
||||
constexpr s32 POSIX_PTHREAD_PROCESS_SHARED = 1;
|
||||
if (!attr || !*attr || pshared != POSIX_PTHREAD_PROCESS_PRIVATE) {
|
||||
return POSIX_EINVAL;
|
||||
}
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
void RegisterMutex(Core::Loader::SymbolsResolver* sym) {
|
||||
// Posix
|
||||
LIB_FUNCTION("ttHNfU+qDBU", "libScePosix", 1, "libkernel", posix_pthread_mutex_init);
|
||||
@ -434,6 +443,7 @@ void RegisterMutex(Core::Loader::SymbolsResolver* sym) {
|
||||
LIB_FUNCTION("5txKfcMUAok", "libScePosix", 1, "libkernel", posix_pthread_mutexattr_setprotocol);
|
||||
LIB_FUNCTION("HF7lK46xzjY", "libScePosix", 1, "libkernel", posix_pthread_mutexattr_destroy);
|
||||
LIB_FUNCTION("K-jXhbt2gn4", "libScePosix", 1, "libkernel", posix_pthread_mutex_trylock);
|
||||
LIB_FUNCTION("EXv3ztGqtDM", "libScePosix", 1, "libkernel", posix_pthread_mutexattr_setpshared);
|
||||
|
||||
// Posix-Kernel
|
||||
LIB_FUNCTION("ttHNfU+qDBU", "libkernel", 1, "libkernel", posix_pthread_mutex_init);
|
||||
@ -444,6 +454,7 @@ void RegisterMutex(Core::Loader::SymbolsResolver* sym) {
|
||||
LIB_FUNCTION("mDmgMOGVUqg", "libkernel", 1, "libkernel", posix_pthread_mutexattr_settype);
|
||||
LIB_FUNCTION("HF7lK46xzjY", "libkernel", 1, "libkernel", posix_pthread_mutexattr_destroy);
|
||||
LIB_FUNCTION("K-jXhbt2gn4", "libkernel", 1, "libkernel", posix_pthread_mutex_trylock);
|
||||
LIB_FUNCTION("EXv3ztGqtDM", "libkernel", 1, "libkernel", posix_pthread_mutexattr_setpshared);
|
||||
|
||||
// Orbis
|
||||
LIB_FUNCTION("cmo1RIYva9o", "libkernel", 1, "libkernel", ORBIS(scePthreadMutexInit));
|
||||
|
||||
@ -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 "core/libraries/kernel/kernel.h"
|
||||
@ -291,7 +291,7 @@ void RegisterThreadAttr(Core::Loader::SymbolsResolver* sym) {
|
||||
LIB_FUNCTION("2Q0z6rnBrTE", "libScePosix", 1, "libkernel", posix_pthread_attr_setstacksize);
|
||||
LIB_FUNCTION("Ucsu-OK+els", "libScePosix", 1, "libkernel", posix_pthread_attr_get_np);
|
||||
LIB_FUNCTION("RtLRV-pBTTY", "libScePosix", 1, "libkernel", posix_pthread_attr_getschedpolicy);
|
||||
LIB_FUNCTION("JarMIy8kKEY", "libkernel", 1, "libkernel", posix_pthread_attr_setschedpolicy);
|
||||
LIB_FUNCTION("JarMIy8kKEY", "libScePosix", 1, "libkernel", posix_pthread_attr_setschedpolicy);
|
||||
LIB_FUNCTION("E+tyo3lp5Lw", "libScePosix", 1, "libkernel", posix_pthread_attr_setdetachstate);
|
||||
LIB_FUNCTION("zHchY8ft5pk", "libScePosix", 1, "libkernel", posix_pthread_attr_destroy);
|
||||
LIB_FUNCTION("euKRgm0Vn2M", "libScePosix", 1, "libkernel", posix_pthread_attr_setschedparam);
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2024-2026 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <ctime>
|
||||
@ -548,6 +548,8 @@ void RegisterTime(Core::Loader::SymbolsResolver* sym) {
|
||||
initial_ptc = clock->GetUptime();
|
||||
|
||||
// POSIX
|
||||
LIB_FUNCTION("NhpspxdjEKU", "libkernel", 1, "libkernel", posix_nanosleep);
|
||||
LIB_FUNCTION("NhpspxdjEKU", "libScePosix", 1, "libkernel", posix_nanosleep);
|
||||
LIB_FUNCTION("yS8U2TGCe1A", "libkernel", 1, "libkernel", posix_nanosleep);
|
||||
LIB_FUNCTION("yS8U2TGCe1A", "libScePosix", 1, "libkernel", posix_nanosleep);
|
||||
LIB_FUNCTION("QcteRwbsnV0", "libkernel", 1, "libkernel", posix_usleep);
|
||||
|
||||
@ -105,7 +105,7 @@ SaveInstance::SaveInstance(int slot_num, Libraries::UserService::OrbisUserServic
|
||||
mount_point = "/savedata" + std::to_string(slot_num);
|
||||
|
||||
this->exists = fs::exists(param_sfo_path);
|
||||
this->mounted = g_mnt->GetMount(mount_point) != nullptr;
|
||||
this->mounted = g_mnt->GetMount(mount_point) != std::nullopt;
|
||||
}
|
||||
|
||||
SaveInstance::~SaveInstance() {
|
||||
|
||||
@ -193,7 +193,7 @@ void VideoOutDriver::Flip(const Request& req) {
|
||||
if (event != nullptr) {
|
||||
event->TriggerEvent(
|
||||
static_cast<u64>(OrbisVideoOutInternalEventId::Flip),
|
||||
Kernel::SceKernelEvent::Filter::VideoOut,
|
||||
Kernel::OrbisKernelEvent::Filter::VideoOut,
|
||||
reinterpret_cast<void*>(static_cast<u64>(OrbisVideoOutInternalEventId::Flip) |
|
||||
(req.flip_arg << 16)));
|
||||
}
|
||||
@ -224,7 +224,7 @@ bool VideoOutDriver::SubmitFlip(VideoOutPort* port, s32 index, s64 flip_arg,
|
||||
bool is_eop /*= false*/) {
|
||||
{
|
||||
std::unique_lock lock{port->port_mutex};
|
||||
if (index != -1 && port->flip_status.flip_pending_num >= port->NumRegisteredBuffers()) {
|
||||
if (index != -1 && port->flip_status.flip_pending_num > 16) {
|
||||
LOG_ERROR(Lib_VideoOut, "Flip queue is full");
|
||||
return false;
|
||||
}
|
||||
@ -252,6 +252,7 @@ void VideoOutDriver::SubmitFlipInternal(VideoOutPort* port, s32 index, s64 flip_
|
||||
frame = presenter->PrepareBlankFrame(false);
|
||||
} else {
|
||||
const auto& buffer = port->buffer_slots[index];
|
||||
ASSERT_MSG(buffer.group_index >= 0, "Trying to flip an unregistered buffer!");
|
||||
const auto& group = port->groups[buffer.group_index];
|
||||
frame = presenter->PrepareFrame(group, buffer.address_left);
|
||||
}
|
||||
@ -314,20 +315,25 @@ void VideoOutDriver::PresentThread(std::stop_token token) {
|
||||
{
|
||||
// Needs lock here as can be concurrently read by `sceVideoOutGetVblankStatus`
|
||||
std::scoped_lock lock{main_port.vo_mutex};
|
||||
|
||||
// Trigger flip events for the port
|
||||
for (auto& event : main_port.vblank_events) {
|
||||
if (event != nullptr) {
|
||||
event->TriggerEvent(static_cast<u64>(OrbisVideoOutInternalEventId::Vblank),
|
||||
Kernel::OrbisKernelEvent::Filter::VideoOut,
|
||||
reinterpret_cast<void*>(
|
||||
static_cast<u64>(OrbisVideoOutInternalEventId::Vblank) |
|
||||
(vblank_status.count << 16)));
|
||||
}
|
||||
}
|
||||
|
||||
// Update vblank status
|
||||
vblank_status.count++;
|
||||
vblank_status.process_time = 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(static_cast<u64>(OrbisVideoOutInternalEventId::Vblank),
|
||||
Kernel::SceKernelEvent::Filter::VideoOut, nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
timer.End();
|
||||
}
|
||||
}
|
||||
|
||||
@ -25,8 +25,8 @@ struct VideoOutPort {
|
||||
std::array<BufferAttributeGroup, MaxDisplayBufferGroups> groups;
|
||||
FlipStatus flip_status;
|
||||
SceVideoOutVblankStatus vblank_status;
|
||||
std::vector<Kernel::SceKernelEqueue> flip_events;
|
||||
std::vector<Kernel::SceKernelEqueue> vblank_events;
|
||||
std::vector<Kernel::EqueueInternal*> flip_events;
|
||||
std::vector<Kernel::EqueueInternal*> vblank_events;
|
||||
std::mutex vo_mutex;
|
||||
std::mutex port_mutex;
|
||||
std::condition_variable vo_cv;
|
||||
|
||||
@ -38,7 +38,7 @@ void PS4_SYSV_ABI sceVideoOutSetBufferAttribute(BufferAttribute* attribute, Pixe
|
||||
attribute->option = SCE_VIDEO_OUT_BUFFER_ATTRIBUTE_OPTION_NONE;
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI sceVideoOutAddFlipEvent(Kernel::SceKernelEqueue eq, s32 handle, void* udata) {
|
||||
s32 PS4_SYSV_ABI sceVideoOutAddFlipEvent(Kernel::OrbisKernelEqueue eq, s32 handle, void* udata) {
|
||||
LOG_INFO(Lib_VideoOut, "handle = {}", handle);
|
||||
|
||||
auto* port = driver->GetPort(handle);
|
||||
@ -46,39 +46,41 @@ s32 PS4_SYSV_ABI sceVideoOutAddFlipEvent(Kernel::SceKernelEqueue eq, s32 handle,
|
||||
return ORBIS_VIDEO_OUT_ERROR_INVALID_HANDLE;
|
||||
}
|
||||
|
||||
if (eq == nullptr) {
|
||||
auto equeue = Kernel::GetEqueue(eq);
|
||||
if (equeue == nullptr) {
|
||||
return ORBIS_VIDEO_OUT_ERROR_INVALID_EVENT_QUEUE;
|
||||
}
|
||||
|
||||
Kernel::EqueueEvent event{};
|
||||
event.event.ident = static_cast<u64>(OrbisVideoOutInternalEventId::Flip);
|
||||
event.event.filter = Kernel::SceKernelEvent::Filter::VideoOut;
|
||||
event.event.flags = Kernel::SceKernelEvent::Flags::Add;
|
||||
event.event.filter = Kernel::OrbisKernelEvent::Filter::VideoOut;
|
||||
event.event.flags = Kernel::OrbisKernelEvent::Flags::Add;
|
||||
event.event.udata = udata;
|
||||
event.event.fflags = 0;
|
||||
event.event.data = 0;
|
||||
event.data = port;
|
||||
eq->AddEvent(event);
|
||||
equeue->AddEvent(event);
|
||||
|
||||
port->flip_events.push_back(eq);
|
||||
port->flip_events.push_back(equeue);
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI sceVideoOutDeleteFlipEvent(Kernel::SceKernelEqueue eq, s32 handle) {
|
||||
s32 PS4_SYSV_ABI sceVideoOutDeleteFlipEvent(Kernel::OrbisKernelEqueue eq, s32 handle) {
|
||||
auto* port = driver->GetPort(handle);
|
||||
if (port == nullptr) {
|
||||
return ORBIS_VIDEO_OUT_ERROR_INVALID_HANDLE;
|
||||
}
|
||||
|
||||
if (eq == nullptr) {
|
||||
auto equeue = Kernel::GetEqueue(eq);
|
||||
if (equeue == nullptr) {
|
||||
return ORBIS_VIDEO_OUT_ERROR_INVALID_EVENT_QUEUE;
|
||||
}
|
||||
eq->RemoveEvent(handle, Kernel::SceKernelEvent::Filter::VideoOut);
|
||||
port->flip_events.erase(find(port->flip_events.begin(), port->flip_events.end(), eq));
|
||||
equeue->RemoveEvent(handle, Kernel::OrbisKernelEvent::Filter::VideoOut);
|
||||
port->flip_events.erase(find(port->flip_events.begin(), port->flip_events.end(), equeue));
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI sceVideoOutAddVblankEvent(Kernel::SceKernelEqueue eq, s32 handle, void* udata) {
|
||||
s32 PS4_SYSV_ABI sceVideoOutAddVblankEvent(Kernel::OrbisKernelEqueue eq, s32 handle, void* udata) {
|
||||
LOG_INFO(Lib_VideoOut, "handle = {}", handle);
|
||||
|
||||
auto* port = driver->GetPort(handle);
|
||||
@ -86,35 +88,37 @@ s32 PS4_SYSV_ABI sceVideoOutAddVblankEvent(Kernel::SceKernelEqueue eq, s32 handl
|
||||
return ORBIS_VIDEO_OUT_ERROR_INVALID_HANDLE;
|
||||
}
|
||||
|
||||
if (eq == nullptr) {
|
||||
auto equeue = Kernel::GetEqueue(eq);
|
||||
if (equeue == nullptr) {
|
||||
return ORBIS_VIDEO_OUT_ERROR_INVALID_EVENT_QUEUE;
|
||||
}
|
||||
|
||||
Kernel::EqueueEvent event{};
|
||||
event.event.ident = static_cast<u64>(OrbisVideoOutInternalEventId::Vblank);
|
||||
event.event.filter = Kernel::SceKernelEvent::Filter::VideoOut;
|
||||
event.event.flags = Kernel::SceKernelEvent::Flags::Add;
|
||||
event.event.filter = Kernel::OrbisKernelEvent::Filter::VideoOut;
|
||||
event.event.flags = Kernel::OrbisKernelEvent::Flags::Add;
|
||||
event.event.udata = udata;
|
||||
event.event.fflags = 0;
|
||||
event.event.data = 0;
|
||||
event.data = port;
|
||||
eq->AddEvent(event);
|
||||
equeue->AddEvent(event);
|
||||
|
||||
port->vblank_events.push_back(eq);
|
||||
port->vblank_events.push_back(equeue);
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI sceVideoOutDeleteVblankEvent(Kernel::SceKernelEqueue eq, s32 handle) {
|
||||
s32 PS4_SYSV_ABI sceVideoOutDeleteVblankEvent(Kernel::OrbisKernelEqueue eq, s32 handle) {
|
||||
auto* port = driver->GetPort(handle);
|
||||
if (port == nullptr) {
|
||||
return ORBIS_VIDEO_OUT_ERROR_INVALID_HANDLE;
|
||||
}
|
||||
|
||||
if (eq == nullptr) {
|
||||
auto equeue = Kernel::GetEqueue(eq);
|
||||
if (equeue == nullptr) {
|
||||
return ORBIS_VIDEO_OUT_ERROR_INVALID_EVENT_QUEUE;
|
||||
}
|
||||
eq->RemoveEvent(handle, Kernel::SceKernelEvent::Filter::VideoOut);
|
||||
port->vblank_events.erase(find(port->vblank_events.begin(), port->vblank_events.end(), eq));
|
||||
equeue->RemoveEvent(handle, Kernel::OrbisKernelEvent::Filter::VideoOut);
|
||||
port->vblank_events.erase(find(port->vblank_events.begin(), port->vblank_events.end(), equeue));
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
@ -180,11 +184,11 @@ s32 PS4_SYSV_ABI sceVideoOutSubmitFlip(s32 handle, s32 bufferIndex, s32 flipMode
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI sceVideoOutGetEventId(const Kernel::SceKernelEvent* ev) {
|
||||
s32 PS4_SYSV_ABI sceVideoOutGetEventId(const Kernel::OrbisKernelEvent* ev) {
|
||||
if (ev == nullptr) {
|
||||
return ORBIS_VIDEO_OUT_ERROR_INVALID_ADDRESS;
|
||||
}
|
||||
if (ev->filter != Kernel::SceKernelEvent::Filter::VideoOut) {
|
||||
if (ev->filter != Kernel::OrbisKernelEvent::Filter::VideoOut) {
|
||||
return ORBIS_VIDEO_OUT_ERROR_INVALID_EVENT;
|
||||
}
|
||||
|
||||
@ -208,16 +212,16 @@ s32 PS4_SYSV_ABI sceVideoOutGetEventId(const Kernel::SceKernelEvent* ev) {
|
||||
}
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI sceVideoOutGetEventData(const Kernel::SceKernelEvent* ev, s64* data) {
|
||||
s32 PS4_SYSV_ABI sceVideoOutGetEventData(const Kernel::OrbisKernelEvent* ev, s64* data) {
|
||||
if (ev == nullptr || data == nullptr) {
|
||||
return ORBIS_VIDEO_OUT_ERROR_INVALID_ADDRESS;
|
||||
}
|
||||
if (ev->filter != Kernel::SceKernelEvent::Filter::VideoOut) {
|
||||
if (ev->filter != Kernel::OrbisKernelEvent::Filter::VideoOut) {
|
||||
return ORBIS_VIDEO_OUT_ERROR_INVALID_EVENT;
|
||||
}
|
||||
|
||||
auto event_data = ev->data >> 0x10;
|
||||
if (ev->ident != static_cast<s32>(OrbisVideoOutInternalEventId::Flip) || ev->data == 0) {
|
||||
if (ev->ident != static_cast<s32>(OrbisVideoOutInternalEventId::Flip) || ev->data >= 0) {
|
||||
*data = event_data;
|
||||
} else {
|
||||
*data = event_data | 0xffff000000000000;
|
||||
@ -225,11 +229,11 @@ s32 PS4_SYSV_ABI sceVideoOutGetEventData(const Kernel::SceKernelEvent* ev, s64*
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI sceVideoOutGetEventCount(const Kernel::SceKernelEvent* ev) {
|
||||
s32 PS4_SYSV_ABI sceVideoOutGetEventCount(const Kernel::OrbisKernelEvent* ev) {
|
||||
if (ev == nullptr) {
|
||||
return ORBIS_VIDEO_OUT_ERROR_INVALID_ADDRESS;
|
||||
}
|
||||
if (ev->filter != Kernel::SceKernelEvent::Filter::VideoOut) {
|
||||
if (ev->filter != Kernel::OrbisKernelEvent::Filter::VideoOut) {
|
||||
return ORBIS_VIDEO_OUT_ERROR_INVALID_EVENT;
|
||||
}
|
||||
|
||||
@ -338,7 +342,7 @@ s32 PS4_SYSV_ABI sceVideoOutGetBufferLabelAddress(s32 handle, uintptr_t* label_a
|
||||
return 16;
|
||||
}
|
||||
|
||||
s32 sceVideoOutSubmitEopFlip(s32 handle, u32 buf_id, u32 mode, u32 arg, void** unk) {
|
||||
s32 sceVideoOutSubmitEopFlip(s32 handle, u32 buf_id, u32 mode, s64 flip_arg, void** unk) {
|
||||
auto* port = driver->GetPort(handle);
|
||||
if (!port) {
|
||||
return ORBIS_VIDEO_OUT_ERROR_INVALID_HANDLE;
|
||||
@ -348,7 +352,7 @@ s32 sceVideoOutSubmitEopFlip(s32 handle, u32 buf_id, u32 mode, u32 arg, void** u
|
||||
Platform::InterruptId::GfxFlip, [=](Platform::InterruptId irq) {
|
||||
ASSERT_MSG(irq == Platform::InterruptId::GfxFlip, "An unexpected IRQ occured");
|
||||
ASSERT_MSG(port->buffer_labels[buf_id] == 1, "Out of order flip IRQ");
|
||||
const auto result = driver->SubmitFlip(port, buf_id, arg, true);
|
||||
const auto result = driver->SubmitFlip(port, buf_id, flip_arg, true);
|
||||
ASSERT_MSG(result, "EOP flip submission failed");
|
||||
});
|
||||
|
||||
|
||||
@ -119,8 +119,8 @@ struct OrbisVideoOutEventData {
|
||||
void PS4_SYSV_ABI sceVideoOutSetBufferAttribute(BufferAttribute* attribute, PixelFormat pixelFormat,
|
||||
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 sceVideoOutAddFlipEvent(Kernel::OrbisKernelEqueue eq, s32 handle, void* udata);
|
||||
s32 PS4_SYSV_ABI sceVideoOutAddVblankEvent(Kernel::OrbisKernelEqueue 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 sceVideoOutGetBufferLabelAddress(s32 handle, uintptr_t* label_addr);
|
||||
@ -133,13 +133,13 @@ s32 PS4_SYSV_ABI sceVideoOutGetResolutionStatus(s32 handle, SceVideoOutResolutio
|
||||
s32 PS4_SYSV_ABI sceVideoOutOpen(Libraries::UserService::OrbisUserServiceUserId userId, s32 busType,
|
||||
s32 index, const void* param);
|
||||
s32 PS4_SYSV_ABI sceVideoOutClose(s32 handle);
|
||||
s32 PS4_SYSV_ABI sceVideoOutGetEventId(const Kernel::SceKernelEvent* ev);
|
||||
s32 PS4_SYSV_ABI sceVideoOutGetEventData(const Kernel::SceKernelEvent* ev, s64* data);
|
||||
s32 PS4_SYSV_ABI sceVideoOutGetEventId(const Kernel::OrbisKernelEvent* ev);
|
||||
s32 PS4_SYSV_ABI sceVideoOutGetEventData(const Kernel::OrbisKernelEvent* ev, s64* data);
|
||||
s32 PS4_SYSV_ABI sceVideoOutColorSettingsSetGamma(SceVideoOutColorSettings* settings, float gamma);
|
||||
s32 PS4_SYSV_ABI sceVideoOutAdjustColor(s32 handle, const SceVideoOutColorSettings* settings);
|
||||
|
||||
// Internal system functions
|
||||
s32 sceVideoOutSubmitEopFlip(s32 handle, u32 buf_id, u32 mode, u32 arg, void** unk);
|
||||
s32 sceVideoOutSubmitEopFlip(s32 handle, u32 buf_id, u32 mode, s64 flip_arg, void** unk);
|
||||
|
||||
void RegisterLib(Core::Loader::SymbolsResolver* sym);
|
||||
|
||||
|
||||
@ -5,6 +5,7 @@
|
||||
#include "common/assert.h"
|
||||
#include "common/decoder.h"
|
||||
#include "common/signal_context.h"
|
||||
#include "core/libraries/kernel/threads/exception.h"
|
||||
#include "core/signals.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
@ -17,6 +18,13 @@
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef _WIN32
|
||||
namespace Libraries::Kernel {
|
||||
void SigactionHandler(int native_signum, siginfo_t* inf, ucontext_t* raw_context);
|
||||
extern std::array<SceKernelExceptionHandler, 32> Handlers;
|
||||
} // namespace Libraries::Kernel
|
||||
#endif
|
||||
|
||||
namespace Core {
|
||||
|
||||
#if defined(_WIN32)
|
||||
@ -66,7 +74,7 @@ static std::string DisassembleInstruction(void* code_address) {
|
||||
return buffer;
|
||||
}
|
||||
|
||||
static void SignalHandler(int sig, siginfo_t* info, void* raw_context) {
|
||||
void SignalHandler(int sig, siginfo_t* info, void* raw_context) {
|
||||
const auto* signals = Signals::Instance();
|
||||
|
||||
auto* code_address = Common::GetRip(raw_context);
|
||||
@ -76,6 +84,13 @@ static void SignalHandler(int sig, siginfo_t* info, void* raw_context) {
|
||||
case SIGBUS: {
|
||||
const bool is_write = Common::IsWriteError(raw_context);
|
||||
if (!signals->DispatchAccessViolation(raw_context, info->si_addr)) {
|
||||
// If the guest has installed a custom signal handler, and the access violation didn't
|
||||
// come from HLE memory tracking, pass the signal on
|
||||
if (Libraries::Kernel::Handlers[sig]) {
|
||||
Libraries::Kernel::SigactionHandler(sig, info,
|
||||
reinterpret_cast<ucontext_t*>(raw_context));
|
||||
return;
|
||||
}
|
||||
UNREACHABLE_MSG("Unhandled access violation at code address {}: {} address {}",
|
||||
fmt::ptr(code_address), is_write ? "Write to" : "Read from",
|
||||
fmt::ptr(info->si_addr));
|
||||
@ -84,17 +99,23 @@ static void SignalHandler(int sig, siginfo_t* info, void* raw_context) {
|
||||
}
|
||||
case SIGILL:
|
||||
if (!signals->DispatchIllegalInstruction(raw_context)) {
|
||||
if (Libraries::Kernel::Handlers[sig]) {
|
||||
Libraries::Kernel::SigactionHandler(sig, info,
|
||||
reinterpret_cast<ucontext_t*>(raw_context));
|
||||
return;
|
||||
}
|
||||
UNREACHABLE_MSG("Unhandled illegal instruction at code address {}: {}",
|
||||
fmt::ptr(code_address), DisassembleInstruction(code_address));
|
||||
}
|
||||
break;
|
||||
case SIGUSR1: { // Sleep thread until signal is received
|
||||
sigset_t sigset;
|
||||
sigemptyset(&sigset);
|
||||
sigaddset(&sigset, SIGUSR1);
|
||||
sigwait(&sigset, &sig);
|
||||
} break;
|
||||
default:
|
||||
if (sig == SIGSLEEP) {
|
||||
// Sleep thread until signal is received again
|
||||
sigset_t sigset;
|
||||
sigemptyset(&sigset);
|
||||
sigaddset(&sigset, SIGSLEEP);
|
||||
sigwait(&sigset, &sig);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -116,7 +137,7 @@ SignalDispatch::SignalDispatch() {
|
||||
"Failed to register access violation signal handler.");
|
||||
ASSERT_MSG(sigaction(SIGILL, &action, nullptr) == 0,
|
||||
"Failed to register illegal instruction signal handler.");
|
||||
ASSERT_MSG(sigaction(SIGUSR1, &action, nullptr) == 0,
|
||||
ASSERT_MSG(sigaction(SIGSLEEP, &action, nullptr) == 0,
|
||||
"Failed to register sleep signal handler.");
|
||||
#endif
|
||||
}
|
||||
|
||||
@ -1,17 +1,29 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2024-2026 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <set>
|
||||
#include <signal.h>
|
||||
#include "common/singleton.h"
|
||||
#include "common/types.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
#define SIGSLEEP -1
|
||||
#elif defined(__APPLE__)
|
||||
#define SIGSLEEP SIGVTALRM
|
||||
#else
|
||||
#define SIGSLEEP SIGRTMAX
|
||||
#endif
|
||||
namespace Core {
|
||||
|
||||
using AccessViolationHandler = bool (*)(void* context, void* fault_address);
|
||||
using IllegalInstructionHandler = bool (*)(void* context);
|
||||
|
||||
#ifndef _WIN32
|
||||
void SignalHandler(int sig, siginfo_t* info, void* raw_context);
|
||||
#endif
|
||||
|
||||
/// Receives OS signals and dispatches to the appropriate handlers.
|
||||
class SignalDispatch {
|
||||
public:
|
||||
|
||||
@ -61,7 +61,10 @@ template <class ReturnType, class... FuncArgs, class... CallArgs>
|
||||
ReturnType ExecuteGuest(PS4_SYSV_ABI ReturnType (*func)(FuncArgs...), CallArgs&&... args) {
|
||||
EnsureThreadInitialized();
|
||||
// clear stack to avoid trash from EnsureThreadInitialized
|
||||
ClearStack<12_KB>();
|
||||
auto* tcb = GetTcbBase();
|
||||
if (tcb != nullptr && tcb->tcb_fiber == nullptr) {
|
||||
ClearStack<12_KB>();
|
||||
}
|
||||
return func(std::forward<CallArgs>(args)...);
|
||||
}
|
||||
|
||||
|
||||
@ -239,6 +239,7 @@ void Emulator::Run(std::filesystem::path file, std::vector<std::string> args,
|
||||
LOG_INFO(Config, "Game-specific config exists: {}", has_game_config);
|
||||
|
||||
LOG_INFO(Config, "General LogType: {}", Config::getLogType());
|
||||
LOG_INFO(Config, "General isIdenticalLogGrouped: {}", Config::groupIdenticalLogs());
|
||||
LOG_INFO(Config, "General isNeo: {}", Config::isNeoModeConsole());
|
||||
LOG_INFO(Config, "General isDevKit: {}", Config::isDevKitConsole());
|
||||
LOG_INFO(Config, "General isConnectedToNetwork: {}", Config::getIsConnectedToNetwork());
|
||||
@ -340,11 +341,8 @@ void Emulator::Run(std::filesystem::path file, std::vector<std::string> args,
|
||||
|
||||
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);
|
||||
}
|
||||
mnt->Mount(mount_data_dir, "/data"); // should just exist, manually create with game serial
|
||||
const auto& mount_data_dir = Common::FS::GetUserPath(Common::FS::PathType::GameDataDir);
|
||||
mnt->Mount(mount_data_dir, "/data");
|
||||
|
||||
// Mounting temp folders
|
||||
const auto& mount_temp_dir = Common::FS::GetUserPath(Common::FS::PathType::TempDataDir) / id;
|
||||
|
||||
@ -462,50 +462,134 @@ static std::pair<u32, u32> SanitizeCopyLayers(const ImageInfo& src_info, const I
|
||||
void Image::CopyImage(Image& src_image) {
|
||||
const auto& src_info = src_image.info;
|
||||
const u32 num_mips = std::min(src_info.resources.levels, info.resources.levels);
|
||||
ASSERT(src_info.resources.layers == info.resources.layers || num_mips == 1);
|
||||
|
||||
// Check format compatibility
|
||||
if (src_info.pixel_format != info.pixel_format) {
|
||||
LOG_DEBUG(Render_Vulkan,
|
||||
"Copy between different formats: src={}, dst={}. Color may be incorrect.",
|
||||
vk::to_string(src_info.pixel_format), vk::to_string(info.pixel_format));
|
||||
}
|
||||
|
||||
const u32 width = src_info.size.width;
|
||||
const u32 height = src_info.size.height;
|
||||
const u32 depth =
|
||||
const u32 base_depth =
|
||||
info.type == AmdGpu::ImageType::Color3D ? info.size.depth : src_info.size.depth;
|
||||
|
||||
auto [test_src_layers, test_dst_layers] = SanitizeCopyLayers(src_info, info, base_depth);
|
||||
|
||||
ASSERT(test_src_layers == test_dst_layers || num_mips == 1 ||
|
||||
(ConvertImageType(src_info.type) != ConvertImageType(info.type) &&
|
||||
(test_src_layers == 1 || test_dst_layers == 1)));
|
||||
|
||||
SetBackingSamples(info.num_samples, false);
|
||||
src_image.SetBackingSamples(src_info.num_samples);
|
||||
|
||||
boost::container::small_vector<vk::ImageCopy, 8> image_copies;
|
||||
|
||||
const bool src_is_2d = ConvertImageType(src_info.type) == vk::ImageType::e2D;
|
||||
const bool src_is_3d = ConvertImageType(src_info.type) == vk::ImageType::e3D;
|
||||
const bool dst_is_2d = ConvertImageType(info.type) == vk::ImageType::e2D;
|
||||
const bool dst_is_3d = ConvertImageType(info.type) == vk::ImageType::e3D;
|
||||
|
||||
const bool is_2d_to_3d = src_is_2d && dst_is_3d;
|
||||
const bool is_3d_to_2d = src_is_3d && dst_is_2d;
|
||||
const bool is_same_type = !is_2d_to_3d && !is_3d_to_2d;
|
||||
|
||||
// Determine aspect mask - exclude stencil
|
||||
vk::ImageAspectFlags aspect = vk::ImageAspectFlagBits::eColor;
|
||||
|
||||
// For depth/stencil images, only copy the depth aspect (skip stencil)
|
||||
if (src_image.aspect_mask & vk::ImageAspectFlagBits::eDepth) {
|
||||
aspect = vk::ImageAspectFlagBits::eDepth;
|
||||
}
|
||||
|
||||
for (u32 mip = 0; mip < num_mips; ++mip) {
|
||||
const auto mip_w = std::max(width >> mip, 1u);
|
||||
const auto mip_h = std::max(height >> mip, 1u);
|
||||
const auto mip_d = std::max(depth >> mip, 1u);
|
||||
const auto [src_layers, dst_layers] = SanitizeCopyLayers(src_info, info, mip_d);
|
||||
const auto mip_d = std::max(base_depth >> mip, 1u);
|
||||
|
||||
image_copies.emplace_back(vk::ImageCopy{
|
||||
.srcSubresource{
|
||||
.aspectMask = src_image.aspect_mask & ~vk::ImageAspectFlagBits::eStencil,
|
||||
.mipLevel = mip,
|
||||
.baseArrayLayer = 0,
|
||||
.layerCount = src_layers,
|
||||
},
|
||||
.dstSubresource{
|
||||
.aspectMask = aspect_mask & ~vk::ImageAspectFlagBits::eStencil,
|
||||
.mipLevel = mip,
|
||||
.baseArrayLayer = 0,
|
||||
.layerCount = dst_layers,
|
||||
},
|
||||
.extent = {mip_w, mip_h, mip_d},
|
||||
});
|
||||
auto [src_layers, dst_layers] = SanitizeCopyLayers(src_info, info, mip_d);
|
||||
|
||||
if (is_same_type) {
|
||||
u32 copy_layers = std::min(src_layers, dst_layers);
|
||||
|
||||
if (src_is_3d)
|
||||
src_layers = 1;
|
||||
if (dst_is_3d)
|
||||
dst_layers = 1;
|
||||
|
||||
vk::ImageCopy copy_region = {
|
||||
.srcSubresource{
|
||||
.aspectMask = aspect,
|
||||
.mipLevel = mip,
|
||||
.baseArrayLayer = 0,
|
||||
.layerCount = copy_layers,
|
||||
},
|
||||
.dstSubresource{
|
||||
.aspectMask = aspect,
|
||||
.mipLevel = mip,
|
||||
.baseArrayLayer = 0,
|
||||
.layerCount = copy_layers,
|
||||
},
|
||||
.extent = vk::Extent3D(mip_w, mip_h, mip_d),
|
||||
};
|
||||
image_copies.push_back(copy_region);
|
||||
} else if (is_2d_to_3d) {
|
||||
vk::ImageCopy copy_region = {
|
||||
.srcSubresource{
|
||||
.aspectMask = aspect,
|
||||
.mipLevel = mip,
|
||||
.baseArrayLayer = 0,
|
||||
.layerCount = src_layers,
|
||||
},
|
||||
.dstSubresource{
|
||||
.aspectMask = aspect,
|
||||
.mipLevel = mip,
|
||||
.baseArrayLayer = 0,
|
||||
.layerCount = 1,
|
||||
},
|
||||
.extent = vk::Extent3D(mip_w, mip_h, src_layers),
|
||||
};
|
||||
image_copies.push_back(copy_region);
|
||||
} else if (is_3d_to_2d) {
|
||||
vk::ImageCopy copy_region = {
|
||||
.srcSubresource{
|
||||
.aspectMask = aspect,
|
||||
.mipLevel = mip,
|
||||
.baseArrayLayer = 0,
|
||||
.layerCount = 1,
|
||||
},
|
||||
.dstSubresource{
|
||||
.aspectMask = aspect,
|
||||
.mipLevel = mip,
|
||||
.baseArrayLayer = 0,
|
||||
.layerCount = dst_layers,
|
||||
},
|
||||
.extent = vk::Extent3D(mip_w, mip_h, dst_layers),
|
||||
};
|
||||
image_copies.push_back(copy_region);
|
||||
}
|
||||
}
|
||||
|
||||
scheduler->EndRendering();
|
||||
|
||||
// Remove the pipeline stage flags - they don't belong here
|
||||
src_image.Transit(vk::ImageLayout::eTransferSrcOptimal, vk::AccessFlagBits2::eTransferRead, {});
|
||||
|
||||
Transit(vk::ImageLayout::eTransferDstOptimal, vk::AccessFlagBits2::eTransferWrite, {});
|
||||
|
||||
auto cmdbuf = scheduler->CommandBuffer();
|
||||
cmdbuf.copyImage(src_image.GetImage(), src_image.backing->state.layout, GetImage(),
|
||||
backing->state.layout, image_copies);
|
||||
|
||||
Transit(vk::ImageLayout::eGeneral,
|
||||
vk::AccessFlagBits2::eShaderRead | vk::AccessFlagBits2::eTransferRead, {});
|
||||
if (!image_copies.empty()) {
|
||||
cmdbuf.copyImage(src_image.GetImage(), vk::ImageLayout::eTransferSrcOptimal, GetImage(),
|
||||
vk::ImageLayout::eTransferDstOptimal, image_copies);
|
||||
}
|
||||
|
||||
// Remove pipeline stage flags here too
|
||||
src_image.Transit(vk::ImageLayout::eShaderReadOnlyOptimal, vk::AccessFlagBits2::eShaderRead,
|
||||
{});
|
||||
|
||||
Transit(vk::ImageLayout::eShaderReadOnlyOptimal, vk::AccessFlagBits2::eShaderRead, {});
|
||||
}
|
||||
|
||||
void Image::CopyImageWithBuffer(Image& src_image, vk::Buffer buffer, u64 offset) {
|
||||
|
||||
@ -297,6 +297,14 @@ std::tuple<ImageId, int, int> TextureCache::ResolveOverlap(const ImageInfo& imag
|
||||
if (image_info.guest_address == cache_image.info.guest_address) {
|
||||
const u32 lhs_block_size = image_info.num_bits * image_info.num_samples;
|
||||
const u32 rhs_block_size = cache_image.info.num_bits * cache_image.info.num_samples;
|
||||
|
||||
if (image_info.pitch != cache_image.info.pitch) {
|
||||
if (safe_to_delete) {
|
||||
FreeImage(cache_image_id);
|
||||
}
|
||||
return {merged_image_id, -1, -1};
|
||||
}
|
||||
|
||||
if (image_info.BlockDim() != cache_image.info.BlockDim() ||
|
||||
lhs_block_size != rhs_block_size) {
|
||||
// Very likely this kind of overlap is caused by allocation from a pool.
|
||||
@ -346,6 +354,111 @@ std::tuple<ImageId, int, int> TextureCache::ResolveOverlap(const ImageInfo& imag
|
||||
return {merged_image_id, -1, -1};
|
||||
}
|
||||
|
||||
// Enhanced debug logging for unreachable case
|
||||
// Calculate expected size based on format and dimensions
|
||||
u64 expected_size =
|
||||
(static_cast<u64>(image_info.size.width) * static_cast<u64>(image_info.size.height) *
|
||||
static_cast<u64>(image_info.size.depth) * static_cast<u64>(image_info.num_bits) / 8);
|
||||
LOG_ERROR(Render_Vulkan,
|
||||
"Unresolvable image overlap with equal memory address:\n"
|
||||
"=== OLD IMAGE (cached) ===\n"
|
||||
" Address: {:#x}\n"
|
||||
" Size: {:#x} bytes\n"
|
||||
" Format: {}\n"
|
||||
" Type: {}\n"
|
||||
" Width: {}\n"
|
||||
" Height: {}\n"
|
||||
" Depth: {}\n"
|
||||
" Pitch: {}\n"
|
||||
" Mip levels: {}\n"
|
||||
" Array layers: {}\n"
|
||||
" Samples: {}\n"
|
||||
" Tile mode: {:#x}\n"
|
||||
" Block size: {} bits\n"
|
||||
" Is block-comp: {}\n"
|
||||
" Guest size: {:#x}\n"
|
||||
" Last accessed: tick {}\n"
|
||||
" Safe to delete: {}\n"
|
||||
"\n"
|
||||
"=== NEW IMAGE (requested) ===\n"
|
||||
" Address: {:#x}\n"
|
||||
" Size: {:#x} bytes\n"
|
||||
" Format: {}\n"
|
||||
" Type: {}\n"
|
||||
" Width: {}\n"
|
||||
" Height: {}\n"
|
||||
" Depth: {}\n"
|
||||
" Pitch: {}\n"
|
||||
" Mip levels: {}\n"
|
||||
" Array layers: {}\n"
|
||||
" Samples: {}\n"
|
||||
" Tile mode: {:#x}\n"
|
||||
" Block size: {} bits\n"
|
||||
" Is block-comp: {}\n"
|
||||
" Guest size: {:#x}\n"
|
||||
"\n"
|
||||
"=== COMPARISON ===\n"
|
||||
" Same format: {}\n"
|
||||
" Same type: {}\n"
|
||||
" Same tile mode: {}\n"
|
||||
" Same block size: {}\n"
|
||||
" Same BlockDim: {}\n"
|
||||
" Same pitch: {}\n"
|
||||
" Old resources <= new: {} (old: {}, new: {})\n"
|
||||
" Old size <= new size: {}\n"
|
||||
" Expected size (calc): {} bytes\n"
|
||||
" Size ratio (new/expected): {:.2f}x\n"
|
||||
" Size ratio (new/old): {:.2f}x\n"
|
||||
" Old vs expected diff: {} bytes ({:+.2f}%)\n"
|
||||
" New vs expected diff: {} bytes ({:+.2f}%)\n"
|
||||
" Merged image ID: {}\n"
|
||||
" Binding type: {}\n"
|
||||
" Current tick: {}\n"
|
||||
" Age (ticks since last access): {}",
|
||||
|
||||
// Old image details
|
||||
cache_image.info.guest_address, cache_image.info.guest_size,
|
||||
vk::to_string(cache_image.info.pixel_format),
|
||||
static_cast<int>(cache_image.info.type), cache_image.info.size.width,
|
||||
cache_image.info.size.height, cache_image.info.size.depth, cache_image.info.pitch,
|
||||
cache_image.info.resources.levels, cache_image.info.resources.layers,
|
||||
cache_image.info.num_samples, static_cast<u32>(cache_image.info.tile_mode),
|
||||
cache_image.info.num_bits, cache_image.info.props.is_block,
|
||||
cache_image.info.guest_size, cache_image.tick_accessed_last, safe_to_delete,
|
||||
|
||||
// New image details
|
||||
image_info.guest_address, image_info.guest_size,
|
||||
vk::to_string(image_info.pixel_format), static_cast<int>(image_info.type),
|
||||
image_info.size.width, image_info.size.height, image_info.size.depth,
|
||||
image_info.pitch, image_info.resources.levels, image_info.resources.layers,
|
||||
image_info.num_samples, static_cast<u32>(image_info.tile_mode),
|
||||
image_info.num_bits, image_info.props.is_block, image_info.guest_size,
|
||||
|
||||
// Comparison
|
||||
(image_info.pixel_format == cache_image.info.pixel_format),
|
||||
(image_info.type == cache_image.info.type),
|
||||
(image_info.tile_mode == cache_image.info.tile_mode),
|
||||
(image_info.num_bits == cache_image.info.num_bits),
|
||||
(image_info.BlockDim() == cache_image.info.BlockDim()),
|
||||
(image_info.pitch == cache_image.info.pitch),
|
||||
(cache_image.info.resources <= image_info.resources),
|
||||
cache_image.info.resources.levels, image_info.resources.levels,
|
||||
(cache_image.info.guest_size <= image_info.guest_size), expected_size,
|
||||
|
||||
// Size ratios
|
||||
static_cast<double>(image_info.guest_size) / expected_size,
|
||||
static_cast<double>(image_info.guest_size) / cache_image.info.guest_size,
|
||||
|
||||
// Difference between actual and expected sizes with percentages
|
||||
static_cast<s64>(cache_image.info.guest_size) - static_cast<s64>(expected_size),
|
||||
(static_cast<double>(cache_image.info.guest_size) / expected_size - 1.0) * 100.0,
|
||||
|
||||
static_cast<s64>(image_info.guest_size) - static_cast<s64>(expected_size),
|
||||
(static_cast<double>(image_info.guest_size) / expected_size - 1.0) * 100.0,
|
||||
|
||||
merged_image_id.index, static_cast<int>(binding), scheduler.CurrentTick(),
|
||||
scheduler.CurrentTick() - cache_image.tick_accessed_last);
|
||||
|
||||
UNREACHABLE_MSG("Encountered unresolvable image overlap with equal memory address.");
|
||||
}
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user