diff --git a/README.md b/README.md index ba68c0d0f..3a2d126d4 100755 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ yuzu emulator early access ============= -This is the source code for early-access 3648. +This is the source code for early-access 3649. ## Legal Notice diff --git a/src/audio_core/audio_core.cpp b/src/audio_core/audio_core.cpp index c21258b7c..756a480b3 100755 --- a/src/audio_core/audio_core.cpp +++ b/src/audio_core/audio_core.cpp @@ -47,12 +47,4 @@ AudioRenderer::ADSP::ADSP& AudioCore::GetADSP() { return *adsp; } -void AudioCore::SetNVDECActive(bool active) { - nvdec_active = active; -} - -bool AudioCore::IsNVDECActive() const { - return nvdec_active; -} - } // namespace AudioCore diff --git a/src/audio_core/audio_core.h b/src/audio_core/audio_core.h index 380c30873..ed2c06d61 100755 --- a/src/audio_core/audio_core.h +++ b/src/audio_core/audio_core.h @@ -57,18 +57,6 @@ public: */ AudioRenderer::ADSP::ADSP& GetADSP(); - /** - * Toggle NVDEC state, used to avoid stall in playback. - * - * @param active - Set true if nvdec is active, otherwise false. - */ - void SetNVDECActive(bool active); - - /** - * Get NVDEC state. - */ - bool IsNVDECActive() const; - private: /** * Create the sinks on startup. @@ -83,8 +71,6 @@ private: std::unique_ptr input_sink; /// The ADSP in the sysmodule std::unique_ptr adsp; - /// Is NVDec currently active? - bool nvdec_active{false}; }; } // namespace AudioCore diff --git a/src/common/settings.cpp b/src/common/settings.cpp index bb3e237da..71b652641 100755 --- a/src/common/settings.cpp +++ b/src/common/settings.cpp @@ -235,6 +235,7 @@ void RestoreGlobalState(bool is_powered_on) { values.bg_green.SetGlobal(true); values.bg_blue.SetGlobal(true); values.enable_compute_pipelines.SetGlobal(true); + values.use_video_framerate.SetGlobal(true); // System values.language_index.SetGlobal(true); diff --git a/src/common/settings.h b/src/common/settings.h index 62db95bd1..d2859dc81 100755 --- a/src/common/settings.h +++ b/src/common/settings.h @@ -482,6 +482,7 @@ struct Values { SwitchableSetting astc_recompression{ AstcRecompression::Uncompressed, AstcRecompression::Uncompressed, AstcRecompression::Bc3, "astc_recompression"}; + SwitchableSetting use_video_framerate{false, "use_video_framerate"}; SwitchableSetting bg_red{0, "bg_red"}; SwitchableSetting bg_green{0, "bg_green"}; diff --git a/src/core/core.cpp b/src/core/core.cpp index 3e4582a9a..f9b4b0b13 100755 --- a/src/core/core.cpp +++ b/src/core/core.cpp @@ -216,6 +216,14 @@ struct System::Impl { } } + void SetNVDECActive(bool is_nvdec_active) { + nvdec_active = is_nvdec_active; + } + + bool GetNVDECActive() { + return nvdec_active; + } + void InitializeDebugger(System& system, u16 port) { debugger = std::make_unique(system, port); } @@ -485,6 +493,8 @@ struct System::Impl { std::atomic_bool is_powered_on{}; bool exit_lock = false; + bool nvdec_active{}; + Reporter reporter; std::unique_ptr cheat_engine; std::unique_ptr memory_freezer; @@ -594,6 +604,14 @@ void System::UnstallApplication() { impl->UnstallApplication(); } +void System::SetNVDECActive(bool is_nvdec_active) { + impl->SetNVDECActive(is_nvdec_active); +} + +bool System::GetNVDECActive() { + return impl->GetNVDECActive(); +} + void System::InitializeDebugger() { impl->InitializeDebugger(*this, Settings::values.gdbstub_port.GetValue()); } diff --git a/src/core/core.h b/src/core/core.h index d4b1166f5..c16e92e7a 100755 --- a/src/core/core.h +++ b/src/core/core.h @@ -189,6 +189,9 @@ public: std::unique_lock StallApplication(); void UnstallApplication(); + void SetNVDECActive(bool is_nvdec_active); + [[nodiscard]] bool GetNVDECActive(); + /** * Initialize the debugger. */ diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp b/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp index a2de66cdc..e882dfd19 100755 --- a/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp @@ -69,7 +69,7 @@ NvResult nvhost_nvdec::Ioctl3(DeviceFD fd, Ioctl command, std::span in void nvhost_nvdec::OnOpen(DeviceFD fd) { LOG_INFO(Service_NVDRV, "NVDEC video stream started"); - system.AudioCore().SetNVDECActive(true); + system.SetNVDECActive(true); } void nvhost_nvdec::OnClose(DeviceFD fd) { @@ -79,7 +79,7 @@ void nvhost_nvdec::OnClose(DeviceFD fd) { if (iter != host1x_file.fd_to_id.end()) { system.GPU().ClearCdmaInstance(iter->second); } - system.AudioCore().SetNVDECActive(false); + system.SetNVDECActive(false); } } // namespace Service::Nvidia::Devices diff --git a/src/core/hle/service/nvnflinger/nvnflinger.cpp b/src/core/hle/service/nvnflinger/nvnflinger.cpp index aa3356611..b41c6240c 100755 --- a/src/core/hle/service/nvnflinger/nvnflinger.cpp +++ b/src/core/hle/service/nvnflinger/nvnflinger.cpp @@ -325,6 +325,10 @@ s64 Nvnflinger::GetNextTicks() const { speed_scale = 0.01f; } } + if (system.GetNVDECActive() && settings.use_video_framerate.GetValue()) { + // Run at intended presentation rate during video playback. + speed_scale = 1.f; + } // As an extension, treat nonpositive swap interval as framerate multiplier. const f32 effective_fps = swap_interval <= 0 ? 120.f * static_cast(1 - swap_interval) diff --git a/src/video_core/buffer_cache/buffer_cache.h b/src/video_core/buffer_cache/buffer_cache.h index ea5318dc0..9fb6494eb 100755 --- a/src/video_core/buffer_cache/buffer_cache.h +++ b/src/video_core/buffer_cache/buffer_cache.h @@ -715,20 +715,38 @@ void BufferCache

::BindHostIndexBuffer() { template void BufferCache

::BindHostVertexBuffers() { + HostBindings host_bindings; + bool any_valid{false}; auto& flags = maxwell3d->dirty.flags; for (u32 index = 0; index < NUM_VERTEX_BUFFERS; ++index) { - const Binding& binding = channel_state->vertex_buffers[index]; - Buffer& buffer = slot_buffers[binding.buffer_id]; - TouchBuffer(buffer, binding.buffer_id); - SynchronizeBuffer(buffer, binding.cpu_addr, binding.size); if (!flags[Dirty::VertexBuffer0 + index]) { continue; } - flags[Dirty::VertexBuffer0 + index] = false; + host_bindings.min_index = std::min(host_bindings.min_index, index); + host_bindings.max_index = std::max(host_bindings.max_index, index); + any_valid = true; + } - const u32 stride = maxwell3d->regs.vertex_streams[index].stride; - const u32 offset = buffer.Offset(binding.cpu_addr); - runtime.BindVertexBuffer(index, buffer, offset, binding.size, stride); + if (any_valid) { + host_bindings.max_index++; + for (u32 index = host_bindings.min_index; index < host_bindings.max_index; index++) { + flags[Dirty::VertexBuffer0 + index] = false; + + const Binding& binding = channel_state->vertex_buffers[index]; + Buffer& buffer = slot_buffers[binding.buffer_id]; + + TouchBuffer(buffer, binding.buffer_id); + SynchronizeBuffer(buffer, binding.cpu_addr, binding.size); + + const u32 stride = maxwell3d->regs.vertex_streams[index].stride; + const u32 offset = buffer.Offset(binding.cpu_addr); + + host_bindings.buffers.push_back(reinterpret_cast(&buffer)); + host_bindings.offsets.push_back(offset); + host_bindings.sizes.push_back(binding.size); + host_bindings.strides.push_back(stride); + } + runtime.BindVertexBuffers(host_bindings); } } @@ -882,15 +900,25 @@ void BufferCache

::BindHostTransformFeedbackBuffers() { if (maxwell3d->regs.transform_feedback_enabled == 0) { return; } + HostBindings host_bindings; for (u32 index = 0; index < NUM_TRANSFORM_FEEDBACK_BUFFERS; ++index) { const Binding& binding = channel_state->transform_feedback_buffers[index]; + if (maxwell3d->regs.transform_feedback.controls[index].varying_count == 0 && + maxwell3d->regs.transform_feedback.controls[index].stride == 0) { + break; + } Buffer& buffer = slot_buffers[binding.buffer_id]; TouchBuffer(buffer, binding.buffer_id); const u32 size = binding.size; SynchronizeBuffer(buffer, binding.cpu_addr, size); const u32 offset = buffer.Offset(binding.cpu_addr); - runtime.BindTransformFeedbackBuffer(index, buffer, offset, size); + host_bindings.buffers.push_back(reinterpret_cast(&buffer)); + host_bindings.offsets.push_back(offset); + host_bindings.sizes.push_back(binding.size); + } + if (host_bindings.buffers.size() > 0) { + runtime.BindTransformFeedbackBuffers(host_bindings); } } @@ -1616,6 +1644,8 @@ void BufferCache

::DownloadBufferMemory(Buffer& buffer, VAddr cpu_addr, u64 si template void BufferCache

::DeleteBuffer(BufferId buffer_id, bool do_not_mark) { + bool dirty_index{false}; + boost::container::small_vector dirty_vertex_buffers; const auto scalar_replace = [buffer_id](Binding& binding) { if (binding.buffer_id == buffer_id) { binding.buffer_id = BufferId{}; @@ -1624,8 +1654,19 @@ void BufferCache

::DeleteBuffer(BufferId buffer_id, bool do_not_mark) { const auto replace = [scalar_replace](std::span bindings) { std::ranges::for_each(bindings, scalar_replace); }; - scalar_replace(channel_state->index_buffer); - replace(channel_state->vertex_buffers); + + if (channel_state->index_buffer.buffer_id == buffer_id) { + channel_state->index_buffer.buffer_id = BufferId{}; + dirty_index = true; + } + + for (u32 index = 0; index < channel_state->vertex_buffers.size(); index++) { + auto& binding = channel_state->vertex_buffers[index]; + if (binding.buffer_id == buffer_id) { + binding.buffer_id = BufferId{}; + dirty_vertex_buffers.push_back(index); + } + } std::ranges::for_each(channel_state->uniform_buffers, replace); std::ranges::for_each(channel_state->storage_buffers, replace); replace(channel_state->transform_feedback_buffers); @@ -1642,20 +1683,21 @@ void BufferCache

::DeleteBuffer(BufferId buffer_id, bool do_not_mark) { delayed_destruction_ring.Push(std::move(slot_buffers[buffer_id])); slot_buffers.erase(buffer_id); - NotifyBufferDeletion(); -} - -template -void BufferCache

::NotifyBufferDeletion() { if constexpr (HAS_PERSISTENT_UNIFORM_BUFFER_BINDINGS) { channel_state->dirty_uniform_buffers.fill(~u32{0}); channel_state->uniform_buffer_binding_sizes.fill({}); } + auto& flags = maxwell3d->dirty.flags; - flags[Dirty::IndexBuffer] = true; - flags[Dirty::VertexBuffers] = true; - for (u32 index = 0; index < NUM_VERTEX_BUFFERS; ++index) { - flags[Dirty::VertexBuffer0 + index] = true; + if (dirty_index) { + flags[Dirty::IndexBuffer] = true; + } + + if (dirty_vertex_buffers.size() > 0) { + flags[Dirty::VertexBuffers] = true; + for (auto index : dirty_vertex_buffers) { + flags[Dirty::VertexBuffer0 + index] = true; + } } channel_state->has_deleted_buffers = true; } diff --git a/src/video_core/buffer_cache/buffer_cache_base.h b/src/video_core/buffer_cache/buffer_cache_base.h index 60a1f285e..cf359e241 100755 --- a/src/video_core/buffer_cache/buffer_cache_base.h +++ b/src/video_core/buffer_cache/buffer_cache_base.h @@ -105,6 +105,15 @@ static constexpr Binding NULL_BINDING{ .buffer_id = NULL_BUFFER_ID, }; +struct HostBindings { + boost::container::small_vector buffers; + boost::container::small_vector offsets; + boost::container::small_vector sizes; + boost::container::small_vector strides; + u32 min_index{NUM_VERTEX_BUFFERS}; + u32 max_index{0}; +}; + class BufferCacheChannelInfo : public ChannelInfo { public: BufferCacheChannelInfo() = delete; @@ -519,8 +528,6 @@ private: void DeleteBuffer(BufferId buffer_id, bool do_not_mark = false); - void NotifyBufferDeletion(); - [[nodiscard]] Binding StorageBufferBinding(GPUVAddr ssbo_addr, u32 cbuf_index, bool is_written) const; diff --git a/src/video_core/renderer_opengl/gl_buffer_cache.cpp b/src/video_core/renderer_opengl/gl_buffer_cache.cpp index 300ccc392..bb9fbda0c 100755 --- a/src/video_core/renderer_opengl/gl_buffer_cache.cpp +++ b/src/video_core/renderer_opengl/gl_buffer_cache.cpp @@ -232,6 +232,15 @@ void BufferCacheRuntime::BindVertexBuffer(u32 index, Buffer& buffer, u32 offset, } } +void BufferCacheRuntime::BindVertexBuffers(VideoCommon::HostBindings& bindings) { + for (u32 index = 0; index < bindings.buffers.size(); index++) { + BindVertexBuffer( + bindings.min_index + index, *reinterpret_cast(bindings.buffers[index]), + static_cast(bindings.offsets[index]), static_cast(bindings.sizes[index]), + static_cast(bindings.strides[index])); + } +} + void BufferCacheRuntime::BindUniformBuffer(size_t stage, u32 binding_index, Buffer& buffer, u32 offset, u32 size) { if (use_assembly_shaders) { @@ -320,6 +329,15 @@ void BufferCacheRuntime::BindTransformFeedbackBuffer(u32 index, Buffer& buffer, static_cast(offset), static_cast(size)); } +void BufferCacheRuntime::BindTransformFeedbackBuffers(VideoCommon::HostBindings& bindings) { + for (u32 index = 0; index < bindings.buffers.size(); index++) { + glBindBufferRange(GL_TRANSFORM_FEEDBACK_BUFFER, index, + reinterpret_cast(bindings.buffers[index])->Handle(), + static_cast(bindings.offsets[index]), + static_cast(bindings.sizes[index])); + } +} + void BufferCacheRuntime::BindTextureBuffer(Buffer& buffer, u32 offset, u32 size, PixelFormat format) { *texture_handles++ = buffer.View(offset, size, format); diff --git a/src/video_core/renderer_opengl/gl_buffer_cache.h b/src/video_core/renderer_opengl/gl_buffer_cache.h index a38627d75..401039cfb 100755 --- a/src/video_core/renderer_opengl/gl_buffer_cache.h +++ b/src/video_core/renderer_opengl/gl_buffer_cache.h @@ -7,7 +7,7 @@ #include #include "common/common_types.h" -#include "video_core/buffer_cache/buffer_cache.h" +#include "video_core/buffer_cache/buffer_cache_base.h" #include "video_core/buffer_cache/memory_tracker_base.h" #include "video_core/rasterizer_interface.h" #include "video_core/renderer_opengl/gl_device.h" @@ -87,6 +87,7 @@ public: void BindIndexBuffer(Buffer& buffer, u32 offset, u32 size); void BindVertexBuffer(u32 index, Buffer& buffer, u32 offset, u32 size, u32 stride); + void BindVertexBuffers(VideoCommon::HostBindings& bindings); void BindUniformBuffer(size_t stage, u32 binding_index, Buffer& buffer, u32 offset, u32 size); @@ -99,6 +100,7 @@ public: bool is_written); void BindTransformFeedbackBuffer(u32 index, Buffer& buffer, u32 offset, u32 size); + void BindTransformFeedbackBuffers(VideoCommon::HostBindings& bindings); void BindTextureBuffer(Buffer& buffer, u32 offset, u32 size, VideoCore::Surface::PixelFormat format); diff --git a/src/video_core/renderer_vulkan/vk_buffer_cache.cpp b/src/video_core/renderer_vulkan/vk_buffer_cache.cpp index 002ab98a0..8900e0c0f 100755 --- a/src/video_core/renderer_vulkan/vk_buffer_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_buffer_cache.cpp @@ -7,7 +7,6 @@ #include #include -#include "video_core/buffer_cache/buffer_cache.h" #include "video_core/renderer_vulkan/maxwell_to_vk.h" #include "video_core/renderer_vulkan/vk_buffer_cache.h" #include "video_core/renderer_vulkan/vk_scheduler.h" @@ -502,6 +501,40 @@ void BufferCacheRuntime::BindVertexBuffer(u32 index, VkBuffer buffer, u32 offset } } +void BufferCacheRuntime::BindVertexBuffers(VideoCommon::HostBindings& bindings) { + boost::container::small_vector buffer_handles; + for (u32 index = 0; index < bindings.buffers.size(); index++) { + auto& buffer = *reinterpret_cast(bindings.buffers[index]); + auto handle = buffer.Handle(); + if (handle == VK_NULL_HANDLE) { + bindings.offsets[index] = 0; + bindings.sizes[index] = VK_WHOLE_SIZE; + if (!device.HasNullDescriptor()) { + ReserveNullBuffer(); + handle = *null_buffer; + } + } + buffer_handles.push_back(handle); + } + if (device.IsExtExtendedDynamicStateSupported()) { + scheduler.Record([bindings = bindings, + buffer_handles = buffer_handles](vk::CommandBuffer cmdbuf) { + cmdbuf.BindVertexBuffers2EXT( + bindings.min_index, bindings.max_index - bindings.min_index, buffer_handles.data(), + reinterpret_cast(bindings.offsets.data()), + reinterpret_cast(bindings.sizes.data()), + reinterpret_cast(bindings.strides.data())); + }); + } else { + scheduler.Record([bindings = bindings, + buffer_handles = buffer_handles](vk::CommandBuffer cmdbuf) { + cmdbuf.BindVertexBuffers( + bindings.min_index, bindings.max_index - bindings.min_index, buffer_handles.data(), + reinterpret_cast(bindings.offsets.data())); + }); + } +} + void BufferCacheRuntime::BindTransformFeedbackBuffer(u32 index, VkBuffer buffer, u32 offset, u32 size) { if (!device.IsExtTransformFeedbackSupported()) { @@ -523,6 +556,25 @@ void BufferCacheRuntime::BindTransformFeedbackBuffer(u32 index, VkBuffer buffer, }); } +void BufferCacheRuntime::BindTransformFeedbackBuffers(VideoCommon::HostBindings& bindings) { + if (!device.IsExtTransformFeedbackSupported()) { + // Already logged in the rasterizer + return; + } + boost::container::small_vector buffer_handles; + for (u32 index = 0; index < bindings.buffers.size(); index++) { + auto& buffer = *reinterpret_cast(bindings.buffers[index]); + buffer_handles.push_back(buffer.Handle()); + } + scheduler.Record( + [bindings = bindings, buffer_handles = buffer_handles](vk::CommandBuffer cmdbuf) { + cmdbuf.BindTransformFeedbackBuffersEXT( + 0, static_cast(buffer_handles.size()), buffer_handles.data(), + reinterpret_cast(bindings.offsets.data()), + reinterpret_cast(bindings.sizes.data())); + }); +} + void BufferCacheRuntime::ReserveNullBuffer() { if (null_buffer) { return; diff --git a/src/video_core/renderer_vulkan/vk_buffer_cache.h b/src/video_core/renderer_vulkan/vk_buffer_cache.h index 6db947080..bc27f4e8d 100755 --- a/src/video_core/renderer_vulkan/vk_buffer_cache.h +++ b/src/video_core/renderer_vulkan/vk_buffer_cache.h @@ -18,6 +18,7 @@ namespace Vulkan { class Device; class DescriptorPool; class Scheduler; +struct HostVertexBinding; class BufferCacheRuntime; @@ -96,8 +97,10 @@ public: void BindQuadIndexBuffer(PrimitiveTopology topology, u32 first, u32 count); void BindVertexBuffer(u32 index, VkBuffer buffer, u32 offset, u32 size, u32 stride); + void BindVertexBuffers(VideoCommon::HostBindings& bindings); void BindTransformFeedbackBuffer(u32 index, VkBuffer buffer, u32 offset, u32 size); + void BindTransformFeedbackBuffers(VideoCommon::HostBindings& bindings); std::span BindMappedUniformBuffer([[maybe_unused]] size_t stage, [[maybe_unused]] u32 binding_index, u32 size) { diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp index 65c33ae8f..1b8706b1b 100755 --- a/src/yuzu/configuration/config.cpp +++ b/src/yuzu/configuration/config.cpp @@ -101,6 +101,12 @@ const std::map Config::renderer_backend_text {Settings::RendererBackend::Null, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "Null"))}, }; +const std::map Config::shader_backend_texts_map = { + {Settings::ShaderBackend::GLSL, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "GLSL"))}, + {Settings::ShaderBackend::GLASM, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "GLASM"))}, + {Settings::ShaderBackend::SPIRV, QStringLiteral(QT_TRANSLATE_NOOP("GMainWindow", "SPIRV"))}, +}; + // This shouldn't have anything except static initializers (no functions). So // QKeySequence(...).toString() is NOT ALLOWED HERE. // This must be in alphabetical order according to action name as it must have the same order as diff --git a/src/yuzu/configuration/config.h b/src/yuzu/configuration/config.h index edc6fbad9..4a1cb82b5 100755 --- a/src/yuzu/configuration/config.h +++ b/src/yuzu/configuration/config.h @@ -54,6 +54,7 @@ public: static const std::map use_docked_mode_texts_map; static const std::map gpu_accuracy_texts_map; static const std::map renderer_backend_texts_map; + static const std::map shader_backend_texts_map; static constexpr UISettings::Theme default_theme{ #ifdef _WIN32 diff --git a/src/yuzu/configuration/configure_graphics_advanced.cpp b/src/yuzu/configuration/configure_graphics_advanced.cpp index c28825a99..e9f0f98ef 100755 --- a/src/yuzu/configuration/configure_graphics_advanced.cpp +++ b/src/yuzu/configuration/configure_graphics_advanced.cpp @@ -42,6 +42,7 @@ void ConfigureGraphicsAdvanced::SetConfiguration() { Settings::values.use_vulkan_driver_pipeline_cache.GetValue()); ui->enable_compute_pipelines_checkbox->setChecked( Settings::values.enable_compute_pipelines.GetValue()); + ui->use_video_framerate_checkbox->setChecked(Settings::values.use_video_framerate.GetValue()); if (Settings::IsConfiguringGlobal()) { ui->gpu_accuracy->setCurrentIndex( @@ -91,6 +92,8 @@ void ConfigureGraphicsAdvanced::ApplyConfiguration() { ConfigurationShared::ApplyPerGameSetting(&Settings::values.enable_compute_pipelines, ui->enable_compute_pipelines_checkbox, enable_compute_pipelines); + ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_video_framerate, + ui->use_video_framerate_checkbox, use_video_framerate); } void ConfigureGraphicsAdvanced::changeEvent(QEvent* event) { @@ -125,6 +128,8 @@ void ConfigureGraphicsAdvanced::SetupPerGameUI() { Settings::values.max_anisotropy.UsingGlobal()); ui->enable_compute_pipelines_checkbox->setEnabled( Settings::values.enable_compute_pipelines.UsingGlobal()); + ui->use_video_framerate_checkbox->setEnabled( + Settings::values.use_video_framerate.UsingGlobal()); return; } @@ -149,6 +154,9 @@ void ConfigureGraphicsAdvanced::SetupPerGameUI() { ConfigurationShared::SetColoredTristate(ui->enable_compute_pipelines_checkbox, Settings::values.enable_compute_pipelines, enable_compute_pipelines); + ConfigurationShared::SetColoredTristate(ui->use_video_framerate_checkbox, + Settings::values.use_video_framerate, + use_video_framerate); ConfigurationShared::SetColoredComboBox( ui->gpu_accuracy, ui->label_gpu_accuracy, static_cast(Settings::values.gpu_accuracy.GetValue(true))); diff --git a/src/yuzu/configuration/configure_graphics_advanced.h b/src/yuzu/configuration/configure_graphics_advanced.h index 97d1b0943..9c9829620 100755 --- a/src/yuzu/configuration/configure_graphics_advanced.h +++ b/src/yuzu/configuration/configure_graphics_advanced.h @@ -47,6 +47,7 @@ private: ConfigurationShared::CheckState use_fast_gpu_time; ConfigurationShared::CheckState use_vulkan_driver_pipeline_cache; ConfigurationShared::CheckState enable_compute_pipelines; + ConfigurationShared::CheckState use_video_framerate; const Core::System& system; }; diff --git a/src/yuzu/configuration/configure_graphics_advanced.ui b/src/yuzu/configuration/configure_graphics_advanced.ui index 5ef7c8c82..babe2d21c 100755 --- a/src/yuzu/configuration/configure_graphics_advanced.ui +++ b/src/yuzu/configuration/configure_graphics_advanced.ui @@ -191,6 +191,16 @@ Compute pipelines are always enabled on all other drivers. + + + + Run the game at normal speed during video playback, even when the framerate is unlocked. + + + Sync to framerate of video playback + + + diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index fef5cafa4..ede2ac973 100755 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp @@ -4116,7 +4116,13 @@ void GMainWindow::UpdateDockedButton() { void GMainWindow::UpdateAPIText() { const auto api = Settings::values.renderer_backend.GetValue(); const auto renderer_status_text = Config::renderer_backend_texts_map.find(api)->second; - renderer_status_button->setText(renderer_status_text.toUpper()); + renderer_status_button->setText( + api == Settings::RendererBackend::OpenGL + ? tr("%1 %2").arg( + renderer_status_text.toUpper(), + Config::shader_backend_texts_map.find(Settings::values.shader_backend.GetValue()) + ->second) + : renderer_status_text.toUpper()); } void GMainWindow::UpdateFilterText() {