From fe4355877977974bf5c629390f7d73b111073872 Mon Sep 17 00:00:00 2001 From: GPUCode Date: Thu, 15 Feb 2024 00:52:57 +0200 Subject: [PATCH 1/3] common: Rework timekeeping with native RDTSC and port to linux --- .gitmodules | 7 +- CMakeLists.txt | 17 +- src/common/log.cpp | 4 + src/common/native_clock.cpp | 43 ++++ src/common/native_clock.h | 32 +++ src/common/rdtsc.cpp | 60 ++++++ src/common/rdtsc.h | 37 ++++ src/common/timer.cpp | 108 ---------- src/common/timer.h | 43 ---- src/common/types.h | 10 +- src/common/uint128.h | 115 +++++++++++ src/core/PS4/GPU/gpu_memory.cpp | 2 +- src/core/PS4/HLE/Graphics/graphics_ctx.h | 3 +- src/core/file_sys/fs.h | 4 +- src/core/hle/kernel/Objects/event_queue.cpp | 32 +-- src/core/hle/kernel/Objects/event_queue.h | 1 + .../hle/kernel/Objects/physical_memory.cpp | 2 +- src/core/hle/kernel/event_queues.h | 2 +- src/core/hle/libraries/libc/libc_math.cpp | 16 +- src/core/hle/libraries/libc/libc_stdio.cpp | 2 +- .../hle/libraries/libkernel/libkernel.cpp | 11 + .../libraries/libkernel/time_management.cpp | 16 +- src/core/linker.cpp | 4 +- src/core/loader/elf.cpp | 2 +- src/core/virtual_memory.cpp | 12 +- src/emuTimer.cpp | 23 --- src/emuTimer.h | 10 - src/emulator.cpp | 34 +--- src/main.cpp | 2 - src/video_core/gpu_memory.cpp | 188 ++++++++++++++++++ src/video_core/gpu_memory.h | 86 ++++++++ src/video_core/tile_manager.cpp | 151 ++++++++++++++ src/video_core/tile_manager.h | 9 + third-party/CMakeLists.txt | 3 +- third-party/fmt | 2 +- third-party/spdlog | 2 +- third-party/vulkan | 2 +- 37 files changed, 818 insertions(+), 279 deletions(-) create mode 100644 src/common/native_clock.cpp create mode 100644 src/common/native_clock.h create mode 100644 src/common/rdtsc.cpp create mode 100644 src/common/rdtsc.h delete mode 100644 src/common/timer.cpp delete mode 100644 src/common/timer.h create mode 100644 src/common/uint128.h delete mode 100644 src/emuTimer.cpp delete mode 100644 src/emuTimer.h create mode 100644 src/video_core/gpu_memory.cpp create mode 100644 src/video_core/gpu_memory.h create mode 100644 src/video_core/tile_manager.cpp create mode 100644 src/video_core/tile_manager.h diff --git a/.gitmodules b/.gitmodules index 9cfbedd0..8a240d58 100644 --- a/.gitmodules +++ b/.gitmodules @@ -33,11 +33,10 @@ path = third-party/toml11 url = https://github.com/ToruNiina/toml11 branch = master -[submodule "third-party/vulkan"] - path = third-party/vulkan - url = https://github.com/shadps4/vulkan.git - branch = main [submodule "third-party/xxHash"] path = third-party/xxHash url = https://github.com/Cyan4973/xxHash.git branch = dev +[submodule "third-party/vulkan"] + path = third-party/vulkan + url = https://github.com/GPUCode/vulkan diff --git a/CMakeLists.txt b/CMakeLists.txt index 7db7e5b6..c3b2dca3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -30,8 +30,8 @@ endfunction() add_subdirectory(third-party) include_directories(src) -set(LIBC_SOURCES src/core/hle/libraries/libc/Libc.cpp - src/core/hle/libraries/libc/Libc.h +set(LIBC_SOURCES src/core/hle/libraries/libc/libc.cpp + src/core/hle/libraries/libc/libc.h src/core/hle/libraries/libc/printf.h src/core/hle/libraries/libc/va_ctx.h src/core/hle/libraries/libc/libc_cxa.cpp @@ -77,12 +77,15 @@ add_executable(shadps4 src/common/fs_file.h src/common/log.cpp src/common/log.h + src/common/native_clock.cpp + src/common/native_clock.h + src/common/rdtsc.cpp + src/common/rdtsc.h src/common/singleton.h src/common/string_util.cpp src/common/string_util.h - src/common/timer.cpp - src/common/timer.h src/common/types.h + src/common/uint128.h src/common/version.h ${LIBC_SOURCES} ${USERSERVICE_SOURCES} @@ -143,8 +146,6 @@ add_executable(shadps4 src/core/PS4/HLE/Graphics/graphics_render.h src/core/PS4/GPU/tile_manager.cpp src/core/PS4/GPU/tile_manager.h - src/emuTimer.cpp - src/emuTimer.h src/core/hle/libraries/libkernel/time_management.cpp src/core/hle/libraries/libkernel/time_management.h "src/common/io_file.cpp" "src/common/io_file.h") @@ -161,6 +162,8 @@ add_custom_command(TARGET shadps4 POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_if_different $ $) -add_custom_command(TARGET shadps4 POST_BUILD +if (WIN32) + add_custom_command(TARGET shadps4 POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_if_different "${PROJECT_SOURCE_DIR}/third-party/winpthread/bin/libwinpthread-1.dll" $) +endif() diff --git a/src/common/log.cpp b/src/common/log.cpp index 535d4bd0..f4bb55f8 100644 --- a/src/common/log.cpp +++ b/src/common/log.cpp @@ -109,7 +109,11 @@ int Init(bool use_stdout) { if (use_stdout) { sinks.push_back(std::make_shared()); } +#ifdef _WIN64 sinks.push_back(std::make_shared(L"shadps4.txt", true)); +#else + sinks.push_back(std::make_shared("shadps4.txt", true)); +#endif spdlog::set_default_logger(std::make_shared("shadps4 logger", begin(sinks), end(sinks))); auto f = std::make_unique("%^|%L|: %v%$", spdlog::pattern_time_type::local, std::string("")); // disable eol spdlog::set_formatter(std::move(f)); diff --git a/src/common/native_clock.cpp b/src/common/native_clock.cpp new file mode 100644 index 00000000..e722de0d --- /dev/null +++ b/src/common/native_clock.cpp @@ -0,0 +1,43 @@ +// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "common/uint128.h" +#include "common/native_clock.h" +#include "common/rdtsc.h" +#ifdef _WIN64 +#include +#else +#include +#endif + +namespace Common { + +NativeClock::NativeClock() + : rdtsc_frequency{EstimateRDTSCFrequency()}, ns_rdtsc_factor{GetFixedPoint64Factor(std::nano::den, + rdtsc_frequency)}, + us_rdtsc_factor{GetFixedPoint64Factor(std::micro::den, rdtsc_frequency)}, + ms_rdtsc_factor{GetFixedPoint64Factor(std::milli::den, rdtsc_frequency)} {} + +u64 NativeClock::GetTimeNS() const { + return MultiplyHigh(GetUptime(), ns_rdtsc_factor); +} + +u64 NativeClock::GetTimeUS() const { + return MultiplyHigh(GetUptime(), us_rdtsc_factor); +} + +u64 NativeClock::GetTimeMS() const { + return MultiplyHigh(GetUptime(), ms_rdtsc_factor); +} + +u64 NativeClock::GetUptime() const { + return FencedRDTSC(); +} + +u64 NativeClock::GetProcessTimeUS() const { + timespec ret; + clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &ret); + return ret.tv_nsec / 1000 + ret.tv_sec * 1000000; +} + +} // namespace Common::X64 diff --git a/src/common/native_clock.h b/src/common/native_clock.h new file mode 100644 index 00000000..227c8d1a --- /dev/null +++ b/src/common/native_clock.h @@ -0,0 +1,32 @@ +// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include +#include "common/types.h" + +namespace Common { + +class NativeClock final { +public: + explicit NativeClock(); + + u64 GetTscFrequency() const { + return rdtsc_frequency; + } + + u64 GetTimeNS() const; + u64 GetTimeUS() const; + u64 GetTimeMS() const; + u64 GetUptime() const; + u64 GetProcessTimeUS() const; + +private: + u64 rdtsc_frequency; + u64 ns_rdtsc_factor; + u64 us_rdtsc_factor; + u64 ms_rdtsc_factor; +}; + +} // namespace Common diff --git a/src/common/rdtsc.cpp b/src/common/rdtsc.cpp new file mode 100644 index 00000000..8d231a98 --- /dev/null +++ b/src/common/rdtsc.cpp @@ -0,0 +1,60 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include +#include "common/rdtsc.h" +#include "common/uint128.h" + +#ifdef _WIN64 +#include +#endif + +namespace Common { + +static constexpr size_t SecondToNanoseconds = 1000000000ULL; + +template +static u64 RoundToNearest(u64 value) { + const auto mod = value % Nearest; + return mod >= (Nearest / 2) ? (value - mod + Nearest) : (value - mod); +} + +static u64 GetTimeNs() { +#ifdef _WIN64 + // GetSystemTimePreciseAsFileTime returns the file time in 100ns units. + static constexpr u64 Multiplier = 100; + // Convert Windows epoch to Unix epoch. + static constexpr u64 WindowsEpochToUnixEpoch = 0x19DB1DED53E8000LL; + FILETIME filetime; + GetSystemTimePreciseAsFileTime(&filetime); + return Multiplier * ((static_cast(filetime.dwHighDateTime) << 32) + + static_cast(filetime.dwLowDateTime) - WindowsEpochToUnixEpoch); +#elif defined(__APPLE__) + return clock_gettime_nsec_np(CLOCK_REALTIME); +#else + timespec ts; + clock_gettime(CLOCK_REALTIME, &ts); + return ts.tv_sec * SecondToNanoseconds + ts.tv_nsec; +#endif +} + +u64 EstimateRDTSCFrequency() { + // Discard the first result measuring the rdtsc. + FencedRDTSC(); + std::this_thread::sleep_for(std::chrono::milliseconds{1}); + FencedRDTSC(); + + // Get the current time. + const auto start_time = GetTimeNs(); + const u64 tsc_start = FencedRDTSC(); + // Wait for 100 milliseconds. + std::this_thread::sleep_for(std::chrono::milliseconds{100}); + const auto end_time = GetTimeNs(); + const u64 tsc_end = FencedRDTSC(); + // Calculate differences. + const u64 tsc_diff = tsc_end - tsc_start; + const u64 tsc_freq = MultiplyAndDivide64(tsc_diff, 1000000000ULL, end_time - start_time); + return RoundToNearest<100'000>(tsc_freq); +} + +} // namespace Common diff --git a/src/common/rdtsc.h b/src/common/rdtsc.h new file mode 100644 index 00000000..3180273e --- /dev/null +++ b/src/common/rdtsc.h @@ -0,0 +1,37 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#ifdef _MSC_VER +#include +#endif + +#include "common/types.h" + +namespace Common { + +#ifdef _MSC_VER +__forceinline static u64 FencedRDTSC() { + _mm_lfence(); + _ReadWriteBarrier(); + const u64 result = __rdtsc(); + _mm_lfence(); + _ReadWriteBarrier(); + return result; +} +#else +static inline u64 FencedRDTSC() { + u64 eax; + u64 edx; + asm volatile("lfence\n\t" + "rdtsc\n\t" + "lfence\n\t" + : "=a"(eax), "=d"(edx)); + return (edx << 32) | eax; +} +#endif + +u64 EstimateRDTSCFrequency(); + +} // namespace Common diff --git a/src/common/timer.cpp b/src/common/timer.cpp deleted file mode 100644 index fa3d172b..00000000 --- a/src/common/timer.cpp +++ /dev/null @@ -1,108 +0,0 @@ -#include "common/timer.h" - -#ifdef _WIN64 -#include -#endif - -namespace Common { - -Timer::Timer() { -#ifdef _WIN64 - LARGE_INTEGER f; - QueryPerformanceFrequency(&f); - m_Frequency = f.QuadPart; -#else -#error Unimplemented Timer constructor -#endif -} - -void Timer::Start() { -#ifdef _WIN64 - LARGE_INTEGER c; - QueryPerformanceCounter(&c); - m_StartTime = c.QuadPart; -#else -#error Unimplemented Timer::Start() -#endif - m_is_timer_paused = false; -} - -void Timer::Pause() { -#ifdef _WIN64 - LARGE_INTEGER c; - QueryPerformanceCounter(&c); - m_PauseTime = c.QuadPart; -#else -#error Unimplemented Timer::Pause() -#endif - m_is_timer_paused = true; -} - -void Timer::Resume() { - u64 current_time = 0; -#ifdef _WIN64 - LARGE_INTEGER c; - QueryPerformanceCounter(&c); - current_time = c.QuadPart; -#else -#error Unimplemented Timer::Resume() -#endif - m_StartTime += current_time - m_PauseTime; - m_is_timer_paused = false; -} - -double Timer::GetTimeMsec() const { - if (m_is_timer_paused) { - return 1000.0 * (static_cast(m_PauseTime - m_StartTime)) / static_cast(m_Frequency); - } - - u64 current_time = 0; -#ifdef _WIN64 - LARGE_INTEGER c; - QueryPerformanceCounter(&c); - current_time = c.QuadPart; -#else -#error Unimplemented Timer::GetTimeMsec() -#endif - return 1000.0 * (static_cast(current_time - m_StartTime)) / static_cast(m_Frequency); -} - -double Timer::GetTimeSec() const { - if (m_is_timer_paused) { - return (static_cast(m_PauseTime - m_StartTime)) / static_cast(m_Frequency); - } - - u64 current_time = 0; -#ifdef _WIN64 - LARGE_INTEGER c; - QueryPerformanceCounter(&c); - current_time = c.QuadPart; -#else -#error Unimplemented Timer::GetTimeSec() -#endif - return (static_cast(current_time - m_StartTime)) / static_cast(m_Frequency); -} - -u64 Timer::GetTicks() const { - if (m_is_timer_paused) { - return (m_PauseTime - m_StartTime); - } - - u64 current_time = 0; -#ifdef _WIN64 - LARGE_INTEGER c; - QueryPerformanceCounter(&c); - current_time = c.QuadPart; -#else -#error Unimplemented Timer::GetTicks() -#endif - return (current_time - m_StartTime); -} - -u64 Timer::getQueryPerformanceCounter() { - LARGE_INTEGER c; - QueryPerformanceCounter(&c); - return c.QuadPart; -} - -} // namespace Common diff --git a/src/common/timer.h b/src/common/timer.h deleted file mode 100644 index e390e7e6..00000000 --- a/src/common/timer.h +++ /dev/null @@ -1,43 +0,0 @@ -#pragma once - -#include "common/types.h" - -namespace Common { - -class Timer final { -public: - Timer(); - ~Timer() = default; - - void Start(); - void Pause(); - void Resume(); - - bool IsPaused() const { - return m_is_timer_paused; - } - - u64 GetFrequency() const { - return m_Frequency; - } - - double GetTimeMsec() const; - double GetTimeSec() const; - u64 GetTicks() const; - - [[nodiscard]] static u64 getQueryPerformanceCounter(); - -public: - Timer(const Timer&) = delete; - Timer& operator=(const Timer&) = delete; - Timer(Timer&&) = delete; - Timer& operator=(Timer&&) = delete; - -private: - bool m_is_timer_paused = true; - u64 m_Frequency{}; - u64 m_StartTime{}; - u64 m_PauseTime{}; -}; - -} // namespace Common diff --git a/src/common/types.h b/src/common/types.h index 9ddfede9..4d86ec25 100644 --- a/src/common/types.h +++ b/src/common/types.h @@ -1,5 +1,6 @@ #pragma once +#include #include using s08 = std::int8_t; @@ -15,9 +16,12 @@ using u64 = std::uint64_t; using f32 = float; using f64 = double; +using u128 = std::array; +static_assert(sizeof(u128) == 16, "u128 must be 128 bits wide"); + #define PS4_SYSV_ABI __attribute__((sysv_abi)) // UDLs for memory size values -constexpr u64 operator""_KB(u64 x) { return 1024ULL * x; } -constexpr u64 operator""_MB(u64 x) { return 1024_KB * x; } -constexpr u64 operator""_GB(u64 x) { return 1024_MB * x; } +constexpr unsigned long long operator""_KB(unsigned long long x) { return 1024ULL * x; } +constexpr unsigned long long operator""_MB(unsigned long long x) { return 1024_KB * x; } +constexpr unsigned long long operator""_GB(unsigned long long x) { return 1024_MB * x; } diff --git a/src/common/uint128.h b/src/common/uint128.h new file mode 100644 index 00000000..c4435791 --- /dev/null +++ b/src/common/uint128.h @@ -0,0 +1,115 @@ +// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include + +#ifdef _MSC_VER +#ifndef __clang__ +#define HAS_INTRINSICS +#include +#pragma intrinsic(__umulh) +#pragma intrinsic(_umul128) +#pragma intrinsic(_udiv128) +#else +#endif +#else +#include +#endif + +#include "common/types.h" + +namespace Common { + +// This function multiplies 2 u64 values and divides it by a u64 value. +[[nodiscard]] static inline u64 MultiplyAndDivide64(u64 a, u64 b, u64 d) { +#ifdef HAS_INTRINSICS + u128 r{}; + r[0] = _umul128(a, b, &r[1]); + u64 remainder; + return _udiv128(r[1], r[0], d, &remainder); +#else + const u64 diva = a / d; + const u64 moda = a % d; + const u64 divb = b / d; + const u64 modb = b % d; + return diva * b + moda * divb + moda * modb / d; +#endif +} + +// This function multiplies 2 u64 values and produces a u128 value; +[[nodiscard]] static inline u128 Multiply64Into128(u64 a, u64 b) { + u128 result; +#ifdef HAS_INTRINSICS + result[0] = _umul128(a, b, &result[1]); +#else + unsigned __int128 tmp = a; + tmp *= b; + std::memcpy(&result, &tmp, sizeof(u128)); +#endif + return result; +} + +[[nodiscard]] static inline u64 GetFixedPoint64Factor(u64 numerator, u64 divisor) { +#ifdef __SIZEOF_INT128__ + const auto base = static_cast(numerator) << 64ULL; + return static_cast(base / divisor); +#elif defined(_M_X64) || defined(_M_ARM64) + std::array r = {0, numerator}; + u64 remainder; + return _udiv128(r[1], r[0], divisor, &remainder); +#else + // This one is bit more inaccurate. + return MultiplyAndDivide64(std::numeric_limits::max(), numerator, divisor); +#endif +} + +[[nodiscard]] static inline u64 MultiplyHigh(u64 a, u64 b) { +#ifdef __SIZEOF_INT128__ + return (static_cast(a) * static_cast(b)) >> 64; +#elif defined(_M_X64) || defined(_M_ARM64) + return __umulh(a, b); // MSVC +#else + // Generic fallback + const u64 a_lo = u32(a); + const u64 a_hi = a >> 32; + const u64 b_lo = u32(b); + const u64 b_hi = b >> 32; + + const u64 a_x_b_hi = a_hi * b_hi; + const u64 a_x_b_mid = a_hi * b_lo; + const u64 b_x_a_mid = b_hi * a_lo; + const u64 a_x_b_lo = a_lo * b_lo; + + const u64 carry_bit = (static_cast(static_cast(a_x_b_mid)) + + static_cast(static_cast(b_x_a_mid)) + (a_x_b_lo >> 32)) >> + 32; + + const u64 multhi = a_x_b_hi + (a_x_b_mid >> 32) + (b_x_a_mid >> 32) + carry_bit; + + return multhi; +#endif +} + +// This function divides a u128 by a u32 value and produces two u64 values: +// the result of division and the remainder +[[nodiscard]] static inline std::pair Divide128On32(u128 dividend, u32 divisor) { + u64 remainder = dividend[0] % divisor; + u64 accum = dividend[0] / divisor; + if (dividend[1] == 0) + return {accum, remainder}; + // We ignore dividend[1] / divisor as that overflows + const u64 first_segment = (dividend[1] % divisor) << 32; + accum += (first_segment / divisor) << 32; + const u64 second_segment = (first_segment % divisor) << 32; + accum += (second_segment / divisor); + remainder += second_segment % divisor; + if (remainder >= divisor) { + accum++; + remainder -= divisor; + } + return {accum, remainder}; +} + +} // namespace Common diff --git a/src/core/PS4/GPU/gpu_memory.cpp b/src/core/PS4/GPU/gpu_memory.cpp index c278f37e..8f3086bc 100644 --- a/src/core/PS4/GPU/gpu_memory.cpp +++ b/src/core/PS4/GPU/gpu_memory.cpp @@ -1,5 +1,5 @@ #include "gpu_memory.h" - +#include #include #include "common/singleton.h" diff --git a/src/core/PS4/HLE/Graphics/graphics_ctx.h b/src/core/PS4/HLE/Graphics/graphics_ctx.h index eb355056..cd2b6a3c 100644 --- a/src/core/PS4/HLE/Graphics/graphics_ctx.h +++ b/src/core/PS4/HLE/Graphics/graphics_ctx.h @@ -2,8 +2,9 @@ #include "common/types.h" #include -#include +#include #include +#include namespace HLE::Libs::Graphics { diff --git a/src/core/file_sys/fs.h b/src/core/file_sys/fs.h index 45bf718f..8bfdbff9 100644 --- a/src/core/file_sys/fs.h +++ b/src/core/file_sys/fs.h @@ -1,4 +1,6 @@ #pragma once + +#include #include #include #include @@ -52,4 +54,4 @@ class HandleTable { std::mutex m_mutex; }; -} // namespace Core::FileSys \ No newline at end of file +} // namespace Core::FileSys diff --git a/src/core/hle/kernel/Objects/event_queue.cpp b/src/core/hle/kernel/Objects/event_queue.cpp index 1ca2d6f1..8720daf6 100644 --- a/src/core/hle/kernel/Objects/event_queue.cpp +++ b/src/core/hle/kernel/Objects/event_queue.cpp @@ -1,6 +1,5 @@ #include "common/debug.h" -#include "common/timer.h" -#include "core/hle/kernel/objects/event_queue.h" +#include "core/hle/kernel/Objects/event_queue.h" namespace Core::Kernel { @@ -24,28 +23,19 @@ int EqueueInternal::addEvent(const EqueueEvent& event) { int EqueueInternal::waitForEvents(SceKernelEvent* ev, int num, u32 micros) { std::unique_lock lock{m_mutex}; + int ret = 0; - u32 timeElapsed = 0; - Common::Timer t; - t.Start(); + const auto predicate = [&] { + ret = getTriggeredEvents(ev, num); + return ret > 0; + }; - for (;;) { - int ret = getTriggeredEvents(ev, num); - - if (ret > 0 || (timeElapsed >= micros && micros != 0)) { - return ret; - } - - if (micros == 0) { - m_cond.wait(lock); - } else { - m_cond.wait_for(lock, std::chrono::microseconds(micros - timeElapsed)); - } - - timeElapsed = static_cast(t.GetTimeSec() * 1000000.0); + if (micros == 0) { + m_cond.wait(lock, predicate); + } else { + m_cond.wait_for(lock, std::chrono::microseconds(micros), predicate); } - - return 0; + return ret; } bool EqueueInternal::triggerEvent(u64 ident, s16 filter, void* trigger_data) { diff --git a/src/core/hle/kernel/Objects/event_queue.h b/src/core/hle/kernel/Objects/event_queue.h index 3217bed2..407ccee9 100644 --- a/src/core/hle/kernel/Objects/event_queue.h +++ b/src/core/hle/kernel/Objects/event_queue.h @@ -3,6 +3,7 @@ #include #include #include +#include #include "common/types.h" namespace Core::Kernel { diff --git a/src/core/hle/kernel/Objects/physical_memory.cpp b/src/core/hle/kernel/Objects/physical_memory.cpp index a4bca4c8..58900f81 100644 --- a/src/core/hle/kernel/Objects/physical_memory.cpp +++ b/src/core/hle/kernel/Objects/physical_memory.cpp @@ -1,4 +1,4 @@ -#include "core/hle/kernel/objects/physical_memory.h" +#include "core/hle/kernel/Objects/physical_memory.h" namespace Core::Kernel { diff --git a/src/core/hle/kernel/event_queues.h b/src/core/hle/kernel/event_queues.h index e26be3bc..2f39f83a 100644 --- a/src/core/hle/kernel/event_queues.h +++ b/src/core/hle/kernel/event_queues.h @@ -1,6 +1,6 @@ #pragma once -#include "core/hle/kernel/objects/event_queue.h" +#include "core/hle/kernel/Objects/event_queue.h" namespace Core::Kernel { diff --git a/src/core/hle/libraries/libc/libc_math.cpp b/src/core/hle/libraries/libc/libc_math.cpp index 541dfb1a..4052b7c4 100644 --- a/src/core/hle/libraries/libc/libc_math.cpp +++ b/src/core/hle/libraries/libc/libc_math.cpp @@ -4,35 +4,35 @@ namespace Core::Libraries::LibC { float PS4_SYSV_ABI ps4_atan2f(float y, float x) { - return std::atan2f(y, x); + return atan2f(y, x); } float PS4_SYSV_ABI ps4_acosf(float num) { - return std::acosf(num); + return acosf(num); } float PS4_SYSV_ABI ps4_tanf(float num) { - return std::tanf(num); + return tanf(num); } float PS4_SYSV_ABI ps4_asinf(float num) { - return std::asinf(num); + return asinf(num); } double PS4_SYSV_ABI ps4_pow(double base, double exponent) { - return std::pow(base, exponent); + return pow(base, exponent); } double PS4_SYSV_ABI ps4__Sin(double x) { - return std::sin(x); + return sin(x); } float PS4_SYSV_ABI ps4__Fsin(float arg) { - return std::sinf(arg); + return sinf(arg); } double PS4_SYSV_ABI ps4_exp2(double arg) { - return std::exp2(arg); + return exp2(arg); } } // namespace Core::Libraries::LibC diff --git a/src/core/hle/libraries/libc/libc_stdio.cpp b/src/core/hle/libraries/libc/libc_stdio.cpp index b3b5dac2..aae85483 100644 --- a/src/core/hle/libraries/libc/libc_stdio.cpp +++ b/src/core/hle/libraries/libc/libc_stdio.cpp @@ -12,7 +12,7 @@ int PS4_SYSV_ABI ps4_printf(VA_ARGS) { } int PS4_SYSV_ABI ps4_fprintf(FILE* file, VA_ARGS) { - int fd = _fileno(file); + int fd = fileno(file); if (fd == 1 || fd == 2) { // output stdout and stderr to console VA_CTX(ctx); return printf_ctx(&ctx); diff --git a/src/core/hle/libraries/libkernel/libkernel.cpp b/src/core/hle/libraries/libkernel/libkernel.cpp index e0cb27bd..9eadbcb8 100644 --- a/src/core/hle/libraries/libkernel/libkernel.cpp +++ b/src/core/hle/libraries/libkernel/libkernel.cpp @@ -15,6 +15,8 @@ #ifdef _WIN64 #include #include +#else +#include #endif #include "thread_management.h" @@ -56,6 +58,7 @@ int* PS4_SYSV_ABI __Error() { return &libc_error; } #define PROT_WRITE 0x2 int PS4_SYSV_ABI sceKernelMmap(void* addr, u64 len, int prot, int flags, int fd, off_t offset, void** res) { +#ifdef _WIN64 PRINT_FUNCTION_NAME(); if (prot > 3) // READ,WRITE or bitwise READ | WRITE supported { @@ -86,6 +89,14 @@ int PS4_SYSV_ABI sceKernelMmap(void* addr, u64 len, int prot, int flags, int fd, } *res = ret; return 0; +#else + void* result = mmap(addr, len, prot, flags, fd, offset); + if (result != MAP_FAILED) { + *res = result; + return 0; + } + std::abort(); +#endif } PS4_SYSV_ABI void* posix_mmap(void* addr, u64 len, int prot, int flags, int fd, u64 offset) { diff --git a/src/core/hle/libraries/libkernel/time_management.cpp b/src/core/hle/libraries/libkernel/time_management.cpp index a1b40ce8..00861a1b 100644 --- a/src/core/hle/libraries/libkernel/time_management.cpp +++ b/src/core/hle/libraries/libkernel/time_management.cpp @@ -1,27 +1,31 @@ -#include "common/timer.h" +#include "common/native_clock.h" #include "core/hle/libraries/libkernel/time_management.h" #include "core/hle/libraries/libs.h" -#include "emuTimer.h" namespace Core::Libraries::LibKernel { +static u64 initial_ptc; +static std::unique_ptr clock; + u64 PS4_SYSV_ABI sceKernelGetProcessTime() { - return static_cast(Emulator::emuTimer::getTimeMsec() * 1000.0); // return time in microseconds + return clock->GetProcessTimeUS(); } u64 PS4_SYSV_ABI sceKernelGetProcessTimeCounter() { - return Emulator::emuTimer::getTimeCounter(); + return clock->GetUptime() - initial_ptc; } u64 PS4_SYSV_ABI sceKernelGetProcessTimeCounterFrequency() { - return Emulator::emuTimer::getTimeFrequency(); + return clock->GetTscFrequency(); } u64 PS4_SYSV_ABI sceKernelReadTsc() { - return Common::Timer::getQueryPerformanceCounter(); + return clock->GetUptime(); } void timeSymbolsRegister(Loader::SymbolsResolver* sym) { + clock = std::make_unique(); + initial_ptc = clock->GetUptime(); LIB_FUNCTION("4J2sUJmuHZQ", "libkernel", 1, "libkernel", 1, 1, sceKernelGetProcessTime); LIB_FUNCTION("fgxnMeTNUtY", "libkernel", 1, "libkernel", 1, 1, sceKernelGetProcessTimeCounter); LIB_FUNCTION("BNowx2l588E", "libkernel", 1, "libkernel", 1, 1, sceKernelGetProcessTimeCounterFrequency); diff --git a/src/core/linker.cpp b/src/core/linker.cpp index cc35c884..611d6b1b 100644 --- a/src/core/linker.cpp +++ b/src/core/linker.cpp @@ -658,12 +658,12 @@ void Linker::Resolve(const std::string& name, int Symtype, Module* m, Loader::Sy } else { - __debugbreak();//den tha prepei na ftasoume edo + //__debugbreak();//den tha prepei na ftasoume edo } } else { - __debugbreak();//oute edo mallon + //__debugbreak();//oute edo mallon } } diff --git a/src/core/loader/elf.cpp b/src/core/loader/elf.cpp index 729d6b93..b23f6c6f 100644 --- a/src/core/loader/elf.cpp +++ b/src/core/loader/elf.cpp @@ -5,7 +5,7 @@ namespace Core::Loader { -constexpr bool log_file_loader = true; // disable it to disable logging +constexpr bool log_file_loader = false; // disable it to disable logging static std::string_view getProgramTypeName(program_type_es type) { switch (type) { diff --git a/src/core/virtual_memory.cpp b/src/core/virtual_memory.cpp index f730aff4..cdff12c2 100644 --- a/src/core/virtual_memory.cpp +++ b/src/core/virtual_memory.cpp @@ -79,7 +79,8 @@ bool memory_protect(u64 address, u64 size, MemoryMode mode, MemoryMode* old_mode } return true; #else -#error Unimplement memory_protect function + int ret = mprotect(reinterpret_cast(address), size, convertMemoryMode(mode)); + return true; #endif } @@ -117,6 +118,7 @@ bool memory_patch(u64 vaddr, u64 value) { static u64 AlignUp(u64 pos, u64 align) { return (align != 0 ? (pos + (align - 1)) & ~(align - 1) : pos); } u64 memory_alloc_aligned(u64 address, u64 size, MemoryMode mode, u64 alignment) { +#ifdef _WIN64 // try allocate aligned address inside user area MEM_ADDRESS_REQUIREMENTS req{}; MEM_EXTENDED_PARAMETER param{}; @@ -134,5 +136,13 @@ u64 memory_alloc_aligned(u64 address, u64 size, MemoryMode mode, u64 alignment) LOG_ERROR_IF(true, "VirtualAlloc2() failed: 0x{:X}\n", err); } return ptr; +#else + void* hint_address = reinterpret_cast(AlignUp(address, alignment)); + void* ptr = mmap(hint_address, size, convertMemoryMode(mode), MAP_ANON | MAP_PRIVATE, -1, 0); + if (ptr == MAP_FAILED) { + std::abort(); + } + return reinterpret_cast(ptr); +#endif } } // namespace VirtualMemory diff --git a/src/emuTimer.cpp b/src/emuTimer.cpp deleted file mode 100644 index b05c001b..00000000 --- a/src/emuTimer.cpp +++ /dev/null @@ -1,23 +0,0 @@ -#include "common/timer.h" - -namespace Emulator::emuTimer { - -static Common::Timer timer; - -void start() { - timer.Start(); -} - -double getTimeMsec() { - return timer.GetTimeMsec(); -} - -u64 getTimeCounter() { - return timer.GetTicks(); -} - -u64 getTimeFrequency() { - return timer.GetFrequency(); -} - -} // namespace Emulator::emuTimer diff --git a/src/emuTimer.h b/src/emuTimer.h deleted file mode 100644 index 26c20743..00000000 --- a/src/emuTimer.h +++ /dev/null @@ -1,10 +0,0 @@ -#pragma once - -#include "common/types.h" - -namespace Emulator::emuTimer { -void start(); -double getTimeMsec(); -u64 getTimeCounter(); -u64 getTimeFrequency(); -} // namespace Emulator::emuTimer diff --git a/src/emulator.cpp b/src/emulator.cpp index 51f13be6..e52cd054 100644 --- a/src/emulator.cpp +++ b/src/emulator.cpp @@ -1,6 +1,5 @@ #include #include -#include "common/timer.h" #include "common/singleton.h" #include "common/version.h" #include "emulator.h" @@ -90,8 +89,6 @@ static void calculateFps(double game_time_s) { } } void emuRun() { - Common::Timer timer; - timer.Start(); auto window_ctx = Common::Singleton::Instance(); { // init window and wait until init finishes @@ -100,7 +97,7 @@ void emuRun() { Graphics::Vulkan::vulkanCreate(window_ctx); window_ctx->m_is_graphic_initialized = true; window_ctx->m_graphic_initialized_cond.notify_one(); - calculateFps(timer.GetTimeSec()); + calculateFps(0); // TODO: Proper fps } bool exit_loop = false; @@ -138,10 +135,6 @@ void emuRun() { continue; } if (m_game_is_paused) { - if (!timer.IsPaused()) { - timer.Pause(); - } - SDL_WaitEvent(&event); switch (event.type) { @@ -171,21 +164,13 @@ void emuRun() { continue; } exit_loop = m_emu_needs_exit; - if (m_game_is_paused) { - if (!timer.IsPaused()) { - timer.Pause(); - } - } else { - if (timer.IsPaused()) { - timer.Resume(); - } - + if (!m_game_is_paused) { if (!exit_loop) { update(); } if (!exit_loop) { if (HLE::Libs::Graphics::VideoOut::videoOutFlip(100000)) { // flip every 0.1 sec - calculateFps(timer.GetTimeSec()); + calculateFps(0); // TODO: Proper fps } } } @@ -215,7 +200,7 @@ void DrawBuffer(HLE::Libs::Graphics::VideoOutVulkanImage* image) { window_ctx->swapchain.current_index = static_cast(-1); auto result = vkAcquireNextImageKHR(window_ctx->m_graphic_ctx.m_device, window_ctx->swapchain.swapchain, UINT64_MAX, nullptr, - window_ctx->swapchain.present_complete_fence, &window_ctx->swapchain.current_index); + VK_NULL_HANDLE, &window_ctx->swapchain.current_index); if (result != VK_SUCCESS) { fmt::print("Can't aquireNextImage\n"); @@ -226,16 +211,6 @@ void DrawBuffer(HLE::Libs::Graphics::VideoOutVulkanImage* image) { std::exit(0); } - do { - result = vkWaitForFences(window_ctx->m_graphic_ctx.m_device, 1, &window_ctx->swapchain.present_complete_fence, VK_TRUE, 100000000); - } while (result == VK_TIMEOUT); - if (result != VK_SUCCESS) { - fmt::print("vkWaitForFences is not success\n"); - std::exit(0); - } - - vkResetFences(window_ctx->m_graphic_ctx.m_device, 1, &window_ctx->swapchain.present_complete_fence); - auto blt_src_image = image; auto blt_dst_image = window_ctx->swapchain; @@ -272,6 +247,7 @@ void DrawBuffer(HLE::Libs::Graphics::VideoOutVulkanImage* image) { buffer.end(); buffer.executeWithSemaphore(); + buffer.waitForFence(); // HACK: The whole vulkan backend needs a rewrite VkPresentInfoKHR present{}; present.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR; diff --git a/src/main.cpp b/src/main.cpp index b7723ae7..84b00ee9 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -14,7 +14,6 @@ #include "core/PS4/HLE/Graphics/video_out.h" #include "core/hle/libraries/libs.h" #include "core/linker.h" -#include "emuTimer.h" #include "emulator.h" #include #include "core/file_sys/fs.h" @@ -31,7 +30,6 @@ int main(int argc, char* argv[]) { auto height = Config::getScreenHeight(); Emu::emuInit(width, height); HLE::Libs::Graphics::VideoOut::videoOutInit(width, height); - Emulator::emuTimer::start(); // Argument 1 is the path of self file to boot const char* const path = argv[1]; diff --git a/src/video_core/gpu_memory.cpp b/src/video_core/gpu_memory.cpp new file mode 100644 index 00000000..4b5cfe6d --- /dev/null +++ b/src/video_core/gpu_memory.cpp @@ -0,0 +1,188 @@ +#include "gpu_memory.h" +#include +#include + +#include "common/singleton.h" + +void* GPU::memoryCreateObj(u64 submit_id, HLE::Libs::Graphics::GraphicCtx* ctx, void* todo /*CommandBuffer?*/, u64 virtual_addr, u64 size, + const GPUObject& info) { + auto* gpumemory = Common::Singleton::Instance(); + + return gpumemory->memoryCreateObj(submit_id, ctx, nullptr, &virtual_addr, &size, 1, info); +} + +void GPU::memorySetAllocArea(u64 virtual_addr, u64 size) { + auto* gpumemory = Common::Singleton::Instance(); + + std::scoped_lock lock{gpumemory->m_mutex}; + + MemoryHeap h; + h.allocated_virtual_addr = virtual_addr; + h.allocated_size = size; + + gpumemory->m_heaps.push_back(h); +} + +u64 GPU::calculate_hash(const u08* buf, u64 size) { return (size > 0 && buf != nullptr ? XXH3_64bits(buf, size) : 0); } + +bool GPU::vulkanAllocateMemory(HLE::Libs::Graphics::GraphicCtx* ctx, HLE::Libs::Graphics::VulkanMemory* mem) { + static std::atomic_uint64_t unique_id = 0; + + VkPhysicalDeviceMemoryProperties memory_properties{}; + vkGetPhysicalDeviceMemoryProperties(ctx->m_physical_device, &memory_properties); + + u32 index = 0; + for (; index < memory_properties.memoryTypeCount; index++) { + if ((mem->requirements.memoryTypeBits & (static_cast(1) << index)) != 0 && + (memory_properties.memoryTypes[index].propertyFlags & mem->property) == mem->property) { + break; + } + } + + mem->type = index; + mem->offset = 0; + + VkMemoryAllocateInfo alloc_info{}; + alloc_info.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; + alloc_info.pNext = nullptr; + alloc_info.allocationSize = mem->requirements.size; + alloc_info.memoryTypeIndex = index; + + mem->unique_id = ++unique_id; + + auto result = vkAllocateMemory(ctx->m_device, &alloc_info, nullptr, &mem->memory); + + if (result == VK_SUCCESS) { + return true; + } + return false; +} + +void GPU::flushGarlic(HLE::Libs::Graphics::GraphicCtx* ctx) { + auto* gpumemory = Common::Singleton::Instance(); + gpumemory->flushAllHeaps(ctx); +} + +int GPU::GPUMemory::getHeapId(u64 virtual_addr, u64 size) { + int index = 0; + for (const auto& heap : m_heaps) { + if ((virtual_addr >= heap.allocated_virtual_addr && virtual_addr < heap.allocated_virtual_addr + heap.allocated_size) || + ((virtual_addr + size - 1) >= heap.allocated_virtual_addr && + (virtual_addr + size - 1) < heap.allocated_virtual_addr + heap.allocated_size)) { + return index; + } + index++; + } + return -1; +} + +void* GPU::GPUMemory::memoryCreateObj(u64 submit_id, HLE::Libs::Graphics::GraphicCtx* ctx, void* todo, const u64* virtual_addr, const u64* size, + int virtual_addr_num, const GPUObject& info) { + auto* gpumemory = Common::Singleton::Instance(); + + std::scoped_lock lock{gpumemory->m_mutex}; + + int heap_id = gpumemory->getHeapId(virtual_addr[0], size[0]); + + if (heap_id < 0) { + return nullptr; + } + auto& heap = m_heaps[heap_id]; + + ObjInfo objInfo = {}; + + // Copy parameters from info to obj + for (int i = 0; i < 8; i++) { + objInfo.obj_params[i] = info.obj_params[i]; + } + + objInfo.gpu_object.objectType = info.objectType; + objInfo.gpu_object.obj = nullptr; + + for (int h = 0; h < virtual_addr_num; h++) { + if (info.check_hash) { + objInfo.hash[h] = GPU::calculate_hash(reinterpret_cast(virtual_addr[h]), size[h]); + } else { + objInfo.hash[h] = 0; + } + } + objInfo.submit_id = submit_id; + objInfo.check_hash = info.check_hash; + + objInfo.gpu_object.obj = info.getCreateFunc()(ctx, objInfo.obj_params, virtual_addr, size, virtual_addr_num, &objInfo.mem); + + objInfo.update_func = info.getUpdateFunc(); + int index = static_cast(heap.objects.size()); + + HeapObject hobj{}; + hobj.block = createHeapBlock(virtual_addr, size, virtual_addr_num, heap_id, index); + hobj.info = objInfo; + hobj.free = false; + heap.objects.push_back(hobj); + + return objInfo.gpu_object.obj; +} + +GPU::HeapBlock GPU::GPUMemory::createHeapBlock(const u64* virtual_addr, const u64* size, int virtual_addr_num, int heap_id, int obj_id) { + auto& heap = m_heaps[heap_id]; + + GPU::HeapBlock heapBlock{}; + heapBlock.virtual_addr_num = virtual_addr_num; + for (int vi = 0; vi < virtual_addr_num; vi++) { + heapBlock.virtual_addr[vi] = virtual_addr[vi]; + heapBlock.size[vi] = size[vi]; + } + return heapBlock; +} + +void GPU::GPUMemory::update(u64 submit_id, HLE::Libs::Graphics::GraphicCtx* ctx, int heap_id, int obj_id) { + auto& heap = m_heaps[heap_id]; + + auto& heapObj = heap.objects[obj_id]; + auto& objInfo = heapObj.info; + bool need_update = false; + + if (submit_id > objInfo.submit_id) { + uint64_t hash[3] = {}; + + for (int i = 0; i < heapObj.block.virtual_addr_num; i++) { + if (objInfo.check_hash) { + hash[i] = GPU::calculate_hash(reinterpret_cast(heapObj.block.virtual_addr[i]), heapObj.block.size[i]); + } else { + hash[i] = 0; + } + } + + for (int i = 0; i < heapObj.block.virtual_addr_num; i++) { + if (objInfo.hash[i] != hash[i]) { + need_update = true; + objInfo.hash[i] = hash[i]; + } + } + + if (submit_id != UINT64_MAX) { + objInfo.submit_id = submit_id; + } + } + + if (need_update) { + objInfo.update_func(ctx, objInfo.obj_params, objInfo.gpu_object.obj, heapObj.block.virtual_addr, heapObj.block.size, + heapObj.block.virtual_addr_num); + } +} + +void GPU::GPUMemory::flushAllHeaps(HLE::Libs::Graphics::GraphicCtx* ctx) { + std::scoped_lock lock{m_mutex}; + + int heap_id = 0; + for (auto& heap : m_heaps) { + int index = 0; + for (auto& heapObj : heap.objects) { + if (!heapObj.free) { + update(UINT64_MAX, ctx, heap_id, index); + } + index++; + } + heap_id++; + } +} diff --git a/src/video_core/gpu_memory.h b/src/video_core/gpu_memory.h new file mode 100644 index 00000000..87095ec7 --- /dev/null +++ b/src/video_core/gpu_memory.h @@ -0,0 +1,86 @@ +#pragma once + +#include "common/types.h" +#include +#include + +namespace VideoCore { + +class GPUObject; + +enum class MemoryMode : u32 { + NoAccess = 0, + Read = 1, + Write = 2, + ReadWrite = 3, +}; + +enum class MemoryObjectType : u64 { + Invalid, + VideoOutBuffer, +}; + +struct GpuMemoryObject { + MemoryObjectType object_type = MemoryObjectType::Invalid; + void* obj = nullptr; +}; + +struct HeapBlock { + std::array virtual_address{}; + std::array size{}; + u32 virtual_addr_num = 0; +}; + +class GPUObject { +public: + GPUObject() = default; + virtual ~GPUObject() = default; + u64 obj_params[8] = {}; + bool check_hash = false; + bool isReadOnly = false; + MemoryObjectType objectType = MemoryObjectType::Invalid; +}; + +struct ObjInfo { + std::array obj_params{}; + GpuMemoryObject gpu_object; + std::array hash{}; + u64 submit_id = 0; + bool check_hash = false; +}; + +struct HeapObject { + HeapBlock block; + ObjInfo info; + bool free = true; +}; +struct MemoryHeap { + u64 allocated_virtual_addr = 0; + u64 allocated_size = 0; + std::vector objects; +}; + +class GPUMemory { +public: + GPUMemory() {} + virtual ~GPUMemory() {} + int getHeapId(u64 vaddr, u64 size); + void* memoryCreateObj(u64 submit_id, HLE::Libs::Graphics::GraphicCtx* ctx, /*CommandBuffer* buffer*/ void* todo, const u64* virtual_addr, + const u64* size, int virtual_addr_num, const GPUObject& info); + HeapBlock createHeapBlock(const u64* virtual_addr, const u64* size, int virtual_addr_num, int heap_id, int obj_id); + void update(u64 submit_id, HLE::Libs::Graphics::GraphicCtx* ctx, int heap_id, int obj_id); + void flushAllHeaps(HLE::Libs::Graphics::GraphicCtx* ctx); + +private: + std::mutex m_mutex; + std::vector m_heaps; +}; + +void memorySetAllocArea(u64 virtual_addr, u64 size); +void* memoryCreateObj(u64 submit_id, HLE::Libs::Graphics::GraphicCtx* ctx, /*CommandBuffer* buffer*/ void* todo, u64 virtual_addr, u64 size, + const GPUObject& info); +u64 calculate_hash(const u08* buf, u64 size); +bool vulkanAllocateMemory(HLE::Libs::Graphics::GraphicCtx* ctx, HLE::Libs::Graphics::VulkanMemory* mem); +void flushGarlic(HLE::Libs::Graphics::GraphicCtx* ctx); + +} // namespace VideoCore diff --git a/src/video_core/tile_manager.cpp b/src/video_core/tile_manager.cpp new file mode 100644 index 00000000..c0d4ed3b --- /dev/null +++ b/src/video_core/tile_manager.cpp @@ -0,0 +1,151 @@ +#include +#include +#include "video_core/tile_manager.h" + +namespace VideoCore { + +class TileManager32 { + public: + u32 m_macro_tile_height = 0; + u32 m_bank_height = 0; + u32 m_num_banks = 0; + u32 m_num_pipes = 0; + u32 m_padded_width = 0; + u32 m_padded_height = 0; + u32 m_pipe_bits = 0; + u32 m_bank_bits = 0; + + TileManager32(u32 width, u32 height, bool is_neo) { + m_macro_tile_height = (is_neo ? 128 : 64); + m_bank_height = is_neo ? 2 : 1; + m_num_banks = is_neo ? 8 : 16; + m_num_pipes = is_neo ? 16 : 8; + m_padded_width = width; + if (height == 1080) { + m_padded_height = is_neo ? 1152 : 1088; + } + if (height == 720) { + m_padded_height = 768; + } + m_pipe_bits = is_neo ? 4 : 3; + m_bank_bits = is_neo ? 3 : 4; + } + + static u32 GetElementIndex(u32 x, u32 y) { + u32 elem = 0; + elem |= ((x >> 0u) & 0x1u) << 0u; + elem |= ((x >> 1u) & 0x1u) << 1u; + elem |= ((y >> 0u) & 0x1u) << 2u; + elem |= ((x >> 2u) & 0x1u) << 3u; + elem |= ((y >> 1u) & 0x1u) << 4u; + elem |= ((y >> 2u) & 0x1u) << 5u; + + return elem; + } + + static u32 GetPipeIndex(u32 x, u32 y, bool is_neo) { + u32 pipe = 0; + + if (!is_neo) { + pipe |= (((x >> 3u) ^ (y >> 3u) ^ (x >> 4u)) & 0x1u) << 0u; + pipe |= (((x >> 4u) ^ (y >> 4u)) & 0x1u) << 1u; + pipe |= (((x >> 5u) ^ (y >> 5u)) & 0x1u) << 2u; + } else { + pipe |= (((x >> 3u) ^ (y >> 3u) ^ (x >> 4u)) & 0x1u) << 0u; + pipe |= (((x >> 4u) ^ (y >> 4u)) & 0x1u) << 1u; + pipe |= (((x >> 5u) ^ (y >> 5u)) & 0x1u) << 2u; + pipe |= (((x >> 6u) ^ (y >> 5u)) & 0x1u) << 3u; + } + + return pipe; + } + + static u32 GetBankIndex(u32 x, u32 y, u32 bank_width, u32 bank_height, u32 num_banks, u32 num_pipes) { + const u32 x_shift_offset = std::bit_width(bank_width * num_pipes); + const u32 y_shift_offset = std::bit_width(bank_height); + const u32 xs = x >> x_shift_offset; + const u32 ys = y >> y_shift_offset; + u32 bank = 0; + switch (num_banks) { + case 8: + bank |= (((xs >> 3u) ^ (ys >> 5u)) & 0x1u) << 0u; + bank |= (((xs >> 4u) ^ (ys >> 4u) ^ (ys >> 5u)) & 0x1u) << 1u; + bank |= (((xs >> 5u) ^ (ys >> 3u)) & 0x1u) << 2u; + break; + case 16: + bank |= (((xs >> 3u) ^ (ys >> 6u)) & 0x1u) << 0u; + bank |= (((xs >> 4u) ^ (ys >> 5u) ^ (ys >> 6u)) & 0x1u) << 1u; + bank |= (((xs >> 5u) ^ (ys >> 4u)) & 0x1u) << 2u; + bank |= (((xs >> 6u) ^ (ys >> 3u)) & 0x1u) << 3u; + break; + default:; + } + + return bank; + } + + u64 GetTiledOffset(u32 x, u32 y, bool is_neo) const { + u64 element_index = GetElementIndex(x, y); + + u32 xh = x; + u32 yh = y; + u64 pipe = GetPipeIndex(xh, yh, is_neo); + u64 bank = GetBankIndex(xh, yh, 1, m_bank_height, m_num_banks, m_num_pipes); + u32 tile_bytes = (8 * 8 * 32 + 7) / 8; + u64 element_offset = (element_index * 32); + u64 tile_split_slice = 0; + + if (tile_bytes > 512) { + tile_split_slice = element_offset / (static_cast(512) * 8); + element_offset %= (static_cast(512) * 8); + tile_bytes = 512; + } + + u64 macro_tile_bytes = (128 / 8) * (m_macro_tile_height / 8) * tile_bytes / (m_num_pipes * m_num_banks); + u64 macro_tiles_per_row = m_padded_width / 128; + u64 macro_tile_row_index = y / m_macro_tile_height; + u64 macro_tile_column_index = x / 128; + u64 macro_tile_index = (macro_tile_row_index * macro_tiles_per_row) + macro_tile_column_index; + u64 macro_tile_offset = macro_tile_index * macro_tile_bytes; + u64 macro_tiles_per_slice = macro_tiles_per_row * (m_padded_height / m_macro_tile_height); + u64 slice_bytes = macro_tiles_per_slice * macro_tile_bytes; + u64 slice_offset = tile_split_slice * slice_bytes; + u64 tile_row_index = (y / 8) % m_bank_height; + u64 tile_index = tile_row_index; + u64 tile_offset = tile_index * tile_bytes; + + u64 tile_split_slice_rotation = ((m_num_banks / 2) + 1) * tile_split_slice; + bank ^= tile_split_slice_rotation; + bank &= (m_num_banks - 1); + + u64 total_offset = (slice_offset + macro_tile_offset + tile_offset) * 8 + element_offset; + u64 bit_offset = total_offset & 0x7u; + total_offset /= 8; + + u64 pipe_interleave_offset = total_offset & 0xffu; + u64 offset = total_offset >> 8u; + u64 byte_offset = pipe_interleave_offset | (pipe << (8u)) | (bank << (8u + m_pipe_bits)) | (offset << (8u + m_pipe_bits + m_bank_bits)); + + return ((byte_offset << 3u) | bit_offset) / 8; + } +}; + +void ConvertTileToLinear(u08* dst, const u08* src,u32 width, u32 height, bool is_neo) { + const TileManager32 t{width, height, is_neo}; + for (u32 y = 0; y < height; y++) { + u32 x = 0; + u64 linear_offset = y * width * 4; + + for (; x + 1 < width; x += 2) { + auto tiled_offset = t.GetTiledOffset(x, y, is_neo); + std::memcpy(dst + linear_offset, src + tiled_offset, sizeof(u64)); + linear_offset += sizeof(u64); + } + if (x < width) { + auto tiled_offset = t.GetTiledOffset(x, y, is_neo); + std::memcpy(dst + linear_offset, src + tiled_offset, sizeof(u32)); + } + } +} + +} // namespace VideoCore diff --git a/src/video_core/tile_manager.h b/src/video_core/tile_manager.h new file mode 100644 index 00000000..b58b99c9 --- /dev/null +++ b/src/video_core/tile_manager.h @@ -0,0 +1,9 @@ +#pragma once + +#include "common/types.h" + +namespace VideoCore { + +void ConvertTileToLinear(void* dst, const void* src, u32 width, u32 height, bool neo); + +} // namespace VideoCore diff --git a/third-party/CMakeLists.txt b/third-party/CMakeLists.txt index 0e04aae4..d9bbe697 100644 --- a/third-party/CMakeLists.txt +++ b/third-party/CMakeLists.txt @@ -30,7 +30,6 @@ add_subdirectory(toml11 EXCLUDE_FROM_ALL) # Vulkan add_subdirectory(vulkan EXCLUDE_FROM_ALL) -target_include_directories(vulkan-1 INTERFACE vulkan/include) # Winpthreads if (WIN32) @@ -66,6 +65,6 @@ target_include_directories(imgui PUBLIC imgui/include ) -target_link_libraries(imgui PRIVATE SDL3-shared ${CMAKE_DL_LIBS} Zydis winpthread discord-rpc) +target_link_libraries(imgui PRIVATE SDL3-shared ${CMAKE_DL_LIBS} Zydis discord-rpc) diff --git a/third-party/fmt b/third-party/fmt index 661b23ed..8e42eef4 160000 --- a/third-party/fmt +++ b/third-party/fmt @@ -1 +1 @@ -Subproject commit 661b23edeb52d400cf5812e7330f14f05c072fab +Subproject commit 8e42eef4950feb5d2b76574a9cd2591dfaae2449 diff --git a/third-party/spdlog b/third-party/spdlog index 76dfc7e7..134f9194 160000 --- a/third-party/spdlog +++ b/third-party/spdlog @@ -1 +1 @@ -Subproject commit 76dfc7e7c0d3c69d3cdaa3399b63545235ccbb02 +Subproject commit 134f9194bb93072b72b8cfa27ac3bb30a0fb5b57 diff --git a/third-party/vulkan b/third-party/vulkan index 8c9feb4f..72b2e740 160000 --- a/third-party/vulkan +++ b/third-party/vulkan @@ -1 +1 @@ -Subproject commit 8c9feb4f480b32f7c7421af546aa6ffb558bdd5e +Subproject commit 72b2e740754bc6b86b724fa5b2c90dca6f69462e From b62c44c9147c20f40bb95ee83c79887513ae7e56 Mon Sep 17 00:00:00 2001 From: georgemoralis Date: Fri, 23 Feb 2024 14:10:44 +0200 Subject: [PATCH 2/3] ugly workaround :D --- CMakeLists.txt | 2 +- lib/clang_rt.builtins-x86_64.lib | Bin 0 -> 174020 bytes 2 files changed, 1 insertion(+), 1 deletion(-) create mode 100644 lib/clang_rt.builtins-x86_64.lib diff --git a/CMakeLists.txt b/CMakeLists.txt index c3b2dca3..c6ca6d4b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -155,7 +155,7 @@ create_target_directory_groups(shadps4) target_link_libraries(shadps4 PRIVATE magic_enum::magic_enum fmt::fmt spdlog::spdlog toml11::toml11) target_link_libraries(shadps4 PRIVATE discord-rpc imgui SDL3-shared vulkan-1 xxhash Zydis) if (WIN32) - target_link_libraries(shadps4 PRIVATE mincore winpthread) + target_link_libraries(shadps4 PRIVATE mincore winpthread ${PROJECT_SOURCE_DIR}/lib/clang_rt.builtins-x86_64.lib) endif() add_custom_command(TARGET shadps4 POST_BUILD diff --git a/lib/clang_rt.builtins-x86_64.lib b/lib/clang_rt.builtins-x86_64.lib new file mode 100644 index 0000000000000000000000000000000000000000..a04d968d627a8d995fdc697975e86b6fe7bc15bd GIT binary patch literal 174020 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ð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 Date: Fri, 23 Feb 2024 14:33:01 +0200 Subject: [PATCH 3/3] should work without lib --- CMakeLists.txt | 2 +- lib/clang_rt.builtins-x86_64.lib | Bin 174020 -> 0 bytes 2 files changed, 1 insertion(+), 1 deletion(-) delete mode 100644 lib/clang_rt.builtins-x86_64.lib diff --git a/CMakeLists.txt b/CMakeLists.txt index c6ca6d4b..9f148834 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -155,7 +155,7 @@ create_target_directory_groups(shadps4) target_link_libraries(shadps4 PRIVATE magic_enum::magic_enum fmt::fmt spdlog::spdlog toml11::toml11) target_link_libraries(shadps4 PRIVATE discord-rpc imgui SDL3-shared vulkan-1 xxhash Zydis) if (WIN32) - target_link_libraries(shadps4 PRIVATE mincore winpthread ${PROJECT_SOURCE_DIR}/lib/clang_rt.builtins-x86_64.lib) + target_link_libraries(shadps4 PRIVATE mincore winpthread clang_rt.builtins-x86_64.lib) endif() add_custom_command(TARGET shadps4 POST_BUILD diff --git a/lib/clang_rt.builtins-x86_64.lib b/lib/clang_rt.builtins-x86_64.lib deleted file mode 100644 index a04d968d627a8d995fdc697975e86b6fe7bc15bd..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 174020 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ð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