summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CMakeLists.txt2
m---------externals/dynarmic0
-rw-r--r--src/audio_core/CMakeLists.txt3
-rw-r--r--src/audio_core/audio_renderer.cpp32
-rw-r--r--src/audio_core/audio_renderer.h5
-rw-r--r--src/audio_core/behavior_info.cpp100
-rw-r--r--src/audio_core/behavior_info.h66
-rw-r--r--src/audio_core/common.h47
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic_64.cpp10
-rw-r--r--src/core/arm/unicorn/arm_unicorn.cpp11
-rw-r--r--src/core/crypto/key_manager.cpp3
-rw-r--r--src/core/file_sys/program_metadata.cpp11
-rw-r--r--src/core/file_sys/program_metadata.h6
-rw-r--r--src/core/hle/kernel/hle_ipc.cpp32
-rw-r--r--src/core/hle/kernel/hle_ipc.h12
-rw-r--r--src/core/hle/kernel/memory/slab_heap.h4
-rw-r--r--src/core/hle/service/am/am.cpp4
-rw-r--r--src/core/hle/service/audio/audren_u.cpp13
-rw-r--r--src/core/hle/service/es/es.cpp1
-rw-r--r--src/core/hle/service/hid/controllers/npad.cpp4
-rw-r--r--src/core/hle/service/time/standard_network_system_clock_core.h2
-rw-r--r--src/core/hle/service/time/steady_clock_core.h1
-rw-r--r--src/core/hle/service/time/system_clock_context_update_callback.h2
-rw-r--r--src/core/hle/service/time/system_clock_core.cpp2
-rw-r--r--src/core/hle/service/time/system_clock_core.h2
-rw-r--r--src/core/hle/service/time/time_zone_manager.cpp4
-rw-r--r--src/core/loader/elf.cpp5
-rw-r--r--src/core/loader/nro.cpp23
-rw-r--r--src/core/loader/nro.h2
-rw-r--r--src/core/loader/nso.cpp2
-rw-r--r--src/core/settings.h1
-rw-r--r--src/video_core/dma_pusher.cpp4
-rw-r--r--src/video_core/dma_pusher.h11
-rw-r--r--src/video_core/engines/maxwell_3d.cpp4
-rw-r--r--src/video_core/engines/maxwell_3d.h2
-rw-r--r--src/video_core/gpu.cpp2
-rw-r--r--src/video_core/renderer_opengl/gl_shader_decompiler.cpp1
-rw-r--r--src/video_core/renderer_vulkan/fixed_pipeline_state.cpp515
-rw-r--r--src/video_core/renderer_vulkan/fixed_pipeline_state.h374
-rw-r--r--src/video_core/renderer_vulkan/renderer_vulkan.cpp6
-rw-r--r--src/video_core/renderer_vulkan/shaders/quad_indexed.comp50
-rw-r--r--src/video_core/renderer_vulkan/vk_compute_pass.cpp205
-rw-r--r--src/video_core/renderer_vulkan/vk_compute_pass.h19
-rw-r--r--src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp91
-rw-r--r--src/video_core/renderer_vulkan/vk_pipeline_cache.cpp8
-rw-r--r--src/video_core/renderer_vulkan/vk_rasterizer.cpp37
-rw-r--r--src/video_core/renderer_vulkan/vk_rasterizer.h1
-rw-r--r--src/video_core/renderer_vulkan/vk_stream_buffer.cpp76
-rw-r--r--src/video_core/renderer_vulkan/vk_stream_buffer.h5
-rw-r--r--src/video_core/shader/decode/memory.cpp2
-rw-r--r--src/video_core/shader/decode/texture.cpp23
-rw-r--r--src/video_core/shader/track.cpp11
-rw-r--r--src/video_core/texture_cache/format_lookup_table.cpp6
-rw-r--r--src/yuzu/configuration/config.cpp3
-rw-r--r--src/yuzu/configuration/configure_debug.cpp2
-rw-r--r--src/yuzu/configuration/configure_debug.ui7
-rw-r--r--src/yuzu/main.cpp62
-rw-r--r--src/yuzu/main.h2
-rw-r--r--src/yuzu/main.ui18
-rw-r--r--src/yuzu_cmd/config.cpp2
-rw-r--r--src/yuzu_cmd/default_ini.h3
61 files changed, 1281 insertions, 683 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 467d769a2..c906c5a50 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -164,7 +164,7 @@ if (ENABLE_SDL2)
164 set(SDL2_LIBRARIES "SDL2::SDL2") 164 set(SDL2_LIBRARIES "SDL2::SDL2")
165 endif() 165 endif()
166 166
167 include_directories(${SDL2_INCLUDE_DIRS}) 167 include_directories(SYSTEM ${SDL2_INCLUDE_DIRS})
168 add_library(SDL2 INTERFACE) 168 add_library(SDL2 INTERFACE)
169 target_link_libraries(SDL2 INTERFACE "${SDL2_LIBRARIES}") 169 target_link_libraries(SDL2 INTERFACE "${SDL2_LIBRARIES}")
170 endif() 170 endif()
diff --git a/externals/dynarmic b/externals/dynarmic
Subproject 57b987c185ae6677861cbf781f08ed1649b0543 Subproject a3cd05577c9b6c51f0f345d0e915b6feab68fe1
diff --git a/src/audio_core/CMakeLists.txt b/src/audio_core/CMakeLists.txt
index c381dbe1d..5ef38a337 100644
--- a/src/audio_core/CMakeLists.txt
+++ b/src/audio_core/CMakeLists.txt
@@ -7,9 +7,12 @@ add_library(audio_core STATIC
7 audio_out.h 7 audio_out.h
8 audio_renderer.cpp 8 audio_renderer.cpp
9 audio_renderer.h 9 audio_renderer.h
10 behavior_info.cpp
11 behavior_info.h
10 buffer.h 12 buffer.h
11 codec.cpp 13 codec.cpp
12 codec.h 14 codec.h
15 common.h
13 null_sink.h 16 null_sink.h
14 sink.h 17 sink.h
15 sink_details.cpp 18 sink_details.cpp
diff --git a/src/audio_core/audio_renderer.cpp b/src/audio_core/audio_renderer.cpp
index 7a9dc61d4..d18ef6940 100644
--- a/src/audio_core/audio_renderer.cpp
+++ b/src/audio_core/audio_renderer.cpp
@@ -6,6 +6,7 @@
6#include "audio_core/audio_out.h" 6#include "audio_core/audio_out.h"
7#include "audio_core/audio_renderer.h" 7#include "audio_core/audio_renderer.h"
8#include "audio_core/codec.h" 8#include "audio_core/codec.h"
9#include "audio_core/common.h"
9#include "common/assert.h" 10#include "common/assert.h"
10#include "common/logging/log.h" 11#include "common/logging/log.h"
11#include "core/core.h" 12#include "core/core.h"
@@ -79,7 +80,7 @@ AudioRenderer::AudioRenderer(Core::Timing::CoreTiming& core_timing, Core::Memory
79 std::size_t instance_number) 80 std::size_t instance_number)
80 : worker_params{params}, buffer_event{buffer_event}, voices(params.voice_count), 81 : worker_params{params}, buffer_event{buffer_event}, voices(params.voice_count),
81 effects(params.effect_count), memory{memory_} { 82 effects(params.effect_count), memory{memory_} {
82 83 behavior_info.SetUserRevision(params.revision);
83 audio_out = std::make_unique<AudioCore::AudioOut>(); 84 audio_out = std::make_unique<AudioCore::AudioOut>();
84 stream = audio_out->OpenStream(core_timing, STREAM_SAMPLE_RATE, STREAM_NUM_CHANNELS, 85 stream = audio_out->OpenStream(core_timing, STREAM_SAMPLE_RATE, STREAM_NUM_CHANNELS,
85 fmt::format("AudioRenderer-Instance{}", instance_number), 86 fmt::format("AudioRenderer-Instance{}", instance_number),
@@ -109,17 +110,17 @@ Stream::State AudioRenderer::GetStreamState() const {
109 return stream->GetState(); 110 return stream->GetState();
110} 111}
111 112
112static constexpr u32 VersionFromRevision(u32_le rev) { 113ResultVal<std::vector<u8>> AudioRenderer::UpdateAudioRenderer(const std::vector<u8>& input_params) {
113 // "REV7" -> 7
114 return ((rev >> 24) & 0xff) - 0x30;
115}
116
117std::vector<u8> AudioRenderer::UpdateAudioRenderer(const std::vector<u8>& input_params) {
118 // Copy UpdateDataHeader struct 114 // Copy UpdateDataHeader struct
119 UpdateDataHeader config{}; 115 UpdateDataHeader config{};
120 std::memcpy(&config, input_params.data(), sizeof(UpdateDataHeader)); 116 std::memcpy(&config, input_params.data(), sizeof(UpdateDataHeader));
121 u32 memory_pool_count = worker_params.effect_count + (worker_params.voice_count * 4); 117 u32 memory_pool_count = worker_params.effect_count + (worker_params.voice_count * 4);
122 118
119 if (!behavior_info.UpdateInput(input_params, sizeof(UpdateDataHeader))) {
120 LOG_ERROR(Audio, "Failed to update behavior info input parameters");
121 return Audren::ERR_INVALID_PARAMETERS;
122 }
123
123 // Copy MemoryPoolInfo structs 124 // Copy MemoryPoolInfo structs
124 std::vector<MemoryPoolInfo> mem_pool_info(memory_pool_count); 125 std::vector<MemoryPoolInfo> mem_pool_info(memory_pool_count);
125 std::memcpy(mem_pool_info.data(), 126 std::memcpy(mem_pool_info.data(),
@@ -173,8 +174,7 @@ std::vector<u8> AudioRenderer::UpdateAudioRenderer(const std::vector<u8>& input_
173 // Copy output header 174 // Copy output header
174 UpdateDataHeader response_data{worker_params}; 175 UpdateDataHeader response_data{worker_params};
175 std::vector<u8> output_params(response_data.total_size); 176 std::vector<u8> output_params(response_data.total_size);
176 const auto audren_revision = VersionFromRevision(config.revision); 177 if (behavior_info.IsElapsedFrameCountSupported()) {
177 if (audren_revision >= 5) {
178 response_data.frame_count = 0x10; 178 response_data.frame_count = 0x10;
179 response_data.total_size += 0x10; 179 response_data.total_size += 0x10;
180 } 180 }
@@ -200,7 +200,19 @@ std::vector<u8> AudioRenderer::UpdateAudioRenderer(const std::vector<u8>& input_
200 sizeof(EffectOutStatus)); 200 sizeof(EffectOutStatus));
201 effect_out_status_offset += sizeof(EffectOutStatus); 201 effect_out_status_offset += sizeof(EffectOutStatus);
202 } 202 }
203 return output_params; 203
204 // Update behavior info output
205 const std::size_t behavior_out_status_offset{
206 sizeof(UpdateDataHeader) + response_data.memory_pools_size + response_data.voices_size +
207 response_data.effects_size + response_data.sinks_size +
208 response_data.performance_manager_size};
209
210 if (!behavior_info.UpdateOutput(output_params, behavior_out_status_offset)) {
211 LOG_ERROR(Audio, "Failed to update behavior info output parameters");
212 return Audren::ERR_INVALID_PARAMETERS;
213 }
214
215 return MakeResult(output_params);
204} 216}
205 217
206void AudioRenderer::VoiceState::SetWaveIndex(std::size_t index) { 218void AudioRenderer::VoiceState::SetWaveIndex(std::size_t index) {
diff --git a/src/audio_core/audio_renderer.h b/src/audio_core/audio_renderer.h
index 62faf9f19..b42770fae 100644
--- a/src/audio_core/audio_renderer.h
+++ b/src/audio_core/audio_renderer.h
@@ -8,11 +8,13 @@
8#include <memory> 8#include <memory>
9#include <vector> 9#include <vector>
10 10
11#include "audio_core/behavior_info.h"
11#include "audio_core/stream.h" 12#include "audio_core/stream.h"
12#include "common/common_funcs.h" 13#include "common/common_funcs.h"
13#include "common/common_types.h" 14#include "common/common_types.h"
14#include "common/swap.h" 15#include "common/swap.h"
15#include "core/hle/kernel/object.h" 16#include "core/hle/kernel/object.h"
17#include "core/hle/result.h"
16 18
17namespace Core::Timing { 19namespace Core::Timing {
18class CoreTiming; 20class CoreTiming;
@@ -226,7 +228,7 @@ public:
226 std::shared_ptr<Kernel::WritableEvent> buffer_event, std::size_t instance_number); 228 std::shared_ptr<Kernel::WritableEvent> buffer_event, std::size_t instance_number);
227 ~AudioRenderer(); 229 ~AudioRenderer();
228 230
229 std::vector<u8> UpdateAudioRenderer(const std::vector<u8>& input_params); 231 ResultVal<std::vector<u8>> UpdateAudioRenderer(const std::vector<u8>& input_params);
230 void QueueMixedBuffer(Buffer::Tag tag); 232 void QueueMixedBuffer(Buffer::Tag tag);
231 void ReleaseAndQueueBuffers(); 233 void ReleaseAndQueueBuffers();
232 u32 GetSampleRate() const; 234 u32 GetSampleRate() const;
@@ -237,6 +239,7 @@ public:
237private: 239private:
238 class EffectState; 240 class EffectState;
239 class VoiceState; 241 class VoiceState;
242 BehaviorInfo behavior_info{};
240 243
241 AudioRendererParameter worker_params; 244 AudioRendererParameter worker_params;
242 std::shared_ptr<Kernel::WritableEvent> buffer_event; 245 std::shared_ptr<Kernel::WritableEvent> buffer_event;
diff --git a/src/audio_core/behavior_info.cpp b/src/audio_core/behavior_info.cpp
new file mode 100644
index 000000000..94b7a3bf1
--- /dev/null
+++ b/src/audio_core/behavior_info.cpp
@@ -0,0 +1,100 @@
1// Copyright 2020 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <cstring>
6#include "audio_core/behavior_info.h"
7#include "audio_core/common.h"
8#include "common/logging/log.h"
9
10namespace AudioCore {
11
12BehaviorInfo::BehaviorInfo() : process_revision(CURRENT_PROCESS_REVISION) {}
13BehaviorInfo::~BehaviorInfo() = default;
14
15bool BehaviorInfo::UpdateInput(const std::vector<u8>& buffer, std::size_t offset) {
16 if (!CanConsumeBuffer(buffer.size(), offset, sizeof(InParams))) {
17 LOG_ERROR(Audio, "Buffer is an invalid size!");
18 return false;
19 }
20 InParams params{};
21 std::memcpy(&params, buffer.data() + offset, sizeof(InParams));
22
23 if (!IsValidRevision(params.revision)) {
24 LOG_ERROR(Audio, "Invalid input revision, revision=0x{:08X}", params.revision);
25 return false;
26 }
27
28 if (user_revision != params.revision) {
29 LOG_ERROR(Audio,
30 "User revision differs from input revision, expecting 0x{:08X} but got 0x{:08X}",
31 user_revision, params.revision);
32 return false;
33 }
34
35 ClearError();
36 UpdateFlags(params.flags);
37
38 // TODO(ogniK): Check input params size when InfoUpdater is used
39
40 return true;
41}
42
43bool BehaviorInfo::UpdateOutput(std::vector<u8>& buffer, std::size_t offset) {
44 if (!CanConsumeBuffer(buffer.size(), offset, sizeof(OutParams))) {
45 LOG_ERROR(Audio, "Buffer is an invalid size!");
46 return false;
47 }
48
49 OutParams params{};
50 std::memcpy(params.errors.data(), errors.data(), sizeof(ErrorInfo) * errors.size());
51 params.error_count = static_cast<u32_le>(error_count);
52 std::memcpy(buffer.data() + offset, &params, sizeof(OutParams));
53 return true;
54}
55
56void BehaviorInfo::ClearError() {
57 error_count = 0;
58}
59
60void BehaviorInfo::UpdateFlags(u64_le dest_flags) {
61 flags = dest_flags;
62}
63
64void BehaviorInfo::SetUserRevision(u32_le revision) {
65 user_revision = revision;
66}
67
68bool BehaviorInfo::IsAdpcmLoopContextBugFixed() const {
69 return IsRevisionSupported(2, user_revision);
70}
71
72bool BehaviorInfo::IsSplitterSupported() const {
73 return IsRevisionSupported(2, user_revision);
74}
75
76bool BehaviorInfo::IsLongSizePreDelaySupported() const {
77 return IsRevisionSupported(3, user_revision);
78}
79
80bool BehaviorInfo::IsAudioRenererProcessingTimeLimit80PercentSupported() const {
81 return IsRevisionSupported(5, user_revision);
82}
83
84bool BehaviorInfo::IsAudioRenererProcessingTimeLimit75PercentSupported() const {
85 return IsRevisionSupported(4, user_revision);
86}
87
88bool BehaviorInfo::IsAudioRenererProcessingTimeLimit70PercentSupported() const {
89 return IsRevisionSupported(1, user_revision);
90}
91
92bool BehaviorInfo::IsElapsedFrameCountSupported() const {
93 return IsRevisionSupported(5, user_revision);
94}
95
96bool BehaviorInfo::IsMemoryPoolForceMappingEnabled() const {
97 return (flags & 1) != 0;
98}
99
100} // namespace AudioCore
diff --git a/src/audio_core/behavior_info.h b/src/audio_core/behavior_info.h
new file mode 100644
index 000000000..c5e91ab39
--- /dev/null
+++ b/src/audio_core/behavior_info.h
@@ -0,0 +1,66 @@
1// Copyright 2020 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <array>
8
9#include <vector>
10#include "common/common_funcs.h"
11#include "common/common_types.h"
12#include "common/swap.h"
13
14namespace AudioCore {
15class BehaviorInfo {
16public:
17 explicit BehaviorInfo();
18 ~BehaviorInfo();
19
20 bool UpdateInput(const std::vector<u8>& buffer, std::size_t offset);
21 bool UpdateOutput(std::vector<u8>& buffer, std::size_t offset);
22
23 void ClearError();
24 void UpdateFlags(u64_le dest_flags);
25 void SetUserRevision(u32_le revision);
26
27 bool IsAdpcmLoopContextBugFixed() const;
28 bool IsSplitterSupported() const;
29 bool IsLongSizePreDelaySupported() const;
30 bool IsAudioRenererProcessingTimeLimit80PercentSupported() const;
31 bool IsAudioRenererProcessingTimeLimit75PercentSupported() const;
32 bool IsAudioRenererProcessingTimeLimit70PercentSupported() const;
33 bool IsElapsedFrameCountSupported() const;
34 bool IsMemoryPoolForceMappingEnabled() const;
35
36private:
37 u32_le process_revision{};
38 u32_le user_revision{};
39 u64_le flags{};
40
41 struct ErrorInfo {
42 u32_le result{};
43 INSERT_PADDING_WORDS(1);
44 u64_le result_info{};
45 };
46 static_assert(sizeof(ErrorInfo) == 0x10, "ErrorInfo is an invalid size");
47
48 std::array<ErrorInfo, 10> errors{};
49 std::size_t error_count{};
50
51 struct InParams {
52 u32_le revision{};
53 u32_le padding{};
54 u64_le flags{};
55 };
56 static_assert(sizeof(InParams) == 0x10, "InParams is an invalid size");
57
58 struct OutParams {
59 std::array<ErrorInfo, 10> errors{};
60 u32_le error_count{};
61 INSERT_PADDING_BYTES(12);
62 };
63 static_assert(sizeof(OutParams) == 0xb0, "OutParams is an invalid size");
64};
65
66} // namespace AudioCore
diff --git a/src/audio_core/common.h b/src/audio_core/common.h
new file mode 100644
index 000000000..98478b66b
--- /dev/null
+++ b/src/audio_core/common.h
@@ -0,0 +1,47 @@
1// Copyright 2020 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6#include "common/common_funcs.h"
7#include "common/common_types.h"
8#include "common/swap.h"
9#include "core/hle/result.h"
10
11namespace AudioCore {
12namespace Audren {
13constexpr ResultCode ERR_INVALID_PARAMETERS{ErrorModule::Audio, 41};
14}
15
16constexpr u32_le CURRENT_PROCESS_REVISION = Common::MakeMagic('R', 'E', 'V', '8');
17
18static constexpr u32 VersionFromRevision(u32_le rev) {
19 // "REV7" -> 7
20 return ((rev >> 24) & 0xff) - 0x30;
21}
22
23static constexpr bool IsRevisionSupported(u32 required, u32_le user_revision) {
24 const auto base = VersionFromRevision(user_revision);
25 return required <= base;
26}
27
28static constexpr bool IsValidRevision(u32_le revision) {
29 const auto base = VersionFromRevision(revision);
30 constexpr auto max_rev = VersionFromRevision(CURRENT_PROCESS_REVISION);
31 return base <= max_rev;
32}
33
34static constexpr bool CanConsumeBuffer(std::size_t size, std::size_t offset, std::size_t required) {
35 if (offset > size) {
36 return false;
37 }
38 if (size < required) {
39 return false;
40 }
41 if ((size - offset) < required) {
42 return false;
43 }
44 return true;
45}
46
47} // namespace AudioCore
diff --git a/src/core/arm/dynarmic/arm_dynarmic_64.cpp b/src/core/arm/dynarmic/arm_dynarmic_64.cpp
index 9add5d363..65cbfe5e6 100644
--- a/src/core/arm/dynarmic/arm_dynarmic_64.cpp
+++ b/src/core/arm/dynarmic/arm_dynarmic_64.cpp
@@ -20,6 +20,7 @@
20#include "core/hle/kernel/scheduler.h" 20#include "core/hle/kernel/scheduler.h"
21#include "core/hle/kernel/svc.h" 21#include "core/hle/kernel/svc.h"
22#include "core/memory.h" 22#include "core/memory.h"
23#include "core/settings.h"
23 24
24namespace Core { 25namespace Core {
25 26
@@ -144,6 +145,8 @@ std::shared_ptr<Dynarmic::A64::Jit> ARM_Dynarmic_64::MakeJit(Common::PageTable&
144 config.page_table_address_space_bits = address_space_bits; 145 config.page_table_address_space_bits = address_space_bits;
145 config.silently_mirror_page_table = false; 146 config.silently_mirror_page_table = false;
146 config.absolute_offset_page_table = true; 147 config.absolute_offset_page_table = true;
148 config.detect_misaligned_access_via_page_table = 16 | 32 | 64 | 128;
149 config.only_detect_misalignment_via_page_table_on_page_boundary = true;
147 150
148 // Multi-process state 151 // Multi-process state
149 config.processor_id = core_index; 152 config.processor_id = core_index;
@@ -159,8 +162,11 @@ std::shared_ptr<Dynarmic::A64::Jit> ARM_Dynarmic_64::MakeJit(Common::PageTable&
159 // Unpredictable instructions 162 // Unpredictable instructions
160 config.define_unpredictable_behaviour = true; 163 config.define_unpredictable_behaviour = true;
161 164
162 config.detect_misaligned_access_via_page_table = 16 | 32 | 64 | 128; 165 // Optimizations
163 config.only_detect_misalignment_via_page_table_on_page_boundary = true; 166 if (Settings::values.disable_cpu_opt) {
167 config.enable_optimizations = false;
168 config.enable_fast_dispatch = false;
169 }
164 170
165 return std::make_shared<Dynarmic::A64::Jit>(config); 171 return std::make_shared<Dynarmic::A64::Jit>(config);
166} 172}
diff --git a/src/core/arm/unicorn/arm_unicorn.cpp b/src/core/arm/unicorn/arm_unicorn.cpp
index d189efb63..b96583123 100644
--- a/src/core/arm/unicorn/arm_unicorn.cpp
+++ b/src/core/arm/unicorn/arm_unicorn.cpp
@@ -11,6 +11,7 @@
11#include "core/core_timing.h" 11#include "core/core_timing.h"
12#include "core/hle/kernel/scheduler.h" 12#include "core/hle/kernel/scheduler.h"
13#include "core/hle/kernel/svc.h" 13#include "core/hle/kernel/svc.h"
14#include "core/memory.h"
14 15
15namespace Core { 16namespace Core {
16 17
@@ -171,7 +172,17 @@ MICROPROFILE_DEFINE(ARM_Jit_Unicorn, "ARM JIT", "Unicorn", MP_RGB(255, 64, 64));
171 172
172void ARM_Unicorn::ExecuteInstructions(std::size_t num_instructions) { 173void ARM_Unicorn::ExecuteInstructions(std::size_t num_instructions) {
173 MICROPROFILE_SCOPE(ARM_Jit_Unicorn); 174 MICROPROFILE_SCOPE(ARM_Jit_Unicorn);
175
176 // Temporarily map the code page for Unicorn
177 u64 map_addr{GetPC() & ~Memory::PAGE_MASK};
178 std::vector<u8> page_buffer(Memory::PAGE_SIZE);
179 system.Memory().ReadBlock(map_addr, page_buffer.data(), page_buffer.size());
180
181 CHECKED(uc_mem_map_ptr(uc, map_addr, page_buffer.size(),
182 UC_PROT_READ | UC_PROT_WRITE | UC_PROT_EXEC, page_buffer.data()));
174 CHECKED(uc_emu_start(uc, GetPC(), 1ULL << 63, 0, num_instructions)); 183 CHECKED(uc_emu_start(uc, GetPC(), 1ULL << 63, 0, num_instructions));
184 CHECKED(uc_mem_unmap(uc, map_addr, page_buffer.size()));
185
175 system.CoreTiming().AddTicks(num_instructions); 186 system.CoreTiming().AddTicks(num_instructions);
176 if (GDBStub::IsServerEnabled()) { 187 if (GDBStub::IsServerEnabled()) {
177 if (last_bkpt_hit && last_bkpt.type == GDBStub::BreakpointType::Execute) { 188 if (last_bkpt_hit && last_bkpt.type == GDBStub::BreakpointType::Execute) {
diff --git a/src/core/crypto/key_manager.cpp b/src/core/crypto/key_manager.cpp
index 87e6a1fd3..8997c7082 100644
--- a/src/core/crypto/key_manager.cpp
+++ b/src/core/crypto/key_manager.cpp
@@ -1202,7 +1202,8 @@ const boost::container::flat_map<std::string, KeyIndex<S128KeyType>> KeyManager:
1202 {S128KeyType::Source, static_cast<u64>(SourceKeyType::KeyAreaKey), 1202 {S128KeyType::Source, static_cast<u64>(SourceKeyType::KeyAreaKey),
1203 static_cast<u64>(KeyAreaKeyType::System)}}, 1203 static_cast<u64>(KeyAreaKeyType::System)}},
1204 {"titlekek_source", {S128KeyType::Source, static_cast<u64>(SourceKeyType::Titlekek), 0}}, 1204 {"titlekek_source", {S128KeyType::Source, static_cast<u64>(SourceKeyType::Titlekek), 0}},
1205 {"keyblob_mac_key_source", {S128KeyType::Source, static_cast<u64>(SourceKeyType::KeyblobMAC)}}, 1205 {"keyblob_mac_key_source",
1206 {S128KeyType::Source, static_cast<u64>(SourceKeyType::KeyblobMAC), 0}},
1206 {"tsec_key", {S128KeyType::TSEC, 0, 0}}, 1207 {"tsec_key", {S128KeyType::TSEC, 0, 0}},
1207 {"secure_boot_key", {S128KeyType::SecureBoot, 0, 0}}, 1208 {"secure_boot_key", {S128KeyType::SecureBoot, 0, 0}},
1208 {"sd_seed", {S128KeyType::SDSeed, 0, 0}}, 1209 {"sd_seed", {S128KeyType::SDSeed, 0, 0}},
diff --git a/src/core/file_sys/program_metadata.cpp b/src/core/file_sys/program_metadata.cpp
index 1d6c30962..43169bf9f 100644
--- a/src/core/file_sys/program_metadata.cpp
+++ b/src/core/file_sys/program_metadata.cpp
@@ -51,6 +51,17 @@ Loader::ResultStatus ProgramMetadata::Load(VirtualFile file) {
51 return Loader::ResultStatus::Success; 51 return Loader::ResultStatus::Success;
52} 52}
53 53
54/*static*/ ProgramMetadata ProgramMetadata::GetDefault() {
55 ProgramMetadata result;
56
57 result.LoadManual(
58 true /*is_64_bit*/, FileSys::ProgramAddressSpaceType::Is39Bit /*address_space*/,
59 0x2c /*main_thread_prio*/, 0 /*main_thread_core*/, 0x00100000 /*main_thread_stack_size*/,
60 {}, 0xFFFFFFFFFFFFFFFF /*filesystem_permissions*/, {} /*capabilities*/);
61
62 return result;
63}
64
54void ProgramMetadata::LoadManual(bool is_64_bit, ProgramAddressSpaceType address_space, 65void ProgramMetadata::LoadManual(bool is_64_bit, ProgramAddressSpaceType address_space,
55 s32 main_thread_prio, u32 main_thread_core, 66 s32 main_thread_prio, u32 main_thread_core,
56 u32 main_thread_stack_size, u64 title_id, 67 u32 main_thread_stack_size, u64 title_id,
diff --git a/src/core/file_sys/program_metadata.h b/src/core/file_sys/program_metadata.h
index f8759a396..35069972b 100644
--- a/src/core/file_sys/program_metadata.h
+++ b/src/core/file_sys/program_metadata.h
@@ -44,9 +44,13 @@ public:
44 ProgramMetadata(); 44 ProgramMetadata();
45 ~ProgramMetadata(); 45 ~ProgramMetadata();
46 46
47 /// Gets a default ProgramMetadata configuration, should only be used for homebrew formats where
48 /// we do not have an NPDM file
49 static ProgramMetadata GetDefault();
50
47 Loader::ResultStatus Load(VirtualFile file); 51 Loader::ResultStatus Load(VirtualFile file);
48 52
49 // Load from parameters instead of NPDM file, used for KIP 53 /// Load from parameters instead of NPDM file, used for KIP
50 void LoadManual(bool is_64_bit, ProgramAddressSpaceType address_space, s32 main_thread_prio, 54 void LoadManual(bool is_64_bit, ProgramAddressSpaceType address_space, s32 main_thread_prio,
51 u32 main_thread_core, u32 main_thread_stack_size, u64 title_id, 55 u32 main_thread_core, u32 main_thread_stack_size, u64 title_id,
52 u64 filesystem_permissions, KernelCapabilityDescriptors capabilities); 56 u64 filesystem_permissions, KernelCapabilityDescriptors capabilities);
diff --git a/src/core/hle/kernel/hle_ipc.cpp b/src/core/hle/kernel/hle_ipc.cpp
index d65dae3ae..91d94025c 100644
--- a/src/core/hle/kernel/hle_ipc.cpp
+++ b/src/core/hle/kernel/hle_ipc.cpp
@@ -282,19 +282,19 @@ ResultCode HLERequestContext::WriteToOutgoingCommandBuffer(Thread& thread) {
282 return RESULT_SUCCESS; 282 return RESULT_SUCCESS;
283} 283}
284 284
285std::vector<u8> HLERequestContext::ReadBuffer(int buffer_index) const { 285std::vector<u8> HLERequestContext::ReadBuffer(std::size_t buffer_index) const {
286 std::vector<u8> buffer; 286 std::vector<u8> buffer;
287 const bool is_buffer_a{BufferDescriptorA().size() > std::size_t(buffer_index) && 287 const bool is_buffer_a{BufferDescriptorA().size() > buffer_index &&
288 BufferDescriptorA()[buffer_index].Size()}; 288 BufferDescriptorA()[buffer_index].Size()};
289 auto& memory = Core::System::GetInstance().Memory(); 289 auto& memory = Core::System::GetInstance().Memory();
290 290
291 if (is_buffer_a) { 291 if (is_buffer_a) {
292 ASSERT_MSG(BufferDescriptorA().size() > std::size_t(buffer_index), 292 ASSERT_MSG(BufferDescriptorA().size() > buffer_index,
293 "BufferDescriptorA invalid buffer_index {}", buffer_index); 293 "BufferDescriptorA invalid buffer_index {}", buffer_index);
294 buffer.resize(BufferDescriptorA()[buffer_index].Size()); 294 buffer.resize(BufferDescriptorA()[buffer_index].Size());
295 memory.ReadBlock(BufferDescriptorA()[buffer_index].Address(), buffer.data(), buffer.size()); 295 memory.ReadBlock(BufferDescriptorA()[buffer_index].Address(), buffer.data(), buffer.size());
296 } else { 296 } else {
297 ASSERT_MSG(BufferDescriptorX().size() > std::size_t(buffer_index), 297 ASSERT_MSG(BufferDescriptorX().size() > buffer_index,
298 "BufferDescriptorX invalid buffer_index {}", buffer_index); 298 "BufferDescriptorX invalid buffer_index {}", buffer_index);
299 buffer.resize(BufferDescriptorX()[buffer_index].Size()); 299 buffer.resize(BufferDescriptorX()[buffer_index].Size());
300 memory.ReadBlock(BufferDescriptorX()[buffer_index].Address(), buffer.data(), buffer.size()); 300 memory.ReadBlock(BufferDescriptorX()[buffer_index].Address(), buffer.data(), buffer.size());
@@ -304,13 +304,13 @@ std::vector<u8> HLERequestContext::ReadBuffer(int buffer_index) const {
304} 304}
305 305
306std::size_t HLERequestContext::WriteBuffer(const void* buffer, std::size_t size, 306std::size_t HLERequestContext::WriteBuffer(const void* buffer, std::size_t size,
307 int buffer_index) const { 307 std::size_t buffer_index) const {
308 if (size == 0) { 308 if (size == 0) {
309 LOG_WARNING(Core, "skip empty buffer write"); 309 LOG_WARNING(Core, "skip empty buffer write");
310 return 0; 310 return 0;
311 } 311 }
312 312
313 const bool is_buffer_b{BufferDescriptorB().size() > std::size_t(buffer_index) && 313 const bool is_buffer_b{BufferDescriptorB().size() > buffer_index &&
314 BufferDescriptorB()[buffer_index].Size()}; 314 BufferDescriptorB()[buffer_index].Size()};
315 const std::size_t buffer_size{GetWriteBufferSize(buffer_index)}; 315 const std::size_t buffer_size{GetWriteBufferSize(buffer_index)};
316 if (size > buffer_size) { 316 if (size > buffer_size) {
@@ -321,13 +321,13 @@ std::size_t HLERequestContext::WriteBuffer(const void* buffer, std::size_t size,
321 321
322 auto& memory = Core::System::GetInstance().Memory(); 322 auto& memory = Core::System::GetInstance().Memory();
323 if (is_buffer_b) { 323 if (is_buffer_b) {
324 ASSERT_MSG(BufferDescriptorB().size() > std::size_t(buffer_index), 324 ASSERT_MSG(BufferDescriptorB().size() > buffer_index,
325 "BufferDescriptorB invalid buffer_index {}", buffer_index); 325 "BufferDescriptorB invalid buffer_index {}", buffer_index);
326 ASSERT_MSG(BufferDescriptorB()[buffer_index].Size() >= size, 326 ASSERT_MSG(BufferDescriptorB()[buffer_index].Size() >= size,
327 "BufferDescriptorB buffer_index {} is not large enough", buffer_index); 327 "BufferDescriptorB buffer_index {} is not large enough", buffer_index);
328 memory.WriteBlock(BufferDescriptorB()[buffer_index].Address(), buffer, size); 328 memory.WriteBlock(BufferDescriptorB()[buffer_index].Address(), buffer, size);
329 } else { 329 } else {
330 ASSERT_MSG(BufferDescriptorC().size() > std::size_t(buffer_index), 330 ASSERT_MSG(BufferDescriptorC().size() > buffer_index,
331 "BufferDescriptorC invalid buffer_index {}", buffer_index); 331 "BufferDescriptorC invalid buffer_index {}", buffer_index);
332 ASSERT_MSG(BufferDescriptorC()[buffer_index].Size() >= size, 332 ASSERT_MSG(BufferDescriptorC()[buffer_index].Size() >= size,
333 "BufferDescriptorC buffer_index {} is not large enough", buffer_index); 333 "BufferDescriptorC buffer_index {} is not large enough", buffer_index);
@@ -337,17 +337,17 @@ std::size_t HLERequestContext::WriteBuffer(const void* buffer, std::size_t size,
337 return size; 337 return size;
338} 338}
339 339
340std::size_t HLERequestContext::GetReadBufferSize(int buffer_index) const { 340std::size_t HLERequestContext::GetReadBufferSize(std::size_t buffer_index) const {
341 const bool is_buffer_a{BufferDescriptorA().size() > std::size_t(buffer_index) && 341 const bool is_buffer_a{BufferDescriptorA().size() > buffer_index &&
342 BufferDescriptorA()[buffer_index].Size()}; 342 BufferDescriptorA()[buffer_index].Size()};
343 if (is_buffer_a) { 343 if (is_buffer_a) {
344 ASSERT_MSG(BufferDescriptorA().size() > std::size_t(buffer_index), 344 ASSERT_MSG(BufferDescriptorA().size() > buffer_index,
345 "BufferDescriptorA invalid buffer_index {}", buffer_index); 345 "BufferDescriptorA invalid buffer_index {}", buffer_index);
346 ASSERT_MSG(BufferDescriptorA()[buffer_index].Size() > 0, 346 ASSERT_MSG(BufferDescriptorA()[buffer_index].Size() > 0,
347 "BufferDescriptorA buffer_index {} is empty", buffer_index); 347 "BufferDescriptorA buffer_index {} is empty", buffer_index);
348 return BufferDescriptorA()[buffer_index].Size(); 348 return BufferDescriptorA()[buffer_index].Size();
349 } else { 349 } else {
350 ASSERT_MSG(BufferDescriptorX().size() > std::size_t(buffer_index), 350 ASSERT_MSG(BufferDescriptorX().size() > buffer_index,
351 "BufferDescriptorX invalid buffer_index {}", buffer_index); 351 "BufferDescriptorX invalid buffer_index {}", buffer_index);
352 ASSERT_MSG(BufferDescriptorX()[buffer_index].Size() > 0, 352 ASSERT_MSG(BufferDescriptorX()[buffer_index].Size() > 0,
353 "BufferDescriptorX buffer_index {} is empty", buffer_index); 353 "BufferDescriptorX buffer_index {} is empty", buffer_index);
@@ -355,15 +355,15 @@ std::size_t HLERequestContext::GetReadBufferSize(int buffer_index) const {
355 } 355 }
356} 356}
357 357
358std::size_t HLERequestContext::GetWriteBufferSize(int buffer_index) const { 358std::size_t HLERequestContext::GetWriteBufferSize(std::size_t buffer_index) const {
359 const bool is_buffer_b{BufferDescriptorB().size() > std::size_t(buffer_index) && 359 const bool is_buffer_b{BufferDescriptorB().size() > buffer_index &&
360 BufferDescriptorB()[buffer_index].Size()}; 360 BufferDescriptorB()[buffer_index].Size()};
361 if (is_buffer_b) { 361 if (is_buffer_b) {
362 ASSERT_MSG(BufferDescriptorB().size() > std::size_t(buffer_index), 362 ASSERT_MSG(BufferDescriptorB().size() > buffer_index,
363 "BufferDescriptorB invalid buffer_index {}", buffer_index); 363 "BufferDescriptorB invalid buffer_index {}", buffer_index);
364 return BufferDescriptorB()[buffer_index].Size(); 364 return BufferDescriptorB()[buffer_index].Size();
365 } else { 365 } else {
366 ASSERT_MSG(BufferDescriptorC().size() > std::size_t(buffer_index), 366 ASSERT_MSG(BufferDescriptorC().size() > buffer_index,
367 "BufferDescriptorC invalid buffer_index {}", buffer_index); 367 "BufferDescriptorC invalid buffer_index {}", buffer_index);
368 return BufferDescriptorC()[buffer_index].Size(); 368 return BufferDescriptorC()[buffer_index].Size();
369 } 369 }
diff --git a/src/core/hle/kernel/hle_ipc.h b/src/core/hle/kernel/hle_ipc.h
index 050ad8fd7..af3330297 100644
--- a/src/core/hle/kernel/hle_ipc.h
+++ b/src/core/hle/kernel/hle_ipc.h
@@ -179,10 +179,11 @@ public:
179 } 179 }
180 180
181 /// Helper function to read a buffer using the appropriate buffer descriptor 181 /// Helper function to read a buffer using the appropriate buffer descriptor
182 std::vector<u8> ReadBuffer(int buffer_index = 0) const; 182 std::vector<u8> ReadBuffer(std::size_t buffer_index = 0) const;
183 183
184 /// Helper function to write a buffer using the appropriate buffer descriptor 184 /// Helper function to write a buffer using the appropriate buffer descriptor
185 std::size_t WriteBuffer(const void* buffer, std::size_t size, int buffer_index = 0) const; 185 std::size_t WriteBuffer(const void* buffer, std::size_t size,
186 std::size_t buffer_index = 0) const;
186 187
187 /* Helper function to write a buffer using the appropriate buffer descriptor 188 /* Helper function to write a buffer using the appropriate buffer descriptor
188 * 189 *
@@ -194,7 +195,8 @@ public:
194 */ 195 */
195 template <typename ContiguousContainer, 196 template <typename ContiguousContainer,
196 typename = std::enable_if_t<!std::is_pointer_v<ContiguousContainer>>> 197 typename = std::enable_if_t<!std::is_pointer_v<ContiguousContainer>>>
197 std::size_t WriteBuffer(const ContiguousContainer& container, int buffer_index = 0) const { 198 std::size_t WriteBuffer(const ContiguousContainer& container,
199 std::size_t buffer_index = 0) const {
198 using ContiguousType = typename ContiguousContainer::value_type; 200 using ContiguousType = typename ContiguousContainer::value_type;
199 201
200 static_assert(std::is_trivially_copyable_v<ContiguousType>, 202 static_assert(std::is_trivially_copyable_v<ContiguousType>,
@@ -205,10 +207,10 @@ public:
205 } 207 }
206 208
207 /// Helper function to get the size of the input buffer 209 /// Helper function to get the size of the input buffer
208 std::size_t GetReadBufferSize(int buffer_index = 0) const; 210 std::size_t GetReadBufferSize(std::size_t buffer_index = 0) const;
209 211
210 /// Helper function to get the size of the output buffer 212 /// Helper function to get the size of the output buffer
211 std::size_t GetWriteBufferSize(int buffer_index = 0) const; 213 std::size_t GetWriteBufferSize(std::size_t buffer_index = 0) const;
212 214
213 template <typename T> 215 template <typename T>
214 std::shared_ptr<T> GetCopyObject(std::size_t index) { 216 std::shared_ptr<T> GetCopyObject(std::size_t index) {
diff --git a/src/core/hle/kernel/memory/slab_heap.h b/src/core/hle/kernel/memory/slab_heap.h
index 049403e15..be95fc3f7 100644
--- a/src/core/hle/kernel/memory/slab_heap.h
+++ b/src/core/hle/kernel/memory/slab_heap.h
@@ -51,7 +51,7 @@ public:
51 } 51 }
52 52
53 void Free(void* obj) { 53 void Free(void* obj) {
54 Node* node = reinterpret_cast<Node*>(obj); 54 Node* node = static_cast<Node*>(obj);
55 55
56 Node* cur_head = head.load(); 56 Node* cur_head = head.load();
57 do { 57 do {
@@ -145,7 +145,7 @@ public:
145 } 145 }
146 146
147 T* Allocate() { 147 T* Allocate() {
148 T* obj = reinterpret_cast<T*>(AllocateImpl()); 148 T* obj = static_cast<T*>(AllocateImpl());
149 if (obj != nullptr) { 149 if (obj != nullptr) {
150 new (obj) T(); 150 new (obj) T();
151 } 151 }
diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp
index 557608e76..3ece2cf3c 100644
--- a/src/core/hle/service/am/am.cpp
+++ b/src/core/hle/service/am/am.cpp
@@ -903,7 +903,7 @@ private:
903 void PopOutData(Kernel::HLERequestContext& ctx) { 903 void PopOutData(Kernel::HLERequestContext& ctx) {
904 LOG_DEBUG(Service_AM, "called"); 904 LOG_DEBUG(Service_AM, "called");
905 905
906 const auto storage = applet->GetBroker().PopNormalDataToGame(); 906 auto storage = applet->GetBroker().PopNormalDataToGame();
907 if (storage == nullptr) { 907 if (storage == nullptr) {
908 LOG_ERROR(Service_AM, 908 LOG_ERROR(Service_AM,
909 "storage is a nullptr. There is no data in the current normal channel"); 909 "storage is a nullptr. There is no data in the current normal channel");
@@ -934,7 +934,7 @@ private:
934 void PopInteractiveOutData(Kernel::HLERequestContext& ctx) { 934 void PopInteractiveOutData(Kernel::HLERequestContext& ctx) {
935 LOG_DEBUG(Service_AM, "called"); 935 LOG_DEBUG(Service_AM, "called");
936 936
937 const auto storage = applet->GetBroker().PopInteractiveDataToGame(); 937 auto storage = applet->GetBroker().PopInteractiveDataToGame();
938 if (storage == nullptr) { 938 if (storage == nullptr) {
939 LOG_ERROR(Service_AM, 939 LOG_ERROR(Service_AM,
940 "storage is a nullptr. There is no data in the current interactive channel"); 940 "storage is a nullptr. There is no data in the current interactive channel");
diff --git a/src/core/hle/service/audio/audren_u.cpp b/src/core/hle/service/audio/audren_u.cpp
index 175cabf45..d8359abaa 100644
--- a/src/core/hle/service/audio/audren_u.cpp
+++ b/src/core/hle/service/audio/audren_u.cpp
@@ -92,11 +92,16 @@ private:
92 } 92 }
93 93
94 void RequestUpdateImpl(Kernel::HLERequestContext& ctx) { 94 void RequestUpdateImpl(Kernel::HLERequestContext& ctx) {
95 LOG_WARNING(Service_Audio, "(STUBBED) called"); 95 LOG_DEBUG(Service_Audio, "(STUBBED) called");
96
97 auto result = renderer->UpdateAudioRenderer(ctx.ReadBuffer());
98
99 if (result.Succeeded()) {
100 ctx.WriteBuffer(result.Unwrap());
101 }
96 102
97 ctx.WriteBuffer(renderer->UpdateAudioRenderer(ctx.ReadBuffer()));
98 IPC::ResponseBuilder rb{ctx, 2}; 103 IPC::ResponseBuilder rb{ctx, 2};
99 rb.Push(RESULT_SUCCESS); 104 rb.Push(result.Code());
100 } 105 }
101 106
102 void Start(Kernel::HLERequestContext& ctx) { 107 void Start(Kernel::HLERequestContext& ctx) {
@@ -252,8 +257,6 @@ private:
252 } 257 }
253 258
254 void GetAudioDeviceOutputVolume(Kernel::HLERequestContext& ctx) { 259 void GetAudioDeviceOutputVolume(Kernel::HLERequestContext& ctx) {
255 IPC::RequestParser rp{ctx};
256
257 const auto device_name_buffer = ctx.ReadBuffer(); 260 const auto device_name_buffer = ctx.ReadBuffer();
258 const std::string name = Common::StringFromBuffer(device_name_buffer); 261 const std::string name = Common::StringFromBuffer(device_name_buffer);
259 262
diff --git a/src/core/hle/service/es/es.cpp b/src/core/hle/service/es/es.cpp
index df00ae625..86f36915a 100644
--- a/src/core/hle/service/es/es.cpp
+++ b/src/core/hle/service/es/es.cpp
@@ -76,7 +76,6 @@ private:
76 } 76 }
77 77
78 void ImportTicket(Kernel::HLERequestContext& ctx) { 78 void ImportTicket(Kernel::HLERequestContext& ctx) {
79 IPC::RequestParser rp{ctx};
80 const auto ticket = ctx.ReadBuffer(); 79 const auto ticket = ctx.ReadBuffer();
81 const auto cert = ctx.ReadBuffer(1); 80 const auto cert = ctx.ReadBuffer(1);
82 81
diff --git a/src/core/hle/service/hid/controllers/npad.cpp b/src/core/hle/service/hid/controllers/npad.cpp
index c1e32b28c..c55d900e2 100644
--- a/src/core/hle/service/hid/controllers/npad.cpp
+++ b/src/core/hle/service/hid/controllers/npad.cpp
@@ -107,6 +107,7 @@ void Controller_NPad::InitNewlyAddedControler(std::size_t controller_idx) {
107 switch (controller_type) { 107 switch (controller_type) {
108 case NPadControllerType::None: 108 case NPadControllerType::None:
109 UNREACHABLE(); 109 UNREACHABLE();
110 break;
110 case NPadControllerType::Handheld: 111 case NPadControllerType::Handheld:
111 controller.joy_styles.handheld.Assign(1); 112 controller.joy_styles.handheld.Assign(1);
112 controller.device_type.handheld.Assign(1); 113 controller.device_type.handheld.Assign(1);
@@ -363,6 +364,7 @@ void Controller_NPad::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8*
363 switch (controller_type) { 364 switch (controller_type) {
364 case NPadControllerType::None: 365 case NPadControllerType::None:
365 UNREACHABLE(); 366 UNREACHABLE();
367 break;
366 case NPadControllerType::Handheld: 368 case NPadControllerType::Handheld:
367 handheld_entry.connection_status.raw = 0; 369 handheld_entry.connection_status.raw = 0;
368 handheld_entry.connection_status.IsWired.Assign(1); 370 handheld_entry.connection_status.IsWired.Assign(1);
@@ -500,7 +502,7 @@ void Controller_NPad::SetNpadMode(u32 npad_id, NPadAssignments assignment_mode)
500 502
501void Controller_NPad::VibrateController(const std::vector<u32>& controller_ids, 503void Controller_NPad::VibrateController(const std::vector<u32>& controller_ids,
502 const std::vector<Vibration>& vibrations) { 504 const std::vector<Vibration>& vibrations) {
503 LOG_WARNING(Service_HID, "(STUBBED) called"); 505 LOG_DEBUG(Service_HID, "(STUBBED) called");
504 506
505 if (!can_controllers_vibrate) { 507 if (!can_controllers_vibrate) {
506 return; 508 return;
diff --git a/src/core/hle/service/time/standard_network_system_clock_core.h b/src/core/hle/service/time/standard_network_system_clock_core.h
index 3f505c37c..c993bdf79 100644
--- a/src/core/hle/service/time/standard_network_system_clock_core.h
+++ b/src/core/hle/service/time/standard_network_system_clock_core.h
@@ -23,7 +23,7 @@ public:
23 standard_network_clock_sufficient_accuracy = value; 23 standard_network_clock_sufficient_accuracy = value;
24 } 24 }
25 25
26 bool IsStandardNetworkSystemClockAccuracySufficient(Core::System& system) { 26 bool IsStandardNetworkSystemClockAccuracySufficient(Core::System& system) const {
27 SystemClockContext context{}; 27 SystemClockContext context{};
28 if (GetClockContext(system, context) != RESULT_SUCCESS) { 28 if (GetClockContext(system, context) != RESULT_SUCCESS) {
29 return {}; 29 return {};
diff --git a/src/core/hle/service/time/steady_clock_core.h b/src/core/hle/service/time/steady_clock_core.h
index 84af3d105..d80a2385f 100644
--- a/src/core/hle/service/time/steady_clock_core.h
+++ b/src/core/hle/service/time/steady_clock_core.h
@@ -16,6 +16,7 @@ namespace Service::Time::Clock {
16class SteadyClockCore { 16class SteadyClockCore {
17public: 17public:
18 SteadyClockCore() = default; 18 SteadyClockCore() = default;
19 virtual ~SteadyClockCore() = default;
19 20
20 const Common::UUID& GetClockSourceId() const { 21 const Common::UUID& GetClockSourceId() const {
21 return clock_source_id; 22 return clock_source_id;
diff --git a/src/core/hle/service/time/system_clock_context_update_callback.h b/src/core/hle/service/time/system_clock_context_update_callback.h
index 6260de6c3..2b0fa7e75 100644
--- a/src/core/hle/service/time/system_clock_context_update_callback.h
+++ b/src/core/hle/service/time/system_clock_context_update_callback.h
@@ -20,7 +20,7 @@ namespace Service::Time::Clock {
20class SystemClockContextUpdateCallback { 20class SystemClockContextUpdateCallback {
21public: 21public:
22 SystemClockContextUpdateCallback(); 22 SystemClockContextUpdateCallback();
23 ~SystemClockContextUpdateCallback(); 23 virtual ~SystemClockContextUpdateCallback();
24 24
25 bool NeedUpdate(const SystemClockContext& value) const; 25 bool NeedUpdate(const SystemClockContext& value) const;
26 26
diff --git a/src/core/hle/service/time/system_clock_core.cpp b/src/core/hle/service/time/system_clock_core.cpp
index 1a3ab8cfa..d31d4e2ca 100644
--- a/src/core/hle/service/time/system_clock_core.cpp
+++ b/src/core/hle/service/time/system_clock_core.cpp
@@ -9,7 +9,7 @@
9namespace Service::Time::Clock { 9namespace Service::Time::Clock {
10 10
11SystemClockCore::SystemClockCore(SteadyClockCore& steady_clock_core) 11SystemClockCore::SystemClockCore(SteadyClockCore& steady_clock_core)
12 : steady_clock_core{steady_clock_core}, is_initialized{} { 12 : steady_clock_core{steady_clock_core} {
13 context.steady_time_point.clock_source_id = steady_clock_core.GetClockSourceId(); 13 context.steady_time_point.clock_source_id = steady_clock_core.GetClockSourceId();
14} 14}
15 15
diff --git a/src/core/hle/service/time/system_clock_core.h b/src/core/hle/service/time/system_clock_core.h
index 54407a6c5..608dd3b2e 100644
--- a/src/core/hle/service/time/system_clock_core.h
+++ b/src/core/hle/service/time/system_clock_core.h
@@ -22,7 +22,7 @@ class SystemClockContextUpdateCallback;
22class SystemClockCore { 22class SystemClockCore {
23public: 23public:
24 explicit SystemClockCore(SteadyClockCore& steady_clock_core); 24 explicit SystemClockCore(SteadyClockCore& steady_clock_core);
25 ~SystemClockCore(); 25 virtual ~SystemClockCore();
26 26
27 SteadyClockCore& GetSteadyClockCore() const { 27 SteadyClockCore& GetSteadyClockCore() const {
28 return steady_clock_core; 28 return steady_clock_core;
diff --git a/src/core/hle/service/time/time_zone_manager.cpp b/src/core/hle/service/time/time_zone_manager.cpp
index c8159bcd5..69152d0ac 100644
--- a/src/core/hle/service/time/time_zone_manager.cpp
+++ b/src/core/hle/service/time/time_zone_manager.cpp
@@ -518,8 +518,8 @@ static bool ParseTimeZoneBinary(TimeZoneRule& time_zone_rule, FileSys::VirtualFi
518 constexpr s32 time_zone_max_leaps{50}; 518 constexpr s32 time_zone_max_leaps{50};
519 constexpr s32 time_zone_max_chars{50}; 519 constexpr s32 time_zone_max_chars{50};
520 if (!(0 <= header.leap_count && header.leap_count < time_zone_max_leaps && 520 if (!(0 <= header.leap_count && header.leap_count < time_zone_max_leaps &&
521 0 < header.type_count && header.type_count < time_zone_rule.ttis.size() && 521 0 < header.type_count && header.type_count < s32(time_zone_rule.ttis.size()) &&
522 0 <= header.time_count && header.time_count < time_zone_rule.ats.size() && 522 0 <= header.time_count && header.time_count < s32(time_zone_rule.ats.size()) &&
523 0 <= header.char_count && header.char_count < time_zone_max_chars && 523 0 <= header.char_count && header.char_count < time_zone_max_chars &&
524 (header.ttis_std_count == header.type_count || header.ttis_std_count == 0) && 524 (header.ttis_std_count == header.type_count || header.ttis_std_count == 0) &&
525 (header.ttis_gmt_count == header.type_count || header.ttis_gmt_count == 0))) { 525 (header.ttis_gmt_count == header.type_count || header.ttis_gmt_count == 0))) {
diff --git a/src/core/loader/elf.cpp b/src/core/loader/elf.cpp
index 1e9ed2837..8f7615115 100644
--- a/src/core/loader/elf.cpp
+++ b/src/core/loader/elf.cpp
@@ -398,6 +398,11 @@ AppLoader_ELF::LoadResult AppLoader_ELF::Load(Kernel::Process& process) {
398 Kernel::CodeSet codeset = elf_reader.LoadInto(base_address); 398 Kernel::CodeSet codeset = elf_reader.LoadInto(base_address);
399 const VAddr entry_point = codeset.entrypoint; 399 const VAddr entry_point = codeset.entrypoint;
400 400
401 // Setup the process code layout
402 if (process.LoadFromMetadata(FileSys::ProgramMetadata::GetDefault(), buffer.size()).IsError()) {
403 return {ResultStatus::ErrorNotInitialized, {}};
404 }
405
401 process.LoadModule(std::move(codeset), entry_point); 406 process.LoadModule(std::move(codeset), entry_point);
402 407
403 is_loaded = true; 408 is_loaded = true;
diff --git a/src/core/loader/nro.cpp b/src/core/loader/nro.cpp
index 5d7e8136e..906544bc9 100644
--- a/src/core/loader/nro.cpp
+++ b/src/core/loader/nro.cpp
@@ -131,7 +131,7 @@ static constexpr u32 PageAlignSize(u32 size) {
131} 131}
132 132
133static bool LoadNroImpl(Kernel::Process& process, const std::vector<u8>& data, 133static bool LoadNroImpl(Kernel::Process& process, const std::vector<u8>& data,
134 const std::string& name, VAddr load_base) { 134 const std::string& name) {
135 if (data.size() < sizeof(NroHeader)) { 135 if (data.size() < sizeof(NroHeader)) {
136 return {}; 136 return {};
137 } 137 }
@@ -187,19 +187,25 @@ static bool LoadNroImpl(Kernel::Process& process, const std::vector<u8>& data,
187 codeset.DataSegment().size += bss_size; 187 codeset.DataSegment().size += bss_size;
188 program_image.resize(static_cast<u32>(program_image.size()) + bss_size); 188 program_image.resize(static_cast<u32>(program_image.size()) + bss_size);
189 189
190 // Setup the process code layout
191 if (process.LoadFromMetadata(FileSys::ProgramMetadata::GetDefault(), program_image.size())
192 .IsError()) {
193 return false;
194 }
195
190 // Load codeset for current process 196 // Load codeset for current process
191 codeset.memory = std::move(program_image); 197 codeset.memory = std::move(program_image);
192 process.LoadModule(std::move(codeset), load_base); 198 process.LoadModule(std::move(codeset), process.PageTable().GetCodeRegionStart());
193 199
194 // Register module with GDBStub 200 // Register module with GDBStub
195 GDBStub::RegisterModule(name, load_base, load_base); 201 GDBStub::RegisterModule(name, process.PageTable().GetCodeRegionStart(),
202 process.PageTable().GetCodeRegionEnd());
196 203
197 return true; 204 return true;
198} 205}
199 206
200bool AppLoader_NRO::LoadNro(Kernel::Process& process, const FileSys::VfsFile& file, 207bool AppLoader_NRO::LoadNro(Kernel::Process& process, const FileSys::VfsFile& file) {
201 VAddr load_base) { 208 return LoadNroImpl(process, file.ReadAllBytes(), file.GetName());
202 return LoadNroImpl(process, file.ReadAllBytes(), file.GetName(), load_base);
203} 209}
204 210
205AppLoader_NRO::LoadResult AppLoader_NRO::Load(Kernel::Process& process) { 211AppLoader_NRO::LoadResult AppLoader_NRO::Load(Kernel::Process& process) {
@@ -207,10 +213,7 @@ AppLoader_NRO::LoadResult AppLoader_NRO::Load(Kernel::Process& process) {
207 return {ResultStatus::ErrorAlreadyLoaded, {}}; 213 return {ResultStatus::ErrorAlreadyLoaded, {}};
208 } 214 }
209 215
210 // Load NRO 216 if (!LoadNro(process, *file)) {
211 const VAddr base_address = process.PageTable().GetCodeRegionStart();
212
213 if (!LoadNro(process, *file, base_address)) {
214 return {ResultStatus::ErrorLoadingNRO, {}}; 217 return {ResultStatus::ErrorLoadingNRO, {}};
215 } 218 }
216 219
diff --git a/src/core/loader/nro.h b/src/core/loader/nro.h
index 71811bc29..4593d48fb 100644
--- a/src/core/loader/nro.h
+++ b/src/core/loader/nro.h
@@ -47,7 +47,7 @@ public:
47 bool IsRomFSUpdatable() const override; 47 bool IsRomFSUpdatable() const override;
48 48
49private: 49private:
50 bool LoadNro(Kernel::Process& process, const FileSys::VfsFile& file, VAddr load_base); 50 bool LoadNro(Kernel::Process& process, const FileSys::VfsFile& file);
51 51
52 std::vector<u8> icon_data; 52 std::vector<u8> icon_data;
53 std::unique_ptr<FileSys::NACP> nacp; 53 std::unique_ptr<FileSys::NACP> nacp;
diff --git a/src/core/loader/nso.cpp b/src/core/loader/nso.cpp
index 612ff9bf6..575330a86 100644
--- a/src/core/loader/nso.cpp
+++ b/src/core/loader/nso.cpp
@@ -37,7 +37,7 @@ static_assert(sizeof(MODHeader) == 0x1c, "MODHeader has incorrect size.");
37 37
38std::vector<u8> DecompressSegment(const std::vector<u8>& compressed_data, 38std::vector<u8> DecompressSegment(const std::vector<u8>& compressed_data,
39 const NSOSegmentHeader& header) { 39 const NSOSegmentHeader& header) {
40 const std::vector<u8> uncompressed_data = 40 std::vector<u8> uncompressed_data =
41 Common::Compression::DecompressDataLZ4(compressed_data, header.size); 41 Common::Compression::DecompressDataLZ4(compressed_data, header.size);
42 42
43 ASSERT_MSG(uncompressed_data.size() == header.size, "{} != {}", header.size, 43 ASSERT_MSG(uncompressed_data.size() == header.size, "{} != {}", header.size,
diff --git a/src/core/settings.h b/src/core/settings.h
index 79ec01731..c73d1c596 100644
--- a/src/core/settings.h
+++ b/src/core/settings.h
@@ -464,6 +464,7 @@ struct Values {
464 bool dump_nso; 464 bool dump_nso;
465 bool reporting_services; 465 bool reporting_services;
466 bool quest_flag; 466 bool quest_flag;
467 bool disable_cpu_opt;
467 468
468 // BCAT 469 // BCAT
469 std::string bcat_backend; 470 std::string bcat_backend;
diff --git a/src/video_core/dma_pusher.cpp b/src/video_core/dma_pusher.cpp
index 713c14182..0b77afc71 100644
--- a/src/video_core/dma_pusher.cpp
+++ b/src/video_core/dma_pusher.cpp
@@ -12,7 +12,7 @@
12 12
13namespace Tegra { 13namespace Tegra {
14 14
15DmaPusher::DmaPusher(GPU& gpu) : gpu(gpu) {} 15DmaPusher::DmaPusher(Core::System& system, GPU& gpu) : gpu{gpu}, system{system} {}
16 16
17DmaPusher::~DmaPusher() = default; 17DmaPusher::~DmaPusher() = default;
18 18
@@ -26,7 +26,7 @@ void DmaPusher::DispatchCalls() {
26 26
27 dma_pushbuffer_subindex = 0; 27 dma_pushbuffer_subindex = 0;
28 28
29 while (Core::System::GetInstance().IsPoweredOn()) { 29 while (system.IsPoweredOn()) {
30 if (!Step()) { 30 if (!Step()) {
31 break; 31 break;
32 } 32 }
diff --git a/src/video_core/dma_pusher.h b/src/video_core/dma_pusher.h
index 6ab06518f..d6188614a 100644
--- a/src/video_core/dma_pusher.h
+++ b/src/video_core/dma_pusher.h
@@ -10,6 +10,10 @@
10#include "common/bit_field.h" 10#include "common/bit_field.h"
11#include "common/common_types.h" 11#include "common/common_types.h"
12 12
13namespace Core {
14class System;
15}
16
13namespace Tegra { 17namespace Tegra {
14 18
15enum class SubmissionMode : u32 { 19enum class SubmissionMode : u32 {
@@ -56,7 +60,7 @@ using CommandList = std::vector<Tegra::CommandListHeader>;
56 */ 60 */
57class DmaPusher { 61class DmaPusher {
58public: 62public:
59 explicit DmaPusher(GPU& gpu); 63 explicit DmaPusher(Core::System& system, GPU& gpu);
60 ~DmaPusher(); 64 ~DmaPusher();
61 65
62 void Push(CommandList&& entries) { 66 void Push(CommandList&& entries) {
@@ -72,8 +76,6 @@ private:
72 76
73 void CallMethod(u32 argument) const; 77 void CallMethod(u32 argument) const;
74 78
75 GPU& gpu;
76
77 std::vector<CommandHeader> command_headers; ///< Buffer for list of commands fetched at once 79 std::vector<CommandHeader> command_headers; ///< Buffer for list of commands fetched at once
78 80
79 std::queue<CommandList> dma_pushbuffer; ///< Queue of command lists to be processed 81 std::queue<CommandList> dma_pushbuffer; ///< Queue of command lists to be processed
@@ -92,6 +94,9 @@ private:
92 94
93 GPUVAddr dma_mget{}; ///< main pushbuffer last read address 95 GPUVAddr dma_mget{}; ///< main pushbuffer last read address
94 bool ib_enable{true}; ///< IB mode enabled 96 bool ib_enable{true}; ///< IB mode enabled
97
98 GPU& gpu;
99 Core::System& system;
95}; 100};
96 101
97} // namespace Tegra 102} // namespace Tegra
diff --git a/src/video_core/engines/maxwell_3d.cpp b/src/video_core/engines/maxwell_3d.cpp
index ba63b44b4..baa74ad4c 100644
--- a/src/video_core/engines/maxwell_3d.cpp
+++ b/src/video_core/engines/maxwell_3d.cpp
@@ -92,6 +92,10 @@ void Maxwell3D::InitializeRegisterDefaults() {
92 color_mask.A.Assign(1); 92 color_mask.A.Assign(1);
93 } 93 }
94 94
95 for (auto& format : regs.vertex_attrib_format) {
96 format.constant.Assign(1);
97 }
98
95 // NVN games expect these values to be enabled at boot 99 // NVN games expect these values to be enabled at boot
96 regs.rasterize_enable = 1; 100 regs.rasterize_enable = 1;
97 regs.rt_separate_frag_data = 1; 101 regs.rt_separate_frag_data = 1;
diff --git a/src/video_core/engines/maxwell_3d.h b/src/video_core/engines/maxwell_3d.h
index 5cf6a4cc3..59d5752d2 100644
--- a/src/video_core/engines/maxwell_3d.h
+++ b/src/video_core/engines/maxwell_3d.h
@@ -1149,7 +1149,7 @@ public:
1149 1149
1150 /// Returns whether the vertex array specified by index is supposed to be 1150 /// Returns whether the vertex array specified by index is supposed to be
1151 /// accessed per instance or not. 1151 /// accessed per instance or not.
1152 bool IsInstancingEnabled(u32 index) const { 1152 bool IsInstancingEnabled(std::size_t index) const {
1153 return is_instanced[index]; 1153 return is_instanced[index];
1154 } 1154 }
1155 } instanced_arrays; 1155 } instanced_arrays;
diff --git a/src/video_core/gpu.cpp b/src/video_core/gpu.cpp
index 8acf2eda2..a606f4abd 100644
--- a/src/video_core/gpu.cpp
+++ b/src/video_core/gpu.cpp
@@ -27,7 +27,7 @@ GPU::GPU(Core::System& system, std::unique_ptr<VideoCore::RendererBase>&& render
27 : system{system}, renderer{std::move(renderer_)}, is_async{is_async} { 27 : system{system}, renderer{std::move(renderer_)}, is_async{is_async} {
28 auto& rasterizer{renderer->Rasterizer()}; 28 auto& rasterizer{renderer->Rasterizer()};
29 memory_manager = std::make_unique<Tegra::MemoryManager>(system, rasterizer); 29 memory_manager = std::make_unique<Tegra::MemoryManager>(system, rasterizer);
30 dma_pusher = std::make_unique<Tegra::DmaPusher>(*this); 30 dma_pusher = std::make_unique<Tegra::DmaPusher>(system, *this);
31 maxwell_3d = std::make_unique<Engines::Maxwell3D>(system, rasterizer, *memory_manager); 31 maxwell_3d = std::make_unique<Engines::Maxwell3D>(system, rasterizer, *memory_manager);
32 fermi_2d = std::make_unique<Engines::Fermi2D>(rasterizer); 32 fermi_2d = std::make_unique<Engines::Fermi2D>(rasterizer);
33 kepler_compute = std::make_unique<Engines::KeplerCompute>(system, rasterizer, *memory_manager); 33 kepler_compute = std::make_unique<Engines::KeplerCompute>(system, rasterizer, *memory_manager);
diff --git a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
index 4c7808a67..0cd3ad7e1 100644
--- a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
@@ -1145,6 +1145,7 @@ private:
1145 return {"gl_FragCoord"s + GetSwizzle(element), Type::Float}; 1145 return {"gl_FragCoord"s + GetSwizzle(element), Type::Float};
1146 default: 1146 default:
1147 UNREACHABLE(); 1147 UNREACHABLE();
1148 return {"0", Type::Int};
1148 } 1149 }
1149 case Attribute::Index::FrontColor: 1150 case Attribute::Index::FrontColor:
1150 return {"gl_Color"s + GetSwizzle(element), Type::Float}; 1151 return {"gl_Color"s + GetSwizzle(element), Type::Float};
diff --git a/src/video_core/renderer_vulkan/fixed_pipeline_state.cpp b/src/video_core/renderer_vulkan/fixed_pipeline_state.cpp
index 2bb376555..be1c31978 100644
--- a/src/video_core/renderer_vulkan/fixed_pipeline_state.cpp
+++ b/src/video_core/renderer_vulkan/fixed_pipeline_state.cpp
@@ -2,10 +2,12 @@
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <cstring>
5#include <tuple> 6#include <tuple>
6 7
7#include <boost/functional/hash.hpp> 8#include <boost/functional/hash.hpp>
8 9
10#include "common/cityhash.h"
9#include "common/common_types.h" 11#include "common/common_types.h"
10#include "video_core/renderer_vulkan/fixed_pipeline_state.h" 12#include "video_core/renderer_vulkan/fixed_pipeline_state.h"
11 13
@@ -13,289 +15,352 @@ namespace Vulkan {
13 15
14namespace { 16namespace {
15 17
16constexpr FixedPipelineState::DepthStencil GetDepthStencilState(const Maxwell& regs) { 18constexpr std::size_t POINT = 0;
17 const FixedPipelineState::StencilFace front_stencil( 19constexpr std::size_t LINE = 1;
18 regs.stencil_front_op_fail, regs.stencil_front_op_zfail, regs.stencil_front_op_zpass, 20constexpr std::size_t POLYGON = 2;
19 regs.stencil_front_func_func); 21constexpr std::array POLYGON_OFFSET_ENABLE_LUT = {
20 const FixedPipelineState::StencilFace back_stencil = 22 POINT, // Points
21 regs.stencil_two_side_enable 23 LINE, // Lines
22 ? FixedPipelineState::StencilFace(regs.stencil_back_op_fail, regs.stencil_back_op_zfail, 24 LINE, // LineLoop
23 regs.stencil_back_op_zpass, 25 LINE, // LineStrip
24 regs.stencil_back_func_func) 26 POLYGON, // Triangles
25 : front_stencil; 27 POLYGON, // TriangleStrip
26 return FixedPipelineState::DepthStencil( 28 POLYGON, // TriangleFan
27 regs.depth_test_enable == 1, regs.depth_write_enabled == 1, regs.depth_bounds_enable == 1, 29 POLYGON, // Quads
28 regs.stencil_enable == 1, regs.depth_test_func, front_stencil, back_stencil); 30 POLYGON, // QuadStrip
29} 31 POLYGON, // Polygon
30 32 LINE, // LinesAdjacency
31constexpr FixedPipelineState::InputAssembly GetInputAssemblyState(const Maxwell& regs) { 33 LINE, // LineStripAdjacency
32 return FixedPipelineState::InputAssembly( 34 POLYGON, // TrianglesAdjacency
33 regs.draw.topology, regs.primitive_restart.enabled, 35 POLYGON, // TriangleStripAdjacency
34 regs.draw.topology == Maxwell::PrimitiveTopology::Points ? regs.point_size : 0.0f); 36 POLYGON, // Patches
35} 37};
36
37constexpr FixedPipelineState::BlendingAttachment GetBlendingAttachmentState(
38 const Maxwell& regs, std::size_t render_target) {
39 const auto& mask = regs.color_mask[regs.color_mask_common ? 0 : render_target];
40 const std::array components = {mask.R != 0, mask.G != 0, mask.B != 0, mask.A != 0};
41
42 const FixedPipelineState::BlendingAttachment default_blending(
43 false, Maxwell::Blend::Equation::Add, Maxwell::Blend::Factor::One,
44 Maxwell::Blend::Factor::Zero, Maxwell::Blend::Equation::Add, Maxwell::Blend::Factor::One,
45 Maxwell::Blend::Factor::Zero, components);
46 if (render_target >= regs.rt_control.count) {
47 return default_blending;
48 }
49 38
50 if (!regs.independent_blend_enable) { 39} // Anonymous namespace
51 const auto& src = regs.blend;
52 if (!src.enable[render_target]) {
53 return default_blending;
54 }
55 return FixedPipelineState::BlendingAttachment(
56 true, src.equation_rgb, src.factor_source_rgb, src.factor_dest_rgb, src.equation_a,
57 src.factor_source_a, src.factor_dest_a, components);
58 }
59 40
60 if (!regs.blend.enable[render_target]) { 41void FixedPipelineState::DepthStencil::Fill(const Maxwell& regs) noexcept {
61 return default_blending; 42 raw = 0;
43 front.action_stencil_fail.Assign(PackStencilOp(regs.stencil_front_op_fail));
44 front.action_depth_fail.Assign(PackStencilOp(regs.stencil_front_op_zfail));
45 front.action_depth_pass.Assign(PackStencilOp(regs.stencil_front_op_zpass));
46 front.test_func.Assign(PackComparisonOp(regs.stencil_front_func_func));
47 if (regs.stencil_two_side_enable) {
48 back.action_stencil_fail.Assign(PackStencilOp(regs.stencil_back_op_fail));
49 back.action_depth_fail.Assign(PackStencilOp(regs.stencil_back_op_zfail));
50 back.action_depth_pass.Assign(PackStencilOp(regs.stencil_back_op_zpass));
51 back.test_func.Assign(PackComparisonOp(regs.stencil_back_func_func));
52 } else {
53 back.action_stencil_fail.Assign(front.action_stencil_fail);
54 back.action_depth_fail.Assign(front.action_depth_fail);
55 back.action_depth_pass.Assign(front.action_depth_pass);
56 back.test_func.Assign(front.test_func);
62 } 57 }
63 const auto& src = regs.independent_blend[render_target]; 58 depth_test_enable.Assign(regs.depth_test_enable);
64 return FixedPipelineState::BlendingAttachment( 59 depth_write_enable.Assign(regs.depth_write_enabled);
65 true, src.equation_rgb, src.factor_source_rgb, src.factor_dest_rgb, src.equation_a, 60 depth_bounds_enable.Assign(regs.depth_bounds_enable);
66 src.factor_source_a, src.factor_dest_a, components); 61 stencil_enable.Assign(regs.stencil_enable);
62 depth_test_func.Assign(PackComparisonOp(regs.depth_test_func));
67} 63}
68 64
69constexpr FixedPipelineState::ColorBlending GetColorBlendingState(const Maxwell& regs) { 65void FixedPipelineState::Rasterizer::Fill(const Maxwell& regs) noexcept {
70 return FixedPipelineState::ColorBlending( 66 const auto& clip = regs.view_volume_clip_control;
71 {regs.blend_color.r, regs.blend_color.g, regs.blend_color.b, regs.blend_color.a},
72 regs.rt_control.count,
73 {GetBlendingAttachmentState(regs, 0), GetBlendingAttachmentState(regs, 1),
74 GetBlendingAttachmentState(regs, 2), GetBlendingAttachmentState(regs, 3),
75 GetBlendingAttachmentState(regs, 4), GetBlendingAttachmentState(regs, 5),
76 GetBlendingAttachmentState(regs, 6), GetBlendingAttachmentState(regs, 7)});
77}
78
79constexpr FixedPipelineState::Tessellation GetTessellationState(const Maxwell& regs) {
80 return FixedPipelineState::Tessellation(regs.patch_vertices, regs.tess_mode.prim,
81 regs.tess_mode.spacing, regs.tess_mode.cw != 0);
82}
83
84constexpr std::size_t Point = 0;
85constexpr std::size_t Line = 1;
86constexpr std::size_t Polygon = 2;
87constexpr std::array PolygonOffsetEnableLUT = {
88 Point, // Points
89 Line, // Lines
90 Line, // LineLoop
91 Line, // LineStrip
92 Polygon, // Triangles
93 Polygon, // TriangleStrip
94 Polygon, // TriangleFan
95 Polygon, // Quads
96 Polygon, // QuadStrip
97 Polygon, // Polygon
98 Line, // LinesAdjacency
99 Line, // LineStripAdjacency
100 Polygon, // TrianglesAdjacency
101 Polygon, // TriangleStripAdjacency
102 Polygon, // Patches
103};
104
105constexpr FixedPipelineState::Rasterizer GetRasterizerState(const Maxwell& regs) {
106 const std::array enabled_lut = {regs.polygon_offset_point_enable, 67 const std::array enabled_lut = {regs.polygon_offset_point_enable,
107 regs.polygon_offset_line_enable, 68 regs.polygon_offset_line_enable,
108 regs.polygon_offset_fill_enable}; 69 regs.polygon_offset_fill_enable};
109 const auto topology = static_cast<std::size_t>(regs.draw.topology.Value()); 70 const u32 topology_index = static_cast<u32>(regs.draw.topology.Value());
110 const bool depth_bias_enabled = enabled_lut[PolygonOffsetEnableLUT[topology]];
111 71
112 const auto& clip = regs.view_volume_clip_control; 72 u32 packed_front_face = PackFrontFace(regs.front_face);
113 const bool depth_clamp_enabled = clip.depth_clamp_near == 1 || clip.depth_clamp_far == 1;
114
115 Maxwell::FrontFace front_face = regs.front_face;
116 if (regs.screen_y_control.triangle_rast_flip != 0 && 73 if (regs.screen_y_control.triangle_rast_flip != 0 &&
117 regs.viewport_transform[0].scale_y > 0.0f) { 74 regs.viewport_transform[0].scale_y > 0.0f) {
118 if (front_face == Maxwell::FrontFace::CounterClockWise) 75 // Flip front face
119 front_face = Maxwell::FrontFace::ClockWise; 76 packed_front_face = 1 - packed_front_face;
120 else if (front_face == Maxwell::FrontFace::ClockWise)
121 front_face = Maxwell::FrontFace::CounterClockWise;
122 } 77 }
123 78
124 const bool gl_ndc = regs.depth_mode == Maxwell::DepthMode::MinusOneToOne; 79 raw = 0;
125 return FixedPipelineState::Rasterizer(regs.cull_test_enabled, depth_bias_enabled, 80 topology.Assign(topology_index);
126 depth_clamp_enabled, gl_ndc, regs.cull_face, front_face); 81 primitive_restart_enable.Assign(regs.primitive_restart.enabled != 0 ? 1 : 0);
82 cull_enable.Assign(regs.cull_test_enabled != 0 ? 1 : 0);
83 depth_bias_enable.Assign(enabled_lut[POLYGON_OFFSET_ENABLE_LUT[topology_index]] != 0 ? 1 : 0);
84 depth_clamp_enable.Assign(clip.depth_clamp_near == 1 || clip.depth_clamp_far == 1 ? 1 : 0);
85 ndc_minus_one_to_one.Assign(regs.depth_mode == Maxwell::DepthMode::MinusOneToOne ? 1 : 0);
86 cull_face.Assign(PackCullFace(regs.cull_face));
87 front_face.Assign(packed_front_face);
88 polygon_mode.Assign(PackPolygonMode(regs.polygon_mode_front));
89 patch_control_points_minus_one.Assign(regs.patch_vertices - 1);
90 tessellation_primitive.Assign(static_cast<u32>(regs.tess_mode.prim.Value()));
91 tessellation_spacing.Assign(static_cast<u32>(regs.tess_mode.spacing.Value()));
92 tessellation_clockwise.Assign(regs.tess_mode.cw.Value());
93 logic_op_enable.Assign(regs.logic_op.enable != 0 ? 1 : 0);
94 logic_op.Assign(PackLogicOp(regs.logic_op.operation));
95 std::memcpy(&point_size, &regs.point_size, sizeof(point_size)); // TODO: C++20 std::bit_cast
127} 96}
128 97
129} // Anonymous namespace 98void FixedPipelineState::ColorBlending::Fill(const Maxwell& regs) noexcept {
130 99 for (std::size_t index = 0; index < std::size(attachments); ++index) {
131std::size_t FixedPipelineState::VertexBinding::Hash() const noexcept { 100 attachments[index].Fill(regs, index);
132 return (index << stride) ^ divisor; 101 }
133} 102}
134 103
135bool FixedPipelineState::VertexBinding::operator==(const VertexBinding& rhs) const noexcept { 104void FixedPipelineState::BlendingAttachment::Fill(const Maxwell& regs, std::size_t index) {
136 return std::tie(index, stride, divisor) == std::tie(rhs.index, rhs.stride, rhs.divisor); 105 const auto& mask = regs.color_mask[regs.color_mask_common ? 0 : index];
137} 106
107 raw = 0;
108 mask_r.Assign(mask.R);
109 mask_g.Assign(mask.G);
110 mask_b.Assign(mask.B);
111 mask_a.Assign(mask.A);
112
113 // TODO: C++20 Use templated lambda to deduplicate code
114
115 if (!regs.independent_blend_enable) {
116 const auto& src = regs.blend;
117 if (!src.enable[index]) {
118 return;
119 }
120 equation_rgb.Assign(PackBlendEquation(src.equation_rgb));
121 equation_a.Assign(PackBlendEquation(src.equation_a));
122 factor_source_rgb.Assign(PackBlendFactor(src.factor_source_rgb));
123 factor_dest_rgb.Assign(PackBlendFactor(src.factor_dest_rgb));
124 factor_source_a.Assign(PackBlendFactor(src.factor_source_a));
125 factor_dest_a.Assign(PackBlendFactor(src.factor_dest_a));
126 enable.Assign(1);
127 return;
128 }
138 129
139std::size_t FixedPipelineState::VertexAttribute::Hash() const noexcept { 130 if (!regs.blend.enable[index]) {
140 return static_cast<std::size_t>(index) ^ (static_cast<std::size_t>(buffer) << 13) ^ 131 return;
141 (static_cast<std::size_t>(type) << 22) ^ (static_cast<std::size_t>(size) << 31) ^ 132 }
142 (static_cast<std::size_t>(offset) << 36); 133 const auto& src = regs.independent_blend[index];
134 equation_rgb.Assign(PackBlendEquation(src.equation_rgb));
135 equation_a.Assign(PackBlendEquation(src.equation_a));
136 factor_source_rgb.Assign(PackBlendFactor(src.factor_source_rgb));
137 factor_dest_rgb.Assign(PackBlendFactor(src.factor_dest_rgb));
138 factor_source_a.Assign(PackBlendFactor(src.factor_source_a));
139 factor_dest_a.Assign(PackBlendFactor(src.factor_dest_a));
140 enable.Assign(1);
143} 141}
144 142
145bool FixedPipelineState::VertexAttribute::operator==(const VertexAttribute& rhs) const noexcept { 143std::size_t FixedPipelineState::Hash() const noexcept {
146 return std::tie(index, buffer, type, size, offset) == 144 const u64 hash = Common::CityHash64(reinterpret_cast<const char*>(this), sizeof *this);
147 std::tie(rhs.index, rhs.buffer, rhs.type, rhs.size, rhs.offset); 145 return static_cast<std::size_t>(hash);
148} 146}
149 147
150std::size_t FixedPipelineState::StencilFace::Hash() const noexcept { 148bool FixedPipelineState::operator==(const FixedPipelineState& rhs) const noexcept {
151 return static_cast<std::size_t>(action_stencil_fail) ^ 149 return std::memcmp(this, &rhs, sizeof *this) == 0;
152 (static_cast<std::size_t>(action_depth_fail) << 4) ^
153 (static_cast<std::size_t>(action_depth_fail) << 20) ^
154 (static_cast<std::size_t>(action_depth_pass) << 36);
155} 150}
156 151
157bool FixedPipelineState::StencilFace::operator==(const StencilFace& rhs) const noexcept { 152FixedPipelineState GetFixedPipelineState(const Maxwell& regs) {
158 return std::tie(action_stencil_fail, action_depth_fail, action_depth_pass, test_func) == 153 FixedPipelineState fixed_state;
159 std::tie(rhs.action_stencil_fail, rhs.action_depth_fail, rhs.action_depth_pass, 154 fixed_state.rasterizer.Fill(regs);
160 rhs.test_func); 155 fixed_state.depth_stencil.Fill(regs);
156 fixed_state.color_blending.Fill(regs);
157 fixed_state.padding = {};
158 return fixed_state;
161} 159}
162 160
163std::size_t FixedPipelineState::BlendingAttachment::Hash() const noexcept { 161u32 FixedPipelineState::PackComparisonOp(Maxwell::ComparisonOp op) noexcept {
164 return static_cast<std::size_t>(enable) ^ (static_cast<std::size_t>(rgb_equation) << 5) ^ 162 // OpenGL enums go from 0x200 to 0x207 and the others from 1 to 8
165 (static_cast<std::size_t>(src_rgb_func) << 10) ^ 163 // If we substract 0x200 to OpenGL enums and 1 to the others we get a 0-7 range.
166 (static_cast<std::size_t>(dst_rgb_func) << 15) ^ 164 // Perfect for a hash.
167 (static_cast<std::size_t>(a_equation) << 20) ^ 165 const u32 value = static_cast<u32>(op);
168 (static_cast<std::size_t>(src_a_func) << 25) ^ 166 return value - (value >= 0x200 ? 0x200 : 1);
169 (static_cast<std::size_t>(dst_a_func) << 30) ^
170 (static_cast<std::size_t>(components[0]) << 35) ^
171 (static_cast<std::size_t>(components[1]) << 36) ^
172 (static_cast<std::size_t>(components[2]) << 37) ^
173 (static_cast<std::size_t>(components[3]) << 38);
174} 167}
175 168
176bool FixedPipelineState::BlendingAttachment::operator==(const BlendingAttachment& rhs) const 169Maxwell::ComparisonOp FixedPipelineState::UnpackComparisonOp(u32 packed) noexcept {
177 noexcept { 170 // Read PackComparisonOp for the logic behind this.
178 return std::tie(enable, rgb_equation, src_rgb_func, dst_rgb_func, a_equation, src_a_func, 171 return static_cast<Maxwell::ComparisonOp>(packed + 1);
179 dst_a_func, components) ==
180 std::tie(rhs.enable, rhs.rgb_equation, rhs.src_rgb_func, rhs.dst_rgb_func,
181 rhs.a_equation, rhs.src_a_func, rhs.dst_a_func, rhs.components);
182} 172}
183 173
184std::size_t FixedPipelineState::VertexInput::Hash() const noexcept { 174u32 FixedPipelineState::PackStencilOp(Maxwell::StencilOp op) noexcept {
185 std::size_t hash = num_bindings ^ (num_attributes << 32); 175 switch (op) {
186 for (std::size_t i = 0; i < num_bindings; ++i) { 176 case Maxwell::StencilOp::Keep:
187 boost::hash_combine(hash, bindings[i].Hash()); 177 case Maxwell::StencilOp::KeepOGL:
188 } 178 return 0;
189 for (std::size_t i = 0; i < num_attributes; ++i) { 179 case Maxwell::StencilOp::Zero:
190 boost::hash_combine(hash, attributes[i].Hash()); 180 case Maxwell::StencilOp::ZeroOGL:
181 return 1;
182 case Maxwell::StencilOp::Replace:
183 case Maxwell::StencilOp::ReplaceOGL:
184 return 2;
185 case Maxwell::StencilOp::Incr:
186 case Maxwell::StencilOp::IncrOGL:
187 return 3;
188 case Maxwell::StencilOp::Decr:
189 case Maxwell::StencilOp::DecrOGL:
190 return 4;
191 case Maxwell::StencilOp::Invert:
192 case Maxwell::StencilOp::InvertOGL:
193 return 5;
194 case Maxwell::StencilOp::IncrWrap:
195 case Maxwell::StencilOp::IncrWrapOGL:
196 return 6;
197 case Maxwell::StencilOp::DecrWrap:
198 case Maxwell::StencilOp::DecrWrapOGL:
199 return 7;
191 } 200 }
192 return hash; 201 return 0;
193} 202}
194 203
195bool FixedPipelineState::VertexInput::operator==(const VertexInput& rhs) const noexcept { 204Maxwell::StencilOp FixedPipelineState::UnpackStencilOp(u32 packed) noexcept {
196 return std::equal(bindings.begin(), bindings.begin() + num_bindings, rhs.bindings.begin(), 205 static constexpr std::array LUT = {Maxwell::StencilOp::Keep, Maxwell::StencilOp::Zero,
197 rhs.bindings.begin() + rhs.num_bindings) && 206 Maxwell::StencilOp::Replace, Maxwell::StencilOp::Incr,
198 std::equal(attributes.begin(), attributes.begin() + num_attributes, 207 Maxwell::StencilOp::Decr, Maxwell::StencilOp::Invert,
199 rhs.attributes.begin(), rhs.attributes.begin() + rhs.num_attributes); 208 Maxwell::StencilOp::IncrWrap, Maxwell::StencilOp::DecrWrap};
209 return LUT[packed];
200} 210}
201 211
202std::size_t FixedPipelineState::InputAssembly::Hash() const noexcept { 212u32 FixedPipelineState::PackCullFace(Maxwell::CullFace cull) noexcept {
203 std::size_t point_size_int = 0; 213 // FrontAndBack is 0x408, by substracting 0x406 in it we get 2.
204 std::memcpy(&point_size_int, &point_size, sizeof(point_size)); 214 // Individual cull faces are in 0x404 and 0x405, substracting 0x404 we get 0 and 1.
205 return (static_cast<std::size_t>(topology) << 24) ^ (point_size_int << 32) ^ 215 const u32 value = static_cast<u32>(cull);
206 static_cast<std::size_t>(primitive_restart_enable); 216 return value - (value == 0x408 ? 0x406 : 0x404);
207} 217}
208 218
209bool FixedPipelineState::InputAssembly::operator==(const InputAssembly& rhs) const noexcept { 219Maxwell::CullFace FixedPipelineState::UnpackCullFace(u32 packed) noexcept {
210 return std::tie(topology, primitive_restart_enable, point_size) == 220 static constexpr std::array LUT = {Maxwell::CullFace::Front, Maxwell::CullFace::Back,
211 std::tie(rhs.topology, rhs.primitive_restart_enable, rhs.point_size); 221 Maxwell::CullFace::FrontAndBack};
222 return LUT[packed];
212} 223}
213 224
214std::size_t FixedPipelineState::Tessellation::Hash() const noexcept { 225u32 FixedPipelineState::PackFrontFace(Maxwell::FrontFace face) noexcept {
215 return static_cast<std::size_t>(patch_control_points) ^ 226 return static_cast<u32>(face) - 0x900;
216 (static_cast<std::size_t>(primitive) << 6) ^ (static_cast<std::size_t>(spacing) << 8) ^
217 (static_cast<std::size_t>(clockwise) << 10);
218} 227}
219 228
220bool FixedPipelineState::Tessellation::operator==(const Tessellation& rhs) const noexcept { 229Maxwell::FrontFace FixedPipelineState::UnpackFrontFace(u32 packed) noexcept {
221 return std::tie(patch_control_points, primitive, spacing, clockwise) == 230 return static_cast<Maxwell::FrontFace>(packed + 0x900);
222 std::tie(rhs.patch_control_points, rhs.primitive, rhs.spacing, rhs.clockwise);
223} 231}
224 232
225std::size_t FixedPipelineState::Rasterizer::Hash() const noexcept { 233u32 FixedPipelineState::PackPolygonMode(Maxwell::PolygonMode mode) noexcept {
226 return static_cast<std::size_t>(cull_enable) ^ 234 return static_cast<u32>(mode) - 0x1B00;
227 (static_cast<std::size_t>(depth_bias_enable) << 1) ^
228 (static_cast<std::size_t>(depth_clamp_enable) << 2) ^
229 (static_cast<std::size_t>(ndc_minus_one_to_one) << 3) ^
230 (static_cast<std::size_t>(cull_face) << 24) ^
231 (static_cast<std::size_t>(front_face) << 48);
232} 235}
233 236
234bool FixedPipelineState::Rasterizer::operator==(const Rasterizer& rhs) const noexcept { 237Maxwell::PolygonMode FixedPipelineState::UnpackPolygonMode(u32 packed) noexcept {
235 return std::tie(cull_enable, depth_bias_enable, depth_clamp_enable, ndc_minus_one_to_one, 238 return static_cast<Maxwell::PolygonMode>(packed + 0x1B00);
236 cull_face, front_face) ==
237 std::tie(rhs.cull_enable, rhs.depth_bias_enable, rhs.depth_clamp_enable,
238 rhs.ndc_minus_one_to_one, rhs.cull_face, rhs.front_face);
239} 239}
240 240
241std::size_t FixedPipelineState::DepthStencil::Hash() const noexcept { 241u32 FixedPipelineState::PackLogicOp(Maxwell::LogicOperation op) noexcept {
242 std::size_t hash = static_cast<std::size_t>(depth_test_enable) ^ 242 return static_cast<u32>(op) - 0x1500;
243 (static_cast<std::size_t>(depth_write_enable) << 1) ^
244 (static_cast<std::size_t>(depth_bounds_enable) << 2) ^
245 (static_cast<std::size_t>(stencil_enable) << 3) ^
246 (static_cast<std::size_t>(depth_test_function) << 4);
247 boost::hash_combine(hash, front_stencil.Hash());
248 boost::hash_combine(hash, back_stencil.Hash());
249 return hash;
250} 243}
251 244
252bool FixedPipelineState::DepthStencil::operator==(const DepthStencil& rhs) const noexcept { 245Maxwell::LogicOperation FixedPipelineState::UnpackLogicOp(u32 packed) noexcept {
253 return std::tie(depth_test_enable, depth_write_enable, depth_bounds_enable, depth_test_function, 246 return static_cast<Maxwell::LogicOperation>(packed + 0x1500);
254 stencil_enable, front_stencil, back_stencil) ==
255 std::tie(rhs.depth_test_enable, rhs.depth_write_enable, rhs.depth_bounds_enable,
256 rhs.depth_test_function, rhs.stencil_enable, rhs.front_stencil,
257 rhs.back_stencil);
258} 247}
259 248
260std::size_t FixedPipelineState::ColorBlending::Hash() const noexcept { 249u32 FixedPipelineState::PackBlendEquation(Maxwell::Blend::Equation equation) noexcept {
261 std::size_t hash = attachments_count << 13; 250 switch (equation) {
262 for (std::size_t rt = 0; rt < static_cast<std::size_t>(attachments_count); ++rt) { 251 case Maxwell::Blend::Equation::Add:
263 boost::hash_combine(hash, attachments[rt].Hash()); 252 case Maxwell::Blend::Equation::AddGL:
253 return 0;
254 case Maxwell::Blend::Equation::Subtract:
255 case Maxwell::Blend::Equation::SubtractGL:
256 return 1;
257 case Maxwell::Blend::Equation::ReverseSubtract:
258 case Maxwell::Blend::Equation::ReverseSubtractGL:
259 return 2;
260 case Maxwell::Blend::Equation::Min:
261 case Maxwell::Blend::Equation::MinGL:
262 return 3;
263 case Maxwell::Blend::Equation::Max:
264 case Maxwell::Blend::Equation::MaxGL:
265 return 4;
264 } 266 }
265 return hash; 267 return 0;
266} 268}
267 269
268bool FixedPipelineState::ColorBlending::operator==(const ColorBlending& rhs) const noexcept { 270Maxwell::Blend::Equation FixedPipelineState::UnpackBlendEquation(u32 packed) noexcept {
269 return std::equal(attachments.begin(), attachments.begin() + attachments_count, 271 static constexpr std::array LUT = {
270 rhs.attachments.begin(), rhs.attachments.begin() + rhs.attachments_count); 272 Maxwell::Blend::Equation::Add, Maxwell::Blend::Equation::Subtract,
271} 273 Maxwell::Blend::Equation::ReverseSubtract, Maxwell::Blend::Equation::Min,
272 274 Maxwell::Blend::Equation::Max};
273std::size_t FixedPipelineState::Hash() const noexcept { 275 return LUT[packed];
274 std::size_t hash = 0;
275 boost::hash_combine(hash, vertex_input.Hash());
276 boost::hash_combine(hash, input_assembly.Hash());
277 boost::hash_combine(hash, tessellation.Hash());
278 boost::hash_combine(hash, rasterizer.Hash());
279 boost::hash_combine(hash, depth_stencil.Hash());
280 boost::hash_combine(hash, color_blending.Hash());
281 return hash;
282} 276}
283 277
284bool FixedPipelineState::operator==(const FixedPipelineState& rhs) const noexcept { 278u32 FixedPipelineState::PackBlendFactor(Maxwell::Blend::Factor factor) noexcept {
285 return std::tie(vertex_input, input_assembly, tessellation, rasterizer, depth_stencil, 279 switch (factor) {
286 color_blending) == std::tie(rhs.vertex_input, rhs.input_assembly, 280 case Maxwell::Blend::Factor::Zero:
287 rhs.tessellation, rhs.rasterizer, rhs.depth_stencil, 281 case Maxwell::Blend::Factor::ZeroGL:
288 rhs.color_blending); 282 return 0;
283 case Maxwell::Blend::Factor::One:
284 case Maxwell::Blend::Factor::OneGL:
285 return 1;
286 case Maxwell::Blend::Factor::SourceColor:
287 case Maxwell::Blend::Factor::SourceColorGL:
288 return 2;
289 case Maxwell::Blend::Factor::OneMinusSourceColor:
290 case Maxwell::Blend::Factor::OneMinusSourceColorGL:
291 return 3;
292 case Maxwell::Blend::Factor::SourceAlpha:
293 case Maxwell::Blend::Factor::SourceAlphaGL:
294 return 4;
295 case Maxwell::Blend::Factor::OneMinusSourceAlpha:
296 case Maxwell::Blend::Factor::OneMinusSourceAlphaGL:
297 return 5;
298 case Maxwell::Blend::Factor::DestAlpha:
299 case Maxwell::Blend::Factor::DestAlphaGL:
300 return 6;
301 case Maxwell::Blend::Factor::OneMinusDestAlpha:
302 case Maxwell::Blend::Factor::OneMinusDestAlphaGL:
303 return 7;
304 case Maxwell::Blend::Factor::DestColor:
305 case Maxwell::Blend::Factor::DestColorGL:
306 return 8;
307 case Maxwell::Blend::Factor::OneMinusDestColor:
308 case Maxwell::Blend::Factor::OneMinusDestColorGL:
309 return 9;
310 case Maxwell::Blend::Factor::SourceAlphaSaturate:
311 case Maxwell::Blend::Factor::SourceAlphaSaturateGL:
312 return 10;
313 case Maxwell::Blend::Factor::Source1Color:
314 case Maxwell::Blend::Factor::Source1ColorGL:
315 return 11;
316 case Maxwell::Blend::Factor::OneMinusSource1Color:
317 case Maxwell::Blend::Factor::OneMinusSource1ColorGL:
318 return 12;
319 case Maxwell::Blend::Factor::Source1Alpha:
320 case Maxwell::Blend::Factor::Source1AlphaGL:
321 return 13;
322 case Maxwell::Blend::Factor::OneMinusSource1Alpha:
323 case Maxwell::Blend::Factor::OneMinusSource1AlphaGL:
324 return 14;
325 case Maxwell::Blend::Factor::ConstantColor:
326 case Maxwell::Blend::Factor::ConstantColorGL:
327 return 15;
328 case Maxwell::Blend::Factor::OneMinusConstantColor:
329 case Maxwell::Blend::Factor::OneMinusConstantColorGL:
330 return 16;
331 case Maxwell::Blend::Factor::ConstantAlpha:
332 case Maxwell::Blend::Factor::ConstantAlphaGL:
333 return 17;
334 case Maxwell::Blend::Factor::OneMinusConstantAlpha:
335 case Maxwell::Blend::Factor::OneMinusConstantAlphaGL:
336 return 18;
337 }
338 return 0;
289} 339}
290 340
291FixedPipelineState GetFixedPipelineState(const Maxwell& regs) { 341Maxwell::Blend::Factor FixedPipelineState::UnpackBlendFactor(u32 packed) noexcept {
292 FixedPipelineState fixed_state; 342 static constexpr std::array LUT = {
293 fixed_state.input_assembly = GetInputAssemblyState(regs); 343 Maxwell::Blend::Factor::Zero,
294 fixed_state.tessellation = GetTessellationState(regs); 344 Maxwell::Blend::Factor::One,
295 fixed_state.rasterizer = GetRasterizerState(regs); 345 Maxwell::Blend::Factor::SourceColor,
296 fixed_state.depth_stencil = GetDepthStencilState(regs); 346 Maxwell::Blend::Factor::OneMinusSourceColor,
297 fixed_state.color_blending = GetColorBlendingState(regs); 347 Maxwell::Blend::Factor::SourceAlpha,
298 return fixed_state; 348 Maxwell::Blend::Factor::OneMinusSourceAlpha,
349 Maxwell::Blend::Factor::DestAlpha,
350 Maxwell::Blend::Factor::OneMinusDestAlpha,
351 Maxwell::Blend::Factor::DestColor,
352 Maxwell::Blend::Factor::OneMinusDestColor,
353 Maxwell::Blend::Factor::SourceAlphaSaturate,
354 Maxwell::Blend::Factor::Source1Color,
355 Maxwell::Blend::Factor::OneMinusSource1Color,
356 Maxwell::Blend::Factor::Source1Alpha,
357 Maxwell::Blend::Factor::OneMinusSource1Alpha,
358 Maxwell::Blend::Factor::ConstantColor,
359 Maxwell::Blend::Factor::OneMinusConstantColor,
360 Maxwell::Blend::Factor::ConstantAlpha,
361 Maxwell::Blend::Factor::OneMinusConstantAlpha,
362 };
363 return LUT[packed];
299} 364}
300 365
301} // namespace Vulkan 366} // namespace Vulkan
diff --git a/src/video_core/renderer_vulkan/fixed_pipeline_state.h b/src/video_core/renderer_vulkan/fixed_pipeline_state.h
index 4c8ba7f90..9fe6bdbf9 100644
--- a/src/video_core/renderer_vulkan/fixed_pipeline_state.h
+++ b/src/video_core/renderer_vulkan/fixed_pipeline_state.h
@@ -7,6 +7,7 @@
7#include <array> 7#include <array>
8#include <type_traits> 8#include <type_traits>
9 9
10#include "common/bit_field.h"
10#include "common/common_types.h" 11#include "common/common_types.h"
11 12
12#include "video_core/engines/maxwell_3d.h" 13#include "video_core/engines/maxwell_3d.h"
@@ -16,93 +17,48 @@ namespace Vulkan {
16 17
17using Maxwell = Tegra::Engines::Maxwell3D::Regs; 18using Maxwell = Tegra::Engines::Maxwell3D::Regs;
18 19
19// TODO(Rodrigo): Optimize this structure. 20struct alignas(32) FixedPipelineState {
21 static u32 PackComparisonOp(Maxwell::ComparisonOp op) noexcept;
22 static Maxwell::ComparisonOp UnpackComparisonOp(u32 packed) noexcept;
20 23
21struct FixedPipelineState { 24 static u32 PackStencilOp(Maxwell::StencilOp op) noexcept;
22 using PixelFormat = VideoCore::Surface::PixelFormat; 25 static Maxwell::StencilOp UnpackStencilOp(u32 packed) noexcept;
23 26
24 struct VertexBinding { 27 static u32 PackCullFace(Maxwell::CullFace cull) noexcept;
25 constexpr VertexBinding(u32 index, u32 stride, u32 divisor) 28 static Maxwell::CullFace UnpackCullFace(u32 packed) noexcept;
26 : index{index}, stride{stride}, divisor{divisor} {}
27 VertexBinding() = default;
28 29
29 u32 index; 30 static u32 PackFrontFace(Maxwell::FrontFace face) noexcept;
30 u32 stride; 31 static Maxwell::FrontFace UnpackFrontFace(u32 packed) noexcept;
31 u32 divisor;
32 32
33 std::size_t Hash() const noexcept; 33 static u32 PackPolygonMode(Maxwell::PolygonMode mode) noexcept;
34 34 static Maxwell::PolygonMode UnpackPolygonMode(u32 packed) noexcept;
35 bool operator==(const VertexBinding& rhs) const noexcept;
36
37 bool operator!=(const VertexBinding& rhs) const noexcept {
38 return !operator==(rhs);
39 }
40 };
41
42 struct VertexAttribute {
43 constexpr VertexAttribute(u32 index, u32 buffer, Maxwell::VertexAttribute::Type type,
44 Maxwell::VertexAttribute::Size size, u32 offset)
45 : index{index}, buffer{buffer}, type{type}, size{size}, offset{offset} {}
46 VertexAttribute() = default;
47
48 u32 index;
49 u32 buffer;
50 Maxwell::VertexAttribute::Type type;
51 Maxwell::VertexAttribute::Size size;
52 u32 offset;
53
54 std::size_t Hash() const noexcept;
55
56 bool operator==(const VertexAttribute& rhs) const noexcept;
57
58 bool operator!=(const VertexAttribute& rhs) const noexcept {
59 return !operator==(rhs);
60 }
61 };
62
63 struct StencilFace {
64 constexpr StencilFace(Maxwell::StencilOp action_stencil_fail,
65 Maxwell::StencilOp action_depth_fail,
66 Maxwell::StencilOp action_depth_pass, Maxwell::ComparisonOp test_func)
67 : action_stencil_fail{action_stencil_fail}, action_depth_fail{action_depth_fail},
68 action_depth_pass{action_depth_pass}, test_func{test_func} {}
69 StencilFace() = default;
70
71 Maxwell::StencilOp action_stencil_fail;
72 Maxwell::StencilOp action_depth_fail;
73 Maxwell::StencilOp action_depth_pass;
74 Maxwell::ComparisonOp test_func;
75 35
76 std::size_t Hash() const noexcept; 36 static u32 PackLogicOp(Maxwell::LogicOperation op) noexcept;
37 static Maxwell::LogicOperation UnpackLogicOp(u32 packed) noexcept;
77 38
78 bool operator==(const StencilFace& rhs) const noexcept; 39 static u32 PackBlendEquation(Maxwell::Blend::Equation equation) noexcept;
40 static Maxwell::Blend::Equation UnpackBlendEquation(u32 packed) noexcept;
79 41
80 bool operator!=(const StencilFace& rhs) const noexcept { 42 static u32 PackBlendFactor(Maxwell::Blend::Factor factor) noexcept;
81 return !operator==(rhs); 43 static Maxwell::Blend::Factor UnpackBlendFactor(u32 packed) noexcept;
82 }
83 };
84 44
85 struct BlendingAttachment { 45 struct BlendingAttachment {
86 constexpr BlendingAttachment(bool enable, Maxwell::Blend::Equation rgb_equation, 46 union {
87 Maxwell::Blend::Factor src_rgb_func, 47 u32 raw;
88 Maxwell::Blend::Factor dst_rgb_func, 48 BitField<0, 1, u32> mask_r;
89 Maxwell::Blend::Equation a_equation, 49 BitField<1, 1, u32> mask_g;
90 Maxwell::Blend::Factor src_a_func, 50 BitField<2, 1, u32> mask_b;
91 Maxwell::Blend::Factor dst_a_func, 51 BitField<3, 1, u32> mask_a;
92 std::array<bool, 4> components) 52 BitField<4, 3, u32> equation_rgb;
93 : enable{enable}, rgb_equation{rgb_equation}, src_rgb_func{src_rgb_func}, 53 BitField<7, 3, u32> equation_a;
94 dst_rgb_func{dst_rgb_func}, a_equation{a_equation}, src_a_func{src_a_func}, 54 BitField<10, 5, u32> factor_source_rgb;
95 dst_a_func{dst_a_func}, components{components} {} 55 BitField<15, 5, u32> factor_dest_rgb;
96 BlendingAttachment() = default; 56 BitField<20, 5, u32> factor_source_a;
97 57 BitField<25, 5, u32> factor_dest_a;
98 bool enable; 58 BitField<30, 1, u32> enable;
99 Maxwell::Blend::Equation rgb_equation; 59 };
100 Maxwell::Blend::Factor src_rgb_func; 60
101 Maxwell::Blend::Factor dst_rgb_func; 61 void Fill(const Maxwell& regs, std::size_t index);
102 Maxwell::Blend::Equation a_equation;
103 Maxwell::Blend::Factor src_a_func;
104 Maxwell::Blend::Factor dst_a_func;
105 std::array<bool, 4> components;
106 62
107 std::size_t Hash() const noexcept; 63 std::size_t Hash() const noexcept;
108 64
@@ -111,136 +67,178 @@ struct FixedPipelineState {
111 bool operator!=(const BlendingAttachment& rhs) const noexcept { 67 bool operator!=(const BlendingAttachment& rhs) const noexcept {
112 return !operator==(rhs); 68 return !operator==(rhs);
113 } 69 }
114 };
115
116 struct VertexInput {
117 std::size_t num_bindings = 0;
118 std::size_t num_attributes = 0;
119 std::array<VertexBinding, Maxwell::NumVertexArrays> bindings;
120 std::array<VertexAttribute, Maxwell::NumVertexAttributes> attributes;
121
122 std::size_t Hash() const noexcept;
123 70
124 bool operator==(const VertexInput& rhs) const noexcept; 71 constexpr std::array<bool, 4> Mask() const noexcept {
72 return {mask_r != 0, mask_g != 0, mask_b != 0, mask_a != 0};
73 }
125 74
126 bool operator!=(const VertexInput& rhs) const noexcept { 75 Maxwell::Blend::Equation EquationRGB() const noexcept {
127 return !operator==(rhs); 76 return UnpackBlendEquation(equation_rgb.Value());
128 } 77 }
129 };
130 78
131 struct InputAssembly { 79 Maxwell::Blend::Equation EquationAlpha() const noexcept {
132 constexpr InputAssembly(Maxwell::PrimitiveTopology topology, bool primitive_restart_enable, 80 return UnpackBlendEquation(equation_a.Value());
133 float point_size) 81 }
134 : topology{topology}, primitive_restart_enable{primitive_restart_enable},
135 point_size{point_size} {}
136 InputAssembly() = default;
137 82
138 Maxwell::PrimitiveTopology topology; 83 Maxwell::Blend::Factor SourceRGBFactor() const noexcept {
139 bool primitive_restart_enable; 84 return UnpackBlendFactor(factor_source_rgb.Value());
140 float point_size; 85 }
141 86
142 std::size_t Hash() const noexcept; 87 Maxwell::Blend::Factor DestRGBFactor() const noexcept {
88 return UnpackBlendFactor(factor_dest_rgb.Value());
89 }
143 90
144 bool operator==(const InputAssembly& rhs) const noexcept; 91 Maxwell::Blend::Factor SourceAlphaFactor() const noexcept {
92 return UnpackBlendFactor(factor_source_a.Value());
93 }
145 94
146 bool operator!=(const InputAssembly& rhs) const noexcept { 95 Maxwell::Blend::Factor DestAlphaFactor() const noexcept {
147 return !operator==(rhs); 96 return UnpackBlendFactor(factor_dest_a.Value());
148 } 97 }
149 }; 98 };
150 99
151 struct Tessellation { 100 struct VertexInput {
152 constexpr Tessellation(u32 patch_control_points, Maxwell::TessellationPrimitive primitive, 101 union Binding {
153 Maxwell::TessellationSpacing spacing, bool clockwise) 102 u16 raw;
154 : patch_control_points{patch_control_points}, primitive{primitive}, spacing{spacing}, 103 BitField<0, 1, u16> enabled;
155 clockwise{clockwise} {} 104 BitField<1, 12, u16> stride;
156 Tessellation() = default; 105 };
157 106
158 u32 patch_control_points; 107 union Attribute {
159 Maxwell::TessellationPrimitive primitive; 108 u32 raw;
160 Maxwell::TessellationSpacing spacing; 109 BitField<0, 1, u32> enabled;
161 bool clockwise; 110 BitField<1, 5, u32> buffer;
162 111 BitField<6, 14, u32> offset;
163 std::size_t Hash() const noexcept; 112 BitField<20, 3, u32> type;
164 113 BitField<23, 6, u32> size;
165 bool operator==(const Tessellation& rhs) const noexcept; 114
115 constexpr Maxwell::VertexAttribute::Type Type() const noexcept {
116 return static_cast<Maxwell::VertexAttribute::Type>(type.Value());
117 }
118
119 constexpr Maxwell::VertexAttribute::Size Size() const noexcept {
120 return static_cast<Maxwell::VertexAttribute::Size>(size.Value());
121 }
122 };
123
124 std::array<Binding, Maxwell::NumVertexArrays> bindings;
125 std::array<u32, Maxwell::NumVertexArrays> binding_divisors;
126 std::array<Attribute, Maxwell::NumVertexAttributes> attributes;
127
128 void SetBinding(std::size_t index, bool enabled, u32 stride, u32 divisor) noexcept {
129 auto& binding = bindings[index];
130 binding.raw = 0;
131 binding.enabled.Assign(enabled ? 1 : 0);
132 binding.stride.Assign(stride);
133 binding_divisors[index] = divisor;
134 }
166 135
167 bool operator!=(const Tessellation& rhs) const noexcept { 136 void SetAttribute(std::size_t index, bool enabled, u32 buffer, u32 offset,
168 return !operator==(rhs); 137 Maxwell::VertexAttribute::Type type,
138 Maxwell::VertexAttribute::Size size) noexcept {
139 auto& attribute = attributes[index];
140 attribute.raw = 0;
141 attribute.enabled.Assign(enabled ? 1 : 0);
142 attribute.buffer.Assign(buffer);
143 attribute.offset.Assign(offset);
144 attribute.type.Assign(static_cast<u32>(type));
145 attribute.size.Assign(static_cast<u32>(size));
169 } 146 }
170 }; 147 };
171 148
172 struct Rasterizer { 149 struct Rasterizer {
173 constexpr Rasterizer(bool cull_enable, bool depth_bias_enable, bool depth_clamp_enable, 150 union {
174 bool ndc_minus_one_to_one, Maxwell::CullFace cull_face, 151 u32 raw;
175 Maxwell::FrontFace front_face) 152 BitField<0, 4, u32> topology;
176 : cull_enable{cull_enable}, depth_bias_enable{depth_bias_enable}, 153 BitField<4, 1, u32> primitive_restart_enable;
177 depth_clamp_enable{depth_clamp_enable}, ndc_minus_one_to_one{ndc_minus_one_to_one}, 154 BitField<5, 1, u32> cull_enable;
178 cull_face{cull_face}, front_face{front_face} {} 155 BitField<6, 1, u32> depth_bias_enable;
179 Rasterizer() = default; 156 BitField<7, 1, u32> depth_clamp_enable;
180 157 BitField<8, 1, u32> ndc_minus_one_to_one;
181 bool cull_enable; 158 BitField<9, 2, u32> cull_face;
182 bool depth_bias_enable; 159 BitField<11, 1, u32> front_face;
183 bool depth_clamp_enable; 160 BitField<12, 2, u32> polygon_mode;
184 bool ndc_minus_one_to_one; 161 BitField<14, 5, u32> patch_control_points_minus_one;
185 Maxwell::CullFace cull_face; 162 BitField<19, 2, u32> tessellation_primitive;
186 Maxwell::FrontFace front_face; 163 BitField<21, 2, u32> tessellation_spacing;
187 164 BitField<23, 1, u32> tessellation_clockwise;
188 std::size_t Hash() const noexcept; 165 BitField<24, 1, u32> logic_op_enable;
166 BitField<25, 4, u32> logic_op;
167 };
168
169 // TODO(Rodrigo): Move this to push constants
170 u32 point_size;
171
172 void Fill(const Maxwell& regs) noexcept;
173
174 constexpr Maxwell::PrimitiveTopology Topology() const noexcept {
175 return static_cast<Maxwell::PrimitiveTopology>(topology.Value());
176 }
189 177
190 bool operator==(const Rasterizer& rhs) const noexcept; 178 Maxwell::CullFace CullFace() const noexcept {
179 return UnpackCullFace(cull_face.Value());
180 }
191 181
192 bool operator!=(const Rasterizer& rhs) const noexcept { 182 Maxwell::FrontFace FrontFace() const noexcept {
193 return !operator==(rhs); 183 return UnpackFrontFace(front_face.Value());
194 } 184 }
195 }; 185 };
196 186
197 struct DepthStencil { 187 struct DepthStencil {
198 constexpr DepthStencil(bool depth_test_enable, bool depth_write_enable, 188 template <std::size_t Position>
199 bool depth_bounds_enable, bool stencil_enable, 189 union StencilFace {
200 Maxwell::ComparisonOp depth_test_function, StencilFace front_stencil, 190 BitField<Position + 0, 3, u32> action_stencil_fail;
201 StencilFace back_stencil) 191 BitField<Position + 3, 3, u32> action_depth_fail;
202 : depth_test_enable{depth_test_enable}, depth_write_enable{depth_write_enable}, 192 BitField<Position + 6, 3, u32> action_depth_pass;
203 depth_bounds_enable{depth_bounds_enable}, stencil_enable{stencil_enable}, 193 BitField<Position + 9, 3, u32> test_func;
204 depth_test_function{depth_test_function}, front_stencil{front_stencil}, 194
205 back_stencil{back_stencil} {} 195 Maxwell::StencilOp ActionStencilFail() const noexcept {
206 DepthStencil() = default; 196 return UnpackStencilOp(action_stencil_fail);
207 197 }
208 bool depth_test_enable; 198
209 bool depth_write_enable; 199 Maxwell::StencilOp ActionDepthFail() const noexcept {
210 bool depth_bounds_enable; 200 return UnpackStencilOp(action_depth_fail);
211 bool stencil_enable; 201 }
212 Maxwell::ComparisonOp depth_test_function; 202
213 StencilFace front_stencil; 203 Maxwell::StencilOp ActionDepthPass() const noexcept {
214 StencilFace back_stencil; 204 return UnpackStencilOp(action_depth_pass);
215 205 }
216 std::size_t Hash() const noexcept; 206
217 207 Maxwell::ComparisonOp TestFunc() const noexcept {
218 bool operator==(const DepthStencil& rhs) const noexcept; 208 return UnpackComparisonOp(test_func);
219 209 }
220 bool operator!=(const DepthStencil& rhs) const noexcept { 210 };
221 return !operator==(rhs); 211
212 union {
213 u32 raw;
214 StencilFace<0> front;
215 StencilFace<12> back;
216 BitField<24, 1, u32> depth_test_enable;
217 BitField<25, 1, u32> depth_write_enable;
218 BitField<26, 1, u32> depth_bounds_enable;
219 BitField<27, 1, u32> stencil_enable;
220 BitField<28, 3, u32> depth_test_func;
221 };
222
223 void Fill(const Maxwell& regs) noexcept;
224
225 Maxwell::ComparisonOp DepthTestFunc() const noexcept {
226 return UnpackComparisonOp(depth_test_func);
222 } 227 }
223 }; 228 };
224 229
225 struct ColorBlending { 230 struct ColorBlending {
226 constexpr ColorBlending(
227 std::array<float, 4> blend_constants, std::size_t attachments_count,
228 std::array<BlendingAttachment, Maxwell::NumRenderTargets> attachments)
229 : attachments_count{attachments_count}, attachments{attachments} {}
230 ColorBlending() = default;
231
232 std::size_t attachments_count;
233 std::array<BlendingAttachment, Maxwell::NumRenderTargets> attachments; 231 std::array<BlendingAttachment, Maxwell::NumRenderTargets> attachments;
234 232
235 std::size_t Hash() const noexcept; 233 void Fill(const Maxwell& regs) noexcept;
236
237 bool operator==(const ColorBlending& rhs) const noexcept;
238
239 bool operator!=(const ColorBlending& rhs) const noexcept {
240 return !operator==(rhs);
241 }
242 }; 234 };
243 235
236 VertexInput vertex_input;
237 Rasterizer rasterizer;
238 DepthStencil depth_stencil;
239 ColorBlending color_blending;
240 std::array<u8, 20> padding;
241
244 std::size_t Hash() const noexcept; 242 std::size_t Hash() const noexcept;
245 243
246 bool operator==(const FixedPipelineState& rhs) const noexcept; 244 bool operator==(const FixedPipelineState& rhs) const noexcept;
@@ -248,25 +246,11 @@ struct FixedPipelineState {
248 bool operator!=(const FixedPipelineState& rhs) const noexcept { 246 bool operator!=(const FixedPipelineState& rhs) const noexcept {
249 return !operator==(rhs); 247 return !operator==(rhs);
250 } 248 }
251
252 VertexInput vertex_input;
253 InputAssembly input_assembly;
254 Tessellation tessellation;
255 Rasterizer rasterizer;
256 DepthStencil depth_stencil;
257 ColorBlending color_blending;
258}; 249};
259static_assert(std::is_trivially_copyable_v<FixedPipelineState::VertexBinding>); 250static_assert(std::has_unique_object_representations_v<FixedPipelineState>);
260static_assert(std::is_trivially_copyable_v<FixedPipelineState::VertexAttribute>);
261static_assert(std::is_trivially_copyable_v<FixedPipelineState::StencilFace>);
262static_assert(std::is_trivially_copyable_v<FixedPipelineState::BlendingAttachment>);
263static_assert(std::is_trivially_copyable_v<FixedPipelineState::VertexInput>);
264static_assert(std::is_trivially_copyable_v<FixedPipelineState::InputAssembly>);
265static_assert(std::is_trivially_copyable_v<FixedPipelineState::Tessellation>);
266static_assert(std::is_trivially_copyable_v<FixedPipelineState::Rasterizer>);
267static_assert(std::is_trivially_copyable_v<FixedPipelineState::DepthStencil>);
268static_assert(std::is_trivially_copyable_v<FixedPipelineState::ColorBlending>);
269static_assert(std::is_trivially_copyable_v<FixedPipelineState>); 251static_assert(std::is_trivially_copyable_v<FixedPipelineState>);
252static_assert(std::is_trivially_constructible_v<FixedPipelineState>);
253static_assert(sizeof(FixedPipelineState) % 32 == 0, "Size is not aligned");
270 254
271FixedPipelineState GetFixedPipelineState(const Maxwell& regs); 255FixedPipelineState GetFixedPipelineState(const Maxwell& regs);
272 256
diff --git a/src/video_core/renderer_vulkan/renderer_vulkan.cpp b/src/video_core/renderer_vulkan/renderer_vulkan.cpp
index dd590c38b..04532f8f8 100644
--- a/src/video_core/renderer_vulkan/renderer_vulkan.cpp
+++ b/src/video_core/renderer_vulkan/renderer_vulkan.cpp
@@ -42,7 +42,7 @@
42#include <vulkan/vulkan_win32.h> 42#include <vulkan/vulkan_win32.h>
43#endif 43#endif
44 44
45#ifdef __linux__ 45#if !defined(_WIN32) && !defined(__APPLE__)
46#include <X11/Xlib.h> 46#include <X11/Xlib.h>
47#include <vulkan/vulkan_wayland.h> 47#include <vulkan/vulkan_wayland.h>
48#include <vulkan/vulkan_xlib.h> 48#include <vulkan/vulkan_xlib.h>
@@ -119,7 +119,7 @@ vk::Instance CreateInstance(Common::DynamicLibrary& library, vk::InstanceDispatc
119 extensions.push_back(VK_KHR_WIN32_SURFACE_EXTENSION_NAME); 119 extensions.push_back(VK_KHR_WIN32_SURFACE_EXTENSION_NAME);
120 break; 120 break;
121#endif 121#endif
122#ifdef __linux__ 122#if !defined(_WIN32) && !defined(__APPLE__)
123 case Core::Frontend::WindowSystemType::X11: 123 case Core::Frontend::WindowSystemType::X11:
124 extensions.push_back(VK_KHR_XLIB_SURFACE_EXTENSION_NAME); 124 extensions.push_back(VK_KHR_XLIB_SURFACE_EXTENSION_NAME);
125 break; 125 break;
@@ -345,7 +345,7 @@ bool RendererVulkan::CreateSurface() {
345 } 345 }
346 } 346 }
347#endif 347#endif
348#ifdef __linux__ 348#if !defined(_WIN32) && !defined(__APPLE__)
349 if (window_info.type == Core::Frontend::WindowSystemType::X11) { 349 if (window_info.type == Core::Frontend::WindowSystemType::X11) {
350 const VkXlibSurfaceCreateInfoKHR xlib_ci{ 350 const VkXlibSurfaceCreateInfoKHR xlib_ci{
351 VK_STRUCTURE_TYPE_XLIB_SURFACE_CREATE_INFO_KHR, nullptr, 0, 351 VK_STRUCTURE_TYPE_XLIB_SURFACE_CREATE_INFO_KHR, nullptr, 0,
diff --git a/src/video_core/renderer_vulkan/shaders/quad_indexed.comp b/src/video_core/renderer_vulkan/shaders/quad_indexed.comp
new file mode 100644
index 000000000..5a472ba9b
--- /dev/null
+++ b/src/video_core/renderer_vulkan/shaders/quad_indexed.comp
@@ -0,0 +1,50 @@
1// Copyright 2020 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5/*
6 * Build instructions:
7 * $ glslangValidator -V quad_indexed.comp -o output.spv
8 * $ spirv-opt -O --strip-debug output.spv -o optimized.spv
9 * $ xxd -i optimized.spv
10 *
11 * Then copy that bytecode to the C++ file
12 */
13
14#version 460 core
15
16layout (local_size_x = 1024) in;
17
18layout (std430, set = 0, binding = 0) readonly buffer InputBuffer {
19 uint input_indexes[];
20};
21
22layout (std430, set = 0, binding = 1) writeonly buffer OutputBuffer {
23 uint output_indexes[];
24};
25
26layout (push_constant) uniform PushConstants {
27 uint base_vertex;
28 int index_shift; // 0: uint8, 1: uint16, 2: uint32
29};
30
31void main() {
32 int primitive = int(gl_GlobalInvocationID.x);
33 if (primitive * 6 >= output_indexes.length()) {
34 return;
35 }
36
37 int index_size = 8 << index_shift;
38 int flipped_shift = 2 - index_shift;
39 int mask = (1 << flipped_shift) - 1;
40
41 const int quad_swizzle[6] = int[](0, 1, 2, 0, 2, 3);
42 for (uint vertex = 0; vertex < 6; ++vertex) {
43 int offset = primitive * 4 + quad_swizzle[vertex];
44 int int_offset = offset >> flipped_shift;
45 int bit_offset = (offset & mask) * index_size;
46 uint packed_input = input_indexes[int_offset];
47 uint index = bitfieldExtract(packed_input, bit_offset, index_size);
48 output_indexes[primitive * 6 + vertex] = index + base_vertex;
49 }
50}
diff --git a/src/video_core/renderer_vulkan/vk_compute_pass.cpp b/src/video_core/renderer_vulkan/vk_compute_pass.cpp
index 878a78755..7b0268033 100644
--- a/src/video_core/renderer_vulkan/vk_compute_pass.cpp
+++ b/src/video_core/renderer_vulkan/vk_compute_pass.cpp
@@ -135,11 +135,11 @@ VkDescriptorUpdateTemplateEntryKHR BuildQuadArrayPassDescriptorUpdateTemplateEnt
135 return entry; 135 return entry;
136} 136}
137 137
138VkPushConstantRange BuildQuadArrayPassPushConstantRange() { 138VkPushConstantRange BuildComputePushConstantRange(std::size_t size) {
139 VkPushConstantRange range; 139 VkPushConstantRange range;
140 range.stageFlags = VK_SHADER_STAGE_COMPUTE_BIT; 140 range.stageFlags = VK_SHADER_STAGE_COMPUTE_BIT;
141 range.offset = 0; 141 range.offset = 0;
142 range.size = sizeof(u32); 142 range.size = static_cast<u32>(size);
143 return range; 143 return range;
144} 144}
145 145
@@ -220,7 +220,130 @@ constexpr u8 uint8_pass[] = {
220 0xf9, 0x00, 0x02, 0x00, 0x1d, 0x00, 0x00, 0x00, 0xf8, 0x00, 0x02, 0x00, 0x1d, 0x00, 0x00, 0x00, 220 0xf9, 0x00, 0x02, 0x00, 0x1d, 0x00, 0x00, 0x00, 0xf8, 0x00, 0x02, 0x00, 0x1d, 0x00, 0x00, 0x00,
221 0xfd, 0x00, 0x01, 0x00, 0x38, 0x00, 0x01, 0x00}; 221 0xfd, 0x00, 0x01, 0x00, 0x38, 0x00, 0x01, 0x00};
222 222
223std::array<VkDescriptorSetLayoutBinding, 2> BuildUint8PassDescriptorSetBindings() { 223// Quad indexed SPIR-V module. Generated from the "shaders/" directory.
224constexpr u8 QUAD_INDEXED_SPV[] = {
225 0x03, 0x02, 0x23, 0x07, 0x00, 0x00, 0x01, 0x00, 0x07, 0x00, 0x08, 0x00, 0x7c, 0x00, 0x00, 0x00,
226 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x06, 0x00,
227 0x01, 0x00, 0x00, 0x00, 0x47, 0x4c, 0x53, 0x4c, 0x2e, 0x73, 0x74, 0x64, 0x2e, 0x34, 0x35, 0x30,
228 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
229 0x0f, 0x00, 0x06, 0x00, 0x05, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x6d, 0x61, 0x69, 0x6e,
230 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x10, 0x00, 0x06, 0x00, 0x04, 0x00, 0x00, 0x00,
231 0x11, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
232 0x47, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00,
233 0x47, 0x00, 0x04, 0x00, 0x15, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
234 0x48, 0x00, 0x04, 0x00, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00,
235 0x48, 0x00, 0x05, 0x00, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00,
236 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x03, 0x00, 0x16, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
237 0x47, 0x00, 0x04, 0x00, 0x18, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
238 0x47, 0x00, 0x04, 0x00, 0x18, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
239 0x48, 0x00, 0x05, 0x00, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00,
240 0x00, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, 0x22, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
241 0x23, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x47, 0x00, 0x03, 0x00, 0x22, 0x00, 0x00, 0x00,
242 0x02, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x56, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00,
243 0x04, 0x00, 0x00, 0x00, 0x48, 0x00, 0x04, 0x00, 0x57, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
244 0x18, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, 0x57, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
245 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x03, 0x00, 0x57, 0x00, 0x00, 0x00,
246 0x03, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x59, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00,
247 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x59, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00,
248 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x72, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00,
249 0x19, 0x00, 0x00, 0x00, 0x13, 0x00, 0x02, 0x00, 0x02, 0x00, 0x00, 0x00, 0x21, 0x00, 0x03, 0x00,
250 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x15, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00,
251 0x20, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00,
252 0x07, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x15, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00,
253 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x17, 0x00, 0x04, 0x00, 0x0a, 0x00, 0x00, 0x00,
254 0x09, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x0b, 0x00, 0x00, 0x00,
255 0x01, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, 0x0b, 0x00, 0x00, 0x00,
256 0x0c, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x2b, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00,
257 0x0d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00,
258 0x01, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x2b, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00,
259 0x13, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x1d, 0x00, 0x03, 0x00, 0x15, 0x00, 0x00, 0x00,
260 0x09, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x03, 0x00, 0x16, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00,
261 0x20, 0x00, 0x04, 0x00, 0x17, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00,
262 0x3b, 0x00, 0x04, 0x00, 0x17, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
263 0x14, 0x00, 0x02, 0x00, 0x1b, 0x00, 0x00, 0x00, 0x2b, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00,
264 0x21, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x04, 0x00, 0x22, 0x00, 0x00, 0x00,
265 0x09, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x23, 0x00, 0x00, 0x00,
266 0x09, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, 0x23, 0x00, 0x00, 0x00,
267 0x24, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x2b, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00,
268 0x25, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x26, 0x00, 0x00, 0x00,
269 0x09, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x2b, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00,
270 0x2b, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x2b, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00,
271 0x3b, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x2b, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00,
272 0x3f, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x04, 0x00, 0x41, 0x00, 0x00, 0x00,
273 0x06, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x00, 0x00, 0x2b, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00,
274 0x42, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2b, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00,
275 0x43, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x09, 0x00, 0x41, 0x00, 0x00, 0x00,
276 0x44, 0x00, 0x00, 0x00, 0x42, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00, 0x2b, 0x00, 0x00, 0x00,
277 0x42, 0x00, 0x00, 0x00, 0x2b, 0x00, 0x00, 0x00, 0x43, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00,
278 0x46, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x41, 0x00, 0x00, 0x00, 0x1d, 0x00, 0x03, 0x00,
279 0x56, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x03, 0x00, 0x57, 0x00, 0x00, 0x00,
280 0x56, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x58, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
281 0x57, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, 0x58, 0x00, 0x00, 0x00, 0x59, 0x00, 0x00, 0x00,
282 0x02, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x5b, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
283 0x09, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x69, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00,
284 0x09, 0x00, 0x00, 0x00, 0x2b, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00,
285 0x00, 0x04, 0x00, 0x00, 0x2b, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0x71, 0x00, 0x00, 0x00,
286 0x01, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x06, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x72, 0x00, 0x00, 0x00,
287 0x70, 0x00, 0x00, 0x00, 0x71, 0x00, 0x00, 0x00, 0x71, 0x00, 0x00, 0x00, 0x36, 0x00, 0x05, 0x00,
288 0x02, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
289 0xf8, 0x00, 0x02, 0x00, 0x05, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, 0x46, 0x00, 0x00, 0x00,
290 0x47, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0xf9, 0x00, 0x02, 0x00, 0x74, 0x00, 0x00, 0x00,
291 0xf8, 0x00, 0x02, 0x00, 0x74, 0x00, 0x00, 0x00, 0xf6, 0x00, 0x04, 0x00, 0x73, 0x00, 0x00, 0x00,
292 0x76, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0x00, 0x02, 0x00, 0x75, 0x00, 0x00, 0x00,
293 0xf8, 0x00, 0x02, 0x00, 0x75, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, 0x0e, 0x00, 0x00, 0x00,
294 0x0f, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00,
295 0x09, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x7c, 0x00, 0x04, 0x00,
296 0x06, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x84, 0x00, 0x05, 0x00,
297 0x06, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00,
298 0x44, 0x00, 0x05, 0x00, 0x09, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00,
299 0x00, 0x00, 0x00, 0x00, 0x7c, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x00,
300 0x19, 0x00, 0x00, 0x00, 0xaf, 0x00, 0x05, 0x00, 0x1b, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00,
301 0x14, 0x00, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x00, 0xf7, 0x00, 0x03, 0x00, 0x1e, 0x00, 0x00, 0x00,
302 0x00, 0x00, 0x00, 0x00, 0xfa, 0x00, 0x04, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x1d, 0x00, 0x00, 0x00,
303 0x1e, 0x00, 0x00, 0x00, 0xf8, 0x00, 0x02, 0x00, 0x1d, 0x00, 0x00, 0x00, 0xf9, 0x00, 0x02, 0x00,
304 0x73, 0x00, 0x00, 0x00, 0xf8, 0x00, 0x02, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00,
305 0x26, 0x00, 0x00, 0x00, 0x27, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00,
306 0x3d, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x27, 0x00, 0x00, 0x00,
307 0xc4, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x29, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00,
308 0x28, 0x00, 0x00, 0x00, 0x82, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x2e, 0x00, 0x00, 0x00,
309 0x2b, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0xc4, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00,
310 0x31, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00, 0x2e, 0x00, 0x00, 0x00, 0x82, 0x00, 0x05, 0x00,
311 0x06, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00, 0x00, 0x31, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00,
312 0xf9, 0x00, 0x02, 0x00, 0x35, 0x00, 0x00, 0x00, 0xf8, 0x00, 0x02, 0x00, 0x35, 0x00, 0x00, 0x00,
313 0xf5, 0x00, 0x07, 0x00, 0x09, 0x00, 0x00, 0x00, 0x7b, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00,
314 0x1e, 0x00, 0x00, 0x00, 0x6f, 0x00, 0x00, 0x00, 0x36, 0x00, 0x00, 0x00, 0xb0, 0x00, 0x05, 0x00,
315 0x1b, 0x00, 0x00, 0x00, 0x3c, 0x00, 0x00, 0x00, 0x7b, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x00, 0x00,
316 0xf6, 0x00, 0x04, 0x00, 0x37, 0x00, 0x00, 0x00, 0x36, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
317 0xfa, 0x00, 0x04, 0x00, 0x3c, 0x00, 0x00, 0x00, 0x36, 0x00, 0x00, 0x00, 0x37, 0x00, 0x00, 0x00,
318 0xf8, 0x00, 0x02, 0x00, 0x36, 0x00, 0x00, 0x00, 0x84, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00,
319 0x40, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x03, 0x00,
320 0x47, 0x00, 0x00, 0x00, 0x44, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, 0x07, 0x00, 0x00, 0x00,
321 0x48, 0x00, 0x00, 0x00, 0x47, 0x00, 0x00, 0x00, 0x7b, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00,
322 0x06, 0x00, 0x00, 0x00, 0x49, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0x80, 0x00, 0x05, 0x00,
323 0x06, 0x00, 0x00, 0x00, 0x4a, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x49, 0x00, 0x00, 0x00,
324 0xc3, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x4e, 0x00, 0x00, 0x00, 0x4a, 0x00, 0x00, 0x00,
325 0x2e, 0x00, 0x00, 0x00, 0xc7, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x52, 0x00, 0x00, 0x00,
326 0x4a, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00, 0x00, 0x84, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00,
327 0x54, 0x00, 0x00, 0x00, 0x52, 0x00, 0x00, 0x00, 0x29, 0x00, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00,
328 0x5b, 0x00, 0x00, 0x00, 0x5c, 0x00, 0x00, 0x00, 0x59, 0x00, 0x00, 0x00, 0x42, 0x00, 0x00, 0x00,
329 0x4e, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0x5d, 0x00, 0x00, 0x00,
330 0x5c, 0x00, 0x00, 0x00, 0xcb, 0x00, 0x06, 0x00, 0x09, 0x00, 0x00, 0x00, 0x62, 0x00, 0x00, 0x00,
331 0x5d, 0x00, 0x00, 0x00, 0x54, 0x00, 0x00, 0x00, 0x29, 0x00, 0x00, 0x00, 0x7c, 0x00, 0x04, 0x00,
332 0x09, 0x00, 0x00, 0x00, 0x65, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x80, 0x00, 0x05, 0x00,
333 0x09, 0x00, 0x00, 0x00, 0x67, 0x00, 0x00, 0x00, 0x65, 0x00, 0x00, 0x00, 0x7b, 0x00, 0x00, 0x00,
334 0x41, 0x00, 0x05, 0x00, 0x69, 0x00, 0x00, 0x00, 0x6a, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00,
335 0x42, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0x6b, 0x00, 0x00, 0x00,
336 0x6a, 0x00, 0x00, 0x00, 0x80, 0x00, 0x05, 0x00, 0x09, 0x00, 0x00, 0x00, 0x6c, 0x00, 0x00, 0x00,
337 0x62, 0x00, 0x00, 0x00, 0x6b, 0x00, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00, 0x5b, 0x00, 0x00, 0x00,
338 0x6d, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x42, 0x00, 0x00, 0x00, 0x67, 0x00, 0x00, 0x00,
339 0x3e, 0x00, 0x03, 0x00, 0x6d, 0x00, 0x00, 0x00, 0x6c, 0x00, 0x00, 0x00, 0x80, 0x00, 0x05, 0x00,
340 0x09, 0x00, 0x00, 0x00, 0x6f, 0x00, 0x00, 0x00, 0x7b, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00,
341 0xf9, 0x00, 0x02, 0x00, 0x35, 0x00, 0x00, 0x00, 0xf8, 0x00, 0x02, 0x00, 0x37, 0x00, 0x00, 0x00,
342 0xf9, 0x00, 0x02, 0x00, 0x73, 0x00, 0x00, 0x00, 0xf8, 0x00, 0x02, 0x00, 0x76, 0x00, 0x00, 0x00,
343 0xf9, 0x00, 0x02, 0x00, 0x74, 0x00, 0x00, 0x00, 0xf8, 0x00, 0x02, 0x00, 0x73, 0x00, 0x00, 0x00,
344 0xfd, 0x00, 0x01, 0x00, 0x38, 0x00, 0x01, 0x00};
345
346std::array<VkDescriptorSetLayoutBinding, 2> BuildInputOutputDescriptorSetBindings() {
224 std::array<VkDescriptorSetLayoutBinding, 2> bindings; 347 std::array<VkDescriptorSetLayoutBinding, 2> bindings;
225 bindings[0].binding = 0; 348 bindings[0].binding = 0;
226 bindings[0].descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER; 349 bindings[0].descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;
@@ -235,7 +358,7 @@ std::array<VkDescriptorSetLayoutBinding, 2> BuildUint8PassDescriptorSetBindings(
235 return bindings; 358 return bindings;
236} 359}
237 360
238VkDescriptorUpdateTemplateEntryKHR BuildUint8PassDescriptorUpdateTemplateEntry() { 361VkDescriptorUpdateTemplateEntryKHR BuildInputOutputDescriptorUpdateTemplate() {
239 VkDescriptorUpdateTemplateEntryKHR entry; 362 VkDescriptorUpdateTemplateEntryKHR entry;
240 entry.dstBinding = 0; 363 entry.dstBinding = 0;
241 entry.dstArrayElement = 0; 364 entry.dstArrayElement = 0;
@@ -337,14 +460,14 @@ QuadArrayPass::QuadArrayPass(const VKDevice& device, VKScheduler& scheduler,
337 VKUpdateDescriptorQueue& update_descriptor_queue) 460 VKUpdateDescriptorQueue& update_descriptor_queue)
338 : VKComputePass(device, descriptor_pool, BuildQuadArrayPassDescriptorSetLayoutBinding(), 461 : VKComputePass(device, descriptor_pool, BuildQuadArrayPassDescriptorSetLayoutBinding(),
339 BuildQuadArrayPassDescriptorUpdateTemplateEntry(), 462 BuildQuadArrayPassDescriptorUpdateTemplateEntry(),
340 BuildQuadArrayPassPushConstantRange(), std::size(quad_array), quad_array), 463 BuildComputePushConstantRange(sizeof(u32)), std::size(quad_array), quad_array),
341 scheduler{scheduler}, staging_buffer_pool{staging_buffer_pool}, 464 scheduler{scheduler}, staging_buffer_pool{staging_buffer_pool},
342 update_descriptor_queue{update_descriptor_queue} {} 465 update_descriptor_queue{update_descriptor_queue} {}
343 466
344QuadArrayPass::~QuadArrayPass() = default; 467QuadArrayPass::~QuadArrayPass() = default;
345 468
346std::pair<VkBuffer, VkDeviceSize> QuadArrayPass::Assemble(u32 num_vertices, u32 first) { 469std::pair<VkBuffer, VkDeviceSize> QuadArrayPass::Assemble(u32 num_vertices, u32 first) {
347 const u32 num_triangle_vertices = num_vertices * 6 / 4; 470 const u32 num_triangle_vertices = (num_vertices / 4) * 6;
348 const std::size_t staging_size = num_triangle_vertices * sizeof(u32); 471 const std::size_t staging_size = num_triangle_vertices * sizeof(u32);
349 auto& buffer = staging_buffer_pool.GetUnusedBuffer(staging_size, false); 472 auto& buffer = staging_buffer_pool.GetUnusedBuffer(staging_size, false);
350 473
@@ -383,8 +506,8 @@ std::pair<VkBuffer, VkDeviceSize> QuadArrayPass::Assemble(u32 num_vertices, u32
383Uint8Pass::Uint8Pass(const VKDevice& device, VKScheduler& scheduler, 506Uint8Pass::Uint8Pass(const VKDevice& device, VKScheduler& scheduler,
384 VKDescriptorPool& descriptor_pool, VKStagingBufferPool& staging_buffer_pool, 507 VKDescriptorPool& descriptor_pool, VKStagingBufferPool& staging_buffer_pool,
385 VKUpdateDescriptorQueue& update_descriptor_queue) 508 VKUpdateDescriptorQueue& update_descriptor_queue)
386 : VKComputePass(device, descriptor_pool, BuildUint8PassDescriptorSetBindings(), 509 : VKComputePass(device, descriptor_pool, BuildInputOutputDescriptorSetBindings(),
387 BuildUint8PassDescriptorUpdateTemplateEntry(), {}, std::size(uint8_pass), 510 BuildInputOutputDescriptorUpdateTemplate(), {}, std::size(uint8_pass),
388 uint8_pass), 511 uint8_pass),
389 scheduler{scheduler}, staging_buffer_pool{staging_buffer_pool}, 512 scheduler{scheduler}, staging_buffer_pool{staging_buffer_pool},
390 update_descriptor_queue{update_descriptor_queue} {} 513 update_descriptor_queue{update_descriptor_queue} {}
@@ -425,4 +548,70 @@ std::pair<VkBuffer, u64> Uint8Pass::Assemble(u32 num_vertices, VkBuffer src_buff
425 return {*buffer.handle, 0}; 548 return {*buffer.handle, 0};
426} 549}
427 550
551QuadIndexedPass::QuadIndexedPass(const VKDevice& device, VKScheduler& scheduler,
552 VKDescriptorPool& descriptor_pool,
553 VKStagingBufferPool& staging_buffer_pool,
554 VKUpdateDescriptorQueue& update_descriptor_queue)
555 : VKComputePass(device, descriptor_pool, BuildInputOutputDescriptorSetBindings(),
556 BuildInputOutputDescriptorUpdateTemplate(),
557 BuildComputePushConstantRange(sizeof(u32) * 2), std::size(QUAD_INDEXED_SPV),
558 QUAD_INDEXED_SPV),
559 scheduler{scheduler}, staging_buffer_pool{staging_buffer_pool},
560 update_descriptor_queue{update_descriptor_queue} {}
561
562QuadIndexedPass::~QuadIndexedPass() = default;
563
564std::pair<VkBuffer, u64> QuadIndexedPass::Assemble(
565 Tegra::Engines::Maxwell3D::Regs::IndexFormat index_format, u32 num_vertices, u32 base_vertex,
566 VkBuffer src_buffer, u64 src_offset) {
567 const u32 index_shift = [index_format] {
568 switch (index_format) {
569 case Tegra::Engines::Maxwell3D::Regs::IndexFormat::UnsignedByte:
570 return 0;
571 case Tegra::Engines::Maxwell3D::Regs::IndexFormat::UnsignedShort:
572 return 1;
573 case Tegra::Engines::Maxwell3D::Regs::IndexFormat::UnsignedInt:
574 return 2;
575 }
576 UNREACHABLE();
577 return 2;
578 }();
579 const u32 input_size = num_vertices << index_shift;
580 const u32 num_tri_vertices = (num_vertices / 4) * 6;
581
582 const std::size_t staging_size = num_tri_vertices * sizeof(u32);
583 auto& buffer = staging_buffer_pool.GetUnusedBuffer(staging_size, false);
584
585 update_descriptor_queue.Acquire();
586 update_descriptor_queue.AddBuffer(src_buffer, src_offset, input_size);
587 update_descriptor_queue.AddBuffer(*buffer.handle, 0, staging_size);
588 const auto set = CommitDescriptorSet(update_descriptor_queue, scheduler.GetFence());
589
590 scheduler.RequestOutsideRenderPassOperationContext();
591 scheduler.Record([layout = *layout, pipeline = *pipeline, buffer = *buffer.handle, set,
592 num_tri_vertices, base_vertex, index_shift](vk::CommandBuffer cmdbuf) {
593 static constexpr u32 dispatch_size = 1024;
594 const std::array push_constants = {base_vertex, index_shift};
595 cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_COMPUTE, pipeline);
596 cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_COMPUTE, layout, 0, set, {});
597 cmdbuf.PushConstants(layout, VK_SHADER_STAGE_COMPUTE_BIT, 0, sizeof(push_constants),
598 &push_constants);
599 cmdbuf.Dispatch(Common::AlignUp(num_tri_vertices, dispatch_size) / dispatch_size, 1, 1);
600
601 VkBufferMemoryBarrier barrier;
602 barrier.sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER;
603 barrier.pNext = nullptr;
604 barrier.srcAccessMask = VK_ACCESS_SHADER_WRITE_BIT;
605 barrier.dstAccessMask = VK_ACCESS_VERTEX_ATTRIBUTE_READ_BIT;
606 barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
607 barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
608 barrier.buffer = buffer;
609 barrier.offset = 0;
610 barrier.size = static_cast<VkDeviceSize>(num_tri_vertices * sizeof(u32));
611 cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT,
612 VK_PIPELINE_STAGE_VERTEX_INPUT_BIT, 0, {}, barrier, {});
613 });
614 return {*buffer.handle, 0};
615}
616
428} // namespace Vulkan 617} // namespace Vulkan
diff --git a/src/video_core/renderer_vulkan/vk_compute_pass.h b/src/video_core/renderer_vulkan/vk_compute_pass.h
index ec80c8683..26bf834de 100644
--- a/src/video_core/renderer_vulkan/vk_compute_pass.h
+++ b/src/video_core/renderer_vulkan/vk_compute_pass.h
@@ -8,6 +8,7 @@
8#include <utility> 8#include <utility>
9#include <vector> 9#include <vector>
10#include "common/common_types.h" 10#include "common/common_types.h"
11#include "video_core/engines/maxwell_3d.h"
11#include "video_core/renderer_vulkan/vk_descriptor_pool.h" 12#include "video_core/renderer_vulkan/vk_descriptor_pool.h"
12#include "video_core/renderer_vulkan/wrapper.h" 13#include "video_core/renderer_vulkan/wrapper.h"
13 14
@@ -73,4 +74,22 @@ private:
73 VKUpdateDescriptorQueue& update_descriptor_queue; 74 VKUpdateDescriptorQueue& update_descriptor_queue;
74}; 75};
75 76
77class QuadIndexedPass final : public VKComputePass {
78public:
79 explicit QuadIndexedPass(const VKDevice& device, VKScheduler& scheduler,
80 VKDescriptorPool& descriptor_pool,
81 VKStagingBufferPool& staging_buffer_pool,
82 VKUpdateDescriptorQueue& update_descriptor_queue);
83 ~QuadIndexedPass();
84
85 std::pair<VkBuffer, u64> Assemble(Tegra::Engines::Maxwell3D::Regs::IndexFormat index_format,
86 u32 num_vertices, u32 base_vertex, VkBuffer src_buffer,
87 u64 src_offset);
88
89private:
90 VKScheduler& scheduler;
91 VKStagingBufferPool& staging_buffer_pool;
92 VKUpdateDescriptorQueue& update_descriptor_queue;
93};
94
76} // namespace Vulkan 95} // namespace Vulkan
diff --git a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp
index b540b838d..343999cf5 100644
--- a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp
+++ b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp
@@ -26,12 +26,13 @@ MICROPROFILE_DECLARE(Vulkan_PipelineCache);
26 26
27namespace { 27namespace {
28 28
29VkStencilOpState GetStencilFaceState(const FixedPipelineState::StencilFace& face) { 29template <class StencilFace>
30VkStencilOpState GetStencilFaceState(const StencilFace& face) {
30 VkStencilOpState state; 31 VkStencilOpState state;
31 state.failOp = MaxwellToVK::StencilOp(face.action_stencil_fail); 32 state.failOp = MaxwellToVK::StencilOp(face.ActionStencilFail());
32 state.passOp = MaxwellToVK::StencilOp(face.action_depth_pass); 33 state.passOp = MaxwellToVK::StencilOp(face.ActionDepthPass());
33 state.depthFailOp = MaxwellToVK::StencilOp(face.action_depth_fail); 34 state.depthFailOp = MaxwellToVK::StencilOp(face.ActionDepthFail());
34 state.compareOp = MaxwellToVK::ComparisonOp(face.test_func); 35 state.compareOp = MaxwellToVK::ComparisonOp(face.TestFunc());
35 state.compareMask = 0; 36 state.compareMask = 0;
36 state.writeMask = 0; 37 state.writeMask = 0;
37 state.reference = 0; 38 state.reference = 0;
@@ -157,43 +158,47 @@ std::vector<vk::ShaderModule> VKGraphicsPipeline::CreateShaderModules(
157vk::Pipeline VKGraphicsPipeline::CreatePipeline(const RenderPassParams& renderpass_params, 158vk::Pipeline VKGraphicsPipeline::CreatePipeline(const RenderPassParams& renderpass_params,
158 const SPIRVProgram& program) const { 159 const SPIRVProgram& program) const {
159 const auto& vi = fixed_state.vertex_input; 160 const auto& vi = fixed_state.vertex_input;
160 const auto& ia = fixed_state.input_assembly;
161 const auto& ds = fixed_state.depth_stencil; 161 const auto& ds = fixed_state.depth_stencil;
162 const auto& cd = fixed_state.color_blending; 162 const auto& cd = fixed_state.color_blending;
163 const auto& ts = fixed_state.tessellation;
164 const auto& rs = fixed_state.rasterizer; 163 const auto& rs = fixed_state.rasterizer;
165 164
166 std::vector<VkVertexInputBindingDescription> vertex_bindings; 165 std::vector<VkVertexInputBindingDescription> vertex_bindings;
167 std::vector<VkVertexInputBindingDivisorDescriptionEXT> vertex_binding_divisors; 166 std::vector<VkVertexInputBindingDivisorDescriptionEXT> vertex_binding_divisors;
168 for (std::size_t i = 0; i < vi.num_bindings; ++i) { 167 for (std::size_t index = 0; index < std::size(vi.bindings); ++index) {
169 const auto& binding = vi.bindings[i]; 168 const auto& binding = vi.bindings[index];
170 const bool instanced = binding.divisor != 0; 169 if (!binding.enabled) {
170 continue;
171 }
172 const bool instanced = vi.binding_divisors[index] != 0;
171 const auto rate = instanced ? VK_VERTEX_INPUT_RATE_INSTANCE : VK_VERTEX_INPUT_RATE_VERTEX; 173 const auto rate = instanced ? VK_VERTEX_INPUT_RATE_INSTANCE : VK_VERTEX_INPUT_RATE_VERTEX;
172 174
173 auto& vertex_binding = vertex_bindings.emplace_back(); 175 auto& vertex_binding = vertex_bindings.emplace_back();
174 vertex_binding.binding = binding.index; 176 vertex_binding.binding = static_cast<u32>(index);
175 vertex_binding.stride = binding.stride; 177 vertex_binding.stride = binding.stride;
176 vertex_binding.inputRate = rate; 178 vertex_binding.inputRate = rate;
177 179
178 if (instanced) { 180 if (instanced) {
179 auto& binding_divisor = vertex_binding_divisors.emplace_back(); 181 auto& binding_divisor = vertex_binding_divisors.emplace_back();
180 binding_divisor.binding = binding.index; 182 binding_divisor.binding = static_cast<u32>(index);
181 binding_divisor.divisor = binding.divisor; 183 binding_divisor.divisor = vi.binding_divisors[index];
182 } 184 }
183 } 185 }
184 186
185 std::vector<VkVertexInputAttributeDescription> vertex_attributes; 187 std::vector<VkVertexInputAttributeDescription> vertex_attributes;
186 const auto& input_attributes = program[0]->entries.attributes; 188 const auto& input_attributes = program[0]->entries.attributes;
187 for (std::size_t i = 0; i < vi.num_attributes; ++i) { 189 for (std::size_t index = 0; index < std::size(vi.attributes); ++index) {
188 const auto& attribute = vi.attributes[i]; 190 const auto& attribute = vi.attributes[index];
189 if (input_attributes.find(attribute.index) == input_attributes.end()) { 191 if (!attribute.enabled) {
192 continue;
193 }
194 if (input_attributes.find(static_cast<u32>(index)) == input_attributes.end()) {
190 // Skip attributes not used by the vertex shaders. 195 // Skip attributes not used by the vertex shaders.
191 continue; 196 continue;
192 } 197 }
193 auto& vertex_attribute = vertex_attributes.emplace_back(); 198 auto& vertex_attribute = vertex_attributes.emplace_back();
194 vertex_attribute.location = attribute.index; 199 vertex_attribute.location = static_cast<u32>(index);
195 vertex_attribute.binding = attribute.buffer; 200 vertex_attribute.binding = attribute.buffer;
196 vertex_attribute.format = MaxwellToVK::VertexFormat(attribute.type, attribute.size); 201 vertex_attribute.format = MaxwellToVK::VertexFormat(attribute.Type(), attribute.Size());
197 vertex_attribute.offset = attribute.offset; 202 vertex_attribute.offset = attribute.offset;
198 } 203 }
199 204
@@ -219,15 +224,15 @@ vk::Pipeline VKGraphicsPipeline::CreatePipeline(const RenderPassParams& renderpa
219 input_assembly_ci.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO; 224 input_assembly_ci.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO;
220 input_assembly_ci.pNext = nullptr; 225 input_assembly_ci.pNext = nullptr;
221 input_assembly_ci.flags = 0; 226 input_assembly_ci.flags = 0;
222 input_assembly_ci.topology = MaxwellToVK::PrimitiveTopology(device, ia.topology); 227 input_assembly_ci.topology = MaxwellToVK::PrimitiveTopology(device, rs.Topology());
223 input_assembly_ci.primitiveRestartEnable = 228 input_assembly_ci.primitiveRestartEnable =
224 ia.primitive_restart_enable && SupportsPrimitiveRestart(input_assembly_ci.topology); 229 rs.primitive_restart_enable != 0 && SupportsPrimitiveRestart(input_assembly_ci.topology);
225 230
226 VkPipelineTessellationStateCreateInfo tessellation_ci; 231 VkPipelineTessellationStateCreateInfo tessellation_ci;
227 tessellation_ci.sType = VK_STRUCTURE_TYPE_PIPELINE_TESSELLATION_STATE_CREATE_INFO; 232 tessellation_ci.sType = VK_STRUCTURE_TYPE_PIPELINE_TESSELLATION_STATE_CREATE_INFO;
228 tessellation_ci.pNext = nullptr; 233 tessellation_ci.pNext = nullptr;
229 tessellation_ci.flags = 0; 234 tessellation_ci.flags = 0;
230 tessellation_ci.patchControlPoints = ts.patch_control_points; 235 tessellation_ci.patchControlPoints = rs.patch_control_points_minus_one.Value() + 1;
231 236
232 VkPipelineViewportStateCreateInfo viewport_ci; 237 VkPipelineViewportStateCreateInfo viewport_ci;
233 viewport_ci.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO; 238 viewport_ci.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO;
@@ -246,8 +251,8 @@ vk::Pipeline VKGraphicsPipeline::CreatePipeline(const RenderPassParams& renderpa
246 rasterization_ci.rasterizerDiscardEnable = VK_FALSE; 251 rasterization_ci.rasterizerDiscardEnable = VK_FALSE;
247 rasterization_ci.polygonMode = VK_POLYGON_MODE_FILL; 252 rasterization_ci.polygonMode = VK_POLYGON_MODE_FILL;
248 rasterization_ci.cullMode = 253 rasterization_ci.cullMode =
249 rs.cull_enable ? MaxwellToVK::CullFace(rs.cull_face) : VK_CULL_MODE_NONE; 254 rs.cull_enable ? MaxwellToVK::CullFace(rs.CullFace()) : VK_CULL_MODE_NONE;
250 rasterization_ci.frontFace = MaxwellToVK::FrontFace(rs.front_face); 255 rasterization_ci.frontFace = MaxwellToVK::FrontFace(rs.FrontFace());
251 rasterization_ci.depthBiasEnable = rs.depth_bias_enable; 256 rasterization_ci.depthBiasEnable = rs.depth_bias_enable;
252 rasterization_ci.depthBiasConstantFactor = 0.0f; 257 rasterization_ci.depthBiasConstantFactor = 0.0f;
253 rasterization_ci.depthBiasClamp = 0.0f; 258 rasterization_ci.depthBiasClamp = 0.0f;
@@ -271,40 +276,38 @@ vk::Pipeline VKGraphicsPipeline::CreatePipeline(const RenderPassParams& renderpa
271 depth_stencil_ci.flags = 0; 276 depth_stencil_ci.flags = 0;
272 depth_stencil_ci.depthTestEnable = ds.depth_test_enable; 277 depth_stencil_ci.depthTestEnable = ds.depth_test_enable;
273 depth_stencil_ci.depthWriteEnable = ds.depth_write_enable; 278 depth_stencil_ci.depthWriteEnable = ds.depth_write_enable;
274 depth_stencil_ci.depthCompareOp = ds.depth_test_enable 279 depth_stencil_ci.depthCompareOp =
275 ? MaxwellToVK::ComparisonOp(ds.depth_test_function) 280 ds.depth_test_enable ? MaxwellToVK::ComparisonOp(ds.DepthTestFunc()) : VK_COMPARE_OP_ALWAYS;
276 : VK_COMPARE_OP_ALWAYS;
277 depth_stencil_ci.depthBoundsTestEnable = ds.depth_bounds_enable; 281 depth_stencil_ci.depthBoundsTestEnable = ds.depth_bounds_enable;
278 depth_stencil_ci.stencilTestEnable = ds.stencil_enable; 282 depth_stencil_ci.stencilTestEnable = ds.stencil_enable;
279 depth_stencil_ci.front = GetStencilFaceState(ds.front_stencil); 283 depth_stencil_ci.front = GetStencilFaceState(ds.front);
280 depth_stencil_ci.back = GetStencilFaceState(ds.back_stencil); 284 depth_stencil_ci.back = GetStencilFaceState(ds.back);
281 depth_stencil_ci.minDepthBounds = 0.0f; 285 depth_stencil_ci.minDepthBounds = 0.0f;
282 depth_stencil_ci.maxDepthBounds = 0.0f; 286 depth_stencil_ci.maxDepthBounds = 0.0f;
283 287
284 std::array<VkPipelineColorBlendAttachmentState, Maxwell::NumRenderTargets> cb_attachments; 288 std::array<VkPipelineColorBlendAttachmentState, Maxwell::NumRenderTargets> cb_attachments;
285 const std::size_t num_attachments = 289 const std::size_t num_attachments = renderpass_params.color_attachments.size();
286 std::min(cd.attachments_count, renderpass_params.color_attachments.size()); 290 for (std::size_t index = 0; index < num_attachments; ++index) {
287 for (std::size_t i = 0; i < num_attachments; ++i) { 291 static constexpr std::array COMPONENT_TABLE = {
288 static constexpr std::array component_table = {
289 VK_COLOR_COMPONENT_R_BIT, VK_COLOR_COMPONENT_G_BIT, VK_COLOR_COMPONENT_B_BIT, 292 VK_COLOR_COMPONENT_R_BIT, VK_COLOR_COMPONENT_G_BIT, VK_COLOR_COMPONENT_B_BIT,
290 VK_COLOR_COMPONENT_A_BIT}; 293 VK_COLOR_COMPONENT_A_BIT};
291 const auto& blend = cd.attachments[i]; 294 const auto& blend = cd.attachments[index];
292 295
293 VkColorComponentFlags color_components = 0; 296 VkColorComponentFlags color_components = 0;
294 for (std::size_t j = 0; j < component_table.size(); ++j) { 297 for (std::size_t i = 0; i < COMPONENT_TABLE.size(); ++i) {
295 if (blend.components[j]) { 298 if (blend.Mask()[i]) {
296 color_components |= component_table[j]; 299 color_components |= COMPONENT_TABLE[i];
297 } 300 }
298 } 301 }
299 302
300 VkPipelineColorBlendAttachmentState& attachment = cb_attachments[i]; 303 VkPipelineColorBlendAttachmentState& attachment = cb_attachments[index];
301 attachment.blendEnable = blend.enable; 304 attachment.blendEnable = blend.enable != 0;
302 attachment.srcColorBlendFactor = MaxwellToVK::BlendFactor(blend.src_rgb_func); 305 attachment.srcColorBlendFactor = MaxwellToVK::BlendFactor(blend.SourceRGBFactor());
303 attachment.dstColorBlendFactor = MaxwellToVK::BlendFactor(blend.dst_rgb_func); 306 attachment.dstColorBlendFactor = MaxwellToVK::BlendFactor(blend.DestRGBFactor());
304 attachment.colorBlendOp = MaxwellToVK::BlendEquation(blend.rgb_equation); 307 attachment.colorBlendOp = MaxwellToVK::BlendEquation(blend.EquationRGB());
305 attachment.srcAlphaBlendFactor = MaxwellToVK::BlendFactor(blend.src_a_func); 308 attachment.srcAlphaBlendFactor = MaxwellToVK::BlendFactor(blend.SourceAlphaFactor());
306 attachment.dstAlphaBlendFactor = MaxwellToVK::BlendFactor(blend.dst_a_func); 309 attachment.dstAlphaBlendFactor = MaxwellToVK::BlendFactor(blend.DestAlphaFactor());
307 attachment.alphaBlendOp = MaxwellToVK::BlendEquation(blend.a_equation); 310 attachment.alphaBlendOp = MaxwellToVK::BlendEquation(blend.EquationAlpha());
308 attachment.colorWriteMask = color_components; 311 attachment.colorWriteMask = color_components;
309 } 312 }
310 313
diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp
index 90e3a8edd..8fdc6400d 100644
--- a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp
+++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp
@@ -329,12 +329,12 @@ VKPipelineCache::DecompileShaders(const GraphicsPipelineCacheKey& key) {
329 const auto& gpu = system.GPU().Maxwell3D(); 329 const auto& gpu = system.GPU().Maxwell3D();
330 330
331 Specialization specialization; 331 Specialization specialization;
332 if (fixed_state.input_assembly.topology == Maxwell::PrimitiveTopology::Points) { 332 if (fixed_state.rasterizer.Topology() == Maxwell::PrimitiveTopology::Points) {
333 ASSERT(fixed_state.input_assembly.point_size != 0.0f); 333 ASSERT(fixed_state.rasterizer.point_size != 0);
334 specialization.point_size = fixed_state.input_assembly.point_size; 334 std::memcpy(&specialization.point_size, &fixed_state.rasterizer.point_size, sizeof(u32));
335 } 335 }
336 for (std::size_t i = 0; i < Maxwell::NumVertexAttributes; ++i) { 336 for (std::size_t i = 0; i < Maxwell::NumVertexAttributes; ++i) {
337 specialization.attribute_types[i] = fixed_state.vertex_input.attributes[i].type; 337 specialization.attribute_types[i] = fixed_state.vertex_input.attributes[i].Type();
338 } 338 }
339 specialization.ndc_minus_one_to_one = fixed_state.rasterizer.ndc_minus_one_to_one; 339 specialization.ndc_minus_one_to_one = fixed_state.rasterizer.ndc_minus_one_to_one;
340 340
diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp
index 4ca0febb8..71007bbe8 100644
--- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp
+++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp
@@ -292,6 +292,7 @@ RasterizerVulkan::RasterizerVulkan(Core::System& system, Core::Frontend::EmuWind
292 staging_pool(device, memory_manager, scheduler), descriptor_pool(device), 292 staging_pool(device, memory_manager, scheduler), descriptor_pool(device),
293 update_descriptor_queue(device, scheduler), renderpass_cache(device), 293 update_descriptor_queue(device, scheduler), renderpass_cache(device),
294 quad_array_pass(device, scheduler, descriptor_pool, staging_pool, update_descriptor_queue), 294 quad_array_pass(device, scheduler, descriptor_pool, staging_pool, update_descriptor_queue),
295 quad_indexed_pass(device, scheduler, descriptor_pool, staging_pool, update_descriptor_queue),
295 uint8_pass(device, scheduler, descriptor_pool, staging_pool, update_descriptor_queue), 296 uint8_pass(device, scheduler, descriptor_pool, staging_pool, update_descriptor_queue),
296 texture_cache(system, *this, device, resource_manager, memory_manager, scheduler, 297 texture_cache(system, *this, device, resource_manager, memory_manager, scheduler,
297 staging_pool), 298 staging_pool),
@@ -806,25 +807,29 @@ void RasterizerVulkan::SetupVertexArrays(FixedPipelineState::VertexInput& vertex
806 BufferBindings& buffer_bindings) { 807 BufferBindings& buffer_bindings) {
807 const auto& regs = system.GPU().Maxwell3D().regs; 808 const auto& regs = system.GPU().Maxwell3D().regs;
808 809
809 for (u32 index = 0; index < static_cast<u32>(Maxwell::NumVertexAttributes); ++index) { 810 for (std::size_t index = 0; index < Maxwell::NumVertexAttributes; ++index) {
810 const auto& attrib = regs.vertex_attrib_format[index]; 811 const auto& attrib = regs.vertex_attrib_format[index];
811 if (!attrib.IsValid()) { 812 if (!attrib.IsValid()) {
813 vertex_input.SetAttribute(index, false, 0, 0, {}, {});
812 continue; 814 continue;
813 } 815 }
814 816
815 const auto& buffer = regs.vertex_array[attrib.buffer]; 817 [[maybe_unused]] const auto& buffer = regs.vertex_array[attrib.buffer];
816 ASSERT(buffer.IsEnabled()); 818 ASSERT(buffer.IsEnabled());
817 819
818 vertex_input.attributes[vertex_input.num_attributes++] = 820 vertex_input.SetAttribute(index, true, attrib.buffer, attrib.offset, attrib.type.Value(),
819 FixedPipelineState::VertexAttribute(index, attrib.buffer, attrib.type, attrib.size, 821 attrib.size.Value());
820 attrib.offset);
821 } 822 }
822 823
823 for (u32 index = 0; index < static_cast<u32>(Maxwell::NumVertexArrays); ++index) { 824 for (std::size_t index = 0; index < Maxwell::NumVertexArrays; ++index) {
824 const auto& vertex_array = regs.vertex_array[index]; 825 const auto& vertex_array = regs.vertex_array[index];
825 if (!vertex_array.IsEnabled()) { 826 if (!vertex_array.IsEnabled()) {
827 vertex_input.SetBinding(index, false, 0, 0);
826 continue; 828 continue;
827 } 829 }
830 vertex_input.SetBinding(
831 index, true, vertex_array.stride,
832 regs.instanced_arrays.IsInstancingEnabled(index) ? vertex_array.divisor : 0);
828 833
829 const GPUVAddr start{vertex_array.StartAddress()}; 834 const GPUVAddr start{vertex_array.StartAddress()};
830 const GPUVAddr end{regs.vertex_array_limit[index].LimitAddress()}; 835 const GPUVAddr end{regs.vertex_array_limit[index].LimitAddress()};
@@ -832,10 +837,6 @@ void RasterizerVulkan::SetupVertexArrays(FixedPipelineState::VertexInput& vertex
832 ASSERT(end > start); 837 ASSERT(end > start);
833 const std::size_t size{end - start + 1}; 838 const std::size_t size{end - start + 1};
834 const auto [buffer, offset] = buffer_cache.UploadMemory(start, size); 839 const auto [buffer, offset] = buffer_cache.UploadMemory(start, size);
835
836 vertex_input.bindings[vertex_input.num_bindings++] = FixedPipelineState::VertexBinding(
837 index, vertex_array.stride,
838 regs.instanced_arrays.IsInstancingEnabled(index) ? vertex_array.divisor : 0);
839 buffer_bindings.AddVertexBinding(buffer, offset); 840 buffer_bindings.AddVertexBinding(buffer, offset);
840 } 841 }
841} 842}
@@ -844,18 +845,26 @@ void RasterizerVulkan::SetupIndexBuffer(BufferBindings& buffer_bindings, DrawPar
844 bool is_indexed) { 845 bool is_indexed) {
845 const auto& regs = system.GPU().Maxwell3D().regs; 846 const auto& regs = system.GPU().Maxwell3D().regs;
846 switch (regs.draw.topology) { 847 switch (regs.draw.topology) {
847 case Maxwell::PrimitiveTopology::Quads: 848 case Maxwell::PrimitiveTopology::Quads: {
848 if (params.is_indexed) { 849 if (!params.is_indexed) {
849 UNIMPLEMENTED();
850 } else {
851 const auto [buffer, offset] = 850 const auto [buffer, offset] =
852 quad_array_pass.Assemble(params.num_vertices, params.base_vertex); 851 quad_array_pass.Assemble(params.num_vertices, params.base_vertex);
853 buffer_bindings.SetIndexBinding(buffer, offset, VK_INDEX_TYPE_UINT32); 852 buffer_bindings.SetIndexBinding(buffer, offset, VK_INDEX_TYPE_UINT32);
854 params.base_vertex = 0; 853 params.base_vertex = 0;
855 params.num_vertices = params.num_vertices * 6 / 4; 854 params.num_vertices = params.num_vertices * 6 / 4;
856 params.is_indexed = true; 855 params.is_indexed = true;
856 break;
857 } 857 }
858 const GPUVAddr gpu_addr = regs.index_array.IndexStart();
859 auto [buffer, offset] = buffer_cache.UploadMemory(gpu_addr, CalculateIndexBufferSize());
860 std::tie(buffer, offset) = quad_indexed_pass.Assemble(
861 regs.index_array.format, params.num_vertices, params.base_vertex, buffer, offset);
862
863 buffer_bindings.SetIndexBinding(buffer, offset, VK_INDEX_TYPE_UINT32);
864 params.num_vertices = (params.num_vertices / 4) * 6;
865 params.base_vertex = 0;
858 break; 866 break;
867 }
859 default: { 868 default: {
860 if (!is_indexed) { 869 if (!is_indexed) {
861 break; 870 break;
diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.h b/src/video_core/renderer_vulkan/vk_rasterizer.h
index 46037860a..d9108f862 100644
--- a/src/video_core/renderer_vulkan/vk_rasterizer.h
+++ b/src/video_core/renderer_vulkan/vk_rasterizer.h
@@ -254,6 +254,7 @@ private:
254 VKUpdateDescriptorQueue update_descriptor_queue; 254 VKUpdateDescriptorQueue update_descriptor_queue;
255 VKRenderPassCache renderpass_cache; 255 VKRenderPassCache renderpass_cache;
256 QuadArrayPass quad_array_pass; 256 QuadArrayPass quad_array_pass;
257 QuadIndexedPass quad_indexed_pass;
257 Uint8Pass uint8_pass; 258 Uint8Pass uint8_pass;
258 259
259 VKTextureCache texture_cache; 260 VKTextureCache texture_cache;
diff --git a/src/video_core/renderer_vulkan/vk_stream_buffer.cpp b/src/video_core/renderer_vulkan/vk_stream_buffer.cpp
index 38a93a01a..868447af2 100644
--- a/src/video_core/renderer_vulkan/vk_stream_buffer.cpp
+++ b/src/video_core/renderer_vulkan/vk_stream_buffer.cpp
@@ -3,6 +3,7 @@
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <algorithm> 5#include <algorithm>
6#include <limits>
6#include <optional> 7#include <optional>
7#include <tuple> 8#include <tuple>
8#include <vector> 9#include <vector>
@@ -22,22 +23,38 @@ namespace {
22constexpr u64 WATCHES_INITIAL_RESERVE = 0x4000; 23constexpr u64 WATCHES_INITIAL_RESERVE = 0x4000;
23constexpr u64 WATCHES_RESERVE_CHUNK = 0x1000; 24constexpr u64 WATCHES_RESERVE_CHUNK = 0x1000;
24 25
25constexpr u64 STREAM_BUFFER_SIZE = 256 * 1024 * 1024; 26constexpr u64 PREFERRED_STREAM_BUFFER_SIZE = 256 * 1024 * 1024;
26 27
27std::optional<u32> FindMemoryType(const VKDevice& device, u32 filter, 28/// Find a memory type with the passed requirements
28 VkMemoryPropertyFlags wanted) { 29std::optional<u32> FindMemoryType(const VkPhysicalDeviceMemoryProperties& properties,
29 const auto properties = device.GetPhysical().GetMemoryProperties(); 30 VkMemoryPropertyFlags wanted,
30 for (u32 i = 0; i < properties.memoryTypeCount; i++) { 31 u32 filter = std::numeric_limits<u32>::max()) {
31 if (!(filter & (1 << i))) { 32 for (u32 i = 0; i < properties.memoryTypeCount; ++i) {
32 continue; 33 const auto flags = properties.memoryTypes[i].propertyFlags;
33 } 34 if ((flags & wanted) == wanted && (filter & (1U << i)) != 0) {
34 if ((properties.memoryTypes[i].propertyFlags & wanted) == wanted) {
35 return i; 35 return i;
36 } 36 }
37 } 37 }
38 return std::nullopt; 38 return std::nullopt;
39} 39}
40 40
41/// Get the preferred host visible memory type.
42u32 GetMemoryType(const VkPhysicalDeviceMemoryProperties& properties,
43 u32 filter = std::numeric_limits<u32>::max()) {
44 // Prefer device local host visible allocations. Both AMD and Nvidia now provide one.
45 // Otherwise search for a host visible allocation.
46 static constexpr auto HOST_MEMORY =
47 VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT;
48 static constexpr auto DYNAMIC_MEMORY = HOST_MEMORY | VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
49
50 std::optional preferred_type = FindMemoryType(properties, DYNAMIC_MEMORY);
51 if (!preferred_type) {
52 preferred_type = FindMemoryType(properties, HOST_MEMORY);
53 ASSERT_MSG(preferred_type, "No host visible and coherent memory type found");
54 }
55 return preferred_type.value_or(0);
56}
57
41} // Anonymous namespace 58} // Anonymous namespace
42 59
43VKStreamBuffer::VKStreamBuffer(const VKDevice& device, VKScheduler& scheduler, 60VKStreamBuffer::VKStreamBuffer(const VKDevice& device, VKScheduler& scheduler,
@@ -51,7 +68,7 @@ VKStreamBuffer::VKStreamBuffer(const VKDevice& device, VKScheduler& scheduler,
51VKStreamBuffer::~VKStreamBuffer() = default; 68VKStreamBuffer::~VKStreamBuffer() = default;
52 69
53std::tuple<u8*, u64, bool> VKStreamBuffer::Map(u64 size, u64 alignment) { 70std::tuple<u8*, u64, bool> VKStreamBuffer::Map(u64 size, u64 alignment) {
54 ASSERT(size <= STREAM_BUFFER_SIZE); 71 ASSERT(size <= stream_buffer_size);
55 mapped_size = size; 72 mapped_size = size;
56 73
57 if (alignment > 0) { 74 if (alignment > 0) {
@@ -61,7 +78,7 @@ std::tuple<u8*, u64, bool> VKStreamBuffer::Map(u64 size, u64 alignment) {
61 WaitPendingOperations(offset); 78 WaitPendingOperations(offset);
62 79
63 bool invalidated = false; 80 bool invalidated = false;
64 if (offset + size > STREAM_BUFFER_SIZE) { 81 if (offset + size > stream_buffer_size) {
65 // The buffer would overflow, save the amount of used watches and reset the state. 82 // The buffer would overflow, save the amount of used watches and reset the state.
66 invalidation_mark = current_watch_cursor; 83 invalidation_mark = current_watch_cursor;
67 current_watch_cursor = 0; 84 current_watch_cursor = 0;
@@ -98,40 +115,37 @@ void VKStreamBuffer::Unmap(u64 size) {
98} 115}
99 116
100void VKStreamBuffer::CreateBuffers(VkBufferUsageFlags usage) { 117void VKStreamBuffer::CreateBuffers(VkBufferUsageFlags usage) {
118 const auto memory_properties = device.GetPhysical().GetMemoryProperties();
119 const u32 preferred_type = GetMemoryType(memory_properties);
120 const u32 preferred_heap = memory_properties.memoryTypes[preferred_type].heapIndex;
121
122 // Substract from the preferred heap size some bytes to avoid getting out of memory.
123 const VkDeviceSize heap_size = memory_properties.memoryHeaps[preferred_heap].size;
124 const VkDeviceSize allocable_size = heap_size - 4 * 1024 * 1024;
125
101 VkBufferCreateInfo buffer_ci; 126 VkBufferCreateInfo buffer_ci;
102 buffer_ci.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; 127 buffer_ci.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
103 buffer_ci.pNext = nullptr; 128 buffer_ci.pNext = nullptr;
104 buffer_ci.flags = 0; 129 buffer_ci.flags = 0;
105 buffer_ci.size = STREAM_BUFFER_SIZE; 130 buffer_ci.size = std::min(PREFERRED_STREAM_BUFFER_SIZE, allocable_size);
106 buffer_ci.usage = usage; 131 buffer_ci.usage = usage;
107 buffer_ci.sharingMode = VK_SHARING_MODE_EXCLUSIVE; 132 buffer_ci.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
108 buffer_ci.queueFamilyIndexCount = 0; 133 buffer_ci.queueFamilyIndexCount = 0;
109 buffer_ci.pQueueFamilyIndices = nullptr; 134 buffer_ci.pQueueFamilyIndices = nullptr;
110 135
111 const auto& dev = device.GetLogical(); 136 buffer = device.GetLogical().CreateBuffer(buffer_ci);
112 buffer = dev.CreateBuffer(buffer_ci); 137
113 138 const auto requirements = device.GetLogical().GetBufferMemoryRequirements(*buffer);
114 const auto& dld = device.GetDispatchLoader(); 139 const u32 required_flags = requirements.memoryTypeBits;
115 const auto requirements = dev.GetBufferMemoryRequirements(*buffer); 140 stream_buffer_size = static_cast<u64>(requirements.size);
116 // Prefer device local host visible allocations (this should hit AMD's pinned memory). 141
117 auto type =
118 FindMemoryType(device, requirements.memoryTypeBits,
119 VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT |
120 VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
121 if (!type) {
122 // Otherwise search for a host visible allocation.
123 type = FindMemoryType(device, requirements.memoryTypeBits,
124 VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT |
125 VK_MEMORY_PROPERTY_HOST_COHERENT_BIT);
126 ASSERT_MSG(type, "No host visible and coherent memory type found");
127 }
128 VkMemoryAllocateInfo memory_ai; 142 VkMemoryAllocateInfo memory_ai;
129 memory_ai.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; 143 memory_ai.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
130 memory_ai.pNext = nullptr; 144 memory_ai.pNext = nullptr;
131 memory_ai.allocationSize = requirements.size; 145 memory_ai.allocationSize = requirements.size;
132 memory_ai.memoryTypeIndex = *type; 146 memory_ai.memoryTypeIndex = GetMemoryType(memory_properties, required_flags);
133 147
134 memory = dev.AllocateMemory(memory_ai); 148 memory = device.GetLogical().AllocateMemory(memory_ai);
135 buffer.BindMemory(*memory, 0); 149 buffer.BindMemory(*memory, 0);
136} 150}
137 151
diff --git a/src/video_core/renderer_vulkan/vk_stream_buffer.h b/src/video_core/renderer_vulkan/vk_stream_buffer.h
index 58ce8b973..dfddf7ad6 100644
--- a/src/video_core/renderer_vulkan/vk_stream_buffer.h
+++ b/src/video_core/renderer_vulkan/vk_stream_buffer.h
@@ -56,8 +56,9 @@ private:
56 const VKDevice& device; ///< Vulkan device manager. 56 const VKDevice& device; ///< Vulkan device manager.
57 VKScheduler& scheduler; ///< Command scheduler. 57 VKScheduler& scheduler; ///< Command scheduler.
58 58
59 vk::Buffer buffer; ///< Mapped buffer. 59 vk::Buffer buffer; ///< Mapped buffer.
60 vk::DeviceMemory memory; ///< Memory allocation. 60 vk::DeviceMemory memory; ///< Memory allocation.
61 u64 stream_buffer_size{}; ///< Stream buffer size.
61 62
62 u64 offset{}; ///< Buffer iterator. 63 u64 offset{}; ///< Buffer iterator.
63 u64 mapped_size{}; ///< Size reserved for the current copy. 64 u64 mapped_size{}; ///< Size reserved for the current copy.
diff --git a/src/video_core/shader/decode/memory.cpp b/src/video_core/shader/decode/memory.cpp
index 8112ead3e..9392f065b 100644
--- a/src/video_core/shader/decode/memory.cpp
+++ b/src/video_core/shader/decode/memory.cpp
@@ -479,7 +479,7 @@ std::tuple<Node, Node, GlobalMemoryBase> ShaderIR::TrackGlobalMemory(NodeBlock&
479 bb.push_back(Comment(fmt::format("Base address is c[0x{:x}][0x{:x}]", index, offset))); 479 bb.push_back(Comment(fmt::format("Base address is c[0x{:x}][0x{:x}]", index, offset)));
480 480
481 const GlobalMemoryBase descriptor{index, offset}; 481 const GlobalMemoryBase descriptor{index, offset};
482 const auto& [entry, is_new] = used_global_memory.try_emplace(descriptor); 482 const auto& entry = used_global_memory.try_emplace(descriptor).first;
483 auto& usage = entry->second; 483 auto& usage = entry->second;
484 usage.is_written |= is_write; 484 usage.is_written |= is_write;
485 usage.is_read |= is_read; 485 usage.is_read |= is_read;
diff --git a/src/video_core/shader/decode/texture.cpp b/src/video_core/shader/decode/texture.cpp
index 6c4a1358b..e68f1d305 100644
--- a/src/video_core/shader/decode/texture.cpp
+++ b/src/video_core/shader/decode/texture.cpp
@@ -139,7 +139,7 @@ u32 ShaderIR::DecodeTexture(NodeBlock& bb, u32 pc) {
139 } 139 }
140 const Node component = Immediate(static_cast<u32>(instr.tld4s.component)); 140 const Node component = Immediate(static_cast<u32>(instr.tld4s.component));
141 141
142 const SamplerInfo info{TextureType::Texture2D, false, is_depth_compare}; 142 const SamplerInfo info{TextureType::Texture2D, false, is_depth_compare, false};
143 const Sampler& sampler = *GetSampler(instr.sampler, info); 143 const Sampler& sampler = *GetSampler(instr.sampler, info);
144 144
145 Node4 values; 145 Node4 values;
@@ -171,13 +171,12 @@ u32 ShaderIR::DecodeTexture(NodeBlock& bb, u32 pc) {
171 const auto coord_count = GetCoordCount(texture_type); 171 const auto coord_count = GetCoordCount(texture_type);
172 Node index_var{}; 172 Node index_var{};
173 const Sampler* sampler = 173 const Sampler* sampler =
174 is_bindless ? GetBindlessSampler(base_reg, index_var, {{texture_type, is_array, false}}) 174 is_bindless
175 : GetSampler(instr.sampler, {{texture_type, is_array, false}}); 175 ? GetBindlessSampler(base_reg, index_var, {{texture_type, is_array, false, false}})
176 : GetSampler(instr.sampler, {{texture_type, is_array, false, false}});
176 Node4 values; 177 Node4 values;
177 if (sampler == nullptr) { 178 if (sampler == nullptr) {
178 for (u32 element = 0; element < values.size(); ++element) { 179 std::generate(values.begin(), values.end(), [] { return Immediate(0); });
179 values[element] = Immediate(0);
180 }
181 WriteTexInstructionFloat(bb, instr, values); 180 WriteTexInstructionFloat(bb, instr, values);
182 break; 181 break;
183 } 182 }
@@ -269,7 +268,6 @@ u32 ShaderIR::DecodeTexture(NodeBlock& bb, u32 pc) {
269 "NDV is not implemented"); 268 "NDV is not implemented");
270 269
271 auto texture_type = instr.tmml.texture_type.Value(); 270 auto texture_type = instr.tmml.texture_type.Value();
272 const bool is_array = instr.tmml.array != 0;
273 Node index_var{}; 271 Node index_var{};
274 const Sampler* sampler = 272 const Sampler* sampler =
275 is_bindless ? GetBindlessSampler(instr.gpr20, index_var) : GetSampler(instr.sampler); 273 is_bindless ? GetBindlessSampler(instr.gpr20, index_var) : GetSampler(instr.sampler);
@@ -593,8 +591,9 @@ Node4 ShaderIR::GetTexCode(Instruction instr, TextureType texture_type,
593 ++parameter_register; 591 ++parameter_register;
594 } 592 }
595 593
596 const auto [coord_count, total_coord_count] = ValidateAndGetCoordinateElement( 594 const auto coord_counts = ValidateAndGetCoordinateElement(texture_type, depth_compare, is_array,
597 texture_type, depth_compare, is_array, lod_bias_enabled, 4, 5); 595 lod_bias_enabled, 4, 5);
596 const auto coord_count = std::get<0>(coord_counts);
598 // If enabled arrays index is always stored in the gpr8 field 597 // If enabled arrays index is always stored in the gpr8 field
599 const u64 array_register = instr.gpr8.Value(); 598 const u64 array_register = instr.gpr8.Value();
600 // First coordinate index is the gpr8 or gpr8 + 1 when arrays are used 599 // First coordinate index is the gpr8 or gpr8 + 1 when arrays are used
@@ -632,8 +631,10 @@ Node4 ShaderIR::GetTexsCode(Instruction instr, TextureType texture_type,
632 const bool lod_bias_enabled = 631 const bool lod_bias_enabled =
633 (process_mode != TextureProcessMode::None && process_mode != TextureProcessMode::LZ); 632 (process_mode != TextureProcessMode::None && process_mode != TextureProcessMode::LZ);
634 633
635 const auto [coord_count, total_coord_count] = ValidateAndGetCoordinateElement( 634 const auto coord_counts = ValidateAndGetCoordinateElement(texture_type, depth_compare, is_array,
636 texture_type, depth_compare, is_array, lod_bias_enabled, 4, 4); 635 lod_bias_enabled, 4, 4);
636 const auto coord_count = std::get<0>(coord_counts);
637
637 // If enabled arrays index is always stored in the gpr8 field 638 // If enabled arrays index is always stored in the gpr8 field
638 const u64 array_register = instr.gpr8.Value(); 639 const u64 array_register = instr.gpr8.Value();
639 // First coordinate index is stored in gpr8 field or (gpr8 + 1) when arrays are used 640 // First coordinate index is stored in gpr8 field or (gpr8 + 1) when arrays are used
diff --git a/src/video_core/shader/track.cpp b/src/video_core/shader/track.cpp
index 224943ad9..513e9bf49 100644
--- a/src/video_core/shader/track.cpp
+++ b/src/video_core/shader/track.cpp
@@ -76,12 +76,13 @@ std::tuple<Node, TrackSampler> ShaderIR::TrackBindlessSampler(Node tracked, cons
76 s64 cursor) { 76 s64 cursor) {
77 if (const auto cbuf = std::get_if<CbufNode>(&*tracked)) { 77 if (const auto cbuf = std::get_if<CbufNode>(&*tracked)) {
78 // Constant buffer found, test if it's an immediate 78 // Constant buffer found, test if it's an immediate
79 const auto offset = cbuf->GetOffset(); 79 const auto& offset = cbuf->GetOffset();
80 if (const auto immediate = std::get_if<ImmediateNode>(&*offset)) { 80 if (const auto immediate = std::get_if<ImmediateNode>(&*offset)) {
81 auto track = 81 auto track =
82 MakeTrackSampler<BindlessSamplerNode>(cbuf->GetIndex(), immediate->GetValue()); 82 MakeTrackSampler<BindlessSamplerNode>(cbuf->GetIndex(), immediate->GetValue());
83 return {tracked, track}; 83 return {tracked, track};
84 } else if (const auto operation = std::get_if<OperationNode>(&*offset)) { 84 }
85 if (const auto operation = std::get_if<OperationNode>(&*offset)) {
85 const u32 bound_buffer = registry.GetBoundBuffer(); 86 const u32 bound_buffer = registry.GetBoundBuffer();
86 if (bound_buffer != cbuf->GetIndex()) { 87 if (bound_buffer != cbuf->GetIndex()) {
87 return {}; 88 return {};
@@ -94,12 +95,12 @@ std::tuple<Node, TrackSampler> ShaderIR::TrackBindlessSampler(Node tracked, cons
94 const auto offset_inm = std::get_if<ImmediateNode>(&*base_offset); 95 const auto offset_inm = std::get_if<ImmediateNode>(&*base_offset);
95 const auto& gpu_driver = registry.AccessGuestDriverProfile(); 96 const auto& gpu_driver = registry.AccessGuestDriverProfile();
96 const u32 bindless_cv = NewCustomVariable(); 97 const u32 bindless_cv = NewCustomVariable();
97 const Node op = 98 Node op =
98 Operation(OperationCode::UDiv, gpr, Immediate(gpu_driver.GetTextureHandlerSize())); 99 Operation(OperationCode::UDiv, gpr, Immediate(gpu_driver.GetTextureHandlerSize()));
99 100
100 const Node cv_node = GetCustomVariable(bindless_cv); 101 const Node cv_node = GetCustomVariable(bindless_cv);
101 Node amend_op = Operation(OperationCode::Assign, cv_node, std::move(op)); 102 Node amend_op = Operation(OperationCode::Assign, cv_node, std::move(op));
102 const std::size_t amend_index = DeclareAmend(amend_op); 103 const std::size_t amend_index = DeclareAmend(std::move(amend_op));
103 AmendNodeCv(amend_index, code[cursor]); 104 AmendNodeCv(amend_index, code[cursor]);
104 // TODO Implement Bindless Index custom variable 105 // TODO Implement Bindless Index custom variable
105 auto track = MakeTrackSampler<ArraySamplerNode>(cbuf->GetIndex(), 106 auto track = MakeTrackSampler<ArraySamplerNode>(cbuf->GetIndex(),
@@ -142,7 +143,7 @@ std::tuple<Node, u32, u32> ShaderIR::TrackCbuf(Node tracked, const NodeBlock& co
142 s64 cursor) const { 143 s64 cursor) const {
143 if (const auto cbuf = std::get_if<CbufNode>(&*tracked)) { 144 if (const auto cbuf = std::get_if<CbufNode>(&*tracked)) {
144 // Constant buffer found, test if it's an immediate 145 // Constant buffer found, test if it's an immediate
145 const auto offset = cbuf->GetOffset(); 146 const auto& offset = cbuf->GetOffset();
146 if (const auto immediate = std::get_if<ImmediateNode>(&*offset)) { 147 if (const auto immediate = std::get_if<ImmediateNode>(&*offset)) {
147 return {tracked, cbuf->GetIndex(), immediate->GetValue()}; 148 return {tracked, cbuf->GetIndex(), immediate->GetValue()};
148 } 149 }
diff --git a/src/video_core/texture_cache/format_lookup_table.cpp b/src/video_core/texture_cache/format_lookup_table.cpp
index e151c26c4..25d2ee2e8 100644
--- a/src/video_core/texture_cache/format_lookup_table.cpp
+++ b/src/video_core/texture_cache/format_lookup_table.cpp
@@ -196,9 +196,9 @@ std::size_t FormatLookupTable::CalculateIndex(TextureFormat format, bool is_srgb
196 ComponentType alpha_component) noexcept { 196 ComponentType alpha_component) noexcept {
197 const auto format_index = static_cast<std::size_t>(format); 197 const auto format_index = static_cast<std::size_t>(format);
198 const auto red_index = static_cast<std::size_t>(red_component); 198 const auto red_index = static_cast<std::size_t>(red_component);
199 const auto green_index = static_cast<std::size_t>(red_component); 199 const auto green_index = static_cast<std::size_t>(green_component);
200 const auto blue_index = static_cast<std::size_t>(red_component); 200 const auto blue_index = static_cast<std::size_t>(blue_component);
201 const auto alpha_index = static_cast<std::size_t>(red_component); 201 const auto alpha_index = static_cast<std::size_t>(alpha_component);
202 const std::size_t srgb_index = is_srgb ? 1 : 0; 202 const std::size_t srgb_index = is_srgb ? 1 : 0;
203 203
204 return format_index * PerFormat + 204 return format_index * PerFormat +
diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp
index 3b9ab38dd..7f6dfac84 100644
--- a/src/yuzu/configuration/config.cpp
+++ b/src/yuzu/configuration/config.cpp
@@ -532,6 +532,8 @@ void Config::ReadDebuggingValues() {
532 Settings::values.reporting_services = 532 Settings::values.reporting_services =
533 ReadSetting(QStringLiteral("reporting_services"), false).toBool(); 533 ReadSetting(QStringLiteral("reporting_services"), false).toBool();
534 Settings::values.quest_flag = ReadSetting(QStringLiteral("quest_flag"), false).toBool(); 534 Settings::values.quest_flag = ReadSetting(QStringLiteral("quest_flag"), false).toBool();
535 Settings::values.disable_cpu_opt =
536 ReadSetting(QStringLiteral("disable_cpu_opt"), false).toBool();
535 537
536 qt_config->endGroup(); 538 qt_config->endGroup();
537} 539}
@@ -1001,6 +1003,7 @@ void Config::SaveDebuggingValues() {
1001 WriteSetting(QStringLiteral("dump_exefs"), Settings::values.dump_exefs, false); 1003 WriteSetting(QStringLiteral("dump_exefs"), Settings::values.dump_exefs, false);
1002 WriteSetting(QStringLiteral("dump_nso"), Settings::values.dump_nso, false); 1004 WriteSetting(QStringLiteral("dump_nso"), Settings::values.dump_nso, false);
1003 WriteSetting(QStringLiteral("quest_flag"), Settings::values.quest_flag, false); 1005 WriteSetting(QStringLiteral("quest_flag"), Settings::values.quest_flag, false);
1006 WriteSetting(QStringLiteral("disable_cpu_opt"), Settings::values.disable_cpu_opt, false);
1004 1007
1005 qt_config->endGroup(); 1008 qt_config->endGroup();
1006} 1009}
diff --git a/src/yuzu/configuration/configure_debug.cpp b/src/yuzu/configuration/configure_debug.cpp
index 9631059c7..c2026763e 100644
--- a/src/yuzu/configuration/configure_debug.cpp
+++ b/src/yuzu/configuration/configure_debug.cpp
@@ -36,6 +36,7 @@ void ConfigureDebug::SetConfiguration() {
36 ui->homebrew_args_edit->setText(QString::fromStdString(Settings::values.program_args)); 36 ui->homebrew_args_edit->setText(QString::fromStdString(Settings::values.program_args));
37 ui->reporting_services->setChecked(Settings::values.reporting_services); 37 ui->reporting_services->setChecked(Settings::values.reporting_services);
38 ui->quest_flag->setChecked(Settings::values.quest_flag); 38 ui->quest_flag->setChecked(Settings::values.quest_flag);
39 ui->disable_cpu_opt->setChecked(Settings::values.disable_cpu_opt);
39 ui->enable_graphics_debugging->setEnabled(!Core::System::GetInstance().IsPoweredOn()); 40 ui->enable_graphics_debugging->setEnabled(!Core::System::GetInstance().IsPoweredOn());
40 ui->enable_graphics_debugging->setChecked(Settings::values.renderer_debug); 41 ui->enable_graphics_debugging->setChecked(Settings::values.renderer_debug);
41} 42}
@@ -48,6 +49,7 @@ void ConfigureDebug::ApplyConfiguration() {
48 Settings::values.program_args = ui->homebrew_args_edit->text().toStdString(); 49 Settings::values.program_args = ui->homebrew_args_edit->text().toStdString();
49 Settings::values.reporting_services = ui->reporting_services->isChecked(); 50 Settings::values.reporting_services = ui->reporting_services->isChecked();
50 Settings::values.quest_flag = ui->quest_flag->isChecked(); 51 Settings::values.quest_flag = ui->quest_flag->isChecked();
52 Settings::values.disable_cpu_opt = ui->disable_cpu_opt->isChecked();
51 Settings::values.renderer_debug = ui->enable_graphics_debugging->isChecked(); 53 Settings::values.renderer_debug = ui->enable_graphics_debugging->isChecked();
52 Debugger::ToggleConsole(); 54 Debugger::ToggleConsole();
53 Log::Filter filter; 55 Log::Filter filter;
diff --git a/src/yuzu/configuration/configure_debug.ui b/src/yuzu/configuration/configure_debug.ui
index e028c4c80..e0d4c4a44 100644
--- a/src/yuzu/configuration/configure_debug.ui
+++ b/src/yuzu/configuration/configure_debug.ui
@@ -215,6 +215,13 @@
215 </property> 215 </property>
216 </widget> 216 </widget>
217 </item> 217 </item>
218 <item>
219 <widget class="QCheckBox" name="disable_cpu_opt">
220 <property name="text">
221 <string>Disable CPU JIT optimizations</string>
222 </property>
223 </widget>
224 </item>
218 </layout> 225 </layout>
219 </widget> 226 </widget>
220 </item> 227 </item>
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp
index 2c8eb481d..05baec7e1 100644
--- a/src/yuzu/main.cpp
+++ b/src/yuzu/main.cpp
@@ -802,10 +802,6 @@ void GMainWindow::ConnectMenuEvents() {
802 connect(ui.action_Load_Folder, &QAction::triggered, this, &GMainWindow::OnMenuLoadFolder); 802 connect(ui.action_Load_Folder, &QAction::triggered, this, &GMainWindow::OnMenuLoadFolder);
803 connect(ui.action_Install_File_NAND, &QAction::triggered, this, 803 connect(ui.action_Install_File_NAND, &QAction::triggered, this,
804 &GMainWindow::OnMenuInstallToNAND); 804 &GMainWindow::OnMenuInstallToNAND);
805 connect(ui.action_Select_NAND_Directory, &QAction::triggered, this,
806 [this] { OnMenuSelectEmulatedDirectory(EmulatedDirectoryTarget::NAND); });
807 connect(ui.action_Select_SDMC_Directory, &QAction::triggered, this,
808 [this] { OnMenuSelectEmulatedDirectory(EmulatedDirectoryTarget::SDMC); });
809 connect(ui.action_Exit, &QAction::triggered, this, &QMainWindow::close); 805 connect(ui.action_Exit, &QAction::triggered, this, &QMainWindow::close);
810 connect(ui.action_Load_Amiibo, &QAction::triggered, this, &GMainWindow::OnLoadAmiibo); 806 connect(ui.action_Load_Amiibo, &QAction::triggered, this, &GMainWindow::OnLoadAmiibo);
811 807
@@ -940,16 +936,18 @@ bool GMainWindow::LoadROM(const QString& filename) {
940 default: 936 default:
941 if (static_cast<u32>(result) > 937 if (static_cast<u32>(result) >
942 static_cast<u32>(Core::System::ResultStatus::ErrorLoader)) { 938 static_cast<u32>(Core::System::ResultStatus::ErrorLoader)) {
943 LOG_CRITICAL(Frontend, "Failed to load ROM!");
944 const u16 loader_id = static_cast<u16>(Core::System::ResultStatus::ErrorLoader); 939 const u16 loader_id = static_cast<u16>(Core::System::ResultStatus::ErrorLoader);
945 const u16 error_id = static_cast<u16>(result) - loader_id; 940 const u16 error_id = static_cast<u16>(result) - loader_id;
941 const std::string error_code = fmt::format("({:04X}-{:04X})", loader_id, error_id);
942 LOG_CRITICAL(Frontend, "Failed to load ROM! {}", error_code);
946 QMessageBox::critical( 943 QMessageBox::critical(
947 this, tr("Error while loading ROM!"), 944 this,
945 tr("Error while loading ROM! ").append(QString::fromStdString(error_code)),
948 QString::fromStdString(fmt::format( 946 QString::fromStdString(fmt::format(
949 "While attempting to load the ROM requested, an error occured. Please " 947 "{}<br>Please follow <a href='https://yuzu-emu.org/help/quickstart/'>the "
950 "refer to the yuzu wiki for more information or the yuzu discord for " 948 "yuzu quickstart guide</a> to redump your files.<br>You can refer "
951 "additional help.\n\nError Code: {:04X}-{:04X}\nError Description: {}", 949 "to the yuzu wiki</a> or the yuzu Discord</a> for help.",
952 loader_id, error_id, static_cast<Loader::ResultStatus>(error_id)))); 950 static_cast<Loader::ResultStatus>(error_id))));
953 } else { 951 } else {
954 QMessageBox::critical( 952 QMessageBox::critical(
955 this, tr("Error while loading ROM!"), 953 this, tr("Error while loading ROM!"),
@@ -1663,28 +1661,6 @@ void GMainWindow::OnMenuInstallToNAND() {
1663 } 1661 }
1664} 1662}
1665 1663
1666void GMainWindow::OnMenuSelectEmulatedDirectory(EmulatedDirectoryTarget target) {
1667 const auto res = QMessageBox::information(
1668 this, tr("Changing Emulated Directory"),
1669 tr("You are about to change the emulated %1 directory of the system. Please note "
1670 "that this does not also move the contents of the previous directory to the "
1671 "new one and you will have to do that yourself.")
1672 .arg(target == EmulatedDirectoryTarget::SDMC ? tr("SD card") : tr("NAND")),
1673 QMessageBox::StandardButtons{QMessageBox::Ok, QMessageBox::Cancel});
1674
1675 if (res == QMessageBox::Cancel)
1676 return;
1677
1678 QString dir_path = QFileDialog::getExistingDirectory(this, tr("Select Directory"));
1679 if (!dir_path.isEmpty()) {
1680 FileUtil::GetUserPath(target == EmulatedDirectoryTarget::SDMC ? FileUtil::UserPath::SDMCDir
1681 : FileUtil::UserPath::NANDDir,
1682 dir_path.toStdString());
1683 Core::System::GetInstance().GetFileSystemController().CreateFactories(*vfs);
1684 game_list->PopulateAsync(UISettings::values.game_dirs);
1685 }
1686}
1687
1688void GMainWindow::OnMenuRecentFile() { 1664void GMainWindow::OnMenuRecentFile() {
1689 QAction* action = qobject_cast<QAction*>(sender()); 1665 QAction* action = qobject_cast<QAction*>(sender());
1690 assert(action); 1666 assert(action);
@@ -2095,27 +2071,25 @@ void GMainWindow::OnReinitializeKeys(ReinitializeKeyBehavior behavior) {
2095 2071
2096 QString errors; 2072 QString errors;
2097 if (!pdm.HasFuses()) { 2073 if (!pdm.HasFuses()) {
2098 errors += tr("- Missing fuses - Cannot derive SBK\n"); 2074 errors += tr("Missing fuses");
2099 } 2075 }
2100 if (!pdm.HasBoot0()) { 2076 if (!pdm.HasBoot0()) {
2101 errors += tr("- Missing BOOT0 - Cannot derive master keys\n"); 2077 errors += tr(" - Missing BOOT0");
2102 } 2078 }
2103 if (!pdm.HasPackage2()) { 2079 if (!pdm.HasPackage2()) {
2104 errors += tr("- Missing BCPKG2-1-Normal-Main - Cannot derive general keys\n"); 2080 errors += tr(" - Missing BCPKG2-1-Normal-Main");
2105 } 2081 }
2106 if (!pdm.HasProdInfo()) { 2082 if (!pdm.HasProdInfo()) {
2107 errors += tr("- Missing PRODINFO - Cannot derive title keys\n"); 2083 errors += tr(" - Missing PRODINFO");
2108 } 2084 }
2109 if (!errors.isEmpty()) { 2085 if (!errors.isEmpty()) {
2110 QMessageBox::warning( 2086 QMessageBox::warning(
2111 this, tr("Warning Missing Derivation Components"), 2087 this, tr("Derivation Components Missing"),
2112 tr("The following are missing from your configuration that may hinder key " 2088 tr("Components are missing that may hinder key derivation from completing. "
2113 "derivation. It will be attempted but may not complete.<br><br>") + 2089 "<br>Please follow <a href='https://yuzu-emu.org/help/quickstart/'>the yuzu "
2114 errors + 2090 "quickstart guide</a> to get all your keys and "
2115 tr("<br><br>You can get all of these and dump all of your games easily by " 2091 "games.<br><br><small>(%1)</small>")
2116 "following <a href='https://yuzu-emu.org/help/quickstart/'>the " 2092 .arg(errors));
2117 "quickstart guide</a>. Alternatively, you can use another method of dumping "
2118 "to obtain all of your keys."));
2119 } 2093 }
2120 2094
2121 QProgressDialog prog; 2095 QProgressDialog prog;
diff --git a/src/yuzu/main.h b/src/yuzu/main.h
index a67125567..0b750689d 100644
--- a/src/yuzu/main.h
+++ b/src/yuzu/main.h
@@ -196,8 +196,6 @@ private slots:
196 void OnMenuLoadFile(); 196 void OnMenuLoadFile();
197 void OnMenuLoadFolder(); 197 void OnMenuLoadFolder();
198 void OnMenuInstallToNAND(); 198 void OnMenuInstallToNAND();
199 /// Called whenever a user select the "File->Select -- Directory" where -- is NAND or SD Card
200 void OnMenuSelectEmulatedDirectory(EmulatedDirectoryTarget target);
201 void OnMenuRecentFile(); 199 void OnMenuRecentFile();
202 void OnConfigure(); 200 void OnConfigure();
203 void OnLoadAmiibo(); 201 void OnLoadAmiibo();
diff --git a/src/yuzu/main.ui b/src/yuzu/main.ui
index a2c9e4547..ae414241e 100644
--- a/src/yuzu/main.ui
+++ b/src/yuzu/main.ui
@@ -64,8 +64,6 @@
64 <addaction name="separator"/> 64 <addaction name="separator"/>
65 <addaction name="menu_recent_files"/> 65 <addaction name="menu_recent_files"/>
66 <addaction name="separator"/> 66 <addaction name="separator"/>
67 <addaction name="action_Select_NAND_Directory"/>
68 <addaction name="action_Select_SDMC_Directory"/>
69 <addaction name="separator"/> 67 <addaction name="separator"/>
70 <addaction name="action_Load_Amiibo"/> 68 <addaction name="action_Load_Amiibo"/>
71 <addaction name="separator"/> 69 <addaction name="separator"/>
@@ -217,22 +215,6 @@
217 <string>Show Status Bar</string> 215 <string>Show Status Bar</string>
218 </property> 216 </property>
219 </action> 217 </action>
220 <action name="action_Select_NAND_Directory">
221 <property name="text">
222 <string>Select NAND Directory...</string>
223 </property>
224 <property name="toolTip">
225 <string>Selects a folder to use as the root of the emulated NAND</string>
226 </property>
227 </action>
228 <action name="action_Select_SDMC_Directory">
229 <property name="text">
230 <string>Select SD Card Directory...</string>
231 </property>
232 <property name="toolTip">
233 <string>Selects a folder to use as the root of the emulated SD card</string>
234 </property>
235 </action>
236 <action name="action_Fullscreen"> 218 <action name="action_Fullscreen">
237 <property name="checkable"> 219 <property name="checkable">
238 <bool>true</bool> 220 <bool>true</bool>
diff --git a/src/yuzu_cmd/config.cpp b/src/yuzu_cmd/config.cpp
index f4cd905c9..80341747f 100644
--- a/src/yuzu_cmd/config.cpp
+++ b/src/yuzu_cmd/config.cpp
@@ -425,6 +425,8 @@ void Config::ReadValues() {
425 Settings::values.reporting_services = 425 Settings::values.reporting_services =
426 sdl2_config->GetBoolean("Debugging", "reporting_services", false); 426 sdl2_config->GetBoolean("Debugging", "reporting_services", false);
427 Settings::values.quest_flag = sdl2_config->GetBoolean("Debugging", "quest_flag", false); 427 Settings::values.quest_flag = sdl2_config->GetBoolean("Debugging", "quest_flag", false);
428 Settings::values.disable_cpu_opt =
429 sdl2_config->GetBoolean("Debugging", "disable_cpu_opt", false);
428 430
429 const auto title_list = sdl2_config->Get("AddOns", "title_ids", ""); 431 const auto title_list = sdl2_config->Get("AddOns", "title_ids", "");
430 std::stringstream ss(title_list); 432 std::stringstream ss(title_list);
diff --git a/src/yuzu_cmd/default_ini.h b/src/yuzu_cmd/default_ini.h
index d63d7a58e..171d16fa0 100644
--- a/src/yuzu_cmd/default_ini.h
+++ b/src/yuzu_cmd/default_ini.h
@@ -280,6 +280,9 @@ dump_nso=false
280# Determines whether or not yuzu will report to the game that the emulated console is in Kiosk Mode 280# Determines whether or not yuzu will report to the game that the emulated console is in Kiosk Mode
281# false: Retail/Normal Mode (default), true: Kiosk Mode 281# false: Retail/Normal Mode (default), true: Kiosk Mode
282quest_flag = 282quest_flag =
283# Determines whether or not JIT CPU optimizations are enabled
284# false: Optimizations Enabled, true: Optimizations Disabled
285disable_cpu_opt =
283 286
284[WebService] 287[WebService]
285# Whether or not to enable telemetry 288# Whether or not to enable telemetry