LYN-8837 o3de Material Editor - Texture Settings Editor - Hangs when converting texture (#6359)

Fixed a editor hanging issue when preview texture with astc format (starts multiple job threads inside a job thread)
Fixed an issue of changing texture setting didn't trigger image re-process.
Fixed an issue with image asset which has texture setting may have dependency with wrong preset
Added a EIF_HDR for source image in hdr format.
Fixed astc compression issue which may wrongly compress image to HDR astc format

Signed-off-by: Qing Tao <55564570+VickyAtAZ@users.noreply.github.com>
monroegm-disable-blank-issue-2
Qing Tao 4 years ago committed by GitHub
parent 7188529652
commit d9f0a3012d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -7,7 +7,7 @@
"UUID": "{5D9ECB52-4CD9-4CB8-80E3-10CAE5EFB8A2}", "UUID": "{5D9ECB52-4CD9-4CB8-80E3-10CAE5EFB8A2}",
"Name": "AlbedoWithGenericAlpha", "Name": "AlbedoWithGenericAlpha",
"RGB_Weight": "CIEXYZ", "RGB_Weight": "CIEXYZ",
"PixelFormat": "ASTC_4x4", "PixelFormat": "BC3",
"MipMapSetting": { "MipMapSetting": {
"MipGenType": "Box" "MipGenType": "Box"
} }

@ -79,6 +79,7 @@ namespace ImageProcessingAtom
}; };
bool IsASTCFormat(EPixelFormat fmt); bool IsASTCFormat(EPixelFormat fmt);
bool IsHDRFormat(EPixelFormat fmt);
} // namespace ImageProcessingAtom } // namespace ImageProcessingAtom
namespace AZ namespace AZ

@ -78,7 +78,7 @@ namespace ImageProcessingAtom
return true; return true;
} }
astcenc_profile GetAstcProfile(bool isSrgb, EPixelFormat pixelFormat) astcenc_profile GetAstcProfile(bool isSrgb, bool isHDR)
{ {
// select profile depends on LDR or HDR, SRGB or Linear // select profile depends on LDR or HDR, SRGB or Linear
// ASTCENC_PRF_LDR // ASTCENC_PRF_LDR
@ -86,8 +86,6 @@ namespace ImageProcessingAtom
// ASTCENC_PRF_HDR_RGB_LDR_A // ASTCENC_PRF_HDR_RGB_LDR_A
// ASTCENC_PRF_HDR // ASTCENC_PRF_HDR
auto formatInfo = CPixelFormats::GetInstance().GetPixelFormatInfo(pixelFormat);
bool isHDR = formatInfo->eSampleType == ESampleType::eSampleType_Half || formatInfo->eSampleType == ESampleType::eSampleType_Float;
astcenc_profile profile; astcenc_profile profile;
if (isHDR) if (isHDR)
{ {
@ -170,7 +168,7 @@ namespace ImageProcessingAtom
auto dstFormatInfo = CPixelFormats::GetInstance().GetPixelFormatInfo(fmtDst); auto dstFormatInfo = CPixelFormats::GetInstance().GetPixelFormatInfo(fmtDst);
const float quality = GetAstcCompressQuality(compressOption->compressQuality); const float quality = GetAstcCompressQuality(compressOption->compressQuality);
const astcenc_profile profile = GetAstcProfile(srcImage->HasImageFlags(EIF_SRGBRead), fmtSrc); const astcenc_profile profile = GetAstcProfile(srcImage->HasImageFlags(EIF_SRGBRead), srcImage->HasImageFlags(EIF_HDR));
astcenc_config config; astcenc_config config;
astcenc_error status; astcenc_error status;
@ -182,10 +180,12 @@ namespace ImageProcessingAtom
// Create a context based on the configuration // Create a context based on the configuration
astcenc_context* context; astcenc_context* context;
AZ::u32 blockCount = ((srcImage->GetWidth(0)+ dstFormatInfo->blockWidth-1)/dstFormatInfo->blockWidth) * ((srcImage->GetHeight(0) + dstFormatInfo->blockHeight-1)/dstFormatInfo->blockHeight); 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); AZ::u32 threadCount = AZStd::min(AZStd::thread::hardware_concurrency()/2, blockCount);
status = astcenc_context_alloc(&config, threadCount, &context); status = astcenc_context_alloc(&config, threadCount, &context);
AZ_Assert( status == ASTCENC_SUCCESS, "ERROR: Codec context alloc failed: %s\n", astcenc_get_error_string(status)); AZ_Assert( status == ASTCENC_SUCCESS, "ERROR: Codec context alloc failed: %s\n", astcenc_get_error_string(status));
AZ::Job* currentJob = AZ::JobContext::GetGlobalContext()->GetJobManager().GetCurrentJob();
const astcenc_type dataType =GetAstcDataType(fmtSrc); const astcenc_type dataType =GetAstcDataType(fmtSrc);
// Compress the image for each mips // Compress the image for each mips
@ -209,8 +209,22 @@ namespace ImageProcessingAtom
dstImage->GetImagePointer(mip, dstMem, dstPitch); dstImage->GetImagePointer(mip, dstMem, dstPitch);
AZ::u32 dataSize = dstImage->GetMipBufSize(mip); AZ::u32 dataSize = dstImage->GetMipBufSize(mip);
if (threadCount == 1)
{
astcenc_error error = astcenc_compress_image(context, &image, &swizzle, dstMem, dataSize, 0);
if (error != ASTCENC_SUCCESS)
{
status = error;
}
}
else
{
AZ::JobCompletion* completionJob = nullptr;
if (!currentJob)
{
completionJob = aznew AZ::JobCompletion();
}
// Create jobs for each compression thread // Create jobs for each compression thread
auto completionJob = aznew AZ::JobCompletion();
for (AZ::u32 threadIdx = 0; threadIdx < threadCount; threadIdx++) for (AZ::u32 threadIdx = 0; threadIdx < threadCount; threadIdx++)
{ {
const auto jobLambda = [&status, context, &image, &swizzle, dstMem, dataSize, threadIdx]() const auto jobLambda = [&status, context, &image, &swizzle, dstMem, dataSize, threadIdx]()
@ -223,16 +237,38 @@ namespace ImageProcessingAtom
}; };
AZ::Job* simulationJob = AZ::CreateJobFunction(AZStd::move(jobLambda), true, nullptr); //auto-deletes AZ::Job* simulationJob = AZ::CreateJobFunction(AZStd::move(jobLambda), true, nullptr); //auto-deletes
// adds this job as child to current job if there is a current job
// otherwise adds it as a dependent for the complete job
if (currentJob)
{
currentJob->StartAsChild(simulationJob);
}
else
{
simulationJob->SetDependent(completionJob); simulationJob->SetDependent(completionJob);
simulationJob->Start(); simulationJob->Start();
} }
astcenc_error error = astcenc_compress_image(context, &image, &swizzle, dstMem, dataSize, threadIdx);
if (error != ASTCENC_SUCCESS)
{
status = error;
}
}
if (currentJob)
{
currentJob->WaitForChildren();
}
if (completionJob) if (completionJob)
{ {
completionJob->StartAndWaitForCompletion(); completionJob->StartAndWaitForCompletion();
delete completionJob; delete completionJob;
completionJob = nullptr; completionJob = nullptr;
} }
}
if (status != ASTCENC_SUCCESS) if (status != ASTCENC_SUCCESS)
{ {

@ -221,18 +221,19 @@ namespace ImageProcessingAtom
m_isShuttingDown = true; m_isShuttingDown = true;
} }
PresetName GetImagePreset(const AZStd::string& filepath) PresetName GetImagePreset(const AZStd::string& imageFileFullPath)
{ {
// first let preset from asset info // first let preset from asset info
TextureSettings textureSettings; TextureSettings textureSettings;
StringOutcome output = TextureSettings::LoadTextureSetting(filepath, textureSettings); AZStd::string settingFilePath = imageFileFullPath + TextureSettings::ExtensionName;
TextureSettings::LoadTextureSetting(settingFilePath, textureSettings);
if (!textureSettings.m_preset.IsEmpty()) if (!textureSettings.m_preset.IsEmpty())
{ {
return textureSettings.m_preset; return textureSettings.m_preset;
} }
return BuilderSettingManager::Instance()->GetSuggestedPreset(filepath); return BuilderSettingManager::Instance()->GetSuggestedPreset(imageFileFullPath);
} }
void HandlePresetDependency(PresetName presetName, AZStd::vector<AssetBuilderSDK::SourceFileDependency>& sourceDependencyList) void HandlePresetDependency(PresetName presetName, AZStd::vector<AssetBuilderSDK::SourceFileDependency>& sourceDependencyList)
@ -305,13 +306,14 @@ namespace ImageProcessingAtom
// add source dependency for .assetinfo file // add source dependency for .assetinfo file
AssetBuilderSDK::SourceFileDependency sourceFileDependency; AssetBuilderSDK::SourceFileDependency sourceFileDependency;
sourceFileDependency.m_sourceDependencyType = AssetBuilderSDK::SourceFileDependency::SourceFileDependencyType::Absolute; sourceFileDependency.m_sourceDependencyType = AssetBuilderSDK::SourceFileDependency::SourceFileDependencyType::Absolute;
sourceFileDependency.m_sourceFileDependencyPath = request.m_sourceFile; sourceFileDependency.m_sourceFileDependencyPath = request.m_sourceFile + TextureSettings::ExtensionName;
AZ::StringFunc::Path::ReplaceExtension(sourceFileDependency.m_sourceFileDependencyPath, TextureSettings::ExtensionName);
response.m_sourceFileDependencyList.push_back(sourceFileDependency); response.m_sourceFileDependencyList.push_back(sourceFileDependency);
// add source dependencies for .preset files // add source dependencies for .preset files
// Get the preset for this file // Get the preset for this file
auto presetName = GetImagePreset(request.m_sourceFile); AZ::IO::FixedMaxPath fullPath(request.m_watchFolder);
fullPath /= request.m_sourceFile;
auto presetName = GetImagePreset(fullPath.c_str());
HandlePresetDependency(presetName, response.m_sourceFileDependencyList); HandlePresetDependency(presetName, response.m_sourceFileDependencyList);
response.m_result = AssetBuilderSDK::CreateJobsResultCode::Success; response.m_result = AssetBuilderSDK::CreateJobsResultCode::Success;

@ -8,6 +8,7 @@
#include <ImageLoader/ImageLoaders.h> #include <ImageLoader/ImageLoaders.h>
#include <Processing/ImageFlags.h>
#include <Atom/ImageProcessing/ImageObject.h> #include <Atom/ImageProcessing/ImageObject.h>
// warning C4251: class QT_Type needs to have dll-interface to be used by clients of class 'QT_Type' // warning C4251: class QT_Type needs to have dll-interface to be used by clients of class 'QT_Type'
AZ_PUSH_DISABLE_WARNING(4251, "-Wunknown-warning-option") AZ_PUSH_DISABLE_WARNING(4251, "-Wunknown-warning-option")
@ -26,21 +27,31 @@ namespace ImageProcessingAtom
return nullptr; return nullptr;
} }
IImageObject* loadedImage = nullptr;
if (TIFFLoader::IsExtensionSupported(ext.toUtf8())) if (TIFFLoader::IsExtensionSupported(ext.toUtf8()))
{ {
return TIFFLoader::LoadImageFromTIFF(filename); loadedImage = TIFFLoader::LoadImageFromTIFF(filename);
} }
else if (DdsLoader::IsExtensionSupported(ext.toUtf8())) else if (DdsLoader::IsExtensionSupported(ext.toUtf8()))
{ {
return DdsLoader::LoadImageFromFile(filename); loadedImage = DdsLoader::LoadImageFromFile(filename);
} }
else if (QtImageLoader::IsExtensionSupported(ext.toUtf8())) else if (QtImageLoader::IsExtensionSupported(ext.toUtf8()))
{ {
return QtImageLoader::LoadImageFromFile(filename); loadedImage = QtImageLoader::LoadImageFromFile(filename);
} }
else if (ExrLoader::IsExtensionSupported(ext.toUtf8())) else if (ExrLoader::IsExtensionSupported(ext.toUtf8()))
{ {
return ExrLoader::LoadImageFromFile(filename); loadedImage = ExrLoader::LoadImageFromFile(filename);
}
if (loadedImage)
{
if (IsHDRFormat(loadedImage->GetPixelFormat()))
{
loadedImage->AddImageFlags(EIF_HDR);
}
return loadedImage;
} }
AZ_Warning("ImageProcessing", false, "No proper image loader to load file: %s", filename.c_str()); AZ_Warning("ImageProcessing", false, "No proper image loader to load file: %s", filename.c_str());

@ -19,7 +19,7 @@ namespace ImageProcessingAtom
const static AZ::u32 EIF_Decal = 0x4; // this is usually set through the preset const static AZ::u32 EIF_Decal = 0x4; // this is usually set through the preset
const static AZ::u32 EIF_Greyscale = 0x8; // hint for the engine (e.g. greyscale light beams can be applied to shadow mask), can be for DXT1 because compression artfacts don't count as color const static AZ::u32 EIF_Greyscale = 0x8; // hint for the engine (e.g. greyscale light beams can be applied to shadow mask), can be for DXT1 because compression artfacts don't count as color
const static AZ::u32 EIF_SupressEngineReduce = 0x10; // info for the engine: don't reduce texture resolution on this texture const static AZ::u32 EIF_SupressEngineReduce = 0x10; // info for the engine: don't reduce texture resolution on this texture
const static AZ::u32 EIF_UNUSED_BIT = 0x40; // Free to use const static AZ::u32 EIF_HDR = 0x40; // the image contains HDR data
const static AZ::u32 EIF_AttachedAlpha = 0x400; // deprecated: info for the engine: it's a texture with attached alpha channel const static AZ::u32 EIF_AttachedAlpha = 0x400; // deprecated: info for the engine: it's a texture with attached alpha channel
const static AZ::u32 EIF_SRGBRead = 0x800; // info for the engine: if gamma corrected rendering is on, this texture requires SRGBRead (it's not stored in linear) const static AZ::u32 EIF_SRGBRead = 0x800; // info for the engine: if gamma corrected rendering is on, this texture requires SRGBRead (it's not stored in linear)
const static AZ::u32 EIF_DontResize = 0x8000; // info for the engine: for dds textures that shouldn't be resized const static AZ::u32 EIF_DontResize = 0x8000; // info for the engine: for dds textures that shouldn't be resized

@ -52,6 +52,24 @@ namespace ImageProcessingAtom
return false; return false;
} }
bool IsHDRFormat(EPixelFormat fmt)
{
switch (fmt)
{
case ePixelFormat_BC6UH:
case ePixelFormat_R9G9B9E5:
case ePixelFormat_R32G32B32A32F:
case ePixelFormat_R32G32F:
case ePixelFormat_R32F:
case ePixelFormat_R16G16B16A16F:
case ePixelFormat_R16G16F:
case ePixelFormat_R16F:
return true;
default:
return false;
}
}
PixelFormatInfo::PixelFormatInfo( PixelFormatInfo::PixelFormatInfo(
uint32_t a_bitsPerPixel, uint32_t a_bitsPerPixel,
uint32_t a_Channels, uint32_t a_Channels,

Loading…
Cancel
Save