interpretdfd.c 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346
  1. /* -*- tab-width: 4; -*- */
  2. /* vi: set sw=2 ts=4 expandtab: */
  3. /* Copyright 2019-2020 The Khronos Group Inc.
  4. * SPDX-License-Identifier: Apache-2.0
  5. */
  6. /**
  7. * @file
  8. * @~English
  9. * @brief Utility for interpreting a data format descriptor.
  10. * @author Andrew Garrard
  11. */
  12. #include <stdint.h>
  13. #include <stdio.h>
  14. #include <KHR/khr_df.h>
  15. #include "dfd.h"
  16. /**
  17. * @~English
  18. * @brief Interpret a Data Format Descriptor for a simple format.
  19. *
  20. * @param DFD Pointer to a Data Format Descriptor to interpret,
  21. described as 32-bit words in native endianness.
  22. Note that this is the whole descriptor, not just
  23. the basic descriptor block.
  24. * @param R Information about the decoded red channel, if any.
  25. * @param G Information about the decoded green channel, if any.
  26. * @param B Information about the decoded blue channel, if any.
  27. * @param A Information about the decoded alpha channel, if any.
  28. * @param wordBytes Byte size of the channels (unpacked) or total size (packed).
  29. *
  30. * @return An enumerant describing the decoded value,
  31. * or an error code in case of failure.
  32. **/
  33. enum InterpretDFDResult interpretDFD(const uint32_t *DFD,
  34. InterpretedDFDChannel *R,
  35. InterpretedDFDChannel *G,
  36. InterpretedDFDChannel *B,
  37. InterpretedDFDChannel *A,
  38. uint32_t *wordBytes)
  39. {
  40. /* We specifically handle "simple" cases that can be translated */
  41. /* to things a GPU can access. For simplicity, we also ignore */
  42. /* the compressed formats, which are generally a single sample */
  43. /* (and I believe are all defined to be little-endian in their */
  44. /* in-memory layout, even if some documentation confuses this). */
  45. /* We also just worry about layout and ignore sRGB, since that's */
  46. /* trivial to extract anyway. */
  47. /* DFD points to the whole descriptor, not the basic descriptor block. */
  48. /* Make everything else relative to the basic descriptor block. */
  49. const uint32_t *BDFDB = DFD+1;
  50. uint32_t numSamples = KHR_DFDSAMPLECOUNT(BDFDB);
  51. uint32_t sampleCounter;
  52. int determinedEndianness = 0;
  53. int determinedNormalizedness = 0;
  54. int determinedSignedness = 0;
  55. int determinedFloatness = 0;
  56. enum InterpretDFDResult result = 0; /* Build this up incrementally. */
  57. /* Clear these so following code doesn't get confused. */
  58. R->offset = R->size = 0;
  59. G->offset = G->size = 0;
  60. B->offset = B->size = 0;
  61. A->offset = A->size = 0;
  62. /* First rule out the multiple planes case (trivially) */
  63. /* - that is, we check that only bytesPlane0 is non-zero. */
  64. /* This means we don't handle YUV even if the API could. */
  65. /* (We rely on KHR_DF_WORD_BYTESPLANE0..3 being the same and */
  66. /* KHR_DF_WORD_BYTESPLANE4..7 being the same as a short cut.) */
  67. if ((BDFDB[KHR_DF_WORD_BYTESPLANE0] & ~KHR_DF_MASK_BYTESPLANE0)
  68. || BDFDB[KHR_DF_WORD_BYTESPLANE4]) return i_UNSUPPORTED_MULTIPLE_PLANES;
  69. /* Only support the RGB color model. */
  70. /* We could expand this to allow "UNSPECIFIED" as well. */
  71. if (KHR_DFDVAL(BDFDB, MODEL) != KHR_DF_MODEL_RGBSDA) return i_UNSUPPORTED_CHANNEL_TYPES;
  72. /* We only pay attention to sRGB. */
  73. if (KHR_DFDVAL(BDFDB, TRANSFER) == KHR_DF_TRANSFER_SRGB) result |= i_SRGB_FORMAT_BIT;
  74. /* We only support samples at coordinate 0,0,0,0. */
  75. /* (We could confirm this from texel_block_dimensions in 1.2, but */
  76. /* the interpretation might change in later versions.) */
  77. for (sampleCounter = 0; sampleCounter < numSamples; ++sampleCounter) {
  78. if (KHR_DFDSVAL(BDFDB, sampleCounter, SAMPLEPOSITION_ALL))
  79. return i_UNSUPPORTED_MULTIPLE_SAMPLE_LOCATIONS;
  80. }
  81. /* Set flags and check for consistency. */
  82. for (sampleCounter = 0; sampleCounter < numSamples; ++sampleCounter) {
  83. /* Note: We're ignoring 9995, which is weird and worth special-casing */
  84. /* rather than trying to generalise to all float formats. */
  85. if (!determinedFloatness) {
  86. if (KHR_DFDSVAL(BDFDB, sampleCounter, QUALIFIERS)
  87. & KHR_DF_SAMPLE_DATATYPE_FLOAT) {
  88. result |= i_FLOAT_FORMAT_BIT;
  89. determinedFloatness = 1;
  90. }
  91. } else {
  92. /* Check whether we disagree with our predetermined floatness. */
  93. /* Note that this could justifiably happen with (say) D24S8. */
  94. if (KHR_DFDSVAL(BDFDB, sampleCounter, QUALIFIERS)
  95. & KHR_DF_SAMPLE_DATATYPE_FLOAT) {
  96. if (!(result & i_FLOAT_FORMAT_BIT)) return i_UNSUPPORTED_MIXED_CHANNELS;
  97. } else {
  98. if ((result & i_FLOAT_FORMAT_BIT)) return i_UNSUPPORTED_MIXED_CHANNELS;
  99. }
  100. }
  101. if (!determinedSignedness) {
  102. if (KHR_DFDSVAL(BDFDB, sampleCounter, QUALIFIERS)
  103. & KHR_DF_SAMPLE_DATATYPE_SIGNED) {
  104. result |= i_SIGNED_FORMAT_BIT;
  105. determinedSignedness = 1;
  106. }
  107. } else {
  108. /* Check whether we disagree with our predetermined signedness. */
  109. if (KHR_DFDSVAL(BDFDB, sampleCounter, QUALIFIERS)
  110. & KHR_DF_SAMPLE_DATATYPE_SIGNED) {
  111. if (!(result & i_SIGNED_FORMAT_BIT)) return i_UNSUPPORTED_MIXED_CHANNELS;
  112. } else {
  113. if ((result & i_SIGNED_FORMAT_BIT)) return i_UNSUPPORTED_MIXED_CHANNELS;
  114. }
  115. }
  116. /* We define "unnormalized" as "sample_upper = 1". */
  117. /* We don't check whether any non-1 normalization value is correct */
  118. /* (i.e. set to the maximum bit value, and check min value) on */
  119. /* the assumption that we're looking at a format which *came* from */
  120. /* an API we can support. */
  121. if (!determinedNormalizedness) {
  122. /* The ambiguity here is if the bottom bit is a single-bit value, */
  123. /* as in RGBA 5:5:5:1, so we defer the decision if the channel only has one bit. */
  124. if (KHR_DFDSVAL(BDFDB, sampleCounter, BITLENGTH) > 0) {
  125. if ((result & i_FLOAT_FORMAT_BIT)) {
  126. if (*(float *)(void *)&BDFDB[KHR_DF_WORD_SAMPLESTART +
  127. KHR_DF_WORD_SAMPLEWORDS * sampleCounter +
  128. KHR_DF_SAMPLEWORD_SAMPLEUPPER] != 1.0f) {
  129. result |= i_NORMALIZED_FORMAT_BIT;
  130. }
  131. } else {
  132. if (KHR_DFDSVAL(BDFDB, sampleCounter, SAMPLEUPPER) != 1U) {
  133. result |= i_NORMALIZED_FORMAT_BIT;
  134. }
  135. }
  136. determinedNormalizedness = 1;
  137. }
  138. }
  139. /* Note: We don't check for inconsistent normalization, because */
  140. /* channels composed of multiple samples will have 0 in the */
  141. /* lower/upper range. */
  142. /* This heuristic should handle 64-bit integers, too. */
  143. }
  144. /* If this is a packed format, we work out our offsets differently. */
  145. /* We assume a packed format has channels that aren't byte-aligned. */
  146. /* If we have a format in which every channel is byte-aligned *and* packed, */
  147. /* we have the RGBA/ABGR ambiguity; we *probably* don't want the packed */
  148. /* version in this case, and if hardware has to pack it and swizzle, */
  149. /* that's up to the hardware to special-case. */
  150. for (sampleCounter = 0; sampleCounter < numSamples; ++sampleCounter) {
  151. if (KHR_DFDSVAL(BDFDB, sampleCounter, BITOFFSET) & 0x7U) {
  152. result |= i_PACKED_FORMAT_BIT;
  153. /* Once we're packed, we're packed, no need to keep checking. */
  154. break;
  155. }
  156. }
  157. /* Remember: the canonical ordering of samples is to start with */
  158. /* the lowest bit of the channel/location which touches bit 0 of */
  159. /* the data, when the latter is concatenated in little-endian order, */
  160. /* and then progress until all the bits of that channel/location */
  161. /* have been processed. Multiple channels sharing the same source */
  162. /* bits are processed in channel ID order. (I should clarify this */
  163. /* for partially-shared data, but it doesn't really matter so long */
  164. /* as everything is consecutive, except to make things canonical.) */
  165. /* Note: For standard formats we could determine big/little-endianness */
  166. /* simply from whether the first sample starts in bit 0; technically */
  167. /* it's possible to have a format with unaligned channels wherein the */
  168. /* first channel starts at bit 0 and is one byte, yet other channels */
  169. /* take more bytes or aren't aligned (e.g. D24S8), but this should be */
  170. /* irrelevant for the formats that we support. */
  171. if ((result & i_PACKED_FORMAT_BIT)) {
  172. /* A packed format. */
  173. uint32_t currentChannel = ~0U; /* Don't start matched. */
  174. uint32_t currentBitOffset = 0;
  175. uint32_t currentByteOffset = 0;
  176. uint32_t currentBitLength = 0;
  177. *wordBytes = (BDFDB[KHR_DF_WORD_BYTESPLANE0] & 0xFFU);
  178. for (sampleCounter = 0; sampleCounter < numSamples; ++sampleCounter) {
  179. uint32_t sampleBitOffset = KHR_DFDSVAL(BDFDB, sampleCounter, BITOFFSET);
  180. uint32_t sampleByteOffset = sampleBitOffset >> 3U;
  181. /* The sample bitLength field stores the bit length - 1. */
  182. uint32_t sampleBitLength = KHR_DFDSVAL(BDFDB, sampleCounter, BITLENGTH) + 1;
  183. uint32_t sampleChannel = KHR_DFDSVAL(BDFDB, sampleCounter, CHANNELID);
  184. InterpretedDFDChannel *sampleChannelPtr;
  185. switch (sampleChannel) {
  186. case KHR_DF_CHANNEL_RGBSDA_RED:
  187. sampleChannelPtr = R;
  188. break;
  189. case KHR_DF_CHANNEL_RGBSDA_GREEN:
  190. sampleChannelPtr = G;
  191. break;
  192. case KHR_DF_CHANNEL_RGBSDA_BLUE:
  193. sampleChannelPtr = B;
  194. break;
  195. case KHR_DF_CHANNEL_RGBSDA_ALPHA:
  196. sampleChannelPtr = A;
  197. break;
  198. default:
  199. return i_UNSUPPORTED_CHANNEL_TYPES;
  200. }
  201. if (sampleChannel == currentChannel) {
  202. /* Continuation of the same channel. */
  203. /* Since a big (>32-bit) channel isn't "packed", */
  204. /* this should only happen in big-endian, or if */
  205. /* we have a wacky format that we won't support. */
  206. if (sampleByteOffset == currentByteOffset - 1U && /* One byte earlier */
  207. ((currentBitOffset + currentBitLength) & 7U) == 0 && /* Already at the end of a byte */
  208. (sampleBitOffset & 7U) == 0) { /* Start at the beginning of the byte */
  209. /* All is good, continue big-endian. */
  210. /* N.B. We shouldn't be here if we decided we were little-endian, */
  211. /* so we don't bother to check that disagreement. */
  212. result |= i_BIG_ENDIAN_FORMAT_BIT;
  213. determinedEndianness = 1;
  214. } else {
  215. /* Oh dear. */
  216. /* We could be little-endian, but not with any standard format. */
  217. /* More likely we've got something weird that we can't support. */
  218. return i_UNSUPPORTED_NONTRIVIAL_ENDIANNESS;
  219. }
  220. /* Remember where we are. */
  221. currentBitOffset = sampleBitOffset;
  222. currentByteOffset = sampleByteOffset;
  223. currentBitLength = sampleBitLength;
  224. /* Accumulate the bit length. */
  225. sampleChannelPtr->size += sampleBitLength;
  226. } else {
  227. /* Everything is new. Hopefully. */
  228. currentChannel = sampleChannel;
  229. currentBitOffset = sampleBitOffset;
  230. currentByteOffset = sampleByteOffset;
  231. currentBitLength = sampleBitLength;
  232. if (sampleChannelPtr->size) {
  233. /* Uh-oh, we've seen this channel before. */
  234. return i_UNSUPPORTED_NONTRIVIAL_ENDIANNESS;
  235. }
  236. /* For now, record the bit offset in little-endian terms, */
  237. /* because we may not know to reverse it yet. */
  238. sampleChannelPtr->offset = sampleBitOffset;
  239. sampleChannelPtr->size = sampleBitLength;
  240. }
  241. }
  242. if ((result & i_BIG_ENDIAN_FORMAT_BIT)) {
  243. /* Our bit offsets to bit 0 of each channel are in little-endian terms. */
  244. /* We need to do a byte swap to work out where they should be. */
  245. /* We assume, for sanity, that byte sizes are a power of two for this. */
  246. uint32_t offsetMask = (*wordBytes - 1U) << 3U;
  247. R->offset ^= offsetMask;
  248. G->offset ^= offsetMask;
  249. B->offset ^= offsetMask;
  250. A->offset ^= offsetMask;
  251. }
  252. } else {
  253. /* Not a packed format. */
  254. /* Everything is byte-aligned. */
  255. /* Question is whether there multiple samples per channel. */
  256. uint32_t currentChannel = ~0U; /* Don't start matched. */
  257. uint32_t currentByteOffset = 0;
  258. uint32_t currentByteLength = 0;
  259. for (sampleCounter = 0; sampleCounter < numSamples; ++sampleCounter) {
  260. uint32_t sampleByteOffset = KHR_DFDSVAL(BDFDB, sampleCounter, BITOFFSET) >> 3U;
  261. uint32_t sampleByteLength = (KHR_DFDSVAL(BDFDB, sampleCounter, BITLENGTH) + 1) >> 3U;
  262. uint32_t sampleChannel = KHR_DFDSVAL(BDFDB, sampleCounter, CHANNELID);
  263. InterpretedDFDChannel *sampleChannelPtr;
  264. switch (sampleChannel) {
  265. case KHR_DF_CHANNEL_RGBSDA_RED:
  266. sampleChannelPtr = R;
  267. break;
  268. case KHR_DF_CHANNEL_RGBSDA_GREEN:
  269. sampleChannelPtr = G;
  270. break;
  271. case KHR_DF_CHANNEL_RGBSDA_BLUE:
  272. sampleChannelPtr = B;
  273. break;
  274. case KHR_DF_CHANNEL_RGBSDA_ALPHA:
  275. sampleChannelPtr = A;
  276. break;
  277. default:
  278. return i_UNSUPPORTED_CHANNEL_TYPES;
  279. }
  280. if (sampleChannel == currentChannel) {
  281. /* Continuation of the same channel. */
  282. /* Either big-endian, or little-endian with a very large channel. */
  283. if (sampleByteOffset == currentByteOffset - 1) { /* One byte earlier */
  284. if (determinedEndianness && !(result & i_BIG_ENDIAN_FORMAT_BIT)) {
  285. return i_UNSUPPORTED_NONTRIVIAL_ENDIANNESS;
  286. }
  287. /* All is good, continue big-endian. */
  288. result |= i_BIG_ENDIAN_FORMAT_BIT;
  289. determinedEndianness = 1;
  290. /* Update the start */
  291. sampleChannelPtr->offset = sampleByteOffset;
  292. } else if (sampleByteOffset == currentByteOffset + currentByteLength) {
  293. if (determinedEndianness && (result & i_BIG_ENDIAN_FORMAT_BIT)) {
  294. return i_UNSUPPORTED_NONTRIVIAL_ENDIANNESS;
  295. }
  296. /* All is good, continue little-endian. */
  297. determinedEndianness = 1;
  298. } else {
  299. /* Oh dear. */
  300. /* We could be little-endian, but not with any standard format. */
  301. /* More likely we've got something weird that we can't support. */
  302. return i_UNSUPPORTED_NONTRIVIAL_ENDIANNESS;
  303. }
  304. /* Remember where we are. */
  305. currentByteOffset = sampleByteOffset;
  306. currentByteLength = sampleByteLength;
  307. /* Accumulate the byte length. */
  308. sampleChannelPtr->size += sampleByteLength;
  309. /* Assume these are all the same. */
  310. *wordBytes = sampleChannelPtr->size;
  311. } else {
  312. /* Everything is new. Hopefully. */
  313. currentChannel = sampleChannel;
  314. currentByteOffset = sampleByteOffset;
  315. currentByteLength = sampleByteLength;
  316. if (sampleChannelPtr->size) {
  317. /* Uh-oh, we've seen this channel before. */
  318. return i_UNSUPPORTED_NONTRIVIAL_ENDIANNESS;
  319. }
  320. /* For now, record the byte offset in little-endian terms, */
  321. /* because we may not know to reverse it yet. */
  322. sampleChannelPtr->offset = sampleByteOffset;
  323. sampleChannelPtr->size = sampleByteLength;
  324. /* Assume these are all the same. */
  325. *wordBytes = sampleByteLength;
  326. }
  327. }
  328. }
  329. return result;
  330. }