diff options
Diffstat (limited to '')
56 files changed, 576 insertions, 214 deletions
diff --git a/externals/find-modules/FindOpus.cmake b/externals/find-modules/FindOpus.cmake index 2ba515352..25a44fd87 100644 --- a/externals/find-modules/FindOpus.cmake +++ b/externals/find-modules/FindOpus.cmake @@ -2,9 +2,7 @@ # SPDX-License-Identifier: GPL-2.0-or-later find_package(PkgConfig QUIET) -if (PKG_CONFIG_FOUND) - pkg_search_module(OPUS QUIET IMPORTED_TARGET opus) -endif() +pkg_search_module(OPUS QUIET IMPORTED_TARGET opus) include(FindPackageHandleStandardArgs) find_package_handle_standard_args(Opus diff --git a/externals/find-modules/Findenet.cmake b/externals/find-modules/Findenet.cmake index 6dae76f4c..859a6f386 100644 --- a/externals/find-modules/Findenet.cmake +++ b/externals/find-modules/Findenet.cmake @@ -3,9 +3,7 @@ # SPDX-License-Identifier: GPL-3.0-or-later find_package(PkgConfig QUIET) -if (PKG_CONFIG_FOUND) - pkg_search_module(ENET QUIET IMPORTED_TARGET libenet) -endif() +pkg_search_module(ENET QUIET IMPORTED_TARGET libenet) include(FindPackageHandleStandardArgs) find_package_handle_standard_args(enet diff --git a/externals/find-modules/Findhttplib.cmake b/externals/find-modules/Findhttplib.cmake index b72bad076..4d17cb393 100644 --- a/externals/find-modules/Findhttplib.cmake +++ b/externals/find-modules/Findhttplib.cmake @@ -9,9 +9,7 @@ if (httplib_FOUND) find_package_handle_standard_args(httplib CONFIG_MODE) else() find_package(PkgConfig QUIET) - if (PKG_CONFIG_FOUND) - pkg_search_module(HTTPLIB QUIET IMPORTED_TARGET cpp-httplib) - endif() + pkg_search_module(HTTPLIB QUIET IMPORTED_TARGET cpp-httplib) find_package_handle_standard_args(httplib REQUIRED_VARS HTTPLIB_INCLUDEDIR VERSION_VAR HTTPLIB_VERSION diff --git a/externals/find-modules/Findinih.cmake b/externals/find-modules/Findinih.cmake index 8d1a07243..b8d38dcff 100644 --- a/externals/find-modules/Findinih.cmake +++ b/externals/find-modules/Findinih.cmake @@ -3,9 +3,7 @@ # SPDX-License-Identifier: GPL-3.0-or-later find_package(PkgConfig QUIET) -if (PKG_CONFIG_FOUND) - pkg_search_module(INIREADER QUIET IMPORTED_TARGET INIReader) -endif() +pkg_search_module(INIREADER QUIET IMPORTED_TARGET INIReader) include(FindPackageHandleStandardArgs) find_package_handle_standard_args(inih diff --git a/externals/find-modules/Findlibusb.cmake b/externals/find-modules/Findlibusb.cmake index 66f61001c..0eadce957 100644 --- a/externals/find-modules/Findlibusb.cmake +++ b/externals/find-modules/Findlibusb.cmake @@ -3,9 +3,7 @@ # SPDX-License-Identifier: GPL-3.0-or-later find_package(PkgConfig QUIET) -if (PKG_CONFIG_FOUND) - pkg_search_module(LIBUSB QUIET IMPORTED_TARGET libusb-1.0) -endif() +pkg_search_module(LIBUSB QUIET IMPORTED_TARGET libusb-1.0) include(FindPackageHandleStandardArgs) find_package_handle_standard_args(libusb diff --git a/externals/find-modules/Findlz4.cmake b/externals/find-modules/Findlz4.cmake index f4c7005ba..c82405c59 100644 --- a/externals/find-modules/Findlz4.cmake +++ b/externals/find-modules/Findlz4.cmake @@ -8,9 +8,7 @@ if (lz4_FOUND) find_package_handle_standard_args(lz4 CONFIG_MODE) else() find_package(PkgConfig QUIET) - if (PKG_CONFIG_FOUND) - pkg_search_module(LZ4 QUIET IMPORTED_TARGET liblz4) - endif() + pkg_search_module(LZ4 QUIET IMPORTED_TARGET liblz4) find_package_handle_standard_args(lz4 REQUIRED_VARS LZ4_LINK_LIBRARIES VERSION_VAR LZ4_VERSION diff --git a/externals/find-modules/Findzstd.cmake b/externals/find-modules/Findzstd.cmake index 1aacc41d0..f6eb9643a 100644 --- a/externals/find-modules/Findzstd.cmake +++ b/externals/find-modules/Findzstd.cmake @@ -8,9 +8,7 @@ if (zstd_FOUND) find_package_handle_standard_args(zstd CONFIG_MODE) else() find_package(PkgConfig QUIET) - if (PKG_CONFIG_FOUND) - pkg_search_module(ZSTD QUIET IMPORTED_TARGET libzstd) - endif() + pkg_search_module(ZSTD QUIET IMPORTED_TARGET libzstd) find_package_handle_standard_args(zstd REQUIRED_VARS ZSTD_LINK_LIBRARIES VERSION_VAR ZSTD_VERSION diff --git a/src/audio_core/device/audio_buffers.h b/src/audio_core/device/audio_buffers.h index 3dae1a3b7..15082f6c6 100644 --- a/src/audio_core/device/audio_buffers.h +++ b/src/audio_core/device/audio_buffers.h @@ -91,9 +91,10 @@ public: * @param core_timing - The CoreTiming instance * @param session - The device session * - * @return Is the buffer was released. + * @return If any buffer was released. */ - bool ReleaseBuffers(const Core::Timing::CoreTiming& core_timing, const DeviceSession& session) { + bool ReleaseBuffers(const Core::Timing::CoreTiming& core_timing, const DeviceSession& session, + bool force) { std::scoped_lock l{lock}; bool buffer_released{false}; while (registered_count > 0) { @@ -103,7 +104,8 @@ public: } // Check with the backend if this buffer can be released yet. - if (!session.IsBufferConsumed(buffers[index])) { + // If we're shutting down, we don't care if it's been played or not. + if (!force && !session.IsBufferConsumed(buffers[index])) { break; } diff --git a/src/audio_core/device/device_session.cpp b/src/audio_core/device/device_session.cpp index 995060414..5a327a606 100644 --- a/src/audio_core/device/device_session.cpp +++ b/src/audio_core/device/device_session.cpp @@ -73,6 +73,12 @@ void DeviceSession::Stop() { } } +void DeviceSession::ClearBuffers() { + if (stream) { + stream->ClearQueue(); + } +} + void DeviceSession::AppendBuffers(std::span<const AudioBuffer> buffers) const { for (const auto& buffer : buffers) { Sink::SinkBuffer new_buffer{ diff --git a/src/audio_core/device/device_session.h b/src/audio_core/device/device_session.h index 74f4dc085..75f766c68 100644 --- a/src/audio_core/device/device_session.h +++ b/src/audio_core/device/device_session.h @@ -91,6 +91,11 @@ public: void Stop(); /** + * Clear out the underlying audio buffers in the backend stream. + */ + void ClearBuffers(); + + /** * Set this device session's volume. * * @param volume - New volume for this session. diff --git a/src/audio_core/in/audio_in_system.cpp b/src/audio_core/in/audio_in_system.cpp index 4324cafd8..934ef8c1c 100644 --- a/src/audio_core/in/audio_in_system.cpp +++ b/src/audio_core/in/audio_in_system.cpp @@ -23,7 +23,6 @@ System::~System() { void System::Finalize() { Stop(); session->Finalize(); - buffer_event->Signal(); } void System::StartSession() { @@ -102,6 +101,10 @@ Result System::Stop() { if (state == State::Started) { session->Stop(); session->SetVolume(0.0f); + session->ClearBuffers(); + if (buffers.ReleaseBuffers(system.CoreTiming(), *session, true)) { + buffer_event->Signal(); + } state = State::Stopped; } @@ -138,7 +141,7 @@ void System::RegisterBuffers() { } void System::ReleaseBuffers() { - bool signal{buffers.ReleaseBuffers(system.CoreTiming(), *session)}; + bool signal{buffers.ReleaseBuffers(system.CoreTiming(), *session, false)}; if (signal) { // Signal if any buffer was released, or if none are registered, we need more. diff --git a/src/audio_core/out/audio_out_system.cpp b/src/audio_core/out/audio_out_system.cpp index a66208ed9..e096a1dac 100644 --- a/src/audio_core/out/audio_out_system.cpp +++ b/src/audio_core/out/audio_out_system.cpp @@ -24,7 +24,6 @@ System::~System() { void System::Finalize() { Stop(); session->Finalize(); - buffer_event->Signal(); } std::string_view System::GetDefaultOutputDeviceName() const { @@ -102,6 +101,10 @@ Result System::Stop() { if (state == State::Started) { session->Stop(); session->SetVolume(0.0f); + session->ClearBuffers(); + if (buffers.ReleaseBuffers(system.CoreTiming(), *session, true)) { + buffer_event->Signal(); + } state = State::Stopped; } @@ -138,7 +141,7 @@ void System::RegisterBuffers() { } void System::ReleaseBuffers() { - bool signal{buffers.ReleaseBuffers(system.CoreTiming(), *session)}; + bool signal{buffers.ReleaseBuffers(system.CoreTiming(), *session, false)}; if (signal) { // Signal if any buffer was released, or if none are registered, we need more. buffer_event->Signal(); diff --git a/src/common/settings.cpp b/src/common/settings.cpp index d8ffe34c3..149e621f9 100644 --- a/src/common/settings.cpp +++ b/src/common/settings.cpp @@ -40,6 +40,7 @@ void LogSettings() { LOG_INFO(Config, "yuzu Configuration:"); log_setting("Controls_UseDockedMode", values.use_docked_mode.GetValue()); log_setting("System_RngSeed", values.rng_seed.GetValue().value_or(0)); + log_setting("System_DeviceName", values.device_name.GetValue()); log_setting("System_CurrentUser", values.current_user.GetValue()); log_setting("System_LanguageIndex", values.language_index.GetValue()); log_setting("System_RegionIndex", values.region_index.GetValue()); diff --git a/src/common/settings.h b/src/common/settings.h index 7ce9ea23c..6b199af93 100644 --- a/src/common/settings.h +++ b/src/common/settings.h @@ -458,6 +458,7 @@ struct Values { // System SwitchableSetting<std::optional<u32>> rng_seed{std::optional<u32>(), "rng_seed"}; + Setting<std::string> device_name{"Yuzu", "device_name"}; // Measured in seconds since epoch std::optional<s64> custom_rtc; // Set on game boot, reset on stop. Seconds difference between current time and `custom_rtc` diff --git a/src/core/frontend/emu_window.h b/src/core/frontend/emu_window.h index 95363b645..cf85ba29e 100644 --- a/src/core/frontend/emu_window.h +++ b/src/core/frontend/emu_window.h @@ -131,6 +131,10 @@ public: return active_config; } + bool StrictContextRequired() const { + return strict_context_required; + } + /** * Requests the internal configuration to be replaced by the specified argument at some point in * the future. @@ -207,6 +211,8 @@ protected: WindowSystemInfo window_info; + bool strict_context_required = false; + private: /** * Handler called when the minimal client area was requested to be changed via SetConfig. diff --git a/src/core/hle/kernel/hle_ipc.cpp b/src/core/hle/kernel/hle_ipc.cpp index 06010b8d1..738b6d0f1 100644 --- a/src/core/hle/kernel/hle_ipc.cpp +++ b/src/core/hle/kernel/hle_ipc.cpp @@ -167,6 +167,9 @@ void HLERequestContext::ParseCommandBuffer(const KHandleTable& handle_table, u32 } if (incoming) { // Populate the object lists with the data in the IPC request. + incoming_copy_handles.reserve(handle_descriptor_header->num_handles_to_copy); + incoming_move_handles.reserve(handle_descriptor_header->num_handles_to_move); + for (u32 handle = 0; handle < handle_descriptor_header->num_handles_to_copy; ++handle) { incoming_copy_handles.push_back(rp.Pop<Handle>()); } @@ -181,6 +184,11 @@ void HLERequestContext::ParseCommandBuffer(const KHandleTable& handle_table, u32 } } + buffer_x_desciptors.reserve(command_header->num_buf_x_descriptors); + buffer_a_desciptors.reserve(command_header->num_buf_a_descriptors); + buffer_b_desciptors.reserve(command_header->num_buf_b_descriptors); + buffer_w_desciptors.reserve(command_header->num_buf_w_descriptors); + for (u32 i = 0; i < command_header->num_buf_x_descriptors; ++i) { buffer_x_desciptors.push_back(rp.PopRaw<IPC::BufferDescriptorX>()); } @@ -318,25 +326,23 @@ Result HLERequestContext::WriteToOutgoingCommandBuffer(KThread& requesting_threa } std::vector<u8> HLERequestContext::ReadBuffer(std::size_t buffer_index) const { - std::vector<u8> buffer{}; const bool is_buffer_a{BufferDescriptorA().size() > buffer_index && BufferDescriptorA()[buffer_index].Size()}; - if (is_buffer_a) { ASSERT_OR_EXECUTE_MSG( - BufferDescriptorA().size() > buffer_index, { return buffer; }, + BufferDescriptorA().size() > buffer_index, { return {}; }, "BufferDescriptorA invalid buffer_index {}", buffer_index); - buffer.resize(BufferDescriptorA()[buffer_index].Size()); + std::vector<u8> buffer(BufferDescriptorA()[buffer_index].Size()); memory.ReadBlock(BufferDescriptorA()[buffer_index].Address(), buffer.data(), buffer.size()); + return buffer; } else { ASSERT_OR_EXECUTE_MSG( - BufferDescriptorX().size() > buffer_index, { return buffer; }, + BufferDescriptorX().size() > buffer_index, { return {}; }, "BufferDescriptorX invalid buffer_index {}", buffer_index); - buffer.resize(BufferDescriptorX()[buffer_index].Size()); + std::vector<u8> buffer(BufferDescriptorX()[buffer_index].Size()); memory.ReadBlock(BufferDescriptorX()[buffer_index].Address(), buffer.data(), buffer.size()); + return buffer; } - - return buffer; } std::size_t HLERequestContext::WriteBuffer(const void* buffer, std::size_t size, diff --git a/src/core/hle/kernel/k_process.cpp b/src/core/hle/kernel/k_process.cpp index d1dc62401..a1abf5d68 100644 --- a/src/core/hle/kernel/k_process.cpp +++ b/src/core/hle/kernel/k_process.cpp @@ -285,6 +285,17 @@ void KProcess::UnregisterThread(KThread* thread) { thread_list.remove(thread); } +u64 KProcess::GetFreeThreadCount() const { + if (resource_limit != nullptr) { + const auto current_value = + resource_limit->GetCurrentValue(LimitableResource::ThreadCountMax); + const auto limit_value = resource_limit->GetLimitValue(LimitableResource::ThreadCountMax); + return limit_value - current_value; + } else { + return 0; + } +} + Result KProcess::Reset() { // Lock the process and the scheduler. KScopedLightLock lk(state_lock); diff --git a/src/core/hle/kernel/k_process.h b/src/core/hle/kernel/k_process.h index 2e0cc3d0b..09bf2f1d0 100644 --- a/src/core/hle/kernel/k_process.h +++ b/src/core/hle/kernel/k_process.h @@ -304,6 +304,9 @@ public: /// from this process' thread list. void UnregisterThread(KThread* thread); + /// Retrieves the number of available threads for this process. + u64 GetFreeThreadCount() const; + /// Clears the signaled state of the process if and only if it's signaled. /// /// @pre The process must not be already terminated. If this is called on a diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp index e520cab47..788ee2160 100644 --- a/src/core/hle/kernel/svc.cpp +++ b/src/core/hle/kernel/svc.cpp @@ -784,63 +784,29 @@ static Result GetInfo(Core::System& system, u64* result, u64 info_id, Handle han LOG_TRACE(Kernel_SVC, "called info_id=0x{:X}, info_sub_id=0x{:X}, handle=0x{:08X}", info_id, info_sub_id, handle); - enum class GetInfoType : u64 { - // 1.0.0+ - AllowedCPUCoreMask = 0, - AllowedThreadPriorityMask = 1, - MapRegionBaseAddr = 2, - MapRegionSize = 3, - HeapRegionBaseAddr = 4, - HeapRegionSize = 5, - TotalPhysicalMemoryAvailable = 6, - TotalPhysicalMemoryUsed = 7, - IsCurrentProcessBeingDebugged = 8, - RegisterResourceLimit = 9, - IdleTickCount = 10, - RandomEntropy = 11, - ThreadTickCount = 0xF0000002, - // 2.0.0+ - ASLRRegionBaseAddr = 12, - ASLRRegionSize = 13, - StackRegionBaseAddr = 14, - StackRegionSize = 15, - // 3.0.0+ - SystemResourceSize = 16, - SystemResourceUsage = 17, - TitleId = 18, - // 4.0.0+ - PrivilegedProcessId = 19, - // 5.0.0+ - UserExceptionContextAddr = 20, - // 6.0.0+ - TotalPhysicalMemoryAvailableWithoutSystemResource = 21, - TotalPhysicalMemoryUsedWithoutSystemResource = 22, - - // Homebrew only - MesosphereCurrentProcess = 65001, - }; - - const auto info_id_type = static_cast<GetInfoType>(info_id); + const auto info_id_type = static_cast<InfoType>(info_id); switch (info_id_type) { - case GetInfoType::AllowedCPUCoreMask: - case GetInfoType::AllowedThreadPriorityMask: - case GetInfoType::MapRegionBaseAddr: - case GetInfoType::MapRegionSize: - case GetInfoType::HeapRegionBaseAddr: - case GetInfoType::HeapRegionSize: - case GetInfoType::ASLRRegionBaseAddr: - case GetInfoType::ASLRRegionSize: - case GetInfoType::StackRegionBaseAddr: - case GetInfoType::StackRegionSize: - case GetInfoType::TotalPhysicalMemoryAvailable: - case GetInfoType::TotalPhysicalMemoryUsed: - case GetInfoType::SystemResourceSize: - case GetInfoType::SystemResourceUsage: - case GetInfoType::TitleId: - case GetInfoType::UserExceptionContextAddr: - case GetInfoType::TotalPhysicalMemoryAvailableWithoutSystemResource: - case GetInfoType::TotalPhysicalMemoryUsedWithoutSystemResource: { + case InfoType::CoreMask: + case InfoType::PriorityMask: + case InfoType::AliasRegionAddress: + case InfoType::AliasRegionSize: + case InfoType::HeapRegionAddress: + case InfoType::HeapRegionSize: + case InfoType::AslrRegionAddress: + case InfoType::AslrRegionSize: + case InfoType::StackRegionAddress: + case InfoType::StackRegionSize: + case InfoType::TotalMemorySize: + case InfoType::UsedMemorySize: + case InfoType::SystemResourceSizeTotal: + case InfoType::SystemResourceSizeUsed: + case InfoType::ProgramId: + case InfoType::UserExceptionContextAddress: + case InfoType::TotalNonSystemMemorySize: + case InfoType::UsedNonSystemMemorySize: + case InfoType::IsApplication: + case InfoType::FreeThreadCount: { if (info_sub_id != 0) { LOG_ERROR(Kernel_SVC, "Info sub id is non zero! info_id={}, info_sub_id={}", info_id, info_sub_id); @@ -856,79 +822,83 @@ static Result GetInfo(Core::System& system, u64* result, u64 info_id, Handle han } switch (info_id_type) { - case GetInfoType::AllowedCPUCoreMask: + case InfoType::CoreMask: *result = process->GetCoreMask(); return ResultSuccess; - case GetInfoType::AllowedThreadPriorityMask: + case InfoType::PriorityMask: *result = process->GetPriorityMask(); return ResultSuccess; - case GetInfoType::MapRegionBaseAddr: + case InfoType::AliasRegionAddress: *result = process->PageTable().GetAliasRegionStart(); return ResultSuccess; - case GetInfoType::MapRegionSize: + case InfoType::AliasRegionSize: *result = process->PageTable().GetAliasRegionSize(); return ResultSuccess; - case GetInfoType::HeapRegionBaseAddr: + case InfoType::HeapRegionAddress: *result = process->PageTable().GetHeapRegionStart(); return ResultSuccess; - case GetInfoType::HeapRegionSize: + case InfoType::HeapRegionSize: *result = process->PageTable().GetHeapRegionSize(); return ResultSuccess; - case GetInfoType::ASLRRegionBaseAddr: + case InfoType::AslrRegionAddress: *result = process->PageTable().GetAliasCodeRegionStart(); return ResultSuccess; - case GetInfoType::ASLRRegionSize: + case InfoType::AslrRegionSize: *result = process->PageTable().GetAliasCodeRegionSize(); return ResultSuccess; - case GetInfoType::StackRegionBaseAddr: + case InfoType::StackRegionAddress: *result = process->PageTable().GetStackRegionStart(); return ResultSuccess; - case GetInfoType::StackRegionSize: + case InfoType::StackRegionSize: *result = process->PageTable().GetStackRegionSize(); return ResultSuccess; - case GetInfoType::TotalPhysicalMemoryAvailable: + case InfoType::TotalMemorySize: *result = process->GetTotalPhysicalMemoryAvailable(); return ResultSuccess; - case GetInfoType::TotalPhysicalMemoryUsed: + case InfoType::UsedMemorySize: *result = process->GetTotalPhysicalMemoryUsed(); return ResultSuccess; - case GetInfoType::SystemResourceSize: + case InfoType::SystemResourceSizeTotal: *result = process->GetSystemResourceSize(); return ResultSuccess; - case GetInfoType::SystemResourceUsage: + case InfoType::SystemResourceSizeUsed: LOG_WARNING(Kernel_SVC, "(STUBBED) Attempted to query system resource usage"); *result = process->GetSystemResourceUsage(); return ResultSuccess; - case GetInfoType::TitleId: + case InfoType::ProgramId: *result = process->GetProgramID(); return ResultSuccess; - case GetInfoType::UserExceptionContextAddr: + case InfoType::UserExceptionContextAddress: *result = process->GetProcessLocalRegionAddress(); return ResultSuccess; - case GetInfoType::TotalPhysicalMemoryAvailableWithoutSystemResource: + case InfoType::TotalNonSystemMemorySize: *result = process->GetTotalPhysicalMemoryAvailableWithoutSystemResource(); return ResultSuccess; - case GetInfoType::TotalPhysicalMemoryUsedWithoutSystemResource: + case InfoType::UsedNonSystemMemorySize: *result = process->GetTotalPhysicalMemoryUsedWithoutSystemResource(); return ResultSuccess; + case InfoType::FreeThreadCount: + *result = process->GetFreeThreadCount(); + return ResultSuccess; + default: break; } @@ -937,11 +907,11 @@ static Result GetInfo(Core::System& system, u64* result, u64 info_id, Handle han return ResultInvalidEnumValue; } - case GetInfoType::IsCurrentProcessBeingDebugged: + case InfoType::DebuggerAttached: *result = 0; return ResultSuccess; - case GetInfoType::RegisterResourceLimit: { + case InfoType::ResourceLimit: { if (handle != 0) { LOG_ERROR(Kernel, "Handle is non zero! handle={:08X}", handle); return ResultInvalidHandle; @@ -969,7 +939,7 @@ static Result GetInfo(Core::System& system, u64* result, u64 info_id, Handle han return ResultSuccess; } - case GetInfoType::RandomEntropy: + case InfoType::RandomEntropy: if (handle != 0) { LOG_ERROR(Kernel_SVC, "Process Handle is non zero, expected 0 result but got {:016X}", handle); @@ -985,13 +955,13 @@ static Result GetInfo(Core::System& system, u64* result, u64 info_id, Handle han *result = system.Kernel().CurrentProcess()->GetRandomEntropy(info_sub_id); return ResultSuccess; - case GetInfoType::PrivilegedProcessId: + case InfoType::InitialProcessIdRange: LOG_WARNING(Kernel_SVC, "(STUBBED) Attempted to query privileged process id bounds, returned 0"); *result = 0; return ResultSuccess; - case GetInfoType::ThreadTickCount: { + case InfoType::ThreadTickCount: { constexpr u64 num_cpus = 4; if (info_sub_id != 0xFFFFFFFFFFFFFFFF && info_sub_id >= num_cpus) { LOG_ERROR(Kernel_SVC, "Core count is out of range, expected {} but got {}", num_cpus, @@ -1026,7 +996,7 @@ static Result GetInfo(Core::System& system, u64* result, u64 info_id, Handle han *result = out_ticks; return ResultSuccess; } - case GetInfoType::IdleTickCount: { + case InfoType::IdleTickCount: { // Verify the input handle is invalid. R_UNLESS(handle == InvalidHandle, ResultInvalidHandle); @@ -1040,7 +1010,7 @@ static Result GetInfo(Core::System& system, u64* result, u64 info_id, Handle han *result = system.Kernel().CurrentScheduler()->GetIdleThread()->GetCpuTime(); return ResultSuccess; } - case GetInfoType::MesosphereCurrentProcess: { + case InfoType::MesosphereCurrentProcess: { // Verify the input handle is invalid. R_UNLESS(handle == InvalidHandle, ResultInvalidHandle); diff --git a/src/core/hle/service/audio/audin_u.cpp b/src/core/hle/service/audio/audin_u.cpp index 26dec7147..053e8f9dd 100644 --- a/src/core/hle/service/audio/audin_u.cpp +++ b/src/core/hle/service/audio/audin_u.cpp @@ -203,8 +203,9 @@ private: }; AudInU::AudInU(Core::System& system_) - : ServiceFramework{system_, "audin:u"}, service_context{system_, "AudInU"}, - impl{std::make_unique<AudioCore::AudioIn::Manager>(system_)} { + : ServiceFramework{system_, "audin:u", ServiceThreadType::CreateNew}, + service_context{system_, "AudInU"}, impl{std::make_unique<AudioCore::AudioIn::Manager>( + system_)} { // clang-format off static const FunctionInfo functions[] = { {0, &AudInU::ListAudioIns, "ListAudioIns"}, diff --git a/src/core/hle/service/audio/audout_u.cpp b/src/core/hle/service/audio/audout_u.cpp index 991e30ba1..29751f075 100644 --- a/src/core/hle/service/audio/audout_u.cpp +++ b/src/core/hle/service/audio/audout_u.cpp @@ -26,8 +26,9 @@ public: explicit IAudioOut(Core::System& system_, AudioCore::AudioOut::Manager& manager, size_t session_id, const std::string& device_name, const AudioOutParameter& in_params, u32 handle, u64 applet_resource_user_id) - : ServiceFramework{system_, "IAudioOut"}, service_context{system_, "IAudioOut"}, - event{service_context.CreateEvent("AudioOutEvent")}, + : ServiceFramework{system_, "IAudioOut", ServiceThreadType::CreateNew}, + service_context{system_, "IAudioOut"}, event{service_context.CreateEvent( + "AudioOutEvent")}, impl{std::make_shared<AudioCore::AudioOut::Out>(system_, manager, event, session_id)} { // clang-format off @@ -220,8 +221,9 @@ private: }; AudOutU::AudOutU(Core::System& system_) - : ServiceFramework{system_, "audout:u"}, service_context{system_, "AudOutU"}, - impl{std::make_unique<AudioCore::AudioOut::Manager>(system_)} { + : ServiceFramework{system_, "audout:u", ServiceThreadType::CreateNew}, + service_context{system_, "AudOutU"}, impl{std::make_unique<AudioCore::AudioOut::Manager>( + system_)} { // clang-format off static const FunctionInfo functions[] = { {0, &AudOutU::ListAudioOuts, "ListAudioOuts"}, diff --git a/src/core/hle/service/audio/audren_u.cpp b/src/core/hle/service/audio/audren_u.cpp index ead16c321..3a1c231b6 100644 --- a/src/core/hle/service/audio/audren_u.cpp +++ b/src/core/hle/service/audio/audren_u.cpp @@ -35,9 +35,10 @@ public: AudioCore::AudioRendererParameterInternal& params, Kernel::KTransferMemory* transfer_memory, u64 transfer_memory_size, u32 process_handle, u64 applet_resource_user_id, s32 session_id) - : ServiceFramework{system_, "IAudioRenderer"}, service_context{system_, "IAudioRenderer"}, - rendered_event{service_context.CreateEvent("IAudioRendererEvent")}, manager{manager_}, - impl{std::make_unique<Renderer>(system_, manager, rendered_event)} { + : ServiceFramework{system_, "IAudioRenderer", ServiceThreadType::CreateNew}, + service_context{system_, "IAudioRenderer"}, rendered_event{service_context.CreateEvent( + "IAudioRendererEvent")}, + manager{manager_}, impl{std::make_unique<Renderer>(system_, manager, rendered_event)} { // clang-format off static const FunctionInfo functions[] = { {0, &IAudioRenderer::GetSampleRate, "GetSampleRate"}, @@ -242,8 +243,10 @@ class IAudioDevice final : public ServiceFramework<IAudioDevice> { public: explicit IAudioDevice(Core::System& system_, u64 applet_resource_user_id, u32 revision, u32 device_num) - : ServiceFramework{system_, "IAudioDevice"}, service_context{system_, "IAudioDevice"}, - impl{std::make_unique<AudioDevice>(system_, applet_resource_user_id, revision)}, + : ServiceFramework{system_, "IAudioDevice", ServiceThreadType::CreateNew}, + service_context{system_, "IAudioDevice"}, impl{std::make_unique<AudioDevice>( + system_, applet_resource_user_id, + revision)}, event{service_context.CreateEvent(fmt::format("IAudioDeviceEvent-{}", device_num))} { static const FunctionInfo functions[] = { {0, &IAudioDevice::ListAudioDeviceName, "ListAudioDeviceName"}, @@ -418,7 +421,7 @@ private: }; AudRenU::AudRenU(Core::System& system_) - : ServiceFramework{system_, "audren:u"}, + : ServiceFramework{system_, "audren:u", ServiceThreadType::CreateNew}, service_context{system_, "audren:u"}, impl{std::make_unique<Manager>(system_)} { // clang-format off static const FunctionInfo functions[] = { diff --git a/src/core/hle/service/set/set.cpp b/src/core/hle/service/set/set.cpp index 4f1a8d6b7..16c5eaf75 100644 --- a/src/core/hle/service/set/set.cpp +++ b/src/core/hle/service/set/set.cpp @@ -191,6 +191,13 @@ void SET::GetKeyCodeMap2(Kernel::HLERequestContext& ctx) { GetKeyCodeMapImpl(ctx); } +void SET::GetDeviceNickName(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_SET, "called"); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); + ctx.WriteBuffer(Settings::values.device_name.GetValue()); +} + SET::SET(Core::System& system_) : ServiceFramework{system_, "set"} { // clang-format off static const FunctionInfo functions[] = { @@ -205,7 +212,7 @@ SET::SET(Core::System& system_) : ServiceFramework{system_, "set"} { {8, &SET::GetQuestFlag, "GetQuestFlag"}, {9, &SET::GetKeyCodeMap2, "GetKeyCodeMap2"}, {10, nullptr, "GetFirmwareVersionForDebug"}, - {11, nullptr, "GetDeviceNickName"}, + {11, &SET::GetDeviceNickName, "GetDeviceNickName"}, }; // clang-format on diff --git a/src/core/hle/service/set/set.h b/src/core/hle/service/set/set.h index 60cad3e6f..375975711 100644 --- a/src/core/hle/service/set/set.h +++ b/src/core/hle/service/set/set.h @@ -50,6 +50,7 @@ private: void GetRegionCode(Kernel::HLERequestContext& ctx); void GetKeyCodeMap(Kernel::HLERequestContext& ctx); void GetKeyCodeMap2(Kernel::HLERequestContext& ctx); + void GetDeviceNickName(Kernel::HLERequestContext& ctx); }; } // namespace Service::Set diff --git a/src/core/hle/service/set/set_sys.cpp b/src/core/hle/service/set/set_sys.cpp index d7cea6aac..94c20edda 100644 --- a/src/core/hle/service/set/set_sys.cpp +++ b/src/core/hle/service/set/set_sys.cpp @@ -3,6 +3,7 @@ #include "common/assert.h" #include "common/logging/log.h" +#include "common/settings.h" #include "core/file_sys/errors.h" #include "core/file_sys/system_archive/system_version.h" #include "core/hle/ipc_helpers.h" @@ -176,6 +177,13 @@ void SET_SYS::GetSettingsItemValue(Kernel::HLERequestContext& ctx) { rb.Push(response); } +void SET_SYS::GetDeviceNickName(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_SET, "called"); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); + ctx.WriteBuffer(::Settings::values.device_name.GetValue()); +} + SET_SYS::SET_SYS(Core::System& system_) : ServiceFramework{system_, "set:sys"} { // clang-format off static const FunctionInfo functions[] = { @@ -253,7 +261,7 @@ SET_SYS::SET_SYS(Core::System& system_) : ServiceFramework{system_, "set:sys"} { {74, nullptr, "SetWirelessLanEnableFlag"}, {75, nullptr, "GetInitialLaunchSettings"}, {76, nullptr, "SetInitialLaunchSettings"}, - {77, nullptr, "GetDeviceNickName"}, + {77, &SET_SYS::GetDeviceNickName, "GetDeviceNickName"}, {78, nullptr, "SetDeviceNickName"}, {79, nullptr, "GetProductModel"}, {80, nullptr, "GetLdnChannel"}, diff --git a/src/core/hle/service/set/set_sys.h b/src/core/hle/service/set/set_sys.h index 258ef8c57..464ac3da1 100644 --- a/src/core/hle/service/set/set_sys.h +++ b/src/core/hle/service/set/set_sys.h @@ -29,6 +29,7 @@ private: void GetFirmwareVersion2(Kernel::HLERequestContext& ctx); void GetColorSetId(Kernel::HLERequestContext& ctx); void SetColorSetId(Kernel::HLERequestContext& ctx); + void GetDeviceNickName(Kernel::HLERequestContext& ctx); ColorSet color_set = ColorSet::BasicWhite; }; diff --git a/src/input_common/drivers/camera.cpp b/src/input_common/drivers/camera.cpp index dceea67e0..fad9177dc 100644 --- a/src/input_common/drivers/camera.cpp +++ b/src/input_common/drivers/camera.cpp @@ -17,7 +17,7 @@ Camera::Camera(std::string input_engine_) : InputEngine(std::move(input_engine_) PreSetController(identifier); } -void Camera::SetCameraData(std::size_t width, std::size_t height, std::vector<u32> data) { +void Camera::SetCameraData(std::size_t width, std::size_t height, std::span<const u32> data) { const std::size_t desired_width = getImageWidth(); const std::size_t desired_height = getImageHeight(); status.data.resize(desired_width * desired_height); diff --git a/src/input_common/drivers/camera.h b/src/input_common/drivers/camera.h index b8a7c75e5..38fb1ae4c 100644 --- a/src/input_common/drivers/camera.h +++ b/src/input_common/drivers/camera.h @@ -3,6 +3,8 @@ #pragma once +#include <span> + #include "input_common/input_engine.h" namespace InputCommon { @@ -15,7 +17,7 @@ class Camera final : public InputEngine { public: explicit Camera(std::string input_engine_); - void SetCameraData(std::size_t width, std::size_t height, std::vector<u32> data); + void SetCameraData(std::size_t width, std::size_t height, std::span<const u32> data); std::size_t getImageWidth() const; std::size_t getImageHeight() const; diff --git a/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp b/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp index 8e3e40cd5..41dc6d031 100644 --- a/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp +++ b/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp @@ -1345,8 +1345,10 @@ void EmitContext::DefineInputs(const IR::Program& program) { if (info.uses_fswzadd || info.uses_subgroup_invocation_id || info.uses_subgroup_shuffles || (profile.warp_size_potentially_larger_than_guest && (info.uses_subgroup_vote || info.uses_subgroup_mask))) { + AddCapability(spv::Capability::GroupNonUniform); subgroup_local_invocation_id = DefineInput(*this, U32[1], false, spv::BuiltIn::SubgroupLocalInvocationId); + Decorate(subgroup_local_invocation_id, spv::Decoration::Flat); } if (info.uses_fswzadd) { const Id f32_one{Const(1.0f)}; diff --git a/src/video_core/gpu.cpp b/src/video_core/gpu.cpp index 28b38273e..c6d54be63 100644 --- a/src/video_core/gpu.cpp +++ b/src/video_core/gpu.cpp @@ -223,8 +223,6 @@ struct GPU::Impl { /// core timing events. void Start() { gpu_thread.StartThread(*renderer, renderer->Context(), *scheduler); - cpu_context = renderer->GetRenderWindow().CreateSharedContext(); - cpu_context->MakeCurrent(); } void NotifyShutdown() { @@ -235,6 +233,9 @@ struct GPU::Impl { /// Obtain the CPU Context void ObtainContext() { + if (!cpu_context) { + cpu_context = renderer->GetRenderWindow().CreateSharedContext(); + } cpu_context->MakeCurrent(); } diff --git a/src/video_core/renderer_opengl/gl_device.cpp b/src/video_core/renderer_opengl/gl_device.cpp index e2e3dac34..cee5c3247 100644 --- a/src/video_core/renderer_opengl/gl_device.cpp +++ b/src/video_core/renderer_opengl/gl_device.cpp @@ -112,7 +112,7 @@ bool IsASTCSupported() { } } // Anonymous namespace -Device::Device() { +Device::Device(Core::Frontend::EmuWindow& emu_window) { if (!GLAD_GL_VERSION_4_6) { LOG_ERROR(Render_OpenGL, "OpenGL 4.6 is not available"); throw std::runtime_error{"Insufficient version"}; @@ -126,9 +126,9 @@ Device::Device() { const bool is_intel = vendor_name == "Intel"; #ifdef __unix__ - const bool is_linux = true; + constexpr bool is_linux = true; #else - const bool is_linux = false; + constexpr bool is_linux = false; #endif bool disable_fast_buffer_sub_data = false; @@ -193,9 +193,11 @@ Device::Device() { } } + strict_context_required = emu_window.StrictContextRequired(); // Blocks AMD and Intel OpenGL drivers on Windows from using asynchronous shader compilation. + // Blocks EGL on Wayland from using asynchronous shader compilation. use_asynchronous_shaders = Settings::values.use_asynchronous_shaders.GetValue() && - !(is_amd || (is_intel && !is_linux)); + !(is_amd || (is_intel && !is_linux)) && !strict_context_required; use_driver_cache = is_nvidia; LOG_INFO(Render_OpenGL, "Renderer_VariableAOFFI: {}", has_variable_aoffi); diff --git a/src/video_core/renderer_opengl/gl_device.h b/src/video_core/renderer_opengl/gl_device.h index 5ef51ebcf..2a72d84be 100644 --- a/src/video_core/renderer_opengl/gl_device.h +++ b/src/video_core/renderer_opengl/gl_device.h @@ -5,6 +5,7 @@ #include <cstddef> #include "common/common_types.h" +#include "core/frontend/emu_window.h" #include "shader_recompiler/stage.h" namespace Settings { @@ -15,7 +16,7 @@ namespace OpenGL { class Device { public: - explicit Device(); + explicit Device(Core::Frontend::EmuWindow& emu_window); [[nodiscard]] std::string GetVendorName() const; @@ -173,6 +174,10 @@ public: return can_report_memory; } + bool StrictContextRequired() const { + return strict_context_required; + } + private: static bool TestVariableAoffi(); static bool TestPreciseBug(); @@ -216,6 +221,7 @@ private: bool has_cbuf_ftou_bug{}; bool has_bool_ref_bug{}; bool can_report_memory{}; + bool strict_context_required{}; std::string vendor_name; }; diff --git a/src/video_core/renderer_opengl/gl_shader_cache.cpp b/src/video_core/renderer_opengl/gl_shader_cache.cpp index b12ecd478..f8868a012 100644 --- a/src/video_core/renderer_opengl/gl_shader_cache.cpp +++ b/src/video_core/renderer_opengl/gl_shader_cache.cpp @@ -174,6 +174,7 @@ ShaderCache::ShaderCache(RasterizerOpenGL& rasterizer_, Core::Frontend::EmuWindo texture_cache{texture_cache_}, buffer_cache{buffer_cache_}, program_manager{program_manager_}, state_tracker{state_tracker_}, shader_notify{shader_notify_}, use_asynchronous_shaders{device.UseAsynchronousShaders()}, + strict_context_required{device.StrictContextRequired()}, profile{ .supported_spirv = 0x00010000, @@ -256,9 +257,14 @@ void ShaderCache::LoadDiskResources(u64 title_id, std::stop_token stop_loading, } shader_cache_filename = base_dir / "opengl.bin"; - if (!workers) { + if (!workers && !strict_context_required) { workers = CreateWorkers(); } + std::optional<Context> strict_context; + if (strict_context_required) { + strict_context.emplace(emu_window); + } + struct { std::mutex mutex; size_t total{}; @@ -266,44 +272,49 @@ void ShaderCache::LoadDiskResources(u64 title_id, std::stop_token stop_loading, bool has_loaded{}; } state; + const auto queue_work{[&](Common::UniqueFunction<void, Context*>&& work) { + if (strict_context_required) { + work(&strict_context.value()); + } else { + workers->QueueWork(std::move(work)); + } + }}; const auto load_compute{[&](std::ifstream& file, FileEnvironment env) { ComputePipelineKey key; file.read(reinterpret_cast<char*>(&key), sizeof(key)); - workers->QueueWork( - [this, key, env = std::move(env), &state, &callback](Context* ctx) mutable { - ctx->pools.ReleaseContents(); - auto pipeline{CreateComputePipeline(ctx->pools, key, env)}; - std::scoped_lock lock{state.mutex}; - if (pipeline) { - compute_cache.emplace(key, std::move(pipeline)); - } - ++state.built; - if (state.has_loaded) { - callback(VideoCore::LoadCallbackStage::Build, state.built, state.total); - } - }); + queue_work([this, key, env = std::move(env), &state, &callback](Context* ctx) mutable { + ctx->pools.ReleaseContents(); + auto pipeline{CreateComputePipeline(ctx->pools, key, env)}; + std::scoped_lock lock{state.mutex}; + if (pipeline) { + compute_cache.emplace(key, std::move(pipeline)); + } + ++state.built; + if (state.has_loaded) { + callback(VideoCore::LoadCallbackStage::Build, state.built, state.total); + } + }); ++state.total; }}; const auto load_graphics{[&](std::ifstream& file, std::vector<FileEnvironment> envs) { GraphicsPipelineKey key; file.read(reinterpret_cast<char*>(&key), sizeof(key)); - workers->QueueWork( - [this, key, envs = std::move(envs), &state, &callback](Context* ctx) mutable { - boost::container::static_vector<Shader::Environment*, 5> env_ptrs; - for (auto& env : envs) { - env_ptrs.push_back(&env); - } - ctx->pools.ReleaseContents(); - auto pipeline{CreateGraphicsPipeline(ctx->pools, key, MakeSpan(env_ptrs), false)}; - std::scoped_lock lock{state.mutex}; - if (pipeline) { - graphics_cache.emplace(key, std::move(pipeline)); - } - ++state.built; - if (state.has_loaded) { - callback(VideoCore::LoadCallbackStage::Build, state.built, state.total); - } - }); + queue_work([this, key, envs = std::move(envs), &state, &callback](Context* ctx) mutable { + boost::container::static_vector<Shader::Environment*, 5> env_ptrs; + for (auto& env : envs) { + env_ptrs.push_back(&env); + } + ctx->pools.ReleaseContents(); + auto pipeline{CreateGraphicsPipeline(ctx->pools, key, MakeSpan(env_ptrs), false)}; + std::scoped_lock lock{state.mutex}; + if (pipeline) { + graphics_cache.emplace(key, std::move(pipeline)); + } + ++state.built; + if (state.has_loaded) { + callback(VideoCore::LoadCallbackStage::Build, state.built, state.total); + } + }); ++state.total; }}; LoadPipelines(stop_loading, shader_cache_filename, CACHE_VERSION, load_compute, load_graphics); @@ -315,6 +326,9 @@ void ShaderCache::LoadDiskResources(u64 title_id, std::stop_token stop_loading, state.has_loaded = true; lock.unlock(); + if (strict_context_required) { + return; + } workers->WaitForRequests(stop_loading); if (!use_asynchronous_shaders) { workers.reset(); diff --git a/src/video_core/renderer_opengl/gl_shader_cache.h b/src/video_core/renderer_opengl/gl_shader_cache.h index 53ffea904..f82420592 100644 --- a/src/video_core/renderer_opengl/gl_shader_cache.h +++ b/src/video_core/renderer_opengl/gl_shader_cache.h @@ -69,6 +69,7 @@ private: StateTracker& state_tracker; VideoCore::ShaderNotify& shader_notify; const bool use_asynchronous_shaders; + const bool strict_context_required; GraphicsPipelineKey graphics_key{}; GraphicsPipeline* current_pipeline{}; diff --git a/src/video_core/renderer_opengl/renderer_opengl.cpp b/src/video_core/renderer_opengl/renderer_opengl.cpp index 5b5e178ad..bc75680f0 100644 --- a/src/video_core/renderer_opengl/renderer_opengl.cpp +++ b/src/video_core/renderer_opengl/renderer_opengl.cpp @@ -140,8 +140,8 @@ RendererOpenGL::RendererOpenGL(Core::TelemetrySession& telemetry_session_, Core::Memory::Memory& cpu_memory_, Tegra::GPU& gpu_, std::unique_ptr<Core::Frontend::GraphicsContext> context_) : RendererBase{emu_window_, std::move(context_)}, telemetry_session{telemetry_session_}, - emu_window{emu_window_}, cpu_memory{cpu_memory_}, gpu{gpu_}, state_tracker{}, - program_manager{device}, + emu_window{emu_window_}, cpu_memory{cpu_memory_}, gpu{gpu_}, device{emu_window_}, + state_tracker{}, program_manager{device}, rasterizer(emu_window, gpu, cpu_memory, device, screen_info, program_manager, state_tracker) { if (Settings::values.renderer_debug && GLAD_GL_KHR_debug) { glEnable(GL_DEBUG_OUTPUT); diff --git a/src/video_core/renderer_vulkan/renderer_vulkan.cpp b/src/video_core/renderer_vulkan/renderer_vulkan.cpp index 18be54729..f502a7d09 100644 --- a/src/video_core/renderer_vulkan/renderer_vulkan.cpp +++ b/src/video_core/renderer_vulkan/renderer_vulkan.cpp @@ -139,23 +139,25 @@ void RendererVulkan::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) { RenderScreenshot(*framebuffer, use_accelerated); bool has_been_recreated = false; - const auto recreate_swapchain = [&] { + const auto recreate_swapchain = [&](u32 width, u32 height) { if (!has_been_recreated) { has_been_recreated = true; scheduler.Finish(); } - const Layout::FramebufferLayout layout = render_window.GetFramebufferLayout(); - swapchain.Create(layout.width, layout.height, is_srgb); + swapchain.Create(width, height, is_srgb); }; - if (swapchain.NeedsRecreation(is_srgb)) { - recreate_swapchain(); + + const Layout::FramebufferLayout layout = render_window.GetFramebufferLayout(); + if (swapchain.NeedsRecreation(is_srgb) || swapchain.GetWidth() != layout.width || + swapchain.GetHeight() != layout.height) { + recreate_swapchain(layout.width, layout.height); } bool is_outdated; do { swapchain.AcquireNextImage(); is_outdated = swapchain.IsOutDated(); if (is_outdated) { - recreate_swapchain(); + recreate_swapchain(layout.width, layout.height); } } while (is_outdated); if (has_been_recreated) { diff --git a/src/video_core/renderer_vulkan/vk_buffer_cache.cpp b/src/video_core/renderer_vulkan/vk_buffer_cache.cpp index 558b8db56..84d36fea6 100644 --- a/src/video_core/renderer_vulkan/vk_buffer_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_buffer_cache.cpp @@ -285,6 +285,9 @@ void BufferCacheRuntime::BindQuadArrayIndexBuffer(u32 first, u32 count) { void BufferCacheRuntime::BindVertexBuffer(u32 index, VkBuffer buffer, u32 offset, u32 size, u32 stride) { + if (index >= device.GetMaxVertexInputBindings()) { + return; + } if (device.IsExtExtendedDynamicStateSupported()) { scheduler.Record([index, buffer, offset, size, stride](vk::CommandBuffer cmdbuf) { const VkDeviceSize vk_offset = buffer != VK_NULL_HANDLE ? offset : 0; diff --git a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp index 00d7dbeea..515d8d869 100644 --- a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp +++ b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp @@ -529,7 +529,9 @@ void GraphicsPipeline::MakePipeline(VkRenderPass render_pass) { static_vector<VkVertexInputBindingDivisorDescriptionEXT, 32> vertex_binding_divisors; static_vector<VkVertexInputAttributeDescription, 32> vertex_attributes; if (key.state.dynamic_vertex_input) { - for (size_t index = 0; index < key.state.attributes.size(); ++index) { + const size_t num_vertex_arrays = std::min( + key.state.attributes.size(), static_cast<size_t>(device.GetMaxVertexInputBindings())); + for (size_t index = 0; index < num_vertex_arrays; ++index) { const u32 type = key.state.DynamicAttributeType(index); if (!stage_infos[0].loads.Generic(index) || type == 0) { continue; @@ -551,7 +553,9 @@ void GraphicsPipeline::MakePipeline(VkRenderPass render_pass) { }); } } else { - for (size_t index = 0; index < Maxwell::NumVertexArrays; ++index) { + const size_t num_vertex_arrays = std::min( + Maxwell::NumVertexArrays, static_cast<size_t>(device.GetMaxVertexInputBindings())); + for (size_t index = 0; index < num_vertex_arrays; ++index) { const bool instanced = key.state.binding_divisors[index] != 0; const auto rate = instanced ? VK_VERTEX_INPUT_RATE_INSTANCE : VK_VERTEX_INPUT_RATE_VERTEX; @@ -580,6 +584,8 @@ void GraphicsPipeline::MakePipeline(VkRenderPass render_pass) { }); } } + ASSERT(vertex_attributes.size() <= device.GetMaxVertexInputAttributes()); + VkPipelineVertexInputStateCreateInfo vertex_input_ci{ .sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO, .pNext = nullptr, diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp index 66c9f959a..e7262420c 100644 --- a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp @@ -342,6 +342,15 @@ PipelineCache::PipelineCache(RasterizerVulkan& rasterizer_, const Device& device .support_snorm_render_buffer = true, .support_viewport_index_layer = device.IsExtShaderViewportIndexLayerSupported(), }; + + if (device.GetMaxVertexInputAttributes() < Maxwell::NumVertexAttributes) { + LOG_WARNING(Render_Vulkan, "maxVertexInputAttributes is too low: {} < {}", + device.GetMaxVertexInputAttributes(), Maxwell::NumVertexAttributes); + } + if (device.GetMaxVertexInputBindings() < Maxwell::NumVertexArrays) { + LOG_WARNING(Render_Vulkan, "maxVertexInputBindings is too low: {} < {}", + device.GetMaxVertexInputBindings(), Maxwell::NumVertexArrays); + } } PipelineCache::~PipelineCache() = default; diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp index a8b82abae..4b7126c30 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp +++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp @@ -658,8 +658,7 @@ void RasterizerVulkan::BeginTransformFeedback() { return; } UNIMPLEMENTED_IF(regs.IsShaderConfigEnabled(Maxwell::ShaderType::TessellationInit) || - regs.IsShaderConfigEnabled(Maxwell::ShaderType::Tessellation) || - regs.IsShaderConfigEnabled(Maxwell::ShaderType::Geometry)); + regs.IsShaderConfigEnabled(Maxwell::ShaderType::Tessellation)); scheduler.Record( [](vk::CommandBuffer cmdbuf) { cmdbuf.BeginTransformFeedbackEXT(0, 0, nullptr, nullptr); }); } diff --git a/src/video_core/renderer_vulkan/vk_swapchain.cpp b/src/video_core/renderer_vulkan/vk_swapchain.cpp index d7be417f5..b6810eef9 100644 --- a/src/video_core/renderer_vulkan/vk_swapchain.cpp +++ b/src/video_core/renderer_vulkan/vk_swapchain.cpp @@ -67,17 +67,19 @@ VkExtent2D ChooseSwapExtent(const VkSurfaceCapabilitiesKHR& capabilities, u32 wi } // Anonymous namespace -Swapchain::Swapchain(VkSurfaceKHR surface_, const Device& device_, Scheduler& scheduler_, u32 width, - u32 height, bool srgb) +Swapchain::Swapchain(VkSurfaceKHR surface_, const Device& device_, Scheduler& scheduler_, + u32 width_, u32 height_, bool srgb) : surface{surface_}, device{device_}, scheduler{scheduler_} { - Create(width, height, srgb); + Create(width_, height_, srgb); } Swapchain::~Swapchain() = default; -void Swapchain::Create(u32 width, u32 height, bool srgb) { +void Swapchain::Create(u32 width_, u32 height_, bool srgb) { is_outdated = false; is_suboptimal = false; + width = width_; + height = height_; const auto physical_device = device.GetPhysical(); const auto capabilities{physical_device.GetSurfaceCapabilitiesKHR(surface)}; @@ -88,7 +90,7 @@ void Swapchain::Create(u32 width, u32 height, bool srgb) { device.GetLogical().WaitIdle(); Destroy(); - CreateSwapchain(capabilities, width, height, srgb); + CreateSwapchain(capabilities, srgb); CreateSemaphores(); CreateImageViews(); @@ -148,8 +150,7 @@ void Swapchain::Present(VkSemaphore render_semaphore) { } } -void Swapchain::CreateSwapchain(const VkSurfaceCapabilitiesKHR& capabilities, u32 width, u32 height, - bool srgb) { +void Swapchain::CreateSwapchain(const VkSurfaceCapabilitiesKHR& capabilities, bool srgb) { const auto physical_device{device.GetPhysical()}; const auto formats{physical_device.GetSurfaceFormatsKHR(surface)}; const auto present_modes{physical_device.GetSurfacePresentModesKHR(surface)}; diff --git a/src/video_core/renderer_vulkan/vk_swapchain.h b/src/video_core/renderer_vulkan/vk_swapchain.h index 111b3902d..caf1ff32b 100644 --- a/src/video_core/renderer_vulkan/vk_swapchain.h +++ b/src/video_core/renderer_vulkan/vk_swapchain.h @@ -80,9 +80,16 @@ public: return *present_semaphores[frame_index]; } + u32 GetWidth() const { + return width; + } + + u32 GetHeight() const { + return height; + } + private: - void CreateSwapchain(const VkSurfaceCapabilitiesKHR& capabilities, u32 width, u32 height, - bool srgb); + void CreateSwapchain(const VkSurfaceCapabilitiesKHR& capabilities, bool srgb); void CreateSemaphores(); void CreateImageViews(); @@ -105,6 +112,9 @@ private: std::vector<u64> resource_ticks; std::vector<vk::Semaphore> present_semaphores; + u32 width; + u32 height; + u32 image_index{}; u32 frame_index{}; diff --git a/src/video_core/vulkan_common/vulkan_device.cpp b/src/video_core/vulkan_common/vulkan_device.cpp index 3604c83c1..c4d31681a 100644 --- a/src/video_core/vulkan_common/vulkan_device.cpp +++ b/src/video_core/vulkan_common/vulkan_device.cpp @@ -421,7 +421,7 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR VkPhysicalDevice8BitStorageFeatures bit8_storage{ .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_8BIT_STORAGE_FEATURES, .pNext = nullptr, - .storageBuffer8BitAccess = false, + .storageBuffer8BitAccess = true, .uniformAndStorageBuffer8BitAccess = true, .storagePushConstant8 = false, }; @@ -1054,6 +1054,7 @@ void Device::CheckSuitability(bool requires_swapchain) const { std::make_pair(bit16_storage.storageBuffer16BitAccess, "storageBuffer16BitAccess"), std::make_pair(bit16_storage.uniformAndStorageBuffer16BitAccess, "uniformAndStorageBuffer16BitAccess"), + std::make_pair(bit8_storage.storageBuffer8BitAccess, "storageBuffer8BitAccess"), std::make_pair(bit8_storage.uniformAndStorageBuffer8BitAccess, "uniformAndStorageBuffer8BitAccess"), std::make_pair(host_query_reset.hostQueryReset, "hostQueryReset"), @@ -1405,6 +1406,10 @@ void Device::SetupFeatures() { is_shader_storage_image_multisample = features.shaderStorageImageMultisample; is_blit_depth_stencil_supported = TestDepthStencilBlits(); is_optimal_astc_supported = IsOptimalAstcSupported(features); + + const VkPhysicalDeviceLimits& limits{properties.limits}; + max_vertex_input_attributes = limits.maxVertexInputAttributes; + max_vertex_input_bindings = limits.maxVertexInputBindings; } void Device::SetupProperties() { diff --git a/src/video_core/vulkan_common/vulkan_device.h b/src/video_core/vulkan_common/vulkan_device.h index 3755275c3..6a26c4e6e 100644 --- a/src/video_core/vulkan_common/vulkan_device.h +++ b/src/video_core/vulkan_common/vulkan_device.h @@ -373,6 +373,14 @@ public: return must_emulate_bgr565; } + u32 GetMaxVertexInputAttributes() const { + return max_vertex_input_attributes; + } + + u32 GetMaxVertexInputBindings() const { + return max_vertex_input_bindings; + } + private: /// Checks if the physical device is suitable. void CheckSuitability(bool requires_swapchain) const; @@ -473,6 +481,8 @@ private: bool supports_d24_depth{}; ///< Supports D24 depth buffers. bool cant_blit_msaa{}; ///< Does not support MSAA<->MSAA blitting. bool must_emulate_bgr565{}; ///< Emulates BGR565 by swizzling RGB565 format. + u32 max_vertex_input_attributes{}; ///< Max vertex input attributes in pipeline + u32 max_vertex_input_bindings{}; ///< Max vertex input buffers in pipeline // Telemetry parameters std::string vendor_name; ///< Device's driver name. diff --git a/src/yuzu/bootmanager.cpp b/src/yuzu/bootmanager.cpp index 5b5b6fed8..682b37f47 100644 --- a/src/yuzu/bootmanager.cpp +++ b/src/yuzu/bootmanager.cpp @@ -44,6 +44,8 @@ #include "yuzu/bootmanager.h" #include "yuzu/main.h" +static Core::Frontend::WindowSystemType GetWindowSystemType(); + EmuThread::EmuThread(Core::System& system_) : system{system_} {} EmuThread::~EmuThread() = default; @@ -61,8 +63,6 @@ void EmuThread::run() { // Main process has been loaded. Make the context current to this thread and begin GPU and CPU // execution. - gpu.Start(); - gpu.ObtainContext(); emit LoadProgress(VideoCore::LoadCallbackStage::Prepare, 0, 0); @@ -77,9 +77,15 @@ void EmuThread::run() { emit LoadProgress(VideoCore::LoadCallbackStage::Complete, 0, 0); gpu.ReleaseContext(); + gpu.Start(); system.GetCpuManager().OnGpuReady(); + system.RegisterExitCallback([this]() { + stop_source.request_stop(); + SetRunning(false); + }); + // Holds whether the cpu was running during the last iteration, // so that the DebugModeLeft signal can be emitted before the // next execution step @@ -225,6 +231,9 @@ public: explicit RenderWidget(GRenderWindow* parent) : QWidget(parent), render_window(parent) { setAttribute(Qt::WA_NativeWindow); setAttribute(Qt::WA_PaintOnScreen); + if (GetWindowSystemType() == Core::Frontend::WindowSystemType::Wayland) { + setAttribute(Qt::WA_DontCreateNativeAncestors); + } } virtual ~RenderWidget() = default; @@ -269,12 +278,14 @@ static Core::Frontend::WindowSystemType GetWindowSystemType() { return Core::Frontend::WindowSystemType::X11; else if (platform_name == QStringLiteral("wayland")) return Core::Frontend::WindowSystemType::Wayland; + else if (platform_name == QStringLiteral("wayland-egl")) + return Core::Frontend::WindowSystemType::Wayland; else if (platform_name == QStringLiteral("cocoa")) return Core::Frontend::WindowSystemType::Cocoa; else if (platform_name == QStringLiteral("android")) return Core::Frontend::WindowSystemType::Android; - LOG_CRITICAL(Frontend, "Unknown Qt platform!"); + LOG_CRITICAL(Frontend, "Unknown Qt platform {}!", platform_name.toStdString()); return Core::Frontend::WindowSystemType::Windows; } @@ -314,6 +325,9 @@ GRenderWindow::GRenderWindow(GMainWindow* parent, EmuThread* emu_thread_, input_subsystem->Initialize(); this->setMouseTracking(true); + strict_context_required = QGuiApplication::platformName() == QStringLiteral("wayland") || + QGuiApplication::platformName() == QStringLiteral("wayland-egl"); + connect(this, &GRenderWindow::FirstFrameDisplayed, parent, &GMainWindow::OnLoadComplete); connect(this, &GRenderWindow::ExecuteProgramSignal, parent, &GMainWindow::OnExecuteProgram, Qt::QueuedConnection); @@ -750,6 +764,7 @@ void GRenderWindow::InitializeCamera() { return; } + camera_data.resize(CAMERA_WIDTH * CAMERA_HEIGHT); camera_capture->setCaptureDestination(QCameraImageCapture::CaptureDestination::CaptureToBuffer); connect(camera_capture.get(), &QCameraImageCapture::imageCaptured, this, &GRenderWindow::OnCameraCapture); @@ -805,16 +820,13 @@ void GRenderWindow::RequestCameraCapture() { } void GRenderWindow::OnCameraCapture(int requestId, const QImage& img) { - constexpr std::size_t camera_width = 320; - constexpr std::size_t camera_height = 240; + // TODO: Capture directly in the format and resolution needed const auto converted = - img.scaled(camera_width, camera_height, Qt::AspectRatioMode::IgnoreAspectRatio, + img.scaled(CAMERA_WIDTH, CAMERA_HEIGHT, Qt::AspectRatioMode::IgnoreAspectRatio, Qt::TransformationMode::SmoothTransformation) .mirrored(false, true); - std::vector<u32> camera_data{}; - camera_data.resize(camera_width * camera_height); - std::memcpy(camera_data.data(), converted.bits(), camera_width * camera_height * sizeof(u32)); - input_subsystem->GetCamera()->SetCameraData(camera_width, camera_height, camera_data); + std::memcpy(camera_data.data(), converted.bits(), CAMERA_WIDTH * CAMERA_HEIGHT * sizeof(u32)); + input_subsystem->GetCamera()->SetCameraData(CAMERA_WIDTH, CAMERA_HEIGHT, camera_data); pending_camera_snapshots = 0; } @@ -952,6 +964,12 @@ void GRenderWindow::OnMinimalClientAreaChangeRequest(std::pair<u32, u32> minimal bool GRenderWindow::InitializeOpenGL() { #ifdef HAS_OPENGL + if (!QOpenGLContext::supportsThreadedOpenGL()) { + QMessageBox::warning(this, tr("OpenGL not available!"), + tr("OpenGL shared contexts are not supported.")); + return false; + } + // TODO: One of these flags might be interesting: WA_OpaquePaintEvent, WA_NoBackground, // WA_DontShowOnScreen, WA_DeleteOnClose auto child = new OpenGLRenderWidget(this); diff --git a/src/yuzu/bootmanager.h b/src/yuzu/bootmanager.h index f4deae4ee..5bbcf61f7 100644 --- a/src/yuzu/bootmanager.h +++ b/src/yuzu/bootmanager.h @@ -84,9 +84,10 @@ public: } /** - * Requests for the emulation thread to stop running + * Requests for the emulation thread to immediately stop running */ - void RequestStop() { + void ForceStop() { + LOG_WARNING(Frontend, "Force stopping EmuThread"); stop_source.request_stop(); SetRunning(false); } @@ -246,6 +247,9 @@ private: #if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) && YUZU_USE_QT_MULTIMEDIA std::unique_ptr<QCamera> camera; std::unique_ptr<QCameraImageCapture> camera_capture; + static constexpr std::size_t CAMERA_WIDTH = 320; + static constexpr std::size_t CAMERA_HEIGHT = 240; + std::vector<u32> camera_data; #endif std::unique_ptr<QTimer> camera_timer; diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp index 90fb4b0a4..a8d47a2f9 100644 --- a/src/yuzu/configuration/config.cpp +++ b/src/yuzu/configuration/config.cpp @@ -783,6 +783,8 @@ void Config::ReadSystemValues() { } } + ReadBasicSetting(Settings::values.device_name); + if (global) { ReadBasicSetting(Settings::values.current_user); Settings::values.current_user = std::clamp<int>(Settings::values.current_user.GetValue(), 0, @@ -1405,6 +1407,7 @@ void Config::SaveSystemValues() { Settings::values.rng_seed.UsingGlobal()); WriteSetting(QStringLiteral("rng_seed"), Settings::values.rng_seed.GetValue(global).value_or(0), 0, Settings::values.rng_seed.UsingGlobal()); + WriteBasicSetting(Settings::values.device_name); if (global) { WriteBasicSetting(Settings::values.current_user); diff --git a/src/yuzu/configuration/configure_system.cpp b/src/yuzu/configuration/configure_system.cpp index bc9d9d77a..9b14e5903 100644 --- a/src/yuzu/configuration/configure_system.cpp +++ b/src/yuzu/configuration/configure_system.cpp @@ -72,6 +72,8 @@ void ConfigureSystem::SetConfiguration() { ui->custom_rtc_checkbox->setChecked(Settings::values.custom_rtc.has_value()); ui->custom_rtc_edit->setEnabled(Settings::values.custom_rtc.has_value()); ui->custom_rtc_edit->setDateTime(QDateTime::fromSecsSinceEpoch(rtc_time)); + ui->device_name_edit->setText( + QString::fromUtf8(Settings::values.device_name.GetValue().c_str())); if (Settings::IsConfiguringGlobal()) { ui->combo_language->setCurrentIndex(Settings::values.language_index.GetValue()); @@ -115,6 +117,8 @@ void ConfigureSystem::ApplyConfiguration() { } } + Settings::values.device_name = ui->device_name_edit->text().toStdString(); + if (!enabled) { return; } diff --git a/src/yuzu/configuration/configure_system.ui b/src/yuzu/configuration/configure_system.ui index b234ea87b..46892f5c1 100644 --- a/src/yuzu/configuration/configure_system.ui +++ b/src/yuzu/configuration/configure_system.ui @@ -432,6 +432,13 @@ </property> </widget> </item> + <item row="7" column="0"> + <widget class="QLabel" name="device_name_label"> + <property name="text"> + <string>Device Name</string> + </property> + </widget> + </item> <item row="3" column="1"> <widget class="QComboBox" name="combo_sound"> <item> @@ -476,6 +483,13 @@ </property> </widget> </item> + <item row="7" column="1"> + <widget class="QLineEdit" name="device_name_edit"> + <property name="maxLength"> + <number>128</number> + </property> + </widget> + </item> <item row="6" column="1"> <widget class="QLineEdit" name="rng_seed_edit"> <property name="sizePolicy"> diff --git a/src/yuzu/game_list.cpp b/src/yuzu/game_list.cpp index 5c33c1b0f..22aa19c56 100644 --- a/src/yuzu/game_list.cpp +++ b/src/yuzu/game_list.cpp @@ -554,6 +554,12 @@ void GameList::AddGamePopup(QMenu& context_menu, u64 program_id, const std::stri QAction* dump_romfs_sdmc = dump_romfs_menu->addAction(tr("Dump RomFS to SDMC")); QAction* copy_tid = context_menu.addAction(tr("Copy Title ID to Clipboard")); QAction* navigate_to_gamedb_entry = context_menu.addAction(tr("Navigate to GameDB entry")); +#ifndef WIN32 + QMenu* shortcut_menu = context_menu.addMenu(tr("Create Shortcut")); + QAction* create_desktop_shortcut = shortcut_menu->addAction(tr("Add to Desktop")); + QAction* create_applications_menu_shortcut = + shortcut_menu->addAction(tr("Add to Applications Menu")); +#endif context_menu.addSeparator(); QAction* properties = context_menu.addAction(tr("Properties")); @@ -619,6 +625,14 @@ void GameList::AddGamePopup(QMenu& context_menu, u64 program_id, const std::stri connect(navigate_to_gamedb_entry, &QAction::triggered, [this, program_id]() { emit NavigateToGamedbEntryRequested(program_id, compatibility_list); }); +#ifndef WIN32 + connect(create_desktop_shortcut, &QAction::triggered, [this, program_id, path]() { + emit CreateShortcut(program_id, path, GameListShortcutTarget::Desktop); + }); + connect(create_applications_menu_shortcut, &QAction::triggered, [this, program_id, path]() { + emit CreateShortcut(program_id, path, GameListShortcutTarget::Applications); + }); +#endif connect(properties, &QAction::triggered, [this, path]() { emit OpenPerGameGeneralRequested(path); }); }; diff --git a/src/yuzu/game_list.h b/src/yuzu/game_list.h index cdf085019..f7ff93ed9 100644 --- a/src/yuzu/game_list.h +++ b/src/yuzu/game_list.h @@ -52,6 +52,11 @@ enum class DumpRomFSTarget { SDMC, }; +enum class GameListShortcutTarget { + Desktop, + Applications, +}; + enum class InstalledEntryType { Game, Update, @@ -108,6 +113,8 @@ signals: const std::string& game_path); void DumpRomFSRequested(u64 program_id, const std::string& game_path, DumpRomFSTarget target); void CopyTIDRequested(u64 program_id); + void CreateShortcut(u64 program_id, const std::string& game_path, + GameListShortcutTarget target); void NavigateToGamedbEntryRequested(u64 program_id, const CompatibilityList& compatibility_list); void OpenPerGameGeneralRequested(const std::string& file); diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index 6c204416f..2e6c2311a 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp @@ -4,6 +4,8 @@ #include <cinttypes> #include <clocale> #include <cmath> +#include <fstream> +#include <iostream> #include <memory> #include <thread> #ifdef __APPLE__ @@ -1249,6 +1251,7 @@ void GMainWindow::ConnectWidgetEvents() { connect(game_list, &GameList::CopyTIDRequested, this, &GMainWindow::OnGameListCopyTID); connect(game_list, &GameList::NavigateToGamedbEntryRequested, this, &GMainWindow::OnGameListNavigateToGamedbEntry); + connect(game_list, &GameList::CreateShortcut, this, &GMainWindow::OnGameListCreateShortcut); connect(game_list, &GameList::AddDirectory, this, &GMainWindow::OnGameListAddDirectory); connect(game_list_placeholder, &GameListPlaceholder::AddDirectory, this, &GMainWindow::OnGameListAddDirectory); @@ -1707,9 +1710,6 @@ void GMainWindow::BootGame(const QString& filename, u64 program_id, std::size_t system->RegisterExecuteProgramCallback( [this](std::size_t program_index_) { render_window->ExecuteProgram(program_index_); }); - // Register an Exit callback such that Core can exit the currently running application. - system->RegisterExitCallback([this]() { render_window->Exit(); }); - connect(render_window, &GRenderWindow::Closed, this, &GMainWindow::OnStopGame); connect(render_window, &GRenderWindow::MouseActivity, this, &GMainWindow::OnMouseActivity); // BlockingQueuedConnection is important here, it makes sure we've finished refreshing our views @@ -1796,12 +1796,16 @@ void GMainWindow::ShutdownGame() { system->SetShuttingDown(true); system->DetachDebugger(); discord_rpc->Pause(); - emu_thread->RequestStop(); + + RequestGameExit(); emit EmulationStopping(); // Wait for emulation thread to complete and delete it - emu_thread->wait(); + if (!emu_thread->wait(5000)) { + emu_thread->ForceStop(); + emu_thread->wait(); + } emu_thread = nullptr; emulation_running = false; @@ -2378,6 +2382,152 @@ void GMainWindow::OnGameListNavigateToGamedbEntry(u64 program_id, QDesktopServices::openUrl(QUrl(QStringLiteral("https://yuzu-emu.org/game/") + directory)); } +void GMainWindow::OnGameListCreateShortcut(u64 program_id, const std::string& game_path, + GameListShortcutTarget target) { + // Get path to yuzu executable + const QStringList args = QApplication::arguments(); + std::filesystem::path yuzu_command = args[0].toStdString(); + +#if defined(__linux__) || defined(__FreeBSD__) + // If relative path, make it an absolute path + if (yuzu_command.c_str()[0] == '.') { + yuzu_command = Common::FS::GetCurrentDir() / yuzu_command; + } + +#if defined(__linux__) + // Warn once if we are making a shortcut to a volatile AppImage + const std::string appimage_ending = + std::string(Common::g_scm_rev).substr(0, 9).append(".AppImage"); + if (yuzu_command.string().ends_with(appimage_ending) && + !UISettings::values.shortcut_already_warned) { + if (QMessageBox::warning(this, tr("Create Shortcut"), + tr("This will create a shortcut to the current AppImage. This may " + "not work well if you update. Continue?"), + QMessageBox::StandardButton::Ok | + QMessageBox::StandardButton::Cancel) == + QMessageBox::StandardButton::Cancel) { + return; + } + UISettings::values.shortcut_already_warned = true; + } +#endif // __linux__ +#endif // __linux__ || __FreeBSD__ + + std::filesystem::path target_directory{}; + // Determine target directory for shortcut +#if defined(__linux__) || defined(__FreeBSD__) + const char* home = std::getenv("HOME"); + const std::filesystem::path home_path = (home == nullptr ? "~" : home); + const char* xdg_data_home = std::getenv("XDG_DATA_HOME"); + + if (target == GameListShortcutTarget::Desktop) { + target_directory = home_path / "Desktop"; + if (!Common::FS::IsDir(target_directory)) { + QMessageBox::critical( + this, tr("Create Shortcut"), + tr("Cannot create shortcut on desktop. Path \"%1\" does not exist.") + .arg(QString::fromStdString(target_directory)), + QMessageBox::StandardButton::Ok); + return; + } + } else if (target == GameListShortcutTarget::Applications) { + target_directory = (xdg_data_home == nullptr ? home_path / ".local/share" : xdg_data_home) / + "applications"; + if (!Common::FS::CreateDirs(target_directory)) { + QMessageBox::critical(this, tr("Create Shortcut"), + tr("Cannot create shortcut in applications menu. Path \"%1\" " + "does not exist and cannot be created.") + .arg(QString::fromStdString(target_directory)), + QMessageBox::StandardButton::Ok); + return; + } + } +#endif + + const std::string game_file_name = std::filesystem::path(game_path).filename().string(); + // Determine full paths for icon and shortcut +#if defined(__linux__) || defined(__FreeBSD__) + std::filesystem::path system_icons_path = + (xdg_data_home == nullptr ? home_path / ".local/share/" : xdg_data_home) / + "icons/hicolor/256x256"; + if (!Common::FS::CreateDirs(system_icons_path)) { + QMessageBox::critical( + this, tr("Create Icon"), + tr("Cannot create icon file. Path \"%1\" does not exist and cannot be created.") + .arg(QString::fromStdString(system_icons_path)), + QMessageBox::StandardButton::Ok); + return; + } + std::filesystem::path icon_path = + system_icons_path / (program_id == 0 ? fmt::format("yuzu-{}.png", game_file_name) + : fmt::format("yuzu-{:016X}.png", program_id)); + const std::filesystem::path shortcut_path = + target_directory / (program_id == 0 ? fmt::format("yuzu-{}.desktop", game_file_name) + : fmt::format("yuzu-{:016X}.desktop", program_id)); +#else + const std::filesystem::path icon_path{}; + const std::filesystem::path shortcut_path{}; +#endif + + // Get title from game file + const FileSys::PatchManager pm{program_id, system->GetFileSystemController(), + system->GetContentProvider()}; + const auto control = pm.GetControlMetadata(); + const auto loader = Loader::GetLoader(*system, vfs->OpenFile(game_path, FileSys::Mode::Read)); + + std::string title{fmt::format("{:016X}", program_id)}; + + if (control.first != nullptr) { + title = control.first->GetApplicationName(); + } else { + loader->ReadTitle(title); + } + + // Get icon from game file + std::vector<u8> icon_image_file{}; + if (control.second != nullptr) { + icon_image_file = control.second->ReadAllBytes(); + } else if (loader->ReadIcon(icon_image_file) != Loader::ResultStatus::Success) { + LOG_WARNING(Frontend, "Could not read icon from {:s}", game_path); + } + + QImage icon_jpeg = + QImage::fromData(icon_image_file.data(), static_cast<int>(icon_image_file.size())); +#if defined(__linux__) || defined(__FreeBSD__) + // Convert and write the icon as a PNG + if (!icon_jpeg.save(QString::fromStdString(icon_path.string()))) { + LOG_ERROR(Frontend, "Could not write icon as PNG to file"); + } else { + LOG_INFO(Frontend, "Wrote an icon to {}", icon_path.string()); + } +#endif // __linux__ + +#if defined(__linux__) || defined(__FreeBSD__) + const std::string comment = + tr("Start %1 with the yuzu Emulator").arg(QString::fromStdString(title)).toStdString(); + const std::string arguments = fmt::format("-g \"{:s}\"", game_path); + const std::string categories = "Game;Emulator;Qt;"; + const std::string keywords = "Switch;Nintendo;"; +#else + const std::string comment{}; + const std::string arguments{}; + const std::string categories{}; + const std::string keywords{}; +#endif + if (!CreateShortcut(shortcut_path.string(), title, comment, icon_path.string(), + yuzu_command.string(), arguments, categories, keywords)) { + QMessageBox::critical(this, tr("Create Shortcut"), + tr("Failed to create a shortcut at %1") + .arg(QString::fromStdString(shortcut_path.string()))); + return; + } + + LOG_INFO(Frontend, "Wrote a shortcut to {}", shortcut_path.string()); + QMessageBox::information( + this, tr("Create Shortcut"), + tr("Successfully created a shortcut to %1").arg(QString::fromStdString(title))); +} + void GMainWindow::OnGameListOpenDirectory(const QString& directory) { std::filesystem::path fs_path; if (directory == QStringLiteral("SDMC")) { @@ -2915,9 +3065,15 @@ static QScreen* GuessCurrentScreen(QWidget* window) { }); } +bool GMainWindow::UsingExclusiveFullscreen() { + return Settings::values.fullscreen_mode.GetValue() == Settings::FullscreenMode::Exclusive || + QGuiApplication::platformName() == QStringLiteral("wayland") || + QGuiApplication::platformName() == QStringLiteral("wayland-egl"); +} + void GMainWindow::ShowFullscreen() { - const auto show_fullscreen = [](QWidget* window) { - if (Settings::values.fullscreen_mode.GetValue() == Settings::FullscreenMode::Exclusive) { + const auto show_fullscreen = [this](QWidget* window) { + if (UsingExclusiveFullscreen()) { window->showFullScreen(); return; } @@ -2945,7 +3101,7 @@ void GMainWindow::ShowFullscreen() { void GMainWindow::HideFullscreen() { if (ui->action_Single_Window_Mode->isChecked()) { - if (Settings::values.fullscreen_mode.GetValue() == Settings::FullscreenMode::Exclusive) { + if (UsingExclusiveFullscreen()) { showNormal(); restoreGeometry(UISettings::values.geometry); } else { @@ -2959,7 +3115,7 @@ void GMainWindow::HideFullscreen() { statusBar()->setVisible(ui->action_Show_Status_Bar->isChecked()); ui->menubar->show(); } else { - if (Settings::values.fullscreen_mode.GetValue() == Settings::FullscreenMode::Exclusive) { + if (UsingExclusiveFullscreen()) { render_window->showNormal(); render_window->restoreGeometry(UISettings::values.renderwindow_geometry); } else { @@ -3296,6 +3452,38 @@ void GMainWindow::OpenPerGameConfiguration(u64 title_id, const std::string& file } } +bool GMainWindow::CreateShortcut(const std::string& shortcut_path, const std::string& title, + const std::string& comment, const std::string& icon_path, + const std::string& command, const std::string& arguments, + const std::string& categories, const std::string& keywords) { +#if defined(__linux__) || defined(__FreeBSD__) + // This desktop file template was writting referencing + // https://specifications.freedesktop.org/desktop-entry-spec/desktop-entry-spec-1.0.html + std::string shortcut_contents{}; + shortcut_contents.append("[Desktop Entry]\n"); + shortcut_contents.append("Type=Application\n"); + shortcut_contents.append("Version=1.0\n"); + shortcut_contents.append(fmt::format("Name={:s}\n", title)); + shortcut_contents.append(fmt::format("Comment={:s}\n", comment)); + shortcut_contents.append(fmt::format("Icon={:s}\n", icon_path)); + shortcut_contents.append(fmt::format("TryExec={:s}\n", command)); + shortcut_contents.append(fmt::format("Exec={:s} {:s}\n", command, arguments)); + shortcut_contents.append(fmt::format("Categories={:s}\n", categories)); + shortcut_contents.append(fmt::format("Keywords={:s}\n", keywords)); + + std::ofstream shortcut_stream(shortcut_path); + if (!shortcut_stream.is_open()) { + LOG_WARNING(Common, "Failed to create file {:s}", shortcut_path); + return false; + } + shortcut_stream << shortcut_contents; + shortcut_stream.close(); + + return true; +#endif + return false; +} + void GMainWindow::OnLoadAmiibo() { if (emu_thread == nullptr || !emu_thread->IsRunning()) { return; diff --git a/src/yuzu/main.h b/src/yuzu/main.h index 62d629973..1047ba276 100644 --- a/src/yuzu/main.h +++ b/src/yuzu/main.h @@ -38,6 +38,7 @@ class QProgressDialog; class WaitTreeWidget; enum class GameListOpenTarget; enum class GameListRemoveTarget; +enum class GameListShortcutTarget; enum class DumpRomFSTarget; enum class InstalledEntryType; class GameListPlaceholder; @@ -293,6 +294,8 @@ private slots: void OnGameListCopyTID(u64 program_id); void OnGameListNavigateToGamedbEntry(u64 program_id, const CompatibilityList& compatibility_list); + void OnGameListCreateShortcut(u64 program_id, const std::string& game_path, + GameListShortcutTarget target); void OnGameListOpenDirectory(const QString& directory); void OnGameListAddDirectory(); void OnGameListShowList(bool show); @@ -320,6 +323,7 @@ private slots: void OnDisplayTitleBars(bool); void InitializeHotkeys(); void ToggleFullscreen(); + bool UsingExclusiveFullscreen(); void ShowFullscreen(); void HideFullscreen(); void ToggleWindowMode(); @@ -365,6 +369,10 @@ private: bool CheckDarkMode(); QString GetTasStateDescription() const; + bool CreateShortcut(const std::string& shortcut_path, const std::string& title, + const std::string& comment, const std::string& icon_path, + const std::string& command, const std::string& arguments, + const std::string& categories, const std::string& keywords); std::unique_ptr<Ui::MainWindow> ui; diff --git a/src/yuzu/uisettings.h b/src/yuzu/uisettings.h index 452038cd9..2006b883e 100644 --- a/src/yuzu/uisettings.h +++ b/src/yuzu/uisettings.h @@ -138,6 +138,7 @@ struct Values { bool configuration_applied; bool reset_to_defaults; + bool shortcut_already_warned{false}; Settings::Setting<bool> disable_web_applet{true, "disable_web_applet"}; }; diff --git a/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp b/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp index 37dd1747c..31f28a507 100644 --- a/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp +++ b/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp @@ -115,7 +115,7 @@ bool EmuWindow_SDL2::IsShown() const { void EmuWindow_SDL2::OnResize() { int width, height; - SDL_GetWindowSize(render_window, &width, &height); + SDL_GL_GetDrawableSize(render_window, &width, &height); UpdateCurrentFramebufferLayout(width, height); } diff --git a/src/yuzu_cmd/emu_window/emu_window_sdl2_gl.cpp b/src/yuzu_cmd/emu_window/emu_window_sdl2_gl.cpp index 9b660c13c..ddcb048d6 100644 --- a/src/yuzu_cmd/emu_window/emu_window_sdl2_gl.cpp +++ b/src/yuzu_cmd/emu_window/emu_window_sdl2_gl.cpp @@ -104,6 +104,8 @@ EmuWindow_SDL2_GL::EmuWindow_SDL2_GL(InputCommon::InputSubsystem* input_subsyste exit(1); } + strict_context_required = strcmp(SDL_GetCurrentVideoDriver(), "wayland") == 0; + SetWindowIcon(); if (fullscreen) { |