diff options
35 files changed, 397 insertions, 100 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 @@ | |||
| 2 | # SPDX-License-Identifier: GPL-2.0-or-later | 2 | # SPDX-License-Identifier: GPL-2.0-or-later |
| 3 | 3 | ||
| 4 | find_package(PkgConfig QUIET) | 4 | find_package(PkgConfig QUIET) |
| 5 | if (PKG_CONFIG_FOUND) | 5 | pkg_search_module(OPUS QUIET IMPORTED_TARGET opus) |
| 6 | pkg_search_module(OPUS QUIET IMPORTED_TARGET opus) | ||
| 7 | endif() | ||
| 8 | 6 | ||
| 9 | include(FindPackageHandleStandardArgs) | 7 | include(FindPackageHandleStandardArgs) |
| 10 | find_package_handle_standard_args(Opus | 8 | 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 @@ | |||
| 3 | # SPDX-License-Identifier: GPL-3.0-or-later | 3 | # SPDX-License-Identifier: GPL-3.0-or-later |
| 4 | 4 | ||
| 5 | find_package(PkgConfig QUIET) | 5 | find_package(PkgConfig QUIET) |
| 6 | if (PKG_CONFIG_FOUND) | 6 | pkg_search_module(ENET QUIET IMPORTED_TARGET libenet) |
| 7 | pkg_search_module(ENET QUIET IMPORTED_TARGET libenet) | ||
| 8 | endif() | ||
| 9 | 7 | ||
| 10 | include(FindPackageHandleStandardArgs) | 8 | include(FindPackageHandleStandardArgs) |
| 11 | find_package_handle_standard_args(enet | 9 | 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) | |||
| 9 | find_package_handle_standard_args(httplib CONFIG_MODE) | 9 | find_package_handle_standard_args(httplib CONFIG_MODE) |
| 10 | else() | 10 | else() |
| 11 | find_package(PkgConfig QUIET) | 11 | find_package(PkgConfig QUIET) |
| 12 | if (PKG_CONFIG_FOUND) | 12 | pkg_search_module(HTTPLIB QUIET IMPORTED_TARGET cpp-httplib) |
| 13 | pkg_search_module(HTTPLIB QUIET IMPORTED_TARGET cpp-httplib) | ||
| 14 | endif() | ||
| 15 | find_package_handle_standard_args(httplib | 13 | find_package_handle_standard_args(httplib |
| 16 | REQUIRED_VARS HTTPLIB_INCLUDEDIR | 14 | REQUIRED_VARS HTTPLIB_INCLUDEDIR |
| 17 | VERSION_VAR HTTPLIB_VERSION | 15 | 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 @@ | |||
| 3 | # SPDX-License-Identifier: GPL-3.0-or-later | 3 | # SPDX-License-Identifier: GPL-3.0-or-later |
| 4 | 4 | ||
| 5 | find_package(PkgConfig QUIET) | 5 | find_package(PkgConfig QUIET) |
| 6 | if (PKG_CONFIG_FOUND) | 6 | pkg_search_module(INIREADER QUIET IMPORTED_TARGET INIReader) |
| 7 | pkg_search_module(INIREADER QUIET IMPORTED_TARGET INIReader) | ||
| 8 | endif() | ||
| 9 | 7 | ||
| 10 | include(FindPackageHandleStandardArgs) | 8 | include(FindPackageHandleStandardArgs) |
| 11 | find_package_handle_standard_args(inih | 9 | 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 @@ | |||
| 3 | # SPDX-License-Identifier: GPL-3.0-or-later | 3 | # SPDX-License-Identifier: GPL-3.0-or-later |
| 4 | 4 | ||
| 5 | find_package(PkgConfig QUIET) | 5 | find_package(PkgConfig QUIET) |
| 6 | if (PKG_CONFIG_FOUND) | 6 | pkg_search_module(LIBUSB QUIET IMPORTED_TARGET libusb-1.0) |
| 7 | pkg_search_module(LIBUSB QUIET IMPORTED_TARGET libusb-1.0) | ||
| 8 | endif() | ||
| 9 | 7 | ||
| 10 | include(FindPackageHandleStandardArgs) | 8 | include(FindPackageHandleStandardArgs) |
| 11 | find_package_handle_standard_args(libusb | 9 | 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) | |||
| 8 | find_package_handle_standard_args(lz4 CONFIG_MODE) | 8 | find_package_handle_standard_args(lz4 CONFIG_MODE) |
| 9 | else() | 9 | else() |
| 10 | find_package(PkgConfig QUIET) | 10 | find_package(PkgConfig QUIET) |
| 11 | if (PKG_CONFIG_FOUND) | 11 | pkg_search_module(LZ4 QUIET IMPORTED_TARGET liblz4) |
| 12 | pkg_search_module(LZ4 QUIET IMPORTED_TARGET liblz4) | ||
| 13 | endif() | ||
| 14 | find_package_handle_standard_args(lz4 | 12 | find_package_handle_standard_args(lz4 |
| 15 | REQUIRED_VARS LZ4_LINK_LIBRARIES | 13 | REQUIRED_VARS LZ4_LINK_LIBRARIES |
| 16 | VERSION_VAR LZ4_VERSION | 14 | 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) | |||
| 8 | find_package_handle_standard_args(zstd CONFIG_MODE) | 8 | find_package_handle_standard_args(zstd CONFIG_MODE) |
| 9 | else() | 9 | else() |
| 10 | find_package(PkgConfig QUIET) | 10 | find_package(PkgConfig QUIET) |
| 11 | if (PKG_CONFIG_FOUND) | 11 | pkg_search_module(ZSTD QUIET IMPORTED_TARGET libzstd) |
| 12 | pkg_search_module(ZSTD QUIET IMPORTED_TARGET libzstd) | ||
| 13 | endif() | ||
| 14 | find_package_handle_standard_args(zstd | 12 | find_package_handle_standard_args(zstd |
| 15 | REQUIRED_VARS ZSTD_LINK_LIBRARIES | 13 | REQUIRED_VARS ZSTD_LINK_LIBRARIES |
| 16 | VERSION_VAR ZSTD_VERSION | 14 | VERSION_VAR ZSTD_VERSION |
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: | |||
| 131 | return active_config; | 131 | return active_config; |
| 132 | } | 132 | } |
| 133 | 133 | ||
| 134 | bool StrictContextRequired() const { | ||
| 135 | return strict_context_required; | ||
| 136 | } | ||
| 137 | |||
| 134 | /** | 138 | /** |
| 135 | * Requests the internal configuration to be replaced by the specified argument at some point in | 139 | * Requests the internal configuration to be replaced by the specified argument at some point in |
| 136 | * the future. | 140 | * the future. |
| @@ -207,6 +211,8 @@ protected: | |||
| 207 | 211 | ||
| 208 | WindowSystemInfo window_info; | 212 | WindowSystemInfo window_info; |
| 209 | 213 | ||
| 214 | bool strict_context_required = false; | ||
| 215 | |||
| 210 | private: | 216 | private: |
| 211 | /** | 217 | /** |
| 212 | * Handler called when the minimal client area was requested to be changed via SetConfig. | 218 | * Handler called when the minimal client area was requested to be changed via SetConfig. |
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: | |||
| 203 | }; | 203 | }; |
| 204 | 204 | ||
| 205 | AudInU::AudInU(Core::System& system_) | 205 | AudInU::AudInU(Core::System& system_) |
| 206 | : ServiceFramework{system_, "audin:u"}, service_context{system_, "AudInU"}, | 206 | : ServiceFramework{system_, "audin:u", ServiceThreadType::CreateNew}, |
| 207 | impl{std::make_unique<AudioCore::AudioIn::Manager>(system_)} { | 207 | service_context{system_, "AudInU"}, impl{std::make_unique<AudioCore::AudioIn::Manager>( |
| 208 | system_)} { | ||
| 208 | // clang-format off | 209 | // clang-format off |
| 209 | static const FunctionInfo functions[] = { | 210 | static const FunctionInfo functions[] = { |
| 210 | {0, &AudInU::ListAudioIns, "ListAudioIns"}, | 211 | {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: | |||
| 26 | explicit IAudioOut(Core::System& system_, AudioCore::AudioOut::Manager& manager, | 26 | explicit IAudioOut(Core::System& system_, AudioCore::AudioOut::Manager& manager, |
| 27 | size_t session_id, const std::string& device_name, | 27 | size_t session_id, const std::string& device_name, |
| 28 | const AudioOutParameter& in_params, u32 handle, u64 applet_resource_user_id) | 28 | const AudioOutParameter& in_params, u32 handle, u64 applet_resource_user_id) |
| 29 | : ServiceFramework{system_, "IAudioOut"}, service_context{system_, "IAudioOut"}, | 29 | : ServiceFramework{system_, "IAudioOut", ServiceThreadType::CreateNew}, |
| 30 | event{service_context.CreateEvent("AudioOutEvent")}, | 30 | service_context{system_, "IAudioOut"}, event{service_context.CreateEvent( |
| 31 | "AudioOutEvent")}, | ||
| 31 | impl{std::make_shared<AudioCore::AudioOut::Out>(system_, manager, event, session_id)} { | 32 | impl{std::make_shared<AudioCore::AudioOut::Out>(system_, manager, event, session_id)} { |
| 32 | 33 | ||
| 33 | // clang-format off | 34 | // clang-format off |
| @@ -220,8 +221,9 @@ private: | |||
| 220 | }; | 221 | }; |
| 221 | 222 | ||
| 222 | AudOutU::AudOutU(Core::System& system_) | 223 | AudOutU::AudOutU(Core::System& system_) |
| 223 | : ServiceFramework{system_, "audout:u"}, service_context{system_, "AudOutU"}, | 224 | : ServiceFramework{system_, "audout:u", ServiceThreadType::CreateNew}, |
| 224 | impl{std::make_unique<AudioCore::AudioOut::Manager>(system_)} { | 225 | service_context{system_, "AudOutU"}, impl{std::make_unique<AudioCore::AudioOut::Manager>( |
| 226 | system_)} { | ||
| 225 | // clang-format off | 227 | // clang-format off |
| 226 | static const FunctionInfo functions[] = { | 228 | static const FunctionInfo functions[] = { |
| 227 | {0, &AudOutU::ListAudioOuts, "ListAudioOuts"}, | 229 | {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: | |||
| 35 | AudioCore::AudioRendererParameterInternal& params, | 35 | AudioCore::AudioRendererParameterInternal& params, |
| 36 | Kernel::KTransferMemory* transfer_memory, u64 transfer_memory_size, | 36 | Kernel::KTransferMemory* transfer_memory, u64 transfer_memory_size, |
| 37 | u32 process_handle, u64 applet_resource_user_id, s32 session_id) | 37 | u32 process_handle, u64 applet_resource_user_id, s32 session_id) |
| 38 | : ServiceFramework{system_, "IAudioRenderer"}, service_context{system_, "IAudioRenderer"}, | 38 | : ServiceFramework{system_, "IAudioRenderer", ServiceThreadType::CreateNew}, |
| 39 | rendered_event{service_context.CreateEvent("IAudioRendererEvent")}, manager{manager_}, | 39 | service_context{system_, "IAudioRenderer"}, rendered_event{service_context.CreateEvent( |
| 40 | impl{std::make_unique<Renderer>(system_, manager, rendered_event)} { | 40 | "IAudioRendererEvent")}, |
| 41 | manager{manager_}, impl{std::make_unique<Renderer>(system_, manager, rendered_event)} { | ||
| 41 | // clang-format off | 42 | // clang-format off |
| 42 | static const FunctionInfo functions[] = { | 43 | static const FunctionInfo functions[] = { |
| 43 | {0, &IAudioRenderer::GetSampleRate, "GetSampleRate"}, | 44 | {0, &IAudioRenderer::GetSampleRate, "GetSampleRate"}, |
| @@ -242,8 +243,10 @@ class IAudioDevice final : public ServiceFramework<IAudioDevice> { | |||
| 242 | public: | 243 | public: |
| 243 | explicit IAudioDevice(Core::System& system_, u64 applet_resource_user_id, u32 revision, | 244 | explicit IAudioDevice(Core::System& system_, u64 applet_resource_user_id, u32 revision, |
| 244 | u32 device_num) | 245 | u32 device_num) |
| 245 | : ServiceFramework{system_, "IAudioDevice"}, service_context{system_, "IAudioDevice"}, | 246 | : ServiceFramework{system_, "IAudioDevice", ServiceThreadType::CreateNew}, |
| 246 | impl{std::make_unique<AudioDevice>(system_, applet_resource_user_id, revision)}, | 247 | service_context{system_, "IAudioDevice"}, impl{std::make_unique<AudioDevice>( |
| 248 | system_, applet_resource_user_id, | ||
| 249 | revision)}, | ||
| 247 | event{service_context.CreateEvent(fmt::format("IAudioDeviceEvent-{}", device_num))} { | 250 | event{service_context.CreateEvent(fmt::format("IAudioDeviceEvent-{}", device_num))} { |
| 248 | static const FunctionInfo functions[] = { | 251 | static const FunctionInfo functions[] = { |
| 249 | {0, &IAudioDevice::ListAudioDeviceName, "ListAudioDeviceName"}, | 252 | {0, &IAudioDevice::ListAudioDeviceName, "ListAudioDeviceName"}, |
| @@ -418,7 +421,7 @@ private: | |||
| 418 | }; | 421 | }; |
| 419 | 422 | ||
| 420 | AudRenU::AudRenU(Core::System& system_) | 423 | AudRenU::AudRenU(Core::System& system_) |
| 421 | : ServiceFramework{system_, "audren:u"}, | 424 | : ServiceFramework{system_, "audren:u", ServiceThreadType::CreateNew}, |
| 422 | service_context{system_, "audren:u"}, impl{std::make_unique<Manager>(system_)} { | 425 | service_context{system_, "audren:u"}, impl{std::make_unique<Manager>(system_)} { |
| 423 | // clang-format off | 426 | // clang-format off |
| 424 | static const FunctionInfo functions[] = { | 427 | static const FunctionInfo functions[] = { |
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) { | |||
| 1345 | if (info.uses_fswzadd || info.uses_subgroup_invocation_id || info.uses_subgroup_shuffles || | 1345 | if (info.uses_fswzadd || info.uses_subgroup_invocation_id || info.uses_subgroup_shuffles || |
| 1346 | (profile.warp_size_potentially_larger_than_guest && | 1346 | (profile.warp_size_potentially_larger_than_guest && |
| 1347 | (info.uses_subgroup_vote || info.uses_subgroup_mask))) { | 1347 | (info.uses_subgroup_vote || info.uses_subgroup_mask))) { |
| 1348 | AddCapability(spv::Capability::GroupNonUniform); | ||
| 1348 | subgroup_local_invocation_id = | 1349 | subgroup_local_invocation_id = |
| 1349 | DefineInput(*this, U32[1], false, spv::BuiltIn::SubgroupLocalInvocationId); | 1350 | DefineInput(*this, U32[1], false, spv::BuiltIn::SubgroupLocalInvocationId); |
| 1351 | Decorate(subgroup_local_invocation_id, spv::Decoration::Flat); | ||
| 1350 | } | 1352 | } |
| 1351 | if (info.uses_fswzadd) { | 1353 | if (info.uses_fswzadd) { |
| 1352 | const Id f32_one{Const(1.0f)}; | 1354 | 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 { | |||
| 223 | /// core timing events. | 223 | /// core timing events. |
| 224 | void Start() { | 224 | void Start() { |
| 225 | gpu_thread.StartThread(*renderer, renderer->Context(), *scheduler); | 225 | gpu_thread.StartThread(*renderer, renderer->Context(), *scheduler); |
| 226 | cpu_context = renderer->GetRenderWindow().CreateSharedContext(); | ||
| 227 | cpu_context->MakeCurrent(); | ||
| 228 | } | 226 | } |
| 229 | 227 | ||
| 230 | void NotifyShutdown() { | 228 | void NotifyShutdown() { |
| @@ -235,6 +233,9 @@ struct GPU::Impl { | |||
| 235 | 233 | ||
| 236 | /// Obtain the CPU Context | 234 | /// Obtain the CPU Context |
| 237 | void ObtainContext() { | 235 | void ObtainContext() { |
| 236 | if (!cpu_context) { | ||
| 237 | cpu_context = renderer->GetRenderWindow().CreateSharedContext(); | ||
| 238 | } | ||
| 238 | cpu_context->MakeCurrent(); | 239 | cpu_context->MakeCurrent(); |
| 239 | } | 240 | } |
| 240 | 241 | ||
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() { | |||
| 112 | } | 112 | } |
| 113 | } // Anonymous namespace | 113 | } // Anonymous namespace |
| 114 | 114 | ||
| 115 | Device::Device() { | 115 | Device::Device(Core::Frontend::EmuWindow& emu_window) { |
| 116 | if (!GLAD_GL_VERSION_4_6) { | 116 | if (!GLAD_GL_VERSION_4_6) { |
| 117 | LOG_ERROR(Render_OpenGL, "OpenGL 4.6 is not available"); | 117 | LOG_ERROR(Render_OpenGL, "OpenGL 4.6 is not available"); |
| 118 | throw std::runtime_error{"Insufficient version"}; | 118 | throw std::runtime_error{"Insufficient version"}; |
| @@ -126,9 +126,9 @@ Device::Device() { | |||
| 126 | const bool is_intel = vendor_name == "Intel"; | 126 | const bool is_intel = vendor_name == "Intel"; |
| 127 | 127 | ||
| 128 | #ifdef __unix__ | 128 | #ifdef __unix__ |
| 129 | const bool is_linux = true; | 129 | constexpr bool is_linux = true; |
| 130 | #else | 130 | #else |
| 131 | const bool is_linux = false; | 131 | constexpr bool is_linux = false; |
| 132 | #endif | 132 | #endif |
| 133 | 133 | ||
| 134 | bool disable_fast_buffer_sub_data = false; | 134 | bool disable_fast_buffer_sub_data = false; |
| @@ -193,9 +193,11 @@ Device::Device() { | |||
| 193 | } | 193 | } |
| 194 | } | 194 | } |
| 195 | 195 | ||
| 196 | strict_context_required = emu_window.StrictContextRequired(); | ||
| 196 | // Blocks AMD and Intel OpenGL drivers on Windows from using asynchronous shader compilation. | 197 | // Blocks AMD and Intel OpenGL drivers on Windows from using asynchronous shader compilation. |
| 198 | // Blocks EGL on Wayland from using asynchronous shader compilation. | ||
| 197 | use_asynchronous_shaders = Settings::values.use_asynchronous_shaders.GetValue() && | 199 | use_asynchronous_shaders = Settings::values.use_asynchronous_shaders.GetValue() && |
| 198 | !(is_amd || (is_intel && !is_linux)); | 200 | !(is_amd || (is_intel && !is_linux)) && !strict_context_required; |
| 199 | use_driver_cache = is_nvidia; | 201 | use_driver_cache = is_nvidia; |
| 200 | 202 | ||
| 201 | LOG_INFO(Render_OpenGL, "Renderer_VariableAOFFI: {}", has_variable_aoffi); | 203 | 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 @@ | |||
| 5 | 5 | ||
| 6 | #include <cstddef> | 6 | #include <cstddef> |
| 7 | #include "common/common_types.h" | 7 | #include "common/common_types.h" |
| 8 | #include "core/frontend/emu_window.h" | ||
| 8 | #include "shader_recompiler/stage.h" | 9 | #include "shader_recompiler/stage.h" |
| 9 | 10 | ||
| 10 | namespace Settings { | 11 | namespace Settings { |
| @@ -15,7 +16,7 @@ namespace OpenGL { | |||
| 15 | 16 | ||
| 16 | class Device { | 17 | class Device { |
| 17 | public: | 18 | public: |
| 18 | explicit Device(); | 19 | explicit Device(Core::Frontend::EmuWindow& emu_window); |
| 19 | 20 | ||
| 20 | [[nodiscard]] std::string GetVendorName() const; | 21 | [[nodiscard]] std::string GetVendorName() const; |
| 21 | 22 | ||
| @@ -173,6 +174,10 @@ public: | |||
| 173 | return can_report_memory; | 174 | return can_report_memory; |
| 174 | } | 175 | } |
| 175 | 176 | ||
| 177 | bool StrictContextRequired() const { | ||
| 178 | return strict_context_required; | ||
| 179 | } | ||
| 180 | |||
| 176 | private: | 181 | private: |
| 177 | static bool TestVariableAoffi(); | 182 | static bool TestVariableAoffi(); |
| 178 | static bool TestPreciseBug(); | 183 | static bool TestPreciseBug(); |
| @@ -216,6 +221,7 @@ private: | |||
| 216 | bool has_cbuf_ftou_bug{}; | 221 | bool has_cbuf_ftou_bug{}; |
| 217 | bool has_bool_ref_bug{}; | 222 | bool has_bool_ref_bug{}; |
| 218 | bool can_report_memory{}; | 223 | bool can_report_memory{}; |
| 224 | bool strict_context_required{}; | ||
| 219 | 225 | ||
| 220 | std::string vendor_name; | 226 | std::string vendor_name; |
| 221 | }; | 227 | }; |
diff --git a/src/video_core/renderer_opengl/gl_shader_cache.cpp b/src/video_core/renderer_opengl/gl_shader_cache.cpp index a59d0d24e..fff55d585 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 | |||
| 174 | texture_cache{texture_cache_}, buffer_cache{buffer_cache_}, program_manager{program_manager_}, | 174 | texture_cache{texture_cache_}, buffer_cache{buffer_cache_}, program_manager{program_manager_}, |
| 175 | state_tracker{state_tracker_}, shader_notify{shader_notify_}, | 175 | state_tracker{state_tracker_}, shader_notify{shader_notify_}, |
| 176 | use_asynchronous_shaders{device.UseAsynchronousShaders()}, | 176 | use_asynchronous_shaders{device.UseAsynchronousShaders()}, |
| 177 | strict_context_required{device.StrictContextRequired()}, | ||
| 177 | profile{ | 178 | profile{ |
| 178 | .supported_spirv = 0x00010000, | 179 | .supported_spirv = 0x00010000, |
| 179 | 180 | ||
| @@ -255,9 +256,14 @@ void ShaderCache::LoadDiskResources(u64 title_id, std::stop_token stop_loading, | |||
| 255 | } | 256 | } |
| 256 | shader_cache_filename = base_dir / "opengl.bin"; | 257 | shader_cache_filename = base_dir / "opengl.bin"; |
| 257 | 258 | ||
| 258 | if (!workers) { | 259 | if (!workers && !strict_context_required) { |
| 259 | workers = CreateWorkers(); | 260 | workers = CreateWorkers(); |
| 260 | } | 261 | } |
| 262 | std::optional<Context> strict_context; | ||
| 263 | if (strict_context_required) { | ||
| 264 | strict_context.emplace(emu_window); | ||
| 265 | } | ||
| 266 | |||
| 261 | struct { | 267 | struct { |
| 262 | std::mutex mutex; | 268 | std::mutex mutex; |
| 263 | size_t total{}; | 269 | size_t total{}; |
| @@ -265,44 +271,49 @@ void ShaderCache::LoadDiskResources(u64 title_id, std::stop_token stop_loading, | |||
| 265 | bool has_loaded{}; | 271 | bool has_loaded{}; |
| 266 | } state; | 272 | } state; |
| 267 | 273 | ||
| 274 | const auto queue_work{[&](Common::UniqueFunction<void, Context*>&& work) { | ||
| 275 | if (strict_context_required) { | ||
| 276 | work(&strict_context.value()); | ||
| 277 | } else { | ||
| 278 | workers->QueueWork(std::move(work)); | ||
| 279 | } | ||
| 280 | }}; | ||
| 268 | const auto load_compute{[&](std::ifstream& file, FileEnvironment env) { | 281 | const auto load_compute{[&](std::ifstream& file, FileEnvironment env) { |
| 269 | ComputePipelineKey key; | 282 | ComputePipelineKey key; |
| 270 | file.read(reinterpret_cast<char*>(&key), sizeof(key)); | 283 | file.read(reinterpret_cast<char*>(&key), sizeof(key)); |
| 271 | workers->QueueWork( | 284 | queue_work([this, key, env = std::move(env), &state, &callback](Context* ctx) mutable { |
| 272 | [this, key, env = std::move(env), &state, &callback](Context* ctx) mutable { | 285 | ctx->pools.ReleaseContents(); |
| 273 | ctx->pools.ReleaseContents(); | 286 | auto pipeline{CreateComputePipeline(ctx->pools, key, env)}; |
| 274 | auto pipeline{CreateComputePipeline(ctx->pools, key, env)}; | 287 | std::scoped_lock lock{state.mutex}; |
| 275 | std::scoped_lock lock{state.mutex}; | 288 | if (pipeline) { |
| 276 | if (pipeline) { | 289 | compute_cache.emplace(key, std::move(pipeline)); |
| 277 | compute_cache.emplace(key, std::move(pipeline)); | 290 | } |
| 278 | } | 291 | ++state.built; |
| 279 | ++state.built; | 292 | if (state.has_loaded) { |
| 280 | if (state.has_loaded) { | 293 | callback(VideoCore::LoadCallbackStage::Build, state.built, state.total); |
| 281 | callback(VideoCore::LoadCallbackStage::Build, state.built, state.total); | 294 | } |
| 282 | } | 295 | }); |
| 283 | }); | ||
| 284 | ++state.total; | 296 | ++state.total; |
| 285 | }}; | 297 | }}; |
| 286 | const auto load_graphics{[&](std::ifstream& file, std::vector<FileEnvironment> envs) { | 298 | const auto load_graphics{[&](std::ifstream& file, std::vector<FileEnvironment> envs) { |
| 287 | GraphicsPipelineKey key; | 299 | GraphicsPipelineKey key; |
| 288 | file.read(reinterpret_cast<char*>(&key), sizeof(key)); | 300 | file.read(reinterpret_cast<char*>(&key), sizeof(key)); |
| 289 | workers->QueueWork( | 301 | queue_work([this, key, envs = std::move(envs), &state, &callback](Context* ctx) mutable { |
| 290 | [this, key, envs = std::move(envs), &state, &callback](Context* ctx) mutable { | 302 | boost::container::static_vector<Shader::Environment*, 5> env_ptrs; |
| 291 | boost::container::static_vector<Shader::Environment*, 5> env_ptrs; | 303 | for (auto& env : envs) { |
| 292 | for (auto& env : envs) { | 304 | env_ptrs.push_back(&env); |
| 293 | env_ptrs.push_back(&env); | 305 | } |
| 294 | } | 306 | ctx->pools.ReleaseContents(); |
| 295 | ctx->pools.ReleaseContents(); | 307 | auto pipeline{CreateGraphicsPipeline(ctx->pools, key, MakeSpan(env_ptrs), false)}; |
| 296 | auto pipeline{CreateGraphicsPipeline(ctx->pools, key, MakeSpan(env_ptrs), false)}; | 308 | std::scoped_lock lock{state.mutex}; |
| 297 | std::scoped_lock lock{state.mutex}; | 309 | if (pipeline) { |
| 298 | if (pipeline) { | 310 | graphics_cache.emplace(key, std::move(pipeline)); |
| 299 | graphics_cache.emplace(key, std::move(pipeline)); | 311 | } |
| 300 | } | 312 | ++state.built; |
| 301 | ++state.built; | 313 | if (state.has_loaded) { |
| 302 | if (state.has_loaded) { | 314 | callback(VideoCore::LoadCallbackStage::Build, state.built, state.total); |
| 303 | callback(VideoCore::LoadCallbackStage::Build, state.built, state.total); | 315 | } |
| 304 | } | 316 | }); |
| 305 | }); | ||
| 306 | ++state.total; | 317 | ++state.total; |
| 307 | }}; | 318 | }}; |
| 308 | LoadPipelines(stop_loading, shader_cache_filename, CACHE_VERSION, load_compute, load_graphics); | 319 | LoadPipelines(stop_loading, shader_cache_filename, CACHE_VERSION, load_compute, load_graphics); |
| @@ -314,6 +325,9 @@ void ShaderCache::LoadDiskResources(u64 title_id, std::stop_token stop_loading, | |||
| 314 | state.has_loaded = true; | 325 | state.has_loaded = true; |
| 315 | lock.unlock(); | 326 | lock.unlock(); |
| 316 | 327 | ||
| 328 | if (strict_context_required) { | ||
| 329 | return; | ||
| 330 | } | ||
| 317 | workers->WaitForRequests(stop_loading); | 331 | workers->WaitForRequests(stop_loading); |
| 318 | if (!use_asynchronous_shaders) { | 332 | if (!use_asynchronous_shaders) { |
| 319 | workers.reset(); | 333 | 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: | |||
| 69 | StateTracker& state_tracker; | 69 | StateTracker& state_tracker; |
| 70 | VideoCore::ShaderNotify& shader_notify; | 70 | VideoCore::ShaderNotify& shader_notify; |
| 71 | const bool use_asynchronous_shaders; | 71 | const bool use_asynchronous_shaders; |
| 72 | const bool strict_context_required; | ||
| 72 | 73 | ||
| 73 | GraphicsPipelineKey graphics_key{}; | 74 | GraphicsPipelineKey graphics_key{}; |
| 74 | GraphicsPipeline* current_pipeline{}; | 75 | 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_, | |||
| 140 | Core::Memory::Memory& cpu_memory_, Tegra::GPU& gpu_, | 140 | Core::Memory::Memory& cpu_memory_, Tegra::GPU& gpu_, |
| 141 | std::unique_ptr<Core::Frontend::GraphicsContext> context_) | 141 | std::unique_ptr<Core::Frontend::GraphicsContext> context_) |
| 142 | : RendererBase{emu_window_, std::move(context_)}, telemetry_session{telemetry_session_}, | 142 | : RendererBase{emu_window_, std::move(context_)}, telemetry_session{telemetry_session_}, |
| 143 | emu_window{emu_window_}, cpu_memory{cpu_memory_}, gpu{gpu_}, state_tracker{}, | 143 | emu_window{emu_window_}, cpu_memory{cpu_memory_}, gpu{gpu_}, device{emu_window_}, |
| 144 | program_manager{device}, | 144 | state_tracker{}, program_manager{device}, |
| 145 | rasterizer(emu_window, gpu, cpu_memory, device, screen_info, program_manager, state_tracker) { | 145 | rasterizer(emu_window, gpu, cpu_memory, device, screen_info, program_manager, state_tracker) { |
| 146 | if (Settings::values.renderer_debug && GLAD_GL_KHR_debug) { | 146 | if (Settings::values.renderer_debug && GLAD_GL_KHR_debug) { |
| 147 | glEnable(GL_DEBUG_OUTPUT); | 147 | 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) { | |||
| 139 | RenderScreenshot(*framebuffer, use_accelerated); | 139 | RenderScreenshot(*framebuffer, use_accelerated); |
| 140 | 140 | ||
| 141 | bool has_been_recreated = false; | 141 | bool has_been_recreated = false; |
| 142 | const auto recreate_swapchain = [&] { | 142 | const auto recreate_swapchain = [&](u32 width, u32 height) { |
| 143 | if (!has_been_recreated) { | 143 | if (!has_been_recreated) { |
| 144 | has_been_recreated = true; | 144 | has_been_recreated = true; |
| 145 | scheduler.Finish(); | 145 | scheduler.Finish(); |
| 146 | } | 146 | } |
| 147 | const Layout::FramebufferLayout layout = render_window.GetFramebufferLayout(); | 147 | swapchain.Create(width, height, is_srgb); |
| 148 | swapchain.Create(layout.width, layout.height, is_srgb); | ||
| 149 | }; | 148 | }; |
| 150 | if (swapchain.NeedsRecreation(is_srgb)) { | 149 | |
| 151 | recreate_swapchain(); | 150 | const Layout::FramebufferLayout layout = render_window.GetFramebufferLayout(); |
| 151 | if (swapchain.NeedsRecreation(is_srgb) || swapchain.GetWidth() != layout.width || | ||
| 152 | swapchain.GetHeight() != layout.height) { | ||
| 153 | recreate_swapchain(layout.width, layout.height); | ||
| 152 | } | 154 | } |
| 153 | bool is_outdated; | 155 | bool is_outdated; |
| 154 | do { | 156 | do { |
| 155 | swapchain.AcquireNextImage(); | 157 | swapchain.AcquireNextImage(); |
| 156 | is_outdated = swapchain.IsOutDated(); | 158 | is_outdated = swapchain.IsOutDated(); |
| 157 | if (is_outdated) { | 159 | if (is_outdated) { |
| 158 | recreate_swapchain(); | 160 | recreate_swapchain(layout.width, layout.height); |
| 159 | } | 161 | } |
| 160 | } while (is_outdated); | 162 | } while (is_outdated); |
| 161 | if (has_been_recreated) { | 163 | 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) { | |||
| 285 | 285 | ||
| 286 | void BufferCacheRuntime::BindVertexBuffer(u32 index, VkBuffer buffer, u32 offset, u32 size, | 286 | void BufferCacheRuntime::BindVertexBuffer(u32 index, VkBuffer buffer, u32 offset, u32 size, |
| 287 | u32 stride) { | 287 | u32 stride) { |
| 288 | if (index >= device.GetMaxVertexInputBindings()) { | ||
| 289 | return; | ||
| 290 | } | ||
| 288 | if (device.IsExtExtendedDynamicStateSupported()) { | 291 | if (device.IsExtExtendedDynamicStateSupported()) { |
| 289 | scheduler.Record([index, buffer, offset, size, stride](vk::CommandBuffer cmdbuf) { | 292 | scheduler.Record([index, buffer, offset, size, stride](vk::CommandBuffer cmdbuf) { |
| 290 | const VkDeviceSize vk_offset = buffer != VK_NULL_HANDLE ? offset : 0; | 293 | 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 006128638..4b10fe7bc 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) { | |||
| 529 | static_vector<VkVertexInputBindingDivisorDescriptionEXT, 32> vertex_binding_divisors; | 529 | static_vector<VkVertexInputBindingDivisorDescriptionEXT, 32> vertex_binding_divisors; |
| 530 | static_vector<VkVertexInputAttributeDescription, 32> vertex_attributes; | 530 | static_vector<VkVertexInputAttributeDescription, 32> vertex_attributes; |
| 531 | if (key.state.dynamic_vertex_input) { | 531 | if (key.state.dynamic_vertex_input) { |
| 532 | for (size_t index = 0; index < key.state.attributes.size(); ++index) { | 532 | const size_t num_vertex_arrays = std::min( |
| 533 | key.state.attributes.size(), static_cast<size_t>(device.GetMaxVertexInputBindings())); | ||
| 534 | for (size_t index = 0; index < num_vertex_arrays; ++index) { | ||
| 533 | const u32 type = key.state.DynamicAttributeType(index); | 535 | const u32 type = key.state.DynamicAttributeType(index); |
| 534 | if (!stage_infos[0].loads.Generic(index) || type == 0) { | 536 | if (!stage_infos[0].loads.Generic(index) || type == 0) { |
| 535 | continue; | 537 | continue; |
| @@ -551,7 +553,9 @@ void GraphicsPipeline::MakePipeline(VkRenderPass render_pass) { | |||
| 551 | }); | 553 | }); |
| 552 | } | 554 | } |
| 553 | } else { | 555 | } else { |
| 554 | for (size_t index = 0; index < Maxwell::NumVertexArrays; ++index) { | 556 | const size_t num_vertex_arrays = std::min( |
| 557 | Maxwell::NumVertexArrays, static_cast<size_t>(device.GetMaxVertexInputBindings())); | ||
| 558 | for (size_t index = 0; index < num_vertex_arrays; ++index) { | ||
| 555 | const bool instanced = key.state.binding_divisors[index] != 0; | 559 | const bool instanced = key.state.binding_divisors[index] != 0; |
| 556 | const auto rate = | 560 | const auto rate = |
| 557 | instanced ? VK_VERTEX_INPUT_RATE_INSTANCE : VK_VERTEX_INPUT_RATE_VERTEX; | 561 | instanced ? VK_VERTEX_INPUT_RATE_INSTANCE : VK_VERTEX_INPUT_RATE_VERTEX; |
| @@ -580,6 +584,8 @@ void GraphicsPipeline::MakePipeline(VkRenderPass render_pass) { | |||
| 580 | }); | 584 | }); |
| 581 | } | 585 | } |
| 582 | } | 586 | } |
| 587 | ASSERT(vertex_attributes.size() <= device.GetMaxVertexInputAttributes()); | ||
| 588 | |||
| 583 | VkPipelineVertexInputStateCreateInfo vertex_input_ci{ | 589 | VkPipelineVertexInputStateCreateInfo vertex_input_ci{ |
| 584 | .sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO, | 590 | .sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO, |
| 585 | .pNext = nullptr, | 591 | .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 81f5f3e11..86fdde014 100644 --- a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp | |||
| @@ -341,6 +341,15 @@ PipelineCache::PipelineCache(RasterizerVulkan& rasterizer_, const Device& device | |||
| 341 | .support_snorm_render_buffer = true, | 341 | .support_snorm_render_buffer = true, |
| 342 | .support_viewport_index_layer = device.IsExtShaderViewportIndexLayerSupported(), | 342 | .support_viewport_index_layer = device.IsExtShaderViewportIndexLayerSupported(), |
| 343 | }; | 343 | }; |
| 344 | |||
| 345 | if (device.GetMaxVertexInputAttributes() < Maxwell::NumVertexAttributes) { | ||
| 346 | LOG_WARNING(Render_Vulkan, "maxVertexInputAttributes is too low: {} < {}", | ||
| 347 | device.GetMaxVertexInputAttributes(), Maxwell::NumVertexAttributes); | ||
| 348 | } | ||
| 349 | if (device.GetMaxVertexInputBindings() < Maxwell::NumVertexArrays) { | ||
| 350 | LOG_WARNING(Render_Vulkan, "maxVertexInputBindings is too low: {} < {}", | ||
| 351 | device.GetMaxVertexInputBindings(), Maxwell::NumVertexArrays); | ||
| 352 | } | ||
| 344 | } | 353 | } |
| 345 | 354 | ||
| 346 | PipelineCache::~PipelineCache() = default; | 355 | PipelineCache::~PipelineCache() = default; |
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 | |||
| 67 | 67 | ||
| 68 | } // Anonymous namespace | 68 | } // Anonymous namespace |
| 69 | 69 | ||
| 70 | Swapchain::Swapchain(VkSurfaceKHR surface_, const Device& device_, Scheduler& scheduler_, u32 width, | 70 | Swapchain::Swapchain(VkSurfaceKHR surface_, const Device& device_, Scheduler& scheduler_, |
| 71 | u32 height, bool srgb) | 71 | u32 width_, u32 height_, bool srgb) |
| 72 | : surface{surface_}, device{device_}, scheduler{scheduler_} { | 72 | : surface{surface_}, device{device_}, scheduler{scheduler_} { |
| 73 | Create(width, height, srgb); | 73 | Create(width_, height_, srgb); |
| 74 | } | 74 | } |
| 75 | 75 | ||
| 76 | Swapchain::~Swapchain() = default; | 76 | Swapchain::~Swapchain() = default; |
| 77 | 77 | ||
| 78 | void Swapchain::Create(u32 width, u32 height, bool srgb) { | 78 | void Swapchain::Create(u32 width_, u32 height_, bool srgb) { |
| 79 | is_outdated = false; | 79 | is_outdated = false; |
| 80 | is_suboptimal = false; | 80 | is_suboptimal = false; |
| 81 | width = width_; | ||
| 82 | height = height_; | ||
| 81 | 83 | ||
| 82 | const auto physical_device = device.GetPhysical(); | 84 | const auto physical_device = device.GetPhysical(); |
| 83 | const auto capabilities{physical_device.GetSurfaceCapabilitiesKHR(surface)}; | 85 | const auto capabilities{physical_device.GetSurfaceCapabilitiesKHR(surface)}; |
| @@ -88,7 +90,7 @@ void Swapchain::Create(u32 width, u32 height, bool srgb) { | |||
| 88 | device.GetLogical().WaitIdle(); | 90 | device.GetLogical().WaitIdle(); |
| 89 | Destroy(); | 91 | Destroy(); |
| 90 | 92 | ||
| 91 | CreateSwapchain(capabilities, width, height, srgb); | 93 | CreateSwapchain(capabilities, srgb); |
| 92 | CreateSemaphores(); | 94 | CreateSemaphores(); |
| 93 | CreateImageViews(); | 95 | CreateImageViews(); |
| 94 | 96 | ||
| @@ -148,8 +150,7 @@ void Swapchain::Present(VkSemaphore render_semaphore) { | |||
| 148 | } | 150 | } |
| 149 | } | 151 | } |
| 150 | 152 | ||
| 151 | void Swapchain::CreateSwapchain(const VkSurfaceCapabilitiesKHR& capabilities, u32 width, u32 height, | 153 | void Swapchain::CreateSwapchain(const VkSurfaceCapabilitiesKHR& capabilities, bool srgb) { |
| 152 | bool srgb) { | ||
| 153 | const auto physical_device{device.GetPhysical()}; | 154 | const auto physical_device{device.GetPhysical()}; |
| 154 | const auto formats{physical_device.GetSurfaceFormatsKHR(surface)}; | 155 | const auto formats{physical_device.GetSurfaceFormatsKHR(surface)}; |
| 155 | const auto present_modes{physical_device.GetSurfacePresentModesKHR(surface)}; | 156 | 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: | |||
| 80 | return *present_semaphores[frame_index]; | 80 | return *present_semaphores[frame_index]; |
| 81 | } | 81 | } |
| 82 | 82 | ||
| 83 | u32 GetWidth() const { | ||
| 84 | return width; | ||
| 85 | } | ||
| 86 | |||
| 87 | u32 GetHeight() const { | ||
| 88 | return height; | ||
| 89 | } | ||
| 90 | |||
| 83 | private: | 91 | private: |
| 84 | void CreateSwapchain(const VkSurfaceCapabilitiesKHR& capabilities, u32 width, u32 height, | 92 | void CreateSwapchain(const VkSurfaceCapabilitiesKHR& capabilities, bool srgb); |
| 85 | bool srgb); | ||
| 86 | void CreateSemaphores(); | 93 | void CreateSemaphores(); |
| 87 | void CreateImageViews(); | 94 | void CreateImageViews(); |
| 88 | 95 | ||
| @@ -105,6 +112,9 @@ private: | |||
| 105 | std::vector<u64> resource_ticks; | 112 | std::vector<u64> resource_ticks; |
| 106 | std::vector<vk::Semaphore> present_semaphores; | 113 | std::vector<vk::Semaphore> present_semaphores; |
| 107 | 114 | ||
| 115 | u32 width; | ||
| 116 | u32 height; | ||
| 117 | |||
| 108 | u32 image_index{}; | 118 | u32 image_index{}; |
| 109 | u32 frame_index{}; | 119 | u32 frame_index{}; |
| 110 | 120 | ||
diff --git a/src/video_core/vulkan_common/vulkan_device.cpp b/src/video_core/vulkan_common/vulkan_device.cpp index 6a2ad4b1d..f45030311 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 | |||
| 421 | VkPhysicalDevice8BitStorageFeatures bit8_storage{ | 421 | VkPhysicalDevice8BitStorageFeatures bit8_storage{ |
| 422 | .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_8BIT_STORAGE_FEATURES, | 422 | .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_8BIT_STORAGE_FEATURES, |
| 423 | .pNext = nullptr, | 423 | .pNext = nullptr, |
| 424 | .storageBuffer8BitAccess = false, | 424 | .storageBuffer8BitAccess = true, |
| 425 | .uniformAndStorageBuffer8BitAccess = true, | 425 | .uniformAndStorageBuffer8BitAccess = true, |
| 426 | .storagePushConstant8 = false, | 426 | .storagePushConstant8 = false, |
| 427 | }; | 427 | }; |
| @@ -1044,6 +1044,7 @@ void Device::CheckSuitability(bool requires_swapchain) const { | |||
| 1044 | std::make_pair(bit16_storage.storageBuffer16BitAccess, "storageBuffer16BitAccess"), | 1044 | std::make_pair(bit16_storage.storageBuffer16BitAccess, "storageBuffer16BitAccess"), |
| 1045 | std::make_pair(bit16_storage.uniformAndStorageBuffer16BitAccess, | 1045 | std::make_pair(bit16_storage.uniformAndStorageBuffer16BitAccess, |
| 1046 | "uniformAndStorageBuffer16BitAccess"), | 1046 | "uniformAndStorageBuffer16BitAccess"), |
| 1047 | std::make_pair(bit8_storage.storageBuffer8BitAccess, "storageBuffer8BitAccess"), | ||
| 1047 | std::make_pair(bit8_storage.uniformAndStorageBuffer8BitAccess, | 1048 | std::make_pair(bit8_storage.uniformAndStorageBuffer8BitAccess, |
| 1048 | "uniformAndStorageBuffer8BitAccess"), | 1049 | "uniformAndStorageBuffer8BitAccess"), |
| 1049 | std::make_pair(host_query_reset.hostQueryReset, "hostQueryReset"), | 1050 | std::make_pair(host_query_reset.hostQueryReset, "hostQueryReset"), |
| @@ -1380,6 +1381,10 @@ void Device::SetupFeatures() { | |||
| 1380 | is_shader_storage_image_multisample = features.shaderStorageImageMultisample; | 1381 | is_shader_storage_image_multisample = features.shaderStorageImageMultisample; |
| 1381 | is_blit_depth_stencil_supported = TestDepthStencilBlits(); | 1382 | is_blit_depth_stencil_supported = TestDepthStencilBlits(); |
| 1382 | is_optimal_astc_supported = IsOptimalAstcSupported(features); | 1383 | is_optimal_astc_supported = IsOptimalAstcSupported(features); |
| 1384 | |||
| 1385 | const VkPhysicalDeviceLimits& limits{properties.limits}; | ||
| 1386 | max_vertex_input_attributes = limits.maxVertexInputAttributes; | ||
| 1387 | max_vertex_input_bindings = limits.maxVertexInputBindings; | ||
| 1383 | } | 1388 | } |
| 1384 | 1389 | ||
| 1385 | void Device::SetupProperties() { | 1390 | void Device::SetupProperties() { |
diff --git a/src/video_core/vulkan_common/vulkan_device.h b/src/video_core/vulkan_common/vulkan_device.h index db802437c..391b7604c 100644 --- a/src/video_core/vulkan_common/vulkan_device.h +++ b/src/video_core/vulkan_common/vulkan_device.h | |||
| @@ -368,6 +368,14 @@ public: | |||
| 368 | return must_emulate_bgr565; | 368 | return must_emulate_bgr565; |
| 369 | } | 369 | } |
| 370 | 370 | ||
| 371 | u32 GetMaxVertexInputAttributes() const { | ||
| 372 | return max_vertex_input_attributes; | ||
| 373 | } | ||
| 374 | |||
| 375 | u32 GetMaxVertexInputBindings() const { | ||
| 376 | return max_vertex_input_bindings; | ||
| 377 | } | ||
| 378 | |||
| 371 | private: | 379 | private: |
| 372 | /// Checks if the physical device is suitable. | 380 | /// Checks if the physical device is suitable. |
| 373 | void CheckSuitability(bool requires_swapchain) const; | 381 | void CheckSuitability(bool requires_swapchain) const; |
| @@ -467,6 +475,8 @@ private: | |||
| 467 | bool supports_d24_depth{}; ///< Supports D24 depth buffers. | 475 | bool supports_d24_depth{}; ///< Supports D24 depth buffers. |
| 468 | bool cant_blit_msaa{}; ///< Does not support MSAA<->MSAA blitting. | 476 | bool cant_blit_msaa{}; ///< Does not support MSAA<->MSAA blitting. |
| 469 | bool must_emulate_bgr565{}; ///< Emulates BGR565 by swizzling RGB565 format. | 477 | bool must_emulate_bgr565{}; ///< Emulates BGR565 by swizzling RGB565 format. |
| 478 | u32 max_vertex_input_attributes{}; ///< Max vertex input attributes in pipeline | ||
| 479 | u32 max_vertex_input_bindings{}; ///< Max vertex input buffers in pipeline | ||
| 470 | 480 | ||
| 471 | // Telemetry parameters | 481 | // Telemetry parameters |
| 472 | std::string vendor_name; ///< Device's driver name. | 482 | std::string vendor_name; ///< Device's driver name. |
diff --git a/src/yuzu/bootmanager.cpp b/src/yuzu/bootmanager.cpp index f7321258c..642f96690 100644 --- a/src/yuzu/bootmanager.cpp +++ b/src/yuzu/bootmanager.cpp | |||
| @@ -61,8 +61,6 @@ void EmuThread::run() { | |||
| 61 | 61 | ||
| 62 | // Main process has been loaded. Make the context current to this thread and begin GPU and CPU | 62 | // Main process has been loaded. Make the context current to this thread and begin GPU and CPU |
| 63 | // execution. | 63 | // execution. |
| 64 | gpu.Start(); | ||
| 65 | |||
| 66 | gpu.ObtainContext(); | 64 | gpu.ObtainContext(); |
| 67 | 65 | ||
| 68 | emit LoadProgress(VideoCore::LoadCallbackStage::Prepare, 0, 0); | 66 | emit LoadProgress(VideoCore::LoadCallbackStage::Prepare, 0, 0); |
| @@ -77,6 +75,7 @@ void EmuThread::run() { | |||
| 77 | emit LoadProgress(VideoCore::LoadCallbackStage::Complete, 0, 0); | 75 | emit LoadProgress(VideoCore::LoadCallbackStage::Complete, 0, 0); |
| 78 | 76 | ||
| 79 | gpu.ReleaseContext(); | 77 | gpu.ReleaseContext(); |
| 78 | gpu.Start(); | ||
| 80 | 79 | ||
| 81 | system.GetCpuManager().OnGpuReady(); | 80 | system.GetCpuManager().OnGpuReady(); |
| 82 | 81 | ||
| @@ -229,6 +228,7 @@ class RenderWidget : public QWidget { | |||
| 229 | public: | 228 | public: |
| 230 | explicit RenderWidget(GRenderWindow* parent) : QWidget(parent), render_window(parent) { | 229 | explicit RenderWidget(GRenderWindow* parent) : QWidget(parent), render_window(parent) { |
| 231 | setAttribute(Qt::WA_NativeWindow); | 230 | setAttribute(Qt::WA_NativeWindow); |
| 231 | setAttribute(Qt::WA_DontCreateNativeAncestors); | ||
| 232 | setAttribute(Qt::WA_PaintOnScreen); | 232 | setAttribute(Qt::WA_PaintOnScreen); |
| 233 | } | 233 | } |
| 234 | 234 | ||
| @@ -319,6 +319,8 @@ GRenderWindow::GRenderWindow(GMainWindow* parent, EmuThread* emu_thread_, | |||
| 319 | input_subsystem->Initialize(); | 319 | input_subsystem->Initialize(); |
| 320 | this->setMouseTracking(true); | 320 | this->setMouseTracking(true); |
| 321 | 321 | ||
| 322 | strict_context_required = QGuiApplication::platformName() == QStringLiteral("wayland"); | ||
| 323 | |||
| 322 | connect(this, &GRenderWindow::FirstFrameDisplayed, parent, &GMainWindow::OnLoadComplete); | 324 | connect(this, &GRenderWindow::FirstFrameDisplayed, parent, &GMainWindow::OnLoadComplete); |
| 323 | connect(this, &GRenderWindow::ExecuteProgramSignal, parent, &GMainWindow::OnExecuteProgram, | 325 | connect(this, &GRenderWindow::ExecuteProgramSignal, parent, &GMainWindow::OnExecuteProgram, |
| 324 | Qt::QueuedConnection); | 326 | Qt::QueuedConnection); |
| @@ -957,6 +959,12 @@ void GRenderWindow::OnMinimalClientAreaChangeRequest(std::pair<u32, u32> minimal | |||
| 957 | 959 | ||
| 958 | bool GRenderWindow::InitializeOpenGL() { | 960 | bool GRenderWindow::InitializeOpenGL() { |
| 959 | #ifdef HAS_OPENGL | 961 | #ifdef HAS_OPENGL |
| 962 | if (!QOpenGLContext::supportsThreadedOpenGL()) { | ||
| 963 | QMessageBox::warning(this, tr("OpenGL not available!"), | ||
| 964 | tr("OpenGL shared contexts are not supported.")); | ||
| 965 | return false; | ||
| 966 | } | ||
| 967 | |||
| 960 | // TODO: One of these flags might be interesting: WA_OpaquePaintEvent, WA_NoBackground, | 968 | // TODO: One of these flags might be interesting: WA_OpaquePaintEvent, WA_NoBackground, |
| 961 | // WA_DontShowOnScreen, WA_DeleteOnClose | 969 | // WA_DontShowOnScreen, WA_DeleteOnClose |
| 962 | auto child = new OpenGLRenderWidget(this); | 970 | auto child = new OpenGLRenderWidget(this); |
diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp index 722fc708e..90fb4b0a4 100644 --- a/src/yuzu/configuration/config.cpp +++ b/src/yuzu/configuration/config.cpp | |||
| @@ -697,7 +697,6 @@ void Config::ReadRendererValues() { | |||
| 697 | ReadGlobalSetting(Settings::values.fsr_sharpening_slider); | 697 | ReadGlobalSetting(Settings::values.fsr_sharpening_slider); |
| 698 | ReadGlobalSetting(Settings::values.anti_aliasing); | 698 | ReadGlobalSetting(Settings::values.anti_aliasing); |
| 699 | ReadGlobalSetting(Settings::values.max_anisotropy); | 699 | ReadGlobalSetting(Settings::values.max_anisotropy); |
| 700 | ReadGlobalSetting(Settings::values.use_speed_limit); | ||
| 701 | ReadGlobalSetting(Settings::values.speed_limit); | 700 | ReadGlobalSetting(Settings::values.speed_limit); |
| 702 | ReadGlobalSetting(Settings::values.use_disk_shader_cache); | 701 | ReadGlobalSetting(Settings::values.use_disk_shader_cache); |
| 703 | ReadGlobalSetting(Settings::values.gpu_accuracy); | 702 | ReadGlobalSetting(Settings::values.gpu_accuracy); |
| @@ -1328,7 +1327,6 @@ void Config::SaveRendererValues() { | |||
| 1328 | static_cast<u32>(Settings::values.anti_aliasing.GetDefault()), | 1327 | static_cast<u32>(Settings::values.anti_aliasing.GetDefault()), |
| 1329 | Settings::values.anti_aliasing.UsingGlobal()); | 1328 | Settings::values.anti_aliasing.UsingGlobal()); |
| 1330 | WriteGlobalSetting(Settings::values.max_anisotropy); | 1329 | WriteGlobalSetting(Settings::values.max_anisotropy); |
| 1331 | WriteGlobalSetting(Settings::values.use_speed_limit); | ||
| 1332 | WriteGlobalSetting(Settings::values.speed_limit); | 1330 | WriteGlobalSetting(Settings::values.speed_limit); |
| 1333 | WriteGlobalSetting(Settings::values.use_disk_shader_cache); | 1331 | WriteGlobalSetting(Settings::values.use_disk_shader_cache); |
| 1334 | WriteSetting(QString::fromStdString(Settings::values.gpu_accuracy.GetLabel()), | 1332 | WriteSetting(QString::fromStdString(Settings::values.gpu_accuracy.GetLabel()), |
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 | |||
| 554 | QAction* dump_romfs_sdmc = dump_romfs_menu->addAction(tr("Dump RomFS to SDMC")); | 554 | QAction* dump_romfs_sdmc = dump_romfs_menu->addAction(tr("Dump RomFS to SDMC")); |
| 555 | QAction* copy_tid = context_menu.addAction(tr("Copy Title ID to Clipboard")); | 555 | QAction* copy_tid = context_menu.addAction(tr("Copy Title ID to Clipboard")); |
| 556 | QAction* navigate_to_gamedb_entry = context_menu.addAction(tr("Navigate to GameDB entry")); | 556 | QAction* navigate_to_gamedb_entry = context_menu.addAction(tr("Navigate to GameDB entry")); |
| 557 | #ifndef WIN32 | ||
| 558 | QMenu* shortcut_menu = context_menu.addMenu(tr("Create Shortcut")); | ||
| 559 | QAction* create_desktop_shortcut = shortcut_menu->addAction(tr("Add to Desktop")); | ||
| 560 | QAction* create_applications_menu_shortcut = | ||
| 561 | shortcut_menu->addAction(tr("Add to Applications Menu")); | ||
| 562 | #endif | ||
| 557 | context_menu.addSeparator(); | 563 | context_menu.addSeparator(); |
| 558 | QAction* properties = context_menu.addAction(tr("Properties")); | 564 | QAction* properties = context_menu.addAction(tr("Properties")); |
| 559 | 565 | ||
| @@ -619,6 +625,14 @@ void GameList::AddGamePopup(QMenu& context_menu, u64 program_id, const std::stri | |||
| 619 | connect(navigate_to_gamedb_entry, &QAction::triggered, [this, program_id]() { | 625 | connect(navigate_to_gamedb_entry, &QAction::triggered, [this, program_id]() { |
| 620 | emit NavigateToGamedbEntryRequested(program_id, compatibility_list); | 626 | emit NavigateToGamedbEntryRequested(program_id, compatibility_list); |
| 621 | }); | 627 | }); |
| 628 | #ifndef WIN32 | ||
| 629 | connect(create_desktop_shortcut, &QAction::triggered, [this, program_id, path]() { | ||
| 630 | emit CreateShortcut(program_id, path, GameListShortcutTarget::Desktop); | ||
| 631 | }); | ||
| 632 | connect(create_applications_menu_shortcut, &QAction::triggered, [this, program_id, path]() { | ||
| 633 | emit CreateShortcut(program_id, path, GameListShortcutTarget::Applications); | ||
| 634 | }); | ||
| 635 | #endif | ||
| 622 | connect(properties, &QAction::triggered, | 636 | connect(properties, &QAction::triggered, |
| 623 | [this, path]() { emit OpenPerGameGeneralRequested(path); }); | 637 | [this, path]() { emit OpenPerGameGeneralRequested(path); }); |
| 624 | }; | 638 | }; |
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 { | |||
| 52 | SDMC, | 52 | SDMC, |
| 53 | }; | 53 | }; |
| 54 | 54 | ||
| 55 | enum class GameListShortcutTarget { | ||
| 56 | Desktop, | ||
| 57 | Applications, | ||
| 58 | }; | ||
| 59 | |||
| 55 | enum class InstalledEntryType { | 60 | enum class InstalledEntryType { |
| 56 | Game, | 61 | Game, |
| 57 | Update, | 62 | Update, |
| @@ -108,6 +113,8 @@ signals: | |||
| 108 | const std::string& game_path); | 113 | const std::string& game_path); |
| 109 | void DumpRomFSRequested(u64 program_id, const std::string& game_path, DumpRomFSTarget target); | 114 | void DumpRomFSRequested(u64 program_id, const std::string& game_path, DumpRomFSTarget target); |
| 110 | void CopyTIDRequested(u64 program_id); | 115 | void CopyTIDRequested(u64 program_id); |
| 116 | void CreateShortcut(u64 program_id, const std::string& game_path, | ||
| 117 | GameListShortcutTarget target); | ||
| 111 | void NavigateToGamedbEntryRequested(u64 program_id, | 118 | void NavigateToGamedbEntryRequested(u64 program_id, |
| 112 | const CompatibilityList& compatibility_list); | 119 | const CompatibilityList& compatibility_list); |
| 113 | void OpenPerGameGeneralRequested(const std::string& file); | 120 | void OpenPerGameGeneralRequested(const std::string& file); |
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index b3ae03eaf..70552bdb8 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp | |||
| @@ -4,6 +4,8 @@ | |||
| 4 | #include <cinttypes> | 4 | #include <cinttypes> |
| 5 | #include <clocale> | 5 | #include <clocale> |
| 6 | #include <cmath> | 6 | #include <cmath> |
| 7 | #include <fstream> | ||
| 8 | #include <iostream> | ||
| 7 | #include <memory> | 9 | #include <memory> |
| 8 | #include <thread> | 10 | #include <thread> |
| 9 | #ifdef __APPLE__ | 11 | #ifdef __APPLE__ |
| @@ -1249,6 +1251,7 @@ void GMainWindow::ConnectWidgetEvents() { | |||
| 1249 | connect(game_list, &GameList::CopyTIDRequested, this, &GMainWindow::OnGameListCopyTID); | 1251 | connect(game_list, &GameList::CopyTIDRequested, this, &GMainWindow::OnGameListCopyTID); |
| 1250 | connect(game_list, &GameList::NavigateToGamedbEntryRequested, this, | 1252 | connect(game_list, &GameList::NavigateToGamedbEntryRequested, this, |
| 1251 | &GMainWindow::OnGameListNavigateToGamedbEntry); | 1253 | &GMainWindow::OnGameListNavigateToGamedbEntry); |
| 1254 | connect(game_list, &GameList::CreateShortcut, this, &GMainWindow::OnGameListCreateShortcut); | ||
| 1252 | connect(game_list, &GameList::AddDirectory, this, &GMainWindow::OnGameListAddDirectory); | 1255 | connect(game_list, &GameList::AddDirectory, this, &GMainWindow::OnGameListAddDirectory); |
| 1253 | connect(game_list_placeholder, &GameListPlaceholder::AddDirectory, this, | 1256 | connect(game_list_placeholder, &GameListPlaceholder::AddDirectory, this, |
| 1254 | &GMainWindow::OnGameListAddDirectory); | 1257 | &GMainWindow::OnGameListAddDirectory); |
| @@ -1787,6 +1790,9 @@ void GMainWindow::ShutdownGame() { | |||
| 1787 | 1790 | ||
| 1788 | AllowOSSleep(); | 1791 | AllowOSSleep(); |
| 1789 | 1792 | ||
| 1793 | // Disable unlimited frame rate | ||
| 1794 | Settings::values.use_speed_limit.SetValue(true); | ||
| 1795 | |||
| 1790 | system->SetShuttingDown(true); | 1796 | system->SetShuttingDown(true); |
| 1791 | system->DetachDebugger(); | 1797 | system->DetachDebugger(); |
| 1792 | discord_rpc->Pause(); | 1798 | discord_rpc->Pause(); |
| @@ -2376,6 +2382,152 @@ void GMainWindow::OnGameListNavigateToGamedbEntry(u64 program_id, | |||
| 2376 | QDesktopServices::openUrl(QUrl(QStringLiteral("https://yuzu-emu.org/game/") + directory)); | 2382 | QDesktopServices::openUrl(QUrl(QStringLiteral("https://yuzu-emu.org/game/") + directory)); |
| 2377 | } | 2383 | } |
| 2378 | 2384 | ||
| 2385 | void GMainWindow::OnGameListCreateShortcut(u64 program_id, const std::string& game_path, | ||
| 2386 | GameListShortcutTarget target) { | ||
| 2387 | // Get path to yuzu executable | ||
| 2388 | const QStringList args = QApplication::arguments(); | ||
| 2389 | std::filesystem::path yuzu_command = args[0].toStdString(); | ||
| 2390 | |||
| 2391 | #if defined(__linux__) || defined(__FreeBSD__) | ||
| 2392 | // If relative path, make it an absolute path | ||
| 2393 | if (yuzu_command.c_str()[0] == '.') { | ||
| 2394 | yuzu_command = Common::FS::GetCurrentDir() / yuzu_command; | ||
| 2395 | } | ||
| 2396 | |||
| 2397 | #if defined(__linux__) | ||
| 2398 | // Warn once if we are making a shortcut to a volatile AppImage | ||
| 2399 | const std::string appimage_ending = | ||
| 2400 | std::string(Common::g_scm_rev).substr(0, 9).append(".AppImage"); | ||
| 2401 | if (yuzu_command.string().ends_with(appimage_ending) && | ||
| 2402 | !UISettings::values.shortcut_already_warned) { | ||
| 2403 | if (QMessageBox::warning(this, tr("Create Shortcut"), | ||
| 2404 | tr("This will create a shortcut to the current AppImage. This may " | ||
| 2405 | "not work well if you update. Continue?"), | ||
| 2406 | QMessageBox::StandardButton::Ok | | ||
| 2407 | QMessageBox::StandardButton::Cancel) == | ||
| 2408 | QMessageBox::StandardButton::Cancel) { | ||
| 2409 | return; | ||
| 2410 | } | ||
| 2411 | UISettings::values.shortcut_already_warned = true; | ||
| 2412 | } | ||
| 2413 | #endif // __linux__ | ||
| 2414 | #endif // __linux__ || __FreeBSD__ | ||
| 2415 | |||
| 2416 | std::filesystem::path target_directory{}; | ||
| 2417 | // Determine target directory for shortcut | ||
| 2418 | #if defined(__linux__) || defined(__FreeBSD__) | ||
| 2419 | const char* home = std::getenv("HOME"); | ||
| 2420 | const std::filesystem::path home_path = (home == nullptr ? "~" : home); | ||
| 2421 | const char* xdg_data_home = std::getenv("XDG_DATA_HOME"); | ||
| 2422 | |||
| 2423 | if (target == GameListShortcutTarget::Desktop) { | ||
| 2424 | target_directory = home_path / "Desktop"; | ||
| 2425 | if (!Common::FS::IsDir(target_directory)) { | ||
| 2426 | QMessageBox::critical( | ||
| 2427 | this, tr("Create Shortcut"), | ||
| 2428 | tr("Cannot create shortcut on desktop. Path \"%1\" does not exist.") | ||
| 2429 | .arg(QString::fromStdString(target_directory)), | ||
| 2430 | QMessageBox::StandardButton::Ok); | ||
| 2431 | return; | ||
| 2432 | } | ||
| 2433 | } else if (target == GameListShortcutTarget::Applications) { | ||
| 2434 | target_directory = (xdg_data_home == nullptr ? home_path / ".local/share" : xdg_data_home) / | ||
| 2435 | "applications"; | ||
| 2436 | if (!Common::FS::CreateDirs(target_directory)) { | ||
| 2437 | QMessageBox::critical(this, tr("Create Shortcut"), | ||
| 2438 | tr("Cannot create shortcut in applications menu. Path \"%1\" " | ||
| 2439 | "does not exist and cannot be created.") | ||
| 2440 | .arg(QString::fromStdString(target_directory)), | ||
| 2441 | QMessageBox::StandardButton::Ok); | ||
| 2442 | return; | ||
| 2443 | } | ||
| 2444 | } | ||
| 2445 | #endif | ||
| 2446 | |||
| 2447 | const std::string game_file_name = std::filesystem::path(game_path).filename().string(); | ||
| 2448 | // Determine full paths for icon and shortcut | ||
| 2449 | #if defined(__linux__) || defined(__FreeBSD__) | ||
| 2450 | std::filesystem::path system_icons_path = | ||
| 2451 | (xdg_data_home == nullptr ? home_path / ".local/share/" : xdg_data_home) / | ||
| 2452 | "icons/hicolor/256x256"; | ||
| 2453 | if (!Common::FS::CreateDirs(system_icons_path)) { | ||
| 2454 | QMessageBox::critical( | ||
| 2455 | this, tr("Create Icon"), | ||
| 2456 | tr("Cannot create icon file. Path \"%1\" does not exist and cannot be created.") | ||
| 2457 | .arg(QString::fromStdString(system_icons_path)), | ||
| 2458 | QMessageBox::StandardButton::Ok); | ||
| 2459 | return; | ||
| 2460 | } | ||
| 2461 | std::filesystem::path icon_path = | ||
| 2462 | system_icons_path / (program_id == 0 ? fmt::format("yuzu-{}.png", game_file_name) | ||
| 2463 | : fmt::format("yuzu-{:016X}.png", program_id)); | ||
| 2464 | const std::filesystem::path shortcut_path = | ||
| 2465 | target_directory / (program_id == 0 ? fmt::format("yuzu-{}.desktop", game_file_name) | ||
| 2466 | : fmt::format("yuzu-{:016X}.desktop", program_id)); | ||
| 2467 | #else | ||
| 2468 | const std::filesystem::path icon_path{}; | ||
| 2469 | const std::filesystem::path shortcut_path{}; | ||
| 2470 | #endif | ||
| 2471 | |||
| 2472 | // Get title from game file | ||
| 2473 | const FileSys::PatchManager pm{program_id, system->GetFileSystemController(), | ||
| 2474 | system->GetContentProvider()}; | ||
| 2475 | const auto control = pm.GetControlMetadata(); | ||
| 2476 | const auto loader = Loader::GetLoader(*system, vfs->OpenFile(game_path, FileSys::Mode::Read)); | ||
| 2477 | |||
| 2478 | std::string title{fmt::format("{:016X}", program_id)}; | ||
| 2479 | |||
| 2480 | if (control.first != nullptr) { | ||
| 2481 | title = control.first->GetApplicationName(); | ||
| 2482 | } else { | ||
| 2483 | loader->ReadTitle(title); | ||
| 2484 | } | ||
| 2485 | |||
| 2486 | // Get icon from game file | ||
| 2487 | std::vector<u8> icon_image_file{}; | ||
| 2488 | if (control.second != nullptr) { | ||
| 2489 | icon_image_file = control.second->ReadAllBytes(); | ||
| 2490 | } else if (loader->ReadIcon(icon_image_file) != Loader::ResultStatus::Success) { | ||
| 2491 | LOG_WARNING(Frontend, "Could not read icon from {:s}", game_path); | ||
| 2492 | } | ||
| 2493 | |||
| 2494 | QImage icon_jpeg = | ||
| 2495 | QImage::fromData(icon_image_file.data(), static_cast<int>(icon_image_file.size())); | ||
| 2496 | #if defined(__linux__) || defined(__FreeBSD__) | ||
| 2497 | // Convert and write the icon as a PNG | ||
| 2498 | if (!icon_jpeg.save(QString::fromStdString(icon_path.string()))) { | ||
| 2499 | LOG_ERROR(Frontend, "Could not write icon as PNG to file"); | ||
| 2500 | } else { | ||
| 2501 | LOG_INFO(Frontend, "Wrote an icon to {}", icon_path.string()); | ||
| 2502 | } | ||
| 2503 | #endif // __linux__ | ||
| 2504 | |||
| 2505 | #if defined(__linux__) || defined(__FreeBSD__) | ||
| 2506 | const std::string comment = | ||
| 2507 | tr("Start %1 with the yuzu Emulator").arg(QString::fromStdString(title)).toStdString(); | ||
| 2508 | const std::string arguments = fmt::format("-g \"{:s}\"", game_path); | ||
| 2509 | const std::string categories = "Game;Emulator;Qt;"; | ||
| 2510 | const std::string keywords = "Switch;Nintendo;"; | ||
| 2511 | #else | ||
| 2512 | const std::string comment{}; | ||
| 2513 | const std::string arguments{}; | ||
| 2514 | const std::string categories{}; | ||
| 2515 | const std::string keywords{}; | ||
| 2516 | #endif | ||
| 2517 | if (!CreateShortcut(shortcut_path.string(), title, comment, icon_path.string(), | ||
| 2518 | yuzu_command.string(), arguments, categories, keywords)) { | ||
| 2519 | QMessageBox::critical(this, tr("Create Shortcut"), | ||
| 2520 | tr("Failed to create a shortcut at %1") | ||
| 2521 | .arg(QString::fromStdString(shortcut_path.string()))); | ||
| 2522 | return; | ||
| 2523 | } | ||
| 2524 | |||
| 2525 | LOG_INFO(Frontend, "Wrote a shortcut to {}", shortcut_path.string()); | ||
| 2526 | QMessageBox::information( | ||
| 2527 | this, tr("Create Shortcut"), | ||
| 2528 | tr("Successfully created a shortcut to %1").arg(QString::fromStdString(title))); | ||
| 2529 | } | ||
| 2530 | |||
| 2379 | void GMainWindow::OnGameListOpenDirectory(const QString& directory) { | 2531 | void GMainWindow::OnGameListOpenDirectory(const QString& directory) { |
| 2380 | std::filesystem::path fs_path; | 2532 | std::filesystem::path fs_path; |
| 2381 | if (directory == QStringLiteral("SDMC")) { | 2533 | if (directory == QStringLiteral("SDMC")) { |
| @@ -2913,9 +3065,14 @@ static QScreen* GuessCurrentScreen(QWidget* window) { | |||
| 2913 | }); | 3065 | }); |
| 2914 | } | 3066 | } |
| 2915 | 3067 | ||
| 3068 | bool GMainWindow::UsingExclusiveFullscreen() { | ||
| 3069 | return Settings::values.fullscreen_mode.GetValue() == Settings::FullscreenMode::Exclusive || | ||
| 3070 | QGuiApplication::platformName() == QStringLiteral("wayland"); | ||
| 3071 | } | ||
| 3072 | |||
| 2916 | void GMainWindow::ShowFullscreen() { | 3073 | void GMainWindow::ShowFullscreen() { |
| 2917 | const auto show_fullscreen = [](QWidget* window) { | 3074 | const auto show_fullscreen = [this](QWidget* window) { |
| 2918 | if (Settings::values.fullscreen_mode.GetValue() == Settings::FullscreenMode::Exclusive) { | 3075 | if (UsingExclusiveFullscreen()) { |
| 2919 | window->showFullScreen(); | 3076 | window->showFullScreen(); |
| 2920 | return; | 3077 | return; |
| 2921 | } | 3078 | } |
| @@ -2943,7 +3100,7 @@ void GMainWindow::ShowFullscreen() { | |||
| 2943 | 3100 | ||
| 2944 | void GMainWindow::HideFullscreen() { | 3101 | void GMainWindow::HideFullscreen() { |
| 2945 | if (ui->action_Single_Window_Mode->isChecked()) { | 3102 | if (ui->action_Single_Window_Mode->isChecked()) { |
| 2946 | if (Settings::values.fullscreen_mode.GetValue() == Settings::FullscreenMode::Exclusive) { | 3103 | if (UsingExclusiveFullscreen()) { |
| 2947 | showNormal(); | 3104 | showNormal(); |
| 2948 | restoreGeometry(UISettings::values.geometry); | 3105 | restoreGeometry(UISettings::values.geometry); |
| 2949 | } else { | 3106 | } else { |
| @@ -2957,7 +3114,7 @@ void GMainWindow::HideFullscreen() { | |||
| 2957 | statusBar()->setVisible(ui->action_Show_Status_Bar->isChecked()); | 3114 | statusBar()->setVisible(ui->action_Show_Status_Bar->isChecked()); |
| 2958 | ui->menubar->show(); | 3115 | ui->menubar->show(); |
| 2959 | } else { | 3116 | } else { |
| 2960 | if (Settings::values.fullscreen_mode.GetValue() == Settings::FullscreenMode::Exclusive) { | 3117 | if (UsingExclusiveFullscreen()) { |
| 2961 | render_window->showNormal(); | 3118 | render_window->showNormal(); |
| 2962 | render_window->restoreGeometry(UISettings::values.renderwindow_geometry); | 3119 | render_window->restoreGeometry(UISettings::values.renderwindow_geometry); |
| 2963 | } else { | 3120 | } else { |
| @@ -3294,6 +3451,38 @@ void GMainWindow::OpenPerGameConfiguration(u64 title_id, const std::string& file | |||
| 3294 | } | 3451 | } |
| 3295 | } | 3452 | } |
| 3296 | 3453 | ||
| 3454 | bool GMainWindow::CreateShortcut(const std::string& shortcut_path, const std::string& title, | ||
| 3455 | const std::string& comment, const std::string& icon_path, | ||
| 3456 | const std::string& command, const std::string& arguments, | ||
| 3457 | const std::string& categories, const std::string& keywords) { | ||
| 3458 | #if defined(__linux__) || defined(__FreeBSD__) | ||
| 3459 | // This desktop file template was writting referencing | ||
| 3460 | // https://specifications.freedesktop.org/desktop-entry-spec/desktop-entry-spec-1.0.html | ||
| 3461 | std::string shortcut_contents{}; | ||
| 3462 | shortcut_contents.append("[Desktop Entry]\n"); | ||
| 3463 | shortcut_contents.append("Type=Application\n"); | ||
| 3464 | shortcut_contents.append("Version=1.0\n"); | ||
| 3465 | shortcut_contents.append(fmt::format("Name={:s}\n", title)); | ||
| 3466 | shortcut_contents.append(fmt::format("Comment={:s}\n", comment)); | ||
| 3467 | shortcut_contents.append(fmt::format("Icon={:s}\n", icon_path)); | ||
| 3468 | shortcut_contents.append(fmt::format("TryExec={:s}\n", command)); | ||
| 3469 | shortcut_contents.append(fmt::format("Exec={:s} {:s}\n", command, arguments)); | ||
| 3470 | shortcut_contents.append(fmt::format("Categories={:s}\n", categories)); | ||
| 3471 | shortcut_contents.append(fmt::format("Keywords={:s}\n", keywords)); | ||
| 3472 | |||
| 3473 | std::ofstream shortcut_stream(shortcut_path); | ||
| 3474 | if (!shortcut_stream.is_open()) { | ||
| 3475 | LOG_WARNING(Common, "Failed to create file {:s}", shortcut_path); | ||
| 3476 | return false; | ||
| 3477 | } | ||
| 3478 | shortcut_stream << shortcut_contents; | ||
| 3479 | shortcut_stream.close(); | ||
| 3480 | |||
| 3481 | return true; | ||
| 3482 | #endif | ||
| 3483 | return false; | ||
| 3484 | } | ||
| 3485 | |||
| 3297 | void GMainWindow::OnLoadAmiibo() { | 3486 | void GMainWindow::OnLoadAmiibo() { |
| 3298 | if (emu_thread == nullptr || !emu_thread->IsRunning()) { | 3487 | if (emu_thread == nullptr || !emu_thread->IsRunning()) { |
| 3299 | return; | 3488 | 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; | |||
| 38 | class WaitTreeWidget; | 38 | class WaitTreeWidget; |
| 39 | enum class GameListOpenTarget; | 39 | enum class GameListOpenTarget; |
| 40 | enum class GameListRemoveTarget; | 40 | enum class GameListRemoveTarget; |
| 41 | enum class GameListShortcutTarget; | ||
| 41 | enum class DumpRomFSTarget; | 42 | enum class DumpRomFSTarget; |
| 42 | enum class InstalledEntryType; | 43 | enum class InstalledEntryType; |
| 43 | class GameListPlaceholder; | 44 | class GameListPlaceholder; |
| @@ -293,6 +294,8 @@ private slots: | |||
| 293 | void OnGameListCopyTID(u64 program_id); | 294 | void OnGameListCopyTID(u64 program_id); |
| 294 | void OnGameListNavigateToGamedbEntry(u64 program_id, | 295 | void OnGameListNavigateToGamedbEntry(u64 program_id, |
| 295 | const CompatibilityList& compatibility_list); | 296 | const CompatibilityList& compatibility_list); |
| 297 | void OnGameListCreateShortcut(u64 program_id, const std::string& game_path, | ||
| 298 | GameListShortcutTarget target); | ||
| 296 | void OnGameListOpenDirectory(const QString& directory); | 299 | void OnGameListOpenDirectory(const QString& directory); |
| 297 | void OnGameListAddDirectory(); | 300 | void OnGameListAddDirectory(); |
| 298 | void OnGameListShowList(bool show); | 301 | void OnGameListShowList(bool show); |
| @@ -320,6 +323,7 @@ private slots: | |||
| 320 | void OnDisplayTitleBars(bool); | 323 | void OnDisplayTitleBars(bool); |
| 321 | void InitializeHotkeys(); | 324 | void InitializeHotkeys(); |
| 322 | void ToggleFullscreen(); | 325 | void ToggleFullscreen(); |
| 326 | bool UsingExclusiveFullscreen(); | ||
| 323 | void ShowFullscreen(); | 327 | void ShowFullscreen(); |
| 324 | void HideFullscreen(); | 328 | void HideFullscreen(); |
| 325 | void ToggleWindowMode(); | 329 | void ToggleWindowMode(); |
| @@ -365,6 +369,10 @@ private: | |||
| 365 | bool CheckDarkMode(); | 369 | bool CheckDarkMode(); |
| 366 | 370 | ||
| 367 | QString GetTasStateDescription() const; | 371 | QString GetTasStateDescription() const; |
| 372 | bool CreateShortcut(const std::string& shortcut_path, const std::string& title, | ||
| 373 | const std::string& comment, const std::string& icon_path, | ||
| 374 | const std::string& command, const std::string& arguments, | ||
| 375 | const std::string& categories, const std::string& keywords); | ||
| 368 | 376 | ||
| 369 | std::unique_ptr<Ui::MainWindow> ui; | 377 | std::unique_ptr<Ui::MainWindow> ui; |
| 370 | 378 | ||
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 { | |||
| 138 | 138 | ||
| 139 | bool configuration_applied; | 139 | bool configuration_applied; |
| 140 | bool reset_to_defaults; | 140 | bool reset_to_defaults; |
| 141 | bool shortcut_already_warned{false}; | ||
| 141 | Settings::Setting<bool> disable_web_applet{true, "disable_web_applet"}; | 142 | Settings::Setting<bool> disable_web_applet{true, "disable_web_applet"}; |
| 142 | }; | 143 | }; |
| 143 | 144 | ||
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 { | |||
| 115 | 115 | ||
| 116 | void EmuWindow_SDL2::OnResize() { | 116 | void EmuWindow_SDL2::OnResize() { |
| 117 | int width, height; | 117 | int width, height; |
| 118 | SDL_GetWindowSize(render_window, &width, &height); | 118 | SDL_GL_GetDrawableSize(render_window, &width, &height); |
| 119 | UpdateCurrentFramebufferLayout(width, height); | 119 | UpdateCurrentFramebufferLayout(width, height); |
| 120 | } | 120 | } |
| 121 | 121 | ||
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 | |||
| 104 | exit(1); | 104 | exit(1); |
| 105 | } | 105 | } |
| 106 | 106 | ||
| 107 | strict_context_required = strcmp(SDL_GetCurrentVideoDriver(), "wayland") == 0; | ||
| 108 | |||
| 107 | SetWindowIcon(); | 109 | SetWindowIcon(); |
| 108 | 110 | ||
| 109 | if (fullscreen) { | 111 | if (fullscreen) { |