Okay so, update time. Here’s the latest iteration. I’m still following the line route with Slate because going down the mesh route is a going to be a very hard process. This is without any culling still for the radar ‘range’, so every point is still submitted to be drawn and in this test case, there’s a total of 4,225 points in this test level with 65 rows and 65 columns - so still not a lot of points in the grand scheme of things.
Here’s the code now translated into C++. There are a few issues, for one thing NativePaint() is const, so I can’t create a member variable TArray<FVector2D> and just change the values (this just in, apparently I can make a ‘mutable’ member variable which will allow the changes). - so atm it creates and allocates the array many times per-frame. So, changes so far:
- Moved Paint function to C++
- Changed all Get functions for the Vertex Buffer to be FORCEINLINE
- Overwrote DrawLines() and created new inline version, which doesn’t call InContext.MaxLayer++ each time it’s called
- Turned off Anti-Aliasing
I tried to go into FSlateDrawElements::MakeLines() to actually copy the code that’s there to prevent function call overhead, but unfortunately ‘Init’ is a private member of that class for some reason (annoying) - so I cannae do that. Definitely room for speed improvements here.
BZGame_TopoRadar.h
UCLASS()
class BZGAME_API UBZGame_TopoRadar : public UBZGame_BaseWidget
{
GENERATED_BODY()
public:
UBZGame_TopoRadar(const FObjectInitializer& ObjectInitializer);
virtual void NativeConstruct() override;
virtual void NativePaint(FPaintContext& InContext) const override;
protected:
int32 NumRows, NumColumns;
// Can't modify because OnPaint() is const :(
// TArray<FVector2D> DrawPoints;
//
// FORCEINLINE void AddItemToPoints(const FVector2D& Point)
// {
// DrawPoints.Add(Point);
// }
FORCEINLINE void Fast_DrawGrid(FPaintContext& InContext, const TArray<FVector2D>& InPoints) const
{
// Ideally want to be able to do whatever this does right here, but lots of FSlateDrawElement is private -.-
FSlateDrawElement::MakeLines(
InContext.OutDrawElements,
InContext.MaxLayer,
InContext.AllottedGeometry.ToPaintGeometry(),
InPoints,
InContext.MyClippingRect,
ESlateDrawEffect::None,
FLinearColor::Blue,
false);
}
// We want to do this, but Init() is private :(
// FPaintGeometry PaintGeo = InContext.AllottedGeometry.ToPaintGeometry();
// PaintGeo.CommitTransformsIfUsingLegacyConstructor();
// FSlateDrawElement& DrawElt = InContext.OutDrawElements.AddUninitialized();
// DrawElt.Init(0, PaintGeo, InContext.MyClippingRect, ESlateDrawEffect::None);
// DrawElt.ElementType = EElementType::ET_Line;
// DrawElt.DataPayload.SetLinesPayloadProperties(DrawPoints, FLinearColor::Blue, false, ESlateLineJoinType::Sharp);
//}
};
BZGame_TopoRadar.cpp
DECLARE_STATS_GROUP(TEXT("Radar"), STATGROUP_Radar, STATCAT_Advanced);
DECLARE_CYCLE_STAT(TEXT("BZ ~ Draw Radar Grid"), STAT_DrawRadar, STATGROUP_Radar);
void UBZGame_TopoRadar::NativeConstruct()
{
Super::NativeConstruct();
if (OwningBZHud) { OwningBZHud->SetRadarWidget(this); }
// Cached Rows/Columns
// Get the Game Instance. Again, can probably be cached or at least inlined
UBZGame_GameInstance* BZGI = UBZGame_GameInstance::GetInstance(this);
ASSERTV(BZGI != nullptr, TEXT("BZGame Instance Is Nullptr"));
BZGI->GetRadarGridSize(NumRows, NumColumns);
}
void UBZGame_TopoRadar::NativePaint(FPaintContext& InContext) const
{
Super::NativePaint(InContext);
SCOPE_CYCLE_COUNTER(STAT_DrawRadar);
// Get the Game Instance. Again, can probably be cached or at least inlined
UBZGame_GameInstance* BZGI = UBZGame_GameInstance::GetInstance(GetWorld());
ASSERTV(BZGI != nullptr, TEXT("BZGame Instance Is Nullptr"));
TArray<FVector2D> DPArray;
// Just submit all the points for now. We need to do some 'clipping' here to only get points in radar-range of the player.
for (int32 CIdx = 0; CIdx < NumColumns; CIdx++)
{
for (int32 RIdx = 0; RIdx < NumRows; RIdx++)
{
const FVector WorldPos = BZGI->Fast_GetRadarPosAtGrid(CIdx, RIdx);
const FVector2D ScreenP = ABZGame_InGameHUD::WorldToTopographicalRadar(WorldPos);
DPArray.Add(ScreenP);
}
Fast_DrawGrid(InContext, DPArray);
DPArray.Empty();
}
for (int32 RIdx = 0; RIdx < NumRows; RIdx++)
{
for (int32 CIdx = 0; CIdx < NumColumns; CIdx++)
{
const FVector WorldPos = BZGI->Fast_GetRadarPosAtGrid(CIdx, RIdx);
FVector2D ScreenP = ABZGame_InGameHUD::WorldToTopographicalRadar(WorldPos);
DPArray.Add(ScreenP);
}
Fast_DrawGrid(InContext, DPArray);
DPArray.Empty();
}
}
Another thing I’m doing is getting and transforming all of the points twice… If I can figure out how to draw the grid without that, that’ll save some performance too, and cut the transform cost in half.