diff options
| author | 2021-03-20 19:11:56 -0300 | |
|---|---|---|
| committer | 2021-07-22 21:51:23 -0400 | |
| commit | 76c8a962ac4eae77e71d66a72c448930240339f9 (patch) | |
| tree | 267bdb72f0fad43779080cd1907dd8159a6c7154 /src | |
| parent | shader: Refactor half floating instructions (diff) | |
| download | yuzu-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.cpp | 191 | ||||
| -rw-r--r-- | src/shader_recompiler/backend/spirv/emit_context.h | 14 | ||||
| -rw-r--r-- | src/shader_recompiler/backend/spirv/emit_spirv.cpp | 107 | ||||
| -rw-r--r-- | src/shader_recompiler/backend/spirv/emit_spirv.h | 4 | ||||
| -rw-r--r-- | src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp | 16 | ||||
| -rw-r--r-- | src/shader_recompiler/backend/spirv/emit_spirv_memory.cpp | 46 | ||||
| -rw-r--r-- | src/shader_recompiler/ir_opt/collect_shader_info_pass.cpp | 6 | ||||
| -rw-r--r-- | src/shader_recompiler/profile.h | 1 | ||||
| -rw-r--r-- | src/shader_recompiler/shader_info.h | 2 | ||||
| -rw-r--r-- | src/video_core/renderer_vulkan/vk_pipeline_cache.cpp | 1 |
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 | |||
| 52 | Id 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 | |||
| 63 | Id DefineInput(EmitContext& ctx, Id type, std::optional<spv::BuiltIn> builtin = std::nullopt) { | ||
| 64 | return DefineVariable(ctx, type, builtin, spv::StorageClass::Input); | ||
| 65 | } | ||
| 66 | |||
| 67 | Id 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 | ||
| 53 | void VectorTypes::Define(Sirit::Module& sirit_ctx, Id base_type, std::string_view name) { | 72 | void VectorTypes::Define(Sirit::Module& sirit_ctx, Id base_type, std::string_view name) { |
| @@ -144,59 +163,8 @@ void EmitContext::DefineCommonConstants() { | |||
| 144 | } | 163 | } |
| 145 | 164 | ||
| 146 | void EmitContext::DefineInterfaces(const Info& info, Stage stage) { | 165 | void 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 | ||
| 202 | void EmitContext::DefineConstantBuffers(const Info& info, u32& binding) { | 170 | void 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 | ||
| 228 | void 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 | |||
| 255 | void EmitContext::DefineStorageBuffers(const Info& info, u32& binding) { | 196 | void 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 | ||
| 255 | void 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 | |||
| 295 | void 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 | |||
| 322 | void 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 | ||
| 116 | Id 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 | |||
| 129 | void 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 | |||
| 116 | void SetupDenormControl(const Profile& profile, const IR::Program& program, EmitContext& ctx, | 153 | void 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 | ||
| 213 | void 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 | |||
| 176 | Id PhiArgDef(EmitContext& ctx, IR::Inst* inst, size_t index) { | 232 | Id 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) { | |||
| 202 | std::vector<u32> EmitSPIRV(const Profile& profile, Environment& env, IR::Program& program, | 258 | std::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); | |||
| 81 | void EmitLoadStorageU16(EmitContext& ctx); | 81 | void EmitLoadStorageU16(EmitContext& ctx); |
| 82 | void EmitLoadStorageS16(EmitContext& ctx); | 82 | void EmitLoadStorageS16(EmitContext& ctx); |
| 83 | Id EmitLoadStorage32(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset); | 83 | Id EmitLoadStorage32(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset); |
| 84 | void EmitLoadStorage64(EmitContext& ctx); | 84 | Id EmitLoadStorage64(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset); |
| 85 | void EmitLoadStorage128(EmitContext& ctx); | 85 | Id EmitLoadStorage128(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset); |
| 86 | void EmitWriteStorageU8(EmitContext& ctx); | 86 | void EmitWriteStorageU8(EmitContext& ctx); |
| 87 | void EmitWriteStorageS8(EmitContext& ctx); | 87 | void EmitWriteStorageS8(EmitContext& ctx); |
| 88 | void EmitWriteStorageU16(EmitContext& ctx); | 88 | void 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 | ||
| 127 | Id EmitGetAttribute(EmitContext& ctx, IR::Attribute attr) { | 131 | Id 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 | ||
| 9 | namespace Shader::Backend::SPIRV { | 9 | namespace Shader::Backend::SPIRV { |
| 10 | 10 | namespace { | |
| 11 | static Id StorageIndex(EmitContext& ctx, const IR::Value& offset, size_t element_size) { | 11 | Id 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 | ||
| 25 | Id 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 | |||
| 25 | void EmitLoadGlobalU8(EmitContext&) { | 51 | void 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 | ||
| 97 | Id EmitLoadStorage32(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset) { | 123 | Id 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 | ||
| 107 | void EmitLoadStorage64(EmitContext&) { | 127 | Id 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 | ||
| 111 | void EmitLoadStorage128(EmitContext&) { | 131 | Id 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 | ||
| 115 | void EmitWriteStorageU8(EmitContext&) { | 135 | void 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 | ||
| 9 | struct Profile { | 9 | struct 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, |