summaryrefslogtreecommitdiff
path: root/src/shader_recompiler/backend
diff options
context:
space:
mode:
authorGravatar ReinUsesLisp2021-06-24 02:41:09 -0300
committerGravatar ameerj2021-07-22 21:51:39 -0400
commit7dafa96ab59892b7f1fbffdb61e4326e6443955f (patch)
tree5ab58d56860db635542ea1ec24be258bd86b40b9 /src/shader_recompiler/backend
parentvk_graphics_pipeline: Implement conservative rendering (diff)
downloadyuzu-7dafa96ab59892b7f1fbffdb61e4326e6443955f.tar.gz
yuzu-7dafa96ab59892b7f1fbffdb61e4326e6443955f.tar.xz
yuzu-7dafa96ab59892b7f1fbffdb61e4326e6443955f.zip
shader: Rework varyings and implement passthrough geometry shaders
Put all varyings into a single std::bitset with helpers to access it. Implement passthrough geometry shaders using host's.
Diffstat (limited to 'src/shader_recompiler/backend')
-rw-r--r--src/shader_recompiler/backend/glasm/emit_context.cpp15
-rw-r--r--src/shader_recompiler/backend/glasm/emit_glasm.cpp6
-rw-r--r--src/shader_recompiler/backend/glasm/emit_glasm_context_get_set.cpp6
-rw-r--r--src/shader_recompiler/backend/glsl/emit_context.cpp58
-rw-r--r--src/shader_recompiler/backend/glsl/emit_glsl.cpp2
-rw-r--r--src/shader_recompiler/backend/glsl/emit_glsl_context_get_set.cpp2
-rw-r--r--src/shader_recompiler/backend/glsl/emit_glsl_special.cpp4
-rw-r--r--src/shader_recompiler/backend/spirv/emit_context.cpp97
-rw-r--r--src/shader_recompiler/backend/spirv/emit_context.h2
-rw-r--r--src/shader_recompiler/backend/spirv/emit_spirv.cpp19
-rw-r--r--src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp2
11 files changed, 116 insertions, 97 deletions
diff --git a/src/shader_recompiler/backend/glasm/emit_context.cpp b/src/shader_recompiler/backend/glasm/emit_context.cpp
index 21e14867c..80dad9ff3 100644
--- a/src/shader_recompiler/backend/glasm/emit_context.cpp
+++ b/src/shader_recompiler/backend/glasm/emit_context.cpp
@@ -83,14 +83,13 @@ EmitContext::EmitContext(IR::Program& program, Bindings& bindings, const Profile
83 break; 83 break;
84 } 84 }
85 const std::string_view attr_stage{stage == Stage::Fragment ? "fragment" : "vertex"}; 85 const std::string_view attr_stage{stage == Stage::Fragment ? "fragment" : "vertex"};
86 for (size_t index = 0; index < info.input_generics.size(); ++index) { 86 for (size_t index = 0; index < IR::NUM_GENERICS; ++index) {
87 const auto& generic{info.input_generics[index]}; 87 if (info.loads.Generic(index)) {
88 if (generic.used) {
89 Add("{}ATTRIB in_attr{}[]={{{}.attrib[{}..{}]}};", 88 Add("{}ATTRIB in_attr{}[]={{{}.attrib[{}..{}]}};",
90 InterpDecorator(generic.interpolation), index, attr_stage, index, index); 89 InterpDecorator(info.interpolation[index]), index, attr_stage, index, index);
91 } 90 }
92 } 91 }
93 if (IsInputArray(stage) && info.loads_position) { 92 if (IsInputArray(stage) && info.loads.AnyComponent(IR::Attribute::PositionX)) {
94 Add("ATTRIB vertex_position=vertex.position;"); 93 Add("ATTRIB vertex_position=vertex.position;");
95 } 94 }
96 if (info.uses_invocation_id) { 95 if (info.uses_invocation_id) {
@@ -102,7 +101,7 @@ EmitContext::EmitContext(IR::Program& program, Bindings& bindings, const Profile
102 if (info.stores_tess_level_inner) { 101 if (info.stores_tess_level_inner) {
103 Add("OUTPUT result_patch_tessinner[]={{result.patch.tessinner[0..1]}};"); 102 Add("OUTPUT result_patch_tessinner[]={{result.patch.tessinner[0..1]}};");
104 } 103 }
105 if (info.stores_clip_distance) { 104 if (info.stores.ClipDistances()) {
106 Add("OUTPUT result_clip[]={{result.clip[0..7]}};"); 105 Add("OUTPUT result_clip[]={{result.clip[0..7]}};");
107 } 106 }
108 for (size_t index = 0; index < info.uses_patches.size(); ++index) { 107 for (size_t index = 0; index < info.uses_patches.size(); ++index) {
@@ -124,8 +123,8 @@ EmitContext::EmitContext(IR::Program& program, Bindings& bindings, const Profile
124 Add("OUTPUT frag_color{}=result.color[{}];", index, index); 123 Add("OUTPUT frag_color{}=result.color[{}];", index, index);
125 } 124 }
126 } 125 }
127 for (size_t index = 0; index < info.stores_generics.size(); ++index) { 126 for (size_t index = 0; index < IR::NUM_GENERICS; ++index) {
128 if (info.stores_generics[index]) { 127 if (info.stores.Generic(index)) {
129 Add("OUTPUT out_attr{}[]={{result.attrib[{}..{}]}};", index, index, index); 128 Add("OUTPUT out_attr{}[]={{result.attrib[{}..{}]}};", index, index, index);
130 } 129 }
131 } 130 }
diff --git a/src/shader_recompiler/backend/glasm/emit_glasm.cpp b/src/shader_recompiler/backend/glasm/emit_glasm.cpp
index 79314f130..2b96977b3 100644
--- a/src/shader_recompiler/backend/glasm/emit_glasm.cpp
+++ b/src/shader_recompiler/backend/glasm/emit_glasm.cpp
@@ -296,8 +296,10 @@ void SetupOptions(const IR::Program& program, const Profile& profile,
296 if (info.uses_sparse_residency) { 296 if (info.uses_sparse_residency) {
297 header += "OPTION EXT_sparse_texture2;"; 297 header += "OPTION EXT_sparse_texture2;";
298 } 298 }
299 if (((info.stores_viewport_index || info.stores_layer) && stage != Stage::Geometry) || 299 const bool stores_viewport_layer{info.stores[IR::Attribute::ViewportIndex] ||
300 info.stores_viewport_mask) { 300 info.stores[IR::Attribute::Layer]};
301 if ((stage != Stage::Geometry && stores_viewport_layer) ||
302 info.stores[IR::Attribute::ViewportMask]) {
301 if (profile.support_viewport_index_layer_non_geometry) { 303 if (profile.support_viewport_index_layer_non_geometry) {
302 header += "OPTION NV_viewport_array2;"; 304 header += "OPTION NV_viewport_array2;";
303 } 305 }
diff --git a/src/shader_recompiler/backend/glasm/emit_glasm_context_get_set.cpp b/src/shader_recompiler/backend/glasm/emit_glasm_context_get_set.cpp
index bc195d248..02c9dc6d7 100644
--- a/src/shader_recompiler/backend/glasm/emit_glasm_context_get_set.cpp
+++ b/src/shader_recompiler/backend/glasm/emit_glasm_context_get_set.cpp
@@ -261,7 +261,7 @@ void EmitGetAttributeIndexed(EmitContext& ctx, IR::Inst& inst, ScalarS32 offset,
261 fmt::format("{}.z", value), fmt::format("{}.w", value)}; 261 fmt::format("{}.z", value), fmt::format("{}.w", value)};
262 read(compare_index, values); 262 read(compare_index, values);
263 }}; 263 }};
264 if (ctx.info.loads_position) { 264 if (ctx.info.loads.AnyComponent(IR::Attribute::PositionX)) {
265 const u32 index{static_cast<u32>(IR::Attribute::PositionX)}; 265 const u32 index{static_cast<u32>(IR::Attribute::PositionX)};
266 if (IsInputArray(ctx.stage)) { 266 if (IsInputArray(ctx.stage)) {
267 read_swizzled(index, fmt::format("vertex_position{}", VertexIndex(ctx, vertex))); 267 read_swizzled(index, fmt::format("vertex_position{}", VertexIndex(ctx, vertex)));
@@ -269,8 +269,8 @@ void EmitGetAttributeIndexed(EmitContext& ctx, IR::Inst& inst, ScalarS32 offset,
269 read_swizzled(index, fmt::format("{}.position", ctx.attrib_name)); 269 read_swizzled(index, fmt::format("{}.position", ctx.attrib_name));
270 } 270 }
271 } 271 }
272 for (u32 index = 0; index < ctx.info.input_generics.size(); ++index) { 272 for (u32 index = 0; index < static_cast<u32>(IR::NUM_GENERICS); ++index) {
273 if (!ctx.info.input_generics[index].used) { 273 if (!ctx.info.loads.Generic(index)) {
274 continue; 274 continue;
275 } 275 }
276 read_swizzled(index, fmt::format("in_attr{}{}[0]", index, VertexIndex(ctx, vertex))); 276 read_swizzled(index, fmt::format("in_attr{}{}[0]", index, VertexIndex(ctx, vertex)));
diff --git a/src/shader_recompiler/backend/glsl/emit_context.cpp b/src/shader_recompiler/backend/glsl/emit_context.cpp
index 14c009535..0d7f7bc3b 100644
--- a/src/shader_recompiler/backend/glsl/emit_context.cpp
+++ b/src/shader_recompiler/backend/glsl/emit_context.cpp
@@ -212,22 +212,22 @@ std::string_view OutputPrimitive(OutputTopology topology) {
212} 212}
213 213
214void SetupLegacyOutPerVertex(EmitContext& ctx, std::string& header) { 214void SetupLegacyOutPerVertex(EmitContext& ctx, std::string& header) {
215 if (!ctx.info.stores_legacy_varyings) { 215 if (!ctx.info.stores.Legacy()) {
216 return; 216 return;
217 } 217 }
218 if (ctx.info.stores_fixed_fnc_textures) { 218 if (ctx.info.stores.FixedFunctionTexture()) {
219 header += "vec4 gl_TexCoord[8];"; 219 header += "vec4 gl_TexCoord[8];";
220 } 220 }
221 if (ctx.info.stores_color_front_diffuse) { 221 if (ctx.info.stores.AnyComponent(IR::Attribute::ColorFrontDiffuseR)) {
222 header += "vec4 gl_FrontColor;"; 222 header += "vec4 gl_FrontColor;";
223 } 223 }
224 if (ctx.info.stores_color_front_specular) { 224 if (ctx.info.stores.AnyComponent(IR::Attribute::ColorFrontSpecularR)) {
225 header += "vec4 gl_FrontSecondaryColor;"; 225 header += "vec4 gl_FrontSecondaryColor;";
226 } 226 }
227 if (ctx.info.stores_color_back_diffuse) { 227 if (ctx.info.stores.AnyComponent(IR::Attribute::ColorBackDiffuseR)) {
228 header += "vec4 gl_BackColor;"; 228 header += "vec4 gl_BackColor;";
229 } 229 }
230 if (ctx.info.stores_color_back_specular) { 230 if (ctx.info.stores.AnyComponent(IR::Attribute::ColorBackSpecularR)) {
231 header += "vec4 gl_BackSecondaryColor;"; 231 header += "vec4 gl_BackSecondaryColor;";
232 } 232 }
233} 233}
@@ -237,32 +237,32 @@ void SetupOutPerVertex(EmitContext& ctx, std::string& header) {
237 return; 237 return;
238 } 238 }
239 header += "out gl_PerVertex{vec4 gl_Position;"; 239 header += "out gl_PerVertex{vec4 gl_Position;";
240 if (ctx.info.stores_point_size) { 240 if (ctx.info.stores[IR::Attribute::PointSize]) {
241 header += "float gl_PointSize;"; 241 header += "float gl_PointSize;";
242 } 242 }
243 if (ctx.info.stores_clip_distance) { 243 if (ctx.info.stores.ClipDistances()) {
244 header += "float gl_ClipDistance[];"; 244 header += "float gl_ClipDistance[];";
245 } 245 }
246 if (ctx.info.stores_viewport_index && ctx.profile.support_viewport_index_layer_non_geometry && 246 if (ctx.info.stores[IR::Attribute::ViewportIndex] &&
247 ctx.stage != Stage::Geometry) { 247 ctx.profile.support_viewport_index_layer_non_geometry && ctx.stage != Stage::Geometry) {
248 header += "int gl_ViewportIndex;"; 248 header += "int gl_ViewportIndex;";
249 } 249 }
250 SetupLegacyOutPerVertex(ctx, header); 250 SetupLegacyOutPerVertex(ctx, header);
251 header += "};"; 251 header += "};";
252 if (ctx.info.stores_viewport_index && ctx.stage == Stage::Geometry) { 252 if (ctx.info.stores[IR::Attribute::ViewportIndex] && ctx.stage == Stage::Geometry) {
253 header += "out int gl_ViewportIndex;"; 253 header += "out int gl_ViewportIndex;";
254 } 254 }
255} 255}
256 256
257void SetupLegacyInPerFragment(EmitContext& ctx, std::string& header) { 257void SetupLegacyInPerFragment(EmitContext& ctx, std::string& header) {
258 if (!ctx.info.loads_legacy_varyings) { 258 if (!ctx.info.loads.Legacy()) {
259 return; 259 return;
260 } 260 }
261 header += "in gl_PerFragment{"; 261 header += "in gl_PerFragment{";
262 if (ctx.info.loads_fixed_fnc_textures) { 262 if (ctx.info.loads.FixedFunctionTexture()) {
263 header += "vec4 gl_TexCoord[8];"; 263 header += "vec4 gl_TexCoord[8];";
264 } 264 }
265 if (ctx.info.loads_color_front_diffuse) { 265 if (ctx.info.loads.AnyComponent(IR::Attribute::ColorFrontDiffuseR)) {
266 header += "vec4 gl_Color;"; 266 header += "vec4 gl_Color;";
267 } 267 }
268 header += "};"; 268 header += "};";
@@ -325,14 +325,13 @@ EmitContext::EmitContext(IR::Program& program, Bindings& bindings, const Profile
325 SetupOutPerVertex(*this, header); 325 SetupOutPerVertex(*this, header);
326 SetupLegacyInPerFragment(*this, header); 326 SetupLegacyInPerFragment(*this, header);
327 327
328 for (size_t index = 0; index < info.input_generics.size(); ++index) { 328 for (size_t index = 0; index < IR::NUM_GENERICS; ++index) {
329 const auto& generic{info.input_generics[index]}; 329 if (!info.loads.Generic(index) || !runtime_info.previous_stage_stores.Generic(index)) {
330 if (!generic.used || !runtime_info.previous_stage_stores_generic[index]) {
331 continue; 330 continue;
332 } 331 }
333 header += 332 header += fmt::format("layout(location={}){}in vec4 in_attr{}{};", index,
334 fmt::format("layout(location={}){}in vec4 in_attr{}{};", index, 333 InterpDecorator(info.interpolation[index]), index,
335 InterpDecorator(generic.interpolation), index, InputArrayDecorator(stage)); 334 InputArrayDecorator(stage));
336 } 335 }
337 for (size_t index = 0; index < info.uses_patches.size(); ++index) { 336 for (size_t index = 0; index < info.uses_patches.size(); ++index) {
338 if (!info.uses_patches[index]) { 337 if (!info.uses_patches[index]) {
@@ -349,11 +348,10 @@ EmitContext::EmitContext(IR::Program& program, Bindings& bindings, const Profile
349 header += fmt::format("layout(location={})out vec4 frag_color{};", index, index); 348 header += fmt::format("layout(location={})out vec4 frag_color{};", index, index);
350 } 349 }
351 } 350 }
352 for (size_t index = 0; index < info.stores_generics.size(); ++index) { 351 for (size_t index = 0; index < IR::NUM_GENERICS; ++index) {
353 if (!info.stores_generics[index]) { 352 if (info.stores.Generic(index)) {
354 continue; 353 DefineGenericOutput(index, program.invocations);
355 } 354 }
356 DefineGenericOutput(index, program.invocations);
357 } 355 }
358 DefineConstantBuffers(bindings); 356 DefineConstantBuffers(bindings);
359 DefineStorageBuffers(bindings); 357 DefineStorageBuffers(bindings);
@@ -398,14 +396,14 @@ void EmitContext::SetupExtensions() {
398 header += "#extension GL_NV_shader_thread_shuffle : enable\n"; 396 header += "#extension GL_NV_shader_thread_shuffle : enable\n";
399 } 397 }
400 } 398 }
401 if ((info.stores_viewport_index || info.stores_layer) && 399 if ((info.stores[IR::Attribute::ViewportIndex] || info.stores[IR::Attribute::Layer]) &&
402 profile.support_viewport_index_layer_non_geometry && stage != Stage::Geometry) { 400 profile.support_viewport_index_layer_non_geometry && stage != Stage::Geometry) {
403 header += "#extension GL_ARB_shader_viewport_layer_array : enable\n"; 401 header += "#extension GL_ARB_shader_viewport_layer_array : enable\n";
404 } 402 }
405 if (info.uses_sparse_residency && profile.support_gl_sparse_textures) { 403 if (info.uses_sparse_residency && profile.support_gl_sparse_textures) {
406 header += "#extension GL_ARB_sparse_texture2 : enable\n"; 404 header += "#extension GL_ARB_sparse_texture2 : enable\n";
407 } 405 }
408 if (info.stores_viewport_mask && profile.support_viewport_mask) { 406 if (info.stores[IR::Attribute::ViewportMask] && profile.support_viewport_mask) {
409 header += "#extension GL_NV_viewport_array2 : enable\n"; 407 header += "#extension GL_NV_viewport_array2 : enable\n";
410 } 408 }
411 if (info.uses_typeless_image_reads) { 409 if (info.uses_typeless_image_reads) {
@@ -535,20 +533,20 @@ void EmitContext::DefineHelperFunctions() {
535 fmt::format("float IndexedAttrLoad(int offset{}){{int base_index=offset>>2;uint " 533 fmt::format("float IndexedAttrLoad(int offset{}){{int base_index=offset>>2;uint "
536 "masked_index=uint(base_index)&3u;switch(base_index>>2){{", 534 "masked_index=uint(base_index)&3u;switch(base_index>>2){{",
537 vertex_arg)}; 535 vertex_arg)};
538 if (info.loads_position) { 536 if (info.loads.AnyComponent(IR::Attribute::PositionX)) {
539 const auto position_idx{is_array ? "gl_in[vertex]." : ""}; 537 const auto position_idx{is_array ? "gl_in[vertex]." : ""};
540 func += fmt::format("case {}:return {}{}[masked_index];", 538 func += fmt::format("case {}:return {}{}[masked_index];",
541 static_cast<u32>(IR::Attribute::PositionX) >> 2, position_idx, 539 static_cast<u32>(IR::Attribute::PositionX) >> 2, position_idx,
542 position_name); 540 position_name);
543 } 541 }
544 const u32 base_attribute_value = static_cast<u32>(IR::Attribute::Generic0X) >> 2; 542 const u32 base_attribute_value = static_cast<u32>(IR::Attribute::Generic0X) >> 2;
545 for (u32 i = 0; i < info.input_generics.size(); ++i) { 543 for (u32 index = 0; index < IR::NUM_GENERICS; ++index) {
546 if (!info.input_generics[i].used) { 544 if (!info.loads.Generic(index)) {
547 continue; 545 continue;
548 } 546 }
549 const auto vertex_idx{is_array ? "[vertex]" : ""}; 547 const auto vertex_idx{is_array ? "[vertex]" : ""};
550 func += fmt::format("case {}:return in_attr{}{}[masked_index];", 548 func += fmt::format("case {}:return in_attr{}{}[masked_index];",
551 base_attribute_value + i, i, vertex_idx); 549 base_attribute_value + index, index, vertex_idx);
552 } 550 }
553 func += "default: return 0.0;}}"; 551 func += "default: return 0.0;}}";
554 header += func; 552 header += func;
diff --git a/src/shader_recompiler/backend/glsl/emit_glsl.cpp b/src/shader_recompiler/backend/glsl/emit_glsl.cpp
index 32c4f1da2..8deaf5760 100644
--- a/src/shader_recompiler/backend/glsl/emit_glsl.cpp
+++ b/src/shader_recompiler/backend/glsl/emit_glsl.cpp
@@ -171,7 +171,7 @@ void EmitCode(EmitContext& ctx, const IR::Program& program) {
171} 171}
172 172
173std::string GlslVersionSpecifier(const EmitContext& ctx) { 173std::string GlslVersionSpecifier(const EmitContext& ctx) {
174 if (ctx.uses_y_direction || ctx.info.stores_legacy_varyings || ctx.info.loads_legacy_varyings) { 174 if (ctx.uses_y_direction || ctx.info.stores.Legacy() || ctx.info.loads.Legacy()) {
175 return " compatibility"; 175 return " compatibility";
176 } 176 }
177 return ""; 177 return "";
diff --git a/src/shader_recompiler/backend/glsl/emit_glsl_context_get_set.cpp b/src/shader_recompiler/backend/glsl/emit_glsl_context_get_set.cpp
index 3d2ba2eee..16e2a8502 100644
--- a/src/shader_recompiler/backend/glsl/emit_glsl_context_get_set.cpp
+++ b/src/shader_recompiler/backend/glsl/emit_glsl_context_get_set.cpp
@@ -179,7 +179,7 @@ void EmitGetAttribute(EmitContext& ctx, IR::Inst& inst, IR::Attribute attr,
179 const char swizzle{"xyzw"[element]}; 179 const char swizzle{"xyzw"[element]};
180 if (IR::IsGeneric(attr)) { 180 if (IR::IsGeneric(attr)) {
181 const u32 index{IR::GenericAttributeIndex(attr)}; 181 const u32 index{IR::GenericAttributeIndex(attr)};
182 if (!ctx.runtime_info.previous_stage_stores_generic[index]) { 182 if (!ctx.runtime_info.previous_stage_stores.Generic(index)) {
183 ctx.AddF32("{}=0.f;", inst, attr); 183 ctx.AddF32("{}=0.f;", inst, attr);
184 return; 184 return;
185 } 185 }
diff --git a/src/shader_recompiler/backend/glsl/emit_glsl_special.cpp b/src/shader_recompiler/backend/glsl/emit_glsl_special.cpp
index 6420aaa21..298881c7b 100644
--- a/src/shader_recompiler/backend/glsl/emit_glsl_special.cpp
+++ b/src/shader_recompiler/backend/glsl/emit_glsl_special.cpp
@@ -20,8 +20,8 @@ void InitializeOutputVaryings(EmitContext& ctx) {
20 if (ctx.stage == Stage::VertexB || ctx.stage == Stage::Geometry) { 20 if (ctx.stage == Stage::VertexB || ctx.stage == Stage::Geometry) {
21 ctx.Add("gl_Position=vec4(0,0,0,1);"); 21 ctx.Add("gl_Position=vec4(0,0,0,1);");
22 } 22 }
23 for (size_t index = 0; index < ctx.info.stores_generics.size(); ++index) { 23 for (size_t index = 0; index < IR::NUM_GENERICS; ++index) {
24 if (!ctx.info.stores_generics[index]) { 24 if (!ctx.info.stores.Generic(index)) {
25 continue; 25 continue;
26 } 26 }
27 const auto& info_array{ctx.output_generics.at(index)}; 27 const auto& info_array{ctx.output_generics.at(index)};
diff --git a/src/shader_recompiler/backend/spirv/emit_context.cpp b/src/shader_recompiler/backend/spirv/emit_context.cpp
index 4c6501129..af4fb0c69 100644
--- a/src/shader_recompiler/backend/spirv/emit_context.cpp
+++ b/src/shader_recompiler/backend/spirv/emit_context.cpp
@@ -557,7 +557,7 @@ void EmitContext::DefineCommonConstants() {
557} 557}
558 558
559void EmitContext::DefineInterfaces(const IR::Program& program) { 559void EmitContext::DefineInterfaces(const IR::Program& program) {
560 DefineInputs(program.info); 560 DefineInputs(program);
561 DefineOutputs(program); 561 DefineOutputs(program);
562} 562}
563 563
@@ -693,16 +693,16 @@ void EmitContext::DefineAttributeMemAccess(const Info& info) {
693 const Id compare_index{OpShiftRightArithmetic(U32[1], base_index, Const(2U))}; 693 const Id compare_index{OpShiftRightArithmetic(U32[1], base_index, Const(2U))};
694 std::vector<Sirit::Literal> literals; 694 std::vector<Sirit::Literal> literals;
695 std::vector<Id> labels; 695 std::vector<Id> labels;
696 if (info.loads_position) { 696 if (info.loads.AnyComponent(IR::Attribute::PositionX)) {
697 literals.push_back(static_cast<u32>(IR::Attribute::PositionX) >> 2); 697 literals.push_back(static_cast<u32>(IR::Attribute::PositionX) >> 2);
698 labels.push_back(OpLabel()); 698 labels.push_back(OpLabel());
699 } 699 }
700 const u32 base_attribute_value = static_cast<u32>(IR::Attribute::Generic0X) >> 2; 700 const u32 base_attribute_value = static_cast<u32>(IR::Attribute::Generic0X) >> 2;
701 for (u32 i = 0; i < info.input_generics.size(); ++i) { 701 for (u32 index = 0; index < static_cast<u32>(IR::NUM_GENERICS); ++index) {
702 if (!info.input_generics[i].used) { 702 if (!info.loads.Generic(index)) {
703 continue; 703 continue;
704 } 704 }
705 literals.push_back(base_attribute_value + i); 705 literals.push_back(base_attribute_value + index);
706 labels.push_back(OpLabel()); 706 labels.push_back(OpLabel());
707 } 707 }
708 OpSelectionMerge(end_block, spv::SelectionControlMask::MaskNone); 708 OpSelectionMerge(end_block, spv::SelectionControlMask::MaskNone);
@@ -710,7 +710,7 @@ void EmitContext::DefineAttributeMemAccess(const Info& info) {
710 AddLabel(default_label); 710 AddLabel(default_label);
711 OpReturnValue(Const(0.0f)); 711 OpReturnValue(Const(0.0f));
712 size_t label_index{0}; 712 size_t label_index{0};
713 if (info.loads_position) { 713 if (info.loads.AnyComponent(IR::Attribute::PositionX)) {
714 AddLabel(labels[label_index]); 714 AddLabel(labels[label_index]);
715 const Id pointer{is_array 715 const Id pointer{is_array
716 ? OpAccessChain(input_f32, input_position, vertex, masked_index) 716 ? OpAccessChain(input_f32, input_position, vertex, masked_index)
@@ -719,18 +719,18 @@ void EmitContext::DefineAttributeMemAccess(const Info& info) {
719 OpReturnValue(result); 719 OpReturnValue(result);
720 ++label_index; 720 ++label_index;
721 } 721 }
722 for (size_t i = 0; i < info.input_generics.size(); i++) { 722 for (size_t index = 0; index < IR::NUM_GENERICS; ++index) {
723 if (!info.input_generics[i].used) { 723 if (!info.loads.Generic(index)) {
724 continue; 724 continue;
725 } 725 }
726 AddLabel(labels[label_index]); 726 AddLabel(labels[label_index]);
727 const auto type{AttrTypes(*this, static_cast<u32>(i))}; 727 const auto type{AttrTypes(*this, static_cast<u32>(index))};
728 if (!type) { 728 if (!type) {
729 OpReturnValue(Const(0.0f)); 729 OpReturnValue(Const(0.0f));
730 ++label_index; 730 ++label_index;
731 continue; 731 continue;
732 } 732 }
733 const Id generic_id{input_generics.at(i)}; 733 const Id generic_id{input_generics.at(index)};
734 const Id pointer{is_array 734 const Id pointer{is_array
735 ? OpAccessChain(type->pointer, generic_id, vertex, masked_index) 735 ? OpAccessChain(type->pointer, generic_id, vertex, masked_index)
736 : OpAccessChain(type->pointer, generic_id, masked_index)}; 736 : OpAccessChain(type->pointer, generic_id, masked_index)};
@@ -758,19 +758,19 @@ void EmitContext::DefineAttributeMemAccess(const Info& info) {
758 const Id compare_index{OpShiftRightArithmetic(U32[1], base_index, Const(2U))}; 758 const Id compare_index{OpShiftRightArithmetic(U32[1], base_index, Const(2U))};
759 std::vector<Sirit::Literal> literals; 759 std::vector<Sirit::Literal> literals;
760 std::vector<Id> labels; 760 std::vector<Id> labels;
761 if (info.stores_position) { 761 if (info.stores.AnyComponent(IR::Attribute::PositionX)) {
762 literals.push_back(static_cast<u32>(IR::Attribute::PositionX) >> 2); 762 literals.push_back(static_cast<u32>(IR::Attribute::PositionX) >> 2);
763 labels.push_back(OpLabel()); 763 labels.push_back(OpLabel());
764 } 764 }
765 const u32 base_attribute_value = static_cast<u32>(IR::Attribute::Generic0X) >> 2; 765 const u32 base_attribute_value = static_cast<u32>(IR::Attribute::Generic0X) >> 2;
766 for (size_t i = 0; i < info.stores_generics.size(); i++) { 766 for (size_t index = 0; index < IR::NUM_GENERICS; ++index) {
767 if (!info.stores_generics[i]) { 767 if (!info.stores.Generic(index)) {
768 continue; 768 continue;
769 } 769 }
770 literals.push_back(base_attribute_value + static_cast<u32>(i)); 770 literals.push_back(base_attribute_value + static_cast<u32>(index));
771 labels.push_back(OpLabel()); 771 labels.push_back(OpLabel());
772 } 772 }
773 if (info.stores_clip_distance) { 773 if (info.stores.ClipDistances()) {
774 literals.push_back(static_cast<u32>(IR::Attribute::ClipDistance0) >> 2); 774 literals.push_back(static_cast<u32>(IR::Attribute::ClipDistance0) >> 2);
775 labels.push_back(OpLabel()); 775 labels.push_back(OpLabel());
776 literals.push_back(static_cast<u32>(IR::Attribute::ClipDistance4) >> 2); 776 literals.push_back(static_cast<u32>(IR::Attribute::ClipDistance4) >> 2);
@@ -781,28 +781,28 @@ void EmitContext::DefineAttributeMemAccess(const Info& info) {
781 AddLabel(default_label); 781 AddLabel(default_label);
782 OpReturn(); 782 OpReturn();
783 size_t label_index{0}; 783 size_t label_index{0};
784 if (info.stores_position) { 784 if (info.stores.AnyComponent(IR::Attribute::PositionX)) {
785 AddLabel(labels[label_index]); 785 AddLabel(labels[label_index]);
786 const Id pointer{OpAccessChain(output_f32, output_position, masked_index)}; 786 const Id pointer{OpAccessChain(output_f32, output_position, masked_index)};
787 OpStore(pointer, store_value); 787 OpStore(pointer, store_value);
788 OpReturn(); 788 OpReturn();
789 ++label_index; 789 ++label_index;
790 } 790 }
791 for (size_t i = 0; i < info.stores_generics.size(); ++i) { 791 for (size_t index = 0; index < IR::NUM_GENERICS; ++index) {
792 if (!info.stores_generics[i]) { 792 if (!info.stores.Generic(index)) {
793 continue; 793 continue;
794 } 794 }
795 if (output_generics[i][0].num_components != 4) { 795 if (output_generics[index][0].num_components != 4) {
796 throw NotImplementedException("Physical stores and transform feedbacks"); 796 throw NotImplementedException("Physical stores and transform feedbacks");
797 } 797 }
798 AddLabel(labels[label_index]); 798 AddLabel(labels[label_index]);
799 const Id generic_id{output_generics[i][0].id}; 799 const Id generic_id{output_generics[index][0].id};
800 const Id pointer{OpAccessChain(output_f32, generic_id, masked_index)}; 800 const Id pointer{OpAccessChain(output_f32, generic_id, masked_index)};
801 OpStore(pointer, store_value); 801 OpStore(pointer, store_value);
802 OpReturn(); 802 OpReturn();
803 ++label_index; 803 ++label_index;
804 } 804 }
805 if (info.stores_clip_distance) { 805 if (info.stores.ClipDistances()) {
806 AddLabel(labels[label_index]); 806 AddLabel(labels[label_index]);
807 const Id pointer{OpAccessChain(output_f32, clip_distances, masked_index)}; 807 const Id pointer{OpAccessChain(output_f32, clip_distances, masked_index)};
808 OpStore(pointer, store_value); 808 OpStore(pointer, store_value);
@@ -1146,7 +1146,10 @@ void EmitContext::DefineImages(const Info& info, u32& binding) {
1146 } 1146 }
1147} 1147}
1148 1148
1149void EmitContext::DefineInputs(const Info& info) { 1149void EmitContext::DefineInputs(const IR::Program& program) {
1150 const Info& info{program.info};
1151 const VaryingState loads{info.loads.mask | info.passthrough.mask};
1152
1150 if (info.uses_workgroup_id) { 1153 if (info.uses_workgroup_id) {
1151 workgroup_id = DefineInput(*this, U32[3], false, spv::BuiltIn::WorkgroupId); 1154 workgroup_id = DefineInput(*this, U32[3], false, spv::BuiltIn::WorkgroupId);
1152 } 1155 }
@@ -1183,15 +1186,20 @@ void EmitContext::DefineInputs(const Info& info) {
1183 fswzadd_lut_b = 1186 fswzadd_lut_b =
1184 ConstantComposite(F32[4], f32_minus_one, f32_minus_one, f32_one, f32_minus_one); 1187 ConstantComposite(F32[4], f32_minus_one, f32_minus_one, f32_one, f32_minus_one);
1185 } 1188 }
1186 if (info.loads_primitive_id) { 1189 if (loads[IR::Attribute::PrimitiveId]) {
1187 primitive_id = DefineInput(*this, U32[1], false, spv::BuiltIn::PrimitiveId); 1190 primitive_id = DefineInput(*this, U32[1], false, spv::BuiltIn::PrimitiveId);
1188 } 1191 }
1189 if (info.loads_position) { 1192 if (loads.AnyComponent(IR::Attribute::PositionX)) {
1190 const bool is_fragment{stage != Stage::Fragment}; 1193 const bool is_fragment{stage != Stage::Fragment};
1191 const spv::BuiltIn built_in{is_fragment ? spv::BuiltIn::Position : spv::BuiltIn::FragCoord}; 1194 const spv::BuiltIn built_in{is_fragment ? spv::BuiltIn::Position : spv::BuiltIn::FragCoord};
1192 input_position = DefineInput(*this, F32[4], true, built_in); 1195 input_position = DefineInput(*this, F32[4], true, built_in);
1196 if (profile.support_geometry_shader_passthrough) {
1197 if (info.passthrough.AnyComponent(IR::Attribute::PositionX)) {
1198 Decorate(input_position, spv::Decoration::PassthroughNV);
1199 }
1200 }
1193 } 1201 }
1194 if (info.loads_instance_id) { 1202 if (loads[IR::Attribute::InstanceId]) {
1195 if (profile.support_vertex_instance_id) { 1203 if (profile.support_vertex_instance_id) {
1196 instance_id = DefineInput(*this, U32[1], true, spv::BuiltIn::InstanceId); 1204 instance_id = DefineInput(*this, U32[1], true, spv::BuiltIn::InstanceId);
1197 } else { 1205 } else {
@@ -1199,7 +1207,7 @@ void EmitContext::DefineInputs(const Info& info) {
1199 base_instance = DefineInput(*this, U32[1], true, spv::BuiltIn::BaseInstance); 1207 base_instance = DefineInput(*this, U32[1], true, spv::BuiltIn::BaseInstance);
1200 } 1208 }
1201 } 1209 }
1202 if (info.loads_vertex_id) { 1210 if (loads[IR::Attribute::VertexId]) {
1203 if (profile.support_vertex_instance_id) { 1211 if (profile.support_vertex_instance_id) {
1204 vertex_id = DefineInput(*this, U32[1], true, spv::BuiltIn::VertexId); 1212 vertex_id = DefineInput(*this, U32[1], true, spv::BuiltIn::VertexId);
1205 } else { 1213 } else {
@@ -1207,24 +1215,24 @@ void EmitContext::DefineInputs(const Info& info) {
1207 base_vertex = DefineInput(*this, U32[1], true, spv::BuiltIn::BaseVertex); 1215 base_vertex = DefineInput(*this, U32[1], true, spv::BuiltIn::BaseVertex);
1208 } 1216 }
1209 } 1217 }
1210 if (info.loads_front_face) { 1218 if (loads[IR::Attribute::FrontFace]) {
1211 front_face = DefineInput(*this, U1, true, spv::BuiltIn::FrontFacing); 1219 front_face = DefineInput(*this, U1, true, spv::BuiltIn::FrontFacing);
1212 } 1220 }
1213 if (info.loads_point_coord) { 1221 if (loads[IR::Attribute::PointSpriteS] || loads[IR::Attribute::PointSpriteT]) {
1214 point_coord = DefineInput(*this, F32[2], true, spv::BuiltIn::PointCoord); 1222 point_coord = DefineInput(*this, F32[2], true, spv::BuiltIn::PointCoord);
1215 } 1223 }
1216 if (info.loads_tess_coord) { 1224 if (loads[IR::Attribute::TessellationEvaluationPointU] ||
1225 loads[IR::Attribute::TessellationEvaluationPointV]) {
1217 tess_coord = DefineInput(*this, F32[3], false, spv::BuiltIn::TessCoord); 1226 tess_coord = DefineInput(*this, F32[3], false, spv::BuiltIn::TessCoord);
1218 } 1227 }
1219 for (size_t index = 0; index < info.input_generics.size(); ++index) { 1228 for (size_t index = 0; index < IR::NUM_GENERICS; ++index) {
1220 if (!runtime_info.previous_stage_stores_generic[index]) { 1229 const AttributeType input_type{runtime_info.generic_input_types[index]};
1230 if (!runtime_info.previous_stage_stores.Generic(index)) {
1221 continue; 1231 continue;
1222 } 1232 }
1223 const InputVarying generic{info.input_generics[index]}; 1233 if (!loads.Generic(index)) {
1224 if (!generic.used) {
1225 continue; 1234 continue;
1226 } 1235 }
1227 const AttributeType input_type{runtime_info.generic_input_types[index]};
1228 if (input_type == AttributeType::Disabled) { 1236 if (input_type == AttributeType::Disabled) {
1229 continue; 1237 continue;
1230 } 1238 }
@@ -1234,10 +1242,13 @@ void EmitContext::DefineInputs(const Info& info) {
1234 Name(id, fmt::format("in_attr{}", index)); 1242 Name(id, fmt::format("in_attr{}", index));
1235 input_generics[index] = id; 1243 input_generics[index] = id;
1236 1244
1245 if (info.passthrough.Generic(index) && profile.support_geometry_shader_passthrough) {
1246 Decorate(id, spv::Decoration::PassthroughNV);
1247 }
1237 if (stage != Stage::Fragment) { 1248 if (stage != Stage::Fragment) {
1238 continue; 1249 continue;
1239 } 1250 }
1240 switch (generic.interpolation) { 1251 switch (info.interpolation[index]) {
1241 case Interpolation::Smooth: 1252 case Interpolation::Smooth:
1242 // Default 1253 // Default
1243 // Decorate(id, spv::Decoration::Smooth); 1254 // Decorate(id, spv::Decoration::Smooth);
@@ -1266,42 +1277,42 @@ void EmitContext::DefineInputs(const Info& info) {
1266void EmitContext::DefineOutputs(const IR::Program& program) { 1277void EmitContext::DefineOutputs(const IR::Program& program) {
1267 const Info& info{program.info}; 1278 const Info& info{program.info};
1268 const std::optional<u32> invocations{program.invocations}; 1279 const std::optional<u32> invocations{program.invocations};
1269 if (info.stores_position || stage == Stage::VertexB) { 1280 if (info.stores.AnyComponent(IR::Attribute::PositionX) || stage == Stage::VertexB) {
1270 output_position = DefineOutput(*this, F32[4], invocations, spv::BuiltIn::Position); 1281 output_position = DefineOutput(*this, F32[4], invocations, spv::BuiltIn::Position);
1271 } 1282 }
1272 if (info.stores_point_size || runtime_info.fixed_state_point_size) { 1283 if (info.stores[IR::Attribute::PointSize] || runtime_info.fixed_state_point_size) {
1273 if (stage == Stage::Fragment) { 1284 if (stage == Stage::Fragment) {
1274 throw NotImplementedException("Storing PointSize in fragment stage"); 1285 throw NotImplementedException("Storing PointSize in fragment stage");
1275 } 1286 }
1276 output_point_size = DefineOutput(*this, F32[1], invocations, spv::BuiltIn::PointSize); 1287 output_point_size = DefineOutput(*this, F32[1], invocations, spv::BuiltIn::PointSize);
1277 } 1288 }
1278 if (info.stores_clip_distance) { 1289 if (info.stores.ClipDistances()) {
1279 if (stage == Stage::Fragment) { 1290 if (stage == Stage::Fragment) {
1280 throw NotImplementedException("Storing ClipDistance in fragment stage"); 1291 throw NotImplementedException("Storing ClipDistance in fragment stage");
1281 } 1292 }
1282 const Id type{TypeArray(F32[1], Const(8U))}; 1293 const Id type{TypeArray(F32[1], Const(8U))};
1283 clip_distances = DefineOutput(*this, type, invocations, spv::BuiltIn::ClipDistance); 1294 clip_distances = DefineOutput(*this, type, invocations, spv::BuiltIn::ClipDistance);
1284 } 1295 }
1285 if (info.stores_layer && 1296 if (info.stores[IR::Attribute::Layer] &&
1286 (profile.support_viewport_index_layer_non_geometry || stage == Stage::Geometry)) { 1297 (profile.support_viewport_index_layer_non_geometry || stage == Stage::Geometry)) {
1287 if (stage == Stage::Fragment) { 1298 if (stage == Stage::Fragment) {
1288 throw NotImplementedException("Storing Layer in fragment stage"); 1299 throw NotImplementedException("Storing Layer in fragment stage");
1289 } 1300 }
1290 layer = DefineOutput(*this, U32[1], invocations, spv::BuiltIn::Layer); 1301 layer = DefineOutput(*this, U32[1], invocations, spv::BuiltIn::Layer);
1291 } 1302 }
1292 if (info.stores_viewport_index && 1303 if (info.stores[IR::Attribute::ViewportIndex] &&
1293 (profile.support_viewport_index_layer_non_geometry || stage == Stage::Geometry)) { 1304 (profile.support_viewport_index_layer_non_geometry || stage == Stage::Geometry)) {
1294 if (stage == Stage::Fragment) { 1305 if (stage == Stage::Fragment) {
1295 throw NotImplementedException("Storing ViewportIndex in fragment stage"); 1306 throw NotImplementedException("Storing ViewportIndex in fragment stage");
1296 } 1307 }
1297 viewport_index = DefineOutput(*this, U32[1], invocations, spv::BuiltIn::ViewportIndex); 1308 viewport_index = DefineOutput(*this, U32[1], invocations, spv::BuiltIn::ViewportIndex);
1298 } 1309 }
1299 if (info.stores_viewport_mask && profile.support_viewport_mask) { 1310 if (info.stores[IR::Attribute::ViewportMask] && profile.support_viewport_mask) {
1300 viewport_mask = DefineOutput(*this, TypeArray(U32[1], Const(1u)), std::nullopt, 1311 viewport_mask = DefineOutput(*this, TypeArray(U32[1], Const(1u)), std::nullopt,
1301 spv::BuiltIn::ViewportMaskNV); 1312 spv::BuiltIn::ViewportMaskNV);
1302 } 1313 }
1303 for (size_t index = 0; index < info.stores_generics.size(); ++index) { 1314 for (size_t index = 0; index < IR::NUM_GENERICS; ++index) {
1304 if (info.stores_generics[index]) { 1315 if (info.stores.Generic(index)) {
1305 DefineGenericOutput(*this, index, invocations); 1316 DefineGenericOutput(*this, index, invocations);
1306 } 1317 }
1307 } 1318 }
diff --git a/src/shader_recompiler/backend/spirv/emit_context.h b/src/shader_recompiler/backend/spirv/emit_context.h
index 527685fb8..e277bc358 100644
--- a/src/shader_recompiler/backend/spirv/emit_context.h
+++ b/src/shader_recompiler/backend/spirv/emit_context.h
@@ -300,7 +300,7 @@ private:
300 void DefineAttributeMemAccess(const Info& info); 300 void DefineAttributeMemAccess(const Info& info);
301 void DefineGlobalMemoryFunctions(const Info& info); 301 void DefineGlobalMemoryFunctions(const Info& info);
302 302
303 void DefineInputs(const Info& info); 303 void DefineInputs(const IR::Program& program);
304 void DefineOutputs(const IR::Program& program); 304 void DefineOutputs(const IR::Program& program);
305}; 305};
306 306
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv.cpp b/src/shader_recompiler/backend/spirv/emit_spirv.cpp
index 278c262f8..ddb86d070 100644
--- a/src/shader_recompiler/backend/spirv/emit_spirv.cpp
+++ b/src/shader_recompiler/backend/spirv/emit_spirv.cpp
@@ -281,11 +281,19 @@ void DefineEntryPoint(const IR::Program& program, EmitContext& ctx, Id main) {
281 ctx.AddExecutionMode(main, spv::ExecutionMode::OutputTriangleStrip); 281 ctx.AddExecutionMode(main, spv::ExecutionMode::OutputTriangleStrip);
282 break; 282 break;
283 } 283 }
284 if (program.info.stores_point_size) { 284 if (program.info.stores[IR::Attribute::PointSize]) {
285 ctx.AddCapability(spv::Capability::GeometryPointSize); 285 ctx.AddCapability(spv::Capability::GeometryPointSize);
286 } 286 }
287 ctx.AddExecutionMode(main, spv::ExecutionMode::OutputVertices, program.output_vertices); 287 ctx.AddExecutionMode(main, spv::ExecutionMode::OutputVertices, program.output_vertices);
288 ctx.AddExecutionMode(main, spv::ExecutionMode::Invocations, program.invocations); 288 ctx.AddExecutionMode(main, spv::ExecutionMode::Invocations, program.invocations);
289 if (program.is_geometry_passthrough) {
290 if (ctx.profile.support_geometry_shader_passthrough) {
291 ctx.AddExtension("SPV_NV_geometry_shader_passthrough");
292 ctx.AddCapability(spv::Capability::GeometryShaderPassthroughNV);
293 } else {
294 LOG_WARNING(Shader_SPIRV, "Geometry shader passthrough used with no support");
295 }
296 }
289 break; 297 break;
290 case Stage::Fragment: 298 case Stage::Fragment:
291 execution_model = spv::ExecutionModel::Fragment; 299 execution_model = spv::ExecutionModel::Fragment;
@@ -377,20 +385,21 @@ void SetupCapabilities(const Profile& profile, const Info& info, EmitContext& ct
377 ctx.AddExtension("SPV_EXT_demote_to_helper_invocation"); 385 ctx.AddExtension("SPV_EXT_demote_to_helper_invocation");
378 ctx.AddCapability(spv::Capability::DemoteToHelperInvocationEXT); 386 ctx.AddCapability(spv::Capability::DemoteToHelperInvocationEXT);
379 } 387 }
380 if (info.stores_viewport_index) { 388 if (info.stores[IR::Attribute::ViewportIndex]) {
381 ctx.AddCapability(spv::Capability::MultiViewport); 389 ctx.AddCapability(spv::Capability::MultiViewport);
382 } 390 }
383 if (info.stores_viewport_mask && profile.support_viewport_mask) { 391 if (info.stores[IR::Attribute::ViewportMask] && profile.support_viewport_mask) {
384 ctx.AddExtension("SPV_NV_viewport_array2"); 392 ctx.AddExtension("SPV_NV_viewport_array2");
385 ctx.AddCapability(spv::Capability::ShaderViewportMaskNV); 393 ctx.AddCapability(spv::Capability::ShaderViewportMaskNV);
386 } 394 }
387 if (info.stores_layer || info.stores_viewport_index) { 395 if (info.stores[IR::Attribute::Layer] || info.stores[IR::Attribute::ViewportIndex]) {
388 if (profile.support_viewport_index_layer_non_geometry && ctx.stage != Stage::Geometry) { 396 if (profile.support_viewport_index_layer_non_geometry && ctx.stage != Stage::Geometry) {
389 ctx.AddExtension("SPV_EXT_shader_viewport_index_layer"); 397 ctx.AddExtension("SPV_EXT_shader_viewport_index_layer");
390 ctx.AddCapability(spv::Capability::ShaderViewportIndexLayerEXT); 398 ctx.AddCapability(spv::Capability::ShaderViewportIndexLayerEXT);
391 } 399 }
392 } 400 }
393 if (!profile.support_vertex_instance_id && (info.loads_instance_id || info.loads_vertex_id)) { 401 if (!profile.support_vertex_instance_id &&
402 (info.loads[IR::Attribute::InstanceId] || info.loads[IR::Attribute::VertexId])) {
394 ctx.AddExtension("SPV_KHR_shader_draw_parameters"); 403 ctx.AddExtension("SPV_KHR_shader_draw_parameters");
395 ctx.AddCapability(spv::Capability::DrawParameters); 404 ctx.AddCapability(spv::Capability::DrawParameters);
396 } 405 }
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp
index 85bd72389..77fbb2b2f 100644
--- a/src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp
+++ b/src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp
@@ -298,7 +298,7 @@ Id EmitGetAttribute(EmitContext& ctx, IR::Attribute attr, Id vertex) {
298 if (IR::IsGeneric(attr)) { 298 if (IR::IsGeneric(attr)) {
299 const u32 index{IR::GenericAttributeIndex(attr)}; 299 const u32 index{IR::GenericAttributeIndex(attr)};
300 const std::optional<AttrInfo> type{AttrTypes(ctx, index)}; 300 const std::optional<AttrInfo> type{AttrTypes(ctx, index)};
301 if (!type || !ctx.runtime_info.previous_stage_stores_generic[index]) { 301 if (!type || !ctx.runtime_info.previous_stage_stores.Generic(index)) {
302 // Attribute is disabled 302 // Attribute is disabled
303 return ctx.Const(0.0f); 303 return ctx.Const(0.0f);
304 } 304 }