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 basis_transcode.cpp |
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 | } |
376 | ktxTexture2_Destroy(prototype); |
377 | return result; |
378 | } |
379 | |
380 | /** |
381 | * @memberof ktxTexture2 @private |
382 | * @ingroup reader |
383 | * @~English |
384 | * @brief Transcode a KTX2 texture with BasisLZ supercompressed ETC1S images. |
385 | * |
386 | * Inflates the images from BasisLZ supercompression back to ETC1S |
387 | * then transcodes them to the specified block-compressed format. The |
388 | * transcoded images replace the original images and the texture's fields |
389 | * including the DFD are modified to reflect the new format. |
390 | * |
391 | * BasisLZ supercompressed textures must be transcoded to a desired target |
392 | * block-compressed format before they can be uploaded to a GPU via a graphics |
393 | * API. |
394 | * |
395 | * The following block compressed transcode targets are available: @c KTX_TTF_ETC1_RGB, |
396 | * @c KTX_TTF_ETC2_RGBA, @c KTX_TTF_BC1_RGB, @c KTX_TTF_BC3_RGBA, |
397 | * @c KTX_TTF_BC4_R, @c KTX_TTF_BC5_RG, @c KTX_TTF_BC7_RGBA, |
398 | * @c @c KTX_TTF_PVRTC1_4_RGB, @c KTX_TTF_PVRTC1_4_RGBA, |
399 | * @c KTX_TTF_PVRTC2_4_RGB, @c KTX_TTF_PVRTC2_4_RGBA, @c KTX_TTF_ASTC_4x4_RGBA, |
400 | * @c KTX_TTF_ETC2_EAC_R11, @c KTX_TTF_ETC2_EAC_RG11, @c KTX_TTF_ETC and |
401 | * @c KTX_TTF_BC1_OR_3. |
402 | * |
403 | * @c KTX_TTF_ETC automatically selects between @c KTX_TTF_ETC1_RGB and |
404 | * @c KTX_TTF_ETC2_RGBA according to whether an alpha channel is available. @c KTX_TTF_BC1_OR_3 |
405 | * does likewise between @c KTX_TTF_BC1_RGB and @c KTX_TTF_BC3_RGBA. Note that if |
406 | * @c KTX_TTF_PVRTC1_4_RGBA or @c KTX_TTF_PVRTC2_4_RGBA is specified and there is no alpha |
407 | * channel @c KTX_TTF_PVRTC1_4_RGB or @c KTX_TTF_PVRTC2_4_RGB respectively will be selected. |
408 | * |
409 | * ATC & FXT1 formats are not supported by KTX2 & libktx as there are no equivalent Vulkan formats. |
410 | * |
411 | * The following uncompressed transcode targets are also available: @c KTX_TTF_RGBA32, |
412 | * @c KTX_TTF_RGB565, KTX_TTF_BGR565 and KTX_TTF_RGBA4444. |
413 | * |
414 | * The following @p transcodeFlags are available. |
415 | * |
416 | * @sa ktxtexture2_CompressBasis(). |
417 | * |
418 | * @param[in] This pointer to the ktxTexture2 object of interest. |
419 | * @param[in] outputFormat a value from the ktx_texture_transcode_fmt_e enum |
420 | * specifying the target format. |
421 | * @param[in] transcodeFlags bitfield of flags modifying the transcode |
422 | * operation. @sa ktx_texture_decode_flags_e. |
423 | * |
424 | * @return KTX_SUCCESS on success, other KTX_* enum values on error. |
425 | * |
426 | * @exception KTX_FILE_DATA_ERROR |
427 | * Supercompression global data is corrupted. |
428 | * @exception KTX_INVALID_OPERATION |
429 | * The texture's format is not transcodable (not |
430 | * ETC1S/BasisLZ or UASTC). |
431 | * @exception KTX_INVALID_OPERATION |
432 | * Supercompression global data is missing, i.e., |
433 | * the texture object is invalid. |
434 | * @exception KTX_INVALID_OPERATION |
435 | * Image data is missing, i.e., the texture object |
436 | * is invalid. |
437 | * @exception KTX_INVALID_OPERATION |
438 | * @p outputFormat is PVRTC1 but the texture does |
439 | * does not have power-of-two dimensions. |
440 | * @exception KTX_INVALID_VALUE @p outputFormat is invalid. |
441 | * @exception KTX_TRANSCODE_FAILED |
442 | * Something went wrong during transcoding. The |
443 | * texture object will be corrupted. |
444 | * @exception KTX_UNSUPPORTED_FEATURE |
445 | * KTX_TF_PVRTC_DECODE_TO_NEXT_POW2 was requested |
446 | * or the specified transcode target has not been |
447 | * included in the library being used. |
448 | * @exception KTX_OUT_OF_MEMORY Not enough memory to carry out transcoding. |
449 | */ |
450 | KTX_error_code |
451 | ktxTexture2_transcodeLzEtc1s(ktxTexture2* This, |
452 | alpha_content_e alphaContent, |
453 | ktxTexture2* prototype, |
454 | ktx_transcode_fmt_e outputFormat, |
455 | ktx_transcode_flags transcodeFlags) |
456 | { |
457 | DECLARE_PRIVATE(priv, This); |
458 | DECLARE_PRIVATE(protoPriv, prototype); |
459 | KTX_error_code result = KTX_SUCCESS; |
460 | |
461 | assert(This->supercompressionScheme == KTX_SS_BASIS_LZ); |
462 | |
463 | uint8_t* bgd = priv._supercompressionGlobalData; |
464 | ktxBasisLzGlobalHeader& bgdh = *reinterpret_cast<ktxBasisLzGlobalHeader*>(bgd); |
465 | if (!(bgdh.endpointsByteLength && bgdh.selectorsByteLength && bgdh.tablesByteLength)) { |
466 | debug_printf("ktxTexture_TranscodeBasis: missing endpoints, selectors or tables" ); |
467 | return KTX_FILE_DATA_ERROR; |
468 | } |
469 | |
470 | // Compute some helpful numbers. |
471 | // |
472 | // firstImages contains the indices of the first images for each level to |
473 | // ease finding the correct slice description when iterating from smallest |
474 | // level to largest or when randomly accessing them (t.b.c). The last array |
475 | // entry contains the total number of images, for calculating the offsets |
476 | // of the endpoints, etc. |
477 | uint32_t* firstImages = new uint32_t[This->numLevels+1]; |
478 | |
479 | // Temporary invariant value |
480 | uint32_t layersFaces = This->numLayers * This->numFaces; |
481 | firstImages[0] = 0; |
482 | for (uint32_t level = 1; level <= This->numLevels; level++) { |
483 | // NOTA BENE: numFaces * depth is only reasonable because they can't |
484 | // both be > 1. I.e there are no 3d cubemaps. |
485 | firstImages[level] = firstImages[level - 1] |
486 | + layersFaces * MAX(This->baseDepth >> (level - 1), 1); |
487 | } |
488 | uint32_t& imageCount = firstImages[This->numLevels]; |
489 | |
490 | if (BGD_TABLES_ADDR(0, bgdh, imageCount) + bgdh.tablesByteLength > priv._sgdByteLength) { |
491 | return KTX_FILE_DATA_ERROR; |
492 | } |
493 | // FIXME: Do more validation. |
494 | |
495 | // Prepare low-level transcoder for transcoding slices. |
496 | basist::basisu_lowlevel_etc1s_transcoder bit; |
497 | |
498 | // basisu_transcoder_state is used to find the previous frame when |
499 | // decoding a video P-Frame. It tracks the previous frame for each mip |
500 | // level. For cube map array textures we need to find the previous frame |
501 | // for each face so we a state per face. Although providing this is only |
502 | // needed for video, it is easier to always pass our own. |
503 | std::vector<basisu_transcoder_state> xcoderStates; |
504 | xcoderStates.resize(This->isVideo ? This->numFaces : 1); |
505 | |
506 | bit.decode_palettes(bgdh.endpointCount, BGD_ENDPOINTS_ADDR(bgd, imageCount), |
507 | bgdh.endpointsByteLength, |
508 | bgdh.selectorCount, BGD_SELECTORS_ADDR(bgd, bgdh, imageCount), |
509 | bgdh.selectorsByteLength); |
510 | |
511 | bit.decode_tables(BGD_TABLES_ADDR(bgd, bgdh, imageCount), |
512 | bgdh.tablesByteLength); |
513 | |
514 | // Find matching VkFormat and calculate output sizes. |
515 | |
516 | const bool isVideo = This->isVideo; |
517 | |
518 | ktx_uint8_t* pXcodedData = prototype->pData; |
519 | // Inconveniently, the output buffer size parameter of transcode_image |
520 | // has to be in pixels for uncompressed output and in blocks for |
521 | // compressed output. The only reason for humouring the API is so |
522 | // its buffer size tests provide a real check. An alternative is to |
523 | // always provide the size in bytes which will always pass. |
524 | ktx_uint32_t outputBlockByteLength |
525 | = prototype->_protected->_formatSize.blockSizeInBits / 8; |
526 | ktx_size_t xcodedDataLength |
527 | = prototype->dataSize / outputBlockByteLength; |
528 | ktxLevelIndexEntry* protoLevelIndex; |
529 | uint64_t levelOffsetWrite; |
530 | const ktxBasisLzEtc1sImageDesc* imageDescs = BGD_ETC1S_IMAGE_DESCS(bgd); |
531 | |
532 | // Finally we're ready to transcode the slices. |
533 | |
534 | // FIXME: Iframe flag needs to be queryable by the application. In Basis |
535 | // the app can query file_info and image_info from the transcoder which |
536 | // returns a structure with lots of info about the image. |
537 | |
538 | protoLevelIndex = protoPriv._levelIndex; |
539 | levelOffsetWrite = 0; |
540 | for (int32_t level = This->numLevels - 1; level >= 0; level--) { |
541 | uint64_t levelOffset = ktxTexture2_levelDataOffset(This, level); |
542 | uint64_t writeOffset = levelOffsetWrite; |
543 | uint64_t writeOffsetBlocks = levelOffsetWrite / outputBlockByteLength; |
544 | uint32_t levelWidth = MAX(1, This->baseWidth >> level); |
545 | uint32_t levelHeight = MAX(1, This->baseHeight >> level); |
546 | // ETC1S texel block dimensions |
547 | const uint32_t bw = 4, bh = 4; |
548 | uint32_t levelBlocksX = (levelWidth + (bw - 1)) / bw; |
549 | uint32_t levelBlocksY = (levelHeight + (bh - 1)) / bh; |
550 | uint32_t depth = MAX(1, This->baseDepth >> level); |
551 | //uint32_t faceSlices = This->numFaces == 1 ? depth : This->numFaces; |
552 | uint32_t faceSlices = This->numFaces * depth; |
553 | uint32_t numImages = This->numLayers * faceSlices; |
554 | uint32_t image = firstImages[level]; |
555 | uint32_t endImage = image + numImages; |
556 | ktx_size_t levelImageSizeOut, levelSizeOut; |
557 | uint32_t stateIndex = 0; |
558 | |
559 | levelSizeOut = 0; |
560 | // FIXME: Figure out a way to get the size out of the transcoder. |
561 | levelImageSizeOut = ktxTexture2_GetImageSize(prototype, level); |
562 | for (; image < endImage; image++) { |
563 | const ktxBasisLzEtc1sImageDesc& imageDesc = imageDescs[image]; |
564 | |
565 | basisu_transcoder_state& xcoderState = xcoderStates[stateIndex]; |
566 | // We have face0 [face1 ...] within each layer. Use `stateIndex` |
567 | // rather than a double loop of layers and faceSlices as this |
568 | // works for 3d texture and non-array cube maps as well as |
569 | // cube map arrays without special casing. |
570 | if (++stateIndex == xcoderStates.size()) |
571 | stateIndex = 0; |
572 | |
573 | if (alphaContent != eNone) |
574 | { |
575 | // The slice descriptions should have alpha information. |
576 | if (imageDesc.alphaSliceByteOffset == 0 |
577 | || imageDesc.alphaSliceByteLength == 0) |
578 | return KTX_FILE_DATA_ERROR; |
579 | } |
580 | |
581 | bool status; |
582 | status = bit.transcode_image( |
583 | (transcoder_texture_format)outputFormat, |
584 | pXcodedData + writeOffset, |
585 | (uint32_t)(xcodedDataLength - writeOffsetBlocks), |
586 | This->pData, |
587 | (uint32_t)This->dataSize, |
588 | levelBlocksX, |
589 | levelBlocksY, |
590 | levelWidth, |
591 | levelHeight, |
592 | level, |
593 | (uint32_t)(levelOffset + imageDesc.rgbSliceByteOffset), |
594 | imageDesc.rgbSliceByteLength, |
595 | (uint32_t)(levelOffset + imageDesc.alphaSliceByteOffset), |
596 | imageDesc.alphaSliceByteLength, |
597 | transcodeFlags, |
598 | alphaContent != eNone, |
599 | isVideo, |
600 | // Our P-Frame flag is in the same bit as |
601 | // cSliceDescFlagsFrameIsIFrame. We have to |
602 | // invert it to make it an I-Frame flag. |
603 | // |
604 | // API currently doesn't have any way to pass |
605 | // the I-Frame flag. |
606 | //imageDesc.imageFlags ^ cSliceDescFlagsFrameIsIFrame, |
607 | 0, // output_row_pitch_in_blocks_or_pixels |
608 | &xcoderState, |
609 | 0 // output_rows_in_pixels |
610 | ); |
611 | if (!status) { |
612 | result = KTX_TRANSCODE_FAILED; |
613 | goto cleanup; |
614 | } |
615 | |
616 | writeOffset += levelImageSizeOut; |
617 | levelSizeOut += levelImageSizeOut; |
618 | } // end images loop |
619 | protoLevelIndex[level].byteOffset = levelOffsetWrite; |
620 | protoLevelIndex[level].byteLength = levelSizeOut; |
621 | protoLevelIndex[level].uncompressedByteLength = levelSizeOut; |
622 | levelOffsetWrite += levelSizeOut; |
623 | assert(levelOffsetWrite == writeOffset); |
624 | // In case of transcoding to uncompressed. |
625 | levelOffsetWrite = _KTX_PADN(protoPriv._requiredLevelAlignment, |
626 | levelOffsetWrite); |
627 | } // level loop |
628 | |
629 | result = KTX_SUCCESS; |
630 | |
631 | cleanup: |
632 | delete[] firstImages; |
633 | return result; |
634 | } |
635 | |
636 | |
637 | KTX_error_code |
638 | ktxTexture2_transcodeUastc(ktxTexture2* This, |
639 | alpha_content_e alphaContent, |
640 | ktxTexture2* prototype, |
641 | ktx_transcode_fmt_e outputFormat, |
642 | ktx_transcode_flags transcodeFlags) |
643 | { |
644 | assert(This->supercompressionScheme != KTX_SS_BASIS_LZ); |
645 | |
646 | ktx_uint8_t* pXcodedData = prototype->pData; |
647 | ktx_uint32_t outputBlockByteLength |
648 | = prototype->_protected->_formatSize.blockSizeInBits / 8; |
649 | ktx_size_t xcodedDataLength |
650 | = prototype->dataSize / outputBlockByteLength; |
651 | DECLARE_PRIVATE(protoPriv, prototype); |
652 | ktxLevelIndexEntry* protoLevelIndex = protoPriv._levelIndex; |
653 | ktx_size_t levelOffsetWrite = 0; |
654 | |
655 | basisu_lowlevel_uastc_transcoder uit; |
656 | // See comment on same declaration in transcodeEtc1s. |
657 | std::vector<basisu_transcoder_state> xcoderStates; |
658 | xcoderStates.resize(This->isVideo ? This->numFaces : 1); |
659 | |
660 | for (ktx_int32_t level = This->numLevels - 1; level >= 0; level--) |
661 | { |
662 | ktx_uint32_t depth; |
663 | uint64_t writeOffset = levelOffsetWrite; |
664 | uint64_t writeOffsetBlocks = levelOffsetWrite / outputBlockByteLength; |
665 | ktx_size_t levelImageSizeIn, levelImageOffsetIn; |
666 | ktx_size_t levelImageSizeOut, levelSizeOut; |
667 | ktx_uint32_t levelImageCount; |
668 | uint32_t levelWidth = MAX(1, This->baseWidth >> level); |
669 | uint32_t levelHeight = MAX(1, This->baseHeight >> level); |
670 | // UASTC texel block dimensions |
671 | const uint32_t bw = 4, bh = 4; |
672 | uint32_t levelBlocksX = (levelWidth + (bw - 1)) / bw; |
673 | uint32_t levelBlocksY = (levelHeight + (bh - 1)) / bh; |
674 | uint32_t stateIndex = 0; |
675 | |
676 | depth = MAX(1, This->baseDepth >> level); |
677 | |
678 | levelImageCount = This->numLayers * This->numFaces * depth; |
679 | levelImageSizeIn = ktxTexture_calcImageSize(ktxTexture(This), level, |
680 | KTX_FORMAT_VERSION_TWO); |
681 | levelImageSizeOut = ktxTexture_calcImageSize(ktxTexture(prototype), |
682 | level, |
683 | KTX_FORMAT_VERSION_TWO); |
684 | |
685 | levelImageOffsetIn = ktxTexture2_levelDataOffset(This, level); |
686 | levelSizeOut = 0; |
687 | bool status; |
688 | for (uint32_t image = 0; image < levelImageCount; image++) { |
689 | basisu_transcoder_state& xcoderState = xcoderStates[stateIndex]; |
690 | // See comment before same lines in transcodeEtc1s. |
691 | if (++stateIndex == xcoderStates.size()) |
692 | stateIndex = 0; |
693 | |
694 | status = uit.transcode_image( |
695 | (transcoder_texture_format)outputFormat, |
696 | pXcodedData + writeOffset, |
697 | (uint32_t)(xcodedDataLength - writeOffsetBlocks), |
698 | This->pData, |
699 | (uint32_t)This->dataSize, |
700 | levelBlocksX, |
701 | levelBlocksY, |
702 | levelWidth, |
703 | levelHeight, |
704 | level, |
705 | (uint32_t)levelImageOffsetIn, |
706 | (uint32_t)levelImageSizeIn, |
707 | transcodeFlags, |
708 | alphaContent != eNone, |
709 | This->isVideo, // is_video |
710 | //imageDesc.imageFlags ^ cSliceDescFlagsFrameIsIFrame, |
711 | 0, // output_row_pitch_in_blocks_or_pixels |
712 | &xcoderState, // pState |
713 | 0, // output_rows_in_pixels, |
714 | -1, // channel0 |
715 | -1 // channel1 |
716 | ); |
717 | if (!status) |
718 | return KTX_TRANSCODE_FAILED; |
719 | writeOffset += levelImageSizeOut; |
720 | levelSizeOut += levelImageSizeOut; |
721 | levelImageOffsetIn += levelImageSizeIn; |
722 | } |
723 | protoLevelIndex[level].byteOffset = levelOffsetWrite; |
724 | // writeOffset will be equal to total size of the images in the level. |
725 | protoLevelIndex[level].byteLength = levelSizeOut; |
726 | protoLevelIndex[level].uncompressedByteLength = levelSizeOut; |
727 | levelOffsetWrite += levelSizeOut; |
728 | } |
729 | // In case of transcoding to uncompressed. |
730 | levelOffsetWrite = _KTX_PADN(protoPriv._requiredLevelAlignment, |
731 | levelOffsetWrite); |
732 | return KTX_SUCCESS; |
733 | } |
734 | |