summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.github/ISSUE_TEMPLATE/bug-report-feature-request.md (renamed from .github/ISSUE_TEMPLATE.md)11
-rw-r--r--.github/ISSUE_TEMPLATE/config.yml8
m---------externals/Vulkan-Headers0
-rw-r--r--src/core/arm/cpu_interrupt_handler.cpp2
-rw-r--r--src/video_core/renderer_vulkan/fixed_pipeline_state.cpp115
-rw-r--r--src/video_core/renderer_vulkan/fixed_pipeline_state.h207
-rw-r--r--src/video_core/renderer_vulkan/vk_device.cpp26
-rw-r--r--src/video_core/renderer_vulkan/vk_device.h6
-rw-r--r--src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp89
-rw-r--r--src/video_core/renderer_vulkan/vk_pipeline_cache.cpp13
-rw-r--r--src/video_core/renderer_vulkan/vk_pipeline_cache.h8
-rw-r--r--src/video_core/renderer_vulkan/vk_rasterizer.cpp276
-rw-r--r--src/video_core/renderer_vulkan/vk_rasterizer.h13
-rw-r--r--src/video_core/renderer_vulkan/vk_state_tracker.cpp68
-rw-r--r--src/video_core/renderer_vulkan/vk_state_tracker.h50
-rw-r--r--src/video_core/renderer_vulkan/wrapper.cpp10
-rw-r--r--src/video_core/renderer_vulkan/wrapper.h54
-rw-r--r--src/yuzu/configuration/config.cpp12
-rw-r--r--src/yuzu/main.cpp2
19 files changed, 686 insertions, 284 deletions
diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE/bug-report-feature-request.md
index 70e1bba67..5706243bb 100644
--- a/.github/ISSUE_TEMPLATE.md
+++ b/.github/ISSUE_TEMPLATE/bug-report-feature-request.md
@@ -1,4 +1,13 @@
1<!-- 1---
2name: Bug Report / Feature Request
3about: Tech support does not belong here. You should only file an issue here if you think you have experienced an actual bug with yuzu or you are requesting a feature you believe would make yuzu better.
4title: ''
5labels: ''
6assignees: ''
7
8---
9
10<!---
2Please keep in mind yuzu is EXPERIMENTAL SOFTWARE. 11Please keep in mind yuzu is EXPERIMENTAL SOFTWARE.
3 12
4Please read the FAQ: 13Please read the FAQ:
diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml
new file mode 100644
index 000000000..52faafad3
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/config.yml
@@ -0,0 +1,8 @@
1blank_issues_enabled: false
2contact_links:
3 - name: yuzu Discord
4 url: https://discord.com/invite/u77vRWY
5 about: If you are experiencing an issue with yuzu, and you need tech support, or if you have a general question, try asking in the official yuzu Discord linked here. Piracy is not allowed.
6 - name: Community forums
7 url: https://community.citra-emu.org
8 about: This is an alternative place for tech support, however helpers there are not as active.
diff --git a/externals/Vulkan-Headers b/externals/Vulkan-Headers
Subproject 9250d5ae8f50202005233dc0512a1d460c8b483 Subproject 8188e3fbbc105591064093440f88081fb957d4f
diff --git a/src/core/arm/cpu_interrupt_handler.cpp b/src/core/arm/cpu_interrupt_handler.cpp
index 2f1a1a269..df0350881 100644
--- a/src/core/arm/cpu_interrupt_handler.cpp
+++ b/src/core/arm/cpu_interrupt_handler.cpp
@@ -2,8 +2,6 @@
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#pragma once
6
7#include "common/thread.h" 5#include "common/thread.h"
8#include "core/arm/cpu_interrupt_handler.h" 6#include "core/arm/cpu_interrupt_handler.h"
9 7
diff --git a/src/video_core/renderer_vulkan/fixed_pipeline_state.cpp b/src/video_core/renderer_vulkan/fixed_pipeline_state.cpp
index 424278816..d1f0ea932 100644
--- a/src/video_core/renderer_vulkan/fixed_pipeline_state.cpp
+++ b/src/video_core/renderer_vulkan/fixed_pipeline_state.cpp
@@ -39,52 +39,18 @@ constexpr std::array POLYGON_OFFSET_ENABLE_LUT = {
39 39
40} // Anonymous namespace 40} // Anonymous namespace
41 41
42void FixedPipelineState::DepthStencil::Fill(const Maxwell& regs) noexcept { 42void FixedPipelineState::Fill(const Maxwell& regs, bool has_extended_dynamic_state) {
43 raw = 0;
44 front.action_stencil_fail.Assign(PackStencilOp(regs.stencil_front_op_fail));
45 front.action_depth_fail.Assign(PackStencilOp(regs.stencil_front_op_zfail));
46 front.action_depth_pass.Assign(PackStencilOp(regs.stencil_front_op_zpass));
47 front.test_func.Assign(PackComparisonOp(regs.stencil_front_func_func));
48 if (regs.stencil_two_side_enable) {
49 back.action_stencil_fail.Assign(PackStencilOp(regs.stencil_back_op_fail));
50 back.action_depth_fail.Assign(PackStencilOp(regs.stencil_back_op_zfail));
51 back.action_depth_pass.Assign(PackStencilOp(regs.stencil_back_op_zpass));
52 back.test_func.Assign(PackComparisonOp(regs.stencil_back_func_func));
53 } else {
54 back.action_stencil_fail.Assign(front.action_stencil_fail);
55 back.action_depth_fail.Assign(front.action_depth_fail);
56 back.action_depth_pass.Assign(front.action_depth_pass);
57 back.test_func.Assign(front.test_func);
58 }
59 depth_test_enable.Assign(regs.depth_test_enable);
60 depth_write_enable.Assign(regs.depth_write_enabled);
61 depth_bounds_enable.Assign(regs.depth_bounds_enable);
62 stencil_enable.Assign(regs.stencil_enable);
63 depth_test_func.Assign(PackComparisonOp(regs.depth_test_func));
64}
65
66void FixedPipelineState::Rasterizer::Fill(const Maxwell& regs) noexcept {
67 const auto& clip = regs.view_volume_clip_control; 43 const auto& clip = regs.view_volume_clip_control;
68 const std::array enabled_lut = {regs.polygon_offset_point_enable, 44 const std::array enabled_lut = {regs.polygon_offset_point_enable,
69 regs.polygon_offset_line_enable, 45 regs.polygon_offset_line_enable,
70 regs.polygon_offset_fill_enable}; 46 regs.polygon_offset_fill_enable};
71 const u32 topology_index = static_cast<u32>(regs.draw.topology.Value()); 47 const u32 topology_index = static_cast<u32>(regs.draw.topology.Value());
72 48
73 u32 packed_front_face = PackFrontFace(regs.front_face);
74 if (regs.screen_y_control.triangle_rast_flip != 0) {
75 // Flip front face
76 packed_front_face = 1 - packed_front_face;
77 }
78
79 raw = 0; 49 raw = 0;
80 topology.Assign(topology_index);
81 primitive_restart_enable.Assign(regs.primitive_restart.enabled != 0 ? 1 : 0); 50 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); 51 depth_bias_enable.Assign(enabled_lut[POLYGON_OFFSET_ENABLE_LUT[topology_index]] != 0 ? 1 : 0);
84 depth_clamp_disabled.Assign(regs.view_volume_clip_control.depth_clamp_disabled.Value()); 52 depth_clamp_disabled.Assign(regs.view_volume_clip_control.depth_clamp_disabled.Value());
85 ndc_minus_one_to_one.Assign(regs.depth_mode == Maxwell::DepthMode::MinusOneToOne ? 1 : 0); 53 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)); 54 polygon_mode.Assign(PackPolygonMode(regs.polygon_mode_front));
89 patch_control_points_minus_one.Assign(regs.patch_vertices - 1); 55 patch_control_points_minus_one.Assign(regs.patch_vertices - 1);
90 tessellation_primitive.Assign(static_cast<u32>(regs.tess_mode.prim.Value())); 56 tessellation_primitive.Assign(static_cast<u32>(regs.tess_mode.prim.Value()));
@@ -93,19 +59,37 @@ void FixedPipelineState::Rasterizer::Fill(const Maxwell& regs) noexcept {
93 logic_op_enable.Assign(regs.logic_op.enable != 0 ? 1 : 0); 59 logic_op_enable.Assign(regs.logic_op.enable != 0 ? 1 : 0);
94 logic_op.Assign(PackLogicOp(regs.logic_op.operation)); 60 logic_op.Assign(PackLogicOp(regs.logic_op.operation));
95 rasterize_enable.Assign(regs.rasterize_enable != 0 ? 1 : 0); 61 rasterize_enable.Assign(regs.rasterize_enable != 0 ? 1 : 0);
62
96 std::memcpy(&point_size, &regs.point_size, sizeof(point_size)); // TODO: C++20 std::bit_cast 63 std::memcpy(&point_size, &regs.point_size, sizeof(point_size)); // TODO: C++20 std::bit_cast
97}
98 64
99void FixedPipelineState::ColorBlending::Fill(const Maxwell& regs) noexcept { 65 for (std::size_t index = 0; index < Maxwell::NumVertexArrays; ++index) {
66 binding_divisors[index] =
67 regs.instanced_arrays.IsInstancingEnabled(index) ? regs.vertex_array[index].divisor : 0;
68 }
69
70 for (std::size_t index = 0; index < Maxwell::NumVertexAttributes; ++index) {
71 const auto& input = regs.vertex_attrib_format[index];
72 auto& attribute = attributes[index];
73 attribute.raw = 0;
74 attribute.enabled.Assign(input.IsConstant() ? 0 : 1);
75 attribute.buffer.Assign(input.buffer);
76 attribute.offset.Assign(input.offset);
77 attribute.type.Assign(static_cast<u32>(input.type.Value()));
78 attribute.size.Assign(static_cast<u32>(input.size.Value()));
79 }
80
100 for (std::size_t index = 0; index < std::size(attachments); ++index) { 81 for (std::size_t index = 0; index < std::size(attachments); ++index) {
101 attachments[index].Fill(regs, index); 82 attachments[index].Fill(regs, index);
102 } 83 }
103}
104 84
105void FixedPipelineState::ViewportSwizzles::Fill(const Maxwell& regs) noexcept {
106 const auto& transform = regs.viewport_transform; 85 const auto& transform = regs.viewport_transform;
107 std::transform(transform.begin(), transform.end(), swizzles.begin(), 86 std::transform(transform.begin(), transform.end(), viewport_swizzles.begin(),
108 [](const auto& viewport) { return static_cast<u16>(viewport.swizzle.raw); }); 87 [](const auto& viewport) { return static_cast<u16>(viewport.swizzle.raw); });
88
89 if (!has_extended_dynamic_state) {
90 no_extended_dynamic_state.Assign(1);
91 dynamic_state.Fill(regs);
92 }
109} 93}
110 94
111void FixedPipelineState::BlendingAttachment::Fill(const Maxwell& regs, std::size_t index) { 95void FixedPipelineState::BlendingAttachment::Fill(const Maxwell& regs, std::size_t index) {
@@ -147,20 +131,57 @@ void FixedPipelineState::BlendingAttachment::Fill(const Maxwell& regs, std::size
147 enable.Assign(1); 131 enable.Assign(1);
148} 132}
149 133
150void FixedPipelineState::Fill(const Maxwell& regs) { 134void FixedPipelineState::DynamicState::Fill(const Maxwell& regs) {
151 rasterizer.Fill(regs); 135 const u32 topology_index = static_cast<u32>(regs.draw.topology.Value());
152 depth_stencil.Fill(regs); 136 u32 packed_front_face = PackFrontFace(regs.front_face);
153 color_blending.Fill(regs); 137 if (regs.screen_y_control.triangle_rast_flip != 0) {
154 viewport_swizzles.Fill(regs); 138 // Flip front face
139 packed_front_face = 1 - packed_front_face;
140 }
141
142 raw1 = 0;
143 raw2 = 0;
144 front.action_stencil_fail.Assign(PackStencilOp(regs.stencil_front_op_fail));
145 front.action_depth_fail.Assign(PackStencilOp(regs.stencil_front_op_zfail));
146 front.action_depth_pass.Assign(PackStencilOp(regs.stencil_front_op_zpass));
147 front.test_func.Assign(PackComparisonOp(regs.stencil_front_func_func));
148 if (regs.stencil_two_side_enable) {
149 back.action_stencil_fail.Assign(PackStencilOp(regs.stencil_back_op_fail));
150 back.action_depth_fail.Assign(PackStencilOp(regs.stencil_back_op_zfail));
151 back.action_depth_pass.Assign(PackStencilOp(regs.stencil_back_op_zpass));
152 back.test_func.Assign(PackComparisonOp(regs.stencil_back_func_func));
153 } else {
154 back.action_stencil_fail.Assign(front.action_stencil_fail);
155 back.action_depth_fail.Assign(front.action_depth_fail);
156 back.action_depth_pass.Assign(front.action_depth_pass);
157 back.test_func.Assign(front.test_func);
158 }
159 stencil_enable.Assign(regs.stencil_enable);
160 depth_write_enable.Assign(regs.depth_write_enabled);
161 depth_bounds_enable.Assign(regs.depth_bounds_enable);
162 depth_test_enable.Assign(regs.depth_test_enable);
163 front_face.Assign(packed_front_face);
164 depth_test_func.Assign(PackComparisonOp(regs.depth_test_func));
165 topology.Assign(topology_index);
166 cull_face.Assign(PackCullFace(regs.cull_face));
167 cull_enable.Assign(regs.cull_test_enabled != 0 ? 1 : 0);
168
169 for (std::size_t index = 0; index < Maxwell::NumVertexArrays; ++index) {
170 const auto& input = regs.vertex_array[index];
171 VertexBinding& binding = vertex_bindings[index];
172 binding.raw = 0;
173 binding.enabled.Assign(input.IsEnabled() ? 1 : 0);
174 binding.stride.Assign(static_cast<u16>(input.stride.Value()));
175 }
155} 176}
156 177
157std::size_t FixedPipelineState::Hash() const noexcept { 178std::size_t FixedPipelineState::Hash() const noexcept {
158 const u64 hash = Common::CityHash64(reinterpret_cast<const char*>(this), sizeof *this); 179 const u64 hash = Common::CityHash64(reinterpret_cast<const char*>(this), Size());
159 return static_cast<std::size_t>(hash); 180 return static_cast<std::size_t>(hash);
160} 181}
161 182
162bool FixedPipelineState::operator==(const FixedPipelineState& rhs) const noexcept { 183bool FixedPipelineState::operator==(const FixedPipelineState& rhs) const noexcept {
163 return std::memcmp(this, &rhs, sizeof *this) == 0; 184 return std::memcmp(this, &rhs, Size()) == 0;
164} 185}
165 186
166u32 FixedPipelineState::PackComparisonOp(Maxwell::ComparisonOp op) noexcept { 187u32 FixedPipelineState::PackComparisonOp(Maxwell::ComparisonOp op) noexcept {
diff --git a/src/video_core/renderer_vulkan/fixed_pipeline_state.h b/src/video_core/renderer_vulkan/fixed_pipeline_state.h
index 31a6398f2..cdcbb65f5 100644
--- a/src/video_core/renderer_vulkan/fixed_pipeline_state.h
+++ b/src/video_core/renderer_vulkan/fixed_pipeline_state.h
@@ -60,14 +60,6 @@ struct FixedPipelineState {
60 60
61 void Fill(const Maxwell& regs, std::size_t index); 61 void Fill(const Maxwell& regs, std::size_t index);
62 62
63 std::size_t Hash() const noexcept;
64
65 bool operator==(const BlendingAttachment& rhs) const noexcept;
66
67 bool operator!=(const BlendingAttachment& rhs) const noexcept {
68 return !operator==(rhs);
69 }
70
71 constexpr std::array<bool, 4> Mask() const noexcept { 63 constexpr std::array<bool, 4> Mask() const noexcept {
72 return {mask_r != 0, mask_g != 0, mask_b != 0, mask_a != 0}; 64 return {mask_r != 0, mask_g != 0, mask_b != 0, mask_a != 0};
73 } 65 }
@@ -97,156 +89,116 @@ struct FixedPipelineState {
97 } 89 }
98 }; 90 };
99 91
100 struct VertexInput { 92 union VertexAttribute {
101 union Binding { 93 u32 raw;
102 u16 raw; 94 BitField<0, 1, u32> enabled;
103 BitField<0, 1, u16> enabled; 95 BitField<1, 5, u32> buffer;
104 BitField<1, 12, u16> stride; 96 BitField<6, 14, u32> offset;
105 }; 97 BitField<20, 3, u32> type;
98 BitField<23, 6, u32> size;
106 99
107 union Attribute { 100 constexpr Maxwell::VertexAttribute::Type Type() const noexcept {
108 u32 raw; 101 return static_cast<Maxwell::VertexAttribute::Type>(type.Value());
109 BitField<0, 1, u32> enabled;
110 BitField<1, 5, u32> buffer;
111 BitField<6, 14, u32> offset;
112 BitField<20, 3, u32> type;
113 BitField<23, 6, u32> size;
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(static_cast<u16>(stride));
133 binding_divisors[index] = divisor;
134 } 102 }
135 103
136 void SetAttribute(std::size_t index, bool enabled, u32 buffer, u32 offset, 104 constexpr Maxwell::VertexAttribute::Size Size() const noexcept {
137 Maxwell::VertexAttribute::Type type, 105 return static_cast<Maxwell::VertexAttribute::Size>(size.Value());
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));
146 } 106 }
147 }; 107 };
148 108
149 struct Rasterizer { 109 template <std::size_t Position>
150 union { 110 union StencilFace {
151 u32 raw; 111 BitField<Position + 0, 3, u32> action_stencil_fail;
152 BitField<0, 4, u32> topology; 112 BitField<Position + 3, 3, u32> action_depth_fail;
153 BitField<4, 1, u32> primitive_restart_enable; 113 BitField<Position + 6, 3, u32> action_depth_pass;
154 BitField<5, 1, u32> cull_enable; 114 BitField<Position + 9, 3, u32> test_func;
155 BitField<6, 1, u32> depth_bias_enable;
156 BitField<7, 1, u32> depth_clamp_disabled;
157 BitField<8, 1, u32> ndc_minus_one_to_one;
158 BitField<9, 2, u32> cull_face;
159 BitField<11, 1, u32> front_face;
160 BitField<12, 2, u32> polygon_mode;
161 BitField<14, 5, u32> patch_control_points_minus_one;
162 BitField<19, 2, u32> tessellation_primitive;
163 BitField<21, 2, u32> tessellation_spacing;
164 BitField<23, 1, u32> tessellation_clockwise;
165 BitField<24, 1, u32> logic_op_enable;
166 BitField<25, 4, u32> logic_op;
167 BitField<29, 1, u32> rasterize_enable;
168 };
169
170 // TODO(Rodrigo): Move this to push constants
171 u32 point_size;
172 115
173 void Fill(const Maxwell& regs) noexcept; 116 Maxwell::StencilOp ActionStencilFail() const noexcept {
117 return UnpackStencilOp(action_stencil_fail);
118 }
174 119
175 constexpr Maxwell::PrimitiveTopology Topology() const noexcept { 120 Maxwell::StencilOp ActionDepthFail() const noexcept {
176 return static_cast<Maxwell::PrimitiveTopology>(topology.Value()); 121 return UnpackStencilOp(action_depth_fail);
177 } 122 }
178 123
179 Maxwell::CullFace CullFace() const noexcept { 124 Maxwell::StencilOp ActionDepthPass() const noexcept {
180 return UnpackCullFace(cull_face.Value()); 125 return UnpackStencilOp(action_depth_pass);
181 } 126 }
182 127
183 Maxwell::FrontFace FrontFace() const noexcept { 128 Maxwell::ComparisonOp TestFunc() const noexcept {
184 return UnpackFrontFace(front_face.Value()); 129 return UnpackComparisonOp(test_func);
185 } 130 }
186 }; 131 };
187 132
188 struct DepthStencil { 133 union VertexBinding {
189 template <std::size_t Position> 134 u16 raw;
190 union StencilFace { 135 BitField<0, 12, u16> stride;
191 BitField<Position + 0, 3, u32> action_stencil_fail; 136 BitField<12, 1, u16> enabled;
192 BitField<Position + 3, 3, u32> action_depth_fail; 137 };
193 BitField<Position + 6, 3, u32> action_depth_pass;
194 BitField<Position + 9, 3, u32> test_func;
195
196 Maxwell::StencilOp ActionStencilFail() const noexcept {
197 return UnpackStencilOp(action_stencil_fail);
198 }
199
200 Maxwell::StencilOp ActionDepthFail() const noexcept {
201 return UnpackStencilOp(action_depth_fail);
202 }
203
204 Maxwell::StencilOp ActionDepthPass() const noexcept {
205 return UnpackStencilOp(action_depth_pass);
206 }
207
208 Maxwell::ComparisonOp TestFunc() const noexcept {
209 return UnpackComparisonOp(test_func);
210 }
211 };
212 138
139 struct DynamicState {
213 union { 140 union {
214 u32 raw; 141 u32 raw1;
215 StencilFace<0> front; 142 StencilFace<0> front;
216 StencilFace<12> back; 143 StencilFace<12> back;
217 BitField<24, 1, u32> depth_test_enable; 144 BitField<24, 1, u32> stencil_enable;
218 BitField<25, 1, u32> depth_write_enable; 145 BitField<25, 1, u32> depth_write_enable;
219 BitField<26, 1, u32> depth_bounds_enable; 146 BitField<26, 1, u32> depth_bounds_enable;
220 BitField<27, 1, u32> stencil_enable; 147 BitField<27, 1, u32> depth_test_enable;
221 BitField<28, 3, u32> depth_test_func; 148 BitField<28, 1, u32> front_face;
149 BitField<29, 3, u32> depth_test_func;
150 };
151 union {
152 u32 raw2;
153 BitField<0, 4, u32> topology;
154 BitField<4, 2, u32> cull_face;
155 BitField<6, 1, u32> cull_enable;
222 }; 156 };
157 std::array<VertexBinding, Maxwell::NumVertexArrays> vertex_bindings;
223 158
224 void Fill(const Maxwell& regs) noexcept; 159 void Fill(const Maxwell& regs);
225 160
226 Maxwell::ComparisonOp DepthTestFunc() const noexcept { 161 Maxwell::ComparisonOp DepthTestFunc() const noexcept {
227 return UnpackComparisonOp(depth_test_func); 162 return UnpackComparisonOp(depth_test_func);
228 } 163 }
229 };
230
231 struct ColorBlending {
232 std::array<BlendingAttachment, Maxwell::NumRenderTargets> attachments;
233 164
234 void Fill(const Maxwell& regs) noexcept; 165 Maxwell::CullFace CullFace() const noexcept {
235 }; 166 return UnpackCullFace(cull_face.Value());
167 }
236 168
237 struct ViewportSwizzles { 169 Maxwell::FrontFace FrontFace() const noexcept {
238 std::array<u16, Maxwell::NumViewports> swizzles; 170 return UnpackFrontFace(front_face.Value());
171 }
239 172
240 void Fill(const Maxwell& regs) noexcept; 173 constexpr Maxwell::PrimitiveTopology Topology() const noexcept {
174 return static_cast<Maxwell::PrimitiveTopology>(topology.Value());
175 }
241 }; 176 };
242 177
243 VertexInput vertex_input; 178 union {
244 Rasterizer rasterizer; 179 u32 raw;
245 DepthStencil depth_stencil; 180 BitField<0, 1, u32> no_extended_dynamic_state;
246 ColorBlending color_blending; 181 BitField<2, 1, u32> primitive_restart_enable;
247 ViewportSwizzles viewport_swizzles; 182 BitField<3, 1, u32> depth_bias_enable;
183 BitField<4, 1, u32> depth_clamp_disabled;
184 BitField<5, 1, u32> ndc_minus_one_to_one;
185 BitField<6, 2, u32> polygon_mode;
186 BitField<8, 5, u32> patch_control_points_minus_one;
187 BitField<13, 2, u32> tessellation_primitive;
188 BitField<15, 2, u32> tessellation_spacing;
189 BitField<17, 1, u32> tessellation_clockwise;
190 BitField<18, 1, u32> logic_op_enable;
191 BitField<19, 4, u32> logic_op;
192 BitField<23, 1, u32> rasterize_enable;
193 };
194 u32 point_size;
195 std::array<u32, Maxwell::NumVertexArrays> binding_divisors;
196 std::array<VertexAttribute, Maxwell::NumVertexAttributes> attributes;
197 std::array<BlendingAttachment, Maxwell::NumRenderTargets> attachments;
198 std::array<u16, Maxwell::NumViewports> viewport_swizzles;
199 DynamicState dynamic_state;
248 200
249 void Fill(const Maxwell& regs); 201 void Fill(const Maxwell& regs, bool has_extended_dynamic_state);
250 202
251 std::size_t Hash() const noexcept; 203 std::size_t Hash() const noexcept;
252 204
@@ -255,6 +207,11 @@ struct FixedPipelineState {
255 bool operator!=(const FixedPipelineState& rhs) const noexcept { 207 bool operator!=(const FixedPipelineState& rhs) const noexcept {
256 return !operator==(rhs); 208 return !operator==(rhs);
257 } 209 }
210
211 std::size_t Size() const noexcept {
212 const std::size_t total_size = sizeof *this;
213 return total_size - (no_extended_dynamic_state != 0 ? 0 : sizeof(DynamicState));
214 }
258}; 215};
259static_assert(std::has_unique_object_representations_v<FixedPipelineState>); 216static_assert(std::has_unique_object_representations_v<FixedPipelineState>);
260static_assert(std::is_trivially_copyable_v<FixedPipelineState>); 217static_assert(std::is_trivially_copyable_v<FixedPipelineState>);
diff --git a/src/video_core/renderer_vulkan/vk_device.cpp b/src/video_core/renderer_vulkan/vk_device.cpp
index 9fd8ac3f6..fdaea4210 100644
--- a/src/video_core/renderer_vulkan/vk_device.cpp
+++ b/src/video_core/renderer_vulkan/vk_device.cpp
@@ -313,6 +313,16 @@ bool VKDevice::Create() {
313 LOG_INFO(Render_Vulkan, "Device doesn't support custom border colors"); 313 LOG_INFO(Render_Vulkan, "Device doesn't support custom border colors");
314 } 314 }
315 315
316 VkPhysicalDeviceExtendedDynamicStateFeaturesEXT dynamic_state;
317 if (ext_extended_dynamic_state) {
318 dynamic_state.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTENDED_DYNAMIC_STATE_FEATURES_EXT;
319 dynamic_state.pNext = nullptr;
320 dynamic_state.extendedDynamicState = VK_TRUE;
321 SetNext(next, dynamic_state);
322 } else {
323 LOG_INFO(Render_Vulkan, "Device doesn't support extended dynamic state");
324 }
325
316 if (!ext_depth_range_unrestricted) { 326 if (!ext_depth_range_unrestricted) {
317 LOG_INFO(Render_Vulkan, "Device doesn't support depth range unrestricted"); 327 LOG_INFO(Render_Vulkan, "Device doesn't support depth range unrestricted");
318 } 328 }
@@ -541,6 +551,7 @@ std::vector<const char*> VKDevice::LoadExtensions() {
541 bool has_ext_subgroup_size_control{}; 551 bool has_ext_subgroup_size_control{};
542 bool has_ext_transform_feedback{}; 552 bool has_ext_transform_feedback{};
543 bool has_ext_custom_border_color{}; 553 bool has_ext_custom_border_color{};
554 bool has_ext_extended_dynamic_state{};
544 for (const auto& extension : physical.EnumerateDeviceExtensionProperties()) { 555 for (const auto& extension : physical.EnumerateDeviceExtensionProperties()) {
545 Test(extension, nv_viewport_swizzle, VK_NV_VIEWPORT_SWIZZLE_EXTENSION_NAME, true); 556 Test(extension, nv_viewport_swizzle, VK_NV_VIEWPORT_SWIZZLE_EXTENSION_NAME, true);
546 Test(extension, khr_uniform_buffer_standard_layout, 557 Test(extension, khr_uniform_buffer_standard_layout,
@@ -558,6 +569,8 @@ std::vector<const char*> VKDevice::LoadExtensions() {
558 false); 569 false);
559 Test(extension, has_ext_custom_border_color, VK_EXT_CUSTOM_BORDER_COLOR_EXTENSION_NAME, 570 Test(extension, has_ext_custom_border_color, VK_EXT_CUSTOM_BORDER_COLOR_EXTENSION_NAME,
560 false); 571 false);
572 Test(extension, has_ext_extended_dynamic_state,
573 VK_EXT_EXTENDED_DYNAMIC_STATE_EXTENSION_NAME, false);
561 if (Settings::values.renderer_debug) { 574 if (Settings::values.renderer_debug) {
562 Test(extension, nv_device_diagnostics_config, 575 Test(extension, nv_device_diagnostics_config,
563 VK_NV_DEVICE_DIAGNOSTICS_CONFIG_EXTENSION_NAME, true); 576 VK_NV_DEVICE_DIAGNOSTICS_CONFIG_EXTENSION_NAME, true);
@@ -643,6 +656,19 @@ std::vector<const char*> VKDevice::LoadExtensions() {
643 } 656 }
644 } 657 }
645 658
659 if (has_ext_extended_dynamic_state) {
660 VkPhysicalDeviceExtendedDynamicStateFeaturesEXT dynamic_state;
661 dynamic_state.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTENDED_DYNAMIC_STATE_FEATURES_EXT;
662 dynamic_state.pNext = nullptr;
663 features.pNext = &dynamic_state;
664 physical.GetFeatures2KHR(features);
665
666 if (dynamic_state.extendedDynamicState) {
667 extensions.push_back(VK_EXT_EXTENDED_DYNAMIC_STATE_EXTENSION_NAME);
668 ext_extended_dynamic_state = true;
669 }
670 }
671
646 return extensions; 672 return extensions;
647} 673}
648 674
diff --git a/src/video_core/renderer_vulkan/vk_device.h b/src/video_core/renderer_vulkan/vk_device.h
index 6b9227b09..ae5c21baa 100644
--- a/src/video_core/renderer_vulkan/vk_device.h
+++ b/src/video_core/renderer_vulkan/vk_device.h
@@ -182,6 +182,11 @@ public:
182 return ext_custom_border_color; 182 return ext_custom_border_color;
183 } 183 }
184 184
185 /// Returns true if the device supports VK_EXT_extended_dynamic_state.
186 bool IsExtExtendedDynamicStateSupported() const {
187 return ext_extended_dynamic_state;
188 }
189
185 /// Returns the vendor name reported from Vulkan. 190 /// Returns the vendor name reported from Vulkan.
186 std::string_view GetVendorName() const { 191 std::string_view GetVendorName() const {
187 return vendor_name; 192 return vendor_name;
@@ -239,6 +244,7 @@ private:
239 bool ext_shader_viewport_index_layer{}; ///< Support for VK_EXT_shader_viewport_index_layer. 244 bool ext_shader_viewport_index_layer{}; ///< Support for VK_EXT_shader_viewport_index_layer.
240 bool ext_transform_feedback{}; ///< Support for VK_EXT_transform_feedback. 245 bool ext_transform_feedback{}; ///< Support for VK_EXT_transform_feedback.
241 bool ext_custom_border_color{}; ///< Support for VK_EXT_custom_border_color. 246 bool ext_custom_border_color{}; ///< Support for VK_EXT_custom_border_color.
247 bool ext_extended_dynamic_state{}; ///< Support for VK_EXT_extended_dynamic_state.
242 bool nv_device_diagnostics_config{}; ///< Support for VK_NV_device_diagnostics_config. 248 bool nv_device_diagnostics_config{}; ///< Support for VK_NV_device_diagnostics_config.
243 249
244 // Telemetry parameters 250 // Telemetry parameters
diff --git a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp
index 69b6bba00..844445105 100644
--- a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp
+++ b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp
@@ -176,20 +176,32 @@ std::vector<vk::ShaderModule> VKGraphicsPipeline::CreateShaderModules(
176 176
177vk::Pipeline VKGraphicsPipeline::CreatePipeline(const RenderPassParams& renderpass_params, 177vk::Pipeline VKGraphicsPipeline::CreatePipeline(const RenderPassParams& renderpass_params,
178 const SPIRVProgram& program) const { 178 const SPIRVProgram& program) const {
179 const auto& vi = fixed_state.vertex_input; 179 const auto& state = fixed_state;
180 const auto& ds = fixed_state.depth_stencil; 180 const auto& viewport_swizzles = state.viewport_swizzles;
181 const auto& cd = fixed_state.color_blending; 181
182 const auto& rs = fixed_state.rasterizer; 182 FixedPipelineState::DynamicState dynamic;
183 const auto& viewport_swizzles = fixed_state.viewport_swizzles.swizzles; 183 if (device.IsExtExtendedDynamicStateSupported()) {
184 // Insert dummy values, as long as they are valid they don't matter as extended dynamic
185 // state is ignored
186 dynamic.raw1 = 0;
187 dynamic.raw2 = 0;
188 for (FixedPipelineState::VertexBinding& binding : dynamic.vertex_bindings) {
189 // Enable all vertex bindings
190 binding.raw = 0;
191 binding.enabled.Assign(1);
192 }
193 } else {
194 dynamic = state.dynamic_state;
195 }
184 196
185 std::vector<VkVertexInputBindingDescription> vertex_bindings; 197 std::vector<VkVertexInputBindingDescription> vertex_bindings;
186 std::vector<VkVertexInputBindingDivisorDescriptionEXT> vertex_binding_divisors; 198 std::vector<VkVertexInputBindingDivisorDescriptionEXT> vertex_binding_divisors;
187 for (std::size_t index = 0; index < std::size(vi.bindings); ++index) { 199 for (std::size_t index = 0; index < Maxwell::NumVertexArrays; ++index) {
188 const auto& binding = vi.bindings[index]; 200 const auto& binding = dynamic.vertex_bindings[index];
189 if (!binding.enabled) { 201 if (!binding.enabled) {
190 continue; 202 continue;
191 } 203 }
192 const bool instanced = vi.binding_divisors[index] != 0; 204 const bool instanced = state.binding_divisors[index] != 0;
193 const auto rate = instanced ? VK_VERTEX_INPUT_RATE_INSTANCE : VK_VERTEX_INPUT_RATE_VERTEX; 205 const auto rate = instanced ? VK_VERTEX_INPUT_RATE_INSTANCE : VK_VERTEX_INPUT_RATE_VERTEX;
194 206
195 auto& vertex_binding = vertex_bindings.emplace_back(); 207 auto& vertex_binding = vertex_bindings.emplace_back();
@@ -200,14 +212,14 @@ vk::Pipeline VKGraphicsPipeline::CreatePipeline(const RenderPassParams& renderpa
200 if (instanced) { 212 if (instanced) {
201 auto& binding_divisor = vertex_binding_divisors.emplace_back(); 213 auto& binding_divisor = vertex_binding_divisors.emplace_back();
202 binding_divisor.binding = static_cast<u32>(index); 214 binding_divisor.binding = static_cast<u32>(index);
203 binding_divisor.divisor = vi.binding_divisors[index]; 215 binding_divisor.divisor = state.binding_divisors[index];
204 } 216 }
205 } 217 }
206 218
207 std::vector<VkVertexInputAttributeDescription> vertex_attributes; 219 std::vector<VkVertexInputAttributeDescription> vertex_attributes;
208 const auto& input_attributes = program[0]->entries.attributes; 220 const auto& input_attributes = program[0]->entries.attributes;
209 for (std::size_t index = 0; index < std::size(vi.attributes); ++index) { 221 for (std::size_t index = 0; index < state.attributes.size(); ++index) {
210 const auto& attribute = vi.attributes[index]; 222 const auto& attribute = state.attributes[index];
211 if (!attribute.enabled) { 223 if (!attribute.enabled) {
212 continue; 224 continue;
213 } 225 }
@@ -244,15 +256,15 @@ vk::Pipeline VKGraphicsPipeline::CreatePipeline(const RenderPassParams& renderpa
244 input_assembly_ci.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO; 256 input_assembly_ci.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO;
245 input_assembly_ci.pNext = nullptr; 257 input_assembly_ci.pNext = nullptr;
246 input_assembly_ci.flags = 0; 258 input_assembly_ci.flags = 0;
247 input_assembly_ci.topology = MaxwellToVK::PrimitiveTopology(device, rs.Topology()); 259 input_assembly_ci.topology = MaxwellToVK::PrimitiveTopology(device, dynamic.Topology());
248 input_assembly_ci.primitiveRestartEnable = 260 input_assembly_ci.primitiveRestartEnable =
249 rs.primitive_restart_enable != 0 && SupportsPrimitiveRestart(input_assembly_ci.topology); 261 state.primitive_restart_enable != 0 && SupportsPrimitiveRestart(input_assembly_ci.topology);
250 262
251 VkPipelineTessellationStateCreateInfo tessellation_ci; 263 VkPipelineTessellationStateCreateInfo tessellation_ci;
252 tessellation_ci.sType = VK_STRUCTURE_TYPE_PIPELINE_TESSELLATION_STATE_CREATE_INFO; 264 tessellation_ci.sType = VK_STRUCTURE_TYPE_PIPELINE_TESSELLATION_STATE_CREATE_INFO;
253 tessellation_ci.pNext = nullptr; 265 tessellation_ci.pNext = nullptr;
254 tessellation_ci.flags = 0; 266 tessellation_ci.flags = 0;
255 tessellation_ci.patchControlPoints = rs.patch_control_points_minus_one.Value() + 1; 267 tessellation_ci.patchControlPoints = state.patch_control_points_minus_one.Value() + 1;
256 268
257 VkPipelineViewportStateCreateInfo viewport_ci; 269 VkPipelineViewportStateCreateInfo viewport_ci;
258 viewport_ci.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO; 270 viewport_ci.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO;
@@ -280,13 +292,13 @@ vk::Pipeline VKGraphicsPipeline::CreatePipeline(const RenderPassParams& renderpa
280 rasterization_ci.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO; 292 rasterization_ci.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO;
281 rasterization_ci.pNext = nullptr; 293 rasterization_ci.pNext = nullptr;
282 rasterization_ci.flags = 0; 294 rasterization_ci.flags = 0;
283 rasterization_ci.depthClampEnable = rs.depth_clamp_disabled == 0 ? VK_TRUE : VK_FALSE; 295 rasterization_ci.depthClampEnable = state.depth_clamp_disabled == 0 ? VK_TRUE : VK_FALSE;
284 rasterization_ci.rasterizerDiscardEnable = rs.rasterize_enable == 0 ? VK_TRUE : VK_FALSE; 296 rasterization_ci.rasterizerDiscardEnable = state.rasterize_enable == 0 ? VK_TRUE : VK_FALSE;
285 rasterization_ci.polygonMode = VK_POLYGON_MODE_FILL; 297 rasterization_ci.polygonMode = VK_POLYGON_MODE_FILL;
286 rasterization_ci.cullMode = 298 rasterization_ci.cullMode =
287 rs.cull_enable ? MaxwellToVK::CullFace(rs.CullFace()) : VK_CULL_MODE_NONE; 299 dynamic.cull_enable ? MaxwellToVK::CullFace(dynamic.CullFace()) : VK_CULL_MODE_NONE;
288 rasterization_ci.frontFace = MaxwellToVK::FrontFace(rs.FrontFace()); 300 rasterization_ci.frontFace = MaxwellToVK::FrontFace(dynamic.FrontFace());
289 rasterization_ci.depthBiasEnable = rs.depth_bias_enable; 301 rasterization_ci.depthBiasEnable = state.depth_bias_enable;
290 rasterization_ci.depthBiasConstantFactor = 0.0f; 302 rasterization_ci.depthBiasConstantFactor = 0.0f;
291 rasterization_ci.depthBiasClamp = 0.0f; 303 rasterization_ci.depthBiasClamp = 0.0f;
292 rasterization_ci.depthBiasSlopeFactor = 0.0f; 304 rasterization_ci.depthBiasSlopeFactor = 0.0f;
@@ -307,14 +319,15 @@ vk::Pipeline VKGraphicsPipeline::CreatePipeline(const RenderPassParams& renderpa
307 depth_stencil_ci.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO; 319 depth_stencil_ci.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO;
308 depth_stencil_ci.pNext = nullptr; 320 depth_stencil_ci.pNext = nullptr;
309 depth_stencil_ci.flags = 0; 321 depth_stencil_ci.flags = 0;
310 depth_stencil_ci.depthTestEnable = ds.depth_test_enable; 322 depth_stencil_ci.depthTestEnable = dynamic.depth_test_enable;
311 depth_stencil_ci.depthWriteEnable = ds.depth_write_enable; 323 depth_stencil_ci.depthWriteEnable = dynamic.depth_write_enable;
312 depth_stencil_ci.depthCompareOp = 324 depth_stencil_ci.depthCompareOp = dynamic.depth_test_enable
313 ds.depth_test_enable ? MaxwellToVK::ComparisonOp(ds.DepthTestFunc()) : VK_COMPARE_OP_ALWAYS; 325 ? MaxwellToVK::ComparisonOp(dynamic.DepthTestFunc())
314 depth_stencil_ci.depthBoundsTestEnable = ds.depth_bounds_enable; 326 : VK_COMPARE_OP_ALWAYS;
315 depth_stencil_ci.stencilTestEnable = ds.stencil_enable; 327 depth_stencil_ci.depthBoundsTestEnable = dynamic.depth_bounds_enable;
316 depth_stencil_ci.front = GetStencilFaceState(ds.front); 328 depth_stencil_ci.stencilTestEnable = dynamic.stencil_enable;
317 depth_stencil_ci.back = GetStencilFaceState(ds.back); 329 depth_stencil_ci.front = GetStencilFaceState(dynamic.front);
330 depth_stencil_ci.back = GetStencilFaceState(dynamic.back);
318 depth_stencil_ci.minDepthBounds = 0.0f; 331 depth_stencil_ci.minDepthBounds = 0.0f;
319 depth_stencil_ci.maxDepthBounds = 0.0f; 332 depth_stencil_ci.maxDepthBounds = 0.0f;
320 333
@@ -324,7 +337,7 @@ vk::Pipeline VKGraphicsPipeline::CreatePipeline(const RenderPassParams& renderpa
324 static constexpr std::array COMPONENT_TABLE = { 337 static constexpr std::array COMPONENT_TABLE = {
325 VK_COLOR_COMPONENT_R_BIT, VK_COLOR_COMPONENT_G_BIT, VK_COLOR_COMPONENT_B_BIT, 338 VK_COLOR_COMPONENT_R_BIT, VK_COLOR_COMPONENT_G_BIT, VK_COLOR_COMPONENT_B_BIT,
326 VK_COLOR_COMPONENT_A_BIT}; 339 VK_COLOR_COMPONENT_A_BIT};
327 const auto& blend = cd.attachments[index]; 340 const auto& blend = state.attachments[index];
328 341
329 VkColorComponentFlags color_components = 0; 342 VkColorComponentFlags color_components = 0;
330 for (std::size_t i = 0; i < COMPONENT_TABLE.size(); ++i) { 343 for (std::size_t i = 0; i < COMPONENT_TABLE.size(); ++i) {
@@ -354,11 +367,27 @@ vk::Pipeline VKGraphicsPipeline::CreatePipeline(const RenderPassParams& renderpa
354 color_blend_ci.pAttachments = cb_attachments.data(); 367 color_blend_ci.pAttachments = cb_attachments.data();
355 std::memset(color_blend_ci.blendConstants, 0, sizeof(color_blend_ci.blendConstants)); 368 std::memset(color_blend_ci.blendConstants, 0, sizeof(color_blend_ci.blendConstants));
356 369
357 static constexpr std::array dynamic_states = { 370 std::vector dynamic_states = {
358 VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR, 371 VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR,
359 VK_DYNAMIC_STATE_DEPTH_BIAS, VK_DYNAMIC_STATE_BLEND_CONSTANTS, 372 VK_DYNAMIC_STATE_DEPTH_BIAS, VK_DYNAMIC_STATE_BLEND_CONSTANTS,
360 VK_DYNAMIC_STATE_DEPTH_BOUNDS, VK_DYNAMIC_STATE_STENCIL_COMPARE_MASK, 373 VK_DYNAMIC_STATE_DEPTH_BOUNDS, VK_DYNAMIC_STATE_STENCIL_COMPARE_MASK,
361 VK_DYNAMIC_STATE_STENCIL_WRITE_MASK, VK_DYNAMIC_STATE_STENCIL_REFERENCE}; 374 VK_DYNAMIC_STATE_STENCIL_WRITE_MASK, VK_DYNAMIC_STATE_STENCIL_REFERENCE,
375 };
376 if (device.IsExtExtendedDynamicStateSupported()) {
377 static constexpr std::array extended = {
378 VK_DYNAMIC_STATE_CULL_MODE_EXT,
379 VK_DYNAMIC_STATE_FRONT_FACE_EXT,
380 VK_DYNAMIC_STATE_PRIMITIVE_TOPOLOGY_EXT,
381 VK_DYNAMIC_STATE_VERTEX_INPUT_BINDING_STRIDE_EXT,
382 VK_DYNAMIC_STATE_DEPTH_TEST_ENABLE_EXT,
383 VK_DYNAMIC_STATE_DEPTH_WRITE_ENABLE_EXT,
384 VK_DYNAMIC_STATE_DEPTH_COMPARE_OP_EXT,
385 VK_DYNAMIC_STATE_DEPTH_BOUNDS_TEST_ENABLE_EXT,
386 VK_DYNAMIC_STATE_STENCIL_TEST_ENABLE_EXT,
387 VK_DYNAMIC_STATE_STENCIL_OP_EXT,
388 };
389 dynamic_states.insert(dynamic_states.end(), extended.begin(), extended.end());
390 }
362 391
363 VkPipelineDynamicStateCreateInfo dynamic_state_ci; 392 VkPipelineDynamicStateCreateInfo dynamic_state_ci;
364 dynamic_state_ci.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO; 393 dynamic_state_ci.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO;
diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp
index ea66e621e..3da835324 100644
--- a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp
+++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp
@@ -116,12 +116,12 @@ u32 FillDescriptorLayout(const ShaderEntries& entries,
116} // Anonymous namespace 116} // Anonymous namespace
117 117
118std::size_t GraphicsPipelineCacheKey::Hash() const noexcept { 118std::size_t GraphicsPipelineCacheKey::Hash() const noexcept {
119 const u64 hash = Common::CityHash64(reinterpret_cast<const char*>(this), sizeof *this); 119 const u64 hash = Common::CityHash64(reinterpret_cast<const char*>(this), Size());
120 return static_cast<std::size_t>(hash); 120 return static_cast<std::size_t>(hash);
121} 121}
122 122
123bool GraphicsPipelineCacheKey::operator==(const GraphicsPipelineCacheKey& rhs) const noexcept { 123bool GraphicsPipelineCacheKey::operator==(const GraphicsPipelineCacheKey& rhs) const noexcept {
124 return std::memcmp(&rhs, this, sizeof *this) == 0; 124 return std::memcmp(&rhs, this, Size()) == 0;
125} 125}
126 126
127std::size_t ComputePipelineCacheKey::Hash() const noexcept { 127std::size_t ComputePipelineCacheKey::Hash() const noexcept {
@@ -312,18 +312,19 @@ VKPipelineCache::DecompileShaders(const GraphicsPipelineCacheKey& key) {
312 const auto& gpu = system.GPU().Maxwell3D(); 312 const auto& gpu = system.GPU().Maxwell3D();
313 313
314 Specialization specialization; 314 Specialization specialization;
315 if (fixed_state.rasterizer.Topology() == Maxwell::PrimitiveTopology::Points) { 315 if (fixed_state.dynamic_state.Topology() == Maxwell::PrimitiveTopology::Points ||
316 device.IsExtExtendedDynamicStateSupported()) {
316 float point_size; 317 float point_size;
317 std::memcpy(&point_size, &fixed_state.rasterizer.point_size, sizeof(float)); 318 std::memcpy(&point_size, &fixed_state.point_size, sizeof(float));
318 specialization.point_size = point_size; 319 specialization.point_size = point_size;
319 ASSERT(point_size != 0.0f); 320 ASSERT(point_size != 0.0f);
320 } 321 }
321 for (std::size_t i = 0; i < Maxwell::NumVertexAttributes; ++i) { 322 for (std::size_t i = 0; i < Maxwell::NumVertexAttributes; ++i) {
322 const auto& attribute = fixed_state.vertex_input.attributes[i]; 323 const auto& attribute = fixed_state.attributes[i];
323 specialization.enabled_attributes[i] = attribute.enabled.Value() != 0; 324 specialization.enabled_attributes[i] = attribute.enabled.Value() != 0;
324 specialization.attribute_types[i] = attribute.Type(); 325 specialization.attribute_types[i] = attribute.Type();
325 } 326 }
326 specialization.ndc_minus_one_to_one = fixed_state.rasterizer.ndc_minus_one_to_one; 327 specialization.ndc_minus_one_to_one = fixed_state.ndc_minus_one_to_one;
327 328
328 SPIRVProgram program; 329 SPIRVProgram program;
329 std::vector<VkDescriptorSetLayoutBinding> bindings; 330 std::vector<VkDescriptorSetLayoutBinding> bindings;
diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.h b/src/video_core/renderer_vulkan/vk_pipeline_cache.h
index 0a36e5112..0a3fe65fb 100644
--- a/src/video_core/renderer_vulkan/vk_pipeline_cache.h
+++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.h
@@ -44,10 +44,10 @@ class VKUpdateDescriptorQueue;
44using Maxwell = Tegra::Engines::Maxwell3D::Regs; 44using Maxwell = Tegra::Engines::Maxwell3D::Regs;
45 45
46struct GraphicsPipelineCacheKey { 46struct GraphicsPipelineCacheKey {
47 FixedPipelineState fixed_state;
48 RenderPassParams renderpass_params; 47 RenderPassParams renderpass_params;
48 u32 padding;
49 std::array<GPUVAddr, Maxwell::MaxShaderProgram> shaders; 49 std::array<GPUVAddr, Maxwell::MaxShaderProgram> shaders;
50 u64 padding; // This is necessary for unique object representations 50 FixedPipelineState fixed_state;
51 51
52 std::size_t Hash() const noexcept; 52 std::size_t Hash() const noexcept;
53 53
@@ -56,6 +56,10 @@ struct GraphicsPipelineCacheKey {
56 bool operator!=(const GraphicsPipelineCacheKey& rhs) const noexcept { 56 bool operator!=(const GraphicsPipelineCacheKey& rhs) const noexcept {
57 return !operator==(rhs); 57 return !operator==(rhs);
58 } 58 }
59
60 std::size_t Size() const noexcept {
61 return sizeof(renderpass_params) + sizeof(padding) + sizeof(shaders) + fixed_state.Size();
62 }
59}; 63};
60static_assert(std::has_unique_object_representations_v<GraphicsPipelineCacheKey>); 64static_assert(std::has_unique_object_representations_v<GraphicsPipelineCacheKey>);
61static_assert(std::is_trivially_copyable_v<GraphicsPipelineCacheKey>); 65static_assert(std::is_trivially_copyable_v<GraphicsPipelineCacheKey>);
diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp
index a8d94eac3..380ed532b 100644
--- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp
+++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp
@@ -186,13 +186,22 @@ bool HasToPreserveDepthContents(bool is_clear, const Maxwell& regs) {
186 scissor.max_y < regs.zeta_height; 186 scissor.max_y < regs.zeta_height;
187} 187}
188 188
189template <std::size_t N>
190std::array<VkDeviceSize, N> ExpandStrides(const std::array<u16, N>& strides) {
191 std::array<VkDeviceSize, N> expanded;
192 std::copy(strides.begin(), strides.end(), expanded.begin());
193 return expanded;
194}
195
189} // Anonymous namespace 196} // Anonymous namespace
190 197
191class BufferBindings final { 198class BufferBindings final {
192public: 199public:
193 void AddVertexBinding(VkBuffer buffer, VkDeviceSize offset) { 200 void AddVertexBinding(VkBuffer buffer, VkDeviceSize offset, VkDeviceSize size, u32 stride) {
194 vertex.buffers[vertex.num_buffers] = buffer; 201 vertex.buffers[vertex.num_buffers] = buffer;
195 vertex.offsets[vertex.num_buffers] = offset; 202 vertex.offsets[vertex.num_buffers] = offset;
203 vertex.sizes[vertex.num_buffers] = size;
204 vertex.strides[vertex.num_buffers] = static_cast<u16>(stride);
196 ++vertex.num_buffers; 205 ++vertex.num_buffers;
197 } 206 }
198 207
@@ -202,76 +211,76 @@ public:
202 index.type = type; 211 index.type = type;
203 } 212 }
204 213
205 void Bind(VKScheduler& scheduler) const { 214 void Bind(const VKDevice& device, VKScheduler& scheduler) const {
206 // Use this large switch case to avoid dispatching more memory in the record lambda than 215 // Use this large switch case to avoid dispatching more memory in the record lambda than
207 // what we need. It looks horrible, but it's the best we can do on standard C++. 216 // what we need. It looks horrible, but it's the best we can do on standard C++.
208 switch (vertex.num_buffers) { 217 switch (vertex.num_buffers) {
209 case 0: 218 case 0:
210 return BindStatic<0>(scheduler); 219 return BindStatic<0>(device, scheduler);
211 case 1: 220 case 1:
212 return BindStatic<1>(scheduler); 221 return BindStatic<1>(device, scheduler);
213 case 2: 222 case 2:
214 return BindStatic<2>(scheduler); 223 return BindStatic<2>(device, scheduler);
215 case 3: 224 case 3:
216 return BindStatic<3>(scheduler); 225 return BindStatic<3>(device, scheduler);
217 case 4: 226 case 4:
218 return BindStatic<4>(scheduler); 227 return BindStatic<4>(device, scheduler);
219 case 5: 228 case 5:
220 return BindStatic<5>(scheduler); 229 return BindStatic<5>(device, scheduler);
221 case 6: 230 case 6:
222 return BindStatic<6>(scheduler); 231 return BindStatic<6>(device, scheduler);
223 case 7: 232 case 7:
224 return BindStatic<7>(scheduler); 233 return BindStatic<7>(device, scheduler);
225 case 8: 234 case 8:
226 return BindStatic<8>(scheduler); 235 return BindStatic<8>(device, scheduler);
227 case 9: 236 case 9:
228 return BindStatic<9>(scheduler); 237 return BindStatic<9>(device, scheduler);
229 case 10: 238 case 10:
230 return BindStatic<10>(scheduler); 239 return BindStatic<10>(device, scheduler);
231 case 11: 240 case 11:
232 return BindStatic<11>(scheduler); 241 return BindStatic<11>(device, scheduler);
233 case 12: 242 case 12:
234 return BindStatic<12>(scheduler); 243 return BindStatic<12>(device, scheduler);
235 case 13: 244 case 13:
236 return BindStatic<13>(scheduler); 245 return BindStatic<13>(device, scheduler);
237 case 14: 246 case 14:
238 return BindStatic<14>(scheduler); 247 return BindStatic<14>(device, scheduler);
239 case 15: 248 case 15:
240 return BindStatic<15>(scheduler); 249 return BindStatic<15>(device, scheduler);
241 case 16: 250 case 16:
242 return BindStatic<16>(scheduler); 251 return BindStatic<16>(device, scheduler);
243 case 17: 252 case 17:
244 return BindStatic<17>(scheduler); 253 return BindStatic<17>(device, scheduler);
245 case 18: 254 case 18:
246 return BindStatic<18>(scheduler); 255 return BindStatic<18>(device, scheduler);
247 case 19: 256 case 19:
248 return BindStatic<19>(scheduler); 257 return BindStatic<19>(device, scheduler);
249 case 20: 258 case 20:
250 return BindStatic<20>(scheduler); 259 return BindStatic<20>(device, scheduler);
251 case 21: 260 case 21:
252 return BindStatic<21>(scheduler); 261 return BindStatic<21>(device, scheduler);
253 case 22: 262 case 22:
254 return BindStatic<22>(scheduler); 263 return BindStatic<22>(device, scheduler);
255 case 23: 264 case 23:
256 return BindStatic<23>(scheduler); 265 return BindStatic<23>(device, scheduler);
257 case 24: 266 case 24:
258 return BindStatic<24>(scheduler); 267 return BindStatic<24>(device, scheduler);
259 case 25: 268 case 25:
260 return BindStatic<25>(scheduler); 269 return BindStatic<25>(device, scheduler);
261 case 26: 270 case 26:
262 return BindStatic<26>(scheduler); 271 return BindStatic<26>(device, scheduler);
263 case 27: 272 case 27:
264 return BindStatic<27>(scheduler); 273 return BindStatic<27>(device, scheduler);
265 case 28: 274 case 28:
266 return BindStatic<28>(scheduler); 275 return BindStatic<28>(device, scheduler);
267 case 29: 276 case 29:
268 return BindStatic<29>(scheduler); 277 return BindStatic<29>(device, scheduler);
269 case 30: 278 case 30:
270 return BindStatic<30>(scheduler); 279 return BindStatic<30>(device, scheduler);
271 case 31: 280 case 31:
272 return BindStatic<31>(scheduler); 281 return BindStatic<31>(device, scheduler);
273 case 32: 282 case 32:
274 return BindStatic<32>(scheduler); 283 return BindStatic<32>(device, scheduler);
275 } 284 }
276 UNREACHABLE(); 285 UNREACHABLE();
277 } 286 }
@@ -282,6 +291,8 @@ private:
282 std::size_t num_buffers = 0; 291 std::size_t num_buffers = 0;
283 std::array<VkBuffer, Maxwell::NumVertexArrays> buffers; 292 std::array<VkBuffer, Maxwell::NumVertexArrays> buffers;
284 std::array<VkDeviceSize, Maxwell::NumVertexArrays> offsets; 293 std::array<VkDeviceSize, Maxwell::NumVertexArrays> offsets;
294 std::array<VkDeviceSize, Maxwell::NumVertexArrays> sizes;
295 std::array<u16, Maxwell::NumVertexArrays> strides;
285 } vertex; 296 } vertex;
286 297
287 struct { 298 struct {
@@ -291,15 +302,23 @@ private:
291 } index; 302 } index;
292 303
293 template <std::size_t N> 304 template <std::size_t N>
294 void BindStatic(VKScheduler& scheduler) const { 305 void BindStatic(const VKDevice& device, VKScheduler& scheduler) const {
295 if (index.buffer) { 306 if (device.IsExtExtendedDynamicStateSupported()) {
296 BindStatic<N, true>(scheduler); 307 if (index.buffer) {
308 BindStatic<N, true, true>(scheduler);
309 } else {
310 BindStatic<N, false, true>(scheduler);
311 }
297 } else { 312 } else {
298 BindStatic<N, false>(scheduler); 313 if (index.buffer) {
314 BindStatic<N, true, false>(scheduler);
315 } else {
316 BindStatic<N, false, false>(scheduler);
317 }
299 } 318 }
300 } 319 }
301 320
302 template <std::size_t N, bool is_indexed> 321 template <std::size_t N, bool is_indexed, bool has_extended_dynamic_state>
303 void BindStatic(VKScheduler& scheduler) const { 322 void BindStatic(VKScheduler& scheduler) const {
304 static_assert(N <= Maxwell::NumVertexArrays); 323 static_assert(N <= Maxwell::NumVertexArrays);
305 if constexpr (N == 0) { 324 if constexpr (N == 0) {
@@ -311,6 +330,31 @@ private:
311 std::copy(vertex.buffers.begin(), vertex.buffers.begin() + N, buffers.begin()); 330 std::copy(vertex.buffers.begin(), vertex.buffers.begin() + N, buffers.begin());
312 std::copy(vertex.offsets.begin(), vertex.offsets.begin() + N, offsets.begin()); 331 std::copy(vertex.offsets.begin(), vertex.offsets.begin() + N, offsets.begin());
313 332
333 if constexpr (has_extended_dynamic_state) {
334 // With extended dynamic states we can specify the length and stride of a vertex buffer
335 // std::array<VkDeviceSize, N> sizes;
336 std::array<u16, N> strides;
337 // std::copy(vertex.sizes.begin(), vertex.sizes.begin() + N, sizes.begin());
338 std::copy(vertex.strides.begin(), vertex.strides.begin() + N, strides.begin());
339
340 if constexpr (is_indexed) {
341 scheduler.Record(
342 [buffers, offsets, strides, index = index](vk::CommandBuffer cmdbuf) {
343 cmdbuf.BindIndexBuffer(index.buffer, index.offset, index.type);
344 cmdbuf.BindVertexBuffers2EXT(0, static_cast<u32>(N), buffers.data(),
345 offsets.data(), nullptr,
346 ExpandStrides(strides).data());
347 });
348 } else {
349 scheduler.Record([buffers, offsets, strides](vk::CommandBuffer cmdbuf) {
350 cmdbuf.BindVertexBuffers2EXT(0, static_cast<u32>(N), buffers.data(),
351 offsets.data(), nullptr,
352 ExpandStrides(strides).data());
353 });
354 }
355 return;
356 }
357
314 if constexpr (is_indexed) { 358 if constexpr (is_indexed) {
315 // Indexed draw 359 // Indexed draw
316 scheduler.Record([buffers, offsets, index = index](vk::CommandBuffer cmdbuf) { 360 scheduler.Record([buffers, offsets, index = index](vk::CommandBuffer cmdbuf) {
@@ -369,7 +413,7 @@ void RasterizerVulkan::Draw(bool is_indexed, bool is_instanced) {
369 413
370 const auto& gpu = system.GPU().Maxwell3D(); 414 const auto& gpu = system.GPU().Maxwell3D();
371 GraphicsPipelineCacheKey key; 415 GraphicsPipelineCacheKey key;
372 key.fixed_state.Fill(gpu.regs); 416 key.fixed_state.Fill(gpu.regs, device.IsExtExtendedDynamicStateSupported());
373 417
374 buffer_cache.Map(CalculateGraphicsStreamBufferSize(is_indexed)); 418 buffer_cache.Map(CalculateGraphicsStreamBufferSize(is_indexed));
375 419
@@ -402,7 +446,7 @@ void RasterizerVulkan::Draw(bool is_indexed, bool is_instanced) {
402 446
403 UpdateDynamicStates(); 447 UpdateDynamicStates();
404 448
405 buffer_bindings.Bind(scheduler); 449 buffer_bindings.Bind(device, scheduler);
406 450
407 BeginTransformFeedback(); 451 BeginTransformFeedback();
408 452
@@ -822,7 +866,7 @@ RasterizerVulkan::DrawParameters RasterizerVulkan::SetupGeometry(FixedPipelineSt
822 const auto& gpu = system.GPU().Maxwell3D(); 866 const auto& gpu = system.GPU().Maxwell3D();
823 const auto& regs = gpu.regs; 867 const auto& regs = gpu.regs;
824 868
825 SetupVertexArrays(fixed_state.vertex_input, buffer_bindings); 869 SetupVertexArrays(buffer_bindings);
826 870
827 const u32 base_instance = regs.vb_base_instance; 871 const u32 base_instance = regs.vb_base_instance;
828 const u32 num_instances = is_instanced ? gpu.mme_draw.instance_count : 1; 872 const u32 num_instances = is_instanced ? gpu.mme_draw.instance_count : 1;
@@ -893,6 +937,17 @@ void RasterizerVulkan::UpdateDynamicStates() {
893 UpdateBlendConstants(regs); 937 UpdateBlendConstants(regs);
894 UpdateDepthBounds(regs); 938 UpdateDepthBounds(regs);
895 UpdateStencilFaces(regs); 939 UpdateStencilFaces(regs);
940 if (device.IsExtExtendedDynamicStateSupported()) {
941 UpdateCullMode(regs);
942 UpdateDepthBoundsTestEnable(regs);
943 UpdateDepthTestEnable(regs);
944 UpdateDepthWriteEnable(regs);
945 UpdateDepthCompareOp(regs);
946 UpdateFrontFace(regs);
947 UpdatePrimitiveTopology(regs);
948 UpdateStencilOp(regs);
949 UpdateStencilTestEnable(regs);
950 }
896} 951}
897 952
898void RasterizerVulkan::BeginTransformFeedback() { 953void RasterizerVulkan::BeginTransformFeedback() {
@@ -940,41 +995,25 @@ void RasterizerVulkan::EndTransformFeedback() {
940 [](vk::CommandBuffer cmdbuf) { cmdbuf.EndTransformFeedbackEXT(0, 0, nullptr, nullptr); }); 995 [](vk::CommandBuffer cmdbuf) { cmdbuf.EndTransformFeedbackEXT(0, 0, nullptr, nullptr); });
941} 996}
942 997
943void RasterizerVulkan::SetupVertexArrays(FixedPipelineState::VertexInput& vertex_input, 998void RasterizerVulkan::SetupVertexArrays(BufferBindings& buffer_bindings) {
944 BufferBindings& buffer_bindings) {
945 const auto& regs = system.GPU().Maxwell3D().regs; 999 const auto& regs = system.GPU().Maxwell3D().regs;
946 1000
947 for (std::size_t index = 0; index < Maxwell::NumVertexAttributes; ++index) {
948 const auto& attrib = regs.vertex_attrib_format[index];
949 if (attrib.IsConstant()) {
950 vertex_input.SetAttribute(index, false, 0, 0, {}, {});
951 continue;
952 }
953 vertex_input.SetAttribute(index, true, attrib.buffer, attrib.offset, attrib.type.Value(),
954 attrib.size.Value());
955 }
956
957 for (std::size_t index = 0; index < Maxwell::NumVertexArrays; ++index) { 1001 for (std::size_t index = 0; index < Maxwell::NumVertexArrays; ++index) {
958 const auto& vertex_array = regs.vertex_array[index]; 1002 const auto& vertex_array = regs.vertex_array[index];
959 if (!vertex_array.IsEnabled()) { 1003 if (!vertex_array.IsEnabled()) {
960 vertex_input.SetBinding(index, false, 0, 0);
961 continue; 1004 continue;
962 } 1005 }
963 vertex_input.SetBinding(
964 index, true, vertex_array.stride,
965 regs.instanced_arrays.IsInstancingEnabled(index) ? vertex_array.divisor : 0);
966
967 const GPUVAddr start{vertex_array.StartAddress()}; 1006 const GPUVAddr start{vertex_array.StartAddress()};
968 const GPUVAddr end{regs.vertex_array_limit[index].LimitAddress()}; 1007 const GPUVAddr end{regs.vertex_array_limit[index].LimitAddress()};
969 1008
970 ASSERT(end >= start); 1009 ASSERT(end >= start);
971 const std::size_t size{end - start}; 1010 const std::size_t size = end - start;
972 if (size == 0) { 1011 if (size == 0) {
973 buffer_bindings.AddVertexBinding(DefaultBuffer(), 0); 1012 buffer_bindings.AddVertexBinding(DefaultBuffer(), 0, DEFAULT_BUFFER_SIZE, 0);
974 continue; 1013 continue;
975 } 1014 }
976 const auto info = buffer_cache.UploadMemory(start, size); 1015 const auto info = buffer_cache.UploadMemory(start, size);
977 buffer_bindings.AddVertexBinding(info.handle, info.offset); 1016 buffer_bindings.AddVertexBinding(info.handle, info.offset, size, vertex_array.stride);
978 } 1017 }
979} 1018}
980 1019
@@ -1326,6 +1365,117 @@ void RasterizerVulkan::UpdateStencilFaces(Tegra::Engines::Maxwell3D::Regs& regs)
1326 } 1365 }
1327} 1366}
1328 1367
1368void RasterizerVulkan::UpdateCullMode(Tegra::Engines::Maxwell3D::Regs& regs) {
1369 if (!state_tracker.TouchCullMode()) {
1370 return;
1371 }
1372 scheduler.Record(
1373 [enabled = regs.cull_test_enabled, cull_face = regs.cull_face](vk::CommandBuffer cmdbuf) {
1374 cmdbuf.SetCullModeEXT(enabled ? MaxwellToVK::CullFace(cull_face) : VK_CULL_MODE_NONE);
1375 });
1376}
1377
1378void RasterizerVulkan::UpdateDepthBoundsTestEnable(Tegra::Engines::Maxwell3D::Regs& regs) {
1379 if (!state_tracker.TouchDepthBoundsTestEnable()) {
1380 return;
1381 }
1382 scheduler.Record([enable = regs.depth_bounds_enable](vk::CommandBuffer cmdbuf) {
1383 cmdbuf.SetDepthBoundsTestEnableEXT(enable);
1384 });
1385}
1386
1387void RasterizerVulkan::UpdateDepthTestEnable(Tegra::Engines::Maxwell3D::Regs& regs) {
1388 if (!state_tracker.TouchDepthTestEnable()) {
1389 return;
1390 }
1391 scheduler.Record([enable = regs.depth_test_enable](vk::CommandBuffer cmdbuf) {
1392 cmdbuf.SetDepthTestEnableEXT(enable);
1393 });
1394}
1395
1396void RasterizerVulkan::UpdateDepthWriteEnable(Tegra::Engines::Maxwell3D::Regs& regs) {
1397 if (!state_tracker.TouchDepthWriteEnable()) {
1398 return;
1399 }
1400 scheduler.Record([enable = regs.depth_write_enabled](vk::CommandBuffer cmdbuf) {
1401 cmdbuf.SetDepthWriteEnableEXT(enable);
1402 });
1403}
1404
1405void RasterizerVulkan::UpdateDepthCompareOp(Tegra::Engines::Maxwell3D::Regs& regs) {
1406 if (!state_tracker.TouchDepthCompareOp()) {
1407 return;
1408 }
1409 scheduler.Record([func = regs.depth_test_func](vk::CommandBuffer cmdbuf) {
1410 cmdbuf.SetDepthCompareOpEXT(MaxwellToVK::ComparisonOp(func));
1411 });
1412}
1413
1414void RasterizerVulkan::UpdateFrontFace(Tegra::Engines::Maxwell3D::Regs& regs) {
1415 if (!state_tracker.TouchFrontFace()) {
1416 return;
1417 }
1418
1419 VkFrontFace front_face = MaxwellToVK::FrontFace(regs.front_face);
1420 if (regs.screen_y_control.triangle_rast_flip != 0) {
1421 front_face = front_face == VK_FRONT_FACE_CLOCKWISE ? VK_FRONT_FACE_COUNTER_CLOCKWISE
1422 : VK_FRONT_FACE_CLOCKWISE;
1423 }
1424 scheduler.Record(
1425 [front_face](vk::CommandBuffer cmdbuf) { cmdbuf.SetFrontFaceEXT(front_face); });
1426}
1427
1428void RasterizerVulkan::UpdatePrimitiveTopology(Tegra::Engines::Maxwell3D::Regs& regs) {
1429 if (!state_tracker.TouchPrimitiveTopology()) {
1430 return;
1431 }
1432 const Maxwell::PrimitiveTopology primitive_topology = regs.draw.topology.Value();
1433 scheduler.Record([this, primitive_topology](vk::CommandBuffer cmdbuf) {
1434 cmdbuf.SetPrimitiveTopologyEXT(MaxwellToVK::PrimitiveTopology(device, primitive_topology));
1435 });
1436}
1437
1438void RasterizerVulkan::UpdateStencilOp(Tegra::Engines::Maxwell3D::Regs& regs) {
1439 if (!state_tracker.TouchStencilOp()) {
1440 return;
1441 }
1442 const Maxwell::StencilOp fail = regs.stencil_front_op_fail;
1443 const Maxwell::StencilOp zfail = regs.stencil_front_op_zfail;
1444 const Maxwell::StencilOp zpass = regs.stencil_front_op_zpass;
1445 const Maxwell::ComparisonOp compare = regs.stencil_front_func_func;
1446 if (regs.stencil_two_side_enable) {
1447 scheduler.Record([fail, zfail, zpass, compare](vk::CommandBuffer cmdbuf) {
1448 cmdbuf.SetStencilOpEXT(VK_STENCIL_FACE_FRONT_AND_BACK, MaxwellToVK::StencilOp(fail),
1449 MaxwellToVK::StencilOp(zpass), MaxwellToVK::StencilOp(zfail),
1450 MaxwellToVK::ComparisonOp(compare));
1451 });
1452 } else {
1453 const Maxwell::StencilOp back_fail = regs.stencil_back_op_fail;
1454 const Maxwell::StencilOp back_zfail = regs.stencil_back_op_zfail;
1455 const Maxwell::StencilOp back_zpass = regs.stencil_back_op_zpass;
1456 const Maxwell::ComparisonOp back_compare = regs.stencil_back_func_func;
1457 scheduler.Record([fail, zfail, zpass, compare, back_fail, back_zfail, back_zpass,
1458 back_compare](vk::CommandBuffer cmdbuf) {
1459 cmdbuf.SetStencilOpEXT(VK_STENCIL_FACE_FRONT_BIT, MaxwellToVK::StencilOp(fail),
1460 MaxwellToVK::StencilOp(zpass), MaxwellToVK::StencilOp(zfail),
1461 MaxwellToVK::ComparisonOp(compare));
1462 cmdbuf.SetStencilOpEXT(VK_STENCIL_FACE_BACK_BIT, MaxwellToVK::StencilOp(back_fail),
1463 MaxwellToVK::StencilOp(back_zpass),
1464 MaxwellToVK::StencilOp(back_zfail),
1465 MaxwellToVK::ComparisonOp(back_compare));
1466 });
1467 }
1468}
1469
1470void RasterizerVulkan::UpdateStencilTestEnable(Tegra::Engines::Maxwell3D::Regs& regs) {
1471 if (!state_tracker.TouchStencilTestEnable()) {
1472 return;
1473 }
1474 scheduler.Record([enable = regs.stencil_enable](vk::CommandBuffer cmdbuf) {
1475 cmdbuf.SetStencilTestEnableEXT(enable);
1476 });
1477}
1478
1329std::size_t RasterizerVulkan::CalculateGraphicsStreamBufferSize(bool is_indexed) const { 1479std::size_t RasterizerVulkan::CalculateGraphicsStreamBufferSize(bool is_indexed) const {
1330 std::size_t size = CalculateVertexArraysSize(); 1480 std::size_t size = CalculateVertexArraysSize();
1331 if (is_indexed) { 1481 if (is_indexed) {
diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.h b/src/video_core/renderer_vulkan/vk_rasterizer.h
index 83e00e7e9..923178b0b 100644
--- a/src/video_core/renderer_vulkan/vk_rasterizer.h
+++ b/src/video_core/renderer_vulkan/vk_rasterizer.h
@@ -185,8 +185,7 @@ private:
185 185
186 bool WalkAttachmentOverlaps(const CachedSurfaceView& attachment); 186 bool WalkAttachmentOverlaps(const CachedSurfaceView& attachment);
187 187
188 void SetupVertexArrays(FixedPipelineState::VertexInput& vertex_input, 188 void SetupVertexArrays(BufferBindings& buffer_bindings);
189 BufferBindings& buffer_bindings);
190 189
191 void SetupIndexBuffer(BufferBindings& buffer_bindings, DrawParameters& params, bool is_indexed); 190 void SetupIndexBuffer(BufferBindings& buffer_bindings, DrawParameters& params, bool is_indexed);
192 191
@@ -246,6 +245,16 @@ private:
246 void UpdateDepthBounds(Tegra::Engines::Maxwell3D::Regs& regs); 245 void UpdateDepthBounds(Tegra::Engines::Maxwell3D::Regs& regs);
247 void UpdateStencilFaces(Tegra::Engines::Maxwell3D::Regs& regs); 246 void UpdateStencilFaces(Tegra::Engines::Maxwell3D::Regs& regs);
248 247
248 void UpdateCullMode(Tegra::Engines::Maxwell3D::Regs& regs);
249 void UpdateDepthBoundsTestEnable(Tegra::Engines::Maxwell3D::Regs& regs);
250 void UpdateDepthTestEnable(Tegra::Engines::Maxwell3D::Regs& regs);
251 void UpdateDepthWriteEnable(Tegra::Engines::Maxwell3D::Regs& regs);
252 void UpdateDepthCompareOp(Tegra::Engines::Maxwell3D::Regs& regs);
253 void UpdateFrontFace(Tegra::Engines::Maxwell3D::Regs& regs);
254 void UpdatePrimitiveTopology(Tegra::Engines::Maxwell3D::Regs& regs);
255 void UpdateStencilOp(Tegra::Engines::Maxwell3D::Regs& regs);
256 void UpdateStencilTestEnable(Tegra::Engines::Maxwell3D::Regs& regs);
257
249 std::size_t CalculateGraphicsStreamBufferSize(bool is_indexed) const; 258 std::size_t CalculateGraphicsStreamBufferSize(bool is_indexed) const;
250 259
251 std::size_t CalculateComputeStreamBufferSize() const; 260 std::size_t CalculateComputeStreamBufferSize() const;
diff --git a/src/video_core/renderer_vulkan/vk_state_tracker.cpp b/src/video_core/renderer_vulkan/vk_state_tracker.cpp
index 94a89e388..e5a583dd5 100644
--- a/src/video_core/renderer_vulkan/vk_state_tracker.cpp
+++ b/src/video_core/renderer_vulkan/vk_state_tracker.cpp
@@ -36,6 +36,15 @@ Flags MakeInvalidationFlags() {
36 flags[BlendConstants] = true; 36 flags[BlendConstants] = true;
37 flags[DepthBounds] = true; 37 flags[DepthBounds] = true;
38 flags[StencilProperties] = true; 38 flags[StencilProperties] = true;
39 flags[CullMode] = true;
40 flags[DepthBoundsEnable] = true;
41 flags[DepthTestEnable] = true;
42 flags[DepthWriteEnable] = true;
43 flags[DepthCompareOp] = true;
44 flags[FrontFace] = true;
45 flags[PrimitiveTopology] = true;
46 flags[StencilOp] = true;
47 flags[StencilTestEnable] = true;
39 return flags; 48 return flags;
40} 49}
41 50
@@ -75,6 +84,57 @@ void SetupDirtyStencilProperties(Tables& tables) {
75 table[OFF(stencil_back_func_mask)] = StencilProperties; 84 table[OFF(stencil_back_func_mask)] = StencilProperties;
76} 85}
77 86
87void SetupDirtyCullMode(Tables& tables) {
88 auto& table = tables[0];
89 table[OFF(cull_face)] = CullMode;
90 table[OFF(cull_test_enabled)] = CullMode;
91}
92
93void SetupDirtyDepthBoundsEnable(Tables& tables) {
94 tables[0][OFF(depth_bounds_enable)] = DepthBoundsEnable;
95}
96
97void SetupDirtyDepthTestEnable(Tables& tables) {
98 tables[0][OFF(depth_test_enable)] = DepthTestEnable;
99}
100
101void SetupDirtyDepthWriteEnable(Tables& tables) {
102 tables[0][OFF(depth_write_enabled)] = DepthWriteEnable;
103}
104
105void SetupDirtyDepthCompareOp(Tables& tables) {
106 tables[0][OFF(depth_test_func)] = DepthCompareOp;
107}
108
109void SetupDirtyFrontFace(Tables& tables) {
110 auto& table = tables[0];
111 table[OFF(front_face)] = FrontFace;
112 table[OFF(screen_y_control)] = FrontFace;
113}
114
115void SetupDirtyPrimitiveTopology(Tables& tables) {
116 tables[0][OFF(draw.topology)] = PrimitiveTopology;
117}
118
119void SetupDirtyStencilOp(Tables& tables) {
120 auto& table = tables[0];
121 table[OFF(stencil_front_op_fail)] = StencilOp;
122 table[OFF(stencil_front_op_zfail)] = StencilOp;
123 table[OFF(stencil_front_op_zpass)] = StencilOp;
124 table[OFF(stencil_front_func_func)] = StencilOp;
125 table[OFF(stencil_back_op_fail)] = StencilOp;
126 table[OFF(stencil_back_op_zfail)] = StencilOp;
127 table[OFF(stencil_back_op_zpass)] = StencilOp;
128 table[OFF(stencil_back_func_func)] = StencilOp;
129
130 // Table 0 is used by StencilProperties
131 tables[1][OFF(stencil_two_side_enable)] = StencilOp;
132}
133
134void SetupDirtyStencilTestEnable(Tables& tables) {
135 tables[0][OFF(stencil_enable)] = StencilTestEnable;
136}
137
78} // Anonymous namespace 138} // Anonymous namespace
79 139
80StateTracker::StateTracker(Core::System& system) 140StateTracker::StateTracker(Core::System& system)
@@ -90,6 +150,14 @@ void StateTracker::Initialize() {
90 SetupDirtyBlendConstants(tables); 150 SetupDirtyBlendConstants(tables);
91 SetupDirtyDepthBounds(tables); 151 SetupDirtyDepthBounds(tables);
92 SetupDirtyStencilProperties(tables); 152 SetupDirtyStencilProperties(tables);
153 SetupDirtyCullMode(tables);
154 SetupDirtyDepthBoundsEnable(tables);
155 SetupDirtyDepthTestEnable(tables);
156 SetupDirtyDepthWriteEnable(tables);
157 SetupDirtyDepthCompareOp(tables);
158 SetupDirtyFrontFace(tables);
159 SetupDirtyPrimitiveTopology(tables);
160 SetupDirtyStencilOp(tables);
93} 161}
94 162
95void StateTracker::InvalidateCommandBufferState() { 163void StateTracker::InvalidateCommandBufferState() {
diff --git a/src/video_core/renderer_vulkan/vk_state_tracker.h b/src/video_core/renderer_vulkan/vk_state_tracker.h
index 03bc415b2..54ca0d6c6 100644
--- a/src/video_core/renderer_vulkan/vk_state_tracker.h
+++ b/src/video_core/renderer_vulkan/vk_state_tracker.h
@@ -26,6 +26,16 @@ enum : u8 {
26 DepthBounds, 26 DepthBounds,
27 StencilProperties, 27 StencilProperties,
28 28
29 CullMode,
30 DepthBoundsEnable,
31 DepthTestEnable,
32 DepthWriteEnable,
33 DepthCompareOp,
34 FrontFace,
35 PrimitiveTopology,
36 StencilOp,
37 StencilTestEnable,
38
29 Last 39 Last
30}; 40};
31static_assert(Last <= std::numeric_limits<u8>::max()); 41static_assert(Last <= std::numeric_limits<u8>::max());
@@ -64,6 +74,46 @@ public:
64 return Exchange(Dirty::StencilProperties, false); 74 return Exchange(Dirty::StencilProperties, false);
65 } 75 }
66 76
77 bool TouchCullMode() {
78 return Exchange(Dirty::CullMode, false);
79 }
80
81 bool TouchDepthBoundsTestEnable() {
82 return Exchange(Dirty::DepthBoundsEnable, false);
83 }
84
85 bool TouchDepthTestEnable() {
86 return Exchange(Dirty::DepthTestEnable, false);
87 }
88
89 bool TouchDepthBoundsEnable() {
90 return Exchange(Dirty::DepthBoundsEnable, false);
91 }
92
93 bool TouchDepthWriteEnable() {
94 return Exchange(Dirty::DepthWriteEnable, false);
95 }
96
97 bool TouchDepthCompareOp() {
98 return Exchange(Dirty::DepthCompareOp, false);
99 }
100
101 bool TouchFrontFace() {
102 return Exchange(Dirty::FrontFace, false);
103 }
104
105 bool TouchPrimitiveTopology() {
106 return Exchange(Dirty::PrimitiveTopology, false);
107 }
108
109 bool TouchStencilOp() {
110 return Exchange(Dirty::StencilOp, false);
111 }
112
113 bool TouchStencilTestEnable() {
114 return Exchange(Dirty::StencilTestEnable, false);
115 }
116
67private: 117private:
68 bool Exchange(std::size_t id, bool new_value) const noexcept { 118 bool Exchange(std::size_t id, bool new_value) const noexcept {
69 auto& flags = system.GPU().Maxwell3D().dirty.flags; 119 auto& flags = system.GPU().Maxwell3D().dirty.flags;
diff --git a/src/video_core/renderer_vulkan/wrapper.cpp b/src/video_core/renderer_vulkan/wrapper.cpp
index 0d485a662..051298cc8 100644
--- a/src/video_core/renderer_vulkan/wrapper.cpp
+++ b/src/video_core/renderer_vulkan/wrapper.cpp
@@ -88,6 +88,16 @@ void Load(VkDevice device, DeviceDispatch& dld) noexcept {
88 X(vkCmdSetStencilWriteMask); 88 X(vkCmdSetStencilWriteMask);
89 X(vkCmdSetViewport); 89 X(vkCmdSetViewport);
90 X(vkCmdWaitEvents); 90 X(vkCmdWaitEvents);
91 X(vkCmdBindVertexBuffers2EXT);
92 X(vkCmdSetCullModeEXT);
93 X(vkCmdSetDepthBoundsTestEnableEXT);
94 X(vkCmdSetDepthCompareOpEXT);
95 X(vkCmdSetDepthTestEnableEXT);
96 X(vkCmdSetDepthWriteEnableEXT);
97 X(vkCmdSetFrontFaceEXT);
98 X(vkCmdSetPrimitiveTopologyEXT);
99 X(vkCmdSetStencilOpEXT);
100 X(vkCmdSetStencilTestEnableEXT);
91 X(vkCreateBuffer); 101 X(vkCreateBuffer);
92 X(vkCreateBufferView); 102 X(vkCreateBufferView);
93 X(vkCreateCommandPool); 103 X(vkCreateCommandPool);
diff --git a/src/video_core/renderer_vulkan/wrapper.h b/src/video_core/renderer_vulkan/wrapper.h
index d56fdb3f9..71daac9d7 100644
--- a/src/video_core/renderer_vulkan/wrapper.h
+++ b/src/video_core/renderer_vulkan/wrapper.h
@@ -207,6 +207,16 @@ struct DeviceDispatch : public InstanceDispatch {
207 PFN_vkCmdSetStencilWriteMask vkCmdSetStencilWriteMask; 207 PFN_vkCmdSetStencilWriteMask vkCmdSetStencilWriteMask;
208 PFN_vkCmdSetViewport vkCmdSetViewport; 208 PFN_vkCmdSetViewport vkCmdSetViewport;
209 PFN_vkCmdWaitEvents vkCmdWaitEvents; 209 PFN_vkCmdWaitEvents vkCmdWaitEvents;
210 PFN_vkCmdBindVertexBuffers2EXT vkCmdBindVertexBuffers2EXT;
211 PFN_vkCmdSetCullModeEXT vkCmdSetCullModeEXT;
212 PFN_vkCmdSetDepthBoundsTestEnableEXT vkCmdSetDepthBoundsTestEnableEXT;
213 PFN_vkCmdSetDepthCompareOpEXT vkCmdSetDepthCompareOpEXT;
214 PFN_vkCmdSetDepthTestEnableEXT vkCmdSetDepthTestEnableEXT;
215 PFN_vkCmdSetDepthWriteEnableEXT vkCmdSetDepthWriteEnableEXT;
216 PFN_vkCmdSetFrontFaceEXT vkCmdSetFrontFaceEXT;
217 PFN_vkCmdSetPrimitiveTopologyEXT vkCmdSetPrimitiveTopologyEXT;
218 PFN_vkCmdSetStencilOpEXT vkCmdSetStencilOpEXT;
219 PFN_vkCmdSetStencilTestEnableEXT vkCmdSetStencilTestEnableEXT;
210 PFN_vkCreateBuffer vkCreateBuffer; 220 PFN_vkCreateBuffer vkCreateBuffer;
211 PFN_vkCreateBufferView vkCreateBufferView; 221 PFN_vkCreateBufferView vkCreateBufferView;
212 PFN_vkCreateCommandPool vkCreateCommandPool; 222 PFN_vkCreateCommandPool vkCreateCommandPool;
@@ -969,6 +979,50 @@ public:
969 buffer_barriers.data(), image_barriers.size(), image_barriers.data()); 979 buffer_barriers.data(), image_barriers.size(), image_barriers.data());
970 } 980 }
971 981
982 void BindVertexBuffers2EXT(u32 first_binding, u32 binding_count, const VkBuffer* buffers,
983 const VkDeviceSize* offsets, const VkDeviceSize* sizes,
984 const VkDeviceSize* strides) const noexcept {
985 dld->vkCmdBindVertexBuffers2EXT(handle, first_binding, binding_count, buffers, offsets,
986 sizes, strides);
987 }
988
989 void SetCullModeEXT(VkCullModeFlags cull_mode) const noexcept {
990 dld->vkCmdSetCullModeEXT(handle, cull_mode);
991 }
992
993 void SetDepthBoundsTestEnableEXT(bool enable) const noexcept {
994 dld->vkCmdSetDepthBoundsTestEnableEXT(handle, enable ? VK_TRUE : VK_FALSE);
995 }
996
997 void SetDepthCompareOpEXT(VkCompareOp compare_op) const noexcept {
998 dld->vkCmdSetDepthCompareOpEXT(handle, compare_op);
999 }
1000
1001 void SetDepthTestEnableEXT(bool enable) const noexcept {
1002 dld->vkCmdSetDepthTestEnableEXT(handle, enable ? VK_TRUE : VK_FALSE);
1003 }
1004
1005 void SetDepthWriteEnableEXT(bool enable) const noexcept {
1006 dld->vkCmdSetDepthWriteEnableEXT(handle, enable ? VK_TRUE : VK_FALSE);
1007 }
1008
1009 void SetFrontFaceEXT(VkFrontFace front_face) const noexcept {
1010 dld->vkCmdSetFrontFaceEXT(handle, front_face);
1011 }
1012
1013 void SetPrimitiveTopologyEXT(VkPrimitiveTopology primitive_topology) const noexcept {
1014 dld->vkCmdSetPrimitiveTopologyEXT(handle, primitive_topology);
1015 }
1016
1017 void SetStencilOpEXT(VkStencilFaceFlags face_mask, VkStencilOp fail_op, VkStencilOp pass_op,
1018 VkStencilOp depth_fail_op, VkCompareOp compare_op) const noexcept {
1019 dld->vkCmdSetStencilOpEXT(handle, face_mask, fail_op, pass_op, depth_fail_op, compare_op);
1020 }
1021
1022 void SetStencilTestEnableEXT(bool enable) const noexcept {
1023 dld->vkCmdSetStencilTestEnableEXT(handle, enable ? VK_TRUE : VK_FALSE);
1024 }
1025
972 void BindTransformFeedbackBuffersEXT(u32 first, u32 count, const VkBuffer* buffers, 1026 void BindTransformFeedbackBuffersEXT(u32 first, u32 count, const VkBuffer* buffers,
973 const VkDeviceSize* offsets, 1027 const VkDeviceSize* offsets,
974 const VkDeviceSize* sizes) const noexcept { 1028 const VkDeviceSize* sizes) const noexcept {
diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp
index bbbd96113..5e0d0e7af 100644
--- a/src/yuzu/configuration/config.cpp
+++ b/src/yuzu/configuration/config.cpp
@@ -212,7 +212,7 @@ const std::array<int, Settings::NativeKeyboard::NumKeyboardMods> Config::default
212// UISetting::values.shortcuts, which is alphabetically ordered. 212// UISetting::values.shortcuts, which is alphabetically ordered.
213// clang-format off 213// clang-format off
214const std::array<UISettings::Shortcut, 16> Config::default_hotkeys{{ 214const std::array<UISettings::Shortcut, 16> Config::default_hotkeys{{
215 {QStringLiteral("Capture Screenshot"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+P"), Qt::ApplicationShortcut}}, 215 {QStringLiteral("Capture Screenshot"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+P"), Qt::WidgetWithChildrenShortcut}},
216 {QStringLiteral("Change Docked Mode"), QStringLiteral("Main Window"), {QStringLiteral("F10"), Qt::ApplicationShortcut}}, 216 {QStringLiteral("Change Docked Mode"), QStringLiteral("Main Window"), {QStringLiteral("F10"), Qt::ApplicationShortcut}},
217 {QStringLiteral("Continue/Pause Emulation"), QStringLiteral("Main Window"), {QStringLiteral("F4"), Qt::WindowShortcut}}, 217 {QStringLiteral("Continue/Pause Emulation"), QStringLiteral("Main Window"), {QStringLiteral("F4"), Qt::WindowShortcut}},
218 {QStringLiteral("Decrease Speed Limit"), QStringLiteral("Main Window"), {QStringLiteral("-"), Qt::ApplicationShortcut}}, 218 {QStringLiteral("Decrease Speed Limit"), QStringLiteral("Main Window"), {QStringLiteral("-"), Qt::ApplicationShortcut}},
@@ -220,8 +220,8 @@ const std::array<UISettings::Shortcut, 16> Config::default_hotkeys{{
220 {QStringLiteral("Exit yuzu"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+Q"), Qt::WindowShortcut}}, 220 {QStringLiteral("Exit yuzu"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+Q"), Qt::WindowShortcut}},
221 {QStringLiteral("Fullscreen"), QStringLiteral("Main Window"), {QStringLiteral("F11"), Qt::WindowShortcut}}, 221 {QStringLiteral("Fullscreen"), QStringLiteral("Main Window"), {QStringLiteral("F11"), Qt::WindowShortcut}},
222 {QStringLiteral("Increase Speed Limit"), QStringLiteral("Main Window"), {QStringLiteral("+"), Qt::ApplicationShortcut}}, 222 {QStringLiteral("Increase Speed Limit"), QStringLiteral("Main Window"), {QStringLiteral("+"), Qt::ApplicationShortcut}},
223 {QStringLiteral("Load Amiibo"), QStringLiteral("Main Window"), {QStringLiteral("F2"), Qt::ApplicationShortcut}}, 223 {QStringLiteral("Load Amiibo"), QStringLiteral("Main Window"), {QStringLiteral("F2"), Qt::WidgetWithChildrenShortcut}},
224 {QStringLiteral("Load File"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+O"), Qt::WindowShortcut}}, 224 {QStringLiteral("Load File"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+O"), Qt::WidgetWithChildrenShortcut}},
225 {QStringLiteral("Mute Audio"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+M"), Qt::WindowShortcut}}, 225 {QStringLiteral("Mute Audio"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+M"), Qt::WindowShortcut}},
226 {QStringLiteral("Restart Emulation"), QStringLiteral("Main Window"), {QStringLiteral("F6"), Qt::WindowShortcut}}, 226 {QStringLiteral("Restart Emulation"), QStringLiteral("Main Window"), {QStringLiteral("F6"), Qt::WindowShortcut}},
227 {QStringLiteral("Stop Emulation"), QStringLiteral("Main Window"), {QStringLiteral("F5"), Qt::WindowShortcut}}, 227 {QStringLiteral("Stop Emulation"), QStringLiteral("Main Window"), {QStringLiteral("F5"), Qt::WindowShortcut}},
@@ -665,11 +665,13 @@ void Config::ReadShortcutValues() {
665 const auto& [keyseq, context] = shortcut; 665 const auto& [keyseq, context] = shortcut;
666 qt_config->beginGroup(group); 666 qt_config->beginGroup(group);
667 qt_config->beginGroup(name); 667 qt_config->beginGroup(name);
668 // No longer using ReadSetting for shortcut.second as it innacurately returns a value of 1
669 // for WidgetWithChildrenShortcut which is a value of 3. Needed to fix shortcuts the open
670 // a file dialog in windowed mode
668 UISettings::values.shortcuts.push_back( 671 UISettings::values.shortcuts.push_back(
669 {name, 672 {name,
670 group, 673 group,
671 {ReadSetting(QStringLiteral("KeySeq"), keyseq).toString(), 674 {ReadSetting(QStringLiteral("KeySeq"), keyseq).toString(), shortcut.second}});
672 ReadSetting(QStringLiteral("Context"), context).toInt()}});
673 qt_config->endGroup(); 675 qt_config->endGroup();
674 qt_config->endGroup(); 676 qt_config->endGroup();
675 } 677 }
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp
index fb299a39b..9844e4764 100644
--- a/src/yuzu/main.cpp
+++ b/src/yuzu/main.cpp
@@ -753,7 +753,7 @@ void GMainWindow::InitializeHotkeys() {
753 }); 753 });
754 connect(hotkey_registry.GetHotkey(main_window, QStringLiteral("Capture Screenshot"), this), 754 connect(hotkey_registry.GetHotkey(main_window, QStringLiteral("Capture Screenshot"), this),
755 &QShortcut::activated, this, [&] { 755 &QShortcut::activated, this, [&] {
756 if (emu_thread->IsRunning()) { 756 if (emu_thread != nullptr && emu_thread->IsRunning()) {
757 OnCaptureScreenshot(); 757 OnCaptureScreenshot();
758 } 758 }
759 }); 759 });