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 !

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

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 ?

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.