Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • videolan/dav1d
  • ePirat/dav1d
  • magsoft/dav1d
  • chouquette/dav1d
  • shiz/dav1d
  • tdaede/dav1d
  • tmatth/dav1d
  • dwbuiten/dav1d
  • mstorsjo/dav1d
  • janne/dav1d
  • ltrudeau/dav1d
  • rzumer/dav1d
  • lu_zero/dav1d
  • rbultje/dav1d
  • tbr/dav1d
  • thresh/dav1d
  • haasn/dav1d
  • midtskogen/dav1d
  • SmilingWolf/dav1d
  • lotharkript/dav1d
  • jamrial/dav1d
  • barrbrain/dav1d
  • robUx4/dav1d
  • jbk/dav1d
  • skal65535/dav1d
  • tappara/dav1d
  • dalecurtis/dav1d
  • montytyper/dav1d
  • TLaurent/dav1d
  • liwei/dav1d
  • CounterPillow/dav1d
  • rswarbrick-argon/dav1d
  • mjbshaw/dav1d
  • fcartegnie/dav1d
  • jyavenard/dav1d
  • xuefeng/dav1d
  • licao/dav1d
  • FredB/dav1d
  • jn7163/dav1d
  • bherman.aconspart/dav1d
  • anisse/dav1d
  • koda/dav1d
  • mihulet88/dav1d
  • sabdfl/dav1d
  • brion/dav1d
  • tj_davies/dav1d
  • EwoutH/dav1d
  • KyleSiefring/dav1d
  • manass3018/dav1d
  • krish-iyer/dav1d
  • stebler/dav1d
  • hchen1506/dav1d
  • f3ndot/dav1d
  • linkmauve/dav1d
  • malvanos/dav1d
  • rcss/dav1d
  • DonDiego/dav1d
  • ledyba-z/dav1d
  • seiqan2/dav1d
  • t0934812955/dav1d
  • xclaesse/dav1d
  • lynne/dav1d
  • loveingpowellalways/dav1d
  • govind.sharma/dav1d
  • kossh1/dav1d
  • davidandsabrina4ever2014/dav1d
  • abdouseck664/dav1d
  • jennifer.derrick61583/dav1d
  • msaas01925/dav1d
  • akymaster/dav1d
  • sylvestre/dav1d
  • morgan.shenkin/dav1d
  • B3rn4arD/dav1d
  • evzien/dav1d
  • mwozniak/dav1d
  • TompSciGit/dav1d
  • namse/dav1d
  • kkourin/dav1d
  • nico/dav1d
  • galad/dav1d
  • ltnokiago/dav1d
  • mindfreeze/dav1d
  • DmitriySychov/dav1d
  • oddstone/dav1d
  • nasirhemed/dav1d
  • richselwood/dav1d
  • longervision/dav1d
  • kurosu/dav1d
  • heitbaum/dav1d
  • Opiyonag/dav1d
  • salomethirot-arm/dav1d
  • dillmo71/dav1d
  • jwright-arm/dav1d
  • stonef385/dav1d
  • y-guyon/dav1d
  • andrekempe-arm/dav-1-d-reloaded
  • joedrago/dav1d
  • Rtytry/dav1d
  • altanai/dav1d
  • beiluo97/dav1d
  • wtc/dav1d
  • Asilx21/dav1d
  • DarioSucic/dav1d
  • Siberiawind/dav1d
  • edelmirocove17/dav1d
  • Mtndude/dav1d
  • dconrad/dav1d
  • ChildSoap/dav1d
  • kalan5269/dav1d
  • Jolincai/dav1d
  • kawiddoes/dav1d
  • ledyba/dav1d
  • minhhien231186/dav1d
  • beiluo971/dav1d
  • hakantezgoren34/dav1d
  • chigita73/dav1d
  • slomo/dav1d
  • Starbuck5/dav1d
  • jbeich/dav1d
  • berrylcm/dav1d
  • philip584521/dav1d
  • IgorKey/dav1d
  • shekar007/dav1d
  • jdek/dav1d
  • oldsssteveo/dav1d
  • Jingwiw/dav1d
  • vigneshv/dav1d
  • andrey.semashev/dav1d
  • v.cvetkov/dav1d
  • kattmedhatt/dav1d
  • ccawley2011/dav1d
  • rportalez/dav1d
  • Skantes/dav1d
  • arpadpanyik-arm/dav1d
  • asenat/dav1d
  • pcc/dav1d
  • nickg/dav1d
  • BogdanW3/dav1d
  • brad/dav1d
  • MARBEAN2/dav1d
  • yintong.ustc/dav1d
  • cosmin/dav1d
  • kasper93/dav1d
  • HecaiYuan/dav1d
  • jerrytsai569/dav1d
  • ttwuandes/dav1d
  • OctopusET/dav1d
  • maryla-uc/dav1d
  • Un1q32/dav1d
  • pranavk/dav1d
  • twulz/dav1d
  • gianni-r/dav1d
152 results
Show changes
Commits on Source (1202)
Showing with 1480 additions and 812 deletions
...@@ -4,5 +4,7 @@ ...@@ -4,5 +4,7 @@
*~ *~
tags tags
.DS_Store .DS_Store
/tests/argon
/tests/dav1d-test-data /tests/dav1d-test-data
*.snap *.snap
/tools/output/xxhash.h
...@@ -4,49 +4,56 @@ stages: ...@@ -4,49 +4,56 @@ stages:
- test - test
.debian-amd64-common: .debian-amd64-common:
image: registry.videolan.org/dav1d-debian-unstable:20200602183013 image: registry.videolan.org/dav1d-debian-unstable:20250207200301
stage: build
tags:
- docker
- amd64
.debian-amd64-minimum:
image: registry.videolan.org/dav1d-debian-minimum:20250207200301
stage: build stage: build
tags: tags:
- docker - docker
- amd64 - amd64
.debian-llvm-mingw-common: .debian-llvm-mingw-common:
image: registry.videolan.org/vlc-debian-llvm-mingw:20190218133533 image: registry.videolan.org/vlc-debian-llvm-msvcrt:20250305204125
stage: build stage: build
tags: tags:
- docker - docker
- amd64 - amd64
.debian-aarch64-common: .debian-aarch64-common:
image: registry.videolan.org/dav1d-debian-buster-aarch64:20200218203017 image: registry.videolan.org/dav1d-debian-bookworm-aarch64:20250215002814
stage: build stage: build
tags: tags:
- docker - docker
- aarch64 - aarch64
.debian-armv7-common: .debian-armv7-common:
image: registry.videolan.org/dav1d-debian-unstable-armv7:20190202101732 image: registry.videolan.org/dav1d-debian-bookworm-armv7:20250215014239
stage: build stage: build
tags: tags:
- docker - docker
- armv7 - armv7
.debian-ppc64le-common: .debian-ppc64le-common:
image: registry.videolan.org/dav1d-debian-unstable-ppc64le:20190606105121 image: registry.videolan.org/dav1d-debian-unstable-ppc64le:20250215003029
stage: build stage: build
tags: tags:
- docker - docker
- ppc64le - ppc64le
.ubuntu-common: .android-common:
image: registry.videolan.org/dav1d-ubuntu-bionic:20200121182340 image: registry.videolan.org/vlc-debian-android:20241118101328
stage: build stage: build
tags: tags:
- docker - docker
- amd64 - amd64
.android-common: .debian-wasm-emscripten-common:
image: registry.videolan.org/vlc-debian-android:20200323093226 image: registry.videolan.org/vlc-debian-wasm-emscripten:20250207201514
stage: build stage: build
tags: tags:
- docker - docker
...@@ -65,6 +72,7 @@ style-check: ...@@ -65,6 +72,7 @@ style-check:
exit 1; exit 1;
fi; fi;
done done
- rg '[\u061c\u2000-\u200f\u2028-\u202f\u205f-\u206f]' ./ && echo "Invisible Unicode characters" && exit 1
- git remote rm upstream 2> /dev/null || true - git remote rm upstream 2> /dev/null || true
- git remote add upstream https://code.videolan.org/videolan/dav1d.git - git remote add upstream https://code.videolan.org/videolan/dav1d.git
- git fetch -q upstream master - git fetch -q upstream master
...@@ -91,6 +99,7 @@ x86inc-check: ...@@ -91,6 +99,7 @@ x86inc-check:
- git diff --exit-code x86inc/master:x86inc.asm src/ext/x86/x86inc.asm - git diff --exit-code x86inc/master:x86inc.asm src/ext/x86/x86inc.asm
allow_failure: true allow_failure: true
build-debian: build-debian:
extends: .debian-amd64-common extends: .debian-amd64-common
tags: tags:
...@@ -98,8 +107,9 @@ build-debian: ...@@ -98,8 +107,9 @@ build-debian:
- avx2 - avx2
- amd64 - amd64
script: script:
- meson build --buildtype release - meson setup build --buildtype release
--werror -Dtrim_dsp=false
--werror
- ninja -C build - ninja -C build
- cd build && meson test -v - cd build && meson test -v
artifacts: artifacts:
...@@ -110,19 +120,35 @@ build-debian: ...@@ -110,19 +120,35 @@ build-debian:
build-debian-static: build-debian-static:
extends: .debian-amd64-common extends: .debian-amd64-common
script: script:
- meson build --buildtype release - meson setup build --buildtype release
--default-library static --default-library static
--werror --werror
- ninja -C build - ninja -C build
- cd build && meson test -v - cd build && meson test -v
- nm -A -g src/libdav1d.a | grep " [ABCDGRST] " | (! grep -v " _*dav1d_") - nm -A -g src/libdav1d.a | grep " [ABCDGRST] " | (! grep -v " _*dav1d_")
build-debian-illegal-instructions:
extends: .debian-amd64-common
tags:
- docker
- avx2
- amd64
script:
- meson setup build --buildtype debug
- ninja -C build
- cd build
- exit_code=0
- time meson test -v --suite checkasm --wrapper 'qemu-x86_64 -cpu Conroe' || exit_code=$((exit_code + $?))
- time meson test -v --suite checkasm --wrapper 'qemu-x86_64 -cpu Penryn' || exit_code=$((exit_code + $?))
- if [ $exit_code -ne 0 ]; then exit $exit_code; fi
build-debian32: build-debian32:
extends: .debian-amd64-common extends: .debian-amd64-common
script: script:
- meson build --buildtype release - meson setup build --buildtype release
--werror -Dtrim_dsp=false
--cross-file package/crossfiles/i686-linux32.meson --werror
--cross-file package/crossfiles/i686-linux32.meson
- ninja -C build - ninja -C build
- cd build && meson test -v - cd build && meson test -v
artifacts: artifacts:
...@@ -133,101 +159,148 @@ build-debian32: ...@@ -133,101 +159,148 @@ build-debian32:
build-debian-examples: build-debian-examples:
extends: .debian-amd64-common extends: .debian-amd64-common
script: script:
- meson build --buildtype release - meson setup build --buildtype release
--werror --werror
-Denable_examples=true -Denable_examples=true
- ninja -C build - ninja -C build
build-win32: build-debian-no-tools:
extends: .debian-amd64-common extends: .debian-amd64-common
script: script:
- wineserver -p && wine wineboot - meson setup build --buildtype release
- meson build --buildtype release --werror
--werror -Denable_tools=false
--libdir lib - ninja -C build
--prefix "$(pwd)/build/dav1d_install"
--cross-file package/crossfiles/i686-w64-mingw32.meson build-debian-bitdepth:
-Ddefault_library=both extends: .debian-amd64-common
script:
- meson setup build --buildtype release
--werror
-Dbitdepths=$DEPTH
- ninja -C build
parallel:
matrix:
- DEPTH: [8, 16]
build-debian-avx:
extends: .debian-amd64-common
tags:
- docker
- avx2
- amd64
variables:
CFLAGS: '-mavx'
script:
- meson setup build --buildtype debugoptimized
--werror
- ninja -C build
- cd build
- time meson test -v --suite checkasm
build-debian-minimum:
extends: .debian-amd64-minimum
script:
- meson setup build --buildtype release
--werror
-Dtrim_dsp=false
- ninja -C build - ninja -C build
- ninja -C build install
- cd build && meson test -v - cd build && meson test -v
- i686-w64-mingw32-nm -A -g src/libdav1d.a | grep " [ABCDGRST] " | (! grep -E -v " \.| _*dav1d_")
artifacts:
name: "$CI_JOB_NAME-$CI_COMMIT_REF_SLUG"
paths:
- build/dav1d_install/
expire_in: 1 week
build-win32-unaligned-stack: build-debian-avx512:
extends: .debian-llvm-mingw-common extends: .debian-amd64-common
tags:
- docker
- amd64-avx512
variables:
CFLAGS: '-mavx'
script: script:
- wineserver -p && wine wineboot - meson setup build --buildtype debugoptimized
- meson build --buildtype release --werror
--werror - ninja -C build
--cross-file package/crossfiles/i686-w64-mingw32.meson - cd build
-Dstack_alignment=4 - time meson test -v --suite checkasm
build-debian-clang:
extends: .debian-amd64-common
variables:
CC: clang
CC_LD: mold
script:
- meson setup build --buildtype release
--werror
-Dtrim_dsp=false
- ninja -C build - ninja -C build
- cd build && meson test -v - cd build && meson test -v
build-win64: build-win:
extends: .debian-amd64-common extends: .debian-amd64-common
script: script:
- wineserver -p && wine wineboot - wineserver -p && wine wineboot
- meson build --buildtype release - meson setup build --buildtype release
--werror --werror
--libdir lib --libdir lib
--prefix "$(pwd)/build/dav1d_install" --prefix "$(pwd)/build/dav1d_install"
--cross-file package/crossfiles/x86_64-w64-mingw32.meson --cross-file package/crossfiles/${CROSSFILE}.meson
-Ddefault_library=both -Ddefault_library=both
-Dtrim_dsp=false
- ninja -C build - ninja -C build
- ninja -C build install - ninja -C build install
- cd build && meson test -v - cd build && meson test -v
- x86_64-w64-mingw32-nm -A -g src/libdav1d.a | grep " [ABCDGRST] " | (! grep -E -v " \.| _*dav1d_") - ${CROSSFILE}-nm -A -g src/libdav1d.a | grep " [ABCDGRST] " | (! grep -E -v " \.| _*dav1d_")
artifacts: artifacts:
name: "$CI_JOB_NAME-$CI_COMMIT_REF_SLUG" name: "$CI_JOB_NAME-$CI_COMMIT_REF_SLUG"
paths: paths:
- build/dav1d_install/ - build/dav1d_install/
expire_in: 1 week expire_in: 1 week
parallel:
matrix:
- CROSSFILE: [i686-w64-mingw32, x86_64-w64-mingw32]
build-win-arm32: build-win32-unaligned-stack:
extends: .debian-llvm-mingw-common extends: .debian-llvm-mingw-common
script: script:
- meson build --buildtype release - wineserver -p && wine wineboot
--werror - meson setup build --buildtype release
--libdir lib --werror
--prefix "$(pwd)/build/dav1d_install" --cross-file package/crossfiles/i686-w64-mingw32.meson
--cross-file /opt/crossfiles/armv7-w64-mingw32.meson -Dstack_alignment=4
-Ddefault_library=both -Dtrim_dsp=false
- ninja -C build - ninja -C build
- armv7-w64-mingw32-nm -A -g build/src/libdav1d.a | grep " [ABCDGRST] " | (! grep -E -v " \.| _*dav1d_") - cd build && meson test -v
build-win-arm64: build-win-arm:
extends: .debian-llvm-mingw-common extends: .debian-llvm-mingw-common
script: script:
- meson build --buildtype release - meson setup build --buildtype release
--werror --werror
--libdir lib --libdir lib
--prefix "$(pwd)/build/dav1d_install" --prefix "$(pwd)/build/dav1d_install"
--cross-file /opt/crossfiles/aarch64-w64-mingw32.meson --cross-file package/crossfiles/${CROSSFILE}.meson
-Ddefault_library=both -Ddefault_library=both
-Dtrim_dsp=false
- ninja -C build - ninja -C build
- ninja -C build install - ninja -C build install
- aarch64-w64-mingw32-nm -A -g build/src/libdav1d.a | grep " [ABCDGRST] " | (! grep -E -v " \.| _*dav1d_") - ${CROSSFILE}-nm -A -g build/src/libdav1d.a | grep " [ABCDGRST] " | (! grep -E -v " \.| _*dav1d_")
artifacts: artifacts:
name: "$CI_JOB_NAME-$CI_COMMIT_REF_SLUG" name: "$CI_JOB_NAME-$CI_COMMIT_REF_SLUG"
paths: paths:
- build/dav1d_install/ - build/dav1d_install/
expire_in: 1 week expire_in: 1 week
parallel:
matrix:
- CROSSFILE: [armv7-w64-mingw32, aarch64-w64-mingw32]
.build-android-common: .build-android-common:
extends: .android-common extends: .android-common
script: script:
- meson build --buildtype release - meson setup build --buildtype release
--werror --werror
--libdir lib --libdir lib
--prefix "$(pwd)/build/dav1d_install" --prefix "$(pwd)/build/dav1d_install"
--cross-file $CROSSFILE --cross-file $CROSSFILE
-Ddefault_library=both -Ddefault_library=both
-Dtrim_dsp=false
- ninja -C build - ninja -C build
- ninja -C build install - ninja -C build install
...@@ -235,22 +308,20 @@ build-android-armv7: ...@@ -235,22 +308,20 @@ build-android-armv7:
extends: .build-android-common extends: .build-android-common
variables: variables:
CROSSFILE: package/crossfiles/arm-android.meson CROSSFILE: package/crossfiles/arm-android.meson
except: rules:
- tags - if: '$CI_COMMIT_BRANCH'
build-android-aarch64: build-android-aarch64:
extends: .build-android-common extends: .build-android-common
variables: variables:
CROSSFILE: package/crossfiles/aarch64-android.meson CROSSFILE: package/crossfiles/aarch64-android.meson
except: rules:
- tags - if: '$CI_COMMIT_BRANCH'
build-android-armv7-release: build-android-armv7-release:
extends: build-android-armv7 extends: build-android-armv7
except: rules:
only: - if: '$CI_COMMIT_TAG && $CI_PROJECT_PATH == "videolan/dav1d"'
refs:
- tags@videolan/dav1d
artifacts: artifacts:
name: "$CI_JOB_NAME-$CI_COMMIT_REF_SLUG" name: "$CI_JOB_NAME-$CI_COMMIT_REF_SLUG"
paths: paths:
...@@ -259,10 +330,8 @@ build-android-armv7-release: ...@@ -259,10 +330,8 @@ build-android-armv7-release:
build-android-aarch64-release: build-android-aarch64-release:
extends: build-android-aarch64 extends: build-android-aarch64
except: rules:
only: - if: '$CI_COMMIT_TAG && $CI_PROJECT_PATH == "videolan/dav1d"'
refs:
- tags@videolan/dav1d
artifacts: artifacts:
name: "$CI_JOB_NAME-$CI_COMMIT_REF_SLUG" name: "$CI_JOB_NAME-$CI_COMMIT_REF_SLUG"
paths: paths:
...@@ -272,8 +341,8 @@ build-android-aarch64-release: ...@@ -272,8 +341,8 @@ build-android-aarch64-release:
build-debian-aarch64: build-debian-aarch64:
extends: .debian-aarch64-common extends: .debian-aarch64-common
script: script:
- meson build --buildtype debugoptimized - meson setup build --buildtype debugoptimized
--werror --werror
- ninja -C build - ninja -C build
- cd build && meson test -v - cd build && meson test -v
...@@ -283,35 +352,49 @@ build-debian-aarch64-clang-5: ...@@ -283,35 +352,49 @@ build-debian-aarch64-clang-5:
CC: clang-5.0 CC: clang-5.0
CFLAGS: '-integrated-as' CFLAGS: '-integrated-as'
script: script:
- meson build --buildtype release - meson setup build --buildtype release
- ninja -C build
- cd build && meson test -v
build-debian-aarch64-clang-18:
extends: .debian-amd64-common
variables:
QEMU_LD_PREFIX: /usr/aarch64-linux-gnu/
script:
- meson setup build --buildtype release
-Dtrim_dsp=false
--werror
--cross-file package/crossfiles/aarch64-linux-clang.meson
- ninja -C build - ninja -C build
- cd build && meson test -v - cd build && meson test -v
build-macos: build-macos:
stage: build stage: build
tags: tags:
- amd64
- macos - macos
script: script:
- meson build --buildtype release - meson setup build --buildtype release
-Ddefault_library=both -Ddefault_library=both
--werror -Dtrim_dsp=false
--werror
- ninja -C build - ninja -C build
- cd build && meson test -v - cd build && meson test -v
build-debian-werror: build-debian-werror:
extends: .debian-aarch64-common extends: .debian-aarch64-common
variables: variables:
CC: clang-7 CC: clang
script: script:
- meson build --buildtype debug - meson setup build --buildtype debug
--werror --werror
- ninja -C build - ninja -C build
build-debian-armv7: build-debian-armv7:
extends: .debian-armv7-common extends: .debian-armv7-common
script: script:
- linux32 meson build --buildtype debugoptimized - linux32 meson setup build --buildtype debugoptimized
--werror --werror
- ninja -C build - ninja -C build
- cd build && meson test -v - cd build && meson test -v
...@@ -321,32 +404,61 @@ build-debian-armv7-clang-5: ...@@ -321,32 +404,61 @@ build-debian-armv7-clang-5:
CC: clang-5.0 CC: clang-5.0
CFLAGS: '-integrated-as' CFLAGS: '-integrated-as'
script: script:
- linux32 meson build --buildtype release - linux32 meson setup build --buildtype release
- ninja -C build - ninja -C build
- cd build && meson test -v - cd build && meson test -v
build-ubuntu-snap:
extends: .ubuntu-common
script:
- cd package/snap && snapcraft snap
- |
if [ "$CI_PROJECT_NAMESPACE" = "videolan" ]; then
echo $SNAP_LOGIN | base64 --decode | snapcraft login --with -
snapcraft push dav1d_*.snap --release edge
snapcraft logout
fi
artifacts:
name: "$CI_JOB_NAME-$CI_COMMIT_REF_SLUG"
paths:
- package/snap/dav1d_*.snap
expire_in: 1 week
allow_failure: true
build-debian-ppc64le: build-debian-ppc64le:
extends: .debian-ppc64le-common extends: .debian-ppc64le-common
variables:
CC: gcc-13
script: script:
- meson build --buildtype release - meson setup build --buildtype release
--werror -Dtrim_dsp=false
--werror
- ninja -C build
- cd build && meson test -v
build-debian-wasm:
extends: .debian-wasm-emscripten-common
script:
- source $EMSCRIPTEN_SDK/emsdk_env.sh
- meson setup build --buildtype release
--werror
--default-library static
--cross-file package/crossfiles/${CROSSFILE}.meson
- ninja -C build
- cd build && meson test -v
parallel:
matrix:
- CROSSFILE: [wasm32, wasm64]
build-debian-riscv64:
extends: .debian-amd64-common
variables:
QEMU_CPU: rv64,v=true,vext_spec=v1.0,vlen=256,elen=64
QEMU_LD_PREFIX: /usr/riscv64-linux-gnu/
script:
- meson setup build --buildtype release
-Dtrim_dsp=false
--werror
--cross-file package/crossfiles/${CROSSFILE}.meson
- ninja -C build
- cd build && meson test -v
parallel:
matrix:
- CROSSFILE: [riscv64-linux, riscv64-linux-clang]
build-debian-loongarch64:
extends: .debian-amd64-common
variables:
QEMU_CPU: max-loongarch-cpu
QEMU_LD_PREFIX: /opt/cross-tools/target/
script:
- meson setup build --buildtype release
-Dtrim_dsp=false
--werror
--cross-file package/crossfiles/loongarch64-linux.meson
- ninja -C build - ninja -C build
- cd build && meson test -v - cd build && meson test -v
...@@ -362,7 +474,12 @@ build-debian-ppc64le: ...@@ -362,7 +474,12 @@ build-debian-ppc64le:
- test -d cache/dav1d-test-data.git && GIT_DIR=cache/dav1d-test-data.git git fetch --refmap=refs/heads/master:refs/heads/master origin master - test -d cache/dav1d-test-data.git && GIT_DIR=cache/dav1d-test-data.git git fetch --refmap=refs/heads/master:refs/heads/master origin master
- test -d cache/dav1d-test-data.git || git clone --bare https://code.videolan.org/videolan/dav1d-test-data.git cache/dav1d-test-data.git - test -d cache/dav1d-test-data.git || git clone --bare https://code.videolan.org/videolan/dav1d-test-data.git cache/dav1d-test-data.git
- git clone cache/dav1d-test-data.git tests/dav1d-test-data - git clone cache/dav1d-test-data.git tests/dav1d-test-data
- git -C tests/dav1d-test-data describe --always --long
dependencies: [] dependencies: []
artifacts:
when: always
reports:
junit: build/meson-logs/testlog.junit.xml
.test-asm-common: .test-asm-common:
extends: extends:
...@@ -374,25 +491,47 @@ build-debian-ppc64le: ...@@ -374,25 +491,47 @@ build-debian-ppc64le:
- avx2 - avx2
script: script:
- meson configure build -Dtestdata_tests=true - meson configure build -Dtestdata_tests=true
- ninja -C build
- cd build - cd build
- exit_code=0 - exit_code=0
- time meson test -q --suite testdata-8 --suite testdata-10 --suite testdata-12 --test-args "--cpumask 0" || exit_code=$((exit_code + $?)) - time meson test -q --suite testdata --test-args "--cpumask 0" || exit_code=$((exit_code + $?))
- time meson test -q --suite testdata-8 --suite testdata-10 --suite testdata-12 --test-args "--cpumask sse2" || exit_code=$((exit_code + $?)) - time meson test -q --suite testdata --test-args "--cpumask sse2" || exit_code=$((exit_code + $?))
- time meson test -q --suite testdata-8 --suite testdata-10 --suite testdata-12 --test-args "--cpumask ssse3" || exit_code=$((exit_code + $?)) - time meson test -q --suite testdata --test-args "--cpumask ssse3" || exit_code=$((exit_code + $?))
- time meson test -q --suite testdata-8 --suite testdata-10 --suite testdata-12 --test-args "--cpumask sse41" || exit_code=$((exit_code + $?)) - time meson test -q --suite testdata --test-args "--cpumask sse41" || exit_code=$((exit_code + $?))
- time meson test -q --suite testdata-8 --suite testdata-10 --suite testdata-12 --test-args "--cpumask avx2" || exit_code=$((exit_code + $?)) - time meson test -q --suite testdata --test-args "--cpumask avx2" || exit_code=$((exit_code + $?))
- if [ $exit_code -ne 0 ]; then exit $exit_code; fi - if [ $exit_code -ne 0 ]; then exit $exit_code; fi
.test-argon:
stage: test
cache:
key: argon-20230512
paths:
- cache/argon/
variables:
ARGON_URL: https://streams.videolan.org/argon/argon.tar.zst
before_script:
- test -d cache/argon || mkdir -p cache/argon
- test -f cache/argon/argon.tar.zst ||
(cd cache/argon && curl --remote-name "${ARGON_URL}" --remote-name "${ARGON_URL}.sha512sum" &&
sha512sum --check argon.tar.zst.sha512sum )
- tar -xf cache/argon/argon.tar.zst -C tests
dependencies: []
allow_failure: true
rules:
- if: '$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH'
test-debian: test-debian:
extends: extends:
- .debian-amd64-common - .debian-amd64-common
- .test-common - .test-common
needs: ["build-debian"] needs: ["build-debian"]
script: script:
- meson build --buildtype release - meson setup build --buildtype release
-Dtestdata_tests=true -Dtestdata_tests=true
-Dlogging=false -Denable_seek_stress=true
-Db_coverage=true -Dlogging=false
-Db_coverage=true
-Dtrim_dsp=false
- ninja -C build - ninja -C build
- cd build && time meson test -v - cd build && time meson test -v
- ninja coverage-html - ninja coverage-html
...@@ -400,13 +539,18 @@ test-debian: ...@@ -400,13 +539,18 @@ test-debian:
- ninja coverage-xml - ninja coverage-xml
- grep -Eo 'line-rate="[^"]+"' meson-logs/coverage.xml | head -n 1 | - grep -Eo 'line-rate="[^"]+"' meson-logs/coverage.xml | head -n 1 |
grep -Eo '[0-9.]+' | awk '{ print "coverage:", $1 * 100 } ' grep -Eo '[0-9.]+' | awk '{ print "coverage:", $1 * 100 } '
- time meson test -v --suite testdata_seek-stress --test-args "--threads 2 --framedelay 1"
- time meson test -v --suite testdata_seek-stress --test-args "--threads 2 --framedelay 2"
- time meson test -v --suite testdata --test-args "--threads=1 --negstride"
coverage: '/^coverage: (\d+.\d+)$/' coverage: '/^coverage: (\d+.\d+)$/'
artifacts: artifacts:
expose_as: 'Coverage HTML report' expose_as: 'Coverage HTML report'
paths: paths:
- coverage/ - coverage/
reports: reports:
cobertura: build/meson-logs/coverage.xml coverage_report:
coverage_format: cobertura
path: build/meson-logs/coverage.xml
test-debian-asm: test-debian-asm:
extends: extends:
...@@ -420,20 +564,23 @@ test-debian32-asm: ...@@ -420,20 +564,23 @@ test-debian32-asm:
needs: ["build-debian32"] needs: ["build-debian32"]
dependencies: ["build-debian32"] dependencies: ["build-debian32"]
test-debian-mt: test-debian-avx512:
extends: extends:
- .debian-amd64-common - .debian-amd64-common
- .test-common - .test-common
needs: ["build-debian"] tags:
dependencies: ["build-debian"] - docker
- amd64-avx512
variables:
CFLAGS: '-mavx'
needs: ["build-debian-avx512"]
script: script:
- meson configure build -Dtestdata_tests=true - meson setup build --buildtype release
- cd build -Dtestdata_tests=true
- exit_code=0 -Dtrim_dsp=false
- time meson test -q --suite testdata-8 --suite testdata-10 --suite testdata-12 --test-args "--tilethreads 1 --framethreads 2" || exit_code=$((exit_code + $?)) - ninja -C build
- time meson test -q --suite testdata-8 --suite testdata-10 --suite testdata-12 --test-args "--tilethreads 2 --framethreads 1" || exit_code=$((exit_code + $?)) - cd build && time meson test --suite testdata --test-args "--cpumask avx512icl"
- time meson test -q --suite testdata-8 --suite testdata-10 --suite testdata-12 --test-args "--tilethreads 2 --framethreads 2" || exit_code=$((exit_code + $?)) - time meson test --suite testdata --test-args "--threads 2 --framedelay 2 --cpumask avx512icl"
- if [ $exit_code -ne 0 ]; then exit $exit_code; fi
test-debian-unaligned-stack: test-debian-unaligned-stack:
extends: extends:
...@@ -445,10 +592,12 @@ test-debian-unaligned-stack: ...@@ -445,10 +592,12 @@ test-debian-unaligned-stack:
- avx2 - avx2
- amd64 - amd64
script: script:
- meson build --buildtype release - meson setup build --buildtype release
-Dtestdata_tests=true -Dtestdata_tests=true
-Dlogging=false -Denable_seek_stress=true
-Dstack_alignment=16 -Dlogging=false
-Dstack_alignment=16
-Dtrim_dsp=false
- ninja -C build - ninja -C build
- cd build && time meson test -v - cd build && time meson test -v
...@@ -460,15 +609,16 @@ test-debian-asan: ...@@ -460,15 +609,16 @@ test-debian-asan:
variables: variables:
ASAN_OPTIONS: 'detect_leaks=0' ASAN_OPTIONS: 'detect_leaks=0'
script: script:
- meson build --buildtype debugoptimized - meson setup build --buildtype debugoptimized
-Dtestdata_tests=true -Dtestdata_tests=true
-Dlogging=false -Dlogging=false
-Db_sanitize=address -Db_sanitize=address
- ninja -C build - ninja -C build
- cd build - cd build
- exit_code=0 - exit_code=0
- time meson test -v --setup=sanitizer --test-args "--cpumask 0" || exit_code=$((exit_code + $?)) - time meson test -v --setup=sanitizer --suite checkasm || exit_code=$((exit_code + $?))
- time meson test -v --setup=sanitizer --test-args "--cpumask 0xff" || exit_code=$((exit_code + $?)) - time meson test -v --setup=sanitizer --suite testdata --test-args "--cpumask 0" || exit_code=$((exit_code + $?))
- time meson test -v --setup=sanitizer --suite testdata --test-args "--cpumask 0xff" || exit_code=$((exit_code + $?))
- if [ $exit_code -ne 0 ]; then exit $exit_code; fi - if [ $exit_code -ne 0 ]; then exit $exit_code; fi
test-debian-msan: test-debian-msan:
...@@ -480,12 +630,13 @@ test-debian-msan: ...@@ -480,12 +630,13 @@ test-debian-msan:
MSAN_OPTIONS: 'exitcode=1' MSAN_OPTIONS: 'exitcode=1'
CC: clang CC: clang
script: script:
- meson build --buildtype debugoptimized - meson setup build --buildtype debugoptimized
-Dtestdata_tests=true -Dtestdata_tests=true
-Dlogging=false -Denable_seek_stress=true
-Db_sanitize=memory -Dlogging=false
-Db_lundef=false -Db_sanitize=memory
-Denable_asm=false -Db_lundef=false
-Denable_asm=false
- ninja -C build - ninja -C build
- cd build && time meson test -v --setup=sanitizer - cd build && time meson test -v --setup=sanitizer
...@@ -498,30 +649,58 @@ test-debian-ubsan: ...@@ -498,30 +649,58 @@ test-debian-ubsan:
UBSAN_OPTIONS: 'print_stacktrace=1:halt_on_error=1' UBSAN_OPTIONS: 'print_stacktrace=1:halt_on_error=1'
CC: clang CC: clang
script: script:
- meson build --buildtype debugoptimized - meson setup build --buildtype debugoptimized
-Dtestdata_tests=true -Dtestdata_tests=true
-Dlogging=false -Denable_seek_stress=true
-Db_sanitize=undefined -Dlogging=false
-Db_lundef=false -Db_sanitize=undefined
-Denable_asm=false -Db_lundef=false
-Denable_asm=false
- ninja -C build - ninja -C build
- cd build && time meson test -v --setup=sanitizer - cd build && time meson test -v --setup=sanitizer
test-debian-tsan:
extends:
- .debian-amd64-common
- .test-common
needs: ["build-debian"]
variables:
TSAN_OPTIONS: 'halt_on_error=1'
CC: clang
script:
- meson setup build --buildtype debugoptimized
-Dtestdata_tests=true
-Denable_seek_stress=true
-Dlogging=false
-Db_sanitize=thread
-Db_lundef=false
- ninja -C build
- cd build
- exit_code=0
- time meson test -v --setup=sanitizer --suite testdata --test-args "--threads 2 --framedelay 1" || exit_code=$((exit_code + $?))
- time meson test -v --setup=sanitizer --suite testdata --test-args "--threads 2 --framedelay 2" || exit_code=$((exit_code + $?))
- time meson test -v --setup=sanitizer --suite testdata --test-args "--threads 2 --framedelay 2 --negstride" || exit_code=$((exit_code + $?))
- time meson test -v --setup=sanitizer --suite testdata_seek-stress --test-args "--threads 2 --framedelay 1" || exit_code=$((exit_code + $?))
- time meson test -v --setup=sanitizer --suite testdata_seek-stress --test-args "--threads 2 --framedelay 2" || exit_code=$((exit_code + $?))
- time meson test -v --setup=sanitizer --suite oss-fuzz-asan --suite oss-fuzz-msan --suite oss-fuzz-ubsan || exit_code=$((exit_code + $?))
- if [ $exit_code -ne 0 ]; then exit $exit_code; fi
test-win64: test-win64:
extends: extends:
- .debian-amd64-common - .debian-amd64-common
- .test-common - .test-common
needs: ["build-win64"] needs: ["build-win: [x86_64-w64-mingw32]"]
tags: tags:
- docker - docker
- avx2 - avx2
- amd64 - amd64
script: script:
- wineserver -p && wine wineboot - wineserver -p && wine wineboot
- meson build --buildtype release - meson setup build --buildtype release
-Dtestdata_tests=true -Dtestdata_tests=true
-Dlogging=false -Dlogging=false
--cross-file package/crossfiles/x86_64-w64-mingw32.meson -Dtrim_dsp=false
--cross-file package/crossfiles/x86_64-w64-mingw32.meson
- ninja -C build - ninja -C build
- cd build && time meson test -v - cd build && time meson test -v
...@@ -531,9 +710,10 @@ test-debian-aarch64: ...@@ -531,9 +710,10 @@ test-debian-aarch64:
- .test-common - .test-common
needs: ["build-debian-aarch64"] needs: ["build-debian-aarch64"]
script: script:
- meson build --buildtype release - meson setup build --buildtype release
-Dtestdata_tests=true -Dtestdata_tests=true
-Dlogging=false -Dlogging=false
-Dtrim_dsp=false
- ninja -C build - ninja -C build
- cd build && time meson test -v - cd build && time meson test -v
...@@ -541,14 +721,66 @@ test-debian-ppc64le: ...@@ -541,14 +721,66 @@ test-debian-ppc64le:
extends: extends:
- .debian-ppc64le-common - .debian-ppc64le-common
- .test-common - .test-common
variables:
CC: gcc-13
needs: ["build-debian-ppc64le"] needs: ["build-debian-ppc64le"]
script: script:
- meson build --buildtype release - meson setup build --buildtype release
-Dtestdata_tests=true -Dtestdata_tests=true
-Dlogging=false -Dlogging=false
-Dtrim_dsp=false
- ninja -C build - ninja -C build
- cd build && time meson test -v - cd build && time meson test -v
test-debian-riscv64:
extends:
- .debian-amd64-common
- .test-common
needs: ["build-debian-riscv64"]
script:
- meson setup build --buildtype release
-Dtestdata_tests=true
-Dlogging=false
-Dtrim_dsp=false
--cross-file package/crossfiles/riscv64-linux.meson
- ninja -C build
- cd build && time meson test -v --timeout-multiplier 10
variables:
QEMU_LD_PREFIX: /usr/riscv64-linux-gnu/
parallel:
matrix:
- QEMU_CPU: [ "rv64,v=true,vext_spec=v1.0,vlen=128,elen=64",
"rv64,v=true,vext_spec=v1.0,vlen=256,elen=64",
"rv64,v=true,vext_spec=v1.0,vlen=512,elen=64",
"rv64,v=true,vext_spec=v1.0,vlen=1024,elen=64" ]
test-debian-aarch64-qemu:
extends:
- .debian-amd64-common
- .test-common
needs: ["build-debian-aarch64"]
script:
- meson setup build --buildtype release
-Dtestdata_tests=true
-Dlogging=false
-Dtrim_dsp=false
--cross-file package/crossfiles/aarch64-linux.meson
- ninja -C build
- cd build && time meson test -v --timeout-multiplier 10
variables:
QEMU_LD_PREFIX: /usr/aarch64-linux-gnu/
parallel:
matrix:
# sve-default-vector-length sets the max vector length in bytes;
# the default is 64, allowing up to 512 bit vectors. Testing 1024
# and 2048 bit vectors requires raising this limit. The sve<n>
# option sets the active vector length in bits.
- QEMU_CPU: [ "max,sve-default-vector-length=256,sve128=on",
"max,sve-default-vector-length=256,sve256=on",
"max,sve-default-vector-length=256,sve512=on",
"max,sve-default-vector-length=256,sve1024=on",
"max,sve-default-vector-length=256,sve2048=on" ]
test-debian-armv7-clang-5: test-debian-armv7-clang-5:
extends: extends:
- .debian-armv7-common - .debian-armv7-common
...@@ -558,18 +790,121 @@ test-debian-armv7-clang-5: ...@@ -558,18 +790,121 @@ test-debian-armv7-clang-5:
CC: clang-5.0 CC: clang-5.0
CFLAGS: '-integrated-as' CFLAGS: '-integrated-as'
script: script:
- linux32 meson build --buildtype release - linux32 meson setup build --buildtype release
-Dtestdata_tests=true -Dtestdata_tests=true
-Dlogging=false -Dlogging=false
-Dtrim_dsp=false
- ninja -C build - ninja -C build
- cd build && time meson test -v - cd build && time meson test -v
test-debian-loongarch64:
extends:
- .debian-amd64-common
- .test-common
needs: ["build-debian-loongarch64"]
variables:
QEMU_CPU: max-loongarch-cpu
QEMU_LD_PREFIX: /opt/cross-tools/target/
script:
- meson setup build --buildtype release
-Dtestdata_tests=true
-Dlogging=false
-Dtrim_dsp=false
--cross-file package/crossfiles/loongarch64-linux.meson
- ninja -C build
- cd build && time meson test -v --timeout-multiplier 10
.test-argon-script: &test-argon-script
- meson setup build --buildtype release
-Dlogging=false
-Dtrim_dsp=false
- cd build && ninja
- exit_code=0
test-debian-argon:
extends:
- .debian-amd64-common
- .test-argon
needs: ["build-debian"]
tags:
- docker
- amd64
- avx2
script:
- *test-argon-script
- ../tests/dav1d_argon.bash -t 1 -c 0 || exit_code=$((exit_code + $?))
- ../tests/dav1d_argon.bash -t 2 -c sse2 -g 0 || exit_code=$((exit_code + $?))
- ../tests/dav1d_argon.bash -t 3 -c ssse3 || exit_code=$((exit_code + $?))
- ../tests/dav1d_argon.bash -t 4 -c sse41 || exit_code=$((exit_code + $?))
- ../tests/dav1d_argon.bash -t 5 -c avx2 || exit_code=$((exit_code + $?))
- if [ $exit_code -ne 0 ]; then exit $exit_code; fi
test-debian32-argon:
extends:
- .debian-amd64-common
- .test-argon
needs: ["build-debian32"]
script:
- meson setup build --buildtype release
-Dlogging=false
-Dtrim_dsp=false
--cross-file package/crossfiles/i686-linux32.meson
- cd build && ninja
- exit_code=0
- ../tests/dav1d_argon.bash -t 2 -c sse2 || exit_code=$((exit_code + $?))
- ../tests/dav1d_argon.bash -t 2 -c ssse3 || exit_code=$((exit_code + $?))
- ../tests/dav1d_argon.bash -t 2 -c sse41 -g 0 || exit_code=$((exit_code + $?))
- if [ $exit_code -ne 0 ]; then exit $exit_code; fi
test-debian-argon-avx512:
extends:
- .debian-amd64-common
- .test-argon
needs: ["build-debian-avx512"]
tags:
- docker
- amd64-avx512
script:
- *test-argon-script
- ../tests/dav1d_argon.bash -t 2 -c avx512icl || exit_code=$((exit_code + $?))
- if [ $exit_code -ne 0 ]; then exit $exit_code; fi
test-debian-armv7-argon:
extends:
- .debian-armv7-common
- .test-argon
needs: ["build-debian-armv7"]
script:
- *test-argon-script
- ../tests/dav1d_argon.bash -t 3 -c 0 || exit_code=$((exit_code + $?))
- ../tests/dav1d_argon.bash -t 2 -c neon || exit_code=$((exit_code + $?))
- ../tests/dav1d_argon.bash -t 1 -c neon -g 0 || exit_code=$((exit_code + $?))
- if [ $exit_code -ne 0 ]; then exit $exit_code; fi
test-debian-aarch64-argon:
extends:
- .debian-aarch64-common
- .test-argon
needs: ["build-debian-aarch64"]
tags:
- docker
- aarch64
- dotprod
script:
- *test-argon-script
- ../tests/dav1d_argon.bash -t 2 -c 0 || exit_code=$((exit_code + $?))
- ../tests/dav1d_argon.bash -t 3 -c neon || exit_code=$((exit_code + $?))
- ../tests/dav1d_argon.bash -t 1 -c neon -g 0 || exit_code=$((exit_code + $?))
- ../tests/dav1d_argon.bash -t 4 -c dotprod || exit_code=$((exit_code + $?))
- if [ $exit_code -ne 0 ]; then exit $exit_code; fi
.pages-common: .pages-common:
extends: .debian-amd64-common extends: .debian-amd64-common
script: script:
- meson build --buildtype release - meson setup build --buildtype release
--werror --werror
-Denable_docs=true
- ninja -C build doc/html - ninja -C build doc/html
- mv build/doc/html public - mv build/doc/html public
artifacts: artifacts:
...@@ -578,14 +913,14 @@ test-debian-armv7-clang-5: ...@@ -578,14 +913,14 @@ test-debian-armv7-clang-5:
build-pages: build-pages:
extends: .pages-common extends: .pages-common
except: rules:
refs: - if: '$CI_COMMIT_BRANCH && $CI_COMMIT_BRANCH != $CI_DEFAULT_BRANCH'
- master
pages: pages:
extends: .pages-common extends: .pages-common
only: rules:
refs: - if: '$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH'
- master changes:
changes: - include/dav1d/*
- include/dav1d/* - doc/meson.build
- doc/Doxyfile.in.in
Changes for 0.8.0 'Eurasian hobby": Changes for 1.5.1 'Sonic':
--------------------------
1.5.1 is a minor release of dav1d, focusing on optimizations and stack reduction:
- Rewrite of the looprestoration (SGR, wiener) to reduce stack usage
- Rewrite of {put,prep}_scaled functions
Now, the required stack space for dav1d should be: 62 KB on x86_64 and
58KB on arm and aarch64.
- Improvements on the SSSE3 SGR
- Improvements on ARM32/ARM64 looprestoration optimizations
- RISC-V: blend optimizations for high bitdepth
- Power9: blend optimizations for 8bpc
- Port RISC-V to POSIX/non-Linux OS
- AArch64: Add Neon implementation of load_tmvs
- Fix a rare, but possible deadlock, in flush()
Changes for 1.5.0 'Sonic':
--------------------------
1.5.0 is a major release of dav1d, that:
- WARNING: we removed some of the SSE2 optimizations, so if you care about
systems without SSSE3, you should be careful when updating!
- Add Arm OpenBSD run-time CPU feature
- Optimize index offset calculations for decode_coefs
- picture: copy HDR10+ and T35 metadata only to visible frames
- SSSE3 new optimizations for 6-tap (8bit and hbd)
- AArch64/SVE: Add HBD subpel filters using 128-bit SVE2
- AArch64: Add USMMLA implempentation for 6-tap H/HV
- AArch64: Optimize Armv8.0 NEON for HBD horizontal filters and 6-tap filters
- Power9: Optimized ITX till 16x4.
- Loongarch: numerous optimizations
- RISC-V optimizations for pal, cdef_filter, ipred, mc_blend, mc_bdir, itx
- Allow playing videos in full-screen mode in dav1dplay
Changes for 1.4.3 'Road Runner':
--------------------------------
1.4.3 is a small release focused on security issues
- AArch64: Fix potential out of bounds access in DotProd H/HV filters
- cli: Prevent buffer over-read
Changes for 1.4.2 'Road Runner':
--------------------------------
1.4.2 is a small release of dav1d, improving notably ARM, AVX-512 and PowerPC
- AVX2 optimizations for 8-tap and new variants for 6-tap
- AVX-512 optimizations for 8-tap and new variants for 6-tap
- Improve entropy decoding on ARM64
- New ARM64 optimizations for convolutions based on DotProd extension
- New ARM64 optimizations for convolutions based on i8mm extension
- New ARM64 optimizations for subpel and prep filters for i8mm
- Misc improvements on existing ARM64 optimizations, notably for put/prep
- New PowerPC9 optimizations for loopfilter
- Support for macOS kperf API for benchmarking
Changes for 1.4.1 'Road Runner':
--------------------------------
1.4.1 is a small release of dav1d, improving notably ARM and RISC-V speed
- Optimizations for 6tap filters for NEON (ARM)
- More RISC-V optimizations for itx (4x8, 8x4, 4x16, 16x4, 8x16, 16x8)
- Reduction of binary size on ARM64, ARM32 and RISC-V
- Fix out-of-bounds read in 8bpc SSE2/SSSE3 wiener_filter
- Msac optimizations
Changes for 1.4.0 'Road Runner':
--------------------------------
1.4.0 is a medium release of dav1d, focusing on new architecture support and optimizations
- AVX-512 optimizations for z1, z2, z3 in 8bit and high-bitdepth
- New architecture supported: loongarch
- Loongarch optimizations for 8bit
- New architecture supported: RISC-V
- RISC-V optimizations for itx
- Misc improvements in threading and in reducing binary size
- Fix potential integer overflow with extremely large frame sizes (CVE-2024-1580)
Changes for 1.3.0 'Tundra Peregrine Falcon (Calidus)':
------------------------------------------------------
1.3.0 is a medium release of dav1d, focus on new APIs and memory usage reduction.
- Reduce memory usage in numerous places
- ABI break in Dav1dSequenceHeader, Dav1dFrameHeader, Dav1dContentLightLevel structures
- new API function to check the API version: dav1d_version_api()
- Rewrite of the SGR functions for ARM64 to be faster
- NEON implemetation of save_tmvs for ARM32 and ARM64
- x86 palette DSP for pal_idx_finish function
Changes for 1.2.1 'Arctic Peregrine Falcon':
--------------------------------------------
1.2.1 is a small release of dav1d, adding more SIMD and fixes
- Fix a threading race on task_thread.init_done
- NEON z2 8bpc and high bit-depth optimizations
- SSSE3 z2 high bit-depth optimziations
- Fix a desynced luma/chroma planes issue with Film Grain
- Reduce memory consumption
- Improve dav1d_parse_sequence_header() speed
- OBU: Improve header parsing and fix potential overflows
- OBU: Improve ITU-T T.35 parsing speed
- Misc buildsystems, CI and headers fixes
Changes for 1.2.0 'Arctic Peregrine Falcon':
--------------------------------------------
1.2.0 is a small release of dav1d, adding more SIMD and fixes
- Improvements on attachments of props and T.35 entries on output pictures
- NEON z1/z3 high bit-depth optimizations and improvements for 8bpc
- SSSE3 z2/z3 8bpc and SSSE3 z1/z3 high bit-depth optimziations
- refmvs.save_tmvs optimizations in SSSE3/AVX2/AVX-512
- AVX-512 optimizations for high bit-depth itx (16x64, 32x64, 64x16, 64x32, 64x64)
- AVX2 optimizations for 12bpc for 16x32, 32x16, 32x32 itx
Changes for 1.1.0 'Arctic Peregrine Falcon':
--------------------------------------------
1.1.0 is an important release of dav1d, fixing numerous bugs, and adding SIMD
- New function dav1d_get_frame_delay to query the decoder frame delay
- Numerous fixes for strict conformity to the specs and samples
- NEON and AVX-512 misc fixes and improvements
- Partial AVX2 12bpc transform implementations
- AVX-512 high bit-depth cdef_filter, loopfilter, itx
- NEON z1/z3 optimization for 8bpc
- SSSE3 z1 optimization for 8bpc
"From VideoLAN with love"
Changes for 1.0.0 'Peregrine Falcon':
-------------------------------------
1.0.0 is a major release of dav1d, adding important features and bug fixes.
It notably changes, in an important way, the way threading works, by adding
an automatic thread management.
It also adds support for AVX-512 acceleration, and adds speedups to existing x86
code (from SSE2 to AVX2).
1.0.0 adds new grain API to ease acceleration on the GPU, and adds an API call
to get information of which frame failed to decode, in error cases.
Finally, 1.0.0 fixes numerous small bugs that were reported since the beginning
of the project to have a proper release.
.''.
.''. . *''* :_\/_: .
:_\/_: _\(/_ .:.*_\/_* : /\ : .'.:.'.
.''.: /\ : ./)\ ':'* /\ * : '..'. -=:o:=-
:_\/_:'.:::. ' *''* * '.\'/.' _\(/_'.':'.'
: /\ : ::::: *_\/_* -= o =- /)\ ' *
'..' ':::' * /\ * .'/.\'. '
* *..* :
* :
* 1.0.0
Changes for 0.9.2 'Golden Eagle':
---------------------------------
0.9.2 is a small update of dav1d on the 0.9.x branch:
- x86: SSE4 optimizations of inverse transforms for 10bit for all sizes
- x86: mc.resize optimizations with AVX2/SSSE3 for 10/12b
- x86: SSSE3 optimizations for cdef_filter in 10/12b and mc_w_mask_422/444 in 8b
- ARM NEON optimizations for FilmGrain Gen_grain functions
- Optimizations for splat_mv in SSE2/AVX2 and NEON
- x86: SGR improvements for SSSE3 CPUs
- x86: AVX2 optimizations for cfl_ac
Changes for 0.9.1 'Golden Eagle':
---------------------------------
0.9.1 is a middle-size revision of dav1d, adding notably 10b acceleration for SSSE3:
- 10/12b SSSE3 optimizations for mc (avg, w_avg, mask, w_mask, emu_edge),
prep/put_bilin, prep/put_8tap, ipred (dc/h/v, paeth, smooth, pal, filter), wiener,
sgr (10b), warp8x8, deblock, film_grain, cfl_ac/pred for 32bit and 64bit x86 processors
- Film grain NEON for fguv 10/12b, fgy/fguv 8b and fgy/fguv 10/12 arm32
- Fixes for filmgrain on ARM
- itx 10bit optimizations for 4x4/x8/x16, 8x4/x8/x16 for SSE4
- Misc improvements on SSE2, SSE4
Changes for 0.9.0 'Golden Eagle':
---------------------------------
0.9.0 is a major version of dav1d, adding notably 10b acceleration on x64.
Details:
- x86 (64bit) AVX2 implementation of most 10b/12b functions, which should provide
a large boost for high-bitdepth decoding on modern x86 computers and servers.
- ARM64 neon implementation of FilmGrain (4:2:0/4:2:2/4:4:4 8bit)
- New API to signal events happening during the decoding process
Changes for 0.8.2 'Eurasian Hobby':
-----------------------------------
0.8.2 is a middle-size update of the 0.8.0 branch:
- ARM32 optimizations for ipred and itx in 10/12bits,
completing the 10b/12b work on ARM64 and ARM32
- Give the post-filters their own threads
- ARM64: rewrite the wiener functions
- Speed up coefficient decoding, 0.5%-3% global decoding gain
- x86 optimizations for CDEF_filter and wiener in 10/12bit
- x86: rewrite the SGR AVX2 asm
- x86: improve msac speed on SSE2+ machines
- ARM32: improve speed of ipred and warp
- ARM64: improve speed of ipred, cdef_dir, cdef_filter, warp_motion and itx16
- ARM32/64: improve speed of looprestoration
- Add seeking, pausing to the player
- Update the player for rendering of 10b/12b
- Misc speed improvements and fixes on all platforms
- Add a xxh3 muxer in the dav1d application
Changes for 0.8.1 'Eurasian Hobby':
-----------------------------------
0.8.1 is a minor update on 0.8.0:
- Keep references to buffers valid after dav1d_close(). Fixes a regression
caused by the picture buffer pool added in 0.8.0.
- ARM32 optimizations for 10bit bitdepth for SGR
- ARM32 optimizations for 16bit bitdepth for blend/w_masl/emu_edge
- ARM64 optimizations for 10bit bitdepth for SGR
- x86 optimizations for wiener in SSE2/SSSE3/AVX2
Changes for 0.8.0 'Eurasian Hobby':
----------------------------------- -----------------------------------
0.8.0 is a major update for dav1d: 0.8.0 is a major update for dav1d:
...@@ -61,7 +308,7 @@ Changes for 0.6.0 'Gyrfalcon': ...@@ -61,7 +308,7 @@ Changes for 0.6.0 'Gyrfalcon':
- New SSSE3 optimizations for film grain - New SSSE3 optimizations for film grain
- New AVX2 optimizations for msac_adapt16 - New AVX2 optimizations for msac_adapt16
- Fix rare mismatches against the reference decoder, notably because of clipping - Fix rare mismatches against the reference decoder, notably because of clipping
- Improvements on ARM64 on msac, cdef and looprestoration optimizations - Improvements on ARM64 on msac, cdef, mc_blend_v and looprestoration optimizations
- Improvements on AVX2 optimizations for cdef_filter - Improvements on AVX2 optimizations for cdef_filter
- Improvements in the C version for itxfm, cdef_filter - Improvements in the C version for itxfm, cdef_filter
......
...@@ -2,11 +2,13 @@ ...@@ -2,11 +2,13 @@
# dav1d # dav1d
**dav1d** is a new **AV1** cross-platform **d**ecoder, open-source, and focused on speed and correctness. **dav1d** is an **AV1** cross-platform **d**ecoder, open-source, and focused on speed and correctness.
It is now battle-tested and production-ready and can be used everywhere.
The canonical repository URL for this repo is https://code.videolan.org/videolan/dav1d The canonical repository URL for this repo is https://code.videolan.org/videolan/dav1d
This project is partially funded by the *Alliance for Open Media*/**AOM**. This project was partially funded by the *Alliance for Open Media*/**AOM**.
## Goal and Features ## Goal and Features
...@@ -34,17 +36,18 @@ The plan is the following: ...@@ -34,17 +36,18 @@ The plan is the following:
5. Make it fast on mobile, by writing asm for ARMv8 chips, 5. Make it fast on mobile, by writing asm for ARMv8 chips,
6. Make it fast on older desktop, by writing asm for SSSE3+ chips, 6. Make it fast on older desktop, by writing asm for SSSE3+ chips,
7. Make high bit-depth fast on mobile, by writing asm for ARMv8 chips. 7. Make high bit-depth fast on mobile, by writing asm for ARMv8 chips.
### On-going
8. Make it fast on older mobile, by writing asm for ARMv7 chips, 8. Make it fast on older mobile, by writing asm for ARMv7 chips,
9. Make high bit-depth fast on older mobile, by writing asm for ARMv7 chips, 9. Make high bit-depth fast on older mobile, by writing asm for ARMv7 chips,
10. Improve C code base with [various tweaks](https://code.videolan.org/videolan/dav1d/wikis/task-list), 10. Make high bit-depth fast on desktop, by writing asm for AVX2 chips,
11. Accelerate for less common architectures, like PPC, SSE2 or AVX-512. 11. Make high bit-depth fast on older desktop, by writing asm for SSSE3+ chips,
12. Improve threading.
### On-going
13. Improve C code base with [various tweaks](https://code.videolan.org/videolan/dav1d/wikis/task-list),
14. Accelerate for less common architectures, like PPC, SSE2, RISC-V or AVX-512.
### After ### After
12. Make high bit-depth fast on desktop, by writing asm for AVX2 chips, 15. Use more GPU decoding, when possible.
13. Make high bit-depth fast on older desktop, by writing asm for SSSE3+ chips,
14. Use more GPU, when possible.
# Contribute # Contribute
...@@ -59,7 +62,7 @@ Our contributions guidelines are quite strict. We want to build a coherent codeb ...@@ -59,7 +62,7 @@ Our contributions guidelines are quite strict. We want to build a coherent codeb
Notably, the codebase is in pure C and asm. Notably, the codebase is in pure C and asm.
We are on IRC, on the **#dav1d** channel on *Freenode*. We are on IRC, on the **#dav1d** channel on [*Libera.chat*](http://libera.chat/). If you do not have an IRC Client at hand, use [IRC Web Interface](https://web.libera.chat/#dav1d).
See the [contributions document](CONTRIBUTING.md). See the [contributions document](CONTRIBUTING.md).
...@@ -77,9 +80,9 @@ The [VideoLAN Code of Conduct](https://wiki.videolan.org/CoC) applies to this pr ...@@ -77,9 +80,9 @@ The [VideoLAN Code of Conduct](https://wiki.videolan.org/CoC) applies to this pr
# Compile # Compile
1. Install [Meson](https://mesonbuild.com/) (0.47 or higher), [Ninja](https://ninja-build.org/), and, for x86\* targets, [nasm](https://nasm.us/) (2.14 or higher) 1. Install [Meson](https://mesonbuild.com/) (0.49 or higher), [Ninja](https://ninja-build.org/), and, for x86\* targets, [nasm](https://nasm.us/) (2.14 or higher)
2. Run `mkdir build && cd build` to create a build directory and enter it 2. Run `mkdir build && cd build` to create a build directory and enter it
3. Run `meson ..` to configure meson, add `--default-library=static` if static linking is desired 3. Run `meson setup ..` to configure meson, add `--default-library=static` if static linking is desired
4. Run `ninja` to compile 4. Run `ninja` to compile
## Cross-Compilation for 32- or 64-bit Windows, 32-bit Linux ## Cross-Compilation for 32- or 64-bit Windows, 32-bit Linux
...@@ -87,13 +90,13 @@ The [VideoLAN Code of Conduct](https://wiki.videolan.org/CoC) applies to this pr ...@@ -87,13 +90,13 @@ The [VideoLAN Code of Conduct](https://wiki.videolan.org/CoC) applies to this pr
If you're on a linux build machine trying to compile .exe for a Windows target/host machine, run If you're on a linux build machine trying to compile .exe for a Windows target/host machine, run
``` ```
meson build --cross-file=package/crossfiles/x86_64-w64-mingw32.meson meson setup build --cross-file=package/crossfiles/x86_64-w64-mingw32.meson
``` ```
or, for 32-bit: or, for 32-bit:
``` ```
meson build --cross-file=package/crossfiles/i686-w64-mingw32.meson meson setup build --cross-file=package/crossfiles/i686-w64-mingw32.meson
``` ```
`mingw-w64` is a pre-requisite and should be installed on your linux machine via your preferred method or package manager. Note the binary name formats may differ between distributions. Verify the names, and use `alias` if certain binaries cannot be found. `mingw-w64` is a pre-requisite and should be installed on your linux machine via your preferred method or package manager. Note the binary name formats may differ between distributions. Verify the names, and use `alias` if certain binaries cannot be found.
...@@ -101,9 +104,17 @@ meson build --cross-file=package/crossfiles/i686-w64-mingw32.meson ...@@ -101,9 +104,17 @@ meson build --cross-file=package/crossfiles/i686-w64-mingw32.meson
For 32-bit linux, run For 32-bit linux, run
``` ```
meson build --cross-file=package/crossfiles/i686-linux32.meson meson setup build --cross-file=package/crossfiles/i686-linux32.meson
``` ```
## Build documentation
1. Install [doxygen](https://www.doxygen.nl/) and [graphviz](https://www.graphviz.org/)
2. Run `meson setup build -Denable_docs=true` to create the build directory
3. Run `ninja -C build doc/html` to build the docs
The result can be found in `build/doc/html/`. An online version built from master can be found [here](https://videolan.videolan.me/dav1d/).
# Run tests # Run tests
1. In the root directory, run `git clone https://code.videolan.org/videolan/dav1d-test-data.git tests/dav1d-test-data` to fetch the test data repository 1. In the root directory, run `git clone https://code.videolan.org/videolan/dav1d-test-data.git tests/dav1d-test-data` to fetch the test data repository
...@@ -145,6 +156,3 @@ Please read the [AV1 patent license](doc/PATENTS) that applies to the AV1 specif ...@@ -145,6 +156,3 @@ Please read the [AV1 patent license](doc/PATENTS) that applies to the AV1 specif
## Will you care about <my_arch>? <my_os>? ## Will you care about <my_arch>? <my_os>?
- We do, but we don't have either the time or the knowledge. Therefore, patches and contributions welcome. - We do, but we don't have either the time or the knowledge. Therefore, patches and contributions welcome.
## Where can I find documentation?
- The current library documentation, built from master, can be found [here](https://videolan.videolan.me/dav1d/).
# The dav1d project and VideoLAN association would like to thank # The dav1d project and VideoLAN association would like to thank
## AOM ## AOM
The Alliance for Open Media (AOM) for funding this project. The Alliance for Open Media (AOM) for partially funding this project.
## Companies ## Companies
* Two Orioles LLC, for important coding effort * Two Orioles LLC, for important coding effort
...@@ -16,16 +16,20 @@ The Alliance for Open Media (AOM) for funding this project. ...@@ -16,16 +16,20 @@ The Alliance for Open Media (AOM) for funding this project.
And all the dav1d Authors (git shortlog -sn), including: And all the dav1d Authors (git shortlog -sn), including:
Martin Storsjö, Janne Grunau, Henrik Gramner, Ronald S. Bultje, James Almer, Henrik Gramner, Martin Storsjö, Ronald S. Bultje, Janne Grunau, James Almer,
Marvin Scholz, Luc Trudeau, Victorien Le Couviour--Tuffet, Jean-Baptiste Kempf, Victorien Le Couviour--Tuffet, Matthias Dressel, Nathan E. Egge,
Hugo Beauzée-Luyssen, Matthias Dressel, Konstantin Pavlov, David Michael Barr, Jean-Baptiste Kempf, Marvin Scholz, Luc Trudeau, Niklas Haas,
Steve Lhomme, Niklas Haas, B Krishnan Iyer, Francois Cartegnie, Liwei Wang, Hugo Beauzée-Luyssen, Konstantin Pavlov, David Michael Barr, Steve Lhomme,
Nathan E. Egge, Derek Buitenhuis, Michael Bradshaw, Raphaël Zumer, yuanhecai, Luca Barbato, Wan-Teh Chang, Kyle Siefring, B Krishnan Iyer,
Xuefeng Jiang, Luca Barbato, Jan Beich, Wan-Teh Chang, Justin Bull, Boyuan Xiao, Francois Cartegnie, Liwei Wang, David Conrad, Derek Buitenhuis, Jan Beich,
Dale Curtis, Kyle Siefring, Raphael Zumer, Rupert Swarbrick, Thierry Foucu, Michael Bradshaw, Raphaël Zumer, Xuefeng Jiang, Arpad Panyik, Christophe Gisquet,
Thomas Daede, Colin Lee, Emmanuel Gil Peyrot, Lynne, Michail Alvanos, Justin Bull, Boyuan Xiao, Dale Curtis, Emmanuel Gil Peyrot, Raphael Zumer,
Nico Weber, SmilingWolf, Tristan Laurent, Vittorio Giovara, Anisse Astier, Rupert Swarbrick, Thierry Foucu, Thomas Daede, jinbo, André Kempe, Colin Lee,
Dmitriy Sychov, Ewout ter Hoeven, Fred Barbier, Jean-Yves Avenard, Jonathan Wright, Lynne, Michail Alvanos, Nico Weber, Salome Thirot, SmilingWolf,
Mark Shuttleworth, Matthieu Bouron, Nicolas Frattaroli, Pablo Stebler, Tristan Laurent, Tristan Matthews, Vittorio Giovara, Yannis Guyon,
Rostislav Pehlivanov, Shiz, Steinar Midtskogen, Sylvestre Ledru, Timo Gurr, Andrey Semashev, Anisse Astier, Anton Mitrofanov, Charlie Hayden, Dmitriy Sychov,
Tristan Matthews, Xavier Claessens, Xu Guangxin, kossh1 and skal. Ewout ter Hoeven, Fred Barbier, Hao Chen, Jean-Yves Avenard, Joe Drago,
Mark Shuttleworth, Matthieu Bouron, Mehdi Sabwat, Nicolas Frattaroli,
Pablo Stebler, Rostislav Pehlivanov, Sebastian Dröge, Shiz, Steinar Midtskogen,
Sylvain BERTRAND, Sylvestre Ledru, Timo Gurr, Vibhoothi,
Vignesh Venkatasubramanian, Xavier Claessens, Xu Guangxin, kossh1 and skal.
PROJECT_NAME = dav1d PROJECT_NAME = dav1d
PROJECT_NUMBER = \@VCS_TAG\@
PROJECT_BRIEF = dav1d is an AV1 decoder
OUTPUT_DIRECTORY = @DOXYGEN_OUTPUT@ OUTPUT_DIRECTORY = @DOXYGEN_OUTPUT@
STRIP_FROM_PATH = @DOXYGEN_STRIP@ STRIP_FROM_PATH = @DOXYGEN_STRIP@
OUTPUT_LANGUAGE = English OUTPUT_LANGUAGE = English
...@@ -7,10 +9,12 @@ EXTRACT_ALL = YES ...@@ -7,10 +9,12 @@ EXTRACT_ALL = YES
OPTIMIZE_OUTPUT_FOR_C = YES OPTIMIZE_OUTPUT_FOR_C = YES
DOXYFILE_ENCODING = UTF-8 DOXYFILE_ENCODING = UTF-8
TYPEDEF_HIDES_STRUCT = YES TYPEDEF_HIDES_STRUCT = YES
HAVE_DOT = YES
QUIET = YES QUIET = YES
WARNINGS = YES WARNINGS = YES
WARN_IF_UNDOCUMENTED = YES WARN_IF_UNDOCUMENTED = YES
WARN_AS_ERROR = FAIL_ON_WARNINGS
INPUT = @DOXYGEN_INPUT@ INPUT = @DOXYGEN_INPUT@
FILE_PATTERNS = *.h FILE_PATTERNS = *.h
......
# Copyright © 2018, VideoLAN and dav1d authors # Copyright © 2018-2022, VideoLAN and dav1d authors
# All rights reserved. # All rights reserved.
# #
# Redistribution and use in source and binary forms, with or without # Redistribution and use in source and binary forms, with or without
...@@ -22,22 +22,30 @@ ...@@ -22,22 +22,30 @@
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
doxygen = find_program('doxygen', required: false) if not get_option('enable_docs')
dot = find_program('dot', required: false) subdir_done()
endif
if doxygen.found() and dot.found() doxygen = find_program('doxygen')
conf_data = configuration_data() dot = find_program('dot')
conf_data.set('DOXYGEN_INPUT', join_paths(dav1d_src_root, 'include/dav1d'))
conf_data.set('DOXYGEN_STRIP', join_paths(dav1d_src_root, 'include'))
conf_data.set('DOXYGEN_OUTPUT', meson.current_build_dir())
doxyfile = configure_file(input: 'Doxyfile.in',
output: 'Doxyfile',
configuration: conf_data)
custom_target('doc', conf_data = configuration_data()
build_by_default: false, conf_data.set('DOXYGEN_INPUT', dav1d_src_root / 'include/dav1d')
command: [doxygen, doxyfile], conf_data.set('DOXYGEN_STRIP', dav1d_src_root / 'include')
output: ['html'] conf_data.set('DOXYGEN_OUTPUT', meson.current_build_dir())
) doxyfile = configure_file(input: 'Doxyfile.in.in',
endif output: 'Doxyfile.in',
configuration: conf_data)
doxyfile_rev_target = vcs_tag(command: [
'git', '--git-dir', dav1d_git_dir, 'describe', '--long', '--always'
],
input: doxyfile,
output: 'Doxyfile'
)
custom_target('doc',
build_by_default: false,
command: [doxygen, doxyfile_rev_target],
output: ['html']
)
...@@ -39,6 +39,11 @@ ...@@ -39,6 +39,11 @@
#include "dp_fifo.h" #include "dp_fifo.h"
#include "dp_renderer.h" #include "dp_renderer.h"
#define FRAME_OFFSET_TO_PTS(foff) \
(uint64_t)(((foff) * rd_ctx->spf) * 1000000000.0 + .5)
#define TS_TO_PTS(ts) \
(uint64_t)(((ts) * rd_ctx->timebase) * 1000000000.0 + .5)
// Selected renderer callbacks and cookie // Selected renderer callbacks and cookie
static const Dav1dPlayRenderInfo *renderer_info = { NULL }; static const Dav1dPlayRenderInfo *renderer_info = { NULL };
...@@ -59,27 +64,43 @@ typedef struct render_context ...@@ -59,27 +64,43 @@ typedef struct render_context
// Lock to protect access to the context structure // Lock to protect access to the context structure
SDL_mutex *lock; SDL_mutex *lock;
// Timestamp of previous decoded frame // Timestamp of last displayed frame (in timebase unit)
int64_t last_pts; int64_t last_ts;
// Timestamp of current decoded frame // Timestamp of last decoded frame (in timebase unit)
int64_t current_pts; int64_t current_ts;
// Ticks when last frame was received // Ticks when last frame was received
uint32_t last_ticks; uint32_t last_ticks;
// PTS time base // PTS time base
double timebase; double timebase;
// Seconds per frame
double spf;
// Number of frames
uint32_t total;
// Fifo // Fifo
Dav1dPlayPtrFifo *fifo; Dav1dPlayPtrFifo *fifo;
// Custom SDL2 event type // Custom SDL2 event types
uint32_t renderer_event_type; uint32_t event_types;
// User pause state
uint8_t user_paused;
// Internal pause state
uint8_t paused;
// Start of internal pause state
uint32_t pause_start;
// Duration of internal pause state
uint32_t pause_time;
// Seek accumulator
int seek;
// Indicates if termination of the decoder thread was requested // Indicates if termination of the decoder thread was requested
uint8_t dec_should_terminate; uint8_t dec_should_terminate;
} Dav1dPlayRenderContext; } Dav1dPlayRenderContext;
static void dp_settings_print_usage(const char *const app, static void dp_settings_print_usage(const char *const app,
const char *const reason, ...) const char *const reason, ...)
{ {
if (reason) { if (reason) {
va_list args; va_list args;
...@@ -93,11 +114,13 @@ static void dp_settings_print_usage(const char *const app, ...@@ -93,11 +114,13 @@ static void dp_settings_print_usage(const char *const app,
fprintf(stderr, "Supported options:\n" fprintf(stderr, "Supported options:\n"
" --input/-i $file: input file\n" " --input/-i $file: input file\n"
" --untimed/-u: ignore PTS, render as fast as possible\n" " --untimed/-u: ignore PTS, render as fast as possible\n"
" --framethreads $num: number of frame threads (default: 1)\n" " --threads $num: number of threads (default: 0)\n"
" --tilethreads $num: number of tile threads (default: 1)\n" " --framedelay $num: maximum frame delay, capped at $threads (default: 0);\n"
" set to 1 for low-latency decoding\n"
" --highquality: enable high quality rendering\n" " --highquality: enable high quality rendering\n"
" --zerocopy/-z: enable zero copy upload path\n" " --zerocopy/-z: enable zero copy upload path\n"
" --gpugrain/-g: enable GPU grain synthesis\n" " --gpugrain/-g: enable GPU grain synthesis\n"
" --fullscreen/-f: enable full screen mode\n"
" --version/-v: print version and exit\n" " --version/-v: print version and exit\n"
" --renderer/-r: select renderer backend (default: auto)\n"); " --renderer/-r: select renderer backend (default: auto)\n");
exit(1); exit(1);
...@@ -115,18 +138,18 @@ static unsigned parse_unsigned(const char *const optarg, const int option, ...@@ -115,18 +138,18 @@ static unsigned parse_unsigned(const char *const optarg, const int option,
} }
static void dp_rd_ctx_parse_args(Dav1dPlayRenderContext *rd_ctx, static void dp_rd_ctx_parse_args(Dav1dPlayRenderContext *rd_ctx,
const int argc, char *const *const argv) const int argc, char *const *const argv)
{ {
int o; int o;
Dav1dPlaySettings *settings = &rd_ctx->settings; Dav1dPlaySettings *settings = &rd_ctx->settings;
Dav1dSettings *lib_settings = &rd_ctx->lib_settings; Dav1dSettings *lib_settings = &rd_ctx->lib_settings;
// Short options // Short options
static const char short_opts[] = "i:vuzgr:"; static const char short_opts[] = "i:vuzgfr:";
enum { enum {
ARG_FRAME_THREADS = 256, ARG_THREADS = 256,
ARG_TILE_THREADS, ARG_FRAME_DELAY,
ARG_HIGH_QUALITY, ARG_HIGH_QUALITY,
}; };
...@@ -135,11 +158,12 @@ static void dp_rd_ctx_parse_args(Dav1dPlayRenderContext *rd_ctx, ...@@ -135,11 +158,12 @@ static void dp_rd_ctx_parse_args(Dav1dPlayRenderContext *rd_ctx,
{ "input", 1, NULL, 'i' }, { "input", 1, NULL, 'i' },
{ "version", 0, NULL, 'v' }, { "version", 0, NULL, 'v' },
{ "untimed", 0, NULL, 'u' }, { "untimed", 0, NULL, 'u' },
{ "framethreads", 1, NULL, ARG_FRAME_THREADS }, { "threads", 1, NULL, ARG_THREADS },
{ "tilethreads", 1, NULL, ARG_TILE_THREADS }, { "framedelay", 1, NULL, ARG_FRAME_DELAY },
{ "highquality", 0, NULL, ARG_HIGH_QUALITY }, { "highquality", 0, NULL, ARG_HIGH_QUALITY },
{ "zerocopy", 0, NULL, 'z' }, { "zerocopy", 0, NULL, 'z' },
{ "gpugrain", 0, NULL, 'g' }, { "gpugrain", 0, NULL, 'g' },
{ "fullscreen", 0, NULL, 'f'},
{ "renderer", 0, NULL, 'r'}, { "renderer", 0, NULL, 'r'},
{ NULL, 0, NULL, 0 }, { NULL, 0, NULL, 0 },
}; };
...@@ -164,16 +188,19 @@ static void dp_rd_ctx_parse_args(Dav1dPlayRenderContext *rd_ctx, ...@@ -164,16 +188,19 @@ static void dp_rd_ctx_parse_args(Dav1dPlayRenderContext *rd_ctx,
case 'g': case 'g':
settings->gpugrain = true; settings->gpugrain = true;
break; break;
case 'f':
settings->fullscreen = true;
break;
case 'r': case 'r':
settings->renderer_name = optarg; settings->renderer_name = optarg;
break; break;
case ARG_FRAME_THREADS: case ARG_THREADS:
lib_settings->n_frame_threads = lib_settings->n_threads =
parse_unsigned(optarg, ARG_FRAME_THREADS, argv[0]); parse_unsigned(optarg, ARG_THREADS, argv[0]);
break; break;
case ARG_TILE_THREADS: case ARG_FRAME_DELAY:
lib_settings->n_tile_threads = lib_settings->max_frame_delay =
parse_unsigned(optarg, ARG_TILE_THREADS, argv[0]); parse_unsigned(optarg, ARG_FRAME_DELAY, argv[0]);
break; break;
default: default:
dp_settings_print_usage(argv[0], NULL); dp_settings_print_usage(argv[0], NULL);
...@@ -213,76 +240,77 @@ static Dav1dPlayRenderContext *dp_rd_ctx_create(int argc, char **argv) ...@@ -213,76 +240,77 @@ static Dav1dPlayRenderContext *dp_rd_ctx_create(int argc, char **argv)
Dav1dPlayRenderContext *rd_ctx; Dav1dPlayRenderContext *rd_ctx;
// Alloc // Alloc
rd_ctx = malloc(sizeof(Dav1dPlayRenderContext)); rd_ctx = calloc(1, sizeof(Dav1dPlayRenderContext));
if (rd_ctx == NULL) { if (rd_ctx == NULL) {
return NULL; return NULL;
} }
// Parse and validate arguments
dav1d_default_settings(&rd_ctx->lib_settings);
memset(&rd_ctx->settings, 0, sizeof(rd_ctx->settings));
dp_rd_ctx_parse_args(rd_ctx, argc, argv);
// Init SDL2 library
if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER) < 0) {
fprintf(stderr, "SDL_Init failed: %s\n", SDL_GetError());
goto fail;
}
// Register a custom event to notify our SDL main thread // Register a custom event to notify our SDL main thread
// about new frames // about new frames
rd_ctx->renderer_event_type = SDL_RegisterEvents(1); rd_ctx->event_types = SDL_RegisterEvents(3);
if (rd_ctx->renderer_event_type == UINT32_MAX) { if (rd_ctx->event_types == UINT32_MAX) {
fprintf(stderr, "Failure to create custom SDL event type!\n"); fprintf(stderr, "Failure to create custom SDL event types!\n");
free(rd_ctx); goto fail;
return NULL;
} }
rd_ctx->fifo = dp_fifo_create(5); rd_ctx->fifo = dp_fifo_create(5);
if (rd_ctx->fifo == NULL) { if (rd_ctx->fifo == NULL) {
fprintf(stderr, "Failed to create FIFO for output pictures!\n"); fprintf(stderr, "Failed to create FIFO for output pictures!\n");
free(rd_ctx); goto fail;
return NULL;
} }
rd_ctx->lock = SDL_CreateMutex(); rd_ctx->lock = SDL_CreateMutex();
if (rd_ctx->lock == NULL) { if (rd_ctx->lock == NULL) {
fprintf(stderr, "SDL_CreateMutex failed: %s\n", SDL_GetError()); fprintf(stderr, "SDL_CreateMutex failed: %s\n", SDL_GetError());
dp_fifo_destroy(rd_ctx->fifo); goto fail;
free(rd_ctx);
return NULL;
} }
// Parse and validate arguments
dav1d_default_settings(&rd_ctx->lib_settings);
memset(&rd_ctx->settings, 0, sizeof(rd_ctx->settings));
dp_rd_ctx_parse_args(rd_ctx, argc, argv);
// Select renderer // Select renderer
renderer_info = dp_get_renderer(rd_ctx->settings.renderer_name); renderer_info = dp_get_renderer(rd_ctx->settings.renderer_name);
if (renderer_info == NULL) { if (renderer_info == NULL) {
printf("No suitable rendered matching %s found.\n", printf("No suitable renderer matching %s found.\n",
(rd_ctx->settings.renderer_name) ? rd_ctx->settings.renderer_name : "auto"); (rd_ctx->settings.renderer_name) ? rd_ctx->settings.renderer_name : "auto");
} else { } else {
printf("Using %s renderer\n", renderer_info->name); printf("Using %s renderer\n", renderer_info->name);
} }
rd_ctx->rd_priv = (renderer_info) ? renderer_info->create_renderer() : NULL; rd_ctx->rd_priv = (renderer_info) ? renderer_info->create_renderer(&rd_ctx->settings) : NULL;
if (rd_ctx->rd_priv == NULL) { if (rd_ctx->rd_priv == NULL) {
SDL_DestroyMutex(rd_ctx->lock); goto fail;
dp_fifo_destroy(rd_ctx->fifo);
free(rd_ctx);
return NULL;
} }
rd_ctx->last_pts = 0;
rd_ctx->last_ticks = 0;
rd_ctx->current_pts = 0;
rd_ctx->timebase = 0;
rd_ctx->dec_should_terminate = 0;
return rd_ctx; return rd_ctx;
fail:
if (rd_ctx->lock)
SDL_DestroyMutex(rd_ctx->lock);
if (rd_ctx->fifo)
dp_fifo_destroy(rd_ctx->fifo);
free(rd_ctx);
SDL_Quit();
return NULL;
} }
/** /**
* Notify about new available frame * Notify about new event
*/ */
static void dp_rd_ctx_post_event(Dav1dPlayRenderContext *rd_ctx, uint32_t code) static void dp_rd_ctx_post_event(Dav1dPlayRenderContext *rd_ctx, uint32_t type)
{ {
SDL_Event event; SDL_Event event;
SDL_zero(event); SDL_zero(event);
event.type = rd_ctx->renderer_event_type; event.type = type;
event.user.code = code;
SDL_PushEvent(&event); SDL_PushEvent(&event);
} }
...@@ -294,10 +322,137 @@ static void dp_rd_ctx_post_event(Dav1dPlayRenderContext *rd_ctx, uint32_t code) ...@@ -294,10 +322,137 @@ static void dp_rd_ctx_post_event(Dav1dPlayRenderContext *rd_ctx, uint32_t code)
* new picture. * new picture.
*/ */
static void dp_rd_ctx_update_with_dav1d_picture(Dav1dPlayRenderContext *rd_ctx, static void dp_rd_ctx_update_with_dav1d_picture(Dav1dPlayRenderContext *rd_ctx,
Dav1dPicture *dav1d_pic) Dav1dPicture *dav1d_pic)
{ {
rd_ctx->current_ts = dav1d_pic->m.timestamp;
renderer_info->update_frame(rd_ctx->rd_priv, dav1d_pic, &rd_ctx->settings); renderer_info->update_frame(rd_ctx->rd_priv, dav1d_pic, &rd_ctx->settings);
rd_ctx->current_pts = dav1d_pic->m.timestamp; }
/**
* Toggle pause state
*/
static void dp_rd_ctx_toggle_pause(Dav1dPlayRenderContext *rd_ctx)
{
SDL_LockMutex(rd_ctx->lock);
rd_ctx->user_paused = !rd_ctx->user_paused;
if (rd_ctx->seek)
goto out;
rd_ctx->paused = rd_ctx->user_paused;
uint32_t now = SDL_GetTicks();
if (rd_ctx->paused)
rd_ctx->pause_start = now;
else {
rd_ctx->pause_time += now - rd_ctx->pause_start;
rd_ctx->pause_start = 0;
rd_ctx->last_ticks = now;
}
out:
SDL_UnlockMutex(rd_ctx->lock);
}
/**
* Query pause state
*/
static int dp_rd_ctx_is_paused(Dav1dPlayRenderContext *rd_ctx)
{
int ret;
SDL_LockMutex(rd_ctx->lock);
ret = rd_ctx->paused;
SDL_UnlockMutex(rd_ctx->lock);
return ret;
}
/**
* Request seeking, in seconds
*/
static void dp_rd_ctx_seek(Dav1dPlayRenderContext *rd_ctx, int sec)
{
SDL_LockMutex(rd_ctx->lock);
rd_ctx->seek += sec;
if (!rd_ctx->paused)
rd_ctx->pause_start = SDL_GetTicks();
rd_ctx->paused = 1;
SDL_UnlockMutex(rd_ctx->lock);
}
static int decode_frame(Dav1dPicture **p, Dav1dContext *c,
Dav1dData *data, DemuxerContext *in_ctx);
static inline void destroy_pic(void *a);
/**
* Seek the stream, if requested
*/
static int dp_rd_ctx_handle_seek(Dav1dPlayRenderContext *rd_ctx,
DemuxerContext *in_ctx,
Dav1dContext *c, Dav1dData *data)
{
int res = 0;
SDL_LockMutex(rd_ctx->lock);
if (!rd_ctx->seek)
goto out;
int64_t seek = rd_ctx->seek * 1000000000ULL;
uint64_t pts = TS_TO_PTS(rd_ctx->current_ts);
pts = ((int64_t)pts > -seek) ? pts + seek : 0;
int end = pts >= FRAME_OFFSET_TO_PTS(rd_ctx->total);
if (end)
pts = FRAME_OFFSET_TO_PTS(rd_ctx->total - 1);
uint64_t target_pts = pts;
dav1d_flush(c);
uint64_t shift = FRAME_OFFSET_TO_PTS(5);
while (1) {
if (shift > pts)
shift = pts;
if ((res = input_seek(in_ctx, pts - shift)))
goto out;
Dav1dSequenceHeader seq;
uint64_t cur_pts;
do {
if ((res = input_read(in_ctx, data)))
break;
cur_pts = TS_TO_PTS(data->m.timestamp);
res = dav1d_parse_sequence_header(&seq, data->data, data->sz);
} while (res && cur_pts < pts);
if (!res && cur_pts <= pts)
break;
if (shift > pts)
shift = pts;
pts -= shift;
}
if (!res) {
pts = TS_TO_PTS(data->m.timestamp);
while (pts < target_pts) {
Dav1dPicture *p;
if ((res = decode_frame(&p, c, data, in_ctx)))
break;
if (p) {
pts = TS_TO_PTS(p->m.timestamp);
if (pts < target_pts)
destroy_pic(p);
else {
dp_fifo_push(rd_ctx->fifo, p);
uint32_t type = rd_ctx->event_types + DAV1D_EVENT_SEEK_FRAME;
dp_rd_ctx_post_event(rd_ctx, type);
}
}
}
if (!res) {
rd_ctx->last_ts = data->m.timestamp - rd_ctx->spf / rd_ctx->timebase;
rd_ctx->current_ts = data->m.timestamp;
}
}
out:
rd_ctx->paused = rd_ctx->user_paused;
if (!rd_ctx->paused && rd_ctx->seek) {
uint32_t now = SDL_GetTicks();
rd_ctx->pause_time += now - rd_ctx->pause_start;
rd_ctx->pause_start = 0;
rd_ctx->last_ticks = now;
}
rd_ctx->seek = 0;
SDL_UnlockMutex(rd_ctx->lock);
if (res)
fprintf(stderr, "Error seeking, aborting\n");
return res;
} }
/** /**
...@@ -329,14 +484,15 @@ static int dp_rd_ctx_should_terminate(Dav1dPlayRenderContext *rd_ctx) ...@@ -329,14 +484,15 @@ static int dp_rd_ctx_should_terminate(Dav1dPlayRenderContext *rd_ctx)
*/ */
static void dp_rd_ctx_render(Dav1dPlayRenderContext *rd_ctx) static void dp_rd_ctx_render(Dav1dPlayRenderContext *rd_ctx)
{ {
SDL_LockMutex(rd_ctx->lock);
// Calculate time since last frame was received // Calculate time since last frame was received
uint32_t ticks_now = SDL_GetTicks(); uint32_t ticks_now = SDL_GetTicks();
uint32_t ticks_diff = (rd_ctx->last_ticks != 0) ? ticks_now - rd_ctx->last_ticks : 0; uint32_t ticks_diff = (rd_ctx->last_ticks != 0) ? ticks_now - rd_ctx->last_ticks : 0;
// Calculate when to display the frame // Calculate when to display the frame
int64_t pts_diff = rd_ctx->current_pts - rd_ctx->last_pts; int64_t ts_diff = rd_ctx->current_ts - rd_ctx->last_ts;
int32_t wait_time = (pts_diff * rd_ctx->timebase) * 1000 - ticks_diff; int32_t pts_diff = (ts_diff * rd_ctx->timebase) * 1000.0 + .5;
rd_ctx->last_pts = rd_ctx->current_pts; int32_t wait_time = pts_diff - ticks_diff;
// In untimed mode, simply don't wait // In untimed mode, simply don't wait
if (rd_ctx->settings.untimed) if (rd_ctx->settings.untimed)
...@@ -347,13 +503,59 @@ static void dp_rd_ctx_render(Dav1dPlayRenderContext *rd_ctx) ...@@ -347,13 +503,59 @@ static void dp_rd_ctx_render(Dav1dPlayRenderContext *rd_ctx)
// accurate player this would need to be done in a better way. // accurate player this would need to be done in a better way.
if (wait_time > 0) { if (wait_time > 0) {
SDL_Delay(wait_time); SDL_Delay(wait_time);
} else if (wait_time < -10) { // Do not warn for minor time drifts } else if (wait_time < -10 && !rd_ctx->paused) { // Do not warn for minor time drifts
fprintf(stderr, "Frame displayed %f seconds too late\n", wait_time/(float)1000); fprintf(stderr, "Frame displayed %f seconds too late\n", wait_time / 1000.0);
} }
renderer_info->render(rd_ctx->rd_priv, &rd_ctx->settings); renderer_info->render(rd_ctx->rd_priv, &rd_ctx->settings);
rd_ctx->last_ts = rd_ctx->current_ts;
rd_ctx->last_ticks = SDL_GetTicks(); rd_ctx->last_ticks = SDL_GetTicks();
SDL_UnlockMutex(rd_ctx->lock);
}
static int decode_frame(Dav1dPicture **p, Dav1dContext *c,
Dav1dData *data, DemuxerContext *in_ctx)
{
int res;
// Send data packets we got from the demuxer to dav1d
if ((res = dav1d_send_data(c, data)) < 0) {
// On EAGAIN, dav1d can not consume more data and
// dav1d_get_picture needs to be called first, which
// will happen below, so just keep going in that case
// and do not error out.
if (res != DAV1D_ERR(EAGAIN)) {
dav1d_data_unref(data);
goto err;
}
}
*p = calloc(1, sizeof(**p));
// Try to get a decoded frame
if ((res = dav1d_get_picture(c, *p)) < 0) {
// In all error cases, even EAGAIN, p needs to be freed as
// it is never added to the queue and would leak.
free(*p);
*p = NULL;
// On EAGAIN, it means dav1d has not enough data to decode
// therefore this is not a decoding error but just means
// we need to feed it more data, which happens in the next
// run of the decoder loop.
if (res != DAV1D_ERR(EAGAIN))
goto err;
}
return data->sz == 0 ? input_read(in_ctx, data) : 0;
err:
fprintf(stderr, "Error decoding frame: %s\n",
strerror(-res));
return res;
}
static inline void destroy_pic(void *a)
{
Dav1dPicture *p = (Dav1dPicture *)a;
dav1d_picture_unref(p);
free(p);
} }
/* Decoder thread "main" function */ /* Decoder thread "main" function */
...@@ -366,10 +568,7 @@ static int decoder_thread_main(void *cookie) ...@@ -366,10 +568,7 @@ static int decoder_thread_main(void *cookie)
Dav1dData data; Dav1dData data;
DemuxerContext *in_ctx = NULL; DemuxerContext *in_ctx = NULL;
int res = 0; int res = 0;
unsigned n_out = 0, total, timebase[2], fps[2]; unsigned total, timebase[2], fps[2];
// Store current ticks for stats calculation
uint32_t decoder_start = SDL_GetTicks();
Dav1dPlaySettings settings = rd_ctx->settings; Dav1dPlaySettings settings = rd_ctx->settings;
...@@ -382,8 +581,9 @@ static int decoder_thread_main(void *cookie) ...@@ -382,8 +581,9 @@ static int decoder_thread_main(void *cookie)
goto cleanup; goto cleanup;
} }
double timebase_d = timebase[1]/(double)timebase[0]; rd_ctx->timebase = (double)timebase[1] / timebase[0];
rd_ctx->timebase = timebase_d; rd_ctx->spf = (double)fps[1] / fps[0];
rd_ctx->total = total;
if ((res = dav1d_open(&c, &rd_ctx->lib_settings))) { if ((res = dav1d_open(&c, &rd_ctx->lib_settings))) {
fprintf(stderr, "Failed opening dav1d decoder\n"); fprintf(stderr, "Failed opening dav1d decoder\n");
...@@ -398,55 +598,29 @@ static int decoder_thread_main(void *cookie) ...@@ -398,55 +598,29 @@ static int decoder_thread_main(void *cookie)
} }
// Decoder loop // Decoder loop
do { while (1) {
if (dp_rd_ctx_should_terminate(rd_ctx)) if (dp_rd_ctx_should_terminate(rd_ctx) ||
(res = dp_rd_ctx_handle_seek(rd_ctx, in_ctx, c, &data)) ||
(res = decode_frame(&p, c, &data, in_ctx)))
{
break; break;
// Send data packets we got from the demuxer to dav1d
if ((res = dav1d_send_data(c, &data)) < 0) {
// On EAGAIN, dav1d can not consume more data and
// dav1d_get_picture needs to be called first, which
// will happen below, so just keep going in that case
// and do not error out.
if (res != DAV1D_ERR(EAGAIN)) {
dav1d_data_unref(&data);
fprintf(stderr, "Error decoding frame: %s\n",
strerror(-res));
break;
}
} }
else if (p) {
p = calloc(1, sizeof(*p));
// Try to get a decoded frame
if ((res = dav1d_get_picture(c, p)) < 0) {
// In all error cases, even EAGAIN, p needs to be freed as
// it is never added to the queue and would leak.
free(p);
// On EAGAIN, it means dav1d has not enough data to decode
// therefore this is not a decoding error but just means
// we need to feed it more data, which happens in the next
// run of this decoder loop.
if (res != DAV1D_ERR(EAGAIN)) {
fprintf(stderr, "Error decoding frame: %s\n",
strerror(-res));
break;
}
res = 0;
} else {
// Queue frame // Queue frame
dp_fifo_push(rd_ctx->fifo, p); SDL_LockMutex(rd_ctx->lock);
dp_rd_ctx_post_event(rd_ctx, DAV1D_EVENT_NEW_FRAME); int seek = rd_ctx->seek;
SDL_UnlockMutex(rd_ctx->lock);
n_out++; if (!seek) {
dp_fifo_push(rd_ctx->fifo, p);
uint32_t type = rd_ctx->event_types + DAV1D_EVENT_NEW_FRAME;
dp_rd_ctx_post_event(rd_ctx, type);
}
} }
} while ((data.sz > 0 || !input_read(in_ctx, &data))); }
// Release remaining data // Release remaining data
if (data.sz > 0) dav1d_data_unref(&data); if (data.sz > 0)
dav1d_data_unref(&data);
// Do not drain in case an error occured and caused us to leave the // Do not drain in case an error occured and caused us to leave the
// decoding loop early. // decoding loop early.
if (res < 0) if (res < 0)
...@@ -461,7 +635,6 @@ static int decoder_thread_main(void *cookie) ...@@ -461,7 +635,6 @@ static int decoder_thread_main(void *cookie)
do { do {
if (dp_rd_ctx_should_terminate(rd_ctx)) if (dp_rd_ctx_should_terminate(rd_ctx))
break; break;
p = calloc(1, sizeof(*p)); p = calloc(1, sizeof(*p));
res = dav1d_get_picture(c, p); res = dav1d_get_picture(c, p);
if (res < 0) { if (res < 0) {
...@@ -474,19 +647,13 @@ static int decoder_thread_main(void *cookie) ...@@ -474,19 +647,13 @@ static int decoder_thread_main(void *cookie)
} else { } else {
// Queue frame // Queue frame
dp_fifo_push(rd_ctx->fifo, p); dp_fifo_push(rd_ctx->fifo, p);
dp_rd_ctx_post_event(rd_ctx, DAV1D_EVENT_NEW_FRAME); uint32_t type = rd_ctx->event_types + DAV1D_EVENT_NEW_FRAME;
dp_rd_ctx_post_event(rd_ctx, type);
n_out++;
} }
} while (res != DAV1D_ERR(EAGAIN)); } while (res != DAV1D_ERR(EAGAIN));
// Print stats
uint32_t decoding_time_ms = SDL_GetTicks() - decoder_start;
printf("Decoded %u frames in %d seconds, avg %.02f fps\n",
n_out, decoding_time_ms/1000, n_out / (decoding_time_ms / 1000.0));
cleanup: cleanup:
dp_rd_ctx_post_event(rd_ctx, DAV1D_EVENT_DEC_QUIT); dp_rd_ctx_post_event(rd_ctx, rd_ctx->event_types + DAV1D_EVENT_DEC_QUIT);
if (in_ctx) if (in_ctx)
input_close(in_ctx); input_close(in_ctx);
...@@ -508,10 +675,6 @@ int main(int argc, char **argv) ...@@ -508,10 +675,6 @@ int main(int argc, char **argv)
return 1; return 1;
} }
// Init SDL2 library
if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER) < 0)
return 10;
// Create render context // Create render context
Dav1dPlayRenderContext *rd_ctx = dp_rd_ctx_create(argc, argv); Dav1dPlayRenderContext *rd_ctx = dp_rd_ctx_create(argc, argv);
if (rd_ctx == NULL) { if (rd_ctx == NULL) {
...@@ -543,41 +706,87 @@ int main(int argc, char **argv) ...@@ -543,41 +706,87 @@ int main(int argc, char **argv)
decoder_thread = SDL_CreateThread(decoder_thread_main, "Decoder thread", rd_ctx); decoder_thread = SDL_CreateThread(decoder_thread_main, "Decoder thread", rd_ctx);
// Main loop // Main loop
#define NUM_MAX_EVENTS 8
SDL_Event events[NUM_MAX_EVENTS];
int num_frame_events = 0;
uint32_t start_time = 0, n_out = 0;
while (1) { while (1) {
int num_events = 0;
SDL_Event e; SDL_WaitEvent(NULL);
if (SDL_WaitEvent(&e)) { while (num_events < NUM_MAX_EVENTS && SDL_PollEvent(&events[num_events++]))
if (e.type == SDL_QUIT) { break;
for (int i = 0; i < num_events; ++i) {
SDL_Event *e = &events[i];
if (e->type == SDL_QUIT) {
dp_rd_ctx_request_shutdown(rd_ctx); dp_rd_ctx_request_shutdown(rd_ctx);
} else if (e.type == SDL_WINDOWEVENT) { dp_fifo_flush(rd_ctx->fifo, destroy_pic);
if (e.window.event == SDL_WINDOWEVENT_SIZE_CHANGED) { goto out;
} else if (e->type == SDL_WINDOWEVENT) {
if (e->window.event == SDL_WINDOWEVENT_SIZE_CHANGED) {
// TODO: Handle window resizes // TODO: Handle window resizes
} else if(e->window.event == SDL_WINDOWEVENT_EXPOSED) {
dp_rd_ctx_render(rd_ctx);
}
} else if (e->type == SDL_KEYDOWN) {
SDL_KeyboardEvent *kbde = (SDL_KeyboardEvent *)e;
if (kbde->keysym.sym == SDLK_SPACE) {
dp_rd_ctx_toggle_pause(rd_ctx);
} else if (kbde->keysym.sym == SDLK_ESCAPE) {
dp_rd_ctx_request_shutdown(rd_ctx);
dp_fifo_flush(rd_ctx->fifo, destroy_pic);
goto out;
} else if (kbde->keysym.sym == SDLK_LEFT ||
kbde->keysym.sym == SDLK_RIGHT)
{
if (kbde->keysym.sym == SDLK_LEFT)
dp_rd_ctx_seek(rd_ctx, -5);
else if (kbde->keysym.sym == SDLK_RIGHT)
dp_rd_ctx_seek(rd_ctx, +5);
dp_fifo_flush(rd_ctx->fifo, destroy_pic);
SDL_FlushEvent(rd_ctx->event_types + DAV1D_EVENT_NEW_FRAME);
num_frame_events = 0;
} }
} else if (e.type == rd_ctx->renderer_event_type) { } else if (e->type == rd_ctx->event_types + DAV1D_EVENT_NEW_FRAME) {
if (e.user.code == DAV1D_EVENT_NEW_FRAME) { num_frame_events++;
// Dequeue frame and update the render context with it // Store current ticks for stats calculation
Dav1dPicture *p = dp_fifo_shift(rd_ctx->fifo); if (start_time == 0)
start_time = SDL_GetTicks();
// Do not update textures during termination } else if (e->type == rd_ctx->event_types + DAV1D_EVENT_SEEK_FRAME) {
if (!dp_rd_ctx_should_terminate(rd_ctx)) // Dequeue frame and update the render context with it
dp_rd_ctx_update_with_dav1d_picture(rd_ctx, p); Dav1dPicture *p = dp_fifo_shift(rd_ctx->fifo);
dav1d_picture_unref(p); // Do not update textures during termination
free(p); if (!dp_rd_ctx_should_terminate(rd_ctx)) {
} else if (e.user.code == DAV1D_EVENT_DEC_QUIT) { dp_rd_ctx_update_with_dav1d_picture(rd_ctx, p);
break; n_out++;
} }
destroy_pic(p);
} else if (e->type == rd_ctx->event_types + DAV1D_EVENT_DEC_QUIT) {
goto out;
} }
} }
if (num_frame_events && !dp_rd_ctx_is_paused(rd_ctx)) {
// Do not render during termination // Dequeue frame and update the render context with it
if (!dp_rd_ctx_should_terminate(rd_ctx)) Dav1dPicture *p = dp_fifo_shift(rd_ctx->fifo);
dp_rd_ctx_render(rd_ctx); // Do not update textures during termination
if (!dp_rd_ctx_should_terminate(rd_ctx)) {
dp_rd_ctx_update_with_dav1d_picture(rd_ctx, p);
dp_rd_ctx_render(rd_ctx);
n_out++;
}
destroy_pic(p);
num_frame_events--;
}
} }
out:;
// Print stats
uint32_t time_ms = SDL_GetTicks() - start_time - rd_ctx->pause_time;
printf("Decoded %u frames in %d seconds, avg %.02f fps\n",
n_out, time_ms / 1000, n_out/ (time_ms / 1000.0));
int decoder_ret = 0; int decoder_ret = 0;
SDL_WaitThread(decoder_thread, &decoder_ret); SDL_WaitThread(decoder_thread, &decoder_ret);
dp_rd_ctx_destroy(rd_ctx); dp_rd_ctx_destroy(rd_ctx);
SDL_Quit();
return decoder_ret; return decoder_ret;
} }
...@@ -37,6 +37,8 @@ struct dp_fifo ...@@ -37,6 +37,8 @@ struct dp_fifo
size_t capacity; size_t capacity;
size_t count; size_t count;
void **entries; void **entries;
int push_wait;
int flush;
}; };
...@@ -54,6 +56,8 @@ Dav1dPlayPtrFifo *dp_fifo_create(size_t capacity) ...@@ -54,6 +56,8 @@ Dav1dPlayPtrFifo *dp_fifo_create(size_t capacity)
fifo->capacity = capacity; fifo->capacity = capacity;
fifo->count = 0; fifo->count = 0;
fifo->push_wait = 0;
fifo->flush = 0;
fifo->lock = SDL_CreateMutex(); fifo->lock = SDL_CreateMutex();
if (fifo->lock == NULL) { if (fifo->lock == NULL) {
...@@ -90,8 +94,16 @@ void dp_fifo_destroy(Dav1dPlayPtrFifo *fifo) ...@@ -90,8 +94,16 @@ void dp_fifo_destroy(Dav1dPlayPtrFifo *fifo)
void dp_fifo_push(Dav1dPlayPtrFifo *fifo, void *element) void dp_fifo_push(Dav1dPlayPtrFifo *fifo, void *element)
{ {
SDL_LockMutex(fifo->lock); SDL_LockMutex(fifo->lock);
while (fifo->count == fifo->capacity) while (fifo->count == fifo->capacity) {
fifo->push_wait = 1;
SDL_CondWait(fifo->cond_change, fifo->lock); SDL_CondWait(fifo->cond_change, fifo->lock);
fifo->push_wait = 0;
if (fifo->flush) {
SDL_CondSignal(fifo->cond_change);
SDL_UnlockMutex(fifo->lock);
return;
}
}
fifo->entries[fifo->count++] = element; fifo->entries[fifo->count++] = element;
if (fifo->count == 1) if (fifo->count == 1)
SDL_CondSignal(fifo->cond_change); SDL_CondSignal(fifo->cond_change);
...@@ -120,4 +132,16 @@ void *dp_fifo_shift(Dav1dPlayPtrFifo *fifo) ...@@ -120,4 +132,16 @@ void *dp_fifo_shift(Dav1dPlayPtrFifo *fifo)
return res; return res;
} }
void dp_fifo_flush(Dav1dPlayPtrFifo *fifo, void (*destroy_elem)(void *))
{
SDL_LockMutex(fifo->lock);
fifo->flush = 1;
if (fifo->push_wait) {
SDL_CondSignal(fifo->cond_change);
SDL_CondWait(fifo->cond_change, fifo->lock);
}
while (fifo->count)
destroy_elem(fifo->entries[--fifo->count]);
fifo->flush = 0;
SDL_UnlockMutex(fifo->lock);
}
...@@ -59,3 +59,5 @@ void *dp_fifo_shift(Dav1dPlayPtrFifo *fifo); ...@@ -59,3 +59,5 @@ void *dp_fifo_shift(Dav1dPlayPtrFifo *fifo);
* other thread will call dp_fifo_shift will lead to a deadlock. * other thread will call dp_fifo_shift will lead to a deadlock.
*/ */
void dp_fifo_push(Dav1dPlayPtrFifo *fifo, void *element); void dp_fifo_push(Dav1dPlayPtrFifo *fifo, void *element);
void dp_fifo_flush(Dav1dPlayPtrFifo *fifo, void (*destroy_elem)(void *));
...@@ -30,22 +30,32 @@ ...@@ -30,22 +30,32 @@
#include "dav1d/dav1d.h" #include "dav1d/dav1d.h"
#include <SDL.h> #include <SDL.h>
#ifdef HAVE_PLACEBO #if HAVE_PLACEBO
# include <libplacebo/config.h> # include <libplacebo/config.h>
#endif #endif
// Check libplacebo Vulkan rendering // Check libplacebo Vulkan rendering
#if defined(HAVE_VULKAN) && defined(SDL_VIDEO_VULKAN) #if HAVE_VULKAN && defined(SDL_VIDEO_VULKAN)
# if defined(PL_HAVE_VULKAN) && PL_HAVE_VULKAN # if defined(PL_HAVE_VULKAN) && PL_HAVE_VULKAN
# define HAVE_RENDERER_PLACEBO # define HAVE_RENDERER_PLACEBO 1
# define HAVE_PLACEBO_VULKAN # define HAVE_PLACEBO_VULKAN 1
# endif # endif
#endif #endif
// Check libplacebo OpenGL rendering // Check libplacebo OpenGL rendering
#if defined(PL_HAVE_OPENGL) && PL_HAVE_OPENGL #if defined(PL_HAVE_OPENGL) && PL_HAVE_OPENGL
# define HAVE_RENDERER_PLACEBO # define HAVE_RENDERER_PLACEBO 1
# define HAVE_PLACEBO_OPENGL # define HAVE_PLACEBO_OPENGL 1
#endif
#ifndef HAVE_RENDERER_PLACEBO
#define HAVE_RENDERER_PLACEBO 0
#endif
#ifndef HAVE_PLACEBO_VULKAN
#define HAVE_PLACEBO_VULKAN 0
#endif
#ifndef HAVE_PLACEBO_OPENGL
#define HAVE_PLACEBO_OPENGL 0
#endif #endif
/** /**
...@@ -61,13 +71,17 @@ typedef struct { ...@@ -61,13 +71,17 @@ typedef struct {
int untimed; int untimed;
int zerocopy; int zerocopy;
int gpugrain; int gpugrain;
int fullscreen;
} Dav1dPlaySettings; } Dav1dPlaySettings;
#define WINDOW_WIDTH 910 #define WINDOW_WIDTH 910
#define WINDOW_HEIGHT 512 #define WINDOW_HEIGHT 512
#define DAV1D_EVENT_NEW_FRAME 1 enum {
#define DAV1D_EVENT_DEC_QUIT 2 DAV1D_EVENT_NEW_FRAME,
DAV1D_EVENT_SEEK_FRAME,
DAV1D_EVENT_DEC_QUIT
};
/** /**
* Renderer info * Renderer info
...@@ -79,12 +93,12 @@ typedef struct rdr_info ...@@ -79,12 +93,12 @@ typedef struct rdr_info
// Cookie passed to the renderer implementation callbacks // Cookie passed to the renderer implementation callbacks
void *cookie; void *cookie;
// Callback to create the renderer // Callback to create the renderer
void* (*create_renderer)(); void* (*create_renderer)(const Dav1dPlaySettings *settings);
// Callback to destroy the renderer // Callback to destroy the renderer
void (*destroy_renderer)(void *cookie); void (*destroy_renderer)(void *cookie);
// Callback to the render function that renders a prevously sent frame // Callback to the render function that renders a prevously sent frame
void (*render)(void *cookie, const Dav1dPlaySettings *settings); void (*render)(void *cookie, const Dav1dPlaySettings *settings);
// Callback to the send frame function // Callback to the send frame function, _may_ also unref dav1d_pic!
int (*update_frame)(void *cookie, Dav1dPicture *dav1d_pic, int (*update_frame)(void *cookie, Dav1dPicture *dav1d_pic,
const Dav1dPlaySettings *settings); const Dav1dPlaySettings *settings);
// Callback for alloc/release pictures (optional) // Callback for alloc/release pictures (optional)
......
...@@ -26,17 +26,17 @@ ...@@ -26,17 +26,17 @@
#include "dp_renderer.h" #include "dp_renderer.h"
#ifdef HAVE_RENDERER_PLACEBO #if HAVE_RENDERER_PLACEBO
#include <assert.h> #include <assert.h>
#include <libplacebo/renderer.h> #include <libplacebo/renderer.h>
#include <libplacebo/utils/upload.h> #include <libplacebo/utils/dav1d.h>
#ifdef HAVE_PLACEBO_VULKAN #if HAVE_PLACEBO_VULKAN
# include <libplacebo/vulkan.h> # include <libplacebo/vulkan.h>
# include <SDL_vulkan.h> # include <SDL_vulkan.h>
#endif #endif
#ifdef HAVE_PLACEBO_OPENGL #if HAVE_PLACEBO_OPENGL
# include <libplacebo/opengl.h> # include <libplacebo/opengl.h>
# include <SDL_opengl.h> # include <SDL_opengl.h>
#endif #endif
...@@ -49,60 +49,66 @@ typedef struct renderer_priv_ctx ...@@ -49,60 +49,66 @@ typedef struct renderer_priv_ctx
{ {
// SDL window // SDL window
SDL_Window *win; SDL_Window *win;
// Placebo context // Placebo log
struct pl_context *ctx; pl_log log;
// Placebo renderer // Placebo renderer
struct pl_renderer *renderer; pl_renderer renderer;
#ifdef HAVE_PLACEBO_VULKAN #if HAVE_PLACEBO_VULKAN
// Placebo Vulkan handle // Placebo Vulkan handle
const struct pl_vulkan *vk; pl_vulkan vk;
// Placebo Vulkan instance // Placebo Vulkan instance
const struct pl_vk_inst *vk_inst; pl_vk_inst vk_inst;
// Vulkan surface // Vulkan surface
VkSurfaceKHR surf; VkSurfaceKHR surf;
#endif #endif
#ifdef HAVE_PLACEBO_OPENGL #if HAVE_PLACEBO_OPENGL
// Placebo OpenGL handle // Placebo OpenGL handle
const struct pl_opengl *gl; pl_opengl gl;
// SDL OpenGL context
SDL_GLContext gl_context;
#endif #endif
// Placebo GPU // Placebo GPU
const struct pl_gpu *gpu; pl_gpu gpu;
// Placebo swapchain // Placebo swapchain
const struct pl_swapchain *swapchain; pl_swapchain swapchain;
// Lock protecting access to the texture // Lock protecting access to the texture
SDL_mutex *lock; SDL_mutex *lock;
// Image to render, and planes backing them // Image to render, and planes backing them
struct pl_image image; struct pl_frame image;
const struct pl_tex *plane_tex[3]; pl_tex plane_tex[3];
} Dav1dPlayRendererPrivateContext; } Dav1dPlayRendererPrivateContext;
static Dav1dPlayRendererPrivateContext* static Dav1dPlayRendererPrivateContext*
placebo_renderer_create_common(int window_flags) placebo_renderer_create_common(const Dav1dPlaySettings *settings, int window_flags)
{ {
if (settings->fullscreen)
window_flags |= SDL_WINDOW_FULLSCREEN_DESKTOP;
// Create Window // Create Window
SDL_Window *sdlwin = dp_create_sdl_window(window_flags | SDL_WINDOW_RESIZABLE); SDL_Window *sdlwin = dp_create_sdl_window(window_flags | SDL_WINDOW_RESIZABLE);
if (sdlwin == NULL) if (sdlwin == NULL)
return NULL; return NULL;
SDL_ShowCursor(0);
// Alloc // Alloc
Dav1dPlayRendererPrivateContext *rd_priv_ctx = malloc(sizeof(Dav1dPlayRendererPrivateContext)); Dav1dPlayRendererPrivateContext *const rd_priv_ctx =
if (rd_priv_ctx == NULL) { calloc(1, sizeof(Dav1dPlayRendererPrivateContext));
if (rd_priv_ctx == NULL)
return NULL; return NULL;
}
*rd_priv_ctx = (Dav1dPlayRendererPrivateContext) {0};
rd_priv_ctx->win = sdlwin; rd_priv_ctx->win = sdlwin;
// Init libplacebo // Init libplacebo
rd_priv_ctx->ctx = pl_context_create(PL_API_VER, &(struct pl_context_params) { rd_priv_ctx->log = pl_log_create(PL_API_VER, pl_log_params(
.log_cb = pl_log_color, .log_cb = pl_log_color,
#ifndef NDEBUG #ifndef NDEBUG
.log_level = PL_LOG_DEBUG, .log_level = PL_LOG_DEBUG,
#else #else
.log_level = PL_LOG_WARN, .log_level = PL_LOG_WARN,
#endif #endif
}); ));
if (rd_priv_ctx->ctx == NULL) { if (rd_priv_ctx->log == NULL) {
free(rd_priv_ctx); free(rd_priv_ctx);
return NULL; return NULL;
} }
...@@ -111,7 +117,7 @@ static Dav1dPlayRendererPrivateContext* ...@@ -111,7 +117,7 @@ static Dav1dPlayRendererPrivateContext*
rd_priv_ctx->lock = SDL_CreateMutex(); rd_priv_ctx->lock = SDL_CreateMutex();
if (rd_priv_ctx->lock == NULL) { if (rd_priv_ctx->lock == NULL) {
fprintf(stderr, "SDL_CreateMutex failed: %s\n", SDL_GetError()); fprintf(stderr, "SDL_CreateMutex failed: %s\n", SDL_GetError());
pl_context_destroy(&(rd_priv_ctx->ctx)); pl_log_destroy(&rd_priv_ctx->log);
free(rd_priv_ctx); free(rd_priv_ctx);
return NULL; return NULL;
} }
...@@ -119,40 +125,39 @@ static Dav1dPlayRendererPrivateContext* ...@@ -119,40 +125,39 @@ static Dav1dPlayRendererPrivateContext*
return rd_priv_ctx; return rd_priv_ctx;
} }
#ifdef HAVE_PLACEBO_OPENGL #if HAVE_PLACEBO_OPENGL
static void *placebo_renderer_create_gl() static void *placebo_renderer_create_gl(const Dav1dPlaySettings *settings)
{ {
SDL_Window *sdlwin = NULL; SDL_Window *sdlwin = NULL;
SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, SDL_GL_CONTEXT_DEBUG_FLAG); SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, SDL_GL_CONTEXT_DEBUG_FLAG);
// Common init // Common init
Dav1dPlayRendererPrivateContext *rd_priv_ctx = Dav1dPlayRendererPrivateContext *rd_priv_ctx =
placebo_renderer_create_common(SDL_WINDOW_OPENGL); placebo_renderer_create_common(settings, SDL_WINDOW_OPENGL);
if (rd_priv_ctx == NULL) if (rd_priv_ctx == NULL)
return NULL; return NULL;
sdlwin = rd_priv_ctx->win; sdlwin = rd_priv_ctx->win;
// Init OpenGL rd_priv_ctx->gl_context = SDL_GL_CreateContext(sdlwin);
struct pl_opengl_params params = pl_opengl_default_params; SDL_GL_MakeCurrent(sdlwin, rd_priv_ctx->gl_context);
# ifndef NDEBUG
params.debug = true;
# endif
SDL_GLContext glcontext = SDL_GL_CreateContext(sdlwin); rd_priv_ctx->gl = pl_opengl_create(rd_priv_ctx->log, pl_opengl_params(
SDL_GL_MakeCurrent(sdlwin, glcontext); .allow_software = true,
#ifndef NDEBUG
rd_priv_ctx->gl = pl_opengl_create(rd_priv_ctx->ctx, &params); .debug = true,
#endif
));
if (!rd_priv_ctx->gl) { if (!rd_priv_ctx->gl) {
fprintf(stderr, "Failed creating opengl device!\n"); fprintf(stderr, "Failed creating opengl device!\n");
exit(2); exit(2);
} }
rd_priv_ctx->swapchain = pl_opengl_create_swapchain(rd_priv_ctx->gl, rd_priv_ctx->swapchain = pl_opengl_create_swapchain(rd_priv_ctx->gl,
&(struct pl_opengl_swapchain_params) { pl_opengl_swapchain_params(
.swap_buffers = (void (*)(void *)) SDL_GL_SwapWindow, .swap_buffers = (void (*)(void *)) SDL_GL_SwapWindow,
.priv = sdlwin, .priv = sdlwin,
}); ));
if (!rd_priv_ctx->swapchain) { if (!rd_priv_ctx->swapchain) {
fprintf(stderr, "Failed creating opengl swapchain!\n"); fprintf(stderr, "Failed creating opengl swapchain!\n");
...@@ -176,14 +181,14 @@ static void *placebo_renderer_create_gl() ...@@ -176,14 +181,14 @@ static void *placebo_renderer_create_gl()
} }
#endif #endif
#ifdef HAVE_PLACEBO_VULKAN #if HAVE_PLACEBO_VULKAN
static void *placebo_renderer_create_vk() static void *placebo_renderer_create_vk(const Dav1dPlaySettings *settings)
{ {
SDL_Window *sdlwin = NULL; SDL_Window *sdlwin = NULL;
// Common init // Common init
Dav1dPlayRendererPrivateContext *rd_priv_ctx = Dav1dPlayRendererPrivateContext *rd_priv_ctx =
placebo_renderer_create_common(SDL_WINDOW_VULKAN); placebo_renderer_create_common(settings, SDL_WINDOW_VULKAN);
if (rd_priv_ctx == NULL) if (rd_priv_ctx == NULL)
return NULL; return NULL;
...@@ -211,11 +216,10 @@ static void *placebo_renderer_create_vk() ...@@ -211,11 +216,10 @@ static void *placebo_renderer_create_vk()
printf(" %s\n", extensions[i]); printf(" %s\n", extensions[i]);
} }
struct pl_vk_inst_params iparams = pl_vk_inst_default_params; rd_priv_ctx->vk_inst = pl_vk_inst_create(rd_priv_ctx->log, pl_vk_inst_params(
iparams.extensions = extensions; .extensions = extensions,
iparams.num_extensions = num; .num_extensions = num,
));
rd_priv_ctx->vk_inst = pl_vk_inst_create(rd_priv_ctx->ctx, &iparams);
if (!rd_priv_ctx->vk_inst) { if (!rd_priv_ctx->vk_inst) {
fprintf(stderr, "Failed creating Vulkan instance!\n"); fprintf(stderr, "Failed creating Vulkan instance!\n");
exit(1); exit(1);
...@@ -227,12 +231,11 @@ static void *placebo_renderer_create_vk() ...@@ -227,12 +231,11 @@ static void *placebo_renderer_create_vk()
exit(1); exit(1);
} }
struct pl_vulkan_params params = pl_vulkan_default_params; rd_priv_ctx->vk = pl_vulkan_create(rd_priv_ctx->log, pl_vulkan_params(
params.instance = rd_priv_ctx->vk_inst->instance; .instance = rd_priv_ctx->vk_inst->instance,
params.surface = rd_priv_ctx->surf; .surface = rd_priv_ctx->surf,
params.allow_software = true; .allow_software = true,
));
rd_priv_ctx->vk = pl_vulkan_create(rd_priv_ctx->ctx, &params);
if (!rd_priv_ctx->vk) { if (!rd_priv_ctx->vk) {
fprintf(stderr, "Failed creating vulkan device!\n"); fprintf(stderr, "Failed creating vulkan device!\n");
exit(2); exit(2);
...@@ -240,10 +243,10 @@ static void *placebo_renderer_create_vk() ...@@ -240,10 +243,10 @@ static void *placebo_renderer_create_vk()
// Create swapchain // Create swapchain
rd_priv_ctx->swapchain = pl_vulkan_create_swapchain(rd_priv_ctx->vk, rd_priv_ctx->swapchain = pl_vulkan_create_swapchain(rd_priv_ctx->vk,
&(struct pl_vulkan_swapchain_params) { pl_vulkan_swapchain_params(
.surface = rd_priv_ctx->surf, .surface = rd_priv_ctx->surf,
.present_mode = VK_PRESENT_MODE_IMMEDIATE_KHR, .present_mode = VK_PRESENT_MODE_IMMEDIATE_KHR,
}); ));
if (!rd_priv_ctx->swapchain) { if (!rd_priv_ctx->swapchain) {
fprintf(stderr, "Failed creating vulkan swapchain!\n"); fprintf(stderr, "Failed creating vulkan swapchain!\n");
...@@ -275,21 +278,23 @@ static void placebo_renderer_destroy(void *cookie) ...@@ -275,21 +278,23 @@ static void placebo_renderer_destroy(void *cookie)
for (int i = 0; i < 3; i++) for (int i = 0; i < 3; i++)
pl_tex_destroy(rd_priv_ctx->gpu, &(rd_priv_ctx->plane_tex[i])); pl_tex_destroy(rd_priv_ctx->gpu, &(rd_priv_ctx->plane_tex[i]));
#ifdef HAVE_PLACEBO_VULKAN #if HAVE_PLACEBO_VULKAN
if (rd_priv_ctx->vk) { if (rd_priv_ctx->vk) {
pl_vulkan_destroy(&(rd_priv_ctx->vk)); pl_vulkan_destroy(&(rd_priv_ctx->vk));
vkDestroySurfaceKHR(rd_priv_ctx->vk_inst->instance, rd_priv_ctx->surf, NULL); vkDestroySurfaceKHR(rd_priv_ctx->vk_inst->instance, rd_priv_ctx->surf, NULL);
pl_vk_inst_destroy(&(rd_priv_ctx->vk_inst)); pl_vk_inst_destroy(&(rd_priv_ctx->vk_inst));
} }
#endif #endif
#ifdef HAVE_PLACEBO_OPENGL #if HAVE_PLACEBO_OPENGL
if (rd_priv_ctx->gl) if (rd_priv_ctx->gl)
pl_opengl_destroy(&(rd_priv_ctx->gl)); pl_opengl_destroy(&(rd_priv_ctx->gl));
if (rd_priv_ctx->gl_context)
SDL_GL_DeleteContext(rd_priv_ctx->gl_context);
#endif #endif
SDL_DestroyWindow(rd_priv_ctx->win); SDL_DestroyWindow(rd_priv_ctx->win);
pl_context_destroy(&(rd_priv_ctx->ctx)); pl_log_destroy(&rd_priv_ctx->log);
} }
static void placebo_render(void *cookie, const Dav1dPlaySettings *settings) static void placebo_render(void *cookie, const Dav1dPlaySettings *settings)
...@@ -305,7 +310,7 @@ static void placebo_render(void *cookie, const Dav1dPlaySettings *settings) ...@@ -305,7 +310,7 @@ static void placebo_render(void *cookie, const Dav1dPlaySettings *settings)
// Prepare rendering // Prepare rendering
if (rd_priv_ctx->renderer == NULL) { if (rd_priv_ctx->renderer == NULL) {
rd_priv_ctx->renderer = pl_renderer_create(rd_priv_ctx->ctx, rd_priv_ctx->gpu); rd_priv_ctx->renderer = pl_renderer_create(rd_priv_ctx->log, rd_priv_ctx->gpu);
} }
struct pl_swapchain_frame frame; struct pl_swapchain_frame frame;
...@@ -315,26 +320,18 @@ static void placebo_render(void *cookie, const Dav1dPlaySettings *settings) ...@@ -315,26 +320,18 @@ static void placebo_render(void *cookie, const Dav1dPlaySettings *settings)
return; return;
} }
struct pl_render_params render_params = {0}; struct pl_frame target;
if (settings->highquality) pl_frame_from_swapchain(&target, &frame);
render_params = pl_render_default_params; pl_rect2df_aspect_copy(&target.crop, &rd_priv_ctx->image.crop, 0.0);
if (pl_frame_is_cropped(&target))
struct pl_render_target target; pl_tex_clear(rd_priv_ctx->gpu, frame.fbo, (float[4]){ 0.0 });
pl_render_target_from_swapchain(&target, &frame);
target.profile = (struct pl_icc_profile) {
.data = NULL,
.len = 0,
};
#if PL_API_VER >= 66
pl_rect2df_aspect_copy(&target.dst_rect, &rd_priv_ctx->image.src_rect, 0.0);
if (pl_render_target_partial(&target))
pl_tex_clear(rd_priv_ctx->gpu, target.fbo, (float[4]){ 0.0 });
#endif
if (!pl_render_image(rd_priv_ctx->renderer, &rd_priv_ctx->image, &target, &render_params)) { if (!pl_render_image(rd_priv_ctx->renderer, &rd_priv_ctx->image, &target,
settings->highquality ? &pl_render_default_params
: &pl_render_fast_params))
{
fprintf(stderr, "Failed rendering frame!\n"); fprintf(stderr, "Failed rendering frame!\n");
pl_tex_clear(rd_priv_ctx->gpu, target.fbo, (float[4]){ 1.0 }); pl_tex_clear(rd_priv_ctx->gpu, frame.fbo, (float[4]){ 1.0 });
} }
ok = pl_swapchain_submit_frame(rd_priv_ctx->swapchain); ok = pl_swapchain_submit_frame(rd_priv_ctx->swapchain);
...@@ -351,320 +348,36 @@ static void placebo_render(void *cookie, const Dav1dPlaySettings *settings) ...@@ -351,320 +348,36 @@ static void placebo_render(void *cookie, const Dav1dPlaySettings *settings)
static int placebo_upload_image(void *cookie, Dav1dPicture *dav1d_pic, static int placebo_upload_image(void *cookie, Dav1dPicture *dav1d_pic,
const Dav1dPlaySettings *settings) const Dav1dPlaySettings *settings)
{ {
Dav1dPlayRendererPrivateContext *rd_priv_ctx = cookie; Dav1dPlayRendererPrivateContext *p = cookie;
assert(rd_priv_ctx != NULL); assert(p != NULL);
int ret = 0;
SDL_LockMutex(rd_priv_ctx->lock);
if (!dav1d_pic)
if (dav1d_pic == NULL) { return ret;
SDL_UnlockMutex(rd_priv_ctx->lock);
return 0; SDL_LockMutex(p->lock);
} if (!pl_upload_dav1dpicture(p->gpu, &p->image, p->plane_tex, pl_dav1d_upload_params(
.picture = dav1d_pic,
int width = dav1d_pic->p.w; .film_grain = settings->gpugrain,
int height = dav1d_pic->p.h; .gpu_allocated = settings->zerocopy,
int sub_x = 0, sub_y = 0; .asynchronous = true,
int bytes = (dav1d_pic->p.bpc + 7) / 8; // rounded up )))
enum pl_chroma_location chroma_loc = PL_CHROMA_UNKNOWN; {
struct pl_image *image = &rd_priv_ctx->image;
*image = (struct pl_image) {
.num_planes = 3,
.width = width,
.height = height,
.src_rect = {0, 0, width, height},
.repr = {
.bits = {
.sample_depth = bytes * 8,
.color_depth = dav1d_pic->p.bpc,
},
},
};
// Figure out the correct plane dimensions/count
switch (dav1d_pic->p.layout) {
case DAV1D_PIXEL_LAYOUT_I400:
image->num_planes = 1;
break;
case DAV1D_PIXEL_LAYOUT_I420:
sub_x = sub_y = 1;
break;
case DAV1D_PIXEL_LAYOUT_I422:
sub_x = 1;
break;
case DAV1D_PIXEL_LAYOUT_I444:
break;
}
// Set the right colorspace metadata etc.
switch (dav1d_pic->seq_hdr->pri) {
case DAV1D_COLOR_PRI_UNKNOWN: image->color.primaries = PL_COLOR_PRIM_UNKNOWN; break;
case DAV1D_COLOR_PRI_BT709: image->color.primaries = PL_COLOR_PRIM_BT_709; break;
case DAV1D_COLOR_PRI_BT470M: image->color.primaries = PL_COLOR_PRIM_BT_470M; break;
case DAV1D_COLOR_PRI_BT470BG: image->color.primaries = PL_COLOR_PRIM_BT_601_625; break;
case DAV1D_COLOR_PRI_BT601: image->color.primaries = PL_COLOR_PRIM_BT_601_625; break;
case DAV1D_COLOR_PRI_BT2020: image->color.primaries = PL_COLOR_PRIM_BT_2020; break;
case DAV1D_COLOR_PRI_XYZ:
// Handled below
assert(dav1d_pic->seq_hdr->mtrx == DAV1D_MC_IDENTITY);
break;
default:
printf("warning: unknown dav1d color primaries %d.. ignoring, picture "
"may be very incorrect\n", dav1d_pic->seq_hdr->pri);
break;
}
switch (dav1d_pic->seq_hdr->trc) {
case DAV1D_TRC_BT709:
case DAV1D_TRC_BT470M:
case DAV1D_TRC_BT470BG:
case DAV1D_TRC_BT601:
case DAV1D_TRC_SMPTE240:
case DAV1D_TRC_BT2020_10BIT:
case DAV1D_TRC_BT2020_12BIT:
// These all map to the effective "SDR" CRT-based EOTF, BT.1886
image->color.transfer = PL_COLOR_TRC_BT_1886;
break;
case DAV1D_TRC_UNKNOWN: image->color.transfer = PL_COLOR_TRC_UNKNOWN; break;
case DAV1D_TRC_LINEAR: image->color.transfer = PL_COLOR_TRC_LINEAR; break;
case DAV1D_TRC_SRGB: image->color.transfer = PL_COLOR_TRC_SRGB; break;
case DAV1D_TRC_SMPTE2084: image->color.transfer = PL_COLOR_TRC_PQ; break;
case DAV1D_TRC_HLG: image->color.transfer = PL_COLOR_TRC_HLG; break;
default:
printf("warning: unknown dav1d color transfer %d.. ignoring, picture "
"may be very incorrect\n", dav1d_pic->seq_hdr->trc);
break;
}
switch (dav1d_pic->seq_hdr->mtrx) {
case DAV1D_MC_IDENTITY:
// This is going to be either RGB or XYZ
if (dav1d_pic->seq_hdr->pri == DAV1D_COLOR_PRI_XYZ) {
image->repr.sys = PL_COLOR_SYSTEM_XYZ;
} else {
image->repr.sys = PL_COLOR_SYSTEM_RGB;
}
break;
case DAV1D_MC_UNKNOWN:
// PL_COLOR_SYSTEM_UNKNOWN maps to RGB, so hard-code this one
image->repr.sys = pl_color_system_guess_ycbcr(width, height);
break;
case DAV1D_MC_BT709: image->repr.sys = PL_COLOR_SYSTEM_BT_709; break;
case DAV1D_MC_BT601: image->repr.sys = PL_COLOR_SYSTEM_BT_601; break;
case DAV1D_MC_SMPTE240: image->repr.sys = PL_COLOR_SYSTEM_SMPTE_240M; break;
case DAV1D_MC_SMPTE_YCGCO: image->repr.sys = PL_COLOR_SYSTEM_YCGCO; break;
case DAV1D_MC_BT2020_NCL: image->repr.sys = PL_COLOR_SYSTEM_BT_2020_NC; break;
case DAV1D_MC_BT2020_CL: image->repr.sys = PL_COLOR_SYSTEM_BT_2020_C; break;
case DAV1D_MC_ICTCP:
// This one is split up based on the actual HDR curve in use
if (dav1d_pic->seq_hdr->trc == DAV1D_TRC_HLG) {
image->repr.sys = PL_COLOR_SYSTEM_BT_2100_HLG;
} else {
image->repr.sys = PL_COLOR_SYSTEM_BT_2100_PQ;
}
break;
default:
printf("warning: unknown dav1d color matrix %d.. ignoring, picture "
"may be very incorrect\n", dav1d_pic->seq_hdr->mtrx);
break;
}
if (dav1d_pic->seq_hdr->color_range) {
image->repr.levels = PL_COLOR_LEVELS_PC;
} else {
image->repr.levels = PL_COLOR_LEVELS_TV;
}
switch (dav1d_pic->seq_hdr->chr) {
case DAV1D_CHR_UNKNOWN: chroma_loc = PL_CHROMA_UNKNOWN; break;
case DAV1D_CHR_VERTICAL: chroma_loc = PL_CHROMA_LEFT; break;
case DAV1D_CHR_COLOCATED: chroma_loc = PL_CHROMA_TOP_LEFT; break;
}
#if PL_API_VER >= 63
if (settings->gpugrain && dav1d_pic->frame_hdr->film_grain.present) {
Dav1dFilmGrainData *src = &dav1d_pic->frame_hdr->film_grain.data;
struct pl_av1_grain_data *dst = &image->av1_grain;
*dst = (struct pl_av1_grain_data) {
.grain_seed = src->seed,
.num_points_y = src->num_y_points,
.chroma_scaling_from_luma = src->chroma_scaling_from_luma,
.num_points_uv = { src->num_uv_points[0], src->num_uv_points[1] },
.scaling_shift = src->scaling_shift,
.ar_coeff_lag = src->ar_coeff_lag,
.ar_coeff_shift = (int)src->ar_coeff_shift,
.grain_scale_shift = src->grain_scale_shift,
.uv_mult = { src->uv_mult[0], src->uv_mult[1] },
.uv_mult_luma = { src->uv_luma_mult[0], src->uv_luma_mult[1] },
.uv_offset = { src->uv_offset[0], src->uv_offset[1] },
.overlap = src->overlap_flag,
};
assert(sizeof(dst->points_y) == sizeof(src->y_points));
assert(sizeof(dst->points_uv) == sizeof(src->uv_points));
assert(sizeof(dst->ar_coeffs_y) == sizeof(src->ar_coeffs_y));
memcpy(dst->points_y, src->y_points, sizeof(src->y_points));
memcpy(dst->points_uv, src->uv_points, sizeof(src->uv_points));
memcpy(dst->ar_coeffs_y, src->ar_coeffs_y, sizeof(src->ar_coeffs_y));
// this one has different row sizes for alignment
for (int c = 0; c < 2; c++) {
for (int i = 0; i < 25; i++)
dst->ar_coeffs_uv[c][i] = src->ar_coeffs_uv[c][i];
}
}
#endif
// Upload the actual planes
struct pl_plane_data data[3] = {
{
// Y plane
.type = PL_FMT_UNORM,
.width = width,
.height = height,
.pixel_stride = bytes,
.row_stride = dav1d_pic->stride[0],
.component_size = {bytes * 8},
.component_map = {0},
}, {
// U plane
.type = PL_FMT_UNORM,
.width = width >> sub_x,
.height = height >> sub_y,
.pixel_stride = bytes,
.row_stride = dav1d_pic->stride[1],
.component_size = {bytes * 8},
.component_map = {1},
}, {
// V plane
.type = PL_FMT_UNORM,
.width = width >> sub_x,
.height = height >> sub_y,
.pixel_stride = bytes,
.row_stride = dav1d_pic->stride[1],
.component_size = {bytes * 8},
.component_map = {2},
},
};
bool ok = true;
for (int i = 0; i < image->num_planes; i++) {
if (settings->zerocopy) {
const struct pl_buf *buf = dav1d_pic->allocator_data;
assert(buf);
data[i].buf = buf;
data[i].buf_offset = (uintptr_t) dav1d_pic->data[i] - (uintptr_t) buf->data;
} else {
data[i].pixels = dav1d_pic->data[i];
}
ok &= pl_upload_plane(rd_priv_ctx->gpu, &image->planes[i], &rd_priv_ctx->plane_tex[i], &data[i]);
}
// Apply the correct chroma plane shift. This has to be done after pl_upload_plane
#if PL_API_VER >= 67
pl_image_set_chroma_location(image, chroma_loc);
#else
pl_chroma_location_offset(chroma_loc, &image->planes[1].shift_x, &image->planes[1].shift_y);
pl_chroma_location_offset(chroma_loc, &image->planes[2].shift_x, &image->planes[2].shift_y);
#endif
if (!ok) {
fprintf(stderr, "Failed uploading planes!\n"); fprintf(stderr, "Failed uploading planes!\n");
*image = (struct pl_image) {0}; p->image = (struct pl_frame) {0};
ret = -1;
} }
SDL_UnlockMutex(p->lock);
SDL_UnlockMutex(rd_priv_ctx->lock); return ret;
return !ok;
} }
// Align to power of 2 static int placebo_alloc_pic(Dav1dPicture *const pic, void *cookie)
#define ALIGN2(x, align) (((x) + (align) - 1) & ~((align) - 1))
static int placebo_alloc_pic(Dav1dPicture *const p, void *cookie)
{ {
Dav1dPlayRendererPrivateContext *rd_priv_ctx = cookie; Dav1dPlayRendererPrivateContext *rd_priv_ctx = cookie;
assert(rd_priv_ctx != NULL); assert(rd_priv_ctx != NULL);
SDL_LockMutex(rd_priv_ctx->lock);
const struct pl_gpu *gpu = rd_priv_ctx->gpu;
int ret = DAV1D_ERR(ENOMEM);
// Copied from dav1d_default_picture_alloc
const int hbd = p->p.bpc > 8;
const int aligned_w = ALIGN2(p->p.w, 128);
const int aligned_h = ALIGN2(p->p.h, 128);
const int has_chroma = p->p.layout != DAV1D_PIXEL_LAYOUT_I400;
const int ss_ver = p->p.layout == DAV1D_PIXEL_LAYOUT_I420;
const int ss_hor = p->p.layout != DAV1D_PIXEL_LAYOUT_I444;
p->stride[0] = aligned_w << hbd;
p->stride[1] = has_chroma ? (aligned_w >> ss_hor) << hbd : 0;
// Align strides up to multiples of the GPU performance hints
p->stride[0] = ALIGN2(p->stride[0], gpu->limits.align_tex_xfer_stride);
p->stride[1] = ALIGN2(p->stride[1], gpu->limits.align_tex_xfer_stride);
// Aligning offsets to 4 also implicity aligns to the texel size (1 or 2)
size_t off_align = ALIGN2(gpu->limits.align_tex_xfer_offset, 4);
const size_t y_sz = ALIGN2(p->stride[0] * aligned_h, off_align);
const size_t uv_sz = ALIGN2(p->stride[1] * (aligned_h >> ss_ver), off_align);
// The extra DAV1D_PICTURE_ALIGNMENTs are to brute force plane alignment,
// even in the case that the driver gives us insane alignments
const size_t pic_size = y_sz + 2 * uv_sz;
const size_t total_size = pic_size + DAV1D_PICTURE_ALIGNMENT * 4;
// Validate size limitations
if (total_size > gpu->limits.max_xfer_size) {
printf("alloc of %zu bytes exceeds limits\n", total_size);
goto err;
}
const struct pl_buf *buf = pl_buf_create(gpu, &(struct pl_buf_params) {
.type = PL_BUF_TEX_TRANSFER,
.host_mapped = true,
.size = total_size,
.memory_type = PL_BUF_MEM_HOST,
.user_data = p,
});
if (!buf) {
printf("alloc of GPU mapped buffer failed\n");
goto err;
}
assert(buf->data);
uintptr_t base = (uintptr_t) buf->data, data[3];
data[0] = ALIGN2(base, DAV1D_PICTURE_ALIGNMENT);
data[1] = ALIGN2(data[0] + y_sz, DAV1D_PICTURE_ALIGNMENT);
data[2] = ALIGN2(data[1] + uv_sz, DAV1D_PICTURE_ALIGNMENT);
// Sanity check offset alignment for the sake of debugging SDL_LockMutex(rd_priv_ctx->lock);
if (data[0] - base != ALIGN2(data[0] - base, off_align) || int ret = pl_allocate_dav1dpicture(pic, (void *) rd_priv_ctx->gpu);
data[1] - base != ALIGN2(data[1] - base, off_align) ||
data[2] - base != ALIGN2(data[2] - base, off_align))
{
printf("GPU buffer horribly misaligned, expect slowdown!\n");
}
p->allocator_data = (void *) buf;
p->data[0] = (void *) data[0];
p->data[1] = (void *) data[1];
p->data[2] = (void *) data[2];
ret = 0;
// fall through
err:
SDL_UnlockMutex(rd_priv_ctx->lock); SDL_UnlockMutex(rd_priv_ctx->lock);
return ret; return ret;
} }
...@@ -673,15 +386,13 @@ static void placebo_release_pic(Dav1dPicture *pic, void *cookie) ...@@ -673,15 +386,13 @@ static void placebo_release_pic(Dav1dPicture *pic, void *cookie)
{ {
Dav1dPlayRendererPrivateContext *rd_priv_ctx = cookie; Dav1dPlayRendererPrivateContext *rd_priv_ctx = cookie;
assert(rd_priv_ctx != NULL); assert(rd_priv_ctx != NULL);
assert(pic->allocator_data);
SDL_LockMutex(rd_priv_ctx->lock); SDL_LockMutex(rd_priv_ctx->lock);
const struct pl_gpu *gpu = rd_priv_ctx->gpu; pl_release_dav1dpicture(pic, (void *) rd_priv_ctx->gpu);
pl_buf_destroy(gpu, (const struct pl_buf **) &pic->allocator_data);
SDL_UnlockMutex(rd_priv_ctx->lock); SDL_UnlockMutex(rd_priv_ctx->lock);
} }
#ifdef HAVE_PLACEBO_VULKAN #if HAVE_PLACEBO_VULKAN
const Dav1dPlayRenderInfo rdr_placebo_vk = { const Dav1dPlayRenderInfo rdr_placebo_vk = {
.name = "placebo-vk", .name = "placebo-vk",
.create_renderer = placebo_renderer_create_vk, .create_renderer = placebo_renderer_create_vk,
...@@ -690,28 +401,20 @@ const Dav1dPlayRenderInfo rdr_placebo_vk = { ...@@ -690,28 +401,20 @@ const Dav1dPlayRenderInfo rdr_placebo_vk = {
.update_frame = placebo_upload_image, .update_frame = placebo_upload_image,
.alloc_pic = placebo_alloc_pic, .alloc_pic = placebo_alloc_pic,
.release_pic = placebo_release_pic, .release_pic = placebo_release_pic,
# if PL_API_VER >= 63
.supports_gpu_grain = 1, .supports_gpu_grain = 1,
# endif
}; };
#else #else
const Dav1dPlayRenderInfo rdr_placebo_vk = { NULL }; const Dav1dPlayRenderInfo rdr_placebo_vk = { NULL };
#endif #endif
#ifdef HAVE_PLACEBO_OPENGL #if HAVE_PLACEBO_OPENGL
const Dav1dPlayRenderInfo rdr_placebo_gl = { const Dav1dPlayRenderInfo rdr_placebo_gl = {
.name = "placebo-gl", .name = "placebo-gl",
.create_renderer = placebo_renderer_create_gl, .create_renderer = placebo_renderer_create_gl,
.destroy_renderer = placebo_renderer_destroy, .destroy_renderer = placebo_renderer_destroy,
.render = placebo_render, .render = placebo_render,
.update_frame = placebo_upload_image, .update_frame = placebo_upload_image,
.alloc_pic = placebo_alloc_pic,
.release_pic = placebo_release_pic,
# if PL_API_VER >= 63
.supports_gpu_grain = 1, .supports_gpu_grain = 1,
# endif
}; };
#else #else
const Dav1dPlayRenderInfo rdr_placebo_gl = { NULL }; const Dav1dPlayRenderInfo rdr_placebo_gl = { NULL };
......
...@@ -43,12 +43,18 @@ typedef struct renderer_priv_ctx ...@@ -43,12 +43,18 @@ typedef struct renderer_priv_ctx
SDL_Texture *tex; SDL_Texture *tex;
} Dav1dPlayRendererPrivateContext; } Dav1dPlayRendererPrivateContext;
static void *sdl_renderer_create() static void *sdl_renderer_create(const Dav1dPlaySettings *settings)
{ {
SDL_Window *win = dp_create_sdl_window(0); int window_flags = 0;
if (settings->fullscreen)
window_flags |= SDL_WINDOW_FULLSCREEN_DESKTOP;
SDL_Window *win = dp_create_sdl_window(window_flags);
if (win == NULL) if (win == NULL)
return NULL; return NULL;
SDL_ShowCursor(0);
// Alloc // Alloc
Dav1dPlayRendererPrivateContext *rd_priv_ctx = malloc(sizeof(Dav1dPlayRendererPrivateContext)); Dav1dPlayRendererPrivateContext *rd_priv_ctx = malloc(sizeof(Dav1dPlayRendererPrivateContext));
if (rd_priv_ctx == NULL) { if (rd_priv_ctx == NULL) {
...@@ -79,7 +85,9 @@ static void sdl_renderer_destroy(void *cookie) ...@@ -79,7 +85,9 @@ static void sdl_renderer_destroy(void *cookie)
Dav1dPlayRendererPrivateContext *rd_priv_ctx = cookie; Dav1dPlayRendererPrivateContext *rd_priv_ctx = cookie;
assert(rd_priv_ctx != NULL); assert(rd_priv_ctx != NULL);
SDL_DestroyTexture(rd_priv_ctx->tex);
SDL_DestroyRenderer(rd_priv_ctx->renderer); SDL_DestroyRenderer(rd_priv_ctx->renderer);
SDL_DestroyWindow(rd_priv_ctx->win);
SDL_DestroyMutex(rd_priv_ctx->lock); SDL_DestroyMutex(rd_priv_ctx->lock);
free(rd_priv_ctx); free(rd_priv_ctx);
} }
...@@ -142,6 +150,7 @@ static int sdl_update_texture(void *cookie, Dav1dPicture *dav1d_pic, ...@@ -142,6 +150,7 @@ static int sdl_update_texture(void *cookie, Dav1dPicture *dav1d_pic,
if (texture == NULL) { if (texture == NULL) {
texture = SDL_CreateTexture(rd_priv_ctx->renderer, SDL_PIXELFORMAT_IYUV, texture = SDL_CreateTexture(rd_priv_ctx->renderer, SDL_PIXELFORMAT_IYUV,
SDL_TEXTUREACCESS_STREAMING, width, height); SDL_TEXTUREACCESS_STREAMING, width, height);
SDL_RenderSetLogicalSize(rd_priv_ctx->renderer, width, height);
} }
SDL_UpdateYUVTexture(texture, NULL, SDL_UpdateYUVTexture(texture, NULL,
......
...@@ -43,24 +43,28 @@ dav1dplay_sources = files( ...@@ -43,24 +43,28 @@ dav1dplay_sources = files(
sdl2_dependency = dependency('sdl2', version: '>= 2.0.1', required: true) sdl2_dependency = dependency('sdl2', version: '>= 2.0.1', required: true)
if sdl2_dependency.found() if sdl2_dependency.found()
dav1dplay_deps = [sdl2_dependency] dav1dplay_deps = [sdl2_dependency, libm_dependency]
dav1dplay_cflags = [] dav1dplay_cflags = []
placebo_dependency = dependency('libplacebo', version: '>= 1.18.0', required: false) placebo_dependency = dependency('libplacebo', version: '>= 4.160.0', required: false)
if placebo_dependency.found() have_vulkan = false
have_placebo = placebo_dependency.found()
if have_placebo
dav1dplay_deps += placebo_dependency dav1dplay_deps += placebo_dependency
dav1dplay_cflags += '-DHAVE_PLACEBO'
# If libplacebo is found, we might be able to use Vulkan # If libplacebo is found, we might be able to use Vulkan
# with it, in which case we need the Vulkan library too. # with it, in which case we need the Vulkan library too.
vulkan_dependency = dependency('vulkan', required: false) vulkan_dependency = dependency('vulkan', required: false)
if vulkan_dependency.found() if vulkan_dependency.found()
dav1dplay_deps += vulkan_dependency dav1dplay_deps += vulkan_dependency
dav1dplay_cflags += '-DHAVE_VULKAN' have_vulkan = true
endif endif
endif endif
dav1dplay_cflags += '-DHAVE_PLACEBO=' + (have_placebo ? '1' : '0')
dav1dplay_cflags += '-DHAVE_VULKAN=' + (have_vulkan ? '1' : '0')
dav1dplay = executable('dav1dplay', dav1dplay = executable('dav1dplay',
dav1dplay_sources, dav1dplay_sources,
rev_target, rev_target,
......
exclude = .*/tests/.* exclude = .*/tests/.*
exclude = .*/tools/.* exclude = .*/tools/.*
exclude = .*/include/common/dump.h exclude = .*/include/common/dump.h
gcov-ignore-parse-errors = negative_hits.warn
...@@ -31,10 +31,23 @@ ...@@ -31,10 +31,23 @@
#include "config.h" #include "config.h"
#include <stddef.h> #include <stddef.h>
#include <assert.h>
#ifndef __has_attribute
#define __has_attribute(x) 0
#endif
#ifndef __has_feature
#define __has_feature(x) 0
#endif
#ifdef __GNUC__ #ifdef __GNUC__
#define ATTR_ALIAS __attribute__((may_alias)) #define ATTR_ALIAS __attribute__((may_alias))
#if defined(__MINGW32__) && !defined(__clang__)
#define ATTR_FORMAT_PRINTF(fmt, attr) __attribute__((__format__(__gnu_printf__, fmt, attr)))
#else
#define ATTR_FORMAT_PRINTF(fmt, attr) __attribute__((__format__(__printf__, fmt, attr))) #define ATTR_FORMAT_PRINTF(fmt, attr) __attribute__((__format__(__printf__, fmt, attr)))
#endif
#define COLD __attribute__((cold)) #define COLD __attribute__((cold))
#else #else
#define ATTR_ALIAS #define ATTR_ALIAS
...@@ -47,7 +60,7 @@ ...@@ -47,7 +60,7 @@
#define ALIGN_64_VAL 64 #define ALIGN_64_VAL 64
#define ALIGN_32_VAL 32 #define ALIGN_32_VAL 32
#define ALIGN_16_VAL 16 #define ALIGN_16_VAL 16
#elif ARCH_X86_32 || ARCH_ARM || ARCH_AARCH64 || ARCH_PPC64LE #elif ARCH_AARCH64 || ARCH_ARM || ARCH_LOONGARCH || ARCH_PPC64LE || ARCH_X86_32
/* ARM doesn't benefit from anything more than 16-byte alignment. */ /* ARM doesn't benefit from anything more than 16-byte alignment. */
#define ALIGN_64_VAL 16 #define ALIGN_64_VAL 16
#define ALIGN_32_VAL 16 #define ALIGN_32_VAL 16
...@@ -92,9 +105,29 @@ ...@@ -92,9 +105,29 @@
*/ */
#ifdef _MSC_VER #ifdef _MSC_VER
#define NOINLINE __declspec(noinline) #define NOINLINE __declspec(noinline)
#else /* !_MSC_VER */ #elif __has_attribute(noclone)
#define NOINLINE __attribute__((noinline, noclone))
#else
#define NOINLINE __attribute__((noinline)) #define NOINLINE __attribute__((noinline))
#endif /* !_MSC_VER */ #endif
#ifdef _MSC_VER
#define ALWAYS_INLINE __forceinline
#else
#define ALWAYS_INLINE __attribute__((always_inline)) inline
#endif
#if (defined(__ELF__) || defined(__MACH__) || (defined(_WIN32) && defined(__clang__))) && __has_attribute(visibility)
#define EXTERN extern __attribute__((visibility("hidden")))
#else
#define EXTERN extern
#endif
#if ARCH_X86_64 && __has_attribute(model)
#define ATTR_MCMODEL_SMALL __attribute__((model("small")))
#else
#define ATTR_MCMODEL_SMALL
#endif
#ifdef __clang__ #ifdef __clang__
#define NO_SANITIZE(x) __attribute__((no_sanitize(x))) #define NO_SANITIZE(x) __attribute__((no_sanitize(x)))
...@@ -103,11 +136,11 @@ ...@@ -103,11 +136,11 @@
#endif #endif
#if defined(NDEBUG) && (defined(__GNUC__) || defined(__clang__)) #if defined(NDEBUG) && (defined(__GNUC__) || defined(__clang__))
#undef assert
#define assert(x) do { if (!(x)) __builtin_unreachable(); } while (0) #define assert(x) do { if (!(x)) __builtin_unreachable(); } while (0)
#elif defined(NDEBUG) && defined(_MSC_VER) #elif defined(NDEBUG) && defined(_MSC_VER)
#undef assert
#define assert __assume #define assert __assume
#else
#include <assert.h>
#endif #endif
#if defined(__GNUC__) && !defined(__INTEL_COMPILER) && !defined(__clang__) #if defined(__GNUC__) && !defined(__INTEL_COMPILER) && !defined(__clang__)
...@@ -116,8 +149,8 @@ ...@@ -116,8 +149,8 @@
# define dav1d_uninit(x) x # define dav1d_uninit(x) x
#endif #endif
#ifdef _MSC_VER #if defined(_MSC_VER) && !defined(__clang__)
#include <intrin.h> #include <intrin.h>
static inline int ctz(const unsigned int mask) { static inline int ctz(const unsigned int mask) {
unsigned long idx; unsigned long idx;
...@@ -159,8 +192,22 @@ static inline int clzll(const unsigned long long mask) { ...@@ -159,8 +192,22 @@ static inline int clzll(const unsigned long long mask) {
} }
#endif /* !_MSC_VER */ #endif /* !_MSC_VER */
#ifndef __has_feature #ifndef static_assert
#define __has_feature(x) 0 #define CHECK_OFFSET(type, field, name) \
struct check_##type##_##field { int x[(name == offsetof(type, field)) ? 1 : -1]; }
#define CHECK_SIZE(type, size) \
struct check_##type##_size { int x[(size == sizeof(type)) ? 1 : -1]; }
#else
#define CHECK_OFFSET(type, field, name) \
static_assert(name == offsetof(type, field), #field)
#define CHECK_SIZE(type, size) \
static_assert(size == sizeof(type), #type)
#endif
#ifdef _MSC_VER
#define PACKED(...) __pragma(pack(push, 1)) __VA_ARGS__ __pragma(pack(pop))
#else
#define PACKED(...) __VA_ARGS__ __attribute__((__packed__))
#endif #endif
#endif /* DAV1D_COMMON_ATTRIBUTES_H */ #endif /* DAV1D_COMMON_ATTRIBUTES_H */
...@@ -34,7 +34,7 @@ ...@@ -34,7 +34,7 @@
#include "common/attributes.h" #include "common/attributes.h"
#if !defined(BITDEPTH) #if !defined(BITDEPTH)
typedef void pixel; typedef uint8_t pixel; /* can't be void due to pointer-to-array usage */
typedef void coef; typedef void coef;
#define HIGHBD_DECL_SUFFIX /* nothing */ #define HIGHBD_DECL_SUFFIX /* nothing */
#define HIGHBD_CALL_SUFFIX /* nothing */ #define HIGHBD_CALL_SUFFIX /* nothing */
......
/*
* Copyright © 2021, VideoLAN and dav1d authors
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef DAV1D_COMMON_FRAME_H
#define DAV1D_COMMON_FRAME_H
/*
* Checks whether Dav1dFrameType == INTER || == SWITCH
* Both are defined as odd numbers {1, 3} and therefore have the LSB set.
* See also: AV1 spec 6.8.2
*/
#define IS_INTER_OR_SWITCH(frame_header) \
((frame_header)->frame_type & 1)
/*
* Checks whether Dav1dFrameType == KEY || == INTRA
* See also: AV1 spec 6.8.2
*/
#define IS_KEY_OR_INTRA(frame_header) \
(!IS_INTER_OR_SWITCH(frame_header))
#endif /* DAV1D_COMMON_FRAME_H */
...@@ -65,11 +65,11 @@ static inline int apply_sign64(const int v, const int64_t s) { ...@@ -65,11 +65,11 @@ static inline int apply_sign64(const int v, const int64_t s) {
} }
static inline int ulog2(const unsigned v) { static inline int ulog2(const unsigned v) {
return 31 - clz(v); return 31 ^ clz(v);
} }
static inline int u64log2(const uint64_t v) { static inline int u64log2(const uint64_t v) {
return 63 - clzll(v); return 63 ^ clzll(v);
} }
static inline unsigned inv_recenter(const unsigned r, const unsigned v) { static inline unsigned inv_recenter(const unsigned r, const unsigned v) {
......
...@@ -32,24 +32,26 @@ ...@@ -32,24 +32,26 @@
#include <stdlib.h> #include <stdlib.h>
#if defined(NDEBUG) #if defined(NDEBUG)
#define debug_abort() #define debug_print(...) do {} while (0)
#define debug_abort() do {} while (0)
#else #else
#define debug_print(...) fprintf(stderr, __VA_ARGS__)
#define debug_abort abort #define debug_abort abort
#endif #endif
#define validate_input_or_ret_with_msg(x, r, ...) \ #define validate_input_or_ret_with_msg(x, r, ...) \
if (!(x)) { \ if (!(x)) { \
fprintf(stderr, "Input validation check \'%s\' failed in %s!\n", \ debug_print("Input validation check \'%s\' failed in %s!\n", \
#x, __func__); \ #x, __func__); \
fprintf(stderr, __VA_ARGS__); \ debug_print(__VA_ARGS__); \
debug_abort(); \ debug_abort(); \
return r; \ return r; \
} }
#define validate_input_or_ret(x, r) \ #define validate_input_or_ret(x, r) \
if (!(x)) { \ if (!(x)) { \
fprintf(stderr, "Input validation check \'%s\' failed in %s!\n", \ debug_print("Input validation check \'%s\' failed in %s!\n", \
#x, __func__); \ #x, __func__); \
debug_abort(); \ debug_abort(); \
return r; \ return r; \
} }
......