shader: Implement NDC [-1, 1], attribute types and default varying initialization
This commit is contained in:
		| @@ -67,6 +67,18 @@ Id DefineInput(EmitContext& ctx, Id type, std::optional<spv::BuiltIn> builtin = | ||||
| Id DefineOutput(EmitContext& ctx, Id type, std::optional<spv::BuiltIn> builtin = std::nullopt) { | ||||
|     return DefineVariable(ctx, type, builtin, spv::StorageClass::Output); | ||||
| } | ||||
|  | ||||
| Id GetAttributeType(EmitContext& ctx, AttributeType type) { | ||||
|     switch (type) { | ||||
|     case AttributeType::Float: | ||||
|         return ctx.F32[4]; | ||||
|     case AttributeType::SignedInt: | ||||
|         return ctx.TypeVector(ctx.TypeInt(32, true), 4); | ||||
|     case AttributeType::UnsignedInt: | ||||
|         return ctx.U32[4]; | ||||
|     } | ||||
|     throw InvalidArgument("Invalid attribute type {}", type); | ||||
| } | ||||
| } // Anonymous namespace | ||||
|  | ||||
| void VectorTypes::Define(Sirit::Module& sirit_ctx, Id base_type, std::string_view name) { | ||||
| @@ -82,11 +94,11 @@ void VectorTypes::Define(Sirit::Module& sirit_ctx, Id base_type, std::string_vie | ||||
| } | ||||
|  | ||||
| EmitContext::EmitContext(const Profile& profile_, IR::Program& program, u32& binding) | ||||
|     : Sirit::Module(0x00010000), profile{profile_} { | ||||
|     : Sirit::Module(0x00010000), profile{profile_}, stage{program.stage} { | ||||
|     AddCapability(spv::Capability::Shader); | ||||
|     DefineCommonTypes(program.info); | ||||
|     DefineCommonConstants(); | ||||
|     DefineInterfaces(program.info, program.stage); | ||||
|     DefineInterfaces(program.info); | ||||
|     DefineConstantBuffers(program.info, binding); | ||||
|     DefineStorageBuffers(program.info, binding); | ||||
|     DefineTextures(program.info, binding); | ||||
| @@ -130,6 +142,9 @@ void EmitContext::DefineCommonTypes(const Info& info) { | ||||
|     U32.Define(*this, TypeInt(32, false), "u32"); | ||||
|  | ||||
|     input_f32 = Name(TypePointer(spv::StorageClass::Input, F32[1]), "input_f32"); | ||||
|     input_u32 = Name(TypePointer(spv::StorageClass::Input, U32[1]), "input_u32"); | ||||
|     input_s32 = Name(TypePointer(spv::StorageClass::Input, TypeInt(32, true)), "input_s32"); | ||||
|  | ||||
|     output_f32 = Name(TypePointer(spv::StorageClass::Output, F32[1]), "output_f32"); | ||||
|  | ||||
|     if (info.uses_int8) { | ||||
| @@ -162,9 +177,9 @@ void EmitContext::DefineCommonConstants() { | ||||
|     u32_zero_value = Constant(U32[1], 0U); | ||||
| } | ||||
|  | ||||
| void EmitContext::DefineInterfaces(const Info& info, Stage stage) { | ||||
|     DefineInputs(info, stage); | ||||
|     DefineOutputs(info, stage); | ||||
| void EmitContext::DefineInterfaces(const Info& info) { | ||||
|     DefineInputs(info); | ||||
|     DefineOutputs(info); | ||||
| } | ||||
|  | ||||
| void EmitContext::DefineConstantBuffers(const Info& info, u32& binding) { | ||||
| @@ -252,7 +267,7 @@ void EmitContext::DefineLabels(IR::Program& program) { | ||||
|     } | ||||
| } | ||||
|  | ||||
| void EmitContext::DefineInputs(const Info& info, Stage stage) { | ||||
| void EmitContext::DefineInputs(const Info& info) { | ||||
|     if (info.uses_workgroup_id) { | ||||
|         workgroup_id = DefineInput(*this, U32[3], spv::BuiltIn::WorkgroupId); | ||||
|     } | ||||
| @@ -288,8 +303,8 @@ void EmitContext::DefineInputs(const Info& info, Stage stage) { | ||||
|         if (!info.loads_generics[index]) { | ||||
|             continue; | ||||
|         } | ||||
|         // FIXME: Declare size from input | ||||
|         const Id id{DefineInput(*this, F32[4])}; | ||||
|         const Id type{GetAttributeType(*this, profile.generic_input_types[index])}; | ||||
|         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; | ||||
| @@ -323,8 +338,8 @@ void EmitContext::DefineConstantBuffers(const Info& info, Id UniformDefinitions: | ||||
|     } | ||||
| } | ||||
|  | ||||
| void EmitContext::DefineOutputs(const Info& info, Stage stage) { | ||||
|     if (info.stores_position) { | ||||
| void EmitContext::DefineOutputs(const Info& info) { | ||||
|     if (info.stores_position || stage == Stage::VertexB) { | ||||
|         output_position = DefineOutput(*this, F32[4], spv::BuiltIn::Position); | ||||
|     } | ||||
|     for (size_t i = 0; i < info.stores_generics.size(); ++i) { | ||||
|   | ||||
| @@ -52,6 +52,7 @@ public: | ||||
|     [[nodiscard]] Id Def(const IR::Value& value); | ||||
|  | ||||
|     const Profile& profile; | ||||
|     Stage stage{}; | ||||
|  | ||||
|     Id void_id{}; | ||||
|     Id U1{}; | ||||
| @@ -72,6 +73,9 @@ public: | ||||
|     UniformDefinitions uniform_types; | ||||
|  | ||||
|     Id input_f32{}; | ||||
|     Id input_u32{}; | ||||
|     Id input_s32{}; | ||||
|  | ||||
|     Id output_f32{}; | ||||
|  | ||||
|     Id storage_u32{}; | ||||
| @@ -104,7 +108,7 @@ public: | ||||
| private: | ||||
|     void DefineCommonTypes(const Info& info); | ||||
|     void DefineCommonConstants(); | ||||
|     void DefineInterfaces(const Info& info, Stage stage); | ||||
|     void DefineInterfaces(const Info& info); | ||||
|     void DefineConstantBuffers(const Info& info, u32& binding); | ||||
|     void DefineStorageBuffers(const Info& info, u32& binding); | ||||
|     void DefineTextures(const Info& info, u32& binding); | ||||
| @@ -113,8 +117,8 @@ private: | ||||
|     void DefineConstantBuffers(const Info& info, Id UniformDefinitions::*member_type, u32 binding, | ||||
|                                Id type, char type_char, u32 element_size); | ||||
|  | ||||
|     void DefineInputs(const Info& info, Stage stage); | ||||
|     void DefineOutputs(const Info& info, Stage stage); | ||||
|     void DefineInputs(const Info& info); | ||||
|     void DefineOutputs(const Info& info); | ||||
| }; | ||||
|  | ||||
| } // namespace Shader::Backend::SPIRV | ||||
|   | ||||
| @@ -28,6 +28,8 @@ void EmitLoopMerge(EmitContext& ctx, Id merge_label, Id continue_label); | ||||
| void EmitSelectionMerge(EmitContext& ctx, Id merge_label); | ||||
| void EmitReturn(EmitContext& ctx); | ||||
| void EmitDemoteToHelperInvocation(EmitContext& ctx, Id continue_label); | ||||
| void EmitPrologue(EmitContext& ctx); | ||||
| void EmitEpilogue(EmitContext& ctx); | ||||
| void EmitGetRegister(EmitContext& ctx); | ||||
| void EmitSetRegister(EmitContext& ctx); | ||||
| void EmitGetPred(EmitContext& ctx); | ||||
|   | ||||
| @@ -2,30 +2,26 @@ | ||||
| // Licensed under GPLv2 or any later version | ||||
| // Refer to the license.txt file included. | ||||
|  | ||||
| #include <tuple> | ||||
|  | ||||
| #include "shader_recompiler/backend/spirv/emit_spirv.h" | ||||
|  | ||||
| #pragma optimize("", off) | ||||
|  | ||||
| namespace Shader::Backend::SPIRV { | ||||
| namespace { | ||||
| Id InputAttrPointer(EmitContext& ctx, IR::Attribute attr) { | ||||
|     const u32 element{static_cast<u32>(attr) % 4}; | ||||
|     const auto element_id{[&] { return ctx.Constant(ctx.U32[1], element); }}; | ||||
|     if (IR::IsGeneric(attr)) { | ||||
|         const u32 index{IR::GenericAttributeIndex(attr)}; | ||||
|         return ctx.OpAccessChain(ctx.input_f32, ctx.input_generics.at(index), element_id()); | ||||
|     } | ||||
|     switch (attr) { | ||||
|     case IR::Attribute::PositionX: | ||||
|     case IR::Attribute::PositionY: | ||||
|     case IR::Attribute::PositionZ: | ||||
|     case IR::Attribute::PositionW: | ||||
|         return ctx.OpAccessChain(ctx.input_f32, ctx.input_position, element_id()); | ||||
|     case IR::Attribute::InstanceId: | ||||
|         return ctx.OpLoad(ctx.U32[1], ctx.instance_id); | ||||
|     case IR::Attribute::VertexId: | ||||
|         return ctx.OpLoad(ctx.U32[1], ctx.vertex_id); | ||||
|     default: | ||||
|         throw NotImplementedException("Read attribute {}", attr); | ||||
| std::tuple<Id, Id, bool> AttrTypes(EmitContext& ctx, u32 index) { | ||||
|     const bool is_first_reader{ctx.stage == Stage::VertexB}; | ||||
|     const AttributeType type{ctx.profile.generic_input_types.at(index)}; | ||||
|     switch (type) { | ||||
|     case AttributeType::Float: | ||||
|         return {ctx.input_f32, ctx.F32[1], false}; | ||||
|     case AttributeType::UnsignedInt: | ||||
|         return {ctx.input_u32, ctx.U32[1], true}; | ||||
|     case AttributeType::SignedInt: | ||||
|         return {ctx.input_s32, ctx.TypeInt(32, true), true}; | ||||
|     } | ||||
|     throw InvalidArgument("Invalid attribute type {}", type); | ||||
| } | ||||
|  | ||||
| Id OutputAttrPointer(EmitContext& ctx, IR::Attribute attr) { | ||||
| @@ -129,19 +125,40 @@ Id EmitGetCbufU64(EmitContext& ctx, const IR::Value& binding, const IR::Value& o | ||||
| } | ||||
|  | ||||
| Id EmitGetAttribute(EmitContext& ctx, IR::Attribute attr) { | ||||
|     if (!ctx.profile.support_vertex_instance_id) { | ||||
|         switch (attr) { | ||||
|         case IR::Attribute::InstanceId: | ||||
|     const u32 element{static_cast<u32>(attr) % 4}; | ||||
|     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 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; | ||||
|     } | ||||
|     switch (attr) { | ||||
|     case IR::Attribute::PositionX: | ||||
|     case IR::Attribute::PositionY: | ||||
|     case IR::Attribute::PositionZ: | ||||
|     case IR::Attribute::PositionW: | ||||
|         return ctx.OpLoad(ctx.F32[1], | ||||
|                           ctx.OpAccessChain(ctx.input_f32, ctx.input_position, element_id())); | ||||
|     case IR::Attribute::InstanceId: | ||||
|         if (ctx.profile.support_vertex_instance_id) { | ||||
|             return ctx.OpLoad(ctx.U32[1], ctx.instance_id); | ||||
|         } else { | ||||
|             return ctx.OpISub(ctx.U32[1], ctx.OpLoad(ctx.U32[1], ctx.instance_index), | ||||
|                               ctx.OpLoad(ctx.U32[1], ctx.base_instance)); | ||||
|         case IR::Attribute::VertexId: | ||||
|         } | ||||
|     case IR::Attribute::VertexId: | ||||
|         if (ctx.profile.support_vertex_instance_id) { | ||||
|             return ctx.OpLoad(ctx.U32[1], ctx.vertex_id); | ||||
|         } else { | ||||
|             return ctx.OpISub(ctx.U32[1], ctx.OpLoad(ctx.U32[1], ctx.vertex_index), | ||||
|                               ctx.OpLoad(ctx.U32[1], ctx.base_vertex)); | ||||
|         default: | ||||
|             break; | ||||
|         } | ||||
|     default: | ||||
|         throw NotImplementedException("Read attribute {}", attr); | ||||
|     } | ||||
|     return ctx.OpLoad(ctx.F32[1], InputAttrPointer(ctx, attr)); | ||||
| } | ||||
|  | ||||
| void EmitSetAttribute(EmitContext& ctx, IR::Attribute attr, Id value) { | ||||
|   | ||||
							
								
								
									
										35
									
								
								src/shader_recompiler/backend/spirv/emit_spirv_special.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								src/shader_recompiler/backend/spirv/emit_spirv_special.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,35 @@ | ||||
| // Copyright 2021 yuzu Emulator Project | ||||
| // Licensed under GPLv2 or any later version | ||||
| // Refer to the license.txt file included. | ||||
|  | ||||
| #include "shader_recompiler/backend/spirv/emit_spirv.h" | ||||
|  | ||||
| namespace Shader::Backend::SPIRV { | ||||
|  | ||||
| void EmitPrologue(EmitContext& ctx) { | ||||
|     if (ctx.stage == Stage::VertexB) { | ||||
|         const Id zero{ctx.Constant(ctx.F32[1], 0.0f)}; | ||||
|         const Id one{ctx.Constant(ctx.F32[1], 1.0f)}; | ||||
|         const Id null_vector{ctx.ConstantComposite(ctx.F32[4], zero, zero, zero, zero)}; | ||||
|         ctx.OpStore(ctx.output_position, ctx.ConstantComposite(ctx.F32[4], zero, zero, zero, one)); | ||||
|         for (const Id generic_id : ctx.output_generics) { | ||||
|             if (Sirit::ValidId(generic_id)) { | ||||
|                 ctx.OpStore(generic_id, null_vector); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| void EmitEpilogue(EmitContext& ctx) { | ||||
|     if (ctx.profile.convert_depth_mode) { | ||||
|         const Id type{ctx.F32[1]}; | ||||
|         const Id position{ctx.OpLoad(ctx.F32[4], ctx.output_position)}; | ||||
|         const Id z{ctx.OpCompositeExtract(type, position, 2u)}; | ||||
|         const Id w{ctx.OpCompositeExtract(type, position, 3u)}; | ||||
|         const Id screen_depth{ctx.OpFMul(type, ctx.OpFAdd(type, z, w), ctx.Constant(type, 0.5f))}; | ||||
|         const Id vector{ctx.OpCompositeInsert(ctx.F32[4], screen_depth, position, 2u)}; | ||||
|         ctx.OpStore(ctx.output_position, vector); | ||||
|     } | ||||
| } | ||||
|  | ||||
| } // namespace Shader::Backend::SPIRV | ||||
		Reference in New Issue
	
	Block a user