shader: Split profile and runtime information in separate structs
This commit is contained in:
		| @@ -23,23 +23,25 @@ std::string_view InterpDecorator(Interpolation interp) { | |||||||
| } | } | ||||||
| } // Anonymous namespace | } // Anonymous namespace | ||||||
|  |  | ||||||
| EmitContext::EmitContext(IR::Program& program, Bindings& bindings, const Profile& profile_) | EmitContext::EmitContext(IR::Program& program, Bindings& bindings, const Profile& profile_, | ||||||
|     : info{program.info}, profile{profile_} { |                          const RuntimeInfo& runtime_info_) | ||||||
|  |     : profile{profile_}, runtime_info{runtime_info_} { | ||||||
|     // FIXME: Temporary partial implementation |     // FIXME: Temporary partial implementation | ||||||
|  |     const auto& info{program.info}; | ||||||
|     u32 cbuf_index{}; |     u32 cbuf_index{}; | ||||||
|     for (const auto& desc : program.info.constant_buffer_descriptors) { |     for (const auto& desc : info.constant_buffer_descriptors) { | ||||||
|         if (desc.count != 1) { |         if (desc.count != 1) { | ||||||
|             throw NotImplementedException("Constant buffer descriptor array"); |             throw NotImplementedException("Constant buffer descriptor array"); | ||||||
|         } |         } | ||||||
|         Add("CBUFFER c{}[]={{program.buffer[{}]}};", desc.index, cbuf_index); |         Add("CBUFFER c{}[]={{program.buffer[{}]}};", desc.index, cbuf_index); | ||||||
|         ++cbuf_index; |         ++cbuf_index; | ||||||
|     } |     } | ||||||
|     for (const auto& desc : program.info.storage_buffers_descriptors) { |     for (const auto& desc : info.storage_buffers_descriptors) { | ||||||
|         if (desc.count != 1) { |         if (desc.count != 1) { | ||||||
|             throw NotImplementedException("Storage buffer descriptor array"); |             throw NotImplementedException("Storage buffer descriptor array"); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|     if (const size_t num = program.info.storage_buffers_descriptors.size(); num > 0) { |     if (const size_t num = info.storage_buffers_descriptors.size(); num > 0) { | ||||||
|         Add("PARAM c[{}]={{program.local[0..{}]}};", num, num - 1); |         Add("PARAM c[{}]={{program.local[0..{}]}};", num, num - 1); | ||||||
|     } |     } | ||||||
|     stage = program.stage; |     stage = program.stage; | ||||||
| @@ -67,8 +69,8 @@ EmitContext::EmitContext(IR::Program& program, Bindings& bindings, const Profile | |||||||
|         break; |         break; | ||||||
|     } |     } | ||||||
|     const std::string_view attr_stage{stage == Stage::Fragment ? "fragment" : "vertex"}; |     const std::string_view attr_stage{stage == Stage::Fragment ? "fragment" : "vertex"}; | ||||||
|     for (size_t index = 0; index < program.info.input_generics.size(); ++index) { |     for (size_t index = 0; index < info.input_generics.size(); ++index) { | ||||||
|         const auto& generic{program.info.input_generics[index]}; |         const auto& generic{info.input_generics[index]}; | ||||||
|         if (generic.used) { |         if (generic.used) { | ||||||
|             Add("{}ATTRIB in_attr{}[]={{{}.attrib[{}..{}]}};", |             Add("{}ATTRIB in_attr{}[]={{{}.attrib[{}..{}]}};", | ||||||
|                 InterpDecorator(generic.interpolation), index, attr_stage, index, index); |                 InterpDecorator(generic.interpolation), index, attr_stage, index, index); | ||||||
| @@ -101,8 +103,8 @@ EmitContext::EmitContext(IR::Program& program, Bindings& bindings, const Profile | |||||||
|                 index, index); |                 index, index); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|     for (size_t index = 0; index < program.info.stores_frag_color.size(); ++index) { |     for (size_t index = 0; index < info.stores_frag_color.size(); ++index) { | ||||||
|         if (!program.info.stores_frag_color[index]) { |         if (!info.stores_frag_color[index]) { | ||||||
|             continue; |             continue; | ||||||
|         } |         } | ||||||
|         if (index == 0) { |         if (index == 0) { | ||||||
| @@ -111,28 +113,28 @@ EmitContext::EmitContext(IR::Program& program, Bindings& bindings, const Profile | |||||||
|             Add("OUTPUT frag_color{}=result.color[{}];", index, index); |             Add("OUTPUT frag_color{}=result.color[{}];", index, index); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|     for (size_t index = 0; index < program.info.stores_generics.size(); ++index) { |     for (size_t index = 0; index < info.stores_generics.size(); ++index) { | ||||||
|         if (program.info.stores_generics[index]) { |         if (info.stores_generics[index]) { | ||||||
|             Add("OUTPUT out_attr{}[]={{result.attrib[{}..{}]}};", index, index, index); |             Add("OUTPUT out_attr{}[]={{result.attrib[{}..{}]}};", index, index, index); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|     image_buffer_bindings.reserve(program.info.image_buffer_descriptors.size()); |     image_buffer_bindings.reserve(info.image_buffer_descriptors.size()); | ||||||
|     for (const auto& desc : program.info.image_buffer_descriptors) { |     for (const auto& desc : info.image_buffer_descriptors) { | ||||||
|         image_buffer_bindings.push_back(bindings.image); |         image_buffer_bindings.push_back(bindings.image); | ||||||
|         bindings.image += desc.count; |         bindings.image += desc.count; | ||||||
|     } |     } | ||||||
|     image_bindings.reserve(program.info.image_descriptors.size()); |     image_bindings.reserve(info.image_descriptors.size()); | ||||||
|     for (const auto& desc : program.info.image_descriptors) { |     for (const auto& desc : info.image_descriptors) { | ||||||
|         image_bindings.push_back(bindings.image); |         image_bindings.push_back(bindings.image); | ||||||
|         bindings.image += desc.count; |         bindings.image += desc.count; | ||||||
|     } |     } | ||||||
|     texture_buffer_bindings.reserve(program.info.texture_buffer_descriptors.size()); |     texture_buffer_bindings.reserve(info.texture_buffer_descriptors.size()); | ||||||
|     for (const auto& desc : program.info.texture_buffer_descriptors) { |     for (const auto& desc : info.texture_buffer_descriptors) { | ||||||
|         texture_buffer_bindings.push_back(bindings.texture); |         texture_buffer_bindings.push_back(bindings.texture); | ||||||
|         bindings.texture += desc.count; |         bindings.texture += desc.count; | ||||||
|     } |     } | ||||||
|     texture_bindings.reserve(program.info.texture_descriptors.size()); |     texture_bindings.reserve(info.texture_descriptors.size()); | ||||||
|     for (const auto& desc : program.info.texture_descriptors) { |     for (const auto& desc : info.texture_descriptors) { | ||||||
|         texture_bindings.push_back(bindings.texture); |         texture_bindings.push_back(bindings.texture); | ||||||
|         bindings.texture += desc.count; |         bindings.texture += desc.count; | ||||||
|     } |     } | ||||||
|   | |||||||
| @@ -16,6 +16,7 @@ | |||||||
| namespace Shader { | namespace Shader { | ||||||
| struct Info; | struct Info; | ||||||
| struct Profile; | struct Profile; | ||||||
|  | struct RuntimeInfo; | ||||||
| } // namespace Shader | } // namespace Shader | ||||||
|  |  | ||||||
| namespace Shader::Backend { | namespace Shader::Backend { | ||||||
| @@ -31,7 +32,8 @@ namespace Shader::Backend::GLASM { | |||||||
|  |  | ||||||
| class EmitContext { | class EmitContext { | ||||||
| public: | public: | ||||||
|     explicit EmitContext(IR::Program& program, Bindings& bindings, const Profile& profile_); |     explicit EmitContext(IR::Program& program, Bindings& bindings, const Profile& profile_, | ||||||
|  |                          const RuntimeInfo& runtime_info_); | ||||||
|  |  | ||||||
|     template <typename... Args> |     template <typename... Args> | ||||||
|     void Add(const char* format_str, IR::Inst& inst, Args&&... args) { |     void Add(const char* format_str, IR::Inst& inst, Args&&... args) { | ||||||
| @@ -56,8 +58,8 @@ public: | |||||||
|  |  | ||||||
|     std::string code; |     std::string code; | ||||||
|     RegAlloc reg_alloc{*this}; |     RegAlloc reg_alloc{*this}; | ||||||
|     const Info& info; |  | ||||||
|     const Profile& profile; |     const Profile& profile; | ||||||
|  |     const RuntimeInfo& runtime_info; | ||||||
|  |  | ||||||
|     std::vector<u32> texture_buffer_bindings; |     std::vector<u32> texture_buffer_bindings; | ||||||
|     std::vector<u32> image_buffer_bindings; |     std::vector<u32> image_buffer_bindings; | ||||||
|   | |||||||
| @@ -374,8 +374,9 @@ std::string_view GetTessSpacing(TessSpacing spacing) { | |||||||
| } | } | ||||||
| } // Anonymous namespace | } // Anonymous namespace | ||||||
|  |  | ||||||
| std::string EmitGLASM(const Profile& profile, IR::Program& program, Bindings& bindings) { | std::string EmitGLASM(const Profile& profile, const RuntimeInfo& runtime_info, IR::Program& program, | ||||||
|     EmitContext ctx{program, bindings, profile}; |                       Bindings& bindings) { | ||||||
|  |     EmitContext ctx{program, bindings, profile, runtime_info}; | ||||||
|     Precolor(ctx, program); |     Precolor(ctx, program); | ||||||
|     EmitCode(ctx, program); |     EmitCode(ctx, program); | ||||||
|     std::string header{StageHeader(program.stage)}; |     std::string header{StageHeader(program.stage)}; | ||||||
| @@ -385,18 +386,18 @@ std::string EmitGLASM(const Profile& profile, IR::Program& program, Bindings& bi | |||||||
|         header += fmt::format("VERTICES_OUT {};", program.invocations); |         header += fmt::format("VERTICES_OUT {};", program.invocations); | ||||||
|         break; |         break; | ||||||
|     case Stage::TessellationEval: |     case Stage::TessellationEval: | ||||||
|         header += |         header += fmt::format("TESS_MODE {};" | ||||||
|             fmt::format("TESS_MODE {};" |                               "TESS_SPACING {};" | ||||||
|                         "TESS_SPACING {};" |                               "TESS_VERTEX_ORDER {};", | ||||||
|                         "TESS_VERTEX_ORDER {};", |                               GetTessMode(runtime_info.tess_primitive), | ||||||
|                         GetTessMode(profile.tess_primitive), GetTessSpacing(profile.tess_spacing), |                               GetTessSpacing(runtime_info.tess_spacing), | ||||||
|                         profile.tess_clockwise ? "CW" : "CCW"); |                               runtime_info.tess_clockwise ? "CW" : "CCW"); | ||||||
|         break; |         break; | ||||||
|     case Stage::Geometry: |     case Stage::Geometry: | ||||||
|         header += fmt::format("PRIMITIVE_IN {};" |         header += fmt::format("PRIMITIVE_IN {};" | ||||||
|                               "PRIMITIVE_OUT {};" |                               "PRIMITIVE_OUT {};" | ||||||
|                               "VERTICES_OUT {};", |                               "VERTICES_OUT {};", | ||||||
|                               InputPrimitive(profile.input_topology), |                               InputPrimitive(runtime_info.input_topology), | ||||||
|                               OutputPrimitive(program.output_topology), program.output_vertices); |                               OutputPrimitive(program.output_topology), program.output_vertices); | ||||||
|         break; |         break; | ||||||
|     case Stage::Compute: |     case Stage::Compute: | ||||||
|   | |||||||
| @@ -12,12 +12,12 @@ | |||||||
|  |  | ||||||
| namespace Shader::Backend::GLASM { | namespace Shader::Backend::GLASM { | ||||||
|  |  | ||||||
| [[nodiscard]] std::string EmitGLASM(const Profile& profile, IR::Program& program, | [[nodiscard]] std::string EmitGLASM(const Profile& profile, const RuntimeInfo& runtime_info, | ||||||
|                                     Bindings& binding); |                                     IR::Program& program, Bindings& bindings); | ||||||
|  |  | ||||||
| [[nodiscard]] inline std::string EmitGLASM(const Profile& profile, IR::Program& program) { | [[nodiscard]] inline std::string EmitGLASM(const Profile& profile, IR::Program& program) { | ||||||
|     Bindings binding; |     Bindings binding; | ||||||
|     return EmitGLASM(profile, program, binding); |     return EmitGLASM(profile, {}, program, binding); | ||||||
| } | } | ||||||
|  |  | ||||||
| } // namespace Shader::Backend::GLASM | } // namespace Shader::Backend::GLASM | ||||||
|   | |||||||
| @@ -136,7 +136,7 @@ Id DefineInput(EmitContext& ctx, Id type, bool per_invocation, | |||||||
|         break; |         break; | ||||||
|     case Stage::Geometry: |     case Stage::Geometry: | ||||||
|         if (per_invocation) { |         if (per_invocation) { | ||||||
|             const u32 num_vertices{NumVertices(ctx.profile.input_topology)}; |             const u32 num_vertices{NumVertices(ctx.runtime_info.input_topology)}; | ||||||
|             type = ctx.TypeArray(type, ctx.Const(num_vertices)); |             type = ctx.TypeArray(type, ctx.Const(num_vertices)); | ||||||
|         } |         } | ||||||
|         break; |         break; | ||||||
| @@ -161,8 +161,8 @@ void DefineGenericOutput(EmitContext& ctx, size_t index, std::optional<u32> invo | |||||||
|     while (element < 4) { |     while (element < 4) { | ||||||
|         const u32 remainder{4 - element}; |         const u32 remainder{4 - element}; | ||||||
|         const TransformFeedbackVarying* xfb_varying{}; |         const TransformFeedbackVarying* xfb_varying{}; | ||||||
|         if (!ctx.profile.xfb_varyings.empty()) { |         if (!ctx.runtime_info.xfb_varyings.empty()) { | ||||||
|             xfb_varying = &ctx.profile.xfb_varyings[base_attr_index + element]; |             xfb_varying = &ctx.runtime_info.xfb_varyings[base_attr_index + element]; | ||||||
|             xfb_varying = xfb_varying && xfb_varying->components > 0 ? xfb_varying : nullptr; |             xfb_varying = xfb_varying && xfb_varying->components > 0 ? xfb_varying : nullptr; | ||||||
|         } |         } | ||||||
|         const u32 num_components{xfb_varying ? xfb_varying->components : remainder}; |         const u32 num_components{xfb_varying ? xfb_varying->components : remainder}; | ||||||
| @@ -208,7 +208,7 @@ Id GetAttributeType(EmitContext& ctx, AttributeType type) { | |||||||
| } | } | ||||||
|  |  | ||||||
| std::optional<AttrInfo> AttrTypes(EmitContext& ctx, u32 index) { | std::optional<AttrInfo> AttrTypes(EmitContext& ctx, u32 index) { | ||||||
|     const AttributeType type{ctx.profile.generic_input_types.at(index)}; |     const AttributeType type{ctx.runtime_info.generic_input_types.at(index)}; | ||||||
|     switch (type) { |     switch (type) { | ||||||
|     case AttributeType::Float: |     case AttributeType::Float: | ||||||
|         return AttrInfo{ctx.input_f32, ctx.F32[1], false}; |         return AttrInfo{ctx.input_f32, ctx.F32[1], false}; | ||||||
| @@ -441,13 +441,15 @@ void VectorTypes::Define(Sirit::Module& sirit_ctx, Id base_type, std::string_vie | |||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| EmitContext::EmitContext(const Profile& profile_, IR::Program& program, Bindings& binding) | EmitContext::EmitContext(const Profile& profile_, const RuntimeInfo& runtime_info_, | ||||||
|     : Sirit::Module(profile_.supported_spirv), profile{profile_}, stage{program.stage} { |                          IR::Program& program, Bindings& bindings) | ||||||
|  |     : Sirit::Module(profile_.supported_spirv), profile{profile_}, | ||||||
|  |       runtime_info{runtime_info_}, stage{program.stage} { | ||||||
|     const bool is_unified{profile.unified_descriptor_binding}; |     const bool is_unified{profile.unified_descriptor_binding}; | ||||||
|     u32& uniform_binding{is_unified ? binding.unified : binding.uniform_buffer}; |     u32& uniform_binding{is_unified ? bindings.unified : bindings.uniform_buffer}; | ||||||
|     u32& storage_binding{is_unified ? binding.unified : binding.storage_buffer}; |     u32& storage_binding{is_unified ? bindings.unified : bindings.storage_buffer}; | ||||||
|     u32& texture_binding{is_unified ? binding.unified : binding.texture}; |     u32& texture_binding{is_unified ? bindings.unified : bindings.texture}; | ||||||
|     u32& image_binding{is_unified ? binding.unified : binding.image}; |     u32& image_binding{is_unified ? bindings.unified : bindings.image}; | ||||||
|     AddCapability(spv::Capability::Shader); |     AddCapability(spv::Capability::Shader); | ||||||
|     DefineCommonTypes(program.info); |     DefineCommonTypes(program.info); | ||||||
|     DefineCommonConstants(); |     DefineCommonConstants(); | ||||||
| @@ -1211,7 +1213,7 @@ void EmitContext::DefineInputs(const Info& info) { | |||||||
|         if (!generic.used) { |         if (!generic.used) { | ||||||
|             continue; |             continue; | ||||||
|         } |         } | ||||||
|         const AttributeType input_type{profile.generic_input_types[index]}; |         const AttributeType input_type{runtime_info.generic_input_types[index]}; | ||||||
|         if (input_type == AttributeType::Disabled) { |         if (input_type == AttributeType::Disabled) { | ||||||
|             continue; |             continue; | ||||||
|         } |         } | ||||||
| @@ -1256,7 +1258,7 @@ void EmitContext::DefineOutputs(const IR::Program& program) { | |||||||
|     if (info.stores_position || stage == Stage::VertexB) { |     if (info.stores_position || stage == Stage::VertexB) { | ||||||
|         output_position = DefineOutput(*this, F32[4], invocations, spv::BuiltIn::Position); |         output_position = DefineOutput(*this, F32[4], invocations, spv::BuiltIn::Position); | ||||||
|     } |     } | ||||||
|     if (info.stores_point_size || profile.fixed_state_point_size) { |     if (info.stores_point_size || runtime_info.fixed_state_point_size) { | ||||||
|         if (stage == Stage::Fragment) { |         if (stage == Stage::Fragment) { | ||||||
|             throw NotImplementedException("Storing PointSize in fragment stage"); |             throw NotImplementedException("Storing PointSize in fragment stage"); | ||||||
|         } |         } | ||||||
|   | |||||||
| @@ -103,7 +103,8 @@ struct GenericElementInfo { | |||||||
|  |  | ||||||
| class EmitContext final : public Sirit::Module { | class EmitContext final : public Sirit::Module { | ||||||
| public: | public: | ||||||
|     explicit EmitContext(const Profile& profile, IR::Program& program, Bindings& binding); |     explicit EmitContext(const Profile& profile, const RuntimeInfo& runtime_info, | ||||||
|  |                          IR::Program& program, Bindings& binding); | ||||||
|     ~EmitContext(); |     ~EmitContext(); | ||||||
|  |  | ||||||
|     [[nodiscard]] Id Def(const IR::Value& value); |     [[nodiscard]] Id Def(const IR::Value& value); | ||||||
| @@ -150,6 +151,7 @@ public: | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     const Profile& profile; |     const Profile& profile; | ||||||
|  |     const RuntimeInfo& runtime_info; | ||||||
|     Stage stage{}; |     Stage stage{}; | ||||||
|  |  | ||||||
|     Id void_id{}; |     Id void_id{}; | ||||||
|   | |||||||
| @@ -226,16 +226,17 @@ void DefineEntryPoint(const IR::Program& program, EmitContext& ctx, Id main) { | |||||||
|     case Stage::TessellationEval: |     case Stage::TessellationEval: | ||||||
|         execution_model = spv::ExecutionModel::TessellationEvaluation; |         execution_model = spv::ExecutionModel::TessellationEvaluation; | ||||||
|         ctx.AddCapability(spv::Capability::Tessellation); |         ctx.AddCapability(spv::Capability::Tessellation); | ||||||
|         ctx.AddExecutionMode(main, ExecutionMode(ctx.profile.tess_primitive)); |         ctx.AddExecutionMode(main, ExecutionMode(ctx.runtime_info.tess_primitive)); | ||||||
|         ctx.AddExecutionMode(main, ExecutionMode(ctx.profile.tess_spacing)); |         ctx.AddExecutionMode(main, ExecutionMode(ctx.runtime_info.tess_spacing)); | ||||||
|         ctx.AddExecutionMode(main, ctx.profile.tess_clockwise ? spv::ExecutionMode::VertexOrderCw |         ctx.AddExecutionMode(main, ctx.runtime_info.tess_clockwise | ||||||
|                                                               : spv::ExecutionMode::VertexOrderCcw); |                                        ? spv::ExecutionMode::VertexOrderCw | ||||||
|  |                                        : spv::ExecutionMode::VertexOrderCcw); | ||||||
|         break; |         break; | ||||||
|     case Stage::Geometry: |     case Stage::Geometry: | ||||||
|         execution_model = spv::ExecutionModel::Geometry; |         execution_model = spv::ExecutionModel::Geometry; | ||||||
|         ctx.AddCapability(spv::Capability::Geometry); |         ctx.AddCapability(spv::Capability::Geometry); | ||||||
|         ctx.AddCapability(spv::Capability::GeometryStreams); |         ctx.AddCapability(spv::Capability::GeometryStreams); | ||||||
|         switch (ctx.profile.input_topology) { |         switch (ctx.runtime_info.input_topology) { | ||||||
|         case InputTopology::Points: |         case InputTopology::Points: | ||||||
|             ctx.AddExecutionMode(main, spv::ExecutionMode::InputPoints); |             ctx.AddExecutionMode(main, spv::ExecutionMode::InputPoints); | ||||||
|             break; |             break; | ||||||
| @@ -279,7 +280,7 @@ void DefineEntryPoint(const IR::Program& program, EmitContext& ctx, Id main) { | |||||||
|         if (program.info.stores_frag_depth) { |         if (program.info.stores_frag_depth) { | ||||||
|             ctx.AddExecutionMode(main, spv::ExecutionMode::DepthReplacing); |             ctx.AddExecutionMode(main, spv::ExecutionMode::DepthReplacing); | ||||||
|         } |         } | ||||||
|         if (ctx.profile.force_early_z) { |         if (ctx.runtime_info.force_early_z) { | ||||||
|             ctx.AddExecutionMode(main, spv::ExecutionMode::EarlyFragmentTests); |             ctx.AddExecutionMode(main, spv::ExecutionMode::EarlyFragmentTests); | ||||||
|         } |         } | ||||||
|         break; |         break; | ||||||
| @@ -402,7 +403,7 @@ void SetupCapabilities(const Profile& profile, const Info& info, EmitContext& ct | |||||||
|     if (info.uses_sample_id) { |     if (info.uses_sample_id) { | ||||||
|         ctx.AddCapability(spv::Capability::SampleRateShading); |         ctx.AddCapability(spv::Capability::SampleRateShading); | ||||||
|     } |     } | ||||||
|     if (!ctx.profile.xfb_varyings.empty()) { |     if (!ctx.runtime_info.xfb_varyings.empty()) { | ||||||
|         ctx.AddCapability(spv::Capability::TransformFeedback); |         ctx.AddCapability(spv::Capability::TransformFeedback); | ||||||
|     } |     } | ||||||
|     if (info.uses_derivatives) { |     if (info.uses_derivatives) { | ||||||
| @@ -433,8 +434,9 @@ void PatchPhiNodes(IR::Program& program, EmitContext& ctx) { | |||||||
| } | } | ||||||
| } // Anonymous namespace | } // Anonymous namespace | ||||||
|  |  | ||||||
| std::vector<u32> EmitSPIRV(const Profile& profile, IR::Program& program, Bindings& binding) { | std::vector<u32> EmitSPIRV(const Profile& profile, const RuntimeInfo& runtime_info, | ||||||
|     EmitContext ctx{profile, program, binding}; |                            IR::Program& program, Bindings& bindings) { | ||||||
|  |     EmitContext ctx{profile, runtime_info, program, bindings}; | ||||||
|     const Id main{DefineMain(ctx, program)}; |     const Id main{DefineMain(ctx, program)}; | ||||||
|     DefineEntryPoint(program, ctx, main); |     DefineEntryPoint(program, ctx, main); | ||||||
|     if (profile.support_float_controls) { |     if (profile.support_float_controls) { | ||||||
|   | |||||||
| @@ -16,12 +16,12 @@ | |||||||
|  |  | ||||||
| namespace Shader::Backend::SPIRV { | namespace Shader::Backend::SPIRV { | ||||||
|  |  | ||||||
| [[nodiscard]] std::vector<u32> EmitSPIRV(const Profile& profile, IR::Program& program, | [[nodiscard]] std::vector<u32> EmitSPIRV(const Profile& profile, const RuntimeInfo& runtime_info, | ||||||
|                                          Bindings& binding); |                                          IR::Program& program, Bindings& bindings); | ||||||
|  |  | ||||||
| [[nodiscard]] inline std::vector<u32> EmitSPIRV(const Profile& profile, IR::Program& program) { | [[nodiscard]] inline std::vector<u32> EmitSPIRV(const Profile& profile, IR::Program& program) { | ||||||
|     Bindings binding; |     Bindings binding; | ||||||
|     return EmitSPIRV(profile, program, binding); |     return EmitSPIRV(profile, {}, program, binding); | ||||||
| } | } | ||||||
|  |  | ||||||
| } // namespace Shader::Backend::SPIRV | } // namespace Shader::Backend::SPIRV | ||||||
|   | |||||||
| @@ -17,7 +17,7 @@ struct AttrInfo { | |||||||
| }; | }; | ||||||
|  |  | ||||||
| std::optional<AttrInfo> AttrTypes(EmitContext& ctx, u32 index) { | std::optional<AttrInfo> AttrTypes(EmitContext& ctx, u32 index) { | ||||||
|     const AttributeType type{ctx.profile.generic_input_types.at(index)}; |     const AttributeType type{ctx.runtime_info.generic_input_types.at(index)}; | ||||||
|     switch (type) { |     switch (type) { | ||||||
|     case AttributeType::Float: |     case AttributeType::Float: | ||||||
|         return AttrInfo{ctx.input_f32, ctx.F32[1], false}; |         return AttrInfo{ctx.input_f32, ctx.F32[1], false}; | ||||||
| @@ -468,7 +468,7 @@ Id EmitIsHelperInvocation(EmitContext& ctx) { | |||||||
| } | } | ||||||
|  |  | ||||||
| Id EmitYDirection(EmitContext& ctx) { | Id EmitYDirection(EmitContext& ctx) { | ||||||
|     return ctx.Const(ctx.profile.y_negate ? -1.0f : 1.0f); |     return ctx.Const(ctx.runtime_info.y_negate ? -1.0f : 1.0f); | ||||||
| } | } | ||||||
|  |  | ||||||
| Id EmitLoadLocal(EmitContext& ctx, Id word_offset) { | Id EmitLoadLocal(EmitContext& ctx, Id word_offset) { | ||||||
|   | |||||||
| @@ -18,8 +18,8 @@ void ConvertDepthMode(EmitContext& ctx) { | |||||||
| } | } | ||||||
|  |  | ||||||
| void SetFixedPipelinePointSize(EmitContext& ctx) { | void SetFixedPipelinePointSize(EmitContext& ctx) { | ||||||
|     if (ctx.profile.fixed_state_point_size) { |     if (ctx.runtime_info.fixed_state_point_size) { | ||||||
|         const float point_size{*ctx.profile.fixed_state_point_size}; |         const float point_size{*ctx.runtime_info.fixed_state_point_size}; | ||||||
|         ctx.OpStore(ctx.output_point_size, ctx.Const(point_size)); |         ctx.OpStore(ctx.output_point_size, ctx.Const(point_size)); | ||||||
|     } |     } | ||||||
| } | } | ||||||
| @@ -62,7 +62,10 @@ Id ComparisonFunction(EmitContext& ctx, CompareFunction comparison, Id operand_1 | |||||||
| } | } | ||||||
|  |  | ||||||
| void AlphaTest(EmitContext& ctx) { | void AlphaTest(EmitContext& ctx) { | ||||||
|     const auto comparison{*ctx.profile.alpha_test_func}; |     if (!ctx.runtime_info.alpha_test_func) { | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  |     const auto comparison{*ctx.runtime_info.alpha_test_func}; | ||||||
|     if (comparison == CompareFunction::Always) { |     if (comparison == CompareFunction::Always) { | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
| @@ -76,7 +79,7 @@ void AlphaTest(EmitContext& ctx) { | |||||||
|  |  | ||||||
|     const Id true_label{ctx.OpLabel()}; |     const Id true_label{ctx.OpLabel()}; | ||||||
|     const Id discard_label{ctx.OpLabel()}; |     const Id discard_label{ctx.OpLabel()}; | ||||||
|     const Id alpha_reference{ctx.Const(ctx.profile.alpha_test_reference)}; |     const Id alpha_reference{ctx.Const(ctx.runtime_info.alpha_test_reference)}; | ||||||
|     const Id condition{ComparisonFunction(ctx, comparison, alpha, alpha_reference)}; |     const Id condition{ComparisonFunction(ctx, comparison, alpha, alpha_reference)}; | ||||||
|  |  | ||||||
|     ctx.OpSelectionMerge(true_label, spv::SelectionControlMask::MaskNone); |     ctx.OpSelectionMerge(true_label, spv::SelectionControlMask::MaskNone); | ||||||
| @@ -113,7 +116,7 @@ void EmitPrologue(EmitContext& ctx) { | |||||||
| } | } | ||||||
|  |  | ||||||
| void EmitEpilogue(EmitContext& ctx) { | void EmitEpilogue(EmitContext& ctx) { | ||||||
|     if (ctx.stage == Stage::VertexB && ctx.profile.convert_depth_mode) { |     if (ctx.stage == Stage::VertexB && ctx.runtime_info.convert_depth_mode) { | ||||||
|         ConvertDepthMode(ctx); |         ConvertDepthMode(ctx); | ||||||
|     } |     } | ||||||
|     if (ctx.stage == Stage::Fragment) { |     if (ctx.stage == Stage::Fragment) { | ||||||
| @@ -122,7 +125,7 @@ void EmitEpilogue(EmitContext& ctx) { | |||||||
| } | } | ||||||
|  |  | ||||||
| void EmitEmitVertex(EmitContext& ctx, const IR::Value& stream) { | void EmitEmitVertex(EmitContext& ctx, const IR::Value& stream) { | ||||||
|     if (ctx.profile.convert_depth_mode) { |     if (ctx.runtime_info.convert_depth_mode) { | ||||||
|         ConvertDepthMode(ctx); |         ConvertDepthMode(ctx); | ||||||
|     } |     } | ||||||
|     if (stream.IsImmediate()) { |     if (stream.IsImmediate()) { | ||||||
|   | |||||||
| @@ -81,19 +81,22 @@ struct Profile { | |||||||
|     bool support_viewport_mask{}; |     bool support_viewport_mask{}; | ||||||
|     bool support_typeless_image_loads{}; |     bool support_typeless_image_loads{}; | ||||||
|     bool support_demote_to_helper_invocation{}; |     bool support_demote_to_helper_invocation{}; | ||||||
|     bool warp_size_potentially_larger_than_guest{}; |  | ||||||
|     bool support_int64_atomics{}; |     bool support_int64_atomics{}; | ||||||
|  |  | ||||||
|  |     bool warp_size_potentially_larger_than_guest{}; | ||||||
|     bool lower_left_origin_mode{}; |     bool lower_left_origin_mode{}; | ||||||
|  |  | ||||||
|     // FClamp is broken and OpFMax + OpFMin should be used instead |     /// OpFClamp is broken and OpFMax + OpFMin should be used instead | ||||||
|     bool has_broken_spirv_clamp{}; |     bool has_broken_spirv_clamp{}; | ||||||
|     // Offset image operands with an unsigned type do not work |     /// Offset image operands with an unsigned type do not work | ||||||
|     bool has_broken_unsigned_image_offsets{}; |     bool has_broken_unsigned_image_offsets{}; | ||||||
|     // Signed instructions with unsigned data types are misinterpreted |     /// Signed instructions with unsigned data types are misinterpreted | ||||||
|     bool has_broken_signed_operations{}; |     bool has_broken_signed_operations{}; | ||||||
|     // Ignores SPIR-V ordered vs unordered using GLSL semantics |     /// Ignores SPIR-V ordered vs unordered using GLSL semantics | ||||||
|     bool ignore_nan_fp_comparisons{}; |     bool ignore_nan_fp_comparisons{}; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | struct RuntimeInfo { | ||||||
|     std::array<AttributeType, 32> generic_input_types{}; |     std::array<AttributeType, 32> generic_input_types{}; | ||||||
|     bool convert_depth_mode{}; |     bool convert_depth_mode{}; | ||||||
|     bool force_early_z{}; |     bool force_early_z{}; | ||||||
|   | |||||||
| @@ -61,33 +61,15 @@ const Shader::Profile profile{ | |||||||
|     .support_viewport_mask = true, |     .support_viewport_mask = true, | ||||||
|     .support_typeless_image_loads = true, |     .support_typeless_image_loads = true, | ||||||
|     .support_demote_to_helper_invocation = false, |     .support_demote_to_helper_invocation = false, | ||||||
|     .warp_size_potentially_larger_than_guest = true, |  | ||||||
|     .support_int64_atomics = false, |     .support_int64_atomics = false, | ||||||
|  |  | ||||||
|  |     .warp_size_potentially_larger_than_guest = true, | ||||||
|     .lower_left_origin_mode = true, |     .lower_left_origin_mode = true, | ||||||
|  |  | ||||||
|     .has_broken_spirv_clamp = true, |     .has_broken_spirv_clamp = true, | ||||||
|     .has_broken_unsigned_image_offsets = true, |     .has_broken_unsigned_image_offsets = true, | ||||||
|     .has_broken_signed_operations = true, |     .has_broken_signed_operations = true, | ||||||
|     .ignore_nan_fp_comparisons = true, |     .ignore_nan_fp_comparisons = true, | ||||||
|  |  | ||||||
|     .generic_input_types = {}, |  | ||||||
|     .convert_depth_mode = false, |  | ||||||
|     .force_early_z = false, |  | ||||||
|  |  | ||||||
|     .tess_primitive = {}, |  | ||||||
|     .tess_spacing = {}, |  | ||||||
|     .tess_clockwise = false, |  | ||||||
|  |  | ||||||
|     .input_topology = Shader::InputTopology::Triangles, |  | ||||||
|  |  | ||||||
|     .fixed_state_point_size = std::nullopt, |  | ||||||
|  |  | ||||||
|     .alpha_test_func = Shader::CompareFunction::Always, |  | ||||||
|     .alpha_test_reference = 0.0f, |  | ||||||
|  |  | ||||||
|     .y_negate = false, |  | ||||||
|  |  | ||||||
|     .xfb_varyings = {}, |  | ||||||
| }; | }; | ||||||
|  |  | ||||||
| using Shader::Backend::GLASM::EmitGLASM; | using Shader::Backend::GLASM::EmitGLASM; | ||||||
| @@ -302,10 +284,10 @@ std::unique_ptr<GraphicsProgram> ShaderCache::CreateGraphicsProgram( | |||||||
|         const size_t stage_index{index - 1}; |         const size_t stage_index{index - 1}; | ||||||
|         infos[stage_index] = &program.info; |         infos[stage_index] = &program.info; | ||||||
|         if (device.UseAssemblyShaders()) { |         if (device.UseAssemblyShaders()) { | ||||||
|             const std::string code{EmitGLASM(profile, program, binding)}; |             const std::string code{EmitGLASM(profile, {}, program, binding)}; | ||||||
|             assembly_programs[stage_index] = CompileProgram(code, AssemblyStage(stage_index)); |             assembly_programs[stage_index] = CompileProgram(code, AssemblyStage(stage_index)); | ||||||
|         } else { |         } else { | ||||||
|             const std::vector<u32> code{EmitSPIRV(profile, program, binding)}; |             const std::vector<u32> code{EmitSPIRV(profile, {}, program, binding)}; | ||||||
|             AddShader(Stage(stage_index), source_program.handle, code); |             AddShader(Stage(stage_index), source_program.handle, code); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|   | |||||||
| @@ -89,6 +89,208 @@ Shader::CompareFunction MaxwellToCompareFunction(Maxwell::ComparisonOp compariso | |||||||
|     UNIMPLEMENTED_MSG("Unimplemented comparison op={}", comparison); |     UNIMPLEMENTED_MSG("Unimplemented comparison op={}", comparison); | ||||||
|     return {}; |     return {}; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | static Shader::AttributeType CastAttributeType(const FixedPipelineState::VertexAttribute& attr) { | ||||||
|  |     if (attr.enabled == 0) { | ||||||
|  |         return Shader::AttributeType::Disabled; | ||||||
|  |     } | ||||||
|  |     switch (attr.Type()) { | ||||||
|  |     case Maxwell::VertexAttribute::Type::SignedNorm: | ||||||
|  |     case Maxwell::VertexAttribute::Type::UnsignedNorm: | ||||||
|  |     case Maxwell::VertexAttribute::Type::UnsignedScaled: | ||||||
|  |     case Maxwell::VertexAttribute::Type::SignedScaled: | ||||||
|  |     case Maxwell::VertexAttribute::Type::Float: | ||||||
|  |         return Shader::AttributeType::Float; | ||||||
|  |     case Maxwell::VertexAttribute::Type::SignedInt: | ||||||
|  |         return Shader::AttributeType::SignedInt; | ||||||
|  |     case Maxwell::VertexAttribute::Type::UnsignedInt: | ||||||
|  |         return Shader::AttributeType::UnsignedInt; | ||||||
|  |     } | ||||||
|  |     return Shader::AttributeType::Float; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | std::vector<Shader::TransformFeedbackVarying> MakeTransformFeedbackVaryings( | ||||||
|  |     const GraphicsPipelineCacheKey& key) { | ||||||
|  |     static constexpr std::array VECTORS{ | ||||||
|  |         28,  // gl_Position | ||||||
|  |         32,  // Generic 0 | ||||||
|  |         36,  // Generic 1 | ||||||
|  |         40,  // Generic 2 | ||||||
|  |         44,  // Generic 3 | ||||||
|  |         48,  // Generic 4 | ||||||
|  |         52,  // Generic 5 | ||||||
|  |         56,  // Generic 6 | ||||||
|  |         60,  // Generic 7 | ||||||
|  |         64,  // Generic 8 | ||||||
|  |         68,  // Generic 9 | ||||||
|  |         72,  // Generic 10 | ||||||
|  |         76,  // Generic 11 | ||||||
|  |         80,  // Generic 12 | ||||||
|  |         84,  // Generic 13 | ||||||
|  |         88,  // Generic 14 | ||||||
|  |         92,  // Generic 15 | ||||||
|  |         96,  // Generic 16 | ||||||
|  |         100, // Generic 17 | ||||||
|  |         104, // Generic 18 | ||||||
|  |         108, // Generic 19 | ||||||
|  |         112, // Generic 20 | ||||||
|  |         116, // Generic 21 | ||||||
|  |         120, // Generic 22 | ||||||
|  |         124, // Generic 23 | ||||||
|  |         128, // Generic 24 | ||||||
|  |         132, // Generic 25 | ||||||
|  |         136, // Generic 26 | ||||||
|  |         140, // Generic 27 | ||||||
|  |         144, // Generic 28 | ||||||
|  |         148, // Generic 29 | ||||||
|  |         152, // Generic 30 | ||||||
|  |         156, // Generic 31 | ||||||
|  |         160, // gl_FrontColor | ||||||
|  |         164, // gl_FrontSecondaryColor | ||||||
|  |         160, // gl_BackColor | ||||||
|  |         164, // gl_BackSecondaryColor | ||||||
|  |         192, // gl_TexCoord[0] | ||||||
|  |         196, // gl_TexCoord[1] | ||||||
|  |         200, // gl_TexCoord[2] | ||||||
|  |         204, // gl_TexCoord[3] | ||||||
|  |         208, // gl_TexCoord[4] | ||||||
|  |         212, // gl_TexCoord[5] | ||||||
|  |         216, // gl_TexCoord[6] | ||||||
|  |         220, // gl_TexCoord[7] | ||||||
|  |     }; | ||||||
|  |     std::vector<Shader::TransformFeedbackVarying> xfb(256); | ||||||
|  |     for (size_t buffer = 0; buffer < Maxwell::NumTransformFeedbackBuffers; ++buffer) { | ||||||
|  |         const auto& locations = key.state.xfb_state.varyings[buffer]; | ||||||
|  |         const auto& layout = key.state.xfb_state.layouts[buffer]; | ||||||
|  |         const u32 varying_count = layout.varying_count; | ||||||
|  |         u32 highest = 0; | ||||||
|  |         for (u32 offset = 0; offset < varying_count; ++offset) { | ||||||
|  |             const u32 base_offset = offset; | ||||||
|  |             const u8 location = locations[offset]; | ||||||
|  |  | ||||||
|  |             Shader::TransformFeedbackVarying varying; | ||||||
|  |             varying.buffer = layout.stream; | ||||||
|  |             varying.stride = layout.stride; | ||||||
|  |             varying.offset = offset * 4; | ||||||
|  |             varying.components = 1; | ||||||
|  |  | ||||||
|  |             if (std::ranges::find(VECTORS, Common::AlignDown(location, 4)) != VECTORS.end()) { | ||||||
|  |                 UNIMPLEMENTED_IF_MSG(location % 4 != 0, "Unaligned TFB"); | ||||||
|  |  | ||||||
|  |                 const u8 base_index = location / 4; | ||||||
|  |                 while (offset + 1 < varying_count && base_index == locations[offset + 1] / 4) { | ||||||
|  |                     ++offset; | ||||||
|  |                     ++varying.components; | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |             xfb[location] = varying; | ||||||
|  |             highest = std::max(highest, (base_offset + varying.components) * 4); | ||||||
|  |         } | ||||||
|  |         UNIMPLEMENTED_IF(highest != layout.stride); | ||||||
|  |     } | ||||||
|  |     return xfb; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | Shader::RuntimeInfo MakeRuntimeInfo(const GraphicsPipelineCacheKey& key, | ||||||
|  |                                     const Shader::IR::Program& program) { | ||||||
|  |     Shader::RuntimeInfo info; | ||||||
|  |  | ||||||
|  |     const Shader::Stage stage{program.stage}; | ||||||
|  |     const bool has_geometry{key.unique_hashes[4] != 0}; | ||||||
|  |     const bool gl_ndc{key.state.ndc_minus_one_to_one != 0}; | ||||||
|  |     const float point_size{Common::BitCast<float>(key.state.point_size)}; | ||||||
|  |     switch (stage) { | ||||||
|  |     case Shader::Stage::VertexB: | ||||||
|  |         if (!has_geometry) { | ||||||
|  |             if (key.state.topology == Maxwell::PrimitiveTopology::Points) { | ||||||
|  |                 info.fixed_state_point_size = point_size; | ||||||
|  |             } | ||||||
|  |             if (key.state.xfb_enabled != 0) { | ||||||
|  |                 info.xfb_varyings = MakeTransformFeedbackVaryings(key); | ||||||
|  |             } | ||||||
|  |             info.convert_depth_mode = gl_ndc; | ||||||
|  |         } | ||||||
|  |         std::ranges::transform(key.state.attributes, info.generic_input_types.begin(), | ||||||
|  |                                &CastAttributeType); | ||||||
|  |         break; | ||||||
|  |     case Shader::Stage::TessellationEval: | ||||||
|  |         // We have to flip tessellation clockwise for some reason... | ||||||
|  |         info.tess_clockwise = key.state.tessellation_clockwise == 0; | ||||||
|  |         info.tess_primitive = [&key] { | ||||||
|  |             const u32 raw{key.state.tessellation_primitive.Value()}; | ||||||
|  |             switch (static_cast<Maxwell::TessellationPrimitive>(raw)) { | ||||||
|  |             case Maxwell::TessellationPrimitive::Isolines: | ||||||
|  |                 return Shader::TessPrimitive::Isolines; | ||||||
|  |             case Maxwell::TessellationPrimitive::Triangles: | ||||||
|  |                 return Shader::TessPrimitive::Triangles; | ||||||
|  |             case Maxwell::TessellationPrimitive::Quads: | ||||||
|  |                 return Shader::TessPrimitive::Quads; | ||||||
|  |             } | ||||||
|  |             UNREACHABLE(); | ||||||
|  |             return Shader::TessPrimitive::Triangles; | ||||||
|  |         }(); | ||||||
|  |         info.tess_spacing = [&] { | ||||||
|  |             const u32 raw{key.state.tessellation_spacing}; | ||||||
|  |             switch (static_cast<Maxwell::TessellationSpacing>(raw)) { | ||||||
|  |             case Maxwell::TessellationSpacing::Equal: | ||||||
|  |                 return Shader::TessSpacing::Equal; | ||||||
|  |             case Maxwell::TessellationSpacing::FractionalOdd: | ||||||
|  |                 return Shader::TessSpacing::FractionalOdd; | ||||||
|  |             case Maxwell::TessellationSpacing::FractionalEven: | ||||||
|  |                 return Shader::TessSpacing::FractionalEven; | ||||||
|  |             } | ||||||
|  |             UNREACHABLE(); | ||||||
|  |             return Shader::TessSpacing::Equal; | ||||||
|  |         }(); | ||||||
|  |         break; | ||||||
|  |     case Shader::Stage::Geometry: | ||||||
|  |         if (program.output_topology == Shader::OutputTopology::PointList) { | ||||||
|  |             info.fixed_state_point_size = point_size; | ||||||
|  |         } | ||||||
|  |         if (key.state.xfb_enabled != 0) { | ||||||
|  |             info.xfb_varyings = MakeTransformFeedbackVaryings(key); | ||||||
|  |         } | ||||||
|  |         info.convert_depth_mode = gl_ndc; | ||||||
|  |         break; | ||||||
|  |     case Shader::Stage::Fragment: | ||||||
|  |         info.alpha_test_func = MaxwellToCompareFunction( | ||||||
|  |             key.state.UnpackComparisonOp(key.state.alpha_test_func.Value())); | ||||||
|  |         info.alpha_test_reference = Common::BitCast<float>(key.state.alpha_test_ref); | ||||||
|  |         break; | ||||||
|  |     default: | ||||||
|  |         break; | ||||||
|  |     } | ||||||
|  |     switch (key.state.topology) { | ||||||
|  |     case Maxwell::PrimitiveTopology::Points: | ||||||
|  |         info.input_topology = Shader::InputTopology::Points; | ||||||
|  |         break; | ||||||
|  |     case Maxwell::PrimitiveTopology::Lines: | ||||||
|  |     case Maxwell::PrimitiveTopology::LineLoop: | ||||||
|  |     case Maxwell::PrimitiveTopology::LineStrip: | ||||||
|  |         info.input_topology = Shader::InputTopology::Lines; | ||||||
|  |         break; | ||||||
|  |     case Maxwell::PrimitiveTopology::Triangles: | ||||||
|  |     case Maxwell::PrimitiveTopology::TriangleStrip: | ||||||
|  |     case Maxwell::PrimitiveTopology::TriangleFan: | ||||||
|  |     case Maxwell::PrimitiveTopology::Quads: | ||||||
|  |     case Maxwell::PrimitiveTopology::QuadStrip: | ||||||
|  |     case Maxwell::PrimitiveTopology::Polygon: | ||||||
|  |     case Maxwell::PrimitiveTopology::Patches: | ||||||
|  |         info.input_topology = Shader::InputTopology::Triangles; | ||||||
|  |         break; | ||||||
|  |     case Maxwell::PrimitiveTopology::LinesAdjacency: | ||||||
|  |     case Maxwell::PrimitiveTopology::LineStripAdjacency: | ||||||
|  |         info.input_topology = Shader::InputTopology::LinesAdjacency; | ||||||
|  |         break; | ||||||
|  |     case Maxwell::PrimitiveTopology::TrianglesAdjacency: | ||||||
|  |     case Maxwell::PrimitiveTopology::TriangleStripAdjacency: | ||||||
|  |         info.input_topology = Shader::InputTopology::TrianglesAdjacency; | ||||||
|  |         break; | ||||||
|  |     } | ||||||
|  |     info.force_early_z = key.state.early_z != 0; | ||||||
|  |     info.y_negate = key.state.y_negate != 0; | ||||||
|  |     return info; | ||||||
|  | } | ||||||
| } // Anonymous namespace | } // Anonymous namespace | ||||||
|  |  | ||||||
| size_t ComputePipelineCacheKey::Hash() const noexcept { | size_t ComputePipelineCacheKey::Hash() const noexcept { | ||||||
| @@ -124,7 +326,7 @@ PipelineCache::PipelineCache(RasterizerVulkan& rasterizer_, Tegra::Engines::Maxw | |||||||
|       serialization_thread(1, "yuzu:PipelineSerialization") { |       serialization_thread(1, "yuzu:PipelineSerialization") { | ||||||
|     const auto& float_control{device.FloatControlProperties()}; |     const auto& float_control{device.FloatControlProperties()}; | ||||||
|     const VkDriverIdKHR driver_id{device.GetDriverID()}; |     const VkDriverIdKHR driver_id{device.GetDriverID()}; | ||||||
|     base_profile = Shader::Profile{ |     profile = Shader::Profile{ | ||||||
|         .supported_spirv = device.IsKhrSpirv1_4Supported() ? 0x00010400U : 0x00010000U, |         .supported_spirv = device.IsKhrSpirv1_4Supported() ? 0x00010400U : 0x00010000U, | ||||||
|         .unified_descriptor_binding = true, |         .unified_descriptor_binding = true, | ||||||
|         .support_descriptor_aliasing = true, |         .support_descriptor_aliasing = true, | ||||||
| @@ -153,14 +355,10 @@ PipelineCache::PipelineCache(RasterizerVulkan& rasterizer_, Tegra::Engines::Maxw | |||||||
|         .support_viewport_mask = device.IsNvViewportArray2Supported(), |         .support_viewport_mask = device.IsNvViewportArray2Supported(), | ||||||
|         .support_typeless_image_loads = device.IsFormatlessImageLoadSupported(), |         .support_typeless_image_loads = device.IsFormatlessImageLoadSupported(), | ||||||
|         .support_demote_to_helper_invocation = true, |         .support_demote_to_helper_invocation = true, | ||||||
|         .warp_size_potentially_larger_than_guest = device.IsWarpSizePotentiallyBiggerThanGuest(), |  | ||||||
|         .support_int64_atomics = device.IsExtShaderAtomicInt64Supported(), |         .support_int64_atomics = device.IsExtShaderAtomicInt64Supported(), | ||||||
|  |         .warp_size_potentially_larger_than_guest = device.IsWarpSizePotentiallyBiggerThanGuest(), | ||||||
|         .has_broken_spirv_clamp = driver_id == VK_DRIVER_ID_INTEL_PROPRIETARY_WINDOWS_KHR, |         .has_broken_spirv_clamp = driver_id == VK_DRIVER_ID_INTEL_PROPRIETARY_WINDOWS_KHR, | ||||||
|         .has_broken_unsigned_image_offsets = false, |         .has_broken_unsigned_image_offsets = false, | ||||||
|         .generic_input_types{}, |  | ||||||
|         .fixed_state_point_size{}, |  | ||||||
|         .alpha_test_func{}, |  | ||||||
|         .xfb_varyings{}, |  | ||||||
|     }; |     }; | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -329,8 +527,8 @@ std::unique_ptr<GraphicsPipeline> PipelineCache::CreateGraphicsPipeline( | |||||||
|         const size_t stage_index{index - 1}; |         const size_t stage_index{index - 1}; | ||||||
|         infos[stage_index] = &program.info; |         infos[stage_index] = &program.info; | ||||||
|  |  | ||||||
|         const Shader::Profile profile{MakeProfile(key, program)}; |         const Shader::RuntimeInfo runtime_info{MakeRuntimeInfo(key, program)}; | ||||||
|         const std::vector<u32> code{EmitSPIRV(profile, program, binding)}; |         const std::vector<u32> code{EmitSPIRV(profile, runtime_info, program, binding)}; | ||||||
|         device.SaveShader(code); |         device.SaveShader(code); | ||||||
|         modules[stage_index] = BuildShader(device, code); |         modules[stage_index] = BuildShader(device, code); | ||||||
|         if (device.HasDebuggingToolAttached()) { |         if (device.HasDebuggingToolAttached()) { | ||||||
| @@ -391,7 +589,7 @@ std::unique_ptr<ComputePipeline> PipelineCache::CreateComputePipeline( | |||||||
|  |  | ||||||
|     Shader::Maxwell::Flow::CFG cfg{env, pools.flow_block, env.StartAddress()}; |     Shader::Maxwell::Flow::CFG cfg{env, pools.flow_block, env.StartAddress()}; | ||||||
|     Shader::IR::Program program{TranslateProgram(pools.inst, pools.block, env, cfg)}; |     Shader::IR::Program program{TranslateProgram(pools.inst, pools.block, env, cfg)}; | ||||||
|     const std::vector<u32> code{EmitSPIRV(base_profile, program)}; |     const std::vector<u32> code{EmitSPIRV(profile, program)}; | ||||||
|     device.SaveShader(code); |     device.SaveShader(code); | ||||||
|     vk::ShaderModule spv_module{BuildShader(device, code)}; |     vk::ShaderModule spv_module{BuildShader(device, code)}; | ||||||
|     if (device.HasDebuggingToolAttached()) { |     if (device.HasDebuggingToolAttached()) { | ||||||
| @@ -403,206 +601,4 @@ std::unique_ptr<ComputePipeline> PipelineCache::CreateComputePipeline( | |||||||
|                                              thread_worker, program.info, std::move(spv_module)); |                                              thread_worker, program.info, std::move(spv_module)); | ||||||
| } | } | ||||||
|  |  | ||||||
| static Shader::AttributeType CastAttributeType(const FixedPipelineState::VertexAttribute& attr) { |  | ||||||
|     if (attr.enabled == 0) { |  | ||||||
|         return Shader::AttributeType::Disabled; |  | ||||||
|     } |  | ||||||
|     switch (attr.Type()) { |  | ||||||
|     case Maxwell::VertexAttribute::Type::SignedNorm: |  | ||||||
|     case Maxwell::VertexAttribute::Type::UnsignedNorm: |  | ||||||
|     case Maxwell::VertexAttribute::Type::UnsignedScaled: |  | ||||||
|     case Maxwell::VertexAttribute::Type::SignedScaled: |  | ||||||
|     case Maxwell::VertexAttribute::Type::Float: |  | ||||||
|         return Shader::AttributeType::Float; |  | ||||||
|     case Maxwell::VertexAttribute::Type::SignedInt: |  | ||||||
|         return Shader::AttributeType::SignedInt; |  | ||||||
|     case Maxwell::VertexAttribute::Type::UnsignedInt: |  | ||||||
|         return Shader::AttributeType::UnsignedInt; |  | ||||||
|     } |  | ||||||
|     return Shader::AttributeType::Float; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static std::vector<Shader::TransformFeedbackVarying> MakeTransformFeedbackVaryings( |  | ||||||
|     const GraphicsPipelineCacheKey& key) { |  | ||||||
|     static constexpr std::array VECTORS{ |  | ||||||
|         28,  // gl_Position |  | ||||||
|         32,  // Generic 0 |  | ||||||
|         36,  // Generic 1 |  | ||||||
|         40,  // Generic 2 |  | ||||||
|         44,  // Generic 3 |  | ||||||
|         48,  // Generic 4 |  | ||||||
|         52,  // Generic 5 |  | ||||||
|         56,  // Generic 6 |  | ||||||
|         60,  // Generic 7 |  | ||||||
|         64,  // Generic 8 |  | ||||||
|         68,  // Generic 9 |  | ||||||
|         72,  // Generic 10 |  | ||||||
|         76,  // Generic 11 |  | ||||||
|         80,  // Generic 12 |  | ||||||
|         84,  // Generic 13 |  | ||||||
|         88,  // Generic 14 |  | ||||||
|         92,  // Generic 15 |  | ||||||
|         96,  // Generic 16 |  | ||||||
|         100, // Generic 17 |  | ||||||
|         104, // Generic 18 |  | ||||||
|         108, // Generic 19 |  | ||||||
|         112, // Generic 20 |  | ||||||
|         116, // Generic 21 |  | ||||||
|         120, // Generic 22 |  | ||||||
|         124, // Generic 23 |  | ||||||
|         128, // Generic 24 |  | ||||||
|         132, // Generic 25 |  | ||||||
|         136, // Generic 26 |  | ||||||
|         140, // Generic 27 |  | ||||||
|         144, // Generic 28 |  | ||||||
|         148, // Generic 29 |  | ||||||
|         152, // Generic 30 |  | ||||||
|         156, // Generic 31 |  | ||||||
|         160, // gl_FrontColor |  | ||||||
|         164, // gl_FrontSecondaryColor |  | ||||||
|         160, // gl_BackColor |  | ||||||
|         164, // gl_BackSecondaryColor |  | ||||||
|         192, // gl_TexCoord[0] |  | ||||||
|         196, // gl_TexCoord[1] |  | ||||||
|         200, // gl_TexCoord[2] |  | ||||||
|         204, // gl_TexCoord[3] |  | ||||||
|         208, // gl_TexCoord[4] |  | ||||||
|         212, // gl_TexCoord[5] |  | ||||||
|         216, // gl_TexCoord[6] |  | ||||||
|         220, // gl_TexCoord[7] |  | ||||||
|     }; |  | ||||||
|     std::vector<Shader::TransformFeedbackVarying> xfb(256); |  | ||||||
|     for (size_t buffer = 0; buffer < Maxwell::NumTransformFeedbackBuffers; ++buffer) { |  | ||||||
|         const auto& locations = key.state.xfb_state.varyings[buffer]; |  | ||||||
|         const auto& layout = key.state.xfb_state.layouts[buffer]; |  | ||||||
|         const u32 varying_count = layout.varying_count; |  | ||||||
|         u32 highest = 0; |  | ||||||
|         for (u32 offset = 0; offset < varying_count; ++offset) { |  | ||||||
|             const u32 base_offset = offset; |  | ||||||
|             const u8 location = locations[offset]; |  | ||||||
|  |  | ||||||
|             Shader::TransformFeedbackVarying varying; |  | ||||||
|             varying.buffer = layout.stream; |  | ||||||
|             varying.stride = layout.stride; |  | ||||||
|             varying.offset = offset * 4; |  | ||||||
|             varying.components = 1; |  | ||||||
|  |  | ||||||
|             if (std::ranges::find(VECTORS, Common::AlignDown(location, 4)) != VECTORS.end()) { |  | ||||||
|                 UNIMPLEMENTED_IF_MSG(location % 4 != 0, "Unaligned TFB"); |  | ||||||
|  |  | ||||||
|                 const u8 base_index = location / 4; |  | ||||||
|                 while (offset + 1 < varying_count && base_index == locations[offset + 1] / 4) { |  | ||||||
|                     ++offset; |  | ||||||
|                     ++varying.components; |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|             xfb[location] = varying; |  | ||||||
|             highest = std::max(highest, (base_offset + varying.components) * 4); |  | ||||||
|         } |  | ||||||
|         UNIMPLEMENTED_IF(highest != layout.stride); |  | ||||||
|     } |  | ||||||
|     return xfb; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| Shader::Profile PipelineCache::MakeProfile(const GraphicsPipelineCacheKey& key, |  | ||||||
|                                            const Shader::IR::Program& program) { |  | ||||||
|     Shader::Profile profile{base_profile}; |  | ||||||
|  |  | ||||||
|     const Shader::Stage stage{program.stage}; |  | ||||||
|     const bool has_geometry{key.unique_hashes[4] != 0}; |  | ||||||
|     const bool gl_ndc{key.state.ndc_minus_one_to_one != 0}; |  | ||||||
|     const float point_size{Common::BitCast<float>(key.state.point_size)}; |  | ||||||
|     switch (stage) { |  | ||||||
|     case Shader::Stage::VertexB: |  | ||||||
|         if (!has_geometry) { |  | ||||||
|             if (key.state.topology == Maxwell::PrimitiveTopology::Points) { |  | ||||||
|                 profile.fixed_state_point_size = point_size; |  | ||||||
|             } |  | ||||||
|             if (key.state.xfb_enabled != 0) { |  | ||||||
|                 profile.xfb_varyings = MakeTransformFeedbackVaryings(key); |  | ||||||
|             } |  | ||||||
|             profile.convert_depth_mode = gl_ndc; |  | ||||||
|         } |  | ||||||
|         std::ranges::transform(key.state.attributes, profile.generic_input_types.begin(), |  | ||||||
|                                &CastAttributeType); |  | ||||||
|         break; |  | ||||||
|     case Shader::Stage::TessellationEval: |  | ||||||
|         // We have to flip tessellation clockwise for some reason... |  | ||||||
|         profile.tess_clockwise = key.state.tessellation_clockwise == 0; |  | ||||||
|         profile.tess_primitive = [&key] { |  | ||||||
|             const u32 raw{key.state.tessellation_primitive.Value()}; |  | ||||||
|             switch (static_cast<Maxwell::TessellationPrimitive>(raw)) { |  | ||||||
|             case Maxwell::TessellationPrimitive::Isolines: |  | ||||||
|                 return Shader::TessPrimitive::Isolines; |  | ||||||
|             case Maxwell::TessellationPrimitive::Triangles: |  | ||||||
|                 return Shader::TessPrimitive::Triangles; |  | ||||||
|             case Maxwell::TessellationPrimitive::Quads: |  | ||||||
|                 return Shader::TessPrimitive::Quads; |  | ||||||
|             } |  | ||||||
|             UNREACHABLE(); |  | ||||||
|             return Shader::TessPrimitive::Triangles; |  | ||||||
|         }(); |  | ||||||
|         profile.tess_spacing = [&] { |  | ||||||
|             const u32 raw{key.state.tessellation_spacing}; |  | ||||||
|             switch (static_cast<Maxwell::TessellationSpacing>(raw)) { |  | ||||||
|             case Maxwell::TessellationSpacing::Equal: |  | ||||||
|                 return Shader::TessSpacing::Equal; |  | ||||||
|             case Maxwell::TessellationSpacing::FractionalOdd: |  | ||||||
|                 return Shader::TessSpacing::FractionalOdd; |  | ||||||
|             case Maxwell::TessellationSpacing::FractionalEven: |  | ||||||
|                 return Shader::TessSpacing::FractionalEven; |  | ||||||
|             } |  | ||||||
|             UNREACHABLE(); |  | ||||||
|             return Shader::TessSpacing::Equal; |  | ||||||
|         }(); |  | ||||||
|         break; |  | ||||||
|     case Shader::Stage::Geometry: |  | ||||||
|         if (program.output_topology == Shader::OutputTopology::PointList) { |  | ||||||
|             profile.fixed_state_point_size = point_size; |  | ||||||
|         } |  | ||||||
|         if (key.state.xfb_enabled != 0) { |  | ||||||
|             profile.xfb_varyings = MakeTransformFeedbackVaryings(key); |  | ||||||
|         } |  | ||||||
|         profile.convert_depth_mode = gl_ndc; |  | ||||||
|         break; |  | ||||||
|     case Shader::Stage::Fragment: |  | ||||||
|         profile.alpha_test_func = MaxwellToCompareFunction( |  | ||||||
|             key.state.UnpackComparisonOp(key.state.alpha_test_func.Value())); |  | ||||||
|         profile.alpha_test_reference = Common::BitCast<float>(key.state.alpha_test_ref); |  | ||||||
|         break; |  | ||||||
|     default: |  | ||||||
|         break; |  | ||||||
|     } |  | ||||||
|     switch (key.state.topology) { |  | ||||||
|     case Maxwell::PrimitiveTopology::Points: |  | ||||||
|         profile.input_topology = Shader::InputTopology::Points; |  | ||||||
|         break; |  | ||||||
|     case Maxwell::PrimitiveTopology::Lines: |  | ||||||
|     case Maxwell::PrimitiveTopology::LineLoop: |  | ||||||
|     case Maxwell::PrimitiveTopology::LineStrip: |  | ||||||
|         profile.input_topology = Shader::InputTopology::Lines; |  | ||||||
|         break; |  | ||||||
|     case Maxwell::PrimitiveTopology::Triangles: |  | ||||||
|     case Maxwell::PrimitiveTopology::TriangleStrip: |  | ||||||
|     case Maxwell::PrimitiveTopology::TriangleFan: |  | ||||||
|     case Maxwell::PrimitiveTopology::Quads: |  | ||||||
|     case Maxwell::PrimitiveTopology::QuadStrip: |  | ||||||
|     case Maxwell::PrimitiveTopology::Polygon: |  | ||||||
|     case Maxwell::PrimitiveTopology::Patches: |  | ||||||
|         profile.input_topology = Shader::InputTopology::Triangles; |  | ||||||
|         break; |  | ||||||
|     case Maxwell::PrimitiveTopology::LinesAdjacency: |  | ||||||
|     case Maxwell::PrimitiveTopology::LineStripAdjacency: |  | ||||||
|         profile.input_topology = Shader::InputTopology::LinesAdjacency; |  | ||||||
|         break; |  | ||||||
|     case Maxwell::PrimitiveTopology::TrianglesAdjacency: |  | ||||||
|     case Maxwell::PrimitiveTopology::TriangleStripAdjacency: |  | ||||||
|         profile.input_topology = Shader::InputTopology::TrianglesAdjacency; |  | ||||||
|         break; |  | ||||||
|     } |  | ||||||
|     profile.force_early_z = key.state.early_z != 0; |  | ||||||
|     profile.y_negate = key.state.y_negate != 0; |  | ||||||
|     return profile; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| } // namespace Vulkan | } // namespace Vulkan | ||||||
|   | |||||||
| @@ -129,9 +129,6 @@ private: | |||||||
|                                                            Shader::Environment& env, |                                                            Shader::Environment& env, | ||||||
|                                                            bool build_in_parallel); |                                                            bool build_in_parallel); | ||||||
|  |  | ||||||
|     Shader::Profile MakeProfile(const GraphicsPipelineCacheKey& key, |  | ||||||
|                                 const Shader::IR::Program& program); |  | ||||||
|  |  | ||||||
|     const Device& device; |     const Device& device; | ||||||
|     VKScheduler& scheduler; |     VKScheduler& scheduler; | ||||||
|     DescriptorPool& descriptor_pool; |     DescriptorPool& descriptor_pool; | ||||||
| @@ -148,7 +145,7 @@ private: | |||||||
|  |  | ||||||
|     ShaderPools main_pools; |     ShaderPools main_pools; | ||||||
|  |  | ||||||
|     Shader::Profile base_profile; |     Shader::Profile profile; | ||||||
|     std::filesystem::path pipeline_cache_filename; |     std::filesystem::path pipeline_cache_filename; | ||||||
|  |  | ||||||
|     Common::ThreadWorker workers; |     Common::ThreadWorker workers; | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user