Bug in DistanceField BuildGridTilesCS

Hi.

In GlobalDistanceField.cpp, function ComputeUpdateRegionsAndUpdateViewState(), the modified bounds get collected:

Clipmap.UpdateBounds.Empty(PrimitiveModifiedBounds.Num() / 2);

for (int32 BoundsIndex = 0; BoundsIndex < PrimitiveModifiedBounds.Num(); BoundsIndex++)
{
	const FBox ModifiedBounds = PrimitiveModifiedBounds[BoundsIndex];

	if (ModifiedBounds.ComputeSquaredDistanceToBox(ClipmapBounds) < ClipmapInfluenceRadius * ClipmapInfluenceRadius)
	{
		++NumCulledPrimitiveModifiedBounds;
		Clipmap.UpdateBounds.Add(FClipmapUpdateBounds(ModifiedBounds.GetCenter(), ModifiedBounds.GetExtent(), true));
...

Then shortly below, it checks if there are too many bounds and should not do a partial update. Notice the limit being 1023 bounds.

// Only use partial updates with small numbers of primitive modifications
bool bUsePartialUpdatesForUpdateBounds = bUsePartialUpdates && NumCulledPrimitiveModifiedBounds < 1024;

Then the bounds buffer gets uploaded and PackedClipmap.NumUpdateBounds is updated with the number of bounds:

FRDGUploadData<FVector4f> UpdateBoundsData(GraphBuilder, BufferStrideInFloat4 * Clipmap.UpdateBounds.Num());

for (int32 UpdateBoundsIndex = 0; UpdateBoundsIndex < Clipmap.UpdateBounds.Num(); ++UpdateBoundsIndex)
{
	const FClipmapUpdateBounds& UpdateBounds = Clipmap.UpdateBounds[UpdateBoundsIndex];

	UpdateBoundsData[PackedClipmap.NumUpdateBounds * BufferStrideInFloat4 + 0] = FVector4f((FVector3f)(UpdateBounds.Center + PreViewTranslation), UpdateBounds.bExpandByInfluenceRadius ? 1.0f : 0.0f);
	UpdateBoundsData[PackedClipmap.NumUpdateBounds * BufferStrideInFloat4 + 1] = FVector4f((FVector3f)UpdateBounds.Extent, 0.0f);
	++PackedClipmap.NumUpdateBounds;
}

When setting up the FBuildGridTilesCS parameters, the NumUpdateBounds is sent into the shader:

PassParameters->NumUpdateBounds = PackedClipmap.NumUpdateBounds;

Now, the shader itself in GlobalDistanceField.usf, has the following:

#define MAX_CULLED_TILES 511u
groupshared uint SharedCulledBoundsList[MAX_CULLED_TILES];

And then when it does the Grid culling itself:

if (DistanceSq <= UpdateBoundsInfluenceRadiusSq)
{
	uint DestIndex;
	InterlockedAdd(SharedNumCulledBounds, 1, DestIndex);
	if (DestIndex < MAX_CULLED_TILES)
	{
		SharedCulledBoundsList[DestIndex] = UpdateBoundsIndex;
	}
}

Notice that we can send up to 1023 bounds for a partial update on a clipmap, but if more than 511 survive grid culling, they will be silently dropped and not processed at all.

Doesn’t the clamping done with NumCulledPrimitiveModifiedBounds in C++ should be in sync with the value of MAX_CULLED_TILES in the shader (ie: worst case)?

Ernesto.

[Attachment Removed]

Hi Ernesto,

Yeah this can indeed be an issue.

Changing MAX_CULLED_TILES to be the same as 1023 bounds limit can end up being too conservative since 1023 is a global limit, while MAX_CULLED_TILES is the per tile limit.

One possible solution to handle this is to modify the shader to do multiple passes (eg: uint NumPasses = (NumUpdateBounds + MAX_CULLED_TILES - 1) / MAX_CULLED_TILES;) similar to what we do in CompositeObjectsIntoPagesCS.

I’ll look into making this change for 5.8.

Cheers,

Tiago

[Attachment Removed]