diff options
Diffstat (limited to 'src')
43 files changed, 220 insertions, 375 deletions
diff --git a/src/common/fs/fs_util.cpp b/src/common/fs/fs_util.cpp index 357cf5855..9f8671982 100644 --- a/src/common/fs/fs_util.cpp +++ b/src/common/fs/fs_util.cpp @@ -20,6 +20,10 @@ std::string ToUTF8String(std::u8string_view u8_string) { return std::string{u8_string.begin(), u8_string.end()}; } +std::string BufferToUTF8String(std::span<const u8> buffer) { + return std::string{buffer.begin(), std::ranges::find(buffer, u8{0})}; +} + std::string PathToUTF8String(const std::filesystem::path& path) { return ToUTF8String(path.u8string()); } diff --git a/src/common/fs/fs_util.h b/src/common/fs/fs_util.h index ec9950ee7..1ec82eb35 100644 --- a/src/common/fs/fs_util.h +++ b/src/common/fs/fs_util.h @@ -47,6 +47,17 @@ concept IsChar = std::same_as<T, char>; [[nodiscard]] std::string ToUTF8String(std::u8string_view u8_string); /** + * Converts a buffer of bytes to a UTF8-encoded std::string. + * This converts from the start of the buffer until the first encountered null-terminator. + * If no null-terminator is found, this converts the entire buffer instead. + * + * @param buffer Buffer of bytes + * + * @returns UTF-8 encoded std::string. + */ +[[nodiscard]] std::string BufferToUTF8String(std::span<const u8> buffer); + +/** * Converts a filesystem path to a UTF-8 encoded std::string. * * @param path Filesystem path diff --git a/src/common/settings.cpp b/src/common/settings.cpp index 66268ea0f..996315999 100644 --- a/src/common/settings.cpp +++ b/src/common/settings.cpp @@ -48,8 +48,8 @@ void LogSettings() { log_setting("Core_UseMultiCore", values.use_multi_core.GetValue()); log_setting("CPU_Accuracy", values.cpu_accuracy.GetValue()); log_setting("Renderer_UseResolutionFactor", values.resolution_factor.GetValue()); - log_setting("Renderer_UseFrameLimit", values.use_frame_limit.GetValue()); - log_setting("Renderer_FrameLimit", values.frame_limit.GetValue()); + log_setting("Renderer_UseSpeedLimit", values.use_speed_limit.GetValue()); + log_setting("Renderer_SpeedLimit", values.speed_limit.GetValue()); log_setting("Renderer_UseDiskShaderCache", values.use_disk_shader_cache.GetValue()); log_setting("Renderer_GPUAccuracyLevel", values.gpu_accuracy.GetValue()); log_setting("Renderer_UseAsynchronousGpuEmulation", @@ -132,8 +132,8 @@ void RestoreGlobalState(bool is_powered_on) { values.vulkan_device.SetGlobal(true); values.aspect_ratio.SetGlobal(true); values.max_anisotropy.SetGlobal(true); - values.use_frame_limit.SetGlobal(true); - values.frame_limit.SetGlobal(true); + values.use_speed_limit.SetGlobal(true); + values.speed_limit.SetGlobal(true); values.use_disk_shader_cache.SetGlobal(true); values.gpu_accuracy.SetGlobal(true); values.use_asynchronous_gpu_emulation.SetGlobal(true); diff --git a/src/common/settings.h b/src/common/settings.h index 801bed603..cfc1ab46f 100644 --- a/src/common/settings.h +++ b/src/common/settings.h @@ -336,14 +336,15 @@ struct Values { "fullscreen_mode"}; Setting<int> aspect_ratio{0, "aspect_ratio"}; Setting<int> max_anisotropy{0, "max_anisotropy"}; - Setting<bool> use_frame_limit{true, "use_frame_limit"}; - Setting<u16> frame_limit{100, "frame_limit"}; + Setting<bool> use_speed_limit{true, "use_speed_limit"}; + Setting<u16> speed_limit{100, "speed_limit"}; Setting<bool> use_disk_shader_cache{true, "use_disk_shader_cache"}; Setting<GPUAccuracy> gpu_accuracy{GPUAccuracy::High, "gpu_accuracy"}; Setting<bool> use_asynchronous_gpu_emulation{true, "use_asynchronous_gpu_emulation"}; Setting<bool> use_nvdec_emulation{true, "use_nvdec_emulation"}; Setting<bool> accelerate_astc{true, "accelerate_astc"}; Setting<bool> use_vsync{true, "use_vsync"}; + BasicSetting<u16> fps_cap{1000, "fps_cap"}; BasicSetting<bool> disable_fps_limit{false, "disable_fps_limit"}; Setting<ShaderBackend> shader_backend{ShaderBackend::GLASM, "shader_backend"}; Setting<bool> use_asynchronous_shaders{false, "use_asynchronous_shaders"}; diff --git a/src/core/core.cpp b/src/core/core.cpp index 15226cf41..d3e84c4ef 100644 --- a/src/core/core.cpp +++ b/src/core/core.cpp @@ -411,7 +411,7 @@ struct System::Impl { std::string status_details = ""; std::unique_ptr<Core::PerfStats> perf_stats; - Core::FrameLimiter frame_limiter; + Core::SpeedLimiter speed_limiter; bool is_multicore{}; bool is_async_gpu{}; @@ -606,12 +606,12 @@ const Core::PerfStats& System::GetPerfStats() const { return *impl->perf_stats; } -Core::FrameLimiter& System::FrameLimiter() { - return impl->frame_limiter; +Core::SpeedLimiter& System::SpeedLimiter() { + return impl->speed_limiter; } -const Core::FrameLimiter& System::FrameLimiter() const { - return impl->frame_limiter; +const Core::SpeedLimiter& System::SpeedLimiter() const { + return impl->speed_limiter; } Loader::ResultStatus System::GetGameName(std::string& out) const { diff --git a/src/core/core.h b/src/core/core.h index b93c32e60..ea143043c 100644 --- a/src/core/core.h +++ b/src/core/core.h @@ -94,7 +94,7 @@ class ARM_Interface; class CpuManager; class DeviceMemory; class ExclusiveMonitor; -class FrameLimiter; +class SpeedLimiter; class PerfStats; class Reporter; class TelemetrySession; @@ -292,11 +292,11 @@ public: /// Provides a constant reference to the internal PerfStats instance. [[nodiscard]] const Core::PerfStats& GetPerfStats() const; - /// Provides a reference to the frame limiter; - [[nodiscard]] Core::FrameLimiter& FrameLimiter(); + /// Provides a reference to the speed limiter; + [[nodiscard]] Core::SpeedLimiter& SpeedLimiter(); - /// Provides a constant referent to the frame limiter - [[nodiscard]] const Core::FrameLimiter& FrameLimiter() const; + /// Provides a constant reference to the speed limiter + [[nodiscard]] const Core::SpeedLimiter& SpeedLimiter() const; /// Gets the name of the current game [[nodiscard]] Loader::ResultStatus GetGameName(std::string& out) const; diff --git a/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp b/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp index 2cc0da124..ce6065db2 100644 --- a/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp +++ b/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp @@ -54,7 +54,7 @@ void nvdisp_disp0::flip(u32 buffer_handle, u32 offset, u32 format, u32 width, u3 system.GetPerfStats().EndSystemFrame(); system.GPU().SwapBuffers(&framebuffer); - system.FrameLimiter().DoFrameLimiting(system.CoreTiming().GetGlobalTimeUs()); + system.SpeedLimiter().DoSpeedLimiting(system.CoreTiming().GetGlobalTimeUs()); system.GetPerfStats().BeginSystemFrame(); } diff --git a/src/core/hle/service/nvflinger/nvflinger.cpp b/src/core/hle/service/nvflinger/nvflinger.cpp index 1d810562f..941748970 100644 --- a/src/core/hle/service/nvflinger/nvflinger.cpp +++ b/src/core/hle/service/nvflinger/nvflinger.cpp @@ -307,11 +307,12 @@ void NVFlinger::Compose() { } s64 NVFlinger::GetNextTicks() const { - if (Settings::values.disable_fps_limit.GetValue()) { - return 0; - } - constexpr s64 max_hertz = 120LL; - return (1000000000 * (1LL << swap_interval)) / max_hertz; + static constexpr s64 max_hertz = 120LL; + + const auto& settings = Settings::values; + const bool unlocked_fps = settings.disable_fps_limit.GetValue(); + const s64 fps_cap = unlocked_fps ? static_cast<s64>(settings.fps_cap.GetValue()) : 1; + return (1000000000 * (1LL << swap_interval)) / (max_hertz * fps_cap); } } // namespace Service::NVFlinger diff --git a/src/core/perf_stats.cpp b/src/core/perf_stats.cpp index 6635a1339..c9ded49d0 100644 --- a/src/core/perf_stats.cpp +++ b/src/core/perf_stats.cpp @@ -127,15 +127,15 @@ double PerfStats::GetLastFrameTimeScale() const { return duration_cast<DoubleSecs>(previous_frame_length).count() / FRAME_LENGTH; } -void FrameLimiter::DoFrameLimiting(microseconds current_system_time_us) { - if (!Settings::values.use_frame_limit.GetValue() || +void SpeedLimiter::DoSpeedLimiting(microseconds current_system_time_us) { + if (!Settings::values.use_speed_limit.GetValue() || Settings::values.use_multi_core.GetValue()) { return; } auto now = Clock::now(); - const double sleep_scale = Settings::values.frame_limit.GetValue() / 100.0; + const double sleep_scale = Settings::values.speed_limit.GetValue() / 100.0; // Max lag caused by slow frames. Shouldn't be more than the length of a frame at the current // speed percent or it will clamp too much and prevent this from properly limiting to that @@ -143,17 +143,17 @@ void FrameLimiter::DoFrameLimiting(microseconds current_system_time_us) { // limiting const microseconds max_lag_time_us = duration_cast<microseconds>( std::chrono::duration<double, std::chrono::microseconds::period>(25ms / sleep_scale)); - frame_limiting_delta_err += duration_cast<microseconds>( + speed_limiting_delta_err += duration_cast<microseconds>( std::chrono::duration<double, std::chrono::microseconds::period>( (current_system_time_us - previous_system_time_us) / sleep_scale)); - frame_limiting_delta_err -= duration_cast<microseconds>(now - previous_walltime); - frame_limiting_delta_err = - std::clamp(frame_limiting_delta_err, -max_lag_time_us, max_lag_time_us); + speed_limiting_delta_err -= duration_cast<microseconds>(now - previous_walltime); + speed_limiting_delta_err = + std::clamp(speed_limiting_delta_err, -max_lag_time_us, max_lag_time_us); - if (frame_limiting_delta_err > microseconds::zero()) { - std::this_thread::sleep_for(frame_limiting_delta_err); + if (speed_limiting_delta_err > microseconds::zero()) { + std::this_thread::sleep_for(speed_limiting_delta_err); auto now_after_sleep = Clock::now(); - frame_limiting_delta_err -= duration_cast<microseconds>(now_after_sleep - now); + speed_limiting_delta_err -= duration_cast<microseconds>(now_after_sleep - now); now = now_after_sleep; } diff --git a/src/core/perf_stats.h b/src/core/perf_stats.h index e5d603717..a2541906f 100644 --- a/src/core/perf_stats.h +++ b/src/core/perf_stats.h @@ -85,11 +85,11 @@ private: double previous_fps = 0; }; -class FrameLimiter { +class SpeedLimiter { public: using Clock = std::chrono::high_resolution_clock; - void DoFrameLimiting(std::chrono::microseconds current_system_time_us); + void DoSpeedLimiting(std::chrono::microseconds current_system_time_us); private: /// Emulated system time (in microseconds) at the last limiter invocation @@ -98,7 +98,7 @@ private: Clock::time_point previous_walltime = Clock::now(); /// Accumulated difference between walltime and emulated time - std::chrono::microseconds frame_limiting_delta_err{0}; + std::chrono::microseconds speed_limiting_delta_err{0}; }; } // namespace Core diff --git a/src/core/telemetry_session.cpp b/src/core/telemetry_session.cpp index 422de3a7d..5a8cfd301 100644 --- a/src/core/telemetry_session.cpp +++ b/src/core/telemetry_session.cpp @@ -221,8 +221,8 @@ void TelemetrySession::AddInitialInfo(Loader::AppLoader& app_loader, TranslateRenderer(Settings::values.renderer_backend.GetValue())); AddField(field_type, "Renderer_ResolutionFactor", Settings::values.resolution_factor.GetValue()); - AddField(field_type, "Renderer_UseFrameLimit", Settings::values.use_frame_limit.GetValue()); - AddField(field_type, "Renderer_FrameLimit", Settings::values.frame_limit.GetValue()); + AddField(field_type, "Renderer_UseSpeedLimit", Settings::values.use_speed_limit.GetValue()); + AddField(field_type, "Renderer_SpeedLimit", Settings::values.speed_limit.GetValue()); AddField(field_type, "Renderer_UseDiskShaderCache", Settings::values.use_disk_shader_cache.GetValue()); AddField(field_type, "Renderer_GPUAccuracyLevel", diff --git a/src/shader_recompiler/backend/glasm/emit_glasm.cpp b/src/shader_recompiler/backend/glasm/emit_glasm.cpp index a5e8c9b6e..4ce1c4f54 100644 --- a/src/shader_recompiler/backend/glasm/emit_glasm.cpp +++ b/src/shader_recompiler/backend/glasm/emit_glasm.cpp @@ -350,7 +350,7 @@ std::string_view InputPrimitive(InputTopology topology) { case InputTopology::Lines: return "LINES"; case InputTopology::LinesAdjacency: - return "LINESS_ADJACENCY"; + return "LINES_ADJACENCY"; case InputTopology::Triangles: return "TRIANGLES"; case InputTopology::TrianglesAdjacency: diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_instructions.h b/src/shader_recompiler/backend/spirv/emit_spirv_instructions.h index f99c02848..c9db1c164 100644 --- a/src/shader_recompiler/backend/spirv/emit_spirv_instructions.h +++ b/src/shader_recompiler/backend/spirv/emit_spirv_instructions.h @@ -2,6 +2,8 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#pragma once + #include <sirit/sirit.h> #include "common/common_types.h" diff --git a/src/shader_recompiler/exception.h b/src/shader_recompiler/exception.h index 337e7f0c8..277be8541 100644 --- a/src/shader_recompiler/exception.h +++ b/src/shader_recompiler/exception.h @@ -4,7 +4,7 @@ #pragma once -#include <stdexcept> +#include <exception> #include <string> #include <string_view> #include <utility> @@ -17,7 +17,7 @@ class Exception : public std::exception { public: explicit Exception(std::string message) noexcept : err_message{std::move(message)} {} - const char* what() const noexcept override { + [[nodiscard]] const char* what() const noexcept override { return err_message.c_str(); } @@ -36,21 +36,21 @@ private: class LogicError : public Exception { public: template <typename... Args> - LogicError(const char* message, Args&&... args) + explicit LogicError(const char* message, Args&&... args) : Exception{fmt::format(fmt::runtime(message), std::forward<Args>(args)...)} {} }; class RuntimeError : public Exception { public: template <typename... Args> - RuntimeError(const char* message, Args&&... args) + explicit RuntimeError(const char* message, Args&&... args) : Exception{fmt::format(fmt::runtime(message), std::forward<Args>(args)...)} {} }; class NotImplementedException : public Exception { public: template <typename... Args> - NotImplementedException(const char* message, Args&&... args) + explicit NotImplementedException(const char* message, Args&&... args) : Exception{fmt::format(fmt::runtime(message), std::forward<Args>(args)...)} { Append(" is not implemented"); } @@ -59,7 +59,7 @@ public: class InvalidArgument : public Exception { public: template <typename... Args> - InvalidArgument(const char* message, Args&&... args) + explicit InvalidArgument(const char* message, Args&&... args) : Exception{fmt::format(fmt::runtime(message), std::forward<Args>(args)...)} {} }; diff --git a/src/shader_recompiler/frontend/ir/ir_emitter.h b/src/shader_recompiler/frontend/ir/ir_emitter.h index 53f7b3b06..1b89ca5a0 100644 --- a/src/shader_recompiler/frontend/ir/ir_emitter.h +++ b/src/shader_recompiler/frontend/ir/ir_emitter.h @@ -327,8 +327,8 @@ public: const Value& derivates, const Value& offset, const F32& lod_clamp, TextureInstInfo info); [[nodiscard]] Value ImageRead(const Value& handle, const Value& coords, TextureInstInfo info); - [[nodiscard]] void ImageWrite(const Value& handle, const Value& coords, const Value& color, - TextureInstInfo info); + void ImageWrite(const Value& handle, const Value& coords, const Value& color, + TextureInstInfo info); [[nodiscard]] Value ImageAtomicIAdd(const Value& handle, const Value& coords, const Value& value, TextureInstInfo info); diff --git a/src/shader_recompiler/frontend/ir/value.h b/src/shader_recompiler/frontend/ir/value.h index 0c6bf684d..795194d41 100644 --- a/src/shader_recompiler/frontend/ir/value.h +++ b/src/shader_recompiler/frontend/ir/value.h @@ -197,8 +197,8 @@ public: } template <typename FlagsType> - requires(sizeof(FlagsType) <= sizeof(u32) && std::is_trivially_copyable_v<FlagsType>) - [[nodiscard]] void SetFlags(FlagsType value) noexcept { + requires(sizeof(FlagsType) <= sizeof(u32) && + std::is_trivially_copyable_v<FlagsType>) void SetFlags(FlagsType value) noexcept { std::memcpy(&flags, &value, sizeof(value)); } diff --git a/src/shader_recompiler/frontend/maxwell/control_flow.cpp b/src/shader_recompiler/frontend/maxwell/control_flow.cpp index 1a954a509..efe457baa 100644 --- a/src/shader_recompiler/frontend/maxwell/control_flow.cpp +++ b/src/shader_recompiler/frontend/maxwell/control_flow.cpp @@ -73,7 +73,7 @@ Token OpcodeToken(Opcode opcode) { return Token::PBK; case Opcode::PCNT: case Opcode::CONT: - return Token::PBK; + return Token::PCNT; case Opcode::PEXIT: case Opcode::EXIT: return Token::PEXIT; diff --git a/src/shader_recompiler/object_pool.h b/src/shader_recompiler/object_pool.h index f8b255b66..f3b12d04b 100644 --- a/src/shader_recompiler/object_pool.h +++ b/src/shader_recompiler/object_pool.h @@ -63,6 +63,7 @@ private: used_objects = std::exchange(rhs.used_objects, 0); num_objects = std::exchange(rhs.num_objects, 0); storage = std::move(rhs.storage); + return *this; } Chunk(Chunk&& rhs) noexcept diff --git a/src/video_core/buffer_cache/buffer_cache.h b/src/video_core/buffer_cache/buffer_cache.h index 24c858104..3b43554f9 100644 --- a/src/video_core/buffer_cache/buffer_cache.h +++ b/src/video_core/buffer_cache/buffer_cache.h @@ -817,7 +817,6 @@ void BufferCache<P>::CommitAsyncFlushesHigh() { const std::size_t size = interval.upper() - interval.lower(); const VAddr cpu_addr = interval.lower(); ForEachBufferInRange(cpu_addr, size, [&](BufferId buffer_id, Buffer& buffer) { - boost::container::small_vector<BufferCopy, 1> copies; buffer.ForEachDownloadRangeAndClear( cpu_addr, size, [&](u64 range_offset, u64 range_size) { const VAddr buffer_addr = buffer.CpuAddr(); diff --git a/src/video_core/engines/fermi_2d.h b/src/video_core/engines/fermi_2d.h index a4170ffff..d76c5ed56 100644 --- a/src/video_core/engines/fermi_2d.h +++ b/src/video_core/engines/fermi_2d.h @@ -299,7 +299,7 @@ public: }; private: - VideoCore::RasterizerInterface* rasterizer; + VideoCore::RasterizerInterface* rasterizer = nullptr; /// Performs the copy from the source surface to the destination surface as configured in the /// registers. diff --git a/src/video_core/engines/maxwell_dma.h b/src/video_core/engines/maxwell_dma.h index d3329b0f8..9e457ae16 100644 --- a/src/video_core/engines/maxwell_dma.h +++ b/src/video_core/engines/maxwell_dma.h @@ -227,7 +227,7 @@ private: Core::System& system; MemoryManager& memory_manager; - VideoCore::RasterizerInterface* rasterizer; + VideoCore::RasterizerInterface* rasterizer = nullptr; std::vector<u8> read_buffer; std::vector<u8> write_buffer; diff --git a/src/video_core/renderer_base.cpp b/src/video_core/renderer_base.cpp index c9a360aaf..3ea72fda9 100644 --- a/src/video_core/renderer_base.cpp +++ b/src/video_core/renderer_base.cpp @@ -19,9 +19,6 @@ RendererBase::~RendererBase() = default; void RendererBase::RefreshBaseSettings() { UpdateCurrentFramebufferLayout(); - - renderer_settings.use_framelimiter = Settings::values.use_frame_limit.GetValue(); - renderer_settings.set_background_color = true; } void RendererBase::UpdateCurrentFramebufferLayout() { diff --git a/src/video_core/renderer_base.h b/src/video_core/renderer_base.h index 63d8ad42a..22b80c328 100644 --- a/src/video_core/renderer_base.h +++ b/src/video_core/renderer_base.h @@ -21,9 +21,6 @@ class GraphicsContext; namespace VideoCore { struct RendererSettings { - std::atomic_bool use_framelimiter{false}; - std::atomic_bool set_background_color{false}; - // Screenshot std::atomic<bool> screenshot_requested{false}; void* screenshot_bits{}; diff --git a/src/video_core/renderer_opengl/gl_shader_cache.cpp b/src/video_core/renderer_opengl/gl_shader_cache.cpp index 8d6cc074c..1f4dda17e 100644 --- a/src/video_core/renderer_opengl/gl_shader_cache.cpp +++ b/src/video_core/renderer_opengl/gl_shader_cache.cpp @@ -441,7 +441,6 @@ std::unique_ptr<GraphicsPipeline> ShaderCache::CreateGraphicsPipeline( std::array<const Shader::Info*, Maxwell::MaxShaderStage> infos{}; - OGLProgram source_program; std::array<std::string, 5> sources; std::array<std::vector<u32>, 5> sources_spirv; Shader::Backend::Bindings binding; diff --git a/src/video_core/renderer_opengl/renderer_opengl.cpp b/src/video_core/renderer_opengl/renderer_opengl.cpp index 285e78384..f1b00c24c 100644 --- a/src/video_core/renderer_opengl/renderer_opengl.cpp +++ b/src/video_core/renderer_opengl/renderer_opengl.cpp @@ -328,12 +328,10 @@ void RendererOpenGL::ConfigureFramebufferTexture(TextureInfo& texture, } void RendererOpenGL::DrawScreen(const Layout::FramebufferLayout& layout) { - if (renderer_settings.set_background_color) { - // Update background color before drawing - glClearColor(Settings::values.bg_red.GetValue() / 255.0f, - Settings::values.bg_green.GetValue() / 255.0f, - Settings::values.bg_blue.GetValue() / 255.0f, 1.0f); - } + // Update background color before drawing + glClearColor(Settings::values.bg_red.GetValue() / 255.0f, + Settings::values.bg_green.GetValue() / 255.0f, + Settings::values.bg_blue.GetValue() / 255.0f, 1.0f); // Set projection matrix const std::array ortho_matrix = diff --git a/src/video_core/renderer_vulkan/vk_buffer_cache.cpp b/src/video_core/renderer_vulkan/vk_buffer_cache.cpp index f4b3ee95c..8ac58bc2f 100644 --- a/src/video_core/renderer_vulkan/vk_buffer_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_buffer_cache.cpp @@ -358,7 +358,7 @@ void BufferCacheRuntime::ReserveNullBuffer() { if (null_buffer) { return; } - null_buffer = device.GetLogical().CreateBuffer(VkBufferCreateInfo{ + VkBufferCreateInfo create_info{ .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, .pNext = nullptr, .flags = 0, @@ -367,9 +367,13 @@ void BufferCacheRuntime::ReserveNullBuffer() { .sharingMode = VK_SHARING_MODE_EXCLUSIVE, .queueFamilyIndexCount = 0, .pQueueFamilyIndices = nullptr, - }); + }; + if (device.IsExtTransformFeedbackSupported()) { + create_info.usage |= VK_BUFFER_USAGE_TRANSFORM_FEEDBACK_BUFFER_BIT_EXT; + } + null_buffer = device.GetLogical().CreateBuffer(create_info); if (device.HasDebuggingToolAttached()) { - null_buffer.SetObjectNameEXT("Null index buffer"); + null_buffer.SetObjectNameEXT("Null buffer"); } null_buffer_commit = memory_allocator.Commit(null_buffer, MemoryUsage::DeviceLocal); diff --git a/src/video_core/renderer_vulkan/vk_compute_pass.cpp b/src/video_core/renderer_vulkan/vk_compute_pass.cpp index 8e426ce2c..561cf5e11 100644 --- a/src/video_core/renderer_vulkan/vk_compute_pass.cpp +++ b/src/video_core/renderer_vulkan/vk_compute_pass.cpp @@ -258,10 +258,9 @@ std::pair<VkBuffer, VkDeviceSize> Uint8Pass::Assemble(u32 num_vertices, VkBuffer update_descriptor_queue.AddBuffer(src_buffer, src_offset, num_vertices); update_descriptor_queue.AddBuffer(staging.buffer, staging.offset, staging_size); const void* const descriptor_data{update_descriptor_queue.UpdateData()}; - const VkBuffer buffer{staging.buffer}; scheduler.RequestOutsideRenderPassOperationContext(); - scheduler.Record([this, buffer, descriptor_data, num_vertices](vk::CommandBuffer cmdbuf) { + scheduler.Record([this, descriptor_data, num_vertices](vk::CommandBuffer cmdbuf) { static constexpr u32 DISPATCH_SIZE = 1024; static constexpr VkMemoryBarrier WRITE_BARRIER{ .sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER, @@ -319,14 +318,14 @@ std::pair<VkBuffer, VkDeviceSize> QuadIndexedPass::Assemble( const void* const descriptor_data{update_descriptor_queue.UpdateData()}; scheduler.RequestOutsideRenderPassOperationContext(); - scheduler.Record([this, buffer = staging.buffer, descriptor_data, num_tri_vertices, base_vertex, + scheduler.Record([this, descriptor_data, num_tri_vertices, base_vertex, index_shift](vk::CommandBuffer cmdbuf) { static constexpr u32 DISPATCH_SIZE = 1024; static constexpr VkMemoryBarrier WRITE_BARRIER{ .sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER, .pNext = nullptr, .srcAccessMask = VK_ACCESS_SHADER_WRITE_BIT, - .dstAccessMask = VK_ACCESS_VERTEX_ATTRIBUTE_READ_BIT, + .dstAccessMask = VK_ACCESS_INDEX_READ_BIT, }; const std::array push_constants{base_vertex, index_shift}; const VkDescriptorSet set = descriptor_allocator.Commit(); diff --git a/src/video_core/renderer_vulkan/vk_staging_buffer_pool.cpp b/src/video_core/renderer_vulkan/vk_staging_buffer_pool.cpp index 555b12ed7..5d5329abf 100644 --- a/src/video_core/renderer_vulkan/vk_staging_buffer_pool.cpp +++ b/src/video_core/renderer_vulkan/vk_staging_buffer_pool.cpp @@ -61,11 +61,15 @@ std::optional<u32> FindMemoryTypeIndex(const VkPhysicalDeviceMemoryProperties& p return std::nullopt; } -u32 FindMemoryTypeIndex(const VkPhysicalDeviceMemoryProperties& props, u32 type_mask) { - // Try to find a DEVICE_LOCAL_BIT type, Nvidia and AMD have a dedicated heap for this - std::optional<u32> type = FindMemoryTypeIndex(props, type_mask, STREAM_FLAGS); - if (type) { - return *type; +u32 FindMemoryTypeIndex(const VkPhysicalDeviceMemoryProperties& props, u32 type_mask, + bool try_device_local) { + std::optional<u32> type; + if (try_device_local) { + // Try to find a DEVICE_LOCAL_BIT type, Nvidia and AMD have a dedicated heap for this + type = FindMemoryTypeIndex(props, type_mask, STREAM_FLAGS); + if (type) { + return *type; + } } // Otherwise try without the DEVICE_LOCAL_BIT type = FindMemoryTypeIndex(props, type_mask, HOST_FLAGS); @@ -115,12 +119,21 @@ StagingBufferPool::StagingBufferPool(const Device& device_, MemoryAllocator& mem .buffer = *stream_buffer, }; const auto memory_properties = device.GetPhysical().GetMemoryProperties(); - stream_memory = dev.AllocateMemory(VkMemoryAllocateInfo{ + VkMemoryAllocateInfo stream_memory_info{ .sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO, .pNext = make_dedicated ? &dedicated_info : nullptr, .allocationSize = requirements.size, - .memoryTypeIndex = FindMemoryTypeIndex(memory_properties, requirements.memoryTypeBits), - }); + .memoryTypeIndex = + FindMemoryTypeIndex(memory_properties, requirements.memoryTypeBits, true), + }; + stream_memory = dev.TryAllocateMemory(stream_memory_info); + if (!stream_memory) { + LOG_INFO(Render_Vulkan, "Dynamic memory allocation failed, trying with system memory"); + stream_memory_info.memoryTypeIndex = + FindMemoryTypeIndex(memory_properties, requirements.memoryTypeBits, false); + stream_memory = dev.AllocateMemory(stream_memory_info); + } + if (device.HasDebuggingToolAttached()) { stream_memory.SetObjectNameEXT("Stream Buffer Memory"); } diff --git a/src/video_core/renderer_vulkan/vk_stream_buffer.cpp b/src/video_core/renderer_vulkan/vk_stream_buffer.cpp deleted file mode 100644 index 7b4875d0e..000000000 --- a/src/video_core/renderer_vulkan/vk_stream_buffer.cpp +++ /dev/null @@ -1,168 +0,0 @@ -// Copyright 2019 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#include <algorithm> -#include <limits> -#include <optional> -#include <tuple> -#include <vector> - -#include "common/alignment.h" -#include "common/assert.h" -#include "common/literals.h" -#include "video_core/renderer_vulkan/vk_scheduler.h" -#include "video_core/renderer_vulkan/vk_stream_buffer.h" -#include "video_core/vulkan_common/vulkan_device.h" -#include "video_core/vulkan_common/vulkan_wrapper.h" - -namespace Vulkan { - -namespace { - -using namespace Common::Literals; - -constexpr VkBufferUsageFlags BUFFER_USAGE = - VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | VK_BUFFER_USAGE_INDEX_BUFFER_BIT | - VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT; - -constexpr u64 WATCHES_INITIAL_RESERVE = 0x4000; -constexpr u64 WATCHES_RESERVE_CHUNK = 0x1000; - -constexpr u64 PREFERRED_STREAM_BUFFER_SIZE = 256_MiB; - -/// Find a memory type with the passed requirements -std::optional<u32> FindMemoryType(const VkPhysicalDeviceMemoryProperties& properties, - VkMemoryPropertyFlags wanted, - u32 filter = std::numeric_limits<u32>::max()) { - for (u32 i = 0; i < properties.memoryTypeCount; ++i) { - const auto flags = properties.memoryTypes[i].propertyFlags; - if ((flags & wanted) == wanted && (filter & (1U << i)) != 0) { - return i; - } - } - return std::nullopt; -} - -/// Get the preferred host visible memory type. -u32 GetMemoryType(const VkPhysicalDeviceMemoryProperties& properties, - u32 filter = std::numeric_limits<u32>::max()) { - // Prefer device local host visible allocations. Both AMD and Nvidia now provide one. - // Otherwise search for a host visible allocation. - static constexpr auto HOST_MEMORY = - VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT; - static constexpr auto DYNAMIC_MEMORY = HOST_MEMORY | VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT; - - std::optional preferred_type = FindMemoryType(properties, DYNAMIC_MEMORY); - if (!preferred_type) { - preferred_type = FindMemoryType(properties, HOST_MEMORY); - ASSERT_MSG(preferred_type, "No host visible and coherent memory type found"); - } - return preferred_type.value_or(0); -} - -} // Anonymous namespace - -VKStreamBuffer::VKStreamBuffer(const Device& device_, VKScheduler& scheduler_) - : device{device_}, scheduler{scheduler_} { - CreateBuffers(); - ReserveWatches(current_watches, WATCHES_INITIAL_RESERVE); - ReserveWatches(previous_watches, WATCHES_INITIAL_RESERVE); -} - -VKStreamBuffer::~VKStreamBuffer() = default; - -std::pair<u8*, u64> VKStreamBuffer::Map(u64 size, u64 alignment) { - ASSERT(size <= stream_buffer_size); - mapped_size = size; - - if (alignment > 0) { - offset = Common::AlignUp(offset, alignment); - } - - WaitPendingOperations(offset); - - if (offset + size > stream_buffer_size) { - // The buffer would overflow, save the amount of used watches and reset the state. - invalidation_mark = current_watch_cursor; - current_watch_cursor = 0; - offset = 0; - - // Swap watches and reset waiting cursors. - std::swap(previous_watches, current_watches); - wait_cursor = 0; - wait_bound = 0; - - // Ensure that we don't wait for uncommitted fences. - scheduler.Flush(); - } - - return std::make_pair(memory.Map(offset, size), offset); -} - -void VKStreamBuffer::Unmap(u64 size) { - ASSERT_MSG(size <= mapped_size, "Reserved size is too small"); - - memory.Unmap(); - - offset += size; - - if (current_watch_cursor + 1 >= current_watches.size()) { - // Ensure that there are enough watches. - ReserveWatches(current_watches, WATCHES_RESERVE_CHUNK); - } - auto& watch = current_watches[current_watch_cursor++]; - watch.upper_bound = offset; - watch.tick = scheduler.CurrentTick(); -} - -void VKStreamBuffer::CreateBuffers() { - const auto memory_properties = device.GetPhysical().GetMemoryProperties(); - const u32 preferred_type = GetMemoryType(memory_properties); - const u32 preferred_heap = memory_properties.memoryTypes[preferred_type].heapIndex; - - // Substract from the preferred heap size some bytes to avoid getting out of memory. - const VkDeviceSize heap_size = memory_properties.memoryHeaps[preferred_heap].size; - // As per DXVK's example, using `heap_size / 2` - const VkDeviceSize allocable_size = heap_size / 2; - buffer = device.GetLogical().CreateBuffer({ - .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, - .pNext = nullptr, - .flags = 0, - .size = std::min(PREFERRED_STREAM_BUFFER_SIZE, allocable_size), - .usage = BUFFER_USAGE, - .sharingMode = VK_SHARING_MODE_EXCLUSIVE, - .queueFamilyIndexCount = 0, - .pQueueFamilyIndices = nullptr, - }); - - const auto requirements = device.GetLogical().GetBufferMemoryRequirements(*buffer); - const u32 required_flags = requirements.memoryTypeBits; - stream_buffer_size = static_cast<u64>(requirements.size); - - memory = device.GetLogical().AllocateMemory({ - .sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO, - .pNext = nullptr, - .allocationSize = requirements.size, - .memoryTypeIndex = GetMemoryType(memory_properties, required_flags), - }); - buffer.BindMemory(*memory, 0); -} - -void VKStreamBuffer::ReserveWatches(std::vector<Watch>& watches, std::size_t grow_size) { - watches.resize(watches.size() + grow_size); -} - -void VKStreamBuffer::WaitPendingOperations(u64 requested_upper_bound) { - if (!invalidation_mark) { - return; - } - while (requested_upper_bound < wait_bound && wait_cursor < *invalidation_mark) { - auto& watch = previous_watches[wait_cursor]; - wait_bound = watch.upper_bound; - scheduler.Wait(watch.tick); - ++wait_cursor; - } -} - -} // namespace Vulkan diff --git a/src/video_core/renderer_vulkan/vk_stream_buffer.h b/src/video_core/renderer_vulkan/vk_stream_buffer.h deleted file mode 100644 index 2e9c8cb46..000000000 --- a/src/video_core/renderer_vulkan/vk_stream_buffer.h +++ /dev/null @@ -1,76 +0,0 @@ -// Copyright 2019 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#pragma once - -#include <optional> -#include <utility> -#include <vector> - -#include "common/common_types.h" -#include "video_core/vulkan_common/vulkan_wrapper.h" - -namespace Vulkan { - -class Device; -class VKFenceWatch; -class VKScheduler; - -class VKStreamBuffer final { -public: - explicit VKStreamBuffer(const Device& device, VKScheduler& scheduler); - ~VKStreamBuffer(); - - /** - * Reserves a region of memory from the stream buffer. - * @param size Size to reserve. - * @returns A pair of a raw memory pointer (with offset added), and the buffer offset - */ - std::pair<u8*, u64> Map(u64 size, u64 alignment); - - /// Ensures that "size" bytes of memory are available to the GPU, potentially recording a copy. - void Unmap(u64 size); - - VkBuffer Handle() const noexcept { - return *buffer; - } - - u64 Address() const noexcept { - return 0; - } - -private: - struct Watch { - u64 tick{}; - u64 upper_bound{}; - }; - - /// Creates Vulkan buffer handles committing the required the required memory. - void CreateBuffers(); - - /// Increases the amount of watches available. - void ReserveWatches(std::vector<Watch>& watches, std::size_t grow_size); - - void WaitPendingOperations(u64 requested_upper_bound); - - const Device& device; ///< Vulkan device manager. - VKScheduler& scheduler; ///< Command scheduler. - - vk::Buffer buffer; ///< Mapped buffer. - vk::DeviceMemory memory; ///< Memory allocation. - u64 stream_buffer_size{}; ///< Stream buffer size. - - u64 offset{}; ///< Buffer iterator. - u64 mapped_size{}; ///< Size reserved for the current copy. - - std::vector<Watch> current_watches; ///< Watches recorded in the current iteration. - std::size_t current_watch_cursor{}; ///< Count of watches, reset on invalidation. - std::optional<std::size_t> invalidation_mark; ///< Number of watches used in the previous cycle. - - std::vector<Watch> previous_watches; ///< Watches used in the previous iteration. - std::size_t wait_cursor{}; ///< Last watch being waited for completion. - u64 wait_bound{}; ///< Highest offset being watched for completion. -}; - -} // namespace Vulkan diff --git a/src/video_core/texture_cache/render_targets.h b/src/video_core/texture_cache/render_targets.h index 9b9544b07..0cb227d69 100644 --- a/src/video_core/texture_cache/render_targets.h +++ b/src/video_core/texture_cache/render_targets.h @@ -24,10 +24,10 @@ struct RenderTargets { return std::ranges::any_of(color_buffer_ids, contains) || contains(depth_buffer_id); } - std::array<ImageViewId, NUM_RT> color_buffer_ids; - ImageViewId depth_buffer_id; + std::array<ImageViewId, NUM_RT> color_buffer_ids{}; + ImageViewId depth_buffer_id{}; std::array<u8, NUM_RT> draw_buffers{}; - Extent2D size; + Extent2D size{}; }; } // namespace VideoCommon diff --git a/src/video_core/vulkan_common/vulkan_wrapper.cpp b/src/video_core/vulkan_common/vulkan_wrapper.cpp index bbf0fccae..70898004a 100644 --- a/src/video_core/vulkan_common/vulkan_wrapper.cpp +++ b/src/video_core/vulkan_common/vulkan_wrapper.cpp @@ -202,7 +202,7 @@ void SetObjectName(const DeviceDispatch* dld, VkDevice device, T handle, VkObjec const VkDebugUtilsObjectNameInfoEXT name_info{ .sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_OBJECT_NAME_INFO_EXT, .pNext = nullptr, - .objectType = VK_OBJECT_TYPE_IMAGE, + .objectType = type, .objectHandle = reinterpret_cast<u64>(handle), .pObjectName = name, }; diff --git a/src/yuzu/applets/qt_web_browser.cpp b/src/yuzu/applets/qt_web_browser.cpp index b112dd7b0..652d99570 100644 --- a/src/yuzu/applets/qt_web_browser.cpp +++ b/src/yuzu/applets/qt_web_browser.cpp @@ -107,6 +107,7 @@ void QtNXWebEngineView::LoadLocalWebPage(const std::string& main_url, is_local = true; LoadExtractedFonts(); + FocusFirstLinkElement(); SetUserAgent(UserAgent::WebApplet); SetFinished(false); SetExitReason(Service::AM::Applets::WebExitReason::EndButtonPressed); @@ -121,6 +122,7 @@ void QtNXWebEngineView::LoadExternalWebPage(const std::string& main_url, const std::string& additional_args) { is_local = false; + FocusFirstLinkElement(); SetUserAgent(UserAgent::WebApplet); SetFinished(false); SetExitReason(Service::AM::Applets::WebExitReason::EndButtonPressed); @@ -208,7 +210,7 @@ void QtNXWebEngineView::HandleWindowFooterButtonPressedOnce() { if (input_interpreter->IsButtonPressedOnce(button)) { page()->runJavaScript( QStringLiteral("yuzu_key_callbacks[%1] == null;").arg(static_cast<u8>(button)), - [&](const QVariant& variant) { + [this, button](const QVariant& variant) { if (variant.toBool()) { switch (button) { case HIDButton::A: @@ -364,6 +366,17 @@ void QtNXWebEngineView::LoadExtractedFonts() { Qt::QueuedConnection); } +void QtNXWebEngineView::FocusFirstLinkElement() { + QWebEngineScript focus_link_element; + + focus_link_element.setName(QStringLiteral("focus_link_element.js")); + focus_link_element.setSourceCode(QString::fromStdString(FOCUS_LINK_ELEMENT_SCRIPT)); + focus_link_element.setWorldId(QWebEngineScript::MainWorld); + focus_link_element.setInjectionPoint(QWebEngineScript::Deferred); + focus_link_element.setRunsOnSubFrames(true); + default_profile->scripts()->insert(focus_link_element); +} + #endif QtWebBrowser::QtWebBrowser(GMainWindow& main_window) { diff --git a/src/yuzu/applets/qt_web_browser.h b/src/yuzu/applets/qt_web_browser.h index 7ad07409f..7e9f703fc 100644 --- a/src/yuzu/applets/qt_web_browser.h +++ b/src/yuzu/applets/qt_web_browser.h @@ -161,6 +161,9 @@ private: /// Loads the extracted fonts using JavaScript. void LoadExtractedFonts(); + /// Brings focus to the first available link element. + void FocusFirstLinkElement(); + InputCommon::InputSubsystem* input_subsystem; std::unique_ptr<UrlRequestInterceptor> url_interceptor; diff --git a/src/yuzu/applets/qt_web_browser_scripts.h b/src/yuzu/applets/qt_web_browser_scripts.h index 992837a85..c4ba8d40f 100644 --- a/src/yuzu/applets/qt_web_browser_scripts.h +++ b/src/yuzu/applets/qt_web_browser_scripts.h @@ -73,6 +73,12 @@ constexpr char LOAD_NX_FONT[] = R"( })(); )"; +constexpr char FOCUS_LINK_ELEMENT_SCRIPT[] = R"( +if (document.getElementsByTagName("a").length > 0) { + document.getElementsByTagName("a")[0].focus(); +} +)"; + constexpr char GAMEPAD_SCRIPT[] = R"( window.addEventListener("gamepadconnected", function(e) { console.log("Gamepad connected at index %d: %s. %d buttons, %d axes.", diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp index 85c37b842..ecd5dfac1 100644 --- a/src/yuzu/configuration/config.cpp +++ b/src/yuzu/configuration/config.cpp @@ -806,8 +806,8 @@ void Config::ReadRendererValues() { ReadGlobalSetting(Settings::values.fullscreen_mode); ReadGlobalSetting(Settings::values.aspect_ratio); ReadGlobalSetting(Settings::values.max_anisotropy); - ReadGlobalSetting(Settings::values.use_frame_limit); - ReadGlobalSetting(Settings::values.frame_limit); + ReadGlobalSetting(Settings::values.use_speed_limit); + ReadGlobalSetting(Settings::values.speed_limit); ReadGlobalSetting(Settings::values.use_disk_shader_cache); ReadGlobalSetting(Settings::values.gpu_accuracy); ReadGlobalSetting(Settings::values.use_asynchronous_gpu_emulation); @@ -823,6 +823,7 @@ void Config::ReadRendererValues() { ReadGlobalSetting(Settings::values.bg_blue); if (global) { + ReadBasicSetting(Settings::values.fps_cap); ReadBasicSetting(Settings::values.renderer_debug); ReadBasicSetting(Settings::values.enable_nsight_aftermath); ReadBasicSetting(Settings::values.disable_shader_loop_safety_checks); @@ -1337,8 +1338,8 @@ void Config::SaveRendererValues() { Settings::values.fullscreen_mode.UsingGlobal()); WriteGlobalSetting(Settings::values.aspect_ratio); WriteGlobalSetting(Settings::values.max_anisotropy); - WriteGlobalSetting(Settings::values.use_frame_limit); - WriteGlobalSetting(Settings::values.frame_limit); + WriteGlobalSetting(Settings::values.use_speed_limit); + WriteGlobalSetting(Settings::values.speed_limit); WriteGlobalSetting(Settings::values.use_disk_shader_cache); WriteSetting(QString::fromStdString(Settings::values.gpu_accuracy.GetLabel()), static_cast<u32>(Settings::values.gpu_accuracy.GetValue(global)), @@ -1360,6 +1361,7 @@ void Config::SaveRendererValues() { WriteGlobalSetting(Settings::values.bg_blue); if (global) { + WriteBasicSetting(Settings::values.fps_cap); WriteBasicSetting(Settings::values.renderer_debug); WriteBasicSetting(Settings::values.enable_nsight_aftermath); WriteBasicSetting(Settings::values.disable_shader_loop_safety_checks); diff --git a/src/yuzu/configuration/configure_general.cpp b/src/yuzu/configuration/configure_general.cpp index 18f25def6..1f647a0d1 100644 --- a/src/yuzu/configuration/configure_general.cpp +++ b/src/yuzu/configuration/configure_general.cpp @@ -24,8 +24,8 @@ ConfigureGeneral::ConfigureGeneral(QWidget* parent) SetConfiguration(); if (Settings::IsConfiguringGlobal()) { - connect(ui->toggle_frame_limit, &QCheckBox::clicked, ui->frame_limit, - [this]() { ui->frame_limit->setEnabled(ui->toggle_frame_limit->isChecked()); }); + connect(ui->toggle_speed_limit, &QCheckBox::clicked, ui->speed_limit, + [this]() { ui->speed_limit->setEnabled(ui->toggle_speed_limit->isChecked()); }); } connect(ui->button_reset_defaults, &QPushButton::clicked, this, @@ -45,16 +45,18 @@ void ConfigureGeneral::SetConfiguration() { ui->toggle_background_pause->setChecked(UISettings::values.pause_when_in_background.GetValue()); ui->toggle_hide_mouse->setChecked(UISettings::values.hide_mouse.GetValue()); - ui->toggle_frame_limit->setChecked(Settings::values.use_frame_limit.GetValue()); - ui->frame_limit->setValue(Settings::values.frame_limit.GetValue()); + ui->toggle_speed_limit->setChecked(Settings::values.use_speed_limit.GetValue()); + ui->speed_limit->setValue(Settings::values.speed_limit.GetValue()); + + ui->fps_cap->setValue(Settings::values.fps_cap.GetValue()); ui->button_reset_defaults->setEnabled(runtime_lock); if (Settings::IsConfiguringGlobal()) { - ui->frame_limit->setEnabled(Settings::values.use_frame_limit.GetValue()); + ui->speed_limit->setEnabled(Settings::values.use_speed_limit.GetValue()); } else { - ui->frame_limit->setEnabled(Settings::values.use_frame_limit.GetValue() && - use_frame_limit != ConfigurationShared::CheckState::Global); + ui->speed_limit->setEnabled(Settings::values.use_speed_limit.GetValue() && + use_speed_limit != ConfigurationShared::CheckState::Global); } } @@ -87,20 +89,22 @@ void ConfigureGeneral::ApplyConfiguration() { UISettings::values.pause_when_in_background = ui->toggle_background_pause->isChecked(); UISettings::values.hide_mouse = ui->toggle_hide_mouse->isChecked(); + Settings::values.fps_cap.SetValue(ui->fps_cap->value()); + // Guard if during game and set to game-specific value - if (Settings::values.use_frame_limit.UsingGlobal()) { - Settings::values.use_frame_limit.SetValue(ui->toggle_frame_limit->checkState() == + if (Settings::values.use_speed_limit.UsingGlobal()) { + Settings::values.use_speed_limit.SetValue(ui->toggle_speed_limit->checkState() == Qt::Checked); - Settings::values.frame_limit.SetValue(ui->frame_limit->value()); + Settings::values.speed_limit.SetValue(ui->speed_limit->value()); } } else { - bool global_frame_limit = use_frame_limit == ConfigurationShared::CheckState::Global; - Settings::values.use_frame_limit.SetGlobal(global_frame_limit); - Settings::values.frame_limit.SetGlobal(global_frame_limit); - if (!global_frame_limit) { - Settings::values.use_frame_limit.SetValue(ui->toggle_frame_limit->checkState() == + bool global_speed_limit = use_speed_limit == ConfigurationShared::CheckState::Global; + Settings::values.use_speed_limit.SetGlobal(global_speed_limit); + Settings::values.speed_limit.SetGlobal(global_speed_limit); + if (!global_speed_limit) { + Settings::values.use_speed_limit.SetValue(ui->toggle_speed_limit->checkState() == Qt::Checked); - Settings::values.frame_limit.SetValue(ui->frame_limit->value()); + Settings::values.speed_limit.SetValue(ui->speed_limit->value()); } } } @@ -122,8 +126,8 @@ void ConfigureGeneral::SetupPerGameUI() { // Disables each setting if: // - A game is running (thus settings in use), and // - A non-global setting is applied. - ui->toggle_frame_limit->setEnabled(Settings::values.use_frame_limit.UsingGlobal()); - ui->frame_limit->setEnabled(Settings::values.frame_limit.UsingGlobal()); + ui->toggle_speed_limit->setEnabled(Settings::values.use_speed_limit.UsingGlobal()); + ui->speed_limit->setEnabled(Settings::values.speed_limit.UsingGlobal()); return; } @@ -135,13 +139,13 @@ void ConfigureGeneral::SetupPerGameUI() { ui->button_reset_defaults->setVisible(false); - ConfigurationShared::SetColoredTristate(ui->toggle_frame_limit, - Settings::values.use_frame_limit, use_frame_limit); + ConfigurationShared::SetColoredTristate(ui->toggle_speed_limit, + Settings::values.use_speed_limit, use_speed_limit); ConfigurationShared::SetColoredTristate(ui->use_multi_core, Settings::values.use_multi_core, use_multi_core); - connect(ui->toggle_frame_limit, &QCheckBox::clicked, ui->frame_limit, [this]() { - ui->frame_limit->setEnabled(ui->toggle_frame_limit->isChecked() && - (use_frame_limit != ConfigurationShared::CheckState::Global)); + connect(ui->toggle_speed_limit, &QCheckBox::clicked, ui->speed_limit, [this]() { + ui->speed_limit->setEnabled(ui->toggle_speed_limit->isChecked() && + (use_speed_limit != ConfigurationShared::CheckState::Global)); }); } diff --git a/src/yuzu/configuration/configure_general.h b/src/yuzu/configuration/configure_general.h index a0fd52492..c9df37d73 100644 --- a/src/yuzu/configuration/configure_general.h +++ b/src/yuzu/configuration/configure_general.h @@ -43,6 +43,6 @@ private: std::unique_ptr<Ui::ConfigureGeneral> ui; - ConfigurationShared::CheckState use_frame_limit; + ConfigurationShared::CheckState use_speed_limit; ConfigurationShared::CheckState use_multi_core; }; diff --git a/src/yuzu/configuration/configure_general.ui b/src/yuzu/configuration/configure_general.ui index bc7041090..8ce97edec 100644 --- a/src/yuzu/configuration/configure_general.ui +++ b/src/yuzu/configuration/configure_general.ui @@ -27,14 +27,14 @@ <item> <layout class="QHBoxLayout" name="horizontalLayout_2"> <item> - <widget class="QCheckBox" name="toggle_frame_limit"> + <widget class="QCheckBox" name="toggle_speed_limit"> <property name="text"> <string>Limit Speed Percent</string> </property> </widget> </item> <item> - <widget class="QSpinBox" name="frame_limit"> + <widget class="QSpinBox" name="speed_limit"> <property name="suffix"> <string>%</string> </property> @@ -52,6 +52,36 @@ </layout> </item> <item> + <layout class="QHBoxLayout" name="horizontalLayout_2"> + <item> + <widget class="QLabel" name="fps_cap_label"> + <property name="text"> + <string>Framerate Cap</string> + </property> + <property name="toolTip"> + <string>Requires the use of the FPS Limiter Toggle hotkey to take effect.</string> + </property> + </widget> + </item> + <item> + <widget class="QSpinBox" name="fps_cap"> + <property name="suffix"> + <string>x</string> + </property> + <property name="minimum"> + <number>1</number> + </property> + <property name="maximum"> + <number>1000</number> + </property> + <property name="value"> + <number>500</number> + </property> + </widget> + </item> + </layout> + </item> + <item> <widget class="QCheckBox" name="use_multi_core"> <property name="text"> <string>Multicore CPU Emulation</string> diff --git a/src/yuzu/configuration/configure_graphics.cpp b/src/yuzu/configuration/configure_graphics.cpp index 4a5b17740..37e896258 100644 --- a/src/yuzu/configuration/configure_graphics.cpp +++ b/src/yuzu/configuration/configure_graphics.cpp @@ -31,7 +31,7 @@ ConfigureGraphics::ConfigureGraphics(QWidget* parent) } ui->backend->addItem(QStringLiteral("GLSL")); - ui->backend->addItem(tr("GLASM (NVIDIA Only)")); + ui->backend->addItem(tr("GLASM (Assembly Shaders, NVIDIA Only)")); ui->backend->addItem(QStringLiteral("SPIR-V (Experimental, Mesa Only)")); SetupPerGameUI(); diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index f848b2982..9544f0fb0 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp @@ -972,23 +972,23 @@ void GMainWindow::InitializeHotkeys() { }); connect(hotkey_registry.GetHotkey(main_window, QStringLiteral("Toggle Speed Limit"), this), &QShortcut::activated, this, [&] { - Settings::values.use_frame_limit.SetValue( - !Settings::values.use_frame_limit.GetValue()); + Settings::values.use_speed_limit.SetValue( + !Settings::values.use_speed_limit.GetValue()); UpdateStatusBar(); }); constexpr u16 SPEED_LIMIT_STEP = 5; connect(hotkey_registry.GetHotkey(main_window, QStringLiteral("Increase Speed Limit"), this), &QShortcut::activated, this, [&] { - if (Settings::values.frame_limit.GetValue() < 9999 - SPEED_LIMIT_STEP) { - Settings::values.frame_limit.SetValue(SPEED_LIMIT_STEP + - Settings::values.frame_limit.GetValue()); + if (Settings::values.speed_limit.GetValue() < 9999 - SPEED_LIMIT_STEP) { + Settings::values.speed_limit.SetValue(SPEED_LIMIT_STEP + + Settings::values.speed_limit.GetValue()); UpdateStatusBar(); } }); connect(hotkey_registry.GetHotkey(main_window, QStringLiteral("Decrease Speed Limit"), this), &QShortcut::activated, this, [&] { - if (Settings::values.frame_limit.GetValue() > SPEED_LIMIT_STEP) { - Settings::values.frame_limit.SetValue(Settings::values.frame_limit.GetValue() - + if (Settings::values.speed_limit.GetValue() > SPEED_LIMIT_STEP) { + Settings::values.speed_limit.SetValue(Settings::values.speed_limit.GetValue() - SPEED_LIMIT_STEP); UpdateStatusBar(); } @@ -2910,16 +2910,16 @@ void GMainWindow::UpdateStatusBar() { shader_building_label->setVisible(false); } - if (Settings::values.use_frame_limit.GetValue()) { + if (Settings::values.use_speed_limit.GetValue()) { emu_speed_label->setText(tr("Speed: %1% / %2%") .arg(results.emulation_speed * 100.0, 0, 'f', 0) - .arg(Settings::values.frame_limit.GetValue())); + .arg(Settings::values.speed_limit.GetValue())); } else { emu_speed_label->setText(tr("Speed: %1%").arg(results.emulation_speed * 100.0, 0, 'f', 0)); } if (Settings::values.disable_fps_limit) { game_fps_label->setText( - tr("Game: %1 FPS (Limit off)").arg(results.average_game_fps, 0, 'f', 0)); + tr("Game: %1 FPS (Unlocked)").arg(results.average_game_fps, 0, 'f', 0)); } else { game_fps_label->setText(tr("Game: %1 FPS").arg(results.average_game_fps, 0, 'f', 0)); } diff --git a/src/yuzu_cmd/config.cpp b/src/yuzu_cmd/config.cpp index 640d7d111..5af1ee6a8 100644 --- a/src/yuzu_cmd/config.cpp +++ b/src/yuzu_cmd/config.cpp @@ -451,12 +451,13 @@ void Config::ReadValues() { ReadSetting("Renderer", Settings::values.fullscreen_mode); ReadSetting("Renderer", Settings::values.aspect_ratio); ReadSetting("Renderer", Settings::values.max_anisotropy); - ReadSetting("Renderer", Settings::values.use_frame_limit); - ReadSetting("Renderer", Settings::values.frame_limit); + ReadSetting("Renderer", Settings::values.use_speed_limit); + ReadSetting("Renderer", Settings::values.speed_limit); 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.fps_cap); ReadSetting("Renderer", Settings::values.disable_fps_limit); ReadSetting("Renderer", Settings::values.shader_backend); ReadSetting("Renderer", Settings::values.use_asynchronous_shaders); diff --git a/src/yuzu_cmd/default_ini.h b/src/yuzu_cmd/default_ini.h index b7115b06a..e646e2d2f 100644 --- a/src/yuzu_cmd/default_ini.h +++ b/src/yuzu_cmd/default_ini.h @@ -265,13 +265,13 @@ use_nvdec_emulation = # 0: Off, 1 (default): On accelerate_astc = -# Turns on the frame limiter, which will limit frames output to the target game speed +# Turns on the speed limiter, which will limit the emulation speed to the desired speed limit value # 0: Off, 1: On (default) -use_frame_limit = +use_speed_limit = # Limits the speed of the game to run no faster than this value as a percentage of target speed # 1 - 9999: Speed limit as a percentage of target game speed. 100 (default) -frame_limit = +speed_limit = # Whether to use disk based shader cache # 0: Off, 1 (default): On @@ -299,6 +299,10 @@ bg_red = bg_blue = bg_green = +# Caps the unlocked framerate to a multiple of the title's target FPS. +# 1 - 1000: Target FPS multiple cap. 1000 (default) +fps_cap = + [Audio] # Which audio output engine to use. # auto (default): Auto-select |