diff options
| author | 2021-04-12 19:41:22 -0300 | |
|---|---|---|
| committer | 2021-07-22 21:51:27 -0400 | |
| commit | f263760c5a3aff771123b32b15677e1f7a089640 (patch) | |
| tree | 3d9c41ce708beb8a76f28ceee1c9ebb25108a471 /src/shader_recompiler/backend/spirv | |
| parent | shader: Implement OUT (diff) | |
| download | yuzu-f263760c5a3aff771123b32b15677e1f7a089640.tar.gz yuzu-f263760c5a3aff771123b32b15677e1f7a089640.tar.xz yuzu-f263760c5a3aff771123b32b15677e1f7a089640.zip | |
shader: Implement geometry shaders
Diffstat (limited to 'src/shader_recompiler/backend/spirv')
5 files changed, 163 insertions, 65 deletions
diff --git a/src/shader_recompiler/backend/spirv/emit_context.cpp b/src/shader_recompiler/backend/spirv/emit_context.cpp index bf2210899..01b77a7d1 100644 --- a/src/shader_recompiler/backend/spirv/emit_context.cpp +++ b/src/shader_recompiler/backend/spirv/emit_context.cpp | |||
| @@ -140,7 +140,27 @@ Id DefineVariable(EmitContext& ctx, Id type, std::optional<spv::BuiltIn> builtin | |||
| 140 | return id; | 140 | return id; |
| 141 | } | 141 | } |
| 142 | 142 | ||
| 143 | u32 NumVertices(InputTopology input_topology) { | ||
| 144 | switch (input_topology) { | ||
| 145 | case InputTopology::Points: | ||
| 146 | return 1; | ||
| 147 | case InputTopology::Lines: | ||
| 148 | return 2; | ||
| 149 | case InputTopology::LinesAdjacency: | ||
| 150 | return 4; | ||
| 151 | case InputTopology::Triangles: | ||
| 152 | return 3; | ||
| 153 | case InputTopology::TrianglesAdjacency: | ||
| 154 | return 6; | ||
| 155 | } | ||
| 156 | throw InvalidArgument("Invalid input topology {}", input_topology); | ||
| 157 | } | ||
| 158 | |||
| 143 | Id DefineInput(EmitContext& ctx, Id type, std::optional<spv::BuiltIn> builtin = std::nullopt) { | 159 | Id DefineInput(EmitContext& ctx, Id type, std::optional<spv::BuiltIn> builtin = std::nullopt) { |
| 160 | if (ctx.stage == Stage::Geometry) { | ||
| 161 | const u32 num_vertices{NumVertices(ctx.profile.input_topology)}; | ||
| 162 | type = ctx.TypeArray(type, ctx.Constant(ctx.U32[1], num_vertices)); | ||
| 163 | } | ||
| 144 | return DefineVariable(ctx, type, builtin, spv::StorageClass::Input); | 164 | return DefineVariable(ctx, type, builtin, spv::StorageClass::Input); |
| 145 | } | 165 | } |
| 146 | 166 | ||
| @@ -455,12 +475,16 @@ void EmitContext::DefineSharedMemory(const IR::Program& program) { | |||
| 455 | 475 | ||
| 456 | void EmitContext::DefineAttributeMemAccess(const Info& info) { | 476 | void EmitContext::DefineAttributeMemAccess(const Info& info) { |
| 457 | const auto make_load{[&] { | 477 | const auto make_load{[&] { |
| 478 | const bool is_array{stage == Stage::Geometry}; | ||
| 458 | const Id end_block{OpLabel()}; | 479 | const Id end_block{OpLabel()}; |
| 459 | const Id default_label{OpLabel()}; | 480 | const Id default_label{OpLabel()}; |
| 460 | 481 | ||
| 461 | const Id func_type_load{TypeFunction(F32[1], U32[1])}; | 482 | const Id func_type_load{is_array ? TypeFunction(F32[1], U32[1], U32[1]) |
| 483 | : TypeFunction(F32[1], U32[1])}; | ||
| 462 | const Id func{OpFunction(F32[1], spv::FunctionControlMask::MaskNone, func_type_load)}; | 484 | const Id func{OpFunction(F32[1], spv::FunctionControlMask::MaskNone, func_type_load)}; |
| 463 | const Id offset{OpFunctionParameter(U32[1])}; | 485 | const Id offset{OpFunctionParameter(U32[1])}; |
| 486 | const Id vertex{is_array ? OpFunctionParameter(U32[1]) : Id{}}; | ||
| 487 | |||
| 464 | AddLabel(); | 488 | AddLabel(); |
| 465 | const Id base_index{OpShiftRightArithmetic(U32[1], offset, Constant(U32[1], 2U))}; | 489 | const Id base_index{OpShiftRightArithmetic(U32[1], offset, Constant(U32[1], 2U))}; |
| 466 | const Id masked_index{OpBitwiseAnd(U32[1], base_index, Constant(U32[1], 3U))}; | 490 | const Id masked_index{OpBitwiseAnd(U32[1], base_index, Constant(U32[1], 3U))}; |
| @@ -472,7 +496,7 @@ void EmitContext::DefineAttributeMemAccess(const Info& info) { | |||
| 472 | labels.push_back(OpLabel()); | 496 | labels.push_back(OpLabel()); |
| 473 | } | 497 | } |
| 474 | const u32 base_attribute_value = static_cast<u32>(IR::Attribute::Generic0X) >> 2; | 498 | const u32 base_attribute_value = static_cast<u32>(IR::Attribute::Generic0X) >> 2; |
| 475 | for (u32 i = 0; i < info.input_generics.size(); i++) { | 499 | for (u32 i = 0; i < info.input_generics.size(); ++i) { |
| 476 | if (!info.input_generics[i].used) { | 500 | if (!info.input_generics[i].used) { |
| 477 | continue; | 501 | continue; |
| 478 | } | 502 | } |
| @@ -486,7 +510,10 @@ void EmitContext::DefineAttributeMemAccess(const Info& info) { | |||
| 486 | size_t label_index{0}; | 510 | size_t label_index{0}; |
| 487 | if (info.loads_position) { | 511 | if (info.loads_position) { |
| 488 | AddLabel(labels[label_index]); | 512 | AddLabel(labels[label_index]); |
| 489 | const Id result{OpLoad(F32[1], OpAccessChain(input_f32, input_position, masked_index))}; | 513 | const Id pointer{is_array |
| 514 | ? OpAccessChain(input_f32, input_position, vertex, masked_index) | ||
| 515 | : OpAccessChain(input_f32, input_position, masked_index)}; | ||
| 516 | const Id result{OpLoad(F32[1], pointer)}; | ||
| 490 | OpReturnValue(result); | 517 | OpReturnValue(result); |
| 491 | ++label_index; | 518 | ++label_index; |
| 492 | } | 519 | } |
| @@ -502,7 +529,9 @@ void EmitContext::DefineAttributeMemAccess(const Info& info) { | |||
| 502 | continue; | 529 | continue; |
| 503 | } | 530 | } |
| 504 | const Id generic_id{input_generics.at(i)}; | 531 | const Id generic_id{input_generics.at(i)}; |
| 505 | const Id pointer{OpAccessChain(type->pointer, generic_id, masked_index)}; | 532 | const Id pointer{is_array |
| 533 | ? OpAccessChain(type->pointer, generic_id, vertex, masked_index) | ||
| 534 | : OpAccessChain(type->pointer, generic_id, masked_index)}; | ||
| 506 | const Id value{OpLoad(type->id, pointer)}; | 535 | const Id value{OpLoad(type->id, pointer)}; |
| 507 | const Id result{type->needs_cast ? OpBitcast(F32[1], value) : value}; | 536 | const Id result{type->needs_cast ? OpBitcast(F32[1], value) : value}; |
| 508 | OpReturnValue(result); | 537 | OpReturnValue(result); |
| @@ -910,13 +939,13 @@ void EmitContext::DefineOutputs(const Info& info) { | |||
| 910 | } | 939 | } |
| 911 | if (info.stores_point_size || profile.fixed_state_point_size) { | 940 | if (info.stores_point_size || profile.fixed_state_point_size) { |
| 912 | if (stage == Stage::Fragment) { | 941 | if (stage == Stage::Fragment) { |
| 913 | throw NotImplementedException("Storing PointSize in Fragment stage"); | 942 | throw NotImplementedException("Storing PointSize in fragment stage"); |
| 914 | } | 943 | } |
| 915 | output_point_size = DefineOutput(*this, F32[1], spv::BuiltIn::PointSize); | 944 | output_point_size = DefineOutput(*this, F32[1], spv::BuiltIn::PointSize); |
| 916 | } | 945 | } |
| 917 | if (info.stores_clip_distance) { | 946 | if (info.stores_clip_distance) { |
| 918 | if (stage == Stage::Fragment) { | 947 | if (stage == Stage::Fragment) { |
| 919 | throw NotImplementedException("Storing PointSize in Fragment stage"); | 948 | throw NotImplementedException("Storing ClipDistance in fragment stage"); |
| 920 | } | 949 | } |
| 921 | const Id type{TypeArray(F32[1], Constant(U32[1], 8U))}; | 950 | const Id type{TypeArray(F32[1], Constant(U32[1], 8U))}; |
| 922 | clip_distances = DefineOutput(*this, type, spv::BuiltIn::ClipDistance); | 951 | clip_distances = DefineOutput(*this, type, spv::BuiltIn::ClipDistance); |
| @@ -924,7 +953,7 @@ void EmitContext::DefineOutputs(const Info& info) { | |||
| 924 | if (info.stores_viewport_index && | 953 | if (info.stores_viewport_index && |
| 925 | (profile.support_viewport_index_layer_non_geometry || stage == Shader::Stage::Geometry)) { | 954 | (profile.support_viewport_index_layer_non_geometry || stage == Shader::Stage::Geometry)) { |
| 926 | if (stage == Stage::Fragment) { | 955 | if (stage == Stage::Fragment) { |
| 927 | throw NotImplementedException("Storing ViewportIndex in Fragment stage"); | 956 | throw NotImplementedException("Storing ViewportIndex in fragment stage"); |
| 928 | } | 957 | } |
| 929 | viewport_index = DefineOutput(*this, U32[1], spv::BuiltIn::ViewportIndex); | 958 | viewport_index = DefineOutput(*this, U32[1], spv::BuiltIn::ViewportIndex); |
| 930 | } | 959 | } |
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv.cpp b/src/shader_recompiler/backend/spirv/emit_spirv.cpp index 3258b0cf8..d7c5890ab 100644 --- a/src/shader_recompiler/backend/spirv/emit_spirv.cpp +++ b/src/shader_recompiler/backend/spirv/emit_spirv.cpp | |||
| @@ -134,6 +134,44 @@ void DefineEntryPoint(const IR::Program& program, EmitContext& ctx, Id main) { | |||
| 134 | case Shader::Stage::VertexB: | 134 | case Shader::Stage::VertexB: |
| 135 | execution_model = spv::ExecutionModel::Vertex; | 135 | execution_model = spv::ExecutionModel::Vertex; |
| 136 | break; | 136 | break; |
| 137 | case Shader::Stage::Geometry: | ||
| 138 | execution_model = spv::ExecutionModel::Geometry; | ||
| 139 | ctx.AddCapability(spv::Capability::Geometry); | ||
| 140 | ctx.AddCapability(spv::Capability::GeometryStreams); | ||
| 141 | switch (ctx.profile.input_topology) { | ||
| 142 | case InputTopology::Points: | ||
| 143 | ctx.AddExecutionMode(main, spv::ExecutionMode::InputPoints); | ||
| 144 | break; | ||
| 145 | case InputTopology::Lines: | ||
| 146 | ctx.AddExecutionMode(main, spv::ExecutionMode::InputLines); | ||
| 147 | break; | ||
| 148 | case InputTopology::LinesAdjacency: | ||
| 149 | ctx.AddExecutionMode(main, spv::ExecutionMode::InputLinesAdjacency); | ||
| 150 | break; | ||
| 151 | case InputTopology::Triangles: | ||
| 152 | ctx.AddExecutionMode(main, spv::ExecutionMode::Triangles); | ||
| 153 | break; | ||
| 154 | case InputTopology::TrianglesAdjacency: | ||
| 155 | ctx.AddExecutionMode(main, spv::ExecutionMode::InputTrianglesAdjacency); | ||
| 156 | break; | ||
| 157 | } | ||
| 158 | switch (program.output_topology) { | ||
| 159 | case OutputTopology::PointList: | ||
| 160 | ctx.AddExecutionMode(main, spv::ExecutionMode::OutputPoints); | ||
| 161 | break; | ||
| 162 | case OutputTopology::LineStrip: | ||
| 163 | ctx.AddExecutionMode(main, spv::ExecutionMode::OutputLineStrip); | ||
| 164 | break; | ||
| 165 | case OutputTopology::TriangleStrip: | ||
| 166 | ctx.AddExecutionMode(main, spv::ExecutionMode::OutputTriangleStrip); | ||
| 167 | break; | ||
| 168 | } | ||
| 169 | if (program.info.stores_point_size) { | ||
| 170 | ctx.AddCapability(spv::Capability::GeometryPointSize); | ||
| 171 | } | ||
| 172 | ctx.AddExecutionMode(main, spv::ExecutionMode::OutputVertices, program.output_vertices); | ||
| 173 | ctx.AddExecutionMode(main, spv::ExecutionMode::Invocations, program.invocations); | ||
| 174 | break; | ||
| 137 | case Shader::Stage::Fragment: | 175 | case Shader::Stage::Fragment: |
| 138 | execution_model = spv::ExecutionModel::Fragment; | 176 | execution_model = spv::ExecutionModel::Fragment; |
| 139 | ctx.AddExecutionMode(main, spv::ExecutionMode::OriginUpperLeft); | 177 | ctx.AddExecutionMode(main, spv::ExecutionMode::OriginUpperLeft); |
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv.h b/src/shader_recompiler/backend/spirv/emit_spirv.h index 440075212..c0e1b8833 100644 --- a/src/shader_recompiler/backend/spirv/emit_spirv.h +++ b/src/shader_recompiler/backend/spirv/emit_spirv.h | |||
| @@ -34,8 +34,8 @@ void EmitMemoryBarrierDeviceLevel(EmitContext& ctx); | |||
| 34 | void EmitMemoryBarrierSystemLevel(EmitContext& ctx); | 34 | void EmitMemoryBarrierSystemLevel(EmitContext& ctx); |
| 35 | void EmitPrologue(EmitContext& ctx); | 35 | void EmitPrologue(EmitContext& ctx); |
| 36 | void EmitEpilogue(EmitContext& ctx); | 36 | void EmitEpilogue(EmitContext& ctx); |
| 37 | void EmitEmitVertex(EmitContext& ctx, Id stream); | 37 | void EmitEmitVertex(EmitContext& ctx, const IR::Value& stream); |
| 38 | void EmitEndPrimitive(EmitContext& ctx, Id stream); | 38 | void EmitEndPrimitive(EmitContext& ctx, const IR::Value& stream); |
| 39 | void EmitGetRegister(EmitContext& ctx); | 39 | void EmitGetRegister(EmitContext& ctx); |
| 40 | void EmitSetRegister(EmitContext& ctx); | 40 | void EmitSetRegister(EmitContext& ctx); |
| 41 | void EmitGetPred(EmitContext& ctx); | 41 | void EmitGetPred(EmitContext& ctx); |
| @@ -51,10 +51,10 @@ Id EmitGetCbufS16(EmitContext& ctx, const IR::Value& binding, const IR::Value& o | |||
| 51 | Id EmitGetCbufU32(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset); | 51 | Id EmitGetCbufU32(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset); |
| 52 | Id EmitGetCbufF32(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset); | 52 | Id EmitGetCbufF32(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset); |
| 53 | Id EmitGetCbufU32x2(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset); | 53 | Id EmitGetCbufU32x2(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset); |
| 54 | Id EmitGetAttribute(EmitContext& ctx, IR::Attribute attr); | 54 | Id EmitGetAttribute(EmitContext& ctx, IR::Attribute attr, Id vertex); |
| 55 | void EmitSetAttribute(EmitContext& ctx, IR::Attribute attr, Id value); | 55 | void EmitSetAttribute(EmitContext& ctx, IR::Attribute attr, Id value, Id vertex); |
| 56 | Id EmitGetAttributeIndexed(EmitContext& ctx, Id offset); | 56 | Id EmitGetAttributeIndexed(EmitContext& ctx, Id offset, Id vertex); |
| 57 | void EmitSetAttributeIndexed(EmitContext& ctx, Id offset, Id value); | 57 | void EmitSetAttributeIndexed(EmitContext& ctx, Id offset, Id value, Id vertex); |
| 58 | void EmitSetFragColor(EmitContext& ctx, u32 index, u32 component, Id value); | 58 | void EmitSetFragColor(EmitContext& ctx, u32 index, u32 component, Id value); |
| 59 | void EmitSetFragDepth(EmitContext& ctx, Id value); | 59 | void EmitSetFragDepth(EmitContext& ctx, Id value); |
| 60 | void EmitGetZFlag(EmitContext& ctx); | 60 | void EmitGetZFlag(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 d552a1b52..a91b4c212 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 | |||
| @@ -3,6 +3,7 @@ | |||
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include <tuple> | 5 | #include <tuple> |
| 6 | #include <utility> | ||
| 6 | 7 | ||
| 7 | #include "shader_recompiler/backend/spirv/emit_spirv.h" | 8 | #include "shader_recompiler/backend/spirv/emit_spirv.h" |
| 8 | 9 | ||
| @@ -29,6 +30,15 @@ std::optional<AttrInfo> AttrTypes(EmitContext& ctx, u32 index) { | |||
| 29 | throw InvalidArgument("Invalid attribute type {}", type); | 30 | throw InvalidArgument("Invalid attribute type {}", type); |
| 30 | } | 31 | } |
| 31 | 32 | ||
| 33 | template <typename... Args> | ||
| 34 | Id AttrPointer(EmitContext& ctx, Id pointer_type, Id vertex, Id base, Args&&... args) { | ||
| 35 | if (ctx.stage == Stage::Geometry) { | ||
| 36 | return ctx.OpAccessChain(pointer_type, base, vertex, std::forward<Args>(args)...); | ||
| 37 | } else { | ||
| 38 | return ctx.OpAccessChain(pointer_type, base, std::forward<Args>(args)...); | ||
| 39 | } | ||
| 40 | } | ||
| 41 | |||
| 32 | std::optional<Id> OutputAttrPointer(EmitContext& ctx, IR::Attribute attr) { | 42 | std::optional<Id> OutputAttrPointer(EmitContext& ctx, IR::Attribute attr) { |
| 33 | const u32 element{static_cast<u32>(attr) % 4}; | 43 | const u32 element{static_cast<u32>(attr) % 4}; |
| 34 | const auto element_id{[&] { return ctx.Constant(ctx.U32[1], element); }}; | 44 | const auto element_id{[&] { return ctx.Constant(ctx.U32[1], element); }}; |
| @@ -66,6 +76,31 @@ std::optional<Id> OutputAttrPointer(EmitContext& ctx, IR::Attribute attr) { | |||
| 66 | throw NotImplementedException("Read attribute {}", attr); | 76 | throw NotImplementedException("Read attribute {}", attr); |
| 67 | } | 77 | } |
| 68 | } | 78 | } |
| 79 | |||
| 80 | Id GetCbuf(EmitContext& ctx, Id result_type, Id UniformDefinitions::*member_ptr, u32 element_size, | ||
| 81 | const IR::Value& binding, const IR::Value& offset) { | ||
| 82 | if (!binding.IsImmediate()) { | ||
| 83 | throw NotImplementedException("Constant buffer indexing"); | ||
| 84 | } | ||
| 85 | const Id cbuf{ctx.cbufs[binding.U32()].*member_ptr}; | ||
| 86 | const Id uniform_type{ctx.uniform_types.*member_ptr}; | ||
| 87 | if (!offset.IsImmediate()) { | ||
| 88 | Id index{ctx.Def(offset)}; | ||
| 89 | if (element_size > 1) { | ||
| 90 | const u32 log2_element_size{static_cast<u32>(std::countr_zero(element_size))}; | ||
| 91 | const Id shift{ctx.Constant(ctx.U32[1], log2_element_size)}; | ||
| 92 | index = ctx.OpShiftRightArithmetic(ctx.U32[1], ctx.Def(offset), shift); | ||
| 93 | } | ||
| 94 | const Id access_chain{ctx.OpAccessChain(uniform_type, cbuf, ctx.u32_zero_value, index)}; | ||
| 95 | return ctx.OpLoad(result_type, access_chain); | ||
| 96 | } | ||
| 97 | if (offset.U32() % element_size != 0) { | ||
| 98 | throw NotImplementedException("Unaligned immediate constant buffer load"); | ||
| 99 | } | ||
| 100 | const Id imm_offset{ctx.Constant(ctx.U32[1], offset.U32() / element_size)}; | ||
| 101 | const Id access_chain{ctx.OpAccessChain(uniform_type, cbuf, ctx.u32_zero_value, imm_offset)}; | ||
| 102 | return ctx.OpLoad(result_type, access_chain); | ||
| 103 | } | ||
| 69 | } // Anonymous namespace | 104 | } // Anonymous namespace |
| 70 | 105 | ||
| 71 | void EmitGetRegister(EmitContext&) { | 106 | void EmitGetRegister(EmitContext&) { |
| @@ -100,31 +135,6 @@ void EmitGetIndirectBranchVariable(EmitContext&) { | |||
| 100 | throw NotImplementedException("SPIR-V Instruction"); | 135 | throw NotImplementedException("SPIR-V Instruction"); |
| 101 | } | 136 | } |
| 102 | 137 | ||
| 103 | static Id GetCbuf(EmitContext& ctx, Id result_type, Id UniformDefinitions::*member_ptr, | ||
| 104 | u32 element_size, const IR::Value& binding, const IR::Value& offset) { | ||
| 105 | if (!binding.IsImmediate()) { | ||
| 106 | throw NotImplementedException("Constant buffer indexing"); | ||
| 107 | } | ||
| 108 | const Id cbuf{ctx.cbufs[binding.U32()].*member_ptr}; | ||
| 109 | const Id uniform_type{ctx.uniform_types.*member_ptr}; | ||
| 110 | if (!offset.IsImmediate()) { | ||
| 111 | Id index{ctx.Def(offset)}; | ||
| 112 | if (element_size > 1) { | ||
| 113 | const u32 log2_element_size{static_cast<u32>(std::countr_zero(element_size))}; | ||
| 114 | const Id shift{ctx.Constant(ctx.U32[1], log2_element_size)}; | ||
| 115 | index = ctx.OpShiftRightArithmetic(ctx.U32[1], ctx.Def(offset), shift); | ||
| 116 | } | ||
| 117 | const Id access_chain{ctx.OpAccessChain(uniform_type, cbuf, ctx.u32_zero_value, index)}; | ||
| 118 | return ctx.OpLoad(result_type, access_chain); | ||
| 119 | } | ||
| 120 | if (offset.U32() % element_size != 0) { | ||
| 121 | throw NotImplementedException("Unaligned immediate constant buffer load"); | ||
| 122 | } | ||
| 123 | const Id imm_offset{ctx.Constant(ctx.U32[1], offset.U32() / element_size)}; | ||
| 124 | const Id access_chain{ctx.OpAccessChain(uniform_type, cbuf, ctx.u32_zero_value, imm_offset)}; | ||
| 125 | return ctx.OpLoad(result_type, access_chain); | ||
| 126 | } | ||
| 127 | |||
| 128 | Id EmitGetCbufU8(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset) { | 138 | Id EmitGetCbufU8(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset) { |
| 129 | const Id load{GetCbuf(ctx, ctx.U8, &UniformDefinitions::U8, sizeof(u8), binding, offset)}; | 139 | const Id load{GetCbuf(ctx, ctx.U8, &UniformDefinitions::U8, sizeof(u8), binding, offset)}; |
| 130 | return ctx.OpUConvert(ctx.U32[1], load); | 140 | return ctx.OpUConvert(ctx.U32[1], load); |
| @@ -157,7 +167,7 @@ Id EmitGetCbufU32x2(EmitContext& ctx, const IR::Value& binding, const IR::Value& | |||
| 157 | return GetCbuf(ctx, ctx.U32[2], &UniformDefinitions::U32x2, sizeof(u32[2]), binding, offset); | 167 | return GetCbuf(ctx, ctx.U32[2], &UniformDefinitions::U32x2, sizeof(u32[2]), binding, offset); |
| 158 | } | 168 | } |
| 159 | 169 | ||
| 160 | Id EmitGetAttribute(EmitContext& ctx, IR::Attribute attr) { | 170 | Id EmitGetAttribute(EmitContext& ctx, IR::Attribute attr, Id vertex) { |
| 161 | const u32 element{static_cast<u32>(attr) % 4}; | 171 | const u32 element{static_cast<u32>(attr) % 4}; |
| 162 | const auto element_id{[&] { return ctx.Constant(ctx.U32[1], element); }}; | 172 | const auto element_id{[&] { return ctx.Constant(ctx.U32[1], element); }}; |
| 163 | if (IR::IsGeneric(attr)) { | 173 | if (IR::IsGeneric(attr)) { |
| @@ -168,7 +178,7 @@ Id EmitGetAttribute(EmitContext& ctx, IR::Attribute attr) { | |||
| 168 | return ctx.Constant(ctx.F32[1], 0.0f); | 178 | return ctx.Constant(ctx.F32[1], 0.0f); |
| 169 | } | 179 | } |
| 170 | const Id generic_id{ctx.input_generics.at(index)}; | 180 | const Id generic_id{ctx.input_generics.at(index)}; |
| 171 | const Id pointer{ctx.OpAccessChain(type->pointer, generic_id, element_id())}; | 181 | const Id pointer{AttrPointer(ctx, type->pointer, vertex, generic_id, element_id())}; |
| 172 | const Id value{ctx.OpLoad(type->id, pointer)}; | 182 | const Id value{ctx.OpLoad(type->id, pointer)}; |
| 173 | return type->needs_cast ? ctx.OpBitcast(ctx.F32[1], value) : value; | 183 | return type->needs_cast ? ctx.OpBitcast(ctx.F32[1], value) : value; |
| 174 | } | 184 | } |
| @@ -177,8 +187,8 @@ Id EmitGetAttribute(EmitContext& ctx, IR::Attribute attr) { | |||
| 177 | case IR::Attribute::PositionY: | 187 | case IR::Attribute::PositionY: |
| 178 | case IR::Attribute::PositionZ: | 188 | case IR::Attribute::PositionZ: |
| 179 | case IR::Attribute::PositionW: | 189 | case IR::Attribute::PositionW: |
| 180 | return ctx.OpLoad(ctx.F32[1], | 190 | return ctx.OpLoad( |
| 181 | ctx.OpAccessChain(ctx.input_f32, ctx.input_position, element_id())); | 191 | ctx.F32[1], AttrPointer(ctx, ctx.input_f32, vertex, ctx.input_position, element_id())); |
| 182 | case IR::Attribute::InstanceId: | 192 | case IR::Attribute::InstanceId: |
| 183 | if (ctx.profile.support_vertex_instance_id) { | 193 | if (ctx.profile.support_vertex_instance_id) { |
| 184 | return ctx.OpLoad(ctx.U32[1], ctx.instance_id); | 194 | return ctx.OpLoad(ctx.U32[1], ctx.instance_id); |
| @@ -198,29 +208,32 @@ Id EmitGetAttribute(EmitContext& ctx, IR::Attribute attr) { | |||
| 198 | ctx.Constant(ctx.U32[1], std::numeric_limits<u32>::max()), | 208 | ctx.Constant(ctx.U32[1], std::numeric_limits<u32>::max()), |
| 199 | ctx.u32_zero_value); | 209 | ctx.u32_zero_value); |
| 200 | case IR::Attribute::PointSpriteS: | 210 | case IR::Attribute::PointSpriteS: |
| 201 | return ctx.OpLoad(ctx.F32[1], ctx.OpAccessChain(ctx.input_f32, ctx.point_coord, | 211 | return ctx.OpLoad(ctx.F32[1], AttrPointer(ctx, ctx.input_f32, vertex, ctx.point_coord, |
| 202 | ctx.Constant(ctx.U32[1], 0U))); | 212 | ctx.u32_zero_value)); |
| 203 | case IR::Attribute::PointSpriteT: | 213 | case IR::Attribute::PointSpriteT: |
| 204 | return ctx.OpLoad(ctx.F32[1], ctx.OpAccessChain(ctx.input_f32, ctx.point_coord, | 214 | return ctx.OpLoad(ctx.F32[1], AttrPointer(ctx, ctx.input_f32, vertex, ctx.point_coord, |
| 205 | ctx.Constant(ctx.U32[1], 1U))); | 215 | ctx.Constant(ctx.U32[1], 1U))); |
| 206 | default: | 216 | default: |
| 207 | throw NotImplementedException("Read attribute {}", attr); | 217 | throw NotImplementedException("Read attribute {}", attr); |
| 208 | } | 218 | } |
| 209 | } | 219 | } |
| 210 | 220 | ||
| 211 | void EmitSetAttribute(EmitContext& ctx, IR::Attribute attr, Id value) { | 221 | void EmitSetAttribute(EmitContext& ctx, IR::Attribute attr, Id value, [[maybe_unused]] Id vertex) { |
| 212 | const std::optional<Id> output{OutputAttrPointer(ctx, attr)}; | 222 | const std::optional<Id> output{OutputAttrPointer(ctx, attr)}; |
| 213 | if (!output) { | 223 | if (output) { |
| 214 | return; | 224 | ctx.OpStore(*output, value); |
| 215 | } | 225 | } |
| 216 | ctx.OpStore(*output, value); | ||
| 217 | } | 226 | } |
| 218 | 227 | ||
| 219 | Id EmitGetAttributeIndexed(EmitContext& ctx, Id offset) { | 228 | Id EmitGetAttributeIndexed(EmitContext& ctx, Id offset, Id vertex) { |
| 220 | return ctx.OpFunctionCall(ctx.F32[1], ctx.indexed_load_func, offset); | 229 | if (ctx.stage == Stage::Geometry) { |
| 230 | return ctx.OpFunctionCall(ctx.F32[1], ctx.indexed_load_func, offset, vertex); | ||
| 231 | } else { | ||
| 232 | return ctx.OpFunctionCall(ctx.F32[1], ctx.indexed_load_func, offset); | ||
| 233 | } | ||
| 221 | } | 234 | } |
| 222 | 235 | ||
| 223 | void EmitSetAttributeIndexed(EmitContext& ctx, Id offset, Id value) { | 236 | void EmitSetAttributeIndexed(EmitContext& ctx, Id offset, Id value, [[maybe_unused]] Id vertex) { |
| 224 | ctx.OpFunctionCall(ctx.void_id, ctx.indexed_store_func, offset, value); | 237 | ctx.OpFunctionCall(ctx.void_id, ctx.indexed_store_func, offset, value); |
| 225 | } | 238 | } |
| 226 | 239 | ||
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_special.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_special.cpp index d20f4def3..6c8fcd5a5 100644 --- a/src/shader_recompiler/backend/spirv/emit_spirv_special.cpp +++ b/src/shader_recompiler/backend/spirv/emit_spirv_special.cpp | |||
| @@ -5,6 +5,17 @@ | |||
| 5 | #include "shader_recompiler/backend/spirv/emit_spirv.h" | 5 | #include "shader_recompiler/backend/spirv/emit_spirv.h" |
| 6 | 6 | ||
| 7 | namespace Shader::Backend::SPIRV { | 7 | namespace Shader::Backend::SPIRV { |
| 8 | namespace { | ||
| 9 | void ConvertDepthMode(EmitContext& ctx) { | ||
| 10 | const Id type{ctx.F32[1]}; | ||
| 11 | const Id position{ctx.OpLoad(ctx.F32[4], ctx.output_position)}; | ||
| 12 | const Id z{ctx.OpCompositeExtract(type, position, 2u)}; | ||
| 13 | const Id w{ctx.OpCompositeExtract(type, position, 3u)}; | ||
| 14 | const Id screen_depth{ctx.OpFMul(type, ctx.OpFAdd(type, z, w), ctx.Constant(type, 0.5f))}; | ||
| 15 | const Id vector{ctx.OpCompositeInsert(ctx.F32[4], screen_depth, position, 2u)}; | ||
| 16 | ctx.OpStore(ctx.output_position, vector); | ||
| 17 | } | ||
| 18 | } // Anonymous namespace | ||
| 8 | 19 | ||
| 9 | void EmitPrologue(EmitContext& ctx) { | 20 | void EmitPrologue(EmitContext& ctx) { |
| 10 | if (ctx.stage == Stage::VertexB) { | 21 | if (ctx.stage == Stage::VertexB) { |
| @@ -25,23 +36,30 @@ void EmitPrologue(EmitContext& ctx) { | |||
| 25 | } | 36 | } |
| 26 | 37 | ||
| 27 | void EmitEpilogue(EmitContext& ctx) { | 38 | void EmitEpilogue(EmitContext& ctx) { |
| 28 | if (ctx.profile.convert_depth_mode) { | 39 | if (ctx.stage == Stage::VertexB && ctx.profile.convert_depth_mode) { |
| 29 | const Id type{ctx.F32[1]}; | 40 | ConvertDepthMode(ctx); |
| 30 | const Id position{ctx.OpLoad(ctx.F32[4], ctx.output_position)}; | ||
| 31 | const Id z{ctx.OpCompositeExtract(type, position, 2u)}; | ||
| 32 | const Id w{ctx.OpCompositeExtract(type, position, 3u)}; | ||
| 33 | const Id screen_depth{ctx.OpFMul(type, ctx.OpFAdd(type, z, w), ctx.Constant(type, 0.5f))}; | ||
| 34 | const Id vector{ctx.OpCompositeInsert(ctx.F32[4], screen_depth, position, 2u)}; | ||
| 35 | ctx.OpStore(ctx.output_position, vector); | ||
| 36 | } | 41 | } |
| 37 | } | 42 | } |
| 38 | 43 | ||
| 39 | void EmitEmitVertex(EmitContext& ctx, Id stream) { | 44 | void EmitEmitVertex(EmitContext& ctx, const IR::Value& stream) { |
| 40 | ctx.OpEmitStreamVertex(stream); | 45 | if (ctx.profile.convert_depth_mode) { |
| 46 | ConvertDepthMode(ctx); | ||
| 47 | } | ||
| 48 | if (!stream.IsImmediate()) { | ||
| 49 | // LOG_WARNING(..., "EmitVertex's stream is not constant"); | ||
| 50 | ctx.OpEmitStreamVertex(ctx.u32_zero_value); | ||
| 51 | return; | ||
| 52 | } | ||
| 53 | ctx.OpEmitStreamVertex(ctx.Def(stream)); | ||
| 41 | } | 54 | } |
| 42 | 55 | ||
| 43 | void EmitEndPrimitive(EmitContext& ctx, Id stream) { | 56 | void EmitEndPrimitive(EmitContext& ctx, const IR::Value& stream) { |
| 44 | ctx.OpEndStreamPrimitive(stream); | 57 | if (!stream.IsImmediate()) { |
| 58 | // LOG_WARNING(..., "EndPrimitive's stream is not constant"); | ||
| 59 | ctx.OpEndStreamPrimitive(ctx.u32_zero_value); | ||
| 60 | return; | ||
| 61 | } | ||
| 62 | ctx.OpEndStreamPrimitive(ctx.Def(stream)); | ||
| 45 | } | 63 | } |
| 46 | 64 | ||
| 47 | } // namespace Shader::Backend::SPIRV | 65 | } // namespace Shader::Backend::SPIRV |