xref: /aosp_15_r20/external/libvpx/test/vp9_c_vs_simd_encode.sh (revision fb1b10ab9aebc7c7068eedab379b749d7e3900be)
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