You've already forked UnrealEngineUWP
mirror of
https://github.com/izzy2lost/UnrealEngineUWP.git
synced 2026-03-26 18:15:20 -07:00
#fyi Dan.Thompson Original CL Desc ----------------------------------------------------------------- Plumb the mip layout down to the child texture ddc key versions as certain child formats need to know mip dimensions in order to know if aspects of the build affect the textures. #rb fabian.giesen #rb charles.bloom #preflight 641378f03f3d31c94a2e86b5 #preflight 64149d297d1280ffe750d78e [CL 24691763 by Dan Thompson in ue5-main branch]
475 lines
15 KiB
C++
475 lines
15 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(RGBA32F) \
|
|
op(XGXR8) \
|
|
op(RGBA8) \
|
|
op(POTERROR) \
|
|
op(R16F) \
|
|
op(R32F) \
|
|
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 == GTextureFormatNameRGBA32F)
|
|
{
|
|
return TEXT("RGBA32F");
|
|
}
|
|
else if (InBuildSettings.TextureFormatName == GTextureFormatNameR16F)
|
|
{
|
|
return TEXT("R16F");
|
|
}
|
|
else if (InBuildSettings.TextureFormatName == GTextureFormatNameR32F)
|
|
{
|
|
return TEXT("R32F");
|
|
}
|
|
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 EPixelFormat GetEncodedPixelFormat(const FTextureBuildSettings& BuildSettings, 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 == GTextureFormatNameRGBA32F)
|
|
{
|
|
return PF_A32B32G32R32F;
|
|
}
|
|
else if (BuildSettings.TextureFormatName == GTextureFormatNameR16F)
|
|
{
|
|
return PF_R16F;
|
|
}
|
|
else if (BuildSettings.TextureFormatName == GTextureFormatNameR32F)
|
|
{
|
|
return PF_R32_FLOAT;
|
|
}
|
|
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::GetEncodedPixelFormat()"), *BuildSettings.TextureFormatName.ToString());
|
|
return PF_Unknown;
|
|
}
|
|
|
|
virtual bool CompressImage(
|
|
FImage& InImage,
|
|
const FTextureBuildSettings& BuildSettings,
|
|
const FIntVector3& InMip0Dimensions,
|
|
int32 InMip0NumSlicesNoDepth,
|
|
int32 InMipIndex,
|
|
int32 InMipCount,
|
|
FStringView DebugTexturePathName,
|
|
bool bImageHasAlphaChannel,
|
|
FCompressedImage2D& OutCompressedImage
|
|
) const override
|
|
{
|
|
OutCompressedImage.PixelFormat = GetEncodedPixelFormat(BuildSettings, bImageHasAlphaChannel);
|
|
|
|
// @todo Oodle : fix me : lots of pointless code dupe here
|
|
// most of these should just map the Name to an ERawImageFormat and do CopyImage
|
|
|
|
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;
|
|
|
|
// BGRA to RGBA : @todo Oodle : just use ImageCore CopyImageRGBABGRA
|
|
// 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 == GTextureFormatNameRGBA32F)
|
|
{
|
|
FImage Image;
|
|
InImage.CopyTo(Image, ERawImageFormat::RGBA32F, 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 || BuildSettings.bTextureArray) ? Image.NumSlices : 1;
|
|
OutCompressedImage.RawData = MoveTemp(Image.RawData);
|
|
|
|
return true;
|
|
}
|
|
else if (BuildSettings.TextureFormatName == GTextureFormatNameR32F)
|
|
{
|
|
FImage Image;
|
|
InImage.CopyTo(Image, ERawImageFormat::R32F, 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 == 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);
|
|
|