Skip to content

Commit

Permalink
Fixes for ASTC compression.
Browse files Browse the repository at this point in the history
HDR compression requires averages and variances, which requires texture
information. The context is created per thread and this information
computed per block so a separate copy of the full image isn't required.

Fixed error reported when creating an sRGB ASTC HDR texture. Also fixed the
quality flags for PVRTC compression.
  • Loading branch information
akb825 committed Dec 31, 2020
1 parent f87cd69 commit 6bf549a
Show file tree
Hide file tree
Showing 4 changed files with 150 additions and 69 deletions.
181 changes: 121 additions & 60 deletions lib/src/AstcConverter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,29 +27,114 @@
#include "astcenc.h"
#include "astcenc_internal.h"

// Need to stub out these functions, since they don't work on all compilers.
// Need to stub out these functions, since they don't work on all compilers. Return true for all,
// since it will only be called if support was compiled in.
int cpu_supports_sse41()
{
return 0;
return 1;
}

int cpu_supports_popcnt()
{
return 0;
return 1;
}

int cpu_supports_avx2()
{
return 0;
return 1;
}

namespace cuttlefish
{

static const unsigned int maxBlockSize = 12;

class AstcConverter::AstcThreadData : public Converter::ThreadData
{
public:
explicit AstcThreadData(AstcConverter& converter)
{
unsigned int flags = 0;
if (converter.m_alphaType == Texture::Alpha::Standard ||
converter.m_alphaType == Texture::Alpha::PreMultiplied)
{
flags |= ASTCENC_FLG_USE_ALPHA_WEIGHT;
}
if (converter.m_colorSpace == ColorSpace::sRGB)
flags |= ASTCENC_FLG_USE_PERCEPTUAL;

astcenc_preset preset;
switch (converter.m_quality)
{
case Texture::Quality::Lowest:
preset = ASTCENC_PRE_FASTEST;
break;
case Texture::Quality::Low:
preset = ASTCENC_PRE_FAST;
break;
case Texture::Quality::Normal:
preset = ASTCENC_PRE_MEDIUM;
break;
case Texture::Quality::High:
preset = ASTCENC_PRE_THOROUGH;
break;
case Texture::Quality::Highest:
preset = ASTCENC_PRE_EXHAUSTIVE;
break;
default:
assert(false);
return;
}

astcenc_config config;
astcenc_config_init(converter.m_hdr ? ASTCENC_PRF_HDR : ASTCENC_PRF_LDR, converter.m_blockX,
converter.m_blockY, 1, preset, flags, config);

astcenc_context_alloc(config, 1, &context);

dummyImage.dim_x = converter.m_blockX;
dummyImage.dim_y = converter.m_blockY;
dummyImage.dim_z = 1;
dummyImage.data_type = ASTCENC_TYPE_F32;
dummyImage.data = nullptr;

if (context->config.v_rgb_mean != 0.0f || context->config.v_rgb_stdev != 0.0f ||
context->config.v_a_mean != 0.0f || context->config.v_a_stdev != 0.0f)
{
context->input_averages = m_inputAverages;
context->input_variances = m_inputVariances;
context->input_alpha_averages = m_inputAlphaAverages;

astcenc_swizzle swizzle;
swizzle.r = converter.m_colorMask.r ? ASTCENC_SWZ_R : ASTCENC_SWZ_0;
swizzle.g = converter.m_colorMask.g ? ASTCENC_SWZ_G : ASTCENC_SWZ_0;
swizzle.b = converter.m_colorMask.b ? ASTCENC_SWZ_B : ASTCENC_SWZ_0;
if (converter.m_colorMask.a)
{
swizzle.a = converter.m_alphaType == Texture::Alpha::None ?
ASTCENC_SWZ_1 : ASTCENC_SWZ_A;
}
else
swizzle.a = ASTCENC_SWZ_0;
init_compute_averages_and_variances(dummyImage, context->config.v_rgb_power,
context->config.v_a_power, context->config.v_rgba_radius,
context->config.a_scale_radius, swizzle, context->arg, context->ag);
}
}

~AstcThreadData()
{
astcenc_context_free(context);
}

astcenc_context* context;
astcenc_image dummyImage;
compress_symbolic_block_buffers tempBuffers;

private:
float4 m_inputAverages[maxBlockSize*maxBlockSize];
float4 m_inputVariances[maxBlockSize*maxBlockSize];
float m_inputAlphaAverages[maxBlockSize*maxBlockSize];
};

static bool initialize()
Expand All @@ -62,72 +147,24 @@ static bool initialize()
AstcConverter::AstcConverter(const Texture& texture, const Image& image, unsigned int blockX,
unsigned int blockY, Texture::Quality quality)
: Converter(image), m_blockX(blockX), m_blockY(blockY),
m_jobsX((image.width() + blockX - 1)/blockX), m_jobsY((image.height() + blockY - 1)/blockY)
m_jobsX((image.width() + blockX - 1)/blockX), m_jobsY((image.height() + blockY - 1)/blockY),
m_quality(quality), m_alphaType(texture.alphaType()), m_colorSpace(texture.colorSpace()),
m_colorMask(texture.colorMask()), m_hdr(texture.type() == Texture::Type::UFloat)
{
static bool initialized = initialize();
(void)initialized;

data().resize(m_jobsX*m_jobsY*blockSize);

assert(texture.type() == Texture::Type::UNorm || texture.type() == Texture::Type::UFloat);
m_colorMask = texture.colorMask();
m_hdr = texture.type() == Texture::Type::UFloat;

// Just need the image for sizing information.
m_dummyImage.reset(new astcenc_image);
m_dummyImage->dim_x = image.width();
m_dummyImage->dim_y = image.height();
m_dummyImage->dim_z = 1;
m_dummyImage->data_type = m_hdr ? ASTCENC_TYPE_F16 : ASTCENC_TYPE_U8;
m_dummyImage->data = nullptr;

unsigned int flags = 0;
if (texture.alphaType() == Texture::Alpha::Standard ||
texture.alphaType() == Texture::Alpha::PreMultiplied)
{
flags |= ASTCENC_FLG_USE_ALPHA_WEIGHT;
}
if (texture.colorSpace() == ColorSpace::sRGB)
flags |= ASTCENC_FLG_USE_PERCEPTUAL;
astcenc_config config;
astcenc_config_init(m_hdr ? ASTCENC_PRF_HDR : ASTCENC_PRF_LDR, m_blockX, m_blockY, 1,
static_cast<astcenc_preset>(quality), flags, config);

astcenc_context_alloc(config, 1, &m_context);

if (m_context->config.v_rgb_mean != 0.0f || m_context->config.v_rgb_stdev != 0.0f ||
m_context->config.v_a_mean != 0.0f || m_context->config.v_a_stdev != 0.0f)
{
unsigned int texelCount = image.width()*image.height();
m_context->input_averages = new float4[texelCount];
m_context->input_variances = new float4[texelCount];
m_context->input_alpha_averages = new float[texelCount];

astcenc_swizzle swizzle;
swizzle.r = texture.colorMask().r ? ASTCENC_SWZ_R : ASTCENC_SWZ_0;
swizzle.g = texture.colorMask().g ? ASTCENC_SWZ_G : ASTCENC_SWZ_0;
swizzle.b = texture.colorMask().b ? ASTCENC_SWZ_B : ASTCENC_SWZ_0;
if (texture.colorMask().a)
swizzle.a = texture.alphaType() == Texture::Alpha::None ? ASTCENC_SWZ_1 : ASTCENC_SWZ_A;
else
swizzle.a = ASTCENC_SWZ_0;
init_compute_averages_and_variances(*m_dummyImage, m_context->config.v_rgb_power,
m_context->config.v_a_power, m_context->config.v_rgba_radius,
m_context->config.a_scale_radius, swizzle, m_context->arg, m_context->ag);
compute_averages_and_variances(*m_context, m_context->ag);
}
data().resize(m_jobsX*m_jobsY*blockSize);
}

AstcConverter::~AstcConverter()
{
delete[] m_context->input_averages;
delete[] m_context->input_variances;
delete[] m_context->input_alpha_averages;
astcenc_context_free(m_context);
}

void AstcConverter::process(unsigned int x, unsigned int y, ThreadData* threadData)
{
ColorRGBAf imageData[maxBlockSize*maxBlockSize];
imageblock astcBlock;
astcBlock.xpos = x*m_blockX;
astcBlock.ypos = y*m_blockY;
Expand All @@ -139,6 +176,7 @@ void AstcConverter::process(unsigned int x, unsigned int y, ThreadData* threadDa
for (unsigned int i = 0; i < m_blockX; ++i, ++index)
{
unsigned int scanlineIdx = std::min(x*m_blockX + i, image().width() - 1);
imageData[index] = scanline[scanlineIdx];
astcBlock.data_r[index] = scanline[scanlineIdx].r;
astcBlock.data_g[index] = scanline[scanlineIdx].g;
astcBlock.data_b[index] = scanline[scanlineIdx].b;
Expand All @@ -149,20 +187,43 @@ void AstcConverter::process(unsigned int x, unsigned int y, ThreadData* threadDa
}
}

auto astcThreadData = static_cast<AstcThreadData*>(threadData);
void* imageDataPtr = imageData;
astcThreadData->dummyImage.data = &imageDataPtr;

// Fill in the rest of the information.
imageblock_initialize_work_from_orig(&astcBlock, m_blockX*m_blockY);
update_imageblock_flags(&astcBlock, m_blockX, m_blockY, 1);

astcenc_context* context = astcThreadData->context;
if (astcThreadData->context->input_averages)
{
// This assumes that it's going to be a pool of thread jobs, so always make sure there's
// exactly one job ready.
context->manage_avg_var.reset();
context->manage_avg_var.init(1);
compute_averages_and_variances(*context, astcThreadData->context->ag);
}

symbolic_compressed_block symbolicBlock;
auto block = reinterpret_cast<physical_compressed_block*>(
data().data() + (y*m_jobsX + x)*blockSize);
compress_block(*m_context, *m_dummyImage, &astcBlock, symbolicBlock, *block,
&reinterpret_cast<AstcThreadData*>(threadData)->tempBuffers);
compress_block(*context, astcThreadData->dummyImage, &astcBlock, symbolicBlock, *block,
&astcThreadData->tempBuffers);
}

std::unique_ptr<Converter::ThreadData> AstcConverter::createThreadData()
{
return std::unique_ptr<ThreadData>(new AstcThreadData);
#if CUTTLEFISH_MSC
#pragma warning(push)
#pragma warning(disable: 4316)
#endif

return std::unique_ptr<ThreadData>(new AstcThreadData(*this));

#if CUTTLEFISH_MSC
#pragma warning(pop)
#endif
}

} // namespace cuttlefish
Expand Down
7 changes: 4 additions & 3 deletions lib/src/AstcConverter.h
Original file line number Diff line number Diff line change
Expand Up @@ -44,16 +44,17 @@ class AstcConverter : public Converter

private:
class AstcThreadData;
friend class AstcThreadData;

unsigned int m_blockX;
unsigned int m_blockY;
unsigned int m_jobsX;
unsigned int m_jobsY;
Texture::Quality m_quality;
Texture::Alpha m_alphaType;
ColorSpace m_colorSpace;
Texture::ColorMask m_colorMask;
bool m_hdr;

std::unique_ptr<astcenc_image> m_dummyImage;
astcenc_context* m_context;
};

} // namespace cuttlefish
Expand Down
26 changes: 24 additions & 2 deletions lib/src/PvrtcConverter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -95,8 +95,30 @@ void PvrtcConverter::process(unsigned int, unsigned int, ThreadData*)
return;
}

pvrTexture.Transcode(pixelType, PVRTLVT_UnsignedByteNorm, PVRTLCS_Linear,
static_cast<PVRTexLibCompressorQuality>(m_quality));
PVRTexLibCompressorQuality quality;
switch (m_quality)
{
case Texture::Quality::Lowest:
quality = PVRTLCQ_PVRTCFastest;
break;
case Texture::Quality::Low:
quality = PVRTLCQ_PVRTCLow;
break;
case Texture::Quality::Normal:
quality = PVRTLCQ_PVRTCNormal;
break;
case Texture::Quality::High:
quality = PVRTLCQ_PVRTCHigh;
break;
case Texture::Quality::Highest:
quality = PVRTLCQ_PVRTCBest;
break;
default:
assert(false);
return;
}

pvrTexture.Transcode(pixelType, PVRTLVT_UnsignedByteNorm, PVRTLCS_Linear, quality);

auto textureData = reinterpret_cast<const std::uint8_t*>(pvrTexture.GetTextureDataPointer());
data().assign(textureData, textureData + pvrTexture.GetTextureDataSize());
Expand Down
5 changes: 1 addition & 4 deletions lib/src/Texture.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1214,11 +1214,8 @@ bool Texture::convert(Format format, Type type, Quality quality, Alpha alphaType
if (!imagesComplete() || !isFormatValid(format, type))
return false;

if (m_impl->colorSpace == ColorSpace::sRGB && (!hasNativeSRGB(format, type) ||
type != Type::UNorm))
{
if (m_impl->colorSpace == ColorSpace::sRGB && !hasNativeSRGB(format, type))
return false;
}

m_impl->format = format;
m_impl->type = type;
Expand Down

0 comments on commit 6bf549a

Please sign in to comment.