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>
This commit is contained in:
@@ -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,29 +209,65 @@ namespace ImageProcessingAtom
|
|||||||
dstImage->GetImagePointer(mip, dstMem, dstPitch);
|
dstImage->GetImagePointer(mip, dstMem, dstPitch);
|
||||||
AZ::u32 dataSize = dstImage->GetMipBufSize(mip);
|
AZ::u32 dataSize = dstImage->GetMipBufSize(mip);
|
||||||
|
|
||||||
// Create jobs for each compression thread
|
if (threadCount == 1)
|
||||||
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, 0);
|
||||||
|
if (error != ASTCENC_SUCCESS)
|
||||||
{
|
{
|
||||||
|
status = error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
AZ::JobCompletion* completionJob = nullptr;
|
||||||
|
if (!currentJob)
|
||||||
|
{
|
||||||
|
completionJob = aznew AZ::JobCompletion();
|
||||||
|
}
|
||||||
|
// Create jobs for each compression thread
|
||||||
|
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
|
||||||
|
|
||||||
|
// 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->Start();
|
||||||
|
}
|
||||||
|
|
||||||
astcenc_error error = astcenc_compress_image(context, &image, &swizzle, dstMem, dataSize, threadIdx);
|
astcenc_error error = astcenc_compress_image(context, &image, &swizzle, dstMem, dataSize, threadIdx);
|
||||||
if (error != ASTCENC_SUCCESS)
|
if (error != ASTCENC_SUCCESS)
|
||||||
{
|
{
|
||||||
status = error;
|
status = error;
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
|
if (currentJob)
|
||||||
|
{
|
||||||
|
currentJob->WaitForChildren();
|
||||||
|
}
|
||||||
|
|
||||||
AZ::Job* simulationJob = AZ::CreateJobFunction(AZStd::move(jobLambda), true, nullptr); //auto-deletes
|
if (completionJob)
|
||||||
simulationJob->SetDependent(completionJob);
|
{
|
||||||
simulationJob->Start();
|
completionJob->StartAndWaitForCompletion();
|
||||||
}
|
delete completionJob;
|
||||||
|
completionJob = nullptr;
|
||||||
if (completionJob)
|
}
|
||||||
{
|
|
||||||
completionJob->StartAndWaitForCompletion();
|
|
||||||
delete completionJob;
|
|
||||||
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,
|
||||||
|
|||||||
Reference in New Issue
Block a user