Files
UnrealEngine/Engine/Shaders/Private/GPUFeedbackCompaction.usf
2025-05-18 13:04:45 +08:00

188 lines
4.5 KiB
HLSL

// Copyright Epic Games, Inc. All Rights Reserved.
#include "Common.ush"
#include "Hash.ush"
#if FEEDBACK_BUFFER_STRIDE == 1
#define FeedbackType uint
#elif FEEDBACK_BUFFER_STRIDE == 2
#define FeedbackType uint2
#endif
// Output compacted buffer
RWStructuredBuffer<uint2> RWCompactedFeedbackBuffer;
uint CompactedFeedbackBufferSize;
uint CompactedFeedbackCountShiftBits;
// Input feedback buffer and allocation size count
StructuredBuffer<uint> FeedbackBufferAllocator;
StructuredBuffer<FeedbackType> FeedbackBuffer;
uint FeedbackBufferSize;
// Indirect args
RWBuffer<uint> RWBuildHashTableIndirectArgs;
// Feedback element hashes
RWStructuredBuffer<uint> RWHashTableKeys;
// Deduplicated feedback elements
RWStructuredBuffer<uint> RWHashTableElementIndices;
StructuredBuffer<uint> HashTableElementIndices;
RWStructuredBuffer<uint> RWHashTableElementCounts;
StructuredBuffer<uint> HashTableElementCounts;
uint HashTableSize;
uint HashTableIndexWrapMask;
/**
* Setups FBuildFeedbackHashTableCS arguments to run one lane per feedback element
*/
[numthreads(1, 1, 1)]
void BuildFeedbackHashTableIndirectArgsCS(
uint3 GroupId : SV_GroupID,
uint3 DispatchThreadId : SV_DispatchThreadID,
uint3 GroupThreadId : SV_GroupThreadID)
{
if (DispatchThreadId.x == 0)
{
WriteDispatchIndirectArgs(RWBuildHashTableIndirectArgs, 0, DivideAndRoundUp64(min(FeedbackBufferAllocator[0], FeedbackBufferSize)), 1, 1);
}
}
/**
* Hash key generation functions for each FeedbackType
*/
uint GetHashKey(uint PackedFeedback)
{
return MurmurMix(PackedFeedback);
}
uint GetHashKey(uint2 PackedFeedback)
{
return MurmurAdd(MurmurMix(PackedFeedback.x), PackedFeedback.y);
}
/**
* Copy input feedback item to output for each FeedbackType
*/
uint2 ExpandInputType(uint PackedFeedback)
{
return uint2(PackedFeedback, 0);
}
uint2 ExpandInputType(uint2 PackedFeedback)
{
return PackedFeedback;
}
/**
* Takes input Key, which is a hash and tries to add it to the linear probing hash table
* Returns true if element was added for the first time
* Returns Index which is index to the hash table bucket
*/
bool HashTableAdd(uint Key, inout uint Index)
{
// Just skip add if search gets too long
const uint MaxLinearProbingSteps = 32;
LOOP
for (uint LinearProbingStep = 0; LinearProbingStep < MaxLinearProbingSteps; ++LinearProbingStep)
{
Index = (Key + LinearProbingStep) & HashTableIndexWrapMask;
uint StoredKey = RWHashTableKeys[Index];
if (StoredKey != Key)
{
if (StoredKey != 0)
{
continue;
}
uint PrevKey;
InterlockedCompareExchange(RWHashTableKeys[Index], 0, Key, PrevKey);
if (PrevKey == 0)
{
RWHashTableKeys[Index] = Key;
return true;
}
else if (PrevKey != Key)
{
continue;
}
}
break;
}
return false;
}
/**
* Takes a list of feedback elements and builds a hash table with element counts
*/
[numthreads(THREADGROUP_SIZE, 1, 1)]
void BuildFeedbackHashTableCS(
uint3 GroupId : SV_GroupID,
uint3 DispatchThreadId : SV_DispatchThreadID,
uint3 GroupThreadId : SV_GroupThreadID)
{
uint ElementIndex = DispatchThreadId.x;
if (ElementIndex < FeedbackBufferAllocator[0])
{
FeedbackType PackedFeedback = FeedbackBuffer[ElementIndex];
uint Key = GetHashKey(PackedFeedback);
uint HashTableIndex = 0;
const bool bAdded = HashTableAdd(Key, HashTableIndex);
if (bAdded)
{
RWHashTableElementIndices[HashTableIndex] = ElementIndex;
}
InterlockedAdd(RWHashTableElementCounts[HashTableIndex], 1);
}
}
/**
* Compacts feedback element hash table into a unique and tightly packed array of feedback elements with counts
*/
[numthreads(THREADGROUP_SIZE, 1, 1)]
void CompactFeedbackHashTableCS(
uint3 GroupId : SV_GroupID,
uint3 DispatchThreadId : SV_DispatchThreadID,
uint3 GroupThreadId : SV_GroupThreadID)
{
uint HashTableIndex = DispatchThreadId.x;
if (HashTableIndex < HashTableSize)
{
const uint ElementCount = HashTableElementCounts[HashTableIndex];
if (ElementCount > 0)
{
uint WriteOffset = 0;
InterlockedAdd(RWCompactedFeedbackBuffer[0].x, 1, WriteOffset);
// Skip header element
WriteOffset += 1;
if (WriteOffset < CompactedFeedbackBufferSize)
{
uint ElementIndex = HashTableElementIndices[HashTableIndex];
if (ElementIndex < FeedbackBufferSize)
{
uint2 PackedFeedback = ExpandInputType(FeedbackBuffer[ElementIndex]);
// Store element count in compacted .y
PackedFeedback.y |= (ElementCount << CompactedFeedbackCountShiftBits);
RWCompactedFeedbackBuffer[WriteOffset] = PackedFeedback;
}
}
}
}
}