mirror of
https://git.eden-emu.dev/eden-emu/eden.git
synced 2026-08-15 13:16:43 +00:00
Compare commits
9 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| a99cc426d6 | |||
| 4d49341918 | |||
| 2f0f8a979c | |||
| 413c7543ba | |||
| 975aa4e2f2 | |||
| a1f9e68f46 | |||
| 02dee4a20b | |||
| bc9b9480fb | |||
| d1ceeeca22 |
+21
-6
@@ -1,3 +1,6 @@
|
||||
# SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
# SPDX-FileCopyrightText: 2023 yuzu Emulator Project
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
@@ -16,15 +19,24 @@ if (NOT FILE_LIST)
|
||||
endif()
|
||||
|
||||
set(DIRECTORY_NAME ${HEADER_NAME})
|
||||
|
||||
set(FILE_DATA "")
|
||||
|
||||
string(APPEND FILE_DATA "[[nodiscard]] static inline std::vector<FileSys::VirtualFile> CollectFiles_${DIRECTORY_NAME}() {\n")
|
||||
string(APPEND FILE_DATA [[
|
||||
std::vector<FileSys::VirtualFile> vfs_files;
|
||||
auto const fn = [&](std::string_view name, std::span<const u8> data) {
|
||||
vfs_files.push_back(std::make_shared<FileSys::VectorVfsFile>(
|
||||
std::vector<u8>(data.begin(), data.end()),
|
||||
std::string{name}
|
||||
));
|
||||
};
|
||||
]])
|
||||
foreach(ZONE_FILE ${FILE_LIST})
|
||||
if (ZONE_FILE STREQUAL "\n")
|
||||
continue()
|
||||
endif()
|
||||
|
||||
string(APPEND FILE_DATA "{\"${ZONE_FILE}\",\n{")
|
||||
|
||||
string(APPEND FILE_DATA " {\n")
|
||||
string(APPEND FILE_DATA " constexpr uint8_t tzdb_data[] = {\n")
|
||||
file(READ ${ZONE_PATH}/${ZONE_FILE} ZONE_DATA HEX)
|
||||
string(LENGTH "${ZONE_DATA}" ZONE_DATA_LEN)
|
||||
foreach(I RANGE 0 ${ZONE_DATA_LEN} 2)
|
||||
@@ -42,9 +54,12 @@ foreach(ZONE_FILE ${FILE_LIST})
|
||||
string(APPEND FILE_DATA " ")
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
string(APPEND FILE_DATA "}},\n")
|
||||
string(APPEND FILE_DATA " };\n")
|
||||
string(APPEND FILE_DATA " fn(\"${ZONE_FILE}\", tzdb_data);\n")
|
||||
string(APPEND FILE_DATA " }\n")
|
||||
endforeach()
|
||||
string(APPEND FILE_DATA " return vfs_files;\n")
|
||||
string(APPEND FILE_DATA "}\n")
|
||||
|
||||
file(READ ${NX_TZDB_SOURCE_DIR}/tzdb_template.h.in NX_TZDB_TEMPLATE_H_IN)
|
||||
file(CONFIGURE OUTPUT ${NX_TZDB_INCLUDE_DIR}/nx_tzdb/${HEADER_NAME}.h CONTENT "${NX_TZDB_TEMPLATE_H_IN}")
|
||||
|
||||
Vendored
+3
-3
@@ -9,10 +9,10 @@
|
||||
|
||||
namespace NxTzdb {
|
||||
|
||||
// @DIRECTORY_NAME@
|
||||
|
||||
// clang-format off
|
||||
const static std::map<const char*, const std::vector<uint8_t>> @DIRECTORY_NAME@ =
|
||||
{
|
||||
@FILE_DATA@};
|
||||
@FILE_DATA@
|
||||
// clang-format on
|
||||
|
||||
} // namespace NxTzdb
|
||||
|
||||
+4
-1
@@ -159,7 +159,10 @@ else()
|
||||
endif()
|
||||
|
||||
if (ARCHITECTURE_x86_64)
|
||||
add_compile_options(-mcx16)
|
||||
add_compile_options($<$<COMPILE_LANGUAGE:C,CXX>:-mcx16>)
|
||||
if (PLATFORM_LINUX OR PLATFORM_FREEBSD)
|
||||
add_compile_options($<$<COMPILE_LANGUAGE:C,CXX>:-mtls-dialect=gnu2>)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if (APPLE AND CXX_CLANG)
|
||||
|
||||
@@ -184,19 +184,12 @@ if(ARCHITECTURE_x86_64)
|
||||
x64/cpu_detect.h
|
||||
x64/cpu_wait.cpp
|
||||
x64/cpu_wait.h
|
||||
x64/native_clock.cpp
|
||||
x64/native_clock.h
|
||||
x64/rdtsc.cpp
|
||||
x64/rdtsc.h
|
||||
x64/xbyak_abi.h
|
||||
x64/xbyak_util.h)
|
||||
x64/xbyak.h)
|
||||
target_link_libraries(common PRIVATE xbyak::xbyak)
|
||||
endif()
|
||||
|
||||
if(HAS_NCE)
|
||||
target_sources(common PRIVATE arm64/native_clock.cpp arm64/native_clock.h)
|
||||
endif()
|
||||
|
||||
if(MSVC)
|
||||
target_compile_definitions(
|
||||
common
|
||||
|
||||
@@ -1,87 +0,0 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#ifdef ANDROID
|
||||
#include <sys/system_properties.h>
|
||||
#endif
|
||||
#include "common/arm64/native_clock.h"
|
||||
|
||||
namespace Common::Arm64 {
|
||||
|
||||
namespace {
|
||||
|
||||
NativeClock::FactorType GetFixedPointFactor(u64 num, u64 den) {
|
||||
return (static_cast<NativeClock::FactorType>(num) << 64) / den;
|
||||
}
|
||||
|
||||
u64 MultiplyHigh(u64 m, NativeClock::FactorType factor) {
|
||||
return static_cast<u64>((m * factor) >> 64);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
NativeClock::NativeClock() {
|
||||
const u64 host_cntfrq = GetHostCNTFRQ();
|
||||
ns_cntfrq_factor = GetFixedPointFactor(NsRatio::den, host_cntfrq);
|
||||
us_cntfrq_factor = GetFixedPointFactor(UsRatio::den, host_cntfrq);
|
||||
ms_cntfrq_factor = GetFixedPointFactor(MsRatio::den, host_cntfrq);
|
||||
guest_cntfrq_factor = GetFixedPointFactor(CNTFRQ, host_cntfrq);
|
||||
gputick_cntfrq_factor = GetFixedPointFactor(GPUTickFreq, host_cntfrq);
|
||||
}
|
||||
|
||||
std::chrono::nanoseconds NativeClock::GetTimeNS() const {
|
||||
return std::chrono::nanoseconds{MultiplyHigh(GetUptime(), ns_cntfrq_factor)};
|
||||
}
|
||||
|
||||
std::chrono::microseconds NativeClock::GetTimeUS() const {
|
||||
return std::chrono::microseconds{MultiplyHigh(GetUptime(), us_cntfrq_factor)};
|
||||
}
|
||||
|
||||
std::chrono::milliseconds NativeClock::GetTimeMS() const {
|
||||
return std::chrono::milliseconds{MultiplyHigh(GetUptime(), ms_cntfrq_factor)};
|
||||
}
|
||||
|
||||
s64 NativeClock::GetCNTPCT() const {
|
||||
return MultiplyHigh(GetUptime(), guest_cntfrq_factor);
|
||||
}
|
||||
|
||||
s64 NativeClock::GetGPUTick() const {
|
||||
return MultiplyHigh(GetUptime(), gputick_cntfrq_factor);
|
||||
}
|
||||
|
||||
s64 NativeClock::GetUptime() const {
|
||||
s64 cntvct_el0 = 0;
|
||||
asm volatile("dsb ish\n\t"
|
||||
"mrs %[cntvct_el0], cntvct_el0\n\t"
|
||||
"dsb ish\n\t"
|
||||
: [cntvct_el0] "=r"(cntvct_el0));
|
||||
return cntvct_el0;
|
||||
}
|
||||
|
||||
bool NativeClock::IsNative() const {
|
||||
return true;
|
||||
}
|
||||
|
||||
s64 NativeClock::GetHostCNTFRQ() {
|
||||
u64 cntfrq_el0 = 0;
|
||||
std::string_view board{""};
|
||||
#ifdef ANDROID
|
||||
char buffer[PROP_VALUE_MAX];
|
||||
int len{__system_property_get("ro.product.board", buffer)};
|
||||
board = std::string_view(buffer, static_cast<size_t>(len));
|
||||
#endif
|
||||
if (board == "s5e9925") { // Exynos 2200
|
||||
cntfrq_el0 = 25600000;
|
||||
} else if (board == "exynos2100") { // Exynos 2100
|
||||
cntfrq_el0 = 26000000;
|
||||
} else if (board == "exynos9810") { // Exynos 9810
|
||||
cntfrq_el0 = 26000000;
|
||||
} else if (board == "s5e8825") { // Exynos 1280
|
||||
cntfrq_el0 = 26000000;
|
||||
} else {
|
||||
asm("mrs %[cntfrq_el0], cntfrq_el0" : [cntfrq_el0] "=r"(cntfrq_el0));
|
||||
}
|
||||
return cntfrq_el0;
|
||||
}
|
||||
|
||||
} // namespace Common::Arm64
|
||||
@@ -1,45 +0,0 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common/wall_clock.h"
|
||||
|
||||
namespace Common::Arm64 {
|
||||
|
||||
class NativeClock final : public WallClock {
|
||||
public:
|
||||
explicit NativeClock();
|
||||
|
||||
std::chrono::nanoseconds GetTimeNS() const override;
|
||||
|
||||
std::chrono::microseconds GetTimeUS() const override;
|
||||
|
||||
std::chrono::milliseconds GetTimeMS() const override;
|
||||
|
||||
s64 GetCNTPCT() const override;
|
||||
|
||||
s64 GetGPUTick() const override;
|
||||
|
||||
s64 GetUptime() const override;
|
||||
|
||||
bool IsNative() const override;
|
||||
|
||||
static s64 GetHostCNTFRQ();
|
||||
|
||||
public:
|
||||
using FactorType = unsigned __int128;
|
||||
|
||||
FactorType GetGuestCNTFRQFactor() const {
|
||||
return guest_cntfrq_factor;
|
||||
}
|
||||
|
||||
private:
|
||||
FactorType ns_cntfrq_factor;
|
||||
FactorType us_cntfrq_factor;
|
||||
FactorType ms_cntfrq_factor;
|
||||
FactorType guest_cntfrq_factor;
|
||||
FactorType gputick_cntfrq_factor;
|
||||
};
|
||||
|
||||
} // namespace Common::Arm64
|
||||
@@ -591,6 +591,8 @@ struct Values {
|
||||
SwitchableSetting<bool> gpu_unswizzle_enabled{linkage, false, "gpu_unswizzle_enabled",
|
||||
Category::RendererHacks};
|
||||
|
||||
SwitchableSetting<bool> legacy_descriptor_indices{linkage, true, "legacy_descriptor_indices", Category::RendererHacks};
|
||||
|
||||
SwitchableSetting<ExtendedDynamicState> dyna_state{linkage,
|
||||
#if defined(ANDROID)
|
||||
ExtendedDynamicState::Disabled,
|
||||
|
||||
+174
-55
@@ -1,77 +1,196 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "common/steady_clock.h"
|
||||
#include "common/uint128.h"
|
||||
#include "common/wall_clock.h"
|
||||
|
||||
#ifdef __ANDROID__
|
||||
#include <sys/system_properties.h>
|
||||
#endif
|
||||
#ifdef ARCHITECTURE_x86_64
|
||||
#include "common/x64/cpu_detect.h"
|
||||
#include "common/x64/native_clock.h"
|
||||
#include "common/x64/rdtsc.h"
|
||||
#endif
|
||||
#ifdef HAS_NCE
|
||||
#include "common/arm64/native_clock.h"
|
||||
#endif
|
||||
|
||||
namespace Common {
|
||||
|
||||
class StandardWallClock final : public WallClock {
|
||||
public:
|
||||
explicit StandardWallClock() {}
|
||||
|
||||
std::chrono::nanoseconds GetTimeNS() const override {
|
||||
return std::chrono::duration_cast<std::chrono::nanoseconds>(
|
||||
std::chrono::system_clock::now().time_since_epoch());
|
||||
}
|
||||
|
||||
std::chrono::microseconds GetTimeUS() const override {
|
||||
return std::chrono::duration_cast<std::chrono::microseconds>(
|
||||
std::chrono::system_clock::now().time_since_epoch());
|
||||
}
|
||||
|
||||
std::chrono::milliseconds GetTimeMS() const override {
|
||||
return std::chrono::duration_cast<std::chrono::milliseconds>(
|
||||
std::chrono::system_clock::now().time_since_epoch());
|
||||
}
|
||||
|
||||
s64 GetCNTPCT() const override {
|
||||
return GetUptime() * NsToCNTPCTRatio::num / NsToCNTPCTRatio::den;
|
||||
}
|
||||
|
||||
s64 GetGPUTick() const override {
|
||||
return GetUptime() * NsToGPUTickRatio::num / NsToGPUTickRatio::den;
|
||||
}
|
||||
|
||||
s64 GetUptime() const override {
|
||||
return std::chrono::duration_cast<std::chrono::nanoseconds>(
|
||||
std::chrono::steady_clock::now().time_since_epoch())
|
||||
.count();
|
||||
}
|
||||
|
||||
bool IsNative() const override {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
std::unique_ptr<WallClock> CreateOptimalClock() {
|
||||
#if defined(ARCHITECTURE_x86_64)
|
||||
const auto& caps = GetCPUCaps();
|
||||
WallClock::WallClock(bool invariant_, u64 rdtsc_frequency_) noexcept
|
||||
: invariant{invariant_}
|
||||
, rdtsc_frequency{rdtsc_frequency_}
|
||||
, ns_rdtsc_factor{GetFixedPoint64Factor(NsRatio::den, rdtsc_frequency_)}
|
||||
, us_rdtsc_factor{GetFixedPoint64Factor(UsRatio::den, rdtsc_frequency_)}
|
||||
, ms_rdtsc_factor{GetFixedPoint64Factor(MsRatio::den, rdtsc_frequency_)}
|
||||
, cntpct_rdtsc_factor{GetFixedPoint64Factor(CNTFRQ, rdtsc_frequency_)}
|
||||
, gputick_rdtsc_factor{GetFixedPoint64Factor(GPUTickFreq, rdtsc_frequency_)}
|
||||
{}
|
||||
|
||||
if (caps.invariant_tsc && caps.tsc_frequency >= std::nano::den) {
|
||||
return std::make_unique<X64::NativeClock>(caps.tsc_frequency);
|
||||
} else {
|
||||
// Fallback to StandardWallClock if the hardware TSC
|
||||
// - Is not invariant
|
||||
// - Is not more precise than 1 GHz (1ns resolution)
|
||||
return std::make_unique<StandardWallClock>();
|
||||
}
|
||||
std::chrono::nanoseconds WallClock::GetTimeNS() const {
|
||||
if (invariant)
|
||||
return std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::system_clock::now().time_since_epoch());
|
||||
return std::chrono::nanoseconds{MultiplyHigh(GetUptime(), ns_rdtsc_factor)};
|
||||
}
|
||||
|
||||
std::chrono::microseconds WallClock::GetTimeUS() const {
|
||||
if (invariant)
|
||||
return std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::system_clock::now().time_since_epoch());
|
||||
return std::chrono::microseconds{MultiplyHigh(GetUptime(), us_rdtsc_factor)};
|
||||
}
|
||||
|
||||
std::chrono::milliseconds WallClock::GetTimeMS() const {
|
||||
if (invariant)
|
||||
return std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch());
|
||||
return std::chrono::milliseconds{MultiplyHigh(GetUptime(), ms_rdtsc_factor)};
|
||||
}
|
||||
|
||||
s64 WallClock::GetCNTPCT() const {
|
||||
if (invariant)
|
||||
return GetUptime() * NsToCNTPCTRatio::num / NsToCNTPCTRatio::den;
|
||||
return MultiplyHigh(GetUptime(), cntpct_rdtsc_factor);
|
||||
}
|
||||
|
||||
s64 WallClock::GetGPUTick() const {
|
||||
if (invariant)
|
||||
return GetUptime() * NsToGPUTickRatio::num / NsToGPUTickRatio::den;
|
||||
return MultiplyHigh(GetUptime(), gputick_rdtsc_factor);
|
||||
}
|
||||
|
||||
s64 WallClock::GetUptime() const {
|
||||
if (invariant)
|
||||
return std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::steady_clock::now().time_since_epoch()).count();
|
||||
return s64(Common::X64::FencedRDTSC());
|
||||
}
|
||||
|
||||
bool WallClock::IsNative() const {
|
||||
if (invariant)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
#elif defined(HAS_NCE)
|
||||
return std::make_unique<Arm64::NativeClock>();
|
||||
namespace {
|
||||
|
||||
[[nodiscard]] WallClock::FactorType GetFixedPointFactor(u64 num, u64 den) noexcept {
|
||||
return (WallClock::FactorType(num) << 64) / den;
|
||||
}
|
||||
|
||||
[[nodiscard]] u64 MultiplyHigh(u64 m, WallClock::FactorType factor) noexcept {
|
||||
return static_cast<u64>((m * factor) >> 64);
|
||||
}
|
||||
|
||||
[[nodiscard]] s64 GetHostCNTFRQ() noexcept {
|
||||
u64 cntfrq_el0 = 0;
|
||||
#ifdef ANDROID
|
||||
std::string_view board{""};
|
||||
char buffer[PROP_VALUE_MAX];
|
||||
int len{__system_property_get("ro.product.board", buffer)};
|
||||
board = std::string_view(buffer, static_cast<size_t>(len));
|
||||
if (board == "s5e9925") { // Exynos 2200
|
||||
cntfrq_el0 = 25600000;
|
||||
} else if (board == "exynos2100") { // Exynos 2100
|
||||
cntfrq_el0 = 26000000;
|
||||
} else if (board == "exynos9810") { // Exynos 9810
|
||||
cntfrq_el0 = 26000000;
|
||||
} else if (board == "s5e8825") { // Exynos 1280
|
||||
cntfrq_el0 = 26000000;
|
||||
} else {
|
||||
asm volatile("mrs %[cntfrq_el0], cntfrq_el0" : [cntfrq_el0] "=r"(cntfrq_el0));
|
||||
}
|
||||
return cntfrq_el0;
|
||||
#else
|
||||
return std::make_unique<StandardWallClock>();
|
||||
asm volatile("mrs %[cntfrq_el0], cntfrq_el0" : [cntfrq_el0] "=r"(cntfrq_el0));
|
||||
return cntfrq_el0;
|
||||
#endif
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
WallClock::WallClock(bool invariant_, u64 rdtsc_frequency_) noexcept {
|
||||
const u64 host_cntfrq = std::max<u64>(GetHostCNTFRQ(), 1);
|
||||
ns_cntfrq_factor = GetFixedPointFactor(NsRatio::den, host_cntfrq);
|
||||
us_cntfrq_factor = GetFixedPointFactor(UsRatio::den, host_cntfrq);
|
||||
ms_cntfrq_factor = GetFixedPointFactor(MsRatio::den, host_cntfrq);
|
||||
guest_cntfrq_factor = GetFixedPointFactor(CNTFRQ, host_cntfrq);
|
||||
gputick_cntfrq_factor = GetFixedPointFactor(GPUTickFreq, host_cntfrq);
|
||||
}
|
||||
|
||||
std::chrono::nanoseconds WallClock::GetTimeNS() const {
|
||||
return std::chrono::nanoseconds{MultiplyHigh(GetUptime(), ns_cntfrq_factor)};
|
||||
}
|
||||
|
||||
std::chrono::microseconds WallClock::GetTimeUS() const {
|
||||
return std::chrono::microseconds{MultiplyHigh(GetUptime(), us_cntfrq_factor)};
|
||||
}
|
||||
|
||||
std::chrono::milliseconds WallClock::GetTimeMS() const {
|
||||
return std::chrono::milliseconds{MultiplyHigh(GetUptime(), ms_cntfrq_factor)};
|
||||
}
|
||||
|
||||
s64 WallClock::GetCNTPCT() const {
|
||||
return MultiplyHigh(GetUptime(), guest_cntfrq_factor);
|
||||
}
|
||||
|
||||
s64 WallClock::GetGPUTick() const {
|
||||
return MultiplyHigh(GetUptime(), gputick_cntfrq_factor);
|
||||
}
|
||||
|
||||
s64 WallClock::GetUptime() const {
|
||||
s64 cntvct_el0 = 0;
|
||||
asm volatile(
|
||||
"dsb ish\n\t"
|
||||
"mrs %[cntvct_el0], cntvct_el0\n\t"
|
||||
"dsb ish\n\t"
|
||||
: [cntvct_el0] "=r"(cntvct_el0)
|
||||
);
|
||||
return cntvct_el0;
|
||||
}
|
||||
|
||||
bool WallClock::IsNative() const {
|
||||
return true;
|
||||
}
|
||||
#else
|
||||
WallClock::WallClock(bool invariant_, u64 rdtsc_frequency_) noexcept {}
|
||||
|
||||
std::chrono::nanoseconds WallClock::GetTimeNS() const {
|
||||
return std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::system_clock::now().time_since_epoch());
|
||||
}
|
||||
|
||||
std::chrono::microseconds WallClock::GetTimeUS() const {
|
||||
return std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::system_clock::now().time_since_epoch());
|
||||
}
|
||||
|
||||
std::chrono::milliseconds WallClock::GetTimeMS() const {
|
||||
return std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch());
|
||||
}
|
||||
|
||||
s64 WallClock::GetCNTPCT() const {
|
||||
return GetUptime() * NsToCNTPCTRatio::num / NsToCNTPCTRatio::den;
|
||||
}
|
||||
|
||||
s64 WallClock::GetGPUTick() const {
|
||||
return GetUptime() * NsToGPUTickRatio::num / NsToGPUTickRatio::den;
|
||||
}
|
||||
|
||||
s64 WallClock::GetUptime() const {
|
||||
return std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::steady_clock::now().time_since_epoch()).count();
|
||||
}
|
||||
|
||||
bool WallClock::IsNative() const {
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
WallClock CreateOptimalClock() noexcept {
|
||||
#if defined(ARCHITECTURE_x86_64)
|
||||
auto const& caps = GetCPUCaps();
|
||||
return WallClock(!(caps.invariant_tsc && caps.tsc_frequency >= std::nano::den), std::max<u64>(caps.tsc_frequency, 1));
|
||||
#elif defined(HAS_NCE)
|
||||
return WallClock(false, 1);
|
||||
#else
|
||||
return WallClock(true, 1);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
+35
-10
@@ -1,4 +1,4 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
|
||||
@@ -20,28 +20,28 @@ public:
|
||||
static constexpr u64 GPUTickFreq = 614'400'000; // GM20B GPU Tick Frequency = 614.4 MHz
|
||||
static constexpr u64 CPUTickFreq = 1'020'000'000; // T210/4 A57 CPU Tick Frequency = 1020.0 MHz
|
||||
|
||||
virtual ~WallClock() = default;
|
||||
explicit WallClock(bool invariant, u64 rdtsc_frequency_) noexcept;
|
||||
|
||||
/// @returns The time in nanoseconds since the construction of this clock.
|
||||
virtual std::chrono::nanoseconds GetTimeNS() const = 0;
|
||||
std::chrono::nanoseconds GetTimeNS() const;
|
||||
|
||||
/// @returns The time in microseconds since the construction of this clock.
|
||||
virtual std::chrono::microseconds GetTimeUS() const = 0;
|
||||
std::chrono::microseconds GetTimeUS() const;
|
||||
|
||||
/// @returns The time in milliseconds since the construction of this clock.
|
||||
virtual std::chrono::milliseconds GetTimeMS() const = 0;
|
||||
std::chrono::milliseconds GetTimeMS() const;
|
||||
|
||||
/// @returns The guest CNTPCT ticks since the construction of this clock.
|
||||
virtual s64 GetCNTPCT() const = 0;
|
||||
s64 GetCNTPCT() const;
|
||||
|
||||
/// @returns The guest GPU ticks since the construction of this clock.
|
||||
virtual s64 GetGPUTick() const = 0;
|
||||
s64 GetGPUTick() const;
|
||||
|
||||
/// @returns The raw host timer ticks since an indeterminate epoch.
|
||||
virtual s64 GetUptime() const = 0;
|
||||
s64 GetUptime() const;
|
||||
|
||||
/// @returns Whether the clock directly uses the host's hardware clock.
|
||||
virtual bool IsNative() const = 0;
|
||||
bool IsNative() const;
|
||||
|
||||
static inline u64 NSToCNTPCT(u64 ns) {
|
||||
return ns * NsToCNTPCTRatio::num / NsToCNTPCTRatio::den;
|
||||
@@ -85,8 +85,33 @@ protected:
|
||||
using CPUTickToUsRatio = std::ratio<std::micro::den, CPUTickFreq>;
|
||||
using CPUTickToCNTPCTRatio = std::ratio<CNTFRQ, CPUTickFreq>;
|
||||
using CPUTickToGPUTickRatio = std::ratio<GPUTickFreq, CPUTickFreq>;
|
||||
|
||||
#if defined(ARCHITECTURE_x86_64)
|
||||
bool invariant;
|
||||
u64 rdtsc_frequency;
|
||||
u64 ns_rdtsc_factor;
|
||||
u64 us_rdtsc_factor;
|
||||
u64 ms_rdtsc_factor;
|
||||
u64 cntpct_rdtsc_factor;
|
||||
u64 gputick_rdtsc_factor;
|
||||
#elif defined(HAS_NCE)
|
||||
public:
|
||||
using FactorType = unsigned __int128;
|
||||
|
||||
FactorType GetGuestCNTFRQFactor() const {
|
||||
return guest_cntfrq_factor;
|
||||
}
|
||||
protected:
|
||||
FactorType ns_cntfrq_factor;
|
||||
FactorType us_cntfrq_factor;
|
||||
FactorType ms_cntfrq_factor;
|
||||
FactorType guest_cntfrq_factor;
|
||||
FactorType gputick_cntfrq_factor;
|
||||
#else
|
||||
|
||||
#endif
|
||||
};
|
||||
|
||||
[[nodiscard]] std::unique_ptr<WallClock> CreateOptimalClock();
|
||||
[[nodiscard]] WallClock CreateOptimalClock() noexcept;
|
||||
|
||||
} // namespace Common
|
||||
|
||||
@@ -1,46 +0,0 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "common/uint128.h"
|
||||
#include "common/x64/native_clock.h"
|
||||
#include "common/x64/rdtsc.h"
|
||||
|
||||
namespace Common::X64 {
|
||||
|
||||
NativeClock::NativeClock(u64 rdtsc_frequency_)
|
||||
: rdtsc_frequency{rdtsc_frequency_}, ns_rdtsc_factor{GetFixedPoint64Factor(NsRatio::den,
|
||||
rdtsc_frequency)},
|
||||
us_rdtsc_factor{GetFixedPoint64Factor(UsRatio::den, rdtsc_frequency)},
|
||||
ms_rdtsc_factor{GetFixedPoint64Factor(MsRatio::den, rdtsc_frequency)},
|
||||
cntpct_rdtsc_factor{GetFixedPoint64Factor(CNTFRQ, rdtsc_frequency)},
|
||||
gputick_rdtsc_factor{GetFixedPoint64Factor(GPUTickFreq, rdtsc_frequency)} {}
|
||||
|
||||
std::chrono::nanoseconds NativeClock::GetTimeNS() const {
|
||||
return std::chrono::nanoseconds{MultiplyHigh(GetUptime(), ns_rdtsc_factor)};
|
||||
}
|
||||
|
||||
std::chrono::microseconds NativeClock::GetTimeUS() const {
|
||||
return std::chrono::microseconds{MultiplyHigh(GetUptime(), us_rdtsc_factor)};
|
||||
}
|
||||
|
||||
std::chrono::milliseconds NativeClock::GetTimeMS() const {
|
||||
return std::chrono::milliseconds{MultiplyHigh(GetUptime(), ms_rdtsc_factor)};
|
||||
}
|
||||
|
||||
s64 NativeClock::GetCNTPCT() const {
|
||||
return MultiplyHigh(GetUptime(), cntpct_rdtsc_factor);
|
||||
}
|
||||
|
||||
s64 NativeClock::GetGPUTick() const {
|
||||
return MultiplyHigh(GetUptime(), gputick_rdtsc_factor);
|
||||
}
|
||||
|
||||
s64 NativeClock::GetUptime() const {
|
||||
return static_cast<s64>(FencedRDTSC());
|
||||
}
|
||||
|
||||
bool NativeClock::IsNative() const {
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace Common::X64
|
||||
@@ -1,38 +0,0 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common/wall_clock.h"
|
||||
|
||||
namespace Common::X64 {
|
||||
|
||||
class NativeClock final : public WallClock {
|
||||
public:
|
||||
explicit NativeClock(u64 rdtsc_frequency_);
|
||||
|
||||
std::chrono::nanoseconds GetTimeNS() const override;
|
||||
|
||||
std::chrono::microseconds GetTimeUS() const override;
|
||||
|
||||
std::chrono::milliseconds GetTimeMS() const override;
|
||||
|
||||
s64 GetCNTPCT() const override;
|
||||
|
||||
s64 GetGPUTick() const override;
|
||||
|
||||
s64 GetUptime() const override;
|
||||
|
||||
bool IsNative() const override;
|
||||
|
||||
private:
|
||||
u64 rdtsc_frequency;
|
||||
|
||||
u64 ns_rdtsc_factor;
|
||||
u64 us_rdtsc_factor;
|
||||
u64 ms_rdtsc_factor;
|
||||
u64 cntpct_rdtsc_factor;
|
||||
u64 gputick_rdtsc_factor;
|
||||
};
|
||||
|
||||
} // namespace Common::X64
|
||||
@@ -1,13 +1,37 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: 2016 Citra Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <type_traits>
|
||||
#include <bitset>
|
||||
#include <initializer_list>
|
||||
#include <xbyak/xbyak.h>
|
||||
#include "common/assert.h"
|
||||
|
||||
// xbyak hates human beings
|
||||
#ifdef __GNUC__
|
||||
#pragma GCC diagnostic ignored "-Wconversion"
|
||||
#pragma GCC diagnostic ignored "-Wshadow"
|
||||
#endif
|
||||
#ifdef __clang__
|
||||
#pragma clang diagnostic ignored "-Wconversion"
|
||||
#pragma clang diagnostic ignored "-Wshadow"
|
||||
#endif
|
||||
|
||||
// You must ensure this matches with src/common/x64/xbyak.h on root dir
|
||||
#include <ankerl/unordered_dense.h>
|
||||
#include <boost/unordered_map.hpp>
|
||||
#define XBYAK_STD_UNORDERED_SET ankerl::unordered_dense::set
|
||||
#define XBYAK_STD_UNORDERED_MAP ankerl::unordered_dense::map
|
||||
#define XBYAK_STD_UNORDERED_MULTIMAP boost::unordered_multimap
|
||||
#include <xbyak/xbyak.h>
|
||||
#include <xbyak/xbyak_util.h>
|
||||
|
||||
#include <xbyak/xbyak.h>
|
||||
|
||||
namespace Common::X64 {
|
||||
|
||||
constexpr size_t RegToIndex(const Xbyak::Reg& reg) {
|
||||
@@ -174,12 +198,13 @@ inline ABIFrameInfo ABI_CalculateFrameSize(std::bitset<32> regs, size_t rsp_alig
|
||||
rsp_alignment -= subtraction;
|
||||
subtraction += rsp_alignment & 0xF;
|
||||
|
||||
return ABIFrameInfo{static_cast<s32>(subtraction),
|
||||
static_cast<s32>(subtraction - xmm_base_subtraction)};
|
||||
return ABIFrameInfo{
|
||||
s32(subtraction),
|
||||
s32(subtraction - xmm_base_subtraction)
|
||||
};
|
||||
}
|
||||
|
||||
inline size_t ABI_PushRegistersAndAdjustStack(Xbyak::CodeGenerator& code, std::bitset<32> regs,
|
||||
size_t rsp_alignment, size_t needed_frame_size = 0) {
|
||||
inline size_t ABI_PushRegistersAndAdjustStack(Xbyak::CodeGenerator& code, std::bitset<32> regs, size_t rsp_alignment, size_t needed_frame_size = 0) {
|
||||
auto frame_info = ABI_CalculateFrameSize(regs, rsp_alignment, needed_frame_size);
|
||||
|
||||
for (size_t i = 0; i < regs.size(); ++i) {
|
||||
@@ -202,8 +227,7 @@ inline size_t ABI_PushRegistersAndAdjustStack(Xbyak::CodeGenerator& code, std::b
|
||||
return ABI_SHADOW_SPACE;
|
||||
}
|
||||
|
||||
inline void ABI_PopRegistersAndAdjustStack(Xbyak::CodeGenerator& code, std::bitset<32> regs,
|
||||
size_t rsp_alignment, size_t needed_frame_size = 0) {
|
||||
inline void ABI_PopRegistersAndAdjustStack(Xbyak::CodeGenerator& code, std::bitset<32> regs, size_t rsp_alignment, size_t needed_frame_size = 0) {
|
||||
auto frame_info = ABI_CalculateFrameSize(regs, rsp_alignment, needed_frame_size);
|
||||
|
||||
for (size_t i = 0; i < regs.size(); ++i) {
|
||||
@@ -226,4 +250,38 @@ inline void ABI_PopRegistersAndAdjustStack(Xbyak::CodeGenerator& code, std::bits
|
||||
}
|
||||
}
|
||||
|
||||
// Constants for use with cmpps/cmpss
|
||||
enum {
|
||||
CMP_EQ = 0,
|
||||
CMP_LT = 1,
|
||||
CMP_LE = 2,
|
||||
CMP_UNORD = 3,
|
||||
CMP_NEQ = 4,
|
||||
CMP_NLT = 5,
|
||||
CMP_NLE = 6,
|
||||
CMP_ORD = 7,
|
||||
};
|
||||
|
||||
constexpr bool IsWithin2G(uintptr_t ref, uintptr_t target) {
|
||||
const u64 distance = target - (ref + 5);
|
||||
return !(distance >= 0x8000'0000ULL && distance <= ~0x8000'0000ULL);
|
||||
}
|
||||
|
||||
inline bool IsWithin2G(const Xbyak::CodeGenerator& code, uintptr_t target) {
|
||||
return IsWithin2G(reinterpret_cast<uintptr_t>(code.getCurr()), target);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline void CallFarFunction(Xbyak::CodeGenerator& code, const T f) {
|
||||
static_assert(std::is_pointer_v<T>, "Argument must be a (function) pointer.");
|
||||
size_t addr = reinterpret_cast<size_t>(f);
|
||||
if (IsWithin2G(code, addr)) {
|
||||
code.call(f);
|
||||
} else {
|
||||
// ABI_RETURN is a safe temp register to use before a call
|
||||
code.mov(ABI_RETURN, addr);
|
||||
code.call(ABI_RETURN);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Common::X64
|
||||
@@ -1,46 +0,0 @@
|
||||
// SPDX-FileCopyrightText: 2016 Citra Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <type_traits>
|
||||
#include <xbyak/xbyak.h>
|
||||
#include "common/x64/xbyak_abi.h"
|
||||
|
||||
namespace Common::X64 {
|
||||
|
||||
// Constants for use with cmpps/cmpss
|
||||
enum {
|
||||
CMP_EQ = 0,
|
||||
CMP_LT = 1,
|
||||
CMP_LE = 2,
|
||||
CMP_UNORD = 3,
|
||||
CMP_NEQ = 4,
|
||||
CMP_NLT = 5,
|
||||
CMP_NLE = 6,
|
||||
CMP_ORD = 7,
|
||||
};
|
||||
|
||||
constexpr bool IsWithin2G(uintptr_t ref, uintptr_t target) {
|
||||
const u64 distance = target - (ref + 5);
|
||||
return !(distance >= 0x8000'0000ULL && distance <= ~0x8000'0000ULL);
|
||||
}
|
||||
|
||||
inline bool IsWithin2G(const Xbyak::CodeGenerator& code, uintptr_t target) {
|
||||
return IsWithin2G(reinterpret_cast<uintptr_t>(code.getCurr()), target);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline void CallFarFunction(Xbyak::CodeGenerator& code, const T f) {
|
||||
static_assert(std::is_pointer_v<T>, "Argument must be a (function) pointer.");
|
||||
size_t addr = reinterpret_cast<size_t>(f);
|
||||
if (IsWithin2G(code, addr)) {
|
||||
code.call(f);
|
||||
} else {
|
||||
// ABI_RETURN is a safe temp register to use before a call
|
||||
code.mov(ABI_RETURN, addr);
|
||||
code.call(ABI_RETURN);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Common::X64
|
||||
@@ -113,8 +113,7 @@ void DynarmicCallbacks32::CallSVC(u32 swi) {
|
||||
}
|
||||
|
||||
void DynarmicCallbacks32::AddTicks(u64 ticks) {
|
||||
ASSERT_MSG(!m_parent.m_uses_wall_clock, "Dynarmic ticking disabled");
|
||||
|
||||
ASSERT(!m_parent.m_uses_wall_clock && "Dynarmic ticking disabled");
|
||||
// Divide the number of ticks by the amount of CPU cores. TODO(Subv): This yields only a
|
||||
// rough approximation of the amount of executed ticks in the system, it may be thrown off
|
||||
// if not all cores are doing a similar amount of work. Instead of doing this, we should
|
||||
@@ -123,14 +122,12 @@ void DynarmicCallbacks32::AddTicks(u64 ticks) {
|
||||
u64 amortized_ticks = ticks / Core::Hardware::NUM_CPU_CORES;
|
||||
// Always execute at least one tick.
|
||||
amortized_ticks = std::max<u64>(amortized_ticks, 1);
|
||||
|
||||
m_parent.m_system.CoreTiming().AddTicks(amortized_ticks);
|
||||
}
|
||||
|
||||
u64 DynarmicCallbacks32::GetTicksRemaining() {
|
||||
ASSERT_MSG(!m_parent.m_uses_wall_clock, "Dynarmic ticking disabled");
|
||||
|
||||
return std::max<s64>(m_parent.m_system.CoreTiming().GetDowncount(), 0);
|
||||
ASSERT(!m_parent.m_uses_wall_clock && "Dynarmic ticking disabled");
|
||||
return std::max<s64>(m_parent.m_system.CoreTiming().downcount, 0);
|
||||
}
|
||||
|
||||
bool DynarmicCallbacks32::CheckMemoryAccess(u64 addr, u64 size, Kernel::DebugWatchpointType type) {
|
||||
|
||||
@@ -150,8 +150,7 @@ void DynarmicCallbacks64::CallSVC(u32 svc) {
|
||||
}
|
||||
|
||||
void DynarmicCallbacks64::AddTicks(u64 ticks) {
|
||||
ASSERT_MSG(!m_parent.m_uses_wall_clock, "Dynarmic ticking disabled");
|
||||
|
||||
ASSERT(!m_parent.m_uses_wall_clock && "Dynarmic ticking disabled");
|
||||
// Divide the number of ticks by the amount of CPU cores. TODO(Subv): This yields only a
|
||||
// rough approximation of the amount of executed ticks in the system, it may be thrown off
|
||||
// if not all cores are doing a similar amount of work. Instead of doing this, we should
|
||||
@@ -160,13 +159,12 @@ void DynarmicCallbacks64::AddTicks(u64 ticks) {
|
||||
u64 amortized_ticks = ticks / Core::Hardware::NUM_CPU_CORES;
|
||||
// Always execute at least one tick.
|
||||
amortized_ticks = std::max<u64>(amortized_ticks, 1);
|
||||
|
||||
m_parent.m_system.CoreTiming().AddTicks(amortized_ticks);
|
||||
}
|
||||
|
||||
u64 DynarmicCallbacks64::GetTicksRemaining() {
|
||||
ASSERT(!m_parent.m_uses_wall_clock && "Dynarmic ticking disabled");
|
||||
return std::max<s64>(m_parent.m_system.CoreTiming().GetDowncount(), 0);
|
||||
return std::max<s64>(m_parent.m_system.CoreTiming().downcount, 0);
|
||||
}
|
||||
|
||||
u64 DynarmicCallbacks64::GetCNTPCT() {
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
#include <numeric>
|
||||
#include <bit>
|
||||
#include "common/arm64/native_clock.h"
|
||||
#include "common/wall_clock.h"
|
||||
#include "common/alignment.h"
|
||||
#include "common/literals.h"
|
||||
#include "core/arm/nce/arm_nce.h"
|
||||
@@ -578,7 +578,11 @@ void Patcher::WriteMsrHandler(ModuleDestLabel module_dest, oaknut::XReg src_reg,
|
||||
}
|
||||
|
||||
void Patcher::WriteCntpctHandler(ModuleDestLabel module_dest, oaknut::XReg dest_reg, oaknut::VectorCodeGenerator& cg) {
|
||||
static Common::Arm64::NativeClock clock{};
|
||||
#if defined(HAS_NCE)
|
||||
static Common::WallClock clock(false, 1);
|
||||
#else
|
||||
static Common::WallClock clock(true, 1);
|
||||
#endif
|
||||
const auto factor = clock.GetGuestCNTFRQFactor();
|
||||
const auto raw_factor = std::bit_cast<std::array<u64, 2>>(factor);
|
||||
|
||||
|
||||
+67
-88
@@ -57,15 +57,51 @@ void CoreTiming::Initialize(std::function<void()>&& on_thread_init_) {
|
||||
Reset();
|
||||
on_thread_init = std::move(on_thread_init_);
|
||||
event_fifo_id = 0;
|
||||
shutting_down = false;
|
||||
cpu_ticks = 0;
|
||||
if (is_multicore) {
|
||||
timer_thread.emplace([](CoreTiming& instance) {
|
||||
timer_thread = std::jthread([this](std::stop_token stop_token) {
|
||||
Common::SetCurrentThreadName("HostTiming");
|
||||
Common::SetCurrentThreadPriority(Common::ThreadPriority::High);
|
||||
instance.on_thread_init();
|
||||
instance.ThreadLoop();
|
||||
}, std::ref(*this));
|
||||
on_thread_init();
|
||||
has_started = true;
|
||||
while (!stop_token.stop_requested()) {
|
||||
while (!paused && !stop_token.stop_requested()) {
|
||||
paused_set = false;
|
||||
if (auto const next_time = Advance(); next_time) {
|
||||
// There are more events left in the queue, wait until the next event.
|
||||
auto wait_time = *next_time - GetGlobalTimeNs().count();
|
||||
if (wait_time > 0) {
|
||||
#ifdef _WIN32
|
||||
while (!paused && !event.IsSet() && wait_time > 0) {
|
||||
wait_time = *next_time - GetGlobalTimeNs().count();
|
||||
if (wait_time >= timer_resolution_ns) {
|
||||
Common::Windows::SleepForOneTick();
|
||||
} else {
|
||||
#ifdef ARCHITECTURE_x86_64
|
||||
Common::X64::MicroSleep();
|
||||
#else
|
||||
std::this_thread::yield();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
if (event.IsSet())
|
||||
event.Reset();
|
||||
#else
|
||||
event.WaitFor(std::chrono::nanoseconds(wait_time));
|
||||
#endif
|
||||
}
|
||||
} else {
|
||||
// Queue is empty, wait until another event is scheduled and signals us to
|
||||
// continue.
|
||||
wait_set = true;
|
||||
event.Wait();
|
||||
}
|
||||
wait_set = false;
|
||||
}
|
||||
paused_set = true;
|
||||
pause_event.Wait();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -90,7 +126,7 @@ void CoreTiming::SyncPause(bool is_paused) {
|
||||
}
|
||||
|
||||
Pause(is_paused);
|
||||
if (timer_thread) {
|
||||
if (timer_thread.joinable()) {
|
||||
if (!is_paused) {
|
||||
pause_event.Set();
|
||||
}
|
||||
@@ -190,33 +226,22 @@ void CoreTiming::ResetTicks() {
|
||||
}
|
||||
|
||||
u64 CoreTiming::GetClockTicks() const {
|
||||
u64 fres;
|
||||
if (is_multicore) [[likely]] {
|
||||
fres = clock->GetCNTPCT();
|
||||
} else {
|
||||
fres = Common::WallClock::CPUTickToCNTPCT(cpu_ticks);
|
||||
u64 fres = is_multicore ? clock.GetCNTPCT() : Common::WallClock::CPUTickToCNTPCT(cpu_ticks);
|
||||
if (auto const overclock = Settings::values.fast_cpu_time.GetValue(); overclock != Settings::CpuClock::Off) {
|
||||
fres = u64(f64(fres) * (1.7 + 0.3 * u32(overclock)));
|
||||
}
|
||||
|
||||
const auto overclock = Settings::values.fast_cpu_time.GetValue();
|
||||
|
||||
if (overclock != Settings::CpuClock::Off) {
|
||||
fres = (u64) ((double) fres * (1.7 + 0.3 * u32(overclock)));
|
||||
}
|
||||
|
||||
if (Settings::values.sync_core_speed.GetValue()) {
|
||||
const auto ticks = double(fres);
|
||||
const auto speed_limit = double(Settings::SpeedLimit())*0.01;
|
||||
return u64(ticks/speed_limit);
|
||||
} else {
|
||||
return fres;
|
||||
}
|
||||
if (::Settings::values.sync_core_speed.GetValue()) {
|
||||
auto const ticks = f64(fres);
|
||||
auto const speed_limit = f64(Settings::SpeedLimit()) * 0.01;
|
||||
return u64(ticks / speed_limit);
|
||||
}
|
||||
return fres;
|
||||
}
|
||||
|
||||
u64 CoreTiming::GetGPUTicks() const {
|
||||
if (is_multicore) [[likely]] {
|
||||
return clock->GetGPUTick();
|
||||
}
|
||||
return Common::WallClock::CPUTickToGPUTick(cpu_ticks);
|
||||
return is_multicore
|
||||
? clock.GetGPUTick()
|
||||
: Common::WallClock::CPUTickToGPUTick(cpu_ticks);
|
||||
}
|
||||
|
||||
std::optional<s64> CoreTiming::Advance() {
|
||||
@@ -278,75 +303,29 @@ std::optional<s64> CoreTiming::Advance() {
|
||||
}
|
||||
}
|
||||
|
||||
void CoreTiming::ThreadLoop() {
|
||||
has_started = true;
|
||||
while (!shutting_down) {
|
||||
while (!paused) {
|
||||
paused_set = false;
|
||||
const auto next_time = Advance();
|
||||
if (next_time) {
|
||||
// There are more events left in the queue, wait until the next event.
|
||||
auto wait_time = *next_time - GetGlobalTimeNs().count();
|
||||
if (wait_time > 0) {
|
||||
#ifdef _WIN32
|
||||
while (!paused && !event.IsSet() && wait_time > 0) {
|
||||
wait_time = *next_time - GetGlobalTimeNs().count();
|
||||
if (wait_time >= timer_resolution_ns) {
|
||||
Common::Windows::SleepForOneTick();
|
||||
} else {
|
||||
#ifdef ARCHITECTURE_x86_64
|
||||
Common::X64::MicroSleep();
|
||||
#else
|
||||
std::this_thread::yield();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
if (event.IsSet()) {
|
||||
event.Reset();
|
||||
}
|
||||
#else
|
||||
event.WaitFor(std::chrono::nanoseconds(wait_time));
|
||||
#endif
|
||||
}
|
||||
} else {
|
||||
// Queue is empty, wait until another event is scheduled and signals us to
|
||||
// continue.
|
||||
wait_set = true;
|
||||
event.Wait();
|
||||
}
|
||||
wait_set = false;
|
||||
}
|
||||
|
||||
paused_set = true;
|
||||
pause_event.Wait();
|
||||
}
|
||||
}
|
||||
|
||||
void CoreTiming::Reset() {
|
||||
paused = true;
|
||||
shutting_down = true;
|
||||
pause_event.Set();
|
||||
event.Set();
|
||||
if (timer_thread) {
|
||||
timer_thread->join();
|
||||
if (timer_thread.joinable()) {
|
||||
timer_thread.request_stop();
|
||||
timer_thread.join();
|
||||
}
|
||||
timer_thread.reset();
|
||||
has_started = false;
|
||||
}
|
||||
|
||||
std::chrono::nanoseconds CoreTiming::GetGlobalTimeNs() const {
|
||||
if (is_multicore) [[likely]] {
|
||||
return clock->GetTimeNS();
|
||||
}
|
||||
return std::chrono::nanoseconds{Common::WallClock::CPUTickToNS(cpu_ticks)};
|
||||
/// @brief Returns current time in nanoseconds.
|
||||
std::chrono::nanoseconds CoreTiming::GetGlobalTimeNs() const noexcept {
|
||||
return is_multicore
|
||||
? clock.GetTimeNS()
|
||||
: std::chrono::nanoseconds{Common::WallClock::CPUTickToNS(cpu_ticks)};
|
||||
}
|
||||
|
||||
std::chrono::microseconds CoreTiming::GetGlobalTimeUs() const {
|
||||
if (is_multicore) [[likely]] {
|
||||
return clock->GetTimeUS();
|
||||
}
|
||||
return std::chrono::microseconds{Common::WallClock::CPUTickToUS(cpu_ticks)};
|
||||
/// @brief Returns current time in microseconds.
|
||||
std::chrono::microseconds CoreTiming::GetGlobalTimeUs() const noexcept {
|
||||
return is_multicore
|
||||
? clock.GetTimeUS()
|
||||
: std::chrono::microseconds{Common::WallClock::CPUTickToUS(cpu_ticks)};
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
|
||||
+6
-12
@@ -1,4 +1,4 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
|
||||
@@ -118,7 +118,7 @@ public:
|
||||
|
||||
void Idle();
|
||||
|
||||
s64 GetDowncount() const {
|
||||
s64 GetDowncount() const noexcept {
|
||||
return downcount;
|
||||
}
|
||||
|
||||
@@ -128,11 +128,8 @@ public:
|
||||
/// Returns the current GPU tick value.
|
||||
u64 GetGPUTicks() const;
|
||||
|
||||
/// Returns current time in microseconds.
|
||||
std::chrono::microseconds GetGlobalTimeUs() const;
|
||||
|
||||
/// Returns current time in nanoseconds.
|
||||
std::chrono::nanoseconds GetGlobalTimeNs() const;
|
||||
[[nodiscard]] std::chrono::microseconds GetGlobalTimeUs() const noexcept;
|
||||
[[nodiscard]] std::chrono::nanoseconds GetGlobalTimeNs() const noexcept;
|
||||
|
||||
/// Checks for events manually and returns time in nanoseconds for next event, threadsafe.
|
||||
std::optional<s64> Advance();
|
||||
@@ -141,13 +138,11 @@ public:
|
||||
void SetTimerResolutionNs(std::chrono::nanoseconds ns);
|
||||
#endif
|
||||
|
||||
private:
|
||||
struct Event;
|
||||
void ThreadLoop();
|
||||
|
||||
void Reset();
|
||||
|
||||
std::unique_ptr<Common::WallClock> clock;
|
||||
Common::WallClock clock;
|
||||
|
||||
s64 global_timer = 0;
|
||||
|
||||
@@ -165,11 +160,10 @@ private:
|
||||
Common::Event pause_event{};
|
||||
mutable std::mutex basic_lock;
|
||||
std::mutex advance_lock;
|
||||
std::optional<std::jthread> timer_thread;
|
||||
std::jthread timer_thread;
|
||||
std::atomic<bool> paused{};
|
||||
std::atomic<bool> paused_set{};
|
||||
std::atomic<bool> wait_set{};
|
||||
std::atomic<bool> shutting_down{};
|
||||
std::atomic<bool> has_started{};
|
||||
std::function<void()> on_thread_init{};
|
||||
|
||||
|
||||
@@ -1,86 +1,48 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <span>
|
||||
#include <vector>
|
||||
|
||||
#include "common/swap.h"
|
||||
#include "core/file_sys/system_archive/time_zone_binary.h"
|
||||
#include "core/file_sys/vfs/vfs_static.h"
|
||||
#include "core/file_sys/vfs/vfs_types.h"
|
||||
#include "core/file_sys/vfs/vfs_vector.h"
|
||||
|
||||
#include "nx_tzdb.h"
|
||||
|
||||
namespace FileSys::SystemArchive {
|
||||
|
||||
const static std::map<std::string, const std::map<const char*, const std::vector<u8>>&>
|
||||
tzdb_zoneinfo_dirs = {{"Africa", NxTzdb::africa},
|
||||
{"America", NxTzdb::america},
|
||||
{"Antarctica", NxTzdb::antarctica},
|
||||
{"Arctic", NxTzdb::arctic},
|
||||
{"Asia", NxTzdb::asia},
|
||||
{"Atlantic", NxTzdb::atlantic},
|
||||
{"Australia", NxTzdb::australia},
|
||||
{"Brazil", NxTzdb::brazil},
|
||||
{"Canada", NxTzdb::canada},
|
||||
{"Chile", NxTzdb::chile},
|
||||
{"Etc", NxTzdb::etc},
|
||||
{"Europe", NxTzdb::europe},
|
||||
{"Indian", NxTzdb::indian},
|
||||
{"Mexico", NxTzdb::mexico},
|
||||
{"Pacific", NxTzdb::pacific},
|
||||
{"US", NxTzdb::us}};
|
||||
|
||||
const static std::map<std::string, const std::map<const char*, const std::vector<u8>>&>
|
||||
tzdb_america_dirs = {{"Argentina", NxTzdb::america_argentina},
|
||||
{"Indiana", NxTzdb::america_indiana},
|
||||
{"Kentucky", NxTzdb::america_kentucky},
|
||||
{"North_Dakota", NxTzdb::america_north_dakota}};
|
||||
|
||||
static void GenerateFiles(std::vector<VirtualFile>& directory,
|
||||
const std::map<const char*, const std::vector<u8>>& files) {
|
||||
for (const auto& [filename, data] : files) {
|
||||
const auto data_copy{data};
|
||||
const std::string filename_copy{filename};
|
||||
VirtualFile file{
|
||||
std::make_shared<VectorVfsFile>(std::move(data_copy), std::move(filename_copy))};
|
||||
directory.push_back(file);
|
||||
}
|
||||
}
|
||||
|
||||
static std::vector<VirtualFile> GenerateZoneinfoFiles() {
|
||||
std::vector<VirtualFile> zoneinfo_files;
|
||||
GenerateFiles(zoneinfo_files, NxTzdb::zoneinfo);
|
||||
return zoneinfo_files;
|
||||
}
|
||||
|
||||
VirtualDir TimeZoneBinary() {
|
||||
std::vector<VirtualDir> america_sub_dirs;
|
||||
for (const auto& [dir_name, files] : tzdb_america_dirs) {
|
||||
std::vector<VirtualFile> vfs_files;
|
||||
GenerateFiles(vfs_files, files);
|
||||
america_sub_dirs.push_back(std::make_shared<VectorVfsDirectory>(
|
||||
std::move(vfs_files), std::vector<VirtualDir>{}, dir_name));
|
||||
}
|
||||
|
||||
america_sub_dirs.push_back(std::make_shared<VectorVfsDirectory>(NxTzdb::CollectFiles_america_argentina(), std::vector<VirtualDir>{}, "Argentina"));
|
||||
america_sub_dirs.push_back(std::make_shared<VectorVfsDirectory>(NxTzdb::CollectFiles_america_indiana(), std::vector<VirtualDir>{}, "Indiana"));
|
||||
america_sub_dirs.push_back(std::make_shared<VectorVfsDirectory>(NxTzdb::CollectFiles_america_kentucky(), std::vector<VirtualDir>{}, "Kentucky"));
|
||||
america_sub_dirs.push_back(std::make_shared<VectorVfsDirectory>(NxTzdb::CollectFiles_america_north_dakota(), std::vector<VirtualDir>{}, "North_Dakota"));
|
||||
std::vector<VirtualDir> zoneinfo_sub_dirs;
|
||||
for (const auto& [dir_name, files] : tzdb_zoneinfo_dirs) {
|
||||
std::vector<VirtualFile> vfs_files;
|
||||
GenerateFiles(vfs_files, files);
|
||||
if (dir_name == "America") {
|
||||
zoneinfo_sub_dirs.push_back(std::make_shared<VectorVfsDirectory>(
|
||||
std::move(vfs_files), std::move(america_sub_dirs), dir_name));
|
||||
} else {
|
||||
zoneinfo_sub_dirs.push_back(std::make_shared<VectorVfsDirectory>(
|
||||
std::move(vfs_files), std::vector<VirtualDir>{}, dir_name));
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<VirtualDir> zoneinfo_dir{std::make_shared<VectorVfsDirectory>(
|
||||
GenerateZoneinfoFiles(), std::move(zoneinfo_sub_dirs), "zoneinfo")};
|
||||
std::vector<VirtualFile> root_files;
|
||||
GenerateFiles(root_files, NxTzdb::base);
|
||||
|
||||
return std::make_shared<VectorVfsDirectory>(std::move(root_files), std::move(zoneinfo_dir),
|
||||
"data");
|
||||
zoneinfo_sub_dirs.push_back(std::make_shared<VectorVfsDirectory>(NxTzdb::CollectFiles_africa(), std::vector<VirtualDir>{}, "Africa"));
|
||||
zoneinfo_sub_dirs.push_back(std::make_shared<VectorVfsDirectory>(NxTzdb::CollectFiles_america(), std::move(america_sub_dirs), "America"));
|
||||
zoneinfo_sub_dirs.push_back(std::make_shared<VectorVfsDirectory>(NxTzdb::CollectFiles_antarctica(), std::vector<VirtualDir>{}, "Antarctica"));
|
||||
zoneinfo_sub_dirs.push_back(std::make_shared<VectorVfsDirectory>(NxTzdb::CollectFiles_arctic(), std::vector<VirtualDir>{}, "Arctic"));
|
||||
zoneinfo_sub_dirs.push_back(std::make_shared<VectorVfsDirectory>(NxTzdb::CollectFiles_asia(), std::vector<VirtualDir>{}, "Asia"));
|
||||
zoneinfo_sub_dirs.push_back(std::make_shared<VectorVfsDirectory>(NxTzdb::CollectFiles_atlantic(), std::vector<VirtualDir>{}, "Atlantic"));
|
||||
zoneinfo_sub_dirs.push_back(std::make_shared<VectorVfsDirectory>(NxTzdb::CollectFiles_australia(), std::vector<VirtualDir>{}, "Australia"));
|
||||
zoneinfo_sub_dirs.push_back(std::make_shared<VectorVfsDirectory>(NxTzdb::CollectFiles_brazil(), std::vector<VirtualDir>{}, "Brazil"));
|
||||
zoneinfo_sub_dirs.push_back(std::make_shared<VectorVfsDirectory>(NxTzdb::CollectFiles_canada(), std::vector<VirtualDir>{}, "Canada"));
|
||||
zoneinfo_sub_dirs.push_back(std::make_shared<VectorVfsDirectory>(NxTzdb::CollectFiles_chile(), std::vector<VirtualDir>{}, "Chile"));
|
||||
zoneinfo_sub_dirs.push_back(std::make_shared<VectorVfsDirectory>(NxTzdb::CollectFiles_etc(), std::vector<VirtualDir>{}, "Etc"));
|
||||
zoneinfo_sub_dirs.push_back(std::make_shared<VectorVfsDirectory>(NxTzdb::CollectFiles_europe(), std::vector<VirtualDir>{}, "Europe"));
|
||||
zoneinfo_sub_dirs.push_back(std::make_shared<VectorVfsDirectory>(NxTzdb::CollectFiles_indian(), std::vector<VirtualDir>{}, "Indian"));
|
||||
zoneinfo_sub_dirs.push_back(std::make_shared<VectorVfsDirectory>(NxTzdb::CollectFiles_mexico(), std::vector<VirtualDir>{}, "Mexico"));
|
||||
zoneinfo_sub_dirs.push_back(std::make_shared<VectorVfsDirectory>(NxTzdb::CollectFiles_pacific(), std::vector<VirtualDir>{}, "Pacific"));
|
||||
zoneinfo_sub_dirs.push_back(std::make_shared<VectorVfsDirectory>(NxTzdb::CollectFiles_us(), std::vector<VirtualDir>{}, "US"));
|
||||
std::vector<VirtualDir> zoneinfo_dir{std::make_shared<VectorVfsDirectory>(NxTzdb::CollectFiles_zoneinfo(), std::move(zoneinfo_sub_dirs), "zoneinfo")};
|
||||
// last files (root)
|
||||
return std::make_shared<VectorVfsDirectory>(NxTzdb::CollectFiles_base(), std::move(zoneinfo_dir), "data");
|
||||
}
|
||||
|
||||
} // namespace FileSys::SystemArchive
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: 2016 Citra Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
@@ -13,6 +16,16 @@ namespace IPC {
|
||||
/// Size of the command buffer area, in 32-bit words.
|
||||
constexpr std::size_t COMMAND_BUFFER_LENGTH = 0x100 / sizeof(u32);
|
||||
|
||||
/// Must match bitfields
|
||||
constexpr std::size_t MAX_BUFFER_DESCRIPTORS = 16;
|
||||
constexpr std::size_t MAX_INCOMING_MOVE_HANDLERS = 16;
|
||||
constexpr std::size_t MAX_INCOMING_COPY_HANDLERS = 16;
|
||||
|
||||
/// Doesn't need to match bitfields but usually not big enough
|
||||
constexpr std::size_t MAX_OUTGOING_COPY_OBJECTS = 16;
|
||||
constexpr std::size_t MAX_OUTGOING_MOVE_OBJECTS = 16;
|
||||
constexpr std::size_t MAX_OUTGOING_DOMAIN_OBJECTS = 16;
|
||||
|
||||
enum class ControlCommand : u32 {
|
||||
ConvertSessionToDomain = 0,
|
||||
ConvertDomainToSession = 1,
|
||||
|
||||
@@ -128,10 +128,12 @@ Result SessionRequestManager::HandleDomainSyncRequest(Kernel::KServerSession* se
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
HLERequestContext::HLERequestContext(Kernel::KernelCore& kernel_, Core::Memory::Memory& memory_,
|
||||
Kernel::KServerSession* server_session_,
|
||||
Kernel::KThread* thread_)
|
||||
: server_session(server_session_), thread(thread_), kernel{kernel_}, memory{memory_} {
|
||||
HLERequestContext::HLERequestContext(Kernel::KernelCore& kernel_, Core::Memory::Memory& memory_, Kernel::KServerSession* server_session_, Kernel::KThread* thread_)
|
||||
: server_session(server_session_)
|
||||
, thread(thread_)
|
||||
, kernel{kernel_}
|
||||
, memory{memory_}
|
||||
{
|
||||
cmd_buf[0] = 0;
|
||||
}
|
||||
|
||||
@@ -155,9 +157,6 @@ void HLERequestContext::ParseCommandBuffer(u32_le* src_cmdbuf, bool incoming) {
|
||||
}
|
||||
if (incoming) {
|
||||
// Populate the object lists with the data in the IPC request.
|
||||
incoming_copy_handles.reserve(handle_descriptor_header->num_handles_to_copy);
|
||||
incoming_move_handles.reserve(handle_descriptor_header->num_handles_to_move);
|
||||
|
||||
for (u32 handle = 0; handle < handle_descriptor_header->num_handles_to_copy; ++handle) {
|
||||
incoming_copy_handles.push_back(rp.Pop<Handle>());
|
||||
}
|
||||
@@ -172,11 +171,6 @@ void HLERequestContext::ParseCommandBuffer(u32_le* src_cmdbuf, bool incoming) {
|
||||
}
|
||||
}
|
||||
|
||||
buffer_x_descriptors.reserve(command_header->num_buf_x_descriptors);
|
||||
buffer_a_descriptors.reserve(command_header->num_buf_a_descriptors);
|
||||
buffer_b_descriptors.reserve(command_header->num_buf_b_descriptors);
|
||||
buffer_w_descriptors.reserve(command_header->num_buf_w_descriptors);
|
||||
|
||||
for (u32 i = 0; i < command_header->num_buf_x_descriptors; ++i) {
|
||||
buffer_x_descriptors.push_back(rp.PopRaw<IPC::BufferDescriptorX>());
|
||||
}
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
@@ -11,6 +14,7 @@
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
#include <vector>
|
||||
#include <boost/container/static_vector.hpp>
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/common_types.h"
|
||||
@@ -181,8 +185,7 @@ private:
|
||||
*/
|
||||
class HLERequestContext {
|
||||
public:
|
||||
explicit HLERequestContext(Kernel::KernelCore& kernel, Core::Memory::Memory& memory,
|
||||
Kernel::KServerSession* session, Kernel::KThread* thread);
|
||||
explicit HLERequestContext(Kernel::KernelCore& kernel, Core::Memory::Memory& memory, Kernel::KServerSession* session, Kernel::KThread* thread);
|
||||
~HLERequestContext();
|
||||
|
||||
/// Returns a pointer to the IPC command buffer for this request.
|
||||
@@ -233,19 +236,19 @@ public:
|
||||
return data_payload_offset;
|
||||
}
|
||||
|
||||
[[nodiscard]] const std::vector<IPC::BufferDescriptorX>& BufferDescriptorX() const {
|
||||
[[nodiscard]] const boost::container::static_vector<IPC::BufferDescriptorX, 16>& BufferDescriptorX() const {
|
||||
return buffer_x_descriptors;
|
||||
}
|
||||
|
||||
[[nodiscard]] const std::vector<IPC::BufferDescriptorABW>& BufferDescriptorA() const {
|
||||
[[nodiscard]] const boost::container::static_vector<IPC::BufferDescriptorABW, 16>& BufferDescriptorA() const {
|
||||
return buffer_a_descriptors;
|
||||
}
|
||||
|
||||
[[nodiscard]] const std::vector<IPC::BufferDescriptorABW>& BufferDescriptorB() const {
|
||||
[[nodiscard]] const boost::container::static_vector<IPC::BufferDescriptorABW, 16>& BufferDescriptorB() const {
|
||||
return buffer_b_descriptors;
|
||||
}
|
||||
|
||||
[[nodiscard]] const std::vector<IPC::BufferDescriptorC>& BufferDescriptorC() const {
|
||||
[[nodiscard]] const boost::container::static_vector<IPC::BufferDescriptorC, 16>& BufferDescriptorC() const {
|
||||
return buffer_c_descriptors;
|
||||
}
|
||||
|
||||
@@ -399,38 +402,35 @@ private:
|
||||
Kernel::KHandleTable* client_handle_table{};
|
||||
Kernel::KThread* thread{};
|
||||
|
||||
std::vector<Handle> incoming_move_handles;
|
||||
std::vector<Handle> incoming_copy_handles;
|
||||
boost::container::static_vector<IPC::BufferDescriptorX, IPC::MAX_BUFFER_DESCRIPTORS> buffer_x_descriptors;
|
||||
boost::container::static_vector<IPC::BufferDescriptorABW, IPC::MAX_BUFFER_DESCRIPTORS> buffer_a_descriptors;
|
||||
boost::container::static_vector<IPC::BufferDescriptorABW, IPC::MAX_BUFFER_DESCRIPTORS> buffer_b_descriptors;
|
||||
boost::container::static_vector<IPC::BufferDescriptorABW, IPC::MAX_BUFFER_DESCRIPTORS> buffer_w_descriptors;
|
||||
boost::container::static_vector<IPC::BufferDescriptorC, IPC::MAX_BUFFER_DESCRIPTORS> buffer_c_descriptors;
|
||||
boost::container::static_vector<Handle, IPC::MAX_INCOMING_MOVE_HANDLERS> incoming_move_handles;
|
||||
boost::container::static_vector<Handle, IPC::MAX_INCOMING_COPY_HANDLERS> incoming_copy_handles;
|
||||
boost::container::static_vector<Kernel::KAutoObject*, IPC::MAX_OUTGOING_MOVE_OBJECTS> outgoing_move_objects;
|
||||
boost::container::static_vector<Kernel::KAutoObject*, IPC::MAX_OUTGOING_COPY_OBJECTS> outgoing_copy_objects;
|
||||
boost::container::static_vector<SessionRequestHandlerPtr, IPC::MAX_OUTGOING_DOMAIN_OBJECTS> outgoing_domain_objects;
|
||||
|
||||
std::vector<Kernel::KAutoObject*> outgoing_move_objects;
|
||||
std::vector<Kernel::KAutoObject*> outgoing_copy_objects;
|
||||
std::vector<SessionRequestHandlerPtr> outgoing_domain_objects;
|
||||
mutable std::array<Common::ScratchBuffer<u8>, 3> read_buffer_data_a{};
|
||||
mutable std::array<Common::ScratchBuffer<u8>, 3> read_buffer_data_x{};
|
||||
|
||||
std::optional<IPC::CommandHeader> command_header;
|
||||
std::optional<IPC::HandleDescriptorHeader> handle_descriptor_header;
|
||||
std::optional<IPC::DataPayloadHeader> data_payload_header;
|
||||
std::optional<IPC::DomainMessageHeader> domain_message_header;
|
||||
std::vector<IPC::BufferDescriptorX> buffer_x_descriptors;
|
||||
std::vector<IPC::BufferDescriptorABW> buffer_a_descriptors;
|
||||
std::vector<IPC::BufferDescriptorABW> buffer_b_descriptors;
|
||||
std::vector<IPC::BufferDescriptorABW> buffer_w_descriptors;
|
||||
std::vector<IPC::BufferDescriptorC> buffer_c_descriptors;
|
||||
std::weak_ptr<SessionRequestManager> manager{};
|
||||
Kernel::KernelCore& kernel;
|
||||
Core::Memory::Memory& memory;
|
||||
|
||||
u32_le command{};
|
||||
u64 pid{};
|
||||
u32_le command{};
|
||||
u32 write_size{};
|
||||
u32 data_payload_offset{};
|
||||
u32 handles_offset{};
|
||||
u32 domain_offset{};
|
||||
|
||||
std::weak_ptr<SessionRequestManager> manager{};
|
||||
bool is_deferred{false};
|
||||
|
||||
Kernel::KernelCore& kernel;
|
||||
Core::Memory::Memory& memory;
|
||||
|
||||
mutable std::array<Common::ScratchBuffer<u8>, 3> read_buffer_data_a{};
|
||||
mutable std::array<Common::ScratchBuffer<u8>, 3> read_buffer_data_x{};
|
||||
bool is_deferred = false;
|
||||
};
|
||||
|
||||
} // namespace Service
|
||||
|
||||
@@ -26,8 +26,11 @@ namespace Service::android {
|
||||
BufferQueueProducer::BufferQueueProducer(Service::KernelHelpers::ServiceContext& service_context_,
|
||||
std::shared_ptr<BufferQueueCore> buffer_queue_core_,
|
||||
Service::Nvidia::NvCore::NvMap& nvmap_)
|
||||
: service_context{service_context_}, core{std::move(buffer_queue_core_)}, slots(core->slots),
|
||||
clock{Common::CreateOptimalClock()}, nvmap(nvmap_) {
|
||||
: service_context{service_context_}, core{std::move(buffer_queue_core_)}
|
||||
, slots(core->slots)
|
||||
, clock{Common::CreateOptimalClock()}
|
||||
, nvmap(nvmap_)
|
||||
{
|
||||
buffer_wait_event = service_context.CreateEvent("BufferQueue:WaitEvent");
|
||||
}
|
||||
|
||||
@@ -485,7 +488,7 @@ Status BufferQueueProducer::QueueBuffer(s32 slot, const QueueBufferInput& input,
|
||||
slots[slot].buffer_state = BufferState::Queued;
|
||||
slots[slot].frame_number = core->frame_counter;
|
||||
slots[slot].queue_time = timestamp;
|
||||
slots[slot].presentation_time = clock->GetTimeNS().count();
|
||||
slots[slot].presentation_time = clock.GetTimeNS().count();
|
||||
slots[slot].fence = fence;
|
||||
|
||||
item.slot = slot;
|
||||
|
||||
@@ -89,8 +89,7 @@ private:
|
||||
s32 next_callback_ticket{};
|
||||
s32 current_callback_ticket{};
|
||||
std::condition_variable_any callback_condition;
|
||||
std::unique_ptr<Common::WallClock> clock;
|
||||
|
||||
Common::WallClock clock;
|
||||
Service::Nvidia::NvCore::NvMap& nvmap;
|
||||
};
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
|
||||
@@ -20,6 +20,7 @@
|
||||
#include "common/settings.h"
|
||||
#include "core/arm/arm_interface.h"
|
||||
#include "core/core.h"
|
||||
#include "core/hle/ipc.h"
|
||||
#include "core/hle/kernel/k_page_table.h"
|
||||
#include "core/hle/kernel/k_process.h"
|
||||
#include "core/hle/result.h"
|
||||
@@ -124,8 +125,7 @@ json GetFullDataAuto(const std::string& timestamp, u64 title_id, Core::System& s
|
||||
}
|
||||
|
||||
template <bool read_value, typename DescriptorType>
|
||||
json GetHLEBufferDescriptorData(const std::vector<DescriptorType>& buffer,
|
||||
Core::Memory::Memory& memory) {
|
||||
json GetHLEBufferDescriptorData(const boost::container::static_vector<DescriptorType, IPC::MAX_BUFFER_DESCRIPTORS>& buffer, Core::Memory::Memory& memory) {
|
||||
auto buffer_out = json::array();
|
||||
for (const auto& desc : buffer) {
|
||||
auto entry = json{
|
||||
|
||||
@@ -224,7 +224,7 @@ void A64EmitX64::GenTerminalHandlers() {
|
||||
terminal_handler_fast_dispatch_hint = code.getCurr<const void*>();
|
||||
calculate_location_descriptor();
|
||||
code.L(rsb_cache_miss);
|
||||
code.mov(r8, reinterpret_cast<u64>(fast_dispatch_table.data()));
|
||||
code.mov(r8, u64(fast_dispatch_table.data()));
|
||||
//code.mov(r12, qword[code.ABI_JIT_PTR + offsetof(A64JitState, pc)]);
|
||||
code.mov(r12, rbx);
|
||||
if (code.HasHostFeature(HostFeature::SSE42)) {
|
||||
@@ -244,7 +244,7 @@ void A64EmitX64::GenTerminalHandlers() {
|
||||
|
||||
code.align();
|
||||
fast_dispatch_table_lookup = code.getCurr<FastDispatchEntry& (*)(u64)>();
|
||||
code.mov(code.ABI_PARAM2, reinterpret_cast<u64>(fast_dispatch_table.data()));
|
||||
code.mov(code.ABI_PARAM2, u64(fast_dispatch_table.data()));
|
||||
if (code.HasHostFeature(HostFeature::SSE42)) {
|
||||
code.crc32(code.ABI_PARAM1, code.ABI_PARAM2);
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@ struct FrameInfo {
|
||||
};
|
||||
static_assert(ABI_SHADOW_SPACE <= 32);
|
||||
|
||||
static FrameInfo CalculateFrameInfo(const size_t num_gprs, const size_t num_xmms, size_t frame_size) {
|
||||
static FrameInfo CalculateFrameInfo(const size_t num_gprs, const size_t num_xmms, size_t frame_size) noexcept {
|
||||
// We are initially 8 byte aligned because the return value is pushed onto an aligned stack after a call.
|
||||
const size_t rsp_alignment = (num_gprs % 2 == 0) ? 8 : 0;
|
||||
const size_t total_xmm_size = num_xmms * XMM_SIZE;
|
||||
@@ -40,7 +40,7 @@ static FrameInfo CalculateFrameInfo(const size_t num_gprs, const size_t num_xmms
|
||||
};
|
||||
}
|
||||
|
||||
void ABI_PushRegistersAndAdjustStack(BlockOfCode& code, const size_t frame_size, std::bitset<32> const& regs) {
|
||||
static void ABI_PushRegistersAndAdjustStack(BlockOfCode& code, const size_t frame_size, std::bitset<32> regs) noexcept {
|
||||
using namespace Xbyak::util;
|
||||
|
||||
const size_t num_gprs = (ABI_ALL_GPRS & regs).count();
|
||||
@@ -65,7 +65,7 @@ void ABI_PushRegistersAndAdjustStack(BlockOfCode& code, const size_t frame_size,
|
||||
}
|
||||
}
|
||||
|
||||
void ABI_PopRegistersAndAdjustStack(BlockOfCode& code, const size_t frame_size, std::bitset<32> const& regs) {
|
||||
static void ABI_PopRegistersAndAdjustStack(BlockOfCode& code, const size_t frame_size, std::bitset<32> regs) noexcept {
|
||||
using namespace Xbyak::util;
|
||||
|
||||
const size_t num_gprs = (ABI_ALL_GPRS & regs).count();
|
||||
@@ -107,13 +107,13 @@ void ABI_PopCallerSaveRegistersAndAdjustStack(BlockOfCode& code, const std::size
|
||||
|
||||
// Windows ABI registers are not in the same allocation algorithm as unix's
|
||||
void ABI_PushCallerSaveRegistersAndAdjustStackExcept(BlockOfCode& code, const HostLoc exception) {
|
||||
std::bitset<32> regs = ABI_ALL_CALLER_SAVE;
|
||||
auto regs = ABI_ALL_CALLER_SAVE;
|
||||
regs.reset(size_t(exception));
|
||||
ABI_PushRegistersAndAdjustStack(code, 0, regs);
|
||||
}
|
||||
|
||||
void ABI_PopCallerSaveRegistersAndAdjustStackExcept(BlockOfCode& code, const HostLoc exception) {
|
||||
std::bitset<32> regs = ABI_ALL_CALLER_SAVE;
|
||||
auto regs = ABI_ALL_CALLER_SAVE;
|
||||
regs.reset(size_t(exception));
|
||||
ABI_PopRegistersAndAdjustStack(code, 0, regs);
|
||||
}
|
||||
|
||||
@@ -6,23 +6,23 @@
|
||||
* SPDX-License-Identifier: 0BSD
|
||||
*/
|
||||
|
||||
#include "dynarmic/backend/x64/constant_pool.h"
|
||||
|
||||
#include <cstring>
|
||||
|
||||
#include "common/assert.h"
|
||||
|
||||
#include "dynarmic/backend/x64/block_of_code.h"
|
||||
#include "dynarmic/backend/x64/constant_pool.h"
|
||||
|
||||
namespace Dynarmic::Backend::X64 {
|
||||
|
||||
ConstantPool::ConstantPool(BlockOfCode& code, size_t size)
|
||||
: code(code), insertion_point(0) {
|
||||
: code(code)
|
||||
, insertion_point(0)
|
||||
{
|
||||
code.EnsureMemoryCommitted(align_size + size);
|
||||
code.int3();
|
||||
code.align(align_size);
|
||||
pool = std::span<ConstantT>(
|
||||
reinterpret_cast<ConstantT*>(code.AllocateFromCodeSpace(size)), size / align_size);
|
||||
pool = std::span<ConstantT>(reinterpret_cast<ConstantT*>(code.AllocateFromCodeSpace(size)), size / align_size);
|
||||
}
|
||||
|
||||
Xbyak::Address ConstantPool::GetConstant(const Xbyak::AddressFrame& frame, u64 lower, u64 upper) {
|
||||
|
||||
@@ -8,8 +8,6 @@
|
||||
#pragma once
|
||||
|
||||
#include <bitset>
|
||||
#include <xbyak/xbyak.h>
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/common_types.h"
|
||||
#include "dynarmic/backend/x64/xbyak.h"
|
||||
|
||||
@@ -3,13 +3,11 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
|
||||
// TODO: Defining this crashes e v e r y t h i n g
|
||||
// #define XBYAK_STD_UNORDERED_SET ankerl::unordered_dense::set
|
||||
// #define XBYAK_STD_UNORDERED_MAP ankerl::unordered_dense::map
|
||||
// #define XBYAK_STD_UNORDERED_MULTIMAP boost::unordered_multimap
|
||||
|
||||
// You must ensure this matches with src/common/x64/xbyak.h on root dir
|
||||
#include <ankerl/unordered_dense.h>
|
||||
#include <boost/unordered_map.hpp>
|
||||
#define XBYAK_STD_UNORDERED_SET ankerl::unordered_dense::set
|
||||
#define XBYAK_STD_UNORDERED_MAP ankerl::unordered_dense::map
|
||||
#define XBYAK_STD_UNORDERED_MULTIMAP boost::unordered_multimap
|
||||
#include <xbyak/xbyak.h>
|
||||
#include <xbyak/xbyak_util.h>
|
||||
|
||||
@@ -78,7 +78,7 @@ u64 FPToFixed(size_t ibits, FPT op, size_t fbits, bool unsigned_, FPCR fpcr, Rou
|
||||
}
|
||||
|
||||
// Detect Overflow
|
||||
const int min_exponent_for_overflow = static_cast<int>(ibits) - static_cast<int>(mcl::bit::highest_set_bit(value.mantissa + (round_up ? Safe::LogicalShiftRight<u64>(1, exponent) : 0))) - (unsigned_ ? 0 : 1);
|
||||
const int min_exponent_for_overflow = int(ibits) - int(mcl::bit::highest_set_bit(value.mantissa + (round_up ? Safe::LogicalShiftRight<u64>(1, exponent) : 0))) - (unsigned_ ? 0 : 1);
|
||||
if (exponent >= min_exponent_for_overflow) {
|
||||
// Positive overflow
|
||||
if (unsigned_ || !sign) {
|
||||
@@ -87,10 +87,10 @@ u64 FPToFixed(size_t ibits, FPT op, size_t fbits, bool unsigned_, FPCR fpcr, Rou
|
||||
}
|
||||
|
||||
// Negative overflow
|
||||
const u64 min_value = Safe::Negate<u64>(static_cast<u64>(1) << (ibits - 1));
|
||||
const u64 min_value = Safe::Negate<u64>(u64(1) << (ibits - 1));
|
||||
if (!(exponent == min_exponent_for_overflow && int_result == min_value)) {
|
||||
FPProcessException(FPExc::InvalidOp, fpcr, fpsr);
|
||||
return static_cast<u64>(1) << (ibits - 1);
|
||||
return u64(1) << (ibits - 1);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -26,6 +26,7 @@ void EmitSpinLockLock(Xbyak::CodeGenerator& code, Xbyak::Reg64 ptr, Xbyak::Reg32
|
||||
Xbyak::Label start, loop;
|
||||
code.jmp(start, code.T_NEAR);
|
||||
code.L(loop);
|
||||
|
||||
if (waitpkg) {
|
||||
// TODO: this is because we lack regalloc - so better to be safe :(
|
||||
code.push(Xbyak::util::rax);
|
||||
@@ -33,7 +34,14 @@ void EmitSpinLockLock(Xbyak::CodeGenerator& code, Xbyak::Reg64 ptr, Xbyak::Reg32
|
||||
code.push(Xbyak::util::rdx);
|
||||
// TODO: This clobbers EAX and EDX did we tell the regalloc?
|
||||
// ARM ptr for address-monitoring
|
||||
code.umonitor(ptr);
|
||||
|
||||
// XBYAK BUG: code.umonitor(ptr); see issue #255
|
||||
// replace once xbyak has been fixed
|
||||
code.db(0xF3);
|
||||
if (ptr.getIdx() >= 8) code.db(0x41);
|
||||
code.db(0x0F); code.db(0xAE);
|
||||
code.db(uint8_t((3 << 6) | ((6 & 7) << 3) | (ptr.getIdx() & 7)));
|
||||
|
||||
// tmp.bit[0] = 0: C0.1 | Slow Wakup | Better Savings
|
||||
// tmp.bit[0] = 1: C0.2 | Fast Wakup | Lesser Savings
|
||||
// edx:eax is implicitly used as a 64-bit deadline timestamp
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
/* This file is part of the dynarmic project.
|
||||
* Copyright (c) 2020 MerryMage
|
||||
* SPDX-License-Identifier: 0BSD
|
||||
@@ -11,7 +14,7 @@
|
||||
#include <utility>
|
||||
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
#include <xbyak/xbyak_util.h>
|
||||
#include "dynarmic/backend/x64/xbyak.h"
|
||||
|
||||
TEST_CASE("Host CPU supports", "[a64]") {
|
||||
using Cpu = Xbyak::util::Cpu;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
@@ -108,10 +108,10 @@ void EmulatedController::ReloadFromSettings() {
|
||||
// Other or debug controller should always be a pro controller
|
||||
if (npad_id_type != NpadIdType::Other) {
|
||||
SetNpadStyleIndex(MapSettingsTypeToNPad(player.controller_type));
|
||||
original_npad_type = npad_type;
|
||||
original_npad_type = npad_type.load();
|
||||
} else {
|
||||
SetNpadStyleIndex(NpadStyleIndex::Fullkey);
|
||||
original_npad_type = npad_type;
|
||||
original_npad_type = npad_type.load();
|
||||
}
|
||||
|
||||
// Disable special features before disconnecting
|
||||
@@ -577,10 +577,9 @@ void EmulatedController::UnloadInput() {
|
||||
}
|
||||
|
||||
void EmulatedController::EnableConfiguration() {
|
||||
std::unique_lock lock1{connect_mutex}, lock2{npad_mutex};
|
||||
is_configuring = true;
|
||||
tmp_is_connected = is_connected;
|
||||
tmp_npad_type = npad_type;
|
||||
is_configuring.store(true);
|
||||
tmp_is_connected.store(is_connected);
|
||||
tmp_npad_type.store(npad_type);
|
||||
}
|
||||
|
||||
void EmulatedController::DisableConfiguration() {
|
||||
@@ -600,7 +599,7 @@ void EmulatedController::DisableConfiguration() {
|
||||
Disconnect();
|
||||
}
|
||||
SetNpadStyleIndex(tmp_npad_type);
|
||||
original_npad_type = tmp_npad_type;
|
||||
original_npad_type.store(tmp_npad_type);
|
||||
}
|
||||
|
||||
// Apply temporary connected status to the real controller
|
||||
@@ -614,19 +613,16 @@ void EmulatedController::DisableConfiguration() {
|
||||
}
|
||||
|
||||
void EmulatedController::EnableSystemButtons() {
|
||||
std::unique_lock lock{mutex};
|
||||
system_buttons_enabled = true;
|
||||
}
|
||||
|
||||
void EmulatedController::DisableSystemButtons() {
|
||||
std::unique_lock lock{mutex};
|
||||
system_buttons_enabled = false;
|
||||
controller.home_button_state.raw = 0;
|
||||
controller.capture_button_state.raw = 0;
|
||||
}
|
||||
|
||||
void EmulatedController::ResetSystemButtons() {
|
||||
std::unique_lock lock{mutex};
|
||||
controller.home_button_state.home.Assign(false);
|
||||
controller.capture_button_state.capture.Assign(false);
|
||||
}
|
||||
@@ -763,8 +759,7 @@ void EmulatedController::StartMotionCalibration() {
|
||||
}
|
||||
}
|
||||
|
||||
void EmulatedController::SetButton(const Common::Input::CallbackStatus& callback, std::size_t index,
|
||||
Common::UUID uuid) {
|
||||
void EmulatedController::SetButton(const Common::Input::CallbackStatus& callback, std::size_t index, Common::UUID uuid) {
|
||||
const auto player_index = Service::HID::NpadIdTypeToIndex(npad_id_type);
|
||||
const auto& player = Settings::values.players.GetValue()[player_index];
|
||||
|
||||
@@ -772,7 +767,6 @@ void EmulatedController::SetButton(const Common::Input::CallbackStatus& callback
|
||||
return;
|
||||
}
|
||||
|
||||
std::unique_lock lock{mutex};
|
||||
bool value_changed = false;
|
||||
const auto new_status = TransformToButton(callback);
|
||||
auto& current_status = controller.button_values[index];
|
||||
@@ -818,7 +812,6 @@ void EmulatedController::SetButton(const Common::Input::CallbackStatus& callback
|
||||
controller.debug_pad_button_state.raw = 0;
|
||||
controller.home_button_state.raw = 0;
|
||||
controller.capture_button_state.raw = 0;
|
||||
lock.unlock();
|
||||
TriggerOnChange(ControllerTriggerType::Button, false);
|
||||
return;
|
||||
}
|
||||
@@ -922,8 +915,6 @@ void EmulatedController::SetButton(const Common::Input::CallbackStatus& callback
|
||||
break;
|
||||
}
|
||||
|
||||
lock.unlock();
|
||||
|
||||
if (!is_connected) {
|
||||
if (npad_type == NpadStyleIndex::Handheld) {
|
||||
if (npad_id_type == NpadIdType::Handheld) {
|
||||
@@ -952,7 +943,6 @@ void EmulatedController::SetStick(const Common::Input::CallbackStatus& callback,
|
||||
auto trigger_guard = SCOPE_GUARD {
|
||||
TriggerOnChange(ControllerTriggerType::Stick, !is_configuring);
|
||||
};
|
||||
std::unique_lock lock{mutex};
|
||||
const auto stick_value = TransformToStick(callback);
|
||||
|
||||
// Only read stick values that have the same uuid or are over the threshold to avoid flapping
|
||||
@@ -1009,7 +999,6 @@ void EmulatedController::SetTrigger(const Common::Input::CallbackStatus& callbac
|
||||
auto trigger_guard = SCOPE_GUARD {
|
||||
TriggerOnChange(ControllerTriggerType::Trigger, !is_configuring);
|
||||
};
|
||||
std::unique_lock lock{mutex};
|
||||
const auto trigger_value = TransformToTrigger(callback);
|
||||
|
||||
// Only read trigger values that have the same uuid or are pressed once
|
||||
@@ -1057,7 +1046,6 @@ void EmulatedController::SetMotion(const Common::Input::CallbackStatus& callback
|
||||
SCOPE_EXIT {
|
||||
TriggerOnChange(ControllerTriggerType::Motion, !is_configuring);
|
||||
};
|
||||
std::unique_lock lock{mutex};
|
||||
auto& raw_status = controller.motion_values[index].raw_status;
|
||||
auto& emulated = controller.motion_values[index].emulated;
|
||||
|
||||
@@ -1093,7 +1081,6 @@ void EmulatedController::SetColors(const Common::Input::CallbackStatus& callback
|
||||
auto trigger_guard = SCOPE_GUARD {
|
||||
TriggerOnChange(ControllerTriggerType::Color, !is_configuring);
|
||||
};
|
||||
std::unique_lock lock{mutex};
|
||||
controller.color_values[index] = TransformToColor(callback);
|
||||
|
||||
if (is_configuring) {
|
||||
@@ -1136,15 +1123,13 @@ void EmulatedController::SetColors(const Common::Input::CallbackStatus& callback
|
||||
}
|
||||
}
|
||||
|
||||
void EmulatedController::SetBattery(const Common::Input::CallbackStatus& callback,
|
||||
std::size_t index) {
|
||||
void EmulatedController::SetBattery(const Common::Input::CallbackStatus& callback, std::size_t index) {
|
||||
if (index >= controller.battery_values.size()) {
|
||||
return;
|
||||
}
|
||||
SCOPE_EXIT {
|
||||
TriggerOnChange(ControllerTriggerType::Battery, !is_configuring);
|
||||
};
|
||||
std::unique_lock lock{mutex};
|
||||
controller.battery_values[index] = TransformToBattery(callback);
|
||||
|
||||
if (is_configuring) {
|
||||
@@ -1209,47 +1194,33 @@ void EmulatedController::SetCamera(const Common::Input::CallbackStatus& callback
|
||||
SCOPE_EXIT {
|
||||
TriggerOnChange(ControllerTriggerType::IrSensor, !is_configuring);
|
||||
};
|
||||
std::unique_lock lock{mutex};
|
||||
controller.camera_values = TransformToCamera(callback);
|
||||
|
||||
if (is_configuring) {
|
||||
return;
|
||||
if (!is_configuring) {
|
||||
controller.camera_state.sample++;
|
||||
controller.camera_state.format = Core::IrSensor::ImageTransferProcessorFormat(controller.camera_values.format);
|
||||
controller.camera_state.data = controller.camera_values.data;
|
||||
}
|
||||
|
||||
controller.camera_state.sample++;
|
||||
controller.camera_state.format =
|
||||
static_cast<Core::IrSensor::ImageTransferProcessorFormat>(controller.camera_values.format);
|
||||
controller.camera_state.data = controller.camera_values.data;
|
||||
}
|
||||
|
||||
void EmulatedController::SetRingAnalog(const Common::Input::CallbackStatus& callback) {
|
||||
SCOPE_EXIT {
|
||||
TriggerOnChange(ControllerTriggerType::RingController, !is_configuring);
|
||||
};
|
||||
std::unique_lock lock{mutex};
|
||||
const auto force_value = TransformToStick(callback);
|
||||
|
||||
controller.ring_analog_value = force_value.x;
|
||||
|
||||
if (is_configuring) {
|
||||
return;
|
||||
if (!is_configuring) {
|
||||
controller.ring_analog_state.force = force_value.x.value;
|
||||
}
|
||||
|
||||
controller.ring_analog_state.force = force_value.x.value;
|
||||
}
|
||||
|
||||
void EmulatedController::SetNfc(const Common::Input::CallbackStatus& callback) {
|
||||
SCOPE_EXIT {
|
||||
TriggerOnChange(ControllerTriggerType::Nfc, !is_configuring);
|
||||
};
|
||||
std::unique_lock lock{mutex};
|
||||
controller.nfc_values = TransformToNfc(callback);
|
||||
|
||||
if (is_configuring) {
|
||||
return;
|
||||
if (!is_configuring) {
|
||||
controller.nfc_state = controller.nfc_values;
|
||||
}
|
||||
|
||||
controller.nfc_state = controller.nfc_values;
|
||||
}
|
||||
|
||||
bool EmulatedController::SetVibration(bool should_vibrate) {
|
||||
@@ -1258,7 +1229,6 @@ bool EmulatedController::SetVibration(bool should_vibrate) {
|
||||
vibration_value.high_amplitude = 1.0f;
|
||||
vibration_value.low_amplitude = 1.0f;
|
||||
}
|
||||
|
||||
return SetVibration(DeviceIndex::Left, vibration_value);
|
||||
}
|
||||
|
||||
@@ -1268,7 +1238,6 @@ bool EmulatedController::SetVibration(u32 slot, Core::HID::VibrationGcErmCommand
|
||||
vibration_value.high_amplitude = 1.0f;
|
||||
vibration_value.low_amplitude = 1.0f;
|
||||
}
|
||||
|
||||
return SetVibration(DeviceIndex::Left, vibration_value);
|
||||
}
|
||||
|
||||
@@ -1632,7 +1601,7 @@ void EmulatedController::SetSupportedNpadStyleTag(NpadStyleTag supported_styles)
|
||||
// Attempt to reconnect with the original type
|
||||
if (npad_type != original_npad_type) {
|
||||
Disconnect();
|
||||
const auto current_npad_type = npad_type;
|
||||
const auto current_npad_type = npad_type.load();
|
||||
SetNpadStyleIndex(original_npad_type);
|
||||
if (IsControllerSupported()) {
|
||||
Connect();
|
||||
@@ -1650,7 +1619,7 @@ void EmulatedController::SetSupportedNpadStyleTag(NpadStyleTag supported_styles)
|
||||
|
||||
// Fallback Fullkey controllers to Pro controllers
|
||||
if (IsControllerFullkey() && supported_style_tag.fullkey) {
|
||||
LOG_WARNING(Service_HID, "Reconnecting controller type {} as Pro controller", npad_type);
|
||||
LOG_WARNING(Service_HID, "Reconnecting controller type {} as Pro controller", npad_type.load());
|
||||
SetNpadStyleIndex(NpadStyleIndex::Fullkey);
|
||||
Connect();
|
||||
return;
|
||||
@@ -1658,7 +1627,7 @@ void EmulatedController::SetSupportedNpadStyleTag(NpadStyleTag supported_styles)
|
||||
|
||||
// Fallback Dual joycon controllers to Pro controllers
|
||||
if (npad_type == NpadStyleIndex::JoyconDual && supported_style_tag.fullkey) {
|
||||
LOG_WARNING(Service_HID, "Reconnecting controller type {} as Pro controller", npad_type);
|
||||
LOG_WARNING(Service_HID, "Reconnecting controller type {} as Pro controller", npad_type.load());
|
||||
SetNpadStyleIndex(NpadStyleIndex::Fullkey);
|
||||
Connect();
|
||||
return;
|
||||
@@ -1666,19 +1635,16 @@ void EmulatedController::SetSupportedNpadStyleTag(NpadStyleTag supported_styles)
|
||||
|
||||
// Fallback Pro controllers to Dual joycon
|
||||
if (npad_type == NpadStyleIndex::Fullkey && supported_style_tag.joycon_dual) {
|
||||
LOG_WARNING(Service_HID, "Reconnecting controller type {} as Dual Joycons", npad_type);
|
||||
LOG_WARNING(Service_HID, "Reconnecting controller type {} as Dual Joycons", npad_type.load());
|
||||
SetNpadStyleIndex(NpadStyleIndex::JoyconDual);
|
||||
Connect();
|
||||
return;
|
||||
}
|
||||
|
||||
LOG_ERROR(Service_HID, "Controller type {} is not supported. Disconnecting controller",
|
||||
npad_type);
|
||||
LOG_ERROR(Service_HID, "Controller type {} is not supported. Disconnecting controller", npad_type.load());
|
||||
}
|
||||
|
||||
bool EmulatedController::IsControllerFullkey(bool use_temporary_value) const {
|
||||
std::unique_lock lock{mutex};
|
||||
const auto type = is_configuring && use_temporary_value ? tmp_npad_type : npad_type;
|
||||
const auto type = is_configuring.load() && use_temporary_value ? tmp_npad_type.load() : npad_type.load();
|
||||
switch (type) {
|
||||
case NpadStyleIndex::Fullkey:
|
||||
case NpadStyleIndex::GameCube:
|
||||
@@ -1693,39 +1659,26 @@ bool EmulatedController::IsControllerFullkey(bool use_temporary_value) const {
|
||||
}
|
||||
|
||||
bool EmulatedController::IsControllerSupported(bool use_temporary_value) const {
|
||||
std::unique_lock lock{mutex};
|
||||
const auto type = is_configuring && use_temporary_value ? tmp_npad_type : npad_type;
|
||||
const auto type = is_configuring.load() && use_temporary_value ? tmp_npad_type.load() : npad_type.load();
|
||||
switch (type) {
|
||||
case NpadStyleIndex::Fullkey:
|
||||
return supported_style_tag.fullkey.As<bool>();
|
||||
case NpadStyleIndex::Handheld:
|
||||
return supported_style_tag.handheld.As<bool>();
|
||||
case NpadStyleIndex::JoyconDual:
|
||||
return supported_style_tag.joycon_dual.As<bool>();
|
||||
case NpadStyleIndex::JoyconLeft:
|
||||
return supported_style_tag.joycon_left.As<bool>();
|
||||
case NpadStyleIndex::JoyconRight:
|
||||
return supported_style_tag.joycon_right.As<bool>();
|
||||
case NpadStyleIndex::GameCube:
|
||||
return supported_style_tag.gamecube.As<bool>();
|
||||
case NpadStyleIndex::Pokeball:
|
||||
return supported_style_tag.palma.As<bool>();
|
||||
case NpadStyleIndex::NES:
|
||||
return supported_style_tag.lark.As<bool>();
|
||||
case NpadStyleIndex::SNES:
|
||||
return supported_style_tag.lucia.As<bool>();
|
||||
case NpadStyleIndex::N64:
|
||||
return supported_style_tag.lagoon.As<bool>();
|
||||
case NpadStyleIndex::SegaGenesis:
|
||||
return supported_style_tag.lager.As<bool>();
|
||||
default:
|
||||
return false;
|
||||
case NpadStyleIndex::Fullkey: return supported_style_tag.fullkey.As<bool>();
|
||||
case NpadStyleIndex::Handheld: return supported_style_tag.handheld.As<bool>();
|
||||
case NpadStyleIndex::JoyconDual: return supported_style_tag.joycon_dual.As<bool>();
|
||||
case NpadStyleIndex::JoyconLeft: return supported_style_tag.joycon_left.As<bool>();
|
||||
case NpadStyleIndex::JoyconRight: return supported_style_tag.joycon_right.As<bool>();
|
||||
case NpadStyleIndex::GameCube: return supported_style_tag.gamecube.As<bool>();
|
||||
case NpadStyleIndex::Pokeball: return supported_style_tag.palma.As<bool>();
|
||||
case NpadStyleIndex::NES: return supported_style_tag.lark.As<bool>();
|
||||
case NpadStyleIndex::SNES: return supported_style_tag.lucia.As<bool>();
|
||||
case NpadStyleIndex::N64: return supported_style_tag.lagoon.As<bool>();
|
||||
case NpadStyleIndex::SegaGenesis: return supported_style_tag.lager.As<bool>();
|
||||
default: return false;
|
||||
}
|
||||
}
|
||||
|
||||
void EmulatedController::Connect(bool use_temporary_value) {
|
||||
if (!IsControllerSupported(use_temporary_value)) {
|
||||
const auto type = is_configuring && use_temporary_value ? tmp_npad_type : npad_type;
|
||||
const auto type = is_configuring.load() && use_temporary_value ? tmp_npad_type.load() : npad_type.load();
|
||||
LOG_ERROR(Service_HID, "Controller type {} is not supported", type);
|
||||
return;
|
||||
}
|
||||
@@ -1733,12 +1686,10 @@ void EmulatedController::Connect(bool use_temporary_value) {
|
||||
auto trigger_guard = SCOPE_GUARD {
|
||||
TriggerOnChange(ControllerTriggerType::Connected, !is_configuring);
|
||||
};
|
||||
std::unique_lock lock1{connect_mutex}, lock2{mutex};
|
||||
if (is_configuring) {
|
||||
tmp_is_connected = true;
|
||||
return;
|
||||
}
|
||||
|
||||
if (is_connected) {
|
||||
trigger_guard.Cancel();
|
||||
return;
|
||||
@@ -1750,12 +1701,10 @@ void EmulatedController::Disconnect() {
|
||||
auto trigger_guard = SCOPE_GUARD {
|
||||
TriggerOnChange(ControllerTriggerType::Disconnected, !is_configuring);
|
||||
};
|
||||
std::unique_lock lock1{connect_mutex}, lock2{mutex};
|
||||
if (is_configuring) {
|
||||
tmp_is_connected = false;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!is_connected) {
|
||||
trigger_guard.Cancel();
|
||||
return;
|
||||
@@ -1764,19 +1713,16 @@ void EmulatedController::Disconnect() {
|
||||
}
|
||||
|
||||
bool EmulatedController::IsConnected(bool get_temporary_value) const {
|
||||
std::shared_lock lock{connect_mutex};
|
||||
if (get_temporary_value && is_configuring)
|
||||
return tmp_is_connected;
|
||||
return is_connected;
|
||||
}
|
||||
|
||||
NpadIdType EmulatedController::GetNpadIdType() const {
|
||||
std::shared_lock lock{mutex};
|
||||
return npad_id_type;
|
||||
}
|
||||
|
||||
NpadStyleIndex EmulatedController::GetNpadStyleIndex(bool get_temporary_value) const {
|
||||
std::shared_lock lock{npad_mutex};
|
||||
if (get_temporary_value && is_configuring)
|
||||
return tmp_npad_type;
|
||||
return npad_type;
|
||||
@@ -1786,8 +1732,6 @@ void EmulatedController::SetNpadStyleIndex(NpadStyleIndex npad_type_) {
|
||||
auto trigger_guard = SCOPE_GUARD {
|
||||
TriggerOnChange(ControllerTriggerType::Type, !is_configuring);
|
||||
};
|
||||
std::unique_lock lock1{mutex}, lock2{npad_mutex};
|
||||
|
||||
if (is_configuring) {
|
||||
if (tmp_npad_type == npad_type_) {
|
||||
trigger_guard.Cancel();
|
||||
@@ -1796,14 +1740,12 @@ void EmulatedController::SetNpadStyleIndex(NpadStyleIndex npad_type_) {
|
||||
tmp_npad_type = npad_type_;
|
||||
return;
|
||||
}
|
||||
|
||||
if (npad_type == npad_type_) {
|
||||
trigger_guard.Cancel();
|
||||
return;
|
||||
}
|
||||
if (is_connected) {
|
||||
LOG_WARNING(Service_HID, "Controller {} type changed while it's connected",
|
||||
Service::HID::NpadIdTypeToIndex(npad_id_type));
|
||||
LOG_WARNING(Service_HID, "Controller {} type changed while it's connected", Service::HID::NpadIdTypeToIndex(npad_id_type));
|
||||
}
|
||||
npad_type = npad_type_;
|
||||
}
|
||||
@@ -1832,37 +1774,30 @@ LedPattern EmulatedController::GetLedPattern() const {
|
||||
}
|
||||
|
||||
ButtonValues EmulatedController::GetButtonsValues() const {
|
||||
std::unique_lock lock{mutex};
|
||||
return controller.button_values;
|
||||
}
|
||||
|
||||
SticksValues EmulatedController::GetSticksValues() const {
|
||||
std::unique_lock lock{mutex};
|
||||
return controller.stick_values;
|
||||
}
|
||||
|
||||
TriggerValues EmulatedController::GetTriggersValues() const {
|
||||
std::unique_lock lock{mutex};
|
||||
return controller.trigger_values;
|
||||
}
|
||||
|
||||
ControllerMotionValues EmulatedController::GetMotionValues() const {
|
||||
std::unique_lock lock{mutex};
|
||||
return controller.motion_values;
|
||||
}
|
||||
|
||||
ColorValues EmulatedController::GetColorsValues() const {
|
||||
std::unique_lock lock{mutex};
|
||||
return controller.color_values;
|
||||
}
|
||||
|
||||
BatteryValues EmulatedController::GetBatteryValues() const {
|
||||
std::unique_lock lock{mutex};
|
||||
return controller.battery_values;
|
||||
}
|
||||
|
||||
CameraValues EmulatedController::GetCameraValues() const {
|
||||
std::unique_lock lock{mutex};
|
||||
return controller.camera_values;
|
||||
}
|
||||
|
||||
@@ -1871,72 +1806,54 @@ RingAnalogValue EmulatedController::GetRingSensorValues() const {
|
||||
}
|
||||
|
||||
HomeButtonState EmulatedController::GetHomeButtons() const {
|
||||
std::unique_lock lock{mutex};
|
||||
if (is_configuring) {
|
||||
if (is_configuring)
|
||||
return {};
|
||||
}
|
||||
return controller.home_button_state;
|
||||
}
|
||||
|
||||
CaptureButtonState EmulatedController::GetCaptureButtons() const {
|
||||
std::unique_lock lock{mutex};
|
||||
if (is_configuring) {
|
||||
if (is_configuring)
|
||||
return {};
|
||||
}
|
||||
return controller.capture_button_state;
|
||||
}
|
||||
|
||||
NpadButtonState EmulatedController::GetNpadButtons() const {
|
||||
std::unique_lock lock{mutex};
|
||||
if (is_configuring) {
|
||||
if (is_configuring)
|
||||
return {};
|
||||
}
|
||||
return {controller.npad_button_state.raw & GetTurboButtonMask()};
|
||||
}
|
||||
|
||||
DebugPadButton EmulatedController::GetDebugPadButtons() const {
|
||||
std::unique_lock lock{mutex};
|
||||
if (is_configuring) {
|
||||
if (is_configuring)
|
||||
return {};
|
||||
}
|
||||
return controller.debug_pad_button_state;
|
||||
}
|
||||
|
||||
AnalogSticks EmulatedController::GetSticks() const {
|
||||
std::unique_lock lock{mutex};
|
||||
|
||||
if (is_configuring) {
|
||||
if (is_configuring)
|
||||
return {};
|
||||
}
|
||||
|
||||
return controller.analog_stick_state;
|
||||
}
|
||||
|
||||
NpadGcTriggerState EmulatedController::GetTriggers() const {
|
||||
std::unique_lock lock{mutex};
|
||||
if (is_configuring) {
|
||||
if (is_configuring)
|
||||
return {};
|
||||
}
|
||||
return controller.gc_trigger_state;
|
||||
}
|
||||
|
||||
MotionState EmulatedController::GetMotions() const {
|
||||
std::unique_lock lock{mutex};
|
||||
return controller.motion_state;
|
||||
}
|
||||
|
||||
ControllerColors EmulatedController::GetColors() const {
|
||||
std::unique_lock lock{mutex};
|
||||
return controller.colors_state;
|
||||
}
|
||||
|
||||
BatteryLevelState EmulatedController::GetBattery() const {
|
||||
std::unique_lock lock{mutex};
|
||||
return controller.battery_state;
|
||||
}
|
||||
|
||||
const CameraState& EmulatedController::GetCamera() const {
|
||||
std::unique_lock lock{mutex};
|
||||
return controller.camera_state;
|
||||
}
|
||||
|
||||
@@ -1945,15 +1862,14 @@ RingSensorForce EmulatedController::GetRingSensorForce() const {
|
||||
}
|
||||
|
||||
const NfcState& EmulatedController::GetNfc() const {
|
||||
std::unique_lock lock{mutex};
|
||||
return controller.nfc_state;
|
||||
}
|
||||
|
||||
NpadColor EmulatedController::GetNpadColor(u32 color) {
|
||||
return {
|
||||
.r = static_cast<u8>((color >> 16) & 0xFF),
|
||||
.g = static_cast<u8>((color >> 8) & 0xFF),
|
||||
.b = static_cast<u8>(color & 0xFF),
|
||||
.r = u8((color >> 16) & 0xFF),
|
||||
.g = u8((color >> 8) & 0xFF),
|
||||
.b = u8(color & 0xFF),
|
||||
.a = 0xff,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -11,8 +11,10 @@
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <shared_mutex>
|
||||
#include <ankerl/unordered_dense.h>
|
||||
#include <vector>
|
||||
#include <atomic>
|
||||
|
||||
#include <ankerl/unordered_dense.h>
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "common/input.h"
|
||||
@@ -576,13 +578,7 @@ private:
|
||||
NpadButton GetTurboButtonMask() const;
|
||||
|
||||
const NpadIdType npad_id_type;
|
||||
NpadStyleIndex npad_type{NpadStyleIndex::None};
|
||||
NpadStyleIndex original_npad_type{NpadStyleIndex::None};
|
||||
NpadStyleTag supported_style_tag{NpadStyleSet::All};
|
||||
bool is_connected{false};
|
||||
bool is_configuring{false};
|
||||
bool is_initialized{false};
|
||||
bool system_buttons_enabled{true};
|
||||
f32 motion_sensitivity{Core::HID::MotionInput::IsAtRestStandard};
|
||||
u32 turbo_button_state{0};
|
||||
std::size_t nfc_handles{0};
|
||||
@@ -591,9 +587,16 @@ private:
|
||||
std::array<std::chrono::steady_clock::time_point, 2> last_vibration_timepoint{};
|
||||
std::array<bool, HIDCore::available_controllers> controller_connected{};
|
||||
|
||||
// Atomically synched values
|
||||
std::atomic<HID::NpadStyleIndex> npad_type{HID::NpadStyleIndex::None};
|
||||
std::atomic<HID::NpadStyleIndex> original_npad_type{HID::NpadStyleIndex::None};
|
||||
// Temporary values to avoid doing changes while the controller is in configuring mode
|
||||
NpadStyleIndex tmp_npad_type{NpadStyleIndex::None};
|
||||
bool tmp_is_connected{false};
|
||||
std::atomic<HID::NpadStyleIndex> tmp_npad_type{HID::NpadStyleIndex::None};
|
||||
std::atomic<bool> tmp_is_connected{false};
|
||||
std::atomic<bool> is_connected{false};
|
||||
std::atomic<bool> is_configuring{false};
|
||||
std::atomic<bool> is_initialized{false};
|
||||
std::atomic<bool> system_buttons_enabled{true};
|
||||
|
||||
ButtonParams button_params;
|
||||
StickParams stick_params;
|
||||
@@ -632,10 +635,7 @@ private:
|
||||
StickDevices virtual_stick_devices;
|
||||
ControllerMotionDevices virtual_motion_devices;
|
||||
|
||||
mutable std::shared_mutex mutex;
|
||||
mutable std::shared_mutex callback_mutex;
|
||||
mutable std::shared_mutex npad_mutex;
|
||||
mutable std::shared_mutex connect_mutex;
|
||||
mutable std::mutex callback_mutex;
|
||||
ankerl::unordered_dense::map<int, ControllerUpdateCallback> callback_list;
|
||||
int last_callback_key = 0;
|
||||
|
||||
|
||||
@@ -221,12 +221,17 @@ private:
|
||||
Id Texture(EmitContext& ctx, IR::TextureInstInfo info, [[maybe_unused]] const IR::Value& index) {
|
||||
const TextureDefinition& def{ctx.textures.at(info.descriptor_index)};
|
||||
if (def.count > 1) {
|
||||
const DescriptorIndex idx{ctx, index};
|
||||
const Id pointer{ctx.OpAccessChain(def.pointer_type, def.id, idx.Value())};
|
||||
idx.Decorate(ctx, pointer);
|
||||
const Id object{ctx.OpLoad(def.sampled_type, pointer)};
|
||||
idx.Decorate(ctx, object);
|
||||
return object;
|
||||
if (Settings::values.legacy_descriptor_indices.GetValue()) {
|
||||
const Id pointer{ctx.OpAccessChain(def.pointer_type, def.id, ctx.Def(index))};
|
||||
return ctx.OpLoad(def.sampled_type, pointer);
|
||||
} else {
|
||||
const DescriptorIndex idx{ctx, index};
|
||||
const Id pointer{ctx.OpAccessChain(def.pointer_type, def.id, idx.Value())};
|
||||
idx.Decorate(ctx, pointer);
|
||||
const Id object{ctx.OpLoad(def.sampled_type, pointer)};
|
||||
idx.Decorate(ctx, object);
|
||||
return object;
|
||||
}
|
||||
} else {
|
||||
return ctx.OpLoad(def.sampled_type, def.id);
|
||||
}
|
||||
@@ -244,14 +249,20 @@ Id TextureImage(EmitContext& ctx, IR::TextureInstInfo info, const IR::Value& ind
|
||||
} else {
|
||||
const TextureDefinition& def{ctx.textures.at(info.descriptor_index)};
|
||||
if (def.count > 1) {
|
||||
const DescriptorIndex idx{ctx, index};
|
||||
const Id ptr{ctx.OpAccessChain(def.pointer_type, def.id, idx.Value())};
|
||||
idx.Decorate(ctx, ptr);
|
||||
const Id object{ctx.OpLoad(def.sampled_type, ptr)};
|
||||
idx.Decorate(ctx, object);
|
||||
const Id image{ctx.OpImage(def.image_type, object)};
|
||||
idx.Decorate(ctx, image);
|
||||
return image;
|
||||
if (Settings::values.legacy_descriptor_indices.GetValue()) {
|
||||
const Id idx{index.IsImmediate() ? ctx.Const(index.U32()) : ctx.Def(index)};
|
||||
const Id ptr{ctx.OpAccessChain(def.pointer_type, def.id, idx)};
|
||||
return ctx.OpImage(def.image_type, ctx.OpLoad(def.sampled_type, ptr));
|
||||
} else {
|
||||
const DescriptorIndex idx{ctx, index};
|
||||
const Id ptr{ctx.OpAccessChain(def.pointer_type, def.id, idx.Value())};
|
||||
idx.Decorate(ctx, ptr);
|
||||
const Id object{ctx.OpLoad(def.sampled_type, ptr)};
|
||||
idx.Decorate(ctx, object);
|
||||
const Id image{ctx.OpImage(def.image_type, object)};
|
||||
idx.Decorate(ctx, image);
|
||||
return image;
|
||||
}
|
||||
}
|
||||
return ctx.OpImage(def.image_type, ctx.OpLoad(def.sampled_type, def.id));
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
#include <limits>
|
||||
#include <boost/container/small_vector.hpp>
|
||||
|
||||
#include "common/settings.h"
|
||||
#include "shader_recompiler/environment.h"
|
||||
#include "shader_recompiler/frontend/ir/basic_block.h"
|
||||
#include "shader_recompiler/frontend/ir/breadth_first_search.h"
|
||||
@@ -461,7 +462,7 @@ std::optional<ConstBufferAddr> TryGetConstBuffer(const IR::Inst* inst, Environme
|
||||
.secondary_offset = 0,
|
||||
.secondary_shift_left = 0,
|
||||
.dynamic_offset = dynamic_offset,
|
||||
.count = DynamicDescriptorCount(base_offset, size_shift),
|
||||
.count = Settings::values.legacy_descriptor_indices.GetValue() ? 8 : DynamicDescriptorCount(base_offset, size_shift),
|
||||
.has_secondary = false,
|
||||
};
|
||||
}
|
||||
@@ -733,9 +734,10 @@ void TexturePass(Environment& env, IR::Program& program, const HostTranslateInfo
|
||||
break;
|
||||
}
|
||||
u32 index;
|
||||
const u32 size_shift{cbuf.count > 1 ? DynamicDescriptorSizeShift(cbuf.dynamic_offset)
|
||||
: DESCRIPTOR_SIZE_SHIFT};
|
||||
u32 count{cbuf.count};
|
||||
u32 size_shift = cbuf.count > 1 ? DynamicDescriptorSizeShift(cbuf.dynamic_offset) : DESCRIPTOR_SIZE_SHIFT;
|
||||
if (Settings::values.legacy_descriptor_indices.GetValue())
|
||||
size_shift = DESCRIPTOR_SIZE_SHIFT;
|
||||
u32 count = cbuf.count;
|
||||
switch (inst->GetOpcode()) {
|
||||
case IR::Opcode::ImageRead:
|
||||
case IR::Opcode::ImageAtomicIAdd32:
|
||||
@@ -821,8 +823,7 @@ void TexturePass(Environment& env, IR::Program& program, const HostTranslateInfo
|
||||
const auto insert_point{IR::Block::InstructionList::s_iterator_to(*inst)};
|
||||
IR::IREmitter ir{*texture_inst.block, insert_point};
|
||||
const IR::U32 shift{ir.Imm32(size_shift)};
|
||||
inst->SetArg(0, ir.UMin(ir.ShiftRightLogical(cbuf.dynamic_offset, shift),
|
||||
ir.Imm32(count - 1)));
|
||||
inst->SetArg(0, ir.UMin(ir.ShiftRightLogical(cbuf.dynamic_offset, shift), ir.Imm32(count - 1)));
|
||||
} else {
|
||||
inst->SetArg(0, IR::Value{});
|
||||
}
|
||||
|
||||
@@ -11,17 +11,9 @@
|
||||
|
||||
#include <fstream>
|
||||
#include <variant>
|
||||
|
||||
#ifdef ARCHITECTURE_x86_64
|
||||
// xbyak hates human beings
|
||||
#ifdef __GNUC__
|
||||
#pragma GCC diagnostic ignored "-Wconversion"
|
||||
#pragma GCC diagnostic ignored "-Wshadow"
|
||||
#endif
|
||||
#ifdef __clang__
|
||||
#pragma clang diagnostic ignored "-Wconversion"
|
||||
#pragma clang diagnostic ignored "-Wshadow"
|
||||
#endif
|
||||
#include <xbyak/xbyak.h>
|
||||
#include "common/x64/xbyak.h"
|
||||
#endif
|
||||
|
||||
#include "common/assert.h"
|
||||
@@ -39,10 +31,6 @@
|
||||
#include "common/assert.h"
|
||||
#include "common/bit_field.h"
|
||||
#include "common/logging.h"
|
||||
#ifdef ARCHITECTURE_x86_64
|
||||
#include "common/x64/xbyak_abi.h"
|
||||
#include "common/x64/xbyak_util.h"
|
||||
#endif
|
||||
#include "video_core/engines/maxwell_3d.h"
|
||||
|
||||
namespace Tegra {
|
||||
|
||||
@@ -217,6 +217,12 @@ FormatInfo SurfaceFormat(const Device& device, FormatType format_type, bool with
|
||||
SURFACE_FORMAT_ELEM(VK_FORMAT_ASTC_6x5_UNORM_BLOCK, 0, ASTC_2D_6X5_UNORM) \
|
||||
SURFACE_FORMAT_ELEM(VK_FORMAT_ASTC_6x5_SRGB_BLOCK, 0, ASTC_2D_6X5_SRGB) \
|
||||
SURFACE_FORMAT_ELEM(VK_FORMAT_E5B9G9R9_UFLOAT_PACK32, 0, E5B9G9R9_FLOAT) \
|
||||
SURFACE_FORMAT_ELEM(VK_FORMAT_ETC2_R8G8B8_UNORM_BLOCK, 0, ETC2_RGB_UNORM) \
|
||||
SURFACE_FORMAT_ELEM(VK_FORMAT_ETC2_R8G8B8A8_UNORM_BLOCK, 0, ETC2_RGBA_UNORM) \
|
||||
SURFACE_FORMAT_ELEM(VK_FORMAT_ETC2_R8G8B8A1_UNORM_BLOCK, 0, ETC2_RGB_PTA_UNORM) \
|
||||
SURFACE_FORMAT_ELEM(VK_FORMAT_ETC2_R8G8B8_SRGB_BLOCK, 0, ETC2_RGB_SRGB) \
|
||||
SURFACE_FORMAT_ELEM(VK_FORMAT_ETC2_R8G8B8A8_SRGB_BLOCK, 0, ETC2_RGBA_SRGB) \
|
||||
SURFACE_FORMAT_ELEM(VK_FORMAT_ETC2_R8G8B8A1_SRGB_BLOCK, 0, ETC2_RGB_PTA_SRGB) \
|
||||
/* Depth formats */ \
|
||||
SURFACE_FORMAT_ELEM(VK_FORMAT_D32_SFLOAT, usage_attachable, D32_FLOAT) \
|
||||
SURFACE_FORMAT_ELEM(VK_FORMAT_D16_UNORM, usage_attachable, D16_UNORM) \
|
||||
@@ -253,8 +259,8 @@ FormatInfo SurfaceFormat(const Device& device, FormatType format_type, bool with
|
||||
break;
|
||||
}
|
||||
}
|
||||
// Transcode on hardware that doesn't support BCn natively
|
||||
if (!device.IsOptimalBcnSupported() && VideoCore::Surface::IsPixelFormatBCn(pixel_format)) {
|
||||
// Transcode on hardware that doesn't support BCn natively
|
||||
if (pixel_format == PixelFormat::BC4_SNORM) {
|
||||
tuple.format = VK_FORMAT_R8_SNORM;
|
||||
} else if (pixel_format == PixelFormat::BC4_UNORM) {
|
||||
@@ -270,6 +276,9 @@ FormatInfo SurfaceFormat(const Device& device, FormatType format_type, bool with
|
||||
} else {
|
||||
tuple.format = VK_FORMAT_A8B8G8R8_UNORM_PACK32;
|
||||
}
|
||||
} else if (!device.IsOptimalEtc2Supported() && VideoCore::Surface::IsPixelFormatETC2(pixel_format)) {
|
||||
// Transcode on hardware that doesn't support ETC2 natively
|
||||
tuple.format = is_srgb ? VK_FORMAT_A8B8G8R8_SRGB_PACK32 : VK_FORMAT_A8B8G8R8_UNORM_PACK32;
|
||||
}
|
||||
bool const attachable = (tuple.usage & usage_attachable) != 0;
|
||||
bool const storage = (tuple.usage & usage_storage) != 0;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: 2014 Citra Emulator Project
|
||||
@@ -336,6 +336,20 @@ bool IsPixelFormatBCn(PixelFormat format) {
|
||||
}
|
||||
}
|
||||
|
||||
bool IsPixelFormatETC2(PixelFormat format) {
|
||||
switch (format) {
|
||||
case PixelFormat::ETC2_RGB_UNORM:
|
||||
case PixelFormat::ETC2_RGBA_UNORM:
|
||||
case PixelFormat::ETC2_RGB_PTA_UNORM:
|
||||
case PixelFormat::ETC2_RGB_SRGB:
|
||||
case PixelFormat::ETC2_RGBA_SRGB:
|
||||
case PixelFormat::ETC2_RGB_PTA_SRGB:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool IsPixelFormatSRGB(PixelFormat format) {
|
||||
switch (format) {
|
||||
case PixelFormat::A8B8G8R8_SRGB:
|
||||
@@ -344,6 +358,9 @@ bool IsPixelFormatSRGB(PixelFormat format) {
|
||||
case PixelFormat::BC2_SRGB:
|
||||
case PixelFormat::BC3_SRGB:
|
||||
case PixelFormat::BC7_SRGB:
|
||||
case PixelFormat::ETC2_RGB_SRGB:
|
||||
case PixelFormat::ETC2_RGBA_SRGB:
|
||||
case PixelFormat::ETC2_RGB_PTA_SRGB:
|
||||
case PixelFormat::ASTC_2D_4X4_SRGB:
|
||||
case PixelFormat::ASTC_2D_8X8_SRGB:
|
||||
case PixelFormat::ASTC_2D_8X5_SRGB:
|
||||
|
||||
@@ -111,6 +111,12 @@ namespace VideoCore::Surface {
|
||||
PIXEL_FORMAT_ELEM(ASTC_2D_6X5_UNORM, 6, 5, 128) \
|
||||
PIXEL_FORMAT_ELEM(ASTC_2D_6X5_SRGB, 6, 5, 128) \
|
||||
PIXEL_FORMAT_ELEM(E5B9G9R9_FLOAT, 1, 1, 32) \
|
||||
PIXEL_FORMAT_ELEM(ETC2_RGB_UNORM, 4, 4, 64) \
|
||||
PIXEL_FORMAT_ELEM(ETC2_RGBA_UNORM, 4, 4, 128) \
|
||||
PIXEL_FORMAT_ELEM(ETC2_RGB_PTA_UNORM, 4, 4, 64) \
|
||||
PIXEL_FORMAT_ELEM(ETC2_RGB_SRGB, 4, 4, 64) \
|
||||
PIXEL_FORMAT_ELEM(ETC2_RGBA_SRGB, 4, 4, 128) \
|
||||
PIXEL_FORMAT_ELEM(ETC2_RGB_PTA_SRGB, 4, 4, 64) \
|
||||
/* Depth formats */ \
|
||||
PIXEL_FORMAT_ELEM(D32_FLOAT, 1, 1, 32) \
|
||||
PIXEL_FORMAT_ELEM(D16_UNORM, 1, 1, 16) \
|
||||
@@ -181,8 +187,6 @@ constexpr u32 BitsPerBlock(PixelFormat format) noexcept {
|
||||
}
|
||||
}
|
||||
|
||||
#undef PIXEL_FORMAT_LIST
|
||||
|
||||
/// Returns the sizer in bytes of the specified pixel format
|
||||
constexpr u32 BytesPerBlock(PixelFormat pixel_format) {
|
||||
return BitsPerBlock(pixel_format) / CHAR_BIT;
|
||||
@@ -198,6 +202,7 @@ SurfaceType GetFormatType(PixelFormat pixel_format);
|
||||
bool HasAlpha(PixelFormat pixel_format);
|
||||
bool IsPixelFormatASTC(PixelFormat format);
|
||||
bool IsPixelFormatBCn(PixelFormat format);
|
||||
bool IsPixelFormatETC2(PixelFormat format);
|
||||
bool IsPixelFormatSRGB(PixelFormat format);
|
||||
bool IsPixelFormatInteger(PixelFormat format);
|
||||
bool IsPixelFormatSignedInteger(PixelFormat format);
|
||||
|
||||
@@ -191,6 +191,20 @@ PixelFormat PixelFormatFromTextureInfo(TextureFormat format, ComponentType red,
|
||||
return PixelFormat::BC6H_SFLOAT;
|
||||
case Hash(TextureFormat::BC6H_U16, FLOAT):
|
||||
return PixelFormat::BC6H_UFLOAT;
|
||||
/* ETC2 */
|
||||
case Hash(TextureFormat::ETC2_RGB, UNORM, LINEAR):
|
||||
return PixelFormat::ETC2_RGB_UNORM;
|
||||
case Hash(TextureFormat::ETC2_RGB_PTA, UNORM, LINEAR):
|
||||
return PixelFormat::ETC2_RGB_PTA_UNORM;
|
||||
case Hash(TextureFormat::ETC2_RGBA, UNORM, LINEAR):
|
||||
return PixelFormat::ETC2_RGBA_UNORM;
|
||||
case Hash(TextureFormat::ETC2_RGB, UNORM, SRGB):
|
||||
return PixelFormat::ETC2_RGB_SRGB;
|
||||
case Hash(TextureFormat::ETC2_RGB_PTA, UNORM, SRGB):
|
||||
return PixelFormat::ETC2_RGB_PTA_SRGB;
|
||||
case Hash(TextureFormat::ETC2_RGBA, UNORM, SRGB):
|
||||
return PixelFormat::ETC2_RGBA_SRGB;
|
||||
/* ASTC */
|
||||
case Hash(TextureFormat::ASTC_2D_4X4, UNORM, LINEAR):
|
||||
return PixelFormat::ASTC_2D_4X4_UNORM;
|
||||
case Hash(TextureFormat::ASTC_2D_4X4, UNORM, SRGB):
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
@@ -17,210 +20,9 @@ struct fmt::formatter<VideoCore::Surface::PixelFormat> : fmt::formatter<fmt::str
|
||||
using VideoCore::Surface::PixelFormat;
|
||||
const string_view name = [format] {
|
||||
switch (format) {
|
||||
case PixelFormat::A8B8G8R8_UNORM:
|
||||
return "A8B8G8R8_UNORM";
|
||||
case PixelFormat::A8B8G8R8_SNORM:
|
||||
return "A8B8G8R8_SNORM";
|
||||
case PixelFormat::A8B8G8R8_SINT:
|
||||
return "A8B8G8R8_SINT";
|
||||
case PixelFormat::A8B8G8R8_UINT:
|
||||
return "A8B8G8R8_UINT";
|
||||
case PixelFormat::R5G6B5_UNORM:
|
||||
return "R5G6B5_UNORM";
|
||||
case PixelFormat::B5G6R5_UNORM:
|
||||
return "B5G6R5_UNORM";
|
||||
case PixelFormat::A1R5G5B5_UNORM:
|
||||
return "A1R5G5B5_UNORM";
|
||||
case PixelFormat::A2B10G10R10_UNORM:
|
||||
return "A2B10G10R10_UNORM";
|
||||
case PixelFormat::A2B10G10R10_UINT:
|
||||
return "A2B10G10R10_UINT";
|
||||
case PixelFormat::A2R10G10B10_UNORM:
|
||||
return "A2R10G10B10_UNORM";
|
||||
case PixelFormat::A1B5G5R5_UNORM:
|
||||
return "A1B5G5R5_UNORM";
|
||||
case PixelFormat::A5B5G5R1_UNORM:
|
||||
return "A5B5G5R1_UNORM";
|
||||
case PixelFormat::R8_UNORM:
|
||||
return "R8_UNORM";
|
||||
case PixelFormat::R8_SNORM:
|
||||
return "R8_SNORM";
|
||||
case PixelFormat::R8_SINT:
|
||||
return "R8_SINT";
|
||||
case PixelFormat::R8_UINT:
|
||||
return "R8_UINT";
|
||||
case PixelFormat::R16G16B16A16_FLOAT:
|
||||
return "R16G16B16A16_FLOAT";
|
||||
case PixelFormat::R16G16B16A16_UNORM:
|
||||
return "R16G16B16A16_UNORM";
|
||||
case PixelFormat::R16G16B16A16_SNORM:
|
||||
return "R16G16B16A16_SNORM";
|
||||
case PixelFormat::R16G16B16A16_SINT:
|
||||
return "R16G16B16A16_SINT";
|
||||
case PixelFormat::R16G16B16A16_UINT:
|
||||
return "R16G16B16A16_UINT";
|
||||
case PixelFormat::B10G11R11_FLOAT:
|
||||
return "B10G11R11_FLOAT";
|
||||
case PixelFormat::R32G32B32A32_UINT:
|
||||
return "R32G32B32A32_UINT";
|
||||
case PixelFormat::BC1_RGBA_UNORM:
|
||||
return "BC1_RGBA_UNORM";
|
||||
case PixelFormat::BC2_UNORM:
|
||||
return "BC2_UNORM";
|
||||
case PixelFormat::BC3_UNORM:
|
||||
return "BC3_UNORM";
|
||||
case PixelFormat::BC4_UNORM:
|
||||
return "BC4_UNORM";
|
||||
case PixelFormat::BC4_SNORM:
|
||||
return "BC4_SNORM";
|
||||
case PixelFormat::BC5_UNORM:
|
||||
return "BC5_UNORM";
|
||||
case PixelFormat::BC5_SNORM:
|
||||
return "BC5_SNORM";
|
||||
case PixelFormat::BC7_UNORM:
|
||||
return "BC7_UNORM";
|
||||
case PixelFormat::BC6H_UFLOAT:
|
||||
return "BC6H_UFLOAT";
|
||||
case PixelFormat::BC6H_SFLOAT:
|
||||
return "BC6H_SFLOAT";
|
||||
case PixelFormat::ASTC_2D_4X4_UNORM:
|
||||
return "ASTC_2D_4X4_UNORM";
|
||||
case PixelFormat::B8G8R8A8_UNORM:
|
||||
return "B8G8R8A8_UNORM";
|
||||
case PixelFormat::R32G32B32A32_FLOAT:
|
||||
return "R32G32B32A32_FLOAT";
|
||||
case PixelFormat::R32G32B32A32_SINT:
|
||||
return "R32G32B32A32_SINT";
|
||||
case PixelFormat::R32G32_FLOAT:
|
||||
return "R32G32_FLOAT";
|
||||
case PixelFormat::R32G32_SINT:
|
||||
return "R32G32_SINT";
|
||||
case PixelFormat::R32_FLOAT:
|
||||
return "R32_FLOAT";
|
||||
case PixelFormat::R16_FLOAT:
|
||||
return "R16_FLOAT";
|
||||
case PixelFormat::R16_UNORM:
|
||||
return "R16_UNORM";
|
||||
case PixelFormat::R16_SNORM:
|
||||
return "R16_SNORM";
|
||||
case PixelFormat::R16_UINT:
|
||||
return "R16_UINT";
|
||||
case PixelFormat::R16_SINT:
|
||||
return "R16_SINT";
|
||||
case PixelFormat::R16G16_UNORM:
|
||||
return "R16G16_UNORM";
|
||||
case PixelFormat::R16G16_FLOAT:
|
||||
return "R16G16_FLOAT";
|
||||
case PixelFormat::R16G16_UINT:
|
||||
return "R16G16_UINT";
|
||||
case PixelFormat::R16G16_SINT:
|
||||
return "R16G16_SINT";
|
||||
case PixelFormat::R16G16_SNORM:
|
||||
return "R16G16_SNORM";
|
||||
case PixelFormat::R32G32B32_FLOAT:
|
||||
return "R32G32B32_FLOAT";
|
||||
case PixelFormat::A8B8G8R8_SRGB:
|
||||
return "A8B8G8R8_SRGB";
|
||||
case PixelFormat::R8G8_UNORM:
|
||||
return "R8G8_UNORM";
|
||||
case PixelFormat::R8G8_SNORM:
|
||||
return "R8G8_SNORM";
|
||||
case PixelFormat::R8G8_SINT:
|
||||
return "R8G8_SINT";
|
||||
case PixelFormat::R8G8_UINT:
|
||||
return "R8G8_UINT";
|
||||
case PixelFormat::R32G32_UINT:
|
||||
return "R32G32_UINT";
|
||||
case PixelFormat::R16G16B16X16_FLOAT:
|
||||
return "R16G16B16X16_FLOAT";
|
||||
case PixelFormat::R32_UINT:
|
||||
return "R32_UINT";
|
||||
case PixelFormat::R32_SINT:
|
||||
return "R32_SINT";
|
||||
case PixelFormat::ASTC_2D_8X8_UNORM:
|
||||
return "ASTC_2D_8X8_UNORM";
|
||||
case PixelFormat::ASTC_2D_8X5_UNORM:
|
||||
return "ASTC_2D_8X5_UNORM";
|
||||
case PixelFormat::ASTC_2D_5X4_UNORM:
|
||||
return "ASTC_2D_5X4_UNORM";
|
||||
case PixelFormat::B8G8R8A8_SRGB:
|
||||
return "B8G8R8A8_SRGB";
|
||||
case PixelFormat::BC1_RGBA_SRGB:
|
||||
return "BC1_RGBA_SRGB";
|
||||
case PixelFormat::BC2_SRGB:
|
||||
return "BC2_SRGB";
|
||||
case PixelFormat::BC3_SRGB:
|
||||
return "BC3_SRGB";
|
||||
case PixelFormat::BC7_SRGB:
|
||||
return "BC7_SRGB";
|
||||
case PixelFormat::A4B4G4R4_UNORM:
|
||||
return "A4B4G4R4_UNORM";
|
||||
case PixelFormat::G4R4_UNORM:
|
||||
return "G4R4_UNORM";
|
||||
case PixelFormat::ASTC_2D_4X4_SRGB:
|
||||
return "ASTC_2D_4X4_SRGB";
|
||||
case PixelFormat::ASTC_2D_8X8_SRGB:
|
||||
return "ASTC_2D_8X8_SRGB";
|
||||
case PixelFormat::ASTC_2D_8X5_SRGB:
|
||||
return "ASTC_2D_8X5_SRGB";
|
||||
case PixelFormat::ASTC_2D_5X4_SRGB:
|
||||
return "ASTC_2D_5X4_SRGB";
|
||||
case PixelFormat::ASTC_2D_5X5_UNORM:
|
||||
return "ASTC_2D_5X5_UNORM";
|
||||
case PixelFormat::ASTC_2D_5X5_SRGB:
|
||||
return "ASTC_2D_5X5_SRGB";
|
||||
case PixelFormat::ASTC_2D_10X8_UNORM:
|
||||
return "ASTC_2D_10X8_UNORM";
|
||||
case PixelFormat::ASTC_2D_10X8_SRGB:
|
||||
return "ASTC_2D_10X8_SRGB";
|
||||
case PixelFormat::ASTC_2D_6X6_UNORM:
|
||||
return "ASTC_2D_6X6_UNORM";
|
||||
case PixelFormat::ASTC_2D_6X6_SRGB:
|
||||
return "ASTC_2D_6X6_SRGB";
|
||||
case PixelFormat::ASTC_2D_10X6_UNORM:
|
||||
return "ASTC_2D_10X6_UNORM";
|
||||
case PixelFormat::ASTC_2D_10X6_SRGB:
|
||||
return "ASTC_2D_10X6_SRGB";
|
||||
case PixelFormat::ASTC_2D_10X5_UNORM:
|
||||
return "ASTC_2D_10X5_UNORM";
|
||||
case PixelFormat::ASTC_2D_10X5_SRGB:
|
||||
return "ASTC_2D_10X5_SRGB";
|
||||
case PixelFormat::ASTC_2D_10X10_UNORM:
|
||||
return "ASTC_2D_10X10_UNORM";
|
||||
case PixelFormat::ASTC_2D_10X10_SRGB:
|
||||
return "ASTC_2D_10X10_SRGB";
|
||||
case PixelFormat::ASTC_2D_12X10_UNORM:
|
||||
return "ASTC_2D_12X10_UNORM";
|
||||
case PixelFormat::ASTC_2D_12X10_SRGB:
|
||||
return "ASTC_2D_12X10_SRGB";
|
||||
case PixelFormat::ASTC_2D_12X12_UNORM:
|
||||
return "ASTC_2D_12X12_UNORM";
|
||||
case PixelFormat::ASTC_2D_12X12_SRGB:
|
||||
return "ASTC_2D_12X12_SRGB";
|
||||
case PixelFormat::ASTC_2D_8X6_UNORM:
|
||||
return "ASTC_2D_8X6_UNORM";
|
||||
case PixelFormat::ASTC_2D_8X6_SRGB:
|
||||
return "ASTC_2D_8X6_SRGB";
|
||||
case PixelFormat::ASTC_2D_6X5_UNORM:
|
||||
return "ASTC_2D_6X5_UNORM";
|
||||
case PixelFormat::ASTC_2D_6X5_SRGB:
|
||||
return "ASTC_2D_6X5_SRGB";
|
||||
case PixelFormat::E5B9G9R9_FLOAT:
|
||||
return "E5B9G9R9_FLOAT";
|
||||
case PixelFormat::D32_FLOAT:
|
||||
return "D32_FLOAT";
|
||||
case PixelFormat::D16_UNORM:
|
||||
return "D16_UNORM";
|
||||
case PixelFormat::X8_D24_UNORM:
|
||||
return "X8_D24_UNORM";
|
||||
case PixelFormat::S8_UINT:
|
||||
return "S8_UINT";
|
||||
case PixelFormat::D24_UNORM_S8_UINT:
|
||||
return "D24_UNORM_S8_UINT";
|
||||
case PixelFormat::S8_UINT_D24_UNORM:
|
||||
return "S8_UINT_D24_UNORM";
|
||||
case PixelFormat::D32_FLOAT_S8_UINT:
|
||||
return "D32_FLOAT_S8_UINT";
|
||||
#define PIXEL_FORMAT_ELEM(NAME, ...) case PixelFormat::NAME: return #NAME;
|
||||
PIXEL_FORMAT_LIST
|
||||
#undef PIXEL_FORMAT_ELEM
|
||||
case PixelFormat::MaxDepthStencilFormat:
|
||||
case PixelFormat::Invalid:
|
||||
return "Invalid";
|
||||
|
||||
@@ -363,6 +363,11 @@ public:
|
||||
return features.features.textureCompressionBC;
|
||||
}
|
||||
|
||||
/// Returns true if ETC2 is natively supported.
|
||||
bool IsOptimalEtc2Supported() const {
|
||||
return features.features.textureCompressionETC2;
|
||||
}
|
||||
|
||||
/// Returns true if descriptor aliasing is natively supported.
|
||||
bool IsDescriptorAliasingSupported() const {
|
||||
return GetDriverID() != VK_DRIVER_ID_QUALCOMM_PROPRIETARY;
|
||||
|
||||
Reference in New Issue
Block a user