89e6df1c7f
ATOM-16575 clean up image builder presets files Removed unused image builder presets Deprecating preset UUID and use preset name as unique id Delete all .exportsettings file which were only used for legacy imageProcessing gem. Signed-off-by: Qing Tao <qingtao@amazon.com>
321 lines
13 KiB
C++
321 lines
13 KiB
C++
/*
|
|
* 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 <ImageBuilderComponent.h>
|
|
#include <AzCore/std/string/conversions.h>
|
|
#include <AzCore/Serialization/SerializeContext.h>
|
|
#include <AzFramework/StringFunc/StringFunc.h>
|
|
#include <AzFramework/IO/LocalFileIO.h>
|
|
#include <AzCore/Debug/Trace.h>
|
|
#include <BuilderSettings/BuilderSettingManager.h>
|
|
#include <BuilderSettings/CubemapSettings.h>
|
|
#include <ImageLoader/ImageLoaders.h>
|
|
#include <Processing/ImageAssetProducer.h>
|
|
#include <Processing/ImageConvert.h>
|
|
#include <Processing/ImageToProcess.h>
|
|
#include <Processing/PixelFormatInfo.h>
|
|
#include <AzFramework/API/ApplicationAPI.h>
|
|
#include <AzCore/Serialization/EditContextConstants.inl>
|
|
#include <QFile>
|
|
|
|
#include <AzQtComponents/Utilities/QtPluginPaths.h>
|
|
|
|
#include <Atom/RPI.Reflect/Asset/AssetHandler.h>
|
|
#include <Atom/RPI.Reflect/Image/StreamingImageAssetHandler.h>
|
|
#include <Atom/RPI.Reflect/Image/ImageMipChainAsset.h>
|
|
|
|
namespace ImageProcessingAtom
|
|
{
|
|
BuilderPluginComponent::BuilderPluginComponent()
|
|
{
|
|
// AZ Components should only initialize their members to null and empty in constructor
|
|
// after construction, they may be deserialized from file.
|
|
}
|
|
|
|
BuilderPluginComponent::~BuilderPluginComponent()
|
|
{
|
|
}
|
|
|
|
void BuilderPluginComponent::Init()
|
|
{
|
|
}
|
|
|
|
void BuilderPluginComponent::Activate()
|
|
{
|
|
//load qt plugins for some image file formats support
|
|
AzQtComponents::PrepareQtPaths();
|
|
|
|
// create and initialize BuilderSettingManager once since it's will be used for image conversion
|
|
BuilderSettingManager::CreateInstance();
|
|
|
|
auto outcome = BuilderSettingManager::Instance()->LoadConfig();
|
|
AZ_Error("Image Processing", outcome.IsSuccess(), "Failed to load Atom image builder settings.");
|
|
|
|
if (!outcome.IsSuccess())
|
|
{
|
|
return;
|
|
}
|
|
|
|
// Activate is where you'd perform registration with other objects and systems.
|
|
// Since we want to register our builder, we do that here:
|
|
AssetBuilderSDK::AssetBuilderDesc builderDescriptor;
|
|
builderDescriptor.m_name = "Atom Image Builder";
|
|
|
|
for (int i = 0; i < s_TotalSupportedImageExtensions; i++)
|
|
{
|
|
builderDescriptor.m_patterns.push_back(AssetBuilderSDK::AssetBuilderPattern(s_SupportedImageExtensions[i], AssetBuilderSDK::AssetBuilderPattern::PatternType::Wildcard));
|
|
}
|
|
|
|
builderDescriptor.m_busId = azrtti_typeid<ImageBuilderWorker>();
|
|
builderDescriptor.m_createJobFunction = AZStd::bind(&ImageBuilderWorker::CreateJobs, &m_imageBuilder, AZStd::placeholders::_1, AZStd::placeholders::_2);
|
|
builderDescriptor.m_processJobFunction = AZStd::bind(&ImageBuilderWorker::ProcessJob, &m_imageBuilder, AZStd::placeholders::_1, AZStd::placeholders::_2);
|
|
builderDescriptor.m_version = 25; // [ATOM-16575]
|
|
builderDescriptor.m_analysisFingerprint = ImageProcessingAtom::BuilderSettingManager::Instance()->GetAnalysisFingerprint();
|
|
m_imageBuilder.BusConnect(builderDescriptor.m_busId);
|
|
AssetBuilderSDK::AssetBuilderBus::Broadcast(&AssetBuilderSDK::AssetBuilderBusTraits::RegisterBuilderInformation, builderDescriptor);
|
|
|
|
m_assetHandlers.emplace_back(AZ::RPI::MakeAssetHandler<AZ::RPI::ImageMipChainAssetHandler>());
|
|
m_assetHandlers.emplace_back(AZ::RPI::MakeAssetHandler<AZ::RPI::StreamingImageAssetHandler>());
|
|
|
|
ImageProcessingRequestBus::Handler::BusConnect();
|
|
ImageBuilderRequestBus::Handler::BusConnect();
|
|
}
|
|
|
|
void BuilderPluginComponent::Deactivate()
|
|
{
|
|
ImageProcessingRequestBus::Handler::BusDisconnect();
|
|
ImageBuilderRequestBus::Handler::BusDisconnect();
|
|
m_imageBuilder.BusDisconnect();
|
|
BuilderSettingManager::DestroyInstance();
|
|
CPixelFormats::DestroyInstance();
|
|
}
|
|
|
|
void BuilderPluginComponent::Reflect(AZ::ReflectContext* context)
|
|
{
|
|
// components also get Reflect called automatically
|
|
// this is your opportunity to perform static reflection or type registration of any types you want the serializer to know about
|
|
if (AZ::SerializeContext* serialize = azrtti_cast<AZ::SerializeContext*>(context))
|
|
{
|
|
serialize->Class<BuilderPluginComponent, AZ::Component>()
|
|
->Version(0)
|
|
->Attribute(AZ::Edit::Attributes::SystemComponentTags, AZStd::vector<AZ::Crc32>({ AssetBuilderSDK::ComponentTags::AssetBuilder }))
|
|
;
|
|
}
|
|
|
|
BuilderSettingManager::Reflect(context);
|
|
BuilderSettings::Reflect(context);
|
|
MultiplatformPresetSettings::Reflect(context);
|
|
PresetSettings::Reflect(context);
|
|
CubemapSettings::Reflect(context);
|
|
MipmapSettings::Reflect(context);
|
|
TextureSettings::Reflect(context);
|
|
}
|
|
|
|
void BuilderPluginComponent::GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided)
|
|
{
|
|
provided.push_back(AZ_CRC("ImagerBuilderPluginService", 0x6dc0db6e));
|
|
}
|
|
|
|
void BuilderPluginComponent::GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible)
|
|
{
|
|
incompatible.push_back(AZ_CRC("ImagerBuilderPluginService", 0x6dc0db6e));
|
|
}
|
|
|
|
IImageObjectPtr BuilderPluginComponent::LoadImage(const AZStd::string& filePath)
|
|
{
|
|
return IImageObjectPtr(LoadImageFromFile(filePath));
|
|
}
|
|
|
|
IImageObjectPtr BuilderPluginComponent::LoadImagePreview(const AZStd::string& filePath)
|
|
{
|
|
IImageObjectPtr image(LoadImageFromFile(filePath));
|
|
if (image)
|
|
{
|
|
ImageToProcess imageToProcess(image);
|
|
imageToProcess.ConvertFormat(ePixelFormat_R8G8B8A8);
|
|
return imageToProcess.Get();
|
|
}
|
|
return image;
|
|
}
|
|
|
|
IImageObjectPtr BuilderPluginComponent::CreateImage(
|
|
AZ::u32 width,
|
|
AZ::u32 height,
|
|
AZ::u32 maxMipCount,
|
|
EPixelFormat pixelFormat)
|
|
{
|
|
IImageObjectPtr image(IImageObject::CreateImage(width, height, maxMipCount, pixelFormat));
|
|
return image;
|
|
}
|
|
|
|
AZStd::vector<AssetBuilderSDK::JobProduct> BuilderPluginComponent::ConvertImageObject(
|
|
IImageObjectPtr imageObject,
|
|
const AZStd::string& presetName,
|
|
const AZStd::string& platformName,
|
|
const AZStd::string& outputDir,
|
|
const AZ::Data::AssetId& sourceAssetId,
|
|
const AZStd::string& sourceAssetName)
|
|
{
|
|
AZStd::vector<AssetBuilderSDK::JobProduct> outProducts;
|
|
|
|
AZStd::string_view presetFilePath;
|
|
const PresetSettings* preset = BuilderSettingManager::Instance()->GetPreset(PresetName(presetName), platformName, &presetFilePath);
|
|
if (preset == nullptr)
|
|
{
|
|
AZ_Assert(false, "Cannot find preset with name %s.", presetName.c_str());
|
|
return outProducts;
|
|
}
|
|
|
|
AZStd::unique_ptr<ImageConvertProcessDescriptor> desc = AZStd::make_unique<ImageConvertProcessDescriptor>();
|
|
TextureSettings& textureSettings = desc->m_textureSetting;
|
|
textureSettings.m_preset = preset->m_name;
|
|
desc->m_inputImage = imageObject;
|
|
desc->m_presetSetting = *preset;
|
|
desc->m_isPreview = false;
|
|
desc->m_platform = platformName;
|
|
desc->m_filePath = presetFilePath;
|
|
desc->m_isStreaming = BuilderSettingManager::Instance()->GetBuilderSetting(platformName)->m_enableStreaming;
|
|
desc->m_imageName = sourceAssetName;
|
|
desc->m_outputFolder = outputDir;
|
|
desc->m_sourceAssetId = sourceAssetId;
|
|
|
|
// Create an image convert process
|
|
ImageConvertProcess process(AZStd::move(desc));
|
|
process.ProcessAll();
|
|
bool result = process.IsSucceed();
|
|
if (result)
|
|
{
|
|
process.GetAppendOutputProducts(outProducts);
|
|
}
|
|
|
|
return outProducts;
|
|
}
|
|
|
|
bool BuilderPluginComponent::DoesSupportPlatform(const AZStd::string& platformId)
|
|
{
|
|
return ImageProcessingAtom::BuilderSettingManager::Instance()->DoesSupportPlatform(platformId);
|
|
}
|
|
|
|
bool BuilderPluginComponent::IsPresetFormatSquarePow2(const AZStd::string& presetName, const AZStd::string& platformName)
|
|
{
|
|
AZStd::string_view filePath;
|
|
const PresetSettings* preset = BuilderSettingManager::Instance()->GetPreset(PresetName(presetName), platformName, &filePath);
|
|
if (preset == nullptr)
|
|
{
|
|
AZ_Assert(false, "Cannot find preset with name %s.", presetName.c_str());
|
|
return false;
|
|
}
|
|
|
|
const PixelFormatInfo* info = CPixelFormats::GetInstance().GetPixelFormatInfo(preset->m_pixelFormat);
|
|
return info->bSquarePow2;
|
|
}
|
|
|
|
void ImageBuilderWorker::ShutDown()
|
|
{
|
|
// it is important to note that this will be called on a different thread than your process job thread
|
|
m_isShuttingDown = true;
|
|
}
|
|
|
|
// this happens early on in the file scanning pass
|
|
// this function should consistently always create the same jobs, and should do no checking whether the job is up to date or not - just be consistent.
|
|
void ImageBuilderWorker::CreateJobs(const AssetBuilderSDK::CreateJobsRequest& request, AssetBuilderSDK::CreateJobsResponse& response)
|
|
{
|
|
if (m_isShuttingDown)
|
|
{
|
|
response.m_result = AssetBuilderSDK::CreateJobsResultCode::ShuttingDown;
|
|
return;
|
|
}
|
|
|
|
// Get the extension of the file
|
|
AZStd::string ext;
|
|
AzFramework::StringFunc::Path::GetExtension(request.m_sourceFile.c_str(), ext, false);
|
|
AZStd::to_upper(ext.begin(), ext.end());
|
|
|
|
// We process the same file for all platforms
|
|
for (const AssetBuilderSDK::PlatformInfo& platformInfo : request.m_enabledPlatforms)
|
|
{
|
|
if (ImageProcessingAtom::BuilderSettingManager::Instance()->DoesSupportPlatform(platformInfo.m_identifier))
|
|
{
|
|
AssetBuilderSDK::JobDescriptor descriptor;
|
|
descriptor.m_jobKey = ext + " Atom Compile";
|
|
descriptor.SetPlatformIdentifier(platformInfo.m_identifier.c_str());
|
|
descriptor.m_critical = false;
|
|
response.m_createJobOutputs.push_back(descriptor);
|
|
}
|
|
}
|
|
|
|
response.m_result = AssetBuilderSDK::CreateJobsResultCode::Success;
|
|
return;
|
|
}
|
|
|
|
// later on, this function will be called for jobs that actually need doing.
|
|
// the request will contain the CreateJobResponse you constructed earlier, including any keys and values you placed into the hash table
|
|
void ImageBuilderWorker::ProcessJob(const AssetBuilderSDK::ProcessJobRequest& request, AssetBuilderSDK::ProcessJobResponse& response)
|
|
{
|
|
// Before we begin, let's make sure we are not meant to abort.
|
|
AssetBuilderSDK::JobCancelListener jobCancelListener(request.m_jobId);
|
|
|
|
AZStd::vector<AZStd::string> productFilepaths;
|
|
bool imageProcessingSuccessful = false;
|
|
bool needConversion = true;
|
|
|
|
// Do conversion and get exported file's path
|
|
if (needConversion)
|
|
{
|
|
AZ_TracePrintf(AssetBuilderSDK::InfoWindow, "Performing image conversion: %s\n", request.m_fullPath.c_str());
|
|
ImageConvertProcess* process = CreateImageConvertProcess(request.m_fullPath, request.m_tempDirPath,
|
|
request.m_jobDescription.GetPlatformIdentifier(), response.m_outputProducts);
|
|
|
|
if (process != nullptr)
|
|
{
|
|
//the process can be stopped if the job is cancelled or the worker is shutting down
|
|
while (!process->IsFinished() && !m_isShuttingDown && !jobCancelListener.IsCancelled())
|
|
{
|
|
process->UpdateProcess();
|
|
}
|
|
|
|
//get process result
|
|
imageProcessingSuccessful = process->IsSucceed();
|
|
if (imageProcessingSuccessful)
|
|
{
|
|
process->GetAppendOutputProducts(response.m_outputProducts);
|
|
}
|
|
|
|
delete process;
|
|
}
|
|
else
|
|
{
|
|
imageProcessingSuccessful = false;
|
|
}
|
|
}
|
|
|
|
if (imageProcessingSuccessful)
|
|
{
|
|
response.m_resultCode = AssetBuilderSDK::ProcessJobResult_Success;
|
|
}
|
|
else
|
|
{
|
|
if (m_isShuttingDown)
|
|
{
|
|
AZ_TracePrintf(AssetBuilderSDK::ErrorWindow, "Cancelled job %s because shutdown was requested.\n", request.m_fullPath.c_str());
|
|
response.m_resultCode = AssetBuilderSDK::ProcessJobResult_Cancelled;
|
|
}
|
|
else if (jobCancelListener.IsCancelled())
|
|
{
|
|
AZ_TracePrintf(AssetBuilderSDK::ErrorWindow, "Cancelled was requested for job %s.\n", request.m_fullPath.c_str());
|
|
response.m_resultCode = AssetBuilderSDK::ProcessJobResult_Cancelled;
|
|
}
|
|
else
|
|
{
|
|
AZ_TracePrintf(AssetBuilderSDK::ErrorWindow, "Unexpected error during processing job %s.\n", request.m_fullPath.c_str());
|
|
response.m_resultCode = AssetBuilderSDK::ProcessJobResult_Failed;
|
|
}
|
|
}
|
|
}
|
|
} // namespace ImageProcessingAtom
|