Skip to content

Commit

Permalink
Support for A8 and A1B5G5R5 formats (#785)
Browse files Browse the repository at this point in the history
The change contains various general enhancements
including performance optimizations for PNG exports.
  • Loading branch information
aqnuep authored Oct 20, 2023
1 parent 7332293 commit eeac620
Show file tree
Hide file tree
Showing 13 changed files with 184 additions and 137 deletions.
36 changes: 36 additions & 0 deletions lib/dfdutils/createdfd.c
Original file line number Diff line number Diff line change
Expand Up @@ -747,3 +747,39 @@ uint32_t *createDFDDepthStencil(int depthBits,
}
return DFD;
}

/**
* @~English
* @brief Create a Data Format Descriptor for an alpha-only format.
*
* @param bigEndian Set to 1 for big-endian byte ordering and
0 for little-endian byte ordering.
* @param bytes The number of bytes per channel.
* @param suffix Indicates the format suffix for the type.
*
* @return A data format descriptor in malloc'd data. The caller is responsible
* for freeing the descriptor.
**/
uint32_t *createDFDAlpha(int bigEndian, int bytes,
enum VkSuffix suffix) {
uint32_t *DFD;
int channel = 3; /* alpha channel */
if (bigEndian) {
int channelByte;
/* Number of samples = number of channels * bytes per channel */
DFD = writeHeader(bytes, bytes, suffix, i_COLOR);
/* Loop over the bytes that constitute a channel */
for (channelByte = 0; channelByte < bytes; ++channelByte) {
writeSample(DFD, channelByte, channel,
8, 8 * (bytes - channelByte - 1),
channelByte == bytes-1, channelByte == 0, suffix);
}
} else { /* Little-endian */
/* One sample per channel */
DFD = writeHeader(1, bytes, suffix, i_COLOR);
writeSample(DFD, 0, channel,
8 * bytes, 0,
1, 1, suffix);
}
return DFD;
}
4 changes: 4 additions & 0 deletions lib/dfdutils/dfd.h
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,10 @@ uint32_t *createDFDDepthStencil(int depthBits,
int stencilBits,
int sizeBytes);

/* Create a Data Format Descriptor for an alpha-only format */
uint32_t *createDFDAlpha(int bigEndian, int bytes,
enum VkSuffix suffix);

/** @brief Result of interpreting the data format descriptor. */
enum InterpretDFDResult {
i_LITTLE_ENDIAN_FORMAT_BIT = 0, /*!< Confirmed little-endian (default for 8bpc). */
Expand Down
4 changes: 4 additions & 0 deletions lib/dfdutils/dfd2vk.inl
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ if (KHR_DFDVAL(dfd + 1, MODEL) == KHR_DF_MODEL_RGBSDA) {
} else { /* Four channels, one-bit alpha */
if (B.offset == 0) return VK_FORMAT_A1R5G5B5_UNORM_PACK16;
if (B.offset == 1) return VK_FORMAT_R5G5B5A1_UNORM_PACK16;
if (B.offset == 10) return VK_FORMAT_A1B5G5R5_UNORM_PACK16_KHR;
return VK_FORMAT_B5G5R5A1_UNORM_PACK16;
}
} else if (wordBytes == 4) { /* PACK32 */
Expand All @@ -74,6 +75,9 @@ if (KHR_DFDVAL(dfd + 1, MODEL) == KHR_DF_MODEL_RGBSDA) {
}
} else { /* Not a packed format */
if (wordBytes == 1) {
if (A.size > 8 && R.size == 0 && G.size == 0 && B.size == 0 && (r & i_NORMALIZED_FORMAT_BIT) && !(r & i_SIGNED_FORMAT_BIT)) {
return VK_FORMAT_A8_UNORM_KHR;
}
if (A.size > 0) { /* 4 channels */
if (R.offset == 0) { /* RGBA */
if ((r & i_SRGB_FORMAT_BIT)) return VK_FORMAT_R8G8B8A8_SRGB;
Expand Down
6 changes: 6 additions & 0 deletions lib/dfdutils/makedfd2vk.pl
Original file line number Diff line number Diff line change
Expand Up @@ -294,6 +294,7 @@ sub checkSuffices {
} else { /* Four channels, one-bit alpha */
if (B.offset == 0) return VK_FORMAT_A1R5G5B5_UNORM_PACK16;
if (B.offset == 1) return VK_FORMAT_R5G5B5A1_UNORM_PACK16;
if (B.offset == 10) return VK_FORMAT_A1B5G5R5_UNORM_PACK16_KHR;
return VK_FORMAT_B5G5R5A1_UNORM_PACK16;
}
} else if (wordBytes == 4) { /* PACK32 */
Expand All @@ -315,6 +316,11 @@ sub checkSuffices {
for ($byteSize = 1; $byteSize <= 8; $byteSize <<= 1) {
if ($byteSize == 1) {
print " if (wordBytes == $byteSize) {\n";

# Handle the single alpha-only format (unfortunately, the rest of the script could not handle this)
print " if (A.size > 8 && R.size == 0 && G.size == 0 && B.size == 0 && (r & i_NORMALIZED_FORMAT_BIT) && !(r & i_SIGNED_FORMAT_BIT)) {\n";
print " return VK_FORMAT_A8_UNORM_KHR;\n";
print " }\n";
} else {
print " } else if (wordBytes == $byteSize) {\n";
}
Expand Down
9 changes: 9 additions & 0 deletions lib/dfdutils/makevk2dfd.pl
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,15 @@
# If we're not packed, do we have a simple RGBA channel size list with a suffix?
# N.B. We don't want to pick up downsampled or planar formats, which have more _-separated fields
# - "$" matches the end of the format identifier
} elsif ($format =~ m/VK_FORMAT_A([0-9]+)_([^_]+)(_[^_]+)?$/) {
# Special case for alpha-only formats

# Extract our "suffix" (e.g. "UNORM") and bytes per channel
$suffix = $2;
$bytesPerChannel = $1 / 8;

# Output the case entry
print "case $format: return createDFDAlpha($bigEndian, $bytesPerChannel, s_$suffix);\n";
} elsif ($format =~ m/VK_FORMAT_([RGBA0-9]+)_([^_]+)$/) {

# Extract our "channels" (e.g. "B8G8R8") and "suffix" (e.g. "UNORM")
Expand Down
5 changes: 5 additions & 0 deletions lib/dfdutils/vk2dfd.inl
Original file line number Diff line number Diff line change
Expand Up @@ -400,3 +400,8 @@ case VK_FORMAT_A4B4G4R4_UNORM_PACK16_EXT: {
int channels[] = {0,1,2,3}; int bits[] = {4,4,4,4};
return createDFDPacked(0, 4, bits, channels, s_UNORM);
}
case VK_FORMAT_A1B5G5R5_UNORM_PACK16_KHR: {
int channels[] = {0,1,2,3}; int bits[] = {5,5,5,1};
return createDFDPacked(0, 4, bits, channels, s_UNORM);
}
case VK_FORMAT_A8_UNORM_KHR: return createDFDAlpha(0, 1, s_UNORM);
2 changes: 2 additions & 0 deletions lib/dfdutils/vulkan/vulkan_core.h
Original file line number Diff line number Diff line change
Expand Up @@ -1124,6 +1124,8 @@ typedef enum VkFormat {
VK_FORMAT_G16_B16_R16_3PLANE_422_UNORM_KHR = VK_FORMAT_G16_B16_R16_3PLANE_422_UNORM,
VK_FORMAT_G16_B16R16_2PLANE_422_UNORM_KHR = VK_FORMAT_G16_B16R16_2PLANE_422_UNORM,
VK_FORMAT_G16_B16_R16_3PLANE_444_UNORM_KHR = VK_FORMAT_G16_B16_R16_3PLANE_444_UNORM,
VK_FORMAT_A1B5G5R5_UNORM_PACK16_KHR = 1000470000,
VK_FORMAT_A8_UNORM_KHR = 1000470001,
VK_FORMAT_MAX_ENUM = 0x7FFFFFFF
} VkFormat;

Expand Down
2 changes: 2 additions & 0 deletions lib/vkformat_check.c
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,8 @@ isValidFormat(VkFormat format)
case VK_FORMAT_ASTC_6x6x6_SFLOAT_BLOCK_EXT:
case VK_FORMAT_A4R4G4B4_UNORM_PACK16_EXT:
case VK_FORMAT_A4B4G4R4_UNORM_PACK16_EXT:
case VK_FORMAT_A1B5G5R5_UNORM_PACK16_KHR:
case VK_FORMAT_A8_UNORM_KHR:
return true;
default:
return false;
Expand Down
2 changes: 2 additions & 0 deletions lib/vkformat_enum.h
Original file line number Diff line number Diff line change
Expand Up @@ -292,6 +292,8 @@ typedef enum VkFormat {
VK_FORMAT_ASTC_6x6x6_SFLOAT_BLOCK_EXT = 1000288029,
VK_FORMAT_A4R4G4B4_UNORM_PACK16_EXT = 1000340000,
VK_FORMAT_A4B4G4R4_UNORM_PACK16_EXT = 1000340001,
VK_FORMAT_A1B5G5R5_UNORM_PACK16_KHR = 1000470000,
VK_FORMAT_A8_UNORM_KHR = 1000470001,
VK_FORMAT_MAX_ENUM = 0x7FFFFFFF
} VkFormat;

Expand Down
8 changes: 8 additions & 0 deletions lib/vkformat_str.c
Original file line number Diff line number Diff line change
Expand Up @@ -565,6 +565,10 @@ vkFormatString(VkFormat format)
return "VK_FORMAT_A4R4G4B4_UNORM_PACK16_EXT";
case VK_FORMAT_A4B4G4R4_UNORM_PACK16_EXT:
return "VK_FORMAT_A4B4G4R4_UNORM_PACK16_EXT";
case VK_FORMAT_A1B5G5R5_UNORM_PACK16_KHR:
return "VK_FORMAT_A1B5G5R5_UNORM_PACK16_KHR";
case VK_FORMAT_A8_UNORM_KHR:
return "VK_FORMAT_A8_UNORM_KHR";
default:
return "VK_UNKNOWN_FORMAT";
}
Expand Down Expand Up @@ -1152,5 +1156,9 @@ stringToVkFormat(const char* str)
return VK_FORMAT_A4R4G4B4_UNORM_PACK16_EXT;
if (ktx_strcasecmp(str, "A4B4G4R4_UNORM_PACK16_EXT") == 0)
return VK_FORMAT_A4B4G4R4_UNORM_PACK16_EXT;
if (ktx_strcasecmp(str, "A1B5G5R5_UNORM_PACK16_KHR") == 0)
return VK_FORMAT_A1B5G5R5_UNORM_PACK16_KHR;
if (ktx_strcasecmp(str, "A8_UNORM_KHR") == 0)
return VK_FORMAT_A8_UNORM_KHR;
return VK_FORMAT_UNDEFINED;
}
2 changes: 1 addition & 1 deletion tests/cts
Submodule cts updated 112 files
111 changes: 35 additions & 76 deletions tools/ktx/command_create.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -429,6 +429,8 @@ struct OptionsCreate {
VK_FORMAT_D16_UNORM_S8_UINT,
VK_FORMAT_D24_UNORM_S8_UINT,
VK_FORMAT_D32_SFLOAT_S8_UINT,
VK_FORMAT_A8_UNORM_KHR,
VK_FORMAT_A1B5G5R5_UNORM_PACK16_KHR,
};

if (isProhibitedFormat(vkFormat))
Expand Down Expand Up @@ -841,7 +843,7 @@ class CommandCreate : public Command {
uint32_t numMipLevels, uint32_t layerIndex, uint32_t faceIndex, uint32_t depthSliceIndex);

[[nodiscard]] std::string readRawFile(const std::filesystem::path& filepath);
[[nodiscard]] std::unique_ptr<Image> loadInputImage(ImageInput& inputImageFile, const ImageSpec& target);
[[nodiscard]] std::unique_ptr<Image> loadInputImage(ImageInput& inputImageFile);
std::vector<uint8_t> convert(const std::unique_ptr<Image>& image, VkFormat format, ImageInput& inputFile);

std::unique_ptr<const ColorPrimaries> createColorPrimaries(khr_df_primaries_e primaries) const;
Expand Down Expand Up @@ -1159,7 +1161,7 @@ void CommandCreate::executeCreate() {
fatal(rc::INVALID_FILE, "Input image \"{}\" with size {}x{} does not match expected size {}x{} for level {}.",
fmtInFile(inputFilepath), inputImageFile->spec().width(), inputImageFile->spec().height(), imageWidth, imageHeight, levelIndex);

auto image = loadInputImage(*inputImageFile, target);
auto image = loadInputImage(*inputImageFile);

if (colorSpaceInfo.dstTransferFunction != nullptr) {
assert(colorSpaceInfo.srcTransferFunction != nullptr);
Expand Down Expand Up @@ -1296,98 +1298,46 @@ void CommandCreate::compress(KTXTexture2& texture, const OptionsCompress& opts)

// -------------------------------------------------------------------------------------------------

std::unique_ptr<Image> CommandCreate::loadInputImage(ImageInput& inputImageFile, const ImageSpec& target) {
std::unique_ptr<Image> CommandCreate::loadInputImage(ImageInput& inputImageFile) {
std::unique_ptr<Image> image = nullptr;

const auto& inputFormat = inputImageFile.spec().format();
const auto width = inputImageFile.spec().width();
const auto height = inputImageFile.spec().height();

const uint32_t channelCount = std::min(4u, std::max(target.format().channelCount(), inputFormat.channelCount()));
const auto inputBitLength = inputFormat.largestChannelBitLength();
const auto requestBitLength = std::max(imageio::bit_ceil(inputBitLength), 8u);
FormatDescriptor loadFormat;

assert(channelCount >= 1 && channelCount <= 4);
switch (inputImageFile.formatType()) {
case ImageInputFormatType::exr_uint:
image = std::make_unique<rgba32image>(width, height);
loadFormat = createFormatDescriptor(VK_FORMAT_R32G32B32A32_UINT, *this);
break;

if (inputImageFile.formatType() == ImageInputFormatType::exr_float) {
switch (channelCount) {
case 1:
image = std::make_unique<r32fimage>(width, height);
loadFormat = createFormatDescriptor(VK_FORMAT_R32_SFLOAT, *this);
break;
case 2:
image = std::make_unique<rg32fimage>(width, height);
loadFormat = createFormatDescriptor(VK_FORMAT_R32G32_SFLOAT, *this);
break;
case 3:
image = std::make_unique<rgb32fimage>(width, height);
loadFormat = createFormatDescriptor(VK_FORMAT_R32G32B32_SFLOAT, *this);
break;
case 4:
image = std::make_unique<rgba32fimage>(width, height);
loadFormat = createFormatDescriptor(VK_FORMAT_R32G32B32A32_SFLOAT, *this);
break;
}
} else if (requestBitLength == 8) {
switch (channelCount) {
case 1:
image = std::make_unique<r8image>(width, height);
loadFormat = createFormatDescriptor(VK_FORMAT_R8_UNORM, *this);
break;
case 2:
image = std::make_unique<rg8image>(width, height);
loadFormat = createFormatDescriptor(VK_FORMAT_R8G8_UNORM, *this);
break;
case 3:
image = std::make_unique<rgb8image>(width, height);
loadFormat = createFormatDescriptor(VK_FORMAT_R8G8B8_UNORM, *this);
break;
case 4:
case ImageInputFormatType::exr_float:
image = std::make_unique<rgba32fimage>(width, height);
loadFormat = createFormatDescriptor(VK_FORMAT_R32G32B32A32_SFLOAT, *this);
break;

case ImageInputFormatType::npbm: [[fallthrough]];
case ImageInputFormatType::jpg: [[fallthrough]];
case ImageInputFormatType::png_l: [[fallthrough]];
case ImageInputFormatType::png_la: [[fallthrough]];
case ImageInputFormatType::png_rgb: [[fallthrough]];
case ImageInputFormatType::png_rgba:
if (requestBitLength == 8) {
image = std::make_unique<rgba8image>(width, height);
loadFormat = createFormatDescriptor(VK_FORMAT_R8G8B8A8_UNORM, *this);
break;
}
} else if (requestBitLength == 16) {
switch (channelCount) {
case 1:
image = std::make_unique<r16image>(width, height);
loadFormat = createFormatDescriptor(VK_FORMAT_R16_UNORM, *this);
break;
case 2:
image = std::make_unique<rg16image>(width, height);
loadFormat = createFormatDescriptor(VK_FORMAT_R16G16_UNORM, *this);
break;
case 3:
image = std::make_unique<rgb16image>(width, height);
loadFormat = createFormatDescriptor(VK_FORMAT_R16G16B16_UNORM, *this);
break;
case 4:
} else if (requestBitLength == 16) {
image = std::make_unique<rgba16image>(width, height);
loadFormat = createFormatDescriptor(VK_FORMAT_R16G16B16A16_UNORM, *this);
break;
} else {
fatal(rc::INVALID_FILE, "Unsupported format with {}-bit channels.", requestBitLength);
}
} else if (requestBitLength == 32) {
switch (channelCount) {
case 1:
image = std::make_unique<r32image>(width, height);
loadFormat = createFormatDescriptor(VK_FORMAT_R32_UINT, *this);
break;
case 2:
image = std::make_unique<rg32image>(width, height);
loadFormat = createFormatDescriptor(VK_FORMAT_R32G32_UINT, *this);
break;
case 3:
image = std::make_unique<rgb32image>(width, height);
loadFormat = createFormatDescriptor(VK_FORMAT_R32G32B32_UINT, *this);
break;
case 4:
image = std::make_unique<rgba32image>(width, height);
loadFormat = createFormatDescriptor(VK_FORMAT_R32G32B32A32_UINT, *this);
break;
}
} else {
fatal(rc::INVALID_FILE, "Unsupported format with {}-bit channels.", requestBitLength);
break;
}

inputImageFile.readImage(static_cast<uint8_t*>(*image), image->getByteCount(), 0, 0, loadFormat);
Expand Down Expand Up @@ -1653,6 +1603,9 @@ std::vector<uint8_t> CommandCreate::convert(const std::unique_ptr<Image>& image,
case VK_FORMAT_A1R5G5B5_UNORM_PACK16:
requireUNORM(8);
return convertUNORMPacked(image, 1, 5, 5, 5, "argb");
case VK_FORMAT_A1B5G5R5_UNORM_PACK16_KHR:
requireUNORM(8);
return convertUNORMPacked(image, 1, 5, 5, 5, "abgr");
case VK_FORMAT_A4R4G4B4_UNORM_PACK16_EXT:
requireUNORM(8);
return convertUNORMPacked(image, 4, 4, 4, 4, "argb");
Expand Down Expand Up @@ -1862,6 +1815,12 @@ std::vector<uint8_t> CommandCreate::convert(const std::unique_ptr<Image>& image,
fatal(rc::INVALID_ARGUMENTS, "Unsupported format for non-raw create: {}.", toString(options.vkFormat));
break;

case VK_FORMAT_A8_UNORM_KHR:
// Special case for alpha-only
requireUNORM(8);
return convertUNORM<r8image>(image, "a000");
break;

// Not supported

default:
Expand Down
Loading

0 comments on commit eeac620

Please sign in to comment.