diff --git a/03-UnrealEngine/Rendering/RenderingPipeline/向往渲染系列文章阅读笔记/剖析虚幻渲染体系(08)- Shader体系.md b/03-UnrealEngine/Rendering/RenderingPipeline/向往渲染系列文章阅读笔记/剖析虚幻渲染体系(08)- Shader体系.md index a0a255e..ffc72f9 100644 --- a/03-UnrealEngine/Rendering/RenderingPipeline/向往渲染系列文章阅读笔记/剖析虚幻渲染体系(08)- Shader体系.md +++ b/03-UnrealEngine/Rendering/RenderingPipeline/向往渲染系列文章阅读笔记/剖析虚幻渲染体系(08)- Shader体系.md @@ -193,4 +193,472 @@ private: // 缓冲区尺寸. uint32 LayoutConstantBufferSize; }; -``` \ No newline at end of file + +// 定义FRHIUniformBuffer的引用类型. +typedef TRefCountPtr FUniformBufferRHIRef; + +// Engine\Source\Runtime\RenderCore\Public\ShaderParameterMacros.h +// 引用了指定类型的FRHIUniformBuffer的实例资源. 注意是继承了FUniformBufferRHIRef. +template +class TUniformBufferRef : public FUniformBufferRHIRef +{ +public: + TUniformBufferRef(); + + // 根据给定的值创建Uniform Buffer, 并返回结构体引用. (模板) + static TUniformBufferRef 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 + friend class TUniformBuffer; + + friend class TRDGUniformBuffer; +}; +``` + +最后TUniformBuffer和TRDGUniformBuffer引用了FUniformBufferRHIRef。TUniformBuffer为`TUniformBufferRef UniformBufferRHI`成员变量;TRDGUniformBuffer为`TRefCountPtr UniformBufferRHI`。 + +![[UE_Uniform.png]] + +### 定义宏 +- SHADER_PARAMETER_STRUCT_REF:引用着色器参数结构体(全局的才行) +- SHADER_PARAMETER_STRUCT_INCLUDE:包含着色器参数结构体(局部或全局都行) + +# Vertex Factory +我们知道,在引擎中存在着静态网格、蒙皮骨骼、程序化网格以及地形等等类型的网格类型,而材质就是通过顶点工厂FVertexFactory来支持这些网格类型。实际上,顶点工厂要涉及各方面的数据和类型,包含但不限于: +- 顶点着色器。顶点着色器的输入输出需要顶点工厂来表明数据的布局。 +- 顶点工厂的参数和RHI资源。这些数据将从C++层传入到顶点着色器中进行处理。 +- 顶点缓冲和顶点布局。通过顶点布局,我们可以自定义和扩展顶点缓冲的输入,从而实现定制化的Shader代码。 +- 几何预处理。顶点缓冲、网格资源、材质参数等等都可以在真正渲染前预处理它们。 +![[UE_VertexFactory.png]] + +**顶点工厂在渲染层级中的关系。由图可知,顶点工厂是渲染线程的对象,横跨于CPU和GPU两端。** + +```c++ +// Engine\Source\Runtime\RHI\Public\RHI.h +// 顶点元素. +struct FVertexElement +{ + uint8 StreamIndex; // 流索引 + uint8 Offset; // 偏移 + TEnumAsByte 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 > 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> 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 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*& GetTypeList(); + // 获取已存的材质类型列表. + static RENDERCORE_API const TArray& GetSortedMaterialTypes(); + // 通过名字查找FVertexFactoryType + static RENDERCORE_API FVertexFactoryType* GetVFByName(const FHashedName& VFName); + + // 初始化FVertexFactoryType静态成员, 必须在VF类型创建之前调用. + static void Initialize(const TMap >& 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& 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 GlobalListLink; + // 缓存引用的Uniform Buffer的包含. + TMap 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(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 > Streams; + + // VF(顶点工厂)可以显式地将此设置为false,以避免在没有声明的情况下出现错误. 主要用于需要直接从缓冲区获取数据的VF(如Niagara). + bool bNeedsDeclaration = true; + bool bSupportsManualVertexFetch = false; + int8 PrimitiveIdStreamIndex[3] = { -1, -1, -1 }; + +private: + // 只有位置的顶点流, 用于渲染深度Pass的顶点工厂. + TArray > PositionStream; + // 只有位置和法线的顶点流. + TArray > PositionAndNormalStream; + + // 用于常规渲染顶点工厂的RHI顶点声明. + FVertexDeclarationRHIRef Declaration; + + // PositionStream和PositionAndNormalStream对应的RHI资源. + FVertexDeclarationRHIRef PositionDeclaration; + FVertexDeclarationRHIRef PositionAndNormalDeclaration; +}; +``` + +上面展示了Vertex Factory的很多类型,有好几个是核心类,比如FVertexFactory、FVertexElement、FRHIVertexDeclaration、FRHIVertexBuffer、FVertexFactoryType、FVertexStreamComponent、FVertexInputStream、FVertexFactoryShaderParameters等。那么它们之间的关系是什么呢? + +为了更好地说明它们之间的关系,以静态模型的FStaticMeshDataType为例: +![[UE_VertexFactory_FStaticMeshDataType.jpg]] \ No newline at end of file diff --git a/08-Assets/Images/ImageBag/Images/UE_Uniform.png b/08-Assets/Images/ImageBag/Images/UE_Uniform.png new file mode 100644 index 0000000..1a85b16 Binary files /dev/null and b/08-Assets/Images/ImageBag/Images/UE_Uniform.png differ diff --git a/08-Assets/Images/ImageBag/Images/UE_VertexFactory.png b/08-Assets/Images/ImageBag/Images/UE_VertexFactory.png new file mode 100644 index 0000000..331e245 Binary files /dev/null and b/08-Assets/Images/ImageBag/Images/UE_VertexFactory.png differ diff --git a/08-Assets/Images/ImageBag/Images/UE_VertexFactory_FStaticMeshDataType.jpg b/08-Assets/Images/ImageBag/Images/UE_VertexFactory_FStaticMeshDataType.jpg new file mode 100644 index 0000000..2cb947e Binary files /dev/null and b/08-Assets/Images/ImageBag/Images/UE_VertexFactory_FStaticMeshDataType.jpg differ