diff --git a/.appveyor.yml b/.appveyor.yml index 425090564d..30203b0fcc 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -145,7 +145,7 @@ build: verbosity: detailed on_failure: - - ps: $env:APPVEYOR_BUILD_FOLDER/ci_scripts/on_failure.ps1 + - ps: $env:APPVEYOR_BUILD_FOLDER/ci_scripts/on_failure.ps1 $env:APPVEYOR_BUILD_FOLDER $env:BUILD_DIR # Following is for GIT_TRACE: 1 above. # - ps: Get-ChildItem .\.git\lfs\objects\logs\*.log | % { Push-AppveyorArtifact $_.FullName -FileName $_.Name } @@ -164,15 +164,22 @@ init: # - ps: '$blockRdp = $true; iex ((new-object net.webclient).DownloadString(''https://raw.githubusercontent.com/appveyor/ci/master/scripts/enable-rdp.ps1''))' # - ps: | #iex ((new-object net.webclient).DownloadString('https://raw.githubusercontent.com/appveyor/ci/master/scripts/enable-rdp.ps1')) + # N.B.: for some reason indenting "- cmd" on the following lines causes syntax -# errors. Not using PS for downloads because its curl is completely different. +# errors. install: - if not "%appveyor_build_worker_image%" == "Visual Studio 2019" cinst doxygen.install #- ps: | #run mkversion #Update-AppveyorBuild -Version "$($env:ospsuite_version).$($env:appveyor_build_version)" - # Use cmd.exe because PS curl is very different. + # Not using PS for install because "curl" is an alias for + # Invoke-WebRequest a completely different command that is difficult to + # use for downloads. When I wrote the .bat script, so I could use the + # real curl, I did not know PS's curl was just an alias and I could get + # the real curl with "curl.exe". Doh! Now it is a case of if it ain't + # broke don't fix it. + # # To avoid going down a rathole note very well that in at least some of # the CI environments, cmd.exe does not support pushd and popd. Sigh! # Also keep quotes in following echos & rems to avoid issues with diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 2e993a05c2..b099597997 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -124,12 +124,19 @@ jobs: fetch-depth: 0 - uses: seanmiddleditch/gha-setup-ninja@v3 - uses: Honeybunch/setup-mingw@v3 + - name: Pull test images from Git LFS + run: git lfs pull --include=tests/srcimages,tests/testimages - name: Configure Mingw x64 run: cmake -B build -G "Ninja Multi-Config" -DCMAKE_C_COMPILER=gcc -DCMAKE_CXX_COMPILER=g++ - name: Build Mingw x64 Debug run: cmake --build build --config Debug - name: Build Mingw x64 Release run: cmake --build build --config Release + - name: Test Mingw build + run: ctest --test-dir build -C Release + - name: Upload test log + if: ${{ failure() }} + run: ci_scripts/on_failure.ps1 linux: diff --git a/ci_scripts/on_failure.ps1 b/ci_scripts/on_failure.ps1 index 7960b64b53..93f08164b7 100755 --- a/ci_scripts/on_failure.ps1 +++ b/ci_scripts/on_failure.ps1 @@ -1,12 +1,26 @@ # Copyright 2022 The Khronos Group Inc. # SPDX-License-Identifier: Apache-2.0 +if ($args[0]) { $repo_root = $args[0] } else { $repo_root = "." } +if ($args[1]) { $build_dir = $args[1] } else { $build_dir = "build" } + +# In Github Actions, this is called only when there is a failed test step. +# In Appveyor we use the Phase environment variable. echo "Phase = $env:Phase" -if ($env:Phase) { - echo "Now uploading the failed tests" - ls -Name $env:APPVEYOR_BUILD_FOLDER/tests/testimages/toktx* - tar -cvf failed-tests.tar $env:APPVEYOR_BUILD_FOLDER/tests/testimages/toktx* - curl --upload-file failed-tests.tar https://transfer.sh/toktx-failed-tests.tar - echo "Now uploading the test log" - curl --upload-file $env:APPVEYOR_BUILD_FOLDER/$env:BUILD_DIR/Testing/Temporary/LastTest.log https://transfer.sh/ktx-last-test.log +if ($env:GITHUB_ACTIONS -or $env:Phase) { + pushd $repo_root + echo "Now uploading the failed tests" + ls -Name tests/testimages/ktxsc* tests/testimages/toktx* + if (tar -cvf failed-images.tar tests/testimages/ktxsc* tests/testimages/toktx*) { + # N.B. In PS, "curl" is an alias for Invoke-WebRequest. Uploading a + # file via that looks like a p.i.t.a so use the real curl command. + curl.exe --upload-file failed-images.tar https://transfer.sh/ktx-failed-images.tar + } + # Even if there are no failed images and tar exits with false it creates + # the output file. + rm failed-images.tar + + echo "`r`nNow uploading the test log" + curl.exe --upload-file $build_dir/Testing/Temporary/LastTest.log https://transfer.sh/ktx-last-test.log + popd } diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 989395f4d3..972f724b69 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -14,5 +14,6 @@ endif() # tools tests if(KTX_FEATURE_TOOLS) include( ktx2check-tests.cmake ) + include( ktxsc-tests.cmake ) include( toktx-tests.cmake ) endif() diff --git a/tests/ktxsc-tests.cmake b/tests/ktxsc-tests.cmake new file mode 100644 index 0000000000..ace0da9de7 --- /dev/null +++ b/tests/ktxsc-tests.cmake @@ -0,0 +1,95 @@ +# -*- tab-width: 4; -*- +# vi: set sw=2 ts=4 expandtab: + +# Copyright 2022 Mark Callow +# SPDX-License-Identifier: Apache-2.0 + +# toktx share a common scapp class with ktxsc so the toktx tests suffice +# for testing actual compression. + +add_test( NAME ktxsc-test-help + COMMAND ktxsc --help +) +set_tests_properties( + ktxsc-test-help +PROPERTIES + PASS_REGULAR_EXPRESSION "^Usage: ktxsc" +) + +add_test( NAME ktxsc-test-version + COMMAND ktxsc --version +) +set_tests_properties( + ktxsc-test-version +PROPERTIES + PASS_REGULAR_EXPRESSION "^ktxsc v[0-9][0-9\\.]+" +) + +# The near duplication of this and other tests below is due to a "limitation" +# (i.e. a bug) in ctest which checks for neither a zero error code when a +# PASS_REGULAR_EXPRESSION is specified nor a non-zero error code when a +# FAIL_REGULAR_EXPRESSION is specified but only for matches to the REs. +add_test( NAME ktxsc-test-foobar + COMMAND ktxsc --foobar +) +set_tests_properties( + ktxsc-test-foobar +PROPERTIES + WILL_FAIL TRUE + FAIL_REGULAR_EXPRESSION "^Usage: ktxsc" +) +add_test( NAME ktxsc-test-foobar-exit-code + COMMAND ktxsc --foobar +) +set_tests_properties( + ktxsc-test-foobar-exit-code +PROPERTIES + WILL_FAIL TRUE +) + +add_test( NAME ktxsc-test-many-in-one-out + COMMAND ktxsc -o foo a.ktx2 b.ktx2 c.ktx2 +) +set_tests_properties( + ktxsc-test-many-in-one-out +PROPERTIES + WILL_FAIL TRUE + FAIL_REGULAR_EXPRESSION "^Can't use -o when there are multiple infiles." +) + +add_test( NAME ktxsc-test-many-in-one-out-exit-code + COMMAND ktxsc -o foo a.ktx2 b.ktx2 c.ktx2 +) +set_tests_properties( + ktxsc-test-many-in-one-out-exit-code +PROPERTIES + WILL_FAIL TRUE +) + +set( IMG_DIR "${CMAKE_CURRENT_SOURCE_DIR}/testimages" ) + +function( gencmpktx test_name reference source args inplace ) + if (NOT inplace) + set( workfile ktxsc.${reference} ) + add_test( NAME ktxsc-cmp-${test_name} + COMMAND ${BASH_EXECUTABLE} -c "$ --test ${args} -o ${workfile} ${source} && diff ${reference} ${workfile} && rm ${workfile}" + WORKING_DIRECTORY ${IMG_DIR} + ) + elseif(${inplace} STREQUAL "cur-dir") + set( workfile ktxsc.ip1.${reference} ) + add_test( NAME ktxsc-cmp-${test_name} + COMMAND ${BASH_EXECUTABLE} -c "cp ${source} ${workfile} && $ --test ${args} ${workfile} && diff ${reference} ${workfile} && rm ${workfile}" + WORKING_DIRECTORY ${IMG_DIR} + ) + elseif(${inplace} STREQUAL "different-dir") + set( workfile ktxsc.ip2.${reference} ) + add_test( NAME ktxsc-cmp-${test_name} + COMMAND ${BASH_EXECUTABLE} -c "cp ${source} ${workfile} && pushd ../.. && $ --test ${args} ${IMG_DIR}/${workfile} && popd && diff ${reference} ${workfile} && rm ${workfile}" + WORKING_DIRECTORY ${IMG_DIR} + ) + endif() +endfunction() + +gencmpktx( compress-explicit-output skybox_zstd.ktx2 skybox.ktx2 "--zcmp 5" "" "" ) +gencmpktx( compress-in-place-cur-dir skybox_zstd.ktx2 skybox.ktx2 "--zcmp 5" "cur-dir" ) +gencmpktx( compress-in-place-different-dir skybox_zstd.ktx2 skybox.ktx2 "--zcmp 5" "different-dir" ) diff --git a/tests/testimages/genktx2 b/tests/testimages/genktx2 index ab818649ea..b4412b3367 100755 --- a/tests/testimages/genktx2 +++ b/tests/testimages/genktx2 @@ -13,72 +13,30 @@ depth=../.. # Change dir to the testimages folder, the script location... cd $(dirname $0) -# Make paths relative to the testimages directory. -ktx_root=$depth -toktx_vs2013=$ktx_root/build/msvs/win/vs2013/x64/Release/toktx.exe -toktx_vs2015=$ktx_root/build/msvs/win/vs2015/x64/Release/toktx.exe -toktx_cmake=$ktx_root/build/cmake/linux/Release/toktx -toktx_cmake_d=$ktx_root/build/cmake/linux/Debug/toktx -toktx_make=$ktx_root/build/make/linux/out/Release/toktx -toktx_make_d=$ktx_root/build/make/linux/out/Debug/toktx - -ktx2ktx2_vs2013=$ktx_root/build/msvs/win/vs2013/x64/Release/ktx2ktx2.exe -ktx2ktx2_vs2015=$ktx_root/build/msvs/win/vs2015/x64/Release/ktx2ktx2.exe -ktx2ktx2_cmake=$ktx_root/build/cmake/linux/Release/ktx2ktx2 -ktx2ktx2_cmake_d=$ktx_root/build/cmake/linux/Debug/ktx2ktx2 -ktx2ktx2_make=$ktx_root/build/make/linux/out/Release/ktx2ktx2 -ktx2ktx2_make_d=$ktx_root/build/make/linux/out/Debug/ktx2ktx2 - # Ensure generation is not polluted by user environment unset TOKTX_OPTIONS if [ -n "$1" -a -x "$1" ]; then toktx="$1" -elif [ -x "$toktx_vs2013" ]; then - toktx=$toktx_vs2013 -elif [ -x "$toktx_vs2015" ]; then - toktx=$toktx_vs2015 -elif [ -x "$toktx_cmake" ]; then - toktx=$toktx_cmake -elif [ -x "$toktx_cmake_d" ]; then - toktx=$toktx_cmake_d -elif [ -x "$toktx_make" ]; then - toktx=$toktx_gmake -elif [ -x "$toktx_make_d" ]; then - toktx=$toktx_gmake elif which toktx >/dev/null; then toktx=toktx else - echo $0: None of $toktx_vs2013, $toktx_vs2015, $toktx_gmake or $toktx_cmake found. - echo $0: Nor was toktx found in along PATH. + echo $0: toktx not found along $PATH. echo $0: Aborting generation exit 1 fi if [ -n "$2" -a -x "$2" ]; then - ktx2ktx2="$1" -elif [ -x "$ktx2ktx2_vs2013" ]; then - ktx2ktx2=$ktx2ktx2_vs2013 -elif [ -x "$ktx2ktx2_vs2015" ]; then - ktx2ktx2=$ktx2ktx2_vs2015 -elif [ -x "$ktx2ktx2_cmake" ]; then - ktx2ktx2=$ktx2ktx2_cmake -elif [ -x "$ktx2ktx2_cmake_d" ]; then - ktx2ktx2=$ktx2ktx2_cmake_d -elif [ -x "$ktx2ktx2_make" ]; then - ktx2ktx2=$ktx2ktx2_gmake -elif [ -x "$ktx2ktx2_make_d" ]; then - ktx2ktx2=$ktx2ktx2_gmake + ktx2ktx2="$2" elif which ktx2ktx2 >/dev/null; then ktx2ktx2=ktx2ktx2 else - echo $0: None of $ktx2ktx2_vs2013, $ktx2ktx2_vs2015, $ktx2ktx2_gmake or $ktx2ktx2_cmake found. - echo $0: Nor was ktx2ktx2 found in along PATH. + echo $0: ktx2ktx2 not found along $PATH. echo $0: Aborting generation exit 1 fi -# Generate reference files for toktx-tests ... +# Generate some reference files for toktx-tests ... $ktx2ktx2 --test -f -o orient-down-metadata-u.ktx2 orient-down-metadata.ktx $ktx2ktx2 --test -f -o orient-up-metadata-u.ktx2 orient-up-metadata.ktx diff --git a/tests/testimages/genktx2icons b/tests/testimages/genktx2icons index 781e839968..3aa3f22694 100755 --- a/tests/testimages/genktx2icons +++ b/tests/testimages/genktx2icons @@ -17,67 +17,15 @@ ktxspec_dir=../../../KTX-Specification # Change dir to the testimages folder, the script location... cd $(dirname $0) -# Make paths relative to the testimages directory. -ktx_root=$depth -toktx_vs2013=$ktx_root/build/msvs/win/vs2013/x64/Release/toktx.exe -toktx_vs2015=$ktx_root/build/msvs/win/vs2015/x64/Release/toktx.exe -toktx_cmake=$ktx_root/build/cmake/linux/Release/toktx -toktx_cmake_d=$ktx_root/build/cmake/linux/Debug/toktx -toktx_make=$ktx_root/build/make/linux/out/Release/toktx -toktx_make_d=$ktx_root/build/make/linux/out/Debug/toktx - -ktx2ktx2_vs2013=$ktx_root/build/msvs/win/vs2013/x64/Release/ktx2ktx2.exe -ktx2ktx2_vs2015=$ktx_root/build/msvs/win/vs2015/x64/Release/ktx2ktx2.exe -ktx2ktx2_cmake=$ktx_root/build/cmake/linux/Release/ktx2ktx2 -ktx2ktx2_cmake_d=$ktx_root/build/cmake/linux/Debug/ktx2ktx2 -ktx2ktx2_make=$ktx_root/build/make/linux/out/Release/ktx2ktx2 -ktx2ktx2_make_d=$ktx_root/build/make/linux/out/Debug/ktx2ktx2 - # Ensure generation is not polluted by user environment unset TOKTX_OPTIONS if [ -n "$1" -a -x "$1" ]; then toktx="$1" -elif [ -x "$toktx_vs2013" ]; then - toktx=$toktx_vs2013 -elif [ -x "$toktx_vs2015" ]; then - toktx=$toktx_vs2015 -elif [ -x "$toktx_cmake" ]; then - toktx=$toktx_cmake -elif [ -x "$toktx_cmake_d" ]; then - toktx=$toktx_cmake_d -elif [ -x "$toktx_make" ]; then - toktx=$toktx_gmake -elif [ -x "$toktx_make_d" ]; then - toktx=$toktx_gmake elif which toktx >/dev/null; then toktx=toktx else - echo $0: None of $toktx_vs2013, $toktx_vs2015, $toktx_gmake or $toktx_cmake found. - echo $0: Nor was toktx found in along PATH. - echo $0: Aborting generation - exit 1 -fi - -if [ -n "$2" -a -x "$2" ]; then - ktx2ktx2="$1" -elif [ -x "$ktx2ktx2_vs2013" ]; then - ktx2ktx2=$ktx2ktx2_vs2013 -elif [ -x "$ktx2ktx2_vs2015" ]; then - ktx2ktx2=$ktx2ktx2_vs2015 -elif [ -x "$ktx2ktx2_cmake" ]; then - ktx2ktx2=$ktx2ktx2_cmake -elif [ -x "$ktx2ktx2_cmake_d" ]; then - ktx2ktx2=$ktx2ktx2_cmake_d -elif [ -x "$ktx2ktx2_make" ]; then - ktx2ktx2=$ktx2ktx2_gmake -elif [ -x "$ktx2ktx2_make_d" ]; then - ktx2ktx2=$ktx2ktx2_gmake -elif which ktx2ktx2 >/dev/null; then - ktx2ktx2=ktx2ktx2 -else - echo $0: None of $ktx2ktx2_vs2013, $ktx2ktx2_vs2015, $ktx2ktx2_gmake or $ktx2ktx2_cmake found. - echo $0: Nor was ktx2ktx2 found in along PATH. + echo $0: toktx not found along $PATH. echo $0: Aborting generation exit 1 fi diff --git a/tests/testimages/genref b/tests/testimages/genref index 765afe4fee..1883071500 100755 --- a/tests/testimages/genref +++ b/tests/testimages/genref @@ -13,42 +13,30 @@ depth=../.. # Change dir to the testimages folder, the script location... cd $(dirname $0) -# Make paths relative to the testimages directory. -ktx_root=$depth -toktx_vs2013=$ktx_root/build/msvs/win/vs2013/x64/Release/toktx.exe -toktx_vs2015=$ktx_root/build/msvs/win/vs2015/x64/Release/toktx.exe -toktx_cmake=$ktx_root/build/cmake/linux/Release/toktx -toktx_cmake_d=$ktx_root/build/cmake/linux/Debug/toktx -toktx_make=$ktx_root/build/make/linux/out/Release/toktx -toktx_make_d=$ktx_root/build/make/linux/out/Debug/toktx - # Ensure generation is not polluted by user environment unset TOKTX_OPTIONS -if [ -n "$1" -a -x "$1" ]; then +if [ -n "$1" -a -x "$2" ]; then toktx="$1" -elif [ -x "$toktx_vs2013" ]; then - toktx=$toktx_vs2013 -elif [ -x "$toktx_vs2015" ]; then - toktx=$toktx_vs2015 -elif [ -x "$toktx_cmake" ]; then - toktx=$toktx_cmake -elif [ -x "$toktx_cmake_d" ]; then - toktx=$toktx_cmake_d -elif [ -x "$toktx_make" ]; then - toktx=$toktx_gmake -elif [ -x "$toktx_make_d" ]; then - toktx=$toktx_gmake elif which toktx >/dev/null; then toktx=toktx else - echo $0: None of $toktx_vs2013, $toktx_vs2015, $toktx_gmake or $toktx_cmake found. - echo $0: Nor was toktx found in along PATH. + echo $0: toktx not found along $PATH. echo $0: Aborting generation exit 1 fi -# Generate the files ... +if [ -n "$2" -a -x "$1" ]; then + ktxsc="$2" +elif which ktxsc >/dev/null; then + ktxsc=ktxsc +else + echo $0: ktxsc not found along $PATH. + echo $0: Aborting generation + exit 1 +fi + +# Generate the reference files for toktx and sctests ... $toktx --lower_left_maps_to_s0t0 --nometadata rgb-reference.ktx ../srcimages/rgb.ppm # The purpose of this is testing automatic mipmap generation. Since SRGB is @@ -135,3 +123,5 @@ $toktx --test --depth 7 --encode astc --astc_blk_d 6x6 astc_ldr_6x6 $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 + +$ktxsc -o skybox_zstd.ktx2 --force --test --zcmp 5 skybox.ktx2 diff --git a/tests/testimages/skybox_zstd.ktx2 b/tests/testimages/skybox_zstd.ktx2 index 2fd763cd27..5e7ff0a08b 100644 --- a/tests/testimages/skybox_zstd.ktx2 +++ b/tests/testimages/skybox_zstd.ktx2 @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7de61c0bf96ab3dfc27c78266819d10911f30f630d1ab37e49ab3ac5b3a0a842 -size 15981086 +oid sha256:617d81289627bf03793139506fb538b09639bd66351c23243906b592478bd65a +size 15981070 diff --git a/tests/toktx-tests.cmake b/tests/toktx-tests.cmake index bd8294a362..78298d3e5f 100644 --- a/tests/toktx-tests.cmake +++ b/tests/toktx-tests.cmake @@ -121,12 +121,12 @@ PROPERTIES function( gencmpktx test_name reference source args env files ) if(files) add_test( NAME toktx-cmp-${test_name} - COMMAND ${BASH_EXECUTABLE} -c "printf \"${files}\" > ${source} && $ ${args} toktx.${reference} @${source} && diff ${reference} toktx.${reference} && rm toktx.${reference}; rm ${source}" + COMMAND ${BASH_EXECUTABLE} -c "printf \"${files}\" > ${source} && $ --test ${args} toktx.${reference} @${source} && diff ${reference} toktx.${reference} && rm toktx.${reference}; rm ${source}" WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/testimages ) else() add_test( NAME toktx-cmp-${test_name} - COMMAND ${BASH_EXECUTABLE} -c "$ ${args} toktx.${reference} ${source} && diff ${reference} toktx.${reference} && rm toktx.${reference}" + COMMAND ${BASH_EXECUTABLE} -c "$ --test ${args} toktx.${reference} ${source} && diff ${reference} toktx.${reference} && rm toktx.${reference}" WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/testimages ) endif() @@ -147,55 +147,55 @@ gencmpktx( orient-up-metadata orient-up-metadata.ktx "../srcimages/up.ppm" "--lo gencmpktx( orient-down-metadata orient-down-metadata.ktx ../srcimages/up.ppm "" "" "" ) gencmpktx( rgba-reference rgba-reference.ktx ../srcimages/rgba.pam "--lower_left_maps_to_s0t0 --nometadata" "" "" ) gencmpktx( rgb-mipmap-reference rgb-mipmap-reference.ktx "../srcimages/level0.ppm ../srcimages/level1.ppm ../srcimages/level2.ppm ../srcimages/level3.ppm ../srcimages/level4.ppm ../srcimages/level5.ppm ../srcimages/level6.ppm" "--lower_left_maps_to_s0t0 --mipmap --nometadata" "" "" ) -gencmpktx( rgb-mipmap-reference-u rgb-mipmap-reference-u.ktx2 "../srcimages/level0.ppm ../srcimages/level1.ppm ../srcimages/level2.ppm ../srcimages/level3.ppm ../srcimages/level4.ppm ../srcimages/level5.ppm ../srcimages/level6.ppm" "--test --t2 --mipmap" "" "" ) +gencmpktx( rgb-mipmap-reference-u rgb-mipmap-reference-u.ktx2 "../srcimages/level0.ppm ../srcimages/level1.ppm ../srcimages/level2.ppm ../srcimages/level3.ppm ../srcimages/level4.ppm ../srcimages/level5.ppm ../srcimages/level6.ppm" "--t2 --mipmap" "" "" ) if(APPLE) # Run only on macOS until we figure out the BasisLZ/ETC1S compressor non-determinancy. - gencmpktx( alpha_simple_basis alpha_simple_basis.ktx2 ../srcimages/alpha_simple.png "--test --bcmp" "" "" ) - gencmpktx( kodim17_basis kodim17_basis.ktx2 ../srcimages/kodim17.png "--test --bcmp" "" "" ) - gencmpktx( color_grid_basis color_grid_basis.ktx2 ../srcimages/color_grid.png "--test --bcmp" "" "" ) - gencmpktx( 16bit_png_basis camera_camera_BaseColor_basis.ktx2 ../srcimages/camera_camera_BaseColor_16bit.png "--bcmp --test --nowarn" "" "" ) - gencmpktx( paletted_png CesiumLogoFlat.ktx2 ../srcimages/CesiumLogoFlat_palette.png "--bcmp --test --nowarn" "" "" ) + gencmpktx( alpha_simple_basis alpha_simple_basis.ktx2 ../srcimages/alpha_simple.png "--bcmp" "" "" ) + gencmpktx( kodim17_basis kodim17_basis.ktx2 ../srcimages/kodim17.png "--bcmp" "" "" ) + gencmpktx( color_grid_basis color_grid_basis.ktx2 ../srcimages/color_grid.png "--bcmp" "" "" ) + gencmpktx( 16bit_png_basis camera_camera_BaseColor_basis.ktx2 ../srcimages/camera_camera_BaseColor_16bit.png "--bcmp --nowarn" "" "" ) + gencmpktx( paletted_png CesiumLogoFlat.ktx2 ../srcimages/CesiumLogoFlat_palette.png "--bcmp --nowarn" "" "" ) endif() gencmpktx( cimg5293_uastc cimg5293_uastc.ktx2 ../srcimages/CIMG5293.jpg "--uastc --genmipmap --test" "" "" ) gencmpktx( cimg5293_uastc_zstd cimg5293_uastc_zstd.ktx2 ../srcimages/CIMG5293.jpg "--zcmp --uastc --genmipmap --test" "" "" ) - gencmpktx( 16bit_png_uastc camera_camera_BaseColor_uastc.ktx2 ../srcimages/camera_camera_BaseColor_16bit.png "--uastc 1 --test --nowarn" "" "" ) + gencmpktx( 16bit_png_uastc camera_camera_BaseColor_uastc.ktx2 ../srcimages/camera_camera_BaseColor_16bit.png "--uastc 1 --nowarn" "" "" ) -gencmpktx( luminance_reference_u luminance_reference_u.ktx2 ../srcimages/luminance.pgm "--test --t2 --convert_oetf linear" "" "" ) -gencmpktx( luminance_reference_uastc luminance_reference_uastc.ktx2 ../srcimages/luminance.pgm "--test --t2 --uastc --" "" "" ) -gencmpktx( luminance_reference_basis luminance_reference_basis.ktx2 ../srcimages/luminance.pgm "--test --t2 --bcmp" "" "" ) -gencmpktx( luminance_alpha_reference_u luminance_alpha_reference_u.ktx2 ../srcimages/basn4a08.png "--test --t2" "" "" ) -gencmpktx( luminance_alpha_reference_uastc luminance_alpha_reference_uastc.ktx2 ../srcimages/basn4a08.png "--test --t2 --uastc --" "" "" ) +gencmpktx( luminance_reference_u luminance_reference_u.ktx2 ../srcimages/luminance.pgm "--t2 --convert_oetf linear" "" "" ) +gencmpktx( luminance_reference_uastc luminance_reference_uastc.ktx2 ../srcimages/luminance.pgm "--t2 --uastc --" "" "" ) +gencmpktx( luminance_reference_basis luminance_reference_basis.ktx2 ../srcimages/luminance.pgm "--t2 --bcmp" "" "" ) +gencmpktx( luminance_alpha_reference_u luminance_alpha_reference_u.ktx2 ../srcimages/basn4a08.png "--t2" "" "" ) +gencmpktx( luminance_alpha_reference_uastc luminance_alpha_reference_uastc.ktx2 ../srcimages/basn4a08.png "--t2 --uastc --" "" "" ) if(APPLE) -gencmpktx( luminance_alpha_reference_basis luminance_alpha_reference_basis.ktx2 ../srcimages/basn4a08.png "--test --t2 --bcmp" "" "" ) +gencmpktx( luminance_alpha_reference_basis luminance_alpha_reference_basis.ktx2 ../srcimages/basn4a08.png "--t2 --bcmp" "" "" ) endif() -gencmpktx( r_reference_u r_reference_u.ktx2 ../srcimages/luminance.pgm "--test --t2 --convert_oetf linear --target_type R" "" "" ) -gencmpktx( r_reference_uastc r_reference_uastc.ktx2 ../srcimages/luminance.pgm "--test --t2 --uastc --target_type R --swizzle r001 --" "" "" ) +gencmpktx( r_reference_u r_reference_u.ktx2 ../srcimages/luminance.pgm "--t2 --convert_oetf linear --target_type R" "" "" ) +gencmpktx( r_reference_uastc r_reference_uastc.ktx2 ../srcimages/luminance.pgm "--t2 --uastc --target_type R --swizzle r001 --" "" "" ) # This one seems to be okay. -gencmpktx( r_reference_basis r_reference_basis.ktx2 ../srcimages/luminance.pgm "--test --t2 --bcmp --target_type R --swizzle r001" "" "" ) -gencmpktx( rg_reference_u rg_reference_u.ktx2 ../srcimages/basn4a08.png "--test --t2 --convert_oetf linear --target_type RG" "" "" ) -gencmpktx( rg_reference_uastc rg_reference_uastc.ktx2 ../srcimages/basn4a08.png "--test --t2 --uastc --target_type RG --swizzle rg01 --" "" "" ) +gencmpktx( r_reference_basis r_reference_basis.ktx2 ../srcimages/luminance.pgm "--t2 --bcmp --target_type R --swizzle r001" "" "" ) +gencmpktx( rg_reference_u rg_reference_u.ktx2 ../srcimages/basn4a08.png "--t2 --convert_oetf linear --target_type RG" "" "" ) +gencmpktx( rg_reference_uastc rg_reference_uastc.ktx2 ../srcimages/basn4a08.png "--t2 --uastc --target_type RG --swizzle rg01 --" "" "" ) if(APPLE) -gencmpktx( rg_reference_basis rg_reference_basis.ktx2 ../srcimages/basn4a08.png "--test --t2 --bcmp --target_type RG --swizzle ra01" "" "" ) +gencmpktx( rg_reference_basis rg_reference_basis.ktx2 ../srcimages/basn4a08.png "--t2 --bcmp --target_type RG --swizzle ra01" "" "" ) endif() # Input to the following tests is a red RGB texture. -gencmpktx( swizzle_r_to_g_u green_rgb_reference_u.ktx2 ../srcimages/level0.ppm "--nowarn --t2 --test --input_swizzle 0r01" "" "" ) -gencmpktx( swizzle_r_to_gb_convert_to_rgba_u cyan_rgba_reference_u.ktx2 ../srcimages/level0.ppm "--nowarn --t2 --test --input_swizzle 0rr1 --target_type RGBA" "" "" ) +gencmpktx( swizzle_r_to_g_u green_rgb_reference_u.ktx2 ../srcimages/level0.ppm "--nowarn --t2 --input_swizzle 0r01" "" "" ) +gencmpktx( swizzle_r_to_gb_convert_to_rgba_u cyan_rgba_reference_u.ktx2 ../srcimages/level0.ppm "--nowarn --t2 --input_swizzle 0rr1 --target_type RGBA" "" "" ) # BasisU encoders notice that alpha is all 1 and removes it, hence RGB output for these. -gencmpktx( swizzle_r_to_gb_convert_to_rgba_basis cyan_rgb_reference_basis.ktx2 ../srcimages/level0.ppm "--nowarn --t2 --test --input_swizzle 0rr1 --target_type RGBA --bcmp" "" "" ) -gencmpktx( swizzle_r_to_gb_convert_to_rgba_uastc cyan_rgb_reference_uastc.ktx2 ../srcimages/level0.ppm "--nowarn --t2 --test --input_swizzle 0rr1 --target_type RGBA --uastc --" "" "" ) +gencmpktx( swizzle_r_to_gb_convert_to_rgba_basis cyan_rgb_reference_basis.ktx2 ../srcimages/level0.ppm "--nowarn --t2 --input_swizzle 0rr1 --target_type RGBA --bcmp" "" "" ) +gencmpktx( swizzle_r_to_gb_convert_to_rgba_uastc cyan_rgb_reference_uastc.ktx2 ../srcimages/level0.ppm "--nowarn --t2 --input_swizzle 0rr1 --target_type RGBA --uastc --" "" "" ) -gencmpktx( uastc_Iron_Bars_001_normal uastc_Iron_Bars_001_normal.ktx2 ../srcimages/Iron_Bars/Iron_Bars_001_normal_unnormalized.png "--test --assign_oetf linear --genmipmap --normalize --normal_mode --encode uastc --zcmp 5" "" "") +gencmpktx( uastc_Iron_Bars_001_normal uastc_Iron_Bars_001_normal.ktx2 ../srcimages/Iron_Bars/Iron_Bars_001_normal_unnormalized.png "--assign_oetf linear --genmipmap --normalize --normal_mode --encode uastc --zcmp 5" "" "") if(APPLE) -gencmpktx( etc1s_Iron_Bars_001_normal etc1s_Iron_Bars_001_normal.ktx2 ../srcimages/Iron_Bars/Iron_Bars_001_normal_unnormalized.png "--test --assign_oetf linear --genmipmap --normalize --normal_mode --encode etc1s" "" "") +gencmpktx( etc1s_Iron_Bars_001_normal etc1s_Iron_Bars_001_normal.ktx2 ../srcimages/Iron_Bars/Iron_Bars_001_normal_unnormalized.png "--assign_oetf linear --genmipmap --normalize --normal_mode --encode etc1s" "" "") endif() -gencmpktx( gAMA_chunk_png g03n2c08.ktx2 ../srcimages/g03n2c08.png "--test --t2" "" "" ) -gencmpktx( cHRM_chunk_png ccwn2c08.ktx2 ../srcimages/ccwn2c08.png "--test --t2" "" "" ) -gencmpktx( tRNS_chunk_rgb_png tbrn2c08.ktx2 ../srcimages/tbrn2c08.png "--test --t2" "" "" ) -gencmpktx( tRNS_chunk_palette_8-bit_png tbyn3p08.ktx2 ../srcimages/tbyn3p08.png "--test --t2" "" "" ) -gencmpktx( tRNS_chunk_palette_2-bit_png tm3n3p02.ktx2 ../srcimages/tm3n3p02.png "--test --t2" "" "" ) +gencmpktx( gAMA_chunk_png g03n2c08.ktx2 ../srcimages/g03n2c08.png "--t2" "" "" ) +gencmpktx( cHRM_chunk_png ccwn2c08.ktx2 ../srcimages/ccwn2c08.png "--t2" "" "" ) +gencmpktx( tRNS_chunk_rgb_png tbrn2c08.ktx2 ../srcimages/tbrn2c08.png "--t2" "" "" ) +gencmpktx( tRNS_chunk_palette_8-bit_png tbyn3p08.ktx2 ../srcimages/tbyn3p08.png "--t2" "" "" ) +gencmpktx( tRNS_chunk_palette_2-bit_png tm3n3p02.ktx2 ../srcimages/tm3n3p02.png "--t2" "" "" ) gencmpktx( rgb-mipmap-reference-list rgb-mipmap-reference.ktx diff --git a/tools/ktxsc/ktxsc.cpp b/tools/ktxsc/ktxsc.cpp index 07461ad30c..244dd46029 100644 --- a/tools/ktxsc/ktxsc.cpp +++ b/tools/ktxsc/ktxsc.cpp @@ -13,6 +13,11 @@ #include #include +#if defined(_WIN32) +#define WINDOWS_LEAN_AND_MEAN +#include +#endif + #include #include "argparser.h" @@ -49,17 +54,17 @@ Supercompress the images in a KTX2 file. ktxsc [options] [@e infile ...] @section ktxsc_description DESCRIPTION - @b ktxsc can encode and supercompresses the images in Khronos texture - format version 2 files (KTX2) . Uncompressed files, i.e those whose vkFormat - name does not end in @c _BLOCK can be encoded to Basis Universal - (encoded to ETC1S then supercompressed with an integrated LZ step), - encoded to UASTC or supercompressed with Zstandard (zstd). Any image + @b ktxsc can encode and supercompress the images in Khronos texture + format version 2 files (KTX2). Uncompressed files, i.e those whose vkFormat + name does not end in @c _BLOCK can be encoded to ASTC, Basis Universal + (encoded to ETC1S then supercompressed with an integrated LZ step) + or UASTC and optionally supercompressed with Zstandard (zstd). Any image format, except Basis Universal, can be supercompressed with zstd. For best results with UASTC, the data should be conditioned for zstd by using the @e --uastc_rdo_q and, optionally, @e --uastc_rdo_d options. @b ktxsc reads each named @e infile and compresses it in place. When - @e infile is not specified, a single file will be read from @e stdin. and the + @e infile is not specified, a single file will be read from @e stdin and the output written to @e stdout. When one or more files is specified each will be compressed in place. @@ -146,10 +151,9 @@ ktxSupercompressor::usage() " Options are:\n" "\n" " -o outfile, --output=outfile\n" - " Writes the output to outfile. If there is more than 1 input\n" - " file the ommand prints its usage message and exits. If outfile\n" - " is 'stdout', output will be written to stdout. If there is more\n" - " than 1 infile the command prints its usage message and exits.\n" + " Writes the output to outfile. If outfile is 'stdout', output\n" + " will be written to stdout. If there is more than 1 input file\n" + " the command prints its usage message and exits.\n" " -f, --force If the output file cannot be opened, remove it and create a\n" " new file, without prompting for confirmation regardless of\n" " its permissions.\n"; @@ -157,6 +161,17 @@ ktxSupercompressor::usage() } +static _tstring dir_name(_tstring& path) +{ + // Supports both Unix-style and Windows-style. + size_t last_separator = path.find_last_of("/\\"); + if (last_separator != string::npos) { + return path.substr(0, last_separator + 1); + } else { + return std::basic_string<_TCHAR>(); + } +} + int _tmain(int argc, _TCHAR* argv[]) { ktxSupercompressor ktxsc; @@ -167,11 +182,12 @@ int _tmain(int argc, _TCHAR* argv[]) int ktxSupercompressor::main(int argc, _TCHAR *argv[]) { - FILE *inf, *outf; + FILE *inf, *outf = nullptr; KTX_error_code result; ktxTexture2* texture = 0; int exitCode = 0; - const _TCHAR* pTmpFile = 0; + _tstring tmpfile; + processCommandLine(argc, argv, eAllowStdin); validateOptions(); @@ -179,10 +195,8 @@ ktxSupercompressor::main(int argc, _TCHAR *argv[]) std::vector<_tstring>::const_iterator it; for (it = options.infiles.begin(); it < options.infiles.end(); it++) { _tstring infile = *it; - _tstring tmpfile = _T("/tmp/ktxsc.XXXXXX"); if (infile.compare(_T("-")) == 0) { - //infile = 0; inf = stdin; #if defined(_WIN32) /* Set "stdin" to have binary mode */ @@ -201,16 +215,33 @@ ktxSupercompressor::main(int argc, _TCHAR *argv[]) #endif } else if (options.outfile.length()) { outf = _tfopen(options.outfile.c_str(), "wxb"); + // Mingw gcc and possibly some Linux versions do not accept + // 'x' as a mode character, following an earlier version of the + // `fopen` spec. Work around this annoying limitation. Don't + // use ifdefs as all the places suffering this limitation are + // not known to us. + if (!outf && errno == EINVAL) { + outf = _tfopen(options.outfile.c_str(), "r"); + if (outf) { + outf = nullptr; + errno = EEXIST; + } else { + outf = _tfopen(options.outfile.c_str(), "wb"); + } + } } else { + // Make a temporary file in the same directory as the source + // file to avoid cross-device rename issues later. + tmpfile = dir_name(infile) + _T("ktxsc.tmp.XXXXXX"); #if defined(_WIN32) - pTmpFile = _mktemp(&tmpfile[0]); - if (pTmpFile != nullptr) + // Despite receiving size() the debug CRT version of mktemp_s + // asserts that the string template is NUL terminated. + tmpfile.push_back(_T('\0')); + if (_tmktemp_s(&tmpfile[0], tmpfile.size()) == 0) outf = _tfopen(tmpfile.c_str(), "wb"); - else - outf = nullptr; #else - outf = fdopen(mkstemp(&tmpfile[0]), "wb"); - pTmpFile = tmpfile.c_str(); + int fd_tmp = mkstemp(&tmpfile[0]); + outf = fdopen(fd_tmp, "wb"); #endif } @@ -263,7 +294,7 @@ ktxSupercompressor::main(int argc, _TCHAR *argv[]) if ((options.etc1s || options.bopts.uastc) && texture->isCompressed) { cerr << name << ": " << "Cannot encode already block-compressed textures " - << "to Basis Universal or UASTC." + << "to ASTC, Basis Universal or UASTC." << endl; exitCode = 1; goto cleanup; @@ -341,11 +372,17 @@ ktxSupercompressor::main(int argc, _TCHAR *argv[]) (void)fclose(outf); if (!options.outfile.length() && !options.useStdout) { // Move the new file over the original. - assert(pTmpFile && infile.length()); - int err = _trename(tmpfile.c_str(), infile.c_str()); - if (err) { + assert(tmpfile.size() > 0 && infile.length()); +#if defined(_WIN32) + // Windows' rename() fails if the destination file exists! + if (!MoveFileEx(tmpfile.c_str(), infile.c_str(), + MOVEFILE_REPLACE_EXISTING)) +#else + if (_trename(tmpfile.c_str(), infile.c_str())) +#endif + { cerr << name - << ": rename of \"%s\" to \"%s\" failed: " + << ": rename of \"" << tmpfile << "\" to \"" << infile << "\" failed: " << strerror(errno) << endl; exitCode = 2; goto cleanup; @@ -371,7 +408,7 @@ ktxSupercompressor::main(int argc, _TCHAR *argv[]) return 0; cleanup: - if (pTmpFile) (void)_tunlink(pTmpFile); + if (tmpfile.size() > 0) (void)_tunlink(tmpfile.c_str()); if (options.outfile.length()) (void)_tunlink(options.outfile.c_str()); return exitCode; } diff --git a/utils/scapp.h b/utils/scapp.h index ef967e658d..14daf973f2 100644 --- a/utils/scapp.h +++ b/utils/scapp.h @@ -322,17 +322,17 @@ astcEncoderMode(const char* mode) { nml.xy = nml.xy * 2.0 - 1.0; // Unpack to [-1,1] nml.z = sqrt(1 - dot(nml.xy, nml.xy)); // Compute Z - Encoding is optimized for normal maps. For ASTC encoding, - '--encode astc', the encoder is directed to optimize for angular - error instead of simple PSNR. For ETC1S encoding, '@b --encode etc1s', - RDO is disabled (no selector RDO, no endpoint RDO) to provide - better quality. + For ASTC encoding, '--encode astc', encoder parameters are + tuned for better quality on normal maps. For ETC1S encoding, + '@b --encode etc1s', RDO is disabled (no selector RDO, no + endpoint RDO) to provide better quality.
--normalize
-
Normalize input normals to have a unit length. Only valid for - linear textures with 2 or more components. For 2-component inputs 2D - unit normals are calculated. Do not use this to generate X+Y normals - for --normal_mode. For 4-component inputs a 3D unit normal is calculated. - 1.0 is used for the value of the 4th component.
+
Normalize input normals to have a unit length. Only valid + for linear textures with 2 or more components. For 2-component + inputs 2D unit normals are calculated. Do not use these 2D unit + normals to generate X+Y normals for --normal_mode. For + 4-component inputs a 3D unit normal is calculated. 1.0 is used + for the value of the 4th component.
--no_sse
Forbid use of the SSE instruction set. Ignored if CPU does not support SSE. Only the Basis Universal compressor uses SSE.