summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--externals/find-modules/FindOpus.cmake4
-rw-r--r--externals/find-modules/Findenet.cmake4
-rw-r--r--externals/find-modules/Findhttplib.cmake4
-rw-r--r--externals/find-modules/Findinih.cmake4
-rw-r--r--externals/find-modules/Findlibusb.cmake4
-rw-r--r--externals/find-modules/Findlz4.cmake4
-rw-r--r--externals/find-modules/Findzstd.cmake4
-rw-r--r--src/audio_core/device/audio_buffers.h8
-rw-r--r--src/audio_core/device/device_session.cpp6
-rw-r--r--src/audio_core/device/device_session.h5
-rw-r--r--src/audio_core/in/audio_in_system.cpp7
-rw-r--r--src/audio_core/out/audio_out_system.cpp7
-rw-r--r--src/common/settings.cpp1
-rw-r--r--src/common/settings.h1
-rw-r--r--src/core/frontend/emu_window.h6
-rw-r--r--src/core/hle/kernel/hle_ipc.cpp22
-rw-r--r--src/core/hle/kernel/k_process.cpp11
-rw-r--r--src/core/hle/kernel/k_process.h3
-rw-r--r--src/core/hle/kernel/svc.cpp130
-rw-r--r--src/core/hle/service/audio/audin_u.cpp5
-rw-r--r--src/core/hle/service/audio/audout_u.cpp10
-rw-r--r--src/core/hle/service/audio/audren_u.cpp15
-rw-r--r--src/core/hle/service/set/set.cpp9
-rw-r--r--src/core/hle/service/set/set.h1
-rw-r--r--src/core/hle/service/set/set_sys.cpp10
-rw-r--r--src/core/hle/service/set/set_sys.h1
-rw-r--r--src/input_common/drivers/camera.cpp2
-rw-r--r--src/input_common/drivers/camera.h4
-rw-r--r--src/shader_recompiler/backend/spirv/spirv_emit_context.cpp2
-rw-r--r--src/video_core/gpu.cpp5
-rw-r--r--src/video_core/renderer_opengl/gl_device.cpp10
-rw-r--r--src/video_core/renderer_opengl/gl_device.h8
-rw-r--r--src/video_core/renderer_opengl/gl_shader_cache.cpp76
-rw-r--r--src/video_core/renderer_opengl/gl_shader_cache.h1
-rw-r--r--src/video_core/renderer_opengl/renderer_opengl.cpp4
-rw-r--r--src/video_core/renderer_vulkan/renderer_vulkan.cpp14
-rw-r--r--src/video_core/renderer_vulkan/vk_buffer_cache.cpp3
-rw-r--r--src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp10
-rw-r--r--src/video_core/renderer_vulkan/vk_pipeline_cache.cpp9
-rw-r--r--src/video_core/renderer_vulkan/vk_rasterizer.cpp3
-rw-r--r--src/video_core/renderer_vulkan/vk_swapchain.cpp15
-rw-r--r--src/video_core/renderer_vulkan/vk_swapchain.h14
-rw-r--r--src/video_core/vulkan_common/vulkan_device.cpp7
-rw-r--r--src/video_core/vulkan_common/vulkan_device.h10
-rw-r--r--src/yuzu/bootmanager.cpp38
-rw-r--r--src/yuzu/bootmanager.h8
-rw-r--r--src/yuzu/configuration/config.cpp3
-rw-r--r--src/yuzu/configuration/configure_system.cpp4
-rw-r--r--src/yuzu/configuration/configure_system.ui14
-rw-r--r--src/yuzu/game_list.cpp14
-rw-r--r--src/yuzu/game_list.h7
-rw-r--r--src/yuzu/main.cpp206
-rw-r--r--src/yuzu/main.h8
-rw-r--r--src/yuzu/uisettings.h1
-rw-r--r--src/yuzu_cmd/emu_window/emu_window_sdl2.cpp2
-rw-r--r--src/yuzu_cmd/emu_window/emu_window_sdl2_gl.cpp2
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) {