【从0开始的Unreal图形编程】GlobalShader参数的三种写法
上篇文章有些没处理好,里面的Shader类小编多继承了一个FMyShaderBase,不过摆烂了不想改了。
茴字的第一种写法
一个最普通的GlobalShader定义如下
////////////////////////////////////////////////////////////
// XXX.h
////////////////////////////////////////////////////////////
class FShader_VS : public FGlobalShader {
DECLARE_GLOBAL_SHADER(FShader_VS);
public:
FShader_VS() {}
FShader_VS(const ShaderMetaType::CompiledShaderInitializerType& Initializer)
: FGlobalShader(Initializer) {
}
};
class FShader_PS : public FGlobalShader {
DECLARE_GLOBAL_SHADER(FShader_PS);
public:
FShader_PS() {}
FShader_PS(const ShaderMetaType::CompiledShaderInitializerType& Initializer)
: FGlobalShader(Initializer) {
MainColorVal.Bind(Initializer.ParameterMap, TEXT("MainColor"));
}
void SetParameters(FRHICommandList& RHICmdList,
const FLinearColor& MyColor) {
SetShaderValue(RHICmdList, RHICmdList.GetBoundPixelShader(),
MainColorVal, MyColor);
}
private:
LAYOUT_FIELD(FShaderParameter, MainColorVal);
};
////////////////////////////////////////////////////////////
// XXX.cpp
////////////////////////////////////////////////////////////
IMPLEMENT_SHADER_TYPE(, FShader_VS,
TEXT("/GlobalShaderPlug/MyGlobalShader.usf"),
TEXT("MainVS"), SF_Vertex)
IMPLEMENT_SHADER_TYPE(, FShader_PS,
TEXT("/GlobalShaderPlug/MyGlobalShader.usf"),
TEXT("MainPS"), SF_Pixel)
////////////////////////////////////////////////////////////
// MyGlobalShader.usf
////////////////////////////////////////////////////////////
#include "/Engine/Public/Platform.ush"
float4 MainColor;
void MainVS(
in float4 InPosition : ATTRIBUTE0,
out float4 OutPosition : SV_POSITION)
{
OutPosition = InPosition;
}
void MainPS(out float4 OutColor : SV_Target0)
{
OutColor = MainColor;
}
通过MainColorVal.Bind(Initializer.ParameterMap, TEXT("MainColor"));绑定参数(挖个坑,后续再分析内部到底是如何绑定的)
茴字的第二种写法
第二种方法是利用SHADER_USE_PARAMETER_STRUCT,BEGIN_SHADER_PARAMETER_STRUCT,不过在文档中写道Notes: Long term, this macro will no longer be needed. Instead, parameter binding will become the default behavior for shader declarations,不过至少现在,引擎中还存在997Shader使用这种方法。
下面给出源码例子
class FRasterizeToRectsVS : public FGlobalShader
{
DECLARE_EXPORTED_SHADER_TYPE(FRasterizeToRectsVS, Global, RENDERCORE_API);
SHADER_USE_PARAMETER_STRUCT(FRasterizeToRectsVS, FGlobalShader);
BEGIN_SHADER_PARAMETER_STRUCT(FParameters, )
SHADER_PARAMETER_RDG_BUFFER_SRV(Buffer<uint4>, RectCoordBuffer)
SHADER_PARAMETER_RDG_BUFFER_SRV(Buffer<float4>, RectUVBuffer)
SHADER_PARAMETER(FVector2f, InvViewSize)
SHADER_PARAMETER(FVector2f, InvTextureSize)
SHADER_PARAMETER(float, DownsampleFactor)
SHADER_PARAMETER(uint32, NumRects)
END_SHADER_PARAMETER_STRUCT()
class FRectUV : SHADER_PERMUTATION_BOOL("RECT_UV");
using FPermutationDomain = TShaderPermutationDomain<FRectUV>;
static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters);
};
// 对应Shader
Buffer<uint4> RectCoordBuffer; // [MinX, MinY, MaxX, MaxY]
Buffer<uint4> RectUVBuffer;
float DownsampleFactor;
float2 InvViewSize;
float2 InvTextureSize;
bool2 VertMax(uint VertexId)
{
bool2 bVertMax;
bVertMax.x = VertexId == 1 || VertexId == 2 || VertexId == 4;
bVertMax.y = VertexId == 2 || VertexId == 4 || VertexId == 5;
return bVertMax;
}
void RasterizeToRectsVS(
in uint InstanceId : SV_InstanceID,
in uint VertexId : SV_VertexID,
out float4 OutPosition : SV_POSITION,
out float2 OutUV : TEXCOORD0,
out float2 OutRectUV : TEXCOORD1,
out float OutRectIndex : RECT_INDEX)
{
uint4 RectCoord = RectCoordBuffer[InstanceId] * DownsampleFactor;
uint2 VertexCoord = VertMax(VertexId) ? RectCoord.zw : RectCoord.xy;
float4 RectUV = RectCoord * InvViewSize.xyxy;
#if RECT_UV
{
RectUV = RectUVBuffer[InstanceId] * DownsampleFactor * InvTextureSize.xyxy;
}
#endif
float2 VertexUV = VertMax(VertexId) ? RectUV.zw : RectUV.xy;
OutPosition = float4(float2(VertexCoord) * InvViewSize * float2(2.0f, -2.0f) + float2(-1.0, 1.0f), 0.0f, 1.0f);
OutUV = VertexUV;
OutRectUV.x = VertMax(VertexId).x ? 1.0f : 0.0f;
OutRectUV.y = VertMax(VertexId).y ? 1.0f : 0.0f;
OutRectIndex = InstanceId;
}
关于SHADER_USE_PARAMETER_STRUCT(FRasterizeToRectsVS, FGlobalShader);的展开如下,
FRasterizeToRectsVS(const ShaderMetaType::CompiledShaderInitializerType& Initializer)
: FGlobalShader(Initializer)
{
BindForLegacyShaderParameters<FParameters>(this, Initializer.PermutationId, Initializer.ParameterMap, true);
}
FRasterizeToRectsVS() { }
// 这个是配合下面BEGIN_SHADER_PARAMETER_STRUCT和END_SHADER_PARAMETER_STRUCT为传参服务的
static inline const FShaderParametersMetadata* GetRootParametersMetadata()
{
return FParameters::FTypeInfo::GetStructMetadata();
};
可以看到,实际上就是生成了我们自己写的那几个构造函数,也就是说,我们就不用自己写构造函数啦
然后终于到了BEGIN_SHADER_PARAMETER_STRUCT这里了,大概展开就是一个带反射信息的结构体
__declspec(align(16))
class FParameters {
public:
FParameters() {}
struct FTypeInfo {
static constexpr int32 NumRows = 1;
static constexpr int32 NumColumns = 1;
static constexpr int32 NumElements = 0;
static constexpr int32 Alignment = 16;
static constexpr bool bIsStoredInConstantBuffer = true;
static constexpr const ANSICHAR* const FileName = "PixelShaderUtils.h";
static constexpr int32 FileLine = 34;
using TAlignedType = FParameters;
static inline const FShaderParametersMetadata* GetStructMetadata() {
static FShaderParametersMetadata StaticStructMetadata(
FShaderParametersMetadata::EUseCase::ShaderParameterStruct,
EUniformBufferBindingFlags::Shader, L"FParameters",
L"FParameters", nullptr, nullptr, FTypeInfo::FileName,
FTypeInfo::FileLine, sizeof(FParameters),
FParameters::zzGetMembers());
return &StaticStructMetadata;
}
};
// 挖坑,以后讲
static FUniformBufferRHIRef CreateUniformBuffer(const FParameters& InContents, EUniformBufferUsage InUsage) {
return nullptr;
}
private:
typedef FParameters zzTThisStruct;
struct zzFirstMemberId {
enum { HasDeclaredResource = 0 };
};
typedef void* zzFuncPtr;
typedef zzFuncPtr (*zzMemberFunc)(
zzFirstMemberId, TArray<FShaderParametersMetadata::FMember>*);
static zzFuncPtr zzAppendMemberGetPrev(
zzFirstMemberId, TArray<FShaderParametersMetadata::FMember>*) {
return nullptr;
}
// 开始声明结构体成员变量
typedef zzFirstMemberId
SHADER_PARAMETER_RDG_BUFFER_SRV(Buffer<uint4>,RectCoordBuffer)
SHADER_PARAMETER_RDG_BUFFER_SRV(Buffer<float4>, RectUVBuffer)
SHADER_PARAMETER(FVector2f, InvViewSize)
SHADER_PARAMETER(FVector2f, InvTextureSize)
SHADER_PARAMETER(float, DownsampleFactor)
SHADER_PARAMETER(uint32, NumRects)
zzLastMemberId;
// 结束声明结构体成员变量
public:
static TArray<FShaderParametersMetadata::FMember> zzGetMembers() {
TArray<FShaderParametersMetadata::FMember> Members;
zzFuncPtr (*LastFunc)(zzLastMemberId,
TArray<FShaderParametersMetadata::FMember>*);
LastFunc = zzAppendMemberGetPrev;
zzFuncPtr Ptr = (zzFuncPtr)LastFunc;
do {
Ptr = reinterpret_cast<zzMemberFunc>(Ptr)(zzFirstMemberId(),
&Members);
} while (Ptr);
Algo::Reverse(Members);
return Members;
}
};
可以看到,第一个public里面只是声明了一个FTypeInfo作为反射的metadata,这个里面最有用的是通过FParameters::zzGetMembers()传入了这个struct的所有成员变量到metadata里面,然后SHADER_PARAMETER的展开实际上就是利用typedef不断的对zzAppendMemberGetPrev函数进行重载,最终形成一个函数链表,比较有趣的反射思路
点击SHADER_PARAMETER代码
typedef zzFirstMemberId zzMemberIdRectCoordBuffer;
public:
TShaderResourceParameterTypeInfo<FRDGBufferSRV*>::TAlignedType
RectCoordBuffer = nullptr;
static_assert(UBMT_RDG_BUFFER_SRV != UBMT_INVALID,
"Invalid type "
"FRDGBufferSRV*"
" of member "
"RectCoordBuffer"
".");
private:
struct zzNextMemberIdRectCoordBuffer {
enum {
HasDeclaredResource =
zzMemberIdRectCoordBuffer::HasDeclaredResource ||
!TShaderResourceParameterTypeInfo<
FRDGBufferSRV*>::bIsStoredInConstantBuffer
};
};
static zzFuncPtr zzAppendMemberGetPrev(
zzNextMemberIdRectCoordBuffer,
TArray<FShaderParametersMetadata::FMember>* Members) {
static_assert(
TShaderResourceParameterTypeInfo<
FRDGBufferSRV*>::bIsStoredInConstantBuffer ||
TIsArrayOrRefOfType<decltype(L"Buffer<uint4>"), TCHAR>::Value,
"No shader type for "
"RectCoordBuffer"
".");
static_assert(
(((::size_t) & reinterpret_cast<char const volatile&>(
(((zzTThisStruct*)0)->RectCoordBuffer))) &
(TShaderResourceParameterTypeInfo<FRDGBufferSRV*>::Alignment -
1)) == 0,
"Misaligned uniform buffer struct member "
"RectCoordBuffer"
".");
Members->Add(FShaderParametersMetadata::FMember(
L"RectCoordBuffer", L"Buffer<uint4>", 35,
((::size_t) & reinterpret_cast<char const volatile&>(
(((zzTThisStruct*)0)->RectCoordBuffer))),
EUniformBufferBaseType(UBMT_RDG_BUFFER_SRV),
EShaderPrecisionModifier::Float,
TShaderResourceParameterTypeInfo<FRDGBufferSRV*>::NumRows,
TShaderResourceParameterTypeInfo<FRDGBufferSRV*>::NumColumns,
TShaderResourceParameterTypeInfo<FRDGBufferSRV*>::NumElements,
TShaderResourceParameterTypeInfo<
FRDGBufferSRV*>::GetStructMetadata()));
zzFuncPtr (*PrevFunc)(zzMemberIdRectCoordBuffer,
TArray<FShaderParametersMetadata::FMember>*);
PrevFunc = zzAppendMemberGetPrev;
return (zzFuncPtr)PrevFunc;
}
typedef zzNextMemberIdRectCoordBuffer zzMemberIdRectUVBuffer;
public:
TShaderResourceParameterTypeInfo<FRDGBufferSRV*>::TAlignedType
RectUVBuffer = nullptr;
static_assert(UBMT_RDG_BUFFER_SRV != UBMT_INVALID,
"Invalid type "
"FRDGBufferSRV*"
" of member "
"RectUVBuffer"
".");
private:
struct zzNextMemberIdRectUVBuffer {
enum {
HasDeclaredResource = zzMemberIdRectUVBuffer::HasDeclaredResource ||
!TShaderResourceParameterTypeInfo<
FRDGBufferSRV*>::bIsStoredInConstantBuffer
};
};
static zzFuncPtr zzAppendMemberGetPrev(
zzNextMemberIdRectUVBuffer,
TArray<FShaderParametersMetadata::FMember>* Members) {
static_assert(
TShaderResourceParameterTypeInfo<
FRDGBufferSRV*>::bIsStoredInConstantBuffer ||
TIsArrayOrRefOfType<decltype(L"Buffer<float4>"), TCHAR>::Value,
"No shader type for "
"RectUVBuffer"
".");
static_assert(
(((::size_t) & reinterpret_cast<char const volatile&>(
(((zzTThisStruct*)0)->RectUVBuffer))) &
(TShaderResourceParameterTypeInfo<FRDGBufferSRV*>::Alignment -
1)) == 0,
"Misaligned uniform buffer struct member "
"RectUVBuffer"
".");
Members->Add(FShaderParametersMetadata::FMember(
L"RectUVBuffer", L"Buffer<float4>", 36,
((::size_t) & reinterpret_cast<char const volatile&>(
(((zzTThisStruct*)0)->RectUVBuffer))),
EUniformBufferBaseType(UBMT_RDG_BUFFER_SRV),
EShaderPrecisionModifier::Float,
TShaderResourceParameterTypeInfo<FRDGBufferSRV*>::NumRows,
TShaderResourceParameterTypeInfo<FRDGBufferSRV*>::NumColumns,
TShaderResourceParameterTypeInfo<FRDGBufferSRV*>::NumElements,
TShaderResourceParameterTypeInfo<
FRDGBufferSRV*>::GetStructMetadata()));
zzFuncPtr (*PrevFunc)(zzMemberIdRectUVBuffer,
TArray<FShaderParametersMetadata::FMember>*);
PrevFunc = zzAppendMemberGetPrev;
return (zzFuncPtr)PrevFunc;
}
typedef zzNextMemberIdRectUVBuffer zzMemberIdInvViewSize;
public:
TShaderParameterTypeInfo<FVector2f>::TAlignedType InvViewSize;
static_assert(TShaderParameterTypeInfo<FVector2f>::BaseType != UBMT_INVALID,
"Invalid type "
"FVector2f"
" of member "
"InvViewSize"
".");
private:
struct zzNextMemberIdInvViewSize {
enum {
HasDeclaredResource =
zzMemberIdInvViewSize::HasDeclaredResource ||
!TShaderParameterTypeInfo<FVector2f>::bIsStoredInConstantBuffer
};
};
static zzFuncPtr zzAppendMemberGetPrev(
zzNextMemberIdInvViewSize,
TArray<FShaderParametersMetadata::FMember>* Members) {
static_assert(
TShaderParameterTypeInfo<FVector2f>::bIsStoredInConstantBuffer ||
TIsArrayOrRefOfType<decltype(L""), TCHAR>::Value,
"No shader type for "
"InvViewSize"
".");
static_assert(
(((::size_t) & reinterpret_cast<char const volatile&>(
(((zzTThisStruct*)0)->InvViewSize))) &
(TShaderParameterTypeInfo<FVector2f>::Alignment - 1)) == 0,
"Misaligned uniform buffer struct member "
"InvViewSize"
".");
Members->Add(FShaderParametersMetadata::FMember(
L"InvViewSize", L"", 37,
((::size_t) & reinterpret_cast<char const volatile&>(
(((zzTThisStruct*)0)->InvViewSize))),
EUniformBufferBaseType(
TShaderParameterTypeInfo<FVector2f>::BaseType),
EShaderPrecisionModifier::Float,
TShaderParameterTypeInfo<FVector2f>::NumRows,
TShaderParameterTypeInfo<FVector2f>::NumColumns,
TShaderParameterTypeInfo<FVector2f>::NumElements,
TShaderParameterTypeInfo<FVector2f>::GetStructMetadata()));
zzFuncPtr (*PrevFunc)(zzMemberIdInvViewSize,
TArray<FShaderParametersMetadata::FMember>*);
PrevFunc = zzAppendMemberGetPrev;
return (zzFuncPtr)PrevFunc;
}
typedef zzNextMemberIdInvViewSize zzMemberIdInvTextureSize;
public:
TShaderParameterTypeInfo<FVector2f>::TAlignedType InvTextureSize;
static_assert(TShaderParameterTypeInfo<FVector2f>::BaseType != UBMT_INVALID,
"Invalid type "
"FVector2f"
" of member "
"InvTextureSize"
".");
private:
struct zzNextMemberIdInvTextureSize {
enum {
HasDeclaredResource =
zzMemberIdInvTextureSize::HasDeclaredResource ||
!TShaderParameterTypeInfo<FVector2f>::bIsStoredInConstantBuffer
};
};
static zzFuncPtr zzAppendMemberGetPrev(
zzNextMemberIdInvTextureSize,
TArray<FShaderParametersMetadata::FMember>* Members) {
static_assert(
TShaderParameterTypeInfo<FVector2f>::bIsStoredInConstantBuffer ||
TIsArrayOrRefOfType<decltype(L""), TCHAR>::Value,
"No shader type for "
"InvTextureSize"
".");
static_assert(
(((::size_t) & reinterpret_cast<char const volatile&>(
(((zzTThisStruct*)0)->InvTextureSize))) &
(TShaderParameterTypeInfo<FVector2f>::Alignment - 1)) == 0,
"Misaligned uniform buffer struct member "
"InvTextureSize"
".");
Members->Add(FShaderParametersMetadata::FMember(
L"InvTextureSize", L"", 38,
((::size_t) & reinterpret_cast<char const volatile&>(
(((zzTThisStruct*)0)->InvTextureSize))),
EUniformBufferBaseType(
TShaderParameterTypeInfo<FVector2f>::BaseType),
EShaderPrecisionModifier::Float,
TShaderParameterTypeInfo<FVector2f>::NumRows,
TShaderParameterTypeInfo<FVector2f>::NumColumns,
TShaderParameterTypeInfo<FVector2f>::NumElements,
TShaderParameterTypeInfo<FVector2f>::GetStructMetadata()));
zzFuncPtr (*PrevFunc)(zzMemberIdInvTextureSize,
TArray<FShaderParametersMetadata::FMember>*);
PrevFunc = zzAppendMemberGetPrev;
return (zzFuncPtr)PrevFunc;
}
typedef zzNextMemberIdInvTextureSize zzMemberIdDownsampleFactor;
public:
TShaderParameterTypeInfo<float>::TAlignedType DownsampleFactor;
static_assert(TShaderParameterTypeInfo<float>::BaseType != UBMT_INVALID,
"Invalid type "
"float"
" of member "
"DownsampleFactor"
".");
private:
struct zzNextMemberIdDownsampleFactor {
enum {
HasDeclaredResource =
zzMemberIdDownsampleFactor::HasDeclaredResource ||
!TShaderParameterTypeInfo<float>::bIsStoredInConstantBuffer
};
};
static zzFuncPtr zzAppendMemberGetPrev(
zzNextMemberIdDownsampleFactor,
TArray<FShaderParametersMetadata::FMember>* Members) {
static_assert(
TShaderParameterTypeInfo<float>::bIsStoredInConstantBuffer ||
TIsArrayOrRefOfType<decltype(L""), TCHAR>::Value,
"No shader type for "
"DownsampleFactor"
".");
static_assert(
(((::size_t) & reinterpret_cast<char const volatile&>(
(((zzTThisStruct*)0)->DownsampleFactor))) &
(TShaderParameterTypeInfo<float>::Alignment - 1)) == 0,
"Misaligned uniform buffer struct member "
"DownsampleFactor"
".");
Members->Add(FShaderParametersMetadata::FMember(
L"DownsampleFactor", L"", 39,
((::size_t) & reinterpret_cast<char const volatile&>(
(((zzTThisStruct*)0)->DownsampleFactor))),
EUniformBufferBaseType(TShaderParameterTypeInfo<float>::BaseType),
EShaderPrecisionModifier::Float,
TShaderParameterTypeInfo<float>::NumRows,
TShaderParameterTypeInfo<float>::NumColumns,
TShaderParameterTypeInfo<float>::NumElements,
TShaderParameterTypeInfo<float>::GetStructMetadata()));
zzFuncPtr (*PrevFunc)(zzMemberIdDownsampleFactor,
TArray<FShaderParametersMetadata::FMember>*);
PrevFunc = zzAppendMemberGetPrev;
return (zzFuncPtr)PrevFunc;
}
typedef zzNextMemberIdDownsampleFactor zzMemberIdNumRects;
public:
TShaderParameterTypeInfo<uint32>::TAlignedType NumRects;
static_assert(TShaderParameterTypeInfo<uint32>::BaseType != UBMT_INVALID,
"Invalid type "
"uint32"
" of member "
"NumRects"
".");
private:
struct zzNextMemberIdNumRects {
enum {
HasDeclaredResource =
zzMemberIdNumRects::HasDeclaredResource ||
!TShaderParameterTypeInfo<uint32>::bIsStoredInConstantBuffer
};
};
static zzFuncPtr zzAppendMemberGetPrev(
zzNextMemberIdNumRects,
TArray<FShaderParametersMetadata::FMember>* Members) {
static_assert(
TShaderParameterTypeInfo<uint32>::bIsStoredInConstantBuffer ||
TIsArrayOrRefOfType<decltype(L""), TCHAR>::Value,
"No shader type for "
"NumRects"
".");
static_assert((((::size_t) & reinterpret_cast<char const volatile&>(
(((zzTThisStruct*)0)->NumRects))) &
(TShaderParameterTypeInfo<uint32>::Alignment - 1)) == 0,
"Misaligned uniform buffer struct member "
"NumRects"
".");
Members->Add(FShaderParametersMetadata::FMember(
L"NumRects", L"", 40,
((::size_t) & reinterpret_cast<char const volatile&>(
(((zzTThisStruct*)0)->NumRects))),
EUniformBufferBaseType(TShaderParameterTypeInfo<uint32>::BaseType),
EShaderPrecisionModifier::Float,
TShaderParameterTypeInfo<uint32>::NumRows,
TShaderParameterTypeInfo<uint32>::NumColumns,
TShaderParameterTypeInfo<uint32>::NumElements,
TShaderParameterTypeInfo<uint32>::GetStructMetadata()));
zzFuncPtr (*PrevFunc)(zzMemberIdNumRects,
TArray<FShaderParametersMetadata::FMember>*);
PrevFunc = zzAppendMemberGetPrev;
return (zzFuncPtr)PrevFunc;
}
typedef zzNextMemberIdNumRects zzLastMemberId;

而我们这里,实际上只有定义没有实例,所以我们需要在外面实例化这个变量,下面给出我的例子,源码里面稍微多了一层,不便于这里的理解
class FShader_PS : public FGlobalShader {
DECLARE_GLOBAL_SHADER(FShader_PS);
SHADER_USE_PARAMETER_STRUCT(FShader_PS,FGlobalShader);
BEGIN_SHADER_PARAMETER_STRUCT(FParameters, )
SHADER_PARAMETER(FVector4f,MainColor)
END_SHADER_PARAMETER_STRUCT()
};
// 外部进行传参
// 第一种GlobalShader传参
// PixelShader->SetParameters(RHICmdList, MyColor);
// 当前GlobalShader传参
FShader_PS::FParameters Parameters;
Parameters.MainColor = MyColor;
SetShaderParameters(RHICmdList, PixelShader, PixelShader.GetPixelShader(),Parameters);
BEGIN_SHADER_PARAMETER_STRUCT使用指南
通过上述展开,我们知道BEGIN_SHADER_PARAMETER_STRUCT在Shader内部使用时,为了配合第一个宏的展开,类型名称必须是FParameters,实际上这里面就可以衍生出这种操作
// NaniteEditor.h
BEGIN_SHADER_PARAMETER_STRUCT(FNaniteVisualizeLevelInstanceParameters, )
SHADER_PARAMETER_STRUCT_REF(FViewUniformShaderParameters, View)
SHADER_PARAMETER(FVector2f, OutputToInputScale)
SHADER_PARAMETER(FVector2f, OutputToInputBias)
SHADER_PARAMETER(uint32, MaxVisibleClusters)
SHADER_PARAMETER_RDG_BUFFER_SRV(StructuredBuffer<FVisibleCluster>,
VisibleClustersSWHW)
SHADER_PARAMETER(FIntVector4, PageConstants)
SHADER_PARAMETER_SRV(ByteAddressBuffer, ClusterPageData)
SHADER_PARAMETER_RDG_TEXTURE(Texture2D<uint2>, VisBuffer64)
SHADER_PARAMETER_RDG_TEXTURE(Texture2D<uint>, MaterialResolve)
SHADER_PARAMETER_SRV(ByteAddressBuffer, MaterialHitProxyTable)
END_SHADER_PARAMETER_STRUCT()
// NaniteEditor.cpp
class FEmitEditingLevelInstanceDepthPS : public FNaniteGlobalShader {
public:
DECLARE_GLOBAL_SHADER(FEmitEditingLevelInstanceDepthPS);
SHADER_USE_PARAMETER_STRUCT(FEmitEditingLevelInstanceDepthPS,
FNaniteGlobalShader);
using FParameters = FNaniteVisualizeLevelInstanceParameters;
......
};
可以看到,这个时候BEGIN_SHADER_PARAMETER_STRUCT的类型名称就不局限于FParameters,只需要在GlobalShader内部利用using FParameters = XXX;就OK了。这么做的好处在于
- 代码复用,多个GlobalShader内部如果是同一套参数只需要编写一次就可以了
- 类型名从XXX::FParameters变为XXX,更加清晰。
- GlobalShader可以作为一个函数Implement定义在xx.cpp,并不需要写在xx.h中暴露出去
// NaniteEditor.h
BEGIN_SHADER_PARAMETER_STRUCT(FNaniteVisualizeLevelInstanceParameters, )
SHADER_PARAMETER_STRUCT_REF(FViewUniformShaderParameters, View)
SHADER_PARAMETER(FVector2f, OutputToInputScale)
SHADER_PARAMETER(FVector2f, OutputToInputBias)
SHADER_PARAMETER(uint32, MaxVisibleClusters)
SHADER_PARAMETER_RDG_BUFFER_SRV(StructuredBuffer<FVisibleCluster>,
VisibleClustersSWHW)
SHADER_PARAMETER(FIntVector4, PageConstants)
SHADER_PARAMETER_SRV(ByteAddressBuffer, ClusterPageData)
SHADER_PARAMETER_RDG_TEXTURE(Texture2D<uint2>, VisBuffer64)
SHADER_PARAMETER_RDG_TEXTURE(Texture2D<uint>, MaterialResolve)
SHADER_PARAMETER_SRV(ByteAddressBuffer, MaterialHitProxyTable)
END_SHADER_PARAMETER_STRUCT()
void DrawEditorVisualizeLevelInstance(
FRHICommandList& RHICmdList, const FViewInfo& View,
const FIntRect ViewportRect,
const FNaniteVisualizeLevelInstanceParameters& PassParameters);
// NaniteEditor.cpp
class FEmitEditingLevelInstanceDepthPS : public FNaniteGlobalShader {
public:
DECLARE_GLOBAL_SHADER(FEmitEditingLevelInstanceDepthPS);
SHADER_USE_PARAMETER_STRUCT(FEmitEditingLevelInstanceDepthPS,
FNaniteGlobalShader);
using FParameters = FNaniteVisualizeLevelInstanceParameters;
......
};
IMPLEMENT_GLOBAL_SHADER(FEmitEditingLevelInstanceDepthPS,
"/Engine/Private/Nanite/NaniteExportGBuffer.usf",
"EmitEditorLevelInstanceDepthPS", SF_Pixel);
// 函数实现
void DrawEditorVisualizeLevelInstance(
FRHICommandList& RHICmdList, const FViewInfo& View,
const FIntRect ViewportRect,
const FNaniteVisualizeLevelInstanceParameters& PassParameters) {
......
auto PixelShader =
View.ShaderMap->GetShader<FEmitEditingLevelInstanceDepthPS>(
PermutationVector.ToDimensionValueId());
......
}
茴字的第三种写法
在第二种写法的额外使用情况中,实际上我们在并没有完全的消除代码复用的问题,原因是我们写的struct内部的参数,必须要和Shader中的对应,否则会发生编译报错问题。其次,我们以上面哪种方式编写Shader,实际上是位于编译器自动产生的一个Global constant buffer中,为了想像dx那样编写一个Custom constant buffer于是就有了BEGIN_UNIFORM_BUFFER_STRUCT,实际上,引擎内部还是使用过时的BEGIN_GLOBAL_SHADER_PARAMETER_STRUCT不过本质就是
/** Legacy macro definitions. */
#define BEGIN_GLOBAL_SHADER_PARAMETER_STRUCT \ BEGIN_UNIFORM_BUFFER_STRUCT
让我们找个栗子看一看
/*------------------------------------------------------------------------------
Uniform buffer for passing in radix sort parameters.
------------------------------------------------------------------------------*/
BEGIN_GLOBAL_SHADER_PARAMETER_STRUCT(FRadixSortParameters, )
SHADER_PARAMETER(uint32, RadixShift)
SHADER_PARAMETER(uint32, TilesPerGroup)
SHADER_PARAMETER(uint32, ExtraTileCount)
SHADER_PARAMETER(uint32, ExtraKeyCount)
SHADER_PARAMETER(uint32, GroupCount)
END_GLOBAL_SHADER_PARAMETER_STRUCT()
IMPLEMENT_GLOBAL_SHADER_PARAMETER_STRUCT(FRadixSortParameters, "RadixSortUB");
结构体的Begin展开得到(End部分内部调用的宏就是第二种写法中的End)
__declspec(align(16)) class FRadixSortParameters {
public:
FRadixSortParameters() {}
// 新增静态变量
static FShaderParametersMetadata StaticStructMetadata
struct FTypeInfo {
static constexpr int32 NumRows = 1;
static constexpr int32 NumColumns = 1;
static constexpr int32 NumElements = 0;
static constexpr int32 Alignment = 16;
static constexpr bool bIsStoredInConstantBuffer = true;
static constexpr const ANSICHAR* const FileName = "GPUSort.cpp";
static constexpr int32 FileLine = 45;
using TAlignedType = FRadixSortParameters;
static inline const FShaderParametersMetadata* GetStructMetadata() {
return &FRadixSortParameters::StaticStructMetadata;
}
};
static FUniformBufferRHIRef CreateUniformBuffer(
const FRadixSortParameters& InContents, EUniformBufferUsage InUsage) {
// 从return nullptr变为这个
return RHICreateUniformBuffer(
&InContents, StaticStructMetadata.GetLayoutPtr(), InUsage);
}
private:
typedef FRadixSortParameters zzTThisStruct;
struct zzFirstMemberId {
enum { HasDeclaredResource = 0 };
};
typedef void* zzFuncPtr;
typedef zzFuncPtr (*zzMemberFunc)(
zzFirstMemberId, TArray<FShaderParametersMetadata::FMember>*);
static zzFuncPtr zzAppendMemberGetPrev(
zzFirstMemberId, TArray<FShaderParametersMetadata::FMember>*) {
return nullptr;
}
typedef zzFirstMemberId
实际上可以看到就新增了声明一个静态变量StaticStructMetadata,并且需要从调用StaticStructMetadata.GetLayoutPtr()方法,那么这个变量在哪里实现呢?让我们展开IMPLEMENT_GLOBAL_SHADER_PARAMETER_STRUCT(FRadixSortParameters,"RadixSortUB");看看
FShaderParametersMetadata FRadixSortParameters::StaticStructMetadata(
FShaderParametersMetadata::EUseCase::UniformBuffer,
EUniformBufferBindingFlags::Shader, L"FRadixSortParameters",
L"FRadixSortParameters", L"RadixSortUB", nullptr,
FRadixSortParameters::FTypeInfo::FileName,
FRadixSortParameters::FTypeInfo::FileLine, sizeof(FRadixSortParameters),
FRadixSortParameters::zzGetMembers());
原来这个IMPLEMENT_GLOBAL_SHADER_PARAMETER_STRUCT就是实现静态变量而已,而这里L"RadixSortUB"传入的形参名称为InShaderVariableName可以大概猜出,这里应该就对应usf中所自动生成的constant buffer的名字(马后炮)。
有了这些元数据,Engine就会利用反射自动在形如下面的代码
cbuffer RadixSortUB : register(bx) {
uint RadixShift;
uint TilesPerGroup;
uint ExtraTileCount
uint ExtraKeyCount;
uint GroupCount;
}
然后将代码插入Common.ush中,就不用自己手动在Shader中去写了,Good Job!
讲完了他结构体的定义,下面讲一下GlobalShader中的使用,下面给出源码实例
class FRadixSortUpsweepCS : public FGlobalShader
{
DECLARE_SHADER_TYPE(FRadixSortUpsweepCS,Global);
public:
/** Default constructor. */
FRadixSortUpsweepCS()
{
}
/** Initialization constructor. */
explicit FRadixSortUpsweepCS( const ShaderMetaType::CompiledShaderInitializerType& Initializer )
: FGlobalShader(Initializer)
{
RadixSortParameterBuffer.Bind( Initializer.ParameterMap, TEXT("RadixSortParameterBuffer") );
InKeys.Bind( Initializer.ParameterMap, TEXT("InKeys") );
OutOffsets.Bind( Initializer.ParameterMap, TEXT("OutOffsets") );
}
/**
* Set parameters for this shader.
*/
// 这里的FRadixSortUniformBufferRef实际上可以就是一个Ref,后面会讲
// typedef TUniformBufferRef<FRadixSortParameters> FRadixSortUniformBufferRef
void SetParameters(FRHICommandList& RHICmdList, FRHIShaderResourceView* InKeysSRV, FRadixSortUniformBufferRef& RadixSortUniformBuffer, FRHIShaderResourceView* RadixSortParameterBufferSRV)
{
FRHIComputeShader* ComputeShaderRHI = RHICmdList.GetBoundComputeShader();
// 传入参数到GPU上
SetUniformBufferParameter(RHICmdList, ComputeShaderRHI, GetUniformBufferParameter<FRadixSortParameters>(), RadixSortUniformBuffer );
if ( InKeys.IsBound() )
{
RHICmdList.SetShaderResourceViewParameter(ComputeShaderRHI, InKeys.GetBaseIndex(), InKeysSRV);
}
if ( RadixSortParameterBuffer.IsBound() )
{
RHICmdList.SetShaderResourceViewParameter(ComputeShaderRHI, RadixSortParameterBuffer.GetBaseIndex(), RadixSortParameterBufferSRV);
}
}
/**
* Set output buffer for this shader.
*/
void SetOutput(FRHICommandList& RHICmdList, FRHIUnorderedAccessView* OutOffsetsUAV)
{
FRHIComputeShader* ComputeShaderRHI = RHICmdList.GetBoundComputeShader();
if ( OutOffsets.IsBound() )
{
RHICmdList.SetUAVParameter(ComputeShaderRHI, OutOffsets.GetBaseIndex(), OutOffsetsUAV);
}
}
......
private:
/** Uniform parameters stored in a vertex buffer, used to workaround an NVIDIA driver bug. */
LAYOUT_FIELD(FShaderResourceParameter, RadixSortParameterBuffer);
/** The buffer containing input keys. */
LAYOUT_FIELD(FShaderResourceParameter, InKeys);
/** The buffer to which offsets will be written. */
LAYOUT_FIELD(FShaderResourceParameter, OutOffsets);
};
这个时候,大家看到这个Shader的定义已经就已经非常熟了吧,至少小编是非常熟悉了。
在外部的调用实际上就是
FRadixSortParameters SortParameters;
FRadixSortUniformBufferRef SortUniformBufferRef;
// Setup sort parameters.
SortParameters.RadixShift = 0;
SortParameters.TilesPerGroup = TilesPerGroup;
SortParameters.ExtraTileCount = ExtraTileCount;
SortParameters.ExtraKeyCount = ExtraKeyCount;
SortParameters.GroupCount = GroupCount;
TShaderMapRef<FRadixSortUpsweepCS> UpsweepCS(ShaderMap);
SortUniformBufferRef = FRadixSortUniformBufferRef::CreateUniformBufferImmediate( SortParameters, UniformBuffer_SingleDraw );
UpsweepCS->SetParameters(RHICmdList, xxx, SortUniformBufferRef,xxx);
大功告成
后记(个人见解)
茴字的多种写法实际只是调侃,但是也逐渐理解第二种方法备注里面写的Notes: Long term, this macro will no longer be needed. Instead, parameter binding will become the default behavior for shader declarations的含义,感觉它有些不伦不类,说是struct,但在shader中并没有struct包装起来。对于它的那些优点,第三个方法有过之而无不及。
最终的编程指南就是对于不需要struct包装的变量,直接用第一种方法就可以了,而对于需要struct包装的变量,直接采用第三种方法。PS:建议使用新的宏名称无歧义些BEGIN_UNIFORM_BUFFER_STRUCT

浙公网安备 33010602011771号