2022-06-22 20:21:16 -04:00
// Copyright Epic Games, Inc. All Rights Reserved.
# include "TextureBuildFunction.h"
# include "DerivedDataCache.h"
# include "DerivedDataValueId.h"
# include "Engine/TextureDefines.h"
# include "IImageWrapper.h"
# include "IImageWrapperModule.h"
# include "ImageCore.h"
# include "ImageCoreUtils.h"
# include "Interfaces/ITextureFormat.h"
# include "Interfaces/ITextureFormatModule.h"
2022-11-18 17:09:58 -05:00
# include "IO/IoHash.h"
2022-06-22 20:21:16 -04:00
# include "Memory/CompositeBuffer.h"
# include "Memory/SharedBuffer.h"
# include "Modules/ModuleManager.h"
# include "PixelFormat.h"
# include "Serialization/CompactBinary.h"
# include "Serialization/CompactBinaryWriter.h"
# include "Serialization/FileRegions.h"
# include "Serialization/MemoryWriter.h"
2022-08-23 12:41:58 -04:00
# include "TextureBuildUtilities.h"
2022-06-22 20:21:16 -04:00
# include "TextureCompressorModule.h"
# include "TextureFormatManager.h"
DEFINE_LOG_CATEGORY_STATIC ( LogTextureBuildFunction , Log , All ) ;
// Any edits to the texture compressor or this file that will change the output of texture builds
// MUST have a corresponding change to this version. Individual texture formats have a version to
// change that is specific to the format. A merge conflict affecting the version MUST be resolved
2022-11-18 17:09:58 -05:00
// by generating a new version. This also includes the addition of new outputs to the build, as
// this will cause a DDC verification failure unless a new version is created.
// A reminder that for DDC invalidation, running a ddc fill job or the ddc commandlet is a friendly
// thing to do! -run=DerivedDataCache -Fill -TargetPlatform=Platform1,Platform...N
//
static const FGuid TextureBuildFunctionVersion ( TEXT ( " B20676CE-A786-43EE-96F0-2620A4C38ACA " ) ) ;
2022-06-22 20:21:16 -04:00
2024-01-22 10:12:42 -05:00
// This is mirrored in TextureDerivedData.cpp
static const FGuid TextureMetadataDerivedDataVer ( 0xB9106D68 , 0xA61B4F2A , 0x8105E16F , 0x48799976 ) ;
2022-06-22 20:21:16 -04:00
static void ReadCbField ( FCbFieldView Field , bool & OutValue ) { OutValue = Field . AsBool ( OutValue ) ; }
static void ReadCbField ( FCbFieldView Field , int32 & OutValue ) { OutValue = Field . AsInt32 ( OutValue ) ; }
static void ReadCbField ( FCbFieldView Field , uint8 & OutValue ) { OutValue = Field . AsUInt8 ( OutValue ) ; }
static void ReadCbField ( FCbFieldView Field , uint32 & OutValue ) { OutValue = Field . AsUInt32 ( OutValue ) ; }
static void ReadCbField ( FCbFieldView Field , float & OutValue ) { OutValue = Field . AsFloat ( OutValue ) ; }
static void ReadCbField ( FCbFieldView Field , FGuid & OutValue ) { OutValue = Field . AsUuid ( ) ; }
static void ReadCbField ( FCbFieldView Field , FName & OutValue )
{
if ( Field . IsString ( ) )
{
OutValue = FName ( FUTF8ToTCHAR ( Field . AsString ( ) ) ) ;
}
}
static void ReadCbField ( FCbFieldView Field , FColor & OutValue )
{
FCbFieldViewIterator It = Field . AsArrayView ( ) . CreateViewIterator ( ) ;
OutValue . A = It + + - > AsUInt8 ( OutValue . A ) ;
OutValue . R = It + + - > AsUInt8 ( OutValue . R ) ;
OutValue . G = It + + - > AsUInt8 ( OutValue . G ) ;
OutValue . B = It + + - > AsUInt8 ( OutValue . B ) ;
}
static void ReadCbField ( FCbFieldView Field , FVector2f & OutValue )
{
FCbFieldViewIterator It = Field . AsArrayView ( ) . CreateViewIterator ( ) ;
OutValue . X = It + + - > AsFloat ( OutValue . X ) ;
OutValue . Y = It + + - > AsFloat ( OutValue . Y ) ;
}
static void ReadCbField ( FCbFieldView Field , FVector4f & OutValue )
{
FCbFieldViewIterator It = Field . AsArrayView ( ) . CreateViewIterator ( ) ;
OutValue . X = It + + - > AsFloat ( OutValue . X ) ;
OutValue . Y = It + + - > AsFloat ( OutValue . Y ) ;
OutValue . Z = It + + - > AsFloat ( OutValue . Z ) ;
OutValue . W = It + + - > AsFloat ( OutValue . W ) ;
}
static void ReadCbField ( FCbFieldView Field , FIntPoint & OutValue )
{
FCbFieldViewIterator It = Field . AsArrayView ( ) . CreateViewIterator ( ) ;
OutValue . X = It + + - > AsInt32 ( OutValue . X ) ;
OutValue . Y = It + + - > AsInt32 ( OutValue . Y ) ;
}
static FTextureBuildSettings ReadBuildSettingsFromCompactBinary ( const FCbObjectView & Object )
{
FTextureBuildSettings BuildSettings ;
BuildSettings . FormatConfigOverride = Object [ " FormatConfigOverride " ] . AsObjectView ( ) ;
FCbObjectView ColorAdjustmentCbObj = Object [ " ColorAdjustment " ] . AsObjectView ( ) ;
FColorAdjustmentParameters & ColorAdjustment = BuildSettings . ColorAdjustment ;
ReadCbField ( ColorAdjustmentCbObj [ " AdjustBrightness " ] , ColorAdjustment . AdjustBrightness ) ;
ReadCbField ( ColorAdjustmentCbObj [ " AdjustBrightnessCurve " ] , ColorAdjustment . AdjustBrightnessCurve ) ;
ReadCbField ( ColorAdjustmentCbObj [ " AdjustSaturation " ] , ColorAdjustment . AdjustSaturation ) ;
ReadCbField ( ColorAdjustmentCbObj [ " AdjustVibrance " ] , ColorAdjustment . AdjustVibrance ) ;
ReadCbField ( ColorAdjustmentCbObj [ " AdjustRGBCurve " ] , ColorAdjustment . AdjustRGBCurve ) ;
ReadCbField ( ColorAdjustmentCbObj [ " AdjustHue " ] , ColorAdjustment . AdjustHue ) ;
ReadCbField ( ColorAdjustmentCbObj [ " AdjustMinAlpha " ] , ColorAdjustment . AdjustMinAlpha ) ;
ReadCbField ( ColorAdjustmentCbObj [ " AdjustMaxAlpha " ] , ColorAdjustment . AdjustMaxAlpha ) ;
BuildSettings . bUseNewMipFilter = Object [ " bUseNewMipFilter " ] . AsBool ( BuildSettings . bUseNewMipFilter ) ;
2022-09-15 10:49:25 -04:00
BuildSettings . bNormalizeNormals = Object [ " bNormalizeNormals " ] . AsBool ( BuildSettings . bNormalizeNormals ) ;
2022-06-22 20:21:16 -04:00
BuildSettings . bDoScaleMipsForAlphaCoverage = Object [ " bDoScaleMipsForAlphaCoverage " ] . AsBool ( BuildSettings . bDoScaleMipsForAlphaCoverage ) ;
ReadCbField ( Object [ " AlphaCoverageThresholds " ] , BuildSettings . AlphaCoverageThresholds ) ;
ReadCbField ( Object [ " MipSharpening " ] , BuildSettings . MipSharpening ) ;
ReadCbField ( Object [ " DiffuseConvolveMipLevel " ] , BuildSettings . DiffuseConvolveMipLevel ) ;
ReadCbField ( Object [ " SharpenMipKernelSize " ] , BuildSettings . SharpenMipKernelSize ) ;
ReadCbField ( Object [ " MaxTextureResolution " ] , BuildSettings . MaxTextureResolution ) ;
check ( BuildSettings . MaxTextureResolution ! = 0 ) ;
ReadCbField ( Object [ " TextureFormatName " ] , BuildSettings . TextureFormatName ) ;
ReadCbField ( Object [ " bHDRSource " ] , BuildSettings . bHDRSource ) ;
ReadCbField ( Object [ " MipGenSettings " ] , BuildSettings . MipGenSettings ) ;
BuildSettings . bCubemap = Object [ " bCubemap " ] . AsBool ( BuildSettings . bCubemap ) ;
BuildSettings . bTextureArray = Object [ " bTextureArray " ] . AsBool ( BuildSettings . bTextureArray ) ;
BuildSettings . bVolume = Object [ " bVolume " ] . AsBool ( BuildSettings . bVolume ) ;
BuildSettings . bLongLatSource = Object [ " bLongLatSource " ] . AsBool ( BuildSettings . bLongLatSource ) ;
BuildSettings . bSRGB = Object [ " bSRGB " ] . AsBool ( BuildSettings . bSRGB ) ;
ReadCbField ( Object [ " SourceEncodingOverride " ] , BuildSettings . SourceEncodingOverride ) ;
BuildSettings . bHasColorSpaceDefinition = Object [ " bHasColorSpaceDefinition " ] . AsBool ( BuildSettings . bHasColorSpaceDefinition ) ;
ReadCbField ( Object [ " RedChromaticityCoordinate " ] , BuildSettings . RedChromaticityCoordinate ) ;
ReadCbField ( Object [ " GreenChromaticityCoordinate " ] , BuildSettings . GreenChromaticityCoordinate ) ;
ReadCbField ( Object [ " BlueChromaticityCoordinate " ] , BuildSettings . BlueChromaticityCoordinate ) ;
ReadCbField ( Object [ " WhiteChromaticityCoordinate " ] , BuildSettings . WhiteChromaticityCoordinate ) ;
ReadCbField ( Object [ " ChromaticAdaptationMethod " ] , BuildSettings . ChromaticAdaptationMethod ) ;
BuildSettings . bUseLegacyGamma = Object [ " bUseLegacyGamma " ] . AsBool ( BuildSettings . bUseLegacyGamma ) ;
BuildSettings . bPreserveBorder = Object [ " bPreserveBorder " ] . AsBool ( BuildSettings . bPreserveBorder ) ;
BuildSettings . bForceNoAlphaChannel = Object [ " bForceNoAlphaChannel " ] . AsBool ( BuildSettings . bForceNoAlphaChannel ) ;
BuildSettings . bForceAlphaChannel = Object [ " bForceAlphaChannel " ] . AsBool ( BuildSettings . bForceAlphaChannel ) ;
BuildSettings . bComputeBokehAlpha = Object [ " bComputeBokehAlpha " ] . AsBool ( BuildSettings . bComputeBokehAlpha ) ;
BuildSettings . bReplicateRed = Object [ " bReplicateRed " ] . AsBool ( BuildSettings . bReplicateRed ) ;
BuildSettings . bReplicateAlpha = Object [ " bReplicateAlpha " ] . AsBool ( BuildSettings . bReplicateAlpha ) ;
BuildSettings . bDownsampleWithAverage = Object [ " bDownsampleWithAverage " ] . AsBool ( BuildSettings . bDownsampleWithAverage ) ;
BuildSettings . bSharpenWithoutColorShift = Object [ " bSharpenWithoutColorShift " ] . AsBool ( BuildSettings . bSharpenWithoutColorShift ) ;
BuildSettings . bBorderColorBlack = Object [ " bBorderColorBlack " ] . AsBool ( BuildSettings . bBorderColorBlack ) ;
BuildSettings . bFlipGreenChannel = Object [ " bFlipGreenChannel " ] . AsBool ( BuildSettings . bFlipGreenChannel ) ;
BuildSettings . bApplyYCoCgBlockScale = Object [ " bApplyYCoCgBlockScale " ] . AsBool ( BuildSettings . bApplyYCoCgBlockScale ) ;
BuildSettings . bApplyKernelToTopMip = Object [ " bApplyKernelToTopMip " ] . AsBool ( BuildSettings . bApplyKernelToTopMip ) ;
BuildSettings . bRenormalizeTopMip = Object [ " bRenormalizeTopMip " ] . AsBool ( BuildSettings . bRenormalizeTopMip ) ;
2023-07-27 16:46:23 -04:00
BuildSettings . bCPUAccessible = Object [ " bCPUAccessible " ] . AsBool ( BuildSettings . bCPUAccessible ) ;
2022-06-22 20:21:16 -04:00
ReadCbField ( Object [ " CompositeTextureMode " ] , BuildSettings . CompositeTextureMode ) ;
ReadCbField ( Object [ " CompositePower " ] , BuildSettings . CompositePower ) ;
ReadCbField ( Object [ " LODBias " ] , BuildSettings . LODBias ) ;
ReadCbField ( Object [ " LODBiasWithCinematicMips " ] , BuildSettings . LODBiasWithCinematicMips ) ;
2022-08-23 12:41:58 -04:00
BuildSettings . bStreamable_Unused = Object [ " bStreamable " ] . AsBool ( BuildSettings . bStreamable_Unused ) ;
2022-06-22 20:21:16 -04:00
BuildSettings . bVirtualStreamable = Object [ " bVirtualStreamable " ] . AsBool ( BuildSettings . bVirtualStreamable ) ;
BuildSettings . bChromaKeyTexture = Object [ " bChromaKeyTexture " ] . AsBool ( BuildSettings . bChromaKeyTexture ) ;
ReadCbField ( Object [ " PowerOfTwoMode " ] , BuildSettings . PowerOfTwoMode ) ;
ReadCbField ( Object [ " PaddingColor " ] , BuildSettings . PaddingColor ) ;
2023-09-26 18:49:57 -04:00
BuildSettings . bPadWithBorderColor = Object [ " bPadWithBorderColor " ] . AsBool ( BuildSettings . bPadWithBorderColor ) ;
2023-10-03 14:56:03 -04:00
ReadCbField ( Object [ " ResizeDuringBuildX " ] , BuildSettings . ResizeDuringBuildX ) ;
ReadCbField ( Object [ " ResizeDuringBuildY " ] , BuildSettings . ResizeDuringBuildY ) ;
2022-06-22 20:21:16 -04:00
ReadCbField ( Object [ " ChromaKeyColor " ] , BuildSettings . ChromaKeyColor ) ;
ReadCbField ( Object [ " ChromaKeyThreshold " ] , BuildSettings . ChromaKeyThreshold ) ;
ReadCbField ( Object [ " CompressionQuality " ] , BuildSettings . CompressionQuality ) ;
ReadCbField ( Object [ " LossyCompressionAmount " ] , BuildSettings . LossyCompressionAmount ) ;
ReadCbField ( Object [ " Downscale " ] , BuildSettings . Downscale ) ;
ReadCbField ( Object [ " DownscaleOptions " ] , BuildSettings . DownscaleOptions ) ;
ReadCbField ( Object [ " VirtualAddressingModeX " ] , BuildSettings . VirtualAddressingModeX ) ;
ReadCbField ( Object [ " VirtualAddressingModeY " ] , BuildSettings . VirtualAddressingModeY ) ;
ReadCbField ( Object [ " VirtualTextureTileSize " ] , BuildSettings . VirtualTextureTileSize ) ;
ReadCbField ( Object [ " VirtualTextureBorderSize " ] , BuildSettings . VirtualTextureBorderSize ) ;
BuildSettings . OodleEncodeEffort = Object [ " OodleEncodeEffort " ] . AsUInt8 ( BuildSettings . OodleEncodeEffort ) ;
BuildSettings . OodleUniversalTiling = Object [ " OodleUniversalTiling " ] . AsUInt8 ( BuildSettings . OodleUniversalTiling ) ;
BuildSettings . bOodleUsesRDO = Object [ " bOodleUsesRDO " ] . AsBool ( BuildSettings . bOodleUsesRDO ) ;
BuildSettings . OodleRDO = Object [ " OodleRDO " ] . AsUInt8 ( BuildSettings . OodleRDO ) ;
2023-09-01 19:25:25 -04:00
BuildSettings . bOodlePreserveExtremes = Object [ " bOodlePreserveExtremes " ] . AsBool ( BuildSettings . bOodlePreserveExtremes ) ;
2022-06-22 20:21:16 -04:00
ReadCbField ( Object [ " OodleTextureSdkVersion " ] , BuildSettings . OodleTextureSdkVersion ) ;
2022-11-02 13:53:39 -04:00
ReadCbField ( Object [ " TextureAddressModeX " ] , BuildSettings . TextureAddressModeX ) ;
ReadCbField ( Object [ " TextureAddressModeY " ] , BuildSettings . TextureAddressModeY ) ;
ReadCbField ( Object [ " TextureAddressModeZ " ] , BuildSettings . TextureAddressModeZ ) ;
2022-06-22 20:21:16 -04:00
return BuildSettings ;
}
static ERawImageFormat : : Type ComputeRawImageFormat ( ETextureSourceFormat SourceFormat )
{
return FImageCoreUtils : : ConvertToRawImageFormat ( SourceFormat ) ;
}
static bool TryReadTextureSourceFromCompactBinary ( FCbFieldView Source , UE : : DerivedData : : FBuildContext & Context ,
const FTextureBuildSettings & BuildSettings , TArray < FImage > & OutMips )
{
FSharedBuffer InputBuffer = Context . FindInput ( Source . GetName ( ) ) ;
if ( ! InputBuffer )
{
UE_LOG ( LogTextureBuildFunction , Error , TEXT ( " Missing input '%s'. " ) , * WriteToString < 64 > ( Source . GetName ( ) ) ) ;
return false ;
}
if ( InputBuffer . GetSize ( ) = = 0 )
{
UE_LOG ( LogTextureBuildFunction , Error , TEXT ( " Input size zero '%s'. " ) , * WriteToString < 64 > ( Source . GetName ( ) ) ) ;
return false ;
}
ETextureSourceCompressionFormat CompressionFormat = ( ETextureSourceCompressionFormat ) Source [ " CompressionFormat " ] . AsUInt8 ( ) ;
ETextureSourceFormat SourceFormat = ( ETextureSourceFormat ) Source [ " SourceFormat " ] . AsUInt8 ( ) ;
ERawImageFormat : : Type RawImageFormat = ComputeRawImageFormat ( SourceFormat ) ;
EGammaSpace GammaSpace = ( EGammaSpace ) Source [ " GammaSpace " ] . AsUInt8 ( ) ;
int32 NumSlices = Source [ " NumSlices " ] . AsInt32 ( ) ;
int32 SizeX = Source [ " SizeX " ] . AsInt32 ( ) ;
int32 SizeY = Source [ " SizeY " ] . AsInt32 ( ) ;
int32 MipSizeX = SizeX ;
int32 MipSizeY = SizeY ;
const uint8 * DecompressedSourceData = ( const uint8 * ) InputBuffer . GetData ( ) ;
int64 DecompressedSourceDataSize = InputBuffer . GetSize ( ) ;
TArray64 < uint8 > IntermediateDecompressedData ;
if ( CompressionFormat ! = TSCF_None )
{
switch ( CompressionFormat )
{
case TSCF_JPEG :
{
TSharedPtr < IImageWrapper > ImageWrapper = FModuleManager : : GetModuleChecked < IImageWrapperModule > ( FName ( " ImageWrapper " ) ) . CreateImageWrapper ( EImageFormat : : JPEG ) ;
ImageWrapper - > SetCompressed ( ( const uint8 * ) InputBuffer . GetData ( ) , InputBuffer . GetSize ( ) ) ;
ImageWrapper - > GetRaw ( SourceFormat = = TSF_G8 ? ERGBFormat : : Gray : ERGBFormat : : BGRA , 8 , IntermediateDecompressedData ) ;
}
break ;
2023-10-30 12:46:08 -04:00
case TSCF_UEJPEG :
2023-09-13 14:47:22 -04:00
{
2023-10-30 12:46:08 -04:00
TSharedPtr < IImageWrapper > ImageWrapper = FModuleManager : : GetModuleChecked < IImageWrapperModule > ( FName ( " ImageWrapper " ) ) . CreateImageWrapper ( EImageFormat : : UEJPEG ) ;
2023-09-13 14:47:22 -04:00
ImageWrapper - > SetCompressed ( ( const uint8 * ) InputBuffer . GetData ( ) , InputBuffer . GetSize ( ) ) ;
ImageWrapper - > GetRaw ( SourceFormat = = TSF_G8 ? ERGBFormat : : Gray : ERGBFormat : : BGRA , 8 , IntermediateDecompressedData ) ;
}
break ;
2022-06-22 20:21:16 -04:00
case TSCF_PNG :
{
TSharedPtr < IImageWrapper > ImageWrapper = FModuleManager : : GetModuleChecked < IImageWrapperModule > ( FName ( " ImageWrapper " ) ) . CreateImageWrapper ( EImageFormat : : PNG ) ;
ImageWrapper - > SetCompressed ( ( const uint8 * ) InputBuffer . GetData ( ) , InputBuffer . GetSize ( ) ) ;
ERGBFormat RawFormat = ( SourceFormat = = TSF_G8 | | SourceFormat = = TSF_G16 ) ? ERGBFormat : : Gray : ERGBFormat : : RGBA ;
ImageWrapper - > GetRaw ( RawFormat , ( SourceFormat = = TSF_G16 | | SourceFormat = = TSF_RGBA16 ) ? 16 : 8 , IntermediateDecompressedData ) ;
}
break ;
default :
UE_LOG ( LogTextureBuildFunction , Error , TEXT ( " Unexpected source compression format encountered while attempting to build a texture. " ) ) ;
return false ;
}
DecompressedSourceData = IntermediateDecompressedData . GetData ( ) ;
DecompressedSourceDataSize = IntermediateDecompressedData . Num ( ) ;
InputBuffer . Reset ( ) ;
}
FCbArrayView MipsCbArrayView = Source [ " Mips " ] . AsArrayView ( ) ;
2023-05-17 15:57:20 -04:00
OutMips . Reserve ( IntCastChecked < int32 > ( MipsCbArrayView . Num ( ) ) ) ;
2022-06-22 20:21:16 -04:00
for ( FCbFieldView MipsCbArrayIt : MipsCbArrayView )
{
FCbObjectView MipCbObjectView = MipsCbArrayIt . AsObjectView ( ) ;
int64 MipOffset = MipCbObjectView [ " Offset " ] . AsInt64 ( ) ;
int64 MipSize = MipCbObjectView [ " Size " ] . AsInt64 ( ) ;
2023-05-09 05:17:49 -04:00
FImage & SourceMip = OutMips . Emplace_GetRef (
2022-06-22 20:21:16 -04:00
MipSizeX , MipSizeY ,
NumSlices ,
RawImageFormat ,
GammaSpace
) ;
check ( MipOffset + MipSize < = DecompressedSourceDataSize ) ;
2023-05-09 05:17:49 -04:00
check ( SourceMip . GetImageSizeBytes ( ) = = MipSize ) ;
2022-06-22 20:21:16 -04:00
if ( ( MipsCbArrayView . Num ( ) = = 1 ) & & ( CompressionFormat ! = TSCF_None ) )
{
// In the case where there is only one mip and its already in a TArray, there is no need to allocate new array contents, just use a move instead
check ( MipOffset = = 0 ) ;
2023-05-09 05:17:49 -04:00
SourceMip . RawData = MoveTemp ( IntermediateDecompressedData ) ;
2022-06-22 20:21:16 -04:00
}
else
{
2023-05-09 05:17:49 -04:00
SourceMip . RawData . Reset ( MipSize ) ;
SourceMip . RawData . AddUninitialized ( MipSize ) ;
2022-06-22 20:21:16 -04:00
FMemory : : Memcpy (
2023-05-09 05:17:49 -04:00
SourceMip . RawData . GetData ( ) ,
2022-06-22 20:21:16 -04:00
DecompressedSourceData + MipOffset ,
MipSize
) ;
}
MipSizeX = FMath : : Max ( MipSizeX / 2 , 1 ) ;
MipSizeY = FMath : : Max ( MipSizeY / 2 , 1 ) ;
if ( BuildSettings . bVolume )
{
NumSlices = FMath : : Max ( NumSlices / 2 , 1 ) ;
}
}
return true ;
}
FGuid FTextureBuildFunction : : GetVersion ( ) const
{
UE : : DerivedData : : FBuildVersionBuilder Builder ;
2022-11-18 17:09:58 -05:00
Builder < < TextureBuildFunctionVersion ;
2024-01-22 10:12:42 -05:00
Builder < < TextureMetadataDerivedDataVer ;
2022-06-22 20:21:16 -04:00
ITextureFormat * TextureFormat = nullptr ;
GetVersion ( Builder , TextureFormat ) ;
if ( TextureFormat )
{
TArray < FName > SupportedFormats ;
TextureFormat - > GetSupportedFormats ( SupportedFormats ) ;
TArray < uint16 > SupportedFormatVersions ;
for ( const FName & SupportedFormat : SupportedFormats )
{
SupportedFormatVersions . AddUnique ( TextureFormat - > GetVersion ( SupportedFormat ) ) ;
}
SupportedFormatVersions . Sort ( ) ;
Builder < < SupportedFormatVersions ;
}
return Builder . Build ( ) ;
}
void FTextureBuildFunction : : Configure ( UE : : DerivedData : : FBuildConfigContext & Context ) const
{
2022-11-08 13:26:12 -05:00
Context . SetTypeName ( UTF8TEXTVIEW ( " Texture " ) ) ;
2022-06-22 20:21:16 -04:00
Context . SetCacheBucket ( UE : : DerivedData : : FCacheBucket ( ANSITEXTVIEW ( " Texture " ) ) ) ;
const FCbObject Settings = Context . FindConstant ( UTF8TEXTVIEW ( " Settings " ) ) ;
const int64 RequiredMemoryEstimate = Settings [ " RequiredMemoryEstimate " ] . AsInt64 ( ) ;
Context . SetRequiredMemory ( RequiredMemoryEstimate ) ;
}
void FTextureBuildFunction : : Build ( UE : : DerivedData : : FBuildContext & Context ) const
{
const FCbObject Settings = Context . FindConstant ( UTF8TEXTVIEW ( " Settings " ) ) ;
if ( ! Settings )
{
UE_LOG ( LogTextureBuildFunction , Error , TEXT ( " Settings are not available. " ) ) ;
return ;
}
const FTextureBuildSettings BuildSettings = ReadBuildSettingsFromCompactBinary ( Settings [ " Build " ] . AsObjectView ( ) ) ;
const uint16 RequiredTextureFormatVersion = Settings [ " FormatVersion " ] . AsUInt16 ( ) ;
2022-12-06 15:36:46 -05:00
const ITextureFormat * TextureFormat ;
if ( ITextureFormatManagerModule * TFM = GetTextureFormatManager ( ) )
{
TextureFormat = TFM - > FindTextureFormat ( BuildSettings . TextureFormatName ) ;
}
else
{
UE_LOG ( LogTextureBuildFunction , Error , TEXT ( " TextureFormatManager not found! " ) ) ;
return ;
}
const uint16 CurrentTextureFormatVersion = TextureFormat ? TextureFormat - > GetVersion ( BuildSettings . TextureFormatName , & BuildSettings ) : 0 ;
2022-08-23 12:41:58 -04:00
if ( CurrentTextureFormatVersion ! = RequiredTextureFormatVersion )
{
UE_LOG ( LogTextureBuildFunction , Error , TEXT ( " %s has version %hu when version %hu is required. " ) ,
* BuildSettings . TextureFormatName . ToString ( ) , CurrentTextureFormatVersion , RequiredTextureFormatVersion ) ; ;
return ;
}
FTextureEngineParameters EngineParameters ;
if ( UE : : TextureBuildUtilities : : TextureEngineParameters : : FromCompactBinary ( EngineParameters , Context . FindConstant ( UTF8TEXTVIEW ( " EngineParameters " ) ) ) = = false )
{
UE_LOG ( LogTextureBuildFunction , Error , TEXT ( " Engine parameters are not available. " ) ) ;
return ;
2022-06-22 20:21:16 -04:00
}
TArray < FImage > SourceMips ;
if ( ! TryReadTextureSourceFromCompactBinary ( Settings [ " Source " ] , Context , BuildSettings , SourceMips ) )
{
return ;
}
2023-07-27 16:46:23 -04:00
FSharedImageRef CPUCopy ;
if ( BuildSettings . bCPUAccessible )
{
CPUCopy = new FSharedImage ( ) ;
SourceMips [ 0 ] . CopyTo ( * CPUCopy ) ;
// We just use a placeholder texture rather than the source.
SourceMips . Empty ( ) ;
FImage & Placeholder = SourceMips . AddDefaulted_GetRef ( ) ;
UE : : TextureBuildUtilities : : GetPlaceholderTextureImage ( & Placeholder ) ;
}
2022-06-22 20:21:16 -04:00
TArray < FImage > AssociatedNormalSourceMips ;
if ( FCbFieldView CompositeSource = Settings [ " CompositeSource " ] ;
CompositeSource & & ! TryReadTextureSourceFromCompactBinary ( CompositeSource , Context , BuildSettings , AssociatedNormalSourceMips ) )
{
return ;
}
2023-03-09 14:03:35 -05:00
UE_LOG ( LogTextureBuildFunction , Display , TEXT ( " Compressing %s -> %d source mip(s) (%dx%d) to %s... " ) , * Context . GetName ( ) , SourceMips . Num ( ) , SourceMips [ 0 ] . SizeX , SourceMips [ 0 ] . SizeY , * BuildSettings . TextureFormatName . ToString ( ) ) ;
2022-06-22 20:21:16 -04:00
2022-08-23 12:41:58 -04:00
ITextureCompressorModule & TextureCompressorModule = FModuleManager : : GetModuleChecked < ITextureCompressorModule > ( TEXTURE_COMPRESSOR_MODULENAME ) ;
2022-06-22 20:21:16 -04:00
TArray < FCompressedImage2D > CompressedMips ;
uint32 NumMipsInTail ;
uint32 ExtData ;
2022-11-18 17:09:58 -05:00
UE : : TextureBuildUtilities : : FTextureBuildMetadata BuildMetadata ;
2022-08-23 12:41:58 -04:00
bool bBuildSucceeded = TextureCompressorModule . BuildTexture (
2022-06-22 20:21:16 -04:00
SourceMips ,
AssociatedNormalSourceMips ,
BuildSettings ,
Context . GetName ( ) ,
CompressedMips ,
NumMipsInTail ,
2022-08-23 12:41:58 -04:00
ExtData ,
2022-11-18 17:09:58 -05:00
& BuildMetadata
) ;
2022-06-22 20:21:16 -04:00
if ( ! bBuildSucceeded )
{
return ;
}
check ( CompressedMips . Num ( ) > 0 ) ;
2022-08-23 12:41:58 -04:00
FEncodedTextureDescription TextureDescription ;
2022-06-22 20:21:16 -04:00
{
2022-08-23 12:41:58 -04:00
int32 CalculatedMip0SizeX = 0 , CalculatedMip0SizeY = 0 , CalculatedMip0NumSlices = 0 ;
int32 CalculatedMipCount = TextureCompressorModule . GetMipCountForBuildSettings ( SourceMips [ 0 ] . SizeX , SourceMips [ 0 ] . SizeY , SourceMips [ 0 ] . NumSlices , SourceMips . Num ( ) , BuildSettings , CalculatedMip0SizeX , CalculatedMip0SizeY , CalculatedMip0NumSlices ) ;
2022-11-18 17:09:58 -05:00
BuildSettings . GetEncodedTextureDescriptionWithPixelFormat ( & TextureDescription , ( EPixelFormat ) CompressedMips [ 0 ] . PixelFormat , CalculatedMip0SizeX , CalculatedMip0SizeY , CalculatedMip0NumSlices , CalculatedMipCount ) ;
2022-08-23 12:41:58 -04:00
}
2022-06-22 20:21:16 -04:00
2022-08-23 12:41:58 -04:00
FEncodedTextureExtendedData ExtendedData ;
2022-06-22 20:21:16 -04:00
2022-08-23 12:41:58 -04:00
// ExtendedData is only really useful for textures that have a post build step for tiling,
// however it's possible that we ran the old build process where the tiling occurs as part
// of the BuildTexture->CompressImage step via child texture formats. In that case, we've already
// tiled and we need to pass the data back out. Otherwise, this gets ignored and the tiling step
// regenerates it.
{
ExtendedData . NumMipsInTail = NumMipsInTail ;
ExtendedData . ExtData = ExtData ;
2022-06-22 20:21:16 -04:00
2022-08-23 12:41:58 -04:00
int32 EncodedMipCount = TextureDescription . GetNumEncodedMips ( & ExtendedData ) ;
ExtendedData . MipSizesInBytes . AddUninitialized ( EncodedMipCount ) ;
for ( int32 MipIndex = 0 ; MipIndex < EncodedMipCount ; MipIndex + + )
2022-06-22 20:21:16 -04:00
{
2022-08-23 12:41:58 -04:00
ExtendedData . MipSizesInBytes [ MipIndex ] = CompressedMips [ MipIndex ] . RawData . Num ( ) ;
2022-06-22 20:21:16 -04:00
}
}
2022-08-23 12:41:58 -04:00
// Long term, this will be supplied to the build and this would only be called to verify.
int32 NumStreamingMips = TextureDescription . GetNumStreamingMips ( & ExtendedData , EngineParameters ) ;
2022-06-22 20:21:16 -04:00
{
2023-07-27 16:46:23 -04:00
if ( CPUCopy . IsValid ( ) )
{
FCbObject ImageInfoMetadata ;
CPUCopy - > ImageInfoToCompactBinary ( ImageInfoMetadata ) ;
Context . AddValue ( UE : : DerivedData : : FValueId : : FromName ( ANSITEXTVIEW ( " CPUCopyImageInfo " ) ) , ImageInfoMetadata ) ;
FSharedBuffer CPUCopyData = MakeSharedBufferFromArray ( MoveTemp ( CPUCopy - > RawData ) ) ;
Context . AddValue ( UE : : DerivedData : : FValueId : : FromName ( ANSITEXTVIEW ( " CPUCopyRawData " ) ) , CPUCopyData ) ;
}
2024-01-22 10:12:42 -05:00
Context . AddValue ( UE : : DerivedData : : FValueId : : FromName ( ANSITEXTVIEW ( " TextureBuildMetadata " ) ) , BuildMetadata . ToCompactBinaryWithDefaults ( ) ) ;
2022-08-23 12:41:58 -04:00
Context . AddValue ( UE : : DerivedData : : FValueId : : FromName ( ANSITEXTVIEW ( " EncodedTextureDescription " ) ) , UE : : TextureBuildUtilities : : EncodedTextureDescription : : ToCompactBinary ( TextureDescription ) ) ;
Context . AddValue ( UE : : DerivedData : : FValueId : : FromName ( ANSITEXTVIEW ( " EncodedTextureExtendedData " ) ) , UE : : TextureBuildUtilities : : EncodedTextureExtendedData : : ToCompactBinary ( ExtendedData ) ) ;
// Streaming mips
for ( int32 MipIndex = 0 ; MipIndex < NumStreamingMips ; + + MipIndex )
{
TAnsiStringBuilder < 16 > MipName ;
MipName < < ANSITEXTVIEW ( " Mip " ) < < MipIndex ;
FSharedBuffer MipData = MakeSharedBufferFromArray ( MoveTemp ( CompressedMips [ MipIndex ] . RawData ) ) ;
Context . AddValue ( UE : : DerivedData : : FValueId : : FromName ( MipName ) , MipData ) ;
}
// Mip tail
TArray < FSharedBuffer > MipTailComponents ;
for ( int32 MipIndex = NumStreamingMips ; MipIndex < TextureDescription . NumMips ; + + MipIndex )
{
FSharedBuffer MipData = MakeSharedBufferFromArray ( MoveTemp ( CompressedMips [ MipIndex ] . RawData ) ) ;
MipTailComponents . Add ( MipData ) ;
}
FCompositeBuffer MipTail ( MipTailComponents ) ;
if ( MipTail . GetSize ( ) > 0 )
{
Context . AddValue ( UE : : DerivedData : : FValueId : : FromName ( ANSITEXTVIEW ( " MipTail " ) ) , MipTail ) ;
}
2022-06-22 20:21:16 -04:00
}
}
2022-08-23 12:41:58 -04:00
2023-03-09 14:03:35 -05:00
void GenericTextureTilingBuildFunction ( UE : : DerivedData : : FBuildContext & Context , const ITextureTiler * Tiler , const UE : : DerivedData : : FUtf8SharedString & BuildFunctionName )
2022-08-23 12:41:58 -04:00
{
// The texture description is either passed as a constant or as an output from the other build ("build input").
FEncodedTextureDescription TextureDescription ;
{
FCbObject TextureDescriptionCb = Context . FindConstant ( UTF8TEXTVIEW ( " EncodedTextureDescriptionConstant " ) ) ;
if ( ! TextureDescriptionCb )
{
FSharedBuffer RawTextureDescription = Context . FindInput ( UTF8TEXTVIEW ( " EncodedTextureDescriptionInput " ) ) ;
if ( ! RawTextureDescription )
{
return ;
}
TextureDescriptionCb = FCbObject ( RawTextureDescription ) ;
}
UE : : TextureBuildUtilities : : EncodedTextureDescription : : FromCompactBinary ( TextureDescription , TextureDescriptionCb ) ;
}
// The extended data is either passed as a constant, but is not output from the linear build - it's
// our job to make it.
FEncodedTextureExtendedData TextureExtendedData ;
{
FCbObject TextureExtendedDataCb = Context . FindConstant ( UTF8TEXTVIEW ( " EncodedTextureExtendedDataConstant " ) ) ;
if ( TextureExtendedDataCb )
{
UE : : TextureBuildUtilities : : EncodedTextureExtendedData : : FromCompactBinary ( TextureExtendedData , TextureExtendedDataCb ) ;
}
else
{
2023-03-27 16:39:53 -04:00
// If we're in this path we need to have the LODBias delivered to us.
FCbObject LODBiasCb = Context . FindConstant ( UTF8TEXTVIEW ( " LODBias " ) ) ;
TextureExtendedData = Tiler - > GetExtendedDataForTexture ( TextureDescription , LODBiasCb [ " LODBias " ] . AsInt8 ( ) ) ;
2022-08-23 12:41:58 -04:00
}
}
FTextureEngineParameters EngineParameters ;
{
FCbObject EngineParametersCb = Context . FindConstant ( UTF8TEXTVIEW ( " EngineParameters " ) ) ;
UE : : TextureBuildUtilities : : TextureEngineParameters : : FromCompactBinary ( EngineParameters , EngineParametersCb ) ;
}
2024-01-22 10:12:42 -05:00
UE : : TextureBuildUtilities : : FTextureBuildMetadata BuildMetadata ( FCbObject ( Context . FindInput ( ANSITEXTVIEW ( " TextureBuildMetadata " ) ) ) ) ;
2022-11-18 17:09:58 -05:00
2023-08-23 11:04:15 -04:00
UE_LOG ( LogTextureBuildFunction , Display , TEXT ( " Tiling %s with %s -> %d source mip(s) with a tail of %d... " ) , * Context . GetName ( ) , StringCast < TCHAR > ( * BuildFunctionName ) . Get ( ) , TextureDescription . NumMips , TextureExtendedData . NumMipsInTail ) ;
2022-11-18 17:09:58 -05:00
2022-08-23 12:41:58 -04:00
//
// Careful - the linear build might have a different streaming mip count than we output due to mip tail
// packing.
//
int32 InputTextureNumStreamingMips = TextureDescription . GetNumStreamingMips ( nullptr , EngineParameters ) ;
int32 OutputTextureNumStreamingMips = TextureDescription . GetNumStreamingMips ( & TextureExtendedData , EngineParameters ) ;
FSharedBuffer InputTextureMipTailData ;
if ( TextureDescription . NumMips > InputTextureNumStreamingMips )
{
InputTextureMipTailData = Context . FindInput ( UTF8TEXTVIEW ( " MipTail " ) ) ;
}
// We might be packing several mips in to a single tiled mip at the end, so we need to have all the buffers available
// to potentially pass to ProcessMipLevel. Can do this on demand so that the highest mip level isn't in memory for the entire
// mip chain... however all the time is also spent on it and it's only +33% size for the entire chain, so not really worth.
TArray < FSharedBuffer > InputTextureMipBuffers ;
TArray < FMemoryView > InputTextureMipViews ;
uint64 CurrentMipTailOffset = 0 ;
for ( int32 MipIndex = 0 ; MipIndex < TextureDescription . NumMips ; MipIndex + + )
{
FMemoryView SourceMipView ;
if ( MipIndex > = InputTextureNumStreamingMips )
{
// Mip tail.
uint64 SourceMipSize = TextureDescription . GetMipSizeInBytes ( MipIndex ) ;
SourceMipView = InputTextureMipTailData . GetView ( ) . Mid ( CurrentMipTailOffset , SourceMipSize ) ;
CurrentMipTailOffset + = SourceMipSize ;
}
else
{
TUtf8StringBuilder < 10 > StreamingMipName ;
StreamingMipName < < " Mip " < < MipIndex ;
FSharedBuffer SourceData = Context . FindInput ( StreamingMipName ) ;
2023-03-09 14:03:35 -05:00
check ( SourceData . GetSize ( ) = = TextureDescription . GetMipSizeInBytes ( MipIndex ) ) ;
2022-08-23 12:41:58 -04:00
SourceMipView = SourceData . GetView ( ) ;
InputTextureMipBuffers . Add ( SourceData ) ;
}
InputTextureMipViews . Add ( SourceMipView ) ;
}
// If the platform packs mip tails, we need to pass all the relevant mip buffers at once.
int32 FirstMipTailIndex = TextureDescription . NumMips - 1 ;
int32 MipTailCount = 1 ;
if ( TextureExtendedData . NumMipsInTail > 1 )
{
MipTailCount = TextureExtendedData . NumMipsInTail ;
FirstMipTailIndex = TextureDescription . NumMips - MipTailCount ;
}
// Process the mips
TArray < FSharedBuffer > MipTailBuffers ;
for ( int32 MipIndex = 0 ; MipIndex < FirstMipTailIndex + 1 ; MipIndex + + )
{
TUtf8StringBuilder < 10 > StreamingMipName ;
StreamingMipName < < " Mip " < < MipIndex ;
TArrayView < FMemoryView > MipsForLevel = MakeArrayView ( InputTextureMipViews . GetData ( ) + MipIndex , 1 ) ;
if ( MipIndex = = FirstMipTailIndex )
{
MipsForLevel = MakeArrayView ( InputTextureMipViews . GetData ( ) + MipIndex , MipTailCount ) ;
}
FSharedBuffer MipData = Tiler - > ProcessMipLevel ( TextureDescription , TextureExtendedData , MipsForLevel , MipIndex ) ;
// Make sure we got the size we advertised prior to the build. If this ever fires then we
// have a critical mismatch!
check ( TextureExtendedData . MipSizesInBytes [ MipIndex ] = = MipData . GetSize ( ) ) ;
// Save the data to the output.
if ( MipIndex < OutputTextureNumStreamingMips )
{
Context . AddValue ( UE : : DerivedData : : FValueId : : FromName ( StreamingMipName ) , MipData ) ;
}
else
{
MipTailBuffers . Add ( MipData ) ;
}
} // end for each mip
// The mip tail is a bunch of mips all together in one "Value", so assemble them here.
FCompositeBuffer MipTail ( MipTailBuffers ) ;
if ( MipTail . GetSize ( ) > 0 )
{
Context . AddValue ( UE : : DerivedData : : FValueId : : FromName ( UTF8TEXTVIEW ( " MipTail " ) ) , MipTail ) ;
}
Context . AddValue ( UE : : DerivedData : : FValueId : : FromName ( UTF8TEXTVIEW ( " EncodedTextureDescription " ) ) , UE : : TextureBuildUtilities : : EncodedTextureDescription : : ToCompactBinary ( TextureDescription ) ) ;
Context . AddValue ( UE : : DerivedData : : FValueId : : FromName ( UTF8TEXTVIEW ( " EncodedTextureExtendedData " ) ) , UE : : TextureBuildUtilities : : EncodedTextureExtendedData : : ToCompactBinary ( TextureExtendedData ) ) ;
2024-01-22 10:12:42 -05:00
Context . AddValue ( UE : : DerivedData : : FValueId : : FromName ( UTF8TEXTVIEW ( " TextureBuildMetadata " ) ) , BuildMetadata . ToCompactBinaryWithDefaults ( ) ) ;
2022-08-23 12:41:58 -04:00
}