138 lines
5.8 KiB
C++
138 lines
5.8 KiB
C++
// Copyright (c) Wojciech Figat. All rights reserved.
|
|
|
|
#if COMPILE_WITH_TEXTURE_TOOL && COMPILE_WITH_ASTC
|
|
|
|
#include "TextureTool.h"
|
|
#include "Engine/Core/Log.h"
|
|
#include "Engine/Core/Math/Math.h"
|
|
#include "Engine/Graphics/Textures/TextureData.h"
|
|
#include "Engine/Graphics/PixelFormatExtensions.h"
|
|
#include "Engine/Graphics/RenderTools.h"
|
|
#include "Engine/Profiler/ProfilerCPU.h"
|
|
#include <ThirdParty/astc/astcenc.h>
|
|
|
|
bool TextureTool::ConvertAstc(TextureData& dst, const TextureData& src, const PixelFormat dstFormat)
|
|
{
|
|
PROFILE_CPU();
|
|
ASSERT(PixelFormatExtensions::IsCompressedASTC(dstFormat));
|
|
const int32 blockSize = PixelFormatExtensions::ComputeBlockSize(dstFormat);
|
|
const int32 bytesPerBlock = 16; // All ASTC blocks use 128 bits
|
|
|
|
// Configure the compressor run
|
|
const bool isSRGB = PixelFormatExtensions::IsSRGB(dstFormat);
|
|
const bool isHDR = PixelFormatExtensions::IsHDR(src.Format);
|
|
astcenc_profile astcProfile = isHDR ? ASTCENC_PRF_HDR_RGB_LDR_A : (isSRGB ? ASTCENC_PRF_LDR_SRGB : ASTCENC_PRF_LDR);
|
|
float astcQuality = ASTCENC_PRE_MEDIUM;
|
|
unsigned int astcFlags = 0; // TODO: add custom flags support for converter to handle ASTCENC_FLG_MAP_NORMAL
|
|
astcenc_config astcConfig;
|
|
astcenc_error astcError = astcenc_config_init(astcProfile, blockSize, blockSize, 1, astcQuality, astcFlags, &astcConfig);
|
|
if (astcError != ASTCENC_SUCCESS)
|
|
{
|
|
LOG(Warning, "Cannot compress image. ASTC failed with error: {}", String(astcenc_get_error_string(astcError)));
|
|
return true;
|
|
}
|
|
astcenc_swizzle astcSwizzle = { ASTCENC_SWZ_R, ASTCENC_SWZ_G, ASTCENC_SWZ_B, ASTCENC_SWZ_A };
|
|
if (!PixelFormatExtensions::HasAlpha(src.Format))
|
|
{
|
|
// No alpha channel in use so fill with 1
|
|
astcSwizzle.a = ASTCENC_SWZ_1;
|
|
}
|
|
|
|
// Allocate working state given config and thread_count
|
|
astcenc_context* astcContext;
|
|
astcError = astcenc_context_alloc(&astcConfig, 1, &astcContext);
|
|
if (astcError != ASTCENC_SUCCESS)
|
|
{
|
|
LOG(Warning, "Cannot compress image. ASTC failed with error: {}", String(astcenc_get_error_string(astcError)));
|
|
return true;
|
|
}
|
|
TextureData const* textureData = &src;
|
|
TextureData converted;
|
|
|
|
// Encoder uses full 4-component RGBA input image so convert it if needed
|
|
if (PixelFormatExtensions::ComputeComponentsCount(src.Format) != 4 ||
|
|
PixelFormatExtensions::IsCompressed(textureData->Format) ||
|
|
!PixelFormatExtensions::IsRgbAOrder(textureData->Format))
|
|
{
|
|
if (textureData != &src)
|
|
converted = src;
|
|
const PixelFormat tempFormat = isHDR ? PixelFormat::R16G16B16A16_Float : (PixelFormatExtensions::IsSRGB(src.Format) ? PixelFormat::R8G8B8A8_UNorm_sRGB : PixelFormat::R8G8B8A8_UNorm);
|
|
if (!TextureTool::Convert(converted, *textureData, tempFormat))
|
|
textureData = &converted;
|
|
}
|
|
|
|
// When converting from non-sRGB to sRGB we need to change the color-space manually (otherwise image is dark)
|
|
if (PixelFormatExtensions::IsSRGB(src.Format) != isSRGB)
|
|
{
|
|
if (textureData != &src)
|
|
converted = src;
|
|
Function<void(Color&)> transform = [](Color& c)
|
|
{
|
|
c = Color::LinearToSrgb(c);
|
|
};
|
|
if (!TextureTool::Transform(converted, transform))
|
|
textureData = &converted;
|
|
}
|
|
|
|
// Setup output
|
|
dst.Items.Resize(textureData->Items.Count());
|
|
dst.Width = textureData->Width;
|
|
dst.Height = textureData->Height;
|
|
dst.Depth = 1;
|
|
dst.Format = dstFormat;
|
|
|
|
// Compress all array slices
|
|
for (int32 arrayIndex = 0; arrayIndex < textureData->Items.Count() && astcError == ASTCENC_SUCCESS; arrayIndex++)
|
|
{
|
|
const auto& srcSlice = textureData->Items[arrayIndex];
|
|
auto& dstSlice = dst.Items[arrayIndex];
|
|
auto mipLevels = srcSlice.Mips.Count();
|
|
dstSlice.Mips.Resize(mipLevels, false);
|
|
|
|
// Compress all mip levels
|
|
for (int32 mipIndex = 0; mipIndex < mipLevels && astcError == ASTCENC_SUCCESS; mipIndex++)
|
|
{
|
|
const auto& srcMip = srcSlice.Mips[mipIndex];
|
|
auto& dstMip = dstSlice.Mips[mipIndex];
|
|
auto mipWidth = Math::Max(textureData->Width >> mipIndex, 1);
|
|
auto mipHeight = Math::Max(textureData->Height >> mipIndex, 1);
|
|
auto blocksWidth = Math::Max(Math::DivideAndRoundUp(mipWidth, blockSize), 1);
|
|
auto blocksHeight = Math::Max(Math::DivideAndRoundUp(mipHeight, blockSize), 1);
|
|
uint32 mipRowPitch, mipSlicePitch;
|
|
RenderTools::ComputePitch(textureData->Format, mipWidth, mipHeight, mipRowPitch, mipSlicePitch);
|
|
ASSERT(srcMip.RowPitch == mipRowPitch);
|
|
ASSERT(srcMip.DepthPitch == mipSlicePitch);
|
|
ASSERT(srcMip.Lines == mipHeight);
|
|
|
|
// Allocate memory
|
|
dstMip.RowPitch = blocksWidth * bytesPerBlock;
|
|
dstMip.DepthPitch = dstMip.RowPitch * blocksHeight;
|
|
dstMip.Lines = blocksHeight;
|
|
dstMip.Data.Allocate(dstMip.DepthPitch);
|
|
|
|
// Compress image
|
|
astcenc_image astcInput;
|
|
astcInput.dim_x = mipWidth;
|
|
astcInput.dim_y = mipHeight;
|
|
astcInput.dim_z = 1;
|
|
astcInput.data_type = isHDR ? ASTCENC_TYPE_F16 : ASTCENC_TYPE_U8;
|
|
void* srcData = (void*)srcMip.Data.Get();
|
|
astcInput.data = &srcData;
|
|
astcError = astcenc_compress_image(astcContext, &astcInput, &astcSwizzle, dstMip.Data.Get(), dstMip.Data.Length(), 0);
|
|
if (astcError == ASTCENC_SUCCESS)
|
|
astcError = astcenc_compress_reset(astcContext);
|
|
}
|
|
}
|
|
|
|
// Clean up
|
|
if (astcError != ASTCENC_SUCCESS)
|
|
{
|
|
LOG(Warning, "Cannot compress image. ASTC failed with error: {}", String(astcenc_get_error_string(astcError)));
|
|
return true;
|
|
}
|
|
astcenc_context_free(astcContext);
|
|
return astcError != ASTCENC_SUCCESS;
|
|
}
|
|
|
|
#endif
|