Compare commits

..
Author SHA1 Message Date
LibretroAdmin bb4be745e5 scanline sync: hand the vblank clock to the frame thread by seqlock, not a lock
The vblank clock was shared under an slock: the kernel-event thread
took it once per vblank to feed the clock, and the frame thread took it
two or three times a frame to read the beam. Uncontended that costs
little, but it lets the frame thread wait behind the time-critical
clock thread, and the house pattern for a one-writer snapshot read by
another thread is a seqlock - video_driver.c's frame cache, the
viewport, the shader sensor uniforms.

The clock has exactly one writer at a time: the kernel-event thread
while it runs, the frame thread otherwise (the DWM and DirectDraw
sources, and every reset). Every source switch stops the thread before
the frame thread writes and starts it after, and thread start and join
order that handover, so the writer's clock is plain memory. What the
frame thread reads is a published copy:

- vblank_clock_publish() / vblank_clock_snapshot(), in vblank_clock.h:
  a stamp made odd around four relaxed 64-bit and int stores - the
  period and nominal rate as the bits of their doubles - and a reader
  that retries against a publish in progress up to 64 times, then
  takes "no clock this once". The same shape and bound as the frame
  cache's. The writer never waits and the reader never blocks.

- Available where retro_atomic is lock-free and 64 bits wide, which
  both MinGW targets are - on 32-bit x86 the 64-bit fields ride
  cmpxchg8b, which every processor Windows 2000 runs on has. A build
  without them keeps the lock, as the frame cache does.

The DirectDraw source, whose writer is the frame thread itself, reads
its own copy instead of the snapshot.

samples/gfx/vblank_clock/vblank_clock_pub_test: one writer publishing
two million clocks flat out, each built so its fields agree with each
other, and three readers checking every snapshot is one whole publish
and that none goes backwards. Five to six million snapshots, none torn
or backwards; clean under ThreadSanitizer, ASan and UBSan, and run
under TSan in the samples/gfx job. (Under TSan's slowdown about 5% of
reads hit the retry bound against a writer that never pauses; the real
one publishes once a vblank.)
2026-09-18 18:20:53 +02:00
LibretroAdmin 2fbd665566 scanline sync: feed the vblank clock from DWM, the kernel event, or DirectDraw readings
The vblank clock had one source, a thread waiting on
D3DKMTWaitForVerticalBlankEvent, which exists only on WDDM. Scanline
Sync now picks its source per display, best first, and all three feed
the same clock; the frame side - beam by arithmetic, one timer to the
target line - is unchanged.

Read from the binaries first. DXGI's WaitForVBlank resolves
D3DKMTWaitForVerticalBlankEvent by name (dxgi 10.0.26100), and
DirectDraw on WDDM builds WaitForVerticalBlank as a user-mode loop over
D3DKMTGetScanLine - no sleep, no bound, under DirectDraw's process-wide
lock (ddraw 6.1.7600, DdWaitForVerticalBlank, the loop at 71065e12). So
on Vista and later neither is a way in that is better than calling the
export, and the kernel-event tier keeps calling it directly. On XP
(ddraw 5.3.2600.5512, symbols through the OMAP of its BBT layout),
WaitForVerticalBlank makes one call into the display driver and blocks
or spins as that driver sees fit, holding the same lock, and its event
mode returns E_NOTIMPL; GetScanLine is one call into the driver.

- DWM (Vista and later with composition on; always from Windows 8):
  DwmGetCompositionTimingInfo's last vblank and refresh period, read on
  the frame thread - no thread, no kernel wait. With composition on a
  frame reaches the screen at DWM's next composition, which runs on
  this clock. Used only while DWM's period matches the window's
  monitor, since DWM paces to one display; and only while its
  timestamps advance, since exclusive fullscreen stops them. It takes
  over from the kernel event after 60 advancing frames, so a window
  flicking in and out of exclusive fullscreen does not keep restarting
  the clock.

- The kernel vblank event (WDDM): the existing thread, now one source
  among three.

- DirectDraw readings (XDDM - Windows 2000 and XP, where no D3DKMT
  exists): one IDirectDraw::GetScanLine per call on the frame thread,
  on the DirectDraw object DirectDrawEnumerateEx gives for the
  window's monitor. WaitForVerticalBlank is not used.
  vblank_sampler_feed(), new beside the clock, turns each reading into
  the time of the vblank it implies, measuring the beam's line rate
  from pairs of readings in the same frame and the total line count
  from the rate and the period, which XDDM has no API to report. The
  mode's whole-Hz rate from EnumDisplaySettings is only a start - a
  driver reporting "hardware default" starts from 60 Hz - and the clock
  learns the true period. A driver without GetScanLine stops being
  asked after 60 failures.

Until the clock has settled the frame is told there is no scanline, so
Scanline Sync idles instead of polling the counter while it settles.
Only a display none of the three serves - Vista, with no
QueryDisplayConfig for the line counts - still reads the counter.
ddraw.dll and dwmapi.dll are loaded at run time, so nothing is linked
that Windows 2000 lacks; a 32-bit build for _WIN32_WINNT 0x0500
compiles.

samples/gfx/vblank_clock gains the sampler: a counter read twice a
frame at varying points, each read taking 2-30 us, blanking reported
as an error, a 59.94 Hz display started from 60. The total line count
comes out exact and the beam within 2 lines. samples/gfx/vblank_clock_ddraw
runs the tier against a real DirectDraw - wine's, which reports the
counter as an XDDM driver does - and checks the clock's beam against
GetScanLine: within 2-5 lines over 115-119 checks. It runs under wine
in the samples/gfx job.

Not run on real hardware: the DWM and kernel-event tiers need Windows,
and the XDDM tier's cost per GetScanLine and each driver's report of
blanking are unmeasured on XP itself.
2026-09-18 18:03:46 +02:00
LibretroAdmin 9dd810c96e scanline sync: race the beam on a vblank clock and a timer, not by polling the counter
Scanline Sync waited for its target line by calling D3DKMTGetScanLine
in a loop, after a sleep that did not look at the beam at all. Read
from dxgkrnl.sys (10.0.26100.7462, its own PDB), that call is a system
call that probes its input, looks the adapter up by handle, takes the
adapter's DDI lock, checks IRQL and dispatches into the display
miniport's DxgkDdiGetScanLine - about 223 us a call on the panel the
tree's comment measured, some 15 lines at 1080p60. And the tuner
measured those lines against RetroArch's viewport height, which is the
display's only fullscreen at the desktop resolution.

D3DKMTWaitForVerticalBlankEvent, measured in the same comment within
30 us of the real vblank with its period accurate to 0.02%, was already
resolved and never called. In dxgkrnl the wait is a KeWaitForSingleObject
on the adapter's vblank event with a fixed 80 ms timeout, so it cannot
hang.

- A vblank clock thread waits on that event for the window's monitor,
  at time-critical priority, and feeds each timestamp to
  gfx/common/vblank_clock.h. A failed wait is never taken for a vblank;
  an error that returns at once backs off on the thread's stop event.
  Stopping it joins a wait the kernel bounds.

- The mode's active and total lines, and its exact rate, come from
  QueryDisplayConfig, matched to the counter's output by the adapter
  LUID and VidPn source ID that D3DKMTOpenAdapterFromHdc reports
  (win32_display_signal_timing, in dispserv_win32.c beside the
  existing QueryDisplayConfig code). The tuner measures in those lines
  now, and takes the total outright instead of watching a frame to
  count it. Interlaced modes are left out.

- vblank_clock.h is the arithmetic, pure and on its own. A waiter only
  ever wakes late, so it tracks the lower edge of the timestamps: an
  early arrival pulls the phase by half, a late one by a sixty-fourth,
  and the period is corrected from the same error. A whole number of
  periods is missed vblanks and keeps the settle count; anything else
  starts over; the same vblank reported twice is ignored. The beam is
  reported only after 120 good vblanks, and never once none has come
  for two periods - and a period that wanders, variable refresh, never
  settles, so it is never used there.

- D3DKMTGetScanLine answers from the clock when it is running, and
  reads the counter only when it is not. After a frame, the wait for
  the target line is one high-resolution waitable timer aimed at the
  moment the clock says the beam gets there, with a rebind event - the
  window moved, the mode changed - ending it at once, and a bound. No
  polling, no sleep. The counter loop is kept, and bounded, for when
  there is no clock.

samples/gfx/vblank_clock drives vblank_clock.h against 50 simulated
displays up to 600 ppm off their nominal rate, with a waiter waking up
to 460 us late. Beam and wait landing stay within 4 lines (6 is
asserted); the period within 1.8 us. Getting there took the test: a
first version, smoothing the timestamps as if lateness were symmetric,
was 16 lines out, and reporting after 8 vblanks let it be 54 lines out
while converging. Missed and duplicate vblanks, a stale clock, variable
refresh and a target already passed are covered. It runs under C89,
ASan and UBSan in the samples/gfx job.

Not run on hardware: wine has no D3DKMTGetScanLine, and nothing here
has a display. The Windows side is syntax-checked with MinGW and the
linux-c89 flags.
2026-09-18 18:03:42 +02:00
LibretroAdmin 8d61347d46 scanline sync: bound the beam wait, and read the monitor the window is on
Two faults in Scanline Sync, the D3DKMT beam-racing path on Windows.

The wait after a frame spun on D3DKMTGetScanLine until the beam reached
the target line, and that was its only way out. The before-frame tuner
checks once per frame that the query works, but a query that starts
failing during the wait - the display going to sleep, a mode change,
the lock screen, an adapter reset - returns -1, which never reaches the
target: the frame thread spun at full CPU for good. The wait now leaves
at once on a failed read, keeping the tuner's state as it was, and is
bounded to two frames, which covers reaching the target and the
calibration pass that measures a whole frame.

And the scanline counter was opened on whichever display device
answered first at startup - usually the primary - whatever monitor the
window was on, so on a second screen the sync raced another display's
beam, possibly at another refresh rate. It is now opened on the adapter
output driving the window's monitor. WM_MOVE and WM_DISPLAYCHANGE flag
a possible change, and the thread that reads the counter rebinds it
before its next read, so the adapter state is only touched by that
thread; with nothing changed that costs one atomic load. The adapter
handle being replaced is closed with D3DKMTCloseAdapter where gdi32
has it; they were never closed before.

Syntax-checked with MinGW and the linux-c89 flags. Not run: wine has no
D3DKMTGetScanLine, and nothing here has a display to race.
2026-09-18 18:03:38 +02:00
2112 changed files with 279531 additions and 169923 deletions

No files matched your search

+2 -20
View File
@@ -21,12 +21,11 @@ jobs:
timeout-minutes: 30
steps:
- name: Dependencies
timeout-minutes: 20
timeout-minutes: 5
run: |
# The gl2 arms compile against the desktop GL and EGL headers,
# and the Dingux and RS90 video drivers against SDL 1.2's.
# The Direct3D and WASAPI lanes cross-compile with mingw-w64.
pkgs="build-essential libgl-dev libegl-dev libgles-dev libsdl1.2-dev gcc-mingw-w64-x86-64 clang gcc-aarch64-linux-gnu gcc-mipsel-linux-gnu"
pkgs="build-essential libgl-dev libegl-dev libgles-dev libsdl1.2-dev"
if dpkg -s $pkgs >/dev/null 2>&1; then
echo "All packages already present on runner image; skipping apt."
exit 0
@@ -37,23 +36,6 @@ jobs:
echo "apt-get update failed (attempt $i); retrying in 10s"
sleep 10
done
# Fetch first, under a bound: a download is safe to stop and
# resumes, an install is not. A mirror that stalls is left for
# the main archive on the next attempt.
for i in 1 2 3; do
sudo timeout 240 apt-get install -y --download-only \
-o Acquire::Retries=3 -o Acquire::http::Timeout=30 \
$pkgs && break
[ $i = 3 ] && exit 1
echo "package download failed or stalled (attempt $i); retrying from archive.ubuntu.com"
for f in /etc/apt/sources.list /etc/apt/sources.list.d/*; do
[ -f "$f" ] || continue
sudo sed -i -e 's#mirror+file:/etc/apt/apt-mirrors.txt#http://archive.ubuntu.com/ubuntu/#g' \
-e 's#http://azure\.archive\.ubuntu\.com/ubuntu#http://archive.ubuntu.com/ubuntu#g' "$f"
done
sudo timeout 90 apt-get update -y \
-o Acquire::Retries=3 -o Acquire::http::Timeout=30 || true
done
sudo apt-get install -y \
-o Acquire::Retries=3 -o Acquire::http::Timeout=30 \
$pkgs
@@ -35,9 +35,6 @@ on:
- 'libretro-common/include/vfs/**'
- 'libretro-common/include/streams/file_stream.h'
- 'libretro-common/samples/file/vfs/**'
- 'libretro-common/fastjmp/**'
- 'libretro-common/include/fastjmp.h'
- 'libretro-common/samples/fastjmp/**'
- '.github/workflows/Cross-libc-vfs-samples.yml'
pull_request:
paths:
@@ -46,9 +43,6 @@ on:
- 'libretro-common/include/vfs/**'
- 'libretro-common/include/streams/file_stream.h'
- 'libretro-common/samples/file/vfs/**'
- 'libretro-common/fastjmp/**'
- 'libretro-common/include/fastjmp.h'
- 'libretro-common/samples/fastjmp/**'
- '.github/workflows/Cross-libc-vfs-samples.yml'
workflow_dispatch:
@@ -175,15 +169,3 @@ jobs:
# Exercises the Win32 CreateFileMapping/MapViewOfFile
# backend, which no other job in the tree reaches.
./vfs_mapped_ptr_test.exe
# The Win64 fastjmp_set stores xmm6-xmm15 with movaps and faults
# on a misaligned buffer. Linux has no such stores, so the Linux
# samples job cannot see a fastjmp_buf that lost its alignment;
# this is the only lane that can. Under a second to build and run.
- name: fastjmp on Win64
working-directory: libretro-common/samples/fastjmp/fastjmp_test
run: |
set -euo pipefail
make clean
make
./fastjmp_test.exe
@@ -1,48 +0,0 @@
name: CI extern "C" prototype parity
on:
push:
branches:
- master
pull_request:
branches:
- master
workflow_dispatch:
permissions:
contents: read
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: ${{ github.ref != 'refs/heads/master' }}
jobs:
extern-c-prototype-check:
name: extern "C" definitions match their C prototypes
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- name: Checkout
uses: actions/checkout@v5
- name: Checker self-test
shell: bash
run: |
set -eu
# Fixture pass: a matching definition is clean, a definition
# with a parameter more than its prototype (the shape of the
# 0b2613d Xbox boot crash) is flagged, and a parameter of
# another type is flagged.
python3 tools/extern_c_prototype_check.py --selftest
- name: Tree check
shell: bash
run: |
set -eu
# A .cpp file that defines a win32_common.h function under
# extern "C" without including the header (uwp_main.cpp) is
# never checked against the prototype by any compiler: the
# linker matches on the name alone. This is the check that
# compares the two, for every such definition in the tree.
python3 tools/extern_c_prototype_check.py
-91
View File
@@ -1,91 +0,0 @@
name: CI FreeBSD
# Builds RetroArch inside a FreeBSD VM (GitHub has no FreeBSD runners)
# with the same feature set the games/retroarch port enables by default,
# minus Qt (very slow to install). This is what kept the port carrying
# patches on top of each release: libudev is FreeBSD's libudev-devd, the
# input headers come from dev/evdev/, and there is no linux_common.o to
# link against.
#
# Deliberately NOT installed: evdev-proto and v4l_compat (the Linux
# compatibility headers). A stock FreeBSD box must build udev + Wayland
# from the dev/evdev/ headers in base, and configure must turn V4L2 off
# on its own when libv4l is present but linux/videodev2.h is not.
on:
push:
branches: [master]
pull_request:
repository_dispatch:
types: [run_build]
permissions:
contents: read
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: ${{ github.ref != 'refs/heads/master' }}
jobs:
build:
runs-on: ubuntu-latest
timeout-minutes: 90
steps:
- name: Check Out Repo
uses: actions/checkout@v4
- name: Build in FreeBSD
uses: vmactions/freebsd-vm@v1
with:
release: "14.3"
usesh: true
copyback: false
prepare: |
pkg install -y \
gmake pkgconf \
ffmpeg freetype2 openal-soft mesa-libs \
libudev-devd libxkbcommon wayland wayland-protocols \
libX11 libXext libXxf86vm libXinerama libXrandr libXv \
sdl2 pipewire alsa-lib pulseaudio libdrm libv4l
run: |
set -e
freebsd-version
NCPU=$(sysctl -n hw.ncpu)
# --disable-pipewire must not leave HAVE_PIPEWIRE_STABLE
# behind just because libpipewire is installed.
./configure --disable-qt --disable-discord --disable-pipewire
grep -q '^HAVE_PIPEWIRE_STABLE = 0' config.mk
# libv4l2.pc is installed but linux/videodev2.h is not
# (v4l_compat): V4L2 must be auto-disabled, not left on to
# fail at compile time.
grep -q '^HAVE_V4L2 = 0' config.mk
# Real build: force the FreeBSD-specific paths on so a missing
# dependency fails configure instead of silently skipping them.
./configure --disable-qt --disable-discord \
--enable-udev --enable-x11 --enable-wayland --enable-sdl2 \
--enable-ffmpeg --enable-pipewire --enable-alsa \
--enable-pulse --enable-oss
gmake -j$NCPU clean
gmake -j$NCPU info all
./retroarch --version
# udev input must multiplex with kqueue here, so the binary
# must not need libepoll-shim's epoll_* at all.
if nm -u retroarch | grep -q 'epoll_'; then
echo "retroarch still references epoll (libepoll-shim)"; exit 1
fi
# A fresh RetroArch must pick up data sets the ports install
# under /usr/local/share/libretro (assets, info, shaders,
# autoconfig) without any retroarch.cfg edits.
sh samples/frontend/packaged_dirs/run.sh
# With v4l_compat the V4L2 camera and video processor core
# must still compile; only those two objects are rebuilt.
pkg install -y v4l_compat
./configure --disable-qt --disable-discord --enable-v4l2
grep -q '^HAVE_V4L2 = 1' config.mk
gmake -j$NCPU camera/drivers/video4linux2.o \
cores/libretro-video-processor/video_processor_v4l2.o
+1 -18
View File
@@ -24,7 +24,7 @@ jobs:
timeout-minutes: 30
steps:
- name: Dependencies
timeout-minutes: 20
timeout-minutes: 5
run: |
pkgs="build-essential libxkbcommon-dev libx11-xcb-dev zlib1g-dev libfreetype6-dev libegl1-mesa-dev libgles2-mesa-dev libgbm-dev nvidia-cg-toolkit nvidia-cg-dev libavcodec-dev libsdl2-dev libsdl-image1.2-dev libxml2-dev yasm"
if dpkg -s $pkgs >/dev/null 2>&1; then
@@ -37,23 +37,6 @@ jobs:
echo "apt-get update failed (attempt $i); retrying in 10s"
sleep 10
done
# Fetch first, under a bound: a download is safe to stop and
# resumes, an install is not. A mirror that stalls is left for
# the main archive on the next attempt.
for i in 1 2 3; do
sudo timeout 240 apt-get install -y --download-only \
-o Acquire::Retries=3 -o Acquire::http::Timeout=30 \
$pkgs && break
[ $i = 3 ] && exit 1
echo "package download failed or stalled (attempt $i); retrying from archive.ubuntu.com"
for f in /etc/apt/sources.list /etc/apt/sources.list.d/*; do
[ -f "$f" ] || continue
sudo sed -i -e 's#mirror+file:/etc/apt/apt-mirrors.txt#http://archive.ubuntu.com/ubuntu/#g' \
-e 's#http://azure\.archive\.ubuntu\.com/ubuntu#http://archive.ubuntu.com/ubuntu#g' "$f"
done
sudo timeout 90 apt-get update -y \
-o Acquire::Retries=3 -o Acquire::http::Timeout=30 || true
done
sudo apt-get install -y \
-o Acquire::Retries=3 -o Acquire::http::Timeout=30 \
$pkgs
+1 -79
View File
@@ -72,7 +72,7 @@ jobs:
# comment for why SDL2 over xvideo. No sanitizer-specific
# packages required; libasan / libubsan ship with the gcc
# that ubuntu-latest has installed by default.
timeout-minutes: 20
timeout-minutes: 5
run: |
pkgs="build-essential libxkbcommon-dev libx11-xcb-dev xvfb x11-utils zlib1g-dev libfreetype6-dev libegl1-mesa-dev libgles2-mesa-dev libgbm-dev nvidia-cg-toolkit nvidia-cg-dev libavcodec-dev libsdl2-dev libsdl-image1.2-dev libxml2-dev yasm"
if dpkg -s $pkgs >/dev/null 2>&1; then
@@ -85,23 +85,6 @@ jobs:
echo "apt-get update failed (attempt $i); retrying in 10s"
sleep 10
done
# Fetch first, under a bound: a download is safe to stop and
# resumes, an install is not. A mirror that stalls is left for
# the main archive on the next attempt.
for i in 1 2 3; do
sudo timeout 240 apt-get install -y --download-only \
-o Acquire::Retries=3 -o Acquire::http::Timeout=30 \
$pkgs && break
[ $i = 3 ] && exit 1
echo "package download failed or stalled (attempt $i); retrying from archive.ubuntu.com"
for f in /etc/apt/sources.list /etc/apt/sources.list.d/*; do
[ -f "$f" ] || continue
sudo sed -i -e 's#mirror+file:/etc/apt/apt-mirrors.txt#http://archive.ubuntu.com/ubuntu/#g' \
-e 's#http://azure\.archive\.ubuntu\.com/ubuntu#http://archive.ubuntu.com/ubuntu#g' "$f"
done
sudo timeout 90 apt-get update -y \
-o Acquire::Retries=3 -o Acquire::http::Timeout=30 || true
done
sudo apt-get install -y \
-o Acquire::Retries=3 -o Acquire::http::Timeout=30 \
$pkgs
@@ -400,64 +383,3 @@ jobs:
| sort -u | head -20
fi
echo "[done] imageviewer headless smoke (soft-fail)"
- name: Screenshot on exit under LeakSanitizer (strict)
# The smoke above runs with detect_leaks=0, which is how a
# synchronous screenshot leaking its task state and the whole
# captured frame on every call went unnoticed: --max-frames-ss
# and save-state thumbnails both take that path. This run
# takes one and checks for leaks with only the system libraries
# suppressed - Mesa, X11, SDL, D-Bus and friends keep caches
# alive to exit that are not RetroArch's - so anything left is
# ours and fails the job.
env:
ASAN_OPTIONS: abort_on_error=1:detect_leaks=1:print_stacktrace=1
UBSAN_OPTIONS: print_stacktrace=1:halt_on_error=1
LSAN_OPTIONS: suppressions=/tmp/lsan-system.supp:print_suppressions=0
run: |
set -eu
cat > /tmp/lsan-system.supp <<EOF
leak:libGL
leak:libGLX
leak:libGLdispatch
leak:libgallium
leak:libLLVM
leak:_dri.so
leak:libEGL
leak:libgbm
leak:libX11
leak:libxcb
leak:libSDL2
leak:libdbus-1
leak:libfontconfig
leak:libfreetype
leak:libasound
leak:libpulse
EOF
mkdir -p /tmp/lsan-cfg /tmp/lsan-shots
cat > /tmp/lsan-cfg/retroarch.cfg <<EOF
video_driver = "sdl2"
audio_driver = "null"
video_threaded = "false"
video_fullscreen = "false"
video_windowed_fullscreen = "false"
screenshot_directory = "/tmp/lsan-shots"
EOF
set +e
timeout 120 xvfb-run -a -s "-screen 0 320x240x24" ./retroarch \
-c /tmp/lsan-cfg/retroarch.cfg \
-L cores/libretro-imageviewer/image_core.so \
/tmp/test.png \
--max-frames=60 \
--max-frames-ss \
--max-frames-ss-path=/tmp/lsan-shots/exit.png \
> /tmp/lsan.log 2>&1
rc=$?
set -e
if [ "$rc" != 0 ] || grep -qE "leak of|AddressSanitizer:|runtime error:" /tmp/lsan.log; then
echo "exit=${rc}"
grep -E -A 12 "leak of|AddressSanitizer:|runtime error:" /tmp/lsan.log | head -120
exit 1
fi
test -s /tmp/lsan-shots/exit.png
echo "[pass] screenshot on exit, no leaks outside the system libraries"
+5 -46
View File
@@ -21,26 +21,13 @@ concurrency:
jobs:
autoconf-tests:
name: Build and run the autoconfig regression suite (${{ matrix.lane }})
name: Build and run the autoconfig regression suite
runs-on: ubuntu-latest
timeout-minutes: ${{ matrix.sanitizer == '' && 15 || 40 }}
strategy:
fail-fast: false
matrix:
# The suite fails on any sanitizer report in any run, so the
# sanitized lanes hold the whole frontend it drives - runloop,
# task queue, autodetect - to the same bar as the assertions.
include:
- lane: plain
sanitizer: ''
- lane: ASan + UBSan
sanitizer: address,undefined
- lane: TSan
sanitizer: thread
timeout-minutes: 15
steps:
- name: Install dependencies
timeout-minutes: 20
timeout-minutes: 5
run: |
pkgs="build-essential zlib1g-dev libfreetype6-dev"
if dpkg -s $pkgs >/dev/null 2>&1; then
@@ -53,23 +40,6 @@ jobs:
echo "apt-get update failed (attempt $i); retrying in 10s"
sleep 10
done
# Fetch first, under a bound: a download is safe to stop and
# resumes, an install is not. A mirror that stalls is left for
# the main archive on the next attempt.
for i in 1 2 3; do
sudo timeout 240 apt-get install -y --download-only \
-o Acquire::Retries=3 -o Acquire::http::Timeout=30 \
$pkgs && break
[ $i = 3 ] && exit 1
echo "package download failed or stalled (attempt $i); retrying from archive.ubuntu.com"
for f in /etc/apt/sources.list /etc/apt/sources.list.d/*; do
[ -f "$f" ] || continue
sudo sed -i -e 's#mirror+file:/etc/apt/apt-mirrors.txt#http://archive.ubuntu.com/ubuntu/#g' \
-e 's#http://azure\.archive\.ubuntu\.com/ubuntu#http://archive.ubuntu.com/ubuntu#g' "$f"
done
sudo timeout 90 apt-get update -y \
-o Acquire::Retries=3 -o Acquire::http::Timeout=30 || true
done
sudo apt-get install -y \
-o Acquire::Retries=3 -o Acquire::http::Timeout=30 \
$pkgs
@@ -89,22 +59,11 @@ jobs:
grep -q '^HAVE_TEST_DRIVERS = 1' config.mk
- name: Build
run: |
if [ -n "${{ matrix.sanitizer }}" ]; then
make -j"$(nproc)" SANITIZER=${{ matrix.sanitizer }} DEBUG=1
else
make -j"$(nproc)"
fi
run: make -j"$(nproc)"
- name: Run autoconfig regression suite
shell: bash
run: |
set -eu
test -x ./retroarch
if [ "${{ matrix.sanitizer }}" = thread ]; then
# TSan cannot map its shadow under the runner's ASLR
# entropy; setarch -R carries to every child the suite runs.
setarch "$(uname -m)" -R ./tests-other/test_autoconf.sh ./retroarch
else
./tests-other/test_autoconf.sh ./retroarch
fi
./tests-other/test_autoconf.sh ./retroarch
@@ -1,79 +0,0 @@
name: CI Linux filter plugins
# Builds every video filter (gfx/video_filters) and audio DSP filter
# (libretro-common/audio/dsp_filters) plugin with its own Makefile and
# runs samples/gfx/filter_plugins over all of them: each plugin is
# loaded with RTLD_NOW into a host that does not link libm, so a
# symbol it cannot resolve fails the load, then fed a frame or a run
# of audio blocks at its default settings. Every video filter that
# takes more than one worker must give the single-worker output byte for
# byte with 2, 3 and 4 workers running at once. The same run is repeated
# with the plugins and the harness built under ASan, UBSan and
# LeakSanitizer, and under ThreadSanitizer.
on:
push:
branches:
- master
paths:
- 'gfx/video_filters/**'
- 'libretro-common/audio/dsp_filters/**'
- 'libretro-common/include/libretro_dspfilter.h'
- 'samples/gfx/filter_plugins/**'
- '.github/workflows/Linux-filter-plugins.yml'
pull_request:
paths:
- 'gfx/video_filters/**'
- 'libretro-common/audio/dsp_filters/**'
- 'libretro-common/include/libretro_dspfilter.h'
- 'samples/gfx/filter_plugins/**'
- '.github/workflows/Linux-filter-plugins.yml'
workflow_dispatch:
permissions:
contents: read
concurrency:
group: ${{ github.workflow }}-${{ github.event_name == 'pull_request' && github.ref || github.sha }}
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
jobs:
filter-plugins:
name: Load and run every filter plugin
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- name: Checkout
uses: actions/checkout@v5
- name: Build the plugins and the harness
run: |
set -eu
make -C samples/gfx/filter_plugins
# The harness must not bring libm in on the plugins' behalf.
if readelf -d samples/gfx/filter_plugins/filter_plugins_test \
| grep -q 'libm\.so'; then
echo "filter_plugins_test links libm; it would hide a plugin that does not"
exit 1
fi
- name: Load and run every plugin
run: make -C samples/gfx/filter_plugins check
- name: Load and run every plugin under ASan, UBSan and LeakSanitizer
env:
ASAN_OPTIONS: abort_on_error=1:detect_leaks=1:print_stacktrace=1
UBSAN_OPTIONS: halt_on_error=1:print_stacktrace=1
run: |
set -eu
make -C samples/gfx/filter_plugins clean
make -C samples/gfx/filter_plugins check SANITIZER=address,undefined
- name: Load and run every plugin under ThreadSanitizer
env:
TSAN_OPTIONS: halt_on_error=1
run: |
set -eu
make -C samples/gfx/filter_plugins clean
make -C samples/gfx/filter_plugins check SANITIZER=thread
@@ -1,92 +0,0 @@
name: CI Linux frontend overhead [frame-path harness]
# What one runloop_iterate() costs when the core costs nothing, and
# whether anything on the frame path allocates. The harness links the
# shipping release objects with main() replaced, boots on the null
# drivers, runs a core that pushes one frame, one audio batch and one
# input read per retro_run, and counts heap calls over the frame path
# by glibc interposition - which is why this is a release build with
# no allocator sanitizer in the way. It also prints microseconds per
# frame under fast-forward and trips on a loose bound, so a blocking
# call or a sleep that lands on the frame path fails here rather than
# in a bug report about stutter.
#
# Everything a default desktop build compiles in is compiled in, so a
# product that starts costing the frame while idle is measured, not
# excluded.
on:
push:
branches:
- master
pull_request:
branches:
- master
workflow_dispatch:
permissions:
contents: read
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: ${{ github.ref != 'refs/heads/master' }}
jobs:
linux-frontend-overhead:
name: Release build, frame-path heap and cost
runs-on: ubuntu-latest
timeout-minutes: 30
steps:
- name: Install dependencies
timeout-minutes: 20
run: |
pkgs="build-essential libxkbcommon-dev libx11-xcb-dev zlib1g-dev libfreetype6-dev libegl1-mesa-dev libgles2-mesa-dev libgbm-dev libavcodec-dev libsdl2-dev libxml2-dev yasm"
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
# Fetch first, under a bound: a download is safe to stop and
# resumes, an install is not. A mirror that stalls is left for
# the main archive on the next attempt.
for i in 1 2 3; do
sudo timeout 240 apt-get install -y --download-only \
-o Acquire::Retries=3 -o Acquire::http::Timeout=30 \
$pkgs && break
[ $i = 3 ] && exit 1
echo "package download failed or stalled (attempt $i); retrying from archive.ubuntu.com"
for f in /etc/apt/sources.list /etc/apt/sources.list.d/*; do
[ -f "$f" ] || continue
sudo sed -i -e 's#mirror+file:/etc/apt/apt-mirrors.txt#http://archive.ubuntu.com/ubuntu/#g' \
-e 's#http://azure\.archive\.ubuntu\.com/ubuntu#http://archive.ubuntu.com/ubuntu#g' "$f"
done
sudo timeout 90 apt-get update -y \
-o Acquire::Retries=3 -o Acquire::http::Timeout=30 || true
done
sudo apt-get install -y \
-o Acquire::Retries=3 -o Acquire::http::Timeout=30 \
$pkgs
- name: Checkout
uses: actions/checkout@v5
- name: Configure
# Non-Qt, as the harness requires (with Qt, main() lives in
# ui_qt.o). Everything else at its default.
run: ./configure --disable-qt
- name: Build (release)
run: make -j$(getconf _NPROCESSORS_ONLN)
- name: Run the frontend-overhead harness
shell: bash
run: |
set -o pipefail
SAN=none make -C samples/runloop/frontend_overhead check
make -C samples/runloop/frontend_overhead clean
-125
View File
@@ -1,125 +0,0 @@
name: CI Linux gl1 upload
# Runs the gl1 video driver under Xvfb with a test core and checks the
# screen against the frame the core drew, pixel for pixel, down each of
# the driver's upload paths: frames taken straight from the core's
# buffer, the no-GL_EXT_bgra swizzle, native RGB565, the GL 1.1 CPU
# expansion, and texture storage respecified across frame size changes.
on:
push:
branches:
- master
paths:
- 'gfx/drivers/gl1.c'
- 'libretro-common/gfx/scaler/pixconv.c'
- 'libretro-common/include/gfx/scaler/pixconv.h'
- 'samples/gfx/gl1_upload/**'
- '.github/workflows/Linux-gl1-upload.yml'
pull_request:
paths:
- 'gfx/drivers/gl1.c'
- 'libretro-common/gfx/scaler/pixconv.c'
- 'libretro-common/include/gfx/scaler/pixconv.h'
- 'samples/gfx/gl1_upload/**'
- '.github/workflows/Linux-gl1-upload.yml'
workflow_dispatch:
permissions:
contents: read
concurrency:
group: ${{ github.workflow }}-${{ github.event_name == 'pull_request' && github.ref || github.sha }}
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
jobs:
gl1-upload:
name: Run retroarch on gl1 under Xvfb and check the frame
runs-on: ubuntu-latest
timeout-minutes: 25
steps:
- name: Install dependencies
timeout-minutes: 20
run: |
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
# Fetch first, under a bound: a download is safe to stop and
# resumes, an install is not. A mirror that stalls is left for
# the main archive on the next attempt.
for i in 1 2 3; do
sudo timeout 240 apt-get install -y --download-only \
-o Acquire::Retries=3 -o Acquire::http::Timeout=30 \
build-essential zlib1g-dev ccache python3 \
xvfb libgl1-mesa-dri \
libx11-dev libx11-xcb-dev libxcb1-dev libxext-dev \
libxxf86vm-dev libxrandr-dev libxinerama-dev \
libgl-dev libegl-dev && break
[ $i = 3 ] && exit 1
echo "package download failed or stalled (attempt $i); retrying from archive.ubuntu.com"
for f in /etc/apt/sources.list /etc/apt/sources.list.d/*; do
[ -f "$f" ] || continue
sudo sed -i -e 's#mirror+file:/etc/apt/apt-mirrors.txt#http://archive.ubuntu.com/ubuntu/#g' \
-e 's#http://azure\.archive\.ubuntu\.com/ubuntu#http://archive.ubuntu.com/ubuntu#g' "$f"
done
sudo timeout 90 apt-get update -y \
-o Acquire::Retries=3 -o Acquire::http::Timeout=30 || true
done
sudo apt-get install -y \
-o Acquire::Retries=3 -o Acquire::http::Timeout=30 \
build-essential zlib1g-dev ccache python3 \
xvfb libgl1-mesa-dri \
libx11-dev libx11-xcb-dev libxcb1-dev libxext-dev \
libxxf86vm-dev libxrandr-dev libxinerama-dev \
libgl-dev libegl-dev
- 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
export CCACHE_DIR="$HOME/.ccache"
echo "CCACHE_DIR=$CCACHE_DIR" >> "$GITHUB_ENV"
echo "/usr/lib/ccache" >> "$GITHUB_PATH"
ccache -M 500M
ccache -z
- name: Build RetroArch (X11 + OpenGL, the rest off)
shell: bash
run: |
set -eu
./configure --disable-qt --disable-wayland --disable-sdl \
--disable-sdl2 --disable-ffmpeg --disable-vulkan \
--disable-pulse --disable-discord \
--disable-networking --disable-cheevos \
--disable-glslang --disable-slang \
--disable-spirv_cross --disable-xmb \
--disable-materialui --disable-ozone \
--disable-7zip --disable-flac \
--disable-audiomixer --disable-translate \
--disable-accessibility --disable-crtswitchres \
--disable-langextra --disable-libretrodb \
--disable-video_filter --disable-dsp_filter
grep -q '^HAVE_OPENGL1 = 1' config.mk
make -j$(nproc)
ccache -s
- name: Run gl1_upload_test
shell: bash
run: |
set -eu
make -C samples/gfx/gl1_upload
timeout 600 python3 samples/gfx/gl1_upload/gl1_upload_test.py ./retroarch
@@ -1,139 +0,0 @@
name: CI Linux library network backends
# The built-in TLS, SMB and NFS clients are the defaults and nothing is
# bundled. A system mbedTLS or BearSSL can stand in for the TLS client
# (--enable-mbedtls, --enable-bearssl) and a system libsmb2 for the SMB
# client (--enable-libsmb). Each lane configures with its option, builds
# RetroArch to the link, checks the binary carries that library's
# backend and not the built-in client, and then operates the backend:
# the TLS lanes run the cheevos login transport against a local HTTPS
# server, the libsmb2 lane runs the SMB VFS tests against a local smbd.
on:
push:
branches:
- master
pull_request:
branches:
- master
workflow_dispatch:
permissions:
contents: read
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: ${{ github.ref != 'refs/heads/master' }}
jobs:
library-backends:
name: ${{ matrix.lane }}
runs-on: ubuntu-latest
timeout-minutes: 60
strategy:
fail-fast: false
matrix:
include:
- lane: mbedtls
configure: --enable-mbedtls
present: mbedtls_ssl_handshake
absent: ' T ssl_socket_retro_was_resumed'
- lane: bearssl
configure: --enable-bearssl
present: br_ssl_client_init_full
absent: ' T ssl_socket_retro_was_resumed'
- lane: libsmb2
configure: --enable-libsmb
present: ' U smb2_connect_share'
absent: ' T rsmb_connect'
steps:
- name: Dependencies
run: |
sudo apt-get update -y
sudo apt-get install -y build-essential pkg-config cmake \
libgl1-mesa-dev libx11-dev libxext-dev libasound2-dev libsdl2-dev \
zlib1g-dev libfreetype6-dev python3 openssl \
libmbedtls-dev libbearssl-dev samba
- name: Checkout
uses: actions/checkout@v5
- name: libsmb2 from upstream (no Ubuntu package)
if: matrix.lane == 'libsmb2'
run: |
set -eu
git clone -q --depth 1 https://github.com/sahlberg/libsmb2.git "$RUNNER_TEMP/libsmb2"
cmake -S "$RUNNER_TEMP/libsmb2" -B "$RUNNER_TEMP/libsmb2/build" \
-DCMAKE_INSTALL_PREFIX=/usr/local -DBUILD_SHARED_LIBS=ON >/dev/null
make -C "$RUNNER_TEMP/libsmb2/build" -j"$(nproc)" >/dev/null
sudo make -C "$RUNNER_TEMP/libsmb2/build" install >/dev/null
sudo ldconfig
echo "PKG_CONFIG_PATH=/usr/local/lib/pkgconfig" >> "$GITHUB_ENV"
pkg-config --modversion libsmb2 || PKG_CONFIG_PATH=/usr/local/lib/pkgconfig pkg-config --modversion libsmb2
- name: Configure
run: |
set -eu
./configure ${{ matrix.configure }} --disable-qt --disable-wayland --disable-ffmpeg
grep -E '^HAVE_(RETROSSL|MBEDTLS|BEARSSL|RETROSMB|SMBCLIENT|SSL) ' config.mk
case "${{ matrix.lane }}" in
mbedtls) grep -q '^HAVE_MBEDTLS = 1' config.mk; grep -q '^HAVE_RETROSSL = 0' config.mk ;;
bearssl) grep -q '^HAVE_BEARSSL = 1' config.mk; grep -q '^HAVE_RETROSSL = 0' config.mk ;;
libsmb2) grep -q '^HAVE_SMBCLIENT = 1' config.mk; grep -q '^HAVE_RETROSMB = 0' config.mk ;;
esac
- name: Build
run: make -j"$(nproc)"
- name: Check which backend linked
run: |
set -eu
nm retroarch | grep -q -- '${{ matrix.present }}'
if nm retroarch | grep -q -- '${{ matrix.absent }}'; then
echo "the built-in client is still linked in the ${{ matrix.lane }} build"; exit 1
fi
echo "[pass] ${{ matrix.lane }} backend linked, built-in client absent"
- name: Operate the TLS backend (cheevos login transport, ASan + UBSan)
if: matrix.lane != 'libsmb2'
working-directory: samples/network/cheevos_login
run: |
set -eu
case "${{ matrix.lane }}" in mbedtls) be=mbed ;; bearssl) be=bear ;; esac
make clean all SSL_BACKEND=$be SANITIZER=address,undefined
ASAN_OPTIONS=detect_leaks=1:detect_stack_use_after_return=1 \
UBSAN_OPTIONS=print_stacktrace=1:halt_on_error=1 timeout 120 \
make check SSL_BACKEND=$be
echo "[pass] ${{ matrix.lane }}: cheevos login transport over a local HTTPS server"
- name: Operate the libsmb2 backend (SMB VFS against a local smbd)
if: matrix.lane == 'libsmb2'
run: |
set -eu
sudo -E env PATH="$PATH" PKG_CONFIG_PATH="$PKG_CONFIG_PATH" WITH_LIBSMB2=1 \
samples/network/smb_retro/local_samba_test.sh
sudo make -C samples/network/smb_retro clean
# libsmb2 keeps process-wide state (its list of live contexts) with
# no lock of its own; the VFS backend must serialise it. A race
# there needs two cores to show up by timing, so the same tests run
# again under TSan against a TSan-built libsmb2, which reports it on
# any runner, every time.
- name: Operate the libsmb2 backend under TSan (instrumented libsmb2)
if: matrix.lane == 'libsmb2'
run: |
set -eu
cmake -S "$RUNNER_TEMP/libsmb2" -B "$RUNNER_TEMP/libsmb2/build-tsan" \
-DCMAKE_INSTALL_PREFIX=/opt/libsmb2-tsan -DBUILD_SHARED_LIBS=ON \
-DCMAKE_C_FLAGS="-O1 -g -fsanitize=thread" \
-DCMAKE_SHARED_LINKER_FLAGS="-fsanitize=thread" >/dev/null
make -C "$RUNNER_TEMP/libsmb2/build-tsan" -j"$(nproc)" >/dev/null
sudo make -C "$RUNNER_TEMP/libsmb2/build-tsan" install >/dev/null
sudo -E env PATH="$PATH" \
PKG_CONFIG_PATH=/opt/libsmb2-tsan/lib/pkgconfig \
LD_LIBRARY_PATH=/opt/libsmb2-tsan/lib \
TSAN_OPTIONS="halt_on_error=1 exitcode=66" \
CFLAGS="-O1 -g -fsanitize=thread" LDLIBS="-fsanitize=thread" \
WITH_LIBSMB2=1 samples/network/smb_retro/local_samba_test.sh
sudo make -C samples/network/smb_retro clean
@@ -27,14 +27,13 @@ jobs:
steps:
- name: Install dependencies
timeout-minutes: 20
timeout-minutes: 5
run: |
# All three packages ship on the stock ubuntu-latest image, so
# this step is a no-op there and apt is only contacted if the
# image ever drops one of them.
# ffmpeg (with libx264 and libx265): formats/h264/rh264_lossless_test
# and formats/h265/rh265_lossless_test build their corpora and
# their reference decodes with it.
# ffmpeg (with libx264): formats/h264/rh264_lossless_test builds
# its lossless corpus and its reference decodes with it.
if dpkg -s build-essential zlib1g-dev clang ffmpeg >/dev/null 2>&1; then
echo "All packages already present on runner image; skipping apt."
exit 0
@@ -45,23 +44,6 @@ jobs:
echo "apt-get update failed (attempt $i); retrying in 10s"
sleep 10
done
# Fetch first, under a bound: a download is safe to stop and
# resumes, an install is not. A mirror that stalls is left for
# the main archive on the next attempt.
for i in 1 2 3; do
sudo timeout 240 apt-get install -y --download-only \
-o Acquire::Retries=3 -o Acquire::http::Timeout=30 \
build-essential zlib1g-dev clang ffmpeg && break
[ $i = 3 ] && exit 1
echo "package download failed or stalled (attempt $i); retrying from archive.ubuntu.com"
for f in /etc/apt/sources.list /etc/apt/sources.list.d/*; do
[ -f "$f" ] || continue
sudo sed -i -e 's#mirror+file:/etc/apt/apt-mirrors.txt#http://archive.ubuntu.com/ubuntu/#g' \
-e 's#http://azure\.archive\.ubuntu\.com/ubuntu#http://archive.ubuntu.com/ubuntu#g' "$f"
done
sudo timeout 90 apt-get update -y \
-o Acquire::Retries=3 -o Acquire::http::Timeout=30 || true
done
sudo apt-get install -y \
-o Acquire::Retries=3 -o Acquire::http::Timeout=30 \
build-essential zlib1g-dev clang ffmpeg
@@ -110,7 +92,6 @@ jobs:
# self-contained test and exits 0 on success / non-zero on
# failure. These are built AND executed.
declare -a RUN_TARGETS=(
float_to_s32_test
compat_fnmatch_test
snprintf
unbase64_test
@@ -121,20 +102,17 @@ jobs:
config_file_test
path_resolve_realpath_test
fill_pathname_test
nbio_test
rpng
rzip_chunk_size_test
rzip_matches_buf_test
rzip_codec_test
rzip_parallel_test
data_transfer_source_test
data_transfer_prefix_test
data_transfer_window_test
audio_transfer_window_test
rjpeg_test
rh264_lossless_test
rh264_lossless_test_nothreads
rh265_lossless_test
rh265_lossless_test_nothreads
r7z_lzma_props_test
vcdiff_test
net_ifinfo
@@ -157,11 +135,6 @@ jobs:
rtga_test
rbmp_test
rbmp_encode_test
# Every 16-bit input through each pixconv converter, with
# SSE2, with MMX only, and with no SIMD at all.
pixconv_test
pixconv_test_nosimd
pixconv_test_scalar
rpng_chunk_overflow_test
rpng_roundtrip_test
rpng_decode_corpus_test
@@ -173,18 +146,11 @@ jobs:
screenshot_io_bench
word_wrap_overflow_test
task_queue_title_error_test
task_queue_snapshot_test
task_queue_init_failure_test
# Stops the worker with a bound when a detachable
# handler is stuck; waits ~1.5 s by design.
task_queue_detach_test
task_queue_main_thread_test
task_queue_budget_test
image_blit_bands_test
tpool_wait_test
# Four CPU-burning jobs on a pool of four must overlap;
# on a one-core runner it notes that and still passes.
tpool_parallel_test
data_transfer_pool_scrub_test
retro_atomic_test
retro_atomic_extern_c_linkage_test
@@ -200,12 +166,13 @@ jobs:
# microsecond clock; the late bound is loose for a loaded
# runner.
retro_sleep_until_test
# Runs its natural tier only here; procbarrier-tiers.yml
# forces every tier on Linux, Windows and macOS runners.
# Runs its natural tier only here; Makefile.test's
# threads-samples forces each tier in turn. On the
# runner's cores the fence check is load-bearing, which
# this box cannot make it.
retro_procbarrier_test
retro_asym_eventcount_test
sthread_ex_test
fast_cores_test
memshm_test
fastjmp_test
rdds_block_test
@@ -587,26 +554,6 @@ jobs:
make clean
CC=clang make check SANITIZER=thread
- name: Run rzip_parallel_test under Clang + ThreadSanitizer
shell: bash
working-directory: libretro-common/samples/streams/rzip
run: |
# rzip_stream.c's chunk-writer pool hands each slot between the
# writer and one worker on an acquire/release status word and
# parks both sides on retro_eventcount, with no lock anywhere.
# The value checks prove in-order emission; a weakened store
# or a lost wakeup still passes them on x86 TSO, and only TSan
# sees the missing barrier. The test drives the pool at every
# width and through two hundred short lifetimes so the
# start / park / shutdown edges are exercised often.
set -u
set -o pipefail
make clean
CC=clang make rzip_parallel_test SANITIZER=thread
TSAN_OPTIONS=halt_on_error=1 ./rzip_parallel_test
- name: Run crc32_test under Clang + ThreadSanitizer
shell: bash
working-directory: libretro-common/samples/encodings/crc32
@@ -743,7 +690,7 @@ jobs:
steps:
- name: Install dependencies
timeout-minutes: 20
timeout-minutes: 5
run: |
# clang ships on the stock ubuntu-latest image; apt is only
# contacted if the image ever drops it.
@@ -757,23 +704,6 @@ jobs:
echo "apt-get update failed (attempt $i); retrying in 10s"
sleep 10
done
# Fetch first, under a bound: a download is safe to stop and
# resumes, an install is not. A mirror that stalls is left for
# the main archive on the next attempt.
for i in 1 2 3; do
sudo timeout 240 apt-get install -y --download-only \
-o Acquire::Retries=3 -o Acquire::http::Timeout=30 \
clang && break
[ $i = 3 ] && exit 1
echo "package download failed or stalled (attempt $i); retrying from archive.ubuntu.com"
for f in /etc/apt/sources.list /etc/apt/sources.list.d/*; do
[ -f "$f" ] || continue
sudo sed -i -e 's#mirror+file:/etc/apt/apt-mirrors.txt#http://archive.ubuntu.com/ubuntu/#g' \
-e 's#http://azure\.archive\.ubuntu\.com/ubuntu#http://archive.ubuntu.com/ubuntu#g' "$f"
done
sudo timeout 90 apt-get update -y \
-o Acquire::Retries=3 -o Acquire::http::Timeout=30 || true
done
sudo apt-get install -y \
-o Acquire::Retries=3 -o Acquire::http::Timeout=30 \
clang
@@ -863,7 +793,7 @@ jobs:
steps:
- name: Install dependencies
timeout-minutes: 20
timeout-minutes: 5
run: |
for i in 1 2 3; do
sudo timeout 90 apt-get update -y \
@@ -871,23 +801,6 @@ jobs:
echo "apt-get update failed (attempt $i); retrying in 10s"
sleep 10
done
# Fetch first, under a bound: a download is safe to stop and
# resumes, an install is not. A mirror that stalls is left for
# the main archive on the next attempt.
for i in 1 2 3; do
sudo timeout 240 apt-get install -y --download-only \
-o Acquire::Retries=3 -o Acquire::http::Timeout=30 \
build-essential ${{ matrix.apt_pkgs }} qemu-user-static && break
[ $i = 3 ] && exit 1
echo "package download failed or stalled (attempt $i); retrying from archive.ubuntu.com"
for f in /etc/apt/sources.list /etc/apt/sources.list.d/*; do
[ -f "$f" ] || continue
sudo sed -i -e 's#mirror+file:/etc/apt/apt-mirrors.txt#http://archive.ubuntu.com/ubuntu/#g' \
-e 's#http://azure\.archive\.ubuntu\.com/ubuntu#http://archive.ubuntu.com/ubuntu#g' "$f"
done
sudo timeout 90 apt-get update -y \
-o Acquire::Retries=3 -o Acquire::http::Timeout=30 || true
done
sudo apt-get install -y \
-o Acquire::Retries=3 -o Acquire::http::Timeout=30 \
build-essential ${{ matrix.apt_pkgs }} qemu-user-static
@@ -1015,7 +928,7 @@ jobs:
steps:
- name: Install dependencies
timeout-minutes: 20
timeout-minutes: 5
run: |
for i in 1 2 3; do
sudo timeout 90 apt-get update -y \
@@ -1023,23 +936,6 @@ jobs:
echo "apt-get update failed (attempt $i); retrying in 10s"
sleep 10
done
# Fetch first, under a bound: a download is safe to stop and
# resumes, an install is not. A mirror that stalls is left for
# the main archive on the next attempt.
for i in 1 2 3; do
sudo timeout 240 apt-get install -y --download-only \
-o Acquire::Retries=3 -o Acquire::http::Timeout=30 \
build-essential ${{ matrix.apt_pkgs }} qemu-user-static && break
[ $i = 3 ] && exit 1
echo "package download failed or stalled (attempt $i); retrying from archive.ubuntu.com"
for f in /etc/apt/sources.list /etc/apt/sources.list.d/*; do
[ -f "$f" ] || continue
sudo sed -i -e 's#mirror+file:/etc/apt/apt-mirrors.txt#http://archive.ubuntu.com/ubuntu/#g' \
-e 's#http://azure\.archive\.ubuntu\.com/ubuntu#http://archive.ubuntu.com/ubuntu#g' "$f"
done
sudo timeout 90 apt-get update -y \
-o Acquire::Retries=3 -o Acquire::http::Timeout=30 || true
done
sudo apt-get install -y \
-o Acquire::Retries=3 -o Acquire::http::Timeout=30 \
build-essential ${{ matrix.apt_pkgs }} qemu-user-static
@@ -1084,27 +980,6 @@ jobs:
set -o pipefail
${{ matrix.qemu }} -L ${{ matrix.sysroot }} ./crc32_test
- name: Build and run pixconv_test under qemu-user
shell: bash
working-directory: libretro-common/samples/gfx/pixconv
run: |
set -euo pipefail
# The ARM legs are the only place the NEON converters run;
# PowerPC runs the scalar paths big-endian. The armhf
# toolchain defaults to VFP without NEON, so armv7 builds a
# second time with NEON on.
make clean
CC="${{ matrix.cc }}" CFLAGS="${{ matrix.cflags }}" make all
for t in pixconv_test pixconv_test_nosimd pixconv_test_scalar; do
${{ matrix.qemu }} -L ${{ matrix.sysroot }} ./$t
done
if [ "${{ matrix.name }}" = armv7 ]; then
make clean
CC="${{ matrix.cc }}" CFLAGS="-mfpu=neon" make all
${{ matrix.qemu }} -L ${{ matrix.sysroot }} ./pixconv_test
fi
make clean
- name: Run vfs_mapped_ptr_test without mappings (MMAP=0)
shell: bash
working-directory: libretro-common/samples/file/vfs
@@ -33,11 +33,11 @@ jobs:
steps:
- name: Install dependencies
timeout-minutes: 20
timeout-minutes: 8
run: |
# ffmpeg: tools/companion_thumbs_test.sh builds its video-hover
# fixture (a 2 KB Baseline MP4) with it.
pkgs="build-essential check pkg-config zlib1g-dev lcov gcc-mingw-w64-x86-64 gcc-mingw-w64-i686 wine wine64 ffmpeg gcc-aarch64-linux-gnu gcc-arm-linux-gnueabihf gcc-mipsel-linux-gnu qemu-user"
pkgs="build-essential check pkg-config zlib1g-dev lcov gcc-mingw-w64-x86-64 gcc-mingw-w64-i686 wine wine64 ffmpeg"
if dpkg -s $pkgs >/dev/null 2>&1; then
echo "All packages already present on runner image; skipping apt."
exit 0
@@ -48,23 +48,6 @@ jobs:
echo "apt-get update failed (attempt $i); retrying in 10s"
sleep 10
done
# Fetch first, under a bound: a download is safe to stop and
# resumes, an install is not. A mirror that stalls is left for
# the main archive on the next attempt.
for i in 1 2 3; do
sudo timeout 240 apt-get install -y --download-only \
-o Acquire::Retries=3 -o Acquire::http::Timeout=30 \
$pkgs && break
[ $i = 3 ] && exit 1
echo "package download failed or stalled (attempt $i); retrying from archive.ubuntu.com"
for f in /etc/apt/sources.list /etc/apt/sources.list.d/*; do
[ -f "$f" ] || continue
sudo sed -i -e 's#mirror+file:/etc/apt/apt-mirrors.txt#http://archive.ubuntu.com/ubuntu/#g' \
-e 's#http://azure\.archive\.ubuntu\.com/ubuntu#http://archive.ubuntu.com/ubuntu#g' "$f"
done
sudo timeout 90 apt-get update -y \
-o Acquire::Retries=3 -o Acquire::http::Timeout=30 || true
done
sudo apt-get install -y \
-o Acquire::Retries=3 -o Acquire::http::Timeout=30 \
$pkgs
@@ -124,15 +107,6 @@ jobs:
LIBCHECK_CFLAGS="$(pkg-config --cflags check)" \
CFLAGS="$(pkg-config --cflags check)"
echo "[pass] libretro-common unit test suites"
# The ARMv8 AES + PMULL path executes only on ARM; this
# runs the crypto suite under qemu-aarch64 in both the
# dispatched and the baseline-crypto shape.
make -f Makefile.test crypto-aarch64-check
make -f Makefile.test crypto-arm-check
make -f Makefile.test crypto-mips-check
# every lazily set up table or CPU check, first used by
# eight threads at once under TSan
make -f Makefile.test crypto-tsan-check
- name: Check VFS backend symbol parity
shell: bash
@@ -149,44 +123,6 @@ jobs:
# free, on Linux.
python3 tools/vfs_backend_parity.py
- name: Check every command describes itself
shell: bash
working-directory: ${{ github.workspace }}
run: |
set -eu
# HELP prints command.h's tables, and interfaces that hand the
# commands to a client are generated from them: a command
# added without a description reaches all of those blank.
python3 tools/command_table_check.py
- name: Check no video driver deletes overlay textures it was lent
shell: bash
run: |
set -eu
# load_textures lends a driver the overlay pack's handles for
# one page; they are on the pack's other pages too and are
# the pack's to delete. gl2 marked them as borrowed and then
# deleted them anyway at the next page, so rotating an
# Android device blacked the overlay out until the core was
# restarted. No runner can run a GL driver, so this is the
# check at source level: every driver with load_textures
# sets, tests and clears its borrowed marker.
python3 tools/overlay_texture_ownership.py
- name: Check overlay setters refuse an image the driver does not hold
shell: bash
run: |
set -eu
# set_alpha, vertex_geom and tex_geom are called whenever the
# layout, opacity or page changes, not only after a load()
# that worked: with no page the driver's arrays are NULL, and
# after a smaller page or a failed load the index is off the
# end. 7661ab06e7 bounded the GL geometry setters and missed
# set_alpha; D3D, Vulkan and Metal had no check at all. No
# runner can run these drivers, so this is the check at
# source level for the ones that have been audited.
python3 tools/overlay_setter_bounds.py
- name: Check companion backend feature parity
shell: bash
working-directory: ${{ github.workspace }}
@@ -27,7 +27,7 @@ jobs:
steps:
- name: Install dependencies
timeout-minutes: 20
timeout-minutes: 5
run: |
pkgs="build-essential zlib1g-dev"
if dpkg -s $pkgs >/dev/null 2>&1; then
@@ -40,23 +40,6 @@ jobs:
echo "apt-get update failed (attempt $i); retrying in 10s"
sleep 10
done
# Fetch first, under a bound: a download is safe to stop and
# resumes, an install is not. A mirror that stalls is left for
# the main archive on the next attempt.
for i in 1 2 3; do
sudo timeout 240 apt-get install -y --download-only \
-o Acquire::Retries=3 -o Acquire::http::Timeout=30 \
$pkgs && break
[ $i = 3 ] && exit 1
echo "package download failed or stalled (attempt $i); retrying from archive.ubuntu.com"
for f in /etc/apt/sources.list /etc/apt/sources.list.d/*; do
[ -f "$f" ] || continue
sudo sed -i -e 's#mirror+file:/etc/apt/apt-mirrors.txt#http://archive.ubuntu.com/ubuntu/#g' \
-e 's#http://azure\.archive\.ubuntu\.com/ubuntu#http://archive.ubuntu.com/ubuntu#g' "$f"
done
sudo timeout 90 apt-get update -y \
-o Acquire::Retries=3 -o Acquire::http::Timeout=30 || true
done
sudo apt-get install -y \
-o Acquire::Retries=3 -o Acquire::http::Timeout=30 \
$pkgs
+2 -134
View File
@@ -22,15 +22,6 @@ on:
- 'playlist.h'
- 'tasks/task_content*'
- 'samples/menu/playlist_nav/**'
- 'samples/menu/nav_repeat_stall/**'
- 'runloop.c'
- 'samples/menu/network_settings/**'
- 'intl/msg_hash_lbl.h'
- 'msg_hash_lbl_str.h'
- 'samples/menu/resolution_reset/**'
- 'samples/settings/packed_rows/**'
- 'setting_list.h'
- 'gfx/video_defines.h'
- '.github/workflows/Linux-menu-harness.yml'
pull_request:
paths:
@@ -39,15 +30,6 @@ on:
- 'playlist.h'
- 'tasks/task_content*'
- 'samples/menu/playlist_nav/**'
- 'samples/menu/nav_repeat_stall/**'
- 'runloop.c'
- 'samples/menu/network_settings/**'
- 'intl/msg_hash_lbl.h'
- 'msg_hash_lbl_str.h'
- 'samples/menu/resolution_reset/**'
- 'samples/settings/packed_rows/**'
- 'setting_list.h'
- 'gfx/video_defines.h'
- '.github/workflows/Linux-menu-harness.yml'
workflow_dispatch:
@@ -63,7 +45,7 @@ jobs:
steps:
- name: Install dependencies
timeout-minutes: 20
timeout-minutes: 5
run: |
pkgs="build-essential zlib1g-dev"
if dpkg -s $pkgs >/dev/null 2>&1; then
@@ -76,23 +58,6 @@ jobs:
echo "apt-get update failed (attempt $i); retrying in 10s"
sleep 10
done
# Fetch first, under a bound: a download is safe to stop and
# resumes, an install is not. A mirror that stalls is left for
# the main archive on the next attempt.
for i in 1 2 3; do
sudo timeout 240 apt-get install -y --download-only \
-o Acquire::Retries=3 -o Acquire::http::Timeout=30 \
$pkgs && break
[ $i = 3 ] && exit 1
echo "package download failed or stalled (attempt $i); retrying from archive.ubuntu.com"
for f in /etc/apt/sources.list /etc/apt/sources.list.d/*; do
[ -f "$f" ] || continue
sudo sed -i -e 's#mirror+file:/etc/apt/apt-mirrors.txt#http://archive.ubuntu.com/ubuntu/#g' \
-e 's#http://azure\.archive\.ubuntu\.com/ubuntu#http://archive.ubuntu.com/ubuntu#g' "$f"
done
sudo timeout 90 apt-get update -y \
-o Acquire::Retries=3 -o Acquire::http::Timeout=30 || true
done
sudo apt-get install -y \
-o Acquire::Retries=3 -o Acquire::http::Timeout=30 \
$pkgs
@@ -133,12 +98,10 @@ jobs:
#
# Qt in particular has to go: with it enabled main() lives
# in ui_qt.o, and the harness replaces main().
# Networking stays on: the network settings harness below
# needs the SMB and NFS client screens compiled in.
./configure --disable-qt --disable-x11 \
--disable-opengl --disable-opengl_core \
--disable-opengl1 --disable-vulkan \
--disable-cheevos \
--disable-cheevos --disable-networking \
--disable-ffmpeg
make -j$(nproc)
@@ -183,64 +146,6 @@ jobs:
./samples/menu/playlist_nav/menu_playlist_nav_test
make -C samples/menu/playlist_nav clean
- name: Build and run menu_nav_repeat_stall_test
shell: bash
run: |
set -eu
# One press on a menu entry whose frame stalls - Ozone and
# XMB build a large playlist synchronously when the selection
# lands on it - has to move the selection once. The menu acts
# on input one poll old, so the hold time has to be measured
# between polls or the stall fires an auto-repeat. Drives the
# real runloop with a scripted input driver; also checks that
# a hold across the stall still repeats and that a steady hold
# waits out the initial scroll delay. Shares the non-Qt build
# made above.
samples/menu/nav_repeat_stall/build.sh
./samples/menu/nav_repeat_stall/menu_nav_repeat_stall_test
make -C samples/menu/nav_repeat_stall clean
- name: Build and run menu_network_settings_test
shell: bash
run: |
set -eu
# The NFS Network Settings entry opened an empty "null"
# browser: the label its deferred list is pushed with had no
# row in msg_hash_lbl.h, so it resolved to "null" and the
# push found nothing. The harness resolves the SMB and NFS
# labels, opens Settings > Network and both screens through
# the real dispatch, and checks the Quick Menu no longer
# carries the two entries.
samples/menu/network_settings/build.sh
./samples/menu/network_settings/menu_network_settings_test
make -C samples/menu/network_settings clean
- name: Build and run resolution_reset_test
shell: bash
run: |
set -eu
# Start on the Resolution entry reinits and sets the video
# mode at the output size; it must keep the window state the
# frontend is in rather than take a windowed desktop session
# full screen. Shares the non-Qt build made above.
samples/menu/resolution_reset/build.sh
./samples/menu/resolution_reset/resolution_reset_test
make -C samples/menu/resolution_reset clean
- name: Build and run packed_rows_test
shell: bash
run: |
set -eu
# Settings rows that each own one half of a packed settings_t
# word - the custom viewport's origin and size, the window
# size pair - driven through their display strings, a right
# press and the dropdown list. Each row has to show and set
# its own axis and leave its partner as it stands. Shares the
# non-Qt build made above.
samples/settings/packed_rows/build.sh
./samples/settings/packed_rows/packed_rows_test
make -C samples/settings/packed_rows clean
- name: Hot function budget
shell: bash
run: |
@@ -252,17 +157,6 @@ jobs:
# this job already built.
.github/workflows/scripts/hot-function-budget.sh
- name: Lock budget
shell: bash
run: |
set -eu
# Lock acquisitions on the paths meant to take none, or a
# counted few, against tools/lock_acquire_budget.ref: a lock
# put back on a per-frame path passes every lane and shows up
# only as a slower frame. Same release objects as above; this
# build has no Vulkan, so the Vulkan entries report SKIP.
tools/lock_acquire_budget.sh
- name: Build and run threaded_video_test
shell: bash
run: |
@@ -280,29 +174,3 @@ jobs:
samples/gfx/threaded_video/build.sh
./samples/gfx/threaded_video/threaded_video_test 3
make -C samples/gfx/threaded_video clean
- name: Build and run content_load_test
shell: bash
run: |
set -eu
# The content load as a chain of main-thread tasks, against the
# real runloop on the same non-Qt menu build: frames presented
# between stages, one load at a time, a core's reinit deferred
# to the load's driver stage, the dummy-core fallback, a
# hardware-render request carried across the driver rebuild,
# the task queue surviving the load, and the close waiting out
# a save state from the frame loop.
samples/tasks/content_load/build.sh
./samples/tasks/content_load/content_load_test
make -C samples/tasks/content_load clean
- name: Build and run explore_build_test
shell: bash
run: |
set -eu
# The Explore index built in steps: one item per step hashes
# the same as the build in one go, and the explore task returns
# unfinished across checks with Threaded Tasks off.
samples/menu/explore_build/build.sh
./samples/menu/explore_build/explore_build_test
make -C samples/menu/explore_build clean
-222
View File
@@ -1,222 +0,0 @@
name: CI Linux network clients
# The built-in TLS, SMB2/3 and NFSv3 clients against real servers:
# openssl s_server, Samba, unfs3, nfs-ganesha and a MIT KDC on the runner, natively, as Windows
# builds under Wine, under ASan + UBSan + LSan, plus libFuzzer over the
# parsers. Its own job so the libretro-common unit-test job keeps its
# short budget.
on:
push:
branches:
- master
pull_request:
branches:
- master
workflow_dispatch:
permissions:
contents: read
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: ${{ github.ref != 'refs/heads/master' }}
jobs:
network-clients:
name: TLS, SMB and NFS clients against local servers
runs-on: ubuntu-latest
timeout-minutes: 45
steps:
- name: Install dependencies
timeout-minutes: 25
run: |
sudo apt-get update -y
# Fetch first, under a bound: a download is safe to stop and
# resumes, an install is not. A mirror that stalls is left for
# the main archive on the next attempt.
# tshark's dumpcap asks whether to be setuid: no, runs are root
echo "wireshark-common wireshark-common/install-setuid boolean false" | sudo debconf-set-selections
export DEBIAN_FRONTEND=noninteractive
for i in 1 2 3; do
sudo timeout 240 apt-get install -y --download-only --no-install-recommends build-essential clang \
gcc-mingw-w64-x86-64 openssl samba libtirpc-dev flex bison autoconf automake wine wine64 \
nfs-ganesha nfs-ganesha-vfs krb5-kdc krb5-admin-server krb5-user winbind \
samba-dsdb-modules samba-ad-provision samba-vfs-modules acl smbclient tshark && break
[ $i = 3 ] && exit 1
echo "package download failed or stalled (attempt $i); retrying from archive.ubuntu.com"
for f in /etc/apt/sources.list /etc/apt/sources.list.d/*; do
[ -f "$f" ] || continue
sudo sed -i -e 's#mirror+file:/etc/apt/apt-mirrors.txt#http://archive.ubuntu.com/ubuntu/#g' \
-e 's#http://azure\.archive\.ubuntu\.com/ubuntu#http://archive.ubuntu.com/ubuntu#g' "$f"
done
sudo timeout 90 apt-get update -y \
-o Acquire::Retries=3 -o Acquire::http::Timeout=30 || true
done
sudo apt-get install -y --no-install-recommends build-essential clang \
gcc-mingw-w64-x86-64 openssl samba libtirpc-dev flex bison autoconf automake wine wine64 \
nfs-ganesha nfs-ganesha-vfs krb5-kdc krb5-admin-server krb5-user winbind \
samba-dsdb-modules samba-ad-provision samba-vfs-modules acl smbclient tshark
- name: Checkout
uses: actions/checkout@v5
- name: Built-in TLS client against a local openssl server
shell: bash
working-directory: ${{ github.workspace }}
run: |
set -eu
# net_socket_ssl_retro.c end to end: every cipher suite, both
# curves, RSA / P-256 / P-384 certificates under a CA made on
# the spot, verify required / optional / disabled, and the
# refusals that must happen (wrong hostname, untrusted CA). An
# IP address matches only an iPAddress SAN entry, never the
# same address written as a DNS name.
samples/network/tls_retro/local_server_test.sh
# The same tool cross-linked for Windows: the sample's link
# line has to carry Winsock, which nothing on Linux checks.
make -C samples/network/tls_retro clean
- name: Built-in SMB2/3 client against a local smbd
shell: bash
working-directory: ${{ github.workspace }}
run: |
set -eu
# net_smb2.c end to end against Samba on the loopback: every
# dialect 2.0.2 to 3.1.1 with signing mandatory, AES-CCM and
# AES-GCM sealing required, and a refused logon. Root for
# port 445; the script itself skips without smbd.
sudo samples/network/smb_retro/local_samba_test.sh
# and with Kerberos against a Samba AD DC provisioned on the
# spot, NTLM disabled on it so only a ticket can log in
sudo samples/network/smb_retro/local_ad_dc_test.sh
sudo make -C samples/network/smb_retro clean
- name: Windows builds under Wine
shell: bash
working-directory: ${{ github.workspace }}
run: |
set -eu
# The MinGW builds run, not just link: the crypto and keychain
# suites, then the three server matrices with the Windows tools
# talking to the same local openssl, Samba and unfs3.
export WINEDEBUG=-all WINEPREFIX=$RUNNER_TEMP/wineprefix
# the tools below are run through wine, so it must be there
command -v wine >/dev/null
make -C libretro-common -f Makefile.test win32-wine-check
export CC=x86_64-w64-mingw32-gcc RUNNER=wine EXE=.exe
make -C samples/network/tls_retro clean
samples/network/tls_retro/local_server_test.sh
make -C samples/network/tls_retro clean
make -C samples/network/smb_retro clean
# smbd needs root for port 445, and Wine refuses both a prefix
# owned by someone else and creating one inside a directory it
# does not own ($RUNNER_TEMP belongs to the runner user), so the
# root run gets a prefix under /tmp with root's own HOME
sudo -E env HOME=/root WINEPREFIX=/tmp/wineprefix-root wineboot -u
sudo -E env HOME=/root WINEPREFIX=/tmp/wineprefix-root samples/network/smb_retro/local_samba_test.sh
sudo -E env HOME=/root WINEPREFIX=/tmp/wineprefix-root samples/network/smb_retro/local_ad_dc_test.sh
sudo make -C samples/network/smb_retro clean
make -C samples/network/nfs_retro clean
samples/network/nfs_retro/local_unfs3_test.sh
make -C samples/network/nfs_retro clean
sudo -E env HOME=/root WINEPREFIX=/tmp/wineprefix-root samples/network/nfs_retro/local_ganesha_test.sh
sudo make -C samples/network/nfs_retro clean
sudo -E env HOME=/root WINEPREFIX=/tmp/wineprefix-root samples/network/nfs_retro/local_read_plus_test.sh
sudo make -C samples/network/nfs_retro clean
make -C samples/network/krb5_retro clean
samples/network/krb5_retro/local_kdc_test.sh
make -C samples/network/krb5_retro clean
- name: Fuzz the network parsers
shell: bash
working-directory: ${{ github.workspace }}
run: |
set -eu
# libFuzzer over the X.509 parser and the TLS, SMB and NFS
# clients' receive paths (a listener in the harness answers the
# client with the fuzzer's input), under ASan + UBSan, from a
# seed corpus recorded off real servers. Any crash, timeout or
# leak artifact fails the step.
make -C libretro-common/test/fuzz fuzz-run FUZZ_SECONDS=30
if ls libretro-common/test/fuzz/crash-* libretro-common/test/fuzz/leak-* libretro-common/test/fuzz/timeout-* >/dev/null 2>&1; then
echo "fuzzer artifacts found"; exit 1
fi
make -C libretro-common/test/fuzz clean
- name: Network client matrices under ASan + UBSan + LSan
shell: bash
working-directory: ${{ github.workspace }}
run: |
set -eu
# The three server matrices again, with the clients built under
# the sanitizers: every code path a real server drives, checked
# for memory errors, undefined behaviour and leaks. UBSan found
# a NULL memcpy in the SHA-2 stream updates on the SHA-384 PRF
# path the first time this ran.
export CFLAGS="-O1 -g -fsanitize=address,undefined -fno-sanitize-recover=all -fno-omit-frame-pointer"
export LDFLAGS="-fsanitize=address,undefined"
export ASAN_OPTIONS=detect_leaks=1:strict_string_checks=1
export UBSAN_OPTIONS=print_stacktrace=1
make -C samples/network/tls_retro clean
samples/network/tls_retro/local_server_test.sh
make -C samples/network/tls_retro clean
make -C samples/network/smb_retro clean
sudo -E samples/network/smb_retro/local_samba_test.sh
sudo -E samples/network/smb_retro/local_ad_dc_test.sh
make -C samples/network/smb_retro clean
make -C samples/network/nfs_retro clean
samples/network/nfs_retro/local_unfs3_test.sh
make -C samples/network/nfs_retro clean
sudo -E samples/network/nfs_retro/local_ganesha_test.sh
sudo make -C samples/network/nfs_retro clean
sudo -E samples/network/nfs_retro/local_read_plus_test.sh
sudo make -C samples/network/nfs_retro clean
make -C samples/network/krb5_retro clean
samples/network/krb5_retro/local_kdc_test.sh
make -C samples/network/krb5_retro clean
- name: Built-in NFSv3 client against unfs3
shell: bash
working-directory: ${{ github.workspace }}
run: |
set -eu
# net_nfs3.c end to end against a user-space NFSv3 server built
# from unfs3's repository (Ubuntu no longer packages it), on
# fixed ports without a portmapper: mount, write, read, seek,
# stat, mkdir, rename, truncate, list, unlink, refused export.
samples/network/nfs_retro/local_unfs3_test.sh
# NFSv4.0 (one COMPOUND per operation, no portmapper or MOUNT,
# pseudo-filesystem export) against nfs-ganesha from apt
sudo -E samples/network/nfs_retro/local_ganesha_test.sh
# NFSv4.2 READ_PLUS against a fake server reporting the holes
# the filesystem really holds, its replies checked by
# Wireshark's own dissector
sudo make -C samples/network/nfs_retro clean
sudo -E samples/network/nfs_retro/local_read_plus_test.sh
# and its Windows link line, cross-linked
sudo make -C samples/network/nfs_retro clean
CC=x86_64-w64-mingw32-gcc make -C samples/network/nfs_retro nfs_test
make -C samples/network/nfs_retro clean
CC=x86_64-w64-mingw32-gcc make -C samples/network/tls_retro tls_fetch
make -C samples/network/tls_retro clean
- name: Built-in Kerberos client against a MIT KDC
shell: bash
working-directory: ${{ github.workspace }}
run: |
set -eu
# net_krb5.c against krb5kdc on a fixed port: AS with and
# without PA-ENC-TIMESTAMP, TGS for a cifs/ service with the
# ticket checked against the service's keytab key, wrong
# password and unknown service refused.
samples/network/krb5_retro/local_kdc_test.sh
make -C samples/network/krb5_retro clean
CC=x86_64-w64-mingw32-gcc make -C samples/network/krb5_retro krb5_test
make -C samples/network/krb5_retro clean
-107
View File
@@ -1,107 +0,0 @@
name: CI Linux without pkg-config
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:
no-pkg-config:
name: Configure and build with pkg-config unavailable
runs-on: ubuntu-latest
timeout-minutes: 30
steps:
- name: Install dependencies
shell: bash
run: |
set -eu
sudo apt-get update -qq
sudo apt-get install -y -qq --no-install-recommends \
build-essential zlib1g-dev libfreetype-dev libdrm-dev \
libasound2-dev libpulse-dev libpipewire-0.3-dev \
libdbus-1-dev libwayland-dev libwayland-bin libxkbcommon-dev \
libdecor-0-dev libssl-dev qt6-base-dev qt6-base-dev-tools
- name: Checkout
uses: actions/checkout@v5
- name: Configure
shell: bash
run: |
set -eu
# PKG_CONF_PATH=none makes configure behave as it does on a
# system with no pkg-config binary: every check_val falls
# back to a plain link test and takes its cflags from the
# include directories it is given. A library that links but
# whose headers are not on the include path must end up
# disabled, never enabled with empty cflags.
PKG_CONF_PATH=none ./configure --enable-dbus
grep -q '^HAVE_PIPEWIRE = 1' config.mk
grep -q '^HAVE_PIPEWIRE_STABLE = 1' config.mk
grep '^PIPEWIRE_CFLAGS = ' config.mk | grep -q -- '-I/usr/include/pipewire-0.3'
grep '^PIPEWIRE_CFLAGS = ' config.mk | grep -q -- '-I/usr/include/spa-0.2'
# Found without pkg-config: D-Bus by its headers, one of them
# under the library directory; Wayland by its libraries and the
# scanner's own version; Qt through qmake -query.
for have in DBUS WAYLAND WAYLAND_SCANNER LIBDECOR OPENSSL QT QT6; do
grep -q "^HAVE_$have = 1" config.mk \
|| { echo "::error::HAVE_$have not found without pkg-config"; exit 1; }
done
grep '^DBUS_CFLAGS = ' config.mk | grep -q -- '-I/usr/include/dbus-1.0'
grep '^DBUS_CFLAGS = ' config.mk | grep -q -- 'dbus-1.0/include'
- name: Build
shell: bash
run: |
set -eu
make -j"$(nproc)"
test -x ./retroarch
test -f obj-unix/release/audio/drivers/pipewire.o
test -f obj-unix/release/camera/drivers/pipewire.o
test -f obj-unix/release/gfx/common/dbus_common.o
test -f obj-unix/release/gfx/common/wayland_common.o
test -f obj-unix/release/ui/drivers/ui_qt.o
- name: The run-time libdbus mirror matches libdbus
shell: bash
run: |
set -eu
# gfx/common/dbus_runtime.h mirrors libdbus's public structs and
# constants so RetroArch can load it without its headers; the
# D-Bus CFLAGS configure found above lead to the real ones.
make -C samples/frontend/dbus_runtime check \
DBUS_CFLAGS="$(sed -n 's/^DBUS_CFLAGS = //p' config.mk)"
- name: A package forced by name fails when nothing can find it
shell: bash
run: |
set -u
# rsound is found only through pkg-config. Asked for by name with
# no pkg-config, configure must stop and say so rather than
# quietly build without it.
if PKG_CONF_PATH=none ./configure --enable-rsound > forced.log 2>&1; then
cat forced.log
echo "::error::--enable-rsound without pkg-config configured instead of failing"
exit 1
fi
grep -q 'Forced to build with package rsound, but pkg-config was not found' forced.log \
|| { cat forced.log; echo "::error::configure failed for another reason"; exit 1; }
# A forced package with a library check behind it still configures.
PKG_CONF_PATH=none ./configure --enable-pulse > forced.log 2>&1 \
|| { cat forced.log; echo "::error::--enable-pulse failed without pkg-config"; exit 1; }
grep -q '^HAVE_PULSE = 1' config.mk
@@ -1,178 +0,0 @@
name: CI Linux overlay on screen
# The one overlay test that looks at the screen. It builds a real
# retroarch with X11 and OpenGL, runs it under Xvfb on Mesa's software
# rasteriser with an input overlay enabled, threaded video off and then
# on, grabs the screen and checks the overlay is where its config puts
# it and nowhere else. It then checks the menu's mouse cursor with an
# overlay up: a page of "nul" buttons (an LED overlay) must leave the
# cursor to the menu, a page that takes input must not. Last, with the
# overlay LED driver on, it checks that only LED images are hidden and
# that a hidden one stays hidden while its button is held.
#
# Every stubbed overlay test passed while an enabled overlay covered the
# whole screen (2026-09-19). Nothing that stubs the driver, or builds
# the pack by hand, can see that; this can.
on:
push:
branches:
- master
paths:
- 'input/input_driver.c'
- 'input/input_overlay*'
- 'tasks/task_overlay.c'
- 'led/drivers/led_overlay.c'
- 'gfx/gfx_surface.*'
- 'gfx/video_driver.c'
- 'gfx/video_thread_wrapper.*'
- 'gfx/drivers/gl2.c'
- 'menu/menu_driver.c'
- 'input/drivers/x11_input.c'
- 'samples/input/overlay_on_screen/**'
- '.github/workflows/Linux-overlay-on-screen.yml'
pull_request:
paths:
- 'input/input_driver.c'
- 'input/input_overlay*'
- 'tasks/task_overlay.c'
- 'led/drivers/led_overlay.c'
- 'gfx/gfx_surface.*'
- 'gfx/video_driver.c'
- 'gfx/video_thread_wrapper.*'
- 'gfx/drivers/gl2.c'
- 'menu/menu_driver.c'
- 'input/drivers/x11_input.c'
- 'samples/input/overlay_on_screen/**'
- '.github/workflows/Linux-overlay-on-screen.yml'
workflow_dispatch:
permissions:
contents: read
# Only pull requests cancel a superseded run. On master every push
# gets its own group (keyed by the commit), so a burst of pushes queues
# instead of cancelling the run for the commit before it.
concurrency:
group: ${{ github.workflow }}-${{ github.event_name == 'pull_request' && github.ref || github.sha }}
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
jobs:
overlay-on-screen:
name: Run retroarch under Xvfb and look at the overlay
runs-on: ubuntu-latest
timeout-minutes: 25
steps:
- name: Install dependencies
timeout-minutes: 20
run: |
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
# Fetch first, under a bound: a download is safe to stop and
# resumes, an install is not. A mirror that stalls is left for
# the main archive on the next attempt.
for i in 1 2 3; do
sudo timeout 240 apt-get install -y --download-only \
-o Acquire::Retries=3 -o Acquire::http::Timeout=30 \
build-essential zlib1g-dev ccache python3 \
xvfb x11-apps libgl1-mesa-dri \
libx11-dev libx11-xcb-dev libxcb1-dev libxext-dev libxtst6 \
libxxf86vm-dev libxrandr-dev libxinerama-dev \
libgl-dev libegl-dev && break
[ $i = 3 ] && exit 1
echo "package download failed or stalled (attempt $i); retrying from archive.ubuntu.com"
for f in /etc/apt/sources.list /etc/apt/sources.list.d/*; do
[ -f "$f" ] || continue
sudo sed -i -e 's#mirror+file:/etc/apt/apt-mirrors.txt#http://archive.ubuntu.com/ubuntu/#g' \
-e 's#http://azure\.archive\.ubuntu\.com/ubuntu#http://archive.ubuntu.com/ubuntu#g' "$f"
done
sudo timeout 90 apt-get update -y \
-o Acquire::Retries=3 -o Acquire::http::Timeout=30 || true
done
sudo apt-get install -y \
-o Acquire::Retries=3 -o Acquire::http::Timeout=30 \
build-essential zlib1g-dev ccache python3 \
xvfb x11-apps libgl1-mesa-dri \
libx11-dev libx11-xcb-dev libxcb1-dev libxext-dev libxtst6 \
libxxf86vm-dev libxrandr-dev libxinerama-dev \
libgl-dev libegl-dev
- 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
export CCACHE_DIR="$HOME/.ccache"
echo "CCACHE_DIR=$CCACHE_DIR" >> "$GITHUB_ENV"
echo "/usr/lib/ccache" >> "$GITHUB_PATH"
ccache -M 500M
ccache -z
- name: Build RetroArch (X11 + OpenGL, the rest off)
shell: bash
run: |
set -eu
# What draws the overlay has to be real: X11, the gl driver,
# the menu, threads. Everything else is build time.
./configure --disable-qt --disable-wayland --disable-sdl \
--disable-sdl2 --disable-ffmpeg --disable-vulkan \
--disable-pulse --disable-discord \
--disable-networking --disable-cheevos \
--disable-glslang --disable-slang \
--disable-spirv_cross --disable-xmb \
--disable-materialui --disable-ozone \
--disable-7zip --disable-flac \
--disable-audiomixer --disable-translate \
--disable-accessibility --disable-crtswitchres \
--disable-langextra --disable-libretrodb \
--disable-video_filter --disable-dsp_filter
make -j$(nproc)
ccache -s
- name: Run overlay_on_screen_test (threaded video off, then on)
shell: bash
run: |
set -eu
# Two solid magenta squares on a full-screen page. Each must
# be where the config puts it, the middle of the screen must
# not be overlay, and the overlay must cover about the area of
# its two squares. With "does this desc have an image" asked
# of pixels that had just been released, no desc's geometry
# was set and the overlay covered 100% of the screen, in both
# modes.
timeout 180 python3 samples/input/overlay_on_screen/overlay_on_screen_test.py ./retroarch
- name: Run overlay_menu_mouse_test (display and input packs, threaded off and on)
shell: bash
run: |
set -eu
# The menu switched its mouse off whenever any overlay was
# alive, so an LED overlay - nothing on it to press - left a
# fullscreen desktop user with no menu cursor. A pack of
# "nul" buttons must now show the cursor where the pointer is
# moved; the same pack bound to "a" must still not.
timeout 180 python3 samples/input/overlay_on_screen/overlay_menu_mouse_test.py ./retroarch
- name: Run overlay_led_hidden_test (mapped and named LED packs, threaded off and on)
shell: bash
run: |
set -eu
# With the overlay LED driver on and Up held through XTEST: an
# image at a ledN_map slot, "nul" or a control, stays hidden,
# as does one named with _led, and a pack that names its LEDs
# is not mapped.
timeout 300 python3 samples/input/overlay_on_screen/overlay_led_hidden_test.py ./retroarch
+1 -18
View File
@@ -39,7 +39,7 @@ jobs:
steps:
- name: Install dependencies
timeout-minutes: 20
timeout-minutes: 5
run: |
pkgs="build-essential libxkbcommon-dev libx11-xcb-dev xvfb x11-utils zlib1g-dev libfreetype6-dev libegl1-mesa-dev libgles2-mesa-dev libgbm-dev libavcodec-dev libsdl2-dev libsdl-image1.2-dev libxml2-dev yasm"
if dpkg -s $pkgs >/dev/null 2>&1; then
@@ -52,23 +52,6 @@ jobs:
echo "apt-get update failed (attempt $i); retrying in 10s"
sleep 10
done
# Fetch first, under a bound: a download is safe to stop and
# resumes, an install is not. A mirror that stalls is left for
# the main archive on the next attempt.
for i in 1 2 3; do
sudo timeout 240 apt-get install -y --download-only \
-o Acquire::Retries=3 -o Acquire::http::Timeout=30 \
$pkgs && break
[ $i = 3 ] && exit 1
echo "package download failed or stalled (attempt $i); retrying from archive.ubuntu.com"
for f in /etc/apt/sources.list /etc/apt/sources.list.d/*; do
[ -f "$f" ] || continue
sudo sed -i -e 's#mirror+file:/etc/apt/apt-mirrors.txt#http://archive.ubuntu.com/ubuntu/#g' \
-e 's#http://azure\.archive\.ubuntu\.com/ubuntu#http://archive.ubuntu.com/ubuntu#g' "$f"
done
sudo timeout 90 apt-get update -y \
-o Acquire::Retries=3 -o Acquire::http::Timeout=30 || true
done
sudo apt-get install -y \
-o Acquire::Retries=3 -o Acquire::http::Timeout=30 \
$pkgs
+1 -18
View File
@@ -35,7 +35,7 @@ jobs:
timeout-minutes: 30
steps:
- name: Dependencies
timeout-minutes: 20
timeout-minutes: 5
run: |
pkgs="build-essential pkg-config zlib1g-dev qt6-base-dev libgl-dev"
if dpkg -s $pkgs >/dev/null 2>&1; then
@@ -48,23 +48,6 @@ jobs:
echo "apt-get update failed (attempt $i); retrying in 10s"
sleep 10
done
# Fetch first, under a bound: a download is safe to stop and
# resumes, an install is not. A mirror that stalls is left for
# the main archive on the next attempt.
for i in 1 2 3; do
sudo timeout 240 apt-get install -y --download-only \
-o Acquire::Retries=3 -o Acquire::http::Timeout=30 \
$pkgs && break
[ $i = 3 ] && exit 1
echo "package download failed or stalled (attempt $i); retrying from archive.ubuntu.com"
for f in /etc/apt/sources.list /etc/apt/sources.list.d/*; do
[ -f "$f" ] || continue
sudo sed -i -e 's#mirror+file:/etc/apt/apt-mirrors.txt#http://archive.ubuntu.com/ubuntu/#g' \
-e 's#http://azure\.archive\.ubuntu\.com/ubuntu#http://archive.ubuntu.com/ubuntu#g' "$f"
done
sudo timeout 90 apt-get update -y \
-o Acquire::Retries=3 -o Acquire::http::Timeout=30 || true
done
sudo apt-get install -y \
-o Acquire::Retries=3 -o Acquire::http::Timeout=30 \
$pkgs
+1 -66
View File
@@ -27,7 +27,7 @@ jobs:
steps:
- name: Install dependencies
timeout-minutes: 20
timeout-minutes: 5
run: |
pkgs="build-essential libasound2-dev libsdl2-dev libopenal-dev"
if dpkg -s $pkgs >/dev/null 2>&1; then
@@ -40,23 +40,6 @@ jobs:
echo "apt-get update failed (attempt $i); retrying in 10s"
sleep 10
done
# Fetch first, under a bound: a download is safe to stop and
# resumes, an install is not. A mirror that stalls is left for
# the main archive on the next attempt.
for i in 1 2 3; do
sudo timeout 240 apt-get install -y --download-only \
-o Acquire::Retries=3 -o Acquire::http::Timeout=30 \
$pkgs && break
[ $i = 3 ] && exit 1
echo "package download failed or stalled (attempt $i); retrying from archive.ubuntu.com"
for f in /etc/apt/sources.list /etc/apt/sources.list.d/*; do
[ -f "$f" ] || continue
sudo sed -i -e 's#mirror+file:/etc/apt/apt-mirrors.txt#http://archive.ubuntu.com/ubuntu/#g' \
-e 's#http://azure\.archive\.ubuntu\.com/ubuntu#http://archive.ubuntu.com/ubuntu#g' "$f"
done
sudo timeout 90 apt-get update -y \
-o Acquire::Retries=3 -o Acquire::http::Timeout=30 || true
done
sudo apt-get install -y \
-o Acquire::Retries=3 -o Acquire::http::Timeout=30 \
$pkgs
@@ -572,24 +555,6 @@ jobs:
./device_list_driver_test
echo "[pass] device_list_driver_test (ASan)"
- name: Build and run device_label_path_test (ASan + UBSan)
shell: bash
working-directory: samples/audio/device_label_path
run: |
set -eu
# A device list entry with the hardware's name after the path,
# "/dev/dsp0 (HDA Intel)", is stored by the menu as shown and
# comes back to init as the device string; oss opened it as a
# path and failed, so a device picked from the list could not
# be used. Compiles oss and audioio whole with open() redirected
# to a recorder, and checks each reaches it with the path.
make clean && make -j$(nproc) all SANITIZER=address,undefined
test -x device_label_path_test
ASAN_OPTIONS=detect_leaks=1:detect_stack_use_after_return=1 \
UBSAN_OPTIONS=print_stacktrace=1 timeout 60 \
./device_label_path_test
echo "[pass] device_label_path_test (ASan)"
- name: Build and run rate_control_bounds_test (ASan + UBSan)
shell: bash
working-directory: samples/audio/rate_control_bounds
@@ -778,36 +743,6 @@ jobs:
WINEDEBUG=-all timeout 300 make check
echo "[pass] dsound_pump_test (wine)"
- name: Build and run wdmks_loop_test (MinGW, under wine)
shell: bash
working-directory: samples/audio/wdmks_loop
run: |
set -eu
# audio/drivers/wdmks.c compiled whole, its WaveRT loop driven
# by hand: the test owns the driver's state, points the
# position register at a word of its own and moves the
# hardware itself, so nothing reaches the kernel and wine
# needs no sound card. Pins that every byte the cursor passes
# is cleared, so the loop plays silence when the frontend
# stops writing - a pause, a menu, a stall - instead of the
# last loop of audio round and round; that a write after
# that lands a margin ahead of the hardware rather than a
# loop behind it; that a full loop is not taken for an
# overrun; and that a start puts the model back at the top
# of the loop with the hardware; and that the resync margin is
# the FIFO the pin reports plus half a millisecond, two
# milliseconds where it reports none; and that gaps in the
# audio are counted once each, neither a start nor every
# sample of a pause.
if ! command -v x86_64-w64-mingw32-gcc >/dev/null 2>&1 \
|| ! command -v wine >/dev/null 2>&1; then
sudo apt-get install -y -qq gcc-mingw-w64-x86-64 wine64 \
-o Acquire::Retries=3 -o Acquire::http::Timeout=30
fi
make clean all
WINEDEBUG=-all timeout 120 make check
echo "[pass] wdmks_loop_test (wine)"
- name: Build and run wasapi_exclusive_pacing_test (ASan + UBSan, then TSan)
shell: bash
working-directory: samples/audio/wasapi_exclusive_pacing
+1 -18
View File
@@ -41,7 +41,7 @@ jobs:
steps:
- name: Install dependencies
timeout-minutes: 20
timeout-minutes: 5
run: |
pkgs="build-essential pkg-config ffmpeg libavcodec-dev libavformat-dev libavutil-dev libswscale-dev libswresample-dev"
if dpkg -s $pkgs >/dev/null 2>&1; then
@@ -54,23 +54,6 @@ jobs:
echo "apt-get update failed (attempt $i); retrying in 10s"
sleep 10
done
# Fetch first, under a bound: a download is safe to stop and
# resumes, an install is not. A mirror that stalls is left for
# the main archive on the next attempt.
for i in 1 2 3; do
sudo timeout 240 apt-get install -y --download-only \
-o Acquire::Retries=3 -o Acquire::http::Timeout=30 \
$pkgs && break
[ $i = 3 ] && exit 1
echo "package download failed or stalled (attempt $i); retrying from archive.ubuntu.com"
for f in /etc/apt/sources.list /etc/apt/sources.list.d/*; do
[ -f "$f" ] || continue
sudo sed -i -e 's#mirror+file:/etc/apt/apt-mirrors.txt#http://archive.ubuntu.com/ubuntu/#g' \
-e 's#http://azure\.archive\.ubuntu\.com/ubuntu#http://archive.ubuntu.com/ubuntu#g' "$f"
done
sudo timeout 90 apt-get update -y \
-o Acquire::Retries=3 -o Acquire::http::Timeout=30 || true
done
sudo apt-get install -y \
-o Acquire::Retries=3 -o Acquire::http::Timeout=30 \
$pkgs
+1 -18
View File
@@ -27,7 +27,7 @@ jobs:
steps:
- name: Install dependencies
timeout-minutes: 20
timeout-minutes: 5
run: |
pkgs="build-essential ffmpeg"
if dpkg -s $pkgs >/dev/null 2>&1; then
@@ -40,23 +40,6 @@ jobs:
echo "apt-get update failed (attempt $i); retrying in 10s"
sleep 10
done
# Fetch first, under a bound: a download is safe to stop and
# resumes, an install is not. A mirror that stalls is left for
# the main archive on the next attempt.
for i in 1 2 3; do
sudo timeout 240 apt-get install -y --download-only \
-o Acquire::Retries=3 -o Acquire::http::Timeout=30 \
$pkgs && break
[ $i = 3 ] && exit 1
echo "package download failed or stalled (attempt $i); retrying from archive.ubuntu.com"
for f in /etc/apt/sources.list /etc/apt/sources.list.d/*; do
[ -f "$f" ] || continue
sudo sed -i -e 's#mirror+file:/etc/apt/apt-mirrors.txt#http://archive.ubuntu.com/ubuntu/#g' \
-e 's#http://azure\.archive\.ubuntu\.com/ubuntu#http://archive.ubuntu.com/ubuntu#g' "$f"
done
sudo timeout 90 apt-get update -y \
-o Acquire::Retries=3 -o Acquire::http::Timeout=30 || true
done
sudo apt-get install -y \
-o Acquire::Retries=3 -o Acquire::http::Timeout=30 \
$pkgs
+100 -533
View File
@@ -20,31 +20,6 @@ concurrency:
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
@@ -87,11 +62,10 @@ jobs:
steps:
- name: Install dependencies
timeout-minutes: 20
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."
@@ -103,23 +77,6 @@ jobs:
echo "apt-get update failed (attempt $i); retrying in 10s"
sleep 10
done
# Fetch first, under a bound: a download is safe to stop and
# resumes, an install is not. A mirror that stalls is left for
# the main archive on the next attempt.
for i in 1 2 3; do
sudo timeout 240 apt-get install -y --download-only \
-o Acquire::Retries=3 -o Acquire::http::Timeout=30 \
$pkgs && break
[ $i = 3 ] && exit 1
echo "package download failed or stalled (attempt $i); retrying from archive.ubuntu.com"
for f in /etc/apt/sources.list /etc/apt/sources.list.d/*; do
[ -f "$f" ] || continue
sudo sed -i -e 's#mirror+file:/etc/apt/apt-mirrors.txt#http://archive.ubuntu.com/ubuntu/#g' \
-e 's#http://azure\.archive\.ubuntu\.com/ubuntu#http://archive.ubuntu.com/ubuntu#g' "$f"
done
sudo timeout 90 apt-get update -y \
-o Acquire::Retries=3 -o Acquire::http::Timeout=30 || true
done
sudo apt-get install -y \
-o Acquire::Retries=3 -o Acquire::http::Timeout=30 \
$pkgs
@@ -223,6 +180,51 @@ jobs:
done
echo "[pass] atomic backend portability"
- name: Build and run vblank_clock_ddraw_test (MinGW, under wine)
shell: bash
working-directory: samples/gfx/vblank_clock_ddraw
run: |
set -eu
# Scanline Sync's Windows 2000/XP tier against a real DirectDraw
# (wine's, which reports the counter as an XDDM driver does):
# GetScanLine readings into vblank_sampler_feed and the vblank
# clock, and the clock's beam checked against GetScanLine.
if ! command -v x86_64-w64-mingw32-gcc >/dev/null 2>&1 \
|| ! command -v wine >/dev/null 2>&1 \
|| ! command -v xvfb-run >/dev/null 2>&1; then
sudo apt-get install -y -qq gcc-mingw-w64-x86-64 wine64 xvfb \
-o Acquire::Retries=3 -o Acquire::http::Timeout=30
fi
make clean all
WINEDEBUG=-all timeout 300 xvfb-run -a -s "-screen 0 1280x1024x24" make check
echo "[pass] vblank_clock_ddraw_test (wine)"
- name: Build and run vblank_clock_test and vblank_clock_pub_test (C89, ASan + UBSan, TSan)
shell: bash
working-directory: samples/gfx/vblank_clock
run: |
set -eu
# gfx/common/vblank_clock.h: the arithmetic Scanline Sync on
# Windows uses in place of polling D3DKMTGetScanLine - a clock
# fed vblank timestamps from a waiter that only ever wakes
# late, and the beam position it implies. Against 50 simulated
# displays up to 600 ppm off their nominal rate: no beam
# before it settles, beam and wait landing within 6 lines,
# missed and duplicate vblanks, a stale clock, and variable
# refresh never reported.
make clean all SANITIZER=address,undefined
UBSAN_OPTIONS=print_stacktrace=1:halt_on_error=1 timeout 60 ./vblank_clock_test
echo "[pass] vblank_clock_test"
# The seqlock handing the clock from the kernel-event thread to
# the frame thread: one writer publishing flat out, three
# readers checking every snapshot is one whole publish and
# none goes backwards - under ASan+UBSan, then TSan, which
# also checks the pattern is free of formal data races.
UBSAN_OPTIONS=print_stacktrace=1:halt_on_error=1 timeout 120 ./vblank_clock_pub_test
make clean all SANITIZER=thread
TSAN_OPTIONS=halt_on_error=1 timeout 300 ./vblank_clock_pub_test
echo "[pass] vblank_clock_pub_test (ASan, TSan)"
- name: Build and run vulkan_extension_count_test (ASan)
shell: bash
working-directory: samples/gfx/vulkan_extension_count
@@ -376,20 +378,20 @@ jobs:
set -eu
# Regression test for the partial-init leak fix in
# gfx/common/vulkan_common.c::vulkan_emulated_mailbox_init.
# The function brings up three things in sequence (two
# eventcounts and the acquire thread; a condition variable,
# a mutex and the thread when this was written) and used to
# return `false` directly when the second or third failed,
# leaking what the earlier ones had allocated. Both
# production call sites in vulkan_create_swapchain ignore
# the return value, so an init failure also left the
# mailbox half built while VK_DATA_FLAG_EMULATING_MAILBOX
# was still set, setting up a crash the next time
# vulkan_acquire_next_image routed into the emulated path.
# Fix routes every early failure through `goto error` to a
# single deinit call, which is safe on a partly built
# mailbox and ends with a memset, leaving the struct in the
# same shape the deinit-on-shutdown path produces and
# 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
@@ -483,19 +485,42 @@ jobs:
working-directory: samples/gfx/gfx_widgets_msg_queue_race
run: |
set -eu
# The widget message ring in gfx/gfx_widgets.c is a lock-free
# single-producer / single-consumer queue: the main thread
# publishes through msg_queue_tail, the widgets' owner pops
# through msg_queue_head, each cursor released by its writer
# and acquired by the other side, and the slot pointer rides
# that pairing. The test builds the production push and pop
# out of gfx_widgets.c itself and drives them from one producer
# thread and one consumer thread, checking order, loss, and the
# full-ring refusal. ThreadSanitizer is the validator that
# matters: a store to the slot array outside the release /
# acquire pairing, or a cursor published before its slot is
# written, is a race it reports and the value checks may not.
# halt_on_error=1 fails the run on the first race.
# 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 \
@@ -576,16 +601,6 @@ jobs:
TSAN_OPTIONS=halt_on_error=1 timeout 120 \
./gfx_widgets_sanitize_test
echo "[pass] gfx_widgets_sanitize_test (TSan)"
- name: Build and run widget snapshot ownership test
shell: bash
working-directory: tests-other
run: |
set -eu
make -f Makefile.widget_snapshot clean
make -f Makefile.widget_snapshot
timeout 60 ./widget_snapshot_ownership_test
make -f Makefile.widget_snapshot clean
- name: Build and run gfx_widgets_task_bar_stack_test (ASan + UBSan, TSan)
shell: bash
working-directory: samples/gfx/gfx_widgets_task_bar_stack
@@ -637,79 +652,6 @@ jobs:
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: |
@@ -722,10 +664,7 @@ jobs:
# 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
# field, switch again, restore - and 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.
@@ -750,18 +689,6 @@ jobs:
&& 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 \
@@ -819,80 +746,6 @@ jobs:
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
@@ -929,125 +782,6 @@ jobs:
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_cursor_test (ASan + UBSan)
shell: bash
working-directory: samples/gfx/wayland_cursor
run: |
set -eu
# input/common/wayland_cursor.h: the size and scale the Wayland
# context loads its cursor theme at when the compositor has no
# cursor-shape-v1 - XCURSOR_SIZE honoured, fractional scales
# rounded up to a whole buffer scale.
make clean all SANITIZER=address,undefined
ASAN_OPTIONS=detect_leaks=1 UBSAN_OPTIONS=print_stacktrace=1:halt_on_error=1 \
timeout 60 ./wayland_cursor_test
echo "[pass] wayland_cursor_test"
- name: Build and run wayland_resize_test (ASan + UBSan)
shell: bash
working-directory: samples/gfx/wayland_resize
run: |
set -eu
# gfx/common/wayland_resize.h, what the Wayland GL and Vulkan
# contexts do once their buffers are resized, against an
# in-process compositor: configures count again whatever the
# scaling, and the buffer scale goes only to a surface on the
# whole-scale path that is new enough to take it.
export XDG_RUNTIME_DIR=$(mktemp -d)
make clean all SANITIZER=address,undefined
ASAN_OPTIONS=detect_leaks=1 UBSAN_OPTIONS=print_stacktrace=1:halt_on_error=1 \
timeout 60 ./wayland_resize_test
echo "[pass] wayland_resize_test"
- name: Build and run video_mode_select_test (ASan + UBSan)
shell: bash
working-directory: samples/gfx/video_mode_select
run: |
set -eu
# gfx/common/video_mode_select.c: which listed mode a size and
# rate name, and the order a resolution list is shown in - the
# code every display server's mode switch goes through.
make clean all SANITIZER=address,undefined
ASAN_OPTIONS=detect_leaks=1 UBSAN_OPTIONS=print_stacktrace=1:halt_on_error=1 \
timeout 60 ./video_mode_select_test
echo "[pass] video_mode_select_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
@@ -1068,73 +802,6 @@ jobs:
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.
# The fallback tests read DejaVu in place of the assets'
# fallback fonts: Sans for fallback-font.ttf, and Sans Mono
# and Sans as a general and a second face for the chain test.
sudo apt-get install -y -qq fonts-dejavu-core fonts-dejavu-mono \
> /dev/null 2>&1 || sudo apt-get install -y -qq fonts-dejavu-core > /dev/null
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
# A fallback font is read on a thread of its own while two
# fonts on two threads ask for it, and a second face behind
# the first.
make fallback chain SAN=thread
TSAN_OPTIONS=halt_on_error=1 setarch "$(uname -m)" -R \
timeout 120 ./fallback
TSAN_OPTIONS=halt_on_error=1 setarch "$(uname -m)" -R \
timeout 120 ./chain
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
@@ -1209,50 +876,13 @@ jobs:
# 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.
# given, 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
@@ -1322,15 +952,6 @@ jobs:
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
@@ -1384,29 +1005,8 @@ jobs:
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'
./preview_blit_bands_test fixtures/hevc_wpp.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
@@ -1414,48 +1014,15 @@ jobs:
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'
./preview_blit_bands_test fixtures/hevc_wpp.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)"
- name: Build and run hw_ring_fence_test (ASan + UBSan, TSan)
shell: bash
working-directory: samples/gfx/hw_ring_fence
run: |
set -u
set -o pipefail
# The hardware ring's fence handoff between the video thread
# and the core's thread (gfx/video_thread_hw_fence.h), the
# tree's own header, with two threads at one slot: a fence is
# signalled only while idle, waited only once signalled, and
# never inside two calls at once - with no lock. The single
# re-signalled fence it replaced is built too and must fail.
make clean all SANITIZER=address,undefined
timeout 120 make check SANITIZER=address,undefined
echo "[pass] hw_ring_fence_test (ASan + UBSan)"
# The two threads share nothing but atomics, and TSan is
# what says so.
make clean all SANITIZER=thread
TSAN_OPTIONS=halt_on_error=1 timeout 120 make check SANITIZER=thread
echo "[pass] hw_ring_fence_test (TSan)"
make clean
+3 -136
View File
@@ -23,13 +23,13 @@ jobs:
samples-input:
name: Build and run samples/input
runs-on: ubuntu-latest
timeout-minutes: 20
timeout-minutes: 15
steps:
- name: Install dependencies
timeout-minutes: 20
timeout-minutes: 5
run: |
pkgs="build-essential libusb-1.0-0-dev libx11-dev libxtst-dev libxi-dev xvfb gcc-mingw-w64-x86-64 wine64"
pkgs="build-essential libusb-1.0-0-dev"
if dpkg -s $pkgs >/dev/null 2>&1; then
echo "All packages already present on runner image; skipping apt."
exit 0
@@ -40,23 +40,6 @@ jobs:
echo "apt-get update failed (attempt $i); retrying in 10s"
sleep 10
done
# Fetch first, under a bound: a download is safe to stop and
# resumes, an install is not. A mirror that stalls is left for
# the main archive on the next attempt.
for i in 1 2 3; do
sudo timeout 240 apt-get install -y --download-only \
-o Acquire::Retries=3 -o Acquire::http::Timeout=30 \
$pkgs && break
[ $i = 3 ] && exit 1
echo "package download failed or stalled (attempt $i); retrying from archive.ubuntu.com"
for f in /etc/apt/sources.list /etc/apt/sources.list.d/*; do
[ -f "$f" ] || continue
sudo sed -i -e 's#mirror+file:/etc/apt/apt-mirrors.txt#http://archive.ubuntu.com/ubuntu/#g' \
-e 's#http://azure\.archive\.ubuntu\.com/ubuntu#http://archive.ubuntu.com/ubuntu#g' "$f"
done
sudo timeout 90 apt-get update -y \
-o Acquire::Retries=3 -o Acquire::http::Timeout=30 || true
done
sudo apt-get install -y \
-o Acquire::Retries=3 -o Acquire::http::Timeout=30 \
$pkgs
@@ -82,35 +65,6 @@ jobs:
ccache -M 500M
ccache -z
- name: X11 input poll sends no requests (Xvfb, ASan + UBSan, TSan)
shell: bash
working-directory: samples/input/x11_input_poll
run: |
set -eu
# The X input driver's poll runs every frame. Keys, and with
# XInput 2 the pointer, come from events on the driver's own
# connection, drained at the poll, so it sends no request on
# either connection; a query on the shared one waits behind
# whatever the video thread presents through it. XTEST drives
# the keyboard (held before init, auto-repeat, focus changes,
# another client's keyboard grab) and the pointer (a press
# without motion, buttons 1, 3 and 8, the wheel, a drag out of
# the window, a new driver over a held button, and grabbed
# motion across a warp the server holds back behind a server
# grab - where any wait on the server would hang the test).
Xvfb :96 -screen 0 1024x768x24 > /tmp/xvfb96.log 2>&1 &
XVFB_PID=$!
sleep 1
make clean all SANITIZER=address,undefined
DISPLAY=:96 ASAN_OPTIONS=detect_leaks=1 \
UBSAN_OPTIONS=print_stacktrace=1:halt_on_error=1 \
timeout 120 ./x11_input_poll_test
make clean all SANITIZER=thread
DISPLAY=:96 TSAN_OPTIONS=halt_on_error=1 timeout 120 ./x11_input_poll_test
make clean
kill $XVFB_PID
echo "[pass] x11_input_poll_test (ASan, TSan)"
- name: Build and run illuminance_sensor_test (ASan + UBSan)
shell: bash
working-directory: samples/input/illuminance_sensor
@@ -180,90 +134,3 @@ jobs:
ASAN_OPTIONS=detect_leaks=1 UBSAN_OPTIONS=print_stacktrace=1:halt_on_error=1 \
timeout 60 ./wii_handshake_test
echo "[pass] wii_handshake_test (ASan)"
- name: Build and run android_lifecycle_flush_test (ASan + UBSan, TSan)
shell: bash
working-directory: samples/input/android_lifecycle_flush
run: |
set -eu
# The Android background flush handshake, a UI thread against
# an app thread. Pins that onPause() and onStop() return only
# once SRAM, core options and the config are on disk - a
# swiped-away task is killed as soon as they have returned -
# that the save is written once per trip to the background,
# and that the startup pump acknowledges without saving.
make clean all SANITIZER=address,undefined
ASAN_OPTIONS=detect_leaks=1 UBSAN_OPTIONS=print_stacktrace=1:halt_on_error=1 \
timeout 60 ./android_lifecycle_flush_test
make clean all SANITIZER=thread
TSAN_OPTIONS=halt_on_error=1 timeout 60 ./android_lifecycle_flush_test
make clean
echo "[pass] android_lifecycle_flush_test (ASan, TSan)"
- name: Build and run android_kbd_keys_test (ASan + UBSan)
shell: bash
working-directory: samples/input/android_kbd_keys
run: |
set -eu
# Key events from the Android system keyboard, through the
# native input queue to the text field that owns the menu
# line. Pins that a Backspace the soft keyboard sends as a
# key event is handed back to the field instead of being
# consumed natively - consumed, the field kept the
# characters and the next snapshot put them back on the line.
make clean all SANITIZER=address,undefined
ASAN_OPTIONS=detect_leaks=1 UBSAN_OPTIONS=print_stacktrace=1:halt_on_error=1 \
timeout 60 ./android_kbd_keys_test
make clean
echo "[pass] android_kbd_keys_test (ASan)"
- name: Build and run overlay_textures_test (plain, ASan + UBSan, threaded)
shell: bash
working-directory: samples/input/overlay_textures
run: |
set -eu
# input/input_overlay_textures.c as it is, against a stub
# driver that reads every pixel it is handed. Pins every
# route a page takes to the driver: as textures (each page
# gets its own images' textures, a switch uploads nothing,
# the pixels go once the driver has them and the sizes
# stay), and as pixels through load() for a driver that
# declines the textures, has no such path, or fails an
# upload - live pixels on every page. 86fbf34e01 freed the
# pixels before the page lists were matched by them, so
# with threaded video off the fallback showed freed memory;
# under ASan that is a use-after-free here.
make clean all
timeout 60 ./overlay_textures_test
make clean all SANITIZER=address,undefined
ASAN_OPTIONS=detect_leaks=1 UBSAN_OPTIONS=print_stacktrace=1:halt_on_error=1 \
timeout 60 ./overlay_textures_test
# THREADS=1: the threaded wrapper as a stub whose video
# thread runs when the test says so, so both outcomes of
# the race between the uploads and the poll are lanes. The
# quick thread is 86fbf34e01's bug again under threaded
# video; the slow one must find the pack's pixels alive.
make clean all THREADS=1 SANITIZER=address,undefined
ASAN_OPTIONS=detect_leaks=1 UBSAN_OPTIONS=print_stacktrace=1:halt_on_error=1 \
timeout 60 ./overlay_textures_test
make clean
echo "[pass] overlay_textures_test (plain, ASan, threaded ASan)"
- name: Build and run winraw_abs_pos_test (mingw + Wine)
shell: bash
working-directory: samples/input/winraw_abs_pos
run: |
set -eu
# input/drivers/winraw_input.c, cross-built with mingw-w64 and
# run under Wine: an absolute pointer report's position is
# kept as one word, and every read must give back a position
# a report carried, including a report that lands during poll.
# mingw-w64 and Wine come from "Install dependencies", whose
# download is bounded and retried; an unbounded install here
# stalled silently until the job timeout. Stop wineserver on
# exit so it cannot hold the step's output open.
trap 'wineserver -k >/dev/null 2>&1 || true' EXIT
make clean all
WINEDEBUG=-all timeout 300 make check
make clean
echo "[pass] winraw_abs_pos_test (wine)"
+3 -239
View File
@@ -27,9 +27,9 @@ jobs:
steps:
- name: Install dependencies
timeout-minutes: 20
timeout-minutes: 5
run: |
pkgs="build-essential libmbedtls-dev libbearssl-dev"
pkgs="build-essential libmbedtls-dev"
if dpkg -s $pkgs >/dev/null 2>&1; then
echo "All packages already present on runner image; skipping apt."
exit 0
@@ -40,23 +40,6 @@ jobs:
echo "apt-get update failed (attempt $i); retrying in 10s"
sleep 10
done
# Fetch first, under a bound: a download is safe to stop and
# resumes, an install is not. A mirror that stalls is left for
# the main archive on the next attempt.
for i in 1 2 3; do
sudo timeout 240 apt-get install -y --download-only \
-o Acquire::Retries=3 -o Acquire::http::Timeout=30 \
$pkgs && break
[ $i = 3 ] && exit 1
echo "package download failed or stalled (attempt $i); retrying from archive.ubuntu.com"
for f in /etc/apt/sources.list /etc/apt/sources.list.d/*; do
[ -f "$f" ] || continue
sudo sed -i -e 's#mirror+file:/etc/apt/apt-mirrors.txt#http://archive.ubuntu.com/ubuntu/#g' \
-e 's#http://azure\.archive\.ubuntu\.com/ubuntu#http://archive.ubuntu.com/ubuntu#g' "$f"
done
sudo timeout 90 apt-get update -y \
-o Acquire::Retries=3 -o Acquire::http::Timeout=30 || true
done
sudo apt-get install -y \
-o Acquire::Retries=3 -o Acquire::http::Timeout=30 \
$pkgs
@@ -109,136 +92,6 @@ jobs:
TSAN_OPTIONS=halt_on_error=1 timeout 120 ./webdav_options_test
echo "[pass] webdav_options_test (TSan)"
- name: Build and run webdav_reauth_test (ASan + UBSan, TSan)
shell: bash
working-directory: samples/network/webdav_reauth
run: |
set -eu
# Includes network/cloud_sync/webdav.c and plays the server
# for every request type the driver sends (OPTIONS, GET, PUT,
# DELETE, MOVE, MKCOL). A server that keeps answering 401 with
# a Digest challenge - a wrong password - must get one retry
# and then a reported failure; the driver used to retry every
# challenge, so a mistyped password looped the first request
# of a sync forever. A single stale-nonce challenge must still
# be retried and succeed. A retried PUT must send the file:
# it used to re-read it from the end the first attempt left
# it at and upload uninitialised memory in place of the save.
# A malformed challenge (a value with no closing quote, a
# trailing unquoted parameter) must be refused, not crash the
# parser or send it reading past the end of the line.
make clean all SANITIZER=address,undefined
test -x webdav_reauth_test
ASAN_OPTIONS=detect_leaks=1:detect_stack_use_after_return=1 \
UBSAN_OPTIONS=print_stacktrace=1 timeout 60 \
./webdav_reauth_test
echo "[pass] webdav_reauth_test (ASan)"
make clean all SANITIZER=thread
TSAN_OPTIONS=halt_on_error=1 timeout 120 ./webdav_reauth_test
echo "[pass] webdav_reauth_test (TSan)"
- name: Build and run webdav_backup_test (ASan + UBSan, TSan)
shell: bash
working-directory: samples/network/webdav_backup
run: |
set -eu
# Includes network/cloud_sync/webdav.c and plays the server,
# request by request. With destructive sync off, an upload
# must COPY the server's file to deleted/<path>-<time> before
# the PUT replaces it - it used to overwrite it in place, so
# the setting protected against deletes but not overwrites.
# Covers the order (backup first, into an existing deleted/
# collection, named as a delete names it), 404 as "nothing to
# back up", a failed backup stopping the upload, destructive
# sync and the server manifest skipping the backup, and COPY
# and PUT each getting their own Digest retry.
make clean all SANITIZER=address,undefined
test -x webdav_backup_test
ASAN_OPTIONS=detect_leaks=1:detect_stack_use_after_return=1 \
UBSAN_OPTIONS=print_stacktrace=1 timeout 60 \
./webdav_backup_test
echo "[pass] webdav_backup_test (ASan)"
make clean all SANITIZER=thread
TSAN_OPTIONS=halt_on_error=1 timeout 120 ./webdav_backup_test
echo "[pass] webdav_backup_test (TSan)"
- name: Build and run s3_backup_test (ASan + UBSan, TSan)
shell: bash
working-directory: samples/network/s3_backup
run: |
set -eu
# Includes network/cloud_sync/s3.c and plays the server,
# request by request. The S3 driver never read the
# destructive sync setting: deletes removed objects for good
# and uploads overwrote them. With it off, both must first
# CopyObject the object to deleted/<path>-<time>. Covers the
# copy source for virtual-hosted and path-style endpoints,
# x-amz-copy-source being signed, 404 as "nothing to keep", a
# failed copy (including a 200 with an <Error> body) stopping
# the delete or upload, and destructive sync and the server
# manifest skipping the copy.
make clean all SANITIZER=address,undefined
test -x s3_backup_test
ASAN_OPTIONS=detect_leaks=1:detect_stack_use_after_return=1 \
UBSAN_OPTIONS=print_stacktrace=1 timeout 60 \
./s3_backup_test
echo "[pass] s3_backup_test (ASan)"
make clean all SANITIZER=thread
TSAN_OPTIONS=halt_on_error=1 timeout 120 ./s3_backup_test
echo "[pass] s3_backup_test (TSan)"
- name: Build and run smb_backup_test (ASan + UBSan, TSan)
shell: bash
working-directory: samples/network/smb_backup
run: |
set -eu
# Includes network/cloud_sync/smb.c and runs it against an
# in-memory libsmb2 share (fake_smb.c), checking what the
# share holds after each upload. The driver used to truncate
# the server file and then write, so a write failing part way
# left a truncated save; and with destructive sync off it
# overwrote the file without keeping the old copy a delete
# keeps. Uploads now go through <path>.rauploading and the
# replaced file is kept as <path>-<time> (non-destructive).
# Covers a failed write, backup and final rename each leaving
# the old file intact with no temporary file left, no backup
# for destructive sync, a new file or the manifest, an empty
# upload and the configured subdirectory.
make clean all SANITIZER=address,undefined
test -x smb_backup_test
ASAN_OPTIONS=detect_leaks=1:detect_stack_use_after_return=1 \
UBSAN_OPTIONS=print_stacktrace=1 timeout 60 \
./smb_backup_test
echo "[pass] smb_backup_test (ASan)"
make clean all SANITIZER=thread
TSAN_OPTIONS=halt_on_error=1 timeout 120 ./smb_backup_test
echo "[pass] smb_backup_test (TSan)"
- name: Build and run gdrive_backup_test (ASan + UBSan, TSan)
shell: bash
working-directory: samples/network/gdrive_backup
run: |
set -eu
# Includes network/cloud_sync/google_drive.c and plays the
# Drive API, request by request. With destructive sync off, a
# delete kept the old file (renamed <name>-<time>) but an
# upload replaced its content outright. An upload to an
# existing file must now files.copy it to that name in the
# same folder before the PATCH. Covers the order and the copy
# request, a failed copy stopping the upload, a 401 on the
# copy and on the PATCH each refreshing the token once (the
# PATCH retry repeating only the PATCH, not the backup), and
# destructive sync, a new file and the manifest skipping it.
make clean all SANITIZER=address,undefined
test -x gdrive_backup_test
ASAN_OPTIONS=detect_leaks=1:detect_stack_use_after_return=1 \
UBSAN_OPTIONS=print_stacktrace=1 timeout 60 \
./gdrive_backup_test
echo "[pass] gdrive_backup_test (ASan)"
make clean all SANITIZER=thread
TSAN_OPTIONS=halt_on_error=1 timeout 120 ./gdrive_backup_test
echo "[pass] gdrive_backup_test (TSan)"
- name: Build and run cheevos_login_test (bear ASan+UBSan, bear TSan, mbed ASan+UBSan)
shell: bash
working-directory: samples/network/cheevos_login
@@ -253,12 +106,7 @@ jobs:
# from a realistic messy CA bundle (BearSSL), keep-alive pool
# reuse, one-shot replay of a request on a pooled connection
# the server closed while idle, and no replay once response
# bytes have been seen. Each TLS backend's non-blocking send
# must push a 12 MiB body past a server that stops reading for
# a second without ever blocking, and deliver every byte; and
# net_http must send the same upload a slice per update, plain
# and over TLS - it used to go out in one blocking send, a call
# of a second or more (spinning at 100% CPU on a plain socket).
# bytes have been seen.
make clean all SANITIZER=address,undefined
test -x cheevos_login_test
ASAN_OPTIONS=detect_leaks=1:detect_stack_use_after_return=1 \
@@ -273,87 +121,3 @@ jobs:
UBSAN_OPTIONS=print_stacktrace=1:halt_on_error=1 timeout 120 \
make check SSL_BACKEND=mbed
echo "[pass] cheevos_login_test (mbedTLS, ASan)"
# The built-in client, RetroArch's default backend.
make clean all SSL_BACKEND=retro SANITIZER=address,undefined
ASAN_OPTIONS=detect_leaks=1:detect_stack_use_after_return=1 \
UBSAN_OPTIONS=print_stacktrace=1:halt_on_error=1 timeout 120 \
make check SSL_BACKEND=retro
echo "[pass] cheevos_login_test (built-in TLS, ASan)"
# The runner's libmbedtls-dev is 2.28, which has no TLS 1.3,
# so the leg above cannot see either TLS 1.3 failure. Mbed
# TLS 3.6.0 is the one release that shows both: it fails
# every handshake unless psa_crypto_init() was called, and it
# reports session tickets from mbedtls_ssl_read(). Library
# only, ~20 s to build. A download that still fails after
# the retries skips the leg rather than failing the job.
mbed_ver=3.6.0
mbed_sha=3ecf94fcfdaacafb757786a01b7538a61750ebd85c4b024f56ff8ba1490fcd38
mbed_tar="$RUNNER_TEMP/mbedtls-$mbed_ver.tar.bz2"
if curl -fsSL --retry 3 --retry-delay 5 --max-time 120 -o "$mbed_tar" \
"https://github.com/Mbed-TLS/mbedtls/releases/download/v$mbed_ver/mbedtls-$mbed_ver.tar.bz2"; then
echo "$mbed_sha $mbed_tar" | sha256sum -c -
tar -xjf "$mbed_tar" -C "$RUNNER_TEMP"
make -C "$RUNNER_TEMP/mbedtls-$mbed_ver" -j"$(nproc)" lib >/dev/null
make clean all SSL_BACKEND=mbed SANITIZER=address,undefined \
MBEDTLS_DIR="$RUNNER_TEMP/mbedtls-$mbed_ver"
ASAN_OPTIONS=detect_leaks=1:detect_stack_use_after_return=1 \
UBSAN_OPTIONS=print_stacktrace=1:halt_on_error=1 timeout 120 \
make check SSL_BACKEND=mbed
echo "[pass] cheevos_login_test (mbedTLS $mbed_ver TLS 1.3, ASan)"
else
echo "::warning::Mbed TLS $mbed_ver download failed; TLS 1.3 leg skipped"
fi
- name: Build and run net_http_semantics_test (ASan + UBSan, TSan)
shell: bash
working-directory: samples/network/net_http_semantics
run: |
set -eu
# HTTP/1.1 semantics of libretro-common/net/net_http.c against
# an in-process scripted server on 127.0.0.1, which logs which
# connection every request arrived on. Covers GET 200 framing
# and pool reuse, and responses without a body: HEAD, 204 and
# 304 on keep-alive used to sit waiting for a close that never
# came, a 1xx other than 100 was taken as the final status,
# and a 101 or stray bytes after a bodyless response left the
# socket in the pool. Connection and Transfer-Encoding are
# read as lists ("Keep-Alive, Close", "gzip, chunked"),
# HTTP/1.0 without keep-alive is not pooled, and a length
# after "chunked" does not reframe the body. Redirects: an
# absolute-path Location used to go out as "GET //foo", a
# 302 loop was followed forever (now 8 hops), a streamed PUT
# redirected by a 307 is rewound and resent whole, and a 3xx
# body no longer lands in the caller's sink. Bounds: a header
# block (1xx blocks included) over 64 KiB and a chunk size
# with no hex digits fail instead of growing the buffer or
# passing as the last chunk, a Content-Length buffer never
# grows past the body, and a pooled connection that dies
# after the status line is not replayed. A URL shorter than
# its scheme is rejected without an overread. Unit matrix for
# net_http_url_join() and net_http_urldecode(), including
# buffer bounds and in-place decoding; redirects resolved
# through it ("../", "?q", "//host", fragments dropped,
# %20 left encoded). The response header block: lookup and
# iteration, the legacy string_list accessor, and 64 header
# lines costing no per-line allocations. A Content-Length or
# "chunked" on a 1xx block does not frame the final response.
# A followed 3xx's body is dropped as it arrives, not
# buffered to its Content-Length, and bytes past a body's
# Content-Length end the connection, not the transfer.
# Every case has a
# deadline, so a hang fails the case instead of the job.
# net_http is lock-free by design (DNS cache and pool owned by
# the driving thread, resolvers publish with an atomic store);
# keep it that way.
if grep -nE '\b(slock|scond)_[a-z_]+\(' ../../../libretro-common/net/net_http.c; then
echo "::error title=net_http lock::net_http.c must not take locks"
exit 1
fi
make clean all SANITIZER=address,undefined
ASAN_OPTIONS=detect_leaks=1:detect_stack_use_after_return=1 \
UBSAN_OPTIONS=print_stacktrace=1:halt_on_error=1 timeout 120 \
./net_http_semantics_test
echo "[pass] net_http_semantics_test (ASan)"
make clean all SANITIZER=thread
TSAN_OPTIONS=halt_on_error=1 timeout 300 ./net_http_semantics_test
echo "[pass] net_http_semantics_test (TSan)"
+1 -18
View File
@@ -41,7 +41,7 @@ jobs:
steps:
- name: Install dependencies
timeout-minutes: 20
timeout-minutes: 5
run: |
pkgs="build-essential pkg-config libavcodec-dev libavformat-dev libavutil-dev libswscale-dev libswresample-dev"
if dpkg -s $pkgs >/dev/null 2>&1; then
@@ -54,23 +54,6 @@ jobs:
echo "apt-get update failed (attempt $i); retrying in 10s"
sleep 10
done
# Fetch first, under a bound: a download is safe to stop and
# resumes, an install is not. A mirror that stalls is left for
# the main archive on the next attempt.
for i in 1 2 3; do
sudo timeout 240 apt-get install -y --download-only \
-o Acquire::Retries=3 -o Acquire::http::Timeout=30 \
$pkgs && break
[ $i = 3 ] && exit 1
echo "package download failed or stalled (attempt $i); retrying from archive.ubuntu.com"
for f in /etc/apt/sources.list /etc/apt/sources.list.d/*; do
[ -f "$f" ] || continue
sudo sed -i -e 's#mirror+file:/etc/apt/apt-mirrors.txt#http://archive.ubuntu.com/ubuntu/#g' \
-e 's#http://azure\.archive\.ubuntu\.com/ubuntu#http://archive.ubuntu.com/ubuntu#g' "$f"
done
sudo timeout 90 apt-get update -y \
-o Acquire::Retries=3 -o Acquire::http::Timeout=30 || true
done
sudo apt-get install -y \
-o Acquire::Retries=3 -o Acquire::http::Timeout=30 \
$pkgs
+1 -18
View File
@@ -27,7 +27,7 @@ jobs:
steps:
- name: Install dependencies
timeout-minutes: 20
timeout-minutes: 5
run: |
pkgs="build-essential"
if dpkg -s $pkgs >/dev/null 2>&1; then
@@ -40,23 +40,6 @@ jobs:
echo "apt-get update failed (attempt $i); retrying in 10s"
sleep 10
done
# Fetch first, under a bound: a download is safe to stop and
# resumes, an install is not. A mirror that stalls is left for
# the main archive on the next attempt.
for i in 1 2 3; do
sudo timeout 240 apt-get install -y --download-only \
-o Acquire::Retries=3 -o Acquire::http::Timeout=30 \
$pkgs && break
[ $i = 3 ] && exit 1
echo "package download failed or stalled (attempt $i); retrying from archive.ubuntu.com"
for f in /etc/apt/sources.list /etc/apt/sources.list.d/*; do
[ -f "$f" ] || continue
sudo sed -i -e 's#mirror+file:/etc/apt/apt-mirrors.txt#http://archive.ubuntu.com/ubuntu/#g' \
-e 's#http://azure\.archive\.ubuntu\.com/ubuntu#http://archive.ubuntu.com/ubuntu#g' "$f"
done
sudo timeout 90 apt-get update -y \
-o Acquire::Retries=3 -o Acquire::http::Timeout=30 || true
done
sudo apt-get install -y \
-o Acquire::Retries=3 -o Acquire::http::Timeout=30 \
$pkgs
+23 -203
View File
@@ -27,7 +27,7 @@ jobs:
steps:
- name: Install dependencies
timeout-minutes: 20
timeout-minutes: 5
run: |
pkgs="build-essential zlib1g-dev"
if dpkg -s $pkgs >/dev/null 2>&1; then
@@ -40,23 +40,6 @@ jobs:
echo "apt-get update failed (attempt $i); retrying in 10s"
sleep 10
done
# Fetch first, under a bound: a download is safe to stop and
# resumes, an install is not. A mirror that stalls is left for
# the main archive on the next attempt.
for i in 1 2 3; do
sudo timeout 240 apt-get install -y --download-only \
-o Acquire::Retries=3 -o Acquire::http::Timeout=30 \
$pkgs && break
[ $i = 3 ] && exit 1
echo "package download failed or stalled (attempt $i); retrying from archive.ubuntu.com"
for f in /etc/apt/sources.list /etc/apt/sources.list.d/*; do
[ -f "$f" ] || continue
sudo sed -i -e 's#mirror+file:/etc/apt/apt-mirrors.txt#http://archive.ubuntu.com/ubuntu/#g' \
-e 's#http://azure\.archive\.ubuntu\.com/ubuntu#http://archive.ubuntu.com/ubuntu#g' "$f"
done
sudo timeout 90 apt-get update -y \
-o Acquire::Retries=3 -o Acquire::http::Timeout=30 || true
done
sudo apt-get install -y \
-o Acquire::Retries=3 -o Acquire::http::Timeout=30 \
$pkgs
@@ -134,38 +117,6 @@ jobs:
timeout 60 ./archive_name_safety_test
echo "[pass] archive_name_safety_test"
- name: Build and run cloudsync_path_safety_test
shell: bash
working-directory: samples/tasks/cloudsync
run: |
set -eu
# Regression test for the manifest-key check in
# tasks/task_cloudsync.c::task_cloud_sync_fetch_server_file.
# A hostile sync server's manifest key could escape the
# cloud-sync directory via ".." or an absolute component, or
# crash the fetch with a key that has no '/'. The test
# #includes the real predicate (tasks/task_cloudsync_path.c).
make clean all
test -x cloudsync_path_safety_test
timeout 60 ./cloudsync_path_safety_test
echo "[pass] cloudsync_path_safety_test"
- name: Build and run natt_desc_parse_test
shell: bash
working-directory: samples/tasks/natt_desc
run: |
set -eu
# Regression test for the UPnP IGD description parser
# (network/natt_desc.c::natt_parse_desc_node). A router's
# description XML is untrusted; the old parser crashed on a
# leaf or empty root node and never reached a nested
# <service>. The test #includes the real parser and feeds it
# hand-built hostile trees.
make clean all
test -x natt_desc_parse_test
timeout 60 ./natt_desc_parse_test
echo "[pass] natt_desc_parse_test"
- name: Build and run http_method_match_test (ASan)
shell: bash
working-directory: samples/tasks/http
@@ -279,32 +230,6 @@ jobs:
timeout 60 ./bsv_replay_bounds_test
echo "[pass] bsv_replay_bounds_test"
- name: Build and run bsv_replay_init_test (plain, ASan + UBSan)
shell: bash
working-directory: samples/tasks/bsv_replay_init
run: |
set -eu
# Regression oracle for replay start-up and shutdown in
# tasks/task_movie.c and input/bsv/bsvmovie.c (#19622).
# Links the shipping units against a stub core and the real
# task queue / intfstream / VFS, in the iOS configuration
# (RZSTD on, STATESTREAM off). Pre-fix a replay truncated
# inside its first checkpoint, or halted before its first
# frame, left BSV_FLAG_MOVIE_END set with no handle: the
# runloop then paused after every frame, for every content,
# until the process was restarted. Also covers halting a
# recording before its first frame leaving the session stuck
# "recording". If runloop.c changes its per-frame replay
# protocol, frame() in the test must follow.
make clean all
test -x bsv_replay_init_test
timeout 120 ./bsv_replay_init_test
echo "[pass] bsv_replay_init_test"
make clean all SANITIZER=address,undefined
ASAN_OPTIONS=detect_leaks=1 UBSAN_OPTIONS=print_stacktrace=1 \
timeout 120 ./bsv_replay_init_test
echo "[pass] bsv_replay_init_test (ASan + UBSan)"
- name: Build and run bps_patch_bounds_test (ASan)
shell: bash
working-directory: samples/tasks/bps_patch
@@ -802,14 +727,16 @@ jobs:
working-directory: samples/tasks/overlay
run: |
set -eu
# Builds the shipping input/input_overlay_alpha.c and
# led/drivers/led_overlay.c against a stub driver. Which images
# the overlay LED driver hides: a pack's _led images, else the
# image at a ledN_map slot whatever its desc does, and never an
# index off the page - led_map comes from the config with no
# range of its own. And the per-poll alpha pass: an image is set only when
# its alpha changes, a page load sets them all again, and an
# LED going out and back on between polls is shown again.
# The overlay visibility index does not come from the overlay:
# led/drivers/led_overlay.c forwards settings->uints.led_map[],
# which configuration.c reads from the config file with no
# range of its own. Unbounded, "led1_map = 500" wrote past
# overlay_visibility[MAX_VISIBILITY] during drivers_init() and
# handed the same index to the video driver's set_alpha().
# This builds the shipping LED driver to pin that it really
# does forward config values unaltered, and checks the bound
# covers the array exactly. If input_driver.c changes that
# bound, the verbatim copy in the test must follow.
make -f Makefile.visibility clean >/dev/null
make -f Makefile.visibility all SANITIZER=address,undefined -j$(nproc)
ASAN_OPTIONS=detect_leaks=1 \
@@ -1029,39 +956,22 @@ jobs:
# by cancellation plus a stale-completion generation guard
# (menu_explore_cancel_init_task / menu_dbinfo_cancel_task).
#
# Scope is mechanical: menu/, the task translation units the
# menu drives directly, and the content load - a chain of
# main-thread tasks that closes content from the frame loop.
# The remaining wait sites (NAT traversal, the CLI database
# scan, the exit path) are tracked separately; widening this
# guard is what should happen as each of those is converted.
hits=$(grep -rn 'task_queue_wait' menu/ network/ command.c \
# Scope is deliberately narrow and mechanical: menu/ and the
# two task translation units the menu drives directly. The
# remaining wait sites elsewhere (core deinit save/load, NAT
# traversal, netplay MITM) are tracked separately and are
# NOT covered here - widening this guard is what should
# happen as each of those is converted.
hits=$(grep -rn 'task_queue_wait' menu/ \
tasks/task_menu_explore.c \
tasks/task_database_info.c \
tasks/task_netplay_nat_traversal.c \
tasks/task_content.c || true)
tasks/task_netplay_nat_traversal.c || true)
if [[ -n "$hits" ]]; then
printf '%s\n' "$hits"
printf '::error title=Blocking wait on a menu path::task_queue_wait() must not be reachable from the menu. Use cancellation with a stale-completion guard, or a deferred continuation.\n'
exit 1
fi
# The close's blocking helpers wait inside task_save.c, where
# the grep above cannot see them: the menu and the tasks must
# not call them. The load closes through
# retroarch_main_deinit_begin/pending/finish instead.
hits=$(grep -rn 'content_wait_for_\(save\|load\)_state_task' \
menu/ tasks/ --include='*.c' \
| grep -v '^tasks/task_save.c:' || true)
if [[ -n "$hits" ]]; then
printf '%s\n' "$hits"
printf '::error title=Blocking close on a menu path::content_wait_for_*_state_task() blocks the frame loop. Close through retroarch_main_deinit_begin/pending/finish from a main-thread task.\n'
exit 1
fi
grep -q 'retroarch_main_deinit_pending' tasks/task_content.c || {
printf '::error title=Blocking close on a menu path::the content load no longer waits for state tasks from the frame loop.\n'
exit 1
}
printf 'No task_queue_wait or blocking close on the guarded menu paths.\n'
printf 'No task_queue_wait on the guarded menu paths.\n'
- name: Build and run playlist_parity_test (plain, ASan + UBSan)
shell: bash
@@ -1322,7 +1232,7 @@ jobs:
steps:
- name: Install dependencies
timeout-minutes: 20
timeout-minutes: 5
run: |
pkgs="build-essential zlib1g-dev"
if dpkg -s $pkgs >/dev/null 2>&1; then
@@ -1335,23 +1245,6 @@ jobs:
echo "apt-get update failed (attempt $i); retrying in 10s"
sleep 10
done
# Fetch first, under a bound: a download is safe to stop and
# resumes, an install is not. A mirror that stalls is left for
# the main archive on the next attempt.
for i in 1 2 3; do
sudo timeout 240 apt-get install -y --download-only \
-o Acquire::Retries=3 -o Acquire::http::Timeout=30 \
$pkgs && break
[ $i = 3 ] && exit 1
echo "package download failed or stalled (attempt $i); retrying from archive.ubuntu.com"
for f in /etc/apt/sources.list /etc/apt/sources.list.d/*; do
[ -f "$f" ] || continue
sudo sed -i -e 's#mirror+file:/etc/apt/apt-mirrors.txt#http://archive.ubuntu.com/ubuntu/#g' \
-e 's#http://azure\.archive\.ubuntu\.com/ubuntu#http://archive.ubuntu.com/ubuntu#g' "$f"
done
sudo timeout 90 apt-get update -y \
-o Acquire::Retries=3 -o Acquire::http::Timeout=30 || true
done
sudo apt-get install -y \
-o Acquire::Retries=3 -o Acquire::http::Timeout=30 \
$pkgs
@@ -1471,9 +1364,8 @@ jobs:
# sits between two representations: RetroArch's raw
# CRLF-delimited blob and the NULL-terminated key/value array
# emscripten_fetch wants, plus the response blob that has to
# become the same "Name: Value" header block net_http.c
# produces - through net_http_headers_compact(), which the test
# links rather than copies. Neither conversion existed while the backend was
# become the same "Name: Value" string_list net_http.c
# produces. Neither conversion existed while the backend was
# on emscripten_async_wget2_data, which has no header support
# at all - the `headers` argument was accepted and dropped on
# every entry point, so cloud sync could not authenticate.
@@ -1648,78 +1540,6 @@ jobs:
make -f Makefile.transport_fail clean
echo "[pass] http_transport_failure_test (TSan)"
- name: Build and run http_resume_test (ASan + UBSan, TSan)
shell: bash
working-directory: samples/tasks/http
run: |
set -eu
# An interrupted download to a file (task_push_http_download_file,
# as the core updater and thumbnails use) resumes with
# "Range: bytes=N-" and If-Range on the first response's strong
# ETag. A 206 from the right offset continues the file; a 200
# (resource changed) starts it over; a 206 from the wrong offset,
# a first response without Accept-Ranges or a validator, and a
# connection that keeps dropping (more than three resumes) fail
# and remove the partial file. Every interrupted download used
# to fail outright.
make -f Makefile.resume clean all SANITIZER=address,undefined
ASAN_OPTIONS=detect_leaks=1:detect_stack_use_after_return=1 \
UBSAN_OPTIONS=print_stacktrace=1:halt_on_error=1 timeout 300 \
./http_resume_test
echo "[pass] http_resume_test (ASan)"
make -f Makefile.resume clean all SANITIZER=thread
TSAN_OPTIONS=halt_on_error=1 timeout 300 ./http_resume_test
make -f Makefile.resume clean
echo "[pass] http_resume_test (TSan)"
- name: Build and run HTTP initialization failure test
shell: bash
working-directory: samples/tasks/http
run: |
set -eu
for threads in 0 1; do
make -f Makefile.init_failure clean
make -f Makefile.init_failure THREADS=$threads
timeout 60 ./http_init_failure_test
done
make -f Makefile.init_failure clean
- name: Build and run http_limits_test (ASan + UBSan, threaded and not)
shell: bash
working-directory: samples/tasks/http
run: |
set -eu
# net_http.c included whole, with the clock and the socket
# calls stubbed. The DNS cache is capped (it grew by one entry
# per distinct host:port for the whole session), evicts least
# recently used first, and never evicts a lookup still in
# flight. The pool keeps at most 8 idle connections, closing
# the longest-idle first, and closes rather than reuses one
# idle for more than 30 s. On a system without IPv6, an IPv6
# address listed first no longer fails the transfer, and one
# mid-list no longer hides the addresses after it. Bracketed
# IPv6 literals ([::1]:8080) parse in URLs and redirects and
# reach the resolver without their brackets.
for threads in 1 0; do
make -f Makefile.limits clean
make -f Makefile.limits THREADS=$threads SANITIZER=address,undefined
ASAN_OPTIONS=detect_leaks=1 UBSAN_OPTIONS=halt_on_error=1 \
timeout 60 ./http_limits_test
done
make -f Makefile.limits clean
- name: Build and run HTTP DNS connect lifetime test
shell: bash
working-directory: samples/tasks/http
run: |
set -eu
for ssl in 0 1; do
make -f Makefile.dns_lifetime clean
make -f Makefile.dns_lifetime SSL=$ssl
timeout 60 ./http_dns_lifetime_test
done
make -f Makefile.dns_lifetime clean
- name: Build and run http_dns_evict_deadlock_test (ASan, TSan)
shell: bash
working-directory: samples/tasks/http
@@ -1,117 +0,0 @@
name: CI Linux samples [nothing else runs these]
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:
samples-unclaimed:
name: Samples no other job runs
runs-on: ubuntu-latest
timeout-minutes: 30
steps:
- name: Install dependencies
timeout-minutes: 20
run: |
pkgs="build-essential"
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
# Fetch first, under a bound: a download is safe to stop and
# resumes, an install is not. A mirror that stalls is left for
# the main archive on the next attempt.
for i in 1 2 3; do
sudo timeout 240 apt-get install -y --download-only -o Acquire::Retries=3 \
-o Acquire::http::Timeout=30 $pkgs && break
[ $i = 3 ] && exit 1
echo "package download failed or stalled (attempt $i); retrying from archive.ubuntu.com"
for f in /etc/apt/sources.list /etc/apt/sources.list.d/*; do
[ -f "$f" ] || continue
sudo sed -i -e 's#mirror+file:/etc/apt/apt-mirrors.txt#http://archive.ubuntu.com/ubuntu/#g' \
-e 's#http://azure\.archive\.ubuntu\.com/ubuntu#http://archive.ubuntu.com/ubuntu#g' "$f"
done
sudo timeout 90 apt-get update -y \
-o Acquire::Retries=3 -o Acquire::http::Timeout=30 || true
done
sudo apt-get install -y -o Acquire::Retries=3 \
-o Acquire::http::Timeout=30 $pkgs
- name: Checkout
uses: actions/checkout@v5
- name: Run them
shell: bash
run: |
set -eu
# A sample that builds and runs, and that no workflow names,
# is a test nobody runs: font_lifecycle sat that way while
# its expectation drifted away from the font driver it was
# written against. This job takes whatever is left over -
# the list is computed, so a new sample joins it by existing
# rather than by being remembered.
#
# Samples that need a library the runner does not carry
# (SDL, FFmpeg, libusb) are skipped: their own workflows
# install what they need. A sample cross-built with mingw-w64
# and run under Wine gets both installed here, the first time
# one turns up, so it runs rather than fails for want of a
# compiler.
fails=0
wine_ready=0
for mk in $(find samples libretro-common/samples -name Makefile | sort); do
dir=$(dirname "$mk")
name=$(basename "$dir")
(cd "$dir" && make -n check >/dev/null 2>&1) || continue
if grep -rqlF "$name" .github/workflows/ 2>/dev/null; then
continue
fi
if grep -qiE 'sdl|ffmpeg|libav|libusb' "$mk"; then
echo "-- skip $dir (needs a library this job does not install)"
continue
fi
if [ $wine_ready -eq 0 ] && grep -qE 'mingw32-|wine' "$mk"; then
sudo apt-get update -y -qq -o Acquire::Retries=3 \
-o Acquire::http::Timeout=30 >/dev/null || true
sudo apt-get install -y -qq gcc-mingw-w64-x86-64 wine64 \
-o Acquire::Retries=3 -o Acquire::http::Timeout=30
wine_ready=1
fi
echo "== $dir"
if (cd "$dir" && make clean >/dev/null 2>&1 \
&& ASAN_OPTIONS=detect_leaks=1:allocator_may_return_null=1 \
UBSAN_OPTIONS=print_stacktrace=1 \
timeout 300 make SANITIZER=address,undefined check); then
echo " ok"
else
echo " FAIL $dir"
fails=$((fails + 1))
fi
(cd "$dir" && make clean >/dev/null 2>&1) || true
done
if [ "$fails" -ne 0 ]; then
echo "$fails sample(s) failed"
exit 1
fi
+1 -18
View File
@@ -38,7 +38,7 @@ jobs:
steps:
- name: Install dependencies
timeout-minutes: 20
timeout-minutes: 5
run: |
pkgs="build-essential libxkbcommon-dev libx11-xcb-dev xvfb x11-utils zlib1g-dev libfreetype6-dev libegl1-mesa-dev libgles2-mesa-dev libgbm-dev libavcodec-dev libsdl2-dev libsdl-image1.2-dev libxml2-dev yasm"
if dpkg -s $pkgs >/dev/null 2>&1; then
@@ -51,23 +51,6 @@ jobs:
echo "apt-get update failed (attempt $i); retrying in 10s"
sleep 10
done
# Fetch first, under a bound: a download is safe to stop and
# resumes, an install is not. A mirror that stalls is left for
# the main archive on the next attempt.
for i in 1 2 3; do
sudo timeout 240 apt-get install -y --download-only \
-o Acquire::Retries=3 -o Acquire::http::Timeout=30 \
$pkgs && break
[ $i = 3 ] && exit 1
echo "package download failed or stalled (attempt $i); retrying from archive.ubuntu.com"
for f in /etc/apt/sources.list /etc/apt/sources.list.d/*; do
[ -f "$f" ] || continue
sudo sed -i -e 's#mirror+file:/etc/apt/apt-mirrors.txt#http://archive.ubuntu.com/ubuntu/#g' \
-e 's#http://azure\.archive\.ubuntu\.com/ubuntu#http://archive.ubuntu.com/ubuntu#g' "$f"
done
sudo timeout 90 apt-get update -y \
-o Acquire::Retries=3 -o Acquire::http::Timeout=30 || true
done
sudo apt-get install -y \
-o Acquire::Retries=3 -o Acquire::http::Timeout=30 \
$pkgs
@@ -49,7 +49,7 @@ jobs:
- name: Install dependencies
# Same set as Linux-asan-ubsan.yml. libtsan ships with the gcc
# on the runner image, so nothing sanitizer-specific is needed.
timeout-minutes: 20
timeout-minutes: 5
run: |
pkgs="build-essential libxkbcommon-dev libx11-xcb-dev xvfb x11-utils zlib1g-dev libfreetype6-dev libegl1-mesa-dev libgles2-mesa-dev libgbm-dev libavcodec-dev libsdl2-dev libsdl-image1.2-dev libxml2-dev yasm"
if dpkg -s $pkgs >/dev/null 2>&1; then
@@ -62,23 +62,6 @@ jobs:
echo "apt-get update failed (attempt $i); retrying in 10s"
sleep 10
done
# Fetch first, under a bound: a download is safe to stop and
# resumes, an install is not. A mirror that stalls is left for
# the main archive on the next attempt.
for i in 1 2 3; do
sudo timeout 240 apt-get install -y --download-only \
-o Acquire::Retries=3 -o Acquire::http::Timeout=30 \
$pkgs && break
[ $i = 3 ] && exit 1
echo "package download failed or stalled (attempt $i); retrying from archive.ubuntu.com"
for f in /etc/apt/sources.list /etc/apt/sources.list.d/*; do
[ -f "$f" ] || continue
sudo sed -i -e 's#mirror+file:/etc/apt/apt-mirrors.txt#http://archive.ubuntu.com/ubuntu/#g' \
-e 's#http://azure\.archive\.ubuntu\.com/ubuntu#http://archive.ubuntu.com/ubuntu#g' "$f"
done
sudo timeout 90 apt-get update -y \
-o Acquire::Retries=3 -o Acquire::http::Timeout=30 || true
done
sudo apt-get install -y \
-o Acquire::Retries=3 -o Acquire::http::Timeout=30 \
$pkgs
+10 -168
View File
@@ -46,16 +46,6 @@ name: CI Vulkan/GL/D3D11 [idle waits + driver harness]
# frontend does against the driver from the wrong thread is an X
# BadAccess here rather than a corruption elsewhere; the first run
# caught the OSD font rebuild doing exactly that.
#
# 6. Teardown-order check: tools/vulkan_teardown_order_check.py holds
# the order the driver retires a frame's objects in - command
# pools, then descriptor pools, then the pipelines that own the set
# layout - and the rule that destroying a descriptor pool is what
# frees its sets. The other order is legal Vulkan and the
# validation layer passes it, so the harness lanes above cannot see
# it; a driver that walks its own back references as a pool is
# destroyed faults inside vkDestroyDescriptorPool at exit, which is
# what MoltenVK did.
on:
push:
@@ -95,27 +85,6 @@ jobs:
set -eu
python3 tools/vulkan_idle_wait_check.py
vulkan-teardown-order:
name: Vulkan teardown order
runs-on: ubuntu-latest
timeout-minutes: 5
steps:
- name: Checkout
uses: actions/checkout@v5
- name: Checker self-test
shell: bash
run: |
set -eu
python3 tools/vulkan_teardown_order_check.py --selftest
- name: Tree check
shell: bash
run: |
set -eu
python3 tools/vulkan_teardown_order_check.py
vulkan-validation-harness:
name: Threaded-video harness on Vulkan (lavapipe, validation layer)
runs-on: ubuntu-latest
@@ -123,7 +92,7 @@ jobs:
steps:
- name: Install dependencies
timeout-minutes: 20
timeout-minutes: 8
run: |
pkgs="build-essential libxkbcommon-dev libx11-xcb-dev libxcb1-dev libxinerama-dev libxrandr-dev libxxf86vm-dev libxv-dev xvfb x11-utils zlib1g-dev libfreetype6-dev libvulkan-dev mesa-vulkan-drivers vulkan-validationlayers vulkan-tools libasound2-dev"
for i in 1 2 3; do
@@ -132,23 +101,6 @@ jobs:
echo "apt-get update failed (attempt $i); retrying in 10s"
sleep 10
done
# Fetch first, under a bound: a download is safe to stop and
# resumes, an install is not. A mirror that stalls is left for
# the main archive on the next attempt.
for i in 1 2 3; do
sudo timeout 240 apt-get install -y --download-only \
-o Acquire::Retries=3 -o Acquire::http::Timeout=30 \
$pkgs && break
[ $i = 3 ] && exit 1
echo "package download failed or stalled (attempt $i); retrying from archive.ubuntu.com"
for f in /etc/apt/sources.list /etc/apt/sources.list.d/*; do
[ -f "$f" ] || continue
sudo sed -i -e 's#mirror+file:/etc/apt/apt-mirrors.txt#http://archive.ubuntu.com/ubuntu/#g' \
-e 's#http://azure\.archive\.ubuntu\.com/ubuntu#http://archive.ubuntu.com/ubuntu#g' "$f"
done
sudo timeout 90 apt-get update -y \
-o Acquire::Retries=3 -o Acquire::http::Timeout=30 || true
done
sudo apt-get install -y \
-o Acquire::Retries=3 -o Acquire::http::Timeout=30 \
$pkgs
@@ -169,14 +121,8 @@ jobs:
# debug messenger that reports through RARCH_ERR. It is a
# tree-wide define: vulkan_context_t carries a member under it,
# so every TU that includes vulkan_common.h must see it.
# GFX_INSTRUMENT counts what the image and surface paths do -
# uploads, in-place updates, allocating posts, copies - so the
# harness lanes below can hold the budgets the paths are
# written to and print what a run cost. This is the one leg
# that carries it: under a sanitizer the timings mean nothing,
# and the counters change no behaviour.
run: |
make -j$(getconf _NPROCESSORS_ONLN) VULKAN_DEBUG=1 GFX_INSTRUMENT=1
make -j$(getconf _NPROCESSORS_ONLN) VULKAN_DEBUG=1
test -x retroarch
- name: Run the harness on Vulkan under the validation layer
@@ -196,35 +142,8 @@ jobs:
# lines, so it has to name the same build: VK_DEBUG=1. Its
# surface and overlay-page lanes run the driver's real
# in-place texture update and its borrowed-texture overlay
# page here, under the validation layer, and with the tree
# built GFX_INSTRUMENT=1 they print their baselines and hold
# the budgets: one texture per streaming surface, no
# allocation per post, no copy, a 4K submit that stays a
# descriptor hand-off.
SAN=none VK_DEBUG=1 GFX_INSTRUMENT=1 CYCLES=2 make -C samples/gfx/threaded_video check-vulkan
# Once more with the harness core pushing XRGB8888: the
# driver samples a linear 32-bit image itself where it
# converts RGB565 with a compute pass, so a lent frame and
# a window into it take the other upload path (the one
# beetle-psx takes).
HARNESS_CORE_XRGB8888=1 SAN=none VK_DEBUG=1 GFX_INSTRUMENT=1 CYCLES=1 make -C samples/gfx/threaded_video check-vulkan
# And with the harness core as a Vulkan hardware core, which
# runs its own lane alone: the core's image must not be drawn
# from once the core has destroyed it - at a content close,
# unthreaded and under the wrapper, and after a set_image(NULL)
# - and a dupe of a hardware frame must not re-submit a fence
# that is already signalled. Either is a validation error
# here, and on lavapipe a crash.
HARNESS_CORE_HW_VULKAN=1 SAN=none VK_DEBUG=1 GFX_INSTRUMENT=1 CYCLES=1 make -C samples/gfx/threaded_video check-vulkan
make -C samples/gfx/threaded_video clean
# And as a core that creates the device itself, through the
# negotiation interface's create_device - Beetle PSX, for one.
# That device has the extensions the frontend requires and
# none of the optional ones it adds to a device of its own, so
# anything the driver or the filter chain does that leans on
# an optional extension is a validation error here and nowhere
# else. The filter chain's mirror-clamp samplers were.
HARNESS_CORE_HW_VULKAN=2 SAN=none VK_DEBUG=1 GFX_INSTRUMENT=1 CYCLES=1 make -C samples/gfx/threaded_video check-vulkan
# page here, under the validation layer.
SAN=none VK_DEBUG=1 CYCLES=2 make -C samples/gfx/threaded_video check-vulkan
make -C samples/gfx/threaded_video clean
vulkan-validation-sample:
@@ -234,7 +153,7 @@ jobs:
steps:
- name: Install dependencies
timeout-minutes: 20
timeout-minutes: 8
run: |
pkgs="build-essential libx11-dev libx11-xcb-dev libxcb1-dev xvfb x11-utils libvulkan-dev mesa-vulkan-drivers vulkan-validationlayers vulkan-tools"
for i in 1 2 3; do
@@ -243,23 +162,6 @@ jobs:
echo "apt-get update failed (attempt $i); retrying in 10s"
sleep 10
done
# Fetch first, under a bound: a download is safe to stop and
# resumes, an install is not. A mirror that stalls is left for
# the main archive on the next attempt.
for i in 1 2 3; do
sudo timeout 240 apt-get install -y --download-only \
-o Acquire::Retries=3 -o Acquire::http::Timeout=30 \
$pkgs && break
[ $i = 3 ] && exit 1
echo "package download failed or stalled (attempt $i); retrying from archive.ubuntu.com"
for f in /etc/apt/sources.list /etc/apt/sources.list.d/*; do
[ -f "$f" ] || continue
sudo sed -i -e 's#mirror+file:/etc/apt/apt-mirrors.txt#http://archive.ubuntu.com/ubuntu/#g' \
-e 's#http://azure\.archive\.ubuntu\.com/ubuntu#http://archive.ubuntu.com/ubuntu#g' "$f"
done
sudo timeout 90 apt-get update -y \
-o Acquire::Retries=3 -o Acquire::http::Timeout=30 || true
done
sudo apt-get install -y \
-o Acquire::Retries=3 -o Acquire::http::Timeout=30 \
$pkgs
@@ -298,7 +200,7 @@ jobs:
steps:
- name: Install dependencies
timeout-minutes: 20
timeout-minutes: 8
run: |
pkgs="build-essential libxkbcommon-dev libx11-xcb-dev libxcb1-dev libxinerama-dev libxrandr-dev libxxf86vm-dev libxv-dev xvfb x11-utils zlib1g-dev libfreetype6-dev libvulkan-dev mesa-vulkan-drivers vulkan-validationlayers vulkan-tools libasound2-dev"
for i in 1 2 3; do
@@ -307,23 +209,6 @@ jobs:
echo "apt-get update failed (attempt $i); retrying in 10s"
sleep 10
done
# Fetch first, under a bound: a download is safe to stop and
# resumes, an install is not. A mirror that stalls is left for
# the main archive on the next attempt.
for i in 1 2 3; do
sudo timeout 240 apt-get install -y --download-only \
-o Acquire::Retries=3 -o Acquire::http::Timeout=30 \
$pkgs && break
[ $i = 3 ] && exit 1
echo "package download failed or stalled (attempt $i); retrying from archive.ubuntu.com"
for f in /etc/apt/sources.list /etc/apt/sources.list.d/*; do
[ -f "$f" ] || continue
sudo sed -i -e 's#mirror+file:/etc/apt/apt-mirrors.txt#http://archive.ubuntu.com/ubuntu/#g' \
-e 's#http://azure\.archive\.ubuntu\.com/ubuntu#http://archive.ubuntu.com/ubuntu#g' "$f"
done
sudo timeout 90 apt-get update -y \
-o Acquire::Retries=3 -o Acquire::http::Timeout=30 || true
done
sudo apt-get install -y \
-o Acquire::Retries=3 -o Acquire::http::Timeout=30 \
$pkgs
@@ -355,7 +240,6 @@ jobs:
export DISPLAY=:99
export VK_DRIVER_FILES=/usr/share/vulkan/icd.d/lvp_icd.json
SAN=address VK_DEBUG=1 CYCLES=1 make -C samples/gfx/threaded_video check-vulkan
HARNESS_CORE_XRGB8888=1 SAN=address VK_DEBUG=1 CYCLES=1 make -C samples/gfx/threaded_video check-vulkan
make -C samples/gfx/threaded_video clean
vulkan-tsan-harness:
@@ -365,7 +249,7 @@ jobs:
steps:
- name: Install dependencies
timeout-minutes: 20
timeout-minutes: 8
run: |
pkgs="build-essential libxkbcommon-dev libx11-xcb-dev libxcb1-dev libxinerama-dev libxrandr-dev libxxf86vm-dev libxv-dev xvfb x11-utils zlib1g-dev libfreetype6-dev libvulkan-dev mesa-vulkan-drivers vulkan-validationlayers vulkan-tools libasound2-dev"
for i in 1 2 3; do
@@ -374,23 +258,6 @@ jobs:
echo "apt-get update failed (attempt $i); retrying in 10s"
sleep 10
done
# Fetch first, under a bound: a download is safe to stop and
# resumes, an install is not. A mirror that stalls is left for
# the main archive on the next attempt.
for i in 1 2 3; do
sudo timeout 240 apt-get install -y --download-only \
-o Acquire::Retries=3 -o Acquire::http::Timeout=30 \
$pkgs && break
[ $i = 3 ] && exit 1
echo "package download failed or stalled (attempt $i); retrying from archive.ubuntu.com"
for f in /etc/apt/sources.list /etc/apt/sources.list.d/*; do
[ -f "$f" ] || continue
sudo sed -i -e 's#mirror+file:/etc/apt/apt-mirrors.txt#http://archive.ubuntu.com/ubuntu/#g' \
-e 's#http://azure\.archive\.ubuntu\.com/ubuntu#http://archive.ubuntu.com/ubuntu#g' "$f"
done
sudo timeout 90 apt-get update -y \
-o Acquire::Retries=3 -o Acquire::http::Timeout=30 || true
done
sudo apt-get install -y \
-o Acquire::Retries=3 -o Acquire::http::Timeout=30 \
$pkgs
@@ -436,7 +303,7 @@ jobs:
steps:
- name: Install dependencies
timeout-minutes: 20
timeout-minutes: 8
run: |
pkgs="build-essential libxkbcommon-dev libx11-xcb-dev libxcb1-dev libxinerama-dev libxrandr-dev libxxf86vm-dev libxv-dev xvfb x11-utils zlib1g-dev libfreetype6-dev libgl-dev libglx-dev libegl-dev libgles-dev libgl1-mesa-dri libglx-mesa0 mesa-utils libasound2-dev"
for i in 1 2 3; do
@@ -445,23 +312,6 @@ jobs:
echo "apt-get update failed (attempt $i); retrying in 10s"
sleep 10
done
# Fetch first, under a bound: a download is safe to stop and
# resumes, an install is not. A mirror that stalls is left for
# the main archive on the next attempt.
for i in 1 2 3; do
sudo timeout 240 apt-get install -y --download-only \
-o Acquire::Retries=3 -o Acquire::http::Timeout=30 \
$pkgs && break
[ $i = 3 ] && exit 1
echo "package download failed or stalled (attempt $i); retrying from archive.ubuntu.com"
for f in /etc/apt/sources.list /etc/apt/sources.list.d/*; do
[ -f "$f" ] || continue
sudo sed -i -e 's#mirror+file:/etc/apt/apt-mirrors.txt#http://archive.ubuntu.com/ubuntu/#g' \
-e 's#http://azure\.archive\.ubuntu\.com/ubuntu#http://archive.ubuntu.com/ubuntu#g' "$f"
done
sudo timeout 90 apt-get update -y \
-o Acquire::Retries=3 -o Acquire::http::Timeout=30 || true
done
sudo apt-get install -y \
-o Acquire::Retries=3 -o Acquire::http::Timeout=30 \
$pkgs
@@ -512,15 +362,8 @@ jobs:
# d3d12 runs the same lanes on the other explicit-API backend:
# its in-place texture update is fence-tagged against the frame
# that last copied from the upload buffer, which only a running
# driver exercises. It runs with the harness core pushing
# XRGB8888: d3d12 lends its framebuffer only for a row pitch
# on a 256-byte boundary, which 320 pixels reach at 4 bytes and
# not at 2, so that is the run in which a frame is lent and a
# window into the loan is read out of it (lent-window lane).
# driver exercises.
driver: [d3d11, d3d12]
include:
- driver: d3d12
core_xrgb8888: 1
steps:
- name: Checkout
@@ -552,6 +395,5 @@ jobs:
run: |
set -eu
samples/gfx/threaded_video/build.sh
HARNESS_CORE_XRGB8888=${{ matrix.core_xrgb8888 || '' }} \
SAN=none DRIVER=${{ matrix.driver }} CYCLES=1 GDB=1 make -C samples/gfx/threaded_video check-driver
SAN=none DRIVER=${{ matrix.driver }} CYCLES=1 GDB=1 make -C samples/gfx/threaded_video check-driver
make -C samples/gfx/threaded_video clean
-6
View File
@@ -33,12 +33,6 @@ jobs:
run: |
./configure --disable-qt --enable-xdelta
- name: Include order
run: |
# The tree's own headers are found before same-named ones in the
# system include dirs configure or pkg-config put on the line.
sh tools/include_order_check.sh
- name: Compile RA
run: |
make -j$(getconf _NPROCESSORS_ONLN) clean
-79
View File
@@ -32,58 +32,6 @@ jobs:
# companion, the grid, the tables, the dialogs - as AppKit does them.
tools/companion_cocoa_test_macos.sh 2>&1 | grep -v "^$"
- name: Metal texture behaviour the surface, overlay and lent-framebuffer paths assume
run: |
set -o pipefail
# The driver's Metal paths have only ever been compiled. This
# asserts the two behaviours they are built on, on the runner's
# real device: a texture replaced in place while GPU work that
# samples it is in flight - the animated preview's steady
# state, and the one path with no fence check or DO_NOT_WAIT to
# drop a frame with - and an overlay page holding textures the
# pack owns, where a page switch must not take them with it.
#
# 77 means the runner exposed no Metal device; the sample says
# so rather than passing quietly.
cd samples/gfx/metal_texture_update
make check; rc=$?
if [ "$rc" = 77 ]; then
echo "::warning::no Metal device on this runner; the check did not run"
exit 0
fi
exit "$rc"
- name: iCloud Drive cloud sync driver
run: |
set -o pipefail
# Includes network/cloud_sync/icloud_drive.m, with the iCloud
# container redirected to a temporary directory. Cloud sync
# waits for every request to report exactly once: a read whose
# local file could not be created never reported (the sync
# hung), and a cloud file that could not be loaded or a local
# file that could not be read reported twice. With iCloud
# unavailable, read and delete reported success. Also covers
# the ordinary read, missing file, upload and delete paths.
cd samples/network/icloud_drive
make clean check SANITIZER=address,undefined
- name: iCloud (CloudKit) cloud sync driver
run: |
set -o pipefail
# Includes network/cloud_sync/icloud.m, with CloudKit replaced by
# an in-memory database (+[CKContainer defaultContainer] is
# overridden in a category; real CKRecord/CKAsset/CKQuery). A
# read whose local file could not be written, or whose record
# held no readable data, reported success; an upload whose
# lookup failed created a duplicate record; a delete reported
# success before its deletes were sent, whatever became of
# them; and non-destructive sync was ignored. Checks each case,
# that deletes and uploads keep <path>-<time> with destructive
# sync off (and not for destructive sync or the manifest), and
# that every request reports exactly once.
cd samples/network/icloud_cloudkit
make clean check SANITIZER=address,undefined
- name: Compile RA
run: |
set -o pipefail
@@ -98,30 +46,3 @@ jobs:
name: RetroArch-${{ steps.slug.outputs.sha8 }}
path: |
build/Build/Products/Release
# The threaded-video harness on the Metal driver: the real frontend
# under Cocoa's own main (see samples/gfx/threaded_video/harness_cocoa.m),
# every lane the Vulkan and D3D legs run, on the runner's GPU. A
# Makefile build, since the harness links the tree's objects.
metal-harness:
runs-on: macos-latest
timeout-minutes: 60
steps:
- uses: actions/checkout@v6
- name: Configure and build (Makefile, no Qt)
run: |
set -o pipefail
./configure --disable-qt --disable-ffmpeg --disable-discord --disable-cheevos \
--disable-networking --disable-sdl2 --disable-sdl --disable-translate \
--disable-update_cores --disable-update_assets --disable-online_updater \
--disable-cdrom --disable-7zip --disable-ssl 2>&1 | tail -5
make -j$(sysctl -n hw.ncpu) GFX_INSTRUMENT=1 2>&1 | grep -E "error|warning: unused" || true
test -x retroarch
- name: Harness on Metal (RGB565 core, then XRGB8888)
run: |
set -o pipefail
SAN=none GFX_INSTRUMENT=1 DRIVER=metal CYCLES=1 make -C samples/gfx/threaded_video check-driver
HARNESS_CORE_XRGB8888=1 SAN=none GFX_INSTRUMENT=1 DRIVER=metal CYCLES=1 make -C samples/gfx/threaded_video check-driver
+16 -15
View File
@@ -21,27 +21,23 @@ jobs:
build:
runs-on: ubuntu-latest
timeout-minutes: 30
# The image is Alpine 3.15, whose musl cannot load the runner's
# Node 24, so JavaScript actions run on the host and only the
# build steps run inside the image.
env:
ORBIS_IMAGE: reallibretroretroarch/libretro-build-orbis:latest
container:
image: reallibretroretroarch/libretro-build-orbis:latest
options: --user root
steps:
- uses: actions/checkout@v4
- name: Install dependencies
run: |
apk add ncurses-dev make bash python2
apk add libintl icu-dev wget
wget https://dot.net/v1/dotnet-install.sh && chmod 755 dotnet-install.sh && ./dotnet-install.sh -c 3.0 --install-dir ~/cli
- name: Compile RA
run: |
docker run --rm --user root -v "$PWD":/src -w /src "$ORBIS_IMAGE" bash -c '
set -e
apk add ncurses-dev make bash python2
apk add libintl icu-dev wget
wget https://dot.net/v1/dotnet-install.sh && chmod 755 dotnet-install.sh && ./dotnet-install.sh -c 3.0 --install-dir ~/cli
export PATH=~/cli:$PATH # .net cli
make -f Makefile.orbis -j$(getconf _NPROCESSORS_ONLN) HAVE_STATIC_DUMMY=1 info all
clang --version
orbis-ld --version
'
export PATH=~/cli:$PATH # .net cli
make -f Makefile.orbis -j$(getconf _NPROCESSORS_ONLN) HAVE_STATIC_DUMMY=1 info all
- name: Get short SHA
id: slug
@@ -54,3 +50,8 @@ jobs:
retroarch_orbis.elf
retroarch_orbis.oelf
retroarch_orbis.self
- name: Version of binaries
run: |
clang --version
orbis-ld --version
-108
View File
@@ -1,108 +0,0 @@
name: CI removed struct members
# When a struct member goes away, the compiler finds every reader it
# compiles. The readers it does not compile are the ones no Linux box
# sees - the Objective-C drivers above all, and anything behind a
# platform #ifdef - so each of those is found by whichever job builds
# that platform, one round trip at a time.
#
# window_position_x and window_position_y went that way. Packing the
# pair into one word updated the Win32 and SDL3 users, which is what a
# grep of the .c and .h files turns up; ui_cocoa.m kept reading both
# and took out the macOS job and both PowerPC cross jobs at once. Those
# three are among the slowest jobs in the tree, and the answer they
# eventually gave was four lines in one file.
#
# tools/packed_field_check.py does not cover this. It knows the six
# names a size or origin pair is spelled with, and window_position_x is
# not one of them; on the broken tree it printed 1184 lines, none of
# them ui_cocoa.m. The question here is not what a member is called,
# it is whether anything still reaches for one the change took away.
#
# So this lane reads the change rather than the tree's vocabulary: the
# members a push or a pull request removes, minus the ones something
# still declares, against every reader in every language the tree is
# written in. It reads files rather than building them, so one job
# covers what the platform jobs between them would only find one at a
# time.
on:
push:
branches:
- master
pull_request:
branches:
- master
workflow_dispatch:
permissions:
contents: read
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: ${{ github.ref != 'refs/heads/master' }}
jobs:
removed-member-check:
name: readers of a removed member
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- name: Checkout
uses: actions/checkout@v5
with:
# Both ends of the range have to be present, and a pull
# request's base can be behind by any amount.
fetch-depth: 0
- name: Checker self-test
shell: bash
run: |
set -eu
# Fixture pass: a member removed along with its readers is
# clean, one left behind in a language no lane here compiles
# is caught, and a member that merely moved, one whose name
# another struct still declares, one that was renamed, one
# named only in a comment or a log string, one declared in a
# declarator list, and a name too common to reason about are
# each left alone.
#
# The fixtures are handed to the checker directly rather than
# taken from the tree, so this reports the same thing whether
# or not the tree currently passes - it measures the checker.
python3 tools/removed_member_check.py --selftest
- name: Range check
shell: bash
run: |
set -eu
# A pull request is measured against its base; a push against
# what was there before it. A force push or a first push
# leaves 'before' as zeroes, and a merge has more than one
# parent, so fall back to the first parent in either case.
if [ -n "${PR_BASE:-}" ]; then
base="$PR_BASE"; head="$PR_HEAD"
else
base="${EVENT_BEFORE:-}"; head="$GITHUB_SHA"
case "$base" in
''|*[!0]*) ;;
*) base= ;;
esac
if [ -z "$base" ] || ! git cat-file -e "$base^{commit}" 2>/dev/null; then
base="$(git rev-parse "$head^" 2>/dev/null || true)"
fi
fi
if [ -z "$base" ]; then
echo "no base commit to compare against; nothing to check"
exit 0
fi
echo "range: $base..$head"
# Every struct member the range removes, minus the ones the
# tree still declares somewhere, against every .c .h .m .mm
# .cpp .hpp and .inc reader of one.
python3 tools/removed_member_check.py --range "$base..$head"
env:
PR_BASE: ${{ github.event.pull_request.base.sha }}
PR_HEAD: ${{ github.event.pull_request.head.sha }}
EVENT_BEFORE: ${{ github.event.before }}
+4 -118
View File
@@ -6,18 +6,6 @@ on:
pull_request:
repository_dispatch:
types: [run_build]
release:
types: [published]
workflow_dispatch:
inputs:
release_tag:
description: Existing release tag to build (empty builds the selected ref)
type: string
default: ''
publish_release:
description: Upload the ARM64 package to the existing release
type: boolean
default: false
permissions:
contents: read
@@ -26,15 +14,13 @@ env:
ACTIONS_ALLOW_USE_UNSECURE_NODE_VERSION: true
concurrency:
group: ${{ github.workflow }}-${{ github.event_name }}-${{ github.event.pull_request.number || inputs.release_tag || github.ref }}
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: ${{ github.ref != 'refs/heads/master' }}
jobs:
msvc:
runs-on: windows-11-arm
timeout-minutes: 90
outputs:
release_tag: ${{ steps.source.outputs.release_tag }}
strategy:
matrix:
version: [2022]
@@ -43,29 +29,6 @@ jobs:
steps:
- uses: actions/checkout@v5
with:
ref: ${{ github.event.release.tag_name || inputs.release_tag || github.ref }}
- name: Record and verify the source revision
id: source
shell: pwsh
env:
RELEASE_TAG: ${{ github.event.release.tag_name || inputs.release_tag }}
PUBLISH_RELEASE: ${{ github.event_name == 'release' || inputs.publish_release == true }}
run: |
$revision = git rev-parse HEAD
if ($LASTEXITCODE) { throw 'Could not resolve the source revision.' }
"revision=$revision" >> $env:GITHUB_OUTPUT
if ($env:PUBLISH_RELEASE -eq 'true' -and !$env:RELEASE_TAG) {
throw 'Publishing requires an existing release tag.'
}
if ($env:RELEASE_TAG) {
$tagRevision = git rev-parse --verify "refs/tags/$($env:RELEASE_TAG)^{commit}"
if ($LASTEXITCODE -or $tagRevision -ne $revision) {
throw 'The checkout is not the requested release tag.'
}
}
"release_tag=$env:RELEASE_TAG" >> $env:GITHUB_OUTPUT
- name: Add msbuild to PATH
uses: microsoft/setup-msbuild@v1
@@ -77,88 +40,11 @@ jobs:
- name: Get short SHA
id: slug
shell: pwsh
env:
SOURCE_REVISION: ${{ steps.source.outputs.revision }}
run: echo "sha8=$($env:SOURCE_REVISION.Substring(0,8))" >> $env:GITHUB_OUTPUT
shell: powershell
run: echo "sha8=$('${{ github.sha }}'.Substring(0,8))" >> $env:GITHUB_OUTPUT
- uses: actions/upload-artifact@v4
with:
name: retroarch-${{ matrix.version }}-${{ matrix.configuration }}-${{ matrix.platform }}-${{ steps.slug.outputs.sha8 }}
path: |
pkg/msvc/${{ matrix.platform }}/${{ matrix.configuration }}/RetroArch-msvc${{ matrix.version }}.exe
- name: Checkout packaging tools from the workflow revision
if: matrix.configuration == 'Release'
uses: actions/checkout@v5
with:
ref: ${{ github.workflow_sha }}
path: .arm64-packaging
sparse-checkout: pkg/msvc/packaging
- name: Checkout pinned Ozone assets
if: matrix.configuration == 'Release'
uses: actions/checkout@v5
with:
repository: libretro/retroarch-assets
ref: 73106363e14e34c08a5854b4cfbc29f184e3b783
path: .arm64-assets
sparse-checkout: |
ozone
pkg
fonts
- name: Checkout pinned core information
if: matrix.configuration == 'Release'
uses: actions/checkout@v5
with:
repository: libretro/libretro-super
ref: 0532bd63ce3072f50ca4f4ada40d42aef565bd3f
path: .arm64-info
sparse-checkout: dist/info
- name: Verify and package ARM64 frontend
if: matrix.configuration == 'Release'
shell: pwsh
env:
SOURCE_REVISION: ${{ steps.source.outputs.revision }}
run: |
./.arm64-packaging/pkg/msvc/packaging/package-arm64.ps1 `
-Executable ./pkg/msvc/ARM64/Release/RetroArch-msvc2022.exe `
-SourceDirectory . -AssetsDirectory ./.arm64-assets -InfoDirectory ./.arm64-info `
-OutputDirectory ./arm64-dist -SourceRevision $env:SOURCE_REVISION `
-Repository $env:GITHUB_REPOSITORY `
-AssetsRevision 73106363e14e34c08a5854b4cfbc29f184e3b783 `
-InfoRevision 0532bd63ce3072f50ca4f4ada40d42aef565bd3f
- name: Upload ARM64 release package
if: matrix.configuration == 'Release'
uses: actions/upload-artifact@v4
with:
name: retroarch-windows-arm64-package
path: |
arm64-dist/RetroArch-WinARM64.zip
arm64-dist/RetroArch-WinARM64.zip.sha256
if-no-files-found: error
upload-release:
if: github.event_name == 'release' || (github.event_name == 'workflow_dispatch' && inputs.publish_release == true)
needs: msvc
runs-on: ubuntu-latest
timeout-minutes: 10
permissions:
contents: write
steps:
- uses: actions/download-artifact@v4
with:
name: retroarch-windows-arm64-package
path: arm64-dist
- name: Attach ARM64 package to the existing release
env:
GH_TOKEN: ${{ github.token }}
RELEASE_TAG: ${{ needs.msvc.outputs.release_tag }}
run: |
gh release view "$RELEASE_TAG" --repo "$GITHUB_REPOSITORY" > /dev/null
gh release upload "$RELEASE_TAG" --repo "$GITHUB_REPOSITORY" --clobber \
arm64-dist/RetroArch-WinARM64.zip arm64-dist/RetroArch-WinARM64.zip.sha256
+1 -1
View File
@@ -22,7 +22,7 @@ jobs:
runs-on: ubuntu-latest
timeout-minutes: 30
container:
image: reallibretroretroarch/libretro-build-mxe-win32-cross:gcc16
image: reallibretroretroarch/libretro-build-mxe-win32-cross:gcc10
options: --user root
steps:
+1 -1
View File
@@ -22,7 +22,7 @@ jobs:
runs-on: ubuntu-latest
timeout-minutes: 30
container:
image: reallibretroretroarch/libretro-build-mxe-win64-cross:gcc16
image: reallibretroretroarch/libretro-build-mxe-win64-cross:gcc10
options: --user root
steps:
-141
View File
@@ -1,141 +0,0 @@
name: CI procbarrier tiers
# retro_procbarrier picks one of several ways to force a memory barrier
# on every running thread, and which one depends on the OS and the CPU.
# The Linux samples job only ever runs the one its runner picks
# (membarrier). This runs every tier each platform has, on a multi-core
# runner so the fence checks can actually catch a missing barrier, and
# fails if a forced tier quietly fell back to another one.
#
# Linux x86_64 membarrier, page-flip, signal
# Linux arm64 membarrier, signal
# Windows x64 FlushProcessWriteBuffers, page-flip
# Windows arm64 FlushProcessWriteBuffers
# macOS arm64 signal
# macOS x86_64 page-flip, signal
on:
push:
branches: [master]
paths:
- 'libretro-common/rthreads/**'
- 'libretro-common/include/rthreads/**'
- 'libretro-common/include/retro_atomic.h'
- 'libretro-common/samples/rthreads/retro_procbarrier_test/**'
- 'libretro-common/samples/rthreads/retro_asym_eventcount_test/**'
- '.github/workflows/procbarrier-tiers.yml'
pull_request:
paths:
- 'libretro-common/rthreads/**'
- 'libretro-common/include/rthreads/**'
- 'libretro-common/include/retro_atomic.h'
- 'libretro-common/samples/rthreads/retro_procbarrier_test/**'
- 'libretro-common/samples/rthreads/retro_asym_eventcount_test/**'
- '.github/workflows/procbarrier-tiers.yml'
workflow_dispatch:
permissions:
contents: read
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: ${{ github.ref != 'refs/heads/master' }}
jobs:
posix:
name: ${{ matrix.name }}
runs-on: ${{ matrix.os }}
timeout-minutes: 15
strategy:
fail-fast: false
matrix:
include:
- name: linux-x86_64
os: ubuntu-latest
libs: -lpthread -lrt
tiers: natural=membarrier membarrier=membarrier pageflip=page-flip signal=signal
- name: linux-arm64
os: ubuntu-24.04-arm
libs: -lpthread -lrt
tiers: natural=membarrier membarrier=membarrier signal=signal
- name: macos-arm64
os: macos-latest
libs: -lpthread
tiers: natural=signal signal=signal
- name: macos-x86_64
os: macos-15-intel
libs: -lpthread
tiers: natural=page-flip pageflip=page-flip signal=signal
steps:
- uses: actions/checkout@v5
- name: Build
working-directory: libretro-common
run: |
set -e
cc -std=gnu99 -O1 -g -Wall -DHAVE_THREADS -Iinclude \
samples/rthreads/retro_procbarrier_test/retro_procbarrier_test.c \
rthreads/retro_procbarrier.c rthreads/rthreads.c \
-o retro_procbarrier_test ${{ matrix.libs }}
cc -std=gnu99 -O1 -g -Wall -DHAVE_THREADS -Iinclude \
samples/rthreads/retro_asym_eventcount_test/retro_asym_eventcount_test.c \
rthreads/retro_asym_eventcount.c \
rthreads/retro_procbarrier.c rthreads/rthreads.c \
-o retro_asym_eventcount_test ${{ matrix.libs }}
- name: Run every tier
working-directory: libretro-common
env:
REQUIRE_SMP: 1
run: |
bash samples/rthreads/retro_procbarrier_test/run_tiers.sh \
./retro_procbarrier_test ./retro_asym_eventcount_test \
${{ matrix.tiers }}
windows:
name: ${{ matrix.name }}
runs-on: ${{ matrix.os }}
timeout-minutes: 15
strategy:
fail-fast: false
matrix:
include:
- name: windows-x64
os: windows-2022
vcvars: vcvars64.bat
tiers: natural=FlushProcessWriteBuffers fpwb=FlushProcessWriteBuffers pageflip=page-flip
- name: windows-arm64
os: windows-11-arm
vcvars: vcvarsarm64.bat
tiers: natural=FlushProcessWriteBuffers fpwb=FlushProcessWriteBuffers
steps:
- uses: actions/checkout@v5
- name: Build
shell: cmd
working-directory: libretro-common
run: |
for /f "usebackq tokens=*" %%i in (`"%ProgramFiles(x86)%\Microsoft Visual Studio\Installer\vswhere.exe" -latest -products * -property installationPath`) do set VSPATH=%%i
if not defined VSPATH exit /b 1
call "%VSPATH%\VC\Auxiliary\Build\${{ matrix.vcvars }}"
cl /nologo /W3 /O1 /DHAVE_THREADS /Iinclude ^
samples\rthreads\retro_procbarrier_test\retro_procbarrier_test.c ^
rthreads\retro_procbarrier.c rthreads\rthreads.c ^
/Fe:retro_procbarrier_test.exe
if errorlevel 1 exit /b 1
cl /nologo /W3 /O1 /DHAVE_THREADS /Iinclude ^
samples\rthreads\retro_asym_eventcount_test\retro_asym_eventcount_test.c ^
rthreads\retro_asym_eventcount.c ^
rthreads\retro_procbarrier.c rthreads\rthreads.c ^
/Fe:retro_asym_eventcount_test.exe
if errorlevel 1 exit /b 1
- name: Run every tier
shell: bash
working-directory: libretro-common
env:
REQUIRE_SMP: 1
run: |
bash samples/rthreads/retro_procbarrier_test/run_tiers.sh \
./retro_procbarrier_test.exe ./retro_asym_eventcount_test.exe \
${{ matrix.tiers }}
+1 -18
View File
@@ -26,7 +26,7 @@ jobs:
timeout-minutes: 30
steps:
- name: Dependencies
timeout-minutes: 20
timeout-minutes: 5
run: |
# libvulkan-dev brings the Vulkan driver, the slang shader stack
# (shader_vulkan.c, slang_process.c, glslang_util.c, slang_cache.c)
@@ -43,23 +43,6 @@ jobs:
echo "apt-get update failed (attempt $i); retrying in 10s"
sleep 10
done
# Fetch first, under a bound: a download is safe to stop and
# resumes, an install is not. A mirror that stalls is left for
# the main archive on the next attempt.
for i in 1 2 3; do
sudo timeout 240 apt-get install -y --download-only \
-o Acquire::Retries=3 -o Acquire::http::Timeout=30 \
$pkgs && break
[ $i = 3 ] && exit 1
echo "package download failed or stalled (attempt $i); retrying from archive.ubuntu.com"
for f in /etc/apt/sources.list /etc/apt/sources.list.d/*; do
[ -f "$f" ] || continue
sudo sed -i -e 's#mirror+file:/etc/apt/apt-mirrors.txt#http://archive.ubuntu.com/ubuntu/#g' \
-e 's#http://azure\.archive\.ubuntu\.com/ubuntu#http://archive.ubuntu.com/ubuntu#g' "$f"
done
sudo timeout 90 apt-get update -y \
-o Acquire::Retries=3 -o Acquire::http::Timeout=30 || true
done
sudo apt-get install -y \
-o Acquire::Retries=3 -o Acquire::http::Timeout=30 \
$pkgs
+14 -681
View File
@@ -19,29 +19,10 @@ cd "$(dirname "$0")/../../.."
CC=${CC:-gcc}
INC="-I. -Ilibretro-common/include -Ideps -Ideps/rcheevos/include -Iinput/include"
WARN="-Wall -Wno-unused-function -Werror=implicit-function-declaration"
# gcc 14 rejects these outright and older gcc only warns; a lane on
# an older compiler has to reject them too, or a mismatched callback
# passes here and fails on the toolchains that build the platform.
WARN="$WARN -Werror=incompatible-pointer-types -Werror=int-conversion"
BASE="-DRARCH_INTERNAL -DHAVE_AUDIOMIXER -DHAVE_THREADS -DHAVE_CONFIGFILE -DHAVE_MENU"
# A console lane that keeps the host identity takes the Linux branch
# of every #if ladder and proves nothing, so this sits above the
# lanes rather than among them.
HOSTOFF="-U__linux__ -U__gnu_linux__ -Ulinux -U__unix__ -U__unix -Uunix"
fail=0
# A failing compile's output, errors first: a file with a page of
# warnings ahead of its one error pushed the error past the cut, which
# left a failing lane reporting nothing to act on.
show_out() {
if echo "$1" | grep -q ": error: "; then
echo "$1" | grep ": error: " | sed 's/^/ /' | head -8
else
echo "$1" | sed 's/^/ /' | head -12
fi
}
check() {
name="$1"; shift
defs="$1"; shift
@@ -50,7 +31,7 @@ check() {
if ! out=$($CC $WARN $INC $BASE $defs -fsyntax-only "$tu" 2>&1); then
echo "FAIL $name"
echo " $tu"
show_out "$out"
echo "$out" | sed 's/^/ /' | head -12
fail=1; bad=1
fi
done
@@ -117,16 +98,14 @@ ARMWARN="$WARN -Wno-deprecated-declarations"
# a lane whose headers are absent says so and is not a failure.
platform_video() {
name="$1"; defs="$2"; extra="$3"; tu="$4"; probe="$5"
for pr in $probe; do
if [ ! -f "$pr" ]; then
echo "skip $name (no $pr)"
return
fi
done
if [ -n "$probe" ] && [ ! -f "$probe" ]; then
echo "skip $name (no $probe)"
return
fi
if ! out=$($CC $WARN $extra $INC $BASE $defs -fsyntax-only "$tu" 2>&1); then
echo "FAIL $name"
echo " $tu"
show_out "$out"
printf '%s\n' "$out" | sed 's/^/ /' | head -10
fail=1
else
echo "ok $name"
@@ -161,118 +140,6 @@ arm "ps3" ps3 "-D__PSL1GHT__ -DHAVE_MEMINFO"
arm "ps2" "" "-DPS2"
arm "emscripten" emscripten "-D__EMSCRIPTEN__"
# The consoles that carry the cleanroom crypto, keychain and TLS client
# (3DS, Vita, Switch), each in its own shape with the SDK stand-ins the
# random source and the socket layer need. Vita's and the 3DS's socket
# layers are the SDKs' own, so the TLS client itself is compiled only
# in the Switch shape here; those two builds are their workflows' to
# link.
CRYPTO_SET="libretro-common/crypto/crypto.c libretro-common/crypto/kdf.c libretro-common/crypto/pk.c libretro-common/crypto/x509.c libretro-common/file/keychain.c"
TLS_SET="$CRYPTO_SET libretro-common/net/net_socket_ssl_retro.c network/tls_log.c"
CONSOLE_NET="-DHAVE_CRYPTO -DHAVE_KEYCHAIN -DHAVE_SSL -DHAVE_RETROSSL -DHAVE_NETWORKING -DHAVE_THREADS -DHAVE_CONFIGFILE -DRARCH_INTERNAL"
console_set() {
cs_name="$1"; cs_inc="$2"; cs_defs="$3"; shift 3
for cs_tu in "$@"; do
check "$cs_name: $(basename $cs_tu)" "$HOSTOFF $cs_inc $CONSOLE_NET $cs_defs $CDECL" "$cs_tu"
done
}
console_set "3ds crypto" "-Itools/platform_stubs/ctr" "-D_3DS -DARM11" $CRYPTO_SET
console_set "vita crypto" "-Itools/platform_stubs/vita" "-DVITA" $CRYPTO_SET
console_set "switch tls" "-I$STUBS/libnx" "-DHAVE_LIBNX -DSWITCH -D__SWITCH__" $TLS_SET
# The built-in NFS client, which the 3DS, Vita, Switch and Wii U
# build, under the defines their Makefiles set (RARCH_CONSOLE for the
# console sleep and the 3DS transfer cap, Vita's legacy socket layer):
# Wii U against its own wiiu/include, Vita against stubs of vitasdk's
# socket headers.
NFS_SET="libretro-common/net/net_nfs3.c libretro-common/vfs/vfs_implementation_nfs.c libretro-common/vfs/vfs_prefetch.c"
NFSD="-DHAVE_RETRONFS -DHAVE_NFSCLIENT"
console_set "3ds nfs" "-Itools/platform_stubs/ctr $NFSD" "-D_3DS -DARM11 -DRARCH_CONSOLE" $NFS_SET
console_set "vita nfs" "-Itools/platform_stubs/vita $NFSD" "-DVITA -DRARCH_CONSOLE -DHAVE_SOCKET_LEGACY" $NFS_SET
console_set "switch nfs" "-I$STUBS/libnx $NFSD" "-DHAVE_LIBNX -DSWITCH -D__SWITCH__ -DRARCH_CONSOLE" $NFS_SET
console_set "wiiu nfs" "-Iwiiu/include -Itools/platform_stubs/wiiu $NFSD" "-DWIIU -D__WUT__ -DHW_WUP -D__wiiu__ -DRARCH_CONSOLE" $NFS_SET
# The built-in SMB and Kerberos clients, which the 3DS, Vita and Switch
# build on their crypto (the Wii U has no kernel RNG for it to draw on).
SMB_SET="libretro-common/net/net_smb2.c libretro-common/net/net_krb5.c libretro-common/vfs/vfs_implementation_smb.c"
SMBD="-DHAVE_RETROSMB -DHAVE_SMBCLIENT"
console_set "3ds smb" "-Itools/platform_stubs/ctr $SMBD" "-D_3DS -DARM11 -DRARCH_CONSOLE" $SMB_SET
console_set "vita smb" "-Itools/platform_stubs/vita $SMBD" "-DVITA -DRARCH_CONSOLE -DHAVE_SOCKET_LEGACY" $SMB_SET
console_set "switch smb" "-I$STUBS/libnx $SMBD" "-DHAVE_LIBNX -DSWITCH -D__SWITCH__ -DRARCH_CONSOLE" $SMB_SET
# The network stack in the old MSVC projects (2005 - 2017), which no job
# here builds: 32-bit Windows, C89, at the Windows floors those projects
# target - VS2005's _WIN32_WINNT 0x0410 and XP's 0x0501. The stack keeps
# to what those compilers and SDKs have: C89, the LL/ULL suffixes code
# they already build uses, Windows entry points resolved at run time,
# and a CA bundle split into parts under MSVC's 64 KiB string limit.
MSVC_OLD_CC=${MSVC_OLD_CC:-i686-w64-mingw32-gcc}
if command -v "$MSVC_OLD_CC" > /dev/null 2>&1; then
MSVC_OLD_STACK="$CRYPTO_SET libretro-common/net/net_socket_ssl_retro.c network/tls_log.c libretro-common/net/net_smb2.c libretro-common/net/net_krb5.c libretro-common/net/net_nfs3.c libretro-common/vfs/vfs_implementation_smb.c libretro-common/vfs/vfs_implementation_nfs.c libretro-common/vfs/vfs_prefetch.c"
MSVC_OLD_DEFS="-DRARCH_INTERNAL -DHAVE_NETWORKING -DHAVE_THREADS -DHAVE_CONFIGFILE -DHAVE_CRYPTO -DHAVE_KEYCHAIN -DHAVE_SSL -DHAVE_RETROSSL -DHAVE_SMBCLIENT -DHAVE_RETROSMB -DHAVE_NFSCLIENT -DHAVE_RETRONFS -DLEGACY_WIN32_NO_MIGRATION_NOTE"
for winnt in 0x0410 0x0501; do
mo_bad=0
for tu in $MSVC_OLD_STACK; do
if ! out=$($MSVC_OLD_CC -std=c89 -ansi -pedantic -Werror=pedantic -Werror=declaration-after-statement -Wno-long-long -Wno-variadic-macros -Werror=implicit-function-declaration -Wall -Wno-unused-function -Wno-overlength-strings \
-I. -Ilibretro-common/include -D_WIN32_WINNT=$winnt $MSVC_OLD_DEFS -fsyntax-only "$tu" 2>&1) \
|| echo "$out" | grep -q "warning:"; then
echo "FAIL msvc-old network stack, _WIN32_WINNT=$winnt"
echo " $tu"
show_out "$out"
fail=1; mo_bad=1
fi
done
[ "$mo_bad" = 1 ] || echo "ok msvc-old network stack, _WIN32_WINNT=$winnt"
done
# and no single string literal in the CA bundle reaches MSVC's limit
if python3 - << 'PYEOF'
import re, sys
t = open("libretro-common/net/cacert.h", encoding="latin-1").read()
for body in re.findall(r"\[\] = \{(.*?)\};", t, re.S):
lits = re.findall(r'"((?:[^"\\]|\\.)*)"', body)
size = sum(len(l.encode("latin-1").decode("unicode_escape")) for l in lits) + len(lits)
if size >= 65535:
sys.exit(1)
PYEOF
then
echo "ok cacert.h parts under MSVC's 64 KiB string limit"
else
echo "FAIL cacert.h has a part at or over MSVC's 64 KiB string limit"; fail=1
fi
else
echo "skip msvc-old network stack (no $MSVC_OLD_CC)"
fi
# Every platform makefile on Makefile.common, without its toolchain: the
# graph's own DEFINES checked for consistency (TLS, SMB, NFS need the
# networking they are built on) and the frontend compiled under exactly
# them. Makefile.retrofw asked for the TLS client with no networking
# and only its own workflow saw the undefined reference.
pm_out=$(tools/platform_makefile_check.sh 2>&1); pm_rc=$?
printf '%s\n' "$pm_out" | sed 's/^ok /ok platform makefile: /'
[ $pm_rc -eq 0 ] || { echo "FAIL platform makefile check"; fail=1; }
# What each console Makefile actually builds, from its own object list:
# no MCP server on any console (a desktop HAVE_MCP option; listed once
# with the networking objects it broke the 3DS), and no crypto library
# on the 24/32 MiB machines.
co_out=$(tools/console_objects_check.sh 2>&1); co_rc=$?
printf '%s\n' "$co_out" | sed 's/^ok /ok console objects: /'
[ $co_rc -eq 0 ] || { echo "FAIL console object check"; fail=1; }
# The OpenDingux family builds with a MIPS toolchain; the crypto and the
# TLS client compile with a MIPS cross compiler when one is installed.
MIPSEL_CC=${MIPSEL_CC:-mipsel-linux-gnu-gcc}
if command -v "$MIPSEL_CC" >/dev/null 2>&1; then
for tu in $TLS_SET; do
if out=$($MIPSEL_CC -march=mips32r2 $INC $CONSOLE_NET -DDINGUX -D_GNU_SOURCE -Wall -Werror -Wno-unused-function -fsyntax-only "$tu" 2>&1); then
echo "ok dingux mips: $(basename $tu)"
else
echo "FAIL dingux mips: $(basename $tu)"; show_out "$out"; fail=1
fi
done
else
echo "skip dingux mips (no $MIPSEL_CC)"
fi
platform_video "odroidgo2 video" \
"-DHAVE_ODROIDGO2 -DHAVE_OPENGL -DHAVE_GLSL" "" \
gfx/drivers/gl2.c ""
@@ -281,192 +148,14 @@ platform_video "gx video" "-DGEKKO -DHW_RVL" "-I$STUBS/gx" \
platform_video "switch video" \
"-DHAVE_LIBNX -DSWITCH -D__SWITCH__" "-I$STUBS/libnx" \
gfx/drivers/switch_nx_gfx.c ""
# The PSP driver folds its VRAM and cache-alias addresses into pointers,
# which a 64-bit host narrows; the arithmetic is 32-bit on the target.
platform_video "psp1 video" "-DPSP" \
"-Itools/platform_stubs/psp -Wno-pointer-to-int-cast -Wno-int-to-pointer-cast \
-Wdeclaration-after-statement -Werror=declaration-after-statement" \
gfx/drivers/psp1_gfx.c ""
# The Vita driver, against psp2 stubs carrying the SceGxm declarations it
# names in the shapes the real headers give them. Same narrowing as the
# PSP driver above: its pool addresses are 32-bit on the target.
platform_video "gxm video" "-DVITA -DRARCH_CONSOLE $HOSTOFF" \
"-Itools/platform_stubs/vita -Wno-pointer-to-int-cast -Wno-int-to-pointer-cast" \
gfx/drivers/gxm_gfx.c ""
# gl1 on Vita, against a vitaGL stub over the host GL headers. The
# driver's Vita branches are compiled nowhere else, and one of them
# carried a declaration after a statement that only the Vita job saw.
platform_video "vita gl1 video" \
"-DVITA -DRARCH_CONSOLE -DHAVE_OPENGL1 -DHAVE_OVERLAY -DHAVE_GFX_WIDGETS $HOSTOFF" \
"-Itools/platform_stubs/vita -Wdeclaration-after-statement \
-Werror=declaration-after-statement" \
gfx/drivers/gl1.c /usr/include/GL/gl.h
# The PS2 driver, against gsKit and PS2SDK stubs carrying what it
# names in the shapes the real headers give them. Makefile.ps2 turns on
# the window offset, which the driver reads. This lane is also the only
# one that compiles retro_atomic.h's EE interrupt-mask backend, which
# no other platform selects.
platform_video "ps2 video" \
"-DPS2 -DRARCH_CONSOLE -DHAVE_WINDOW_OFFSET -DHAVE_RGUI $HOSTOFF" \
"-Itools/platform_stubs/ps2" gfx/drivers/ps2_gfx.c ""
# The DOS VGA driver, against DJGPP stubs for the DPMI and port I/O it
# uses. Only the DJGPP job compiled it before; DJGPP defines __unix__.
platform_video "dos vga video" \
"-D__DJGPP__ -DDJGPP -DRARCH_CONSOLE $HOSTOFF -D__unix__" \
"-Itools/platform_stubs/dos -Wdeclaration-after-statement \
-Werror=declaration-after-statement" \
gfx/drivers/vga_gfx.c ""
# The two SDL video drivers. Nothing else here compiled them, which is
# how a field they read through video_info_t went on being read after it
# had been packed away. Each skips where its headers are absent, as the
# dingux lanes below do.
platform_video "sdl2 video" "-DHAVE_SDL2" "-I/usr/include/SDL2" \
gfx/drivers/sdl2_gfx.c /usr/include/SDL2/SDL.h
platform_video "sdl3 video" "-DHAVE_SDL3" "-I/usr/include/SDL3" \
gfx/drivers/sdl3_gfx.c /usr/include/SDL3/SDL.h
platform_video "dingux video" "-DDINGUX" "-I/usr/include/SDL" \
gfx/drivers/sdl_dingux_gfx.c /usr/include/SDL/SDL.h
platform_video "rs90 video" "-DDINGUX -DRS90" "-I/usr/include/SDL" \
gfx/drivers/sdl_rs90_gfx.c /usr/include/SDL/SDL.h
# libdrm's exynos module and its G2D headers, which distros ship only
# where the hardware exists. A runner building libdrm with -Dexynos=true
# turns this on.
platform_video "wiiu gx2 video" "-DWIIU $HOSTOFF" \
"-Iwiiu/include -Iwiiu" gfx/drivers/gx2_gfx.c ""
platform_video "hub75 video" "" "" gfx/drivers/hub75_gfx.c ""
platform_video "openvg video" "-DHAVE_VG -DHAVE_EGL" \
"-Itools/platform_stubs/openvg" gfx/drivers/vg.c ""
platform_video "exynos video" "-DHAVE_EXYNOS" "-I/usr/include/libdrm" \
gfx/drivers/exynos_gfx.c /usr/include/exynos/exynos_fimg2d.h
# The Direct3D drivers, against the DirectX headers the tree vendors in
# gfx/include/dxsdk. Six drivers no other lane compiles, and they read
# the same frame and draw descriptors every other video driver does.
#
# A mingw-w64 cross compiler is all these need beyond the tree; where
# there is none the lanes say so rather than fail.
MINGW_CC=${MINGW_CC:-x86_64-w64-mingw32-gcc}
d3d_video() {
name="$1"; defs="$2"; tu="$3"
if ! command -v "$MINGW_CC" > /dev/null 2>&1; then
echo "skip $name (no $MINGW_CC)"
return
fi
if ! out=$($MINGW_CC $WARN -isystemgfx/include/dxsdk $INC $BASE $defs \
-fsyntax-only "$tu" 2>&1); then
echo "FAIL $name"
echo " $tu"
show_out "$out"
fail=1
else
echo "ok $name"
fi
}
D3DDEFS="-DHAVE_D3D -DHAVE_RGUI -DHAVE_OVERLAY"
d3d_video "d3d8 video" "$D3DDEFS -DHAVE_D3D8" gfx/drivers/d3d8.c
d3d_video "d3d9 video, Cg" "$D3DDEFS -DHAVE_D3D9" gfx/drivers/d3d9cg.c
d3d_video "d3d9 video, HLSL" "$D3DDEFS -DHAVE_D3D9" gfx/drivers/d3d9hlsl.c
d3d_video "d3d10 video" "$D3DDEFS -DHAVE_D3D10" gfx/drivers/d3d10.c
d3d_video "d3d11 video" "$D3DDEFS -DHAVE_D3D11" gfx/drivers/d3d11.c
d3d_video "d3d12 video" "$D3DDEFS -DHAVE_D3D12" gfx/drivers/d3d12.c
d3d_video "gdi video" "-DHAVE_RGUI -DHAVE_OVERLAY -DHAVE_GDI" \
gfx/drivers/gdi_gfx.c
# The context drivers, which no job here compiles either. Each answers
# the frontend's window and size questions, so a change to what those
# hand back reaches all of them at once. Every lane names the headers it
# wants and says so when the runner has none.
platform_video "ctx: null" "" "" gfx/drivers_context/gfx_null_ctx.c ""
platform_video "ctx: x11 gl" "-DHAVE_X11 -DHAVE_OPENGL -DHAVE_EGL" "" \
gfx/drivers_context/x_ctx.c /usr/include/X11/Xlib.h
platform_video "ctx: x11 egl" "-DHAVE_X11 -DHAVE_EGL -DHAVE_OPENGLES" "" \
gfx/drivers_context/xegl_ctx.c /usr/include/X11/Xlib.h
platform_video "ctx: x11 vulkan" "-DHAVE_VULKAN -DHAVE_X11 -DHAVE_XCB" "" \
gfx/drivers_context/x_vk_ctx.c \
"/usr/include/vulkan/vulkan.h /usr/include/X11/Xlib-xcb.h"
platform_video "ctx: khr display" "-DHAVE_VULKAN" "" \
gfx/drivers_context/khr_display_ctx.c /usr/include/vulkan/vulkan.h
platform_video "ctx: kms/gbm" "-DHAVE_EGL -DHAVE_OPENGL -DHAVE_KMS -DHAVE_GBM" \
"-I/usr/include/libdrm" gfx/drivers_context/drm_ctx.c /usr/include/gbm.h
# vivante_fbdev is left out: it calls Vivante's own fbCreateWindow and
# fbGetDisplayByIndex, which only that vendor's EGL headers declare, and
# a lane that let those go implicit would pass what the SDK rejects.
platform_video "ctx: mali fbdev" "-DHAVE_EGL -DHAVE_OPENGLES" "" \
gfx/drivers_context/mali_fbdev_ctx.c /usr/include/EGL/egl.h
platform_video "ctx: opendingux fbdev" "-DHAVE_EGL -DHAVE_OPENGLES -DDINGUX" "" \
gfx/drivers_context/opendingux_fbdev_ctx.c /usr/include/EGL/egl.h
platform_video "ctx: sdl1 gl" "-DHAVE_SDL -DHAVE_OPENGL" "-I/usr/include/SDL" \
gfx/drivers_context/sdl1_gl_ctx.c /usr/include/SDL/SDL.h
platform_video "ctx: sdl2 gl" "-DHAVE_SDL2 -DHAVE_OPENGL" "-I/usr/include/SDL2" \
gfx/drivers_context/sdl2_gl_ctx.c /usr/include/SDL2/SDL.h
# osmesa is the software context a headless build selects, and
# qb/config.libs.sh can enable it from a pkg-config probe alone.
platform_video "ctx: osmesa" "-DHAVE_OSMESA -DHAVE_OPENGL" "" \
gfx/drivers_context/osmesa_ctx.c /usr/include/GL/osmesa.h
# The Wayland protocol headers are generated into gfx/common/wayland by
# configure, so these lanes run only in a tree that has been configured
# with Wayland.
WL_H="/usr/include/wayland-client.h gfx/common/wayland/content-type-v1.h"
platform_video "ctx: wayland gl" "-DHAVE_WAYLAND -DHAVE_EGL -DHAVE_OPENGL" "" \
gfx/drivers_context/wayland_ctx.c "$WL_H"
platform_video "ctx: wayland vulkan" "-DHAVE_WAYLAND -DHAVE_VULKAN" "" \
gfx/drivers_context/wayland_vk_ctx.c "$WL_H /usr/include/vulkan/vulkan.h"
# The shared Wayland code the two contexts above call into. It was the
# only Wayland unit no lane compiled, which is how four declarations
# after a statement sat in it. Its libdecor half is a second lane: the
# function pointers that half calls through are declared under
# HAVE_LIBDECOR_H *and* HAVE_DYLIB, so a lane naming only the first
# proves nothing, and the header libdecor.h itself comes from a
# libdecor-0 include directory the probe checks for.
WLC89="-Wdeclaration-after-statement -Werror=declaration-after-statement"
platform_video "wayland common" "-DHAVE_WAYLAND -DHAVE_EGL -DHAVE_OPENGL" \
"$WLC89" gfx/common/wayland_common.c "$WL_H"
platform_video "wayland common: libdecor" \
"-DHAVE_WAYLAND -DHAVE_EGL -DHAVE_OPENGL -DHAVE_LIBDECOR_H -DHAVE_DYLIB" \
"-I/usr/include/libdecor-0 $WLC89" \
gfx/common/wayland_common.c "$WL_H /usr/include/libdecor-0/libdecor.h"
platform_video "wayland common: backport" \
"-DHAVE_WAYLAND -DHAVE_EGL -DHAVE_OPENGL -DHAVE_WAYLAND_BACKPORT=1" \
"$WLC89" gfx/common/wayland_common.c "$WL_H"
# webOS swaps its own shim in for half of the common Wayland functions,
# and the Wayland context reaches it through prototypes of its own, so
# the shim and the context each get a lane. The shim's webOS protocol
# headers are wayland-scanner output of LG's webos-wayland-extensions
# (Apache-2.0), which the SDK generates at build time; stubs/webos keeps
# a copy laid out so the shim's ../../gfx/common/wayland includes find it.
WEBOS="-DWEBOS -DWEBOS_APP_ID=\"com.retroarch\" -DHAVE_WAYLAND -DHAVE_EGL"
WEBOS="$WEBOS -DHAVE_OPENGLES"
platform_video "ctx: wayland gl (webos)" "$WEBOS" "" \
gfx/drivers_context/wayland_ctx.c "$WL_H"
platform_video "webos wayland shim" "$WEBOS" \
"-I$STUBS/webos/gfx/common" \
input/common/wayland_common_webos.c "$WL_H"
arm "win32" win32 "-D_WIN32 -D_WIN32_WINNT=0x0600"
arm "win32-old" win32 "-D_WIN32 -D_WIN32_WINNT=0x0400"
arm "macos" apple "-D__APPLE__"
arm "ios" apple "-D__APPLE__ -DTARGET_OS_IPHONE=1"
# The GL core driver without slang: its HDR encode is slang's, and state
# for it once sat outside the slang guards, which only the C89 lane -
# built without slang - ever compiled.
platform_video "glcore without slang" "-DHAVE_OPENGL -DHAVE_OPENGL_CORE" \
"" gfx/drivers/gl3.c "/usr/include/GL/gl.h"
platform_video "glcore with slang" \
"-DHAVE_OPENGL -DHAVE_OPENGL_CORE -DHAVE_SLANG -DHAVE_SHADERPIPELINE" \
"" gfx/drivers/gl3.c "/usr/include/GL/gl.h"
# The CoreText rasterizer and the font driver's Apple branch, against
# stand-ins for the SDK headers it includes: no runner builds it but the
# Apple jobs, so a change to the rasterizer interface reached them first.
CORETEXT="$HOSTOFF -D__APPLE__ -D__MACH__ -DHAVE_CORETEXT"
platform_video "macos coretext rasterizer" "$CORETEXT" "-I$STUBS/apple" \
gfx/drivers_font_renderer/coretext.c ""
platform_video "ios coretext rasterizer" "$CORETEXT -DTARGET_OS_IPHONE=1" \
"-I$STUBS/apple" gfx/drivers_font_renderer/coretext.c ""
platform_video "macos font driver" "$CORETEXT" "-I$STUBS/apple" \
gfx/font_driver.c ""
arm "dos/djgpp" "" "-D__DJGPP__ -D__unix__"
arm "linux" "" ""
@@ -501,51 +190,6 @@ check "gl3: GLES3" \
C89="-std=c89 -ansi -pedantic -Werror=pedantic -Werror=declaration-after-statement -Wno-long-long -Wno-variadic-macros -D_GNU_SOURCE -DC89_BUILD"
check "gl3: desktop, C89" \
"$GL3DEFS $GLINC $C89" gfx/drivers/gl3.c
# Without threads, in C89: audio_driver.h and rthreads.h both reach the
# tasks and the audio driver, and a typedef each declared on its own is
# one C89 refuses twice. make C89_BUILD=1 --disable-threads stopped at
# task_save.c on it; no job builds that shape. check() always adds
# HAVE_THREADS, so this lane spells its defines out.
NOTHREADS_BASE=$(echo "$BASE" | sed 's/-DHAVE_THREADS//')
nt_bad=0
for tu in tasks/task_save.c tasks/task_audio_mixer.c \
audio/audio_driver.c audio/audio_thread_wrapper.c; do
if ! out=$($CC $WARN $INC $NOTHREADS_BASE $C89 -fsyntax-only "$tu" 2>&1); then
echo "FAIL audio + tasks: no threads, C89"
echo " $tu"
show_out "$out"
fail=1; nt_bad=1
fi
done
[ "$nt_bad" = 1 ] || echo "ok audio + tasks: no threads, C89"
# WASAPI is in the Windows C89_BUILD lane, which no job here builds, and
# its error-string helper serves only the microphone path, so both halves
# are compiled: C89 flags, with and without HAVE_MICROPHONE, unused
# functions as errors. A real compile rather than -fsyntax-only, which
# stops before GCC looks for unused static functions.
win32_audio() {
name="$1"; defs="$2"; tu="$3"
if ! command -v "$MINGW_CC" > /dev/null 2>&1; then
echo "skip $name (no $MINGW_CC)"
return
fi
if ! out=$($MINGW_CC $WARN -Wunused-function -Werror=unused-function \
$INC $BASE $C89 \
$defs -c -o /dev/null "$tu" 2>&1); then
echo "FAIL $name"
echo " $tu"
show_out "$out"
fail=1
else
echo "ok $name"
fi
}
win32_audio "wasapi: C89, microphone" "-DHAVE_WASAPI -DHAVE_MICROPHONE" \
audio/drivers/wasapi.c
win32_audio "wasapi: C89, no microphone" "-DHAVE_WASAPI" \
audio/drivers/wasapi.c
check "hw ring: OpenGL only" \
"-DHAVE_OPENGL $GLINC" gfx/video_thread_hw.c
check "hw ring: GLES only" \
@@ -572,7 +216,7 @@ check_nothreads() {
if ! out=$($CC $WARN $INC $NOTHREADS $defs -fsyntax-only "$tu" 2>&1); then
echo "FAIL $name"
echo " $tu"
show_out "$out"
echo "$out" | sed 's/^/ /' | head -12
fail=1; bad=1
fi
done
@@ -591,10 +235,10 @@ check_nothreads() {
# eventcount park path (threads on).
check "audio: openal" "-Itools/platform_stubs/openal -DHAVE_AL -DHAVE_THREADS -Wdeclaration-after-statement -Werror=declaration-after-statement" audio/drivers/openal.c
# psp/psp2 audio: index-pair SPSC + eventcount park, one driver per
# platform; compile-only sce stubs.
# psp_audio: index-pair SPSC + eventcount park; compile-only sce
# stubs, PSP and Vita arms.
check "psp: psp_audio" "-Itools/platform_stubs/psp -DPSP -DHAVE_THREADS -Wdeclaration-after-statement -Werror=declaration-after-statement" audio/drivers/psp_audio.c
check "vita: psp2_audio" "-Itools/platform_stubs/vita -DVITA -DHAVE_THREADS -Wdeclaration-after-statement -Werror=declaration-after-statement" audio/drivers/psp2_audio.c
check "vita: psp_audio" "-Itools/platform_stubs/vita -DVITA -DHAVE_THREADS -Wdeclaration-after-statement -Werror=declaration-after-statement" audio/drivers/psp_audio.c
# features_cpu.c on every statically linked platform. It owns
# cpu_features_get_time_usec() and retro_sleep_until_us(); the wait's
@@ -607,6 +251,7 @@ check "vita: psp2_audio" "-Itools/platform_stubs/vita -DVITA -DHAVE_THREADS -Wde
# them. PSP gets _POSIX_C_SOURCE for the host libc's nanosleep, which
# pspsdk's newlib declares unconditionally.
FCPU_TU=libretro-common/features/features_cpu.c
HOSTOFF="-U__linux__ -U__gnu_linux__ -Ulinux -U__unix__ -U__unix -Uunix"
check "features_cpu: 3ds" "$HOSTOFF -Itools/platform_stubs/ctr -D_3DS -D__3DS__ -DARM11 -DRARCH_CONSOLE" $FCPU_TU
check "features_cpu: gekko" "$HOSTOFF -Itools/platform_stubs/gekko -DGEKKO -DHW_RVL -DRARCH_CONSOLE" $FCPU_TU
check "features_cpu: wiiu" "$HOSTOFF -Itools/platform_stubs/wiiu -DWIIU -DRARCH_CONSOLE" $FCPU_TU
@@ -616,118 +261,6 @@ check "features_cpu: ps3" "$HOSTOFF -Itools/platform_stubs/ps3 -D__PS3__
check "features_cpu: psl1ght" "$HOSTOFF -Itools/platform_stubs/psl1ght -D__PS3__ -D__PSL1GHT__ -DRARCH_CONSOLE" $FCPU_TU
check "features_cpu: emscripten" "$HOSTOFF -Itools/platform_stubs/emscripten -D__EMSCRIPTEN__ -DEMSCRIPTEN" $FCPU_TU
# Shared menu and video code under the console platform macros. These
# files are compiled by every desktop job, but the blocks inside them
# that a console selects are not, and a change to a struct those blocks
# touch is green everywhere until a console job runs. The frame and
# draw descriptors they read need no SDK header, so these lanes take
# the stubs already here and shed the host identity like the ones
# above.
MENUGFX="gfx/video_driver.c gfx/gfx_display.c gfx/gfx_widgets.c \
gfx/gfx_thumbnail.c gfx/gfx_surface.c gfx/video_thread_wrapper.c \
menu/drivers/xmb.c menu/drivers/ozone.c menu/drivers/materialui.c"
MG_BASE="-DHAVE_XMB -DHAVE_OZONE -DHAVE_MATERIALUI -DHAVE_RGUI \
-DHAVE_GFX_WIDGETS -DHAVE_OVERLAY -DHAVE_CONFIGFILE -DRARCH_CONSOLE \
-DRARCH_INTERNAL -Iinput/include"
check "console menu+gfx: vita" "$HOSTOFF $MG_BASE -Itools/platform_stubs/vita -DVITA" $MENUGFX
check "console menu+gfx: wiiu" "$HOSTOFF $MG_BASE -Itools/platform_stubs/wiiu -DWIIU" $MENUGFX
check "console menu+gfx: psl1ght" "$HOSTOFF $MG_BASE -Itools/platform_stubs/psl1ght -D__PS3__ -D__PSL1GHT__" $MENUGFX
# video_thread_wrapper.c reaches for libctru's own allocator on 3DS,
# and a guessed linearMemAlign() would let this lane pass what the real
# SDK rejects, so the 3DS lane takes the rest.
check "console menu+gfx: 3ds" "$HOSTOFF $MG_BASE -Itools/platform_stubs/ctr -D_3DS -D__3DS__" \
$(echo "$MENUGFX" | tr ' ' '\n' | grep -v video_thread_wrapper)
check "console menu+gfx: gekko" "$HOSTOFF $MG_BASE -Itools/platform_stubs/gekko -DGEKKO -DHW_RVL" $MENUGFX
# Platform drivers no other job compiles. Each of these reaches a
# compiler only inside its own console toolchain - griffin includes it
# behind that platform's #ifdef and the desktop makefiles never build
# it - so a C89 slip or a missing declaration in one sits until the
# console job runs, which is how a mixed declaration lived in
# gx_joypad.c. These are the ones the stubs already in the tree can
# reach, with one stub added for the pair of PSP input drivers - the
# 3ds audio drivers want fifteen more symbols whose libctru signatures
# cannot be checked from here, and a stub that guessed one would let a
# lane pass what the real SDK rejects, so they are left out.
# The rest need headers no stub here supplies. psp1_gfx and
# dispserv_android already have lanes of their own - the latter gains
# the C89 declaration check below rather than a second lane.
#
# Same warnings as the driver lanes above, C89 declarations included,
# because that is the rule these files are furthest from anyone
# checking. GEKKO takes the vendored libogc headers before its stub:
# the stub carries only what libogc does not.
CDECL="-Wdeclaration-after-statement -Werror=declaration-after-statement"
GEKKO_INC="-Iwii/libogc/include -Itools/platform_stubs/gekko"
check "gekko: gx_input" "$HOSTOFF $GEKKO_INC -DGEKKO -DHW_RVL -DRARCH_CONSOLE $CDECL" input/drivers/gx_input.c
check "gekko: gx_joypad" "$HOSTOFF $GEKKO_INC -DGEKKO -DHW_RVL -DRARCH_CONSOLE $CDECL" input/drivers_joypad/gx_joypad.c
check "gekko: mem2_manager" "$HOSTOFF $GEKKO_INC -DGEKKO -DHW_RVL -DRARCH_CONSOLE $CDECL" libretro-common/memory/mem2_manager.c
check "gekko: dispserv_gx" "$HOSTOFF $GEKKO_INC -DGEKKO -DHW_RVL -DRARCH_CONSOLE $CDECL" gfx/display_servers/dispserv_gx.c
check "gamecube: dispserv_gx" "$HOSTOFF $GEKKO_INC -DGEKKO -DRARCH_CONSOLE $CDECL" gfx/display_servers/dispserv_gx.c
check "gekko: dispserv_gx_modes" "$HOSTOFF $GEKKO_INC -DGEKKO -DHW_RVL -DRARCH_CONSOLE $CDECL" gfx/display_servers/dispserv_gx_modes.c
check "psl1ght: dispserv_ps3" "$HOSTOFF -Itools/platform_stubs/ps3 -D__PS3__ -D__PSL1GHT__ -DRARCH_CONSOLE $CDECL" gfx/display_servers/dispserv_ps3.c gfx/display_servers/dispserv_ps3_modes.c
check "3ds: ctr_input" "$HOSTOFF -Itools/platform_stubs/ctr -D_3DS -D__3DS__ -DARM11 -DRARCH_CONSOLE $CDECL" input/drivers/ctr_input.c
PSP_DEFS="$HOSTOFF -Itools/platform_stubs/psp -DPSP -D_POSIX_C_SOURCE=199309L -DRARCH_CONSOLE"
check "psp: psp_input" "$PSP_DEFS $CDECL" input/drivers/psp_input.c
check "psp: psp_joypad" "$PSP_DEFS $CDECL" input/drivers_joypad/psp_joypad.c
check "orbis: ps4_audio" "$HOSTOFF -Itools/platform_stubs/orbis -DORBIS $CDECL" audio/drivers/ps4_audio.c
check "qnx: alsa_qsa" "$HOSTOFF -Itools/platform_stubs/qnx -D__QNX__ $CDECL" audio/drivers/alsa_qsa.c
check "android: vfs saf" "$HOSTOFF -Itools/platform_stubs/android -DANDROID $CDECL" libretro-common/vfs/vfs_implementation_saf.c
check "android: play delivery" "$HOSTOFF -Itools/platform_stubs/android -DANDROID $CDECL" play_feature_delivery/play_feature_delivery.c
# the Android build carries the built-in NFS client and its backend
# (HAVE_RETRONFS in pkg/android/phoenix-common/jni/Android.mk) and the
# prefetcher both network backends read through
check "android: nfs client" "$HOSTOFF -Itools/platform_stubs/android -DANDROID -DHAVE_NFSCLIENT -DHAVE_RETRONFS -DHAVE_THREADS -DHAVE_NETWORKING $CDECL" libretro-common/net/net_nfs3.c
check "android: nfs backend" "$HOSTOFF -Itools/platform_stubs/android -DANDROID -DHAVE_NFSCLIENT -DHAVE_RETRONFS -DHAVE_THREADS -DHAVE_NETWORKING -DRARCH_INTERNAL $CDECL" libretro-common/vfs/vfs_implementation_nfs.c
check "android: vfs prefetch" "$HOSTOFF -Itools/platform_stubs/android -DANDROID -DHAVE_THREADS $CDECL" libretro-common/vfs/vfs_prefetch.c
# The Android build carries the cleanroom network stack (Android.mk:
# HAVE_CRYPTO, keychain, RETROSSL, RETROSMB, RETRONFS) and is compiled
# by the NDK's clang for aarch64. The same compiler family and target
# here, with the aarch64 cross headers standing in for the NDK's, over
# every translation unit of the stack: the AES/PMULL path takes the
# +crypto target attribute this way, which gcc lanes never exercise.
ANDROID_CLANG=${ANDROID_CLANG:-clang}
ANDROID_SYSINC=${ANDROID_SYSINC:-/usr/aarch64-linux-gnu/include}
android_clang() {
name="$1"; defs="$2"; tu="$3"
if ! command -v "$ANDROID_CLANG" > /dev/null 2>&1 || [ ! -d "$ANDROID_SYSINC" ]; then
echo "skip $name (no $ANDROID_CLANG or $ANDROID_SYSINC)"
return
fi
if ! out=$($ANDROID_CLANG --target=aarch64-linux-gnu -isystem "$ANDROID_SYSINC" \
-Wall -Werror -Wno-unused-function -D__ANDROID__ -DANDROID $INC $defs \
-fsyntax-only "$tu" 2>&1); then
echo "FAIL $name"
echo " $tu"
show_out "$out"
fail=1
else
echo "ok $name"
fi
}
ANDROID_NET="-DHAVE_CRYPTO -DHAVE_KEYCHAIN -DHAVE_SSL -DHAVE_RETROSSL -DHAVE_SMBCLIENT -DHAVE_RETROSMB -DHAVE_NFSCLIENT -DHAVE_RETRONFS -DHAVE_NETWORKING -DHAVE_THREADS -DHAVE_CONFIGFILE -DRARCH_INTERNAL -D_GNU_SOURCE"
for tu in libretro-common/crypto/crypto.c libretro-common/crypto/kdf.c libretro-common/crypto/pk.c \
libretro-common/crypto/x509.c libretro-common/file/keychain.c libretro-common/net/net_socket_ssl_retro.c \
libretro-common/net/net_smb2.c libretro-common/net/net_krb5.c libretro-common/net/net_nfs3.c \
libretro-common/vfs/vfs_implementation_smb.c libretro-common/vfs/vfs_implementation_nfs.c \
libretro-common/vfs/vfs_prefetch.c network/tls_log.c; do
android_clang "android clang/aarch64: $(basename $tu)" "$ANDROID_NET" "$tu"
done
# rwebaudio is its own translation unit in the emscripten build, not
# part of griffin's, and nothing else compiles it at all.
check "emscripten: rwebaudio" "$HOSTOFF -Itools/platform_stubs/emscripten -D__EMSCRIPTEN__ -DEMSCRIPTEN -DHAVE_RWEBAUDIO $CDECL" audio/drivers/rwebaudio.c
check "emscripten: rwebcam" "$HOSTOFF -Itools/platform_stubs/emscripten -D__EMSCRIPTEN__ -DEMSCRIPTEN $CDECL" camera/drivers/rwebcam.c
# Two more that no job here compiles: the S3 cloud-sync backend, which
# only griffin includes and which nothing defines HAVE_S3 for, and the
# Lakka wifi driver, which needs HAVE_LAKKA. Both had a mixed
# declaration. The C89 build cannot see either, because neither is in
# its object list.
NETDEFS="-DHAVE_NETWORKING -DHAVE_CONFIGFILE -DHAVE_OVERLAY -DHAVE_CHEATS"
check "cloudsync: s3" "$NETDEFS -DHAVE_CLOUDSYNC -DHAVE_S3 $CDECL" network/cloud_sync/s3.c
check "lakka: connmanctl" "$NETDEFS -DHAVE_LAKKA -DHAVE_WIFI $CDECL" network/drivers_wifi/connmanctl.c
# The salamander launchers link a hand-picked subset of libretro-common
# with -DIS_SALAMANDER: rtime.c for rtime_localtime, no features_cpu.c.
# A syntax pass cannot see the link error that a symbol from an
@@ -739,7 +272,7 @@ salamander_link() {
if ! out=$($CC $WARN $INC $BASE $defs -o /tmp/salamander_link_check \
tools/platform_stubs/salamander_main.c "$@" 2>&1); then
echo "FAIL $name"
show_out "$out"
echo "$out" | sed 's/^/ /' | head -12
fail=1
else
echo "ok $name"
@@ -755,7 +288,7 @@ salamander_link "salamander link: rtime.c" \
# (jni, looper, native_activity, sensor, configuration, window) with
# the host identity shed.
check "android: runloop" "$HOSTOFF -DANDROID -Itools/platform_stubs/android" runloop.c
check "android: dispserv" "$HOSTOFF -DANDROID -Itools/platform_stubs/android $CDECL" gfx/display_servers/dispserv_android.c
check "android: dispserv" "$HOSTOFF -DANDROID -Itools/platform_stubs/android" gfx/display_servers/dispserv_android.c
# The Android arms of rthreads: thread affinity goes to the kernel
# directly there (bionic keeps cpu_set_t behind _GNU_SOURCE), and the
@@ -763,17 +296,6 @@ check "android: dispserv" "$HOSTOFF -DANDROID -Itools/platform_stubs/android $CD
check "android: rthreads (API 21)" "-DHAVE_THREADS -D__ANDROID__ -D__ANDROID_API__=21 -include tools/platform_stubs/android/bionic_pthread_stub.h -Itools/platform_stubs/android" libretro-common/rthreads/rthreads.c
check "android: rthreads (API 19)" "-DHAVE_THREADS -D__ANDROID__ -D__ANDROID_API__=19 -Itools/platform_stubs/android" libretro-common/rthreads/rthreads.c
# The D-Bus, Mutter and RealtimeKit units, and the elevation chain,
# are built on Linux and BSD only, but griffin and other build systems
# may still see the files elsewhere: as a target with no POSIX headers
# at all (MSVC, consoles), each must reduce to its gate and include
# none of them.
check "no-posix: D-Bus/Mutter/RealtimeKit units" "$HOSTOFF -DHAVE_DYLIB -Itools/platform_stubs/no_posix" \
gfx/common/dbus_runtime.c gfx/common/dbus_common.c \
gfx/common/mutter_displayconfig.c \
frontend/thread_elevation.c frontend/thread_elevation/rtkit.c \
frontend/thread_elevation/eevdf.c
check "android: opensl" "-DANDROID -DHAVE_OPENSL -Itools/platform_stubs/android -Wdeclaration-after-statement -Werror=declaration-after-statement" audio/drivers/opensl.c
check "gekko: rgui" "-DGEKKO -DHAVE_MENU -DHAVE_RGUI -Itools/platform_stubs/gekko" menu/drivers/rgui.c
@@ -827,7 +349,7 @@ check_gates() {
if ! out=$($CC $WARN $INC $NOMENU $defs -fsyntax-only "$tu" 2>&1); then
echo "FAIL $name"
echo " $tu"
show_out "$out"
echo "$out" | sed 's/^/ /' | head -12
fail=1; bad=1
fi
done
@@ -837,11 +359,6 @@ UITU="retroarch.c runloop.c gfx/video_driver.c gfx/gfx_display.c"
UIDEFS="$GLINC -DHAVE_COMMAND -DHAVE_STDIN_CMD"
check_gates "gates: menu + widgets" "$UIDEFS -DHAVE_MENU -DHAVE_GFX_WIDGETS" $UITU
check_gates "gates: menu only" "$UIDEFS -DHAVE_MENU" $UITU
# The keychain without networking: its menu entry and task must be
# declared outside the networking-only parts of tasks_internal.h.
check_gates "gates: keychain menu, no networking" \
"$UIDEFS -DHAVE_MENU -DHAVE_KEYCHAIN -DHAVE_CRYPTO -DHAVE_CONFIGFILE" \
menu/menu_setting.c tasks/task_keychain.c
check_gates "gates: widgets only" "$UIDEFS -DHAVE_GFX_WIDGETS" $UITU
check_gates "gates: neither" "$UIDEFS" $UITU
@@ -859,37 +376,6 @@ check_gates "gates: no networking" "$UIDEFS $(without -DHAVE_NETWORKING)"
check_gates "gates: no run-ahead" "$UIDEFS $(without -DHAVE_RUNAHEAD)" $FETU
check_gates "gates: no subsystems" "$UIDEFS" $FETU
# Each video API owns a GPU index setting and its default, under that
# API's own gate, and a build has any mix of APIs: a default defined
# under another API's gate builds only where both are on. Each API that
# a Linux build can have alone is checked alone.
check_gates "gates: GPU index, EGL only" "$UIDEFS -DHAVE_EGL -DHAVE_OPENGL" configuration.c
check_gates "gates: GPU index, Vulkan only" "$UIDEFS -DHAVE_VULKAN" configuration.c
# griffin is one translation unit, so a file-local name that is fine on
# its own collides there: p256's fmul against gfx's did, and only the
# MSVC job builds griffin. Both crypto shapes, on Linux, for free.
check_gates "griffin: HAVE_CRYPTO + keychain" "$UIDEFS -DRARCH_INTERNAL -DHAVE_GRIFFIN=1 -DHAVE_MENU -DHAVE_RGUI -DHAVE_CONFIGFILE -DHAVE_CRYPTO -DHAVE_KEYCHAIN -D_GNU_SOURCE" griffin/griffin.c
check_gates "griffin: HAVE_RETROSSL" "$UIDEFS -DRARCH_INTERNAL -DHAVE_GRIFFIN=1 -DHAVE_MENU -DHAVE_RGUI -DHAVE_CONFIGFILE -DHAVE_CRYPTO -DHAVE_KEYCHAIN -DHAVE_NETWORKING -DHAVE_SSL -DHAVE_RETROSSL -D_GNU_SOURCE" griffin/griffin.c
check_gates "griffin: HAVE_RETROSMB + cloud sync" "$UIDEFS -DRARCH_INTERNAL -DHAVE_GRIFFIN=1 -DHAVE_MENU -DHAVE_RGUI -DHAVE_CONFIGFILE -DHAVE_CRYPTO -DHAVE_NETWORKING -DHAVE_SMBCLIENT -DHAVE_RETROSMB -DHAVE_CLOUDSYNC -D_GNU_SOURCE" griffin/griffin.c
# The unity build the Android, Apple, webOS and MSVC targets ship: the
# whole network stack beside every codec, image reader and subsystem
# griffin can carry. A static helper in one translation unit that
# another one also defines (get32 in net_smb2.c and rvorbis.c) only
# collides here, so this lane carries as much of griffin as a host can.
GRIFFIN_FULL="-DRARCH_INTERNAL -DHAVE_GRIFFIN=1 -DHAVE_MENU -DHAVE_RGUI -DHAVE_XMB -DHAVE_OZONE -DHAVE_MATERIALUI -DHAVE_GFX_WIDGETS -DHAVE_OVERLAY -DHAVE_CONFIGFILE -DHAVE_CHEEVOS -DRC_DISABLE_LUA -DHAVE_RUNAHEAD -DHAVE_REWIND -DHAVE_PATCH -DHAVE_XDELTA -DHAVE_CHEATS -DHAVE_SCREENSHOTS -DHAVE_TRANSLATE -DHAVE_LIBRETRODB -DHAVE_DYNAMIC -DHAVE_DYLIB -DHAVE_THREADS -DHAVE_AUDIOMIXER -DHAVE_DSP_FILTER -DHAVE_VIDEO_FILTER -DHAVE_RWAV -DHAVE_RFLAC -DHAVE_RVORBIS -DHAVE_RMP3 -DHAVE_RAC3 -DHAVE_RPNG -DHAVE_RJPEG -DHAVE_RBMP -DHAVE_RTGA -DHAVE_RWEBP -DHAVE_RDDS -DHAVE_RZSTD -DHAVE_7ZIP -DHAVE_CHD -DHAVE_RCHD -DHAVE_IMAGEVIEWER -DHAVE_STB_FONT -DHAVE_NETWORKING -DHAVE_IFINFO -DHAVE_NETWORK_CMD -DHAVE_NETPLAYDISCOVERY -DHAVE_ONLINE_UPDATER -DHAVE_UPDATE_ASSETS -DHAVE_UPDATE_CORES -DHAVE_UPDATE_CORE_INFO -DHAVE_CORE_INFO_CACHE -DHAVE_CLOUDSYNC -DHAVE_S3 -DHAVE_CRYPTO -DHAVE_KEYCHAIN -DHAVE_SSL -DHAVE_RETROSSL -DHAVE_SMBCLIENT -DHAVE_RETROSMB -DHAVE_NFSCLIENT -DHAVE_RETRONFS -DHAVE_LANGEXTRA -DHAVE_SHADERPIPELINE -DHAVE_GLSL -DHAVE_OPENGL -D_GNU_SOURCE"
check_gates "griffin: full unity (every codec + network stack)" "$UIDEFS $GRIFFIN_FULL" griffin/griffin.c
# The SMB show-settings row read a HAVE_MENU-only default under
# HAVE_SMBCLIENT alone; with the built-in client on by default that
# broke every --disable-menu build. configuration.c with SMB and no
# menu, and the same with no menu at all.
check_gates "gates: SMB client, no menu" "$UIDEFS -DHAVE_CONFIGFILE -DHAVE_NETWORKING -DHAVE_CRYPTO -DHAVE_SMBCLIENT -DHAVE_RETROSMB -D_GNU_SOURCE" configuration.c
check_gates "gates: NFS client, no menu" "$UIDEFS -DHAVE_CONFIGFILE -DHAVE_NETWORKING -DHAVE_NFSCLIENT -DHAVE_RETRONFS -D_GNU_SOURCE" configuration.c
check_gates "gates: NFS client + menu" "$UIDEFS -DHAVE_CONFIGFILE -DHAVE_NETWORKING -DHAVE_NFSCLIENT -DHAVE_RETRONFS -DHAVE_MENU -DHAVE_RGUI -D_GNU_SOURCE" configuration.c menu/menu_setting.c menu/menu_displaylist.c menu/cbs/menu_cbs_ok.c menu/cbs/menu_cbs_deferred_push.c menu/cbs/menu_cbs_sublabel.c menu/cbs/menu_cbs_title.c
check_gates "gates: no menu, no SMB" "$UIDEFS -DHAVE_CONFIGFILE -DHAVE_NETWORKING -D_GNU_SOURCE" configuration.c
check_gates "griffin: HAVE_RETRONFS" "$UIDEFS -DRARCH_INTERNAL -DHAVE_GRIFFIN=1 -DHAVE_MENU -DHAVE_RGUI -DHAVE_CONFIGFILE -DHAVE_NETWORKING -DHAVE_NFSCLIENT -DHAVE_RETRONFS -D_GNU_SOURCE" griffin/griffin.c
check_gates "griffin: no crypto (console shape)" "$UIDEFS -DRARCH_INTERNAL -DHAVE_GRIFFIN=1 -DHAVE_MENU -DHAVE_RGUI -DHAVE_CONFIGFILE -DHAVE_KEYCHAIN -D_GNU_SOURCE" griffin/griffin.c
# A subsystem's own unit is built only when its gate is on, so each is
# checked with that gate on and the user interface off: the achievement
# and netplay widgets, and the menu entries either drives, are the edges
@@ -913,47 +399,6 @@ check "dsp filters: C89" "$C89 -DHAVE_FILTERS_BUILTIN" $DSP
check "dsp filters: C89, no C99 math declarations" \
"$(echo "$C89" | sed 's/ -D_GNU_SOURCE//') -DHAVE_FILTERS_BUILTIN" $DSP
# The UI companion's shared core, which only a Qt or Cocoa build puts
# in OBJ. A tree configured --disable-qt on a Linux host - which is what
# a desktop job builds - compiles none of it, so a change to a struct it
# reads is green everywhere until one of those jobs runs.
echo "== ui companion: the shared core =="
COMPANION="ui/companion/companion_core.c ui/companion/companion_thumbs.c \
ui/companion/companion_dock.c"
check "ui companion" "-DHAVE_RGUI -DHAVE_CONFIGFILE" $COMPANION
check "ui companion: no threads" \
"$(echo "$BASE" | sed 's/ -DHAVE_THREADS//') -DHAVE_RGUI -DHAVE_CONFIGFILE" \
$COMPANION
# Its own tests, which link the same objects and read the same structs.
# They live outside ui/companion and a change that stops at the three
# files above leaves them behind.
check "ui companion: tests" "-DHAVE_RGUI -DHAVE_CONFIGFILE -Iui/companion" \
ui/companion/test/companion_core_test.c \
ui/companion/test/companion_thumbs_test.c \
ui/companion/test/companion_core_stubs.c
# Video and shader back ends a Linux desktop job does not select. Each
# reads the frame descriptor and the shader parameter block every other
# driver does, and nothing else here compiles them: this configuration
# has no Vulkan, no Cg and none of the framebuffer drivers, so a change
# to those structs is green until a job that does have them runs.
echo "== video back ends this configuration does not build =="
GFXDEFS="-DHAVE_RGUI -DHAVE_OVERLAY -DHAVE_GFX_WIDGETS"
VK_H=/usr/include/vulkan/vulkan.h
platform_video "vulkan back end" "$GFXDEFS -DHAVE_VULKAN" "" \
gfx/drivers/vulkan.c "$VK_H"
platform_video "vulkan shader back end" "$GFXDEFS -DHAVE_VULKAN" "" \
gfx/drivers_shader/shader_vulkan.c "$VK_H"
check "cg shader back end" "$GFXDEFS -DHAVE_CG -DHAVE_OPENGL" \
gfx/drivers_shader/shader_gl_cg.c
check "framebuffer back ends" "$GFXDEFS" \
gfx/drivers/fpga_gfx.c gfx/drivers/hub75_gfx.c gfx/drivers/sunxi_gfx.c
X11_H=/usr/include/X11/Xlib.h
platform_video "x11 shm back end" "$GFXDEFS -DHAVE_X11" "" \
gfx/drivers/xshm_gfx.c "$X11_H"
platform_video "xvideo back end" "$GFXDEFS -DHAVE_X11 -DHAVE_XVIDEO" "" \
gfx/drivers/xvideo.c "$X11_H"
echo "== run-ahead: the dynamic-library gates =="
# The secondary instance exists only with HAVE_DYNAMIC; a build that
# can load libraries but links its core statically (HAVE_DYLIB alone)
@@ -964,116 +409,4 @@ check "runahead: HAVE_DYLIB only" "$RADEFS -DHAVE_DYLIB" runahead
check "runahead: HAVE_DYNAMIC" "$RADEFS -DHAVE_DYNAMIC" runahead.c
check "runahead: both" "$RADEFS -DHAVE_DYNAMIC -DHAVE_DYLIB" runahead.c
# The Metal driver, as Objective-C against stand-ins for the parts of the
# macOS SDK it uses (tools/platform_stubs/metal). No runner here has the
# SDK, so the Apple jobs used to be the first to compile it. A selector
# the stand-ins do not declare is an error: a new Metal call needs its
# declaration added there.
if command -v clang > /dev/null 2>&1; then
if ! out=$(clang -x objective-c -fobjc-runtime=macosx-10.13 -fblocks \
-fsyntax-only -Wall -Wno-unused-function -Werror=objc-method-access \
-Werror=unused-but-set-variable $HOSTOFF -D__APPLE__ -D__MACH__ \
-DHAVE_METAL -Itools/platform_stubs/metal $INC $BASE \
gfx/drivers/metal.m 2>&1); then
echo "FAIL metal video (stubs)"
echo " gfx/drivers/metal.m"
show_out "$out"
fail=1
else
echo "ok metal video (stubs)"
fi
else
echo "skip metal video (stubs) (no clang)"
fi
# gfx/font_layout.h expands inside each driver's render function, and a
# hook that reads a value before declaring its own locals is C99 the C89
# lane rejects, so these lanes reject it too (the PS2 and Wii U drivers,
# built only by C99 toolchains, opt out). And a
# layout counter a driver has no use for is set but never read. GCC 15
# and clang warn about that; the runner's GCC does not, so these lanes
# ask clang, with the warning made an error.
font_layout() {
name="$1"; target="$2"; defs="$3"; tu="$4"
if ! command -v clang > /dev/null 2>&1; then
echo "skip $name (no clang)"
return
fi
if ! out=$(clang $target -Werror=unused-but-set-variable \
-Werror=declaration-after-statement \
$INC $BASE $defs -fsyntax-only "$tu" 2>&1); then
echo "FAIL $name"
echo " $tu"
show_out "$out"
fail=1
else
echo "ok $name"
fi
}
# Vulkan from the Khronos headers the tree carries (gfx/include), as the
# real builds use, so the lane runs on a runner without the SDK headers
font_layout "font layout: vulkan" "" "-DHAVE_VULKAN -Igfx/include" gfx/drivers/vulkan.c
font_layout "font layout: gl" "" "-DHAVE_OPENGL" gfx/drivers/gl2.c
font_layout "font layout: gl1" "" "-DHAVE_OPENGL -DHAVE_OPENGL1" \
gfx/drivers/gl1.c
if command -v sdl2-config > /dev/null 2>&1; then
font_layout "font layout: sdl2" "" "-DHAVE_SDL2 $(sdl2-config --cflags)" \
gfx/drivers/sdl2_gfx.c
else
echo "skip font layout: sdl2 (no sdl2-config)"
fi
if [ -f /usr/include/SDL3/SDL.h ]; then
font_layout "font layout: sdl3" "" "-DHAVE_SDL3 -I/usr/include/SDL3" \
gfx/drivers/sdl3_gfx.c
else
echo "skip font layout: sdl3 (no SDL3 headers)"
fi
# The software OSD blitters, where their headers are installed
if [ -f /usr/include/X11/extensions/Xvlib.h ]; then
font_layout "font layout: xvideo" "" "-DHAVE_X11 -DHAVE_XVIDEO" \
gfx/drivers/xvideo.c
fi
if [ -f /usr/include/SDL/SDL.h ]; then
font_layout "font layout: sdl" "" "-DHAVE_SDL -I/usr/include/SDL" \
gfx/drivers/sdl_gfx.c
fi
if [ -f /usr/include/linux/omapfb.h ]; then
font_layout "font layout: omap" "" "-DHAVE_OMAP" gfx/drivers/omap_gfx.c
fi
font_layout "font layout: switch" "" \
"-DHAVE_LIBNX -DSWITCH -D__SWITCH__ -I$STUBS/libnx" \
gfx/drivers/switch_nx_gfx.c
font_layout "font layout: gxm" "" \
"-DVITA -DRARCH_CONSOLE $HOSTOFF -Itools/platform_stubs/vita" \
gfx/drivers/gxm_gfx.c
font_layout "font layout: ps2" "" \
"-DPS2 -DRARCH_CONSOLE -DHAVE_WINDOW_OFFSET -DHAVE_RGUI $HOSTOFF -Itools/platform_stubs/ps2 \
-Wno-declaration-after-statement" \
gfx/drivers/ps2_gfx.c
font_layout "font layout: gx2" "" \
"-DWIIU $HOSTOFF -Iwiiu/include -Iwiiu -Wno-declaration-after-statement" \
gfx/drivers/gx2_gfx.c
font_layout "font layout: glcore" "" \
"-DHAVE_OPENGL -DHAVE_OPENGL_CORE -DHAVE_SLANG" gfx/drivers/gl3.c
if [ -d /usr/x86_64-w64-mingw32/include ]; then
MINGW_CLANG="--target=x86_64-w64-mingw32 -isystemgfx/include/dxsdk -isystem /usr/x86_64-w64-mingw32/include"
font_layout "font layout: gdi" "$MINGW_CLANG" \
"-DHAVE_RGUI -DHAVE_OVERLAY -DHAVE_GDI -D_WIN32_WINNT=0x0601" \
gfx/drivers/gdi_gfx.c
font_layout "font layout: d3d8" "$MINGW_CLANG" \
"-DHAVE_D3D -DHAVE_RGUI -DHAVE_OVERLAY -DHAVE_D3D8" gfx/drivers/d3d8.c
font_layout "font layout: d3d9 hlsl" "$MINGW_CLANG" \
"-DHAVE_D3D -DHAVE_RGUI -DHAVE_OVERLAY -DHAVE_D3D9" gfx/drivers/d3d9hlsl.c
font_layout "font layout: d3d9 cg" "$MINGW_CLANG" \
"-DHAVE_D3D -DHAVE_RGUI -DHAVE_OVERLAY -DHAVE_D3D9" gfx/drivers/d3d9cg.c
font_layout "font layout: d3d10" "$MINGW_CLANG" \
"-DHAVE_D3D -DHAVE_RGUI -DHAVE_OVERLAY -DHAVE_D3D10" gfx/drivers/d3d10.c
font_layout "font layout: d3d11" "$MINGW_CLANG" \
"-DHAVE_D3D -DHAVE_RGUI -DHAVE_OVERLAY -DHAVE_D3D11" gfx/drivers/d3d11.c
font_layout "font layout: d3d12" "$MINGW_CLANG" \
"-DHAVE_D3D -DHAVE_RGUI -DHAVE_OVERLAY -DHAVE_D3D12" gfx/drivers/d3d12.c
else
echo "skip font layout: gdi, d3d8/9/10/11/12 (no MinGW headers)"
fi
exit $fail
@@ -1,43 +0,0 @@
/* Stand-in for the macOS SDK umbrella header: only the CoreGraphics
* and CoreText declarations gfx/drivers_font_renderer/coretext.c uses. */
#ifndef STUB_AS_H
#define STUB_AS_H
#include <CoreFoundation/CFString.h>
typedef double CGFloat;
typedef unsigned short CGGlyph;
typedef struct { CGFloat x, y; } CGPoint;
typedef struct { CGFloat width, height; } CGSize;
typedef struct { CGPoint origin; CGSize size; } CGRect;
typedef struct { CGFloat a,b,c,d,tx,ty; } CGAffineTransform;
extern const CGAffineTransform CGAffineTransformIdentity;
typedef struct CGContext *CGContextRef;
typedef struct CGColorSpace *CGColorSpaceRef;
typedef struct CGDataProvider *CGDataProviderRef;
typedef struct CGFont *CGFontRef;
typedef const struct __CTFont *CTFontRef;
typedef const struct __CTLine *CTLineRef;
typedef uint32_t CTFontOrientation;
typedef uint32_t CGBitmapInfo;
enum { kCGImageAlphaNone = 0, kCGImageAlphaOnly = 7, kCGBitmapByteOrder16Host = 0x1000 };
extern const CFStringRef kCTFontAttributeName;
extern const CFStringRef kCTForegroundColorFromContextAttributeName;
typedef void (*CGDataProviderReleaseDataCallback)(void *info, const void *data, size_t size);
CGDataProviderRef CGDataProviderCreateWithData(void *, const void *, size_t, CGDataProviderReleaseDataCallback);
CGFontRef CGFontCreateWithDataProvider(CGDataProviderRef);
CTFontRef CTFontCreateWithGraphicsFont(CGFontRef, CGFloat, const CGAffineTransform *, void *);
bool CTFontGetGlyphsForCharacters(CTFontRef, const UniChar *, CGGlyph *, CFIndex);
CGRect CTFontGetBoundingRectsForGlyphs(CTFontRef, CTFontOrientation, const CGGlyph *, CGRect *, CFIndex);
double CTFontGetAdvancesForGlyphs(CTFontRef, CTFontOrientation, const CGGlyph *, CGSize *, CFIndex);
CGFloat CTFontGetAscent(CTFontRef);
CGFloat CTFontGetDescent(CTFontRef);
CGFloat CTFontGetLeading(CTFontRef);
CGColorSpaceRef CGColorSpaceCreateDeviceGray(void);
void CGColorSpaceRelease(CGColorSpaceRef);
CGContextRef CGBitmapContextCreate(void *, size_t, size_t, size_t, size_t, CGColorSpaceRef, uint32_t);
void CGContextRelease(CGContextRef);
void CGContextSetGrayFillColor(CGContextRef, CGFloat, CGFloat);
void CGContextSetTextMatrix(CGContextRef, CGAffineTransform);
void CGContextSetTextPosition(CGContextRef, CGFloat, CGFloat);
CTLineRef CTLineCreateWithAttributedString(CFAttributedStringRef);
void CTLineDraw(CTLineRef, CGContextRef);
#endif
@@ -1,27 +0,0 @@
/* Stand-in for the macOS/iOS SDK header: only the CoreFoundation
* declarations gfx/drivers_font_renderer/coretext.c uses, so the
* compile matrix can check that file in an Apple shape. */
#ifndef STUB_CF_H
#define STUB_CF_H
#include <stddef.h>
#include <stdbool.h>
#include <stdint.h>
typedef long CFIndex;
typedef unsigned short UniChar;
typedef const void *CFTypeRef;
typedef const struct __CFString *CFStringRef;
typedef const struct __CFDictionary *CFDictionaryRef;
typedef const struct __CFAllocator *CFAllocatorRef;
typedef const struct __CFBoolean *CFBooleanRef;
typedef const struct __CFAttributedString *CFAttributedStringRef;
typedef struct { int v; } CFDictionaryKeyCallBacks;
typedef struct { int v; } CFDictionaryValueCallBacks;
extern const CFBooleanRef kCFBooleanTrue;
extern const CFDictionaryKeyCallBacks kCFTypeDictionaryKeyCallBacks;
extern const CFDictionaryValueCallBacks kCFTypeDictionaryValueCallBacks;
void CFRelease(CFTypeRef);
CFTypeRef CFRetain(CFTypeRef);
CFStringRef CFStringCreateWithCharacters(CFAllocatorRef, const UniChar *, CFIndex);
CFDictionaryRef CFDictionaryCreate(CFAllocatorRef, const void **, const void **, CFIndex, const CFDictionaryKeyCallBacks *, const CFDictionaryValueCallBacks *);
CFAttributedStringRef CFAttributedStringCreate(CFAllocatorRef, CFStringRef, CFDictionaryRef);
#endif
@@ -1,2 +0,0 @@
/* iOS spelling of the same stand-in */
#include <ApplicationServices/ApplicationServices.h>
@@ -1,2 +0,0 @@
/* iOS spelling of the same stand-in */
#include <ApplicationServices/ApplicationServices.h>
@@ -51,8 +51,4 @@ void *framebufferBegin(Framebuffer *fb, u32 *out_stride);
void framebufferEnd(Framebuffer *fb);
void framebufferClose(Framebuffer *fb);
/* the kernel random source the crypto's random path draws on */
void randomGet(void *buf, size_t len);
#endif
@@ -1,636 +0,0 @@
/* Generated by wayland-scanner 1.22.0 */
#ifndef WEBOS_FOREIGN_CLIENT_PROTOCOL_H
#define WEBOS_FOREIGN_CLIENT_PROTOCOL_H
#include <stdint.h>
#include <stddef.h>
#include "wayland-client.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @page page_webos_foreign The webos_foreign protocol
* @section page_ifaces_webos_foreign Interfaces
* - @subpage page_iface_wl_webos_foreign - interface for handling foreign surfaces
* - @subpage page_iface_wl_webos_exported - interface for handling exported window
* - @subpage page_iface_wl_webos_imported - interface for imported content or punchthrough region
* @section page_copyright_webos_foreign Copyright
* <pre>
*
* Copyright (c) 2018-2019 LG Electronics, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* SPDX-License-Identifier: Apache-2.0
* </pre>
*/
struct wl_region;
struct wl_surface;
struct wl_webos_exported;
struct wl_webos_foreign;
struct wl_webos_imported;
#ifndef WL_WEBOS_FOREIGN_INTERFACE
#define WL_WEBOS_FOREIGN_INTERFACE
/**
* @page page_iface_wl_webos_foreign wl_webos_foreign
* @section page_iface_wl_webos_foreign_desc Description
*
* This interface defines a way to associate one or more foreign surfaces from
* different clients or a punch-through region to a surface that acts the main
* window.
*
* You can set a region on the main window where a foreign surface or a
* punch-through region can be placed. It is called "exported" region or window
* in this protocol. Then you can request the compositor to punch through that
* region or attach another surface as an "imported" content.
* @section page_iface_wl_webos_foreign_api API
* See @ref iface_wl_webos_foreign.
*/
/**
* @defgroup iface_wl_webos_foreign The wl_webos_foreign interface
*
* This interface defines a way to associate one or more foreign surfaces from
* different clients or a punch-through region to a surface that acts the main
* window.
*
* You can set a region on the main window where a foreign surface or a
* punch-through region can be placed. It is called "exported" region or window
* in this protocol. Then you can request the compositor to punch through that
* region or attach another surface as an "imported" content.
*/
extern const struct wl_interface wl_webos_foreign_interface;
#endif
#ifndef WL_WEBOS_EXPORTED_INTERFACE
#define WL_WEBOS_EXPORTED_INTERFACE
/**
* @page page_iface_wl_webos_exported wl_webos_exported
* @section page_iface_wl_webos_exported_desc Description
*
* This interface defines operations and notifications on an exported window.
* @section page_iface_wl_webos_exported_api API
* See @ref iface_wl_webos_exported.
*/
/**
* @defgroup iface_wl_webos_exported The wl_webos_exported interface
*
* This interface defines operations and notifications on an exported window.
*/
extern const struct wl_interface wl_webos_exported_interface;
#endif
#ifndef WL_WEBOS_IMPORTED_INTERFACE
#define WL_WEBOS_IMPORTED_INTERFACE
/**
* @page page_iface_wl_webos_imported wl_webos_imported
* @section page_iface_wl_webos_imported_desc Description
*
* This interface defines operations and notifications on an imported object.
* @section page_iface_wl_webos_imported_api API
* See @ref iface_wl_webos_imported.
*/
/**
* @defgroup iface_wl_webos_imported The wl_webos_imported interface
*
* This interface defines operations and notifications on an imported object.
*/
extern const struct wl_interface wl_webos_imported_interface;
#endif
#ifndef WL_WEBOS_FOREIGN_WEBOS_EXPORTED_TYPE_ENUM
#define WL_WEBOS_FOREIGN_WEBOS_EXPORTED_TYPE_ENUM
/**
* @ingroup iface_wl_webos_foreign
* type of webos_exported
*
* This indicates the type of the content that the exported window can show.
*/
enum wl_webos_foreign_webos_exported_type {
/**
* exported window for video object
*/
WL_WEBOS_FOREIGN_WEBOS_EXPORTED_TYPE_VIDEO_OBJECT = 0,
/**
* exported window for subtitle object
*/
WL_WEBOS_FOREIGN_WEBOS_EXPORTED_TYPE_SUBTITLE_OBJECT = 1,
/**
* exported window for transparent object that does not need to punch through other windows
*/
WL_WEBOS_FOREIGN_WEBOS_EXPORTED_TYPE_TRANSPARENT_OBJECT = 2,
/**
* exported window for opaque object that always needs to punch through other windows
*/
WL_WEBOS_FOREIGN_WEBOS_EXPORTED_TYPE_OPAQUE_OBJECT = 3,
};
#endif /* WL_WEBOS_FOREIGN_WEBOS_EXPORTED_TYPE_ENUM */
#define WL_WEBOS_FOREIGN_DESTROY 0
#define WL_WEBOS_FOREIGN_EXPORT_ELEMENT 1
#define WL_WEBOS_FOREIGN_IMPORT_ELEMENT 2
/**
* @ingroup iface_wl_webos_foreign
*/
#define WL_WEBOS_FOREIGN_DESTROY_SINCE_VERSION 1
/**
* @ingroup iface_wl_webos_foreign
*/
#define WL_WEBOS_FOREIGN_EXPORT_ELEMENT_SINCE_VERSION 1
/**
* @ingroup iface_wl_webos_foreign
*/
#define WL_WEBOS_FOREIGN_IMPORT_ELEMENT_SINCE_VERSION 1
/** @ingroup iface_wl_webos_foreign */
static inline void
wl_webos_foreign_set_user_data(struct wl_webos_foreign *wl_webos_foreign, void *user_data)
{
wl_proxy_set_user_data((struct wl_proxy *) wl_webos_foreign, user_data);
}
/** @ingroup iface_wl_webos_foreign */
static inline void *
wl_webos_foreign_get_user_data(struct wl_webos_foreign *wl_webos_foreign)
{
return wl_proxy_get_user_data((struct wl_proxy *) wl_webos_foreign);
}
static inline uint32_t
wl_webos_foreign_get_version(struct wl_webos_foreign *wl_webos_foreign)
{
return wl_proxy_get_version((struct wl_proxy *) wl_webos_foreign);
}
/**
* @ingroup iface_wl_webos_foreign
*
* Destroys the foreign object.
*/
static inline void
wl_webos_foreign_destroy(struct wl_webos_foreign *wl_webos_foreign)
{
wl_proxy_marshal_flags((struct wl_proxy *) wl_webos_foreign,
WL_WEBOS_FOREIGN_DESTROY, NULL, wl_proxy_get_version((struct wl_proxy *) wl_webos_foreign), WL_MARSHAL_FLAG_DESTROY);
}
/**
* @ingroup iface_wl_webos_foreign
*
* Requests to create an exported window on a given surface.
*/
static inline struct wl_webos_exported *
wl_webos_foreign_export_element(struct wl_webos_foreign *wl_webos_foreign, struct wl_surface *surface, uint32_t exported_type)
{
struct wl_proxy *id;
id = wl_proxy_marshal_flags((struct wl_proxy *) wl_webos_foreign,
WL_WEBOS_FOREIGN_EXPORT_ELEMENT, &wl_webos_exported_interface, wl_proxy_get_version((struct wl_proxy *) wl_webos_foreign), 0, NULL, surface, exported_type);
return (struct wl_webos_exported *) id;
}
/**
* @ingroup iface_wl_webos_foreign
*
* Requests an import object that can be associated with a given exported window.
*/
static inline struct wl_webos_imported *
wl_webos_foreign_import_element(struct wl_webos_foreign *wl_webos_foreign, const char *window_id, uint32_t exported_type)
{
struct wl_proxy *id;
id = wl_proxy_marshal_flags((struct wl_proxy *) wl_webos_foreign,
WL_WEBOS_FOREIGN_IMPORT_ELEMENT, &wl_webos_imported_interface, wl_proxy_get_version((struct wl_proxy *) wl_webos_foreign), 0, NULL, window_id, exported_type);
return (struct wl_webos_imported *) id;
}
/**
* @ingroup iface_wl_webos_exported
* @struct wl_webos_exported_listener
*/
struct wl_webos_exported_listener {
/**
* window is associated with this wl_webos_exported
*
* Notifies window_id of the exported window.
* @param window_id window_id of associated exported window
*/
void (*window_id_assigned)(void *data,
struct wl_webos_exported *wl_webos_exported,
const char *window_id,
uint32_t exported_type);
};
/**
* @ingroup iface_wl_webos_exported
*/
static inline int
wl_webos_exported_add_listener(struct wl_webos_exported *wl_webos_exported,
const struct wl_webos_exported_listener *listener, void *data)
{
return wl_proxy_add_listener((struct wl_proxy *) wl_webos_exported,
(void (**)(void)) listener, data);
}
#define WL_WEBOS_EXPORTED_DESTROY 0
#define WL_WEBOS_EXPORTED_SET_EXPORTED_WINDOW 1
#define WL_WEBOS_EXPORTED_SET_CROP_REGION 2
#define WL_WEBOS_EXPORTED_SET_PROPERTY 3
/**
* @ingroup iface_wl_webos_exported
*/
#define WL_WEBOS_EXPORTED_WINDOW_ID_ASSIGNED_SINCE_VERSION 1
/**
* @ingroup iface_wl_webos_exported
*/
#define WL_WEBOS_EXPORTED_DESTROY_SINCE_VERSION 1
/**
* @ingroup iface_wl_webos_exported
*/
#define WL_WEBOS_EXPORTED_SET_EXPORTED_WINDOW_SINCE_VERSION 1
/**
* @ingroup iface_wl_webos_exported
*/
#define WL_WEBOS_EXPORTED_SET_CROP_REGION_SINCE_VERSION 2
/**
* @ingroup iface_wl_webos_exported
*/
#define WL_WEBOS_EXPORTED_SET_PROPERTY_SINCE_VERSION 2
/** @ingroup iface_wl_webos_exported */
static inline void
wl_webos_exported_set_user_data(struct wl_webos_exported *wl_webos_exported, void *user_data)
{
wl_proxy_set_user_data((struct wl_proxy *) wl_webos_exported, user_data);
}
/** @ingroup iface_wl_webos_exported */
static inline void *
wl_webos_exported_get_user_data(struct wl_webos_exported *wl_webos_exported)
{
return wl_proxy_get_user_data((struct wl_proxy *) wl_webos_exported);
}
static inline uint32_t
wl_webos_exported_get_version(struct wl_webos_exported *wl_webos_exported)
{
return wl_proxy_get_version((struct wl_proxy *) wl_webos_exported);
}
/**
* @ingroup iface_wl_webos_exported
*
* Destroys the exported object.
*/
static inline void
wl_webos_exported_destroy(struct wl_webos_exported *wl_webos_exported)
{
wl_proxy_marshal_flags((struct wl_proxy *) wl_webos_exported,
WL_WEBOS_EXPORTED_DESTROY, NULL, wl_proxy_get_version((struct wl_proxy *) wl_webos_exported), WL_MARSHAL_FLAG_DESTROY);
}
/**
* @ingroup iface_wl_webos_exported
*
* Sets the exported window with source and destination regions.
*/
static inline void
wl_webos_exported_set_exported_window(struct wl_webos_exported *wl_webos_exported, struct wl_region *source_region, struct wl_region *destination_region)
{
wl_proxy_marshal_flags((struct wl_proxy *) wl_webos_exported,
WL_WEBOS_EXPORTED_SET_EXPORTED_WINDOW, NULL, wl_proxy_get_version((struct wl_proxy *) wl_webos_exported), 0, source_region, destination_region);
}
/**
* @ingroup iface_wl_webos_exported
*
* Sets a region to be cropped.
*/
static inline void
wl_webos_exported_set_crop_region(struct wl_webos_exported *wl_webos_exported, struct wl_region *original_input, struct wl_region *source_region, struct wl_region *destination_region)
{
wl_proxy_marshal_flags((struct wl_proxy *) wl_webos_exported,
WL_WEBOS_EXPORTED_SET_CROP_REGION, NULL, wl_proxy_get_version((struct wl_proxy *) wl_webos_exported), 0, original_input, source_region, destination_region);
}
/**
* @ingroup iface_wl_webos_exported
*
* Sets a property for this webos_exported. The property is given by a string pair (name, value).
*/
static inline void
wl_webos_exported_set_property(struct wl_webos_exported *wl_webos_exported, const char *name, const char *value)
{
wl_proxy_marshal_flags((struct wl_proxy *) wl_webos_exported,
WL_WEBOS_EXPORTED_SET_PROPERTY, NULL, wl_proxy_get_version((struct wl_proxy *) wl_webos_exported), 0, name, value);
}
#ifndef WL_WEBOS_IMPORTED_SURFACE_ALIGNMENT_ENUM
#define WL_WEBOS_IMPORTED_SURFACE_ALIGNMENT_ENUM
/**
* @ingroup iface_wl_webos_imported
* surface alignment
*
* The alignment of an imported surface on the associated exported window.
*/
enum wl_webos_imported_surface_alignment {
WL_WEBOS_IMPORTED_SURFACE_ALIGNMENT_STRETCH = 0,
WL_WEBOS_IMPORTED_SURFACE_ALIGNMENT_NORTH = 1,
WL_WEBOS_IMPORTED_SURFACE_ALIGNMENT_WEST = 2,
WL_WEBOS_IMPORTED_SURFACE_ALIGNMENT_SOUTH = 4,
WL_WEBOS_IMPORTED_SURFACE_ALIGNMENT_EAST = 8,
WL_WEBOS_IMPORTED_SURFACE_ALIGNMENT_CENTER = 16,
WL_WEBOS_IMPORTED_SURFACE_ALIGNMENT_CROP = 32,
WL_WEBOS_IMPORTED_SURFACE_ALIGNMENT_FIT = 64,
};
#endif /* WL_WEBOS_IMPORTED_SURFACE_ALIGNMENT_ENUM */
/**
* @ingroup iface_wl_webos_imported
* @struct wl_webos_imported_listener
*/
struct wl_webos_imported_listener {
/**
* destination region changed
*
* Notifies the change of the destination region of associated
* exported window.
* @param width destination width of exported window
* @param height destination height of exported window
*/
void (*destination_region_changed)(void *data,
struct wl_webos_imported *wl_webos_imported,
uint32_t width,
uint32_t height);
/**
* punchthrough attached
*
* Notifies that a punchthrough is attached to webos_imported.
* @since 2
*/
void (*punchthrough_attached)(void *data,
struct wl_webos_imported *wl_webos_imported,
const char *context_id);
/**
* punchthrough detached
*
* Notifies that a punchthrough is detached from webos_imported.
* @since 2
*/
void (*punchthrough_detached)(void *data,
struct wl_webos_imported *wl_webos_imported,
const char *context_id);
/**
* surface attached
*
* Notifies that a surface is attached to webos_imported.
* @since 2
*/
void (*surface_attached)(void *data,
struct wl_webos_imported *wl_webos_imported,
struct wl_surface *surface);
/**
* surface detached
*
* Notifies that a surface is detached from webos_imported.
* @since 2
*/
void (*surface_detached)(void *data,
struct wl_webos_imported *wl_webos_imported,
struct wl_surface *surface);
};
/**
* @ingroup iface_wl_webos_imported
*/
static inline int
wl_webos_imported_add_listener(struct wl_webos_imported *wl_webos_imported,
const struct wl_webos_imported_listener *listener, void *data)
{
return wl_proxy_add_listener((struct wl_proxy *) wl_webos_imported,
(void (**)(void)) listener, data);
}
#define WL_WEBOS_IMPORTED_SET_SURFACE_ALIGNMENT 0
#define WL_WEBOS_IMPORTED_DESTROY 1
#define WL_WEBOS_IMPORTED_ATTACH_PUNCHTHROUGH 2
#define WL_WEBOS_IMPORTED_SET_PUNCHTHROUGH 3
#define WL_WEBOS_IMPORTED_DETACH_PUNCHTHROUGH 4
#define WL_WEBOS_IMPORTED_ATTACH_SURFACE 5
#define WL_WEBOS_IMPORTED_DETACH_SURFACE 6
#define WL_WEBOS_IMPORTED_ATTACH_PUNCHTHROUGH_WITH_CONTEXT 7
#define WL_WEBOS_IMPORTED_SET_Z_INDEX 8
/**
* @ingroup iface_wl_webos_imported
*/
#define WL_WEBOS_IMPORTED_DESTINATION_REGION_CHANGED_SINCE_VERSION 1
/**
* @ingroup iface_wl_webos_imported
*/
#define WL_WEBOS_IMPORTED_PUNCHTHROUGH_ATTACHED_SINCE_VERSION 2
/**
* @ingroup iface_wl_webos_imported
*/
#define WL_WEBOS_IMPORTED_PUNCHTHROUGH_DETACHED_SINCE_VERSION 2
/**
* @ingroup iface_wl_webos_imported
*/
#define WL_WEBOS_IMPORTED_SURFACE_ATTACHED_SINCE_VERSION 2
/**
* @ingroup iface_wl_webos_imported
*/
#define WL_WEBOS_IMPORTED_SURFACE_DETACHED_SINCE_VERSION 2
/**
* @ingroup iface_wl_webos_imported
*/
#define WL_WEBOS_IMPORTED_SET_SURFACE_ALIGNMENT_SINCE_VERSION 1
/**
* @ingroup iface_wl_webos_imported
*/
#define WL_WEBOS_IMPORTED_DESTROY_SINCE_VERSION 1
/**
* @ingroup iface_wl_webos_imported
*/
#define WL_WEBOS_IMPORTED_ATTACH_PUNCHTHROUGH_SINCE_VERSION 1
/**
* @ingroup iface_wl_webos_imported
*/
#define WL_WEBOS_IMPORTED_SET_PUNCHTHROUGH_SINCE_VERSION 1
/**
* @ingroup iface_wl_webos_imported
*/
#define WL_WEBOS_IMPORTED_DETACH_PUNCHTHROUGH_SINCE_VERSION 1
/**
* @ingroup iface_wl_webos_imported
*/
#define WL_WEBOS_IMPORTED_ATTACH_SURFACE_SINCE_VERSION 1
/**
* @ingroup iface_wl_webos_imported
*/
#define WL_WEBOS_IMPORTED_DETACH_SURFACE_SINCE_VERSION 1
/**
* @ingroup iface_wl_webos_imported
*/
#define WL_WEBOS_IMPORTED_ATTACH_PUNCHTHROUGH_WITH_CONTEXT_SINCE_VERSION 2
/**
* @ingroup iface_wl_webos_imported
*/
#define WL_WEBOS_IMPORTED_SET_Z_INDEX_SINCE_VERSION 2
/** @ingroup iface_wl_webos_imported */
static inline void
wl_webos_imported_set_user_data(struct wl_webos_imported *wl_webos_imported, void *user_data)
{
wl_proxy_set_user_data((struct wl_proxy *) wl_webos_imported, user_data);
}
/** @ingroup iface_wl_webos_imported */
static inline void *
wl_webos_imported_get_user_data(struct wl_webos_imported *wl_webos_imported)
{
return wl_proxy_get_user_data((struct wl_proxy *) wl_webos_imported);
}
static inline uint32_t
wl_webos_imported_get_version(struct wl_webos_imported *wl_webos_imported)
{
return wl_proxy_get_version((struct wl_proxy *) wl_webos_imported);
}
/**
* @ingroup iface_wl_webos_imported
*
* Sets the alignment of the imported surface.
*/
static inline void
wl_webos_imported_set_surface_alignment(struct wl_webos_imported *wl_webos_imported, uint32_t format)
{
wl_proxy_marshal_flags((struct wl_proxy *) wl_webos_imported,
WL_WEBOS_IMPORTED_SET_SURFACE_ALIGNMENT, NULL, wl_proxy_get_version((struct wl_proxy *) wl_webos_imported), 0, format);
}
/**
* @ingroup iface_wl_webos_imported
*
* Destroys the imported object.
*/
static inline void
wl_webos_imported_destroy(struct wl_webos_imported *wl_webos_imported)
{
wl_proxy_marshal_flags((struct wl_proxy *) wl_webos_imported,
WL_WEBOS_IMPORTED_DESTROY, NULL, wl_proxy_get_version((struct wl_proxy *) wl_webos_imported), WL_MARSHAL_FLAG_DESTROY);
}
/**
* @ingroup iface_wl_webos_imported
*
* Requests a punch-through on the exporetd window associated with
* this import object.
*/
static inline void
wl_webos_imported_attach_punchthrough(struct wl_webos_imported *wl_webos_imported)
{
wl_proxy_marshal_flags((struct wl_proxy *) wl_webos_imported,
WL_WEBOS_IMPORTED_ATTACH_PUNCHTHROUGH, NULL, wl_proxy_get_version((struct wl_proxy *) wl_webos_imported), 0);
}
/**
* @ingroup iface_wl_webos_imported
*/
static inline void
wl_webos_imported_set_punchthrough(struct wl_webos_imported *wl_webos_imported, const char *context_id)
{
wl_proxy_marshal_flags((struct wl_proxy *) wl_webos_imported,
WL_WEBOS_IMPORTED_SET_PUNCHTHROUGH, NULL, wl_proxy_get_version((struct wl_proxy *) wl_webos_imported), 0, context_id);
}
/**
* @ingroup iface_wl_webos_imported
*
* Requests to detatch the punch-through from the exported window
* associated with this import object.
*/
static inline void
wl_webos_imported_detach_punchthrough(struct wl_webos_imported *wl_webos_imported)
{
wl_proxy_marshal_flags((struct wl_proxy *) wl_webos_imported,
WL_WEBOS_IMPORTED_DETACH_PUNCHTHROUGH, NULL, wl_proxy_get_version((struct wl_proxy *) wl_webos_imported), 0);
}
/**
* @ingroup iface_wl_webos_imported
*
* Requests to attach the surface as imported to the associated
* exported window.
*/
static inline void
wl_webos_imported_attach_surface(struct wl_webos_imported *wl_webos_imported, struct wl_surface *surface)
{
wl_proxy_marshal_flags((struct wl_proxy *) wl_webos_imported,
WL_WEBOS_IMPORTED_ATTACH_SURFACE, NULL, wl_proxy_get_version((struct wl_proxy *) wl_webos_imported), 0, surface);
}
/**
* @ingroup iface_wl_webos_imported
*
* Requests to detach the surface as imported from the associated
* exported window.
*/
static inline void
wl_webos_imported_detach_surface(struct wl_webos_imported *wl_webos_imported, struct wl_surface *surface)
{
wl_proxy_marshal_flags((struct wl_proxy *) wl_webos_imported,
WL_WEBOS_IMPORTED_DETACH_SURFACE, NULL, wl_proxy_get_version((struct wl_proxy *) wl_webos_imported), 0, surface);
}
/**
* @ingroup iface_wl_webos_imported
*
* Requests a punch-through on the exporetd window associated with
* this import object for a given context id.
*/
static inline void
wl_webos_imported_attach_punchthrough_with_context(struct wl_webos_imported *wl_webos_imported, const char *context_id)
{
wl_proxy_marshal_flags((struct wl_proxy *) wl_webos_imported,
WL_WEBOS_IMPORTED_ATTACH_PUNCHTHROUGH_WITH_CONTEXT, NULL, wl_proxy_get_version((struct wl_proxy *) wl_webos_imported), 0, context_id);
}
/**
* @ingroup iface_wl_webos_imported
*
* Set the z index for webos_imported. The compositor will move the surface,
* that is attached to webos_imported, to this layer. If no surface is attached to
* webos_imported, nothing will happen.
*/
static inline void
wl_webos_imported_set_z_index(struct wl_webos_imported *wl_webos_imported, int32_t z_index)
{
wl_proxy_marshal_flags((struct wl_proxy *) wl_webos_imported,
WL_WEBOS_IMPORTED_SET_Z_INDEX, NULL, wl_proxy_get_version((struct wl_proxy *) wl_webos_imported), 0, z_index);
}
#ifdef __cplusplus
}
#endif
#endif
@@ -1,530 +0,0 @@
/* Generated by wayland-scanner 1.22.0 */
#ifndef WEBOS_INPUT_MANAGER_CLIENT_PROTOCOL_H
#define WEBOS_INPUT_MANAGER_CLIENT_PROTOCOL_H
#include <stdint.h>
#include <stddef.h>
#include "wayland-client.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @page page_webos_input_manager The webos_input_manager protocol
* @section page_ifaces_webos_input_manager Interfaces
* - @subpage page_iface_wl_webos_input_manager - get additional input interface
* - @subpage page_iface_wl_webos_seat - interface for additional input data
* - @subpage page_iface_wl_webos_gyroscope - interface for gyroscope device
* - @subpage page_iface_wl_webos_accelerometer - interface for accelerometer device
* @section page_copyright_webos_input_manager Copyright
* <pre>
*
* Copyright (c) 2014-2020 LG Electronics, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* SPDX-License-Identifier: Apache-2.0
* </pre>
*/
struct wl_seat;
struct wl_webos_accelerometer;
struct wl_webos_gyroscope;
struct wl_webos_input_manager;
struct wl_webos_seat;
#ifndef WL_WEBOS_INPUT_MANAGER_INTERFACE
#define WL_WEBOS_INPUT_MANAGER_INTERFACE
/**
* @page page_iface_wl_webos_input_manager wl_webos_input_manager
* @section page_iface_wl_webos_input_manager_desc Description
*
* wl_webos_seat has more information than wl_seat. User can get it using
* get_webos_seat(seat).
* @section page_iface_wl_webos_input_manager_api API
* See @ref iface_wl_webos_input_manager.
*/
/**
* @defgroup iface_wl_webos_input_manager The wl_webos_input_manager interface
*
* wl_webos_seat has more information than wl_seat. User can get it using
* get_webos_seat(seat).
*/
extern const struct wl_interface wl_webos_input_manager_interface;
#endif
#ifndef WL_WEBOS_SEAT_INTERFACE
#define WL_WEBOS_SEAT_INTERFACE
/**
* @page page_iface_wl_webos_seat wl_webos_seat
* @section page_iface_wl_webos_seat_desc Description
*
* This is a webOS extension for additional input data such as gyroscope or
* accelerometer. If the seat has additional input data, user can request it
* using get_*.
* @section page_iface_wl_webos_seat_api API
* See @ref iface_wl_webos_seat.
*/
/**
* @defgroup iface_wl_webos_seat The wl_webos_seat interface
*
* This is a webOS extension for additional input data such as gyroscope or
* accelerometer. If the seat has additional input data, user can request it
* using get_*.
*/
extern const struct wl_interface wl_webos_seat_interface;
#endif
#ifndef WL_WEBOS_GYROSCOPE_INTERFACE
#define WL_WEBOS_GYROSCOPE_INTERFACE
/**
* @page page_iface_wl_webos_gyroscope wl_webos_gyroscope
* @section page_iface_wl_webos_gyroscope_desc Description
*
* This is an interface for gyroscope device.
* @section page_iface_wl_webos_gyroscope_api API
* See @ref iface_wl_webos_gyroscope.
*/
/**
* @defgroup iface_wl_webos_gyroscope The wl_webos_gyroscope interface
*
* This is an interface for gyroscope device.
*/
extern const struct wl_interface wl_webos_gyroscope_interface;
#endif
#ifndef WL_WEBOS_ACCELEROMETER_INTERFACE
#define WL_WEBOS_ACCELEROMETER_INTERFACE
/**
* @page page_iface_wl_webos_accelerometer wl_webos_accelerometer
* @section page_iface_wl_webos_accelerometer_desc Description
*
* This is an interface for accelerometer device.
* @section page_iface_wl_webos_accelerometer_api API
* See @ref iface_wl_webos_accelerometer.
*/
/**
* @defgroup iface_wl_webos_accelerometer The wl_webos_accelerometer interface
*
* This is an interface for accelerometer device.
*/
extern const struct wl_interface wl_webos_accelerometer_interface;
#endif
/**
* @ingroup iface_wl_webos_input_manager
* @struct wl_webos_input_manager_listener
*/
struct wl_webos_input_manager_listener {
/**
* current cursor visibility
*
* If the current cursor visibility is chagned, notify it.
* @param visibility boolean vaule for visibility of current cursor
* @param webos_seat webos_seat cursor visibility changed, if this value is Null All cursors are hidden
*/
void (*cursor_visibility)(void *data,
struct wl_webos_input_manager *wl_webos_input_manager,
uint32_t visibility,
struct wl_webos_seat *webos_seat);
};
/**
* @ingroup iface_wl_webos_input_manager
*/
static inline int
wl_webos_input_manager_add_listener(struct wl_webos_input_manager *wl_webos_input_manager,
const struct wl_webos_input_manager_listener *listener, void *data)
{
return wl_proxy_add_listener((struct wl_proxy *) wl_webos_input_manager,
(void (**)(void)) listener, data);
}
#define WL_WEBOS_INPUT_MANAGER_GET_WEBOS_SEAT 0
#define WL_WEBOS_INPUT_MANAGER_SET_CURSOR_VISIBILITY 1
/**
* @ingroup iface_wl_webos_input_manager
*/
#define WL_WEBOS_INPUT_MANAGER_CURSOR_VISIBILITY_SINCE_VERSION 1
/**
* @ingroup iface_wl_webos_input_manager
*/
#define WL_WEBOS_INPUT_MANAGER_GET_WEBOS_SEAT_SINCE_VERSION 1
/**
* @ingroup iface_wl_webos_input_manager
*/
#define WL_WEBOS_INPUT_MANAGER_SET_CURSOR_VISIBILITY_SINCE_VERSION 1
/** @ingroup iface_wl_webos_input_manager */
static inline void
wl_webos_input_manager_set_user_data(struct wl_webos_input_manager *wl_webos_input_manager, void *user_data)
{
wl_proxy_set_user_data((struct wl_proxy *) wl_webos_input_manager, user_data);
}
/** @ingroup iface_wl_webos_input_manager */
static inline void *
wl_webos_input_manager_get_user_data(struct wl_webos_input_manager *wl_webos_input_manager)
{
return wl_proxy_get_user_data((struct wl_proxy *) wl_webos_input_manager);
}
static inline uint32_t
wl_webos_input_manager_get_version(struct wl_webos_input_manager *wl_webos_input_manager)
{
return wl_proxy_get_version((struct wl_proxy *) wl_webos_input_manager);
}
/** @ingroup iface_wl_webos_input_manager */
static inline void
wl_webos_input_manager_destroy(struct wl_webos_input_manager *wl_webos_input_manager)
{
wl_proxy_destroy((struct wl_proxy *) wl_webos_input_manager);
}
/**
* @ingroup iface_wl_webos_input_manager
*
* Returns a webos_seat associated with the seat.
*/
static inline struct wl_webos_seat *
wl_webos_input_manager_get_webos_seat(struct wl_webos_input_manager *wl_webos_input_manager, struct wl_seat *seat)
{
struct wl_proxy *id;
id = wl_proxy_marshal_flags((struct wl_proxy *) wl_webos_input_manager,
WL_WEBOS_INPUT_MANAGER_GET_WEBOS_SEAT, &wl_webos_seat_interface, wl_proxy_get_version((struct wl_proxy *) wl_webos_input_manager), 0, NULL, seat);
return (struct wl_webos_seat *) id;
}
/**
* @ingroup iface_wl_webos_input_manager
*
* Sets the cursor visibility.
*/
static inline void
wl_webos_input_manager_set_cursor_visibility(struct wl_webos_input_manager *wl_webos_input_manager, uint32_t visibility)
{
wl_proxy_marshal_flags((struct wl_proxy *) wl_webos_input_manager,
WL_WEBOS_INPUT_MANAGER_SET_CURSOR_VISIBILITY, NULL, wl_proxy_get_version((struct wl_proxy *) wl_webos_input_manager), 0, visibility);
}
#ifndef WL_WEBOS_SEAT_CAPABILITY_ENUM
#define WL_WEBOS_SEAT_CAPABILITY_ENUM
/**
* @ingroup iface_wl_webos_seat
* webos seat capability bitmask
*
* This is a bitmask of capabilities this webOS seat has; if a member is
* set, then it is present on the seat.
*/
enum wl_webos_seat_capability {
/**
* this seat does not provide additional input
*/
WL_WEBOS_SEAT_CAPABILITY_DEFAULT = 0,
/**
* this seat can provide gyroscope data, wl_webos_gyroscope
*/
WL_WEBOS_SEAT_CAPABILITY_GYROSCOPE = 1,
/**
* this seat can provide accelerometer data, wl_webos_accelerometer
*/
WL_WEBOS_SEAT_CAPABILITY_ACCELEROMETER = 2,
};
#endif /* WL_WEBOS_SEAT_CAPABILITY_ENUM */
/**
* @ingroup iface_wl_webos_seat
* @struct wl_webos_seat_listener
*/
struct wl_webos_seat_listener {
/**
* info about the seat
*
* This event will be sent immediately after the client as
* requested the webOS seat from wl_webos_input_manager.
*
* The designator [0, 1, ... N] indicates if this is the
* primary(=0), secondary, etc seat.
* @param id an unique identifier for the seat
* @param name a human readable name for the seat
* @param designator a value for the designation of this seat
*/
void (*info)(void *data,
struct wl_webos_seat *wl_webos_seat,
uint32_t id,
const char *name,
uint32_t designator,
uint32_t capabilities);
};
/**
* @ingroup iface_wl_webos_seat
*/
static inline int
wl_webos_seat_add_listener(struct wl_webos_seat *wl_webos_seat,
const struct wl_webos_seat_listener *listener, void *data)
{
return wl_proxy_add_listener((struct wl_proxy *) wl_webos_seat,
(void (**)(void)) listener, data);
}
#define WL_WEBOS_SEAT_GET_GYROSCOPE 0
#define WL_WEBOS_SEAT_GET_ACCELEROMETER 1
/**
* @ingroup iface_wl_webos_seat
*/
#define WL_WEBOS_SEAT_INFO_SINCE_VERSION 1
/**
* @ingroup iface_wl_webos_seat
*/
#define WL_WEBOS_SEAT_GET_GYROSCOPE_SINCE_VERSION 1
/**
* @ingroup iface_wl_webos_seat
*/
#define WL_WEBOS_SEAT_GET_ACCELEROMETER_SINCE_VERSION 1
/** @ingroup iface_wl_webos_seat */
static inline void
wl_webos_seat_set_user_data(struct wl_webos_seat *wl_webos_seat, void *user_data)
{
wl_proxy_set_user_data((struct wl_proxy *) wl_webos_seat, user_data);
}
/** @ingroup iface_wl_webos_seat */
static inline void *
wl_webos_seat_get_user_data(struct wl_webos_seat *wl_webos_seat)
{
return wl_proxy_get_user_data((struct wl_proxy *) wl_webos_seat);
}
static inline uint32_t
wl_webos_seat_get_version(struct wl_webos_seat *wl_webos_seat)
{
return wl_proxy_get_version((struct wl_proxy *) wl_webos_seat);
}
/** @ingroup iface_wl_webos_seat */
static inline void
wl_webos_seat_destroy(struct wl_webos_seat *wl_webos_seat)
{
wl_proxy_destroy((struct wl_proxy *) wl_webos_seat);
}
/**
* @ingroup iface_wl_webos_seat
*
* If the seat has gyroscope sensor, user can get this interface.
*/
static inline struct wl_webos_gyroscope *
wl_webos_seat_get_gyroscope(struct wl_webos_seat *wl_webos_seat)
{
struct wl_proxy *id;
id = wl_proxy_marshal_flags((struct wl_proxy *) wl_webos_seat,
WL_WEBOS_SEAT_GET_GYROSCOPE, &wl_webos_gyroscope_interface, wl_proxy_get_version((struct wl_proxy *) wl_webos_seat), 0, NULL);
return (struct wl_webos_gyroscope *) id;
}
/**
* @ingroup iface_wl_webos_seat
*
* If the seat has accelerometer sensor, user can get this interface.
*/
static inline struct wl_webos_accelerometer *
wl_webos_seat_get_accelerometer(struct wl_webos_seat *wl_webos_seat)
{
struct wl_proxy *id;
id = wl_proxy_marshal_flags((struct wl_proxy *) wl_webos_seat,
WL_WEBOS_SEAT_GET_ACCELEROMETER, &wl_webos_accelerometer_interface, wl_proxy_get_version((struct wl_proxy *) wl_webos_seat), 0, NULL);
return (struct wl_webos_accelerometer *) id;
}
/**
* @ingroup iface_wl_webos_gyroscope
* @struct wl_webos_gyroscope_listener
*/
struct wl_webos_gyroscope_listener {
/**
* sensor data from the seat
*
* The gyroscope sensor data will be delivered only if the client
* explicitly requested it via "requeest_data".
* @param x data for x-axis
* @param y data for y-axis
* @param z data for z-axis
*/
void (*sensor_data)(void *data,
struct wl_webos_gyroscope *wl_webos_gyroscope,
wl_fixed_t x,
wl_fixed_t y,
wl_fixed_t z);
};
/**
* @ingroup iface_wl_webos_gyroscope
*/
static inline int
wl_webos_gyroscope_add_listener(struct wl_webos_gyroscope *wl_webos_gyroscope,
const struct wl_webos_gyroscope_listener *listener, void *data)
{
return wl_proxy_add_listener((struct wl_proxy *) wl_webos_gyroscope,
(void (**)(void)) listener, data);
}
#define WL_WEBOS_GYROSCOPE_REQUEST_DATA 0
/**
* @ingroup iface_wl_webos_gyroscope
*/
#define WL_WEBOS_GYROSCOPE_SENSOR_DATA_SINCE_VERSION 1
/**
* @ingroup iface_wl_webos_gyroscope
*/
#define WL_WEBOS_GYROSCOPE_REQUEST_DATA_SINCE_VERSION 1
/** @ingroup iface_wl_webos_gyroscope */
static inline void
wl_webos_gyroscope_set_user_data(struct wl_webos_gyroscope *wl_webos_gyroscope, void *user_data)
{
wl_proxy_set_user_data((struct wl_proxy *) wl_webos_gyroscope, user_data);
}
/** @ingroup iface_wl_webos_gyroscope */
static inline void *
wl_webos_gyroscope_get_user_data(struct wl_webos_gyroscope *wl_webos_gyroscope)
{
return wl_proxy_get_user_data((struct wl_proxy *) wl_webos_gyroscope);
}
static inline uint32_t
wl_webos_gyroscope_get_version(struct wl_webos_gyroscope *wl_webos_gyroscope)
{
return wl_proxy_get_version((struct wl_proxy *) wl_webos_gyroscope);
}
/** @ingroup iface_wl_webos_gyroscope */
static inline void
wl_webos_gyroscope_destroy(struct wl_webos_gyroscope *wl_webos_gyroscope)
{
wl_proxy_destroy((struct wl_proxy *) wl_webos_gyroscope);
}
/**
* @ingroup iface_wl_webos_gyroscope
*
* Requests to enable/disable sending data for gyroscope device.
*/
static inline void
wl_webos_gyroscope_request_data(struct wl_webos_gyroscope *wl_webos_gyroscope, uint32_t enabled)
{
wl_proxy_marshal_flags((struct wl_proxy *) wl_webos_gyroscope,
WL_WEBOS_GYROSCOPE_REQUEST_DATA, NULL, wl_proxy_get_version((struct wl_proxy *) wl_webos_gyroscope), 0, enabled);
}
/**
* @ingroup iface_wl_webos_accelerometer
* @struct wl_webos_accelerometer_listener
*/
struct wl_webos_accelerometer_listener {
/**
* sensor data from the seat
*
* The accelerometer sensor data will be delivered only if the
* client explicitly requested it via "request_data".
* @param x data for x-axis
* @param y data for y-axis
* @param z data for z-axis
*/
void (*sensor_data)(void *data,
struct wl_webos_accelerometer *wl_webos_accelerometer,
wl_fixed_t x,
wl_fixed_t y,
wl_fixed_t z);
};
/**
* @ingroup iface_wl_webos_accelerometer
*/
static inline int
wl_webos_accelerometer_add_listener(struct wl_webos_accelerometer *wl_webos_accelerometer,
const struct wl_webos_accelerometer_listener *listener, void *data)
{
return wl_proxy_add_listener((struct wl_proxy *) wl_webos_accelerometer,
(void (**)(void)) listener, data);
}
#define WL_WEBOS_ACCELEROMETER_REQUEST_DATA 0
/**
* @ingroup iface_wl_webos_accelerometer
*/
#define WL_WEBOS_ACCELEROMETER_SENSOR_DATA_SINCE_VERSION 1
/**
* @ingroup iface_wl_webos_accelerometer
*/
#define WL_WEBOS_ACCELEROMETER_REQUEST_DATA_SINCE_VERSION 1
/** @ingroup iface_wl_webos_accelerometer */
static inline void
wl_webos_accelerometer_set_user_data(struct wl_webos_accelerometer *wl_webos_accelerometer, void *user_data)
{
wl_proxy_set_user_data((struct wl_proxy *) wl_webos_accelerometer, user_data);
}
/** @ingroup iface_wl_webos_accelerometer */
static inline void *
wl_webos_accelerometer_get_user_data(struct wl_webos_accelerometer *wl_webos_accelerometer)
{
return wl_proxy_get_user_data((struct wl_proxy *) wl_webos_accelerometer);
}
static inline uint32_t
wl_webos_accelerometer_get_version(struct wl_webos_accelerometer *wl_webos_accelerometer)
{
return wl_proxy_get_version((struct wl_proxy *) wl_webos_accelerometer);
}
/** @ingroup iface_wl_webos_accelerometer */
static inline void
wl_webos_accelerometer_destroy(struct wl_webos_accelerometer *wl_webos_accelerometer)
{
wl_proxy_destroy((struct wl_proxy *) wl_webos_accelerometer);
}
/**
* @ingroup iface_wl_webos_accelerometer
*
* Requests to enable/disable sending data for accelerometer device.
*/
static inline void
wl_webos_accelerometer_request_data(struct wl_webos_accelerometer *wl_webos_accelerometer, uint32_t enabled)
{
wl_proxy_marshal_flags((struct wl_proxy *) wl_webos_accelerometer,
WL_WEBOS_ACCELEROMETER_REQUEST_DATA, NULL, wl_proxy_get_version((struct wl_proxy *) wl_webos_accelerometer), 0, enabled);
}
#ifdef __cplusplus
}
#endif
#endif
@@ -1,506 +0,0 @@
/* Generated by wayland-scanner 1.22.0 */
#ifndef WEBOS_SHELL_CLIENT_PROTOCOL_H
#define WEBOS_SHELL_CLIENT_PROTOCOL_H
#include <stdint.h>
#include <stddef.h>
#include "wayland-client.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @page page_webos_shell The webos_shell protocol
* @section page_ifaces_webos_shell Interfaces
* - @subpage page_iface_wl_webos_shell - interface for webOS shell
* - @subpage page_iface_wl_webos_shell_surface - interface to handle webos shell surfaces
* @section page_copyright_webos_shell Copyright
* <pre>
*
* Copyright (c) 2013-2020 LG Electronics, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* SPDX-License-Identifier: Apache-2.0
* </pre>
*/
struct wl_surface;
struct wl_webos_shell;
struct wl_webos_shell_surface;
#ifndef WL_WEBOS_SHELL_INTERFACE
#define WL_WEBOS_SHELL_INTERFACE
/**
* @page page_iface_wl_webos_shell wl_webos_shell
* @section page_iface_wl_webos_shell_desc Description
*
* This interface provides handlings on webOS shell surfaces.
* @section page_iface_wl_webos_shell_api API
* See @ref iface_wl_webos_shell.
*/
/**
* @defgroup iface_wl_webos_shell The wl_webos_shell interface
*
* This interface provides handlings on webOS shell surfaces.
*/
extern const struct wl_interface wl_webos_shell_interface;
#endif
#ifndef WL_WEBOS_SHELL_SURFACE_INTERFACE
#define WL_WEBOS_SHELL_SURFACE_INTERFACE
/**
* @page page_iface_wl_webos_shell_surface wl_webos_shell_surface
* @section page_iface_wl_webos_shell_surface_desc Description
*
* This allows the client to associate an normal surface with a
* shell surface. The shell surface provides webOS specific functionality
* to surfaces.
* @section page_iface_wl_webos_shell_surface_api API
* See @ref iface_wl_webos_shell_surface.
*/
/**
* @defgroup iface_wl_webos_shell_surface The wl_webos_shell_surface interface
*
* This allows the client to associate an normal surface with a
* shell surface. The shell surface provides webOS specific functionality
* to surfaces.
*/
extern const struct wl_interface wl_webos_shell_surface_interface;
#endif
#define WL_WEBOS_SHELL_GET_SYSTEM_PIP 0
#define WL_WEBOS_SHELL_GET_SHELL_SURFACE 1
/**
* @ingroup iface_wl_webos_shell
*/
#define WL_WEBOS_SHELL_GET_SYSTEM_PIP_SINCE_VERSION 1
/**
* @ingroup iface_wl_webos_shell
*/
#define WL_WEBOS_SHELL_GET_SHELL_SURFACE_SINCE_VERSION 1
/** @ingroup iface_wl_webos_shell */
static inline void
wl_webos_shell_set_user_data(struct wl_webos_shell *wl_webos_shell, void *user_data)
{
wl_proxy_set_user_data((struct wl_proxy *) wl_webos_shell, user_data);
}
/** @ingroup iface_wl_webos_shell */
static inline void *
wl_webos_shell_get_user_data(struct wl_webos_shell *wl_webos_shell)
{
return wl_proxy_get_user_data((struct wl_proxy *) wl_webos_shell);
}
static inline uint32_t
wl_webos_shell_get_version(struct wl_webos_shell *wl_webos_shell)
{
return wl_proxy_get_version((struct wl_proxy *) wl_webos_shell);
}
/** @ingroup iface_wl_webos_shell */
static inline void
wl_webos_shell_destroy(struct wl_webos_shell *wl_webos_shell)
{
wl_proxy_destroy((struct wl_proxy *) wl_webos_shell);
}
/**
* @ingroup iface_wl_webos_shell
*
* This request is deprecated. If you want to create a new request,
* you can reuse this one. FYI, in that case, you also need to modify
* get_system_pip method in luna-surfacemanager as well.
*/
static inline void
wl_webos_shell_get_system_pip(struct wl_webos_shell *wl_webos_shell)
{
wl_proxy_marshal_flags((struct wl_proxy *) wl_webos_shell,
WL_WEBOS_SHELL_GET_SYSTEM_PIP, NULL, wl_proxy_get_version((struct wl_proxy *) wl_webos_shell), 0);
}
/**
* @ingroup iface_wl_webos_shell
*
* Creates an webOS shell surface for an existing surface.
*/
static inline struct wl_webos_shell_surface *
wl_webos_shell_get_shell_surface(struct wl_webos_shell *wl_webos_shell, struct wl_surface *surface)
{
struct wl_proxy *id;
id = wl_proxy_marshal_flags((struct wl_proxy *) wl_webos_shell,
WL_WEBOS_SHELL_GET_SHELL_SURFACE, &wl_webos_shell_surface_interface, wl_proxy_get_version((struct wl_proxy *) wl_webos_shell), 0, NULL, surface);
return (struct wl_webos_shell_surface *) id;
}
#ifndef WL_WEBOS_SHELL_SURFACE_LOCATION_HINT_ENUM
#define WL_WEBOS_SHELL_SURFACE_LOCATION_HINT_ENUM
/**
* @ingroup iface_wl_webos_shell_surface
* location mask for the surface
*
* Using this mask the surface can be positioned onto
* different locations on screen. By not using absolute
* coordinates we can create greater flexibility in the
* compositor and the clients.
*/
enum wl_webos_shell_surface_location_hint {
WL_WEBOS_SHELL_SURFACE_LOCATION_HINT_NORTH = 1,
WL_WEBOS_SHELL_SURFACE_LOCATION_HINT_WEST = 2,
WL_WEBOS_SHELL_SURFACE_LOCATION_HINT_SOUTH = 4,
WL_WEBOS_SHELL_SURFACE_LOCATION_HINT_EAST = 8,
WL_WEBOS_SHELL_SURFACE_LOCATION_HINT_CENTER = 16,
};
#endif /* WL_WEBOS_SHELL_SURFACE_LOCATION_HINT_ENUM */
#ifndef WL_WEBOS_SHELL_SURFACE_STATE_ENUM
#define WL_WEBOS_SHELL_SURFACE_STATE_ENUM
/**
* @ingroup iface_wl_webos_shell_surface
* the state of the surface
*
* The state provides info to the client on how the compositor has placed
* the surface.
*
* The default state will indicate to the client that it is windowed. The
* "position_changed" event will tell the position in screen coordinates
* when the surface is in this state.
*/
enum wl_webos_shell_surface_state {
WL_WEBOS_SHELL_SURFACE_STATE_DEFAULT = 0,
WL_WEBOS_SHELL_SURFACE_STATE_MINIMIZED = 1,
WL_WEBOS_SHELL_SURFACE_STATE_MAXIMIZED = 2,
WL_WEBOS_SHELL_SURFACE_STATE_FULLSCREEN = 3,
};
#endif /* WL_WEBOS_SHELL_SURFACE_STATE_ENUM */
#ifndef WL_WEBOS_SHELL_SURFACE_WEBOS_KEY_ENUM
#define WL_WEBOS_SHELL_SURFACE_WEBOS_KEY_ENUM
/**
* @ingroup iface_wl_webos_shell_surface
* the key that the surface handles
*
* The client uses wl_webos_shell_surface.set_key_mask to notify server
* that it will handle specific keys only.
*/
enum wl_webos_shell_surface_webos_key {
WL_WEBOS_SHELL_SURFACE_WEBOS_KEY_HOME = 1,
WL_WEBOS_SHELL_SURFACE_WEBOS_KEY_BACK = 2,
WL_WEBOS_SHELL_SURFACE_WEBOS_KEY_EXIT = 4,
WL_WEBOS_SHELL_SURFACE_WEBOS_KEY_NAVIGATION_LEFT = 8,
WL_WEBOS_SHELL_SURFACE_WEBOS_KEY_NAVIGATION_RIGHT = 16,
WL_WEBOS_SHELL_SURFACE_WEBOS_KEY_NAVIGATION_UP = 32,
WL_WEBOS_SHELL_SURFACE_WEBOS_KEY_NAVIGATION_DOWN = 64,
WL_WEBOS_SHELL_SURFACE_WEBOS_KEY_NAVIGATION_OK = 128,
WL_WEBOS_SHELL_SURFACE_WEBOS_KEY_NUMERIC_KEYS = 256,
WL_WEBOS_SHELL_SURFACE_WEBOS_KEY_REMOTE_COLOR_RED = 512,
WL_WEBOS_SHELL_SURFACE_WEBOS_KEY_REMOTE_COLOR_GREEN = 1024,
WL_WEBOS_SHELL_SURFACE_WEBOS_KEY_REMOTE_COLOR_YELLOW = 2048,
WL_WEBOS_SHELL_SURFACE_WEBOS_KEY_REMOTE_COLOR_BLUE = 4096,
WL_WEBOS_SHELL_SURFACE_WEBOS_KEY_REMOTE_PROGRAMME_GROUP = 8192,
WL_WEBOS_SHELL_SURFACE_WEBOS_KEY_REMOTE_PLAYBACK_GROUP = 16384,
WL_WEBOS_SHELL_SURFACE_WEBOS_KEY_REMOTE_TELETEXT_GROUP = 32768,
WL_WEBOS_SHELL_SURFACE_WEBOS_KEY_LOCAL_LEFT = 65536,
WL_WEBOS_SHELL_SURFACE_WEBOS_KEY_LOCAL_RIGHT = 131072,
WL_WEBOS_SHELL_SURFACE_WEBOS_KEY_LOCAL_UP = 262144,
WL_WEBOS_SHELL_SURFACE_WEBOS_KEY_LOCAL_DOWN = 524288,
WL_WEBOS_SHELL_SURFACE_WEBOS_KEY_LOCAL_OK = 1048576,
WL_WEBOS_SHELL_SURFACE_WEBOS_KEY_REMOTE_MAGNIFIER_GROUP = 2097152,
WL_WEBOS_SHELL_SURFACE_WEBOS_KEY_MINIMAL_PLAYBACK_GROUP = 4194304,
WL_WEBOS_SHELL_SURFACE_WEBOS_KEY_GUIDE = 8388608,
WL_WEBOS_SHELL_SURFACE_WEBOS_KEY_TELETEXT_ACTIVE_GROUP = 16777216,
WL_WEBOS_SHELL_SURFACE_WEBOS_KEY_DATA = 33554432,
WL_WEBOS_SHELL_SURFACE_WEBOS_KEY_INFO = 67108864,
WL_WEBOS_SHELL_SURFACE_WEBOS_KEY_DEFAULT = 0xFFFFFFF8,
};
#endif /* WL_WEBOS_SHELL_SURFACE_WEBOS_KEY_ENUM */
#ifndef WL_WEBOS_SHELL_SURFACE_ADDON_STATUS_ENUM
#define WL_WEBOS_SHELL_SURFACE_ADDON_STATUS_ENUM
/**
* @ingroup iface_wl_webos_shell_surface
* the status of the add-on
*
* Status of the add-on for the given shell surface.
* null: neither server-side add-on set nor loaded
* loaded: the server-side add-on is loaded and visible
* denied: the server-side add-on is not allowed to load
* error: the server-side add-on is not loaded due to an error
*/
enum wl_webos_shell_surface_addon_status {
WL_WEBOS_SHELL_SURFACE_ADDON_STATUS_NULL = 0,
WL_WEBOS_SHELL_SURFACE_ADDON_STATUS_LOADED = 1,
WL_WEBOS_SHELL_SURFACE_ADDON_STATUS_DENIED = 2,
WL_WEBOS_SHELL_SURFACE_ADDON_STATUS_ERROR = 3,
};
#endif /* WL_WEBOS_SHELL_SURFACE_ADDON_STATUS_ENUM */
/**
* @ingroup iface_wl_webos_shell_surface
* @struct wl_webos_shell_surface_listener
*/
struct wl_webos_shell_surface_listener {
/**
* the surface state was changed
*
* The compositor or the user has taken action that has resulted
* in this surface to change state.
*/
void (*state_changed)(void *data,
struct wl_webos_shell_surface *wl_webos_shell_surface,
uint32_t state);
/**
* the surface position on screen changed
*
* This event will only be sent the surfaces that are in their
* default state. It will be not sent for fullscreen surfaces nor
* for surfaces that are minimized.
*/
void (*position_changed)(void *data,
struct wl_webos_shell_surface *wl_webos_shell_surface,
int32_t x,
int32_t y);
/**
* request closing of window
*
* The close event is sent when the compositor requests closing
* of a window, e.g. through some gesture or other interaction,
* without explicitly quitting or killing the client.
*/
void (*close)(void *data,
struct wl_webos_shell_surface *wl_webos_shell_surface);
/**
* indicates what areas are exposed
*
* The array contains non-overlapping rectangles that define
* areas that are visible in this surface. The array elements are a
* series of ints arranged as follows x,y,w,h,x,y,w,h,-1. The
* values will not contain negative numbers and the -1 is treated
* as a terminating marker.
*/
void (*exposed)(void *data,
struct wl_webos_shell_surface *wl_webos_shell_surface,
struct wl_array *rectangles);
/**
* the surface state is about to change
*
* Be sure that the actual state should not be changed with this.
* Client can only prepare that state will be changed.
*/
void (*state_about_to_change)(void *data,
struct wl_webos_shell_surface *wl_webos_shell_surface,
uint32_t state);
/**
* notify the add-on status when changed
*
* Notifies when the add-on status changes.
* @since 2
*/
void (*addon_status_changed)(void *data,
struct wl_webos_shell_surface *wl_webos_shell_surface,
uint32_t addon_status);
};
/**
* @ingroup iface_wl_webos_shell_surface
*/
static inline int
wl_webos_shell_surface_add_listener(struct wl_webos_shell_surface *wl_webos_shell_surface,
const struct wl_webos_shell_surface_listener *listener, void *data)
{
return wl_proxy_add_listener((struct wl_proxy *) wl_webos_shell_surface,
(void (**)(void)) listener, data);
}
#define WL_WEBOS_SHELL_SURFACE_SET_LOCATION_HINT 0
#define WL_WEBOS_SHELL_SURFACE_SET_STATE 1
#define WL_WEBOS_SHELL_SURFACE_SET_PROPERTY 2
#define WL_WEBOS_SHELL_SURFACE_SET_KEY_MASK 3
#define WL_WEBOS_SHELL_SURFACE_SET_ADDON 4
#define WL_WEBOS_SHELL_SURFACE_RESET_ADDON 5
/**
* @ingroup iface_wl_webos_shell_surface
*/
#define WL_WEBOS_SHELL_SURFACE_STATE_CHANGED_SINCE_VERSION 1
/**
* @ingroup iface_wl_webos_shell_surface
*/
#define WL_WEBOS_SHELL_SURFACE_POSITION_CHANGED_SINCE_VERSION 1
/**
* @ingroup iface_wl_webos_shell_surface
*/
#define WL_WEBOS_SHELL_SURFACE_CLOSE_SINCE_VERSION 1
/**
* @ingroup iface_wl_webos_shell_surface
*/
#define WL_WEBOS_SHELL_SURFACE_EXPOSED_SINCE_VERSION 1
/**
* @ingroup iface_wl_webos_shell_surface
*/
#define WL_WEBOS_SHELL_SURFACE_STATE_ABOUT_TO_CHANGE_SINCE_VERSION 1
/**
* @ingroup iface_wl_webos_shell_surface
*/
#define WL_WEBOS_SHELL_SURFACE_ADDON_STATUS_CHANGED_SINCE_VERSION 2
/**
* @ingroup iface_wl_webos_shell_surface
*/
#define WL_WEBOS_SHELL_SURFACE_SET_LOCATION_HINT_SINCE_VERSION 1
/**
* @ingroup iface_wl_webos_shell_surface
*/
#define WL_WEBOS_SHELL_SURFACE_SET_STATE_SINCE_VERSION 1
/**
* @ingroup iface_wl_webos_shell_surface
*/
#define WL_WEBOS_SHELL_SURFACE_SET_PROPERTY_SINCE_VERSION 1
/**
* @ingroup iface_wl_webos_shell_surface
*/
#define WL_WEBOS_SHELL_SURFACE_SET_KEY_MASK_SINCE_VERSION 1
/**
* @ingroup iface_wl_webos_shell_surface
*/
#define WL_WEBOS_SHELL_SURFACE_SET_ADDON_SINCE_VERSION 2
/**
* @ingroup iface_wl_webos_shell_surface
*/
#define WL_WEBOS_SHELL_SURFACE_RESET_ADDON_SINCE_VERSION 2
/** @ingroup iface_wl_webos_shell_surface */
static inline void
wl_webos_shell_surface_set_user_data(struct wl_webos_shell_surface *wl_webos_shell_surface, void *user_data)
{
wl_proxy_set_user_data((struct wl_proxy *) wl_webos_shell_surface, user_data);
}
/** @ingroup iface_wl_webos_shell_surface */
static inline void *
wl_webos_shell_surface_get_user_data(struct wl_webos_shell_surface *wl_webos_shell_surface)
{
return wl_proxy_get_user_data((struct wl_proxy *) wl_webos_shell_surface);
}
static inline uint32_t
wl_webos_shell_surface_get_version(struct wl_webos_shell_surface *wl_webos_shell_surface)
{
return wl_proxy_get_version((struct wl_proxy *) wl_webos_shell_surface);
}
/** @ingroup iface_wl_webos_shell_surface */
static inline void
wl_webos_shell_surface_destroy(struct wl_webos_shell_surface *wl_webos_shell_surface)
{
wl_proxy_destroy((struct wl_proxy *) wl_webos_shell_surface);
}
/**
* @ingroup iface_wl_webos_shell_surface
*
* The location hint is a guideline to the compositor where
* to place the surface. The compositor will do its best to
* place it there, however this is not guaranteed. Currently
* The location hint is only applied to _WEBOS_WINDOW_TYPE_POPUP
* and _WEBOS_WINDOW_TYPE_SYSTEM_UI surfaces.
*/
static inline void
wl_webos_shell_surface_set_location_hint(struct wl_webos_shell_surface *wl_webos_shell_surface, uint32_t hint)
{
wl_proxy_marshal_flags((struct wl_proxy *) wl_webos_shell_surface,
WL_WEBOS_SHELL_SURFACE_SET_LOCATION_HINT, NULL, wl_proxy_get_version((struct wl_proxy *) wl_webos_shell_surface), 0, hint);
}
/**
* @ingroup iface_wl_webos_shell_surface
*
* The client requests its surface state to the compositor.
*/
static inline void
wl_webos_shell_surface_set_state(struct wl_webos_shell_surface *wl_webos_shell_surface, uint32_t state)
{
wl_proxy_marshal_flags((struct wl_proxy *) wl_webos_shell_surface,
WL_WEBOS_SHELL_SURFACE_SET_STATE, NULL, wl_proxy_get_version((struct wl_proxy *) wl_webos_shell_surface), 0, state);
}
/**
* @ingroup iface_wl_webos_shell_surface
*
* The client sets the surface property to the compositor. The property
* is given by a string pair (name, value).
*/
static inline void
wl_webos_shell_surface_set_property(struct wl_webos_shell_surface *wl_webos_shell_surface, const char *name, const char *value)
{
wl_proxy_marshal_flags((struct wl_proxy *) wl_webos_shell_surface,
WL_WEBOS_SHELL_SURFACE_SET_PROPERTY, NULL, wl_proxy_get_version((struct wl_proxy *) wl_webos_shell_surface), 0, name, value);
}
/**
* @ingroup iface_wl_webos_shell_surface
*
* A value of '1' in the mask indicates that the respective key event (both press and release)
* will be delivered to the client. Bitwise XOR operation against the default mask value and
* a supported individual values will result in that key not being delivered to the client.
* Notice that the default value omits certain keys.
*
* The compositor is free to ignore certain keys even if set to '1' depending on the overall
* system UI behaviour, most commonly this will mean the HOME key.
*
* NOTE: This protocol will eventually replace the string property based key filtering.
*/
static inline void
wl_webos_shell_surface_set_key_mask(struct wl_webos_shell_surface *wl_webos_shell_surface, uint32_t webos_key)
{
wl_proxy_marshal_flags((struct wl_proxy *) wl_webos_shell_surface,
WL_WEBOS_SHELL_SURFACE_SET_KEY_MASK, NULL, wl_proxy_get_version((struct wl_proxy *) wl_webos_shell_surface), 0, webos_key);
}
/**
* @ingroup iface_wl_webos_shell_surface
*
* Sets the path to the server-side add-on. The compositor loads the add-on
* on receiving this request if the add-on is identified as a valid one.
*/
static inline void
wl_webos_shell_surface_set_addon(struct wl_webos_shell_surface *wl_webos_shell_surface, const char *path)
{
wl_proxy_marshal_flags((struct wl_proxy *) wl_webos_shell_surface,
WL_WEBOS_SHELL_SURFACE_SET_ADDON, NULL, wl_proxy_get_version((struct wl_proxy *) wl_webos_shell_surface), 0, path);
}
/**
* @ingroup iface_wl_webos_shell_surface
*
* Requests to unload the add-on if it is loaded.
* If there is no add-on loaded this request is silently ignored.
*/
static inline void
wl_webos_shell_surface_reset_addon(struct wl_webos_shell_surface *wl_webos_shell_surface)
{
wl_proxy_marshal_flags((struct wl_proxy *) wl_webos_shell_surface,
WL_WEBOS_SHELL_SURFACE_RESET_ADDON, NULL, wl_proxy_get_version((struct wl_proxy *) wl_webos_shell_surface), 0);
}
#ifdef __cplusplus
}
#endif
#endif
@@ -1,597 +0,0 @@
/* Generated by wayland-scanner 1.22.0 */
#ifndef WEBOS_SURFACE_GROUP_CLIENT_PROTOCOL_H
#define WEBOS_SURFACE_GROUP_CLIENT_PROTOCOL_H
#include <stdint.h>
#include <stddef.h>
#include "wayland-client.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @page page_webos_surface_group The webos_surface_group protocol
* @section page_ifaces_webos_surface_group Interfaces
* - @subpage page_iface_wl_webos_surface_group_compositor - a way to group multi client surfaces together
* - @subpage page_iface_wl_webos_surface_group -
* - @subpage page_iface_wl_webos_surface_group_layer - the handle for the group owner to manipulate the layer
* @section page_copyright_webos_surface_group Copyright
* <pre>
*
* Copyright (c) 2014-2020 LG Electronics, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* SPDX-License-Identifier: Apache-2.0
* </pre>
*/
struct wl_surface;
struct wl_webos_surface_group;
struct wl_webos_surface_group_compositor;
struct wl_webos_surface_group_layer;
#ifndef WL_WEBOS_SURFACE_GROUP_COMPOSITOR_INTERFACE
#define WL_WEBOS_SURFACE_GROUP_COMPOSITOR_INTERFACE
/**
* @page page_iface_wl_webos_surface_group_compositor wl_webos_surface_group_compositor
* @section page_iface_wl_webos_surface_group_compositor_desc Description
*
* A surface group is a mechanism to group surfaces together that are
* not part of the same client, hence transiency, nor subsurfaces will
* fit the bill. However the compositor will treat surfaces belonging
* to a group very similarly as to surfaces that are transient.
*
* The group will be controlled by the process that creates it. The group
* will have a root surface and the grouped surfaces will be added as
* layers on top or below it. The creator of the group will designate a z order
* and a name for each layer. Other processes wanting to add their
* surface into the group need to know the both the name of the group and
* the name of the layer.
*
* A group can allow anonymous surfaces to be attached to them as well.
*
* Practical suggestion, the name of the group should match the appId of
* parent surface.
* @section page_iface_wl_webos_surface_group_compositor_api API
* See @ref iface_wl_webos_surface_group_compositor.
*/
/**
* @defgroup iface_wl_webos_surface_group_compositor The wl_webos_surface_group_compositor interface
*
* A surface group is a mechanism to group surfaces together that are
* not part of the same client, hence transiency, nor subsurfaces will
* fit the bill. However the compositor will treat surfaces belonging
* to a group very similarly as to surfaces that are transient.
*
* The group will be controlled by the process that creates it. The group
* will have a root surface and the grouped surfaces will be added as
* layers on top or below it. The creator of the group will designate a z order
* and a name for each layer. Other processes wanting to add their
* surface into the group need to know the both the name of the group and
* the name of the layer.
*
* A group can allow anonymous surfaces to be attached to them as well.
*
* Practical suggestion, the name of the group should match the appId of
* parent surface.
*/
extern const struct wl_interface wl_webos_surface_group_compositor_interface;
#endif
#ifndef WL_WEBOS_SURFACE_GROUP_INTERFACE
#define WL_WEBOS_SURFACE_GROUP_INTERFACE
/**
* @page page_iface_wl_webos_surface_group wl_webos_surface_group
* @section page_iface_wl_webos_surface_group_api API
* See @ref iface_wl_webos_surface_group.
*/
/**
* @defgroup iface_wl_webos_surface_group The wl_webos_surface_group interface
*/
extern const struct wl_interface wl_webos_surface_group_interface;
#endif
#ifndef WL_WEBOS_SURFACE_GROUP_LAYER_INTERFACE
#define WL_WEBOS_SURFACE_GROUP_LAYER_INTERFACE
/**
* @page page_iface_wl_webos_surface_group_layer wl_webos_surface_group_layer
* @section page_iface_wl_webos_surface_group_layer_desc Description
*
* The handle can be used to change the z order of a particular layer
* @section page_iface_wl_webos_surface_group_layer_api API
* See @ref iface_wl_webos_surface_group_layer.
*/
/**
* @defgroup iface_wl_webos_surface_group_layer The wl_webos_surface_group_layer interface
*
* The handle can be used to change the z order of a particular layer
*/
extern const struct wl_interface wl_webos_surface_group_layer_interface;
#endif
#define WL_WEBOS_SURFACE_GROUP_COMPOSITOR_CREATE_SURFACE_GROUP 0
#define WL_WEBOS_SURFACE_GROUP_COMPOSITOR_GET_SURFACE_GROUP 1
/**
* @ingroup iface_wl_webos_surface_group_compositor
*/
#define WL_WEBOS_SURFACE_GROUP_COMPOSITOR_CREATE_SURFACE_GROUP_SINCE_VERSION 1
/**
* @ingroup iface_wl_webos_surface_group_compositor
*/
#define WL_WEBOS_SURFACE_GROUP_COMPOSITOR_GET_SURFACE_GROUP_SINCE_VERSION 1
/** @ingroup iface_wl_webos_surface_group_compositor */
static inline void
wl_webos_surface_group_compositor_set_user_data(struct wl_webos_surface_group_compositor *wl_webos_surface_group_compositor, void *user_data)
{
wl_proxy_set_user_data((struct wl_proxy *) wl_webos_surface_group_compositor, user_data);
}
/** @ingroup iface_wl_webos_surface_group_compositor */
static inline void *
wl_webos_surface_group_compositor_get_user_data(struct wl_webos_surface_group_compositor *wl_webos_surface_group_compositor)
{
return wl_proxy_get_user_data((struct wl_proxy *) wl_webos_surface_group_compositor);
}
static inline uint32_t
wl_webos_surface_group_compositor_get_version(struct wl_webos_surface_group_compositor *wl_webos_surface_group_compositor)
{
return wl_proxy_get_version((struct wl_proxy *) wl_webos_surface_group_compositor);
}
/** @ingroup iface_wl_webos_surface_group_compositor */
static inline void
wl_webos_surface_group_compositor_destroy(struct wl_webos_surface_group_compositor *wl_webos_surface_group_compositor)
{
wl_proxy_destroy((struct wl_proxy *) wl_webos_surface_group_compositor);
}
/**
* @ingroup iface_wl_webos_surface_group_compositor
*
* The group will be identified by the 'name' parameter. If a group already
* exists with the given name an error will be reported to the client.
*/
static inline struct wl_webos_surface_group *
wl_webos_surface_group_compositor_create_surface_group(struct wl_webos_surface_group_compositor *wl_webos_surface_group_compositor, struct wl_surface *parent, const char *name)
{
struct wl_proxy *id;
id = wl_proxy_marshal_flags((struct wl_proxy *) wl_webos_surface_group_compositor,
WL_WEBOS_SURFACE_GROUP_COMPOSITOR_CREATE_SURFACE_GROUP, &wl_webos_surface_group_interface, wl_proxy_get_version((struct wl_proxy *) wl_webos_surface_group_compositor), 0, NULL, parent, name);
return (struct wl_webos_surface_group *) id;
}
/**
* @ingroup iface_wl_webos_surface_group_compositor
*
* A handle to an existing group.
*/
static inline struct wl_webos_surface_group *
wl_webos_surface_group_compositor_get_surface_group(struct wl_webos_surface_group_compositor *wl_webos_surface_group_compositor, const char *name)
{
struct wl_proxy *id;
id = wl_proxy_marshal_flags((struct wl_proxy *) wl_webos_surface_group_compositor,
WL_WEBOS_SURFACE_GROUP_COMPOSITOR_GET_SURFACE_GROUP, &wl_webos_surface_group_interface, wl_proxy_get_version((struct wl_proxy *) wl_webos_surface_group_compositor), 0, NULL, name);
return (struct wl_webos_surface_group *) id;
}
#ifndef WL_WEBOS_SURFACE_GROUP_Z_HINT_ENUM
#define WL_WEBOS_SURFACE_GROUP_Z_HINT_ENUM
/**
* @ingroup iface_wl_webos_surface_group
* hint for the z position of the anonymous surfaces
*
* The position of anonymous layer is always relative to the root element
* and the named layers in this group.
*
* If there are multiple entries with the same index designator they will
* get positioned in the order of which they get attached.
*/
enum wl_webos_surface_group_z_hint {
/**
* below the root surface and the lowest named layer
*/
WL_WEBOS_SURFACE_GROUP_Z_HINT_BELOW = 0,
/**
* above the root surface and above the highest named layer
*/
WL_WEBOS_SURFACE_GROUP_Z_HINT_ABOVE = 1,
/**
* above all of the anonymous surfaces
*/
WL_WEBOS_SURFACE_GROUP_Z_HINT_TOP = 2,
};
#endif /* WL_WEBOS_SURFACE_GROUP_Z_HINT_ENUM */
/**
* @ingroup iface_wl_webos_surface_group
* @struct wl_webos_surface_group_listener
*/
struct wl_webos_surface_group_listener {
/**
* sent to clients attached to this group
*
* If the owner crashes or normally destroys this group the
* attached client will receive this notification.
*
* Since compositor does not know what to do with client surface
* still attached to this group it will not show them. A well
* behaving client will 'detach' from this group and release its
* assosicated resource
*/
void (*owner_destroyed)(void *data,
struct wl_webos_surface_group *wl_webos_surface_group);
};
/**
* @ingroup iface_wl_webos_surface_group
*/
static inline int
wl_webos_surface_group_add_listener(struct wl_webos_surface_group *wl_webos_surface_group,
const struct wl_webos_surface_group_listener *listener, void *data)
{
return wl_proxy_add_listener((struct wl_proxy *) wl_webos_surface_group,
(void (**)(void)) listener, data);
}
#define WL_WEBOS_SURFACE_GROUP_CREATE_LAYER 0
#define WL_WEBOS_SURFACE_GROUP_ATTACH 1
#define WL_WEBOS_SURFACE_GROUP_ATTACH_ANONYMOUS 2
#define WL_WEBOS_SURFACE_GROUP_ALLOW_ANONYMOUS_LAYERS 3
#define WL_WEBOS_SURFACE_GROUP_DETACH 4
#define WL_WEBOS_SURFACE_GROUP_DESTROY 5
#define WL_WEBOS_SURFACE_GROUP_FOCUS_OWNER 6
#define WL_WEBOS_SURFACE_GROUP_FOCUS_LAYER 7
#define WL_WEBOS_SURFACE_GROUP_COMMIT_KEY_INDEX 8
/**
* @ingroup iface_wl_webos_surface_group
*/
#define WL_WEBOS_SURFACE_GROUP_OWNER_DESTROYED_SINCE_VERSION 1
/**
* @ingroup iface_wl_webos_surface_group
*/
#define WL_WEBOS_SURFACE_GROUP_CREATE_LAYER_SINCE_VERSION 1
/**
* @ingroup iface_wl_webos_surface_group
*/
#define WL_WEBOS_SURFACE_GROUP_ATTACH_SINCE_VERSION 1
/**
* @ingroup iface_wl_webos_surface_group
*/
#define WL_WEBOS_SURFACE_GROUP_ATTACH_ANONYMOUS_SINCE_VERSION 1
/**
* @ingroup iface_wl_webos_surface_group
*/
#define WL_WEBOS_SURFACE_GROUP_ALLOW_ANONYMOUS_LAYERS_SINCE_VERSION 1
/**
* @ingroup iface_wl_webos_surface_group
*/
#define WL_WEBOS_SURFACE_GROUP_DETACH_SINCE_VERSION 1
/**
* @ingroup iface_wl_webos_surface_group
*/
#define WL_WEBOS_SURFACE_GROUP_DESTROY_SINCE_VERSION 1
/**
* @ingroup iface_wl_webos_surface_group
*/
#define WL_WEBOS_SURFACE_GROUP_FOCUS_OWNER_SINCE_VERSION 1
/**
* @ingroup iface_wl_webos_surface_group
*/
#define WL_WEBOS_SURFACE_GROUP_FOCUS_LAYER_SINCE_VERSION 1
/**
* @ingroup iface_wl_webos_surface_group
*/
#define WL_WEBOS_SURFACE_GROUP_COMMIT_KEY_INDEX_SINCE_VERSION 1
/** @ingroup iface_wl_webos_surface_group */
static inline void
wl_webos_surface_group_set_user_data(struct wl_webos_surface_group *wl_webos_surface_group, void *user_data)
{
wl_proxy_set_user_data((struct wl_proxy *) wl_webos_surface_group, user_data);
}
/** @ingroup iface_wl_webos_surface_group */
static inline void *
wl_webos_surface_group_get_user_data(struct wl_webos_surface_group *wl_webos_surface_group)
{
return wl_proxy_get_user_data((struct wl_proxy *) wl_webos_surface_group);
}
static inline uint32_t
wl_webos_surface_group_get_version(struct wl_webos_surface_group *wl_webos_surface_group)
{
return wl_proxy_get_version((struct wl_proxy *) wl_webos_surface_group);
}
/**
* @ingroup iface_wl_webos_surface_group
*
* The layer will be identified by the 'name' parameter. The compositor
* will place the layer in the given z-index relative to the root layer.
* If negative z-indecies are provided they will be placed underneath the
* root surface accordingly
*
* The creator will get a handle 'wl_webos_surface_group_layer' to the
* newly created group. The handle can be used to manipulate the layer.
*
* If a layer group already exists with the given parameters an error
* will be reported to the client.
*/
static inline struct wl_webos_surface_group_layer *
wl_webos_surface_group_create_layer(struct wl_webos_surface_group *wl_webos_surface_group, const char *name, int32_t z_index)
{
struct wl_proxy *id;
id = wl_proxy_marshal_flags((struct wl_proxy *) wl_webos_surface_group,
WL_WEBOS_SURFACE_GROUP_CREATE_LAYER, &wl_webos_surface_group_layer_interface, wl_proxy_get_version((struct wl_proxy *) wl_webos_surface_group), 0, NULL, name, z_index);
return (struct wl_webos_surface_group_layer *) id;
}
/**
* @ingroup iface_wl_webos_surface_group
*
* Processes that want to attach a surface to an existing group and layer
* will call this function.
*
* An error will be reported if either the group, the layer or the layer
* already has a surface attached to it.
*/
static inline void
wl_webos_surface_group_attach(struct wl_webos_surface_group *wl_webos_surface_group, struct wl_surface *surface, const char *layer_name)
{
wl_proxy_marshal_flags((struct wl_proxy *) wl_webos_surface_group,
WL_WEBOS_SURFACE_GROUP_ATTACH, NULL, wl_proxy_get_version((struct wl_proxy *) wl_webos_surface_group), 0, surface, layer_name);
}
/**
* @ingroup iface_wl_webos_surface_group
*
* Clients that have a "multi-purpose" surface that might apply to many use
* cases can try to attach their surface to a named group. The group controls
* if that is allowed or not.
*
* An error will be reported if the group is not available or it does not
* allow anonymous surfaces
*/
static inline void
wl_webos_surface_group_attach_anonymous(struct wl_webos_surface_group *wl_webos_surface_group, struct wl_surface *surface, uint32_t z_hint)
{
wl_proxy_marshal_flags((struct wl_proxy *) wl_webos_surface_group,
WL_WEBOS_SURFACE_GROUP_ATTACH_ANONYMOUS, NULL, wl_proxy_get_version((struct wl_proxy *) wl_webos_surface_group), 0, surface, z_hint);
}
/**
* @ingroup iface_wl_webos_surface_group
*
* Anonymous layers are layers that do not belong to any named layer.
* They do not enjoy the same benefits as named layers. Their actucal
* z-index might change if there are named layers or a z index of a
* named layer is updated.
*/
static inline void
wl_webos_surface_group_allow_anonymous_layers(struct wl_webos_surface_group *wl_webos_surface_group, uint32_t allow)
{
wl_proxy_marshal_flags((struct wl_proxy *) wl_webos_surface_group,
WL_WEBOS_SURFACE_GROUP_ALLOW_ANONYMOUS_LAYERS, NULL, wl_proxy_get_version((struct wl_proxy *) wl_webos_surface_group), 0, allow);
}
/**
* @ingroup iface_wl_webos_surface_group
*
* When a client detaches from a group the compositor will treat is as any other surface.
* In practice this means that if the client does not want the surface to appear "dangling"
* or visible in recents the client should hide/destroy the surface, then detach it from
* the group to avoid any unwanted behavior.
*/
static inline void
wl_webos_surface_group_detach(struct wl_webos_surface_group *wl_webos_surface_group, struct wl_surface *surface)
{
wl_proxy_marshal_flags((struct wl_proxy *) wl_webos_surface_group,
WL_WEBOS_SURFACE_GROUP_DETACH, NULL, wl_proxy_get_version((struct wl_proxy *) wl_webos_surface_group), 0, surface);
}
/**
* @ingroup iface_wl_webos_surface_group
*
* Destroys the surface group object.
*/
static inline void
wl_webos_surface_group_destroy(struct wl_webos_surface_group *wl_webos_surface_group)
{
wl_proxy_marshal_flags((struct wl_proxy *) wl_webos_surface_group,
WL_WEBOS_SURFACE_GROUP_DESTROY, NULL, wl_proxy_get_version((struct wl_proxy *) wl_webos_surface_group), WL_MARSHAL_FLAG_DESTROY);
}
/**
* @ingroup iface_wl_webos_surface_group
*
* Using "focus_owner" a surface-group-client or a surface-group-owner can transfer
* keybord-focus to surface-group-owner.
*/
static inline void
wl_webos_surface_group_focus_owner(struct wl_webos_surface_group *wl_webos_surface_group)
{
wl_proxy_marshal_flags((struct wl_proxy *) wl_webos_surface_group,
WL_WEBOS_SURFACE_GROUP_FOCUS_OWNER, NULL, wl_proxy_get_version((struct wl_proxy *) wl_webos_surface_group), 0);
}
/**
* @ingroup iface_wl_webos_surface_group
*
* Using "focus_layer" a surface-group-owner or a surface-group-client can transfer
* keybord-focus to specific surface-group-client attached to surface-group-layer "layer".
*/
static inline void
wl_webos_surface_group_focus_layer(struct wl_webos_surface_group *wl_webos_surface_group, const char *layer)
{
wl_proxy_marshal_flags((struct wl_proxy *) wl_webos_surface_group,
WL_WEBOS_SURFACE_GROUP_FOCUS_LAYER, NULL, wl_proxy_get_version((struct wl_proxy *) wl_webos_surface_group), 0, layer);
}
/**
* @ingroup iface_wl_webos_surface_group
*
* Using "commit_key_index", the compositor will send key events in key index order
* regardless of z-order in the surface group.
* Key index set to each layer becomes effective after "commit_key_index" request.
* Parameter 'commit' should be 1 when applying the key index order.
* Set parameter 'commit' to 0 not to use the key index order.
* If anonymous layer is allowed, the key index order doesn't work.
*/
static inline void
wl_webos_surface_group_commit_key_index(struct wl_webos_surface_group *wl_webos_surface_group, uint32_t commit)
{
wl_proxy_marshal_flags((struct wl_proxy *) wl_webos_surface_group,
WL_WEBOS_SURFACE_GROUP_COMMIT_KEY_INDEX, NULL, wl_proxy_get_version((struct wl_proxy *) wl_webos_surface_group), 0, commit);
}
/**
* @ingroup iface_wl_webos_surface_group_layer
* @struct wl_webos_surface_group_layer_listener
*/
struct wl_webos_surface_group_layer_listener {
/**
* notify surface-group-owner that surface is attached to this layer
*
* When any surafce is attached to this layer, the compositor
* needs to notify surface-group-owner.
*/
void (*surface_attached)(void *data,
struct wl_webos_surface_group_layer *wl_webos_surface_group_layer);
/**
* notify surface-group-owner that surface is detached from this layer
*
* When any surafce is detached from this layer, the compositor
* needs to notify surface-group-owner. This will be usefull if
* surface attached to this layer is destroyed by associated
* surface-group-client.
*/
void (*surface_detached)(void *data,
struct wl_webos_surface_group_layer *wl_webos_surface_group_layer);
};
/**
* @ingroup iface_wl_webos_surface_group_layer
*/
static inline int
wl_webos_surface_group_layer_add_listener(struct wl_webos_surface_group_layer *wl_webos_surface_group_layer,
const struct wl_webos_surface_group_layer_listener *listener, void *data)
{
return wl_proxy_add_listener((struct wl_proxy *) wl_webos_surface_group_layer,
(void (**)(void)) listener, data);
}
#define WL_WEBOS_SURFACE_GROUP_LAYER_SET_Z_INDEX 0
#define WL_WEBOS_SURFACE_GROUP_LAYER_SET_KEY_INDEX 1
#define WL_WEBOS_SURFACE_GROUP_LAYER_DESTROY 2
/**
* @ingroup iface_wl_webos_surface_group_layer
*/
#define WL_WEBOS_SURFACE_GROUP_LAYER_SURFACE_ATTACHED_SINCE_VERSION 1
/**
* @ingroup iface_wl_webos_surface_group_layer
*/
#define WL_WEBOS_SURFACE_GROUP_LAYER_SURFACE_DETACHED_SINCE_VERSION 1
/**
* @ingroup iface_wl_webos_surface_group_layer
*/
#define WL_WEBOS_SURFACE_GROUP_LAYER_SET_Z_INDEX_SINCE_VERSION 1
/**
* @ingroup iface_wl_webos_surface_group_layer
*/
#define WL_WEBOS_SURFACE_GROUP_LAYER_SET_KEY_INDEX_SINCE_VERSION 1
/**
* @ingroup iface_wl_webos_surface_group_layer
*/
#define WL_WEBOS_SURFACE_GROUP_LAYER_DESTROY_SINCE_VERSION 1
/** @ingroup iface_wl_webos_surface_group_layer */
static inline void
wl_webos_surface_group_layer_set_user_data(struct wl_webos_surface_group_layer *wl_webos_surface_group_layer, void *user_data)
{
wl_proxy_set_user_data((struct wl_proxy *) wl_webos_surface_group_layer, user_data);
}
/** @ingroup iface_wl_webos_surface_group_layer */
static inline void *
wl_webos_surface_group_layer_get_user_data(struct wl_webos_surface_group_layer *wl_webos_surface_group_layer)
{
return wl_proxy_get_user_data((struct wl_proxy *) wl_webos_surface_group_layer);
}
static inline uint32_t
wl_webos_surface_group_layer_get_version(struct wl_webos_surface_group_layer *wl_webos_surface_group_layer)
{
return wl_proxy_get_version((struct wl_proxy *) wl_webos_surface_group_layer);
}
/**
* @ingroup iface_wl_webos_surface_group_layer
*
* Updates the z index for this layer. The compositor will move the surface
* attached to this layer accordingly. If no surface is attached to this
* layer nothing will happen
*/
static inline void
wl_webos_surface_group_layer_set_z_index(struct wl_webos_surface_group_layer *wl_webos_surface_group_layer, int32_t z_index)
{
wl_proxy_marshal_flags((struct wl_proxy *) wl_webos_surface_group_layer,
WL_WEBOS_SURFACE_GROUP_LAYER_SET_Z_INDEX, NULL, wl_proxy_get_version((struct wl_proxy *) wl_webos_surface_group_layer), 0, z_index);
}
/**
* @ingroup iface_wl_webos_surface_group_layer
*
* Sets the key index for this layer. The compositor will send key events
* to this layer accordingly after "commit_key_index" request.
* If no surface is attached to this layer nothing will happen.
* Key index should be neither redundant nor 0 (0 is onwner's key index).
* The larger the key index, the faster the layer receives the key event.
*/
static inline void
wl_webos_surface_group_layer_set_key_index(struct wl_webos_surface_group_layer *wl_webos_surface_group_layer, int32_t key_index)
{
wl_proxy_marshal_flags((struct wl_proxy *) wl_webos_surface_group_layer,
WL_WEBOS_SURFACE_GROUP_LAYER_SET_KEY_INDEX, NULL, wl_proxy_get_version((struct wl_proxy *) wl_webos_surface_group_layer), 0, key_index);
}
/**
* @ingroup iface_wl_webos_surface_group_layer
*
* Destroys the surface group layer object.
*/
static inline void
wl_webos_surface_group_layer_destroy(struct wl_webos_surface_group_layer *wl_webos_surface_group_layer)
{
wl_proxy_marshal_flags((struct wl_proxy *) wl_webos_surface_group_layer,
WL_WEBOS_SURFACE_GROUP_LAYER_DESTROY, NULL, wl_proxy_get_version((struct wl_proxy *) wl_webos_surface_group_layer), WL_MARSHAL_FLAG_DESTROY);
}
#ifdef __cplusplus
}
#endif
#endif
+1 -28
View File
@@ -86,7 +86,7 @@ apple/RetroArch_iOS.xcodeproj/project.xcworkspace/*
.cproject
.settings
libretro-super
/run.sh
run.sh
convert_rumble.awk
*~
assets
@@ -266,41 +266,21 @@ gfx/common/wayland/webos-surface-group.c
gfx/common/wayland/webos-surface-group.h
gfx/common/wayland/tearing-control-v1.h
gfx/common/wayland/tearing-control-v1.c
gfx/common/wayland/drm-lease-v1.h
gfx/common/wayland/drm-lease-v1.c
# samples/: built harness executables and objects. Each directory's
# test is <dir>_test; nothing built there is source.
samples/**/*_test
samples/**/*_test_*
samples/audio/stretch/stretch_bench
samples/**/*.o
libretro-common/samples/**/*_test
libretro-common/samples/**/*_test_*
!libretro-common/samples/**/*_test/
!samples/**/*_test_*.c
!libretro-common/samples/**/*_test_*.c
!samples/**/*_test_*.h
!libretro-common/samples/**/*_test_*.h
# Harness executables that are not named <dir>_test. This one had been
# committed as a built binary, which made any make clean in that
# directory show up as a deletion.
samples/audio/embedded_alsa/abi_check
libretro-common/samples/rthreads/retro_eventcount_bench/retro_eventcount_bench
libretro-common/samples/rthreads/retro_eventcount_bench/retro_eventcount_bench_*
# The driver's text with its Windows includes swapped, produced by each
# suite's Makefile from audio/drivers/wasapi.c. Committed copies went
# stale against the driver they are generated from.
samples/audio/wasapi_exclusive_pacing/wasapi_sim.c
samples/audio/discrete_multichannel/wasapi_sim.c
samples/gfx/font_lifecycle/lifecycle
samples/gfx/font_lifecycle/lifecycle-threaded
samples/gfx/font_lifecycle/create
# Generated by samples/gfx/wayland_drm_lease from the bundled protocol
# XML; the client half of the same pair lands in gfx/common/wayland.
samples/gfx/wayland_drm_lease/drm-lease-v1-client-protocol.h
samples/gfx/wayland_drm_lease/drm-lease-v1-server-protocol.h
samples/gfx/wayland_drm_lease/drm-lease-v1-protocol.c
# libretro-common samples
libretro-common/samples/streams/rzip/rzip
@@ -339,10 +319,3 @@ gfx/drivers/rsx_shaders/modern_opaque.vpo
pkg/psl1ght/pkg/USRDIR/shaders_cg/
!pkg/psl1ght/pkg/USRDIR/system/
samples/audio/embedded_alsa/ealsa_abi.h
# Python bytecode caches, written by tools/*.py whenever they run.
__pycache__/
*.pyc
samples/audio/asio_clock/asio_clock_driver.h
samples/gfx/integer_scale_axis/integer_scale_driver.h
samples/gfx/integer_scale_axis/integer_scale_axis_test
+2 -2
View File
@@ -7,7 +7,7 @@ stages:
- trigger-static-cores
build-retroarch-windows-x64:
image: $CI_SERVER_HOST:5050/libretro-infrastructure/libretro-build-mxe-win64-cross:gcc16
image: $CI_SERVER_HOST:5050/libretro-infrastructure/libretro-build-mxe-win64-cross:gcc10
stage: build
variables:
MEDIA_PATH: .media
@@ -51,7 +51,7 @@ build-retroarch-windows-x64:
- "rm -f ${MEDIA_PATH}/${CI_PROJECT_NAME}/redist/opengl32.dll"
build-retroarch-windows-i686:
image: $CI_SERVER_HOST:5050/libretro-infrastructure/libretro-build-mxe-win32-cross:gcc16
image: $CI_SERVER_HOST:5050/libretro-infrastructure/libretro-build-mxe-win32-cross:gcc10
stage: build
variables:
MEDIA_PATH: .media
-17
View File
@@ -90,23 +90,6 @@
- MENU/XMB: Tab change possible by gestures
- MENU/XMB: Horizontal menu is now optional
- NETWORK: New network commands SAVE_STATE_SLOT N and GET_CONFIG_PARAM
- NETWORK: HELP network command lists every command with what it does
- NETWORK: LIST_CORES, LIST_PLAYLISTS and GET_PLAYLIST network commands, to find content and the core to load it with
- NETWORK: MCP server: AI assistants can drive RetroArch through the Model Context Protocol, every network command a tool (off by default, see docs/mcp-server.md)
- NETWORK: Built-in TLS 1.2/1.3 client; mbedTLS and BearSSL are no longer bundled (--enable-mbedtls / --enable-bearssl use a system one)
- NETWORK: Built-in SMB2/3 client with signing, AES-CCM/GCM encryption and Kerberos; libsmb2 is no longer bundled (--enable-libsmb uses a system one)
- NETWORK: Built-in NFSv3 and NFSv4 (4.0, 4.1, 4.2) client for loading content from nfs:// exports
- NETWORK: NFS files stay usable across a server restart, a dropped connection or a long pause: the client reconnects and reopens what it needs
- 3DS/VITA/SWITCH/WIIU: nfs:// content through the built-in NFS client
- 3DS/VITA/SWITCH: smb:// content through the built-in SMB client
- WINDOWS/MSVC: the Visual Studio 2005 - 2017 projects build the TLS client, the keychain, SMB and NFS
- NETWORK: SMB files stay usable across a server restart: the connection is mended and the file opened again where it was
- NETWORK: NFSv4.2 reads use READ_PLUS: holes in sparse files no longer cross the network
- 3DS/SWITCH: nfs:// content through the built-in NFS client
- NETWORK: Optional SMB/NFS read-ahead with background prefetch, for large disc images over a slow link (off by default)
- NETWORK: Saved passwords are encrypted in retroarch-keychain.cfg; an optional passphrase lets the keychain move to another machine (see docs/network-stack.md)
- NETWORK: SMB 3.1.1 signs with AES-GMAC where the server offers it, several times faster than AES-CMAC on signed reads
- NETWORK: HTTPS downloads whose length is set by the server closing the connection now finish instead of hanging
- OVERLAY: Allow overlays to position the viewport
- OVERLAY: Hold button function
- OVERLAY: Allow interaction during next overlay swap
-10
View File
@@ -268,16 +268,6 @@ Debug builds report handlers that overrun a frame - see
task_queue_set_slow_handler_cb() - which is the cheapest way to find
out whether a handler you are adding respects any of this.
The waits that remain are the accepted ones, none of them reachable
while the menu is up or a core runs, each commented where it is:
* the command-line scan without a menu, which exits once done
(retroarch.c);
* the save/load state waits on exit, at startup and when an init
fails (content_wait_for_save_state_task() and
content_wait_for_load_state_task());
* the Wii U main loop's vsync wait (platform_wiiu.c).
Win32 file dialogs are modal by nature and stay so.
Commit message density
----------------------
Keep commit message short and appropriate for the size of the change. One or two
+2 -15
View File
@@ -10,12 +10,6 @@ endif
include config.mk
# The absolute include paths configure found on the system
# (/usr/local/include on the BSDs, /opt/vc/include, ...) are searched after
# the tree's own; the relative ones (gfx/include/ANGLE) are the tree's.
SYSTEM_INCLUDE_DIRS := $(filter -I/%,$(INCLUDE_DIRS))
INCLUDE_DIRS := $(filter-out -I/%,$(INCLUDE_DIRS))
# Put your favorite compile flags in this file, if you want different defaults than upstream.
# Do not attempt to create that file upstream.
# (It'd be better to put this comment in that file, but .gitignore doesn't work on files that exist in the repo.)
@@ -131,11 +125,7 @@ ifneq ($(CXX_BUILD), 1)
CFLAGS += -D_GNU_SOURCE
endif
# The tree's own headers come before any system or pkg-config path, so a
# same-named header installed on the system cannot stand in for one of
# ours.
DEF_FLAGS := -I. $(INCLUDE_DIRS) -Ideps $(filter-out -I.,$(DEF_FLAGS)) \
$(SYSTEM_INCLUDE_DIRS)
DEF_FLAGS += $(INCLUDE_DIRS) -Ideps
CFLAGS += $(DEF_FLAGS)
CXXFLAGS += $(DEF_FLAGS) -D__STDC_CONSTANT_MACROS
@@ -245,10 +235,7 @@ config.mk:
else
-include $(RARCH_OBJ:.o=.d)
ifeq ($(HAVE_CONFIG_MK),)
# The Wayland protocol sources are generated by configure, so a tree
# configured before a protocol was added has no way to make the new
# object: ask for the reconfigure rather than fail on a missing file.
config.mk: configure qb/* gfx/common/wayland/generate_wayland_protos.sh
config.mk: configure qb/*
@echo "config.mk is outdated or non-existing. Run ./configure again."
@exit 1
endif
+301 -148
View File
@@ -325,12 +325,9 @@ OBJ += \
audio/audio_pipeline_stretch.o \
audio/audio_binaural.o \
input/input_driver.o \
input/input_overlay_textures.o \
input/input_overlay_alpha.o \
input/common/input_hid_common.o \
led/led_driver.o \
gfx/video_driver.o \
gfx/common/video_mode_select.o \
gfx/gfx_display.o \
gfx/gfx_animation.o \
gfx/gfx_instrument.o \
@@ -403,7 +400,6 @@ DEFINES += -DHAVE_WINDOW_OFFSET
endif
OBJ += $(LIBRETRO_COMM_DIR)/audio/resampler/audio_resampler.o
OBJ += $(LIBRETRO_COMM_DIR)/audio/resampler/audio_resampler_int16.o
ifeq ($(HAVE_DSP_FILTER), 1)
DEFINES += -DHAVE_DSP_FILTER
@@ -432,27 +428,8 @@ OBJ += \
tasks/task_database.o \
disk_control_interface.o
# The cleanroom crypto library. Off unless a build asks for it: the
# round tables, curve constants and 4 KiB RSA working set are nothing
# for a desktop and too much for GameCube, Wii or PS2, whose Makefiles
# do not set it.
ifeq ($(HAVE_CRYPTO), 1)
DEFINES += -DHAVE_CRYPTO
OBJ += $(LIBRETRO_COMM_DIR)/crypto/crypto.o \
$(LIBRETRO_COMM_DIR)/crypto/kdf.o \
$(LIBRETRO_COMM_DIR)/crypto/pk.o \
$(LIBRETRO_COMM_DIR)/crypto/x509.o
endif
ifeq ($(HAVE_CONFIGFILE), 1)
DEFINES += -DHAVE_CONFIGFILE
ifeq ($(HAVE_CRYPTO), 1)
ifeq ($(HAVE_KEYCHAIN), 1)
DEFINES += -DHAVE_KEYCHAIN
OBJ += $(LIBRETRO_COMM_DIR)/file/keychain.o \
tasks/task_keychain.o
endif
endif
endif
ifeq ($(HAVE_BLISSBOX), 1)
@@ -495,6 +472,9 @@ ifeq ($(HAVE_CC_RESAMPLER), 1)
OBJ += audio/drivers_resampler/cc_resampler.o
OBJ += audio/drivers_resampler/cc_resampler_int16.o
ifeq ($(HAVE_NEON),1)
OBJ += audio/drivers_resampler/cc_resampler_neon.o
endif
endif
ifeq ($(HAVE_LANGEXTRA), 1)
@@ -598,48 +578,199 @@ ifeq ($(HAVE_LIBRETRODB), 1)
endif
endif
# TLS needs the sockets: a makefile that asks for a client without
# networking (a handheld with the crypto and keychain for the store
# but no network) gets neither the define nor the object.
ifneq ($(HAVE_NETWORKING), 1)
HAVE_SSL = 0
HAVE_RETROSSL = 0
HAVE_BEARSSL = 0
HAVE_MBEDTLS = 0
endif
# TLS: the built-in client by default; a system mbedTLS or BearSSL in
# its place when configure was asked for one. Nothing is bundled.
ifeq ($(HAVE_RETROSSL), 1)
HAVE_SSL = 1
DEFINES += -DHAVE_SSL -DHAVE_RETROSSL
else ifeq ($(HAVE_BEARSSL), 1)
ifeq ($(HAVE_BUILTINBEARSSL), 1)
HAVE_SSL = 1
DEFINES += -DHAVE_SSL -DHAVE_BEARSSL
LIBS += $(if $(BEARSSL_LIBS),$(BEARSSL_LIBS),-lbearssl)
DEF_FLAGS += $(BEARSSL_CFLAGS)
else ifeq ($(HAVE_MBEDTLS), 1)
# these -Is are only needed for BearSSL itself
INCLUDE_DIRS += -Ideps/bearssl-0.6/src -Ideps/bearssl-0.6/inc
OBJS_BEAR = deps/bearssl-0.6/src/aead/ccm.o \
deps/bearssl-0.6/src/codec/ccopy.o \
deps/bearssl-0.6/src/codec/dec32be.o \
deps/bearssl-0.6/src/codec/dec32le.o \
deps/bearssl-0.6/src/codec/dec64be.o \
deps/bearssl-0.6/src/codec/enc32be.o \
deps/bearssl-0.6/src/codec/enc32le.o \
deps/bearssl-0.6/src/codec/enc64be.o \
deps/bearssl-0.6/src/ec/ec_all_m31.o \
deps/bearssl-0.6/src/ec/ec_c25519_m31.o \
deps/bearssl-0.6/src/ec/ecdsa_atr.o \
deps/bearssl-0.6/src/ec/ecdsa_i31_bits.o \
deps/bearssl-0.6/src/ec/ecdsa_i31_vrfy_asn1.o \
deps/bearssl-0.6/src/ec/ecdsa_i31_vrfy_raw.o \
deps/bearssl-0.6/src/ec/ec_p256_m31.o \
deps/bearssl-0.6/src/ec/ec_prime_i31.o \
deps/bearssl-0.6/src/ec/ec_secp256r1.o \
deps/bearssl-0.6/src/ec/ec_secp384r1.o \
deps/bearssl-0.6/src/ec/ec_secp521r1.o \
deps/bearssl-0.6/src/hash/ghash_ctmul64.o \
deps/bearssl-0.6/src/hash/ghash_pclmul.o \
deps/bearssl-0.6/src/hash/md5.o \
deps/bearssl-0.6/src/hash/multihash.o \
deps/bearssl-0.6/src/hash/sha1.o \
deps/bearssl-0.6/src/hash/sha2big.o \
deps/bearssl-0.6/src/hash/sha2small.o \
deps/bearssl-0.6/src/int/i31_add.o \
deps/bearssl-0.6/src/int/i31_bitlen.o \
deps/bearssl-0.6/src/int/i31_decmod.o \
deps/bearssl-0.6/src/int/i31_decode.o \
deps/bearssl-0.6/src/int/i31_decred.o \
deps/bearssl-0.6/src/int/i31_encode.o \
deps/bearssl-0.6/src/int/i31_fmont.o \
deps/bearssl-0.6/src/int/i31_iszero.o \
deps/bearssl-0.6/src/int/i31_modpow2.o \
deps/bearssl-0.6/src/int/i31_modpow.o \
deps/bearssl-0.6/src/int/i31_montmul.o \
deps/bearssl-0.6/src/int/i31_muladd.o \
deps/bearssl-0.6/src/int/i31_ninv31.o \
deps/bearssl-0.6/src/int/i31_rshift.o \
deps/bearssl-0.6/src/int/i31_sub.o \
deps/bearssl-0.6/src/int/i31_tmont.o \
deps/bearssl-0.6/src/int/i32_div32.o \
deps/bearssl-0.6/src/int/i62_modpow2.o \
deps/bearssl-0.6/src/mac/hmac_ct.o \
deps/bearssl-0.6/src/mac/hmac.o \
deps/bearssl-0.6/src/rand/hmac_drbg.o \
deps/bearssl-0.6/src/rand/sysrng.o \
deps/bearssl-0.6/src/rsa/rsa_default_pkcs1_vrfy.o \
deps/bearssl-0.6/src/rsa/rsa_default_pub.o \
deps/bearssl-0.6/src/rsa/rsa_i31_pkcs1_vrfy.o \
deps/bearssl-0.6/src/rsa/rsa_i31_pub.o \
deps/bearssl-0.6/src/rsa/rsa_i62_pkcs1_vrfy.o \
deps/bearssl-0.6/src/rsa/rsa_i62_pub.o \
deps/bearssl-0.6/src/rsa/rsa_pkcs1_sig_unpad.o \
deps/bearssl-0.6/src/ssl/prf_md5sha1.o \
deps/bearssl-0.6/src/ssl/prf.o \
deps/bearssl-0.6/src/ssl/prf_sha256.o \
deps/bearssl-0.6/src/ssl/prf_sha384.o \
deps/bearssl-0.6/src/ssl/ssl_client_default_rsapub.o \
deps/bearssl-0.6/src/ssl/ssl_client_full.o \
deps/bearssl-0.6/src/ssl/ssl_client.o \
deps/bearssl-0.6/src/ssl/ssl_engine_default_aescbc.o \
deps/bearssl-0.6/src/ssl/ssl_engine_default_aesccm.o \
deps/bearssl-0.6/src/ssl/ssl_engine_default_aesgcm.o \
deps/bearssl-0.6/src/ssl/ssl_engine_default_chapol.o \
deps/bearssl-0.6/src/ssl/ssl_engine_default_descbc.o \
deps/bearssl-0.6/src/ssl/ssl_engine_default_ecdsa.o \
deps/bearssl-0.6/src/ssl/ssl_engine_default_rsavrfy.o \
deps/bearssl-0.6/src/ssl/ssl_engine.o \
deps/bearssl-0.6/src/ssl/ssl_hs_client.o \
deps/bearssl-0.6/src/ssl/ssl_rec_cbc.o \
deps/bearssl-0.6/src/ssl/ssl_rec_ccm.o \
deps/bearssl-0.6/src/ssl/ssl_rec_chapol.o \
deps/bearssl-0.6/src/ssl/ssl_rec_gcm.o \
deps/bearssl-0.6/src/symcipher/aes_ct64_cbcdec.o \
deps/bearssl-0.6/src/symcipher/aes_ct64_cbcenc.o \
deps/bearssl-0.6/src/symcipher/aes_ct64_ctrcbc.o \
deps/bearssl-0.6/src/symcipher/aes_ct64_ctr.o \
deps/bearssl-0.6/src/symcipher/aes_ct64_dec.o \
deps/bearssl-0.6/src/symcipher/aes_ct64_enc.o \
deps/bearssl-0.6/src/symcipher/aes_ct64.o \
deps/bearssl-0.6/src/symcipher/aes_x86ni_cbcdec.o \
deps/bearssl-0.6/src/symcipher/aes_x86ni_cbcenc.o \
deps/bearssl-0.6/src/symcipher/aes_x86ni_ctrcbc.o \
deps/bearssl-0.6/src/symcipher/aes_x86ni_ctr.o \
deps/bearssl-0.6/src/symcipher/aes_x86ni.o \
deps/bearssl-0.6/src/symcipher/chacha20_ct.o \
deps/bearssl-0.6/src/symcipher/chacha20_sse2.o \
deps/bearssl-0.6/src/symcipher/des_ct_cbcdec.o \
deps/bearssl-0.6/src/symcipher/des_ct_cbcenc.o \
deps/bearssl-0.6/src/symcipher/des_ct.o \
deps/bearssl-0.6/src/symcipher/des_support.o \
deps/bearssl-0.6/src/symcipher/poly1305_ctmul.o \
deps/bearssl-0.6/src/symcipher/poly1305_ctmulq.o \
deps/bearssl-0.6/src/x509/x509_decoder.o \
deps/bearssl-0.6/src/x509/x509_minimal_full.o \
deps/bearssl-0.6/src/x509/x509_minimal.o \
OBJ += $(OBJS_BEAR)
else ifeq ($(HAVE_BUILTINMBEDTLS), 1)
HAVE_SSL = 1
DEFINES += -DHAVE_SSL
ifeq ($(DEBUG), 1)
DEFINES += -DMBEDTLS_SSL_DEBUG_ALL
endif
LIBS += $(MBEDTLS_LIBS) $(MBEDX509_LIBS) $(MBEDCRYPTO_LIBS)
DEF_FLAGS += $(MBEDTLS_CFLAGS) $(MBEDX509_CFLAGS) $(MBEDCRYPTO_CFLAGS)
# MinGW requires this for some reason,
# even though the include paths are relative to the source
INCLUDE_DIRS += -Ideps/mbedtls
OBJS_TLS_CRYPTO = deps/mbedtls/aes.o \
deps/mbedtls/aesni.o \
deps/mbedtls/arc4.o \
deps/mbedtls/asn1parse.o \
deps/mbedtls/asn1write.o \
deps/mbedtls/base64.o \
deps/mbedtls/bignum.o \
deps/mbedtls/blowfish.o \
deps/mbedtls/camellia.o \
deps/mbedtls/ccm.o \
deps/mbedtls/cipher.o \
deps/mbedtls/cipher_wrap.o \
deps/mbedtls/ctr_drbg.o \
deps/mbedtls/des.o \
deps/mbedtls/dhm.o \
deps/mbedtls/ecdh.o \
deps/mbedtls/ecdsa.o \
deps/mbedtls/ecp.o \
deps/mbedtls/ecp_curves.o \
deps/mbedtls/entropy.o \
deps/mbedtls/entropy_poll.o \
deps/mbedtls/gcm.o \
deps/mbedtls/hmac_drbg.o \
deps/mbedtls/md.o \
deps/mbedtls/md5.o \
deps/mbedtls/md_wrap.o \
deps/mbedtls/oid.o \
deps/mbedtls/padlock.o \
deps/mbedtls/pem.o \
deps/mbedtls/pk.o \
deps/mbedtls/pk_wrap.o \
deps/mbedtls/pkcs12.o \
deps/mbedtls/pkcs5.o \
deps/mbedtls/pkparse.o \
deps/mbedtls/pkwrite.o \
deps/mbedtls/ripemd160.o \
deps/mbedtls/rsa.o \
deps/mbedtls/sha1.o \
deps/mbedtls/sha_alt.o \
deps/mbedtls/sha256.o \
deps/mbedtls/sha512.o \
deps/mbedtls/threading.o \
deps/mbedtls/timing.o \
deps/mbedtls/xtea.o
OBJS_TLS_X509 = deps/mbedtls/certs.o \
deps/mbedtls/x509.o \
deps/mbedtls/x509_create.o \
deps/mbedtls/x509_crl.o \
deps/mbedtls/x509_crt.o \
deps/mbedtls/x509_csr.o \
deps/mbedtls/x509write_crt.o \
deps/mbedtls/x509write_csr.o
OBJS_TLS = deps/mbedtls/debug.o \
deps/mbedtls/net_sockets.o \
deps/mbedtls/ssl_cache.o \
deps/mbedtls/ssl_ciphersuites.o \
deps/mbedtls/ssl_cli.o \
deps/mbedtls/ssl_cookie.o \
deps/mbedtls/ssl_srv.o \
deps/mbedtls/ssl_ticket.o \
deps/mbedtls/ssl_tls.o
OBJ += $(OBJS_TLS_CRYPTO) $(OBJS_TLS_X509) $(OBJS_TLS)
else ifeq ($(HAVE_SSL), 1)
DEFINES += -DHAVE_SSL
LIBS += $(SYSTEMMBEDTLS_LIBS) $(SYSTEMMBEDX509_LIBS) $(SYSTEMMBEDCRYPTO_LIBS)
DEF_FLAGS += $(SYSTEMMBEDTLS_CFLAGS) $(SYSTEMMBEDX509_CFLAGS) $(SYSTEMMBEDCRYPTO_CFLAGS)
endif
# Miscellaneous
ifeq ($(HAVE_COMMAND), 1)
DEFINES += -DHAVE_COMMAND
# The MCP server: desktop builds, through ./configure; no console
# Makefile sets HAVE_MCP.
ifeq ($(HAVE_NETWORKING), 1)
ifeq ($(HAVE_MCP), 1)
DEFINES += -DHAVE_MCP
OBJ += network/mcp_server.o
endif
endif
endif
ifeq ($(HAVE_STDIN_CMD), 1)
@@ -866,7 +997,6 @@ endif
OBJ += $(LIBRETRO_COMM_DIR)/audio/conversion/s16_to_float.o \
$(LIBRETRO_COMM_DIR)/audio/conversion/float_to_s16.o \
$(LIBRETRO_COMM_DIR)/audio/conversion/float_to_s32.o \
$(LIBRETRO_COMM_DIR)/audio/conversion/mono_to_stereo_float.o \
$(LIBRETRO_COMM_DIR)/audio/conversion/stereo_to_mono_float.o \
@@ -1093,19 +1223,7 @@ ifeq ($(HAVE_THREADS), 1)
$(LIBRETRO_COMM_DIR)/queues/retro_waitable_spsc.o \
gfx/video_thread_wrapper.o \
gfx/video_thread_hw.o \
audio/audio_thread_wrapper.o \
frontend/thread_elevation.o \
frontend/thread_elevation/eevdf.o
# RealtimeKit and screensaver inhibit over libdbus loaded at run
# time: no D-Bus headers or link dependency, just the library where
# the desktop has it.
ifneq ($(findstring Linux,$(OS))$(findstring BSD,$(OS)),)
OBJ += gfx/common/dbus_runtime.o \
gfx/common/dbus_common.o \
gfx/common/mutter_displayconfig.o \
frontend/thread_elevation/rtkit.o
endif
audio/audio_thread_wrapper.o
DEFINES += -DHAVE_THREADS
ifeq ($(findstring Haiku,$(OS)),)
LIBS += $(THREADS_LIBS)
@@ -1146,13 +1264,13 @@ endif
ifeq ($(TARGET), retroarch_orbis)
OBJ += input/drivers/ps4_input.o \
input/drivers_joypad/ps4_joypad.o \
audio/drivers/ps4_audio.o
audio/drivers/psp_audio.o
endif
ifeq ($(TARGET), retroarch_vita)
OBJ += input/drivers/psp_input.o \
input/drivers_joypad/psp_joypad.o \
audio/drivers/psp2_audio.o \
audio/drivers/psp_audio.o \
frontend/drivers/platform_psp.o
endif
@@ -1217,16 +1335,7 @@ ifeq ($(HAVE_WAYLAND), 1)
gfx/common/wayland/tablet-unstable-v2.o \
gfx/common/wayland/content-type-v1.o \
gfx/common/wayland/single-pixel-buffer-v1.o \
gfx/common/wayland/tearing-control-v1.o \
gfx/common/wayland/color-management-v1.o \
gfx/common/wayland_color.o \
gfx/common/wayland/kde-output-device-v2.o \
gfx/common/wayland/kde-output-management-v2.o \
gfx/common/wayland_kwin_output.o \
gfx/common/wayland/wlr-output-management-unstable-v1.o \
gfx/common/wayland_wlr_output.o \
gfx/common/wayland/drm-lease-v1.o \
gfx/common/wayland_drm_lease.o
gfx/common/wayland/tearing-control-v1.o
ifeq ($(HAVE_VULKAN), 1)
OBJ += gfx/drivers_context/wayland_vk_ctx.o
@@ -1298,7 +1407,7 @@ ifeq ($(HAVE_X11), 1)
DEFINES += -DHAVE_X11
DEF_FLAGS += $(X11_CFLAGS) $(XEXT_CFLAGS) $(XF86VM_CFLAGS) $(XINERAMA_CFLAGS)
ifeq ($(HAVE_XCB),1)
LIBS += $(X11_XCB_LIBS)
LIBS += -lX11-xcb
endif
ifeq ($(HAVE_XI2),1)
LIBS += -lXi
@@ -1311,10 +1420,7 @@ ifeq ($(HAVE_X11), 1)
OBJ += gfx/drivers_context/x_ctx.o
endif
ifeq ($(HAVE_VULKAN), 1)
# The X11 Vulkan context creates an XCB surface.
ifeq ($(HAVE_XCB), 1)
OBJ += gfx/drivers_context/x_vk_ctx.o
endif
OBJ += gfx/drivers_context/x_vk_ctx.o
endif
endif
@@ -1332,6 +1438,7 @@ endif
ifeq ($(HAVE_DBUS), 1)
LIBS += $(DBUS_LIBS)
DEF_FLAGS += $(DBUS_CFLAGS)
OBJ += gfx/common/dbus_common.o
endif
ifeq ($(HAVE_UDEV), 1)
@@ -1430,8 +1537,7 @@ OBJ += gfx/drivers_context/gfx_null_ctx.o
ifeq ($(HAVE_KMS), 1)
HAVE_AND_WILL_USE_DRM = 1
OBJ += gfx/drivers_context/drm_ctx.o \
gfx/display_servers/dispserv_kms.o \
gfx/common/drm_hdr.o
gfx/display_servers/dispserv_kms.o
ifeq ($(HAVE_ODROIDGO2), 1)
OBJ += gfx/drivers_context/drm_go2_ctx.o
@@ -1648,16 +1754,14 @@ else ifeq ($(HAVE_SDL2), 1)
HAVE_SDL_COMMON = 1
OBJ += gfx/drivers/sdl2_gfx.o \
gfx/common/sdl2_common.o \
gfx/display_servers/dispserv_sdl2.o \
audio/drivers/sdl2_audio.o
gfx/display_servers/dispserv_sdl2.o
DEF_FLAGS += $(SDL2_CFLAGS)
# See the SDL3 note above: drop SDL's WinMain shim lib, we set
# SDL_MAIN_HANDLED and provide our own entry point.
LIBS += $(filter-out -lSDL2main -lSDLmain -lSDL_main,$(SDL2_LIBS))
else ifeq ($(HAVE_SDL), 1)
HAVE_SDL_COMMON = 1
OBJ += gfx/drivers/sdl_gfx.o \
audio/drivers/sdl1_audio.o
OBJ += gfx/drivers/sdl_gfx.o
DEF_FLAGS += $(SDL_CFLAGS)
# See the SDL3 note above: drop SDL's WinMain shim lib.
LIBS += $(filter-out -lSDLmain -lSDL_main,$(SDL_LIBS))
@@ -1674,27 +1778,18 @@ else ifeq ($(HAVE_SDL_DINGUX), 1)
endif
OBJ += input/drivers/sdl_dingux_input.o \
input/drivers_joypad/sdl_dingux_joypad.o \
audio/drivers/sdl1_audio.o
input/drivers_joypad/sdl_dingux_joypad.o
DEF_FLAGS += $(SDL_DINGUX_CFLAGS)
LIBS += $(SDL_DINGUX_LIBS)
endif
ifeq ($(HAVE_SDL_COMMON), 1)
ifeq ($(HAVE_SDL2), 1)
OBJ += input/drivers/sdl2_input.o \
input/drivers_joypad/sdl2_joypad.o
else
OBJ += input/drivers/sdl1_input.o \
input/drivers_joypad/sdl1_joypad.o
endif
OBJ += input/drivers/sdl_input.o \
input/drivers_joypad/sdl_joypad.o \
audio/drivers/sdl_audio.o
ifeq ($(HAVE_GL_CONTEXT), 1)
ifeq ($(HAVE_SDL2), 1)
OBJ += gfx/drivers_context/sdl2_gl_ctx.o
else
OBJ += gfx/drivers_context/sdl1_gl_ctx.o
endif
OBJ += gfx/drivers_context/sdl_gl_ctx.o
endif
INCLUDE_DIRS += $(BSD_LOCAL_INC)
@@ -1777,10 +1872,6 @@ ifeq ($(HAVE_DISPMANX), 1)
HAVE_VIDEOCORE = 1
endif
ifeq ($(HAVE_VIDEOCORE), 1)
OBJ += gfx/display_servers/dispserv_videocore.o
endif
ifeq ($(HAVE_SUNXI), 1)
OBJ += gfx/drivers/sunxi_gfx.o \
gfx/include/pixman/pixman-arm-neon-asm.o
@@ -2436,7 +2527,6 @@ ifeq ($(HAVE_NETWORKING), 1)
$(LIBRETRO_COMM_DIR)/net/net_socket.o \
core_updater_list.o \
network/natt.o \
network/natt_desc.o \
tasks/task_netplay_lan_scan.o \
tasks/task_netplay_nat_traversal.o \
tasks/task_pl_thumbnail_download.o \
@@ -2463,25 +2553,20 @@ ifeq ($(HAVE_NETWORKING), 1)
ifeq ($(HAVE_CLOUDSYNC), 1)
DEFINES += -DHAVE_CLOUDSYNC
OBJ += tasks/task_cloudsync.o \
tasks/task_cloudsync_path.o \
network/cloud_sync/webdav.o \
network/cloud_sync_driver.o
ifeq ($(HAVE_SSL), 1)
OBJ += network/cloud_sync/google_drive.o
endif
ifneq (,$(filter 1,$(HAVE_SMBCLIENT) $(HAVE_RETROSMB)))
ifneq (,$(filter 1,$(HAVE_SMBCLIENT) $(HAVE_BUILTINSMBCLIENT)))
OBJ += network/cloud_sync/smb.o
endif
endif
ifeq ($(HAVE_RETROSSL), 1)
OBJ += $(LIBRETRO_COMM_DIR)/net/net_socket_ssl_retro.o \
network/tls_log.o
else ifeq ($(HAVE_BEARSSL), 1)
ifeq ($(HAVE_BUILTINBEARSSL), 1)
OBJ += $(LIBRETRO_COMM_DIR)/net/net_socket_ssl_bear.o
else ifeq ($(HAVE_MBEDTLS), 1)
OBJ += $(LIBRETRO_COMM_DIR)/net/net_socket_ssl_mbed.o \
network/tls_log.o
else ifeq ($(HAVE_SSL), 1)
OBJ += $(LIBRETRO_COMM_DIR)/net/net_socket_ssl_mbed.o
endif
ifeq ($(HAVE_IFINFO), 1)
@@ -2845,41 +2930,109 @@ ifeq ($(HAVE_TEST_DRIVERS), 1)
OBJ += input/drivers/test_input.o
endif
# The built-in NFSv3 client and its nfs:// VFS backend. Needs only
# networking; the console Makefiles do not set it.
ifeq ($(HAVE_RETRONFS), 1)
DEFINES += -DHAVE_NFSCLIENT -DHAVE_RETRONFS
OBJ += $(LIBRETRO_COMM_DIR)/net/net_nfs3.o \
$(LIBRETRO_COMM_DIR)/vfs/vfs_implementation_nfs.o
endif
# The background prefetcher the network backends read through; once,
# whichever of them are built.
ifneq (,$(filter 1,$(HAVE_RETRONFS) $(HAVE_RETROSMB) $(HAVE_SMBCLIENT)))
OBJ += $(LIBRETRO_COMM_DIR)/vfs/vfs_prefetch.o
endif
# The built-in SMB2/3 client (libretro-common/net/net_smb2.c) stands in
# for libsmb2: the same HAVE_SMBCLIENT surface for the menu, VFS and
# cloud sync, no bundled or system library, needs HAVE_CRYPTO.
ifeq ($(HAVE_RETROSMB), 1)
DEFINES += -DHAVE_SMBCLIENT -DHAVE_RETROSMB
OBJ += $(LIBRETRO_COMM_DIR)/net/net_smb2.o \
$(LIBRETRO_COMM_DIR)/net/net_krb5.o \
$(LIBRETRO_COMM_DIR)/vfs/vfs_implementation_smb.o
else ifeq ($(HAVE_SMBCLIENT), 1)
# a system libsmb2 behind the same HAVE_SMBCLIENT surface
ifneq (,$(filter 1,$(HAVE_SMBCLIENT) $(HAVE_BUILTINSMBCLIENT)))
DEFINES += -DHAVE_SMBCLIENT
# The libsmb2 public headers gate their <stdint.h> include on
# HAVE_STDINT_H (autoconf-style); every TU that includes them needs
# the define.
# HAVE_STDINT_H (autoconf-style), and every TU that includes
# them - the bundled library sources and frontend consumers
# like network/cloud_sync/smb.c alike - needs the define.
# It used to arrive only as a side effect of an unrelated block,
# which broke builds that did not enable that block on hundreds
# of unknown uint*_t types. Own the dependency here.
DEFINES += -DHAVE_STDINT_H
ifeq ($(shell pkg-config --exists libsmb2 && echo yes),yes)
CFLAGS += $(shell pkg-config --cflags libsmb2)
LIBS += $(shell pkg-config --libs libsmb2)
else
LIBS += -lsmb2
ifeq ($(HAVE_SMBCLIENT),1)
# system libsmb2
ifeq ($(shell pkg-config --exists libsmb2 && echo yes),yes)
CFLAGS += $(shell pkg-config --cflags libsmb2)
LIBS += $(shell pkg-config --libs libsmb2)
else
LIBS += -lsmb2
endif
else ifeq ($(HAVE_BUILTINSMBCLIENT),1)
# builtin libsmb2
ifneq ($(findstring Win32,$(OS)),)
# WIN32_LEAN_AND_MEAN is defined with an empty replacement list on
# purpose. It has to be on the command line because the bundled
# libsmb2 needs it, but it lands on every TU in the build, and ~40
# RetroArch headers do a bare "#define WIN32_LEAN_AND_MEAN" of their
# own before including <windows.h>. A plain -DWIN32_LEAN_AND_MEAN
# expands to 1, which does not match those, so each one of them
# warns about a macro redefinition. Nothing tests the value, only
# #ifndef, so an empty definition is equivalent and silent.
CFLAGS += -D_WINDOWS -DWIN32_LEAN_AND_MEAN=
CFLAGS += -DNEED_RANDOM -DNEED_SRANDOM -DNEED_GETLOGIN_R
endif
INCLUDE_DIRS += -I$(DEPS_DIR)/libsmb2/include
INCLUDE_DIRS += -I$(DEPS_DIR)/libsmb2/include/smb2
CFLAGS += "-D_U_=__attribute__((unused))"
CFLAGS += -DHAVE_TIME_H -DHAVE_FCNTL_H -DHAVE_UNISTD_H
CFLAGS += -DHAVE_STDLIB_H -DSTDC_HEADERS
CFLAGS += -DHAVE_STRING_H
CFLAGS += -DHAVE_LINGER
ifeq ($(findstring Win32,$(OS)),)
CFLAGS += -DHAVE_SYS_UIO_H
CFLAGS += -DHAVE_POLL_H -DHAVE_NETDB_H
CFLAGS += -DHAVE_NETINET_TCP_H -DHAVE_NETINET_IN_H
CFLAGS += -DHAVE_SYS_SOCKET_H -DHAVE_ARPA_INET_H
endif
OBJ += \
deps/libsmb2/lib/aes.o \
deps/libsmb2/lib/aes_apple.o \
deps/libsmb2/lib/aes128ccm.o \
deps/libsmb2/lib/asn1-ber.o \
deps/libsmb2/lib/aes_reference.o \
deps/libsmb2/lib/alloc.o \
deps/libsmb2/lib/compat.o \
deps/libsmb2/lib/dcerpc.o \
deps/libsmb2/lib/dcerpc-lsa.o \
deps/libsmb2/lib/dcerpc-srvsvc.o \
deps/libsmb2/lib/errors.o \
deps/libsmb2/lib/hmac.o \
deps/libsmb2/lib/hmac-md5.o \
deps/libsmb2/lib/init.o \
deps/libsmb2/lib/krb5-wrapper.o \
deps/libsmb2/lib/libsmb2.o \
deps/libsmb2/lib/md4c.o \
deps/libsmb2/lib/ntlmssp.o \
deps/libsmb2/lib/pdu.o \
deps/libsmb2/lib/sha1.o \
deps/libsmb2/lib/sha224-256.o \
deps/libsmb2/lib/sha384-512.o \
deps/libsmb2/lib/smb2-cmd-close.o \
deps/libsmb2/lib/smb2-cmd-create.o \
deps/libsmb2/lib/smb2-cmd-echo.o \
deps/libsmb2/lib/smb2-cmd-error.o \
deps/libsmb2/lib/smb2-cmd-flush.o \
deps/libsmb2/lib/smb2-cmd-ioctl.o \
deps/libsmb2/lib/smb2-cmd-lock.o \
deps/libsmb2/lib/smb2-cmd-logoff.o \
deps/libsmb2/lib/smb2-cmd-negotiate.o \
deps/libsmb2/lib/smb2-cmd-oplock-break.o \
deps/libsmb2/lib/smb2-cmd-notify-change.o \
deps/libsmb2/lib/smb2-cmd-query-directory.o \
deps/libsmb2/lib/smb2-cmd-query-info.o \
deps/libsmb2/lib/smb2-cmd-read.o \
deps/libsmb2/lib/smb2-cmd-session-setup.o \
deps/libsmb2/lib/smb2-cmd-set-info.o \
deps/libsmb2/lib/smb2-cmd-tree-connect.o \
deps/libsmb2/lib/smb2-cmd-tree-disconnect.o \
deps/libsmb2/lib/smb2-cmd-write.o \
deps/libsmb2/lib/smb2-data-file-info.o \
deps/libsmb2/lib/smb2-data-filesystem-info.o \
deps/libsmb2/lib/smb2-data-security-descriptor.o \
deps/libsmb2/lib/smb2-data-reparse-point.o \
deps/libsmb2/lib/smb2-share-enum.o \
deps/libsmb2/lib/smb2-signing.o \
deps/libsmb2/lib/smb3-seal.o \
deps/libsmb2/lib/socket.o \
deps/libsmb2/lib/spnego-wrapper.o \
deps/libsmb2/lib/sync.o \
deps/libsmb2/lib/timestamps.o \
deps/libsmb2/lib/usha.o \
deps/libsmb2/lib/unicode.o
endif
OBJ += $(LIBRETRO_COMM_DIR)/vfs/vfs_implementation_smb.o
endif
+4 -8
View File
@@ -65,8 +65,11 @@ ifeq ($(GRIFFIN_BUILD), 1)
DEFINES += -DHAVE_OVERLAY
DEFINES += -DHAVE_CORE_INFO_CACHE
#DEFINES += -DHAVE_SOCKET_LEGACY
#-DHAVE_SSL -DHAVE_BUILTINMBEDTLS -DMBEDTLS_SSL_DEBUG_ALL
#ssl is currently incompatible with griffin due to use of the "static" flag on repeating functions that will conflict when included in one file
CFLAGS += -I. \
-Ideps \
-Ideps/mbedtls \
-Ideps/rcheevos/include \
-Ilibretro-common/include
else
@@ -100,14 +103,7 @@ else
HAVE_IFINFO = 1
HAVE_CHEEVOS = 1
HAVE_THREADS = 1
# the cleanroom crypto, keychain and TLS client
HAVE_CRYPTO = 1
HAVE_KEYCHAIN = 1
HAVE_RETROSSL = 1
# the built-in NFS client (nfs://); it needs no crypto
HAVE_RETRONFS = 1
# the built-in SMB client (smb://), on the crypto above
HAVE_RETROSMB = 1
HAVE_BUILTINMBEDTLS = 1
HAVE_CORE_INFO_CACHE = 1
HAVE_CLOUDSYNC = 1
+2 -5
View File
@@ -43,10 +43,7 @@ HAVE_AL = 1
HAVE_ALSA = 0
HAVE_DSP_FILTER = 1
HAVE_VIDEO_FILTER = 1
# the cleanroom crypto, keychain and TLS client
HAVE_CRYPTO = 1
HAVE_KEYCHAIN = 1
HAVE_RETROSSL = 1
HAVE_BUILTINMBEDTLS = 1
HAVE_C99 = 1
HAVE_CC = 1
HAVE_CC_RESAMPLER = 1
@@ -80,7 +77,7 @@ HAVE_OPENGL1 = 0
HAVE_OPENGLES = 1
HAVE_OPENGLES3 = 0
HAVE_OPENGL_CORE = 0
HAVE_OPENSSL = 0
HAVE_OPENSSL = 1
HAVE_OVERLAY = 1
HAVE_RBMP = 1
HAVE_RJPEG = 1
+4 -2
View File
@@ -3,6 +3,7 @@ DEPS_DIR := $(ROOT_DIR)/deps
DEBUG = 0
HAVE_LOGGER = 0
HAVE_FILE_LOGGER = 0
HAVE_CC_RESAMPLER = 1
WHOLE_ARCHIVE_LINK = 0
BIG_STACK = 1
PC_DEVELOPMENT_IP_ADDRESS = 255.255.255.255
@@ -327,7 +328,8 @@ else ifeq ($(platform), vita)
libretro-common/audio/conversion/s16_to_float_neon.o \
libretro-common/audio/conversion/float_to_s16_neon.o \
memory/neon/memcpy-neon.o \
libretro-common/audio/resampler/drivers/sinc_resampler_neon.o
libretro-common/audio/resampler/drivers/sinc_resampler_neon.o \
audio/drivers_resampler/cc_resampler_neon.o
LIBDIRS += -L.
LDFLAGS += -Wl,-q
@@ -1007,7 +1009,7 @@ else ifneq (,$(findstring unix,$(platform)))
EXT_TARGET := $(TARGET_NAME)
EXT_INTER_TARGET := $(TARGET_NAME)
INCLUDE += -Ilibretro-common/include -Igfx/include -Ideps -Ideps/rcheevos/include -Ideps/SPIRV-Cross -Ideps/glslang -I.
LIBS += -ldl -lm -lpthread -lGL -ludev -lpulse -lX11 -lX11-xcb -lxcb -lXxf86vm
LIBS += -ldl -lm -lpthread -lGL -ludev -lpulse -lX11 -lX11-xcb -lXxf86vm
endif
ifneq (,$(findstring msvc,$(platform)))
+1
View File
@@ -52,6 +52,7 @@ HAVE_VIDEO_FILTER = 1
HAVE_STATIC_VIDEO_FILTERS = 1
HAVE_STATIC_AUDIO_FILTERS = 1
HAVE_FILTERS_BUILTIN = 1
HAVE_BUILTINMBEDTLS = 0
HAVE_C99 = 1
HAVE_CC = 1
HAVE_CC_RESAMPLER = 1
+2 -9
View File
@@ -58,14 +58,7 @@ HAVE_RVORBIS = 1
HAVE_RMODTRACKER = 1
HAVE_CORE_INFO_CACHE = 1
HAVE_CLOUDSYNC = 1
# The cleanroom crypto, keychain and TLS 1.2/1.3 client; nothing to link.
HAVE_CRYPTO = 1
HAVE_KEYCHAIN = 1
HAVE_RETROSSL = 1
# The built-in NFS client (nfs://); it needs no crypto.
HAVE_RETRONFS = 1
# The built-in SMB client (smb://), on the crypto above.
HAVE_RETROSMB = 1
HAVE_SSL = 1
# RetroArch libnx useful flags
HAVE_THREADS = 1
@@ -157,7 +150,7 @@ LDFLAGS = -specs=$(DEVKITPRO)/libnx/switch.specs $(ARCH) -Wl,--allow-multiple-de
# add things from Makefile.common
CFLAGS += $(DEF_FLAGS)
LIBS := -lswresample -lavformat -lavcodec -lavutil -lswscale -lstdc++ -lbz2 -lpng -lz -lnx -lvpx -lopus -llzma -lnx -lm
LIBS := -lswresample -lavformat -lavcodec -lavutil -lswscale -lstdc++ -lbz2 -lpng -lz -lnx -lvpx -lopus -llzma -lmbedtls -lmbedx509 -lmbedcrypto -lnx -lm
ifeq ($(HAVE_OPENGL), 1)
LIBS := -lEGL -lglapi -ldrm_nouveau $(LIBS)
+1
View File
@@ -54,6 +54,7 @@ HAVE_VIDEO_FILTER = 1
HAVE_STATIC_VIDEO_FILTERS = 1
HAVE_STATIC_AUDIO_FILTERS = 1
HAVE_FILTERS_BUILTIN = 1
HAVE_BUILTINMBEDTLS = 0
HAVE_C99 = 1
HAVE_CC = 1
HAVE_CC_RESAMPLER = 1
+6 -19
View File
@@ -122,29 +122,20 @@ LIBS += -logc
CFLAGS += -DGEKKO -U__INT32_TYPE__ -U __UINT32_TYPE__ -D__INT32_TYPE__=int
# 24 MB of MEM1 makes resident code weight count, so features that cannot
# do their job on this hardware are compiled out: runahead (the second
# savestate never fits beside a running core), the webp/dds thumbnail
# decoders, the mod/s3m/xm tracker, 7z (LZMA dictionaries alone outsize
# the console's free RAM, so 7z archives could never extract anyway) and
# libretrodb (defaulted on by Makefile.common; database scanning is
# impractical here, and the explore menu goes with it). Together these
# return about 640 KB of MEM1 to content.
HAVE_LIBRETRODB := 0
HAVE_RUNAHEAD := 0
HAVE_RUNAHEAD := 1
HAVE_DSP_FILTER := 1
HAVE_VIDEO_FILTER := 1
HAVE_FILTERS_BUILTIN := 1
HAVE_THREADS := 1
HAVE_RPNG := 1
HAVE_RWEBP := 0
HAVE_RDDS := 0
HAVE_RWEBP := 1
HAVE_RDDS := 1
HAVE_RJPEG := 1
HAVE_RBMP := 1
HAVE_RTGA := 1
HAVE_RMODTRACKER := 0
HAVE_RMODTRACKER := 1
HAVE_OVERLAY := 1
HAVE_7ZIP := 0
HAVE_7ZIP := 1
HAVE_RZSTD := 1
HAVE_CONFIGFILE := 1
HAVE_PATCH := 1
@@ -170,13 +161,9 @@ INCLUDE += -I./libretro-common/include \
-Ideps
CFLAGS += -Wall -std=gnu99 $(MACHDEP) $(PLATCFLAGS) $(INCLUDE)
OBJ = $(PLATOBJS) audio/drivers/gx_audio.o gfx/drivers/gx_gfx.o gfx/display_servers/dispserv_gx.o gfx/display_servers/dispserv_gx_modes.o input/drivers/gx_input.o frontend/drivers/platform_gx.o input/drivers_joypad/gx_joypad.o
OBJ = $(PLATOBJS) audio/drivers/gx_audio.o gfx/drivers/gx_gfx.o input/drivers/gx_input.o frontend/drivers/platform_gx.o input/drivers_joypad/gx_joypad.o
ifeq ($(EXTERNAL_LIBOGC), 0)
# Bridge the prebuilt in-tree libogc's __syscalls table onto the
# per-symbol __syscall_* hooks modern devkitPPC libsysbase dispatches
# through (see wii/libogc_syscalls_compat.c).
OBJ += wii/libogc_syscalls_compat.o
OBJ += wii/libogc/libfat/libfat.o wii/libogc/libfat/fatfile.o wii/libogc/libfat/file_allocation_table.o wii/libogc/libfat/cache.o wii/libogc/libfat/fatdir.o wii/libogc/libfat/partition.o wii/libogc/libfat/directory.o wii/libogc/libfat/filetime.o wii/libogc/libfat/disc.o
endif
+1 -1
View File
@@ -19,7 +19,7 @@ AUTH_INFO = 000000000000000000000000001C004000FF00000000008000000000000000000000
OBJ += deps/xxHash/xxhash.o \
# input/drivers/ps4_input.o \
# input/drivers_joypad/ps4_joypad.o \
# audio/drivers/ps4_audio.o \
# audio/drivers/psp_audio.o \
# gfx/drivers_context/orbis_ctx.o \
# frontend/drivers/platform_orbis.o
+2 -6
View File
@@ -54,11 +54,7 @@ HAVE_VIDEO_FILTER = 1
HAVE_STATIC_VIDEO_FILTERS = 1
HAVE_STATIC_AUDIO_FILTERS = 1
HAVE_FILTERS_BUILTIN = 1
# the cleanroom crypto, keychain and TLS client (HAVE_OPENSSL linked a
# library no backend used)
HAVE_CRYPTO = 1
HAVE_KEYCHAIN = 1
HAVE_RETROSSL = 1
HAVE_BUILTINMBEDTLS = 0
HAVE_C99 = 1
HAVE_CC = 1
HAVE_CC_RESAMPLER = 1
@@ -89,7 +85,7 @@ HAVE_OPENGL1 = 0
HAVE_OPENGLES = 0
HAVE_OPENGLES3 = 0
HAVE_OPENGL_CORE = 0
HAVE_OPENSSL = 0
HAVE_OPENSSL = 1
HAVE_OVERLAY = 0
HAVE_RBMP = 1
HAVE_RJPEG = 1
+2 -6
View File
@@ -51,11 +51,7 @@ HAVE_VIDEO_FILTER = 1
HAVE_STATIC_VIDEO_FILTERS = 1
HAVE_STATIC_AUDIO_FILTERS = 1
HAVE_FILTERS_BUILTIN = 1
# the cleanroom crypto, keychain and TLS client (HAVE_OPENSSL linked a
# library no backend used)
HAVE_CRYPTO = 1
HAVE_KEYCHAIN = 1
HAVE_RETROSSL = 1
HAVE_BUILTINMBEDTLS = 0
HAVE_C99 = 1
HAVE_CC = 1
HAVE_CC_RESAMPLER = 1
@@ -87,7 +83,7 @@ HAVE_OPENGL1 = 0
HAVE_OPENGLES = 0
HAVE_OPENGLES3 = 0
HAVE_OPENGL_CORE = 0
HAVE_OPENSSL = 0
HAVE_OPENSSL = 1
HAVE_OVERLAY = 0
HAVE_RBMP = 1
HAVE_RJPEG = 1
+1
View File
@@ -51,6 +51,7 @@ HAVE_VIDEO_FILTER = 1
HAVE_STATIC_VIDEO_FILTERS = 1
HAVE_STATIC_AUDIO_FILTERS = 1
HAVE_FILTERS_BUILTIN = 1
HAVE_BUILTINMBEDTLS = 0
HAVE_C99 = 1
HAVE_CC = 1
HAVE_CC_RESAMPLER = 1
+1
View File
@@ -53,6 +53,7 @@ HAVE_VIDEO_FILTER = 1
HAVE_STATIC_VIDEO_FILTERS = 1
HAVE_STATIC_AUDIO_FILTERS = 1
HAVE_FILTERS_BUILTIN = 1
HAVE_BUILTINMBEDTLS = 0
HAVE_C99 = 1
HAVE_CC = 1
HAVE_CC_RESAMPLER = 1
+1 -8
View File
@@ -75,17 +75,10 @@ else
HAVE_VITAGL := 0
endif
HAVE_NETWORKING := 1
# the cleanroom crypto, keychain and TLS client
HAVE_CRYPTO := 1
HAVE_KEYCHAIN := 1
HAVE_RETROSSL := 1
HAVE_BUILTINMBEDTLS := 1
HAVE_IFINFO := 1
HAVE_NETPLAYDISCOVERY := 1
HAVE_SOCKET_LEGACY := 1
# the built-in NFS client (nfs://); it needs no crypto
HAVE_RETRONFS := 1
# the built-in SMB client (smb://), on the crypto above
HAVE_RETROSMB := 1
HAVE_MENU := 1
HAVE_MENU_COMMON := 1
HAVE_GFX_WIDGETS := 1
+3 -8
View File
@@ -49,20 +49,15 @@ HAVE_AUDIOIO = 0
HAVE_AUDIOMIXER = 1
HAVE_BLISSBOX = 0
HAVE_BSV_MOVIE = 1
HAVE_BUILTINBEARSSL = 0
HAVE_RFLAC = 1
HAVE_DSP_FILTER = 1
HAVE_VIDEO_FILTER = 1
HAVE_STATIC_VIDEO_FILTERS = 1
HAVE_STATIC_AUDIO_FILTERS = 1
HAVE_FILTERS_BUILTIN = 1
# The cleanroom network stack, as on the desktop builds: the crypto,
# the encrypted keychain, the TLS 1.2/1.3 client and the SMB2/3 client
# with Kerberos; plus the NFSv3/v4 client. Needs nothing but sockets.
HAVE_CRYPTO = 1
HAVE_KEYCHAIN = 1
HAVE_RETROSSL = 1
HAVE_RETROSMB = 1
HAVE_RETRONFS = 1
HAVE_BUILTINMBEDTLS = 1
HAVE_BUILTINSMBCLIENT := 1
HAVE_C99 = 1
HAVE_CC = 1
HAVE_CC_RESAMPLER = 1
+1 -1
View File
@@ -179,7 +179,7 @@ INCLUDE += -I./libretro-common/include \
-Ideps/rcheevos/include
CFLAGS += -Wall -std=gnu99 $(MACHDEP) $(PLATCFLAGS) $(INCLUDE)
OBJ = $(PLATOBJS) audio/drivers/gx_audio.o gfx/drivers/gx_gfx.o gfx/display_servers/dispserv_gx.o gfx/display_servers/dispserv_gx_modes.o input/drivers/gx_input.o frontend/drivers/platform_gx.o input/drivers_joypad/gx_joypad.o libretro-common/memory/mem2_manager.o gfx/drivers/gx_gfx_vi_encoder.o frontend/drivers/platform_wii.o input/drivers_hid/wiiusb_hid.o
OBJ = $(PLATOBJS) audio/drivers/gx_audio.o gfx/drivers/gx_gfx.o input/drivers/gx_input.o frontend/drivers/platform_gx.o input/drivers_joypad/gx_joypad.o libretro-common/memory/mem2_manager.o gfx/drivers/gx_gfx_vi_encoder.o frontend/drivers/platform_wii.o input/drivers_hid/wiiusb_hid.o
ifeq ($(EXTERNAL_LIBOGC), 0)
OBJ += wii/libogc/libfat/libfat.o wii/libogc/libfat/fatfile.o wii/libogc/libfat/file_allocation_table.o wii/libogc/libfat/cache.o wii/libogc/libfat/fatdir.o wii/libogc/libfat/partition.o wii/libogc/libfat/directory.o wii/libogc/libfat/filetime.o wii/libogc/libfat/disc.o
-2
View File
@@ -161,8 +161,6 @@ endif
HAVE_LANGEXTRA = 1
HAVE_LIBRETRODB = 1
HAVE_NETWORKING = 1
# the built-in NFS client (nfs://); it needs no crypto
HAVE_RETRONFS = 1
HAVE_IFINFO = 1
HAVE_NETPLAYDISCOVERY = 1
HAVE_CHEEVOS = 1
-44
View File
@@ -1,44 +0,0 @@
/* RetroArch - A frontend for libretro.
* Copyright (C) 2026 - The RetroArch team
*
* RetroArch is free software: you can redistribute it and/or modify it under the terms
* of the GNU General Public License as published by the Free Software Found-
* ation, either version 3 of the License, or (at your option) any later version.
*
* RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with RetroArch.
* If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef __AUDIO_DEVICE_LABEL_H
#define __AUDIO_DEVICE_LABEL_H
#include <stddef.h>
#include <string.h>
#include <retro_inline.h>
#include <compat/strl.h>
/* A device list entry can be a path with the hardware's name after
* it in brackets - "/dev/dsp0 (HDA Intel)" - which is how oss and
* audioio list their nodes so the user can tell them apart. The menu
* stores the entry as shown, and it comes back to init as the device
* string, so a driver that opens the string as a path takes the path
* off the front first: everything before the first " (". A string
* without one is a path already and is copied as it is. Returns the
* length of the path. */
static INLINE size_t audio_device_label_path(char *dst, size_t len,
const char *entry)
{
const char *cut = strstr(entry, " (");
size_t n = cut ? (size_t)(cut - entry) : strlen(entry);
if (n >= len)
n = len - 1;
memcpy(dst, entry, n);
dst[n] = '\0';
return n;
}
#endif
+209 -341
View File
@@ -118,22 +118,6 @@ static bool audio_driver_pipeline_transport_publish(uint32_t tempo_q16,
* out of pipe_ring per pass (one batch slice), and the producer stages
* at most this many when it has to convert a float batch first. */
#define AUDIO_PIPE_SLICE_INT16S AUDIO_CHUNK_SIZE_NONBLOCKING
/* Multichannel fold staging (audio_driver_state_t::multi_fold), in
* frames. Every multichannel entry slices its batch to at most this
* many frames before folding, so the region is sized once at init and
* the fold never grows it on the batch callback. Shared by the float
* and int16 paths; sized for float, the larger. */
#define AUDIO_MULTI_FOLD_FRAMES (AUDIO_CHUNK_SIZE_NONBLOCKING >> 1)
/* Recorder remap staging (audio_driver_state_t::record_remap): two
* halves of one batch slice at the widest frame either side can carry
* (a multichannel core is bounded at 2 + 2 * extra pairs, a recorder
* layout at the same), so it too is a fixed region carved at init. In
* int16 samples per half. */
#define AUDIO_RECORD_REMAP_CHANNELS 16
#define AUDIO_RECORD_REMAP_INT16S ((AUDIO_CHUNK_SIZE_NONBLOCKING >> 1) \
* AUDIO_RECORD_REMAP_CHANNELS)
/* The canonical wide frame on a multi-channel core's ring: a slot per
* speaker position bit, FL and FR first. */
#define AUDIO_PIPE_CANON_CHANNELS 11
@@ -264,6 +248,8 @@ audio_driver_t audio_null = {
audio_driver_t *audio_drivers[] = {
#ifdef HAVE_ALSA
&audio_alsa,
#if !defined(__QNX__) && !defined(MIYOO) && defined(HAVE_THREADS)
#endif
#endif
#ifdef HAVE_TINYALSA
&audio_tinyalsa,
@@ -307,11 +293,8 @@ audio_driver_t *audio_drivers[] = {
#ifdef HAVE_DSOUND
&audio_dsound,
#endif
#ifdef HAVE_SDL
&audio_sdl1,
#endif
#ifdef HAVE_SDL2
&audio_sdl2,
#if defined(HAVE_SDL) || defined(HAVE_SDL2)
&audio_sdl,
#endif
#ifdef HAVE_SDL3
&audio_sdl3,
@@ -340,15 +323,9 @@ audio_driver_t *audio_drivers[] = {
#if defined(HAVE_AUDIOWORKLET)
&audio_audioworklet,
#endif
#if defined(PSP)
#if defined(PSP) || defined(VITA) || defined(ORBIS)
&audio_psp,
#endif
#if defined(VITA)
&audio_psp2,
#endif
#if defined(ORBIS)
&audio_ps4,
#endif
#if defined(PS2)
&audio_ps2,
#endif
@@ -373,6 +350,7 @@ microphone_driver_t microphone_null = {
NULL,
NULL,
NULL,
NULL,
"null",
NULL,
NULL,
@@ -383,13 +361,12 @@ microphone_driver_t microphone_null = {
microphone_driver_t *microphone_drivers[] = {
#ifdef HAVE_ALSA
&microphone_alsa,
#if !defined(__QNX__) && !defined(MIYOO) && defined(HAVE_THREADS)
#endif
#endif
#ifdef HAVE_WASAPI
&microphone_wasapi,
#endif
#ifdef HAVE_WDMKS
&microphone_wdmks,
#endif
#ifdef HAVE_SDL2
&microphone_sdl, /* Microphones are not supported in SDL 1 */
#endif
@@ -611,11 +588,27 @@ static bool audio_driver_resampler_realloc(audio_driver_state_t *audio_st,
audio_st->resampler_quality, audio_st->src_ratio_orig, hq_oversampling);
audio_st->resampler_hq = initialized && hq_oversampling
&& audio_st->src_ratio_orig >= 2.0
&& audio_st->resampler
&& (audio_st->resampler->caps & RESAMPLER_CAP_HQ_OVERSAMPLE);
&& audio_st->resampler == &sinc_resampler;
return initialized;
}
/* Map the shared resampler quality enum onto the integer sinc driver's own
* quality enum (they use different orderings). */
static enum sinc_int16_quality audio_sinc_int16_quality_map(
enum resampler_quality q)
{
switch (q)
{
case RESAMPLER_QUALITY_LOWEST: return SINC_INT16_QUALITY_LOWEST;
case RESAMPLER_QUALITY_LOWER: return SINC_INT16_QUALITY_LOWER;
case RESAMPLER_QUALITY_HIGHER: return SINC_INT16_QUALITY_HIGHER;
case RESAMPLER_QUALITY_HIGHEST: return SINC_INT16_QUALITY_HIGHEST;
case RESAMPLER_QUALITY_NORMAL:
case RESAMPLER_QUALITY_DONTCARE:
default: return SINC_INT16_QUALITY_NORMAL;
}
}
size_t audio_driver_get_underruns(void)
{
audio_driver_state_t *audio_st = &audio_driver_st;
@@ -657,13 +650,21 @@ static void audio_driver_extra_free(audio_driver_state_t *audio_st)
/* An int16 resampler instance of the kind the front pair uses. */
static void *audio_driver_int16_resampler_new(audio_driver_state_t *audio_st)
{
retro_resampler_int16_t rs;
retro_resampler_int16_new(&rs,
(audio_st->resampler && audio_st->resampler->short_ident)
? audio_st->resampler->short_ident : NULL,
audio_st->resampler_quality, audio_st->src_ratio_orig,
audio_st->resampler_hq);
return rs.data;
const char *rs_ident = (audio_st->resampler && audio_st->resampler->short_ident)
? audio_st->resampler->short_ident : "";
if (string_is_equal(rs_ident, "sinc"))
return sinc_resampler_int16_init_hq(audio_st->src_ratio_orig,
audio_sinc_int16_quality_map(audio_st->resampler_quality),
audio_st->resampler_hq);
#ifdef HAVE_NEAREST_RESAMPLER
if (string_is_equal(rs_ident, "nearest"))
return nearest_resampler_int16_init();
#endif
#ifdef HAVE_CC_RESAMPLER
if (string_is_equal(rs_ident, "cc"))
return cc_resampler_int16_init(audio_st->src_ratio_orig);
#endif
return NULL;
}
/* Room for a batch of extras: the buffers, and resampler instances of
@@ -984,50 +985,12 @@ static INLINE bool audio_driver_pipe_ahead(const audio_driver_state_t *audio_st,
return d && d <= audio_st->pipe_ring.capacity;
}
/* The main thread's side of the resume mark: a new sequence, armed at
* @at or not armed at all. The main thread is the mark's only writer. */
static void audio_driver_pipe_fade_publish(audio_driver_state_t *audio_st,
bool armed, size_t at)
{
unsigned seq = ((unsigned)retro_atomic_load_relaxed_int(
&audio_st->pipe_fade_in_mark) >> 1) + 1;
if (armed)
retro_atomic_store_release_size(&audio_st->pipe_fade_in_at, at);
retro_atomic_store_release_int(&audio_st->pipe_fade_in_mark,
(int)((seq << 1) | (armed ? 1u : 0u)));
}
/* The consumer's side: the resume point a mark still asks for, and the
* mark's sequence to record once it is acted on. The position is taken
* between two reads of the mark that agree, so it is that mark's. */
static bool audio_driver_pipe_fade_pending(audio_driver_state_t *audio_st,
size_t *at, unsigned *seq)
{
for (;;)
{
unsigned mark = (unsigned)retro_atomic_load_acquire_int(
&audio_st->pipe_fade_in_mark);
if (!(mark & 1u) || (mark >> 1) == audio_st->pipe_fade_in_seen)
return false;
*at = retro_atomic_load_acquire_size(&audio_st->pipe_fade_in_at);
if ((unsigned)retro_atomic_load_acquire_int(
&audio_st->pipe_fade_in_mark) == mark)
{
*seq = mark >> 1;
return true;
}
}
}
/* The ring's positions restart with it; so do the marks in them. Both
* owners are parked. */
/* The ring's positions restart with it; so do the marks in them. */
static void audio_driver_pipe_marks_clear(audio_driver_state_t *audio_st)
{
audio_st->pipe_discard_seen =
retro_atomic_load_acquire_int(&audio_st->pipe_discard_gen);
audio_st->pipe_fade_in_seen =
(unsigned)retro_atomic_load_acquire_int(
&audio_st->pipe_fade_in_mark) >> 1;
retro_atomic_store_release_int(&audio_st->pipe_fade_in_set, 0);
audio_st->pipe_arm_fade = false;
}
#endif
@@ -1053,23 +1016,6 @@ static bool audio_driver_deinit_internal(bool audio_enable)
if (n)
RARCH_LOG("[Audio] Driver \"%s\": %u period%s of silence for want of audio this session.\n",
audio_driver_get_ident(), (unsigned)n, n == 1 ? "" : "s");
#ifdef HAVE_THREADS
/* And the consumer's side of it: whether the pipe ever ran
* short of the core, which is the other place silence comes
* from, and how low it got. */
if (audio_st->pipe_threaded
&& retro_atomic_load_acquire_size(&audio_st->pipe_held_min1))
{
size_t waits = retro_atomic_load_acquire_size(&audio_st->pipe_source_waits);
size_t low = retro_atomic_load_acquire_size(&audio_st->pipe_held_min1) - 1;
RARCH_LOG("[Audio] Pipeline: waited for the core %u time%s after priming,"
" %.2f ms at the longest; the pipe held %u frames (%.1f ms) at its lowest.\n",
(unsigned)waits, waits == 1 ? "" : "s",
(double)retro_atomic_load_acquire_size(&audio_st->pipe_source_wait_max_us) / 1000.0,
(unsigned)low,
audio_st->input > 0.0f ? (double)low * 1000.0 / (double)audio_st->input : 0.0);
}
#endif
if (audio_st->context_audio_data)
audio->free(audio_st->context_audio_data);
audio_st->context_audio_data = NULL;
@@ -1121,10 +1067,10 @@ static bool audio_driver_deinit_internal(bool audio_enable)
audio_st->pipe_wide = NULL;
audio_st->pipe_wide_bytes = 0;
audio_st->pipe_channels = 2;
/* multi_fold is a region of arena_float, freed with it below. */
free(audio_st->multi_fold);
audio_st->multi_fold = NULL;
audio_st->multi_fold_frames = 0;
/* record_remap is a region of arena_int16, freed with it above. */
free(audio_st->record_remap);
audio_st->record_remap = NULL;
audio_st->record_remap_frames = 0;
audio_st->core_layout = AUDIO_LAYOUT_STEREO;
@@ -3458,14 +3404,18 @@ static void audio_driver_flush(audio_driver_state_t *audio_st,
* batch. The resampler sees identical bytes either way, so output is
* bit-exact.
*
* Nothing downstream requires the pointer to be aligned: every
* resampler reads data_in scalar-wise or with unaligned loads, and so
* does the bypass clamp. */
* The pointer must still be suitably aligned: the CC resampler's ARM
* NEON assembly loads its input with an explicit alignment hint
* (`vld1.f32 d16, [r1, :64]!` in cc_resampler_neon.S), which faults on
* an under-aligned address. input_data is memalign_alloc(64); a
* core-owned buffer carries no such guarantee, so fall back to the copy
* unless the pointer is at least 16-byte aligned. */
{
bool synth_on = midi_driver_synth_active() && audio_st->synth_buf;
bool copy_input = !is_float
|| (audio_volume_gain != 1.0f)
|| synth_on
|| (((uintptr_t)data & 0xf) != 0)
#ifdef HAVE_DSP_FILTER
|| (audio_st->dsp != NULL)
#endif
@@ -4104,13 +4054,10 @@ bool audio_driver_init_internal(void *settings_data, bool audio_cb_inited)
* in addition to its own. */
size_t i16_rewind = AUDIO_ARENA_NEXT(i16_in_scratch,
max_buffer_samples, AUDIO_ARENA_ALIGN_INT16);
size_t i16_record = AUDIO_ARENA_NEXT(i16_rewind,
max_buffer_samples, AUDIO_ARENA_ALIGN_INT16);
size_t i16_total = i16_rewind + max_buffer_samples;
#else
size_t i16_record = AUDIO_ARENA_NEXT(i16_in_scratch,
max_buffer_samples, AUDIO_ARENA_ALIGN_INT16);
size_t i16_total = i16_in_scratch + max_buffer_samples;
#endif
size_t i16_total = i16_record + 2 * AUDIO_RECORD_REMAP_INT16S;
size_t f32_input = 0;
size_t f32_synth = AUDIO_ARENA_NEXT(f32_input,
max_buffer_samples, AUDIO_ARENA_ALIGN_FLOAT);
@@ -4119,13 +4066,10 @@ bool audio_driver_init_internal(void *settings_data, bool audio_cb_inited)
#ifdef HAVE_REWIND
size_t f32_rewind = AUDIO_ARENA_NEXT(f32_out,
outsamples_max, AUDIO_ARENA_ALIGN_FLOAT);
size_t f32_fold = AUDIO_ARENA_NEXT(f32_rewind,
max_buffer_samples, AUDIO_ARENA_ALIGN_FLOAT);
size_t f32_total = f32_rewind + max_buffer_samples;
#else
size_t f32_fold = AUDIO_ARENA_NEXT(f32_out,
outsamples_max, AUDIO_ARENA_ALIGN_FLOAT);
size_t f32_total = f32_out + outsamples_max;
#endif
size_t f32_total = f32_fold + AUDIO_MULTI_FOLD_FRAMES * 2;
int16_t *arena_int16 = (int16_t*)memalign_alloc(64,
i16_total * sizeof(int16_t));
float *arena_float = (float*)memalign_alloc(64,
@@ -4186,10 +4130,6 @@ bool audio_driver_init_internal(void *settings_data, bool audio_cb_inited)
audio_driver_st.rewind_buf_f = arena_float + f32_rewind;
#endif
audio_driver_st.output_samples_buf_length = outsamples_max * sizeof(float);
audio_driver_st.multi_fold = arena_float + f32_fold;
audio_driver_st.multi_fold_frames = AUDIO_MULTI_FOLD_FRAMES;
audio_driver_st.record_remap = arena_int16 + i16_record;
audio_driver_st.record_remap_frames = AUDIO_RECORD_REMAP_INT16S;
if (!audio_enable)
{
@@ -4289,9 +4229,6 @@ bool audio_driver_init_internal(void *settings_data, bool audio_cb_inited)
retro_atomic_store_release_int(&audio_driver_st.pipe_ctrl_avail, -1);
audio_driver_st.pipe_underruns_seen = 0;
audio_driver_st.pipe_priming = true;
retro_atomic_size_init(&audio_driver_st.pipe_source_waits, 0);
retro_atomic_size_init(&audio_driver_st.pipe_source_wait_max_us, 0);
retro_atomic_size_init(&audio_driver_st.pipe_held_min1, 0);
if (audio_driver_st.pipe_channels > 2)
{
size_t wide = audio_driver_st.pipe_pass_frames * AUDIO_PIPE_CANON_CHANNELS * sizeof(float);
@@ -4331,7 +4268,7 @@ bool audio_driver_init_internal(void *settings_data, bool audio_cb_inited)
audio_driver_st.pipe_park_ready = true;
AUDIO_FLAGS_SET(&audio_driver_st, AUDIO_FLAG_PIPELINE_THREADED);
audio_driver_st.pipe_threaded = true;
retro_atomic_int_init(&audio_driver_st.pipe_consumer_gone, 0);
audio_driver_st.pipe_consumer_gone = false;
RARCH_LOG("[Audio] Threaded pipeline: ring holds %u frames of %s core audio.\n",
(unsigned)(audio_driver_st.pipe_ring.capacity / audio_driver_st.pipe_frame_bytes),
audio_driver_st.pipe_float ? "float" : "int16");
@@ -4531,14 +4468,35 @@ bool audio_driver_init_internal(void *settings_data, bool audio_cb_inited)
&& audio_driver_st.resampler->short_ident)
{
const char *rs_ident = audio_driver_st.resampler->short_ident;
retro_resampler_int16_t rs;
retro_resampler_int16_new(&rs, rs_ident,
audio_driver_st.resampler_quality,
audio_driver_st.src_ratio_orig, audio_driver_st.resampler_hq);
audio_driver_st.resampler_data_int16 = rs.data;
audio_driver_st.resampler_int16_process = rs.process;
audio_driver_st.resampler_int16_free = rs.free;
audio_driver_st.resampler_int16_reset = rs.reset;
if (string_is_equal(rs_ident, "sinc"))
{
audio_driver_st.resampler_data_int16 = sinc_resampler_int16_init_hq(
audio_driver_st.src_ratio_orig,
audio_sinc_int16_quality_map(audio_driver_st.resampler_quality),
audio_driver_st.resampler_hq);
audio_driver_st.resampler_int16_process = sinc_resampler_int16_process;
audio_driver_st.resampler_int16_free = sinc_resampler_int16_free;
audio_driver_st.resampler_int16_reset = sinc_resampler_int16_reset;
}
#ifdef HAVE_NEAREST_RESAMPLER
else if (string_is_equal(rs_ident, "nearest"))
{
audio_driver_st.resampler_data_int16 = nearest_resampler_int16_init();
audio_driver_st.resampler_int16_process = nearest_resampler_int16_process;
audio_driver_st.resampler_int16_free = nearest_resampler_int16_free;
audio_driver_st.resampler_int16_reset = nearest_resampler_int16_reset;
}
#endif
#ifdef HAVE_CC_RESAMPLER
else if (string_is_equal(rs_ident, "cc"))
{
audio_driver_st.resampler_data_int16 = cc_resampler_int16_init(
audio_driver_st.src_ratio_orig);
audio_driver_st.resampler_int16_process = cc_resampler_int16_process;
audio_driver_st.resampler_int16_free = cc_resampler_int16_free;
audio_driver_st.resampler_int16_reset = cc_resampler_int16_reset;
}
#endif
if (audio_driver_st.resampler_int16_process)
RARCH_LOG("[Audio] %s resampler: integer s16 path %s.\n",
rs_ident,
@@ -4604,7 +4562,18 @@ bool audio_driver_init_internal(void *settings_data, bool audio_cb_inited)
? (double)audio_driver_st.buffer_size / frame_bytes
* 1000.0 / out_rate
: 0.0;
const char *ident = audio_driver_get_ident();
const char *ident = audio_driver_st.current_audio->ident;
#ifdef HAVE_THREADS
/* Name the driver the user chose, not the wrapper it runs
* under. */
if (string_is_equal(ident, "audio-thread"))
{
const audio_driver_t *inner = audio_thread_wrapped_driver(
audio_driver_st.context_audio_data);
if (inner)
ident = inner->ident;
}
#endif
RARCH_LOG("[Audio] Driver \"%s\" reports a %u-byte buffer: "
"%.1f ms of %u-channel %s at %u Hz against a %u ms latency setting%s; "
"rate control %s it near %.1f ms.\n",
@@ -4642,8 +4611,7 @@ bool audio_driver_init_internal(void *settings_data, bool audio_cb_inited)
* SIGFPE" workaround for the same hazard. Check it once, centrally,
* and fall back to no rate control rather than making every driver
* defend itself. */
if ( !audio_driver_st.current_audio->write_avail
|| !audio_driver_st.current_audio->buffer_size)
if (!audio_driver_st.current_audio->buffer_size)
RARCH_WARN("[Audio] Rate control was desired, but driver does not support needed features.\n");
else if (audio_driver_st.buffer_size == 0)
RARCH_WARN("[Audio] Rate control was desired, but the driver "
@@ -4700,8 +4668,7 @@ error:
void audio_driver_pipeline_consumer_exit(void)
{
/* Release: the consumer's loop is done before the word that says so. */
retro_atomic_store_release_int(&audio_driver_st.pipe_consumer_gone, 1);
audio_driver_st.pipe_consumer_gone = true;
}
void audio_driver_publish_runloop(void)
@@ -5053,7 +5020,7 @@ void audio_driver_pause_fade(bool paused)
retro_atomic_store_release_size(&audio_st->pipe_discard_to,
retro_atomic_load_acquire_size(&audio_st->pipe_ring.head));
retro_atomic_fetch_add_int(&audio_st->pipe_discard_gen, 1);
audio_driver_pipe_fade_publish(audio_st, false, 0);
retro_atomic_store_release_int(&audio_st->pipe_fade_in_set, 0);
#endif
/* Only when something was playing is there a step to smooth. */
@@ -5296,19 +5263,9 @@ static size_t audio_driver_pipe_target_frames(audio_driver_state_t *audio_st)
}
/* Never under one publish: the core delivers a frame at a time, and
* a pipe holding less than that between publishes is a device that
* runs dry between them whatever its own buffer holds.
*
* And where nothing regulates the fill - no rate control - one
* publish is no margin at all: the pipe holds a frame just after
* the core hands one over and nothing just before the next, so a
* frame late by any amount reaches the device as silence. Two
* publishes there, so a frame's worth is held at the worst phase,
* which is what a late frame costs. */
* runs dry between them whatever its own buffer holds. */
if (target < audio_st->pipe_pass_frames)
target = audio_st->pipe_pass_frames;
if ( !(AUDIO_FLAGS_GET(audio_st) & AUDIO_FLAG_CONTROL)
&& target < 2 * audio_st->pipe_pass_frames)
target = 2 * audio_st->pipe_pass_frames;
/* Within the ring less two publishes: one arriving, one of swing
* in the fill between the core's publish and the consumer's pass. */
ring_max = audio_st->pipe_ring.capacity / audio_st->pipe_frame_bytes;
@@ -5318,29 +5275,6 @@ static size_t audio_driver_pipe_target_frames(audio_driver_state_t *audio_st)
ring_max = 0;
return target < ring_max ? target : ring_max;
}
/* What priming fills the pipe to: the target and the device's buffer
* on top, since the device starts empty and the pipe is to hold a
* buffer's worth ahead of it. The same figure is the line above which
* audio is late after an underrun: trimming to the target alone threw
* the device's share away on the first pass after every prime, and the
* pipe then ran a device buffer short of what it had just been filled
* to. Zero when there is no target. */
static size_t audio_driver_pipe_prime_frames(audio_driver_state_t *audio_st)
{
size_t target = audio_driver_pipe_target_frames(audio_st);
size_t frame_bytes, device, room, limit;
if (!target)
return 0;
frame_bytes = audio_driver_dev_frame_bytes(audio_st);
device = (size_t)((double)audio_st->buffer_size / frame_bytes
/ audio_st->src_ratio_orig);
room = audio_st->pipe_ring.capacity / audio_st->pipe_frame_bytes;
limit = room > audio_st->pipe_pass_frames
? room - audio_st->pipe_pass_frames : 1;
target += device;
return target > limit ? limit : target;
}
#endif
/* Input and output do not overlap. Convert only the two populated slots. */
@@ -5612,8 +5546,9 @@ static void audio_driver_submit_width(audio_driver_state_t *audio_st,
* whatever the consumer flushes next: mark where that starts. */
if (from_core && audio_st->fade_in_pending)
{
audio_driver_pipe_fade_publish(audio_st, true,
retro_atomic_store_release_size(&audio_st->pipe_fade_in_at,
retro_atomic_load_relaxed_size(&audio_st->pipe_ring.head));
retro_atomic_store_release_int(&audio_st->pipe_fade_in_set, 1);
audio_st->fade_in_pending = false;
}
@@ -5652,8 +5587,7 @@ static void audio_driver_submit_width(audio_driver_state_t *audio_st,
: (AUDIO_FLAGS_GET(audio_st) & AUDIO_FLAG_NONBLOCK) != 0)
break;
if ( !(AUDIO_FLAGS_GET(audio_st) & AUDIO_FLAG_STARTED)
|| retro_atomic_load_acquire_int(
&audio_st->pipe_consumer_gone))
|| audio_st->pipe_consumer_gone)
break;
/* Sleep until the consumer has completed a pass. The
* generation is taken first, then the wait window is opened
@@ -6141,27 +6075,6 @@ bool audio_driver_pipeline_transport_step(struct audio_pipeline_stretch *stage,
output_budget, finishing, complete, &progress);
}
/* The consumer's account of the core, kept for the teardown line:
* a pass that starts with the pipe at its lowest so far, and a wait
* for source that ran after priming, with how long it took. */
static void audio_driver_pipe_note_held(audio_driver_state_t *st, size_t held)
{
size_t cur = retro_atomic_load_relaxed_size(&st->pipe_held_min1);
if (!cur || held + 1 < cur)
retro_atomic_store_release_size(&st->pipe_held_min1, held + 1);
}
static void audio_driver_pipe_note_wait(audio_driver_state_t *st,
retro_time_t began)
{
retro_time_t took = cpu_features_get_time_usec() - began;
retro_atomic_fetch_add_size(&st->pipe_source_waits, 1);
if (took > 0 && (size_t)took
> retro_atomic_load_relaxed_size(&st->pipe_source_wait_max_us))
retro_atomic_store_release_size(&st->pipe_source_wait_max_us,
(size_t)took);
}
static bool audio_driver_transport_wait(audio_driver_state_t *st, size_t need,
unsigned metadata_threshold)
{
@@ -6190,13 +6103,8 @@ static bool audio_driver_transport_wait(audio_driver_state_t *st, size_t need,
|| retro_atomic_load_acquire_int(&st->pipe_wake))
retro_eventcount_cancel_wait(&st->pipe_data);
else
{
retro_time_t began = st->pipe_priming ? 0 : cpu_features_get_time_usec();
retro_eventcount_commit_wait_timeout(&st->pipe_data, key,
AUDIO_PIPE_WAIT_MAX_US);
if (began)
audio_driver_pipe_note_wait(st, began);
}
if (retro_atomic_load_acquire_int(&st->pipe_wake))
{
retro_atomic_store_release_int(&st->pipe_wake, 0);
@@ -6229,28 +6137,26 @@ static void audio_driver_transport_consume(audio_driver_state_t *st)
size_t seen = audio->underruns(st->context_audio_data);
if (seen != st->pipe_underruns_seen)
{
size_t target = audio_driver_pipe_prime_frames(st);
size_t target = audio_driver_pipe_target_frames(st);
if (!audio_driver_pipeline_transport_discard(held > target ? held - target : 0))
return;
st->pipe_underruns_seen = seen;
held = retro_spsc_read_avail(&st->pipe_ring) / st->pipe_frame_bytes;
/* Silence with the pipe short is the core having been late,
* and the cushion the pipe held ahead of the device is what
* the stall burned. Nothing rebuilds it on this path - no
* rate control, and a core paced to the display never runs
* ahead - so from here every ordinary bit of jitter would be
* a hole. Prime again: the same wait the first pass made,
* while the device is silent anyway, so the next stall meets
* a full cushion. */
if (held < target)
st->pipe_priming = true;
}
}
if (st->pipe_priming)
{
size_t need = audio_driver_pipe_prime_frames(st);
if (!need)
need = 1;
size_t target = audio_driver_pipe_target_frames(st);
size_t need = 1;
if (target)
{
size_t room = st->pipe_ring.capacity / st->pipe_frame_bytes;
size_t device = (size_t)((double)st->buffer_size
/ audio_driver_dev_frame_bytes(st) / st->src_ratio_orig);
size_t limit = room > st->pipe_pass_frames ? room - st->pipe_pass_frames : 1;
need = target + device;
if (need > limit) need = limit;
}
if (!audio_driver_transport_wait(st, need * st->pipe_frame_bytes,
AUDIO_PIPELINE_LAYOUT_CAPACITY))
return;
@@ -6260,8 +6166,6 @@ static void audio_driver_transport_consume(audio_driver_state_t *st)
&& audio_pipeline_stretch_needs_input(st->pipe_transport)
&& !audio_driver_transport_wait(st, st->pipe_frame_bytes, 1))
return;
if (!st->pipe_priming)
audio_driver_pipe_note_held(st, held);
/* What a pause left in the ring is stale behind its tail, and so is
* what the stage holds: taken out unplayed, up to where the pause was
* published. */
@@ -6287,23 +6191,20 @@ static void audio_driver_transport_consume(audio_driver_state_t *st)
/* Never across the point the core's audio resumes at: there the
* stage drops the silence it holds and the ramp is armed for what
* it renders next. */
if (retro_atomic_load_acquire_int(&st->pipe_fade_in_set))
{
size_t at;
unsigned seq;
if (audio_driver_pipe_fade_pending(st, &at, &seq))
size_t at = retro_atomic_load_acquire_size(&st->pipe_fade_in_at);
if (audio_driver_pipe_ahead(st, tail, at))
{
if (audio_driver_pipe_ahead(st, tail, at))
{
size_t upto = (at - tail) / st->pipe_frame_bytes;
if (input_budget > upto)
input_budget = upto;
}
else
{
audio_driver_transport_discard(0, false);
st->pipe_arm_fade = true;
st->pipe_fade_in_seen = seq;
}
size_t upto = (at - tail) / st->pipe_frame_bytes;
if (input_budget > upto)
input_budget = upto;
}
else
{
audio_driver_transport_discard(0, false);
st->pipe_arm_fade = true;
retro_atomic_store_release_int(&st->pipe_fade_in_set, 0);
}
}
}
@@ -6370,9 +6271,18 @@ static void audio_driver_pipeline_consume(audio_driver_state_t *audio_st)
size_t need = audio_st->pipe_frame_bytes;
if (audio_st->pipe_priming)
{
size_t prime = audio_driver_pipe_prime_frames(audio_st);
if (prime)
need = prime * audio_st->pipe_frame_bytes;
size_t target = audio_driver_pipe_target_frames(audio_st);
if (target)
{
size_t frame_bytes = audio_driver_dev_frame_bytes(audio_st);
size_t device = (size_t)((double)audio_st->buffer_size / frame_bytes
/ audio_st->src_ratio_orig);
size_t room = audio_st->pipe_ring.capacity / audio_st->pipe_frame_bytes;
target += device;
if (target > room - audio_st->pipe_pass_frames)
target = room - audio_st->pipe_pass_frames;
need = target * audio_st->pipe_frame_bytes;
}
}
while (retro_spsc_read_avail(&audio_st->pipe_ring) < need)
{
@@ -6409,25 +6319,15 @@ static void audio_driver_pipeline_consume(audio_driver_state_t *audio_st)
retro_eventcount_cancel_wait(&audio_st->pipe_data);
continue;
}
if (!retro_eventcount_commit_wait_timeout(&audio_st->pipe_data, key,
AUDIO_PIPE_WAIT_MAX_US))
{
retro_time_t began = audio_st->pipe_priming
? 0 : cpu_features_get_time_usec();
bool woken = retro_eventcount_commit_wait_timeout(&audio_st->pipe_data,
key, AUDIO_PIPE_WAIT_MAX_US);
if (began)
audio_driver_pipe_note_wait(audio_st, began);
if (!woken)
{
/* Nothing came within the bound. A producer that has
* stopped is the wrapper's business, not this pass's. */
if (retro_atomic_load_acquire_int(&audio_st->pipe_data_gen) == gen)
return;
}
/* Nothing came within the bound. A producer that has stopped
* is the wrapper's business, not this pass's. */
if (retro_atomic_load_acquire_int(&audio_st->pipe_data_gen) == gen)
return;
}
}
if (!audio_st->pipe_priming)
audio_driver_pipe_note_held(audio_st,
retro_spsc_read_avail(&audio_st->pipe_ring) / audio_st->pipe_frame_bytes);
audio_st->pipe_priming = false;
}
@@ -6499,7 +6399,7 @@ static void audio_driver_pipeline_consume(audio_driver_state_t *audio_st)
size_t seen = audio->underruns(audio_st->context_audio_data);
if (seen != audio_st->pipe_underruns_seen)
{
size_t target = audio_driver_pipe_prime_frames(audio_st) * audio_st->pipe_frame_bytes;
size_t target = audio_driver_pipe_target_frames(audio_st) * audio_st->pipe_frame_bytes;
size_t held = retro_spsc_read_avail(&audio_st->pipe_ring);
bool discarded = false;
audio_st->pipe_underruns_seen = seen;
@@ -6521,17 +6421,6 @@ static void audio_driver_pipeline_consume(audio_driver_state_t *audio_st)
audio_driver_reset_resamplers(audio_st);
audio_driver_state_unlock();
}
/* Silence with the pipe short is the core having been late,
* and what the stall burned is the cushion the pipe held
* ahead of the device; nothing on this path rebuilds it.
* Prime again on the next pass - the wait the first pass
* made, while the device is silent anyway - so the next
* stall meets a full cushion rather than an empty pipe. */
if (held < target)
{
audio_st->pipe_priming = true;
return;
}
if (have > held / audio_st->pipe_frame_bytes)
have = held / audio_st->pipe_frame_bytes;
}
@@ -6577,22 +6466,19 @@ static void audio_driver_pipeline_consume(audio_driver_state_t *audio_st)
}
/* Never across the point the core's audio resumes at: the ramp is
* armed for the chunk that starts there. */
if (retro_atomic_load_acquire_int(&audio_st->pipe_fade_in_set))
{
size_t at;
unsigned seq;
if (audio_driver_pipe_fade_pending(audio_st, &at, &seq))
size_t at = retro_atomic_load_acquire_size(&audio_st->pipe_fade_in_at);
if (audio_driver_pipe_ahead(audio_st, tail, at))
{
if (audio_driver_pipe_ahead(audio_st, tail, at))
{
size_t upto = (at - tail) / audio_st->pipe_frame_bytes;
if (have > upto)
have = upto;
}
else
{
audio_st->pipe_arm_fade = true;
audio_st->pipe_fade_in_seen = seq;
}
size_t upto = (at - tail) / audio_st->pipe_frame_bytes;
if (have > upto)
have = upto;
}
else
{
audio_st->pipe_arm_fade = true;
retro_atomic_store_release_int(&audio_st->pipe_fade_in_set, 0);
}
}
}
@@ -6665,11 +6551,13 @@ static void audio_driver_record_push(audio_driver_state_t *audio_st,
capacity = frames > (AUDIO_CHUNK_SIZE_NONBLOCKING >> 1)
? (AUDIO_CHUNK_SIZE_NONBLOCKING >> 1) : frames;
need = capacity * (channels > rchannels ? channels : rchannels);
/* The region is carved out of arena_int16 at init for
* AUDIO_RECORD_REMAP_CHANNELS per frame; wider than that is not a
* layout either side can declare, so this is a check, not a grow. */
if (!audio_st->record_remap || need > audio_st->record_remap_frames)
return;
if (need > audio_st->record_remap_frames)
{
int16_t *nb = (int16_t*)realloc(audio_st->record_remap, need * 2 * sizeof(int16_t));
if (!nb) return;
audio_st->record_remap = nb;
audio_st->record_remap_frames = need;
}
while (frames)
{
size_t n = frames > capacity ? capacity : frames;
@@ -6845,11 +6733,18 @@ size_t audio_driver_sample_batch(const int16_t *data, size_t frames)
static bool audio_driver_multi_fold_room(audio_driver_state_t *audio_st,
size_t frames, size_t sample)
{
/* The region is carved out of arena_float by
* audio_driver_init_internal() at AUDIO_MULTI_FOLD_FRAMES frames;
* callers slice to that bound, so this is a check, never a grow. */
if (frames > audio_st->multi_fold_frames)
{
/* float and int16 batches share the buffer: sized for float,
* the larger */
void *n = realloc(audio_st->multi_fold, frames * 2 * sizeof(float));
if (!n)
return false;
audio_st->multi_fold = n;
audio_st->multi_fold_frames = frames;
}
(void)sample;
return audio_st->multi_fold && frames <= audio_st->multi_fold_frames;
return true;
}
static bool audio_driver_multi_layout_ok(unsigned channels, unsigned layout)
@@ -7871,7 +7766,6 @@ bool audio_driver_mixer_add_stream(audio_mixer_stream_params_t *params)
if (audio_driver_mixer_use_s16(audio_driver_st.stat_core_is_float))
voice = audio_mixer_play_s16(handle, looped,
GAIN_TO_Q16(params->volume),
audio_driver_st.resampler_ident,
audio_driver_st.resampler_quality, stop_cb);
else
voice = audio_mixer_play(handle, looped, params->volume,
@@ -7962,7 +7856,6 @@ static void audio_driver_mixer_play_stream_internal(
audio_mixer_play_s16(audio_driver_st.mixer_streams[i].handle,
(type == AUDIO_STREAM_STATE_PLAYING_LOOPED) ? true : false,
AUDIO_MIXER_GAIN_UNITY,
audio_driver_st.resampler_ident,
audio_driver_st.resampler_quality,
audio_driver_st.mixer_streams[i].stop_cb);
else
@@ -8408,7 +8301,7 @@ bool audio_driver_start(bool is_shutdown)
}
RARCH_DBG("[Audio] Started audio driver \"%s\" (is_shutdown=%s)\n",
audio_driver_get_ident(), is_shutdown ? "true" : "false");
audio->ident, is_shutdown ? "true" : "false");
return true;
@@ -8842,8 +8735,7 @@ bool audio_driver_stop(void)
audio_driver_state_unlock();
}
AUDIO_FLAGS_CLEAR(audio_st, AUDIO_FLAG_STARTED);
RARCH_DBG("[Audio] Stopped audio driver \"%s\".\n",
audio_driver_get_ident());
RARCH_DBG("[Audio] Stopped audio driver \"%s\".\n", audio->ident);
}
return stopped;
@@ -9012,7 +8904,7 @@ bool audio_compute_buffer_statistics(audio_statistics_t *stats)
}
#ifdef HAVE_MENU
void audio_driver_menu_sample(bool by_clock)
void audio_driver_menu_sample(void)
{
static int16_t samples_buf[1024] = {0};
settings_t *settings = config_get_ptr();
@@ -9038,40 +8930,10 @@ void audio_driver_menu_sample(bool by_clock)
|| info->sample_rate <= 0.0 || info->fps <= 0.0)
return;
frames = floor(info->sample_rate / info->fps);
if (by_clock)
{
/* What has played since the last feed, carrying the part of a
* frame that did not make a whole one. The first feed is one
* content frame; a gap longer than a tenth of a second is a
* stall or a return to the menu, not time to make up. */
retro_time_t now = cpu_features_get_time_usec();
if (audio_st->menu_feed_last)
{
retro_time_t elapsed = now - audio_st->menu_feed_last;
double owed;
if (elapsed < 0)
elapsed = 0;
else if (elapsed > 100000)
elapsed = 100000;
owed = info->sample_rate
* (double)elapsed / 1000000.0 + audio_st->menu_feed_frac;
frames = floor(owed);
audio_st->menu_feed_frac = owed - frames;
}
audio_st->menu_feed_last = now;
}
else
{
audio_st->menu_feed_last = 0;
audio_st->menu_feed_frac = 0.0;
}
/* Validate before conversion and the stereo sample-count multiply. */
if (!(frames >= 0.0 && frames <= UINT_MAX / 2))
return;
sample_count = (unsigned)frames * 2;
/* Less than a frame has played since the last feed */
if (by_clock && !sample_count)
return;
if ((AUDIO_FLAGS_GET(audio_st) & AUDIO_FLAG_SUSPENDED))
check_flush = false;
@@ -9560,12 +9422,32 @@ static bool mic_driver_open_mic_internal(retro_microphone_t* microphone)
&& microphone->resampler
&& microphone->resampler->short_ident)
{
retro_resampler_int16_t rs;
retro_resampler_int16_new(&rs, microphone->resampler->short_ident,
mic_st->resampler_quality, microphone->orig_ratio, false);
microphone->resampler_data_int16 = rs.data;
microphone->resampler_int16_process = rs.process;
microphone->resampler_int16_free = rs.free;
const char *rs_ident = microphone->resampler->short_ident;
if (string_is_equal(rs_ident, "sinc"))
{
microphone->resampler_data_int16 = sinc_resampler_int16_init(
microphone->orig_ratio,
audio_sinc_int16_quality_map(mic_st->resampler_quality));
microphone->resampler_int16_process = sinc_resampler_int16_process;
microphone->resampler_int16_free = sinc_resampler_int16_free;
}
#ifdef HAVE_NEAREST_RESAMPLER
else if (string_is_equal(rs_ident, "nearest"))
{
microphone->resampler_data_int16 = nearest_resampler_int16_init();
microphone->resampler_int16_process = nearest_resampler_int16_process;
microphone->resampler_int16_free = nearest_resampler_int16_free;
}
#endif
#ifdef HAVE_CC_RESAMPLER
else if (string_is_equal(rs_ident, "cc"))
{
microphone->resampler_data_int16 = cc_resampler_int16_init(
microphone->orig_ratio);
microphone->resampler_int16_process = cc_resampler_int16_process;
microphone->resampler_int16_free = cc_resampler_int16_free;
}
#endif
if (!microphone->resampler_data_int16)
{
microphone->resampler_int16_process = NULL;
@@ -9943,23 +9825,9 @@ static void microphone_driver_capture_thread(void *data)
continue;
}
/* What the device has now, which can be less than was asked for:
* a driver returns short whenever its bounded wait expires with
* something in hand, and alsa returns a period even when the
* slice spans several. read() blocks until it has everything it
* was asked for, so reading the whole slice on a short count
* parks the worker inside the driver - and teardown joins this
* thread unconditionally. Read what is there. */
{
size_t want = slice * sample_size;
size_t got = mic_st->driver->wait_readable(mic_st->driver_context,
microphone->microphone_context, want);
if (got < sample_size)
continue;
if (got < want)
slice = got / sample_size;
}
if (!mic_st->driver->wait_readable(mic_st->driver_context,
microphone->microphone_context, slice * sample_size))
continue;
/* The flush - device read, up-channel, resample, ring write -
* runs without the lock: SPSC, this thread is the producer. The
+14 -52
View File
@@ -31,10 +31,12 @@
* calls into it - so the type has to exist there even though nothing
* uses it. */
#include <rthreads/retro_eventcount.h>
/* Declarations only, with or without threads: the state holds lock and
* condition pointers either way. Declaring the types here instead would
* repeat typedefs a C89 compiler refuses once a file includes both. */
#ifdef HAVE_THREADS
#include <rthreads/rthreads.h>
#else
typedef struct slock slock_t;
typedef struct scond scond_t;
#endif
#include <retro_inline.h>
#include <libretro.h>
#include <retro_miscellaneous.h>
@@ -411,12 +413,6 @@ typedef struct audio_driver
bool (*device_clock_ppm)(void *data, double *ppm);
} audio_driver_t;
/* What a driver's device-clock word holds until it has an estimate.
* Every estimate is kept inside +-100000 ppm, so the estimate and
* whether there is one share one int: one store publishes, one load
* reads, and no reader can pair a flag with a stale value. */
#define AUDIO_CLOCK_PPM_NONE (-0x7FFFFFFF - 1)
/* The layout the user asked for, one of the AUDIO_LAYOUT_ masks. A
* driver opening a device reads this; a driver without the layout
* hook is not asked. */
@@ -550,10 +546,8 @@ typedef struct
/* Written once by the wrapper thread as it leaves its loop, read by
* the producer's wait. Its own field, not a bit in flags: the main
* thread read-modify-writes flags and a second writer would lose
* bits. An atomic and not a volatile bool: volatile orders nothing
* between the two, so the producer had no guarantee of seeing the
* consumer's last word at all. */
retro_atomic_int_t pipe_consumer_gone;
* bits. */
volatile bool pipe_consumer_gone;
/* Throttle channel for audio_sync without vsync: the consumer bumps
* pipe_gen after every pass and notifies pipe_space; a producer that
* found the ring full waits for the generation to change. No lock:
@@ -691,15 +685,6 @@ typedef struct
* pipe's target then, not just a frame. Consumer thread only after
* init. */
bool pipe_priming;
/* How the consumer fared against the core after priming, said once
* at teardown beside the driver's silence count: the passes that
* found the pipe short and had to wait for the core, the longest
* such wait, and the least the pipe held at the start of a pass
* (kept as frames + 1, so zero is no pass yet). Consumer writes,
* the main thread reads at teardown. */
retro_atomic_size_t pipe_source_waits;
retro_atomic_size_t pipe_source_wait_max_us;
retro_atomic_size_t pipe_held_min1;
/* The audio thread's own copy of AUDIO_FLAG_PIPELINE_THREADED. Set
* before the wrapper thread is released and cleared after it is
* joined, so the thread never reads the flags word - which the main
@@ -769,13 +754,6 @@ typedef struct
retro_time_t avg_flush_delta;
double avg_expected_delta;
/* The menu's silence at Menu Frame Rate 'Display Rate' is measured
* out by the clock rather than by the content's frame: when it was
* last fed (0 = not feeding by the clock), and the fraction of a
* frame the last feed left owing. */
retro_time_t menu_feed_last;
double menu_feed_frac;
/* Rate-limit state for the DRC compute.
*
* The DRC ratio is updated approximately once per game-frame's worth
@@ -917,7 +895,7 @@ typedef struct
unsigned out_channels;
/* The multi-channel batch entry (RETRO_ENVIRONMENT_GET_AUDIO_
* SAMPLE_BATCH_MULTI): the layout the core last delivered, and
* the stereo fold of a batch slice, a fixed region of arena_float. The
* the stereo fold of a batch, grown to the largest batch seen. The
* pipeline carries stereo; a core's wider frame is folded here at
* the boundary, and the device's upmix widens the stereo again.
* core_layout is stereo until the core delivers something else,
@@ -1002,16 +980,12 @@ typedef struct
* has been published since. The core's first audio after a resume
* starts at pipe_fade_in_at: the consumer never takes a chunk across
* it and arms the resume ramp on reaching it, through pipe_arm_fade,
* consumer thread only. pipe_fade_in_mark is (sequence << 1) | armed,
* written by the main thread alone; the consumer records the sequence
* it has acted on in pipe_fade_in_seen instead of clearing the mark,
* so a mark published while it reads is never cleared unseen. */
* consumer thread only. */
retro_atomic_size_t pipe_discard_to;
retro_atomic_int_t pipe_discard_gen;
int pipe_discard_seen;
retro_atomic_size_t pipe_fade_in_at;
retro_atomic_int_t pipe_fade_in_mark;
unsigned pipe_fade_in_seen;
retro_atomic_int_t pipe_fade_in_set;
bool pipe_arm_fade;
uint8_t pipe_transport_follow;
/* Mutually exclusive with pipe_transport; shares its output storage. */
@@ -1416,18 +1390,7 @@ size_t audio_driver_sample_batch_rewind(
#endif
#ifdef HAVE_MENU
/**
* audio_driver_menu_sample:
* @by_clock : feed the silence the clock says has played since the last
* feed, rather than one content frame of it.
*
* Feeds the device silence while the core is not running behind the
* menu, with the menu sounds, the mixer and thumbnail audio mixed in.
* One content frame per call holds a blocking writer to the content's
* rate; by the clock, with a non-blocking writer, the device is kept fed
* at whatever rate the menu runs.
**/
void audio_driver_menu_sample(bool by_clock);
void audio_driver_menu_sample(void);
#endif
extern audio_driver_t audio_rsound;
@@ -1439,8 +1402,7 @@ extern audio_driver_t audio_roar;
extern audio_driver_t audio_openal;
extern audio_driver_t audio_opensl;
extern audio_driver_t audio_jack;
extern audio_driver_t audio_sdl1;
extern audio_driver_t audio_sdl2;
extern audio_driver_t audio_sdl;
extern audio_driver_t audio_sdl3;
extern audio_driver_t audio_xa;
extern audio_driver_t audio_pulse;
@@ -1463,11 +1425,11 @@ extern audio_driver_t audio_ps3;
extern audio_driver_t audio_gx;
extern audio_driver_t audio_ax;
extern audio_driver_t audio_psp;
extern audio_driver_t audio_psp2;
extern audio_driver_t audio_ps4;
extern audio_driver_t audio_ps2;
extern audio_driver_t audio_ctr_csnd;
extern audio_driver_t audio_ctr_dsp;
#ifdef HAVE_THREADS
#endif
extern audio_driver_t audio_switch;
extern audio_driver_t audio_switch_libnx_audren;
extern audio_driver_t audio_rwebaudio;
+125 -190
View File
@@ -18,15 +18,12 @@
#include <string.h>
#include <lists/string_list.h>
#include <retro_atomic.h>
#include <rthreads/retro_eventcount.h>
#include <rthreads/rthreads.h>
#include <features/features_cpu.h>
#include "audio_thread_wrapper.h"
#include "audio_driver.h"
#include "../verbosity.h"
#include "../frontend/thread_elevation.h"
/* How long a handshake between the main thread and the audio thread
* may run before it is reported. Both are sub-millisecond on a device
@@ -46,48 +43,29 @@
* request signals the condition and cuts the wait short. */
#define AUDIO_THREAD_IDLE_WAIT_US 1000
enum audio_thread_init_state
{
AUDIO_THREAD_INIT_PENDING = 0,
AUDIO_THREAD_INIT_OK,
AUDIO_THREAD_INIT_FAILED,
AUDIO_THREAD_INIT_ABANDONED
};
typedef struct audio_thread
{
const audio_driver_t *driver;
void *driver_data;
sthread_t *thread;
slock_t *lock;
scond_t *cond;
const char *device;
unsigned *new_rate;
/* Both threads park here: the audio thread between passes it has
* nothing to do on and while stopped, the main thread for the
* stop acknowledgement and for init() to return. Every change to
* the words below is written first and notified after, so a waiter's
* re-check between prepare and commit sees it. */
retro_eventcount_t ec;
/* The thread's two loop conditions, tested on every pass without
* any lock. alive only ever goes false. */
retro_atomic_int_t alive;
retro_atomic_int_t stopped;
/* The thread is parked in a stopped loop and the driver is stopped;
* block() waits for it. */
retro_atomic_int_t stopped_ack;
/* How init() ended, and who owns this struct: the thread moves it
* from PENDING to OK or FAILED, the frontend from PENDING to
* ABANDONED when it gives up waiting. One compare-and-swap each, so
* exactly one side wins - the other reads what happened and acts on
* that. An abandoned thread frees the struct itself when init()
* finally returns. */
retro_atomic_int_t init_state;
int inited;
/* The frontend stopped waiting for init(): this thread owns its own
* state from there and frees it when init() finally returns. */
bool abandoned;
/* Initialization options. */
unsigned out_rate;
unsigned latency;
bool alive;
bool stopped;
bool stopped_ack;
bool is_paused;
bool is_shutdown;
bool use_float;
@@ -99,38 +77,6 @@ typedef struct audio_thread
bool prefer_fast_cores;
} audio_thread_t;
/* Audio thread: parks while stopped, acknowledging the stop so block()
* can return. block() clears the ack before it raises the flag, so an
* ack found clear inside the wait window means a start and a second
* stop landed since this pass acknowledged, and that stop is answered
* before parking. The ack is stored and notified before the window,
* never inside it: a notify from inside would cut the thread's own
* wait short. */
static void audio_thread_park_stopped(audio_thread_t *thr)
{
for (;;)
{
int key;
if (!retro_atomic_load_acquire_int(&thr->stopped))
return;
retro_atomic_store_release_int(&thr->stopped_ack, 1);
retro_eventcount_notify(&thr->ec);
key = retro_eventcount_prepare_wait(&thr->ec);
if (!retro_atomic_load_acquire_int(&thr->stopped))
{
retro_eventcount_cancel_wait(&thr->ec);
return;
}
if (!retro_atomic_load_acquire_int(&thr->stopped_ack))
{
retro_eventcount_cancel_wait(&thr->ec);
continue;
}
retro_eventcount_commit_wait(&thr->ec, key);
}
}
/**
* The thread that manages the life of the audio driver.
* The wrapped audio driver lives and dies with this function.
@@ -140,31 +86,15 @@ static void audio_thread_loop(void *data)
bool is_shutdown;
audio_thread_t *thr = (audio_thread_t*)data;
if (!thr)
return;
sthread_setname("ra-audio");
/* Best effort and never fatal: a refusal leaves the default. */
if (thr->raise_priority)
{
const char *via = NULL;
const char *added = NULL;
switch (thread_elevation_raise_current(&via, &added))
{
case THREAD_ELEVATION_GRANTED:
RARCH_LOG("[Audio] Audio thread priority raised.\n");
break;
case THREAD_ELEVATION_PENDING:
/* Finishing on a thread of its own; this one does not wait. */
RARCH_LOG("[Audio] Audio thread priority not raised directly; asking %s.\n", via);
break;
default:
RARCH_LOG("[Audio] Audio thread priority not raised; the system refused or has no such class.\n");
break;
}
if (added)
RARCH_LOG("[Audio] Audio thread runs on %s.\n", added);
if (sthread_raise_current_priority())
RARCH_LOG("[Audio] Audio thread priority raised.\n");
else
RARCH_LOG("[Audio] Audio thread priority not raised; the system refused or has no such class.\n");
}
if (thr->prefer_fast_cores)
@@ -173,32 +103,38 @@ static void audio_thread_loop(void *data)
RARCH_LOG("[Audio] Audio thread placed on the performance cores.\n");
}
if (!thr)
return;
thr->driver_data = thr->driver->init(
thr->device, thr->out_rate, thr->latency,
thr->new_rate);
/* Read by the frontend once it sees the outcome below */
thr->use_float = false;
if (thr->driver_data && thr->driver->use_float)
slock_lock(thr->lock);
thr->inited = thr->driver_data ? 1 : -1;
if (thr->inited > 0 && thr->driver->use_float)
thr->use_float = thr->driver->use_float(thr->driver_data);
thr->layout = AUDIO_LAYOUT_STEREO;
if (thr->driver_data && thr->driver->layout)
if (thr->inited > 0 && thr->driver->layout)
thr->layout = thr->driver->layout(thr->driver_data);
/* Publish the outcome, unless the frontend gave up waiting first:
* then nobody is waiting for this any more and nobody else will
* free it. */
if (!retro_atomic_cas_int(&thr->init_state, AUDIO_THREAD_INIT_PENDING,
thr->driver_data ? AUDIO_THREAD_INIT_OK : AUDIO_THREAD_INIT_FAILED))
scond_signal(thr->cond);
{
if (thr->driver_data && thr->driver->free)
thr->driver->free(thr->driver_data);
retro_eventcount_free(&thr->ec);
free(thr);
return;
}
retro_eventcount_notify(&thr->ec);
bool abandoned = thr->abandoned;
slock_unlock(thr->lock);
if (!thr->driver_data)
/* Nobody is waiting for this any more, and nobody else will free
* it: init() took longer than the frontend was willing to wait. */
if (abandoned)
{
if (thr->driver_data && thr->driver->free)
thr->driver->free(thr->driver_data);
slock_free(thr->lock);
scond_free(thr->cond);
free(thr);
return;
}
}
if (thr->inited < 0)
return;
/* Wait until we start to avoid calling
@@ -208,8 +144,15 @@ static void audio_thread_loop(void *data)
* this loop rather than the one below. block() waits for the
* acknowledgement whichever loop the thread is in, so this one
* gives it too. */
audio_thread_park_stopped(thr);
slock_lock(thr->lock);
while (thr->stopped)
{
thr->stopped_ack = true;
scond_signal(thr->cond);
scond_wait(thr->cond, thr->lock);
}
is_shutdown = thr->is_shutdown;
slock_unlock(thr->lock);
/* The loop below only calls the driver's start() when it comes out
* of a stop; the initial start has to be made here, on this thread,
@@ -218,37 +161,41 @@ static void audio_thread_loop(void *data)
for (;;)
{
/* Tested without the lock: a pass that is neither leaving nor
* parking has nothing to say to the main thread, and that is
* every pass while audio is playing. A request that lands just
* after either test is taken on the next pass, which is where it
* was taken before. */
if (!retro_atomic_load_acquire_int(&thr->alive))
slock_lock(thr->lock);
if (!thr->alive)
{
retro_atomic_store_release_int(&thr->stopped_ack, 1);
retro_eventcount_notify(&thr->ec);
scond_signal(thr->cond);
thr->stopped_ack = true;
slock_unlock(thr->lock);
break;
}
if (retro_atomic_load_acquire_int(&thr->stopped))
if (thr->stopped)
{
thr->driver->stop(thr->driver_data);
audio_thread_park_stopped(thr);
while (thr->stopped)
{
/* If we stop right after start,
* we might not be able to properly ack.
* Signal in the loop instead. */
thr->stopped_ack = true;
scond_signal(thr->cond);
scond_wait(thr->cond, thr->lock);
}
thr->driver->start(thr->driver_data, thr->is_shutdown);
}
slock_unlock(thr->lock);
if (!audio_driver_callback())
{
/* The re-check between prepare and commit is what keeps a
* request that lands here from going unseen until the
* timeout */
int key = retro_eventcount_prepare_wait(&thr->ec);
if ( retro_atomic_load_acquire_int(&thr->alive)
&& !retro_atomic_load_acquire_int(&thr->stopped))
retro_eventcount_commit_wait_timeout(&thr->ec, key,
slock_lock(thr->lock);
if (thr->alive && !thr->stopped)
scond_wait_timeout(thr->cond, thr->lock,
AUDIO_THREAD_IDLE_WAIT_US);
else
retro_eventcount_cancel_wait(&thr->ec);
slock_unlock(thr->lock);
}
}
@@ -265,19 +212,23 @@ static void audio_thread_block(audio_thread_t *thr)
if (!thr)
return;
if (retro_atomic_load_acquire_int(&thr->stopped))
if (thr->stopped)
return;
slock_lock(thr->lock);
/* alive goes false when the thread is told to leave its loop, or
* leaves it on its own after a failed write. Either way it will not
* acknowledge anything again, and the wait below would never end.
* There is nothing running to park, so there is nothing to wait
* for. */
if (!retro_atomic_load_acquire_int(&thr->alive))
if (!thr->alive)
{
slock_unlock(thr->lock);
return;
retro_atomic_store_release_int(&thr->stopped_ack, 0);
retro_atomic_store_release_int(&thr->stopped, 1);
retro_eventcount_notify(&thr->ec);
}
thr->stopped_ack = false;
thr->stopped = true;
scond_signal(thr->cond);
/* The thread may be asleep in the pipeline waiting for data; wake it
* so it comes back to the loop and acknowledges now rather than
* after its timeout. */
@@ -287,22 +238,12 @@ static void audio_thread_block(audio_thread_t *thr)
* abandoned: callers past it act as though the thread is parked,
* and free() joins the thread either way, so a device that never
* returns from a write still stops here. It is reported, so the log
* names what the frontend is waiting on. A thread that leaves its
* loop meanwhile acks on the way out. */
* names what the frontend is waiting on. */
{
bool warned = false;
for (;;)
while (!thr->stopped_ack)
{
int key;
if (retro_atomic_load_acquire_int(&thr->stopped_ack))
break;
key = retro_eventcount_prepare_wait(&thr->ec);
if (retro_atomic_load_acquire_int(&thr->stopped_ack))
{
retro_eventcount_cancel_wait(&thr->ec);
break;
}
if (retro_eventcount_commit_wait_timeout(&thr->ec, key,
if (scond_wait_timeout(thr->cond, thr->lock,
AUDIO_THREAD_HANDSHAKE_WARN_US))
continue;
if (!warned)
@@ -314,6 +255,8 @@ static void audio_thread_block(audio_thread_t *thr)
}
}
}
slock_unlock(thr->lock);
}
/**
@@ -325,8 +268,10 @@ static void audio_thread_unblock(audio_thread_t *thr)
if (!thr)
return;
retro_atomic_store_release_int(&thr->stopped, 0);
retro_eventcount_notify(&thr->ec);
slock_lock(thr->lock); /* Prevent the audio thread from touching this flag... */
thr->stopped = false; /* ...so that the main thread can do it. */
scond_signal(thr->cond); /* Then let the audio thread know that it's okay to resume. */
slock_unlock(thr->lock); /* "As you were." */
}
static void audio_thread_free(void *data)
@@ -338,11 +283,11 @@ static void audio_thread_free(void *data)
if (thr->thread)
{
/* Let the thread out of whichever wait it is in; it leaves its
* loop on seeing alive false. */
retro_atomic_store_release_int(&thr->stopped, 0);
retro_atomic_store_release_int(&thr->alive, 0);
retro_eventcount_notify(&thr->ec);
slock_lock(thr->lock); /* Let the audio thread finish what it's doing... */
thr->stopped = false; /* Then stop it. "You're fired." */
thr->alive = false;
scond_signal(thr->cond); /* Let the thread know it's okay to continue */
slock_unlock(thr->lock); /* At this point, it will exit its loop. */
/* It may be asleep in the pipeline waiting for data; wake it so
* it sees alive == false now rather than after its timeout. */
@@ -352,7 +297,10 @@ static void audio_thread_free(void *data)
* (It will call the wrapped driver's free() function.) */
}
retro_eventcount_free(&thr->ec);
if (thr->lock)
slock_free(thr->lock);
if (thr->cond)
scond_free(thr->cond);
free(thr);
/* The audio driver is done, clean up the thread itself. */
}
@@ -368,7 +316,7 @@ static bool audio_thread_alive(void *data)
/* A thread that has ended after a failed write reports the device
* as not alive, which is what it is; block() below is a no-op then,
* and the answer has to come from somewhere. */
if (!retro_atomic_load_acquire_int(&thr->alive))
if (!thr->alive)
return false;
audio_thread_block(thr);
@@ -385,7 +333,9 @@ void audio_thread_apply_control(void *data,
bool running;
if (!thr || !control)
return;
running = !retro_atomic_load_acquire_int(&thr->stopped);
slock_lock(thr->lock);
running = !thr->stopped;
slock_unlock(thr->lock);
audio_thread_block(thr);
control(userdata);
if (running)
@@ -419,10 +369,10 @@ static bool audio_thread_start(void *data, bool is_shutdown)
audio_driver_enable_callback();
/* Written before the release store that clears stopped, which the
* thread acquire-loads before it reads is_shutdown */
slock_lock(thr->lock);
thr->is_paused = false;
thr->is_shutdown = is_shutdown;
slock_unlock(thr->lock);
audio_thread_unblock(thr);
return true;
@@ -540,8 +490,10 @@ static ssize_t audio_thread_write(void *data, const void *s, size_t len)
_len = thr->driver->write(thr->driver_data, s, len);
if (_len < 0)
{
retro_atomic_store_release_int(&thr->alive, 0);
retro_eventcount_notify(&thr->ec);
slock_lock(thr->lock);
thr->alive = false;
scond_signal(thr->cond);
slock_unlock(thr->lock);
}
return _len;
}
@@ -648,13 +600,13 @@ bool audio_init_thread(const audio_driver_t **out_driver,
thr->new_rate = new_rate;
thr->latency = latency;
if (!retro_eventcount_init(&thr->ec))
if (!(thr->cond = scond_new()))
goto error;
if (!(thr->lock = slock_new()))
goto error;
retro_atomic_int_init(&thr->alive, 1);
retro_atomic_int_init(&thr->stopped, 1);
retro_atomic_int_init(&thr->stopped_ack, 0);
retro_atomic_int_init(&thr->init_state, AUDIO_THREAD_INIT_PENDING);
thr->alive = true;
thr->stopped = true;
if (!(thr->thread = sthread_create(audio_thread_loop, thr)))
goto error;
@@ -665,44 +617,27 @@ bool audio_init_thread(const audio_driver_t **out_driver,
* with no way out. Past the deadline the thread is told it owns its
* own state and this returns without it - freeing it here would pull
* it out from under a thread still using it. */
slock_lock(thr->lock);
{
bool warned = false;
retro_time_t giveup = cpu_features_get_time_usec()
+ AUDIO_THREAD_HANDSHAKE_GIVEUP_US;
for (;;)
while (!thr->inited)
{
int key;
retro_time_t now;
if (retro_atomic_load_acquire_int(&thr->init_state)
!= AUDIO_THREAD_INIT_PENDING)
break;
now = cpu_features_get_time_usec();
retro_time_t now = cpu_features_get_time_usec();
if (now >= giveup)
{
/* The thread may publish in this same instant: the
* exchange decides, and a lost one means init() has just
* returned and the state is this thread's after all. */
if (retro_atomic_cas_int(&thr->init_state,
AUDIO_THREAD_INIT_PENDING, AUDIO_THREAD_INIT_ABANDONED))
{
RARCH_ERR("[Audio] Driver \"%s\" did not return from init after %d seconds; going on without audio.\n",
thr->driver->ident ? thr->driver->ident : "?",
(int)(AUDIO_THREAD_HANDSHAKE_GIVEUP_US / 1000000));
sthread_detach(thr->thread);
*out_driver = NULL;
*out_data = NULL;
return false;
}
continue;
thr->abandoned = true;
slock_unlock(thr->lock);
RARCH_ERR("[Audio] Driver \"%s\" did not return from init after %d seconds; going on without audio.\n",
thr->driver->ident ? thr->driver->ident : "?",
(int)(AUDIO_THREAD_HANDSHAKE_GIVEUP_US / 1000000));
sthread_detach(thr->thread);
*out_driver = NULL;
*out_data = NULL;
return false;
}
key = retro_eventcount_prepare_wait(&thr->ec);
if (retro_atomic_load_acquire_int(&thr->init_state)
!= AUDIO_THREAD_INIT_PENDING)
{
retro_eventcount_cancel_wait(&thr->ec);
break;
}
if (retro_eventcount_commit_wait_timeout(&thr->ec, key,
if (scond_wait_timeout(thr->cond, thr->lock,
AUDIO_THREAD_HANDSHAKE_WARN_US))
continue;
if (!warned)
@@ -714,9 +649,9 @@ bool audio_init_thread(const audio_driver_t **out_driver,
}
}
}
slock_unlock(thr->lock);
if (retro_atomic_load_acquire_int(&thr->init_state)
!= AUDIO_THREAD_INIT_OK) /* Thread failed. */
if (thr->inited < 0) /* Thread failed. */
goto error;
*out_driver = &audio_thread;
+114 -115
View File
@@ -29,7 +29,6 @@
#include <stdlib.h>
#include <lists/string_list.h>
#include <retro_atomic.h>
#ifdef HAVE_CONFIG_H
#include "../../config.h"
@@ -61,25 +60,37 @@ typedef struct alsa_microphone_handle
alsa_stream_info_t stream_info;
} alsa_microphone_handle_t;
/* The microphone driver context carries nothing of its own: what
* init() and free() set up and tear down is ALSA's global state, and
* the frontend only needs a handle to hand back. The driver itself is
* that handle. */
extern microphone_driver_t microphone_alsa;
typedef struct alsa_microphone
{
bool nonblock;
} alsa_microphone_t;
static void *alsa_microphone_init(void)
{
alsa_microphone_t *alsa = (alsa_microphone_t*)calloc(1, sizeof(alsa_microphone_t));
if (!alsa)
{
RARCH_ERR("[ALSA] Failed to allocate driver context.\n");
return NULL;
}
RARCH_LOG("[ALSA] Using ALSA version %s.\n", snd_asoundlib_version());
return (void*)&microphone_alsa;
return alsa;
}
static void alsa_microphone_close_mic(void *driver_context, void *mic_context);
static void alsa_microphone_free(void *driver_context)
{
alsa_microphone_t *alsa = (alsa_microphone_t*)driver_context;
/* The mic frontend should've closed all mics before calling free(). */
if (driver_context)
if (alsa)
{
snd_config_update_free_global();
free(alsa);
}
}
static bool alsa_microphone_start_mic(void *driver_context, void *mic_context);
@@ -106,15 +117,13 @@ static int alsa_microphone_read(void *driver_context, void *mic_context, void *s
{
snd_pcm_sframes_t size;
snd_pcm_state_t state;
alsa_microphone_t *alsa = (alsa_microphone_t*)driver_context;
alsa_microphone_handle_t *mic = (alsa_microphone_handle_t*)mic_context;
uint8_t *buf = (uint8_t*)s;
snd_pcm_sframes_t read = 0;
int errnum = 0;
bool eagain_retry = true;
int laps = ALSA_WAIT_READABLE_LAPS;
int timeout_ms;
if (!driver_context || !mic || !buf)
if (!alsa || !mic || !buf)
return -1;
size = BYTES_TO_FRAMES(len, mic->stream_info.frame_bits);
@@ -137,55 +146,88 @@ static int alsa_microphone_read(void *driver_context, void *mic_context, void *s
}
}
timeout_ms = alsa_microphone_wait_ms(mic);
while (size)
if (alsa->nonblock)
{
snd_pcm_sframes_t frames;
int rc = snd_pcm_wait(mic->pcm, timeout_ms);
/* Nothing delivered in the time two periods take. A device that
* has stopped must not park the caller, so give up after a
* bounded number of these and return what there is. */
if (rc == 0)
while (size)
{
if (--laps < 0)
break;
continue;
}
snd_pcm_sframes_t frames = snd_pcm_readi(mic->pcm, buf, size);
if (rc == -EPIPE || rc == -ESTRPIPE || rc == -EINTR)
{
if (snd_pcm_recover(mic->pcm, rc, 1) < 0)
return -1;
continue;
}
frames = snd_pcm_readi(mic->pcm, buf, size);
if (frames == -EPIPE || frames == -EINTR || frames == -ESTRPIPE)
{
if (snd_pcm_recover(mic->pcm, frames, 1) < 0)
return -1;
break;
}
else if (frames == -EAGAIN)
{
/* Definitely not supposed to happen. */
if (eagain_retry)
if (frames == -EPIPE || frames == -EINTR || frames == -ESTRPIPE)
{
eagain_retry = false;
errnum = snd_pcm_recover(mic->pcm, frames, 0);
if (errnum < 0)
{
RARCH_ERR("[ALSA] Failed to read from microphone: %s.\n", snd_strerror(frames));
RARCH_ERR("[ALSA] Additionally, recovery failed with: %s.\n", snd_strerror(errnum));
return -1;
}
break;
}
else if (frames == -EAGAIN)
break;
else if (frames < 0)
return -1;
read += frames;
buf += FRAMES_TO_BYTES(frames, mic->stream_info.frame_bits);
size -= frames;
}
}
else
{
bool eagain_retry = true;
int laps = ALSA_WAIT_READABLE_LAPS;
int timeout_ms = alsa_microphone_wait_ms(mic);
while (size)
{
snd_pcm_sframes_t frames;
int rc = snd_pcm_wait(mic->pcm, timeout_ms);
/* Nothing delivered in the time two periods take. A device that
* has stopped must not park the caller, so give up after a
* bounded number of these and return what there is. */
if (rc == 0)
{
if (--laps < 0)
break;
continue;
}
break;
}
else if (frames < 0)
return -1;
read += frames;
buf += FRAMES_TO_BYTES(frames, mic->stream_info.frame_bits);
size -= frames;
if (rc == -EPIPE || rc == -ESTRPIPE || rc == -EINTR)
{
if (snd_pcm_recover(mic->pcm, rc, 1) < 0)
return -1;
continue;
}
frames = snd_pcm_readi(mic->pcm, buf, size);
if (frames == -EPIPE || frames == -EINTR || frames == -ESTRPIPE)
{
if (snd_pcm_recover(mic->pcm, frames, 1) < 0)
return -1;
break;
}
else if (frames == -EAGAIN)
{
/* Definitely not supposed to happen. */
if (eagain_retry)
{
eagain_retry = false;
continue;
}
break;
}
else if (frames < 0)
return -1;
read += frames;
buf += FRAMES_TO_BYTES(frames, mic->stream_info.frame_bits);
size -= frames;
}
}
return FRAMES_TO_BYTES(read, mic->stream_info.frame_bits);
@@ -202,6 +244,12 @@ static bool alsa_microphone_mic_alive(const void *driver_context, const void *mi
return snd_pcm_state(mic->pcm) == SND_PCM_STATE_RUNNING;
}
static void alsa_microphone_set_nonblock_state(void *driver_context, bool nonblock)
{
alsa_microphone_t *alsa = (alsa_microphone_t*)driver_context;
alsa->nonblock = nonblock;
}
static struct string_list *alsa_microphone_device_list_new(const void *data)
{
return alsa_device_list_type_new("Input");
@@ -219,9 +267,10 @@ static void *alsa_microphone_open_mic(void *driver_context,
unsigned latency,
unsigned *new_rate)
{
alsa_microphone_t *alsa = (alsa_microphone_t*)driver_context;
alsa_microphone_handle_t *mic = NULL;
if (!driver_context) /* If we weren't given a valid ALSA context... */
if (!alsa) /* If we weren't given a valid ALSA context... */
return NULL;
/* If the microphone context couldn't be allocated... */
@@ -238,7 +287,7 @@ static void *alsa_microphone_open_mic(void *driver_context,
error:
RARCH_ERR("[ALSA] Failed to initialize microphone.\n");
alsa_microphone_close_mic(driver_context, mic);
alsa_microphone_close_mic(alsa, mic);
return NULL;
@@ -351,6 +400,7 @@ microphone_driver_t microphone_alsa = {
alsa_microphone_init,
alsa_microphone_free,
alsa_microphone_read,
alsa_microphone_set_nonblock_state,
"alsa",
alsa_microphone_device_list_new,
alsa_microphone_device_list_free,
@@ -372,10 +422,6 @@ typedef struct alsa
/* Frames snd_pcm_writei() accepted since open; less what the device
* still holds, it is what the device has consumed. */
uint64_t frames_written;
/* Xruns the device reported on the write path: -EPIPE is the
* device having run out of audio, which -EINTR and -ESTRPIPE
* beside it are not. */
retro_atomic_size_t underruns;
bool nonblock;
/* Stopped, as the frontend sees it: alive() is its inverse. Held
* says how: the stream paused with its buffer kept, or dropped,
@@ -420,20 +466,8 @@ typedef struct alsa
double clk_a_sx, clk_a_sy, clk_a_sxx, clk_a_sxy, clk_a_n;
int clk_a_ppm;
int clk_a_valid;
/* Raised wherever the device stops and starts again - a pause, a
* resume, an xrun recovery - from whichever thread that happens on.
* Its position stood still meanwhile while time did not, so both
* fits start again from the writer's next sample; the last
* published estimate stands until the new window publishes one. */
retro_atomic_int_t clk_rebase;
} alsa_t;
static int alsa_recover(alsa_t *alsa, int err)
{
retro_atomic_store_release_int(&alsa->clk_rebase, 1);
return snd_pcm_recover(alsa->pcm, err, 1);
}
/* The layout the device actually has, from its channel map; the
* requested one where the map could not be read; stereo where the
* device would not open wider. */
@@ -515,7 +549,6 @@ static bool alsa_start(void *data, bool is_shutdown)
}
alsa->is_paused = false;
alsa->held = false;
retro_atomic_store_release_int(&alsa->clk_rebase, 1);
return true;
}
@@ -540,9 +573,7 @@ static ssize_t alsa_write(void *data, const void *buf_, size_t len)
if (frames == -EPIPE || frames == -EINTR || frames == -ESTRPIPE)
{
if (frames == -EPIPE)
retro_atomic_fetch_add_size(&alsa->underruns, 1);
if (alsa_recover(alsa, frames) < 0)
if (snd_pcm_recover(alsa->pcm, frames, 1) < 0)
return -1;
break;
@@ -580,9 +611,7 @@ static ssize_t alsa_write(void *data, const void *buf_, size_t len)
if (frames == -EPIPE || frames == -EINTR || frames == -ESTRPIPE)
{
if (frames == -EPIPE)
retro_atomic_fetch_add_size(&alsa->underruns, 1);
if (alsa_recover(alsa, frames) < 0)
if (snd_pcm_recover(alsa->pcm, frames, 1) < 0)
return -1;
break;
}
@@ -594,7 +623,7 @@ static ssize_t alsa_write(void *data, const void *buf_, size_t len)
rc = snd_pcm_wait(alsa->pcm, wait_ms);
if (rc == -EPIPE || rc == -ESTRPIPE || rc == -EINTR)
{
if (alsa_recover(alsa, rc) < 0)
if (snd_pcm_recover(alsa->pcm, rc, 1) < 0)
return -1;
}
continue;
@@ -633,7 +662,6 @@ static bool alsa_stop(void *data)
{
alsa->is_paused = true;
alsa->held = true;
retro_atomic_store_release_int(&alsa->clk_rebase, 1);
return true;
}
ret = snd_pcm_drop(alsa->pcm);
@@ -644,7 +672,6 @@ static bool alsa_stop(void *data)
}
alsa->is_paused = true;
alsa->held = false;
retro_atomic_store_release_int(&alsa->clk_rebase, 1);
return true;
}
@@ -743,7 +770,7 @@ static size_t alsa_wait_writable(void *data, size_t len)
if (avail == -EPIPE || avail == -ESTRPIPE || avail == -EINTR)
{
if (alsa_recover(alsa, (int)avail) < 0)
if (snd_pcm_recover(alsa->pcm, (int)avail, 1) < 0)
return 0;
if (--laps < 0)
return 0;
@@ -759,7 +786,7 @@ static size_t alsa_wait_writable(void *data, size_t len)
rc = snd_pcm_start(alsa->pcm);
if (rc == -EPIPE || rc == -ESTRPIPE || rc == -EINTR)
{
if (alsa_recover(alsa, rc) < 0)
if (snd_pcm_recover(alsa->pcm, rc, 1) < 0)
return 0;
}
else if (rc < 0)
@@ -771,7 +798,7 @@ static size_t alsa_wait_writable(void *data, size_t len)
return 0;
if (rc == -EPIPE || rc == -ESTRPIPE || rc == -EINTR)
{
if (alsa_recover(alsa, rc) < 0)
if (snd_pcm_recover(alsa->pcm, rc, 1) < 0)
return 0;
}
else if (rc < 0)
@@ -849,16 +876,6 @@ static void alsa_clock_sample(alsa_t *alsa)
if (!sys_ns)
return;
/* A load and a store rather than an exchange, which the volatile
* fallback of retro_atomic lacks: a raise landing between the two
* is one that has just been served. */
if (retro_atomic_load_acquire_int(&alsa->clk_rebase))
{
retro_atomic_store_relaxed_int(&alsa->clk_rebase, 0);
alsa->clk_have_anchor = 0;
alsa->clk_a_have_anchor = 0;
}
/* The hardware's own clock against the system's, where the driver
* counts one. This needs no rate and no delay - it is two clocks
* compared directly. */
@@ -973,12 +990,6 @@ static size_t alsa_frames_consumed(void *data)
return (size_t)(alsa->frames_written - (uint64_t)delay);
}
static size_t alsa_underruns(void *data)
{
alsa_t *alsa = (alsa_t*)data;
return alsa ? retro_atomic_load_acquire_size(&alsa->underruns) : 0;
}
audio_driver_t audio_alsa = {
alsa_init,
alsa_write,
@@ -996,7 +1007,7 @@ audio_driver_t audio_alsa = {
NULL, /* write_raw */
alsa_wait_writable,
alsa_frames_consumed,
alsa_underruns,
NULL, /* underruns */
alsa_layout,
NULL, /* frames_consumed_fallback */
alsa_device_clock_ppm
@@ -1167,10 +1178,6 @@ typedef struct ealsa
size_t period_frames;
size_t buffer_frames;
uint64_t frames_written; /* handed to the device since it opened */
/* Xruns the device reported on the write path: EPIPE is the device
* having run out of audio, which the ESTRPIPE beside it - a resume
* after a suspend - is not. */
retro_atomic_size_t underruns;
bool nonblock;
bool has_float;
bool can_pause;
@@ -1592,8 +1599,6 @@ static ssize_t tinyalsa_write(void *data, const void *buf, size_t len)
{
/* An underrun, or a resume after a suspend: prepared
* again and the frames go out on the next turn. */
if (errno == EPIPE)
retro_atomic_fetch_add_size(&ea->underruns, 1);
if (!ealsa_recover(ea))
return -1;
continue;
@@ -1777,12 +1782,6 @@ static void tinyalsa_device_list_free(void *data, void *array_list_data)
string_list_free(s);
}
static size_t tinyalsa_underruns(void *data)
{
tinyalsa_t *ea = (tinyalsa_t*)data;
return ea ? retro_atomic_load_acquire_size(&ea->underruns) : 0;
}
audio_driver_t audio_tinyalsa = {
tinyalsa_init,
tinyalsa_write,
@@ -1800,7 +1799,7 @@ audio_driver_t audio_tinyalsa = {
NULL, /* write_raw */
tinyalsa_wait_writable,
tinyalsa_frames_consumed,
tinyalsa_underruns,
NULL, /* underruns */
tinyalsa_layout
};
+2 -24
View File
@@ -16,12 +16,6 @@
#define ALSA_PCM_NEW_HW_PARAMS_API
#define ALSA_PCM_NEW_SW_PARAMS_API
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <retro_atomic.h>
#include <sys/asoundlib.h>
#include <retro_math.h>
@@ -44,12 +38,6 @@ typedef struct alsa
unsigned buf_size;
unsigned buf_count;
unsigned rate;
/* Times the device reported it had run out of audio. Counted where
* the status says so, on the write path, which is why it is counted
* and not said: a log line there costs part of the margin it is
* reporting on. The frontend shows it and logs the total once at
* teardown. */
retro_atomic_size_t underruns;
bool nonblock;
bool has_float;
bool can_pause;
@@ -182,7 +170,6 @@ static void *alsa_qsa_init(const char *device,
alsa->buffer[i] = alsa->buffer_chunk + i * alsa->buf_size;
alsa->has_float = false;
retro_atomic_size_init(&alsa->underruns, 0);
alsa->can_pause = true;
RARCH_LOG("[ALSA QSA] Can pause: %s.\n",
alsa->can_pause ? "yes" : "no");
@@ -219,7 +206,7 @@ static int check_pcm_status(void *data, int channel_type)
}
else if (status.status == SND_PCM_STATUS_UNDERRUN)
{
retro_atomic_fetch_add_size(&alsa->underruns, 1);
RARCH_LOG("[ALSA QSA] check_pcm_status: SNDP_CM_STATUS_UNDERRUN.\n");
if ((ret = snd_pcm_channel_prepare(alsa->pcm, channel_type)) < 0)
{
RARCH_ERR("[ALSA QSA] Invalid state detected for underrun on snd_pcm_channel_prepare: %s.\n",
@@ -435,12 +422,6 @@ static size_t alsa_qsa_buffer_size(void *data)
return alsa->buf_size * alsa->buf_count;
}
static size_t alsa_qsa_underruns(void *data)
{
alsa_qsa_t *alsa = (alsa_qsa_t*)data;
return alsa ? retro_atomic_load_acquire_size(&alsa->underruns) : 0;
}
audio_driver_t audio_alsa = {
alsa_qsa_init,
alsa_qsa_write,
@@ -455,8 +436,5 @@ audio_driver_t audio_alsa = {
NULL,
alsa_qsa_write_avail,
alsa_qsa_buffer_size,
NULL, /* write_raw */
NULL, /* wait_writable */
NULL, /* frames_consumed */
alsa_qsa_underruns
NULL /* write_raw */
};
+14 -31
View File
@@ -802,7 +802,8 @@ typedef struct ra_asio
double clk_sxx;
double clk_sxy;
double clk_n;
retro_atomic_int_t clk_ppm; /* AUDIO_CLOCK_PPM_NONE until known */
retro_atomic_int_t clk_ppm;
retro_atomic_int_t clk_valid;
/* Frames the device has taken: a period per callback, silence
* included. Written by the callback thread, read by the writer, so
* atomic: an aligned word is what the machine will not tear, which
@@ -810,12 +811,6 @@ typedef struct ra_asio
* - and the underrun counter beside it is already atomic for the
* same reason. */
retro_atomic_size_t consumed;
/* The frontend's reinit latch, taken on the main thread at init.
* The ASIO callbacks run on the driver's own thread, where reaching
* for a frontend singleton is the thing the worker-read audit
* exists to catch; wasapi captures the same pointer at
* sthread_create. */
retro_atomic_int_t *reinit_request;
/* Set by the message callback on kAsioResetRequest or
* kAsioBufferSizeChange: the buffers are to be disposed and created
* anew - the driver's preferred size may have changed - when the
@@ -1055,11 +1050,8 @@ static void asio_cb_sample_rate_changed(ASIOSampleRate rate)
RARCH_WARN("[ASIO] The device changed its sample rate to %.0f Hz; audio will reinitialise.\n",
(double)rate);
if (g_asio)
{
retro_atomic_store_release_int(&g_asio->rebuild_pending, 1);
if (g_asio->reinit_request)
retro_atomic_store_release_int(g_asio->reinit_request, 1);
}
retro_atomic_store_release_int(&audio_state_get_ptr()->reinit_request, 1);
}
static long asio_cb_message(long selector, long value,
@@ -1093,11 +1085,8 @@ static long asio_cb_message(long selector, long value,
RARCH_WARN("[ASIO] Driver requests %s; audio will reinitialise.\n",
selector == kAsioResetRequest ? "a reset" : "a buffer size change");
if (g_asio)
{
retro_atomic_store_release_int(&g_asio->rebuild_pending, 1);
if (g_asio->reinit_request)
retro_atomic_store_release_int(g_asio->reinit_request, 1);
}
retro_atomic_store_release_int(&audio_state_get_ptr()->reinit_request, 1);
return 1L;
case kAsioResyncRequest:
/* The driver lost its place - a system pause, a clock that
@@ -1192,6 +1181,7 @@ static ASIOTime * asio_cb_buffer_switch_time_info(
if (ppm > -100000.0 && ppm < 100000.0)
{
retro_atomic_store_release_int(&ad->clk_ppm, (int)ppm);
retro_atomic_store_release_int(&ad->clk_valid, 1);
}
}
}
@@ -1613,8 +1603,6 @@ static void *ra_asio_init(const char *device, unsigned rate,
bool rate_moved = false;
g_asio_persistent = NULL;
ad->reinit_request = &audio_state_get_ptr()->reinit_request;
/* Nothing was freed when this was parked, so a callback still
* in flight from then was harmless - it read a cleared g_asio
* and left. What follows here is not harmless: the ring is
@@ -1637,7 +1625,8 @@ static void *ra_asio_init(const char *device, unsigned rate,
* drift. */
ad->clk_have_anchor = 0;
ad->clk_n = 0.0;
retro_atomic_store_release_int(&ad->clk_ppm, AUDIO_CLOCK_PPM_NONE);
retro_atomic_store_release_int(&ad->clk_valid, 0);
retro_atomic_store_release_int(&ad->clk_ppm, 0);
/* The rate, settled before anything is sized from it.
*
@@ -1738,10 +1727,9 @@ static void *ra_asio_init(const char *device, unsigned rate,
ad = (ra_asio_t *)calloc(1, sizeof(ra_asio_t));
if (!ad)
return NULL;
/* Taken here, on the main thread, for the driver's callbacks. */
ad->reinit_request = &audio_state_get_ptr()->reinit_request;
retro_atomic_size_init(&ad->underruns, 0);
retro_atomic_int_init(&ad->clk_ppm, AUDIO_CLOCK_PPM_NONE);
retro_atomic_int_init(&ad->clk_ppm, 0);
retro_atomic_int_init(&ad->clk_valid, 0);
ad->clk_have_anchor = 0;
retro_atomic_size_init(&ad->consumed, 0);
@@ -2060,7 +2048,6 @@ static void ra_asio_set_nonblock_state(void *data, bool state)
static void ra_asio_free(void *data)
{
int clk_ppm;
ra_asio_t *ad = (ra_asio_t *)data;
if (!ad)
return;
@@ -2069,10 +2056,10 @@ static void ra_asio_free(void *data)
* was doing, against the rate this driver asked for. Logged beside
* the underrun count rather than acted on: until this has been read
* off real interfaces it is a measurement, not an input. */
clk_ppm = retro_atomic_load_acquire_int(&ad->clk_ppm);
if (clk_ppm != AUDIO_CLOCK_PPM_NONE)
if (retro_atomic_load_acquire_int(&ad->clk_valid))
RARCH_LOG("[ASIO] Device clock, from the driver's time information:"
" %+d ppm against %u Hz.\n", clk_ppm, ad->sample_rate);
" %+d ppm against %u Hz.\n",
retro_atomic_load_acquire_int(&ad->clk_ppm), ad->sample_rate);
else
RARCH_LOG("[ASIO] Device clock: the driver reported no usable time"
" information.\n");
@@ -2200,13 +2187,9 @@ static size_t ra_asio_underruns(void *data)
static bool ra_asio_device_clock_ppm(void *data, double *ppm)
{
ra_asio_t *ad = (ra_asio_t*)data;
int v;
if (!ad)
if (!ad || !retro_atomic_load_acquire_int(&ad->clk_valid))
return false;
v = retro_atomic_load_acquire_int(&ad->clk_ppm);
if (v == AUDIO_CLOCK_PPM_NONE)
return false;
*ppm = (double)v;
*ppm = (double)retro_atomic_load_acquire_int(&ad->clk_ppm);
return true;
}
+5 -106
View File
@@ -20,7 +20,6 @@
#include <sys/ioctl.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <sys/audioio.h>
@@ -28,12 +27,7 @@
#include "../../config.h"
#endif
#include <compat/strl.h>
#include <lists/string_list.h>
#include <retro_miscellaneous.h>
#include "../audio_driver.h"
#include "../audio_device_label.h"
#include "../../verbosity.h"
#define DEFAULT_DEV "/dev/audio"
@@ -42,19 +36,12 @@ static void *audioio_init(const char *device, unsigned rate, unsigned latency,
unsigned *new_out_rate)
{
struct audio_info info;
char audiodev[PATH_MAX_LENGTH];
const char *audiodev = device ? device : DEFAULT_DEV;
int *fd = (int*)calloc(1, sizeof(int));
if (!fd)
return NULL;
/* The device may be a list entry, "/dev/audio0 (name)": the path
* is what is opened. */
if (device)
audio_device_label_path(audiodev, sizeof(audiodev), device);
else
strlcpy(audiodev, DEFAULT_DEV, sizeof(audiodev));
AUDIO_INITINFO(&info);
#ifdef AUMODE_PLAY_ALL
@@ -191,104 +178,20 @@ static size_t audioio_buffer_size(void *data)
int *fd = (int*)data;
if (ioctl(*fd, AUDIO_GETINFO, &info) < 0)
return 0;
return false;
return info.play.buffer_size;
}
#ifdef AUDIO_GETBUFINFO
/* audio(4)'s play.seek is the count of bytes pending, so what the
* device will take is its buffer less what is still queued in it.
*
* AUDIO_GETBUFINFO is NetBSD's, and so is seek: the Solaris
* audio_prinfo carries buffer_size and samples and no seek, which is
* why this is not offered there. Reporting the whole buffer instead -
* which is what happened before - reads to rate control as a device
* that never drains, and it answers a device that is always empty
* with its full upward correction, for as long as it is on. The
* interface asks for nothing rather than a constant, and nothing is
* what the other branch gives. */
static size_t audioio_write_avail(void *data)
{
struct audio_info info;
int *fd = (int*)data;
if (ioctl(*fd, AUDIO_GETBUFINFO, &info) < 0)
return 0;
if (info.play.seek >= info.play.buffer_size)
return 0;
return info.play.buffer_size - info.play.seek;
return audioio_buffer_size(data);
}
#endif
/* The device is opened with AUDIO_ENCODING_SLINEAR at 16 bits; the
* audio(4) encodings are integer PCM, mu-law and A-law, none float. */
static bool audioio_use_float(void *data) { return false; }
/* The nodes an audioio kernel offers: /dev/audio and /dev/audio0..15,
* as oss.c walks /dev/dsp*. AUDIO_GETDEV names the hardware behind a
* node on every audioio kernel, so the label carries it. A node that
* cannot be opened for output is not offered; a busy one is, since it
* exists and may be free later. Needs no driver instance, which is
* what the menu has when init failed and the user needs to pick
* another device. */
static void *audioio_device_list_new(void *data)
{
int i;
union string_list_elem_attr attr;
struct string_list *sl = string_list_new();
(void)data;
attr.i = 0;
if (!sl)
return NULL;
for (i = -1; i < 16; i++)
{
char path[32];
char label[160];
int fd;
if (i < 0)
strlcpy(path, DEFAULT_DEV, sizeof(path));
else
snprintf(path, sizeof(path), "/dev/audio%d", i);
fd = open(path, O_WRONLY | O_NONBLOCK);
if (fd < 0 && errno != EBUSY)
continue;
strlcpy(label, path, sizeof(label));
if (fd >= 0)
{
audio_device_t dev;
if (ioctl(fd, AUDIO_GETDEV, &dev) == 0 && dev.name[0])
{
size_t _len = strlcat(label, " (", sizeof(label));
_len += strlcpy(label + _len, dev.name, sizeof(label) - _len);
strlcpy(label + _len, ")", sizeof(label) - _len);
}
close(fd);
}
string_list_append(sl, label, attr);
}
if (!sl->size)
{
string_list_free(sl);
return NULL;
}
return sl;
}
static void audioio_device_list_free(void *data, void *array_list_data)
{
struct string_list *sl = (struct string_list*)array_list_data;
(void)data;
if (sl)
string_list_free(sl);
}
audio_driver_t audio_audioio = {
audioio_init,
audioio_write,
@@ -299,13 +202,9 @@ audio_driver_t audio_audioio = {
audioio_free,
audioio_use_float,
"audioio",
audioio_device_list_new,
audioio_device_list_free,
#ifdef AUDIO_GETBUFINFO
NULL,
NULL,
audioio_write_avail,
#else
NULL, /* write_avail - no way to measure the fill here */
#endif
audioio_buffer_size,
NULL /* write_raw */
};
+2 -29
View File
@@ -16,9 +16,7 @@
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <malloc.h>
#include <boolean.h>
#include <retro_atomic.h>
#include <emscripten/wasm_worker.h>
#include <emscripten/webaudio.h>
@@ -67,11 +65,6 @@ typedef struct audioworklet_data
* ring_init false and the driver does not come up. */
retro_spsc_t ring;
size_t ring_size;
/* Quanta the worklet had to fill with silence because the ring came
* up short, or because it could not get at the ring at all. Counted
* here and not said: this is the render callback, and a line from
* it costs part of the quantum it is reporting on. */
retro_atomic_size_t underruns;
bool ring_init;
unsigned rate;
unsigned latency;
@@ -119,9 +112,7 @@ static bool audioworklet_process_cb(int numInputs, const AudioSampleFrame *input
/* busyspin is safe as of emscripten 4.0.4 */
if (!emscripten_lock_busyspin_wait_acquire(&audioworklet->buffer_lock, 2.5))
{
/* The output arrays arrive zeroed, so this quantum goes out
* as silence. */
retro_atomic_fetch_add_size(&audioworklet->underruns, 1);
printf("[WARN] [AudioWorklet] Worklet: could not acquire lock\n");
return true;
}
@@ -145,10 +136,6 @@ static bool audioworklet_process_cb(int numInputs, const AudioSampleFrame *input
if (writing_frames < outputs[0].samplesPerChannel)
{
int zero_frames = outputs[0].samplesPerChannel - writing_frames;
/* Only where audio was expected: a driver that is not running is
* not being starved. */
if (audioworklet->driver_running)
retro_atomic_fetch_add_size(&audioworklet->underruns, 1);
memset(outputs[0].data + writing_frames, 0, zero_frames * sizeof(float));
memset(outputs[0].data + writing_frames + outputs[0].samplesPerChannel, 0, zero_frames * sizeof(float));
}
@@ -300,10 +287,7 @@ static void *audioworklet_init(const char *device, unsigned rate,
return NULL;
audioworklet->worklet_stack = memalign(16, WORKLET_STACK_SIZE);
if (!audioworklet->worklet_stack)
{
free(audioworklet);
return NULL;
}
audioworklet_static_data = audioworklet;
audioworklet->latency = latency;
@@ -317,7 +301,6 @@ static void *audioworklet_init(const char *device, unsigned rate,
* below take their existing error exits. */
if (!audioworklet->ring_init)
audioworklet->init_error = true;
retro_atomic_size_init(&audioworklet->underruns, 0);
emscripten_lock_init(&audioworklet->buffer_lock);
#ifdef PROXY_TO_PTHREAD
emscripten_lock_init(&audioworklet->trywrite_lock);
@@ -624,13 +607,6 @@ static size_t audioworklet_buffer_size(void *data)
static bool audioworklet_use_float(void *data) { return true; }
static size_t audioworklet_underruns(void *data)
{
audioworklet_data_t *audioworklet = (audioworklet_data_t*)data;
return audioworklet
? retro_atomic_load_acquire_size(&audioworklet->underruns) : 0;
}
audio_driver_t audio_audioworklet = {
audioworklet_init,
audioworklet_write,
@@ -645,8 +621,5 @@ audio_driver_t audio_audioworklet = {
NULL,
audioworklet_write_avail,
audioworklet_buffer_size,
NULL, /* write_raw */
NULL, /* wait_writable */
NULL, /* frames_consumed */
audioworklet_underruns
NULL /* write_raw */
};
+66 -58
View File
@@ -328,12 +328,6 @@ typedef struct coreaudio
* on that path, read by the frontend's overlay, and by it once at
* teardown for the log. Never logged from here. */
retro_atomic_size_t underruns;
/* The frontend's reinit latch, taken on the main thread at init:
* the default-device listeners run on HAL threads, where reaching
* for a frontend singleton is the thing the worker-read audit
* exists to catch. wasapi captures the same pointer at
* sthread_create. */
retro_atomic_int_t *reinit_request;
/* The largest number_frames the callback may be handed, as the
* device and the unit describe themselves, and the largest it was
@@ -353,19 +347,19 @@ typedef struct coreaudio
/* Render callbacks handed invalid output buffers. */
retro_atomic_size_t format_errors;
/* The worst a callback came up short by and how full the ring was
* that time, in frames: the shortfall in the high 16 bits, the ring
* in the low 16, each saturating at 0xFFFF. One word, so the two
* always come from the same callback. Written by the render thread
* and read once at teardown - never logged from there. A buzz is a
* callback finding less than a period and padding the rest with
* silence, so these two numbers say whether that is what is
* happening and by how much, which "it buzzes" cannot. Out here rather than beside period_frames, which is the
/* The worst a callback came up short by, in samples, and how full
* the ring was that time. Written by the render thread with two
* atomic stores and read once at teardown - never logged from
* there. A buzz is a callback finding less than a period and
* padding the rest with silence, so these two numbers say whether
* that is what is happening and by how much, which "it buzzes"
* cannot. Out here rather than beside period_frames, which is the
* HAL's and macOS-only: the callback and the teardown log read
* these on every Apple platform, so declaring them under
* !TARGET_OS_IPHONE left the iOS and tvOS builds referring to
* members that were not there. */
retro_atomic_int_t worst_short;
retro_atomic_size_t worst_short;
retro_atomic_size_t worst_short_avail;
/* The output unit: ComponentInstance or AudioComponentInstance,
* both of which are this type on every SDK. */
@@ -420,12 +414,14 @@ typedef struct coreaudio
uint64_t ct_anchor_host;
int ct_have_anchor;
double ct_sx, ct_sy, ct_sxx, ct_sxy, ct_n;
retro_atomic_int_t ct_ppm; /* AUDIO_CLOCK_PPM_NONE until known */
retro_atomic_int_t ct_ppm;
retro_atomic_int_t ct_valid;
/* What the HAL says itself, where it fills it in: mRateScalar is
* the device clock against nominal, already computed by someone
* with better information than this fit has. Kept beside it rather
* than instead of it, so the two can be compared on hardware. */
retro_atomic_int_t ct_scalar_ppm; /* AUDIO_CLOCK_PPM_NONE until known */
retro_atomic_int_t ct_scalar_ppm;
retro_atomic_int_t ct_scalar_valid;
unsigned output_rate; /* Hardware output rate */
/* The layout the output unit's input bus was set to, as the
* frontend's mask, and its channel count: the unit routes each
@@ -578,8 +574,6 @@ static void coreaudio_free(void *data)
{
coreaudio_t *dev = (coreaudio_t*)data;
size_t n;
int clk_ppm;
unsigned worst;
if (!dev)
return;
@@ -589,12 +583,11 @@ static void coreaudio_free(void *data)
* shortfall against the ring and the period, which is what says
* whether a buzz is the ring being too small for the device's
* period rather than the source being late. */
worst = (unsigned)retro_atomic_load_acquire_int(&dev->worst_short);
if ((n = retro_atomic_load_acquire_size(&dev->underruns)))
RARCH_LOG("[CoreAudio] The callback came up short %u time%s; at worst it wanted %u samples more than the %u it found, against a %u-sample ring and a %u-frame device period.\n",
(unsigned)n, n == 1 ? "" : "s",
(worst >> 16) * dev->channels,
(worst & 0xFFFFu) * dev->channels,
(unsigned)retro_atomic_load_acquire_size(&dev->worst_short),
(unsigned)retro_atomic_load_acquire_size(&dev->worst_short_avail),
(unsigned)dev->usable,
#if !TARGET_OS_IPHONE
(unsigned)dev->period_frames
@@ -610,18 +603,17 @@ static void coreaudio_free(void *data)
* measurements, and a clock estimate that is wrong is worse than
* one that is absent. Where both appear they should agree, and a
* disagreement is the interesting result. */
clk_ppm = retro_atomic_load_acquire_int(&dev->ct_ppm);
if (clk_ppm != AUDIO_CLOCK_PPM_NONE)
if (retro_atomic_load_acquire_int(&dev->ct_valid))
RARCH_LOG("[CoreAudio] Device clock, fitted from the callback's"
" timestamps: %+d ppm against %u Hz.\n",
clk_ppm, dev->output_rate);
retro_atomic_load_acquire_int(&dev->ct_ppm), dev->output_rate);
else
RARCH_LOG("[CoreAudio] Device clock: not enough usable timestamps"
" to fit one.\n");
clk_ppm = retro_atomic_load_acquire_int(&dev->ct_scalar_ppm);
if (clk_ppm != AUDIO_CLOCK_PPM_NONE)
if (retro_atomic_load_acquire_int(&dev->ct_scalar_valid))
RARCH_LOG("[CoreAudio] Device clock, as the HAL reports it"
" (mRateScalar): %+d ppm.\n", clk_ppm);
" (mRateScalar): %+d ppm.\n",
retro_atomic_load_acquire_int(&dev->ct_scalar_ppm));
/* What the device was said to be able to ask for, and what it did
* ask for. Said whether or not anything went wrong, because the
@@ -683,6 +675,7 @@ static OSStatus coreaudio_audio_write_cb(void *userdata,
{
retro_atomic_store_release_int(&dev->ct_scalar_ppm,
(int)((time_stamp->mRateScalar - 1.0) * 1000000.0));
retro_atomic_store_release_int(&dev->ct_scalar_valid, 1);
}
if ( (f & kAudioTimeStampSampleTimeValid)
@@ -727,6 +720,7 @@ static OSStatus coreaudio_audio_write_cb(void *userdata,
if (ppm > -100000.0 && ppm < 100000.0)
{
retro_atomic_store_release_int(&dev->ct_ppm, (int)ppm);
retro_atomic_store_release_int(&dev->ct_valid, 1);
}
}
}
@@ -819,18 +813,12 @@ static OSStatus coreaudio_audio_write_cb(void *userdata,
*action_flags |= kAudioUnitRenderAction_OutputIsSilence;
retro_atomic_fetch_add_size(&dev->underruns, 1);
{
/* Whole frames both: avail was rounded down above, and
* frames_needed is a multiple of the channel count. */
size_t shortfall = (frames_needed - avail) / dev->channels;
size_t have = avail / dev->channels;
if (shortfall > 0xFFFF)
shortfall = 0xFFFF;
if (have > 0xFFFF)
have = 0xFFFF;
if (shortfall > ((unsigned)retro_atomic_load_acquire_int(
&dev->worst_short) >> 16))
retro_atomic_store_release_int(&dev->worst_short,
(int)((shortfall << 16) | have));
size_t shortfall = frames_needed - avail;
if (shortfall > retro_atomic_load_acquire_size(&dev->worst_short))
{
retro_atomic_store_release_size(&dev->worst_short, shortfall);
retro_atomic_store_release_size(&dev->worst_short_avail, avail);
}
}
}
else
@@ -1383,11 +1371,11 @@ static void *coreaudio_init(const char *device,
}
dev->ct_have_anchor = 0;
dev->ct_n = 0.0;
retro_atomic_int_init(&dev->ct_ppm, AUDIO_CLOCK_PPM_NONE);
retro_atomic_int_init(&dev->ct_scalar_ppm, AUDIO_CLOCK_PPM_NONE);
retro_atomic_int_init(&dev->ct_ppm, 0);
retro_atomic_int_init(&dev->ct_valid, 0);
retro_atomic_int_init(&dev->ct_scalar_ppm, 0);
retro_atomic_int_init(&dev->ct_scalar_valid, 0);
retro_atomic_size_init(&dev->underruns, 0);
/* Taken here, on the main thread, for the HAL's listeners. */
dev->reinit_request = &audio_state_get_ptr()->reinit_request;
/* What coreaudio_run() waits for before starting; the HAL's pull on
* macOS, and on iOS the ring's own quarter, which is what the unit
* was asked to use. */
@@ -1399,7 +1387,8 @@ static void *coreaudio_init(const char *device,
retro_atomic_size_init(&dev->max_pull_observed, 0);
retro_atomic_size_init(&dev->oversized_pulls, 0);
retro_atomic_size_init(&dev->format_errors, 0);
retro_atomic_int_init(&dev->worst_short, 0);
retro_atomic_size_init(&dev->worst_short, 0);
retro_atomic_size_init(&dev->worst_short_avail, 0);
dev->write_ptr = 0;
dev->read_ptr = 0;
@@ -1801,16 +1790,19 @@ static void coreaudio_device_list_free(void *data, void *array_list_data)
static bool coreaudio_device_clock_ppm(void *data, double *ppm)
{
coreaudio_t *dev = (coreaudio_t*)data;
int v;
if (!dev)
return false;
v = retro_atomic_load_acquire_int(&dev->ct_scalar_ppm);
if (v == AUDIO_CLOCK_PPM_NONE)
v = retro_atomic_load_acquire_int(&dev->ct_ppm);
if (v == AUDIO_CLOCK_PPM_NONE)
return false;
*ppm = (double)v;
return true;
if (retro_atomic_load_acquire_int(&dev->ct_scalar_valid))
{
*ppm = (double)retro_atomic_load_acquire_int(&dev->ct_scalar_ppm);
return true;
}
if (retro_atomic_load_acquire_int(&dev->ct_valid))
{
*ppm = (double)retro_atomic_load_acquire_int(&dev->ct_ppm);
return true;
}
return false;
}
static size_t coreaudio_frames_consumed(void *data)
@@ -1904,8 +1896,9 @@ audio_driver_t audio_coreaudio = {
* built again against whatever the default now is. */
static void coreaudio_output_default_moved(coreaudio_t *dev)
{
if (dev && dev->follows_default && dev->reinit_request)
retro_atomic_store_release_int(dev->reinit_request, 1);
if (dev && dev->follows_default)
retro_atomic_store_release_int(
&audio_state_get_ptr()->reinit_request, 1);
}
static OSStatus coreaudio_output_default_listener(ca_obj_id_t obj,
@@ -2005,7 +1998,7 @@ typedef struct coreaudio_mic
/* The reader's park, notified by the capture callback after each
* slice it writes (and on the ones it cannot: a full ring still
* moves the epoch, so a parked reader re-checks). An eventcount,
* as in sdl2_audio's mic: the callback's notify is an atomic bump
* as in sdl_audio's mic: the callback's notify is an atomic bump
* and a load with no reader parked - no lock ever on the capture
* thread - and the reader's timed waits re-check the ring inside
* the prepare/commit window, so a slice landing between the check
@@ -2017,6 +2010,7 @@ typedef struct coreaudio_mic
void *cb_buf;
size_t cb_buf_size;
unsigned sample_rate;
bool nonblock;
bool use_float;
#if !TARGET_OS_IPHONE
AudioDeviceID device;
@@ -2028,10 +2022,12 @@ typedef struct coreaudio_mic
/* Driver-wide context: created by init(), destroyed by free(). The
* frontend holds it for as long as the driver is selected, longer than
* any microphone opened through it, so it is never the same allocation
* as one. It tracks the one microphone open through it. */
* as one. It latches the non-blocking state so a mic opened later
* inherits it. */
typedef struct coreaudio_mic_driver
{
coreaudio_mic_t *mic;
bool nonblock;
} coreaudio_mic_driver_t;
static void coreaudio_mic_close(void *driver_context, void *mic_context);
@@ -2360,7 +2356,7 @@ static int coreaudio_mic_read(void *driver_context, void *mic_context,
avail = retro_spsc_read_avail(&mic->ring);
n = avail < len ? avail : len;
if (!n)
if (!n && !mic->nonblock)
{
/* Blocking: one slice's worth of wait for the callback, timed,
* with the ring re-checked inside the window. */
@@ -2377,6 +2373,16 @@ static int coreaudio_mic_read(void *driver_context, void *mic_context,
return (int)n;
}
static void coreaudio_mic_set_nonblock_state(void *driver_context, bool state)
{
coreaudio_mic_driver_t *drv = (coreaudio_mic_driver_t*)driver_context;
if (!drv)
return;
drv->nonblock = state;
if (drv->mic)
drv->mic->nonblock = state;
}
static struct string_list *coreaudio_mic_device_list_new(const void *driver_context)
{
#if TARGET_OS_IPHONE
@@ -2467,6 +2473,7 @@ static void *coreaudio_mic_open(void *driver_context, const char *device,
retro_atomic_int_init(&mic->running, 0);
retro_atomic_int_init(&mic->initialized, 0);
mic->sample_rate = rate;
mic->nonblock = drv->nonblock;
if (!retro_eventcount_init(&mic->park))
goto error;
@@ -2680,6 +2687,7 @@ microphone_driver_t microphone_coreaudio = {
coreaudio_mic_init,
coreaudio_mic_free,
coreaudio_mic_read,
coreaudio_mic_set_nonblock_state,
"coreaudio",
coreaudio_mic_device_list_new,
coreaudio_mic_device_list_free,
+11 -156
View File
@@ -17,7 +17,6 @@
#include <string.h>
#include <retro_atomic.h>
#include <features/features_cpu.h>
#include <malloc.h>
#include "../audio_driver.h"
@@ -25,17 +24,6 @@
#include "../../retroarch.h"
#include "../../verbosity.h"
/* libctru defines this from 1.5 on; it is the SoC clock by sixteen. */
#ifndef SYSCLOCK_ARM11
#define SYSCLOCK_ARM11 (16756991u * 8u * 2u)
#endif
/* The rate the channel is set to and the rate the frontend is told,
* which have to be the one number: the channel interpolates with
* NDSP_INTERP_NONE, so anything the frontend resamples to that the
* channel is not playing at is error with nothing to correct it. */
#define CTR_DSP_AUDIO_RATE 32728
typedef struct
{
ndspWaveBuf dsp_buf; /* TODO/FIXME - find out alignment */
@@ -54,27 +42,6 @@ typedef struct
* that once every four seconds and would miss ninety wraps. */
uint32_t last_pos;
retro_atomic_size_t consumed;
/* The DSP's clock against the rate the channel is set to, fitted
* from the sample position this callback already reads and
* svcGetSystemTick(), which counts in the ARM11's domain rather
* than the DSP's. Accumulated in the callback, which owns all of
* it, and published as one int in ppm.
*
* cpu_features_get_perf_counter() and not
* cpu_features_get_time_usec(): the counter is svcGetSystemTick()
* here, the ARM11's own tick; the clock is osGetTime(), which
* resolves in milliseconds - a hundred parts per million of
* quantisation on a ten second window, against a figure measured
* in single parts. SYSCLOCK_ARM11 is the counter's scale, since
* features_cpu.h offers no frequency beside it.
*
* A measurement. Nothing acts on it. */
uint64_t clk_pos;
uint64_t clk_anchor_pos;
uint64_t clk_anchor_tick;
int clk_have_anchor;
double clk_sx, clk_sy, clk_sxx, clk_sxy, clk_n;
retro_atomic_int_t clk_ppm; /* AUDIO_CLOCK_PPM_NONE until known */
bool nonblock;
bool playing;
} ctr_dsp_audio_t;
@@ -83,75 +50,18 @@ typedef struct
#define CTR_DSP_AUDIO_COUNT_MASK (CTR_DSP_AUDIO_COUNT - 1u)
#define CTR_DSP_AUDIO_SIZE (CTR_DSP_AUDIO_COUNT * sizeof(int16_t) * 2)
/* One (position, tick) pair into the fit. Runs on the DSP callback's
* own thread, which owns every field it touches here. */
static void ctr_dsp_audio_clock_sample(ctr_dsp_audio_t *ctr, uint32_t frames)
{
uint64_t tick = (uint64_t)cpu_features_get_perf_counter();
double x, y, denom;
ctr->clk_pos += frames;
/* Taken on the first frame, on a position that went backwards, and
* again once the window has run long enough to be worth
* restarting. */
if ( !ctr->clk_have_anchor
|| tick <= ctr->clk_anchor_tick
|| (tick - ctr->clk_anchor_tick) > (uint64_t)SYSCLOCK_ARM11 * 30)
{
ctr->clk_anchor_pos = ctr->clk_pos;
ctr->clk_anchor_tick = tick;
ctr->clk_have_anchor = 1;
ctr->clk_sx = ctr->clk_sy = ctr->clk_sxx = ctr->clk_sxy
= ctr->clk_n = 0.0;
retro_atomic_store_release_int(&ctr->clk_ppm, AUDIO_CLOCK_PPM_NONE);
return;
}
/* Seconds and frames from the anchor: a fit on the raw values
* loses its answer to cancellation. */
x = (double)(tick - ctr->clk_anchor_tick) / (double)SYSCLOCK_ARM11;
y = (double)(ctr->clk_pos - ctr->clk_anchor_pos);
ctr->clk_sx += x;
ctr->clk_sy += y;
ctr->clk_sxx += x * x;
ctr->clk_sxy += x * y;
ctr->clk_n += 1.0;
/* A second of window at least, as the interface asks. */
if (ctr->clk_n < 4.0 || x < 1.0)
return;
denom = ctr->clk_n * ctr->clk_sxx - ctr->clk_sx * ctr->clk_sx;
if (denom <= 0.0)
return;
{
double slope = (ctr->clk_n * ctr->clk_sxy - ctr->clk_sx * ctr->clk_sy)
/ denom;
double ppm = (slope / (double)CTR_DSP_AUDIO_RATE - 1.0) * 1000000.0;
if (ppm > -100000.0 && ppm < 100000.0)
{
retro_atomic_store_release_int(&ctr->clk_ppm, (int)ppm);
}
}
}
static void ctr_dsp_audio_frame_cb(void *data)
{
ctr_dsp_audio_t *ctr = (ctr_dsp_audio_t*)data;
uint32_t pos = ndspChnGetSamplePos(ctr->channel);
/* Unsigned subtraction and the mask do the unwrap: the difference is
* right across the wrap without a comparison. Only this callback
* writes either field. */
uint32_t frames = (pos - ctr->last_pos) & CTR_DSP_AUDIO_COUNT_MASK;
retro_atomic_fetch_add_size(&ctr->consumed, (size_t)frames);
retro_atomic_fetch_add_size(&ctr->consumed,
(size_t)((pos - ctr->last_pos) & CTR_DSP_AUDIO_COUNT_MASK));
ctr->last_pos = pos;
ctr_dsp_audio_clock_sample(ctr, frames);
LightEvent_Signal(&ctr->frame_event);
}
@@ -184,11 +94,13 @@ static void *ctr_dsp_audio_init(const char *device, unsigned rate, unsigned late
ndspExit();
return NULL;
}
retro_atomic_int_init(&ctr->clk_ppm, AUDIO_CLOCK_PPM_NONE);
LightEvent_Init(&ctr->frame_event, RESET_ONESHOT);
ndspSetCallback(ctr_dsp_audio_frame_cb, ctr);
*new_rate = CTR_DSP_AUDIO_RATE;
if (!ctr)
return NULL;
*new_rate = 32730;
ctr->channel = 0;
@@ -198,7 +110,7 @@ static void *ctr_dsp_audio_init(const char *device, unsigned rate, unsigned late
ndspChnReset(ctr->channel);
ndspChnSetFormat(ctr->channel, NDSP_FORMAT_STEREO_PCM16);
ndspChnSetInterp(ctr->channel, NDSP_INTERP_NONE);
ndspChnSetRate(ctr->channel, (float)CTR_DSP_AUDIO_RATE);
ndspChnSetRate(ctr->channel, 32728.0f);
ndspChnWaveBufClear(ctr->channel);
ctr->dsp_buf.data_pcm16 = linearAlloc(CTR_DSP_AUDIO_SIZE);
@@ -232,38 +144,6 @@ static void ctr_dsp_audio_free(void *data)
* advance before giving up on it. */
#define CTR_DSP_AUDIO_WAIT_LAPS 20
/* LightEvent_Wait() has no timeout, so a DSP that has stopped
* signalling its frame event - reset, or the channel dropped coming
* back from sleep - never returns from it. Never reached while the DSP
* runs: it signals every frame, so this only decides how long a
* stopped one takes to be noticed. */
#define CTR_DSP_AUDIO_STALL_TIMEOUT_NS 256000000LL
#define CTR_DSP_AUDIO_WAIT_WRITABLE_LAPS 8
/* LightEvent_WaitTimeout() is libctru 2's; on 1.x the wait is the
* try-wait polled in slices short against the DSP's frame. Non-zero is
* the timeout either way. */
#ifdef USE_CTRULIB_2
#define ctr_dsp_audio_frame_wait(ctr, ns) \
LightEvent_WaitTimeout(&(ctr)->frame_event, (ns))
#else
#define CTR_DSP_AUDIO_POLL_SLICE_NS 500000LL
static int ctr_dsp_audio_frame_wait(ctr_dsp_audio_t *ctr, s64 timeout_ns)
{
s64 left = timeout_ns;
while (left > 0)
{
if (LightEvent_TryWait(&ctr->frame_event))
return 0;
svcSleepThread(CTR_DSP_AUDIO_POLL_SLICE_NS);
left -= CTR_DSP_AUDIO_POLL_SLICE_NS;
}
return 1;
}
#endif
static ssize_t ctr_dsp_audio_write(void *data, const void *buf, size_t len)
{
u32 pos;
@@ -385,9 +265,6 @@ static size_t ctr_dsp_audio_wait_writable(void *data, size_t len)
{
ctr_dsp_audio_t *ctr = (ctr_dsp_audio_t*)data;
uint32_t want = (uint32_t)(len >> 2);
/* Each wait ends on a timeout; this ends the loop when the DSP
* keeps calling back but never frees enough. */
int laps = CTR_DSP_AUDIO_WAIT_WRITABLE_LAPS;
if (want > (CTR_DSP_AUDIO_COUNT >> 1))
want = CTR_DSP_AUDIO_COUNT >> 1;
@@ -396,18 +273,13 @@ static size_t ctr_dsp_audio_wait_writable(void *data, size_t len)
{
uint32_t avail;
if (!ctr->playing)
break;
return 0;
avail = (ndspChnGetSamplePos(ctr->channel) - ctr->pos)
& CTR_DSP_AUDIO_COUNT_MASK;
if (avail >= want)
return avail * 2 * sizeof(int16_t);
if (--laps < 0)
break;
/* Non-zero is the timeout. */
if (ctr_dsp_audio_frame_wait(ctr, CTR_DSP_AUDIO_STALL_TIMEOUT_NS))
break;
LightEvent_Wait(&ctr->frame_event);
}
return 0;
}
/* Both in bytes of int16 stereo, as the interface asks: the ring is
@@ -419,19 +291,6 @@ static size_t ctr_dsp_audio_buffer_size(void *data)
return CTR_DSP_AUDIO_COUNT * 2 * sizeof(int16_t);
}
static bool ctr_dsp_audio_device_clock_ppm(void *data, double *ppm)
{
ctr_dsp_audio_t *ctr = (ctr_dsp_audio_t*)data;
int v;
if (!ctr)
return false;
v = retro_atomic_load_acquire_int(&ctr->clk_ppm);
if (v == AUDIO_CLOCK_PPM_NONE)
return false;
*ppm = (double)v;
return true;
}
audio_driver_t audio_ctr_dsp = {
ctr_dsp_audio_init,
ctr_dsp_audio_write,
@@ -448,9 +307,5 @@ audio_driver_t audio_ctr_dsp = {
ctr_dsp_audio_buffer_size,
NULL, /* write_raw */
ctr_dsp_audio_wait_writable,
ctr_dsp_audio_frames_consumed,
NULL, /* underruns */
NULL, /* layout */
NULL, /* frames_consumed_fallback */
ctr_dsp_audio_device_clock_ppm
ctr_dsp_audio_frames_consumed
};
+1 -11
View File
@@ -151,9 +151,6 @@ typedef struct dsound
* 48 kHz it holds twelve hours, and the estimate treats a wrap as a
* restart. */
retro_atomic_int_t frames_played;
/* Blocks the mixer thread had room for and nothing to put in, so
* the device played the silence written into them instead. */
retro_atomic_size_t underruns;
DWORD last_read_ptr;
} dsound_t;
@@ -385,7 +382,6 @@ static DWORD CALLBACK dsound_thread(PVOID data)
* fill block with silence. */
memset(region.chunk1, 0, region.size1);
memset(region.chunk2, 0, region.size2);
retro_atomic_fetch_add_size(&ds->underruns, 1);
}
else
{
@@ -986,12 +982,6 @@ static uint32_t dsound_layout(void *data)
return ds ? ds->layout : AUDIO_LAYOUT_STEREO;
}
static size_t dsound_underruns(void *data)
{
dsound_t *ds = (dsound_t*)data;
return ds ? retro_atomic_load_acquire_size(&ds->underruns) : 0;
}
audio_driver_t audio_dsound = {
dsound_init,
dsound_write,
@@ -1009,6 +999,6 @@ audio_driver_t audio_dsound = {
NULL, /* write_raw */
dsound_wait_writable,
dsound_frames_consumed,
dsound_underruns,
NULL, /* underruns */
dsound_layout
};
+1 -143
View File
@@ -21,13 +21,11 @@
#ifdef GEKKO
#include <gccore.h>
#include <ogcsys.h>
#include <ogc/lwp_watchdog.h>
#else
#include <cafe/ai.h>
#endif
#include <boolean.h>
#include <features/features_cpu.h>
#include <retro_inline.h>
#include <defines/gx_defines.h>
@@ -57,40 +55,6 @@ typedef struct
* way. 32-bit so the read cannot tear: at 48 kHz it wraps in about
* a day, which the estimator's differences handle. */
volatile uint32_t consumed;
/* Chunks the DMA started on that the writer had not reached: the
* callback zeroes each chunk as it retires it, so what goes out is
* silence. Volatile rather than atomic for the reason consumed is. */
volatile uint32_t underruns;
/* One (position, time) pair per chunk for the device clock, taken
* in the callback because that is where the position is exact:
* sampling consumed from the frontend would be a chunk out, which
* over any usable window is more error than the figure being
* measured. clk_seq brackets the pair so the reader can tell it
* was not caught mid-write.
*
* The fit itself is done in device_clock_ppm(), on the frontend's
* thread. This callback is a DMA interrupt, and floating point
* there is not a thing to be doing.
*
* clk_tb is the PPC timebase, which is what
* cpu_features_get_perf_counter() reads here - a counter, not a
* clock. ticks_to_microsecs() is its scale; features_cpu.h offers
* the counter and no frequency beside it. */
volatile uint32_t clk_pos;
volatile uint32_t clk_tb;
volatile uint32_t clk_seq;
/* The fit, touched only by device_clock_ppm(). Sums in seconds and
* frames from the anchor, because a fit on raw values loses its
* answer to cancellation. The anchor is retaken well inside the
* 32-bit timebase's wrap. */
uint32_t clk_last_seq;
uint32_t clk_anchor_pos;
uint32_t clk_anchor_tb;
int clk_have_anchor;
int clk_valid;
int clk_ppm;
double clk_sx, clk_sy, clk_sxx, clk_sxy, clk_n;
unsigned rate;
bool nonblock;
bool is_paused;
} gx_audio_t;
@@ -116,23 +80,11 @@ static void gx_audio_dma_callback(void)
wa->dma_busy = wa->dma_next;
wa->dma_next = (wa->dma_next + 1) & (BLOCKS - 1);
/* The chunk the DMA is about to take is one the writer has not
* filled, and the callback erased it as it retired. */
if (wa->dma_next == wa->dma_write)
wa->underruns++;
DCFlushRange(wa->data[wa->dma_next], CHUNK_SIZE);
AIInitDMA((uint32_t)wa->data[wa->dma_next], CHUNK_SIZE);
/* A chunk the DMA has finished with: device time. */
wa->consumed += CHUNK_FRAMES;
/* The pair the clock fit reads, published between two bumps of the
* sequence so a reader can tell a torn one. */
wa->clk_seq++;
wa->clk_pos = wa->consumed;
wa->clk_tb = (uint32_t)cpu_features_get_perf_counter();
wa->clk_seq++;
OSSignalCond(wa->dma_cond);
}
@@ -167,13 +119,11 @@ static void *gx_audio_init(const char *device,
{
AISetDSPSampleRate(AI_SAMPLERATE_32KHZ);
*new_rate = 32000;
wa->rate = 32000;
}
else /* Ranges 32001-39999 (in settings going up from 32000) and 48000-max (default high) -> set to 48000 hz */
{
AISetDSPSampleRate(AI_SAMPLERATE_48KHZ);
*new_rate = 48000;
wa->rate = 48000;
}
wa->dma_write = BLOCKS - 1;
@@ -362,94 +312,6 @@ static size_t gx_audio_wait_writable(void *data, size_t len)
* so the slot cannot be NULL either. */
static bool gx_audio_use_float(void *data) { return false; }
static size_t gx_audio_underruns(void *data)
{
gx_audio_t *wa = (gx_audio_t*)data;
return wa ? (size_t)wa->underruns : 0;
}
/* The AI's own clock against the rate this driver reports, in parts
* per million. What the hardware does with the rate it was asked for
* is not the rate it was asked for: the AI divides its source clock by
* a figure that does not land on 48000, and nothing downstream has
* ever been told. A measurement; nothing acts on it. */
static bool gx_audio_device_clock_ppm(void *data, double *ppm)
{
gx_audio_t *wa = (gx_audio_t*)data;
uint32_t s1 = 0, s2 = 0, pos = 0, tb = 0;
int tries = 4;
if (!wa || !wa->rate)
return false;
/* A pair the callback was not in the middle of writing. */
do
{
s1 = wa->clk_seq;
pos = wa->clk_pos;
tb = wa->clk_tb;
s2 = wa->clk_seq;
} while ((s1 != s2 || (s1 & 1u)) && --tries > 0);
if (s1 == s2 && !(s1 & 1u) && s1 != wa->clk_last_seq)
{
wa->clk_last_seq = s1;
/* Retaken on the first pair, when the DMA has restarted and the
* position went backwards, and well inside the wrap of the
* 32-bit timebase this samples. The frame bound catches the
* case the tick bound cannot: nothing reads this while the
* statistics are off, and a gap long enough to wrap the
* timebase comes back looking like a short interval. */
if ( !wa->clk_have_anchor
|| pos - wa->clk_anchor_pos > 0x80000000u
|| (uint32_t)(pos - wa->clk_anchor_pos) > wa->rate * 31u
|| (uint64_t)ticks_to_microsecs(
(uint64_t)(uint32_t)(tb - wa->clk_anchor_tb)) > 30000000ull)
{
wa->clk_anchor_pos = pos;
wa->clk_anchor_tb = tb;
wa->clk_have_anchor = 1;
wa->clk_valid = 0;
wa->clk_sx = wa->clk_sy = wa->clk_sxx = wa->clk_sxy = wa->clk_n = 0.0;
}
else
{
double x = (double)(uint64_t)ticks_to_microsecs(
(uint64_t)(uint32_t)(tb - wa->clk_anchor_tb)) / 1000000.0;
double y = (double)(uint32_t)(pos - wa->clk_anchor_pos);
wa->clk_sx += x;
wa->clk_sy += y;
wa->clk_sxx += x * x;
wa->clk_sxy += x * y;
wa->clk_n += 1.0;
/* A second of window at least, as the interface asks. */
if (wa->clk_n >= 4.0 && x >= 1.0)
{
double denom = wa->clk_n * wa->clk_sxx - wa->clk_sx * wa->clk_sx;
if (denom > 0.0)
{
double slope = (wa->clk_n * wa->clk_sxy
- wa->clk_sx * wa->clk_sy) / denom;
double est = (slope / (double)wa->rate - 1.0) * 1000000.0;
if (est > -100000.0 && est < 100000.0)
{
wa->clk_ppm = (int)est;
wa->clk_valid = 1;
}
}
}
}
}
if (!wa->clk_valid)
return false;
*ppm = (double)wa->clk_ppm;
return true;
}
audio_driver_t audio_gx = {
gx_audio_init,
gx_audio_write,
@@ -466,9 +328,5 @@ audio_driver_t audio_gx = {
gx_audio_buffer_size,
NULL, /* write_raw */
gx_audio_wait_writable,
gx_audio_frames_consumed,
gx_audio_underruns,
NULL, /* layout */
NULL, /* frames_consumed_fallback */
gx_audio_device_clock_ppm
gx_audio_frames_consumed
};
+14 -23
View File
@@ -43,11 +43,6 @@ typedef struct jack
unsigned channels; /* ports, and samples a frame in the ring */
uint32_t layout; /* the frontend's mask, one port a position */
jack_ringbuffer_t *buffer;
/* The frontend's reinit latch, taken on the main thread at init:
* the server's callbacks run on its threads, where reaching for a
* frontend singleton is the thing the worker-read audit exists to
* catch. wasapi does the same at sthread_create. */
retro_atomic_int_t *reinit_request;
#ifdef HAVE_THREADS
/* Parked on by the writer; notified from JACK's process and
* shutdown callbacks. An eventcount, not a lock and condition
@@ -107,7 +102,8 @@ typedef struct jack
int clk_have_anchor;
double clk_sx, clk_sy, clk_sxx, clk_sxy, clk_n;
unsigned clk_rate;
retro_atomic_int_t clk_ppm; /* AUDIO_CLOCK_PPM_NONE until known */
retro_atomic_int_t clk_ppm;
retro_atomic_int_t clk_valid;
/* The server saying its rate or its period has changed. Both make
* everything downstream wrong - what the resampler produces, what
* the ring was sized for, how long a write waits - so both ask the
@@ -149,13 +145,9 @@ static size_t ja_read_deinterleaved(jack_t *jd, float *dst[JACK_MAX_PORTS], jack
static bool ja_device_clock_ppm(void *data, double *ppm)
{
jack_t *jd = (jack_t*)data;
int v;
if (!jd)
if (!jd || !retro_atomic_load_acquire_int(&jd->clk_valid))
return false;
v = retro_atomic_load_acquire_int(&jd->clk_ppm);
if (v == AUDIO_CLOCK_PPM_NONE)
return false;
*ppm = (double)v;
*ppm = (double)retro_atomic_load_acquire_int(&jd->clk_ppm);
return true;
}
@@ -229,6 +221,7 @@ static int ja_process_cb(jack_nframes_t nframes, void *data)
if (ppm > -100000.0 && ppm < 100000.0)
{
retro_atomic_store_release_int(&jd->clk_ppm, (int)ppm);
retro_atomic_store_release_int(&jd->clk_valid, 1);
}
}
}
@@ -294,8 +287,8 @@ static int ja_sample_rate_cb(jack_nframes_t nframes, void *data)
if (jd)
{
retro_atomic_store_release_int(&jd->reconfigure, 1);
if (jd->reinit_request)
retro_atomic_store_release_int(jd->reinit_request, 1);
retro_atomic_store_release_int(
&audio_state_get_ptr()->reinit_request, 1);
}
return 0;
}
@@ -306,8 +299,8 @@ static int ja_buffer_size_cb(jack_nframes_t nframes, void *data)
if (jd)
{
retro_atomic_store_release_int(&jd->reconfigure, 1);
if (jd->reinit_request)
retro_atomic_store_release_int(jd->reinit_request, 1);
retro_atomic_store_release_int(
&audio_state_get_ptr()->reinit_request, 1);
}
return 0;
}
@@ -448,15 +441,14 @@ static void *ja_init(const char *device,
goto error;
#endif
jd->reinit_request = &audio_state_get_ptr()->reinit_request;
jd->client = jack_client_open("RetroArch", JackNullOption, NULL);
if (!jd->client)
goto error;
*new_rate = jack_get_sample_rate(jd->client);
jd->clk_rate = *new_rate;
retro_atomic_int_init(&jd->clk_ppm, AUDIO_CLOCK_PPM_NONE);
retro_atomic_int_init(&jd->clk_ppm, 0);
retro_atomic_int_init(&jd->clk_valid, 0);
jack_set_process_callback(jd->client, ja_process_cb, jd);
/* Registered before activation, as JACK requires of a client that
@@ -705,7 +697,6 @@ static void ja_free(void *data)
{
jack_t *jd = (jack_t*)data;
size_t x;
int clk_ppm;
retro_atomic_store_release_int(&jd->shutdown, 1);
@@ -714,10 +705,10 @@ static void ja_free(void *data)
* are measurements, and a clock estimate that is wrong is worse
* than one that is absent. With a device driving the graph this is
* that device; with something else driving it, it is that. */
clk_ppm = retro_atomic_load_acquire_int(&jd->clk_ppm);
if (clk_ppm != AUDIO_CLOCK_PPM_NONE)
if (retro_atomic_load_acquire_int(&jd->clk_valid))
RARCH_LOG("[JACK] Server clock, fitted from the cycle times:"
" %+d ppm against %u Hz.\n", clk_ppm, jd->clk_rate);
" %+d ppm against %u Hz.\n",
retro_atomic_load_acquire_int(&jd->clk_ppm), jd->clk_rate);
else
RARCH_LOG("[JACK] Server clock: not enough usable cycle times to"
" fit one.\n");
+3 -23
View File
@@ -137,10 +137,6 @@ typedef struct al
/* Frames the source has finished playing, for the sink rate
* estimate; see al_frames_consumed(). */
retro_atomic_size_t consumed;
/* Times the source was found STOPPED with a write to make: it had
* played its queue out and the device went quiet for want of
* audio. INITIAL is the first write, which has not started it. */
retro_atomic_size_t underruns;
} al_t;
static void al_free(void *data)
@@ -331,14 +327,8 @@ static void *al_init(const char *device, unsigned rate, unsigned latency,
* OpenAL Soft extension, so asked for rather than assumed: core
* OpenAL has nothing of the kind and Apple's does not carry it. */
if (alcIsExtensionPresent(al->handle, "ALC_SOFT_device_clock"))
{
/* Through a union, as al_init_events() resolves its own: an
* object pointer cast to a function pointer is not legal C. */
union { void *p; al_get_integer64v_t f; } clk;
clk.p = alcGetProcAddress(al->handle, "alcGetInteger64vSOFT");
if (clk.p)
al->clk_get = clk.f;
}
al->clk_get = (al_get_integer64v_t)alcGetProcAddress(
al->handle, "alcGetInteger64vSOFT");
RARCH_LOG("[OpenAL] Device clock: %s.\n",
al->clk_get ? "reported by the implementation (ALC_SOFT_device_clock)"
: "not offered by this implementation");
@@ -614,11 +604,7 @@ static ssize_t al_write(void *data, const void *s, size_t len)
ALint val;
alGetSourcei(al->source, AL_SOURCE_STATE, &val);
if (val != AL_PLAYING)
{
if (val == AL_STOPPED)
retro_atomic_fetch_add_size(&al->underruns, 1);
alSourcePlay(al->source);
}
}
return _len;
@@ -798,12 +784,6 @@ static void al_device_list_free(void *u, void *slp)
string_list_free(sl);
}
static size_t al_underruns(void *data)
{
al_t *al = (al_t*)data;
return al ? retro_atomic_load_acquire_size(&al->underruns) : 0;
}
audio_driver_t audio_openal = {
al_init,
al_write,
@@ -821,7 +801,7 @@ audio_driver_t audio_openal = {
NULL, /* write_raw */
al_wait_writable,
al_frames_consumed,
al_underruns,
NULL, /* underruns */
al_layout,
NULL, /* frames_consumed_fallback */
al_device_clock_ppm
+3 -77
View File
@@ -34,7 +34,7 @@
* reached in normal operation - the callback fires as each enqueued
* block finishes - so the value only decides how long a device that
* has stopped consuming takes to be noticed. Matches the flat
* bail-out timeouts in sdl2_audio.c and wasapi.c. */
* bail-out timeouts in sdl_audio.c and wasapi.c. */
#define OPENSL_STALL_TIMEOUT_US 256000
/* Helper macros, COM-style. */
@@ -69,10 +69,6 @@ typedef struct sl
* level 21+); blocks then hold 32-bit float frames and the frontend
* skips its float-to-int16 pass. */
bool use_float;
/* The layout asked for, and the channels the player was created
* with. They agree unless the wider format was refused. */
uint32_t layout;
unsigned channels;
bool nonblock;
bool is_paused;
} sl_t;
@@ -108,9 +104,6 @@ typedef struct sl_shared
* and the ASIO callback count. Written only by the callback, read by
* the frontend through sl_frames_consumed(). */
retro_atomic_size_t consumed;
/* Callbacks that left the queue empty: the device has nothing to
* play next and goes quiet until the writer enqueues again. */
retro_atomic_size_t underruns;
unsigned frames_per_block;
bool park_ready;
} sl_shared_t;
@@ -135,8 +128,7 @@ static void opensl_callback(SLAndroidSimpleBufferQueueItf bq, void *ctx)
/* A player torn down, or replaced, since this was dispatched. */
if (!retro_atomic_load_acquire_int(&sh->live) || bq != sh->bq)
return;
if (retro_atomic_fetch_sub_int(&sh->buffered_blocks, 1) == 1)
retro_atomic_fetch_add_size(&sh->underruns, 1);
retro_atomic_fetch_sub_int(&sh->buffered_blocks, 1);
/* A block the device has played: device time, whatever the writer
* managed to supply. */
retro_atomic_fetch_add_size(&sh->consumed, sh->frames_per_block);
@@ -227,7 +219,6 @@ static void *sl_init(const char *device, unsigned rate, unsigned latency,
{
unsigned i;
unsigned frames_per_block;
unsigned channels;
unsigned frame_size = 2 * sizeof(int16_t);
SLDataFormat_PCM fmt_pcm = {0};
#if defined(ANDROID) && defined(__ANDROID_API__) && (__ANDROID_API__ >= 21)
@@ -263,14 +254,6 @@ static void *sl_init(const char *device, unsigned rate, unsigned latency,
RARCH_LOG("[OpenSL] Requested audio latency: %u ms.\n", latency);
/* Anything wider than stereo needs the PCM_EX format below, which
* is API level 21 and up; without it the count is not one this
* driver can ask for and the stereo path is what runs. */
sl->layout = audio_driver_requested_layout();
channels = audio_layout_channels(sl->layout);
if (channels < 2 || channels > 8)
channels = 2;
GOTO_IF_FAIL(slCreateEngine(&sl->engine_object, 0, NULL, 0, NULL, NULL));
GOTO_IF_FAIL(SLObjectItf_Realize(sl->engine_object, SL_BOOLEAN_FALSE));
GOTO_IF_FAIL(SLObjectItf_GetInterface(sl->engine_object, SL_IID_ENGINE, &sl->engine));
@@ -316,46 +299,10 @@ static void *sl_init(const char *device, unsigned rate, unsigned latency,
audio_sink.pLocator = &loc_outmix;
#if defined(ANDROID) && defined(__ANDROID_API__) && (__ANDROID_API__ >= 21)
/* The layout the frontend wants, where the player takes it. The
* frontend's mask is the channel mask OpenSL asks for - both are
* the WAVEFORMATEXTENSIBLE bits - so it goes across as it is, and
* the frames arrive in its ascending-bit order either way. Refused,
* this falls through to the stereo attempts below unchanged. */
if (channels > 2)
{
fmt_pcm_ex.formatType = SL_ANDROID_DATAFORMAT_PCM_EX;
fmt_pcm_ex.numChannels = channels;
fmt_pcm_ex.sampleRate = rate * 1000; /* milli-Hz */
fmt_pcm_ex.bitsPerSample = 32;
fmt_pcm_ex.containerSize = 32;
fmt_pcm_ex.channelMask = sl->layout;
fmt_pcm_ex.endianness = SL_BYTEORDER_LITTLEENDIAN;
fmt_pcm_ex.representation = SL_ANDROID_PCM_REPRESENTATION_FLOAT;
audio_src.pFormat = &fmt_pcm_ex;
res = SLEngineItf_CreateAudioPlayer(sl->engine, &sl->buffer_queue_object,
&audio_src, &audio_sink, 1, &id, &req);
if (res == SL_RESULT_SUCCESS)
{
sl->use_float = true;
sl->channels = channels;
frame_size = channels * sizeof(float);
RARCH_LOG("[OpenSL] Float output, %u channels.\n", channels);
}
else
{
sl->buffer_queue_object = NULL;
RARCH_LOG("[OpenSL] %u channels refused (0x%x), using stereo.\n",
channels, (unsigned)res);
}
}
/* Float first. API level 21 added SLAndroidDataFormat_PCM_EX; a
* runtime that refuses it (older device, or a libOpenSLES built
* without it) fails player creation, and the 16-bit format below is
* tried in that case. */
if (!sl->use_float)
{
fmt_pcm_ex.formatType = SL_ANDROID_DATAFORMAT_PCM_EX;
fmt_pcm_ex.numChannels = 2;
fmt_pcm_ex.sampleRate = rate * 1000; /* milli-Hz */
@@ -371,7 +318,6 @@ static void *sl_init(const char *device, unsigned rate, unsigned latency,
if (res == SL_RESULT_SUCCESS)
{
sl->use_float = true;
sl->channels = 2;
frame_size = 2 * sizeof(float);
RARCH_LOG("[OpenSL] Float output.\n");
}
@@ -381,7 +327,6 @@ static void *sl_init(const char *device, unsigned rate, unsigned latency,
RARCH_LOG("[OpenSL] Float output refused (0x%x), using 16-bit.\n",
(unsigned)res);
}
}
#endif
if (!sl->use_float)
@@ -398,7 +343,6 @@ static void *sl_init(const char *device, unsigned rate, unsigned latency,
GOTO_IF_FAIL(SLEngineItf_CreateAudioPlayer(sl->engine, &sl->buffer_queue_object,
&audio_src, &audio_sink,
1, &id, &req));
sl->channels = 2;
frame_size = 2 * sizeof(int16_t);
}
GOTO_IF_FAIL(SLObjectItf_Realize(sl->buffer_queue_object, SL_BOOLEAN_FALSE));
@@ -635,22 +579,6 @@ static bool sl_use_float(void *data)
return sl->use_float;
}
static size_t sl_underruns(void *data)
{
sl_t *sl = (sl_t*)data;
if (!sl)
return 0;
return retro_atomic_load_acquire_size(&sl_shared.underruns);
}
static uint32_t sl_layout(void *data)
{
sl_t *sl = (sl_t*)data;
if (!sl || sl->channels != audio_layout_channels(sl->layout))
return AUDIO_LAYOUT_STEREO;
return sl->layout;
}
audio_driver_t audio_opensl = {
sl_init,
sl_write,
@@ -667,7 +595,5 @@ audio_driver_t audio_opensl = {
sl_buffer_size,
NULL, /* write_raw */
sl_wait_writable,
sl_frames_consumed,
sl_underruns,
sl_layout
sl_frames_consumed
};
+1 -10
View File
@@ -37,9 +37,7 @@
#include "../../config.h"
#endif
#include <retro_miscellaneous.h>
#include "../audio_driver.h"
#include "../audio_device_label.h"
#include "../../verbosity.h"
#ifdef HAVE_OSS_BSD
@@ -83,19 +81,12 @@ static void *oss_init(const char *device,
unsigned *new_out_rate)
{
int frags, frag, channels, format, new_rate;
char oss_device[PATH_MAX_LENGTH];
oss_audio_t *ossaudio = (oss_audio_t*)calloc(1, sizeof(oss_audio_t));
const char *oss_device = device ? device : DEFAULT_OSS_DEV;
if (!ossaudio)
return NULL;
/* The device may be a list entry, "/dev/dsp0 (card)": the path is
* what is opened. */
if (device)
audio_device_label_path(oss_device, sizeof(oss_device), device);
else
strlcpy(oss_device, DEFAULT_OSS_DEV, sizeof(oss_device));
if ((ossaudio->fd = open(oss_device, O_WRONLY)) < 0)
{
free(ossaudio);
+10 -14
View File
@@ -61,10 +61,6 @@ typedef struct pipewire_audio
* way. Written only by the callback, read by the frontend through
* pwire_frames_consumed(). */
retro_atomic_size_t consumed;
/* Quanta the graph asked for and this driver had nothing at all
* for, so the whole buffer went out as silence. A short read is
* not one: that hands the graph fewer frames, not silence. */
retro_atomic_size_t underruns;
/* The device clock, fitted from the time report the graph already
* hands over.
@@ -109,6 +105,7 @@ static size_t pwire_calc_frame_size(enum spa_audio_format fmt, uint32_t nchannel
case SPA_AUDIO_FORMAT_F32_BE:
case SPA_AUDIO_FORMAT_F32_LE:
return 4 * nchannels;
break;
default:
RARCH_ERR("[PipeWire] Bad spa_audio_format %d.\n", fmt);
break;
@@ -409,6 +406,13 @@ static bool pwire_microphone_mic_alive(const void *driver_context, const void *m
return pw_stream_get_state(mic->stream, &error) == PW_STREAM_STATE_STREAMING;
}
static void pwire_microphone_set_nonblock_state(void *driver_context, bool nonblock)
{
pipewire_core_t *pw = (pipewire_core_t*)driver_context;
if (pw)
pw->nonblock = nonblock;
}
static struct string_list *pwire_microphone_device_list_new(const void *driver_context)
{
pipewire_core_t *pw = (pipewire_core_t*)driver_context;
@@ -604,6 +608,7 @@ microphone_driver_t microphone_pipewire = {
pwire_microphone_init,
pwire_microphone_free,
pwire_microphone_read,
pwire_microphone_set_nonblock_state,
"pipewire",
pwire_microphone_device_list_new,
pwire_microphone_device_list_free,
@@ -657,11 +662,8 @@ static void pwire_playback_process_cb(void *data)
avail = spa_ringbuffer_get_read_index(&audio->ring, &idx);
if (avail <= 0)
{
/* fill rest buffer with silence */
memset(p, 0x00, n_bytes);
retro_atomic_fetch_add_size(&audio->underruns, 1);
}
else
{
if (avail < (int32_t)n_bytes)
@@ -1262,12 +1264,6 @@ static size_t pwire_wait_writable(void *data, size_t len)
return (size_t)avail;
}
static size_t pwire_underruns(void *data)
{
pipewire_audio_t *audio = (pipewire_audio_t*)data;
return audio ? retro_atomic_load_acquire_size(&audio->underruns) : 0;
}
audio_driver_t audio_pipewire = {
pwire_init,
pwire_write,
@@ -1285,7 +1281,7 @@ audio_driver_t audio_pipewire = {
NULL, /* write_raw */
pwire_wait_writable,
pwire_frames_consumed,
pwire_underruns,
NULL, /* underruns */
pwire_layout,
NULL, /* frames_consumed_fallback */
pwire_device_clock_ppm
+11 -96
View File
@@ -20,32 +20,14 @@
#include <kernel.h>
#include <audsrv.h>
#include <retro_timers.h>
#include "../audio_driver.h"
#define AUDIO_BUFFER 128 * 1024
#define AUDIO_CHANNELS 2
#define AUDIO_BITS 16
#define AUDIO_FRAME_BYTES (AUDIO_CHANNELS * (AUDIO_BITS / 8))
/* audsrv_wait_audio() is the SDK's own wait and has no bound, so a
* server that stops draining parks the caller for good. Polling what
* audsrv reports free ends either way: the laps cap the case where it
* keeps draining but never frees enough, and the case where it has
* stopped. Never reached while audio plays - a period frees long
* before this does. */
#define AUDIO_WAIT_LAPS 8
#define AUDIO_WAIT_US 4000
typedef struct ps2_audio
{
/* Bytes handed to audsrv since the format was set. Less what its
* ring still holds, that is what the SPU has played. */
uint64_t bytes_written;
/* What audsrv reported free before anything was queued, which is
* its ring. write_avail() is measured against it. */
size_t ring_size;
/* TODO/FIXME - nonblock is not implemented */
bool nonblock;
bool running;
@@ -55,7 +37,6 @@ static void *ps2_audio_init(const char *device,
unsigned rate, unsigned latency,
unsigned *new_rate)
{
int avail;
struct audsrv_fmt_t format;
ps2_audio_t *ps2 = (ps2_audio_t*)calloc(1, sizeof(ps2_audio_t));
@@ -69,10 +50,6 @@ static void *ps2_audio_init(const char *device,
audsrv_set_format(&format);
audsrv_set_volume(MAX_VOLUME);
/* Asked with nothing queued, so what is free is the whole ring. */
avail = audsrv_available();
ps2->ring_size = (avail > 0) ? (size_t)avail : (size_t)AUDIO_BUFFER;
return ps2;
}
@@ -89,14 +66,10 @@ static void ps2_audio_free(void *data)
static ssize_t ps2_audio_write(void *data, const void *s, size_t len)
{
int ret;
ps2_audio_t* ps2 = (ps2_audio_t*)data;
if (!ps2->running)
return -1;
ret = audsrv_play_audio(s, len);
if (ret > 0)
ps2->bytes_written += (uint64_t)ret;
return ret;
return audsrv_play_audio(s, len);
}
static bool ps2_audio_alive(void *data)
@@ -134,72 +107,16 @@ static void ps2_audio_set_nonblock_state(void *data, bool toggle)
ps2->nonblock = toggle;
}
/* What audsrv says is free in its ring: a measurement, not the whole
* buffer reported as free every frame, which is what this used to do
* and what rate control answered with its full upward correction for
* as long as it was on. */
static size_t ps2_audio_write_avail(void *data)
{
int avail;
ps2_audio_t *ps2 = (ps2_audio_t*)data;
if (!ps2 || !ps2->running)
return 0;
if ((avail = audsrv_available()) <= 0)
return 0;
if ((size_t)avail > ps2->ring_size)
return ps2->ring_size;
return (size_t)avail;
}
/* Frames the SPU has played: what was handed over, less what audsrv
* still holds for it. */
static size_t ps2_audio_frames_consumed(void *data)
{
int queued;
uint64_t played;
ps2_audio_t *ps2 = (ps2_audio_t*)data;
if (!ps2)
return 0;
if ((queued = audsrv_queued()) < 0)
queued = 0;
played = ((uint64_t)queued >= ps2->bytes_written)
? 0 : ps2->bytes_written - (uint64_t)queued;
return (size_t)(played / AUDIO_FRAME_BYTES);
}
static size_t ps2_audio_wait_writable(void *data, size_t len)
{
int laps = AUDIO_WAIT_LAPS;
ps2_audio_t *ps2 = (ps2_audio_t*)data;
if (!ps2 || !ps2->running)
return 0;
/* Capped at half the ring, so the wait always has an end within a
* draining server's reach. */
if (len > ps2->ring_size / 2)
len = ps2->ring_size / 2;
for (;;)
{
size_t avail = ps2_audio_write_avail(ps2);
if (avail >= len)
return avail;
if (--laps < 0)
break;
retro_sleep_us(AUDIO_WAIT_US);
}
return 0;
}
/* No write_avail(): the driver hands audio to audsrv and has nothing
* to measure its fill with. It used to report the whole buffer as free
* whenever it was running, which the rate control read as an empty
* device every frame and answered with its full upward correction,
* permanently - audio a half percent fast, and sharp, for as long as
* rate control was on. With no write_avail() the frontend disables
* rate control for this driver and says so, which is the truth. */
static bool ps2_audio_use_float(void *data) { return false; }
static size_t ps2_audio_buffer_size(void *data)
{
ps2_audio_t *ps2 = (ps2_audio_t*)data;
return ps2 ? ps2->ring_size : (size_t)AUDIO_BUFFER;
}
static size_t ps2_audio_buffer_size(void *data) { return AUDIO_BUFFER; }
audio_driver_t audio_ps2 = {
ps2_audio_init,
@@ -213,9 +130,7 @@ audio_driver_t audio_ps2 = {
"ps2",
NULL,
NULL,
ps2_audio_write_avail,
NULL, /* write_avail */
ps2_audio_buffer_size,
NULL, /* write_raw */
ps2_audio_wait_writable,
ps2_audio_frames_consumed
NULL /* write_raw */
};
+14 -92
View File
@@ -16,9 +16,7 @@
#include <stdlib.h>
#include <string.h>
#include <malloc.h>
#include <retro_atomic.h>
#include <retro_spsc.h>
#include <defines/ps3_defines.h>
@@ -52,24 +50,7 @@ typedef struct
uint32_t audio_port;
bool nonblock;
bool started;
/* Set by the thread tearing the driver down, read by the output
* thread's loop. An atomic and not a volatile bool: volatile orders
* nothing between the two. */
retro_atomic_int_t quit_thread;
/* Blocks the output thread had nothing for and sent as silence. */
retro_atomic_size_t underruns;
/* One block, handed to audioAddData() as it comes out of the ring.
* On the heap and not the output thread's stack: that thread is
* created with 4 KiB, which a stereo block already half fills and
* a six channel one would run straight past. Aligned as the stack
* copy was. */
float *out_block;
size_t out_block_size;
/* The layout asked for, and the channels the port was opened with.
* They agree unless the port refused the count, which is what
* layout() reports on. */
uint32_t layout;
unsigned channels;
volatile bool quit_thread;
} ps3_audio_t;
@@ -79,30 +60,27 @@ static void ps3_event_loop(void *data)
static void ps3_event_loop(uint64_t data)
#endif
{
float out_tmp[AUDIO_BLOCK_SAMPLES * AUDIO_CHANNELS]
__attribute__((aligned(16)));
sys_event_queue_t id;
sys_ipc_key_t key;
sys_event_t event;
ps3_audio_t *aud = (ps3_audio_t*)(uintptr_t)data;
float *out_block = aud->out_block;
size_t block = aud->out_block_size;
audioCreateNotifyEventQueue(&id, &key);
audioSetNotifyEventQueue(key);
while (!retro_atomic_load_acquire_int(&aud->quit_thread))
while (!aud->quit_thread)
{
sysEventQueueReceive(id, &event, PS3_SYS_NO_TIMEOUT);
if (retro_spsc_read_avail(&aud->ring) >= block)
retro_spsc_read(&aud->ring, out_block, block);
if (retro_spsc_read_avail(&aud->ring) >= sizeof(out_tmp))
retro_spsc_read(&aud->ring, out_tmp, sizeof(out_tmp));
else
{
memset(out_block, 0, block);
retro_atomic_fetch_add_size(&aud->underruns, 1);
}
memset(out_tmp, 0, sizeof(out_tmp));
sysLwCondSignal(&aud->cond);
audioAddData(aud->audio_port, out_block,
audioAddData(aud->audio_port, out_tmp,
AUDIO_BLOCK_SAMPLES, 1.0);
}
@@ -114,7 +92,6 @@ static void *ps3_audio_init(const char *device,
unsigned rate, unsigned latency,
unsigned *new_rate)
{
unsigned channels;
audioPortParam params;
ps3_audio_t *data = NULL;
#ifdef __PSL1GHT__
@@ -135,16 +112,7 @@ static void *ps3_audio_init(const char *device,
audioInit();
/* The layout the frontend wants, where the port takes that many
* channels: libaudio opens two, six or eight, so a four channel
* mask is not one of them and opens as stereo. layout() reports
* what was granted. */
data->layout = audio_driver_requested_layout();
channels = audio_layout_channels(data->layout);
if (channels != 2 && channels != 6 && channels != 8)
channels = AUDIO_CHANNELS;
params.numChannels = channels;
params.numChannels = AUDIO_CHANNELS;
params.numBlocks = AUDIO_BLOCKS;
params.param_attrib = 0;
#if 0
@@ -156,40 +124,16 @@ static void *ps3_audio_init(const char *device,
if (audioPortOpen(&params, &data->audio_port) != CELL_OK)
{
/* A port that would not take the wider count still takes two. */
if (channels == AUDIO_CHANNELS)
{
audioQuit();
free(data);
return NULL;
}
channels = AUDIO_CHANNELS;
params.numChannels = channels;
if (audioPortOpen(&params, &data->audio_port) != CELL_OK)
{
audioQuit();
free(data);
return NULL;
}
}
data->channels = channels;
data->out_block_size = AUDIO_BLOCK_SAMPLES * channels * sizeof(float);
data->out_block = (float*)memalign(16, data->out_block_size);
if (!data->out_block)
{
audioPortClose(data->audio_port);
audioQuit();
free(data);
return NULL;
}
data->ring_size = AUDIO_BLOCK_SAMPLES *
channels * AUDIO_BLOCKS * sizeof(float);
AUDIO_CHANNELS * AUDIO_BLOCKS * sizeof(float);
data->ring_init = retro_spsc_init(&data->ring, data->ring_size);
if (!data->ring_init)
{
free(data->out_block);
audioPortClose(data->audio_port);
audioQuit();
free(data);
@@ -201,8 +145,6 @@ static void *ps3_audio_init(const char *device,
audioPortStart(data->audio_port);
data->started = true;
retro_atomic_int_init(&data->quit_thread, 0);
retro_atomic_size_init(&data->underruns, 0);
sysThreadCreate(&data->thread, ps3_event_loop,
#ifdef __PSL1GHT__
data,
@@ -248,8 +190,7 @@ static ssize_t ps3_audio_write(void *data, const void *s, size_t len)
int laps = PS3_AUDIO_WAIT_LAPS;
while (ps3_audio_write_avail(aud) < len)
{
if ( !aud->started
|| retro_atomic_load_acquire_int(&aud->quit_thread))
if (!aud->started || aud->quit_thread)
return 0;
ps3_audio_wait_block(aud);
if (--laps < 0)
@@ -303,7 +244,7 @@ static void ps3_audio_free(void *data)
uint64_t val;
ps3_audio_t *aud = data;
retro_atomic_store_release_int(&aud->quit_thread, 1);
aud->quit_thread = true;
ps3_audio_start(aud, false);
sysThreadJoin(aud->thread, &val);
@@ -316,7 +257,6 @@ static void ps3_audio_free(void *data)
sysLwMutexDestroy(&aud->cond_lock);
sysLwCondDestroy(&aud->cond);
free(aud->out_block);
free(data);
}
@@ -353,8 +293,7 @@ static size_t ps3_audio_wait_writable(void *data, size_t len)
for (;;)
{
if ( !aud->started
|| retro_atomic_load_acquire_int(&aud->quit_thread))
if (!aud->started || aud->quit_thread)
return 0;
avail = ps3_audio_write_avail(aud);
if (avail >= len)
@@ -368,20 +307,6 @@ static size_t ps3_audio_wait_writable(void *data, size_t len)
}
}
static uint32_t ps3_audio_layout(void *data)
{
ps3_audio_t *aud = (ps3_audio_t*)data;
if (!aud || aud->channels != audio_layout_channels(aud->layout))
return AUDIO_LAYOUT_STEREO;
return aud->layout;
}
static size_t ps3_audio_underruns(void *data)
{
ps3_audio_t *aud = (ps3_audio_t*)data;
return aud ? retro_atomic_load_acquire_size(&aud->underruns) : 0;
}
audio_driver_t audio_ps3 = {
ps3_audio_init,
ps3_audio_write,
@@ -397,8 +322,5 @@ audio_driver_t audio_ps3 = {
ps3_audio_write_avail,
ps3_audio_buffer_size,
NULL, /* write_raw */
ps3_audio_wait_writable,
NULL, /* frames_consumed */
ps3_audio_underruns,
ps3_audio_layout
ps3_audio_wait_writable
};
-527
View File
@@ -1,527 +0,0 @@
/* RetroArch - A frontend for libretro.
* Copyright (C) 2010-2014 - Hans-Kristian Arntzen
* Copyright (C) 2011-2017 - Daniel De Matteis
* Copyright (C) 2014-2017 - Ali Bouhlel
*
* RetroArch is free software: you can redistribute it and/or modify it under the terms
* of the GNU General Public License as published by the Free Software Found-
* ation, either version 3 of the License, or (at your option) any later version.
*
* RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with RetroArch.
* If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdint.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <rthreads/rthreads.h>
#include <rthreads/retro_eventcount.h>
#include <retro_atomic.h>
#include <libSceAudioOut.h>
#include <defines/ps4_defines.h>
#include "../audio_driver.h"
typedef struct ps4_audio
{
/* The ring, followed by one period of silence for the worker
* to output when the ring runs short. */
uint32_t* buffer_u32;
sthread_t *worker_thread;
/* The writer's park; the worker notifies after every period it
* frees. Gated: the steady state, writer ahead and never waiting,
* costs the worker nothing per period, where the old
* signal-every-iteration took cond_lock each time. */
retro_eventcount_t park;
/* For the sink rate estimate and the statistics overlay.
* Only the worker writes either. */
retro_atomic_size_t consumed;
retro_atomic_size_t underruns;
int port;
int rate;
/* Frames the ring holds. Set before the worker starts, and
* fixed for the life of the driver. */
unsigned ring;
/* The ring's index pair, the SPSC discipline by hand because the
* worker consumes in place - ps4->buffer_u32 + read_pos goes straight
* into the output syscall, and a retro_spsc would add a bounce
* copy per period. Producer publishes write_pos with release
* after the samples land; consumer publishes read_pos with
* release after the syscall returns; each side reads the other's
* index with acquire and its own relaxed. fifo_lock guarded
* nothing else, and is gone. */
retro_atomic_int_t read_pos;
retro_atomic_int_t write_pos;
retro_atomic_int_t running;
bool nonblock;
} ps4_audio_t;
#define AUDIO_OUT_COUNT 512u
/* The ring is what the latency setting asks for, so it is a whole
* number of periods rather than a power of two and the wrap is a
* compare. The window the device is playing stays inside the ring
* as held until the output call after it returns (see the worker),
* so the writer's room is the ring less that window less one frame.
* Six periods is the floor: five of room, which holds a delivery of
* a video frame of audio - 1600 frames for 30 Hz content at 48 kHz -
* beside the window in flight without dipping under a period each
* refill. Anything at or above the default is unaffected. */
#define AUDIO_RING_MIN (AUDIO_OUT_COUNT * 6u)
#define AUDIO_RING_MAX (AUDIO_OUT_COUNT * 64u)
/* Frames held, and frames the writer may still place - one short of
* the ring, so a full ring and an empty one do not both read as
* write_pos == read_pos. */
#define RING_HELD(w, r, n) \
((unsigned)((w) >= (r) ? (w) - (r) : (n) - (r) + (w)))
#define RING_FREE(w, r, n) ((n) - 1u - RING_HELD((w), (r), (n)))
/* Bound on any wait for the audio thread to consume: one wait, and how
* many of them before the caller gets the pass back. The thread
* consumes a period every period while the device runs. */
#define PS4_AUDIO_WAIT_US 100000
#define PS4_AUDIO_WAIT_LAPS 8
/* The only rate sceAudioOut opens at. */
#define PS4_AUDIO_RATE 48000
/* The ring the latency setting asks for: that many milliseconds at
* the rate the device opened at, in whole periods so no window the
* worker hands over crosses the ring's end. The window the device is
* playing is inside it, so this is the whole of the driver's
* buffering and what buffer_size() reports. */
static unsigned ps4_ring_frames(unsigned rate, unsigned latency)
{
unsigned frames = rate * latency / 1000u;
frames = ((frames + AUDIO_OUT_COUNT - 1u) / AUDIO_OUT_COUNT)
* AUDIO_OUT_COUNT;
if (frames < AUDIO_RING_MIN)
return AUDIO_RING_MIN;
if (frames > AUDIO_RING_MAX)
return AUDIO_RING_MAX;
return frames;
}
/* Return port used */
static int ps4_configure_audio(unsigned rate, unsigned *new_rate)
{
/* Every other rate is refused, which leaves the session with no
* audio at all; open at the one it takes and tell the frontend to
* resample to it. */
if (rate != PS4_AUDIO_RATE)
*new_rate = PS4_AUDIO_RATE;
return sceAudioOutOpen(0xff,
SCE_AUDIO_OUT_PORT_TYPE_MAIN, 0, AUDIO_OUT_COUNT,
PS4_AUDIO_RATE, SCE_AUDIO_OUT_MODE_STEREO);
}
static void ps4_audio_mainloop(void *data)
{
ps4_audio_t* ps4 = (ps4_audio_t*)data;
/* The worker's own cursor: the next window to hand over. The
* published read_pos runs one window behind it while a ring
* window is in flight. The output call returns once the window
* handed to the previous call has been output, and with this
* call's window queued and still to be read by the hardware -
* the SDKs document passing NULL as the way to wait for that
* last window - so the window a call hands over is the device's
* until the call after it returns, and is released there. */
uint16_t play_pos = (uint16_t)
retro_atomic_load_relaxed_int(&ps4->read_pos);
while (retro_atomic_load_acquire_int(&ps4->running))
{
bool cond = false;
uint16_t next_pos = play_pos;
uint16_t write_pos = (uint16_t)
retro_atomic_load_acquire_int(&ps4->write_pos);
/* A period in hand is a period to play. Holding one back
* as a reserve only hands the device silence while the
* ring has audio in it. */
cond = RING_HELD(write_pos, play_pos, ps4->ring)
< AUDIO_OUT_COUNT;
if (!cond)
{
next_pos += AUDIO_OUT_COUNT;
if (next_pos >= ps4->ring)
next_pos = 0;
}
else
retro_atomic_fetch_add_size(&ps4->underruns, 1);
sceAudioOutOutput(ps4->port,
ps4->buffer_u32
+ (cond ? ps4->ring : play_pos));
retro_atomic_fetch_add_size(&ps4->consumed, AUDIO_OUT_COUNT);
/* The previous call's window has been output; this call's is
* the one in flight, so everything before play_pos is the
* writer's. When this call handed over silence, play_pos is
* where read_pos already stands. */
retro_atomic_store_release_int(&ps4->read_pos, play_pos);
play_pos = next_pos;
retro_eventcount_notify(&ps4->park);
}
return;
}
static void *ps4_audio_init(const char *device,
unsigned rate, unsigned latency,
unsigned *new_rate)
{
int port;
ps4_audio_t *ps4 = (ps4_audio_t*)calloc(1, sizeof(ps4_audio_t));
if (!ps4)
return NULL;
/* The library is what opens the port, so it comes up first. */
sceAudioOutInit();
if ((port = ps4_configure_audio(rate, new_rate)) < 0)
{
free(ps4);
return NULL;
}
ps4->rate = *new_rate ? *new_rate : rate;
ps4->ring = ps4_ring_frames(ps4->rate, latency);
/* Cache aligned, not necessary but helpful. */
/* The ring, and one period of silence past its end. */
ps4->buffer_u32 = (uint32_t*)calloc(ps4->ring + AUDIO_OUT_COUNT,
sizeof(uint32_t));
retro_atomic_size_init(&ps4->consumed, 0);
retro_atomic_size_init(&ps4->underruns, 0);
retro_atomic_int_init(&ps4->read_pos, 0);
retro_atomic_int_init(&ps4->write_pos, 0);
ps4->port = port;
if ( !ps4->buffer_u32
|| !retro_eventcount_init(&ps4->park))
{
sceAudioOutClose(port);
free(ps4->buffer_u32);
free(ps4);
return NULL;
}
ps4->nonblock = false;
/* running has to be set before the worker starts, so a worker that
* never starts leaves a driver reporting itself alive with nothing
* consuming the ring: writes wait out their laps and return zero,
* and teardown acts on a state that was never true. */
retro_atomic_int_init(&ps4->running, 1);
if (!(ps4->worker_thread = sthread_create(ps4_audio_mainloop, ps4)))
{
retro_eventcount_free(&ps4->park);
sceAudioOutClose(port);
free(ps4->buffer_u32);
free(ps4);
return NULL;
}
return ps4;
}
static void ps4_audio_free(void *data)
{
ps4_audio_t* ps4 = (ps4_audio_t*)data;
if (!ps4)
return;
if (retro_atomic_load_acquire_int(&ps4->running))
{
if (ps4->worker_thread)
{
retro_atomic_store_release_int(&ps4->running, 0);
sthread_join(ps4->worker_thread);
}
}
retro_eventcount_free(&ps4->park);
free(ps4->buffer_u32);
ps4->worker_thread = NULL;
sceAudioOutClose(ps4->port);
free(ps4);
}
static ssize_t ps4_audio_write(void *data, const void *s, size_t len)
{
ps4_audio_t* ps4 = (ps4_audio_t*)data;
uint16_t write_pos = (uint16_t)
retro_atomic_load_relaxed_int(&ps4->write_pos);
/* Frames, and wide enough for them: narrowed to uint16_t, a write of
* more than 65535 frames wrapped to a small count that the room check
* then waved through, and the copy below took len bytes for it. The
* ring holds one frame short of its length, so no wait can free room
* for more than that at once - take what fits and report the short
* write rather than spending the laps on room that will not come. A
* len that is not whole frames carries the frames it has. */
size_t sample_count = len / sizeof(uint32_t);
if (!retro_atomic_load_acquire_int(&ps4->running))
return -1;
if (!sample_count)
return 0;
if (sample_count > (size_t)(ps4->ring - 1u))
sample_count = ps4->ring - 1u;
len = sample_count * sizeof(uint32_t);
/* The ring is counted in uint32_t frames (write_pos, read_pos,
* ring); len is bytes. Both room checks below used to
* compare the frame count against len, i.e. demanded four times the
* room actually needed: non-blocking writes were refused - the audio
* dropped - with plenty of space free, and blocking ones waited for
* space that rate control was not trying to free. ps4_write_avail()
* and ps4_wait_writable() already convert; compare frames to
* frames here too. */
if (ps4->nonblock)
{
if (RING_FREE(write_pos, (uint16_t)
retro_atomic_load_acquire_int(&ps4->read_pos),
ps4->ring) < sample_count)
return 0;
}
{
/* The audio thread notifies every period it consumes. One that
* has stopped consuming - the device suspended, the thread gone
* - notifies nothing; each wait is bounded, the loop is
* lap-bounded, and the write then returns having written
* nothing rather than holding the caller. Room and liveness
* are re-checked inside the eventcount's window, so a period
* freed between the check and the park costs nothing. */
int laps = PS4_AUDIO_WAIT_LAPS;
while (RING_FREE(write_pos, (uint16_t)
retro_atomic_load_acquire_int(&ps4->read_pos),
ps4->ring) < sample_count)
{
int key;
if (--laps < 0 || !retro_atomic_load_acquire_int(&ps4->running))
return 0;
key = retro_eventcount_prepare_wait(&ps4->park);
if ( (RING_FREE(write_pos, (uint16_t)
retro_atomic_load_acquire_int(&ps4->read_pos),
ps4->ring) >= sample_count)
|| !retro_atomic_load_acquire_int(&ps4->running))
{
retro_eventcount_cancel_wait(&ps4->park);
continue;
}
if (!retro_eventcount_commit_wait_timeout(&ps4->park, key,
PS4_AUDIO_WAIT_US))
continue;
}
}
if ((write_pos + sample_count) > ps4->ring)
{
memcpy(ps4->buffer_u32 + write_pos, s,
(ps4->ring - write_pos) * sizeof(uint32_t));
memcpy(ps4->buffer_u32, (uint32_t*)s +
(ps4->ring - write_pos),
(write_pos + sample_count - ps4->ring) * sizeof(uint32_t));
}
else
memcpy(ps4->buffer_u32 + write_pos, s, len);
write_pos = (uint16_t)(write_pos + sample_count);
if (write_pos >= ps4->ring)
write_pos -= ps4->ring;
/* Release: the samples land before the index that publishes
* them. */
retro_atomic_store_release_int(&ps4->write_pos, write_pos);
return len;
}
static bool ps4_audio_alive(void *data)
{
ps4_audio_t* ps4 = (ps4_audio_t*)data;
if (!ps4)
return false;
return retro_atomic_load_acquire_int(&ps4->running) != 0;
}
static bool ps4_audio_stop(void *data)
{
ps4_audio_t* ps4 = (ps4_audio_t*)data;
if (ps4)
{
retro_atomic_store_release_int(&ps4->running, 0);
/* A writer parked on the ring must see the flag drop; the
* worker may already be gone and notify nothing further. */
retro_eventcount_notify(&ps4->park);
if (ps4->worker_thread)
{
sthread_join(ps4->worker_thread);
ps4->worker_thread = NULL;
}
}
return true;
}
static bool ps4_audio_start(void *data, bool is_shutdown)
{
ps4_audio_t* ps4 = (ps4_audio_t*)data;
if (ps4 && !retro_atomic_load_acquire_int(&ps4->running))
{
if (!ps4->worker_thread)
{
retro_atomic_store_release_int(&ps4->running, 1);
if (!(ps4->worker_thread = sthread_create(ps4_audio_mainloop, ps4)))
{
retro_atomic_store_release_int(&ps4->running, 0);
return false;
}
}
}
return true;
}
static void ps4_audio_set_nonblock_state(void *data, bool toggle)
{
ps4_audio_t* ps4 = (ps4_audio_t*)data;
if (ps4)
ps4->nonblock = toggle;
}
static size_t ps4_write_avail(void *data)
{
size_t _len;
ps4_audio_t* ps4 = (ps4_audio_t*)data;
if (!ps4 || !retro_atomic_load_acquire_int(&ps4->running))
return 0;
_len = RING_FREE((uint16_t)
retro_atomic_load_relaxed_int(&ps4->write_pos), (uint16_t)
retro_atomic_load_acquire_int(&ps4->read_pos), ps4->ring);
return _len * sizeof(uint32_t);
}
/* Sleep on the condition the output thread signals after every block
* until the fifo has room for len, in the same units ps4_audio_write()
* compares against, capped at half the fifo so the wait always ends.
* Returns the free space as ps4_write_avail() reports it, or 0 when
* the output thread is not running. */
static size_t ps4_wait_writable(void *data, size_t len)
{
ps4_audio_t* ps4 = (ps4_audio_t*)data;
size_t avail;
int laps = PS4_AUDIO_WAIT_LAPS;
/* len arrives in bytes; the ring is counted in uint32_t frames. */
size_t want = len / sizeof(uint32_t);
if (want > ps4->ring / 2)
want = ps4->ring / 2;
for (;;)
{
int key;
if (!retro_atomic_load_acquire_int(&ps4->running))
return 0;
avail = RING_FREE((uint16_t)
retro_atomic_load_relaxed_int(&ps4->write_pos), (uint16_t)
retro_atomic_load_acquire_int(&ps4->read_pos), ps4->ring);
if (avail >= want)
break;
/* Bounded per wait and overall: a thread that has stopped
* consuming hands the pass back as no space coming from this
* call. The room is re-checked inside the window. */
if (--laps < 0)
return 0;
key = retro_eventcount_prepare_wait(&ps4->park);
if (RING_FREE((uint16_t)
retro_atomic_load_relaxed_int(&ps4->write_pos), (uint16_t)
retro_atomic_load_acquire_int(&ps4->read_pos),
ps4->ring) >= want
|| !retro_atomic_load_acquire_int(&ps4->running))
{
retro_eventcount_cancel_wait(&ps4->park);
continue;
}
if (!retro_eventcount_commit_wait_timeout(&ps4->park, key,
PS4_AUDIO_WAIT_US))
continue;
}
return avail * sizeof(uint32_t);
}
/* sceAudioOut is opened in SCE_AUDIO_OUT_MODE_STEREO, which is 16-bit
* PCM; float output would need a different port mode. */
static bool ps4_audio_use_float(void *data) { return false; }
static size_t ps4_buffer_size(void *data)
{
ps4_audio_t* ps4 = (ps4_audio_t*)data;
if (!ps4)
return 0;
/* In bytes: the ring, in uint32_t frames of int16 stereo. The
* window the device is playing is held inside it. */
return ps4->ring * sizeof(uint32_t);
}
/* Frames the device has taken since init: the output call returns
* when it has taken the one before, so the worker counts a period
* per completed call. */
static size_t ps4_frames_consumed(void *data)
{
ps4_audio_t* ps4 = (ps4_audio_t*)data;
if (!ps4)
return 0;
return retro_atomic_load_acquire_size(&ps4->consumed);
}
/* Periods played as silence for want of audio. */
static size_t ps4_underruns(void *data)
{
ps4_audio_t* ps4 = (ps4_audio_t*)data;
if (!ps4)
return 0;
return retro_atomic_load_acquire_size(&ps4->underruns);
}
audio_driver_t audio_ps4 = {
ps4_audio_init,
ps4_audio_write,
ps4_audio_stop,
ps4_audio_start,
ps4_audio_alive,
ps4_audio_set_nonblock_state,
ps4_audio_free,
ps4_audio_use_float,
"orbis",
NULL,
NULL,
ps4_write_avail,
ps4_buffer_size,
NULL, /* write_raw */
ps4_wait_writable,
ps4_frames_consumed,
ps4_underruns
};
-523
View File
@@ -1,523 +0,0 @@
/* RetroArch - A frontend for libretro.
* Copyright (C) 2010-2014 - Hans-Kristian Arntzen
* Copyright (C) 2011-2017 - Daniel De Matteis
* Copyright (C) 2014-2017 - Ali Bouhlel
*
* RetroArch is free software: you can redistribute it and/or modify it under the terms
* of the GNU General Public License as published by the Free Software Found-
* ation, either version 3 of the License, or (at your option) any later version.
*
* RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with RetroArch.
* If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdint.h>
#include <malloc.h>
#include <stdio.h>
#include <string.h>
#include <rthreads/rthreads.h>
#include <rthreads/retro_eventcount.h>
#include <retro_atomic.h>
#include <psp2/audioout.h>
#include "../audio_driver.h"
typedef struct psp2_audio
{
/* The ring, followed by one period of silence for the worker
* to output when the ring runs short. */
uint32_t* buffer_u32;
sthread_t *worker_thread;
/* The writer's park; the worker notifies after every period it
* frees. Gated: the steady state, writer ahead and never waiting,
* costs the worker nothing per period, where the old
* signal-every-iteration took cond_lock each time. */
retro_eventcount_t park;
/* For the sink rate estimate and the statistics overlay.
* Only the worker writes either. */
retro_atomic_size_t consumed;
retro_atomic_size_t underruns;
int port;
int rate;
/* Frames the ring holds. Set before the worker starts, and
* fixed for the life of the driver. */
unsigned ring;
/* The ring's index pair, the SPSC discipline by hand because the
* worker consumes in place - psp->buffer_u32 + read_pos goes straight
* into the output syscall, and a retro_spsc would add a bounce
* copy per period. Producer publishes write_pos with release
* after the samples land; consumer publishes read_pos with
* release after the syscall returns; each side reads the other's
* index with acquire and its own relaxed. fifo_lock guarded
* nothing else, and is gone. */
retro_atomic_int_t read_pos;
retro_atomic_int_t write_pos;
retro_atomic_int_t running;
bool nonblock;
} psp2_audio_t;
#define AUDIO_OUT_COUNT 512u
/* The ring is what the latency setting asks for, so it is a whole
* number of periods rather than a power of two and the wrap is a
* compare. The window the device is playing stays inside the ring
* as held until the output call after it returns (see the worker),
* so the writer's room is the ring less that window less one frame.
* Six periods is the floor: five of room, which holds a delivery of
* a video frame of audio - 1600 frames for 30 Hz content at 48 kHz -
* beside the window in flight without dipping under a period each
* refill. Anything at or above the default is unaffected. */
#define AUDIO_RING_MIN (AUDIO_OUT_COUNT * 6u)
#define AUDIO_RING_MAX (AUDIO_OUT_COUNT * 64u)
/* Frames held, and frames the writer may still place - one short of
* the ring, so a full ring and an empty one do not both read as
* write_pos == read_pos. */
#define RING_HELD(w, r, n) \
((unsigned)((w) >= (r) ? (w) - (r) : (n) - (r) + (w)))
#define RING_FREE(w, r, n) ((n) - 1u - RING_HELD((w), (r), (n)))
/* Bound on any wait for the audio thread to consume: one wait, and how
* many of them before the caller gets the pass back. The thread
* consumes a period every period while the device runs. */
#define PSP2_AUDIO_WAIT_US 100000
#define PSP2_AUDIO_WAIT_LAPS 8
/* The only rate the main port opens at. */
#define PSP2_AUDIO_RATE 48000
/* The ring the latency setting asks for: that many milliseconds at
* the rate the device opened at, in whole periods so no window the
* worker hands over crosses the ring's end. The window the device is
* playing is inside it, so this is the whole of the driver's
* buffering and what buffer_size() reports. */
static unsigned psp2_ring_frames(unsigned rate, unsigned latency)
{
unsigned frames = rate * latency / 1000u;
frames = ((frames + AUDIO_OUT_COUNT - 1u) / AUDIO_OUT_COUNT)
* AUDIO_OUT_COUNT;
if (frames < AUDIO_RING_MIN)
return AUDIO_RING_MIN;
if (frames > AUDIO_RING_MAX)
return AUDIO_RING_MAX;
return frames;
}
/* Return port used */
static int psp2_configure_audio(unsigned rate, unsigned *new_rate)
{
/* The main port refuses every other rate, which leaves the session
* with no audio at all; open at the one it takes and tell the
* frontend to resample to it. */
if (rate != PSP2_AUDIO_RATE)
*new_rate = PSP2_AUDIO_RATE;
return sceAudioOutOpenPort(
SCE_AUDIO_OUT_PORT_TYPE_MAIN, AUDIO_OUT_COUNT,
PSP2_AUDIO_RATE, SCE_AUDIO_OUT_MODE_STEREO);
}
static void psp2_audio_mainloop(void *data)
{
psp2_audio_t* psp = (psp2_audio_t*)data;
/* The worker's own cursor: the next window to hand over. The
* published read_pos runs one window behind it while a ring
* window is in flight. The output call returns once the window
* handed to the previous call has been output, and with this
* call's window queued and still to be read by the hardware -
* the SDKs document passing NULL as the way to wait for that
* last window - so the window a call hands over is the device's
* until the call after it returns, and is released there. */
uint16_t play_pos = (uint16_t)
retro_atomic_load_relaxed_int(&psp->read_pos);
while (retro_atomic_load_acquire_int(&psp->running))
{
bool cond = false;
uint16_t next_pos = play_pos;
uint16_t write_pos = (uint16_t)
retro_atomic_load_acquire_int(&psp->write_pos);
/* A period in hand is a period to play. Holding one back
* as a reserve only hands the device silence while the
* ring has audio in it. */
cond = RING_HELD(write_pos, play_pos, psp->ring)
< AUDIO_OUT_COUNT;
if (!cond)
{
next_pos += AUDIO_OUT_COUNT;
if (next_pos >= psp->ring)
next_pos = 0;
}
else
retro_atomic_fetch_add_size(&psp->underruns, 1);
sceAudioOutOutput(psp->port,
psp->buffer_u32
+ (cond ? psp->ring : play_pos));
retro_atomic_fetch_add_size(&psp->consumed, AUDIO_OUT_COUNT);
/* The previous call's window has been output; this call's is
* the one in flight, so everything before play_pos is the
* writer's. When this call handed over silence, play_pos is
* where read_pos already stands. */
retro_atomic_store_release_int(&psp->read_pos, play_pos);
play_pos = next_pos;
retro_eventcount_notify(&psp->park);
}
return;
}
static void *psp2_audio_init(const char *device,
unsigned rate, unsigned latency,
unsigned *new_rate)
{
int port;
psp2_audio_t *psp = (psp2_audio_t*)calloc(1, sizeof(psp2_audio_t));
if (!psp)
return NULL;
if ((port = psp2_configure_audio(rate, new_rate)) < 0)
{
free(psp);
return NULL;
}
psp->rate = *new_rate ? *new_rate : rate;
psp->ring = psp2_ring_frames(psp->rate, latency);
/* Cache aligned, not necessary but helpful. */
/* The ring, and one period of silence past its end. */
psp->buffer_u32 = (uint32_t*)calloc(psp->ring + AUDIO_OUT_COUNT,
sizeof(uint32_t));
retro_atomic_size_init(&psp->consumed, 0);
retro_atomic_size_init(&psp->underruns, 0);
retro_atomic_int_init(&psp->read_pos, 0);
retro_atomic_int_init(&psp->write_pos, 0);
psp->port = port;
if ( !psp->buffer_u32
|| !retro_eventcount_init(&psp->park))
{
sceAudioOutReleasePort(port);
free(psp->buffer_u32);
free(psp);
return NULL;
}
psp->nonblock = false;
/* running has to be set before the worker starts, so a worker that
* never starts leaves a driver reporting itself alive with nothing
* consuming the ring: writes wait out their laps and return zero,
* and teardown acts on a state that was never true. */
retro_atomic_int_init(&psp->running, 1);
if (!(psp->worker_thread = sthread_create(psp2_audio_mainloop, psp)))
{
retro_eventcount_free(&psp->park);
sceAudioOutReleasePort(port);
free(psp->buffer_u32);
free(psp);
return NULL;
}
return psp;
}
static void psp2_audio_free(void *data)
{
psp2_audio_t* psp = (psp2_audio_t*)data;
if (!psp)
return;
if (retro_atomic_load_acquire_int(&psp->running))
{
if (psp->worker_thread)
{
retro_atomic_store_release_int(&psp->running, 0);
sthread_join(psp->worker_thread);
}
}
retro_eventcount_free(&psp->park);
free(psp->buffer_u32);
psp->worker_thread = NULL;
sceAudioOutReleasePort(psp->port);
free(psp);
}
static ssize_t psp2_audio_write(void *data, const void *s, size_t len)
{
psp2_audio_t* psp = (psp2_audio_t*)data;
uint16_t write_pos = (uint16_t)
retro_atomic_load_relaxed_int(&psp->write_pos);
/* Frames, and wide enough for them: narrowed to uint16_t, a write of
* more than 65535 frames wrapped to a small count that the room check
* then waved through, and the copy below took len bytes for it. The
* ring holds one frame short of its length, so no wait can free room
* for more than that at once - take what fits and report the short
* write rather than spending the laps on room that will not come. A
* len that is not whole frames carries the frames it has. */
size_t sample_count = len / sizeof(uint32_t);
if (!retro_atomic_load_acquire_int(&psp->running))
return -1;
if (!sample_count)
return 0;
if (sample_count > (size_t)(psp->ring - 1u))
sample_count = psp->ring - 1u;
len = sample_count * sizeof(uint32_t);
/* The ring is counted in uint32_t frames (write_pos, read_pos,
* ring); len is bytes. Both room checks below used to
* compare the frame count against len, i.e. demanded four times the
* room actually needed: non-blocking writes were refused - the audio
* dropped - with plenty of space free, and blocking ones waited for
* space that rate control was not trying to free. psp2_write_avail()
* and psp2_wait_writable() already convert; compare frames to
* frames here too. */
if (psp->nonblock)
{
if (RING_FREE(write_pos, (uint16_t)
retro_atomic_load_acquire_int(&psp->read_pos),
psp->ring) < sample_count)
return 0;
}
{
/* The audio thread notifies every period it consumes. One that
* has stopped consuming - the device suspended, the thread gone
* - notifies nothing; each wait is bounded, the loop is
* lap-bounded, and the write then returns having written
* nothing rather than holding the caller. Room and liveness
* are re-checked inside the eventcount's window, so a period
* freed between the check and the park costs nothing. */
int laps = PSP2_AUDIO_WAIT_LAPS;
while (RING_FREE(write_pos, (uint16_t)
retro_atomic_load_acquire_int(&psp->read_pos),
psp->ring) < sample_count)
{
int key;
if (--laps < 0 || !retro_atomic_load_acquire_int(&psp->running))
return 0;
key = retro_eventcount_prepare_wait(&psp->park);
if ( (RING_FREE(write_pos, (uint16_t)
retro_atomic_load_acquire_int(&psp->read_pos),
psp->ring) >= sample_count)
|| !retro_atomic_load_acquire_int(&psp->running))
{
retro_eventcount_cancel_wait(&psp->park);
continue;
}
if (!retro_eventcount_commit_wait_timeout(&psp->park, key,
PSP2_AUDIO_WAIT_US))
continue;
}
}
if ((write_pos + sample_count) > psp->ring)
{
memcpy(psp->buffer_u32 + write_pos, s,
(psp->ring - write_pos) * sizeof(uint32_t));
memcpy(psp->buffer_u32, (uint32_t*)s +
(psp->ring - write_pos),
(write_pos + sample_count - psp->ring) * sizeof(uint32_t));
}
else
memcpy(psp->buffer_u32 + write_pos, s, len);
write_pos = (uint16_t)(write_pos + sample_count);
if (write_pos >= psp->ring)
write_pos -= psp->ring;
/* Release: the samples land before the index that publishes
* them. */
retro_atomic_store_release_int(&psp->write_pos, write_pos);
return len;
}
static bool psp2_audio_alive(void *data)
{
psp2_audio_t* psp = (psp2_audio_t*)data;
if (!psp)
return false;
return retro_atomic_load_acquire_int(&psp->running) != 0;
}
static bool psp2_audio_stop(void *data)
{
psp2_audio_t* psp = (psp2_audio_t*)data;
if (psp)
{
retro_atomic_store_release_int(&psp->running, 0);
/* A writer parked on the ring must see the flag drop; the
* worker may already be gone and notify nothing further. */
retro_eventcount_notify(&psp->park);
if (psp->worker_thread)
{
sthread_join(psp->worker_thread);
psp->worker_thread = NULL;
}
}
return true;
}
static bool psp2_audio_start(void *data, bool is_shutdown)
{
psp2_audio_t* psp = (psp2_audio_t*)data;
if (psp && !retro_atomic_load_acquire_int(&psp->running))
{
if (!psp->worker_thread)
{
retro_atomic_store_release_int(&psp->running, 1);
if (!(psp->worker_thread = sthread_create(psp2_audio_mainloop, psp)))
{
retro_atomic_store_release_int(&psp->running, 0);
return false;
}
}
}
return true;
}
static void psp2_audio_set_nonblock_state(void *data, bool toggle)
{
psp2_audio_t* psp = (psp2_audio_t*)data;
if (psp)
psp->nonblock = toggle;
}
static size_t psp2_write_avail(void *data)
{
size_t _len;
psp2_audio_t* psp = (psp2_audio_t*)data;
if (!psp || !retro_atomic_load_acquire_int(&psp->running))
return 0;
_len = RING_FREE((uint16_t)
retro_atomic_load_relaxed_int(&psp->write_pos), (uint16_t)
retro_atomic_load_acquire_int(&psp->read_pos), psp->ring);
return _len * sizeof(uint32_t);
}
/* Sleep on the condition the output thread signals after every block
* until the fifo has room for len, in the same units psp2_audio_write()
* compares against, capped at half the fifo so the wait always ends.
* Returns the free space as psp2_write_avail() reports it, or 0 when
* the output thread is not running. */
static size_t psp2_wait_writable(void *data, size_t len)
{
psp2_audio_t* psp = (psp2_audio_t*)data;
size_t avail;
int laps = PSP2_AUDIO_WAIT_LAPS;
/* len arrives in bytes; the ring is counted in uint32_t frames. */
size_t want = len / sizeof(uint32_t);
if (want > psp->ring / 2)
want = psp->ring / 2;
for (;;)
{
int key;
if (!retro_atomic_load_acquire_int(&psp->running))
return 0;
avail = RING_FREE((uint16_t)
retro_atomic_load_relaxed_int(&psp->write_pos), (uint16_t)
retro_atomic_load_acquire_int(&psp->read_pos), psp->ring);
if (avail >= want)
break;
/* Bounded per wait and overall: a thread that has stopped
* consuming hands the pass back as no space coming from this
* call. The room is re-checked inside the window. */
if (--laps < 0)
return 0;
key = retro_eventcount_prepare_wait(&psp->park);
if (RING_FREE((uint16_t)
retro_atomic_load_relaxed_int(&psp->write_pos), (uint16_t)
retro_atomic_load_acquire_int(&psp->read_pos),
psp->ring) >= want
|| !retro_atomic_load_acquire_int(&psp->running))
{
retro_eventcount_cancel_wait(&psp->park);
continue;
}
if (!retro_eventcount_commit_wait_timeout(&psp->park, key,
PSP2_AUDIO_WAIT_US))
continue;
}
return avail * sizeof(uint32_t);
}
/* sceAudioOut takes 16-bit PCM only; there is no float output on the
* Vita hardware or in the kernel API. */
static bool psp2_audio_use_float(void *data) { return false; }
static size_t psp2_buffer_size(void *data)
{
psp2_audio_t* psp = (psp2_audio_t*)data;
if (!psp)
return 0;
/* In bytes: the ring, in uint32_t frames of int16 stereo. The
* window the device is playing is held inside it. */
return psp->ring * sizeof(uint32_t);
}
/* Frames the device has taken since init: the output call returns
* when it has taken the one before, so the worker counts a period
* per completed call. */
static size_t psp2_frames_consumed(void *data)
{
psp2_audio_t* psp = (psp2_audio_t*)data;
if (!psp)
return 0;
return retro_atomic_load_acquire_size(&psp->consumed);
}
/* Periods played as silence for want of audio. */
static size_t psp2_underruns(void *data)
{
psp2_audio_t* psp = (psp2_audio_t*)data;
if (!psp)
return 0;
return retro_atomic_load_acquire_size(&psp->underruns);
}
audio_driver_t audio_psp2 = {
psp2_audio_init,
psp2_audio_write,
psp2_audio_stop,
psp2_audio_start,
psp2_audio_alive,
psp2_audio_set_nonblock_state,
psp2_audio_free,
psp2_audio_use_float,
"vita",
NULL,
NULL,
psp2_write_avail,
psp2_buffer_size,
NULL, /* write_raw */
psp2_wait_writable,
psp2_frames_consumed,
psp2_underruns
};
+127 -203
View File
@@ -16,7 +16,9 @@
*/
#include <stdint.h>
#if defined(VITA) || defined(PSP)
#include <malloc.h>
#endif
#include <stdio.h>
#include <string.h>
@@ -24,16 +26,26 @@
#include <rthreads/retro_eventcount.h>
#include <retro_atomic.h>
#if defined(VITA)
#include <psp2/kernel/processmgr.h>
#include <psp2/kernel/threadmgr.h>
#include <psp2/kernel/sysmem.h>
#include <psp2/audioout.h>
#elif defined(PSP)
#include <pspkernel.h>
#include <pspaudio.h>
#elif defined(ORBIS)
#include <libSceAudioOut.h>
#include <defines/ps4_defines.h>
#include <verbosity.h>
#endif
#include "../audio_driver.h"
typedef struct psp_audio
{
/* The ring, followed by one period of silence for the worker
* to output when the ring runs short. */
uint32_t* buffer_u32;
uint32_t* buffer;
uint32_t* zeroBuffer;
sthread_t *worker_thread;
/* The writer's park; the worker notifies after every period it
@@ -42,20 +54,13 @@ typedef struct psp_audio
* signal-every-iteration took cond_lock each time. */
retro_eventcount_t park;
/* For the sink rate estimate and the statistics overlay.
* Only the worker writes either. */
retro_atomic_size_t consumed;
retro_atomic_size_t underruns;
SceUID thread;
int port;
int rate;
/* Frames the ring holds. Set before the worker starts, and
* fixed for the life of the driver. */
unsigned ring;
/* The ring's index pair, the SPSC discipline by hand because the
* worker consumes in place - psp->buffer_u32 + read_pos goes straight
* worker consumes in place - psp->buffer + read_pos goes straight
* into the output syscall, and a retro_spsc would add a bounce
* copy per period. Producer publishes write_pos with release
* after the samples land; consumer publishes read_pos with
@@ -70,25 +75,8 @@ typedef struct psp_audio
} psp_audio_t;
#define AUDIO_OUT_COUNT 512u
/* The ring is what the latency setting asks for, so it is a whole
* number of periods rather than a power of two and the wrap is a
* compare. The window the device is playing stays inside the ring
* as held until the output call after it returns (see the worker),
* so the writer's room is the ring less that window less one frame.
* Six periods is the floor: five of room, which holds a delivery of
* a video frame of audio - 1600 frames for 30 Hz content at 48 kHz -
* beside the window in flight without dipping under a period each
* refill. Anything at or above the default is unaffected. */
#define AUDIO_RING_MIN (AUDIO_OUT_COUNT * 6u)
#define AUDIO_RING_MAX (AUDIO_OUT_COUNT * 64u)
/* Frames held, and frames the writer may still place - one short of
* the ring, so a full ring and an empty one do not both read as
* write_pos == read_pos. */
#define RING_HELD(w, r, n) \
((unsigned)((w) >= (r) ? (w) - (r) : (n) - (r) + (w)))
#define RING_FREE(w, r, n) ((n) - 1u - RING_HELD((w), (r), (n)))
#define AUDIO_BUFFER_SIZE (1u<<13u)
#define AUDIO_BUFFER_SIZE_MASK (AUDIO_BUFFER_SIZE-1)
/* Bound on any wait for the audio thread to consume: one wait, and how
* many of them before the caller gets the pass back. The thread
@@ -96,92 +84,62 @@ typedef struct psp_audio
#define PSP_AUDIO_WAIT_US 100000
#define PSP_AUDIO_WAIT_LAPS 8
/* The rate the SRC always takes, for a setting it will not. */
#define PSP_AUDIO_RATE_FALLBACK 48000
/* The ring the latency setting asks for: that many milliseconds at
* the rate the device opened at, in whole periods so no window the
* worker hands over crosses the ring's end. The window the device is
* playing is inside it, so this is the whole of the driver's
* buffering and what buffer_size() reports. */
static unsigned psp_ring_frames(unsigned rate, unsigned latency)
{
unsigned frames = rate * latency / 1000u;
frames = ((frames + AUDIO_OUT_COUNT - 1u) / AUDIO_OUT_COUNT)
* AUDIO_OUT_COUNT;
if (frames < AUDIO_RING_MIN)
return AUDIO_RING_MIN;
if (frames > AUDIO_RING_MAX)
return AUDIO_RING_MAX;
return frames;
}
/* Return port used */
static int psp_configure_audio(unsigned rate, unsigned *new_rate)
static int psp_configure_audio(unsigned rate)
{
int port = sceAudioSRCChReserve(AUDIO_OUT_COUNT, rate, 2);
/* The SRC takes a fixed set of rates and refuses the rest outright,
* which leaves the session with no audio at all. Open at a rate it
* takes and tell the frontend to resample to that instead. */
if (port < 0)
{
if ((port = sceAudioSRCChReserve(AUDIO_OUT_COUNT,
PSP_AUDIO_RATE_FALLBACK, 2)) >= 0)
*new_rate = PSP_AUDIO_RATE_FALLBACK;
}
return port;
#if defined(VITA)
return sceAudioOutOpenPort(
SCE_AUDIO_OUT_PORT_TYPE_MAIN, AUDIO_OUT_COUNT,
rate, SCE_AUDIO_OUT_MODE_STEREO);
#elif defined(ORBIS)
return sceAudioOutOpen(0xff,
SCE_AUDIO_OUT_PORT_TYPE_MAIN, 0, AUDIO_OUT_COUNT,
rate, SCE_AUDIO_OUT_MODE_STEREO);
#else
return sceAudioSRCChReserve(AUDIO_OUT_COUNT, rate, 2);
#endif
}
static void psp_audio_mainloop(void *data)
{
psp_audio_t* psp = (psp_audio_t*)data;
/* The worker's own cursor: the next window to hand over. The
* published read_pos runs one window behind it while a ring
* window is in flight. The output call returns once the window
* handed to the previous call has been output, and with this
* call's window queued and still to be read by the hardware -
* the SDKs document passing NULL as the way to wait for that
* last window - so the window a call hands over is the device's
* until the call after it returns, and is released there. */
uint16_t play_pos = (uint16_t)
retro_atomic_load_relaxed_int(&psp->read_pos);
while (retro_atomic_load_acquire_int(&psp->running))
{
bool cond = false;
uint16_t next_pos = play_pos;
uint16_t read_pos = (uint16_t)
retro_atomic_load_relaxed_int(&psp->read_pos);
#if defined(VITA) || defined(ORBIS)
uint16_t read_pos_2 = read_pos;
#endif
uint16_t write_pos = (uint16_t)
retro_atomic_load_acquire_int(&psp->write_pos);
/* A period in hand is a period to play. Holding one back
* as a reserve only hands the device silence while the
* ring has audio in it. */
cond = RING_HELD(write_pos, play_pos, psp->ring)
< AUDIO_OUT_COUNT;
cond = ((uint16_t)(write_pos - read_pos) & AUDIO_BUFFER_SIZE_MASK)
< (AUDIO_OUT_COUNT * 2);
if (!cond)
{
next_pos += AUDIO_OUT_COUNT;
if (next_pos >= psp->ring)
next_pos = 0;
read_pos += AUDIO_OUT_COUNT;
read_pos &= AUDIO_BUFFER_SIZE_MASK;
/* Release: the period is free for the writer only after
* this store; the syscall below reads the old window, which
* the writer cannot touch until it sees the new index. */
retro_atomic_store_release_int(&psp->read_pos, read_pos);
}
else
retro_atomic_fetch_add_size(&psp->underruns, 1);
sceAudioSRCOutputBlocking(PSP_AUDIO_VOLUME_MAX,
psp->buffer_u32
+ (cond ? psp->ring : play_pos));
retro_atomic_fetch_add_size(&psp->consumed, AUDIO_OUT_COUNT);
/* The previous call's window has been output; this call's is
* the one in flight, so everything before play_pos is the
* writer's. When this call handed over silence, play_pos is
* where read_pos already stands. */
retro_atomic_store_release_int(&psp->read_pos, play_pos);
play_pos = next_pos;
retro_eventcount_notify(&psp->park);
#if defined(VITA) || defined(ORBIS)
sceAudioOutOutput(psp->port,
cond ? (psp->zeroBuffer)
: (psp->buffer + read_pos_2));
#else
sceAudioSRCOutputBlocking(PSP_AUDIO_VOLUME_MAX,
cond
? (psp->zeroBuffer)
: (psp->buffer + read_pos));
#endif
}
return;
@@ -197,49 +155,37 @@ static void *psp_audio_init(const char *device,
if (!psp)
return NULL;
if ((port = psp_configure_audio(rate, new_rate)) < 0)
if ((port = psp_configure_audio(rate)) < 0)
{
free(psp);
return NULL;
}
psp->rate = *new_rate ? *new_rate : rate;
psp->ring = psp_ring_frames(psp->rate, latency);
#if defined(ORBIS)
sceAudioOutInit();
#endif
/* Cache aligned, not necessary but helpful. */
/* The ring, and one period of silence past its end. */
psp->buffer_u32 = (uint32_t*)calloc(psp->ring + AUDIO_OUT_COUNT,
sizeof(uint32_t));
psp->buffer = (uint32_t*)malloc(AUDIO_BUFFER_SIZE * sizeof(uint32_t));
memset(psp->buffer, 0, AUDIO_BUFFER_SIZE * sizeof(uint32_t));
psp->zeroBuffer = (uint32_t*)malloc(AUDIO_OUT_COUNT * sizeof(uint32_t));
memset(psp->zeroBuffer, 0, AUDIO_OUT_COUNT * sizeof(uint32_t));
retro_atomic_size_init(&psp->consumed, 0);
retro_atomic_size_init(&psp->underruns, 0);
retro_atomic_int_init(&psp->read_pos, 0);
retro_atomic_int_init(&psp->write_pos, 0);
psp->port = port;
if ( !psp->buffer_u32
|| !retro_eventcount_init(&psp->park))
if (!retro_eventcount_init(&psp->park))
{
sceAudioSRCChRelease();
free(psp->buffer_u32);
free(psp->buffer);
free(psp->zeroBuffer);
free(psp);
return NULL;
}
psp->nonblock = false;
/* running has to be set before the worker starts, so a worker that
* never starts leaves a driver reporting itself alive with nothing
* consuming the ring: writes wait out their laps and return zero,
* and teardown acts on a state that was never true. */
retro_atomic_int_init(&psp->running, 1);
if (!(psp->worker_thread = sthread_create(psp_audio_mainloop, psp)))
{
retro_eventcount_free(&psp->park);
sceAudioSRCChRelease();
free(psp->buffer_u32);
free(psp);
return NULL;
}
psp->worker_thread = sthread_create(psp_audio_mainloop, psp);
return psp;
}
@@ -259,10 +205,17 @@ static void psp_audio_free(void *data)
}
}
retro_eventcount_free(&psp->park);
free(psp->buffer_u32);
free(psp->buffer);
psp->worker_thread = NULL;
free(psp->zeroBuffer);
sceAudioSRCChRelease();
#if defined(VITA)
sceAudioOutReleasePort(psp->port);
#elif defined(ORBIS)
sceAudioOutClose(psp->port);
#else
sceAudioSRCChRelease();
#endif
free(psp);
@@ -273,25 +226,13 @@ static ssize_t psp_audio_write(void *data, const void *s, size_t len)
psp_audio_t* psp = (psp_audio_t*)data;
uint16_t write_pos = (uint16_t)
retro_atomic_load_relaxed_int(&psp->write_pos);
/* Frames, and wide enough for them: narrowed to uint16_t, a write of
* more than 65535 frames wrapped to a small count that the room check
* then waved through, and the copy below took len bytes for it. The
* ring holds one frame short of its length, so no wait can free room
* for more than that at once - take what fits and report the short
* write rather than spending the laps on room that will not come. A
* len that is not whole frames carries the frames it has. */
size_t sample_count = len / sizeof(uint32_t);
uint16_t sample_count = len / sizeof(uint32_t);
if (!retro_atomic_load_acquire_int(&psp->running))
return -1;
if (!sample_count)
return 0;
if (sample_count > (size_t)(psp->ring - 1u))
sample_count = psp->ring - 1u;
len = sample_count * sizeof(uint32_t);
/* The ring is counted in uint32_t frames (write_pos, read_pos,
* ring); len is bytes. Both room checks below used to
* AUDIO_BUFFER_SIZE); len is bytes. Both room checks below used to
* compare the frame count against len, i.e. demanded four times the
* room actually needed: non-blocking writes were refused - the audio
* dropped - with plenty of space free, and blocking ones waited for
@@ -300,9 +241,9 @@ static ssize_t psp_audio_write(void *data, const void *s, size_t len)
* frames here too. */
if (psp->nonblock)
{
if (RING_FREE(write_pos, (uint16_t)
retro_atomic_load_acquire_int(&psp->read_pos),
psp->ring) < sample_count)
if (AUDIO_BUFFER_SIZE - ((uint16_t)(write_pos - (uint16_t)
retro_atomic_load_acquire_int(&psp->read_pos))
& AUDIO_BUFFER_SIZE_MASK) < sample_count)
return 0;
}
@@ -315,17 +256,17 @@ static ssize_t psp_audio_write(void *data, const void *s, size_t len)
* are re-checked inside the eventcount's window, so a period
* freed between the check and the park costs nothing. */
int laps = PSP_AUDIO_WAIT_LAPS;
while (RING_FREE(write_pos, (uint16_t)
retro_atomic_load_acquire_int(&psp->read_pos),
psp->ring) < sample_count)
while (AUDIO_BUFFER_SIZE - ((uint16_t)(write_pos - (uint16_t)
retro_atomic_load_acquire_int(&psp->read_pos))
& AUDIO_BUFFER_SIZE_MASK) < sample_count)
{
int key;
if (--laps < 0 || !retro_atomic_load_acquire_int(&psp->running))
return 0;
key = retro_eventcount_prepare_wait(&psp->park);
if ( (RING_FREE(write_pos, (uint16_t)
retro_atomic_load_acquire_int(&psp->read_pos),
psp->ring) >= sample_count)
if ( (AUDIO_BUFFER_SIZE - ((uint16_t)(write_pos - (uint16_t)
retro_atomic_load_acquire_int(&psp->read_pos))
& AUDIO_BUFFER_SIZE_MASK) >= sample_count)
|| !retro_atomic_load_acquire_int(&psp->running))
{
retro_eventcount_cancel_wait(&psp->park);
@@ -337,20 +278,19 @@ static ssize_t psp_audio_write(void *data, const void *s, size_t len)
}
}
if ((write_pos + sample_count) > psp->ring)
if ((write_pos + sample_count) > AUDIO_BUFFER_SIZE)
{
memcpy(psp->buffer_u32 + write_pos, s,
(psp->ring - write_pos) * sizeof(uint32_t));
memcpy(psp->buffer_u32, (uint32_t*)s +
(psp->ring - write_pos),
(write_pos + sample_count - psp->ring) * sizeof(uint32_t));
memcpy(psp->buffer + write_pos, s,
(AUDIO_BUFFER_SIZE - write_pos) * sizeof(uint32_t));
memcpy(psp->buffer, (uint32_t*)s +
(AUDIO_BUFFER_SIZE - write_pos),
(write_pos + sample_count - AUDIO_BUFFER_SIZE) * sizeof(uint32_t));
}
else
memcpy(psp->buffer_u32 + write_pos, s, len);
memcpy(psp->buffer + write_pos, s, len);
write_pos = (uint16_t)(write_pos + sample_count);
if (write_pos >= psp->ring)
write_pos -= psp->ring;
write_pos += sample_count;
write_pos &= AUDIO_BUFFER_SIZE_MASK;
/* Release: the samples land before the index that publishes
* them. */
retro_atomic_store_release_int(&psp->write_pos, write_pos);
@@ -369,6 +309,9 @@ static bool psp_audio_stop(void *data)
{
psp_audio_t* psp = (psp_audio_t*)data;
#if defined(ORBIS)
return false;
#else
if (psp)
{
retro_atomic_store_release_int(&psp->running, 0);
@@ -383,6 +326,7 @@ static bool psp_audio_stop(void *data)
}
}
return true;
#endif
}
static bool psp_audio_start(void *data, bool is_shutdown)
@@ -394,11 +338,7 @@ static bool psp_audio_start(void *data, bool is_shutdown)
if (!psp->worker_thread)
{
retro_atomic_store_release_int(&psp->running, 1);
if (!(psp->worker_thread = sthread_create(psp_audio_mainloop, psp)))
{
retro_atomic_store_release_int(&psp->running, 0);
return false;
}
psp->worker_thread = sthread_create(psp_audio_mainloop, psp);
}
}
@@ -419,9 +359,10 @@ static size_t psp_write_avail(void *data)
if (!psp || !retro_atomic_load_acquire_int(&psp->running))
return 0;
_len = RING_FREE((uint16_t)
retro_atomic_load_relaxed_int(&psp->write_pos), (uint16_t)
retro_atomic_load_acquire_int(&psp->read_pos), psp->ring);
_len = AUDIO_BUFFER_SIZE - ((uint16_t)((uint16_t)
retro_atomic_load_relaxed_int(&psp->write_pos) - (uint16_t)
retro_atomic_load_acquire_int(&psp->read_pos))
& AUDIO_BUFFER_SIZE_MASK);
return _len * sizeof(uint32_t);
}
@@ -438,17 +379,18 @@ static size_t psp_wait_writable(void *data, size_t len)
/* len arrives in bytes; the ring is counted in uint32_t frames. */
size_t want = len / sizeof(uint32_t);
if (want > psp->ring / 2)
want = psp->ring / 2;
if (want > AUDIO_BUFFER_SIZE / 2)
want = AUDIO_BUFFER_SIZE / 2;
for (;;)
{
int key;
if (!retro_atomic_load_acquire_int(&psp->running))
return 0;
avail = RING_FREE((uint16_t)
retro_atomic_load_relaxed_int(&psp->write_pos), (uint16_t)
retro_atomic_load_acquire_int(&psp->read_pos), psp->ring);
avail = AUDIO_BUFFER_SIZE - ((uint16_t)((uint16_t)
retro_atomic_load_relaxed_int(&psp->write_pos) - (uint16_t)
retro_atomic_load_acquire_int(&psp->read_pos))
& AUDIO_BUFFER_SIZE_MASK);
if (avail >= want)
break;
/* Bounded per wait and overall: a thread that has stopped
@@ -457,10 +399,10 @@ static size_t psp_wait_writable(void *data, size_t len)
if (--laps < 0)
return 0;
key = retro_eventcount_prepare_wait(&psp->park);
if (RING_FREE((uint16_t)
retro_atomic_load_relaxed_int(&psp->write_pos), (uint16_t)
retro_atomic_load_acquire_int(&psp->read_pos),
psp->ring) >= want
if ((AUDIO_BUFFER_SIZE - ((uint16_t)((uint16_t)
retro_atomic_load_relaxed_int(&psp->write_pos) - (uint16_t)
retro_atomic_load_acquire_int(&psp->read_pos))
& AUDIO_BUFFER_SIZE_MASK)) >= want
|| !retro_atomic_load_acquire_int(&psp->running))
{
retro_eventcount_cancel_wait(&psp->park);
@@ -478,32 +420,10 @@ static size_t psp_wait_writable(void *data, size_t len)
static bool psp_audio_use_float(void *data) { return false; }
static size_t psp_buffer_size(void *data)
{
psp_audio_t* psp = (psp_audio_t*)data;
if (!psp)
return 0;
/* In bytes: the ring, in uint32_t frames of int16 stereo. The
* window the device is playing is held inside it. */
return psp->ring * sizeof(uint32_t);
}
/* Frames the device has taken since init: the output call returns
* when it has taken the one before, so the worker counts a period
* per completed call. */
static size_t psp_frames_consumed(void *data)
{
psp_audio_t* psp = (psp_audio_t*)data;
if (!psp)
return 0;
return retro_atomic_load_acquire_size(&psp->consumed);
}
/* Periods played as silence for want of audio. */
static size_t psp_underruns(void *data)
{
psp_audio_t* psp = (psp_audio_t*)data;
if (!psp)
return 0;
return retro_atomic_load_acquire_size(&psp->underruns);
/* In bytes: the ring holds AUDIO_BUFFER_SIZE uint32_t frames of int16
* stereo. The comment beside this had the multiplication and left it
* out. */
return AUDIO_BUFFER_SIZE * sizeof(uint32_t);
}
audio_driver_t audio_psp = {
@@ -515,13 +435,17 @@ audio_driver_t audio_psp = {
psp_audio_set_nonblock_state,
psp_audio_free,
psp_audio_use_float,
#if defined(VITA)
"vita",
#elif defined(ORBIS)
"orbis",
#else
"psp",
#endif
NULL,
NULL,
psp_write_avail,
psp_buffer_size,
NULL, /* write_raw */
psp_wait_writable,
psp_frames_consumed,
psp_underruns
psp_wait_writable
};
+8 -111
View File
@@ -63,24 +63,6 @@ typedef struct
* lowers it at once. */
retro_atomic_size_t writable_cached;
retro_atomic_size_t sink_frames_cached;
/* Times the server ran out of audio for this stream, from its own
* underflow callback. One atomic add there, read by the frontend. */
retro_atomic_size_t underruns;
/* The sink's clock against the rate the stream was opened at,
* fitted from the timing info the server already hands over:
* read_index is what the sink has played, in bytes, and timestamp
* is when that was true. A fit over every sample rather than two
* points, because noise on a single anchor divides by the window
* and reads as drift.
*
* Accumulated in the latency-update callback, on the mainloop's
* thread, and published as one int in ppm. Nothing acts on it. */
unsigned frame_bytes;
uint64_t clk_anchor_pos;
int64_t clk_anchor_us;
int clk_have_anchor;
double clk_sx, clk_sy, clk_sxx, clk_sxy, clk_n;
retro_atomic_int_t clk_ppm; /* AUDIO_CLOCK_PPM_NONE until known */
} pa_t;
/* A note for the eventcount census: this driver stays off it, on
@@ -236,64 +218,6 @@ static void pulse_stream_request_cb(pa_stream *s, size_t len, void *data)
pa_threaded_mainloop_signal(pa->mainloop, 0);
}
/* One (position, time) pair from the server's timing info into the
* fit. read_index is the sink's play position in bytes; it can step
* back on a rewind or a flush, which restarts the fit rather than
* reading as an enormous negative drift. */
static void pulse_clock_sample(pa_t *pa, const pa_timing_info *ti)
{
uint64_t pos;
int64_t us;
double x, y, denom;
if (!pa->frame_bytes || ti->read_index < 0)
return;
pos = (uint64_t)ti->read_index / pa->frame_bytes;
us = (int64_t)ti->timestamp.tv_sec * 1000000
+ (int64_t)ti->timestamp.tv_usec;
if (!pa->clk_have_anchor || pos < pa->clk_anchor_pos
|| us <= pa->clk_anchor_us)
{
pa->clk_anchor_pos = pos;
pa->clk_anchor_us = us;
pa->clk_have_anchor = 1;
pa->clk_sx = pa->clk_sy = pa->clk_sxx = pa->clk_sxy = pa->clk_n = 0.0;
retro_atomic_store_release_int(&pa->clk_ppm, AUDIO_CLOCK_PPM_NONE);
return;
}
/* Seconds and frames from the anchor: a fit on the raw values
* loses its answer to cancellation. */
x = (double)(us - pa->clk_anchor_us) / 1000000.0;
y = (double)(pos - pa->clk_anchor_pos);
pa->clk_sx += x;
pa->clk_sy += y;
pa->clk_sxx += x * x;
pa->clk_sxy += x * y;
pa->clk_n += 1.0;
/* A second of window at least, as the interface says. */
if (pa->clk_n < 4.0 || x < 1.0)
return;
denom = pa->clk_n * pa->clk_sxx - pa->clk_sx * pa->clk_sx;
if (denom <= 0.0)
return;
{
double slope = (pa->clk_n * pa->clk_sxy - pa->clk_sx * pa->clk_sy)
/ denom;
double ppm = (slope / (double)pa->rate - 1.0) * 1000000.0;
if (ppm > -100000.0 && ppm < 100000.0)
{
retro_atomic_store_release_int(&pa->clk_ppm, (int)ppm);
}
}
}
static void pulse_stream_latency_update_cb(pa_stream *s, void *data)
{
pa_t *pa = (pa_t*)data;
@@ -302,25 +226,22 @@ static void pulse_stream_latency_update_cb(pa_stream *s, void *data)
* the server's queue; NULL until timing data has arrived. */
const pa_timing_info *ti = pa_stream_get_timing_info(s);
if (ti && pa->rate)
{
retro_atomic_store_release_size(&pa->sink_frames_cached,
(size_t)((uint64_t)ti->sink_usec * pa->rate / 1000000));
pulse_clock_sample(pa, ti);
}
pa_threaded_mainloop_signal(pa->mainloop, 0);
}
/* The server telling us this stream ran dry. Counted, not logged: it
* arrives on the mainloop's thread while audio is playing, which is
* the one place a log line costs what it is reporting on. */
static void pulse_underrun_update_cb(pa_stream *s, void *data)
{
#if 0
pa_t *pa = (pa_t*)data;
(void)s;
if (pa)
retro_atomic_fetch_add_size(&pa->underruns, 1);
RARCH_LOG("[PulseAudio] Underrun (Buffer: %u, Writable size: %u).\n",
(unsigned)pa->buffer_size,
(unsigned)pa_stream_writable_size(pa->stream));
#endif
}
static void pulse_buffer_attr_cb(pa_stream *s, void *data)
@@ -378,8 +299,6 @@ static void *pulse_init(const char *device, unsigned rate,
return NULL;
retro_atomic_size_init(&pa->writable_cached, 0);
retro_atomic_size_init(&pa->sink_frames_cached, 0);
retro_atomic_size_init(&pa->underruns, 0);
retro_atomic_int_init(&pa->clk_ppm, AUDIO_CLOCK_PPM_NONE);
memset(&spec, 0, sizeof(spec));
@@ -432,8 +351,7 @@ static void *pulse_init(const char *device, unsigned rate,
spec.channels = (uint8_t)audio_layout_channels(pa->layout);
spec.rate = rate;
pulse_channel_map(pa->layout, &map);
pa->rate = rate;
pa->frame_bytes = (unsigned)pa_frame_size(&spec);
pa->rate = rate;
pa->stream = pa_stream_new(pa->context, "audio", &spec, &map);
if (!pa->stream)
@@ -860,25 +778,6 @@ static void pulse_device_list_free(void *data, void *array_list_data)
string_list_free(s);
}
static size_t pulse_underruns(void *data)
{
pa_t *pa = (pa_t*)data;
return pa ? retro_atomic_load_acquire_size(&pa->underruns) : 0;
}
static bool pulse_device_clock_ppm(void *data, double *ppm)
{
pa_t *pa = (pa_t*)data;
int v;
if (!pa)
return false;
v = retro_atomic_load_acquire_int(&pa->clk_ppm);
if (v == AUDIO_CLOCK_PPM_NONE)
return false;
*ppm = (double)v;
return true;
}
audio_driver_t audio_pulse = {
pulse_init,
pulse_write,
@@ -896,8 +795,6 @@ audio_driver_t audio_pulse = {
NULL, /* write_raw */
pulse_wait_writable,
pulse_frames_consumed,
pulse_underruns,
pulse_layout,
NULL, /* frames_consumed_fallback */
pulse_device_clock_ppm
NULL, /* underruns */
pulse_layout
};
+2 -4
View File
@@ -143,6 +143,7 @@ static void ra_free(void *data)
* float PCM codec in the versions this driver has been built against;
* revisit only if one appears. */
static bool ra_use_float(void *data) { return false; }
static size_t ra_write_avail(void *data) { return 0; }
audio_driver_t audio_roar = {
ra_init,
@@ -156,10 +157,7 @@ audio_driver_t audio_roar = {
"roar",
NULL,
NULL,
/* write_avail - the VS API reports neither the room in the stream
* nor what the server still holds. NULL disables rate control;
* a constant would instead feed it a fill that never changes. */
NULL,
ra_write_avail,
NULL, /* buffer_size */
NULL /* write_raw */
};
+4 -21
View File
@@ -24,7 +24,6 @@
#include <retro_atomic.h>
#include "../audio_driver.h"
#include "../../verbosity.h"
#include "rsound.h"
typedef struct rsd
@@ -33,9 +32,9 @@ typedef struct rsd
/* Single producer (the core thread in rs_write), single consumer
* (librsound's worker in rsound_audio_cb): a lock-free retro_spsc
* ring, so the handoff between them needs nothing of librsound's.
* retro_spsc rounds capacity up to a power of two; fifo_size is the
* size asked for and the producer never fills past it. */
* ring, so neither side takes librsound's callback lock to touch
* it. retro_spsc rounds capacity up to a power of two; fifo_size
* is the size asked for and the producer never fills past it. */
retro_spsc_t ring;
bool ring_init;
/* Only the bounded waits a full ring puts the writer into; the ring
@@ -66,10 +65,6 @@ typedef struct rsd
* volatile orders nothing between threads and the re-check below
* has to see the store that came before the notify. */
retro_atomic_int_t has_error;
/* Callbacks librsound asked to fill and the ring came up short
* for; librsound zero-fills the shortfall, so what goes out is
* silence the device played for want of audio. */
retro_atomic_size_t underruns;
} rsd_t;
/* Room in the ring against the size asked for: the physical room less
@@ -88,9 +83,6 @@ static ssize_t rsound_audio_cb(void *data, size_t bytes, void *userdata)
size_t write_size = retro_spsc_read(&rsd->ring, data, bytes);
retro_eventcount_notify(&rsd->park);
if (write_size < bytes)
retro_atomic_fetch_add_size(&rsd->underruns, 1);
return write_size;
}
@@ -117,7 +109,6 @@ static void *rs_init(const char *device, unsigned rate, unsigned latency,
goto error;
retro_atomic_int_init(&rsd->has_error, 0);
retro_atomic_size_init(&rsd->underruns, 0);
/* Checked: on a backend that parks through a condition variable the
* init allocates, and a failure leaves an object whose commit_wait
* would hand scond_wait a NULL cond. */
@@ -374,12 +365,6 @@ static size_t rs_wait_writable(void *data, size_t len)
}
static bool rs_use_float(void *data) { return false; }
static size_t rs_underruns(void *data)
{
rsd_t *rsd = (rsd_t*)data;
return rsd ? retro_atomic_load_acquire_size(&rsd->underruns) : 0;
}
audio_driver_t audio_rsound = {
rs_init,
rs_write,
@@ -395,7 +380,5 @@ audio_driver_t audio_rsound = {
rs_write_avail,
rs_buffer_size,
NULL, /* write_raw */
rs_wait_writable,
NULL, /* frames_consumed */
rs_underruns
rs_wait_writable
};
Loaded 100 of 2112 files, more files were not shown because too many files have changed in this diff. Show more