Compare commits

..

17 Commits

Author SHA1 Message Date
lat9nq
a71498d163 qt_common: Remove yuzu prefix 2023-05-03 18:11:53 -04:00
lat9nq
35e7f36a39 configure_graphics: No there isn't a hyphen in VSync
Most sources seem to suggest VSync and not V-Sync
2023-05-02 21:52:43 -04:00
lat9nq
d82cad3fb3 configure_input_player: Add missing include
Cleaning up includes in bootmanager and configure_graphics has exposed a
missing include here.
2023-05-02 21:52:43 -04:00
lat9nq
cd2981ee12 configure_graphics: Clean up includes [IWYU] 2023-05-02 21:52:43 -04:00
lat9nq
0c0f5b7ccc bootmanager: Clean up includes [IWYU]
bootmanager: Remove system-specific headers

IWYU can be too complete I suppose.
2023-05-02 21:52:43 -04:00
lat9nq
a546ecbb12 configure_graphics: Actively find present modes
When Vulkan devices are enumerated, this also determines the available
present modes for each device, maps them to a vector, and gives
those options to the user.
OpenGL options are limited to On/Off.

Required creating a VkSurfaceKHR during device enumeration, which
may or may not be desireable. For the sake of a less confusing UI.

Also fixes a bug where if a graphics device disappears on the host, we
don't try and select the non-existant devices.

configure_graphics: Remove vsync runtime lock for Vulkan

configure_graphics: Recommend Mailbox present mode

configure_graphics: Fix type-limits warning

configure_graphics: Clean up includes

configure_graphics: Add tooltip
2023-05-02 21:52:43 -04:00
lat9nq
952b271092 vk_swapchain: Use certain modes for unlocked
Uses mailbox, then immediate for unlocked framerate depending on
support for either. Also adds support for FIFO_RELAXED.

This function now assumes vsync_mode was originially configured to a value
that the driver supports.

vk_swapchain: ChooseSwapPresentMode determines updates

Simplifies swapchain a bit and allows us to change the present mode
during guest runtime.

vk_swapchain: Fix MSVC error

vk_swapchain: Enforce available present modes

Some frontends don't check the value of vsync_mode before comitting it.
Just as well, since a driver update or misconfiguration could problems
in the swap chain.

vk_swapchain: Silence warnings

Silences GCC warnings implicit-fallthrough and shadow, which apparently
are not enabled on clang.
2023-05-02 21:52:43 -04:00
lat9nq
a090a380be bootmanager: Remove inaccurate switch
Those vulkan settings do not correspond 1:1 to the swap intervals that
they set for OpenGL, so remove it.

bootmanager: Add missing include

I didn't add this log why did it break
2023-05-02 21:51:30 -04:00
lat9nq
cbd79df233 qt_common: Move window info function out of bootmanager
Function is useful outside of bootmanager, so put it in a common place.

qt_common: Add missing include

qt_common: Add some newlines

qt_common: Add trailing newline

qt_common: Add trainline newline
2023-05-02 21:51:30 -04:00
lat9nq
c6c11c1553 vulkan_surface: Pass only window info for surface creation
We don't need the whole EmuWindow when creating a surface,
and it creates onerous requirements outside of typical usage for
creating a surface elsewhere.
2023-05-02 21:51:30 -04:00
lat9nq
2528cf7c54 settings: Enable FIFO relaxed
Not entirely sure if we need this, but there's also no reason not to
support it.

settings: Give VSyncMode values
2023-05-02 21:51:30 -04:00
lat9nq
6b973c5986 configure_graphics: Fix another typo 2023-05-02 21:51:29 -04:00
lat9nq
40f062f749 telemetry_session: Make translate function static
Addresses review feedback

Co-authored-by: Lioncash <mathew1800@gmail.com>
2023-05-02 21:51:29 -04:00
lat9nq
29a56496bf bootmanager: Return value in impossible case
The setting is ranged, so this return statement is unreachable.
But GCC can't tell I guess.
2023-05-02 21:51:29 -04:00
lat9nq
41a103c0fc configure_graphics: Fix typo 2023-05-02 21:51:29 -04:00
lat9nq
66ed1c1872 default_ini: Update V-Sync description
default_ini: Update vsync text

default_ini: Add tooltip from configure_graphics
2023-05-02 21:51:29 -04:00
lat9nq
6f0929df82 configuration: Expose separate swap present modes
Previously, yuzu would try and guess which vsync mode to use given
different scenarios, but apparently we didn't always get it right. This
exposes the separate modes in a drop-down the user can select.

If a mode isn't available in Vulkan, it defaults to FIFO.
2023-05-02 21:51:29 -04:00
59 changed files with 584 additions and 520 deletions

View File

@@ -45,7 +45,6 @@ void LogSettings() {
log_setting("System_LanguageIndex", values.language_index.GetValue());
log_setting("System_RegionIndex", values.region_index.GetValue());
log_setting("System_TimeZoneIndex", values.time_zone_index.GetValue());
log_setting("System_UnsafeMemoryLayout", values.use_unsafe_extended_memory_layout.GetValue());
log_setting("Core_UseMultiCore", values.use_multi_core.GetValue());
log_setting("CPU_Accuracy", values.cpu_accuracy.GetValue());
log_setting("Renderer_UseResolutionScaling", values.resolution_setup.GetValue());
@@ -61,7 +60,7 @@ void LogSettings() {
log_setting("Renderer_NvdecEmulation", values.nvdec_emulation.GetValue());
log_setting("Renderer_AccelerateASTC", values.accelerate_astc.GetValue());
log_setting("Renderer_AsyncASTC", values.async_astc.GetValue());
log_setting("Renderer_UseVsync", values.use_vsync.GetValue());
log_setting("Renderer_UseVsync", values.vsync_mode.GetValue());
log_setting("Renderer_ShaderBackend", values.shader_backend.GetValue());
log_setting("Renderer_UseAsynchronousShaders", values.use_asynchronous_shaders.GetValue());
log_setting("Renderer_AnisotropicFilteringLevel", values.max_anisotropy.GetValue());
@@ -192,7 +191,7 @@ void RestoreGlobalState(bool is_powered_on) {
// Core
values.use_multi_core.SetGlobal(true);
values.use_unsafe_extended_memory_layout.SetGlobal(true);
values.use_extended_memory_layout.SetGlobal(true);
// CPU
values.cpu_accuracy.SetGlobal(true);
@@ -223,7 +222,6 @@ void RestoreGlobalState(bool is_powered_on) {
values.nvdec_emulation.SetGlobal(true);
values.accelerate_astc.SetGlobal(true);
values.async_astc.SetGlobal(true);
values.use_vsync.SetGlobal(true);
values.shader_backend.SetGlobal(true);
values.use_asynchronous_shaders.SetGlobal(true);
values.use_fast_gpu_time.SetGlobal(true);

View File

@@ -16,6 +16,13 @@
namespace Settings {
enum class VSyncMode : u32 {
Immediate = 0,
Mailbox = 1,
FIFO = 2,
FIFORelaxed = 3,
};
enum class RendererBackend : u32 {
OpenGL = 0,
Vulkan = 1,
@@ -388,8 +395,7 @@ struct Values {
// Core
SwitchableSetting<bool> use_multi_core{true, "use_multi_core"};
SwitchableSetting<bool> use_unsafe_extended_memory_layout{false,
"use_unsafe_extended_memory_layout"};
SwitchableSetting<bool> use_extended_memory_layout{false, "use_extended_memory_layout"};
// Cpu
SwitchableSetting<CPUAccuracy, true> cpu_accuracy{CPUAccuracy::Auto, CPUAccuracy::Auto,
@@ -456,7 +462,8 @@ struct Values {
SwitchableSetting<NvdecEmulation> nvdec_emulation{NvdecEmulation::GPU, "nvdec_emulation"};
SwitchableSetting<bool> accelerate_astc{true, "accelerate_astc"};
SwitchableSetting<bool> async_astc{false, "async_astc"};
SwitchableSetting<bool> use_vsync{true, "use_vsync"};
Setting<VSyncMode, true> vsync_mode{VSyncMode::FIFO, VSyncMode::Immediate,
VSyncMode::FIFORelaxed, "use_vsync"};
SwitchableSetting<ShaderBackend, true> shader_backend{ShaderBackend::GLSL, ShaderBackend::GLSL,
ShaderBackend::SPIRV, "shader_backend"};
SwitchableSetting<bool> use_asynchronous_shaders{false, "use_asynchronous_shaders"};

View File

@@ -137,7 +137,7 @@ struct System::Impl {
device_memory = std::make_unique<Core::DeviceMemory>();
is_multicore = Settings::values.use_multi_core.GetValue();
extended_memory_layout = Settings::values.use_unsafe_extended_memory_layout.GetValue();
extended_memory_layout = Settings::values.use_extended_memory_layout.GetValue();
core_timing.SetMulticore(is_multicore);
core_timing.Initialize([&system]() { system.RegisterHostThread(); });
@@ -169,7 +169,7 @@ struct System::Impl {
void ReinitializeIfNecessary(System& system) {
const bool must_reinitialize =
is_multicore != Settings::values.use_multi_core.GetValue() ||
extended_memory_layout != Settings::values.use_unsafe_extended_memory_layout.GetValue();
extended_memory_layout != Settings::values.use_extended_memory_layout.GetValue();
if (!must_reinitialize) {
return;
@@ -178,7 +178,7 @@ struct System::Impl {
LOG_DEBUG(Kernel, "Re-initializing");
is_multicore = Settings::values.use_multi_core.GetValue();
extended_memory_layout = Settings::values.use_unsafe_extended_memory_layout.GetValue();
extended_memory_layout = Settings::values.use_extended_memory_layout.GetValue();
Initialize(system);
}
@@ -293,7 +293,6 @@ struct System::Impl {
ASSERT(Kernel::KProcess::Initialize(main_process, system, "main",
Kernel::KProcess::ProcessType::Userland, resource_limit)
.IsSuccess());
Kernel::KProcess::Register(system.Kernel(), main_process);
kernel.MakeApplicationProcess(main_process);
const auto [load_result, load_parameters] = app_loader->Load(*main_process, system);
if (load_result != Loader::ResultStatus::Success) {

View File

@@ -35,13 +35,12 @@ namespace {
using namespace Common::Literals;
u32 GetMemorySizeForInit() {
return Settings::values.use_unsafe_extended_memory_layout ? Smc::MemorySize_8GB
: Smc::MemorySize_4GB;
return Settings::values.use_extended_memory_layout ? Smc::MemorySize_8GB : Smc::MemorySize_4GB;
}
Smc::MemoryArrangement GetMemoryArrangeForInit() {
return Settings::values.use_unsafe_extended_memory_layout ? Smc::MemoryArrangement_8GB
: Smc::MemoryArrangement_4GB;
return Settings::values.use_extended_memory_layout ? Smc::MemoryArrangement_8GB
: Smc::MemoryArrangement_4GB;
}
} // namespace

View File

@@ -182,8 +182,8 @@ public:
explicit KAutoObjectWithList(KernelCore& kernel) : KAutoObject(kernel) {}
static int Compare(const KAutoObjectWithList& lhs, const KAutoObjectWithList& rhs) {
const uintptr_t lid = reinterpret_cast<uintptr_t>(std::addressof(lhs));
const uintptr_t rid = reinterpret_cast<uintptr_t>(std::addressof(rhs));
const u64 lid = lhs.GetId();
const u64 rid = rhs.GetId();
if (lid < rid) {
return -1;

View File

@@ -95,7 +95,7 @@ struct KernelCore::Impl {
pt_heap_region.GetSize());
}
InitializeHackSharedMemory(kernel);
InitializeHackSharedMemory();
RegisterHostThread(nullptr);
}
@@ -216,12 +216,10 @@ struct KernelCore::Impl {
auto* main_thread{Kernel::KThread::Create(system.Kernel())};
main_thread->SetCurrentCore(core);
ASSERT(Kernel::KThread::InitializeMainThread(system, main_thread, core).IsSuccess());
KThread::Register(system.Kernel(), main_thread);
auto* idle_thread{Kernel::KThread::Create(system.Kernel())};
idle_thread->SetCurrentCore(core);
ASSERT(Kernel::KThread::InitializeIdleThread(system, idle_thread, core).IsSuccess());
KThread::Register(system.Kernel(), idle_thread);
schedulers[i]->Initialize(main_thread, idle_thread, core);
}
@@ -232,7 +230,6 @@ struct KernelCore::Impl {
const Core::Timing::CoreTiming& core_timing) {
system_resource_limit = KResourceLimit::Create(system.Kernel());
system_resource_limit->Initialize(&core_timing);
KResourceLimit::Register(kernel, system_resource_limit);
const auto sizes{memory_layout->GetTotalAndKernelMemorySizes()};
const auto total_size{sizes.first};
@@ -358,7 +355,6 @@ struct KernelCore::Impl {
ASSERT(KThread::InitializeHighPriorityThread(system, shutdown_threads[core_id], {}, {},
core_id)
.IsSuccess());
KThread::Register(system.Kernel(), shutdown_threads[core_id]);
}
}
@@ -733,7 +729,7 @@ struct KernelCore::Impl {
memory_manager->Initialize(management_region.GetAddress(), management_region.GetSize());
}
void InitializeHackSharedMemory(KernelCore& kernel) {
void InitializeHackSharedMemory() {
// Setup memory regions for emulated processes
// TODO(bunnei): These should not be hardcoded regions initialized within the kernel
constexpr std::size_t hid_size{0x40000};
@@ -750,23 +746,14 @@ struct KernelCore::Impl {
hid_shared_mem->Initialize(system.DeviceMemory(), nullptr, Svc::MemoryPermission::None,
Svc::MemoryPermission::Read, hid_size);
KSharedMemory::Register(kernel, hid_shared_mem);
font_shared_mem->Initialize(system.DeviceMemory(), nullptr, Svc::MemoryPermission::None,
Svc::MemoryPermission::Read, font_size);
KSharedMemory::Register(kernel, font_shared_mem);
irs_shared_mem->Initialize(system.DeviceMemory(), nullptr, Svc::MemoryPermission::None,
Svc::MemoryPermission::Read, irs_size);
KSharedMemory::Register(kernel, irs_shared_mem);
time_shared_mem->Initialize(system.DeviceMemory(), nullptr, Svc::MemoryPermission::None,
Svc::MemoryPermission::Read, time_size);
KSharedMemory::Register(kernel, time_shared_mem);
hidbus_shared_mem->Initialize(system.DeviceMemory(), nullptr, Svc::MemoryPermission::None,
Svc::MemoryPermission::Read, hidbus_size);
KSharedMemory::Register(kernel, hidbus_shared_mem);
}
std::mutex registered_objects_lock;
@@ -1085,15 +1072,12 @@ static std::jthread RunHostThreadFunc(KernelCore& kernel, KProcess* process,
// Commit the thread reservation.
thread_reservation.Commit();
// Register the thread.
KThread::Register(kernel, thread);
return std::jthread(
[&kernel, thread, thread_name{std::move(thread_name)}, func{std::move(func)}] {
// Set the thread name.
Common::SetCurrentThreadName(thread_name.c_str());
// Set the thread as current.
// Register the thread.
kernel.RegisterHostThread(thread);
// Run the callback.
@@ -1115,9 +1099,6 @@ std::jthread KernelCore::RunOnHostCoreProcess(std::string&& process_name,
// Ensure that we don't hold onto any extra references.
SCOPE_EXIT({ process->Close(); });
// Register the new process.
KProcess::Register(*this, process);
// Run the host thread.
return RunHostThreadFunc(*this, process, std::move(process_name), std::move(func));
}
@@ -1143,9 +1124,6 @@ void KernelCore::RunOnGuestCoreProcess(std::string&& process_name, std::function
// Ensure that we don't hold onto any extra references.
SCOPE_EXIT({ process->Close(); });
// Register the new process.
KProcess::Register(*this, process);
// Reserve a new thread from the process resource limit.
KScopedResourceReservation thread_reservation(process, LimitableResource::ThreadCountMax);
ASSERT(thread_reservation.Succeeded());
@@ -1158,9 +1136,6 @@ void KernelCore::RunOnGuestCoreProcess(std::string&& process_name, std::function
// Commit the thread reservation.
thread_reservation.Commit();
// Register the new thread.
KThread::Register(*this, thread);
// Begin running the thread.
ASSERT(R_SUCCEEDED(thread->Run()));
}

View File

@@ -156,7 +156,6 @@ public:
auto* session = Kernel::KSession::Create(kernel);
session->Initialize(nullptr, 0);
Kernel::KSession::Register(kernel, session);
auto next_manager = std::make_shared<Service::SessionRequestManager>(
kernel, manager->GetServerManager());

View File

@@ -25,9 +25,6 @@ ServiceContext::ServiceContext(Core::System& system_, std::string name_)
Kernel::KProcess::ProcessType::KernelInternal,
kernel.GetSystemResourceLimit())
.IsSuccess());
// Register the process.
Kernel::KProcess::Register(kernel, process);
process_created = true;
}

View File

@@ -12,9 +12,6 @@ Mutex::Mutex(Core::System& system) : m_system(system) {
m_event = Kernel::KEvent::Create(system.Kernel());
m_event->Initialize(nullptr);
// Register the event.
Kernel::KEvent::Register(system.Kernel(), m_event);
ASSERT(R_SUCCEEDED(m_event->Signal()));
}

View File

@@ -33,9 +33,6 @@ ServerManager::ServerManager(Core::System& system) : m_system{system}, m_serve_m
// Initialize event.
m_event = Kernel::KEvent::Create(system.Kernel());
m_event->Initialize(nullptr);
// Register event.
Kernel::KEvent::Register(system.Kernel(), m_event);
}
ServerManager::~ServerManager() {
@@ -163,9 +160,6 @@ Result ServerManager::ManageDeferral(Kernel::KEvent** out_event) {
// Initialize the event.
m_deferral_event->Initialize(nullptr);
// Register the event.
Kernel::KEvent::Register(m_system.Kernel(), m_deferral_event);
// Set the output.
*out_event = m_deferral_event;

View File

@@ -64,9 +64,6 @@ Result ServiceManager::RegisterService(std::string name, u32 max_sessions,
auto* port = Kernel::KPort::Create(kernel);
port->Initialize(ServerSessionCountMax, false, 0);
// Register the port.
Kernel::KPort::Register(kernel, port);
service_ports.emplace(name, port);
registered_services.emplace(name, handler);
if (deferral_event) {

View File

@@ -49,9 +49,6 @@ void Controller::CloneCurrentObject(HLERequestContext& ctx) {
// Commit the session reservation.
session_reservation.Commit();
// Register the session.
Kernel::KSession::Register(system.Kernel(), session);
// Register with server manager.
session_manager->GetServerManager().RegisterSession(&session->GetServerSession(),
session_manager);

View File

@@ -462,7 +462,7 @@ struct Memory::Impl {
}
if (Settings::IsFastmemEnabled()) {
const bool is_read_enable = !Settings::IsGPULevelExtreme() || !cached;
const bool is_read_enable = Settings::IsGPULevelHigh() || !cached;
system.DeviceMemory().buffer.Protect(vaddr, size, is_read_enable, !cached);
}

View File

@@ -85,6 +85,20 @@ static const char* TranslateNvdecEmulation(Settings::NvdecEmulation backend) {
return "Unknown";
}
static constexpr const char* TranslateVSyncMode(Settings::VSyncMode mode) {
switch (mode) {
case Settings::VSyncMode::Immediate:
return "Immediate";
case Settings::VSyncMode::Mailbox:
return "Mailbox";
case Settings::VSyncMode::FIFO:
return "FIFO";
case Settings::VSyncMode::FIFORelaxed:
return "FIFO Relaxed";
}
return "Unknown";
}
u64 GetTelemetryId() {
u64 telemetry_id{};
const auto filename = Common::FS::GetYuzuPath(Common::FS::YuzuPath::ConfigDir) / "telemetry_id";
@@ -241,7 +255,8 @@ void TelemetrySession::AddInitialInfo(Loader::AppLoader& app_loader,
AddField(field_type, "Renderer_NvdecEmulation",
TranslateNvdecEmulation(Settings::values.nvdec_emulation.GetValue()));
AddField(field_type, "Renderer_AccelerateASTC", Settings::values.accelerate_astc.GetValue());
AddField(field_type, "Renderer_UseVsync", Settings::values.use_vsync.GetValue());
AddField(field_type, "Renderer_UseVsync",
TranslateVSyncMode(Settings::values.vsync_mode.GetValue()));
AddField(field_type, "Renderer_ShaderBackend",
static_cast<u32>(Settings::values.shader_backend.GetValue()));
AddField(field_type, "Renderer_UseAsynchronousShaders",

View File

@@ -1426,7 +1426,7 @@ bool BufferCache<P>::SynchronizeBufferNoModified(Buffer& buffer, VAddr cpu_addr,
.size = sub_size,
});
total_size_bytes += sub_size;
largest_copy = std::max<u64>(largest_copy, sub_size);
largest_copy = std::max(largest_copy, sub_size);
}
const std::span<BufferCopy> copies_span(copies.data(), copies.size());
UploadMemory(buffer, total_size_bytes, largest_copy, copies_span);

View File

@@ -170,8 +170,7 @@ private:
std::size_t page_index{cpu_address >> HIGHER_PAGE_BITS};
u64 page_offset{cpu_address & HIGHER_PAGE_MASK};
while (remaining_size > 0) {
const std::size_t copy_amount{
std::min<std::size_t>(HIGHER_PAGE_SIZE - page_offset, remaining_size)};
const std::size_t copy_amount{std::min(HIGHER_PAGE_SIZE - page_offset, remaining_size)};
auto* manager{top_tier[page_index]};
if (manager) {
if constexpr (BOOL_BREAK) {
@@ -207,8 +206,7 @@ private:
u64 begin = std::numeric_limits<u64>::max();
u64 end = 0;
while (remaining_size > 0) {
const std::size_t copy_amount{
std::min<std::size_t>(HIGHER_PAGE_SIZE - page_offset, remaining_size)};
const std::size_t copy_amount{std::min(HIGHER_PAGE_SIZE - page_offset, remaining_size)};
auto* manager{top_tier[page_index]};
const auto execute = [&] {
auto [new_begin, new_end] = func(manager, page_offset, copy_amount);

View File

@@ -4,20 +4,13 @@
#pragma once
#include <algorithm>
#include <condition_variable>
#include <cstring>
#include <deque>
#include <functional>
#include <memory>
#include <mutex>
#include <thread>
#include <queue>
#include "common/common_types.h"
#include "common/microprofile.h"
#include "common/scope_exit.h"
#include "common/settings.h"
#include "common/thread.h"
#include "video_core/delayed_destruction_ring.h"
#include "video_core/gpu.h"
#include "video_core/host1x/host1x.h"
@@ -30,26 +23,15 @@ class FenceBase {
public:
explicit FenceBase(bool is_stubbed_) : is_stubbed{is_stubbed_} {}
bool IsStubbed() const {
return is_stubbed;
}
protected:
bool is_stubbed;
};
template <typename Traits>
template <typename TFence, typename TTextureCache, typename TTBufferCache, typename TQueryCache>
class FenceManager {
using TFence = typename Traits::FenceType;
using TTextureCache = typename Traits::TextureCacheType;
using TBufferCache = typename Traits::BufferCacheType;
using TQueryCache = typename Traits::QueryCacheType;
static constexpr bool can_async_check = Traits::HAS_ASYNC_CHECK;
public:
/// Notify the fence manager about a new frame
void TickFrame() {
std::unique_lock lock(ring_guard);
delayed_destruction_ring.Tick();
}
@@ -64,33 +46,17 @@ public:
}
void SignalFence(std::function<void()>&& func) {
rasterizer.InvalidateGPUCache();
bool delay_fence = Settings::IsGPULevelHigh();
if constexpr (!can_async_check) {
TryReleasePendingFences<false>();
}
TryReleasePendingFences();
const bool should_flush = ShouldFlush();
CommitAsyncFlushes();
uncommitted_operations.emplace_back(std::move(func));
CommitOperations();
TFence new_fence = CreateFence(!should_flush);
if constexpr (can_async_check) {
guard.lock();
}
if (delay_fence) {
uncommitted_operations.emplace_back(std::move(func));
}
pending_operations.emplace_back(std::move(uncommitted_operations));
fences.push(new_fence);
QueueFence(new_fence);
if (!delay_fence) {
func();
}
fences.push(std::move(new_fence));
if (should_flush) {
rasterizer.FlushCommands();
}
if constexpr (can_async_check) {
guard.unlock();
cv.notify_all();
}
}
void SignalSyncPoint(u32 value) {
@@ -100,30 +66,29 @@ public:
}
void WaitPendingFences() {
if constexpr (!can_async_check) {
TryReleasePendingFences<true>();
while (!fences.empty()) {
TFence& current_fence = fences.front();
if (ShouldWait()) {
WaitFence(current_fence);
}
PopAsyncFlushes();
auto operations = std::move(pending_operations.front());
pending_operations.pop_front();
for (auto& operation : operations) {
operation();
}
PopFence();
}
}
protected:
explicit FenceManager(VideoCore::RasterizerInterface& rasterizer_, Tegra::GPU& gpu_,
TTextureCache& texture_cache_, TBufferCache& buffer_cache_,
TTextureCache& texture_cache_, TTBufferCache& buffer_cache_,
TQueryCache& query_cache_)
: rasterizer{rasterizer_}, gpu{gpu_}, syncpoint_manager{gpu.Host1x().GetSyncpointManager()},
texture_cache{texture_cache_}, buffer_cache{buffer_cache_}, query_cache{query_cache_} {
if constexpr (can_async_check) {
fence_thread =
std::jthread([this](std::stop_token token) { ReleaseThreadFunc(token); });
}
}
texture_cache{texture_cache_}, buffer_cache{buffer_cache_}, query_cache{query_cache_} {}
virtual ~FenceManager() {
if constexpr (can_async_check) {
fence_thread.request_stop();
cv.notify_all();
fence_thread.join();
}
}
virtual ~FenceManager() = default;
/// Creates a Fence Interface, does not create a backend fence if 'is_stubbed' is
/// true
@@ -139,20 +104,15 @@ protected:
Tegra::GPU& gpu;
Tegra::Host1x::SyncpointManager& syncpoint_manager;
TTextureCache& texture_cache;
TBufferCache& buffer_cache;
TTBufferCache& buffer_cache;
TQueryCache& query_cache;
private:
template <bool force_wait>
void TryReleasePendingFences() {
while (!fences.empty()) {
TFence& current_fence = fences.front();
if (ShouldWait() && !IsFenceSignaled(current_fence)) {
if constexpr (force_wait) {
WaitFence(current_fence);
} else {
return;
}
return;
}
PopAsyncFlushes();
auto operations = std::move(pending_operations.front());
@@ -160,49 +120,7 @@ private:
for (auto& operation : operations) {
operation();
}
{
std::unique_lock lock(ring_guard);
delayed_destruction_ring.Push(std::move(current_fence));
}
fences.pop();
}
}
void ReleaseThreadFunc(std::stop_token stop_token) {
std::string name = "GPUFencingThread";
MicroProfileOnThreadCreate(name.c_str());
// Cleanup
SCOPE_EXIT({ MicroProfileOnThreadExit(); });
Common::SetCurrentThreadName(name.c_str());
Common::SetCurrentThreadPriority(Common::ThreadPriority::High);
TFence current_fence;
std::deque<std::function<void()>> current_operations;
while (!stop_token.stop_requested()) {
{
std::unique_lock lock(guard);
cv.wait(lock, [&] { return stop_token.stop_requested() || !fences.empty(); });
if (stop_token.stop_requested()) [[unlikely]] {
return;
}
current_fence = std::move(fences.front());
current_operations = std::move(pending_operations.front());
fences.pop();
pending_operations.pop_front();
}
if (!current_fence->IsStubbed()) {
WaitFence(current_fence);
}
PopAsyncFlushes();
for (auto& operation : current_operations) {
operation();
}
{
std::unique_lock lock(ring_guard);
delayed_destruction_ring.Push(std::move(current_fence));
}
PopFence();
}
}
@@ -236,16 +154,19 @@ private:
query_cache.CommitAsyncFlushes();
}
void PopFence() {
delayed_destruction_ring.Push(std::move(fences.front()));
fences.pop();
}
void CommitOperations() {
pending_operations.emplace_back(std::move(uncommitted_operations));
}
std::queue<TFence> fences;
std::deque<std::function<void()>> uncommitted_operations;
std::deque<std::deque<std::function<void()>>> pending_operations;
std::mutex guard;
std::mutex ring_guard;
std::condition_variable cv;
std::jthread fence_thread;
DelayedDestructionRing<TFence, 6> delayed_destruction_ring;
};

View File

@@ -82,7 +82,6 @@ void MemoryManager::SetEntry(size_t position, MemoryManager::EntryType entry) {
}
PTEKind MemoryManager::GetPageKind(GPUVAddr gpu_addr) const {
std::unique_lock<std::mutex> lock(guard);
return kind_map.GetValueAt(gpu_addr);
}
@@ -161,10 +160,7 @@ GPUVAddr MemoryManager::BigPageTableOp(GPUVAddr gpu_addr, [[maybe_unused]] VAddr
}
remaining_size -= big_page_size;
}
{
std::unique_lock<std::mutex> lock(guard);
kind_map.Map(gpu_addr, gpu_addr + size, kind);
}
kind_map.Map(gpu_addr, gpu_addr + size, kind);
return gpu_addr;
}
@@ -557,7 +553,6 @@ size_t MemoryManager::MaxContinuousRange(GPUVAddr gpu_addr, size_t size) const {
}
size_t MemoryManager::GetMemoryLayoutSize(GPUVAddr gpu_addr, size_t max_size) const {
std::unique_lock<std::mutex> lock(guard);
return kind_map.GetContinuousSizeFrom(gpu_addr);
}
@@ -750,10 +745,10 @@ void MemoryManager::FlushCaching() {
return;
}
accumulator->Callback([this](GPUVAddr addr, size_t size) {
GetSubmappedRangeImpl<false>(addr, size, page_stash2);
GetSubmappedRangeImpl<false>(addr, size, page_stash);
});
rasterizer->InnerInvalidation(page_stash2);
page_stash2.clear();
rasterizer->InnerInvalidation(page_stash);
page_stash.clear();
accumulator->Clear();
}

View File

@@ -5,7 +5,6 @@
#include <atomic>
#include <map>
#include <mutex>
#include <optional>
#include <vector>
@@ -216,9 +215,6 @@ private:
std::vector<u64> big_page_continuous;
std::vector<std::pair<VAddr, std::size_t>> page_stash{};
std::vector<std::pair<VAddr, std::size_t>> page_stash2{};
mutable std::mutex guard;
static constexpr size_t continuous_bits = 64;

View File

@@ -6,7 +6,6 @@
#include <algorithm>
#include <array>
#include <cstring>
#include <functional>
#include <iterator>
#include <list>
#include <memory>
@@ -18,19 +17,13 @@
#include "common/assert.h"
#include "common/settings.h"
#include "core/memory.h"
#include "video_core/control/channel_state_cache.h"
#include "video_core/engines/maxwell_3d.h"
#include "video_core/memory_manager.h"
#include "video_core/rasterizer_interface.h"
#include "video_core/texture_cache/slot_vector.h"
namespace VideoCommon {
using AsyncJobId = SlotId;
static constexpr AsyncJobId NULL_ASYNC_JOB_ID{0};
template <class QueryCache, class HostCounter>
class CounterStreamBase {
public:
@@ -100,13 +93,9 @@ private:
template <class QueryCache, class CachedQuery, class CounterStream, class HostCounter>
class QueryCacheBase : public VideoCommon::ChannelSetupCaches<VideoCommon::ChannelInfo> {
public:
explicit QueryCacheBase(VideoCore::RasterizerInterface& rasterizer_,
Core::Memory::Memory& cpu_memory_)
: rasterizer{rasterizer_},
cpu_memory{cpu_memory_}, streams{{CounterStream{static_cast<QueryCache&>(*this),
VideoCore::QueryType::SamplesPassed}}} {
(void)slot_async_jobs.insert(); // Null value
}
explicit QueryCacheBase(VideoCore::RasterizerInterface& rasterizer_)
: rasterizer{rasterizer_}, streams{{CounterStream{static_cast<QueryCache&>(*this),
VideoCore::QueryType::SamplesPassed}}} {}
void InvalidateRegion(VAddr addr, std::size_t size) {
std::unique_lock lock{mutex};
@@ -137,15 +126,10 @@ public:
query = Register(type, *cpu_addr, host_ptr, timestamp.has_value());
}
auto result = query->BindCounter(Stream(type).Current(), timestamp);
if (result) {
auto async_job_id = query->GetAsyncJob();
auto& async_job = slot_async_jobs[async_job_id];
async_job.collected = true;
async_job.value = *result;
query->SetAsyncJob(NULL_ASYNC_JOB_ID);
query->BindCounter(Stream(type).Current(), timestamp);
if (Settings::values.use_asynchronous_gpu_emulation.GetValue()) {
AsyncFlushQuery(*cpu_addr);
}
AsyncFlushQuery(query, timestamp, lock);
}
/// Updates counters from GPU state. Expected to be called once per draw, clear or dispatch.
@@ -189,18 +173,15 @@ public:
}
void CommitAsyncFlushes() {
std::unique_lock lock{mutex};
committed_flushes.push_back(uncommitted_flushes);
uncommitted_flushes.reset();
}
bool HasUncommittedFlushes() const {
std::unique_lock lock{mutex};
return uncommitted_flushes != nullptr;
}
bool ShouldWaitAsyncFlushes() const {
std::unique_lock lock{mutex};
if (committed_flushes.empty()) {
return false;
}
@@ -208,7 +189,6 @@ public:
}
void PopAsyncFlushes() {
std::unique_lock lock{mutex};
if (committed_flushes.empty()) {
return;
}
@@ -217,25 +197,15 @@ public:
committed_flushes.pop_front();
return;
}
for (AsyncJobId async_job_id : *flush_list) {
AsyncJob& async_job = slot_async_jobs[async_job_id];
if (!async_job.collected) {
FlushAndRemoveRegion(async_job.query_location, 2, true);
}
for (VAddr query_address : *flush_list) {
FlushAndRemoveRegion(query_address, 4);
}
committed_flushes.pop_front();
}
private:
struct AsyncJob {
bool collected = false;
u64 value = 0;
VAddr query_location = 0;
std::optional<u64> timestamp{};
};
/// Flushes a memory range to guest memory and removes it from the cache.
void FlushAndRemoveRegion(VAddr addr, std::size_t size, bool async = false) {
void FlushAndRemoveRegion(VAddr addr, std::size_t size) {
const u64 addr_begin = addr;
const u64 addr_end = addr_begin + size;
const auto in_range = [addr_begin, addr_end](const CachedQuery& query) {
@@ -256,16 +226,7 @@ private:
continue;
}
rasterizer.UpdatePagesCachedCount(query.GetCpuAddr(), query.SizeInBytes(), -1);
AsyncJobId async_job_id = query.GetAsyncJob();
auto flush_result = query.Flush(async);
if (async_job_id == NULL_ASYNC_JOB_ID) {
ASSERT_MSG(false, "This should not be reachable at all");
continue;
}
AsyncJob& async_job = slot_async_jobs[async_job_id];
async_job.collected = true;
async_job.value = flush_result;
query.SetAsyncJob(NULL_ASYNC_JOB_ID);
query.Flush();
}
std::erase_if(contents, in_range);
}
@@ -292,60 +253,26 @@ private:
return found != std::end(contents) ? &*found : nullptr;
}
void AsyncFlushQuery(CachedQuery* query, std::optional<u64> timestamp,
std::unique_lock<std::recursive_mutex>& lock) {
const AsyncJobId new_async_job_id = slot_async_jobs.insert();
{
AsyncJob& async_job = slot_async_jobs[new_async_job_id];
query->SetAsyncJob(new_async_job_id);
async_job.query_location = query->GetCpuAddr();
async_job.collected = false;
if (!uncommitted_flushes) {
uncommitted_flushes = std::make_shared<std::vector<AsyncJobId>>();
}
uncommitted_flushes->push_back(new_async_job_id);
void AsyncFlushQuery(VAddr addr) {
if (!uncommitted_flushes) {
uncommitted_flushes = std::make_shared<std::vector<VAddr>>();
}
lock.unlock();
std::function<void()> operation([this, new_async_job_id, timestamp] {
std::unique_lock local_lock{mutex};
AsyncJob& async_job = slot_async_jobs[new_async_job_id];
u64 value = async_job.value;
VAddr address = async_job.query_location;
slot_async_jobs.erase(new_async_job_id);
local_lock.unlock();
if (timestamp) {
u64 timestamp_value = *timestamp;
cpu_memory.WriteBlockUnsafe(address + sizeof(u64), &timestamp_value, sizeof(u64));
cpu_memory.WriteBlockUnsafe(address, &value, sizeof(u64));
rasterizer.InvalidateRegion(address, sizeof(u64) * 2,
VideoCommon::CacheType::NoQueryCache);
} else {
u32 small_value = static_cast<u32>(value);
cpu_memory.WriteBlockUnsafe(address, &small_value, sizeof(u32));
rasterizer.InvalidateRegion(address, sizeof(u32),
VideoCommon::CacheType::NoQueryCache);
}
});
rasterizer.SyncOperation(std::move(operation));
uncommitted_flushes->push_back(addr);
}
static constexpr std::uintptr_t YUZU_PAGESIZE = 4096;
static constexpr unsigned YUZU_PAGEBITS = 12;
SlotVector<AsyncJob> slot_async_jobs;
VideoCore::RasterizerInterface& rasterizer;
Core::Memory::Memory& cpu_memory;
mutable std::recursive_mutex mutex;
std::recursive_mutex mutex;
std::unordered_map<u64, std::vector<CachedQuery>> cached_queries;
std::array<CounterStream, VideoCore::NumQueryTypes> streams;
std::shared_ptr<std::vector<AsyncJobId>> uncommitted_flushes{};
std::list<std::shared_ptr<std::vector<AsyncJobId>>> 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>
@@ -364,12 +291,12 @@ public:
virtual ~HostCounterBase() = default;
/// Returns the current value of the query.
u64 Query(bool async = false) {
u64 Query() {
if (result) {
return *result;
}
u64 value = BlockingQuery(async) + base_result;
u64 value = BlockingQuery() + base_result;
if (dependency) {
value += dependency->Query();
dependency = nullptr;
@@ -390,7 +317,7 @@ public:
protected:
/// Returns the value of query from the backend API blocking as needed.
virtual u64 BlockingQuery(bool async = false) const = 0;
virtual u64 BlockingQuery() const = 0;
private:
std::shared_ptr<HostCounter> dependency; ///< Counter to add to this value.
@@ -413,33 +340,26 @@ public:
CachedQueryBase& operator=(const CachedQueryBase&) = delete;
/// Flushes the query to guest memory.
virtual u64 Flush(bool async = false) {
virtual void Flush() {
// When counter is nullptr it means that it's just been reset. We are supposed to write a
// zero in these cases.
const u64 value = counter ? counter->Query(async) : 0;
if (async) {
return value;
}
const u64 value = counter ? counter->Query() : 0;
std::memcpy(host_ptr, &value, sizeof(u64));
if (timestamp) {
std::memcpy(host_ptr + TIMESTAMP_OFFSET, &*timestamp, sizeof(u64));
}
return value;
}
/// Binds a counter to this query.
std::optional<u64> BindCounter(std::shared_ptr<HostCounter> counter_,
std::optional<u64> timestamp_) {
std::optional<u64> result{};
void BindCounter(std::shared_ptr<HostCounter> counter_, std::optional<u64> timestamp_) {
if (counter) {
// If there's an old counter set it means the query is being rewritten by the game.
// To avoid losing the data forever, flush here.
result = std::make_optional(Flush());
Flush();
}
counter = std::move(counter_);
timestamp = timestamp_;
return result;
}
VAddr GetCpuAddr() const noexcept {
@@ -454,14 +374,6 @@ public:
return with_timestamp ? LARGE_QUERY_SIZE : SMALL_QUERY_SIZE;
}
void SetAsyncJob(AsyncJobId assigned_async_job_) {
assigned_async_job = assigned_async_job_;
}
AsyncJobId GetAsyncJob() const {
return assigned_async_job;
}
protected:
/// Returns true when querying the counter may potentially block.
bool WaitPending() const noexcept {
@@ -477,7 +389,6 @@ private:
u8* host_ptr; ///< Writable host pointer.
std::shared_ptr<HostCounter> counter; ///< Host counter to query, owns the dependency tree.
std::optional<u64> timestamp; ///< Timestamp to flush to guest memory.
AsyncJobId assigned_async_job;
};
} // namespace VideoCommon

View File

@@ -30,17 +30,7 @@ private:
};
using Fence = std::shared_ptr<GLInnerFence>;
struct FenceManagerParams {
using FenceType = Fence;
using BufferCacheType = BufferCache;
using TextureCacheType = TextureCache;
using QueryCacheType = QueryCache;
static constexpr bool HAS_ASYNC_CHECK = false;
};
using GenericFenceManager = VideoCommon::FenceManager<FenceManagerParams>;
using GenericFenceManager = VideoCommon::FenceManager<Fence, TextureCache, BufferCache, QueryCache>;
class FenceManagerOpenGL final : public GenericFenceManager {
public:

View File

@@ -26,8 +26,8 @@ constexpr GLenum GetTarget(VideoCore::QueryType type) {
} // Anonymous namespace
QueryCache::QueryCache(RasterizerOpenGL& rasterizer_, Core::Memory::Memory& cpu_memory_)
: QueryCacheBase(rasterizer_, cpu_memory_), gl_rasterizer{rasterizer_} {}
QueryCache::QueryCache(RasterizerOpenGL& rasterizer_)
: QueryCacheBase(rasterizer_), gl_rasterizer{rasterizer_} {}
QueryCache::~QueryCache() = default;
@@ -74,7 +74,7 @@ void HostCounter::EndQuery() {
glEndQuery(GetTarget(type));
}
u64 HostCounter::BlockingQuery([[maybe_unused]] bool async) const {
u64 HostCounter::BlockingQuery() const {
GLint64 value;
glGetQueryObjecti64v(query.handle, GL_QUERY_RESULT, &value);
return static_cast<u64>(value);
@@ -96,7 +96,7 @@ CachedQuery& CachedQuery::operator=(CachedQuery&& rhs) noexcept {
return *this;
}
u64 CachedQuery::Flush([[maybe_unused]] bool async) {
void CachedQuery::Flush() {
// Waiting for a query while another query of the same target is enabled locks Nvidia's driver.
// To avoid this disable and re-enable keeping the dependency stream.
// But we only have to do this if we have pending waits to be done.
@@ -106,13 +106,11 @@ u64 CachedQuery::Flush([[maybe_unused]] bool async) {
stream.Update(false);
}
auto result = VideoCommon::CachedQueryBase<HostCounter>::Flush();
VideoCommon::CachedQueryBase<HostCounter>::Flush();
if (slice_counter) {
stream.Update(true);
}
return result;
}
} // namespace OpenGL

View File

@@ -28,7 +28,7 @@ using CounterStream = VideoCommon::CounterStreamBase<QueryCache, HostCounter>;
class QueryCache final
: public VideoCommon::QueryCacheBase<QueryCache, CachedQuery, CounterStream, HostCounter> {
public:
explicit QueryCache(RasterizerOpenGL& rasterizer_, Core::Memory::Memory& cpu_memory_);
explicit QueryCache(RasterizerOpenGL& rasterizer_);
~QueryCache();
OGLQuery AllocateQuery(VideoCore::QueryType type);
@@ -51,7 +51,7 @@ public:
void EndQuery();
private:
u64 BlockingQuery(bool async = false) const override;
u64 BlockingQuery() const override;
QueryCache& cache;
const VideoCore::QueryType type;
@@ -70,7 +70,7 @@ public:
CachedQuery(const CachedQuery&) = delete;
CachedQuery& operator=(const CachedQuery&) = delete;
u64 Flush(bool async = false) override;
void Flush() override;
private:
QueryCache* cache;

View File

@@ -63,7 +63,7 @@ RasterizerOpenGL::RasterizerOpenGL(Core::Frontend::EmuWindow& emu_window_, Tegra
buffer_cache(*this, cpu_memory_, buffer_cache_runtime),
shader_cache(*this, emu_window_, device, texture_cache, buffer_cache, program_manager,
state_tracker, gpu.ShaderNotify()),
query_cache(*this, cpu_memory_), accelerate_dma(buffer_cache, texture_cache),
query_cache(*this), accelerate_dma(buffer_cache, texture_cache),
fence_manager(*this, gpu, texture_cache, buffer_cache, query_cache),
blit_image(program_manager_) {}

View File

@@ -88,7 +88,7 @@ RendererVulkan::RendererVulkan(Core::TelemetrySession& telemetry_session_,
instance(CreateInstance(library, dld, VK_API_VERSION_1_1, render_window.GetWindowInfo().type,
Settings::values.renderer_debug.GetValue())),
debug_callback(Settings::values.renderer_debug ? CreateDebugCallback(instance) : nullptr),
surface(CreateSurface(instance, render_window)),
surface(CreateSurface(instance, render_window.GetWindowInfo())),
device(CreateDevice(instance, dld, *surface)), memory_allocator(device, false),
state_tracker(), scheduler(device, state_tracker),
swapchain(*surface, device, scheduler, render_window.GetFramebufferLayout().width,
@@ -134,7 +134,7 @@ void RendererVulkan::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) {
Frame* frame = present_manager.GetRenderFrame();
blit_screen.DrawToSwapchain(frame, *framebuffer, use_accelerated, is_srgb);
scheduler.Flush(*frame->render_ready);
present_manager.Present(frame);
scheduler.Record([this, frame](vk::CommandBuffer) { present_manager.PushFrame(frame); });
gpu.RendererFrameEndNotify();
rasterizer.TickFrame();

View File

@@ -5,7 +5,6 @@
#include "video_core/renderer_vulkan/vk_buffer_cache.h"
#include "video_core/renderer_vulkan/vk_fence_manager.h"
#include "video_core/renderer_vulkan/vk_query_cache.h"
#include "video_core/renderer_vulkan/vk_scheduler.h"
#include "video_core/renderer_vulkan/vk_texture_cache.h"
#include "video_core/vulkan_common/vulkan_device.h"

View File

@@ -40,16 +40,7 @@ private:
};
using Fence = std::shared_ptr<InnerFence>;
struct FenceManagerParams {
using FenceType = Fence;
using BufferCacheType = BufferCache;
using TextureCacheType = TextureCache;
using QueryCacheType = QueryCache;
static constexpr bool HAS_ASYNC_CHECK = true;
};
using GenericFenceManager = VideoCommon::FenceManager<FenceManagerParams>;
using GenericFenceManager = VideoCommon::FenceManager<Fence, TextureCache, BufferCache, QueryCache>;
class FenceManager final : public GenericFenceManager {
public:

View File

@@ -153,19 +153,16 @@ Frame* PresentManager::GetRenderFrame() {
return frame;
}
void PresentManager::Present(Frame* frame) {
void PresentManager::PushFrame(Frame* frame) {
if (!use_present_thread) {
scheduler.WaitWorker();
CopyToSwapchain(frame);
free_queue.push(frame);
return;
}
scheduler.Record([this, frame](vk::CommandBuffer) {
std::unique_lock lock{queue_mutex};
present_queue.push(frame);
frame_cv.notify_one();
});
std::unique_lock lock{queue_mutex};
present_queue.push(frame);
frame_cv.notify_one();
}
void PresentManager::RecreateFrame(Frame* frame, u32 width, u32 height, bool is_srgb,

View File

@@ -45,7 +45,7 @@ public:
Frame* GetRenderFrame();
/// Pushes a frame for presentation
void Present(Frame* frame);
void PushFrame(Frame* frame);
/// Recreates the present frame to match the provided parameters
void RecreateFrame(Frame* frame, u32 width, u32 height, bool is_srgb,

View File

@@ -66,10 +66,9 @@ void QueryPool::Reserve(std::pair<VkQueryPool, u32> query) {
}
}
QueryCache::QueryCache(VideoCore::RasterizerInterface& rasterizer_,
Core::Memory::Memory& cpu_memory_, const Device& device_,
QueryCache::QueryCache(VideoCore::RasterizerInterface& rasterizer_, const Device& device_,
Scheduler& scheduler_)
: QueryCacheBase{rasterizer_, cpu_memory_}, device{device_}, scheduler{scheduler_},
: QueryCacheBase{rasterizer_}, device{device_}, scheduler{scheduler_},
query_pools{
QueryPool{device_, scheduler_, QueryType::SamplesPassed},
} {}
@@ -99,10 +98,8 @@ HostCounter::HostCounter(QueryCache& cache_, std::shared_ptr<HostCounter> depend
query{cache_.AllocateQuery(type_)}, tick{cache_.GetScheduler().CurrentTick()} {
const vk::Device* logical = &cache.GetDevice().GetLogical();
cache.GetScheduler().Record([logical, query = query](vk::CommandBuffer cmdbuf) {
const bool use_precise = Settings::IsGPULevelHigh();
logical->ResetQueryPool(query.first, query.second, 1);
cmdbuf.BeginQuery(query.first, query.second,
use_precise ? VK_QUERY_CONTROL_PRECISE_BIT : 0);
cmdbuf.BeginQuery(query.first, query.second, VK_QUERY_CONTROL_PRECISE_BIT);
});
}
@@ -115,10 +112,8 @@ void HostCounter::EndQuery() {
[query = query](vk::CommandBuffer cmdbuf) { cmdbuf.EndQuery(query.first, query.second); });
}
u64 HostCounter::BlockingQuery(bool async) const {
if (!async) {
cache.GetScheduler().Wait(tick);
}
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),

View File

@@ -52,8 +52,7 @@ private:
class QueryCache final
: public VideoCommon::QueryCacheBase<QueryCache, CachedQuery, CounterStream, HostCounter> {
public:
explicit QueryCache(VideoCore::RasterizerInterface& rasterizer_,
Core::Memory::Memory& cpu_memory_, const Device& device_,
explicit QueryCache(VideoCore::RasterizerInterface& rasterizer_, const Device& device_,
Scheduler& scheduler_);
~QueryCache();
@@ -84,7 +83,7 @@ public:
void EndQuery();
private:
u64 BlockingQuery(bool async = false) const override;
u64 BlockingQuery() const override;
QueryCache& cache;
const VideoCore::QueryType type;

View File

@@ -172,8 +172,7 @@ RasterizerVulkan::RasterizerVulkan(Core::Frontend::EmuWindow& emu_window_, Tegra
buffer_cache(*this, cpu_memory_, buffer_cache_runtime),
pipeline_cache(*this, device, scheduler, descriptor_pool, update_descriptor_queue,
render_pass_cache, buffer_cache, texture_cache, gpu.ShaderNotify()),
query_cache{*this, cpu_memory_, device, scheduler},
accelerate_dma(buffer_cache, texture_cache, scheduler),
query_cache{*this, device, scheduler}, accelerate_dma(buffer_cache, texture_cache, scheduler),
fence_manager(*this, gpu, texture_cache, buffer_cache, query_cache, device, scheduler),
wfi_event(device.GetLogical().CreateEvent()) {
scheduler.SetQueryCache(query_cache);
@@ -676,8 +675,7 @@ bool RasterizerVulkan::AccelerateConditionalRendering() {
const GPUVAddr condition_address{maxwell3d->regs.render_enable.Address()};
Maxwell::ReportSemaphore::Compare cmp;
if (gpu_memory->IsMemoryDirty(condition_address, sizeof(cmp),
VideoCommon::CacheType::BufferCache |
VideoCommon::CacheType::QueryCache)) {
VideoCommon::CacheType::BufferCache)) {
return true;
}
return false;

View File

@@ -14,6 +14,7 @@
#include "video_core/renderer_vulkan/vk_swapchain.h"
#include "video_core/vulkan_common/vulkan_device.h"
#include "video_core/vulkan_common/vulkan_wrapper.h"
#include "vulkan/vulkan_core.h"
namespace Vulkan {
@@ -33,23 +34,47 @@ VkSurfaceFormatKHR ChooseSwapSurfaceFormat(vk::Span<VkSurfaceFormatKHR> formats)
return found != formats.end() ? *found : formats[0];
}
VkPresentModeKHR ChooseSwapPresentMode(vk::Span<VkPresentModeKHR> modes) {
// Mailbox (triple buffering) doesn't lock the application like fifo (vsync),
// prefer it if vsync option is not selected
const auto found_mailbox = std::find(modes.begin(), modes.end(), VK_PRESENT_MODE_MAILBOX_KHR);
if (Settings::values.fullscreen_mode.GetValue() == Settings::FullscreenMode::Borderless &&
found_mailbox != modes.end() && !Settings::values.use_vsync.GetValue()) {
return VK_PRESENT_MODE_MAILBOX_KHR;
}
if (!Settings::values.use_speed_limit.GetValue()) {
// FIFO present mode locks the framerate to the monitor's refresh rate,
// Find an alternative to surpass this limitation if FPS is unlocked.
const auto found_imm = std::find(modes.begin(), modes.end(), VK_PRESENT_MODE_IMMEDIATE_KHR);
if (found_imm != modes.end()) {
return VK_PRESENT_MODE_IMMEDIATE_KHR;
static constexpr VkPresentModeKHR ChooseSwapPresentMode(bool has_imm, bool has_mailbox,
bool has_fifo_relaxed) {
// Mailbox doesn't lock the application like FIFO (vsync)
// FIFO present mode locks the framerate to the monitor's refresh rate
Settings::VSyncMode setting = [has_imm, has_mailbox]() {
// Choose Mailbox or Immediate if unlocked and those modes are supported
const auto mode = Settings::values.vsync_mode.GetValue();
if (Settings::values.use_speed_limit.GetValue()) {
return mode;
}
switch (mode) {
case Settings::VSyncMode::FIFO:
case Settings::VSyncMode::FIFORelaxed:
if (has_mailbox) {
return Settings::VSyncMode::Mailbox;
} else if (has_imm) {
return Settings::VSyncMode::Immediate;
}
[[fallthrough]];
default:
return mode;
}
}();
if ((setting == Settings::VSyncMode::Mailbox && !has_mailbox) ||
(setting == Settings::VSyncMode::Immediate && !has_imm) ||
(setting == Settings::VSyncMode::FIFORelaxed && !has_fifo_relaxed)) {
setting = Settings::VSyncMode::FIFO;
}
switch (setting) {
case Settings::VSyncMode::Immediate:
return VK_PRESENT_MODE_IMMEDIATE_KHR;
case Settings::VSyncMode::Mailbox:
return VK_PRESENT_MODE_MAILBOX_KHR;
case Settings::VSyncMode::FIFO:
return VK_PRESENT_MODE_FIFO_KHR;
case Settings::VSyncMode::FIFORelaxed:
return VK_PRESENT_MODE_FIFO_RELAXED_KHR;
default:
return VK_PRESENT_MODE_FIFO_KHR;
}
return VK_PRESENT_MODE_FIFO_KHR;
}
VkExtent2D ChooseSwapExtent(const VkSurfaceCapabilitiesKHR& capabilities, u32 width, u32 height) {
@@ -167,11 +192,17 @@ void Swapchain::Present(VkSemaphore render_semaphore) {
void Swapchain::CreateSwapchain(const VkSurfaceCapabilitiesKHR& capabilities, bool srgb) {
const auto physical_device{device.GetPhysical()};
const auto formats{physical_device.GetSurfaceFormatsKHR(surface)};
const auto present_modes{physical_device.GetSurfacePresentModesKHR(surface)};
const auto present_modes = physical_device.GetSurfacePresentModesKHR(surface);
has_mailbox = std::find(present_modes.begin(), present_modes.end(),
VK_PRESENT_MODE_MAILBOX_KHR) != present_modes.end();
has_imm = std::find(present_modes.begin(), present_modes.end(),
VK_PRESENT_MODE_IMMEDIATE_KHR) != present_modes.end();
has_fifo_relaxed = std::find(present_modes.begin(), present_modes.end(),
VK_PRESENT_MODE_FIFO_RELAXED_KHR) != present_modes.end();
const VkCompositeAlphaFlagBitsKHR alpha_flags{ChooseAlphaFlags(capabilities)};
surface_format = ChooseSwapSurfaceFormat(formats);
present_mode = ChooseSwapPresentMode(present_modes);
present_mode = ChooseSwapPresentMode(has_imm, has_mailbox, has_fifo_relaxed);
u32 requested_image_count{capabilities.minImageCount + 1};
// Ensure Triple buffering if possible.
@@ -232,7 +263,6 @@ void Swapchain::CreateSwapchain(const VkSurfaceCapabilitiesKHR& capabilities, bo
extent = swapchain_ci.imageExtent;
current_srgb = srgb;
current_fps_unlocked = !Settings::values.use_speed_limit.GetValue();
images = swapchain.GetImages();
image_count = static_cast<u32>(images.size());
@@ -254,14 +284,9 @@ void Swapchain::Destroy() {
swapchain.reset();
}
bool Swapchain::HasFpsUnlockChanged() const {
return current_fps_unlocked != !Settings::values.use_speed_limit.GetValue();
}
bool Swapchain::NeedsPresentModeUpdate() const {
// Mailbox present mode is the ideal for all scenarios. If it is not available,
// A different present mode is needed to support unlocked FPS above the monitor's refresh rate.
return present_mode != VK_PRESENT_MODE_MAILBOX_KHR && HasFpsUnlockChanged();
const auto requested_mode = ChooseSwapPresentMode(has_imm, has_mailbox, has_fifo_relaxed);
return present_mode != requested_mode;
}
} // namespace Vulkan

View File

@@ -116,8 +116,6 @@ private:
void Destroy();
bool HasFpsUnlockChanged() const;
bool NeedsPresentModeUpdate() const;
const VkSurfaceKHR surface;
@@ -142,9 +140,11 @@ private:
VkExtent2D extent{};
VkPresentModeKHR present_mode{};
VkSurfaceFormatKHR surface_format{};
bool has_imm{false};
bool has_mailbox{false};
bool has_fifo_relaxed{false};
bool current_srgb{};
bool current_fps_unlocked{};
bool is_outdated{};
bool is_suboptimal{};
};

View File

@@ -888,7 +888,7 @@ void TextureCache<P>::DownloadImageIntoBuffer(typename TextureCache<P>::Image* i
buffer,
download_map.buffer,
};
std::array<u64, 2> buffer_offsets{
std::array buffer_offsets{
buffer_offset,
download_map.offset,
};

View File

@@ -617,9 +617,7 @@ bool Device::ShouldBoostClocks() const {
const bool is_steam_deck = vendor_id == 0x1002 && device_id == 0x163F;
const bool is_debugging = this->HasDebuggingToolAttached();
return validated_driver && !is_steam_deck && !is_debugging;
return validated_driver && !is_steam_deck;
}
bool Device::GetSuitability(bool requires_swapchain) {

View File

@@ -23,10 +23,10 @@
namespace Vulkan {
vk::SurfaceKHR CreateSurface(const vk::Instance& instance,
const Core::Frontend::EmuWindow& emu_window) {
vk::SurfaceKHR CreateSurface(
const vk::Instance& instance,
[[maybe_unused]] const Core::Frontend::EmuWindow::WindowSystemInfo& window_info) {
[[maybe_unused]] const vk::InstanceDispatch& dld = instance.Dispatch();
[[maybe_unused]] const auto& window_info = emu_window.GetWindowInfo();
VkSurfaceKHR unsafe_surface = nullptr;
#ifdef _WIN32

View File

@@ -3,15 +3,12 @@
#pragma once
#include "core/frontend/emu_window.h"
#include "video_core/vulkan_common/vulkan_wrapper.h"
namespace Core::Frontend {
class EmuWindow;
}
namespace Vulkan {
[[nodiscard]] vk::SurfaceKHR CreateSurface(const vk::Instance& instance,
const Core::Frontend::EmuWindow& emu_window);
[[nodiscard]] vk::SurfaceKHR CreateSurface(
const vk::Instance& instance, const Core::Frontend::EmuWindow::WindowSystemInfo& window_info);
} // namespace Vulkan

View File

@@ -189,6 +189,8 @@ add_executable(yuzu
multiplayer/state.h
multiplayer/validation.h
precompiled_headers.h
qt_common.cpp
qt_common.h
startup_checks.cpp
startup_checks.h
uisettings.cpp

View File

@@ -1,36 +1,48 @@
// SPDX-FileCopyrightText: 2014 Citra Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <algorithm>
#include <array>
#include <cmath>
#include <cstring>
#include <string>
#include <tuple>
#include <type_traits>
#include <glad/glad.h>
#include <QApplication>
#include <QtCore/qglobal.h>
#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) && YUZU_USE_QT_MULTIMEDIA
#include <QCamera>
#include <QCameraImageCapture>
#include <QCameraInfo>
#endif
#include <QCursor>
#include <QEvent>
#include <QGuiApplication>
#include <QHBoxLayout>
#include <QKeyEvent>
#include <QLayout>
#include <QList>
#include <QMessageBox>
#include <QPainter>
#include <QScreen>
#include <QString>
#include <QStringList>
#include <QSize>
#include <QStringLiteral>
#include <QSurfaceFormat>
#include <QTimer>
#include <QWindow>
#include <QtCore/qobjectdefs.h>
#ifdef HAS_OPENGL
#include <QOffscreenSurface>
#include <QOpenGLContext>
#endif
#if !defined(WIN32)
#include <qpa/qplatformnativeinterface.h>
#endif
#include <fmt/format.h>
#include "common/assert.h"
#include "common/microprofile.h"
#include "common/polyfill_thread.h"
#include "common/scm_rev.h"
#include "common/settings.h"
#include "common/settings_input.h"
#include "common/thread.h"
#include "core/core.h"
#include "core/cpu_manager.h"
#include "core/frontend/framebuffer_layout.h"
@@ -40,11 +52,16 @@
#include "input_common/drivers/tas_input.h"
#include "input_common/drivers/touch_screen.h"
#include "input_common/main.h"
#include "video_core/gpu.h"
#include "video_core/rasterizer_interface.h"
#include "video_core/renderer_base.h"
#include "yuzu/bootmanager.h"
#include "yuzu/main.h"
#include "yuzu/qt_common.h"
static Core::Frontend::WindowSystemType GetWindowSystemType();
class QObject;
class QPaintEngine;
class QSurface;
EmuThread::EmuThread(Core::System& system) : m_system{system} {}
@@ -154,7 +171,10 @@ public:
// disable vsync for any shared contexts
auto format = share_context->format();
format.setSwapInterval(main_surface ? Settings::values.use_vsync.GetValue() : 0);
const int swap_interval =
Settings::values.vsync_mode.GetValue() == Settings::VSyncMode::Immediate ? 0 : 1;
format.setSwapInterval(main_surface ? swap_interval : 0);
context = std::make_unique<QOpenGLContext>();
context->setShareContext(share_context);
@@ -221,7 +241,7 @@ public:
explicit RenderWidget(GRenderWindow* parent) : QWidget(parent), render_window(parent) {
setAttribute(Qt::WA_NativeWindow);
setAttribute(Qt::WA_PaintOnScreen);
if (GetWindowSystemType() == Core::Frontend::WindowSystemType::Wayland) {
if (QtCommon::GetWindowSystemType() == Core::Frontend::WindowSystemType::Wayland) {
setAttribute(Qt::WA_DontCreateNativeAncestors);
}
}
@@ -259,46 +279,6 @@ struct NullRenderWidget : public RenderWidget {
explicit NullRenderWidget(GRenderWindow* parent) : RenderWidget(parent) {}
};
static Core::Frontend::WindowSystemType GetWindowSystemType() {
// Determine WSI type based on Qt platform.
QString platform_name = QGuiApplication::platformName();
if (platform_name == QStringLiteral("windows"))
return Core::Frontend::WindowSystemType::Windows;
else if (platform_name == QStringLiteral("xcb"))
return Core::Frontend::WindowSystemType::X11;
else if (platform_name == QStringLiteral("wayland"))
return Core::Frontend::WindowSystemType::Wayland;
else if (platform_name == QStringLiteral("wayland-egl"))
return Core::Frontend::WindowSystemType::Wayland;
else if (platform_name == QStringLiteral("cocoa"))
return Core::Frontend::WindowSystemType::Cocoa;
else if (platform_name == QStringLiteral("android"))
return Core::Frontend::WindowSystemType::Android;
LOG_CRITICAL(Frontend, "Unknown Qt platform {}!", platform_name.toStdString());
return Core::Frontend::WindowSystemType::Windows;
}
static Core::Frontend::EmuWindow::WindowSystemInfo GetWindowSystemInfo(QWindow* window) {
Core::Frontend::EmuWindow::WindowSystemInfo wsi;
wsi.type = GetWindowSystemType();
// Our Win32 Qt external doesn't have the private API.
#if defined(WIN32) || defined(__APPLE__)
wsi.render_surface = window ? reinterpret_cast<void*>(window->winId()) : nullptr;
#else
QPlatformNativeInterface* pni = QGuiApplication::platformNativeInterface();
wsi.display_connection = pni->nativeResourceForWindow("display", window);
if (wsi.type == Core::Frontend::WindowSystemType::Wayland)
wsi.render_surface = window ? pni->nativeResourceForWindow("surface", window) : nullptr;
else
wsi.render_surface = window ? reinterpret_cast<void*>(window->winId()) : nullptr;
#endif
wsi.render_surface_scale = window ? static_cast<float>(window->devicePixelRatio()) : 1.0f;
return wsi;
}
GRenderWindow::GRenderWindow(GMainWindow* parent, EmuThread* emu_thread_,
std::shared_ptr<InputCommon::InputSubsystem> input_subsystem_,
Core::System& system_)
@@ -904,7 +884,7 @@ bool GRenderWindow::InitRenderTarget() {
}
// Update the Window System information with the new render target
window_info = GetWindowSystemInfo(child_widget->windowHandle());
window_info = QtCommon::GetWindowSystemInfo(child_widget->windowHandle());
child_widget->resize(Layout::ScreenUndocked::Width, Layout::ScreenUndocked::Height);
layout()->addWidget(child_widget);

View File

@@ -5,27 +5,46 @@
#include <atomic>
#include <condition_variable>
#include <cstddef>
#include <memory>
#include <mutex>
#include <stop_token>
#include <utility>
#include <vector>
#include <QByteArray>
#include <QImage>
#include <QObject>
#include <QPoint>
#include <QString>
#include <QStringList>
#include <QThread>
#include <QTouchEvent>
#include <QWidget>
#include <qglobal.h>
#include <qnamespace.h>
#include <qobjectdefs.h>
#include "common/common_types.h"
#include "common/logging/log.h"
#include "common/polyfill_thread.h"
#include "common/thread.h"
#include "core/frontend/emu_window.h"
class GRenderWindow;
class GMainWindow;
class QCamera;
class QCameraImageCapture;
class QCloseEvent;
class QFocusEvent;
class QKeyEvent;
class QMouseEvent;
class QObject;
class QResizeEvent;
class QShowEvent;
class QTimer;
class QTouchEvent;
class QWheelEvent;
namespace Core {
enum class SystemResultStatus : u32;
class System;
} // namespace Core
@@ -40,7 +59,6 @@ enum class TasState;
namespace VideoCore {
enum class LoadCallbackStage;
class RendererBase;
} // namespace VideoCore
class EmuThread final : public QThread {

View File

@@ -6,6 +6,7 @@
#include <QSettings>
#include "common/fs/fs.h"
#include "common/fs/path_util.h"
#include "common/settings.h"
#include "core/core.h"
#include "core/hle/service/acc/profile_manager.h"
#include "core/hle/service/hid/controllers/npad.h"
@@ -497,7 +498,7 @@ void Config::ReadCoreValues() {
qt_config->beginGroup(QStringLiteral("Core"));
ReadGlobalSetting(Settings::values.use_multi_core);
ReadGlobalSetting(Settings::values.use_unsafe_extended_memory_layout);
ReadGlobalSetting(Settings::values.use_extended_memory_layout);
qt_config->endGroup();
}
@@ -709,7 +710,6 @@ void Config::ReadRendererValues() {
ReadGlobalSetting(Settings::values.nvdec_emulation);
ReadGlobalSetting(Settings::values.accelerate_astc);
ReadGlobalSetting(Settings::values.async_astc);
ReadGlobalSetting(Settings::values.use_vsync);
ReadGlobalSetting(Settings::values.shader_backend);
ReadGlobalSetting(Settings::values.use_asynchronous_shaders);
ReadGlobalSetting(Settings::values.use_fast_gpu_time);
@@ -720,6 +720,10 @@ void Config::ReadRendererValues() {
ReadGlobalSetting(Settings::values.bg_blue);
if (global) {
Settings::values.vsync_mode.SetValue(static_cast<Settings::VSyncMode>(
ReadSetting(QString::fromStdString(Settings::values.vsync_mode.GetLabel()),
static_cast<u32>(Settings::values.vsync_mode.GetDefault()))
.value<u32>()));
ReadBasicSetting(Settings::values.renderer_debug);
ReadBasicSetting(Settings::values.renderer_shader_feedback);
ReadBasicSetting(Settings::values.enable_nsight_aftermath);
@@ -1162,7 +1166,7 @@ void Config::SaveCoreValues() {
qt_config->beginGroup(QStringLiteral("Core"));
WriteGlobalSetting(Settings::values.use_multi_core);
WriteGlobalSetting(Settings::values.use_unsafe_extended_memory_layout);
WriteGlobalSetting(Settings::values.use_extended_memory_layout);
qt_config->endGroup();
}
@@ -1352,7 +1356,6 @@ void Config::SaveRendererValues() {
Settings::values.nvdec_emulation.UsingGlobal());
WriteGlobalSetting(Settings::values.accelerate_astc);
WriteGlobalSetting(Settings::values.async_astc);
WriteGlobalSetting(Settings::values.use_vsync);
WriteSetting(QString::fromStdString(Settings::values.shader_backend.GetLabel()),
static_cast<u32>(Settings::values.shader_backend.GetValue(global)),
static_cast<u32>(Settings::values.shader_backend.GetDefault()),
@@ -1366,6 +1369,9 @@ void Config::SaveRendererValues() {
WriteGlobalSetting(Settings::values.bg_blue);
if (global) {
WriteSetting(QString::fromStdString(Settings::values.vsync_mode.GetLabel()),
static_cast<u32>(Settings::values.vsync_mode.GetValue()),
static_cast<u32>(Settings::values.vsync_mode.GetDefault()));
WriteBasicSetting(Settings::values.renderer_debug);
WriteBasicSetting(Settings::values.renderer_shader_feedback);
WriteBasicSetting(Settings::values.enable_nsight_aftermath);

View File

@@ -35,6 +35,9 @@ void ConfigureGeneral::SetConfiguration() {
ui->use_multi_core->setEnabled(runtime_lock);
ui->use_multi_core->setChecked(Settings::values.use_multi_core.GetValue());
ui->use_extended_memory_layout->setEnabled(runtime_lock);
ui->use_extended_memory_layout->setChecked(
Settings::values.use_extended_memory_layout.GetValue());
ui->toggle_check_exit->setChecked(UISettings::values.confirm_before_closing.GetValue());
ui->toggle_user_on_boot->setChecked(UISettings::values.select_user_on_boot.GetValue());
@@ -76,6 +79,9 @@ void ConfigureGeneral::ResetDefaults() {
void ConfigureGeneral::ApplyConfiguration() {
ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_multi_core, ui->use_multi_core,
use_multi_core);
ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_extended_memory_layout,
ui->use_extended_memory_layout,
use_extended_memory_layout);
if (Settings::IsConfiguringGlobal()) {
UISettings::values.confirm_before_closing = ui->toggle_check_exit->isChecked();
@@ -135,6 +141,9 @@ void ConfigureGeneral::SetupPerGameUI() {
Settings::values.use_speed_limit, use_speed_limit);
ConfigurationShared::SetColoredTristate(ui->use_multi_core, Settings::values.use_multi_core,
use_multi_core);
ConfigurationShared::SetColoredTristate(ui->use_extended_memory_layout,
Settings::values.use_extended_memory_layout,
use_extended_memory_layout);
connect(ui->toggle_speed_limit, &QCheckBox::clicked, ui->speed_limit, [this]() {
ui->speed_limit->setEnabled(ui->toggle_speed_limit->isChecked() &&

View File

@@ -47,6 +47,7 @@ private:
ConfigurationShared::CheckState use_speed_limit;
ConfigurationShared::CheckState use_multi_core;
ConfigurationShared::CheckState use_extended_memory_layout;
const Core::System& system;
};

View File

@@ -61,6 +61,13 @@
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="use_extended_memory_layout">
<property name="text">
<string>Extended memory layout (8GB DRAM)</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="toggle_check_exit">
<property name="text">

View File

@@ -4,20 +4,76 @@
// Include this early to include Vulkan headers how we want to
#include "video_core/vulkan_common/vulkan_wrapper.h"
#include <algorithm>
#include <iosfwd>
#include <iterator>
#include <string>
#include <tuple>
#include <utility>
#include <vector>
#include <QBoxLayout>
#include <QCheckBox>
#include <QColorDialog>
#include <QVulkanInstance>
#include <QComboBox>
#include <QIcon>
#include <QLabel>
#include <QPixmap>
#include <QPushButton>
#include <QSlider>
#include <QStringLiteral>
#include <QtCore/qobjectdefs.h>
#include <qcoreevent.h>
#include <qglobal.h>
#include <vulkan/vulkan_core.h>
#include "common/common_types.h"
#include "common/dynamic_library.h"
#include "common/logging/log.h"
#include "common/settings.h"
#include "core/core.h"
#include "ui_configure_graphics.h"
#include "video_core/vulkan_common/vulkan_instance.h"
#include "video_core/vulkan_common/vulkan_library.h"
#include "video_core/vulkan_common/vulkan_surface.h"
#include "yuzu/configuration/configuration_shared.h"
#include "yuzu/configuration/configure_graphics.h"
#include "yuzu/qt_common.h"
#include "yuzu/uisettings.h"
static const std::vector<VkPresentModeKHR> default_present_modes{VK_PRESENT_MODE_IMMEDIATE_KHR,
VK_PRESENT_MODE_FIFO_KHR};
// Converts a setting to a present mode (or vice versa)
static constexpr VkPresentModeKHR VSyncSettingToMode(Settings::VSyncMode mode) {
switch (mode) {
case Settings::VSyncMode::Immediate:
return VK_PRESENT_MODE_IMMEDIATE_KHR;
case Settings::VSyncMode::Mailbox:
return VK_PRESENT_MODE_MAILBOX_KHR;
case Settings::VSyncMode::FIFO:
return VK_PRESENT_MODE_FIFO_KHR;
case Settings::VSyncMode::FIFORelaxed:
return VK_PRESENT_MODE_FIFO_RELAXED_KHR;
default:
return VK_PRESENT_MODE_FIFO_KHR;
}
}
static constexpr Settings::VSyncMode PresentModeToSetting(VkPresentModeKHR mode) {
switch (mode) {
case VK_PRESENT_MODE_IMMEDIATE_KHR:
return Settings::VSyncMode::Immediate;
case VK_PRESENT_MODE_MAILBOX_KHR:
return Settings::VSyncMode::Mailbox;
case VK_PRESENT_MODE_FIFO_KHR:
return Settings::VSyncMode::FIFO;
case VK_PRESENT_MODE_FIFO_RELAXED_KHR:
return Settings::VSyncMode::FIFORelaxed;
default:
return Settings::VSyncMode::FIFO;
}
}
ConfigureGraphics::ConfigureGraphics(const Core::System& system_, QWidget* parent)
: QWidget(parent), ui{std::make_unique<Ui::ConfigureGraphics>()}, system{system_} {
vulkan_device = Settings::values.vulkan_device.GetValue();
@@ -39,13 +95,16 @@ ConfigureGraphics::ConfigureGraphics(const Core::System& system_, QWidget* paren
connect(ui->api, qOverload<int>(&QComboBox::currentIndexChanged), this, [this] {
UpdateAPILayout();
PopulateVSyncModeSelection();
if (!Settings::IsConfiguringGlobal()) {
ConfigurationShared::SetHighlight(
ui->api_widget, ui->api->currentIndex() != ConfigurationShared::USE_GLOBAL_INDEX);
}
});
connect(ui->device, qOverload<int>(&QComboBox::activated), this,
[this](int device) { UpdateDeviceSelection(device); });
connect(ui->device, qOverload<int>(&QComboBox::activated), this, [this](int device) {
UpdateDeviceSelection(device);
PopulateVSyncModeSelection();
});
connect(ui->backend, qOverload<int>(&QComboBox::activated), this,
[this](int backend) { UpdateShaderBackendSelection(backend); });
@@ -70,6 +129,43 @@ ConfigureGraphics::ConfigureGraphics(const Core::System& system_, QWidget* paren
ui->fsr_sharpening_label->setVisible(Settings::IsConfiguringGlobal());
}
void ConfigureGraphics::PopulateVSyncModeSelection() {
const Settings::RendererBackend backend{GetCurrentGraphicsBackend()};
if (backend == Settings::RendererBackend::Null) {
ui->vsync_mode_combobox->setEnabled(false);
return;
}
ui->vsync_mode_combobox->setEnabled(true);
const int current_index = //< current selected vsync mode from combobox
ui->vsync_mode_combobox->currentIndex();
const auto current_mode = //< current selected vsync mode as a VkPresentModeKHR
current_index == -1 ? VSyncSettingToMode(Settings::values.vsync_mode.GetValue())
: vsync_mode_combobox_enum_map[current_index];
int index{};
const int device{ui->device->currentIndex()}; //< current selected Vulkan device
const auto& present_modes = //< relevant vector of present modes for the selected device or API
backend == Settings::RendererBackend::Vulkan ? device_present_modes[device]
: default_present_modes;
ui->vsync_mode_combobox->clear();
vsync_mode_combobox_enum_map.clear();
vsync_mode_combobox_enum_map.reserve(present_modes.size());
for (const auto present_mode : present_modes) {
const auto mode_name = TranslateVSyncMode(present_mode, backend);
if (mode_name.isEmpty()) {
continue;
}
ui->vsync_mode_combobox->insertItem(index, mode_name);
vsync_mode_combobox_enum_map.push_back(present_mode);
if (present_mode == current_mode) {
ui->vsync_mode_combobox->setCurrentIndex(index);
}
index++;
}
}
void ConfigureGraphics::UpdateDeviceSelection(int device) {
if (device == -1) {
return;
@@ -99,6 +195,9 @@ void ConfigureGraphics::SetConfiguration() {
ui->nvdec_emulation_widget->setEnabled(runtime_lock);
ui->resolution_combobox->setEnabled(runtime_lock);
ui->accelerate_astc->setEnabled(runtime_lock);
ui->vsync_mode_layout->setEnabled(runtime_lock ||
Settings::values.renderer_backend.GetValue() ==
Settings::RendererBackend::Vulkan);
ui->use_disk_shader_cache->setChecked(Settings::values.use_disk_shader_cache.GetValue());
ui->use_asynchronous_gpu_emulation->setChecked(
Settings::values.use_asynchronous_gpu_emulation.GetValue());
@@ -170,7 +269,24 @@ void ConfigureGraphics::SetConfiguration() {
Settings::values.bg_green.GetValue(),
Settings::values.bg_blue.GetValue()));
UpdateAPILayout();
PopulateVSyncModeSelection(); //< must happen after UpdateAPILayout
SetFSRIndicatorText(ui->fsr_sharpening_slider->sliderPosition());
// VSync setting needs to be determined after populating the VSync combobox
if (Settings::IsConfiguringGlobal()) {
const auto vsync_mode_setting = Settings::values.vsync_mode.GetValue();
const auto vsync_mode = VSyncSettingToMode(vsync_mode_setting);
int index{};
for (const auto mode : vsync_mode_combobox_enum_map) {
if (mode == vsync_mode) {
break;
}
index++;
}
if (static_cast<unsigned long>(index) < vsync_mode_combobox_enum_map.size()) {
ui->vsync_mode_combobox->setCurrentIndex(index);
}
}
}
void ConfigureGraphics::SetFSRIndicatorText(int percentage) {
@@ -178,6 +294,27 @@ void ConfigureGraphics::SetFSRIndicatorText(int percentage) {
tr("%1%", "FSR sharpening percentage (e.g. 50%)").arg(100 - (percentage / 2)));
}
const QString ConfigureGraphics::TranslateVSyncMode(VkPresentModeKHR mode,
Settings::RendererBackend backend) const {
switch (mode) {
case VK_PRESENT_MODE_IMMEDIATE_KHR:
return backend == Settings::RendererBackend::OpenGL
? tr("Off")
: QStringLiteral("Immediate (%1)").arg(tr("VSync Off"));
case VK_PRESENT_MODE_MAILBOX_KHR:
return QStringLiteral("Mailbox (%1)").arg(tr("Recommended"));
case VK_PRESENT_MODE_FIFO_KHR:
return backend == Settings::RendererBackend::OpenGL
? tr("On")
: QStringLiteral("FIFO (%1)").arg(tr("VSync On"));
case VK_PRESENT_MODE_FIFO_RELAXED_KHR:
return QStringLiteral("FIFO Relaxed");
default:
return {};
break;
}
}
void ConfigureGraphics::ApplyConfiguration() {
const auto resolution_setup = static_cast<Settings::ResolutionSetup>(
ui->resolution_combobox->currentIndex() -
@@ -232,6 +369,10 @@ void ConfigureGraphics::ApplyConfiguration() {
Settings::values.anti_aliasing.SetValue(anti_aliasing);
}
Settings::values.fsr_sharpening_slider.SetValue(ui->fsr_sharpening_slider->value());
const auto mode = vsync_mode_combobox_enum_map[ui->vsync_mode_combobox->currentIndex()];
const auto vsync_mode = PresentModeToSetting(mode);
Settings::values.vsync_mode.SetValue(vsync_mode);
} else {
if (ui->resolution_combobox->currentIndex() == ConfigurationShared::USE_GLOBAL_INDEX) {
Settings::values.resolution_setup.SetGlobal(true);
@@ -345,7 +486,9 @@ void ConfigureGraphics::UpdateAPILayout() {
ui->backend_widget->setVisible(true);
break;
case Settings::RendererBackend::Vulkan:
ui->device->setCurrentIndex(vulkan_device);
if (static_cast<int>(vulkan_device) < ui->device->count()) {
ui->device->setCurrentIndex(vulkan_device);
}
ui->device_widget->setVisible(true);
ui->backend_widget->setVisible(false);
break;
@@ -363,16 +506,27 @@ void ConfigureGraphics::RetrieveVulkanDevices() try {
using namespace Vulkan;
auto* window = this->window()->windowHandle();
auto wsi = QtCommon::GetWindowSystemInfo(window);
vk::InstanceDispatch dld;
const Common::DynamicLibrary library = OpenLibrary();
const vk::Instance instance = CreateInstance(library, dld, VK_API_VERSION_1_1);
const vk::Instance instance = CreateInstance(library, dld, VK_API_VERSION_1_1, wsi.type);
const std::vector<VkPhysicalDevice> physical_devices = instance.EnumeratePhysicalDevices();
vk::SurfaceKHR surface = //< needed to view present modes for a device
CreateSurface(instance, wsi);
vulkan_devices.clear();
vulkan_devices.reserve(physical_devices.size());
device_present_modes.clear();
device_present_modes.reserve(physical_devices.size());
for (const VkPhysicalDevice device : physical_devices) {
const std::string name = vk::PhysicalDevice(device, dld).GetProperties().deviceName;
const auto physical_device = vk::PhysicalDevice(device, dld);
const std::string name = physical_device.GetProperties().deviceName;
const std::vector<VkPresentModeKHR> present_modes =
physical_device.GetSurfacePresentModesKHR(*surface);
vulkan_devices.push_back(QString::fromStdString(name));
device_present_modes.push_back(present_modes);
}
} catch (const Vulkan::vk::Exception& exception) {
LOG_ERROR(Frontend, "Failed to enumerate devices with error: {}", exception.what());
@@ -465,4 +619,6 @@ 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)));
ui->vsync_mode_layout->setVisible(false);
}

View File

@@ -5,9 +5,21 @@
#include <memory>
#include <vector>
#include <QColor>
#include <QString>
#include <QWidget>
#include "common/settings.h"
#include <qobjectdefs.h>
#include <vulkan/vulkan_core.h>
#include "common/common_types.h"
class QEvent;
class QObject;
namespace Settings {
enum class NvdecEmulation : u32;
enum class RendererBackend : u32;
enum class ShaderBackend : u32;
} // namespace Settings
namespace Core {
class System;
@@ -35,6 +47,7 @@ private:
void changeEvent(QEvent* event) override;
void RetranslateUI();
void PopulateVSyncModeSelection();
void UpdateBackgroundColorButton(QColor color);
void UpdateAPILayout();
void UpdateDeviceSelection(int device);
@@ -43,6 +56,10 @@ private:
void RetrieveVulkanDevices();
void SetFSRIndicatorText(int percentage);
/* Turns a Vulkan present mode into a textual string for a UI
* (and eventually for a human to read) */
const QString TranslateVSyncMode(VkPresentModeKHR mode,
Settings::RendererBackend backend) const;
void SetupPerGameUI();
@@ -58,6 +75,10 @@ private:
ConfigurationShared::CheckState use_asynchronous_gpu_emulation;
std::vector<QString> vulkan_devices;
std::vector<std::vector<VkPresentModeKHR>> device_present_modes;
std::vector<VkPresentModeKHR>
vsync_mode_combobox_enum_map; //< Keeps track of which present mode corresponds to which
// selection in the combobox
u32 vulkan_device{};
Settings::ShaderBackend shader_backend{};

View File

@@ -188,6 +188,44 @@
</property>
</widget>
</item>
<item>
<widget class="QWidget" name="vsync_mode_layout" native="true">
<layout class="QHBoxLayout" name="horizontalLayout_4">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QLabel" name="vsync_mode_label">
<property name="text">
<string>VSync Mode:</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="vsync_mode_combobox">
<property name="toolTip">
<string>FIFO (VSync) does not drop frames or exhibit tearing but is limited by the screen refresh rate.
FIFO Relaxed is similar to FIFO but allows tearing as it recovers from a slow down.
Mailbox can have lower latency than FIFO and does not tear but may drop frames.
Immediate (no synchronization) just presents whatever is available and can exhibit tearing.</string>
</property>
<property name="currentText">
<string/>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QWidget" name="nvdec_emulation_widget" native="true">
<layout class="QHBoxLayout" name="nvdec_emulation_layout">
@@ -366,7 +404,7 @@
</item>
<item>
<property name="text">
<string>1.5X (1080p/1620p) [EXPERIMENTAL]</string>
<string>1.5X (1080p/1620p) [EXPERIMENTAL]</string>
</property>
</item>
<item>

View File

@@ -21,7 +21,6 @@ ConfigureGraphicsAdvanced::~ConfigureGraphicsAdvanced() = default;
void ConfigureGraphicsAdvanced::SetConfiguration() {
const bool runtime_lock = !system.IsPoweredOn();
ui->use_vsync->setEnabled(runtime_lock);
ui->async_present->setEnabled(runtime_lock);
ui->renderer_force_max_clock->setEnabled(runtime_lock);
ui->async_astc->setEnabled(runtime_lock);
@@ -30,7 +29,6 @@ void ConfigureGraphicsAdvanced::SetConfiguration() {
ui->async_present->setChecked(Settings::values.async_presentation.GetValue());
ui->renderer_force_max_clock->setChecked(Settings::values.renderer_force_max_clock.GetValue());
ui->use_vsync->setChecked(Settings::values.use_vsync.GetValue());
ui->async_astc->setChecked(Settings::values.async_astc.GetValue());
ui->use_asynchronous_shaders->setChecked(Settings::values.use_asynchronous_shaders.GetValue());
ui->use_fast_gpu_time->setChecked(Settings::values.use_fast_gpu_time.GetValue());
@@ -63,7 +61,6 @@ void ConfigureGraphicsAdvanced::ApplyConfiguration() {
renderer_force_max_clock);
ConfigurationShared::ApplyPerGameSetting(&Settings::values.max_anisotropy,
ui->anisotropic_filtering_combobox);
ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_vsync, ui->use_vsync, use_vsync);
ConfigurationShared::ApplyPerGameSetting(&Settings::values.async_astc, ui->async_astc,
async_astc);
ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_asynchronous_shaders,
@@ -97,7 +94,6 @@ void ConfigureGraphicsAdvanced::SetupPerGameUI() {
ui->async_present->setEnabled(Settings::values.async_presentation.UsingGlobal());
ui->renderer_force_max_clock->setEnabled(
Settings::values.renderer_force_max_clock.UsingGlobal());
ui->use_vsync->setEnabled(Settings::values.use_vsync.UsingGlobal());
ui->async_astc->setEnabled(Settings::values.async_astc.UsingGlobal());
ui->use_asynchronous_shaders->setEnabled(
Settings::values.use_asynchronous_shaders.UsingGlobal());
@@ -117,7 +113,6 @@ void ConfigureGraphicsAdvanced::SetupPerGameUI() {
ConfigurationShared::SetColoredTristate(ui->renderer_force_max_clock,
Settings::values.renderer_force_max_clock,
renderer_force_max_clock);
ConfigurationShared::SetColoredTristate(ui->use_vsync, Settings::values.use_vsync, use_vsync);
ConfigurationShared::SetColoredTristate(ui->async_astc, Settings::values.async_astc,
async_astc);
ConfigurationShared::SetColoredTristate(ui->use_asynchronous_shaders,

View File

@@ -86,16 +86,6 @@
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="use_vsync">
<property name="toolTip">
<string>VSync prevents the screen from tearing, but some graphics cards have lower performance with VSync enabled. Keep it enabled if you don't notice a performance difference.</string>
</property>
<property name="text">
<string>Use VSync</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="async_astc">
<property name="toolTip">

View File

@@ -8,6 +8,7 @@
#include <QInputDialog>
#include <QMenu>
#include <QMessageBox>
#include <QMouseEvent>
#include <QTimer>
#include "common/assert.h"
#include "common/param_package.h"

View File

@@ -111,9 +111,6 @@ void ConfigureSystem::SetConfiguration() {
ui->custom_rtc_edit->setDateTime(QDateTime::fromSecsSinceEpoch(rtc_time));
ui->device_name_edit->setText(
QString::fromUtf8(Settings::values.device_name.GetValue().c_str()));
ui->use_unsafe_extended_memory_layout->setEnabled(enabled);
ui->use_unsafe_extended_memory_layout->setChecked(
Settings::values.use_unsafe_extended_memory_layout.GetValue());
if (Settings::IsConfiguringGlobal()) {
ui->combo_language->setCurrentIndex(Settings::values.language_index.GetValue());
@@ -163,9 +160,6 @@ void ConfigureSystem::ApplyConfiguration() {
ConfigurationShared::ApplyPerGameSetting(&Settings::values.region_index, ui->combo_region);
ConfigurationShared::ApplyPerGameSetting(&Settings::values.time_zone_index,
ui->combo_time_zone);
ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_unsafe_extended_memory_layout,
ui->use_unsafe_extended_memory_layout,
use_unsafe_extended_memory_layout);
if (Settings::IsConfiguringGlobal()) {
// Guard if during game and set to game-specific value
@@ -221,10 +215,6 @@ void ConfigureSystem::SetupPerGameUI() {
Settings::values.rng_seed.GetValue().has_value(),
Settings::values.rng_seed.GetValue(true).has_value(), use_rng_seed);
ConfigurationShared::SetColoredTristate(ui->use_unsafe_extended_memory_layout,
Settings::values.use_unsafe_extended_memory_layout,
use_unsafe_extended_memory_layout);
ui->custom_rtc_checkbox->setVisible(false);
ui->custom_rtc_edit->setVisible(false);
}

View File

@@ -41,7 +41,6 @@ private:
bool enabled = false;
ConfigurationShared::CheckState use_rng_seed;
ConfigurationShared::CheckState use_unsafe_extended_memory_layout;
Core::System& system;
};

View File

@@ -478,13 +478,6 @@
</property>
</widget>
</item>
<item row="7" column="0">
<widget class="QCheckBox" name="use_unsafe_extended_memory_layout">
<property name="text">
<string>Unsafe extended memory layout (8GB DRAM)</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>

55
src/yuzu/qt_common.cpp Normal file
View File

@@ -0,0 +1,55 @@
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <QGuiApplication>
#include <QStringLiteral>
#include <QWindow>
#include "common/logging/log.h"
#include "core/frontend/emu_window.h"
#include "yuzu/qt_common.h"
#ifdef __linux__
#include <qpa/qplatformnativeinterface.h>
#endif
namespace QtCommon {
Core::Frontend::WindowSystemType GetWindowSystemType() {
// Determine WSI type based on Qt platform.
QString platform_name = QGuiApplication::platformName();
if (platform_name == QStringLiteral("windows"))
return Core::Frontend::WindowSystemType::Windows;
else if (platform_name == QStringLiteral("xcb"))
return Core::Frontend::WindowSystemType::X11;
else if (platform_name == QStringLiteral("wayland"))
return Core::Frontend::WindowSystemType::Wayland;
else if (platform_name == QStringLiteral("wayland-egl"))
return Core::Frontend::WindowSystemType::Wayland;
else if (platform_name == QStringLiteral("cocoa"))
return Core::Frontend::WindowSystemType::Cocoa;
else if (platform_name == QStringLiteral("android"))
return Core::Frontend::WindowSystemType::Android;
LOG_CRITICAL(Frontend, "Unknown Qt platform {}!", platform_name.toStdString());
return Core::Frontend::WindowSystemType::Windows;
} // namespace Core::Frontend::WindowSystemType
Core::Frontend::EmuWindow::WindowSystemInfo GetWindowSystemInfo(QWindow* window) {
Core::Frontend::EmuWindow::WindowSystemInfo wsi;
wsi.type = GetWindowSystemType();
// Our Win32 Qt external doesn't have the private API.
#if defined(WIN32) || defined(__APPLE__)
wsi.render_surface = window ? reinterpret_cast<void*>(window->winId()) : nullptr;
#else
QPlatformNativeInterface* pni = QGuiApplication::platformNativeInterface();
wsi.display_connection = pni->nativeResourceForWindow("display", window);
if (wsi.type == Core::Frontend::WindowSystemType::Wayland)
wsi.render_surface = window ? pni->nativeResourceForWindow("surface", window) : nullptr;
else
wsi.render_surface = window ? reinterpret_cast<void*>(window->winId()) : nullptr;
#endif
wsi.render_surface_scale = window ? static_cast<float>(window->devicePixelRatio()) : 1.0f;
return wsi;
}
} // namespace QtCommon

15
src/yuzu/qt_common.h Normal file
View File

@@ -0,0 +1,15 @@
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <QWindow>
#include "core/frontend/emu_window.h"
namespace QtCommon {
Core::Frontend::WindowSystemType GetWindowSystemType();
Core::Frontend::EmuWindow::WindowSystemInfo GetWindowSystemInfo(QWindow* window);
} // namespace QtCommon

View File

@@ -274,7 +274,7 @@ void Config::ReadValues() {
// Core
ReadSetting("Core", Settings::values.use_multi_core);
ReadSetting("Core", Settings::values.use_unsafe_extended_memory_layout);
ReadSetting("Core", Settings::values.use_extended_memory_layout);
// Cpu
ReadSetting("Cpu", Settings::values.cpu_accuracy);
@@ -320,7 +320,7 @@ void Config::ReadValues() {
ReadSetting("Renderer", Settings::values.use_disk_shader_cache);
ReadSetting("Renderer", Settings::values.gpu_accuracy);
ReadSetting("Renderer", Settings::values.use_asynchronous_gpu_emulation);
ReadSetting("Renderer", Settings::values.use_vsync);
ReadSetting("Renderer", Settings::values.vsync_mode);
ReadSetting("Renderer", Settings::values.shader_backend);
ReadSetting("Renderer", Settings::values.use_asynchronous_shaders);
ReadSetting("Renderer", Settings::values.nvdec_emulation);

View File

@@ -163,9 +163,9 @@ keyboard_enabled =
# 0: Disabled, 1 (default): Enabled
use_multi_core =
# Enable unsafe extended guest system memory layout (8GB DRAM)
# Enable extended guest system memory layout (8GB DRAM)
# 0 (default): Disabled, 1: Enabled
use_unsafe_extended_memory_layout =
use_extended_memory_layout =
[Cpu]
# Adjusts various optimizations.
@@ -325,8 +325,14 @@ aspect_ratio =
# 0: Default, 1: 2x, 2: 4x, 3: 8x, 4: 16x
max_anisotropy =
# Whether to enable V-Sync (caps the framerate at 60FPS) or not.
# 0 (default): Off, 1: On
# Whether to enable VSync or not.
# OpenGL: Values other than 0 enable VSync
# Vulkan: FIFO is selected if the requested mode is not supported by the driver.
# FIFO (VSync) does not drop frames or exhibit tearing but is limited by the screen refresh rate.
# FIFO Relaxed is similar to FIFO but allows tearing as it recovers from a slow down.
# Mailbox can have lower latency than FIFO and does not tear but may drop frames.
# Immediate (no synchronization) just presents whatever is available and can exhibit tearing.
# 0: Immediate (Off), 1: Mailbox, 2 (Default): FIFO (On), 3: FIFO Relaxed
use_vsync =
# Selects the OpenGL shader backend. NV_gpu_program5 is required for GLASM. If NV_gpu_program5 is

View File

@@ -49,7 +49,7 @@
"overrides": [
{
"name": "catch2",
"version": "3.3.1"
"version": "3.0.1"
},
{
"name": "fmt",