175 lines
6.0 KiB
HLSL
175 lines
6.0 KiB
HLSL
// Copyright Epic Games, Inc. All Rights Reserved.
|
|
|
|
#include "../Common.ush"
|
|
#include "../SceneData.ush"
|
|
#include "../ViewData.ush"
|
|
#include "../ComputeShaderUtils.ush"
|
|
#include "../WaveOpUtil.ush"
|
|
#include "../SceneCulling/SceneCulling.ush"
|
|
|
|
#define NANITE_MULTI_VIEW 1
|
|
|
|
#include "NaniteDataDecode.ush"
|
|
#include "NaniteSceneCommon.ush"
|
|
|
|
float DefaultAnimationMinScreenSize;
|
|
|
|
RWStructuredBuffer<uint2> OutInstanceWorkGroups;
|
|
RWBuffer<uint> OutInstanceWorkArgs;
|
|
|
|
void EmitChunk(uint ChunkId, uint ViewMask)
|
|
{
|
|
uint ChunkOutOffset = 0;
|
|
WaveInterlockedAddScalar_(OutInstanceWorkArgs[0u], 1u, ChunkOutOffset);
|
|
OutInstanceWorkGroups[ChunkOutOffset] = uint2(ChunkId, ViewMask);
|
|
}
|
|
|
|
[numthreads(THREAD_GROUP_SIZE, 1, 1)]
|
|
void NaniteSkinningUpdateChunkCullCS(uint ChunkId : SV_DispatchThreadID)
|
|
{
|
|
// exit if out of range
|
|
if (ChunkId >= NumAllocatedChunks)
|
|
{
|
|
return;
|
|
}
|
|
|
|
// early out if chunk not allocated / used by any cell
|
|
if (!IsChunkUsed(ChunkId))
|
|
{
|
|
return;
|
|
}
|
|
FPackedChunkAttributes PackedChunkAttributes = LoadPackedExplicitChunkBounds(ChunkId);
|
|
|
|
|
|
uint CellId = ExplicitChunkCellIds[ChunkId];
|
|
FSceneHiearchyCellData CellData = GetSceneHiearchyCellData(CellId);
|
|
|
|
const float LevelCellSize = CellData.BlockData.LevelCellSize;
|
|
FInstanceChunkAttributes ChunkAttributes = UnpackInstanceChunkAttributes(PackedChunkAttributes, LevelCellSize * 2.0f, LevelCellSize * 0.5f);
|
|
|
|
// None are skinned
|
|
if ((ChunkAttributes.AnyFlags & PCAF_ANY_SKINNED_MESH) == 0u)
|
|
{
|
|
return;
|
|
}
|
|
FAabb CellRelativeBounds = ChunkAttributes.Aabb;
|
|
float3 ImplicitBoundsMin = float3(CellData.LocalCellCoord) * LevelCellSize - (LevelCellSize * 0.5f).xxx;
|
|
float3 LocalBoundsCenter = (CellRelativeBounds.Min + CellRelativeBounds.Max) * 0.5f + ImplicitBoundsMin;
|
|
float3 LocalBoundsExtent = (CellRelativeBounds.Max - CellRelativeBounds.Min) * 0.5f;
|
|
float MinAnimationMinScreenSize = ChunkAttributes.MinAnimationMinScreenSize;
|
|
bool bHasDrawDistance = MinAnimationMinScreenSize > 0.0f;
|
|
float CellRadius = length(LocalBoundsExtent);
|
|
|
|
// Infinity, emit the chunk
|
|
if (ChunkAttributes.MinAnimationMinScreenSize < 0.0f)
|
|
{
|
|
// infinite range, mark all views
|
|
EmitChunk(ChunkId, (1u << NumSceneRendererPrimaryViews) - 1u);
|
|
return;
|
|
}
|
|
|
|
float AnimationMinScreenSize = ChunkAttributes.MinAnimationMinScreenSize != 0.0f ? ChunkAttributes.MinAnimationMinScreenSize : DefaultAnimationMinScreenSize;
|
|
|
|
|
|
uint ViewMask = 0u;
|
|
for (uint ViewIndex = 0u; ViewIndex < NumSceneRendererPrimaryViews; ++ViewIndex)
|
|
{
|
|
FNaniteView NaniteView = GetNaniteView(ViewIndex);
|
|
float4x4 LocalToTranslatedWorld = MakeTranslationMatrix(DFFastToTranslatedWorld(CellData.BlockData.WorldPos, NaniteView.PreViewTranslation));
|
|
const float3 CenterTranslatedWorld = mul( float4( LocalBoundsCenter, 1.0f ), LocalToTranslatedWorld ).xyz;
|
|
|
|
// Adjust for cell radius
|
|
float DrawDist = max(0.0f, length(CenterTranslatedWorld - NaniteView.CullingViewOriginTranslatedWorld) - CellRadius);
|
|
|
|
//if (NaniteView.CullingViewScreenMultipleSq * Square(ChunkAttributes.MaxInstanceRadius) >= Square(GlobalConservativeAnimationMinScreenSize) * Square(DrawDist))
|
|
if (NaniteView.CullingViewScreenMultipleSq * Square(CellData.MaxInstanceRadius) >= Square(AnimationMinScreenSize) * Square(DrawDist))
|
|
{
|
|
ViewMask |= 1u << ViewIndex;
|
|
}
|
|
}
|
|
|
|
if (ViewMask != 0u)
|
|
{
|
|
EmitChunk(ChunkId, ViewMask);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
StructuredBuffer<uint2> InstanceWorkGroups;
|
|
|
|
[numthreads(THREAD_GROUP_SIZE, 1, 1)]
|
|
void NaniteSkinningUpdateViewDataCS(uint3 GroupId : SV_GroupID, uint GroupThreadIndex : SV_GroupIndex)
|
|
{
|
|
uint ChunkId = InstanceWorkGroups[GroupId.x].x;
|
|
uint ChunkViewMask = InstanceWorkGroups[GroupId.x].y;
|
|
uint PackedItemChunkDesc = InstanceHierarchyItemChunks[ChunkId];
|
|
|
|
bool bIsRLEPackedChunk = false;
|
|
uint GroupNumInstances = UnpackChunkInstanceCount(PackedItemChunkDesc, bIsRLEPackedChunk);
|
|
if (GroupThreadIndex >= GroupNumInstances)
|
|
{
|
|
return;
|
|
}
|
|
uint InstanceId = UnpackChunkInstanceId(bIsRLEPackedChunk, PackedItemChunkDesc, GroupThreadIndex);
|
|
FInstanceSceneData InstanceSceneData = GetInstanceSceneData(InstanceId);
|
|
|
|
if (!InstanceSceneData.ValidInstance)
|
|
{
|
|
return;
|
|
}
|
|
if ((InstanceSceneData.Flags & INSTANCE_SCENE_DATA_FLAG_HIDDEN) != 0u)
|
|
{
|
|
return;
|
|
}
|
|
if (InstanceSceneData.NaniteRuntimeResourceID == 0xFFFFFFFFu)
|
|
{
|
|
// Only process valid Nanite instances
|
|
return;
|
|
}
|
|
FPrimitiveSceneData PrimitiveData = GetPrimitiveData(InstanceSceneData.PrimitiveId);
|
|
if ((PrimitiveData.Flags & PRIMITIVE_SCENE_DATA_FLAG_SKINNED_MESH) == 0u)
|
|
{
|
|
return;
|
|
}
|
|
|
|
// TODO: figure out a neat way to achieve this!
|
|
// Force the inclusion of the SceneUbExNaniteSkinning UB through magic*!
|
|
// SceneUbExNaniteSkinning.Whatever
|
|
// * The magic is that the shader compiler in BuildShaderFileToUniformBufferMap looks at the verbatim source file to figure out what UBs to inject, and this happens before macros are expanded or comments stripped.
|
|
|
|
if (PrimitiveData.AnimationMinScreenSize < 0.0f)
|
|
{
|
|
// infinite range, mark all views
|
|
MarkInstanceAsDeformingWithViewMask(InstanceId, (1u << NumSceneRendererPrimaryViews) - 1u);
|
|
return;
|
|
}
|
|
|
|
float AnimationMinScreenSize = PrimitiveData.AnimationMinScreenSize != 0.0f
|
|
? PrimitiveData.AnimationMinScreenSize
|
|
: DefaultAnimationMinScreenSize;
|
|
|
|
const float RadiusSq = length2( InstanceSceneData.LocalBoundsExtent * InstanceSceneData.NonUniformScale.xyz );
|
|
|
|
uint ViewMask = 0u;
|
|
for (uint ViewIndex = 0u; ViewIndex < NumSceneRendererPrimaryViews; ++ViewIndex)
|
|
{
|
|
uint CurrentViewMask = 1u << ViewIndex;
|
|
if ((ChunkViewMask & CurrentViewMask) == 0u)
|
|
{
|
|
continue;
|
|
}
|
|
FNaniteView NaniteView = GetNaniteView(ViewIndex);
|
|
FInstanceDynamicData DynamicData = CalculateInstanceDynamicData(NaniteView, InstanceSceneData);
|
|
|
|
const float3 CenterTranslatedWorld = mul( float4( InstanceSceneData.LocalBoundsCenter, 1.0f ), DynamicData.LocalToTranslatedWorld ).xyz;
|
|
float InstanceDrawDistSq = length2(CenterTranslatedWorld - NaniteView.CullingViewOriginTranslatedWorld);
|
|
|
|
if (NaniteView.CullingViewScreenMultipleSq * RadiusSq >= Square(AnimationMinScreenSize) * InstanceDrawDistSq)
|
|
{
|
|
ViewMask |= CurrentViewMask;
|
|
}
|
|
}
|
|
MarkInstanceAsDeformingWithViewMask(InstanceId, ViewMask);
|
|
}
|