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