mirror of
https://github.com/libretro/RetroArch.git
synced 2026-11-07 08:36:02 -10:00
Screen resolution on PS3 lived in the ps3 context driver: a list of resolution ids it probed for at init (leaked on every reinit - the malloc'd list was never freed), stepped through with left/right on the menu entry and applied with OK by reinitialising the driver. On PSL1GHT that did nothing at all: the RSX driver configures the video output itself, from the mode the system menu has it in, and never looked at the chosen id. dispserv_ps3 is now the display server for __PS3__. It lists the modes the connected display takes (480, 576, 960x1080, 720, the 1080-line scalers, 1080), marks the one in use, and switches between them. The menu entry now opens the same resolution dropdown the desktop platforms use, and Start resets to the system menu's mode as it does elsewhere. A choice is stored in current_resolution_id - the video output resolution id, as before, so existing configs keep their modes - and the video driver is reinitialised to configure the output in it; picking the mode already in use reinitialises nothing. A refresh-rate-only set_resolution (the autoswitch) is refused. ps3_display_server_resolution() is what configures the output: the chosen mode when the display takes it, otherwise the system menu's. The RSX driver's rsx_init_screen now uses it in place of the system mode, which makes the setting work on PSL1GHT; the PSGL context uses it for its device size in place of the unchecked stored id, so a choice the current display cannot show (a config moved to another TV) falls back instead of failing. The mode table and those rules are in dispserv_ps3_modes.c, which takes the display's answers through a callback and so has no SDK dependency. The context driver loses its list and its output size/prev/next callbacks, and global_t loses the list, count, check, idx and initial fields nothing reads any more. Test: samples/gfx/dispserv_ps3 links the real server and mode rules against stubbed PSL1GHT video output queries (declared in the tree's PS3 stub <sysutil/video.h>, now carrying PSL1GHT's layouts) for displays taking different sets of modes with the system menu in each: the list is exactly what the display takes with the mode in use marked; each entry, through the label the menu prints and the parse the menu does, selects its mode, stores its id, reinitialises once (never for the mode in use) and reports its rate and size; the mode the driver configures follows the choice and falls back to the system mode for one the display lacks or an unknown id; refresh-only and unavailable modes are refused without touching anything. Wired into Linux-samples-gfx (ASan + UBSan); compile-matrix gains a PSL1GHT syntax lane for the server.
1379 lines
72 KiB
YAML
1379 lines
72 KiB
YAML
name: CI Linux samples/gfx
|
|
|
|
on:
|
|
push:
|
|
branches:
|
|
- master
|
|
pull_request:
|
|
branches:
|
|
- master
|
|
workflow_dispatch:
|
|
|
|
permissions:
|
|
contents: read
|
|
|
|
env:
|
|
ACTIONS_ALLOW_USE_UNSECURE_NODE_VERSION: true
|
|
|
|
concurrency:
|
|
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
|
|
cancel-in-progress: ${{ github.ref != 'refs/heads/master' }}
|
|
|
|
jobs:
|
|
video-thread-state:
|
|
name: The video thread reads its snapshot
|
|
runs-on: ubuntu-latest
|
|
timeout-minutes: 10
|
|
|
|
steps:
|
|
- name: Checkout
|
|
uses: actions/checkout@v5
|
|
|
|
- name: Tree check
|
|
shell: bash
|
|
run: |
|
|
set -eu
|
|
# The main thread builds a video_frame_info_t and hands it
|
|
# over; a driver that reaches back into config_get_ptr() from
|
|
# frame() or present_last() reads settings the main thread
|
|
# may be writing. TSan found one of those in the null driver
|
|
# rather than anyone noticing it by reading, so this looks
|
|
# for the rest: it walks what those two entry points call
|
|
# and fails on live-state access it can reach. The drivers
|
|
# for platforms with no video thread are listed in the
|
|
# checker with that reason, and the list has to shrink when
|
|
# one of them stops doing it.
|
|
python3 tools/video_thread_state_check.py
|
|
|
|
surface-requirements:
|
|
name: Producers ask the surface layer
|
|
runs-on: ubuntu-latest
|
|
timeout-minutes: 10
|
|
|
|
steps:
|
|
- name: Checkout
|
|
uses: actions/checkout@v5
|
|
|
|
- name: Self-test
|
|
shell: bash
|
|
run: |
|
|
set -eu
|
|
# A producer outside the allowlist that reads the video flags
|
|
# directly must be caught, and so must an entry whose file has
|
|
# stopped reading them - the second is what makes the list
|
|
# shrink as producers migrate instead of sitting there.
|
|
python3 tools/surface_requirements_check.py --selftest
|
|
|
|
- name: Tree check
|
|
shell: bash
|
|
run: |
|
|
set -eu
|
|
# What a producer should emit is the surface layer's answer
|
|
# (gfx_surface_query_requirements), so a decoder has one place
|
|
# to ask rather than three unrelated flags to know about. The
|
|
# files still asking directly are listed in the checker and
|
|
# are migrated one at a time; a new one may not join them.
|
|
python3 tools/surface_requirements_check.py
|
|
|
|
samples-gfx:
|
|
name: Build and run samples/gfx
|
|
runs-on: ubuntu-latest
|
|
# The gfx_thumbnail_preview step alone is ~5 min: two sanitizer
|
|
# builds, the video harness under each, and the windowed-animation
|
|
# harness under ASan over ~70 MB of lossless frames. 20 was cut
|
|
# too close once the animation harness joined; the run was
|
|
# cancelled mid-TSan pass.
|
|
timeout-minutes: 30
|
|
|
|
steps:
|
|
- name: Install dependencies
|
|
timeout-minutes: 5
|
|
run: |
|
|
pkgs="build-essential libx11-dev libxrandr-dev libdrm-dev"
|
|
pkgs="$pkgs xserver-xorg-core xserver-xorg-video-dummy x11-xserver-utils libsdl2-dev libxinerama-dev"
|
|
pkgs="$pkgs libwayland-dev"
|
|
pkgs="$pkgs gcc-mingw-w64-x86-64 wine64 openbox cmake ninja-build"
|
|
if dpkg -s $pkgs >/dev/null 2>&1; then
|
|
echo "All packages already present on runner image; skipping apt."
|
|
exit 0
|
|
fi
|
|
for i in 1 2 3; do
|
|
sudo timeout 90 apt-get update -y \
|
|
-o Acquire::Retries=3 -o Acquire::http::Timeout=30 && break
|
|
echo "apt-get update failed (attempt $i); retrying in 10s"
|
|
sleep 10
|
|
done
|
|
sudo apt-get install -y \
|
|
-o Acquire::Retries=3 -o Acquire::http::Timeout=30 \
|
|
$pkgs
|
|
|
|
- name: Checkout
|
|
uses: actions/checkout@v5
|
|
|
|
- name: Restore ccache
|
|
uses: actions/cache@v4
|
|
with:
|
|
path: ~/.ccache
|
|
key: ccache-${{ github.workflow }}-${{ github.job }}-${{ github.sha }}
|
|
restore-keys: |
|
|
ccache-${{ github.workflow }}-${{ github.job }}-
|
|
|
|
- name: Enable ccache
|
|
run: |
|
|
set -eu
|
|
command -v ccache >/dev/null 2>&1 || sudo apt-get install -y -qq ccache
|
|
export CCACHE_DIR="$HOME/.ccache"
|
|
echo "CCACHE_DIR=$CCACHE_DIR" >> "$GITHUB_ENV"
|
|
echo "/usr/lib/ccache" >> "$GITHUB_PATH"
|
|
ccache -M 500M
|
|
ccache -z
|
|
|
|
- name: Video modeline engine goldens and C89 lane
|
|
shell: bash
|
|
run: |
|
|
set -eu
|
|
# The engine in gfx/modeline/ replaced the SwitchRes 2.2.1
|
|
# library; tools/modeline_gold.ref is the reference output of
|
|
# the same matrix run through that library. The scoring and
|
|
# timing arithmetic are compared bit-for-bit: any drift in
|
|
# modeline_create/compare/adjust, the presets or the list
|
|
# logic shows up here as a diff rather than as a CRT that
|
|
# loses sync in the field.
|
|
C89="-std=c89 -ansi -pedantic -Werror=pedantic -Wno-long-long"
|
|
C89="$C89 -Werror=declaration-after-statement -Wno-variadic-macros"
|
|
C89="$C89 -D_GNU_SOURCE"
|
|
INC="-Ilibretro-common/include -I. -Ideps"
|
|
# Binaries and outputs go to a scratch dir, never the tree.
|
|
T=$(mktemp -d)
|
|
gcc $C89 $INC tools/modeline_gold.c \
|
|
libretro-common/compat/compat_strl.c -lm -o "$T/modeline_gold"
|
|
"$T/modeline_gold" > "$T/modeline_gold.out"
|
|
diff -u tools/modeline_gold.ref "$T/modeline_gold.out"
|
|
echo "[pass] modeline goldens (C89)"
|
|
gcc -g -O1 -fsanitize=address,undefined -fno-sanitize-recover=all \
|
|
-std=gnu89 -D_GNU_SOURCE $INC tools/modeline_gold.c \
|
|
libretro-common/compat/compat_strl.c -lm -o "$T/modeline_gold_san"
|
|
ASAN_OPTIONS=detect_leaks=1 UBSAN_OPTIONS=print_stacktrace=1 \
|
|
"$T/modeline_gold_san" > "$T/modeline_gold_san.out"
|
|
diff -u tools/modeline_gold.ref "$T/modeline_gold_san.out"
|
|
echo "[pass] modeline goldens (ASan/UBSan)"
|
|
rm -rf "$T"
|
|
# The consumer and the X11 apply path are C89 too; the
|
|
# full C89_BUILD covers them only after a complete build.
|
|
DEF="-DRARCH_INTERNAL -DHAVE_THREADS -DHAVE_MENU -DHAVE_MODELINE"
|
|
DEF="$DEF -DHAVE_X11 -DHAVE_XRANDR -DHAVE_CONFIGFILE -DHAVE_DYNAMIC"
|
|
for f in gfx/modeline/modeline_core.c \
|
|
gfx/modeline/modeline_monitor.c \
|
|
gfx/modeline/modeline_list.c \
|
|
gfx/modeline/modeline_ini.c \
|
|
gfx/video_crt_switch.c \
|
|
gfx/display_servers/dispserv_x11.c; do
|
|
gcc $C89 $INC $DEF -c "$f" -o /dev/null
|
|
done
|
|
echo "[pass] modeline C89 syntax check"
|
|
|
|
- name: Atomic backend portability of the gfx sources
|
|
shell: bash
|
|
run: |
|
|
set -eu
|
|
# The gfx sources that use retro_atomic.h have to build on
|
|
# every backend, not just the one this host selects. The
|
|
# extended operations (exchange, CAS, the pointer ops) are
|
|
# offered only where a backend can express them, so a caller
|
|
# that uses one without gating on it builds here and breaks
|
|
# on whichever console lands elsewhere; the counters have to
|
|
# stay warning-clean as statements, which an expression-form
|
|
# fetch_add is not. Both have reached a console lane before
|
|
# this one, hence -Werror.
|
|
INC="-Ilibretro-common/include -I. -Ideps"
|
|
DEF="-DRARCH_INTERNAL -DHAVE_THREADS -DHAVE_MENU -DHAVE_RGUI"
|
|
DEF="$DEF -DHAVE_CONFIGFILE -DHAVE_CHEATS -D_GNU_SOURCE"
|
|
DEF="$DEF -DRETRO_ATOMIC_SUPPRESS_WARNING"
|
|
# The PS2 backend needs ps2sdk's <kernel.h>; the sample under
|
|
# libretro-common/samples/atomic carries a stand-in for it.
|
|
PS2STUB="libretro-common/samples/atomic/retro_atomic_ps2_backend"
|
|
for backend in C11 GCC_NEW SYNC PS2 VOLATILE; do
|
|
EXTRA=""
|
|
if [ "$backend" = "PS2" ]; then EXTRA="-I$PS2STUB/ps2stub"; fi
|
|
for f in gfx/video_thread_wrapper.c \
|
|
gfx/font_driver.c \
|
|
gfx/gfx_thumbnail.c \
|
|
libretro-common/queues/task_queue.c; do
|
|
gcc -std=gnu99 -O2 -Wall -Werror $INC $EXTRA $DEF \
|
|
"-DRETRO_ATOMIC_FORCE_$backend" -c "$f" -o /dev/null
|
|
done
|
|
echo "[pass] RETRO_ATOMIC_FORCE_$backend"
|
|
done
|
|
echo "[pass] atomic backend portability"
|
|
|
|
- name: Build and run vulkan_extension_count_test (ASan)
|
|
shell: bash
|
|
working-directory: samples/gfx/vulkan_extension_count
|
|
run: |
|
|
set -eu
|
|
# Regression test for the heap-overflow fix in
|
|
# gfx/common/vulkan_common.c::vulkan_find_device_extensions.
|
|
# Pre-fix the function appended the required-extension list
|
|
# twice -- once via memcpy at the top of the body, then
|
|
# again in a per-element loop -- consuming
|
|
# (count_initial + 2*num_required + num_optional) slots in
|
|
# the caller's buffer. vulkan_context_create_device_wrapper
|
|
# sized its malloc for (count_initial + num_required +
|
|
# num_optional) entries, so a libretro core using the Vulkan
|
|
# HW context-negotiation interface against a GPU exposing at
|
|
# least one optional extension hit a one-element heap-buffer-
|
|
# overflow (8 bytes on 64-bit) at the end of the malloc'd
|
|
# block. Build under AddressSanitizer so any reintroduction
|
|
# of the duplicate write is caught at the bounds level. If
|
|
# vulkan_common.c amends the append-to-enabled[] block, the
|
|
# verbatim copy in vulkan_extension_count_test.c must follow.
|
|
make clean all SANITIZER=address
|
|
test -x vulkan_extension_count_test
|
|
timeout 60 ./vulkan_extension_count_test
|
|
echo "[pass] vulkan_extension_count_test"
|
|
|
|
- name: Build and run vulkan_swapchain_clamp_test (ASan)
|
|
shell: bash
|
|
working-directory: samples/gfx/vulkan_swapchain_clamp
|
|
run: |
|
|
set -eu
|
|
# Regression test for the unclamped-swapchain-image-count
|
|
# fix in gfx/common/vulkan_common.c::vulkan_create_swapchain.
|
|
# Pre-fix the two vkGetSwapchainImagesKHR calls (count
|
|
# query + image fill) had no clamp between them, so a driver
|
|
# returning more than VULKAN_MAX_SWAPCHAIN_IMAGES (16) images
|
|
# on the second call wrote past context.swapchain_images[16]
|
|
# and every loop bounded by num_swapchain_images walked past
|
|
# its compile-time-sized companion array (~12 such loops
|
|
# across init/deinit/textures/buffers/descriptor pools/
|
|
# command buffers/readback and direct vk->swapchain[i]
|
|
# uses). Build under AddressSanitizer so any reintroduction
|
|
# of either the request-side or the post-create clamp is
|
|
# caught at the bounds level. If vulkan_common.c amends
|
|
# the cap or the post-create clamp, the verbatim copies in
|
|
# vulkan_swapchain_clamp_test.c must follow.
|
|
make clean all SANITIZER=address
|
|
test -x vulkan_swapchain_clamp_test
|
|
timeout 60 ./vulkan_swapchain_clamp_test
|
|
echo "[pass] vulkan_swapchain_clamp_test"
|
|
|
|
- name: Build and run vulkan_texture_size_test (ASan)
|
|
shell: bash
|
|
working-directory: samples/gfx/vulkan_texture_size
|
|
run: |
|
|
set -eu
|
|
# Regression test for the 32-bit-overflow fix in
|
|
# gfx/drivers/vulkan.c::vulkan_create_texture's staging-
|
|
# buffer sizing. Pre-fix the buffer_info.size calculation
|
|
# (buffer_width * height) was unsigned*unsigned in 32-bit
|
|
# before being widened to VkDeviceSize on assignment. With
|
|
# dimensions large enough to wrap (e.g. 65536x16385x4 ->
|
|
# 0x1_0004_0000 truncates to 0x40000), the staging buffer
|
|
# was malloc'd at the wrapped (small) size while the per-row
|
|
# upload memcpy loop walked the full width x height,
|
|
# writing past the mapped region into adjacent heap memory.
|
|
# Reachable from libretro cores supplying oversized
|
|
# retro_framebuffer dimensions and from vulkan_load_texture
|
|
# / vulkan_set_texture_frame. Build under AddressSanitizer
|
|
# so any reintroduction of the 32-bit arithmetic is caught
|
|
# at the bounds level. If vulkan.c amends the size
|
|
# calculation or upload-loop strides, the verbatim copies
|
|
# in vulkan_texture_size_test.c must follow.
|
|
make clean all SANITIZER=address
|
|
test -x vulkan_texture_size_test
|
|
timeout 60 ./vulkan_texture_size_test
|
|
echo "[pass] vulkan_texture_size_test"
|
|
|
|
- name: Build and run slang_texture_index_bounds_test (ASan)
|
|
shell: bash
|
|
working-directory: samples/gfx/slang_texture_index_bounds
|
|
run: |
|
|
set -eu
|
|
# Regression test for the texture-semantic index-bounds
|
|
# fix in gfx/drivers_shader/slang_process.cpp::
|
|
# validate_texture_semantic_index(). Pre-fix only
|
|
# SLANG_TEXTURE_SEMANTIC_PASS_OUTPUT was bounded against
|
|
# reflection->pass_number; ORIGINAL_HISTORY, PASS_FEEDBACK
|
|
# and USER had no upper bound on their array index. The
|
|
# index suffix in arrayed semantic names like
|
|
# `OriginalHistory42` is parsed via strtoul in
|
|
# slang_name_to_texture_semantic_array() and propagates
|
|
# into the downstream resize_minimum() calls in
|
|
# set_ubo_texture_offset() and the direct sampler-binding
|
|
# loop. A malicious slang shader declaring
|
|
# `OriginalHistory4294967294` makes std::vector::resize
|
|
# request ~128 GiB, throwing std::bad_alloc which
|
|
# propagates unhandled out of the filter-chain create
|
|
# path, terminating the process. Reachable from any
|
|
# malicious slang preset (downloaded via Online Updater
|
|
# or shipped third-party). Build under AddressSanitizer
|
|
# so any reintroduction is caught at the bounds level.
|
|
# If slang_process.cpp amends the cap table or the
|
|
# dispatch structure, the verbatim copy in
|
|
# slang_texture_index_bounds_test.c must follow.
|
|
make clean all SANITIZER=address
|
|
test -x slang_texture_index_bounds_test
|
|
timeout 60 ./slang_texture_index_bounds_test
|
|
echo "[pass] slang_texture_index_bounds_test"
|
|
|
|
- name: Build and run shader_dir_cycle_test (ASan + UBSan)
|
|
shell: bash
|
|
working-directory: samples/gfx/shader_dir_cycle
|
|
run: |
|
|
set -eu
|
|
# Which folder the Next/Previous Shader hotkeys cycle, over a
|
|
# shader tree built on disk, with gfx/video_shader_parse.c
|
|
# compiled for real and only the frontend stubbed.
|
|
#
|
|
# The folder is chosen from the loaded preset, and the cases
|
|
# that decide it are all shapes the menu itself writes:
|
|
# retroarch.slangp and the core auto preset are simple
|
|
# presets (#reference), so they stand in for what they point
|
|
# at, while a preset the user loaded as itself anchors where
|
|
# it sits however it is written - "Save Preset As" emits a
|
|
# #reference too, and following those would cycle a preset's
|
|
# source folder instead of the folder it was saved into.
|
|
#
|
|
# Two orderings are what this pins. Following a reference
|
|
# one hop further than the wrapper puts the cycling in a
|
|
# folder the user never loaded from; and re-deriving the
|
|
# anchor from a preset the hotkey itself just applied walks
|
|
# back to whatever that preset references, so an entry that
|
|
# is a simple preset traps the cycling on the pair forever.
|
|
# The setting is also toggled mid-run: the list is built
|
|
# under one value and must be dropped when it changes, or
|
|
# turning it on never leaves the root it was cycling.
|
|
make clean all SANITIZER=address,undefined
|
|
test -x shader_dir_cycle_test
|
|
# The tree is built under a scratch dir, never in the repo.
|
|
T=$(mktemp -d)
|
|
ASAN_OPTIONS=detect_leaks=1 UBSAN_OPTIONS=print_stacktrace=1 \
|
|
timeout 60 ./shader_dir_cycle_test "$T"
|
|
rm -rf "${T:?}"
|
|
echo "[pass] shader_dir_cycle_test"
|
|
|
|
- name: Build and run vulkan_mailbox_init_leak_test (ASan + LSan)
|
|
shell: bash
|
|
working-directory: samples/gfx/vulkan_mailbox_init_leak
|
|
run: |
|
|
set -eu
|
|
# Regression test for the partial-init leak fix in
|
|
# gfx/common/vulkan_common.c::vulkan_emulated_mailbox_init.
|
|
# Pre-fix the function had three sequential allocations
|
|
# (scond_new, slock_new, sthread_create) and on any of the
|
|
# latter two failures returned `false` directly, leaking
|
|
# the already-allocated cond and/or lock. Both production
|
|
# call sites in vulkan_create_swapchain ignore the return
|
|
# value, so an init failure also left vk->mailbox.lock ==
|
|
# NULL and vk->mailbox.cond == NULL while VK_DATA_FLAG_
|
|
# EMULATING_MAILBOX was still set, setting up a NULL-deref
|
|
# the next time vulkan_acquire_next_image routed into
|
|
# vulkan_emulated_mailbox_acquire_next_image (slock_lock
|
|
# on a NULL pointer). Fix routes every early failure
|
|
# through `goto error` to a single deinit call, which is
|
|
# null-safe and ends with a memset, leaving the struct in
|
|
# the same shape the deinit-on-shutdown path produces and
|
|
# tripping the existing `mailbox.swapchain == VK_NULL_
|
|
# HANDLE` guard at vulkan_acquire_next_image. Build under
|
|
# AddressSanitizer with leak detection so any
|
|
# reintroduction is caught at the leak level. If
|
|
# vulkan_common.c amends the init or deinit, the verbatim
|
|
# copies in vulkan_mailbox_init_leak_test.c must follow.
|
|
make clean all SANITIZER=address
|
|
test -x vulkan_mailbox_init_leak_test
|
|
ASAN_OPTIONS=detect_leaks=1 timeout 60 \
|
|
./vulkan_mailbox_init_leak_test
|
|
echo "[pass] vulkan_mailbox_init_leak_test"
|
|
|
|
- name: Build and run vulkan_ctx_double_free_test (ASan)
|
|
shell: bash
|
|
working-directory: samples/gfx/vulkan_ctx_double_free
|
|
run: |
|
|
set -eu
|
|
# Regression test for the double-free / use-after-free
|
|
# fix in the Vulkan context drivers' set_video_mode error
|
|
# paths: gfx/drivers_context/wayland_vk_ctx.c,
|
|
# w_vk_ctx.c, x_vk_ctx.c. Pre-fix each set_video_mode
|
|
# called its own destroy()/destroy_resources()+free() on
|
|
# ctx_data before returning false; the caller in
|
|
# gfx/drivers/vulkan.c::vulkan_init then ran
|
|
# vulkan_free()->ctx_driver->destroy(ctx_data) on the
|
|
# already-freed pointer (UAF read of struct fields, then
|
|
# a second free of the same allocation). Reachable from
|
|
# vulkan_surface_create() failure (missing extension /
|
|
# driver issue), Wayland's set_video_mode_common_*
|
|
# helpers failing, X11's XGetVisualInfo returning NULL,
|
|
# or win32_set_video_mode failing. Cocoa (cocoa_vk_ctx)
|
|
# and Android (android_vk_ctx) already handle this
|
|
# correctly by returning false without freeing -- the
|
|
# fix makes Wayland/Win32/X11 match. Build under
|
|
# AddressSanitizer so any reintroduction is caught at
|
|
# the bounds level (UAF + double-free both fire). If
|
|
# any of the three context drivers amends the
|
|
# set_video_mode error path to once again destroy
|
|
# ctx_data, the verbatim copy in
|
|
# vulkan_ctx_double_free_test.c must follow.
|
|
make clean all SANITIZER=address
|
|
test -x vulkan_ctx_double_free_test
|
|
timeout 60 ./vulkan_ctx_double_free_test
|
|
echo "[pass] vulkan_ctx_double_free_test"
|
|
|
|
- name: Build and run gfx_thumbnail_status_atomic_test (TSan)
|
|
shell: bash
|
|
working-directory: samples/gfx/gfx_thumbnail_status_atomic
|
|
run: |
|
|
set -eu
|
|
# Regression test for the cross-thread thumbnail status
|
|
# synchronisation in gfx/gfx_thumbnail.c. The .status
|
|
# field is read by the video thread (gfx_thumbnail_draw)
|
|
# and written by the upload-callback thread
|
|
# (gfx_thumbnail_handle_upload), with a release-store /
|
|
# acquire-load pairing that guards the texture / width /
|
|
# height fields published alongside the AVAILABLE
|
|
# status. Pre-port the file used a hand-rolled
|
|
# __atomic_* / _Interlocked / volatile shim. Post-port
|
|
# the .status field is retro_atomic_int_t and the
|
|
# accesses route through retro_atomic_*_int macros.
|
|
#
|
|
# ThreadSanitizer is the right validator for this test:
|
|
# it instruments every atomic load and store and would
|
|
# flag a missing-barrier regression as a data race, even
|
|
# on x86 TSO where the hardware otherwise hides the bug.
|
|
# ASan/UBSan would not catch barrier removal at all
|
|
# (no out-of-bounds access, no UB). The test also
|
|
# carries static-assertion checks that validate the
|
|
# struct layout invariant (.status size == sizeof(int))
|
|
# so the field's atomic type does not change the
|
|
# gfx_thumbnail_t ABI. If gfx_thumbnail.c amends
|
|
# GFX_THUMB_STATUS_STORE / GFX_THUMB_STATUS_LOAD or the
|
|
# .status field type, the verbatim copies in
|
|
# gfx_thumbnail_status_atomic_test.c must follow.
|
|
#
|
|
# STRESS_ITERS is bounded here because the default of
|
|
# 1000000 measures ~145 s on a single core before TSan
|
|
# multiplies it, and this step allows 60 s. 20000 runs
|
|
# in ~2.4 s under TSan on one core and still routes every
|
|
# round trip through the instrumented release/acquire
|
|
# pair; the default stays at 1000000 for local soak runs.
|
|
make clean all SANITIZER=thread EXTRA_CFLAGS="-DSTRESS_ITERS=20000"
|
|
test -x gfx_thumbnail_status_atomic_test
|
|
TSAN_OPTIONS=halt_on_error=1 timeout 60 \
|
|
./gfx_thumbnail_status_atomic_test
|
|
echo "[pass] gfx_thumbnail_status_atomic_test"
|
|
|
|
- name: Build and run gfx_widgets_msg_queue_race_test (TSan)
|
|
shell: bash
|
|
working-directory: samples/gfx/gfx_widgets_msg_queue_race
|
|
run: |
|
|
set -eu
|
|
# Regression test for the producer-producer race fix on
|
|
# dispgfx_widget_t::msg_queue in gfx/gfx_widgets.c.
|
|
# Pre-fix the FIFO had no dedicated lock: producers could
|
|
# be invoked from any thread (the threaded task system at
|
|
# libretro-common/queues/task_queue.c runs a worker
|
|
# thread; gfx/video_driver.c::video_driver_frame released
|
|
# RUNLOOP_MSG_QUEUE_LOCK before calling the producer, so
|
|
# main-thread producers ran unlocked) yet there was no
|
|
# lock dedicated to msg_queue itself. Post-fix a
|
|
# msg_queue_lock is acquired around every fifo_write (in
|
|
# gfx_widgets_msg_queue_push), every fifo_read (in
|
|
# gfx_widgets_iterate), and the avail-check that gates
|
|
# them. The outer fast-path FIFO_*_AVAIL checks that
|
|
# used to wrap the function bodies were dropped: reading
|
|
# the FIFO cursors outside the lock is itself a data
|
|
# race against the locked writes (TSan-detectable;
|
|
# benign on x86 TSO but real on weak-memory hardware).
|
|
#
|
|
# ThreadSanitizer is the right validator: it instruments
|
|
# every memory access on fifo_buffer_t::end and
|
|
# ::first and would flag a missing-lock regression as a
|
|
# data race on the producer side, the consumer side, or
|
|
# the avail check. ASan/UBSan would not catch the lock
|
|
# removal at all. halt_on_error=1 makes TSan exit
|
|
# non-zero on the first observed race, which is what we
|
|
# want for CI: any race here means the production
|
|
# locking discipline is broken.
|
|
#
|
|
# The test mirrors the post-fix locking protocol of
|
|
# gfx_widgets_msg_queue_push and gfx_widgets_iterate's
|
|
# FIFO interaction; the widget allocation, font
|
|
# measurement, and animation push that wrap the actual
|
|
# FIFO calls in production are not part of the race and
|
|
# are stripped from the test. If gfx_widgets.c amends
|
|
# the locking around msg_queue, the verbatim copies in
|
|
# gfx_widgets_msg_queue_race_test.c must follow.
|
|
make clean all SANITIZER=thread
|
|
test -x gfx_widgets_msg_queue_race_test
|
|
TSAN_OPTIONS=halt_on_error=1 timeout 60 \
|
|
./gfx_widgets_msg_queue_race_test
|
|
echo "[pass] gfx_widgets_msg_queue_race_test"
|
|
|
|
- name: Build and run gfx_animation_sanitize_test (ASan + UBSan)
|
|
shell: bash
|
|
working-directory: samples/gfx/gfx_animation_sanitize
|
|
run: |
|
|
set -eu
|
|
# Regression test for the stack overflow in
|
|
# gfx/gfx_animation.c::gfx_animation_ticker(). Pre-fix it
|
|
# passed PATH_MAX_LENGTH to utf8cpy() as the destination
|
|
# size, in all five places it copies, regardless of what the
|
|
# caller had provided. utf8cpy()'s second argument is a
|
|
# size in BYTES; ticker->len, which the surrounding logic
|
|
# uses, is a width in GLYPHS -- so the guard
|
|
# `str_len <= ticker->len` compared glyph counts and then
|
|
# copied bytes, and one glyph of CJK or emoji is three or
|
|
# four of them. PATH_MAX_LENGTH is 512 and not one of the
|
|
# 27 call sites in tree provides that much (mostly
|
|
# NAME_MAX_LENGTH, 64 bytes in one RGUI case), so the
|
|
# function was licensed to write 511 bytes plus a
|
|
# terminator into any of them. Reachable from content: RGUI
|
|
# sets ticker.len from term_layout.width and ticker.s to a
|
|
# NAME_MAX_LENGTH stack buffer, so a playlist entry with a
|
|
# long enough Japanese, Cyrillic or emoji title overran it.
|
|
# Fixed by giving the struct an s_len field carrying the
|
|
# destination size in bytes, mirroring the dst_str_len that
|
|
# gfx_animation_ticker_smooth() had all along.
|
|
#
|
|
# AddressSanitizer is the validator: the test allocates
|
|
# every ticker destination at exactly the length it hands
|
|
# over, so a single byte past the end is a report rather
|
|
# than a stomp on whatever followed. It also sweeps every
|
|
# easing type through push/update/expire, frees animation
|
|
# subjects while their animation is in flight, and deinits
|
|
# with animations still running. If gfx_animation.c amends
|
|
# the destination sizing, this goes red.
|
|
make clean all SANITIZER=address,undefined
|
|
test -x gfx_animation_sanitize_test
|
|
ASAN_OPTIONS=detect_leaks=1 timeout 120 \
|
|
./gfx_animation_sanitize_test
|
|
echo "[pass] gfx_animation_sanitize_test"
|
|
|
|
|
|
- name: Build and run gfx_widgets_sanitize_test (ASan + UBSan)
|
|
shell: bash
|
|
working-directory: samples/gfx/gfx_widgets_sanitize
|
|
run: |
|
|
set -eu
|
|
# Sanitizer sweep of gfx/gfx_widgets.c, focused on the
|
|
# message queue: it has the same shape as the
|
|
# gfx_animation ticker overflow -- a caller-supplied
|
|
# string, internal fixed buffers, and arithmetic that has
|
|
# to keep glyphs and bytes apart. Multi-byte text,
|
|
# over-long text, text with no break opportunity, embedded
|
|
# newlines and empty strings across every message/title
|
|
# pairing, a 256-message overflow to drive eviction and
|
|
# free, and eight init/deinit cycles with messages still
|
|
# outstanding at teardown.
|
|
#
|
|
# Like the thumbnail sweep this pins no historical defect
|
|
# and is a tripwire. Two things decide whether it sweeps
|
|
# anything: the font metric stubs return a non-zero
|
|
# proportional width (a zero-width font makes every "does
|
|
# this fit" test succeed and the layout paths are never
|
|
# entered), and gfx_widgets_init() returning false makes
|
|
# the whole run vacuous -- hence the asserted push and
|
|
# frame counters.
|
|
make clean all SANITIZER=address,undefined
|
|
test -x gfx_widgets_sanitize_test
|
|
ASAN_OPTIONS=detect_leaks=1 timeout 120 \
|
|
./gfx_widgets_sanitize_test
|
|
echo "[pass] gfx_widgets_sanitize_test (ASan)"
|
|
make clean all SANITIZER=thread
|
|
TSAN_OPTIONS=halt_on_error=1 timeout 120 \
|
|
./gfx_widgets_sanitize_test
|
|
echo "[pass] gfx_widgets_sanitize_test (TSan)"
|
|
- name: Build and run gfx_widgets_task_bar_stack_test (ASan + UBSan, TSan)
|
|
shell: bash
|
|
working-directory: samples/gfx/gfx_widgets_task_bar_stack
|
|
run: |
|
|
set -eu
|
|
make clean all SANITIZER=address,undefined
|
|
test -x gfx_widgets_task_bar_stack_test
|
|
ASAN_OPTIONS=detect_leaks=1 timeout 120 \
|
|
./gfx_widgets_task_bar_stack_test
|
|
echo "[pass] gfx_widgets_task_bar_stack_test (ASan)"
|
|
make clean all SANITIZER=thread
|
|
TSAN_OPTIONS=halt_on_error=1 timeout 120 \
|
|
./gfx_widgets_task_bar_stack_test
|
|
echo "[pass] gfx_widgets_task_bar_stack_test (TSan)"
|
|
- name: Build and run display_servers_test (ASan + UBSan)
|
|
shell: bash
|
|
working-directory: samples/gfx/display_servers
|
|
run: |
|
|
set -eu
|
|
# Regression test for the XRandR query handling in
|
|
# gfx/display_servers/dispserv_x11.c. Pre-fix every query
|
|
# was treated as infallible: XRRGetOutputInfo() returns NULL
|
|
# for an output the server cannot describe and
|
|
# info->connection was read straight off it; likewise
|
|
# XRRGetCrtcInfo() and crtc->width, XRRGetScreenResources(),
|
|
# and XRRGetScreenInfo() feeding a XRRFreeScreenConfigInfo()
|
|
# that is not NULL-tolerant. On any X server advertising
|
|
# RandR without a usable output -- Xvfb, a headless box, a
|
|
# display with everything disconnected -- RetroArch died
|
|
# inside video_display_server_init() before drawing
|
|
# anything. Both orientation walks also indexed
|
|
# screen->crtcs with the output's ncrtc, reading past the
|
|
# end and then configuring a crtc other than the one whose
|
|
# geometry had just been read.
|
|
#
|
|
# The test stubs libXrandr and the handful of Xlib entry
|
|
# points the driver reaches, so it needs the headers but no
|
|
# X server, no display and no RandR extension: what is under
|
|
# test is what happens when the queries FAIL, and a working
|
|
# display is exactly the environment that cannot produce
|
|
# that. The stub numbers screen crtcs from 100 and output
|
|
# crtcs from 200 so which array was walked is read off the
|
|
# call log, and tracks every pointer it hands out so a
|
|
# double free or leak is attributed to the case that caused
|
|
# it. If dispserv_x11.c amends the query handling, this
|
|
# goes red.
|
|
make clean all SANITIZER=address,undefined
|
|
test -x display_servers_test
|
|
timeout 60 ./display_servers_test
|
|
echo "[pass] display_servers_test"
|
|
|
|
- name: Build and run dispserv_gx_test (ASan + UBSan)
|
|
shell: bash
|
|
working-directory: samples/gfx/dispserv_gx
|
|
run: |
|
|
set -eu
|
|
# The GameCube/Wii display server (dispserv_gx.c and the mode
|
|
# rules in dispserv_gx_modes.c), linked as they ship against
|
|
# stubbed libogc queries, on every TV standard: the list holds
|
|
# only modes the standard shows, one of them current, and each
|
|
# entry - through the label the menu prints and the parse the
|
|
# menu does - selects exactly that mode, stores its config id
|
|
# and reports the refresh rate it listed.
|
|
#
|
|
# The menu's parse used to run strtoul() inside
|
|
# VIDEO_SCALE_PUT_H, whose PACK evaluates its argument twice;
|
|
# the second call returned 0 and every pick from the list
|
|
# went out as Wx0. Guard the shape as well as the behaviour.
|
|
if grep -nE 'VIDEO_SCALE_(PUT_[WH]|PACK)\([^;]*strto' \
|
|
../../../menu/cbs/menu_cbs_ok.c; then
|
|
echo "::error::strtoul inside a VIDEO_SCALE macro runs twice"
|
|
exit 1
|
|
fi
|
|
make clean all SANITIZER=address,undefined
|
|
ASAN_OPTIONS=detect_leaks=1 UBSAN_OPTIONS=print_stacktrace=1:halt_on_error=1 \
|
|
timeout 60 ./dispserv_gx_test
|
|
make clean
|
|
|
|
- name: Build and run dispserv_ps3_test (ASan + UBSan)
|
|
shell: bash
|
|
working-directory: samples/gfx/dispserv_ps3
|
|
run: |
|
|
set -eu
|
|
# The PS3 display server (dispserv_ps3.c and the mode rules in
|
|
# dispserv_ps3_modes.c), linked as they ship against stubbed
|
|
# PSL1GHT video output queries, for displays taking different
|
|
# sets of modes with the system menu in each: the list holds
|
|
# exactly what the display takes with the mode in use marked,
|
|
# each entry - through the menu's label and parse - selects
|
|
# that mode and reinitialises the driver once, and
|
|
# ps3_display_server_resolution(), which the RSX driver
|
|
# configures the output from, follows the choice (before, the
|
|
# RSX driver ignored the setting and kept the system menu's
|
|
# mode) and falls back to the system mode when the display
|
|
# does not take it.
|
|
make clean all SANITIZER=address,undefined
|
|
ASAN_OPTIONS=detect_leaks=1 UBSAN_OPTIONS=print_stacktrace=1:halt_on_error=1 \
|
|
timeout 60 ./dispserv_ps3_test
|
|
make clean
|
|
|
|
- name: X11 window poll sends no requests (Xvfb, ASan + UBSan)
|
|
shell: bash
|
|
run: |
|
|
set -eu
|
|
# x11_check_window() and x11_has_focus() run every frame; a
|
|
# reply-bearing request there waits behind whatever else is
|
|
# queued on the connection (a software Vulkan WSI's whole
|
|
# PutImage frame). Focus comes from FocusIn/FocusOut, so the
|
|
# X contexts must select FocusChangeMask.
|
|
for f in x_ctx.c x_vk_ctx.c xegl_ctx.c; do
|
|
grep -q FocusChangeMask gfx/drivers_context/$f \
|
|
|| { echo "::error::$f does not select FocusChangeMask"; exit 1; }
|
|
done
|
|
sudo apt-get install -y -qq xvfb > /dev/null
|
|
Xvfb :97 -screen 0 1024x768x24 > /tmp/xvfb97.log 2>&1 &
|
|
XVFB_PID=$!
|
|
sleep 1
|
|
(cd samples/gfx/x11_frame_poll \
|
|
&& make clean all SANITIZER=address,undefined \
|
|
&& DISPLAY=:97 ASAN_OPTIONS=detect_leaks=1 UBSAN_OPTIONS=print_stacktrace=1 \
|
|
timeout 60 ./x11_frame_poll_test)
|
|
kill $XVFB_PID
|
|
echo "[pass] x11_frame_poll_test"
|
|
|
|
- name: Live mode switching on an Xorg dummy server (X11 + SDL)
|
|
shell: bash
|
|
run: |
|
|
set -eu
|
|
# The stubbed display_servers_test proves the driver issues
|
|
# the right RandR calls; this proves a real X server accepts
|
|
# them. Xorg's 'dummy' driver honours XRRCreateMode /
|
|
# XRRAddOutputMode / XRRSetCrtcConfig with arbitrary timings
|
|
# (Xvfb's RandR shim returns success and lists nothing, so it
|
|
# cannot stand in). The X11 sample runs the modeline engine
|
|
# end to end - generate a 15 kHz timing, put it on the wire
|
|
# through dispserv_x11, read the crtc back and compare every
|
|
# field, switch again, restore - then the Screen Resolution
|
|
# callbacks: the listed modes, a smaller, a larger and a
|
|
# rate-only switch with the framebuffer following, an
|
|
# unlisted size refused, and back. The SDL sample switches
|
|
# a real SDL2 window among the listed modes through
|
|
# dispserv_sdl2 (and dispserv_sdl3 on an SDL3 build), for the
|
|
# modeline path and the menu picker.
|
|
# The Win32 build runs after them under Wine.
|
|
sudo Xorg :99 -config samples/gfx/display_servers_x11_live/xorg-dummy.conf \
|
|
-logfile /tmp/xorg-dummy.log -noreset -nolisten tcp -ac \
|
|
> /tmp/xorg-dummy.out 2>&1 &
|
|
XORG_PID=$!
|
|
for i in $(seq 1 30); do
|
|
DISPLAY=:99 xrandr >/dev/null 2>&1 && break
|
|
sleep 0.5
|
|
done
|
|
DISPLAY=:99 xrandr | head -3
|
|
export DISPLAY=:99
|
|
# SDL3's X11 backend makes a window fullscreen through the
|
|
# window manager and reverts the mode switch when none
|
|
# answers; a light one runs for the SDL samples.
|
|
openbox > /tmp/openbox.out 2>&1 &
|
|
sleep 1
|
|
(cd samples/gfx/display_servers_x11_live \
|
|
&& make clean all SANITIZER=address,undefined \
|
|
&& ASAN_OPTIONS=detect_leaks=1 UBSAN_OPTIONS=print_stacktrace=1 \
|
|
timeout 120 ./display_servers_x11_live_test)
|
|
echo "[pass] display_servers_x11_live_test"
|
|
# The refresh rate a core polls every frame: answered from
|
|
# memory once the X event pump drains the connection, with no
|
|
# request to the server, and changed by the next pump after
|
|
# another client switches the mode - under a reader on a
|
|
# second thread, once under ASan+UBSan and once under TSan.
|
|
(cd samples/gfx/x11_refresh_rate \
|
|
&& make clean all SANITIZER=address,undefined \
|
|
&& ASAN_OPTIONS=detect_leaks=1 UBSAN_OPTIONS=print_stacktrace=1:halt_on_error=1 \
|
|
timeout 120 ./x11_refresh_rate_test \
|
|
&& make clean all SANITIZER=thread \
|
|
&& TSAN_OPTIONS=halt_on_error=1 timeout 120 ./x11_refresh_rate_test)
|
|
echo "[pass] x11_refresh_rate_test"
|
|
(cd samples/gfx/display_servers_sdl2_live \
|
|
&& make clean all SANITIZER=address,undefined \
|
|
&& ASAN_OPTIONS=detect_leaks=1 UBSAN_OPTIONS=print_stacktrace=1 \
|
|
LSAN_OPTIONS=suppressions=lsan.supp \
|
|
timeout 120 ./display_servers_sdl2_live_test)
|
|
echo "[pass] display_servers_sdl2_live_test"
|
|
# The SDL3 server against an SDL3 built from a pinned release
|
|
# (the runner image ships no libsdl3-dev): video and events
|
|
# only, X11 backend.
|
|
SDL3_VER=3.2.20
|
|
if [ ! -f /opt/sdl3/lib/pkgconfig/sdl3.pc ]; then
|
|
curl -sL -o /tmp/sdl3.tar.gz \
|
|
"https://github.com/libsdl-org/SDL/releases/download/release-$SDL3_VER/SDL3-$SDL3_VER.tar.gz"
|
|
tar -C /tmp -xzf /tmp/sdl3.tar.gz
|
|
cmake -S "/tmp/SDL3-$SDL3_VER" -B /tmp/sdl3-build -G Ninja \
|
|
-DCMAKE_BUILD_TYPE=Release -DSDL_SHARED=ON -DSDL_STATIC=OFF \
|
|
-DSDL_TESTS=OFF -DSDL_EXAMPLES=OFF -DSDL_AUDIO=OFF -DSDL_CAMERA=OFF \
|
|
-DSDL_WAYLAND=OFF -DSDL_X11=ON -DSDL_OPENGL=OFF -DSDL_OPENGLES=OFF \
|
|
-DSDL_VULKAN=OFF -DSDL_RENDER=OFF -DCMAKE_INSTALL_PREFIX=/opt/sdl3 \
|
|
> /tmp/sdl3-cmake.log
|
|
ninja -C /tmp/sdl3-build > /tmp/sdl3-build.log
|
|
sudo ninja -C /tmp/sdl3-build install > /tmp/sdl3-install.log
|
|
fi
|
|
(cd samples/gfx/display_servers_sdl3_live \
|
|
&& PKG_CONFIG_PATH=/opt/sdl3/lib/pkgconfig make clean all SANITIZER=address,undefined \
|
|
&& LD_LIBRARY_PATH=/opt/sdl3/lib \
|
|
ASAN_OPTIONS=detect_leaks=1 UBSAN_OPTIONS=print_stacktrace=1 \
|
|
LSAN_OPTIONS=suppressions=lsan.supp \
|
|
timeout 120 ./display_servers_sdl3_live_test)
|
|
echo "[pass] display_servers_sdl3_live_test"
|
|
# The Win32 server, its vendor timing paths and the engine
|
|
# cross-built with mingw-w64 and run under Wine on the same
|
|
# display: Wine backs EnumDisplaySettingsExA and
|
|
# ChangeDisplaySettingsExA with the server's RandR modes, so
|
|
# the CDS listed-mode switch is real; ADL, ATI legacy and
|
|
# PowerStrip are linked and must decline.
|
|
(cd samples/gfx/display_servers_win32_live \
|
|
&& make clean all \
|
|
&& WINEDEBUG=-all timeout 300 wine ./display_servers_win32_live_test.exe)
|
|
echo "[pass] display_servers_win32_live_test (Wine)"
|
|
# The vendor timing paths against stand-ins: a mock
|
|
# atiadlxx.dll that re-plugs the monitor after a forced set,
|
|
# a scratch HKLM key for the ATI legacy registry layout, a
|
|
# TPShidden window answering PowerStrip's messages.
|
|
(cd samples/gfx/display_servers_win32_vendor \
|
|
&& make clean all \
|
|
&& WINEDEBUG=-all timeout 300 wine ./display_servers_win32_vendor_test.exe)
|
|
echo "[pass] display_servers_win32_vendor_test (Wine)"
|
|
# The server must be back on its desktop mode with nothing
|
|
# generated left behind.
|
|
xrandr | head -3
|
|
if xrandr | grep -q 'RA-'; then
|
|
echo "generated modes left on the server"; exit 1
|
|
fi
|
|
pkill openbox || true
|
|
sudo kill "$XORG_PID" || true
|
|
|
|
- name: Screen Resolution through Mutter's DisplayConfig (ASan + UBSan)
|
|
shell: bash
|
|
run: |
|
|
set -eu
|
|
# Under GNOME's Wayland session the X11 driver runs on
|
|
# XWayland, which lists every mode at the desktop's current
|
|
# rate and only scales the window when "switched", and the
|
|
# Wayland driver had no resolution list at all; Mutter's
|
|
# org.gnome.Mutter.DisplayConfig D-Bus interface is the only
|
|
# way to the monitor's real modes. mock_mutter.py carries
|
|
# Mutter's signatures and its ApplyMonitorsConfig checks; the
|
|
# test drives gfx/common/mutter_displayconfig.c directly and
|
|
# through dispserv_x11 (real Xorg dummy server, and Xwayland
|
|
# under a headless Weston) and dispserv_wl, with and without
|
|
# Mutter on the bus. Without it every path must behave
|
|
# exactly as before: no session bus, a bus without Mutter, a
|
|
# real X server (Mutter is never asked), XWayland's own list,
|
|
# and no Wayland resolution list. Last, a real headless Mutter
|
|
# and its XWayland: Mutter names XWayland's outputs after its
|
|
# connectors (DP-1, Meta-0), so only the XWAYLAND extension
|
|
# says what the server is.
|
|
sudo apt-get install -y -qq libdbus-1-dev dbus python3-dbus \
|
|
python3-gi weston xwayland mutter > /dev/null
|
|
(cd samples/gfx/mutter_displayconfig \
|
|
&& make clean all SANITIZER=address,undefined \
|
|
&& ASAN_OPTIONS=detect_leaks=1 \
|
|
UBSAN_OPTIONS=print_stacktrace=1:halt_on_error=1 \
|
|
XORG="sudo Xorg" timeout 300 ./run.sh)
|
|
echo "[pass] mutter_displayconfig_test"
|
|
|
|
- name: D3D11 hardware ring under threaded video (mingw + Wine, headless)
|
|
shell: bash
|
|
run: |
|
|
set -o pipefail
|
|
# The threaded wrapper hands a D3D11 hardware core a deferred
|
|
# context and replays its frames on the video thread. Nothing
|
|
# on Linux exercises that, and it shipped showing one frame
|
|
# and then black. The real proxy and the driver's own
|
|
# FinishCommandList argument, run under Wine, where wined3d
|
|
# implements deferred contexts over OpenGL. Requires the ring
|
|
# and the immediate context to show every frame, and the ring
|
|
# as it used to be to fail.
|
|
sudo apt-get install -y -qq xvfb > /dev/null
|
|
samples/gfx/d3d11_hw_ring/run.sh
|
|
|
|
- name: libretro_d3d11.h version 2, the contract run (mingw + Wine, headless)
|
|
shell: bash
|
|
run: |
|
|
set -o pipefail
|
|
# Version 2 of the D3D11 hardware render interface lets a core
|
|
# and a frontend that presents from another thread share the
|
|
# device's one immediate context by taking turns, so the core
|
|
# keeps everything a deferred context cannot do - reading back
|
|
# above all. This runs the two against each other on a real
|
|
# D3D11 device (wined3d), and requires every frame and every
|
|
# readback to be right, and a core that ignores what
|
|
# lock_context tells it to be caught.
|
|
sudo apt-get install -y -qq xvfb > /dev/null
|
|
REQUIRE_D3D11=1 samples/gfx/d3d11_hw_interface_v2/run.sh
|
|
|
|
- name: libretro_d3d12.h version 2, the contract run (mingw + Wine + vkd3d, headless)
|
|
shell: bash
|
|
run: |
|
|
set -o pipefail
|
|
# Version 2 of the D3D12 hardware render interface lets a core
|
|
# hand a frame to a frontend that presents from another thread
|
|
# without the copy version 1 forces. This runs a core and such a
|
|
# frontend against each other on a real D3D12 device - vkd3d
|
|
# over Mesa's software Vulkan driver - and requires every frame
|
|
# to arrive intact, and a core that ignores wait_sync_index to
|
|
# be caught.
|
|
sudo apt-get install -y -qq xvfb libvkd3d1 mesa-vulkan-drivers > /dev/null
|
|
REQUIRE_D3D12=1 samples/gfx/d3d12_hw_interface_v2/run.sh
|
|
|
|
- name: Build and run display_servers_kms_test (ASan + UBSan)
|
|
shell: bash
|
|
working-directory: samples/gfx/display_servers_kms
|
|
run: |
|
|
set -eu
|
|
# Regression test for two defects in
|
|
# gfx/display_servers/dispserv_kms.c::
|
|
# kms_display_server_get_resolution_list(). g_drm_connector
|
|
# is owned by gfx/common/drm_common.c and set to NULL by
|
|
# drm_free(), which runs on every context teardown -- a
|
|
# resolution change, a fullscreen toggle, a driver reinit --
|
|
# while the display server object outlives it, so the menu
|
|
# could ask for the list with no connector to describe and
|
|
# the function read straight through NULL. g_drm_mode, the
|
|
# sibling global, was already tested for exactly this a few
|
|
# lines earlier. Separately the qsort() at the end was
|
|
# passed `count`, which was declared zero and never
|
|
# assigned; `j` is the element count. So the sort ran over
|
|
# zero elements and the resolution list came back in
|
|
# whatever order the connector reported its modes -- no
|
|
# crash, no diagnostic, just an unsorted list that looks
|
|
# plausible.
|
|
#
|
|
# The test fabricates the drmModeConnector the driver reads,
|
|
# so it needs the libdrm headers but links nothing from
|
|
# libdrm: presenting a connector that is absent, empty, or
|
|
# reporting modes in an awkward order is the whole exercise
|
|
# and a real one will not do it on request. The ordering
|
|
# check feeds modes in descending size, which a working sort
|
|
# must reverse and a broken one leaves untouched. If
|
|
# dispserv_kms.c amends either, this goes red.
|
|
make clean all SANITIZER=address,undefined
|
|
test -x display_servers_kms_test
|
|
timeout 60 ./display_servers_kms_test
|
|
echo "[pass] display_servers_kms_test"
|
|
|
|
- name: Build and run drm_hdr_test (ASan + UBSan)
|
|
shell: bash
|
|
working-directory: samples/gfx/drm_hdr
|
|
run: |
|
|
set -eu
|
|
# gfx/common/drm_hdr.c: which EDIDs take HDR10 and what they
|
|
# declare, malformed ones refused without an overread, and the
|
|
# HDR_OUTPUT_METADATA blob, whose layout must be the kernel's.
|
|
make clean all SANITIZER=address,undefined
|
|
ASAN_OPTIONS=detect_leaks=1 UBSAN_OPTIONS=print_stacktrace=1:halt_on_error=1 \
|
|
timeout 60 ./drm_hdr_test
|
|
echo "[pass] drm_hdr_test"
|
|
|
|
- name: Build and run wayland_wlr_output_test (ASan + UBSan)
|
|
shell: bash
|
|
working-directory: samples/gfx/wayland_wlr_output
|
|
run: |
|
|
set -eu
|
|
# gfx/common/wayland_wlr_output.c, the Wayland display server's
|
|
# resolution list and mode switch on wlroots compositors, against
|
|
# an in-process compositor that checks every configuration names
|
|
# each head exactly once, as the protocol requires.
|
|
make clean all SANITIZER=address,undefined
|
|
ASAN_OPTIONS=detect_leaks=1 UBSAN_OPTIONS=print_stacktrace=1:halt_on_error=1 \
|
|
timeout 60 ./wayland_wlr_output_test
|
|
echo "[pass] wayland_wlr_output_test"
|
|
|
|
- name: Build and run wayland_kwin_output_test (ASan + UBSan)
|
|
shell: bash
|
|
working-directory: samples/gfx/wayland_kwin_output
|
|
run: |
|
|
set -eu
|
|
# gfx/common/wayland_kwin_output.c, the Wayland display server's
|
|
# resolution list and mode switch on KWin, against an in-process
|
|
# compositor speaking KWin's output protocols in both the
|
|
# registry form and the older per-output globals.
|
|
make clean all SANITIZER=address,undefined
|
|
ASAN_OPTIONS=detect_leaks=1 UBSAN_OPTIONS=print_stacktrace=1:halt_on_error=1 \
|
|
timeout 60 ./wayland_kwin_output_test
|
|
echo "[pass] wayland_kwin_output_test"
|
|
|
|
- name: Build and run wayland_color_management_test (ASan + UBSan)
|
|
shell: bash
|
|
working-directory: samples/gfx/wayland_color_management
|
|
run: |
|
|
set -eu
|
|
# gfx/common/wayland_color.c, which tags an HDR GL surface as
|
|
# Windows-scRGB, against an in-process compositor that records
|
|
# every fatal wp_color_manager_v1 rule the client could break.
|
|
make clean all SANITIZER=address,undefined
|
|
ASAN_OPTIONS=detect_leaks=1 UBSAN_OPTIONS=print_stacktrace=1:halt_on_error=1 \
|
|
timeout 60 ./wayland_color_management_test
|
|
echo "[pass] wayland_color_management_test"
|
|
|
|
- name: Build and run wayland_drm_lease_test (ASan + UBSan)
|
|
shell: bash
|
|
working-directory: samples/gfx/wayland_drm_lease
|
|
run: |
|
|
set -eu
|
|
# The DRM lease path in gfx/common/wayland_drm_lease.c and
|
|
# gfx/display_servers/dispserv_wl.c, driven by a test
|
|
# compositor on libwayland-server: what is offered, which
|
|
# connector an index picks, an index past the offer, a
|
|
# refusal, a revoke arriving while the client is idle, and
|
|
# no Wayland session at all. The leased descriptor and the
|
|
# device's non-master fd are the ones a mistake leaks, so
|
|
# the lane runs under ASan. Both protocol halves are
|
|
# generated here from deps/wayland-protocols, which needs
|
|
# wayland-scanner and the client and server libraries -
|
|
# libwayland-dev carries all three.
|
|
make clean all SANITIZER=address,undefined
|
|
test -x wayland_drm_lease_test
|
|
ASAN_OPTIONS=detect_leaks=1 UBSAN_OPTIONS=print_stacktrace=1 \
|
|
timeout 60 ./wayland_drm_lease_test
|
|
echo "[pass] wayland_drm_lease_test"
|
|
|
|
- name: Build and run modeline_edid_parse_test (ASan + UBSan)
|
|
shell: bash
|
|
working-directory: samples/gfx/modeline_edid_parse
|
|
run: |
|
|
set -eu
|
|
# The EDID reader in gfx/modeline/modeline_edid.c, fed the
|
|
# block the writer produces (round trip), a hand-built EDID
|
|
# 1.4 with CTA-861 and DisplayID 2.0 extensions checked
|
|
# field by field, the defect cases (short, wrong header, bad
|
|
# checksums, a declared but missing extension, data block
|
|
# and section lengths running past their block), and a
|
|
# deterministic byte-noise sweep. The parser is handed
|
|
# whatever an OS reader returns, so every length and offset
|
|
# in the data is hostile; under ASan a read past the buffer
|
|
# is the failure this lane exists to catch.
|
|
make clean all SANITIZER=address,undefined
|
|
test -x modeline_edid_parse_test
|
|
ASAN_OPTIONS=detect_leaks=1 UBSAN_OPTIONS=print_stacktrace=1 \
|
|
timeout 60 ./modeline_edid_parse_test
|
|
|
|
- name: Build and run integer_scale_axis_test (ASan + UBSan)
|
|
shell: bash
|
|
working-directory: samples/gfx/integer_scale_axis
|
|
run: |
|
|
set -eu
|
|
# video_viewport_get_scaled_integer(), pulled out of
|
|
# gfx/video_driver.c by extract_integer.awk, swept over
|
|
# viewport sizes, frame sizes, every axis mode that touches
|
|
# X and every scaling mode. Y+X has to be integer on both
|
|
# axes: the X axis was once multiplied from the
|
|
# aspect-corrected width (256x224 at 64:49 is 293), giving
|
|
# 1172x896 at 1080p - a 4.58x horizontal scale and uneven
|
|
# pixel columns - on every video driver.
|
|
make clean all SANITIZER=address,undefined
|
|
test -x integer_scale_axis_test
|
|
ASAN_OPTIONS=detect_leaks=1 UBSAN_OPTIONS=print_stacktrace=1 \
|
|
timeout 60 ./integer_scale_axis_test
|
|
make clean
|
|
|
|
- name: Build and run font_lifecycle_test (ASan + UBSan)
|
|
shell: bash
|
|
working-directory: samples/gfx/font_lifecycle
|
|
run: |
|
|
set -eu
|
|
# Where a font is created follows the threading hint; where
|
|
# it is REBUILT does not - a rebuild marshals whenever the
|
|
# wrapper is up, because a GL backend making its context
|
|
# current on the main thread while the video thread holds it
|
|
# is an X BadAccess. Nothing ran this sample, and it had the
|
|
# older rule written into it.
|
|
make clean all SANITIZER=address,undefined
|
|
ASAN_OPTIONS=detect_leaks=1:allocator_may_return_null=1 \
|
|
UBSAN_OPTIONS=print_stacktrace=1 timeout 120 make check SANITIZER=address,undefined
|
|
make clean
|
|
|
|
- name: Build and run overlay_apng_test (ASan + UBSan, TSan)
|
|
shell: bash
|
|
working-directory: samples/gfx/overlay_apng
|
|
run: |
|
|
set -eu
|
|
# An animated overlay image is a surface with slots whose
|
|
# texture is updated in place, so the pages' handles stand
|
|
# while its frames advance. The sample builds its own APNG
|
|
# fixture and holds that contract: distinct frames, a pass
|
|
# that ends and replays, one texture across the submits, and
|
|
# a free with a frame still in flight.
|
|
make clean all SANITIZER=address,undefined
|
|
ASAN_OPTIONS=detect_leaks=1:allocator_may_return_null=1 \
|
|
UBSAN_OPTIONS=print_stacktrace=1 timeout 120 ./overlay_apng_test fixtures/anim.png
|
|
make clean all SANITIZER=thread
|
|
TSAN_OPTIONS=halt_on_error=1 timeout 120 ./overlay_apng_test fixtures/anim.png
|
|
make clean
|
|
|
|
- name: Build and run gfx_thumbnail_anim_test (ASan + UBSan, TSan)
|
|
shell: bash
|
|
working-directory: samples/gfx/gfx_thumbnail_anim
|
|
run: |
|
|
set -eu
|
|
# Regression test for the two faults that froze animated
|
|
# thumbnails in the file browser, in gfx/gfx_thumbnail.c.
|
|
#
|
|
# gfx_thumbnail_request_file malloc'd its tag and never set
|
|
# tag->path, so gfx_thumbnail_anim_open - which
|
|
# gfx_thumbnail_handle_upload feeds that field for every
|
|
# load with no video stream to adopt - read uninitialised
|
|
# heap and returned without touching the file. Undefined
|
|
# behaviour rather than a stable failure: a freed tag is
|
|
# the size of the next one, so the allocator commonly
|
|
# handed back a block still holding the previous entry's
|
|
# path, which opened an animation and looked like the
|
|
# feature working. gfx_thumbnail_request fills the field
|
|
# for playlist entries, which is why playlists animated and
|
|
# the browser did not.
|
|
#
|
|
# Separately, anim_install marked windowed animations
|
|
# 'read pending' via !data_transfer_complete(). A window
|
|
# never completes - done stays clear for its whole life -
|
|
# and the pump animate() uses (iterate_while) declines
|
|
# windows by design, so pending forced the pump branch, the
|
|
# pump was a no-op, and animate() returned before ever
|
|
# advancing a frame, every vsync, forever. That is every
|
|
# path-based open: animated WEBP, APNG, and any WEBM/MP4
|
|
# with no still stream to adopt.
|
|
#
|
|
# The oracle links the real gfx_thumbnail.c and measures
|
|
# animation as DISTINCT FRAMES REACHING THE TEXTURE - the
|
|
# video-driver stub CRCs every upload - not as internal
|
|
# flags, which is what every earlier test in this area got
|
|
# wrong and why neither fault was caught. A 188-byte
|
|
# 3-frame lossless WEBP is embedded, so there are no
|
|
# fixtures. One lane drives anim_open with a garbage path
|
|
# and requires no stream; one opens the real animation and
|
|
# requires multiple distinct uploads within bounded paced
|
|
# vsyncs, naming the windowed+pending deadlock explicitly
|
|
# before the timeout would. Both pre-fix trees score zero
|
|
# uploads in 240 vsyncs. If gfx_thumbnail.c amends the
|
|
# pending decision or the request plumbing, this goes red.
|
|
make clean all SANITIZER=address,undefined
|
|
test -x gfx_thumbnail_anim_test
|
|
ASAN_OPTIONS=detect_leaks=1 timeout 120 \
|
|
./gfx_thumbnail_anim_test
|
|
echo "[pass] gfx_thumbnail_anim_test (ASan)"
|
|
make clean all SANITIZER=thread
|
|
TSAN_OPTIONS=halt_on_error=1 timeout 120 \
|
|
./gfx_thumbnail_anim_test
|
|
echo "[pass] gfx_thumbnail_anim_test (TSan)"
|
|
|
|
- name: Build and run badge_cache_test (ASan + UBSan)
|
|
shell: bash
|
|
working-directory: samples/cheevos/badge_cache
|
|
run: |
|
|
set -eu
|
|
# rcheevos_get_badge_texture() never reads, decodes or
|
|
# uploads on the calling thread, so the achievement widgets
|
|
# can ask from the video thread. The test links the real
|
|
# cheevos/cheevos_badge.c with the task and upload stubbed
|
|
# as steppable queues and checks: nothing synchronous on
|
|
# the first ask, one decode per badge, the upload posted
|
|
# only from the decode's completion, the handle handed out
|
|
# exactly once and owned by the taker, locked and unlocked
|
|
# as separate keys, a missing file requesting a download
|
|
# only when asked and loading when the download lands, a
|
|
# failed load not retried per ask, a reset unloading ready
|
|
# handles and orphaning in-flight ones (their late delivery
|
|
# unloaded, never handed to whatever reused the slot), an
|
|
# off-main-thread ask only recorded until the main thread's
|
|
# service starts it, the default badge lent rather than
|
|
# given, a 0 reported as a short wait only while a file on
|
|
# disk is loading, and a refused async upload treated as a
|
|
# failure.
|
|
make clean all SANITIZER=address,undefined
|
|
test -x badge_cache_test
|
|
ASAN_OPTIONS=detect_leaks=1 timeout 60 ./badge_cache_test
|
|
echo "[pass] badge_cache_test (ASan)"
|
|
make clean
|
|
|
|
- name: Build and run psp1_gfx_contracts_test (ASan + UBSan)
|
|
shell: bash
|
|
working-directory: samples/gfx/psp1_gfx_contracts
|
|
run: |
|
|
set -eu
|
|
# gfx/drivers/psp1_gfx.c is reached only by the griffin unity
|
|
# build for PSP, so nothing else here compiles it, let alone
|
|
# runs it. This links the real driver against a stand-in for
|
|
# the parts of the machine its correctness turns on: main RAM
|
|
# and VRAM each mapped twice so the cached and uncached
|
|
# aliases genuinely alias, a no-access page after VRAM so a
|
|
# blit off the end faults, display lists that stay the GE's
|
|
# until they are waited on, and a vblank sub-interrupt that
|
|
# can be fired from inside free().
|
|
#
|
|
# What it holds: the 24bpp BGR byte order video_driver.h
|
|
# specifies for read_viewport, filling the whole buffer the
|
|
# screenshot task hands over, releasing the interrupt handler
|
|
# before the driver struct it writes through, keeping the GE
|
|
# off a display list or framebuffer that is in use, both blits
|
|
# staying inside the buffers they target, cache-line ownership
|
|
# of everything the GE writes, and init failing cleanly when
|
|
# an allocation does.
|
|
make clean all SANITIZER=address,undefined
|
|
rc=0
|
|
ASAN_OPTIONS=detect_leaks=1 UBSAN_OPTIONS=print_stacktrace=1 \
|
|
timeout 120 ./psp1_gfx_contracts_test || rc=$?
|
|
# 77: the runner would not hand over the PSP address windows.
|
|
if [ "$rc" = 77 ]; then
|
|
echo "[skip] PSP address windows unavailable"
|
|
exit 0
|
|
fi
|
|
[ "$rc" = 0 ]
|
|
make clean
|
|
|
|
- name: Build and run gfx_thumbnail_preview_test (ASan + UBSan, TSan)
|
|
shell: bash
|
|
working-directory: samples/gfx/gfx_thumbnail_preview
|
|
run: |
|
|
set -eu
|
|
# Regression test for the file-browser video preview in
|
|
# gfx/gfx_thumbnail.c, covering the windowed open, the
|
|
# anim-first status/alpha handshake, and the preview-audio
|
|
# hand-off to libretro-common/audio/audio_mixer.c.
|
|
#
|
|
# Every fault this pins was invisible to a decode-level
|
|
# test, which is why they shipped one after another:
|
|
#
|
|
# - the windowed open produced a texture and reached
|
|
# AVAILABLE but nothing raised alpha, so every video
|
|
# decoded perfectly and drew at zero opacity;
|
|
# - a LEADING moov left the demuxer's avail below the
|
|
# media floor, so the first sample was unreachable, and
|
|
# since the feeder anchors at tell==0 nothing ever
|
|
# raised the bound - a standstill with no way out;
|
|
# - a TRAILING moov cannot be reached by a resident head
|
|
# at all, so preview audio was silent on every such
|
|
# file until the moov island was committed instead;
|
|
# - the mixer hand-off copied the whole container out of
|
|
# a partially-committed window (SIGSEGV), took an
|
|
# int32_t length that truncated a 7 GB container to
|
|
# 3.5 GB, dropped every feeder raise for want of an
|
|
# M4A arm in audio_mixer_voice_set_avail (the same
|
|
# second of audio on a loop), and applied the resident
|
|
# bound only under HAVE_ROPUS.
|
|
#
|
|
# So this links the REAL gfx_thumbnail.c AND the REAL
|
|
# audio_mixer.c. The earlier harness stubbed the mixer and
|
|
# was therefore modelling the two functions three of those
|
|
# defects lived in. The stubbed audio thread consumes at a
|
|
# settable rate and TOUCHES every byte it is told is
|
|
# resident, so an over-claimed avail faults under ASan
|
|
# rather than passing quietly, and it counts feeder stalls
|
|
# (excluding the file end, where hitting the wall is the
|
|
# loop point rather than a starved feeder).
|
|
#
|
|
# The fixtures are container LAYOUTS, not clips - where the
|
|
# moov sits, whether media starts past 4 GiB, whether the
|
|
# moov outgrows the head - because layout is what every one
|
|
# of these faults turned on. The multi-gigabyte ones are
|
|
# sparse: two 7.28 GB files cost ~400 KB of disk.
|
|
#
|
|
# The windowed-animation harness below decodes its two
|
|
# lossless fixtures three times each; they are sized just past
|
|
# what the feeder may keep resident (~40 MB and ~30 MB), and
|
|
# mkfixtures.py reuses an existing set, so the second
|
|
# "make fixtures" (TSan pass) costs nothing. ~55 s under
|
|
# ASan on one core.
|
|
# Frame counts are set from measurement, not taste. At 300
|
|
# ASan / 200 TSan frames this step took ~5.6 min on one
|
|
# core, against a job budget shared with a dozen others;
|
|
# 120 / 60 brings it to ~1.5 min and still crosses every
|
|
# window boundary the assertions rest on (the feeder moves
|
|
# at AUDIO_BYTES_PER_TICK, not per frame). The job timeout
|
|
# goes to 20 min to leave headroom for the slowest runner.
|
|
sudo apt-get install -y -o Acquire::Retries=3 -o Acquire::http::Timeout=30 ffmpeg
|
|
make clean all SANITIZER=address,undefined
|
|
make fixtures
|
|
test -x gfx_thumbnail_preview_test
|
|
ASAN_OPTIONS=detect_leaks=1:allocator_may_return_null=1 \
|
|
UBSAN_OPTIONS=print_stacktrace=1 \
|
|
AUDIO_BYTES_PER_TICK=65536 FRAMES=120 timeout 300 \
|
|
./gfx_thumbnail_preview_test fixtures/*.mp4 fixtures/anim_lossless.webp
|
|
echo "[pass] gfx_thumbnail_preview_test (ASan)"
|
|
# The same session with HAVE_THREADS: the thumbnail decodes on
|
|
# its worker and the pool, the app's own path, paced at a
|
|
# display's rate with a bound on the longest stall.
|
|
test -x gfx_thumbnail_preview_test_threaded
|
|
ASAN_OPTIONS=detect_leaks=1:allocator_may_return_null=1 \
|
|
UBSAN_OPTIONS=print_stacktrace=1 \
|
|
THREADS=4 FRAMES=180 PACE_HZ=60 PACE_MAX_STALL_MS=800 NOAUDIO=1 timeout 300 \
|
|
./gfx_thumbnail_preview_test_threaded fixtures/bframes_h264.mp4 fixtures/bframes_hevc.mp4
|
|
echo "[pass] gfx_thumbnail_preview_test_threaded (ASan, 4 threads)"
|
|
# The frame-indexed types (animated WEBP, APNG) through the
|
|
# same session, windowed: animated WEBP had no progressive
|
|
# open, so the session read the whole file into the
|
|
# reservation (for every still WEBP thumbnail too) and
|
|
# charged one window for it; APNG had the open but no byte
|
|
# cursor, so the feeder never moved past the 12 MB primed at
|
|
# open and a longer animation looped early forever. Mapping
|
|
# residency is measured with mincore, so the bound holds
|
|
# under the sanitizers. ASan+UBSan only: the harness is
|
|
# single-threaded (no TSan target), and the lossless decode
|
|
# of ~900 720p frames takes it past the job budget under
|
|
# TSan (>5 min) for no coverage.
|
|
test -x preview_anim_window_test
|
|
ASAN_OPTIONS=detect_leaks=1:allocator_may_return_null=1 \
|
|
# V1 in this run plays long_video.mp4 (60 s at 4 Mbit/s,
|
|
# 29 MB, generated by mkfixtures) end to end through the
|
|
# session and requires the resident bytes to stay inside
|
|
# the bitrate-sized window: measured 5.25 MiB peak against
|
|
# the 20 MiB the fixed 4+8+8 MiB constants held before.
|
|
UBSAN_OPTIONS=print_stacktrace=1 timeout 600 \
|
|
./preview_anim_window_test fixtures
|
|
echo "[pass] preview_anim_window_test (ASan)"
|
|
# The decode test is the missing half of the harness above:
|
|
# the REAL mixer -> audio_transfer -> rmp4 -> raac chain over
|
|
# a budget-fed window, at budgets small enough that the
|
|
# decoder reaches the resident frontier. The stubbed audio
|
|
# thread above models exactly the layer these faults lived
|
|
# in: no wall over the packets on the trailing-moov path
|
|
# (SIGSEGV inside raac_decode_frame the moment the decoder
|
|
# outran the feeder), demuxer bounds that refused to lower,
|
|
# a wall stall taken for end of stream (mid-file rewinds,
|
|
# then the voice released), and set_avail compiling to a
|
|
# no-op without HAVE_RWEBM - the _norwebm build exists for
|
|
# that last one.
|
|
test -x preview_audio_decode_test
|
|
test -x preview_audio_decode_test_norwebm
|
|
ASAN_OPTIONS=detect_leaks=1:allocator_may_return_null=1 \
|
|
UBSAN_OPTIONS=print_stacktrace=1 timeout 600 bash -c '
|
|
./preview_audio_decode_test fixtures/trailing_large.mp4 1024 island
|
|
./preview_audio_decode_test fixtures/trailing_large.mp4 262144 island
|
|
./preview_audio_decode_test_norwebm fixtures/trailing_large.mp4 1024 island
|
|
./preview_audio_decode_test_norwebm fixtures/trailing_large.mp4 262144 island'
|
|
echo "[pass] preview_audio_decode_test (ASan)"
|
|
# The threaded decode against the single-threaded one, frame
|
|
# for frame: the colour conversion in row bands on a pool,
|
|
# for the VP9 fixture (four tile columns) the columns decoded
|
|
# side by side, for the HEVC fixture (WPP) the CTB rows on a
|
|
# wavefront. Every frame must be byte-identical.
|
|
test -x preview_blit_bands_test
|
|
ASAN_OPTIONS=detect_leaks=1:allocator_may_return_null=1 \
|
|
UBSAN_OPTIONS=print_stacktrace=1 timeout 300 bash -c '
|
|
./preview_blit_bands_test fixtures/long_video.mp4
|
|
./preview_blit_bands_test fixtures/vp9_tiles.webm
|
|
./preview_blit_bands_test fixtures/hevc_wpp.mp4
|
|
./preview_blit_bands_test fixtures/bframes_hevc.mp4
|
|
./preview_blit_bands_test fixtures/hevc_nowpp.mp4 fixtures/mbaff_h264.mp4
|
|
RH265_PUBLISH_DELAY=200 ./preview_blit_bands_test fixtures/hevc_wpp.mp4 fixtures/bframes_hevc.mp4 fixtures/hevc_nowpp.mp4'
|
|
echo "[pass] preview_blit_bands_test (ASan)"
|
|
# Catching up: behind the clock the stream drops only what
|
|
# nothing references, and every picture still shown is the
|
|
# full decode's; the threaded build does the same with four
|
|
# pictures in flight, the drops taken at submit.
|
|
test -x preview_catchup_test && test -x preview_catchup_test_threaded
|
|
ASAN_OPTIONS=detect_leaks=1:allocator_may_return_null=1 \
|
|
UBSAN_OPTIONS=print_stacktrace=1 timeout 300 bash -c '
|
|
./preview_catchup_test fixtures/bframes_h264.mp4 fixtures/bframes_hevc.mp4
|
|
./preview_catchup_test_threaded fixtures/bframes_h264.mp4 fixtures/bframes_hevc.mp4'
|
|
echo "[pass] preview_catchup_test (ASan, one picture at a time and four in flight)"
|
|
# The audio chain against ffmpeg: the track's rate exact, the
|
|
# resampled one within a fraction of a percent, from the first
|
|
# half second.
|
|
test -x preview_audio_oracle_test
|
|
ASAN_OPTIONS=detect_leaks=1:allocator_may_return_null=1 \
|
|
UBSAN_OPTIONS=print_stacktrace=1 timeout 300 \
|
|
./preview_audio_oracle_test fixtures/trailing_small.mp4 fixtures/trailing_large.mp4
|
|
echo "[pass] preview_audio_oracle_test (ASan)"
|
|
# clean keeps fixtures/ by design; regenerate anyway so the
|
|
# step cannot silently run against an empty glob.
|
|
make clean all SANITIZER=thread
|
|
make fixtures
|
|
TSAN_OPTIONS=halt_on_error=1:allocator_may_return_null=1 FRAMES=60 timeout 300 \
|
|
./gfx_thumbnail_preview_test fixtures/*.mp4 fixtures/anim_lossless.webp
|
|
echo "[pass] gfx_thumbnail_preview_test (TSan)"
|
|
# and threaded under TSan, which is the run that can see a
|
|
# race between the worker, the pool and the poll
|
|
THREADS=4 FRAMES=180 PACE_HZ=60 PACE_MAX_STALL_MS=1500 NOAUDIO=1 timeout 300 \
|
|
./gfx_thumbnail_preview_test_threaded fixtures/bframes_h264.mp4 fixtures/bframes_hevc.mp4
|
|
echo "[pass] gfx_thumbnail_preview_test_threaded (TSan, 4 threads)"
|
|
# The pool and the tile-column threads under TSan: the
|
|
# shared frame buffers and contexts are written in disjoint
|
|
# columns, the counts merged after the join.
|
|
TSAN_OPTIONS=halt_on_error=1:allocator_may_return_null=1 timeout 300 bash -c '
|
|
./preview_blit_bands_test fixtures/long_video.mp4
|
|
./preview_blit_bands_test fixtures/vp9_tiles.webm
|
|
./preview_blit_bands_test fixtures/hevc_wpp.mp4
|
|
./preview_blit_bands_test fixtures/bframes_hevc.mp4
|
|
./preview_blit_bands_test fixtures/hevc_nowpp.mp4 fixtures/mbaff_h264.mp4
|
|
RH265_PUBLISH_DELAY=200 ./preview_blit_bands_test fixtures/hevc_wpp.mp4 fixtures/bframes_hevc.mp4 fixtures/hevc_nowpp.mp4'
|
|
echo "[pass] preview_blit_bands_test (TSan)"
|
|
TSAN_OPTIONS=halt_on_error=1:allocator_may_return_null=1 timeout 300 bash -c '
|
|
./preview_catchup_test_threaded fixtures/bframes_h264.mp4 fixtures/bframes_hevc.mp4'
|
|
echo "[pass] preview_catchup_test_threaded (TSan)"
|
|
TSAN_OPTIONS=halt_on_error=1 timeout 600 bash -c '
|
|
./preview_audio_decode_test fixtures/trailing_large.mp4 1024 island
|
|
./preview_audio_decode_test fixtures/trailing_large.mp4 262144 island'
|
|
echo "[pass] preview_audio_decode_test (TSan)"
|