Compare commits
49 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| ee47642488 | |||
| 8688c59239 | |||
| 63e7957bce | |||
| 63ed7d9af7 | |||
| 3c8c17be4d | |||
| aed5878dd3 | |||
| e5291e2031 | |||
| 3be87bed8d | |||
| 31b9797296 | |||
| 5299554bb0 | |||
| 2dbef58eeb | |||
| 494e34af6a | |||
| ad8afaf1ef | |||
| 2686bf6734 | |||
| 33e92c15eb | |||
| 7461196839 | |||
| b769bea61b | |||
| b65314dc21 | |||
| 1841f63a40 | |||
| b01aa721ff | |||
| f190547bbb | |||
| c82318c9cb | |||
| c871320760 | |||
| 4e81b799f1 | |||
| ef811c6425 | |||
| abe6d39b1a | |||
| 3791c7ca82 | |||
| 3f4444b552 | |||
| 5b735a4c9d | |||
| 28d343d288 | |||
| da6673e79a | |||
| 992f325662 | |||
| 622bc478e6 | |||
| 67189d747c | |||
| 8b996015a8 | |||
| d4c79a0404 | |||
| aef3ae1cb9 | |||
| f40b425cd7 | |||
| 53cf91d151 | |||
| 198c6ad0d7 | |||
| 7dca0bc11d | |||
| 50384e2619 | |||
| 0498669b4b | |||
| bb8bf740e9 | |||
| 7f4de3466a | |||
| d702b393a4 | |||
| 8fdb51ab46 | |||
| 60a3980561 | |||
| d4cab35533 |
+3
-2
@@ -411,12 +411,13 @@ if (CONAN_REQUIRED_LIBS)
|
||||
# Download conan.cmake automatically, you can also just copy the conan.cmake file
|
||||
if(NOT EXISTS "${CMAKE_BINARY_DIR}/conan.cmake")
|
||||
message(STATUS "Downloading conan.cmake from https://github.com/conan-io/cmake-conan")
|
||||
file(DOWNLOAD "https://github.com/conan-io/cmake-conan/raw/v0.15/conan.cmake"
|
||||
# TODO: Use a tagged release. The latest tagged release does not support VS2022 as of this writing.
|
||||
file(DOWNLOAD "https://raw.githubusercontent.com/conan-io/cmake-conan/43e385830ee35377dbd2dcbe8d5a9e750301ea00/conan.cmake"
|
||||
"${CMAKE_BINARY_DIR}/conan.cmake")
|
||||
endif()
|
||||
include(${CMAKE_BINARY_DIR}/conan.cmake)
|
||||
|
||||
conan_check(VERSION 1.24.0 REQUIRED)
|
||||
conan_check(VERSION 1.41.0 REQUIRED)
|
||||
|
||||
# Manually add iconv to fix a dep conflict between qt and sdl2
|
||||
# We don't need to add it through find_package or anything since the other two can find it just fine
|
||||
|
||||
Vendored
+5816
File diff suppressed because it is too large
Load Diff
Vendored
+5830
File diff suppressed because it is too large
Load Diff
Vendored
+2387
-1311
File diff suppressed because it is too large
Load Diff
Vendored
+2399
-1324
File diff suppressed because it is too large
Load Diff
Vendored
+2852
-1754
File diff suppressed because it is too large
Load Diff
Vendored
+2662
-1557
File diff suppressed because it is too large
Load Diff
Vendored
+2562
-1503
File diff suppressed because it is too large
Load Diff
Vendored
+5826
File diff suppressed because it is too large
Load Diff
Vendored
+5785
File diff suppressed because it is too large
Load Diff
Vendored
+2708
-1623
File diff suppressed because it is too large
Load Diff
Vendored
+2614
-1499
File diff suppressed because it is too large
Load Diff
Vendored
+2393
-1316
File diff suppressed because it is too large
Load Diff
Vendored
+2815
-1732
File diff suppressed because it is too large
Load Diff
Vendored
+2906
-1792
File diff suppressed because it is too large
Load Diff
Vendored
+5812
File diff suppressed because it is too large
Load Diff
Vendored
+5819
File diff suppressed because it is too large
Load Diff
Vendored
+2411
-1321
File diff suppressed because it is too large
Load Diff
Vendored
+5827
File diff suppressed because it is too large
Load Diff
@@ -64,7 +64,7 @@ public:
|
||||
using propagate_on_container_copy_assignment = std::true_type;
|
||||
using propagate_on_container_move_assignment = std::true_type;
|
||||
using propagate_on_container_swap = std::true_type;
|
||||
using is_always_equal = std::true_type;
|
||||
using is_always_equal = std::false_type;
|
||||
|
||||
constexpr AlignmentAllocator() noexcept = default;
|
||||
|
||||
@@ -83,6 +83,11 @@ public:
|
||||
struct rebind {
|
||||
using other = AlignmentAllocator<T2, Align>;
|
||||
};
|
||||
|
||||
template <typename T2, size_t Align2>
|
||||
constexpr bool operator==(const AlignmentAllocator<T2, Align2>&) const noexcept {
|
||||
return std::is_same_v<T, T2> && Align == Align2;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace Common
|
||||
|
||||
@@ -7,7 +7,6 @@
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <atomic>
|
||||
#include <chrono>
|
||||
#include <map>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
@@ -487,9 +486,9 @@ struct Values {
|
||||
// System
|
||||
Setting<std::optional<u32>> rng_seed{std::optional<u32>(), "rng_seed"};
|
||||
// Measured in seconds since epoch
|
||||
std::optional<std::chrono::seconds> custom_rtc;
|
||||
std::optional<s64> custom_rtc;
|
||||
// Set on game boot, reset on stop. Seconds difference between current time and `custom_rtc`
|
||||
std::chrono::seconds custom_rtc_differential;
|
||||
s64 custom_rtc_differential;
|
||||
|
||||
BasicSetting<s32> current_user{0, "current_user"};
|
||||
RangedSetting<s32> language_index{1, 0, 17, "language_index"};
|
||||
|
||||
@@ -263,7 +263,7 @@ void ARM_Dynarmic_64::Run() {
|
||||
}
|
||||
|
||||
void ARM_Dynarmic_64::Step() {
|
||||
cb->InterpreterFallback(jit->GetPC(), 1);
|
||||
jit->Step();
|
||||
}
|
||||
|
||||
ARM_Dynarmic_64::ARM_Dynarmic_64(System& system_, CPUInterrupts& interrupt_handlers_,
|
||||
|
||||
+34
-2
@@ -140,25 +140,45 @@ struct System::Impl {
|
||||
cpu_manager{system}, reporter{system}, applet_manager{system}, time_manager{system} {}
|
||||
|
||||
SystemResultStatus Run() {
|
||||
std::unique_lock<std::mutex> lk(suspend_guard);
|
||||
status = SystemResultStatus::Success;
|
||||
|
||||
kernel.Suspend(false);
|
||||
core_timing.SyncPause(false);
|
||||
cpu_manager.Pause(false);
|
||||
is_paused = false;
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
SystemResultStatus Pause() {
|
||||
std::unique_lock<std::mutex> lk(suspend_guard);
|
||||
status = SystemResultStatus::Success;
|
||||
|
||||
core_timing.SyncPause(true);
|
||||
kernel.Suspend(true);
|
||||
cpu_manager.Pause(true);
|
||||
is_paused = true;
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
std::unique_lock<std::mutex> StallCPU() {
|
||||
std::unique_lock<std::mutex> lk(suspend_guard);
|
||||
kernel.Suspend(true);
|
||||
core_timing.SyncPause(true);
|
||||
cpu_manager.Pause(true);
|
||||
return lk;
|
||||
}
|
||||
|
||||
void UnstallCPU() {
|
||||
if (!is_paused) {
|
||||
core_timing.SyncPause(false);
|
||||
kernel.Suspend(false);
|
||||
cpu_manager.Pause(false);
|
||||
}
|
||||
}
|
||||
|
||||
SystemResultStatus Init(System& system, Frontend::EmuWindow& emu_window) {
|
||||
LOG_DEBUG(Core, "initialized OK");
|
||||
|
||||
@@ -176,8 +196,9 @@ struct System::Impl {
|
||||
cpu_manager.Initialize();
|
||||
core_timing.Initialize([&system]() { system.RegisterHostThread(); });
|
||||
|
||||
const auto current_time = std::chrono::duration_cast<std::chrono::seconds>(
|
||||
std::chrono::system_clock::now().time_since_epoch());
|
||||
const auto posix_time = std::chrono::system_clock::now().time_since_epoch();
|
||||
const auto current_time =
|
||||
std::chrono::duration_cast<std::chrono::seconds>(posix_time).count();
|
||||
Settings::values.custom_rtc_differential =
|
||||
Settings::values.custom_rtc.value_or(current_time) - current_time;
|
||||
|
||||
@@ -367,6 +388,9 @@ struct System::Impl {
|
||||
return perf_stats->GetAndResetStats(core_timing.GetGlobalTimeUs());
|
||||
}
|
||||
|
||||
std::mutex suspend_guard;
|
||||
bool is_paused{};
|
||||
|
||||
Timing::CoreTiming core_timing;
|
||||
Kernel::KernelCore kernel;
|
||||
/// RealVfsFilesystem instance
|
||||
@@ -464,6 +488,14 @@ void System::Shutdown() {
|
||||
impl->Shutdown();
|
||||
}
|
||||
|
||||
std::unique_lock<std::mutex> System::StallCPU() {
|
||||
return impl->StallCPU();
|
||||
}
|
||||
|
||||
void System::UnstallCPU() {
|
||||
impl->UnstallCPU();
|
||||
}
|
||||
|
||||
SystemResultStatus System::Load(Frontend::EmuWindow& emu_window, const std::string& filepath,
|
||||
u64 program_id, std::size_t program_index) {
|
||||
return impl->Load(*this, emu_window, filepath, program_id, program_index);
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#include <cstddef>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
@@ -160,6 +161,9 @@ public:
|
||||
/// Shutdown the emulated system.
|
||||
void Shutdown();
|
||||
|
||||
std::unique_lock<std::mutex> StallCPU();
|
||||
void UnstallCPU();
|
||||
|
||||
/**
|
||||
* Load an executable application.
|
||||
* @param emu_window Reference to the host-system window used for video output and keyboard
|
||||
|
||||
@@ -363,6 +363,8 @@ ResultCode KPageTable::UnmapProcessCodeMemory(VAddr dst_addr, VAddr src_addr, st
|
||||
block_manager->Update(src_addr, num_pages, KMemoryState::Normal,
|
||||
KMemoryPermission::ReadAndWrite);
|
||||
|
||||
system.InvalidateCpuInstructionCacheRange(dst_addr, size);
|
||||
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
|
||||
@@ -49,6 +49,11 @@ public:
|
||||
/// Gets the current running thread
|
||||
[[nodiscard]] KThread* GetCurrentThread() const;
|
||||
|
||||
/// Gets the idle thread
|
||||
[[nodiscard]] KThread* GetIdleThread() const {
|
||||
return idle_thread;
|
||||
}
|
||||
|
||||
/// Returns true if the scheduler is idle
|
||||
[[nodiscard]] bool IsIdle() const {
|
||||
return GetCurrentThread() == idle_thread;
|
||||
|
||||
@@ -886,7 +886,24 @@ static ResultCode GetInfo(Core::System& system, u64* result, u64 info_id, Handle
|
||||
*result = out_ticks;
|
||||
return ResultSuccess;
|
||||
}
|
||||
case GetInfoType::IdleTickCount: {
|
||||
if (handle == 0) {
|
||||
LOG_ERROR(Kernel_SVC, "Thread handle does not exist, handle=0x{:08X}",
|
||||
static_cast<Handle>(handle));
|
||||
return ResultInvalidHandle;
|
||||
}
|
||||
|
||||
if (info_sub_id != 0xFFFFFFFFFFFFFFFF && info_sub_id != system.CurrentCoreIndex()) {
|
||||
LOG_ERROR(Kernel_SVC, "Core is not the current core, got {}", info_sub_id);
|
||||
return ResultInvalidCombination;
|
||||
}
|
||||
|
||||
const auto& scheduler = *system.Kernel().CurrentScheduler();
|
||||
const auto* const idle_thread = scheduler.GetIdleThread();
|
||||
|
||||
*result = idle_thread->GetCpuTime();
|
||||
return ResultSuccess;
|
||||
}
|
||||
default:
|
||||
LOG_ERROR(Kernel_SVC, "Unimplemented svcGetInfo id=0x{:016X}", info_id);
|
||||
return ResultInvalidEnumValue;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
// Copyright 2018 yuzu emulator team
|
||||
// Copyright 2021 yuzu emulator team
|
||||
// Copyright 2021 Skyline Team and Contributors (https://github.com/skyline-emu/)
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
@@ -7,6 +8,7 @@
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/scope_exit.h"
|
||||
#include "core/core.h"
|
||||
#include "core/hle/kernel/k_event.h"
|
||||
#include "core/hle/kernel/k_writable_event.h"
|
||||
@@ -72,85 +74,127 @@ NvResult nvhost_ctrl::NvOsGetConfigU32(const std::vector<u8>& input, std::vector
|
||||
}
|
||||
|
||||
NvResult nvhost_ctrl::IocCtrlEventWait(const std::vector<u8>& input, std::vector<u8>& output,
|
||||
bool is_async) {
|
||||
bool is_allocation) {
|
||||
IocCtrlEventWaitParams params{};
|
||||
std::memcpy(¶ms, input.data(), sizeof(params));
|
||||
LOG_DEBUG(Service_NVDRV, "syncpt_id={}, threshold={}, timeout={}, is_async={}",
|
||||
params.syncpt_id, params.threshold, params.timeout, is_async);
|
||||
LOG_DEBUG(Service_NVDRV, "syncpt_id={}, threshold={}, timeout={}, is_allocation={}",
|
||||
params.fence.id, params.fence.value, params.timeout, is_allocation);
|
||||
|
||||
if (params.syncpt_id >= MaxSyncPoints) {
|
||||
bool must_unmark_fail = !is_allocation;
|
||||
const u32 event_id = params.value.raw;
|
||||
SCOPE_EXIT({
|
||||
std::memcpy(output.data(), ¶ms, sizeof(params));
|
||||
if (must_unmark_fail) {
|
||||
events_interface.fails[event_id] = 0;
|
||||
}
|
||||
});
|
||||
|
||||
const u32 fence_id = static_cast<u32>(params.fence.id);
|
||||
|
||||
if (fence_id >= MaxSyncPoints) {
|
||||
return NvResult::BadParameter;
|
||||
}
|
||||
|
||||
u32 event_id = params.value & 0x00FF;
|
||||
|
||||
if (event_id >= MaxNvEvents) {
|
||||
std::memcpy(output.data(), ¶ms, sizeof(params));
|
||||
return NvResult::BadParameter;
|
||||
}
|
||||
|
||||
if (syncpoint_manager.IsSyncpointExpired(params.syncpt_id, params.threshold)) {
|
||||
params.value = syncpoint_manager.GetSyncpointMin(params.syncpt_id);
|
||||
std::memcpy(output.data(), ¶ms, sizeof(params));
|
||||
if (params.fence.value == 0) {
|
||||
return NvResult::Success;
|
||||
}
|
||||
|
||||
if (const auto new_value = syncpoint_manager.RefreshSyncpoint(params.syncpt_id);
|
||||
syncpoint_manager.IsSyncpointExpired(params.syncpt_id, params.threshold)) {
|
||||
params.value = new_value;
|
||||
std::memcpy(output.data(), ¶ms, sizeof(params));
|
||||
if (syncpoint_manager.IsSyncpointExpired(fence_id, params.fence.value)) {
|
||||
params.value.raw = syncpoint_manager.GetSyncpointMin(fence_id);
|
||||
return NvResult::Success;
|
||||
}
|
||||
|
||||
if (const auto new_value = syncpoint_manager.RefreshSyncpoint(fence_id);
|
||||
syncpoint_manager.IsSyncpointExpired(fence_id, params.fence.value)) {
|
||||
params.value.raw = new_value;
|
||||
return NvResult::Success;
|
||||
}
|
||||
|
||||
auto& event = events_interface.events[event_id];
|
||||
auto& gpu = system.GPU();
|
||||
const u32 target_value = params.fence.value;
|
||||
|
||||
// This is mostly to take into account unimplemented features. As synced
|
||||
// gpu is always synced.
|
||||
if (!gpu.IsAsync()) {
|
||||
event.event->GetWritableEvent().Signal();
|
||||
return NvResult::Success;
|
||||
}
|
||||
const u32 current_syncpoint_value = event.fence.value;
|
||||
const s32 diff = current_syncpoint_value - params.threshold;
|
||||
if (diff >= 0) {
|
||||
event.event->GetWritableEvent().Signal();
|
||||
params.value = current_syncpoint_value;
|
||||
std::memcpy(output.data(), ¶ms, sizeof(params));
|
||||
return NvResult::Success;
|
||||
}
|
||||
const u32 target_value = current_syncpoint_value - diff;
|
||||
|
||||
if (!is_async) {
|
||||
params.value = 0;
|
||||
}
|
||||
const auto check_failing = [&]() {
|
||||
if (!is_allocation && events_interface.fails[event_id] > 1) {
|
||||
{
|
||||
auto lk = system.StallCPU();
|
||||
gpu.WaitFence(fence_id, target_value);
|
||||
system.UnstallCPU();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
if (params.timeout == 0) {
|
||||
std::memcpy(output.data(), ¶ms, sizeof(params));
|
||||
if (check_failing()) {
|
||||
return NvResult::Success;
|
||||
}
|
||||
return NvResult::Timeout;
|
||||
}
|
||||
|
||||
EventState status = events_interface.status[event_id];
|
||||
const bool bad_parameter = status != EventState::Free && status != EventState::Registered;
|
||||
if (bad_parameter) {
|
||||
std::memcpy(output.data(), ¶ms, sizeof(params));
|
||||
auto lock = events_interface.Lock();
|
||||
|
||||
u32 slot = [&]() {
|
||||
if (is_allocation) {
|
||||
params.value.raw = 0;
|
||||
return events_interface.FindFreeEvent(fence_id);
|
||||
} else {
|
||||
return params.value.raw;
|
||||
}
|
||||
}();
|
||||
|
||||
if (slot >= MaxNvEvents) {
|
||||
return NvResult::BadParameter;
|
||||
}
|
||||
events_interface.SetEventStatus(event_id, EventState::Waiting);
|
||||
events_interface.assigned_syncpt[event_id] = params.syncpt_id;
|
||||
events_interface.assigned_value[event_id] = target_value;
|
||||
if (is_async) {
|
||||
params.value = params.syncpt_id << 4;
|
||||
} else {
|
||||
params.value = ((params.syncpt_id & 0xfff) << 16) | 0x10000000;
|
||||
|
||||
auto* event = events_interface.events[slot].event;
|
||||
|
||||
if (!event) {
|
||||
return NvResult::BadParameter;
|
||||
}
|
||||
params.value |= event_id;
|
||||
event.event->GetWritableEvent().Clear();
|
||||
gpu.RegisterSyncptInterrupt(params.syncpt_id, target_value);
|
||||
std::memcpy(output.data(), ¶ms, sizeof(params));
|
||||
|
||||
if (events_interface.IsBeingUsed(slot)) {
|
||||
return NvResult::BadParameter;
|
||||
}
|
||||
|
||||
if (check_failing()) {
|
||||
return NvResult::Success;
|
||||
}
|
||||
|
||||
params.value.raw = 0;
|
||||
|
||||
events_interface.status[slot].store(EventState::Waiting, std::memory_order_relaxed);
|
||||
events_interface.assigned_syncpt[slot] = fence_id;
|
||||
events_interface.assigned_value[slot] = target_value;
|
||||
if (is_allocation) {
|
||||
params.value.syncpoint_id_for_allocation.Assign(static_cast<u16>(fence_id));
|
||||
params.value.event_allocated.Assign(1);
|
||||
} else {
|
||||
params.value.syncpoint_id.Assign(fence_id);
|
||||
}
|
||||
params.value.raw |= slot;
|
||||
|
||||
gpu.RegisterSyncptInterrupt(fence_id, target_value);
|
||||
return NvResult::Timeout;
|
||||
}
|
||||
|
||||
NvResult nvhost_ctrl::FreeEvent(u32 slot) {
|
||||
if (slot >= MaxNvEvents) {
|
||||
return NvResult::BadParameter;
|
||||
}
|
||||
|
||||
if (!events_interface.registered[slot]) {
|
||||
return NvResult::Success;
|
||||
}
|
||||
|
||||
if (events_interface.IsBeingUsed(slot)) {
|
||||
return NvResult::Busy;
|
||||
}
|
||||
|
||||
events_interface.Free(slot);
|
||||
return NvResult::Success;
|
||||
}
|
||||
|
||||
NvResult nvhost_ctrl::IocCtrlEventRegister(const std::vector<u8>& input, std::vector<u8>& output) {
|
||||
IocCtrlEventRegisterParams params{};
|
||||
std::memcpy(¶ms, input.data(), sizeof(params));
|
||||
@@ -159,16 +203,16 @@ NvResult nvhost_ctrl::IocCtrlEventRegister(const std::vector<u8>& input, std::ve
|
||||
if (event_id >= MaxNvEvents) {
|
||||
return NvResult::BadParameter;
|
||||
}
|
||||
|
||||
auto lock = events_interface.Lock();
|
||||
|
||||
if (events_interface.registered[event_id]) {
|
||||
const auto event_state = events_interface.status[event_id];
|
||||
if (event_state != EventState::Free) {
|
||||
LOG_WARNING(Service_NVDRV, "Event already registered! Unregistering previous event");
|
||||
events_interface.UnregisterEvent(event_id);
|
||||
} else {
|
||||
return NvResult::BadParameter;
|
||||
const auto result = FreeEvent(event_id);
|
||||
if (result != NvResult::Success) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
events_interface.RegisterEvent(event_id);
|
||||
events_interface.Create(event_id);
|
||||
return NvResult::Success;
|
||||
}
|
||||
|
||||
@@ -178,31 +222,33 @@ NvResult nvhost_ctrl::IocCtrlEventUnregister(const std::vector<u8>& input,
|
||||
std::memcpy(¶ms, input.data(), sizeof(params));
|
||||
const u32 event_id = params.user_event_id & 0x00FF;
|
||||
LOG_DEBUG(Service_NVDRV, " called, user_event_id: {:X}", event_id);
|
||||
if (event_id >= MaxNvEvents) {
|
||||
return NvResult::BadParameter;
|
||||
}
|
||||
if (!events_interface.registered[event_id]) {
|
||||
return NvResult::BadParameter;
|
||||
}
|
||||
events_interface.UnregisterEvent(event_id);
|
||||
return NvResult::Success;
|
||||
|
||||
auto lock = events_interface.Lock();
|
||||
return FreeEvent(event_id);
|
||||
}
|
||||
|
||||
NvResult nvhost_ctrl::IocCtrlClearEventWait(const std::vector<u8>& input, std::vector<u8>& output) {
|
||||
IocCtrlEventSignalParams params{};
|
||||
IocCtrlEventClearParams params{};
|
||||
std::memcpy(¶ms, input.data(), sizeof(params));
|
||||
|
||||
u32 event_id = params.event_id & 0x00FF;
|
||||
LOG_WARNING(Service_NVDRV, "cleared event wait on, event_id: {:X}", event_id);
|
||||
u32 event_id = params.event_id.slot;
|
||||
LOG_DEBUG(Service_NVDRV, "cleared event wait on, event_id: {:X}", event_id);
|
||||
|
||||
if (event_id >= MaxNvEvents) {
|
||||
return NvResult::BadParameter;
|
||||
}
|
||||
if (events_interface.status[event_id] == EventState::Waiting) {
|
||||
events_interface.LiberateEvent(event_id);
|
||||
}
|
||||
|
||||
syncpoint_manager.RefreshSyncpoint(events_interface.events[event_id].fence.id);
|
||||
auto lock = events_interface.Lock();
|
||||
|
||||
if (events_interface.status[event_id].exchange(
|
||||
EventState::Cancelling, std::memory_order_acq_rel) == EventState::Waiting) {
|
||||
system.GPU().CancelSyncptInterrupt(events_interface.assigned_syncpt[event_id],
|
||||
events_interface.assigned_value[event_id]);
|
||||
syncpoint_manager.RefreshSyncpoint(events_interface.events[event_id].fence.id);
|
||||
}
|
||||
events_interface.fails[event_id]++;
|
||||
events_interface.status[event_id].store(EventState::Cancelled, std::memory_order_release);
|
||||
events_interface.events[event_id].event->GetWritableEvent().Clear();
|
||||
|
||||
return NvResult::Success;
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
|
||||
#include <array>
|
||||
#include <vector>
|
||||
#include "common/bit_field.h"
|
||||
#include "common/common_types.h"
|
||||
#include "core/hle/service/nvdrv/devices/nvdevice.h"
|
||||
#include "core/hle/service/nvdrv/nvdrv.h"
|
||||
@@ -28,6 +29,24 @@ public:
|
||||
void OnOpen(DeviceFD fd) override;
|
||||
void OnClose(DeviceFD fd) override;
|
||||
|
||||
union SyncpointEventValue {
|
||||
u32 raw;
|
||||
|
||||
union {
|
||||
BitField<0, 4, u32> partial_slot;
|
||||
BitField<4, 28, u32> syncpoint_id;
|
||||
};
|
||||
|
||||
struct {
|
||||
u16 slot;
|
||||
union {
|
||||
BitField<0, 12, u16> syncpoint_id_for_allocation;
|
||||
BitField<12, 1, u16> event_allocated;
|
||||
};
|
||||
};
|
||||
};
|
||||
static_assert(sizeof(SyncpointEventValue) == sizeof(u32));
|
||||
|
||||
private:
|
||||
struct IocSyncptReadParams {
|
||||
u32_le id{};
|
||||
@@ -84,27 +103,18 @@ private:
|
||||
};
|
||||
static_assert(sizeof(IocGetConfigParams) == 387, "IocGetConfigParams is incorrect size");
|
||||
|
||||
struct IocCtrlEventSignalParams {
|
||||
u32_le event_id{};
|
||||
struct IocCtrlEventClearParams {
|
||||
SyncpointEventValue event_id{};
|
||||
};
|
||||
static_assert(sizeof(IocCtrlEventSignalParams) == 4,
|
||||
"IocCtrlEventSignalParams is incorrect size");
|
||||
static_assert(sizeof(IocCtrlEventClearParams) == 4,
|
||||
"IocCtrlEventClearParams is incorrect size");
|
||||
|
||||
struct IocCtrlEventWaitParams {
|
||||
u32_le syncpt_id{};
|
||||
u32_le threshold{};
|
||||
s32_le timeout{};
|
||||
u32_le value{};
|
||||
};
|
||||
static_assert(sizeof(IocCtrlEventWaitParams) == 16, "IocCtrlEventWaitParams is incorrect size");
|
||||
|
||||
struct IocCtrlEventWaitAsyncParams {
|
||||
u32_le syncpt_id{};
|
||||
u32_le threshold{};
|
||||
Fence fence{};
|
||||
u32_le timeout{};
|
||||
u32_le value{};
|
||||
SyncpointEventValue value{};
|
||||
};
|
||||
static_assert(sizeof(IocCtrlEventWaitAsyncParams) == 16,
|
||||
static_assert(sizeof(IocCtrlEventWaitParams) == 16,
|
||||
"IocCtrlEventWaitAsyncParams is incorrect size");
|
||||
|
||||
struct IocCtrlEventRegisterParams {
|
||||
@@ -125,11 +135,14 @@ private:
|
||||
static_assert(sizeof(IocCtrlEventKill) == 8, "IocCtrlEventKill is incorrect size");
|
||||
|
||||
NvResult NvOsGetConfigU32(const std::vector<u8>& input, std::vector<u8>& output);
|
||||
NvResult IocCtrlEventWait(const std::vector<u8>& input, std::vector<u8>& output, bool is_async);
|
||||
NvResult IocCtrlEventWait(const std::vector<u8>& input, std::vector<u8>& output,
|
||||
bool is_allocation);
|
||||
NvResult IocCtrlEventRegister(const std::vector<u8>& input, std::vector<u8>& output);
|
||||
NvResult IocCtrlEventUnregister(const std::vector<u8>& input, std::vector<u8>& output);
|
||||
NvResult IocCtrlClearEventWait(const std::vector<u8>& input, std::vector<u8>& output);
|
||||
|
||||
NvResult FreeEvent(u32 slot);
|
||||
|
||||
EventInterface& events_interface;
|
||||
SyncpointManager& syncpoint_manager;
|
||||
};
|
||||
|
||||
@@ -69,8 +69,7 @@ NvResult nvhost_nvdec_common::Submit(const std::vector<u8>& input, std::vector<u
|
||||
std::vector<Reloc> relocs(params.relocation_count);
|
||||
std::vector<u32> reloc_shifts(params.relocation_count);
|
||||
std::vector<SyncptIncr> syncpt_increments(params.syncpoint_count);
|
||||
std::vector<SyncptIncr> wait_checks(params.syncpoint_count);
|
||||
std::vector<Fence> fences(params.fence_count);
|
||||
std::vector<u32> fence_thresholds(params.fence_count);
|
||||
|
||||
// Slice input into their respective buffers
|
||||
std::size_t offset = sizeof(IoctlSubmit);
|
||||
@@ -78,15 +77,13 @@ NvResult nvhost_nvdec_common::Submit(const std::vector<u8>& input, std::vector<u
|
||||
offset += SliceVectors(input, relocs, params.relocation_count, offset);
|
||||
offset += SliceVectors(input, reloc_shifts, params.relocation_count, offset);
|
||||
offset += SliceVectors(input, syncpt_increments, params.syncpoint_count, offset);
|
||||
offset += SliceVectors(input, wait_checks, params.syncpoint_count, offset);
|
||||
offset += SliceVectors(input, fences, params.fence_count, offset);
|
||||
offset += SliceVectors(input, fence_thresholds, params.fence_count, offset);
|
||||
|
||||
auto& gpu = system.GPU();
|
||||
if (gpu.UseNvdec()) {
|
||||
for (std::size_t i = 0; i < syncpt_increments.size(); i++) {
|
||||
const SyncptIncr& syncpt_incr = syncpt_increments[i];
|
||||
fences[i].id = syncpt_incr.id;
|
||||
fences[i].value =
|
||||
fence_thresholds[i] =
|
||||
syncpoint_manager.IncreaseSyncpoint(syncpt_incr.id, syncpt_incr.increments);
|
||||
}
|
||||
}
|
||||
@@ -98,11 +95,6 @@ NvResult nvhost_nvdec_common::Submit(const std::vector<u8>& input, std::vector<u
|
||||
cmdlist.size() * sizeof(u32));
|
||||
gpu.PushCommandBuffer(cmdlist);
|
||||
}
|
||||
if (gpu.UseNvdec()) {
|
||||
fences[0].value = syncpoint_manager.IncreaseSyncpoint(fences[0].id, 1);
|
||||
Tegra::ChCommandHeaderList cmdlist{{(4 << 28) | fences[0].id}};
|
||||
gpu.PushCommandBuffer(cmdlist);
|
||||
}
|
||||
std::memcpy(output.data(), ¶ms, sizeof(IoctlSubmit));
|
||||
// Some games expect command_buffers to be written back
|
||||
offset = sizeof(IoctlSubmit);
|
||||
@@ -110,8 +102,7 @@ NvResult nvhost_nvdec_common::Submit(const std::vector<u8>& input, std::vector<u
|
||||
offset += WriteVectors(output, relocs, offset);
|
||||
offset += WriteVectors(output, reloc_shifts, offset);
|
||||
offset += WriteVectors(output, syncpt_increments, offset);
|
||||
offset += WriteVectors(output, wait_checks, offset);
|
||||
offset += WriteVectors(output, fences, offset);
|
||||
offset += WriteVectors(output, fence_thresholds, offset);
|
||||
|
||||
return NvResult::Success;
|
||||
}
|
||||
|
||||
@@ -56,19 +56,16 @@ protected:
|
||||
s32 target{};
|
||||
s32 target_offset{};
|
||||
};
|
||||
static_assert(sizeof(Reloc) == 0x10, "CommandBuffer has incorrect size");
|
||||
static_assert(sizeof(Reloc) == 0x10, "Reloc has incorrect size");
|
||||
|
||||
struct SyncptIncr {
|
||||
u32 id{};
|
||||
u32 increments{};
|
||||
u32 unk0{};
|
||||
u32 unk1{};
|
||||
u32 unk2{};
|
||||
};
|
||||
static_assert(sizeof(SyncptIncr) == 0x8, "CommandBuffer has incorrect size");
|
||||
|
||||
struct Fence {
|
||||
u32 id{};
|
||||
u32 value{};
|
||||
};
|
||||
static_assert(sizeof(Fence) == 0x8, "CommandBuffer has incorrect size");
|
||||
static_assert(sizeof(SyncptIncr) == 0x14, "SyncptIncr has incorrect size");
|
||||
|
||||
struct IoctlGetSyncpoint {
|
||||
// Input
|
||||
|
||||
@@ -1,3 +1,8 @@
|
||||
// Copyright 2021 yuzu emulator team
|
||||
// Copyright 2021 Skyline Team and Contributors (https://github.com/skyline-emu/)
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
@@ -81,11 +86,15 @@ enum class NvResult : u32 {
|
||||
ModuleNotPresent = 0xA000E,
|
||||
};
|
||||
|
||||
// obtained from
|
||||
// https://github.com/skyline-emu/skyline/blob/nvdec-dev/app/src/main/cpp/skyline/services/nvdrv/devices/nvhost/ctrl.h#L47
|
||||
enum class EventState {
|
||||
Free = 0,
|
||||
Registered = 1,
|
||||
Waiting = 2,
|
||||
Busy = 3,
|
||||
Available = 0,
|
||||
Waiting = 1,
|
||||
Cancelling = 2,
|
||||
Signalling = 3,
|
||||
Signalled = 4,
|
||||
Cancelled = 5,
|
||||
};
|
||||
|
||||
union Ioctl {
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
// Copyright 2018 yuzu emulator team
|
||||
// Copyright 2021 yuzu emulator team
|
||||
// Copyright 2021 Skyline Team and Contributors (https://github.com/skyline-emu/)
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <bit>
|
||||
#include <utility>
|
||||
|
||||
#include <fmt/format.h>
|
||||
@@ -28,6 +30,64 @@
|
||||
|
||||
namespace Service::Nvidia {
|
||||
|
||||
std::unique_lock<std::mutex> EventInterface::Lock() {
|
||||
return std::unique_lock<std::mutex>(events_mutex);
|
||||
}
|
||||
|
||||
void EventInterface::Signal(u32 event_id) {
|
||||
if (status[event_id].exchange(EventState::Signalling, std::memory_order_acq_rel) ==
|
||||
EventState::Waiting) {
|
||||
events[event_id].event->GetWritableEvent().Signal();
|
||||
}
|
||||
status[event_id].store(EventState::Signalled, std::memory_order_release);
|
||||
}
|
||||
|
||||
void EventInterface::Create(u32 event_id) {
|
||||
events[event_id].event =
|
||||
module.service_context.CreateEvent(fmt::format("NVDRV::NvEvent_{}", event_id));
|
||||
status[event_id] = EventState::Available;
|
||||
registered[event_id] = true;
|
||||
const u64 mask = 1ULL << event_id;
|
||||
events_mask |= mask;
|
||||
}
|
||||
|
||||
void EventInterface::Free(u32 event_id) {
|
||||
module.service_context.CloseEvent(events[event_id].event);
|
||||
events[event_id].event = nullptr;
|
||||
status[event_id] = EventState::Available;
|
||||
registered[event_id] = false;
|
||||
const u64 mask = ~(1ULL << event_id);
|
||||
events_mask &= mask;
|
||||
}
|
||||
|
||||
u32 EventInterface::FindFreeEvent(u32 syncpoint_id) {
|
||||
u32 slot{MaxNvEvents};
|
||||
u32 free_slot{MaxNvEvents};
|
||||
for (u32 i = 0; i < MaxNvEvents; i++) {
|
||||
if (events[i].event) {
|
||||
if (!IsBeingUsed(i)) {
|
||||
slot = i;
|
||||
if (assigned_syncpt[i] == syncpoint_id) {
|
||||
return slot;
|
||||
}
|
||||
}
|
||||
} else if (free_slot == MaxNvEvents) {
|
||||
free_slot = i;
|
||||
}
|
||||
}
|
||||
if (free_slot < MaxNvEvents) {
|
||||
Create(free_slot);
|
||||
return free_slot;
|
||||
}
|
||||
|
||||
if (slot < MaxNvEvents) {
|
||||
return slot;
|
||||
}
|
||||
|
||||
LOG_CRITICAL(Service_NVDRV, "Failed to allocate an event");
|
||||
return 0;
|
||||
}
|
||||
|
||||
void InstallInterfaces(SM::ServiceManager& service_manager, NVFlinger::NVFlinger& nvflinger,
|
||||
Core::System& system) {
|
||||
auto module_ = std::make_shared<Module>(system);
|
||||
@@ -40,11 +100,11 @@ void InstallInterfaces(SM::ServiceManager& service_manager, NVFlinger::NVFlinger
|
||||
}
|
||||
|
||||
Module::Module(Core::System& system)
|
||||
: syncpoint_manager{system.GPU()}, service_context{system, "nvdrv"} {
|
||||
: syncpoint_manager{system.GPU()}, events_interface{*this}, service_context{system, "nvdrv"} {
|
||||
events_interface.events_mask = 0;
|
||||
for (u32 i = 0; i < MaxNvEvents; i++) {
|
||||
events_interface.events[i].event =
|
||||
service_context.CreateEvent(fmt::format("NVDRV::NvEvent_{}", i));
|
||||
events_interface.status[i] = EventState::Free;
|
||||
events_interface.status[i] = EventState::Available;
|
||||
events_interface.events[i].event = nullptr;
|
||||
events_interface.registered[i] = false;
|
||||
}
|
||||
auto nvmap_dev = std::make_shared<Devices::nvmap>(system);
|
||||
@@ -64,8 +124,11 @@ Module::Module(Core::System& system)
|
||||
}
|
||||
|
||||
Module::~Module() {
|
||||
auto lock = events_interface.Lock();
|
||||
for (u32 i = 0; i < MaxNvEvents; i++) {
|
||||
service_context.CloseEvent(events_interface.events[i].event);
|
||||
if (events_interface.registered[i]) {
|
||||
service_context.CloseEvent(events_interface.events[i].event);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -171,21 +234,35 @@ NvResult Module::Close(DeviceFD fd) {
|
||||
}
|
||||
|
||||
void Module::SignalSyncpt(const u32 syncpoint_id, const u32 value) {
|
||||
for (u32 i = 0; i < MaxNvEvents; i++) {
|
||||
const u32 max = MaxNvEvents - std::countl_zero(events_interface.events_mask);
|
||||
const u32 min = std::countr_zero(events_interface.events_mask);
|
||||
for (u32 i = min; i < max; i++) {
|
||||
if (events_interface.assigned_syncpt[i] == syncpoint_id &&
|
||||
events_interface.assigned_value[i] == value) {
|
||||
events_interface.LiberateEvent(i);
|
||||
events_interface.events[i].event->GetWritableEvent().Signal();
|
||||
events_interface.Signal(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Kernel::KReadableEvent& Module::GetEvent(const u32 event_id) {
|
||||
return events_interface.events[event_id].event->GetReadableEvent();
|
||||
}
|
||||
Kernel::KEvent* Module::GetEvent(u32 event_id) {
|
||||
const auto event = Devices::nvhost_ctrl::SyncpointEventValue{.raw = event_id};
|
||||
|
||||
Kernel::KWritableEvent& Module::GetEventWriteable(const u32 event_id) {
|
||||
return events_interface.events[event_id].event->GetWritableEvent();
|
||||
const bool allocated = event.event_allocated.Value() != 0;
|
||||
const u32 slot{allocated ? event.partial_slot.Value() : static_cast<u32>(event.slot)};
|
||||
if (slot >= MaxNvEvents) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const u32 syncpoint_id{allocated ? event.syncpoint_id_for_allocation.Value()
|
||||
: event.syncpoint_id.Value()};
|
||||
|
||||
auto lock = events_interface.Lock();
|
||||
|
||||
if (events_interface.registered[slot] &&
|
||||
events_interface.assigned_syncpt[slot] == syncpoint_id) {
|
||||
return events_interface.events[slot].event;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
} // namespace Service::Nvidia
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
// Copyright 2018 yuzu emulator team
|
||||
// Copyright 2021 Skyline Team and Contributors (https://github.com/skyline-emu/)
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
@@ -40,15 +41,22 @@ struct NvEvent {
|
||||
Fence fence{};
|
||||
};
|
||||
|
||||
struct EventInterface {
|
||||
// Mask representing currently busy events
|
||||
class Module;
|
||||
|
||||
class EventInterface {
|
||||
public:
|
||||
EventInterface(Module& module_) : module{module_} {}
|
||||
|
||||
// Mask representing registered events
|
||||
u64 events_mask{};
|
||||
// Each kernel event associated to an NV event
|
||||
std::array<NvEvent, MaxNvEvents> events;
|
||||
std::array<NvEvent, MaxNvEvents> events{};
|
||||
// The status of the current NVEvent
|
||||
std::array<EventState, MaxNvEvents> status{};
|
||||
std::array<std::atomic<EventState>, MaxNvEvents> status{};
|
||||
// Tells if an NVEvent is registered or not
|
||||
std::array<bool, MaxNvEvents> registered{};
|
||||
// Tells the NVEvent that it has failed.
|
||||
std::array<u32, MaxNvEvents> fails{};
|
||||
// When an NVEvent is waiting on GPU interrupt, this is the sync_point
|
||||
// associated with it.
|
||||
std::array<u32, MaxNvEvents> assigned_syncpt{};
|
||||
@@ -57,50 +65,26 @@ struct EventInterface {
|
||||
std::array<u32, MaxNvEvents> assigned_value{};
|
||||
// Constant to denote an unasigned syncpoint.
|
||||
static constexpr u32 unassigned_syncpt = 0xFFFFFFFF;
|
||||
std::optional<u32> GetFreeEvent() const {
|
||||
u64 mask = events_mask;
|
||||
for (u32 i = 0; i < MaxNvEvents; i++) {
|
||||
const bool is_free = (mask & 0x1) == 0;
|
||||
if (is_free) {
|
||||
if (status[i] == EventState::Registered || status[i] == EventState::Free) {
|
||||
return {i};
|
||||
}
|
||||
}
|
||||
mask = mask >> 1;
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
void SetEventStatus(const u32 event_id, EventState new_status) {
|
||||
EventState old_status = status[event_id];
|
||||
if (old_status == new_status) {
|
||||
return;
|
||||
}
|
||||
status[event_id] = new_status;
|
||||
if (new_status == EventState::Registered) {
|
||||
registered[event_id] = true;
|
||||
}
|
||||
if (new_status == EventState::Waiting || new_status == EventState::Busy) {
|
||||
events_mask |= (1ULL << event_id);
|
||||
}
|
||||
}
|
||||
void RegisterEvent(const u32 event_id) {
|
||||
registered[event_id] = true;
|
||||
if (status[event_id] == EventState::Free) {
|
||||
status[event_id] = EventState::Registered;
|
||||
}
|
||||
}
|
||||
void UnregisterEvent(const u32 event_id) {
|
||||
registered[event_id] = false;
|
||||
if (status[event_id] == EventState::Registered) {
|
||||
status[event_id] = EventState::Free;
|
||||
}
|
||||
}
|
||||
void LiberateEvent(const u32 event_id) {
|
||||
status[event_id] = registered[event_id] ? EventState::Registered : EventState::Free;
|
||||
events_mask &= ~(1ULL << event_id);
|
||||
assigned_syncpt[event_id] = unassigned_syncpt;
|
||||
assigned_value[event_id] = 0;
|
||||
|
||||
bool IsBeingUsed(u32 event_id) {
|
||||
const auto current_status = status[event_id].load(std::memory_order_acquire);
|
||||
return current_status == EventState::Waiting || current_status == EventState::Cancelling ||
|
||||
current_status == EventState::Signalling;
|
||||
}
|
||||
|
||||
std::unique_lock<std::mutex> Lock();
|
||||
|
||||
void Signal(u32 event_id);
|
||||
|
||||
void Create(u32 event_id);
|
||||
|
||||
void Free(u32 event_id);
|
||||
|
||||
u32 FindFreeEvent(u32 syncpoint_id);
|
||||
|
||||
private:
|
||||
std::mutex events_mutex;
|
||||
Module& module;
|
||||
};
|
||||
|
||||
class Module final {
|
||||
@@ -137,11 +121,11 @@ public:
|
||||
|
||||
void SignalSyncpt(const u32 syncpoint_id, const u32 value);
|
||||
|
||||
Kernel::KReadableEvent& GetEvent(u32 event_id);
|
||||
|
||||
Kernel::KWritableEvent& GetEventWriteable(u32 event_id);
|
||||
Kernel::KEvent* GetEvent(u32 event_id);
|
||||
|
||||
private:
|
||||
friend class EventInterface;
|
||||
|
||||
/// Manages syncpoints on the host
|
||||
SyncpointManager syncpoint_manager;
|
||||
|
||||
@@ -157,6 +141,9 @@ private:
|
||||
EventInterface events_interface;
|
||||
|
||||
KernelHelpers::ServiceContext service_context;
|
||||
|
||||
void CreateEvent(u32 event_id);
|
||||
void FreeEvent(u32 event_id);
|
||||
};
|
||||
|
||||
/// Registers all NVDRV services with the specified service manager.
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#include "common/logging/log.h"
|
||||
#include "core/core.h"
|
||||
#include "core/hle/ipc_helpers.h"
|
||||
#include "core/hle/kernel/k_event.h"
|
||||
#include "core/hle/kernel/k_readable_event.h"
|
||||
#include "core/hle/kernel/k_thread.h"
|
||||
#include "core/hle/kernel/k_writable_event.h"
|
||||
@@ -168,7 +169,7 @@ void NVDRV::Initialize(Kernel::HLERequestContext& ctx) {
|
||||
void NVDRV::QueryEvent(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto fd = rp.Pop<DeviceFD>();
|
||||
const auto event_id = rp.Pop<u32>() & 0x00FF;
|
||||
const auto event_id = rp.Pop<u32>();
|
||||
LOG_WARNING(Service_NVDRV, "(STUBBED) called, fd={:X}, event_id={:X}", fd, event_id);
|
||||
|
||||
if (!is_initialized) {
|
||||
@@ -184,12 +185,14 @@ void NVDRV::QueryEvent(Kernel::HLERequestContext& ctx) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (event_id < MaxNvEvents) {
|
||||
auto* event = nvdrv->GetEvent(event_id);
|
||||
|
||||
if (event) {
|
||||
IPC::ResponseBuilder rb{ctx, 3, 1};
|
||||
rb.Push(ResultSuccess);
|
||||
auto& event = nvdrv->GetEvent(event_id);
|
||||
event.Clear();
|
||||
rb.PushCopyObjects(event);
|
||||
auto& readable_event = event->GetReadableEvent();
|
||||
readable_event.Clear();
|
||||
rb.PushCopyObjects(readable_event);
|
||||
rb.PushEnum(NvResult::Success);
|
||||
} else {
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
|
||||
@@ -13,18 +13,19 @@
|
||||
#include "core/hle/service/time/time_manager.h"
|
||||
|
||||
namespace Service::Time {
|
||||
|
||||
namespace {
|
||||
constexpr Clock::TimeSpanType standard_network_clock_accuracy{0x0009356907420000ULL};
|
||||
|
||||
static std::chrono::seconds GetSecondsSinceEpoch() {
|
||||
return std::chrono::duration_cast<std::chrono::seconds>(
|
||||
std::chrono::system_clock::now().time_since_epoch()) +
|
||||
s64 GetSecondsSinceEpoch() {
|
||||
const auto time_since_epoch = std::chrono::system_clock::now().time_since_epoch();
|
||||
return std::chrono::duration_cast<std::chrono::seconds>(time_since_epoch).count() +
|
||||
Settings::values.custom_rtc_differential;
|
||||
}
|
||||
|
||||
static s64 GetExternalRtcValue() {
|
||||
return GetSecondsSinceEpoch().count() + TimeManager::GetExternalTimeZoneOffset();
|
||||
s64 GetExternalRtcValue() {
|
||||
return GetSecondsSinceEpoch() + TimeManager::GetExternalTimeZoneOffset();
|
||||
}
|
||||
} // Anonymous namespace
|
||||
|
||||
struct TimeManager::Impl final {
|
||||
explicit Impl(Core::System& system)
|
||||
|
||||
@@ -471,46 +471,42 @@ CalibrationConfigurationJob::CalibrationConfigurationJob(
|
||||
std::function<void(u16, u16, u16, u16)> data_callback) {
|
||||
|
||||
std::thread([=, this] {
|
||||
constexpr u16 CALIBRATION_THRESHOLD = 100;
|
||||
|
||||
u16 min_x{UINT16_MAX};
|
||||
u16 min_y{UINT16_MAX};
|
||||
u16 max_x{};
|
||||
u16 max_y{};
|
||||
|
||||
Status current_status{Status::Initialized};
|
||||
SocketCallback callback{[](Response::Version) {}, [](Response::PortInfo) {},
|
||||
[&](Response::PadData data) {
|
||||
if (current_status == Status::Initialized) {
|
||||
// Receiving data means the communication is ready now
|
||||
current_status = Status::Ready;
|
||||
status_callback(current_status);
|
||||
}
|
||||
if (data.touch[0].is_active == 0) {
|
||||
return;
|
||||
}
|
||||
LOG_DEBUG(Input, "Current touch: {} {}", data.touch[0].x,
|
||||
data.touch[0].y);
|
||||
min_x = std::min(min_x, static_cast<u16>(data.touch[0].x));
|
||||
min_y = std::min(min_y, static_cast<u16>(data.touch[0].y));
|
||||
if (current_status == Status::Ready) {
|
||||
// First touch - min data (min_x/min_y)
|
||||
current_status = Status::Stage1Completed;
|
||||
status_callback(current_status);
|
||||
}
|
||||
if (data.touch[0].x - min_x > CALIBRATION_THRESHOLD &&
|
||||
data.touch[0].y - min_y > CALIBRATION_THRESHOLD) {
|
||||
// Set the current position as max value and finishes
|
||||
// configuration
|
||||
max_x = data.touch[0].x;
|
||||
max_y = data.touch[0].y;
|
||||
current_status = Status::Completed;
|
||||
data_callback(min_x, min_y, max_x, max_y);
|
||||
status_callback(current_status);
|
||||
SocketCallback callback{
|
||||
[](Response::Version) {}, [](Response::PortInfo) {},
|
||||
[&](Response::PadData data) {
|
||||
static constexpr u16 CALIBRATION_THRESHOLD = 100;
|
||||
static constexpr u16 MAX_VALUE = UINT16_MAX;
|
||||
|
||||
complete_event.Set();
|
||||
}
|
||||
}};
|
||||
if (current_status == Status::Initialized) {
|
||||
// Receiving data means the communication is ready now
|
||||
current_status = Status::Ready;
|
||||
status_callback(current_status);
|
||||
}
|
||||
const auto& touchpad_0 = data.touch[0];
|
||||
if (touchpad_0.is_active == 0) {
|
||||
return;
|
||||
}
|
||||
LOG_DEBUG(Input, "Current touch: {} {}", touchpad_0.x, touchpad_0.y);
|
||||
const u16 min_x = std::min(MAX_VALUE, static_cast<u16>(touchpad_0.x));
|
||||
const u16 min_y = std::min(MAX_VALUE, static_cast<u16>(touchpad_0.y));
|
||||
if (current_status == Status::Ready) {
|
||||
// First touch - min data (min_x/min_y)
|
||||
current_status = Status::Stage1Completed;
|
||||
status_callback(current_status);
|
||||
}
|
||||
if (touchpad_0.x - min_x > CALIBRATION_THRESHOLD &&
|
||||
touchpad_0.y - min_y > CALIBRATION_THRESHOLD) {
|
||||
// Set the current position as max value and finishes configuration
|
||||
const u16 max_x = touchpad_0.x;
|
||||
const u16 max_y = touchpad_0.y;
|
||||
current_status = Status::Completed;
|
||||
data_callback(min_x, min_y, max_x, max_y);
|
||||
status_callback(current_status);
|
||||
|
||||
complete_event.Set();
|
||||
}
|
||||
}};
|
||||
Socket socket{host, port, std::move(callback)};
|
||||
std::thread worker_thread{SocketLoop, &socket};
|
||||
complete_event.Wait();
|
||||
|
||||
@@ -492,7 +492,8 @@ void TexturePass(Environment& env, IR::Program& program) {
|
||||
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(std::countr_zero(DESCRIPTOR_SIZE))};
|
||||
inst->SetArg(0, ir.ShiftRightArithmetic(cbuf.dynamic_offset, shift));
|
||||
inst->SetArg(0, ir.UMin(ir.ShiftRightArithmetic(cbuf.dynamic_offset, shift),
|
||||
ir.Imm32(DESCRIPTOR_SIZE - 1)));
|
||||
} else {
|
||||
inst->SetArg(0, IR::Value{});
|
||||
}
|
||||
|
||||
@@ -38,6 +38,9 @@ enum : u8 {
|
||||
|
||||
Shaders,
|
||||
|
||||
// Special entries
|
||||
DepthBiasGlobal,
|
||||
|
||||
LastCommonEntry,
|
||||
};
|
||||
|
||||
|
||||
@@ -216,7 +216,7 @@ public:
|
||||
|
||||
void RegisterSyncptInterrupt(u32 syncpoint_id, u32 value);
|
||||
|
||||
[[nodiscard]] bool CancelSyncptInterrupt(u32 syncpoint_id, u32 value);
|
||||
bool CancelSyncptInterrupt(u32 syncpoint_id, u32 value);
|
||||
|
||||
[[nodiscard]] u64 GetTicks() const;
|
||||
|
||||
|
||||
@@ -258,9 +258,9 @@ private:
|
||||
|
||||
void AsyncFlushQuery(VAddr addr) {
|
||||
if (!uncommitted_flushes) {
|
||||
uncommitted_flushes = std::make_shared<std::unordered_set<VAddr>>();
|
||||
uncommitted_flushes = std::make_shared<std::vector<VAddr>>();
|
||||
}
|
||||
uncommitted_flushes->insert(addr);
|
||||
uncommitted_flushes->push_back(addr);
|
||||
}
|
||||
|
||||
static constexpr std::uintptr_t PAGE_SIZE = 4096;
|
||||
@@ -276,8 +276,8 @@ private:
|
||||
|
||||
std::array<CounterStream, VideoCore::NumQueryTypes> streams;
|
||||
|
||||
std::shared_ptr<std::unordered_set<VAddr>> uncommitted_flushes{};
|
||||
std::list<std::shared_ptr<std::unordered_set<VAddr>>> committed_flushes;
|
||||
std::shared_ptr<std::vector<VAddr>> uncommitted_flushes{};
|
||||
std::list<std::shared_ptr<std::vector<VAddr>>> committed_flushes;
|
||||
};
|
||||
|
||||
template <class QueryCache, class HostCounter>
|
||||
|
||||
@@ -42,7 +42,7 @@ private:
|
||||
};
|
||||
static_assert(sizeof(CacheEntry) == 8, "CacheEntry should be 8 bytes!");
|
||||
|
||||
std::array<CacheEntry, 0x1000000> cached_pages;
|
||||
std::array<CacheEntry, 0x2000000> cached_pages;
|
||||
Core::Memory::Memory& cpu_memory;
|
||||
};
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#include <limits>
|
||||
#include <optional>
|
||||
#include <span>
|
||||
#include <stdexcept>
|
||||
#include <vector>
|
||||
|
||||
#include <glad/glad.h>
|
||||
|
||||
@@ -21,12 +21,12 @@ public:
|
||||
|
||||
/// Returns the current logical tick.
|
||||
[[nodiscard]] u64 CurrentTick() const noexcept {
|
||||
return current_tick.load(std::memory_order_relaxed);
|
||||
return current_tick.load(std::memory_order_acquire);
|
||||
}
|
||||
|
||||
/// Returns the last known GPU tick.
|
||||
[[nodiscard]] u64 KnownGpuTick() const noexcept {
|
||||
return gpu_tick.load(std::memory_order_relaxed);
|
||||
return gpu_tick.load(std::memory_order_acquire);
|
||||
}
|
||||
|
||||
/// Returns the timeline semaphore handle.
|
||||
@@ -41,12 +41,21 @@ public:
|
||||
|
||||
/// Advance to the logical tick and return the old one
|
||||
[[nodiscard]] u64 NextTick() noexcept {
|
||||
return current_tick.fetch_add(1, std::memory_order::relaxed);
|
||||
return current_tick.fetch_add(1, std::memory_order_release);
|
||||
}
|
||||
|
||||
/// Refresh the known GPU tick
|
||||
void Refresh() {
|
||||
gpu_tick.store(semaphore.GetCounter(), std::memory_order_relaxed);
|
||||
u64 this_tick{};
|
||||
u64 counter{};
|
||||
do {
|
||||
this_tick = gpu_tick.load(std::memory_order_acquire);
|
||||
counter = semaphore.GetCounter();
|
||||
if (counter < this_tick) {
|
||||
return;
|
||||
}
|
||||
} while (!gpu_tick.compare_exchange_weak(this_tick, counter, std::memory_order_release,
|
||||
std::memory_order_relaxed));
|
||||
}
|
||||
|
||||
/// Waits for a tick to be hit on the GPU
|
||||
|
||||
@@ -117,7 +117,8 @@ u64 HostCounter::BlockingQuery() const {
|
||||
cache.GetScheduler().Wait(tick);
|
||||
u64 data;
|
||||
const VkResult query_result = cache.GetDevice().GetLogical().GetQueryResults(
|
||||
query.first, query.second, 1, sizeof(data), &data, sizeof(data), VK_QUERY_RESULT_64_BIT);
|
||||
query.first, query.second, 1, sizeof(data), &data, sizeof(data),
|
||||
VK_QUERY_RESULT_64_BIT | VK_QUERY_RESULT_WAIT_BIT);
|
||||
|
||||
switch (query_result) {
|
||||
case VK_SUCCESS:
|
||||
|
||||
@@ -627,9 +627,21 @@ void RasterizerVulkan::UpdateDepthBias(Tegra::Engines::Maxwell3D::Regs& regs) {
|
||||
if (!state_tracker.TouchDepthBias()) {
|
||||
return;
|
||||
}
|
||||
scheduler.Record([constant = regs.polygon_offset_units, clamp = regs.polygon_offset_clamp,
|
||||
float units = regs.polygon_offset_units / 2.0f;
|
||||
const bool is_d24 = regs.zeta.format == Tegra::DepthFormat::S8_UINT_Z24_UNORM ||
|
||||
regs.zeta.format == Tegra::DepthFormat::D24X8_UNORM ||
|
||||
regs.zeta.format == Tegra::DepthFormat::D24S8_UNORM ||
|
||||
regs.zeta.format == Tegra::DepthFormat::D24C8_UNORM;
|
||||
if (is_d24 && !device.SupportsD24DepthBuffer()) {
|
||||
// the base formulas can be obtained from here:
|
||||
// https://docs.microsoft.com/en-us/windows/win32/direct3d11/d3d10-graphics-programming-guide-output-merger-stage-depth-bias
|
||||
const double rescale_factor =
|
||||
static_cast<double>(1ULL << (32 - 24)) / (static_cast<double>(0x1.ep+127));
|
||||
units = static_cast<float>(static_cast<double>(units) * rescale_factor);
|
||||
}
|
||||
scheduler.Record([constant = units, clamp = regs.polygon_offset_clamp,
|
||||
factor = regs.polygon_offset_factor](vk::CommandBuffer cmdbuf) {
|
||||
cmdbuf.SetDepthBias(constant, clamp, factor / 2.0f);
|
||||
cmdbuf.SetDepthBias(constant, clamp, factor);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -54,6 +54,7 @@ void SetupDirtyViewports(Tables& tables) {
|
||||
FillBlock(tables[0], OFF(viewport_transform), NUM(viewport_transform), Viewports);
|
||||
FillBlock(tables[0], OFF(viewports), NUM(viewports), Viewports);
|
||||
tables[0][OFF(viewport_transform_enabled)] = Viewports;
|
||||
tables[1][OFF(screen_y_control)] = Viewports;
|
||||
}
|
||||
|
||||
void SetupDirtyScissors(Tables& tables) {
|
||||
|
||||
@@ -79,7 +79,8 @@ public:
|
||||
}
|
||||
|
||||
bool TouchDepthBias() {
|
||||
return Exchange(Dirty::DepthBias, false);
|
||||
return Exchange(Dirty::DepthBias, false) ||
|
||||
Exchange(VideoCommon::Dirty::DepthBiasGlobal, false);
|
||||
}
|
||||
|
||||
bool TouchBlendConstants() {
|
||||
|
||||
@@ -221,6 +221,7 @@ void TextureCache<P>::UpdateRenderTargets(bool is_clear) {
|
||||
BindRenderTarget(&render_targets.depth_buffer_id, FindDepthBuffer(is_clear));
|
||||
}
|
||||
const ImageViewId depth_buffer_id = render_targets.depth_buffer_id;
|
||||
|
||||
PrepareImageView(depth_buffer_id, true, is_clear && IsFullClear(depth_buffer_id));
|
||||
|
||||
for (size_t index = 0; index < NUM_RT; ++index) {
|
||||
@@ -230,6 +231,8 @@ void TextureCache<P>::UpdateRenderTargets(bool is_clear) {
|
||||
maxwell3d.regs.render_area.width,
|
||||
maxwell3d.regs.render_area.height,
|
||||
};
|
||||
|
||||
flags[Dirty::DepthBiasGlobal] = true;
|
||||
}
|
||||
|
||||
template <class P>
|
||||
|
||||
@@ -623,6 +623,10 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
|
||||
is_float16_supported = false;
|
||||
}
|
||||
|
||||
supports_d24_depth =
|
||||
IsFormatSupported(VK_FORMAT_D24_UNORM_S8_UINT,
|
||||
VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT, FormatType::Optimal);
|
||||
|
||||
graphics_queue = logical.GetQueue(graphics_family);
|
||||
present_queue = logical.GetQueue(present_family);
|
||||
}
|
||||
|
||||
@@ -332,6 +332,10 @@ public:
|
||||
return sets_per_pool;
|
||||
}
|
||||
|
||||
bool SupportsD24DepthBuffer() const {
|
||||
return supports_d24_depth;
|
||||
}
|
||||
|
||||
private:
|
||||
/// Checks if the physical device is suitable.
|
||||
void CheckSuitability(bool requires_swapchain) const;
|
||||
@@ -425,6 +429,7 @@ private:
|
||||
bool has_broken_cube_compatibility{}; ///< Has broken cube compatiblity bit
|
||||
bool has_renderdoc{}; ///< Has RenderDoc attached
|
||||
bool has_nsight_graphics{}; ///< Has Nsight Graphics attached
|
||||
bool supports_d24_depth{}; ///< Supports D24 depth buffers.
|
||||
|
||||
// Telemetry parameters
|
||||
std::string vendor_name; ///< Device's driver name.
|
||||
|
||||
@@ -918,8 +918,7 @@ void Config::ReadSystemValues() {
|
||||
const auto custom_rtc_enabled =
|
||||
ReadSetting(QStringLiteral("custom_rtc_enabled"), false).toBool();
|
||||
if (custom_rtc_enabled) {
|
||||
Settings::values.custom_rtc =
|
||||
std::chrono::seconds(ReadSetting(QStringLiteral("custom_rtc"), 0).toULongLong());
|
||||
Settings::values.custom_rtc = ReadSetting(QStringLiteral("custom_rtc"), 0).toLongLong();
|
||||
} else {
|
||||
Settings::values.custom_rtc = std::nullopt;
|
||||
}
|
||||
@@ -1450,9 +1449,7 @@ void Config::SaveSystemValues() {
|
||||
WriteSetting(QStringLiteral("custom_rtc_enabled"), Settings::values.custom_rtc.has_value(),
|
||||
false);
|
||||
WriteSetting(QStringLiteral("custom_rtc"),
|
||||
QVariant::fromValue<long long>(
|
||||
Settings::values.custom_rtc.value_or(std::chrono::seconds{}).count()),
|
||||
0);
|
||||
QVariant::fromValue<long long>(Settings::values.custom_rtc.value_or(0)), 0);
|
||||
}
|
||||
|
||||
WriteGlobalSetting(Settings::values.sound_index);
|
||||
|
||||
@@ -65,8 +65,7 @@ void ConfigureSystem::SetConfiguration() {
|
||||
QStringLiteral("%1")
|
||||
.arg(Settings::values.rng_seed.GetValue().value_or(0), 8, 16, QLatin1Char{'0'})
|
||||
.toUpper();
|
||||
const auto rtc_time = Settings::values.custom_rtc.value_or(
|
||||
std::chrono::seconds(QDateTime::currentSecsSinceEpoch()));
|
||||
const auto rtc_time = Settings::values.custom_rtc.value_or(QDateTime::currentSecsSinceEpoch());
|
||||
|
||||
ui->rng_seed_checkbox->setChecked(Settings::values.rng_seed.GetValue().has_value());
|
||||
ui->rng_seed_edit->setEnabled(Settings::values.rng_seed.GetValue().has_value() &&
|
||||
@@ -75,7 +74,7 @@ void ConfigureSystem::SetConfiguration() {
|
||||
|
||||
ui->custom_rtc_checkbox->setChecked(Settings::values.custom_rtc.has_value());
|
||||
ui->custom_rtc_edit->setEnabled(Settings::values.custom_rtc.has_value());
|
||||
ui->custom_rtc_edit->setDateTime(QDateTime::fromSecsSinceEpoch(rtc_time.count()));
|
||||
ui->custom_rtc_edit->setDateTime(QDateTime::fromSecsSinceEpoch(rtc_time));
|
||||
|
||||
if (Settings::IsConfiguringGlobal()) {
|
||||
ui->combo_language->setCurrentIndex(Settings::values.language_index.GetValue());
|
||||
@@ -108,10 +107,9 @@ void ConfigureSystem::ApplyConfiguration() {
|
||||
// to allow in-game time to be fast forwarded
|
||||
if (Settings::IsConfiguringGlobal()) {
|
||||
if (ui->custom_rtc_checkbox->isChecked()) {
|
||||
Settings::values.custom_rtc =
|
||||
std::chrono::seconds(ui->custom_rtc_edit->dateTime().toSecsSinceEpoch());
|
||||
Settings::values.custom_rtc = ui->custom_rtc_edit->dateTime().toSecsSinceEpoch();
|
||||
if (system.IsPoweredOn()) {
|
||||
const s64 posix_time{Settings::values.custom_rtc->count() +
|
||||
const s64 posix_time{*Settings::values.custom_rtc +
|
||||
Service::Time::TimeManager::GetExternalTimeZoneOffset()};
|
||||
system.GetTimeManager().UpdateLocalSystemClockTime(posix_time);
|
||||
}
|
||||
|
||||
@@ -14,14 +14,14 @@
|
||||
<item row="0" column="0" colspan="4">
|
||||
<widget class="QLabel" name="label_1">
|
||||
<property name="text">
|
||||
<string>Reads controller input from scripts in the same format as TAS-nx scripts.<br/>For a more detailed explanation please consult the FAQ on the yuzu website.</string>
|
||||
<string><html><head/><body><p>Reads controller input from scripts in the same format as TAS-nx scripts.<br/>For a more detailed explanation, please consult the <a href="https://yuzu-emu.org/help/feature/tas/"><span style=" text-decoration: underline; color:#039be5;">help page</span></a> on the yuzu website.</p></body></html></string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0" colspan="4">
|
||||
<widget class="QLabel" name="label_2">
|
||||
<property name="text">
|
||||
<string>To check which hotkeys control the playback/recording, please refer to the Hotkey settings (General -> Hotkeys).</string>
|
||||
<string>To check which hotkeys control the playback/recording, please refer to the Hotkey settings (Configure -> General -> Hotkeys).</string>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
|
||||
+2
-2
@@ -192,7 +192,7 @@ static void RemoveCachedContents() {
|
||||
}
|
||||
|
||||
GMainWindow::GMainWindow()
|
||||
: system{std::make_unique<Core::System>()},
|
||||
: ui{std::make_unique<Ui::MainWindow>()}, system{std::make_unique<Core::System>()},
|
||||
input_subsystem{std::make_shared<InputCommon::InputSubsystem>()},
|
||||
config{std::make_unique<Config>(*system)},
|
||||
vfs{std::make_shared<FileSys::RealVfsFilesystem>()},
|
||||
@@ -1666,7 +1666,7 @@ void GMainWindow::OnTransferableShaderCacheOpenFile(u64 program_id) {
|
||||
const auto shader_cache_folder_path{shader_cache_dir / fmt::format("{:016x}", program_id)};
|
||||
if (!Common::FS::CreateDirs(shader_cache_folder_path)) {
|
||||
QMessageBox::warning(this, tr("Error Opening Transferable Shader Cache"),
|
||||
tr("Filed to create the shader cache directory for this title."));
|
||||
tr("Failed to create the shader cache directory for this title."));
|
||||
return;
|
||||
}
|
||||
const auto shader_path_string{Common::FS::PathToUTF8String(shader_cache_folder_path)};
|
||||
|
||||
@@ -412,8 +412,7 @@ void Config::ReadValues() {
|
||||
|
||||
const auto custom_rtc_enabled = sdl2_config->GetBoolean("System", "custom_rtc_enabled", false);
|
||||
if (custom_rtc_enabled) {
|
||||
Settings::values.custom_rtc =
|
||||
std::chrono::seconds(sdl2_config->GetInteger("System", "custom_rtc", 0));
|
||||
Settings::values.custom_rtc = sdl2_config->GetInteger("System", "custom_rtc", 0);
|
||||
} else {
|
||||
Settings::values.custom_rtc = std::nullopt;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user