Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
godotengine
GitHub Repository: godotengine/godot
Path: blob/master/thirdparty/libktx/lib/basis_transcode.cpp
9902 views
1
/* -*- tab-width: 4; -*- */
2
/* vi: set sw=2 ts=4 expandtab: */
3
4
/*
5
* Copyright 2019-2020 The Khronos Group Inc.
6
* SPDX-License-Identifier: Apache-2.0
7
*/
8
9
/**
10
* @internal
11
* @file
12
* @~English
13
*
14
* @brief Functions for transcoding Basis Universal BasisLZ/ETC1S and UASTC textures.
15
*
16
* Two worlds collide here too. More uglyness!
17
*
18
* @author Mark Callow, www.edgewise-consulting.com
19
*/
20
21
#include <inttypes.h>
22
#include <stdio.h>
23
#include <KHR/khr_df.h>
24
25
#include "dfdutils/dfd.h"
26
#include "ktx.h"
27
#include "ktxint.h"
28
#include "texture2.h"
29
#include "vkformat_enum.h"
30
#include "vk_format.h"
31
#include "basis_sgd.h"
32
#include "transcoder/basisu_file_headers.h"
33
#include "transcoder/basisu_transcoder.h"
34
#include "transcoder/basisu_transcoder_internal.h"
35
36
#undef DECLARE_PRIVATE
37
#undef DECLARE_PROTECTED
38
#define DECLARE_PRIVATE(n,t2) ktxTexture2_private& n = *(t2->_private)
39
#define DECLARE_PROTECTED(n,t2) ktxTexture_protected& n = *(t2->_protected)
40
41
using namespace basisu;
42
using namespace basist;
43
44
inline bool isPow2(uint32_t x) { return x && ((x & (x - 1U)) == 0U); }
45
46
inline bool isPow2(uint64_t x) { return x && ((x & (x - 1U)) == 0U); }
47
48
KTX_error_code
49
ktxTexture2_transcodeLzEtc1s(ktxTexture2* This,
50
alpha_content_e alphaContent,
51
ktxTexture2* prototype,
52
ktx_transcode_fmt_e outputFormat,
53
ktx_transcode_flags transcodeFlags);
54
KTX_error_code
55
ktxTexture2_transcodeUastc(ktxTexture2* This,
56
alpha_content_e alphaContent,
57
ktxTexture2* prototype,
58
ktx_transcode_fmt_e outputFormat,
59
ktx_transcode_flags transcodeFlags);
60
61
/**
62
* @memberof ktxTexture2
63
* @ingroup reader
64
* @~English
65
* @brief Transcode a KTX2 texture with BasisLZ/ETC1S or UASTC images.
66
*
67
* If the texture contains BasisLZ supercompressed images, Inflates them from
68
* back to ETC1S then transcodes them to the specified block-compressed
69
* format. If the texture contains UASTC images, inflates them, if they have been
70
* supercompressed with zstd, then transcodes then to the specified format, The
71
* transcoded images replace the original images and the texture's fields including
72
* the DFD are modified to reflect the new format.
73
*
74
* These types of textures must be transcoded to a desired target
75
* block-compressed format before they can be uploaded to a GPU via a
76
* graphics API.
77
*
78
* The following block compressed transcode targets are available: @c KTX_TTF_ETC1_RGB,
79
* @c KTX_TTF_ETC2_RGBA, @c KTX_TTF_BC1_RGB, @c KTX_TTF_BC3_RGBA,
80
* @c KTX_TTF_BC4_R, @c KTX_TTF_BC5_RG, @c KTX_TTF_BC7_RGBA,
81
* @c @c KTX_TTF_PVRTC1_4_RGB, @c KTX_TTF_PVRTC1_4_RGBA,
82
* @c KTX_TTF_PVRTC2_4_RGB, @c KTX_TTF_PVRTC2_4_RGBA, @c KTX_TTF_ASTC_4x4_RGBA,
83
* @c KTX_TTF_ETC2_EAC_R11, @c KTX_TTF_ETC2_EAC_RG11, @c KTX_TTF_ETC and
84
* @c KTX_TTF_BC1_OR_3.
85
*
86
* @c KTX_TTF_ETC automatically selects between @c KTX_TTF_ETC1_RGB and
87
* @c KTX_TTF_ETC2_RGBA according to whether an alpha channel is available. @c KTX_TTF_BC1_OR_3
88
* does likewise between @c KTX_TTF_BC1_RGB and @c KTX_TTF_BC3_RGBA. Note that if
89
* @c KTX_TTF_PVRTC1_4_RGBA or @c KTX_TTF_PVRTC2_4_RGBA is specified and there is no alpha
90
* channel @c KTX_TTF_PVRTC1_4_RGB or @c KTX_TTF_PVRTC2_4_RGB respectively will be selected.
91
*
92
* Transcoding to ATC & FXT1 formats is not supported by libktx as there
93
* are no equivalent Vulkan formats.
94
*
95
* The following uncompressed transcode targets are also available: @c KTX_TTF_RGBA32,
96
* @c KTX_TTF_RGB565, KTX_TTF_BGR565 and KTX_TTF_RGBA4444.
97
*
98
* The following @p transcodeFlags are available.
99
*
100
* @sa ktxtexture2_CompressBasis().
101
*
102
* @param[in] This pointer to the ktxTexture2 object of interest.
103
* @param[in] outputFormat a value from the ktx_texture_transcode_fmt_e enum
104
* specifying the target format.
105
* @param[in] transcodeFlags bitfield of flags modifying the transcode
106
* operation. @sa ktx_texture_decode_flags_e.
107
*
108
* @return KTX_SUCCESS on success, other KTX_* enum values on error.
109
*
110
* @exception KTX_FILE_DATA_ERROR
111
* Supercompression global data is corrupted.
112
* @exception KTX_INVALID_OPERATION
113
* The texture's format is not transcodable (not
114
* ETC1S/BasisLZ or UASTC).
115
* @exception KTX_INVALID_OPERATION
116
* Supercompression global data is missing, i.e.,
117
* the texture object is invalid.
118
* @exception KTX_INVALID_OPERATION
119
* Image data is missing, i.e., the texture object
120
* is invalid.
121
* @exception KTX_INVALID_OPERATION
122
* @p outputFormat is PVRTC1 but the texture does
123
* does not have power-of-two dimensions.
124
* @exception KTX_INVALID_VALUE @p outputFormat is invalid.
125
* @exception KTX_TRANSCODE_FAILED
126
* Something went wrong during transcoding.
127
* @exception KTX_UNSUPPORTED_FEATURE
128
* KTX_TF_PVRTC_DECODE_TO_NEXT_POW2 was requested
129
* or the specified transcode target has not been
130
* included in the library being used.
131
* @exception KTX_OUT_OF_MEMORY Not enough memory to carry out transcoding.
132
*/
133
KTX_error_code
134
ktxTexture2_TranscodeBasis(ktxTexture2* This,
135
ktx_transcode_fmt_e outputFormat,
136
ktx_transcode_flags transcodeFlags)
137
{
138
uint32_t* BDB = This->pDfd + 1;
139
khr_df_model_e colorModel = (khr_df_model_e)KHR_DFDVAL(BDB, MODEL);
140
if (colorModel != KHR_DF_MODEL_UASTC
141
// Constructor has checked color model matches BASIS_LZ.
142
&& This->supercompressionScheme != KTX_SS_BASIS_LZ)
143
{
144
return KTX_INVALID_OPERATION; // Not in a transcodable format.
145
}
146
147
DECLARE_PRIVATE(priv, This);
148
if (This->supercompressionScheme == KTX_SS_BASIS_LZ) {
149
if (!priv._supercompressionGlobalData || priv._sgdByteLength == 0)
150
return KTX_INVALID_OPERATION;
151
}
152
153
if (transcodeFlags & KTX_TF_PVRTC_DECODE_TO_NEXT_POW2) {
154
debug_printf("ktxTexture_TranscodeBasis: KTX_TF_PVRTC_DECODE_TO_NEXT_POW2 currently unsupported\n");
155
return KTX_UNSUPPORTED_FEATURE;
156
}
157
158
if (outputFormat == KTX_TTF_PVRTC1_4_RGB
159
|| outputFormat == KTX_TTF_PVRTC1_4_RGBA) {
160
if ((!isPow2(This->baseWidth)) || (!isPow2(This->baseHeight))) {
161
debug_printf("ktxTexture_TranscodeBasis: PVRTC1 only supports power of 2 dimensions\n");
162
return KTX_INVALID_OPERATION;
163
}
164
}
165
166
const bool srgb = (KHR_DFDVAL(BDB, TRANSFER) == KHR_DF_TRANSFER_SRGB);
167
alpha_content_e alphaContent = eNone;
168
if (colorModel == KHR_DF_MODEL_ETC1S) {
169
if (KHR_DFDSAMPLECOUNT(BDB) == 2) {
170
uint32_t channelId = KHR_DFDSVAL(BDB, 1, CHANNELID);
171
if (channelId == KHR_DF_CHANNEL_ETC1S_AAA) {
172
alphaContent = eAlpha;
173
} else if (channelId == KHR_DF_CHANNEL_ETC1S_GGG){
174
alphaContent = eGreen;
175
} else {
176
return KTX_FILE_DATA_ERROR;
177
}
178
}
179
} else {
180
uint32_t channelId = KHR_DFDSVAL(BDB, 0, CHANNELID);
181
if (channelId == KHR_DF_CHANNEL_UASTC_RGBA)
182
alphaContent = eAlpha;
183
else if (channelId == KHR_DF_CHANNEL_UASTC_RRRG)
184
alphaContent = eGreen;
185
}
186
187
VkFormat vkFormat;
188
189
// Do some format mapping.
190
switch (outputFormat) {
191
case KTX_TTF_BC1_OR_3:
192
outputFormat = alphaContent != eNone ? KTX_TTF_BC3_RGBA
193
: KTX_TTF_BC1_RGB;
194
break;
195
case KTX_TTF_ETC:
196
outputFormat = alphaContent != eNone ? KTX_TTF_ETC2_RGBA
197
: KTX_TTF_ETC1_RGB;
198
break;
199
case KTX_TTF_PVRTC1_4_RGBA:
200
// This transcoder does not write opaque alpha blocks.
201
outputFormat = alphaContent != eNone ? KTX_TTF_PVRTC1_4_RGBA
202
: KTX_TTF_PVRTC1_4_RGB;
203
break;
204
case KTX_TTF_PVRTC2_4_RGBA:
205
// This transcoder does not write opaque alpha blocks.
206
outputFormat = alphaContent != eNone ? KTX_TTF_PVRTC2_4_RGBA
207
: KTX_TTF_PVRTC2_4_RGB;
208
break;
209
default:
210
/*NOP*/;
211
}
212
213
switch (outputFormat) {
214
case KTX_TTF_ETC1_RGB:
215
vkFormat = srgb ? VK_FORMAT_ETC2_R8G8B8_SRGB_BLOCK
216
: VK_FORMAT_ETC2_R8G8B8_UNORM_BLOCK;
217
break;
218
case KTX_TTF_ETC2_RGBA:
219
vkFormat = srgb ? VK_FORMAT_ETC2_R8G8B8A8_SRGB_BLOCK
220
: VK_FORMAT_ETC2_R8G8B8A8_UNORM_BLOCK;
221
break;
222
case KTX_TTF_ETC2_EAC_R11:
223
vkFormat = VK_FORMAT_EAC_R11_UNORM_BLOCK;
224
break;
225
case KTX_TTF_ETC2_EAC_RG11:
226
vkFormat = VK_FORMAT_EAC_R11G11_UNORM_BLOCK;
227
break;
228
case KTX_TTF_BC1_RGB:
229
// Transcoding doesn't support BC1 alpha.
230
vkFormat = srgb ? VK_FORMAT_BC1_RGB_SRGB_BLOCK
231
: VK_FORMAT_BC1_RGB_UNORM_BLOCK;
232
break;
233
case KTX_TTF_BC3_RGBA:
234
vkFormat = srgb ? VK_FORMAT_BC3_SRGB_BLOCK
235
: VK_FORMAT_BC3_UNORM_BLOCK;
236
break;
237
case KTX_TTF_BC4_R:
238
vkFormat = VK_FORMAT_BC4_UNORM_BLOCK;
239
break;
240
case KTX_TTF_BC5_RG:
241
vkFormat = VK_FORMAT_BC5_UNORM_BLOCK;
242
break;
243
case KTX_TTF_PVRTC1_4_RGB:
244
case KTX_TTF_PVRTC1_4_RGBA:
245
vkFormat = srgb ? VK_FORMAT_PVRTC1_4BPP_SRGB_BLOCK_IMG
246
: VK_FORMAT_PVRTC1_4BPP_UNORM_BLOCK_IMG;
247
break;
248
case KTX_TTF_PVRTC2_4_RGB:
249
case KTX_TTF_PVRTC2_4_RGBA:
250
vkFormat = srgb ? VK_FORMAT_PVRTC2_4BPP_SRGB_BLOCK_IMG
251
: VK_FORMAT_PVRTC2_4BPP_UNORM_BLOCK_IMG;
252
break;
253
case KTX_TTF_BC7_RGBA:
254
vkFormat = srgb ? VK_FORMAT_BC7_SRGB_BLOCK
255
: VK_FORMAT_BC7_UNORM_BLOCK;
256
break;
257
case KTX_TTF_ASTC_4x4_RGBA:
258
vkFormat = srgb ? VK_FORMAT_ASTC_4x4_SRGB_BLOCK
259
: VK_FORMAT_ASTC_4x4_UNORM_BLOCK;
260
break;
261
case KTX_TTF_RGB565:
262
vkFormat = VK_FORMAT_R5G6B5_UNORM_PACK16;
263
break;
264
case KTX_TTF_BGR565:
265
vkFormat = VK_FORMAT_B5G6R5_UNORM_PACK16;
266
break;
267
case KTX_TTF_RGBA4444:
268
vkFormat = VK_FORMAT_R4G4B4A4_UNORM_PACK16;
269
break;
270
case KTX_TTF_RGBA32:
271
vkFormat = srgb ? VK_FORMAT_R8G8B8A8_SRGB
272
: VK_FORMAT_R8G8B8A8_UNORM;
273
break;
274
default:
275
return KTX_INVALID_VALUE;
276
}
277
278
basis_tex_format textureFormat;
279
if (colorModel == KHR_DF_MODEL_UASTC)
280
textureFormat = basis_tex_format::cUASTC4x4;
281
else
282
textureFormat = basis_tex_format::cETC1S;
283
284
if (!basis_is_format_supported((transcoder_texture_format)outputFormat,
285
textureFormat)) {
286
return KTX_UNSUPPORTED_FEATURE;
287
}
288
289
290
// Create a prototype texture to use for calculating sizes in the target
291
// format and, as useful side effects, provide us with a properly sized
292
// data allocation and the DFD for the target format.
293
ktxTextureCreateInfo createInfo;
294
createInfo.glInternalformat = 0;
295
createInfo.vkFormat = vkFormat;
296
createInfo.baseWidth = This->baseWidth;
297
createInfo.baseHeight = This->baseHeight;
298
createInfo.baseDepth = This->baseDepth;
299
createInfo.generateMipmaps = This->generateMipmaps;
300
createInfo.isArray = This->isArray;
301
createInfo.numDimensions = This->numDimensions;
302
createInfo.numFaces = This->numFaces;
303
createInfo.numLayers = This->numLayers;
304
createInfo.numLevels = This->numLevels;
305
createInfo.pDfd = nullptr;
306
307
KTX_error_code result;
308
ktxTexture2* prototype;
309
result = ktxTexture2_Create(&createInfo, KTX_TEXTURE_CREATE_ALLOC_STORAGE,
310
&prototype);
311
312
if (result != KTX_SUCCESS) {
313
assert(result == KTX_OUT_OF_MEMORY); // The only run time error
314
return result;
315
}
316
317
if (!This->pData) {
318
if (ktxTexture_isActiveStream((ktxTexture*)This)) {
319
// Load pending. Complete it.
320
result = ktxTexture2_LoadImageData(This, NULL, 0);
321
if (result != KTX_SUCCESS)
322
{
323
ktxTexture2_Destroy(prototype);
324
return result;
325
}
326
} else {
327
// No data to transcode.
328
ktxTexture2_Destroy(prototype);
329
return KTX_INVALID_OPERATION;
330
}
331
}
332
333
// Transcoder global initialization. Requires ~9 milliseconds when compiled
334
// and executed natively on a Core i7 2.2 GHz. If this is too slow, the
335
// tables it computes can easily be moved to be compiled in.
336
static bool transcoderInitialized;
337
if (!transcoderInitialized) {
338
basisu_transcoder_init();
339
transcoderInitialized = true;
340
}
341
342
if (textureFormat == basis_tex_format::cETC1S) {
343
result = ktxTexture2_transcodeLzEtc1s(This, alphaContent,
344
prototype, outputFormat,
345
transcodeFlags);
346
} else {
347
result = ktxTexture2_transcodeUastc(This, alphaContent,
348
prototype, outputFormat,
349
transcodeFlags);
350
}
351
352
if (result == KTX_SUCCESS) {
353
// Fix up the current texture
354
DECLARE_PROTECTED(thisPrtctd, This);
355
DECLARE_PRIVATE(protoPriv, prototype);
356
DECLARE_PROTECTED(protoPrtctd, prototype);
357
memcpy(&thisPrtctd._formatSize, &protoPrtctd._formatSize,
358
sizeof(ktxFormatSize));
359
This->vkFormat = vkFormat;
360
This->isCompressed = prototype->isCompressed;
361
This->supercompressionScheme = KTX_SS_NONE;
362
priv._requiredLevelAlignment = protoPriv._requiredLevelAlignment;
363
// Copy the levelIndex from the prototype to This.
364
memcpy(priv._levelIndex, protoPriv._levelIndex,
365
This->numLevels * sizeof(ktxLevelIndexEntry));
366
// Move the DFD and data from the prototype to This.
367
free(This->pDfd);
368
This->pDfd = prototype->pDfd;
369
prototype->pDfd = 0;
370
free(This->pData);
371
This->pData = prototype->pData;
372
This->dataSize = prototype->dataSize;
373
prototype->pData = 0;
374
prototype->dataSize = 0;
375
// Free SGD data
376
This->_private->_sgdByteLength = 0;
377
if (This->_private->_supercompressionGlobalData) {
378
free(This->_private->_supercompressionGlobalData);
379
This->_private->_supercompressionGlobalData = NULL;
380
}
381
}
382
ktxTexture2_Destroy(prototype);
383
return result;
384
}
385
386
/**
387
* @memberof ktxTexture2 @private
388
* @ingroup reader
389
* @~English
390
* @brief Transcode a KTX2 texture with BasisLZ supercompressed ETC1S images.
391
*
392
* Inflates the images from BasisLZ supercompression back to ETC1S
393
* then transcodes them to the specified block-compressed format. The
394
* transcoded images replace the original images and the texture's fields
395
* including the DFD are modified to reflect the new format.
396
*
397
* BasisLZ supercompressed textures must be transcoded to a desired target
398
* block-compressed format before they can be uploaded to a GPU via a graphics
399
* API.
400
*
401
* The following block compressed transcode targets are available: @c KTX_TTF_ETC1_RGB,
402
* @c KTX_TTF_ETC2_RGBA, @c KTX_TTF_BC1_RGB, @c KTX_TTF_BC3_RGBA,
403
* @c KTX_TTF_BC4_R, @c KTX_TTF_BC5_RG, @c KTX_TTF_BC7_RGBA,
404
* @c @c KTX_TTF_PVRTC1_4_RGB, @c KTX_TTF_PVRTC1_4_RGBA,
405
* @c KTX_TTF_PVRTC2_4_RGB, @c KTX_TTF_PVRTC2_4_RGBA, @c KTX_TTF_ASTC_4x4_RGBA,
406
* @c KTX_TTF_ETC2_EAC_R11, @c KTX_TTF_ETC2_EAC_RG11, @c KTX_TTF_ETC and
407
* @c KTX_TTF_BC1_OR_3.
408
*
409
* @c KTX_TTF_ETC automatically selects between @c KTX_TTF_ETC1_RGB and
410
* @c KTX_TTF_ETC2_RGBA according to whether an alpha channel is available. @c KTX_TTF_BC1_OR_3
411
* does likewise between @c KTX_TTF_BC1_RGB and @c KTX_TTF_BC3_RGBA. Note that if
412
* @c KTX_TTF_PVRTC1_4_RGBA or @c KTX_TTF_PVRTC2_4_RGBA is specified and there is no alpha
413
* channel @c KTX_TTF_PVRTC1_4_RGB or @c KTX_TTF_PVRTC2_4_RGB respectively will be selected.
414
*
415
* ATC & FXT1 formats are not supported by KTX2 & libktx as there are no equivalent Vulkan formats.
416
*
417
* The following uncompressed transcode targets are also available: @c KTX_TTF_RGBA32,
418
* @c KTX_TTF_RGB565, KTX_TTF_BGR565 and KTX_TTF_RGBA4444.
419
*
420
* The following @p transcodeFlags are available.
421
*
422
* @sa ktxtexture2_CompressBasis().
423
*
424
* @param[in] This pointer to the ktxTexture2 object of interest.
425
* @param[in] outputFormat a value from the ktx_texture_transcode_fmt_e enum
426
* specifying the target format.
427
* @param[in] transcodeFlags bitfield of flags modifying the transcode
428
* operation. @sa ktx_texture_decode_flags_e.
429
*
430
* @return KTX_SUCCESS on success, other KTX_* enum values on error.
431
*
432
* @exception KTX_FILE_DATA_ERROR
433
* Supercompression global data is corrupted.
434
* @exception KTX_INVALID_OPERATION
435
* The texture's format is not transcodable (not
436
* ETC1S/BasisLZ or UASTC).
437
* @exception KTX_INVALID_OPERATION
438
* Supercompression global data is missing, i.e.,
439
* the texture object is invalid.
440
* @exception KTX_INVALID_OPERATION
441
* Image data is missing, i.e., the texture object
442
* is invalid.
443
* @exception KTX_INVALID_OPERATION
444
* @p outputFormat is PVRTC1 but the texture does
445
* does not have power-of-two dimensions.
446
* @exception KTX_INVALID_VALUE @p outputFormat is invalid.
447
* @exception KTX_TRANSCODE_FAILED
448
* Something went wrong during transcoding. The
449
* texture object will be corrupted.
450
* @exception KTX_UNSUPPORTED_FEATURE
451
* KTX_TF_PVRTC_DECODE_TO_NEXT_POW2 was requested
452
* or the specified transcode target has not been
453
* included in the library being used.
454
* @exception KTX_OUT_OF_MEMORY Not enough memory to carry out transcoding.
455
*/
456
KTX_error_code
457
ktxTexture2_transcodeLzEtc1s(ktxTexture2* This,
458
alpha_content_e alphaContent,
459
ktxTexture2* prototype,
460
ktx_transcode_fmt_e outputFormat,
461
ktx_transcode_flags transcodeFlags)
462
{
463
DECLARE_PRIVATE(priv, This);
464
DECLARE_PRIVATE(protoPriv, prototype);
465
KTX_error_code result = KTX_SUCCESS;
466
467
assert(This->supercompressionScheme == KTX_SS_BASIS_LZ);
468
469
uint8_t* bgd = priv._supercompressionGlobalData;
470
ktxBasisLzGlobalHeader& bgdh = *reinterpret_cast<ktxBasisLzGlobalHeader*>(bgd);
471
if (!(bgdh.endpointsByteLength && bgdh.selectorsByteLength && bgdh.tablesByteLength)) {
472
debug_printf("ktxTexture_TranscodeBasis: missing endpoints, selectors or tables");
473
return KTX_FILE_DATA_ERROR;
474
}
475
476
// Compute some helpful numbers.
477
//
478
// firstImages contains the indices of the first images for each level to
479
// ease finding the correct slice description when iterating from smallest
480
// level to largest or when randomly accessing them (t.b.c). The last array
481
// entry contains the total number of images, for calculating the offsets
482
// of the endpoints, etc.
483
uint32_t* firstImages = new uint32_t[This->numLevels+1];
484
485
// Temporary invariant value
486
uint32_t layersFaces = This->numLayers * This->numFaces;
487
firstImages[0] = 0;
488
for (uint32_t level = 1; level <= This->numLevels; level++) {
489
// NOTA BENE: numFaces * depth is only reasonable because they can't
490
// both be > 1. I.e there are no 3d cubemaps.
491
firstImages[level] = firstImages[level - 1]
492
+ layersFaces * MAX(This->baseDepth >> (level - 1), 1);
493
}
494
uint32_t& imageCount = firstImages[This->numLevels];
495
496
if (BGD_TABLES_ADDR(0, bgdh, imageCount) + bgdh.tablesByteLength > priv._sgdByteLength) {
497
// Compiler will not allow `goto cleanup;` because "jump bypasses variable initialization."
498
// The static initializations below this and before the loop are presumably the issue
499
// as the compiler is,presumably, inserting code to destruct those at the end of the
500
// function.
501
delete[] firstImages;
502
return KTX_FILE_DATA_ERROR;
503
}
504
// FIXME: Do more validation.
505
506
// Prepare low-level transcoder for transcoding slices.
507
basist::basisu_lowlevel_etc1s_transcoder bit;
508
509
// basisu_transcoder_state is used to find the previous frame when
510
// decoding a video P-Frame. It tracks the previous frame for each mip
511
// level. For cube map array textures we need to find the previous frame
512
// for each face so we a state per face. Although providing this is only
513
// needed for video, it is easier to always pass our own.
514
std::vector<basisu_transcoder_state> xcoderStates;
515
xcoderStates.resize(This->isVideo ? This->numFaces : 1);
516
517
bit.decode_palettes(bgdh.endpointCount, BGD_ENDPOINTS_ADDR(bgd, imageCount),
518
bgdh.endpointsByteLength,
519
bgdh.selectorCount, BGD_SELECTORS_ADDR(bgd, bgdh, imageCount),
520
bgdh.selectorsByteLength);
521
522
bit.decode_tables(BGD_TABLES_ADDR(bgd, bgdh, imageCount),
523
bgdh.tablesByteLength);
524
525
// Find matching VkFormat and calculate output sizes.
526
527
const bool isVideo = This->isVideo;
528
529
ktx_uint8_t* pXcodedData = prototype->pData;
530
// Inconveniently, the output buffer size parameter of transcode_image
531
// has to be in pixels for uncompressed output and in blocks for
532
// compressed output. The only reason for humouring the API is so
533
// its buffer size tests provide a real check. An alternative is to
534
// always provide the size in bytes which will always pass.
535
ktx_uint32_t outputBlockByteLength
536
= prototype->_protected->_formatSize.blockSizeInBits / 8;
537
ktx_size_t xcodedDataLength
538
= prototype->dataSize / outputBlockByteLength;
539
ktxLevelIndexEntry* protoLevelIndex;
540
uint64_t levelOffsetWrite;
541
const ktxBasisLzEtc1sImageDesc* imageDescs = BGD_ETC1S_IMAGE_DESCS(bgd);
542
543
// Finally we're ready to transcode the slices.
544
545
// FIXME: Iframe flag needs to be queryable by the application. In Basis
546
// the app can query file_info and image_info from the transcoder which
547
// returns a structure with lots of info about the image.
548
549
protoLevelIndex = protoPriv._levelIndex;
550
levelOffsetWrite = 0;
551
for (int32_t level = This->numLevels - 1; level >= 0; level--) {
552
uint64_t levelOffset = ktxTexture2_levelDataOffset(This, level);
553
uint64_t writeOffset = levelOffsetWrite;
554
uint64_t writeOffsetBlocks = levelOffsetWrite / outputBlockByteLength;
555
uint32_t levelWidth = MAX(1, This->baseWidth >> level);
556
uint32_t levelHeight = MAX(1, This->baseHeight >> level);
557
// ETC1S texel block dimensions
558
const uint32_t bw = 4, bh = 4;
559
uint32_t levelBlocksX = (levelWidth + (bw - 1)) / bw;
560
uint32_t levelBlocksY = (levelHeight + (bh - 1)) / bh;
561
uint32_t depth = MAX(1, This->baseDepth >> level);
562
//uint32_t faceSlices = This->numFaces == 1 ? depth : This->numFaces;
563
uint32_t faceSlices = This->numFaces * depth;
564
uint32_t numImages = This->numLayers * faceSlices;
565
uint32_t image = firstImages[level];
566
uint32_t endImage = image + numImages;
567
ktx_size_t levelImageSizeOut, levelSizeOut;
568
uint32_t stateIndex = 0;
569
570
levelSizeOut = 0;
571
// FIXME: Figure out a way to get the size out of the transcoder.
572
levelImageSizeOut = ktxTexture2_GetImageSize(prototype, level);
573
for (; image < endImage; image++) {
574
const ktxBasisLzEtc1sImageDesc& imageDesc = imageDescs[image];
575
576
basisu_transcoder_state& xcoderState = xcoderStates[stateIndex];
577
// We have face0 [face1 ...] within each layer. Use `stateIndex`
578
// rather than a double loop of layers and faceSlices as this
579
// works for 3d texture and non-array cube maps as well as
580
// cube map arrays without special casing.
581
if (++stateIndex == xcoderStates.size())
582
stateIndex = 0;
583
584
if (alphaContent != eNone)
585
{
586
// The slice descriptions should have alpha information.
587
if (imageDesc.alphaSliceByteOffset == 0
588
|| imageDesc.alphaSliceByteLength == 0)
589
return KTX_FILE_DATA_ERROR;
590
}
591
592
bool status;
593
status = bit.transcode_image(
594
(transcoder_texture_format)outputFormat,
595
pXcodedData + writeOffset,
596
(uint32_t)(xcodedDataLength - writeOffsetBlocks),
597
This->pData,
598
(uint32_t)This->dataSize,
599
levelBlocksX,
600
levelBlocksY,
601
levelWidth,
602
levelHeight,
603
level,
604
(uint32_t)(levelOffset + imageDesc.rgbSliceByteOffset),
605
imageDesc.rgbSliceByteLength,
606
(uint32_t)(levelOffset + imageDesc.alphaSliceByteOffset),
607
imageDesc.alphaSliceByteLength,
608
transcodeFlags,
609
alphaContent != eNone,
610
isVideo,
611
// Our P-Frame flag is in the same bit as
612
// cSliceDescFlagsFrameIsIFrame. We have to
613
// invert it to make it an I-Frame flag.
614
//
615
// API currently doesn't have any way to pass
616
// the I-Frame flag.
617
//imageDesc.imageFlags ^ cSliceDescFlagsFrameIsIFrame,
618
0, // output_row_pitch_in_blocks_or_pixels
619
&xcoderState,
620
0 // output_rows_in_pixels
621
);
622
if (!status) {
623
result = KTX_TRANSCODE_FAILED;
624
goto cleanup;
625
}
626
627
writeOffset += levelImageSizeOut;
628
levelSizeOut += levelImageSizeOut;
629
} // end images loop
630
protoLevelIndex[level].byteOffset = levelOffsetWrite;
631
protoLevelIndex[level].byteLength = levelSizeOut;
632
protoLevelIndex[level].uncompressedByteLength = levelSizeOut;
633
levelOffsetWrite += levelSizeOut;
634
assert(levelOffsetWrite == writeOffset);
635
// In case of transcoding to uncompressed.
636
levelOffsetWrite = _KTX_PADN(protoPriv._requiredLevelAlignment,
637
levelOffsetWrite);
638
} // level loop
639
640
result = KTX_SUCCESS;
641
642
cleanup:
643
delete[] firstImages;
644
return result;
645
}
646
647
648
KTX_error_code
649
ktxTexture2_transcodeUastc(ktxTexture2* This,
650
alpha_content_e alphaContent,
651
ktxTexture2* prototype,
652
ktx_transcode_fmt_e outputFormat,
653
ktx_transcode_flags transcodeFlags)
654
{
655
assert(This->supercompressionScheme != KTX_SS_BASIS_LZ);
656
657
ktx_uint8_t* pXcodedData = prototype->pData;
658
ktx_uint32_t outputBlockByteLength
659
= prototype->_protected->_formatSize.blockSizeInBits / 8;
660
ktx_size_t xcodedDataLength
661
= prototype->dataSize / outputBlockByteLength;
662
DECLARE_PRIVATE(protoPriv, prototype);
663
ktxLevelIndexEntry* protoLevelIndex = protoPriv._levelIndex;
664
ktx_size_t levelOffsetWrite = 0;
665
666
basisu_lowlevel_uastc_ldr_4x4_transcoder uit;
667
// See comment on same declaration in transcodeEtc1s.
668
std::vector<basisu_transcoder_state> xcoderStates;
669
xcoderStates.resize(This->isVideo ? This->numFaces : 1);
670
671
for (ktx_int32_t level = This->numLevels - 1; level >= 0; level--)
672
{
673
ktx_uint32_t depth;
674
uint64_t writeOffset = levelOffsetWrite;
675
uint64_t writeOffsetBlocks = levelOffsetWrite / outputBlockByteLength;
676
ktx_size_t levelImageSizeIn, levelImageOffsetIn;
677
ktx_size_t levelImageSizeOut, levelSizeOut;
678
ktx_uint32_t levelImageCount;
679
uint32_t levelWidth = MAX(1, This->baseWidth >> level);
680
uint32_t levelHeight = MAX(1, This->baseHeight >> level);
681
// UASTC texel block dimensions
682
const uint32_t bw = 4, bh = 4;
683
uint32_t levelBlocksX = (levelWidth + (bw - 1)) / bw;
684
uint32_t levelBlocksY = (levelHeight + (bh - 1)) / bh;
685
uint32_t stateIndex = 0;
686
687
depth = MAX(1, This->baseDepth >> level);
688
689
levelImageCount = This->numLayers * This->numFaces * depth;
690
levelImageSizeIn = ktxTexture_calcImageSize(ktxTexture(This), level,
691
KTX_FORMAT_VERSION_TWO);
692
levelImageSizeOut = ktxTexture_calcImageSize(ktxTexture(prototype),
693
level,
694
KTX_FORMAT_VERSION_TWO);
695
696
levelImageOffsetIn = ktxTexture2_levelDataOffset(This, level);
697
levelSizeOut = 0;
698
bool status;
699
for (uint32_t image = 0; image < levelImageCount; image++) {
700
basisu_transcoder_state& xcoderState = xcoderStates[stateIndex];
701
// See comment before same lines in transcodeEtc1s.
702
if (++stateIndex == xcoderStates.size())
703
stateIndex = 0;
704
705
status = uit.transcode_image(
706
(transcoder_texture_format)outputFormat,
707
pXcodedData + writeOffset,
708
(uint32_t)(xcodedDataLength - writeOffsetBlocks),
709
This->pData,
710
(uint32_t)This->dataSize,
711
levelBlocksX,
712
levelBlocksY,
713
levelWidth,
714
levelHeight,
715
level,
716
(uint32_t)levelImageOffsetIn,
717
(uint32_t)levelImageSizeIn,
718
transcodeFlags,
719
alphaContent != eNone,
720
This->isVideo, // is_video
721
//imageDesc.imageFlags ^ cSliceDescFlagsFrameIsIFrame,
722
0, // output_row_pitch_in_blocks_or_pixels
723
&xcoderState, // pState
724
0, // output_rows_in_pixels,
725
-1, // channel0
726
-1 // channel1
727
);
728
if (!status)
729
return KTX_TRANSCODE_FAILED;
730
writeOffset += levelImageSizeOut;
731
levelSizeOut += levelImageSizeOut;
732
levelImageOffsetIn += levelImageSizeIn;
733
}
734
protoLevelIndex[level].byteOffset = levelOffsetWrite;
735
// writeOffset will be equal to total size of the images in the level.
736
protoLevelIndex[level].byteLength = levelSizeOut;
737
protoLevelIndex[level].uncompressedByteLength = levelSizeOut;
738
levelOffsetWrite += levelSizeOut;
739
}
740
// In case of transcoding to uncompressed.
741
levelOffsetWrite = _KTX_PADN(protoPriv._requiredLevelAlignment,
742
levelOffsetWrite);
743
return KTX_SUCCESS;
744
}
745
746