Files
UnrealEngineUWP/Engine/Source/Runtime/SlateRHIRenderer/Private/SlateRHIResourceManager.cpp
T
Nick Darnell ac05f885af Copying //UE4/Dev-Editor to Dev-Main (//UE4/Dev-Main)
#lockdown Nick.Penwarden

[CL 2754371 by Nick Darnell in Main branch]
2015-11-04 16:14:13 -05:00

1052 lines
31 KiB
C++

// Copyright 1998-2015 Epic Games, Inc. All Rights Reserved.
#include "SlateRHIRendererPrivatePCH.h"
#include "ImageWrapper.h"
#include "SlateNativeTextureResource.h"
#include "SlateUTextureResource.h"
#include "SlateMaterialResource.h"
#include "ImageUtils.h"
DECLARE_DWORD_ACCUMULATOR_STAT(TEXT("Num Texture Atlases"), STAT_SlateNumTextureAtlases, STATGROUP_SlateMemory);
DECLARE_DWORD_ACCUMULATOR_STAT(TEXT("Num Non-Atlased Textures"), STAT_SlateNumNonAtlasedTextures, STATGROUP_SlateMemory);
DECLARE_DWORD_ACCUMULATOR_STAT(TEXT("Num Dynamic Textures"), STAT_SlateNumDynamicTextures, STATGROUP_SlateMemory);
DECLARE_CYCLE_STAT(TEXT("GetResource Time"), STAT_SlateGetResourceTime, STATGROUP_SlateVerbose);
FDynamicResourceMap::FDynamicResourceMap()
: TextureMemorySincePurge(0)
, LastExpiredMaterialNumMarker(0)
{
}
TSharedPtr<FSlateDynamicTextureResource> FDynamicResourceMap::GetDynamicTextureResource( FName ResourceName ) const
{
return NativeTextureMap.FindRef( ResourceName );
}
TSharedPtr<FSlateUTextureResource> FDynamicResourceMap::GetUTextureResource( UTexture* TextureObject ) const
{
if(TextureObject)
{
return TextureMap.FindRef(TextureObject);
}
return nullptr;
}
TSharedPtr<FSlateMaterialResource> FDynamicResourceMap::GetMaterialResource( const UMaterialInterface* Material ) const
{
return MaterialMap.FindRef( Material );
}
void FDynamicResourceMap::AddDynamicTextureResource( FName ResourceName, TSharedRef<FSlateDynamicTextureResource> InResource )
{
NativeTextureMap.Add( ResourceName, InResource );
}
void FDynamicResourceMap::AddUTextureResource( UTexture* TextureObject, TSharedRef<FSlateUTextureResource> InResource)
{
if ( TextureObject )
{
check(TextureObject == InResource->TextureObject);
TextureMap.Add(TextureObject, InResource);
TextureMemorySincePurge += TextureObject->GetResourceSize(EResourceSizeMode::Inclusive);
RemoveExpiredTextureResources();
}
}
void FDynamicResourceMap::AddMaterialResource( const UMaterialInterface* Material, TSharedRef<FSlateMaterialResource> InMaterialResource )
{
check( Material == InMaterialResource->GetMaterialObject() );
MaterialMap.Add(Material, InMaterialResource);
RemoveExpiredMaterialResources();
}
void FDynamicResourceMap::RemoveDynamicTextureResource(FName ResourceName)
{
NativeTextureMap.Remove(ResourceName);
}
void FDynamicResourceMap::RemoveUTextureResource( UTexture* TextureObject )
{
if(TextureObject)
{
TextureMap.Remove(TextureObject);
TextureMemorySincePurge -= TextureObject->GetResourceSize(EResourceSizeMode::Inclusive);
}
}
void FDynamicResourceMap::RemoveMaterialResource( const UMaterialInterface* Material )
{
MaterialMap.Remove(Material);
}
void FDynamicResourceMap::Empty()
{
EmptyUTextureResources();
EmptyMaterialResources();
EmptyDynamicTextureResources();
}
void FDynamicResourceMap::EmptyDynamicTextureResources()
{
NativeTextureMap.Empty();
}
void FDynamicResourceMap::EmptyUTextureResources()
{
TextureMap.Empty();
TextureMemorySincePurge = 0;
}
void FDynamicResourceMap::EmptyMaterialResources()
{
MaterialMap.Empty();
}
void FDynamicResourceMap::ReleaseResources()
{
for (TMap<FName, TSharedPtr<FSlateDynamicTextureResource> >::TIterator It(NativeTextureMap); It; ++It)
{
BeginReleaseResource(It.Value()->RHIRefTexture);
}
for ( TextureResourceMap::TIterator It(TextureMap); It; ++It )
{
It.Value()->UpdateRenderResource(nullptr);
}
}
void FDynamicResourceMap::RemoveExpiredTextureResources()
{
// We attempt to purge every 10Mb of accumulated textures.
static const uint64 PurgeAfterAddingNewBytes = 1024 * 1024 * 10; // 10Mb
if ( TextureMemorySincePurge >= PurgeAfterAddingNewBytes )
{
for ( TextureResourceMap::TIterator It(TextureMap); It; ++It )
{
if ( It.Key().IsStale() )
{
It.RemoveCurrent();
}
}
TextureMemorySincePurge = 0;
}
}
void FDynamicResourceMap::RemoveExpiredMaterialResources()
{
static const int32 CheckingIncrement = 10;
if ( MaterialMap.Num() > ( LastExpiredMaterialNumMarker + CheckingIncrement ) )
{
for ( MaterialResourceMap::TIterator It(MaterialMap); It; ++It )
{
if ( It.Key().IsStale() )
{
It.RemoveCurrent();
}
}
LastExpiredMaterialNumMarker = MaterialMap.Num();
}
}
FSlateRHIResourceManager::FSlateRHIResourceManager()
: CurrentAccessedUObject(nullptr)
, BadResourceTexture(nullptr)
{
FCoreDelegates::OnPreExit.AddRaw( this, &FSlateRHIResourceManager::OnAppExit );
MaxAltasedTextureSize = FIntPoint(256,256);
if( GIsEditor )
{
AtlasSize = 2048;
}
else
{
AtlasSize = 1024;
if( GConfig )
{
int32 RequestedSize = 1024;
GConfig->GetInt( TEXT("SlateRenderer"), TEXT("TextureAtlasSize"), RequestedSize, GEngineIni );
AtlasSize = FMath::Clamp<uint32>( RequestedSize, 0, 2048 );
int32 MaxAtlasedTextureWidth = 256;
int32 MaxAtlasedTextureHeight = 256;
GConfig->GetInt( TEXT("SlateRenderer"), TEXT("MaxAtlasedTextureWidth"), MaxAtlasedTextureWidth, GEngineIni );
GConfig->GetInt( TEXT("SlateRenderer"), TEXT("MaxAtlasedTextureHeight"),MaxAtlasedTextureHeight, GEngineIni );
// Max texture size cannot be larger than the max size of the atlas
MaxAltasedTextureSize.X = FMath::Clamp<int32>( MaxAtlasedTextureWidth, 0, AtlasSize );
MaxAltasedTextureSize.Y = FMath::Clamp<int32>( MaxAtlasedTextureHeight, 0, AtlasSize );
}
}
}
FSlateRHIResourceManager::~FSlateRHIResourceManager()
{
FCoreDelegates::OnPreExit.RemoveAll( this );
if ( GIsRHIInitialized )
{
FlushRenderingCommands();
DeleteResources();
}
}
int32 FSlateRHIResourceManager::GetNumAtlasPages() const
{
return TextureAtlases.Num();
}
FIntPoint FSlateRHIResourceManager::GetAtlasPageSize() const
{
return FIntPoint(1024, 1024);
}
FSlateShaderResource* FSlateRHIResourceManager::GetAtlasPageResource(const int32 InIndex) const
{
return TextureAtlases[InIndex]->GetAtlasTexture();
}
bool FSlateRHIResourceManager::IsAtlasPageResourceAlphaOnly() const
{
return false;
}
void FSlateRHIResourceManager::AddReferencedObjects(FReferenceCollector& Collector)
{
for ( TSet<UObject*>* AccessedUObjects : AllAccessedUObject )
{
Collector.AddReferencedObjects(*AccessedUObjects);
}
}
void FSlateRHIResourceManager::CreateTextures( const TArray< const FSlateBrush* >& Resources )
{
TMap<FName,FNewTextureInfo> TextureInfoMap;
const uint32 Stride = GPixelFormats[PF_R8G8B8A8].BlockBytes;
for( int32 ResourceIndex = 0; ResourceIndex < Resources.Num(); ++ResourceIndex )
{
const FSlateBrush& Brush = *Resources[ResourceIndex];
const FName TextureName = Brush.GetResourceName();
if( TextureName != NAME_None && !Brush.HasUObject() && !Brush.IsDynamicallyLoaded() && !ResourceMap.Contains(TextureName) )
{
// Find the texture or add it if it doesnt exist (only load the texture once)
FNewTextureInfo& Info = TextureInfoMap.FindOrAdd( TextureName );
Info.bSrgb = (Brush.ImageType != ESlateBrushImageType::Linear);
// Only atlas the texture if none of the brushes that use it tile it and the image is srgb
Info.bShouldAtlas &= ( Brush.Tiling == ESlateBrushTileType::NoTile && Info.bSrgb && AtlasSize > 0 );
// Texture has been loaded if the texture data is valid
if( !Info.TextureData.IsValid() )
{
uint32 Width = 0;
uint32 Height = 0;
TArray<uint8> RawData;
bool bSucceeded = LoadTexture( Brush, Width, Height, RawData );
Info.TextureData = MakeShareable( new FSlateTextureData( Width, Height, Stride, RawData ) );
const bool bTooLargeForAtlas = (Width >= (uint32)MaxAltasedTextureSize.X || Height >= (uint32)MaxAltasedTextureSize.Y || Width >= AtlasSize || Height >= AtlasSize );
Info.bShouldAtlas &= !bTooLargeForAtlas;
if( !bSucceeded || !ensureMsgf( Info.TextureData->GetRawBytes().Num() > 0, TEXT("Slate resource: (%s) contains no data"), *TextureName.ToString() ) )
{
TextureInfoMap.Remove( TextureName );
}
}
}
}
// Sort textures by size. The largest textures are atlased first which creates a more compact atlas
TextureInfoMap.ValueSort( FCompareFNewTextureInfoByTextureSize() );
for( TMap<FName,FNewTextureInfo>::TConstIterator It(TextureInfoMap); It; ++It )
{
const FNewTextureInfo& Info = It.Value();
FName TextureName = It.Key();
FString NameStr = TextureName.ToString();
checkSlow( TextureName != NAME_None );
FSlateShaderResourceProxy* NewTexture = GenerateTextureResource( Info );
ResourceMap.Add( TextureName, NewTexture );
}
}
bool FSlateRHIResourceManager::LoadTexture( const FSlateBrush& InBrush, uint32& Width, uint32& Height, TArray<uint8>& DecodedImage )
{
FString ResourcePath = GetResourcePath( InBrush );
return LoadTexture(InBrush.GetResourceName(), ResourcePath, Width, Height, DecodedImage);
}
/**
* Loads a UTexture2D from a package and stores it in the cache
*
* @param TextureName The name of the texture to load
*/
bool FSlateRHIResourceManager::LoadTexture( const FName& TextureName, const FString& ResourcePath, uint32& Width, uint32& Height, TArray<uint8>& DecodedImage )
{
checkSlow( IsThreadSafeForSlateRendering() );
bool bSucceeded = true;
uint32 BytesPerPixel = 4;
TArray<uint8> RawFileData;
if( FFileHelper::LoadFileToArray( RawFileData, *ResourcePath ) )
{
IImageWrapperModule& ImageWrapperModule = FModuleManager::LoadModuleChecked<IImageWrapperModule>( FName("ImageWrapper") );
IImageWrapperPtr ImageWrapper = ImageWrapperModule.CreateImageWrapper( EImageFormat::PNG );
if ( ImageWrapper.IsValid() && ImageWrapper->SetCompressed( RawFileData.GetData(), RawFileData.Num() ) )
{
Width = ImageWrapper->GetWidth();
Height = ImageWrapper->GetHeight();
const TArray<uint8>* RawData = NULL;
if (ImageWrapper->GetRaw( ERGBFormat::BGRA, 8, RawData))
{
DecodedImage.AddUninitialized( Width*Height*BytesPerPixel );
DecodedImage = *RawData;
}
else
{
UE_LOG(LogSlate, Log, TEXT("Invalid texture format for Slate resource only RGBA and RGB pngs are supported: %s"), *TextureName.ToString() );
bSucceeded = false;
}
}
else
{
UE_LOG(LogSlate, Log, TEXT("Only pngs are supported in Slate"));
bSucceeded = false;
}
}
else
{
UE_LOG(LogSlate, Log, TEXT("Could not find file for Slate resource: %s"), *TextureName.ToString() );
bSucceeded = false;
}
return bSucceeded;
}
FSlateShaderResourceProxy* FSlateRHIResourceManager::GenerateTextureResource( const FNewTextureInfo& Info )
{
FSlateShaderResourceProxy* NewProxy = NULL;
const uint32 Width = Info.TextureData->GetWidth();
const uint32 Height = Info.TextureData->GetHeight();
if( Info.bShouldAtlas )
{
const FAtlasedTextureSlot* NewSlot = NULL;
FSlateTextureAtlasRHI* Atlas = NULL;
// See if any atlases can hold the texture
for( int32 AtlasIndex = 0; AtlasIndex < TextureAtlases.Num() && !NewSlot; ++AtlasIndex )
{
Atlas = TextureAtlases[AtlasIndex];
NewSlot = Atlas->AddTexture( Width, Height, Info.TextureData->GetRawBytes() );
}
if( !NewSlot )
{
INC_DWORD_STAT_BY(STAT_SlateNumTextureAtlases, 1);
Atlas = new FSlateTextureAtlasRHI( AtlasSize, AtlasSize, ESlateTextureAtlasPaddingStyle::DilateBorder );
TextureAtlases.Add( Atlas );
NewSlot = TextureAtlases.Last()->AddTexture( Width, Height, Info.TextureData->GetRawBytes() );
}
check( Atlas && NewSlot );
// Create a proxy to the atlased texture. The texture being used is the atlas itself with sub uvs to access the correct texture
NewProxy = new FSlateShaderResourceProxy;
NewProxy->Resource = Atlas->GetAtlasTexture();
const uint32 Padding = NewSlot->Padding;
NewProxy->StartUV = FVector2D((float)(NewSlot->X + Padding) / Atlas->GetWidth(), (float)(NewSlot->Y + Padding) / Atlas->GetHeight());
NewProxy->SizeUV = FVector2D( (float)(NewSlot->Width-Padding*2) / Atlas->GetWidth(), (float)(NewSlot->Height-Padding*2) / Atlas->GetHeight() );
NewProxy->ActualSize = FIntPoint( Width, Height );
}
else
{
NewProxy = new FSlateShaderResourceProxy;
// Create a new standalone texture because we can't atlas this one
FSlateTexture2DRHIRef* Texture = new FSlateTexture2DRHIRef( Width, Height, PF_B8G8R8A8, Info.TextureData, Info.bSrgb ? TexCreate_SRGB : TexCreate_None );
// Add it to the list of non atlased textures that we must clean up later
NonAtlasedTextures.Add( Texture );
INC_DWORD_STAT_BY( STAT_SlateNumNonAtlasedTextures, 1 );
BeginInitResource( Texture );
// The texture proxy only contains a single texture
NewProxy->Resource = Texture;
NewProxy->StartUV = FVector2D(0.0f, 0.0f);
NewProxy->SizeUV = FVector2D(1.0f, 1.0f);
NewProxy->ActualSize = FIntPoint( Width, Height );
}
return NewProxy;
}
static void LoadUObjectForBrush( const FSlateBrush& InBrush )
{
// Load the utexture
FString Path = InBrush.GetResourceName().ToString();
if (!Path.IsEmpty() && Path.StartsWith(FSlateBrush::UTextureIdentifier()))
{
FString NewPath = Path.RightChop(FSlateBrush::UTextureIdentifier().Len());
UObject* TextureObject = LoadObject<UTexture2D>(NULL, *NewPath, NULL, LOAD_None, NULL);
FSlateBrush* Brush = const_cast<FSlateBrush*>(&InBrush);
// Set the texture object to a default texture to prevent constant loading of missing textures
if( !TextureObject )
{
UE_LOG(LogSlate, Warning, TEXT("Error loading loading UTexture from path: %s not found"), *Path);
TextureObject = GEngine->DefaultTexture;
}
else
{
// We do this here because this deprecated system of loading textures will not report references and we dont want the Slate RHI resource manager to manage references
TextureObject->AddToRoot();
}
Brush->SetResourceObject(TextureObject);
UE_LOG(LogSlate, Warning, TEXT("The texture:// method of loading UTextures for use in Slate is deprecated. Please convert %s to a Brush Asset"), *Path);
}
}
FSlateShaderResourceProxy* FSlateRHIResourceManager::GetShaderResource( const FSlateBrush& InBrush )
{
SCOPE_CYCLE_COUNTER( STAT_SlateGetResourceTime );
checkSlow( IsThreadSafeForSlateRendering() );
FSlateShaderResourceProxy* Texture = NULL;
if( !InBrush.IsDynamicallyLoaded() && !InBrush.HasUObject() )
{
Texture = ResourceMap.FindRef( InBrush.GetResourceName() );
}
else if (InBrush.GetResourceObject() && InBrush.GetResourceObject()->IsA<UMaterialInterface>())
{
FSlateMaterialResource* Resource = GetMaterialResource(InBrush.GetResourceObject(), InBrush.ImageSize, nullptr);
Texture = Resource->SlateProxy;
}
else if( InBrush.IsDynamicallyLoaded() || ( InBrush.HasUObject() ) )
{
if( InBrush.HasUObject() && InBrush.GetResourceObject() == nullptr )
{
// Hack for loading via the deprecated path
LoadUObjectForBrush( InBrush );
}
Texture = FindOrCreateDynamicTextureResource( InBrush );
}
return Texture;
}
FSlateShaderResource* FSlateRHIResourceManager::GetFontShaderResource( uint32 FontAtlasIndex, FSlateShaderResource* FontTextureAtlas, const class UObject* FontMaterial )
{
if( FontMaterial == nullptr )
{
return FontTextureAtlas;
}
else
{
return GetMaterialResource( FontMaterial, FVector2D::ZeroVector, FontTextureAtlas );
}
}
ISlateAtlasProvider* FSlateRHIResourceManager::GetTextureAtlasProvider()
{
return this;
}
TSharedPtr<FSlateDynamicTextureResource> FSlateRHIResourceManager::MakeDynamicTextureResource( FName ResourceName, uint32 Width, uint32 Height, const TArray< uint8 >& Bytes )
{
// Make storage for the image
FSlateTextureDataRef TextureStorage = MakeShareable( new FSlateTextureData( Width, Height, GPixelFormats[PF_B8G8R8A8].BlockBytes, Bytes ) );
TSharedPtr<FSlateDynamicTextureResource> TextureResource;
// Get a resource from the free list if possible
if(DynamicTextureFreeList.Num() > 0)
{
TextureResource = DynamicTextureFreeList.Pop(/*bAllowShrinking=*/ false);
}
else
{
// Free list is empty, we have to allocate a new resource
TextureResource = MakeShareable(new FSlateDynamicTextureResource(nullptr));
}
TextureResource->Proxy->ActualSize = FIntPoint(TextureStorage->GetWidth(), TextureStorage->GetHeight());
// Init render thread data
ENQUEUE_UNIQUE_RENDER_COMMAND_TWOPARAMETER(InitNewSlateDynamicTextureResource,
FSlateDynamicTextureResource*, TextureResource, TextureResource.Get(),
FSlateTextureDataPtr, InNewTextureData, TextureStorage,
{
if(InNewTextureData.IsValid())
{
// Set the texture to use as the texture we just loaded
TextureResource->RHIRefTexture->SetTextureData(InNewTextureData, PF_B8G8R8A8, TexCreate_SRGB);
}
// Initialize and link the rendering resource
TextureResource->RHIRefTexture->InitResource();
});
// Map the new resource so we don't have to load again
DynamicResourceMap.AddDynamicTextureResource( ResourceName, TextureResource.ToSharedRef() );
INC_DWORD_STAT_BY(STAT_SlateNumDynamicTextures, 1);
return TextureResource;
}
TSharedPtr<FSlateDynamicTextureResource> FSlateRHIResourceManager::GetDynamicTextureResourceByName( FName ResourceName )
{
return DynamicResourceMap.GetDynamicTextureResource( ResourceName );
}
TSharedPtr<FSlateUTextureResource> FSlateRHIResourceManager::MakeDynamicUTextureResource(UTexture* InTextureObject)
{
// Generated texture resource
TSharedPtr<FSlateUTextureResource> TextureResource;
// Data for a loaded disk image
FNewTextureInfo Info;
bool bUsingDeprecatedUTexturePath = false;
bool bSucceeded = false;
if( InTextureObject != NULL )
{
TextureResource = DynamicResourceMap.GetUTextureResource( InTextureObject );
if( TextureResource.IsValid() )
{
// Bail out of the resource is already loaded
return TextureResource;
}
bSucceeded = true;
}
if( bSucceeded )
{
// Get a resource from the free list if possible
if (UTextureFreeList.Num() > 0)
{
TextureResource = UTextureFreeList.Pop(/*bAllowShrinking=*/ false);
TextureResource->TextureObject = InTextureObject;
}
else
{
// Free list is empty, we have to allocate a new resource
TextureResource = MakeShareable(new FSlateUTextureResource(InTextureObject));
}
TextureResource->Proxy->ActualSize = FIntPoint(InTextureObject->GetSurfaceWidth(), InTextureObject->GetSurfaceHeight());
checkSlow(!GetAccessedUObjects().Contains(InTextureObject));
}
else
{
// Add the null texture so we don't continuously try to load it.
TextureResource = FSlateUTextureResource::NullResource;
}
DynamicResourceMap.AddUTextureResource(InTextureObject, TextureResource.ToSharedRef());
return TextureResource;
}
FSlateShaderResourceProxy* FSlateRHIResourceManager::FindOrCreateDynamicTextureResource(const FSlateBrush& InBrush)
{
checkSlow( IsThreadSafeForSlateRendering() );
const FName ResourceName = InBrush.GetResourceName();
if ( ResourceName.IsValid() && ResourceName != NAME_None )
{
if ( UObject* ResourceObject = InBrush.GetResourceObject() )
{
if ( !ResourceObject->IsA<UTexture>() )
{
#if !(UE_BUILD_SHIPPING || UE_BUILD_TEST)
static TSet<UObject*> FailedTextures;
if ( !FailedTextures.Contains(ResourceObject) )
{
FailedTextures.Add(ResourceObject);
ensureMsgf(false, TEXT("Slate RHI Error - Invalid Texture2D '%s'."), *ResourceName.ToString());
}
ResourceObject = GetBadResourceTexture();
#else
return nullptr;
#endif
}
UTexture* TextureObject = Cast<UTexture>(ResourceObject);
TSharedPtr<FSlateUTextureResource> TextureResource = DynamicResourceMap.GetUTextureResource(TextureObject);
if ( !TextureResource.IsValid() )
{
TextureResource = MakeDynamicUTextureResource(TextureObject);
if ( TextureResource.IsValid() )
{
INC_DWORD_STAT_BY(STAT_SlateNumDynamicTextures, 1);
}
}
if ( TextureResource.IsValid() && TextureResource->TextureObject && TextureResource->TextureObject->Resource )
{
TextureResource->UpdateRenderResource(TextureObject->Resource);
GetAccessedUObjects().Add(TextureResource->TextureObject);
return TextureResource->Proxy;
}
}
else
{
TSharedPtr<FSlateDynamicTextureResource> TextureResource = DynamicResourceMap.GetDynamicTextureResource( ResourceName );
if( !TextureResource.IsValid() )
{
uint32 Width;
uint32 Height;
TArray<uint8> RawData;
// Load the image from disk
bool bSucceeded = LoadTexture(ResourceName, ResourceName.ToString(), Width, Height, RawData);
if(bSucceeded)
{
TextureResource = MakeDynamicTextureResource(ResourceName, Width, Height, RawData);
}
}
if(TextureResource.IsValid())
{
return TextureResource->Proxy;
}
}
}
// dynamic texture was not found or loaded
return nullptr;
}
FSlateMaterialResource* FSlateRHIResourceManager::GetMaterialResource(const UObject* InMaterial, FVector2D ImageSize, FSlateShaderResource* TextureMask )
{
checkSlow(IsThreadSafeForSlateRendering());
const UMaterialInterface* Material = CastChecked<UMaterialInterface>(InMaterial);
TSharedPtr<FSlateMaterialResource> MaterialResource = DynamicResourceMap.GetMaterialResource(Material);
if (!MaterialResource.IsValid())
{
// Get a resource from the free list if possible
if(MaterialResourceFreeList.Num() > 0)
{
MaterialResource = MaterialResourceFreeList.Pop();
MaterialResource->UpdateMaterial( *Material, ImageSize, TextureMask );
}
else
{
MaterialResource = MakeShareable(new FSlateMaterialResource(*Material, ImageSize, TextureMask));
}
DynamicResourceMap.AddMaterialResource(Material, MaterialResource.ToSharedRef());
}
else if( MaterialResource->GetTextureMaskResource() != TextureMask )
{
MaterialResource->UpdateMaterial( *Material, ImageSize, TextureMask );
}
else
{
// Keep the resource up to date
MaterialResource->UpdateRenderResource(Material->GetRenderProxy(false, false));
MaterialResource->SlateProxy->ActualSize = ImageSize.IntPoint();
}
GetAccessedUObjects().Add(const_cast<UMaterialInterface*>( Material ));
return MaterialResource.Get();
}
void FSlateRHIResourceManager::OnAppExit()
{
ReleaseResources();
FlushRenderingCommands();
DeleteResources();
}
bool FSlateRHIResourceManager::ContainsTexture( const FName& ResourceName ) const
{
return ResourceMap.Contains( ResourceName );
}
void FSlateRHIResourceManager::ReleaseDynamicResource( const FSlateBrush& InBrush )
{
checkSlow( IsThreadSafeForSlateRendering() )
// Note: Only dynamically loaded or utexture brushes can be dynamically released
if( InBrush.HasUObject() || InBrush.IsDynamicallyLoaded() )
{
FName ResourceName = InBrush.GetResourceName();
UObject* ResourceObject = InBrush.GetResourceObject();
if( ResourceObject && DynamicResourceMap.GetNumObjectResources() > 0 )
{
TSharedPtr<FSlateUTextureResource> TextureResource = DynamicResourceMap.GetUTextureResource(Cast<UTexture>(ResourceObject));
if(TextureResource.IsValid())
{
//remove it from the accessed textures
GetAccessedUObjects().Remove(TextureResource->TextureObject);
DynamicResourceMap.RemoveUTextureResource(TextureResource->TextureObject);
UTextureFreeList.Add(TextureResource);
DEC_DWORD_STAT_BY(STAT_SlateNumDynamicTextures, 1);
}
else
{
UMaterialInterface* Material = Cast<UMaterialInterface>(ResourceObject);
TSharedPtr<FSlateMaterialResource> MaterialResource = DynamicResourceMap.GetMaterialResource(Material);
DynamicResourceMap.RemoveMaterialResource(Material);
if (MaterialResource.IsValid())
{
MaterialResourceFreeList.Add( MaterialResource );
}
}
}
else if( !ResourceObject )
{
TSharedPtr<FSlateDynamicTextureResource> TextureResource = DynamicResourceMap.GetDynamicTextureResource(ResourceName);
if( TextureResource.IsValid() )
{
// Release the rendering resource, its no longer being used
BeginReleaseResource(TextureResource->RHIRefTexture);
//remove it from the texture map
DynamicResourceMap.RemoveDynamicTextureResource(ResourceName);
DynamicTextureFreeList.Add( TextureResource );
DEC_DWORD_STAT_BY(STAT_SlateNumDynamicTextures, 1);
}
}
}
}
void FSlateRHIResourceManager::LoadUsedTextures()
{
TArray< const FSlateBrush* > Resources;
FSlateStyleRegistry::GetAllResources( Resources );
CreateTextures( Resources );
}
void FSlateRHIResourceManager::LoadStyleResources( const ISlateStyle& Style )
{
TArray< const FSlateBrush* > Resources;
Style.GetResources( Resources );
CreateTextures( Resources );
}
void FSlateRHIResourceManager::BeginReleasingAccessedResources(bool bImmediatelyFlush)
{
if ( IsThreadSafeForSlateRendering() )
{
if ( CurrentAccessedUObject )
{
DirtyAccessedObjectSets.Enqueue(CurrentAccessedUObject);
ENQUEUE_UNIQUE_RENDER_COMMAND_ONEPARAMETER(UpdateSlateUTextureResource,
FSlateRHIResourceManager*, InManager, this,
{
TSet<UObject*>* Objects;
check(InManager->DirtyAccessedObjectSets.Dequeue(Objects));
InManager->CleanAccessedObjectSets.Enqueue(Objects);
});
CurrentAccessedUObject = nullptr;
}
if ( bImmediatelyFlush )
{
// Release all accessed object sets, we only manipulate the set on the main
// thread, so this is fine.
for ( TSet<UObject*>* AccessedUObjects : AllAccessedUObject )
{
AccessedUObjects->Empty();
}
}
}
}
TSet<UObject*>& FSlateRHIResourceManager::GetAccessedUObjects()
{
// If the current CurrentAccessedUObject is nullptr, that means we need a fresh
// one from the clean queue, or we need to create one.
if ( CurrentAccessedUObject == nullptr )
{
if ( CleanAccessedObjectSets.Dequeue(CurrentAccessedUObject) )
{
CurrentAccessedUObject->Empty();
}
else
{
AllAccessedUObject.Add(new TSet<UObject*>());
CurrentAccessedUObject = AllAccessedUObject.Last();
}
}
return *CurrentAccessedUObject;
}
void FSlateRHIResourceManager::UpdateTextureAtlases()
{
for( int32 AtlasIndex = 0; AtlasIndex < TextureAtlases.Num(); ++AtlasIndex )
{
TextureAtlases[AtlasIndex]->ConditionalUpdateTexture();
}
}
FCachedRenderBuffers* FSlateRHIResourceManager::FindOrCreateCachedBuffersForHandle(const TSharedRef<FSlateRenderDataHandle, ESPMode::ThreadSafe>& RenderHandle)
{
FCachedRenderBuffers* Buffers = CachedBuffers.FindRef(&RenderHandle.Get());
if ( Buffers == nullptr )
{
// Rather than having a global pool, we associate the pools with a particular layout cacher.
// If we don't do this, all buffers eventually become as larger as the largest buffer, and it
// would be much better to keep the pools coherent with the sizes typically associated with
// a particular caching panel.
const ILayoutCache* LayoutCacher = RenderHandle->GetCacher();
TArray< FCachedRenderBuffers* >& Pool = CachedBufferPool.FindOrAdd(LayoutCacher);
// If the cached buffer pool is empty, time to create a new one!
if ( Pool.Num() == 0 )
{
Buffers = new FCachedRenderBuffers();
Buffers->VertexBuffer.Init(200);
Buffers->IndexBuffer.Init(200);
}
else
{
// If we found one in the pool, lets use it!
Buffers = Pool[0];
Pool.RemoveAtSwap(0, 1, false);
}
CachedBuffers.Add(&RenderHandle.Get(), Buffers);
}
return Buffers;
}
void FSlateRHIResourceManager::ReleaseCachedRenderData(FSlateRenderDataHandle* InRenderHandle)
{
// Should only be called by the rendering thread
check(IsInRenderingThread());
check(InRenderHandle);
FCachedRenderBuffers* PooledBuffer = CachedBuffers.FindRef(InRenderHandle);
if ( ensure(PooledBuffer != nullptr) )
{
const ILayoutCache* LayoutCacher = InRenderHandle->GetCacher();
TArray< FCachedRenderBuffers* >* Pool = CachedBufferPool.Find(LayoutCacher);
if ( Pool )
{
Pool->Add(PooledBuffer);
}
else
{
// The buffer pool may have already been released, so lets just delete this buffer.
PooledBuffer->VertexBuffer.Destroy();
PooledBuffer->IndexBuffer.Destroy();
delete PooledBuffer;
}
CachedBuffers.Remove(InRenderHandle);
}
}
void FSlateRHIResourceManager::ReleaseCachingResourcesFor(const ILayoutCache* Cacher)
{
TArray< FCachedRenderBuffers* >* Pool = CachedBufferPool.Find(Cacher);
if ( Pool )
{
for ( FCachedRenderBuffers* PooledBuffer : *Pool )
{
PooledBuffer->VertexBuffer.Destroy();
PooledBuffer->IndexBuffer.Destroy();
delete PooledBuffer;
}
CachedBufferPool.Remove(Cacher);
}
}
void FSlateRHIResourceManager::ReleaseResources()
{
checkSlow( IsThreadSafeForSlateRendering() );
for( int32 AtlasIndex = 0; AtlasIndex < TextureAtlases.Num(); ++AtlasIndex )
{
TextureAtlases[AtlasIndex]->ReleaseAtlasTexture();
}
for( int32 ResourceIndex = 0; ResourceIndex < NonAtlasedTextures.Num(); ++ResourceIndex )
{
BeginReleaseResource( NonAtlasedTextures[ResourceIndex] );
}
DynamicResourceMap.ReleaseResources();
for ( TCachedBufferMap::TIterator BufferIt(CachedBuffers); BufferIt; ++BufferIt )
{
FSlateRenderDataHandle* Handle = BufferIt.Key();
FCachedRenderBuffers* Buffer = BufferIt.Value();
Handle->Disconnect();
Buffer->VertexBuffer.Destroy();
Buffer->IndexBuffer.Destroy();
}
for ( TCachedBufferPoolMap::TIterator BufferIt(CachedBufferPool); BufferIt; ++BufferIt )
{
TArray< FCachedRenderBuffers* >& Pool = BufferIt.Value();
for ( FCachedRenderBuffers* PooledBuffer : Pool )
{
PooledBuffer->VertexBuffer.Destroy();
PooledBuffer->IndexBuffer.Destroy();
}
}
// Note the base class has texture proxies only which do not need to be released
}
void FSlateRHIResourceManager::DeleteResources()
{
for( int32 AtlasIndex = 0; AtlasIndex < TextureAtlases.Num(); ++AtlasIndex )
{
delete TextureAtlases[AtlasIndex];
}
for( int32 ResourceIndex = 0; ResourceIndex < NonAtlasedTextures.Num(); ++ResourceIndex )
{
delete NonAtlasedTextures[ResourceIndex];
}
SET_DWORD_STAT(STAT_SlateNumNonAtlasedTextures, 0);
SET_DWORD_STAT(STAT_SlateNumTextureAtlases, 0);
SET_DWORD_STAT(STAT_SlateNumDynamicTextures, 0);
// Verify rendering commands were flushed by ensuring there's nothing left to be processed
// in the dirty queue, they should all be in clean.
check(DirtyAccessedObjectSets.IsEmpty());
// Remove everything from the clean set.
TSet<UObject*>* DummyObjects;
while ( CleanAccessedObjectSets.Dequeue(DummyObjects) ) { }
// Release all accessed object sets.
for ( TSet<UObject*>* AccessedUObjects : AllAccessedUObject )
{
AccessedUObjects->Empty();
delete AccessedUObjects;
}
AllAccessedUObject.Empty();
DynamicResourceMap.Empty();
TextureAtlases.Empty();
NonAtlasedTextures.Empty();
DynamicTextureFreeList.Empty();
MaterialResourceFreeList.Empty();
UTextureFreeList.Empty();
// Clean up mapping to texture
ClearTextureMap();
for ( TCachedBufferMap::TIterator BufferIt(CachedBuffers); BufferIt; ++BufferIt )
{
FCachedRenderBuffers* Buffer = BufferIt.Value();
delete Buffer;
}
CachedBuffers.Empty();
for ( TCachedBufferPoolMap::TIterator BufferIt(CachedBufferPool); BufferIt; ++BufferIt )
{
TArray< FCachedRenderBuffers* >& Pool = BufferIt.Value();
for ( FCachedRenderBuffers* PooledBuffer : Pool )
{
delete PooledBuffer;
}
}
CachedBufferPool.Empty();
}
void FSlateRHIResourceManager::ReloadTextures()
{
checkSlow( IsThreadSafeForSlateRendering() );
// Release rendering resources
ReleaseResources();
// wait for all rendering resources to be released
FlushRenderingCommands();
// Delete allocated resources (cpu)
DeleteResources();
// Reload everythng
LoadUsedTextures();
}
UTexture* FSlateRHIResourceManager::GetBadResourceTexture()
{
if ( BadResourceTexture == nullptr )
{
BadResourceTexture = FImageUtils::CreateCheckerboardTexture(FColor(255, 0, 255), FColor(255, 255, 0));
BadResourceTexture->AddToRoot();
}
return BadResourceTexture;
}