1#!/bin/sh 2## 3## Copyright (c) 2023 The WebM project authors. All Rights Reserved. 4## 5## Use of this source code is governed by a BSD-style license 6## that can be found in the LICENSE file in the root of the source 7## tree. An additional intellectual property rights grant can be found 8## in the file PATENTS. All contributing project authors may 9## be found in the AUTHORS file in the root of the source tree. 10## 11## This script checks the bit exactness between C and SIMD 12## implementations of VP9 encoder. 13## 14. $(dirname $0)/tools_common.sh 15 16TEST_BITRATES="1600 6400" 17PRESETS="good rt" 18TEST_CLIPS="yuv_raw_input y4m_360p_10bit_input yuv_480p_raw_input y4m_720p_input" 19OUT_FILE_SUFFIX=".ivf" 20SCRIPT_DIR=$(dirname "$0") 21LIBVPX_SOURCE_DIR=$(cd "${SCRIPT_DIR}/.."; pwd) 22 23# Clips used in test. 24YUV_RAW_INPUT="${LIBVPX_TEST_DATA_PATH}/hantro_collage_w352h288.yuv" 25YUV_480P_RAW_INPUT="${LIBVPX_TEST_DATA_PATH}/niklas_640_480_30.yuv" 26Y4M_360P_10BIT_INPUT="${LIBVPX_TEST_DATA_PATH}/crowd_run_360p_10_150f.y4m" 27Y4M_720P_INPUT="${LIBVPX_TEST_DATA_PATH}/niklas_1280_720_30.y4m" 28 29# Number of frames to test. 30VP9_ENCODE_C_VS_SIMD_TEST_FRAME_LIMIT=20 31 32# Create a temporary directory for output files. 33if [ -n "${TMPDIR}" ]; then 34 VPX_TEST_TEMP_ROOT="${TMPDIR}" 35elif [ -n "${TEMPDIR}" ]; then 36 VPX_TEST_TEMP_ROOT="${TEMPDIR}" 37else 38 VPX_TEST_TEMP_ROOT=/tmp 39fi 40 41VPX_TEST_OUTPUT_DIR="${VPX_TEST_TEMP_ROOT}/vpx_test_$$" 42 43if ! mkdir -p "${VPX_TEST_OUTPUT_DIR}" || \ 44 [ ! -d "${VPX_TEST_OUTPUT_DIR}" ]; then 45 echo "${0##*/}: Cannot create output directory, giving up." 46 echo "${0##*/}: VPX_TEST_OUTPUT_DIR=${VPX_TEST_OUTPUT_DIR}" 47 exit 1 48fi 49 50elog() { 51 echo "$@" 1>&2 52} 53 54# Echoes path to $1 when it's executable and exists in ${VPX_TEST_OUTPUT_DIR}, 55# or an empty string. Caller is responsible for testing the string once the 56# function returns. 57vp9_enc_tool_path() { 58 local target="$1" 59 local tool_path="${VPX_TEST_OUTPUT_DIR}/build_target_${target}/vpxenc" 60 61 if [ ! -x "${tool_path}" ]; then 62 tool_path="" 63 fi 64 echo "${tool_path}" 65} 66 67# Environment check: Make sure input and source directories are available. 68vp9_c_vs_simd_enc_verify_environment() { 69 if [ ! -e "${YUV_RAW_INPUT}" ]; then 70 elog "libvpx test data must exist in LIBVPX_TEST_DATA_PATH." 71 return 1 72 fi 73 if [ ! -e "${YUV_480P_RAW_INPUT}" ]; then 74 elog "libvpx test data must exist in LIBVPX_TEST_DATA_PATH." 75 return 1 76 fi 77 if [ ! -e "${Y4M_720P_INPUT}" ]; then 78 elog "libvpx test data must exist in LIBVPX_TEST_DATA_PATH." 79 return 1 80 fi 81 if [ ! -e "${Y4M_360P_10BIT_INPUT}" ]; then 82 elog "libvpx test data must exist in LIBVPX_TEST_DATA_PATH." 83 return 1 84 fi 85 if [ ! -d "$LIBVPX_SOURCE_DIR" ]; then 86 elog "LIBVPX_SOURCE_DIR does not exist." 87 return 1 88 fi 89} 90 91# This is not needed since tools_common.sh does the same cleanup. 92# Keep the code here for our reference. 93# cleanup() { 94# rm -rf ${VPX_TEST_OUTPUT_DIR} 95# } 96 97# Echo VPX_SIMD_CAPS_MASK for different instruction set architecture. 98avx512f() { 99 echo "0x1FF" 100} 101 102avx2() { 103 echo "0x0FF" 104} 105 106sse4_1() { 107 echo "0x03F" 108} 109 110ssse3() { 111 echo "0x01F" 112} 113 114sse2() { 115 echo "0x007" 116} 117 118# Echo clip details to be used as input to vpxenc. 119yuv_raw_input() { 120 echo ""${YUV_RAW_INPUT}" 121 --width=352 122 --height=288 123 --bit-depth=8 124 --profile=0" 125} 126 127yuv_480p_raw_input() { 128 echo ""${YUV_480P_RAW_INPUT}" 129 --width=640 130 --height=480 131 --bit-depth=8 132 --profile=0" 133} 134 135y4m_720p_input() { 136 echo ""${Y4M_720P_INPUT}" 137 --bit-depth=8 138 --profile=0" 139} 140 141y4m_360p_10bit_input() { 142 echo ""${Y4M_360P_10BIT_INPUT}" 143 --bit-depth=10 144 --profile=2" 145} 146 147has_x86_isa_extn() { 148 instruction_set=$1 149 if ! grep -q "$instruction_set" /proc/cpuinfo; then 150 # This instruction_set is not supported. 151 return 1 152 fi 153 # This instruction_set is supported. 154 return 0 155} 156 157# Echo good encode params for use with VP9 encoder. 158vp9_encode_good_params() { 159 echo "--codec=vp9 \ 160 --good \ 161 --test-decode=fatal \ 162 --ivf \ 163 --threads=1 \ 164 --static-thresh=0 \ 165 --tile-columns=0 \ 166 --end-usage=vbr \ 167 --kf-max-dist=160 \ 168 --kf-min-dist=0 \ 169 --lag-in-frames=19 \ 170 --max-q=63 \ 171 --min-q=0 \ 172 --passes=2 \ 173 --undershoot-pct=100 \ 174 --overshoot-pct=100 \ 175 --verbose \ 176 --auto-alt-ref=1 \ 177 --drop-frame=0 \ 178 --bias-pct=50 \ 179 --minsection-pct=0 \ 180 --maxsection-pct=2000 \ 181 --arnr-maxframes=7 \ 182 --arnr-strength=5 \ 183 --sharpness=0 \ 184 --frame-parallel=0" 185} 186 187# Echo realtime encode params for use with VP9 encoder. 188vp9_encode_rt_params() { 189 echo "--codec=vp9 \ 190 --rt \ 191 --test-decode=fatal \ 192 --ivf \ 193 --threads=1 \ 194 --static-thresh=0 \ 195 --tile-columns=0 \ 196 --tile-rows=0 \ 197 --end-usage=cbr \ 198 --kf-max-dist=90000 \ 199 --lag-in-frames=0 \ 200 --max-q=58 \ 201 --min-q=2 \ 202 --passes=1 \ 203 --undershoot-pct=50 \ 204 --overshoot-pct=50 \ 205 --verbose \ 206 --row-mt=0 \ 207 --buf-sz=1000 \ 208 --buf-initial-sz=500 \ 209 --buf-optimal-sz=600 \ 210 --max-intra-rate=300 \ 211 --resize-allowed=0 \ 212 --noise-sensitivity=0 \ 213 --aq-mode=3 \ 214 --error-resilient=0" 215} 216 217# Configures for the given target in the 218# ${VPX_TEST_OUTPUT_DIR}/build_target_${target} directory. 219vp9_enc_build() { 220 local target=$1 221 local configure="$2" 222 local tmp_build_dir=${VPX_TEST_OUTPUT_DIR}/build_target_${target} 223 mkdir -p "$tmp_build_dir" 224 local save_dir="$PWD" 225 cd "$tmp_build_dir" 226 227 echo "Building target: ${target}" 228 local config_args="--disable-install-docs \ 229 --enable-unit-tests \ 230 --enable-debug \ 231 --enable-postproc \ 232 --enable-vp9-postproc \ 233 --enable-vp9-temporal-denoising \ 234 --enable-vp9-highbitdepth" 235 236 eval "$configure" --target="${target}" "${config_args}" ${devnull} 237 eval make -j$(nproc) ${devnull} 238 echo "Done building target: ${target}" 239 cd "${save_dir}" 240} 241 242compare_enc_output() { 243 local target=$1 244 local cpu=$2 245 local clip=$3 246 local bitrate=$4 247 local preset=$5 248 if ! diff -q ${VPX_TEST_OUTPUT_DIR}/Out-generic-gnu-"${clip}"-${preset}-${bitrate}kbps-cpu${cpu}${OUT_FILE_SUFFIX} \ 249 ${VPX_TEST_OUTPUT_DIR}/Out-${target}-"${clip}"-${preset}-${bitrate}kbps-cpu${cpu}${OUT_FILE_SUFFIX}; then 250 elog "C vs ${target} encode mismatches for ${clip}, at ${bitrate} kbps, speed ${cpu}, ${preset} preset" 251 return 1 252 fi 253} 254 255vp9_enc_test() { 256 local encoder="$1" 257 local target=$2 258 if [ -z "$(vp9_enc_tool_path "${target}")" ]; then 259 elog "vpxenc not found. It must exist in ${VPX_TEST_OUTPUT_DIR}/build_target_${target} path" 260 return 1 261 fi 262 263 local tmp_build_dir=${VPX_TEST_OUTPUT_DIR}/build_target_${target} 264 local save_dir="$PWD" 265 cd "$tmp_build_dir" 266 for preset in ${PRESETS}; do 267 if [ "${preset}" = "good" ]; then 268 local max_cpu_used=5 269 local test_params=vp9_encode_good_params 270 elif [ "${preset}" = "rt" ]; then 271 local max_cpu_used=9 272 local test_params=vp9_encode_rt_params 273 else 274 elog "Invalid preset" 275 cd "${save_dir}" 276 return 1 277 fi 278 279 # Enable armv8 test for real-time only 280 if [ "${preset}" = "good" ] && [ "${target}" = "armv8-linux-gcc" ]; then 281 continue 282 fi 283 284 for cpu in $(seq 0 $max_cpu_used); do 285 for clip in ${TEST_CLIPS}; do 286 for bitrate in ${TEST_BITRATES}; do 287 eval "${encoder}" $($clip) $($test_params) \ 288 "--limit=${VP9_ENCODE_C_VS_SIMD_TEST_FRAME_LIMIT}" \ 289 "--cpu-used=${cpu}" "--target-bitrate=${bitrate}" "-o" \ 290 ${VPX_TEST_OUTPUT_DIR}/Out-${target}-"${clip}"-${preset}-${bitrate}kbps-cpu${cpu}${OUT_FILE_SUFFIX} \ 291 ${devnull} 292 293 if [ "${target}" != "generic-gnu" ]; then 294 if ! compare_enc_output ${target} $cpu ${clip} $bitrate ${preset}; then 295 # Find the mismatch 296 cd "${save_dir}" 297 return 1 298 fi 299 fi 300 done 301 done 302 done 303 done 304 cd "${save_dir}" 305} 306 307vp9_test_generic() { 308 local configure="$LIBVPX_SOURCE_DIR/configure" 309 local target="generic-gnu" 310 311 echo "Build for: ${target}" 312 vp9_enc_build ${target} ${configure} 313 local encoder="$(vp9_enc_tool_path "${target}")" 314 vp9_enc_test $encoder "${target}" 315} 316 317# This function encodes VP9 bitstream by enabling SSE2, SSSE3, SSE4_1, AVX2, AVX512f as there are 318# no functions with MMX, SSE, SSE3 and AVX specialization. 319# The value of environment variable 'VPX_SIMD_CAPS' controls enabling of different instruction 320# set extension optimizations. The value of the flag 'VPX_SIMD_CAPS' and the corresponding 321# instruction set extension optimization enabled are as follows: 322# AVX512 AVX2 AVX SSE4_1 SSSE3 SSE3 SSE2 SSE MMX 323# 1 1 1 1 1 1 1 1 1 -> 0x1FF -> Enable AVX512 and lower variants 324# 0 1 1 1 1 1 1 1 1 -> 0x0FF -> Enable AVX2 and lower variants 325# 0 0 1 1 1 1 1 1 1 -> 0x07F -> Enable AVX and lower variants 326# 0 0 0 1 1 1 1 1 1 -> 0x03F -> Enable SSE4_1 and lower variants 327# 0 0 0 0 1 1 1 1 1 -> 0x01F -> Enable SSSE3 and lower variants 328# 0 0 0 0 0 1 1 1 1 -> 0x00F -> Enable SSE3 and lower variants 329# 0 0 0 0 0 0 1 1 1 -> 0x007 -> Enable SSE2 and lower variants 330# 0 0 0 0 0 0 0 1 1 -> 0x003 -> Enable SSE and lower variants 331# 0 0 0 0 0 0 0 0 1 -> 0x001 -> Enable MMX 332## NOTE: In x86_64 platform, it is not possible to enable sse/mmx/c using "VPX_SIMD_CAPS_MASK" as 333# all x86_64 platforms implement sse2. 334vp9_test_x86() { 335 local arch=$1 336 337 if ! uname -m | grep -q "x86"; then 338 elog "Machine architecture is not x86 or x86_64" 339 return 0 340 fi 341 342 if [ $arch = "x86" ]; then 343 local target="x86-linux-gcc" 344 elif [ $arch = "x86_64" ]; then 345 local target="x86_64-linux-gcc" 346 fi 347 348 local x86_isa_variants="avx512f avx2 sse4_1 ssse3 sse2" 349 local configure="$LIBVPX_SOURCE_DIR/configure" 350 351 echo "Build for x86: ${target}" 352 vp9_enc_build ${target} ${configure} 353 local encoder="$(vp9_enc_tool_path "${target}")" 354 for isa in $x86_isa_variants; do 355 # Note that if has_x86_isa_extn returns 1, it is false, and vice versa. 356 if ! has_x86_isa_extn $isa; then 357 echo "${isa} is not supported in this machine" 358 continue 359 fi 360 export VPX_SIMD_CAPS_MASK=$($isa) 361 if ! vp9_enc_test $encoder ${target}; then 362 # Find the mismatch 363 return 1 364 fi 365 unset VPX_SIMD_CAPS_MASK 366 done 367} 368 369vp9_test_arm() { 370 local target="armv8-linux-gcc" 371 local configure="CROSS=aarch64-linux-gnu- $LIBVPX_SOURCE_DIR/configure --extra-cflags=-march=armv8.4-a \ 372 --extra-cxxflags=-march=armv8.4-a" 373 echo "Build for arm64: ${target}" 374 vp9_enc_build ${target} "${configure}" 375 376 local encoder="$(vp9_enc_tool_path "${target}")" 377 if ! vp9_enc_test "qemu-aarch64 -L /usr/aarch64-linux-gnu ${encoder}" ${target}; then 378 # Find the mismatch 379 return 1 380 fi 381} 382 383vp9_c_vs_simd_enc_test() { 384 # Test Generic 385 vp9_test_generic 386 387 # Test x86 (32 bit) 388 echo "vp9 test for x86 (32 bit): Started." 389 if ! vp9_test_x86 "x86"; then 390 echo "vp9 test for x86 (32 bit): Done, test failed." 391 return 1 392 else 393 echo "vp9 test for x86 (32 bit): Done, all tests passed." 394 fi 395 396 # Test x86_64 (64 bit) 397 if [ "$(eval uname -m)" = "x86_64" ]; then 398 echo "vp9 test for x86_64 (64 bit): Started." 399 if ! vp9_test_x86 "x86_64"; then 400 echo "vp9 test for x86_64 (64 bit): Done, test failed." 401 return 1 402 else 403 echo "vp9 test for x86_64 (64 bit): Done, all tests passed." 404 fi 405 fi 406 407 # Test ARM 408 echo "vp9_test_arm: Started." 409 if ! vp9_test_arm; then 410 echo "vp9 test for arm: Done, test failed." 411 return 1 412 else 413 echo "vp9 test for arm: Done, all tests passed." 414 fi 415} 416 417# Setup a trap function to clean up build, and output files after tests complete. 418# trap cleanup EXIT 419 420run_tests vp9_c_vs_simd_enc_verify_environment vp9_c_vs_simd_enc_test 421