Compare commits

..

1 Commits

Author SHA1 Message Date
Liam a1c1ad096d common: fix bitfield aliasing on GCC/Clang 2022-07-09 22:43:45 -04:00
33 changed files with 227 additions and 350 deletions
+3 -7
View File
@@ -2,7 +2,6 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#include <limits>
#include <optional>
#include <vector>
#include "audio_core/audio_out.h"
@@ -89,12 +88,9 @@ AudioRenderer::AudioRenderer(Core::Timing::CoreTiming& core_timing_, Core::Memor
stream = audio_out->OpenStream(
core_timing, params.sample_rate, AudioCommon::STREAM_NUM_CHANNELS,
fmt::format("AudioRenderer-Instance{}", instance_number), std::move(release_callback));
process_event =
Core::Timing::CreateEvent(fmt::format("AudioRenderer-Instance{}-Process", instance_number),
[this](std::uintptr_t, s64, std::chrono::nanoseconds) {
ReleaseAndQueueBuffers();
return std::nullopt;
});
process_event = Core::Timing::CreateEvent(
fmt::format("AudioRenderer-Instance{}-Process", instance_number),
[this](std::uintptr_t, std::chrono::nanoseconds) { ReleaseAndQueueBuffers(); });
for (s32 i = 0; i < NUM_BUFFERS; ++i) {
QueueMixedBuffer(i);
}
+2 -3
View File
@@ -34,10 +34,9 @@ Stream::Stream(Core::Timing::CoreTiming& core_timing_, u32 sample_rate_, Format
ReleaseCallback&& release_callback_, SinkStream& sink_stream_, std::string&& name_)
: sample_rate{sample_rate_}, format{format_}, release_callback{std::move(release_callback_)},
sink_stream{sink_stream_}, core_timing{core_timing_}, name{std::move(name_)} {
release_event = Core::Timing::CreateEvent(
name, [this](std::uintptr_t, s64 time, std::chrono::nanoseconds ns_late) {
release_event =
Core::Timing::CreateEvent(name, [this](std::uintptr_t, std::chrono::nanoseconds ns_late) {
ReleaseActiveBuffer(ns_late);
return std::nullopt;
});
}
+9
View File
@@ -146,7 +146,16 @@ public:
}
constexpr void Assign(const T& value) {
#ifdef _MSC_VER
storage = static_cast<StorageType>((storage & ~mask) | FormatValue(value));
#else
// Explicitly reload with memcpy to avoid compiler aliasing quirks
// regarding optimization: GCC/Clang clobber chained stores to
// different bitfields in the same struct with the last value.
StorageTypeWithEndian storage_;
std::memcpy(&storage_, &storage, sizeof(storage_));
storage = static_cast<StorageType>((storage_ & ~mask) | FormatValue(value));
#endif
}
[[nodiscard]] constexpr T Value() const {
+17 -68
View File
@@ -22,11 +22,10 @@ std::shared_ptr<EventType> CreateEvent(std::string name, TimedCallback&& callbac
}
struct CoreTiming::Event {
s64 time;
u64 time;
u64 fifo_order;
std::uintptr_t user_data;
std::weak_ptr<EventType> type;
s64 reschedule_time;
// Sort by time, unless the times are the same, in which case sort by
// the order added to the queue
@@ -59,11 +58,15 @@ void CoreTiming::Initialize(std::function<void()>&& on_thread_init_) {
event_fifo_id = 0;
shutting_down = false;
ticks = 0;
const auto empty_timed_callback = [](std::uintptr_t, u64, std::chrono::nanoseconds)
-> std::optional<std::chrono::nanoseconds> { return std::nullopt; };
const auto empty_timed_callback = [](std::uintptr_t, std::chrono::nanoseconds) {};
ev_lost = CreateEvent("_lost_event", empty_timed_callback);
if (is_multicore) {
worker_threads.emplace_back(ThreadEntry, std::ref(*this), 0);
const auto hardware_concurrency = std::thread::hardware_concurrency();
size_t id = 0;
worker_threads.emplace_back(ThreadEntry, std::ref(*this), id++);
if (hardware_concurrency > 8) {
worker_threads.emplace_back(ThreadEntry, std::ref(*this), id++);
}
}
}
@@ -78,7 +81,6 @@ void CoreTiming::Shutdown() {
thread.join();
}
worker_threads.clear();
pause_callbacks.clear();
ClearPendingEvents();
has_started = false;
}
@@ -96,14 +98,6 @@ void CoreTiming::Pause(bool is_paused_) {
}
}
paused_state.store(is_paused_, std::memory_order_relaxed);
if (!is_paused_) {
pause_end_time = GetGlobalTimeNs().count();
}
for (auto& cb : pause_callbacks) {
cb(is_paused_);
}
}
void CoreTiming::SyncPause(bool is_paused_) {
@@ -127,14 +121,6 @@ void CoreTiming::SyncPause(bool is_paused_) {
wait_signal_cv.wait(main_lock, [this] { return pause_count == 0; });
}
}
if (!is_paused_) {
pause_end_time = GetGlobalTimeNs().count();
}
for (auto& cb : pause_callbacks) {
cb(is_paused_);
}
}
bool CoreTiming::IsRunning() const {
@@ -148,30 +134,12 @@ bool CoreTiming::HasPendingEvents() const {
void CoreTiming::ScheduleEvent(std::chrono::nanoseconds ns_into_future,
const std::shared_ptr<EventType>& event_type,
std::uintptr_t user_data, bool absolute_time) {
std::uintptr_t user_data) {
std::unique_lock main_lock(event_mutex);
const auto next_time{absolute_time ? ns_into_future : GetGlobalTimeNs() + ns_into_future};
const u64 timeout = static_cast<u64>((GetGlobalTimeNs() + ns_into_future).count());
event_queue.emplace_back(Event{next_time.count(), event_fifo_id++, user_data, event_type, 0});
pending_events.fetch_add(1, std::memory_order_relaxed);
std::push_heap(event_queue.begin(), event_queue.end(), std::greater<>());
if (is_multicore) {
event_cv.notify_one();
}
}
void CoreTiming::ScheduleLoopingEvent(std::chrono::nanoseconds start_time,
std::chrono::nanoseconds resched_time,
const std::shared_ptr<EventType>& event_type,
std::uintptr_t user_data, bool absolute_time) {
std::unique_lock main_lock(event_mutex);
const auto next_time{absolute_time ? start_time : GetGlobalTimeNs() + start_time};
event_queue.emplace_back(
Event{next_time.count(), event_fifo_id++, user_data, event_type, resched_time.count()});
event_queue.emplace_back(Event{timeout, event_fifo_id++, user_data, event_type});
pending_events.fetch_add(1, std::memory_order_relaxed);
std::push_heap(event_queue.begin(), event_queue.end(), std::greater<>());
@@ -250,11 +218,6 @@ void CoreTiming::RemoveEvent(const std::shared_ptr<EventType>& event_type) {
}
}
void CoreTiming::RegisterPauseCallback(PauseCallback&& callback) {
std::unique_lock main_lock(event_mutex);
pause_callbacks.emplace_back(std::move(callback));
}
std::optional<s64> CoreTiming::Advance() {
global_timer = GetGlobalTimeNs().count();
@@ -265,31 +228,17 @@ std::optional<s64> CoreTiming::Advance() {
event_queue.pop_back();
if (const auto event_type{evt.type.lock()}) {
sequence_mutex.lock();
event_mutex.unlock();
const auto new_schedule_time{event_type->callback(
evt.user_data, evt.time,
std::chrono::nanoseconds{GetGlobalTimeNs().count() - evt.time})};
event_type->guard.lock();
sequence_mutex.unlock();
const s64 delay = static_cast<s64>(GetGlobalTimeNs().count() - evt.time);
event_type->callback(evt.user_data, std::chrono::nanoseconds{delay});
event_type->guard.unlock();
event_mutex.lock();
pending_events.fetch_sub(1, std::memory_order_relaxed);
if (evt.reschedule_time != 0) {
// If this event was scheduled into a pause, its time now is going to be way behind.
// Re-set this event to continue from the end of the pause.
auto next_time{evt.time + evt.reschedule_time};
if (evt.time < pause_end_time) {
next_time = pause_end_time + evt.reschedule_time;
}
const auto next_schedule_time{new_schedule_time.has_value()
? new_schedule_time.value().count()
: evt.reschedule_time};
event_queue.emplace_back(
Event{next_time, event_fifo_id++, evt.user_data, evt.type, next_schedule_time});
pending_events.fetch_add(1, std::memory_order_relaxed);
std::push_heap(event_queue.begin(), event_queue.end(), std::greater<>());
}
}
global_timer = GetGlobalTimeNs().count();
+6 -19
View File
@@ -20,9 +20,8 @@
namespace Core::Timing {
/// A callback that may be scheduled for a particular core timing event.
using TimedCallback = std::function<std::optional<std::chrono::nanoseconds>(
std::uintptr_t user_data, s64 time, std::chrono::nanoseconds ns_late)>;
using PauseCallback = std::function<void(bool paused)>;
using TimedCallback =
std::function<void(std::uintptr_t user_data, std::chrono::nanoseconds ns_late)>;
/// Contains the characteristics of a particular event.
struct EventType {
@@ -33,6 +32,7 @@ struct EventType {
TimedCallback callback;
/// A pointer to the name of the event.
const std::string name;
mutable std::mutex guard;
};
/**
@@ -94,15 +94,7 @@ public:
/// Schedules an event in core timing
void ScheduleEvent(std::chrono::nanoseconds ns_into_future,
const std::shared_ptr<EventType>& event_type, std::uintptr_t user_data = 0,
bool absolute_time = false);
/// Schedules an event which will automatically re-schedule itself with the given time, until
/// unscheduled
void ScheduleLoopingEvent(std::chrono::nanoseconds start_time,
std::chrono::nanoseconds resched_time,
const std::shared_ptr<EventType>& event_type,
std::uintptr_t user_data = 0, bool absolute_time = false);
const std::shared_ptr<EventType>& event_type, std::uintptr_t user_data = 0);
void UnscheduleEvent(const std::shared_ptr<EventType>& event_type, std::uintptr_t user_data);
@@ -134,9 +126,6 @@ public:
/// Checks for events manually and returns time in nanoseconds for next event, threadsafe.
std::optional<s64> Advance();
/// Register a callback function to be called when coretiming pauses.
void RegisterPauseCallback(PauseCallback&& callback);
private:
struct Event;
@@ -148,7 +137,7 @@ private:
std::unique_ptr<Common::WallClock> clock;
s64 global_timer = 0;
u64 global_timer = 0;
// The queue is a min-heap using std::make_heap/push_heap/pop_heap.
// We don't use std::priority_queue because we need to be able to serialize, unserialize and
@@ -168,19 +157,17 @@ private:
std::condition_variable wait_pause_cv;
std::condition_variable wait_signal_cv;
mutable std::mutex event_mutex;
mutable std::mutex sequence_mutex;
std::atomic<bool> paused_state{};
bool is_paused{};
bool shutting_down{};
bool is_multicore{};
size_t pause_count{};
s64 pause_end_time{};
/// Cycle timing
u64 ticks{};
s64 downcount{};
std::vector<PauseCallback> pause_callbacks{};
};
/// Creates a core timing event with the given name and callback.
+1 -4
View File
@@ -11,14 +11,11 @@ namespace Core::Hardware {
InterruptManager::InterruptManager(Core::System& system_in) : system(system_in) {
gpu_interrupt_event = Core::Timing::CreateEvent(
"GPUInterrupt",
[this](std::uintptr_t message, u64 time,
std::chrono::nanoseconds) -> std::optional<std::chrono::nanoseconds> {
"GPUInterrupt", [this](std::uintptr_t message, std::chrono::nanoseconds) {
auto nvdrv = system.ServiceManager().GetService<Service::Nvidia::NVDRV>("nvdrv");
const u32 syncpt = static_cast<u32>(message >> 32);
const u32 value = static_cast<u32>(message);
nvdrv->SignalGPUInterruptSyncpt(syncpt, value);
return std::nullopt;
});
}
+4 -5
View File
@@ -234,18 +234,17 @@ struct KernelCore::Impl {
void InitializePreemption(KernelCore& kernel) {
preemption_event = Core::Timing::CreateEvent(
"PreemptionCallback",
[this, &kernel](std::uintptr_t, s64 time,
std::chrono::nanoseconds) -> std::optional<std::chrono::nanoseconds> {
"PreemptionCallback", [this, &kernel](std::uintptr_t, std::chrono::nanoseconds) {
{
KScopedSchedulerLock lock(kernel);
global_scheduler_context->PreemptThreads();
}
return std::nullopt;
const auto time_interval = std::chrono::nanoseconds{std::chrono::milliseconds(10)};
system.CoreTiming().ScheduleEvent(time_interval, preemption_event);
});
const auto time_interval = std::chrono::nanoseconds{std::chrono::milliseconds(10)};
system.CoreTiming().ScheduleLoopingEvent(time_interval, time_interval, preemption_event);
system.CoreTiming().ScheduleEvent(time_interval, preemption_event);
}
void InitializeShutdownThreads() {
+9 -11
View File
@@ -11,17 +11,15 @@
namespace Kernel {
TimeManager::TimeManager(Core::System& system_) : system{system_} {
time_manager_event_type = Core::Timing::CreateEvent(
"Kernel::TimeManagerCallback",
[this](std::uintptr_t thread_handle, s64 time,
std::chrono::nanoseconds) -> std::optional<std::chrono::nanoseconds> {
KThread* thread = reinterpret_cast<KThread*>(thread_handle);
{
KScopedSchedulerLock sl(system.Kernel());
thread->OnTimer();
}
return std::nullopt;
});
time_manager_event_type =
Core::Timing::CreateEvent("Kernel::TimeManagerCallback",
[this](std::uintptr_t thread_handle, std::chrono::nanoseconds) {
KThread* thread = reinterpret_cast<KThread*>(thread_handle);
{
KScopedSchedulerLock sl(system.Kernel());
thread->OnTimer();
}
});
}
void TimeManager::ScheduleTimeEvent(KThread* thread, s64 nanoseconds) {
+27 -14
View File
@@ -74,34 +74,26 @@ IAppletResource::IAppletResource(Core::System& system_,
// Register update callbacks
pad_update_event = Core::Timing::CreateEvent(
"HID::UpdatePadCallback",
[this](std::uintptr_t user_data, s64 time,
std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> {
[this](std::uintptr_t user_data, std::chrono::nanoseconds ns_late) {
const auto guard = LockService();
UpdateControllers(user_data, ns_late);
return std::nullopt;
});
mouse_keyboard_update_event = Core::Timing::CreateEvent(
"HID::UpdateMouseKeyboardCallback",
[this](std::uintptr_t user_data, s64 time,
std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> {
[this](std::uintptr_t user_data, std::chrono::nanoseconds ns_late) {
const auto guard = LockService();
UpdateMouseKeyboard(user_data, ns_late);
return std::nullopt;
});
motion_update_event = Core::Timing::CreateEvent(
"HID::UpdateMotionCallback",
[this](std::uintptr_t user_data, s64 time,
std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> {
[this](std::uintptr_t user_data, std::chrono::nanoseconds ns_late) {
const auto guard = LockService();
UpdateMotion(user_data, ns_late);
return std::nullopt;
});
system.CoreTiming().ScheduleLoopingEvent(pad_update_ns, pad_update_ns, pad_update_event);
system.CoreTiming().ScheduleLoopingEvent(mouse_keyboard_update_ns, mouse_keyboard_update_ns,
mouse_keyboard_update_event);
system.CoreTiming().ScheduleLoopingEvent(motion_update_ns, motion_update_ns,
motion_update_event);
system.CoreTiming().ScheduleEvent(pad_update_ns, pad_update_event);
system.CoreTiming().ScheduleEvent(mouse_keyboard_update_ns, mouse_keyboard_update_event);
system.CoreTiming().ScheduleEvent(motion_update_ns, motion_update_event);
system.HIDCore().ReloadInputDevices();
}
@@ -143,6 +135,13 @@ void IAppletResource::UpdateControllers(std::uintptr_t user_data,
}
controller->OnUpdate(core_timing);
}
// If ns_late is higher than the update rate ignore the delay
if (ns_late > pad_update_ns) {
ns_late = {};
}
core_timing.ScheduleEvent(pad_update_ns - ns_late, pad_update_event);
}
void IAppletResource::UpdateMouseKeyboard(std::uintptr_t user_data,
@@ -151,12 +150,26 @@ void IAppletResource::UpdateMouseKeyboard(std::uintptr_t user_data,
controllers[static_cast<size_t>(HidController::Mouse)]->OnUpdate(core_timing);
controllers[static_cast<size_t>(HidController::Keyboard)]->OnUpdate(core_timing);
// If ns_late is higher than the update rate ignore the delay
if (ns_late > mouse_keyboard_update_ns) {
ns_late = {};
}
core_timing.ScheduleEvent(mouse_keyboard_update_ns - ns_late, mouse_keyboard_update_event);
}
void IAppletResource::UpdateMotion(std::uintptr_t user_data, std::chrono::nanoseconds ns_late) {
auto& core_timing = system.CoreTiming();
controllers[static_cast<size_t>(HidController::NPad)]->OnMotionUpdate(core_timing);
// If ns_late is higher than the update rate ignore the delay
if (ns_late > motion_update_ns) {
ns_late = {};
}
core_timing.ScheduleEvent(motion_update_ns - ns_late, motion_update_event);
}
class IActiveVibrationDeviceList final : public ServiceFramework<IActiveVibrationDeviceList> {
+11 -5
View File
@@ -50,15 +50,12 @@ HidBus::HidBus(Core::System& system_)
// Register update callbacks
hidbus_update_event = Core::Timing::CreateEvent(
"Hidbus::UpdateCallback",
[this](std::uintptr_t user_data, s64 time,
std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> {
[this](std::uintptr_t user_data, std::chrono::nanoseconds ns_late) {
const auto guard = LockService();
UpdateHidbus(user_data, ns_late);
return std::nullopt;
});
system_.CoreTiming().ScheduleLoopingEvent(hidbus_update_ns, hidbus_update_ns,
hidbus_update_event);
system_.CoreTiming().ScheduleEvent(hidbus_update_ns, hidbus_update_event);
}
HidBus::~HidBus() {
@@ -66,6 +63,8 @@ HidBus::~HidBus() {
}
void HidBus::UpdateHidbus(std::uintptr_t user_data, std::chrono::nanoseconds ns_late) {
auto& core_timing = system.CoreTiming();
if (is_hidbus_enabled) {
for (std::size_t i = 0; i < devices.size(); ++i) {
if (!devices[i].is_device_initializated) {
@@ -83,6 +82,13 @@ void HidBus::UpdateHidbus(std::uintptr_t user_data, std::chrono::nanoseconds ns_
sizeof(HidbusStatusManagerEntry));
}
}
// If ns_late is higher than the update rate ignore the delay
if (ns_late > hidbus_update_ns) {
ns_late = {};
}
core_timing.ScheduleEvent(hidbus_update_ns - ns_late, hidbus_update_event);
}
std::optional<std::size_t> HidBus::GetDeviceIndexFromHandle(BusHandle handle) const {
+7 -6
View File
@@ -67,20 +67,21 @@ NVFlinger::NVFlinger(Core::System& system_, HosBinderDriverServer& hos_binder_dr
// Schedule the screen composition events
composition_event = Core::Timing::CreateEvent(
"ScreenComposition",
[this](std::uintptr_t, s64 time,
std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> {
"ScreenComposition", [this](std::uintptr_t, std::chrono::nanoseconds ns_late) {
const auto lock_guard = Lock();
Compose();
return std::max(std::chrono::nanoseconds::zero(),
std::chrono::nanoseconds(GetNextTicks()) - ns_late);
const auto ticks = std::chrono::nanoseconds{GetNextTicks()};
const auto ticks_delta = ticks - ns_late;
const auto future_ns = std::max(std::chrono::nanoseconds::zero(), ticks_delta);
this->system.CoreTiming().ScheduleEvent(future_ns, composition_event);
});
if (system.IsMulticore()) {
vsync_thread = std::jthread([this](std::stop_token token) { SplitVSync(token); });
} else {
system.CoreTiming().ScheduleLoopingEvent(frame_ns, frame_ns, composition_event);
system.CoreTiming().ScheduleEvent(frame_ns, composition_event);
}
}
+4 -4
View File
@@ -184,12 +184,10 @@ CheatEngine::~CheatEngine() {
void CheatEngine::Initialize() {
event = Core::Timing::CreateEvent(
"CheatEngine::FrameCallback::" + Common::HexToString(metadata.main_nso_build_id),
[this](std::uintptr_t user_data, s64 time,
std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> {
[this](std::uintptr_t user_data, std::chrono::nanoseconds ns_late) {
FrameCallback(user_data, ns_late);
return std::nullopt;
});
core_timing.ScheduleLoopingEvent(CHEAT_ENGINE_NS, CHEAT_ENGINE_NS, event);
core_timing.ScheduleEvent(CHEAT_ENGINE_NS, event);
metadata.process_id = system.CurrentProcess()->GetProcessID();
metadata.title_id = system.GetCurrentProcessProgramID();
@@ -239,6 +237,8 @@ void CheatEngine::FrameCallback(std::uintptr_t, std::chrono::nanoseconds ns_late
MICROPROFILE_SCOPE(Cheat_Engine);
vm.Execute(metadata);
core_timing.ScheduleEvent(CHEAT_ENGINE_NS - ns_late, event);
}
} // namespace Core::Memory
+1 -3
View File
@@ -53,10 +53,8 @@ Freezer::Freezer(Core::Timing::CoreTiming& core_timing_, Core::Memory::Memory& m
: core_timing{core_timing_}, memory{memory_} {
event = Core::Timing::CreateEvent(
"MemoryFreezer::FrameCallback",
[this](std::uintptr_t user_data, s64 time,
std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> {
[this](std::uintptr_t user_data, std::chrono::nanoseconds ns_late) {
FrameCallback(user_data, ns_late);
return std::nullopt;
});
core_timing.ScheduleEvent(memory_freezer_ns, event);
}
+1 -4
View File
@@ -9,7 +9,6 @@
#include <cstdlib>
#include <memory>
#include <mutex>
#include <optional>
#include <string>
#include "core/core.h"
@@ -26,15 +25,13 @@ u64 expected_callback = 0;
std::mutex control_mutex;
template <unsigned int IDX>
std::optional<std::chrono::nanoseconds> HostCallbackTemplate(std::uintptr_t user_data, s64 time,
std::chrono::nanoseconds ns_late) {
void HostCallbackTemplate(std::uintptr_t user_data, std::chrono::nanoseconds ns_late) {
std::unique_lock<std::mutex> lk(control_mutex);
static_assert(IDX < CB_IDS.size(), "IDX out of range");
callbacks_ran_flags.set(IDX);
REQUIRE(CB_IDS[IDX] == user_data);
delays[IDX] = ns_late.count();
++expected_callback;
return std::nullopt;
}
struct ScopeInit final {
+1 -5
View File
@@ -131,12 +131,9 @@ constexpr std::array VIEW_CLASS_ASTC_8x8_RGBA{
// PixelFormat::ASTC_2D_10X5_SRGB
// Missing formats:
// PixelFormat::ASTC_2D_10X6_UNORM
// PixelFormat::ASTC_2D_10X6_SRGB
constexpr std::array VIEW_CLASS_ASTC_10x6_RGBA{
PixelFormat::ASTC_2D_10X6_UNORM,
};
constexpr std::array VIEW_CLASS_ASTC_10x8_RGBA{
PixelFormat::ASTC_2D_10X8_UNORM,
PixelFormat::ASTC_2D_10X8_SRGB,
@@ -229,7 +226,6 @@ constexpr Table MakeViewTable() {
EnableRange(view, VIEW_CLASS_ASTC_6x6_RGBA);
EnableRange(view, VIEW_CLASS_ASTC_8x5_RGBA);
EnableRange(view, VIEW_CLASS_ASTC_8x8_RGBA);
EnableRange(view, VIEW_CLASS_ASTC_10x6_RGBA);
EnableRange(view, VIEW_CLASS_ASTC_10x8_RGBA);
EnableRange(view, VIEW_CLASS_ASTC_10x10_RGBA);
EnableRange(view, VIEW_CLASS_ASTC_12x12_RGBA);
@@ -98,7 +98,6 @@ constexpr std::array<FormatTuple, VideoCore::Surface::MaxPixelFormat> FORMAT_TAB
{GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x8_KHR}, // ASTC_2D_10X8_SRGB
{GL_COMPRESSED_RGBA_ASTC_6x6_KHR}, // ASTC_2D_6X6_UNORM
{GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x6_KHR}, // ASTC_2D_6X6_SRGB
{GL_COMPRESSED_RGBA_ASTC_10x6_KHR}, // ASTC_2D_10X6_UNORM
{GL_COMPRESSED_RGBA_ASTC_10x10_KHR}, // ASTC_2D_10X10_UNORM
{GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x10_KHR}, // ASTC_2D_10X10_SRGB
{GL_COMPRESSED_RGBA_ASTC_12x12_KHR}, // ASTC_2D_12X12_UNORM
@@ -195,7 +195,6 @@ struct FormatTuple {
{VK_FORMAT_ASTC_10x8_SRGB_BLOCK}, // ASTC_2D_10X8_SRGB
{VK_FORMAT_ASTC_6x6_UNORM_BLOCK}, // ASTC_2D_6X6_UNORM
{VK_FORMAT_ASTC_6x6_SRGB_BLOCK}, // ASTC_2D_6X6_SRGB
{VK_FORMAT_ASTC_10x6_UNORM_BLOCK}, // ASTC_2D_10X6_UNORM
{VK_FORMAT_ASTC_10x10_UNORM_BLOCK}, // ASTC_2D_10X10_UNORM
{VK_FORMAT_ASTC_10x10_SRGB_BLOCK}, // ASTC_2D_10X10_SRGB
{VK_FORMAT_ASTC_12x12_UNORM_BLOCK}, // ASTC_2D_12X12_UNORM
-1
View File
@@ -247,7 +247,6 @@ bool IsPixelFormatASTC(PixelFormat format) {
case PixelFormat::ASTC_2D_10X8_SRGB:
case PixelFormat::ASTC_2D_6X6_UNORM:
case PixelFormat::ASTC_2D_6X6_SRGB:
case PixelFormat::ASTC_2D_10X6_UNORM:
case PixelFormat::ASTC_2D_10X10_UNORM:
case PixelFormat::ASTC_2D_10X10_SRGB:
case PixelFormat::ASTC_2D_12X12_UNORM:
-4
View File
@@ -94,7 +94,6 @@ enum class PixelFormat {
ASTC_2D_10X8_SRGB,
ASTC_2D_6X6_UNORM,
ASTC_2D_6X6_SRGB,
ASTC_2D_10X6_UNORM,
ASTC_2D_10X10_UNORM,
ASTC_2D_10X10_SRGB,
ASTC_2D_12X12_UNORM,
@@ -228,7 +227,6 @@ constexpr std::array<u8, MaxPixelFormat> BLOCK_WIDTH_TABLE = {{
10, // ASTC_2D_10X8_SRGB
6, // ASTC_2D_6X6_UNORM
6, // ASTC_2D_6X6_SRGB
10, // ASTC_2D_10X6_UNORM
10, // ASTC_2D_10X10_UNORM
10, // ASTC_2D_10X10_SRGB
12, // ASTC_2D_12X12_UNORM
@@ -331,7 +329,6 @@ constexpr std::array<u8, MaxPixelFormat> BLOCK_HEIGHT_TABLE = {{
8, // ASTC_2D_10X8_SRGB
6, // ASTC_2D_6X6_UNORM
6, // ASTC_2D_6X6_SRGB
6, // ASTC_2D_10X6_UNORM
10, // ASTC_2D_10X10_UNORM
10, // ASTC_2D_10X10_SRGB
12, // ASTC_2D_12X12_UNORM
@@ -434,7 +431,6 @@ constexpr std::array<u8, MaxPixelFormat> BITS_PER_BLOCK_TABLE = {{
128, // ASTC_2D_10X8_SRGB
128, // ASTC_2D_6X6_UNORM
128, // ASTC_2D_6X6_SRGB
128, // ASTC_2D_10X6_UNORM
128, // ASTC_2D_10X10_UNORM
128, // ASTC_2D_10X10_SRGB
128, // ASTC_2D_12X12_UNORM
@@ -206,8 +206,6 @@ PixelFormat PixelFormatFromTextureInfo(TextureFormat format, ComponentType red,
return PixelFormat::ASTC_2D_6X6_UNORM;
case Hash(TextureFormat::ASTC_2D_6X6, UNORM, SRGB):
return PixelFormat::ASTC_2D_6X6_SRGB;
case Hash(TextureFormat::ASTC_2D_10X6, UNORM, LINEAR):
return PixelFormat::ASTC_2D_10X6_UNORM;
case Hash(TextureFormat::ASTC_2D_10X10, UNORM, LINEAR):
return PixelFormat::ASTC_2D_10X10_UNORM;
case Hash(TextureFormat::ASTC_2D_10X10, UNORM, SRGB):
-2
View File
@@ -175,8 +175,6 @@ struct fmt::formatter<VideoCore::Surface::PixelFormat> : fmt::formatter<fmt::str
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_10X10_UNORM:
return "ASTC_2D_10X10_UNORM";
case PixelFormat::ASTC_2D_10X10_SRGB:
+2 -2
View File
@@ -30,6 +30,8 @@ add_executable(yuzu
applets/qt_web_browser_scripts.h
bootmanager.cpp
bootmanager.h
check_vulkan.cpp
check_vulkan.h
compatdb.ui
compatibility_list.cpp
compatibility_list.h
@@ -153,8 +155,6 @@ add_executable(yuzu
main.cpp
main.h
main.ui
startup_checks.cpp
startup_checks.h
uisettings.cpp
uisettings.h
util/controller_navigation.cpp
+53
View File
@@ -0,0 +1,53 @@
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "video_core/vulkan_common/vulkan_wrapper.h"
#include <filesystem>
#include <fstream>
#include "common/fs/fs.h"
#include "common/fs/path_util.h"
#include "common/logging/log.h"
#include "video_core/vulkan_common/vulkan_instance.h"
#include "video_core/vulkan_common/vulkan_library.h"
#include "yuzu/check_vulkan.h"
#include "yuzu/uisettings.h"
constexpr char TEMP_FILE_NAME[] = "vulkan_check";
bool CheckVulkan() {
if (UISettings::values.has_broken_vulkan) {
return true;
}
LOG_DEBUG(Frontend, "Checking presence of Vulkan");
const auto fs_config_loc = Common::FS::GetYuzuPath(Common::FS::YuzuPath::ConfigDir);
const auto temp_file_loc = fs_config_loc / TEMP_FILE_NAME;
if (std::filesystem::exists(temp_file_loc)) {
LOG_WARNING(Frontend, "Detected recovery from previous failed Vulkan initialization");
UISettings::values.has_broken_vulkan = true;
std::filesystem::remove(temp_file_loc);
return false;
}
std::ofstream temp_file_handle(temp_file_loc);
temp_file_handle.close();
try {
Vulkan::vk::InstanceDispatch dld;
const Common::DynamicLibrary library = Vulkan::OpenLibrary();
const Vulkan::vk::Instance instance =
Vulkan::CreateInstance(library, dld, VK_API_VERSION_1_0);
} catch (const Vulkan::vk::Exception& exception) {
LOG_ERROR(Frontend, "Failed to initialize Vulkan: {}", exception.what());
// Don't set has_broken_vulkan to true here: we care when loading Vulkan crashes the
// application, not when we can handle it.
}
std::filesystem::remove(temp_file_loc);
return true;
}
+6
View File
@@ -0,0 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
bool CheckVulkan();
+8
View File
@@ -682,6 +682,12 @@ void Config::ReadRendererValues() {
ReadGlobalSetting(Settings::values.bg_green);
ReadGlobalSetting(Settings::values.bg_blue);
if (!global && UISettings::values.has_broken_vulkan &&
Settings::values.renderer_backend.GetValue() == Settings::RendererBackend::Vulkan &&
!Settings::values.renderer_backend.UsingGlobal()) {
Settings::values.renderer_backend.SetGlobal(true);
}
if (global) {
ReadBasicSetting(Settings::values.renderer_debug);
ReadBasicSetting(Settings::values.renderer_shader_feedback);
@@ -801,6 +807,7 @@ void Config::ReadUIValues() {
ReadBasicSetting(UISettings::values.pause_when_in_background);
ReadBasicSetting(UISettings::values.mute_when_in_background);
ReadBasicSetting(UISettings::values.hide_mouse);
ReadBasicSetting(UISettings::values.has_broken_vulkan);
ReadBasicSetting(UISettings::values.disable_web_applet);
qt_config->endGroup();
@@ -1348,6 +1355,7 @@ void Config::SaveUIValues() {
WriteBasicSetting(UISettings::values.pause_when_in_background);
WriteBasicSetting(UISettings::values.mute_when_in_background);
WriteBasicSetting(UISettings::values.hide_mouse);
WriteBasicSetting(UISettings::values.has_broken_vulkan);
WriteBasicSetting(UISettings::values.disable_web_applet);
qt_config->endGroup();
+31 -6
View File
@@ -58,9 +58,24 @@ ConfigureGraphics::ConfigureGraphics(const Core::System& system_, QWidget* paren
UpdateBackgroundColorButton(new_bg_color);
});
ui->api->setEnabled(!UISettings::values.has_broken_vulkan);
ui->api_widget->setEnabled(!UISettings::values.has_broken_vulkan ||
Settings::IsConfiguringGlobal());
connect(ui->button_check_vulkan, &QAbstractButton::clicked, this, [this] {
UISettings::values.has_broken_vulkan = false;
if (RetrieveVulkanDevices()) {
ui->api->setEnabled(true);
ui->button_check_vulkan->hide();
for (const auto& device : vulkan_devices) {
ui->device->addItem(device);
}
} else {
UISettings::values.has_broken_vulkan = true;
}
});
ui->api->setEnabled(!UISettings::values.has_broken_vulkan.GetValue());
ui->button_check_vulkan->setVisible(UISettings::values.has_broken_vulkan.GetValue());
ui->bg_label->setVisible(Settings::IsConfiguringGlobal());
ui->bg_combobox->setVisible(!Settings::IsConfiguringGlobal());
}
@@ -300,7 +315,7 @@ void ConfigureGraphics::UpdateAPILayout() {
vulkan_device = Settings::values.vulkan_device.GetValue(true);
shader_backend = Settings::values.shader_backend.GetValue(true);
ui->device_widget->setEnabled(false);
ui->backend_widget->setEnabled(false);
ui->backend_widget->setEnabled(UISettings::values.has_broken_vulkan.GetValue());
} else {
vulkan_device = Settings::values.vulkan_device.GetValue();
shader_backend = Settings::values.shader_backend.GetValue();
@@ -322,9 +337,9 @@ void ConfigureGraphics::UpdateAPILayout() {
}
}
void ConfigureGraphics::RetrieveVulkanDevices() try {
bool ConfigureGraphics::RetrieveVulkanDevices() try {
if (UISettings::values.has_broken_vulkan) {
return;
return false;
}
using namespace Vulkan;
@@ -340,8 +355,11 @@ void ConfigureGraphics::RetrieveVulkanDevices() try {
const std::string name = vk::PhysicalDevice(device, dld).GetProperties().deviceName;
vulkan_devices.push_back(QString::fromStdString(name));
}
return true;
} catch (const Vulkan::vk::Exception& exception) {
LOG_ERROR(Frontend, "Failed to enumerate devices with error: {}", exception.what());
return false;
}
Settings::RendererBackend ConfigureGraphics::GetCurrentGraphicsBackend() const {
@@ -422,4 +440,11 @@ void ConfigureGraphics::SetupPerGameUI() {
ui->api, static_cast<int>(Settings::values.renderer_backend.GetValue(true)));
ConfigurationShared::InsertGlobalItem(
ui->nvdec_emulation, static_cast<int>(Settings::values.nvdec_emulation.GetValue(true)));
if (UISettings::values.has_broken_vulkan) {
ui->backend_widget->setEnabled(true);
ConfigurationShared::SetColoredComboBox(
ui->backend, ui->backend_widget,
static_cast<int>(Settings::values.shader_backend.GetValue(true)));
}
}
+1 -1
View File
@@ -41,7 +41,7 @@ private:
void UpdateDeviceSelection(int device);
void UpdateShaderBackendSelection(int backend);
void RetrieveVulkanDevices();
bool RetrieveVulkanDevices();
void SetupPerGameUI();
+8 -1
View File
@@ -6,7 +6,7 @@
<rect>
<x>0</x>
<y>0</y>
<width>541</width>
<width>471</width>
<height>759</height>
</rect>
</property>
@@ -574,6 +574,13 @@
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="button_check_vulkan">
<property name="text">
<string>Check for Working Vulkan</string>
</property>
</widget>
</item>
</layout>
</widget>
<resources/>
+13 -16
View File
@@ -115,6 +115,7 @@ static FileSys::VirtualFile VfsDirectoryCreateFileWrapper(const FileSys::Virtual
#include "video_core/shader_notify.h"
#include "yuzu/about_dialog.h"
#include "yuzu/bootmanager.h"
#include "yuzu/check_vulkan.h"
#include "yuzu/compatdb.h"
#include "yuzu/compatibility_list.h"
#include "yuzu/configuration/config.h"
@@ -130,7 +131,6 @@ static FileSys::VirtualFile VfsDirectoryCreateFileWrapper(const FileSys::Virtual
#include "yuzu/install_dialog.h"
#include "yuzu/loading_screen.h"
#include "yuzu/main.h"
#include "yuzu/startup_checks.h"
#include "yuzu/uisettings.h"
using namespace Common::Literals;
@@ -252,7 +252,7 @@ static QString PrettyProductName() {
return QSysInfo::prettyProductName();
}
GMainWindow::GMainWindow(bool has_broken_vulkan)
GMainWindow::GMainWindow()
: 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)},
@@ -352,15 +352,17 @@ GMainWindow::GMainWindow(bool has_broken_vulkan)
MigrateConfigFiles();
if (has_broken_vulkan) {
UISettings::values.has_broken_vulkan = true;
QMessageBox::warning(this, tr("Broken Vulkan Installation Detected"),
tr("Vulkan initialization failed during boot.<br><br>Click <a "
"href='https://yuzu-emu.org/wiki/faq/"
"#yuzu-starts-with-the-error-broken-vulkan-installation-detected'>"
"here for instructions to fix the issue</a>."));
if (!CheckVulkan()) {
config->Save();
QMessageBox::warning(
this, tr("Broken Vulkan Installation Detected"),
tr("Vulkan initialization failed on the previous boot.<br><br>Click <a "
"href='https://yuzu-emu.org/wiki/faq/"
"#yuzu-starts-with-the-error-broken-vulkan-installation-detected'>here for "
"instructions to fix the issue</a>."));
}
if (UISettings::values.has_broken_vulkan) {
Settings::values.renderer_backend = Settings::RendererBackend::OpenGL;
renderer_status_button->setDisabled(true);
@@ -3851,11 +3853,6 @@ void GMainWindow::SetDiscordEnabled([[maybe_unused]] bool state) {
#endif
int main(int argc, char* argv[]) {
bool has_broken_vulkan = false;
if (StartupChecks(argv[0], &has_broken_vulkan)) {
return 0;
}
Common::DetachedTasks detached_tasks;
MicroProfileOnThreadCreate("Frontend");
SCOPE_EXIT({ MicroProfileShutdown(); });
@@ -3895,7 +3892,7 @@ int main(int argc, char* argv[]) {
// generating shaders
setlocale(LC_ALL, "C");
GMainWindow main_window{has_broken_vulkan};
GMainWindow main_window{};
// After settings have been loaded by GMainWindow, apply the filter
main_window.show();
+1 -1
View File
@@ -118,7 +118,7 @@ class GMainWindow : public QMainWindow {
public:
void filterBarSetChecked(bool state);
void UpdateUITheme();
explicit GMainWindow(bool has_broken_vulkan);
explicit GMainWindow();
~GMainWindow() override;
bool DropAction(QDropEvent* event);
-136
View File
@@ -1,136 +0,0 @@
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "video_core/vulkan_common/vulkan_wrapper.h"
#ifdef _WIN32
#include <cstring> // for memset, strncpy
#include <processthreadsapi.h>
#include <windows.h>
#elif defined(YUZU_UNIX)
#include <errno.h>
#include <sys/wait.h>
#include <unistd.h>
#endif
#include <cstdio>
#include "video_core/vulkan_common/vulkan_instance.h"
#include "video_core/vulkan_common/vulkan_library.h"
#include "yuzu/startup_checks.h"
void CheckVulkan() {
// Just start the Vulkan loader, this will crash if something is wrong
try {
Vulkan::vk::InstanceDispatch dld;
const Common::DynamicLibrary library = Vulkan::OpenLibrary();
const Vulkan::vk::Instance instance =
Vulkan::CreateInstance(library, dld, VK_API_VERSION_1_0);
} catch (const Vulkan::vk::Exception& exception) {
std::fprintf(stderr, "Failed to initialize Vulkan: %s\n", exception.what());
}
}
bool StartupChecks(const char* arg0, bool* has_broken_vulkan) {
#ifdef _WIN32
// Check environment variable to see if we are the child
char variable_contents[8];
const DWORD startup_check_var =
GetEnvironmentVariableA(STARTUP_CHECK_ENV_VAR, variable_contents, 8);
if (startup_check_var > 0 && std::strncmp(variable_contents, "ON", 8) == 0) {
CheckVulkan();
return true;
}
// Set the startup variable for child processes
const bool env_var_set = SetEnvironmentVariableA(STARTUP_CHECK_ENV_VAR, "ON");
if (!env_var_set) {
std::fprintf(stderr, "SetEnvironmentVariableA failed to set %s with error %d\n",
STARTUP_CHECK_ENV_VAR, GetLastError());
return false;
}
PROCESS_INFORMATION process_info;
std::memset(&process_info, '\0', sizeof(process_info));
if (!SpawnChild(arg0, &process_info)) {
return false;
}
// Wait until the processs exits and get exit code from it
WaitForSingleObject(process_info.hProcess, INFINITE);
DWORD exit_code = STILL_ACTIVE;
const int err = GetExitCodeProcess(process_info.hProcess, &exit_code);
if (err == 0) {
std::fprintf(stderr, "GetExitCodeProcess failed with error %d\n", GetLastError());
}
// Vulkan is broken if the child crashed (return value is not zero)
*has_broken_vulkan = (exit_code != 0);
if (CloseHandle(process_info.hProcess) == 0) {
std::fprintf(stderr, "CloseHandle failed with error %d\n", GetLastError());
}
if (CloseHandle(process_info.hThread) == 0) {
std::fprintf(stderr, "CloseHandle failed with error %d\n", GetLastError());
}
if (!SetEnvironmentVariableA(STARTUP_CHECK_ENV_VAR, nullptr)) {
std::fprintf(stderr, "SetEnvironmentVariableA failed to clear %s with error %d\n",
STARTUP_CHECK_ENV_VAR, GetLastError());
}
#elif defined(YUZU_UNIX)
const pid_t pid = fork();
if (pid == 0) {
CheckVulkan();
return true;
} else if (pid == -1) {
const int err = errno;
std::fprintf(stderr, "fork failed with error %d\n", err);
return false;
}
// Get exit code from child process
int status;
const int r_val = wait(&status);
if (r_val == -1) {
const int err = errno;
std::fprintf(stderr, "wait failed with error %d\n", err);
return false;
}
// Vulkan is broken if the child crashed (return value is not zero)
*has_broken_vulkan = (status != 0);
#endif
return false;
}
#ifdef _WIN32
bool SpawnChild(const char* arg0, PROCESS_INFORMATION* pi) {
STARTUPINFOA startup_info;
std::memset(&startup_info, '\0', sizeof(startup_info));
startup_info.cb = sizeof(startup_info);
char p_name[255];
std::strncpy(p_name, arg0, 255);
const bool process_created = CreateProcessA(nullptr, // lpApplicationName
p_name, // lpCommandLine
nullptr, // lpProcessAttributes
nullptr, // lpThreadAttributes
false, // bInheritHandles
0, // dwCreationFlags
nullptr, // lpEnvironment
nullptr, // lpCurrentDirectory
&startup_info, // lpStartupInfo
pi // lpProcessInformation
);
if (!process_created) {
std::fprintf(stderr, "CreateProcessA failed with error %d\n", GetLastError());
return false;
}
return true;
}
#endif
-17
View File
@@ -1,17 +0,0 @@
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#ifdef _WIN32
#include <windows.h>
#endif
constexpr char STARTUP_CHECK_ENV_VAR[] = "YUZU_DO_STARTUP_CHECKS";
void CheckVulkan();
bool StartupChecks(const char* arg0, bool* has_broken_vulkan);
#ifdef _WIN32
bool SpawnChild(const char* arg0, PROCESS_INFORMATION* pi);
#endif
+1 -1
View File
@@ -78,7 +78,7 @@ struct Values {
Settings::Setting<bool> mute_when_in_background{false, "muteWhenInBackground"};
Settings::Setting<bool> hide_mouse{true, "hideInactiveMouse"};
// Set when Vulkan is known to crash the application
bool has_broken_vulkan = false;
Settings::Setting<bool> has_broken_vulkan{false, "has_broken_vulkan"};
Settings::Setting<bool> select_user_on_boot{false, "select_user_on_boot"};