UnrealImGui/Source/ImGui/Private/TextureManager.cpp

189 lines
5.8 KiB
C++
Raw Normal View History

// Distributed under the MIT License (MIT) (see accompanying LICENSE file)
#include "TextureManager.h"
2023-09-21 23:33:16 +00:00
#include "RHITypes.h"
#include <Engine/Texture2D.h>
#include <Framework/Application/SlateApplication.h>
#include <algorithm>
void FTextureManager::InitializeErrorTexture(const FColor& Color)
{
CreatePlainTextureInternal(NAME_ErrorTexture, 2, 2, Color);
}
TextureIndex FTextureManager::CreateTexture(const FName& Name, int32 Width, int32 Height, uint32 SrcBpp, uint8* SrcData, TFunction<void(uint8*)> SrcDataCleanup)
{
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);
}
TextureIndex FTextureManager::CreateTextureResources(const FName& Name, UTexture* Texture)
{
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.
return AddTextureEntry(Name, Texture, false);
}
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)
{
// 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);
auto DataCleanup = [SrcDataCleanup](uint8* Data, const FUpdateTextureRegion2D* UpdateRegion)
{
SrcDataCleanup(Data);
delete UpdateRegion;
};
Texture->UpdateTextureRegions(0, 1u, TextureRegion, SrcBpp * Width, SrcBpp, SrcData, DataCleanup);
// Create an entry for the texture.
if (Name == NAME_ErrorTexture)
{
ErrorTexture = { Name, Texture, true };
return INDEX_ErrorTexture;
}
else
{
return AddTextureEntry(Name, Texture, true);
}
}
TextureIndex FTextureManager::CreatePlainTextureInternal(const FName& Name, int32 Width, int32 Height, const FColor& Color)
{
// 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);
auto SrcDataCleanup = [](uint8* Data) { delete[] Data; };
// Create new texture from raw data.
return CreateTextureInternal(Name, Width, Height, Bpp, SrcData, SrcDataCleanup);
}
TextureIndex FTextureManager::AddTextureEntry(const FName& Name, UTexture* Texture, bool bAddToRoot)
{
// Try to find an entry with that name.
TextureIndex Index = FindTextureIndex(Name);
// If this is a new name, try to find an entry to reuse.
if (Index == INDEX_NONE)
{
Index = FindTextureIndex(NAME_None);
}
// Either update/reuse an entry or add a new one.
if (Index != INDEX_NONE)
{
TextureResources[Index] = { Name, Texture, bAddToRoot };
return Index;
}
else
{
return TextureResources.Emplace(Name, Texture, bAddToRoot);
}
}
FTextureManager::FTextureEntry::FTextureEntry(const FName& InName, UTexture* InTexture, bool bAddToRoot)
: 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();
}
// Create brush and resource handle for input texture.
Brush.SetResourceObject(InTexture);
CachedResourceHandle = FSlateApplication::Get().GetRenderer()->GetResourceHandle(Brush);
}
FTextureManager::FTextureEntry::~FTextureEntry()
{
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);
CachedResourceHandle = MoveTemp(Other.CachedResourceHandle);
// 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);
return *this;
}
const FSlateResourceHandle& FTextureManager::FTextureEntry::GetResourceHandle() const
{
if (!CachedResourceHandle.IsValid() && Brush.HasUObject())
{
CachedResourceHandle = FSlateApplication::Get().GetRenderer()->GetResourceHandle(Brush);
}
return CachedResourceHandle;
}
void FTextureManager::FTextureEntry::Reset(bool bReleaseResources)
{
if (bReleaseResources)
{
// 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();
}
}
// 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();
CachedResourceHandle = FSlateResourceHandle();
}