2022-11-22 10:25:22 -05:00
// Copyright Epic Games, Inc. All Rights Reserved.
# include "SRenderResourceViewer.h"
# include "Widgets/Input/SSearchBox.h"
# include "Widgets/Input/SCheckBox.h"
# include "Widgets/Input/SButton.h"
# include "Widgets/Docking/SDockTab.h"
2023-03-23 10:10:52 -04:00
# include "Framework/Commands/Commands.h"
# include "Framework/MultiBox/MultiBoxBuilder.h"
2022-11-22 10:25:22 -05:00
# include "RHI.h"
2023-03-23 10:10:52 -04:00
# include "ContentBrowserModule.h"
# include "IContentBrowserSingleton.h"
2022-11-22 10:25:22 -05:00
# define LOCTEXT_NAMESPACE "SRenderResourceView"
namespace RenderResourceViewerInternal
{
const FName ColumnName ( " Name " ) ;
const FName ColumnType ( " Type " ) ;
const FName ColumnSize ( " Size " ) ;
const FName ColumnFlags ( " Flags " ) ;
2022-12-01 05:27:05 -05:00
const FName ColumnOwner ( " Owner " ) ;
2022-11-22 10:25:22 -05:00
// Format the size to nearest size unit of Byte/KB/MB
FString GetFormatedSize ( uint64 SizeInBytes )
{
const uint64 KB = 1024 ;
const uint64 MB = KB * KB ;
if ( SizeInBytes < KB )
{
return FString : : FromInt ( SizeInBytes ) + TEXT ( " B " ) ;
}
else if ( SizeInBytes < MB )
{
uint32 SizeInKB = SizeInBytes / KB ;
return FString : : FromInt ( SizeInKB ) + TEXT ( " KB " ) ;
}
else
{
uint32 SizeInMB = SizeInBytes / MB ;
return FString : : FromInt ( SizeInMB ) + TEXT ( " MB " ) ;
}
}
struct FResourceInfoRow : public SMultiColumnTableRow < TSharedPtr < FRHIResourceStats > >
{
SLATE_BEGIN_ARGS ( FResourceInfoRow ) { }
SLATE_END_ARGS ( )
TSharedPtr < FRHIResourceStats > Info ;
void Construct ( const FArguments & InArgs , const TSharedRef < STableViewBase > & InOwnerTable , TSharedPtr < FRHIResourceStats > InItem )
{
Info = InItem ;
SMultiColumnTableRow < TSharedPtr < FRHIResourceStats > > : : Construct ( FSuperRowType : : FArguments ( ) , InOwnerTable ) ;
}
TSharedRef < SWidget > GenerateWidgetForColumn ( const FName & Column )
{
if ( Column = = RenderResourceViewerInternal : : ColumnName )
{
return SNew ( STextBlock )
2022-12-01 05:27:05 -05:00
. Text ( FText : : FromName ( Info - > Name ) ) ;
2022-11-22 10:25:22 -05:00
}
else if ( Column = = RenderResourceViewerInternal : : ColumnType )
{
return SNew ( STextBlock )
. Text ( FText : : FromString ( Info - > Type ) ) ;
}
else if ( Column = = RenderResourceViewerInternal : : ColumnSize )
{
return SNew ( STextBlock )
. Text ( FText : : FromString ( RenderResourceViewerInternal : : GetFormatedSize ( Info - > SizeInBytes ) ) ) ;
}
else if ( Column = = RenderResourceViewerInternal : : ColumnFlags )
{
return SNew ( STextBlock )
. Text ( FText : : FromString ( Info - > Flags ) ) ;
}
2022-12-01 05:27:05 -05:00
else if ( Column = = RenderResourceViewerInternal : : ColumnOwner )
{
return SNew ( STextBlock )
. Text ( FText : : FromName ( Info - > OwnerName ) ) ;
}
2022-11-22 10:25:22 -05:00
return SNullWidget : : NullWidget ;
}
} ;
2023-03-23 10:10:52 -04:00
class FContextMenuCommands : public TCommands < FContextMenuCommands >
{
public :
FContextMenuCommands ( )
: TCommands < FContextMenuCommands > ( TEXT ( " RenderResourceViewer " ) , NSLOCTEXT ( " Contexts " , " RenderResourceViewer " , " Render Resource Viewer " ) , NAME_None , FAppStyle : : GetAppStyleSetName ( ) )
{ }
virtual void RegisterCommands ( ) override
{
UI_COMMAND ( Command_FindInContentBrowser ,
" Browse to Asset " ,
" Browses to the associated asset and selects it in the most recently used Content Browser (summoning one if necessary) " ,
EUserInterfaceActionType : : Button ,
FInputChord ( EModifierKey : : Control , EKeys : : B ) ) ;
}
TSharedPtr < FUICommandInfo > Command_FindInContentBrowser ;
} ;
2022-11-22 10:25:22 -05:00
}
# define RENDER_RESOURCE_VIEWER_ADD_CHECKBOX(bIsChecked, DisplayText, Tooltip, OnCheckStateChangedFunc) \
+ SHorizontalBox : : Slot ( ) \
. AutoWidth ( ) \
. Padding ( FMargin ( 0.0f , 2.0f , 2.0f , 0.0f ) ) \
[ \
SNew ( STextBlock ) \
. Text ( DisplayText ) \
. ToolTipText ( Tooltip ) \
] \
+ SHorizontalBox : : Slot ( ) \
. AutoWidth ( ) \
. Padding ( FMargin ( 0.0f , 0.0f , 10.0f , 0.0f ) ) \
[ \
SNew ( SCheckBox ) \
2023-03-20 13:06:22 -04:00
. ToolTipText ( Tooltip ) \
2022-11-22 10:25:22 -05:00
. IsChecked ( bIsChecked ) \
. OnCheckStateChanged ( this , & OnCheckStateChangedFunc ) \
]
2022-12-01 05:27:05 -05:00
# define RENDER_RESOURCE_VIEWER_ADD_COLUMN(Name, Width, Label) \
+ SHeaderRow : : Column ( Name ) \
. FillWidth ( Width ) \
. DefaultLabel ( Label ) \
. SortMode ( this , & SRenderResourceViewerWidget : : GetColumnSortMode , Name ) \
. OnSort ( this , & SRenderResourceViewerWidget : : OnColumnSortModeChanged )
2022-11-22 10:25:22 -05:00
void SRenderResourceViewerWidget : : Construct ( const FArguments & InArgs , const TSharedRef < SDockTab > & ConstructUnderMajorTab , const TSharedPtr < SWindow > & ConstructUnderWindow )
{
SortByColumn = RenderResourceViewerInternal : : ColumnSize ;
SortMode = EColumnSortMode : : Descending ;
ChildSlot
[
SNew ( SVerticalBox )
+ SVerticalBox : : Slot ( )
. FillHeight ( 0.5f )
[
SNew ( SBox )
[
SNew ( SVerticalBox )
+ SVerticalBox : : Slot ( )
. AutoHeight ( )
[
SNew ( SHorizontalBox )
+ SHorizontalBox : : Slot ( )
. AutoWidth ( )
. Padding ( FMargin ( 0.0f , 2.0f , 10.0f , 0.0f ) )
[
SNew ( STextBlock )
. Text ( LOCTEXT ( " FilterText " , " FilterByFlags: " ) )
]
// A row of checkboxes to filter resource list by flags
2023-03-20 12:49:48 -04:00
RENDER_RESOURCE_VIEWER_ADD_CHECKBOX ( bShowResident , LOCTEXT ( " ResidentText " , " Resident " ) , LOCTEXT ( " ResidentTooltip " , " Resource is accessible by GPU, and not evicted (unused) " ) , SRenderResourceViewerWidget : : OnResidentCheckboxChanged )
RENDER_RESOURCE_VIEWER_ADD_CHECKBOX ( bShowTransient , LOCTEXT ( " TransientText " , " Transient " ) , LOCTEXT ( " TransientTooltip " , " Resource is only allocated during the duration of the render passes where it's active and will share underlying memory with other resources in the frame " ) , SRenderResourceViewerWidget : : OnTransientCheckboxChanged )
RENDER_RESOURCE_VIEWER_ADD_CHECKBOX ( bShowStreaming , LOCTEXT ( " StreamingText " , " Streaming " ) , LOCTEXT ( " StreamingTooltip " , " Resource is a streamable texture " ) , SRenderResourceViewerWidget : : OnStreamingCheckboxChanged )
RENDER_RESOURCE_VIEWER_ADD_CHECKBOX ( bShowRT , LOCTEXT ( " RTText " , " RT " ) , LOCTEXT ( " RTTooltip " , " Resource can be written to as a Render Target buffer by GPU " ) , SRenderResourceViewerWidget : : OnRTCheckboxChanged )
RENDER_RESOURCE_VIEWER_ADD_CHECKBOX ( bShowDS , LOCTEXT ( " DSText " , " DS " ) , LOCTEXT ( " DSTooltip " , " Resource can be written to as a Depth Stencil buffer by GPU " ) , SRenderResourceViewerWidget : : OnDSCheckboxChanged )
RENDER_RESOURCE_VIEWER_ADD_CHECKBOX ( bShowUAV , LOCTEXT ( " UAVText " , " UAV " ) , LOCTEXT ( " UAVTooltip " , " Resource supports Unordered Access View which allows temporally unordered read/write access from multiple GPU threads without generating memory conflicts " ) , SRenderResourceViewerWidget : : OnUAVCheckboxChanged )
RENDER_RESOURCE_VIEWER_ADD_CHECKBOX ( bShowRTAS , LOCTEXT ( " RTASText " , " RTAS " ) , LOCTEXT ( " RTASTooltip " , " Resource is a Ray Tracing Acceleration Structure " ) , SRenderResourceViewerWidget : : OnRTASCheckboxChanged )
2022-11-22 10:25:22 -05:00
// Refresh button to update the resource list
+ SHorizontalBox : : Slot ( )
. AutoWidth ( )
. Padding ( FMargin ( 0.0f , 0.0f , 10.0f , 0.0f ) )
[
SNew ( SButton )
. Text ( LOCTEXT ( " RefreshText " , " Refresh " ) )
. ToolTipText ( LOCTEXT ( " RefreshTooltip " , " Refresh the resource list " ) )
. OnClicked ( this , & SRenderResourceViewerWidget : : OnRefreshButtonClicked )
]
]
// Text filter box to search by resource name
+ SVerticalBox : : Slot ( )
. AutoHeight ( )
[
SAssignNew ( FilterTextBox , SSearchBox )
2022-12-01 05:27:05 -05:00
. HintText ( LOCTEXT ( " FilterTextBoxHint " , " Search resources by name or owner. " ) )
. ToolTipText ( LOCTEXT ( " FilterTextBoxToolTip " , " Type here to filter the list of render resources by name or owner. " ) )
2022-11-22 10:25:22 -05:00
. OnTextChanged ( this , & SRenderResourceViewerWidget : : FilterTextBox_OnTextChanged )
]
// Display total resource count and size in text
+ SVerticalBox : : Slot ( )
. AutoHeight ( )
[
SNew ( SHorizontalBox )
+ SHorizontalBox : : Slot ( )
. AutoWidth ( )
. Padding ( FMargin ( 0.0f , 0.0f , 10.0f , 0.0f ) )
[
SNew ( STextBlock )
. Text ( LOCTEXT ( " TotalResourceCountText " , " Total Resource Count: " ) )
]
+ SHorizontalBox : : Slot ( )
. AutoWidth ( )
. Padding ( FMargin ( 0.0f , 0.0f , 10.0f , 0.0f ) )
[
SNew ( STextBlock )
. Text ( this , & SRenderResourceViewerWidget : : GetResourceCountText )
]
+ SHorizontalBox : : Slot ( )
. Padding ( FMargin ( 0.0f , 0.0f , 10.0f , 0.0f ) )
. AutoWidth ( )
[
SNew ( STextBlock )
. Text ( LOCTEXT ( " TotalResourceSizeText " , " Total Resource Size: " ) )
]
+ SHorizontalBox : : Slot ( )
. AutoWidth ( )
[
SNew ( STextBlock )
. Text ( this , & SRenderResourceViewerWidget : : GetResourceSizeText )
]
]
// Display resource list
+ SVerticalBox : : Slot ( )
. FillHeight ( 1.f )
[
SAssignNew ( ResourceListView , SListView < TSharedPtr < FRHIResourceStats > > )
. ScrollbarVisibility ( EVisibility : : Visible )
. ItemHeight ( 24.0f )
2023-03-30 13:22:49 -04:00
. ToolTipText ( LOCTEXT ( " ResourceListViewToolTip " , " CTRL+B or use right click menu to find the asset in Content Browser " ) )
2022-11-22 10:25:22 -05:00
. ListItemsSource ( & ResourceInfos )
. SelectionMode ( ESelectionMode : : SingleToggle )
. OnGenerateRow ( this , & SRenderResourceViewerWidget : : HandleResourceGenerateRow )
2023-03-23 10:10:52 -04:00
. OnContextMenuOpening ( FOnContextMenuOpening : : CreateSP ( this , & SRenderResourceViewerWidget : : OpenContextMenu ) )
2022-11-22 10:25:22 -05:00
. HeaderRow
(
SNew ( SHeaderRow )
2022-12-01 05:27:05 -05:00
RENDER_RESOURCE_VIEWER_ADD_COLUMN ( RenderResourceViewerInternal : : ColumnName , 0.3f , LOCTEXT ( " NameColumn " , " Resource Name " ) )
RENDER_RESOURCE_VIEWER_ADD_COLUMN ( RenderResourceViewerInternal : : ColumnType , 0.1f , LOCTEXT ( " TypeColumn " , " Type " ) )
RENDER_RESOURCE_VIEWER_ADD_COLUMN ( RenderResourceViewerInternal : : ColumnSize , 0.1f , LOCTEXT ( " SizeColumn " , " Size " ) )
RENDER_RESOURCE_VIEWER_ADD_COLUMN ( RenderResourceViewerInternal : : ColumnFlags , 0.15f , LOCTEXT ( " FlagsColumn " , " Flags " ) )
RENDER_RESOURCE_VIEWER_ADD_COLUMN ( RenderResourceViewerInternal : : ColumnOwner , 0.4f , LOCTEXT ( " OwnerColumn " , " Owner " ) )
2022-11-22 10:25:22 -05:00
)
]
]
]
] ;
2023-03-23 10:10:52 -04:00
InitCommandList ( ) ;
2023-02-16 09:11:29 -05:00
RefreshNodes ( true ) ;
2022-11-22 10:25:22 -05:00
}
2023-02-16 09:11:29 -05:00
void SRenderResourceViewerWidget : : RefreshNodes ( bool bUpdateRHIResources )
2022-11-22 10:25:22 -05:00
{
const int32 NumberOfResourcesToShow = - 1 ;
// Retrieve the whole list of RHI tracked resources
2022-11-24 13:44:45 -05:00
# if RHI_ENABLE_RESOURCE_INFO
2023-02-16 09:11:29 -05:00
if ( bUpdateRHIResources )
{
RHIResources . Reset ( ) ;
RHIGetTrackedResourceStats ( RHIResources ) ;
}
2022-11-24 13:44:45 -05:00
# endif
2022-11-22 10:25:22 -05:00
// Filter the resource list by search keyword and flags
ResourceInfos . Reset ( ) ;
TotalResourceSize = 0 ;
for ( const TSharedPtr < FRHIResourceStats > & Info : RHIResources )
{
2023-03-28 06:23:05 -04:00
ensure ( Info - > bHasFlags ) ;
2022-12-01 05:27:05 -05:00
bool bContainsFilterText = FilterText . IsEmpty ( ) | | Info - > Name . ToString ( ) . Contains ( FilterText . ToString ( ) ) | | Info - > OwnerName . ToString ( ) . Contains ( FilterText . ToString ( ) ) ;
2023-03-28 06:23:05 -04:00
bool bContainsFilterFlags = true ;
// If the resource has a flag set and its matching check box is un-ticked, exclude from display.
2023-02-10 07:22:54 -05:00
// bMarkedForDelete resources are excluded from display
2023-03-28 06:23:05 -04:00
if ( ( ! bShowResident & & Info - > bResident ) | |
( ! bShowTransient & & Info - > bTransient ) | |
( ! bShowStreaming & & Info - > bStreaming ) | |
( ! bShowRT & & Info - > bRenderTarget ) | |
( ! bShowDS & & Info - > bDepthStencil ) | |
( ! bShowUAV & & Info - > bUnorderedAccessView ) | |
( ! bShowRTAS & & Info - > bRayTracingAccelerationStructure ) | |
Info - > bMarkedForDelete )
2023-02-10 07:22:54 -05:00
{
bContainsFilterFlags = false ;
}
2022-11-22 10:25:22 -05:00
if ( bContainsFilterText & & bContainsFilterFlags )
{
ResourceInfos . Add ( Info ) ;
TotalResourceSize + = Info - > SizeInBytes ;
}
}
TotalResourceCount = ResourceInfos . Num ( ) ;
// Sort the resource list based on columns.
{
if ( SortByColumn = = RenderResourceViewerInternal : : ColumnName )
{
ResourceInfos . Sort ( [ this ] ( const TSharedPtr < FRHIResourceStats > & A , const TSharedPtr < FRHIResourceStats > & B ) {
2022-12-01 05:27:05 -05:00
if ( SortMode = = EColumnSortMode : : Ascending ) return ( A - > Name . LexicalLess ( B - > Name ) ) ;
else if ( SortMode = = EColumnSortMode : : Descending ) return ( ! A - > Name . LexicalLess ( B - > Name ) ) ;
2022-11-22 10:25:22 -05:00
return true ;
} ) ;
}
else if ( SortByColumn = = RenderResourceViewerInternal : : ColumnType )
{
ResourceInfos . Sort ( [ this ] ( const TSharedPtr < FRHIResourceStats > & A , const TSharedPtr < FRHIResourceStats > & B ) {
if ( SortMode = = EColumnSortMode : : Ascending ) return ( A - > Type < B - > Type ) ;
else if ( SortMode = = EColumnSortMode : : Descending ) return ( A - > Type > = B - > Type ) ;
return true ;
} ) ;
}
else if ( SortByColumn = = RenderResourceViewerInternal : : ColumnSize )
{
ResourceInfos . Sort ( [ this ] ( const TSharedPtr < FRHIResourceStats > & A , const TSharedPtr < FRHIResourceStats > & B ) {
if ( SortMode = = EColumnSortMode : : Ascending ) return ( A - > SizeInBytes < B - > SizeInBytes ) ;
else if ( SortMode = = EColumnSortMode : : Descending ) return ( A - > SizeInBytes > = B - > SizeInBytes ) ;
return true ;
} ) ;
}
else if ( SortByColumn = = RenderResourceViewerInternal : : ColumnFlags )
{
ResourceInfos . Sort ( [ this ] ( const TSharedPtr < FRHIResourceStats > & A , const TSharedPtr < FRHIResourceStats > & B ) {
if ( SortMode = = EColumnSortMode : : Ascending ) return ( A - > Flags < B - > Flags ) ;
else if ( SortMode = = EColumnSortMode : : Descending ) return ( A - > Flags > = B - > Flags ) ;
return true ;
} ) ;
}
2022-12-01 05:27:05 -05:00
else if ( SortByColumn = = RenderResourceViewerInternal : : ColumnOwner )
{
ResourceInfos . Sort ( [ this ] ( const TSharedPtr < FRHIResourceStats > & A , const TSharedPtr < FRHIResourceStats > & B ) {
if ( SortMode = = EColumnSortMode : : Ascending ) return ( A - > OwnerName . LexicalLess ( B - > OwnerName ) ) ;
else if ( SortMode = = EColumnSortMode : : Descending ) return ( ! A - > OwnerName . LexicalLess ( B - > OwnerName ) ) ;
return true ;
} ) ;
}
2022-11-22 10:25:22 -05:00
}
ResourceListView - > RebuildList ( ) ;
}
TSharedRef < ITableRow > SRenderResourceViewerWidget : : HandleResourceGenerateRow ( TSharedPtr < FRHIResourceStats > Item , const TSharedRef < STableViewBase > & OwnerTable )
{
return SNew ( RenderResourceViewerInternal : : FResourceInfoRow , OwnerTable , Item ) ;
}
void SRenderResourceViewerWidget : : OnColumnSortModeChanged ( const EColumnSortPriority : : Type SortPriority , const FName & ColumnId , const EColumnSortMode : : Type InSortMode )
{
SortByColumn = ColumnId ;
SortMode = InSortMode ;
RefreshNodes ( ) ;
}
FText SRenderResourceViewerWidget : : GetResourceSizeText ( ) const
{
return FText : : FromString ( RenderResourceViewerInternal : : GetFormatedSize ( TotalResourceSize ) ) ;
}
2023-03-23 10:10:52 -04:00
FReply SRenderResourceViewerWidget : : OnKeyDown ( const FGeometry & MyGeometry , const FKeyEvent & InKeyEvent )
{
return CommandList - > ProcessCommandBindings ( InKeyEvent ) = = true ? FReply : : Handled ( ) : FReply : : Unhandled ( ) ;
}
void SRenderResourceViewerWidget : : InitCommandList ( )
{
RenderResourceViewerInternal : : FContextMenuCommands : : Register ( ) ;
CommandList = MakeShared < FUICommandList > ( ) ;
CommandList - > MapAction ( RenderResourceViewerInternal : : FContextMenuCommands : : Get ( ) . Command_FindInContentBrowser ,
FExecuteAction : : CreateSP ( this , & SRenderResourceViewerWidget : : ContextMenu_FindInContentBrowser ) ,
FCanExecuteAction : : CreateSP ( this , & SRenderResourceViewerWidget : : ContextMenu_FindInContentBrowser_CanExecute ) ) ;
}
TSharedPtr < SWidget > SRenderResourceViewerWidget : : OpenContextMenu ( )
{
FMenuBuilder MenuBuilder ( true , CommandList . ToSharedRef ( ) ) ;
MenuBuilder . AddMenuEntry
(
RenderResourceViewerInternal : : FContextMenuCommands : : Get ( ) . Command_FindInContentBrowser ,
NAME_None ,
TAttribute < FText > ( ) ,
TAttribute < FText > ( ) ,
FSlateIcon ( FAppStyle : : GetAppStyleSetName ( ) , " SystemWideCommands.FindInContentBrowser.Small " )
) ;
return MenuBuilder . MakeWidget ( ) ;
}
void SRenderResourceViewerWidget : : ContextMenu_FindInContentBrowser ( )
{
UObject * SelectedAsset = nullptr ;
const TArray < TSharedPtr < FRHIResourceStats > > & SelectedNodes = ResourceListView - > GetSelectedItems ( ) ;
if ( SelectedNodes . Num ( ) > 0 )
{
// Find the UObject asset from the owner name path
FString ObjectPathString = SelectedNodes [ 0 ] - > OwnerName . ToString ( ) ;
int32 LODIdx = ObjectPathString . Find ( TEXT ( " [LOD " ) ) ;
if ( LODIdx > - 1 )
{
ObjectPathString = ObjectPathString . Left ( LODIdx ) ;
}
SelectedAsset = FSoftObjectPath ( ObjectPathString ) . ResolveObject ( ) ;
}
if ( SelectedAsset )
{
// Highlight the asset in content browser
const TArray < UObject * > & Assets = { SelectedAsset } ;
const FContentBrowserModule & ContentBrowserModule = FModuleManager : : Get ( ) . LoadModuleChecked < FContentBrowserModule > ( " ContentBrowser " ) ;
ContentBrowserModule . Get ( ) . SyncBrowserToAssets ( Assets ) ;
}
}
bool SRenderResourceViewerWidget : : ContextMenu_FindInContentBrowser_CanExecute ( ) const
{
return ResourceListView - > GetSelectedItems ( ) . Num ( ) > 0 ;
}
2022-11-22 10:25:22 -05:00
# undef LOCTEXT_NAMESPACE