summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-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/unicorn/arm_unicorn.cpp11
-rw-r--r--src/core/crypto/key_manager.cpp3
-rw-r--r--src/core/hle/service/audio/audren_u.cpp9
-rw-r--r--src/core/hle/service/time/time_zone_manager.cpp4
-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/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/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.cpp22
-rw-r--r--src/video_core/shader/decode/memory.cpp2
-rw-r--r--src/video_core/shader/decode/texture.cpp23
19 files changed, 813 insertions, 508 deletions
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/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/hle/service/audio/audren_u.cpp b/src/core/hle/service/audio/audren_u.cpp
index 07dd2caec..d8359abaa 100644
--- a/src/core/hle/service/audio/audren_u.cpp
+++ b/src/core/hle/service/audio/audren_u.cpp
@@ -94,9 +94,14 @@ private:
94 void RequestUpdateImpl(Kernel::HLERequestContext& ctx) { 94 void RequestUpdateImpl(Kernel::HLERequestContext& ctx) {
95 LOG_DEBUG(Service_Audio, "(STUBBED) called"); 95 LOG_DEBUG(Service_Audio, "(STUBBED) called");
96 96
97 ctx.WriteBuffer(renderer->UpdateAudioRenderer(ctx.ReadBuffer())); 97 auto result = renderer->UpdateAudioRenderer(ctx.ReadBuffer());
98
99 if (result.Succeeded()) {
100 ctx.WriteBuffer(result.Unwrap());
101 }
102
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) {
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/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/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/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 857bea19f..71007bbe8 100644
--- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp
+++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp
@@ -292,8 +292,8 @@ 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 uint8_pass(device, scheduler, descriptor_pool, staging_pool, update_descriptor_queue),
296 quad_indexed_pass(device, scheduler, descriptor_pool, staging_pool, update_descriptor_queue), 295 quad_indexed_pass(device, scheduler, descriptor_pool, staging_pool, update_descriptor_queue),
296 uint8_pass(device, scheduler, descriptor_pool, staging_pool, update_descriptor_queue),
297 texture_cache(system, *this, device, resource_manager, memory_manager, scheduler, 297 texture_cache(system, *this, device, resource_manager, memory_manager, scheduler,
298 staging_pool), 298 staging_pool),
299 pipeline_cache(system, *this, device, scheduler, descriptor_pool, update_descriptor_queue, 299 pipeline_cache(system, *this, device, scheduler, descriptor_pool, update_descriptor_queue,
@@ -807,25 +807,29 @@ void RasterizerVulkan::SetupVertexArrays(FixedPipelineState::VertexInput& vertex
807 BufferBindings& buffer_bindings) { 807 BufferBindings& buffer_bindings) {
808 const auto& regs = system.GPU().Maxwell3D().regs; 808 const auto& regs = system.GPU().Maxwell3D().regs;
809 809
810 for (u32 index = 0; index < static_cast<u32>(Maxwell::NumVertexAttributes); ++index) { 810 for (std::size_t index = 0; index < Maxwell::NumVertexAttributes; ++index) {
811 const auto& attrib = regs.vertex_attrib_format[index]; 811 const auto& attrib = regs.vertex_attrib_format[index];
812 if (!attrib.IsValid()) { 812 if (!attrib.IsValid()) {
813 vertex_input.SetAttribute(index, false, 0, 0, {}, {});
813 continue; 814 continue;
814 } 815 }
815 816
816 const auto& buffer = regs.vertex_array[attrib.buffer]; 817 [[maybe_unused]] const auto& buffer = regs.vertex_array[attrib.buffer];
817 ASSERT(buffer.IsEnabled()); 818 ASSERT(buffer.IsEnabled());
818 819
819 vertex_input.attributes[vertex_input.num_attributes++] = 820 vertex_input.SetAttribute(index, true, attrib.buffer, attrib.offset, attrib.type.Value(),
820 FixedPipelineState::VertexAttribute(index, attrib.buffer, attrib.type, attrib.size, 821 attrib.size.Value());
821 attrib.offset);
822 } 822 }
823 823
824 for (u32 index = 0; index < static_cast<u32>(Maxwell::NumVertexArrays); ++index) { 824 for (std::size_t index = 0; index < Maxwell::NumVertexArrays; ++index) {
825 const auto& vertex_array = regs.vertex_array[index]; 825 const auto& vertex_array = regs.vertex_array[index];
826 if (!vertex_array.IsEnabled()) { 826 if (!vertex_array.IsEnabled()) {
827 vertex_input.SetBinding(index, false, 0, 0);
827 continue; 828 continue;
828 } 829 }
830 vertex_input.SetBinding(
831 index, true, vertex_array.stride,
832 regs.instanced_arrays.IsInstancingEnabled(index) ? vertex_array.divisor : 0);
829 833
830 const GPUVAddr start{vertex_array.StartAddress()}; 834 const GPUVAddr start{vertex_array.StartAddress()};
831 const GPUVAddr end{regs.vertex_array_limit[index].LimitAddress()}; 835 const GPUVAddr end{regs.vertex_array_limit[index].LimitAddress()};
@@ -833,10 +837,6 @@ void RasterizerVulkan::SetupVertexArrays(FixedPipelineState::VertexInput& vertex
833 ASSERT(end > start); 837 ASSERT(end > start);
834 const std::size_t size{end - start + 1}; 838 const std::size_t size{end - start + 1};
835 const auto [buffer, offset] = buffer_cache.UploadMemory(start, size); 839 const auto [buffer, offset] = buffer_cache.UploadMemory(start, size);
836
837 vertex_input.bindings[vertex_input.num_bindings++] = FixedPipelineState::VertexBinding(
838 index, vertex_array.stride,
839 regs.instanced_arrays.IsInstancingEnabled(index) ? vertex_array.divisor : 0);
840 buffer_bindings.AddVertexBinding(buffer, offset); 840 buffer_bindings.AddVertexBinding(buffer, offset);
841 } 841 }
842} 842}
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