From b053253096dd78a757592bd1aa24e1ffe305336f Mon Sep 17 00:00:00 2001 From: Mark Callow <2244683+MarkCallow@users.noreply.github.com> Date: Sun, 15 Aug 2021 20:30:58 +0900 Subject: [PATCH] Support array and 3d textures. (#468) * Fix array & 3d texture creation. * Disable mipmap generation for 3d textures as they need completely different resampling code. * Add tests. * Fix ktxTexture2_SetImageFromStream's failure to check the return value of ktxTexture_GetImageOffset. Co-authored-by: unknown --- lib/writer2.c | 9 ++- tests/srcimages/blue16.png | 3 + tests/srcimages/green16.png | 3 + tests/srcimages/indigo16.png | 3 + tests/srcimages/orange16.png | 3 + tests/srcimages/red16.png | 3 + tests/srcimages/violet16.png | 3 + tests/srcimages/yellow16.png | 3 + tests/testimages/3dtex_7_reference_u.ktx2 | 3 + .../arraytex_7_mipmap_reference_u.ktx2 | 3 + tests/testimages/arraytex_7_reference_u.ktx2 | 3 + tests/testimages/genref | 6 +- tests/toktx-tests.cmake | 15 +++- tools/toktx/toktx.cc | 73 ++++++++++++++----- 14 files changed, 109 insertions(+), 24 deletions(-) create mode 100644 tests/srcimages/blue16.png create mode 100644 tests/srcimages/green16.png create mode 100644 tests/srcimages/indigo16.png create mode 100644 tests/srcimages/orange16.png create mode 100644 tests/srcimages/red16.png create mode 100644 tests/srcimages/violet16.png create mode 100644 tests/srcimages/yellow16.png create mode 100644 tests/testimages/3dtex_7_reference_u.ktx2 create mode 100644 tests/testimages/arraytex_7_mipmap_reference_u.ktx2 create mode 100644 tests/testimages/arraytex_7_reference_u.ktx2 diff --git a/lib/writer2.c b/lib/writer2.c index 3e19608c8c..75d4b4db57 100644 --- a/lib/writer2.c +++ b/lib/writer2.c @@ -126,6 +126,7 @@ ktxTexture2_setImageFromStream(ktxTexture2* This, ktx_uint32_t level, { ktx_size_t imageByteLength; ktx_size_t imageByteOffset; + ktx_error_code_e result; if (!This || !src) return KTX_INVALID_VALUE; @@ -133,8 +134,12 @@ ktxTexture2_setImageFromStream(ktxTexture2* This, ktx_uint32_t level, if (!This->pData) return KTX_INVALID_OPERATION; - ktxTexture_GetImageOffset(ktxTexture(This), level, layer, faceSlice, - &imageByteOffset); + result = ktxTexture_GetImageOffset(ktxTexture(This), + level, layer, faceSlice, + &imageByteOffset); + if (result != KTX_SUCCESS) + return result; + imageByteLength = ktxTexture_GetImageSize(ktxTexture(This), level); if (srcSize != imageByteLength) diff --git a/tests/srcimages/blue16.png b/tests/srcimages/blue16.png new file mode 100644 index 0000000000..0afe26dcf0 --- /dev/null +++ b/tests/srcimages/blue16.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e22025642f84649132c1889e976c6541ec811e2dccfa674b6972d18b71ad1d9d +size 281 diff --git a/tests/srcimages/green16.png b/tests/srcimages/green16.png new file mode 100644 index 0000000000..cb91c57777 --- /dev/null +++ b/tests/srcimages/green16.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:652ffe431550a0c5dd1c22ce0a7f15f5f31c45df9b3b8d76ed1a0784d2e5180f +size 281 diff --git a/tests/srcimages/indigo16.png b/tests/srcimages/indigo16.png new file mode 100644 index 0000000000..9b6ecb9dd8 --- /dev/null +++ b/tests/srcimages/indigo16.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:95824e61a3386244880356b7f8a170eb7529e05663321f0d9ca5a688cc1e2502 +size 281 diff --git a/tests/srcimages/orange16.png b/tests/srcimages/orange16.png new file mode 100644 index 0000000000..6c711fa674 --- /dev/null +++ b/tests/srcimages/orange16.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:1b946f0e4cbf677352a4c1e1e5895cd98ddbbb8c61a4873351cd944e70d64425 +size 281 diff --git a/tests/srcimages/red16.png b/tests/srcimages/red16.png new file mode 100644 index 0000000000..f1c0cd728e --- /dev/null +++ b/tests/srcimages/red16.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:062d79eb926321a7055208eef53de8948c6689b5cd416f15517562da68d50910 +size 281 diff --git a/tests/srcimages/violet16.png b/tests/srcimages/violet16.png new file mode 100644 index 0000000000..8de8b00196 --- /dev/null +++ b/tests/srcimages/violet16.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a5ed32f3763354d0d73cc0db46fba93538070649a09df7faa04832a12d749f5a +size 281 diff --git a/tests/srcimages/yellow16.png b/tests/srcimages/yellow16.png new file mode 100644 index 0000000000..217aa164e3 --- /dev/null +++ b/tests/srcimages/yellow16.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f533219662019d0827e04255887888f408eaaa6278d0df8e6e1a87b44d9045b5 +size 281 diff --git a/tests/testimages/3dtex_7_reference_u.ktx2 b/tests/testimages/3dtex_7_reference_u.ktx2 new file mode 100644 index 0000000000..41d837aba6 --- /dev/null +++ b/tests/testimages/3dtex_7_reference_u.ktx2 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:6cb8af8982268c0f7ce089970d689f483a8faf4cfae6792d18c7ec22e61caf2b +size 7452 diff --git a/tests/testimages/arraytex_7_mipmap_reference_u.ktx2 b/tests/testimages/arraytex_7_mipmap_reference_u.ktx2 new file mode 100644 index 0000000000..783572e2b5 --- /dev/null +++ b/tests/testimages/arraytex_7_mipmap_reference_u.ktx2 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:3db8173ae9869cf28cfe1d979a0d222bbccf5686145253553f4ea195d794fa2c +size 9928 diff --git a/tests/testimages/arraytex_7_reference_u.ktx2 b/tests/testimages/arraytex_7_reference_u.ktx2 new file mode 100644 index 0000000000..a11ffad895 --- /dev/null +++ b/tests/testimages/arraytex_7_reference_u.ktx2 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:9f029d85a3e29a4a9cb36e584a8a96153bf5649561206601c68f78d43c4e988c +size 7452 diff --git a/tests/testimages/genref b/tests/testimages/genref index 87ecb47332..ee36833061 100755 --- a/tests/testimages/genref +++ b/tests/testimages/genref @@ -121,4 +121,8 @@ $toktx --test --encode astc --astc_blk_d 8x6 astc_ldr_8x6_FlightHelmet_baseCo $toktx --test --encode astc --astc_blk_d 10x5 astc_ldr_10x5_FlightHelmet_baseColor.ktx2 ../srcimages/FlightHelmet_baseColor.png $toktx --test --encode astc --astc_blk_d 8x8 astc_ldr_8x8_FlightHelmet_baseColor.ktx2 ../srcimages/FlightHelmet_baseColor.png $toktx --test --encode astc --astc_blk_d 12x10 astc_ldr_12x10_FlightHelmet_baseColor.ktx2 ../srcimages/FlightHelmet_baseColor.png -$toktx --test --encode astc --astc_blk_d 12x12 astc_ldr_12x12_FlightHelmet_baseColor.ktx2 ../srcimages/FlightHelmet_baseColor.png \ No newline at end of file +$toktx --test --encode astc --astc_blk_d 12x12 astc_ldr_12x12_FlightHelmet_baseColor.ktx2 ../srcimages/FlightHelmet_baseColor.png + +$toktx --test --layers 7 --t2 arraytex_7_reference_u.ktx2 ../srcimages/red16.png ../srcimages/orange16.png ../srcimages/yellow16.png ../srcimages/green16.png ../srcimages/blue16.png ../srcimages/indigo16.png ../srcimages/violet16.png +$toktx --test --depth 7 --t2 3dtex_7_reference_u.ktx2 ../srcimages/red16.png ../srcimages/orange16.png ../srcimages/yellow16.png ../srcimages/green16.png ../srcimages/blue16.png ../srcimages/indigo16.png ../srcimages/violet16.png +$toktx --test --layers 7 --genmipmap --t2 arraytex_7_mipmap_reference_u.ktx2 ../srcimages/red16.png ../srcimages/orange16.png ../srcimages/yellow16.png ../srcimages/green16.png ../srcimages/blue16.png ../srcimages/indigo16.png ../srcimages/violet16.png diff --git a/tests/toktx-tests.cmake b/tests/toktx-tests.cmake index ad41bfa7a6..059c4e6eaa 100644 --- a/tests/toktx-tests.cmake +++ b/tests/toktx-tests.cmake @@ -83,6 +83,14 @@ add_test( NAME toktx-different-colortype-second-file-warning WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/testimages ) +add_test( NAME toktx-depth-layers + COMMAND toktx --depth 4 --layers 4 a b c d e +) + +add_test( NAME toktx-depth-genmipmap + COMMAND toktx --test --depth 7 --genmipmap --t2 3dtex_7_mipmap_reference_u.ktx2 ../srcimages/red16.png ../srcimages/orange16.png ../srcimages/yellow16.png ../srcimages/green16.png ../srcimages/blue16.png ../srcimages/indigo16.png ../srcimages/violet16.png +) + set_tests_properties( toktx-test-foobar toktx-automipmap-mipmaps @@ -98,6 +106,8 @@ set_tests_properties( toktx-invalid-swizzle-char toktx-invalid-target-type toktx-different-colortype-second-file-error + toktx-depth-layers + toktx-depth-genmipmap PROPERTIES WILL_FAIL TRUE ) @@ -227,4 +237,7 @@ gencmpktx( astc_ldr_8x6_FlightHelmet_baseColor astc_ldr_8x6_FlightHelmet_base gencmpktx( astc_ldr_10x5_FlightHelmet_baseColor astc_ldr_10x5_FlightHelmet_baseColor.ktx2 ../srcimages/FlightHelmet_baseColor.png "--test --encode astc --astc_blk_d 10x5" "" "") gencmpktx( astc_ldr_8x8_FlightHelmet_baseColor astc_ldr_8x8_FlightHelmet_baseColor.ktx2 ../srcimages/FlightHelmet_baseColor.png "--test --encode astc --astc_blk_d 8x8" "" "") gencmpktx( astc_ldr_12x10_FlightHelmet_baseColor astc_ldr_12x10_FlightHelmet_baseColor.ktx2 ../srcimages/FlightHelmet_baseColor.png "--test --encode astc --astc_blk_d 12x10" "" "") -gencmpktx( astc_ldr_12x12_FlightHelmet_baseColor astc_ldr_12x12_FlightHelmet_baseColor.ktx2 ../srcimages/FlightHelmet_baseColor.png "--test --encode astc --astc_blk_d 12x12" "" "") \ No newline at end of file +gencmpktx( astc_ldr_12x12_FlightHelmet_baseColor astc_ldr_12x12_FlightHelmet_baseColor.ktx2 ../srcimages/FlightHelmet_baseColor.png "--test --encode astc --astc_blk_d 12x12" "" "") +gencmpktx( 3dtex_7_reference_u 3dtex_7_reference_u.ktx2 ../srcimages/red16.png ../srcimages/orange16.png ../srcimages/yellow16.png ../srcimages/green16.png ../srcimages/blue16.png ../srcimages/indigo16.png ../srcimages/violet16.png "--test --t2 --depth 7" "" "") +gencmpktx( arraytex_7_reference_u arraytex_7_reference_u.ktx2 ../srcimages/red16.png ../srcimages/orange16.png ../srcimages/yellow16.png ../srcimages/green16.png ../srcimages/blue16.png ../srcimages/indigo16.png ../srcimages/violet16.png "--test --t2 --layers 7" "" "") +gencmpktx( arraytex_7_mipmap_reference_u arraytex_7_mipmap_reference_u.ktx2 ../srcimages/red16.png ../srcimages/orange16.png ../srcimages/yellow16.png ../srcimages/green16.png ../srcimages/blue16.png ../srcimages/indigo16.png ../srcimages/violet16.png "--test --t2 --layers 7 --genmipmap" "" "") diff --git a/tools/toktx/toktx.cc b/tools/toktx/toktx.cc index 015ff3f93e..e5e78e1ca3 100644 --- a/tools/toktx/toktx.cc +++ b/tools/toktx/toktx.cc @@ -717,9 +717,8 @@ toktxApp::main(int argc, _TCHAR *argv[]) else createInfo.numFaces = 1; - // TO DO: handle array textures createInfo.numLayers = options.layers; - createInfo.isArray = KTX_FALSE; + createInfo.isArray = options.layers > 1; // TO DO: handle 3D textures. @@ -1089,7 +1088,11 @@ toktxApp::main(int argc, _TCHAR *argv[]) createInfo.baseWidth = levelWidth = image->getWidth(); createInfo.baseHeight = levelHeight = image->getHeight(); createInfo.baseDepth = levelDepth = options.depth; - if (image->getHeight() == 1 && !options.two_d) + if (options.depth > 1) { + // In this case, don't care about image->getHeight(). Images are + // always considered to be 2d. No need to set options.two_d. + createInfo.numDimensions = 3; + } else if (image->getHeight() == 1 && !options.two_d) createInfo.numDimensions = 1; else createInfo.numDimensions = 2; @@ -1172,6 +1175,7 @@ toktxApp::main(int argc, _TCHAR *argv[]) level = 0; levelWidth = createInfo.baseWidth; levelHeight = createInfo.baseHeight; + levelDepth = createInfo.baseDepth; if (faceSlice == (options.cubemap ? 6 : levelDepth)) { faceSlice = 0; layer++; @@ -1198,12 +1202,19 @@ toktxApp::main(int argc, _TCHAR *argv[]) cout << ", imageSize = " << imageSize << endl; } #endif - ktxTexture_SetImageFromMemory(ktxTexture(texture), - level, - layer, - faceSlice, - *image, - image->getByteCount()); + ret = ktxTexture_SetImageFromMemory(ktxTexture(texture), + level, + layer, + faceSlice, + *image, + image->getByteCount()); + // Only an error in this program could lead to ret != SUCCESS + // hence no user message. + assert(ret == KTX_SUCCESS); + + // This does not work for mipmaps for 3d textures. For those it is + // necessary to present the base images for each slice to a + // resampler that can sample across images. if (options.genmipmap) { for (uint32_t glevel = 1; glevel < createInfo.numLevels; glevel++) { @@ -1230,12 +1241,13 @@ toktxApp::main(int argc, _TCHAR *argv[]) //if (options.gmopts.mipRenormalize) // levelImage->renormalize_normal_map(); - ktxTexture_SetImageFromMemory(ktxTexture(texture), + ret = ktxTexture_SetImageFromMemory(ktxTexture(texture), glevel, layer, faceSlice, *levelImage, levelImage->getByteCount()); + assert(ret == KTX_SUCCESS); delete levelImage; } } @@ -1255,21 +1267,36 @@ toktxApp::main(int argc, _TCHAR *argv[]) /* * Add orientation metadata. - * Note: 1D textures and 2D textures with a height of 1 don't need - * orientation metadata */ - if (options.metadata && createInfo.baseHeight > 1) { + if (options.metadata) { ktxHashList* ht = &texture->kvDataHead; - char orientation[10]; + char orientation[20]; if (options.ktx2) { orientation[0] = 'r'; - orientation[1] = options.lower_left_maps_to_s0t0 ? 'u' : 'd'; - orientation[2] = 0; + if (createInfo.baseHeight > 1) { + orientation[1] = options.lower_left_maps_to_s0t0 ? 'u' : 'd'; + if (createInfo.baseDepth > 1) { + orientation[2] = options.lower_left_maps_to_s0t0 + ? 'o' : 'i'; + } + else { + orientation[2] = 0; + } + } else { + orientation[1] = 0; + } } else { - assert(strlen(KTX_ORIENTATION2_FMT) < sizeof(orientation)); - - snprintf(orientation, sizeof(orientation), KTX_ORIENTATION2_FMT, - 'r', options.lower_left_maps_to_s0t0 ? 'u' : 'd'); + assert(strlen(KTX_ORIENTATION3_FMT) < sizeof(orientation)); + if (createInfo.baseHeight == 1) { + snprintf(orientation, sizeof(orientation), KTX_ORIENTATION1_FMT, + 'r'); + } else if (createInfo.baseDepth == 1) { + snprintf(orientation, sizeof(orientation), KTX_ORIENTATION2_FMT, + 'r', options.lower_left_maps_to_s0t0 ? 'u' : 'd'); + } else + snprintf(orientation, sizeof(orientation), KTX_ORIENTATION3_FMT, + 'r', options.lower_left_maps_to_s0t0 ? 'u' : 'd', + options.lower_left_maps_to_s0t0 ? 'o' : 'i'); } ktxHashList_AddKVPair(ht, KTX_ORIENTATION_KEY, (unsigned int)strlen(orientation) + 1, @@ -1467,6 +1494,12 @@ toktxApp::validateOptions() exit(1); } + if (options.depth > 1 && options.genmipmap) { + error("generation of mipmaps for 3d textures is not supported.\n" + "A PR to add this feature will be gratefully accepted!"); + exit(1); + } + if (options.outfile.compare(_T("-")) != 0 && options.outfile.find_last_of('.') == _tstring::npos) {