summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorGravatar ReinUsesLisp2021-04-15 22:46:11 -0300
committerGravatar ameerj2021-07-22 21:51:27 -0400
commit183855e396cc6918d36fbf3e38ea426e934b4e3e (patch)
treea665794753520c09a1d34d8a086352894ec1cb72 /src
parentshader: Mark atomic instructions as writes (diff)
downloadyuzu-183855e396cc6918d36fbf3e38ea426e934b4e3e.tar.gz
yuzu-183855e396cc6918d36fbf3e38ea426e934b4e3e.tar.xz
yuzu-183855e396cc6918d36fbf3e38ea426e934b4e3e.zip
shader: Implement tessellation shaders, polygon mode and invocation id
Diffstat (limited to 'src')
-rw-r--r--src/shader_recompiler/CMakeLists.txt2
-rw-r--r--src/shader_recompiler/backend/spirv/emit_context.cpp147
-rw-r--r--src/shader_recompiler/backend/spirv/emit_context.h10
-rw-r--r--src/shader_recompiler/backend/spirv/emit_spirv.cpp39
-rw-r--r--src/shader_recompiler/backend/spirv/emit_spirv.h3
-rw-r--r--src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp88
-rw-r--r--src/shader_recompiler/frontend/ir/ir_emitter.cpp12
-rw-r--r--src/shader_recompiler/frontend/ir/ir_emitter.h4
-rw-r--r--src/shader_recompiler/frontend/ir/microinstruction.cpp1
-rw-r--r--src/shader_recompiler/frontend/ir/opcodes.cpp1
-rw-r--r--src/shader_recompiler/frontend/ir/opcodes.inc3
-rw-r--r--src/shader_recompiler/frontend/ir/patch.cpp28
-rw-r--r--src/shader_recompiler/frontend/ir/patch.h149
-rw-r--r--src/shader_recompiler/frontend/ir/type.h41
-rw-r--r--src/shader_recompiler/frontend/ir/value.cpp9
-rw-r--r--src/shader_recompiler/frontend/ir/value.h4
-rw-r--r--src/shader_recompiler/frontend/maxwell/program.cpp5
-rw-r--r--src/shader_recompiler/frontend/maxwell/translate/impl/load_store_attribute.cpp33
-rw-r--r--src/shader_recompiler/frontend/maxwell/translate/impl/move_special_register.cpp2
-rw-r--r--src/shader_recompiler/ir_opt/collect_shader_info_pass.cpp41
-rw-r--r--src/shader_recompiler/profile.h16
-rw-r--r--src/shader_recompiler/shader_info.h5
-rw-r--r--src/video_core/renderer_vulkan/maxwell_to_vk.cpp13
-rw-r--r--src/video_core/renderer_vulkan/maxwell_to_vk.h2
-rw-r--r--src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp3
-rw-r--r--src/video_core/renderer_vulkan/vk_pipeline_cache.cpp30
-rw-r--r--src/video_core/renderer_vulkan/vk_staging_buffer_pool.cpp2
-rw-r--r--src/video_core/vulkan_common/vulkan_device.cpp3
28 files changed, 605 insertions, 91 deletions
diff --git a/src/shader_recompiler/CMakeLists.txt b/src/shader_recompiler/CMakeLists.txt
index bbbfa98a3..7c11d15bf 100644
--- a/src/shader_recompiler/CMakeLists.txt
+++ b/src/shader_recompiler/CMakeLists.txt
@@ -41,6 +41,8 @@ add_library(shader_recompiler STATIC
41 frontend/ir/opcodes.cpp 41 frontend/ir/opcodes.cpp
42 frontend/ir/opcodes.h 42 frontend/ir/opcodes.h
43 frontend/ir/opcodes.inc 43 frontend/ir/opcodes.inc
44 frontend/ir/patch.cpp
45 frontend/ir/patch.h
44 frontend/ir/post_order.cpp 46 frontend/ir/post_order.cpp
45 frontend/ir/post_order.h 47 frontend/ir/post_order.h
46 frontend/ir/pred.h 48 frontend/ir/pred.h
diff --git a/src/shader_recompiler/backend/spirv/emit_context.cpp b/src/shader_recompiler/backend/spirv/emit_context.cpp
index 032cf5e03..067f61613 100644
--- a/src/shader_recompiler/backend/spirv/emit_context.cpp
+++ b/src/shader_recompiler/backend/spirv/emit_context.cpp
@@ -125,19 +125,36 @@ u32 NumVertices(InputTopology input_topology) {
125 throw InvalidArgument("Invalid input topology {}", input_topology); 125 throw InvalidArgument("Invalid input topology {}", input_topology);
126} 126}
127 127
128Id DefineInput(EmitContext& ctx, Id type, std::optional<spv::BuiltIn> builtin = std::nullopt) { 128Id DefineInput(EmitContext& ctx, Id type, bool per_invocation,
129 if (ctx.stage == Stage::Geometry) { 129 std::optional<spv::BuiltIn> builtin = std::nullopt) {
130 const u32 num_vertices{NumVertices(ctx.profile.input_topology)}; 130 switch (ctx.stage) {
131 type = ctx.TypeArray(type, ctx.Constant(ctx.U32[1], num_vertices)); 131 case Stage::TessellationControl:
132 case Stage::TessellationEval:
133 if (per_invocation) {
134 type = ctx.TypeArray(type, ctx.Constant(ctx.U32[1], 32u));
135 }
136 break;
137 case Stage::Geometry:
138 if (per_invocation) {
139 const u32 num_vertices{NumVertices(ctx.profile.input_topology)};
140 type = ctx.TypeArray(type, ctx.Constant(ctx.U32[1], num_vertices));
141 }
142 break;
143 default:
144 break;
132 } 145 }
133 return DefineVariable(ctx, type, builtin, spv::StorageClass::Input); 146 return DefineVariable(ctx, type, builtin, spv::StorageClass::Input);
134} 147}
135 148
136Id DefineOutput(EmitContext& ctx, Id type, std::optional<spv::BuiltIn> builtin = std::nullopt) { 149Id DefineOutput(EmitContext& ctx, Id type, std::optional<u32> invocations,
150 std::optional<spv::BuiltIn> builtin = std::nullopt) {
151 if (invocations && ctx.stage == Stage::TessellationControl) {
152 type = ctx.TypeArray(type, ctx.Constant(ctx.U32[1], *invocations));
153 }
137 return DefineVariable(ctx, type, builtin, spv::StorageClass::Output); 154 return DefineVariable(ctx, type, builtin, spv::StorageClass::Output);
138} 155}
139 156
140void DefineGenericOutput(EmitContext& ctx, size_t index) { 157void DefineGenericOutput(EmitContext& ctx, size_t index, std::optional<u32> invocations) {
141 static constexpr std::string_view swizzle{"xyzw"}; 158 static constexpr std::string_view swizzle{"xyzw"};
142 const size_t base_attr_index{static_cast<size_t>(IR::Attribute::Generic0X) + index * 4}; 159 const size_t base_attr_index{static_cast<size_t>(IR::Attribute::Generic0X) + index * 4};
143 u32 element{0}; 160 u32 element{0};
@@ -150,7 +167,7 @@ void DefineGenericOutput(EmitContext& ctx, size_t index) {
150 } 167 }
151 const u32 num_components{xfb_varying ? xfb_varying->components : remainder}; 168 const u32 num_components{xfb_varying ? xfb_varying->components : remainder};
152 169
153 const Id id{DefineOutput(ctx, ctx.F32[num_components])}; 170 const Id id{DefineOutput(ctx, ctx.F32[num_components], invocations)};
154 ctx.Decorate(id, spv::Decoration::Location, static_cast<u32>(index)); 171 ctx.Decorate(id, spv::Decoration::Location, static_cast<u32>(index));
155 if (element > 0) { 172 if (element > 0) {
156 ctx.Decorate(id, spv::Decoration::Component, element); 173 ctx.Decorate(id, spv::Decoration::Component, element);
@@ -161,10 +178,10 @@ void DefineGenericOutput(EmitContext& ctx, size_t index) {
161 ctx.Decorate(id, spv::Decoration::Offset, xfb_varying->offset); 178 ctx.Decorate(id, spv::Decoration::Offset, xfb_varying->offset);
162 } 179 }
163 if (num_components < 4 || element > 0) { 180 if (num_components < 4 || element > 0) {
164 ctx.Name(id, fmt::format("out_attr{}", index));
165 } else {
166 const std::string_view subswizzle{swizzle.substr(element, num_components)}; 181 const std::string_view subswizzle{swizzle.substr(element, num_components)};
167 ctx.Name(id, fmt::format("out_attr{}_{}", index, subswizzle)); 182 ctx.Name(id, fmt::format("out_attr{}_{}", index, subswizzle));
183 } else {
184 ctx.Name(id, fmt::format("out_attr{}", index));
168 } 185 }
169 const GenericElementInfo info{ 186 const GenericElementInfo info{
170 .id = id, 187 .id = id,
@@ -383,7 +400,7 @@ EmitContext::EmitContext(const Profile& profile_, IR::Program& program, u32& bin
383 AddCapability(spv::Capability::Shader); 400 AddCapability(spv::Capability::Shader);
384 DefineCommonTypes(program.info); 401 DefineCommonTypes(program.info);
385 DefineCommonConstants(); 402 DefineCommonConstants();
386 DefineInterfaces(program.info); 403 DefineInterfaces(program);
387 DefineLocalMemory(program); 404 DefineLocalMemory(program);
388 DefineSharedMemory(program); 405 DefineSharedMemory(program);
389 DefineSharedMemoryFunctions(program); 406 DefineSharedMemoryFunctions(program);
@@ -472,9 +489,9 @@ void EmitContext::DefineCommonConstants() {
472 f32_zero_value = Constant(F32[1], 0.0f); 489 f32_zero_value = Constant(F32[1], 0.0f);
473} 490}
474 491
475void EmitContext::DefineInterfaces(const Info& info) { 492void EmitContext::DefineInterfaces(const IR::Program& program) {
476 DefineInputs(info); 493 DefineInputs(program.info);
477 DefineOutputs(info); 494 DefineOutputs(program);
478} 495}
479 496
480void EmitContext::DefineLocalMemory(const IR::Program& program) { 497void EmitContext::DefineLocalMemory(const IR::Program& program) {
@@ -972,26 +989,29 @@ void EmitContext::DefineLabels(IR::Program& program) {
972 989
973void EmitContext::DefineInputs(const Info& info) { 990void EmitContext::DefineInputs(const Info& info) {
974 if (info.uses_workgroup_id) { 991 if (info.uses_workgroup_id) {
975 workgroup_id = DefineInput(*this, U32[3], spv::BuiltIn::WorkgroupId); 992 workgroup_id = DefineInput(*this, U32[3], false, spv::BuiltIn::WorkgroupId);
976 } 993 }
977 if (info.uses_local_invocation_id) { 994 if (info.uses_local_invocation_id) {
978 local_invocation_id = DefineInput(*this, U32[3], spv::BuiltIn::LocalInvocationId); 995 local_invocation_id = DefineInput(*this, U32[3], false, spv::BuiltIn::LocalInvocationId);
996 }
997 if (info.uses_invocation_id) {
998 invocation_id = DefineInput(*this, U32[1], false, spv::BuiltIn::InvocationId);
979 } 999 }
980 if (info.uses_is_helper_invocation) { 1000 if (info.uses_is_helper_invocation) {
981 is_helper_invocation = DefineInput(*this, U1, spv::BuiltIn::HelperInvocation); 1001 is_helper_invocation = DefineInput(*this, U1, false, spv::BuiltIn::HelperInvocation);
982 } 1002 }
983 if (info.uses_subgroup_mask) { 1003 if (info.uses_subgroup_mask) {
984 subgroup_mask_eq = DefineInput(*this, U32[4], spv::BuiltIn::SubgroupEqMaskKHR); 1004 subgroup_mask_eq = DefineInput(*this, U32[4], false, spv::BuiltIn::SubgroupEqMaskKHR);
985 subgroup_mask_lt = DefineInput(*this, U32[4], spv::BuiltIn::SubgroupLtMaskKHR); 1005 subgroup_mask_lt = DefineInput(*this, U32[4], false, spv::BuiltIn::SubgroupLtMaskKHR);
986 subgroup_mask_le = DefineInput(*this, U32[4], spv::BuiltIn::SubgroupLeMaskKHR); 1006 subgroup_mask_le = DefineInput(*this, U32[4], false, spv::BuiltIn::SubgroupLeMaskKHR);
987 subgroup_mask_gt = DefineInput(*this, U32[4], spv::BuiltIn::SubgroupGtMaskKHR); 1007 subgroup_mask_gt = DefineInput(*this, U32[4], false, spv::BuiltIn::SubgroupGtMaskKHR);
988 subgroup_mask_ge = DefineInput(*this, U32[4], spv::BuiltIn::SubgroupGeMaskKHR); 1008 subgroup_mask_ge = DefineInput(*this, U32[4], false, spv::BuiltIn::SubgroupGeMaskKHR);
989 } 1009 }
990 if (info.uses_subgroup_invocation_id || 1010 if (info.uses_subgroup_invocation_id ||
991 (profile.warp_size_potentially_larger_than_guest && 1011 (profile.warp_size_potentially_larger_than_guest &&
992 (info.uses_subgroup_vote || info.uses_subgroup_mask))) { 1012 (info.uses_subgroup_vote || info.uses_subgroup_mask))) {
993 subgroup_local_invocation_id = 1013 subgroup_local_invocation_id =
994 DefineInput(*this, U32[1], spv::BuiltIn::SubgroupLocalInvocationId); 1014 DefineInput(*this, U32[1], false, spv::BuiltIn::SubgroupLocalInvocationId);
995 } 1015 }
996 if (info.uses_fswzadd) { 1016 if (info.uses_fswzadd) {
997 const Id f32_one{Constant(F32[1], 1.0f)}; 1017 const Id f32_one{Constant(F32[1], 1.0f)};
@@ -1004,29 +1024,32 @@ void EmitContext::DefineInputs(const Info& info) {
1004 if (info.loads_position) { 1024 if (info.loads_position) {
1005 const bool is_fragment{stage != Stage::Fragment}; 1025 const bool is_fragment{stage != Stage::Fragment};
1006 const spv::BuiltIn built_in{is_fragment ? spv::BuiltIn::Position : spv::BuiltIn::FragCoord}; 1026 const spv::BuiltIn built_in{is_fragment ? spv::BuiltIn::Position : spv::BuiltIn::FragCoord};
1007 input_position = DefineInput(*this, F32[4], built_in); 1027 input_position = DefineInput(*this, F32[4], true, built_in);
1008 } 1028 }
1009 if (info.loads_instance_id) { 1029 if (info.loads_instance_id) {
1010 if (profile.support_vertex_instance_id) { 1030 if (profile.support_vertex_instance_id) {
1011 instance_id = DefineInput(*this, U32[1], spv::BuiltIn::InstanceId); 1031 instance_id = DefineInput(*this, U32[1], true, spv::BuiltIn::InstanceId);
1012 } else { 1032 } else {
1013 instance_index = DefineInput(*this, U32[1], spv::BuiltIn::InstanceIndex); 1033 instance_index = DefineInput(*this, U32[1], true, spv::BuiltIn::InstanceIndex);
1014 base_instance = DefineInput(*this, U32[1], spv::BuiltIn::BaseInstance); 1034 base_instance = DefineInput(*this, U32[1], true, spv::BuiltIn::BaseInstance);
1015 } 1035 }
1016 } 1036 }
1017 if (info.loads_vertex_id) { 1037 if (info.loads_vertex_id) {
1018 if (profile.support_vertex_instance_id) { 1038 if (profile.support_vertex_instance_id) {
1019 vertex_id = DefineInput(*this, U32[1], spv::BuiltIn::VertexId); 1039 vertex_id = DefineInput(*this, U32[1], true, spv::BuiltIn::VertexId);
1020 } else { 1040 } else {
1021 vertex_index = DefineInput(*this, U32[1], spv::BuiltIn::VertexIndex); 1041 vertex_index = DefineInput(*this, U32[1], true, spv::BuiltIn::VertexIndex);
1022 base_vertex = DefineInput(*this, U32[1], spv::BuiltIn::BaseVertex); 1042 base_vertex = DefineInput(*this, U32[1], true, spv::BuiltIn::BaseVertex);
1023 } 1043 }
1024 } 1044 }
1025 if (info.loads_front_face) { 1045 if (info.loads_front_face) {
1026 front_face = DefineInput(*this, U1, spv::BuiltIn::FrontFacing); 1046 front_face = DefineInput(*this, U1, true, spv::BuiltIn::FrontFacing);
1027 } 1047 }
1028 if (info.loads_point_coord) { 1048 if (info.loads_point_coord) {
1029 point_coord = DefineInput(*this, F32[2], spv::BuiltIn::PointCoord); 1049 point_coord = DefineInput(*this, F32[2], true, spv::BuiltIn::PointCoord);
1050 }
1051 if (info.loads_tess_coord) {
1052 tess_coord = DefineInput(*this, F32[3], false, spv::BuiltIn::TessCoord);
1030 } 1053 }
1031 for (size_t index = 0; index < info.input_generics.size(); ++index) { 1054 for (size_t index = 0; index < info.input_generics.size(); ++index) {
1032 const InputVarying generic{info.input_generics[index]}; 1055 const InputVarying generic{info.input_generics[index]};
@@ -1038,7 +1061,7 @@ void EmitContext::DefineInputs(const Info& info) {
1038 continue; 1061 continue;
1039 } 1062 }
1040 const Id type{GetAttributeType(*this, input_type)}; 1063 const Id type{GetAttributeType(*this, input_type)};
1041 const Id id{DefineInput(*this, type)}; 1064 const Id id{DefineInput(*this, type, true)};
1042 Decorate(id, spv::Decoration::Location, static_cast<u32>(index)); 1065 Decorate(id, spv::Decoration::Location, static_cast<u32>(index));
1043 Name(id, fmt::format("in_attr{}", index)); 1066 Name(id, fmt::format("in_attr{}", index));
1044 input_generics[index] = id; 1067 input_generics[index] = id;
@@ -1059,58 +1082,98 @@ void EmitContext::DefineInputs(const Info& info) {
1059 break; 1082 break;
1060 } 1083 }
1061 } 1084 }
1085 if (stage == Stage::TessellationEval) {
1086 for (size_t index = 0; index < info.uses_patches.size(); ++index) {
1087 if (!info.uses_patches[index]) {
1088 continue;
1089 }
1090 const Id id{DefineInput(*this, F32[4], false)};
1091 Decorate(id, spv::Decoration::Patch);
1092 Decorate(id, spv::Decoration::Location, static_cast<u32>(index));
1093 patches[index] = id;
1094 }
1095 }
1062} 1096}
1063 1097
1064void EmitContext::DefineOutputs(const Info& info) { 1098void EmitContext::DefineOutputs(const IR::Program& program) {
1099 const Info& info{program.info};
1100 const std::optional<u32> invocations{program.invocations};
1065 if (info.stores_position || stage == Stage::VertexB) { 1101 if (info.stores_position || stage == Stage::VertexB) {
1066 output_position = DefineOutput(*this, F32[4], spv::BuiltIn::Position); 1102 output_position = DefineOutput(*this, F32[4], invocations, spv::BuiltIn::Position);
1067 } 1103 }
1068 if (info.stores_point_size || profile.fixed_state_point_size) { 1104 if (info.stores_point_size || profile.fixed_state_point_size) {
1069 if (stage == Stage::Fragment) { 1105 if (stage == Stage::Fragment) {
1070 throw NotImplementedException("Storing PointSize in fragment stage"); 1106 throw NotImplementedException("Storing PointSize in fragment stage");
1071 } 1107 }
1072 output_point_size = DefineOutput(*this, F32[1], spv::BuiltIn::PointSize); 1108 output_point_size = DefineOutput(*this, F32[1], invocations, spv::BuiltIn::PointSize);
1073 } 1109 }
1074 if (info.stores_clip_distance) { 1110 if (info.stores_clip_distance) {
1075 if (stage == Stage::Fragment) { 1111 if (stage == Stage::Fragment) {
1076 throw NotImplementedException("Storing ClipDistance in fragment stage"); 1112 throw NotImplementedException("Storing ClipDistance in fragment stage");
1077 } 1113 }
1078 const Id type{TypeArray(F32[1], Constant(U32[1], 8U))}; 1114 const Id type{TypeArray(F32[1], Constant(U32[1], 8U))};
1079 clip_distances = DefineOutput(*this, type, spv::BuiltIn::ClipDistance); 1115 clip_distances = DefineOutput(*this, type, invocations, spv::BuiltIn::ClipDistance);
1080 } 1116 }
1081 if (info.stores_layer && 1117 if (info.stores_layer &&
1082 (profile.support_viewport_index_layer_non_geometry || stage == Stage::Geometry)) { 1118 (profile.support_viewport_index_layer_non_geometry || stage == Stage::Geometry)) {
1083 if (stage == Stage::Fragment) { 1119 if (stage == Stage::Fragment) {
1084 throw NotImplementedException("Storing Layer in fragment stage"); 1120 throw NotImplementedException("Storing Layer in fragment stage");
1085 } 1121 }
1086 layer = DefineOutput(*this, U32[1], spv::BuiltIn::Layer); 1122 layer = DefineOutput(*this, U32[1], invocations, spv::BuiltIn::Layer);
1087 } 1123 }
1088 if (info.stores_viewport_index && 1124 if (info.stores_viewport_index &&
1089 (profile.support_viewport_index_layer_non_geometry || stage == Stage::Geometry)) { 1125 (profile.support_viewport_index_layer_non_geometry || stage == Stage::Geometry)) {
1090 if (stage == Stage::Fragment) { 1126 if (stage == Stage::Fragment) {
1091 throw NotImplementedException("Storing ViewportIndex in fragment stage"); 1127 throw NotImplementedException("Storing ViewportIndex in fragment stage");
1092 } 1128 }
1093 viewport_index = DefineOutput(*this, U32[1], spv::BuiltIn::ViewportIndex); 1129 viewport_index = DefineOutput(*this, U32[1], invocations, spv::BuiltIn::ViewportIndex);
1094 } 1130 }
1095 for (size_t index = 0; index < info.stores_generics.size(); ++index) { 1131 for (size_t index = 0; index < info.stores_generics.size(); ++index) {
1096 if (info.stores_generics[index]) { 1132 if (info.stores_generics[index]) {
1097 DefineGenericOutput(*this, index); 1133 DefineGenericOutput(*this, index, invocations);
1098 } 1134 }
1099 } 1135 }
1100 if (stage == Stage::Fragment) { 1136 switch (stage) {
1137 case Stage::TessellationControl:
1138 if (info.stores_tess_level_outer) {
1139 const Id type{TypeArray(F32[1], Constant(U32[1], 4))};
1140 output_tess_level_outer =
1141 DefineOutput(*this, type, std::nullopt, spv::BuiltIn::TessLevelOuter);
1142 Decorate(output_tess_level_outer, spv::Decoration::Patch);
1143 }
1144 if (info.stores_tess_level_inner) {
1145 const Id type{TypeArray(F32[1], Constant(U32[1], 2))};
1146 output_tess_level_inner =
1147 DefineOutput(*this, type, std::nullopt, spv::BuiltIn::TessLevelInner);
1148 Decorate(output_tess_level_inner, spv::Decoration::Patch);
1149 }
1150 for (size_t index = 0; index < info.uses_patches.size(); ++index) {
1151 if (!info.uses_patches[index]) {
1152 continue;
1153 }
1154 const Id id{DefineOutput(*this, F32[4], std::nullopt)};
1155 Decorate(id, spv::Decoration::Patch);
1156 Decorate(id, spv::Decoration::Location, static_cast<u32>(index));
1157 patches[index] = id;
1158 }
1159 break;
1160 case Stage::Fragment:
1101 for (u32 index = 0; index < 8; ++index) { 1161 for (u32 index = 0; index < 8; ++index) {
1102 if (!info.stores_frag_color[index]) { 1162 if (!info.stores_frag_color[index]) {
1103 continue; 1163 continue;
1104 } 1164 }
1105 frag_color[index] = DefineOutput(*this, F32[4]); 1165 frag_color[index] = DefineOutput(*this, F32[4], std::nullopt);
1106 Decorate(frag_color[index], spv::Decoration::Location, index); 1166 Decorate(frag_color[index], spv::Decoration::Location, index);
1107 Name(frag_color[index], fmt::format("frag_color{}", index)); 1167 Name(frag_color[index], fmt::format("frag_color{}", index));
1108 } 1168 }
1109 if (info.stores_frag_depth) { 1169 if (info.stores_frag_depth) {
1110 frag_depth = DefineOutput(*this, F32[1]); 1170 frag_depth = DefineOutput(*this, F32[1], std::nullopt);
1111 Decorate(frag_depth, spv::Decoration::BuiltIn, spv::BuiltIn::FragDepth); 1171 Decorate(frag_depth, spv::Decoration::BuiltIn, spv::BuiltIn::FragDepth);
1112 Name(frag_depth, "frag_depth"); 1172 Name(frag_depth, "frag_depth");
1113 } 1173 }
1174 break;
1175 default:
1176 break;
1114 } 1177 }
1115} 1178}
1116 1179
diff --git a/src/shader_recompiler/backend/spirv/emit_context.h b/src/shader_recompiler/backend/spirv/emit_context.h
index 0da14d5f8..ba0a253b3 100644
--- a/src/shader_recompiler/backend/spirv/emit_context.h
+++ b/src/shader_recompiler/backend/spirv/emit_context.h
@@ -147,6 +147,7 @@ public:
147 147
148 Id workgroup_id{}; 148 Id workgroup_id{};
149 Id local_invocation_id{}; 149 Id local_invocation_id{};
150 Id invocation_id{};
150 Id is_helper_invocation{}; 151 Id is_helper_invocation{};
151 Id subgroup_local_invocation_id{}; 152 Id subgroup_local_invocation_id{};
152 Id subgroup_mask_eq{}; 153 Id subgroup_mask_eq{};
@@ -162,6 +163,7 @@ public:
162 Id base_vertex{}; 163 Id base_vertex{};
163 Id front_face{}; 164 Id front_face{};
164 Id point_coord{}; 165 Id point_coord{};
166 Id tess_coord{};
165 Id clip_distances{}; 167 Id clip_distances{};
166 Id layer{}; 168 Id layer{};
167 Id viewport_index{}; 169 Id viewport_index{};
@@ -204,6 +206,10 @@ public:
204 Id output_position{}; 206 Id output_position{};
205 std::array<std::array<GenericElementInfo, 4>, 32> output_generics{}; 207 std::array<std::array<GenericElementInfo, 4>, 32> output_generics{};
206 208
209 Id output_tess_level_outer{};
210 Id output_tess_level_inner{};
211 std::array<Id, 30> patches{};
212
207 std::array<Id, 8> frag_color{}; 213 std::array<Id, 8> frag_color{};
208 Id frag_depth{}; 214 Id frag_depth{};
209 215
@@ -212,7 +218,7 @@ public:
212private: 218private:
213 void DefineCommonTypes(const Info& info); 219 void DefineCommonTypes(const Info& info);
214 void DefineCommonConstants(); 220 void DefineCommonConstants();
215 void DefineInterfaces(const Info& info); 221 void DefineInterfaces(const IR::Program& program);
216 void DefineLocalMemory(const IR::Program& program); 222 void DefineLocalMemory(const IR::Program& program);
217 void DefineSharedMemory(const IR::Program& program); 223 void DefineSharedMemory(const IR::Program& program);
218 void DefineSharedMemoryFunctions(const IR::Program& program); 224 void DefineSharedMemoryFunctions(const IR::Program& program);
@@ -226,7 +232,7 @@ private:
226 void DefineLabels(IR::Program& program); 232 void DefineLabels(IR::Program& program);
227 233
228 void DefineInputs(const Info& info); 234 void DefineInputs(const Info& info);
229 void DefineOutputs(const Info& info); 235 void DefineOutputs(const IR::Program& program);
230}; 236};
231 237
232} // namespace Shader::Backend::SPIRV 238} // namespace Shader::Backend::SPIRV
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv.cpp b/src/shader_recompiler/backend/spirv/emit_spirv.cpp
index 3bf4c6a9e..105602ccf 100644
--- a/src/shader_recompiler/backend/spirv/emit_spirv.cpp
+++ b/src/shader_recompiler/backend/spirv/emit_spirv.cpp
@@ -45,6 +45,8 @@ ArgType Arg(EmitContext& ctx, const IR::Value& arg) {
45 return arg.Label(); 45 return arg.Label();
46 } else if constexpr (std::is_same_v<ArgType, IR::Attribute>) { 46 } else if constexpr (std::is_same_v<ArgType, IR::Attribute>) {
47 return arg.Attribute(); 47 return arg.Attribute();
48 } else if constexpr (std::is_same_v<ArgType, IR::Patch>) {
49 return arg.Patch();
48 } else if constexpr (std::is_same_v<ArgType, IR::Reg>) { 50 } else if constexpr (std::is_same_v<ArgType, IR::Reg>) {
49 return arg.Reg(); 51 return arg.Reg();
50 } 52 }
@@ -120,6 +122,30 @@ Id DefineMain(EmitContext& ctx, IR::Program& program) {
120 return main; 122 return main;
121} 123}
122 124
125spv::ExecutionMode ExecutionMode(TessPrimitive primitive) {
126 switch (primitive) {
127 case TessPrimitive::Isolines:
128 return spv::ExecutionMode::Isolines;
129 case TessPrimitive::Triangles:
130 return spv::ExecutionMode::Triangles;
131 case TessPrimitive::Quads:
132 return spv::ExecutionMode::Quads;
133 }
134 throw InvalidArgument("Tessellation primitive {}", primitive);
135}
136
137spv::ExecutionMode ExecutionMode(TessSpacing spacing) {
138 switch (spacing) {
139 case TessSpacing::Equal:
140 return spv::ExecutionMode::SpacingEqual;
141 case TessSpacing::FractionalOdd:
142 return spv::ExecutionMode::SpacingFractionalOdd;
143 case TessSpacing::FractionalEven:
144 return spv::ExecutionMode::SpacingFractionalEven;
145 }
146 throw InvalidArgument("Tessellation spacing {}", spacing);
147}
148
123void DefineEntryPoint(const IR::Program& program, EmitContext& ctx, Id main) { 149void DefineEntryPoint(const IR::Program& program, EmitContext& ctx, Id main) {
124 const std::span interfaces(ctx.interfaces.data(), ctx.interfaces.size()); 150 const std::span interfaces(ctx.interfaces.data(), ctx.interfaces.size());
125 spv::ExecutionModel execution_model{}; 151 spv::ExecutionModel execution_model{};
@@ -134,6 +160,19 @@ void DefineEntryPoint(const IR::Program& program, EmitContext& ctx, Id main) {
134 case Stage::VertexB: 160 case Stage::VertexB:
135 execution_model = spv::ExecutionModel::Vertex; 161 execution_model = spv::ExecutionModel::Vertex;
136 break; 162 break;
163 case Stage::TessellationControl:
164 execution_model = spv::ExecutionModel::TessellationControl;
165 ctx.AddCapability(spv::Capability::Tessellation);
166 ctx.AddExecutionMode(main, spv::ExecutionMode::OutputVertices, program.invocations);
167 break;
168 case Stage::TessellationEval:
169 execution_model = spv::ExecutionModel::TessellationEvaluation;
170 ctx.AddCapability(spv::Capability::Tessellation);
171 ctx.AddExecutionMode(main, ExecutionMode(ctx.profile.tess_primitive));
172 ctx.AddExecutionMode(main, ExecutionMode(ctx.profile.tess_spacing));
173 ctx.AddExecutionMode(main, ctx.profile.tess_clockwise ? spv::ExecutionMode::VertexOrderCw
174 : spv::ExecutionMode::VertexOrderCcw);
175 break;
137 case Stage::Geometry: 176 case Stage::Geometry:
138 execution_model = spv::ExecutionModel::Geometry; 177 execution_model = spv::ExecutionModel::Geometry;
139 ctx.AddCapability(spv::Capability::Geometry); 178 ctx.AddCapability(spv::Capability::Geometry);
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv.h b/src/shader_recompiler/backend/spirv/emit_spirv.h
index 55b2edba0..8caf30f1b 100644
--- a/src/shader_recompiler/backend/spirv/emit_spirv.h
+++ b/src/shader_recompiler/backend/spirv/emit_spirv.h
@@ -55,6 +55,8 @@ Id EmitGetAttribute(EmitContext& ctx, IR::Attribute attr, Id vertex);
55void EmitSetAttribute(EmitContext& ctx, IR::Attribute attr, Id value, Id vertex); 55void EmitSetAttribute(EmitContext& ctx, IR::Attribute attr, Id value, Id vertex);
56Id EmitGetAttributeIndexed(EmitContext& ctx, Id offset, Id vertex); 56Id EmitGetAttributeIndexed(EmitContext& ctx, Id offset, Id vertex);
57void EmitSetAttributeIndexed(EmitContext& ctx, Id offset, Id value, Id vertex); 57void EmitSetAttributeIndexed(EmitContext& ctx, Id offset, Id value, Id vertex);
58Id EmitGetPatch(EmitContext& ctx, IR::Patch patch);
59void EmitSetPatch(EmitContext& ctx, IR::Patch patch, Id value);
58void EmitSetFragColor(EmitContext& ctx, u32 index, u32 component, Id value); 60void EmitSetFragColor(EmitContext& ctx, u32 index, u32 component, Id value);
59void EmitSetFragDepth(EmitContext& ctx, Id value); 61void EmitSetFragDepth(EmitContext& ctx, Id value);
60void EmitGetZFlag(EmitContext& ctx); 62void EmitGetZFlag(EmitContext& ctx);
@@ -67,6 +69,7 @@ void EmitSetCFlag(EmitContext& ctx);
67void EmitSetOFlag(EmitContext& ctx); 69void EmitSetOFlag(EmitContext& ctx);
68Id EmitWorkgroupId(EmitContext& ctx); 70Id EmitWorkgroupId(EmitContext& ctx);
69Id EmitLocalInvocationId(EmitContext& ctx); 71Id EmitLocalInvocationId(EmitContext& ctx);
72Id EmitInvocationId(EmitContext& ctx);
70Id EmitIsHelperInvocation(EmitContext& ctx); 73Id EmitIsHelperInvocation(EmitContext& ctx);
71Id EmitLoadLocal(EmitContext& ctx, Id word_offset); 74Id EmitLoadLocal(EmitContext& ctx, Id word_offset);
72void EmitWriteLocal(EmitContext& ctx, Id word_offset, Id value); 75void EmitWriteLocal(EmitContext& ctx, Id word_offset, Id value);
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 59c56c5ba..4a1aeece5 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
@@ -32,13 +32,26 @@ std::optional<AttrInfo> AttrTypes(EmitContext& ctx, u32 index) {
32 32
33template <typename... Args> 33template <typename... Args>
34Id AttrPointer(EmitContext& ctx, Id pointer_type, Id vertex, Id base, Args&&... args) { 34Id AttrPointer(EmitContext& ctx, Id pointer_type, Id vertex, Id base, Args&&... args) {
35 if (ctx.stage == Stage::Geometry) { 35 switch (ctx.stage) {
36 case Stage::TessellationControl:
37 case Stage::TessellationEval:
38 case Stage::Geometry:
36 return ctx.OpAccessChain(pointer_type, base, vertex, std::forward<Args>(args)...); 39 return ctx.OpAccessChain(pointer_type, base, vertex, std::forward<Args>(args)...);
37 } else { 40 default:
38 return ctx.OpAccessChain(pointer_type, base, std::forward<Args>(args)...); 41 return ctx.OpAccessChain(pointer_type, base, std::forward<Args>(args)...);
39 } 42 }
40} 43}
41 44
45template <typename... Args>
46Id OutputAccessChain(EmitContext& ctx, Id result_type, Id base, Args&&... args) {
47 if (ctx.stage == Stage::TessellationControl) {
48 const Id invocation_id{ctx.OpLoad(ctx.U32[1], ctx.invocation_id)};
49 return ctx.OpAccessChain(result_type, base, invocation_id, std::forward<Args>(args)...);
50 } else {
51 return ctx.OpAccessChain(result_type, base, std::forward<Args>(args)...);
52 }
53}
54
42std::optional<Id> OutputAttrPointer(EmitContext& ctx, IR::Attribute attr) { 55std::optional<Id> OutputAttrPointer(EmitContext& ctx, IR::Attribute attr) {
43 if (IR::IsGeneric(attr)) { 56 if (IR::IsGeneric(attr)) {
44 const u32 index{IR::GenericAttributeIndex(attr)}; 57 const u32 index{IR::GenericAttributeIndex(attr)};
@@ -49,7 +62,7 @@ std::optional<Id> OutputAttrPointer(EmitContext& ctx, IR::Attribute attr) {
49 } else { 62 } else {
50 const u32 index_element{element - info.first_element}; 63 const u32 index_element{element - info.first_element};
51 const Id index_id{ctx.Constant(ctx.U32[1], index_element)}; 64 const Id index_id{ctx.Constant(ctx.U32[1], index_element)};
52 return ctx.OpAccessChain(ctx.output_f32, info.id, index_id); 65 return OutputAccessChain(ctx, ctx.output_f32, info.id, index_id);
53 } 66 }
54 } 67 }
55 switch (attr) { 68 switch (attr) {
@@ -61,7 +74,7 @@ std::optional<Id> OutputAttrPointer(EmitContext& ctx, IR::Attribute attr) {
61 case IR::Attribute::PositionW: { 74 case IR::Attribute::PositionW: {
62 const u32 element{static_cast<u32>(attr) % 4}; 75 const u32 element{static_cast<u32>(attr) % 4};
63 const Id element_id{ctx.Constant(ctx.U32[1], element)}; 76 const Id element_id{ctx.Constant(ctx.U32[1], element)};
64 return ctx.OpAccessChain(ctx.output_f32, ctx.output_position, element_id); 77 return OutputAccessChain(ctx, ctx.output_f32, ctx.output_position, element_id);
65 } 78 }
66 case IR::Attribute::ClipDistance0: 79 case IR::Attribute::ClipDistance0:
67 case IR::Attribute::ClipDistance1: 80 case IR::Attribute::ClipDistance1:
@@ -74,7 +87,7 @@ std::optional<Id> OutputAttrPointer(EmitContext& ctx, IR::Attribute attr) {
74 const u32 base{static_cast<u32>(IR::Attribute::ClipDistance0)}; 87 const u32 base{static_cast<u32>(IR::Attribute::ClipDistance0)};
75 const u32 index{static_cast<u32>(attr) - base}; 88 const u32 index{static_cast<u32>(attr) - base};
76 const Id clip_num{ctx.Constant(ctx.U32[1], index)}; 89 const Id clip_num{ctx.Constant(ctx.U32[1], index)};
77 return ctx.OpAccessChain(ctx.output_f32, ctx.clip_distances, clip_num); 90 return OutputAccessChain(ctx, ctx.output_f32, ctx.clip_distances, clip_num);
78 } 91 }
79 case IR::Attribute::Layer: 92 case IR::Attribute::Layer:
80 return ctx.profile.support_viewport_index_layer_non_geometry || 93 return ctx.profile.support_viewport_index_layer_non_geometry ||
@@ -222,11 +235,18 @@ Id EmitGetAttribute(EmitContext& ctx, IR::Attribute attr, Id vertex) {
222 ctx.Constant(ctx.U32[1], std::numeric_limits<u32>::max()), 235 ctx.Constant(ctx.U32[1], std::numeric_limits<u32>::max()),
223 ctx.u32_zero_value); 236 ctx.u32_zero_value);
224 case IR::Attribute::PointSpriteS: 237 case IR::Attribute::PointSpriteS:
225 return ctx.OpLoad(ctx.F32[1], AttrPointer(ctx, ctx.input_f32, vertex, ctx.point_coord, 238 return ctx.OpLoad(ctx.F32[1],
226 ctx.u32_zero_value)); 239 ctx.OpAccessChain(ctx.input_f32, ctx.point_coord, ctx.u32_zero_value));
227 case IR::Attribute::PointSpriteT: 240 case IR::Attribute::PointSpriteT:
228 return ctx.OpLoad(ctx.F32[1], AttrPointer(ctx, ctx.input_f32, vertex, ctx.point_coord, 241 return ctx.OpLoad(ctx.F32[1], ctx.OpAccessChain(ctx.input_f32, ctx.point_coord,
229 ctx.Constant(ctx.U32[1], 1U))); 242 ctx.Constant(ctx.U32[1], 1U)));
243 case IR::Attribute::TessellationEvaluationPointU:
244 return ctx.OpLoad(ctx.F32[1],
245 ctx.OpAccessChain(ctx.input_f32, ctx.tess_coord, ctx.u32_zero_value));
246 case IR::Attribute::TessellationEvaluationPointV:
247 return ctx.OpLoad(ctx.F32[1], ctx.OpAccessChain(ctx.input_f32, ctx.tess_coord,
248 ctx.Constant(ctx.U32[1], 1U)));
249
230 default: 250 default:
231 throw NotImplementedException("Read attribute {}", attr); 251 throw NotImplementedException("Read attribute {}", attr);
232 } 252 }
@@ -240,9 +260,12 @@ void EmitSetAttribute(EmitContext& ctx, IR::Attribute attr, Id value, [[maybe_un
240} 260}
241 261
242Id EmitGetAttributeIndexed(EmitContext& ctx, Id offset, Id vertex) { 262Id EmitGetAttributeIndexed(EmitContext& ctx, Id offset, Id vertex) {
243 if (ctx.stage == Stage::Geometry) { 263 switch (ctx.stage) {
264 case Stage::TessellationControl:
265 case Stage::TessellationEval:
266 case Stage::Geometry:
244 return ctx.OpFunctionCall(ctx.F32[1], ctx.indexed_load_func, offset, vertex); 267 return ctx.OpFunctionCall(ctx.F32[1], ctx.indexed_load_func, offset, vertex);
245 } else { 268 default:
246 return ctx.OpFunctionCall(ctx.F32[1], ctx.indexed_load_func, offset); 269 return ctx.OpFunctionCall(ctx.F32[1], ctx.indexed_load_func, offset);
247 } 270 }
248} 271}
@@ -251,6 +274,45 @@ void EmitSetAttributeIndexed(EmitContext& ctx, Id offset, Id value, [[maybe_unus
251 ctx.OpFunctionCall(ctx.void_id, ctx.indexed_store_func, offset, value); 274 ctx.OpFunctionCall(ctx.void_id, ctx.indexed_store_func, offset, value);
252} 275}
253 276
277Id EmitGetPatch(EmitContext& ctx, IR::Patch patch) {
278 if (!IR::IsGeneric(patch)) {
279 throw NotImplementedException("Non-generic patch load");
280 }
281 const u32 index{IR::GenericPatchIndex(patch)};
282 const Id element{ctx.Constant(ctx.U32[1], IR::GenericPatchElement(patch))};
283 const Id pointer{ctx.OpAccessChain(ctx.input_f32, ctx.patches.at(index), element)};
284 return ctx.OpLoad(ctx.F32[1], pointer);
285}
286
287void EmitSetPatch(EmitContext& ctx, IR::Patch patch, Id value) {
288 const Id pointer{[&] {
289 if (IR::IsGeneric(patch)) {
290 const u32 index{IR::GenericPatchIndex(patch)};
291 const Id element{ctx.Constant(ctx.U32[1], IR::GenericPatchElement(patch))};
292 return ctx.OpAccessChain(ctx.output_f32, ctx.patches.at(index), element);
293 }
294 switch (patch) {
295 case IR::Patch::TessellationLodLeft:
296 case IR::Patch::TessellationLodRight:
297 case IR::Patch::TessellationLodTop:
298 case IR::Patch::TessellationLodBottom: {
299 const u32 index{static_cast<u32>(patch) - u32(IR::Patch::TessellationLodLeft)};
300 const Id index_id{ctx.Constant(ctx.U32[1], index)};
301 return ctx.OpAccessChain(ctx.output_f32, ctx.output_tess_level_outer, index_id);
302 }
303 case IR::Patch::TessellationLodInteriorU:
304 return ctx.OpAccessChain(ctx.output_f32, ctx.output_tess_level_inner,
305 ctx.u32_zero_value);
306 case IR::Patch::TessellationLodInteriorV:
307 return ctx.OpAccessChain(ctx.output_f32, ctx.output_tess_level_inner,
308 ctx.Constant(ctx.U32[1], 1u));
309 default:
310 throw NotImplementedException("Patch {}", patch);
311 }
312 }()};
313 ctx.OpStore(pointer, value);
314}
315
254void EmitSetFragColor(EmitContext& ctx, u32 index, u32 component, Id value) { 316void EmitSetFragColor(EmitContext& ctx, u32 index, u32 component, Id value) {
255 const Id component_id{ctx.Constant(ctx.U32[1], component)}; 317 const Id component_id{ctx.Constant(ctx.U32[1], component)};
256 const Id pointer{ctx.OpAccessChain(ctx.output_f32, ctx.frag_color.at(index), component_id)}; 318 const Id pointer{ctx.OpAccessChain(ctx.output_f32, ctx.frag_color.at(index), component_id)};
@@ -301,6 +363,10 @@ Id EmitLocalInvocationId(EmitContext& ctx) {
301 return ctx.OpLoad(ctx.U32[3], ctx.local_invocation_id); 363 return ctx.OpLoad(ctx.U32[3], ctx.local_invocation_id);
302} 364}
303 365
366Id EmitInvocationId(EmitContext& ctx) {
367 return ctx.OpLoad(ctx.U32[1], ctx.invocation_id);
368}
369
304Id EmitIsHelperInvocation(EmitContext& ctx) { 370Id EmitIsHelperInvocation(EmitContext& ctx) {
305 return ctx.OpLoad(ctx.U1, ctx.is_helper_invocation); 371 return ctx.OpLoad(ctx.U1, ctx.is_helper_invocation);
306} 372}
diff --git a/src/shader_recompiler/frontend/ir/ir_emitter.cpp b/src/shader_recompiler/frontend/ir/ir_emitter.cpp
index d66eb17a6..b821d9f47 100644
--- a/src/shader_recompiler/frontend/ir/ir_emitter.cpp
+++ b/src/shader_recompiler/frontend/ir/ir_emitter.cpp
@@ -331,6 +331,14 @@ void IREmitter::SetAttributeIndexed(const U32& phys_address, const F32& value, c
331 Inst(Opcode::SetAttributeIndexed, phys_address, value, vertex); 331 Inst(Opcode::SetAttributeIndexed, phys_address, value, vertex);
332} 332}
333 333
334F32 IREmitter::GetPatch(Patch patch) {
335 return Inst<F32>(Opcode::GetPatch, patch);
336}
337
338void IREmitter::SetPatch(Patch patch, const F32& value) {
339 Inst(Opcode::SetPatch, patch, value);
340}
341
334void IREmitter::SetFragColor(u32 index, u32 component, const F32& value) { 342void IREmitter::SetFragColor(u32 index, u32 component, const F32& value) {
335 Inst(Opcode::SetFragColor, Imm32(index), Imm32(component), value); 343 Inst(Opcode::SetFragColor, Imm32(index), Imm32(component), value);
336} 344}
@@ -363,6 +371,10 @@ U32 IREmitter::LocalInvocationIdZ() {
363 return U32{CompositeExtract(Inst(Opcode::LocalInvocationId), 2)}; 371 return U32{CompositeExtract(Inst(Opcode::LocalInvocationId), 2)};
364} 372}
365 373
374U32 IREmitter::InvocationId() {
375 return Inst<U32>(Opcode::InvocationId);
376}
377
366U1 IREmitter::IsHelperInvocation() { 378U1 IREmitter::IsHelperInvocation() {
367 return Inst<U1>(Opcode::IsHelperInvocation); 379 return Inst<U1>(Opcode::IsHelperInvocation);
368} 380}
diff --git a/src/shader_recompiler/frontend/ir/ir_emitter.h b/src/shader_recompiler/frontend/ir/ir_emitter.h
index e70359eb1..7f8f1ad42 100644
--- a/src/shader_recompiler/frontend/ir/ir_emitter.h
+++ b/src/shader_recompiler/frontend/ir/ir_emitter.h
@@ -84,6 +84,9 @@ public:
84 [[nodiscard]] F32 GetAttributeIndexed(const U32& phys_address, const U32& vertex); 84 [[nodiscard]] F32 GetAttributeIndexed(const U32& phys_address, const U32& vertex);
85 void SetAttributeIndexed(const U32& phys_address, const F32& value, const U32& vertex); 85 void SetAttributeIndexed(const U32& phys_address, const F32& value, const U32& vertex);
86 86
87 [[nodiscard]] F32 GetPatch(Patch patch);
88 void SetPatch(Patch patch, const F32& value);
89
87 void SetFragColor(u32 index, u32 component, const F32& value); 90 void SetFragColor(u32 index, u32 component, const F32& value);
88 void SetFragDepth(const F32& value); 91 void SetFragDepth(const F32& value);
89 92
@@ -95,6 +98,7 @@ public:
95 [[nodiscard]] U32 LocalInvocationIdY(); 98 [[nodiscard]] U32 LocalInvocationIdY();
96 [[nodiscard]] U32 LocalInvocationIdZ(); 99 [[nodiscard]] U32 LocalInvocationIdZ();
97 100
101 [[nodiscard]] U32 InvocationId();
98 [[nodiscard]] U1 IsHelperInvocation(); 102 [[nodiscard]] U1 IsHelperInvocation();
99 103
100 [[nodiscard]] U32 LaneId(); 104 [[nodiscard]] U32 LaneId();
diff --git a/src/shader_recompiler/frontend/ir/microinstruction.cpp b/src/shader_recompiler/frontend/ir/microinstruction.cpp
index 204c55fa8..b2d7573d9 100644
--- a/src/shader_recompiler/frontend/ir/microinstruction.cpp
+++ b/src/shader_recompiler/frontend/ir/microinstruction.cpp
@@ -73,6 +73,7 @@ bool Inst::MayHaveSideEffects() const noexcept {
73 case Opcode::EndPrimitive: 73 case Opcode::EndPrimitive:
74 case Opcode::SetAttribute: 74 case Opcode::SetAttribute:
75 case Opcode::SetAttributeIndexed: 75 case Opcode::SetAttributeIndexed:
76 case Opcode::SetPatch:
76 case Opcode::SetFragColor: 77 case Opcode::SetFragColor:
77 case Opcode::SetFragDepth: 78 case Opcode::SetFragDepth:
78 case Opcode::WriteGlobalU8: 79 case Opcode::WriteGlobalU8:
diff --git a/src/shader_recompiler/frontend/ir/opcodes.cpp b/src/shader_recompiler/frontend/ir/opcodes.cpp
index 7d3e0b2ab..7f04b647b 100644
--- a/src/shader_recompiler/frontend/ir/opcodes.cpp
+++ b/src/shader_recompiler/frontend/ir/opcodes.cpp
@@ -24,6 +24,7 @@ constexpr Type Label{Type::Label};
24constexpr Type Reg{Type::Reg}; 24constexpr Type Reg{Type::Reg};
25constexpr Type Pred{Type::Pred}; 25constexpr Type Pred{Type::Pred};
26constexpr Type Attribute{Type::Attribute}; 26constexpr Type Attribute{Type::Attribute};
27constexpr Type Patch{Type::Patch};
27constexpr Type U1{Type::U1}; 28constexpr Type U1{Type::U1};
28constexpr Type U8{Type::U8}; 29constexpr Type U8{Type::U8};
29constexpr Type U16{Type::U16}; 30constexpr Type U16{Type::U16};
diff --git a/src/shader_recompiler/frontend/ir/opcodes.inc b/src/shader_recompiler/frontend/ir/opcodes.inc
index 7a21fe746..a86542cd8 100644
--- a/src/shader_recompiler/frontend/ir/opcodes.inc
+++ b/src/shader_recompiler/frontend/ir/opcodes.inc
@@ -48,6 +48,8 @@ OPCODE(GetAttribute, F32, Attr
48OPCODE(SetAttribute, Void, Attribute, F32, U32, ) 48OPCODE(SetAttribute, Void, Attribute, F32, U32, )
49OPCODE(GetAttributeIndexed, F32, U32, U32, ) 49OPCODE(GetAttributeIndexed, F32, U32, U32, )
50OPCODE(SetAttributeIndexed, Void, U32, F32, U32, ) 50OPCODE(SetAttributeIndexed, Void, U32, F32, U32, )
51OPCODE(GetPatch, F32, Patch, )
52OPCODE(SetPatch, Void, Patch, F32, )
51OPCODE(SetFragColor, Void, U32, U32, F32, ) 53OPCODE(SetFragColor, Void, U32, U32, F32, )
52OPCODE(SetFragDepth, Void, F32, ) 54OPCODE(SetFragDepth, Void, F32, )
53OPCODE(GetZFlag, U1, Void, ) 55OPCODE(GetZFlag, U1, Void, )
@@ -60,6 +62,7 @@ OPCODE(SetCFlag, Void, U1,
60OPCODE(SetOFlag, Void, U1, ) 62OPCODE(SetOFlag, Void, U1, )
61OPCODE(WorkgroupId, U32x3, ) 63OPCODE(WorkgroupId, U32x3, )
62OPCODE(LocalInvocationId, U32x3, ) 64OPCODE(LocalInvocationId, U32x3, )
65OPCODE(InvocationId, U32, )
63OPCODE(IsHelperInvocation, U1, ) 66OPCODE(IsHelperInvocation, U1, )
64 67
65// Undefined 68// Undefined
diff --git a/src/shader_recompiler/frontend/ir/patch.cpp b/src/shader_recompiler/frontend/ir/patch.cpp
new file mode 100644
index 000000000..1f770bc48
--- /dev/null
+++ b/src/shader_recompiler/frontend/ir/patch.cpp
@@ -0,0 +1,28 @@
1// Copyright 2021 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "shader_recompiler/frontend/ir/patch.h"
6#include "shader_recompiler/exception.h"
7
8namespace Shader::IR {
9
10bool IsGeneric(Patch patch) noexcept {
11 return patch >= Patch::Component0 && patch <= Patch::Component119;
12}
13
14u32 GenericPatchIndex(Patch patch) {
15 if (!IsGeneric(patch)) {
16 throw InvalidArgument("Patch {} is not generic", patch);
17 }
18 return (static_cast<u32>(patch) - static_cast<u32>(Patch::Component0)) / 4;
19}
20
21u32 GenericPatchElement(Patch patch) {
22 if (!IsGeneric(patch)) {
23 throw InvalidArgument("Patch {} is not generic", patch);
24 }
25 return (static_cast<u32>(patch) - static_cast<u32>(Patch::Component0)) % 4;
26}
27
28} // namespace Shader::IR
diff --git a/src/shader_recompiler/frontend/ir/patch.h b/src/shader_recompiler/frontend/ir/patch.h
new file mode 100644
index 000000000..6d66ff0d6
--- /dev/null
+++ b/src/shader_recompiler/frontend/ir/patch.h
@@ -0,0 +1,149 @@
1// Copyright 2021 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include "common/common_types.h"
8
9namespace Shader::IR {
10
11enum class Patch : u64 {
12 TessellationLodLeft,
13 TessellationLodTop,
14 TessellationLodRight,
15 TessellationLodBottom,
16 TessellationLodInteriorU,
17 TessellationLodInteriorV,
18 ComponentPadding0,
19 ComponentPadding1,
20 Component0,
21 Component1,
22 Component2,
23 Component3,
24 Component4,
25 Component5,
26 Component6,
27 Component7,
28 Component8,
29 Component9,
30 Component10,
31 Component11,
32 Component12,
33 Component13,
34 Component14,
35 Component15,
36 Component16,
37 Component17,
38 Component18,
39 Component19,
40 Component20,
41 Component21,
42 Component22,
43 Component23,
44 Component24,
45 Component25,
46 Component26,
47 Component27,
48 Component28,
49 Component29,
50 Component30,
51 Component31,
52 Component32,
53 Component33,
54 Component34,
55 Component35,
56 Component36,
57 Component37,
58 Component38,
59 Component39,
60 Component40,
61 Component41,
62 Component42,
63 Component43,
64 Component44,
65 Component45,
66 Component46,
67 Component47,
68 Component48,
69 Component49,
70 Component50,
71 Component51,
72 Component52,
73 Component53,
74 Component54,
75 Component55,
76 Component56,
77 Component57,
78 Component58,
79 Component59,
80 Component60,
81 Component61,
82 Component62,
83 Component63,
84 Component64,
85 Component65,
86 Component66,
87 Component67,
88 Component68,
89 Component69,
90 Component70,
91 Component71,
92 Component72,
93 Component73,
94 Component74,
95 Component75,
96 Component76,
97 Component77,
98 Component78,
99 Component79,
100 Component80,
101 Component81,
102 Component82,
103 Component83,
104 Component84,
105 Component85,
106 Component86,
107 Component87,
108 Component88,
109 Component89,
110 Component90,
111 Component91,
112 Component92,
113 Component93,
114 Component94,
115 Component95,
116 Component96,
117 Component97,
118 Component98,
119 Component99,
120 Component100,
121 Component101,
122 Component102,
123 Component103,
124 Component104,
125 Component105,
126 Component106,
127 Component107,
128 Component108,
129 Component109,
130 Component110,
131 Component111,
132 Component112,
133 Component113,
134 Component114,
135 Component115,
136 Component116,
137 Component117,
138 Component118,
139 Component119,
140};
141static_assert(static_cast<u64>(Patch::Component119) == 127);
142
143[[nodiscard]] bool IsGeneric(Patch patch) noexcept;
144
145[[nodiscard]] u32 GenericPatchIndex(Patch patch);
146
147[[nodiscard]] u32 GenericPatchElement(Patch patch);
148
149} // namespace Shader::IR
diff --git a/src/shader_recompiler/frontend/ir/type.h b/src/shader_recompiler/frontend/ir/type.h
index 9a32ca1e8..8b3b33852 100644
--- a/src/shader_recompiler/frontend/ir/type.h
+++ b/src/shader_recompiler/frontend/ir/type.h
@@ -20,26 +20,27 @@ enum class Type {
20 Reg = 1 << 2, 20 Reg = 1 << 2,
21 Pred = 1 << 3, 21 Pred = 1 << 3,
22 Attribute = 1 << 4, 22 Attribute = 1 << 4,
23 U1 = 1 << 5, 23 Patch = 1 << 5,
24 U8 = 1 << 6, 24 U1 = 1 << 6,
25 U16 = 1 << 7, 25 U8 = 1 << 7,
26 U32 = 1 << 8, 26 U16 = 1 << 8,
27 U64 = 1 << 9, 27 U32 = 1 << 9,
28 F16 = 1 << 10, 28 U64 = 1 << 10,
29 F32 = 1 << 11, 29 F16 = 1 << 11,
30 F64 = 1 << 12, 30 F32 = 1 << 12,
31 U32x2 = 1 << 13, 31 F64 = 1 << 13,
32 U32x3 = 1 << 14, 32 U32x2 = 1 << 14,
33 U32x4 = 1 << 15, 33 U32x3 = 1 << 15,
34 F16x2 = 1 << 16, 34 U32x4 = 1 << 16,
35 F16x3 = 1 << 17, 35 F16x2 = 1 << 17,
36 F16x4 = 1 << 18, 36 F16x3 = 1 << 18,
37 F32x2 = 1 << 19, 37 F16x4 = 1 << 19,
38 F32x3 = 1 << 20, 38 F32x2 = 1 << 20,
39 F32x4 = 1 << 21, 39 F32x3 = 1 << 21,
40 F64x2 = 1 << 22, 40 F32x4 = 1 << 22,
41 F64x3 = 1 << 23, 41 F64x2 = 1 << 23,
42 F64x4 = 1 << 24, 42 F64x3 = 1 << 24,
43 F64x4 = 1 << 25,
43}; 44};
44DECLARE_ENUM_FLAG_OPERATORS(Type) 45DECLARE_ENUM_FLAG_OPERATORS(Type)
45 46
diff --git a/src/shader_recompiler/frontend/ir/value.cpp b/src/shader_recompiler/frontend/ir/value.cpp
index 1e7ffb86d..bf5f8c0c2 100644
--- a/src/shader_recompiler/frontend/ir/value.cpp
+++ b/src/shader_recompiler/frontend/ir/value.cpp
@@ -18,6 +18,8 @@ Value::Value(IR::Pred value) noexcept : type{Type::Pred}, pred{value} {}
18 18
19Value::Value(IR::Attribute value) noexcept : type{Type::Attribute}, attribute{value} {} 19Value::Value(IR::Attribute value) noexcept : type{Type::Attribute}, attribute{value} {}
20 20
21Value::Value(IR::Patch value) noexcept : type{Type::Patch}, patch{value} {}
22
21Value::Value(bool value) noexcept : type{Type::U1}, imm_u1{value} {} 23Value::Value(bool value) noexcept : type{Type::U1}, imm_u1{value} {}
22 24
23Value::Value(u8 value) noexcept : type{Type::U8}, imm_u8{value} {} 25Value::Value(u8 value) noexcept : type{Type::U8}, imm_u8{value} {}
@@ -109,6 +111,11 @@ IR::Attribute Value::Attribute() const {
109 return attribute; 111 return attribute;
110} 112}
111 113
114IR::Patch Value::Patch() const {
115 ValidateAccess(Type::Patch);
116 return patch;
117}
118
112bool Value::U1() const { 119bool Value::U1() const {
113 if (IsIdentity()) { 120 if (IsIdentity()) {
114 return inst->Arg(0).U1(); 121 return inst->Arg(0).U1();
@@ -182,6 +189,8 @@ bool Value::operator==(const Value& other) const {
182 return pred == other.pred; 189 return pred == other.pred;
183 case Type::Attribute: 190 case Type::Attribute:
184 return attribute == other.attribute; 191 return attribute == other.attribute;
192 case Type::Patch:
193 return patch == other.patch;
185 case Type::U1: 194 case Type::U1:
186 return imm_u1 == other.imm_u1; 195 return imm_u1 == other.imm_u1;
187 case Type::U8: 196 case Type::U8:
diff --git a/src/shader_recompiler/frontend/ir/value.h b/src/shader_recompiler/frontend/ir/value.h
index a0962863d..303745563 100644
--- a/src/shader_recompiler/frontend/ir/value.h
+++ b/src/shader_recompiler/frontend/ir/value.h
@@ -9,6 +9,7 @@
9#include "shader_recompiler/frontend/ir/attribute.h" 9#include "shader_recompiler/frontend/ir/attribute.h"
10#include "shader_recompiler/frontend/ir/pred.h" 10#include "shader_recompiler/frontend/ir/pred.h"
11#include "shader_recompiler/frontend/ir/reg.h" 11#include "shader_recompiler/frontend/ir/reg.h"
12#include "shader_recompiler/frontend/ir/patch.h"
12#include "shader_recompiler/frontend/ir/type.h" 13#include "shader_recompiler/frontend/ir/type.h"
13 14
14namespace Shader::IR { 15namespace Shader::IR {
@@ -24,6 +25,7 @@ public:
24 explicit Value(IR::Reg value) noexcept; 25 explicit Value(IR::Reg value) noexcept;
25 explicit Value(IR::Pred value) noexcept; 26 explicit Value(IR::Pred value) noexcept;
26 explicit Value(IR::Attribute value) noexcept; 27 explicit Value(IR::Attribute value) noexcept;
28 explicit Value(IR::Patch value) noexcept;
27 explicit Value(bool value) noexcept; 29 explicit Value(bool value) noexcept;
28 explicit Value(u8 value) noexcept; 30 explicit Value(u8 value) noexcept;
29 explicit Value(u16 value) noexcept; 31 explicit Value(u16 value) noexcept;
@@ -46,6 +48,7 @@ public:
46 [[nodiscard]] IR::Reg Reg() const; 48 [[nodiscard]] IR::Reg Reg() const;
47 [[nodiscard]] IR::Pred Pred() const; 49 [[nodiscard]] IR::Pred Pred() const;
48 [[nodiscard]] IR::Attribute Attribute() const; 50 [[nodiscard]] IR::Attribute Attribute() const;
51 [[nodiscard]] IR::Patch Patch() const;
49 [[nodiscard]] bool U1() const; 52 [[nodiscard]] bool U1() const;
50 [[nodiscard]] u8 U8() const; 53 [[nodiscard]] u8 U8() const;
51 [[nodiscard]] u16 U16() const; 54 [[nodiscard]] u16 U16() const;
@@ -67,6 +70,7 @@ private:
67 IR::Reg reg; 70 IR::Reg reg;
68 IR::Pred pred; 71 IR::Pred pred;
69 IR::Attribute attribute; 72 IR::Attribute attribute;
73 IR::Patch patch;
70 bool imm_u1; 74 bool imm_u1;
71 u8 imm_u8; 75 u8 imm_u8;
72 u16 imm_u16; 76 u16 imm_u16;
diff --git a/src/shader_recompiler/frontend/maxwell/program.cpp b/src/shader_recompiler/frontend/maxwell/program.cpp
index ab67446c8..20a1d61cc 100644
--- a/src/shader_recompiler/frontend/maxwell/program.cpp
+++ b/src/shader_recompiler/frontend/maxwell/program.cpp
@@ -70,6 +70,11 @@ IR::Program TranslateProgram(ObjectPool<IR::Inst>& inst_pool, ObjectPool<IR::Blo
70 program.stage = env.ShaderStage(); 70 program.stage = env.ShaderStage();
71 program.local_memory_size = env.LocalMemorySize(); 71 program.local_memory_size = env.LocalMemorySize();
72 switch (program.stage) { 72 switch (program.stage) {
73 case Stage::TessellationControl: {
74 const ProgramHeader& sph{env.SPH()};
75 program.invocations = sph.common2.threads_per_input_primitive;
76 break;
77 }
73 case Stage::Geometry: { 78 case Stage::Geometry: {
74 const ProgramHeader& sph{env.SPH()}; 79 const ProgramHeader& sph{env.SPH()};
75 program.output_topology = sph.common3.output_topology; 80 program.output_topology = sph.common3.output_topology;
diff --git a/src/shader_recompiler/frontend/maxwell/translate/impl/load_store_attribute.cpp b/src/shader_recompiler/frontend/maxwell/translate/impl/load_store_attribute.cpp
index eb6a80de2..7d7dcc3cb 100644
--- a/src/shader_recompiler/frontend/maxwell/translate/impl/load_store_attribute.cpp
+++ b/src/shader_recompiler/frontend/maxwell/translate/impl/load_store_attribute.cpp
@@ -70,12 +70,6 @@ void TranslatorVisitor::ALD(u64 insn) {
70 BitField<47, 2, Size> size; 70 BitField<47, 2, Size> size;
71 } const ald{insn}; 71 } const ald{insn};
72 72
73 if (ald.o != 0) {
74 throw NotImplementedException("O");
75 }
76 if (ald.patch != 0) {
77 throw NotImplementedException("P");
78 }
79 const u64 offset{ald.absolute_offset.Value()}; 73 const u64 offset{ald.absolute_offset.Value()};
80 if (offset % 4 != 0) { 74 if (offset % 4 != 0) {
81 throw NotImplementedException("Unaligned absolute offset {}", offset); 75 throw NotImplementedException("Unaligned absolute offset {}", offset);
@@ -84,11 +78,19 @@ void TranslatorVisitor::ALD(u64 insn) {
84 const u32 num_elements{NumElements(ald.size)}; 78 const u32 num_elements{NumElements(ald.size)};
85 if (ald.index_reg == IR::Reg::RZ) { 79 if (ald.index_reg == IR::Reg::RZ) {
86 for (u32 element = 0; element < num_elements; ++element) { 80 for (u32 element = 0; element < num_elements; ++element) {
87 const IR::Attribute attr{offset / 4 + element}; 81 if (ald.patch != 0) {
88 F(ald.dest_reg + element, ir.GetAttribute(attr, vertex)); 82 const IR::Patch patch{offset / 4 + element};
83 F(ald.dest_reg + element, ir.GetPatch(patch));
84 } else {
85 const IR::Attribute attr{offset / 4 + element};
86 F(ald.dest_reg + element, ir.GetAttribute(attr, vertex));
87 }
89 } 88 }
90 return; 89 return;
91 } 90 }
91 if (ald.patch != 0) {
92 throw NotImplementedException("Indirect patch read");
93 }
92 HandleIndexed(*this, ald.index_reg, num_elements, [&](u32 element, IR::U32 final_offset) { 94 HandleIndexed(*this, ald.index_reg, num_elements, [&](u32 element, IR::U32 final_offset) {
93 F(ald.dest_reg + element, ir.GetAttributeIndexed(final_offset, vertex)); 95 F(ald.dest_reg + element, ir.GetAttributeIndexed(final_offset, vertex));
94 }); 96 });
@@ -106,9 +108,6 @@ void TranslatorVisitor::AST(u64 insn) {
106 BitField<47, 2, Size> size; 108 BitField<47, 2, Size> size;
107 } const ast{insn}; 109 } const ast{insn};
108 110
109 if (ast.patch != 0) {
110 throw NotImplementedException("P");
111 }
112 if (ast.index_reg != IR::Reg::RZ) { 111 if (ast.index_reg != IR::Reg::RZ) {
113 throw NotImplementedException("Indexed store"); 112 throw NotImplementedException("Indexed store");
114 } 113 }
@@ -120,11 +119,19 @@ void TranslatorVisitor::AST(u64 insn) {
120 const u32 num_elements{NumElements(ast.size)}; 119 const u32 num_elements{NumElements(ast.size)};
121 if (ast.index_reg == IR::Reg::RZ) { 120 if (ast.index_reg == IR::Reg::RZ) {
122 for (u32 element = 0; element < num_elements; ++element) { 121 for (u32 element = 0; element < num_elements; ++element) {
123 const IR::Attribute attr{offset / 4 + element}; 122 if (ast.patch != 0) {
124 ir.SetAttribute(attr, F(ast.src_reg + element), vertex); 123 const IR::Patch patch{offset / 4 + element};
124 ir.SetPatch(patch, F(ast.src_reg + element));
125 } else {
126 const IR::Attribute attr{offset / 4 + element};
127 ir.SetAttribute(attr, F(ast.src_reg + element), vertex);
128 }
125 } 129 }
126 return; 130 return;
127 } 131 }
132 if (ast.patch != 0) {
133 throw NotImplementedException("Indexed tessellation patch store");
134 }
128 HandleIndexed(*this, ast.index_reg, num_elements, [&](u32 element, IR::U32 final_offset) { 135 HandleIndexed(*this, ast.index_reg, num_elements, [&](u32 element, IR::U32 final_offset) {
129 ir.SetAttributeIndexed(final_offset, F(ast.src_reg + element), vertex); 136 ir.SetAttributeIndexed(final_offset, F(ast.src_reg + element), vertex);
130 }); 137 });
diff --git a/src/shader_recompiler/frontend/maxwell/translate/impl/move_special_register.cpp b/src/shader_recompiler/frontend/maxwell/translate/impl/move_special_register.cpp
index bc822d585..660b84c20 100644
--- a/src/shader_recompiler/frontend/maxwell/translate/impl/move_special_register.cpp
+++ b/src/shader_recompiler/frontend/maxwell/translate/impl/move_special_register.cpp
@@ -113,6 +113,8 @@ enum class SpecialRegister : u64 {
113 113
114[[nodiscard]] IR::U32 Read(IR::IREmitter& ir, SpecialRegister special_register) { 114[[nodiscard]] IR::U32 Read(IR::IREmitter& ir, SpecialRegister special_register) {
115 switch (special_register) { 115 switch (special_register) {
116 case SpecialRegister::SR_INVOCATION_ID:
117 return ir.InvocationId();
116 case SpecialRegister::SR_THREAD_KILL: 118 case SpecialRegister::SR_THREAD_KILL:
117 return IR::U32{ir.Select(ir.IsHelperInvocation(), ir.Imm32(-1), ir.Imm32(0))}; 119 return IR::U32{ir.Select(ir.IsHelperInvocation(), ir.Imm32(-1), ir.Imm32(0))};
118 case SpecialRegister::SR_INVOCATION_INFO: 120 case SpecialRegister::SR_INVOCATION_INFO:
diff --git a/src/shader_recompiler/ir_opt/collect_shader_info_pass.cpp b/src/shader_recompiler/ir_opt/collect_shader_info_pass.cpp
index 617ec05ce..aadcf7999 100644
--- a/src/shader_recompiler/ir_opt/collect_shader_info_pass.cpp
+++ b/src/shader_recompiler/ir_opt/collect_shader_info_pass.cpp
@@ -53,6 +53,10 @@ void GetAttribute(Info& info, IR::Attribute attribute) {
53 case IR::Attribute::PointSpriteT: 53 case IR::Attribute::PointSpriteT:
54 info.loads_point_coord = true; 54 info.loads_point_coord = true;
55 break; 55 break;
56 case IR::Attribute::TessellationEvaluationPointU:
57 case IR::Attribute::TessellationEvaluationPointV:
58 info.loads_tess_coord = true;
59 break;
56 default: 60 default:
57 throw NotImplementedException("Get attribute {}", attribute); 61 throw NotImplementedException("Get attribute {}", attribute);
58 } 62 }
@@ -94,6 +98,34 @@ void SetAttribute(Info& info, IR::Attribute attribute) {
94 } 98 }
95} 99}
96 100
101void GetPatch(Info& info, IR::Patch patch) {
102 if (!IR::IsGeneric(patch)) {
103 throw NotImplementedException("Reading non-generic patch {}", patch);
104 }
105 info.uses_patches.at(IR::GenericPatchIndex(patch)) = true;
106}
107
108void SetPatch(Info& info, IR::Patch patch) {
109 if (IR::IsGeneric(patch)) {
110 info.uses_patches.at(IR::GenericPatchIndex(patch)) = true;
111 return;
112 }
113 switch (patch) {
114 case IR::Patch::TessellationLodLeft:
115 case IR::Patch::TessellationLodTop:
116 case IR::Patch::TessellationLodRight:
117 case IR::Patch::TessellationLodBottom:
118 info.stores_tess_level_outer = true;
119 break;
120 case IR::Patch::TessellationLodInteriorU:
121 case IR::Patch::TessellationLodInteriorV:
122 info.stores_tess_level_inner = true;
123 break;
124 default:
125 throw NotImplementedException("Set patch {}", patch);
126 }
127}
128
97void VisitUsages(Info& info, IR::Inst& inst) { 129void VisitUsages(Info& info, IR::Inst& inst) {
98 switch (inst.GetOpcode()) { 130 switch (inst.GetOpcode()) {
99 case IR::Opcode::CompositeConstructF16x2: 131 case IR::Opcode::CompositeConstructF16x2:
@@ -350,6 +382,12 @@ void VisitUsages(Info& info, IR::Inst& inst) {
350 case IR::Opcode::SetAttribute: 382 case IR::Opcode::SetAttribute:
351 SetAttribute(info, inst.Arg(0).Attribute()); 383 SetAttribute(info, inst.Arg(0).Attribute());
352 break; 384 break;
385 case IR::Opcode::GetPatch:
386 GetPatch(info, inst.Arg(0).Patch());
387 break;
388 case IR::Opcode::SetPatch:
389 SetPatch(info, inst.Arg(0).Patch());
390 break;
353 case IR::Opcode::GetAttributeIndexed: 391 case IR::Opcode::GetAttributeIndexed:
354 info.loads_indexed_attributes = true; 392 info.loads_indexed_attributes = true;
355 break; 393 break;
@@ -368,6 +406,9 @@ void VisitUsages(Info& info, IR::Inst& inst) {
368 case IR::Opcode::LocalInvocationId: 406 case IR::Opcode::LocalInvocationId:
369 info.uses_local_invocation_id = true; 407 info.uses_local_invocation_id = true;
370 break; 408 break;
409 case IR::Opcode::InvocationId:
410 info.uses_invocation_id = true;
411 break;
371 case IR::Opcode::IsHelperInvocation: 412 case IR::Opcode::IsHelperInvocation:
372 info.uses_is_helper_invocation = true; 413 info.uses_is_helper_invocation = true;
373 break; 414 break;
diff --git a/src/shader_recompiler/profile.h b/src/shader_recompiler/profile.h
index c26017d75..3a04f075e 100644
--- a/src/shader_recompiler/profile.h
+++ b/src/shader_recompiler/profile.h
@@ -38,6 +38,18 @@ enum class CompareFunction {
38 Always, 38 Always,
39}; 39};
40 40
41enum class TessPrimitive {
42 Isolines,
43 Triangles,
44 Quads,
45};
46
47enum class TessSpacing {
48 Equal,
49 FractionalOdd,
50 FractionalEven,
51};
52
41struct TransformFeedbackVarying { 53struct TransformFeedbackVarying {
42 u32 buffer{}; 54 u32 buffer{};
43 u32 stride{}; 55 u32 stride{};
@@ -74,6 +86,10 @@ struct Profile {
74 bool convert_depth_mode{}; 86 bool convert_depth_mode{};
75 bool force_early_z{}; 87 bool force_early_z{};
76 88
89 TessPrimitive tess_primitive{};
90 TessSpacing tess_spacing{};
91 bool tess_clockwise{};
92
77 InputTopology input_topology{}; 93 InputTopology input_topology{};
78 94
79 std::optional<float> fixed_state_point_size; 95 std::optional<float> fixed_state_point_size;
diff --git a/src/shader_recompiler/shader_info.h b/src/shader_recompiler/shader_info.h
index 336c6131a..4dbf9ed12 100644
--- a/src/shader_recompiler/shader_info.h
+++ b/src/shader_recompiler/shader_info.h
@@ -101,8 +101,10 @@ struct Info {
101 101
102 bool uses_workgroup_id{}; 102 bool uses_workgroup_id{};
103 bool uses_local_invocation_id{}; 103 bool uses_local_invocation_id{};
104 bool uses_invocation_id{};
104 bool uses_is_helper_invocation{}; 105 bool uses_is_helper_invocation{};
105 bool uses_subgroup_invocation_id{}; 106 bool uses_subgroup_invocation_id{};
107 std::array<bool, 30> uses_patches{};
106 108
107 std::array<InputVarying, 32> input_generics{}; 109 std::array<InputVarying, 32> input_generics{};
108 bool loads_position{}; 110 bool loads_position{};
@@ -110,6 +112,7 @@ struct Info {
110 bool loads_vertex_id{}; 112 bool loads_vertex_id{};
111 bool loads_front_face{}; 113 bool loads_front_face{};
112 bool loads_point_coord{}; 114 bool loads_point_coord{};
115 bool loads_tess_coord{};
113 bool loads_indexed_attributes{}; 116 bool loads_indexed_attributes{};
114 117
115 std::array<bool, 8> stores_frag_color{}; 118 std::array<bool, 8> stores_frag_color{};
@@ -120,6 +123,8 @@ struct Info {
120 bool stores_clip_distance{}; 123 bool stores_clip_distance{};
121 bool stores_layer{}; 124 bool stores_layer{};
122 bool stores_viewport_index{}; 125 bool stores_viewport_index{};
126 bool stores_tess_level_outer{};
127 bool stores_tess_level_inner{};
123 bool stores_indexed_attributes{}; 128 bool stores_indexed_attributes{};
124 129
125 bool uses_fp16{}; 130 bool uses_fp16{};
diff --git a/src/video_core/renderer_vulkan/maxwell_to_vk.cpp b/src/video_core/renderer_vulkan/maxwell_to_vk.cpp
index dc4ff0da2..8f0b0b8ec 100644
--- a/src/video_core/renderer_vulkan/maxwell_to_vk.cpp
+++ b/src/video_core/renderer_vulkan/maxwell_to_vk.cpp
@@ -685,6 +685,19 @@ VkCullModeFlagBits CullFace(Maxwell::CullFace cull_face) {
685 return {}; 685 return {};
686} 686}
687 687
688VkPolygonMode PolygonMode(Maxwell::PolygonMode polygon_mode) {
689 switch (polygon_mode) {
690 case Maxwell::PolygonMode::Point:
691 return VK_POLYGON_MODE_POINT;
692 case Maxwell::PolygonMode::Line:
693 return VK_POLYGON_MODE_LINE;
694 case Maxwell::PolygonMode::Fill:
695 return VK_POLYGON_MODE_FILL;
696 }
697 UNIMPLEMENTED_MSG("Unimplemented polygon mode={}", polygon_mode);
698 return {};
699}
700
688VkComponentSwizzle SwizzleSource(Tegra::Texture::SwizzleSource swizzle) { 701VkComponentSwizzle SwizzleSource(Tegra::Texture::SwizzleSource swizzle) {
689 switch (swizzle) { 702 switch (swizzle) {
690 case Tegra::Texture::SwizzleSource::Zero: 703 case Tegra::Texture::SwizzleSource::Zero:
diff --git a/src/video_core/renderer_vulkan/maxwell_to_vk.h b/src/video_core/renderer_vulkan/maxwell_to_vk.h
index 9f78e15b6..50a599c11 100644
--- a/src/video_core/renderer_vulkan/maxwell_to_vk.h
+++ b/src/video_core/renderer_vulkan/maxwell_to_vk.h
@@ -65,6 +65,8 @@ VkFrontFace FrontFace(Maxwell::FrontFace front_face);
65 65
66VkCullModeFlagBits CullFace(Maxwell::CullFace cull_face); 66VkCullModeFlagBits CullFace(Maxwell::CullFace cull_face);
67 67
68VkPolygonMode PolygonMode(Maxwell::PolygonMode polygon_mode);
69
68VkComponentSwizzle SwizzleSource(Tegra::Texture::SwizzleSource swizzle); 70VkComponentSwizzle SwizzleSource(Tegra::Texture::SwizzleSource swizzle);
69 71
70VkViewportCoordinateSwizzleNV ViewportSwizzle(Maxwell::ViewportSwizzle swizzle); 72VkViewportCoordinateSwizzleNV ViewportSwizzle(Maxwell::ViewportSwizzle swizzle);
diff --git a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp
index 84720a6f9..d5e9dae0f 100644
--- a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp
+++ b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp
@@ -355,7 +355,8 @@ void GraphicsPipeline::MakePipeline(const Device& device, VkRenderPass render_pa
355 static_cast<VkBool32>(state.depth_clamp_disabled == 0 ? VK_TRUE : VK_FALSE), 355 static_cast<VkBool32>(state.depth_clamp_disabled == 0 ? VK_TRUE : VK_FALSE),
356 .rasterizerDiscardEnable = 356 .rasterizerDiscardEnable =
357 static_cast<VkBool32>(state.rasterize_enable == 0 ? VK_TRUE : VK_FALSE), 357 static_cast<VkBool32>(state.rasterize_enable == 0 ? VK_TRUE : VK_FALSE),
358 .polygonMode = VK_POLYGON_MODE_FILL, 358 .polygonMode =
359 MaxwellToVK::PolygonMode(FixedPipelineState::UnpackPolygonMode(state.polygon_mode)),
359 .cullMode = static_cast<VkCullModeFlags>( 360 .cullMode = static_cast<VkCullModeFlags>(
360 dynamic.cull_enable ? MaxwellToVK::CullFace(dynamic.CullFace()) : VK_CULL_MODE_NONE), 361 dynamic.cull_enable ? MaxwellToVK::CullFace(dynamic.CullFace()) : VK_CULL_MODE_NONE),
361 .frontFace = MaxwellToVK::FrontFace(dynamic.FrontFace()), 362 .frontFace = MaxwellToVK::FrontFace(dynamic.FrontFace()),
diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp
index ee22255bf..0bccc640a 100644
--- a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp
+++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp
@@ -1040,6 +1040,36 @@ Shader::Profile PipelineCache::MakeProfile(const GraphicsPipelineCacheKey& key,
1040 std::ranges::transform(key.state.attributes, profile.generic_input_types.begin(), 1040 std::ranges::transform(key.state.attributes, profile.generic_input_types.begin(),
1041 &CastAttributeType); 1041 &CastAttributeType);
1042 break; 1042 break;
1043 case Shader::Stage::TessellationEval:
1044 // We have to flip tessellation clockwise for some reason...
1045 profile.tess_clockwise = key.state.tessellation_clockwise == 0;
1046 profile.tess_primitive = [&key] {
1047 const u32 raw{key.state.tessellation_primitive.Value()};
1048 switch (static_cast<Maxwell::TessellationPrimitive>(raw)) {
1049 case Maxwell::TessellationPrimitive::Isolines:
1050 return Shader::TessPrimitive::Isolines;
1051 case Maxwell::TessellationPrimitive::Triangles:
1052 return Shader::TessPrimitive::Triangles;
1053 case Maxwell::TessellationPrimitive::Quads:
1054 return Shader::TessPrimitive::Quads;
1055 }
1056 UNREACHABLE();
1057 return Shader::TessPrimitive::Triangles;
1058 }();
1059 profile.tess_spacing = [&] {
1060 const u32 raw{key.state.tessellation_spacing};
1061 switch (static_cast<Maxwell::TessellationSpacing>(raw)) {
1062 case Maxwell::TessellationSpacing::Equal:
1063 return Shader::TessSpacing::Equal;
1064 case Maxwell::TessellationSpacing::FractionalOdd:
1065 return Shader::TessSpacing::FractionalOdd;
1066 case Maxwell::TessellationSpacing::FractionalEven:
1067 return Shader::TessSpacing::FractionalEven;
1068 }
1069 UNREACHABLE();
1070 return Shader::TessSpacing::Equal;
1071 }();
1072 break;
1043 case Shader::Stage::Geometry: 1073 case Shader::Stage::Geometry:
1044 if (program.output_topology == Shader::OutputTopology::PointList) { 1074 if (program.output_topology == Shader::OutputTopology::PointList) {
1045 profile.fixed_state_point_size = point_size; 1075 profile.fixed_state_point_size = point_size;
diff --git a/src/video_core/renderer_vulkan/vk_staging_buffer_pool.cpp b/src/video_core/renderer_vulkan/vk_staging_buffer_pool.cpp
index 0412b5234..555b12ed7 100644
--- a/src/video_core/renderer_vulkan/vk_staging_buffer_pool.cpp
+++ b/src/video_core/renderer_vulkan/vk_staging_buffer_pool.cpp
@@ -91,7 +91,7 @@ StagingBufferPool::StagingBufferPool(const Device& device_, MemoryAllocator& mem
91 .flags = 0, 91 .flags = 0,
92 .size = STREAM_BUFFER_SIZE, 92 .size = STREAM_BUFFER_SIZE,
93 .usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | 93 .usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT |
94 VK_BUFFER_USAGE_INDEX_BUFFER_BIT, 94 VK_BUFFER_USAGE_INDEX_BUFFER_BIT | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT,
95 .sharingMode = VK_SHARING_MODE_EXCLUSIVE, 95 .sharingMode = VK_SHARING_MODE_EXCLUSIVE,
96 .queueFamilyIndexCount = 0, 96 .queueFamilyIndexCount = 0,
97 .pQueueFamilyIndices = nullptr, 97 .pQueueFamilyIndices = nullptr,
diff --git a/src/video_core/vulkan_common/vulkan_device.cpp b/src/video_core/vulkan_common/vulkan_device.cpp
index 87cfe6312..f0de19ba1 100644
--- a/src/video_core/vulkan_common/vulkan_device.cpp
+++ b/src/video_core/vulkan_common/vulkan_device.cpp
@@ -225,7 +225,7 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
225 .drawIndirectFirstInstance = false, 225 .drawIndirectFirstInstance = false,
226 .depthClamp = true, 226 .depthClamp = true,
227 .depthBiasClamp = true, 227 .depthBiasClamp = true,
228 .fillModeNonSolid = false, 228 .fillModeNonSolid = true,
229 .depthBounds = false, 229 .depthBounds = false,
230 .wideLines = false, 230 .wideLines = false,
231 .largePoints = true, 231 .largePoints = true,
@@ -670,6 +670,7 @@ void Device::CheckSuitability(bool requires_swapchain) const {
670 std::make_pair(features.largePoints, "largePoints"), 670 std::make_pair(features.largePoints, "largePoints"),
671 std::make_pair(features.multiViewport, "multiViewport"), 671 std::make_pair(features.multiViewport, "multiViewport"),
672 std::make_pair(features.depthBiasClamp, "depthBiasClamp"), 672 std::make_pair(features.depthBiasClamp, "depthBiasClamp"),
673 std::make_pair(features.fillModeNonSolid, "fillModeNonSolid"),
673 std::make_pair(features.geometryShader, "geometryShader"), 674 std::make_pair(features.geometryShader, "geometryShader"),
674 std::make_pair(features.tessellationShader, "tessellationShader"), 675 std::make_pair(features.tessellationShader, "tessellationShader"),
675 std::make_pair(features.occlusionQueryPrecise, "occlusionQueryPrecise"), 676 std::make_pair(features.occlusionQueryPrecise, "occlusionQueryPrecise"),