Files
UnrealEngineUWP/Engine/Source/Developer/TextureFormatUncompressed/Private/TextureFormatUncompressed.cpp
charles bloom be238dee17 fix failures on texture import of rare cases
fix RHI upload of textures that are multiple of 4 in top mip but not in lower mips
dont pad CompressedImage sizes, store true size
clean up Texture size limits and VT conditions
better default settings for texture import
clean up initialization order of TextureFormatManagerModule

#preflight 6250814a11261bc7b23d8f4b
#rb fabian.giesen,julien.stjean

[CL 19693287 by charles bloom in ue5-main branch]
2022-04-08 16:06:54 -04:00

430 lines
14 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "CoreMinimal.h"
#include "Misc/FileHelper.h"
#include "Misc/Paths.h"
#include "Modules/ModuleManager.h"
#include "Interfaces/ITextureFormat.h"
#include "Interfaces/ITextureFormatModule.h"
#include "TextureCompressorModule.h"
#include "PixelFormat.h"
#include "ImageCore.h"
#include "TextureBuildFunction.h"
#include "DerivedDataBuildFunctionFactory.h"
#include "DerivedDataSharedString.h"
DEFINE_LOG_CATEGORY_STATIC(LogTextureFormatUncompressed, Log, All);
class FUncompressedTextureBuildFunction final : public FTextureBuildFunction
{
const UE::DerivedData::FUtf8SharedString& GetName() const final
{
static const UE::DerivedData::FUtf8SharedString Name(UTF8TEXTVIEW("UncompressedTexture"));
return Name;
}
void GetVersion(UE::DerivedData::FBuildVersionBuilder& Builder, ITextureFormat*& OutTextureFormatVersioning) const final
{
static FGuid Version(TEXT("c04fe27a-53f6-402e-85b3-648ac6b1ad87"));
Builder << Version;
OutTextureFormatVersioning = FModuleManager::GetModuleChecked<ITextureFormatModule>(TEXT("TextureFormatUncompressed")).GetTextureFormat();
}
};
/**
* Macro trickery for supported format names.
*/
#define ENUM_SUPPORTED_FORMATS(op) \
op(BGRA8) \
op(G8) \
op(G16) \
op(VU8) \
op(RGBA16F) \
op(XGXR8) \
op(RGBA8) \
op(POTERROR) \
op(R16F) \
op(R5G6B5) \
op(A1RGB555) \
op(RGB555A1)
#define DECL_FORMAT_NAME(FormatName) static FName GTextureFormatName##FormatName = FName(TEXT(#FormatName));
ENUM_SUPPORTED_FORMATS(DECL_FORMAT_NAME);
#undef DECL_FORMAT_NAME
#define DECL_FORMAT_NAME_ENTRY(FormatName) GTextureFormatName##FormatName ,
static FName GSupportedTextureFormatNames[] =
{
ENUM_SUPPORTED_FORMATS(DECL_FORMAT_NAME_ENTRY)
};
#undef DECL_FORMAT_NAME_ENTRY
#undef ENUM_SUPPORTED_FORMATS
/**
* Uncompressed texture format handler.
*/
class FTextureFormatUncompressed : public ITextureFormat
{
virtual bool AllowParallelBuild() const override
{
return true;
}
virtual FName GetEncoderName(FName Format) const override
{
static const FName UncomName("Uncompressed");
return UncomName;
}
virtual uint16 GetVersion(
FName Format,
const FTextureBuildSettings* BuildSettings
) const override
{
return 0;
}
virtual FString GetDerivedDataKeyString(const FTextureBuildSettings& InBuildSettings) const override
{
if (InBuildSettings.TextureFormatName == GTextureFormatNameRGBA16F)
{
return TEXT("RGBA16F");
}
else if (InBuildSettings.TextureFormatName == GTextureFormatNameR16F)
{
return TEXT("R16F");
}
else
{
// default implementation of GetDerivedDataKeyString returns empty string
// match that so we don't change the DDC key
return TEXT("");
}
}
virtual void GetSupportedFormats(TArray<FName>& OutFormats) const override
{
for (int32 i = 0; i < UE_ARRAY_COUNT(GSupportedTextureFormatNames); ++i)
{
OutFormats.Add(GSupportedTextureFormatNames[i]);
}
}
virtual FTextureFormatCompressorCaps GetFormatCapabilities() const override
{
return FTextureFormatCompressorCaps(); // Default capabilities.
}
virtual EPixelFormat GetPixelFormatForImage(const FTextureBuildSettings& BuildSettings, const struct FImage& Image, bool bImageHasAlphaChannel) const override
{
if (BuildSettings.TextureFormatName == GTextureFormatNameG8)
{
return PF_G8;
}
else if (BuildSettings.TextureFormatName == GTextureFormatNameG16)
{
return PF_G16;
}
else if (BuildSettings.TextureFormatName == GTextureFormatNameVU8)
{
return PF_V8U8;
}
else if (BuildSettings.TextureFormatName == GTextureFormatNameBGRA8 || BuildSettings.TextureFormatName == GTextureFormatNameRGBA8 ||
BuildSettings.TextureFormatName == GTextureFormatNameXGXR8)
{
return PF_B8G8R8A8;
}
else if (BuildSettings.TextureFormatName == GTextureFormatNameRGBA16F)
{
return PF_FloatRGBA;
}
else if (BuildSettings.TextureFormatName == GTextureFormatNameR16F)
{
return PF_R16F;
}
else if (BuildSettings.TextureFormatName == GTextureFormatNamePOTERROR)
{
return PF_B8G8R8A8;
}
else if (BuildSettings.TextureFormatName == GTextureFormatNameR5G6B5)
{
return PF_R5G6B5_UNORM;
}
else if (BuildSettings.TextureFormatName == GTextureFormatNameA1RGB555 || BuildSettings.TextureFormatName == GTextureFormatNameRGB555A1)
{
return PF_B5G5R5A1_UNORM;
}
UE_LOG(LogTextureFormatUncompressed, Fatal, TEXT("Unhandled texture format '%s' given to FTextureFormatUncompressed::GetPixelFormatForImage()"), *BuildSettings.TextureFormatName.ToString());
return PF_Unknown;
}
virtual bool CompressImage(
const FImage& InImage,
const FTextureBuildSettings& BuildSettings,
FStringView DebugTexturePathName,
bool bImageHasAlphaChannel,
FCompressedImage2D& OutCompressedImage
) const override
{
OutCompressedImage.PixelFormat = GetPixelFormatForImage(BuildSettings, InImage, bImageHasAlphaChannel);
if (BuildSettings.TextureFormatName == GTextureFormatNameG8)
{
FImage Image;
InImage.CopyTo(Image, ERawImageFormat::G8, BuildSettings.GetDestGammaSpace());
OutCompressedImage.SizeX = Image.SizeX;
OutCompressedImage.SizeY = Image.SizeY;
OutCompressedImage.SizeZ = (BuildSettings.bVolume || BuildSettings.bTextureArray) ? Image.NumSlices : 1;
OutCompressedImage.RawData = MoveTemp(Image.RawData);
return true;
}
else if (BuildSettings.TextureFormatName == GTextureFormatNameG16)
{
FImage Image;
InImage.CopyTo(Image, ERawImageFormat::G16, EGammaSpace::Linear);
OutCompressedImage.SizeX = Image.SizeX;
OutCompressedImage.SizeY = Image.SizeY;
OutCompressedImage.SizeZ = BuildSettings.bVolume ? Image.NumSlices : 1;
OutCompressedImage.RawData = MoveTemp(Image.RawData);
return true;
}
else if (BuildSettings.TextureFormatName == GTextureFormatNameVU8)
{
FImage Image;
InImage.CopyTo(Image, ERawImageFormat::BGRA8, BuildSettings.GetDestGammaSpace());
OutCompressedImage.SizeX = Image.SizeX;
OutCompressedImage.SizeY = Image.SizeY;
OutCompressedImage.SizeZ = (BuildSettings.bVolume || BuildSettings.bTextureArray) ? Image.NumSlices : 1;
uint64 NumTexels = (uint64)Image.SizeX * Image.SizeY * Image.NumSlices;
OutCompressedImage.RawData.Empty(NumTexels * 2);
OutCompressedImage.RawData.AddUninitialized(NumTexels * 2);
const FColor* FirstColor = (&Image.AsBGRA8()[0]);
const FColor* LastColor = FirstColor + NumTexels;
int8* Dest = (int8*)OutCompressedImage.RawData.GetData();
for (const FColor* Color = FirstColor; Color < LastColor; ++Color)
{
*Dest++ = (int32)Color->R - 128;
*Dest++ = (int32)Color->G - 128;
}
return true;
}
else if (BuildSettings.TextureFormatName == GTextureFormatNameBGRA8)
{
FImage Image;
InImage.CopyTo(Image, ERawImageFormat::BGRA8, BuildSettings.GetDestGammaSpace());
OutCompressedImage.SizeX = Image.SizeX;
OutCompressedImage.SizeY = Image.SizeY;
OutCompressedImage.SizeZ = (BuildSettings.bVolume || BuildSettings.bTextureArray) ? Image.NumSlices : 1;
OutCompressedImage.RawData = MoveTemp(Image.RawData);
return true;
}
else if (BuildSettings.TextureFormatName == GTextureFormatNameRGBA8)
{
FImage Image;
InImage.CopyTo(Image, ERawImageFormat::BGRA8, BuildSettings.GetDestGammaSpace());
OutCompressedImage.SizeX = Image.SizeX;
OutCompressedImage.SizeY = Image.SizeY;
OutCompressedImage.SizeZ = (BuildSettings.bVolume || BuildSettings.bTextureArray) ? Image.NumSlices : 1;
// swizzle each texel
uint64 NumTexels = (uint64)Image.SizeX * Image.SizeY * Image.NumSlices;
OutCompressedImage.RawData.Empty(NumTexels * 4);
OutCompressedImage.RawData.AddUninitialized(NumTexels * 4);
const FColor* FirstColor = (&Image.AsBGRA8()[0]);
const FColor* LastColor = FirstColor + NumTexels;
uint8* Dest = OutCompressedImage.RawData.GetData();
for (const FColor* Color = FirstColor; Color < LastColor; ++Color)
{
*Dest++ = Color->R;
*Dest++ = Color->G;
*Dest++ = Color->B;
*Dest++ = Color->A;
}
return true;
}
else if (BuildSettings.TextureFormatName == GTextureFormatNameXGXR8)
{
FImage Image;
InImage.CopyTo(Image, ERawImageFormat::BGRA8, BuildSettings.GetDestGammaSpace());
OutCompressedImage.SizeX = Image.SizeX;
OutCompressedImage.SizeY = Image.SizeY;
OutCompressedImage.SizeZ = (BuildSettings.bVolume || BuildSettings.bTextureArray) ? Image.NumSlices : 1;
// swizzle each texel
uint64 NumTexels = (uint64)Image.SizeX * Image.SizeY * Image.NumSlices;
OutCompressedImage.RawData.Empty(NumTexels * 4);
OutCompressedImage.RawData.AddUninitialized(NumTexels * 4);
const FColor* FirstColor = (&Image.AsBGRA8()[0]);
const FColor* LastColor = FirstColor + NumTexels;
uint8* Dest = OutCompressedImage.RawData.GetData();
for (const FColor* Color = FirstColor; Color < LastColor; ++Color)
{
*Dest++ = Color->B;
*Dest++ = Color->G;
*Dest++ = Color->A;
*Dest++ = Color->R;
}
return true;
}
else if (BuildSettings.TextureFormatName == GTextureFormatNameRGBA16F)
{
FImage Image;
InImage.CopyTo(Image, ERawImageFormat::RGBA16F, EGammaSpace::Linear);
OutCompressedImage.SizeX = Image.SizeX;
OutCompressedImage.SizeY = Image.SizeY;
OutCompressedImage.SizeZ = (BuildSettings.bVolume || BuildSettings.bTextureArray) ? Image.NumSlices : 1;
OutCompressedImage.RawData = MoveTemp(Image.RawData);
return true;
}
else if (BuildSettings.TextureFormatName == GTextureFormatNameR16F)
{
FImage Image;
InImage.CopyTo(Image, ERawImageFormat::R16F, EGammaSpace::Linear);
OutCompressedImage.SizeX = Image.SizeX;
OutCompressedImage.SizeY = Image.SizeY;
OutCompressedImage.SizeZ = BuildSettings.bVolume ? Image.NumSlices : 1;
OutCompressedImage.RawData = MoveTemp(Image.RawData);
return true;
}
else if (BuildSettings.TextureFormatName == GTextureFormatNamePOTERROR)
{
// load the error image data we will just repeat into the texture
TArray64<uint8> ErrorData;
FFileHelper::LoadFileToArray(ErrorData, *(FPaths::EngineDir() / TEXT("Content/MobileResources/PowerOfTwoError64x64.raw")));
check(ErrorData.Num() == 16384);
// set output
OutCompressedImage.SizeX = InImage.SizeX;
OutCompressedImage.SizeY = InImage.SizeY;
OutCompressedImage.SizeZ = (BuildSettings.bVolume || BuildSettings.bTextureArray) ? InImage.NumSlices : 1;
// allocate output memory
check(InImage.NumSlices == 1);
uint64 NumTexels = (uint64)InImage.SizeX * InImage.SizeY;
OutCompressedImage.RawData.Empty(NumTexels * 4);
OutCompressedImage.RawData.AddUninitialized(NumTexels * 4);
// write out texels
uint8* Src = ErrorData.GetData();
uint8* Dest = OutCompressedImage.RawData.GetData();
for (int32 Y = 0; Y < InImage.SizeY; Y++)
{
for (int32 X = 0; X < InImage.SizeX * 4; X++)
{
int32 SrcX = X & (64 * 4 - 1);
int32 SrcY = Y & 63;
Dest[(uint64)Y * InImage.SizeX * 4 + X] = Src[(uint64)SrcY * 64 * 4 + SrcX];
}
}
return true;
}
else if (BuildSettings.TextureFormatName == GTextureFormatNameR5G6B5 || BuildSettings.TextureFormatName == GTextureFormatNameRGB555A1 || BuildSettings.TextureFormatName == GTextureFormatNameA1RGB555)
{
FImage Image;
InImage.CopyTo(Image, ERawImageFormat::BGRA8, BuildSettings.GetDestGammaSpace());
OutCompressedImage.SizeX = Image.SizeX;
OutCompressedImage.SizeY = Image.SizeY;
OutCompressedImage.SizeZ = (BuildSettings.bVolume || BuildSettings.bTextureArray) ? Image.NumSlices : 1;
// swizzle each texel
uint64 NumTexels = (uint64)Image.SizeX * Image.SizeY * Image.NumSlices;
OutCompressedImage.RawData.Empty(NumTexels * 2);
OutCompressedImage.RawData.AddUninitialized(NumTexels * 2);
const FColor* FirstColor = (&Image.AsBGRA8()[0]);
const FColor* LastColor = FirstColor + NumTexels;
uint16* Dest = (uint16*)OutCompressedImage.RawData.GetData();
if(BuildSettings.TextureFormatName == GTextureFormatNameR5G6B5)
{
for (const FColor* Color = FirstColor; Color < LastColor; ++Color)
{
uint16 BGR565 = (uint16(Color->R >> 3) << 11) | (uint16(Color->G >> 2) << 5) | uint16(Color->B >> 3);
*Dest++ = BGR565;
}
}
else if(BuildSettings.TextureFormatName == GTextureFormatNameA1RGB555)
{
for (const FColor* Color = FirstColor; Color < LastColor; ++Color)
{
//most rhi supports alpha on the highest bit
uint16 BGR555A1 = (uint16(Color->A >> 7) << 15) | (uint16(Color->R >> 3) << 10) | (uint16(Color->G >> 3) << 5) | uint16(Color->B >> 3);
*Dest++ = BGR555A1;
}
}
else
{
for (const FColor* Color = FirstColor; Color < LastColor; ++Color)
{
//OpenGL GL_RGB5_A1 only supports alpha on the lowest bit
uint16 BGR555A1 = (uint16(Color->R >> 3) << 11) | (uint16(Color->G >> 3) << 6) | (uint16(Color->B >> 3) << 1) | uint16(Color->A >> 7);
*Dest++ = BGR555A1;
}
}
return true;
}
UE_LOG(LogTextureFormatUncompressed, Warning,
TEXT("Cannot convert uncompressed image to format '%s'."),
*BuildSettings.TextureFormatName.ToString()
);
return false;
}
};
/**
* Module for uncompressed texture formats.
*/
static ITextureFormat* Singleton = NULL;
class FTextureFormatUncompressedModule : public ITextureFormatModule
{
public:
virtual ~FTextureFormatUncompressedModule()
{
delete Singleton;
Singleton = NULL;
}
virtual bool CanCallGetTextureFormats() override { return false; }
virtual ITextureFormat* GetTextureFormat()
{
if (!Singleton)
{
Singleton = new FTextureFormatUncompressed();
}
return Singleton;
}
static inline UE::DerivedData::TBuildFunctionFactory<FUncompressedTextureBuildFunction> BuildFunctionFactory;
};
IMPLEMENT_MODULE(FTextureFormatUncompressedModule, TextureFormatUncompressed);