diff --git a/Gems/Atom/Feature/Common/Code/Source/FrameCaptureSystemComponent.cpp b/Gems/Atom/Feature/Common/Code/Source/FrameCaptureSystemComponent.cpp index 90499be8b8..f8c1258fc0 100644 --- a/Gems/Atom/Feature/Common/Code/Source/FrameCaptureSystemComponent.cpp +++ b/Gems/Atom/Feature/Common/Code/Source/FrameCaptureSystemComponent.cpp @@ -21,6 +21,8 @@ #include #include +#include +#include #include #include @@ -55,6 +57,43 @@ namespace AZ FrameCaptureOutputResult PngFrameCaptureOutput( const AZStd::string& outputFilePath, const AZ::RPI::AttachmentReadback::ReadbackResult& readbackResult) { + AZStd::shared_ptr> buffer = readbackResult.m_dataBuffer; + + // convert bgra to rgba by swapping channels + const int numChannels = AZ::RHI::GetFormatComponentCount(readbackResult.m_imageDescriptor.m_format); + if (readbackResult.m_imageDescriptor.m_format == RHI::Format::B8G8R8A8_UNORM) + { + buffer = AZStd::make_shared>(readbackResult.m_dataBuffer->size()); + AZStd::copy(readbackResult.m_dataBuffer->begin(), readbackResult.m_dataBuffer->end(), buffer->begin()); + + AZ::JobCompletion jobCompletion; + const int numThreads = 8; + const int numPixelsPerThread = buffer->size() / numChannels / numThreads; + for (int i = 0; i < numThreads; ++i) + { + int startPixel = i * numPixelsPerThread; + + AZ::Job* job = AZ::CreateJobFunction( + [&, startPixel, numPixelsPerThread]() + { + for (int pixelOffset = 0; pixelOffset < numPixelsPerThread; ++pixelOffset) + { + if (startPixel * numChannels + numChannels < buffer->size()) + { + AZStd::swap( + buffer->data()[(startPixel + pixelOffset) * numChannels], + buffer->data()[(startPixel + pixelOffset) * numChannels + 2] + ); + } + } + }, true, nullptr); + + job->SetDependent(&jobCompletion); + job->Start(); + } + jobCompletion.StartAndWaitForCompletion(); + } + using namespace OIIO; AZStd::unique_ptr out = ImageOutput::create(outputFilePath.c_str()); if (out) @@ -62,13 +101,13 @@ namespace AZ ImageSpec spec( readbackResult.m_imageDescriptor.m_size.m_width, readbackResult.m_imageDescriptor.m_size.m_height, - AZ::RHI::GetFormatComponentCount(readbackResult.m_imageDescriptor.m_format) + numChannels ); spec.attribute("png:compressionLevel", r_pngCompressionLevel); if (out->open(outputFilePath.c_str(), spec)) { - out->write_image(TypeDesc::UINT8, readbackResult.m_dataBuffer->data()); + out->write_image(TypeDesc::UINT8, buffer->data()); out->close(); return FrameCaptureOutputResult{FrameCaptureResult::Success, AZStd::nullopt}; } @@ -460,13 +499,23 @@ namespace AZ #if defined(OPEN_IMAGE_IO_ENABLED) else if (extension == "png") { - AZStd::string folderPath; - AzFramework::StringFunc::Path::GetFolderPath(m_outputFilePath.c_str(), folderPath); - AZ::IO::SystemFile::CreateDir(folderPath.c_str()); + if (readbackResult.m_imageDescriptor.m_format == RHI::Format::R8G8B8A8_UNORM || + readbackResult.m_imageDescriptor.m_format == RHI::Format::B8G8R8A8_UNORM) + { + AZStd::string folderPath; + AzFramework::StringFunc::Path::GetFolderPath(m_outputFilePath.c_str(), folderPath); + AZ::IO::SystemFile::CreateDir(folderPath.c_str()); - const auto frameCaptureResult = PngFrameCaptureOutput(m_outputFilePath, readbackResult); - m_result = frameCaptureResult.m_result; - m_latestCaptureInfo = frameCaptureResult.m_errorMessage.value_or(""); + const auto frameCaptureResult = PngFrameCaptureOutput(m_outputFilePath, readbackResult); + m_result = frameCaptureResult.m_result; + m_latestCaptureInfo = frameCaptureResult.m_errorMessage.value_or(""); + } + else + { + m_latestCaptureInfo = AZStd::string::format( + "Can't save image with format %s to a png file", RHI::ToString(readbackResult.m_imageDescriptor.m_format)); + m_result = FrameCaptureResult::UnsupportedFormat; + } } #endif else