2014-12-07 19:09:38 -05:00
// Copyright 1998-2015 Epic Games, Inc. All Rights Reserved.
2014-03-14 14:13:41 -04:00
# include "Core.h"
# include "CoreMisc.h"
# include "ImageCore.h"
# include "ModuleInterface.h"
# include "ModuleManager.h"
# include "TargetPlatform.h"
# include "TextureCompressorModule.h"
# include "PixelFormat.h"
# include "GenericPlatformProcess.h"
2014-06-17 18:27:26 -04:00
# define MAX_QUALITY 4
2014-03-14 14:13:41 -04:00
DEFINE_LOG_CATEGORY_STATIC ( LogTextureFormatPVR , Log , All ) ;
/**
* Macro trickery for supported format names .
*/
# define ENUM_SUPPORTED_FORMATS(op) \
op ( PVRTC2 ) \
op ( PVRTC4 ) \
op ( PVRTCN ) \
op ( AutoPVRTC )
# 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
// PVR file header format
# if PLATFORM_SUPPORTS_PRAGMA_PACK
# pragma pack(push, 4)
# endif
struct FPVRHeader
{
uint32 Version ;
uint32 Flags ;
uint64 PixelFormat ;
uint32 ColorSpace ;
uint32 ChannelType ;
uint32 Height ;
uint32 Width ;
uint32 Depth ;
uint32 NumSurfaces ;
uint32 NumFaces ;
uint32 NumMipmaps ;
uint32 MetaDataSize ;
} ;
# if PLATFORM_SUPPORTS_PRAGMA_PACK
# pragma pack(pop)
# endif
/**
* Converts a power - of - two image to a square format ( ex : 256 x512 - > 512 x512 ) . Be wary of memory waste when too many texture are not square .
*
* @ param Image The image to be converted to a square
* @ return true if the image was converted successfully , else false
*/
static bool SquarifyImage ( FImage & Image , uint32 MinSquareSize )
{
// Early out
if ( Image . SizeX = = Image . SizeY & & Image . SizeX > = int32 ( MinSquareSize ) )
{
return true ;
}
Gamma Correction - Changing the way all FColors are converted into FLinearColor by default. Previously all sRGB textures coming into the engine along with all other usage of FColor -> FLinearColor used a lookup table that assumed the final gamma correction would simply be pow(color, 1/DisplayGamma). However, that's not the case, we use the IEC 61966-2-1 standard on most platforms for both the scene renderer, as well as for gamma correction in Slate. In Slate you should now see an image matching Photoshop instead of being slightly darker in the lower ranges. However, because we don't want to invalidate all existing textures that users have authored, all existing UTextures have a UseLegacyGamma flag set to true, all new textures will be set to false. The flag is part of the DDC key calculation, but steps were taken so that when legacy is true, keys match existing keys to prevent universally invalidating all games DDCs just to make this change.
To summarize,
Old Pipeline: sRGB-Pow(2.2) -> Linear -> sRGB-IEC 61966
New Pipeline: sRGB-IEC 61966 -> Linear -> sRGB-IEC 61966
#codereview gil.gribb, nick.penwarden, martin.mittring
[CL 2571070 by Nick Darnell in Main branch]
2015-05-29 16:03:43 -04:00
// Figure out the squarified size
uint32 SquareSize = FMath : : Max ( Image . SizeX , Image . SizeY ) ;
2014-03-14 14:13:41 -04:00
if ( SquareSize < MinSquareSize )
{
SquareSize = MinSquareSize ;
}
Gamma Correction - Changing the way all FColors are converted into FLinearColor by default. Previously all sRGB textures coming into the engine along with all other usage of FColor -> FLinearColor used a lookup table that assumed the final gamma correction would simply be pow(color, 1/DisplayGamma). However, that's not the case, we use the IEC 61966-2-1 standard on most platforms for both the scene renderer, as well as for gamma correction in Slate. In Slate you should now see an image matching Photoshop instead of being slightly darker in the lower ranges. However, because we don't want to invalidate all existing textures that users have authored, all existing UTextures have a UseLegacyGamma flag set to true, all new textures will be set to false. The flag is part of the DDC key calculation, but steps were taken so that when legacy is true, keys match existing keys to prevent universally invalidating all games DDCs just to make this change.
To summarize,
Old Pipeline: sRGB-Pow(2.2) -> Linear -> sRGB-IEC 61966
New Pipeline: sRGB-IEC 61966 -> Linear -> sRGB-IEC 61966
#codereview gil.gribb, nick.penwarden, martin.mittring
[CL 2571070 by Nick Darnell in Main branch]
2015-05-29 16:03:43 -04:00
// Calculate how many times to duplicate each row of column
uint32 MultX = SquareSize / Image . SizeX ;
uint32 MultY = SquareSize / Image . SizeY ;
2014-03-14 14:13:41 -04:00
// Only give memory overhead warning if we're actually going to use a larger image
// Small mips that have to be upscaled for compression only save the smaller mip for use
if ( MultX = = 1 | | MultY = = 1 )
{
float FOverhead = float ( FMath : : Min ( Image . SizeX , Image . SizeY ) ) / float ( SquareSize ) ;
2014-05-06 06:26:25 -04:00
int32 POverhead = FMath : : RoundToInt ( 100.0f - ( FOverhead * 100.0f ) ) ;
2014-03-14 14:13:41 -04:00
UE_LOG ( LogTextureFormatPVR , Warning , TEXT ( " Expanding mip (%d,%d) to (%d, %d). Memory overhead: ~%d%% " ) ,
Image . SizeX , Image . SizeY , SquareSize , SquareSize , POverhead ) ;
}
else if ( MultX ! = MultY )
{
float FOverhead = float ( FMath : : Min ( Image . SizeX , Image . SizeY ) ) / float ( FMath : : Max ( Image . SizeX , Image . SizeY ) ) ;
2014-05-06 06:26:25 -04:00
int32 POverhead = FMath : : RoundToInt ( 100.0f - ( FOverhead * 100.0f ) ) ;
2014-03-14 14:13:41 -04:00
UE_LOG ( LogTextureFormatPVR , Warning , TEXT ( " Expanding mip (%d,%d) to (%d, %d). Memory overhead: ~%d%% " ) ,
Image . SizeX , Image . SizeY , FMath : : Max ( Image . SizeX , Image . SizeY ) , FMath : : Max ( Image . SizeX , Image . SizeY ) , POverhead ) ;
}
Gamma Correction - Changing the way all FColors are converted into FLinearColor by default. Previously all sRGB textures coming into the engine along with all other usage of FColor -> FLinearColor used a lookup table that assumed the final gamma correction would simply be pow(color, 1/DisplayGamma). However, that's not the case, we use the IEC 61966-2-1 standard on most platforms for both the scene renderer, as well as for gamma correction in Slate. In Slate you should now see an image matching Photoshop instead of being slightly darker in the lower ranges. However, because we don't want to invalidate all existing textures that users have authored, all existing UTextures have a UseLegacyGamma flag set to true, all new textures will be set to false. The flag is part of the DDC key calculation, but steps were taken so that when legacy is true, keys match existing keys to prevent universally invalidating all games DDCs just to make this change.
To summarize,
Old Pipeline: sRGB-Pow(2.2) -> Linear -> sRGB-IEC 61966
New Pipeline: sRGB-IEC 61966 -> Linear -> sRGB-IEC 61966
#codereview gil.gribb, nick.penwarden, martin.mittring
[CL 2571070 by Nick Darnell in Main branch]
2015-05-29 16:03:43 -04:00
// Allocate room to fill out into
TArray < uint32 > SquareRawData ;
2015-03-28 06:38:28 -04:00
SquareRawData . SetNumUninitialized ( SquareSize * SquareSize * Image . NumSlices ) ;
Gamma Correction - Changing the way all FColors are converted into FLinearColor by default. Previously all sRGB textures coming into the engine along with all other usage of FColor -> FLinearColor used a lookup table that assumed the final gamma correction would simply be pow(color, 1/DisplayGamma). However, that's not the case, we use the IEC 61966-2-1 standard on most platforms for both the scene renderer, as well as for gamma correction in Slate. In Slate you should now see an image matching Photoshop instead of being slightly darker in the lower ranges. However, because we don't want to invalidate all existing textures that users have authored, all existing UTextures have a UseLegacyGamma flag set to true, all new textures will be set to false. The flag is part of the DDC key calculation, but steps were taken so that when legacy is true, keys match existing keys to prevent universally invalidating all games DDCs just to make this change.
To summarize,
Old Pipeline: sRGB-Pow(2.2) -> Linear -> sRGB-IEC 61966
New Pipeline: sRGB-IEC 61966 -> Linear -> sRGB-IEC 61966
#codereview gil.gribb, nick.penwarden, martin.mittring
[CL 2571070 by Nick Darnell in Main branch]
2015-05-29 16:03:43 -04:00
2014-03-14 14:13:41 -04:00
int32 SourceSliceSize = Image . SizeX * Image . SizeY ;
int32 DestSliceSize = SquareSize * SquareSize ;
for ( int32 SliceIndex = 0 ; SliceIndex < Image . NumSlices ; + + SliceIndex )
{
uint32 * RectData = ( ( uint32 * ) Image . RawData . GetData ( ) ) + SliceIndex * SourceSliceSize ;
uint32 * SquareData = ( ( uint32 * ) SquareRawData . GetData ( ) ) + SliceIndex * DestSliceSize ;
for ( int32 Y = 0 ; Y < Image . SizeY ; + + Y )
{
for ( int32 X = 0 ; X < Image . SizeX ; + + X )
{
uint32 SourceColor = * ( RectData + Y * Image . SizeX + X ) ;
Gamma Correction - Changing the way all FColors are converted into FLinearColor by default. Previously all sRGB textures coming into the engine along with all other usage of FColor -> FLinearColor used a lookup table that assumed the final gamma correction would simply be pow(color, 1/DisplayGamma). However, that's not the case, we use the IEC 61966-2-1 standard on most platforms for both the scene renderer, as well as for gamma correction in Slate. In Slate you should now see an image matching Photoshop instead of being slightly darker in the lower ranges. However, because we don't want to invalidate all existing textures that users have authored, all existing UTextures have a UseLegacyGamma flag set to true, all new textures will be set to false. The flag is part of the DDC key calculation, but steps were taken so that when legacy is true, keys match existing keys to prevent universally invalidating all games DDCs just to make this change.
To summarize,
Old Pipeline: sRGB-Pow(2.2) -> Linear -> sRGB-IEC 61966
New Pipeline: sRGB-IEC 61966 -> Linear -> sRGB-IEC 61966
#codereview gil.gribb, nick.penwarden, martin.mittring
[CL 2571070 by Nick Darnell in Main branch]
2015-05-29 16:03:43 -04:00
2014-03-14 14:13:41 -04:00
for ( uint32 YDup = 0 ; YDup < MultY ; + + YDup )
{
for ( uint32 XDup = 0 ; XDup < MultX ; + + XDup )
{
uint32 * DestColor = SquareData + ( ( Y * MultY + YDup ) * SquareSize + ( X * MultX + XDup ) ) ;
* DestColor = SourceColor ;
}
}
}
}
}
// Put the new image data into the existing Image (copying from uint32 array to uint8 array)
Image . RawData . Empty ( SquareSize * SquareSize * Image . NumSlices * sizeof ( uint32 ) ) ;
2015-03-28 06:38:28 -04:00
Image . RawData . SetNumUninitialized ( SquareSize * SquareSize * Image . NumSlices * sizeof ( uint32 ) ) ;
2014-03-14 14:13:41 -04:00
uint32 * FinalData = ( uint32 * ) Image . RawData . GetData ( ) ;
FMemory : : Memcpy ( Image . RawData . GetData ( ) , SquareRawData . GetData ( ) , SquareSize * SquareSize * Image . NumSlices * sizeof ( uint32 ) ) ;
Gamma Correction - Changing the way all FColors are converted into FLinearColor by default. Previously all sRGB textures coming into the engine along with all other usage of FColor -> FLinearColor used a lookup table that assumed the final gamma correction would simply be pow(color, 1/DisplayGamma). However, that's not the case, we use the IEC 61966-2-1 standard on most platforms for both the scene renderer, as well as for gamma correction in Slate. In Slate you should now see an image matching Photoshop instead of being slightly darker in the lower ranges. However, because we don't want to invalidate all existing textures that users have authored, all existing UTextures have a UseLegacyGamma flag set to true, all new textures will be set to false. The flag is part of the DDC key calculation, but steps were taken so that when legacy is true, keys match existing keys to prevent universally invalidating all games DDCs just to make this change.
To summarize,
Old Pipeline: sRGB-Pow(2.2) -> Linear -> sRGB-IEC 61966
New Pipeline: sRGB-IEC 61966 -> Linear -> sRGB-IEC 61966
#codereview gil.gribb, nick.penwarden, martin.mittring
[CL 2571070 by Nick Darnell in Main branch]
2015-05-29 16:03:43 -04:00
Image . SizeX = SquareSize ;
Image . SizeY = SquareSize ;
2014-03-14 14:13:41 -04:00
Gamma Correction - Changing the way all FColors are converted into FLinearColor by default. Previously all sRGB textures coming into the engine along with all other usage of FColor -> FLinearColor used a lookup table that assumed the final gamma correction would simply be pow(color, 1/DisplayGamma). However, that's not the case, we use the IEC 61966-2-1 standard on most platforms for both the scene renderer, as well as for gamma correction in Slate. In Slate you should now see an image matching Photoshop instead of being slightly darker in the lower ranges. However, because we don't want to invalidate all existing textures that users have authored, all existing UTextures have a UseLegacyGamma flag set to true, all new textures will be set to false. The flag is part of the DDC key calculation, but steps were taken so that when legacy is true, keys match existing keys to prevent universally invalidating all games DDCs just to make this change.
To summarize,
Old Pipeline: sRGB-Pow(2.2) -> Linear -> sRGB-IEC 61966
New Pipeline: sRGB-IEC 61966 -> Linear -> sRGB-IEC 61966
#codereview gil.gribb, nick.penwarden, martin.mittring
[CL 2571070 by Nick Darnell in Main branch]
2015-05-29 16:03:43 -04:00
return true ;
2014-03-14 14:13:41 -04:00
}
static void DeriveNormalZ ( FImage & Image )
{
int32 SliceSize = Image . SizeX * Image . SizeY ;
for ( int32 SliceIndex = 0 ; SliceIndex < Image . NumSlices ; + + SliceIndex )
{
FColor * RectData = ( FColor * ) Image . RawData . GetData ( ) + SliceIndex * SliceSize ;
for ( int32 Y = 0 ; Y < Image . SizeY ; + + Y )
{
for ( int32 X = 0 ; X < Image . SizeX ; + + X )
{
FColor & SourceColor = * ( RectData + Y * Image . SizeX + X ) ;
const float NormalX = SourceColor . R / 255.0f * 2 - 1 ;
const float NormalY = SourceColor . G / 255.0f * 2 - 1 ;
const float NormalZ = FMath : : Sqrt ( FMath : : Clamp < float > ( 1 - ( NormalX * NormalX + NormalY * NormalY ) , 0 , 1 ) ) ;
2014-05-06 06:26:25 -04:00
SourceColor . B = FMath : : TruncToInt ( ( NormalZ + 1 ) / 2.0f * 255.0f ) ;
2014-03-14 14:13:41 -04:00
}
}
}
}
/**
* Checks if the passed image is a proper power - of - 2 image
*
* @ param Image The Image to evaluate
* @ return true if the image is a power of 2 , else false
*/
static bool ValidateImagePower ( const FImage & Image )
{
Gamma Correction - Changing the way all FColors are converted into FLinearColor by default. Previously all sRGB textures coming into the engine along with all other usage of FColor -> FLinearColor used a lookup table that assumed the final gamma correction would simply be pow(color, 1/DisplayGamma). However, that's not the case, we use the IEC 61966-2-1 standard on most platforms for both the scene renderer, as well as for gamma correction in Slate. In Slate you should now see an image matching Photoshop instead of being slightly darker in the lower ranges. However, because we don't want to invalidate all existing textures that users have authored, all existing UTextures have a UseLegacyGamma flag set to true, all new textures will be set to false. The flag is part of the DDC key calculation, but steps were taken so that when legacy is true, keys match existing keys to prevent universally invalidating all games DDCs just to make this change.
To summarize,
Old Pipeline: sRGB-Pow(2.2) -> Linear -> sRGB-IEC 61966
New Pipeline: sRGB-IEC 61966 -> Linear -> sRGB-IEC 61966
#codereview gil.gribb, nick.penwarden, martin.mittring
[CL 2571070 by Nick Darnell in Main branch]
2015-05-29 16:03:43 -04:00
// Image must already have power of 2 dimensions
bool bDimensionsValid = true ;
int DimX = Image . SizeX ;
int DimY = Image . SizeY ;
while ( DimX > = 2 )
{
if ( DimX % 2 = = 1 )
{
bDimensionsValid = false ;
break ;
}
DimX / = 2 ;
}
while ( DimY > = 2 & & bDimensionsValid )
{
if ( DimY % 2 = = 1 )
{
bDimensionsValid = false ;
break ;
}
DimY / = 2 ;
}
if ( ! bDimensionsValid )
{
return false ;
}
2014-03-14 14:13:41 -04:00
return true ;
}
/**
* Fills the output structure with the original uncompressed mip information
*
* @ param InImage The mip to compress
* @ param OutCompressImage The output image ( uncompressed in this case )
*/
Gamma Correction - Changing the way all FColors are converted into FLinearColor by default. Previously all sRGB textures coming into the engine along with all other usage of FColor -> FLinearColor used a lookup table that assumed the final gamma correction would simply be pow(color, 1/DisplayGamma). However, that's not the case, we use the IEC 61966-2-1 standard on most platforms for both the scene renderer, as well as for gamma correction in Slate. In Slate you should now see an image matching Photoshop instead of being slightly darker in the lower ranges. However, because we don't want to invalidate all existing textures that users have authored, all existing UTextures have a UseLegacyGamma flag set to true, all new textures will be set to false. The flag is part of the DDC key calculation, but steps were taken so that when legacy is true, keys match existing keys to prevent universally invalidating all games DDCs just to make this change.
To summarize,
Old Pipeline: sRGB-Pow(2.2) -> Linear -> sRGB-IEC 61966
New Pipeline: sRGB-IEC 61966 -> Linear -> sRGB-IEC 61966
#codereview gil.gribb, nick.penwarden, martin.mittring
[CL 2571070 by Nick Darnell in Main branch]
2015-05-29 16:03:43 -04:00
static void UseOriginal ( const FImage & InImage , FCompressedImage2D & OutCompressedImage , EPixelFormat CompressedPixelFormat , EGammaSpace GammaSpace )
2014-03-14 14:13:41 -04:00
{
// Get Raw Data
FImage Image ;
Gamma Correction - Changing the way all FColors are converted into FLinearColor by default. Previously all sRGB textures coming into the engine along with all other usage of FColor -> FLinearColor used a lookup table that assumed the final gamma correction would simply be pow(color, 1/DisplayGamma). However, that's not the case, we use the IEC 61966-2-1 standard on most platforms for both the scene renderer, as well as for gamma correction in Slate. In Slate you should now see an image matching Photoshop instead of being slightly darker in the lower ranges. However, because we don't want to invalidate all existing textures that users have authored, all existing UTextures have a UseLegacyGamma flag set to true, all new textures will be set to false. The flag is part of the DDC key calculation, but steps were taken so that when legacy is true, keys match existing keys to prevent universally invalidating all games DDCs just to make this change.
To summarize,
Old Pipeline: sRGB-Pow(2.2) -> Linear -> sRGB-IEC 61966
New Pipeline: sRGB-IEC 61966 -> Linear -> sRGB-IEC 61966
#codereview gil.gribb, nick.penwarden, martin.mittring
[CL 2571070 by Nick Darnell in Main branch]
2015-05-29 16:03:43 -04:00
InImage . CopyTo ( Image , ERawImageFormat : : BGRA8 , GammaSpace ) ;
2014-03-14 14:13:41 -04:00
Gamma Correction - Changing the way all FColors are converted into FLinearColor by default. Previously all sRGB textures coming into the engine along with all other usage of FColor -> FLinearColor used a lookup table that assumed the final gamma correction would simply be pow(color, 1/DisplayGamma). However, that's not the case, we use the IEC 61966-2-1 standard on most platforms for both the scene renderer, as well as for gamma correction in Slate. In Slate you should now see an image matching Photoshop instead of being slightly darker in the lower ranges. However, because we don't want to invalidate all existing textures that users have authored, all existing UTextures have a UseLegacyGamma flag set to true, all new textures will be set to false. The flag is part of the DDC key calculation, but steps were taken so that when legacy is true, keys match existing keys to prevent universally invalidating all games DDCs just to make this change.
To summarize,
Old Pipeline: sRGB-Pow(2.2) -> Linear -> sRGB-IEC 61966
New Pipeline: sRGB-IEC 61966 -> Linear -> sRGB-IEC 61966
#codereview gil.gribb, nick.penwarden, martin.mittring
[CL 2571070 by Nick Darnell in Main branch]
2015-05-29 16:03:43 -04:00
// Fill out the output information
OutCompressedImage . SizeX = Image . SizeX ;
OutCompressedImage . SizeY = Image . SizeY ;
OutCompressedImage . PixelFormat = CompressedPixelFormat ;
2014-03-14 14:13:41 -04:00
// Output Data
2015-03-28 06:38:28 -04:00
OutCompressedImage . RawData . SetNumUninitialized ( Image . SizeX * Image . SizeY * 4 ) ;
Gamma Correction - Changing the way all FColors are converted into FLinearColor by default. Previously all sRGB textures coming into the engine along with all other usage of FColor -> FLinearColor used a lookup table that assumed the final gamma correction would simply be pow(color, 1/DisplayGamma). However, that's not the case, we use the IEC 61966-2-1 standard on most platforms for both the scene renderer, as well as for gamma correction in Slate. In Slate you should now see an image matching Photoshop instead of being slightly darker in the lower ranges. However, because we don't want to invalidate all existing textures that users have authored, all existing UTextures have a UseLegacyGamma flag set to true, all new textures will be set to false. The flag is part of the DDC key calculation, but steps were taken so that when legacy is true, keys match existing keys to prevent universally invalidating all games DDCs just to make this change.
To summarize,
Old Pipeline: sRGB-Pow(2.2) -> Linear -> sRGB-IEC 61966
New Pipeline: sRGB-IEC 61966 -> Linear -> sRGB-IEC 61966
#codereview gil.gribb, nick.penwarden, martin.mittring
[CL 2571070 by Nick Darnell in Main branch]
2015-05-29 16:03:43 -04:00
void * MipData = ( void * ) Image . RawData . GetData ( ) ;
FMemory : : Memcpy ( MipData , Image . RawData . GetData ( ) , Image . SizeX * Image . SizeY * 4 ) ;
2014-03-14 14:13:41 -04:00
}
2014-12-04 10:35:50 -05:00
static int32 GetDefaultCompressionValue ( )
2014-06-17 18:27:26 -04:00
{
2014-06-20 17:03:43 -04:00
// start at default quality, then lookup in .ini file
2014-12-04 10:35:50 -05:00
int32 CompressionModeValue = 0 ;
2014-06-20 17:03:43 -04:00
GConfig - > GetInt ( TEXT ( " /Script/UnrealEd.CookerSettings " ) , TEXT ( " DefaultPVRTCQuality " ) , CompressionModeValue , GEngineIni ) ;
2014-06-17 18:27:26 -04:00
FParse : : Value ( FCommandLine : : Get ( ) , TEXT ( " -pvrtcquality= " ) , CompressionModeValue ) ;
CompressionModeValue = FMath : : Min < uint32 > ( CompressionModeValue , MAX_QUALITY ) ;
2014-12-04 10:35:50 -05:00
return CompressionModeValue ;
}
2014-06-17 18:27:26 -04:00
2014-12-04 10:35:50 -05:00
static FString GetPVRTCQualityString ( )
{
2014-06-17 18:27:26 -04:00
// convert to a string
FString CompressionMode ;
2014-12-04 10:35:50 -05:00
switch ( GetDefaultCompressionValue ( ) )
2014-06-17 18:27:26 -04:00
{
case 0 : CompressionMode = TEXT ( " fastest " ) ; break ;
case 1 : CompressionMode = TEXT ( " fast " ) ; break ;
case 2 : CompressionMode = TEXT ( " normal " ) ; break ;
case 3 : CompressionMode = TEXT ( " high " ) ; break ;
case 4 : CompressionMode = TEXT ( " best " ) ; break ;
default : UE_LOG ( LogTemp , Fatal , TEXT ( " Max quality higher than expected " ) ) ;
}
return CompressionMode ;
}
static uint16 GetPVRTCQualityForVersion ( )
{
// top 3 bits for compression value
2014-12-04 10:35:50 -05:00
return GetDefaultCompressionValue ( ) < < 13 ;
2014-06-17 18:27:26 -04:00
}
2014-03-14 14:13:41 -04:00
/**
* PVR texture format handler .
*/
class FTextureFormatPVR : public ITextureFormat
{
2014-06-13 06:14:46 -04:00
virtual bool AllowParallelBuild ( ) const override
2014-03-14 14:13:41 -04:00
{
return true ;
}
2014-06-13 06:14:46 -04:00
virtual uint16 GetVersion ( FName Format ) const override
2014-03-14 14:13:41 -04:00
{
2014-06-17 18:27:26 -04:00
return 7 + GetPVRTCQualityForVersion ( ) ;
2014-03-14 14:13:41 -04:00
}
2014-06-13 06:14:46 -04:00
virtual void GetSupportedFormats ( TArray < FName > & OutFormats ) const override
2014-03-14 14:13:41 -04:00
{
for ( int32 i = 0 ; i < ARRAY_COUNT ( GSupportedTextureFormatNames ) ; + + i )
{
OutFormats . Add ( GSupportedTextureFormatNames [ i ] ) ;
}
}
2014-06-13 06:14:46 -04:00
virtual FTextureFormatCompressorCaps GetFormatCapabilities ( ) const override
2014-04-23 20:04:50 -04:00
{
FTextureFormatCompressorCaps RetCaps ;
// PVR compressor is limited to <=4096 in any direction.
RetCaps . MaxTextureDimension = 4096 ;
return RetCaps ;
}
2014-03-14 14:13:41 -04:00
virtual bool CompressImage (
const FImage & InImage ,
const struct FTextureBuildSettings & BuildSettings ,
bool bImageHasAlphaChannel ,
FCompressedImage2D & OutCompressedImage
2014-06-13 06:14:46 -04:00
) const override
2014-03-14 14:13:41 -04:00
{
Gamma Correction - Changing the way all FColors are converted into FLinearColor by default. Previously all sRGB textures coming into the engine along with all other usage of FColor -> FLinearColor used a lookup table that assumed the final gamma correction would simply be pow(color, 1/DisplayGamma). However, that's not the case, we use the IEC 61966-2-1 standard on most platforms for both the scene renderer, as well as for gamma correction in Slate. In Slate you should now see an image matching Photoshop instead of being slightly darker in the lower ranges. However, because we don't want to invalidate all existing textures that users have authored, all existing UTextures have a UseLegacyGamma flag set to true, all new textures will be set to false. The flag is part of the DDC key calculation, but steps were taken so that when legacy is true, keys match existing keys to prevent universally invalidating all games DDCs just to make this change.
To summarize,
Old Pipeline: sRGB-Pow(2.2) -> Linear -> sRGB-IEC 61966
New Pipeline: sRGB-IEC 61966 -> Linear -> sRGB-IEC 61966
#codereview gil.gribb, nick.penwarden, martin.mittring
[CL 2571070 by Nick Darnell in Main branch]
2015-05-29 16:03:43 -04:00
// Get Raw Image Data from passed in FImage
FImage Image ;
InImage . CopyTo ( Image , ERawImageFormat : : BGRA8 , BuildSettings . GetGammaSpace ( ) ) ;
// Get the compressed format
EPixelFormat CompressedPixelFormat = PF_Unknown ;
2014-03-14 14:13:41 -04:00
if ( BuildSettings . TextureFormatName = = GTextureFormatNamePVRTC2 )
{
CompressedPixelFormat = PF_PVRTC2 ;
}
else if ( BuildSettings . TextureFormatName = = GTextureFormatNamePVRTC4 | | BuildSettings . TextureFormatName = = GTextureFormatNamePVRTCN )
{
CompressedPixelFormat = PF_PVRTC4 ;
}
else if ( BuildSettings . TextureFormatName = = GTextureFormatNameAutoPVRTC )
{
CompressedPixelFormat = bImageHasAlphaChannel ? PF_PVRTC4 : PF_PVRTC2 ;
}
// Verify Power of 2
if ( ! ValidateImagePower ( Image ) )
{
Gamma Correction - Changing the way all FColors are converted into FLinearColor by default. Previously all sRGB textures coming into the engine along with all other usage of FColor -> FLinearColor used a lookup table that assumed the final gamma correction would simply be pow(color, 1/DisplayGamma). However, that's not the case, we use the IEC 61966-2-1 standard on most platforms for both the scene renderer, as well as for gamma correction in Slate. In Slate you should now see an image matching Photoshop instead of being slightly darker in the lower ranges. However, because we don't want to invalidate all existing textures that users have authored, all existing UTextures have a UseLegacyGamma flag set to true, all new textures will be set to false. The flag is part of the DDC key calculation, but steps were taken so that when legacy is true, keys match existing keys to prevent universally invalidating all games DDCs just to make this change.
To summarize,
Old Pipeline: sRGB-Pow(2.2) -> Linear -> sRGB-IEC 61966
New Pipeline: sRGB-IEC 61966 -> Linear -> sRGB-IEC 61966
#codereview gil.gribb, nick.penwarden, martin.mittring
[CL 2571070 by Nick Darnell in Main branch]
2015-05-29 16:03:43 -04:00
UE_LOG ( LogTextureFormatPVR , Warning , TEXT ( " Mip size (%d,%d) does not have power-of-two dimensions and cannot be compressed to PVRTC%d " ) ,
2014-03-14 14:13:41 -04:00
Image . SizeX , Image . SizeY , CompressedPixelFormat = = PF_PVRTC2 ? 2 : 4 ) ;
Gamma Correction - Changing the way all FColors are converted into FLinearColor by default. Previously all sRGB textures coming into the engine along with all other usage of FColor -> FLinearColor used a lookup table that assumed the final gamma correction would simply be pow(color, 1/DisplayGamma). However, that's not the case, we use the IEC 61966-2-1 standard on most platforms for both the scene renderer, as well as for gamma correction in Slate. In Slate you should now see an image matching Photoshop instead of being slightly darker in the lower ranges. However, because we don't want to invalidate all existing textures that users have authored, all existing UTextures have a UseLegacyGamma flag set to true, all new textures will be set to false. The flag is part of the DDC key calculation, but steps were taken so that when legacy is true, keys match existing keys to prevent universally invalidating all games DDCs just to make this change.
To summarize,
Old Pipeline: sRGB-Pow(2.2) -> Linear -> sRGB-IEC 61966
New Pipeline: sRGB-IEC 61966 -> Linear -> sRGB-IEC 61966
#codereview gil.gribb, nick.penwarden, martin.mittring
[CL 2571070 by Nick Darnell in Main branch]
2015-05-29 16:03:43 -04:00
return false ;
2014-03-14 14:13:41 -04:00
}
// Squarify image
int32 FinalSquareSize = FGenericPlatformMath : : Max ( Image . SizeX , Image . SizeY ) ;
SquarifyImage ( Image , ( CompressedPixelFormat = = PF_PVRTC2 ) ? 16 : 8 ) ;
check ( Image . SizeX = = Image . SizeY ) ;
if ( BuildSettings . TextureFormatName = = GTextureFormatNamePVRTCN )
{
// Derive Z from X and Y to be consistent with BC5 normal maps used on PC (toss the texture's actual Z)
DeriveNormalZ ( Image ) ;
}
bool bCompressionSucceeded = true ;
int32 SliceSize = Image . SizeX * Image . SizeY ;
for ( int32 SliceIndex = 0 ; SliceIndex < Image . NumSlices & & bCompressionSucceeded ; + + SliceIndex )
{
TArray < uint8 > CompressedSliceData ;
bCompressionSucceeded = CompressImageUsingPVRTexTool (
Image . AsBGRA8 ( ) + SliceIndex * SliceSize ,
CompressedPixelFormat ,
Image . SizeX ,
Image . SizeY ,
Gamma Correction - Changing the way all FColors are converted into FLinearColor by default. Previously all sRGB textures coming into the engine along with all other usage of FColor -> FLinearColor used a lookup table that assumed the final gamma correction would simply be pow(color, 1/DisplayGamma). However, that's not the case, we use the IEC 61966-2-1 standard on most platforms for both the scene renderer, as well as for gamma correction in Slate. In Slate you should now see an image matching Photoshop instead of being slightly darker in the lower ranges. However, because we don't want to invalidate all existing textures that users have authored, all existing UTextures have a UseLegacyGamma flag set to true, all new textures will be set to false. The flag is part of the DDC key calculation, but steps were taken so that when legacy is true, keys match existing keys to prevent universally invalidating all games DDCs just to make this change.
To summarize,
Old Pipeline: sRGB-Pow(2.2) -> Linear -> sRGB-IEC 61966
New Pipeline: sRGB-IEC 61966 -> Linear -> sRGB-IEC 61966
#codereview gil.gribb, nick.penwarden, martin.mittring
[CL 2571070 by Nick Darnell in Main branch]
2015-05-29 16:03:43 -04:00
Image . IsGammaCorrected ( ) ,
2014-03-14 14:13:41 -04:00
FinalSquareSize ,
CompressedSliceData
) ;
OutCompressedImage . RawData . Append ( CompressedSliceData ) ;
}
if ( bCompressionSucceeded )
{
OutCompressedImage . SizeX = FinalSquareSize ;
OutCompressedImage . SizeY = FinalSquareSize ;
OutCompressedImage . PixelFormat = CompressedPixelFormat ;
}
Gamma Correction - Changing the way all FColors are converted into FLinearColor by default. Previously all sRGB textures coming into the engine along with all other usage of FColor -> FLinearColor used a lookup table that assumed the final gamma correction would simply be pow(color, 1/DisplayGamma). However, that's not the case, we use the IEC 61966-2-1 standard on most platforms for both the scene renderer, as well as for gamma correction in Slate. In Slate you should now see an image matching Photoshop instead of being slightly darker in the lower ranges. However, because we don't want to invalidate all existing textures that users have authored, all existing UTextures have a UseLegacyGamma flag set to true, all new textures will be set to false. The flag is part of the DDC key calculation, but steps were taken so that when legacy is true, keys match existing keys to prevent universally invalidating all games DDCs just to make this change.
To summarize,
Old Pipeline: sRGB-Pow(2.2) -> Linear -> sRGB-IEC 61966
New Pipeline: sRGB-IEC 61966 -> Linear -> sRGB-IEC 61966
#codereview gil.gribb, nick.penwarden, martin.mittring
[CL 2571070 by Nick Darnell in Main branch]
2015-05-29 16:03:43 -04:00
// Return success status
return bCompressionSucceeded ;
2014-03-14 14:13:41 -04:00
}
static bool CompressImageUsingPVRTexTool ( void * SourceData , EPixelFormat PixelFormat , int32 SizeX , int32 SizeY , bool bSRGB , int32 FinalSquareSize , TArray < uint8 > & OutCompressedData )
{
// Figure out whether to use 2 bits or 4 bits per pixel (PVRTC2/PVRTC4)
bool bIsPVRTC2 = ( PixelFormat = = PF_PVRTC2 ) ;
const int32 BlockSizeX = bIsPVRTC2 ? 8 : 4 ; // PVRTC2 uses 8x4 blocks, PVRTC4 uses 4x4 blocks
const int32 BlockSizeY = 4 ;
const int32 BlockBytes = 8 ; // Both PVRTC2 and PVRTC4 are 8 bytes per block
const uint32 DestSizeX = FinalSquareSize ;
const uint32 DestSizeY = FinalSquareSize ;
// min 2x2 blocks per mip
const uint32 DestBlocksX = FGenericPlatformMath : : Max < uint32 > ( DestSizeX / BlockSizeX , 2 ) ;
const uint32 DestBlocksY = FGenericPlatformMath : : Max < uint32 > ( DestSizeY / BlockSizeY , 2 ) ;
const uint32 DestNumBytes = DestBlocksX * DestBlocksY * BlockBytes ;
// If using an image that's too small, compressor needs to generate mips for us with an upscaled image
check ( SizeX = = SizeY ) ;
const int32 SourceSquareSize = SizeX ;
bool bGenerateMips = ( FinalSquareSize < SourceSquareSize ) ? true : false ;
// Allocate space to store compressed data.
OutCompressedData . Empty ( DestNumBytes ) ;
OutCompressedData . AddUninitialized ( DestNumBytes ) ;
void * MipData = OutCompressedData . GetData ( ) ;
// Write SourceData into PVR file on disk
// Init Header
FPVRHeader PVRHeader ;
PVRHeader . Version = 0x03525650 ; // endianess does not match
PVRHeader . Flags = 0 ;
PVRHeader . PixelFormat = 0x0808080861726762 ; // Format of the UTexture 8bpp and BGRA ordered
PVRHeader . ColorSpace = 0 ; // Setting to 1 indicates SRGB, but causes PVRTexTool to unpack to linear. We want the image to remain in sRGB space as we do the conversion in the shader.
PVRHeader . ChannelType = 0 ;
PVRHeader . Height = SizeY ;
PVRHeader . Width = SizeX ;
PVRHeader . Depth = 1 ;
PVRHeader . NumSurfaces = 1 ;
PVRHeader . NumFaces = 1 ;
PVRHeader . NumMipmaps = 1 ;
PVRHeader . MetaDataSize = 0 ;
// Get file paths for intermediates. Unique path to avoid filename collision
FGuid Guid ;
FPlatformMisc : : CreateGuid ( Guid ) ;
FString InputFilePath = FString : : Printf ( TEXT ( " Cache/%x%x%x%xRGBToPVRIn.pvr " ) , Guid . A , Guid . B , Guid . C , Guid . D ) ;
InputFilePath = FPaths : : GameIntermediateDir ( ) + InputFilePath ;
FString OutputFilePath = FString : : Printf ( TEXT ( " Cache/%x%x%x%xRGBToPVROut.pvr " ) , Guid . A , Guid . B , Guid . C , Guid . D ) ;
OutputFilePath = FPaths : : GameIntermediateDir ( ) + OutputFilePath ;
FArchive * PVRFile = NULL ;
while ( ! PVRFile )
{
PVRFile = IFileManager : : Get ( ) . CreateFileWriter ( * InputFilePath ) ; // Occasionally returns NULL due to error code ERROR_SHARING_VIOLATION
FPlatformProcess : : Sleep ( 0.01f ) ; // ... no choice but to wait for the file to become free to access
}
// Write out header
uint32 HeaderSize = sizeof ( PVRHeader ) ;
check ( HeaderSize = = 52 ) ;
PVRFile - > Serialize ( & PVRHeader , HeaderSize ) ;
// Write out uncompressed data
PVRFile - > Serialize ( SourceData , SizeX * SizeY * sizeof ( uint32 ) ) ;
// Finished writing file
PVRFile - > Close ( ) ;
delete PVRFile ;
// Compress PVR file to PVRTC
Gamma Correction - Changing the way all FColors are converted into FLinearColor by default. Previously all sRGB textures coming into the engine along with all other usage of FColor -> FLinearColor used a lookup table that assumed the final gamma correction would simply be pow(color, 1/DisplayGamma). However, that's not the case, we use the IEC 61966-2-1 standard on most platforms for both the scene renderer, as well as for gamma correction in Slate. In Slate you should now see an image matching Photoshop instead of being slightly darker in the lower ranges. However, because we don't want to invalidate all existing textures that users have authored, all existing UTextures have a UseLegacyGamma flag set to true, all new textures will be set to false. The flag is part of the DDC key calculation, but steps were taken so that when legacy is true, keys match existing keys to prevent universally invalidating all games DDCs just to make this change.
To summarize,
Old Pipeline: sRGB-Pow(2.2) -> Linear -> sRGB-IEC 61966
New Pipeline: sRGB-IEC 61966 -> Linear -> sRGB-IEC 61966
#codereview gil.gribb, nick.penwarden, martin.mittring
[CL 2571070 by Nick Darnell in Main branch]
2015-05-29 16:03:43 -04:00
FString CompressionMode = GetPVRTCQualityString ( ) ;
2014-03-14 14:13:41 -04:00
2014-06-17 18:27:26 -04:00
// Use PowerVR's new CLI tool commandline
FString Params = FString : : Printf ( TEXT ( " -i \" %s \" -o \" %s \" %s -legacypvr -q pvrtc%s -f PVRTC1_%d " ) ,
2014-03-14 14:13:41 -04:00
* InputFilePath , * OutputFilePath ,
bGenerateMips ? TEXT ( " -m " ) : TEXT ( " " ) ,
Gamma Correction - Changing the way all FColors are converted into FLinearColor by default. Previously all sRGB textures coming into the engine along with all other usage of FColor -> FLinearColor used a lookup table that assumed the final gamma correction would simply be pow(color, 1/DisplayGamma). However, that's not the case, we use the IEC 61966-2-1 standard on most platforms for both the scene renderer, as well as for gamma correction in Slate. In Slate you should now see an image matching Photoshop instead of being slightly darker in the lower ranges. However, because we don't want to invalidate all existing textures that users have authored, all existing UTextures have a UseLegacyGamma flag set to true, all new textures will be set to false. The flag is part of the DDC key calculation, but steps were taken so that when legacy is true, keys match existing keys to prevent universally invalidating all games DDCs just to make this change.
To summarize,
Old Pipeline: sRGB-Pow(2.2) -> Linear -> sRGB-IEC 61966
New Pipeline: sRGB-IEC 61966 -> Linear -> sRGB-IEC 61966
#codereview gil.gribb, nick.penwarden, martin.mittring
[CL 2571070 by Nick Darnell in Main branch]
2015-05-29 16:03:43 -04:00
* CompressionMode ,
2014-03-14 14:13:41 -04:00
bIsPVRTC2 ? 2 : 4 ) ;
2014-06-17 18:27:26 -04:00
# if PLATFORM_MAC
2014-06-20 10:41:51 -04:00
FString CompressorPath ( FPaths : : EngineDir ( ) + TEXT ( " Binaries/ThirdParty/ImgTec/PVRTexToolCLI " ) ) ;
2014-07-08 00:06:17 -04:00
# elif PLATFORM_LINUX
FString CompressorPath ( FPaths : : EngineDir ( ) + TEXT ( " Binaries/ThirdParty/ImgTec/PVRTexToolCLI.lnx " ) ) ;
# elif PLATFORM_WINDOWS
2014-06-17 18:27:26 -04:00
FString CompressorPath ( FPaths : : EngineDir ( ) + TEXT ( " Binaries/ThirdParty/ImgTec/PVRTexToolCLI.exe " ) ) ;
2014-07-08 00:06:17 -04:00
# else
# error Unsupported platform
2014-03-14 14:13:41 -04:00
# endif
2014-06-17 18:27:26 -04:00
UE_LOG ( LogTemp , Log , TEXT ( " Running texturetool with '%s' " ) , * Params ) ;
2014-03-14 14:13:41 -04:00
// Give a debug message about the process
if ( IsRunningCommandlet ( ) )
{
//UE_LOG(LogTextureFormatPVR, Display, TEXT("Compressing mip (%dx%d) to PVRTC%d for mobile devices..."), ImageSizeX, ImageSizeY, bIsPVRTC2 ? 2 : 4);
}
// Start Compressor
FProcHandle Proc = FPlatformProcess : : CreateProc ( * CompressorPath , * Params , true , false , false , NULL , - 1 , NULL , NULL ) ;
bool bConversionWasSuccessful = true ;
// Failed to start the compressor process?
if ( ! Proc . IsValid ( ) )
{
UE_LOG ( LogTextureFormatPVR , Error , TEXT ( " Failed to start PVR compressor tool. (Path:%s) " ) , * CompressorPath ) ;
bConversionWasSuccessful = false ;
}
if ( bConversionWasSuccessful )
{
// Wait for the process to complete
int ReturnCode ;
while ( ! FPlatformProcess : : GetProcReturnCode ( Proc , & ReturnCode ) )
{
FPlatformProcess : : Sleep ( 0.01f ) ;
}
2015-03-12 00:06:15 -04:00
FPlatformProcess : : CloseProc ( Proc ) ;
2014-03-14 14:13:41 -04:00
// Did it fail?
if ( ReturnCode ! = 0 )
{
UE_LOG ( LogTextureFormatPVR , Error , TEXT ( " PVR tool Failed with Return Code %d Mip Size (%d,%d) " ) , ReturnCode , SizeX , SizeY ) ;
bConversionWasSuccessful = false ;
}
}
// Open compressed file and put the data in OutCompressedImage
if ( bConversionWasSuccessful )
{
// Calculate which mip to pull from compressed image
int32 MipLevel = 0 ;
{
int32 i = SizeX ;
while ( i > FinalSquareSize )
{
i / = 2 ;
+ + MipLevel ;
}
}
// Get Raw File Data
TArray < uint8 > PVRData ;
FFileHelper : : LoadFileToArray ( PVRData , * OutputFilePath ) ;
// Process It
FPVRHeader * Header = ( FPVRHeader * ) PVRData . GetData ( ) ;
// Calculate the offset to get to the mip data
int FileOffset = HeaderSize ;
for ( int32 i = 0 ; i < MipLevel ; + + i )
{
// Get the mip size for each image before the mip we want
uint32 LocalMipSizeX = FGenericPlatformMath : : Max < uint32 > ( SizeX > > i , 1 ) ;
uint32 LocalMipSizeY = LocalMipSizeX ;
uint32 LocalBlocksX = FGenericPlatformMath : : Max < uint32 > ( LocalMipSizeX / BlockSizeX , 2 ) ;
uint32 LocalBlocksY = FGenericPlatformMath : : Max < uint32 > ( LocalMipSizeY / BlockSizeY , 2 ) ;
uint32 LocalMipSize = LocalBlocksX * LocalBlocksY * BlockBytes ;
// Add that mip's size to the offset
FileOffset + = LocalMipSize ;
}
// Copy compressed data
2014-09-29 04:23:44 -04:00
FMemory : : Memcpy ( MipData , PVRData . GetData ( ) + FileOffset , DestNumBytes ) ;
2014-03-14 14:13:41 -04:00
}
// Delete intermediate files
IFileManager : : Get ( ) . Delete ( * InputFilePath ) ;
IFileManager : : Get ( ) . Delete ( * OutputFilePath ) ;
return bConversionWasSuccessful ;
}
} ;
/**
* Module for PVR texture compression .
*/
static ITextureFormat * Singleton = NULL ;
class FTextureFormatPVRModule : public ITextureFormatModule
{
public :
virtual ~ FTextureFormatPVRModule ( )
{
delete Singleton ;
Singleton = NULL ;
}
virtual ITextureFormat * GetTextureFormat ( )
{
if ( ! Singleton )
{
Singleton = new FTextureFormatPVR ( ) ;
}
return Singleton ;
}
} ;
IMPLEMENT_MODULE ( FTextureFormatPVRModule , TextureFormatPVR ) ;