8.1 本篇概述
Shader是在GPU侧执行的逻辑指令,根据执行单元的不同,可分为顶点着色器(Vertex Shader)、像素着色器(Pixel Shader)、计算着色器(Compute Shader),以及几何着色器、网格着色器等等。
UE的Shader为了跨平台、跨图形API,做了很多封装和抽象,由此阐述的类型和概念非常多,另外,为了优化,提升代码复用率,又增加了排列、PSO、DDC等概念和类型。
前面很多篇章都有涉及Shader的概念、类型和代码,本篇将更加深入且广泛低阐述它的体系。主要阐述UE的以下内容:
Shader的基础概念。Shader的基础类型。Shader的实现层级。Shader的使用方法和用例。Shader的实现和原理。Shader的跨平台机制。
需要注意的是,本篇涉及的Shader既包含C++层的概念和类型,也包括GPU层的概念和类型。
8.2 Shader基础
本章将分析Shader涉及的基础概念和类型,阐述它们之间的基本关系和使用方法。
8.2.1 FShader
FShader是一个已经编译好的着色器代码和它的参数绑定的类型,是我们在渲染代码中最基础、核心、常见的一个类型。它的定义如下:
// Engine\Source\Runtime\RenderCore\Public\Shader.h
class RENDERCORE_API FShader
{
public:
(......)
// 在编译触发之前修改编译环境参数, 可由子类覆盖.
static void ModifyCompilationEnvironment(const FShaderPermutationParameters&, FShaderCompilerEnvironment&) {}
// 是否需要编译指定的排列, 可由子类覆盖.
static bool ShouldCompilePermutation(const FShaderPermutationParameters&) { return true; }
// 检测编译结果是否有效, 可由子类覆盖.
static bool ValidateCompiledResult(EShaderPlatform InPlatform, const FShaderParameterMap& InParameterMap, TArray<FString>& OutError) { return true; }
// 获取各类数据的Hash的接口.
const FSHAHash& GetHash() const;
const FSHAHash& GetVertexFactoryHash() const;
const FSHAHash& GetOutputHash() const;
// 保存并检测shader代码的编译结果.
void Finalize(const FShaderMapResourceCode* Code);
// 数据获取接口.
inline FShaderType* GetType(const FShaderMapPointerTable& InPointerTable) const { return Type.Get(InPointerTable.ShaderTypes); }
inline FShaderType* GetType(const FPointerTableBase* InPointerTable) const { return Type.Get(InPointerTable); }
inline FVertexFactoryType* GetVertexFactoryType(const FShaderMapPointerTable& InPointerTable) const { return VFType.Get(InPointerTable.VFTypes); }
inline FVertexFactoryType* GetVertexFactoryType(const FPointerTableBase* InPointerTable) const { return VFType.Get(InPointerTable); }
inline FShaderType* GetTypeUnfrozen() const { return Type.GetUnfrozen(); }
inline int32 GetResourceIndex() const { checkSlow(ResourceIndex != INDEX_NONE); return ResourceIndex; }
inline EShaderPlatform GetShaderPlatform() const { return Target.GetPlatform(); }
inline EShaderFrequency GetFrequency() const { return Target.GetFrequency(); }
inline const FShaderTarget GetTarget() const { return Target; }
inline bool IsFrozen() const { return Type.IsFrozen(); }
inline uint32 GetNumInstructions() const { return NumInstructions; }
#if WITH_EDITORONLY_DATA
inline uint32 GetNumTextureSamplers() const { return NumTextureSamplers; }
inline uint32 GetCodeSize() const { return CodeSize; }
inline void SetNumInstructions(uint32 Value) { NumInstructions = Value; }
#else
inline uint32 GetNumTextureSamplers() const { return 0u; }
inline uint32 GetCodeSize() const { return 0u; }
#endif
// 尝试返回匹配指定类型的自动绑定的Uniform Buffer, 如果不存在则返回未绑定的.
template<typename UniformBufferStructType>
const TShaderUniformBufferParameter<UniformBufferStructType>& GetUniformBufferParameter() const;
const FShaderUniformBufferParameter& GetUniformBufferParameter(const FShaderParametersMetadata* SearchStruct) const;
const FShaderUniformBufferParameter& GetUniformBufferParameter(const FHashedName SearchName) const;
const FShaderParametersMetadata* FindAutomaticallyBoundUniformBufferStruct(int32 BaseIndex) const;
static inline const FShaderParametersMetadata* GetRootParametersMetadata();
(......)
public:
// 着色器参数绑定.
LAYOUT_FIELD(FShaderParameterBindings, Bindings);
// 着色器参数绑定的映射信息.
LAYOUT_FIELD(FShaderParameterMapInfo, ParameterMapInfo);
protected:
LAYOUT_FIELD(TMemoryImageArray<FHashedName>, UniformBufferParameterStructs);
LAYOUT_FIELD(TMemoryImageArray<FShaderUniformBufferParameter>, UniformBufferParameters);
// 下面3个是编辑器参数.
// 着色器的编译输出和结果参数映射的哈希值, 用于查找匹配的资源.
LAYOUT_FIELD_EDITORONLY(FSHAHash, OutputHash);
// 顶点工厂资源哈希值
LAYOUT_FIELD_EDITORONLY(FSHAHash, VFSourceHash);
// shader资源哈希值.
LAYOUT_FIELD_EDITORONLY(FSHAHash, SourceHash);
private:
// 着色器类型.
LAYOUT_FIELD(TIndexedPtr<FShaderType>, Type);
// 顶点工厂类型.
LAYOUT_FIELD(TIndexedPtr<FVertexFactoryType>, VFType);
// 目标平台和着色频率(frequency).
LAYOUT_FIELD(FShaderTarget, Target);
// 在FShaderMapResource的shader索引.
LAYOUT_FIELD(int32, ResourceIndex);
// shader指令数.
LAYOUT_FIELD(uint32, NumInstructions);
// 纹理采样器数量.
LAYOUT_FIELD_EDITORONLY(uint32, NumTextureSamplers);
// shader代码尺寸.
LAYOUT_FIELD_EDITORONLY(uint32, CodeSize);
};
以上可知,FShader存储着Shader关联的绑定参数、顶点工厂、编译后的各类资源等数据,并提供了编译器修改和检测接口,还有各类数据获取接口。
FShader实际上是个基础父类,它的子类有:
FGlobalShader:全局着色器,它的子类在内存中只有唯一的实例,常用于屏幕方块绘制、后处理等。它的定义如下:
// Engine\Source\Runtime\RenderCore\Public\GlobalShader.h
class FGlobalShader : public FShader
{
public:
(......)
FGlobalShader() : FShader() {}
FGlobalShader(const ShaderMetaType::CompiledShaderInitializerType& Initializer);
// 设置视图着色器参数.
template
inline void SetParameters(TRHICmdList& RHICmdList, ...);
};
相比父类FShader,增加了SetParameters设置视图统一缓冲的接口。FMaterialShader:材质着色器,由FMaterialShaderType指定的材质引用的着色器,是材质蓝图在实例化后的一个shader子集。它的定义如下:
// Engine\Source\Runtime\Renderer\Public\MaterialShader.h
class RENDERER_API FMaterialShader : public FShader
{
public:
(......)
FMaterialShader() = default;
FMaterialShader(const FMaterialShaderType::CompiledShaderInitializerType& Initializer);
// 设置视图Uniform Buffer参数.
template
void SetViewParameters(FRHICommandList& RHICmdList, ...);
// 设置材质相关但非FMeshBatch相关的像素着色器参数
template< typename TRHIShader >
void SetParameters(FRHICommandList& RHICmdList, ...);
// 获取着色器参数绑定.
void GetShaderBindings(const FScene* Scene, ...) const;
private:
// 是否允许Uniform表达式缓存.
static int32 bAllowCachedUniformExpressions;
// bAllowCachedUniformExpressions对应的控制台遍历.
static FAutoConsoleVariableRef CVarAllowCachedUniformExpressions;
#if !(UE_BUILD_TEST || UE_BUILD_SHIPPING || !WITH_EDITOR)
// 验证表达式和着色器图的有效性.
void VerifyExpressionAndShaderMaps(const FMaterialRenderProxy* MaterialRenderProxy, const FMaterial& Material, const FUniformExpressionCache* UniformExpressionCache) const;
#endif
// 分配的参数Uniform Buffer.
LAYOUT_FIELD(TMemoryImageArray, ParameterCollectionUniformBuffers);
// 材质的着色器Uniform Buffer.
LAYOUT_FIELD(FShaderUniformBufferParameter, MaterialUniformBuffer);
(......)
};
下面是FShader继承体系下的部分子类:
FShader
FGlobalShader
TMeshPaintVertexShader
TMeshPaintPixelShader
FDistanceFieldDownsamplingCS
FBaseGPUSkinCacheCS
TGPUSkinCacheCS
FBaseRecomputeTangentsPerTriangleShader
FBaseRecomputeTangentsPerVertexShader
FRadixSortUpsweepCS
FRadixSortDownsweepCS
FParticleTileVS
FBuildMipTreeCS
FScreenVS
FScreenPS
FScreenPSInvertAlpha
FSimpleElementVS
FSimpleElementPS
FStereoLayerVS
FStereoLayerPS_Base
FStereoLayerPS
FUpdateTexture2DSubresouceCS
FUpdateTexture3DSubresouceCS
FCopyTexture2DCS
TCopyDataCS
FLandscapeLayersVS
FLandscapeLayersHeightmapPS
FGenerateMipsCS
FGenerateMipsVS
FGenerateMipsPS
FCopyTextureCS
FMediaShadersVS
FRGBConvertPS
FYUVConvertPS
FYUY2ConvertPS
FRGB10toYUVv210ConvertPS
FInvertAlphaPS
FSetAlphaOnePS
FReadTextureExternalPS
FOculusVertexShader
FRasterizeToRectsVS
FResolveVS
FResolveDepthPS
FResolveDepth2XPS
FAmbientOcclusionPS
FGTAOSpatialFilterCS
FGTAOTemporalFilterCS
FDeferredDecalVS
FDitheredTransitionStencilPS
FObjectCullVS
FObjectCullPS
FDeferredLightPS
TDeferredLightHairVS
FFXAAVS
FFXAAPS
FMotionBlurShader
FSubsurfaceShader
FTonemapVS
FTonemapPS
FTonemapCS
FUpscalePS
FTAAStandaloneCS
FSceneCapturePS
FHZBTestPS
FOcclusionQueryVS
FOcclusionQueryPS
FHZBBuildPS
FHZBBuildCS
FDownsampleDepthPS
FTiledDeferredLightingCS
FShader_VirtualTextureCompress
FShader_VirtualTextureCopy
FPageTableUpdateVS
FPageTableUpdatePS
FSlateElementVS
FSlateElementPS
(......)
FMaterialShader
FDeferredDecalPS
FLightHeightfieldsPS
FLightFunctionVS
FLightFunctionPS
FPostProcessMaterialShader
TTranslucentLightingInjectPS
FVolumetricFogLightFunctionPS
FMeshMaterialShader
FLightmapGBufferVS
FLightmapGBufferPS
FVLMVoxelizationVS
FVLMVoxelizationGS
FVLMVoxelizationPS
FLandscapeGrassWeightVS
FLandscapeGrassWeightPS
FLandscapePhysicalMaterial
FAnisotropyVS
FAnisotropyPS
TBasePassVertexShaderPolicyParamType
TBasePassVertexShaderBaseType
TBasePassVS
TBasePassPixelShaderPolicyParamType
TBasePassPixelShaderBaseType
TBasePassPS
FMeshDecalsVS
FMeshDecalsPS
TDepthOnlyVS
TDepthOnlyPS
FDistortionMeshVS
FDistortionMeshPS
FHairMaterialVS
FHairMaterialPS
FHairVisibilityVS
FHairVisibilityPS
TLightMapDensityVS
TLightMapDensityPS
FShadowDepthVS
FShadowDepthBasePS
TShadowDepthPS
FTranslucencyShadowDepthVS
FTranslucencyShadowDepthPS
FVelocityVS
FVelocityPS
FRenderVolumetricCloudVS
FVolumetricCloudShadowPS
FVoxelizeVolumeVS
FVoxelizeVolumePS
FShader_VirtualTextureMaterialDraw
(......)
FSlateMaterialShaderVS
FSlateMaterialShaderPS
(......)
上述只是列出了FShader的部分继承体系,包含了部分之前已经解析过的Shader类型,比如FDeferredLightPS、FFXAAPS、FTonemapPS、FUpscalePS、TBasePassPS、TDepthOnlyPS等等。
FGlobalShader包含了后处理、光照、工具类、可视化、地形、虚拟纹理等方面的Shader代码,可以是VS、PS、CS,但CS必然是FGlobalShader的子类;FMaterialShader主要包含了模型、专用Pass、体素化等方面的Shader代码,可以是VS、PS、GS等,但不会有CS。
如果新定义了FShader的子类,需要借助下面的宏声明和实现对应的代码(部分常见的宏):
// ------ Shader声明和实现宏 ------
// 声明指定类型(FShader子类)的Shader, 可以是Global, Material, MeshMaterial, ...
#define DECLARE_SHADER_TYPE(ShaderClass,ShaderMetaTypeShortcut,...)
// 实现指定类型的Shader, 可以是Global, Material, MeshMaterial, ...
#define IMPLEMENT_SHADER_TYPE(TemplatePrefix,ShaderClass,SourceFilename,FunctionName,Frequency)
// 声明FGlobalShader及其子类.
#define DECLARE_GLOBAL_SHADER(ShaderClass)
// 实现FGlobalShader及其子类.
#define IMPLEMENT_GLOBAL_SHADER(ShaderClass,SourceFilename,FunctionName,Frequency)
// 实现Material着色器.
#define IMPLEMENT_MATERIAL_SHADER_TYPE(TemplatePrefix,ShaderClass,SourceFilename,FunctionName,Frequency)
// 其它不常见的宏
(......)
// ------ 示例1 ------
class FDeferredLightPS : public FGlobalShader
{
// 在FDeferredLightPS类内声明全局着色器
DECLARE_SHADER_TYPE(FDeferredLightPS, Global)
(......)
};
// 实现FDeferredLightPS着色器, 让它和代码文件, 主入口及着色频率关联起来.
IMPLEMENT_GLOBAL_SHADER(FDeferredLightPS, "/Engine/Private/DeferredLightPixelShaders.usf", "DeferredLightPixelMain", SF_Pixel);
// ------ 示例2 ------
class FDeferredDecalPS : public FMaterialShader
{
// 在类内声明材质着色器
DECLARE_SHADER_TYPE(FDeferredDecalPS,Material);
(......)
};
// 实现FDeferredDecalPS类, 让它和代码文件, 主入口以及着色频率关联起来.
IMPLEMENT_MATERIAL_SHADER_TYPE(,FDeferredDecalPS,TEXT("/Engine/Private/DeferredDecal.usf"),TEXT("MainPS"),SF_Pixel);
8.2.2 Shader Parameter
着色器参数是一组由CPU的C++层传入GPU Shader并存储于GPU寄存器或显存的数据。下面是着色器参数常见类型的定义:
// Engine\Source\Runtime\RenderCore\Public\ShaderParameters.h
// 着色器的寄存器绑定参数, 它的类型可以是float1/2/3/4,数组, UAV等.
class FShaderParameter
{
(......)
public:
// 绑定指定名称的参数.
void Bind(const FShaderParameterMap& ParameterMap,const TCHAR* ParameterName, EShaderParameterFlags Flags = SPF_Optional);
// 是否已被着色器绑定.
bool IsBound() const;
// 是否初始化.
inline bool IsInitialized() const;
// 数据获取接口.
uint32 GetBufferIndex() const;
uint32 GetBaseIndex() const;
uint32 GetNumBytes() const;
(......)
};
// 着色器资源绑定(纹理或采样器)
class FShaderResourceParameter
{
(......)
public:
// 绑定指定名称的参数.
void Bind(const FShaderParameterMap& ParameterMap,const TCHAR* ParameterName,EShaderParameterFlags Flags = SPF_Optional);
bool IsBound() const;
inline bool IsInitialized() const;
uint32 GetBaseIndex() const;
uint32 GetNumResources() const;
(......)
};
// 绑定了UAV或SRV资源的类型.
class FRWShaderParameter
{
(......)
public:
// 绑定指定名称的参数.
void Bind(const FShaderParameterMap& ParameterMap,const TCHAR* BaseName);
bool IsBound() const;
bool IsUAVBound() const;
uint32 GetUAVIndex() const;
// 设置缓冲数据到RHI.
template<typename TShaderRHIRef, typename TRHICmdList>
inline void SetBuffer(TRHICmdList& RHICmdList, const TShaderRHIRef& Shader, const FRWBuffer& RWBuffer) const;
template<typename TShaderRHIRef, typename TRHICmdList>
inline void SetBuffer(TRHICmdList& RHICmdList, const TShaderRHIRef& Shader, const FRWBufferStructured& RWBuffer) const;
// 设置纹理数据到RHI.
template<typename TShaderRHIRef, typename TRHICmdList>
inline void SetTexture(TRHICmdList& RHICmdList, const TShaderRHIRef& Shader, FRHITexture* Texture, FRHIUnorderedAccessView* UAV) const;
// 从RHI取消设置UAV.
template<typename TRHICmdList>
inline void UnsetUAV(TRHICmdList& RHICmdList, FRHIComputeShader* ComputeShader) const;
(......)
};
// 创建指定平台下的Uniform Buffer结构体的着色器代码声明.
extern void CreateUniformBufferShaderDeclaration(const TCHAR* Name,const FShaderParametersMetadata& UniformBufferStruct, EShaderPlatform Platform, FString& OutDeclaration);
// 着色器统一缓冲参数.
class FShaderUniformBufferParameter
{
(......)
public:
// 修改编译环境变量.
static void ModifyCompilationEnvironment(const TCHAR* ParameterName,const FShaderParametersMetadata& Struct,EShaderPlatform Platform,FShaderCompilerEnvironment& OutEnvironment);
// 绑定着色器参数.
void Bind(const FShaderParameterMap& ParameterMap,const TCHAR* ParameterName,EShaderParameterFlags Flags = SPF_Optional);
bool IsBound() const;
inline bool IsInitialized() const;
uint32 GetBaseIndex() const;
(......)
};
// 指定结构体的着色器统一缓冲参数
template<typename TBufferStruct>
class TShaderUniformBufferParameter : public FShaderUniformBufferParameter
{
public:
static void ModifyCompilationEnvironment(const TCHAR* ParameterName,EShaderPlatform Platform, FShaderCompilerEnvironment& OutEnvironment);
(......)
};
由此可见,着色器参数可以绑定任何GPU类型的资源或数据,但不同的类只能绑定特定的着色器类型,不能够混用,比如FRWShaderParameter只能绑定UAV或SRV。有了以上类型,就可以在C++层的Shader类配合LAYOUT_FIELD的相关宏声明具体的Shader参数了。
LAYOUT_FIELD是可以声明指定着色器参数的类型、名字、初始值、位域、写入函数等数据的宏,其相关定义如下:
// Engine\Source\Runtime\Core\Public\Serialization\MemoryLayout.h
// 普通布局
#define LAYOUT_FIELD(T, Name, ...)
// 带初始值
#define LAYOUT_FIELD_INITIALIZED(T, Name, Value, ...)
// 带mutable和初始值
#define LAYOUT_MUTABLE_FIELD_INITIALIZED(T, Name, Value, ...)
// 数组布局
#define LAYOUT_ARRAY(T, Name, NumArray, ...)
#define LAYOUT_MUTABLE_BITFIELD(T, Name, BitFieldSize, ...)
// 位域
#define LAYOUT_BITFIELD(T, Name, BitFieldSize, ...)
// 带写入函数
#define LAYOUT_FIELD_WITH_WRITER(T, Name, Func)
#define LAYOUT_MUTABLE_FIELD_WITH_WRITER(T, Name, Func)
#define LAYOUT_WRITE_MEMORY_IMAGE(Func)
#define LAYOUT_TOSTRING(Func)
借助LAYOUT_FIELD等宏,就可以在C++类中声明指定类型的着色器参数,示例:
struct FMyExampleParam
{
// 声明非虚类.
DECLARE_TYPE_LAYOUT(FMyExampleParam, NonVirtual);
// 位域
LAYOUT_FIELD(FShaderParameter, ShaderParam); // 等价于: FShaderParameter ShaderParam;
LAYOUT_FIELD(FShaderResourceParameter, TextureParam); // 等价于: FShaderResourceParameter TextureParam;
LAYOUT_FIELD(FRWShaderParameter, OutputUAV); // 等价于: FRWShaderParameter OutputUAV;
// 数组, 第3个参数是最大数量.
LAYOUT_ARRAY(FShaderResourceParameter, TextureArray, 5); // 等价于: FShaderResourceParameter TextureArray[5];
LAYOUT_ARRAY(int32, Ids, 64); // 等价于: int32 Ids[64];
LAYOUT_FIELD_INITIALIZED(uint32, Size, 0); // 等价于: int32 Size = 0;
void WriteDataFunc(FMemoryImageWriter& Writer, const TMemoryImagePtr<FOtherExampleParam>& InParameters) const;
// 带写入函数.
LAYOUT_FIELD_WITH_WRITER(TMemoryImagePtr<FOtherExampleParam>, Parameters, WriteDataFunc);
};
8.2.3 Uniform Buffer
UE的Uniform Buffer涉及了几个核心的概念,最底层的是RHI层的FRHIUniformBuffer,封装了各种图形API的统一缓冲区(也叫Constant Buffer),它的定义如下(去掉了实现和调试代码):
// Engine\Source\Runtime\RHI\Public\RHIResources.h
class FRHIUniformBuffer : public FRHIResource
{
public:
// 构造函数.
FRHIUniformBuffer(const FRHIUniformBufferLayout& InLayout);
// 引用计数操作.
uint32 AddRef() const;
uint32 Release() const;
// 数据获取接口.
uint32 GetSize() const;
const FRHIUniformBufferLayout& GetLayout() const;
bool IsGlobal() const;
private:
// RHI Uniform Buffer的布局.
const FRHIUniformBufferLayout* Layout;
// 缓冲区尺寸.
uint32 LayoutConstantBufferSize;
};
再往上一层就是TUniformBufferRef,会引用到上述的FRHIUniformBuffer:
// Engine\Source\Runtime\RHI\Public\RHIResources.h
// 定义FRHIUniformBuffer的引用类型.
typedef TRefCountPtr<FRHIUniformBuffer> FUniformBufferRHIRef;
// Engine\Source\Runtime\RenderCore\Public\ShaderParameterMacros.h
// 引用了指定类型的FRHIUniformBuffer的实例资源. 注意是继承了FUniformBufferRHIRef.
template<typename TBufferStruct>
class TUniformBufferRef : public FUniformBufferRHIRef
{
public:
TUniformBufferRef();
// 根据给定的值创建Uniform Buffer, 并返回结构体引用. (模板)
static TUniformBufferRef<TBufferStruct> CreateUniformBufferImmediate(const TBufferStruct& Value, EUniformBufferUsage Usage, EUniformBufferValidation Validation = EUniformBufferValidation::ValidateResources);
// 根据给定的值创建[局部]的Uniform Buffer, 并返回结构体引用.
static FLocalUniformBuffer CreateLocalUniformBuffer(FRHICommandList& RHICmdList, const TBufferStruct& Value, EUniformBufferUsage Usage);
// 立即刷新缓冲区数据到RHI.
void UpdateUniformBufferImmediate(const TBufferStruct& Value);
private:
// 私有构造体, 只能给TUniformBuffer和TRDGUniformBuffer创建.
TUniformBufferRef(FRHIUniformBuffer* InRHIRef);
template<typename TBufferStruct2>
friend class TUniformBuffer;
friend class TRDGUniformBuffer<TBufferStruct>;
};
再往上一层就是引用了FUniformBufferRHIRef的TUniformBuffer和TRDGUniformBuffer,它们的定义如下:
// Engine\Source\Runtime\RenderCore\Public\UniformBuffer.h
// 引用了Uniform Buffer的资源.
template<typename TBufferStruct>
class TUniformBuffer : public FRenderResource
{
public:
// 构造函数.
TUniformBuffer()
: BufferUsage(UniformBuffer_MultiFrame)
, Contents(nullptr){}
// 析构函数.
~TUniformBuffer()
{
if (Contents)
{
FMemory::Free(Contents);
}
}
// 设置Uniform Buffer的内容数据.
void SetContents(const TBufferStruct& NewContents)
{
SetContentsNoUpdate(NewContents);
UpdateRHI();
}
// 清零Uniform Buffer的内容数据. (若内容为空会先创建)
void SetContentsToZero()
{
if (!Contents)
{
Contents = (uint8*)FMemory::Malloc(sizeof(TBufferStruct), SHADER_PARAMETER_STRUCT_ALIGNMENT);
}
FMemory::Memzero(Contents, sizeof(TBufferStruct));
UpdateRHI();
}
// 获取内容.
const uint8* GetContents() const
{
return Contents;
}
// ----重载FRenderResource的接口----
// 初始化动态RHI资源.
virtual void InitDynamicRHI() override
{
check(IsInRenderingThread());
UniformBufferRHI.SafeRelease();
if (Contents)
{
// 根据二进制流的内容数据创建RHI资源.
UniformBufferRHI = CreateUniformBufferImmediate<TBufferStruct>(*((const TBufferStruct*)Contents), BufferUsage);
}
}
// 释放动态RHI资源.
virtual void ReleaseDynamicRHI() override
{
UniformBufferRHI.SafeRelease();
}
// 数据访问接口.
FRHIUniformBuffer* GetUniformBufferRHI() const
{
return UniformBufferRHI;
}
const TUniformBufferRef<TBufferStruct>& GetUniformBufferRef() const
{
return UniformBufferRHI;
}
// Buffer标记.
EUniformBufferUsage BufferUsage;
protected:
// 设置Uniform Buffer的内容数据.
void SetContentsNoUpdate(const TBufferStruct& NewContents)
{
if (!Contents)
{
Contents = (uint8*)FMemory::Malloc(sizeof(TBufferStruct), SHADER_PARAMETER_STRUCT_ALIGNMENT);
}
FMemory::Memcpy(Contents,&NewContents,sizeof(TBufferStruct));
}
private:
// TUniformBufferRef的引用.
TUniformBufferRef<TBufferStruct> UniformBufferRHI;
// CPU侧的内容数据.
uint8* Contents;
};
// Engine\Source\Runtime\RenderCore\Public\RenderGraphResources.h
class FRDGUniformBuffer : public FRDGResource
{
public:
bool IsGlobal() const;
const FRDGParameterStruct& GetParameters() const;
//////////////////////////////////////////////////////////////////////////
// 获取RHI, 只可在Pass执行时调用.
FRHIUniformBuffer* GetRHI() const
{
return static_cast<FRHIUniformBuffer*>(FRDGResource::GetRHI());
}
//////////////////////////////////////////////////////////////////////////
protected:
// 构造函数.
template <typename TParameterStruct>
explicit FRDGUniformBuffer(TParameterStruct* InParameters, const TCHAR* InName)
: FRDGResource(InName)
, ParameterStruct(InParameters)
, bGlobal(ParameterStruct.HasStaticSlot())
{}
private:
const FRDGParameterStruct ParameterStruct;
// 引用了FRHIUniformBuffer的资源.
// 注意TUniformBufferRef<TBufferStruct>和FUniformBufferRHIRef时等价的.
TRefCountPtr<FRHIUniformBuffer> UniformBufferRHI;
FRDGUniformBufferHandle Handle;
// 是否被全局Shader还是局部Shader绑定.
uint8 bGlobal : 1;
friend FRDGBuilder;
friend FRDGUniformBufferRegistry;
friend FRDGAllocator;
};
// FRDGUniformBuffer的模板版本.
template <typename ParameterStructType>
class TRDGUniformBuffer : public FRDGUniformBuffer
{
public:
// 数据获取接口.
const TRDGParameterStruct<ParameterStructType>& GetParameters() const;
TUniformBufferRef<ParameterStructType> GetRHIRef() const;
const ParameterStructType* operator->() const;
private:
explicit TRDGUniformBuffer(ParameterStructType* InParameters, const TCHAR* InName)
: FRDGUniformBuffer(InParameters, InName)
{}
friend FRDGBuilder;
friend FRDGUniformBufferRegistry;
friend FRDGAllocator;
};
将它们抽象成UML继承图之后,如下所示:
FRHIResourceFRHIUniformBufferFRHIUniformBufferLayout* Layoutuint32 LayoutConstantBufferSizeFUniformBufferRHIRefFRHIUniformBuffer* ReferenceTUniformBufferRefTUniformBufferRef(FRHIUniformBuffer* InRHIRef)CreateUniformBufferImmediate()CreateLocalUniformBuffer()UpdateUniformBufferImmediate()FRenderResourceTUniformBufferuint8* ContentsEUniformBufferUsage BufferUsageTUniformBufferRef UniformBufferRHISetContents()GetUniformBufferRHI()GetUniformBufferRef()FRDGUniformBufferFUniformBufferRHIRef UniformBufferRHIFRDGUniformBufferHandle HandleTRDGUniformBufferGetRHIRef()
吐槽一下:文本绘图语法Mermaid不能指定布局,自动生成的图形布局不够美观,并且在window下放大UI之后,文字显示不全了。凑合着看吧。
以上Uniform Buffer的类型可以通过SHADER_PARAMETER的相关宏定义结构体和结构体成员。SHADER_PARAMETER的相关宏定义如下:
// Engine\Source\Runtime\RenderCore\Public\ShaderParameterMacros.h
// Shader Parameter Struct: 开始/结束.
#define BEGIN_SHADER_PARAMETER_STRUCT(StructTypeName, PrefixKeywords)
#define END_SHADER_PARAMETER_STRUCT()
// Uniform Buffer Struct: 开始/结束/实现.
#define BEGIN_UNIFORM_BUFFER_STRUCT(StructTypeName, PrefixKeywords)
#define BEGIN_UNIFORM_BUFFER_STRUCT_WITH_CONSTRUCTOR(StructTypeName, PrefixKeywords)
#define END_UNIFORM_BUFFER_STRUCT()
#define IMPLEMENT_UNIFORM_BUFFER_STRUCT(StructTypeName,ShaderVariableName)
#define IMPLEMENT_UNIFORM_BUFFER_ALIAS_STRUCT(StructTypeName, UniformBufferAlias)
#define IMPLEMENT_STATIC_UNIFORM_BUFFER_STRUCT(StructTypeName,ShaderVariableName,StaticSlotName)
#define IMPLEMENT_STATIC_UNIFORM_BUFFER_SLOT(SlotName)
// Global Shader Parameter Struct: 开始/结束/实现.
#define BEGIN_GLOBAL_SHADER_PARAMETER_STRUCT
#define BEGIN_GLOBAL_SHADER_PARAMETER_STRUCT_WITH_CONSTRUCTOR
#define END_GLOBAL_SHADER_PARAMETER_STRUCT
#define IMPLEMENT_GLOBAL_SHADER_PARAMETER_STRUCT
#define IMPLEMENT_GLOBAL_SHADER_PARAMETER_ALIAS_STRUCT
// Shader Parameter: 单个, 数组.
#define SHADER_PARAMETER(MemberType, MemberName)
#define SHADER_PARAMETER_EX(MemberType,MemberName,Precision)
#define SHADER_PARAMETER_ARRAY(MemberType,MemberName,ArrayDecl)
#define SHADER_PARAMETER_ARRAY_EX(MemberType,MemberName,ArrayDecl,Precision)
// Shader Parameter: 纹理, SRV, UAV, 采样器及其数组
#define SHADER_PARAMETER_TEXTURE(ShaderType,MemberName)
#define SHADER_PARAMETER_TEXTURE_ARRAY(ShaderType,MemberName, ArrayDecl)
#define SHADER_PARAMETER_SRV(ShaderType,MemberName)
#define SHADER_PARAMETER_UAV(ShaderType,MemberName)
#define SHADER_PARAMETER_SAMPLER(ShaderType,MemberName)
#define SHADER_PARAMETER_SAMPLER_ARRAY(ShaderType,MemberName, ArrayDecl)
// Shader Parameter Struct内的Shader Parameter Struct参数.
#define SHADER_PARAMETER_STRUCT(StructType,MemberName)
#define SHADER_PARAMETER_STRUCT_ARRAY(StructType,MemberName, ArrayDecl)
#define SHADER_PARAMETER_STRUCT_INCLUDE(StructType,MemberName)
// 引用一个[全局]的着色器参数结构体.
#define SHADER_PARAMETER_STRUCT_REF(StructType,MemberName)
// RDG模式的Shader Parameter.
#define SHADER_PARAMETER_RDG_TEXTURE(ShaderType,MemberName)
#define SHADER_PARAMETER_RDG_TEXTURE_SRV(ShaderType,MemberName)
#define SHADER_PARAMETER_RDG_TEXTURE_UAV(ShaderType,MemberName)
#define SHADER_PARAMETER_RDG_TEXTURE_UAV_ARRAY(ShaderType,MemberName, ArrayDecl)
#define SHADER_PARAMETER_RDG_BUFFER(ShaderType,MemberName)
#define SHADER_PARAMETER_RDG_BUFFER_SRV(ShaderType,MemberName)
#define SHADER_PARAMETER_RDG_BUFFER_SRV_ARRAY(ShaderType,MemberName, ArrayDecl)
#define SHADER_PARAMETER_RDG_BUFFER_UAV(ShaderType,MemberName)
#define SHADER_PARAMETER_RDG_BUFFER_UAV(ShaderType,MemberName)
#define SHADER_PARAMETER_RDG_BUFFER_UAV_ARRAY(ShaderType,MemberName, ArrayDecl)
#define SHADER_PARAMETER_RDG_UNIFORM_BUFFER(StructType, MemberName)
注意局部(普通)的Shader Parameter Struct没有实现(IMPLEMENT_SHADER_PARAMETER_STRUCT)宏,Global的才有(IMPLEMENT_GLOBAL_SHADER_PARAMETER_STRUCT)。
下面给出示例,展示如何用上述部分宏来声明着色器的各类参数:
// 定义全局的着色器参数结构体(可在.h或.cpp, 不过一般在.h)
BEGIN_GLOBAL_SHADER_PARAMETER_STRUCT(FMyShaderParameterStruct, )
// 常规单个和数组参数.
SHADER_PARAMETER(float, Intensity)
SHADER_PARAMETER_ARRAY(FVector3, Vertexes, [8])
// 采样器, 纹理, SRV, UAV
SHADER_PARAMETER_SAMPLER(SamplerState, TextureSampler)
SHADER_PARAMETER_TEXTURE(Texture3D, Texture3d)
SHADER_PARAMETER_SRV(Buffer<float4>, VertexColorBuffer)
SHADER_PARAMETER_UAV(RWStructuredBuffer<float4>, OutputTexture)
// 着色器参数结构体
// 引用着色器参数结构体(全局的才行)
SHADER_PARAMETER_STRUCT_REF(FViewUniformShaderParameters, View)
// 包含着色器参数结构体(局部或全局都行)
SHADER_PARAMETER_STRUCT_INCLUDE(FSceneTextureShaderParameters, SceneTextures)
END_GLOBAL_SHADER_PARAMETER_STRUCT()
// 实现全局的着色器参数结构体(只能在.cpp)
IMPLEMENT_GLOBAL_SHADER_PARAMETER_STRUCT(FMyShaderParameterStruct, "MyShaderParameterStruct");
上面的着色器结构体是在C++侧声明和实现的,如果需要正确传入到Shader中,还需要额外的C++代码来完成:
// 声明结构体.
FMyShaderParameterStruct MyShaderParameterStruct;
// 创建RHI资源.
// 可以是多帧(UniformBuffer_MultiFrame)的, 这样只需创建1次就可以缓存指针, 后续有数据更新调用UpdateUniformBufferImmediate即可.
// 也可以是单帧的(UniformBuffer_SingleFrame), 则每帧需要创建和更新数据.
auto MyShaderParameterStructRHI = TUniformBufferRef<FMyShaderParameterStruct>::CreateUniformBufferImmediate(ShaderParameterStruct, EUniformBufferUsage::UniformBuffer_MultiFrame);
// 更新着色器参数结构体.
MyShaderParameterStruct.Intensity = 1.0f;
(......)
// 更新数据到RHI.
MyShaderParameterStructRHI.UpdateUniformBufferImmediate(MyShaderParameterStruct);
8.2.4 Vertex Factory
我们知道,在引擎中存在着静态网格、蒙皮骨骼、程序化网格以及地形等等类型的网格类型,而材质就是通过顶点工厂FVertexFactory来支持这些网格类型。实际上,顶点工厂要涉及各方面的数据和类型,包含但不限于:
顶点着色器。顶点着色器的输入输出需要顶点工厂来表明数据的布局。顶点工厂的参数和RHI资源。这些数据将从C++层传入到顶点着色器中进行处理。顶点缓冲和顶点布局。通过顶点布局,我们可以自定义和扩展顶点缓冲的输入,从而实现定制化的Shader代码。几何预处理。顶点缓冲、网格资源、材质参数等等都可以在真正渲染前预处理它们。
顶点工厂在渲染层级中的关系。由图可知,顶点工厂是渲染线程的对象,横跨于CPU和GPU两端。
FVertexFactory封装了可以链接到顶点着色器的顶点数据资源,它和相关类型的定义如下:
// Engine\Source\Runtime\RHI\Public\RHI.h
// 顶点元素.
struct FVertexElement
{
uint8 StreamIndex; // 流索引
uint8 Offset; // 偏移
TEnumAsByte<EVertexElementType> Type; // 类型
uint8 AttributeIndex;// 属性索引
uint16 Stride; // 步长
// 实例索引或顶点索引是否实例化的, 若是0, 则元素会对每个实例进行重复.
uint16 bUseInstanceIndex;
FVertexElement();
FVertexElement(uint8 InStreamIndex, ...);
void operator=(const FVertexElement& Other);
friend FArchive& operator<<(FArchive& Ar,FVertexElement& Element);
FString ToString() const;
void FromString(const FString& Src);
void FromString(const FStringView& Src);
};
// 顶点声明元素列表的类型.
typedef TArray<FVertexElement,TFixedAllocator<MaxVertexElementCount> > FVertexDeclarationElementList;
// Engine\Source\Runtime\RHI\Public\RHIResources.h
// 顶点声明的RHI资源
class FRHIVertexDeclaration : public FRHIResource
{
public:
virtual bool GetInitializer(FVertexDeclarationElementList& Init) { return false; }
};
// 顶点缓冲区
class FRHIVertexBuffer : public FRHIResource
{
public:
FRHIVertexBuffer(uint32 InSize,uint32 InUsage);
uint32 GetSize() const;
uint32 GetUsage() const;
protected:
FRHIVertexBuffer();
void Swap(FRHIVertexBuffer& Other);
void ReleaseUnderlyingResource();
private:
// 尺寸.
uint32 Size;
// 缓冲区标记, 如BUF_UnorderedAccess
uint32 Usage;
};
// Engine\Source\Runtime\RenderCore\Public\VertexFactory.h
// 顶点输入流.
struct FVertexInputStream
{
// 顶点流索引
uint32 StreamIndex : 4;
// 在VertexBuffer的偏移.
uint32 Offset : 28;
// 顶点缓存区
FRHIVertexBuffer* VertexBuffer;
FVertexInputStream();
FVertexInputStream(uint32 InStreamIndex, uint32 InOffset, FRHIVertexBuffer* InVertexBuffer);
inline bool operator==(const FVertexInputStream& rhs) const;
inline bool operator!=(const FVertexInputStream& rhs) const;
};
// 顶点输入流数组.
typedef TArray<FVertexInputStream, TInlineAllocator<4>> FVertexInputStreamArray;
// 顶点流标记
enum class EVertexStreamUsage : uint8
{
Default = 0 << 0, // 默认
Instancing = 1 << 0, // 实例化
Overridden = 1 << 1, // 覆盖
ManualFetch = 1 << 2 // 手动获取
};
// 顶点输入流类型.
enum class EVertexInputStreamType : uint8
{
Default = 0, // 默认
PositionOnly, // 只有位置
PositionAndNormalOnly // 只有位置和法线
};
// 顶点流组件.
struct FVertexStreamComponent
{
// 流数据的顶点缓冲区, 如果为null, 则不会有数据从此顶点流被读取.
const FVertexBuffer* VertexBuffer = nullptr;
// vertex buffer的偏移.
uint32 StreamOffset = 0;
// 数据的偏移, 相对于顶点缓冲区中每个元素的开头.
uint8 Offset = 0;
// 数据的步长.
uint8 Stride = 0;
// 从流读取的数据类型.
TEnumAsByte<EVertexElementType> Type = VET_None;
// 顶点流标记.
EVertexStreamUsage VertexStreamUsage = EVertexStreamUsage::Default;
(......)
};
// 着色器使用的顶点工厂的参数绑定接口.
class FVertexFactoryShaderParameters
{
public:
// 绑定参数到ParameterMap. 具体逻辑由子类完成.
void Bind(const class FShaderParameterMap& ParameterMap) {}
// 获取顶点工厂的着色器绑定和顶点流. 具体逻辑由子类完成.
void GetElementShaderBindings(
const class FSceneInterface* Scene,
const class FSceneView* View,
const class FMeshMaterialShader* Shader,
const EVertexInputStreamType InputStreamType,
ERHIFeatureLevel::Type FeatureLevel,
const class FVertexFactory* VertexFactory,
const struct FMeshBatchElement& BatchElement,
class FMeshDrawSingleShaderBindings& ShaderBindings,
FVertexInputStreamArray& VertexStreams) const {}
(......)
};
// 用来表示顶点工厂类型的类.
class FVertexFactoryType
{
public:
// 类型定义
typedef FVertexFactoryShaderParameters* (*ConstructParametersType)(EShaderFrequency ShaderFrequency, const class FShaderParameterMap& ParameterMap);
typedef const FTypeLayoutDesc* (*GetParameterTypeLayoutType)(EShaderFrequency ShaderFrequency);
(......)
// 获取顶点工厂类型数量.
static int32 GetNumVertexFactoryTypes();
// 获取全局的着色器工厂列表.
static RENDERCORE_API TLinkedList<FVertexFactoryType*>*& GetTypeList();
// 获取已存的材质类型列表.
static RENDERCORE_API const TArray<FVertexFactoryType*>& GetSortedMaterialTypes();
// 通过名字查找FVertexFactoryType
static RENDERCORE_API FVertexFactoryType* GetVFByName(const FHashedName& VFName);
// 初始化FVertexFactoryType静态成员, 必须在VF类型创建之前调用.
static void Initialize(const TMap<FString, TArray<const TCHAR*> >& ShaderFileToUniformBufferVariables);
static void Uninitialize();
// 构造/析构函数.
RENDERCORE_API FVertexFactoryType(...);
virtual ~FVertexFactoryType();
// 数据获取接口.
const TCHAR* GetName() const;
FName GetFName() const;
const FHashedName& GetHashedName() const;
const TCHAR* GetShaderFilename() const;
// 着色器参数接口.
FVertexFactoryShaderParameters* CreateShaderParameters(...) const;
const FTypeLayoutDesc* GetShaderParameterLayout(...) const;
void GetShaderParameterElementShaderBindings(...) const;
// 标记访问.
bool IsUsedWithMaterials() const;
bool SupportsStaticLighting() const;
bool SupportsDynamicLighting() const;
bool SupportsPrecisePrevWorldPos() const;
bool SupportsPositionOnly() const;
bool SupportsCachingMeshDrawCommands() const;
bool SupportsPrimitiveIdStream() const;
// 获取哈希.
friend uint32 GetTypeHash(const FVertexFactoryType* Type);
// 基于顶点工厂类型的源码和包含计算出来的哈希.
const FSHAHash& GetSourceHash(EShaderPlatform ShaderPlatform) const;
// 是否需要缓存材质的着色器类型.
bool ShouldCache(const FVertexFactoryShaderPermutationParameters& Parameters) const;
void ModifyCompilationEnvironment(...);
void ValidateCompiledResult(EShaderPlatform Platform, ...);
bool SupportsTessellationShaders() const;
// 增加引用的Uniform Buffer包含.
void AddReferencedUniformBufferIncludes(...);
void FlushShaderFileCache(...);
const TMap<const TCHAR*, FCachedUniformBufferDeclaration>& GetReferencedUniformBufferStructsCache() const;
private:
static uint32 NumVertexFactories;
static bool bInitializedSerializationHistory;
// 顶点工厂类型的各类数据和标记.
const TCHAR* Name;
const TCHAR* ShaderFilename;
FName TypeName;
FHashedName HashedName;
uint32 bUsedWithMaterials : 1;
uint32 bSupportsStaticLighting : 1;
uint32 bSupportsDynamicLighting : 1;
uint32 bSupportsPrecisePrevWorldPos : 1;
uint32 bSupportsPositionOnly : 1;
uint32 bSupportsCachingMeshDrawCommands : 1;
uint32 bSupportsPrimitiveIdStream : 1;
ConstructParametersType ConstructParameters;
GetParameterTypeLayoutType GetParameterTypeLayout;
GetParameterTypeElementShaderBindingsType GetParameterTypeElementShaderBindings;
ShouldCacheType ShouldCacheRef;
ModifyCompilationEnvironmentType ModifyCompilationEnvironmentRef;
ValidateCompiledResultType ValidateCompiledResultRef;
SupportsTessellationShadersType SupportsTessellationShadersRef;
// 全局顶点工厂类型列表.
TLinkedList<FVertexFactoryType*> GlobalListLink;
// 缓存引用的Uniform Buffer的包含.
TMap<const TCHAR*, FCachedUniformBufferDeclaration> ReferencedUniformBufferStructsCache;
// 跟踪ReferencedUniformBufferStructsCache缓存了哪些平台的声明.
bool bCachedUniformBufferStructDeclarations;
};
// ------顶点工厂的工具宏------
// 实现顶点工厂参数类型
#define IMPLEMENT_VERTEX_FACTORY_PARAMETER_TYPE(FactoryClass, ShaderFrequency, ParameterClass)
// 顶点工厂类型的声明
#define DECLARE_VERTEX_FACTORY_TYPE(FactoryClass)
// 顶点工厂类型的实现
#define IMPLEMENT_VERTEX_FACTORY_TYPE(FactoryClass,ShaderFilename,bUsedWithMaterials,bSupportsStaticLighting,bSupportsDynamicLighting,bPrecisePrevWorldPos,bSupportsPositionOnly)
// 顶点工厂的虚函数表实现
#define IMPLEMENT_VERTEX_FACTORY_VTABLE(FactoryClass
// 顶点工厂
class FVertexFactory : public FRenderResource
{
public:
FVertexFactory(ERHIFeatureLevel::Type InFeatureLevel);
virtual FVertexFactoryType* GetType() const;
// 获取顶点数据流.
void GetStreams(ERHIFeatureLevel::Type InFeatureLevel, EVertexInputStreamType VertexStreamType, FVertexInputStreamArray& OutVertexStreams) const
{
// Default顶点流类型
if (VertexStreamType == EVertexInputStreamType::Default)
{
bool bSupportsVertexFetch = SupportsManualVertexFetch(InFeatureLevel);
// 将顶点工厂的数据构造到FVertexInputStream中并添加到输出列表
for (int32 StreamIndex = 0;StreamIndex < Streams.Num();StreamIndex++)
{
const FVertexStream& Stream = Streams[StreamIndex];
if (!(EnumHasAnyFlags(EVertexStreamUsage::ManualFetch, Stream.VertexStreamUsage) && bSupportsVertexFetch))
{
if (!Stream.VertexBuffer)
{
OutVertexStreams.Add(FVertexInputStream(StreamIndex, 0, nullptr));
}
else
{
if (EnumHasAnyFlags(EVertexStreamUsage::Overridden, Stream.VertexStreamUsage) && !Stream.VertexBuffer->IsInitialized())
{
OutVertexStreams.Add(FVertexInputStream(StreamIndex, 0, nullptr));
}
else
{
OutVertexStreams.Add(FVertexInputStream(StreamIndex, Stream.Offset, Stream.VertexBuffer->VertexBufferRHI));
}
}
}
}
}
// 只有位置和的顶点流类型
else if (VertexStreamType == EVertexInputStreamType::PositionOnly)
{
// Set the predefined vertex streams.
for (int32 StreamIndex = 0; StreamIndex < PositionStream.Num(); StreamIndex++)
{
const FVertexStream& Stream = PositionStream[StreamIndex];
OutVertexStreams.Add(FVertexInputStream(StreamIndex, Stream.Offset, Stream.VertexBuffer->VertexBufferRHI));
}
}
// 只有位置和法线的顶点流类型
else if (VertexStreamType == EVertexInputStreamType::PositionAndNormalOnly)
{
// Set the predefined vertex streams.
for (int32 StreamIndex = 0; StreamIndex < PositionAndNormalStream.Num(); StreamIndex++)
{
const FVertexStream& Stream = PositionAndNormalStream[StreamIndex];
OutVertexStreams.Add(FVertexInputStream(StreamIndex, Stream.Offset, Stream.VertexBuffer->VertexBufferRHI));
}
}
else
{
// NOT_IMPLEMENTED
}
}
// 偏移实例的数据流.
void OffsetInstanceStreams(uint32 InstanceOffset, EVertexInputStreamType VertexStreamType, FVertexInputStreamArray& VertexStreams) const;
static void ModifyCompilationEnvironment(...);
static void ValidateCompiledResult(...);
static bool SupportsTessellationShaders();
// FRenderResource接口, 释放RHI资源.
virtual void ReleaseRHI();
// 设置/获取顶点声明的RHI引用.
FVertexDeclarationRHIRef& GetDeclaration();
void SetDeclaration(FVertexDeclarationRHIRef& NewDeclaration);
// 根据类型获取顶点声明的RHI引用.
const FVertexDeclarationRHIRef& GetDeclaration(EVertexInputStreamType InputStreamType) const
{
switch (InputStreamType)
{
case EVertexInputStreamType::Default: return Declaration;
case EVertexInputStreamType::PositionOnly: return PositionDeclaration;
case EVertexInputStreamType::PositionAndNormalOnly: return PositionAndNormalDeclaration;
}
return Declaration;
}
// 各类标记.
virtual bool IsGPUSkinned() const;
virtual bool SupportsPositionOnlyStream() const;
virtual bool SupportsPositionAndNormalOnlyStream() const;
virtual bool SupportsNullPixelShader() const;
// 用面向摄像机精灵的方式渲染图元.
virtual bool RendersPrimitivesAsCameraFacingSprites() const;
// 是否需要顶点声明.
bool NeedsDeclaration() const;
// 是否支持手动的顶点获取.
inline bool SupportsManualVertexFetch(const FStaticFeatureLevel InFeatureLevel) const;
// 根据流类型获取索引.
inline int32 GetPrimitiveIdStreamIndex(EVertexInputStreamType InputStreamType) const;
protected:
inline void SetPrimitiveIdStreamIndex(EVertexInputStreamType InputStreamType, int32 StreamIndex)
{
PrimitiveIdStreamIndex[static_cast<uint8>(InputStreamType)] = StreamIndex;
}
// 为顶点流组件创建顶点元素.
FVertexElement AccessStreamComponent(const FVertexStreamComponent& Component,uint8 AttributeIndex);
FVertexElement AccessStreamComponent(const FVertexStreamComponent& Component, uint8 AttributeIndex, EVertexInputStreamType InputStreamType);
// 初始化顶点声明.
void InitDeclaration(const FVertexDeclarationElementList& Elements, EVertexInputStreamType StreamType = EVertexInputStreamType::Default)
{
if (StreamType == EVertexInputStreamType::PositionOnly)
{
PositionDeclaration = PipelineStateCache::GetOrCreateVertexDeclaration(Elements);
}
else if (StreamType == EVertexInputStreamType::PositionAndNormalOnly)
{
PositionAndNormalDeclaration = PipelineStateCache::GetOrCreateVertexDeclaration(Elements);
}
else // (StreamType == EVertexInputStreamType::Default)
{
// Create the vertex declaration for rendering the factory normally.
Declaration = PipelineStateCache::GetOrCreateVertexDeclaration(Elements);
}
}
// 顶点流, 需要设置到顶点流的信息体.
struct FVertexStream
{
const FVertexBuffer* VertexBuffer = nullptr;
uint32 Offset = 0;
uint16 Stride = 0;
EVertexStreamUsage VertexStreamUsage = EVertexStreamUsage::Default;
uint8 Padding = 0;
friend bool operator==(const FVertexStream& A,const FVertexStream& B);
FVertexStream();
};
// 用于渲染顶点工厂的顶点流.
TArray<FVertexStream,TInlineAllocator<8> > Streams;
// VF(顶点工厂)可以显式地将此设置为false,以避免在没有声明的情况下出现错误. 主要用于需要直接从缓冲区获取数据的VF(如Niagara).
bool bNeedsDeclaration = true;
bool bSupportsManualVertexFetch = false;
int8 PrimitiveIdStreamIndex[3] = { -1, -1, -1 };
private:
// 只有位置的顶点流, 用于渲染深度Pass的顶点工厂.
TArray<FVertexStream,TInlineAllocator<2> > PositionStream;
// 只有位置和法线的顶点流.
TArray<FVertexStream, TInlineAllocator<3> > PositionAndNormalStream;
// 用于常规渲染顶点工厂的RHI顶点声明.
FVertexDeclarationRHIRef Declaration;
// PositionStream和PositionAndNormalStream对应的RHI资源.
FVertexDeclarationRHIRef PositionDeclaration;
FVertexDeclarationRHIRef PositionAndNormalDeclaration;
};
上面展示了Vertex Factory的很多类型,有好几个是核心类,比如FVertexFactory、FVertexElement、FRHIVertexDeclaration、FRHIVertexBuffer、FVertexFactoryType、FVertexStreamComponent、FVertexInputStream、FVertexFactoryShaderParameters等。那么它们之间的关系是什么呢?
为了更好地说明它们之间的关系,以静态模型的FStaticMeshDataType为例:
FStaticMeshDataType会包含若干个FVertexStreamComponent实例,每个FVertexStreamComponent包含了一个在FVertexDeclarationElementList的FVertexElement实例索引和一个在FVertexInputStreamArray列表的FVertexStream实例索引。
此外,FVertexFactory是个基类,内置的子类主要有:
FGeometryCacheVertexVertexFactory:几何缓存顶点的顶点工厂,常用于预生成的布料、动作等网格类型。FGPUBaseSkinVertexFactory:GPU蒙皮骨骼网格的父类,它的子类有:
TGPUSkinVertexFactory:可指定骨骼权重方式的GPU蒙皮的顶点工厂。FLocalVertexFactory:局部顶点工厂,常用于静态网格,它拥有数量较多的子类:
FInstancedStaticMeshVertexFactory:实例化的静态网格顶点工厂。FSplineMeshVertexFactory:样条曲线网格顶点工厂。FGeometryCollectionVertexFactory:几何收集顶点工厂。FGPUSkinPassthroughVertexFactory:启用了Skin Cache模式的蒙皮骨骼顶点工厂。FSingleTriangleMeshVertexFactory:单个三角形网格的顶点工厂,用于体积云渲染。......FParticleVertexFactoryBase:用于粒子渲染的顶点工厂基类。FLandscapeVertexFactory:用于渲染地形的顶点工厂。
除了以上继承自FVertexFactory,还有一些不是继承自FVertexFactory的类型,如:
FGPUBaseSkinAPEXClothVertexFactory:布料顶点工厂。
TGPUSkinAPEXClothVertexFactory:可带骨骼权重模式的布料顶点工厂。
除了FVertexFactory,相应的其它核心类也有继承体系。比如FVertexFactoryShaderParameters的子类有:
FGeometryCacheVertexFactoryShaderParametersFGPUSkinVertexFactoryShaderParametersFMeshParticleVertexFactoryShaderParametersFParticleSpriteVertexFactoryShaderParametersFGPUSpriteVertexFactoryShaderParametersVSFGPUSpriteVertexFactoryShaderParametersPSFSplineMeshVertexFactoryShaderParametersFLocalVertexFactoryShaderParametersBaseFLandscapeVertexFactoryVertexShaderParametersFLandscapeVertexFactoryPixelShaderParameters......
另外,有部分顶点工厂还会在内部派生FStaticMeshDataType的类型,以复用静态网格相关的数据成员。
为了更好地说明顶点工厂的使用方式,下面就以最常见的FLocalVertexFactory和使用了FLocalVertexFactory的CableComponent为例:
// Engine\Source\Runtime\Engine\Public\LocalVertexFactory.h
class ENGINE_API FLocalVertexFactory : public FVertexFactory
{
public:
FLocalVertexFactory(ERHIFeatureLevel::Type InFeatureLevel, const char* InDebugName);
// 派生自FStaticMeshDataType的数据类型.
struct FDataType : public FStaticMeshDataType
{
FRHIShaderResourceView* PreSkinPositionComponentSRV = nullptr;
};
// 环境变量更改和校验.
static bool ShouldCompilePermutation(const FVertexFactoryShaderPermutationParameters& Parameters);
static void ModifyCompilationEnvironment(const FVertexFactoryShaderPermutationParameters& Parameters, FShaderCompilerEnvironment& OutEnvironment);
static void ValidateCompiledResult(const FVertexFactoryType* Type, EShaderPlatform Platform, const FShaderParameterMap& ParameterMap, TArray<FString>& OutErrors);
// 由TSynchronizedResource从游戏线程更新而来的数据.
void SetData(const FDataType& InData);
// 从其它顶点工厂复制数据.
void Copy(const FLocalVertexFactory& Other);
// FRenderResource接口.
virtual void InitRHI() override;
virtual void ReleaseRHI() override
{
UniformBuffer.SafeRelease();
FVertexFactory::ReleaseRHI();
}
// 顶点颜色接口.
void SetColorOverrideStream(FRHICommandList& RHICmdList, const FVertexBuffer* ColorVertexBuffer) const;
void GetColorOverrideStream(const FVertexBuffer* ColorVertexBuffer, FVertexInputStreamArray& VertexStreams) const;
// 着色器参数和其它数据接口.
inline FRHIShaderResourceView* GetPositionsSRV() const;
inline FRHIShaderResourceView* GetPreSkinPositionSRV() const;
inline FRHIShaderResourceView* GetTangentsSRV() const;
inline FRHIShaderResourceView* GetTextureCoordinatesSRV() const;
inline FRHIShaderResourceView* GetColorComponentsSRV() const;
inline const uint32 GetColorIndexMask() const;
inline const int GetLightMapCoordinateIndex() const;
inline const int GetNumTexcoords() const;
FRHIUniformBuffer* GetUniformBuffer() const;
(......)
protected:
// 从游戏线程传入的数据. FDataType是FStaticMeshDataType的子类.
FDataType Data;
// 局部顶点工厂的着色器参数.
TUniformBufferRef<FLocalVertexFactoryUniformShaderParameters> UniformBuffer;
// 顶点颜色流索引.
int32 ColorStreamIndex;
(......)
};
// Engine\Source\Runtime\Engine\Public\LocalVertexFactory.cpp
void FLocalVertexFactory::InitRHI()
{
// 是否使用gpu场景.
const bool bCanUseGPUScene = UseGPUScene(GMaxRHIShaderPlatform, GMaxRHIFeatureLevel);
// 初始化位置流和位置声明.
if (Data.PositionComponent.VertexBuffer != Data.TangentBasisComponents[0].VertexBuffer)
{
// 增加顶点声明.
auto AddDeclaration = [this, bCanUseGPUScene](EVertexInputStreamType InputStreamType, bool bAddNormal)
{
// 顶点流元素.
FVertexDeclarationElementList StreamElements;
StreamElements.Add(AccessStreamComponent(Data.PositionComponent, 0, InputStreamType));
bAddNormal = bAddNormal && Data.TangentBasisComponents[1].VertexBuffer != NULL;
if (bAddNormal)
{
StreamElements.Add(AccessStreamComponent(Data.TangentBasisComponents[1], 2, InputStreamType));
}
const uint8 TypeIndex = static_cast<uint8>(InputStreamType);
PrimitiveIdStreamIndex[TypeIndex] = -1;
if (GetType()->SupportsPrimitiveIdStream() && bCanUseGPUScene)
{
// When the VF is used for rendering in normal mesh passes, this vertex buffer and offset will be overridden
StreamElements.Add(AccessStreamComponent(FVertexStreamComponent(&GPrimitiveIdDummy, 0, 0, sizeof(uint32), VET_UInt, EVertexStreamUsage::Instancing), 1, InputStreamType));
PrimitiveIdStreamIndex[TypeIndex] = StreamElements.Last().StreamIndex;
}
// 初始化声明.
InitDeclaration(StreamElements, InputStreamType);
};
// 增加PositionOnly和PositionAndNormalOnly两种顶点声明, 其中前者不需要法线.
AddDeclaration(EVertexInputStreamType::PositionOnly, false);
AddDeclaration(EVertexInputStreamType::PositionAndNormalOnly, true);
}
// 顶点声明元素列表.
FVertexDeclarationElementList Elements;
// 顶点位置
if(Data.PositionComponent.VertexBuffer != NULL)
{
Elements.Add(AccessStreamComponent(Data.PositionComponent,0));
}
// 图元id
{
const uint8 Index = static_cast<uint8>(EVertexInputStreamType::Default);
PrimitiveIdStreamIndex[Index] = -1;
if (GetType()->SupportsPrimitiveIdStream() && bCanUseGPUScene)
{
// When the VF is used for rendering in normal mesh passes, this vertex buffer and offset will be overridden
Elements.Add(AccessStreamComponent(FVertexStreamComponent(&GPrimitiveIdDummy, 0, 0, sizeof(uint32), VET_UInt, EVertexStreamUsage::Instancing), 13));
PrimitiveIdStreamIndex[Index] = Elements.Last().StreamIndex;
}
}
// 切线和法线, 切线法线才需要被顶点流使用, 副法线由shader生成.
uint8 TangentBasisAttributes[2] = { 1, 2 };
for(int32 AxisIndex = 0;AxisIndex < 2;AxisIndex++)
{
if(Data.TangentBasisComponents[AxisIndex].VertexBuffer != NULL)
{
Elements.Add(AccessStreamComponent(Data.TangentBasisComponents[AxisIndex],TangentBasisAttributes[AxisIndex]));
}
}
if (Data.ColorComponentsSRV == nullptr)
{
Data.ColorComponentsSRV = GNullColorVertexBuffer.VertexBufferSRV;
Data.ColorIndexMask = 0;
}
// 顶点颜色
ColorStreamIndex = -1;
if(Data.ColorComponent.VertexBuffer)
{
Elements.Add(AccessStreamComponent(Data.ColorComponent,3));
ColorStreamIndex = Elements.Last().StreamIndex;
}
else
{
FVertexStreamComponent NullColorComponent(&GNullColorVertexBuffer, 0, 0, VET_Color, EVertexStreamUsage::ManualFetch);
Elements.Add(AccessStreamComponent(NullColorComponent, 3));
ColorStreamIndex = Elements.Last().StreamIndex;
}
// 纹理坐标
if(Data.TextureCoordinates.Num())
{
const int32 BaseTexCoordAttribute = 4;
for(int32 CoordinateIndex = 0;CoordinateIndex < Data.TextureCoordinates.Num();CoordinateIndex++)
{
Elements.Add(AccessStreamComponent(
Data.TextureCoordinates[CoordinateIndex],
BaseTexCoordAttribute + CoordinateIndex
));
}
for (int32 CoordinateIndex = Data.TextureCoordinates.Num(); CoordinateIndex < MAX_STATIC_TEXCOORDS / 2; CoordinateIndex++)
{
Elements.Add(AccessStreamComponent(
Data.TextureCoordinates[Data.TextureCoordinates.Num() - 1],
BaseTexCoordAttribute + CoordinateIndex
));
}
}
// 光照图
if(Data.LightMapCoordinateComponent.VertexBuffer)
{
Elements.Add(AccessStreamComponent(Data.LightMapCoordinateComponent,15));
}
else if(Data.TextureCoordinates.Num())
{
Elements.Add(AccessStreamComponent(Data.TextureCoordinates[0],15));
}
// 初始化顶点声明
InitDeclaration(Elements);
const int32 DefaultBaseVertexIndex = 0;
const int32 DefaultPreSkinBaseVertexIndex = 0;
if (RHISupportsManualVertexFetch(GMaxRHIShaderPlatform) || bCanUseGPUScene)
{
SCOPED_LOADTIMER(FLocalVertexFactory_InitRHI_CreateLocalVFUniformBuffer);
UniformBuffer = CreateLocalVFUniformBuffer(this, Data.LODLightmapDataIndex, nullptr, DefaultBaseVertexIndex, DefaultPreSkinBaseVertexIndex);
}
}
// 实现FLocalVertexFactory的参数类型.
IMPLEMENT_VERTEX_FACTORY_PARAMETER_TYPE(FLocalVertexFactory, SF_Vertex, FLocalVertexFactoryShaderParameters);
// 实现FLocalVertexFactory.
IMPLEMENT_VERTEX_FACTORY_TYPE_EX(FLocalVertexFactory,"/Engine/Private/LocalVertexFactory.ush",true,true,true,true,true,true,true);
下面进入CableComponent相关类型关于FLocalVertexFactory的使用:
// Engine\Plugins\Runtime\CableComponent\Source\CableComponent\Private\CableComponent.cpp
class FCableSceneProxy final : public FPrimitiveSceneProxy
{
public:
FCableSceneProxy(UCableComponent* Component)
: FPrimitiveSceneProxy(Component)
, Material(NULL)
// 构造顶点工厂.
, VertexFactory(GetScene().GetFeatureLevel(), "FCableSceneProxy")
(......)
{
// 利用顶点工厂初始化缓冲区.
VertexBuffers.InitWithDummyData(&VertexFactory, GetRequiredVertexCount());
(......)
}
virtual ~FCableSceneProxy()
{
// 释放顶点工厂.
VertexFactory.ReleaseResource();
(......)
}
// 构建Cable网格.
void BuildCableMesh(const TArray<FVector>& InPoints, TArray<FDynamicMeshVertex>& OutVertices, TArray<int32>& OutIndices)
{
(......)
}
// 设置动态数据(渲染线程调用)
void SetDynamicData_RenderThread(FCableDynamicData* NewDynamicData)
{
// 释放旧数据.
if(DynamicData)
{
delete DynamicData;
DynamicData = NULL;
}
DynamicData = NewDynamicData;
// 从Cable点构建顶点.
TArray<FDynamicMeshVertex> Vertices;
TArray<int32> Indices;
BuildCableMesh(NewDynamicData->CablePoints, Vertices, Indices);
// 填充顶点缓冲区数据.
for (int i = 0; i < Vertices.Num(); i++)
{
const FDynamicMeshVertex& Vertex = Vertices[i];
VertexBuffers.PositionVertexBuffer.VertexPosition(i) = Vertex.Position;
VertexBuffers.StaticMeshVertexBuffer.SetVertexTangents(i, Vertex.TangentX.ToFVector(), Vertex.GetTangentY(), Vertex.TangentZ.ToFVector());
VertexBuffers.StaticMeshVertexBuffer.SetVertexUV(i, 0, Vertex.TextureCoordinate[0]);
VertexBuffers.ColorVertexBuffer.VertexColor(i) = Vertex.Color;
}
// 更新顶点缓冲区数据到RHI.
{
auto& VertexBuffer = VertexBuffers.PositionVertexBuffer;
void* VertexBufferData = RHILockVertexBuffer(VertexBuffer.VertexBufferRHI, 0, VertexBuffer.GetNumVertices() * VertexBuffer.GetStride(), RLM_WriteOnly);
FMemory::Memcpy(VertexBufferData, VertexBuffer.GetVertexData(), VertexBuffer.GetNumVertices() * VertexBuffer.GetStride());
RHIUnlockVertexBuffer(VertexBuffer.VertexBufferRHI);
}
(......)
}
virtual void GetDynamicMeshElements(const TArray<const FSceneView*>& Views, const FSceneViewFamily& ViewFamily, uint32 VisibilityMap, FMeshElementCollector& Collector) const override
{
(......)
for (int32 ViewIndex = 0; ViewIndex < Views.Num(); ViewIndex++)
{
if (VisibilityMap & (1 << ViewIndex))
{
const FSceneView* View = Views[ViewIndex];
// 构造FMeshBatch实例.
FMeshBatch& Mesh = Collector.AllocateMesh();
// 将顶点工厂实例传给FMeshBatch实例.
Mesh.VertexFactory = &VertexFactory;
(......)
Collector.AddMesh(ViewIndex, Mesh);
}
}
}
(......)
private:
// 材质
UMaterialInterface* Material;
// 顶点和索引缓冲.
FStaticMeshVertexBuffers VertexBuffers;
FCableIndexBuffer IndexBuffer;
// 顶点工厂.
FLocalVertexFactory VertexFactory;
// 动态数据.
FCableDynamicData* DynamicData;
(......)
};
由上面的代码可知,使用已有的顶点工厂的步骤并复杂,主要在于初始化、赋值和传递给FMeshBatch实例等步骤。
不过,无论是使用已有的还是自定义的顶点工厂,顶点工厂的顶点声明的顺序、类型、组件数量和插槽需要和HLSL层的FVertexFactoryInput保持一致。比如说FLocalVertexFactory::InitRHI的顶点声明顺序是位置、切线、颜色、纹理坐标、光照图,那么我们进入FLocalVertexFactory对应的HLSL文件(由IMPLEMENT_VERTEX_FACTORY_TYPE等宏指定)看看:
// Engine\Shaders\Private\LocalVertexFactory.ush
// 局部顶点工厂对应的输入结构体.
struct FVertexFactoryInput
{
// 位置
float4 Position : ATTRIBUTE0;
// 切线和颜色
#if !MANUAL_VERTEX_FETCH
#if METAL_PROFILE
float3 TangentX : ATTRIBUTE1;
// TangentZ.w contains sign of tangent basis determinant
float4 TangentZ : ATTRIBUTE2;
float4 Color : ATTRIBUTE3;
#else
half3 TangentX : ATTRIBUTE1;
// TangentZ.w contains sign of tangent basis determinant
half4 TangentZ : ATTRIBUTE2;
half4 Color : ATTRIBUTE3;
#endif
#endif
// 纹理坐标
#if NUM_MATERIAL_TEXCOORDS_VERTEX
#if !MANUAL_VERTEX_FETCH
#if GPUSKIN_PASS_THROUGH
// These must match GPUSkinVertexFactory.usf
float2 TexCoords[NUM_MATERIAL_TEXCOORDS_VERTEX] : ATTRIBUTE4;
#if NUM_MATERIAL_TEXCOORDS_VERTEX > 4
#error Too many texture coordinate sets defined on GPUSkin vertex input. Max: 4.
#endif
#else
#if NUM_MATERIAL_TEXCOORDS_VERTEX > 1
float4 PackedTexCoords4[NUM_MATERIAL_TEXCOORDS_VERTEX/2] : ATTRIBUTE4;
#endif
#if NUM_MATERIAL_TEXCOORDS_VERTEX == 1
float2 PackedTexCoords2 : ATTRIBUTE4;
#elif NUM_MATERIAL_TEXCOORDS_VERTEX == 3
float2 PackedTexCoords2 : ATTRIBUTE5;
#elif NUM_MATERIAL_TEXCOORDS_VERTEX == 5
float2 PackedTexCoords2 : ATTRIBUTE6;
#elif NUM_MATERIAL_TEXCOORDS_VERTEX == 7
float2 PackedTexCoords2 : ATTRIBUTE7;
#endif
#endif
#endif
#elif USE_PARTICLE_SUBUVS && !MANUAL_VERTEX_FETCH
float2 TexCoords[1] : ATTRIBUTE4;
#endif
(......)
};
因此可知,FVertexFactoryInput结构体的数据顺序和FLocalVertexFactory的顶点声明是一一对应的。
8.2.5 Shader Permutation
UE的Shader代码是采样的了全能着色器(Uber Shader)的设计架构,这就需要在同一个shader代码文件里增加许多各种各样的宏,以区分不同Pass、功能、Feature Level和质量等级的分支代码。在C++层,为了方便扩展、设置这些宏定义的开启及不同的值,UE采用了着色器排列(Shader Permutation)的概念。
每一个排列包含着一个唯一的哈希键值,将这组排列的值填充到HLSL,编译出对应的着色器代码。下面分析着色器排列的核心类型的定义:
// Engine\Source\Runtime\RenderCore\Public\ShaderPermutation.h
// Bool的着色器排列
struct FShaderPermutationBool
{
using Type = bool;
// 维度数量.
static constexpr int32 PermutationCount = 2;
// 是否多维的排列.
static constexpr bool IsMultiDimensional = false;
// 转换bool到int值.
static int32 ToDimensionValueId(Type E)
{
return E ? 1 : 0;
}
// 转换为定义的值.
static bool ToDefineValue(Type E)
{
return E;
}
// 从排列id转成bool.
static Type FromDimensionValueId(int32 PermutationId)
{
checkf(PermutationId == 0 || PermutationId == 1, TEXT("Invalid shader permutation dimension id %i."), PermutationId);
return PermutationId == 1;
}
};
// 整型的着色器排列
template <typename TType, int32 TDimensionSize, int32 TFirstValue=0>
struct TShaderPermutationInt
{
using Type = TType;
static constexpr int32 PermutationCount = TDimensionSize;
static constexpr bool IsMultiDimensional = false;
// 最大最小值.
static constexpr Type MinValue = static_cast<Type>(TFirstValue);
static constexpr Type MaxValue = static_cast<Type>(TFirstValue + TDimensionSize - 1);
static int32 ToDimensionValueId(Type E)
static int32 ToDefineValue(Type E);
static Type FromDimensionValueId(int32 PermutationId);
};
// 可变维度的整型着色器排列.
template <int32... Ts>
struct TShaderPermutationSparseInt
{
using Type = int32;
static constexpr int32 PermutationCount = 0;
static constexpr bool IsMultiDimensional = false;
static int32 ToDimensionValueId(Type E);
static Type FromDimensionValueId(int32 PermutationId);
};
// 着色器排列域, 数量是可变的
template <typename... Ts>
struct TShaderPermutationDomain
{
using Type = TShaderPermutationDomain<Ts...>;
static constexpr bool IsMultiDimensional = true;
static constexpr int32 PermutationCount = 1;
// 构造函数.
TShaderPermutationDomain<Ts...>() {}
explicit TShaderPermutationDomain<Ts...>(int32 PermutationId)
{
checkf(PermutationId == 0, TEXT("Invalid shader permutation id %i."), PermutationId);
}
// 设置某个维度的值.
template<class DimensionToSet>
void Set(typename DimensionToSet::Type)
{
static_assert(sizeof(typename DimensionToSet::Type) == 0, "Unknown shader permutation dimension.");
}
// 获取某个维度的值.
template<class DimensionToGet>
const typename DimensionToGet::Type Get() const
{
static_assert(sizeof(typename DimensionToGet::Type) == 0, "Unknown shader permutation dimension.");
return DimensionToGet::Type();
}
// 修改编译环境变量.
void ModifyCompilationEnvironment(FShaderCompilerEnvironment& OutEnvironment) const {}
// 数据转换.
static int32 ToDimensionValueId(const Type& PermutationVector)
{
return 0;
}
int32 ToDimensionValueId() const
{
return ToDimensionValueId(*this);
}
static Type FromDimensionValueId(const int32 PermutationId)
{
return Type(PermutationId);
}
bool operator==(const Type& Other) const
{
return true;
}
};
// 下面的宏方便编写shader的c++代码时实现和设置着色器排列.
// 声明指定名字的bool类型着色器排列
#define SHADER_PERMUTATION_BOOL(InDefineName)
// 声明指定名字的int类型着色器排列
#define SHADER_PERMUTATION_INT(InDefineName, Count)
// 声明指定名字和范围的int类型着色器排列
#define SHADER_PERMUTATION_RANGE_INT(InDefineName, Start, Count)
// 声明指定名字的稀疏int类型着色器排列
#define SHADER_PERMUTATION_SPARSE_INT(InDefineName,...)
// 声明指定名字的枚举类型着色器排列
#define SHADER_PERMUTATION_ENUM_CLASS(InDefineName, EnumName)
看上面的模板和宏定义是不是有点懵、不知所以然?没关系,结合FDeferredLightPS的使用案例,会发现着色器排列其实很简单:
// 延迟光源的PS.
class FDeferredLightPS : public FGlobalShader
{
DECLARE_SHADER_TYPE(FDeferredLightPS, Global)
// 声明各个维度的着色器排列, 注意用的是继承, 且父类是用SHADER_PERMUTATION_xxx定义的类型.
// 注意父类的名词(如LIGHT_SOURCE_SHAPE, USE_SOURCE_TEXTURE, USE_IES_PROFILE, ...)就是在HLSL代码中的宏名称.
class FSourceShapeDim : SHADER_PERMUTATION_ENUM_CLASS("LIGHT_SOURCE_SHAPE", ELightSourceShape);
class FSourceTextureDim : SHADER_PERMUTATION_BOOL("USE_SOURCE_TEXTURE");
class FIESProfileDim : SHADER_PERMUTATION_BOOL("USE_IES_PROFILE");
class FInverseSquaredDim : SHADER_PERMUTATION_BOOL("INVERSE_SQUARED_FALLOFF");
class FVisualizeCullingDim : SHADER_PERMUTATION_BOOL("VISUALIZE_LIGHT_CULLING");
class FLightingChannelsDim : SHADER_PERMUTATION_BOOL("USE_LIGHTING_CHANNELS");
class FTransmissionDim : SHADER_PERMUTATION_BOOL("USE_TRANSMISSION");
class FHairLighting : SHADER_PERMUTATION_INT("USE_HAIR_LIGHTING", 2);
class FAtmosphereTransmittance : SHADER_PERMUTATION_BOOL("USE_ATMOSPHERE_TRANSMITTANCE");
class FCloudTransmittance : SHADER_PERMUTATION_BOOL("USE_CLOUD_TRANSMITTANCE");
class FAnistropicMaterials : SHADER_PERMUTATION_BOOL("SUPPORTS_ANISOTROPIC_MATERIALS");
// 声明着色器排列域, 包含了上面定义的所有维度.
using FPermutationDomain = TShaderPermutationDomain<
FSourceShapeDim,
FSourceTextureDim,
FIESProfileDim,
FInverseSquaredDim,
FVisualizeCullingDim,
FLightingChannelsDim,
FTransmissionDim,
FHairLighting,
FAtmosphereTransmittance,
FCloudTransmittance,
FAnistropicMaterials>;
// 是否需要编译指定的着色器排列.
static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters)
{
// 获取着色器排列的值.
FPermutationDomain PermutationVector(Parameters.PermutationId);
// 如果是平行光, 那么IES光照和逆反的衰减将没有任何意义, 可以不编译.
if( PermutationVector.Get< FSourceShapeDim >() == ELightSourceShape::Directional && (
PermutationVector.Get< FIESProfileDim >() ||
PermutationVector.Get< FInverseSquaredDim >() ) )
{
return false;
}
// 如果不是平行光, 那么大气和云体透射将没有任何意义, 可以不编译.
if (PermutationVector.Get< FSourceShapeDim >() != ELightSourceShape::Directional && (PermutationVector.Get<FAtmosphereTransmittance>() || PermutationVector.Get<FCloudTransmittance>()))
{
return false;
}
(......)
return IsFeatureLevelSupported(Parameters.Platform, ERHIFeatureLevel::SM5);
}
(......)
};
// 渲染光源.
void FDeferredShadingSceneRenderer::RenderLight(FRHICommandList& RHICmdList, ...)
{
(......)
for (int32 ViewIndex = 0; ViewIndex < Views.Num(); ViewIndex++)
{
FViewInfo& View = Views[ViewIndex];
(......)
if (LightSceneInfo->Proxy->GetLightType() == LightType_Directional)
{
(......)
// 声明FDeferredLightPS的着色器排列的实例.
FDeferredLightPS::FPermutationDomain PermutationVector;
// 根据渲染状态填充排列值.
PermutationVector.Set< FDeferredLightPS::FSourceShapeDim >( ELightSourceShape::Directional );
PermutationVector.Set< FDeferredLightPS::FIESProfileDim >( false );
PermutationVector.Set< FDeferredLightPS::FInverseSquaredDim >( false );
PermutationVector.Set< FDeferredLightPS::FVisualizeCullingDim >( View.Family->EngineShowFlags.VisualizeLightCulling );
PermutationVector.Set< FDeferredLightPS::FLightingChannelsDim >( View.bUsesLightingChannels );
PermutationVector.Set< FDeferredLightPS::FAnistropicMaterials >(ShouldRenderAnisotropyPass());
PermutationVector.Set< FDeferredLightPS::FTransmissionDim >( bTransmission );
PermutationVector.Set< FDeferredLightPS::FHairLighting>(0);
PermutationVector.Set< FDeferredLightPS::FAtmosphereTransmittance >(bAtmospherePerPixelTransmittance);
PermutationVector.Set< FDeferredLightPS::FCloudTransmittance >(bLight0CloudPerPixelTransmittance || bLight1CloudPerPixelTransmittance);
// 用填充好的排列从视图的ShaderMap获取对应的PS实例.
TShaderMapRef< FDeferredLightPS > PixelShader( View.ShaderMap, PermutationVector );
// 填充PS的其它数据.
GraphicsPSOInit.BoundShaderState.VertexDeclarationRHI = GFilterVertexDeclaration.VertexDeclarationRHI;
GraphicsPSOInit.BoundShaderState.VertexShaderRHI = VertexShader.GetVertexShader();
GraphicsPSOInit.BoundShaderState.PixelShaderRHI = PixelShader.GetPixelShader();
SetGraphicsPipelineState(RHICmdList, GraphicsPSOInit);
PixelShader->SetParameters(RHICmdList, View, LightSceneInfo, ScreenShadowMaskTexture, LightingChannelsTexture, &RenderLightParams);
(......)
}
(......)
}
由此可知,着色器排列本质上只是一组拥有不定维度的键值,在编译shader阶段,shader编译器会尽量为每个不同的排列生成对应的shader实例代码,当然也可以通过ShouldCompilePermutation排除掉部分无意义的排列。预编译好的所有shader存放于视图的ShaderMap中。每个维度的键值可在运行时动态生成,然后用它们组合成的排列域去视图的ShaderMap获取对应的编译好的shader代码,从而进行后续的着色器数据设置和渲染。
另外,值得一提的是,排列维度父类的名词(如LIGHT_SOURCE_SHAPE, USE_SOURCE_TEXTURE, USE_IES_PROFILE, ...)就是在HLSL代码中的宏名称。比如FSourceShapeDim正是控制着HLSL代码的LIGHT_SOURCE_SHAPE,根据FSourceShapeDim的值会选用不同片段的代码,从而控制不同版本和分支的shader代码。
8.3 Shader机制
本章主要分析Shader的部分底层机制,比如Shader Map的存储机制,Shader的编译和缓存策略等。
8.3.1 Shader Map
ShaderMap是存储编译后的shader代码,分为FGlobalShaderMap、FMaterialShaderMap、FMeshMaterialShaderMap三种类型。
8.3.1.1 FShaderMapBase
本小节先阐述Shader Map相关的基础类型和概念,如下:
// Engine\Source\Runtime\Core\Public\Serialization\MemoryImage.h
// 指针表基类.
class FPointerTableBase
{
public:
virtual ~FPointerTableBase() {}
virtual int32 AddIndexedPointer(const FTypeLayoutDesc& TypeDesc, void* Ptr) = 0;
virtual void* GetIndexedPointer(const FTypeLayoutDesc& TypeDesc, uint32 i) const = 0;
};
// Engine\Source\Runtime\RenderCore\Public\Shader.h
// 用以序列化, 反序列化, 编译, 缓存一个专用的shader类. 一个FShaderType可以跨多个维度管理FShader的多个实例,如EShaderPlatform,或permutation id. FShaderType的排列数量简单地由GetPermutationCount()给出。
class FShaderType
{
public:
// 着色器种类, 有全局, 材质, 网格材质, Niagara等.
enum class EShaderTypeForDynamicCast : uint32
{
Global,
Material,
MeshMaterial,
Niagara,
OCIO,
NumShaderTypes,
};
(......)
// 静态数据获取接口.
static TLinkedList<FShaderType*>*& GetTypeList();
static FShaderType* GetShaderTypeByName(const TCHAR* Name);
static TArray<const FShaderType*> GetShaderTypesByFilename(const TCHAR* Filename);
static TMap<FHashedName, FShaderType*>& GetNameToTypeMap();
static const TArray<FShaderType*>& GetSortedTypes(EShaderTypeForDynamicCast Type);
static void Initialize(const TMap<FString, TArray<const TCHAR*> >& ShaderFileToUniformBufferVariables);
static void Uninitialize();
// 构造函数.
FShaderType(...);
virtual ~FShaderType();
FShader* ConstructForDeserialization() const;
FShader* ConstructCompiled(const FShader::CompiledShaderInitializerType& Initializer) const;
bool ShouldCompilePermutation(...) const;
void ModifyCompilationEnvironment(..) const;
bool ValidateCompiledResult(...) const;
// 基于shader type的源码和包含计算哈希值.
const FSHAHash& GetSourceHash(EShaderPlatform ShaderPlatform) const;
// 获取FShaderType指针的哈希值.
friend uint32 GetTypeHash(FShaderType* Ref);
// 访问接口.
(......)
void AddReferencedUniformBufferIncludes(FShaderCompilerEnvironment& OutEnvironment, FString& OutSourceFilePrefix, EShaderPlatform Platform);
void FlushShaderFileCache(const TMap<FString, TArray<const TCHAR*> >& ShaderFileToUniformBufferVariables);
void GetShaderStableKeyParts(struct FStableShaderKeyAndValue& SaveKeyVal);
private:
EShaderTypeForDynamicCast ShaderTypeForDynamicCast;
const FTypeLayoutDesc* TypeLayout;
// 名称.
const TCHAR* Name;
// 类型名.
FName TypeName;
// 哈希名
FHashedName HashedName;
// 哈希的源码文件名.
FHashedName HashedSourceFilename;
// 源文件名.
const TCHAR* SourceFilename;
// 入口命.
const TCHAR* FunctionName;
// 着色频率.
uint32 Frequency;
uint32 TypeSize;
// 排列数量.
int32 TotalPermutationCount;
(......)
// 全局的列表.
TLinkedList<FShaderType*> GlobalListLink;
protected:
bool bCachedUniformBufferStructDeclarations;
// 引用的Uniform Buffer包含的缓存.
TMap<const TCHAR*, FCachedUniformBufferDeclaration> ReferencedUniformBufferStructsCache;
};
// 着色器映射表指针表
class FShaderMapPointerTable : public FPointerTableBase
{
public:
virtual int32 AddIndexedPointer(const FTypeLayoutDesc& TypeDesc, void* Ptr) override;
virtual void* GetIndexedPointer(const FTypeLayoutDesc& TypeDesc, uint32 i) const override;
virtual void SaveToArchive(FArchive& Ar, void* FrozenContent, bool bInlineShaderResources) const;
virtual void LoadFromArchive(FArchive& Ar, void* FrozenContent, bool bInlineShaderResources, bool bLoadedByCookedMaterial);
// 着色器类型
TPtrTable<FShaderType> ShaderTypes;
// 顶点工厂类型
TPtrTable<FVertexFactoryType> VFTypes;
};
// 包含编译期状态的着色器管线实例.
class FShaderPipeline
{
public:
explicit FShaderPipeline(const FShaderPipelineType* InType);
~FShaderPipeline();
// 增加着色器.
void AddShader(FShader* Shader, int32 PermutationId);
// 获取着色器数量.
inline uint32 GetNumShaders() const;
// 查找shader.
template<typename ShaderType>
ShaderType* GetShader(const FShaderMapPointerTable& InPtrTable);
FShader* GetShader(EShaderFrequency Frequency);
const FShader* GetShader(EShaderFrequency Frequency) const;
inline TArray<TShaderRef<FShader>> GetShaders(const FShaderMapBase& InShaderMap) const;
// 校验.
void Validate(const FShaderPipelineType* InPipelineType) const;
// 处理编译好的着色器代码.
void Finalize(const FShaderMapResourceCode* Code);
(......)
enum EFilter
{
EAll, // All pipelines
EOnlyShared, // Only pipelines with shared shaders
EOnlyUnique, // Only pipelines with unique shaders
};
// 哈希值.
LAYOUT_FIELD(FHashedName, TypeName);
// 所有着色频率的FShader实例.
LAYOUT_ARRAY(TMemoryImagePtr<FShader>, Shaders, SF_NumGraphicsFrequencies);
// 排列id.
LAYOUT_ARRAY(int32, PermutationIds, SF_NumGraphicsFrequencies);
};
// 着色器映射表内容.
class FShaderMapContent
{
public:
struct FProjectShaderPipelineToKey
{
inline FHashedName operator()(const FShaderPipeline* InShaderPipeline)
{ return InShaderPipeline->TypeName; }
};
explicit FShaderMapContent(EShaderPlatform InPlatform);
~FShaderMapContent();
EShaderPlatform GetShaderPlatform() const;
// 校验.
void Validate(const FShaderMapBase& InShaderMap);
// 查找shader.
template<typename ShaderType>
ShaderType* GetShader(int32 PermutationId = 0) const;
template<typename ShaderType>
ShaderType* GetShader( const typename ShaderType::FPermutationDomain& PermutationVector ) const;
FShader* GetShader(FShaderType* ShaderType, int32 PermutationId = 0) const;
FShader* GetShader(const FHashedName& TypeName, int32 PermutationId = 0) const;
// 检测是否有指定shader.
bool HasShader(const FHashedName& TypeName, int32 PermutationId) const;
bool HasShader(const FShaderType* Type, int32 PermutationId) const;
inline TArrayView<const TMemoryImagePtr<FShader>> GetShaders() const;
inline TArrayView<const TMemoryImagePtr<FShaderPipeline>> GetShaderPipelines() const;
// 增加, 查找shader或Pipeline接口.
void AddShader(const FHashedName& TypeName, int32 PermutationId, FShader* Shader);
FShader* FindOrAddShader(const FHashedName& TypeName, int32 PermutationId, FShader* Shader);
void AddShaderPipeline(FShaderPipeline* Pipeline);
FShaderPipeline* FindOrAddShaderPipeline(FShaderPipeline* Pipeline);
// 删除接口.
void RemoveShaderTypePermutaion(const FHashedName& TypeName, int32 PermutationId);
inline void RemoveShaderTypePermutaion(const FShaderType* Type, int32 PermutationId);
void RemoveShaderPipelineType(const FShaderPipelineType* ShaderPipelineType);
// 获取着色器列表.
void GetShaderList(const FShaderMapBase& InShaderMap, const FSHAHash& InMaterialShaderMapHash, TMap<FShaderId, TShaderRef<FShader>>& OutShaders) const;
void GetShaderList(const FShaderMapBase& InShaderMap, TMap<FHashedName, TShaderRef<FShader>>& OutShaders) const;
// 获取着色器管线列表.
void GetShaderPipelineList(const FShaderMapBase& InShaderMap, TArray<FShaderPipelineRef>& OutShaderPipelines, FShaderPipeline::EFilter Filter) const;
(.......)
// 获取着色器最大的指令数.
uint32 GetMaxNumInstructionsForShader(const FShaderMapBase& InShaderMap, FShaderType* ShaderType) const;
// 保存编译好的shader代码.
void Finalize(const FShaderMapResourceCode* Code);
// 更新哈希值.
void UpdateHash(FSHA1& Hasher) const;
protected:
using FMemoryImageHashTable = THashTable<FMemoryImageAllocator>;
// 着色器哈希.
LAYOUT_FIELD(FMemoryImageHashTable, ShaderHash);
// 着色器类型.
LAYOUT_FIELD(TMemoryImageArray<FHashedName>, ShaderTypes);
// 着色器排列列表.
LAYOUT_FIELD(TMemoryImageArray<int32>, ShaderPermutations);
// 着色器实例列表.
LAYOUT_FIELD(TMemoryImageArray<TMemoryImagePtr<FShader>>, Shaders);
// 着色器管线列表.
LAYOUT_FIELD(TMemoryImageArray<TMemoryImagePtr<FShaderPipeline>>, ShaderPipelines);
// 着色器编译所在的平台.
LAYOUT_FIELD(TEnumAsByte<EShaderPlatform>, Platform);
};
// FShaderMa的基类.
class FShaderMapBase
{
public:
(......)
private:
const FTypeLayoutDesc& ContentTypeLayout;
// ShaderMap资源.
TRefCountPtr<FShaderMapResource> Resource;
// ShaderMap资源代码.
TRefCountPtr<FShaderMapResourceCode> Code;
// ShaderMap指针表.
FShaderMapPointerTable* PointerTable;
// ShaderMap内容.
FShaderMapContent* Content;
// 内容尺寸.
uint32 FrozenContentSize;
// 着色器数量.
uint32 NumFrozenShaders;
};
// 着色器映射表. 需指定FShaderMapContent和FShaderMapPointerTable
template<typename ContentType, typename PointerTableType = FShaderMapPointerTable>
class TShaderMap : public FShaderMapBase
{
public:
inline const PointerTableType& GetPointerTable();
inline const ContentType* GetContent() const;
inline ContentType* GetMutableContent();
void FinalizeContent()
{
ContentType* LocalContent = this->GetMutableContent();
LocalContent->Finalize(this->GetResourceCode());
FShaderMapBase::FinalizeContent();
}
protected:
TShaderMap();
virtual FShaderMapPointerTable* CreatePointerTable();
};
// 着色器管线引用.
class FShaderPipelineRef
{
public:
FShaderPipelineRef();
FShaderPipelineRef(FShaderPipeline* InPipeline, const FShaderMapBase& InShaderMap);
(......)
// 获取着色器
template<typename ShaderType>
TShaderRef<ShaderType> GetShader() const;
TShaderRef<FShader> GetShader(EShaderFrequency Frequency) const;
inline TArray<TShaderRef<FShader>> GetShaders() const;
// 获取着色管线, 资源等接口.
inline FShaderPipeline* GetPipeline() const;
FShaderMapResource* GetResource() const;
const FShaderMapPointerTable& GetPointerTable() const;
inline FShaderPipeline* operator->() const;
private:
FShaderPipeline* ShaderPipeline; // 着色器管线.
const FShaderMapBase* ShaderMap; // 着色器映射表.
};
上面的很多类型是基类,具体的逻辑需要由子类完成。
8.3.1.2 FGlobalShaderMap
FGlobalShaderMap保存并管理着所有编译好的FGlobalShader代码,它的定义和相关类型如下所示:
// Engine\Source\Runtime\RenderCore\Public\GlobalShader.h
// 用于处理最简单的着色器(没有材质和顶点工厂链接)的shader meta type, 每个简单的shader都应该只有一个实例.
class FGlobalShaderType : public FShaderType
{
friend class FGlobalShaderTypeCompiler;
public:
typedef FShader::CompiledShaderInitializerType CompiledShaderInitializerType;
FGlobalShaderType(...);
bool ShouldCompilePermutation(EShaderPlatform Platform, int32 PermutationId) const;
void SetupCompileEnvironment(EShaderPlatform Platform, int32 PermutationId, FShaderCompilerEnvironment& Environment);
};
// 全局着色器子表.
class FGlobalShaderMapContent : public FShaderMapContent
{
(......)
public:
const FHashedName& GetHashedSourceFilename();
private:
inline FGlobalShaderMapContent(EShaderPlatform InPlatform, const FHashedName& InHashedSourceFilename);
// 哈希的源文件名.
LAYOUT_FIELD(FHashedName, HashedSourceFilename);
};
class FGlobalShaderMapSection : public TShaderMap<FGlobalShaderMapContent, FShaderMapPointerTable>
{
(......)
private:
inline FGlobalShaderMapSection();
inline FGlobalShaderMapSection(EShaderPlatform InPlatform, const FHashedName& InHashedSourceFilename);
TShaderRef<FShader> GetShader(FShaderType* ShaderType, int32 PermutationId = 0) const;
FShaderPipelineRef GetShaderPipeline(const FShaderPipelineType* PipelineType) const;
};
// 全局ShaderMap.
class FGlobalShaderMap
{
public:
explicit FGlobalShaderMap(EShaderPlatform InPlatform);
~FGlobalShaderMap();
// 根据着色器类型和排列id获取编译后的shader代码.
TShaderRef<FShader> GetShader(FShaderType* ShaderType, int32 PermutationId = 0) const;
// 根据排列id获取编译后的shader代码.
template<typename ShaderType>
TShaderRef<ShaderType> GetShader(int32 PermutationId = 0) const
{
TShaderRef<FShader> Shader = GetShader(&ShaderType::StaticType, PermutationId);
return TShaderRef<ShaderType>::Cast(Shader);
}
// 根据着色器类型内的排列获取编译后的shader代码.
template<typename ShaderType>
TShaderRef<ShaderType> GetShader(const typename ShaderType::FPermutationDomain& PermutationVector) const
{
return GetShader<ShaderType>(PermutationVector.ToDimensionValueId());
}
// 检测是否有指定的shader.
bool HasShader(FShaderType* Type, int32 PermutationId) const
{
return GetShader(Type, PermutationId).IsValid();
}
// 获取着色器管线
FShaderPipelineRef GetShaderPipeline(const FShaderPipelineType* PipelineType) const;
// 是否有着色器管线.
bool HasShaderPipeline(const FShaderPipelineType* ShaderPipelineType) const
{
return GetShaderPipeline(ShaderPipelineType).IsValid();
}
bool IsEmpty() const;
void Empty();
void ReleaseAllSections();
// 查找或增加shader.
FShader* FindOrAddShader(const FShaderType* ShaderType, int32 PermutationId, FShader* Shader);
// 查找或增加shader管线.
FShaderPipeline* FindOrAddShaderPipeline(const FShaderPipelineType* ShaderPipelineType, FShaderPipeline* ShaderPipeline);
// 删除接口
void RemoveShaderTypePermutaion(const FShaderType* Type, int32 PermutationId);
void RemoveShaderPipelineType(const FShaderPipelineType* ShaderPipelineType);
// ShaderMapSection操作.
void AddSection(FGlobalShaderMapSection* InSection);
FGlobalShaderMapSection* FindSection(const FHashedName& HashedShaderFilename);
FGlobalShaderMapSection* FindOrAddSection(const FShaderType* ShaderType);
// IO接口.
void LoadFromGlobalArchive(FArchive& Ar);
void SaveToGlobalArchive(FArchive& Ar);
// 清理所有shader.
void BeginCreateAllShaders();
(......)
private:
// 存储了FGlobalShaderMapSection的映射表.
TMap<FHashedName, FGlobalShaderMapSection*> SectionMap;
EShaderPlatform Platform;
};
// 全局ShaderMap的列表, 其中SP_NumPlatforms是49.
extern RENDERCORE_API FGlobalShaderMap* GGlobalShaderMap[SP_NumPlatforms];
上面涉及到了ShaderMap的Content、Section、PointerTable、ShaderType等等方面的类型和概念,数据多,关系复杂,不过抽象成UML图之后就简单明了多了:
FShaderTypeFGlobalShaderTypeFPointerTableBaseFShaderMapPointerTableFShaderMapContentFGlobalShaderMapContentFShaderMapBaseTShaderMapFGlobalShaderMapSectionFShaderPipelineFShaderPipelineRef
以上类图为了简明,只展示了继承关系,若是添加关联、聚合、组合等关系之后,则是以下的模样:
FShaderTypeFGlobalShaderTypeFPointerTableBaseFShaderMapPointerTableFShaderMapContentFHashedName ShaderTypesFShader ShadersFShaderPipeline ShaderPipelinesFGlobalShaderMapContentFHashedName HashedSourceFilenameFShaderMapBaseFShaderMapPointerTable* PointerTableFShaderMapContent* ContentTShaderMapFGlobalShaderMapSectionFShaderPipelineFShader Shaders[5]FShaderPipelineRefFShaderPipeline* ShaderPipelineFShaderFGlobalShaderMapTMap SectionMap
上面阐述完了FGlobalShaderMap及其核心类的关联,下面再看看它是任何被应用到实际渲染中的。首先是在GlobalShader.h和GlobalShader.cpp声明和定义了FGlobalShaderMap的实例和相关接口:
// Engine\Source\Runtime\RenderCore\Private\GlobalShader.h
// 声明可外部访问的FGlobalShaderMap列表.
extern RENDERCORE_API FGlobalShaderMap* GGlobalShaderMap[SP_NumPlatforms];
// 获取指定着色平台的FGlobalShaderMap.
extern RENDERCORE_API FGlobalShaderMap* GetGlobalShaderMap(EShaderPlatform Platform);
// 获取指定FeatureLevel的FGlobalShaderMap.
inline FGlobalShaderMap* GetGlobalShaderMap(ERHIFeatureLevel::Type FeatureLevel)
{
return GetGlobalShaderMap(GShaderPlatformForFeatureLevel[FeatureLevel]);
}
// Engine\Source\Runtime\RenderCore\Private\GlobalShader.cpp
// 声明所有着色平台的FGlobalShaderMap.
FGlobalShaderMap* GGlobalShaderMap[SP_NumPlatforms] = {};
// 获取FGlobalShaderMap.
FGlobalShaderMap* GetGlobalShaderMap(EShaderPlatform Platform)
{
return GGlobalShaderMap[Platform];
}
不过上面只是定义了GGlobalShaderMap,数组内只是一个空的列表,真正的创建堆栈链如下所示:
// Engine\Source\Runtime\Launch\Private\LaunchEngineLoop.cpp
// 引擎预初始化.
int32 FEngineLoop::PreInitPreStartupScreen(const TCHAR* CmdLine)
{
(......)
// 是否开启shader编译, 一般情况下都会开启.
bool bEnableShaderCompile = !FParse::Param(FCommandLine::Get(), TEXT("NoShaderCompile"));
(......)
if (bEnableShaderCompile && !IsRunningDedicatedServer() && !bIsCook)
{
(......)
// 编译GlobalShaderMap
CompileGlobalShaderMap(false);
(......)
}
(......)
}
// Engine\Source\Runtime\Engine\Private\ShaderCompiler\ShaderCompiler.cpp
void CompileGlobalShaderMap(EShaderPlatform Platform, const ITargetPlatform* TargetPlatform, bool bRefreshShaderMap)
{
(......)
// 如果对应平台的GlobalShaderMap未创建, 则创建之.
if (!GGlobalShaderMap[Platform])
{
(......)
// 创建对应平台的FGlobalShaderMap.
GGlobalShaderMap[Platform] = new FGlobalShaderMap(Platform);
// Cooked模式.
if (FPlatformProperties::RequiresCookedData())
{
(......)
}
// Uncooked模式
else
{
// FGlobalShaderMap的id.
FGlobalShaderMapId ShaderMapId(Platform);
const int32 ShaderFilenameNum = ShaderMapId.GetShaderFilenameToDependeciesMap().Num();
const float ProgressStep = 25.0f / ShaderFilenameNum;
TArray<uint32> AsyncDDCRequestHandles;
AsyncDDCRequestHandles.SetNum(ShaderFilenameNum);
int32 HandleIndex = 0;
// 提交DDC请求.
for (const auto& ShaderFilenameDependencies : ShaderMapId.GetShaderFilenameToDependeciesMap())
{
SlowTask.EnterProgressFrame(ProgressStep);
const FString DataKey = GetGlobalShaderMapKeyString(ShaderMapId, Platform, TargetPlatform, ShaderFilenameDependencies.Value);
AsyncDDCRequestHandles[HandleIndex] = GetDerivedDataCacheRef().GetAsynchronous(*DataKey, TEXT("GlobalShaderMap"_SV));
++HandleIndex;
}
// 处理已经结束的DDC请求.
TArray<uint8> CachedData;
HandleIndex = 0;
for (const auto& ShaderFilenameDependencies : ShaderMapId.GetShaderFilenameToDependeciesMap())
{
SlowTask.EnterProgressFrame(ProgressStep);
CachedData.Reset();
GetDerivedDataCacheRef().WaitAsynchronousCompletion(AsyncDDCRequestHandles[HandleIndex]);
if (GetDerivedDataCacheRef().GetAsynchronousResults(AsyncDDCRequestHandles[HandleIndex], CachedData))
{
FMemoryReader MemoryReader(CachedData);
GGlobalShaderMap[Platform]->AddSection(FGlobalShaderMapSection::CreateFromArchive(MemoryReader));
}
else
{
// 没有在DDC中找到, 忽略之.
}
++HandleIndex;
}
}
// 如果有shader没有被加载, 编译之.
VerifyGlobalShaders(Platform, bLoadedFromCacheFile);
// 创建所有着色器.
if (GCreateShadersOnLoad && Platform == GMaxRHIShaderPlatform)
{
GGlobalShaderMap[Platform]->BeginCreateAllShaders();
}
}
}
以上可知,FGlobalShaderMap是在引擎预初始化阶段就被创建出实例,然后会尝试从DDC中读取已经编译好的shader数据。在此之后,其它模块就可以正常访问和操作FGlobalShaderMap的对象了。
另外,在FViewInfo内部,也存有FGlobalShaderMap的实例,不过它也是通过GetGlobalShaderMap获取的实例:
// Engine\Source\Runtime\Renderer\Private\SceneRendering.h
class FViewInfo : public FSceneView
{
public:
(......)
FGlobalShaderMap* ShaderMap;
(......)
};
// Engine\Source\Runtime\Renderer\Private\SceneRendering.cpp
void FViewInfo::Init()
{
(......)
ShaderMap = GetGlobalShaderMap(FeatureLevel);
(......)
}
如此一来,渲染模块内的大多数逻辑都可以方便地获取到FViewInfo的实例,因此也就可以方便地访问FGlobalShaderMap的实例(还不需要指定FeatureLevel)。
8.3.1.3 FMaterialShaderMap
FMaterialShaderMap存储和管理着一组FMaterialShader实例的对象。它和相关的类型定义如下:
// Engine\Source\Runtime\Engine\Public\MaterialShared.h
// 材质ShaderMap内容.
class FMaterialShaderMapContent : public FShaderMapContent
{
public:
(......)
inline uint32 GetNumShaders() const;
inline uint32 GetNumShaderPipelines() const;
private:
struct FProjectMeshShaderMapToKey
{
inline const FHashedName& operator()(const FMeshMaterialShaderMap* InShaderMap) { return InShaderMap->GetVertexFactoryTypeName(); }
};
// 获取/增加/删除操作.
FMeshMaterialShaderMap* GetMeshShaderMap(const FHashedName& VertexFactoryTypeName) const;
void AddMeshShaderMap(const FVertexFactoryType* VertexFactoryType, FMeshMaterialShaderMap* MeshShaderMap);
void RemoveMeshShaderMap(const FVertexFactoryType* VertexFactoryType);
// 有序的网格着色器映射表, 通过VFType->GetId()索引, 用于运行时快速查找.
LAYOUT_FIELD(TMemoryImageArray<TMemoryImagePtr<FMeshMaterialShaderMap>>, OrderedMeshShaderMaps);
// 材质编译输出.
LAYOUT_FIELD(FMaterialCompilationOutput, MaterialCompilationOutput);
// 着色器内容哈希.
LAYOUT_FIELD(FSHAHash, ShaderContentHash);
LAYOUT_FIELD_EDITORONLY(TMemoryImageArray<FMaterialProcessedSource>, ShaderProcessedSource);
LAYOUT_FIELD_EDITORONLY(FMemoryImageString, FriendlyName);
LAYOUT_FIELD_EDITORONLY(FMemoryImageString, DebugDescription);
LAYOUT_FIELD_EDITORONLY(FMemoryImageString, MaterialPath);
};
// 材质着色器映射表, 父类是TShaderMap.
class FMaterialShaderMap : public TShaderMap<FMaterialShaderMapContent, FShaderMapPointerTable>, public FDeferredCleanupInterface
{
public:
using Super = TShaderMap<FMaterialShaderMapContent, FShaderMapPointerTable>;
// 查找指定id和平台的FMaterialShaderMap实例.
static TRefCountPtr<FMaterialShaderMap> FindId(const FMaterialShaderMapId& ShaderMapId, EShaderPlatform Platform);
(......)
// ShaderMap interface
// 获取着色器实例.
TShaderRef<FShader> GetShader(FShaderType* ShaderType, int32 PermutationId = 0) const;
template<typename ShaderType> TShaderRef<ShaderType> GetShader(int32 PermutationId = 0) const;
template<typename ShaderType> TShaderRef<ShaderType> GetShader(const typename ShaderType::FPermutationDomain& PermutationVector) const;
uint32 GetMaxNumInstructionsForShader(FShaderType* ShaderType) const;
void FinalizeContent();
// 编译一个材质的着色器并缓存到shader map中.
void Compile(FMaterial* Material,const FMaterialShaderMapId& ShaderMapId, TRefCountPtr<FShaderCompilerEnvironment> MaterialEnvironment, const FMaterialCompilationOutput& InMaterialCompilationOutput, EShaderPlatform Platform, bool bSynchronousCompile);
// 检测是否有shader丢失.
bool IsComplete(const FMaterial* Material, bool bSilent);
// 尝试增加已有的编译任务.
bool TryToAddToExistingCompilationTask(FMaterial* Material);
// 构建在shader map的shader列表.
void GetShaderList(TMap<FShaderId, TShaderRef<FShader>>& OutShaders) const;
void GetShaderList(TMap<FHashedName, TShaderRef<FShader>>& OutShaders) const;
void GetShaderPipelineList(TArray<FShaderPipelineRef>& OutShaderPipelines) const;
uint32 GetShaderNum() const;
// 注册一个材质着色器映射表到全局表中, 那样就可以被材质使用.
void Register(EShaderPlatform InShaderPlatform);
// Reference counting.
void AddRef();
void Release();
// 删除指定shader type的所有在缓存的入口.
void FlushShadersByShaderType(const FShaderType* ShaderType);
void FlushShadersByShaderPipelineType(const FShaderPipelineType* ShaderPipelineType);
void FlushShadersByVertexFactoryType(const FVertexFactoryType* VertexFactoryType);
static void RemovePendingMaterial(FMaterial* Material);
static const FMaterialShaderMap* GetShaderMapBeingCompiled(const FMaterial* Material);
// Accessors.
FMeshMaterialShaderMap* GetMeshShaderMap(FVertexFactoryType* VertexFactoryType) const;
FMeshMaterialShaderMap* GetMeshShaderMap(const FHashedName& VertexFactoryTypeName) const;
const FMaterialShaderMapId& GetShaderMapId() const;
(......)
private:
// 全局的材质shader map.
static TMap<FMaterialShaderMapId,FMaterialShaderMap*> GIdToMaterialShaderMap[SP_NumPlatforms];
static FCriticalSection GIdToMaterialShaderMapCS;
// 正在编译的材质.
static TMap<TRefCountPtr<FMaterialShaderMap>, TArray<FMaterial*> > ShaderMapsBeingCompiled;
// 着色器映射表id.
FMaterialShaderMapId ShaderMapId;
// 编译期间的id.
uint32 CompilingId;
// 对应的平台.
const ITargetPlatform* CompilingTargetPlatform;
// 被引用的数量.
mutable int32 NumRefs;
// 标记
bool bDeletedThroughDeferredCleanup;
uint32 bRegistered : 1;
uint32 bCompilationFinalized : 1;
uint32 bCompiledSuccessfully : 1;
uint32 bIsPersistent : 1;
(......)
};
FMaterialShaderMap和FGlobalShaderMap不一样的是,它会额外关联一个材质和一个顶点工厂。对于单个FMaterialShaderMap的内部数据内容,如下所示:
FMaterialShaderMap
FLightFunctionPixelShader - FMaterialShaderType
FLocalVertexFactory - FVertexFactoryType
TDepthOnlyPS - FMeshMaterialShaderType
TDepthOnlyVS - FMeshMaterialShaderType
TBasePassPS - FMeshMaterialShaderType
TBasePassVS - FMeshMaterialShaderType
(......)
FGPUSkinVertexFactory - FVertexFactoryType
(......)
由于FMaterialShaderMap跟材质蓝图绑定的,因为它是FMaterial的一个成员:
// Engine\Source\Runtime\Engine\Public\MaterialShared.h
class FMaterial
{
public:
// 获取材质的shader实例.
TShaderRef<FShader> GetShader(class FMeshMaterialShaderType* ShaderType, FVertexFactoryType* VertexFactoryType, int32 PermutationId, bool bFatalIfMissing = true) const;
(......)
private:
// 游戏线程的材质ShaderMap
TRefCountPtr<FMaterialShaderMap> GameThreadShaderMap;
// 渲染线程的材质ShaderMap
TRefCountPtr<FMaterialShaderMap> RenderingThreadShaderMap;
(......)
};
// Engine\Source\Runtime\Engine\Private\Materials\MaterialShared.cpp
TShaderRef<FShader> FMaterial::GetShader(FMeshMaterialShaderType* ShaderType, FVertexFactoryType* VertexFactoryType, int32 PermutationId, bool bFatalIfMissing) const
{
// 从RenderingThreadShaderMap获取shader.
const FMeshMaterialShaderMap* MeshShaderMap = RenderingThreadShaderMap->GetMeshShaderMap(VertexFactoryType);
FShader* Shader = MeshShaderMap ? MeshShaderMap->GetShader(ShaderType, PermutationId) : nullptr;
(......)
// 返回FShader引用.
return TShaderRef<FShader>(Shader, *RenderingThreadShaderMap);
}
因此可以找到,每个FMaterial都有一个FMaterialShaderMap(游戏线程一个,渲染线程一个),如果要获取FMaterial的指定类型的Shader,就需要从该FMaterial的FMaterialShaderMap实例中获取,从而完成了它们之间的链接。
8.3.1.4 FMeshMaterialShaderMap
以上小节阐述了,FGlobalShaderMap存储和管理FGlobalShader,而FMaterialShaderMap存储和管理FMaterialShader,相应地,FMeshMaterialShaderMap则存储和管理FMeshMaterialShader。它的定义如下:
// Engine\Source\Runtime\Engine\Public\MaterialShared.h
class FMeshMaterialShaderMap : public FShaderMapContent
{
public:
FMeshMaterialShaderMap(EShaderPlatform InPlatform, FVertexFactoryType* InVFType);
// 开始编译指定材质和顶点工厂类型的所有材质.
uint32 BeginCompile(
uint32 ShaderMapId,
const FMaterialShaderMapId& InShaderMapId,
const FMaterial* Material,
const FMeshMaterialShaderMapLayout& MeshLayout,
FShaderCompilerEnvironment* MaterialEnvironment,
EShaderPlatform Platform,
TArray<TSharedRef<FShaderCommonCompileJob, ESPMode::ThreadSafe>>& NewJobs,
FString DebugDescription,
FString DebugExtension
);
void FlushShadersByShaderType(const FShaderType* ShaderType);
void FlushShadersByShaderPipelineType(const FShaderPipelineType* ShaderPipelineType);
(......)
private:
// 顶点工厂类型名称.
LAYOUT_FIELD(FHashedName, VertexFactoryTypeName);
};
FMeshMaterialShaderMap通常不能单独被创建,而是附加在FMaterialShaderMapContent之中,随着FMaterialShaderMapContent一起被创建和销毁,具体细节和应用见上一小节。
》的全部内容,本文网址:https://www.7ca.cn/baike/68667.shtml,如对您有帮助可以分享给好友,谢谢。