Skip to content

Commit

Permalink
Fix creating 3D textures and add KTXwriterScParams support to transco…
Browse files Browse the repository at this point in the history
…de (#833)

Fixes #826 and addresses the transcode related notes of #829.
  • Loading branch information
aqnuep authored Jan 2, 2024
1 parent 5b7ee1c commit 01d220c
Show file tree
Hide file tree
Showing 3 changed files with 22 additions and 10 deletions.
20 changes: 11 additions & 9 deletions tools/ktx/command_create.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -827,7 +827,7 @@ class CommandCreate : public Command {
uint32_t numLevels = 0;
uint32_t numLayers = 0;
uint32_t numFaces = 0;
uint32_t numBaseDepths = 0;
uint32_t baseDepth = 0;

public:
virtual int main(int argc, char* argv[]) override;
Expand Down Expand Up @@ -888,22 +888,22 @@ void CommandCreate::processOptions(cxxopts::Options& opts, cxxopts::ParseResult&
numLevels = options.levels.value_or(1);
numLayers = options.layers.value_or(1);
numFaces = options.cubemap ? 6 : 1;
numBaseDepths = options.depth.value_or(1u);
baseDepth = options.depth.value_or(1u);

const auto blockSizeZ = isFormat3DBlockCompressed(options.vkFormat) ?
createFormatDescriptor(options.vkFormat, *this).basic.texelBlockDimension2 + 1u : 1u;
uint32_t expectedInputImages = 0;
for (uint32_t i = 0; i < (options.mipmapGenerate ? 1 : numLevels); ++i)
// If --generate-mipmap is set the input only contains the base level images
expectedInputImages += numLayers * numFaces * ceil_div(std::max(numBaseDepths >> i, 1u), blockSizeZ);
expectedInputImages += numLayers * numFaces * ceil_div(std::max(baseDepth >> i, 1u), blockSizeZ);
if (options.inputFilepaths.size() != expectedInputImages) {
fatal_usage("Too {} input image for {} level{}, {} layer, {} face and {} depth. Provided {} but expected {}.",
options.inputFilepaths.size() > expectedInputImages ? "many" : "few",
numLevels,
options.mipmapGenerate ? " (mips generated)" : "",
numLayers,
numFaces,
numBaseDepths,
baseDepth,
options.inputFilepaths.size(), expectedInputImages);
}

Expand Down Expand Up @@ -1041,7 +1041,7 @@ void CommandCreate::foreachImage(const FormatDescriptor& format, F&& func) {
auto inputFileIt = options.inputFilepaths.begin();

for (uint32_t levelIndex = 0; levelIndex < (options.mipmapGenerate ? 1 : numLevels); ++levelIndex) {
const auto numDepthSlices = ceil_div(std::max(numBaseDepths >> levelIndex, 1u), format.basic.texelBlockDimension2 + 1u);
const auto numDepthSlices = ceil_div(std::max(baseDepth >> levelIndex, 1u), format.basic.texelBlockDimension2 + 1u);
for (uint32_t layerIndex = 0; layerIndex < numLayers; ++layerIndex) {
for (uint32_t faceIndex = 0; faceIndex < numFaces; ++faceIndex) {
for (uint32_t depthSliceIndex = 0; depthSliceIndex < numDepthSlices; ++depthSliceIndex) {
Expand Down Expand Up @@ -1136,7 +1136,7 @@ void CommandCreate::executeCreate() {
target = ImageSpec{
inputImageFile->spec().width(),
inputImageFile->spec().height(),
inputImageFile->spec().depth(),
options.depth.value_or(1u),
options.formatDesc};

ColorSpaceInfo colorSpaceInfo{};
Expand All @@ -1151,11 +1151,11 @@ void CommandCreate::executeCreate() {
fatal(rc::INVALID_FILE, "For --1d textures the input image height must be 1, but for \"{}\" it was {}.",
fmtInFile(inputFilepath), target.height());

const auto maxDimension = std::max(target.width(), std::max(target.height(), numBaseDepths));
const auto maxDimension = std::max(target.width(), std::max(target.height(), baseDepth));
const auto maxLevels = log2(maxDimension) + 1;
if (options.levels.value_or(1) > maxLevels)
fatal_usage("Requested {} levels is too many. With input image \"{}\" sized {}x{} and depth {} the texture can only have {} levels at most.",
options.levels.value_or(1), fmtInFile(inputFilepath), target.width(), target.height(), numBaseDepths, maxLevels);
options.levels.value_or(1), fmtInFile(inputFilepath), target.width(), target.height(), baseDepth, maxLevels);

if (options.encodeASTC)
selectASTCMode(inputImageFile->spec().format().largestChannelBitLength());
Expand Down Expand Up @@ -1226,7 +1226,7 @@ void CommandCreate::executeCreate() {
assert(ret == KTX_SUCCESS && "Internal error"); (void) ret;

if (options.mipmapGenerate) {
const auto maxDimension = std::max(target.width(), std::max(target.height(), numBaseDepths));
const auto maxDimension = std::max(target.width(), std::max(target.height(), baseDepth));
const auto maxLevels = log2(maxDimension) + 1;
uint32_t numMipLevels = options.levels.value_or(maxLevels);
generateMipLevels(texture, std::move(image), *inputImageFile, numMipLevels, layerIndex, faceIndex, depthSliceIndex);
Expand Down Expand Up @@ -1848,6 +1848,8 @@ KTXTexture2 CommandCreate::createTexture(const ImageSpec& target) {
ktxTextureCreateInfo createInfo;
std::memset(&createInfo, 0, sizeof(createInfo));

assert(target.depth() == baseDepth);

createInfo.vkFormat = options.vkFormat;
createInfo.numFaces = numFaces;
createInfo.numLayers = numLayers;
Expand Down
10 changes: 10 additions & 0 deletions tools/ktx/command_transcode.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,16 @@ void CommandTranscode::executeTranscode() {
static_cast<uint32_t>(writer.size() + 1), // +1 to include the \0
writer.c_str());

// Add KTXwriterScParams metadata if supercompression was used
const auto writerScParams = options.compressOptions;
if (writerScParams.size() > 0) {
// Options always contain a leading space
assert(writerScParams[0] == ' ');
ktxHashList_AddKVPair(&texture->kvDataHead, KTX_WRITER_SCPARAMS_KEY,
static_cast<uint32_t>(writerScParams.size()),
writerScParams.c_str() + 1); // +1 to exclude leading space
}

// Save output file
const auto outputPath = std::filesystem::path(DecodeUTF8Path(options.outputFilepath));
if (outputPath.has_parent_path())
Expand Down

0 comments on commit 01d220c

Please sign in to comment.