From ec28817fce898fd1654e7293272fff7c52921d34 Mon Sep 17 00:00:00 2001 From: pineappleEA Date: Thu, 19 Oct 2023 17:53:53 +0200 Subject: [PATCH] early-access version 3938 --- CMakeLists.txt | 3 + README.md | 2 +- src/audio_core/sink/sink_stream.cpp | 4 + src/common/settings.cpp | 5 + src/common/settings.h | 5 + src/common/settings_enums.h | 2 + src/core/hle/service/acc/acc.cpp | 12 +- src/core/hle/service/am/am.cpp | 20 ++- src/core/hle/service/am/am.h | 2 + src/core/hle/service/hle_ipc.cpp | 58 +++++++-- src/core/hle/service/hle_ipc.h | 6 + src/core/hle/service/prepo/prepo.cpp | 40 ++---- src/input_common/drivers/udp_client.cpp | 1 + src/tests/common/unique_function.cpp | 6 +- src/video_core/engines/draw_manager.cpp | 24 +++- src/video_core/engines/draw_manager.h | 2 + src/video_core/fence_manager.h | 5 +- src/video_core/host1x/codecs/codec.cpp | 10 -- src/video_core/host_shaders/CMakeLists.txt | 1 + .../host_shaders/convert_abgr8_to_d32f.frag | 15 +++ .../host_shaders/convert_d32f_to_abgr8.frag | 2 +- src/video_core/renderer_vulkan/blit_image.cpp | 9 ++ src/video_core/renderer_vulkan/blit_image.h | 4 + .../renderer_vulkan/renderer_vulkan.cpp | 14 ++- .../renderer_vulkan/vk_rasterizer.cpp | 10 +- .../renderer_vulkan/vk_rasterizer.h | 4 + .../renderer_vulkan/vk_texture_cache.cpp | 23 +++- src/video_core/texture_cache/util.cpp | 36 +++--- src/yuzu/CMakeLists.txt | 2 +- src/yuzu/configuration/config.cpp | 4 +- src/yuzu/configuration/configure_input.cpp | 36 ++++-- src/yuzu/configuration/configure_input.h | 1 + src/yuzu/configuration/shared_translation.cpp | 8 ++ src/yuzu/main.cpp | 115 +++++++++++------- src/yuzu/main.h | 18 +++ src/yuzu/uisettings.h | 11 ++ 36 files changed, 376 insertions(+), 144 deletions(-) create mode 100755 src/video_core/host_shaders/convert_abgr8_to_d32f.frag diff --git a/CMakeLists.txt b/CMakeLists.txt index 6f502b323..788043b1c 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -357,6 +357,9 @@ function(set_yuzu_qt_components) if (ENABLE_QT_TRANSLATION) list(APPEND YUZU_QT_COMPONENTS2 LinguistTools) endif() + if (USE_DISCORD_PRESENCE) + list(APPEND YUZU_QT_COMPONENTS2 Network) + endif() set(YUZU_QT_COMPONENTS ${YUZU_QT_COMPONENTS2} PARENT_SCOPE) endfunction(set_yuzu_qt_components) diff --git a/README.md b/README.md index 7c7f87125..4c276905b 100755 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ yuzu emulator early access ============= -This is the source code for early-access 3932. +This is the source code for early-access 3938. ## Legal Notice diff --git a/src/audio_core/sink/sink_stream.cpp b/src/audio_core/sink/sink_stream.cpp index 04d4e6bf0..6ff1bf43d 100755 --- a/src/audio_core/sink/sink_stream.cpp +++ b/src/audio_core/sink/sink_stream.cpp @@ -204,6 +204,10 @@ void SinkStream::ProcessAudioOutAndRender(std::span output_buffer, std::siz // paused and we'll desync, so just play silence. if (system.IsPaused() || system.IsShuttingDown()) { if (system.IsShuttingDown()) { + { + std::scoped_lock lk{release_mutex}; + queued_buffers.store(0); + } release_cv.notify_one(); } diff --git a/src/common/settings.cpp b/src/common/settings.cpp index 8226556b5..96de39a4f 100755 --- a/src/common/settings.cpp +++ b/src/common/settings.cpp @@ -45,6 +45,7 @@ SWITCHABLE(CpuAccuracy, true); SWITCHABLE(FullscreenMode, true); SWITCHABLE(GpuAccuracy, true); SWITCHABLE(Language, true); +SWITCHABLE(MemoryLayout, true); SWITCHABLE(NvdecEmulation, false); SWITCHABLE(Region, true); SWITCHABLE(RendererBackend, true); @@ -61,6 +62,10 @@ SWITCHABLE(u32, false); SWITCHABLE(u8, false); SWITCHABLE(u8, true); +// Used in UISettings +// TODO see if we can move this to uisettings.cpp +SWITCHABLE(ConfirmStop, true); + #undef SETTING #undef SWITCHABLE #endif diff --git a/src/common/settings.h b/src/common/settings.h index 1a6d71ee0..3d70f0919 100755 --- a/src/common/settings.h +++ b/src/common/settings.h @@ -67,6 +67,7 @@ SWITCHABLE(CpuAccuracy, true); SWITCHABLE(FullscreenMode, true); SWITCHABLE(GpuAccuracy, true); SWITCHABLE(Language, true); +SWITCHABLE(MemoryLayout, true); SWITCHABLE(NvdecEmulation, false); SWITCHABLE(Region, true); SWITCHABLE(RendererBackend, true); @@ -83,6 +84,10 @@ SWITCHABLE(u32, false); SWITCHABLE(u8, false); SWITCHABLE(u8, true); +// Used in UISettings +// TODO see if we can move this to uisettings.h +SWITCHABLE(ConfirmStop, true); + #undef SETTING #undef SWITCHABLE #endif diff --git a/src/common/settings_enums.h b/src/common/settings_enums.h index 815cafe15..11429d7a8 100755 --- a/src/common/settings_enums.h +++ b/src/common/settings_enums.h @@ -133,6 +133,8 @@ ENUM(CpuAccuracy, Auto, Accurate, Unsafe, Paranoid); ENUM(MemoryLayout, Memory_4Gb, Memory_6Gb, Memory_8Gb); +ENUM(ConfirmStop, Ask_Always, Ask_Based_On_Game, Ask_Never); + ENUM(FullscreenMode, Borderless, Exclusive); ENUM(NvdecEmulation, Off, Cpu, Gpu); diff --git a/src/core/hle/service/acc/acc.cpp b/src/core/hle/service/acc/acc.cpp index 4259d2be7..fe375adb6 100755 --- a/src/core/hle/service/acc/acc.cpp +++ b/src/core/hle/service/acc/acc.cpp @@ -407,13 +407,13 @@ protected: IPC::RequestParser rp{ctx}; const auto base = rp.PopRaw(); - const auto user_data = ctx.ReadBuffer(0); - const auto image_data = ctx.ReadBuffer(1); // TODO: Check if this buffer is always provided + const auto image_data = ctx.ReadBufferA(0); + const auto user_data = ctx.ReadBufferX(0); - LOG_DEBUG(Service_ACC, "called, username='{}', timestamp={:016X}, uuid=0x{}", - Common::StringFromFixedZeroTerminatedBuffer( - reinterpret_cast(base.username.data()), base.username.size()), - base.timestamp, base.user_uuid.RawString()); + LOG_INFO(Service_ACC, "called, username='{}', timestamp={:016X}, uuid=0x{}", + Common::StringFromFixedZeroTerminatedBuffer( + reinterpret_cast(base.username.data()), base.username.size()), + base.timestamp, base.user_uuid.RawString()); if (user_data.size() < sizeof(UserData)) { LOG_ERROR(Service_ACC, "UserData buffer too small!"); diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp index 8d6aa3bb8..26ae274a3 100755 --- a/src/core/hle/service/am/am.cpp +++ b/src/core/hle/service/am/am.cpp @@ -210,8 +210,8 @@ IDisplayController::IDisplayController(Core::System& system_) {21, nullptr, "ClearAppletTransitionBuffer"}, {22, nullptr, "AcquireLastApplicationCaptureSharedBuffer"}, {23, nullptr, "ReleaseLastApplicationCaptureSharedBuffer"}, - {24, nullptr, "AcquireLastForegroundCaptureSharedBuffer"}, - {25, nullptr, "ReleaseLastForegroundCaptureSharedBuffer"}, + {24, &IDisplayController::AcquireLastForegroundCaptureSharedBuffer, "AcquireLastForegroundCaptureSharedBuffer"}, + {25, &IDisplayController::ReleaseLastForegroundCaptureSharedBuffer, "ReleaseLastForegroundCaptureSharedBuffer"}, {26, &IDisplayController::AcquireCallerAppletCaptureSharedBuffer, "AcquireCallerAppletCaptureSharedBuffer"}, {27, &IDisplayController::ReleaseCallerAppletCaptureSharedBuffer, "ReleaseCallerAppletCaptureSharedBuffer"}, {28, nullptr, "TakeScreenShotOfOwnLayerEx"}, @@ -239,6 +239,22 @@ void IDisplayController::TakeScreenShotOfOwnLayer(HLERequestContext& ctx) { rb.Push(ResultSuccess); } +void IDisplayController::AcquireLastForegroundCaptureSharedBuffer(HLERequestContext& ctx) { + LOG_WARNING(Service_AM, "(STUBBED) called"); + + IPC::ResponseBuilder rb{ctx, 4}; + rb.Push(ResultSuccess); + rb.Push(1U); + rb.Push(0); +} + +void IDisplayController::ReleaseLastForegroundCaptureSharedBuffer(HLERequestContext& ctx) { + LOG_WARNING(Service_AM, "(STUBBED) called"); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); +} + void IDisplayController::AcquireCallerAppletCaptureSharedBuffer(HLERequestContext& ctx) { LOG_WARNING(Service_AM, "(STUBBED) called"); diff --git a/src/core/hle/service/am/am.h b/src/core/hle/service/am/am.h index d8ec3c225..a35bb7774 100755 --- a/src/core/hle/service/am/am.h +++ b/src/core/hle/service/am/am.h @@ -124,6 +124,8 @@ public: private: void GetCallerAppletCaptureImageEx(HLERequestContext& ctx); void TakeScreenShotOfOwnLayer(HLERequestContext& ctx); + void AcquireLastForegroundCaptureSharedBuffer(HLERequestContext& ctx); + void ReleaseLastForegroundCaptureSharedBuffer(HLERequestContext& ctx); void AcquireCallerAppletCaptureSharedBuffer(HLERequestContext& ctx); void ReleaseCallerAppletCaptureSharedBuffer(HLERequestContext& ctx); }; diff --git a/src/core/hle/service/hle_ipc.cpp b/src/core/hle/service/hle_ipc.cpp index f6a1e54f2..6f3ae3cc4 100755 --- a/src/core/hle/service/hle_ipc.cpp +++ b/src/core/hle/service/hle_ipc.cpp @@ -23,6 +23,17 @@ #include "core/hle/service/ipc_helpers.h" #include "core/memory.h" +namespace { +static thread_local std::array read_buffer_data_a{ + Common::ScratchBuffer(), + Common::ScratchBuffer(), +}; +static thread_local std::array read_buffer_data_x{ + Common::ScratchBuffer(), + Common::ScratchBuffer(), +}; +} // Anonymous namespace + namespace Service { SessionRequestHandler::SessionRequestHandler(Kernel::KernelCore& kernel_, const char* service_name_) @@ -328,26 +339,57 @@ std::vector HLERequestContext::ReadBufferCopy(std::size_t buffer_index) cons } } +std::span HLERequestContext::ReadBufferA(std::size_t buffer_index) const { + static thread_local std::array read_buffer_a{ + Core::Memory::CpuGuestMemory(memory, 0, 0), + Core::Memory::CpuGuestMemory(memory, 0, 0), + }; + + ASSERT_OR_EXECUTE_MSG( + BufferDescriptorA().size() > buffer_index, { return {}; }, + "BufferDescriptorA invalid buffer_index {}", buffer_index); + auto& read_buffer = read_buffer_a[buffer_index]; + return read_buffer.Read(BufferDescriptorA()[buffer_index].Address(), + BufferDescriptorA()[buffer_index].Size(), + &read_buffer_data_a[buffer_index]); +} + +std::span HLERequestContext::ReadBufferX(std::size_t buffer_index) const { + static thread_local std::array read_buffer_x{ + Core::Memory::CpuGuestMemory(memory, 0, 0), + Core::Memory::CpuGuestMemory(memory, 0, 0), + }; + + ASSERT_OR_EXECUTE_MSG( + BufferDescriptorX().size() > buffer_index, { return {}; }, + "BufferDescriptorX invalid buffer_index {}", buffer_index); + auto& read_buffer = read_buffer_x[buffer_index]; + return read_buffer.Read(BufferDescriptorX()[buffer_index].Address(), + BufferDescriptorX()[buffer_index].Size(), + &read_buffer_data_x[buffer_index]); +} + std::span HLERequestContext::ReadBuffer(std::size_t buffer_index) const { static thread_local std::array read_buffer_a{ Core::Memory::CpuGuestMemory(memory, 0, 0), Core::Memory::CpuGuestMemory(memory, 0, 0), }; - static thread_local std::array read_buffer_data_a{ - Common::ScratchBuffer(), - Common::ScratchBuffer(), - }; static thread_local std::array read_buffer_x{ Core::Memory::CpuGuestMemory(memory, 0, 0), Core::Memory::CpuGuestMemory(memory, 0, 0), }; - static thread_local std::array read_buffer_data_x{ - Common::ScratchBuffer(), - Common::ScratchBuffer(), - }; const bool is_buffer_a{BufferDescriptorA().size() > buffer_index && BufferDescriptorA()[buffer_index].Size()}; + const bool is_buffer_x{BufferDescriptorX().size() > buffer_index && + BufferDescriptorX()[buffer_index].Size()}; + + if (is_buffer_a && is_buffer_x) { + LOG_WARNING(Input, "Both buffer descriptors are available a.size={}, x.size={}", + BufferDescriptorA()[buffer_index].Size(), + BufferDescriptorX()[buffer_index].Size()); + } + if (is_buffer_a) { ASSERT_OR_EXECUTE_MSG( BufferDescriptorA().size() > buffer_index, { return {}; }, diff --git a/src/core/hle/service/hle_ipc.h b/src/core/hle/service/hle_ipc.h index 4bd24c899..ad5259a5c 100755 --- a/src/core/hle/service/hle_ipc.h +++ b/src/core/hle/service/hle_ipc.h @@ -253,6 +253,12 @@ public: return domain_message_header.has_value(); } + /// Helper function to get a span of a buffer using the buffer descriptor A + [[nodiscard]] std::span ReadBufferA(std::size_t buffer_index = 0) const; + + /// Helper function to get a span of a buffer using the buffer descriptor X + [[nodiscard]] std::span ReadBufferX(std::size_t buffer_index = 0) const; + /// Helper function to get a span of a buffer using the appropriate buffer descriptor [[nodiscard]] std::span ReadBuffer(std::size_t buffer_index = 0) const; diff --git a/src/core/hle/service/prepo/prepo.cpp b/src/core/hle/service/prepo/prepo.cpp index 1c9d9f3bf..286ec27a9 100755 --- a/src/core/hle/service/prepo/prepo.cpp +++ b/src/core/hle/service/prepo/prepo.cpp @@ -58,14 +58,8 @@ private: IPC::RequestParser rp{ctx}; const auto process_id = rp.PopRaw(); - const auto data1 = ctx.ReadBuffer(0); - const auto data2 = [&ctx] { - if (ctx.CanReadBuffer(1)) { - return ctx.ReadBuffer(1); - } - - return std::span{}; - }(); + const auto data1 = ctx.ReadBufferA(0); + const auto data2 = ctx.ReadBufferX(0); LOG_DEBUG(Service_PREPO, "called, type={:02X}, process_id={:016X}, data1_size={:016X}, data2_size={:016X}", @@ -85,14 +79,8 @@ private: const auto user_id = rp.PopRaw(); const auto process_id = rp.PopRaw(); - const auto data1 = ctx.ReadBuffer(0); - const auto data2 = [&ctx] { - if (ctx.CanReadBuffer(1)) { - return ctx.ReadBuffer(1); - } - - return std::span{}; - }(); + const auto data1 = ctx.ReadBufferA(0); + const auto data2 = ctx.ReadBufferX(0); LOG_DEBUG(Service_PREPO, "called, type={:02X}, user_id={:016X}{:016X}, process_id={:016X}, " @@ -137,14 +125,8 @@ private: IPC::RequestParser rp{ctx}; const auto title_id = rp.PopRaw(); - const auto data1 = ctx.ReadBuffer(0); - const auto data2 = [&ctx] { - if (ctx.CanReadBuffer(1)) { - return ctx.ReadBuffer(1); - } - - return std::span{}; - }(); + const auto data1 = ctx.ReadBufferA(0); + const auto data2 = ctx.ReadBufferX(0); LOG_DEBUG(Service_PREPO, "called, title_id={:016X}, data1_size={:016X}, data2_size={:016X}", title_id, data1.size(), data2.size()); @@ -161,14 +143,8 @@ private: const auto user_id = rp.PopRaw(); const auto title_id = rp.PopRaw(); - const auto data1 = ctx.ReadBuffer(0); - const auto data2 = [&ctx] { - if (ctx.CanReadBuffer(1)) { - return ctx.ReadBuffer(1); - } - - return std::span{}; - }(); + const auto data1 = ctx.ReadBufferA(0); + const auto data2 = ctx.ReadBufferX(0); LOG_DEBUG(Service_PREPO, "called, user_id={:016X}{:016X}, title_id={:016X}, data1_size={:016X}, " diff --git a/src/input_common/drivers/udp_client.cpp b/src/input_common/drivers/udp_client.cpp index bc8af52ae..b124d5c07 100755 --- a/src/input_common/drivers/udp_client.cpp +++ b/src/input_common/drivers/udp_client.cpp @@ -338,6 +338,7 @@ void UDPClient::StartCommunication(std::size_t client, const std::string& host, for (std::size_t index = 0; index < PADS_PER_CLIENT; ++index) { const PadIdentifier identifier = GetPadIdentifier(client * PADS_PER_CLIENT + index); PreSetController(identifier); + PreSetMotion(identifier, 0); } } diff --git a/src/tests/common/unique_function.cpp b/src/tests/common/unique_function.cpp index 53379eedc..88d716fcc 100755 --- a/src/tests/common/unique_function.cpp +++ b/src/tests/common/unique_function.cpp @@ -46,8 +46,8 @@ TEST_CASE("UniqueFunction", "[common]") { Noisy noisy; REQUIRE(noisy.state == "Default constructed"); - Common::UniqueFunction func = [noisy = std::move(noisy)] { - REQUIRE(noisy.state == "Move constructed"); + Common::UniqueFunction func = [noisy_inner = std::move(noisy)] { + REQUIRE(noisy_inner.state == "Move constructed"); }; REQUIRE(noisy.state == "Moved away"); func(); @@ -101,7 +101,7 @@ TEST_CASE("UniqueFunction", "[common]") { }; Foo object{&num_destroyed}; { - Common::UniqueFunction func = [object = std::move(object)] {}; + Common::UniqueFunction func = [object_inner = std::move(object)] {}; REQUIRE(num_destroyed == 0); } REQUIRE(num_destroyed == 1); diff --git a/src/video_core/engines/draw_manager.cpp b/src/video_core/engines/draw_manager.cpp index f34090791..d77ff455b 100755 --- a/src/video_core/engines/draw_manager.cpp +++ b/src/video_core/engines/draw_manager.cpp @@ -48,8 +48,14 @@ void DrawManager::ProcessMethodCall(u32 method, u32 argument) { SetInlineIndexBuffer(regs.inline_index_4x8.index3); break; case MAXWELL3D_REG_INDEX(vertex_array_instance_first): + DrawArrayInstanced(regs.vertex_array_instance_first.topology.Value(), + regs.vertex_array_instance_first.start.Value(), + regs.vertex_array_instance_first.count.Value(), false); + break; case MAXWELL3D_REG_INDEX(vertex_array_instance_subsequent): { - LOG_WARNING(HW_GPU, "(STUBBED) called"); + DrawArrayInstanced(regs.vertex_array_instance_subsequent.topology.Value(), + regs.vertex_array_instance_subsequent.start.Value(), + regs.vertex_array_instance_subsequent.count.Value(), true); break; } case MAXWELL3D_REG_INDEX(draw_texture.src_y0): { @@ -84,6 +90,22 @@ void DrawManager::DrawArray(PrimitiveTopology topology, u32 vertex_first, u32 ve ProcessDraw(false, num_instances); } +void DrawManager::DrawArrayInstanced(PrimitiveTopology topology, u32 vertex_first, u32 vertex_count, + bool subsequent) { + draw_state.topology = topology; + draw_state.vertex_buffer.first = vertex_first; + draw_state.vertex_buffer.count = vertex_count; + + if (!subsequent) { + draw_state.instance_count = 1; + } + + draw_state.base_instance = draw_state.instance_count - 1; + draw_state.draw_mode = DrawMode::Instance; + draw_state.instance_count++; + ProcessDraw(false, 1); +} + void DrawManager::DrawIndex(PrimitiveTopology topology, u32 index_first, u32 index_count, u32 base_index, u32 base_instance, u32 num_instances) { const auto& regs{maxwell3d->regs}; diff --git a/src/video_core/engines/draw_manager.h b/src/video_core/engines/draw_manager.h index 18d959143..cfc8127fc 100755 --- a/src/video_core/engines/draw_manager.h +++ b/src/video_core/engines/draw_manager.h @@ -66,6 +66,8 @@ public: void DrawArray(PrimitiveTopology topology, u32 vertex_first, u32 vertex_count, u32 base_instance, u32 num_instances); + void DrawArrayInstanced(PrimitiveTopology topology, u32 vertex_first, u32 vertex_count, + bool subsequent); void DrawIndex(PrimitiveTopology topology, u32 index_first, u32 index_count, u32 base_index, u32 base_instance, u32 num_instances); diff --git a/src/video_core/fence_manager.h b/src/video_core/fence_manager.h index 139c824d3..667941b7c 100755 --- a/src/video_core/fence_manager.h +++ b/src/video_core/fence_manager.h @@ -86,7 +86,10 @@ public: uncommitted_operations.emplace_back(std::move(func)); } pending_operations.emplace_back(std::move(uncommitted_operations)); - QueueFence(new_fence); + { + std::scoped_lock lock{buffer_cache.mutex, texture_cache.mutex}; + QueueFence(new_fence); + } if (!delay_fence) { func(); } diff --git a/src/video_core/host1x/codecs/codec.cpp b/src/video_core/host1x/codecs/codec.cpp index 350deff94..70fa1eff4 100755 --- a/src/video_core/host1x/codecs/codec.cpp +++ b/src/video_core/host1x/codecs/codec.cpp @@ -137,16 +137,6 @@ bool Codec::CreateGpuAvDevice() { break; } if ((config->methods & HW_CONFIG_METHOD) != 0 && config->device_type == type) { -#if defined(__unix__) - // Some linux decoding backends are reported to crash with this config method - // TODO(ameerj): Properly support this method - if ((config->methods & AV_CODEC_HW_CONFIG_METHOD_HW_FRAMES_CTX) != 0) { - // skip zero-copy decoders, we don't currently support them - LOG_DEBUG(Service_NVDRV, "Skipping decoder {} with unsupported capability {}.", - av_hwdevice_get_type_name(type), config->methods); - continue; - } -#endif LOG_INFO(Service_NVDRV, "Using {} GPU decoder", av_hwdevice_get_type_name(type)); av_codec_ctx->pix_fmt = config->pix_fmt; return true; diff --git a/src/video_core/host_shaders/CMakeLists.txt b/src/video_core/host_shaders/CMakeLists.txt index fd6783054..db1074f7b 100755 --- a/src/video_core/host_shaders/CMakeLists.txt +++ b/src/video_core/host_shaders/CMakeLists.txt @@ -19,6 +19,7 @@ set(SHADER_FILES block_linear_unswizzle_2d.comp block_linear_unswizzle_3d.comp convert_abgr8_to_d24s8.frag + convert_abgr8_to_d32f.frag convert_d32f_to_abgr8.frag convert_d24s8_to_abgr8.frag convert_depth_to_float.frag diff --git a/src/video_core/host_shaders/convert_abgr8_to_d32f.frag b/src/video_core/host_shaders/convert_abgr8_to_d32f.frag new file mode 100755 index 000000000..095b910c2 --- /dev/null +++ b/src/video_core/host_shaders/convert_abgr8_to_d32f.frag @@ -0,0 +1,15 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#version 450 + +layout(binding = 0) uniform sampler2D color_texture; + +void main() { + ivec2 coord = ivec2(gl_FragCoord.xy); + vec4 color = texelFetch(color_texture, coord, 0).abgr; + + float value = color.a * (color.r + color.g + color.b) / 3.0f; + + gl_FragDepth = value; +} diff --git a/src/video_core/host_shaders/convert_d32f_to_abgr8.frag b/src/video_core/host_shaders/convert_d32f_to_abgr8.frag index 04cfef8b5..4e5a9f955 100755 --- a/src/video_core/host_shaders/convert_d32f_to_abgr8.frag +++ b/src/video_core/host_shaders/convert_d32f_to_abgr8.frag @@ -9,6 +9,6 @@ layout(location = 0) out vec4 color; void main() { ivec2 coord = ivec2(gl_FragCoord.xy); - float depth = textureLod(depth_tex, coord, 0).r; + float depth = texelFetch(depth_tex, coord, 0).r; color = vec4(depth, depth, depth, 1.0); } diff --git a/src/video_core/renderer_vulkan/blit_image.cpp b/src/video_core/renderer_vulkan/blit_image.cpp index d7e7352da..d8aeb6d8b 100755 --- a/src/video_core/renderer_vulkan/blit_image.cpp +++ b/src/video_core/renderer_vulkan/blit_image.cpp @@ -8,6 +8,7 @@ #include "common/settings.h" #include "video_core/host_shaders/blit_color_float_frag_spv.h" #include "video_core/host_shaders/convert_abgr8_to_d24s8_frag_spv.h" +#include "video_core/host_shaders/convert_abgr8_to_d32f_frag_spv.h" #include "video_core/host_shaders/convert_d24s8_to_abgr8_frag_spv.h" #include "video_core/host_shaders/convert_d32f_to_abgr8_frag_spv.h" #include "video_core/host_shaders/convert_depth_to_float_frag_spv.h" @@ -434,6 +435,7 @@ BlitImageHelper::BlitImageHelper(const Device& device_, Scheduler& scheduler_, convert_depth_to_float_frag(BuildShader(device, CONVERT_DEPTH_TO_FLOAT_FRAG_SPV)), convert_float_to_depth_frag(BuildShader(device, CONVERT_FLOAT_TO_DEPTH_FRAG_SPV)), convert_abgr8_to_d24s8_frag(BuildShader(device, CONVERT_ABGR8_TO_D24S8_FRAG_SPV)), + convert_abgr8_to_d32f_frag(BuildShader(device, CONVERT_ABGR8_TO_D32F_FRAG_SPV)), convert_d32f_to_abgr8_frag(BuildShader(device, CONVERT_D32F_TO_ABGR8_FRAG_SPV)), convert_d24s8_to_abgr8_frag(BuildShader(device, CONVERT_D24S8_TO_ABGR8_FRAG_SPV)), convert_s8d24_to_abgr8_frag(BuildShader(device, CONVERT_S8D24_TO_ABGR8_FRAG_SPV)), @@ -559,6 +561,13 @@ void BlitImageHelper::ConvertABGR8ToD24S8(const Framebuffer* dst_framebuffer, Convert(*convert_abgr8_to_d24s8_pipeline, dst_framebuffer, src_image_view); } +void BlitImageHelper::ConvertABGR8ToD32F(const Framebuffer* dst_framebuffer, + const ImageView& src_image_view) { + ConvertPipelineDepthTargetEx(convert_abgr8_to_d32f_pipeline, dst_framebuffer->RenderPass(), + convert_abgr8_to_d32f_frag); + Convert(*convert_abgr8_to_d32f_pipeline, dst_framebuffer, src_image_view); +} + void BlitImageHelper::ConvertD32FToABGR8(const Framebuffer* dst_framebuffer, ImageView& src_image_view) { ConvertPipelineColorTargetEx(convert_d32f_to_abgr8_pipeline, dst_framebuffer->RenderPass(), diff --git a/src/video_core/renderer_vulkan/blit_image.h b/src/video_core/renderer_vulkan/blit_image.h index e667722cc..7e70ee2c7 100755 --- a/src/video_core/renderer_vulkan/blit_image.h +++ b/src/video_core/renderer_vulkan/blit_image.h @@ -67,6 +67,8 @@ public: void ConvertABGR8ToD24S8(const Framebuffer* dst_framebuffer, const ImageView& src_image_view); + void ConvertABGR8ToD32F(const Framebuffer* dst_framebuffer, const ImageView& src_image_view); + void ConvertD32FToABGR8(const Framebuffer* dst_framebuffer, ImageView& src_image_view); void ConvertD24S8ToABGR8(const Framebuffer* dst_framebuffer, ImageView& src_image_view); @@ -130,6 +132,7 @@ private: vk::ShaderModule convert_depth_to_float_frag; vk::ShaderModule convert_float_to_depth_frag; vk::ShaderModule convert_abgr8_to_d24s8_frag; + vk::ShaderModule convert_abgr8_to_d32f_frag; vk::ShaderModule convert_d32f_to_abgr8_frag; vk::ShaderModule convert_d24s8_to_abgr8_frag; vk::ShaderModule convert_s8d24_to_abgr8_frag; @@ -149,6 +152,7 @@ private: vk::Pipeline convert_d16_to_r16_pipeline; vk::Pipeline convert_r16_to_d16_pipeline; vk::Pipeline convert_abgr8_to_d24s8_pipeline; + vk::Pipeline convert_abgr8_to_d32f_pipeline; vk::Pipeline convert_d32f_to_abgr8_pipeline; vk::Pipeline convert_d24s8_to_abgr8_pipeline; vk::Pipeline convert_s8d24_to_abgr8_pipeline; diff --git a/src/video_core/renderer_vulkan/renderer_vulkan.cpp b/src/video_core/renderer_vulkan/renderer_vulkan.cpp index fd013e35c..6f0f23cb4 100755 --- a/src/video_core/renderer_vulkan/renderer_vulkan.cpp +++ b/src/video_core/renderer_vulkan/renderer_vulkan.cpp @@ -132,12 +132,16 @@ void RendererVulkan::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) { const bool use_accelerated = rasterizer.AccelerateDisplay(*framebuffer, framebuffer_addr, framebuffer->stride); const bool is_srgb = use_accelerated && screen_info.is_srgb; - RenderScreenshot(*framebuffer, use_accelerated); - Frame* frame = present_manager.GetRenderFrame(); - blit_screen.DrawToSwapchain(frame, *framebuffer, use_accelerated, is_srgb); - scheduler.Flush(*frame->render_ready); - present_manager.Present(frame); + { + std::scoped_lock lock{rasterizer.LockCaches()}; + RenderScreenshot(*framebuffer, use_accelerated); + + Frame* frame = present_manager.GetRenderFrame(); + blit_screen.DrawToSwapchain(frame, *framebuffer, use_accelerated, is_srgb); + scheduler.Flush(*frame->render_ready); + present_manager.Present(frame); + } gpu.RendererFrameEndNotify(); rasterizer.TickFrame(); diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp index f518945d3..0335caa77 100755 --- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp +++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp @@ -198,7 +198,7 @@ void RasterizerVulkan::PrepareDraw(bool is_indexed, Func&& draw_func) { if (!pipeline) { return; } - std::scoped_lock lock{buffer_cache.mutex, texture_cache.mutex}; + std::scoped_lock lock{LockCaches()}; // update engine as channel may be different. pipeline->SetEngine(maxwell3d, gpu_memory); pipeline->Configure(is_indexed); @@ -708,6 +708,7 @@ void RasterizerVulkan::TiledCacheBarrier() { } void RasterizerVulkan::FlushCommands() { + std::scoped_lock lock{LockCaches()}; if (draw_counter == 0) { return; } @@ -805,6 +806,7 @@ void RasterizerVulkan::FlushWork() { if ((++draw_counter & 7) != 7) { return; } + std::scoped_lock lock{LockCaches()}; if (draw_counter < DRAWS_TO_DISPATCH) { // Send recorded tasks to the worker thread scheduler.DispatchWork(); @@ -1499,7 +1501,7 @@ void RasterizerVulkan::UpdateVertexInput(Tegra::Engines::Maxwell3D::Regs& regs) void RasterizerVulkan::InitializeChannel(Tegra::Control::ChannelState& channel) { CreateChannel(channel); { - std::scoped_lock lock{buffer_cache.mutex, texture_cache.mutex}; + std::scoped_lock lock{LockCaches()}; texture_cache.CreateChannel(channel); buffer_cache.CreateChannel(channel); } @@ -1512,7 +1514,7 @@ void RasterizerVulkan::BindChannel(Tegra::Control::ChannelState& channel) { const s32 channel_id = channel.bind_id; BindToChannel(channel_id); { - std::scoped_lock lock{buffer_cache.mutex, texture_cache.mutex}; + std::scoped_lock lock{LockCaches()}; texture_cache.BindToChannel(channel_id); buffer_cache.BindToChannel(channel_id); } @@ -1525,7 +1527,7 @@ void RasterizerVulkan::BindChannel(Tegra::Control::ChannelState& channel) { void RasterizerVulkan::ReleaseChannel(s32 channel_id) { EraseChannel(channel_id); { - std::scoped_lock lock{buffer_cache.mutex, texture_cache.mutex}; + std::scoped_lock lock{LockCaches()}; texture_cache.EraseChannel(channel_id); buffer_cache.EraseChannel(channel_id); } diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.h b/src/video_core/renderer_vulkan/vk_rasterizer.h index b8c637c47..eec070e2a 100755 --- a/src/video_core/renderer_vulkan/vk_rasterizer.h +++ b/src/video_core/renderer_vulkan/vk_rasterizer.h @@ -133,6 +133,10 @@ public: void ReleaseChannel(s32 channel_id) override; + std::scoped_lock LockCaches() { + return std::scoped_lock{buffer_cache.mutex, texture_cache.mutex}; + } + private: static constexpr size_t MAX_TEXTURES = 192; static constexpr size_t MAX_IMAGES = 48; diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.cpp b/src/video_core/renderer_vulkan/vk_texture_cache.cpp index 3250d4faa..0e5fdd099 100755 --- a/src/video_core/renderer_vulkan/vk_texture_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_texture_cache.cpp @@ -1194,6 +1194,11 @@ void TextureCacheRuntime::ConvertImage(Framebuffer* dst, ImageView& dst_view, Im return blit_image_helper.ConvertD16ToR16(dst, src_view); } break; + case PixelFormat::A8B8G8R8_SRGB: + if (src_view.format == PixelFormat::D32_FLOAT) { + return blit_image_helper.ConvertD32FToABGR8(dst, src_view); + } + break; case PixelFormat::A8B8G8R8_UNORM: if (src_view.format == PixelFormat::S8_UINT_D24_UNORM) { return blit_image_helper.ConvertD24S8ToABGR8(dst, src_view); @@ -1205,6 +1210,16 @@ void TextureCacheRuntime::ConvertImage(Framebuffer* dst, ImageView& dst_view, Im return blit_image_helper.ConvertD32FToABGR8(dst, src_view); } break; + case PixelFormat::B8G8R8A8_SRGB: + if (src_view.format == PixelFormat::D32_FLOAT) { + return blit_image_helper.ConvertD32FToABGR8(dst, src_view); + } + break; + case PixelFormat::B8G8R8A8_UNORM: + if (src_view.format == PixelFormat::D32_FLOAT) { + return blit_image_helper.ConvertD32FToABGR8(dst, src_view); + } + break; case PixelFormat::R32_FLOAT: if (src_view.format == PixelFormat::D32_FLOAT) { return blit_image_helper.ConvertD32ToR32(dst, src_view); @@ -1222,6 +1237,12 @@ void TextureCacheRuntime::ConvertImage(Framebuffer* dst, ImageView& dst_view, Im } break; case PixelFormat::D32_FLOAT: + if (src_view.format == PixelFormat::A8B8G8R8_UNORM || + src_view.format == PixelFormat::B8G8R8A8_UNORM || + src_view.format == PixelFormat::A8B8G8R8_SRGB || + src_view.format == PixelFormat::B8G8R8A8_SRGB) { + return blit_image_helper.ConvertABGR8ToD32F(dst, src_view); + } if (src_view.format == PixelFormat::R32_FLOAT) { return blit_image_helper.ConvertR32ToD32(dst, src_view); } @@ -2034,7 +2055,7 @@ void TextureCacheRuntime::TransitionImageLayout(Image& image) { }, }; scheduler.RequestOutsideRenderPassOperationContext(); - scheduler.Record([barrier = barrier](vk::CommandBuffer cmdbuf) { + scheduler.Record([barrier](vk::CommandBuffer cmdbuf) { cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, 0, barrier); }); diff --git a/src/video_core/texture_cache/util.cpp b/src/video_core/texture_cache/util.cpp index 76b0c6d48..d9959baf3 100755 --- a/src/video_core/texture_cache/util.cpp +++ b/src/video_core/texture_cache/util.cpp @@ -68,6 +68,7 @@ struct LevelInfo { Extent2D tile_size; u32 bpp_log2; u32 tile_width_spacing; + u32 num_levels; }; [[nodiscard]] constexpr u32 AdjustTileSize(u32 shift, u32 unit_factor, u32 dimension) { @@ -118,11 +119,11 @@ template } [[nodiscard]] constexpr Extent3D AdjustMipBlockSize(Extent3D num_tiles, Extent3D block_size, - u32 level) { + u32 level, u32 num_levels) { return { .width = AdjustMipBlockSize(num_tiles.width, block_size.width, level), .height = AdjustMipBlockSize(num_tiles.height, block_size.height, level), - .depth = level == 0 + .depth = level == 0 && num_levels == 1 ? block_size.depth : AdjustMipBlockSize(num_tiles.depth, block_size.depth, level), }; @@ -166,7 +167,7 @@ template } [[nodiscard]] constexpr Extent3D TileShift(const LevelInfo& info, u32 level) { - if (level == 0) { + if (level == 0 && info.num_levels == 1) { return Extent3D{ .width = info.block.width, .height = info.block.height, @@ -257,7 +258,7 @@ template } [[nodiscard]] constexpr LevelInfo MakeLevelInfo(PixelFormat format, Extent3D size, Extent3D block, - u32 tile_width_spacing) { + u32 tile_width_spacing, u32 num_levels) { const u32 bytes_per_block = BytesPerBlock(format); return { .size = @@ -270,16 +271,18 @@ template .tile_size = DefaultBlockSize(format), .bpp_log2 = BytesPerBlockLog2(bytes_per_block), .tile_width_spacing = tile_width_spacing, + .num_levels = num_levels, }; } [[nodiscard]] constexpr LevelInfo MakeLevelInfo(const ImageInfo& info) { - return MakeLevelInfo(info.format, info.size, info.block, info.tile_width_spacing); + return MakeLevelInfo(info.format, info.size, info.block, info.tile_width_spacing, + info.resources.levels); } [[nodiscard]] constexpr u32 CalculateLevelOffset(PixelFormat format, Extent3D size, Extent3D block, u32 tile_width_spacing, u32 level) { - const LevelInfo info = MakeLevelInfo(format, size, block, tile_width_spacing); + const LevelInfo info = MakeLevelInfo(format, size, block, tile_width_spacing, level); u32 offset = 0; for (u32 current_level = 0; current_level < level; ++current_level) { offset += CalculateLevelSize(info, current_level); @@ -466,7 +469,7 @@ template }; const u32 bpp_log2 = BytesPerBlockLog2(info.format); const u32 alignment = StrideAlignment(num_tiles, info.block, bpp_log2, info.tile_width_spacing); - const Extent3D mip_block = AdjustMipBlockSize(num_tiles, info.block, 0); + const Extent3D mip_block = AdjustMipBlockSize(num_tiles, info.block, 0, info.resources.levels); return Extent3D{ .width = Common::AlignUpLog2(num_tiles.width, alignment), .height = Common::AlignUpLog2(num_tiles.height, GOB_SIZE_Y_SHIFT + mip_block.height), @@ -533,7 +536,8 @@ void SwizzleBlockLinearImage(Tegra::MemoryManager& gpu_memory, GPUVAddr gpu_addr UNIMPLEMENTED_IF(copy.image_extent != level_size); const Extent3D num_tiles = AdjustTileSize(level_size, tile_size); - const Extent3D block = AdjustMipBlockSize(num_tiles, level_info.block, level); + const Extent3D block = + AdjustMipBlockSize(num_tiles, level_info.block, level, level_info.num_levels); size_t host_offset = copy.buffer_offset; @@ -698,7 +702,7 @@ u32 CalculateLevelStrideAlignment(const ImageInfo& info, u32 level) { const Extent2D tile_size = DefaultBlockSize(info.format); const Extent3D level_size = AdjustMipSize(info.size, level); const Extent3D num_tiles = AdjustTileSize(level_size, tile_size); - const Extent3D block = AdjustMipBlockSize(num_tiles, info.block, level); + const Extent3D block = AdjustMipBlockSize(num_tiles, info.block, level, info.resources.levels); const u32 bpp_log2 = BytesPerBlockLog2(info.format); return StrideAlignment(num_tiles, block, bpp_log2, info.tile_width_spacing); } @@ -887,7 +891,8 @@ boost::container::small_vector UnswizzleImage(Tegra::Memory .image_extent = level_size, }; const Extent3D num_tiles = AdjustTileSize(level_size, tile_size); - const Extent3D block = AdjustMipBlockSize(num_tiles, level_info.block, level); + const Extent3D block = + AdjustMipBlockSize(num_tiles, level_info.block, level, level_info.num_levels); const u32 stride_alignment = StrideAlignment(num_tiles, info.block, gob, bpp_log2); size_t guest_layer_offset = 0; @@ -1041,7 +1046,7 @@ Extent3D MipBlockSize(const ImageInfo& info, u32 level) { const Extent2D tile_size = DefaultBlockSize(info.format); const Extent3D level_size = AdjustMipSize(info.size, level); const Extent3D num_tiles = AdjustTileSize(level_size, tile_size); - return AdjustMipBlockSize(num_tiles, level_info.block, level); + return AdjustMipBlockSize(num_tiles, level_info.block, level, level_info.num_levels); } boost::container::small_vector FullUploadSwizzles(const ImageInfo& info) { @@ -1063,7 +1068,8 @@ boost::container::small_vector FullUploadSwizzles(const I for (s32 level = 0; level < num_levels; ++level) { const Extent3D level_size = AdjustMipSize(size, level); const Extent3D num_tiles = AdjustTileSize(level_size, tile_size); - const Extent3D block = AdjustMipBlockSize(num_tiles, level_info.block, level); + const Extent3D block = + AdjustMipBlockSize(num_tiles, level_info.block, level, level_info.num_levels); params[level] = SwizzleParameters{ .num_tiles = num_tiles, .block = block, @@ -1292,11 +1298,11 @@ u32 MapSizeBytes(const ImageBase& image) { } } -static_assert(CalculateLevelSize(LevelInfo{{1920, 1080, 1}, {0, 2, 0}, {1, 1}, 2, 0}, 0) == +static_assert(CalculateLevelSize(LevelInfo{{1920, 1080, 1}, {0, 2, 0}, {1, 1}, 2, 0, 1}, 0) == 0x7f8000); -static_assert(CalculateLevelSize(LevelInfo{{32, 32, 1}, {0, 0, 4}, {1, 1}, 4, 0}, 0) == 0x40000); +static_assert(CalculateLevelSize(LevelInfo{{32, 32, 1}, {0, 0, 4}, {1, 1}, 4, 0, 1}, 0) == 0x40000); -static_assert(CalculateLevelSize(LevelInfo{{128, 8, 1}, {0, 4, 0}, {1, 1}, 4, 0}, 0) == 0x40000); +static_assert(CalculateLevelSize(LevelInfo{{128, 8, 1}, {0, 4, 0}, {1, 1}, 4, 0, 1}, 0) == 0x40000); static_assert(CalculateLevelOffset(PixelFormat::R8_SINT, {1920, 1080, 1}, {0, 2, 0}, 0, 7) == 0x2afc00); diff --git a/src/yuzu/CMakeLists.txt b/src/yuzu/CMakeLists.txt index 3c574a97d..064fac272 100755 --- a/src/yuzu/CMakeLists.txt +++ b/src/yuzu/CMakeLists.txt @@ -384,7 +384,7 @@ if (USE_DISCORD_PRESENCE) discord_impl.cpp discord_impl.h ) - target_link_libraries(yuzu PRIVATE DiscordRPC::discord-rpc httplib::httplib) + target_link_libraries(yuzu PRIVATE DiscordRPC::discord-rpc httplib::httplib Qt${QT_MAJOR_VERSION}::Network) target_compile_definitions(yuzu PRIVATE -DUSE_DISCORD_PRESENCE) endif() diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp index 1352704ed..7a2fbcf85 100755 --- a/src/yuzu/configuration/config.cpp +++ b/src/yuzu/configuration/config.cpp @@ -128,8 +128,8 @@ const std::array Config::default_hotkeys{{ {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Fullscreen")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("F11"), QStringLiteral("Home+B"), Qt::WindowShortcut, false}}, {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Load File")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+O"), QStringLiteral(""), Qt::WidgetWithChildrenShortcut, false}}, {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Load/Remove Amiibo")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("F2"), QStringLiteral("Home+A"), Qt::WidgetWithChildrenShortcut, false}}, - {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Restart Emulation")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("F6"), QStringLiteral(""), Qt::WindowShortcut, false}}, - {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Stop Emulation")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("F5"), QStringLiteral(""), Qt::WindowShortcut, false}}, + {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Restart Emulation")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("F6"), QStringLiteral("R+Plus+Minus"), Qt::WindowShortcut, false}}, + {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Stop Emulation")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("F5"), QStringLiteral("L+Plus+Minus"), Qt::WindowShortcut, false}}, {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "TAS Record")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+F7"), QStringLiteral(""), Qt::ApplicationShortcut, false}}, {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "TAS Reset")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+F6"), QStringLiteral(""), Qt::ApplicationShortcut, false}}, {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "TAS Start/Stop")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+F5"), QStringLiteral(""), Qt::ApplicationShortcut, false}}, diff --git a/src/yuzu/configuration/configure_input.cpp b/src/yuzu/configuration/configure_input.cpp index 28c6367da..628f76c49 100755 --- a/src/yuzu/configuration/configure_input.cpp +++ b/src/yuzu/configuration/configure_input.cpp @@ -115,17 +115,9 @@ void ConfigureInput::Initialize(InputCommon::InputSubsystem* input_subsystem, for (std::size_t i = 0; i < player_tabs.size(); ++i) { player_tabs[i]->setLayout(new QHBoxLayout(player_tabs[i])); player_tabs[i]->layout()->addWidget(player_controllers[i]); - connect(player_controllers[i], &ConfigureInputPlayer::Connected, [&, i](bool is_connected) { + connect(player_connected[i], &QCheckBox::clicked, [this, i](int checked) { // Ensures that the controllers are always connected in sequential order - if (is_connected) { - for (std::size_t index = 0; index <= i; ++index) { - player_connected[index]->setChecked(is_connected); - } - } else { - for (std::size_t index = i; index < player_tabs.size(); ++index) { - player_connected[index]->setChecked(is_connected); - } - } + this->propagateMouseClickOnPlayers(i, checked, true); }); connect(player_controllers[i], &ConfigureInputPlayer::RefreshInputDevices, this, &ConfigureInput::UpdateAllInputDevices); @@ -183,6 +175,30 @@ void ConfigureInput::Initialize(InputCommon::InputSubsystem* input_subsystem, LoadConfiguration(); } +void ConfigureInput::propagateMouseClickOnPlayers(size_t player_index, bool checked, bool origin) { + // Origin has already been toggled + if (!origin) { + player_connected[player_index]->setChecked(checked); + } + + if (checked) { + // Check all previous buttons when checked + if (player_index > 0) { + propagateMouseClickOnPlayers(player_index - 1, checked, false); + } + } else { + // Unchecked all following buttons when unchecked + if (player_index < player_tabs.size() - 1) { + // Reconnect current player if it was the last one checked + // (player number was reduced by more than one) + if (origin && player_connected[player_index + 1]->checkState() == Qt::Checked) { + player_connected[player_index]->setCheckState(Qt::Checked); + } + propagateMouseClickOnPlayers(player_index + 1, checked, false); + } + } +} + QList ConfigureInput::GetSubTabs() const { return { ui->tabPlayer1, ui->tabPlayer2, ui->tabPlayer3, ui->tabPlayer4, ui->tabPlayer5, diff --git a/src/yuzu/configuration/configure_input.h b/src/yuzu/configuration/configure_input.h index f919b764d..7d8fa2561 100755 --- a/src/yuzu/configuration/configure_input.h +++ b/src/yuzu/configuration/configure_input.h @@ -56,6 +56,7 @@ private: void UpdateDockedState(bool is_handheld); void UpdateAllInputDevices(); void UpdateAllInputProfiles(std::size_t player_index); + void propagateMouseClickOnPlayers(size_t player_index, bool origin, bool checked); /// Load configuration settings. void LoadConfiguration(); diff --git a/src/yuzu/configuration/shared_translation.cpp b/src/yuzu/configuration/shared_translation.cpp index a4e8af1b4..3fe448f27 100755 --- a/src/yuzu/configuration/shared_translation.cpp +++ b/src/yuzu/configuration/shared_translation.cpp @@ -157,6 +157,7 @@ std::unique_ptr InitializeTranslations(QWidget* parent) { INSERT(UISettings, select_user_on_boot, "Prompt for user on game boot", ""); INSERT(UISettings, pause_when_in_background, "Pause emulation when in background", ""); INSERT(UISettings, confirm_before_closing, "Confirm exit while emulation is running", ""); + INSERT(UISettings, confirm_before_stopping, "Confirm before stopping emulation", ""); INSERT(UISettings, hide_mouse, "Hide mouse on inactivity", ""); INSERT(UISettings, controller_applet_disabled, "Disable controller applet", ""); @@ -383,6 +384,13 @@ std::unique_ptr ComboboxEnumeration(QWidget* parent) { translations->insert( {Settings::EnumMetadata::Index(), {PAIR(ConsoleMode, Docked, "Docked"), PAIR(ConsoleMode, Handheld, "Handheld")}}); + translations->insert( + {Settings::EnumMetadata::Index(), + { + PAIR(ConfirmStop, Ask_Always, "Always ask (Default)"), + PAIR(ConfirmStop, Ask_Based_On_Game, "Only if game specifies not to stop"), + PAIR(ConfirmStop, Ask_Never, "Never ask"), + }}); #undef PAIR #undef CTX_PAIR diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index c62e2fd50..74c3fd8ff 100755 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp @@ -211,7 +211,7 @@ void GMainWindow::ShowTelemetryCallout() { tr("Anonymous " "data is collected to help improve yuzu. " "

Would you like to share your usage data with us?"); - if (QMessageBox::question(this, tr("Telemetry"), telemetry_message) != QMessageBox::Yes) { + if (!question(this, tr("Telemetry"), telemetry_message)) { Settings::values.enable_telemetry = false; system->ApplySettings(); } @@ -2420,9 +2420,8 @@ void GMainWindow::OnGameListRemoveInstalledEntry(u64 program_id, InstalledEntryT } }(); - if (QMessageBox::question(this, tr("Remove Entry"), entry_question, - QMessageBox::Yes | QMessageBox::No, - QMessageBox::No) != QMessageBox::Yes) { + if (!question(this, tr("Remove Entry"), entry_question, QMessageBox::Yes | QMessageBox::No, + QMessageBox::No)) { return; } @@ -2521,8 +2520,8 @@ void GMainWindow::OnGameListRemoveFile(u64 program_id, GameListRemoveTarget targ } }(); - if (QMessageBox::question(this, tr("Remove File"), question, QMessageBox::Yes | QMessageBox::No, - QMessageBox::No) != QMessageBox::Yes) { + if (!GMainWindow::question(this, tr("Remove File"), question, + QMessageBox::Yes | QMessageBox::No, QMessageBox::No)) { return; } @@ -3409,10 +3408,13 @@ void GMainWindow::OnRestartGame() { if (!system->IsPoweredOn()) { return; } - // Make a copy since ShutdownGame edits game_path - const auto current_game = QString(current_game_path); - ShutdownGame(); - BootGame(current_game); + + if (ConfirmShutdownGame()) { + // Make a copy since ShutdownGame edits game_path + const auto current_game = QString(current_game_path); + ShutdownGame(); + BootGame(current_game); + } } void GMainWindow::OnPauseGame() { @@ -3434,18 +3436,39 @@ void GMainWindow::OnPauseContinueGame() { } void GMainWindow::OnStopGame() { - if (system->GetExitLocked() && !ConfirmForceLockedExit()) { - return; + if (ConfirmShutdownGame()) { + play_time_manager->Stop(); + // Update game list to show new play time + game_list->PopulateAsync(UISettings::values.game_dirs); + if (OnShutdownBegin()) { + OnShutdownBeginDialog(); + } else { + OnEmulationStopped(); + } } +} - play_time_manager->Stop(); - // Update game list to show new play time - game_list->PopulateAsync(UISettings::values.game_dirs); - if (OnShutdownBegin()) { - OnShutdownBeginDialog(); +bool GMainWindow::ConfirmShutdownGame() { + if (UISettings::values.confirm_before_stopping.GetValue() == ConfirmStop::Ask_Always) { + if (system->GetExitLocked()) { + if (!ConfirmForceLockedExit()) { + return false; + } + } else { + if (!ConfirmChangeGame()) { + return false; + } + } } else { - OnEmulationStopped(); + if (UISettings::values.confirm_before_stopping.GetValue() == + ConfirmStop::Ask_Based_On_Game && + system->GetExitLocked()) { + if (!ConfirmForceLockedExit()) { + return false; + } + } } + return true; } void GMainWindow::OnLoadComplete() { @@ -3825,22 +3848,11 @@ void GMainWindow::OnTasRecord() { const bool is_recording = input_subsystem->GetTas()->Record(); if (!is_recording) { is_tas_recording_dialog_active = true; - ControllerNavigation* controller_navigation = - new ControllerNavigation(system->HIDCore(), this); - // Use QMessageBox instead of question so we can link controller navigation - QMessageBox* box_dialog = new QMessageBox(); - box_dialog->setWindowTitle(tr("TAS Recording")); - box_dialog->setText(tr("Overwrite file of player 1?")); - box_dialog->setStandardButtons(QMessageBox::Yes | QMessageBox::No); - box_dialog->setDefaultButton(QMessageBox::Yes); - connect(controller_navigation, &ControllerNavigation::TriggerKeyboardEvent, - [box_dialog](Qt::Key key) { - QKeyEvent* event = new QKeyEvent(QEvent::KeyPress, key, Qt::NoModifier); - QCoreApplication::postEvent(box_dialog, event); - }); - int res = box_dialog->exec(); - controller_navigation->UnloadController(); - input_subsystem->GetTas()->SaveRecording(res == QMessageBox::Yes); + + bool answer = question(this, tr("TAS Recording"), tr("Overwrite file of player 1?"), + QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes); + + input_subsystem->GetTas()->SaveRecording(answer); is_tas_recording_dialog_active = false; } OnTasStateChanged(); @@ -4081,6 +4093,29 @@ void GMainWindow::OnLoadAmiibo() { LoadAmiibo(filename); } +bool GMainWindow::question(QWidget* parent, const QString& title, const QString& text, + QMessageBox::StandardButtons buttons, + QMessageBox::StandardButton defaultButton) { + + QMessageBox* box_dialog = new QMessageBox(parent); + box_dialog->setWindowTitle(title); + box_dialog->setText(text); + box_dialog->setStandardButtons(buttons); + box_dialog->setDefaultButton(defaultButton); + + ControllerNavigation* controller_navigation = + new ControllerNavigation(system->HIDCore(), box_dialog); + connect(controller_navigation, &ControllerNavigation::TriggerKeyboardEvent, + [box_dialog](Qt::Key key) { + QKeyEvent* event = new QKeyEvent(QEvent::KeyPress, key, Qt::NoModifier); + QCoreApplication::postEvent(box_dialog, event); + }); + int res = box_dialog->exec(); + + controller_navigation->UnloadController(); + return res == QMessageBox::Yes; +} + void GMainWindow::LoadAmiibo(const QString& filename) { auto* virtual_amiibo = input_subsystem->GetVirtualAmiibo(); const QString title = tr("Error loading Amiibo data"); @@ -4814,8 +4849,7 @@ bool GMainWindow::ConfirmClose() { return true; } const auto text = tr("Are you sure you want to close yuzu?"); - const auto answer = QMessageBox::question(this, tr("yuzu"), text); - return answer != QMessageBox::No; + return question(this, tr("yuzu"), text); } void GMainWindow::closeEvent(QCloseEvent* event) { @@ -4908,11 +4942,11 @@ bool GMainWindow::ConfirmChangeGame() { if (emu_thread == nullptr) return true; - const auto answer = QMessageBox::question( + // Use custom question to link controller navigation + return question( this, tr("yuzu"), tr("Are you sure you want to stop the emulation? Any unsaved progress will be lost."), - QMessageBox::Yes | QMessageBox::No, QMessageBox::No); - return answer != QMessageBox::No; + QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes); } bool GMainWindow::ConfirmForceLockedExit() { @@ -4922,8 +4956,7 @@ bool GMainWindow::ConfirmForceLockedExit() { const auto text = tr("The currently running application has requested yuzu to not exit.\n\n" "Would you like to bypass this and exit anyway?"); - const auto answer = QMessageBox::question(this, tr("yuzu"), text); - return answer != QMessageBox::No; + return question(this, tr("yuzu"), text); } void GMainWindow::RequestGameExit() { diff --git a/src/yuzu/main.h b/src/yuzu/main.h index 1c926f3bf..b7c6bd97e 100755 --- a/src/yuzu/main.h +++ b/src/yuzu/main.h @@ -7,6 +7,7 @@ #include #include +#include #include #include @@ -15,6 +16,7 @@ #include "input_common/drivers/tas_input.h" #include "yuzu/compatibility_list.h" #include "yuzu/hotkeys.h" +#include "yuzu/util/controller_navigation.h" #ifdef __unix__ #include @@ -424,6 +426,11 @@ private: bool CheckSystemArchiveDecryption(); bool CheckFirmwarePresence(); void ConfigureFilesystemProvider(const std::string& filepath); + /** + * Open (or not) the right confirm dialog based on current setting and game exit lock + * @returns true if the player confirmed or the settings do no require it + */ + bool ConfirmShutdownGame(); QString GetTasStateDescription() const; bool CreateShortcut(const std::string& shortcut_path, const std::string& title, @@ -431,6 +438,17 @@ private: const std::string& command, const std::string& arguments, const std::string& categories, const std::string& keywords); + /** + * Mimic the behavior of QMessageBox::question but link controller navigation to the dialog + * The only difference is that it returns a boolean. + * + * @returns true if buttons contains QMessageBox::Yes and the user clicks on the "Yes" button. + */ + bool question(QWidget* parent, const QString& title, const QString& text, + QMessageBox::StandardButtons buttons = + QMessageBox::StandardButtons(QMessageBox::Yes | QMessageBox::No), + QMessageBox::StandardButton defaultButton = QMessageBox::NoButton); + std::unique_ptr ui; std::unique_ptr system; diff --git a/src/yuzu/uisettings.h b/src/yuzu/uisettings.h index c57271fed..06562b1c7 100755 --- a/src/yuzu/uisettings.h +++ b/src/yuzu/uisettings.h @@ -16,7 +16,9 @@ #include "common/settings_enums.h" using Settings::Category; +using Settings::ConfirmStop; using Settings::Setting; +using Settings::SwitchableSetting; #ifndef CANNOT_EXPLICITLY_INSTANTIATE namespace Settings { @@ -94,6 +96,15 @@ struct Values { Setting confirm_before_closing{ linkage, true, "confirmClose", Category::UiGeneral, Settings::Specialization::Default, true, true}; + + SwitchableSetting confirm_before_stopping{linkage, + ConfirmStop::Ask_Always, + "confirmStop", + Category::UiGeneral, + Settings::Specialization::Default, + true, + true}; + Setting first_start{linkage, true, "firstStart", Category::Ui}; Setting pause_when_in_background{linkage, false,