You've already forked UnrealEngineUWP
mirror of
https://github.com/izzy2lost/UnrealEngineUWP.git
synced 2026-03-26 18:15:20 -07:00
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]
430 lines
14 KiB
C++
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);
|
|
|