summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorGravatar ReinUsesLisp2021-03-20 19:11:56 -0300
committerGravatar ameerj2021-07-22 21:51:23 -0400
commit76c8a962ac4eae77e71d66a72c448930240339f9 (patch)
tree267bdb72f0fad43779080cd1907dd8159a6c7154 /src
parentshader: Refactor half floating instructions (diff)
downloadyuzu-76c8a962ac4eae77e71d66a72c448930240339f9.tar.gz
yuzu-76c8a962ac4eae77e71d66a72c448930240339f9.tar.xz
yuzu-76c8a962ac4eae77e71d66a72c448930240339f9.zip
spirv: Implement VertexId and InstanceId, refactor code
Diffstat (limited to 'src')
-rw-r--r--src/shader_recompiler/backend/spirv/emit_context.cpp191
-rw-r--r--src/shader_recompiler/backend/spirv/emit_context.h14
-rw-r--r--src/shader_recompiler/backend/spirv/emit_spirv.cpp107
-rw-r--r--src/shader_recompiler/backend/spirv/emit_spirv.h4
-rw-r--r--src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp16
-rw-r--r--src/shader_recompiler/backend/spirv/emit_spirv_memory.cpp46
-rw-r--r--src/shader_recompiler/ir_opt/collect_shader_info_pass.cpp6
-rw-r--r--src/shader_recompiler/profile.h1
-rw-r--r--src/shader_recompiler/shader_info.h2
-rw-r--r--src/video_core/renderer_vulkan/vk_pipeline_cache.cpp1
10 files changed, 244 insertions, 144 deletions
diff --git a/src/shader_recompiler/backend/spirv/emit_context.cpp b/src/shader_recompiler/backend/spirv/emit_context.cpp
index 6c8f16562..4a4de3676 100644
--- a/src/shader_recompiler/backend/spirv/emit_context.cpp
+++ b/src/shader_recompiler/backend/spirv/emit_context.cpp
@@ -48,6 +48,25 @@ Id ImageType(EmitContext& ctx, const TextureDescriptor& desc) {
48 } 48 }
49 throw InvalidArgument("Invalid texture type {}", desc.type); 49 throw InvalidArgument("Invalid texture type {}", desc.type);
50} 50}
51
52Id DefineVariable(EmitContext& ctx, Id type, std::optional<spv::BuiltIn> builtin,
53 spv::StorageClass storage_class) {
54 const Id pointer_type{ctx.TypePointer(storage_class, type)};
55 const Id id{ctx.AddGlobalVariable(pointer_type, storage_class)};
56 if (builtin) {
57 ctx.Decorate(id, spv::Decoration::BuiltIn, *builtin);
58 }
59 ctx.interfaces.push_back(id);
60 return id;
61}
62
63Id DefineInput(EmitContext& ctx, Id type, std::optional<spv::BuiltIn> builtin = std::nullopt) {
64 return DefineVariable(ctx, type, builtin, spv::StorageClass::Input);
65}
66
67Id DefineOutput(EmitContext& ctx, Id type, std::optional<spv::BuiltIn> builtin = std::nullopt) {
68 return DefineVariable(ctx, type, builtin, spv::StorageClass::Output);
69}
51} // Anonymous namespace 70} // Anonymous namespace
52 71
53void VectorTypes::Define(Sirit::Module& sirit_ctx, Id base_type, std::string_view name) { 72void VectorTypes::Define(Sirit::Module& sirit_ctx, Id base_type, std::string_view name) {
@@ -144,59 +163,8 @@ void EmitContext::DefineCommonConstants() {
144} 163}
145 164
146void EmitContext::DefineInterfaces(const Info& info, Stage stage) { 165void EmitContext::DefineInterfaces(const Info& info, Stage stage) {
147 const auto define{ 166 DefineInputs(info, stage);
148 [this](Id type, std::optional<spv::BuiltIn> builtin, spv::StorageClass storage_class) { 167 DefineOutputs(info, stage);
149 const Id pointer_type{TypePointer(storage_class, type)};
150 const Id id{AddGlobalVariable(pointer_type, storage_class)};
151 if (builtin) {
152 Decorate(id, spv::Decoration::BuiltIn, *builtin);
153 }
154 interfaces.push_back(id);
155 return id;
156 }};
157 using namespace std::placeholders;
158 const auto define_input{std::bind(define, _1, _2, spv::StorageClass::Input)};
159 const auto define_output{std::bind(define, _1, _2, spv::StorageClass::Output)};
160
161 if (info.uses_workgroup_id) {
162 workgroup_id = define_input(U32[3], spv::BuiltIn::WorkgroupId);
163 }
164 if (info.uses_local_invocation_id) {
165 local_invocation_id = define_input(U32[3], spv::BuiltIn::LocalInvocationId);
166 }
167 if (info.loads_position) {
168 const bool is_fragment{stage != Stage::Fragment};
169 const spv::BuiltIn built_in{is_fragment ? spv::BuiltIn::Position : spv::BuiltIn::FragCoord};
170 input_position = define_input(F32[4], built_in);
171 }
172 for (size_t i = 0; i < info.loads_generics.size(); ++i) {
173 if (info.loads_generics[i]) {
174 // FIXME: Declare size from input
175 input_generics[i] = define_input(F32[4], std::nullopt);
176 Decorate(input_generics[i], spv::Decoration::Location, static_cast<u32>(i));
177 Name(input_generics[i], fmt::format("in_attr{}", i));
178 }
179 }
180 if (info.stores_position) {
181 output_position = define_output(F32[4], spv::BuiltIn::Position);
182 }
183 for (size_t i = 0; i < info.stores_generics.size(); ++i) {
184 if (info.stores_generics[i]) {
185 output_generics[i] = define_output(F32[4], std::nullopt);
186 Decorate(output_generics[i], spv::Decoration::Location, static_cast<u32>(i));
187 Name(output_generics[i], fmt::format("out_attr{}", i));
188 }
189 }
190 if (stage == Stage::Fragment) {
191 for (size_t i = 0; i < 8; ++i) {
192 if (!info.stores_frag_color[i]) {
193 continue;
194 }
195 frag_color[i] = define_output(F32[4], std::nullopt);
196 Decorate(frag_color[i], spv::Decoration::Location, static_cast<u32>(i));
197 Name(frag_color[i], fmt::format("frag_color{}", i));
198 }
199 }
200} 168}
201 169
202void EmitContext::DefineConstantBuffers(const Info& info, u32& binding) { 170void EmitContext::DefineConstantBuffers(const Info& info, u32& binding) {
@@ -225,33 +193,6 @@ void EmitContext::DefineConstantBuffers(const Info& info, u32& binding) {
225 } 193 }
226} 194}
227 195
228void EmitContext::DefineConstantBuffers(const Info& info, Id UniformDefinitions::*member_type,
229 u32 binding, Id type, char type_char, u32 element_size) {
230 const Id array_type{TypeArray(type, Constant(U32[1], 65536U / element_size))};
231 Decorate(array_type, spv::Decoration::ArrayStride, element_size);
232
233 const Id struct_type{TypeStruct(array_type)};
234 Name(struct_type, fmt::format("cbuf_block_{}{}", type_char, element_size * CHAR_BIT));
235 Decorate(struct_type, spv::Decoration::Block);
236 MemberName(struct_type, 0, "data");
237 MemberDecorate(struct_type, 0, spv::Decoration::Offset, 0U);
238
239 const Id struct_pointer_type{TypePointer(spv::StorageClass::Uniform, struct_type)};
240 const Id uniform_type{TypePointer(spv::StorageClass::Uniform, type)};
241 uniform_types.*member_type = uniform_type;
242
243 for (const ConstantBufferDescriptor& desc : info.constant_buffer_descriptors) {
244 const Id id{AddGlobalVariable(struct_pointer_type, spv::StorageClass::Uniform)};
245 Decorate(id, spv::Decoration::Binding, binding);
246 Decorate(id, spv::Decoration::DescriptorSet, 0U);
247 Name(id, fmt::format("c{}", desc.index));
248 for (size_t i = 0; i < desc.count; ++i) {
249 cbufs[desc.index + i].*member_type = id;
250 }
251 binding += desc.count;
252 }
253}
254
255void EmitContext::DefineStorageBuffers(const Info& info, u32& binding) { 196void EmitContext::DefineStorageBuffers(const Info& info, u32& binding) {
256 if (info.storage_buffers_descriptors.empty()) { 197 if (info.storage_buffers_descriptors.empty()) {
257 return; 198 return;
@@ -311,4 +252,94 @@ void EmitContext::DefineLabels(IR::Program& program) {
311 } 252 }
312} 253}
313 254
255void EmitContext::DefineInputs(const Info& info, Stage stage) {
256 if (info.uses_workgroup_id) {
257 workgroup_id = DefineInput(*this, U32[3], spv::BuiltIn::WorkgroupId);
258 }
259 if (info.uses_local_invocation_id) {
260 local_invocation_id = DefineInput(*this, U32[3], spv::BuiltIn::LocalInvocationId);
261 }
262 if (info.loads_position) {
263 const bool is_fragment{stage != Stage::Fragment};
264 const spv::BuiltIn built_in{is_fragment ? spv::BuiltIn::Position : spv::BuiltIn::FragCoord};
265 input_position = DefineInput(*this, F32[4], built_in);
266 }
267 if (info.loads_instance_id) {
268 if (profile.support_vertex_instance_id) {
269 instance_id = DefineInput(*this, U32[1], spv::BuiltIn::InstanceId);
270 } else {
271 instance_index = DefineInput(*this, U32[1], spv::BuiltIn::InstanceIndex);
272 base_instance = DefineInput(*this, U32[1], spv::BuiltIn::BaseInstance);
273 }
274 }
275 if (info.loads_vertex_id) {
276 if (profile.support_vertex_instance_id) {
277 vertex_id = DefineInput(*this, U32[1], spv::BuiltIn::VertexId);
278 } else {
279 vertex_index = DefineInput(*this, U32[1], spv::BuiltIn::VertexIndex);
280 base_vertex = DefineInput(*this, U32[1], spv::BuiltIn::BaseVertex);
281 }
282 }
283 for (size_t index = 0; index < info.loads_generics.size(); ++index) {
284 if (!info.loads_generics[index]) {
285 continue;
286 }
287 // FIXME: Declare size from input
288 const Id id{DefineInput(*this, F32[4])};
289 Decorate(id, spv::Decoration::Location, static_cast<u32>(index));
290 Name(id, fmt::format("in_attr{}", index));
291 input_generics[index] = id;
292 }
293}
294
295void EmitContext::DefineConstantBuffers(const Info& info, Id UniformDefinitions::*member_type,
296 u32 binding, Id type, char type_char, u32 element_size) {
297 const Id array_type{TypeArray(type, Constant(U32[1], 65536U / element_size))};
298 Decorate(array_type, spv::Decoration::ArrayStride, element_size);
299
300 const Id struct_type{TypeStruct(array_type)};
301 Name(struct_type, fmt::format("cbuf_block_{}{}", type_char, element_size * CHAR_BIT));
302 Decorate(struct_type, spv::Decoration::Block);
303 MemberName(struct_type, 0, "data");
304 MemberDecorate(struct_type, 0, spv::Decoration::Offset, 0U);
305
306 const Id struct_pointer_type{TypePointer(spv::StorageClass::Uniform, struct_type)};
307 const Id uniform_type{TypePointer(spv::StorageClass::Uniform, type)};
308 uniform_types.*member_type = uniform_type;
309
310 for (const ConstantBufferDescriptor& desc : info.constant_buffer_descriptors) {
311 const Id id{AddGlobalVariable(struct_pointer_type, spv::StorageClass::Uniform)};
312 Decorate(id, spv::Decoration::Binding, binding);
313 Decorate(id, spv::Decoration::DescriptorSet, 0U);
314 Name(id, fmt::format("c{}", desc.index));
315 for (size_t i = 0; i < desc.count; ++i) {
316 cbufs[desc.index + i].*member_type = id;
317 }
318 binding += desc.count;
319 }
320}
321
322void EmitContext::DefineOutputs(const Info& info, Stage stage) {
323 if (info.stores_position) {
324 output_position = DefineOutput(*this, F32[4], spv::BuiltIn::Position);
325 }
326 for (size_t i = 0; i < info.stores_generics.size(); ++i) {
327 if (info.stores_generics[i]) {
328 output_generics[i] = DefineOutput(*this, F32[4]);
329 Decorate(output_generics[i], spv::Decoration::Location, static_cast<u32>(i));
330 Name(output_generics[i], fmt::format("out_attr{}", i));
331 }
332 }
333 if (stage == Stage::Fragment) {
334 for (size_t i = 0; i < 8; ++i) {
335 if (!info.stores_frag_color[i]) {
336 continue;
337 }
338 frag_color[i] = DefineOutput(*this, F32[4]);
339 Decorate(frag_color[i], spv::Decoration::Location, static_cast<u32>(i));
340 Name(frag_color[i], fmt::format("frag_color{}", i));
341 }
342 }
343}
344
314} // namespace Shader::Backend::SPIRV 345} // namespace Shader::Backend::SPIRV
diff --git a/src/shader_recompiler/backend/spirv/emit_context.h b/src/shader_recompiler/backend/spirv/emit_context.h
index 2d7961ac3..9b9e0d6b1 100644
--- a/src/shader_recompiler/backend/spirv/emit_context.h
+++ b/src/shader_recompiler/backend/spirv/emit_context.h
@@ -82,6 +82,12 @@ public:
82 82
83 Id workgroup_id{}; 83 Id workgroup_id{};
84 Id local_invocation_id{}; 84 Id local_invocation_id{};
85 Id instance_id{};
86 Id instance_index{};
87 Id base_instance{};
88 Id vertex_id{};
89 Id vertex_index{};
90 Id base_vertex{};
85 91
86 Id input_position{}; 92 Id input_position{};
87 std::array<Id, 32> input_generics{}; 93 std::array<Id, 32> input_generics{};
@@ -99,11 +105,15 @@ private:
99 void DefineCommonConstants(); 105 void DefineCommonConstants();
100 void DefineInterfaces(const Info& info, Stage stage); 106 void DefineInterfaces(const Info& info, Stage stage);
101 void DefineConstantBuffers(const Info& info, u32& binding); 107 void DefineConstantBuffers(const Info& info, u32& binding);
102 void DefineConstantBuffers(const Info& info, Id UniformDefinitions::*member_type, u32 binding,
103 Id type, char type_char, u32 element_size);
104 void DefineStorageBuffers(const Info& info, u32& binding); 108 void DefineStorageBuffers(const Info& info, u32& binding);
105 void DefineTextures(const Info& info, u32& binding); 109 void DefineTextures(const Info& info, u32& binding);
106 void DefineLabels(IR::Program& program); 110 void DefineLabels(IR::Program& program);
111
112 void DefineConstantBuffers(const Info& info, Id UniformDefinitions::*member_type, u32 binding,
113 Id type, char type_char, u32 element_size);
114
115 void DefineInputs(const Info& info, Stage stage);
116 void DefineOutputs(const Info& info, Stage stage);
107}; 117};
108 118
109} // namespace Shader::Backend::SPIRV 119} // 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 b8978b94a..efd0b70b7 100644
--- a/src/shader_recompiler/backend/spirv/emit_spirv.cpp
+++ b/src/shader_recompiler/backend/spirv/emit_spirv.cpp
@@ -113,6 +113,43 @@ Id TypeId(const EmitContext& ctx, IR::Type type) {
113 } 113 }
114} 114}
115 115
116Id DefineMain(EmitContext& ctx, IR::Program& program) {
117 const Id void_function{ctx.TypeFunction(ctx.void_id)};
118 const Id main{ctx.OpFunction(ctx.void_id, spv::FunctionControlMask::MaskNone, void_function)};
119 for (IR::Block* const block : program.blocks) {
120 ctx.AddLabel(block->Definition<Id>());
121 for (IR::Inst& inst : block->Instructions()) {
122 EmitInst(ctx, &inst);
123 }
124 }
125 ctx.OpFunctionEnd();
126 return main;
127}
128
129void DefineEntryPoint(Environment& env, EmitContext& ctx, Id main) {
130 const std::span interfaces(ctx.interfaces.data(), ctx.interfaces.size());
131 spv::ExecutionModel execution_model{};
132 switch (env.ShaderStage()) {
133 case Shader::Stage::Compute: {
134 const std::array<u32, 3> workgroup_size{env.WorkgroupSize()};
135 execution_model = spv::ExecutionModel::GLCompute;
136 ctx.AddExecutionMode(main, spv::ExecutionMode::LocalSize, workgroup_size[0],
137 workgroup_size[1], workgroup_size[2]);
138 break;
139 }
140 case Shader::Stage::VertexB:
141 execution_model = spv::ExecutionModel::Vertex;
142 break;
143 case Shader::Stage::Fragment:
144 execution_model = spv::ExecutionModel::Fragment;
145 ctx.AddExecutionMode(main, spv::ExecutionMode::OriginUpperLeft);
146 break;
147 default:
148 throw NotImplementedException("Stage {}", env.ShaderStage());
149 }
150 ctx.AddEntryPoint(execution_model, main, "main", interfaces);
151}
152
116void SetupDenormControl(const Profile& profile, const IR::Program& program, EmitContext& ctx, 153void SetupDenormControl(const Profile& profile, const IR::Program& program, EmitContext& ctx,
117 Id main_func) { 154 Id main_func) {
118 if (!profile.support_float_controls) { 155 if (!profile.support_float_controls) {
@@ -173,6 +210,25 @@ void SetupDenormControl(const Profile& profile, const IR::Program& program, Emit
173 } 210 }
174} 211}
175 212
213void SetupCapabilities(const Profile& profile, const Info& info, EmitContext& ctx) {
214 if (info.uses_sampled_1d) {
215 ctx.AddCapability(spv::Capability::Sampled1D);
216 }
217 if (info.uses_sparse_residency) {
218 ctx.AddCapability(spv::Capability::SparseResidency);
219 }
220 if (info.uses_demote_to_helper_invocation) {
221 ctx.AddExtension("SPV_EXT_demote_to_helper_invocation");
222 ctx.AddCapability(spv::Capability::DemoteToHelperInvocationEXT);
223 }
224 if (!profile.support_vertex_instance_id && (info.loads_instance_id || info.loads_vertex_id)) {
225 ctx.AddExtension("SPV_KHR_shader_draw_parameters");
226 ctx.AddCapability(spv::Capability::DrawParameters);
227 }
228 // TODO: Track this usage
229 ctx.AddCapability(spv::Capability::ImageGatherExtended);
230}
231
176Id PhiArgDef(EmitContext& ctx, IR::Inst* inst, size_t index) { 232Id PhiArgDef(EmitContext& ctx, IR::Inst* inst, size_t index) {
177 // Phi nodes can have forward declarations, if an argument is not defined provide a forward 233 // Phi nodes can have forward declarations, if an argument is not defined provide a forward
178 // declaration of it. Invoke will take care of giving it the right definition when it's 234 // declaration of it. Invoke will take care of giving it the right definition when it's
@@ -202,53 +258,10 @@ Id PhiArgDef(EmitContext& ctx, IR::Inst* inst, size_t index) {
202std::vector<u32> EmitSPIRV(const Profile& profile, Environment& env, IR::Program& program, 258std::vector<u32> EmitSPIRV(const Profile& profile, Environment& env, IR::Program& program,
203 u32& binding) { 259 u32& binding) {
204 EmitContext ctx{profile, program, binding}; 260 EmitContext ctx{profile, program, binding};
205 const Id void_function{ctx.TypeFunction(ctx.void_id)}; 261 const Id main{DefineMain(ctx, program)};
206 const Id func{ctx.OpFunction(ctx.void_id, spv::FunctionControlMask::MaskNone, void_function)}; 262 DefineEntryPoint(env, ctx, main);
207 for (IR::Block* const block : program.blocks) { 263 SetupDenormControl(profile, program, ctx, main);
208 ctx.AddLabel(block->Definition<Id>()); 264 SetupCapabilities(profile, program.info, ctx);
209 for (IR::Inst& inst : block->Instructions()) {
210 EmitInst(ctx, &inst);
211 }
212 }
213 ctx.OpFunctionEnd();
214
215 const std::span interfaces(ctx.interfaces.data(), ctx.interfaces.size());
216 spv::ExecutionModel execution_model{};
217 switch (env.ShaderStage()) {
218 case Shader::Stage::Compute: {
219 const std::array<u32, 3> workgroup_size{env.WorkgroupSize()};
220 execution_model = spv::ExecutionModel::GLCompute;
221 ctx.AddExecutionMode(func, spv::ExecutionMode::LocalSize, workgroup_size[0],
222 workgroup_size[1], workgroup_size[2]);
223 break;
224 }
225 case Shader::Stage::VertexB:
226 execution_model = spv::ExecutionModel::Vertex;
227 break;
228 case Shader::Stage::Fragment:
229 execution_model = spv::ExecutionModel::Fragment;
230 ctx.AddExecutionMode(func, spv::ExecutionMode::OriginUpperLeft);
231 break;
232 default:
233 throw NotImplementedException("Stage {}", env.ShaderStage());
234 }
235 ctx.AddEntryPoint(execution_model, func, "main", interfaces);
236
237 SetupDenormControl(profile, program, ctx, func);
238 const Info& info{program.info};
239 if (info.uses_sampled_1d) {
240 ctx.AddCapability(spv::Capability::Sampled1D);
241 }
242 if (info.uses_sparse_residency) {
243 ctx.AddCapability(spv::Capability::SparseResidency);
244 }
245 if (info.uses_demote_to_helper_invocation) {
246 ctx.AddExtension("SPV_EXT_demote_to_helper_invocation");
247 ctx.AddCapability(spv::Capability::DemoteToHelperInvocationEXT);
248 }
249 // TODO: Track this usage
250 ctx.AddCapability(spv::Capability::ImageGatherExtended);
251
252 return ctx.Assemble(); 265 return ctx.Assemble();
253} 266}
254 267
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv.h b/src/shader_recompiler/backend/spirv/emit_spirv.h
index 1fe65f8a9..e297a0e20 100644
--- a/src/shader_recompiler/backend/spirv/emit_spirv.h
+++ b/src/shader_recompiler/backend/spirv/emit_spirv.h
@@ -81,8 +81,8 @@ void EmitLoadStorageS8(EmitContext& ctx);
81void EmitLoadStorageU16(EmitContext& ctx); 81void EmitLoadStorageU16(EmitContext& ctx);
82void EmitLoadStorageS16(EmitContext& ctx); 82void EmitLoadStorageS16(EmitContext& ctx);
83Id EmitLoadStorage32(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset); 83Id EmitLoadStorage32(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset);
84void EmitLoadStorage64(EmitContext& ctx); 84Id EmitLoadStorage64(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset);
85void EmitLoadStorage128(EmitContext& ctx); 85Id EmitLoadStorage128(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset);
86void EmitWriteStorageU8(EmitContext& ctx); 86void EmitWriteStorageU8(EmitContext& ctx);
87void EmitWriteStorageS8(EmitContext& ctx); 87void EmitWriteStorageS8(EmitContext& ctx);
88void EmitWriteStorageU16(EmitContext& ctx); 88void EmitWriteStorageU16(EmitContext& ctx);
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 02d115740..052b84151 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
@@ -19,6 +19,10 @@ Id InputAttrPointer(EmitContext& ctx, IR::Attribute attr) {
19 case IR::Attribute::PositionZ: 19 case IR::Attribute::PositionZ:
20 case IR::Attribute::PositionW: 20 case IR::Attribute::PositionW:
21 return ctx.OpAccessChain(ctx.input_f32, ctx.input_position, element_id()); 21 return ctx.OpAccessChain(ctx.input_f32, ctx.input_position, element_id());
22 case IR::Attribute::InstanceId:
23 return ctx.OpLoad(ctx.U32[1], ctx.instance_id);
24 case IR::Attribute::VertexId:
25 return ctx.OpLoad(ctx.U32[1], ctx.vertex_id);
22 default: 26 default:
23 throw NotImplementedException("Read attribute {}", attr); 27 throw NotImplementedException("Read attribute {}", attr);
24 } 28 }
@@ -125,6 +129,18 @@ Id EmitGetCbufU64(EmitContext& ctx, const IR::Value& binding, const IR::Value& o
125} 129}
126 130
127Id EmitGetAttribute(EmitContext& ctx, IR::Attribute attr) { 131Id EmitGetAttribute(EmitContext& ctx, IR::Attribute attr) {
132 if (!ctx.profile.support_vertex_instance_id) {
133 switch (attr) {
134 case IR::Attribute::InstanceId:
135 return ctx.OpISub(ctx.U32[1], ctx.OpLoad(ctx.U32[1], ctx.instance_index),
136 ctx.OpLoad(ctx.U32[1], ctx.base_instance));
137 case IR::Attribute::VertexId:
138 return ctx.OpISub(ctx.U32[1], ctx.OpLoad(ctx.U32[1], ctx.vertex_index),
139 ctx.OpLoad(ctx.U32[1], ctx.base_vertex));
140 default:
141 break;
142 }
143 }
128 return ctx.OpLoad(ctx.F32[1], InputAttrPointer(ctx, attr)); 144 return ctx.OpLoad(ctx.F32[1], InputAttrPointer(ctx, attr));
129} 145}
130 146
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_memory.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_memory.cpp
index 7d3efc741..088bd3059 100644
--- a/src/shader_recompiler/backend/spirv/emit_spirv_memory.cpp
+++ b/src/shader_recompiler/backend/spirv/emit_spirv_memory.cpp
@@ -7,8 +7,8 @@
7#include "shader_recompiler/backend/spirv/emit_spirv.h" 7#include "shader_recompiler/backend/spirv/emit_spirv.h"
8 8
9namespace Shader::Backend::SPIRV { 9namespace Shader::Backend::SPIRV {
10 10namespace {
11static Id StorageIndex(EmitContext& ctx, const IR::Value& offset, size_t element_size) { 11Id StorageIndex(EmitContext& ctx, const IR::Value& offset, size_t element_size) {
12 if (offset.IsImmediate()) { 12 if (offset.IsImmediate()) {
13 const u32 imm_offset{static_cast<u32>(offset.U32() / element_size)}; 13 const u32 imm_offset{static_cast<u32>(offset.U32() / element_size)};
14 return ctx.Constant(ctx.U32[1], imm_offset); 14 return ctx.Constant(ctx.U32[1], imm_offset);
@@ -22,6 +22,32 @@ static Id StorageIndex(EmitContext& ctx, const IR::Value& offset, size_t element
22 return ctx.OpShiftRightLogical(ctx.U32[1], index, shift_id); 22 return ctx.OpShiftRightLogical(ctx.U32[1], index, shift_id);
23} 23}
24 24
25Id EmitLoadStorage(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
26 u32 num_components) {
27 // TODO: Support reinterpreting bindings, guaranteed to be aligned
28 if (!binding.IsImmediate()) {
29 throw NotImplementedException("Dynamic storage buffer indexing");
30 }
31 const Id ssbo{ctx.ssbos[binding.U32()]};
32 const Id base_index{StorageIndex(ctx, offset, sizeof(u32))};
33 std::array<Id, 4> components;
34 for (u32 element = 0; element < num_components; ++element) {
35 Id index{base_index};
36 if (element > 0) {
37 index = ctx.OpIAdd(ctx.U32[1], base_index, ctx.Constant(ctx.U32[1], element));
38 }
39 const Id pointer{ctx.OpAccessChain(ctx.storage_u32, ssbo, ctx.u32_zero_value, index)};
40 components[element] = ctx.OpLoad(ctx.U32[1], pointer);
41 }
42 if (num_components == 1) {
43 return components[0];
44 } else {
45 const std::span components_span(components.data(), num_components);
46 return ctx.OpCompositeConstruct(ctx.U32[num_components], components_span);
47 }
48}
49} // Anonymous namespace
50
25void EmitLoadGlobalU8(EmitContext&) { 51void EmitLoadGlobalU8(EmitContext&) {
26 throw NotImplementedException("SPIR-V Instruction"); 52 throw NotImplementedException("SPIR-V Instruction");
27} 53}
@@ -95,21 +121,15 @@ void EmitLoadStorageS16(EmitContext&) {
95} 121}
96 122
97Id EmitLoadStorage32(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset) { 123Id EmitLoadStorage32(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset) {
98 if (!binding.IsImmediate()) { 124 return EmitLoadStorage(ctx, binding, offset, 1);
99 throw NotImplementedException("Dynamic storage buffer indexing");
100 }
101 const Id ssbo{ctx.ssbos[binding.U32()]};
102 const Id index{StorageIndex(ctx, offset, sizeof(u32))};
103 const Id pointer{ctx.OpAccessChain(ctx.storage_u32, ssbo, ctx.u32_zero_value, index)};
104 return ctx.OpLoad(ctx.U32[1], pointer);
105} 125}
106 126
107void EmitLoadStorage64(EmitContext&) { 127Id EmitLoadStorage64(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset) {
108 throw NotImplementedException("SPIR-V Instruction"); 128 return EmitLoadStorage(ctx, binding, offset, 2);
109} 129}
110 130
111void EmitLoadStorage128(EmitContext&) { 131Id EmitLoadStorage128(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset) {
112 throw NotImplementedException("SPIR-V Instruction"); 132 return EmitLoadStorage(ctx, binding, offset, 4);
113} 133}
114 134
115void EmitWriteStorageU8(EmitContext&) { 135void EmitWriteStorageU8(EmitContext&) {
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 e72505d61..e7fa3fce0 100644
--- a/src/shader_recompiler/ir_opt/collect_shader_info_pass.cpp
+++ b/src/shader_recompiler/ir_opt/collect_shader_info_pass.cpp
@@ -38,6 +38,12 @@ void GetAttribute(Info& info, IR::Attribute attribute) {
38 case IR::Attribute::PositionW: 38 case IR::Attribute::PositionW:
39 info.loads_position = true; 39 info.loads_position = true;
40 break; 40 break;
41 case IR::Attribute::InstanceId:
42 info.loads_instance_id = true;
43 break;
44 case IR::Attribute::VertexId:
45 info.loads_vertex_id = true;
46 break;
41 default: 47 default:
42 throw NotImplementedException("Get attribute {}", attribute); 48 throw NotImplementedException("Get attribute {}", attribute);
43 } 49 }
diff --git a/src/shader_recompiler/profile.h b/src/shader_recompiler/profile.h
index c6a143598..770299524 100644
--- a/src/shader_recompiler/profile.h
+++ b/src/shader_recompiler/profile.h
@@ -8,6 +8,7 @@ namespace Shader {
8 8
9struct Profile { 9struct Profile {
10 bool unified_descriptor_binding{}; 10 bool unified_descriptor_binding{};
11 bool support_vertex_instance_id{};
11 bool support_float_controls{}; 12 bool support_float_controls{};
12 bool support_separate_denorm_behavior{}; 13 bool support_separate_denorm_behavior{};
13 bool support_separate_rounding_mode{}; 14 bool support_separate_rounding_mode{};
diff --git a/src/shader_recompiler/shader_info.h b/src/shader_recompiler/shader_info.h
index 6eff762e2..f97730b34 100644
--- a/src/shader_recompiler/shader_info.h
+++ b/src/shader_recompiler/shader_info.h
@@ -59,6 +59,8 @@ struct Info {
59 59
60 std::array<bool, 32> loads_generics{}; 60 std::array<bool, 32> loads_generics{};
61 bool loads_position{}; 61 bool loads_position{};
62 bool loads_instance_id{};
63 bool loads_vertex_id{};
62 64
63 std::array<bool, 8> stores_frag_color{}; 65 std::array<bool, 8> stores_frag_color{};
64 bool stores_frag_depth{}; 66 bool stores_frag_depth{};
diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp
index d1399a46d..90e1a30f6 100644
--- a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp
+++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp
@@ -230,6 +230,7 @@ PipelineCache::PipelineCache(RasterizerVulkan& rasterizer_, Tegra::GPU& gpu_,
230 const VkDriverIdKHR driver_id{device.GetDriverID()}; 230 const VkDriverIdKHR driver_id{device.GetDriverID()};
231 profile = Shader::Profile{ 231 profile = Shader::Profile{
232 .unified_descriptor_binding = true, 232 .unified_descriptor_binding = true,
233 .support_vertex_instance_id = false,
233 .support_float_controls = true, 234 .support_float_controls = true,
234 .support_separate_denorm_behavior = float_control.denormBehaviorIndependence == 235 .support_separate_denorm_behavior = float_control.denormBehaviorIndependence ==
235 VK_SHADER_FLOAT_CONTROLS_INDEPENDENCE_ALL_KHR, 236 VK_SHADER_FLOAT_CONTROLS_INDEPENDENCE_ALL_KHR,