概述
代码版本5.3
https://www.yuque.com/chenweilin-tryw7/gbfomp/ygwycvgmwps8niby?singleDoc# 《UE 渲染入门 v2》
上回书说到,渲染需要一个画布
然后 viewportclient->draw
最后通过 Flush_GameThread 刷新画布
FCanvas Canvas(this, nullptr, ViewportWorld, ViewportWorld ? ViewportWorld->GetFeatureLevel() : GMaxRHIFeatureLevel, FCanvas::CDM_DeferDrawing, ViewportClient->ShouldDPIScaleSceneCanvas() ? ViewportClient->GetDPIScale() : 1.0f);
Canvas.SetRenderTargetRect(FIntRect(0, 0, SizeX, SizeY));
{
ViewportClient->Draw(this, &Canvas);
}
Canvas.Flush_GameThread();
在
Flush_GameThread 中
void FCanvas::Flush_GameThread(bool bForce)
{
// 。。。
// FCanvasSortElement compare class
// 进行排序
struct FCompareFCanvasSortElement
{
FORCEINLINE bool operator()( const FCanvasSortElement& A, const FCanvasSortElement& B ) const
{
return B.DepthSortKey < A.DepthSortKey;
}
};
// sort the array of FCanvasSortElement entries so that higher sort keys render first (back-to-front)
SortedElements.Sort(FCompareFCanvasSortElement());
bool bEmitCanvasDrawEvents = GetEmitDrawEvents(); // 局部变量没用到?
// Only create these render commands if we actually have something to draw.
if(SortedElements.Num() > 0 && !GUsingNullRHI)
{
// 有东西要画
FCanvasRenderThreadScope RenderThreadScope(*this);
// iterate over the FCanvasSortElements in sorted order and render all the batched items for each entry
for( int32 Idx=0; Idx < SortedElements.Num(); Idx++ )
{
FCanvasSortElement& SortElement = SortedElements[Idx];
for( int32 BatchIdx=0; BatchIdx < SortElement.RenderBatchArray.Num(); BatchIdx++ )
{
FCanvasBaseRenderItem* RenderItem = SortElement.RenderBatchArray[BatchIdx]; // 要画的东西
if( RenderItem )
{
// mark current render target as dirty since we are drawing to it
bRenderTargetDirty |= RenderItem->Render_GameThread(this, RenderThreadScope);
RenderThreadScope.DeferredDelete(RenderItem); // 延迟删除
}
}
SortElement.RenderBatchArray.Empty();
}
}
else
{
// 。。。
}
如果 SortedElements 有东西,会需要遍历所有对象。拿到 FCanvasSortElement,这同样也是一个array。
再次遍历获取 FCanvasBaseRenderItem。调用 RenderItem->Render_GameThread
同时上文,也提到了FCanvas中的 SortedElements 是正是画布中要画的对象
如看他的子类,用于渲染各种Item
class FMeshMaterialRenderItem : public FCanvasBaseRenderItem {};
class FMeshMaterialRenderItem2 : public FCanvasBaseRenderItem {};
class FCanvasBatchedElementRenderItem : public FCanvasBaseRenderItem {};
class FCanvasTileRendererItem : public FCanvasBaseRenderItem {};
class FCanvasTriangleRendererItem : public FCanvasBaseRenderItem {};
并且Data字段,例如三角形的 RendererItem
class FCanvasTriangleRendererItem : public FCanvasBaseRenderItem
{
class FRenderData
{
// ...
struct FTriangleInst
{
FCanvasUVTri Tri;
FHitProxyId HitProxyId;
};
TArray<FTriangleInst> Triangles;
};
TSharedPtr<FRenderData> Data;
}
能够找到
/** Simple 2d triangle with UVs */
USTRUCT(BlueprintType)
struct FCanvasUVTri
{
GENERATED_BODY()
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = CanvasUVTri)
FVector2D V0_Pos;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = CanvasUVTri)
FVector2D V0_UV;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = CanvasUVTri)
FLinearColor V0_Color;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = CanvasUVTri)
FVector2D V1_Pos;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = CanvasUVTri)
FVector2D V1_UV;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = CanvasUVTri)
FLinearColor V1_Color;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = CanvasUVTri)
FVector2D V2_Pos;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = CanvasUVTri)
FVector2D V2_UV;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = CanvasUVTri)
FLinearColor V2_Color;
};
要绘制的物体,传递向GPU数据的描述结构。
Render_GameThread 会先跑 FCanvasBatchedElementRenderItem,然后在按平台分
bool FCanvasBatchedElementRenderItem::Render_GameThread(const FCanvas* Canvas, FCanvasRenderThreadScope& RenderScope)
{
checkSlow(Data);
bool bDirty=false;
if( Data->BatchedElements.HasPrimsToDraw() ) // 批处理是否需要绘制图元
{
bDirty = true;
// current render target set for the canvas
// 获取 RT 和 gamma(用于后面颜色计算)
const FRenderTarget* CanvasRenderTarget = Canvas->GetRenderTarget();
float Gamma = 1.0f / CanvasRenderTarget->GetDisplayGamma();
if ( Data->Texture && Data->Texture->bIgnoreGammaConversions )
{
Gamma = 1.0f;
}
// Render the batched elements.
// 准备一个绘制参数
struct FBatchedDrawParameters
{
FRenderData* RenderData;
uint32 bHitTesting : 1;
uint32 ViewportSizeX;
uint32 ViewportSizeY;
float DisplayGamma;
uint32 AllowedCanvasModes;
ERHIFeatureLevel::Type FeatureLevel;
EShaderPlatform ShaderPlatform;
};
// all the parameters needed for rendering
FBatchedDrawParameters DrawParameters =
{
Data,
(uint32)(Canvas->IsHitTesting() ? 1 : 0),
(uint32)CanvasRenderTarget->GetSizeXY().X,
(uint32)CanvasRenderTarget->GetSizeXY().Y,
Gamma,
Canvas->GetAllowedModes(),
Canvas->GetFeatureLevel(),
Canvas->GetShaderPlatform()
};
// 添加一个pass,lambda在渲染线程去做
RenderScope.AddPass(
TEXT("CanvasBatchedElements"),
[DrawParameters](FRHICommandList& RHICmdList)
{
FSceneView SceneView = FBatchedElements::CreateProxySceneView(DrawParameters.RenderData->Transform.GetMatrix(), FIntRect(0, 0, DrawParameters.ViewportSizeX, DrawParameters.ViewportSizeY));
FMeshPassProcessorRenderState DrawRenderState;
// disable depth test & writes
DrawRenderState.SetDepthStencilState(TStaticDepthStencilState<false, CF_Always>::GetRHI());
DrawRenderState.SetBlendState(TStaticBlendState<>::GetRHI());
// draw batched items
DrawParameters.RenderData->BatchedElements.Draw( // 《==========这里也要看看
RHICmdList,
DrawRenderState,
DrawParameters.FeatureLevel,
SceneView,
DrawParameters.bHitTesting,
DrawParameters.DisplayGamma);
if(DrawParameters.AllowedCanvasModes & FCanvas::Allow_DeleteOnRender )
{
delete DrawParameters.RenderData;
}
});
}
if( Canvas->GetAllowedModes() & FCanvas::Allow_DeleteOnRender )
{
Data = NULL;
}
return bDirty;
}
我们的剧情从这里的 RenderScope.AddPass 展开
FRDGBuilder::AddPass
RenderScope.AddPass
template <typename ExecuteLambdaType>
void AddPass(const TCHAR* PassName, ExecuteLambdaType&& Lambda)
{
EnqueueRenderCommand(
[PassName, InLambda = Forward<ExecuteLambdaType&&>(Lambda)]
(FCanvasRenderContext& RenderContext) mutable
{
RenderContext.AddPass(RDG_EVENT_NAME("%s", PassName), Forward<ExecuteLambdaType&&>(InLambda));
});
}
在渲染线程,运行
GraphBuilder 就是 FRDGBuilder
RDG 是一个自动化 render pass 管理器
template <typename ExecuteLambdaType, typename ParameterStructType>
void AddPass(FRDGEventName&& PassName, const ParameterStructType* PassParameters, ExecuteLambdaType&& ExecuteLambda)
{
GraphBuilder.AddPass(Forward<FRDGEventName>(PassName), PassParameters, ERDGPassFlags::Raster,
[LocalScissorRect = ScissorRect, LocalViewportRect = ViewportRect, LocalExecuteLambda = Forward<ExecuteLambdaType&&>(ExecuteLambda)](FRHICommandListImmediate& RHICmdList)
{
RHICmdList.SetViewport(static_cast<float>(LocalViewportRect.Min.X), static_cast<float>(LocalViewportRect.Min.Y), 0.0f, static_cast<float>(LocalViewportRect.Max.X), static_cast<float>(LocalViewportRect.Max.Y), 1.0f);
if (LocalScissorRect.Area() > 0)
{
RHICmdList.SetScissorRect(true, static_cast<uint32>(LocalScissorRect.Min.X), static_cast<uint32>(LocalScissorRect.Min.Y), static_cast<uint32>(LocalScissorRect.Max.X), static_cast<uint32>(LocalScissorRect.Max.Y));
}
LocalExecuteLambda(RHICmdList);
});
}
这里最后会执行 LocalExecuteLambda(RHICmdList);
是一路传参数进来的
RenderScope.AddPass 第二个参数的lambda
bool FCanvasBatchedElementRenderItem::Render_GameThread(const FCanvas* Canvas, FCanvasRenderThreadScope& RenderScope)
{
checkSlow(Data);
bool bDirty=false;
if( Data->BatchedElements.HasPrimsToDraw() )
{
// 。。。
RenderScope.AddPass(
TEXT("CanvasBatchedElements"),
[DrawParameters](FRHICommandList& RHICmdList) // 这里的lambda
{
FSceneView SceneView = FBatchedElements::CreateProxySceneView(DrawParameters.RenderData->Transform.GetMatrix(), FIntRect(0, 0, DrawParameters.ViewportSizeX, DrawParameters.ViewportSizeY));
FMeshPassProcessorRenderState DrawRenderState;
// disable depth test & writes
DrawRenderState.SetDepthStencilState(TStaticDepthStencilState<false, CF_Always>::GetRHI());
DrawRenderState.SetBlendState(TStaticBlendState<>::GetRHI());
// draw batched items
DrawParameters.RenderData->BatchedElements.Draw(
RHICmdList,
DrawRenderState,
DrawParameters.FeatureLevel,
SceneView,
DrawParameters.bHitTesting,
DrawParameters.DisplayGamma);
if(DrawParameters.AllowedCanvasModes & FCanvas::Allow_DeleteOnRender )
{
delete DrawParameters.RenderData;
}
});
}
if( Canvas->GetAllowedModes() & FCanvas::Allow_DeleteOnRender )
{
Data = NULL;
}
return bDirty;
}
进行一些设置后会进入
BatchedElements.Draw
先不管addpass,假装已经加进了,看看是怎么draw的
FBatchedElements::Draw
bool FBatchedElements::Draw(FRHICommandList& RHICmdList, const FMeshPassProcessorRenderState& DrawRenderState, ERHIFeatureLevel::Type FeatureLevel, const FSceneView& View, bool bHitTesting, float Gamma /* = 1.0f */, EBlendModeFilter::Type Filter /* = EBlendModeFilter::All */) const
{
const FRelativeViewMatrices RelativeMatrices = FRelativeViewMatrices::Create(View.ViewMatrices);
// 。。。
参数传递进来了这个,里面有很多矩阵,带Inv的是求逆后的
struct FViewMatrices
{
FViewMatrices()
{
ProjectionMatrix.SetIdentity();
ProjectionNoAAMatrix.SetIdentity();
InvProjectionMatrix.SetIdentity();
ViewMatrix.SetIdentity();
InvViewMatrix.SetIdentity();
ViewProjectionMatrix.SetIdentity();
InvViewProjectionMatrix.SetIdentity();
HMDViewMatrixNoRoll.SetIdentity();
TranslatedViewMatrix.SetIdentity();
InvTranslatedViewMatrix.SetIdentity();
OverriddenTranslatedViewMatrix.SetIdentity();
OverriddenInvTranslatedViewMatrix.SetIdentity();
TranslatedViewProjectionMatrix.SetIdentity();
InvTranslatedViewProjectionMatrix.SetIdentity();
ScreenToClipMatrix.SetIdentity();
PreViewTranslation = FVector::ZeroVector;
ViewOrigin = FVector::ZeroVector;
ProjectionScale = FVector2D::ZeroVector;
TemporalAAProjectionJitter = FVector2D::ZeroVector;
ScreenScale = 1.f;
}
// 。。。
}
FRelativeViewMatrices FRelativeViewMatrices::Create(const FViewMatrices& Matrices, const FViewMatrices& PrevMatrices)
{
FInitializer Initializer;
Initializer.ViewToWorld = Matrices.GetInvViewMatrix();
Initializer.WorldToView = Matrices.GetViewMatrix();
Initializer.ViewToClip = Matrices.GetProjectionMatrix();
Initializer.ClipToView = Matrices.GetInvProjectionMatrix();
Initializer.PrevViewToWorld = PrevMatrices.GetInvViewMatrix();
Initializer.PrevClipToView = PrevMatrices.GetInvProjectionMatrix();
return Create(Initializer);
}
这里存了一下各种矩阵,然后create
FRelativeViewMatrices FRelativeViewMatrices::Create(const FInitializer& Initializer)
{
const double TileSize = FLargeWorldRenderScalar::GetTileSize(); // 获取瓦片大小,一种将屏幕分割为瓦片的渲染技术
// We allow a fractional tile position here 我们允许分数瓦片
// Tile offset is applied beofre WorldToView transform, but after ViewToWorld transform ?
// This means that if we use a regular tile offset, the remaining offset in the relative matrices may become too large (>TileSize/2) ?
// Allowing for a fractional tile lets us use the same value for both matrices
// Quantization factor is somewhat arbitrary...controls distribution of precision between tile fraction and relative offset
const FVector ViewOrigin = Initializer.ViewToWorld.GetOrigin(); // view的原点,世界坐标下的
const FVector ViewOriginTile = FLargeWorldRenderScalar::MakeQuantizedTile(ViewOrigin, 8.0); // 相对于瓦片进行量化,获得一个相对于瓦片的位置
// 生成一些矩阵
FRelativeViewMatrices Result;
Result.TilePosition = (FVector3f)ViewOriginTile;
Result.RelativeWorldToView = FLargeWorldRenderScalar::MakeFromRelativeWorldMatrix(ViewOriginTile * TileSize, Initializer.WorldToView);
Result.ViewToRelativeWorld = FLargeWorldRenderScalar::MakeToRelativeWorldMatrix(ViewOriginTile * TileSize, Initializer.ViewToWorld);
Result.ViewToClip = FMatrix44f(Initializer.ViewToClip);
Result.ClipToView = FMatrix44f(Initializer.ClipToView);
Result.RelativeWorldToClip = Result.RelativeWorldToView * Result.ViewToClip;
Result.ClipToRelativeWorld = Result.ClipToView * Result.ViewToRelativeWorld;
Result.PrevViewToRelativeWorld = FLargeWorldRenderScalar::MakeClampedToRelativeWorldMatrix(ViewOriginTile * TileSize, Initializer.PrevViewToWorld);
Result.PrevClipToView = FMatrix44f(Initializer.PrevClipToView);
Result.PrevClipToRelativeWorld = Result.PrevClipToView * Result.PrevViewToRelativeWorld;
return Result;
}
// 获取瓦片大小,一种将屏幕分割为瓦片的渲染技术
朴素的做法是把所有像素一起提交了,瓦片是把屏幕分成很多块,每块16x16 独立计算,当然同时也会引入瓦片之间的过渡物体。
公式是
瓦片的位置 = (ViewOrigin.XYZ / TileSize x 8 + 0.5) / 8
static TScalar MakeQuantizedTile(double InValue, double InQuantization)
{
return static_cast<TScalar>(FMath::FloorToDouble((InValue / GetTileSize()) * InQuantization + 0.5) / InQuantization);
}
回来,开头算了一些矩阵相关的数据
bool FBatchedElements::Draw(FRHICommandList& RHICmdList, const FMeshPassProcessorRenderState& DrawRenderState, ERHIFeatureLevel::Type FeatureLevel, const FSceneView& View, bool bHitTesting, float Gamma /* = 1.0f */, EBlendModeFilter::Type Filter /* = EBlendModeFilter::All */) const
{
const FRelativeViewMatrices RelativeMatrices = FRelativeViewMatrices::Create(View.ViewMatrices);
const FMatrix& WorldToClip = View.ViewMatrices.GetViewProjectionMatrix();
const FMatrix& ClipToWorld = View.ViewMatrices.GetInvViewProjectionMatrix();
const uint32 ViewportSizeX = View.UnscaledViewRect.Width();
const uint32 ViewportSizeY = View.UnscaledViewRect.Height();
PSO
bool FBatchedElements::Draw(FRHICommandList& RHICmdList, const FMeshPassProcessorRenderState& DrawRenderState, ERHIFeatureLevel::Type FeatureLevel, const FSceneView& View, bool bHitTesting, float Gamma /* = 1.0f */, EBlendModeFilter::Type Filter /* = EBlendModeFilter::All */) const
{
// 。。。
FGraphicsPipelineStateInitializer GraphicsPSOInit;
RHICmdList.ApplyCachedRenderTargets(GraphicsPSOInit);
DrawRenderState.ApplyToPSO(GraphicsPSOInit);
uint32 StencilRef = DrawRenderState.GetStencilRef();
GraphicsPSOInit.RasterizerState = TStaticRasterizerState<FM_Solid, CM_None>::GetRHI();
回顾一下DX12的PSO
用一个 D3D12_GRAPHICS_PIPELINE_STATE_DESC 这个结构
来创建 ID3D12PipelineState PSO对象
// Graphics pipeline struct that represents the latest versions of PSO subobjects currently supported by the RHI.
struct FD3D12_GRAPHICS_PIPELINE_STATE_DESC
{
ID3D12RootSignature *pRootSignature;
D3D12_SHADER_BYTECODE VS;
D3D12_SHADER_BYTECODE MS;
D3D12_SHADER_BYTECODE AS;
D3D12_SHADER_BYTECODE PS;
D3D12_SHADER_BYTECODE GS;
#if !D3D12_USE_DERIVED_PSO || D3D12_USE_DERIVED_PSO_SHADER_EXPORTS
D3D12_BLEND_DESC BlendState;
#endif // #if !D3D12_USE_DERIVED_PSO || D3D12_USE_DERIVED_PSO_SHADER_EXPORTS
#if !D3D12_USE_DERIVED_PSO
uint32 SampleMask;
D3D12_RASTERIZER_DESC RasterizerState;
D3D12_DEPTH_STENCIL_DESC1 DepthStencilState;
#endif // #if !D3D12_USE_DERIVED_PSO
D3D12_INPUT_LAYOUT_DESC InputLayout;
D3D12_INDEX_BUFFER_STRIP_CUT_VALUE IBStripCutValue;
D3D12_PRIMITIVE_TOPOLOGY_TYPE PrimitiveTopologyType;
D3D12_RT_FORMAT_ARRAY RTFormatArray;
DXGI_FORMAT DSVFormat;
DXGI_SAMPLE_DESC SampleDesc;
uint32 NodeMask;
D3D12_CACHED_PIPELINE_STATE CachedPSO;
D3D12_PIPELINE_STATE_FLAGS Flags;
FD3D12_GRAPHICS_PIPELINE_STATE_STREAM PipelineStateStream() const;
#if PLATFORM_SUPPORTS_MESH_SHADERS
FD3D12_MESH_PIPELINE_STATE_STREAM MeshPipelineStateStream() const;
#endif
};
这个 DESC描述,有VS PS BlendState 等。一个PSO就是一条管线状态。
看UE这边,名称都很好直接读出意思
class FGraphicsPipelineStateInitializer
{
// 。。。
FBoundShaderStateInput BoundShaderState;
FRHIBlendState* BlendState;
FRHIRasterizerState* RasterizerState;
FRHIDepthStencilState* DepthStencilState;
FImmutableSamplerState ImmutableSamplerState;
EPrimitiveType PrimitiveType;
uint32 RenderTargetsEnabled;
TRenderTargetFormats RenderTargetFormats;
TRenderTargetFlags RenderTargetFlags;
EPixelFormat DepthStencilTargetFormat;
ETextureCreateFlags DepthStencilTargetFlag;
ERenderTargetLoadAction DepthTargetLoadAction;
ERenderTargetStoreAction DepthTargetStoreAction;
ERenderTargetLoadAction StencilTargetLoadAction;
ERenderTargetStoreAction StencilTargetStoreAction;
FExclusiveDepthStencil DepthStencilAccess;
uint16 NumSamples;
ESubpassHint SubpassHint;
uint8 SubpassIndex;
EConservativeRasterization ConservativeRasterization;
bool bDepthBounds;
uint8 MultiViewCount;
bool bHasFragmentDensityAttachment;
EVRSShadingRate ShadingRate;
// Note: these flags do NOT affect compilation of this PSO.
// The resulting object is invariant with respect to whatever is set here, they are
// behavior hints.
// They do not participate in equality comparisons or hashing.
union
{
struct
{
uint16 Reserved : 14;
uint16 bPSOPrecache : 1;
uint16 bFromPSOFileCache : 1;
};
uint16 Flags;
};
// Cached hash off all state data provided at creation time (Only contains hash of data which influences the PSO precaching for the current platform)
// Created from hashing the state data instead of the pointers which are used during fast runtime cache checking and compares
uint64 StatePrecachePSOHash;
};
第一个成员,这里面也有VS, PS 还有些不认识。这里的RHI意味着是跨平台的接口
struct FBoundShaderStateInput
{
// 。。。
FRHIVertexDeclaration* VertexDeclarationRHI = nullptr;
FRHIVertexShader* VertexShaderRHI = nullptr;
FRHIPixelShader* PixelShaderRHI = nullptr;
// 。。。
// 一些其他着色器
#if PLATFORM_SUPPORTS_MESH_SHADERS
FRHIMeshShader* MeshShaderRHI = nullptr;
FRHIAmplificationShader* AmplificationShaderRHI = nullptr;
#endif
#if PLATFORM_SUPPORTS_GEOMETRY_SHADERS
FRHIGeometryShader* GeometryShaderRHI = nullptr;
#endif
}
回来
bool FBatchedElements::Draw(FRHICommandList& RHICmdList, const FMeshPassProcessorRenderState& DrawRenderState, ERHIFeatureLevel::Type FeatureLevel, const FSceneView& View, bool bHitTesting, float Gamma /* = 1.0f */, EBlendModeFilter::Type Filter /* = EBlendModeFilter::All */) const
{
// 。。。
FGraphicsPipelineStateInitializer GraphicsPSOInit; // 后面大段都是为了填充这个变量,就像DX里面的 FD3D12_GRAPHICS_PIPELINE_STATE_DESC
RHICmdList.ApplyCachedRenderTargets(GraphicsPSOInit); // 把之前缓存的数据能赋值的赋值过来
DrawRenderState.ApplyToPSO(GraphicsPSOInit); // 传递 BlendState 和 DepthStencilState
uint32 StencilRef = DrawRenderState.GetStencilRef();
GraphicsPSOInit.RasterizerState = TStaticRasterizerState<FM_Solid, CM_None>::GetRHI();
ApplyCachedRenderTargets
void ApplyCachedRenderTargets(
FGraphicsPipelineStateInitializer& GraphicsPSOInit
)
{
GraphicsPSOInit.RenderTargetsEnabled = PersistentState.CachedNumSimultanousRenderTargets; // 缓存的要渲染的目标数
// 遍历
for (uint32 i = 0; i < GraphicsPSOInit.RenderTargetsEnabled; ++i)
{
if (PersistentState.CachedRenderTargets[i].Texture) // 是贴图
{
GraphicsPSOInit.RenderTargetFormats[i] = UE_PIXELFORMAT_TO_UINT8(PersistentState.CachedRenderTargets[i].Texture->GetFormat()); // 格式,比如 PF_B8G8R8A8
GraphicsPSOInit.RenderTargetFlags[i] = PersistentState.CachedRenderTargets[i].Texture->GetFlags();
}
else // 未知
{
GraphicsPSOInit.RenderTargetFormats[i] = PF_Unknown;
}
if (GraphicsPSOInit.RenderTargetFormats[i] != PF_Unknown)
{
GraphicsPSOInit.NumSamples = static_cast<uint16>(PersistentState.CachedRenderTargets[i].Texture->GetNumSamples()); // 采样次数
}
}
if (PersistentState.CachedDepthStencilTarget.Texture) // DepthStencilTarget 是否存在纹理
{
GraphicsPSOInit.DepthStencilTargetFormat = PersistentState.CachedDepthStencilTarget.Texture->GetFormat();
GraphicsPSOInit.DepthStencilTargetFlag = PersistentState.CachedDepthStencilTarget.Texture->GetFlags();
const FRHITexture2DArray* TextureArray = PersistentState.CachedDepthStencilTarget.Texture->GetTexture2DArray();
}
else
{
GraphicsPSOInit.DepthStencilTargetFormat = PF_Unknown;
}
// 这些都是枚举,通过枚举读意思吧
GraphicsPSOInit.DepthTargetLoadAction = PersistentState.CachedDepthStencilTarget.DepthLoadAction;
GraphicsPSOInit.DepthTargetStoreAction = PersistentState.CachedDepthStencilTarget.DepthStoreAction;
GraphicsPSOInit.StencilTargetLoadAction = PersistentState.CachedDepthStencilTarget.StencilLoadAction;
GraphicsPSOInit.StencilTargetStoreAction = PersistentState.CachedDepthStencilTarget.GetStencilStoreAction();
GraphicsPSOInit.DepthStencilAccess = PersistentState.CachedDepthStencilTarget.GetDepthStencilAccess();
if (GraphicsPSOInit.DepthStencilTargetFormat != PF_Unknown)
{
GraphicsPSOInit.NumSamples = static_cast<uint16>(PersistentState.CachedDepthStencilTarget.Texture->GetNumSamples());
}
GraphicsPSOInit.SubpassHint = PersistentState.SubpassHint;
GraphicsPSOInit.SubpassIndex = PersistentState.SubpassIndex;
GraphicsPSOInit.MultiViewCount = PersistentState.MultiViewCount;
GraphicsPSOInit.bHasFragmentDensityAttachment = PersistentState.HasFragmentDensityAttachment;
}
ApplyToPSO
传递一下值
FORCEINLINE_DEBUGGABLE void ApplyToPSO(FGraphicsPipelineStateInitializer& GraphicsPSOInit) const
{
GraphicsPSOInit.BlendState = BlendState;
GraphicsPSOInit.DepthStencilState = DepthStencilState;
}
bool FBatchedElements::Draw(FRHICommandList& RHICmdList, const FMeshPassProcessorRenderState& DrawRenderState, ERHIFeatureLevel::Type FeatureLevel, const FSceneView& View, bool bHitTesting, float Gamma /* = 1.0f */, EBlendModeFilter::Type Filter /* = EBlendModeFilter::All */) const
{
// 。。。
// 运行环境判断 return (!IsRunningCommandlet() || IsAllowCommandletRendering()) && !IsRunningDedicatedServer() && !(USE_NULL_RHI || bHasNullRHIOnCommandline);
if (UNLIKELY(!FApp::CanEverRender()))
{
return false;
}
// 有东西要画 return( LineVertices.Num() || Points.Num() || Sprites.Num() || MeshElements.Num() || ThickLines.Num() || WireTris.Num() > 0 );
if( HasPrimsToDraw() )
{
// ClipToWorld 是 ViewProject 求逆,看开头。把XYZ坐标轴,变换到世界空间。获取相机的世界位置
FVector CameraX = ClipToWorld.TransformVector(FVector(1,0,0)).GetSafeNormal();
FVector CameraY = ClipToWorld.TransformVector(FVector(0,1,0)).GetSafeNormal();
FVector CameraZ = ClipToWorld.TransformVector(FVector(0,0,1)).GetSafeNormal();
GraphicsPSOInit.PrimitiveType = PT_TriangleList; // 图元类型是 三角形列表
// 光栅化状态,是干嘛的,看看DX的就想起来了
// GPSDesc.RasterizerState = CD3DX12_RASTERIZER_DESC(D3D12_DEFAULT);
// GPSDesc.RasterizerState.FillMode = D3D12_FILL_MODE_SOLID; // fill mode
GraphicsPSOInit.RasterizerState = TStaticRasterizerState<FM_Solid, CM_None>::GetRHI();
如果要画这些东西
if( (LineVertices.Num() > 0 || Points.Num() > 0 || ThickLines.Num() > 0 || WireTris.Num() > 0)
&& (Filter & EBlendModeFilter::OpaqueAndMasked))
{
}
画图元
// Lines/points don't support batched element parameters (yet!)
// 准备一个批处理参数
FBatchedElementParameters* BatchedElementParameters = NULL;
// Draw the line elements.
// 准备画线
if( LineVertices.Num() > 0 )
{
// Prevent integer overflow to buffer overflow.
// 线太多了
if (LineVertices.Num() > UINT32_MAX / sizeof(FSimpleElementVertex))
{
UE_LOG(LogBatchedElements, Error, TEXT("Too many line vertices. Will overflow uint32 buffer size. LineVertices: %d"), LineVertices.Num());
return false;
}
// 图元类型改成画线
GraphicsPSOInit.PrimitiveType = PT_LineList;
// Set the appropriate pixel shader parameters & shader state for the non-textured elements.
PrepareShaders(RHICmdList, GraphicsPSOInit, StencilRef, FeatureLevel, SE_BLEND_Opaque, RelativeMatrices, BatchedElementParameters, GWhiteTexture, bHitTesting, Gamma, NULL, &View);
// 。。。
}
PrepareShaders
/**
* Sets the appropriate vertex and pixel shader.
*/
void FBatchedElements::PrepareShaders(
FRHICommandList& RHICmdList,
FGraphicsPipelineStateInitializer& GraphicsPSOInit,
uint32 StencilRef,
ERHIFeatureLevel::Type FeatureLevel,
ESimpleElementBlendMode BlendMode,
const FRelativeViewMatrices& ViewMatrices,
FBatchedElementParameters* BatchedElementParameters,
const FTexture* Texture,
bool bHitTesting,
float Gamma,
const FDepthFieldGlowInfo* GlowInfo,
const FSceneView* View,
float OpacityMaskRefVal
) const
{
// used to mask individual channels and desaturate
// 单位矩阵的颜色权重
FMatrix ColorWeights( FPlane(1, 0, 0, 0), FPlane(0, 1, 0, 0), FPlane(0, 0, 1, 0), FPlane(0, 0, 0, 0) );
float GammaToUse = Gamma;
ESimpleElementBlendMode MaskedBlendMode = SE_BLEND_Opaque; // 不透明模式
// 在这个区间内,是 RGBA MASK (rgba遮罩相关的混合模式)
if(BlendMode >= SE_BLEND_RGBA_MASK_START && BlendMode <= SE_BLEND_RGBA_MASK_END)
{
/*
* Red, Green, Blue and Alpha color weights all initialized to 0
*/
FPlane R( 0.0f, 0.0f, 0.0f, 0.0f );
FPlane G( 0.0f, 0.0f, 0.0f, 0.0f );
FPlane B( 0.0f, 0.0f, 0.0f, 0.0f );
FPlane A( 0.0f, 0.0f, 0.0f, 0.0f );
/*
* Extract the color components from the in BlendMode to determine which channels should be active
*/
uint32 BlendMask = ( ( uint32 )BlendMode ) - SE_BLEND_RGBA_MASK_START; // 把 START END 映射到 0~31, 然后用二进制位描述信息
bool bRedChannel = ( BlendMask & ( 1 << 0 ) ) != 0;
bool bGreenChannel = ( BlendMask & ( 1 << 1 ) ) != 0;
bool bBlueChannel = ( BlendMask & ( 1 << 2 ) ) != 0;
bool bAlphaChannel = ( BlendMask & ( 1 << 3 ) ) != 0;
bool bDesaturate = ( BlendMask & ( 1 << 4 ) ) != 0;
bool bAlphaOnly = bAlphaChannel && !bRedChannel && !bGreenChannel && !bBlueChannel;
uint32 NumChannelsOn = ( bRedChannel ? 1 : 0 ) + ( bGreenChannel ? 1 : 0 ) + ( bBlueChannel ? 1 : 0 );
GammaToUse = bAlphaOnly? 1.0f: Gamma;
// If we are only to draw the alpha channel, make the Blend state opaque, to allow easy identification of the alpha values
// 只有 alpha 通道
if( bAlphaOnly )
{
MaskedBlendMode = SE_BLEND_Opaque; // 设置不透明模式
// 设置 BlendState,DX里面是 GPSDesc.BlendState = CD3DX12_BLEND_DESC(D3D12_DEFAULT); 龙书第10章
SetBlendState(RHICmdList, GraphicsPSOInit, MaskedBlendMode);
R.W = G.W = B.W = 1.0f;
}
else
{
// 否则,就要判断是透明模式还是不透明模式了
MaskedBlendMode = !bAlphaChannel ? SE_BLEND_Opaque : SE_BLEND_Translucent; // If alpha channel is disabled, do not allow alpha blending
SetBlendState(RHICmdList, GraphicsPSOInit, MaskedBlendMode);
// Determine the red, green, blue and alpha components of their respective weights to enable that colours prominence
R.X = bRedChannel ? 1.0f : 0.0f;
G.Y = bGreenChannel ? 1.0f : 0.0f;
B.Z = bBlueChannel ? 1.0f : 0.0f;
A.W = bAlphaChannel ? 1.0f : 0.0f;
/*
* Determine if desaturation is enabled, if so, we determine the output colour based on the number of active channels that are displayed
* e.g. if Only the red and green channels are being viewed, then the desaturation of the image will be divided by 2
*/
if( bDesaturate && NumChannelsOn )
{
float ValR, ValG, ValB;
ValR = R.X / NumChannelsOn;
ValG = G.Y / NumChannelsOn;
ValB = B.Z / NumChannelsOn;
R = FPlane( ValR, ValG, ValB, 0 );
G = FPlane( ValR, ValG, ValB, 0 );
B = FPlane( ValR, ValG, ValB, 0 );
}
}
ColorWeights = FMatrix( R, G, B, A );
}
// 。。。
}
这个 SetBlendState 是啥呢,在DX12中,龙书第10章
同时也可以看看我们自己小引擎是怎么用的
void FTransparentRenderLayer::BuildPSO()
{
Super::BuildPSO();
/*
* 按PSO顺序渲染 PSO 1 -> PSO 2
* PSO 2 是Src, PSO 1 是Dest
* 不开混合,PSO2覆盖PSO1
* RGB混合 = Src * SrcBlend(混合因子) [某种运算符] Dest * DestBlend(混合因子)
* A混合 = Src * SrcBlendAlpha(混合因子) [某种运算符] Dest * DestBlendAlpha(混合因子)
* 这里的运算符是指
* enum D3D12_BLEND_OP
{
D3D12_BLEND_OP_ADD = 1,
D3D12_BLEND_OP_SUBTRACT = 2, Cd x Fd - Cs x Fs 注意这里是Dest - Src
D3D12_BLEND_OP_REV_SUBTRACT = 3, Cs x Fs - Cd x Fd 注意这里是Src - Dest
D3D12_BLEND_OP_MIN = 4, min(Dest, Src)
D3D12_BLEND_OP_MAX = 5 max(Dest, Src)
} D3D12_BLEND_OP;
* 另外还有逻辑操作符 下面的 LogicOp
* 混合因子是 SrcBlend, DestBlend
* enum D3D12_BLEND
{
D3D12_BLEND_ZERO = 1, F(0, 0, 0, 0)
D3D12_BLEND_ONE = 2, F(1, 1, 1, 1)
D3D12_BLEND_SRC_COLOR = 3, F(Rs, Gs, Bs, As)
D3D12_BLEND_INV_SRC_COLOR = 4, F(1 - Rs, 1 - Gs, 1 - Bs, 1 - As)
D3D12_BLEND_SRC_ALPHA = 5, F(As, As, As, As)
D3D12_BLEND_INV_SRC_ALPHA = 6, F(1 - As, 1 - As, 1 - As, 1 - As)
D3D12_BLEND_DEST_ALPHA = 7, F(Ad, Ad, Ad, Ad)
D3D12_BLEND_INV_DEST_ALPHA = 8, F(1 - Ad, 1 - Ad, 1 - Ad, 1 - Ad)
D3D12_BLEND_DEST_COLOR = 9, F(Rd, Gd, Bd, Ad)
D3D12_BLEND_INV_DEST_COLOR = 10, F(1 - Rd, 1 - Gd, 1 - Bd, 1 - Ad)
D3D12_BLEND_SRC_ALPHA_SAT = 11, F(min(As, 1 - Ad), min(As, 1 - Ad), min(As, 1 - Ad), 1)
D3D12_BLEND_BLEND_FACTOR = 14, F(Bs, Gs, Rs, As) 结合 OMSetBlendFactor 使用
D3D12_BLEND_INV_BLEND_FACTOR = 15, F(1 - Bs, 1 - Gs, 1 - Rs, 1 - As)
D3D12_BLEND_SRC1_COLOR = 16, F(Rs1, Gs1, Bs1, As1)
D3D12_BLEND_INV_SRC1_COLOR = 17, F(1 - Rs1, 1 - Gs1, 1 - Bs1, 1 - As1)
D3D12_BLEND_SRC1_ALPHA = 18, F(As1, As1, As1, As1)
D3D12_BLEND_INV_SRC1_ALPHA = 19, F(1 - As1, 1 - As1, 1 - As1, 1 - As1)
D3D12_BLEND_ALPHA_FACTOR = 20, F(Af, Af, Af, Af)
D3D12_BLEND_INV_ALPHA_FACTOR = 21 F(1 - Af, 1 - Af, 1 - Af, 1 - Af)
} D3D12_BLEND;
* */
D3D12_RENDER_TARGET_BLEND_DESC TransparentRenderTargetBlendDesc;
TransparentRenderTargetBlendDesc.BlendEnable = true;
TransparentRenderTargetBlendDesc.LogicOpEnable = false; // 逻辑运算, 与混合运算互斥
TransparentRenderTargetBlendDesc.SrcBlend = D3D12_BLEND_SRC_ALPHA;
TransparentRenderTargetBlendDesc.DestBlend = D3D12_BLEND_INV_SRC_ALPHA;
TransparentRenderTargetBlendDesc.BlendOp = D3D12_BLEND_OP_ADD;
TransparentRenderTargetBlendDesc.SrcBlendAlpha = D3D12_BLEND_ONE;
TransparentRenderTargetBlendDesc.DestBlendAlpha = D3D12_BLEND_ZERO;
TransparentRenderTargetBlendDesc.BlendOpAlpha = D3D12_BLEND_OP_ADD;
TransparentRenderTargetBlendDesc.LogicOp = D3D12_LOGIC_OP_NOOP;
TransparentRenderTargetBlendDesc.RenderTargetWriteMask = D3D12_COLOR_WRITE_ENABLE_ALL; // 遮罩
IDirectXPipelineState->SetRenderTarget(0, TransparentRenderTargetBlendDesc);
IDirectXPipelineState->SetFillMode(false);
IDirectXPipelineState->Build(Transparent);
}
回来
/**
* Sets the appropriate vertex and pixel shader.
*/
void FBatchedElements::PrepareShaders(
FRHICommandList& RHICmdList,
FGraphicsPipelineStateInitializer& GraphicsPSOInit,
uint32 StencilRef,
ERHIFeatureLevel::Type FeatureLevel,
ESimpleElementBlendMode BlendMode,
const FRelativeViewMatrices& ViewMatrices,
FBatchedElementParameters* BatchedElementParameters,
const FTexture* Texture,
bool bHitTesting,
float Gamma,
const FDepthFieldGlowInfo* GlowInfo,
const FSceneView* View,
float OpacityMaskRefVal
) const
{
// 。。。
if( BatchedElementParameters != NULL )
{
// Use the vertex/pixel shader that we were given
ensure(ViewMatrices.TilePosition.IsZero());
BatchedElementParameters->BindShaders(RHICmdList, GraphicsPSOInit, FeatureLevel, FMatrix(ViewMatrices.RelativeWorldToClip), GammaToUse, ColorWeights, Texture);
}
else
{
}
}
如果有就build shader去了
但是上面代码看过来这里param确实先是null
如果不是null。
FBatchedElementParameters 有非常多的子类,都实现了 BindShaders 做各自的绑定
搜索 public FBatchedElementParameters
先挑一个看一下 BindShaders
/** Batched element parameters for texture paint shaders used for paint blending and paint mask generation */
class FMeshPaintBatchedElementParameters : public FBatchedElementParameters
{
public:
/** Binds vertex and pixel shaders for this element */
virtual void BindShaders(FRHICommandList& RHICmdList, FGraphicsPipelineStateInitializer& GraphicsPSOInit, ERHIFeatureLevel::Type InFeatureLevel, const FMatrix& InTransform, const float InGamma, const FMatrix& ColorWeights, const FTexture* Texture) override
{
MeshPaintRendering::SetMeshPaintShaders(RHICmdList, GraphicsPSOInit, InFeatureLevel, InTransform, InGamma, ShaderParams);
}
public:
/** Shader parameters */
MeshPaintRendering::FMeshPaintShaderParameters ShaderParams;
};
/** Binds the mesh paint vertex and pixel shaders to the graphics device */
void SetMeshPaintShaders(FRHICommandList& RHICmdList, FGraphicsPipelineStateInitializer& GraphicsPSOInit, ERHIFeatureLevel::Type InFeatureLevel, const FMatrix& InTransform,
const float InGamma,
const FMeshPaintShaderParameters& InShaderParams )
{
// VS
TShaderMapRef< TMeshPaintVertexShader > VertexShader(GetGlobalShaderMap(InFeatureLevel)); // 通过全局的shader map 获取shader
// PS
TMeshPaintPixelShader::FPermutationDomain MeshPaintPermutationVector;
MeshPaintPermutationVector.Set<TMeshPaintPixelShader::FMeshPaintUsePaintBrush>(InShaderParams.PaintBrushTexture != nullptr);
MeshPaintPermutationVector.Set<TMeshPaintPixelShader::FMeshPaintUseRotateTowardDirection>(InShaderParams.bRotateBrushTowardsDirection);
MeshPaintPermutationVector.Set<TMeshPaintPixelShader::FMeshPaintUseFillBucket>(InShaderParams.bUseFillBucket);
TShaderMapRef<TMeshPaintPixelShader> PixelShader(GetGlobalShaderMap(InFeatureLevel), MeshPaintPermutationVector);
// 设置PSO的一些数值
GraphicsPSOInit.BoundShaderState.VertexDeclarationRHI = GMeshPaintVertexDeclaration.VertexDeclarationRHI;
GraphicsPSOInit.BoundShaderState.VertexShaderRHI = VertexShader.GetVertexShader();
GraphicsPSOInit.BoundShaderState.PixelShaderRHI = PixelShader.GetPixelShader();
GraphicsPSOInit.PrimitiveType = PT_TriangleList;
RHICmdList.ApplyCachedRenderTargets(GraphicsPSOInit);
// set pso
SetGraphicsPipelineState(RHICmdList, GraphicsPSOInit, 0);
// Set vertex shader parameters
SetShaderParametersLegacyVS(RHICmdList, VertexShader, FMatrix44f(InTransform) ); // LWC_TODO: Precision loss
// Set pixel shader parameters
SetShaderParametersLegacyPS(RHICmdList, PixelShader, InGamma, InShaderParams );
// @todo MeshPaint: Make sure blending/color writes are setup so we can write to ALPHA if needed!
}
差不多是我们DX 这个阶段
void FOpaqueShadowRenderLayer::BuildShader()
{
std::vector<ShaderType::FShaderMacro> ShaderMacro;
BuildingShaderMacro(ShaderMacro);
std::vector<D3D_SHADER_MACRO> ShaderMacroD3D;
ShaderType::ToD3DShaderMacro(ShaderMacro, ShaderMacroD3D);
// 构建Shader
VertexShader.BuildShaders(L"Shader/Shadow.hlsl", "VSMain", "vs_5_1", ShaderMacroD3D.data());
PixelShader.BuildShaders(L"Shader/Shadow.hlsl", "PSMain", "ps_5_1", ShaderMacroD3D.data());
IDirectXPipelineState->BindShader(VertexShader, PixelShader);
// 输入布局
InputElementDesc = {
{"POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0},
{"TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 12, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0}
};
IDirectXPipelineState->BindInputLayout(InputElementDesc.data(), InputElementDesc.size());
}
回来
/**
* Sets the appropriate vertex and pixel shader.
*/
void FBatchedElements::PrepareShaders(
FRHICommandList& RHICmdList,
FGraphicsPipelineStateInitializer& GraphicsPSOInit,
uint32 StencilRef,
ERHIFeatureLevel::Type FeatureLevel,
ESimpleElementBlendMode BlendMode,
const FRelativeViewMatrices& ViewMatrices,
FBatchedElementParameters* BatchedElementParameters,
const FTexture* Texture,
bool bHitTesting,
float Gamma,
const FDepthFieldGlowInfo* GlowInfo,
const FSceneView* View,
float OpacityMaskRefVal
) const
{
// 。。。
if( BatchedElementParameters != NULL )
{
// Use the vertex/pixel shader that we were given
ensure(ViewMatrices.TilePosition.IsZero());
BatchedElementParameters->BindShaders(RHICmdList, GraphicsPSOInit, FeatureLevel, FMatrix(ViewMatrices.RelativeWorldToClip), GammaToUse, ColorWeights, Texture);
}
else
{
}
}
我们要开始看else这部分了
/**
* Sets the appropriate vertex and pixel shader.
*/
void FBatchedElements::PrepareShaders(
FRHICommandList& RHICmdList,
FGraphicsPipelineStateInitializer& GraphicsPSOInit,
uint32 StencilRef,
ERHIFeatureLevel::Type FeatureLevel,
ESimpleElementBlendMode BlendMode,
const FRelativeViewMatrices& ViewMatrices,
FBatchedElementParameters* BatchedElementParameters,
const FTexture* Texture,
bool bHitTesting,
float Gamma,
const FDepthFieldGlowInfo* GlowInfo,
const FSceneView* View,
float OpacityMaskRefVal
) const
{
// 。。。
if( BatchedElementParameters != NULL )
{
// 。。。
}
else
{
// 获取和设置 VertexShader
TShaderMapRef<FSimpleElementVS> VertexShader(GetGlobalShaderMap(FeatureLevel));
GraphicsPSOInit.BoundShaderState.VertexDeclarationRHI = GSimpleElementVertexDeclaration.VertexDeclarationRHI;
GraphicsPSOInit.BoundShaderState.VertexShaderRHI = VertexShader.GetVertexShader(); // FD3D12VertexShader
if (bHitTesting) // 是否命中测试,比如关卡编辑器我点一下地板
{
// 设置命中测试的混合模式
SetHitTestingBlendState(RHICmdList, GraphicsPSOInit, BlendMode);
// PixelShader
TShaderMapRef<FSimpleElementHitProxyPS> HitTestingPixelShader(GetGlobalShaderMap(FeatureLevel));
GraphicsPSOInit.BoundShaderState.PixelShaderRHI = HitTestingPixelShader.GetPixelShader();
// 设置PSO 和 shader参数
SetGraphicsPipelineState(RHICmdList, GraphicsPSOInit, StencilRef);
SetShaderParametersLegacyPS(RHICmdList, HitTestingPixelShader, Texture);
}
else
{
// 根据不同的混合模式设置不同的PSO参数,就不贴全部代码了
if (BlendMode == SE_BLEND_Masked) // 混合遮罩
{
// 。。。
}
// render distance field elements
else if (
BlendMode == SE_BLEND_MaskedDistanceField ||
BlendMode == SE_BLEND_MaskedDistanceFieldShadowed ||
BlendMode == SE_BLEND_TranslucentDistanceField ||
BlendMode == SE_BLEND_TranslucentDistanceFieldShadowed)
{
// 。。。
}
else if(BlendMode == SE_BLEND_TranslucentAlphaOnly || BlendMode == SE_BLEND_TranslucentAlphaOnlyWriteAlpha)
{
// 。。。
}
else if(BlendMode >= SE_BLEND_RGBA_MASK_START && BlendMode <= SE_BLEND_RGBA_MASK_END)
{
// 。。。
}
else
{
// 。。。
}
}
// Set the simple element vertex shader parameters
SetShaderParametersLegacyVS(RHICmdList, VertexShader, ViewMatrices);
}
}
FBatchedElements::DrawPointElements
调用
DrawPointElements(RHICmdList, WorldToClip, ViewportSizeX, ViewportSizeY, CameraX, CameraY);
绘制点
在游戏中拖一个 spline mesh actor 到场景中会触发
void FBatchedElements::DrawPointElements(FRHICommandList& RHICmdList, const FMatrix& Transform, const uint32 ViewportSizeX, const uint32 ViewportSizeY, const FVector& CameraX, const FVector& CameraY) const
{
// Draw the point elements.
if( Points.Num() > 0 )
{
// preallocate some memory to directly fill out
// 为什么乘2,画点其实是画一个正方形,斜切一刀分成两个三角形
const uint32 NumTris = ((uint32)Points.Num()) * 2; // even if Points.Num() == INT32_MAX, this won't overflow a uint32
// 两个三角形,每个三角形三个点
const uint32 NumVertices = NumTris * 3; // but this could
// Prevent integer overflow to buffer overflow. 点太多了
if (NumTris > (UINT32_MAX / 3) ||
NumVertices > UINT32_MAX / sizeof(FSimpleElementVertex))
{
UE_LOG(LogBatchedElements, Error, TEXT("Too many points. Will overflow uint32 buffer size. NumPoints: %d"), Points.Num());
return;
}
// 创建缓冲区
FRHIResourceCreateInfo CreateInfo(TEXT("FBatchedElements_Points"));
FBufferRHIRef VertexBufferRHI = RHICmdList.CreateBuffer(sizeof(FSimpleElementVertex) * NumVertices, BUF_VertexBuffer | BUF_Volatile, 0, ERHIAccess::VertexOrIndexBuffer, CreateInfo);
void* VerticesPtr = RHICmdList.LockBuffer(VertexBufferRHI, 0, sizeof(FSimpleElementVertex) * NumVertices, RLM_WriteOnly);
FSimpleElementVertex* PointVertices = (FSimpleElementVertex*)VerticesPtr;
// 遍历要画的点(其实是画一个小正方形)
uint32 VertIdx = 0;
for(int32 PointIndex = 0; PointIndex < Points.Num(); PointIndex++)
{
// TODO: Support quad primitives here
const FBatchedPoint& Point = Points[PointIndex];
FVector4 TransformedPosition = Transform.TransformFVector4(Point.Position); // Transform 是 WorldToClip 矩阵,获得了一个裁剪空间的位置
// 构造出其他全部的6个点
// Generate vertices for the point such that the post-transform point size is constant.
const uint32 ViewportMajorAxis = ViewportSizeX;//FMath::Max(ViewportSizeX, ViewportSizeY);
const FVector WorldPointX = CameraX * Point.Size / ViewportMajorAxis * TransformedPosition.W; // 相机在世界空间的点,乘以(点的大小 占视口的比例) 相对于摄像机缩放了
const FVector WorldPointY = CameraY * -Point.Size / ViewportMajorAxis * TransformedPosition.W;
PointVertices[VertIdx + 0] = FSimpleElementVertex(Point.Position + WorldPointX - WorldPointY,FVector2D(1,0),Point.Color,Point.HitProxyColor);
PointVertices[VertIdx + 1] = FSimpleElementVertex(Point.Position + WorldPointX + WorldPointY,FVector2D(1,1),Point.Color,Point.HitProxyColor);
PointVertices[VertIdx + 2] = FSimpleElementVertex(Point.Position - WorldPointX - WorldPointY,FVector2D(0,0),Point.Color,Point.HitProxyColor);
PointVertices[VertIdx + 3] = FSimpleElementVertex(Point.Position + WorldPointX + WorldPointY,FVector2D(1,1),Point.Color,Point.HitProxyColor);
PointVertices[VertIdx + 4] = FSimpleElementVertex(Point.Position - WorldPointX - WorldPointY,FVector2D(0,0),Point.Color,Point.HitProxyColor);
PointVertices[VertIdx + 5] = FSimpleElementVertex(Point.Position - WorldPointX + WorldPointY,FVector2D(0,1),Point.Color,Point.HitProxyColor);
VertIdx += 6;
}
RHICmdList.UnlockBuffer(VertexBufferRHI);
RHICmdList.SetStreamSource(0, VertexBufferRHI, 0);
RHICmdList.DrawPrimitive(0, NumTris, 1); // 把图元,记录在命令队列中,
}
}
画图元
// Draw the line elements.
if( LineVertices.Num() > 0 )
{
// Prevent integer overflow to buffer overflow.
if (LineVertices.Num() > UINT32_MAX / sizeof(FSimpleElementVertex))
{
UE_LOG(LogBatchedElements, Error, TEXT("Too many line vertices. Will overflow uint32 buffer size. LineVertices: %d"), LineVertices.Num());
return false;
}
GraphicsPSOInit.PrimitiveType = PT_LineList;
// Set the appropriate pixel shader parameters & shader state for the non-textured elements.
PrepareShaders(RHICmdList, GraphicsPSOInit, StencilRef, FeatureLevel, SE_BLEND_Opaque, RelativeMatrices, BatchedElementParameters, GWhiteTexture, bHitTesting, Gamma, NULL, &View);
FRHIResourceCreateInfo CreateInfo(TEXT("Lines"));
FBufferRHIRef VertexBufferRHI = RHICmdList.CreateBuffer(sizeof(FSimpleElementVertex) * LineVertices.Num(), BUF_VertexBuffer | BUF_Volatile, 0, ERHIAccess::VertexOrIndexBuffer, CreateInfo);
void* VoidPtr = RHICmdList.LockBuffer(VertexBufferRHI, 0, sizeof(FSimpleElementVertex) * LineVertices.Num(), RLM_WriteOnly);
FMemory::Memcpy(VoidPtr, LineVertices.GetData(), sizeof(FSimpleElementVertex) * LineVertices.Num());
RHICmdList.UnlockBuffer(VertexBufferRHI);
RHICmdList.SetStreamSource(0, VertexBufferRHI, 0);
int32 MaxVerticesAllowed = ((GDrawUPVertexCheckCount / sizeof(FSimpleElementVertex)) / 2) * 2;
/*
hack to avoid a crash when trying to render large numbers of line segments.
*/
MaxVerticesAllowed = FMath::Min(MaxVerticesAllowed, 64 * 1024);
int32 MinVertex=0;
int32 TotalVerts = (LineVertices.Num() / 2) * 2;
while( MinVertex < TotalVerts )
{
int32 NumLinePrims = FMath::Min( MaxVerticesAllowed, TotalVerts - MinVertex ) / 2;
RHICmdList.DrawPrimitive(MinVertex, NumLinePrims, 1);
MinVertex += NumLinePrims * 2;
}
VertexBufferRHI.SafeRelease();
}
回到这里,
我们了解了 PrepareShaders
上下的部分和DrawPointElements 很像了
创建一个buffer。
把图元塞进命令队列 DrawPrimitive
其他分支比较类似
比如这个绘制粗线条是绘制,长立方体
if ( ThickLines.Num() > 0 )
{
// 绘制 4 个面(前,上,左,右),每个面两个三角形,就8个三角形。每个三角形3个点。 8 x 3
FBufferRHIRef VertexBufferRHI = RHICmdList.CreateBuffer(sizeof(FSimpleElementVertex) * 8 * 3 * NumLinesThisBatch, BUF_VertexBuffer | BUF_Volatile, 0, ERHIAccess::VertexOrIndexBuffer, CreateInfo);
for (int i = 0; i < NumLinesThisBatch; ++i)
{
// ...
}
RHICmdList.UnlockBuffer(VertexBufferRHI);
RHICmdList.SetStreamSource(0, VertexBufferRHI, 0);
RHICmdList.DrawPrimitive(0, 8 * NumLinesThisBatch, 1);
}
线框三角形、
if (WireTris.Num() > 0)
{
}
绘制精灵(比如做特效)
什么是精灵
struct FBatchedSprite
{
FVector Position;
float SizeX;
float SizeY;
const FTexture* Texture;
FLinearColor Color;
FColor HitProxyColor;
float U;
float UL;
float V;
float VL;
float OpacityMaskRefVal;
uint8 BlendMode;
};
TArray<FBatchedSprite> Sprites;
X, Y 位置,贴图,颜色,UV,混合模式等。
if( Sprites.Num() > 0 )
{
// Sprites don't support batched element parameters (yet!)
FBatchedElementParameters* BatchedElementParameters = NULL;
//Sort sprites by blend mode and texture
struct FCompareSprite
{
explicit FCompareSprite(EBlendModeFilter::Type InFilter) : ValidBlendFilter(InFilter) {}
FORCEINLINE bool operator()( const FBatchedElements::FBatchedSprite& SpriteA, const FBatchedElements::FBatchedSprite& SpriteB ) const
{
if (SpriteA.BlendMode != SpriteB.BlendMode)
{
const bool bBlendAValid = (ValidBlendFilter & SpriteA.BlendMode) != 0;
const bool bBlendBValid = (ValidBlendFilter & SpriteB.BlendMode) != 0;
if (bBlendAValid != bBlendBValid) return bBlendAValid; // we want valid blend modes to sort in front of invalid blend modes
return SpriteA.BlendMode < SpriteB.BlendMode;
}
return SpriteA.Texture < SpriteB.Texture;
}
EBlendModeFilter::Type ValidBlendFilter;
};
// 获取精灵并排序
TArray<FBatchedSprite> SortedSprites = Sprites;
SortedSprites.Sort(FCompareSprite(Filter));
// count the number of sprites that have valid blend modes
// (they have been sorted to the front of the list)
// 计算有合法混合模式的精灵数量
int32 ValidSpriteCount = 0;
while (ValidSpriteCount < SortedSprites.Num() &&
(Filter & GetBlendModeFilter((ESimpleElementBlendMode)SortedSprites[ValidSpriteCount].BlendMode)))
{
++ValidSpriteCount;
}
if (ValidSpriteCount > 0)
{
// Prevent integer overflow to buffer overflow.
// 画的太多了
if (ValidSpriteCount > UINT32_MAX / sizeof(FSimpleElementVertex) * 6)
{
UE_LOG(LogBatchedElements, Error, TEXT("Too many sprites. Will overflow uint32 buffer size. ValidSpriteCount: %d"), ValidSpriteCount);
return false;
}
// 创建buffer
FRHIResourceCreateInfo CreateInfo(TEXT("Sprites"));
FBufferRHIRef VertexBufferRHI = RHICmdList.CreateBuffer(sizeof(FSimpleElementVertex) * ValidSpriteCount * 6, BUF_VertexBuffer | BUF_Volatile, 0, ERHIAccess::VertexOrIndexBuffer, CreateInfo);
void* VoidPtr = RHICmdList.LockBuffer(VertexBufferRHI, 0, sizeof(FSimpleElementVertex) * ValidSpriteCount * 6, RLM_WriteOnly);
FSimpleElementVertex* SpriteList = reinterpret_cast<FSimpleElementVertex*>(VoidPtr);
// 遍历这么多数量
for (int32 SpriteIndex = 0; SpriteIndex < ValidSpriteCount; SpriteIndex++)
{
// 看起来精灵就是一个有贴图的正方形
const FBatchedSprite& Sprite = SortedSprites[SpriteIndex];
FSimpleElementVertex* Vertex = &SpriteList[SpriteIndex * 6];
// Compute the sprite vertices.
const FVector WorldSpriteX = CameraX * Sprite.SizeX;
const FVector WorldSpriteY = CameraY * -Sprite.SizeY * GProjectionSignY;
const float UStart = Sprite.U / Sprite.Texture->GetSizeX();
const float UEnd = (Sprite.U + Sprite.UL) / Sprite.Texture->GetSizeX(); // UL 是 U 方向的长度,VL 同理
const float VStart = Sprite.V / Sprite.Texture->GetSizeY();
const float VEnd = (Sprite.V + Sprite.VL) / Sprite.Texture->GetSizeY();
Vertex[0] = FSimpleElementVertex(Sprite.Position + WorldSpriteX - WorldSpriteY, FVector2D(UEnd, VStart), Sprite.Color, Sprite.HitProxyColor);
Vertex[1] = FSimpleElementVertex(Sprite.Position + WorldSpriteX + WorldSpriteY, FVector2D(UEnd, VEnd), Sprite.Color, Sprite.HitProxyColor);
Vertex[2] = FSimpleElementVertex(Sprite.Position - WorldSpriteX - WorldSpriteY, FVector2D(UStart, VStart), Sprite.Color, Sprite.HitProxyColor);
Vertex[3] = FSimpleElementVertex(Sprite.Position + WorldSpriteX + WorldSpriteY, FVector2D(UEnd, VEnd), Sprite.Color, Sprite.HitProxyColor);
Vertex[4] = FSimpleElementVertex(Sprite.Position - WorldSpriteX - WorldSpriteY, FVector2D(UStart, VStart), Sprite.Color, Sprite.HitProxyColor);
Vertex[5] = FSimpleElementVertex(Sprite.Position - WorldSpriteX + WorldSpriteY, FVector2D(UStart, VEnd), Sprite.Color, Sprite.HitProxyColor);
}
RHICmdList.UnlockBuffer(VertexBufferRHI);
//First time init
const FTexture* CurrentTexture = SortedSprites[0].Texture;
ESimpleElementBlendMode CurrentBlendMode = (ESimpleElementBlendMode)SortedSprites[0].BlendMode;
int32 BatchStartIndex = 0;
float CurrentOpacityMask = SortedSprites[0].OpacityMaskRefVal;
// Start loop at 1, since we've already started the first batch with the first sprite in the list
for (int32 SpriteIndex = 1; SpriteIndex < ValidSpriteCount + 1; SpriteIndex++)
{
// Need to flush the current batch once we hit the end of the list, or if state of this sprite doesn't match current batch
if (SpriteIndex == ValidSpriteCount ||
CurrentTexture != SortedSprites[SpriteIndex].Texture ||
CurrentBlendMode != SortedSprites[SpriteIndex].BlendMode ||
CurrentOpacityMask != SortedSprites[SpriteIndex].OpacityMaskRefVal)
{
const int32 SpriteNum = SpriteIndex - BatchStartIndex;
const int32 BaseVertex = BatchStartIndex * 6;
const int32 PrimCount = SpriteNum * 2;
PrepareShaders(RHICmdList, GraphicsPSOInit, StencilRef, FeatureLevel, CurrentBlendMode, RelativeMatrices, BatchedElementParameters, CurrentTexture, bHitTesting, Gamma, NULL, &View, CurrentOpacityMask);
RHICmdList.SetStreamSource(0, VertexBufferRHI, 0);
RHICmdList.DrawPrimitive(BaseVertex, PrimCount, 1);
// begin the next batch
if (SpriteIndex < ValidSpriteCount)
{
BatchStartIndex = SpriteIndex;
CurrentTexture = SortedSprites[SpriteIndex].Texture;
CurrentBlendMode = (ESimpleElementBlendMode)SortedSprites[SpriteIndex].BlendMode;
CurrentOpacityMask = SortedSprites[SpriteIndex].OpacityMaskRefVal;
}
}
}
VertexBufferRHI.SafeRelease();
}
}
if( MeshElements.Num() > 0)
{
}
Render_GameThread END
上面一大段结束后
这个Draw结束了
bool FCanvasBatchedElementRenderItem::Render_GameThread(const FCanvas* Canvas, FCanvasRenderThreadScope& RenderScope)
{
checkSlow(Data);
bool bDirty=false;
if( Data->BatchedElements.HasPrimsToDraw() ) // 批处理是否需要绘制图元
{
// 。。。
// 添加一个pass,lambda在渲染线程去做
RenderScope.AddPass(
TEXT("CanvasBatchedElements"),
[DrawParameters](FRHICommandList& RHICmdList)
{
// 。。。
DrawParameters.RenderData->BatchedElements.Draw(
RHICmdList,
DrawRenderState,
DrawParameters.FeatureLevel,
SceneView,
DrawParameters.bHitTesting,
DrawParameters.DisplayGamma);
// 。。。
});
}
// 。。。
return bDirty;
}
RenderViewFamilies_RenderThread
前文的这个位置没有仔细深入
BeginRenderingViewFamilies 这个函数准备开始画3D场景了
这个函数前面通过 SendAllEndOfFrameUpdates 确保之前已经画完了
SendAllEndOfFrameUpdates 做了一些tick actor结束帧相关,并且把transform和其他数据传递给了GPU
然后做了一些 emmmmmmmmmmmmmmmmm
然后往渲染线程有了如下调用
void FRendererModule::BeginRenderingViewFamilies(FCanvas* Canvas, TArrayView<FSceneViewFamily*> ViewFamilies)
{
// 。。。
const uint64 DrawSceneEnqueue = FPlatformTime::Cycles64();
ENQUEUE_RENDER_COMMAND(FDrawSceneCommand)(
[LocalSceneRenderers = CopyTemp(SceneRenderers), DrawSceneEnqueue](FRHICommandListImmediate& RHICmdList)
{
uint64 SceneRenderStart = FPlatformTime::Cycles64();
const float StartDelayMillisec = FPlatformTime::ToMilliseconds64(SceneRenderStart - DrawSceneEnqueue);
CSV_CUSTOM_STAT_GLOBAL(DrawSceneCommand_StartDelay, StartDelayMillisec, ECsvCustomStatOp::Set);
RenderViewFamilies_RenderThread(RHICmdList, LocalSceneRenderers);
FlushPendingDeleteRHIResources_RenderThread();
});
}
RenderViewFamilies_RenderThread
渲染线程开始
/**
* Helper function performing actual work in render thread.
*
* @param SceneRenderers List of scene renderers to use for rendering.
*/
static void RenderViewFamilies_RenderThread(FRHICommandListImmediate& RHICmdList, const TArray<FSceneRenderer*>& SceneRenderers)
{
LLM_SCOPE(ELLMTag::SceneRender);
// All renderers point to the same Scene (calling code asserts this)
FScene* const Scene = SceneRenderers[0]->Scene;
FSceneRenderer::RenderThreadBegin(RHICmdList, SceneRenderers);
RenderThreadBegin
void FSceneRenderer::RenderThreadBegin(FRHICommandListImmediate& RHICmdList, const TArray<FSceneRenderer*>& SceneRenderers)
{
CleanUp(RHICmdList);
CleanUp
void FSceneRenderer::CleanUp(FRHICommandListImmediate& RHICmdList)
{
if (GSceneRenderCleanUpState.CompletionMode == ESceneRenderCleanUpMode::Immediate || GSceneRenderCleanUpState.Renderers.IsEmpty())
{
return;
}
if (!GSceneRenderCleanUpState.bWaitForTasksComplete)
{
switch (GSceneRenderCleanUpState.CompletionMode)
{
case ESceneRenderCleanUpMode::Deferred:
WaitForTasksAndDeleteSceneRenderers(RHICmdList, GSceneRenderCleanUpState.Renderers);
break;
case ESceneRenderCleanUpMode::DeferredAndAsync:
GSceneRenderCleanUpState.Task->Wait(ENamedThreads::GetRenderThread_Local()); // 等待线程完成
break;
}
}
FinishCleanUp(RHICmdList);
GSceneRenderCleanUpState = {};
}
GSceneRenderCleanUpState 是场景清理状态
CompletionMode 是清理模式
struct FSceneRenderCleanUpState
{
TArray<FSceneRenderer*> Renderers;
FGraphEventRef Task;
ESceneRenderCleanUpMode CompletionMode = ESceneRenderCleanUpMode::Immediate;
bool bWaitForTasksComplete = false;
};
WaitForTasksAndDeleteSceneRenderers
static void WaitForTasksAndDeleteSceneRenderers(FRHICommandListImmediate& RHICmdList, const TArray<FSceneRenderer*>& SceneRenderers)
{
{
QUICK_SCOPE_CYCLE_COUNTER(STAT_DeleteSceneRenderer_WaitForTasks);
RHICmdList.ImmediateFlush(EImmediateFlushType::WaitForOutstandingTasksOnly); // 等待为未完成任务
}
DeleteSceneRenderers(SceneRenderers, FParallelMeshDrawCommandPass::EWaitThread::Render); // 删除场景的渲染
}
ImmediateFlush
RHICmdList.ImmediateFlush(EImmediateFlushType::WaitForOutstandingTasksOnly);
namespace EImmediateFlushType
{
enum Type
{
WaitForOutstandingTasksOnly = 0, // 等待未完成的任务
DispatchToRHIThread = 1, // 刷新任务会给RHI线程
FlushRHIThread = 2, // 刷新RHI线程
FlushRHIThreadFlushResources = 3 // 刷新RHI线程和资源
};
};
那么
RHICmdList.ImmediateFlush(EImmediateFlushType::WaitForOutstandingTasksOnly); 就是等待未完成任务
FORCEINLINE_DEBUGGABLE void FRHICommandListImmediate::ImmediateFlush(EImmediateFlushType::Type FlushType)
{
if (FlushType == EImmediateFlushType::WaitForOutstandingTasksOnly)
{
WaitForTasks();
}
else
{
// 。。。
}
}
如果有任何为完成的任务,就 WaitUntilTasksComplete
void FRHICommandListImmediate::WaitForTasks()
{
check(IsInRenderingThread());
if (WaitOutstandingTasks.Num())
{
bool bAny = false;
for (int32 Index = 0; Index < WaitOutstandingTasks.Num(); Index++)
{
if (!WaitOutstandingTasks[Index]->IsComplete())
{
bAny = true;
break;
}
}
if (bAny)
{
SCOPE_CYCLE_COUNTER(STAT_ExplicitWait);
ENamedThreads::Type RenderThread_Local = ENamedThreads::GetRenderThread_Local();
check(!FTaskGraphInterface::Get().IsThreadProcessingTasks(RenderThread_Local));
FTaskGraphInterface::Get().WaitUntilTasksComplete(WaitOutstandingTasks, RenderThread_Local);
}
WaitOutstandingTasks.Reset();
}
}
WaitForTasksAndDeleteSceneRenderers
回到
static void WaitForTasksAndDeleteSceneRenderers(FRHICommandListImmediate& RHICmdList, const TArray<FSceneRenderer*>& SceneRenderers)
{
{
QUICK_SCOPE_CYCLE_COUNTER(STAT_DeleteSceneRenderer_WaitForTasks);
RHICmdList.ImmediateFlush(EImmediateFlushType::WaitForOutstandingTasksOnly); // 等待为未完成任务
}
DeleteSceneRenderers(SceneRenderers, FParallelMeshDrawCommandPass::EWaitThread::Render); // 删除场景的渲染
}
DeleteSceneRenderers
static void DeleteSceneRenderers(const TArray<FSceneRenderer*>& SceneRenderers, FParallelMeshDrawCommandPass::EWaitThread WaitThread)
{
SCOPED_NAMED_EVENT_TEXT("DeleteSceneRenderer", FColor::Red);
for (FSceneRenderer* SceneRenderer : SceneRenderers)
{
// Wait for all dispatched shadow mesh draw tasks.
for (int32 PassIndex = 0; PassIndex < SceneRenderer->DispatchedShadowDepthPasses.Num(); ++PassIndex)
{
SceneRenderer->DispatchedShadowDepthPasses[PassIndex]->WaitForTasksAndEmpty(WaitThread); // 等待所有阴影网格
}
for (FViewInfo& View : SceneRenderer->Views)
{
View.WaitForTasks(WaitThread); // 视图绘制完成
}
}
FViewInfo::DestroyAllSnapshots(WaitThread); // 删除所有拍照
for (FSceneRenderer* SceneRenderer : SceneRenderers)
{
delete SceneRenderer;
}
}
RenderThreadBegin
回来
void FSceneRenderer::RenderThreadBegin(FRHICommandListImmediate& RHICmdList, const TArray<FSceneRenderer*>& SceneRenderers)
{
CleanUp(RHICmdList);
// Initialize "AllFamilyViews" and "AllFamilies" array in each scene renderer. Initialize array in first SceneRenderer, then copy to remaining.
// 把所有的 ViewInfo 加到0号里面
for (FSceneRenderer* SceneRenderer : SceneRenderers)
{
for (const FViewInfo& ViewInfo : SceneRenderer->Views)
{
SceneRenderers[0]->AllFamilyViews.Add(&ViewInfo);
}
SceneRenderers[0]->AllFamilies.Add(&SceneRenderer->ViewFamily);
}
// 然后复制给后面的
for (int32 RendererIndex = 1; RendererIndex < SceneRenderers.Num(); RendererIndex++)
{
SceneRenderers[RendererIndex]->AllFamilyViews = SceneRenderers[0]->AllFamilyViews;
SceneRenderers[RendererIndex]->AllFamilies = SceneRenderers[0]->AllFamilies;
}
// 特效
FScene* Scene = SceneRenderers[0]->Scene;
// Cache the FXSystem for the duration of the scene render
// UWorld::CleanupWorldInternal() will mark the system as pending kill on the GameThread and then enqueue a delete command
//-TODO: The call to IsPendingKill should no longer be required as we are caching & using within a single render command
class FFXSystemInterface* FXSystem = Scene ? Scene->FXSystem : nullptr;
if (FXSystem && FXSystem->IsPendingKill())
{
FXSystem = nullptr;
}
for (FSceneRenderer* SceneRenderer : SceneRenderers)
{
SceneRenderer->FXSystem = FXSystem;
}
}
RenderViewFamilies_RenderThread
回来
RenderThreadBegin 看完了
如果前面有没有渲染完成的就等待(阴影网格, 视图,删除所有拍照)
然后手机一下view family
初始化一下FXSystem
有开始,那么最下面也有END
/**
* Helper function performing actual work in render thread.
*
* @param SceneRenderers List of scene renderers to use for rendering.
*/
static void RenderViewFamilies_RenderThread(FRHICommandListImmediate& RHICmdList, const TArray<FSceneRenderer*>& SceneRenderers)
{
LLM_SCOPE(ELLMTag::SceneRender);
// All renderers point to the same Scene (calling code asserts this)
FScene* const Scene = SceneRenderers[0]->Scene;
FSceneRenderer::RenderThreadBegin(RHICmdList, SceneRenderers);
bool bAnyShowHitProxies = false; // 是否有任何命中代理
#if WITH_DEBUG_VIEW_MODES
// Flag so we only call FGPUSkinCacheVisualizationData::Update and draw the visualization text once
bool bUpdatedGPUSkinCacheVisualization = false; // 是否已经更新了GPU的蒙皮缓存
#endif
// update any resources that needed a deferred update
FDeferredUpdateResource::UpdateResources(RHICmdList); // 更新所有需要延迟更新的资源,怎么更新的?
for (FSceneRenderer* SceneRenderer : SceneRenderers)
{
const ERHIFeatureLevel::Type FeatureLevel = SceneRenderer->FeatureLevel;
FSceneViewFamily& ViewFamily = SceneRenderer->ViewFamily;
// 又看到了 FRDGBuilder, 构造一个
FRDGBuilder GraphBuilder(
RHICmdList,
RDG_EVENT_NAME("SceneRenderer_%s(ViewFamily=%s)",
ViewFamily.EngineShowFlags.HitProxies ? TEXT("RenderHitProxies") : TEXT("Render"),
ViewFamily.bResolveScene ? TEXT("Primary") : TEXT("Auxiliary")
),
FSceneRenderer::GetRDGParalelExecuteFlags(FeatureLevel) // 是否允许并行的执行 ERDGBuilderFlags
);
// We need to execute the pre-render view extensions before we do any view dependent work.
// 执行视图扩展,用户可以在这里添加扩展,并在这里执行
FSceneRenderer::ViewExtensionPreRender_RenderThread(GraphBuilder, SceneRenderer);
SCOPE_CYCLE_COUNTER(STAT_TotalSceneRenderingTime);
SCOPED_NAMED_EVENT_TCHAR_CONDITIONAL(*ViewFamily.ProfileDescription, FColor::Red, !ViewFamily.ProfileDescription.IsEmpty());
const uint64 FamilyRenderStart = FPlatformTime::Cycles64();
#if WITH_DEBUG_VIEW_MODES
// 允许GPU蒙皮缓存的可视化?比如勾选一些调试选项,会渲染不一样的shader
const bool bAllowGPUSkinCacheVisualization = AllowDebugViewShaderMode(DVSM_VisualizeGPUSkinCache, ViewFamily.GetShaderPlatform(), ViewFamily.GetFeatureLevel());
if (bAllowGPUSkinCacheVisualization && SceneRenderer->Views.Num() > 0)
{
FViewInfo& View = SceneRenderer->Views[0];
FGPUSkinCacheVisualizationData& VisualizationData = GetGPUSkinCacheVisualizationData();
// Only run visualization update once, but set debug flags for all view families if the mode is active
// Note VisualizationData.Update needs to be called per frame, as || lazy evaluation is used, so need to do it before evaluating VisualizeGPUSkinCache flag
if (bUpdatedGPUSkinCacheVisualization || VisualizationData.Update(View.CurrentGPUSkinCacheVisualizationMode) || ViewFamily.EngineShowFlags.VisualizeGPUSkinCache)
{
// When activating visualization from the command line, enable VisualizeGPUSkinCache.
ViewFamily.EngineShowFlags.SetVisualizeGPUSkinCache(true);
ViewFamily.DebugViewShaderMode = DVSM_VisualizeGPUSkinCache;
// Only draw the visualization info text once.
if (!bUpdatedGPUSkinCacheVisualization)
{
SceneRenderer->DrawGPUSkinCacheVisualizationInfoText(); // 绘制一些数据文本到屏幕上
bUpdatedGPUSkinCacheVisualization = true;
}
}
}
#endif // WITH_DEBUG_VIEW_MODES
#if WITH_MGPU
if (ViewFamily.bForceCopyCrossGPU) // copy 多 gpu
{
GraphBuilder.EnableForceCopyCrossGPU();
}
#endif
if (ViewFamily.EngineShowFlags.HitProxies) // 是否要限速命中代理
{
// Render the scene's hit proxies.
SceneRenderer->RenderHitProxies(GraphBuilder);
bAnyShowHitProxies = true;
}
else
{
// Render the scene. // 重点,后文展开 !!!!!!!!!!!!!!!!!
SceneRenderer->Render(GraphBuilder);
}
SceneRenderer->FlushCrossGPUFences(GraphBuilder);
GraphBuilder.Execute(); // RDG Execute 看他!!!!!!!!!!!!!!!!!!!!!
if (SceneRenderer->ViewFamily.ProfileSceneRenderTime)
{
*SceneRenderer->ViewFamily.ProfileSceneRenderTime = (float)FPlatformTime::ToSeconds64(FPlatformTime::Cycles64() - FamilyRenderStart);
}
}
发束,头发渲染
{
CSV_SCOPED_TIMING_STAT_EXCLUSIVE(PostRenderCleanUp);
if (IsHairStrandsEnabled(EHairStrandsShaderType::All, Scene->GetShaderPlatform()) && (SceneRenderers[0]->AllFamilyViews.Num() > 0) && !bAnyShowHitProxies)
{
FHairStrandsBookmarkParameters Parameters;
CreateHairStrandsBookmarkParameters(Scene, SceneRenderers[0]->Views, SceneRenderers[0]->AllFamilyViews, Parameters);
if (Parameters.HasInstances())
{
RunHairStrandsBookmark(EHairStrandsBookmark::ProcessEndOfFrame, Parameters);
}
}
// Only reset per-frame scene state once all views have processed their frame, including those in planar reflections
for (int32 CacheType = 0; CacheType < UE_ARRAY_COUNT(SceneRenderers[0]->Scene->DistanceFieldSceneData.PrimitiveModifiedBounds); CacheType++)
{
ResetAndShrinkModifiedBounds(Scene->DistanceFieldSceneData.PrimitiveModifiedBounds[CacheType]);
}
// Immediately issue EndFrame() for all extensions in case any of the outstanding tasks they issued getting out of this frame
extern TSet<IPersistentViewUniformBufferExtension*> PersistentViewUniformBufferExtensions;
for (IPersistentViewUniformBufferExtension* Extension : PersistentViewUniformBufferExtensions)
{
Extension->EndFrame();
}
}
光线追踪
#if RHI_RAYTRACING
// Release the ray tracing scene resources if ray tracing wasn't used
if (!Scene->RayTracingScene.bUsedThisFrame)
{
Scene->RayTracingScene.ResetAndReleaseResources();
}
#endif // RHI_RAYTRACING
统计相关,内存啥的
#if STATS
{
QUICK_SCOPE_CYCLE_COUNTER(STAT_RenderViewFamily_RenderThread_MemStats);
// Update scene memory stats that couldn't be tracked continuously
SET_MEMORY_STAT(STAT_RenderingSceneMemory, Scene->GetSizeBytes());
SIZE_T ViewStateMemory = 0;
for (FSceneRenderer* SceneRenderer : SceneRenderers)
{
for (int32 ViewIndex = 0; ViewIndex < SceneRenderer->Views.Num(); ViewIndex++)
{
if (SceneRenderer->Views[ViewIndex].State)
{
ViewStateMemory += SceneRenderer->Views[ViewIndex].State->GetSizeBytes();
}
}
}
SET_MEMORY_STAT(STAT_ViewStateMemory, ViewStateMemory);
SET_MEMORY_STAT(STAT_LightInteractionMemory, FLightPrimitiveInteraction::GetMemoryPoolSize());
}
#endif
FRendererOnScreenNotification 的一个事件广播一下
统计相关
RenderThreadEnd 之前函数开头也有个begin
#if !UE_BUILD_SHIPPING
// Update on screen notifications.
FRendererOnScreenNotification::Get().Broadcast();
#endif
#if STATS
QUICK_SCOPE_CYCLE_COUNTER(STAT_RenderViewFamily_RenderThread_RHIGetGPUFrameCycles);
if (FPlatformProperties::SupportsWindowedMode() == false)
{
/** Update STATS with the total GPU time taken to render the last frame. */
SET_CYCLE_COUNTER(STAT_TotalGPUFrameTime, RHIGetGPUFrameCycles());
}
#endif
FSceneRenderer::RenderThreadEnd(RHICmdList, SceneRenderers);
}
FSceneRenderer::RenderThreadEnd
渲染线程结束
大概看了一下,主要是资源的释放,和等待xxx结束
Lumen 全局环境光 光线追踪
光线追踪的思路:
- 从光源发射射线,反弹到摄像机就结束,会造成很多浪费,因为很多光没有打到视口上(会有噪点)。
- 从摄像机发生射线,开始逆推到光源。反射次数越高,消耗越大。
解决的问题是环境光。
lumen 会经历几个阶段
- 虚拟光源
- 光线追踪
- 光栅化更新
- 动态更新
if (ViewFamily.EngineShowFlags.HitProxies)
{
// Render the scene's hit proxies.
SceneRenderer->RenderHitProxies(GraphBuilder);
bAnyShowHitProxies = true;
}
else
{
// Render the scene.
SceneRenderer->Render(GraphBuilder);
}
从上一回的
SceneRenderer->Render(GraphBuilder);
开始
这东西有两个子类
一个延迟渲染,一个移动端的渲染
class FDeferredShadingSceneRenderer : public FSceneRenderer {};
class FMobileSceneRenderer : public FSceneRenderer {};
FDeferredShadingSceneRenderer::Render
是否渲染,获取渲染输出结构,是否开启nanite
void FDeferredShadingSceneRenderer::Render(FRDGBuilder& GraphBuilder)
{
if (!ViewFamily.EngineShowFlags.Rendering)
{
return;
}
// If this is scene capture rendering depth pre-pass, we'll take the shortcut function RenderSceneCaptureDepth if optimization switch is on.
const ERendererOutput RendererOutput = GetRendererOutput();
const bool bNaniteEnabled = IsNaniteEnabled();
// 捕获渲染过程,以便于开发者进行调试和分析
#if !UE_BUILD_SHIPPING
RenderCaptureInterface::FScopedCapture RenderCapture(GCaptureNextDeferredShadingRendererFrame-- == 0, GraphBuilder, TEXT("DeferredShadingSceneRenderer"));
// Prevent overflow every 2B frames.
GCaptureNextDeferredShadingRendererFrame = FMath::Max(-1, GCaptureNextDeferredShadingRendererFrame);
#endif
FSceneRenderer::UpdateScene
FInitViewTaskDatas InitViewTaskDatas = UpdateScene(GraphBuilder, FGlobalDynamicBuffers(DynamicIndexBufferForInitViews, DynamicVertexBufferForInitViews, DynamicReadBufferForInitViews));
IVisibilityTaskData* FSceneRenderer::UpdateScene(FRDGBuilder& GraphBuilder, FGlobalDynamicBuffers GlobalDynamicBuffers)
{
// Needs to run before UpdateAllPrimitiveSceneInfos, as it may call OnVirtualTextureDestroyedCB, which modifies the uniform
// expression cache. UpdateAllPrimitiveSceneInfos generates mesh draw commands, which use the uniform expression cache.
FVirtualTextureSystem::Get().CallPendingCallbacks();
/**
* UpdateStaticMeshes removes and re-creates cached FMeshDrawCommands. If there are multiple scene renderers being run together,
* we need allocated pipeline state IDs not to change, in case async tasks related to prior scene renderers are still in flight
* (FSubmitNaniteMaterialPassCommandsAnyThreadTask or FDrawVisibleMeshCommandsAnyThreadTask). So we freeze pipeline state IDs,
* preventing them from being de-allocated even if their reference count temporarily goes to zero during calls to
* RemoveCachedMeshDrawCommands followed by CacheMeshDrawCommands (or the Nanite equivalent).
*
* Note that on the first scene renderer, we do want to de-allocate items, so they can be permanently released if no longer in use
* (for example, if there was an impactful change to a render proxy by game logic), but the assumption is that sequential renders
* of the same scene from different views can't make such changes.
*/
if (!bIsFirstSceneRenderer)
{
FGraphicsMinimalPipelineStateId::FreezeIdTable(true);
}
// 更新所以场景基源组件的异步操作
Scene->UpdateAllPrimitiveSceneInfos(GraphBuilder, GetUpdateAllPrimitiveSceneInfosAsyncOps());
// 。。。
}
inline EUpdateAllPrimitiveSceneInfosAsyncOps GetUpdateAllPrimitiveSceneInfosAsyncOps()
{
EUpdateAllPrimitiveSceneInfosAsyncOps AsyncOps = EUpdateAllPrimitiveSceneInfosAsyncOps::None;
if (GAsyncCreateLightPrimitiveInteractions > 0)
{
AsyncOps |= EUpdateAllPrimitiveSceneInfosAsyncOps::CreateLightPrimitiveInteractions;
}
if (GAsyncCacheMeshDrawCommands > 0)
{
AsyncOps |= EUpdateAllPrimitiveSceneInfosAsyncOps::CacheMeshDrawCommands;
}
return AsyncOps;
}
enum class EUpdateAllPrimitiveSceneInfosAsyncOps
{
None = 0,
// Cached mesh draw commands are cached asynchronously. 缓存mesh更新命令
CacheMeshDrawCommands = 1 << 0,
// Light primitive interactions are created asynchronously. 创建灯光基元交互
CreateLightPrimitiveInteractions = 1 << 1,
All = CacheMeshDrawCommands | CreateLightPrimitiveInteractions
};
然后调用 UpdateAllPrimitiveSceneInfos
Scene->UpdateAllPrimitiveSceneInfos(GraphBuilder, GetUpdateAllPrimitiveSceneInfosAsyncOps());
这个如何更新内容很多,后文细说
回来
IVisibilityTaskData* FSceneRenderer::UpdateScene(FRDGBuilder& GraphBuilder, FGlobalDynamicBuffers GlobalDynamicBuffers)
{
// 。。。
if (!bIsFirstSceneRenderer)
{
FGraphicsMinimalPipelineStateId::FreezeIdTable(false);
}
// 设置最终要绘制的矩形框
PrepareViewRectsForRendering(GraphBuilder.RHICmdList);
InitializeSceneTexturesConfig(ViewFamily.SceneTexturesConfig, ViewFamily);
FSceneTexturesConfig& SceneTexturesConfig = GetActiveSceneTexturesConfig();
FSceneTexturesConfig::Set(SceneTexturesConfig);
PrepareViewStateForVisibility(SceneTexturesConfig);
return LaunchVisibilityTasks(GraphBuilder.RHICmdList, *this, GlobalDynamicBuffers);
}
FSceneRenderer::PrepareViewStateForVisibility
发布与未发布情况下对于时间冻结的操作
void FSceneRenderer::PrepareViewStateForVisibility(const FSceneTexturesConfig& SceneTexturesConfig)
{
#if UE_BUILD_SHIPPING
const bool bFreezeTemporalHistories = false;
const bool bFreezeTemporalSequences = false;
#else
bool bFreezeTemporalHistories = CVarFreezeTemporalHistories.GetValueOnRenderThread() != 0;
static int32 CurrentFreezeTemporalHistoriesProgress = 0;
if (CurrentFreezeTemporalHistoriesProgress != CVarFreezeTemporalHistoriesProgress.GetValueOnRenderThread())
{
bFreezeTemporalHistories = false;
CurrentFreezeTemporalHistoriesProgress = CVarFreezeTemporalHistoriesProgress.GetValueOnRenderThread();
}
bool bFreezeTemporalSequences = bFreezeTemporalHistories || CVarFreezeTemporalSequences.GetValueOnRenderThread() != 0;
#endif
路径追踪失效计时器的一个值
// Load this field once so it has a consistent value for all views (and to avoid the atomic load in the loop).
// While the value may not be perfectly in sync when we render other view families, this is ok as this
// invalidation mechanism is only used for interactive rendering where we expect to be constantly drawing the scene.
// Therefore it is acceptable for some view families to be a frame or so behind others.
uint32 CurrentPathTracingInvalidationCounter = Scene->PathTracingInvalidationCounter.Load();
然后开始遍历所有视图,遍历完函数就结束了
这里面主要是一些【运动模糊相关的】
// Setup motion blur parameters (also check for camera movement thresholds)
for(int32 ViewIndex = 0;ViewIndex < Views.Num();ViewIndex++)
{
FViewInfo& View = Views[ViewIndex];
FSceneViewState* ViewState = View.ViewState;
check(View.VerifyMembersChecks());
// Setup global dither fade in and fade out uniform buffers.
// 全局抖动淡入淡出 的 uniform buffers
{
FDitherUniformShaderParameters DitherUniformShaderParameters;
DitherUniformShaderParameters.LODFactor = View.GetTemporalLODTransition();
View.DitherFadeOutUniformBuffer = FDitherUniformBufferRef::CreateUniformBufferImmediate(DitherUniformShaderParameters, UniformBuffer_SingleFrame);
DitherUniformShaderParameters.LODFactor = View.GetTemporalLODTransition() - 1.0f;
View.DitherFadeInUniformBuffer = FDitherUniformBufferRef::CreateUniformBufferImmediate(DitherUniformShaderParameters, UniformBuffer_SingleFrame);
}
// Once per render increment the occlusion frame counter.
if (ViewState)
{
ViewState->OcclusionFrameCounter++;
}
// HighResScreenshot should get best results so we don't do the occlusion optimization based on the former frame
// 比如截图的时候,和这些情况下,要最好的结果,就需要设置true,会关闭运动模糊
extern bool GIsHighResScreenshot;
const bool bIsHitTesting = ViewFamily.EngineShowFlags.HitProxies;
// Don't test occlusion queries in collision viewmode as they can be bigger then the rendering bounds.
const bool bCollisionView = ViewFamily.EngineShowFlags.CollisionVisibility || ViewFamily.EngineShowFlags.CollisionPawn;
if (GIsHighResScreenshot || !DoOcclusionQueries() || bIsHitTesting || bCollisionView || ViewFamily.EngineShowFlags.DisableOcclusionQueries)
{
View.bDisableQuerySubmissions = true;
View.bIgnoreExistingQueries = true;
}
// set up the screen area for occlusion
// 设置屏幕运动模糊的范围
{
float OcclusionPixelMultiplier = 1.0f;
if (UseDownsampledOcclusionQueries())
{
OcclusionPixelMultiplier = 1.0f / static_cast<float>(FMath::Square(SceneTexturesConfig.SmallDepthDownsampleFactor)); // 1 / 根号系数
}
float NumPossiblePixels = static_cast<float>(View.ViewRect.Width() * View.ViewRect.Height()) * OcclusionPixelMultiplier;
View.OneOverNumPossiblePixels = NumPossiblePixels > 0.0 ? 1.0f / NumPossiblePixels : 0.0f;
}
// Still need no jitter to be set for temporal feedback on SSR (it is enabled even when temporal AA is off).
// 检查
check(View.TemporalJitterPixels.X == 0.0f);
check(View.TemporalJitterPixels.Y == 0.0f);
// Cache the projection matrix b
// Cache the projection matrix before AA is applied
// 在应用抗锯齿前先缓存一下投影矩阵
View.ViewMatrices.SaveProjectionNoAAMatrix();
if (ViewState)
{
check(View.bStatePrevViewInfoIsReadOnly);
View.bStatePrevViewInfoIsReadOnly = ViewFamily.bWorldIsPaused || ViewFamily.EngineShowFlags.HitProxies || bFreezeTemporalHistories;
ViewState->SetupDistanceFieldTemporalOffset(ViewFamily); // 设置距离场的时间偏移
if (!View.bStatePrevViewInfoIsReadOnly && !bFreezeTemporalSequences)
{
ViewState->FrameIndex++;
}
if (View.OverrideFrameIndexValue.IsSet())
{
ViewState->FrameIndex = View.OverrideFrameIndexValue.GetValue();
}
}
// Subpixel jitter for temporal AA。这个就是TAA抗锯齿,通过上一帧和这一帧的不同,试图来减少一些锯齿
int32 CVarTemporalAASamplesValue = CVarTemporalAASamples.GetValueOnRenderThread();
EMainTAAPassConfig TAAConfig = GetMainTAAPassConfig(View);
bool bTemporalUpsampling = View.PrimaryScreenPercentageMethod == EPrimaryScreenPercentageMethod::TemporalUpscale;
// Apply a sub pixel offset to the view.
// 对视图应用子像素偏移
// IsTemporalAccumulationBasedMethod 是否是 TAA 和 TSR
if (IsTemporalAccumulationBasedMethod(View.AntiAliasingMethod) && ViewState && (CVarTemporalAASamplesValue > 0 || bTemporalUpsampling) && View.bAllowTemporalJitter)
{
// 一般都是1,如果上下帧
float EffectivePrimaryResolutionFraction = float(View.ViewRect.Width()) / float(View.GetSecondaryViewRectSize().X);
}
上一段有点长了,断一下
void FSceneRenderer::PrepareViewStateForVisibility(const FSceneTexturesConfig& SceneTexturesConfig)
{
// 。。。
// Setup motion blur parameters (also check for camera movement thresholds)
for(int32 ViewIndex = 0;ViewIndex < Views.Num();ViewIndex++)
{
// 。。。
if (IsTemporalAccumulationBasedMethod(View.AntiAliasingMethod) && ViewState && (CVarTemporalAASamplesValue > 0 || bTemporalUpsampling) && View.bAllowTemporalJitter)
{
// 一般都是1,如果上下帧视图缩小了就会不是
float EffectivePrimaryResolutionFraction = float(View.ViewRect.Width()) / float(View.GetSecondaryViewRectSize().X);
// Compute number of TAA samples. 时间累计的抗锯齿的数量【TemporalAASamples】和算法细节有关了
int32 TemporalAASamples;
{
// 设置这个值,根据算法 TemporalAASamples
}
// Compute the new sample index in the temporal sequence.
int32 TemporalSampleIndex = ViewState->TemporalAASampleIndex + 1;
if (TemporalSampleIndex >= TemporalAASamples || View.bCameraCut)
{
TemporalSampleIndex = 0;
}
#if !UE_BUILD_SHIPPING
if (CVarTAADebugOverrideTemporalIndex.GetValueOnRenderThread() >= 0)
{
TemporalSampleIndex = CVarTAADebugOverrideTemporalIndex.GetValueOnRenderThread();
}
#endif
// Updates view state.
// 更新采样的INDEX
if (!View.bStatePrevViewInfoIsReadOnly && !bFreezeTemporalSequences)
{
ViewState->TemporalAASampleIndex = TemporalSampleIndex;
}
// Choose sub pixel sample coordinate in the temporal sequence.
// 目的是计算 SampleX SampleY 采用的像素 XY坐标
float SampleX, SampleY;
if (View.PrimaryScreenPercentageMethod == EPrimaryScreenPercentageMethod::TemporalUpscale)
{
// Uniformly distribute temporal jittering in [-.5; .5], because there is no longer any alignement of input and output pixels.
// 在 [-.5; .5] 中均匀分布的时间抖动,因为输入和输出像素不再对齐。
SampleX = Halton(TemporalSampleIndex + 1, 2) - 0.5f;
SampleY = Halton(TemporalSampleIndex + 1, 3) - 0.5f;
View.MaterialTextureMipBias = -(FMath::Max(-FMath::Log2(EffectivePrimaryResolutionFraction), 0.0f) ) + CVarMinAutomaticViewMipBiasOffset.GetValueOnRenderThread();
View.MaterialTextureMipBias = FMath::Max(View.MaterialTextureMipBias, CVarMinAutomaticViewMipBias.GetValueOnRenderThread());
}
else if( CVarTemporalAASamplesValue == 2 )
{
}
else if( CVarTemporalAASamplesValue == 3 )
{
}
else if( CVarTemporalAASamplesValue == 4 )
{
}
else if( CVarTemporalAASamplesValue == 5 )
{
}
else
{
}
//
View.TemporalJitterSequenceLength = TemporalAASamples;
View.TemporalJitterIndex = TemporalSampleIndex;
View.TemporalJitterPixels.X = SampleX;
View.TemporalJitterPixels.Y = SampleY;
View.ViewMatrices.HackAddTemporalAAProjectionJitter(FVector2D(SampleX * 2.0f / View.ViewRect.Width(), SampleY * -2.0f / View.ViewRect.Height()));
}
// 。。。
}
}
void FSceneRenderer::PrepareViewStateForVisibility(const FSceneTexturesConfig& SceneTexturesConfig)
{
// 。。。
// Setup motion blur parameters (also check for camera movement thresholds)
for(int32 ViewIndex = 0;ViewIndex < Views.Num();ViewIndex++)
{
// 。。。
// Setup a new FPreviousViewInfo from current frame infos.
// 缓存上一帧的视图信息
FPreviousViewInfo NewPrevViewInfo;
{
NewPrevViewInfo.ViewRect = View.ViewRect;
NewPrevViewInfo.ViewMatrices = View.ViewMatrices;
NewPrevViewInfo.ViewRect = View.ViewRect;
}
if ( ViewState )
{
// 。。。
}
else
{
// Without a viewstate, we just assume that camera has not moved.
View.PrevViewInfo = NewPrevViewInfo;
}
}
}
if ( ViewState ) 里面的内容
// update previous frame matrices in case world origin was rebased on this frame
// 如果 world origin 在此帧上重新建立,则更新前一帧矩阵
if (!View.OriginOffsetThisFrame.IsZero())
{
ViewState->PrevFrameViewInfo.ViewMatrices.ApplyWorldOffset(View.OriginOffsetThisFrame);
}
// determine if we are initializing or we should reset the persistent state
// 确定我们是否正在初始化或者是否应该重置持久状态
// 准备一些属性
const float DeltaTime = View.Family->Time.GetRealTimeSeconds() - ViewState->LastRenderTime;
const bool bFirstFrameOrTimeWasReset = DeltaTime < -0.0001f || ViewState->LastRenderTime < 0.0001f; // 第一帧数,或者时间被重置了
const bool bIsLargeCameraMovement = IsLargeCameraMovement(
View,
ViewState->PrevFrameViewInfo.ViewMatrices.GetViewMatrix(),
ViewState->PrevFrameViewInfo.ViewMatrices.GetViewOrigin(),
75.0f, GCameraCutTranslationThreshold); // 用view matrix 判断相机是否经过了大幅运动
const bool bResetCamera = (bFirstFrameOrTimeWasReset || View.bCameraCut || bIsLargeCameraMovement || View.bForceCameraVisibilityReset);
// 光线追踪
#if RHI_RAYTRACING
static const auto CVarTemporalDenoiser = IConsoleManager::Get().FindTConsoleVariableDataInt(TEXT("r.PathTracing.TemporalDenoiser.mode"));
const int TemporalDenoiserMode = CVarTemporalDenoiser ? CVarTemporalDenoiser->GetValueOnAnyThread() : 0;
// 下面一小段 if else 主要是处理是否离线渲染下的 PathTracingInvalidate 路径追踪的属性重置
if (View.bIsOfflineRender) // 离线渲染
{
// In the offline context, we want precise control over when to restart the path tracer's accumulation to allow for motion blur
// So we use the camera cut signal only. In particular - we should not use bForceCameraVisibilityReset since this has
// interactions with the motion blur post process effect in tiled rendering (see comment below).
if (View.bCameraCut || View.bForcePathTracerReset) // 相机切换 || 重置了路径追踪
{
const bool bClearTemporalDenoisingHistory = (TemporalDenoiserMode == 1) ? View.bCameraCut : true;
ViewState->PathTracingInvalidate(bClearTemporalDenoisingHistory); // 重置一些属性,点进去看下
}
}
else
{
// for interactive usage - any movement or scene change should restart the path tracer
// Note: 0.18 deg is the minimum angle for avoiding numerical precision issue (which would cause constant invalidation)
const bool bIsCameraMove = IsLargeCameraMovement(
View,
ViewState->PrevFrameViewInfo.ViewMatrices.GetViewMatrix(),
ViewState->PrevFrameViewInfo.ViewMatrices.GetViewOrigin(),
0.18f /*degree*/, 0.1f /*cm*/);
const bool bIsProjMatrixDifferent = View.ViewMatrices.GetProjectionNoAAMatrix() != View.ViewState->PrevFrameViewInfo.ViewMatrices.GetProjectionNoAAMatrix();
// For each view, we remember what the invalidation counter was set to last time we were here so we can catch all changes
const bool bNeedsInvalidation = ViewState->PathTracingInvalidationCounter != CurrentPathTracingInvalidationCounter;
ViewState->PathTracingInvalidationCounter = CurrentPathTracingInvalidationCounter;
if (bNeedsInvalidation ||
bIsProjMatrixDifferent ||
bIsCameraMove ||
View.bCameraCut ||
View.bForceCameraVisibilityReset ||
View.bForcePathTracerReset)
{
const bool bClearTemporalDenoisingHistory = (TemporalDenoiserMode == 2) ? View.bCameraCut : true;
ViewState->PathTracingInvalidate(bClearTemporalDenoisingHistory);
}
}
#endif // RHI_RAYTRACING
if (bResetCamera) // 重置摄像机?
{
View.PrevViewInfo = NewPrevViewInfo;
// PT: If the motion blur shader is the last shader in the post-processing chain then it is the one that is
// adjusting for the viewport offset. So it is always required and we can't just disable the work the
// shader does. The correct fix would be to disable the effect when we don't need it and to properly mark
// the uber-postprocessing effect as the last effect in the chain.
View.bPrevTransformsReset = true;
}
else
{
View.PrevViewInfo = ViewState->PrevFrameViewInfo;
}
// Replace previous view info of the view state with this frame, clearing out references over render target.
if (!View.bStatePrevViewInfoIsReadOnly)
{
ViewState->PrevFrameViewInfo = NewPrevViewInfo;
}
// If the view has a previous view transform, then overwrite the previous view info for the _current_ frame.
if (View.PreviousViewTransform.IsSet())
{
// Note that we must ensure this transform ends up in ViewState->PrevFrameViewInfo else it will be used to calculate the next frame's motion vectors as well
// 更新view matrix
View.PrevViewInfo.ViewMatrices.UpdateViewMatrix(View.PreviousViewTransform->GetTranslation(), View.PreviousViewTransform->GetRotation().Rotator());
}
// detect conditions where we should reset occlusion(遮挡) queries
// 是否需要重置遮挡查询
if (bFirstFrameOrTimeWasReset ||
ViewState->LastRenderTime + GEngine->PrimitiveProbablyVisibleTime < View.Family->Time.GetRealTimeSeconds() ||
View.bCameraCut ||
View.bForceCameraVisibilityReset ||
IsLargeCameraMovement(
View,
FMatrix(ViewState->PrevViewMatrixForOcclusionQuery),
ViewState->PrevViewOriginForOcclusionQuery,
GEngine->CameraRotationThreshold, GEngine->CameraTranslationThreshold))
{
View.bIgnoreExistingQueries = true;
View.bDisableDistanceBasedFadeTransitions = true;
}
// Turn on/off round-robin occlusion querying in the ViewState
// 动态遮挡的查询方法 (被挡住的东西不提交)
static const auto CVarRROCC = IConsoleManager::Get().FindTConsoleVariableDataInt(TEXT("vr.RoundRobinOcclusion"));
const bool bEnableRoundRobin = CVarRROCC ? (CVarRROCC->GetValueOnAnyThread() != false) : false;
if (bEnableRoundRobin != ViewState->IsRoundRobinEnabled())
{
ViewState->UpdateRoundRobin(bEnableRoundRobin);
View.bIgnoreExistingQueries = true;
}
ViewState->PrevViewMatrixForOcclusionQuery = FMatrix44f(View.ViewMatrices.GetViewMatrix()); // LWC_TODO: Precision loss
ViewState->PrevViewOriginForOcclusionQuery = View.ViewMatrices.GetViewOrigin();
// store old view matrix and detect conditions where we should reset motion blur
// 根据 旧的view matrix 决定 是否需要重置动态模糊
#if RHI_RAYTRACING
{
if (bResetCamera || IsLargeCameraMovement(View, ViewState->PrevFrameViewInfo.ViewMatrices.GetViewMatrix(), ViewState->PrevFrameViewInfo.ViewMatrices.GetViewOrigin(), 0.1f, 0.1f))
{
ViewState->RayTracingNumIterations = 1; // 但是这里改的是光线追踪的迭代次数 emmm
}
else
{
ViewState->RayTracingNumIterations++;
}
}
#endif // RHI_RAYTRACING
// we don't use DeltaTime as it can be 0 (in editor) and is computed by subtracting floats (loses precision over time)
// Clamp DeltaWorldTime to reasonable values for the purposes of motion blur, things like TimeDilation can make it very small
// Offline renders always control the timestep for the view and always need the timescales calculated.
if (!ViewFamily.bWorldIsPaused || View.bIsOfflineRender)
{
ViewState->UpdateMotionBlurTimeScale(View);
}
ViewState->PrevFrameNumber = ViewState->PendingPrevFrameNumber;
ViewState->PendingPrevFrameNumber = View.Family->FrameNumber;
// This finishes the update of view state
ViewState->UpdateLastRenderTime(*View.Family);
ViewState->UpdateTemporalLODTransition(View);
到这里
if ( ViewState ) 里面的内容 结束了
FDeferredShadingSceneRenderer::Render
这里是updatescene 结束,回到render这一层
void FDeferredShadingSceneRenderer::Render(FRDGBuilder& GraphBuilder)
{
// 。。。
FInitViewTaskDatas InitViewTaskDatas = UpdateScene(GraphBuilder, FGlobalDynamicBuffers(DynamicIndexBufferForInitViews, DynamicVertexBufferForInitViews, DynamicReadBufferForInitViews));
// GPU场景作用域的helper,会在构造和析构的时候调用 GPUScene 的 BeginRender 和 EndRender ()
FGPUSceneScopeBeginEndHelper GPUSceneScopeBeginEndHelper(Scene->GPUScene, GPUSceneDynamicContext, Scene);
}
const bool bUseVirtualTexturing = UseVirtualTexturing(FeatureLevel);
if (bUseVirtualTexturing && RendererOutput == ERendererOutput::FinalSceneColor)
{
FVirtualTextureUpdateSettings Settings;
Settings.EnableThrottling(!ViewFamily.bOverrideVirtualTextureThrottle);
VirtualTextureUpdater = FVirtualTextureSystem::Get().BeginUpdate(GraphBuilder, FeatureLevel, Scene, Settings);
VirtualTextureFeedbackBegin(GraphBuilder, Views, GetActiveSceneTexturesConfig().Extent);
}
// Compute & commit the final state of the entire dependency topology of the renderer.
// 提交渲染器的拓扑状态
CommitFinalPipelineState();
// Initialize global system textures (pass-through if already initialized).
GSystemTextures.InitializeTextures(GraphBuilder.RHICmdList, FeatureLevel);
{
if (RendererOutput == ERendererOutput::FinalSceneColor)
{
InitViewTaskDatas.LumenFrameTemporaries = &LumenFrameTemporaries;
// Important that this uses consistent logic throughout the frame, so evaluate once and pass in the flag from here
// NOTE: Must be done after system texture initialization
// TODO: This doesn't take into account the potential for split screen views with separate shadow caches
const bool bEnableVirtualShadowMaps = UseVirtualShadowMaps(ShaderPlatform, FeatureLevel) && ViewFamily.EngineShowFlags.DynamicShadows;
VirtualShadowMapArray.Initialize(GraphBuilder, Scene->GetVirtualShadowMapCache(Views[0]), bEnableVirtualShadowMaps, Views[0].bIsSceneCapture);
if (InitViewTaskDatas.LumenFrameTemporaries)
{
BeginUpdateLumenSceneTasks(GraphBuilder, *InitViewTaskDatas.LumenFrameTemporaries);
}
BeginGatherLumenLights(InitViewTaskDatas.LumenDirectLighting, InitViewTaskDatas.VisibilityTaskData);
}
if (bNaniteEnabled)
{
TArray<FConvexVolume, TInlineAllocator<2>> NaniteCullingViews;
// For now we'll share the same visibility results across all views
for (int32 ViewIndex = 0; ViewIndex < Views.Num(); ViewIndex++)
{
FViewInfo& View = Views[ViewIndex];
NaniteCullingViews.Add(View.ViewFrustum); // 如果开启了nanite,就会遍历view,把视锥添加到nanite的裁剪view里面
}
FNaniteVisibility& NaniteVisibility = Scene->NaniteVisibility[ENaniteMeshPass::BasePass];
const FNaniteMaterialCommands& NaniteMaterials = Scene->NaniteMaterials[ENaniteMeshPass::BasePass];
const FNaniteRasterPipelines& NaniteRasterPipeline = Scene->NaniteRasterPipelines[ENaniteMeshPass::BasePass];
NaniteVisibility.BeginVisibilityFrame();
NaniteBasePassVisibility.Visibility = &NaniteVisibility;
NaniteBasePassVisibility.Query = NaniteVisibility.BeginVisibilityQuery( // 可见性查询
*Scene,
NaniteCullingViews,
&NaniteRasterPipeline,
&NaniteMaterials
);
}
}
上面一段有几个函数进去看看
struct FPerViewPipelineState
{
EDiffuseIndirectMethod DiffuseIndirectMethod; // 漫反射间接方法 包括禁用,SSGI(屏幕空间的全局光照),RTGI(实时全局光照),lumen,插件自定义
IScreenSpaceDenoiser::EMode DiffuseIndirectDenoiser; // 漫反射计算的去噪方法
// Method to use for ambient occlusion.
EAmbientOcclusionMethod AmbientOcclusionMethod; // 环境遮罩的去噪方式,就是我们常说的 AO(Ambient Occlusion环境光遮蔽)这个枚举里面就有SSAO
// Method to use for reflections.
EReflectionsMethod ReflectionsMethod;
// Method to use for reflections on water.
EReflectionsMethod ReflectionsMethodWater;
// Whether there is planar reflection to compose to the reflection. 是否要把平面的反射,合成到反射中
bool bComposePlanarReflections;
// Whether need to generate HZB from the depth buffer. 是否用最远的深度缓冲来生成HZB
bool bFurthestHZB;
bool bClosestHZB;
};
void FDeferredShadingSceneRenderer::CommitFinalPipelineState()
{
// Family pipeline state
// FFamilyPipelineState 有 Nanite 和 HZB 两种剔除方式,看枚举
{
FamilyPipelineState.Set(&FFamilyPipelineState::bNanite, UseNanite(ShaderPlatform)); // TODO: Should this respect ViewFamily.EngineShowFlags.NaniteMeshes?
// sajdlkasjdljlksj
static const auto ICVarHZBOcc = IConsoleManager::Get().FindConsoleVariable(TEXT("r.HZBOcclusion"));
FamilyPipelineState.Set(&FFamilyPipelineState::bHZBOcclusion, ICVarHZBOcc->GetInt() != 0);
}
// 遍历view,
// 获取 TPipelineState<FPerViewPipelineState>& ViewPipelineState = GetViewPipelineStateWritable(View);
// 然后设置 FPerViewPipelineState 的属性,属性和含义参考上面结构体说明
CommitIndirectLightingState();
// Views pipeline states
// 遍历view、
for (int32 ViewIndex = 0; ViewIndex < Views.Num(); ViewIndex++)
{
const FViewInfo& View = Views[ViewIndex];
// 获取 FPerViewPipelineState 和 上面 CommitIndirectLightingState 类似
TPipelineState< FPerViewPipelineState>& ViewPipelineState = GetViewPipelineStateWritable(View);
// Commit HZB state
{
const bool bHasSSGI = ViewPipelineState[&FPerViewPipelineState::DiffuseIndirectMethod] == EDiffuseIndirectMethod::SSGI;
const bool bUseLumen = ViewPipelineState[&FPerViewPipelineState::DiffuseIndirectMethod] == EDiffuseIndirectMethod::Lumen
|| ViewPipelineState[&FPerViewPipelineState::ReflectionsMethod] == EReflectionsMethod::Lumen;
// Requires FurthestHZB
ViewPipelineState.Set(&FPerViewPipelineState::bFurthestHZB,
FamilyPipelineState[&FFamilyPipelineState::bHZBOcclusion] ||
FamilyPipelineState[&FFamilyPipelineState::bNanite] ||
ViewPipelineState[&FPerViewPipelineState::AmbientOcclusionMethod] == EAmbientOcclusionMethod::SSAO ||
ViewPipelineState[&FPerViewPipelineState::ReflectionsMethod] == EReflectionsMethod::SSR ||
bHasSSGI || bUseLumen);
ViewPipelineState.Set(&FPerViewPipelineState::bClosestHZB,
bHasSSGI || bUseLumen);
}
}
// Commit all the pipeline states.
// 提交管线状态
{
for (int32 ViewIndex = 0; ViewIndex < Views.Num(); ViewIndex++)
{
const FViewInfo& View = Views[ViewIndex];
GetViewPipelineStateWritable(View).Commit();
}
FamilyPipelineState.Commit();
}
}
FDeferredShadingSceneRenderer::Render
回到这一层
这个想打印点啥东西呀,还不知道。。。
void FDeferredShadingSceneRenderer::Render(FRDGBuilder& GraphBuilder)
{
// 。。。
ShaderPrint::BeginViews(GraphBuilder, Views);
ON_SCOPE_EXIT
{
ShaderPrint::EndViews(Views);
};
enum class ERendererOutput
{
DepthPrepassOnly, // Only render depth prepass and its related code paths 仅渲染深度预处理及其相关的代码路径
FinalSceneColor // Render the whole pipeline 整个管道
};
if (RendererOutput == ERendererOutput::FinalSceneColor) // 如果渲染整个pipeline
{
// By Copilot
// 用于准备场景中的距离场信息。距离场是一种数据结构,用于存储每个点到最近表面的距离。在图形渲染中,距离场常常用于实现各种效果,如软阴影、环境光遮蔽(AO)和间接光照等
// 具体的实现细节可能会因项目和使用的图形引擎不同而有所不同,但大致的工作流程可能包括以下步骤:
// - 遍历场景中的所有物体,计算每个物体的距离场。
// - 将计算出的距离场数据存储到相应的数据结构中,如3D纹理或者其他类型的缓冲区。
// - 将这些数据提交给渲染管线,以便在后续的渲染步骤中使用。
PrepareDistanceFieldScene(GraphBuilder, ExternalAccessQueue);
for (int32 ViewIndex = 0; ViewIndex < Views.Num(); ViewIndex++)
{
FViewInfo& View = Views[ViewIndex];
RDG_GPU_MASK_SCOPE(GraphBuilder, View.GPUMask);
// shading 能量守恒的初始化操作,反正就是准备参数
ShadingEnergyConservation::Init(GraphBuilder, View);
// FGlintShadingLUTsStateData 是 存储Glint Shading的查找表(LUTs)的状态数据。Glint Shading是一种渲染技术,用于模拟微小表面细节(如头发、布料等)的光泽效果
FGlintShadingLUTsStateData::Init(GraphBuilder, View);
}
// kick off dependent scene updates
// 更新每一帧渲染的阴影场景中的活动灯光(等待场景的更新状态完成,更新灯光的激活状态)
Scene->ShadowScene->UpdateForRenderedFrame(GraphBuilder);
#if RHI_RAYTRACING
// Initialize ray tracing flags, in case they weren't initialized in the CreateSceneRenderers code path 、
// 更新光线追踪的标志
InitializeRayTracingFlags_RenderThread();
// 光线追踪的几何管理器
RayTracingGeometryManager.Tick(bHasRayTracingEnableChanged);
// 是否清空光线追踪集合收集器
if ((GetRayTracingMode() == ERayTracingMode::Dynamic) && bHasRayTracingEnableChanged)
{
Scene->GetRayTracingDynamicGeometryCollection()->Clear();
}
// Now that we have updated all the PrimitiveSceneInfos, update the RayTracing mesh commands cache if needed
// 更新所有的基源组件,如果需要,更新光线追踪mesh的命令缓存
{
// 光线追踪的网格命令模式 ERayTracingMeshCommandsMode 光线追踪,路径追踪,光照图追踪
const ERayTracingMeshCommandsMode CurrentMode = ViewFamily.EngineShowFlags.PathTracing ? ERayTracingMeshCommandsMode::PATH_TRACING : ERayTracingMeshCommandsMode::RAY_TRACING;
bool bNaniteCoarseMeshStreamingModeChanged = false; // nanite 网格流模式是否发生了变化
#if WITH_EDITOR
bNaniteCoarseMeshStreamingModeChanged = Nanite::FCoarseMeshStreamingManager::CheckStreamingMode();
#endif // WITH_EDITOR
const bool bNaniteRayTracingModeChanged = Nanite::GRayTracingManager.CheckModeChanged(); // nanite 光线追踪模式是否发生了变化
// 需要修改光线追踪的命令模式,命令缓存?
if (CurrentMode != Scene->CachedRayTracingMeshCommandsMode || bNaniteCoarseMeshStreamingModeChanged || bNaniteRayTracingModeChanged || bHasRayTracingEnableChanged)
{
// 等待mesh绘制命令完成
Scene->WaitForCacheRayTracingPrimitivesTask();
// In some situations, we need to refresh the cached ray tracing mesh commands because they contain data about the currently bound shader.
// This operation is a bit expensive but only happens once as we transition between modes which should be rare.
// 刷新光线追踪的 mesh命令模式,刷新mesh命令缓存
Scene->CachedRayTracingMeshCommandsMode = CurrentMode;
Scene->RefreshRayTracingMeshCommandCache();
bHasRayTracingEnableChanged = false;
}
// 是否要刷新光线追踪的实例
if (bRefreshRayTracingInstances)
{
Scene->WaitForCacheRayTracingPrimitivesTask();
// In some situations, we need to refresh the cached ray tracing instance.
// eg: Need to update PrimitiveRayTracingFlags
// This operation is a bit expensive but only happens once as we transition between modes which should be rare.
Scene->RefreshRayTracingInstances();
bRefreshRayTracingInstances = false;
}
// nanite光线追踪模式发生了变化
if (bNaniteRayTracingModeChanged)
{
for (FViewInfo& View : Views)
{
if (View.ViewState != nullptr && !View.bIsOfflineRender)
{
// don't invalidate in the offline case because we only get one attempt at rendering each sample
View.ViewState->PathTracingInvalidate();
}
}
}
// 有任何光线追踪的pass被激活了
if (bAnyRayTracingPassEnabled)
{
const int32 ReferenceViewIndex = 0;
FViewInfo& ReferenceView = Views[ReferenceViewIndex];
FGraphEventRef PrereqTask = CreateCompatibilityGraphEvent(MakeArrayView({ Scene->GetCacheRayTracingPrimitivesTask(), InitViewTaskDatas.VisibilityTaskData->GetFrustumCullTask() }));
InitViewTaskDatas.RayTracingRelevantPrimitives = Allocator.Create<FRayTracingRelevantPrimitiveTaskData>();
InitViewTaskDatas.RayTracingRelevantPrimitives->Task = FFunctionGraphTask::CreateAndDispatchWhenReady(
[Scene = Scene, &ReferenceView, &RayTracingRelevantPrimitiveList = InitViewTaskDatas.RayTracingRelevantPrimitives->List]()
{
FTaskTagScope TaskTagScope(ETaskTag::EParallelRenderingThread);
GatherRayTracingRelevantPrimitives(*Scene, ReferenceView, RayTracingRelevantPrimitiveList);
}, TStatId(), PrereqTask, ENamedThreads::AnyNormalThreadHiPriTask);
}
}
Nanite 相关
UE::SVT::GetStreamingManager().BeginAsyncUpdate(GraphBuilder);
bool bUpdateNaniteStreaming = false;
bool bVisualizeNanite = false;
// nanite 更新
if (bNaniteEnabled)
{
Nanite::GGlobalResources.Update(GraphBuilder);
// Only update Nanite streaming residency for the first view when multiple view rendering (nDisplay) is enabled.
// Streaming requests are still accumulated from the remaining views.
bUpdateNaniteStreaming = !ViewFamily.bIsMultipleViewFamily || ViewFamily.bIsFirstViewInMultipleViewFamily;
if (bUpdateNaniteStreaming)
{
Nanite::GStreamingManager.BeginAsyncUpdate(GraphBuilder); // 异步处理流的更新
}
FNaniteVisualizationData& NaniteVisualization = GetNaniteVisualizationData();
if (Views.Num() > 0)
{
const FName& NaniteViewMode = Views[0].CurrentNaniteVisualizationMode;
if (NaniteVisualization.Update(NaniteViewMode))
{
// When activating the view modes from the command line, automatically enable the VisualizeNanite show flag for convenience.
ViewFamily.EngineShowFlags.SetVisualizeNanite(true);
}
bVisualizeNanite = NaniteVisualization.IsActive() && ViewFamily.EngineShowFlags.VisualizeNanite;
}
}
是否要渲染天空和大气
if (RendererOutput == ERendererOutput::FinalSceneColor)
{
const bool bPathTracedAtmosphere = ViewFamily.EngineShowFlags.PathTracing && Views.Num() > 0 && Views[0].FinalPostProcessSettings.PathTracingEnableReferenceAtmosphere;
if (ShouldRenderSkyAtmosphere(Scene, ViewFamily.EngineShowFlags) && !bPathTracedAtmosphere)
{
for (int32 LightIndex = 0; LightIndex < NUM_ATMOSPHERE_LIGHTS; ++LightIndex)
{
if (Scene->AtmosphereLights[LightIndex])
{
PrepareSunLightProxy(*Scene->GetSkyAtmosphereSceneInfo(),LightIndex, *Scene->AtmosphereLights[LightIndex]);
}
}
}
else
{
Scene->ResetAtmosphereLightsProperties();
}
}
GPU掩码,掩码是啥(掩码可以用来控制哪些 GPU 用于执行特定的渲染任务)
#if WITH_MGPU
ComputeGPUMasks(&GraphBuilder.RHICmdList);
#endif // WITH_MGPU
纹理相关的更新,准备一些资源,还没有提交
// By default, limit our GPU usage to only GPUs specified in the view masks.
RDG_GPU_MASK_SCOPE(GraphBuilder, ViewFamily.EngineShowFlags.PathTracing ? FRHIGPUMask::All() : AllViewsGPUMask);
RDG_EVENT_SCOPE(GraphBuilder, "Scene");
RDG_GPU_STAT_SCOPE_VERBOSE(GraphBuilder, Unaccounted, *ViewFamily.ProfileDescription);
if (RendererOutput == ERendererOutput::FinalSceneColor)
{
SCOPE_CYCLE_COUNTER(STAT_FDeferredShadingSceneRenderer_Render_Init);
RDG_RHI_GPU_STAT_SCOPE(GraphBuilder, AllocateRendertargets);
// Force the subsurface profile texture to be updated.
UpdateSubsurfaceProfileTexture(GraphBuilder, ShaderPlatform);
SpecularProfileAtlas::UpdateSpecularProfileTextureAtlas(GraphBuilder, ShaderPlatform);
// Force the rect light texture & IES texture to be updated.
RectLightAtlas::UpdateAtlasTexture(GraphBuilder, FeatureLevel);
IESAtlas::UpdateAtlasTexture(GraphBuilder, FeatureLevel);
}
// 场景纹理配置,和RDG系统纹理创建
FSceneTexturesConfig& SceneTexturesConfig = GetActiveSceneTexturesConfig();
const FRDGSystemTextures& SystemTextures = FRDGSystemTextures::Create(GraphBuilder); // FRDGSystemTextures::Create
const bool bHasRayTracedOverlay = HasRayTracedOverlay(ViewFamily);
const bool bAllowStaticLighting = !bHasRayTracedOverlay && IsStaticLightingAllowed();
// if DDM_AllOpaqueNoVelocity was used, then velocity should have already been rendered as well
const bool bIsEarlyDepthComplete = (DepthPass.EarlyZPassMode == DDM_AllOpaque || DepthPass.EarlyZPassMode == DDM_AllOpaqueNoVelocity);
// Use read-only depth in the base pass if we have a full depth prepass.
const bool bAllowReadOnlyDepthBasePass = bIsEarlyDepthComplete
&& !ViewFamily.EngineShowFlags.ShaderComplexity
&& !ViewFamily.UseDebugViewPS()
&& !ViewFamily.EngineShowFlags.Wireframe
&& !ViewFamily.EngineShowFlags.LightMapDensity;
const FExclusiveDepthStencil::Type BasePassDepthStencilAccess =
bAllowReadOnlyDepthBasePass
? FExclusiveDepthStencil::DepthRead_StencilWrite
: FExclusiveDepthStencil::DepthWrite_StencilWrite;
// Find the visible primitives.
if (GDynamicRHI->RHIIncludeOptionalFlushes())
{
GraphBuilder.RHICmdList.ImmediateFlush(EImmediateFlushType::DispatchToRHIThread);
}
FInstanceCullingManager& InstanceCullingManager = *GraphBuilder.AllocObject<FInstanceCullingManager>(GetSceneUniforms(), Scene->GPUScene.IsEnabled(), GraphBuilder);
FDeferredShadingSceneRenderer::BeginInitViews
{
RDG_GPU_STAT_SCOPE(GraphBuilder, VisibilityCommands);
BeginInitViews(GraphBuilder, SceneTexturesConfig, BasePassDepthStencilAccess, InstanceCullingManager, VirtualTextureUpdater.Get(), InitViewTaskDatas);
}
/**
* Initialize scene's views.
* Check visibility, build visible mesh commands, etc.
*/
void FDeferredShadingSceneRenderer::BeginInitViews(
FRDGBuilder& GraphBuilder,
const FSceneTexturesConfig& SceneTexturesConfig,
FExclusiveDepthStencil::Type BasePassDepthStencilAccess,
FInstanceCullingManager& InstanceCullingManager,
FVirtualTextureUpdater* VirtualTextureUpdater,
FInitViewTaskDatas& TaskDatas)
{
SCOPED_NAMED_EVENT(FDeferredShadingSceneRenderer_InitViews, FColor::Emerald);
SCOPE_CYCLE_COUNTER(STAT_InitViewsTime);
RDG_CSV_STAT_EXCLUSIVE_SCOPE(GraphBuilder, InitViews_Scene);
PreVisibilityFrameSetup(GraphBuilder);
// 。。。
FSceneRenderer::PreVisibilityFrameSetup
/**
* Performs once per frame setup prior to visibility determination.
*/
void FDeferredShadingSceneRenderer::PreVisibilityFrameSetup(FRDGBuilder& GraphBuilder)
{
// Possible stencil dither optimization approach
for (int32 ViewIndex = 0; ViewIndex < Views.Num(); ViewIndex++)
{
FViewInfo& View = Views[ViewIndex];
View.bAllowStencilDither = DepthPass.bDitheredLODTransitionsUseStencil;
}
FSceneRenderer::PreVisibilityFrameSetup(GraphBuilder);
}
void FSceneRenderer::PreVisibilityFrameSetup(FRDGBuilder& GraphBuilder)
{
FRHICommandListImmediate& RHICmdList = GraphBuilder.RHICmdList;
// Notify the RHI we are beginning to render a scene.
// 通知是渲染场景,比如清空渲染目标
RHICmdList.BeginScene();
GetContext().RHIBeginScene(); // 这里开始跨平台调用
void FRHICommandListImmediate::BeginScene()
{
check(IsImmediate() && IsInRenderingThread());
if (Bypass())
{
GetContext().RHIBeginScene(); // 这里开始跨平台调用
return;
}
ALLOC_COMMAND(FRHICommandBeginScene)();
if (!IsRunningRHIInSeparateThread())
{
// if we aren't running an RHIThread, there is no good reason to buffer this frame advance stuff and that complicates state management, so flush everything out now
QUICK_SCOPE_CYCLE_COUNTER(BeginScene_Flush);
CSV_SCOPED_TIMING_STAT(RHITFlushes, BeginScene);
FRHICommandListExecutor::GetImmediateCommandList().ImmediateFlush(EImmediateFlushType::FlushRHIThread);
}
}
回来RHICmdList.BeginScene(); 继续下去
void FSceneRenderer::PreVisibilityFrameSetup(FRDGBuilder& GraphBuilder)
{
FRHICommandListImmediate& RHICmdList = GraphBuilder.RHICmdList;
// Notify the RHI we are beginning to render a scene.
// 通知是渲染场景,比如清空渲染目标
RHICmdList.BeginScene();
FSceneRenderer::PreVisibilityFrameSetup
回来
这里我们看完了 RHICmdList.BeginScene(); 然后跳回这一层
- 头发
-
粒子特效
void FSceneRenderer::PreVisibilityFrameSetup(FRDGBuilder& GraphBuilder) { FRHICommandListImmediate& RHICmdList = GraphBuilder.RHICmdList; // Notify the RHI we are beginning to render a scene. RHICmdList.BeginScene(); // 没有命中代理,且view > 0 处理头发相关的 if (Views.Num() > 0 && !ViewFamily.EngineShowFlags.HitProxies) { FHairStrandsBookmarkParameters Parameters; CreateHairStrandsBookmarkParameters(Scene, Views, AllFamilyViews, Parameters); if (Parameters.HasInstances()) { // 1. Select appropriate LOD & geometry type RunHairStrandsBookmark(GraphBuilder, EHairStrandsBookmark::ProcessLODSelection, Parameters); // 2. Run guide/simulation update // If we are rendering from scene capture we don't need to run another time the hair bookmarks. if (IsHairStrandsEnabled(EHairStrandsShaderType::All, Scene->GetShaderPlatform()) && Views[0].AllowGPUParticleUpdate()) { RunHairStrandsBookmark(GraphBuilder, EHairStrandsBookmark::ProcessGuideInterpolation, Parameters); } } } // Notify the FX system that the scene is about to perform visibility checks. // 特效相关的 if (FXSystem && Views.IsValidIndex(0)) { FXSystem->PreInitViews(GraphBuilder, Views[0].AllowGPUParticleUpdate() && !ViewFamily.EngineShowFlags.HitProxies, AllFamilies, &ViewFamily); }
编辑器下,给灯光画个线
```cpp
#if WITH_EDITOR
// Draw lines to lights affecting this mesh if its selected.
if (ViewFamily.EngineShowFlags.LightInfluences)
{
Scene->WaitForCreateLightPrimitiveInteractionsTask();
for (TConstSetBitIterator<> It(Scene->PrimitivesSelected); It; ++It)
{
const FPrimitiveSceneInfo* PrimitiveSceneInfo = Scene->Primitives[It.GetIndex()];
FLightPrimitiveInteraction *LightList = PrimitiveSceneInfo->LightList;
while (LightList) // 拿到灯
{
const FLightSceneInfo* LightSceneInfo = LightList->GetLight();
bool bDynamic = true;
bool bRelevant = false;
bool bLightMapped = true;
bool bShadowMapped = false;
PrimitiveSceneInfo->Proxy->GetLightRelevance(LightSceneInfo->Proxy, bDynamic, bRelevant, bLightMapped, bShadowMapped);
if (bRelevant)
{
// Draw blue for light-mapped lights and orange for dynamic lights
const FColor LineColor = bLightMapped ? FColor(0,140,255) : FColor(255,140,0);
for (int32 ViewIndex = 0;ViewIndex < Views.Num();ViewIndex++)
{
FViewInfo& View = Views[ViewIndex];
FViewElementPDI LightInfluencesPDI(&View,nullptr,&View.DynamicPrimitiveCollector);
LightInfluencesPDI.DrawLine(PrimitiveSceneInfo->Proxy->GetBounds().Origin, LightSceneInfo->Proxy->GetLightToWorld().GetOrigin(), LineColor, SDPG_World);
}
}
LightList = LightList->GetNextLight();
}
}
}
#endif
#if RHI_RAYTRACING
// 光线追踪的
if (Scene && Views.Num())
{
const int32 ReferenceViewIndex = 0;
const FViewInfo& ReferenceView = Views[ReferenceViewIndex];
Scene->RayTracingScene.InitPreViewTranslation(ReferenceView.ViewMatrices);
Scene->RayTracingScene.bNeedsDebugInstanceGPUSceneIndexBuffer = IsRayTracingInstanceOverlapEnabled(ReferenceView);
}
#endif
// 扩展
for (const auto& ViewExtension : ViewFamily.ViewExtensions)
{
ViewExtension->PreInitViews_RenderThread(GraphBuilder);
}
}
FSceneRenderer::PreVisibilityFrameSetup 函数结束
FDeferredShadingSceneRenderer::BeginInitViews
回到这里
上面我们讨论完了 PreVisibilityFrameSetup
然后继续
/**
* Initialize scene's views.
* Check visibility, build visible mesh commands, etc.
*/
void FDeferredShadingSceneRenderer::BeginInitViews(
FRDGBuilder& GraphBuilder,
const FSceneTexturesConfig& SceneTexturesConfig,
FExclusiveDepthStencil::Type BasePassDepthStencilAccess,
FInstanceCullingManager& InstanceCullingManager,
FVirtualTextureUpdater* VirtualTextureUpdater,
FInitViewTaskDatas& TaskDatas)
{
SCOPED_NAMED_EVENT(FDeferredShadingSceneRenderer_InitViews, FColor::Emerald);
SCOPE_CYCLE_COUNTER(STAT_InitViewsTime);
RDG_CSV_STAT_EXCLUSIVE_SCOPE(GraphBuilder, InitViews_Scene);
PreVisibilityFrameSetup(GraphBuilder);
// Attempt to launch dynamic shadow tasks early before finalizing visibility.
if (GetRendererOutput() == ERendererOutput::FinalSceneColor)
{
BeginInitDynamicShadows(TaskDatas);
}
FRHICommandListImmediate& RHICmdList = GraphBuilder.RHICmdList;
// Create GPU-side representation of the view for instance culling.
// 注册view视图,注册到 实例剔除管理器中
for (int32 ViewIndex = 0; ViewIndex < Views.Num(); ++ViewIndex)
{
Views[ViewIndex].GPUSceneViewId = InstanceCullingManager.RegisterView(Views[ViewIndex]); // FInstanceCullingManager
}
// 特效相关的初始化
// 在naigara初始化之前先初始化buff
{
// This is to init the ViewUniformBuffer before rendering for the Niagara compute shader.
// This needs to run before ComputeViewVisibility() is called, but the views normally initialize the ViewUniformBuffer after that (at the end of this method).
if (FXSystem && FXSystem->RequiresEarlyViewUniformBuffer() && Views.IsValidIndex(0))
{
// during ISR, instanced view RHI resources need to be initialized first.
if (FViewInfo* InstancedView = const_cast<FViewInfo*>(Views[0].GetInstancedView()))
{
InstancedView->InitRHIResources();
}
Views[0].InitRHIResources();
FXSystem->PostInitViews(GraphBuilder, GetSceneViews(), !ViewFamily.EngineShowFlags.HitProxies);
}
}
// lumen可视化绘制
LumenScenePDIVisualization();
// 呃呃呃呃呃呃呃呃呃呃,
TaskDatas.VisibilityTaskData->ProcessRenderThreadTasks(BasePassDepthStencilAccess, InstanceCullingManager, VirtualTextureUpdater);
// Make a second attempt to launch shadow tasks it wasn't able to the first time due to visibility being deferred.
// 再次尝试启动影子任务,由于可见性被延迟,第一次无法启动。
if (GetRendererOutput() == ERendererOutput::FinalSceneColor)
{
BeginInitDynamicShadows(TaskDatas);
}
// This must happen before we start initialising and using views.
if (Scene)
{
// 天空相关的,去更新GPU缓存
UpdateSkyIrradianceGpuBuffer(GraphBuilder, ViewFamily.EngineShowFlags, Scene->SkyLight, Scene->SkyIrradianceEnvironmentMap);
}
// Initialise Sky/View resources before the view global uniform buffer is built.
// 是否要渲染天空大气的效果
if (ShouldRenderSkyAtmosphere(Scene, ViewFamily.EngineShowFlags))
{
InitSkyAtmosphereForViews(RHICmdList);
}
// 前面有一个pre VisibilityFrameSetup 这里有一个post VisibilityFrameSetup
// 还是可见性的处理,后面展开
PostVisibilityFrameSetup(TaskDatas.ILCUpdatePrim);
{
QUICK_SCOPE_CYCLE_COUNTER(STAT_InitViews_InitRHIResources);
// initialize per-view uniform buffer. Do it from back to front because secondary stereo view follows its primary one, but primary needs to know the instanced's params
for (int32 ViewIndex = Views.Num() - 1; ViewIndex >= 0; --ViewIndex)
{
FViewInfo& View = Views[ViewIndex];
// Set the pre-exposure before initializing the constant buffers. (预曝光值)
View.UpdatePreExposure();
// Initialize the view's RHI resources.更新头发的资源,然后再 更新RHI资源
UpdateHairResources(GraphBuilder, View);
View.InitRHIResources();
}
}
// 渲染开始了
{
QUICK_SCOPE_CYCLE_COUNTER(STAT_InitViews_OnStartRender);
OnStartRender(RHICmdList); // 里面清空了一下 GVisualizeTexture 的属性
}
// 立即刷新现场
if (GDynamicRHI->RHIIncludeOptionalFlushes())
{
RHICmdList.ImmediateFlush(EImmediateFlushType::DispatchToRHIThread);
}
}
FDeferredShadingSceneRenderer::Render
回来到这里
BeginInitViews 结束了继续看下去
void FDeferredShadingSceneRenderer::Render(FRDGBuilder& GraphBuilder)
{
// ...
{
RDG_GPU_STAT_SCOPE(GraphBuilder, VisibilityCommands);
BeginInitViews(GraphBuilder, SceneTexturesConfig, BasePassDepthStencilAccess, InstanceCullingManager, VirtualTextureUpdater.Get(), InitViewTaskDatas);
}
// 看到 Extension 就明白这是预留的用户扩展的接口
extern TSet<IPersistentViewUniformBufferExtension*> PersistentViewUniformBufferExtensions;
for (IPersistentViewUniformBufferExtension* Extension : PersistentViewUniformBufferExtensions)
{
Extension->BeginFrame();
for (int32 ViewIndex = 0; ViewIndex < Views.Num(); ViewIndex++)
{
// Must happen before RHI thread flush so any tasks we dispatch here can land in the idle gap during the flush
Extension->PrepareView(&Views[ViewIndex]);
}
}
// 为每个试图准备光线追踪的数据,计算光线追踪的贴画的mesh
#if RHI_RAYTRACING
const int32 ReferenceViewIndex = 0;
FViewInfo& ReferenceView = Views[ReferenceViewIndex];
FRayTracingScene& RayTracingScene = Scene->RayTracingScene;
#endif
if (RendererOutput == ERendererOutput::FinalSceneColor)
{
#if RHI_RAYTRACING
// Prepare the scene for rendering this frame.
// Resets the internal arrays, but does not release any resources.
// 重置光线追踪的场景
RayTracingScene.Reset(IsRayTracingInstanceDebugDataEnabled(ReferenceView));
// 是否为光线追踪的贴花准备数据
// 贴花,比如在凹凸不平的地面上贴上血
if (ShouldPrepareRayTracingDecals(*Scene, ViewFamily))
{
// Calculate decal grid for ray tracing per view since decal fade is view dependent
// TODO: investigate reusing the same grid for all views (ie: different callable shader SBT entries for each view so fade alpha is still correct for each view)
for (int32 ViewIndex = 0; ViewIndex < Views.Num(); ViewIndex++)
{
FViewInfo& View = Views[ViewIndex];
// 如果要,就在这里创建贴画数据
View.RayTracingDecalUniformBuffer = CreateRayTracingDecalData(GraphBuilder, *Scene, View, RayTracingScene.NumCallableShaderSlots);
View.bHasRayTracingDecals = true;
RayTracingScene.NumCallableShaderSlots += Scene->Decals.Num();
}
}
else
{
TRDGUniformBufferRef<FRayTracingDecals> NullRayTracingDecalUniformBuffer = CreateNullRayTracingDecalsUniformBuffer(GraphBuilder);
for (int32 ViewIndex = 0; ViewIndex < Views.Num(); ViewIndex++)
{
FViewInfo& View = Views[ViewIndex];
View.RayTracingDecalUniformBuffer = NullRayTracingDecalUniformBuffer;
View.bHasRayTracingDecals = false;
}
}
// 。。。
#endif // RHI_RAYTRACING
主要看看 CreateRayTracingDecalData 如何创建光线追踪贴花的
TODO 07-04
FDeferredShadingSceneRenderer::Render
跳出来
void FDeferredShadingSceneRenderer::Render(FRDGBuilder& GraphBuilder)
{
// ...
if (RendererOutput == ERendererOutput::FinalSceneColor)
{
// 。。。
// 支持光线追踪 和 RHI 支持 光追 shader
if (IsRayTracingEnabled() && RHISupportsRayTracingShaders(ViewFamily.GetShaderPlatform()))
{
// Nanite raytracing manager update must run before GPUScene update since it can modify primitive data
Nanite::GRayTracingManager.Update(); //
// 。。。
}
// 。。。
}
#endif // RHI_RAYTRACING
Nanite::GRayTracingManager.Update();
void FRayTracingManager::Update()
{
const bool bUsingNaniteRayTracing = GetRayTracingMode() != ERayTracingMode::Fallback;
// process PendingRemoves 处理待删除的东西
{
TSet<uint32> StillPendingRemoves;
for (uint32 GeometryId : PendingRemoves)
{
FInternalData* Data = Geometries[GeometryId];
if (Data->bUpdating)
{
// can't remove until update is finished, delay to next frame
// 正在更新没法删,先记录一下
StillPendingRemoves.Add(GeometryId);
}
else
{
if (Data->AuxiliaryDataOffset != INDEX_NONE) // 辅助数据便宜
{
AuxiliaryDataAllocator.Free(Data->AuxiliaryDataOffset, Data->AuxiliaryDataSize); // 释放
}
ResourceToRayTracingIdMap.Remove(Data->ResourceId); // remove
Geometries.RemoveAt(GeometryId);
delete (Data);
}
}
Swap(PendingRemoves, StillPendingRemoves); // 交换一下
}
int32 NumPrimitivesScheduled = 0;
// scheduling pending builds
// 调度待构建的几何体
{
checkf(ScheduledBuilds.IsEmpty(), TEXT("Scheduled builds were not dispatched last frame."));
for (const FPendingBuild& PendingBuild : PendingBuilds)
{
if (NumPrimitivesScheduled >= GNaniteRayTracingMaxBuiltPrimitivesPerFrame) // 数量超过了没帧最大构建限制
{
break;
}
FInternalData& Data = *Geometries[PendingBuild.GeometryId];
Data.RayTracingGeometryRHI = PendingBuild.RayTracingGeometryRHI;
const FRayTracingGeometryInitializer& Initializer = Data.RayTracingGeometryRHI->GetInitializer();
NumPrimitivesScheduled += Initializer.TotalPrimitiveCount;
if (Data.AuxiliaryDataOffset != INDEX_NONE)
{
AuxiliaryDataAllocator.Free(Data.AuxiliaryDataOffset, Data.AuxiliaryDataSize);
}
Data.AuxiliaryDataSize = Initializer.TotalPrimitiveCount;
Data.AuxiliaryDataOffset = AuxiliaryDataAllocator.Allocate(Data.AuxiliaryDataSize);
for (auto& Primitive : Data.Primitives)
{
if (bUsingNaniteRayTracing)
{
Primitive->CachedRayTracingInstance.GeometryRHI = Data.RayTracingGeometryRHI;
}
auto NaniteProxy = static_cast<Nanite::FSceneProxyBase*>(Primitive->Proxy);
NaniteProxy->SetRayTracingDataOffset(Data.AuxiliaryDataOffset);
Primitive->Scene->GPUScene.AddPrimitiveToUpdate(Primitive->GetIndex(), EPrimitiveDirtyState::ChangedOther); // 把数据加到gpu scene
}
ScheduledBuilds.Add(PendingBuild.GeometryId); // 标记 已经调度了 该几何体的构建任务
}
// not using RemoveAtSwap to avoid starving requests in the middle
// not expecting significant number of elements remaining anyway
// 上面 0~ScheduledBuilds.Num() 拿去调度了
PendingBuilds.RemoveAt(0, ScheduledBuilds.Num());
DEC_DWORD_STAT_BY(STAT_NaniteRayTracingPendingBuilds, ScheduledBuilds.Num());
}
while (ReadbackBuffersNumPending > 0)
{
uint32 Index = (ReadbackBuffersWriteIndex + MaxReadbackBuffers - ReadbackBuffersNumPending) % MaxReadbackBuffers;
FReadbackData& ReadbackData = ReadbackBuffers[Index];
if (ReadbackData.MeshDataReadbackBuffer->IsReady())
{
ReadbackBuffersNumPending--;
auto MeshDataReadbackBufferPtr = (const uint32*)ReadbackData.MeshDataReadbackBuffer->Lock(ReadbackData.NumMeshDataEntries * sizeof(uint32)); // 线程锁
for (int32 GeometryIndex = 0; GeometryIndex < ReadbackData.Entries.Num(); ++GeometryIndex)
{
uint32 GeometryId = ReadbackData.Entries[GeometryIndex];
// FInternalData 看起来像描述nanite 几何体的数据
FInternalData& Data = *Geometries[GeometryId];
auto Header = (const FStreamOutMeshDataHeader*)(MeshDataReadbackBufferPtr + Data.BaseMeshDataOffset);
auto Segments = (const FStreamOutMeshDataSegment*)(Header + 1);
check(Header->NumClusters <= Data.NumResidentClusters);
const uint32 VertexBufferOffset = Header->VertexBufferOffset;
const uint32 IndexBufferOffset = Header->IndexBufferOffset;
const uint32 NumVertices = Header->NumVertices;
if (VertexBufferOffset == 0xFFFFFFFFu || IndexBufferOffset == 0xFFFFFFFFu)
{
// ran out of space in StreamOut buffers
// 说明是空,关闭更新
Data.bUpdating = false;
Data.BaseMeshDataOffset = -1;
check(Data.StagingAuxiliaryDataOffset != INDEX_NONE);
Data.StagingAuxiliaryDataOffset = INDEX_NONE;
UpdateRequests.Add(GeometryId); // request update again
DEC_DWORD_STAT_BY(STAT_NaniteRayTracingInFlightUpdates, 1);
INC_DWORD_STAT_BY(STAT_NaniteRayTracingFailedStreamOutRequests, 1);
continue;
}
// 构建初始化器 FRayTracingGeometryInitializer Initializer;
FRayTracingGeometryInitializer Initializer;
Initializer.DebugName = Data.DebugName;
// Initializer.bFastBuild = false;
// Initializer.bAllowUpdate = false;
Initializer.bAllowCompaction = false; // 是否允许压缩
Initializer.IndexBuffer = IndexBuffer->GetRHI();
Initializer.IndexBufferOffset = IndexBufferOffset * sizeof(uint32);
Initializer.TotalPrimitiveCount = 0;
Initializer.Segments.SetNum(Data.NumSegments); // 初始化的段数
for (uint32 SegmentIndex = 0; SegmentIndex < Data.NumSegments; ++SegmentIndex)
{
// 每一段的
const uint32 NumIndices = Segments[SegmentIndex].NumIndices; // 顶点数量
const uint32 FirstIndex = Segments[SegmentIndex].FirstIndex; // 第一个索引
FRayTracingGeometrySegment& Segment = Initializer.Segments[SegmentIndex];
Segment.FirstPrimitive = FirstIndex / 3;
Segment.NumPrimitives = NumIndices / 3;
Segment.VertexBuffer = VertexBuffer->GetRHI();
Segment.VertexBufferOffset = VertexBufferOffset * sizeof(FVector3f);
Segment.MaxVertices = NumVertices;
Initializer.TotalPrimitiveCount += Segment.NumPrimitives;
}
// 用 FRayTracingGeometryInitializer 创建 FRayTracingGeometryInitializer 集合体
FRayTracingGeometryRHIRef RayTracingGeometryRHI = RHICreateRayTracingGeometry(Initializer);
// 判断是否图元一帧处理太多了
if (NumPrimitivesScheduled < GNaniteRayTracingMaxBuiltPrimitivesPerFrame)
{
NumPrimitivesScheduled += RayTracingGeometryRHI->GetInitializer().TotalPrimitiveCount;
Data.RayTracingGeometryRHI = MoveTemp(RayTracingGeometryRHI);
if (Data.AuxiliaryDataOffset != INDEX_NONE)
{
AuxiliaryDataAllocator.Free(Data.AuxiliaryDataOffset, Data.AuxiliaryDataSize);
}
// allocate persistent auxiliary range
Data.AuxiliaryDataSize = CalculateAuxiliaryDataSizeInUints(Initializer.TotalPrimitiveCount);
Data.AuxiliaryDataOffset = AuxiliaryDataAllocator.Allocate(Data.AuxiliaryDataSize);
for (auto& Primitive : Data.Primitives)
{
if (bUsingNaniteRayTracing)
{
Primitive->CachedRayTracingInstance.GeometryRHI = Data.RayTracingGeometryRHI;
}
auto NaniteProxy = static_cast<Nanite::FSceneProxyBase*>(Primitive->Proxy);
NaniteProxy->SetRayTracingDataOffset(Data.AuxiliaryDataOffset);
// 加到GPU Scene里面
Primitive->Scene->GPUScene.AddPrimitiveToUpdate(Primitive->GetIndex(), EPrimitiveDirtyState::ChangedOther);
}
ScheduledBuilds.Add(GeometryId);
}
else
{
// 太多了,就往pending里面加
FPendingBuild PendingBuild;
PendingBuild.GeometryId = GeometryId;
PendingBuild.RayTracingGeometryRHI = MoveTemp(RayTracingGeometryRHI);
PendingBuilds.Add(MoveTemp(PendingBuild));
INC_DWORD_STAT_BY(STAT_NaniteRayTracingPendingBuilds, 1);
}
}
ReadbackData.Entries.Empty();
ReadbackData.MeshDataReadbackBuffer->Unlock();
}
else
{
break;
}
}
INC_DWORD_STAT_BY(STAT_NaniteRayTracingScheduledBuildsNumPrimitives, NumPrimitivesScheduled);
}
Nanite::GRayTracingManager.Update(); 结束了 回来。
void FDeferredShadingSceneRenderer::Render(FRDGBuilder& GraphBuilder)
{
// ...
if (RendererOutput == ERendererOutput::FinalSceneColor)
{
// 。。。
// 支持光线追踪 和 RHI 支持 光追 shader
if (IsRayTracingEnabled() && RHISupportsRayTracingShaders(ViewFamily.GetShaderPlatform()))
{
// Nanite raytracing manager update must run before GPUScene update since it can modify primitive data
Nanite::GRayTracingManager.Update(); // 处理读取缓冲区的数据,根据数据创建更新的几何体,将几何体添加到计划建立的列表
if (!ViewFamily.EngineShowFlags.PathTracing) // 不支持路径追踪
{
// get the default lighting miss shader (to implicitly fill in the MissShader library before the RT pipeline is created)
// 获取默认的照明缺失着色器(在创建 RT 管道之前隐式填充 MissShader 库)
GetRayTracingLightingMissShader(ReferenceView.ShaderMap);
RayTracingScene.NumMissShaderSlots++;
}
// 光照函数
if (ViewFamily.EngineShowFlags.LightFunctions)
{
// gather all the light functions that may be used (and also count how many miss shaders we will need)
FRayTracingLightFunctionMap RayTracingLightFunctionMap;
if (ViewFamily.EngineShowFlags.PathTracing)
{
RayTracingLightFunctionMap = GatherLightFunctionLightsPathTracing(Scene, ViewFamily.EngineShowFlags, ReferenceView.GetFeatureLevel());
}
else
{
RayTracingLightFunctionMap = GatherLightFunctionLights(Scene, ViewFamily.EngineShowFlags, ReferenceView.GetFeatureLevel());
}
if (!RayTracingLightFunctionMap.IsEmpty())
{
// If we got some light functions in our map, store them in the RDG blackboard so downstream functions can use them.
// The map itself will be strictly read-only from this point on.
GraphBuilder.Blackboard.Create<FRayTracingLightFunctionMap>(MoveTemp(RayTracingLightFunctionMap));
}
}
}
// 。。。
}
#endif // RHI_RAYTRACING
FXSystem
特效,做一些准备工作 pre render
// Notify the FX system that the scene is about to be rendered.
if (FXSystem && Views.IsValidIndex(0))
{
SCOPE_CYCLE_COUNTER(STAT_FDeferredShadingSceneRenderer_FXSystem_PreRender);
GraphBuilder.SetCommandListStat(GET_STATID(STAT_CLM_FXPreRender));
FXSystem->PreRender(GraphBuilder, GetSceneViews(), GetSceneUniforms(), bIsFirstSceneRenderer /*bAllowGPUParticleUpdate*/);
if (FGPUSortManager* GPUSortManager = FXSystem->GetGPUSortManager())
{
GPUSortManager->OnPreRender(GraphBuilder);
}
}
void FFXSystemSet::PreRender(FRDGBuilder& GraphBuilder, TConstStridedView<FSceneView> Views, FSceneUniformBuffer &SceneUniformBuffer, bool bAllowGPUParticleSceneUpdate)
{
for (FFXSystemInterface* FXSystem : FXSystems)
{
check(FXSystem);
FXSystem->PreRender(GraphBuilder, Views, SceneUniformBuffer, bAllowGPUParticleSceneUpdate);
}
}
void FFXSystem::PreRender(FRDGBuilder& GraphBuilder, TConstStridedView<FSceneView> Views, FSceneUniformBuffer &SceneUniformBuffer, bool bAllowGPUParticleSceneUpdate)
{
bAllowGPUParticleSceneUpdate = bAllowGPUParticleSceneUpdate && Views.Num() > 0 && Views[0].AllowGPUParticleUpdate();
if (RHISupportsGPUParticles() && bAllowGPUParticleSceneUpdate) // RHI 支持GPU粒子,允许GPU粒子场景更新
{
RDG_GPU_STAT_SCOPE(GraphBuilder, FXSystemPreRender);
RDG_CSV_STAT_EXCLUSIVE_SCOPE(GraphBuilder, FXSystem);
TUniformBufferRef<FViewUniformShaderParameters> ViewUniformBuffer = Views[0].ViewUniformBuffer; // buff
const FGlobalDistanceFieldParameterData* GlobalDistanceFieldParameterData = GetRendererModule().GetGlobalDistanceFieldParameterData(Views[0]); // 全局距离场参数
AddPass(
GraphBuilder,
RDG_EVENT_NAME("FFXSystem::PreRender"),
[this, ViewUniformBuffer, GlobalDistanceFieldParameterData](FRHICommandListImmediate& RHICmdList)
{
//SCOPE_CYCLE_COUNTER(STAT_FDeferredShadingSceneRenderer_FXSystem_PostRenderOpaque);
SCOPED_DRAW_EVENT(RHICmdList, GPUParticles_PreRender);
UpdateMultiGPUResources(RHICmdList);
RHICmdList.SetCurrentStat(GET_STATID(STAT_CLM_FXPreRender_Prepare));
PrepareGPUSimulation(RHICmdList);
RHICmdList.SetCurrentStat(GET_STATID(STAT_CLM_FXPreRender_Simulate));
SimulateGPUParticles(RHICmdList, EParticleSimulatePhase::Main, {}, nullptr);
RHICmdList.SetCurrentStat(GET_STATID(STAT_CLM_FXPreRender_Finalize));
FinalizeGPUSimulation(RHICmdList);
if (IsParticleCollisionModeSupported(GetShaderPlatform(), PCM_DistanceField))
{
RHICmdList.SetCurrentStat(GET_STATID(STAT_CLM_FXPreRender_PrepareCDF));
PrepareGPUSimulation(RHICmdList);
RHICmdList.SetCurrentStat(GET_STATID(STAT_CLM_FXPreRender_SimulateCDF));
SimulateGPUParticles(RHICmdList, EParticleSimulatePhase::CollisionDistanceField, ViewUniformBuffer, GlobalDistanceFieldParameterData);
//particles rendered during basepass may need to read pos/velocity buffers; must finalize unless we know for sure that nothing in base pass will read it.
RHICmdList.SetCurrentStat(GET_STATID(STAT_CLM_FXPreRender_FinalizeCDF));
FinalizeGPUSimulation(RHICmdList);
}
}
);
}
}
看AddPass的Lambda
UpdateMultiGPUResources(RHICmdList);
void FFXSystem::UpdateMultiGPUResources(FRHICommandListImmediate& RHICmdList)
{
if (LastFrameNewParticles.Num()) // 是否有新的粒子产生
{
//Inject particles spawned in the last frame, but only update the attribute textures
SCOPED_DRAW_EVENT(RHICmdList, ParticleInjection);
SCOPED_GPU_STAT(RHICmdList, ParticleSimulation);
// Set render targets.
// 设置渲染目标
FRHITexture* InjectRenderTargets[2] =
{
ParticleSimulationResources->RenderAttributesTexture.TextureRHI,
ParticleSimulationResources->SimulationAttributesTexture.TextureRHI
};
FRHIRenderPassInfo RPInfo(2, InjectRenderTargets, ERenderTargetActions::Load_Store);
RPInfo.ColorRenderTargets[0].ResolveTarget = ParticleSimulationResources->RenderAttributesTexture.TextureRHI;
RPInfo.ColorRenderTargets[1].ResolveTarget = ParticleSimulationResources->SimulationAttributesTexture.TextureRHI;
{
// 改变资源状态
RHICmdList.Transition({
FRHITransitionInfo(ParticleSimulationResources->RenderAttributesTexture.TextureRHI, ERHIAccess::Unknown, ERHIAccess::RTV),
FRHITransitionInfo(ParticleSimulationResources->SimulationAttributesTexture.TextureRHI, ERHIAccess::Unknown, ERHIAccess::RTV)
});
// 开启渲染通道
RHICmdList.BeginRenderPass(RPInfo, TEXT("UpdateMultiGPUResources")); // 设置render target 并清理,如果有就开启遮挡查询
// 。。。
}
}
// 。。。
}
如何begin一个pass
FORCEINLINE_DEBUGGABLE void BeginRenderPass(const FRHIRenderPassInfo& InInfo, const TCHAR* Name)
{
check(!IsInsideRenderPass()); // 避免在一个pass中又启动了一个pass
check(!IsInsideComputePass()); // 避免在一个计算过程中又启动了一个计算过程
InInfo.Validate();
if (Bypass()) // 是否要绕过渲染过程
{
GetContext().RHIBeginRenderPass(InInfo, Name); // 这里开始分开图形API了
}
else
{
// 不需要就存一个名字
TCHAR* NameCopy = AllocString(Name);
ALLOC_COMMAND(FRHICommandBeginRenderPass)(InInfo, NameCopy);
}
CacheActiveRenderTargets(InInfo); // 缓存激活的渲染目标
ResetSubpass(InInfo.SubpassHint); //
PersistentState.bInsideRenderPass = true;
if (InInfo.NumOcclusionQueries) // 存在遮挡查询
{
PersistentState.bInsideOcclusionQueryBatch = true;
GDynamicRHI->RHIBeginOcclusionQueryBatch_TopOfPipe(*this, InInfo.NumOcclusionQueries);
}
}
回来
void FFXSystem::UpdateMultiGPUResources(FRHICommandListImmediate& RHICmdList)
{
if (LastFrameNewParticles.Num()) // 是否有新的粒子产生
{
// 。。。
{
// 开启渲染通道
RHICmdList.BeginRenderPass(RPInfo, TEXT("UpdateMultiGPUResources"));
// viewport
RHICmdList.SetViewport(0.0f, 0.0f, 0.0f, (float)GParticleSimulationTextureSizeX, (float)GParticleSimulationTextureSizeY, 1.0f);
// PSO
FGraphicsPipelineStateInitializer GraphicsPSOInit;
RHICmdList.ApplyCachedRenderTargets(GraphicsPSOInit);
GraphicsPSOInit.DepthStencilState = TStaticDepthStencilState<false, CF_Always>::GetRHI();
GraphicsPSOInit.RasterizerState = TStaticRasterizerState<FM_Solid, CM_None>::GetRHI();
GraphicsPSOInit.BlendState = TStaticBlendState<>::GetRHI();
// Inject particles. 生成新的粒子
InjectNewParticles<true>(RHICmdList, GraphicsPSOInit, this->FeatureLevel, this->LastFrameNewParticles);
RHICmdList.EndRenderPass();
}
}
// 。。。
}
InjectNewParticles
/**
* Data passed to the GPU to inject a new particle in to the simulation.
*/
struct FNewParticle
{
FVector3f Position; // 粒子初始化位置
float RelativeTime; // 生命周期
FVector3f Velocity; // 初始速度
float TimeScale; // 粒子的时间缩放
FVector2f Size; // 粒子大小
float Rotation; // 粒子初始旋转角度
float RelativeRotationRate; // 粒子旋转速率
float DragCoefficient; // 粒子的空气阻力系数
float VectorFieldScale; // Per-particle vector field scale
/** Resilience for collision. */ // 碰撞的弹力
union
{
float Resilience; // 弹力
int32 AllocatedTileIndex;
} ResilienceAndTileIndex;
/** Random selection of orbit(轨道) attributes. */
float RandomOrbit;
/** The offset at which to inject the new particle. */
FVector2f Offset;
};
/**
* Injects new particles in to the GPU simulation.
* @param NewParticles - A list of particles to inject in to the simulation.
*/
template<bool StaticPropertiesOnly>
void InjectNewParticles(FRHICommandList& RHICmdList, FGraphicsPipelineStateInitializer& GraphicsPSOInit, ERHIFeatureLevel::Type FeatureLevel, const TArray<FNewParticle>& NewParticles)
{
if (GIsRenderingThreadSuspended.Load(EMemoryOrder::Relaxed) || !CVarSimulateGPUParticles.GetValueOnAnyThread()) // 渲染线程是否被暂停,控制台变量,模拟GPU粒子的开关
{
return;
}
const int32 MaxParticlesPerDrawCall = GParticleScratchVertexBufferSize / sizeof(FNewParticle); // 最多要画多少个粒子
FRHIBuffer* ScratchVertexBufferRHI = GParticleScratchVertexBuffer.VertexBufferRHI; // buffer
int32 ParticleCount = NewParticles.Num(); // 数量
int32 FirstParticle = 0;
while ( ParticleCount > 0 )
{
// Copy new particles in to the vertex buffer.
const int32 ParticlesThisDrawCall = FMath::Min<int32>( ParticleCount, MaxParticlesPerDrawCall );
const void* Src = NewParticles.GetData() + FirstParticle;
void* Dest = RHICmdList.LockBuffer( ScratchVertexBufferRHI, 0, ParticlesThisDrawCall * sizeof(FNewParticle), RLM_WriteOnly );
FMemory::Memcpy( Dest, Src, ParticlesThisDrawCall * sizeof(FNewParticle) );
RHICmdList.UnlockBuffer( ScratchVertexBufferRHI );
ParticleCount -= ParticlesThisDrawCall;
FirstParticle += ParticlesThisDrawCall;
// Grab shaders. 获取shader
TShaderMapRef<FParticleInjectionVS> VertexShader(GetGlobalShaderMap(FeatureLevel));
TShaderMapRef<TParticleInjectionPS<StaticPropertiesOnly> > PixelShader(GetGlobalShaderMap(FeatureLevel));
// PSO set
GraphicsPSOInit.BoundShaderState.VertexDeclarationRHI = GParticleInjectionVertexDeclaration.VertexDeclarationRHI;
GraphicsPSOInit.BoundShaderState.VertexShaderRHI = VertexShader.GetVertexShader();
GraphicsPSOInit.BoundShaderState.PixelShaderRHI = PixelShader.GetPixelShader();
GraphicsPSOInit.PrimitiveType = PT_TriangleList;
SetGraphicsPipelineState(RHICmdList, GraphicsPSOInit, 0); // 创建或者获取PSO缓存,命令列表设置PSO
SetShaderParametersLegacyVS(RHICmdList, VertexShader); // 设置shader参数吧
// Stream 0: New particles.
RHICmdList.SetStreamSource(
0,
ScratchVertexBufferRHI,
/*Offset=*/ 0
);
// Stream 1: TexCoord.
RHICmdList.SetStreamSource(
1,
GParticleTexCoordVertexBuffer.VertexBufferRHI,
/*Offset=*/ 0
);
// Inject particles.
RHICmdList.DrawIndexedPrimitive(
GParticleIndexBuffer.IndexBufferRHI,
/*BaseVertexIndex=*/ 0,
/*MinIndex=*/ 0,
/*NumVertices=*/ 4,
/*StartIndex=*/ 0,
/*NumPrimitives=*/ 2,
/*NumInstances=*/ ParticlesThisDrawCall
);
}
}
void FFXSystem::UpdateMultiGPUResources(FRHICommandListImmediate& RHICmdList)
{
if (LastFrameNewParticles.Num()) // 是否有新的粒子产生
{
// 。。。
{
// 。。。
// Inject particles. 生成新的粒子
InjectNewParticles<true>(RHICmdList, GraphicsPSOInit, this->FeatureLevel, this->LastFrameNewParticles);
RHICmdList.EndRenderPass();
}
}
// Clear out particles from last frame
LastFrameNewParticles.Reset();
#if WITH_MGPU
// 多GPU
PhaseToBroadcastResourceTransfer = EParticleSimulatePhase::Last;
while (PhaseToBroadcastResourceTransfer > EParticleSimulatePhase::First && NumGPUSimulations[PhaseToBroadcastResourceTransfer] == 0)
{
PhaseToBroadcastResourceTransfer = static_cast<EParticleSimulatePhase::Type>(PhaseToBroadcastResourceTransfer - 1);
}
PhaseToWaitForResourceTransfer = EParticleSimulatePhase::First;
while (PhaseToWaitForResourceTransfer < PhaseToBroadcastResourceTransfer && NumGPUSimulations[PhaseToWaitForResourceTransfer] == 0)
{
PhaseToWaitForResourceTransfer = static_cast<EParticleSimulatePhase::Type>(PhaseToWaitForResourceTransfer + 1);
}
#endif
}
FFXSystem::PreRender
回来,上面一大串 看完了 UpdateMultiGPUResources
然后继续下去
void FFXSystem::PreRender(FRDGBuilder& GraphBuilder, TConstStridedView<FSceneView> Views, FSceneUniformBuffer &SceneUniformBuffer, bool bAllowGPUParticleSceneUpdate)
{
// 。。。
if (RHISupportsGPUParticles() && bAllowGPUParticleSceneUpdate) // RHI 支持GPU粒子,允许GPU粒子场景更新
{
// 。。。
AddPass(
GraphBuilder,
RDG_EVENT_NAME("FFXSystem::PreRender"),
[this, ViewUniformBuffer, GlobalDistanceFieldParameterData](FRHICommandListImmediate& RHICmdList)
{
//SCOPE_CYCLE_COUNTER(STAT_FDeferredShadingSceneRenderer_FXSystem_PostRenderOpaque);
SCOPED_DRAW_EVENT(RHICmdList, GPUParticles_PreRender);
UpdateMultiGPUResources(RHICmdList);
// 准备GPU的模拟
RHICmdList.SetCurrentStat(GET_STATID(STAT_CLM_FXPreRender_Prepare));
PrepareGPUSimulation(RHICmdList); // 设置 当前 FParticleStateTextures 的状态,
// 模拟GPU粒子
RHICmdList.SetCurrentStat(GET_STATID(STAT_CLM_FXPreRender_Simulate));
SimulateGPUParticles(RHICmdList, EParticleSimulatePhase::Main, {}, nullptr);
// 然后完成
RHICmdList.SetCurrentStat(GET_STATID(STAT_CLM_FXPreRender_Finalize));
FinalizeGPUSimulation(RHICmdList);
// 支持例子碰撞
if (IsParticleCollisionModeSupported(GetShaderPlatform(), PCM_DistanceField))
{
RHICmdList.SetCurrentStat(GET_STATID(STAT_CLM_FXPreRender_PrepareCDF));
PrepareGPUSimulation(RHICmdList);
RHICmdList.SetCurrentStat(GET_STATID(STAT_CLM_FXPreRender_SimulateCDF));
SimulateGPUParticles(RHICmdList, EParticleSimulatePhase::CollisionDistanceField, ViewUniformBuffer, GlobalDistanceFieldParameterData);
//particles rendered during basepass may need to read pos/velocity buffers; must finalize unless we know for sure that nothing in base pass will read it.
RHICmdList.SetCurrentStat(GET_STATID(STAT_CLM_FXPreRender_FinalizeCDF));
FinalizeGPUSimulation(RHICmdList);
}
}
);
}
}
这里粒子系统后面再展开吧
有点长了总结一下
BeginRenderingViewFamilies 这个函数准备开始画3D场景了
这个函数前面通过 SendAllEndOfFrameUpdates 确保之前已经画完了
SendAllEndOfFrameUpdates 做了一些tick actor component 结束帧相关,并且把transform和其他数据传递给了GPU
然后做了一些 xxxxxxxxxxxxxxxxx
然后往渲染线程有了如下调用
RenderViewFamilies_RenderThread(RHICmdList, LocalSceneRenderers);
其中有一步
SceneRenderer->Render(GraphBuilder);
这个SceneRenderer可能是延迟渲染的,或者是移动渲染
比如 FDeferredShadingSceneRenderer::Render
这个函数一路向下翻翻翻
翻到了这里FX相关和GPU排序管理器这里
===========================================
然后回来
FXSystem->PreRender(GraphBuilder, GetSceneViews(), GetSceneUniforms(), bIsFirstSceneRenderer /bAllowGPUParticleUpdate/); 结束了
后面是GPU批次排序
void FDeferredShadingSceneRenderer::Render(FRDGBuilder& GraphBuilder)
{
// 。。。
// Notify the FX system that the scene is about to be rendered.
if (FXSystem && Views.IsValidIndex(0))
{
SCOPE_CYCLE_COUNTER(STAT_FDeferredShadingSceneRenderer_FXSystem_PreRender);
GraphBuilder.SetCommandListStat(GET_STATID(STAT_CLM_FXPreRender));
FXSystem->PreRender(GraphBuilder, GetSceneViews(), GetSceneUniforms(), bIsFirstSceneRenderer /*bAllowGPUParticleUpdate*/);
if (FGPUSortManager* GPUSortManager = FXSystem->GetGPUSortManager()) // GPU排序管理器,来处理不同的GPU任务,然后 OnPreRender
{
GPUSortManager->OnPreRender(GraphBuilder);
}
}
这里面manager也是在addpass
看个结构
/**
* Defines a sort batch that regroups several sort tasks created by FGPUSortManager::AddTask(), becoming sort the batch elements.
* The condition to regroup task in the same batch rely mostly around the key precisions and the sort location (PreRender or PostRenderOpaque).
* Each SortBatch maps to a single GPU sort task.
*/
struct FSortBatch
{
/** An Id defining this batch. Passed along FGPUSortKeyGenDelegate. */
int32 Id = INDEX_NONE;
/** The number of elements (tasks) grouped in this batch. */
int32 NumElements = 0;
/**
* The common requirement flag for this batch. Compatible with each element.
* As tasks are added, the flag can become more restrictive, and new registered tasks
* could require creating new batches if their requirements are too different.
*/
EGPUSortFlags Flags = EGPUSortFlags::None;
/** The buffer to hold the final sorted values. */
FDynamicValueBuffer* DynamicValueBuffer = nullptr;
/** The scratch buffers used to hold the initial keys and values and perform the GPU sort. */
FParticleSortBuffers* SortBuffers = nullptr;
/** This batch processing order in the frame. */
ESortBatchProcessingOrder ProcessingOrder = ESortBatchProcessingOrder::Undefined;
// 。。。
};
void FGPUSortManager::OnPreRender(FRDGBuilder& GraphBuilder)
{
RDG_CSV_STAT_EXCLUSIVE_SCOPE(GraphBuilder, GPUSort);
RDG_GPU_MASK_SCOPE(GraphBuilder, FRHIGPUMask::All());
AddPass(
GraphBuilder,
RDG_EVENT_NAME("FGPUSortManager::OnPreRender"),
[this](FRHICommandListImmediate& RHICmdList)
{
LLM_SCOPE(ELLMTag::GPUSort);
FinalizeSortBatches(); // 设置一些标志位
UpdateSortBuffersPool(RHICmdList); // 更新排序的缓冲区
if (SortBatches.Num())
{
// Sort batches so that the next batch to handle is at the end of the array.
SortBatches.Sort([](const FSortBatch& A, const FSortBatch& B) { return (uint32)A.ProcessingOrder > (uint32)B.ProcessingOrder; });
SCOPED_GPU_STAT(RHICmdList, GPUKeyGenAndSort);
while (SortBatches.Num() && SortBatches.Last().ProcessingOrder == ESortBatchProcessingOrder::KeyGenAndSortAfterPreRender)
{
// Remove the SortBatch but don't remove the SortBuffers from the pool since it can be reused immediately.
FSortBatch SortBatch = SortBatches.Pop();
FParticleSortBuffers* SortBuffers = SortBuffersPool.Last();
SortBatch.SortBuffers = SortBuffers;
checkSlow(SortBuffers && SortBatch.GetUsedValueCount() <= SortBuffers->GetSize());
SCOPED_DRAW_EVENTF(RHICmdList, GPUSortBatch, TEXT("GPUSort_Batch%d(%s)"), SortBatch.Id, GetPrecisionString(SortBatch.Flags));
SortBatch.GenerateKeys(RHICmdList, Callbacks, EGPUSortFlags::KeyGenAfterPreRender);
SortBatch.SortAndResolve(RHICmdList, FeatureLevel);
// Release the sort batch.
checkSlow(SortBatch.DynamicValueBuffer && SortBatch.DynamicValueBuffer->CurrentSortBatchId == SortBatch.Id);
SortBatch.DynamicValueBuffer->CurrentSortBatchId = INDEX_NONE;
SortBatch.SortBuffers = nullptr;
}
for (int32 BatchIndex = SortBatches.Num() - 1; BatchIndex >= 0 && SortBatches[BatchIndex].ProcessingOrder == ESortBatchProcessingOrder::KeyGenAfterPreRenderAndSortAfterPostRenderOpaque; --BatchIndex)
{
// Those sort batches will be processed again after PostRenderOpaque(). Because of this, the particle sort buffers can not be reused.
FSortBatch& SortBatch = SortBatches[BatchIndex];
FParticleSortBuffers* SortBuffers = SortBuffersPool.Pop();
SortBatch.SortBuffers = SortBuffers;
checkSlow(SortBuffers && SortBatch.GetUsedValueCount() <= SortBuffers->GetSize());
SCOPED_DRAW_EVENTF(RHICmdList, GPUSortBatchPreStep, TEXT("GPUSort_Batch%d(PreStep,%s)"), SortBatch.Id, GetPrecisionString(SortBatch.Flags));
SortBatch.GenerateKeys(RHICmdList, Callbacks, EGPUSortFlags::KeyGenAfterPreRender);
}
}
PostPreRenderEvent.Broadcast(RHICmdList);
}
);
}
FGPUScene::Update
文档太长有点卡,下一章节从GPUSceneUpdate开始
{
RDG_CSV_STAT_EXCLUSIVE_SCOPE(GraphBuilder, UpdateGPUScene);
RDG_GPU_STAT_SCOPE(GraphBuilder, GPUSceneUpdate);
if (bIsFirstSceneRenderer) // 第一次刷新RHI
{
GraphBuilder.SetFlushResourcesRHI();
}
Scene->GPUScene.Update(GraphBuilder, GetSceneUniforms(), *Scene, ExternalAccessQueue); // GPU SCENE Update
for (int32 ViewIndex = 0; ViewIndex < Views.Num(); ViewIndex++) // 遍历VIEW
{
FViewInfo& View = Views[ViewIndex];
RDG_GPU_MASK_SCOPE(GraphBuilder, View.GPUMask);
Scene->GPUScene.UploadDynamicPrimitiveShaderDataForView(GraphBuilder, *Scene, View, ExternalAccessQueue); // 更新动态基元shader数据
Scene->GPUScene.DebugRender(GraphBuilder, *Scene, GetSceneUniforms(), View);
}
InstanceCullingManager.BeginDeferredCulling(GraphBuilder, Scene->GPUScene); // 延迟剔除
if (Views.Num() > 0)
{
FViewInfo& View = Views[0];
Scene->UpdatePhysicsField(GraphBuilder, View); // 物理
}
}