Path: blob/master/thirdparty/libktx/external/dfdutils/interpretdfd.c
9906 views
/* -*- tab-width: 4; -*- */1/* vi: set sw=2 ts=4 expandtab: */23/* Copyright 2019-2020 The Khronos Group Inc.4* SPDX-License-Identifier: Apache-2.05*/67/**8* @file9* @~English10* @brief Utility for interpreting a data format descriptor.11* @author Andrew Garrard12*/1314#include <stdint.h>15#include <stdio.h>16#include <KHR/khr_df.h>17#include "dfd.h"1819static uint32_t bit_ceil(uint32_t x) {20x -= 1;21for (uint32_t i = 0; i < sizeof(x) * 8; ++i)22if (1u << i > x)23return 1u << i;24return 0;25}2627/**28* @~English29* @brief Interpret a Data Format Descriptor for a simple format.30*31* Handles "simple" cases that can be translated to things a GPU can access.32* For simplicity, it ignores the compressed formats, which are generally a33* single sample (and I believe are all defined to be little-endian in their34* in-memory layout, even if some documentation confuses this). Focuses on35* the layout and ignores sRGB except for reporting if that is the transfer36* function by way of a bit in the returned value.37*38* @param[in] DFD Pointer to a Data Format Descriptor to interpret,39* described as 32-bit words in native endianness.40* Note that this is the whole descriptor, not just41* the basic descriptor block.42* @param R[in,out] Pointer to struct to receive information about the decoded43* red channel, the Y channel, if YUV, or the depth channel,44* if any.45* @param G[in,out] Pointer to struct to receive information about the decoded46* green channel, the U (Cb) channel, if YUV, or the stencil47* channel, if any.48* @param B[in,out] Pointer to struct to receive information about the decoded49* blue channel, if any or the V (Cr) channel, if YUV.50* @param A[in,out] Pointer to struct to receive information about the decoded51* alpha channel, if any or the second Y channel, if YUV and52* any.53* @param wordBytes[in,out] Pointer to a uint32_t to receive the byte size of54* the channels (unpacked) or total size (packed).55*56* @return An enumerant describing the decoded value,57* or an error code in case of failure.58*59* The mapping of YUV channels to the parameter names used here is based on60* the channel ids in @c khr_df.h and is different from the convention used61* in format names in the Vulkan specification where G == Y, R = Cr and B = Cb.62**/63enum InterpretDFDResult interpretDFD(const uint32_t *DFD,64InterpretedDFDChannel *R,65InterpretedDFDChannel *G,66InterpretedDFDChannel *B,67InterpretedDFDChannel *A,68uint32_t *wordBytes)69{70/* DFD points to the whole descriptor, not the basic descriptor block. */71/* Make everything else relative to the basic descriptor block. */72const uint32_t *BDFDB = DFD+1;7374uint32_t numSamples = KHR_DFDSAMPLECOUNT(BDFDB);75if (numSamples == 0)76return i_UNSUPPORTED_CHANNEL_TYPES;7778int determinedEndianness = 0;79enum InterpretDFDResult result = 0; /* Build this up incrementally. */8081bool isDepthStencil = false;8283/* Clear these so following code doesn't get confused. */84R->offset = R->size = 0;85G->offset = G->size = 0;86B->offset = B->size = 0;87A->offset = A->size = 0;8889/* First rule out the multiple planes case (trivially) */90/* - that is, we check that only bytesPlane0 is non-zero. */91/* This means we don't handle multi-plane YUV, even if the API could. */92/* (We rely on KHR_DF_WORD_BYTESPLANE0..3 being the same and */93/* KHR_DF_WORD_BYTESPLANE4..7 being the same as a short cut.) */94if ((BDFDB[KHR_DF_WORD_BYTESPLANE0] & ~KHR_DF_MASK_BYTESPLANE0)95|| BDFDB[KHR_DF_WORD_BYTESPLANE4]) return i_UNSUPPORTED_MULTIPLE_PLANES;9697/* If this is a packed format, we work out our offsets differently. */98/* We assume a packed format has channels that aren't byte-aligned. */99/* If we have a format in which every channel is byte-aligned *and* packed, */100/* we have the RGBA/ABGR ambiguity; we *probably* don't want the packed */101/* version in this case, and if hardware has to pack it and swizzle, */102/* that's up to the hardware to special-case. */103for (uint32_t sampleCounter = 0; sampleCounter < numSamples; ++sampleCounter) {104uint32_t offset = KHR_DFDSVAL(BDFDB, sampleCounter, BITOFFSET);105uint32_t length = KHR_DFDSVAL(BDFDB, sampleCounter, BITLENGTH) + 1;106if ((offset & 0x7U) || ((offset + length) & 0x7U)) {107result |= i_PACKED_FORMAT_BIT;108/* Once we're packed, we're packed, no need to keep checking. */109break;110}111}112113// Check data types.114bool hasSigned = false;115bool hasFloat = false;116bool hasNormalized = false;117bool hasFixed = false;118khr_df_model_e model = KHR_DFDVAL(BDFDB, MODEL);119120// Note: We're ignoring 9995, which is weird and worth special-casing121// rather than trying to generalise to all float formats.122for (uint32_t i = 0; i < numSamples; ++i) {123const bool isSigned = (KHR_DFDSVAL(BDFDB, i, QUALIFIERS) & KHR_DF_SAMPLE_DATATYPE_SIGNED) != 0;124const bool isFloat = (KHR_DFDSVAL(BDFDB, i, QUALIFIERS) & KHR_DF_SAMPLE_DATATYPE_FLOAT) != 0;125126// We define "unnormalized" as "sample_upper = 1" or "sample_upper = 1.0f".127// We don't check whether any non-1 normalization value is correct128// (i.e. set to the maximum bit value, and check min value) on129// the assumption that we're looking at a format which *came* from130// an API we can support.131bool isFixed;132bool isNormalized;133if (isFloat) {134isNormalized = *(float*) (void*) &BDFDB[KHR_DF_WORD_SAMPLESTART +135KHR_DF_WORD_SAMPLEWORDS * i +136KHR_DF_SAMPLEWORD_SAMPLEUPPER] != 1.0f;137isFixed = false;138} else {139uint32_t sampleUpper = KHR_DFDSVAL(BDFDB, i, SAMPLEUPPER);140uint32_t maxVal = 1U << KHR_DFDSVAL(BDFDB, i, BITLENGTH);141if (!isSigned) maxVal <<= 1;142maxVal--;143isFixed = 1U < sampleUpper && sampleUpper < maxVal;144isNormalized = !isFixed && sampleUpper != 1U;145}146hasSigned |= isSigned;147hasFixed |= isFixed;148hasFloat |= isFloat;149// By our definition the normalizedness of a single bit channel (like in RGBA 5:5:5:1)150// is ambiguous. Ignore these during normalized checks.151if (KHR_DFDSVAL(BDFDB, i, BITLENGTH) > 0)152hasNormalized |= isNormalized;153}154result |= hasSigned ? i_SIGNED_FORMAT_BIT : 0;155result |= hasFloat ? i_FLOAT_FORMAT_BIT : 0;156result |= hasNormalized ? i_NORMALIZED_FORMAT_BIT : 0;157result |= hasFixed ? i_FIXED_FORMAT_BIT : 0;158159// Checks based on color model160if (model == KHR_DF_MODEL_YUVSDA) {161result |= i_NORMALIZED_FORMAT_BIT;162result |= i_COMPRESSED_FORMAT_BIT;163result |= i_YUVSDA_FORMAT_BIT;164165for (uint32_t i = 0; i < numSamples; ++i) {166switch (KHR_DFDSVAL(BDFDB, i, CHANNELID)) {167case KHR_DF_CHANNEL_YUVSDA_Y:168case KHR_DF_CHANNEL_YUVSDA_U:169case KHR_DF_CHANNEL_YUVSDA_V:170case KHR_DF_CHANNEL_YUVSDA_A:171break;172case KHR_DF_CHANNEL_YUVSDA_DEPTH:173case KHR_DF_CHANNEL_YUVSDA_STENCIL:174isDepthStencil = true;175break;176default:177return i_UNSUPPORTED_CHANNEL_TYPES;178}179}180181// Determine wordBytes182uint32_t largestSampleSize = 0;183for (uint32_t i = 0; i < numSamples; ++i) {184uint32_t length = KHR_DFDSVAL(BDFDB, i, BITLENGTH) + 1;185if (largestSampleSize < length)186largestSampleSize = length;187}188*wordBytes = ((result & i_PACKED_FORMAT_BIT) ? 4 : 1) * bit_ceil(largestSampleSize) / 8;189190} else if (KHR_DFDVAL(BDFDB, MODEL) == KHR_DF_MODEL_RGBSDA) {191/* Check if transfer is sRGB. */192if (KHR_DFDVAL(BDFDB, TRANSFER) == KHR_DF_TRANSFER_SRGB) result |= i_SRGB_FORMAT_BIT;193194/* We only support samples at coordinate 0,0,0,0. */195/* (We could confirm this from texel_block_dimensions in 1.2, but */196/* the interpretation might change in later versions.) */197for (uint32_t sampleCounter = 0; sampleCounter < numSamples; ++sampleCounter) {198if (KHR_DFDSVAL(BDFDB, sampleCounter, SAMPLEPOSITION_ALL))199return i_UNSUPPORTED_MULTIPLE_SAMPLE_LOCATIONS;200}201}202203if (model == KHR_DF_MODEL_RGBSDA || model == KHR_DF_MODEL_YUVSDA) {204/* The values of the DEPTH and STENCIL tokens are the same for */205/* RGBSDA and YUVSDA. */206/* For Depth/Stencil formats mixed channels are allowed */207for (uint32_t sampleCounter = 0; sampleCounter < numSamples; ++sampleCounter) {208switch (KHR_DFDSVAL(BDFDB, sampleCounter, CHANNELID)) {209case KHR_DF_CHANNEL_RGBSDA_DEPTH:210case KHR_DF_CHANNEL_RGBSDA_STENCIL:211isDepthStencil = true;212break;213default:214break;215}216}217218// Check for mixed channels219if (!isDepthStencil) {220for (uint32_t i = 0; i < numSamples; ++i) {221const bool isSigned = (KHR_DFDSVAL(BDFDB, i, QUALIFIERS) & KHR_DF_SAMPLE_DATATYPE_SIGNED) != 0;222const bool isFloat = (KHR_DFDSVAL(BDFDB, i, QUALIFIERS) & KHR_DF_SAMPLE_DATATYPE_FLOAT) != 0;223224if (isSigned != hasSigned)225return i_UNSUPPORTED_MIXED_CHANNELS;226if (isFloat != hasFloat)227return i_UNSUPPORTED_MIXED_CHANNELS;228229// Note: We don't check for inconsistent normalization, because230// channels composed of multiple samples will have 0 in the231// lower/upper range. Single bit channels are also ambiguous.232// This heuristic should handle 64-bit integers, too.233}234}235236/* This all relies on the channel id values for RGB being equal to */237/* those for YUV. */238239/* Remember: the canonical ordering of samples is to start with */240/* the lowest bit of the channel/location which touches bit 0 of */241/* the data, when the latter is concatenated in little-endian order, */242/* and then progress until all the bits of that channel/location */243/* have been processed. Multiple channels sharing the same source */244/* bits are processed in channel ID order. (I should clarify this */245/* for partially-shared data, but it doesn't really matter so long */246/* as everything is consecutive, except to make things canonical.) */247/* Note: For standard formats we could determine big/little-endianness */248/* simply from whether the first sample starts in bit 0; technically */249/* it's possible to have a format with unaligned channels wherein the */250/* first channel starts at bit 0 and is one byte, yet other channels */251/* take more bytes or aren't aligned (e.g. D24S8), but this should be */252/* irrelevant for the formats that we support. */253if ((result & i_PACKED_FORMAT_BIT)) {254/* A packed format. */255uint32_t currentChannel = ~0U; /* Don't start matched. */256uint32_t currentBitOffset = 0;257uint32_t currentByteOffset = 0;258uint32_t currentBitLength = 0;259*wordBytes = (BDFDB[KHR_DF_WORD_BYTESPLANE0] & 0xFFU);260for (uint32_t sampleCounter = 0; sampleCounter < numSamples; ++sampleCounter) {261uint32_t sampleBitOffset = KHR_DFDSVAL(BDFDB, sampleCounter, BITOFFSET);262uint32_t sampleByteOffset = sampleBitOffset >> 3U;263/* The sample bitLength field stores the bit length - 1. */264uint32_t sampleBitLength = KHR_DFDSVAL(BDFDB, sampleCounter, BITLENGTH) + 1;265uint32_t sampleChannel = KHR_DFDSVAL(BDFDB, sampleCounter, CHANNELID);266InterpretedDFDChannel *sampleChannelPtr;267switch (sampleChannel) {268case KHR_DF_CHANNEL_RGBSDA_RED:269sampleChannelPtr = R;270break;271case KHR_DF_CHANNEL_RGBSDA_GREEN:272sampleChannelPtr = G;273break;274case KHR_DF_CHANNEL_RGBSDA_BLUE:275sampleChannelPtr = B;276break;277case KHR_DF_CHANNEL_RGBSDA_DEPTH:278sampleChannelPtr = R;279break;280case KHR_DF_CHANNEL_RGBSDA_STENCIL:281sampleChannelPtr = G;282break;283case KHR_DF_CHANNEL_RGBSDA_ALPHA:284sampleChannelPtr = A;285break;286default:287return i_UNSUPPORTED_CHANNEL_TYPES;288}289if (sampleChannel == currentChannel) {290/* Continuation of the same channel. */291/* Since a big (>32-bit) channel isn't "packed", */292/* this should only happen in big-endian, or if */293/* we have a wacky format that we won't support. */294if (sampleByteOffset == currentByteOffset - 1U && /* One byte earlier */295((currentBitOffset + currentBitLength) & 7U) == 0 && /* Already at the end of a byte */296(sampleBitOffset & 7U) == 0) { /* Start at the beginning of the byte */297/* All is good, continue big-endian. */298/* N.B. We shouldn't be here if we decided we were little-endian, */299/* so we don't bother to check that disagreement. */300result |= i_BIG_ENDIAN_FORMAT_BIT;301determinedEndianness = 1;302} else {303/* Oh dear. */304/* We could be little-endian, but not with any standard format. */305/* More likely we've got something weird that we can't support. */306return i_UNSUPPORTED_NONTRIVIAL_ENDIANNESS;307}308/* Remember where we are. */309currentBitOffset = sampleBitOffset;310currentByteOffset = sampleByteOffset;311currentBitLength = sampleBitLength;312/* Accumulate the bit length. */313sampleChannelPtr->size += sampleBitLength;314} else {315/* Everything is new. Hopefully. */316currentChannel = sampleChannel;317currentBitOffset = sampleBitOffset;318currentByteOffset = sampleByteOffset;319currentBitLength = sampleBitLength;320if (sampleChannelPtr->size) {321if (model == KHR_DF_MODEL_YUVSDA && sampleChannel == KHR_DF_CHANNEL_YUVSDA_Y) {322if (sampleChannelPtr == R) {323/* We've got another Y channel. Record details in A. */324if (A->size == 0) {325sampleChannelPtr = A;326} else {327/* Uh-oh, we've already got a second Y or an alpha channel. */328return i_UNSUPPORTED_CHANNEL_TYPES;329}330}331} else {332/* Uh-oh, we've seen this channel before. */333return i_UNSUPPORTED_NONTRIVIAL_ENDIANNESS;334}335}336/* For now, record the bit offset in little-endian terms, */337/* because we may not know to reverse it yet. */338sampleChannelPtr->offset = sampleBitOffset;339sampleChannelPtr->size = sampleBitLength;340}341}342if ((result & i_BIG_ENDIAN_FORMAT_BIT)) {343/* Our bit offsets to bit 0 of each channel are in little-endian terms. */344/* We need to do a byte swap to work out where they should be. */345/* We assume, for sanity, that byte sizes are a power of two for this. */346uint32_t offsetMask = (*wordBytes - 1U) << 3U;347R->offset ^= offsetMask;348G->offset ^= offsetMask;349B->offset ^= offsetMask;350A->offset ^= offsetMask;351}352} else {353/* Not a packed format. */354/* Everything is byte-aligned. */355/* Question is whether there multiple samples per channel. */356uint32_t currentChannel = ~0U; /* Don't start matched. */357uint32_t currentByteOffset = 0;358uint32_t currentByteLength = 0;359for (uint32_t sampleCounter = 0; sampleCounter < numSamples; ++sampleCounter) {360uint32_t sampleByteOffset = KHR_DFDSVAL(BDFDB, sampleCounter, BITOFFSET) >> 3U;361uint32_t sampleByteLength = (KHR_DFDSVAL(BDFDB, sampleCounter, BITLENGTH) + 1) >> 3U;362uint32_t sampleChannel = KHR_DFDSVAL(BDFDB, sampleCounter, CHANNELID);363InterpretedDFDChannel *sampleChannelPtr;364switch (sampleChannel) {365case KHR_DF_CHANNEL_RGBSDA_RED:366sampleChannelPtr = R;367break;368case KHR_DF_CHANNEL_RGBSDA_GREEN:369sampleChannelPtr = G;370break;371case KHR_DF_CHANNEL_RGBSDA_BLUE:372sampleChannelPtr = B;373break;374case KHR_DF_CHANNEL_RGBSDA_DEPTH:375sampleChannelPtr = R;376break;377case KHR_DF_CHANNEL_RGBSDA_STENCIL:378sampleChannelPtr = G;379break;380case KHR_DF_CHANNEL_RGBSDA_ALPHA:381sampleChannelPtr = A;382break;383default:384return i_UNSUPPORTED_CHANNEL_TYPES;385}386if (sampleChannel == currentChannel) {387/* Continuation of the same channel. */388/* Either big-endian, or little-endian with a very large channel. */389if (sampleByteOffset == currentByteOffset - 1) { /* One byte earlier */390if (determinedEndianness && !(result & i_BIG_ENDIAN_FORMAT_BIT)) {391return i_UNSUPPORTED_NONTRIVIAL_ENDIANNESS;392}393/* All is good, continue big-endian. */394result |= i_BIG_ENDIAN_FORMAT_BIT;395determinedEndianness = 1;396/* Update the start */397sampleChannelPtr->offset = sampleByteOffset;398} else if (sampleByteOffset == currentByteOffset + currentByteLength) {399if (determinedEndianness && (result & i_BIG_ENDIAN_FORMAT_BIT)) {400return i_UNSUPPORTED_NONTRIVIAL_ENDIANNESS;401}402/* All is good, continue little-endian. */403determinedEndianness = 1;404} else {405/* Oh dear. */406/* We could be little-endian, but not with any standard format. */407/* More likely we've got something weird that we can't support. */408return i_UNSUPPORTED_NONTRIVIAL_ENDIANNESS;409}410/* Remember where we are. */411currentByteOffset = sampleByteOffset;412currentByteLength = sampleByteLength;413/* Accumulate the byte length. */414sampleChannelPtr->size += sampleByteLength;415/* Assume these are all the same. */416*wordBytes = sampleChannelPtr->size;417} else {418/* Everything is new. Hopefully. */419currentChannel = sampleChannel;420currentByteOffset = sampleByteOffset;421currentByteLength = sampleByteLength;422if (sampleChannelPtr->size) {423if (model == KHR_DF_MODEL_YUVSDA && sampleChannel == KHR_DF_CHANNEL_YUVSDA_Y) {424if (sampleChannelPtr == R) {425/* We've got another Y channel. Record details in A. */426if (A->size == 0) {427sampleChannelPtr = A;428} else {429/* Uh-oh, we've already got a second Y or an alpha channel. */430return i_UNSUPPORTED_CHANNEL_TYPES;431}432}433} else {434/* Uh-oh, we've seen this channel before. */435return i_UNSUPPORTED_NONTRIVIAL_ENDIANNESS;436}437}438/* For now, record the byte offset in little-endian terms, */439/* because we may not know to reverse it yet. */440sampleChannelPtr->offset = sampleByteOffset;441sampleChannelPtr->size = sampleByteLength;442/* Assume these are all the same. */443*wordBytes = sampleByteLength;444}445}446}447} else {448return i_UNSUPPORTED_CHANNEL_TYPES;449}450451if (isDepthStencil) {452/* For Depth/Stencil formats wordBytes is determined by the required alignment of */453/* the larger channel. */454uint32_t largerSize = R->size > G->size ? R->size : G->size;455*wordBytes = bit_ceil(largerSize);456}457458return result;459}460461462