summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/CMakeLists.txt2
-rw-r--r--src/audio_core/CMakeLists.txt6
-rw-r--r--src/audio_core/audio_event.cpp1
-rw-r--r--src/audio_core/audio_manager.h2
-rw-r--r--src/audio_core/audio_render_manager.h2
-rw-r--r--src/audio_core/common/feature_support.h1
-rw-r--r--src/audio_core/renderer/command/effect/i3dl2_reverb.cpp1
-rw-r--r--src/audio_core/renderer/command/effect/reverb.cpp1
-rw-r--r--src/audio_core/renderer/mix/mix_context.cpp1
-rw-r--r--src/audio_core/renderer/voice/voice_context.cpp1
-rw-r--r--src/audio_core/sink/sink_stream.cpp19
-rw-r--r--src/audio_core/sink/sink_stream.h5
-rw-r--r--src/common/fs/file.cpp2
-rw-r--r--src/common/fs/fs_util.cpp1
-rw-r--r--src/common/fs/path_util.cpp1
-rw-r--r--src/common/input.h1
-rw-r--r--src/common/logging/backend.cpp2
-rw-r--r--src/common/polyfill_ranges.h530
-rw-r--r--src/common/polyfill_thread.h323
-rw-r--r--src/common/thread_worker.h5
-rw-r--r--src/common/threadsafe_queue.h4
-rw-r--r--src/core/core.cpp11
-rw-r--r--src/core/cpu_manager.h1
-rw-r--r--src/core/debugger/debugger.cpp1
-rw-r--r--src/core/file_sys/content_archive.cpp1
-rw-r--r--src/core/frontend/applets/controller.h1
-rw-r--r--src/core/frontend/emu_window.h2
-rw-r--r--src/core/hid/emulated_controller.h1
-rw-r--r--src/core/hid/emulated_devices.h1
-rw-r--r--src/core/hle/kernel/k_memory_manager.cpp4
-rw-r--r--src/core/hle/kernel/k_slab_heap.h6
-rw-r--r--src/core/hle/kernel/k_thread_local_page.h1
-rw-r--r--src/core/hle/kernel/service_thread.cpp1
-rw-r--r--src/core/hle/kernel/svc_wrap.h4
-rw-r--r--src/core/hle/service/acc/acc.cpp1
-rw-r--r--src/core/hle/service/acc/profile_manager.cpp1
-rw-r--r--src/core/hle/service/am/am.cpp4
-rw-r--r--src/core/hle/service/audio/audren_u.cpp1
-rw-r--r--src/core/hle/service/nifm/nifm.cpp41
-rw-r--r--src/core/hle/service/nvflinger/nvflinger.h1
-rw-r--r--src/core/internal_network/network_interface.cpp1
-rw-r--r--src/input_common/CMakeLists.txt6
-rw-r--r--src/input_common/drivers/gc_adapter.h2
-rw-r--r--src/input_common/drivers/mouse.cpp1
-rw-r--r--src/input_common/drivers/mouse.h2
-rw-r--r--src/input_common/drivers/tas_input.cpp1
-rw-r--r--src/shader_recompiler/CMakeLists.txt2
-rw-r--r--src/shader_recompiler/backend/spirv/emit_spirv.cpp13
-rw-r--r--src/shader_recompiler/backend/spirv/emit_spirv_control_flow.cpp2
-rw-r--r--src/shader_recompiler/backend/spirv/emit_spirv_warp.cpp35
-rw-r--r--src/shader_recompiler/frontend/ir/opcodes.h1
-rw-r--r--src/shader_recompiler/frontend/maxwell/control_flow.cpp1
-rw-r--r--src/shader_recompiler/frontend/maxwell/decode.cpp1
-rw-r--r--src/shader_recompiler/frontend/maxwell/structured_control_flow.cpp1
-rw-r--r--src/shader_recompiler/frontend/maxwell/translate/impl/floating_point_conversion_integer.cpp7
-rw-r--r--src/shader_recompiler/frontend/maxwell/translate_program.cpp81
-rw-r--r--src/shader_recompiler/frontend/maxwell/translate_program.h9
-rw-r--r--src/shader_recompiler/host_translate_info.h3
-rw-r--r--src/shader_recompiler/ir_opt/layer_pass.cpp68
-rw-r--r--src/shader_recompiler/ir_opt/passes.h1
-rw-r--r--src/shader_recompiler/shader_info.h3
-rw-r--r--src/video_core/buffer_cache/buffer_base.h2
-rw-r--r--src/video_core/buffer_cache/buffer_cache.h1
-rw-r--r--src/video_core/control/channel_state_cache.h2
-rw-r--r--src/video_core/engines/maxwell_3d.cpp48
-rw-r--r--src/video_core/engines/maxwell_3d.h1
-rw-r--r--src/video_core/gpu_thread.cpp2
-rw-r--r--src/video_core/gpu_thread.h1
-rw-r--r--src/video_core/rasterizer_interface.h2
-rw-r--r--src/video_core/renderer_opengl/gl_device.cpp1
-rw-r--r--src/video_core/renderer_opengl/gl_shader_cache.cpp37
-rw-r--r--src/video_core/renderer_opengl/gl_shader_cache.h1
-rw-r--r--src/video_core/renderer_vulkan/fixed_pipeline_state.cpp1
-rw-r--r--src/video_core/renderer_vulkan/vk_blit_screen.cpp1
-rw-r--r--src/video_core/renderer_vulkan/vk_descriptor_pool.cpp1
-rw-r--r--src/video_core/renderer_vulkan/vk_master_semaphore.h1
-rw-r--r--src/video_core/renderer_vulkan/vk_pipeline_cache.cpp37
-rw-r--r--src/video_core/renderer_vulkan/vk_render_pass_cache.h2
-rw-r--r--src/video_core/renderer_vulkan/vk_scheduler.cpp2
-rw-r--r--src/video_core/renderer_vulkan/vk_scheduler.h1
-rw-r--r--src/video_core/renderer_vulkan/vk_swapchain.cpp1
-rw-r--r--src/video_core/shader_cache.h1
-rw-r--r--src/video_core/shader_environment.cpp1
-rw-r--r--src/video_core/shader_environment.h2
-rw-r--r--src/video_core/texture_cache/formatter.cpp1
-rw-r--r--src/video_core/texture_cache/render_targets.h2
-rw-r--r--src/video_core/texture_cache/slot_vector.h1
-rw-r--r--src/video_core/texture_cache/texture_cache_base.h3
-rw-r--r--src/video_core/textures/astc.cpp1
-rw-r--r--src/video_core/transform_feedback.cpp1
-rw-r--r--src/video_core/vulkan_common/vulkan_device.cpp121
-rw-r--r--src/video_core/vulkan_common/vulkan_device.h16
-rw-r--r--src/video_core/vulkan_common/vulkan_instance.cpp28
-rw-r--r--src/video_core/vulkan_common/vulkan_memory_allocator.cpp1
-rw-r--r--src/video_core/vulkan_common/vulkan_surface.cpp38
-rw-r--r--src/yuzu/CMakeLists.txt6
-rw-r--r--src/yuzu/bootmanager.cpp6
-rw-r--r--src/yuzu/bootmanager.h1
-rw-r--r--src/yuzu/configuration/configure_graphics.cpp2
-rw-r--r--src/yuzu/main.cpp6
-rw-r--r--src/yuzu/multiplayer/chat_room.h1
-rw-r--r--src/yuzu/startup_checks.cpp2
-rw-r--r--src/yuzu_cmd/emu_window/emu_window_sdl2_vk.cpp27
103 files changed, 1455 insertions, 194 deletions
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index fc53e76b9..140415474 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -113,6 +113,8 @@ else()
113 113
114 $<$<CXX_COMPILER_ID:Clang>:-Wno-braced-scalar-init> 114 $<$<CXX_COMPILER_ID:Clang>:-Wno-braced-scalar-init>
115 $<$<CXX_COMPILER_ID:Clang>:-Wno-unused-private-field> 115 $<$<CXX_COMPILER_ID:Clang>:-Wno-unused-private-field>
116 $<$<CXX_COMPILER_ID:AppleClang>:-Wno-braced-scalar-init>
117 $<$<CXX_COMPILER_ID:AppleClang>:-Wno-unused-private-field>
116 ) 118 )
117 119
118 if (ARCHITECTURE_x86_64) 120 if (ARCHITECTURE_x86_64)
diff --git a/src/audio_core/CMakeLists.txt b/src/audio_core/CMakeLists.txt
index 0a8c4280c..0a9d9ec29 100644
--- a/src/audio_core/CMakeLists.txt
+++ b/src/audio_core/CMakeLists.txt
@@ -227,7 +227,11 @@ if(ENABLE_CUBEB)
227 target_compile_definitions(audio_core PRIVATE -DHAVE_CUBEB=1) 227 target_compile_definitions(audio_core PRIVATE -DHAVE_CUBEB=1)
228endif() 228endif()
229if(ENABLE_SDL2) 229if(ENABLE_SDL2)
230 target_link_libraries(audio_core PRIVATE SDL2) 230 if (YUZU_USE_EXTERNAL_SDL2)
231 target_link_libraries(audio_core PRIVATE SDL2-static)
232 else()
233 target_link_libraries(audio_core PRIVATE SDL2)
234 endif()
231 target_compile_definitions(audio_core PRIVATE HAVE_SDL2) 235 target_compile_definitions(audio_core PRIVATE HAVE_SDL2)
232endif() 236endif()
233 237
diff --git a/src/audio_core/audio_event.cpp b/src/audio_core/audio_event.cpp
index 424049c7a..d15568e1f 100644
--- a/src/audio_core/audio_event.cpp
+++ b/src/audio_core/audio_event.cpp
@@ -3,6 +3,7 @@
3 3
4#include "audio_core/audio_event.h" 4#include "audio_core/audio_event.h"
5#include "common/assert.h" 5#include "common/assert.h"
6#include "common/polyfill_ranges.h"
6 7
7namespace AudioCore { 8namespace AudioCore {
8 9
diff --git a/src/audio_core/audio_manager.h b/src/audio_core/audio_manager.h
index abf077de4..02270242a 100644
--- a/src/audio_core/audio_manager.h
+++ b/src/audio_core/audio_manager.h
@@ -9,6 +9,8 @@
9#include <mutex> 9#include <mutex>
10#include <thread> 10#include <thread>
11 11
12#include "common/polyfill_thread.h"
13
12#include "audio_core/audio_event.h" 14#include "audio_core/audio_event.h"
13 15
14union Result; 16union Result;
diff --git a/src/audio_core/audio_render_manager.h b/src/audio_core/audio_render_manager.h
index bf4837190..fffa5944d 100644
--- a/src/audio_core/audio_render_manager.h
+++ b/src/audio_core/audio_render_manager.h
@@ -7,6 +7,8 @@
7#include <memory> 7#include <memory>
8#include <mutex> 8#include <mutex>
9 9
10#include "common/polyfill_thread.h"
11
10#include "audio_core/common/common.h" 12#include "audio_core/common/common.h"
11#include "audio_core/renderer/system_manager.h" 13#include "audio_core/renderer/system_manager.h"
12#include "core/hle/service/audio/errors.h" 14#include "core/hle/service/audio/errors.h"
diff --git a/src/audio_core/common/feature_support.h b/src/audio_core/common/feature_support.h
index 55c9e690d..e71905ae8 100644
--- a/src/audio_core/common/feature_support.h
+++ b/src/audio_core/common/feature_support.h
@@ -10,6 +10,7 @@
10#include "common/assert.h" 10#include "common/assert.h"
11#include "common/common_funcs.h" 11#include "common/common_funcs.h"
12#include "common/common_types.h" 12#include "common/common_types.h"
13#include "common/polyfill_ranges.h"
13 14
14namespace AudioCore { 15namespace AudioCore {
15constexpr u32 CurrentRevision = 11; 16constexpr u32 CurrentRevision = 11;
diff --git a/src/audio_core/renderer/command/effect/i3dl2_reverb.cpp b/src/audio_core/renderer/command/effect/i3dl2_reverb.cpp
index c4bf3943a..2187d8a65 100644
--- a/src/audio_core/renderer/command/effect/i3dl2_reverb.cpp
+++ b/src/audio_core/renderer/command/effect/i3dl2_reverb.cpp
@@ -5,6 +5,7 @@
5 5
6#include "audio_core/renderer/adsp/command_list_processor.h" 6#include "audio_core/renderer/adsp/command_list_processor.h"
7#include "audio_core/renderer/command/effect/i3dl2_reverb.h" 7#include "audio_core/renderer/command/effect/i3dl2_reverb.h"
8#include "common/polyfill_ranges.h"
8 9
9namespace AudioCore::AudioRenderer { 10namespace AudioCore::AudioRenderer {
10 11
diff --git a/src/audio_core/renderer/command/effect/reverb.cpp b/src/audio_core/renderer/command/effect/reverb.cpp
index fe2b1eb43..427489214 100644
--- a/src/audio_core/renderer/command/effect/reverb.cpp
+++ b/src/audio_core/renderer/command/effect/reverb.cpp
@@ -6,6 +6,7 @@
6 6
7#include "audio_core/renderer/adsp/command_list_processor.h" 7#include "audio_core/renderer/adsp/command_list_processor.h"
8#include "audio_core/renderer/command/effect/reverb.h" 8#include "audio_core/renderer/command/effect/reverb.h"
9#include "common/polyfill_ranges.h"
9 10
10namespace AudioCore::AudioRenderer { 11namespace AudioCore::AudioRenderer {
11 12
diff --git a/src/audio_core/renderer/mix/mix_context.cpp b/src/audio_core/renderer/mix/mix_context.cpp
index 2427c83ed..35b748ede 100644
--- a/src/audio_core/renderer/mix/mix_context.cpp
+++ b/src/audio_core/renderer/mix/mix_context.cpp
@@ -5,6 +5,7 @@
5 5
6#include "audio_core/renderer/mix/mix_context.h" 6#include "audio_core/renderer/mix/mix_context.h"
7#include "audio_core/renderer/splitter/splitter_context.h" 7#include "audio_core/renderer/splitter/splitter_context.h"
8#include "common/polyfill_ranges.h"
8 9
9namespace AudioCore::AudioRenderer { 10namespace AudioCore::AudioRenderer {
10 11
diff --git a/src/audio_core/renderer/voice/voice_context.cpp b/src/audio_core/renderer/voice/voice_context.cpp
index a501a677d..16a3e839d 100644
--- a/src/audio_core/renderer/voice/voice_context.cpp
+++ b/src/audio_core/renderer/voice/voice_context.cpp
@@ -4,6 +4,7 @@
4#include <ranges> 4#include <ranges>
5 5
6#include "audio_core/renderer/voice/voice_context.h" 6#include "audio_core/renderer/voice/voice_context.h"
7#include "common/polyfill_ranges.h"
7 8
8namespace AudioCore::AudioRenderer { 9namespace AudioCore::AudioRenderer {
9 10
diff --git a/src/audio_core/sink/sink_stream.cpp b/src/audio_core/sink/sink_stream.cpp
index 849f862b0..06c2a876e 100644
--- a/src/audio_core/sink/sink_stream.cpp
+++ b/src/audio_core/sink/sink_stream.cpp
@@ -170,8 +170,8 @@ void SinkStream::ProcessAudioIn(std::span<const s16> input_buffer, std::size_t n
170 170
171 // Get the minimum frames available between the currently playing buffer, and the 171 // Get the minimum frames available between the currently playing buffer, and the
172 // amount we have left to fill 172 // amount we have left to fill
173 size_t frames_available{std::min(playing_buffer.frames - playing_buffer.frames_played, 173 size_t frames_available{std::min<u64>(playing_buffer.frames - playing_buffer.frames_played,
174 num_frames - frames_written)}; 174 num_frames - frames_written)};
175 175
176 samples_buffer.Push(&input_buffer[frames_written * frame_size], 176 samples_buffer.Push(&input_buffer[frames_written * frame_size],
177 frames_available * frame_size); 177 frames_available * frame_size);
@@ -241,8 +241,8 @@ void SinkStream::ProcessAudioOutAndRender(std::span<s16> output_buffer, std::siz
241 241
242 // Get the minimum frames available between the currently playing buffer, and the 242 // Get the minimum frames available between the currently playing buffer, and the
243 // amount we have left to fill 243 // amount we have left to fill
244 size_t frames_available{std::min(playing_buffer.frames - playing_buffer.frames_played, 244 size_t frames_available{std::min<u64>(playing_buffer.frames - playing_buffer.frames_played,
245 num_frames - frames_written)}; 245 num_frames - frames_written)};
246 246
247 samples_buffer.Pop(&output_buffer[frames_written * frame_size], 247 samples_buffer.Pop(&output_buffer[frames_written * frame_size],
248 frames_available * frame_size); 248 frames_available * frame_size);
@@ -266,19 +266,20 @@ void SinkStream::ProcessAudioOutAndRender(std::span<s16> output_buffer, std::siz
266} 266}
267 267
268void SinkStream::Stall() { 268void SinkStream::Stall() {
269 if (stalled) { 269 std::scoped_lock lk{stall_guard};
270 if (stalled_lock) {
270 return; 271 return;
271 } 272 }
272 stalled = true; 273 stalled_lock = system.StallProcesses();
273 system.StallProcesses();
274} 274}
275 275
276void SinkStream::Unstall() { 276void SinkStream::Unstall() {
277 if (!stalled) { 277 std::scoped_lock lk{stall_guard};
278 if (!stalled_lock) {
278 return; 279 return;
279 } 280 }
280 system.UnstallProcesses(); 281 system.UnstallProcesses();
281 stalled = false; 282 stalled_lock.unlock();
282} 283}
283 284
284} // namespace AudioCore::Sink 285} // namespace AudioCore::Sink
diff --git a/src/audio_core/sink/sink_stream.h b/src/audio_core/sink/sink_stream.h
index 38a4b2f51..5fea72ab7 100644
--- a/src/audio_core/sink/sink_stream.h
+++ b/src/audio_core/sink/sink_stream.h
@@ -6,6 +6,7 @@
6#include <array> 6#include <array>
7#include <atomic> 7#include <atomic>
8#include <memory> 8#include <memory>
9#include <mutex>
9#include <span> 10#include <span>
10#include <vector> 11#include <vector>
11 12
@@ -240,8 +241,8 @@ private:
240 f32 system_volume{1.0f}; 241 f32 system_volume{1.0f};
241 /// Set via IAudioDevice service calls 242 /// Set via IAudioDevice service calls
242 f32 device_volume{1.0f}; 243 f32 device_volume{1.0f};
243 /// True if coretiming has been stalled 244 std::mutex stall_guard;
244 bool stalled{false}; 245 std::unique_lock<std::mutex> stalled_lock;
245}; 246};
246 247
247using SinkStreamPtr = std::unique_ptr<SinkStream>; 248using SinkStreamPtr = std::unique_ptr<SinkStream>;
diff --git a/src/common/fs/file.cpp b/src/common/fs/file.cpp
index fa8422c41..656b03cc5 100644
--- a/src/common/fs/file.cpp
+++ b/src/common/fs/file.cpp
@@ -1,6 +1,8 @@
1// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project 1// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later 2// SPDX-License-Identifier: GPL-2.0-or-later
3 3
4#include <vector>
5
4#include "common/fs/file.h" 6#include "common/fs/file.h"
5#include "common/fs/fs.h" 7#include "common/fs/fs.h"
6#include "common/logging/log.h" 8#include "common/logging/log.h"
diff --git a/src/common/fs/fs_util.cpp b/src/common/fs/fs_util.cpp
index eb4ac1deb..813a713c3 100644
--- a/src/common/fs/fs_util.cpp
+++ b/src/common/fs/fs_util.cpp
@@ -4,6 +4,7 @@
4#include <algorithm> 4#include <algorithm>
5 5
6#include "common/fs/fs_util.h" 6#include "common/fs/fs_util.h"
7#include "common/polyfill_ranges.h"
7 8
8namespace Common::FS { 9namespace Common::FS {
9 10
diff --git a/src/common/fs/path_util.cpp b/src/common/fs/path_util.cpp
index 1074f2421..defa3e918 100644
--- a/src/common/fs/path_util.cpp
+++ b/src/common/fs/path_util.cpp
@@ -2,6 +2,7 @@
2// SPDX-License-Identifier: GPL-2.0-or-later 2// SPDX-License-Identifier: GPL-2.0-or-later
3 3
4#include <algorithm> 4#include <algorithm>
5#include <sstream>
5#include <unordered_map> 6#include <unordered_map>
6 7
7#include "common/fs/fs.h" 8#include "common/fs/fs.h"
diff --git a/src/common/input.h b/src/common/input.h
index 449e0193f..fc14fd7bf 100644
--- a/src/common/input.h
+++ b/src/common/input.h
@@ -8,6 +8,7 @@
8#include <string> 8#include <string>
9#include <unordered_map> 9#include <unordered_map>
10#include <utility> 10#include <utility>
11#include <vector>
11#include "common/logging/log.h" 12#include "common/logging/log.h"
12#include "common/param_package.h" 13#include "common/param_package.h"
13#include "common/uuid.h" 14#include "common/uuid.h"
diff --git a/src/common/logging/backend.cpp b/src/common/logging/backend.cpp
index 15d92505e..2a3bded40 100644
--- a/src/common/logging/backend.cpp
+++ b/src/common/logging/backend.cpp
@@ -4,7 +4,6 @@
4#include <atomic> 4#include <atomic>
5#include <chrono> 5#include <chrono>
6#include <climits> 6#include <climits>
7#include <stop_token>
8#include <thread> 7#include <thread>
9 8
10#include <fmt/format.h> 9#include <fmt/format.h>
@@ -18,6 +17,7 @@
18#include "common/fs/fs_paths.h" 17#include "common/fs/fs_paths.h"
19#include "common/fs/path_util.h" 18#include "common/fs/path_util.h"
20#include "common/literals.h" 19#include "common/literals.h"
20#include "common/polyfill_thread.h"
21#include "common/thread.h" 21#include "common/thread.h"
22 22
23#include "common/logging/backend.h" 23#include "common/logging/backend.h"
diff --git a/src/common/polyfill_ranges.h b/src/common/polyfill_ranges.h
new file mode 100644
index 000000000..ca44bfaef
--- /dev/null
+++ b/src/common/polyfill_ranges.h
@@ -0,0 +1,530 @@
1// SPDX-FileCopyrightText: 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4//
5// TODO: remove this file when ranges are supported by all compilation targets
6//
7
8#pragma once
9
10#include <algorithm>
11#include <utility>
12#include <version>
13
14#ifndef __cpp_lib_ranges
15
16namespace std {
17namespace ranges {
18
19template <typename T>
20concept range = requires(T& t) {
21 begin(t);
22 end(t);
23};
24
25template <typename T>
26concept input_range = range<T>;
27
28template <typename T>
29concept output_range = range<T>;
30
31template <range R>
32using range_difference_t = ptrdiff_t;
33
34//
35// find, find_if, find_if_not
36//
37
38struct find_fn {
39 template <typename Iterator, typename T, typename Proj = std::identity>
40 constexpr Iterator operator()(Iterator first, Iterator last, const T& value,
41 Proj proj = {}) const {
42 for (; first != last; ++first) {
43 if (std::invoke(proj, *first) == value) {
44 return first;
45 }
46 }
47 return first;
48 }
49
50 template <ranges::input_range R, typename T, typename Proj = std::identity>
51 constexpr ranges::iterator_t<R> operator()(R&& r, const T& value, Proj proj = {}) const {
52 return operator()(ranges::begin(r), ranges::end(r), value, std::ref(proj));
53 }
54};
55
56struct find_if_fn {
57 template <typename Iterator, typename Proj = std::identity, typename Pred>
58 constexpr Iterator operator()(Iterator first, Iterator last, Pred pred, Proj proj = {}) const {
59 for (; first != last; ++first) {
60 if (std::invoke(pred, std::invoke(proj, *first))) {
61 return first;
62 }
63 }
64 return first;
65 }
66
67 template <ranges::input_range R, typename Proj = std::identity, typename Pred>
68 constexpr ranges::iterator_t<R> operator()(R&& r, Pred pred, Proj proj = {}) const {
69 return operator()(ranges::begin(r), ranges::end(r), std::ref(pred), std::ref(proj));
70 }
71};
72
73struct find_if_not_fn {
74 template <typename Iterator, typename Proj = std::identity, typename Pred>
75 constexpr Iterator operator()(Iterator first, Iterator last, Pred pred, Proj proj = {}) const {
76 for (; first != last; ++first) {
77 if (!std::invoke(pred, std::invoke(proj, *first))) {
78 return first;
79 }
80 }
81 return first;
82 }
83
84 template <ranges::input_range R, typename Proj = std::identity, typename Pred>
85 constexpr ranges::iterator_t<R> operator()(R&& r, Pred pred, Proj proj = {}) const {
86 return operator()(ranges::begin(r), ranges::end(r), std::ref(pred), std::ref(proj));
87 }
88};
89
90inline constexpr find_fn find;
91inline constexpr find_if_fn find_if;
92inline constexpr find_if_not_fn find_if_not;
93
94//
95// any_of, all_of, none_of
96//
97
98struct all_of_fn {
99 template <typename Iterator, typename Proj = std::identity, typename Pred>
100 constexpr bool operator()(Iterator first, Iterator last, Pred pred, Proj proj = {}) const {
101 return ranges::find_if_not(first, last, std::ref(pred), std::ref(proj)) == last;
102 }
103
104 template <ranges::input_range R, typename Proj = std::identity, typename Pred>
105 constexpr bool operator()(R&& r, Pred pred, Proj proj = {}) const {
106 return operator()(ranges::begin(r), ranges::end(r), std::ref(pred), std::ref(proj));
107 }
108};
109
110struct any_of_fn {
111 template <typename Iterator, typename Proj = std::identity, typename Pred>
112 constexpr bool operator()(Iterator first, Iterator last, Pred pred, Proj proj = {}) const {
113 return ranges::find_if(first, last, std::ref(pred), std::ref(proj)) != last;
114 }
115
116 template <ranges::input_range R, typename Proj = std::identity, typename Pred>
117 constexpr bool operator()(R&& r, Pred pred, Proj proj = {}) const {
118 return operator()(ranges::begin(r), ranges::end(r), std::ref(pred), std::ref(proj));
119 }
120};
121
122struct none_of_fn {
123 template <typename Iterator, typename Proj = std::identity, typename Pred>
124 constexpr bool operator()(Iterator first, Iterator last, Pred pred, Proj proj = {}) const {
125 return ranges::find_if(first, last, std::ref(pred), std::ref(proj)) == last;
126 }
127
128 template <ranges::input_range R, typename Proj = std::identity, typename Pred>
129 constexpr bool operator()(R&& r, Pred pred, Proj proj = {}) const {
130 return operator()(ranges::begin(r), ranges::end(r), std::ref(pred), std::ref(proj));
131 }
132};
133
134inline constexpr any_of_fn any_of;
135inline constexpr all_of_fn all_of;
136inline constexpr none_of_fn none_of;
137
138//
139// count, count_if
140//
141
142struct count_fn {
143 template <typename Iterator, typename T, typename Proj = std::identity>
144 constexpr ptrdiff_t operator()(Iterator first, Iterator last, const T& value,
145 Proj proj = {}) const {
146 ptrdiff_t counter = 0;
147 for (; first != last; ++first)
148 if (std::invoke(proj, *first) == value)
149 ++counter;
150 return counter;
151 }
152
153 template <ranges::input_range R, typename T, typename Proj = std::identity>
154 constexpr ptrdiff_t operator()(R&& r, const T& value, Proj proj = {}) const {
155 return operator()(ranges::begin(r), ranges::end(r), value, std::ref(proj));
156 }
157};
158
159struct count_if_fn {
160 template <typename Iterator, typename Proj = std::identity, typename Pred>
161 constexpr ptrdiff_t operator()(Iterator first, Iterator last, Pred pred, Proj proj = {}) const {
162 ptrdiff_t counter = 0;
163 for (; first != last; ++first)
164 if (std::invoke(pred, std::invoke(proj, *first)))
165 ++counter;
166 return counter;
167 }
168
169 template <ranges::input_range R, typename Proj = std::identity, typename Pred>
170 constexpr ptrdiff_t operator()(R&& r, Pred pred, Proj proj = {}) const {
171 return operator()(ranges::begin(r), ranges::end(r), std::ref(pred), std::ref(proj));
172 }
173};
174
175inline constexpr count_fn count;
176inline constexpr count_if_fn count_if;
177
178//
179// transform
180//
181
182struct transform_fn {
183 template <typename InputIterator, typename OutputIterator, typename F,
184 typename Proj = std::identity>
185 constexpr void operator()(InputIterator first1, InputIterator last1, OutputIterator result,
186 F op, Proj proj = {}) const {
187 for (; first1 != last1; ++first1, (void)++result) {
188 *result = std::invoke(op, std::invoke(proj, *first1));
189 }
190 }
191
192 template <ranges::input_range R, typename OutputIterator, typename F,
193 typename Proj = std::identity>
194 constexpr void operator()(R&& r, OutputIterator result, F op, Proj proj = {}) const {
195 return operator()(ranges::begin(r), ranges::end(r), result, std::ref(op), std::ref(proj));
196 }
197};
198
199inline constexpr transform_fn transform;
200
201//
202// sort
203//
204
205struct sort_fn {
206 template <typename Iterator, typename Comp = ranges::less, typename Proj = std::identity>
207 constexpr void operator()(Iterator first, Iterator last, Comp comp = {}, Proj proj = {}) const {
208 if (first == last)
209 return;
210
211 Iterator last_iter = ranges::next(first, last);
212 std::sort(first, last_iter,
213 [&](auto& lhs, auto& rhs) { return comp(proj(lhs), proj(rhs)); });
214 }
215
216 template <ranges::input_range R, typename Comp = ranges::less, typename Proj = std::identity>
217 constexpr void operator()(R&& r, Comp comp = {}, Proj proj = {}) const {
218 return operator()(ranges::begin(r), ranges::end(r), std::move(comp), std::move(proj));
219 }
220};
221
222inline constexpr sort_fn sort;
223
224//
225// fill
226//
227
228struct fill_fn {
229 template <typename T, typename OutputIterator>
230 constexpr OutputIterator operator()(OutputIterator first, OutputIterator last,
231 const T& value) const {
232 while (first != last) {
233 *first++ = value;
234 }
235
236 return first;
237 }
238
239 template <typename T, ranges::output_range R>
240 constexpr ranges::iterator_t<R> operator()(R&& r, const T& value) const {
241 return operator()(ranges::begin(r), ranges::end(r), value);
242 }
243};
244
245inline constexpr fill_fn fill;
246
247//
248// for_each
249//
250
251struct for_each_fn {
252 template <typename Iterator, typename Proj = std::identity, typename Fun>
253 constexpr void operator()(Iterator first, Iterator last, Fun f, Proj proj = {}) const {
254 for (; first != last; ++first) {
255 std::invoke(f, std::invoke(proj, *first));
256 }
257 }
258
259 template <ranges::input_range R, typename Proj = std::identity, typename Fun>
260 constexpr void operator()(R&& r, Fun f, Proj proj = {}) const {
261 return operator()(ranges::begin(r), ranges::end(r), std::move(f), std::ref(proj));
262 }
263};
264
265inline constexpr for_each_fn for_each;
266
267//
268// min_element, max_element
269//
270
271struct min_element_fn {
272 template <typename Iterator, typename Proj = std::identity, typename Comp = ranges::less>
273 constexpr Iterator operator()(Iterator first, Iterator last, Comp comp = {},
274 Proj proj = {}) const {
275 if (first == last) {
276 return last;
277 }
278
279 auto smallest = first;
280 ++first;
281 for (; first != last; ++first) {
282 if (!std::invoke(comp, std::invoke(proj, *smallest), std::invoke(proj, *first))) {
283 smallest = first;
284 }
285 }
286 return smallest;
287 }
288
289 template <ranges::input_range R, typename Proj = std::identity, typename Comp = ranges::less>
290 constexpr ranges::iterator_t<R> operator()(R&& r, Comp comp = {}, Proj proj = {}) const {
291 return operator()(ranges::begin(r), ranges::end(r), std::ref(comp), std::ref(proj));
292 }
293};
294
295struct max_element_fn {
296 template <typename Iterator, typename Proj = std::identity, typename Comp = ranges::less>
297 constexpr Iterator operator()(Iterator first, Iterator last, Comp comp = {},
298 Proj proj = {}) const {
299 if (first == last) {
300 return last;
301 }
302
303 auto largest = first;
304 ++first;
305 for (; first != last; ++first) {
306 if (std::invoke(comp, std::invoke(proj, *largest), std::invoke(proj, *first))) {
307 largest = first;
308 }
309 }
310 return largest;
311 }
312
313 template <ranges::input_range R, typename Proj = std::identity, typename Comp = ranges::less>
314 constexpr ranges::iterator_t<R> operator()(R&& r, Comp comp = {}, Proj proj = {}) const {
315 return operator()(ranges::begin(r), ranges::end(r), std::ref(comp), std::ref(proj));
316 }
317};
318
319inline constexpr min_element_fn min_element;
320inline constexpr max_element_fn max_element;
321
322//
323// replace, replace_if
324//
325
326struct replace_fn {
327 template <typename Iterator, typename T1, typename T2, typename Proj = std::identity>
328 constexpr Iterator operator()(Iterator first, Iterator last, const T1& old_value,
329 const T2& new_value, Proj proj = {}) const {
330 for (; first != last; ++first) {
331 if (old_value == std::invoke(proj, *first)) {
332 *first = new_value;
333 }
334 }
335 return first;
336 }
337
338 template <ranges::input_range R, typename T1, typename T2, typename Proj = std::identity>
339 constexpr ranges::iterator_t<R> operator()(R&& r, const T1& old_value, const T2& new_value,
340 Proj proj = {}) const {
341 return operator()(ranges::begin(r), ranges::end(r), old_value, new_value, std::move(proj));
342 }
343};
344
345struct replace_if_fn {
346 template <typename Iterator, typename T, typename Proj = std::identity, typename Pred>
347 constexpr Iterator operator()(Iterator first, Iterator last, Pred pred, const T& new_value,
348 Proj proj = {}) const {
349 for (; first != last; ++first) {
350 if (!!std::invoke(pred, std::invoke(proj, *first))) {
351 *first = new_value;
352 }
353 }
354 return std::move(first);
355 }
356
357 template <ranges::input_range R, typename T, typename Proj = std::identity, typename Pred>
358 constexpr ranges::iterator_t<R> operator()(R&& r, Pred pred, const T& new_value,
359 Proj proj = {}) const {
360 return operator()(ranges::begin(r), ranges::end(r), std::move(pred), new_value,
361 std::move(proj));
362 }
363};
364
365inline constexpr replace_fn replace;
366inline constexpr replace_if_fn replace_if;
367
368//
369// copy, copy_if
370//
371
372struct copy_fn {
373 template <typename InputIterator, typename OutputIterator>
374 constexpr void operator()(InputIterator first, InputIterator last,
375 OutputIterator result) const {
376 for (; first != last; ++first, (void)++result) {
377 *result = *first;
378 }
379 }
380
381 template <ranges::input_range R, typename OutputIterator>
382 constexpr void operator()(R&& r, OutputIterator result) const {
383 return operator()(ranges::begin(r), ranges::end(r), std::move(result));
384 }
385};
386
387struct copy_if_fn {
388 template <typename InputIterator, typename OutputIterator, typename Proj = std::identity,
389 typename Pred>
390 constexpr void operator()(InputIterator first, InputIterator last, OutputIterator result,
391 Pred pred, Proj proj = {}) const {
392 for (; first != last; ++first) {
393 if (std::invoke(pred, std::invoke(proj, *first))) {
394 *result = *first;
395 ++result;
396 }
397 }
398 }
399
400 template <ranges::input_range R, typename OutputIterator, typename Proj = std::identity,
401 typename Pred>
402 constexpr void operator()(R&& r, OutputIterator result, Pred pred, Proj proj = {}) const {
403 return operator()(ranges::begin(r), ranges::end(r), std::move(result), std::ref(pred),
404 std::ref(proj));
405 }
406};
407
408inline constexpr copy_fn copy;
409inline constexpr copy_if_fn copy_if;
410
411//
412// generate
413//
414
415struct generate_fn {
416 template <typename Iterator, typename F>
417 constexpr Iterator operator()(Iterator first, Iterator last, F gen) const {
418 for (; first != last; *first = std::invoke(gen), ++first)
419 ;
420 return first;
421 }
422
423 template <typename R, std::copy_constructible F>
424 requires std::invocable<F&> && ranges::output_range<R>
425 constexpr ranges::iterator_t<R> operator()(R&& r, F gen) const {
426 return operator()(ranges::begin(r), ranges::end(r), std::move(gen));
427 }
428};
429
430inline constexpr generate_fn generate;
431
432//
433// lower_bound, upper_bound
434//
435
436struct lower_bound_fn {
437 template <typename Iterator, typename T, typename Proj = std::identity,
438 typename Comp = ranges::less>
439 constexpr Iterator operator()(Iterator first, Iterator last, const T& value, Comp comp = {},
440 Proj proj = {}) const {
441 Iterator it;
442 std::ptrdiff_t _count, _step;
443 _count = std::distance(first, last);
444
445 while (_count > 0) {
446 it = first;
447 _step = _count / 2;
448 ranges::advance(it, _step, last);
449 if (comp(std::invoke(proj, *it), value)) {
450 first = ++it;
451 _count -= _step + 1;
452 } else {
453 _count = _step;
454 }
455 }
456 return first;
457 }
458
459 template <ranges::input_range R, typename T, typename Proj = std::identity,
460 typename Comp = ranges::less>
461 constexpr ranges::iterator_t<R> operator()(R&& r, const T& value, Comp comp = {},
462 Proj proj = {}) const {
463 return operator()(ranges::begin(r), ranges::end(r), value, std::ref(comp), std::ref(proj));
464 }
465};
466
467struct upper_bound_fn {
468 template <typename Iterator, typename T, typename Proj = std::identity,
469 typename Comp = ranges::less>
470 constexpr Iterator operator()(Iterator first, Iterator last, const T& value, Comp comp = {},
471 Proj proj = {}) const {
472 Iterator it;
473 std::ptrdiff_t _count, _step;
474 _count = std::distance(first, last);
475
476 while (_count > 0) {
477 it = first;
478 _step = _count / 2;
479 ranges::advance(it, _step, last);
480 if (!comp(value, std::invoke(proj, *it))) {
481 first = ++it;
482 _count -= _step + 1;
483 } else {
484 _count = _step;
485 }
486 }
487 return first;
488 }
489
490 template <ranges::input_range R, typename T, typename Proj = std::identity,
491 typename Comp = ranges::less>
492 constexpr ranges::iterator_t<R> operator()(R&& r, const T& value, Comp comp = {},
493 Proj proj = {}) const {
494 return operator()(ranges::begin(r), ranges::end(r), value, std::ref(comp), std::ref(proj));
495 }
496};
497
498inline constexpr lower_bound_fn lower_bound;
499inline constexpr upper_bound_fn upper_bound;
500
501//
502// adjacent_find
503//
504
505struct adjacent_find_fn {
506 template <typename Iterator, typename Proj = std::identity, typename Pred = ranges::equal_to>
507 constexpr Iterator operator()(Iterator first, Iterator last, Pred pred = {},
508 Proj proj = {}) const {
509 if (first == last)
510 return first;
511 auto _next = ranges::next(first);
512 for (; _next != last; ++_next, ++first)
513 if (std::invoke(pred, std::invoke(proj, *first), std::invoke(proj, *_next)))
514 return first;
515 return _next;
516 }
517
518 template <ranges::input_range R, typename Proj = std::identity,
519 typename Pred = ranges::equal_to>
520 constexpr ranges::iterator_t<R> operator()(R&& r, Pred pred = {}, Proj proj = {}) const {
521 return operator()(ranges::begin(r), ranges::end(r), std::ref(pred), std::ref(proj));
522 }
523};
524
525inline constexpr adjacent_find_fn adjacent_find;
526
527} // namespace ranges
528} // namespace std
529
530#endif
diff --git a/src/common/polyfill_thread.h b/src/common/polyfill_thread.h
new file mode 100644
index 000000000..5a8d1ce08
--- /dev/null
+++ b/src/common/polyfill_thread.h
@@ -0,0 +1,323 @@
1// SPDX-FileCopyrightText: 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4//
5// TODO: remove this file when jthread is supported by all compilation targets
6//
7
8#pragma once
9
10#include <version>
11
12#ifdef __cpp_lib_jthread
13
14#include <stop_token>
15#include <thread>
16
17namespace Common {
18
19template <typename Condvar, typename Lock, typename Pred>
20void CondvarWait(Condvar& cv, Lock& lock, std::stop_token token, Pred&& pred) {
21 cv.wait(lock, token, std::move(pred));
22}
23
24} // namespace Common
25
26#else
27
28#include <atomic>
29#include <functional>
30#include <list>
31#include <memory>
32#include <mutex>
33#include <optional>
34#include <thread>
35#include <type_traits>
36
37namespace std {
38namespace polyfill {
39
40using stop_state_callbacks = list<function<void()>>;
41
42class stop_state {
43public:
44 stop_state() = default;
45 ~stop_state() = default;
46
47 bool request_stop() {
48 stop_state_callbacks callbacks;
49
50 {
51 scoped_lock lk{m_lock};
52
53 if (m_stop_requested.load()) {
54 // Already set, nothing to do
55 return false;
56 }
57
58 // Set as requested
59 m_stop_requested = true;
60
61 // Copy callback list
62 callbacks = m_callbacks;
63 }
64
65 for (auto callback : callbacks) {
66 callback();
67 }
68
69 return true;
70 }
71
72 bool stop_requested() const {
73 return m_stop_requested.load();
74 }
75
76 stop_state_callbacks::const_iterator insert_callback(function<void()> f) {
77 stop_state_callbacks::const_iterator ret{};
78 bool should_run{};
79
80 {
81 scoped_lock lk{m_lock};
82 should_run = m_stop_requested.load();
83 m_callbacks.push_front(f);
84 ret = m_callbacks.begin();
85 }
86
87 if (should_run) {
88 f();
89 }
90
91 return ret;
92 }
93
94 void remove_callback(stop_state_callbacks::const_iterator it) {
95 scoped_lock lk{m_lock};
96 m_callbacks.erase(it);
97 }
98
99private:
100 mutex m_lock;
101 atomic<bool> m_stop_requested;
102 stop_state_callbacks m_callbacks;
103};
104
105} // namespace polyfill
106
107class stop_token;
108class stop_source;
109struct nostopstate_t {
110 explicit nostopstate_t() = default;
111};
112inline constexpr nostopstate_t nostopstate{};
113
114template <class Callback>
115class stop_callback;
116
117class stop_token {
118public:
119 stop_token() noexcept = default;
120
121 stop_token(const stop_token&) noexcept = default;
122 stop_token(stop_token&&) noexcept = default;
123 stop_token& operator=(const stop_token&) noexcept = default;
124 stop_token& operator=(stop_token&&) noexcept = default;
125 ~stop_token() = default;
126
127 void swap(stop_token& other) noexcept {
128 m_stop_state.swap(other.m_stop_state);
129 }
130
131 [[nodiscard]] bool stop_requested() const noexcept {
132 return m_stop_state && m_stop_state->stop_requested();
133 }
134 [[nodiscard]] bool stop_possible() const noexcept {
135 return m_stop_state != nullptr;
136 }
137
138private:
139 friend class stop_source;
140 template <typename Callback>
141 friend class stop_callback;
142 stop_token(shared_ptr<polyfill::stop_state> stop_state) : m_stop_state(move(stop_state)) {}
143
144private:
145 shared_ptr<polyfill::stop_state> m_stop_state;
146};
147
148class stop_source {
149public:
150 stop_source() : m_stop_state(make_shared<polyfill::stop_state>()) {}
151 explicit stop_source(nostopstate_t) noexcept {}
152
153 stop_source(const stop_source&) noexcept = default;
154 stop_source(stop_source&&) noexcept = default;
155 stop_source& operator=(const stop_source&) noexcept = default;
156 stop_source& operator=(stop_source&&) noexcept = default;
157 ~stop_source() = default;
158 void swap(stop_source& other) noexcept {
159 m_stop_state.swap(other.m_stop_state);
160 }
161
162 [[nodiscard]] stop_token get_token() const noexcept {
163 return stop_token(m_stop_state);
164 }
165 [[nodiscard]] bool stop_possible() const noexcept {
166 return m_stop_state != nullptr;
167 }
168 [[nodiscard]] bool stop_requested() const noexcept {
169 return m_stop_state && m_stop_state->stop_requested();
170 }
171 bool request_stop() noexcept {
172 return m_stop_state && m_stop_state->request_stop();
173 }
174
175private:
176 friend class jthread;
177 explicit stop_source(shared_ptr<polyfill::stop_state> stop_state)
178 : m_stop_state(move(stop_state)) {}
179
180private:
181 shared_ptr<polyfill::stop_state> m_stop_state;
182};
183
184template <typename Callback>
185class stop_callback {
186 static_assert(is_nothrow_destructible_v<Callback>);
187 static_assert(is_invocable_v<Callback>);
188
189public:
190 using callback_type = Callback;
191
192 template <typename C>
193 requires constructible_from<Callback, C>
194 explicit stop_callback(const stop_token& st,
195 C&& cb) noexcept(is_nothrow_constructible_v<Callback, C>)
196 : m_stop_state(st.m_stop_state) {
197 if (m_stop_state) {
198 m_callback = m_stop_state->insert_callback(move(cb));
199 }
200 }
201 template <typename C>
202 requires constructible_from<Callback, C>
203 explicit stop_callback(stop_token&& st,
204 C&& cb) noexcept(is_nothrow_constructible_v<Callback, C>)
205 : m_stop_state(move(st.m_stop_state)) {
206 if (m_stop_state) {
207 m_callback = m_stop_state->insert_callback(move(cb));
208 }
209 }
210 ~stop_callback() {
211 if (m_stop_state && m_callback) {
212 m_stop_state->remove_callback(*m_callback);
213 }
214 }
215
216 stop_callback(const stop_callback&) = delete;
217 stop_callback(stop_callback&&) = delete;
218 stop_callback& operator=(const stop_callback&) = delete;
219 stop_callback& operator=(stop_callback&&) = delete;
220
221private:
222 shared_ptr<polyfill::stop_state> m_stop_state;
223 optional<polyfill::stop_state_callbacks::const_iterator> m_callback;
224};
225
226template <typename Callback>
227stop_callback(stop_token, Callback) -> stop_callback<Callback>;
228
229class jthread {
230public:
231 using id = thread::id;
232 using native_handle_type = thread::native_handle_type;
233
234 jthread() noexcept = default;
235
236 template <typename F, typename... Args,
237 typename = enable_if_t<!is_same_v<remove_cvref_t<F>, jthread>>>
238 explicit jthread(F&& f, Args&&... args)
239 : m_stop_state(make_shared<polyfill::stop_state>()),
240 m_thread(make_thread(move(f), move(args)...)) {}
241
242 ~jthread() {
243 if (joinable()) {
244 request_stop();
245 join();
246 }
247 }
248
249 jthread(const jthread&) = delete;
250 jthread(jthread&&) noexcept = default;
251 jthread& operator=(const jthread&) = delete;
252
253 jthread& operator=(jthread&& other) noexcept {
254 m_thread.swap(other.m_thread);
255 m_stop_state.swap(other.m_stop_state);
256 return *this;
257 }
258
259 void swap(jthread& other) noexcept {
260 m_thread.swap(other.m_thread);
261 m_stop_state.swap(other.m_stop_state);
262 }
263 [[nodiscard]] bool joinable() const noexcept {
264 return m_thread.joinable();
265 }
266 void join() {
267 m_thread.join();
268 }
269 void detach() {
270 m_thread.detach();
271 m_stop_state.reset();
272 }
273
274 [[nodiscard]] id get_id() const noexcept {
275 return m_thread.get_id();
276 }
277 [[nodiscard]] native_handle_type native_handle() {
278 return m_thread.native_handle();
279 }
280 [[nodiscard]] stop_source get_stop_source() noexcept {
281 return stop_source(m_stop_state);
282 }
283 [[nodiscard]] stop_token get_stop_token() const noexcept {
284 return stop_source(m_stop_state).get_token();
285 }
286 bool request_stop() noexcept {
287 return get_stop_source().request_stop();
288 }
289 [[nodiscard]] static unsigned int hardware_concurrency() noexcept {
290 return thread::hardware_concurrency();
291 }
292
293private:
294 template <typename F, typename... Args>
295 thread make_thread(F&& f, Args&&... args) {
296 if constexpr (is_invocable_v<decay_t<F>, stop_token, decay_t<Args>...>) {
297 return thread(move(f), get_stop_token(), move(args)...);
298 } else {
299 return thread(move(f), move(args)...);
300 }
301 }
302
303 shared_ptr<polyfill::stop_state> m_stop_state;
304 thread m_thread;
305};
306
307} // namespace std
308
309namespace Common {
310
311template <typename Condvar, typename Lock, typename Pred>
312void CondvarWait(Condvar& cv, Lock& lock, std::stop_token token, Pred pred) {
313 if (token.stop_requested()) {
314 return;
315 }
316
317 std::stop_callback callback(token, [&] { cv.notify_all(); });
318 cv.wait(lock, [&] { return pred() || token.stop_requested(); });
319}
320
321} // namespace Common
322
323#endif
diff --git a/src/common/thread_worker.h b/src/common/thread_worker.h
index 62c60f724..260ad44e4 100644
--- a/src/common/thread_worker.h
+++ b/src/common/thread_worker.h
@@ -7,13 +7,13 @@
7#include <condition_variable> 7#include <condition_variable>
8#include <functional> 8#include <functional>
9#include <mutex> 9#include <mutex>
10#include <stop_token>
11#include <string> 10#include <string>
12#include <thread> 11#include <thread>
13#include <type_traits> 12#include <type_traits>
14#include <vector> 13#include <vector>
15#include <queue> 14#include <queue>
16 15
16#include "common/polyfill_thread.h"
17#include "common/thread.h" 17#include "common/thread.h"
18#include "common/unique_function.h" 18#include "common/unique_function.h"
19 19
@@ -47,7 +47,8 @@ public:
47 if (requests.empty()) { 47 if (requests.empty()) {
48 wait_condition.notify_all(); 48 wait_condition.notify_all();
49 } 49 }
50 condition.wait(lock, stop_token, [this] { return !requests.empty(); }); 50 Common::CondvarWait(condition, lock, stop_token,
51 [this] { return !requests.empty(); });
51 if (stop_token.stop_requested()) { 52 if (stop_token.stop_requested()) {
52 break; 53 break;
53 } 54 }
diff --git a/src/common/threadsafe_queue.h b/src/common/threadsafe_queue.h
index 053798e79..2ef1da064 100644
--- a/src/common/threadsafe_queue.h
+++ b/src/common/threadsafe_queue.h
@@ -12,6 +12,8 @@
12#include <mutex> 12#include <mutex>
13#include <utility> 13#include <utility>
14 14
15#include "common/polyfill_thread.h"
16
15namespace Common { 17namespace Common {
16template <typename T, bool with_stop_token = false> 18template <typename T, bool with_stop_token = false>
17class SPSCQueue { 19class SPSCQueue {
@@ -97,7 +99,7 @@ public:
97 T PopWait(std::stop_token stop_token) { 99 T PopWait(std::stop_token stop_token) {
98 if (Empty()) { 100 if (Empty()) {
99 std::unique_lock lock{cv_mutex}; 101 std::unique_lock lock{cv_mutex};
100 cv.wait(lock, stop_token, [this] { return !Empty(); }); 102 Common::CondvarWait(cv, lock, stop_token, [this] { return !Empty(); });
101 } 103 }
102 if (stop_token.stop_requested()) { 104 if (stop_token.stop_requested()) {
103 return T{}; 105 return T{};
diff --git a/src/core/core.cpp b/src/core/core.cpp
index d8934be52..94d4e2212 100644
--- a/src/core/core.cpp
+++ b/src/core/core.cpp
@@ -189,7 +189,7 @@ struct System::Impl {
189 189
190 kernel.Suspend(false); 190 kernel.Suspend(false);
191 core_timing.SyncPause(false); 191 core_timing.SyncPause(false);
192 is_paused = false; 192 is_paused.store(false, std::memory_order_relaxed);
193 193
194 return status; 194 return status;
195 } 195 }
@@ -200,14 +200,13 @@ struct System::Impl {
200 200
201 core_timing.SyncPause(true); 201 core_timing.SyncPause(true);
202 kernel.Suspend(true); 202 kernel.Suspend(true);
203 is_paused = true; 203 is_paused.store(true, std::memory_order_relaxed);
204 204
205 return status; 205 return status;
206 } 206 }
207 207
208 bool IsPaused() const { 208 bool IsPaused() const {
209 std::unique_lock lk(suspend_guard); 209 return is_paused.load(std::memory_order_relaxed);
210 return is_paused;
211 } 210 }
212 211
213 std::unique_lock<std::mutex> StallProcesses() { 212 std::unique_lock<std::mutex> StallProcesses() {
@@ -218,7 +217,7 @@ struct System::Impl {
218 } 217 }
219 218
220 void UnstallProcesses() { 219 void UnstallProcesses() {
221 if (!is_paused) { 220 if (!IsPaused()) {
222 core_timing.SyncPause(false); 221 core_timing.SyncPause(false);
223 kernel.Suspend(false); 222 kernel.Suspend(false);
224 } 223 }
@@ -465,7 +464,7 @@ struct System::Impl {
465 } 464 }
466 465
467 mutable std::mutex suspend_guard; 466 mutable std::mutex suspend_guard;
468 bool is_paused{}; 467 std::atomic_bool is_paused{};
469 std::atomic<bool> is_shutting_down{}; 468 std::atomic<bool> is_shutting_down{};
470 469
471 Timing::CoreTiming core_timing; 470 Timing::CoreTiming core_timing;
diff --git a/src/core/cpu_manager.h b/src/core/cpu_manager.h
index 95ea3ef39..374367468 100644
--- a/src/core/cpu_manager.h
+++ b/src/core/cpu_manager.h
@@ -10,6 +10,7 @@
10#include <thread> 10#include <thread>
11 11
12#include "common/fiber.h" 12#include "common/fiber.h"
13#include "common/polyfill_thread.h"
13#include "common/thread.h" 14#include "common/thread.h"
14#include "core/hardware_properties.h" 15#include "core/hardware_properties.h"
15 16
diff --git a/src/core/debugger/debugger.cpp b/src/core/debugger/debugger.cpp
index 1a8e02e6a..a9675df76 100644
--- a/src/core/debugger/debugger.cpp
+++ b/src/core/debugger/debugger.cpp
@@ -9,6 +9,7 @@
9#include <boost/process/async_pipe.hpp> 9#include <boost/process/async_pipe.hpp>
10 10
11#include "common/logging/log.h" 11#include "common/logging/log.h"
12#include "common/polyfill_thread.h"
12#include "common/thread.h" 13#include "common/thread.h"
13#include "core/core.h" 14#include "core/core.h"
14#include "core/debugger/debugger.h" 15#include "core/debugger/debugger.h"
diff --git a/src/core/file_sys/content_archive.cpp b/src/core/file_sys/content_archive.cpp
index 78e56bbbd..50303fe42 100644
--- a/src/core/file_sys/content_archive.cpp
+++ b/src/core/file_sys/content_archive.cpp
@@ -7,6 +7,7 @@
7#include <utility> 7#include <utility>
8 8
9#include "common/logging/log.h" 9#include "common/logging/log.h"
10#include "common/polyfill_ranges.h"
10#include "core/crypto/aes_util.h" 11#include "core/crypto/aes_util.h"
11#include "core/crypto/ctr_encryption_layer.h" 12#include "core/crypto/ctr_encryption_layer.h"
12#include "core/crypto/key_manager.h" 13#include "core/crypto/key_manager.h"
diff --git a/src/core/frontend/applets/controller.h b/src/core/frontend/applets/controller.h
index 1d2850ad5..71698df74 100644
--- a/src/core/frontend/applets/controller.h
+++ b/src/core/frontend/applets/controller.h
@@ -4,6 +4,7 @@
4#pragma once 4#pragma once
5 5
6#include <functional> 6#include <functional>
7#include <vector>
7 8
8#include "common/common_types.h" 9#include "common/common_types.h"
9 10
diff --git a/src/core/frontend/emu_window.h b/src/core/frontend/emu_window.h
index ac1906d5e..95363b645 100644
--- a/src/core/frontend/emu_window.h
+++ b/src/core/frontend/emu_window.h
@@ -17,6 +17,8 @@ enum class WindowSystemType {
17 Windows, 17 Windows,
18 X11, 18 X11,
19 Wayland, 19 Wayland,
20 Cocoa,
21 Android,
20}; 22};
21 23
22/** 24/**
diff --git a/src/core/hid/emulated_controller.h b/src/core/hid/emulated_controller.h
index d004ca56a..3f83108d3 100644
--- a/src/core/hid/emulated_controller.h
+++ b/src/core/hid/emulated_controller.h
@@ -8,6 +8,7 @@
8#include <memory> 8#include <memory>
9#include <mutex> 9#include <mutex>
10#include <unordered_map> 10#include <unordered_map>
11#include <vector>
11 12
12#include "common/common_types.h" 13#include "common/common_types.h"
13#include "common/input.h" 14#include "common/input.h"
diff --git a/src/core/hid/emulated_devices.h b/src/core/hid/emulated_devices.h
index 4149eeced..4cdbf9dc6 100644
--- a/src/core/hid/emulated_devices.h
+++ b/src/core/hid/emulated_devices.h
@@ -8,6 +8,7 @@
8#include <memory> 8#include <memory>
9#include <mutex> 9#include <mutex>
10#include <unordered_map> 10#include <unordered_map>
11#include <vector>
11 12
12#include "common/common_types.h" 13#include "common/common_types.h"
13#include "common/input.h" 14#include "common/input.h"
diff --git a/src/core/hle/kernel/k_memory_manager.cpp b/src/core/hle/kernel/k_memory_manager.cpp
index c4bf306e8..bd33571da 100644
--- a/src/core/hle/kernel/k_memory_manager.cpp
+++ b/src/core/hle/kernel/k_memory_manager.cpp
@@ -225,8 +225,8 @@ Result KMemoryManager::AllocatePageGroupImpl(KPageGroup* out, size_t num_pages,
225 ON_RESULT_FAILURE { 225 ON_RESULT_FAILURE {
226 for (const auto& it : out->Nodes()) { 226 for (const auto& it : out->Nodes()) {
227 auto& manager = this->GetManager(it.GetAddress()); 227 auto& manager = this->GetManager(it.GetAddress());
228 const size_t node_num_pages = 228 const size_t node_num_pages = std::min<u64>(
229 std::min(it.GetNumPages(), (manager.GetEndAddress() - it.GetAddress()) / PageSize); 229 it.GetNumPages(), (manager.GetEndAddress() - it.GetAddress()) / PageSize);
230 manager.Free(it.GetAddress(), node_num_pages); 230 manager.Free(it.GetAddress(), node_num_pages);
231 } 231 }
232 out->Finalize(); 232 out->Finalize();
diff --git a/src/core/hle/kernel/k_slab_heap.h b/src/core/hle/kernel/k_slab_heap.h
index a8c77a7d4..68469b041 100644
--- a/src/core/hle/kernel/k_slab_heap.h
+++ b/src/core/hle/kernel/k_slab_heap.h
@@ -6,6 +6,7 @@
6#include <atomic> 6#include <atomic>
7 7
8#include "common/assert.h" 8#include "common/assert.h"
9#include "common/atomic_ops.h"
9#include "common/common_funcs.h" 10#include "common/common_funcs.h"
10#include "common/common_types.h" 11#include "common/common_types.h"
11#include "common/spin_lock.h" 12#include "common/spin_lock.h"
@@ -82,16 +83,13 @@ private:
82 83
83private: 84private:
84 void UpdatePeakImpl(uintptr_t obj) { 85 void UpdatePeakImpl(uintptr_t obj) {
85 static_assert(std::atomic_ref<uintptr_t>::is_always_lock_free);
86 std::atomic_ref<uintptr_t> peak_ref(m_peak);
87
88 const uintptr_t alloc_peak = obj + this->GetObjectSize(); 86 const uintptr_t alloc_peak = obj + this->GetObjectSize();
89 uintptr_t cur_peak = m_peak; 87 uintptr_t cur_peak = m_peak;
90 do { 88 do {
91 if (alloc_peak <= cur_peak) { 89 if (alloc_peak <= cur_peak) {
92 break; 90 break;
93 } 91 }
94 } while (!peak_ref.compare_exchange_strong(cur_peak, alloc_peak)); 92 } while (!Common::AtomicCompareAndSwap(&m_peak, alloc_peak, cur_peak, cur_peak));
95 } 93 }
96 94
97public: 95public:
diff --git a/src/core/hle/kernel/k_thread_local_page.h b/src/core/hle/kernel/k_thread_local_page.h
index 5d466ace7..fe0cff084 100644
--- a/src/core/hle/kernel/k_thread_local_page.h
+++ b/src/core/hle/kernel/k_thread_local_page.h
@@ -10,6 +10,7 @@
10#include "common/assert.h" 10#include "common/assert.h"
11#include "common/common_types.h" 11#include "common/common_types.h"
12#include "common/intrusive_red_black_tree.h" 12#include "common/intrusive_red_black_tree.h"
13#include "common/polyfill_ranges.h"
13#include "core/hle/kernel/memory_types.h" 14#include "core/hle/kernel/memory_types.h"
14#include "core/hle/kernel/slab_helpers.h" 15#include "core/hle/kernel/slab_helpers.h"
15#include "core/hle/result.h" 16#include "core/hle/result.h"
diff --git a/src/core/hle/kernel/service_thread.cpp b/src/core/hle/kernel/service_thread.cpp
index e6e41ac34..0690f9a1c 100644
--- a/src/core/hle/kernel/service_thread.cpp
+++ b/src/core/hle/kernel/service_thread.cpp
@@ -7,6 +7,7 @@
7#include <thread> 7#include <thread>
8#include <vector> 8#include <vector>
9 9
10#include "common/polyfill_thread.h"
10#include "common/scope_exit.h" 11#include "common/scope_exit.h"
11#include "common/thread.h" 12#include "common/thread.h"
12#include "core/hle/ipc_helpers.h" 13#include "core/hle/ipc_helpers.h"
diff --git a/src/core/hle/kernel/svc_wrap.h b/src/core/hle/kernel/svc_wrap.h
index 3730937fe..1ea8c7fbc 100644
--- a/src/core/hle/kernel/svc_wrap.h
+++ b/src/core/hle/kernel/svc_wrap.h
@@ -82,7 +82,7 @@ void SvcWrap64(Core::System& system) {
82} 82}
83 83
84// Used by ControlCodeMemory 84// Used by ControlCodeMemory
85template <Result func(Core::System&, Handle, u32, u64, u64, Svc::MemoryPermission)> 85template <Result func(Core::System&, Handle, u32, VAddr, size_t, Svc::MemoryPermission)>
86void SvcWrap64(Core::System& system) { 86void SvcWrap64(Core::System& system) {
87 FuncReturn(system, func(system, static_cast<Handle>(Param(system, 0)), 87 FuncReturn(system, func(system, static_cast<Handle>(Param(system, 0)),
88 static_cast<u32>(Param(system, 1)), Param(system, 2), Param(system, 3), 88 static_cast<u32>(Param(system, 1)), Param(system, 2), Param(system, 3),
@@ -327,7 +327,7 @@ void SvcWrap64(Core::System& system) {
327} 327}
328 328
329// Used by CreateCodeMemory 329// Used by CreateCodeMemory
330template <Result func(Core::System&, Handle*, u64, u64)> 330template <Result func(Core::System&, Handle*, VAddr, size_t)>
331void SvcWrap64(Core::System& system) { 331void SvcWrap64(Core::System& system) {
332 u32 param_1 = 0; 332 u32 param_1 = 0;
333 const u32 retval = func(system, &param_1, Param(system, 1), Param(system, 2)).raw; 333 const u32 retval = func(system, &param_1, Param(system, 1), Param(system, 2)).raw;
diff --git a/src/core/hle/service/acc/acc.cpp b/src/core/hle/service/acc/acc.cpp
index 85a3f0802..6d1084fd1 100644
--- a/src/core/hle/service/acc/acc.cpp
+++ b/src/core/hle/service/acc/acc.cpp
@@ -7,6 +7,7 @@
7#include "common/fs/file.h" 7#include "common/fs/file.h"
8#include "common/fs/path_util.h" 8#include "common/fs/path_util.h"
9#include "common/logging/log.h" 9#include "common/logging/log.h"
10#include "common/polyfill_ranges.h"
10#include "common/string_util.h" 11#include "common/string_util.h"
11#include "common/swap.h" 12#include "common/swap.h"
12#include "core/constants.h" 13#include "core/constants.h"
diff --git a/src/core/hle/service/acc/profile_manager.cpp b/src/core/hle/service/acc/profile_manager.cpp
index 481e0d141..97f7c6688 100644
--- a/src/core/hle/service/acc/profile_manager.cpp
+++ b/src/core/hle/service/acc/profile_manager.cpp
@@ -9,6 +9,7 @@
9#include "common/fs/file.h" 9#include "common/fs/file.h"
10#include "common/fs/fs.h" 10#include "common/fs/fs.h"
11#include "common/fs/path_util.h" 11#include "common/fs/path_util.h"
12#include "common/polyfill_ranges.h"
12#include "common/settings.h" 13#include "common/settings.h"
13#include "core/hle/service/acc/profile_manager.h" 14#include "core/hle/service/acc/profile_manager.h"
14 15
diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp
index 8ea7fd760..22999c942 100644
--- a/src/core/hle/service/am/am.cpp
+++ b/src/core/hle/service/am/am.cpp
@@ -1125,7 +1125,7 @@ void IStorageAccessor::Write(Kernel::HLERequestContext& ctx) {
1125 1125
1126 const u64 offset{rp.Pop<u64>()}; 1126 const u64 offset{rp.Pop<u64>()};
1127 const std::vector<u8> data{ctx.ReadBuffer()}; 1127 const std::vector<u8> data{ctx.ReadBuffer()};
1128 const std::size_t size{std::min(data.size(), backing.GetSize() - offset)}; 1128 const std::size_t size{std::min<u64>(data.size(), backing.GetSize() - offset)};
1129 1129
1130 LOG_DEBUG(Service_AM, "called, offset={}, size={}", offset, size); 1130 LOG_DEBUG(Service_AM, "called, offset={}, size={}", offset, size);
1131 1131
@@ -1149,7 +1149,7 @@ void IStorageAccessor::Read(Kernel::HLERequestContext& ctx) {
1149 IPC::RequestParser rp{ctx}; 1149 IPC::RequestParser rp{ctx};
1150 1150
1151 const u64 offset{rp.Pop<u64>()}; 1151 const u64 offset{rp.Pop<u64>()};
1152 const std::size_t size{std::min(ctx.GetWriteBufferSize(), backing.GetSize() - offset)}; 1152 const std::size_t size{std::min<u64>(ctx.GetWriteBufferSize(), backing.GetSize() - offset)};
1153 1153
1154 LOG_DEBUG(Service_AM, "called, offset={}, size={}", offset, size); 1154 LOG_DEBUG(Service_AM, "called, offset={}, size={}", offset, size);
1155 1155
diff --git a/src/core/hle/service/audio/audren_u.cpp b/src/core/hle/service/audio/audren_u.cpp
index 034ee273f..3a1c231b6 100644
--- a/src/core/hle/service/audio/audren_u.cpp
+++ b/src/core/hle/service/audio/audren_u.cpp
@@ -14,6 +14,7 @@
14#include "common/bit_util.h" 14#include "common/bit_util.h"
15#include "common/common_funcs.h" 15#include "common/common_funcs.h"
16#include "common/logging/log.h" 16#include "common/logging/log.h"
17#include "common/polyfill_ranges.h"
17#include "common/string_util.h" 18#include "common/string_util.h"
18#include "core/core.h" 19#include "core/core.h"
19#include "core/hle/ipc_helpers.h" 20#include "core/hle/ipc_helpers.h"
diff --git a/src/core/hle/service/nifm/nifm.cpp b/src/core/hle/service/nifm/nifm.cpp
index e3ef06481..4fa9f51a6 100644
--- a/src/core/hle/service/nifm/nifm.cpp
+++ b/src/core/hle/service/nifm/nifm.cpp
@@ -129,6 +129,9 @@ static_assert(sizeof(NifmNetworkProfileData) == 0x18E,
129 "NifmNetworkProfileData has incorrect size."); 129 "NifmNetworkProfileData has incorrect size.");
130#pragma pack(pop) 130#pragma pack(pop)
131 131
132constexpr Result ResultPendingConnection{ErrorModule::NIFM, 111};
133constexpr Result ResultNetworkCommunicationDisabled{ErrorModule::NIFM, 1111};
134
132class IScanRequest final : public ServiceFramework<IScanRequest> { 135class IScanRequest final : public ServiceFramework<IScanRequest> {
133public: 136public:
134 explicit IScanRequest(Core::System& system_) : ServiceFramework{system_, "IScanRequest"} { 137 explicit IScanRequest(Core::System& system_) : ServiceFramework{system_, "IScanRequest"} {
@@ -192,6 +195,10 @@ private:
192 void Submit(Kernel::HLERequestContext& ctx) { 195 void Submit(Kernel::HLERequestContext& ctx) {
193 LOG_WARNING(Service_NIFM, "(STUBBED) called"); 196 LOG_WARNING(Service_NIFM, "(STUBBED) called");
194 197
198 if (state == RequestState::NotSubmitted) {
199 UpdateState(RequestState::Pending);
200 }
201
195 IPC::ResponseBuilder rb{ctx, 2}; 202 IPC::ResponseBuilder rb{ctx, 2};
196 rb.Push(ResultSuccess); 203 rb.Push(ResultSuccess);
197 } 204 }
@@ -201,19 +208,32 @@ private:
201 208
202 IPC::ResponseBuilder rb{ctx, 3}; 209 IPC::ResponseBuilder rb{ctx, 3};
203 rb.Push(ResultSuccess); 210 rb.Push(ResultSuccess);
204 211 rb.PushEnum(state);
205 if (Network::GetHostIPv4Address().has_value()) {
206 rb.PushEnum(RequestState::Connected);
207 } else {
208 rb.PushEnum(RequestState::NotSubmitted);
209 }
210 } 212 }
211 213
212 void GetResult(Kernel::HLERequestContext& ctx) { 214 void GetResult(Kernel::HLERequestContext& ctx) {
213 LOG_WARNING(Service_NIFM, "(STUBBED) called"); 215 LOG_WARNING(Service_NIFM, "(STUBBED) called");
214 216
217 const auto result = [this] {
218 const auto has_connection = Network::GetHostIPv4Address().has_value();
219 switch (state) {
220 case RequestState::NotSubmitted:
221 return has_connection ? ResultSuccess : ResultNetworkCommunicationDisabled;
222 case RequestState::Pending:
223 if (has_connection) {
224 UpdateState(RequestState::Connected);
225 } else {
226 UpdateState(RequestState::Error);
227 }
228 return ResultPendingConnection;
229 case RequestState::Connected:
230 default:
231 return ResultSuccess;
232 }
233 }();
234
215 IPC::ResponseBuilder rb{ctx, 2}; 235 IPC::ResponseBuilder rb{ctx, 2};
216 rb.Push(ResultSuccess); 236 rb.Push(result);
217 } 237 }
218 238
219 void GetSystemEventReadableHandles(Kernel::HLERequestContext& ctx) { 239 void GetSystemEventReadableHandles(Kernel::HLERequestContext& ctx) {
@@ -252,8 +272,15 @@ private:
252 rb.Push<u32>(0); 272 rb.Push<u32>(0);
253 } 273 }
254 274
275 void UpdateState(RequestState new_state) {
276 state = new_state;
277 event1->Signal();
278 }
279
255 KernelHelpers::ServiceContext service_context; 280 KernelHelpers::ServiceContext service_context;
256 281
282 RequestState state;
283
257 Kernel::KEvent* event1; 284 Kernel::KEvent* event1;
258 Kernel::KEvent* event2; 285 Kernel::KEvent* event2;
259}; 286};
diff --git a/src/core/hle/service/nvflinger/nvflinger.h b/src/core/hle/service/nvflinger/nvflinger.h
index 460bef976..9b22397db 100644
--- a/src/core/hle/service/nvflinger/nvflinger.h
+++ b/src/core/hle/service/nvflinger/nvflinger.h
@@ -11,6 +11,7 @@
11#include <vector> 11#include <vector>
12 12
13#include "common/common_types.h" 13#include "common/common_types.h"
14#include "common/polyfill_thread.h"
14#include "core/hle/result.h" 15#include "core/hle/result.h"
15#include "core/hle/service/kernel_helpers.h" 16#include "core/hle/service/kernel_helpers.h"
16 17
diff --git a/src/core/internal_network/network_interface.cpp b/src/core/internal_network/network_interface.cpp
index 057fd3661..7b8e510a2 100644
--- a/src/core/internal_network/network_interface.cpp
+++ b/src/core/internal_network/network_interface.cpp
@@ -9,6 +9,7 @@
9#include "common/bit_cast.h" 9#include "common/bit_cast.h"
10#include "common/common_types.h" 10#include "common/common_types.h"
11#include "common/logging/log.h" 11#include "common/logging/log.h"
12#include "common/polyfill_ranges.h"
12#include "common/settings.h" 13#include "common/settings.h"
13#include "common/string_util.h" 14#include "common/string_util.h"
14#include "core/internal_network/network_interface.h" 15#include "core/internal_network/network_interface.h"
diff --git a/src/input_common/CMakeLists.txt b/src/input_common/CMakeLists.txt
index 77bfe4b58..e41da2726 100644
--- a/src/input_common/CMakeLists.txt
+++ b/src/input_common/CMakeLists.txt
@@ -56,7 +56,11 @@ if (ENABLE_SDL2)
56 drivers/sdl_driver.cpp 56 drivers/sdl_driver.cpp
57 drivers/sdl_driver.h 57 drivers/sdl_driver.h
58 ) 58 )
59 target_link_libraries(input_common PRIVATE SDL2) 59 if (YUZU_USE_EXTERNAL_SDL2)
60 target_link_libraries(input_common PRIVATE SDL2-static)
61 else()
62 target_link_libraries(input_common PRIVATE SDL2)
63 endif()
60 target_compile_definitions(input_common PRIVATE HAVE_SDL2) 64 target_compile_definitions(input_common PRIVATE HAVE_SDL2)
61endif() 65endif()
62 66
diff --git a/src/input_common/drivers/gc_adapter.h b/src/input_common/drivers/gc_adapter.h
index 7f81767f7..b5270fd0b 100644
--- a/src/input_common/drivers/gc_adapter.h
+++ b/src/input_common/drivers/gc_adapter.h
@@ -5,10 +5,10 @@
5 5
6#include <array> 6#include <array>
7#include <memory> 7#include <memory>
8#include <stop_token>
9#include <string> 8#include <string>
10#include <thread> 9#include <thread>
11 10
11#include "common/polyfill_thread.h"
12#include "input_common/input_engine.h" 12#include "input_common/input_engine.h"
13 13
14struct libusb_context; 14struct libusb_context;
diff --git a/src/input_common/drivers/mouse.cpp b/src/input_common/drivers/mouse.cpp
index 98c3157a8..faf9cbdc3 100644
--- a/src/input_common/drivers/mouse.cpp
+++ b/src/input_common/drivers/mouse.cpp
@@ -1,7 +1,6 @@
1// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project 1// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later 2// SPDX-License-Identifier: GPL-2.0-or-later
3 3
4#include <stop_token>
5#include <thread> 4#include <thread>
6#include <fmt/format.h> 5#include <fmt/format.h>
7 6
diff --git a/src/input_common/drivers/mouse.h b/src/input_common/drivers/mouse.h
index 286ce1cf6..72073cc23 100644
--- a/src/input_common/drivers/mouse.h
+++ b/src/input_common/drivers/mouse.h
@@ -3,9 +3,9 @@
3 3
4#pragma once 4#pragma once
5 5
6#include <stop_token>
7#include <thread> 6#include <thread>
8 7
8#include "common/polyfill_thread.h"
9#include "common/vector_math.h" 9#include "common/vector_math.h"
10#include "input_common/input_engine.h" 10#include "input_common/input_engine.h"
11 11
diff --git a/src/input_common/drivers/tas_input.cpp b/src/input_common/drivers/tas_input.cpp
index 21c6ed405..f3ade90da 100644
--- a/src/input_common/drivers/tas_input.cpp
+++ b/src/input_common/drivers/tas_input.cpp
@@ -2,6 +2,7 @@
2// SPDX-License-Identifier: GPL-2.0-or-later 2// SPDX-License-Identifier: GPL-2.0-or-later
3 3
4#include <cstring> 4#include <cstring>
5#include <sstream>
5#include <fmt/format.h> 6#include <fmt/format.h>
6 7
7#include "common/fs/file.h" 8#include "common/fs/file.h"
diff --git a/src/shader_recompiler/CMakeLists.txt b/src/shader_recompiler/CMakeLists.txt
index 967ec9ba7..525b2363c 100644
--- a/src/shader_recompiler/CMakeLists.txt
+++ b/src/shader_recompiler/CMakeLists.txt
@@ -221,6 +221,7 @@ add_library(shader_recompiler STATIC
221 ir_opt/dual_vertex_pass.cpp 221 ir_opt/dual_vertex_pass.cpp
222 ir_opt/global_memory_to_storage_buffer_pass.cpp 222 ir_opt/global_memory_to_storage_buffer_pass.cpp
223 ir_opt/identity_removal_pass.cpp 223 ir_opt/identity_removal_pass.cpp
224 ir_opt/layer_pass.cpp
224 ir_opt/lower_fp16_to_fp32.cpp 225 ir_opt/lower_fp16_to_fp32.cpp
225 ir_opt/lower_int64_to_int32.cpp 226 ir_opt/lower_int64_to_int32.cpp
226 ir_opt/passes.h 227 ir_opt/passes.h
@@ -255,6 +256,7 @@ else()
255 # Bracket depth determines maximum size of a fold expression in Clang since 9c9974c3ccb6. 256 # Bracket depth determines maximum size of a fold expression in Clang since 9c9974c3ccb6.
256 # And this in turns limits the size of a std::array. 257 # And this in turns limits the size of a std::array.
257 $<$<CXX_COMPILER_ID:Clang>:-fbracket-depth=1024> 258 $<$<CXX_COMPILER_ID:Clang>:-fbracket-depth=1024>
259 $<$<CXX_COMPILER_ID:AppleClang>:-fbracket-depth=1024>
258 ) 260 )
259endif() 261endif()
260 262
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv.cpp b/src/shader_recompiler/backend/spirv/emit_spirv.cpp
index 265ac9c85..0f86a8004 100644
--- a/src/shader_recompiler/backend/spirv/emit_spirv.cpp
+++ b/src/shader_recompiler/backend/spirv/emit_spirv.cpp
@@ -402,8 +402,10 @@ void SetupCapabilities(const Profile& profile, const Info& info, EmitContext& ct
402 ctx.AddCapability(spv::Capability::SparseResidency); 402 ctx.AddCapability(spv::Capability::SparseResidency);
403 } 403 }
404 if (info.uses_demote_to_helper_invocation && profile.support_demote_to_helper_invocation) { 404 if (info.uses_demote_to_helper_invocation && profile.support_demote_to_helper_invocation) {
405 ctx.AddExtension("SPV_EXT_demote_to_helper_invocation"); 405 if (profile.supported_spirv < 0x00010600) {
406 ctx.AddCapability(spv::Capability::DemoteToHelperInvocationEXT); 406 ctx.AddExtension("SPV_EXT_demote_to_helper_invocation");
407 }
408 ctx.AddCapability(spv::Capability::DemoteToHelperInvocation);
407 } 409 }
408 if (info.stores[IR::Attribute::ViewportIndex]) { 410 if (info.stores[IR::Attribute::ViewportIndex]) {
409 ctx.AddCapability(spv::Capability::MultiViewport); 411 ctx.AddCapability(spv::Capability::MultiViewport);
@@ -426,12 +428,11 @@ void SetupCapabilities(const Profile& profile, const Info& info, EmitContext& ct
426 if ((info.uses_subgroup_vote || info.uses_subgroup_invocation_id || 428 if ((info.uses_subgroup_vote || info.uses_subgroup_invocation_id ||
427 info.uses_subgroup_shuffles) && 429 info.uses_subgroup_shuffles) &&
428 profile.support_vote) { 430 profile.support_vote) {
429 ctx.AddExtension("SPV_KHR_shader_ballot"); 431 ctx.AddCapability(spv::Capability::GroupNonUniformBallot);
430 ctx.AddCapability(spv::Capability::SubgroupBallotKHR); 432 ctx.AddCapability(spv::Capability::GroupNonUniformShuffle);
431 if (!profile.warp_size_potentially_larger_than_guest) { 433 if (!profile.warp_size_potentially_larger_than_guest) {
432 // vote ops are only used when not taking the long path 434 // vote ops are only used when not taking the long path
433 ctx.AddExtension("SPV_KHR_subgroup_vote"); 435 ctx.AddCapability(spv::Capability::GroupNonUniformVote);
434 ctx.AddCapability(spv::Capability::SubgroupVoteKHR);
435 } 436 }
436 } 437 }
437 if (info.uses_int64_bit_atomics && profile.support_int64_atomics) { 438 if (info.uses_int64_bit_atomics && profile.support_int64_atomics) {
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_control_flow.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_control_flow.cpp
index 7ad0b08ac..fb2c792c1 100644
--- a/src/shader_recompiler/backend/spirv/emit_spirv_control_flow.cpp
+++ b/src/shader_recompiler/backend/spirv/emit_spirv_control_flow.cpp
@@ -12,7 +12,7 @@ void EmitJoin(EmitContext&) {
12 12
13void EmitDemoteToHelperInvocation(EmitContext& ctx) { 13void EmitDemoteToHelperInvocation(EmitContext& ctx) {
14 if (ctx.profile.support_demote_to_helper_invocation) { 14 if (ctx.profile.support_demote_to_helper_invocation) {
15 ctx.OpDemoteToHelperInvocationEXT(); 15 ctx.OpDemoteToHelperInvocation();
16 } else { 16 } else {
17 const Id kill_label{ctx.OpLabel()}; 17 const Id kill_label{ctx.OpLabel()};
18 const Id impossible_label{ctx.OpLabel()}; 18 const Id impossible_label{ctx.OpLabel()};
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_warp.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_warp.cpp
index 7cbbbfaa6..2c90f2368 100644
--- a/src/shader_recompiler/backend/spirv/emit_spirv_warp.cpp
+++ b/src/shader_recompiler/backend/spirv/emit_spirv_warp.cpp
@@ -6,6 +6,10 @@
6 6
7namespace Shader::Backend::SPIRV { 7namespace Shader::Backend::SPIRV {
8namespace { 8namespace {
9Id SubgroupScope(EmitContext& ctx) {
10 return ctx.Const(static_cast<u32>(spv::Scope::Subgroup));
11}
12
9Id GetThreadId(EmitContext& ctx) { 13Id GetThreadId(EmitContext& ctx) {
10 return ctx.OpLoad(ctx.U32[1], ctx.subgroup_local_invocation_id); 14 return ctx.OpLoad(ctx.U32[1], ctx.subgroup_local_invocation_id);
11} 15}
@@ -49,8 +53,9 @@ Id GetMaxThreadId(EmitContext& ctx, Id thread_id, Id clamp, Id segmentation_mask
49} 53}
50 54
51Id SelectValue(EmitContext& ctx, Id in_range, Id value, Id src_thread_id) { 55Id SelectValue(EmitContext& ctx, Id in_range, Id value, Id src_thread_id) {
52 return ctx.OpSelect(ctx.U32[1], in_range, 56 return ctx.OpSelect(
53 ctx.OpSubgroupReadInvocationKHR(ctx.U32[1], value, src_thread_id), value); 57 ctx.U32[1], in_range,
58 ctx.OpGroupNonUniformShuffle(ctx.U32[1], SubgroupScope(ctx), value, src_thread_id), value);
54} 59}
55 60
56Id GetUpperClamp(EmitContext& ctx, Id invocation_id, Id clamp) { 61Id GetUpperClamp(EmitContext& ctx, Id invocation_id, Id clamp) {
@@ -71,40 +76,46 @@ Id EmitLaneId(EmitContext& ctx) {
71 76
72Id EmitVoteAll(EmitContext& ctx, Id pred) { 77Id EmitVoteAll(EmitContext& ctx, Id pred) {
73 if (!ctx.profile.warp_size_potentially_larger_than_guest) { 78 if (!ctx.profile.warp_size_potentially_larger_than_guest) {
74 return ctx.OpSubgroupAllKHR(ctx.U1, pred); 79 return ctx.OpGroupNonUniformAll(ctx.U1, SubgroupScope(ctx), pred);
75 } 80 }
76 const Id mask_ballot{ctx.OpSubgroupBallotKHR(ctx.U32[4], ctx.true_value)}; 81 const Id mask_ballot{
82 ctx.OpGroupNonUniformBallot(ctx.U32[4], SubgroupScope(ctx), ctx.true_value)};
77 const Id active_mask{WarpExtract(ctx, mask_ballot)}; 83 const Id active_mask{WarpExtract(ctx, mask_ballot)};
78 const Id ballot{WarpExtract(ctx, ctx.OpSubgroupBallotKHR(ctx.U32[4], pred))}; 84 const Id ballot{
85 WarpExtract(ctx, ctx.OpGroupNonUniformBallot(ctx.U32[4], SubgroupScope(ctx), pred))};
79 const Id lhs{ctx.OpBitwiseAnd(ctx.U32[1], ballot, active_mask)}; 86 const Id lhs{ctx.OpBitwiseAnd(ctx.U32[1], ballot, active_mask)};
80 return ctx.OpIEqual(ctx.U1, lhs, active_mask); 87 return ctx.OpIEqual(ctx.U1, lhs, active_mask);
81} 88}
82 89
83Id EmitVoteAny(EmitContext& ctx, Id pred) { 90Id EmitVoteAny(EmitContext& ctx, Id pred) {
84 if (!ctx.profile.warp_size_potentially_larger_than_guest) { 91 if (!ctx.profile.warp_size_potentially_larger_than_guest) {
85 return ctx.OpSubgroupAnyKHR(ctx.U1, pred); 92 return ctx.OpGroupNonUniformAny(ctx.U1, SubgroupScope(ctx), pred);
86 } 93 }
87 const Id mask_ballot{ctx.OpSubgroupBallotKHR(ctx.U32[4], ctx.true_value)}; 94 const Id mask_ballot{
95 ctx.OpGroupNonUniformBallot(ctx.U32[4], SubgroupScope(ctx), ctx.true_value)};
88 const Id active_mask{WarpExtract(ctx, mask_ballot)}; 96 const Id active_mask{WarpExtract(ctx, mask_ballot)};
89 const Id ballot{WarpExtract(ctx, ctx.OpSubgroupBallotKHR(ctx.U32[4], pred))}; 97 const Id ballot{
98 WarpExtract(ctx, ctx.OpGroupNonUniformBallot(ctx.U32[4], SubgroupScope(ctx), pred))};
90 const Id lhs{ctx.OpBitwiseAnd(ctx.U32[1], ballot, active_mask)}; 99 const Id lhs{ctx.OpBitwiseAnd(ctx.U32[1], ballot, active_mask)};
91 return ctx.OpINotEqual(ctx.U1, lhs, ctx.u32_zero_value); 100 return ctx.OpINotEqual(ctx.U1, lhs, ctx.u32_zero_value);
92} 101}
93 102
94Id EmitVoteEqual(EmitContext& ctx, Id pred) { 103Id EmitVoteEqual(EmitContext& ctx, Id pred) {
95 if (!ctx.profile.warp_size_potentially_larger_than_guest) { 104 if (!ctx.profile.warp_size_potentially_larger_than_guest) {
96 return ctx.OpSubgroupAllEqualKHR(ctx.U1, pred); 105 return ctx.OpGroupNonUniformAllEqual(ctx.U1, SubgroupScope(ctx), pred);
97 } 106 }
98 const Id mask_ballot{ctx.OpSubgroupBallotKHR(ctx.U32[4], ctx.true_value)}; 107 const Id mask_ballot{
108 ctx.OpGroupNonUniformBallot(ctx.U32[4], SubgroupScope(ctx), ctx.true_value)};
99 const Id active_mask{WarpExtract(ctx, mask_ballot)}; 109 const Id active_mask{WarpExtract(ctx, mask_ballot)};
100 const Id ballot{WarpExtract(ctx, ctx.OpSubgroupBallotKHR(ctx.U32[4], pred))}; 110 const Id ballot{
111 WarpExtract(ctx, ctx.OpGroupNonUniformBallot(ctx.U32[4], SubgroupScope(ctx), pred))};
101 const Id lhs{ctx.OpBitwiseXor(ctx.U32[1], ballot, active_mask)}; 112 const Id lhs{ctx.OpBitwiseXor(ctx.U32[1], ballot, active_mask)};
102 return ctx.OpLogicalOr(ctx.U1, ctx.OpIEqual(ctx.U1, lhs, ctx.u32_zero_value), 113 return ctx.OpLogicalOr(ctx.U1, ctx.OpIEqual(ctx.U1, lhs, ctx.u32_zero_value),
103 ctx.OpIEqual(ctx.U1, lhs, active_mask)); 114 ctx.OpIEqual(ctx.U1, lhs, active_mask));
104} 115}
105 116
106Id EmitSubgroupBallot(EmitContext& ctx, Id pred) { 117Id EmitSubgroupBallot(EmitContext& ctx, Id pred) {
107 const Id ballot{ctx.OpSubgroupBallotKHR(ctx.U32[4], pred)}; 118 const Id ballot{ctx.OpGroupNonUniformBallot(ctx.U32[4], SubgroupScope(ctx), pred)};
108 if (!ctx.profile.warp_size_potentially_larger_than_guest) { 119 if (!ctx.profile.warp_size_potentially_larger_than_guest) {
109 return ctx.OpCompositeExtract(ctx.U32[1], ballot, 0U); 120 return ctx.OpCompositeExtract(ctx.U32[1], ballot, 0U);
110 } 121 }
diff --git a/src/shader_recompiler/frontend/ir/opcodes.h b/src/shader_recompiler/frontend/ir/opcodes.h
index e70d7745c..d155afd0f 100644
--- a/src/shader_recompiler/frontend/ir/opcodes.h
+++ b/src/shader_recompiler/frontend/ir/opcodes.h
@@ -8,6 +8,7 @@
8 8
9#include <fmt/format.h> 9#include <fmt/format.h>
10 10
11#include "common/polyfill_ranges.h"
11#include "shader_recompiler/frontend/ir/type.h" 12#include "shader_recompiler/frontend/ir/type.h"
12 13
13namespace Shader::IR { 14namespace Shader::IR {
diff --git a/src/shader_recompiler/frontend/maxwell/control_flow.cpp b/src/shader_recompiler/frontend/maxwell/control_flow.cpp
index 6939692cd..dce414cb4 100644
--- a/src/shader_recompiler/frontend/maxwell/control_flow.cpp
+++ b/src/shader_recompiler/frontend/maxwell/control_flow.cpp
@@ -9,6 +9,7 @@
9 9
10#include <fmt/format.h> 10#include <fmt/format.h>
11 11
12#include "common/polyfill_ranges.h"
12#include "shader_recompiler/exception.h" 13#include "shader_recompiler/exception.h"
13#include "shader_recompiler/frontend/maxwell/control_flow.h" 14#include "shader_recompiler/frontend/maxwell/control_flow.h"
14#include "shader_recompiler/frontend/maxwell/decode.h" 15#include "shader_recompiler/frontend/maxwell/decode.h"
diff --git a/src/shader_recompiler/frontend/maxwell/decode.cpp b/src/shader_recompiler/frontend/maxwell/decode.cpp
index 455c91470..774f65bc5 100644
--- a/src/shader_recompiler/frontend/maxwell/decode.cpp
+++ b/src/shader_recompiler/frontend/maxwell/decode.cpp
@@ -7,6 +7,7 @@
7#include <memory> 7#include <memory>
8 8
9#include "common/common_types.h" 9#include "common/common_types.h"
10#include "common/polyfill_ranges.h"
10#include "shader_recompiler/exception.h" 11#include "shader_recompiler/exception.h"
11#include "shader_recompiler/frontend/maxwell/decode.h" 12#include "shader_recompiler/frontend/maxwell/decode.h"
12#include "shader_recompiler/frontend/maxwell/opcodes.h" 13#include "shader_recompiler/frontend/maxwell/opcodes.h"
diff --git a/src/shader_recompiler/frontend/maxwell/structured_control_flow.cpp b/src/shader_recompiler/frontend/maxwell/structured_control_flow.cpp
index ce42475d4..80c90fe6a 100644
--- a/src/shader_recompiler/frontend/maxwell/structured_control_flow.cpp
+++ b/src/shader_recompiler/frontend/maxwell/structured_control_flow.cpp
@@ -12,6 +12,7 @@
12 12
13#include <boost/intrusive/list.hpp> 13#include <boost/intrusive/list.hpp>
14 14
15#include "common/polyfill_ranges.h"
15#include "shader_recompiler/environment.h" 16#include "shader_recompiler/environment.h"
16#include "shader_recompiler/frontend/ir/basic_block.h" 17#include "shader_recompiler/frontend/ir/basic_block.h"
17#include "shader_recompiler/frontend/ir/ir_emitter.h" 18#include "shader_recompiler/frontend/ir/ir_emitter.h"
diff --git a/src/shader_recompiler/frontend/maxwell/translate/impl/floating_point_conversion_integer.cpp b/src/shader_recompiler/frontend/maxwell/translate/impl/floating_point_conversion_integer.cpp
index 4942878b9..85c18d942 100644
--- a/src/shader_recompiler/frontend/maxwell/translate/impl/floating_point_conversion_integer.cpp
+++ b/src/shader_recompiler/frontend/maxwell/translate/impl/floating_point_conversion_integer.cpp
@@ -176,12 +176,13 @@ void TranslateF2I(TranslatorVisitor& v, u64 insn, const IR::F16F32F64& src_a) {
176 (f2i.src_format == SrcFormat::F64) != (f2i.dest_format == DestFormat::I64); 176 (f2i.src_format == SrcFormat::F64) != (f2i.dest_format == DestFormat::I64);
177 if (special_nan_cases) { 177 if (special_nan_cases) {
178 if (f2i.dest_format == DestFormat::I32) { 178 if (f2i.dest_format == DestFormat::I32) {
179 constexpr u32 nan_value = 0x8000'0000U;
179 handled_special_case = true; 180 handled_special_case = true;
180 result = IR::U32{v.ir.Select(v.ir.FPIsNan(op_a), v.ir.Imm32(0x8000'0000U), result)}; 181 result = IR::U32{v.ir.Select(v.ir.FPIsNan(op_a), v.ir.Imm32(nan_value), result)};
181 } else if (f2i.dest_format == DestFormat::I64) { 182 } else if (f2i.dest_format == DestFormat::I64) {
183 constexpr u64 nan_value = 0x8000'0000'0000'0000ULL;
182 handled_special_case = true; 184 handled_special_case = true;
183 result = IR::U64{ 185 result = IR::U64{v.ir.Select(v.ir.FPIsNan(op_a), v.ir.Imm64(nan_value), result)};
184 v.ir.Select(v.ir.FPIsNan(op_a), v.ir.Imm64(0x8000'0000'0000'0000UL), result)};
185 } 186 }
186 } 187 }
187 if (!handled_special_case && is_signed) { 188 if (!handled_special_case && is_signed) {
diff --git a/src/shader_recompiler/frontend/maxwell/translate_program.cpp b/src/shader_recompiler/frontend/maxwell/translate_program.cpp
index 376aae0ea..3adbd2b16 100644
--- a/src/shader_recompiler/frontend/maxwell/translate_program.cpp
+++ b/src/shader_recompiler/frontend/maxwell/translate_program.cpp
@@ -9,6 +9,7 @@
9#include "common/settings.h" 9#include "common/settings.h"
10#include "shader_recompiler/exception.h" 10#include "shader_recompiler/exception.h"
11#include "shader_recompiler/frontend/ir/basic_block.h" 11#include "shader_recompiler/frontend/ir/basic_block.h"
12#include "shader_recompiler/frontend/ir/ir_emitter.h"
12#include "shader_recompiler/frontend/ir/post_order.h" 13#include "shader_recompiler/frontend/ir/post_order.h"
13#include "shader_recompiler/frontend/maxwell/structured_control_flow.h" 14#include "shader_recompiler/frontend/maxwell/structured_control_flow.h"
14#include "shader_recompiler/frontend/maxwell/translate/translate.h" 15#include "shader_recompiler/frontend/maxwell/translate/translate.h"
@@ -233,6 +234,8 @@ IR::Program TranslateProgram(ObjectPool<IR::Inst>& inst_pool, ObjectPool<IR::Blo
233 Optimization::VerificationPass(program); 234 Optimization::VerificationPass(program);
234 } 235 }
235 Optimization::CollectShaderInfoPass(env, program); 236 Optimization::CollectShaderInfoPass(env, program);
237 Optimization::LayerPass(program, host_info);
238
236 CollectInterpolationInfo(env, program); 239 CollectInterpolationInfo(env, program);
237 AddNVNStorageBuffers(program); 240 AddNVNStorageBuffers(program);
238 return program; 241 return program;
@@ -331,4 +334,82 @@ void ConvertLegacyToGeneric(IR::Program& program, const Shader::RuntimeInfo& run
331 } 334 }
332} 335}
333 336
337IR::Program GenerateGeometryPassthrough(ObjectPool<IR::Inst>& inst_pool,
338 ObjectPool<IR::Block>& block_pool,
339 const HostTranslateInfo& host_info,
340 IR::Program& source_program,
341 Shader::OutputTopology output_topology) {
342 IR::Program program;
343 program.stage = Stage::Geometry;
344 program.output_topology = output_topology;
345 switch (output_topology) {
346 case OutputTopology::PointList:
347 program.output_vertices = 1;
348 break;
349 case OutputTopology::LineStrip:
350 program.output_vertices = 2;
351 break;
352 default:
353 program.output_vertices = 3;
354 break;
355 }
356
357 program.is_geometry_passthrough = false;
358 program.info.loads.mask = source_program.info.stores.mask;
359 program.info.stores.mask = source_program.info.stores.mask;
360 program.info.stores.Set(IR::Attribute::Layer, true);
361 program.info.stores.Set(source_program.info.emulated_layer, false);
362
363 IR::Block* current_block = block_pool.Create(inst_pool);
364 auto& node{program.syntax_list.emplace_back()};
365 node.type = IR::AbstractSyntaxNode::Type::Block;
366 node.data.block = current_block;
367
368 IR::IREmitter ir{*current_block};
369 for (u32 i = 0; i < program.output_vertices; i++) {
370 // Assign generics from input
371 for (u32 j = 0; j < 32; j++) {
372 if (!program.info.stores.Generic(j)) {
373 continue;
374 }
375
376 const IR::Attribute attr = IR::Attribute::Generic0X + (j * 4);
377 ir.SetAttribute(attr + 0, ir.GetAttribute(attr + 0, ir.Imm32(i)), ir.Imm32(0));
378 ir.SetAttribute(attr + 1, ir.GetAttribute(attr + 1, ir.Imm32(i)), ir.Imm32(0));
379 ir.SetAttribute(attr + 2, ir.GetAttribute(attr + 2, ir.Imm32(i)), ir.Imm32(0));
380 ir.SetAttribute(attr + 3, ir.GetAttribute(attr + 3, ir.Imm32(i)), ir.Imm32(0));
381 }
382
383 // Assign position from input
384 const IR::Attribute attr = IR::Attribute::PositionX;
385 ir.SetAttribute(attr + 0, ir.GetAttribute(attr + 0, ir.Imm32(i)), ir.Imm32(0));
386 ir.SetAttribute(attr + 1, ir.GetAttribute(attr + 1, ir.Imm32(i)), ir.Imm32(0));
387 ir.SetAttribute(attr + 2, ir.GetAttribute(attr + 2, ir.Imm32(i)), ir.Imm32(0));
388 ir.SetAttribute(attr + 3, ir.GetAttribute(attr + 3, ir.Imm32(i)), ir.Imm32(0));
389
390 // Assign layer
391 ir.SetAttribute(IR::Attribute::Layer, ir.GetAttribute(source_program.info.emulated_layer),
392 ir.Imm32(0));
393
394 // Emit vertex
395 ir.EmitVertex(ir.Imm32(0));
396 }
397 ir.EndPrimitive(ir.Imm32(0));
398
399 IR::Block* return_block{block_pool.Create(inst_pool)};
400 IR::IREmitter{*return_block}.Epilogue();
401 current_block->AddBranch(return_block);
402
403 auto& merge{program.syntax_list.emplace_back()};
404 merge.type = IR::AbstractSyntaxNode::Type::Block;
405 merge.data.block = return_block;
406 program.syntax_list.emplace_back().type = IR::AbstractSyntaxNode::Type::Return;
407
408 program.blocks = GenerateBlocks(program.syntax_list);
409 program.post_order_blocks = PostOrder(program.syntax_list.front());
410 Optimization::SsaRewritePass(program);
411
412 return program;
413}
414
334} // namespace Shader::Maxwell 415} // namespace Shader::Maxwell
diff --git a/src/shader_recompiler/frontend/maxwell/translate_program.h b/src/shader_recompiler/frontend/maxwell/translate_program.h
index 02ede8c9c..497afe7cb 100644
--- a/src/shader_recompiler/frontend/maxwell/translate_program.h
+++ b/src/shader_recompiler/frontend/maxwell/translate_program.h
@@ -25,4 +25,13 @@ namespace Shader::Maxwell {
25 25
26void ConvertLegacyToGeneric(IR::Program& program, const RuntimeInfo& runtime_info); 26void ConvertLegacyToGeneric(IR::Program& program, const RuntimeInfo& runtime_info);
27 27
28// Maxwell v1 and older Nvidia cards don't support setting gl_Layer from non-geometry stages.
29// This creates a workaround by setting the layer as a generic output and creating a
30// passthrough geometry shader that reads the generic and sets the layer.
31[[nodiscard]] IR::Program GenerateGeometryPassthrough(ObjectPool<IR::Inst>& inst_pool,
32 ObjectPool<IR::Block>& block_pool,
33 const HostTranslateInfo& host_info,
34 IR::Program& source_program,
35 Shader::OutputTopology output_topology);
36
28} // namespace Shader::Maxwell 37} // namespace Shader::Maxwell
diff --git a/src/shader_recompiler/host_translate_info.h b/src/shader_recompiler/host_translate_info.h
index cc1500690..d5d279554 100644
--- a/src/shader_recompiler/host_translate_info.h
+++ b/src/shader_recompiler/host_translate_info.h
@@ -13,7 +13,8 @@ struct HostTranslateInfo {
13 bool support_float16{}; ///< True when the device supports 16-bit floats 13 bool support_float16{}; ///< True when the device supports 16-bit floats
14 bool support_int64{}; ///< True when the device supports 64-bit integers 14 bool support_int64{}; ///< True when the device supports 64-bit integers
15 bool needs_demote_reorder{}; ///< True when the device needs DemoteToHelperInvocation reordered 15 bool needs_demote_reorder{}; ///< True when the device needs DemoteToHelperInvocation reordered
16 bool support_snorm_render_buffer{}; ///< True when the device supports SNORM render buffers 16 bool support_snorm_render_buffer{}; ///< True when the device supports SNORM render buffers
17 bool support_viewport_index_layer{}; ///< True when the device supports gl_Layer in VS
17}; 18};
18 19
19} // namespace Shader 20} // namespace Shader
diff --git a/src/shader_recompiler/ir_opt/layer_pass.cpp b/src/shader_recompiler/ir_opt/layer_pass.cpp
new file mode 100644
index 000000000..4574f7cf2
--- /dev/null
+++ b/src/shader_recompiler/ir_opt/layer_pass.cpp
@@ -0,0 +1,68 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include <algorithm>
5#include <bit>
6#include <optional>
7
8#include <boost/container/small_vector.hpp>
9
10#include "shader_recompiler/environment.h"
11#include "shader_recompiler/frontend/ir/basic_block.h"
12#include "shader_recompiler/frontend/ir/breadth_first_search.h"
13#include "shader_recompiler/frontend/ir/ir_emitter.h"
14#include "shader_recompiler/host_translate_info.h"
15#include "shader_recompiler/ir_opt/passes.h"
16#include "shader_recompiler/shader_info.h"
17
18namespace Shader::Optimization {
19
20static IR::Attribute EmulatedLayerAttribute(VaryingState& stores) {
21 for (u32 i = 0; i < 32; i++) {
22 if (!stores.Generic(i)) {
23 return IR::Attribute::Generic0X + (i * 4);
24 }
25 }
26 return IR::Attribute::Layer;
27}
28
29static bool PermittedProgramStage(Stage stage) {
30 switch (stage) {
31 case Stage::VertexA:
32 case Stage::VertexB:
33 case Stage::TessellationControl:
34 case Stage::TessellationEval:
35 return true;
36 default:
37 return false;
38 }
39}
40
41void LayerPass(IR::Program& program, const HostTranslateInfo& host_info) {
42 if (host_info.support_viewport_index_layer || !PermittedProgramStage(program.stage)) {
43 return;
44 }
45
46 const auto end{program.post_order_blocks.end()};
47 const auto layer_attribute = EmulatedLayerAttribute(program.info.stores);
48 bool requires_layer_emulation = false;
49
50 for (auto block = program.post_order_blocks.begin(); block != end; ++block) {
51 for (IR::Inst& inst : (*block)->Instructions()) {
52 if (inst.GetOpcode() == IR::Opcode::SetAttribute &&
53 inst.Arg(0).Attribute() == IR::Attribute::Layer) {
54 requires_layer_emulation = true;
55 inst.SetArg(0, IR::Value{layer_attribute});
56 }
57 }
58 }
59
60 if (requires_layer_emulation) {
61 program.info.requires_layer_emulation = true;
62 program.info.emulated_layer = layer_attribute;
63 program.info.stores.Set(IR::Attribute::Layer, false);
64 program.info.stores.Set(layer_attribute, true);
65 }
66}
67
68} // namespace Shader::Optimization
diff --git a/src/shader_recompiler/ir_opt/passes.h b/src/shader_recompiler/ir_opt/passes.h
index 586a0668f..11bfe801a 100644
--- a/src/shader_recompiler/ir_opt/passes.h
+++ b/src/shader_recompiler/ir_opt/passes.h
@@ -23,6 +23,7 @@ void RescalingPass(IR::Program& program);
23void SsaRewritePass(IR::Program& program); 23void SsaRewritePass(IR::Program& program);
24void PositionPass(Environment& env, IR::Program& program); 24void PositionPass(Environment& env, IR::Program& program);
25void TexturePass(Environment& env, IR::Program& program, const HostTranslateInfo& host_info); 25void TexturePass(Environment& env, IR::Program& program, const HostTranslateInfo& host_info);
26void LayerPass(IR::Program& program, const HostTranslateInfo& host_info);
26void VerificationPass(const IR::Program& program); 27void VerificationPass(const IR::Program& program);
27 28
28// Dual Vertex 29// Dual Vertex
diff --git a/src/shader_recompiler/shader_info.h b/src/shader_recompiler/shader_info.h
index ee6252bb5..d9c6e92db 100644
--- a/src/shader_recompiler/shader_info.h
+++ b/src/shader_recompiler/shader_info.h
@@ -204,6 +204,9 @@ struct Info {
204 u32 nvn_buffer_base{}; 204 u32 nvn_buffer_base{};
205 std::bitset<16> nvn_buffer_used{}; 205 std::bitset<16> nvn_buffer_used{};
206 206
207 bool requires_layer_emulation{};
208 IR::Attribute emulated_layer{};
209
207 boost::container::static_vector<ConstantBufferDescriptor, MAX_CBUFS> 210 boost::container::static_vector<ConstantBufferDescriptor, MAX_CBUFS>
208 constant_buffer_descriptors; 211 constant_buffer_descriptors;
209 boost::container::static_vector<StorageBufferDescriptor, MAX_SSBOS> storage_buffers_descriptors; 212 boost::container::static_vector<StorageBufferDescriptor, MAX_SSBOS> storage_buffers_descriptors;
diff --git a/src/video_core/buffer_cache/buffer_base.h b/src/video_core/buffer_cache/buffer_base.h
index f9a6472cf..92d77eef2 100644
--- a/src/video_core/buffer_cache/buffer_base.h
+++ b/src/video_core/buffer_cache/buffer_base.h
@@ -535,7 +535,7 @@ private:
535 const u64* const state_words = Array<type>(); 535 const u64* const state_words = Array<type>();
536 const u64 num_query_words = size / BYTES_PER_WORD + 1; 536 const u64 num_query_words = size / BYTES_PER_WORD + 1;
537 const u64 word_begin = offset / BYTES_PER_WORD; 537 const u64 word_begin = offset / BYTES_PER_WORD;
538 const u64 word_end = std::min(word_begin + num_query_words, NumWords()); 538 const u64 word_end = std::min<u64>(word_begin + num_query_words, NumWords());
539 const u64 page_limit = Common::DivCeil(offset + size, BYTES_PER_PAGE); 539 const u64 page_limit = Common::DivCeil(offset + size, BYTES_PER_PAGE);
540 u64 page_index = (offset / BYTES_PER_PAGE) % PAGES_PER_WORD; 540 u64 page_index = (offset / BYTES_PER_PAGE) % PAGES_PER_WORD;
541 for (u64 word_index = word_begin; word_index < word_end; ++word_index, page_index = 0) { 541 for (u64 word_index = word_begin; word_index < word_end; ++word_index, page_index = 0) {
diff --git a/src/video_core/buffer_cache/buffer_cache.h b/src/video_core/buffer_cache/buffer_cache.h
index 5d3a8293b..6881b34c4 100644
--- a/src/video_core/buffer_cache/buffer_cache.h
+++ b/src/video_core/buffer_cache/buffer_cache.h
@@ -19,6 +19,7 @@
19#include "common/literals.h" 19#include "common/literals.h"
20#include "common/lru_cache.h" 20#include "common/lru_cache.h"
21#include "common/microprofile.h" 21#include "common/microprofile.h"
22#include "common/polyfill_ranges.h"
22#include "common/settings.h" 23#include "common/settings.h"
23#include "core/memory.h" 24#include "core/memory.h"
24#include "video_core/buffer_cache/buffer_base.h" 25#include "video_core/buffer_cache/buffer_base.h"
diff --git a/src/video_core/control/channel_state_cache.h b/src/video_core/control/channel_state_cache.h
index 584a0c26c..cdaf4f8d5 100644
--- a/src/video_core/control/channel_state_cache.h
+++ b/src/video_core/control/channel_state_cache.h
@@ -35,8 +35,6 @@ public:
35 explicit ChannelInfo(Tegra::Control::ChannelState& state); 35 explicit ChannelInfo(Tegra::Control::ChannelState& state);
36 ChannelInfo(const ChannelInfo& state) = delete; 36 ChannelInfo(const ChannelInfo& state) = delete;
37 ChannelInfo& operator=(const ChannelInfo&) = delete; 37 ChannelInfo& operator=(const ChannelInfo&) = delete;
38 ChannelInfo(ChannelInfo&& other) = default;
39 ChannelInfo& operator=(ChannelInfo&& other) = default;
40 38
41 Tegra::Engines::Maxwell3D& maxwell3d; 39 Tegra::Engines::Maxwell3D& maxwell3d;
42 Tegra::Engines::KeplerCompute& kepler_compute; 40 Tegra::Engines::KeplerCompute& kepler_compute;
diff --git a/src/video_core/engines/maxwell_3d.cpp b/src/video_core/engines/maxwell_3d.cpp
index 55462752c..34bbc72cf 100644
--- a/src/video_core/engines/maxwell_3d.cpp
+++ b/src/video_core/engines/maxwell_3d.cpp
@@ -126,7 +126,6 @@ void Maxwell3D::InitializeRegisterDefaults() {
126 draw_command[MAXWELL3D_REG_INDEX(draw_inline_index)] = true; 126 draw_command[MAXWELL3D_REG_INDEX(draw_inline_index)] = true;
127 draw_command[MAXWELL3D_REG_INDEX(inline_index_2x16.even)] = true; 127 draw_command[MAXWELL3D_REG_INDEX(inline_index_2x16.even)] = true;
128 draw_command[MAXWELL3D_REG_INDEX(inline_index_4x8.index0)] = true; 128 draw_command[MAXWELL3D_REG_INDEX(inline_index_4x8.index0)] = true;
129 draw_command[MAXWELL3D_REG_INDEX(draw.instance_id)] = true;
130} 129}
131 130
132void Maxwell3D::ProcessMacro(u32 method, const u32* base_start, u32 amount, bool is_last_call) { 131void Maxwell3D::ProcessMacro(u32 method, const u32* base_start, u32 amount, bool is_last_call) {
@@ -218,16 +217,19 @@ void Maxwell3D::ProcessMethodCall(u32 method, u32 argument, u32 nonshadow_argume
218 regs.index_buffer.count = regs.index_buffer32_first.count; 217 regs.index_buffer.count = regs.index_buffer32_first.count;
219 regs.index_buffer.first = regs.index_buffer32_first.first; 218 regs.index_buffer.first = regs.index_buffer32_first.first;
220 dirty.flags[VideoCommon::Dirty::IndexBuffer] = true; 219 dirty.flags[VideoCommon::Dirty::IndexBuffer] = true;
220 draw_indexed = true;
221 return ProcessDraw(); 221 return ProcessDraw();
222 case MAXWELL3D_REG_INDEX(index_buffer16_first): 222 case MAXWELL3D_REG_INDEX(index_buffer16_first):
223 regs.index_buffer.count = regs.index_buffer16_first.count; 223 regs.index_buffer.count = regs.index_buffer16_first.count;
224 regs.index_buffer.first = regs.index_buffer16_first.first; 224 regs.index_buffer.first = regs.index_buffer16_first.first;
225 dirty.flags[VideoCommon::Dirty::IndexBuffer] = true; 225 dirty.flags[VideoCommon::Dirty::IndexBuffer] = true;
226 draw_indexed = true;
226 return ProcessDraw(); 227 return ProcessDraw();
227 case MAXWELL3D_REG_INDEX(index_buffer8_first): 228 case MAXWELL3D_REG_INDEX(index_buffer8_first):
228 regs.index_buffer.count = regs.index_buffer8_first.count; 229 regs.index_buffer.count = regs.index_buffer8_first.count;
229 regs.index_buffer.first = regs.index_buffer8_first.first; 230 regs.index_buffer.first = regs.index_buffer8_first.first;
230 dirty.flags[VideoCommon::Dirty::IndexBuffer] = true; 231 dirty.flags[VideoCommon::Dirty::IndexBuffer] = true;
232 draw_indexed = true;
231 return ProcessDraw(); 233 return ProcessDraw();
232 case MAXWELL3D_REG_INDEX(topology_override): 234 case MAXWELL3D_REG_INDEX(topology_override):
233 use_topology_override = true; 235 use_topology_override = true;
@@ -300,21 +302,33 @@ void Maxwell3D::CallMethod(u32 method, u32 method_argument, bool is_last_call) {
300 draw_mode = DrawMode::InlineIndex; 302 draw_mode = DrawMode::InlineIndex;
301 }; 303 };
302 switch (method) { 304 switch (method) {
305 case MAXWELL3D_REG_INDEX(draw.begin): {
306 draw_mode =
307 (regs.draw.instance_id == Maxwell3D::Regs::Draw::InstanceId::Subsequent) ||
308 (regs.draw.instance_id == Maxwell3D::Regs::Draw::InstanceId::Unchanged)
309 ? DrawMode::Instance
310 : DrawMode::General;
311 break;
312 }
303 case MAXWELL3D_REG_INDEX(draw.end): 313 case MAXWELL3D_REG_INDEX(draw.end):
304 switch (draw_mode) { 314 switch (draw_mode) {
305 case DrawMode::General: 315 case DrawMode::General:
306 ProcessDraw(1); 316 ProcessDraw();
307 break; 317 break;
308 case DrawMode::InlineIndex: 318 case DrawMode::InlineIndex:
309 regs.index_buffer.count = static_cast<u32>(inline_index_draw_indexes.size() / 4); 319 regs.index_buffer.count = static_cast<u32>(inline_index_draw_indexes.size() / 4);
310 regs.index_buffer.format = Regs::IndexFormat::UnsignedInt; 320 regs.index_buffer.format = Regs::IndexFormat::UnsignedInt;
311 ProcessDraw(1); 321 draw_indexed = true;
322 ProcessDraw();
312 inline_index_draw_indexes.clear(); 323 inline_index_draw_indexes.clear();
313 break; 324 break;
314 case DrawMode::Instance: 325 case DrawMode::Instance:
315 break; 326 break;
316 } 327 }
317 break; 328 break;
329 case MAXWELL3D_REG_INDEX(index_buffer.count):
330 draw_indexed = true;
331 break;
318 case MAXWELL3D_REG_INDEX(draw_inline_index): 332 case MAXWELL3D_REG_INDEX(draw_inline_index):
319 update_inline_index(method_argument); 333 update_inline_index(method_argument);
320 break; 334 break;
@@ -328,13 +342,6 @@ void Maxwell3D::CallMethod(u32 method, u32 method_argument, bool is_last_call) {
328 update_inline_index(regs.inline_index_4x8.index2); 342 update_inline_index(regs.inline_index_4x8.index2);
329 update_inline_index(regs.inline_index_4x8.index3); 343 update_inline_index(regs.inline_index_4x8.index3);
330 break; 344 break;
331 case MAXWELL3D_REG_INDEX(draw.instance_id):
332 draw_mode =
333 (regs.draw.instance_id == Maxwell3D::Regs::Draw::InstanceId::Subsequent) ||
334 (regs.draw.instance_id == Maxwell3D::Regs::Draw::InstanceId::Unchanged)
335 ? DrawMode::Instance
336 : DrawMode::General;
337 break;
338 } 345 }
339 } else { 346 } else {
340 ProcessDeferredDraw(); 347 ProcessDeferredDraw();
@@ -624,27 +631,16 @@ void Maxwell3D::ProcessClearBuffers(u32 layer_count) {
624 631
625void Maxwell3D::ProcessDraw(u32 instance_count) { 632void Maxwell3D::ProcessDraw(u32 instance_count) {
626 LOG_TRACE(HW_GPU, "called, topology={}, count={}", regs.draw.topology.Value(), 633 LOG_TRACE(HW_GPU, "called, topology={}, count={}", regs.draw.topology.Value(),
627 regs.vertex_buffer.count); 634 draw_indexed ? regs.index_buffer.count : regs.vertex_buffer.count);
628
629 ASSERT_MSG(!(regs.index_buffer.count && regs.vertex_buffer.count), "Both indexed and direct?");
630
631 // Both instance configuration registers can not be set at the same time.
632 ASSERT_MSG(regs.draw.instance_id == Maxwell3D::Regs::Draw::InstanceId::First ||
633 regs.draw.instance_id != Maxwell3D::Regs::Draw::InstanceId::Unchanged,
634 "Illegal combination of instancing parameters");
635 635
636 ProcessTopologyOverride(); 636 ProcessTopologyOverride();
637 637
638 const bool is_indexed = regs.index_buffer.count && !regs.vertex_buffer.count;
639 if (ShouldExecute()) { 638 if (ShouldExecute()) {
640 rasterizer->Draw(is_indexed, instance_count); 639 rasterizer->Draw(draw_indexed, instance_count);
641 } 640 }
642 641
643 if (is_indexed) { 642 draw_indexed = false;
644 regs.index_buffer.count = 0; 643 deferred_draw_method.clear();
645 } else {
646 regs.vertex_buffer.count = 0;
647 }
648} 644}
649 645
650void Maxwell3D::ProcessDeferredDraw() { 646void Maxwell3D::ProcessDeferredDraw() {
@@ -667,8 +663,6 @@ void Maxwell3D::ProcessDeferredDraw() {
667 ASSERT_MSG(!(vertex_buffer_count && index_buffer_count), "Instance both indexed and direct?"); 663 ASSERT_MSG(!(vertex_buffer_count && index_buffer_count), "Instance both indexed and direct?");
668 664
669 ProcessDraw(instance_count); 665 ProcessDraw(instance_count);
670
671 deferred_draw_method.clear();
672} 666}
673 667
674} // namespace Tegra::Engines 668} // namespace Tegra::Engines
diff --git a/src/video_core/engines/maxwell_3d.h b/src/video_core/engines/maxwell_3d.h
index deba292a5..a541cd95f 100644
--- a/src/video_core/engines/maxwell_3d.h
+++ b/src/video_core/engines/maxwell_3d.h
@@ -3159,6 +3159,7 @@ private:
3159 std::vector<u32> deferred_draw_method; 3159 std::vector<u32> deferred_draw_method;
3160 enum class DrawMode : u32 { General = 0, Instance, InlineIndex }; 3160 enum class DrawMode : u32 { General = 0, Instance, InlineIndex };
3161 DrawMode draw_mode{DrawMode::General}; 3161 DrawMode draw_mode{DrawMode::General};
3162 bool draw_indexed{};
3162}; 3163};
3163 3164
3164#define ASSERT_REG_POSITION(field_name, position) \ 3165#define ASSERT_REG_POSITION(field_name, position) \
diff --git a/src/video_core/gpu_thread.cpp b/src/video_core/gpu_thread.cpp
index 1bd477011..164a5252a 100644
--- a/src/video_core/gpu_thread.cpp
+++ b/src/video_core/gpu_thread.cpp
@@ -125,7 +125,7 @@ u64 ThreadManager::PushCommand(CommandData&& command_data, bool block) {
125 state.queue.Push(CommandDataContainer(std::move(command_data), fence, block)); 125 state.queue.Push(CommandDataContainer(std::move(command_data), fence, block));
126 126
127 if (block) { 127 if (block) {
128 state.cv.wait(lk, thread.get_stop_token(), [this, fence] { 128 Common::CondvarWait(state.cv, lk, thread.get_stop_token(), [this, fence] {
129 return fence <= state.signaled_fence.load(std::memory_order_relaxed); 129 return fence <= state.signaled_fence.load(std::memory_order_relaxed);
130 }); 130 });
131 } 131 }
diff --git a/src/video_core/gpu_thread.h b/src/video_core/gpu_thread.h
index 64628d3e3..c71a419c7 100644
--- a/src/video_core/gpu_thread.h
+++ b/src/video_core/gpu_thread.h
@@ -10,6 +10,7 @@
10#include <thread> 10#include <thread>
11#include <variant> 11#include <variant>
12 12
13#include "common/polyfill_thread.h"
13#include "common/threadsafe_queue.h" 14#include "common/threadsafe_queue.h"
14#include "video_core/framebuffer_config.h" 15#include "video_core/framebuffer_config.h"
15 16
diff --git a/src/video_core/rasterizer_interface.h b/src/video_core/rasterizer_interface.h
index cfd872a40..b6907463c 100644
--- a/src/video_core/rasterizer_interface.h
+++ b/src/video_core/rasterizer_interface.h
@@ -6,8 +6,8 @@
6#include <functional> 6#include <functional>
7#include <optional> 7#include <optional>
8#include <span> 8#include <span>
9#include <stop_token>
10#include "common/common_types.h" 9#include "common/common_types.h"
10#include "common/polyfill_thread.h"
11#include "video_core/engines/fermi_2d.h" 11#include "video_core/engines/fermi_2d.h"
12#include "video_core/gpu.h" 12#include "video_core/gpu.h"
13 13
diff --git a/src/video_core/renderer_opengl/gl_device.cpp b/src/video_core/renderer_opengl/gl_device.cpp
index 1663e277d..e2e3dac34 100644
--- a/src/video_core/renderer_opengl/gl_device.cpp
+++ b/src/video_core/renderer_opengl/gl_device.cpp
@@ -14,6 +14,7 @@
14 14
15#include "common/literals.h" 15#include "common/literals.h"
16#include "common/logging/log.h" 16#include "common/logging/log.h"
17#include "common/polyfill_ranges.h"
17#include "common/settings.h" 18#include "common/settings.h"
18#include "shader_recompiler/stage.h" 19#include "shader_recompiler/stage.h"
19#include "video_core/renderer_opengl/gl_device.h" 20#include "video_core/renderer_opengl/gl_device.h"
diff --git a/src/video_core/renderer_opengl/gl_shader_cache.cpp b/src/video_core/renderer_opengl/gl_shader_cache.cpp
index 3fe04a115..a38060100 100644
--- a/src/video_core/renderer_opengl/gl_shader_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_cache.cpp
@@ -39,6 +39,7 @@ using Shader::Backend::GLASM::EmitGLASM;
39using Shader::Backend::GLSL::EmitGLSL; 39using Shader::Backend::GLSL::EmitGLSL;
40using Shader::Backend::SPIRV::EmitSPIRV; 40using Shader::Backend::SPIRV::EmitSPIRV;
41using Shader::Maxwell::ConvertLegacyToGeneric; 41using Shader::Maxwell::ConvertLegacyToGeneric;
42using Shader::Maxwell::GenerateGeometryPassthrough;
42using Shader::Maxwell::MergeDualVertexPrograms; 43using Shader::Maxwell::MergeDualVertexPrograms;
43using Shader::Maxwell::TranslateProgram; 44using Shader::Maxwell::TranslateProgram;
44using VideoCommon::ComputeEnvironment; 45using VideoCommon::ComputeEnvironment;
@@ -56,6 +57,17 @@ auto MakeSpan(Container& container) {
56 return std::span(container.data(), container.size()); 57 return std::span(container.data(), container.size());
57} 58}
58 59
60Shader::OutputTopology MaxwellToOutputTopology(Maxwell::PrimitiveTopology topology) {
61 switch (topology) {
62 case Maxwell::PrimitiveTopology::Points:
63 return Shader::OutputTopology::PointList;
64 case Maxwell::PrimitiveTopology::LineStrip:
65 return Shader::OutputTopology::LineStrip;
66 default:
67 return Shader::OutputTopology::TriangleStrip;
68 }
69}
70
59Shader::RuntimeInfo MakeRuntimeInfo(const GraphicsPipelineKey& key, 71Shader::RuntimeInfo MakeRuntimeInfo(const GraphicsPipelineKey& key,
60 const Shader::IR::Program& program, 72 const Shader::IR::Program& program,
61 const Shader::IR::Program* previous_program, 73 const Shader::IR::Program* previous_program,
@@ -220,6 +232,7 @@ ShaderCache::ShaderCache(RasterizerOpenGL& rasterizer_, Core::Frontend::EmuWindo
220 .support_int64 = device.HasShaderInt64(), 232 .support_int64 = device.HasShaderInt64(),
221 .needs_demote_reorder = device.IsAmd(), 233 .needs_demote_reorder = device.IsAmd(),
222 .support_snorm_render_buffer = false, 234 .support_snorm_render_buffer = false,
235 .support_viewport_index_layer = device.HasVertexViewportLayer(),
223 } { 236 } {
224 if (use_asynchronous_shaders) { 237 if (use_asynchronous_shaders) {
225 workers = CreateWorkers(); 238 workers = CreateWorkers();
@@ -314,9 +327,7 @@ GraphicsPipeline* ShaderCache::CurrentGraphicsPipeline() {
314 const auto& regs{maxwell3d->regs}; 327 const auto& regs{maxwell3d->regs};
315 graphics_key.raw = 0; 328 graphics_key.raw = 0;
316 graphics_key.early_z.Assign(regs.mandated_early_z != 0 ? 1 : 0); 329 graphics_key.early_z.Assign(regs.mandated_early_z != 0 ? 1 : 0);
317 graphics_key.gs_input_topology.Assign(graphics_key.unique_hashes[4] != 0 330 graphics_key.gs_input_topology.Assign(regs.draw.topology.Value());
318 ? regs.draw.topology.Value()
319 : Maxwell::PrimitiveTopology{});
320 graphics_key.tessellation_primitive.Assign(regs.tessellation.params.domain_type.Value()); 331 graphics_key.tessellation_primitive.Assign(regs.tessellation.params.domain_type.Value());
321 graphics_key.tessellation_spacing.Assign(regs.tessellation.params.spacing.Value()); 332 graphics_key.tessellation_spacing.Assign(regs.tessellation.params.spacing.Value());
322 graphics_key.tessellation_clockwise.Assign( 333 graphics_key.tessellation_clockwise.Assign(
@@ -415,7 +426,19 @@ std::unique_ptr<GraphicsPipeline> ShaderCache::CreateGraphicsPipeline(
415 std::array<Shader::IR::Program, Maxwell::MaxShaderProgram> programs; 426 std::array<Shader::IR::Program, Maxwell::MaxShaderProgram> programs;
416 const bool uses_vertex_a{key.unique_hashes[0] != 0}; 427 const bool uses_vertex_a{key.unique_hashes[0] != 0};
417 const bool uses_vertex_b{key.unique_hashes[1] != 0}; 428 const bool uses_vertex_b{key.unique_hashes[1] != 0};
429
430 // Layer passthrough generation for devices without GL_ARB_shader_viewport_layer_array
431 Shader::IR::Program* layer_source_program{};
432
418 for (size_t index = 0; index < Maxwell::MaxShaderProgram; ++index) { 433 for (size_t index = 0; index < Maxwell::MaxShaderProgram; ++index) {
434 const bool is_emulated_stage = layer_source_program != nullptr &&
435 index == static_cast<u32>(Maxwell::ShaderType::Geometry);
436 if (key.unique_hashes[index] == 0 && is_emulated_stage) {
437 auto topology = MaxwellToOutputTopology(key.gs_input_topology);
438 programs[index] = GenerateGeometryPassthrough(pools.inst, pools.block, host_info,
439 *layer_source_program, topology);
440 continue;
441 }
419 if (key.unique_hashes[index] == 0) { 442 if (key.unique_hashes[index] == 0) {
420 continue; 443 continue;
421 } 444 }
@@ -443,6 +466,10 @@ std::unique_ptr<GraphicsPipeline> ShaderCache::CreateGraphicsPipeline(
443 Shader::NumDescriptors(program_vb.info.storage_buffers_descriptors); 466 Shader::NumDescriptors(program_vb.info.storage_buffers_descriptors);
444 programs[index] = MergeDualVertexPrograms(program_va, program_vb, env); 467 programs[index] = MergeDualVertexPrograms(program_va, program_vb, env);
445 } 468 }
469
470 if (programs[index].info.requires_layer_emulation) {
471 layer_source_program = &programs[index];
472 }
446 } 473 }
447 const u32 glasm_storage_buffer_limit{device.GetMaxGLASMStorageBufferBlocks()}; 474 const u32 glasm_storage_buffer_limit{device.GetMaxGLASMStorageBufferBlocks()};
448 const bool glasm_use_storage_buffers{total_storage_buffers <= glasm_storage_buffer_limit}; 475 const bool glasm_use_storage_buffers{total_storage_buffers <= glasm_storage_buffer_limit};
@@ -456,7 +483,9 @@ std::unique_ptr<GraphicsPipeline> ShaderCache::CreateGraphicsPipeline(
456 const bool use_glasm{device.UseAssemblyShaders()}; 483 const bool use_glasm{device.UseAssemblyShaders()};
457 const size_t first_index = uses_vertex_a && uses_vertex_b ? 1 : 0; 484 const size_t first_index = uses_vertex_a && uses_vertex_b ? 1 : 0;
458 for (size_t index = first_index; index < Maxwell::MaxShaderProgram; ++index) { 485 for (size_t index = first_index; index < Maxwell::MaxShaderProgram; ++index) {
459 if (key.unique_hashes[index] == 0) { 486 const bool is_emulated_stage = layer_source_program != nullptr &&
487 index == static_cast<u32>(Maxwell::ShaderType::Geometry);
488 if (key.unique_hashes[index] == 0 && !is_emulated_stage) {
460 continue; 489 continue;
461 } 490 }
462 UNIMPLEMENTED_IF(index == 0); 491 UNIMPLEMENTED_IF(index == 0);
diff --git a/src/video_core/renderer_opengl/gl_shader_cache.h b/src/video_core/renderer_opengl/gl_shader_cache.h
index 89f181fe3..53ffea904 100644
--- a/src/video_core/renderer_opengl/gl_shader_cache.h
+++ b/src/video_core/renderer_opengl/gl_shader_cache.h
@@ -4,7 +4,6 @@
4#pragma once 4#pragma once
5 5
6#include <filesystem> 6#include <filesystem>
7#include <stop_token>
8#include <unordered_map> 7#include <unordered_map>
9 8
10#include "common/common_types.h" 9#include "common/common_types.h"
diff --git a/src/video_core/renderer_vulkan/fixed_pipeline_state.cpp b/src/video_core/renderer_vulkan/fixed_pipeline_state.cpp
index 98cc26679..f3f08b42c 100644
--- a/src/video_core/renderer_vulkan/fixed_pipeline_state.cpp
+++ b/src/video_core/renderer_vulkan/fixed_pipeline_state.cpp
@@ -7,6 +7,7 @@
7#include "common/bit_cast.h" 7#include "common/bit_cast.h"
8#include "common/cityhash.h" 8#include "common/cityhash.h"
9#include "common/common_types.h" 9#include "common/common_types.h"
10#include "common/polyfill_ranges.h"
10#include "video_core/renderer_vulkan/fixed_pipeline_state.h" 11#include "video_core/renderer_vulkan/fixed_pipeline_state.h"
11#include "video_core/renderer_vulkan/vk_state_tracker.h" 12#include "video_core/renderer_vulkan/vk_state_tracker.h"
12 13
diff --git a/src/video_core/renderer_vulkan/vk_blit_screen.cpp b/src/video_core/renderer_vulkan/vk_blit_screen.cpp
index 89426121f..6e5abade4 100644
--- a/src/video_core/renderer_vulkan/vk_blit_screen.cpp
+++ b/src/video_core/renderer_vulkan/vk_blit_screen.cpp
@@ -10,6 +10,7 @@
10#include "common/assert.h" 10#include "common/assert.h"
11#include "common/common_types.h" 11#include "common/common_types.h"
12#include "common/math_util.h" 12#include "common/math_util.h"
13#include "common/polyfill_ranges.h"
13#include "common/settings.h" 14#include "common/settings.h"
14#include "core/core.h" 15#include "core/core.h"
15#include "core/frontend/emu_window.h" 16#include "core/frontend/emu_window.h"
diff --git a/src/video_core/renderer_vulkan/vk_descriptor_pool.cpp b/src/video_core/renderer_vulkan/vk_descriptor_pool.cpp
index c7196b64e..b5ae6443c 100644
--- a/src/video_core/renderer_vulkan/vk_descriptor_pool.cpp
+++ b/src/video_core/renderer_vulkan/vk_descriptor_pool.cpp
@@ -7,6 +7,7 @@
7#include <vector> 7#include <vector>
8 8
9#include "common/common_types.h" 9#include "common/common_types.h"
10#include "common/polyfill_ranges.h"
10#include "video_core/renderer_vulkan/vk_descriptor_pool.h" 11#include "video_core/renderer_vulkan/vk_descriptor_pool.h"
11#include "video_core/renderer_vulkan/vk_resource_pool.h" 12#include "video_core/renderer_vulkan/vk_resource_pool.h"
12#include "video_core/renderer_vulkan/vk_scheduler.h" 13#include "video_core/renderer_vulkan/vk_scheduler.h"
diff --git a/src/video_core/renderer_vulkan/vk_master_semaphore.h b/src/video_core/renderer_vulkan/vk_master_semaphore.h
index 362ed579a..689f02ea5 100644
--- a/src/video_core/renderer_vulkan/vk_master_semaphore.h
+++ b/src/video_core/renderer_vulkan/vk_master_semaphore.h
@@ -7,6 +7,7 @@
7#include <thread> 7#include <thread>
8 8
9#include "common/common_types.h" 9#include "common/common_types.h"
10#include "common/polyfill_thread.h"
10#include "video_core/vulkan_common/vulkan_wrapper.h" 11#include "video_core/vulkan_common/vulkan_wrapper.h"
11 12
12namespace Vulkan { 13namespace Vulkan {
diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp
index d4b0a542a..29da442fa 100644
--- a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp
+++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp
@@ -46,6 +46,7 @@ MICROPROFILE_DECLARE(Vulkan_PipelineCache);
46namespace { 46namespace {
47using Shader::Backend::SPIRV::EmitSPIRV; 47using Shader::Backend::SPIRV::EmitSPIRV;
48using Shader::Maxwell::ConvertLegacyToGeneric; 48using Shader::Maxwell::ConvertLegacyToGeneric;
49using Shader::Maxwell::GenerateGeometryPassthrough;
49using Shader::Maxwell::MergeDualVertexPrograms; 50using Shader::Maxwell::MergeDualVertexPrograms;
50using Shader::Maxwell::TranslateProgram; 51using Shader::Maxwell::TranslateProgram;
51using VideoCommon::ComputeEnvironment; 52using VideoCommon::ComputeEnvironment;
@@ -53,13 +54,24 @@ using VideoCommon::FileEnvironment;
53using VideoCommon::GenericEnvironment; 54using VideoCommon::GenericEnvironment;
54using VideoCommon::GraphicsEnvironment; 55using VideoCommon::GraphicsEnvironment;
55 56
56constexpr u32 CACHE_VERSION = 7; 57constexpr u32 CACHE_VERSION = 8;
57 58
58template <typename Container> 59template <typename Container>
59auto MakeSpan(Container& container) { 60auto MakeSpan(Container& container) {
60 return std::span(container.data(), container.size()); 61 return std::span(container.data(), container.size());
61} 62}
62 63
64Shader::OutputTopology MaxwellToOutputTopology(Maxwell::PrimitiveTopology topology) {
65 switch (topology) {
66 case Maxwell::PrimitiveTopology::Points:
67 return Shader::OutputTopology::PointList;
68 case Maxwell::PrimitiveTopology::LineStrip:
69 return Shader::OutputTopology::LineStrip;
70 default:
71 return Shader::OutputTopology::TriangleStrip;
72 }
73}
74
63Shader::CompareFunction MaxwellToCompareFunction(Maxwell::ComparisonOp comparison) { 75Shader::CompareFunction MaxwellToCompareFunction(Maxwell::ComparisonOp comparison) {
64 switch (comparison) { 76 switch (comparison) {
65 case Maxwell::ComparisonOp::Never_D3D: 77 case Maxwell::ComparisonOp::Never_D3D:
@@ -277,7 +289,7 @@ PipelineCache::PipelineCache(RasterizerVulkan& rasterizer_, const Device& device
277 const auto& float_control{device.FloatControlProperties()}; 289 const auto& float_control{device.FloatControlProperties()};
278 const VkDriverIdKHR driver_id{device.GetDriverID()}; 290 const VkDriverIdKHR driver_id{device.GetDriverID()};
279 profile = Shader::Profile{ 291 profile = Shader::Profile{
280 .supported_spirv = device.IsKhrSpirv1_4Supported() ? 0x00010400U : 0x00010000U, 292 .supported_spirv = device.SupportedSpirvVersion(),
281 .unified_descriptor_binding = true, 293 .unified_descriptor_binding = true,
282 .support_descriptor_aliasing = true, 294 .support_descriptor_aliasing = true,
283 .support_int8 = device.IsInt8Supported(), 295 .support_int8 = device.IsInt8Supported(),
@@ -327,6 +339,7 @@ PipelineCache::PipelineCache(RasterizerVulkan& rasterizer_, const Device& device
327 .needs_demote_reorder = driver_id == VK_DRIVER_ID_AMD_PROPRIETARY_KHR || 339 .needs_demote_reorder = driver_id == VK_DRIVER_ID_AMD_PROPRIETARY_KHR ||
328 driver_id == VK_DRIVER_ID_AMD_OPEN_SOURCE_KHR, 340 driver_id == VK_DRIVER_ID_AMD_OPEN_SOURCE_KHR,
329 .support_snorm_render_buffer = true, 341 .support_snorm_render_buffer = true,
342 .support_viewport_index_layer = device.IsExtShaderViewportIndexLayerSupported(),
330 }; 343 };
331} 344}
332 345
@@ -509,7 +522,19 @@ std::unique_ptr<GraphicsPipeline> PipelineCache::CreateGraphicsPipeline(
509 std::array<Shader::IR::Program, Maxwell::MaxShaderProgram> programs; 522 std::array<Shader::IR::Program, Maxwell::MaxShaderProgram> programs;
510 const bool uses_vertex_a{key.unique_hashes[0] != 0}; 523 const bool uses_vertex_a{key.unique_hashes[0] != 0};
511 const bool uses_vertex_b{key.unique_hashes[1] != 0}; 524 const bool uses_vertex_b{key.unique_hashes[1] != 0};
525
526 // Layer passthrough generation for devices without VK_EXT_shader_viewport_index_layer
527 Shader::IR::Program* layer_source_program{};
528
512 for (size_t index = 0; index < Maxwell::MaxShaderProgram; ++index) { 529 for (size_t index = 0; index < Maxwell::MaxShaderProgram; ++index) {
530 const bool is_emulated_stage = layer_source_program != nullptr &&
531 index == static_cast<u32>(Maxwell::ShaderType::Geometry);
532 if (key.unique_hashes[index] == 0 && is_emulated_stage) {
533 auto topology = MaxwellToOutputTopology(key.state.topology);
534 programs[index] = GenerateGeometryPassthrough(pools.inst, pools.block, host_info,
535 *layer_source_program, topology);
536 continue;
537 }
513 if (key.unique_hashes[index] == 0) { 538 if (key.unique_hashes[index] == 0) {
514 continue; 539 continue;
515 } 540 }
@@ -530,6 +555,10 @@ std::unique_ptr<GraphicsPipeline> PipelineCache::CreateGraphicsPipeline(
530 auto program_vb{TranslateProgram(pools.inst, pools.block, env, cfg, host_info)}; 555 auto program_vb{TranslateProgram(pools.inst, pools.block, env, cfg, host_info)};
531 programs[index] = MergeDualVertexPrograms(program_va, program_vb, env); 556 programs[index] = MergeDualVertexPrograms(program_va, program_vb, env);
532 } 557 }
558
559 if (programs[index].info.requires_layer_emulation) {
560 layer_source_program = &programs[index];
561 }
533 } 562 }
534 std::array<const Shader::Info*, Maxwell::MaxShaderStage> infos{}; 563 std::array<const Shader::Info*, Maxwell::MaxShaderStage> infos{};
535 std::array<vk::ShaderModule, Maxwell::MaxShaderStage> modules; 564 std::array<vk::ShaderModule, Maxwell::MaxShaderStage> modules;
@@ -538,7 +567,9 @@ std::unique_ptr<GraphicsPipeline> PipelineCache::CreateGraphicsPipeline(
538 Shader::Backend::Bindings binding; 567 Shader::Backend::Bindings binding;
539 for (size_t index = uses_vertex_a && uses_vertex_b ? 1 : 0; index < Maxwell::MaxShaderProgram; 568 for (size_t index = uses_vertex_a && uses_vertex_b ? 1 : 0; index < Maxwell::MaxShaderProgram;
540 ++index) { 569 ++index) {
541 if (key.unique_hashes[index] == 0) { 570 const bool is_emulated_stage = layer_source_program != nullptr &&
571 index == static_cast<u32>(Maxwell::ShaderType::Geometry);
572 if (key.unique_hashes[index] == 0 && !is_emulated_stage) {
542 continue; 573 continue;
543 } 574 }
544 UNIMPLEMENTED_IF(index == 0); 575 UNIMPLEMENTED_IF(index == 0);
diff --git a/src/video_core/renderer_vulkan/vk_render_pass_cache.h b/src/video_core/renderer_vulkan/vk_render_pass_cache.h
index dc21b7e69..91ad4bf57 100644
--- a/src/video_core/renderer_vulkan/vk_render_pass_cache.h
+++ b/src/video_core/renderer_vulkan/vk_render_pass_cache.h
@@ -12,7 +12,7 @@
12namespace Vulkan { 12namespace Vulkan {
13 13
14struct RenderPassKey { 14struct RenderPassKey {
15 auto operator<=>(const RenderPassKey&) const noexcept = default; 15 bool operator==(const RenderPassKey&) const noexcept = default;
16 16
17 std::array<VideoCore::Surface::PixelFormat, 8> color_formats; 17 std::array<VideoCore::Surface::PixelFormat, 8> color_formats;
18 VideoCore::Surface::PixelFormat depth_format; 18 VideoCore::Surface::PixelFormat depth_format;
diff --git a/src/video_core/renderer_vulkan/vk_scheduler.cpp b/src/video_core/renderer_vulkan/vk_scheduler.cpp
index 4a7b633b7..c09fb3e98 100644
--- a/src/video_core/renderer_vulkan/vk_scheduler.cpp
+++ b/src/video_core/renderer_vulkan/vk_scheduler.cpp
@@ -145,7 +145,7 @@ void Scheduler::WorkerThread(std::stop_token stop_token) {
145 if (work_queue.empty()) { 145 if (work_queue.empty()) {
146 wait_cv.notify_all(); 146 wait_cv.notify_all();
147 } 147 }
148 work_cv.wait(lock, stop_token, [this] { return !work_queue.empty(); }); 148 Common::CondvarWait(work_cv, lock, stop_token, [&] { return !work_queue.empty(); });
149 if (stop_token.stop_requested()) { 149 if (stop_token.stop_requested()) {
150 continue; 150 continue;
151 } 151 }
diff --git a/src/video_core/renderer_vulkan/vk_scheduler.h b/src/video_core/renderer_vulkan/vk_scheduler.h
index 929216749..3858c506c 100644
--- a/src/video_core/renderer_vulkan/vk_scheduler.h
+++ b/src/video_core/renderer_vulkan/vk_scheduler.h
@@ -12,6 +12,7 @@
12 12
13#include "common/alignment.h" 13#include "common/alignment.h"
14#include "common/common_types.h" 14#include "common/common_types.h"
15#include "common/polyfill_thread.h"
15#include "video_core/renderer_vulkan/vk_master_semaphore.h" 16#include "video_core/renderer_vulkan/vk_master_semaphore.h"
16#include "video_core/vulkan_common/vulkan_wrapper.h" 17#include "video_core/vulkan_common/vulkan_wrapper.h"
17 18
diff --git a/src/video_core/renderer_vulkan/vk_swapchain.cpp b/src/video_core/renderer_vulkan/vk_swapchain.cpp
index 706d9ba74..d7be417f5 100644
--- a/src/video_core/renderer_vulkan/vk_swapchain.cpp
+++ b/src/video_core/renderer_vulkan/vk_swapchain.cpp
@@ -7,6 +7,7 @@
7#include <vector> 7#include <vector>
8 8
9#include "common/logging/log.h" 9#include "common/logging/log.h"
10#include "common/polyfill_ranges.h"
10#include "common/settings.h" 11#include "common/settings.h"
11#include "core/core.h" 12#include "core/core.h"
12#include "video_core/renderer_vulkan/vk_scheduler.h" 13#include "video_core/renderer_vulkan/vk_scheduler.h"
diff --git a/src/video_core/shader_cache.h b/src/video_core/shader_cache.h
index a4391202d..f3cc4c70b 100644
--- a/src/video_core/shader_cache.h
+++ b/src/video_core/shader_cache.h
@@ -12,6 +12,7 @@
12#include <vector> 12#include <vector>
13 13
14#include "common/common_types.h" 14#include "common/common_types.h"
15#include "common/polyfill_ranges.h"
15#include "video_core/control/channel_state_cache.h" 16#include "video_core/control/channel_state_cache.h"
16#include "video_core/rasterizer_interface.h" 17#include "video_core/rasterizer_interface.h"
17#include "video_core/shader_environment.h" 18#include "video_core/shader_environment.h"
diff --git a/src/video_core/shader_environment.cpp b/src/video_core/shader_environment.cpp
index f24f320b6..958810747 100644
--- a/src/video_core/shader_environment.cpp
+++ b/src/video_core/shader_environment.cpp
@@ -15,6 +15,7 @@
15#include "common/fs/fs.h" 15#include "common/fs/fs.h"
16#include "common/fs/path_util.h" 16#include "common/fs/path_util.h"
17#include "common/logging/log.h" 17#include "common/logging/log.h"
18#include "common/polyfill_ranges.h"
18#include "shader_recompiler/environment.h" 19#include "shader_recompiler/environment.h"
19#include "video_core/engines/kepler_compute.h" 20#include "video_core/engines/kepler_compute.h"
20#include "video_core/memory_manager.h" 21#include "video_core/memory_manager.h"
diff --git a/src/video_core/shader_environment.h b/src/video_core/shader_environment.h
index bb55b029f..1342fab1e 100644
--- a/src/video_core/shader_environment.h
+++ b/src/video_core/shader_environment.h
@@ -10,12 +10,12 @@
10#include <memory> 10#include <memory>
11#include <optional> 11#include <optional>
12#include <span> 12#include <span>
13#include <stop_token>
14#include <type_traits> 13#include <type_traits>
15#include <unordered_map> 14#include <unordered_map>
16#include <vector> 15#include <vector>
17 16
18#include "common/common_types.h" 17#include "common/common_types.h"
18#include "common/polyfill_thread.h"
19#include "common/unique_function.h" 19#include "common/unique_function.h"
20#include "shader_recompiler/environment.h" 20#include "shader_recompiler/environment.h"
21#include "video_core/engines/maxwell_3d.h" 21#include "video_core/engines/maxwell_3d.h"
diff --git a/src/video_core/texture_cache/formatter.cpp b/src/video_core/texture_cache/formatter.cpp
index ee4f2d406..418890126 100644
--- a/src/video_core/texture_cache/formatter.cpp
+++ b/src/video_core/texture_cache/formatter.cpp
@@ -4,6 +4,7 @@
4#include <algorithm> 4#include <algorithm>
5#include <string> 5#include <string>
6 6
7#include "common/polyfill_ranges.h"
7#include "video_core/texture_cache/formatter.h" 8#include "video_core/texture_cache/formatter.h"
8#include "video_core/texture_cache/image_base.h" 9#include "video_core/texture_cache/image_base.h"
9#include "video_core/texture_cache/image_info.h" 10#include "video_core/texture_cache/image_info.h"
diff --git a/src/video_core/texture_cache/render_targets.h b/src/video_core/texture_cache/render_targets.h
index 1efbd6507..0829d773a 100644
--- a/src/video_core/texture_cache/render_targets.h
+++ b/src/video_core/texture_cache/render_targets.h
@@ -13,7 +13,7 @@ namespace VideoCommon {
13 13
14/// Framebuffer properties used to lookup a framebuffer 14/// Framebuffer properties used to lookup a framebuffer
15struct RenderTargets { 15struct RenderTargets {
16 constexpr auto operator<=>(const RenderTargets&) const noexcept = default; 16 constexpr bool operator==(const RenderTargets&) const noexcept = default;
17 17
18 constexpr bool Contains(std::span<const ImageViewId> elements) const noexcept { 18 constexpr bool Contains(std::span<const ImageViewId> elements) const noexcept {
19 const auto contains = [elements](ImageViewId item) { 19 const auto contains = [elements](ImageViewId item) {
diff --git a/src/video_core/texture_cache/slot_vector.h b/src/video_core/texture_cache/slot_vector.h
index 46e8a86e6..1e2aad76a 100644
--- a/src/video_core/texture_cache/slot_vector.h
+++ b/src/video_core/texture_cache/slot_vector.h
@@ -12,6 +12,7 @@
12 12
13#include "common/assert.h" 13#include "common/assert.h"
14#include "common/common_types.h" 14#include "common/common_types.h"
15#include "common/polyfill_ranges.h"
15 16
16namespace VideoCommon { 17namespace VideoCommon {
17 18
diff --git a/src/video_core/texture_cache/texture_cache_base.h b/src/video_core/texture_cache/texture_cache_base.h
index 9db7195bf..587339a31 100644
--- a/src/video_core/texture_cache/texture_cache_base.h
+++ b/src/video_core/texture_cache/texture_cache_base.h
@@ -16,6 +16,7 @@
16#include "common/hash.h" 16#include "common/hash.h"
17#include "common/literals.h" 17#include "common/literals.h"
18#include "common/lru_cache.h" 18#include "common/lru_cache.h"
19#include "common/polyfill_ranges.h"
19#include "video_core/compatible_formats.h" 20#include "video_core/compatible_formats.h"
20#include "video_core/control/channel_state_cache.h" 21#include "video_core/control/channel_state_cache.h"
21#include "video_core/delayed_destruction_ring.h" 22#include "video_core/delayed_destruction_ring.h"
@@ -60,8 +61,6 @@ public:
60 TextureCacheChannelInfo(Tegra::Control::ChannelState& state) noexcept; 61 TextureCacheChannelInfo(Tegra::Control::ChannelState& state) noexcept;
61 TextureCacheChannelInfo(const TextureCacheChannelInfo& state) = delete; 62 TextureCacheChannelInfo(const TextureCacheChannelInfo& state) = delete;
62 TextureCacheChannelInfo& operator=(const TextureCacheChannelInfo&) = delete; 63 TextureCacheChannelInfo& operator=(const TextureCacheChannelInfo&) = delete;
63 TextureCacheChannelInfo(TextureCacheChannelInfo&& other) noexcept = default;
64 TextureCacheChannelInfo& operator=(TextureCacheChannelInfo&& other) noexcept = default;
65 64
66 DescriptorTable<TICEntry> graphics_image_table{gpu_memory}; 65 DescriptorTable<TICEntry> graphics_image_table{gpu_memory};
67 DescriptorTable<TSCEntry> graphics_sampler_table{gpu_memory}; 66 DescriptorTable<TSCEntry> graphics_sampler_table{gpu_memory};
diff --git a/src/video_core/textures/astc.cpp b/src/video_core/textures/astc.cpp
index 69a32819a..e8d7c7863 100644
--- a/src/video_core/textures/astc.cpp
+++ b/src/video_core/textures/astc.cpp
@@ -15,6 +15,7 @@
15 15
16#include "common/alignment.h" 16#include "common/alignment.h"
17#include "common/common_types.h" 17#include "common/common_types.h"
18#include "common/polyfill_ranges.h"
18#include "common/thread_worker.h" 19#include "common/thread_worker.h"
19#include "video_core/textures/astc.h" 20#include "video_core/textures/astc.h"
20 21
diff --git a/src/video_core/transform_feedback.cpp b/src/video_core/transform_feedback.cpp
index 45071185a..155599316 100644
--- a/src/video_core/transform_feedback.cpp
+++ b/src/video_core/transform_feedback.cpp
@@ -7,6 +7,7 @@
7 7
8#include "common/alignment.h" 8#include "common/alignment.h"
9#include "common/assert.h" 9#include "common/assert.h"
10#include "common/polyfill_ranges.h"
10#include "shader_recompiler/shader_info.h" 11#include "shader_recompiler/shader_info.h"
11#include "video_core/transform_feedback.h" 12#include "video_core/transform_feedback.h"
12 13
diff --git a/src/video_core/vulkan_common/vulkan_device.cpp b/src/video_core/vulkan_common/vulkan_device.cpp
index ddecfca13..652329c38 100644
--- a/src/video_core/vulkan_common/vulkan_device.cpp
+++ b/src/video_core/vulkan_common/vulkan_device.cpp
@@ -12,6 +12,7 @@
12 12
13#include "common/assert.h" 13#include "common/assert.h"
14#include "common/literals.h" 14#include "common/literals.h"
15#include "common/polyfill_ranges.h"
15#include "common/settings.h" 16#include "common/settings.h"
16#include "video_core/vulkan_common/nsight_aftermath_tracker.h" 17#include "video_core/vulkan_common/nsight_aftermath_tracker.h"
17#include "video_core/vulkan_common/vulkan_device.h" 18#include "video_core/vulkan_common/vulkan_device.h"
@@ -74,23 +75,14 @@ enum class NvidiaArchitecture {
74}; 75};
75 76
76constexpr std::array REQUIRED_EXTENSIONS{ 77constexpr std::array REQUIRED_EXTENSIONS{
77 VK_KHR_MAINTENANCE1_EXTENSION_NAME,
78 VK_KHR_STORAGE_BUFFER_STORAGE_CLASS_EXTENSION_NAME,
79 VK_KHR_SHADER_DRAW_PARAMETERS_EXTENSION_NAME,
80 VK_KHR_16BIT_STORAGE_EXTENSION_NAME,
81 VK_KHR_8BIT_STORAGE_EXTENSION_NAME,
82 VK_KHR_DRIVER_PROPERTIES_EXTENSION_NAME,
83 VK_KHR_DESCRIPTOR_UPDATE_TEMPLATE_EXTENSION_NAME,
84 VK_KHR_TIMELINE_SEMAPHORE_EXTENSION_NAME,
85 VK_KHR_SAMPLER_MIRROR_CLAMP_TO_EDGE_EXTENSION_NAME,
86 VK_KHR_SHADER_FLOAT_CONTROLS_EXTENSION_NAME,
87 VK_KHR_VARIABLE_POINTERS_EXTENSION_NAME,
88 VK_EXT_VERTEX_ATTRIBUTE_DIVISOR_EXTENSION_NAME, 78 VK_EXT_VERTEX_ATTRIBUTE_DIVISOR_EXTENSION_NAME,
89 VK_EXT_SHADER_SUBGROUP_BALLOT_EXTENSION_NAME,
90 VK_EXT_SHADER_SUBGROUP_VOTE_EXTENSION_NAME,
91 VK_EXT_ROBUSTNESS_2_EXTENSION_NAME, 79 VK_EXT_ROBUSTNESS_2_EXTENSION_NAME,
80
81 // Core in 1.2, but required due to use of extension methods,
82 // and well-supported by drivers
83 VK_KHR_TIMELINE_SEMAPHORE_EXTENSION_NAME,
84 VK_KHR_DESCRIPTOR_UPDATE_TEMPLATE_EXTENSION_NAME,
92 VK_EXT_HOST_QUERY_RESET_EXTENSION_NAME, 85 VK_EXT_HOST_QUERY_RESET_EXTENSION_NAME,
93 VK_EXT_SHADER_DEMOTE_TO_HELPER_INVOCATION_EXTENSION_NAME,
94#ifdef _WIN32 86#ifdef _WIN32
95 VK_KHR_EXTERNAL_MEMORY_WIN32_EXTENSION_NAME, 87 VK_KHR_EXTERNAL_MEMORY_WIN32_EXTENSION_NAME,
96#endif 88#endif
@@ -99,6 +91,17 @@ constexpr std::array REQUIRED_EXTENSIONS{
99#endif 91#endif
100}; 92};
101 93
94constexpr std::array REQUIRED_EXTENSIONS_BEFORE_1_2{
95 VK_KHR_8BIT_STORAGE_EXTENSION_NAME,
96 VK_KHR_SHADER_FLOAT_CONTROLS_EXTENSION_NAME,
97 VK_KHR_SAMPLER_MIRROR_CLAMP_TO_EDGE_EXTENSION_NAME,
98 VK_KHR_DRIVER_PROPERTIES_EXTENSION_NAME,
99};
100
101constexpr std::array REQUIRED_EXTENSIONS_BEFORE_1_3{
102 VK_EXT_SHADER_DEMOTE_TO_HELPER_INVOCATION_EXTENSION_NAME,
103};
104
102template <typename T> 105template <typename T>
103void SetNext(void**& next, T& data) { 106void SetNext(void**& next, T& data) {
104 *next = &data; 107 *next = &data;
@@ -327,7 +330,8 @@ NvidiaArchitecture GetNvidiaArchitecture(vk::PhysicalDevice physical,
327Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR surface, 330Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR surface,
328 const vk::InstanceDispatch& dld_) 331 const vk::InstanceDispatch& dld_)
329 : instance{instance_}, dld{dld_}, physical{physical_}, properties{physical.GetProperties()}, 332 : instance{instance_}, dld{dld_}, physical{physical_}, properties{physical.GetProperties()},
330 supported_extensions{GetSupportedExtensions(physical)}, 333 instance_version{properties.apiVersion}, supported_extensions{GetSupportedExtensions(
334 physical)},
331 format_properties(GetFormatProperties(physical)) { 335 format_properties(GetFormatProperties(physical)) {
332 CheckSuitability(surface != nullptr); 336 CheckSuitability(surface != nullptr);
333 SetupFamilies(surface); 337 SetupFamilies(surface);
@@ -451,8 +455,8 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
451 }; 455 };
452 SetNext(next, variable_pointers); 456 SetNext(next, variable_pointers);
453 457
454 VkPhysicalDeviceShaderDemoteToHelperInvocationFeaturesEXT demote{ 458 VkPhysicalDeviceShaderDemoteToHelperInvocationFeatures demote{
455 .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_DEMOTE_TO_HELPER_INVOCATION_FEATURES_EXT, 459 .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_DEMOTE_TO_HELPER_INVOCATION_FEATURES,
456 .pNext = nullptr, 460 .pNext = nullptr,
457 .shaderDemoteToHelperInvocation = true, 461 .shaderDemoteToHelperInvocation = true,
458 }; 462 };
@@ -896,28 +900,51 @@ std::string Device::GetDriverName() const {
896 } 900 }
897} 901}
898 902
903static std::vector<const char*> ExtensionsRequiredForInstanceVersion(u32 available_version) {
904 std::vector<const char*> extensions{REQUIRED_EXTENSIONS.begin(), REQUIRED_EXTENSIONS.end()};
905
906 if (available_version < VK_API_VERSION_1_2) {
907 extensions.insert(extensions.end(), REQUIRED_EXTENSIONS_BEFORE_1_2.begin(),
908 REQUIRED_EXTENSIONS_BEFORE_1_2.end());
909 }
910
911 if (available_version < VK_API_VERSION_1_3) {
912 extensions.insert(extensions.end(), REQUIRED_EXTENSIONS_BEFORE_1_3.begin(),
913 REQUIRED_EXTENSIONS_BEFORE_1_3.end());
914 }
915
916 return extensions;
917}
918
899void Device::CheckSuitability(bool requires_swapchain) const { 919void Device::CheckSuitability(bool requires_swapchain) const {
900 std::bitset<REQUIRED_EXTENSIONS.size()> available_extensions; 920 std::vector<const char*> required_extensions =
901 bool has_swapchain = false; 921 ExtensionsRequiredForInstanceVersion(instance_version);
902 for (const VkExtensionProperties& property : physical.EnumerateDeviceExtensionProperties()) { 922 std::vector<const char*> available_extensions;
903 const std::string_view name{property.extensionName}; 923
904 for (size_t i = 0; i < REQUIRED_EXTENSIONS.size(); ++i) { 924 if (requires_swapchain) {
905 if (available_extensions[i]) { 925 required_extensions.push_back(VK_KHR_SWAPCHAIN_EXTENSION_NAME);
906 continue;
907 }
908 available_extensions[i] = name == REQUIRED_EXTENSIONS[i];
909 }
910 has_swapchain = has_swapchain || name == VK_KHR_SWAPCHAIN_EXTENSION_NAME;
911 } 926 }
912 for (size_t i = 0; i < REQUIRED_EXTENSIONS.size(); ++i) { 927
913 if (available_extensions[i]) { 928 auto extension_properties = physical.EnumerateDeviceExtensionProperties();
914 continue; 929
930 for (const VkExtensionProperties& property : extension_properties) {
931 available_extensions.push_back(property.extensionName);
932 }
933
934 bool has_all_required_extensions = true;
935 for (const char* requirement_name : required_extensions) {
936 const bool found =
937 std::ranges::any_of(available_extensions, [&](const char* extension_name) {
938 return std::strcmp(requirement_name, extension_name) == 0;
939 });
940
941 if (!found) {
942 LOG_ERROR(Render_Vulkan, "Missing required extension: {}", requirement_name);
943 has_all_required_extensions = false;
915 } 944 }
916 LOG_ERROR(Render_Vulkan, "Missing required extension: {}", REQUIRED_EXTENSIONS[i]);
917 throw vk::Exception(VK_ERROR_EXTENSION_NOT_PRESENT);
918 } 945 }
919 if (requires_swapchain && !has_swapchain) { 946
920 LOG_ERROR(Render_Vulkan, "Missing required extension: VK_KHR_swapchain"); 947 if (!has_all_required_extensions) {
921 throw vk::Exception(VK_ERROR_EXTENSION_NOT_PRESENT); 948 throw vk::Exception(VK_ERROR_EXTENSION_NOT_PRESENT);
922 } 949 }
923 950
@@ -940,9 +967,8 @@ void Device::CheckSuitability(bool requires_swapchain) const {
940 throw vk::Exception(VK_ERROR_FEATURE_NOT_PRESENT); 967 throw vk::Exception(VK_ERROR_FEATURE_NOT_PRESENT);
941 } 968 }
942 } 969 }
943 VkPhysicalDeviceShaderDemoteToHelperInvocationFeaturesEXT demote{}; 970 VkPhysicalDeviceShaderDemoteToHelperInvocationFeatures demote{};
944 demote.sType = 971 demote.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_DEMOTE_TO_HELPER_INVOCATION_FEATURES;
945 VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_DEMOTE_TO_HELPER_INVOCATION_FEATURES_EXT;
946 demote.pNext = nullptr; 972 demote.pNext = nullptr;
947 973
948 VkPhysicalDeviceVariablePointerFeaturesKHR variable_pointers{}; 974 VkPhysicalDeviceVariablePointerFeaturesKHR variable_pointers{};
@@ -960,7 +986,7 @@ void Device::CheckSuitability(bool requires_swapchain) const {
960 physical.GetFeatures2KHR(features2); 986 physical.GetFeatures2KHR(features2);
961 987
962 const VkPhysicalDeviceFeatures& features{features2.features}; 988 const VkPhysicalDeviceFeatures& features{features2.features};
963 const std::array feature_report{ 989 std::vector feature_report{
964 std::make_pair(features.robustBufferAccess, "robustBufferAccess"), 990 std::make_pair(features.robustBufferAccess, "robustBufferAccess"),
965 std::make_pair(features.vertexPipelineStoresAndAtomics, "vertexPipelineStoresAndAtomics"), 991 std::make_pair(features.vertexPipelineStoresAndAtomics, "vertexPipelineStoresAndAtomics"),
966 std::make_pair(features.imageCubeArray, "imageCubeArray"), 992 std::make_pair(features.imageCubeArray, "imageCubeArray"),
@@ -983,27 +1009,30 @@ void Device::CheckSuitability(bool requires_swapchain) const {
983 "shaderStorageImageWriteWithoutFormat"), 1009 "shaderStorageImageWriteWithoutFormat"),
984 std::make_pair(features.shaderClipDistance, "shaderClipDistance"), 1010 std::make_pair(features.shaderClipDistance, "shaderClipDistance"),
985 std::make_pair(features.shaderCullDistance, "shaderCullDistance"), 1011 std::make_pair(features.shaderCullDistance, "shaderCullDistance"),
986 std::make_pair(demote.shaderDemoteToHelperInvocation, "shaderDemoteToHelperInvocation"),
987 std::make_pair(variable_pointers.variablePointers, "variablePointers"), 1012 std::make_pair(variable_pointers.variablePointers, "variablePointers"),
988 std::make_pair(variable_pointers.variablePointersStorageBuffer, 1013 std::make_pair(variable_pointers.variablePointersStorageBuffer,
989 "variablePointersStorageBuffer"), 1014 "variablePointersStorageBuffer"),
990 std::make_pair(robustness2.robustBufferAccess2, "robustBufferAccess2"), 1015 std::make_pair(robustness2.robustBufferAccess2, "robustBufferAccess2"),
991 std::make_pair(robustness2.robustImageAccess2, "robustImageAccess2"), 1016 std::make_pair(robustness2.robustImageAccess2, "robustImageAccess2"),
992 std::make_pair(robustness2.nullDescriptor, "nullDescriptor"), 1017 std::make_pair(robustness2.nullDescriptor, "nullDescriptor"),
1018 std::make_pair(demote.shaderDemoteToHelperInvocation, "shaderDemoteToHelperInvocation"),
993 }; 1019 };
1020
1021 bool has_all_required_features = true;
994 for (const auto& [is_supported, name] : feature_report) { 1022 for (const auto& [is_supported, name] : feature_report) {
995 if (is_supported) { 1023 if (!is_supported) {
996 continue; 1024 LOG_ERROR(Render_Vulkan, "Missing required feature: {}", name);
1025 has_all_required_features = false;
997 } 1026 }
998 LOG_ERROR(Render_Vulkan, "Missing required feature: {}", name); 1027 }
1028
1029 if (!has_all_required_features) {
999 throw vk::Exception(VK_ERROR_FEATURE_NOT_PRESENT); 1030 throw vk::Exception(VK_ERROR_FEATURE_NOT_PRESENT);
1000 } 1031 }
1001} 1032}
1002 1033
1003std::vector<const char*> Device::LoadExtensions(bool requires_surface) { 1034std::vector<const char*> Device::LoadExtensions(bool requires_surface) {
1004 std::vector<const char*> extensions; 1035 std::vector<const char*> extensions = ExtensionsRequiredForInstanceVersion(instance_version);
1005 extensions.reserve(8 + REQUIRED_EXTENSIONS.size());
1006 extensions.insert(extensions.begin(), REQUIRED_EXTENSIONS.begin(), REQUIRED_EXTENSIONS.end());
1007 if (requires_surface) { 1036 if (requires_surface) {
1008 extensions.push_back(VK_KHR_SWAPCHAIN_EXTENSION_NAME); 1037 extensions.push_back(VK_KHR_SWAPCHAIN_EXTENSION_NAME);
1009 } 1038 }
diff --git a/src/video_core/vulkan_common/vulkan_device.h b/src/video_core/vulkan_common/vulkan_device.h
index d7cc6c593..c85fbba77 100644
--- a/src/video_core/vulkan_common/vulkan_device.h
+++ b/src/video_core/vulkan_common/vulkan_device.h
@@ -211,11 +211,6 @@ public:
211 return khr_uniform_buffer_standard_layout; 211 return khr_uniform_buffer_standard_layout;
212 } 212 }
213 213
214 /// Returns true if the device supports VK_KHR_spirv_1_4.
215 bool IsKhrSpirv1_4Supported() const {
216 return khr_spirv_1_4;
217 }
218
219 /// Returns true if the device supports VK_KHR_push_descriptor. 214 /// Returns true if the device supports VK_KHR_push_descriptor.
220 bool IsKhrPushDescriptorSupported() const { 215 bool IsKhrPushDescriptorSupported() const {
221 return khr_push_descriptor; 216 return khr_push_descriptor;
@@ -316,6 +311,17 @@ public:
316 return ext_shader_atomic_int64; 311 return ext_shader_atomic_int64;
317 } 312 }
318 313
314 /// Returns the minimum supported version of SPIR-V.
315 u32 SupportedSpirvVersion() const {
316 if (instance_version >= VK_API_VERSION_1_3) {
317 return 0x00010600U;
318 }
319 if (khr_spirv_1_4) {
320 return 0x00010400U;
321 }
322 return 0x00010000U;
323 }
324
319 /// Returns true when a known debugging tool is attached. 325 /// Returns true when a known debugging tool is attached.
320 bool HasDebuggingToolAttached() const { 326 bool HasDebuggingToolAttached() const {
321 return has_renderdoc || has_nsight_graphics; 327 return has_renderdoc || has_nsight_graphics;
diff --git a/src/video_core/vulkan_common/vulkan_instance.cpp b/src/video_core/vulkan_common/vulkan_instance.cpp
index a082e3059..562039b56 100644
--- a/src/video_core/vulkan_common/vulkan_instance.cpp
+++ b/src/video_core/vulkan_common/vulkan_instance.cpp
@@ -9,18 +9,21 @@
9#include "common/common_types.h" 9#include "common/common_types.h"
10#include "common/dynamic_library.h" 10#include "common/dynamic_library.h"
11#include "common/logging/log.h" 11#include "common/logging/log.h"
12#include "common/polyfill_ranges.h"
12#include "core/frontend/emu_window.h" 13#include "core/frontend/emu_window.h"
13#include "video_core/vulkan_common/vulkan_instance.h" 14#include "video_core/vulkan_common/vulkan_instance.h"
14#include "video_core/vulkan_common/vulkan_wrapper.h" 15#include "video_core/vulkan_common/vulkan_wrapper.h"
15 16
16// Include these late to avoid polluting previous headers 17// Include these late to avoid polluting previous headers
17#ifdef _WIN32 18#if defined(_WIN32)
18#include <windows.h> 19#include <windows.h>
19// ensure include order 20// ensure include order
20#include <vulkan/vulkan_win32.h> 21#include <vulkan/vulkan_win32.h>
21#endif 22#elif defined(__APPLE__)
22 23#include <vulkan/vulkan_macos.h>
23#if !defined(_WIN32) && !defined(__APPLE__) 24#elif defined(__ANDROID__)
25#include <vulkan/vulkan_android.h>
26#else
24#include <X11/Xlib.h> 27#include <X11/Xlib.h>
25#include <vulkan/vulkan_wayland.h> 28#include <vulkan/vulkan_wayland.h>
26#include <vulkan/vulkan_xlib.h> 29#include <vulkan/vulkan_xlib.h>
@@ -39,8 +42,15 @@ namespace {
39 case Core::Frontend::WindowSystemType::Windows: 42 case Core::Frontend::WindowSystemType::Windows:
40 extensions.push_back(VK_KHR_WIN32_SURFACE_EXTENSION_NAME); 43 extensions.push_back(VK_KHR_WIN32_SURFACE_EXTENSION_NAME);
41 break; 44 break;
42#endif 45#elif defined(__APPLE__)
43#if !defined(_WIN32) && !defined(__APPLE__) 46 case Core::Frontend::WindowSystemType::Cocoa:
47 extensions.push_back(VK_MVK_MACOS_SURFACE_EXTENSION_NAME);
48 break;
49#elif defined(__ANDROID__)
50 case Core::Frontend::WindowSystemType::Android:
51 extensions.push_back(VK_KHR_ANDROID_SURFACE_EXTENSION_NAME);
52 break;
53#else
44 case Core::Frontend::WindowSystemType::X11: 54 case Core::Frontend::WindowSystemType::X11:
45 extensions.push_back(VK_KHR_XLIB_SURFACE_EXTENSION_NAME); 55 extensions.push_back(VK_KHR_XLIB_SURFACE_EXTENSION_NAME);
46 break; 56 break;
@@ -59,6 +69,10 @@ namespace {
59 extensions.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME); 69 extensions.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME);
60 } 70 }
61 extensions.push_back(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME); 71 extensions.push_back(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME);
72
73#ifdef __APPLE__
74 extensions.push_back(VK_KHR_PORTABILITY_ENUMERATION_EXTENSION_NAME);
75#endif
62 return extensions; 76 return extensions;
63} 77}
64 78
@@ -140,7 +154,7 @@ vk::Instance CreateInstance(const Common::DynamicLibrary& library, vk::InstanceD
140 } 154 }
141 vk::Instance instance = 155 vk::Instance instance =
142 std::async([&] { 156 std::async([&] {
143 return vk::Instance::Create(required_version, layers, extensions, dld); 157 return vk::Instance::Create(available_version, layers, extensions, dld);
144 }).get(); 158 }).get();
145 if (!vk::Load(*instance, dld)) { 159 if (!vk::Load(*instance, dld)) {
146 LOG_ERROR(Render_Vulkan, "Failed to load Vulkan instance function pointers"); 160 LOG_ERROR(Render_Vulkan, "Failed to load Vulkan instance function pointers");
diff --git a/src/video_core/vulkan_common/vulkan_memory_allocator.cpp b/src/video_core/vulkan_common/vulkan_memory_allocator.cpp
index 6442898bd..1732866e0 100644
--- a/src/video_core/vulkan_common/vulkan_memory_allocator.cpp
+++ b/src/video_core/vulkan_common/vulkan_memory_allocator.cpp
@@ -12,6 +12,7 @@
12#include "common/assert.h" 12#include "common/assert.h"
13#include "common/common_types.h" 13#include "common/common_types.h"
14#include "common/logging/log.h" 14#include "common/logging/log.h"
15#include "common/polyfill_ranges.h"
15#include "video_core/vulkan_common/vulkan_device.h" 16#include "video_core/vulkan_common/vulkan_device.h"
16#include "video_core/vulkan_common/vulkan_memory_allocator.h" 17#include "video_core/vulkan_common/vulkan_memory_allocator.h"
17#include "video_core/vulkan_common/vulkan_wrapper.h" 18#include "video_core/vulkan_common/vulkan_wrapper.h"
diff --git a/src/video_core/vulkan_common/vulkan_surface.cpp b/src/video_core/vulkan_common/vulkan_surface.cpp
index 69f9c494b..fa9bafa20 100644
--- a/src/video_core/vulkan_common/vulkan_surface.cpp
+++ b/src/video_core/vulkan_common/vulkan_surface.cpp
@@ -11,9 +11,11 @@
11#include <windows.h> 11#include <windows.h>
12// ensure include order 12// ensure include order
13#include <vulkan/vulkan_win32.h> 13#include <vulkan/vulkan_win32.h>
14#endif 14#elif defined(__APPLE__)
15 15#include <vulkan/vulkan_macos.h>
16#if !defined(_WIN32) && !defined(__APPLE__) 16#elif defined(__ANDROID__)
17#include <vulkan/vulkan_android.h>
18#else
17#include <X11/Xlib.h> 19#include <X11/Xlib.h>
18#include <vulkan/vulkan_wayland.h> 20#include <vulkan/vulkan_wayland.h>
19#include <vulkan/vulkan_xlib.h> 21#include <vulkan/vulkan_xlib.h>
@@ -40,8 +42,33 @@ vk::SurfaceKHR CreateSurface(const vk::Instance& instance,
40 throw vk::Exception(VK_ERROR_INITIALIZATION_FAILED); 42 throw vk::Exception(VK_ERROR_INITIALIZATION_FAILED);
41 } 43 }
42 } 44 }
43#endif 45#elif defined(__APPLE__)
44#if !defined(_WIN32) && !defined(__APPLE__) 46 if (window_info.type == Core::Frontend::WindowSystemType::Cocoa) {
47 const VkMacOSSurfaceCreateInfoMVK mvk_ci{VK_STRUCTURE_TYPE_MACOS_SURFACE_CREATE_INFO_MVK,
48 nullptr, 0, window_info.render_surface};
49 const auto vkCreateMacOSSurfaceMVK = reinterpret_cast<PFN_vkCreateMacOSSurfaceMVK>(
50 dld.vkGetInstanceProcAddr(*instance, "vkCreateMacOSSurfaceMVK"));
51 if (!vkCreateMacOSSurfaceMVK ||
52 vkCreateMacOSSurfaceMVK(*instance, &mvk_ci, nullptr, &unsafe_surface) != VK_SUCCESS) {
53 LOG_ERROR(Render_Vulkan, "Failed to initialize Metal surface");
54 throw vk::Exception(VK_ERROR_INITIALIZATION_FAILED);
55 }
56 }
57#elif defined(__ANDROID__)
58 if (window_info.type == Core::Frontend::WindowSystemType::Android) {
59 const VkAndroidSurfaceCreateInfoKHR android_ci{
60 VK_STRUCTURE_TYPE_ANDROID_SURFACE_CREATE_INFO_KHR, nullptr, 0,
61 reinterpret_cast<ANativeWindow*>(window_info.render_surface)};
62 const auto vkCreateAndroidSurfaceKHR = reinterpret_cast<PFN_vkCreateAndroidSurfaceKHR>(
63 dld.vkGetInstanceProcAddr(*instance, "vkCreateAndroidSurfaceKHR"));
64 if (!vkCreateAndroidSurfaceKHR ||
65 vkCreateAndroidSurfaceKHR(*instance, &android_ci, nullptr, &unsafe_surface) !=
66 VK_SUCCESS) {
67 LOG_ERROR(Render_Vulkan, "Failed to initialize Android surface");
68 throw vk::Exception(VK_ERROR_INITIALIZATION_FAILED);
69 }
70 }
71#else
45 if (window_info.type == Core::Frontend::WindowSystemType::X11) { 72 if (window_info.type == Core::Frontend::WindowSystemType::X11) {
46 const VkXlibSurfaceCreateInfoKHR xlib_ci{ 73 const VkXlibSurfaceCreateInfoKHR xlib_ci{
47 VK_STRUCTURE_TYPE_XLIB_SURFACE_CREATE_INFO_KHR, nullptr, 0, 74 VK_STRUCTURE_TYPE_XLIB_SURFACE_CREATE_INFO_KHR, nullptr, 0,
@@ -70,6 +97,7 @@ vk::SurfaceKHR CreateSurface(const vk::Instance& instance,
70 } 97 }
71 } 98 }
72#endif 99#endif
100
73 if (!unsafe_surface) { 101 if (!unsafe_surface) {
74 LOG_ERROR(Render_Vulkan, "Presentation not supported on this platform"); 102 LOG_ERROR(Render_Vulkan, "Presentation not supported on this platform");
75 throw vk::Exception(VK_ERROR_INITIALIZATION_FAILED); 103 throw vk::Exception(VK_ERROR_INITIALIZATION_FAILED);
diff --git a/src/yuzu/CMakeLists.txt b/src/yuzu/CMakeLists.txt
index 4de45c9ba..656dd79a9 100644
--- a/src/yuzu/CMakeLists.txt
+++ b/src/yuzu/CMakeLists.txt
@@ -388,7 +388,11 @@ if (YUZU_USE_BUNDLED_QT AND QT_VERSION VERSION_LESS 6)
388endif() 388endif()
389 389
390if (ENABLE_SDL2) 390if (ENABLE_SDL2)
391 target_link_libraries(yuzu PRIVATE SDL2) 391 if (YUZU_USE_EXTERNAL_SDL2)
392 target_link_libraries(yuzu PRIVATE SDL2-static)
393 else()
394 target_link_libraries(yuzu PRIVATE SDL2)
395 endif()
392 target_compile_definitions(yuzu PRIVATE HAVE_SDL2) 396 target_compile_definitions(yuzu PRIVATE HAVE_SDL2)
393endif() 397endif()
394 398
diff --git a/src/yuzu/bootmanager.cpp b/src/yuzu/bootmanager.cpp
index c934069dd..a6658a26f 100644
--- a/src/yuzu/bootmanager.cpp
+++ b/src/yuzu/bootmanager.cpp
@@ -118,7 +118,7 @@ void EmuThread::run() {
118 } 118 }
119 } else { 119 } else {
120 std::unique_lock lock{running_mutex}; 120 std::unique_lock lock{running_mutex};
121 running_cv.wait(lock, stop_token, [this] { return IsRunning(); }); 121 Common::CondvarWait(running_cv, lock, stop_token, [&] { return IsRunning(); });
122 } 122 }
123 } 123 }
124 124
@@ -267,6 +267,10 @@ static Core::Frontend::WindowSystemType GetWindowSystemType() {
267 return Core::Frontend::WindowSystemType::X11; 267 return Core::Frontend::WindowSystemType::X11;
268 else if (platform_name == QStringLiteral("wayland")) 268 else if (platform_name == QStringLiteral("wayland"))
269 return Core::Frontend::WindowSystemType::Wayland; 269 return Core::Frontend::WindowSystemType::Wayland;
270 else if (platform_name == QStringLiteral("cocoa"))
271 return Core::Frontend::WindowSystemType::Cocoa;
272 else if (platform_name == QStringLiteral("android"))
273 return Core::Frontend::WindowSystemType::Android;
270 274
271 LOG_CRITICAL(Frontend, "Unknown Qt platform!"); 275 LOG_CRITICAL(Frontend, "Unknown Qt platform!");
272 return Core::Frontend::WindowSystemType::Windows; 276 return Core::Frontend::WindowSystemType::Windows;
diff --git a/src/yuzu/bootmanager.h b/src/yuzu/bootmanager.h
index 4a01481cd..ca4aee088 100644
--- a/src/yuzu/bootmanager.h
+++ b/src/yuzu/bootmanager.h
@@ -14,6 +14,7 @@
14#include <QTouchEvent> 14#include <QTouchEvent>
15#include <QWidget> 15#include <QWidget>
16 16
17#include "common/polyfill_thread.h"
17#include "common/thread.h" 18#include "common/thread.h"
18#include "core/frontend/emu_window.h" 19#include "core/frontend/emu_window.h"
19 20
diff --git a/src/yuzu/configuration/configure_graphics.cpp b/src/yuzu/configuration/configure_graphics.cpp
index f1385e972..20bc651f1 100644
--- a/src/yuzu/configuration/configure_graphics.cpp
+++ b/src/yuzu/configuration/configure_graphics.cpp
@@ -360,7 +360,7 @@ void ConfigureGraphics::RetrieveVulkanDevices() try {
360 360
361 vk::InstanceDispatch dld; 361 vk::InstanceDispatch dld;
362 const Common::DynamicLibrary library = OpenLibrary(); 362 const Common::DynamicLibrary library = OpenLibrary();
363 const vk::Instance instance = CreateInstance(library, dld, VK_API_VERSION_1_0); 363 const vk::Instance instance = CreateInstance(library, dld, VK_API_VERSION_1_1);
364 const std::vector<VkPhysicalDevice> physical_devices = instance.EnumeratePhysicalDevices(); 364 const std::vector<VkPhysicalDevice> physical_devices = instance.EnumeratePhysicalDevices();
365 365
366 vulkan_devices.clear(); 366 vulkan_devices.clear();
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp
index c21153560..4f693b339 100644
--- a/src/yuzu/main.cpp
+++ b/src/yuzu/main.cpp
@@ -2850,6 +2850,7 @@ void GMainWindow::ErrorDisplayDisplayError(QString error_code, QString error_tex
2850} 2850}
2851 2851
2852void GMainWindow::OnMenuReportCompatibility() { 2852void GMainWindow::OnMenuReportCompatibility() {
2853#if defined(ARCHITECTURE_x86_64) && !defined(__APPLE__)
2853 const auto& caps = Common::GetCPUCaps(); 2854 const auto& caps = Common::GetCPUCaps();
2854 const bool has_fma = caps.fma || caps.fma4; 2855 const bool has_fma = caps.fma || caps.fma4;
2855 const auto processor_count = std::thread::hardware_concurrency(); 2856 const auto processor_count = std::thread::hardware_concurrency();
@@ -2876,6 +2877,11 @@ void GMainWindow::OnMenuReportCompatibility() {
2876 "&gt; " 2877 "&gt; "
2877 "Web.")); 2878 "Web."));
2878 } 2879 }
2880#else
2881 QMessageBox::critical(this, tr("Hardware requirements not met"),
2882 tr("Your system does not meet the recommended hardware requirements. "
2883 "Compatibility reporting has been disabled."));
2884#endif
2879} 2885}
2880 2886
2881void GMainWindow::OpenURL(const QUrl& url) { 2887void GMainWindow::OpenURL(const QUrl& url) {
diff --git a/src/yuzu/multiplayer/chat_room.h b/src/yuzu/multiplayer/chat_room.h
index 01c70fad0..dd71ea4cd 100644
--- a/src/yuzu/multiplayer/chat_room.h
+++ b/src/yuzu/multiplayer/chat_room.h
@@ -4,6 +4,7 @@
4#pragma once 4#pragma once
5 5
6#include <memory> 6#include <memory>
7#include <unordered_map>
7#include <unordered_set> 8#include <unordered_set>
8#include <QDialog> 9#include <QDialog>
9#include <QSortFilterProxyModel> 10#include <QSortFilterProxyModel>
diff --git a/src/yuzu/startup_checks.cpp b/src/yuzu/startup_checks.cpp
index ccdcf10fa..563818362 100644
--- a/src/yuzu/startup_checks.cpp
+++ b/src/yuzu/startup_checks.cpp
@@ -27,7 +27,7 @@ void CheckVulkan() {
27 Vulkan::vk::InstanceDispatch dld; 27 Vulkan::vk::InstanceDispatch dld;
28 const Common::DynamicLibrary library = Vulkan::OpenLibrary(); 28 const Common::DynamicLibrary library = Vulkan::OpenLibrary();
29 const Vulkan::vk::Instance instance = 29 const Vulkan::vk::Instance instance =
30 Vulkan::CreateInstance(library, dld, VK_API_VERSION_1_0); 30 Vulkan::CreateInstance(library, dld, VK_API_VERSION_1_1);
31 31
32 } catch (const Vulkan::vk::Exception& exception) { 32 } catch (const Vulkan::vk::Exception& exception) {
33 fmt::print(stderr, "Failed to initialize Vulkan: {}\n", exception.what()); 33 fmt::print(stderr, "Failed to initialize Vulkan: {}\n", exception.what());
diff --git a/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.cpp b/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.cpp
index 25948328c..0d580fe4f 100644
--- a/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.cpp
+++ b/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.cpp
@@ -51,11 +51,6 @@ EmuWindow_SDL2_VK::EmuWindow_SDL2_VK(InputCommon::InputSubsystem* input_subsyste
51 window_info.type = Core::Frontend::WindowSystemType::Windows; 51 window_info.type = Core::Frontend::WindowSystemType::Windows;
52 window_info.render_surface = reinterpret_cast<void*>(wm.info.win.window); 52 window_info.render_surface = reinterpret_cast<void*>(wm.info.win.window);
53 break; 53 break;
54#else
55 case SDL_SYSWM_TYPE::SDL_SYSWM_WINDOWS:
56 LOG_CRITICAL(Frontend, "Window manager subsystem Windows not compiled");
57 std::exit(EXIT_FAILURE);
58 break;
59#endif 54#endif
60#ifdef SDL_VIDEO_DRIVER_X11 55#ifdef SDL_VIDEO_DRIVER_X11
61 case SDL_SYSWM_TYPE::SDL_SYSWM_X11: 56 case SDL_SYSWM_TYPE::SDL_SYSWM_X11:
@@ -63,11 +58,6 @@ EmuWindow_SDL2_VK::EmuWindow_SDL2_VK(InputCommon::InputSubsystem* input_subsyste
63 window_info.display_connection = wm.info.x11.display; 58 window_info.display_connection = wm.info.x11.display;
64 window_info.render_surface = reinterpret_cast<void*>(wm.info.x11.window); 59 window_info.render_surface = reinterpret_cast<void*>(wm.info.x11.window);
65 break; 60 break;
66#else
67 case SDL_SYSWM_TYPE::SDL_SYSWM_X11:
68 LOG_CRITICAL(Frontend, "Window manager subsystem X11 not compiled");
69 std::exit(EXIT_FAILURE);
70 break;
71#endif 61#endif
72#ifdef SDL_VIDEO_DRIVER_WAYLAND 62#ifdef SDL_VIDEO_DRIVER_WAYLAND
73 case SDL_SYSWM_TYPE::SDL_SYSWM_WAYLAND: 63 case SDL_SYSWM_TYPE::SDL_SYSWM_WAYLAND:
@@ -75,14 +65,21 @@ EmuWindow_SDL2_VK::EmuWindow_SDL2_VK(InputCommon::InputSubsystem* input_subsyste
75 window_info.display_connection = wm.info.wl.display; 65 window_info.display_connection = wm.info.wl.display;
76 window_info.render_surface = wm.info.wl.surface; 66 window_info.render_surface = wm.info.wl.surface;
77 break; 67 break;
78#else 68#endif
79 case SDL_SYSWM_TYPE::SDL_SYSWM_WAYLAND: 69#ifdef SDL_VIDEO_DRIVER_COCOA
80 LOG_CRITICAL(Frontend, "Window manager subsystem Wayland not compiled"); 70 case SDL_SYSWM_TYPE::SDL_SYSWM_COCOA:
81 std::exit(EXIT_FAILURE); 71 window_info.type = Core::Frontend::WindowSystemType::Cocoa;
72 window_info.render_surface = SDL_Metal_CreateView(render_window);
73 break;
74#endif
75#ifdef SDL_VIDEO_DRIVER_ANDROID
76 case SDL_SYSWM_TYPE::SDL_SYSWM_ANDROID:
77 window_info.type = Core::Frontend::WindowSystemType::Android;
78 window_info.render_surface = reinterpret_cast<void*>(wm.info.android.window);
82 break; 79 break;
83#endif 80#endif
84 default: 81 default:
85 LOG_CRITICAL(Frontend, "Window manager subsystem not implemented"); 82 LOG_CRITICAL(Frontend, "Window manager subsystem {} not implemented", wm.subsystem);
86 std::exit(EXIT_FAILURE); 83 std::exit(EXIT_FAILURE);
87 break; 84 break;
88 } 85 }