c++unreal-engine5unreal

How TGetBaseTypeHelper template works in unreal engine


Recently, I'm reading code of render system in the unreal engine5.3. I feel very confused about some of reflection code in shader. Here is an shader example in AnisotropyRendering.cpp.

class FAnisotropyVS : public FMeshMaterialShader
{
public:
    DECLARE_SHADER_TYPE(FAnisotropyVS, MeshMaterial);

    static bool ShouldCompilePermutation(const FMeshMaterialShaderPermutationParameters& Parameters)
    {
        // Compile if supported by the hardware.
        const bool bIsFeatureSupported = IsFeatureLevelSupported(Parameters.Platform, ERHIFeatureLevel::SM5);

        return 
            bIsFeatureSupported && 
            IsAnisotropyPassCompatible(Parameters.Platform, Parameters.MaterialParameters) &&
            FMeshMaterialShader::ShouldCompilePermutation(Parameters);
    }

    FAnisotropyVS() = default;
    FAnisotropyVS(const ShaderMetaType::CompiledShaderInitializerType& Initializer)
        : FMeshMaterialShader(Initializer)
    {}
};

After expanding DECLARE_SHADER_TYPE macro, I found some piece of code

private:
  using InternalBaseType = typename TGetBaseTypeHelper<FAnisotropyVS>::Type;
  template <typename InternalType>
  static void InternalInitializeBases(FTypeLayoutDesc &TypeDesc) {
    TInitializeBaseHelper<InternalType, InternalBaseType>::Initialize(TypeDesc);
  };
public:
  using DerivedType = FAnisotropyVS;
  static constexpr ETypeLayoutInterface ::Type InterfaceType =
      ETypeLayoutInterface ::NonVirtual;
  template <int Counter> struct InternalLinkType {
    ;
    static inline __attribute__((always_inline)) void
    Initialize(FTypeLayoutDesc &TypeDesc) {}
  };

The confused thing is, compiler tells me InternalBaseType is actually a type alias of FMeshMaterialShader, but it seems to me that InternalBaseType should be a alias of FAnisotropyVS.

TGetBaseTypeHelper template defined in MemoryLayout.h

template<typename T>
struct TGetBaseTypeHelper
{
    UE_STATIC_ONLY(TGetBaseTypeHelper);
    template<typename InternalType> static typename InternalType::DerivedType Test(const typename InternalType::DerivedType*);
    template<typename InternalType> static void Test(...);

    using Type = decltype(Test<T>(nullptr));
};

Because FAnisotropyVS::DerivedType is just FAnisotropyVS. I can't see why InternalBaseType is a alias of FMeshMaterialShader(base type)

I will very appreciate it if someone can enlighten me what happens here. Though this is a very small implementation problem and nobody cares...


Solution

  • In class FMeshMaterialShader

    class RENDERER_API FMeshMaterialShader : public FMaterialShader
    {
        DECLARE_TYPE_LAYOUT(FMeshMaterialShader, NonVirtual);
    };
    

    This macro will be expanded into some code and a piece of it like this

    public:
        using DerivedType = FMeshMaterialShader;
    

    now the DerivedType is FMeshMaterialShader.

    when class FAnisotropyVS inherits class FMeshMaterialShader.

    class FAnisotropyVS looks like this

    class FAnisotropyVS : public FMeshMaterialShader
    {
       // The declaration of FMeshMaterialShader
    public:
       using DerivedType = FMeshMaterialShader;
       //... some code
    
       // The declaration of FAnisotropyVS its own
    public:
        DECLARE_SHADER_TYPE(FAnisotropyVS, MeshMaterial);
    };
    

    Now when DECLARE_SHADER_TYPE(FAnisotropyVS, MeshMaterial); expand like this

    private:
      using InternalBaseType = typename TGetBaseTypeHelper<FAnisotropyVS>::Type;
      // Some code ...
    public:
      using DerivedType = FAnisotropyVS;
      
    

    The DerivedType is still FMeshMaterialShader, because using DerivedType = FAnisotropyVS; is after this piece of code.

    So InternalBaseType in FAnisotropyVS is alias of FMeshMaterialShader