(1)
TypeOffsetTable 是 Primitive Type 相同 Primitive 的结束偏移,不是开始偏移,第一个类型开始偏移是 0,第一个类型结束偏移,是第一个类型的个数
Source\Runtime\Renderer\Private\RendererScene.cpp
FTypeOffsetTableEntry& NextEntry = TypeOffsetTable[TypeIndex];
(1)
Primitives SOA 数据,不会修改这个数据结构,PersistentPrimitiveIdToIndexMap 是进行二次映射,映射到 Primitives,PersistentPrimitiveIdToIndexMap 下标索引和 Primitives 一致,PersistentPrimitiveIdToIndexMap 下标索引内容,对应 Primitives 索引
Primitives[SourceIndex]->PackedIndex 表示应该放置的数组下标
Source\Runtime\Renderer\Private\RendererScene.cpp
FPersistentPrimitiveIndex MovedPersisitentIndex = Primitives[DestIndex]->PersistentIndex;
PersistentPrimitiveIdToIndexMap[MovedPersisitentIndex.Index] = SourceIndex;
Primitives[SourceIndex]->PackedIndex = DestIndex;
(1)
需要更新 instance 条件,UpdatedInstances 和 AddedLocalPrimitiveSceneInfos
PendingAllocateInstanceIds.Reserve(UpdatedInstances.Num() + AddedLocalPrimitiveSceneInfos.Num());
(1)
primitive 更新条件 AddedPrimitiveSceneInfos 和 UpdatedTransforms 和 UpdatedInstances,不太明白为什么 UpdatedInstances 更新需要修改 primitive
const int32 SceneInfosContainerReservedSize = AddedPrimitiveSceneInfos.Num() + UpdatedTransforms.Num() + UpdatedInstances.Num();
(1)
GPUSceneInstanceSceneDataRW 是 float4 的 soa
Saved\ShaderDebugInfo\PCD3D_SM6\Global\FGPUSceneSetInstancePrimitiveIdCS\0\GPUSceneDataManagement.usf
RWStructuredBuffer<float4> GPUSceneInstanceSceneDataRW;
float4 LoadInstanceSceneDataElement(uint Index)
{
return GPUSceneInstanceSceneDataRW[Index];
}
(1)
Source\Runtime\Engine\Public\InstanceUniformShaderParameters.h
ENGINE_API void Build
(
uint32 PrimitiveId,
uint32 RelativeId,
uint32 InstanceFlags,
uint32 LastUpdateFrame,
uint32 CustomDataCount,
float RandomID,
bool bIsVisible = true
);
(1)
Source\Runtime\Renderer\Private\GPUScene.cpp
InstanceUploadInfo.InstanceSceneDataBuffers = PrimitiveSceneInfo->GetInstanceSceneDataBuffers();
if (InstanceUploadInfo.InstanceSceneDataBuffers)
{
const FInstanceSceneDataBuffers::FReadView InstanceSceneDataBuffers = InstanceUploadInfo.InstanceSceneDataBuffers->GetReadView();
InstanceUploadInfo.NumInstances = InstanceUploadInfo.InstanceSceneDataBuffers->GetNumInstances();
InstanceUploadInfo.InstanceFlags = PackFlags(InstanceSceneDataBuffers.Flags);
InstanceUploadInfo.InstanceLightShadowUVBias = InstanceSceneDataBuffers.InstanceLightShadowUVBias;
InstanceUploadInfo.InstanceCustomData = InstanceSceneDataBuffers.InstanceCustomData;
InstanceUploadInfo.InstanceRandomID = InstanceSceneDataBuffers.InstanceRandomIDs;
InstanceUploadInfo.InstanceHierarchyOffset = InstanceSceneDataBuffers.InstanceHierarchyOffset;
InstanceUploadInfo.InstancePayloadExtension = InstanceSceneDataBuffers.InstancePayloadExtension;
InstanceUploadInfo.InstanceLocalBounds = InstanceSceneDataBuffers.InstanceLocalBounds;
#if WITH_EDITOR
InstanceUploadInfo.InstanceEditorData = InstanceSceneDataBuffers.InstanceEditorData;
#endif
(1)
UploadDataSourceAdapter cpu 信息获取读写封装
PrimitiveUploader gpu 数据读写封装
GetPrimitiveShaderData 获取内存 primitive 数据
Source\Runtime\Renderer\Private\GPUScene.cpp
ParallelForTemplate(TEXT("GPUScene Upload Primitives Task"), TaskContext.NumPrimitiveDataUploads, 1, [&TaskContext, &UploadDataSourceAdapter](int32 ItemIndex)
{
FOptionalTaskTagScope TaskTagScope(ETaskTag::EParallelRenderingThread);
FVector4f* DstData = static_cast<FVector4f*>(TaskContext.PrimitiveUploader->GetRef(ItemIndex));
UploadDataSourceAdapter.GetPrimitiveShaderData(ItemIndex, DstData);
}, bExecuteInParallel ? EParallelForFlags::None : EParallelForFlags::ForceSingleThread);
}
(1)
Instance Data 内存数据打包流程
Source\Runtime\Engine\Public\InstanceUniformShaderParameters.h
FORCEINLINE void BuildInternal
(
uint32 PrimitiveId,
uint32 RelativeId,
uint32 InstanceFlags,
uint32 LastUpdateFrame,
uint32 CustomDataCount,
float RandomID,
const FRenderTransform& LocalToWorld,
bool bIsVisible,
bool bSupportsCompressedTransforms
)
{
// Note: layout must match GetInstanceData in SceneData.ush and InitializeInstanceSceneData in GPUSceneWriter.ush
const float RotDeterminant = LocalToWorld.RotDeterminant();
if (RotDeterminant < 0.0f)
{
InstanceFlags |= INSTANCE_SCENE_DATA_FLAG_DETERMINANT_SIGN;
}
else
{
InstanceFlags &= ~INSTANCE_SCENE_DATA_FLAG_DETERMINANT_SIGN;
}
// Mark zero scaled instances as hidden.
if (!bIsVisible || RotDeterminant == 0.0f)
{
InstanceFlags |= INSTANCE_SCENE_DATA_FLAG_HIDDEN;
}
checkSlow((PrimitiveId & 0x000FFFFF) == PrimitiveId);
checkSlow((InstanceFlags & 0x00000FFF) == InstanceFlags);
checkSlow((RelativeId & 0x00FFFFFF) == RelativeId);
checkSlow((CustomDataCount & 0x000000FF) == CustomDataCount);
const uint32 Packed0 = (InstanceFlags << 20u) | PrimitiveId;
const uint32 Packed1 = (CustomDataCount << 24u) | RelativeId;
Data[0].X = *(const float*)&Packed0;
Data[0].Y = *(const float*)&Packed1;
Data[0].Z = *(const float*)&LastUpdateFrame;
Data[0].W = *(const float*)&RandomID;
if (bSupportsCompressedTransforms)
{
FCompressedTransform CompressedLocalToWorld(LocalToWorld);
Data[1] = *(const FVector4f*)&CompressedLocalToWorld.Rotation[0];
Data[2] = *(const FVector3f*)&CompressedLocalToWorld.Translation;
}
else
{
// Note: writes 3x float4s
LocalToWorld.To3x4MatrixTranspose((float*)&Data[1]);
}
}
(1)
instance data 内存数据编码,上传显存数据
Source\Runtime\Renderer\Private\GPUScene.cpp
if (UploadInfo.InstanceSceneDataBuffers != nullptr)
{
InstanceSceneData.BuildInternal(
UploadInfo.PrimitiveID,
InstanceIndex,
UploadInfo.InstanceFlags,
UploadInfo.LastUpdateSceneFrameNumber,
UploadInfo.InstanceCustomDataCount,
RandomID,
UploadInfo.InstanceSceneDataBuffers->GetInstanceToPrimitiveRelative(InstanceIndex),
!UploadInfo.bIsPrimitiveForceHidden && UploadInfo.InstanceSceneDataBuffers->GetInstanceVisible(InstanceIndex),
bSupportsCompressedTransforms
);
}
(1)
instance data 保存在 Primitive data 中的 InstanceSceneDataBuffers 或者 PrimitiveInstances 字段, ism static mesh 或者多个 instance 使用的是 InstanceSceneDataBuffers ,dynamic mesh 使用的是 PrimitiveInstances 字段传递 instance 数据
Source\Runtime\Renderer\Private\GPUScene.cpp
FInstanceSceneShaderData InstanceSceneData;
if (UploadInfo.InstanceSceneDataBuffers != nullptr)
{
InstanceSceneData.BuildInternal(
UploadInfo.PrimitiveID,
InstanceIndex,
UploadInfo.InstanceFlags,
UploadInfo.LastUpdateSceneFrameNumber,
UploadInfo.InstanceCustomDataCount,
RandomID,
UploadInfo.InstanceSceneDataBuffers->GetInstanceToPrimitiveRelative(InstanceIndex),
!UploadInfo.bIsPrimitiveForceHidden && UploadInfo.InstanceSceneDataBuffers->GetInstanceVisible(InstanceIndex),
bSupportsCompressedTransforms
);
}
else if (UploadInfo.PrimitiveInstances.IsEmpty())
{
// This path should only be taken for uninstanced primitives
check(UploadInfo.NumInstances == 1 && InstanceIndex == 0);
InstanceSceneData.BuildInternal(
UploadInfo.PrimitiveID,
InstanceIndex,
UploadInfo.InstanceFlags,
UploadInfo.LastUpdateSceneFrameNumber,
UploadInfo.InstanceCustomDataCount,
RandomID,
UploadInfo.PrimitiveToWorld,
!UploadInfo.bIsPrimitiveForceHidden,
bSupportsCompressedTransforms
);
}
else
{
const FInstanceSceneData& SceneData = UploadInfo.PrimitiveInstances[InstanceIndex];
InstanceSceneData.Build(
UploadInfo.PrimitiveID,
InstanceIndex,
UploadInfo.InstanceFlags,
UploadInfo.LastUpdateSceneFrameNumber,
UploadInfo.InstanceCustomDataCount,
RandomID,
SceneData.LocalToPrimitive,
UploadInfo.PrimitiveToWorld,
!UploadInfo.bIsPrimitiveForceHidden
);
}
(1)
gpu scene 收集 dynamic instance 数据 MeshBatchData
E:\e\UE4\UnrealEngine\Engine\Source\Runtime\Renderer\Private\GPUScene.cpp
void FGPUScenePrimitiveCollector::Add(
const FMeshBatchDynamicPrimitiveData* MeshBatchData,
const FPrimitiveUniformShaderParameters& PrimitiveShaderParams,
uint32 NumInstances,
uint32& OutPrimitiveIndex,
uint32& OutInstanceSceneDataOffset)
{
check(GPUSceneDynamicContext != nullptr);
check(!bCommitted);
// Lazy allocation of the upload data to not waste space and processing if none was needed.
if (UploadData == nullptr)
{
UploadData = AllocateUploadData();
}
const int32 PrimitiveIndex = UploadData->PrimitiveData.Num();
FPrimitiveData& PrimitiveData = UploadData->PrimitiveData.AddDefaulted_GetRef();
if (MeshBatchData != nullptr)
{
// make sure the source data is appropriately structured
MeshBatchData->Validate(NumInstances);
PrimitiveData.SourceData = *MeshBatchData;
}
const int32 PayloadFloat4Stride = PrimitiveData.SourceData.GetPayloadFloat4Stride();
PrimitiveData.ShaderParams = &PrimitiveShaderParams;
PrimitiveData.NumInstances = NumInstances;
PrimitiveData.LocalInstanceSceneDataOffset = UploadData->TotalInstanceCount;
PrimitiveData.LocalPayloadDataOffset = PayloadFloat4Stride > 0 ? UploadData->InstancePayloadDataFloat4Count : INDEX_NONE;
UploadData->TotalInstanceCount += NumInstances;
UploadData->InstancePayloadDataFloat4Count += PayloadFloat4Stride * NumInstances;
if (PrimitiveData.SourceData.DataWriterGPU.IsBound())
{
// Enqueue this primitive data to be executed (either upon upload or deferred to a later GPU write pass)
UploadData->GPUWritePrimitives.Add(PrimitiveIndex);
}
// Set the output data offsets
OutPrimitiveIndex = PrimitiveIndex;
OutInstanceSceneDataOffset = PrimitiveData.LocalInstanceSceneDataOffset;
}
(1)
gpu 数据更新
Source\Runtime\Renderer\Private\DeferredShadingRenderer.cpp
FInitViewTaskDatas InitViewTaskDatas = OnRenderBegin(GraphBuilder);
(1)
Source\Runtime\Renderer\Private\DeferredShadingRenderer.cpp
{
RDG_GPU_STAT_SCOPE(GraphBuilder, VisibilityCommands);
BeginInitViews(GraphBuilder, SceneTexturesConfig, InstanceCullingManager, ExternalAccessQueue, InitViewTaskDatas);
}
(1)
Source\Runtime\Renderer\Private\DeferredShadingRenderer.cpp
InitViewTaskDatas.VisibilityTaskData->FinishGatherDynamicMeshElements(BasePassDepthStencilAccess, InstanceCullingManager, VirtualTextureUpdater.Get());
(1)
Source\Runtime\Renderer\Private\DeferredShadingRenderer.cpp
Scene->GPUScene.UploadDynamicPrimitiveShaderDataForView(GraphBuilder, View);
(1)
Source\Runtime\Engine\Private\SceneManagement.cpp
void FMeshElementCollector::Commit()
{
TRACE_CPUPROFILER_EVENT_SCOPE(FMeshElementCollector::Commit);
check(RHICmdList);
for (TPair<FGPUScenePrimitiveCollector*, FMeshBatch*> Pair : MeshBatchesForGPUScene)
{
GetRendererModule().AddMeshBatchToGPUScene(Pair.Key, *Pair.Value);
}
(1)
bGPUWrite 或者 InstanceDynamicData.Num()) == NumInstances 都可以
Source\Runtime\Engine\Public\MeshBatch.h
/**
* Dynamic primitive/instance data for a mesh batch element.
*
* NOTES:
* - When applied to a FMeshBatchElement, data provided to the TConstArrayView members are expected to live until the end of the frame on the render thread
* - If `DataWriterGPU` is bound and the TConstArrayView members are left empty, the delegate is expected to write any missing data, as it will not be uploaded
*/
struct FMeshBatchDynamicPrimitiveData
{
FORCEINLINE void Validate(uint32 NumInstances) const
{
#if DO_CHECK
// Ensure array views are sized exactly for all instances, or are empty and there is a GPU writer
const bool bGPUWrite = DataWriterGPU.IsBound();
checkf(uint32(InstanceSceneData.Num()) == NumInstances || (bGPUWrite && InstanceSceneData.Num() == 0),
TEXT("DynamicPrimitiveData provided should have %u instances in InstanceSceneData. Found %d"),
NumInstances, InstanceSceneData.Num());
if (PayloadDataFlags & INSTANCE_SCENE_DATA_FLAG_HAS_DYNAMIC_DATA)
{
checkf(uint32(InstanceDynamicData.Num()) == NumInstances || (bGPUWrite && InstanceDynamicData.Num() == 0),
TEXT("DynamicPrimitiveData provided should have %u elements in InstanceDynamicData. Found %d"),
NumInstances, InstanceDynamicData.Num());
}
if (PayloadDataFlags & INSTANCE_SCENE_DATA_FLAG_HAS_CUSTOM_DATA)
{
checkf(NumInstanceCustomDataFloats > 0,
TEXT("DynamicPrimitiveData provided has the custom data flag set, but NumInstanceCustomDataFloats == 0"));
checkf(uint32(InstanceCustomData.Num()) == NumInstances * NumInstanceCustomDataFloats || (bGPUWrite && InstanceCustomData.Num() == 0),
TEXT("DynamicPrimitiveData provided should have %u elements in InstanceCustomData. Found %d"),
NumInstances * NumInstanceCustomDataFloats, InstanceCustomData.Num());
}
#endif
}
};
(1)
dynamic primitive instance 数据上传,取决于 DataWriterGPU
Source\Runtime\Renderer\Private\GPUScene.cpp
void FGPUScene::UploadDynamicPrimitiveShaderDataForViewInternal(FRDGBuilder& GraphBuilder, FViewInfo& View, UE::Renderer::Private::IShadowInvalidatingInstances *ShadowInvalidatingInstances)
// Defer this write to a later GPU write pass
FDeferredGPUWrite DeferredWrite;
DeferredWrite.DataWriterGPU = PrimData.SourceData.DataWriterGPU;
DeferredWrite.ViewId = View.GPUSceneViewId;
DeferredWrite.PrimitiveId = PrimitiveIdStart + PrimitiveIndex;
DeferredWrite.InstanceSceneDataOffset = InstanceIdStart + PrimData.LocalInstanceSceneDataOffset;
uint32 PassIndex = uint32(PrimData.SourceData.DataWriterGPUPass);
DeferredGPUWritePassDelegates[PassIndex].Add(DeferredWrite);
}