diff options
14 files changed, 277 insertions, 91 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 |
diff --git a/src/shader_recompiler/frontend/ir/ir_emitter.cpp b/src/shader_recompiler/frontend/ir/ir_emitter.cpp index 7d48fa1ba..d66eb17a6 100644 --- a/src/shader_recompiler/frontend/ir/ir_emitter.cpp +++ b/src/shader_recompiler/frontend/ir/ir_emitter.cpp | |||
| @@ -308,19 +308,27 @@ U1 IREmitter::GetFlowTestResult(FlowTest test) { | |||
| 308 | } | 308 | } |
| 309 | 309 | ||
| 310 | F32 IREmitter::GetAttribute(IR::Attribute attribute) { | 310 | F32 IREmitter::GetAttribute(IR::Attribute attribute) { |
| 311 | return Inst<F32>(Opcode::GetAttribute, attribute); | 311 | return GetAttribute(attribute, Imm32(0)); |
| 312 | } | 312 | } |
| 313 | 313 | ||
| 314 | void IREmitter::SetAttribute(IR::Attribute attribute, const F32& value) { | 314 | F32 IREmitter::GetAttribute(IR::Attribute attribute, const U32& vertex) { |
| 315 | Inst(Opcode::SetAttribute, attribute, value); | 315 | return Inst<F32>(Opcode::GetAttribute, attribute, vertex); |
| 316 | } | ||
| 317 | |||
| 318 | void IREmitter::SetAttribute(IR::Attribute attribute, const F32& value, const U32& vertex) { | ||
| 319 | Inst(Opcode::SetAttribute, attribute, value, vertex); | ||
| 316 | } | 320 | } |
| 317 | 321 | ||
| 318 | F32 IREmitter::GetAttributeIndexed(const U32& phys_address) { | 322 | F32 IREmitter::GetAttributeIndexed(const U32& phys_address) { |
| 319 | return Inst<F32>(Opcode::GetAttributeIndexed, phys_address); | 323 | return GetAttributeIndexed(phys_address, Imm32(0)); |
| 324 | } | ||
| 325 | |||
| 326 | F32 IREmitter::GetAttributeIndexed(const U32& phys_address, const U32& vertex) { | ||
| 327 | return Inst<F32>(Opcode::GetAttributeIndexed, phys_address, vertex); | ||
| 320 | } | 328 | } |
| 321 | 329 | ||
| 322 | void IREmitter::SetAttributeIndexed(const U32& phys_address, const F32& value) { | 330 | void IREmitter::SetAttributeIndexed(const U32& phys_address, const F32& value, const U32& vertex) { |
| 323 | Inst(Opcode::SetAttributeIndexed, phys_address, value); | 331 | Inst(Opcode::SetAttributeIndexed, phys_address, value, vertex); |
| 324 | } | 332 | } |
| 325 | 333 | ||
| 326 | void IREmitter::SetFragColor(u32 index, u32 component, const F32& value) { | 334 | void IREmitter::SetFragColor(u32 index, u32 component, const F32& value) { |
diff --git a/src/shader_recompiler/frontend/ir/ir_emitter.h b/src/shader_recompiler/frontend/ir/ir_emitter.h index 033c4332e..e70359eb1 100644 --- a/src/shader_recompiler/frontend/ir/ir_emitter.h +++ b/src/shader_recompiler/frontend/ir/ir_emitter.h | |||
| @@ -77,10 +77,12 @@ public: | |||
| 77 | [[nodiscard]] U1 GetFlowTestResult(FlowTest test); | 77 | [[nodiscard]] U1 GetFlowTestResult(FlowTest test); |
| 78 | 78 | ||
| 79 | [[nodiscard]] F32 GetAttribute(IR::Attribute attribute); | 79 | [[nodiscard]] F32 GetAttribute(IR::Attribute attribute); |
| 80 | void SetAttribute(IR::Attribute attribute, const F32& value); | 80 | [[nodiscard]] F32 GetAttribute(IR::Attribute attribute, const U32& vertex); |
| 81 | void SetAttribute(IR::Attribute attribute, const F32& value, const U32& vertex); | ||
| 81 | 82 | ||
| 82 | [[nodiscard]] F32 GetAttributeIndexed(const U32& phys_address); | 83 | [[nodiscard]] F32 GetAttributeIndexed(const U32& phys_address); |
| 83 | void SetAttributeIndexed(const U32& phys_address, const F32& value); | 84 | [[nodiscard]] F32 GetAttributeIndexed(const U32& phys_address, const U32& vertex); |
| 85 | void SetAttributeIndexed(const U32& phys_address, const F32& value, const U32& vertex); | ||
| 84 | 86 | ||
| 85 | void SetFragColor(u32 index, u32 component, const F32& value); | 87 | void SetFragColor(u32 index, u32 component, const F32& value); |
| 86 | void SetFragDepth(const F32& value); | 88 | void SetFragDepth(const F32& value); |
diff --git a/src/shader_recompiler/frontend/ir/opcodes.inc b/src/shader_recompiler/frontend/ir/opcodes.inc index 0e487f1a7..7a21fe746 100644 --- a/src/shader_recompiler/frontend/ir/opcodes.inc +++ b/src/shader_recompiler/frontend/ir/opcodes.inc | |||
| @@ -44,10 +44,10 @@ OPCODE(GetCbufS16, U32, U32, | |||
| 44 | OPCODE(GetCbufU32, U32, U32, U32, ) | 44 | OPCODE(GetCbufU32, U32, U32, U32, ) |
| 45 | OPCODE(GetCbufF32, F32, U32, U32, ) | 45 | OPCODE(GetCbufF32, F32, U32, U32, ) |
| 46 | OPCODE(GetCbufU32x2, U32x2, U32, U32, ) | 46 | OPCODE(GetCbufU32x2, U32x2, U32, U32, ) |
| 47 | OPCODE(GetAttribute, F32, Attribute, ) | 47 | OPCODE(GetAttribute, F32, Attribute, U32, ) |
| 48 | OPCODE(SetAttribute, Void, Attribute, F32, ) | 48 | OPCODE(SetAttribute, Void, Attribute, F32, U32, ) |
| 49 | OPCODE(GetAttributeIndexed, F32, U32, ) | 49 | OPCODE(GetAttributeIndexed, F32, U32, U32, ) |
| 50 | OPCODE(SetAttributeIndexed, Void, U32, F32, ) | 50 | OPCODE(SetAttributeIndexed, Void, U32, F32, U32, ) |
| 51 | OPCODE(SetFragColor, Void, U32, U32, F32, ) | 51 | OPCODE(SetFragColor, Void, U32, U32, F32, ) |
| 52 | OPCODE(SetFragDepth, Void, F32, ) | 52 | OPCODE(SetFragDepth, Void, F32, ) |
| 53 | OPCODE(GetZFlag, U1, Void, ) | 53 | OPCODE(GetZFlag, U1, Void, ) |
diff --git a/src/shader_recompiler/frontend/ir/program.h b/src/shader_recompiler/frontend/ir/program.h index 3a37b3ab9..51e1a8c77 100644 --- a/src/shader_recompiler/frontend/ir/program.h +++ b/src/shader_recompiler/frontend/ir/program.h | |||
| @@ -10,6 +10,7 @@ | |||
| 10 | #include <boost/container/small_vector.hpp> | 10 | #include <boost/container/small_vector.hpp> |
| 11 | 11 | ||
| 12 | #include "shader_recompiler/frontend/ir/basic_block.h" | 12 | #include "shader_recompiler/frontend/ir/basic_block.h" |
| 13 | #include "shader_recompiler/program_header.h" | ||
| 13 | #include "shader_recompiler/shader_info.h" | 14 | #include "shader_recompiler/shader_info.h" |
| 14 | #include "shader_recompiler/stage.h" | 15 | #include "shader_recompiler/stage.h" |
| 15 | 16 | ||
| @@ -21,6 +22,9 @@ struct Program { | |||
| 21 | Info info; | 22 | Info info; |
| 22 | Stage stage{}; | 23 | Stage stage{}; |
| 23 | std::array<u32, 3> workgroup_size{}; | 24 | std::array<u32, 3> workgroup_size{}; |
| 25 | OutputTopology output_topology{}; | ||
| 26 | u32 output_vertices{}; | ||
| 27 | u32 invocations{}; | ||
| 24 | u32 local_memory_size{}; | 28 | u32 local_memory_size{}; |
| 25 | u32 shared_memory_size{}; | 29 | u32 shared_memory_size{}; |
| 26 | }; | 30 | }; |
diff --git a/src/shader_recompiler/frontend/maxwell/program.cpp b/src/shader_recompiler/frontend/maxwell/program.cpp index aaf2a74a7..ab67446c8 100644 --- a/src/shader_recompiler/frontend/maxwell/program.cpp +++ b/src/shader_recompiler/frontend/maxwell/program.cpp | |||
| @@ -69,9 +69,20 @@ IR::Program TranslateProgram(ObjectPool<IR::Inst>& inst_pool, ObjectPool<IR::Blo | |||
| 69 | program.post_order_blocks = PostOrder(program.blocks); | 69 | program.post_order_blocks = PostOrder(program.blocks); |
| 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 | if (program.stage == Stage::Compute) { | 72 | switch (program.stage) { |
| 73 | case Stage::Geometry: { | ||
| 74 | const ProgramHeader& sph{env.SPH()}; | ||
| 75 | program.output_topology = sph.common3.output_topology; | ||
| 76 | program.output_vertices = sph.common4.max_output_vertices; | ||
| 77 | program.invocations = sph.common2.threads_per_input_primitive; | ||
| 78 | break; | ||
| 79 | } | ||
| 80 | case Stage::Compute: | ||
| 73 | program.workgroup_size = env.WorkgroupSize(); | 81 | program.workgroup_size = env.WorkgroupSize(); |
| 74 | program.shared_memory_size = env.SharedMemorySize(); | 82 | program.shared_memory_size = env.SharedMemorySize(); |
| 83 | break; | ||
| 84 | default: | ||
| 85 | break; | ||
| 75 | } | 86 | } |
| 76 | RemoveUnreachableBlocks(program); | 87 | RemoveUnreachableBlocks(program); |
| 77 | 88 | ||
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 79293bd6b..eb6a80de2 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 | |||
| @@ -64,7 +64,7 @@ void TranslatorVisitor::ALD(u64 insn) { | |||
| 64 | BitField<8, 8, IR::Reg> index_reg; | 64 | BitField<8, 8, IR::Reg> index_reg; |
| 65 | BitField<20, 10, u64> absolute_offset; | 65 | BitField<20, 10, u64> absolute_offset; |
| 66 | BitField<20, 11, s64> relative_offset; | 66 | BitField<20, 11, s64> relative_offset; |
| 67 | BitField<39, 8, IR::Reg> array_reg; | 67 | BitField<39, 8, IR::Reg> vertex_reg; |
| 68 | BitField<32, 1, u64> o; | 68 | BitField<32, 1, u64> o; |
| 69 | BitField<31, 1, u64> patch; | 69 | BitField<31, 1, u64> patch; |
| 70 | BitField<47, 2, Size> size; | 70 | BitField<47, 2, Size> size; |
| @@ -80,15 +80,17 @@ void TranslatorVisitor::ALD(u64 insn) { | |||
| 80 | if (offset % 4 != 0) { | 80 | if (offset % 4 != 0) { |
| 81 | throw NotImplementedException("Unaligned absolute offset {}", offset); | 81 | throw NotImplementedException("Unaligned absolute offset {}", offset); |
| 82 | } | 82 | } |
| 83 | const IR::U32 vertex{X(ald.vertex_reg)}; | ||
| 83 | const u32 num_elements{NumElements(ald.size)}; | 84 | const u32 num_elements{NumElements(ald.size)}; |
| 84 | if (ald.index_reg == IR::Reg::RZ) { | 85 | if (ald.index_reg == IR::Reg::RZ) { |
| 85 | for (u32 element = 0; element < num_elements; ++element) { | 86 | for (u32 element = 0; element < num_elements; ++element) { |
| 86 | F(ald.dest_reg + element, ir.GetAttribute(IR::Attribute{offset / 4 + element})); | 87 | const IR::Attribute attr{offset / 4 + element}; |
| 88 | F(ald.dest_reg + element, ir.GetAttribute(attr, vertex)); | ||
| 87 | } | 89 | } |
| 88 | return; | 90 | return; |
| 89 | } | 91 | } |
| 90 | HandleIndexed(*this, ald.index_reg, num_elements, [&](u32 element, IR::U32 final_offset) { | 92 | HandleIndexed(*this, ald.index_reg, num_elements, [&](u32 element, IR::U32 final_offset) { |
| 91 | F(ald.dest_reg + element, ir.GetAttributeIndexed(final_offset)); | 93 | F(ald.dest_reg + element, ir.GetAttributeIndexed(final_offset, vertex)); |
| 92 | }); | 94 | }); |
| 93 | } | 95 | } |
| 94 | 96 | ||
| @@ -100,7 +102,7 @@ void TranslatorVisitor::AST(u64 insn) { | |||
| 100 | BitField<20, 10, u64> absolute_offset; | 102 | BitField<20, 10, u64> absolute_offset; |
| 101 | BitField<20, 11, s64> relative_offset; | 103 | BitField<20, 11, s64> relative_offset; |
| 102 | BitField<31, 1, u64> patch; | 104 | BitField<31, 1, u64> patch; |
| 103 | BitField<39, 8, IR::Reg> array_reg; | 105 | BitField<39, 8, IR::Reg> vertex_reg; |
| 104 | BitField<47, 2, Size> size; | 106 | BitField<47, 2, Size> size; |
| 105 | } const ast{insn}; | 107 | } const ast{insn}; |
| 106 | 108 | ||
| @@ -114,15 +116,17 @@ void TranslatorVisitor::AST(u64 insn) { | |||
| 114 | if (offset % 4 != 0) { | 116 | if (offset % 4 != 0) { |
| 115 | throw NotImplementedException("Unaligned absolute offset {}", offset); | 117 | throw NotImplementedException("Unaligned absolute offset {}", offset); |
| 116 | } | 118 | } |
| 119 | const IR::U32 vertex{X(ast.vertex_reg)}; | ||
| 117 | const u32 num_elements{NumElements(ast.size)}; | 120 | const u32 num_elements{NumElements(ast.size)}; |
| 118 | if (ast.index_reg == IR::Reg::RZ) { | 121 | if (ast.index_reg == IR::Reg::RZ) { |
| 119 | for (u32 element = 0; element < num_elements; ++element) { | 122 | for (u32 element = 0; element < num_elements; ++element) { |
| 120 | ir.SetAttribute(IR::Attribute{offset / 4 + element}, F(ast.src_reg + element)); | 123 | const IR::Attribute attr{offset / 4 + element}; |
| 124 | ir.SetAttribute(attr, F(ast.src_reg + element), vertex); | ||
| 121 | } | 125 | } |
| 122 | return; | 126 | return; |
| 123 | } | 127 | } |
| 124 | HandleIndexed(*this, ast.index_reg, num_elements, [&](u32 element, IR::U32 final_offset) { | 128 | HandleIndexed(*this, ast.index_reg, num_elements, [&](u32 element, IR::U32 final_offset) { |
| 125 | ir.SetAttributeIndexed(final_offset, F(ast.src_reg + element)); | 129 | ir.SetAttributeIndexed(final_offset, F(ast.src_reg + element), vertex); |
| 126 | }); | 130 | }); |
| 127 | } | 131 | } |
| 128 | 132 | ||
diff --git a/src/shader_recompiler/profile.h b/src/shader_recompiler/profile.h index a4e41bda1..06f1f59bd 100644 --- a/src/shader_recompiler/profile.h +++ b/src/shader_recompiler/profile.h | |||
| @@ -18,6 +18,14 @@ enum class AttributeType : u8 { | |||
| 18 | Disabled, | 18 | Disabled, |
| 19 | }; | 19 | }; |
| 20 | 20 | ||
| 21 | enum class InputTopology { | ||
| 22 | Points, | ||
| 23 | Lines, | ||
| 24 | LinesAdjacency, | ||
| 25 | Triangles, | ||
| 26 | TrianglesAdjacency, | ||
| 27 | }; | ||
| 28 | |||
| 21 | struct Profile { | 29 | struct Profile { |
| 22 | u32 supported_spirv{0x00010000}; | 30 | u32 supported_spirv{0x00010000}; |
| 23 | 31 | ||
| @@ -46,6 +54,8 @@ struct Profile { | |||
| 46 | std::array<AttributeType, 32> generic_input_types{}; | 54 | std::array<AttributeType, 32> generic_input_types{}; |
| 47 | bool convert_depth_mode{}; | 55 | bool convert_depth_mode{}; |
| 48 | 56 | ||
| 57 | InputTopology input_topology{}; | ||
| 58 | |||
| 49 | std::optional<float> fixed_state_point_size; | 59 | std::optional<float> fixed_state_point_size; |
| 50 | }; | 60 | }; |
| 51 | 61 | ||
diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp index b953d694b..f49add208 100644 --- a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp | |||
| @@ -769,7 +769,7 @@ std::unique_ptr<GraphicsPipeline> PipelineCache::CreateGraphicsPipeline( | |||
| 769 | const size_t stage_index{index - 1}; | 769 | const size_t stage_index{index - 1}; |
| 770 | infos[stage_index] = &program.info; | 770 | infos[stage_index] = &program.info; |
| 771 | 771 | ||
| 772 | const Shader::Profile profile{MakeProfile(key, program.stage)}; | 772 | const Shader::Profile profile{MakeProfile(key, program)}; |
| 773 | const std::vector<u32> code{EmitSPIRV(profile, program, binding)}; | 773 | const std::vector<u32> code{EmitSPIRV(profile, program, binding)}; |
| 774 | device.SaveShader(code); | 774 | device.SaveShader(code); |
| 775 | modules[stage_index] = BuildShader(device, code); | 775 | modules[stage_index] = BuildShader(device, code); |
| @@ -880,15 +880,59 @@ static Shader::AttributeType CastAttributeType(const FixedPipelineState::VertexA | |||
| 880 | } | 880 | } |
| 881 | 881 | ||
| 882 | Shader::Profile PipelineCache::MakeProfile(const GraphicsPipelineCacheKey& key, | 882 | Shader::Profile PipelineCache::MakeProfile(const GraphicsPipelineCacheKey& key, |
| 883 | Shader::Stage stage) { | 883 | const Shader::IR::Program& program) { |
| 884 | Shader::Profile profile{base_profile}; | 884 | Shader::Profile profile{base_profile}; |
| 885 | if (stage == Shader::Stage::VertexB) { | 885 | |
| 886 | profile.convert_depth_mode = key.state.ndc_minus_one_to_one != 0; | 886 | const Shader::Stage stage{program.stage}; |
| 887 | if (key.state.topology == Maxwell::PrimitiveTopology::Points) { | 887 | const bool has_geometry{key.unique_hashes[4] != u128{}}; |
| 888 | profile.fixed_state_point_size = Common::BitCast<float>(key.state.point_size); | 888 | const bool gl_ndc{key.state.ndc_minus_one_to_one != 0}; |
| 889 | const float point_size{Common::BitCast<float>(key.state.point_size)}; | ||
| 890 | switch (stage) { | ||
| 891 | case Shader::Stage::VertexB: | ||
| 892 | if (!has_geometry) { | ||
| 893 | if (key.state.topology == Maxwell::PrimitiveTopology::Points) { | ||
| 894 | profile.fixed_state_point_size = point_size; | ||
| 895 | } | ||
| 896 | profile.convert_depth_mode = gl_ndc; | ||
| 889 | } | 897 | } |
| 890 | std::ranges::transform(key.state.attributes, profile.generic_input_types.begin(), | 898 | std::ranges::transform(key.state.attributes, profile.generic_input_types.begin(), |
| 891 | &CastAttributeType); | 899 | &CastAttributeType); |
| 900 | break; | ||
| 901 | case Shader::Stage::Geometry: | ||
| 902 | if (program.output_topology == Shader::OutputTopology::PointList) { | ||
| 903 | profile.fixed_state_point_size = point_size; | ||
| 904 | } | ||
| 905 | profile.convert_depth_mode = gl_ndc; | ||
| 906 | break; | ||
| 907 | default: | ||
| 908 | break; | ||
| 909 | } | ||
| 910 | switch (key.state.topology) { | ||
| 911 | case Maxwell::PrimitiveTopology::Points: | ||
| 912 | profile.input_topology = Shader::InputTopology::Points; | ||
| 913 | break; | ||
| 914 | case Maxwell::PrimitiveTopology::Lines: | ||
| 915 | case Maxwell::PrimitiveTopology::LineLoop: | ||
| 916 | case Maxwell::PrimitiveTopology::LineStrip: | ||
| 917 | profile.input_topology = Shader::InputTopology::Lines; | ||
| 918 | break; | ||
| 919 | case Maxwell::PrimitiveTopology::Triangles: | ||
| 920 | case Maxwell::PrimitiveTopology::TriangleStrip: | ||
| 921 | case Maxwell::PrimitiveTopology::TriangleFan: | ||
| 922 | case Maxwell::PrimitiveTopology::Quads: | ||
| 923 | case Maxwell::PrimitiveTopology::QuadStrip: | ||
| 924 | case Maxwell::PrimitiveTopology::Polygon: | ||
| 925 | case Maxwell::PrimitiveTopology::Patches: | ||
| 926 | profile.input_topology = Shader::InputTopology::Triangles; | ||
| 927 | break; | ||
| 928 | case Maxwell::PrimitiveTopology::LinesAdjacency: | ||
| 929 | case Maxwell::PrimitiveTopology::LineStripAdjacency: | ||
| 930 | profile.input_topology = Shader::InputTopology::LinesAdjacency; | ||
| 931 | break; | ||
| 932 | case Maxwell::PrimitiveTopology::TrianglesAdjacency: | ||
| 933 | case Maxwell::PrimitiveTopology::TriangleStripAdjacency: | ||
| 934 | profile.input_topology = Shader::InputTopology::TrianglesAdjacency; | ||
| 935 | break; | ||
| 892 | } | 936 | } |
| 893 | return profile; | 937 | return profile; |
| 894 | } | 938 | } |
diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.h b/src/video_core/renderer_vulkan/vk_pipeline_cache.h index 343ea1554..8b6839966 100644 --- a/src/video_core/renderer_vulkan/vk_pipeline_cache.h +++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.h | |||
| @@ -33,6 +33,10 @@ namespace Core { | |||
| 33 | class System; | 33 | class System; |
| 34 | } | 34 | } |
| 35 | 35 | ||
| 36 | namespace Shader::IR { | ||
| 37 | struct Program; | ||
| 38 | } | ||
| 39 | |||
| 36 | namespace Vulkan { | 40 | namespace Vulkan { |
| 37 | 41 | ||
| 38 | using Maxwell = Tegra::Engines::Maxwell3D::Regs; | 42 | using Maxwell = Tegra::Engines::Maxwell3D::Regs; |
| @@ -160,7 +164,8 @@ private: | |||
| 160 | Shader::Environment& env, | 164 | Shader::Environment& env, |
| 161 | bool build_in_parallel); | 165 | bool build_in_parallel); |
| 162 | 166 | ||
| 163 | Shader::Profile MakeProfile(const GraphicsPipelineCacheKey& key, Shader::Stage stage); | 167 | Shader::Profile MakeProfile(const GraphicsPipelineCacheKey& key, |
| 168 | const Shader::IR::Program& program); | ||
| 164 | 169 | ||
| 165 | Tegra::GPU& gpu; | 170 | Tegra::GPU& gpu; |
| 166 | Tegra::Engines::Maxwell3D& maxwell3d; | 171 | Tegra::Engines::Maxwell3D& maxwell3d; |