Files
UnrealEngine/Engine/Shaders/Private/Nanite/NaniteSkinningUpdateViewData.usf
Brandyn / Techy fcc1b09210 init
2026-04-04 15:40:51 -05:00

178 lines
5.9 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);
}
/**
* We do this instead of relying on FNaniteView::CullingViewScreenMultipleSq because that has a number of other factors based into it, some which make no sense for Nanite.
* This should ideally be cleaned up in the future.
*/
float GetScreenMultipleSquared(FNaniteView NaniteView)
{
return Square(max(NaniteView.ViewToClip[0][0], NaniteView.ViewToClip[1][1]));
}
[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 (GetScreenMultipleSquared(NaniteView) * 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;
}
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 (GetScreenMultipleSquared(NaniteView) * RadiusSq >= Square(AnimationMinScreenSize) * InstanceDrawDistSq)
{
ViewMask |= CurrentViewMask;
}
}
MarkInstanceAsDeformingWithViewMask(InstanceId, ViewMask);
}