shader: Better interpolation and disabled attributes support
This commit is contained in:
		| @@ -76,6 +76,8 @@ Id GetAttributeType(EmitContext& ctx, AttributeType type) { | ||||
|         return ctx.TypeVector(ctx.TypeInt(32, true), 4); | ||||
|     case AttributeType::UnsignedInt: | ||||
|         return ctx.U32[4]; | ||||
|     case AttributeType::Disabled: | ||||
|         break; | ||||
|     } | ||||
|     throw InvalidArgument("Invalid attribute type {}", type); | ||||
| } | ||||
| @@ -305,15 +307,36 @@ void EmitContext::DefineInputs(const Info& info) { | ||||
|     if (info.loads_front_face) { | ||||
|         front_face = DefineInput(*this, U1, spv::BuiltIn::FrontFacing); | ||||
|     } | ||||
|     for (size_t index = 0; index < info.loads_generics.size(); ++index) { | ||||
|         if (!info.loads_generics[index]) { | ||||
|     for (size_t index = 0; index < info.input_generics.size(); ++index) { | ||||
|         const InputVarying generic{info.input_generics[index]}; | ||||
|         if (!generic.used) { | ||||
|             continue; | ||||
|         } | ||||
|         const Id type{GetAttributeType(*this, profile.generic_input_types[index])}; | ||||
|         const AttributeType input_type{profile.generic_input_types[index]}; | ||||
|         if (input_type == AttributeType::Disabled) { | ||||
|             continue; | ||||
|         } | ||||
|         const Id type{GetAttributeType(*this, input_type)}; | ||||
|         const Id id{DefineInput(*this, type)}; | ||||
|         Decorate(id, spv::Decoration::Location, static_cast<u32>(index)); | ||||
|         Name(id, fmt::format("in_attr{}", index)); | ||||
|         input_generics[index] = id; | ||||
|  | ||||
|         if (stage != Stage::Fragment) { | ||||
|             continue; | ||||
|         } | ||||
|         switch (generic.interpolation) { | ||||
|         case Interpolation::Smooth: | ||||
|             // Default | ||||
|             // Decorate(id, spv::Decoration::Smooth); | ||||
|             break; | ||||
|         case Interpolation::NoPerspective: | ||||
|             Decorate(id, spv::Decoration::NoPerspective); | ||||
|             break; | ||||
|         case Interpolation::Flat: | ||||
|             Decorate(id, spv::Decoration::Flat); | ||||
|             break; | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -10,16 +10,23 @@ | ||||
|  | ||||
| namespace Shader::Backend::SPIRV { | ||||
| namespace { | ||||
| std::tuple<Id, Id, bool> AttrTypes(EmitContext& ctx, u32 index) { | ||||
|     const bool is_first_reader{ctx.stage == Stage::VertexB}; | ||||
| struct AttrInfo { | ||||
|     Id pointer; | ||||
|     Id id; | ||||
|     bool needs_cast; | ||||
| }; | ||||
|  | ||||
| std::optional<AttrInfo> AttrTypes(EmitContext& ctx, u32 index) { | ||||
|     const AttributeType type{ctx.profile.generic_input_types.at(index)}; | ||||
|     switch (type) { | ||||
|     case AttributeType::Float: | ||||
|         return {ctx.input_f32, ctx.F32[1], false}; | ||||
|         return AttrInfo{ctx.input_f32, ctx.F32[1], false}; | ||||
|     case AttributeType::UnsignedInt: | ||||
|         return {ctx.input_u32, ctx.U32[1], true}; | ||||
|         return AttrInfo{ctx.input_u32, ctx.U32[1], true}; | ||||
|     case AttributeType::SignedInt: | ||||
|         return {ctx.input_s32, ctx.TypeInt(32, true), true}; | ||||
|         return AttrInfo{ctx.input_s32, ctx.TypeInt(32, true), true}; | ||||
|     case AttributeType::Disabled: | ||||
|         return std::nullopt; | ||||
|     } | ||||
|     throw InvalidArgument("Invalid attribute type {}", type); | ||||
| } | ||||
| @@ -129,11 +136,15 @@ Id EmitGetAttribute(EmitContext& ctx, IR::Attribute attr) { | ||||
|     const auto element_id{[&] { return ctx.Constant(ctx.U32[1], element); }}; | ||||
|     if (IR::IsGeneric(attr)) { | ||||
|         const u32 index{IR::GenericAttributeIndex(attr)}; | ||||
|         const auto [pointer_type, type, needs_cast]{AttrTypes(ctx, index)}; | ||||
|         const std::optional<AttrInfo> type{AttrTypes(ctx, index)}; | ||||
|         if (!type) { | ||||
|             // Attribute is disabled | ||||
|             return ctx.Constant(ctx.F32[1], 0.0f); | ||||
|         } | ||||
|         const Id generic_id{ctx.input_generics.at(index)}; | ||||
|         const Id pointer{ctx.OpAccessChain(pointer_type, generic_id, element_id())}; | ||||
|         const Id value{ctx.OpLoad(type, pointer)}; | ||||
|         return needs_cast ? ctx.OpBitcast(ctx.F32[1], value) : value; | ||||
|         const Id pointer{ctx.OpAccessChain(type->pointer, generic_id, element_id())}; | ||||
|         const Id value{ctx.OpLoad(type->id, pointer)}; | ||||
|         return type->needs_cast ? ctx.OpBitcast(ctx.F32[1], value) : value; | ||||
|     } | ||||
|     switch (attr) { | ||||
|     case IR::Attribute::PositionX: | ||||
|   | ||||
| @@ -27,6 +27,40 @@ static void RemoveUnreachableBlocks(IR::Program& program) { | ||||
|     }); | ||||
| } | ||||
|  | ||||
| static void CollectInterpolationInfo(Environment& env, IR::Program& program) { | ||||
|     if (program.stage != Stage::Fragment) { | ||||
|         return; | ||||
|     } | ||||
|     const ProgramHeader& sph{env.SPH()}; | ||||
|     for (size_t index = 0; index < program.info.input_generics.size(); ++index) { | ||||
|         std::optional<PixelImap> imap; | ||||
|         for (const PixelImap value : sph.ps.GenericInputMap(static_cast<u32>(index))) { | ||||
|             if (value == PixelImap::Unused) { | ||||
|                 continue; | ||||
|             } | ||||
|             if (imap && imap != value) { | ||||
|                 throw NotImplementedException("Per component interpolation"); | ||||
|             } | ||||
|             imap = value; | ||||
|         } | ||||
|         if (!imap) { | ||||
|             continue; | ||||
|         } | ||||
|         program.info.input_generics[index].interpolation = [&] { | ||||
|             switch (*imap) { | ||||
|             case PixelImap::Unused: | ||||
|             case PixelImap::Perspective: | ||||
|                 return Interpolation::Smooth; | ||||
|             case PixelImap::Constant: | ||||
|                 return Interpolation::Flat; | ||||
|             case PixelImap::ScreenLinear: | ||||
|                 return Interpolation::NoPerspective; | ||||
|             } | ||||
|             throw NotImplementedException("Unknown interpolation {}", *imap); | ||||
|         }(); | ||||
|     } | ||||
| } | ||||
|  | ||||
| IR::Program TranslateProgram(ObjectPool<IR::Inst>& inst_pool, ObjectPool<IR::Block>& block_pool, | ||||
|                              Environment& env, Flow::CFG& cfg) { | ||||
|     IR::Program program; | ||||
| @@ -51,6 +85,7 @@ IR::Program TranslateProgram(ObjectPool<IR::Inst>& inst_pool, ObjectPool<IR::Blo | ||||
|     Optimization::IdentityRemovalPass(program); | ||||
|     Optimization::VerificationPass(program); | ||||
|     Optimization::CollectShaderInfoPass(program); | ||||
|     CollectInterpolationInfo(env, program); | ||||
|     return program; | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -151,16 +151,8 @@ void TranslatorVisitor::IPA(u64 insn) { | ||||
|             value = ir.FPMul(value, position_w); | ||||
|         } | ||||
|     } | ||||
|     switch (ipa.interpolation_mode) { | ||||
|     case InterpolationMode::Pass: | ||||
|         break; | ||||
|     case InterpolationMode::Multiply: | ||||
|     if (ipa.interpolation_mode == InterpolationMode::Multiply) { | ||||
|         value = ir.FPMul(value, F(ipa.multiplier)); | ||||
|         break; | ||||
|     case InterpolationMode::Constant: | ||||
|         throw NotImplementedException("IPA.CONSTANT"); | ||||
|     case InterpolationMode::Sc: | ||||
|         throw NotImplementedException("IPA.SC"); | ||||
|     } | ||||
|  | ||||
|     // Saturated IPAs are generally generated out of clamped varyings. | ||||
|   | ||||
| @@ -28,7 +28,7 @@ void AddConstantBufferDescriptor(Info& info, u32 index, u32 count) { | ||||
|  | ||||
| void GetAttribute(Info& info, IR::Attribute attribute) { | ||||
|     if (IR::IsGeneric(attribute)) { | ||||
|         info.loads_generics.at(IR::GenericAttributeIndex(attribute)) = true; | ||||
|         info.input_generics.at(IR::GenericAttributeIndex(attribute)).used = true; | ||||
|         return; | ||||
|     } | ||||
|     switch (attribute) { | ||||
|   | ||||
| @@ -14,6 +14,7 @@ enum class AttributeType : u8 { | ||||
|     Float, | ||||
|     SignedInt, | ||||
|     UnsignedInt, | ||||
|     Disabled, | ||||
| }; | ||||
|  | ||||
| struct Profile { | ||||
|   | ||||
| @@ -31,6 +31,17 @@ enum class TextureType : u32 { | ||||
|     ShadowArrayCube, | ||||
| }; | ||||
|  | ||||
| enum class Interpolation { | ||||
|     Smooth, | ||||
|     Flat, | ||||
|     NoPerspective, | ||||
| }; | ||||
|  | ||||
| struct InputVarying { | ||||
|     Interpolation interpolation{Interpolation::Smooth}; | ||||
|     bool used{false}; | ||||
| }; | ||||
|  | ||||
| struct TextureDescriptor { | ||||
|     TextureType type; | ||||
|     u32 cbuf_index; | ||||
| @@ -58,7 +69,7 @@ struct Info { | ||||
|     bool uses_local_invocation_id{}; | ||||
|     bool uses_subgroup_invocation_id{}; | ||||
|  | ||||
|     std::array<bool, 32> loads_generics{}; | ||||
|     std::array<InputVarying, 32> input_generics{}; | ||||
|     bool loads_position{}; | ||||
|     bool loads_instance_id{}; | ||||
|     bool loads_vertex_id{}; | ||||
|   | ||||
| @@ -221,10 +221,10 @@ void GraphicsPipeline::MakePipeline(const Device& device, const FixedPipelineSta | ||||
|         } | ||||
|     } | ||||
|     static_vector<VkVertexInputAttributeDescription, 32> vertex_attributes; | ||||
|     const auto& input_attributes = stage_infos[0].loads_generics; | ||||
|     const auto& input_attributes = stage_infos[0].input_generics; | ||||
|     for (size_t index = 0; index < state.attributes.size(); ++index) { | ||||
|         const auto& attribute = state.attributes[index]; | ||||
|         if (!attribute.enabled || !input_attributes[index]) { | ||||
|         if (!attribute.enabled || !input_attributes[index].used) { | ||||
|             continue; | ||||
|         } | ||||
|         vertex_attributes.push_back({ | ||||
|   | ||||
| @@ -755,6 +755,9 @@ ComputePipeline PipelineCache::CreateComputePipeline(ShaderPools& pools, | ||||
| } | ||||
|  | ||||
| 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: | ||||
|   | ||||
		Reference in New Issue
	
	Block a user