Sequencer rendering of little white bars is insanely slow

Hello!

I happened to notice that the SSequencerObjectTrack performs really bad once tracks get reasonably complex.
This seems to be due to the CollectAllKeyTimes method of it which has 4 for loops wrapped into each other and is executed every frame.

The frame time in some of these instances is over 100ms which is not acceptable. I have to speed this up by a factor of 4 with quick and dirty changes.

void SSequencerObjectTrack::CollectAllKeyTimes(TArray<float>& OutKeyTimes) const
{
	TArray<TSharedRef<FSequencerSectionKeyAreaNode>> OutNodes;
	RootNode->GetChildKeyAreaNodesRecursively(OutNodes);

    for (int32 i = 0; i < OutNodes.Num(); ++i)
	{
		TArray< TSharedRef<IKeyArea> > KeyAreas = OutNodes[i]->GetAllKeyAreas();
		for (int32 j = 0; j < KeyAreas.Num(); ++j)
		{
			TArray<FKeyHandle> KeyHandles = KeyAreas[j]->GetUnsortedKeyHandles();
			for (int32 k = 0; k < KeyHandles.Num(); ++k)
			{
				AddKeyTime(KeyAreas[j]->GetKeyTime(KeyHandles[k]), OutKeyTimes);
			}
		}
	}
}


void SSequencerObjectTrack::AddKeyTime(const float& NewTime, TArray<float>& OutKeyTimes) const
{
	// @todo Sequencer It might be more efficient to add each key and do the pruning at the end
	for (float& KeyTime : OutKeyTimes)
	{
		if (FMath::IsNearlyEqual(KeyTime, NewTime))
		{
			return;
		}
	}

	OutKeyTimes.Add(NewTime);
}

Improved version:

int32 SSequencerObjectTrack::OnPaint(const FPaintArgs& Args, const FGeometry& AllottedGeometry, const FSlateRect& MyClippingRect, FSlateWindowElementList& OutDrawElements, int32 LayerId, const FWidgetStyle& InWidgetStyle, bool bParentEnabled) const
{
	if (RootNode->GetSequencer().GetSettings()->GetShowCombinedKeyframes())
	{
		FTimeToPixel TimeToPixelConverter(AllottedGeometry, ViewRange.Get());
		TArray<float> OutKeyTimes;
		CollectAllKeyTimes(OutKeyTimes, FVector2D(ViewRange.Get().GetLowerBoundValue(), ViewRange.Get().GetUpperBoundValue()));
		OutKeyTimes.Sort();

		for (int32 i = 0; i < OutKeyTimes.Num(); ++i)
		{
			if (i > 0&& FMath::IsNearlyEqual(OutKeyTimes[i], OutKeyTimes[i - 1]))
			{
				continue;
			}
			float KeyPosition = TimeToPixelConverter.TimeToPixel(OutKeyTimes[i]);
			static const FVector2D KeyMarkSize = FVector2D(3.f, 21.f);

			FSlateDrawElement::MakeBox(
				OutDrawElements,
				LayerId+1,
				AllottedGeometry.ToPaintGeometry(FVector2D(KeyPosition - FMath::CeilToFloat(KeyMarkSize.X/2.f), FMath::CeilToFloat(AllottedGeometry.Size.Y/2.f - KeyMarkSize.Y/2.f)), KeyMarkSize),
				FEditorStyle::GetBrush("Sequencer.KeyMark"),
				MyClippingRect,
				ESlateDrawEffect::None,
				FLinearColor(1.f, 1.f, 1.f, 1.f)
			);
		}
		return LayerId+1;
	}

	return LayerId;
}
void SSequencerObjectTrack::CollectAllKeyTimes(TArray<float>& OutKeyTimes, FVector2D Range) const
{
	TArray<TSharedRef<FSequencerSectionKeyAreaNode>> OutNodes;
	RootNode->GetChildKeyAreaNodesRecursively(OutNodes);

	uint32 Num = 0;
	for (int32 i = 0; i < OutNodes.Num(); ++i)
	{
		auto& KeyAreas = OutNodes[i]->GetAllKeyAreas();
		for (int32 j = 0; j < KeyAreas.Num(); ++j)
		{
			Num += KeyAreas[j]->GetNumberOfKeyHandles();
		}
	}
	OutKeyTimes.SetNumUninitialized(Num);
	Num = 0;
	for (int32 i = 0; i < OutNodes.Num(); ++i)
	{
		auto& KeyAreas = OutNodes[i]->GetAllKeyAreas();
		for (int32 j = 0; j < KeyAreas.Num(); ++j)
		{
			TArray<FKeyHandle> KeyHandles = KeyAreas[j]->GetUnsortedKeyHandles();
			for (int32 k = 0; k < KeyHandles.Num(); ++k)
			{
				float Time = KeyAreas[j]->GetKeyTime(KeyHandles[k]);
				if (Time >= Range.X && Time < Range.Y)
				{
					OutKeyTimes[Num++] = Time;
				}
			}
		}
	}
	OutKeyTimes.SetNum(Num,false);
}

This also applies to the red keyframe dots as well by the way :slight_smile:

Thank you

Jannes