Skip to content

Commit

Permalink
Flesh out support for VK_FORMAT_R16G16_S10_5_NV. (#864)
Browse files Browse the repository at this point in the history
This includes a new round-trip test of vk2dfd and dfd2vk for all Vulkan
formats except multi-plane and modifications to both functions, or
rather their generators, to fix support for some formats, including
VK_FORMAT_R16G16_S10_5_NV and the YUV 4:2:2 single plane formats. Those
listed required changes to`interpretDFD`.

Fixes #859.

Co-authored-by: Andrew Garrard <[email protected]>
  • Loading branch information
MarkCallow and fluppeteer authored Mar 20, 2024
1 parent 3fe8c4d commit 42dfae5
Show file tree
Hide file tree
Showing 14 changed files with 516 additions and 183 deletions.
6 changes: 3 additions & 3 deletions ci_scripts/mkvkformatfiles
Original file line number Diff line number Diff line change
Expand Up @@ -126,10 +126,10 @@ $2 == "VK_HEADER_VERSION" {
if (enum_val+0 > max_std_format_enum) {
max_std_format_enum = enum_val+0;
}
if ($1 !~ /UNDEFINED/) {
format_name_list = format_name_list $1 "," ORS
}
if ($1 !~ /SCALED/) {
if ($1 !~ /UNDEFINED/) {
format_name_list = format_name_list $1 "," ORS
}
java = java " public static final int " $1 " = " enum_val ";" ORS
python = python " " $1 " = " enum_val ORS
}
Expand Down
8 changes: 8 additions & 0 deletions lib/dfdutils/createdfd.c
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,10 @@ static uint32_t setChannelFlags(uint32_t channel, enum VkSuffix suffix)
channel |= KHR_DF_SAMPLE_DATATYPE_LINEAR;
}
break;
case s_S10_5:
channel |=
KHR_DF_SAMPLE_DATATYPE_SIGNED;
break;
}
return channel;
}
Expand Down Expand Up @@ -158,6 +162,10 @@ static void writeSample(uint32_t *DFD, int sampleNo, int channel,
upper.f = 1.0f;
lower.f = 0.0f;
break;
case s_S10_5:
assert(bits == 16 && "Format with this suffix must be 16 bits per channel.");
upper.i = 32;
lower.i = ~upper.i + 1; // -32
}
sample[KHR_DF_SAMPLEWORD_SAMPLELOWER] = lower.i;
sample[KHR_DF_SAMPLEWORD_SAMPLEUPPER] = upper.i;
Expand Down
13 changes: 8 additions & 5 deletions lib/dfdutils/dfd.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,8 @@ enum VkSuffix {
s_SINT, /*!< Signed integer format. */
s_SFLOAT, /*!< Signed float format. */
s_UFLOAT, /*!< Unsigned float format. */
s_SRGB /*!< sRGB normalized format. */
s_SRGB, /*!< sRGB normalized format. */
s_S10_5 /*!< 2's complement fixed-point; 5 fractional bits. */
};

/** Compression scheme, in Vulkan terms. */
Expand Down Expand Up @@ -68,6 +69,7 @@ typedef unsigned int uint32_t;
#endif

uint32_t* vk2dfd(enum VkFormat format);
enum VkFormat dfd2vk(uint32_t* dfd);

/* Create a Data Format Descriptor for an unpacked format. */
uint32_t *createDFDUnpacked(int bigEndian, int numChannels, int bytes,
Expand Down Expand Up @@ -111,10 +113,11 @@ enum InterpretDFDResult {
i_SRGB_FORMAT_BIT = 1u << 2u, /*!< sRGB transfer function. */
i_NORMALIZED_FORMAT_BIT = 1u << 3u, /*!< Normalized (UNORM or SNORM). */
i_SIGNED_FORMAT_BIT = 1u << 4u, /*!< Format is signed. */
i_FLOAT_FORMAT_BIT = 1u << 5u, /*!< Format is floating point. */
i_COMPRESSED_FORMAT_BIT = 1u << 6u, /*!< Format is block compressed (422). */
i_YUVSDA_FORMAT_BIT = 1u << 7u, /*!< Color model is YUVSDA. */
i_UNSUPPORTED_ERROR_BIT = 1u << 8u, /*!< Format not successfully interpreted. */
i_FIXED_FORMAT_BIT = 1u << 5u, /*!< Format is a fixed-point representation. */
i_FLOAT_FORMAT_BIT = 1u << 6u, /*!< Format is floating point. */
i_COMPRESSED_FORMAT_BIT = 1u << 7u, /*!< Format is block compressed (422). */
i_YUVSDA_FORMAT_BIT = 1u << 8u, /*!< Color model is YUVSDA. */
i_UNSUPPORTED_ERROR_BIT = 1u << 9u, /*!< Format not successfully interpreted. */
/** "NONTRIVIAL_ENDIANNESS" means not big-endian, not little-endian
* (a channel has bits that are not consecutive in either order). **/
i_UNSUPPORTED_NONTRIVIAL_ENDIANNESS = i_UNSUPPORTED_ERROR_BIT,
Expand Down
241 changes: 148 additions & 93 deletions lib/dfdutils/dfd2vk.inl

Large diffs are not rendered by default.

113 changes: 84 additions & 29 deletions lib/dfdutils/interpretdfd.c
Original file line number Diff line number Diff line change
Expand Up @@ -29,18 +29,37 @@ static uint32_t bit_ceil(uint32_t x) {
* @~English
* @brief Interpret a Data Format Descriptor for a simple format.
*
* @param DFD Pointer to a Data Format Descriptor to interpret,
described as 32-bit words in native endianness.
Note that this is the whole descriptor, not just
the basic descriptor block.
* @param R Information about the decoded red channel or the depth channel, if any.
* @param G Information about the decoded green channel or the stencil channel, if any.
* @param B Information about the decoded blue channel, if any.
* @param A Information about the decoded alpha channel, if any.
* @param wordBytes Byte size of the channels (unpacked) or total size (packed).
* Handles "simple" cases that can be translated to things a GPU can access.
* For simplicity, it ignores the compressed formats, which are generally a
* single sample (and I believe are all defined to be little-endian in their
* in-memory layout, even if some documentation confuses this). Focuses on
* the layout and ignores sRGB except for reporting if that is the transfer
* function by way of a bit in the returned value.
*
* @param[in] DFD Pointer to a Data Format Descriptor to interpret,
* described as 32-bit words in native endianness.
* Note that this is the whole descriptor, not just
* the basic descriptor block.
* @param R[in,out] Pointer to struct to receive information about the decoded
* red channel, the Y channel, if YUV, or the depth channel,
* if any.
* @param G[in,out] Pointer to struct to receive information about the decoded
* green channel, the U (Cb) channel, if YUV, or the stencil
* channel, if any.
* @param B[in,out] Pointer to struct to receive information about the decoded
* blue channel, if any or the V (Cr) channel, if YUV.
* @param A[in,out] Pointer to struct to receive information about the decoded
* alpha channel, if any or the second Y channel, if YUV and
* any.
* @param wordBytes[in,out] Pointer to a uint32_t to receive the byte size of
* the channels (unpacked) or total size (packed).
*
* @return An enumerant describing the decoded value,
* or an error code in case of failure.
*
* The mapping of YUV channels to the parameter names used here is based on
* the channel ids in @c khr_df.h and is different from the convention used
* in format names in the Vulkan specification where G == Y, R = Cr and B = Cb.
**/
enum InterpretDFDResult interpretDFD(const uint32_t *DFD,
InterpretedDFDChannel *R,
Expand All @@ -49,14 +68,6 @@ enum InterpretDFDResult interpretDFD(const uint32_t *DFD,
InterpretedDFDChannel *A,
uint32_t *wordBytes)
{
/* We specifically handle "simple" cases that can be translated */
/* to things a GPU can access. For simplicity, we also ignore */
/* the compressed formats, which are generally a single sample */
/* (and I believe are all defined to be little-endian in their */
/* in-memory layout, even if some documentation confuses this). */
/* We also just worry about layout and ignore sRGB, since that's */
/* trivial to extract anyway. */

/* DFD points to the whole descriptor, not the basic descriptor block. */
/* Make everything else relative to the basic descriptor block. */
const uint32_t *BDFDB = DFD+1;
Expand All @@ -78,7 +89,7 @@ enum InterpretDFDResult interpretDFD(const uint32_t *DFD,

/* First rule out the multiple planes case (trivially) */
/* - that is, we check that only bytesPlane0 is non-zero. */
/* This means we don't handle YUV even if the API could. */
/* This means we don't handle multi-plane YUV, even if the API could. */
/* (We rely on KHR_DF_WORD_BYTESPLANE0..3 being the same and */
/* KHR_DF_WORD_BYTESPLANE4..7 being the same as a short cut.) */
if ((BDFDB[KHR_DF_WORD_BYTESPLANE0] & ~KHR_DF_MASK_BYTESPLANE0)
Expand All @@ -104,6 +115,8 @@ enum InterpretDFDResult interpretDFD(const uint32_t *DFD,
bool hasSigned = false;
bool hasFloat = false;
bool hasNormalized = false;
bool hasFixed = false;
khr_df_model_e model = KHR_DFDVAL(BDFDB, MODEL);

// Note: We're ignoring 9995, which is weird and worth special-casing
// rather than trying to generalise to all float formats.
Expand All @@ -116,13 +129,23 @@ enum InterpretDFDResult interpretDFD(const uint32_t *DFD,
// (i.e. set to the maximum bit value, and check min value) on
// the assumption that we're looking at a format which *came* from
// an API we can support.
const bool isNormalized = isFloat ?
*(float*) (void*) &BDFDB[KHR_DF_WORD_SAMPLESTART +
bool isFixed;
bool isNormalized;
if (isFloat) {
isNormalized = *(float*) (void*) &BDFDB[KHR_DF_WORD_SAMPLESTART +
KHR_DF_WORD_SAMPLEWORDS * i +
KHR_DF_SAMPLEWORD_SAMPLEUPPER] != 1.0f :
KHR_DFDSVAL(BDFDB, i, SAMPLEUPPER) != 1U;

KHR_DF_SAMPLEWORD_SAMPLEUPPER] != 1.0f;
isFixed = false;
} else {
uint32_t sampleUpper = KHR_DFDSVAL(BDFDB, i, SAMPLEUPPER);
uint32_t maxVal = 1U << KHR_DFDSVAL(BDFDB, i, BITLENGTH);
if (!isSigned) maxVal <<= 1;
maxVal--;
isFixed = 1U < sampleUpper && sampleUpper < maxVal;
isNormalized = !isFixed && sampleUpper != 1U;
}
hasSigned |= isSigned;
hasFixed |= isFixed;
hasFloat |= isFloat;
// By our definition the normalizedness of a single bit channel (like in RGBA 5:5:5:1)
// is ambiguous. Ignore these during normalized checks.
Expand All @@ -132,9 +155,10 @@ enum InterpretDFDResult interpretDFD(const uint32_t *DFD,
result |= hasSigned ? i_SIGNED_FORMAT_BIT : 0;
result |= hasFloat ? i_FLOAT_FORMAT_BIT : 0;
result |= hasNormalized ? i_NORMALIZED_FORMAT_BIT : 0;
result |= hasFixed ? i_FIXED_FORMAT_BIT : 0;

// Checks based on color model
if (KHR_DFDVAL(BDFDB, MODEL) == KHR_DF_MODEL_YUVSDA) {
if (model == KHR_DF_MODEL_YUVSDA) {
result |= i_NORMALIZED_FORMAT_BIT;
result |= i_COMPRESSED_FORMAT_BIT;
result |= i_YUVSDA_FORMAT_BIT;
Expand Down Expand Up @@ -165,7 +189,7 @@ enum InterpretDFDResult interpretDFD(const uint32_t *DFD,
*wordBytes = ((result & i_PACKED_FORMAT_BIT) ? 4 : 1) * bit_ceil(largestSampleSize) / 8;

} else if (KHR_DFDVAL(BDFDB, MODEL) == KHR_DF_MODEL_RGBSDA) {
/* We only pay attention to sRGB. */
/* Check if transfer is sRGB. */
if (KHR_DFDVAL(BDFDB, TRANSFER) == KHR_DF_TRANSFER_SRGB) result |= i_SRGB_FORMAT_BIT;

/* We only support samples at coordinate 0,0,0,0. */
Expand All @@ -175,7 +199,11 @@ enum InterpretDFDResult interpretDFD(const uint32_t *DFD,
if (KHR_DFDSVAL(BDFDB, sampleCounter, SAMPLEPOSITION_ALL))
return i_UNSUPPORTED_MULTIPLE_SAMPLE_LOCATIONS;
}
}

if (model == KHR_DF_MODEL_RGBSDA || model == KHR_DF_MODEL_YUVSDA) {
/* The values of the DEPTH and STENCIL tokens are the same for */
/* RGBSDA and YUVSDA. */
/* For Depth/Stencil formats mixed channels are allowed */
for (uint32_t sampleCounter = 0; sampleCounter < numSamples; ++sampleCounter) {
switch (KHR_DFDSVAL(BDFDB, sampleCounter, CHANNELID)) {
Expand Down Expand Up @@ -206,6 +234,9 @@ enum InterpretDFDResult interpretDFD(const uint32_t *DFD,
}
}

/* This all relies on the channel id values for RGB being equal to */
/* those for YUV. */

/* Remember: the canonical ordering of samples is to start with */
/* the lowest bit of the channel/location which touches bit 0 of */
/* the data, when the latter is concatenated in little-endian order, */
Expand Down Expand Up @@ -288,8 +319,20 @@ enum InterpretDFDResult interpretDFD(const uint32_t *DFD,
currentByteOffset = sampleByteOffset;
currentBitLength = sampleBitLength;
if (sampleChannelPtr->size) {
/* Uh-oh, we've seen this channel before. */
return i_UNSUPPORTED_NONTRIVIAL_ENDIANNESS;
if (model == KHR_DF_MODEL_YUVSDA && sampleChannel == KHR_DF_CHANNEL_YUVSDA_Y) {
if (sampleChannelPtr == R) {
/* We've got another Y channel. Record details in A. */
if (A->size == 0) {
sampleChannelPtr = A;
} else {
/* Uh-oh, we've already got a second Y or an alpha channel. */
return i_UNSUPPORTED_CHANNEL_TYPES;
}
}
} else {
/* Uh-oh, we've seen this channel before. */
return i_UNSUPPORTED_NONTRIVIAL_ENDIANNESS;
}
}
/* For now, record the bit offset in little-endian terms, */
/* because we may not know to reverse it yet. */
Expand Down Expand Up @@ -378,8 +421,20 @@ enum InterpretDFDResult interpretDFD(const uint32_t *DFD,
currentByteOffset = sampleByteOffset;
currentByteLength = sampleByteLength;
if (sampleChannelPtr->size) {
/* Uh-oh, we've seen this channel before. */
return i_UNSUPPORTED_NONTRIVIAL_ENDIANNESS;
if (model == KHR_DF_MODEL_YUVSDA && sampleChannel == KHR_DF_CHANNEL_YUVSDA_Y) {
if (sampleChannelPtr == R) {
/* We've got another Y channel. Record details in A. */
if (A->size == 0) {
sampleChannelPtr = A;
} else {
/* Uh-oh, we've already got a second Y or an alpha channel. */
return i_UNSUPPORTED_CHANNEL_TYPES;
}
}
} else {
/* Uh-oh, we've seen this channel before. */
return i_UNSUPPORTED_NONTRIVIAL_ENDIANNESS;
}
}
/* For now, record the byte offset in little-endian terms, */
/* because we may not know to reverse it yet. */
Expand Down
Loading

0 comments on commit 42dfae5

Please sign in to comment.