2016-01-07 08:17:16 -05:00
|
|
|
// Copyright 1998-2016 Epic Games, Inc. All Rights Reserved.
|
2014-09-18 17:49:40 -04:00
|
|
|
|
|
|
|
|
#include "Core.h"
|
|
|
|
|
#include "CoreMisc.h"
|
|
|
|
|
#include "ImageCore.h"
|
|
|
|
|
#include "ImageWrapper.h"
|
|
|
|
|
#include "ModuleInterface.h"
|
|
|
|
|
#include "ModuleManager.h"
|
|
|
|
|
#include "TargetPlatform.h"
|
|
|
|
|
#include "TextureCompressorModule.h"
|
|
|
|
|
#include "PixelFormat.h"
|
|
|
|
|
#include "GenericPlatformProcess.h"
|
|
|
|
|
|
2014-12-04 10:35:50 -05:00
|
|
|
|
2014-12-04 17:28:16 -05:00
|
|
|
// increment this if you change anything that will affect compression in this file, including FORCED_NORMAL_MAP_COMPRESSION_SIZE_VALUE
|
2015-03-11 17:28:22 -04:00
|
|
|
#define BASE_FORMAT_VERSION 34
|
2014-12-04 17:28:16 -05:00
|
|
|
|
2014-12-04 10:35:50 -05:00
|
|
|
#define MAX_QUALITY_BY_SIZE 4
|
|
|
|
|
#define MAX_QUALITY_BY_SPEED 3
|
2014-12-04 17:28:16 -05:00
|
|
|
#define FORCED_NORMAL_MAP_COMPRESSION_SIZE_VALUE 4
|
2014-12-04 10:35:50 -05:00
|
|
|
|
|
|
|
|
|
2014-09-18 17:49:40 -04:00
|
|
|
DEFINE_LOG_CATEGORY_STATIC(LogTextureFormatASTC, Log, All);
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Macro trickery for supported format names.
|
|
|
|
|
*/
|
|
|
|
|
#define ENUM_SUPPORTED_FORMATS(op) \
|
|
|
|
|
op(ASTC_RGB) \
|
|
|
|
|
op(ASTC_RGBA) \
|
|
|
|
|
op(ASTC_RGBAuto) \
|
|
|
|
|
op(ASTC_NormalAG) \
|
|
|
|
|
op(ASTC_NormalRG)
|
|
|
|
|
|
|
|
|
|
#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
|
|
|
|
|
|
|
|
|
|
// ASTC file header format
|
|
|
|
|
#if PLATFORM_SUPPORTS_PRAGMA_PACK
|
|
|
|
|
#pragma pack(push, 4)
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
#define ASTC_MAGIC_CONSTANT 0x5CA1AB13
|
|
|
|
|
struct FASTCHeader
|
|
|
|
|
{
|
|
|
|
|
uint32 Magic;
|
|
|
|
|
uint8 BlockSizeX;
|
|
|
|
|
uint8 BlockSizeY;
|
|
|
|
|
uint8 BlockSizeZ;
|
|
|
|
|
uint8 TexelCountX[3];
|
|
|
|
|
uint8 TexelCountY[3];
|
|
|
|
|
uint8 TexelCountZ[3];
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
#if PLATFORM_SUPPORTS_PRAGMA_PACK
|
|
|
|
|
#pragma pack(pop)
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
IImageWrapperModule& ImageWrapperModule = FModuleManager::LoadModuleChecked<IImageWrapperModule>(FName("ImageWrapper"));
|
|
|
|
|
|
2014-12-04 10:35:50 -05:00
|
|
|
|
|
|
|
|
static int32 GetDefaultCompressionBySizeValue()
|
|
|
|
|
{
|
|
|
|
|
// start at default quality, then lookup in .ini file
|
|
|
|
|
int32 CompressionModeValue = 0;
|
|
|
|
|
GConfig->GetInt(TEXT("/Script/UnrealEd.CookerSettings"), TEXT("DefaultASTCQualityBySize"), CompressionModeValue, GEngineIni);
|
|
|
|
|
|
|
|
|
|
FParse::Value(FCommandLine::Get(), TEXT("-astcqualitybysize="), CompressionModeValue);
|
|
|
|
|
CompressionModeValue = FMath::Min<uint32>(CompressionModeValue, MAX_QUALITY_BY_SIZE);
|
|
|
|
|
|
|
|
|
|
return CompressionModeValue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int32 GetDefaultCompressionBySpeedValue()
|
|
|
|
|
{
|
|
|
|
|
// start at default quality, then lookup in .ini file
|
|
|
|
|
int32 CompressionModeValue = 0;
|
|
|
|
|
GConfig->GetInt(TEXT("/Script/UnrealEd.CookerSettings"), TEXT("DefaultASTCQualityBySpeed"), CompressionModeValue, GEngineIni);
|
|
|
|
|
|
|
|
|
|
FParse::Value(FCommandLine::Get(), TEXT("-astcqualitybyspeed="), CompressionModeValue);
|
|
|
|
|
CompressionModeValue = FMath::Min<uint32>(CompressionModeValue, MAX_QUALITY_BY_SPEED);
|
|
|
|
|
|
|
|
|
|
return CompressionModeValue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static FString GetQualityString(int32 OverrideSizeValue=-1, int32 OverrideSpeedValue=-1)
|
|
|
|
|
{
|
|
|
|
|
// convert to a string
|
|
|
|
|
FString CompressionMode;
|
|
|
|
|
switch (OverrideSizeValue >= 0 ? OverrideSizeValue : GetDefaultCompressionBySizeValue())
|
|
|
|
|
{
|
|
|
|
|
case 0: CompressionMode = TEXT("12x12"); break;
|
|
|
|
|
case 1: CompressionMode = TEXT("10x10"); break;
|
|
|
|
|
case 2: CompressionMode = TEXT("8x8"); break;
|
|
|
|
|
case 3: CompressionMode = TEXT("6x6"); break;
|
|
|
|
|
case 4: CompressionMode = TEXT("4x4"); break;
|
|
|
|
|
default: UE_LOG(LogTemp, Fatal, TEXT("ASTC size quality higher than expected"));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
switch (OverrideSpeedValue >= 0 ? OverrideSpeedValue : GetDefaultCompressionBySpeedValue())
|
|
|
|
|
{
|
|
|
|
|
case 0: CompressionMode += TEXT(" -veryfast"); break;
|
|
|
|
|
case 1: CompressionMode += TEXT(" -fast"); break;
|
|
|
|
|
case 2: CompressionMode += TEXT(" -medium"); break;
|
|
|
|
|
case 3: CompressionMode += TEXT(" -thorough"); break;
|
|
|
|
|
default: UE_LOG(LogTemp, Fatal, TEXT("ASTC speed quality higher than expected"));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return CompressionMode;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static EPixelFormat GetQualityFormat(int32 OverrideSizeValue=-1)
|
|
|
|
|
{
|
|
|
|
|
// convert to a string
|
|
|
|
|
EPixelFormat Format = PF_Unknown;
|
|
|
|
|
switch (OverrideSizeValue >= 0 ? OverrideSizeValue : GetDefaultCompressionBySizeValue())
|
|
|
|
|
{
|
|
|
|
|
case 0: Format = PF_ASTC_12x12; break;
|
|
|
|
|
case 1: Format = PF_ASTC_10x10; break;
|
|
|
|
|
case 2: Format = PF_ASTC_8x8; break;
|
|
|
|
|
case 3: Format = PF_ASTC_6x6; break;
|
|
|
|
|
case 4: Format = PF_ASTC_4x4; break;
|
|
|
|
|
default: UE_LOG(LogTemp, Fatal, TEXT("Max quality higher than expected"));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return Format;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static uint16 GetQualityVersion()
|
|
|
|
|
{
|
|
|
|
|
// top 3 bits for size compression value, and next 3 for speed
|
|
|
|
|
return (GetDefaultCompressionBySizeValue() << 13) | (GetDefaultCompressionBySpeedValue() << 10);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2014-09-18 17:49:40 -04:00
|
|
|
static bool CompressSliceToASTC(
|
|
|
|
|
const void* SourceData,
|
|
|
|
|
int32 SizeX,
|
|
|
|
|
int32 SizeY,
|
|
|
|
|
FString CompressionParameters,
|
|
|
|
|
TArray<uint8>& OutCompressedData
|
|
|
|
|
)
|
|
|
|
|
{
|
|
|
|
|
// Always Y-invert the image prior to compression for proper orientation post-compression
|
2015-03-11 17:28:22 -04:00
|
|
|
TArray<uint8> LineBuffer;
|
|
|
|
|
LineBuffer.AddUninitialized(16384 * 4);
|
2014-09-18 17:49:40 -04:00
|
|
|
uint32 LineSize = SizeX * 4;
|
|
|
|
|
for (int32 LineIndex = 0; LineIndex < (SizeY / 2); LineIndex++)
|
|
|
|
|
{
|
|
|
|
|
uint8* LineData0 = ((uint8*)SourceData) + (LineSize * LineIndex);
|
|
|
|
|
uint8* LineData1 = ((uint8*)SourceData) + (LineSize * (SizeY - LineIndex - 1));
|
2015-03-11 17:28:22 -04:00
|
|
|
FMemory::Memcpy(LineBuffer.GetData(), LineData0, LineSize);
|
|
|
|
|
FMemory::Memcpy(LineData0, LineData1, LineSize);
|
|
|
|
|
FMemory::Memcpy(LineData1, LineBuffer.GetData(), LineSize);
|
2014-09-18 17:49:40 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Compress and retrieve the PNG data to write out to disk
|
|
|
|
|
IImageWrapperPtr ImageWrapper = ImageWrapperModule.CreateImageWrapper(EImageFormat::PNG);
|
2014-12-04 10:35:50 -05:00
|
|
|
ImageWrapper->SetRaw(SourceData, SizeX * SizeY * 4, SizeX, SizeY, ERGBFormat::RGBA, 8);
|
2014-09-18 17:49:40 -04:00
|
|
|
const TArray<uint8>& FileData = ImageWrapper->GetCompressed();
|
|
|
|
|
int32 FileDataSize = FileData.Num();
|
|
|
|
|
|
|
|
|
|
FGuid Guid;
|
|
|
|
|
FPlatformMisc::CreateGuid(Guid);
|
|
|
|
|
FString InputFilePath = FString::Printf(TEXT("Cache/%08x-%08x-%08x-%08x-RGBToASTCIn.png"), Guid.A, Guid.B, Guid.C, Guid.D);
|
|
|
|
|
FString OutputFilePath = FString::Printf(TEXT("Cache/%08x-%08x-%08x-%08x-RGBToASTCOut.astc"), Guid.A, Guid.B, Guid.C, Guid.D);
|
|
|
|
|
|
|
|
|
|
InputFilePath = FPaths::GameIntermediateDir() + InputFilePath;
|
|
|
|
|
OutputFilePath = FPaths::GameIntermediateDir() + OutputFilePath;
|
|
|
|
|
|
|
|
|
|
FArchive* PNGFile = NULL;
|
|
|
|
|
while (!PNGFile)
|
|
|
|
|
{
|
2014-12-04 10:35:50 -05:00
|
|
|
PNGFile = IFileManager::Get().CreateFileWriter(*InputFilePath); // Occasionally returns NULL due to error code ERROR_SHARING_VIOLATION
|
2014-09-18 17:49:40 -04:00
|
|
|
FPlatformProcess::Sleep(0.01f); // ... no choice but to wait for the file to become free to access
|
|
|
|
|
}
|
|
|
|
|
PNGFile->Serialize((void*)&FileData[0], FileDataSize);
|
|
|
|
|
delete PNGFile;
|
|
|
|
|
|
|
|
|
|
// Compress PNG file to ASTC (using the reference astcenc.exe from ARM)
|
2015-03-11 17:28:22 -04:00
|
|
|
FString Params = FString::Printf(TEXT("-c \"%s\" \"%s\" %s"),
|
2014-09-18 17:49:40 -04:00
|
|
|
*InputFilePath,
|
|
|
|
|
*OutputFilePath,
|
|
|
|
|
*CompressionParameters
|
|
|
|
|
);
|
|
|
|
|
|
2014-12-04 10:35:50 -05:00
|
|
|
UE_LOG(LogTextureFormatASTC, Display, TEXT("Compressing to ASTC (%s)..."), *CompressionParameters);
|
2014-09-18 17:49:40 -04:00
|
|
|
|
|
|
|
|
// Start Compressor
|
2014-12-04 10:35:50 -05:00
|
|
|
#if PLATFORM_MAC
|
|
|
|
|
FString CompressorPath(FPaths::EngineDir() + TEXT("Binaries/ThirdParty/ARM/Mac/astcenc"));
|
|
|
|
|
#elif PLATFORM_LINUX
|
|
|
|
|
FString CompressorPath(FPaths::EngineDir() + TEXT("Binaries/ThirdParty/ARM/Linux32/astcenc"));
|
|
|
|
|
#elif PLATFORM_WINDOWS
|
|
|
|
|
FString CompressorPath(FPaths::EngineDir() + TEXT("Binaries/ThirdParty/ARM/Win32/astcenc.exe"));
|
|
|
|
|
#else
|
|
|
|
|
#error Unsupported platform
|
|
|
|
|
#endif
|
|
|
|
|
FProcHandle Proc = FPlatformProcess::CreateProc(*CompressorPath, *Params, true, false, false, NULL, -1, NULL, NULL);
|
2014-09-18 17:49:40 -04:00
|
|
|
|
|
|
|
|
// Failed to start the compressor process
|
2014-12-04 10:35:50 -05:00
|
|
|
if (!Proc.IsValid())
|
2014-09-18 17:49:40 -04:00
|
|
|
{
|
2014-12-04 10:35:50 -05:00
|
|
|
UE_LOG(LogTextureFormatASTC, Error, TEXT("Failed to start astcenc for compressing images (%s)"), *CompressorPath);
|
2014-09-18 17:49:40 -04:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Wait for the process to complete
|
|
|
|
|
int ReturnCode;
|
|
|
|
|
while (!FPlatformProcess::GetProcReturnCode(Proc, &ReturnCode))
|
|
|
|
|
{
|
|
|
|
|
FPlatformProcess::Sleep(0.01f);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Did it work?
|
|
|
|
|
bool bConversionWasSuccessful = (ReturnCode == 0);
|
|
|
|
|
|
|
|
|
|
// Open compressed file and put the data in OutCompressedImage
|
|
|
|
|
if (bConversionWasSuccessful)
|
|
|
|
|
{
|
|
|
|
|
// Get raw file data
|
|
|
|
|
TArray<uint8> ASTCData;
|
|
|
|
|
FFileHelper::LoadFileToArray(ASTCData, *OutputFilePath);
|
|
|
|
|
|
|
|
|
|
// Process it
|
|
|
|
|
FASTCHeader* Header = (FASTCHeader*)ASTCData.GetData();
|
|
|
|
|
|
|
|
|
|
// Fiddle with the texel count data to get the right value
|
|
|
|
|
uint32 TexelCountX =
|
|
|
|
|
(Header->TexelCountX[0] << 0) +
|
|
|
|
|
(Header->TexelCountX[1] << 8) +
|
|
|
|
|
(Header->TexelCountX[2] << 16);
|
|
|
|
|
uint32 TexelCountY =
|
|
|
|
|
(Header->TexelCountY[0] << 0) +
|
|
|
|
|
(Header->TexelCountY[1] << 8) +
|
|
|
|
|
(Header->TexelCountY[2] << 16);
|
|
|
|
|
uint32 TexelCountZ =
|
|
|
|
|
(Header->TexelCountZ[0] << 0) +
|
|
|
|
|
(Header->TexelCountZ[1] << 8) +
|
|
|
|
|
(Header->TexelCountZ[2] << 16);
|
|
|
|
|
|
2014-12-04 10:35:50 -05:00
|
|
|
// UE_LOG(LogTextureFormatASTC, Display, TEXT(" Compressed Texture Header:"));
|
|
|
|
|
// UE_LOG(LogTextureFormatASTC, Display, TEXT(" Magic: %x"), Header->Magic);
|
|
|
|
|
// UE_LOG(LogTextureFormatASTC, Display, TEXT(" BlockSizeX: %u"), Header->BlockSizeX);
|
|
|
|
|
// UE_LOG(LogTextureFormatASTC, Display, TEXT(" BlockSizeY: %u"), Header->BlockSizeY);
|
|
|
|
|
// UE_LOG(LogTextureFormatASTC, Display, TEXT(" BlockSizeZ: %u"), Header->BlockSizeZ);
|
|
|
|
|
// UE_LOG(LogTextureFormatASTC, Display, TEXT(" TexelCountX: %u"), TexelCountX);
|
|
|
|
|
// UE_LOG(LogTextureFormatASTC, Display, TEXT(" TexelCountY: %u"), TexelCountY);
|
|
|
|
|
// UE_LOG(LogTextureFormatASTC, Display, TEXT(" TexelCountZ: %u"), TexelCountZ);
|
2014-09-18 17:49:40 -04:00
|
|
|
|
|
|
|
|
// Calculate size of this mip in blocks
|
|
|
|
|
uint32 MipSizeX = (TexelCountX + Header->BlockSizeX - 1) / Header->BlockSizeX;
|
|
|
|
|
uint32 MipSizeY = (TexelCountY + Header->BlockSizeY - 1) / Header->BlockSizeY;
|
|
|
|
|
|
|
|
|
|
// A block is always 16 bytes
|
|
|
|
|
uint32 MipSize = MipSizeX * MipSizeY * 16;
|
|
|
|
|
|
|
|
|
|
// Copy the compressed data
|
|
|
|
|
OutCompressedData.Empty(MipSize);
|
|
|
|
|
OutCompressedData.AddUninitialized(MipSize);
|
2014-12-04 10:35:50 -05:00
|
|
|
void* MipData = OutCompressedData.GetData();
|
2014-09-18 17:49:40 -04:00
|
|
|
|
|
|
|
|
// Calculate the offset to get to the mip data
|
|
|
|
|
check(sizeof(FASTCHeader) == 16);
|
|
|
|
|
check(ASTCData.Num() == (sizeof(FASTCHeader) + MipSize));
|
|
|
|
|
FMemory::Memcpy(MipData, ASTCData.GetData() + sizeof(FASTCHeader), MipSize);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
UE_LOG(LogTextureFormatASTC, Error, TEXT("ASTC encoder failed with return code %d, mip size (%d, %d)"), ReturnCode, SizeX, SizeY);
|
2014-12-04 10:35:50 -05:00
|
|
|
IFileManager::Get().Delete(*InputFilePath);
|
|
|
|
|
IFileManager::Get().Delete(*OutputFilePath);
|
2014-09-18 17:49:40 -04:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Delete intermediate files
|
2014-12-04 10:35:50 -05:00
|
|
|
IFileManager::Get().Delete(*InputFilePath);
|
|
|
|
|
IFileManager::Get().Delete(*OutputFilePath);
|
2014-09-18 17:49:40 -04:00
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* ASTC texture format handler.
|
|
|
|
|
*/
|
|
|
|
|
class FTextureFormatASTC : public ITextureFormat
|
|
|
|
|
{
|
2014-12-04 10:46:04 -05:00
|
|
|
virtual bool AllowParallelBuild() const override
|
2014-09-18 17:49:40 -04:00
|
|
|
{
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2014-12-04 10:46:04 -05:00
|
|
|
virtual uint16 GetVersion(FName Format) const override
|
2014-09-18 17:49:40 -04:00
|
|
|
{
|
2014-12-04 17:28:16 -05:00
|
|
|
return GetQualityVersion() + BASE_FORMAT_VERSION;
|
2014-12-04 10:35:50 -05:00
|
|
|
}
|
|
|
|
|
|
2014-12-04 17:28:16 -05:00
|
|
|
// // Since we want to have per texture [group] compression settings, we need to have the key based on the texture
|
|
|
|
|
// virtual FString GetDerivedDataKeyString(const class UTexture& Texture) const override
|
|
|
|
|
// {
|
2015-03-17 09:50:32 -04:00
|
|
|
// const int32 LODBias = UDeviceProfileManager::Get().GetActiveProfile()->GetTextureLODSettings()->CalculateLODBias(Texture.Source.GetSizeX(), Texture.Source.GetSizeY(), Texture.LODGroup, Texture.LODBias, Texture.NumCinematicMipLevels, Texture.MipGenSettings);
|
2014-12-04 17:28:16 -05:00
|
|
|
// check(LODBias >= 0);
|
|
|
|
|
// return FString::Printf(TEXT("%02u%d_"), (uint32)LODBias, CVarVirtualTextureReducedMemoryEnabled->GetValueOnGameThread());
|
|
|
|
|
// }
|
|
|
|
|
|
2014-12-04 10:35:50 -05:00
|
|
|
virtual FTextureFormatCompressorCaps GetFormatCapabilities() const override
|
|
|
|
|
{
|
|
|
|
|
FTextureFormatCompressorCaps RetCaps;
|
|
|
|
|
// use defaults
|
|
|
|
|
return RetCaps;
|
2014-09-18 17:49:40 -04:00
|
|
|
}
|
|
|
|
|
|
2014-12-04 10:46:04 -05:00
|
|
|
virtual void GetSupportedFormats(TArray<FName>& OutFormats) const override
|
2014-09-18 17:49:40 -04:00
|
|
|
{
|
|
|
|
|
for (int32 i = 0; i < ARRAY_COUNT(GSupportedTextureFormatNames); ++i)
|
|
|
|
|
{
|
|
|
|
|
OutFormats.Add(GSupportedTextureFormatNames[i]);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
virtual bool CompressImage(
|
|
|
|
|
const FImage& InImage,
|
|
|
|
|
const struct FTextureBuildSettings& BuildSettings,
|
|
|
|
|
bool bImageHasAlphaChannel,
|
|
|
|
|
FCompressedImage2D& OutCompressedImage
|
2014-12-04 10:46:04 -05:00
|
|
|
) const override
|
2014-09-18 17:49:40 -04:00
|
|
|
{
|
|
|
|
|
// Get Raw Image Data from passed in FImage
|
|
|
|
|
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, BuildSettings.GetGammaSpace());
|
2014-09-18 17:49:40 -04:00
|
|
|
|
|
|
|
|
// Determine the compressed pixel format and compression parameters
|
|
|
|
|
EPixelFormat CompressedPixelFormat = PF_Unknown;
|
|
|
|
|
FString CompressionParameters = TEXT("");
|
|
|
|
|
|
|
|
|
|
if (BuildSettings.TextureFormatName == GTextureFormatNameASTC_RGB ||
|
|
|
|
|
((BuildSettings.TextureFormatName == GTextureFormatNameASTC_RGBAuto) && !bImageHasAlphaChannel))
|
|
|
|
|
{
|
2014-12-04 10:35:50 -05:00
|
|
|
CompressedPixelFormat = GetQualityFormat();
|
2015-03-11 17:28:22 -04:00
|
|
|
CompressionParameters = FString::Printf(TEXT("%s %s -esw bgra -ch 1 1 1 0"), *GetQualityString(), /*BuildSettings.bSRGB ? TEXT("-srgb") :*/ TEXT("") );
|
2014-09-18 17:49:40 -04:00
|
|
|
}
|
|
|
|
|
else if (BuildSettings.TextureFormatName == GTextureFormatNameASTC_RGBA ||
|
|
|
|
|
((BuildSettings.TextureFormatName == GTextureFormatNameASTC_RGBAuto) && bImageHasAlphaChannel))
|
|
|
|
|
{
|
2014-12-04 10:35:50 -05:00
|
|
|
CompressedPixelFormat = GetQualityFormat();
|
2016-03-16 21:16:51 -04:00
|
|
|
CompressionParameters = FString::Printf(TEXT("%s %s -esw bgra -ch 1 1 1 1"), *GetQualityString(), /*BuildSettings.bSRGB ? TEXT("-srgb") :*/ TEXT("") );
|
2014-09-18 17:49:40 -04:00
|
|
|
}
|
|
|
|
|
else if (BuildSettings.TextureFormatName == GTextureFormatNameASTC_NormalAG)
|
|
|
|
|
{
|
2014-12-04 10:35:50 -05:00
|
|
|
CompressedPixelFormat = GetQualityFormat(FORCED_NORMAL_MAP_COMPRESSION_SIZE_VALUE);
|
|
|
|
|
CompressionParameters = FString::Printf(TEXT("%s -esw 0g0b -ch 0 1 0 1 -oplimit 1000 -mincorrel 0.99 -dblimit 60 -b 2.5 -v 3 1 1 0 50 0 -va 1 1 0 50"), *GetQualityString(FORCED_NORMAL_MAP_COMPRESSION_SIZE_VALUE, -1));
|
2014-09-18 17:49:40 -04:00
|
|
|
}
|
|
|
|
|
else if (BuildSettings.TextureFormatName == GTextureFormatNameASTC_NormalRG)
|
|
|
|
|
{
|
2014-12-04 10:35:50 -05:00
|
|
|
CompressedPixelFormat = GetQualityFormat(FORCED_NORMAL_MAP_COMPRESSION_SIZE_VALUE);
|
|
|
|
|
CompressionParameters = FString::Printf(TEXT("%s -esw bg00 -ch 1 1 0 0 -oplimit 1000 -mincorrel 0.99 -dblimit 60 -b 2.5 -v 3 1 1 0 50 0 -va 1 1 0 50"), *GetQualityString(FORCED_NORMAL_MAP_COMPRESSION_SIZE_VALUE, -1));
|
2014-09-18 17:49:40 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Compress the image, slice by slice
|
|
|
|
|
bool bCompressionSucceeded = true;
|
|
|
|
|
int32 SliceSizeInTexels = Image.SizeX * Image.SizeY;
|
|
|
|
|
for (int32 SliceIndex = 0; SliceIndex < Image.NumSlices && bCompressionSucceeded; ++SliceIndex)
|
|
|
|
|
{
|
|
|
|
|
TArray<uint8> CompressedSliceData;
|
|
|
|
|
bCompressionSucceeded = CompressSliceToASTC(
|
|
|
|
|
Image.AsBGRA8() + (SliceIndex * SliceSizeInTexels),
|
|
|
|
|
Image.SizeX,
|
|
|
|
|
Image.SizeY,
|
|
|
|
|
CompressionParameters,
|
|
|
|
|
CompressedSliceData
|
|
|
|
|
);
|
|
|
|
|
OutCompressedImage.RawData.Append(CompressedSliceData);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (bCompressionSucceeded)
|
|
|
|
|
{
|
|
|
|
|
OutCompressedImage.SizeX = Image.SizeX;
|
|
|
|
|
OutCompressedImage.SizeY = Image.SizeY;
|
|
|
|
|
OutCompressedImage.PixelFormat = CompressedPixelFormat;
|
|
|
|
|
}
|
|
|
|
|
return bCompressionSucceeded;
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Module for ASTC texture compression.
|
|
|
|
|
*/
|
|
|
|
|
static ITextureFormat* Singleton = NULL;
|
|
|
|
|
|
|
|
|
|
class FTextureFormatASTCModule : public ITextureFormatModule
|
|
|
|
|
{
|
|
|
|
|
public:
|
|
|
|
|
virtual ~FTextureFormatASTCModule()
|
|
|
|
|
{
|
|
|
|
|
delete Singleton;
|
|
|
|
|
Singleton = NULL;
|
|
|
|
|
}
|
|
|
|
|
virtual ITextureFormat* GetTextureFormat()
|
|
|
|
|
{
|
|
|
|
|
if (!Singleton)
|
|
|
|
|
{
|
|
|
|
|
Singleton = new FTextureFormatASTC();
|
|
|
|
|
}
|
|
|
|
|
return Singleton;
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
IMPLEMENT_MODULE(FTextureFormatASTCModule, TextureFormatASTC);
|