2017-03-26 20:32:57 +00:00
|
|
|
// Distributed under the MIT License (MIT) (see accompanying LICENSE file)
|
|
|
|
|
|
|
|
#include "TextureManager.h"
|
2023-09-21 23:33:16 +00:00
|
|
|
#include "RHITypes.h"
|
2020-06-14 20:50:26 +00:00
|
|
|
#include <Engine/Texture2D.h>
|
|
|
|
#include <Framework/Application/SlateApplication.h>
|
|
|
|
|
2017-03-26 20:32:57 +00:00
|
|
|
#include <algorithm>
|
|
|
|
|
|
|
|
|
2018-08-15 21:20:46 +00:00
|
|
|
void FTextureManager::InitializeErrorTexture(const FColor& Color)
|
|
|
|
{
|
|
|
|
CreatePlainTextureInternal(NAME_ErrorTexture, 2, 2, Color);
|
|
|
|
}
|
|
|
|
|
2018-08-14 19:28:25 +00:00
|
|
|
TextureIndex FTextureManager::CreateTexture(const FName& Name, int32 Width, int32 Height, uint32 SrcBpp, uint8* SrcData, TFunction<void(uint8*)> SrcDataCleanup)
|
2017-03-26 20:32:57 +00:00
|
|
|
{
|
2018-08-15 21:20:46 +00:00
|
|
|
checkf(Name != NAME_None, TEXT("Trying to create a texture with a name 'NAME_None' is not allowed."));
|
|
|
|
|
|
|
|
return CreateTextureInternal(Name, Width, Height, SrcBpp, SrcData, SrcDataCleanup);
|
|
|
|
}
|
|
|
|
|
|
|
|
TextureIndex FTextureManager::CreatePlainTexture(const FName& Name, int32 Width, int32 Height, FColor Color)
|
|
|
|
{
|
|
|
|
checkf(Name != NAME_None, TEXT("Trying to create a texture with a name 'NAME_None' is not allowed."));
|
|
|
|
|
|
|
|
return CreatePlainTextureInternal(Name, Width, Height, Color);
|
|
|
|
}
|
2017-03-26 20:32:57 +00:00
|
|
|
|
2022-03-25 22:53:05 +00:00
|
|
|
TextureIndex FTextureManager::CreateTextureResources(const FName& Name, UTexture* Texture)
|
2018-08-15 21:20:46 +00:00
|
|
|
{
|
|
|
|
checkf(Name != NAME_None, TEXT("Trying to create texture resources with a name 'NAME_None' is not allowed."));
|
|
|
|
checkf(Texture, TEXT("Null Texture."));
|
|
|
|
|
|
|
|
// Create an entry for the texture.
|
2020-06-07 20:58:48 +00:00
|
|
|
return AddTextureEntry(Name, Texture, false);
|
2018-08-15 21:20:46 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void FTextureManager::ReleaseTextureResources(TextureIndex Index)
|
|
|
|
{
|
|
|
|
checkf(IsInRange(Index), TEXT("Invalid texture index %d. Texture resources array has %d entries total."), Index, TextureResources.Num());
|
|
|
|
|
|
|
|
TextureResources[Index] = {};
|
|
|
|
}
|
|
|
|
|
|
|
|
TextureIndex FTextureManager::CreateTextureInternal(const FName& Name, int32 Width, int32 Height, uint32 SrcBpp, uint8* SrcData, TFunction<void(uint8*)> SrcDataCleanup)
|
|
|
|
{
|
2017-03-26 20:32:57 +00:00
|
|
|
// Create a texture.
|
|
|
|
UTexture2D* Texture = UTexture2D::CreateTransient(Width, Height);
|
|
|
|
|
|
|
|
// Create a new resource for that texture.
|
|
|
|
Texture->UpdateResource();
|
|
|
|
|
|
|
|
// Update texture data.
|
|
|
|
FUpdateTextureRegion2D* TextureRegion = new FUpdateTextureRegion2D(0, 0, 0, 0, Width, Height);
|
2018-08-14 19:28:25 +00:00
|
|
|
auto DataCleanup = [SrcDataCleanup](uint8* Data, const FUpdateTextureRegion2D* UpdateRegion)
|
2017-03-26 20:32:57 +00:00
|
|
|
{
|
2018-08-14 19:28:25 +00:00
|
|
|
SrcDataCleanup(Data);
|
2017-03-26 20:32:57 +00:00
|
|
|
delete UpdateRegion;
|
|
|
|
};
|
|
|
|
Texture->UpdateTextureRegions(0, 1u, TextureRegion, SrcBpp * Width, SrcBpp, SrcData, DataCleanup);
|
|
|
|
|
2018-08-15 21:20:46 +00:00
|
|
|
// Create an entry for the texture.
|
|
|
|
if (Name == NAME_ErrorTexture)
|
|
|
|
{
|
|
|
|
ErrorTexture = { Name, Texture, true };
|
|
|
|
return INDEX_ErrorTexture;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2020-06-07 20:58:48 +00:00
|
|
|
return AddTextureEntry(Name, Texture, true);
|
2018-08-15 21:20:46 +00:00
|
|
|
}
|
2017-03-26 20:32:57 +00:00
|
|
|
}
|
|
|
|
|
2018-08-15 21:20:46 +00:00
|
|
|
TextureIndex FTextureManager::CreatePlainTextureInternal(const FName& Name, int32 Width, int32 Height, const FColor& Color)
|
2017-03-26 20:32:57 +00:00
|
|
|
{
|
|
|
|
// Create buffer with raw data.
|
|
|
|
const uint32 ColorPacked = Color.ToPackedARGB();
|
|
|
|
const uint32 Bpp = sizeof(ColorPacked);
|
|
|
|
const uint32 SizeInPixels = Width * Height;
|
|
|
|
const uint32 SizeInBytes = SizeInPixels * Bpp;
|
|
|
|
uint8* SrcData = new uint8[SizeInBytes];
|
|
|
|
std::fill(reinterpret_cast<uint32*>(SrcData), reinterpret_cast<uint32*>(SrcData) + SizeInPixels, ColorPacked);
|
2018-08-14 19:28:25 +00:00
|
|
|
auto SrcDataCleanup = [](uint8* Data) { delete[] Data; };
|
2017-03-26 20:32:57 +00:00
|
|
|
|
2018-08-14 19:28:25 +00:00
|
|
|
// Create new texture from raw data.
|
2018-08-15 21:20:46 +00:00
|
|
|
return CreateTextureInternal(Name, Width, Height, Bpp, SrcData, SrcDataCleanup);
|
2017-03-26 20:32:57 +00:00
|
|
|
}
|
|
|
|
|
2022-03-25 22:53:05 +00:00
|
|
|
TextureIndex FTextureManager::AddTextureEntry(const FName& Name, UTexture* Texture, bool bAddToRoot)
|
2017-03-26 20:32:57 +00:00
|
|
|
{
|
2020-06-07 20:58:48 +00:00
|
|
|
// Try to find an entry with that name.
|
|
|
|
TextureIndex Index = FindTextureIndex(Name);
|
2018-08-15 21:20:46 +00:00
|
|
|
|
2020-06-07 20:58:48 +00:00
|
|
|
// If this is a new name, try to find an entry to reuse.
|
2018-08-15 21:20:46 +00:00
|
|
|
if (Index == INDEX_NONE)
|
|
|
|
{
|
|
|
|
Index = FindTextureIndex(NAME_None);
|
|
|
|
}
|
|
|
|
|
2020-06-07 20:58:48 +00:00
|
|
|
// Either update/reuse an entry or add a new one.
|
2018-08-15 21:20:46 +00:00
|
|
|
if (Index != INDEX_NONE)
|
|
|
|
{
|
|
|
|
TextureResources[Index] = { Name, Texture, bAddToRoot };
|
|
|
|
return Index;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
return TextureResources.Emplace(Name, Texture, bAddToRoot);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-03-25 22:53:05 +00:00
|
|
|
FTextureManager::FTextureEntry::FTextureEntry(const FName& InName, UTexture* InTexture, bool bAddToRoot)
|
2018-08-15 21:20:46 +00:00
|
|
|
: Name(InName)
|
|
|
|
{
|
|
|
|
checkf(InTexture, TEXT("Null texture."));
|
|
|
|
|
|
|
|
if (bAddToRoot)
|
|
|
|
{
|
|
|
|
// Get pointer only for textures that we added to root, so we can later release them.
|
|
|
|
Texture = InTexture;
|
|
|
|
// Add texture to the root to prevent garbage collection.
|
|
|
|
InTexture->AddToRoot();
|
|
|
|
}
|
2017-03-26 20:32:57 +00:00
|
|
|
|
|
|
|
// Create brush and resource handle for input texture.
|
2018-08-14 19:40:25 +00:00
|
|
|
Brush.SetResourceObject(InTexture);
|
2021-04-16 01:30:53 +00:00
|
|
|
CachedResourceHandle = FSlateApplication::Get().GetRenderer()->GetResourceHandle(Brush);
|
2017-03-26 20:32:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
FTextureManager::FTextureEntry::~FTextureEntry()
|
|
|
|
{
|
2018-08-15 21:20:46 +00:00
|
|
|
Reset(true);
|
|
|
|
}
|
|
|
|
|
|
|
|
FTextureManager::FTextureEntry& FTextureManager::FTextureEntry::operator=(FTextureEntry&& Other)
|
|
|
|
{
|
|
|
|
// Release old resources if allocated.
|
|
|
|
Reset(true);
|
|
|
|
|
|
|
|
// Move data and ownership to this instance.
|
|
|
|
Name = MoveTemp(Other.Name);
|
|
|
|
Texture = MoveTemp(Other.Texture);
|
|
|
|
Brush = MoveTemp(Other.Brush);
|
2021-04-16 01:30:53 +00:00
|
|
|
CachedResourceHandle = MoveTemp(Other.CachedResourceHandle);
|
2018-08-15 21:20:46 +00:00
|
|
|
|
|
|
|
// Reset the other entry (without releasing resources which are already moved to this instance) to remove tracks
|
|
|
|
// of ownership and mark it as empty/reusable.
|
|
|
|
Other.Reset(false);
|
2017-03-26 20:32:57 +00:00
|
|
|
|
2018-08-15 21:20:46 +00:00
|
|
|
return *this;
|
|
|
|
}
|
|
|
|
|
2021-04-16 01:30:53 +00:00
|
|
|
const FSlateResourceHandle& FTextureManager::FTextureEntry::GetResourceHandle() const
|
|
|
|
{
|
|
|
|
if (!CachedResourceHandle.IsValid() && Brush.HasUObject())
|
|
|
|
{
|
|
|
|
CachedResourceHandle = FSlateApplication::Get().GetRenderer()->GetResourceHandle(Brush);
|
|
|
|
}
|
|
|
|
return CachedResourceHandle;
|
|
|
|
}
|
|
|
|
|
2018-08-15 21:20:46 +00:00
|
|
|
void FTextureManager::FTextureEntry::Reset(bool bReleaseResources)
|
|
|
|
{
|
|
|
|
if (bReleaseResources)
|
2017-03-26 20:32:57 +00:00
|
|
|
{
|
2018-08-15 21:20:46 +00:00
|
|
|
// Release brush.
|
|
|
|
if (Brush.HasUObject() && FSlateApplication::IsInitialized())
|
|
|
|
{
|
|
|
|
FSlateApplication::Get().GetRenderer()->ReleaseDynamicResource(Brush);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Remove texture from root to allow for garbage collection (it might be invalid, if we never set it
|
|
|
|
// or this is an application shutdown).
|
|
|
|
if (Texture.IsValid())
|
|
|
|
{
|
|
|
|
Texture->RemoveFromRoot();
|
|
|
|
}
|
2017-03-26 20:32:57 +00:00
|
|
|
}
|
2018-08-15 21:20:46 +00:00
|
|
|
|
|
|
|
// We use empty name to mark unused entries.
|
|
|
|
Name = NAME_None;
|
|
|
|
|
|
|
|
// Clean fields to make sure that we don't reference released or moved resources.
|
|
|
|
Texture.Reset();
|
|
|
|
Brush = FSlateNoResource();
|
2021-04-16 01:30:53 +00:00
|
|
|
CachedResourceHandle = FSlateResourceHandle();
|
2017-03-26 20:32:57 +00:00
|
|
|
}
|