Files
UnrealEngine/Engine/Plugins/PCG/Shaders/Private/BuiltInKernels/PCGCountUniqueAttributeValues.usf
Brandyn / Techy fcc1b09210 init
2026-04-04 15:40:51 -05:00

99 lines
3.6 KiB
HLSL

// Copyright Epic Games, Inc. All Rights Reserved.
// Counts how many times each unique value of an attribute is encountered in the input data elements.
#define PCG_OUTPUT_TO_RAW_BUFFER {RawDataOutputEnabled}
// Input: Input elements with attribute.
// Output (PCG_OUTPUT_TO_RAW_BUFFER): Uint array consisting of (string key value, value instance count) pairs. Number of pairs is the number of unique values (known CPU-side).
// Output (!PCG_OUTPUT_TO_RAW_BUFFER): Attribute set with a ValueCount attribute which has the count of each value. Number of elements is the number of unique values (known CPU-side).
void InitializeValueKey(uint InDataIndex, uint InValueKey)
{
#if PCG_OUTPUT_TO_RAW_BUFFER
const uint FirstElementIndex = InValueKey * 2u;
if (FirstElementIndex < Out_GetNumElements(0))
{
// We currently have an output element per possible string key value. This may be compacted in the future.
Out_Store(InDataIndex, FirstElementIndex, InValueKey);
}
#else
// Write value of attribute. We currently have an output element per possible string key value.
const int OutputValueAttributeId = CountUniqueValues_GetOutputValueAttributeId();
Out_SetInt(InDataIndex, InValueKey, OutputValueAttributeId, InValueKey);
#endif
}
[numthreads(64, 1, 1)]
void PCGCountUniqueAttributeValuesCS(uint3 GroupId : SV_GroupID, uint GroupIndex : SV_GroupIndex)
{
#if !PCG_OUTPUT_TO_RAW_BUFFER
// Mark the kernel as having executed. Must run before we early out via thread index, because the kernel is still 'executed' even if the number of
// threads to iterate on is zero. Even if GetNumThreads() returns 0, the kernel will still have been dispatched on a single thread to set this flag.
if (all(GroupId == 0) && GroupIndex == 0)
{
Out_SetAsExecutedInternal();
}
#endif
const uint ThreadIndex = GetUnWrappedDispatchThreadId(GroupId, GroupIndex, 64);
// Pre-job - if this thread maps to any element in the output, initialize the value key. The dispatch thread count will be large enough to ensure
// all output elements are initialized.
{
#if PCG_OUTPUT_TO_RAW_BUFFER
// If outputting to raw buffer, always single data.
InitializeValueKey(/*DataIndex=*/0, ThreadIndex);
#else
uint Out_DataIndex;
uint Out_ElementIndex;
if (Out_GetThreadData(ThreadIndex, Out_DataIndex, Out_ElementIndex))
{
InitializeValueKey(Out_DataIndex, Out_ElementIndex);
}
#endif
}
// Main job - one thread per input data element. Read the string key value and atomic increment the output counter.
{
uint In_DataIndex, In_ElementIndex;
if (!In_GetThreadData(ThreadIndex, In_DataIndex, In_ElementIndex))
{
return;
}
if (In_IsPointRemoved(In_DataIndex, In_ElementIndex))
{
return;
}
const int AttributeToCountId = CountUniqueValues_GetAttributeToCountId();
const int AttributeValue = In_GetStringKey(In_DataIndex, In_ElementIndex, AttributeToCountId);
if (AttributeValue < 0)
{
// Should not happen (0 is null/empty string, negative is invalid).
return;
}
// If not outputting per-data counts, then all counters are aggregated on a single data.
const uint Out_DataIndex = CountUniqueValues_GetEmitPerDataCounts() ? In_DataIndex : 0;
#if PCG_OUTPUT_TO_RAW_BUFFER
const uint ElementIndex = AttributeValue * 2u + 1;
Out_AtomicAdd(/*DataIndex=*/0, ElementIndex, 1u);
#else
// Always one bucket per attribute value.
const uint Out_ElementIndex = AttributeValue;
const int OutputCountAttributeId = CountUniqueValues_GetOutputCountAttributeId();
// Increment count of this attribute.
Out_AtomicAddInt(Out_DataIndex, Out_ElementIndex, OutputCountAttributeId, 1);
#endif
}
}
#undef PCG_OUTPUT_TO_RAW_BUFFER