shader: Implement transform feedbacks and define file format
This commit is contained in:
		| @@ -135,6 +135,45 @@ Id DefineOutput(EmitContext& ctx, Id type, std::optional<spv::BuiltIn> builtin = | ||||
|     return DefineVariable(ctx, type, builtin, spv::StorageClass::Output); | ||||
| } | ||||
|  | ||||
| void DefineGenericOutput(EmitContext& ctx, size_t index) { | ||||
|     static constexpr std::string_view swizzle{"xyzw"}; | ||||
|     const size_t base_attr_index{static_cast<size_t>(IR::Attribute::Generic0X) + index * 4}; | ||||
|     u32 element{0}; | ||||
|     while (element < 4) { | ||||
|         const u32 remainder{4 - element}; | ||||
|         const TransformFeedbackVarying* xfb_varying{}; | ||||
|         if (!ctx.profile.xfb_varyings.empty()) { | ||||
|             xfb_varying = &ctx.profile.xfb_varyings[base_attr_index + element]; | ||||
|             xfb_varying = xfb_varying && xfb_varying->components > 0 ? xfb_varying : nullptr; | ||||
|         } | ||||
|         const u32 num_components{xfb_varying ? xfb_varying->components : remainder}; | ||||
|  | ||||
|         const Id id{DefineOutput(ctx, ctx.F32[num_components])}; | ||||
|         ctx.Decorate(id, spv::Decoration::Location, static_cast<u32>(index)); | ||||
|         if (element > 0) { | ||||
|             ctx.Decorate(id, spv::Decoration::Component, element); | ||||
|         } | ||||
|         if (xfb_varying) { | ||||
|             ctx.Decorate(id, spv::Decoration::XfbBuffer, xfb_varying->buffer); | ||||
|             ctx.Decorate(id, spv::Decoration::XfbStride, xfb_varying->stride); | ||||
|             ctx.Decorate(id, spv::Decoration::Offset, xfb_varying->offset); | ||||
|         } | ||||
|         if (num_components < 4 || element > 0) { | ||||
|             ctx.Name(id, fmt::format("out_attr{}", index)); | ||||
|         } else { | ||||
|             const std::string_view subswizzle{swizzle.substr(element, num_components)}; | ||||
|             ctx.Name(id, fmt::format("out_attr{}_{}", index, subswizzle)); | ||||
|         } | ||||
|         const GenericElementInfo info{ | ||||
|             .id = id, | ||||
|             .first_element = element, | ||||
|             .num_components = num_components, | ||||
|         }; | ||||
|         std::fill_n(ctx.output_generics[index].begin(), num_components, info); | ||||
|         element += num_components; | ||||
|     } | ||||
| } | ||||
|  | ||||
| Id GetAttributeType(EmitContext& ctx, AttributeType type) { | ||||
|     switch (type) { | ||||
|     case AttributeType::Float: | ||||
| @@ -663,12 +702,15 @@ void EmitContext::DefineAttributeMemAccess(const Info& info) { | ||||
|             OpReturn(); | ||||
|             ++label_index; | ||||
|         } | ||||
|         for (size_t i = 0; i < info.stores_generics.size(); i++) { | ||||
|         for (size_t i = 0; i < info.stores_generics.size(); ++i) { | ||||
|             if (!info.stores_generics[i]) { | ||||
|                 continue; | ||||
|             } | ||||
|             if (output_generics[i][0].num_components != 4) { | ||||
|                 throw NotImplementedException("Physical stores and transform feedbacks"); | ||||
|             } | ||||
|             AddLabel(labels[label_index]); | ||||
|             const Id generic_id{output_generics.at(i)}; | ||||
|             const Id generic_id{output_generics[i][0].id}; | ||||
|             const Id pointer{OpAccessChain(output_f32, generic_id, masked_index)}; | ||||
|             OpStore(pointer, store_value); | ||||
|             OpReturn(); | ||||
| @@ -1015,11 +1057,9 @@ void EmitContext::DefineOutputs(const Info& info) { | ||||
|         } | ||||
|         viewport_index = DefineOutput(*this, U32[1], spv::BuiltIn::ViewportIndex); | ||||
|     } | ||||
|     for (size_t i = 0; i < info.stores_generics.size(); ++i) { | ||||
|         if (info.stores_generics[i]) { | ||||
|             output_generics[i] = DefineOutput(*this, F32[4]); | ||||
|             Decorate(output_generics[i], spv::Decoration::Location, static_cast<u32>(i)); | ||||
|             Name(output_generics[i], fmt::format("out_attr{}", i)); | ||||
|     for (size_t index = 0; index < info.stores_generics.size(); ++index) { | ||||
|         if (info.stores_generics[index]) { | ||||
|             DefineGenericOutput(*this, index); | ||||
|         } | ||||
|     } | ||||
|     if (stage == Stage::Fragment) { | ||||
|   | ||||
| @@ -79,6 +79,12 @@ struct StorageDefinitions { | ||||
|     Id U32x4{}; | ||||
| }; | ||||
|  | ||||
| struct GenericElementInfo { | ||||
|     Id id{}; | ||||
|     u32 first_element{}; | ||||
|     u32 num_components{}; | ||||
| }; | ||||
|  | ||||
| class EmitContext final : public Sirit::Module { | ||||
| public: | ||||
|     explicit EmitContext(const Profile& profile, IR::Program& program, u32& binding); | ||||
| @@ -189,7 +195,7 @@ public: | ||||
|  | ||||
|     Id output_point_size{}; | ||||
|     Id output_position{}; | ||||
|     std::array<Id, 32> output_generics{}; | ||||
|     std::array<std::array<GenericElementInfo, 4>, 32> output_generics{}; | ||||
|  | ||||
|     std::array<Id, 8> frag_color{}; | ||||
|     Id frag_depth{}; | ||||
|   | ||||
| @@ -288,6 +288,9 @@ void SetupCapabilities(const Profile& profile, const Info& info, EmitContext& ct | ||||
|     if (info.uses_typeless_image_writes) { | ||||
|         ctx.AddCapability(spv::Capability::StorageImageWriteWithoutFormat); | ||||
|     } | ||||
|     if (!ctx.profile.xfb_varyings.empty()) { | ||||
|         ctx.AddCapability(spv::Capability::TransformFeedback); | ||||
|     } | ||||
|     // TODO: Track this usage | ||||
|     ctx.AddCapability(spv::Capability::ImageGatherExtended); | ||||
|     ctx.AddCapability(spv::Capability::ImageQuery); | ||||
|   | ||||
| @@ -40,11 +40,17 @@ Id AttrPointer(EmitContext& ctx, Id pointer_type, Id vertex, Id base, Args&&... | ||||
| } | ||||
|  | ||||
| std::optional<Id> OutputAttrPointer(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.output_f32, ctx.output_generics.at(index), element_id()); | ||||
|         const u32 element{IR::GenericAttributeElement(attr)}; | ||||
|         const GenericElementInfo& info{ctx.output_generics.at(index).at(element)}; | ||||
|         if (info.num_components == 1) { | ||||
|             return info.id; | ||||
|         } else { | ||||
|             const u32 index_element{element - info.first_element}; | ||||
|             const Id index_id{ctx.Constant(ctx.U32[1], index_element)}; | ||||
|             return ctx.OpAccessChain(ctx.output_f32, info.id, index_id); | ||||
|         } | ||||
|     } | ||||
|     switch (attr) { | ||||
|     case IR::Attribute::PointSize: | ||||
| @@ -52,8 +58,11 @@ std::optional<Id> OutputAttrPointer(EmitContext& ctx, IR::Attribute attr) { | ||||
|     case IR::Attribute::PositionX: | ||||
|     case IR::Attribute::PositionY: | ||||
|     case IR::Attribute::PositionZ: | ||||
|     case IR::Attribute::PositionW: | ||||
|         return ctx.OpAccessChain(ctx.output_f32, ctx.output_position, element_id()); | ||||
|     case IR::Attribute::PositionW: { | ||||
|         const u32 element{static_cast<u32>(attr) % 4}; | ||||
|         const Id element_id{ctx.Constant(ctx.U32[1], element)}; | ||||
|         return ctx.OpAccessChain(ctx.output_f32, ctx.output_position, element_id); | ||||
|     } | ||||
|     case IR::Attribute::ClipDistance0: | ||||
|     case IR::Attribute::ClipDistance1: | ||||
|     case IR::Attribute::ClipDistance2: | ||||
|   | ||||
| @@ -22,6 +22,21 @@ void SetFixedPipelinePointSize(EmitContext& ctx) { | ||||
|         ctx.OpStore(ctx.output_point_size, ctx.Constant(ctx.F32[1], point_size)); | ||||
|     } | ||||
| } | ||||
|  | ||||
| Id DefaultVarying(EmitContext& ctx, u32 num_components, u32 element, Id zero, Id one, | ||||
|                   Id default_vector) { | ||||
|     switch (num_components) { | ||||
|     case 1: | ||||
|         return element == 3 ? one : zero; | ||||
|     case 2: | ||||
|         return ctx.ConstantComposite(ctx.F32[2], zero, element + 1 == 3 ? one : zero); | ||||
|     case 3: | ||||
|         return ctx.ConstantComposite(ctx.F32[3], zero, zero, element + 2 == 3 ? one : zero); | ||||
|     case 4: | ||||
|         return default_vector; | ||||
|     } | ||||
|     throw InvalidArgument("Bad element"); | ||||
| } | ||||
| } // Anonymous namespace | ||||
|  | ||||
| void EmitPrologue(EmitContext& ctx) { | ||||
| @@ -30,9 +45,17 @@ void EmitPrologue(EmitContext& ctx) { | ||||
|         const Id one{ctx.Constant(ctx.F32[1], 1.0f)}; | ||||
|         const Id default_vector{ctx.ConstantComposite(ctx.F32[4], zero, zero, zero, one)}; | ||||
|         ctx.OpStore(ctx.output_position, default_vector); | ||||
|         for (const Id generic_id : ctx.output_generics) { | ||||
|             if (Sirit::ValidId(generic_id)) { | ||||
|                 ctx.OpStore(generic_id, default_vector); | ||||
|         for (const auto& info : ctx.output_generics) { | ||||
|             if (info[0].num_components == 0) { | ||||
|                 continue; | ||||
|             } | ||||
|             u32 element{0}; | ||||
|             while (element < 4) { | ||||
|                 const auto& element_info{info[element]}; | ||||
|                 const u32 num{element_info.num_components}; | ||||
|                 const Id value{DefaultVarying(ctx, num, element, zero, one, default_vector)}; | ||||
|                 ctx.OpStore(element_info.id, value); | ||||
|                 element += num; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user