Slate crash when exiting PIE (SListView and GC)

Hi,

When I close my game in PIE while a SListView is visible on screen., the editor freezes for a second or two then crashes.

UE4Editor-CoreUObject.dll!UObjectBase::IsValidLowLevelFast(bool bRecursive) Line 306	C++
UE4Editor-CoreUObject.dll!UObjectBase::IsValidLowLevelFast(bool bRecursive) Line 320	C++
UE4Editor-CoreUObject.dll!FGCCollector::HandleObjectReference(UObject * & Object, const UObject * ReferencingObject, const UObject * ReferencingProperty) Line 268	C++
UE4Editor-Flare.dll!TListTypeTraits<IFlareShipInterface * __ptr64>::AddReferencedObjects(FReferenceCollector & Collector, TArray<IFlareShipInterface *,FDefaultAllocator> & ItemsWithGeneratedWidgets, TSet<IFlareShipInterface *,DefaultKeyFuncs<IFlareShipInterface *,0>,FDefaultSetAllocator> & SelectedItems) Line 214	C++
UE4Editor-CoreUObject.dll!UGCObjectReferencer::AddReferencedObjects(UObject * InThis, FReferenceCollector & Collector) Line 17	C++
UE4Editor-CoreUObject.dll!FArchiveRealtimeGC::ProcessObjectArray(TArray<UObject *,FDefaultAllocator> & InObjectsToSerializeArray, TRefCountPtr<FGraphEvent> & MyCompletionGraphEvent) Line 706	C++
UE4Editor-CoreUObject.dll!TGraphTask<FArchiveRealtimeGC::FGCTask>::ExecuteTask(TArray<FBaseGraphTask *,FDefaultAllocator> & NewTasks, ENamedThreads::Type CurrentThread) Line 671	C++
UE4Editor-Core.dll!FTaskThread::ProcessTasks(int QueueIndex, bool bAllowStall) Line 428	C++
UE4Editor-Core.dll!FTaskThread::ProcessTasksUntilQuit(int QueueIndex) Line 271	C++
UE4Editor-Core.dll!FTaskThread::Run() Line 562	C++
UE4Editor-Core.dll!FRunnableThreadWin::Run() Line 73	C++
UE4Editor-Core.dll!FRunnableThreadWin::GuardedRun() Line 48	C++
[External Code]	

Here is the code.

// Ship list
TSharedPtr< SListView<IFlareShipInterface*> >   ShipList;
TArray<IFlareShipInterface*>                    ShipListData;

// Create the list
SAssignNew(ShipList, SListView<IFlareShipInterface*>)
.ListItemsSource(&ShipListData)
.SelectionMode(ESelectionMode::Single)
.OnGenerateRow(this, &SFlareSectorMenu::GenerateShipInfo)

// Fill the list
ShipListData.AddUnique(GetShipPawn());
ShipList->RequestListRefresh();

// Display callback
TSharedRef<ITableRow> SFlareSectorMenu::GenerateShipInfo(IFlareShipInterface* Item, const TSharedRef<STableViewBase>& OwnerTable)
{
	AFlarePlayerController* PC = Cast<AFlarePlayerController>(OwnerHUD->GetOwner());

	return SNew(STableRow<IFlareShipInterface*>, OwnerTable)
		.Content()
		[
			SNew(STextBlock).Text(FText::FromString("Test"))
		];
}

I believe something is wrong at low level with this code. The list itself is fine, it works and shows up as expected.

Thanks for helping !

I don’t understand. It says “SListView< UObject* > are valid” which is exactly what I’m doing. It also says that “SListView< FString* >” is not valid, but that’s what the sample code does.

Since I’m holding a TArray of UObjects (specifically AActor) and TArray keeps the object from being GC, why does this not work ?

Hi,

taken from SListView.h

/**
 * A ListView widget observes an array of data items and creates visual representations of these items.
 * ListView relies on the property that holding a reference to a value ensures its existence. In other words,
 * neither SListView<FString> nor SListView<FString*> are valid, while SListView< TSharedPtr<FString> > and
 * SListView< UObject* > are valid.
 *
 * A trivial use case appear below:
 *
 *   Given: TArray< FString* > Items;
 *
 *   SNew( SListView< FString* > )
 *     .ItemHeight(24)
 *     .ListItemsSource( &Items )
 *     .OnGenerateRow( SListView< TSharedPtr<FString> >::MakeOnGenerateWidget( this, &MyClass::OnGenerateRowForList ) )
 *
 * In the example we make all our widgets be 24 screen units tall. The ListView will create widgets based on data items
 * in the Items TArray. When the ListView needs to generate an item, it will do so using the OnGenerateWidgetForList method.
 *
 * A sample implementation of OnGenerateWidgetForList would simply return a TextBlock with the corresponding text:
 *
 * TSharedRef<ITableRow> OnGenerateWidgetForList( FString* InItem, const TSharedRef<STableViewBase>& OwnerTable )
 * {
 *     return SNew(STextBlock).Text( (*InItem) )
 * }
 *
 */

So you must use TSharedPtr as list item or use different OnGenerateRow method.

Cheers

is IFlareShipInterface an UObject? If not then you do not store UObject pointers in your array and it has to be a shared pointer.

You can either change your SListView < IFlareShipInterface* > to SListView < TSharedPtr < IFlareShipInterface > > or, like in example above, define OnGenerateRow method like this: SListView< TSharedPtr < IFlareShipInterface > >::MakeOnGenerateWidget( this, &SFlareSectorMenu::GenerateShipInfo )

So the reason why FString* in the exmaple works is because OnGenerateRow is defined differently than regular one.

I’ve never used this method of defining SListView but it should work if it’s in example;B

Edit:

Something is wrong with code brackets… cant use // <

So, it looks like MakeOnGenerateWidget doesn’t exist. The only search hit in the engine source is the documentation snipped you quoted before.

I tried using TSharedPtr instead of pointers, but now the engine crashes as soon as the list is displayed, inside STableRow::GetBorder(). It looks like “OwnerWidget” is NULL.

ShipListData.AddUnique(MakeShareable(GetShipPawn()));

I’m now using TSharedPtr< IFlareShipInterface > > as my table type. The error above was caused by not having the proper definition for my table row widget.

Now I have a much simpler error. Since I made a TSharedPtr from my ship pawn (using an interface), now, deleting my table apparently tries to delete my pawn. And I can’t fix this, since TSharedPtr doesn’t work with UObjects.

I just want to put Pawns in a list. Why is that so hard ?

Ok, let me tell you how I’d do it.

I assume you want to store a pointer to an actor/interface/uobject/whatever in your list. So what I would do is create a class/structure that holds this pointer and use this class/structure as a list item.

class FMyItem
{
	public:	
	static TSharedPtr< FMyItem> New(IFlareShipInterface* InObject)
	{
		return MakeShareable(new FMyItem(InObject));
	}
	
	private:
	FMyItem(IFlareShipInterface* InPtr) : InterfacePtr(InPtr){}
	
	IFlareShipInterface* InterfacePtr;
}

// just for shorter name
typedef TSharedPtr< FMyItem> FItemPtr;

// here goes your list source
TArray< FItemPtr> ListSource;

// list declaration
TSharedPtr< SListView<FItemPtr> >   ListView;

// OnGenerateRow that you will plug during SListView creation
TSharedRef< ITableRow> OnGenerateRow(FItemPtr Item, const TSharedRef< STableViewBase>& OwnerTable)
{
	return SNew( STableRow< FItemPtr >, OwnerTable )
			[
				...
			];
}

// and this is how you make new item and add it to the list
ListSource.Add(FMyItem::New(GetSomethingThatImplementsDesiredInterface()));

Hope this helps you somehow or will get you on the right track

Yes, this does work ! Thanks a lot for your patient help and explanations.