ATOM-16029 Add direct support for Astc compression (#4157)

* ATOM-16029 Add direct support for Astc compression
Changes include:
- Add ASTCCompressor class
- Enabled 3rdparty::astc-encoder static library.
- Remove ASTC support in PVRTC compressor and ISPC compressor.
- Update unit test to include texts for ASTC compress/decompress.

Signed-off-by: Qing Tao <qingtao@amazon.com>
monroegm-disable-blank-issue-2
Qing Tao 4 years ago committed by GitHub
parent 9184d52a9b
commit fc8697edd5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -62,6 +62,7 @@ ly_add_target(
3rdParty::Qt::Core
3rdParty::Qt::Widgets
3rdParty::Qt::Gui
3rdParty::astc-encoder
3rdParty::etc2comp
3rdParty::PVRTexTool
3rdParty::squish-ccr
@ -77,8 +78,6 @@ ly_add_target(
Gem::Atom_RPI.Public
Gem::Atom_RHI.Reflect
Gem::Atom_Utils.Static
RUNTIME_DEPENDENCIES
3rdParty::ASTCEncoder
)
ly_add_source_properties(

@ -0,0 +1,322 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include <astcenc.h>
#include <AzCore/Jobs/JobCompletion.h>
#include <AzCore/Jobs/JobFunction.h>
#include <AzCore/PlatformIncl.h>
#include <Atom/ImageProcessing/ImageObject.h>
#include <Compressors/ASTCCompressor.h>
#include <Processing/ImageFlags.h>
#include <Processing/ImageToProcess.h>
#include <Processing/PixelFormatInfo.h>
namespace ImageProcessingAtom
{
bool ASTCCompressor::IsCompressedPixelFormatSupported(EPixelFormat fmt)
{
return IsASTCFormat(fmt);
}
bool ASTCCompressor::IsUncompressedPixelFormatSupported(EPixelFormat fmt)
{
// astc encoder requires the compress input image or decompress output image to have four channels
switch (fmt)
{
// uint 8
case ePixelFormat_R8G8B8A8:
case ePixelFormat_R8G8B8X8:
// fp16
case ePixelFormat_R16G16B16A16F:
// fp32
case ePixelFormat_R32G32B32A32F:
return true;
default:
return false;
}
}
EPixelFormat ASTCCompressor::GetSuggestedUncompressedFormat([[maybe_unused]] EPixelFormat compressedfmt, EPixelFormat uncompressedfmt) const
{
if (IsUncompressedPixelFormatSupported(uncompressedfmt))
{
return uncompressedfmt;
}
auto formatInfo = CPixelFormats::GetInstance().GetPixelFormatInfo(uncompressedfmt);
switch (formatInfo->eSampleType)
{
case ESampleType::eSampleType_Half:
return ePixelFormat_R16G16B16A16F;
case ESampleType::eSampleType_Float:
return ePixelFormat_R32G32B32A32F;
}
return ePixelFormat_R8G8B8A8;
}
ColorSpace ASTCCompressor::GetSupportedColorSpace([[maybe_unused]] EPixelFormat compressFormat) const
{
return ColorSpace::autoSelect;
}
const char* ASTCCompressor::GetName() const
{
return "ASTCCompressor";
}
bool ASTCCompressor::DoesSupportDecompress([[maybe_unused]] EPixelFormat fmtDst)
{
return true;
}
astcenc_profile GetAstcProfile(bool isSrgb, EPixelFormat pixelFormat)
{
// select profile depends on LDR or HDR, SRGB or Linear
// ASTCENC_PRF_LDR
// ASTCENC_PRF_LDR_SRGB
// ASTCENC_PRF_HDR_RGB_LDR_A
// ASTCENC_PRF_HDR
auto formatInfo = CPixelFormats::GetInstance().GetPixelFormatInfo(pixelFormat);
bool isHDR = formatInfo->eSampleType == ESampleType::eSampleType_Half || formatInfo->eSampleType == ESampleType::eSampleType_Float;
astcenc_profile profile;
if (isHDR)
{
// HDR is not support in core vulkan 1.1 for android.
// https://arm-software.github.io/vulkan-sdk/_a_s_t_c.html
profile = isSrgb?ASTCENC_PRF_HDR_RGB_LDR_A:ASTCENC_PRF_HDR;
}
else
{
profile = isSrgb?ASTCENC_PRF_LDR_SRGB:ASTCENC_PRF_LDR;
}
return profile;
}
astcenc_type GetAstcDataType(EPixelFormat pixelFormat)
{
auto formatInfo = CPixelFormats::GetInstance().GetPixelFormatInfo(pixelFormat);
astcenc_type dataType = ASTCENC_TYPE_U8;
switch (formatInfo->eSampleType)
{
case ESampleType::eSampleType_Uint8:
dataType = ASTCENC_TYPE_U8;
break;
case ESampleType::eSampleType_Half:
dataType = ASTCENC_TYPE_F16;
break;
case ESampleType::eSampleType_Float:
dataType = ASTCENC_TYPE_F32;
break;
default:
dataType = ASTCENC_TYPE_U8;
AZ_Assert(false, "Unsupport uncompressed format %s", formatInfo->szName);
break;
}
return dataType;
}
float GetAstcCompressQuality(ICompressor::EQuality quality)
{
switch (quality)
{
case ICompressor::EQuality::eQuality_Fast:
return ASTCENC_PRE_FAST;
case ICompressor::EQuality::eQuality_Slow:
return ASTCENC_PRE_THOROUGH;
case ICompressor::EQuality::eQuality_Preview:
case ICompressor::EQuality::eQuality_Normal:
default:
return ASTCENC_PRE_MEDIUM;
}
}
IImageObjectPtr ASTCCompressor::CompressImage(IImageObjectPtr srcImage, EPixelFormat fmtDst, const CompressOption* compressOption) const
{
//validate input
EPixelFormat fmtSrc = srcImage->GetPixelFormat();
//src format need to be uncompressed and dst format need to compressed.
if (!IsUncompressedPixelFormatSupported(fmtSrc) || !IsCompressedPixelFormatSupported(fmtDst))
{
return nullptr;
}
astcenc_swizzle swizzle {ASTCENC_SWZ_R, ASTCENC_SWZ_G, ASTCENC_SWZ_B, compressOption->discardAlpha? ASTCENC_SWZ_1:ASTCENC_SWZ_A};
AZ::u32 flags = 0;
if (srcImage->HasImageFlags(EIF_RenormalizedTexture))
{
ImageToProcess imageToProcess(srcImage);
imageToProcess.ConvertFormatUncompressed(ePixelFormat_R8G8B8X8);
srcImage = imageToProcess.Get();
fmtSrc = srcImage->GetPixelFormat();
flags = ASTCENC_FLG_MAP_NORMAL;
swizzle = astcenc_swizzle{ ASTCENC_SWZ_R, ASTCENC_SWZ_R, ASTCENC_SWZ_R, ASTCENC_SWZ_G };
}
auto dstFormatInfo = CPixelFormats::GetInstance().GetPixelFormatInfo(fmtDst);
const float quality = GetAstcCompressQuality(compressOption->compressQuality);
const astcenc_profile profile = GetAstcProfile(srcImage->HasImageFlags(EIF_SRGBRead), fmtSrc);
astcenc_config config;
astcenc_error status;
status = astcenc_config_init(profile, dstFormatInfo->blockWidth, dstFormatInfo->blockHeight, 1, quality, flags, &config);
//ASTCENC_FLG_MAP_NORMAL
AZ_Assert( status == ASTCENC_SUCCESS, "ERROR: Codec config init failed: %s\n", astcenc_get_error_string(status));
// Create a context based on the configuration
astcenc_context* context;
AZ::u32 blockCount = ((srcImage->GetWidth(0)+ dstFormatInfo->blockWidth-1)/dstFormatInfo->blockWidth) * ((srcImage->GetHeight(0) + dstFormatInfo->blockHeight-1)/dstFormatInfo->blockHeight);
AZ::u32 threadCount = AZStd::min(AZStd::thread::hardware_concurrency(), blockCount);
status = astcenc_context_alloc(&config, threadCount, &context);
AZ_Assert( status == ASTCENC_SUCCESS, "ERROR: Codec context alloc failed: %s\n", astcenc_get_error_string(status));
const astcenc_type dataType =GetAstcDataType(fmtSrc);
// Compress the image for each mips
IImageObjectPtr dstImage(srcImage->AllocateImage(fmtDst));
const AZ::u32 dstMips = dstImage->GetMipCount();
for (AZ::u32 mip = 0; mip < dstMips; ++mip)
{
astcenc_image image;
image.dim_x = srcImage->GetWidth(mip);
image.dim_y = srcImage->GetHeight(mip);
image.dim_z = 1;
image.data_type = dataType;
AZ::u8* srcMem;
AZ::u32 srcPitch;
srcImage->GetImagePointer(mip, srcMem, srcPitch);
image.data = reinterpret_cast<void**>(&srcMem);
AZ::u8* dstMem;
AZ::u32 dstPitch;
dstImage->GetImagePointer(mip, dstMem, dstPitch);
AZ::u32 dataSize = dstImage->GetMipBufSize(mip);
// Create jobs for each compression thread
auto completionJob = aznew AZ::JobCompletion();
for (AZ::u32 threadIdx = 0; threadIdx < threadCount; threadIdx++)
{
const auto jobLambda = [&status, context, &image, &swizzle, dstMem, dataSize, threadIdx]()
{
astcenc_error error = astcenc_compress_image(context, &image, &swizzle, dstMem, dataSize, threadIdx);
if (error != ASTCENC_SUCCESS)
{
status = error;
}
};
AZ::Job* simulationJob = AZ::CreateJobFunction(AZStd::move(jobLambda), true, nullptr); //auto-deletes
simulationJob->SetDependent(completionJob);
simulationJob->Start();
}
if (completionJob)
{
completionJob->StartAndWaitForCompletion();
delete completionJob;
completionJob = nullptr;
}
if (status != ASTCENC_SUCCESS)
{
AZ_Error("Image Processing", false, "ASTCCompressor::CompressImage failed: %s\n", astcenc_get_error_string(status));
astcenc_context_free(context);
return nullptr;
}
// Need to reset to compress next mip
astcenc_compress_reset(context);
}
astcenc_context_free(context);
return dstImage;
}
IImageObjectPtr ASTCCompressor::DecompressImage(IImageObjectPtr srcImage, EPixelFormat fmtDst) const
{
//validate input
EPixelFormat fmtSrc = srcImage->GetPixelFormat(); //compressed
auto srcFormatInfo = CPixelFormats::GetInstance().GetPixelFormatInfo(fmtSrc);
if (!IsCompressedPixelFormatSupported(fmtSrc) || !IsUncompressedPixelFormatSupported(fmtDst))
{
return nullptr;
}
const float quality = ASTCENC_PRE_MEDIUM;
astcenc_swizzle swizzle {ASTCENC_SWZ_R, ASTCENC_SWZ_G, ASTCENC_SWZ_B, ASTCENC_SWZ_A};
if (srcImage->HasImageFlags(EIF_RenormalizedTexture))
{
swizzle = astcenc_swizzle{ASTCENC_SWZ_R, ASTCENC_SWZ_A, ASTCENC_SWZ_Z, ASTCENC_SWZ_1};
}
astcenc_config config;
astcenc_error status;
astcenc_profile profile = GetAstcProfile(srcImage->HasImageFlags(EIF_SRGBRead), fmtDst);
AZ::u32 flags = ASTCENC_FLG_DECOMPRESS_ONLY;
status = astcenc_config_init(profile, srcFormatInfo->blockWidth, srcFormatInfo->blockHeight, 1, quality, flags, &config);
//ASTCENC_FLG_MAP_NORMAL
AZ_Assert( status == ASTCENC_SUCCESS, "astcenc_config_init failed: %s\n", astcenc_get_error_string(status));
// Create a context based on the configuration
const AZ::u32 threadCount = 1; // Decompress function doesn't support multiple threads
astcenc_context* context;
status = astcenc_context_alloc(&config, threadCount, &context);
AZ_Assert( status == ASTCENC_SUCCESS, "astcenc_context_alloc failed: %s\n", astcenc_get_error_string(status));
astcenc_type dataType =GetAstcDataType(fmtDst);
// Decompress the image for each mips
IImageObjectPtr dstImage(srcImage->AllocateImage(fmtDst));
const AZ::u32 dstMips = dstImage->GetMipCount();
for (AZ::u32 mip = 0; mip < dstMips; ++mip)
{
astcenc_image image;
image.dim_x = srcImage->GetWidth(mip);
image.dim_y = srcImage->GetHeight(mip);
image.dim_z = 1;
image.data_type = dataType;
AZ::u8* srcMem;
AZ::u32 srcPitch;
srcImage->GetImagePointer(mip, srcMem, srcPitch);
AZ::u32 srcDataSize = srcImage->GetMipBufSize(mip);
AZ::u8* dstMem;
AZ::u32 dstPitch;
dstImage->GetImagePointer(mip, dstMem, dstPitch);
image.data = reinterpret_cast<void**>(&dstMem);
status = astcenc_decompress_image(context, srcMem, srcDataSize, &image, &swizzle, 0);
if (status != ASTCENC_SUCCESS)
{
AZ_Error("Image Processing", false, "ASTCCompressor::DecompressImage failed: %s\n", astcenc_get_error_string(status));
astcenc_context_free(context);
return nullptr;
}
}
astcenc_context_free(context);
return dstImage;
}
} //namespace ImageProcessingAtom

@ -0,0 +1,30 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#pragma once
#include <Compressors/Compressor.h>
namespace ImageProcessingAtom
{
class ASTCCompressor
: public ICompressor
{
public:
static bool IsCompressedPixelFormatSupported(EPixelFormat fmt);
static bool IsUncompressedPixelFormatSupported(EPixelFormat fmt);
static bool DoesSupportDecompress(EPixelFormat fmtDst);
IImageObjectPtr CompressImage(IImageObjectPtr srcImage, EPixelFormat fmtDst, const CompressOption* compressOption) const override;
IImageObjectPtr DecompressImage(IImageObjectPtr srcImage, EPixelFormat fmtDst) const override;
EPixelFormat GetSuggestedUncompressedFormat(EPixelFormat compressedfmt, EPixelFormat uncompressedfmt) const override;
ColorSpace GetSupportedColorSpace(EPixelFormat compressFormat) const final;
const char* GetName() const final;
};
} // namespace ImageProcessingAtom

@ -8,6 +8,7 @@
#include <AzCore/PlatformIncl.h>
#include <Compressors/ASTCCompressor.h>
#include <Compressors/CTSquisher.h>
#include <Compressors/PVRTC.h>
#include <Compressors/ETC2.h>
@ -15,11 +16,8 @@
namespace ImageProcessingAtom
{
ICompressorPtr ICompressor::FindCompressor(EPixelFormat fmt, [[maybe_unused]] ColorSpace colorSpace, bool isCompressing)
ICompressorPtr ICompressor::FindCompressor(EPixelFormat fmt, ColorSpace colorSpace, bool isCompressing)
{
// The ISPC texture compressor is able to compress BC1, BC3, BC6H and BC7 formats, and all of the ASTC formats.
// Note: The ISPC texture compressor is only able to compress images that are a multiple of the compressed format's blocksize.
// Another limitation is that the compressor requires LDR source images to be in sRGB colorspace.
if (ISPCCompressor::IsCompressedPixelFormatSupported(fmt))
{
if ((isCompressing && ISPCCompressor::IsSourceColorSpaceSupported(colorSpace, fmt)) || (!isCompressing && ISPCCompressor::DoesSupportDecompress(fmt)))
@ -35,6 +33,14 @@ namespace ImageProcessingAtom
return ICompressorPtr(new CTSquisher());
}
}
if (ASTCCompressor::IsCompressedPixelFormatSupported(fmt))
{
if (isCompressing || (!isCompressing && ASTCCompressor::DoesSupportDecompress(fmt)))
{
return ICompressorPtr(new ASTCCompressor());
}
}
// Both ETC2Compressor and PVRTCCompressor can process ETC formats
// According to Mobile team, Etc2Com is faster than PVRTexLib, so we check with ETC2Compressor before PVRTCCompressor

@ -38,8 +38,7 @@ namespace ImageProcessingAtom
EQuality compressQuality = eQuality_Normal;
//required for CTSquisher
AZ::Vector3 rgbWeight = AZ::Vector3(0.3333f, 0.3334f, 0.3333f);
//required for ISPC texture compressor
bool ispcDiscardAlpha = false;
bool discardAlpha = false;
};
public:

@ -57,6 +57,12 @@ namespace ImageProcessingAtom
bool ISPCCompressor::IsCompressedPixelFormatSupported(EPixelFormat fmt)
{
// Even though the ISPC compressor support ASTC formats. But it has restrictions
// 1. Only supports LDR color profile
// 2. Only supports a subset of 2D block sizes
// Also it has overall lower quality compare to astc-encoder
// So we won't add ASTC as part of supported formats here
// Ref: https://solidpixel.github.io/2020/03/02/astc-compared.html
switch (fmt)
{
case ePixelFormat_BC3:
@ -152,7 +158,7 @@ namespace ImageProcessingAtom
if (compressOption)
{
quality = compressOption->compressQuality;
discardAlpha = compressOption->ispcDiscardAlpha;
discardAlpha = compressOption->discardAlpha;
}
// Get the compression profile
@ -230,24 +236,12 @@ namespace ImageProcessingAtom
}
break;
default:
if (IsASTCFormat(destinationFormat))
{
const PixelFormatInfo* info = CPixelFormats::GetInstance().GetPixelFormatInfo(destinationFormat);
astc_enc_settings settings = {};
const auto setProfile = compressionProfile->GetASTC(discardAlpha);
setProfile(&settings, info->blockWidth, info->blockHeight);
// Compress with ASTC
CompressBlocksASTC(&sourceSurface, destinationImageData, &settings);
}
else
{
// No valid pixel format
AZ_Assert(false, "Unhandled pixel format %d", destinationFormat);
return nullptr;
}
break;
{
// No valid pixel format
AZ_Assert(false, "Unhandled pixel format %d", destinationFormat);
return nullptr;
}
break;
}
}

@ -19,7 +19,7 @@
namespace ImageProcessingAtom
{
// Note: PVRTexLib supports ASTC formats, ETC formats, PVRTC formats and BC formats
// Note: PVRTexLib supports ETC formats, PVRTC formats and BC formats
// We haven't tested the performace to compress BC formats compare to CTSquisher
// For PVRTC formats, we only added PVRTC 1 support for now
// The compression for ePVRTPF_EAC_R11 and ePVRTPF_EAC_RG11 are very slow. It takes 7 and 14 minutes for a 2048x2048 texture.
@ -27,34 +27,6 @@ namespace ImageProcessingAtom
{
switch (fmt)
{
case ePixelFormat_ASTC_4x4:
return ePVRTPF_ASTC_4x4;
case ePixelFormat_ASTC_5x4:
return ePVRTPF_ASTC_5x4;
case ePixelFormat_ASTC_5x5:
return ePVRTPF_ASTC_5x5;
case ePixelFormat_ASTC_6x5:
return ePVRTPF_ASTC_6x5;
case ePixelFormat_ASTC_6x6:
return ePVRTPF_ASTC_6x6;
case ePixelFormat_ASTC_8x5:
return ePVRTPF_ASTC_8x5;
case ePixelFormat_ASTC_8x6:
return ePVRTPF_ASTC_8x6;
case ePixelFormat_ASTC_8x8:
return ePVRTPF_ASTC_8x8;
case ePixelFormat_ASTC_10x5:
return ePVRTPF_ASTC_10x5;
case ePixelFormat_ASTC_10x6:
return ePVRTPF_ASTC_10x6;
case ePixelFormat_ASTC_10x8:
return ePVRTPF_ASTC_10x8;
case ePixelFormat_ASTC_10x10:
return ePVRTPF_ASTC_10x10;
case ePixelFormat_ASTC_12x10:
return ePVRTPF_ASTC_12x10;
case ePixelFormat_ASTC_12x12:
return ePVRTPF_ASTC_12x12;
case ePixelFormat_PVRTC2:
return ePVRTPF_PVRTCI_2bpp_RGBA;
case ePixelFormat_PVRTC4:
@ -156,26 +128,7 @@ namespace ImageProcessingAtom
internalQuality = pvrtexture::eETCSlow;
}
}
else if (IsASTCFormat(fmtDst))
{
if (quality == eQuality_Preview)
{
internalQuality = pvrtexture::eASTCVeryFast;
}
else if (quality == eQuality_Fast)
{
internalQuality = pvrtexture::eASTCFast;
}
else if (quality == eQuality_Normal)
{
internalQuality = pvrtexture::eASTCMedium;
}
else
{
internalQuality = pvrtexture::eASTCThorough;
}
}
else
else
{
if (quality == eQuality_Preview)
{
@ -252,7 +205,7 @@ namespace ImageProcessingAtom
if (!isSuccess)
{
AZ_Error("Image Processing", false, "Failed to compress image with PVRTexLib. You may not have astcenc.exe for compressing ASTC formates");
AZ_Error("Image Processing", false, "Failed to compress image with PVRTexLib.");
return nullptr;
}

@ -117,6 +117,11 @@ namespace ImageProcessingAtom
}
}
const ImageConvertProcessDescriptor* ImageConvertProcess::GetInputDesc() const
{
return m_input.get();
}
ImageConvertProcess::ImageConvertProcess(AZStd::unique_ptr<ImageConvertProcessDescriptor>&& descriptor)
: m_image(nullptr)
, m_progressStep(0)
@ -554,12 +559,6 @@ namespace ImageProcessingAtom
// pixel format conversion
bool ImageConvertProcess::ConvertPixelformat()
{
//For ASTC compression we need to clear out the alpha to get accurate rgb compression.
if(m_alphaImage && IsASTCFormat(m_input->m_presetSetting.m_pixelFormat))
{
m_image->Get()->Swizzle("rgb1");
}
//set up compress option
ICompressor::EQuality quality;
if (m_input->m_isPreview)
@ -574,7 +573,14 @@ namespace ImageProcessingAtom
// set the compression options
m_image->GetCompressOption().compressQuality = quality;
m_image->GetCompressOption().rgbWeight = m_input->m_presetSetting.GetColorWeight();
m_image->GetCompressOption().ispcDiscardAlpha = m_input->m_presetSetting.m_discardAlpha;
m_image->GetCompressOption().discardAlpha = m_input->m_presetSetting.m_discardAlpha;
//For ASTC compression we need to clear out the alpha to get accurate rgb compression.
if(m_alphaImage && IsASTCFormat(m_input->m_presetSetting.m_pixelFormat))
{
m_image->GetCompressOption().discardAlpha = true;
}
m_image->ConvertFormat(m_input->m_presetSetting.m_pixelFormat);
return true;

@ -122,6 +122,8 @@ namespace ImageProcessingAtom
// Get output JobProducts and append them to the outProducts vector.
void GetAppendOutputProducts(AZStd::vector<AssetBuilderSDK::JobProduct>& outProducts);
const ImageConvertProcessDescriptor* GetInputDesc() const;
private:
//input image and settings
AZStd::shared_ptr<ImageConvertProcessDescriptor> m_input;

@ -12,8 +12,12 @@
#include <AzQtComponents/Utilities/QtPluginPaths.h>
#include <AzCore/AzCore_Traits_Platform.h>
#include <AzCore/Asset/AssetManager.h>
#include <AzCore/Asset/AssetManagerComponent.h>
#include <AzCore/Jobs/JobContext.h>
#include <AzCore/Jobs/JobManager.h>
#include <AzCore/Memory/Memory.h>
#include <AzCore/Memory/PoolAllocator.h>
#include <AzCore/RTTI/ReflectionManager.h>
@ -123,6 +127,9 @@ namespace UnitTest
AZStd::string m_outputRootFolder;
AZStd::string m_outputFolder;
AZStd::unique_ptr<AZ::JobManager> m_jobManager;
AZStd::unique_ptr<AZ::JobContext> m_jobContext;
void SetUp() override
{
AllocatorsBase::SetupAllocator();
@ -159,6 +166,27 @@ namespace UnitTest
m_jsonSystemComponent->Reflect(m_jsonRegistrationContext.get());
BuilderPluginComponent::Reflect(m_jsonRegistrationContext.get());
// Setup job context for job system
JobManagerDesc jobManagerDesc;
JobManagerThreadDesc threadDesc;
#if AZ_TRAIT_SET_JOB_PROCESSOR_ID
threadDesc.m_cpuId = 0; // Don't set processors IDs on windows
#endif
uint32_t numWorkerThreads = AZStd::thread::hardware_concurrency();
for (unsigned int i = 0; i < numWorkerThreads; ++i)
{
jobManagerDesc.m_workerThreads.push_back(threadDesc);
#if AZ_TRAIT_SET_JOB_PROCESSOR_ID
threadDesc.m_cpuId++;
#endif
}
m_jobManager = AZStd::make_unique<JobManager>(jobManagerDesc);
m_jobContext = AZStd::make_unique<JobContext>(*m_jobManager);
JobContext::SetGlobalContext(m_jobContext.get());
// Startup default local FileIO (hits OSAllocator) if not already setup.
if (AZ::IO::FileIOBase::GetInstance() == nullptr)
{
@ -192,6 +220,10 @@ namespace UnitTest
delete AZ::IO::FileIOBase::GetInstance();
AZ::IO::FileIOBase::SetInstance(nullptr);
JobContext::SetGlobalContext(nullptr);
m_jobContext = nullptr;
m_jobManager = nullptr;
m_jsonRegistrationContext->EnableRemoveReflection();
m_jsonSystemComponent->Reflect(m_jsonRegistrationContext.get());
BuilderPluginComponent::Reflect(m_jsonRegistrationContext.get());
@ -223,7 +255,7 @@ namespace UnitTest
Image_512X288_RGB8_Tga,
Image_1024X1024_RGB8_Tif,
Image_UpperCase_Tga,
Image_512x512_Normal_Tga, // QImage doesn't support loading this file.
Image_1024x1024_normal_tiff,
Image_128x128_Transparent_Tga,
Image_237x177_RGB_Jpg,
Image_GreyScale_Png,
@ -251,7 +283,7 @@ namespace UnitTest
m_imagFileNameMap[Image_512X288_RGB8_Tga] = m_testFileFolder + "512x288_24bit.tga";
m_imagFileNameMap[Image_1024X1024_RGB8_Tif] = m_testFileFolder + "1024x1024_24bit.tif";
m_imagFileNameMap[Image_UpperCase_Tga] = m_testFileFolder + "uppercase.TGA";
m_imagFileNameMap[Image_512x512_Normal_Tga] = m_testFileFolder + "512x512_RGB_N.tga";
m_imagFileNameMap[Image_1024x1024_normal_tiff] = m_testFileFolder + "1024x1024_normal.tiff";
m_imagFileNameMap[Image_128x128_Transparent_Tga] = m_testFileFolder + "128x128_RGBA8.tga";
m_imagFileNameMap[Image_237x177_RGB_Jpg] = m_testFileFolder + "237x177_RGB.jpg";
m_imagFileNameMap[Image_GreyScale_Png] = m_testFileFolder + "greyscale.png";
@ -801,8 +833,7 @@ namespace UnitTest
auto formatInfo = CPixelFormats::GetInstance().GetPixelFormatInfo(pixelFormat);
if (formatInfo->bCompressed)
{
// exclude astc formats until we add astc compressor to all platforms
// exclude pvrtc formats (deprecating)
// skip ASTC formats which are tested in TestConvertASTCCompressor
if (!IsASTCFormat(pixelFormat)
&& pixelFormat != ePixelFormat_PVRTC2 && pixelFormat != ePixelFormat_PVRTC4
&& !IsETCFormat(pixelFormat)) // skip ETC since it's very slow
@ -830,32 +861,121 @@ namespace UnitTest
continue;
}
[[maybe_unused]] auto formatInfo = CPixelFormats::GetInstance().GetPixelFormatInfo(pixelFormat);
imageToProcess.Set(srcImage);
imageToProcess.ConvertFormat(pixelFormat);
auto formatInfo = CPixelFormats::GetInstance().GetPixelFormatInfo(pixelFormat);
ColorSpace sourceColorSpace = srcImage->HasImageFlags(EIF_SRGBRead) ? ColorSpace::sRGB : ColorSpace::linear;
ICompressorPtr compressor = ICompressor::FindCompressor(pixelFormat, sourceColorSpace, true);
if (!imageToProcess.Get())
if (!compressor)
{
AZ_Warning("test", false, "unsupported format: %s", formatInfo->szName);
continue;
}
imageToProcess.Set(srcImage);
imageToProcess.ConvertFormat(pixelFormat);
ASSERT_TRUE(imageToProcess.Get());
ASSERT_TRUE(imageToProcess.Get()->GetPixelFormat() == pixelFormat);
// Get compressor name
ColorSpace sourceColorSpace = srcImage->HasImageFlags(EIF_SRGBRead) ? ColorSpace::sRGB : ColorSpace::linear;
ICompressorPtr compressor = ICompressor::FindCompressor(pixelFormat, sourceColorSpace, true);
//convert back to an uncompressed format and expect it will be successful
imageToProcess.ConvertFormat(srcImage->GetPixelFormat());
ASSERT_TRUE(imageToProcess.Get()->GetPixelFormat() == srcImage->GetPixelFormat());
//save the image to a file so we can check the visual result
// Save the image to a file so we can check the visual result
AZStd::string outputName = AZStd::string::format("%s_%s", imageName.c_str(), compressor->GetName());
SaveImageToFile(imageToProcess.Get(), outputName, 1);
}
}
}
TEST_F(ImageProcessingTest, Test_ConvertAllAstc_Success)
{
// Compress/Decompress to all astc formats (LDR)
auto imageIdx = Image_237x177_RGB_Jpg;
IImageObjectPtr srcImage = IImageObjectPtr(LoadImageFromFile(m_imagFileNameMap[imageIdx]));
QFileInfo fi(m_imagFileNameMap[imageIdx].c_str());
AZStd::string imageName = fi.baseName().toUtf8().constData();
for (uint32 i = 0; i < ePixelFormat_Count; i++)
{
EPixelFormat pixelFormat = (EPixelFormat)i;
if (IsASTCFormat(pixelFormat))
{
ImageToProcess imageToProcess(srcImage);
imageToProcess.ConvertFormat(pixelFormat);
//convert back to an uncompressed format and expect it will be successful
imageToProcess.ConvertFormat(ePixelFormat_R8G8B8A8);
ASSERT_TRUE(imageToProcess.Get()->GetPixelFormat() == ePixelFormat_R8G8B8A8);
ASSERT_TRUE(imageToProcess.Get());
ASSERT_TRUE(imageToProcess.Get()->GetPixelFormat() == pixelFormat);
ASSERT_TRUE(imageToProcess.Get()->GetWidth(0) == srcImage->GetWidth(0));
ASSERT_TRUE(imageToProcess.Get()->GetHeight(0) == srcImage->GetHeight(0));
// convert back to an uncompressed format and expect it will be successful
imageToProcess.ConvertFormat(srcImage->GetPixelFormat());
ASSERT_TRUE(imageToProcess.Get()->GetPixelFormat() == srcImage->GetPixelFormat());
// save the image to a file so we can check the visual result
AZStd::string outputName = AZStd::string::format("ASTC_%s", imageName.c_str());
SaveImageToFile(imageToProcess.Get(), outputName, 1);
}
}
}
TEST_F(ImageProcessingTest, Test_ConvertHdrToAstc_Success)
{
// Compress/Decompress HDR
auto imageIdx = Image_defaultprobe_cm_1536x256_64bits_tif;
IImageObjectPtr srcImage = IImageObjectPtr(LoadImageFromFile(m_imagFileNameMap[imageIdx]));
EPixelFormat dstFormat = ePixelFormat_ASTC_4x4;
ImageToProcess imageToProcess(srcImage);
imageToProcess.ConvertFormat(ePixelFormat_ASTC_4x4);
ASSERT_TRUE(imageToProcess.Get());
ASSERT_TRUE(imageToProcess.Get()->GetPixelFormat() == dstFormat);
ASSERT_TRUE(imageToProcess.Get()->GetWidth(0) == srcImage->GetWidth(0));
ASSERT_TRUE(imageToProcess.Get()->GetHeight(0) == srcImage->GetHeight(0));
//convert back to an uncompressed format and expect it will be successful
imageToProcess.ConvertFormat(srcImage->GetPixelFormat());
ASSERT_TRUE(imageToProcess.Get()->GetPixelFormat() == srcImage->GetPixelFormat());
//save the image to a file so we can check the visual result
SaveImageToFile(imageToProcess.Get(), "ASTC_HDR", 1);
}
TEST_F(ImageProcessingTest, Test_AstcNormalPreset_Success)
{
// Normal.preset which uses ASTC as output format
// This test compress a normal texture and its mipmaps
auto outcome = BuilderSettingManager::Instance()->LoadConfigFromFolder(m_defaultSettingFolder);
ASSERT_TRUE(outcome.IsSuccess());
AZStd::string inputFile;
AZStd::vector<AssetBuilderSDK::JobProduct> outProducts;
inputFile = m_imagFileNameMap[Image_1024x1024_normal_tiff];
IImageObjectPtr srcImage = IImageObjectPtr(LoadImageFromFile(inputFile));
ImageConvertProcess* process = CreateImageConvertProcess(inputFile, m_outputFolder, "ios", outProducts, m_context.get());
const PresetSettings* preset = &process->GetInputDesc()->m_presetSetting;
if (process != nullptr)
{
process->ProcessAll();
//get process result
ASSERT_TRUE(process->IsSucceed());
auto outputImage = process->GetOutputImage();
ASSERT_TRUE(outputImage->GetPixelFormat() == preset->m_pixelFormat);
ASSERT_TRUE(outputImage->GetWidth(0) == srcImage->GetWidth(0));
ASSERT_TRUE(outputImage->GetHeight(0) == srcImage->GetHeight(0));
SaveImageToFile(outputImage, "ASTC_Normal", 10);
delete process;
}
}
TEST_F(ImageProcessingTest, DISABLED_TestImageFilter)
{

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:120aaf43057b07fb3c784264eb48b899cc612f30917d316fe843bb839220dc22
size 203062

@ -1,3 +0,0 @@
version https://git-lfs.github.com/spec/v1
oid sha256:646a9a9035cc3f4dfd57babc0055710d2f5bb8aee0a792f2b65d69b4fd6a94b3
size 786450

@ -114,6 +114,8 @@ set(FILES
../External/CubeMapGen/CImageSurface.cpp
../External/CubeMapGen/CImageSurface.h
../External/CubeMapGen/VectorMacros.h
Source/Compressors/ASTCCompressor.cpp
Source/Compressors/ASTCCompressor.h
Source/Compressors/Compressor.h
Source/Compressors/Compressor.cpp
Source/Compressors/CTSquisher.h

@ -9,7 +9,6 @@
# shared by other platforms:
ly_associate_package(PACKAGE_NAME ilmbase-2.3.0-rev4-multiplatform TARGETS ilmbase PACKAGE_HASH 97547fdf1fbc4d81b8ccf382261f8c25514ed3b3c4f8fd493f0a4fa873bba348)
ly_associate_package(PACKAGE_NAME assimp-5.0.1-rev11-multiplatform TARGETS assimplib PACKAGE_HASH 1a9113788b893ef4a2ee63ac01eb71b981a92894a5a51175703fa225f5804dec)
ly_associate_package(PACKAGE_NAME ASTCEncoder-2017_11_14-rev2-multiplatform TARGETS ASTCEncoder PACKAGE_HASH c240ffc12083ee39a5ce9dc241de44d116e513e1e3e4cc1d05305e7aa3bdc326)
ly_associate_package(PACKAGE_NAME md5-2.0-multiplatform TARGETS md5 PACKAGE_HASH 29e52ad22c78051551f78a40c2709594f0378762ae03b417adca3f4b700affdf)
ly_associate_package(PACKAGE_NAME RapidJSON-1.1.0-rev1-multiplatform TARGETS RapidJSON PACKAGE_HASH 2f5e26ecf86c3b7a262753e7da69ac59928e78e9534361f3d00c1ad5879e4023)
ly_associate_package(PACKAGE_NAME RapidXML-1.13-rev1-multiplatform TARGETS RapidXML PACKAGE_HASH 4b7b5651e47cfd019b6b295cc17bb147b65e53073eaab4a0c0d20a37ab74a246)
@ -44,5 +43,6 @@ ly_associate_package(PACKAGE_NAME SPIRVCross-2021.04.29-rev1-linux
ly_associate_package(PACKAGE_NAME azslc-1.7.23-rev2-linux TARGETS azslc PACKAGE_HASH 1ba84d8321a566d35a1e9aa7400211ba8e6d1c11c08e4be3c93e6e74b8f7aef1)
ly_associate_package(PACKAGE_NAME zlib-1.2.11-rev2-linux TARGETS zlib PACKAGE_HASH 16f3b9e11cda525efb62144f354c1cfc30a5def9eff020dbe49cb00ee7d8234f)
ly_associate_package(PACKAGE_NAME squish-ccr-deb557d-rev1-linux TARGETS squish-ccr PACKAGE_HASH 85fecafbddc6a41a27c5f59ed4a5dfb123a94cb4666782cf26e63c0a4724c530)
ly_associate_package(PACKAGE_NAME astc-encoder-3.2-rev1-linux TARGETS astc-encoder PACKAGE_HASH 2ba97a06474d609945f0ab4419af1f6bbffdd294ca6b869f5fcebec75c573c0f)
ly_associate_package(PACKAGE_NAME ISPCTexComp-36b80aa-rev1-linux TARGETS ISPCTexComp PACKAGE_HASH 065fd12abe4247dde247330313763cf816c3375c221da030bdec35024947f259)
ly_associate_package(PACKAGE_NAME lz4-1.9.3-vcpkg-rev4-linux TARGETS lz4 PACKAGE_HASH 5de3dbd3e2a3537c6555d759b3c5bb98e5456cf85c74ff6d046f809b7087290d)

@ -9,7 +9,6 @@
# shared by other platforms:
ly_associate_package(PACKAGE_NAME ilmbase-2.3.0-rev4-multiplatform TARGETS ilmbase PACKAGE_HASH 97547fdf1fbc4d81b8ccf382261f8c25514ed3b3c4f8fd493f0a4fa873bba348)
ly_associate_package(PACKAGE_NAME assimp-5.0.1-rev11-multiplatform TARGETS assimplib PACKAGE_HASH 1a9113788b893ef4a2ee63ac01eb71b981a92894a5a51175703fa225f5804dec)
ly_associate_package(PACKAGE_NAME ASTCEncoder-2017_11_14-rev2-multiplatform TARGETS ASTCEncoder PACKAGE_HASH c240ffc12083ee39a5ce9dc241de44d116e513e1e3e4cc1d05305e7aa3bdc326)
ly_associate_package(PACKAGE_NAME md5-2.0-multiplatform TARGETS md5 PACKAGE_HASH 29e52ad22c78051551f78a40c2709594f0378762ae03b417adca3f4b700affdf)
ly_associate_package(PACKAGE_NAME RapidJSON-1.1.0-rev1-multiplatform TARGETS RapidJSON PACKAGE_HASH 2f5e26ecf86c3b7a262753e7da69ac59928e78e9534361f3d00c1ad5879e4023)
ly_associate_package(PACKAGE_NAME RapidXML-1.13-rev1-multiplatform TARGETS RapidXML PACKAGE_HASH 4b7b5651e47cfd019b6b295cc17bb147b65e53073eaab4a0c0d20a37ab74a246)
@ -42,6 +41,7 @@ ly_associate_package(PACKAGE_NAME qt-5.15.2-rev5-mac
ly_associate_package(PACKAGE_NAME libsamplerate-0.2.1-rev2-mac TARGETS libsamplerate PACKAGE_HASH b912af40c0ac197af9c43d85004395ba92a6a859a24b7eacd920fed5854a97fe)
ly_associate_package(PACKAGE_NAME zlib-1.2.11-rev2-mac TARGETS zlib PACKAGE_HASH 21714e8a6de4f2523ee92a7f52d51fbee29c5f37ced334e00dc3c029115b472e)
ly_associate_package(PACKAGE_NAME squish-ccr-deb557d-rev1-mac TARGETS squish-ccr PACKAGE_HASH 155bfbfa17c19a9cd2ef025de14c5db598f4290045d5b0d83ab58cb345089a77)
ly_associate_package(PACKAGE_NAME astc-encoder-3.2-rev1-mac TARGETS astc-encoder PACKAGE_HASH 96f6ea8c3e45ec7fe525230c7c53ca665c8300d8e28456cc19bb3159ce6f8dcc)
ly_associate_package(PACKAGE_NAME ISPCTexComp-36b80aa-rev1-mac TARGETS ISPCTexComp PACKAGE_HASH 8a4e93277b8face6ea2fd57c6d017bdb55643ed3d6387110bc5f6b3b884dd169)
ly_associate_package(PACKAGE_NAME lz4-1.9.3-vcpkg-rev4-mac TARGETS lz4 PACKAGE_HASH 891ff630bf34f7ab1d8eaee2ea0a8f1fca89dbdc63fca41ee592703dd488a73b)

@ -9,7 +9,6 @@
# shared by other platforms:
ly_associate_package(PACKAGE_NAME ilmbase-2.3.0-rev4-multiplatform TARGETS ilmbase PACKAGE_HASH 97547fdf1fbc4d81b8ccf382261f8c25514ed3b3c4f8fd493f0a4fa873bba348)
ly_associate_package(PACKAGE_NAME assimp-5.0.1-rev11-multiplatform TARGETS assimplib PACKAGE_HASH 1a9113788b893ef4a2ee63ac01eb71b981a92894a5a51175703fa225f5804dec)
ly_associate_package(PACKAGE_NAME ASTCEncoder-2017_11_14-rev2-multiplatform TARGETS ASTCEncoder PACKAGE_HASH c240ffc12083ee39a5ce9dc241de44d116e513e1e3e4cc1d05305e7aa3bdc326)
ly_associate_package(PACKAGE_NAME md5-2.0-multiplatform TARGETS md5 PACKAGE_HASH 29e52ad22c78051551f78a40c2709594f0378762ae03b417adca3f4b700affdf)
ly_associate_package(PACKAGE_NAME RapidJSON-1.1.0-rev1-multiplatform TARGETS RapidJSON PACKAGE_HASH 2f5e26ecf86c3b7a262753e7da69ac59928e78e9534361f3d00c1ad5879e4023)
ly_associate_package(PACKAGE_NAME RapidXML-1.13-rev1-multiplatform TARGETS RapidXML PACKAGE_HASH 4b7b5651e47cfd019b6b295cc17bb147b65e53073eaab4a0c0d20a37ab74a246)
@ -49,5 +48,6 @@ ly_associate_package(PACKAGE_NAME OpenSSL-1.1.1b-rev2-windows
ly_associate_package(PACKAGE_NAME Crashpad-0.8.0-rev1-windows TARGETS Crashpad PACKAGE_HASH d162aa3070147bc0130a44caab02c5fe58606910252caf7f90472bd48d4e31e2)
ly_associate_package(PACKAGE_NAME zlib-1.2.11-rev2-windows TARGETS zlib PACKAGE_HASH 9afab1d67641ed8bef2fb38fc53942da47f2ab339d9e77d3d20704a48af2da0b)
ly_associate_package(PACKAGE_NAME squish-ccr-deb557d-rev1-windows TARGETS squish-ccr PACKAGE_HASH 5c3d9fa491e488ccaf802304ad23b932268a2b2846e383f088779962af2bfa84)
ly_associate_package(PACKAGE_NAME astc-encoder-3.2-rev1-windows TARGETS astc-encoder PACKAGE_HASH 3addc6fc1a7eb0d6b7f3d530e962af967e6d92b3825ef485da243346357cf78e)
ly_associate_package(PACKAGE_NAME ISPCTexComp-36b80aa-rev1-windows TARGETS ISPCTexComp PACKAGE_HASH b6fa6ea28a2808a9a5524c72c37789c525925e435770f2d94eb2d387360fa2d0)
ly_associate_package(PACKAGE_NAME lz4-1.9.3-vcpkg-rev4-windows TARGETS lz4 PACKAGE_HASH 4ea457b833cd8cfaf8e8e06ed6df601d3e6783b606bdbc44a677f77e19e0db16)

Loading…
Cancel
Save