mirror of
https://github.com/libretro/RetroArch.git
synced 2026-11-12 18:45:33 -10:00
Compare commits
46
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
eba3436f57 | ||
|
|
6e7ad2318b | ||
|
|
07bea38175 | ||
|
|
65a62c10e0 | ||
|
|
855002e6d6 | ||
|
|
5c5c158392 | ||
|
|
a2b9c2aa2d | ||
|
|
9f3c78217f | ||
|
|
ae02a3dd2a | ||
|
|
8a8f7307b2 | ||
|
|
8756cdc136 | ||
|
|
93cc8b8fd2 | ||
|
|
9926d191c0 | ||
|
|
d270b4030f | ||
|
|
1e66f8e705 | ||
|
|
76e903b229 | ||
|
|
ac0b850300 | ||
|
|
75869ea349 | ||
|
|
d7f44c5e94 | ||
|
|
6879826551 | ||
|
|
cc87d6cc22 | ||
|
|
5750097fbe | ||
|
|
155c1d8f59 | ||
|
|
54e6ef1869 | ||
|
|
a26d6b552b | ||
|
|
c4174413a1 | ||
|
|
990384fdb7 | ||
|
|
c7a5f6edb8 | ||
|
|
68d25aa5bf | ||
|
|
434b5525b6 | ||
|
|
6bda23daad | ||
|
|
d4fa1c6401 | ||
|
|
75b43c75a9 | ||
|
|
8714cee2fe | ||
|
|
fd17192acb | ||
|
|
1656558977 | ||
|
|
13677e89f8 | ||
|
|
a1b7f2204a | ||
|
|
d928778305 | ||
|
|
fbc6073586 | ||
|
|
d5eedad5ff | ||
|
|
92e1e205ce | ||
|
|
b42806c02c | ||
|
|
9ce7ef2311 | ||
|
|
cf816b8940 | ||
|
|
68b07f6a1a |
No files matched your search
@@ -235,6 +235,27 @@ jobs:
|
||||
./vfs_positional_nommap_test.exe
|
||||
./vfs_positional_ntverify_test.exe
|
||||
./vfs_positional_pathreopen_test.exe
|
||||
# twelve threads: more than a stream keeps events for
|
||||
./vfs_positional_wide_test.exe
|
||||
# seeks and reads of every size, every byte checked, through
|
||||
# the stream's own buffer
|
||||
./vfs_seek_read_mix_test.exe
|
||||
# writes, seeks, reads, truncates and holes in every write
|
||||
# mode through the stream's own buffer, checked against a copy
|
||||
./vfs_write_mix_test.exe
|
||||
# The same with streams on the C runtime (VFS_WIN32_NO_NATIVE):
|
||||
# the stream contract, the path an open takes, large files,
|
||||
# offsets from threads, writes.
|
||||
make clean MMAP=0
|
||||
make MMAP=0 VFS_NATIVE=0
|
||||
./vfs_bulk_read_test.exe
|
||||
./vfs_seek_contract_test.exe
|
||||
./vfs_large_file_test.exe
|
||||
./vfs_readahead_test.exe
|
||||
./vfs_descriptor_rw_test.exe
|
||||
./vfs_positional_test.exe
|
||||
./vfs_positional_nommap_test.exe
|
||||
./vfs_write_mix_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
|
||||
|
||||
@@ -60,6 +60,7 @@ jobs:
|
||||
./cdfs_dir_record_test
|
||||
./cdfs_pure_api_test
|
||||
./cdfs_io_error_test
|
||||
./cdfs_file_test
|
||||
env:
|
||||
ASAN_OPTIONS: detect_leaks=1
|
||||
UBSAN_OPTIONS: print_stacktrace=1:halt_on_error=1
|
||||
@@ -72,3 +73,4 @@ jobs:
|
||||
WINEDEBUG=-all wine ./cdfs_dir_record_test.exe
|
||||
WINEDEBUG=-all wine ./cdfs_pure_api_test.exe
|
||||
WINEDEBUG=-all wine ./cdfs_io_error_test.exe
|
||||
WINEDEBUG=-all wine ./cdfs_file_test.exe
|
||||
@@ -304,6 +304,12 @@ jobs:
|
||||
# the same run as vfs_positional_test here.
|
||||
vfs_positional_pathreopen_test
|
||||
vfs_positional_ntverify_test
|
||||
vfs_positional_wide_test
|
||||
# seeks and reads of every size, every byte checked
|
||||
vfs_seek_read_mix_test
|
||||
# writes, seeks, reads, truncates and holes in every write
|
||||
# mode, checked against a copy of the file
|
||||
vfs_write_mix_test
|
||||
cdrom_cuesheet_overflow_test
|
||||
rdvd_h262_test
|
||||
rdvd_lpcm_test
|
||||
@@ -318,6 +324,8 @@ jobs:
|
||||
cdfs_dir_record_test
|
||||
cdfs_pure_api_test
|
||||
cdfs_io_error_test
|
||||
# cdfs tracks opened by filename: iso, bin, cue beside its bin
|
||||
cdfs_file_test
|
||||
chd_meta_overflow_test
|
||||
chd_stream_threads_test
|
||||
chd_stream_ahead_test
|
||||
@@ -462,6 +470,9 @@ jobs:
|
||||
# Twenty threads change one record two million times in all
|
||||
# while three copy it: no change lost, no copy torn.
|
||||
retro_cas_record_test
|
||||
# A double-ended queue of one type in one block, against a
|
||||
# model: two million changes, none wrong, none allocating.
|
||||
retro_deque_test
|
||||
sthread_ex_test
|
||||
fast_cores_test
|
||||
memshm_test
|
||||
@@ -491,6 +502,11 @@ jobs:
|
||||
# The FLAC arm, sample-exact in every input mode, a grown
|
||||
# demuxed packet set and a raised WebM wall both resuming.
|
||||
audio_transfer_flac_test
|
||||
# rflac's 16-bit output against the PCM its fixtures were
|
||||
# made from: 16 bits exact, 24 and 32 rounded to the nearest
|
||||
# step, through every stereo coding, with SIMD and without.
|
||||
rflac_s16_test
|
||||
rflac_s16_nosimd_test
|
||||
# The HDR video blits: PQ and HLG to linear scRGB against
|
||||
# double-precision references, and HLG tone-mapped as HDR.
|
||||
image_hdr_blit_test
|
||||
@@ -545,6 +561,10 @@ jobs:
|
||||
# self-contained regression tests came to be compiled every
|
||||
# run and never executed.
|
||||
declare -a NORUN_TARGETS=(
|
||||
# Benchmarks: C runtime streams (VFS_WIN32_NO_NATIVE)
|
||||
# against the stream's own handle, for timing on Windows.
|
||||
vfs_native_bench
|
||||
vfs_native_bench_native
|
||||
# Fixture-driven checks run in Linux-samples-record.yml.
|
||||
flv_remux_test
|
||||
rtmp_socket_native_test
|
||||
|
||||
@@ -81,10 +81,6 @@ on:
|
||||
- 'libretro-common/include/retro_atomic.h'
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
ios_sdk_only:
|
||||
description: 'Audit production VideoToolbox declarations against real iOS SDKs'
|
||||
type: boolean
|
||||
default: false
|
||||
native_aac_only:
|
||||
description: 'Run native AudioToolbox AAC acceptance without GPU/frontend jobs'
|
||||
type: boolean
|
||||
@@ -97,7 +93,7 @@ permissions:
|
||||
contents: read
|
||||
jobs:
|
||||
stream-native:
|
||||
if: ${{ !inputs.ios_sdk_only && !inputs.native_aac_only && !inputs.frontend_stream_only }}
|
||||
if: ${{ !inputs.native_aac_only && !inputs.frontend_stream_only }}
|
||||
runs-on: macos-latest
|
||||
timeout-minutes: 10
|
||||
steps:
|
||||
@@ -156,7 +152,7 @@ jobs:
|
||||
${{ runner.temp }}/vt-metal-dispatch-mrc
|
||||
if-no-files-found: ignore
|
||||
aac-native:
|
||||
if: ${{ !inputs.ios_sdk_only && (inputs.native_aac_only || !inputs.frontend_stream_only) }}
|
||||
if: ${{ inputs.native_aac_only || !inputs.frontend_stream_only }}
|
||||
runs-on: macos-latest
|
||||
timeout-minutes: 10
|
||||
steps:
|
||||
@@ -188,7 +184,7 @@ jobs:
|
||||
path: ${{ runner.temp }}/audiotoolbox-src-evidence
|
||||
if-no-files-found: ignore
|
||||
encode:
|
||||
if: ${{ !inputs.ios_sdk_only && !inputs.native_aac_only && !inputs.frontend_stream_only }}
|
||||
if: ${{ !inputs.native_aac_only && !inputs.frontend_stream_only }}
|
||||
runs-on: macos-latest
|
||||
timeout-minutes: 20
|
||||
steps:
|
||||
@@ -289,7 +285,7 @@ jobs:
|
||||
if-no-files-found: warn
|
||||
|
||||
frontend-build:
|
||||
if: ${{ !inputs.ios_sdk_only && !inputs.native_aac_only && !inputs.frontend_stream_only }}
|
||||
if: ${{ !inputs.native_aac_only && !inputs.frontend_stream_only }}
|
||||
runs-on: macos-latest
|
||||
timeout-minutes: 40
|
||||
steps:
|
||||
@@ -326,7 +322,7 @@ jobs:
|
||||
if-no-files-found: warn
|
||||
|
||||
frontend-stream-build:
|
||||
if: ${{ !inputs.ios_sdk_only && !inputs.native_aac_only }}
|
||||
if: ${{ !inputs.native_aac_only }}
|
||||
runs-on: macos-latest
|
||||
timeout-minutes: 20
|
||||
strategy:
|
||||
@@ -376,46 +372,9 @@ jobs:
|
||||
if-no-files-found: ignore
|
||||
|
||||
frontend-contracts:
|
||||
if: ${{ !inputs.ios_sdk_only && !inputs.native_aac_only }}
|
||||
if: ${{ !inputs.native_aac_only }}
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- name: Source-clock capture and backpressure under sanitizers
|
||||
run: make -C samples/record/metal_frontend check SANITIZER=address,undefined
|
||||
|
||||
ios-sdk:
|
||||
if: ${{ inputs.ios_sdk_only }}
|
||||
runs-on: macos-latest
|
||||
timeout-minutes: 10
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- name: Compile production VideoToolbox headers for iOS and simulator
|
||||
run: python3 samples/record/videotoolbox_encode/ios_sdk_check.py --output-dir "${{ runner.temp }}/vt-ios-sdk"
|
||||
- name: Save actual SDK diagnostics
|
||||
if: ${{ always() }}
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: vt-ios-sdk
|
||||
path: ${{ runner.temp }}/vt-ios-sdk
|
||||
if-no-files-found: error
|
||||
|
||||
ios-app:
|
||||
if: ${{ inputs.ios_sdk_only }}
|
||||
needs: ios-sdk
|
||||
runs-on: macos-latest
|
||||
timeout-minutes: 60
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- name: Build and link actual iOS application with native recording
|
||||
run: python3 samples/record/videotoolbox_encode/ios_app_check.py --output-dir "${{ runner.temp }}/vt-ios-app"
|
||||
- name: Save full application build evidence
|
||||
if: ${{ always() }}
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: vt-ios-app
|
||||
path: |
|
||||
${{ runner.temp }}/vt-ios-app/*.json
|
||||
${{ runner.temp }}/vt-ios-app/*.log
|
||||
${{ runner.temp }}/vt-ios-app/*.txt
|
||||
${{ runner.temp }}/vt-ios-app/build.xcresult
|
||||
if-no-files-found: error
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
#!/bin/sh
|
||||
# Compile every libretro-common file that includes retro_posix_source.h
|
||||
# with $CC and no feature macros of its own, as a core's Makefile does.
|
||||
# webOS.yml runs it with the webOS SDK, whose glibc 2.12 predates
|
||||
# _DEFAULT_SOURCE: a name glibc hides behind __USE_MISC fails here
|
||||
# instead of in a core's webOS build.
|
||||
#
|
||||
# CC=arm-webos-linux-gnueabi-gcc sh posix-source-check.sh
|
||||
|
||||
set -e
|
||||
cd "$(dirname "$0")/../../.."
|
||||
|
||||
CC=${CC:-cc}
|
||||
fail=0
|
||||
n=0
|
||||
for tu in $(grep -rl --include='*.c' '#include <retro_posix_source.h>' \
|
||||
libretro-common | grep -v '/samples/' | sort); do
|
||||
n=$((n + 1))
|
||||
if ! out=$($CC -I. -Ilibretro-common/include -Wall \
|
||||
-Werror=implicit-function-declaration -Werror=implicit-int \
|
||||
-DHAVE_THREADS -DHAVE_NETWORKING -fsyntax-only "$tu" 2>&1); then
|
||||
echo "FAIL $tu"
|
||||
echo "$out" | grep ": error: " | sed 's/^/ /' | head -8
|
||||
fail=1
|
||||
fi
|
||||
done
|
||||
|
||||
if [ "$n" -eq 0 ]; then
|
||||
echo "no file includes retro_posix_source.h"
|
||||
exit 1
|
||||
fi
|
||||
[ "$fail" = 0 ] && echo "ok $n files include retro_posix_source.h"
|
||||
exit $fail
|
||||
@@ -101,6 +101,12 @@ jobs:
|
||||
tar xzf /tmp/webos-deps/cscd98_buildroot-nc4/aarch64-webos-linux-gnu_sdk-buildroot-x86_64.tar.gz
|
||||
./aarch64-webos-linux-gnu_sdk-buildroot/relocate-sdk.sh
|
||||
|
||||
- name: libretro-common feature macros on the SDK's glibc 2.12
|
||||
shell: bash
|
||||
run: |
|
||||
. /tmp/arm-webos-linux-gnueabi_sdk-buildroot/environment-setup
|
||||
sh .github/workflows/scripts/posix-source-check.sh
|
||||
|
||||
- name: Load RARCH_VERSION from version.all
|
||||
id: version
|
||||
shell: bash
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
# Future
|
||||
- LIBRETRO-COMMON/FLAC: 24- and 32-bit FLAC decoded to 16 bits is rounded to the nearest step instead of cut down (every sample was half a step low on average); 16-bit FLAC is unchanged
|
||||
- LIBRETRO-COMMON/FLAC: A seek in a FLAC that has a seek table lands on the frame asked for (it landed as far past it as the seek point was from the start); rflac_seek_point gives the seek point's frame
|
||||
- D3D11: Software-rendering cores draw straight into lent staging textures, copied to the frame texture on the GPU instead of by the CPU
|
||||
- VULKAN: Keep the pipeline cache on disk between runs, one file per GPU, so stock and slang shader pipelines are not recompiled by the driver on every launch, preset load or driver reinit
|
||||
- LINUX: Game Mode implemented natively (I/O priority plus a power-profiles-daemon performance hold); Feral libgamemode is no longer loaded and deps/feralgamemode is removed
|
||||
|
||||
+5
-5
@@ -528,7 +528,7 @@ ifneq ($(HAVE_STRL), 1)
|
||||
OBJ += $(LIBRETRO_COMM_DIR)/compat/compat_strl.o
|
||||
endif
|
||||
|
||||
OBJ += $(LIBRETRO_COMM_DIR)/formats/image_texture.o \
|
||||
OBJ += $(LIBRETRO_COMM_DIR)/formats/image_texture.o $(LIBRETRO_COMM_DIR)/streams/image_texture_file.o \
|
||||
$(LIBRETRO_COMM_DIR)/formats/image_texture_scale.o \
|
||||
$(LIBRETRO_COMM_DIR)/formats/image_texture_set.o \
|
||||
$(LIBRETRO_COMM_DIR)/formats/image/image_yuv_blit.o
|
||||
@@ -2655,9 +2655,9 @@ ifeq ($(HAVE_RMPEG1), 1)
|
||||
$(LIBRETRO_COMM_DIR)/formats/dvd/rdvd_packet.o \
|
||||
$(LIBRETRO_COMM_DIR)/formats/dvd/rdvd_spu.o \
|
||||
$(LIBRETRO_COMM_DIR)/formats/dvd/rdvd_ifo.o \
|
||||
$(LIBRETRO_COMM_DIR)/formats/dvd/rdvd_stream.o \
|
||||
$(LIBRETRO_COMM_DIR)/formats/dvd/rdvd_source.o \
|
||||
$(LIBRETRO_COMM_DIR)/formats/dvd/rdvd_vfs.o
|
||||
$(LIBRETRO_COMM_DIR)/streams/rdvd_stream.o \
|
||||
$(LIBRETRO_COMM_DIR)/streams/rdvd_vfs.o
|
||||
# MPEG-1 audio is layer II, which rmp3 already decodes; a consumer that
|
||||
# wants sound from an MPEG-1 stream needs HAVE_RMP3 as well.
|
||||
endif
|
||||
@@ -2673,7 +2673,7 @@ OBJ += $(LIBRETRO_COMM_DIR)/formats/bmp/rbmp_encode.o \
|
||||
$(LIBRETRO_COMM_DIR)/formats/image_transfer.o \
|
||||
$(LIBRETRO_COMM_DIR)/formats/video_player.o \
|
||||
$(LIBRETRO_COMM_DIR)/formats/audio_transfer.o \
|
||||
$(LIBRETRO_COMM_DIR)/formats/data_transfer.o \
|
||||
$(LIBRETRO_COMM_DIR)/formats/data_transfer.o $(LIBRETRO_COMM_DIR)/streams/data_transfer_file.o \
|
||||
$(LIBRETRO_COMM_DIR)/memmap/memmap.o \
|
||||
$(LIBRETRO_COMM_DIR)/formats/m3u/rm3u.o
|
||||
|
||||
@@ -2889,7 +2889,7 @@ ifeq ($(HAVE_NETWORKING), 1)
|
||||
cheevos/cheevos_client.o \
|
||||
cheevos/cheevos_menu.o \
|
||||
cheevos/cheevos_badge.o \
|
||||
$(LIBRETRO_COMM_DIR)/formats/cdfs/cdfs.o \
|
||||
$(LIBRETRO_COMM_DIR)/formats/cdfs/cdfs.o $(LIBRETRO_COMM_DIR)/streams/cdfs_file.o \
|
||||
deps/rcheevos/src/rc_client.o \
|
||||
deps/rcheevos/src/rc_compat.o \
|
||||
deps/rcheevos/src/rc_libretro.o \
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
#include <streams/file_stream.h>
|
||||
#include <features/features_cpu.h>
|
||||
#include <formats/cdfs.h>
|
||||
#include <streams/cdfs_file.h>
|
||||
#include <compat/strl.h>
|
||||
#include <retro_miscellaneous.h>
|
||||
#include <retro_math.h>
|
||||
|
||||
@@ -33,7 +33,6 @@
|
||||
typedef struct
|
||||
{
|
||||
int64_t pos; /* in the disc image */
|
||||
uint64_t file_pos; /* where the file stands; seeks to it are skipped */
|
||||
ciso_t *ciso;
|
||||
RFILE *file;
|
||||
} rcheevos_ciso_t;
|
||||
@@ -51,12 +50,12 @@ static int rcheevos_ciso_pump(rcheevos_ciso_t *handle, bool opening)
|
||||
|
||||
if (err != CISO_PENDING)
|
||||
return err;
|
||||
if ( handle->file_pos == req.offset
|
||||
|| filestream_seek(handle->file, (int64_t)req.offset,
|
||||
/* At the offset in one call; seek and read where there is none */
|
||||
if ((got = filestream_read_at(handle->file, req.dst,
|
||||
(int64_t)req.length, (int64_t)req.offset)) < 0
|
||||
&& filestream_seek(handle->file, (int64_t)req.offset,
|
||||
RETRO_VFS_SEEK_POSITION_START) == 0)
|
||||
got = filestream_read(handle->file, req.dst, (int64_t)req.length);
|
||||
/* unknown after a failed read: seek next time */
|
||||
handle->file_pos = got > 0 ? req.offset + (uint64_t)got : ~(uint64_t)0;
|
||||
ciso_feed(handle->ciso, got > 0 ? (size_t)got : 0);
|
||||
}
|
||||
}
|
||||
@@ -81,7 +80,6 @@ static void* RC_CCONV rcheevos_ciso_open(const char *path)
|
||||
if (!handle)
|
||||
return NULL;
|
||||
handle->pos = 0;
|
||||
handle->file_pos = 0;
|
||||
handle->ciso = NULL;
|
||||
/* the descriptor path: no stdio buffer between the file and the
|
||||
* buffers rc_hash reads into */
|
||||
|
||||
@@ -571,6 +571,10 @@
|
||||
* times. Off: the fixed-timer pacing threaded video always had. */
|
||||
#define DEFAULT_VIDEO_THREADED_DISPLAY_PACING false
|
||||
|
||||
/* Leave a frame the core marks as a repeat unpresented, so the display
|
||||
* sees the content's own frame rate. Off: every repeat is presented. */
|
||||
#define DEFAULT_VIDEO_SKIP_DUPE_FRAMES false
|
||||
|
||||
/* The Direct3D 9 drivers on a Direct3D9Ex device: a window presented
|
||||
* through the flip model, and the frame latency settings honoured.
|
||||
* Off: the discard swap effect and the runtime's own queue, as before. */
|
||||
@@ -971,6 +975,8 @@
|
||||
#define DEFAULT_MENU_SHOW_SUBLABELS_CURRENT_SELECTION_ONLY false
|
||||
#define DEFAULT_MENU_SHOW_CONFIRM true
|
||||
#define DEFAULT_MENU_DYNAMIC_WALLPAPER_ENABLE true
|
||||
|
||||
#define DEFAULT_MENU_SCROLL_QUICK false
|
||||
#define DEFAULT_MENU_SCROLL_FAST false
|
||||
#define DEFAULT_MENU_SCROLL_DELAY 256
|
||||
|
||||
|
||||
@@ -754,6 +754,7 @@ typedef struct settings
|
||||
bool video_threaded_display_pacing;
|
||||
bool video_d3d9ex_present;
|
||||
bool video_present_timing_from_display;
|
||||
bool video_skip_dupe_frames;
|
||||
bool video_font_enable;
|
||||
bool video_disable_composition;
|
||||
bool video_post_filter_record;
|
||||
@@ -924,6 +925,7 @@ typedef struct settings
|
||||
bool menu_linear_filter;
|
||||
bool menu_texture_mipmapping;
|
||||
bool menu_horizontal_animation;
|
||||
bool menu_scroll_quick;
|
||||
bool menu_scroll_fast;
|
||||
bool menu_show_online_updater;
|
||||
bool menu_show_full_paths;
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
#include "stb_image.h"
|
||||
#else
|
||||
#include <formats/image.h>
|
||||
#include <streams/image_texture_file.h>
|
||||
#endif
|
||||
|
||||
#include <libretro.h>
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
#include <retro_miscellaneous.h>
|
||||
#include <streams/file_stream.h>
|
||||
#include <formats/data_transfer.h>
|
||||
#include <streams/data_transfer_file.h>
|
||||
#include <streams/file_stream.h>
|
||||
#include <formats/rwebm.h>
|
||||
#include <formats/rwebm_video.h>
|
||||
|
||||
@@ -9,8 +9,6 @@
|
||||
#else
|
||||
#define VT_ENCODE_AVAILABLE
|
||||
#endif
|
||||
#include "videotoolbox_encode_hardware.h"
|
||||
|
||||
typedef struct videotoolbox_encode_config
|
||||
{
|
||||
unsigned width, height, slots, bitrate, key_interval, timescale, bit_depth;
|
||||
@@ -59,15 +57,7 @@ static INLINE VT_ENCODE_AVAILABLE videotoolbox_encode_t *videotoolbox_encode_new
|
||||
videotoolbox_encode_t *encoder = NULL;
|
||||
CFMutableDictionaryRef spec = NULL, attrs = NULL, props = NULL;
|
||||
CFTypeRef hardware = NULL, selected_id = NULL;
|
||||
CFStringRef admitted_id = NULL;
|
||||
#ifdef VT_ENCODE_MOBILE
|
||||
bool query_hardware = false;
|
||||
#endif
|
||||
OSStatus status = kVTParameterErr;
|
||||
#ifdef VT_ENCODE_MOBILE
|
||||
if (VT_ENCODE_HARDWARE_KEYS_AVAILABLE)
|
||||
query_hardware = true;
|
||||
#endif
|
||||
if (error) *error = status;
|
||||
if ((encoder_id && !CFStringGetLength(encoder_id)) || !config || !config->width || !config->height || (config->width & 1)
|
||||
|| (config->height & 1) || config->width > 16384 || config->height > 16384
|
||||
@@ -78,20 +68,6 @@ static INLINE VT_ENCODE_AVAILABLE videotoolbox_encode_t *videotoolbox_encode_new
|
||||
|| (config->bit_depth != 8 && config->bit_depth != 10)
|
||||
|| (config->bit_depth == 10 && !config->hevc)
|
||||
|| (config->hdr && config->bit_depth != 10)) return NULL;
|
||||
#ifdef VT_ENCODE_MOBILE
|
||||
if (!query_hardware)
|
||||
{
|
||||
admitted_id = videotoolbox_encode_hardware_id(encoder_id,
|
||||
config->hevc ? kCMVideoCodecType_HEVC : kCMVideoCodecType_H264);
|
||||
if (!admitted_id)
|
||||
{
|
||||
if (error)
|
||||
*error = kVTCouldNotFindVideoEncoderErr;
|
||||
return NULL;
|
||||
}
|
||||
encoder_id = admitted_id;
|
||||
}
|
||||
#endif
|
||||
encoder = (videotoolbox_encode_t*)calloc(1, sizeof(*encoder));
|
||||
spec = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
|
||||
attrs = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
|
||||
@@ -100,10 +76,7 @@ static INLINE VT_ENCODE_AVAILABLE videotoolbox_encode_t *videotoolbox_encode_new
|
||||
encoder->config = *config;
|
||||
encoder->pool = videotoolbox_encode_slots_new(config->slots);
|
||||
if (!encoder->pool) { status = kVTAllocationFailedErr; goto done; }
|
||||
#ifdef VT_ENCODE_HAS_HARDWARE_KEYS
|
||||
if (VT_ENCODE_HARDWARE_KEYS_AVAILABLE)
|
||||
CFDictionarySetValue(spec, kVTVideoEncoderSpecification_RequireHardwareAcceleratedVideoEncoder, kCFBooleanTrue);
|
||||
#endif
|
||||
CFDictionarySetValue(spec, kVTVideoEncoderSpecification_RequireHardwareAcceleratedVideoEncoder, kCFBooleanTrue);
|
||||
if (encoder_id) CFDictionarySetValue(spec,kVTVideoEncoderSpecification_EncoderID,encoder_id);
|
||||
if (gpu_registry_id)
|
||||
{
|
||||
@@ -159,15 +132,10 @@ static INLINE VT_ENCODE_AVAILABLE videotoolbox_encode_t *videotoolbox_encode_new
|
||||
if (status) goto done;
|
||||
status = VTCompressionSessionPrepareToEncodeFrames(encoder->session);
|
||||
if (status) goto done;
|
||||
#ifdef VT_ENCODE_HAS_HARDWARE_KEYS
|
||||
if (VT_ENCODE_HARDWARE_KEYS_AVAILABLE)
|
||||
{
|
||||
status = VTSessionCopyProperty(encoder->session,
|
||||
kVTCompressionPropertyKey_UsingHardwareAcceleratedVideoEncoder, NULL, &hardware);
|
||||
if (!status && (!hardware || CFGetTypeID(hardware) != CFBooleanGetTypeID()
|
||||
|| !CFBooleanGetValue((CFBooleanRef)hardware))) status = kVTCouldNotFindVideoEncoderErr;
|
||||
}
|
||||
#endif
|
||||
status = VTSessionCopyProperty(encoder->session,
|
||||
kVTCompressionPropertyKey_UsingHardwareAcceleratedVideoEncoder, NULL, &hardware);
|
||||
if (!status && (!hardware || CFGetTypeID(hardware) != CFBooleanGetTypeID()
|
||||
|| !CFBooleanGetValue((CFBooleanRef)hardware))) status = kVTCouldNotFindVideoEncoderErr;
|
||||
if (!status && encoder_id)
|
||||
{
|
||||
status=VTSessionCopyProperty(encoder->session,kVTCompressionPropertyKey_EncoderID,NULL,&selected_id);
|
||||
@@ -175,7 +143,6 @@ static INLINE VT_ENCODE_AVAILABLE videotoolbox_encode_t *videotoolbox_encode_new
|
||||
|| !CFEqual(selected_id,encoder_id))) status=kVTCouldNotFindVideoEncoderErr;
|
||||
}
|
||||
done:
|
||||
if (admitted_id) CFRelease((CFTypeRef)admitted_id);
|
||||
if (selected_id) CFRelease(selected_id);
|
||||
if (hardware) CFRelease(hardware);
|
||||
if (props) CFRelease(props);
|
||||
|
||||
@@ -1,85 +0,0 @@
|
||||
/* Copyright (C) 2026 The RetroArch team. SPDX-License-Identifier: GPL-3.0-or-later */
|
||||
#ifndef RETROARCH_VIDEOTOOLBOX_ENCODE_HARDWARE_H
|
||||
#define RETROARCH_VIDEOTOOLBOX_ENCODE_HARDWARE_H
|
||||
|
||||
#if defined(__APPLE__) && !defined(VIDEOTOOLBOX_ENCODE_TEST_TYPES)
|
||||
#include <TargetConditionals.h>
|
||||
#if TARGET_OS_IPHONE
|
||||
#define VT_ENCODE_MOBILE
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if !defined(VT_ENCODE_MOBILE) \
|
||||
|| (defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 170400) \
|
||||
|| (defined(__TV_OS_VERSION_MAX_ALLOWED) && __TV_OS_VERSION_MAX_ALLOWED >= 170400) \
|
||||
|| (defined(__VISION_OS_VERSION_MAX_ALLOWED) && __VISION_OS_VERSION_MAX_ALLOWED >= 10100)
|
||||
#define VT_ENCODE_HAS_HARDWARE_KEYS
|
||||
#endif
|
||||
|
||||
#if defined(VT_ENCODE_MOBILE) && defined(VT_ENCODE_HAS_HARDWARE_KEYS)
|
||||
#define VT_ENCODE_HARDWARE_KEYS_AVAILABLE \
|
||||
__builtin_available(iOS 17.4, tvOS 17.4, macCatalyst 17.4, visionOS 1.1, *)
|
||||
#elif defined(VT_ENCODE_HAS_HARDWARE_KEYS)
|
||||
#define VT_ENCODE_HARDWARE_KEYS_AVAILABLE true
|
||||
#else
|
||||
#define VT_ENCODE_HARDWARE_KEYS_AVAILABLE false
|
||||
#endif
|
||||
|
||||
#if defined(VT_ENCODE_MOBILE) || defined(VIDEOTOOLBOX_ENCODE_HARDWARE_LIST_TEST)
|
||||
/* Cold admission only. Return a retained, advertised hardware encoder ID;
|
||||
* the caller must independently confirm the session selected that same ID. */
|
||||
static INLINE CFStringRef videotoolbox_encode_hardware_id(
|
||||
CFStringRef requested, uint32_t codec_type)
|
||||
{
|
||||
#if defined(VIDEOTOOLBOX_ENCODE_HARDWARE_LIST_TEST) \
|
||||
|| (defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 130000) \
|
||||
|| (defined(__TV_OS_VERSION_MAX_ALLOWED) && __TV_OS_VERSION_MAX_ALLOWED >= 130000) \
|
||||
|| defined(__VISION_OS_VERSION_MAX_ALLOWED)
|
||||
#ifndef VIDEOTOOLBOX_ENCODE_HARDWARE_LIST_TEST
|
||||
if (__builtin_available(iOS 13.0, tvOS 13.0, *))
|
||||
#endif
|
||||
{
|
||||
CFArrayRef list = NULL;
|
||||
CFStringRef selected = NULL;
|
||||
CFIndex i, count;
|
||||
OSStatus status = VTCopyVideoEncoderList(NULL, &list);
|
||||
if (status || !list)
|
||||
{
|
||||
if (list)
|
||||
CFRelease(list);
|
||||
return NULL;
|
||||
}
|
||||
count = CFArrayGetCount(list);
|
||||
if (count >= 0 && count <= 4096)
|
||||
for (i = 0; i < count; i++)
|
||||
{
|
||||
CFDictionaryRef entry = (CFDictionaryRef)CFArrayGetValueAtIndex(list, i);
|
||||
CFTypeRef hardware, codec, identifier;
|
||||
int64_t value = 0;
|
||||
if (!entry || CFGetTypeID(entry) != CFDictionaryGetTypeID())
|
||||
continue;
|
||||
hardware = CFDictionaryGetValue(entry, kVTVideoEncoderList_IsHardwareAccelerated);
|
||||
codec = CFDictionaryGetValue(entry, kVTVideoEncoderList_CodecType);
|
||||
identifier = CFDictionaryGetValue(entry, kVTVideoEncoderList_EncoderID);
|
||||
if (!hardware || CFGetTypeID(hardware) != CFBooleanGetTypeID()
|
||||
|| !CFBooleanGetValue((CFBooleanRef)hardware)
|
||||
|| !codec || CFGetTypeID(codec) != CFNumberGetTypeID()
|
||||
|| !CFNumberGetValue((CFNumberRef)codec, kCFNumberSInt64Type, &value)
|
||||
|| value != (int64_t)codec_type
|
||||
|| !identifier || CFGetTypeID(identifier) != CFStringGetTypeID()
|
||||
|| !CFStringGetLength((CFStringRef)identifier)
|
||||
|| (requested && !CFEqual(identifier, requested)))
|
||||
continue;
|
||||
selected = (CFStringRef)CFRetain(identifier);
|
||||
break;
|
||||
}
|
||||
CFRelease(list);
|
||||
return selected;
|
||||
}
|
||||
#endif
|
||||
(void)requested;
|
||||
(void)codec_type;
|
||||
return NULL;
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
+45
-34
@@ -677,11 +677,9 @@ static void win32_save_position(void)
|
||||
{
|
||||
bool ui_menubar_enable = settings->bools.ui_menubar_enable;
|
||||
bool window_show_decor = settings->bools.video_window_show_decorations;
|
||||
unsigned win_w =
|
||||
VIDEO_SCALE_W(g_win32->pos_dims);
|
||||
unsigned win_h =
|
||||
VIDEO_SCALE_H(g_win32->pos_dims);
|
||||
settings->uints.window_position_pos = g_win32->pos;
|
||||
unsigned win_w = VIDEO_SCALE_W(g_win32->pos_dims);
|
||||
unsigned win_h = VIDEO_SCALE_H(g_win32->pos_dims);
|
||||
|
||||
/* The frame the window reports includes whatever chrome it
|
||||
* is wearing; the setting holds the client area, so take the
|
||||
* chrome off both axes before the pair is stored. */
|
||||
@@ -695,11 +693,12 @@ static void win32_save_position(void)
|
||||
}
|
||||
if (ui_menubar_enable)
|
||||
{
|
||||
int menu_bar_height = GetSystemMetrics(SM_CYMENU);
|
||||
int menu_bar_height = GetSystemMetrics(SM_CYMENU);
|
||||
win_h -= menu_bar_height;
|
||||
}
|
||||
settings->uints.window_position_dims =
|
||||
VIDEO_SCALE_PACK(win_w, win_h);
|
||||
|
||||
settings->uints.window_position_pos = g_win32->pos;
|
||||
settings->uints.window_position_dims = VIDEO_SCALE_PACK(win_w, win_h);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2154,8 +2153,10 @@ void win32_check_window(void *data,
|
||||
if (g_win32_flags & WIN32_CMN_FLAG_RESIZED)
|
||||
{
|
||||
*resize = true;
|
||||
*dims = VIDEO_SCALE_PACK(g_win32_resize_width, g_win32_resize_height);
|
||||
*dims = VIDEO_SCALE_PACK(g_win32_resize_width, g_win32_resize_height);
|
||||
g_win32_flags &= ~WIN32_CMN_FLAG_RESIZED;
|
||||
/* Save the current position and size if set so. */
|
||||
win32_save_position();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -2235,37 +2236,50 @@ bool win32_has_focus(void *data)
|
||||
{
|
||||
settings_t *settings = config_get_ptr();
|
||||
|
||||
/* Ensure window size is big enough for core geometry. */
|
||||
/* Apply/enforce windowed mode size. */
|
||||
if ( settings
|
||||
&& !settings->bools.video_fullscreen
|
||||
&& !settings->bools.video_window_save_positions)
|
||||
&& !settings->bools.video_fullscreen)
|
||||
{
|
||||
unsigned extra_width = 0;
|
||||
unsigned extra_height = 0;
|
||||
unsigned min_width = 0;
|
||||
unsigned min_height = 0;
|
||||
bool window_save_positions = settings->bools.video_window_save_positions;
|
||||
unsigned extra_w = 0;
|
||||
unsigned extra_h = 0;
|
||||
unsigned win_w = 0;
|
||||
unsigned win_h = 0;
|
||||
|
||||
win32_get_av_info_geometry(&min_width, &min_height);
|
||||
/* Follow windowed mode size options in realtime when remembering position. */
|
||||
if (window_save_positions)
|
||||
{
|
||||
win_w = VIDEO_SCALE_W(settings->uints.window_position_dims);
|
||||
win_h = VIDEO_SCALE_H(settings->uints.window_position_dims);
|
||||
}
|
||||
/* Ensure window size is big enough for core geometry. */
|
||||
else
|
||||
win32_get_av_info_geometry(&win_w, &win_h);
|
||||
|
||||
if (settings->bools.video_window_show_decorations)
|
||||
{
|
||||
int border_thickness = GetSystemMetrics(SM_CXSIZEFRAME);
|
||||
int title_bar_height = GetSystemMetrics(SM_CYCAPTION);
|
||||
|
||||
extra_width += border_thickness * 2;
|
||||
extra_height += border_thickness * 2 + title_bar_height;
|
||||
extra_w += border_thickness * 2;
|
||||
extra_h += border_thickness * 2 + title_bar_height;
|
||||
}
|
||||
|
||||
if (settings->bools.ui_menubar_enable)
|
||||
extra_height += GetSystemMetrics(SM_CYMENU);
|
||||
extra_h += GetSystemMetrics(SM_CYMENU);
|
||||
|
||||
if ( ( g_win32_resize_width < min_width
|
||||
|| g_win32_resize_height < min_height)
|
||||
&& min_width - g_win32_resize_width < MIN_WIDTH / 1.5f
|
||||
&& min_height - g_win32_resize_height < MIN_HEIGHT / 1.5f)
|
||||
if ( ( window_save_positions
|
||||
&& ( win_w != g_win32_resize_width
|
||||
|| win_h != g_win32_resize_height))
|
||||
|| ( !window_save_positions
|
||||
&& ( win_w > g_win32_resize_width
|
||||
|| win_h > g_win32_resize_height)
|
||||
&& win_w - g_win32_resize_width < MIN_WIDTH / 1.5f
|
||||
&& win_h - g_win32_resize_height < MIN_HEIGHT / 1.5f)
|
||||
)
|
||||
SetWindowPos(main_window.hwnd, NULL, 0, 0,
|
||||
min_width + extra_width,
|
||||
min_height + extra_height,
|
||||
win_w + extra_w,
|
||||
win_h + extra_h,
|
||||
SWP_NOMOVE);
|
||||
}
|
||||
|
||||
@@ -2439,7 +2453,7 @@ bool win32_set_style(MONITORINFOEX *current_mon, HMONITOR *hm_to_use,
|
||||
{
|
||||
win32_common_state_t *g_win32 = (win32_common_state_t*)&win32_st;
|
||||
bool position_set_from_config = false;
|
||||
bool video_window_save_positions = settings->bools.video_window_save_positions;
|
||||
bool window_save_positions = settings->bools.video_window_save_positions;
|
||||
bool window_show_decor = settings->bools.video_window_show_decorations;
|
||||
|
||||
*style = WS_OVERLAPPEDWINDOW | WS_CLIPSIBLINGS | WS_CLIPCHILDREN;
|
||||
@@ -2454,18 +2468,15 @@ bool win32_set_style(MONITORINFOEX *current_mon, HMONITOR *hm_to_use,
|
||||
|
||||
AdjustWindowRect(rect, *style, FALSE);
|
||||
|
||||
if (video_window_save_positions)
|
||||
if (window_save_positions)
|
||||
{
|
||||
/* Set position from config */
|
||||
int border_thickness = window_show_decor ? GetSystemMetrics(SM_CXSIZEFRAME) : 0;
|
||||
int title_bar_height = window_show_decor ? GetSystemMetrics(SM_CYCAPTION) : 0;
|
||||
unsigned window_position_width =
|
||||
VIDEO_SCALE_W(settings->uints.window_position_dims);
|
||||
unsigned window_position_height =
|
||||
VIDEO_SCALE_H(settings->uints.window_position_dims);
|
||||
unsigned window_position_width = VIDEO_SCALE_W(settings->uints.window_position_dims);
|
||||
unsigned window_position_height = VIDEO_SCALE_H(settings->uints.window_position_dims);
|
||||
|
||||
g_win32->pos =
|
||||
settings->uints.window_position_pos;
|
||||
g_win32->pos = settings->uints.window_position_pos;
|
||||
g_win32->pos_set = true;
|
||||
g_win32->pos_dims = VIDEO_SCALE_PACK(
|
||||
window_position_width + border_thickness * 2,
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
#include <retro_math.h>
|
||||
#include <compat/strl.h>
|
||||
#include <formats/image.h>
|
||||
#include <streams/image_texture_file.h>
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "../../config.h"
|
||||
|
||||
@@ -37,6 +37,7 @@
|
||||
#include <encodings/utf.h>
|
||||
#include <lists/string_list.h>
|
||||
#include <formats/image.h>
|
||||
#include <streams/image_texture_file.h>
|
||||
|
||||
#include <dxgi.h>
|
||||
|
||||
|
||||
@@ -42,6 +42,7 @@
|
||||
#include <encodings/utf.h>
|
||||
#include <lists/string_list.h>
|
||||
#include <formats/image.h>
|
||||
#include <streams/image_texture_file.h>
|
||||
#include <formats/image_yuv_blit.h>
|
||||
#include "../common/video_import_color.h"
|
||||
|
||||
|
||||
@@ -40,6 +40,7 @@
|
||||
#include <encodings/utf.h>
|
||||
#include <lists/string_list.h>
|
||||
#include <formats/image.h>
|
||||
#include <streams/image_texture_file.h>
|
||||
#include <formats/image_yuv_blit.h>
|
||||
#include "../common/video_import_color.h"
|
||||
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
#define CINTERFACE
|
||||
|
||||
#include <formats/image.h>
|
||||
#include <streams/image_texture_file.h>
|
||||
#include <compat/strl.h>
|
||||
#include <compat/posix_string.h>
|
||||
#include <file/file_path.h>
|
||||
|
||||
@@ -31,6 +31,7 @@
|
||||
#endif
|
||||
|
||||
#include <formats/image.h>
|
||||
#include <streams/image_texture_file.h>
|
||||
#include <compat/strl.h>
|
||||
#include <compat/posix_string.h>
|
||||
#include <file/file_path.h>
|
||||
|
||||
@@ -45,6 +45,7 @@
|
||||
#include <gfx/scaler/scaler.h>
|
||||
#include <gfx/math/matrix_4x4.h>
|
||||
#include <formats/image.h>
|
||||
#include <streams/image_texture_file.h>
|
||||
#include <retro_inline.h>
|
||||
#include <retro_miscellaneous.h>
|
||||
#include <retro_math.h>
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
|
||||
#include <encodings/utf.h>
|
||||
#include <formats/image.h>
|
||||
#include <streams/image_texture_file.h>
|
||||
#include <file/file_path.h>
|
||||
#include <string/stdstring.h>
|
||||
|
||||
|
||||
@@ -34,6 +34,7 @@
|
||||
#include <gfx/scaler/scaler.h>
|
||||
#include <gfx/video_frame.h>
|
||||
#include <formats/image.h>
|
||||
#include <streams/image_texture_file.h>
|
||||
#include <formats/image_yuv_blit.h>
|
||||
#include <retro_inline.h>
|
||||
#include <retro_miscellaneous.h>
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
#include <compat/strl.h>
|
||||
#include <string/stdstring.h>
|
||||
#include <formats/image.h>
|
||||
#include <streams/image_texture_file.h>
|
||||
#include <retro_miscellaneous.h>
|
||||
|
||||
#include "slang_process.h"
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
|
||||
#include <compat/strl.h>
|
||||
#include <formats/image.h>
|
||||
#include <streams/image_texture_file.h>
|
||||
#include <string/stdstring.h>
|
||||
#include <gfx/math/matrix_4x4.h>
|
||||
#include <retro_miscellaneous.h>
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
#include <streams/file_stream.h>
|
||||
#include <formats/image.h>
|
||||
#include <formats/data_transfer.h>
|
||||
#include <streams/data_transfer_file.h>
|
||||
#include <formats/video_player.h>
|
||||
#include <retro_atomic.h>
|
||||
#include <features/features_cpu.h>
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
#include <retro_atomic.h>
|
||||
#include <retro_miscellaneous.h>
|
||||
#include <formats/image_yuv_blit.h>
|
||||
#include <streams/image_texture_file.h>
|
||||
|
||||
#include "gfx_surface.h"
|
||||
#include "gfx_instrument.h"
|
||||
|
||||
@@ -9749,6 +9749,34 @@ void video_driver_frame(const void *data, unsigned width,
|
||||
if (data)
|
||||
video_st->main_flags &= ~VIDEO_FLAG_FF_FRAME_OWED;
|
||||
|
||||
/* Skip Duplicate Frames: a repeat from the core is left unpresented,
|
||||
* so the display gets the content's own rate. Only a repeat the core
|
||||
* sent, not the cached frame sent again for pause or the menu, and
|
||||
* not where the presents themselves are the effect: Black Frame
|
||||
* Insertion, shader sub-frames, the threaded presenter's repeats or
|
||||
* a headset's interval. The hold tells the runloop to pace these
|
||||
* iterations itself (runloop_pace_dupe_skip_engages). */
|
||||
if ( !data
|
||||
&& render_frame
|
||||
&& settings->bools.video_skip_dupe_frames
|
||||
&& !video_st->frame_repeat
|
||||
&& !menu_is_alive
|
||||
&& !video_info.input_driver_nonblock_state
|
||||
&& !video_info.black_frame_insertion
|
||||
&& video_info.shader_subframes <= 1
|
||||
&& !video_st->headset_interval
|
||||
#ifdef HAVE_THREADS
|
||||
&& !( video_st->thread_wrapper_active
|
||||
&& video_info.threaded_present_repeat)
|
||||
#endif
|
||||
)
|
||||
{
|
||||
render_frame = false;
|
||||
video_st->dupe_skip_hold = VIDEO_DUPE_SKIP_HOLD;
|
||||
}
|
||||
else if (video_st->dupe_skip_hold && !video_st->frame_repeat)
|
||||
video_st->dupe_skip_hold--;
|
||||
|
||||
/* What the shader chains read as FrameTimeDelta, from the reading
|
||||
* taken at the top of this function. Measured between the frames
|
||||
* the driver is handed, so a frame dropped by fast-forward
|
||||
|
||||
@@ -2054,6 +2054,10 @@ typedef struct
|
||||
unsigned driver_pacing_interval_us;
|
||||
uint8_t driver_pacing_mode;
|
||||
uint8_t driver_pacing_reason;
|
||||
/* Skip Duplicate Frames: the core frames left before the runloop
|
||||
* stops pacing for skipped repeats, rearmed by each one skipped.
|
||||
* Main thread. */
|
||||
uint8_t dupe_skip_hold;
|
||||
|
||||
bool threaded;
|
||||
|
||||
@@ -2606,6 +2610,11 @@ float video_driver_get_aspect_ratio(void);
|
||||
/* The aspect ratio, as the bits of the float in an atomic word. A
|
||||
* union, not a cast through a pointer, so it stays inside what the
|
||||
* standard allows and MSVC's aliasing rules. */
|
||||
|
||||
/* Core frames a skipped repeat keeps the runloop's dupe-skip pacing on
|
||||
* for: long enough to span the gaps between repeats down to 1 in 8. */
|
||||
#define VIDEO_DUPE_SKIP_HOLD 8
|
||||
|
||||
#ifdef HAVE_THREADS
|
||||
#define VIDEO_CACHE_KEEPER ((size_t)sthread_get_current_thread_id())
|
||||
#else
|
||||
|
||||
@@ -229,6 +229,7 @@ ACHIEVEMENTS
|
||||
#define RC_CLIENT_SUPPORTS_HASH 1
|
||||
|
||||
#include "../libretro-common/formats/cdfs/cdfs.c"
|
||||
#include "../libretro-common/streams/cdfs_file.c"
|
||||
|
||||
#include "../cheevos/cheevos.c"
|
||||
#include "../cheevos/cheevos_client.c"
|
||||
@@ -575,6 +576,7 @@ VIDEO IMAGE
|
||||
============================================================ */
|
||||
|
||||
#include "../libretro-common/formats/image_texture.c"
|
||||
#include "../libretro-common/streams/image_texture_file.c"
|
||||
#include "../libretro-common/formats/image/image_yuv_blit.c"
|
||||
#include "../libretro-common/formats/image_texture_scale.c"
|
||||
#include "../libretro-common/formats/image_texture_set.c"
|
||||
@@ -613,6 +615,7 @@ VIDEO IMAGE
|
||||
#include "../libretro-common/formats/image_transfer.c"
|
||||
#include "../libretro-common/formats/video_player.c"
|
||||
#include "../libretro-common/formats/data_transfer.c"
|
||||
#include "../libretro-common/streams/data_transfer_file.c"
|
||||
#ifdef HAVE_RPNG
|
||||
#include "../libretro-common/formats/png/rpng.c"
|
||||
#include "../libretro-common/formats/png/rpng_apng.c"
|
||||
|
||||
@@ -199,6 +199,14 @@ static const uint64_t xxh3_init_acc[8] = {
|
||||
#define XXH3_NOINLINE
|
||||
#endif
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
#define XXH3_FORCEINLINE __forceinline
|
||||
#elif defined(__GNUC__)
|
||||
#define XXH3_FORCEINLINE INLINE __attribute__((always_inline))
|
||||
#else
|
||||
#define XXH3_FORCEINLINE INLINE
|
||||
#endif
|
||||
|
||||
static INLINE uint64_t xxh3_rd64(const uint8_t *p)
|
||||
{
|
||||
uint64_t v;
|
||||
@@ -272,7 +280,8 @@ static INLINE uint64_t xxh3_mix16(const uint8_t *in, const uint8_t *sec,
|
||||
|
||||
/* ---- up to 240 octets ---- */
|
||||
|
||||
static uint64_t xxh3_0to16(const uint8_t *in, size_t len, uint64_t seed)
|
||||
static XXH3_FORCEINLINE uint64_t xxh3_0to16(const uint8_t *in, size_t len,
|
||||
uint64_t seed)
|
||||
{
|
||||
const uint8_t *k = xxh3_secret;
|
||||
|
||||
@@ -303,7 +312,8 @@ static uint64_t xxh3_0to16(const uint8_t *in, size_t len, uint64_t seed)
|
||||
return xxh3_xxh64_avalanche(seed ^ (xxh3_rd64(k + 56) ^ xxh3_rd64(k + 64)));
|
||||
}
|
||||
|
||||
static uint64_t xxh3_17to128(const uint8_t *in, size_t len, uint64_t seed)
|
||||
static XXH3_FORCEINLINE uint64_t xxh3_17to128(const uint8_t *in, size_t len,
|
||||
uint64_t seed)
|
||||
{
|
||||
const uint8_t *k = xxh3_secret;
|
||||
uint64_t acc = (uint64_t)len * XXH3_P64_1;
|
||||
@@ -328,7 +338,7 @@ static uint64_t xxh3_17to128(const uint8_t *in, size_t len, uint64_t seed)
|
||||
return xxh3_avalanche(acc);
|
||||
}
|
||||
|
||||
static XXH3_NOINLINE uint64_t xxh3_129to240(const uint8_t *in, size_t len,
|
||||
static XXH3_FORCEINLINE uint64_t xxh3_129to240(const uint8_t *in, size_t len,
|
||||
uint64_t seed)
|
||||
{
|
||||
const uint8_t *k = xxh3_secret;
|
||||
@@ -1240,15 +1250,28 @@ static XXH3_NOINLINE uint64_t xxh3_long(const uint8_t *in, size_t len,
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Seed 0 apart, where the secret folds into constants; each range its
|
||||
* own function, so none pays another's register saves */
|
||||
#define XXH3_SHORT(name, body) \
|
||||
static XXH3_NOINLINE uint64_t name##_0(const uint8_t *in, size_t len) \
|
||||
{ return body(in, len, 0); } \
|
||||
static XXH3_NOINLINE uint64_t name##_s(const uint8_t *in, size_t len, \
|
||||
uint64_t seed) \
|
||||
{ return body(in, len, seed); }
|
||||
XXH3_SHORT(xxh3_short16, xxh3_0to16)
|
||||
XXH3_SHORT(xxh3_short128, xxh3_17to128)
|
||||
XXH3_SHORT(xxh3_short240, xxh3_129to240)
|
||||
#undef XXH3_SHORT
|
||||
|
||||
uint64_t xxh3_64(const void *data, size_t len, uint64_t seed)
|
||||
{
|
||||
const uint8_t *in = (const uint8_t*)data;
|
||||
if (len <= 16)
|
||||
return xxh3_0to16(in, len, seed);
|
||||
return seed ? xxh3_short16_s(in, len, seed) : xxh3_short16_0(in, len);
|
||||
if (len <= 128)
|
||||
return xxh3_17to128(in, len, seed);
|
||||
return seed ? xxh3_short128_s(in, len, seed) : xxh3_short128_0(in, len);
|
||||
if (len <= 240)
|
||||
return xxh3_129to240(in, len, seed);
|
||||
return seed ? xxh3_short240_s(in, len, seed) : xxh3_short240_0(in, len);
|
||||
return xxh3_long(in, len, seed);
|
||||
}
|
||||
|
||||
|
||||
@@ -110,6 +110,7 @@ static int zstd_src_next(struct zstd_file_context *ctx,
|
||||
return rzstd_blocks_step(ctx->zs, data, len);
|
||||
for (;;)
|
||||
{
|
||||
int64_t got;
|
||||
size_t want = rzstd_blocks_want(ctx->zs);
|
||||
/* all of it out, and nothing after it */
|
||||
if (!want)
|
||||
@@ -117,8 +118,13 @@ static int zstd_src_next(struct zstd_file_context *ctx,
|
||||
? RZSTD_PROCESS_END : RZSTD_PROCESS_ERROR;
|
||||
if (want > RZSTD_WANT_MAX)
|
||||
return RZSTD_PROCESS_ERROR;
|
||||
filestream_seek(f, ctx->in_at, RETRO_VFS_SEEK_POSITION_START);
|
||||
if (filestream_read(f, ctx->in, (int64_t)want) != (int64_t)want)
|
||||
/* At the offset in one call; seek and read where there is none */
|
||||
if ((got = filestream_read_at(f, ctx->in, (int64_t)want,
|
||||
ctx->in_at)) < 0
|
||||
&& filestream_seek(f, ctx->in_at,
|
||||
RETRO_VFS_SEEK_POSITION_START) == 0)
|
||||
got = filestream_read(f, ctx->in, (int64_t)want);
|
||||
if (got != (int64_t)want)
|
||||
return RZSTD_PROCESS_ERROR;
|
||||
ctx->in_at += (int64_t)want;
|
||||
if (rzstd_blocks_give(ctx->zs, ctx->in, want, data, len)
|
||||
|
||||
@@ -2343,7 +2343,7 @@ static void r7z_progress_state(r7z_archive_t *a, int state)
|
||||
}
|
||||
|
||||
/* What a reader is to see from now on: nothing while it is written */
|
||||
static void r7z_progress_set(r7z_archive_t *a, const uint8_t *buf,
|
||||
static void r7z_progress_set(r7z_archive_t *a, uint8_t *buf,
|
||||
uint32_t folder, size_t pos, int state)
|
||||
{
|
||||
r7z_progress_state(a, 0);
|
||||
|
||||
@@ -3635,12 +3635,18 @@ static uint32_t audio_transfer_flac_seek_to(struct audio_transfer_flac *fl,
|
||||
#endif
|
||||
&& fl->data)
|
||||
{
|
||||
uint64_t at = 0;
|
||||
if (rflac_seek(fl->handle, frame, &at) == RFLAC_PROCESS_NEXT
|
||||
uint64_t at = 0;
|
||||
uint64_t point = 0;
|
||||
if (rflac_seek_point(fl->handle, frame, &at, &point)
|
||||
== RFLAC_PROCESS_NEXT
|
||||
&& at < fl->size)
|
||||
{
|
||||
uint64_t landed;
|
||||
rflac_seek_resumed(fl->handle, 0);
|
||||
/* Resumed at the seek point's own frame. Told 0 instead, the
|
||||
* decoder took the point for the start, and the remainder below
|
||||
* was decoded on from the point as if from frame 0: the seek
|
||||
* landed point frames past its target. */
|
||||
rflac_seek_resumed(fl->handle, point);
|
||||
rflac_set_in(fl->handle, (const uint8_t*)fl->data + at,
|
||||
fl->size - (size_t)at);
|
||||
rflac_set_eof(fl->handle);
|
||||
|
||||
@@ -1,31 +1,21 @@
|
||||
#include <retro_posix_source.h>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <formats/cdfs.h>
|
||||
|
||||
#include <retro_miscellaneous.h>
|
||||
#include <compat/strl.h>
|
||||
#include <file/file_path.h>
|
||||
#include <string/stdstring.h>
|
||||
|
||||
#ifdef HAVE_CHD
|
||||
#include <streams/chd_stream.h>
|
||||
#endif
|
||||
|
||||
/* This TU is deliberately kept as a single translation unit but is
|
||||
* organised in two layers:
|
||||
*
|
||||
* - the pure layer (everything through cdfs_parse_cue): ISO-9660
|
||||
* sector/directory/file logic plus cue-sheet parsing. It only ever
|
||||
* reads through an already-open track->stream or a caller-supplied
|
||||
* memory buffer, and never opens a path. Entry points:
|
||||
* cdfs_track_from_stream(), cdfs_parse_cue(), cdfs_open_file() and
|
||||
* the read/seek family.
|
||||
*
|
||||
* - the path adapter layer (cdfs_open_cue_track onward): resolves
|
||||
* filenames, opens cue/bin/iso/chd files via intfstream, then hands
|
||||
* the resulting stream to the pure layer. file_path.h and the VFS
|
||||
* are only used here. Callers that already hold a stream (or an
|
||||
* in-memory image) can skip this layer entirely. */
|
||||
/* ISO-9660 sector/directory/file logic plus cue-sheet parsing. It only
|
||||
* ever reads through an already-open track->stream or a caller-supplied
|
||||
* memory buffer, and never opens a path: streams/cdfs_file.c resolves
|
||||
* filenames, opens cue/bin/iso/chd files and hands the stream in here.
|
||||
* Entry points: cdfs_track_from_stream(), cdfs_parse_cue(),
|
||||
* cdfs_open_file() and the read/seek family. */
|
||||
|
||||
static void cdfs_determine_sector_size(cdfs_track_t* track)
|
||||
{
|
||||
@@ -97,9 +87,9 @@ static void cdfs_determine_sector_size(cdfs_track_t* track)
|
||||
}
|
||||
}
|
||||
|
||||
static void cdfs_determine_sector_size_from_file_size(cdfs_track_t* track)
|
||||
void cdfs_track_sector_size_from_length(cdfs_track_t* track)
|
||||
{
|
||||
/* attempt to determine stream_sector_size from file size */
|
||||
/* attempt to determine stream_sector_size from the stream's length */
|
||||
int64_t _len = intfstream_get_size(track->stream);
|
||||
if (_len <= 0)
|
||||
return;
|
||||
@@ -686,163 +676,6 @@ bool cdfs_parse_cue(const char *cue_text, size_t len,
|
||||
return false;
|
||||
}
|
||||
|
||||
/* --- Path adapter layer ------------------------------------------------ */
|
||||
|
||||
static cdfs_track_t* cdfs_open_cue_track(
|
||||
const char* path, unsigned int track_index)
|
||||
{
|
||||
cdfs_cue_info_t info;
|
||||
char track_path[PATH_MAX_LENGTH];
|
||||
int64_t _len;
|
||||
bool parsed = false;
|
||||
char *cue_contents = NULL;
|
||||
cdfs_track_t *track = NULL;
|
||||
intfstream_t *cue_stream = intfstream_open_file(path,
|
||||
RETRO_VFS_FILE_ACCESS_READ, RETRO_VFS_FILE_ACCESS_HINT_NONE);
|
||||
|
||||
if (!cue_stream)
|
||||
return NULL;
|
||||
|
||||
/* A cue sheet is a small text file: read it with one bulk read and
|
||||
* release the handle before the (potentially slow) bin open below,
|
||||
* instead of interleaving parsing with per-line stream reads. */
|
||||
_len = intfstream_get_size(cue_stream);
|
||||
if (_len > 0 && (cue_contents = (char*)malloc((size_t)_len)))
|
||||
parsed = (intfstream_read(cue_stream, cue_contents, _len) == _len);
|
||||
|
||||
intfstream_close(cue_stream);
|
||||
free(cue_stream);
|
||||
|
||||
if (parsed)
|
||||
parsed = cdfs_parse_cue(cue_contents, (size_t)_len,
|
||||
track_index, &info);
|
||||
|
||||
if (cue_contents)
|
||||
free(cue_contents);
|
||||
|
||||
if (!parsed)
|
||||
return NULL;
|
||||
|
||||
/* Resolve the FILE name from the cue against the cue's own
|
||||
* directory unless it already carries a path. */
|
||||
if ( strchr(info.bin_name, '/')
|
||||
|| strchr(info.bin_name, '\\'))
|
||||
strlcpy(track_path, info.bin_name, sizeof(track_path));
|
||||
else
|
||||
{
|
||||
fill_pathname_basedir(track_path, path, sizeof(track_path));
|
||||
strlcat(track_path, info.bin_name, sizeof(track_path));
|
||||
}
|
||||
|
||||
track = cdfs_track_from_stream(intfstream_open_file(
|
||||
track_path, RETRO_VFS_FILE_ACCESS_READ,
|
||||
RETRO_VFS_FILE_ACCESS_HINT_NONE),
|
||||
info.first_sector_offset, info.first_sector_index);
|
||||
|
||||
if (track && track->stream_sector_size == 0)
|
||||
{
|
||||
track->stream_sector_size = info.sector_size;
|
||||
|
||||
if (info.sector_size == 2352)
|
||||
track->stream_sector_header_size = 16;
|
||||
else if (info.sector_size == 2336)
|
||||
track->stream_sector_header_size = 8;
|
||||
}
|
||||
|
||||
return track;
|
||||
}
|
||||
|
||||
#ifdef HAVE_CHD
|
||||
static cdfs_track_t* cdfs_open_chd_track(const char* path, int32_t track_index)
|
||||
{
|
||||
cdfs_track_t *track;
|
||||
intfstream_t *intf_stream = intfstream_open_chd_track(path,
|
||||
RETRO_VFS_FILE_ACCESS_READ, RETRO_VFS_FILE_ACCESS_HINT_NONE,
|
||||
track_index);
|
||||
if (!intf_stream)
|
||||
return NULL;
|
||||
|
||||
track = cdfs_track_from_stream(intf_stream,
|
||||
intfstream_get_offset_to_start(intf_stream),
|
||||
intfstream_get_first_sector(intf_stream));
|
||||
|
||||
if (track && track->stream_sector_header_size == 0)
|
||||
{
|
||||
track->stream_sector_size = intfstream_get_frame_size(intf_stream);
|
||||
|
||||
if (track->stream_sector_size == 2352)
|
||||
track->stream_sector_header_size = 16;
|
||||
else if (track->stream_sector_size == 2336)
|
||||
track->stream_sector_header_size = 8;
|
||||
}
|
||||
|
||||
return track;
|
||||
}
|
||||
#endif
|
||||
|
||||
struct cdfs_track_t* cdfs_open_track(const char* path,
|
||||
unsigned int track_index)
|
||||
{
|
||||
const char* ext = path_get_extension(path);
|
||||
|
||||
if (string_is_equal_noncase(ext, "cue"))
|
||||
return cdfs_open_cue_track(path, track_index);
|
||||
|
||||
#ifdef HAVE_CHD
|
||||
if (string_is_equal_noncase(ext, "chd"))
|
||||
return cdfs_open_chd_track(path, track_index);
|
||||
#endif
|
||||
|
||||
/* if opening track 1, try opening as a raw track */
|
||||
if (track_index == 1)
|
||||
return cdfs_open_raw_track(path);
|
||||
|
||||
/* unsupported file type */
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct cdfs_track_t* cdfs_open_data_track(const char* path)
|
||||
{
|
||||
const char* ext = path_get_extension(path);
|
||||
|
||||
if (string_is_equal_noncase(ext, "cue"))
|
||||
return cdfs_open_cue_track(path, 0);
|
||||
|
||||
#ifdef HAVE_CHD
|
||||
if (string_is_equal_noncase(ext, "chd"))
|
||||
return cdfs_open_chd_track(path, CHDSTREAM_TRACK_PRIMARY);
|
||||
#endif
|
||||
|
||||
/* unsupported file type - try opening as a raw track */
|
||||
return cdfs_open_raw_track(path);
|
||||
}
|
||||
|
||||
cdfs_track_t* cdfs_open_raw_track(const char* path)
|
||||
{
|
||||
const char *ext = path_get_extension(path);
|
||||
cdfs_track_t *track = NULL;
|
||||
|
||||
if ( string_is_equal_noncase(ext, "bin")
|
||||
|| string_is_equal_noncase(ext, "iso"))
|
||||
{
|
||||
intfstream_t* file = intfstream_open_file(path,
|
||||
RETRO_VFS_FILE_ACCESS_READ, RETRO_VFS_FILE_ACCESS_HINT_NONE);
|
||||
|
||||
track = cdfs_track_from_stream(file, 0, 0);
|
||||
if (track && track->stream_sector_size == 0)
|
||||
{
|
||||
cdfs_determine_sector_size_from_file_size(track);
|
||||
if (track->stream_sector_size == 0)
|
||||
{
|
||||
cdfs_close_track(track);
|
||||
track = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return track;
|
||||
}
|
||||
|
||||
void cdfs_close_track(cdfs_track_t* track)
|
||||
{
|
||||
if (track)
|
||||
|
||||
@@ -29,8 +29,8 @@
|
||||
* avail() for the buffer and how much of it is real, and the
|
||||
* complete()/failed()/capped() terminals.
|
||||
*
|
||||
* - prefix (data_transfer_open_prefix): a file whose length is known
|
||||
* at open. Address space for the whole length is reserved up
|
||||
* - prefix (data_transfer_open_prefix_reader): a source whose length
|
||||
* is known at open, read at offsets through a callback. Address space for the whole length is reserved up
|
||||
* front so the base never moves, and physical pages are committed
|
||||
* DT_COMMIT_STEP at a time as the fill advances, in DT_READ_CHUNK
|
||||
* reads. commit_cap bounds the committed bytes and turns overrun
|
||||
@@ -39,7 +39,7 @@
|
||||
* sequential consumer holds a constant window over a file of any
|
||||
* size.
|
||||
*
|
||||
* - window (data_transfer_open_window): the same reservation used
|
||||
* - window (data_transfer_open_window_reader): the same reservation used
|
||||
* cyclically, for sequential consumers that loop. A permanent
|
||||
* head of 'keep' bytes plus a moving [wlo, whi) that feed()
|
||||
* advances behind the consumer and extends ahead of it;
|
||||
@@ -59,8 +59,11 @@
|
||||
* growable buffer that shares this file and header but touches no
|
||||
* data_transfer_t and no page machinery.
|
||||
*
|
||||
* File access goes through filestream/VFS throughout, so 64-bit
|
||||
* offsets and VFS-only paths work. Where the platform cannot reserve
|
||||
* No file I/O here: every read is the reader's. streams/
|
||||
* data_transfer_file.c opens paths through filestream/VFS - 64-bit
|
||||
* offsets, VFS-only paths - and hands the reader in, keeping the
|
||||
* data_transfer_open_prefix/_window names. Where the platform cannot
|
||||
* reserve
|
||||
* address space, every mode degrades to a plain allocation of
|
||||
* min(len, commit_cap) - the whole file when there is no cap - and a
|
||||
* window simply holds the whole file; reserve_supported() and
|
||||
@@ -68,8 +71,8 @@
|
||||
*
|
||||
* NOT IMPLEMENTED
|
||||
*
|
||||
* - Asynchrony. Every read is a synchronous filestream_read on the
|
||||
* calling thread: iterate() blocks for the length of its budget,
|
||||
* - Asynchrony. Every read is a synchronous call of the reader on
|
||||
* the calling thread: iterate() blocks for the length of its budget,
|
||||
* and there is nothing in flight for free() to cancel. Pacing is
|
||||
* by byte budget and the caller does the time-slicing. The module
|
||||
* began as a wrapper over nbio, which is where its vocabulary came
|
||||
@@ -162,7 +165,6 @@
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <memmap.h>
|
||||
#include <streams/file_stream.h>
|
||||
#include <string.h>
|
||||
#include <formats/data_transfer.h>
|
||||
|
||||
@@ -302,9 +304,10 @@ struct data_transfer_inert { size_t lo, hi; };
|
||||
|
||||
struct data_transfer
|
||||
{
|
||||
/* file-backed reader (open_prefix, and open_window over it) */
|
||||
RFILE *f;
|
||||
/* the reader every prefix and window fills from, and what frees
|
||||
* its user data with the transfer, if anything */
|
||||
data_transfer_read_at_t read_at;
|
||||
data_transfer_release_t release;
|
||||
void *read_ud;
|
||||
uint8_t *map; /* reserved (or fallback-allocated) buffer */
|
||||
size_t map_len; /* reserved bytes (page-rounded), 0 = fallback */
|
||||
@@ -392,17 +395,12 @@ void data_transfer_arena_release(data_transfer_arena_t *a)
|
||||
|
||||
static int data_transfer_read_at(data_transfer_t *dt, size_t off,
|
||||
uint8_t *dst, size_t n);
|
||||
/* open_prefix's body. allow_pool is 0 for open_window, which builds
|
||||
* on a prefix handle but reads map_len in punch/peek and so needs it
|
||||
* to be the file's own rounded length rather than a pool slot's. */
|
||||
static data_transfer_t *data_transfer_open_reader_ex(const char *path,
|
||||
size_t commit_cap, int allow_pool, data_transfer_read_at_t read_at,
|
||||
size_t length, void *ud);
|
||||
static data_transfer_t *data_transfer_open_prefix_ex(const char *path,
|
||||
size_t commit_cap, int allow_pool)
|
||||
{
|
||||
return data_transfer_open_reader_ex(path,commit_cap,allow_pool,NULL,0,NULL);
|
||||
}
|
||||
/* The prefix body. allow_pool is 0 for a window, which builds on a
|
||||
* prefix handle but reads map_len in punch/peek and so needs it to be
|
||||
* the source's own rounded length rather than a pool slot's. */
|
||||
static data_transfer_t *data_transfer_open_reader_ex(size_t commit_cap,
|
||||
int allow_pool, data_transfer_read_at_t read_at, size_t length,
|
||||
void *ud);
|
||||
/* The fill proper. data_transfer_iterate() refuses to run it on a
|
||||
* window; open_window's no-reservation path genuinely needs it and
|
||||
* calls it here directly. */
|
||||
@@ -575,16 +573,19 @@ static data_transfer_t *data_transfer_start_window(data_transfer_t *dt, size_t k
|
||||
return dt;
|
||||
}
|
||||
|
||||
data_transfer_t *data_transfer_open_window(const char *path, size_t keep)
|
||||
{
|
||||
return data_transfer_start_window(data_transfer_open_prefix_ex(path,0,0),keep);
|
||||
}
|
||||
data_transfer_t *data_transfer_open_window_reader(size_t length,size_t keep,
|
||||
data_transfer_read_at_t read_at,void *ud)
|
||||
{
|
||||
if (!read_at) return NULL;
|
||||
return data_transfer_start_window(
|
||||
data_transfer_open_reader_ex(NULL,0,0,read_at,length,ud),keep);
|
||||
data_transfer_open_reader_ex(0,0,read_at,length,ud),keep);
|
||||
}
|
||||
|
||||
void data_transfer_set_release(data_transfer_t *dt,
|
||||
data_transfer_release_t release)
|
||||
{
|
||||
if (dt)
|
||||
dt->release = release;
|
||||
}
|
||||
|
||||
bool data_transfer_reserve_supported(void)
|
||||
@@ -874,6 +875,13 @@ bool data_transfer_window_feed_budget(data_transfer_t *dt, size_t tell,
|
||||
dt->wtell = tell;
|
||||
if (tell > margin && tell - margin > dt->wlo)
|
||||
data_transfer_window_advance(dt, tell - margin);
|
||||
/* A frontier short of the margin behind the consumer moves up to it:
|
||||
* the bytes between are behind the consumer and outside its margin,
|
||||
* never read, and filling them in would make a gap of any size
|
||||
* resident - a consumer that starts or jumps far into a large file,
|
||||
* or one left there by the rewind above. */
|
||||
if (dt->map_len && tell > margin && tell - margin > dt->whi)
|
||||
data_transfer_window_rebase(dt, tell - margin);
|
||||
hi = tell + lookahead;
|
||||
/* The ceiling applies only while the frontier covers the consumer.
|
||||
* A frontier behind tell means the consumer's next read lands on
|
||||
@@ -901,44 +909,18 @@ bool data_transfer_window_feed(data_transfer_t *dt, size_t tell,
|
||||
margin, 0, NULL);
|
||||
}
|
||||
|
||||
static data_transfer_t *data_transfer_open_reader_ex(const char *path,
|
||||
size_t commit_cap, int allow_pool, data_transfer_read_at_t read_at,
|
||||
size_t length, void *ud)
|
||||
static data_transfer_t *data_transfer_open_reader_ex(size_t commit_cap,
|
||||
int allow_pool, data_transfer_read_at_t read_at, size_t length,
|
||||
void *ud)
|
||||
{
|
||||
data_transfer_t *dt;
|
||||
RFILE *f = NULL;
|
||||
int64_t l;
|
||||
/* filestream: 64-bit lengths and offsets everywhere (a plain long
|
||||
* ftell caps at 2 GB on LLP64 and 32-bit targets), and the read
|
||||
* routes through the VFS interface when one is registered, so
|
||||
* paths only the VFS can open - archive members, Android
|
||||
* content:// documents, frontend overrides - work like any
|
||||
* other. */
|
||||
if (read_at)
|
||||
{
|
||||
if (!length || (uint64_t)length > INT64_MAX) return NULL;
|
||||
l = (int64_t)length;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!(f = filestream_open(path,RETRO_VFS_FILE_ACCESS_READ,
|
||||
RETRO_VFS_FILE_ACCESS_HINT_NONE))) return NULL;
|
||||
l = filestream_get_size(f);
|
||||
if (l <= 0 || (uint64_t)l > SIZE_MAX)
|
||||
{
|
||||
filestream_close(f);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
if (!read_at || !length || (uint64_t)length > INT64_MAX)
|
||||
return NULL;
|
||||
if (!(dt = (data_transfer_t*)calloc(1, sizeof(*dt))))
|
||||
{
|
||||
if (f) filestream_close(f);
|
||||
return NULL;
|
||||
}
|
||||
dt->f = f;
|
||||
dt->read_at = read_at;
|
||||
dt->read_ud = ud;
|
||||
dt->len = (size_t)l;
|
||||
dt->len = length;
|
||||
dt->cap = commit_cap;
|
||||
|
||||
{
|
||||
@@ -1019,7 +1001,6 @@ static data_transfer_t *data_transfer_open_reader_ex(const char *path,
|
||||
dt->cap = w;
|
||||
if (!(dt->map = (uint8_t*)malloc(w ? w : 1)))
|
||||
{
|
||||
if (f) filestream_close(f);
|
||||
free(dt);
|
||||
return NULL;
|
||||
}
|
||||
@@ -1028,10 +1009,10 @@ static data_transfer_t *data_transfer_open_reader_ex(const char *path,
|
||||
return dt;
|
||||
}
|
||||
|
||||
data_transfer_t *data_transfer_open_prefix(const char *path,
|
||||
size_t commit_cap)
|
||||
data_transfer_t *data_transfer_open_prefix_reader(size_t length,
|
||||
size_t commit_cap, data_transfer_read_at_t read_at, void *ud)
|
||||
{
|
||||
return data_transfer_open_prefix_ex(path, commit_cap, 1);
|
||||
return data_transfer_open_reader_ex(commit_cap, 1, read_at, length, ud);
|
||||
}
|
||||
|
||||
/* Grow the physical backing to cover at least 'need' bytes.
|
||||
@@ -1161,8 +1142,6 @@ static size_t data_transfer_prefix_iterate(data_transfer_t *dt,
|
||||
size_t end = dt->inert[lo].hi;
|
||||
size_t release_end;
|
||||
if (dt->cap && end > dt->cap) end = dt->cap;
|
||||
if (filestream_seek(dt->f, (int64_t)end, RETRO_VFS_SEEK_POSITION_START) < 0)
|
||||
{ dt->done = dt->failed = 1; break; }
|
||||
/* Commit growth may have armed pages past the preceding
|
||||
* read. Keep an inert interval unreadable after crossing
|
||||
* it, including in strict builds and after refill. */
|
||||
@@ -1224,9 +1203,8 @@ static size_t data_transfer_prefix_iterate(data_transfer_t *dt,
|
||||
continue;
|
||||
}
|
||||
{
|
||||
int64_t r = dt->read_at
|
||||
? dt->read_at(dt->read_ud,dt->avail,dt->map+dt->avail,chunk)
|
||||
: filestream_read(dt->f,dt->map+dt->avail,(int64_t)chunk);
|
||||
int64_t r = dt->read_at(dt->read_ud, dt->avail,
|
||||
dt->map + dt->avail, chunk);
|
||||
got = r > 0 && (uint64_t)r <= chunk ? (size_t)r : 0;
|
||||
}
|
||||
dt->avail += got;
|
||||
@@ -1256,29 +1234,14 @@ static size_t data_transfer_prefix_iterate(data_transfer_t *dt,
|
||||
static int data_transfer_read_at(data_transfer_t *dt, size_t off,
|
||||
uint8_t *dst, size_t n)
|
||||
{
|
||||
int64_t save, r;
|
||||
if (dt->read_at)
|
||||
return dt->read_at(dt->read_ud,off,dst,n) == (int64_t)n;
|
||||
/* At the offset, the cursor and read-ahead untouched; seek and read
|
||||
* where the file has no positional reads */
|
||||
if ((r = filestream_read_at(dt->f, dst, (int64_t)n, (int64_t)off)) >= 0)
|
||||
return r == (int64_t)n;
|
||||
save = filestream_tell(dt->f);
|
||||
if (save < 0
|
||||
|| (save != (int64_t)off
|
||||
&& filestream_seek(dt->f, (int64_t)off,
|
||||
RETRO_VFS_SEEK_POSITION_START) < 0))
|
||||
return 0;
|
||||
r = filestream_read(dt->f, dst, (int64_t)n);
|
||||
if (!dt->window || !dt->map_len)
|
||||
filestream_seek(dt->f, save, RETRO_VFS_SEEK_POSITION_START);
|
||||
return r == (int64_t)n;
|
||||
return dt->read_at(dt->read_ud, off, dst, n) == (int64_t)n;
|
||||
}
|
||||
|
||||
bool data_transfer_prefix_inert(data_transfer_t *dt, size_t from, size_t to)
|
||||
{
|
||||
size_t lo, hi, first, last, count;
|
||||
if (!dt || !dt->f || dt->window || dt->failed || from > to || to > dt->len)
|
||||
if (!dt || !dt->read_at || dt->window || dt->failed || from > to
|
||||
|| to > dt->len)
|
||||
return false;
|
||||
if (!dt->map_len || dt->pooled) return true;
|
||||
lo = (from / dt->page + (from % dt->page != 0)) * dt->page;
|
||||
@@ -1320,7 +1283,7 @@ bool data_transfer_prefix_inert(data_transfer_t *dt, size_t from, size_t to)
|
||||
void data_transfer_discard(data_transfer_t *dt, size_t up_to)
|
||||
{
|
||||
size_t lo;
|
||||
if (!dt || !dt->f || !dt->map_len || !dt->page)
|
||||
if (!dt || !dt->read_at || !dt->map_len || !dt->page)
|
||||
return; /* source or fallback: bytes stay */
|
||||
if (dt->window)
|
||||
return; /* not this surface - see below */
|
||||
@@ -1342,7 +1305,7 @@ bool data_transfer_refill(data_transfer_t *dt, size_t from)
|
||||
size_t lo, end, at, i;
|
||||
if (!dt)
|
||||
return false;
|
||||
if (!dt->f || !dt->map_len || dt->window || from >= dt->low)
|
||||
if (!dt->read_at || !dt->map_len || dt->window || from >= dt->low)
|
||||
return !dt->failed; /* nothing was released there */
|
||||
lo = (from / dt->page) * dt->page;
|
||||
end = dt->low;
|
||||
@@ -1378,7 +1341,7 @@ bool data_transfer_refill(data_transfer_t *dt, size_t from)
|
||||
|
||||
/* The prefix surface is inert on a window handle.
|
||||
*
|
||||
* A window keeps a live RFILE and a reservation, so these three would
|
||||
* A window keeps a live reader and a reservation, so these three would
|
||||
* otherwise pass their own guards on one and then disagree with the
|
||||
* window's bookkeeping. discard() is the dangerous one: its 'low'
|
||||
* frontier knows nothing of keep/wlo/whi/wfreed, so it releases from
|
||||
@@ -1399,7 +1362,7 @@ size_t data_transfer_iterate_while(data_transfer_t *dt, size_t max_bytes,
|
||||
return 0;
|
||||
if (dt->window)
|
||||
return dt->avail; /* not this surface - see above */
|
||||
if (dt->f || dt->src_cb)
|
||||
if (dt->read_at || dt->src_cb)
|
||||
return data_transfer_prefix_iterate(dt, max_bytes,
|
||||
should_continue, ud);
|
||||
return dt->avail;
|
||||
@@ -1447,8 +1410,8 @@ void data_transfer_free(data_transfer_t *dt)
|
||||
{
|
||||
if (!dt)
|
||||
return;
|
||||
if (dt->f)
|
||||
filestream_close(dt->f);
|
||||
if (dt->release)
|
||||
dt->release(dt->read_ud);
|
||||
free(dt->inert);
|
||||
if (dt->map)
|
||||
{
|
||||
|
||||
@@ -2154,6 +2154,13 @@ static uint32_t rflac__decode_samples_with_residual__rice__scalar(rflac_bs* bs,
|
||||
}
|
||||
|
||||
#if defined(RFLAC_SUPPORT_SSE2)
|
||||
/* rflac__s16_round for four lanes, but for the clamp, which the saturating
|
||||
* pack after it does */
|
||||
static INLINE __m128i rflac__mm_round_s16_epi32(__m128i x)
|
||||
{
|
||||
return _mm_srai_epi32(_mm_add_epi32(_mm_srai_epi32(x, 15), _mm_set1_epi32(1)), 1);
|
||||
}
|
||||
|
||||
static INLINE __m128i rflac__mm_packs_interleaved_epi32(__m128i a, __m128i b)
|
||||
{
|
||||
__m128i r;
|
||||
@@ -4387,6 +4394,17 @@ static rflac* rflac_open_with_metadata_private(rflac_read_proc onRead,
|
||||
return pFlac;
|
||||
}
|
||||
|
||||
/* A sample left-justified in 32 bits, to 16: rounded to the nearest step
|
||||
* (halves up) and clamped, where a shift alone rounded down - every sample
|
||||
* of a 24-bit stream half a step low on average. Shifting by 15 first keeps
|
||||
* the rounding add from overflowing; only +32768 can come out, at the very
|
||||
* top, and is clamped. A stream of 16 bits or fewer is unchanged by it. */
|
||||
static INLINE int16_t rflac__s16_round(uint32_t x)
|
||||
{
|
||||
int32_t r = (((int32_t)x >> 15) + 1) >> 1;
|
||||
return (int16_t)(r > 32767 ? 32767 : r);
|
||||
}
|
||||
|
||||
static INLINE void rflac_read_pcm_frames_s16__decode_left_side__scalar(
|
||||
rflac* pFlac, uint64_t frameCount, uint32_t unusedBitsPerSample,
|
||||
const int32_t* pInputSamples0, const int32_t* pInputSamples1,
|
||||
@@ -4416,24 +4434,14 @@ static INLINE void rflac_read_pcm_frames_s16__decode_left_side__scalar(
|
||||
uint32_t right2 = left2 - side2;
|
||||
uint32_t right3 = left3 - side3;
|
||||
|
||||
left0 >>= 16;
|
||||
left1 >>= 16;
|
||||
left2 >>= 16;
|
||||
left3 >>= 16;
|
||||
|
||||
right0 >>= 16;
|
||||
right1 >>= 16;
|
||||
right2 >>= 16;
|
||||
right3 >>= 16;
|
||||
|
||||
pOutputSamples[i*8+0] = (int16_t)left0;
|
||||
pOutputSamples[i*8+1] = (int16_t)right0;
|
||||
pOutputSamples[i*8+2] = (int16_t)left1;
|
||||
pOutputSamples[i*8+3] = (int16_t)right1;
|
||||
pOutputSamples[i*8+4] = (int16_t)left2;
|
||||
pOutputSamples[i*8+5] = (int16_t)right2;
|
||||
pOutputSamples[i*8+6] = (int16_t)left3;
|
||||
pOutputSamples[i*8+7] = (int16_t)right3;
|
||||
pOutputSamples[i*8+0] = rflac__s16_round(left0);
|
||||
pOutputSamples[i*8+1] = rflac__s16_round(right0);
|
||||
pOutputSamples[i*8+2] = rflac__s16_round(left1);
|
||||
pOutputSamples[i*8+3] = rflac__s16_round(right1);
|
||||
pOutputSamples[i*8+4] = rflac__s16_round(left2);
|
||||
pOutputSamples[i*8+5] = rflac__s16_round(right2);
|
||||
pOutputSamples[i*8+6] = rflac__s16_round(left3);
|
||||
pOutputSamples[i*8+7] = rflac__s16_round(right3);
|
||||
}
|
||||
|
||||
for (i = (frameCount4 << 2); i < frameCount; ++i)
|
||||
@@ -4442,11 +4450,8 @@ static INLINE void rflac_read_pcm_frames_s16__decode_left_side__scalar(
|
||||
uint32_t side = pInputSamples1U32[i] << shift1;
|
||||
uint32_t right = left - side;
|
||||
|
||||
left >>= 16;
|
||||
right >>= 16;
|
||||
|
||||
pOutputSamples[i*2+0] = (int16_t)left;
|
||||
pOutputSamples[i*2+1] = (int16_t)right;
|
||||
pOutputSamples[i*2+0] = rflac__s16_round(left);
|
||||
pOutputSamples[i*2+1] = rflac__s16_round(right);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4468,8 +4473,8 @@ static INLINE void rflac_read_pcm_frames_s16__decode_left_side__sse2(
|
||||
__m128i side = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), shift1);
|
||||
__m128i right = _mm_sub_epi32(left, side);
|
||||
|
||||
left = _mm_srai_epi32(left, 16);
|
||||
right = _mm_srai_epi32(right, 16);
|
||||
left = rflac__mm_round_s16_epi32(left);
|
||||
right = rflac__mm_round_s16_epi32(right);
|
||||
|
||||
_mm_storeu_si128((__m128i*)(pOutputSamples + i*8), rflac__mm_packs_interleaved_epi32(left, right));
|
||||
}
|
||||
@@ -4479,11 +4484,8 @@ static INLINE void rflac_read_pcm_frames_s16__decode_left_side__sse2(
|
||||
uint32_t side = pInputSamples1U32[i] << shift1;
|
||||
uint32_t right = left - side;
|
||||
|
||||
left >>= 16;
|
||||
right >>= 16;
|
||||
|
||||
pOutputSamples[i*2+0] = (int16_t)left;
|
||||
pOutputSamples[i*2+1] = (int16_t)right;
|
||||
pOutputSamples[i*2+0] = rflac__s16_round(left);
|
||||
pOutputSamples[i*2+1] = rflac__s16_round(right);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -4509,14 +4511,14 @@ static INLINE void rflac_read_pcm_frames_s16__decode_left_side__neon(
|
||||
uint32x4_t side = vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), shift1_4);
|
||||
uint32x4_t right = vsubq_u32(left, side);
|
||||
|
||||
left = vshrq_n_u32(left, 16);
|
||||
right = vshrq_n_u32(right, 16);
|
||||
left = vreinterpretq_u32_s32(vrshrq_n_s32(vreinterpretq_s32_u32(left), 16));
|
||||
right = vreinterpretq_u32_s32(vrshrq_n_s32(vreinterpretq_s32_u32(right), 16));
|
||||
|
||||
{
|
||||
/* interleaving store; replaces vzip + contiguous store */
|
||||
uint16x4x2_t v;
|
||||
v.val[0] = vmovn_u32(left);
|
||||
v.val[1] = vmovn_u32(right);
|
||||
v.val[0] = vreinterpret_u16_s16(vqmovn_s32(vreinterpretq_s32_u32(left)));
|
||||
v.val[1] = vreinterpret_u16_s16(vqmovn_s32(vreinterpretq_s32_u32(right)));
|
||||
vst2_u16((uint16_t*)pOutputSamples + i*8, v);
|
||||
}
|
||||
}
|
||||
@@ -4527,11 +4529,8 @@ static INLINE void rflac_read_pcm_frames_s16__decode_left_side__neon(
|
||||
uint32_t side = pInputSamples1U32[i] << shift1;
|
||||
uint32_t right = left - side;
|
||||
|
||||
left >>= 16;
|
||||
right >>= 16;
|
||||
|
||||
pOutputSamples[i*2+0] = (int16_t)left;
|
||||
pOutputSamples[i*2+1] = (int16_t)right;
|
||||
pOutputSamples[i*2+0] = rflac__s16_round(left);
|
||||
pOutputSamples[i*2+1] = rflac__s16_round(right);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -4585,24 +4584,14 @@ static INLINE void rflac_read_pcm_frames_s16__decode_right_side__scalar(
|
||||
uint32_t left2 = right2 + side2;
|
||||
uint32_t left3 = right3 + side3;
|
||||
|
||||
left0 >>= 16;
|
||||
left1 >>= 16;
|
||||
left2 >>= 16;
|
||||
left3 >>= 16;
|
||||
|
||||
right0 >>= 16;
|
||||
right1 >>= 16;
|
||||
right2 >>= 16;
|
||||
right3 >>= 16;
|
||||
|
||||
pOutputSamples[i*8+0] = (int16_t)left0;
|
||||
pOutputSamples[i*8+1] = (int16_t)right0;
|
||||
pOutputSamples[i*8+2] = (int16_t)left1;
|
||||
pOutputSamples[i*8+3] = (int16_t)right1;
|
||||
pOutputSamples[i*8+4] = (int16_t)left2;
|
||||
pOutputSamples[i*8+5] = (int16_t)right2;
|
||||
pOutputSamples[i*8+6] = (int16_t)left3;
|
||||
pOutputSamples[i*8+7] = (int16_t)right3;
|
||||
pOutputSamples[i*8+0] = rflac__s16_round(left0);
|
||||
pOutputSamples[i*8+1] = rflac__s16_round(right0);
|
||||
pOutputSamples[i*8+2] = rflac__s16_round(left1);
|
||||
pOutputSamples[i*8+3] = rflac__s16_round(right1);
|
||||
pOutputSamples[i*8+4] = rflac__s16_round(left2);
|
||||
pOutputSamples[i*8+5] = rflac__s16_round(right2);
|
||||
pOutputSamples[i*8+6] = rflac__s16_round(left3);
|
||||
pOutputSamples[i*8+7] = rflac__s16_round(right3);
|
||||
}
|
||||
|
||||
for (i = (frameCount4 << 2); i < frameCount; ++i)
|
||||
@@ -4611,11 +4600,8 @@ static INLINE void rflac_read_pcm_frames_s16__decode_right_side__scalar(
|
||||
uint32_t right = pInputSamples1U32[i] << shift1;
|
||||
uint32_t left = right + side;
|
||||
|
||||
left >>= 16;
|
||||
right >>= 16;
|
||||
|
||||
pOutputSamples[i*2+0] = (int16_t)left;
|
||||
pOutputSamples[i*2+1] = (int16_t)right;
|
||||
pOutputSamples[i*2+0] = rflac__s16_round(left);
|
||||
pOutputSamples[i*2+1] = rflac__s16_round(right);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4638,8 +4624,8 @@ static INLINE void rflac_read_pcm_frames_s16__decode_right_side__sse2(
|
||||
__m128i right = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), shift1);
|
||||
__m128i left = _mm_add_epi32(right, side);
|
||||
|
||||
left = _mm_srai_epi32(left, 16);
|
||||
right = _mm_srai_epi32(right, 16);
|
||||
left = rflac__mm_round_s16_epi32(left);
|
||||
right = rflac__mm_round_s16_epi32(right);
|
||||
|
||||
_mm_storeu_si128((__m128i*)(pOutputSamples + i*8), rflac__mm_packs_interleaved_epi32(left, right));
|
||||
}
|
||||
@@ -4649,11 +4635,8 @@ static INLINE void rflac_read_pcm_frames_s16__decode_right_side__sse2(
|
||||
uint32_t right = pInputSamples1U32[i] << shift1;
|
||||
uint32_t left = right + side;
|
||||
|
||||
left >>= 16;
|
||||
right >>= 16;
|
||||
|
||||
pOutputSamples[i*2+0] = (int16_t)left;
|
||||
pOutputSamples[i*2+1] = (int16_t)right;
|
||||
pOutputSamples[i*2+0] = rflac__s16_round(left);
|
||||
pOutputSamples[i*2+1] = rflac__s16_round(right);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -4679,14 +4662,14 @@ static INLINE void rflac_read_pcm_frames_s16__decode_right_side__neon(
|
||||
uint32x4_t right = vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), shift1_4);
|
||||
uint32x4_t left = vaddq_u32(right, side);
|
||||
|
||||
left = vshrq_n_u32(left, 16);
|
||||
right = vshrq_n_u32(right, 16);
|
||||
left = vreinterpretq_u32_s32(vrshrq_n_s32(vreinterpretq_s32_u32(left), 16));
|
||||
right = vreinterpretq_u32_s32(vrshrq_n_s32(vreinterpretq_s32_u32(right), 16));
|
||||
|
||||
{
|
||||
/* interleaving store; replaces vzip + contiguous store */
|
||||
uint16x4x2_t v;
|
||||
v.val[0] = vmovn_u32(left);
|
||||
v.val[1] = vmovn_u32(right);
|
||||
v.val[0] = vreinterpret_u16_s16(vqmovn_s32(vreinterpretq_s32_u32(left)));
|
||||
v.val[1] = vreinterpret_u16_s16(vqmovn_s32(vreinterpretq_s32_u32(right)));
|
||||
vst2_u16((uint16_t*)pOutputSamples + i*8, v);
|
||||
}
|
||||
}
|
||||
@@ -4696,11 +4679,8 @@ static INLINE void rflac_read_pcm_frames_s16__decode_right_side__neon(
|
||||
uint32_t right = pInputSamples1U32[i] << shift1;
|
||||
uint32_t left = right + side;
|
||||
|
||||
left >>= 16;
|
||||
right >>= 16;
|
||||
|
||||
pOutputSamples[i*2+0] = (int16_t)left;
|
||||
pOutputSamples[i*2+1] = (int16_t)right;
|
||||
pOutputSamples[i*2+0] = rflac__s16_round(left);
|
||||
pOutputSamples[i*2+1] = rflac__s16_round(right);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -4775,24 +4755,14 @@ static INLINE void rflac_read_pcm_frames_s16__decode_mid_side__scalar(
|
||||
temp2R = (mid2 - side2) << shift;
|
||||
temp3R = (mid3 - side3) << shift;
|
||||
|
||||
temp0L >>= 16;
|
||||
temp1L >>= 16;
|
||||
temp2L >>= 16;
|
||||
temp3L >>= 16;
|
||||
|
||||
temp0R >>= 16;
|
||||
temp1R >>= 16;
|
||||
temp2R >>= 16;
|
||||
temp3R >>= 16;
|
||||
|
||||
pOutputSamples[i*8+0] = (int16_t)temp0L;
|
||||
pOutputSamples[i*8+1] = (int16_t)temp0R;
|
||||
pOutputSamples[i*8+2] = (int16_t)temp1L;
|
||||
pOutputSamples[i*8+3] = (int16_t)temp1R;
|
||||
pOutputSamples[i*8+4] = (int16_t)temp2L;
|
||||
pOutputSamples[i*8+5] = (int16_t)temp2R;
|
||||
pOutputSamples[i*8+6] = (int16_t)temp3L;
|
||||
pOutputSamples[i*8+7] = (int16_t)temp3R;
|
||||
pOutputSamples[i*8+0] = rflac__s16_round(temp0L);
|
||||
pOutputSamples[i*8+1] = rflac__s16_round(temp0R);
|
||||
pOutputSamples[i*8+2] = rflac__s16_round(temp1L);
|
||||
pOutputSamples[i*8+3] = rflac__s16_round(temp1R);
|
||||
pOutputSamples[i*8+4] = rflac__s16_round(temp2L);
|
||||
pOutputSamples[i*8+5] = rflac__s16_round(temp2R);
|
||||
pOutputSamples[i*8+6] = rflac__s16_round(temp3L);
|
||||
pOutputSamples[i*8+7] = rflac__s16_round(temp3R);
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -4833,24 +4803,14 @@ static INLINE void rflac_read_pcm_frames_s16__decode_mid_side__scalar(
|
||||
temp2R = ((int32_t)(mid2 - side2) >> 1);
|
||||
temp3R = ((int32_t)(mid3 - side3) >> 1);
|
||||
|
||||
temp0L >>= 16;
|
||||
temp1L >>= 16;
|
||||
temp2L >>= 16;
|
||||
temp3L >>= 16;
|
||||
|
||||
temp0R >>= 16;
|
||||
temp1R >>= 16;
|
||||
temp2R >>= 16;
|
||||
temp3R >>= 16;
|
||||
|
||||
pOutputSamples[i*8+0] = (int16_t)temp0L;
|
||||
pOutputSamples[i*8+1] = (int16_t)temp0R;
|
||||
pOutputSamples[i*8+2] = (int16_t)temp1L;
|
||||
pOutputSamples[i*8+3] = (int16_t)temp1R;
|
||||
pOutputSamples[i*8+4] = (int16_t)temp2L;
|
||||
pOutputSamples[i*8+5] = (int16_t)temp2R;
|
||||
pOutputSamples[i*8+6] = (int16_t)temp3L;
|
||||
pOutputSamples[i*8+7] = (int16_t)temp3R;
|
||||
pOutputSamples[i*8+0] = rflac__s16_round(temp0L);
|
||||
pOutputSamples[i*8+1] = rflac__s16_round(temp0R);
|
||||
pOutputSamples[i*8+2] = rflac__s16_round(temp1L);
|
||||
pOutputSamples[i*8+3] = rflac__s16_round(temp1R);
|
||||
pOutputSamples[i*8+4] = rflac__s16_round(temp2L);
|
||||
pOutputSamples[i*8+5] = rflac__s16_round(temp2R);
|
||||
pOutputSamples[i*8+6] = rflac__s16_round(temp3L);
|
||||
pOutputSamples[i*8+7] = rflac__s16_round(temp3R);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4861,8 +4821,8 @@ static INLINE void rflac_read_pcm_frames_s16__decode_mid_side__scalar(
|
||||
|
||||
mid = (mid << 1) | (side & 0x01);
|
||||
|
||||
pOutputSamples[i*2+0] = (int16_t)(((uint32_t)((int32_t)(mid + side) >> 1) << unusedBitsPerSample) >> 16);
|
||||
pOutputSamples[i*2+1] = (int16_t)(((uint32_t)((int32_t)(mid - side) >> 1) << unusedBitsPerSample) >> 16);
|
||||
pOutputSamples[i*2+0] = rflac__s16_round((uint32_t)(((uint32_t)((int32_t)(mid + side) >> 1) << unusedBitsPerSample)));
|
||||
pOutputSamples[i*2+1] = rflac__s16_round((uint32_t)(((uint32_t)((int32_t)(mid - side) >> 1) << unusedBitsPerSample)));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4892,8 +4852,8 @@ static INLINE void rflac_read_pcm_frames_s16__decode_mid_side__sse2(
|
||||
left = _mm_srai_epi32(_mm_add_epi32(mid, side), 1);
|
||||
right = _mm_srai_epi32(_mm_sub_epi32(mid, side), 1);
|
||||
|
||||
left = _mm_srai_epi32(left, 16);
|
||||
right = _mm_srai_epi32(right, 16);
|
||||
left = rflac__mm_round_s16_epi32(left);
|
||||
right = rflac__mm_round_s16_epi32(right);
|
||||
|
||||
_mm_storeu_si128((__m128i*)(pOutputSamples + i*8), rflac__mm_packs_interleaved_epi32(left, right));
|
||||
}
|
||||
@@ -4904,8 +4864,8 @@ static INLINE void rflac_read_pcm_frames_s16__decode_mid_side__sse2(
|
||||
|
||||
mid = (mid << 1) | (side & 0x01);
|
||||
|
||||
pOutputSamples[i*2+0] = (int16_t)(((int32_t)(mid + side) >> 1) >> 16);
|
||||
pOutputSamples[i*2+1] = (int16_t)(((int32_t)(mid - side) >> 1) >> 16);
|
||||
pOutputSamples[i*2+0] = rflac__s16_round((uint32_t)(((int32_t)(mid + side) >> 1)));
|
||||
pOutputSamples[i*2+1] = rflac__s16_round((uint32_t)(((int32_t)(mid - side) >> 1)));
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -4923,8 +4883,8 @@ static INLINE void rflac_read_pcm_frames_s16__decode_mid_side__sse2(
|
||||
left = _mm_slli_epi32(_mm_add_epi32(mid, side), shift);
|
||||
right = _mm_slli_epi32(_mm_sub_epi32(mid, side), shift);
|
||||
|
||||
left = _mm_srai_epi32(left, 16);
|
||||
right = _mm_srai_epi32(right, 16);
|
||||
left = rflac__mm_round_s16_epi32(left);
|
||||
right = rflac__mm_round_s16_epi32(right);
|
||||
|
||||
_mm_storeu_si128((__m128i*)(pOutputSamples + i*8), rflac__mm_packs_interleaved_epi32(left, right));
|
||||
}
|
||||
@@ -4936,8 +4896,8 @@ static INLINE void rflac_read_pcm_frames_s16__decode_mid_side__sse2(
|
||||
|
||||
mid = (mid << 1) | (side & 0x01);
|
||||
|
||||
pOutputSamples[i*2+0] = (int16_t)(((mid + side) << shift) >> 16);
|
||||
pOutputSamples[i*2+1] = (int16_t)(((mid - side) << shift) >> 16);
|
||||
pOutputSamples[i*2+0] = rflac__s16_round((uint32_t)(((mid + side) << shift)));
|
||||
pOutputSamples[i*2+1] = rflac__s16_round((uint32_t)(((mid - side) << shift)));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4972,14 +4932,14 @@ static INLINE void rflac_read_pcm_frames_s16__decode_mid_side__neon(
|
||||
left = vshrq_n_s32(vreinterpretq_s32_u32(vaddq_u32(mid, side)), 1);
|
||||
right = vshrq_n_s32(vreinterpretq_s32_u32(vsubq_u32(mid, side)), 1);
|
||||
|
||||
left = vshrq_n_s32(left, 16);
|
||||
right = vshrq_n_s32(right, 16);
|
||||
left = vrshrq_n_s32(left, 16);
|
||||
right = vrshrq_n_s32(right, 16);
|
||||
|
||||
{
|
||||
/* interleaving store; replaces vzip + contiguous store */
|
||||
int16x4x2_t v;
|
||||
v.val[0] = vmovn_s32(left);
|
||||
v.val[1] = vmovn_s32(right);
|
||||
v.val[0] = vqmovn_s32(left);
|
||||
v.val[1] = vqmovn_s32(right);
|
||||
vst2_s16(pOutputSamples + i*8, v);
|
||||
}
|
||||
}
|
||||
@@ -4991,8 +4951,8 @@ static INLINE void rflac_read_pcm_frames_s16__decode_mid_side__neon(
|
||||
|
||||
mid = (mid << 1) | (side & 0x01);
|
||||
|
||||
pOutputSamples[i*2+0] = (int16_t)(((int32_t)(mid + side) >> 1) >> 16);
|
||||
pOutputSamples[i*2+1] = (int16_t)(((int32_t)(mid - side) >> 1) >> 16);
|
||||
pOutputSamples[i*2+0] = rflac__s16_round((uint32_t)(((int32_t)(mid + side) >> 1)));
|
||||
pOutputSamples[i*2+1] = rflac__s16_round((uint32_t)(((int32_t)(mid - side) >> 1)));
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -5015,14 +4975,14 @@ static INLINE void rflac_read_pcm_frames_s16__decode_mid_side__neon(
|
||||
left = vreinterpretq_s32_u32(vshlq_u32(vaddq_u32(mid, side), shift4));
|
||||
right = vreinterpretq_s32_u32(vshlq_u32(vsubq_u32(mid, side), shift4));
|
||||
|
||||
left = vshrq_n_s32(left, 16);
|
||||
right = vshrq_n_s32(right, 16);
|
||||
left = vrshrq_n_s32(left, 16);
|
||||
right = vrshrq_n_s32(right, 16);
|
||||
|
||||
{
|
||||
/* interleaving store; replaces vzip + contiguous store */
|
||||
int16x4x2_t v;
|
||||
v.val[0] = vmovn_s32(left);
|
||||
v.val[1] = vmovn_s32(right);
|
||||
v.val[0] = vqmovn_s32(left);
|
||||
v.val[1] = vqmovn_s32(right);
|
||||
vst2_s16(pOutputSamples + i*8, v);
|
||||
}
|
||||
}
|
||||
@@ -5034,8 +4994,8 @@ static INLINE void rflac_read_pcm_frames_s16__decode_mid_side__neon(
|
||||
|
||||
mid = (mid << 1) | (side & 0x01);
|
||||
|
||||
pOutputSamples[i*2+0] = (int16_t)(((mid + side) << shift) >> 16);
|
||||
pOutputSamples[i*2+1] = (int16_t)(((mid - side) << shift) >> 16);
|
||||
pOutputSamples[i*2+0] = rflac__s16_round((uint32_t)(((mid + side) << shift)));
|
||||
pOutputSamples[i*2+1] = rflac__s16_round((uint32_t)(((mid - side) << shift)));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5084,29 +5044,19 @@ static INLINE void rflac_read_pcm_frames_s16__decode_independent_stereo__scalar(
|
||||
uint32_t tempR2 = pInputSamples1U32[i*4+2] << shift1;
|
||||
uint32_t tempR3 = pInputSamples1U32[i*4+3] << shift1;
|
||||
|
||||
tempL0 >>= 16;
|
||||
tempL1 >>= 16;
|
||||
tempL2 >>= 16;
|
||||
tempL3 >>= 16;
|
||||
|
||||
tempR0 >>= 16;
|
||||
tempR1 >>= 16;
|
||||
tempR2 >>= 16;
|
||||
tempR3 >>= 16;
|
||||
|
||||
pOutputSamples[i*8+0] = (int16_t)tempL0;
|
||||
pOutputSamples[i*8+1] = (int16_t)tempR0;
|
||||
pOutputSamples[i*8+2] = (int16_t)tempL1;
|
||||
pOutputSamples[i*8+3] = (int16_t)tempR1;
|
||||
pOutputSamples[i*8+4] = (int16_t)tempL2;
|
||||
pOutputSamples[i*8+5] = (int16_t)tempR2;
|
||||
pOutputSamples[i*8+6] = (int16_t)tempL3;
|
||||
pOutputSamples[i*8+7] = (int16_t)tempR3;
|
||||
pOutputSamples[i*8+0] = rflac__s16_round(tempL0);
|
||||
pOutputSamples[i*8+1] = rflac__s16_round(tempR0);
|
||||
pOutputSamples[i*8+2] = rflac__s16_round(tempL1);
|
||||
pOutputSamples[i*8+3] = rflac__s16_round(tempR1);
|
||||
pOutputSamples[i*8+4] = rflac__s16_round(tempL2);
|
||||
pOutputSamples[i*8+5] = rflac__s16_round(tempR2);
|
||||
pOutputSamples[i*8+6] = rflac__s16_round(tempL3);
|
||||
pOutputSamples[i*8+7] = rflac__s16_round(tempR3);
|
||||
}
|
||||
|
||||
for (i = (frameCount4 << 2); i < frameCount; ++i) {
|
||||
pOutputSamples[i*2+0] = (int16_t)((pInputSamples0U32[i] << shift0) >> 16);
|
||||
pOutputSamples[i*2+1] = (int16_t)((pInputSamples1U32[i] << shift1) >> 16);
|
||||
pOutputSamples[i*2+0] = rflac__s16_round((uint32_t)((pInputSamples0U32[i] << shift0)));
|
||||
pOutputSamples[i*2+1] = rflac__s16_round((uint32_t)((pInputSamples1U32[i] << shift1)));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5127,8 +5077,8 @@ static INLINE void rflac_read_pcm_frames_s16__decode_independent_stereo__sse2(
|
||||
__m128i left = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), shift0);
|
||||
__m128i right = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), shift1);
|
||||
|
||||
left = _mm_srai_epi32(left, 16);
|
||||
right = _mm_srai_epi32(right, 16);
|
||||
left = rflac__mm_round_s16_epi32(left);
|
||||
right = rflac__mm_round_s16_epi32(right);
|
||||
|
||||
/* At this point we have results. We can now pack and interleave these
|
||||
* into a single __m128i object and then store the in the output buffer. */
|
||||
@@ -5136,8 +5086,8 @@ static INLINE void rflac_read_pcm_frames_s16__decode_independent_stereo__sse2(
|
||||
}
|
||||
|
||||
for (i = (frameCount4 << 2); i < frameCount; ++i) {
|
||||
pOutputSamples[i*2+0] = (int16_t)((pInputSamples0U32[i] << shift0) >> 16);
|
||||
pOutputSamples[i*2+1] = (int16_t)((pInputSamples1U32[i] << shift1) >> 16);
|
||||
pOutputSamples[i*2+0] = rflac__s16_round((uint32_t)((pInputSamples0U32[i] << shift0)));
|
||||
pOutputSamples[i*2+1] = rflac__s16_round((uint32_t)((pInputSamples1U32[i] << shift1)));
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -5164,22 +5114,22 @@ static INLINE void rflac_read_pcm_frames_s16__decode_independent_stereo__neon(
|
||||
left = vreinterpretq_s32_u32(vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), shift0_4));
|
||||
right = vreinterpretq_s32_u32(vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), shift1_4));
|
||||
|
||||
left = vshrq_n_s32(left, 16);
|
||||
right = vshrq_n_s32(right, 16);
|
||||
left = vrshrq_n_s32(left, 16);
|
||||
right = vrshrq_n_s32(right, 16);
|
||||
|
||||
{
|
||||
/* interleaving store; replaces vzip + contiguous store */
|
||||
int16x4x2_t v;
|
||||
v.val[0] = vmovn_s32(left);
|
||||
v.val[1] = vmovn_s32(right);
|
||||
v.val[0] = vqmovn_s32(left);
|
||||
v.val[1] = vqmovn_s32(right);
|
||||
vst2_s16(pOutputSamples + i*8, v);
|
||||
}
|
||||
}
|
||||
|
||||
for (i = (frameCount4 << 2); i < frameCount; ++i)
|
||||
{
|
||||
pOutputSamples[i*2+0] = (int16_t)((pInputSamples0U32[i] << shift0) >> 16);
|
||||
pOutputSamples[i*2+1] = (int16_t)((pInputSamples1U32[i] << shift1) >> 16);
|
||||
pOutputSamples[i*2+0] = rflac__s16_round((uint32_t)((pInputSamples0U32[i] << shift0)));
|
||||
pOutputSamples[i*2+1] = rflac__s16_round((uint32_t)((pInputSamples1U32[i] << shift1)));
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -5219,12 +5169,12 @@ static void rflac_read_pcm_frames_s16__decode_left_side__wide(rflac* pFlac,
|
||||
|
||||
for (i = 0; i < frameCount; ++i)
|
||||
{
|
||||
int64_t left = (int64_t)pInLeft[i] << shiftL;
|
||||
int64_t side = pInSide[i] << shiftS;
|
||||
int64_t left = (int64_t)((uint64_t)(int64_t)pInLeft[i] << shiftL);
|
||||
int64_t side = (int64_t)((uint64_t)pInSide[i] << shiftS);
|
||||
int64_t right = left - side;
|
||||
|
||||
pOutputSamples[i*2+0] = (int16_t)(left >> 16);
|
||||
pOutputSamples[i*2+1] = (int16_t)(right >> 16);
|
||||
pOutputSamples[i*2+0] = rflac__s16_round((uint32_t)(left));
|
||||
pOutputSamples[i*2+1] = rflac__s16_round((uint32_t)(right));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5238,12 +5188,12 @@ static void rflac_read_pcm_frames_s16__decode_right_side__wide(rflac* pFlac,
|
||||
|
||||
for (i = 0; i < frameCount; ++i)
|
||||
{
|
||||
int64_t side = pInSide[i] << shiftS;
|
||||
int64_t right = (int64_t)pInRight[i] << shiftR;
|
||||
int64_t side = (int64_t)((uint64_t)pInSide[i] << shiftS);
|
||||
int64_t right = (int64_t)((uint64_t)(int64_t)pInRight[i] << shiftR);
|
||||
int64_t left = right + side;
|
||||
|
||||
pOutputSamples[i*2+0] = (int16_t)(left >> 16);
|
||||
pOutputSamples[i*2+1] = (int16_t)(right >> 16);
|
||||
pOutputSamples[i*2+0] = rflac__s16_round((uint32_t)(left));
|
||||
pOutputSamples[i*2+1] = rflac__s16_round((uint32_t)(right));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5257,17 +5207,17 @@ static void rflac_read_pcm_frames_s16__decode_mid_side__wide(rflac* pFlac,
|
||||
|
||||
for (i = 0; i < frameCount; ++i)
|
||||
{
|
||||
int64_t mid = (int64_t)pInMid[i] << shiftM;
|
||||
int64_t side = pInSide[i] << shiftS;
|
||||
int64_t mid = (int64_t)((uint64_t)(int64_t)pInMid[i] << shiftM);
|
||||
int64_t side = (int64_t)((uint64_t)pInSide[i] << shiftS);
|
||||
int64_t left;
|
||||
int64_t right;
|
||||
|
||||
mid = (mid << 1) | (side & 0x01);
|
||||
mid = (int64_t)((uint64_t)mid << 1) | (side & 0x01);
|
||||
left = (mid + side) >> 1;
|
||||
right = (mid - side) >> 1;
|
||||
|
||||
pOutputSamples[i*2+0] = (int16_t)(left >> 16);
|
||||
pOutputSamples[i*2+1] = (int16_t)(right >> 16);
|
||||
pOutputSamples[i*2+0] = rflac__s16_round((uint32_t)(left));
|
||||
pOutputSamples[i*2+1] = rflac__s16_round((uint32_t)(right));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5282,8 +5232,8 @@ static void rflac_read_pcm_frames_f32__decode_left_side__wide(rflac* pFlac,
|
||||
|
||||
for (i = 0; i < frameCount; ++i)
|
||||
{
|
||||
int64_t left = (int64_t)pInLeft[i] << shiftL;
|
||||
int64_t side = pInSide[i] << shiftS;
|
||||
int64_t left = (int64_t)((uint64_t)(int64_t)pInLeft[i] << shiftL);
|
||||
int64_t side = (int64_t)((uint64_t)pInSide[i] << shiftS);
|
||||
int64_t right = left - side;
|
||||
|
||||
pOutputSamples[i*2+0] = (int32_t)left * factor;
|
||||
@@ -5302,8 +5252,8 @@ static void rflac_read_pcm_frames_f32__decode_right_side__wide(rflac* pFlac,
|
||||
|
||||
for (i = 0; i < frameCount; ++i)
|
||||
{
|
||||
int64_t side = pInSide[i] << shiftS;
|
||||
int64_t right = (int64_t)pInRight[i] << shiftR;
|
||||
int64_t side = (int64_t)((uint64_t)pInSide[i] << shiftS);
|
||||
int64_t right = (int64_t)((uint64_t)(int64_t)pInRight[i] << shiftR);
|
||||
int64_t left = right + side;
|
||||
|
||||
pOutputSamples[i*2+0] = (int32_t)left * factor;
|
||||
@@ -5322,12 +5272,12 @@ static void rflac_read_pcm_frames_f32__decode_mid_side__wide(rflac* pFlac,
|
||||
|
||||
for (i = 0; i < frameCount; ++i)
|
||||
{
|
||||
int64_t mid = (int64_t)pInMid[i] << shiftM;
|
||||
int64_t side = pInSide[i] << shiftS;
|
||||
int64_t mid = (int64_t)((uint64_t)(int64_t)pInMid[i] << shiftM);
|
||||
int64_t side = (int64_t)((uint64_t)pInSide[i] << shiftS);
|
||||
int64_t left;
|
||||
int64_t right;
|
||||
|
||||
mid = (mid << 1) | (side & 0x01);
|
||||
mid = (int64_t)((uint64_t)mid << 1) | (side & 0x01);
|
||||
left = (mid + side) >> 1;
|
||||
right = (mid - side) >> 1;
|
||||
|
||||
@@ -5413,7 +5363,7 @@ static uint64_t rflac_read_pcm_frames_s16(rflac* pFlac, uint64_t framesToRead,
|
||||
unsigned int j;
|
||||
for (j = 0; j < channelCount; ++j) {
|
||||
int32_t sampleS32 = (int32_t)((uint32_t)(pFlac->currentFLACFrame.subframes[j].pSamplesS32[iFirstPCMFrame + i]) << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[j].wastedBitsPerSample));
|
||||
pBufferOut[(i*channelCount)+j] = (int16_t)(sampleS32 >> 16);
|
||||
pBufferOut[(i*channelCount)+j] = rflac__s16_round((uint32_t)(sampleS32));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6875,12 +6825,19 @@ int rflac_process(rflac_t *f, size_t *read, size_t *wrote)
|
||||
}
|
||||
|
||||
int rflac_seek(rflac_t *f, uint64_t frame, uint64_t *byte_offset)
|
||||
{
|
||||
uint64_t point_frame;
|
||||
return rflac_seek_point(f, frame, byte_offset, &point_frame);
|
||||
}
|
||||
|
||||
int rflac_seek_point(rflac_t *f, uint64_t frame, uint64_t *byte_offset,
|
||||
uint64_t *point_frame)
|
||||
{
|
||||
uint32_t i;
|
||||
uint32_t best = 0;
|
||||
int found = 0;
|
||||
|
||||
if (!f || !f->dec || !byte_offset)
|
||||
if (!f || !f->dec || !byte_offset || !point_frame)
|
||||
return RFLAC_PROCESS_ERROR;
|
||||
if (!f->dec->pSeekpoints || !f->dec->seekpointCount)
|
||||
return RFLAC_PROCESS_ERROR;
|
||||
@@ -6905,6 +6862,7 @@ int rflac_seek(rflac_t *f, uint64_t frame, uint64_t *byte_offset)
|
||||
|
||||
*byte_offset = f->dec->firstFLACFramePosInBytes
|
||||
+ f->dec->pSeekpoints[best].flacFrameOffset;
|
||||
*point_frame = f->dec->pSeekpoints[best].firstPCMFrame;
|
||||
|
||||
return RFLAC_PROCESS_NEXT;
|
||||
}
|
||||
|
||||
@@ -494,7 +494,7 @@ static void image_texture_clear(struct texture_image *img)
|
||||
img->fp16 = false;
|
||||
}
|
||||
|
||||
static bool image_texture_load_buffer_probe(data_transfer_t *keep,
|
||||
bool image_texture_load_buffer_probe(data_transfer_t *keep,
|
||||
bool *kept, struct texture_image *out_img,
|
||||
enum image_type_enum type, const void *ptr, size_t len,
|
||||
const image_texture_request_t *req,
|
||||
@@ -543,56 +543,6 @@ bool image_texture_load_buffer_request(struct texture_image *out_img,
|
||||
should_abort, abort_ud, NULL);
|
||||
}
|
||||
|
||||
bool image_texture_load_request(struct texture_image *out_img,
|
||||
const char *path, const image_texture_request_t *req,
|
||||
bool (*should_abort)(void *ud), void *ud)
|
||||
{
|
||||
return image_texture_load_request_ex(out_img, path, req,
|
||||
should_abort, ud, NULL);
|
||||
}
|
||||
|
||||
bool image_texture_load_request_ex(struct texture_image *out_img,
|
||||
const char *path, const image_texture_request_t *req,
|
||||
bool (*should_abort)(void *ud), void *ud, int *png_probe)
|
||||
{
|
||||
enum image_type_enum type = image_texture_get_type(path);
|
||||
|
||||
if (png_probe)
|
||||
*png_probe = -1;
|
||||
|
||||
if (type != IMAGE_TYPE_NONE)
|
||||
{
|
||||
/* The synchronous read rides the data_transfer prefix spine
|
||||
* like every other loader: filestream/VFS routing (overlays
|
||||
* and driver assets from archive members or content://
|
||||
* documents), 64-bit lengths, the hardware guard behind the
|
||||
* read, and honest short-read detection. A zero budget fills
|
||||
* to completion in one blocking call, which is this API's
|
||||
* contract. */
|
||||
struct data_transfer *dt = data_transfer_open_prefix(path, 0);
|
||||
if (dt)
|
||||
{
|
||||
size_t file_len = 0;
|
||||
const uint8_t *ptr = NULL;
|
||||
bool kept = false;
|
||||
bool loaded;
|
||||
|
||||
data_transfer_iterate(dt, 0);
|
||||
ptr = data_transfer_ptr(dt, &file_len);
|
||||
loaded = data_transfer_complete(dt) && ptr && file_len
|
||||
&& image_texture_load_buffer_probe(dt, &kept, out_img, type,
|
||||
ptr, file_len, req, should_abort, ud, png_probe);
|
||||
if (!kept)
|
||||
data_transfer_free(dt);
|
||||
if (loaded)
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (out_img)
|
||||
image_texture_clear(out_img);
|
||||
return false;
|
||||
}
|
||||
|
||||
/* The request the plain loads carry: the caller's image, whose
|
||||
* ->pix10 asks for 10-bit on the way in */
|
||||
static void image_texture_request_of(const struct texture_image *img,
|
||||
@@ -604,10 +554,6 @@ static void image_texture_request_of(const struct texture_image *img,
|
||||
req->want_compressed = true;
|
||||
}
|
||||
|
||||
bool image_texture_load(struct texture_image *out_img, const char *path)
|
||||
{
|
||||
return image_texture_load_ex(out_img, path, NULL, NULL);
|
||||
}
|
||||
|
||||
void image_texture_free(struct texture_image *img)
|
||||
{
|
||||
@@ -827,11 +773,3 @@ bool image_texture_load_buffer(struct texture_image *out_img,
|
||||
buffer_len, &req, NULL, NULL);
|
||||
}
|
||||
|
||||
bool image_texture_load_ex(struct texture_image *out_img,
|
||||
const char *path, bool (*should_abort)(void *ud), void *ud)
|
||||
{
|
||||
image_texture_request_t req;
|
||||
image_texture_request_of(out_img, &req);
|
||||
return image_texture_load_request(out_img, path, &req,
|
||||
should_abort, ud);
|
||||
}
|
||||
@@ -25,6 +25,7 @@
|
||||
#include <boolean.h>
|
||||
#include <formats/image.h>
|
||||
#include <formats/data_transfer.h>
|
||||
#include <streams/image_texture_file.h>
|
||||
#ifdef HAVE_GCD
|
||||
#include <dispatch/dispatch.h>
|
||||
#elif defined(HAVE_THREADS)
|
||||
|
||||
@@ -88,10 +88,49 @@ struct rtga
|
||||
* process() and nothing below changes behaviour. */
|
||||
uint8_t *true_end;
|
||||
size_t avail_req; /* frontier asked for, clamped at IDLE */
|
||||
/* rtga_decode_rows: where the rows go, the row being written (NULL
|
||||
* until the row's first pixel), and rtga's own row, kept for the
|
||||
* next image */
|
||||
const struct rtga_rows *rows;
|
||||
uint32_t *dst;
|
||||
uint32_t *own_row;
|
||||
unsigned own_row_width;
|
||||
bool avail_set;
|
||||
bool need_more; /* last slice stopped at the wall */
|
||||
};
|
||||
|
||||
/* Row y of the image: the surface's, or with rtga_decode_rows the
|
||||
* caller's, or rtga's own when the caller names none. */
|
||||
static uint32_t *rtga_row(rtga_t *tga, int y)
|
||||
{
|
||||
uint8_t *p;
|
||||
if (!tga->rows)
|
||||
return tga->output_image + (size_t)y * (size_t)tga->width;
|
||||
p = tga->rows->row ? tga->rows->row(tga->rows->user, (unsigned)y) : NULL;
|
||||
return p ? (uint32_t*)p : tga->own_row;
|
||||
}
|
||||
|
||||
/* Row y is written. A row handed out by rtga_decode_rows holds bytes
|
||||
* in order, which the pixel words are on a little-endian host. */
|
||||
static void rtga_row_done(rtga_t *tga, int y, uint32_t *dst)
|
||||
{
|
||||
if (!tga->rows)
|
||||
return;
|
||||
#ifdef MSB_FIRST
|
||||
{
|
||||
int col;
|
||||
for (col = 0; col < tga->width; col++)
|
||||
{
|
||||
uint32_t v = dst[col];
|
||||
dst[col] = (v >> 24) | ((v >> 8) & 0xFF00u)
|
||||
| ((v << 8) & 0xFF0000u) | (v << 24);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
if (tga->rows->done)
|
||||
tga->rows->done(tga->rows->user, (unsigned)y, (const uint8_t*)dst);
|
||||
}
|
||||
|
||||
static INLINE uint8_t rtga_get8(rtga_context *s)
|
||||
{
|
||||
if (s->img_buffer < s->img_buffer_end)
|
||||
@@ -209,7 +248,7 @@ static void rtga_indexed_pixels(rtga_t *tga, int npix)
|
||||
{
|
||||
rtga_context ctx;
|
||||
rtga_context *s = &ctx;
|
||||
uint32_t *output = tga->output_image;
|
||||
uint32_t *dst = tga->dst;
|
||||
const uint32_t *p32 = tga->pal32;
|
||||
int width = tga->width;
|
||||
int height = tga->height;
|
||||
@@ -237,15 +276,19 @@ static void rtga_indexed_pixels(rtga_t *tga, int npix)
|
||||
&& s->img_buffer >= s->img_buffer_end)
|
||||
break;
|
||||
dst_row = inverted ? (height - 1 - cur_row) : cur_row;
|
||||
output[(size_t)dst_row * (size_t)width + (size_t)cur_col] =
|
||||
p32[rtga_get8(s)];
|
||||
if (!dst)
|
||||
dst = rtga_row(tga, dst_row);
|
||||
dst[cur_col] = p32[rtga_get8(s)];
|
||||
if (++cur_col >= width)
|
||||
{
|
||||
rtga_row_done(tga, dst_row, dst);
|
||||
dst = NULL;
|
||||
cur_col = 0;
|
||||
++cur_row;
|
||||
}
|
||||
}
|
||||
|
||||
tga->dst = dst;
|
||||
tga->pixel_i = i;
|
||||
tga->cur_col = cur_col;
|
||||
tga->cur_row = cur_row;
|
||||
@@ -343,11 +386,33 @@ static bool rtga_begin(rtga_t *tga, bool supports_rgba)
|
||||
if (tga_width > 0x4000 || tga_height > 0x4000)
|
||||
return false;
|
||||
#endif
|
||||
output = (uint32_t*)malloc(
|
||||
(size_t)tga_width * (size_t)tga_height * sizeof(uint32_t));
|
||||
if (!output)
|
||||
return false;
|
||||
tga->output_image = output;
|
||||
tga->width = tga_width;
|
||||
tga->height = tga_height;
|
||||
if (tga->rows)
|
||||
{
|
||||
if ( tga->rows->size
|
||||
&& !tga->rows->size(tga->rows->user,
|
||||
(unsigned)tga_width, (unsigned)tga_height))
|
||||
return false;
|
||||
if ((unsigned)tga_width > tga->own_row_width)
|
||||
{
|
||||
uint32_t *row = (uint32_t*)realloc(tga->own_row,
|
||||
(size_t)tga_width * sizeof(uint32_t));
|
||||
if (!row)
|
||||
return false;
|
||||
tga->own_row = row;
|
||||
tga->own_row_width = (unsigned)tga_width;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
output = (uint32_t*)malloc(
|
||||
(size_t)tga_width * (size_t)tga_height * sizeof(uint32_t));
|
||||
if (!output)
|
||||
return false;
|
||||
tga->output_image = output;
|
||||
}
|
||||
tga->dst = NULL;
|
||||
|
||||
/* skip to the data's starting position (offset usually = 0) */
|
||||
rtga_skip(s, tga_offset);
|
||||
@@ -439,7 +504,6 @@ static void rtga_fast_rows(rtga_t *tga, int nrows)
|
||||
* decoder. */
|
||||
rtga_context ctx;
|
||||
rtga_context *s = &ctx;
|
||||
uint32_t *output = tga->output_image;
|
||||
int tga_width = tga->width;
|
||||
int tga_height = tga->height;
|
||||
int tga_comp = tga->comp;
|
||||
@@ -458,15 +522,13 @@ static void rtga_fast_rows(rtga_t *tga, int nrows)
|
||||
for (; row < last; ++row)
|
||||
{
|
||||
int dst_row = tga_inverted ? (tga_height - 1 - row) : row;
|
||||
/* size_t index so dst_row * tga_width does not overflow
|
||||
* signed int for a legitimate 65535 x 65535 image - the
|
||||
* same widening the indexed path below already does. */
|
||||
uint32_t *dst = output + (size_t)dst_row * (size_t)tga_width;
|
||||
uint32_t *dst;
|
||||
int bytes_needed = tga_width * tga_comp;
|
||||
int col;
|
||||
|
||||
if (s->img_buffer + bytes_needed > s->img_buffer_end)
|
||||
break;
|
||||
dst = rtga_row(tga, dst_row);
|
||||
|
||||
if (tga_comp == 4)
|
||||
{
|
||||
@@ -535,6 +597,7 @@ static void rtga_fast_rows(rtga_t *tga, int nrows)
|
||||
}
|
||||
|
||||
s->img_buffer += bytes_needed;
|
||||
rtga_row_done(tga, dst_row, dst);
|
||||
}
|
||||
|
||||
/* The body's only early exit is the truncation check, so stopping
|
||||
@@ -563,8 +626,22 @@ static void rtga_fast_rows(rtga_t *tga, int nrows)
|
||||
return;
|
||||
}
|
||||
|
||||
if (row < last)
|
||||
if (row < last && tga->rows)
|
||||
{
|
||||
uint32_t fill = (tga_comp == 4) ? 0u : 0xFF000000u;
|
||||
for (; row < tga_height; ++row)
|
||||
{
|
||||
int dst_row = tga_inverted ? (tga_height - 1 - row) : row;
|
||||
uint32_t *dst = rtga_row(tga, dst_row);
|
||||
int col;
|
||||
for (col = 0; col < tga_width; col++)
|
||||
dst[col] = fill;
|
||||
rtga_row_done(tga, dst_row, dst);
|
||||
}
|
||||
}
|
||||
else if (row < last)
|
||||
{
|
||||
uint32_t *output = tga->output_image;
|
||||
size_t n = (size_t)(tga_height - row) * (size_t)tga_width;
|
||||
uint32_t fill = (tga_comp == 4) ? 0u : 0xFF000000u;
|
||||
uint32_t *p = tga_inverted
|
||||
@@ -592,7 +669,7 @@ static void rtga_generic_pixels(rtga_t *tga, int npix)
|
||||
* decoder. */
|
||||
rtga_context ctx;
|
||||
rtga_context *s = &ctx;
|
||||
uint32_t *output = tga->output_image;
|
||||
uint32_t *dst = tga->dst;
|
||||
int tga_width = tga->width;
|
||||
int tga_height = tga->height;
|
||||
int tga_comp = tga->comp;
|
||||
@@ -711,14 +788,16 @@ static void rtga_generic_pixels(rtga_t *tga, int npix)
|
||||
| ((uint32_t)g << 8) | (uint32_t)b;
|
||||
|
||||
/* Write to correct position using tracked row/col
|
||||
* (avoids per-pixel division and modulo). Use size_t for
|
||||
* the index so dst_row * tga_width does not overflow
|
||||
* signed int for a legitimate 65535 x 65535 image. */
|
||||
* (avoids per-pixel division and modulo). */
|
||||
dst_row = tga_inverted ? (tga_height - 1 - cur_row) : cur_row;
|
||||
output[(size_t)dst_row * (size_t)tga_width + (size_t)cur_col] = pixel;
|
||||
if (!dst)
|
||||
dst = rtga_row(tga, dst_row);
|
||||
dst[cur_col] = pixel;
|
||||
|
||||
if (++cur_col >= tga_width)
|
||||
{
|
||||
rtga_row_done(tga, dst_row, dst);
|
||||
dst = NULL;
|
||||
cur_col = 0;
|
||||
++cur_row;
|
||||
}
|
||||
@@ -726,6 +805,7 @@ static void rtga_generic_pixels(rtga_t *tga, int npix)
|
||||
--RLE_count;
|
||||
}
|
||||
|
||||
tga->dst = dst;
|
||||
tga->pixel_i = i;
|
||||
tga->cur_col = cur_col;
|
||||
tga->cur_row = cur_row;
|
||||
@@ -737,17 +817,14 @@ static void rtga_generic_pixels(rtga_t *tga, int npix)
|
||||
(void)tga_height;
|
||||
}
|
||||
|
||||
/* Abandon an in-flight decode. The END path clears output_image
|
||||
* first, transferring ownership, so this only frees what nobody
|
||||
* received. */
|
||||
/* Abandon an in-flight decode, or one rtga_begin() refused after
|
||||
* making its surface. The END path clears output_image first,
|
||||
* transferring ownership, so this only frees what nobody received. */
|
||||
static void rtga_proc_reset(rtga_t *rtga)
|
||||
{
|
||||
if (rtga->phase != RTGA_PHASE_IDLE)
|
||||
{
|
||||
free(rtga->output_image);
|
||||
rtga->output_image = NULL;
|
||||
}
|
||||
rtga->phase = RTGA_PHASE_IDLE;
|
||||
free(rtga->output_image);
|
||||
rtga->output_image = NULL;
|
||||
rtga->phase = RTGA_PHASE_IDLE;
|
||||
}
|
||||
|
||||
/* Smallest prefix that rtga_begin can parse: the 18-byte header plus
|
||||
@@ -937,6 +1014,53 @@ int rtga_process_image(rtga_t *rtga, void **buf_data,
|
||||
return IMAGE_PROCESS_END;
|
||||
}
|
||||
|
||||
bool rtga_decode_rows(rtga_t *rtga, const uint8_t *data, size_t len,
|
||||
bool rgba, const struct rtga_rows *rows,
|
||||
unsigned *width, unsigned *height)
|
||||
{
|
||||
bool ok;
|
||||
if (width)
|
||||
*width = 0;
|
||||
if (height)
|
||||
*height = 0;
|
||||
if (!rtga || !data || !rows || len < RTGA_MIN_HEADER
|
||||
|| len > (size_t)INT_MAX)
|
||||
return false;
|
||||
|
||||
rtga_proc_reset(rtga);
|
||||
rtga->buff_data = (uint8_t*)data;
|
||||
rtga->s.img_buffer = (uint8_t*)data;
|
||||
rtga->s.img_buffer_original = (uint8_t*)data;
|
||||
rtga->s.img_buffer_end = (uint8_t*)data + len;
|
||||
rtga->true_end = rtga->s.img_buffer_end;
|
||||
rtga->avail_set = false;
|
||||
rtga->avail_req = 0;
|
||||
rtga->need_more = false;
|
||||
rtga->rows = rows;
|
||||
rtga->width = 0;
|
||||
rtga->height = 0;
|
||||
|
||||
ok = rtga_begin(rtga, rgba);
|
||||
if (width)
|
||||
*width = (unsigned)rtga->width;
|
||||
if (height)
|
||||
*height = (unsigned)rtga->height;
|
||||
if (ok)
|
||||
{
|
||||
if (rtga->phase == RTGA_PHASE_FAST)
|
||||
rtga_fast_rows(rtga, rtga->height);
|
||||
else if (rtga->phase == RTGA_PHASE_INDEXED)
|
||||
rtga_indexed_pixels(rtga, rtga->pixel_count);
|
||||
else
|
||||
rtga_generic_pixels(rtga, rtga->pixel_count);
|
||||
}
|
||||
rtga->rows = NULL;
|
||||
rtga->dst = NULL;
|
||||
rtga->phase = RTGA_PHASE_IDLE;
|
||||
rtga->buff_data = NULL;
|
||||
return ok;
|
||||
}
|
||||
|
||||
bool rtga_set_buf_ptr(rtga_t *rtga, void *data)
|
||||
{
|
||||
if (!rtga)
|
||||
@@ -957,6 +1081,7 @@ void rtga_free(rtga_t *rtga)
|
||||
/* A finished decode handed its surface to the caller; an abandoned
|
||||
* one still holds a partial surface. */
|
||||
rtga_proc_reset(rtga);
|
||||
free(rtga->own_row);
|
||||
free(rtga);
|
||||
}
|
||||
|
||||
|
||||
@@ -4814,7 +4814,11 @@ static int start_decoder_setup(vorb *f)
|
||||
if (sparse)
|
||||
{
|
||||
if (c->sorted_entries == 0)
|
||||
{
|
||||
setup_temp_free(f, mults,
|
||||
sizeof(mults[0]) * c->lookup_values);
|
||||
goto skip;
|
||||
}
|
||||
c->multiplicands = (rvorbis_codetype *) setup_malloc(f,
|
||||
sizeof(c->multiplicands[0]) * len * c->dimensions);
|
||||
} else
|
||||
|
||||
@@ -853,7 +853,15 @@ static bool rslc_pp_expand_token(struct rslc_pp *pp, const struct rslc_tok *t,
|
||||
if (!m)
|
||||
return true;
|
||||
if (sym->flags & RSLC_SYM_BUSY)
|
||||
{
|
||||
/* Its own name inside its own expansion: painted, so it is never
|
||||
* a macro again however often those tokens are scanned from here
|
||||
* on - as an argument prescanned and then rescanned in the body
|
||||
* it was substituted into is. Both callers hold the token they
|
||||
* go on to emit. */
|
||||
((struct rslc_tok*)t)->flags |= RSLC_TF_NOEXPAND;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (m->function_like)
|
||||
{
|
||||
@@ -1327,6 +1335,13 @@ static bool rslc_pp_version(struct rslc_pp *pp, uint32_t line)
|
||||
}
|
||||
}
|
||||
if (ok && pp->c->version != 450 && pp->c->version != 460
|
||||
#ifdef RSLC_EXTENDED
|
||||
/* an emulator's own stages say 420 to 440 as well and mean
|
||||
* the same dialect: the layout qualifiers they need came
|
||||
* with 420 */
|
||||
&& !(!pp->c->es && (pp->c->version == 420 || pp->c->version == 430
|
||||
|| pp->c->version == 440))
|
||||
#endif
|
||||
&& !(pp->c->es && (pp->c->version == 310 || pp->c->version == 320)))
|
||||
{
|
||||
rslc_err(pp->c, "unsupported #version", NULL, line);
|
||||
@@ -1767,6 +1782,7 @@ struct rslc_quals
|
||||
bool has_storage;
|
||||
bool push_constant;
|
||||
bool std140;
|
||||
bool std430; /* said so: RSLC_EXTENDED honours it on a uniform block */
|
||||
bool is_uniform;
|
||||
bool is_const;
|
||||
bool is_in;
|
||||
@@ -2051,6 +2067,9 @@ static bool rslc_ty_converts(const struct rslc_type *from, const struct rslc_typ
|
||||
return false;
|
||||
if (from->kind != RSLC_TY_SCALAR && from->kind != RSLC_TY_VEC)
|
||||
return false;
|
||||
if (to->base == RSLC_BASE_DOUBLE)
|
||||
return to->kind == RSLC_TY_SCALAR && (from->base == RSLC_BASE_INT
|
||||
|| from->base == RSLC_BASE_UINT || from->base == RSLC_BASE_FLOAT);
|
||||
if (to->base == RSLC_BASE_FLOAT)
|
||||
return from->base == RSLC_BASE_INT || from->base == RSLC_BASE_UINT;
|
||||
if (to->base == RSLC_BASE_UINT)
|
||||
@@ -2146,6 +2165,17 @@ static uint32_t rslc_p_convert(struct rslc_parser *p, uint32_t e,
|
||||
return e;
|
||||
if (n->op == RSLC_OP_CONST && n->type->kind == RSLC_TY_SCALAR && to->kind == RSLC_TY_SCALAR)
|
||||
{
|
||||
/* A literal made a double keeps the value it was written with:
|
||||
* the node carries the 64 bits, as a float literal's does */
|
||||
if (to->base == RSLC_BASE_DOUBLE && n->type->base != RSLC_BASE_BOOL)
|
||||
{
|
||||
double d = n->type->base == RSLC_BASE_FLOAT ? rslc_p_const_double(n)
|
||||
: n->type->base == RSLC_BASE_INT ? (double)(int32_t)n->a : (double)n->a;
|
||||
uint64_t bits;
|
||||
memcpy(&bits, &d, sizeof(bits));
|
||||
return rslc_p_mk(p, RSLC_OP_CONST, to, rslc_float_bits((float)d),
|
||||
(uint32_t)bits, (uint32_t)(bits >> 32), 0, n->line, 0);
|
||||
}
|
||||
/* Fold a scalar literal. */
|
||||
if (to->base == RSLC_BASE_FLOAT)
|
||||
{
|
||||
@@ -2848,6 +2878,9 @@ static bool rslc_p_eval(struct rslc_parser *p, uint32_t e, struct rslc_cval *out
|
||||
/* a failed compile's nodes may name lists and nodes never made */
|
||||
if (!e || p->c->failed || n->nonconstant)
|
||||
return false;
|
||||
/* a double is calculated where it runs: no constant has 64 bits here */
|
||||
if (n->type && n->type->base == RSLC_BASE_DOUBLE)
|
||||
return false;
|
||||
switch (n->op)
|
||||
{
|
||||
case RSLC_OP_CONST:
|
||||
@@ -3051,11 +3084,12 @@ static bool rslc_p_eval(struct rslc_parser *p, uint32_t e, struct rslc_cval *out
|
||||
* 2 3 4 vec2 vec3 vec4; i2.. ivec, u2.. uvec, b2.. bvec
|
||||
* S sampler2D
|
||||
* A N Z sampler2DArray, sampler2DMSArray, samplerBuffer
|
||||
* J K image2D, image2DArray; g the gvec4 of the image's base,
|
||||
* matched exactly
|
||||
* D1 D3 DC DR sampler1D, sampler3D, samplerCube, samplerCubeArray
|
||||
* J K L image2D, image2DArray, image2DMS; g the gvec4 of the
|
||||
* image's base, matched exactly
|
||||
* D1 D3 DC DR DM sampler1D, sampler3D, samplerCube, samplerCubeArray,
|
||||
* sampler2DMS
|
||||
* H1 H2 HA HC HR the shadow sampler1D, 2D, 2DArray, Cube, CubeArray
|
||||
* P subpassInput
|
||||
* P W subpassInput, subpassInputMS
|
||||
* (RSLC_EXTENDED; their overloads are absent otherwise)
|
||||
* M any matrix; Q square matrix; T transposed M result
|
||||
* X any scalar or vector, same type as other X
|
||||
@@ -3132,7 +3166,7 @@ static const struct rslc_bsig rslc_bsigs[] = {
|
||||
{ RSLC_BFN_BITFIELDREVERSE, "bitfieldReverse", "I:I;U:U" },
|
||||
{ RSLC_BFN_BITCOUNT, "bitCount", "I:I;U:I" },
|
||||
{ RSLC_BFN_FINDLSB, "findLSB", "I:I;U:I" }, { RSLC_BFN_FINDMSB, "findMSB", "I:I;U:I" },
|
||||
{ RSLC_BFN_TEXTURESIZE, "textureSize", "S i:i2" RSLC_X(";A i:i3;N:i3;Z:i"
|
||||
{ RSLC_BFN_TEXTURESIZE, "textureSize", "S i:i2" RSLC_X(";A i:i3;N:i3;Z:i;DM:i2"
|
||||
";D1 i:i;D3 i:i3;DC i:i2;DR i:i3;H1 i:i;H2 i:i2;HA i:i3;HC i:i2;HR i:i3") },
|
||||
{ RSLC_BFN_TEXTUREQUERYLOD, "textureQueryLod", "S 2:2" RSLC_X(";A 2:2"
|
||||
";D1 f:2;D3 3:2;DC 3:2;DR 3:2;H1 f:2;H2 2:2;HA 2:2;HC 3:2;HR 3:2") },
|
||||
@@ -3147,7 +3181,7 @@ static const struct rslc_bsig rslc_bsigs[] = {
|
||||
{ RSLC_BFN_TEXTUREOFFSET, "textureOffset", "S 2 i2:4;S 2 i2 f:4" RSLC_X(";A 3 i2:4;A 3 i2 f:4"
|
||||
";D1 f i:4;D1 f i f:4;D3 3 i3:4;D3 3 i3 f:4"
|
||||
";H1 3 i:f;H1 3 i f:f;H2 3 i2:f;H2 3 i2 f:f;HA 4 i2:f") },
|
||||
{ RSLC_BFN_TEXELFETCH, "texelFetch", "S i2 i:4" RSLC_X(";A i3 i:4;N i3 i:4;Z i:4;D1 i i:4;D3 i3 i:4") },
|
||||
{ RSLC_BFN_TEXELFETCH, "texelFetch", "S i2 i:4" RSLC_X(";A i3 i:4;N i3 i:4;Z i:4;D1 i i:4;D3 i3 i:4;DM i2 i:4") },
|
||||
{ RSLC_BFN_TEXELFETCHOFFSET, "texelFetchOffset", "S i2 i i2:4" },
|
||||
{ RSLC_BFN_TEXTUREPROJOFFSET, "textureProjOffset", "S 3 i2:4;S 4 i2:4;S 3 i2 f:4;S 4 i2 f:4" },
|
||||
{ RSLC_BFN_TEXTURELODOFFSET, "textureLodOffset", "S 2 f i2:4" RSLC_X(";A 3 f i2:4"
|
||||
@@ -3185,10 +3219,11 @@ static const struct rslc_bsig rslc_bsigs[] = {
|
||||
{ RSLC_BFN_MEMORYBARRIERBUFFER, "memoryBarrierBuffer", ":v" },
|
||||
{ RSLC_BFN_MEMORYBARRIERIMAGE, "memoryBarrierImage", ":v" },
|
||||
{ RSLC_BFN_GROUPMEMORYBARRIER, "groupMemoryBarrier", ":v" },
|
||||
{ RSLC_BFN_IMAGESTORE, "imageStore", "J i2 g:v;K i3 g:v" },
|
||||
{ RSLC_BFN_IMAGELOAD, "imageLoad", "J i2:4;K i3:4" },
|
||||
{ RSLC_BFN_IMAGESIZE, "imageSize", "J:i2;K:i3" },
|
||||
{ RSLC_BFN_SUBPASSLOAD, "subpassLoad", "P:4" },
|
||||
{ RSLC_BFN_IMAGESTORE, "imageStore", "J i2 g:v;K i3 g:v;L i2 i g:v" },
|
||||
{ RSLC_BFN_IMAGELOAD, "imageLoad", "J i2:4;K i3:4;L i2 i:4" },
|
||||
{ RSLC_BFN_IMAGESIZE, "imageSize", "J:i2;K:i3;L:i2" },
|
||||
{ RSLC_BFN_SUBPASSLOAD, "subpassLoad", "P:4;W i:4" },
|
||||
{ RSLC_BFN_TEXTURESAMPLES, "textureSamples", "DM:i;N:i" },
|
||||
{ RSLC_BFN_SUBGROUPELECT, "subgroupElect", ":b" },
|
||||
{ RSLC_BFN_SUBGROUPMIN, "subgroupMin", "F:F;I:I;U:U" },
|
||||
{ RSLC_BFN_SUBGROUPMAX, "subgroupMax", "F:F;I:I;U:U" }
|
||||
@@ -3284,20 +3319,22 @@ static bool rslc_bsig_match_tok(struct rslc_parser *p, const char *tok, unsigned
|
||||
else if (len == 2 && (tok[0] == 'D' || tok[0] == 'H'))
|
||||
{
|
||||
unsigned dim = tok[1] == '1' ? RSLC_SDIM_1D
|
||||
: (tok[1] == '2' || tok[1] == 'A') ? RSLC_SDIM_2D
|
||||
: (tok[1] == '2' || tok[1] == 'A' || tok[1] == 'M') ? RSLC_SDIM_2D
|
||||
: tok[1] == '3' ? RSLC_SDIM_3D : RSLC_SDIM_CUBE;
|
||||
unsigned fl = ((tok[1] == 'A' || tok[1] == 'R') ? RSLC_SX_ARRAY : 0)
|
||||
| (tok[1] == 'M' ? RSLC_SX_MS : 0)
|
||||
| (tok[0] == 'H' ? RSLC_SX_SHADOW : 0);
|
||||
if (t->kind != RSLC_TY_SAMPLERX || t->rows != dim || t->cols != fl)
|
||||
return false;
|
||||
*sbase = t->base;
|
||||
return true;
|
||||
}
|
||||
else if (len == 1 && tok[0] == 'P')
|
||||
return t->kind == RSLC_TY_SUBPASSINPUT;
|
||||
else if (len == 1 && (tok[0] == 'J' || tok[0] == 'K'))
|
||||
else if (len == 1 && (tok[0] == 'P' || tok[0] == 'W'))
|
||||
return t->kind == RSLC_TY_SUBPASSINPUT && t->cols == (tok[0] == 'W' ? 2u : 1u);
|
||||
else if (len == 1 && (tok[0] == 'J' || tok[0] == 'K' || tok[0] == 'L'))
|
||||
{
|
||||
if (t->kind != (tok[0] == 'J' ? RSLC_TY_IMAGE2D : RSLC_TY_IMAGE2DARRAY))
|
||||
if (t->kind != (tok[0] == 'J' ? RSLC_TY_IMAGE2D
|
||||
: tok[0] == 'K' ? RSLC_TY_IMAGE2DARRAY : RSLC_TY_IMAGE2DMS))
|
||||
return false;
|
||||
*sbase = t->base;
|
||||
return true;
|
||||
@@ -3391,7 +3428,7 @@ static const struct rslc_type *rslc_bsig_param_type(struct rslc_parser *p,
|
||||
if (len == 1 && (tok[0] == 'S' || tok[0] == 'A' || tok[0] == 'N' || tok[0] == 'Z'))
|
||||
return actual;
|
||||
#ifdef RSLC_EXTENDED
|
||||
if (len == 1 && (tok[0] == 'J' || tok[0] == 'K' || tok[0] == 'P'))
|
||||
if (len == 1 && (tok[0] == 'J' || tok[0] == 'K' || tok[0] == 'L' || tok[0] == 'P' || tok[0] == 'W'))
|
||||
return actual;
|
||||
if (len == 2 && (tok[0] == 'D' || tok[0] == 'H'))
|
||||
return actual;
|
||||
@@ -3612,11 +3649,12 @@ static bool rslc_p_is_lvalue(struct rslc_parser *p, uint32_t e)
|
||||
|| v->storage == RSLC_ST_MEMBER)
|
||||
return false;
|
||||
if (v->storage == RSLC_ST_PARAM && v->dir == RSLC_DIR_IN)
|
||||
return true; /* in params are writable locals */
|
||||
return !(v->quals & RSLC_VQ_PRECISE); /* in params are writable locals; const in ones are not */
|
||||
if (v->storage == RSLC_ST_BUILTIN)
|
||||
return v->builtin == RSLC_BI_POSITION || v->builtin == RSLC_BI_FRAGDEPTH
|
||||
|| v->builtin == RSLC_BI_POINTSIZE || v->builtin == RSLC_BI_CLIPDISTANCE
|
||||
|| v->builtin == RSLC_BI_CULLDISTANCE
|
||||
|| v->builtin == RSLC_BI_FRAGSTENCILREF
|
||||
|| (v->builtin == RSLC_BI_LAYER && v->dir == RSLC_DIR_OUT);
|
||||
return true;
|
||||
}
|
||||
@@ -3927,6 +3965,42 @@ static uint32_t rslc_p_call(struct rslc_parser *p, struct rslc_sym *name, uint32
|
||||
const struct rslc_type *ret = NULL;
|
||||
uint8_t prec = 0;
|
||||
uint32_t i;
|
||||
#ifdef RSLC_EXTENDED
|
||||
/* The rounding, range and selection functions on doubles: every
|
||||
* number among the arguments a double, and so the result */
|
||||
for (i = 0; i < nargs; i++)
|
||||
if (rslc_p_node(p, args[i])->type->base == RSLC_BASE_DOUBLE)
|
||||
break;
|
||||
if (i < nargs)
|
||||
{
|
||||
const struct rslc_type *dt = rslc_type_basic(p->c, RSLC_TY_SCALAR, RSLC_BASE_DOUBLE, 1, 1);
|
||||
unsigned want = (name->bfn == RSLC_BFN_MIX || name->bfn == RSLC_BFN_CLAMP) ? 3
|
||||
: (name->bfn == RSLC_BFN_MIN || name->bfn == RSLC_BFN_MAX) ? 2
|
||||
: (name->bfn == RSLC_BFN_ABS || name->bfn == RSLC_BFN_SIGN || name->bfn == RSLC_BFN_FLOOR
|
||||
|| name->bfn == RSLC_BFN_CEIL || name->bfn == RSLC_BFN_TRUNC || name->bfn == RSLC_BFN_ROUND
|
||||
|| name->bfn == RSLC_BFN_FRACT || name->bfn == RSLC_BFN_SQRT) ? 1 : 0;
|
||||
if (!want || nargs != want)
|
||||
{
|
||||
rslc_err(p->c, "not a function of doubles: ", name->str, line);
|
||||
return 0;
|
||||
}
|
||||
for (i = 0; i < nargs; i++)
|
||||
{
|
||||
const struct rslc_type *at = rslc_p_node(p, args[i])->type;
|
||||
if (name->bfn == RSLC_BFN_MIX && i == 2 && at->kind == RSLC_TY_SCALAR && at->base == RSLC_BASE_BOOL)
|
||||
continue;
|
||||
if (!rslc_ty_converts(at, dt))
|
||||
{
|
||||
rslc_err(p->c, "an argument that is not a number, in ", name->str, line);
|
||||
return 0;
|
||||
}
|
||||
args[i] = rslc_p_convert(p, args[i], dt);
|
||||
}
|
||||
r = rslc_p_mk(p, RSLC_OP_BUILTIN, dt, name->bfn, rslc_list_new(p->c, args, nargs), 0, 0, line, 0);
|
||||
rslc_scratch_release(p->c, &mark);
|
||||
return r;
|
||||
}
|
||||
#endif
|
||||
if (!rslc_p_resolve_builtin(p, bs, args, nargs, &ret, line))
|
||||
return 0;
|
||||
#ifdef RSLC_EXTENDED
|
||||
@@ -3975,7 +4049,8 @@ static uint32_t rslc_p_var_ref(struct rslc_parser *p, struct rslc_var *v, uint32
|
||||
v->precision = rslc_p_default_prec(p, v->type, 0); /* as glslang: the default applies */
|
||||
#ifdef RSLC_EXTENDED
|
||||
if (v->storage == RSLC_ST_BUILTIN
|
||||
&& (v->builtin == RSLC_BI_POINTCOORD || v->builtin == RSLC_BI_PRIMITIVEID))
|
||||
&& (v->builtin == RSLC_BI_POINTCOORD || v->builtin == RSLC_BI_PRIMITIVEID
|
||||
|| v->builtin == RSLC_BI_BARYCOORD || v->builtin == RSLC_BI_BARYCOORDNOPERSP))
|
||||
p->c->prog.spirv_only = v->name->str;
|
||||
#endif
|
||||
e = rslc_p_mk(p, RSLC_OP_VAR, v->type, v->id, 0, 0, 0, line, v->precision);
|
||||
@@ -4053,9 +4128,33 @@ static uint32_t rslc_p_primary(struct rslc_parser *p)
|
||||
return rslc_p_mk(p, RSLC_OP_MEMBER, f->type, e, sym->decl->member, 0, 0, line,
|
||||
rslc_prec_max(v->precision, f->precision));
|
||||
}
|
||||
#endif
|
||||
#ifdef RSLC_EXTENDED
|
||||
if ( sym->decl && sym->decl->kind == RSLC_DK_VAR
|
||||
&& ((struct rslc_var*)sym->decl->ptr)->storage == RSLC_ST_BUILTIN
|
||||
&& ((struct rslc_var*)sym->decl->ptr)->builtin == RSLC_BI_FRAGSTENCILREF
|
||||
&& !p->c->prog.spirv_only)
|
||||
p->c->prog.spirv_only = "gl_FragStencilRefARB";
|
||||
#endif
|
||||
if (sym->decl && sym->decl->kind == RSLC_DK_VAR)
|
||||
return rslc_p_var_ref(p, (struct rslc_var*)sym->decl->ptr, line);
|
||||
#ifdef RSLC_EXTENDED
|
||||
/* gl_WorkGroupSize: the constant the local size layout before it
|
||||
* made (1 where it named none) */
|
||||
if ( p->c->stage == RSLC_STAGE_COMPUTE && !sym->decl
|
||||
&& !strcmp(sym->str, "gl_WorkGroupSize"))
|
||||
{
|
||||
uint32_t args[3];
|
||||
unsigned k;
|
||||
for (k = 0; k < 3; k++)
|
||||
{
|
||||
uint32_t n = p->c->prog.cs_local[k];
|
||||
if (!(args[k] = rslc_p_const_uint(p, n ? n : 1, line)))
|
||||
return 0;
|
||||
}
|
||||
return rslc_p_construct(p, rslc_ty_vecn(p, RSLC_BASE_UINT, 3), args, 3, line);
|
||||
}
|
||||
#endif
|
||||
rslc_err(p->c, "undeclared identifier ", sym->str, line);
|
||||
return 0;
|
||||
}
|
||||
@@ -4185,6 +4284,17 @@ static uint32_t rslc_p_postfix_ops(struct rslc_parser *p, uint32_t e)
|
||||
{
|
||||
if (!rslc_p_expect(p, RSLC_P_LPAREN, "'('") || !rslc_p_expect(p, RSLC_P_RPAREN, "')'"))
|
||||
return 0;
|
||||
#ifdef RSLC_EXTENDED
|
||||
if (!bt->array_len)
|
||||
{
|
||||
/* an unsized array: how long it is is the buffer's to say */
|
||||
e = rslc_p_mk(p, RSLC_OP_UNARY, p->t_int, e, 0, 0, RSLC_UN_ARRAYLEN, line, 0);
|
||||
if (!e)
|
||||
return 0;
|
||||
p->c->prog.spirv_only = "length() of an unsized array";
|
||||
continue;
|
||||
}
|
||||
#endif
|
||||
e = rslc_p_const_int(p, (int32_t)bt->array_len, line);
|
||||
continue;
|
||||
}
|
||||
@@ -4370,7 +4480,9 @@ static const struct rslc_type *rslc_p_binary_type(struct rslc_parser *p, uint16_
|
||||
if (ta->base != tb->base)
|
||||
{
|
||||
const struct rslc_type *ca, *cb;
|
||||
enum rslc_base base = (ta->base == RSLC_BASE_FLOAT || tb->base == RSLC_BASE_FLOAT)
|
||||
enum rslc_base base = (ta->base == RSLC_BASE_DOUBLE || tb->base == RSLC_BASE_DOUBLE)
|
||||
? RSLC_BASE_DOUBLE
|
||||
: (ta->base == RSLC_BASE_FLOAT || tb->base == RSLC_BASE_FLOAT)
|
||||
? RSLC_BASE_FLOAT : RSLC_BASE_UINT;
|
||||
ca = rslc_type_basic(p->c, ta->kind, base, ta->rows, ta->cols);
|
||||
cb = rslc_type_basic(p->c, tb->kind, base, tb->rows, tb->cols);
|
||||
@@ -4621,6 +4733,11 @@ static bool rslc_p_image_format(const char *s, uint32_t len, uint8_t *fmt)
|
||||
{ "rgba32f", RSLC_IMGFMT_RGBA32F }, { "rgba16f", RSLC_IMGFMT_RGBA16F },
|
||||
{ "r32f", RSLC_IMGFMT_R32F }, { "rgba8", RSLC_IMGFMT_RGBA8 },
|
||||
{ "rg8", RSLC_IMGFMT_RG8 }, { "r8", RSLC_IMGFMT_R8 },
|
||||
{ "rgba8_snorm", RSLC_IMGFMT_RGBA8_SNORM }, { "rg32f", RSLC_IMGFMT_RG32F },
|
||||
{ "rg16f", RSLC_IMGFMT_RG16F }, { "r11f_g11f_b10f", RSLC_IMGFMT_R11G11B10F },
|
||||
{ "r16f", RSLC_IMGFMT_R16F }, { "rgba16", RSLC_IMGFMT_RGBA16 },
|
||||
{ "rgb10_a2", RSLC_IMGFMT_RGB10_A2 }, { "rg16", RSLC_IMGFMT_RG16 },
|
||||
{ "r16", RSLC_IMGFMT_R16 },
|
||||
{ "rgba32i", RSLC_IMGFMT_RGBA32I }, { "rgba16i", RSLC_IMGFMT_RGBA16I },
|
||||
{ "rgba8i", RSLC_IMGFMT_RGBA8I }, { "r32i", RSLC_IMGFMT_R32I },
|
||||
{ "rgba32ui", RSLC_IMGFMT_RGBA32UI }, { "rgba16ui", RSLC_IMGFMT_RGBA16UI },
|
||||
@@ -4658,7 +4775,7 @@ static bool rslc_p_layout(struct rslc_parser *p, struct rslc_quals *q)
|
||||
else if (len == 6 && !memcmp(s, "offset", 6)) slot = &q->offset;
|
||||
else if (len == 13 && !memcmp(s, "push_constant", 13)) q->push_constant = true;
|
||||
else if (len == 6 && !memcmp(s, "std140", 6)) q->std140 = true;
|
||||
else if (len == 6 && !memcmp(s, "std430", 6)) { }
|
||||
else if (len == 6 && !memcmp(s, "std430", 6)) q->std430 = true;
|
||||
else if (len == 12 && !memcmp(s, "column_major", 12)) { }
|
||||
else if (len == 9 && !memcmp(s, "row_major", 9))
|
||||
return rslc_p_fail(p, "row_major is not supported", NULL);
|
||||
@@ -4751,6 +4868,12 @@ static bool rslc_p_quals(struct rslc_parser *p, struct rslc_quals *q)
|
||||
case RSLC_KW_WRITEONLY: q->writeonly = true; break;
|
||||
case RSLC_KW_RESTRICT:
|
||||
case RSLC_KW_VOLATILE: break;
|
||||
/* GL_EXT_fragment_shader_barycentric: the input is each
|
||||
* vertex's value, an array of three */
|
||||
case RSLC_KW_PERVERTEX:
|
||||
q->interp = RSLC_INTERP_PERVERTEX;
|
||||
p->c->prog.spirv_only = "pervertexEXT";
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
return true;
|
||||
@@ -4819,13 +4942,41 @@ static const struct rslc_type *rslc_p_type_spec(struct rslc_parser *p,
|
||||
ty = rslc_type_sampler2d(p->c, RSLC_BASE_UINT);
|
||||
else if (k == RSLC_KW_SUBPASSINPUT)
|
||||
ty = rslc_type_basic(p->c, RSLC_TY_SUBPASSINPUT, RSLC_BASE_FLOAT, 1, 1);
|
||||
else if (k >= RSLC_KW_IMAGE2D && k <= RSLC_KW_UIMAGE2DARRAY)
|
||||
else if (k == RSLC_KW_DOUBLE)
|
||||
{
|
||||
/* a 64-bit scalar: converted to and from, calculated with,
|
||||
* compared; never a constant, a vector or a block member */
|
||||
ty = rslc_type_basic(p->c, RSLC_TY_SCALAR, RSLC_BASE_DOUBLE, 1, 1);
|
||||
#ifdef RSLC_EXTENDED
|
||||
p->c->prog.spirv_only = "double";
|
||||
#endif
|
||||
}
|
||||
else if (k == RSLC_KW_SUBPASSINPUTMS)
|
||||
{
|
||||
/* a multisampled attachment: cols 2 */
|
||||
ty = rslc_type_basic(p->c, RSLC_TY_SUBPASSINPUT, RSLC_BASE_FLOAT, 1, 2);
|
||||
#ifdef RSLC_EXTENDED
|
||||
p->c->prog.spirv_only = "a multisampled subpass input";
|
||||
#endif
|
||||
}
|
||||
else if (k >= RSLC_KW_IMAGE2D && k <= RSLC_KW_UIMAGE2DMS)
|
||||
{
|
||||
/* the format is the declaration's layout; it is applied there */
|
||||
static const uint8_t bases[3] = { RSLC_BASE_FLOAT, RSLC_BASE_INT, RSLC_BASE_UINT };
|
||||
unsigned i = k - RSLC_KW_IMAGE2D;
|
||||
ty = rslc_type_basic(p->c, i < 3 ? RSLC_TY_IMAGE2D : RSLC_TY_IMAGE2DARRAY,
|
||||
ty = rslc_type_basic(p->c, i < 3 ? RSLC_TY_IMAGE2D
|
||||
: i < 6 ? RSLC_TY_IMAGE2DARRAY : RSLC_TY_IMAGE2DMS,
|
||||
(enum rslc_base)bases[i % 3], 1, 0);
|
||||
#ifdef RSLC_EXTENDED
|
||||
if (i >= 6 && !p->c->prog.spirv_only)
|
||||
p->c->prog.spirv_only = "a multisampled image";
|
||||
#endif
|
||||
}
|
||||
else if (k >= RSLC_KW_SAMPLER2DMS && k <= RSLC_KW_USAMPLER2DMS)
|
||||
{
|
||||
static const uint8_t bases[3] = { RSLC_BASE_FLOAT, RSLC_BASE_INT, RSLC_BASE_UINT };
|
||||
ty = rslc_type_basic(p->c, RSLC_TY_SAMPLERX,
|
||||
(enum rslc_base)bases[k - RSLC_KW_SAMPLER2DMS], RSLC_SDIM_2D, RSLC_SX_MS);
|
||||
}
|
||||
else if (k >= RSLC_KW_SAMPLER2DARRAY && k <= RSLC_KW_USAMPLERBUFFER)
|
||||
{
|
||||
@@ -5013,7 +5164,7 @@ static bool rslc_p_starts_declaration(struct rslc_parser *p)
|
||||
s = rslc_tok_sym(p->c, t);
|
||||
if (s->kw >= RSLC_KW_CONST && s->kw <= RSLC_KW_STRUCT)
|
||||
return true;
|
||||
if (s->kw >= RSLC_KW_BUFFER && s->kw <= RSLC_KW_SHARED)
|
||||
if (s->kw >= RSLC_KW_BUFFER && s->kw <= RSLC_KW_PERVERTEX)
|
||||
return true; /* RSLC_EXTENDED: only seeded there */
|
||||
if (s->kw >= RSLC_KW_VOID && s->kw <= RSLC_KW_LAST_TYPE)
|
||||
{
|
||||
@@ -5613,18 +5764,30 @@ static bool rslc_p_block_make(struct rslc_parser *p, const struct rslc_quals *q,
|
||||
b->push = idx == 1;
|
||||
b->std140 = idx == 0 || q->std140;
|
||||
#ifdef RSLC_EXTENDED
|
||||
/* a further uniform block is std140 as the first is; a storage
|
||||
* buffer std430 unless it says otherwise, and only its last member
|
||||
* may be an array of unstated length */
|
||||
/* a further uniform block is std140 as the first is, unless it says
|
||||
* std430 (GL_EXT_scalar_block_layout lets a uniform block say so, and
|
||||
* its members are then placed as glslang places them); a storage
|
||||
* buffer std430 unless it says otherwise. Only a block's last member
|
||||
* may be an array of unstated length: a storage buffer's, or a
|
||||
* uniform block's (GL_EXT_uniform_buffer_unsized_array), which only
|
||||
* the SPIR-V destination takes. */
|
||||
b->buffer = q->is_buffer;
|
||||
b->readonly = q->readonly;
|
||||
b->coherent = q->coherent;
|
||||
if (idx >= 2 && !q->is_buffer)
|
||||
b->std140 = true;
|
||||
if (idx != 1 && !q->is_buffer && q->std430 && !q->std140)
|
||||
b->std140 = false;
|
||||
for (i = 0; i < nfields; i++)
|
||||
if (fields[i].type->kind == RSLC_TY_ARRAY && !fields[i].type->array_len
|
||||
&& (!q->is_buffer || i + 1 != nfields))
|
||||
return rslc_p_fail(p, "only a storage buffer's last member may be unsized", NULL);
|
||||
if (fields[i].type->kind == RSLC_TY_ARRAY && !fields[i].type->array_len)
|
||||
{
|
||||
if (i + 1 != nfields)
|
||||
return rslc_p_fail(p, "only a block's last member may be unsized", NULL);
|
||||
if (idx == 1)
|
||||
return rslc_p_fail(p, "a push constant block's member may not be unsized", NULL);
|
||||
if (!q->is_buffer && !p->c->prog.spirv_only)
|
||||
p->c->prog.spirv_only = "an unsized array in a uniform block";
|
||||
}
|
||||
#endif
|
||||
pr->has_block[idx] = true;
|
||||
|
||||
@@ -5706,6 +5869,18 @@ static bool rslc_p_block_decl(struct rslc_parser *p, struct rslc_quals *q,
|
||||
fields[nfields].name = rslc_tok_sym(p->c, nt);
|
||||
fields[nfields].line = nt->line;
|
||||
fields[nfields].precision = rslc_p_default_prec(p, mt, fq.precision);
|
||||
#ifdef RSLC_EXTENDED
|
||||
/* layout(offset = n) on a member: it is placed there and the
|
||||
* rest follow, as a fragment stage's half of a push block shared
|
||||
* with the vertex stage starts past the vertex stage's. SPIR-V
|
||||
* only: nothing printed says it. */
|
||||
if (fq.offset != RSLC_UNSET)
|
||||
{
|
||||
fields[nfields].offset_p1 = fq.offset + 1;
|
||||
if (!p->c->prog.spirv_only)
|
||||
p->c->prog.spirv_only = "a block member's layout(offset)";
|
||||
}
|
||||
#endif
|
||||
nfields++;
|
||||
if (rslc_p_accept(p, RSLC_P_COMMA))
|
||||
continue;
|
||||
@@ -6189,11 +6364,15 @@ static struct rslc_var *rslc_p_var_make(struct rslc_parser *p, const struct rslc
|
||||
rslc_p_fail(p, "an image must be a global uniform", NULL);
|
||||
return NULL;
|
||||
}
|
||||
if (!q->img_fmt)
|
||||
/* an image that is only written may leave its format out; the
|
||||
* device then has to write without one */
|
||||
if (!q->img_fmt && !q->writeonly)
|
||||
{
|
||||
rslc_p_fail(p, "an image needs its format in the layout", NULL);
|
||||
rslc_p_fail(p, "an image that is read needs its format in the layout", NULL);
|
||||
return NULL;
|
||||
}
|
||||
if (!q->img_fmt && !p->c->prog.spirv_only)
|
||||
p->c->prog.spirv_only = "an image without a format";
|
||||
/* rows carries the access: 1 read-write, 2 write only, 3 read only */
|
||||
v->type = rslc_type_basic(p->c, (enum rslc_tkind)vt->kind, (enum rslc_base)vt->base,
|
||||
q->writeonly ? RSLC_IMG_WRITEONLY : q->readonly ? RSLC_IMG_READONLY : RSLC_IMG_READWRITE,
|
||||
@@ -6328,6 +6507,31 @@ static uint32_t rslc_p_declaration(struct rslc_parser *p, bool global)
|
||||
return 0;
|
||||
|
||||
#ifdef RSLC_EXTENDED
|
||||
/* invariant gl_Position; - a variable already declared, a built-in
|
||||
* or an output, made invariant after the fact */
|
||||
if (global && (q.vq & RSLC_VQ_INVARIANT) && P_TOK(p)->kind == RSLC_TK_IDENT
|
||||
&& rslc_tok_sym(p->c, P_TOK(p))->kw == RSLC_KW_NONE
|
||||
&& rslc_tok_sym(p->c, P_TOK(p))->decl
|
||||
&& rslc_tok_sym(p->c, P_TOK(p))->decl->kind == RSLC_DK_VAR
|
||||
&& (rslc_tok_is_punct(P_PEEK(p, 1), RSLC_P_SEMI) || rslc_tok_is_punct(P_PEEK(p, 1), RSLC_P_COMMA)))
|
||||
{
|
||||
for (;;)
|
||||
{
|
||||
struct rslc_sym *s = P_TOK(p)->kind == RSLC_TK_IDENT ? rslc_tok_sym(p->c, P_TOK(p)) : NULL;
|
||||
struct rslc_var *v;
|
||||
if (!s || !s->decl || s->decl->kind != RSLC_DK_VAR)
|
||||
return rslc_p_fail(p, "invariant names a variable", NULL);
|
||||
v = (struct rslc_var*)s->decl->ptr;
|
||||
if (v->storage != RSLC_ST_OUT && v->storage != RSLC_ST_BUILTIN)
|
||||
return rslc_p_fail(p, "only an output can be made invariant", NULL);
|
||||
v->quals |= RSLC_VQ_INVARIANT;
|
||||
p->c->prog.spirv_only = "invariant on a declared variable";
|
||||
rslc_p_next(p);
|
||||
if (!rslc_p_accept(p, RSLC_P_COMMA))
|
||||
break;
|
||||
}
|
||||
return rslc_p_expect(p, RSLC_P_SEMI, "';'") ? 1 : 0;
|
||||
}
|
||||
if (global && P_TOK(p)->kind == RSLC_TK_IDENT && P_TOK(p)->len == 20
|
||||
&& !memcmp(rslc_tok_text(p->c, P_TOK(p)), "spirv_execution_mode", 20))
|
||||
return rslc_p_spirv_execution_mode(p) ? 1 : 0;
|
||||
@@ -6611,6 +6815,15 @@ static bool rslc_p_stage_builtins(struct rslc_parser *p)
|
||||
|| !rslc_p_builtin_var(p, "gl_FrontFacing", p->t_bool, RSLC_BI_FRONTFACING, RSLC_DIR_IN))
|
||||
return false;
|
||||
#ifdef RSLC_EXTENDED
|
||||
/* GL_ARB_shader_stencil_export: the stencil reference written by
|
||||
* the stage */
|
||||
if (!rslc_p_builtin_var(p, "gl_FragStencilRefARB", p->t_int, RSLC_BI_FRAGSTENCILREF, RSLC_DIR_OUT))
|
||||
return false;
|
||||
if ( !rslc_p_builtin_var(p, "gl_BaryCoordEXT", rslc_ty_vecn(p, RSLC_BASE_FLOAT, 3),
|
||||
RSLC_BI_BARYCOORD, RSLC_DIR_IN)
|
||||
|| !rslc_p_builtin_var(p, "gl_BaryCoordNoPerspEXT", rslc_ty_vecn(p, RSLC_BASE_FLOAT, 3),
|
||||
RSLC_BI_BARYCOORDNOPERSP, RSLC_DIR_IN))
|
||||
return false;
|
||||
if (!rslc_p_builtin_var(p, "gl_SampleID", p->t_int, RSLC_BI_SAMPLEID, RSLC_DIR_IN)
|
||||
|| !rslc_p_builtin_var(p, "gl_HelperInvocation", p->t_bool,
|
||||
RSLC_BI_HELPERINVOCATION, RSLC_DIR_IN)
|
||||
@@ -7301,6 +7514,22 @@ uint32_t rslc_b_member(struct rslc_builder *b, uint32_t e, uint32_t field)
|
||||
rslc_prec_max(rslc_b_node(b, e)->prec, bt->fields[field].precision));
|
||||
}
|
||||
|
||||
#ifdef RSLC_EXTENDED
|
||||
uint32_t rslc_b_length(struct rslc_builder *b, uint32_t e)
|
||||
{
|
||||
const struct rslc_type *bt;
|
||||
if (!rslc_b_ok(b) || !e)
|
||||
return 0;
|
||||
bt = rslc_b_node(b, e)->type;
|
||||
if (bt->kind != RSLC_TY_ARRAY)
|
||||
return rslc_b_fail(b, "length() of what is not an array", NULL);
|
||||
if (bt->array_len)
|
||||
return rslc_p_const_int(b->p, (int32_t)bt->array_len, b->line);
|
||||
b->c.prog.spirv_only = "length() of an unsized array";
|
||||
return rslc_p_mk(b->p, RSLC_OP_UNARY, b->p->t_int, e, 0, 0, RSLC_UN_ARRAYLEN, b->line, 0);
|
||||
}
|
||||
#endif
|
||||
|
||||
uint32_t rslc_b_index(struct rslc_builder *b, uint32_t e, uint32_t idx)
|
||||
{
|
||||
if (!rslc_b_ok(b) || !e || !idx)
|
||||
|
||||
@@ -427,9 +427,13 @@ static const char *const rslc_keywords[] = {
|
||||
"samplerCubeArray", "isamplerCubeArray", "usamplerCubeArray",
|
||||
"sampler1DShadow", "sampler2DShadow", "sampler2DArrayShadow",
|
||||
"samplerCubeShadow", "samplerCubeArrayShadow",
|
||||
"sampler2DMS", "isampler2DMS", "usampler2DMS",
|
||||
"image2D", "iimage2D", "uimage2D", "image2DArray", "iimage2DArray", "uimage2DArray",
|
||||
"subpassInput",
|
||||
"buffer", "readonly", "writeonly", "coherent", "restrict", "volatile", "shared"
|
||||
"image2DMS", "iimage2DMS", "uimage2DMS",
|
||||
"subpassInput", "subpassInputMS",
|
||||
"double",
|
||||
"buffer", "readonly", "writeonly", "coherent", "restrict", "volatile", "shared",
|
||||
"pervertexEXT"
|
||||
#endif
|
||||
};
|
||||
|
||||
@@ -984,6 +988,8 @@ uint32_t rslc_layout_field_offset(const struct rslc_type *t, uint32_t field,
|
||||
uint32_t fa, fs, fst;
|
||||
rslc_layout_of(t->fields[i].type, std140, &fa, &fs, &fst);
|
||||
off = (off + fa - 1) / fa * fa;
|
||||
if (t->fields[i].offset_p1)
|
||||
off = t->fields[i].offset_p1 - 1;
|
||||
if (i == field)
|
||||
return off;
|
||||
off += fs;
|
||||
@@ -1209,6 +1215,12 @@ struct spv_const
|
||||
uint32_t id;
|
||||
};
|
||||
|
||||
struct spv_vconst
|
||||
{
|
||||
uint32_t type, id;
|
||||
uint32_t c[4];
|
||||
};
|
||||
|
||||
struct spv_var
|
||||
{
|
||||
uint32_t id; /* the OpVariable, or the constant for a folded const */
|
||||
@@ -1216,6 +1228,7 @@ struct spv_var
|
||||
uint32_t tid; /* type id of the variable's value */
|
||||
uint8_t sc; /* storage class */
|
||||
bool is_const; /* id is a constant value, not a pointer */
|
||||
bool byval; /* a const in parameter: id is its value */
|
||||
bool in_block; /* RSLC_ST_MEMBER / BLOCK: id is the block variable */
|
||||
};
|
||||
|
||||
@@ -1235,6 +1248,10 @@ struct spv_emit
|
||||
uint32_t *func_ids; /* by func id */
|
||||
uint32_t *func_types;
|
||||
struct spv_const *consts;
|
||||
struct spv_vconst *vconsts; /* constant vectors made, by type and components */
|
||||
uint32_t nvconsts, vconsts_cap;
|
||||
struct spv_const *xptrs; /* pointer types to types outside tinfo: type, class, id */
|
||||
uint32_t nxptrs, xptrs_cap;
|
||||
struct rslc_wbuf entry_iface; /* interface ids for OpEntryPoint */
|
||||
struct rslc_wbuf func_vars; /* OpVariable Function words, hoisted */
|
||||
struct rslc_wbuf fntypes; /* interned OpTypeFunction: id, n, words[n] */
|
||||
@@ -1252,6 +1269,8 @@ struct spv_emit
|
||||
uint32_t entry_label;
|
||||
bool need_imagequery;
|
||||
bool need_storageimageextended;
|
||||
bool need_storageimagemultisample;
|
||||
bool need_storageimagewritewithoutformat;
|
||||
bool need_inputattachment;
|
||||
bool need_groupnonuniform;
|
||||
bool need_groupnonuniformarith;
|
||||
@@ -1262,6 +1281,9 @@ struct spv_emit
|
||||
bool need_geometry; /* a pixel stage reading gl_Layer */
|
||||
bool need_clipdistance;
|
||||
bool writes_fragdepth; /* the entry point declares DepthReplacing */
|
||||
bool need_float64; /* Float64 */
|
||||
bool need_barycentric; /* FragmentBarycentricKHR, with its extension */
|
||||
bool writes_stencilref; /* and StencilRefReplacingEXT, with its extension */
|
||||
bool need_culldistance;
|
||||
bool in_main;
|
||||
};
|
||||
@@ -1553,6 +1575,11 @@ static uint32_t spv_type(struct spv_emit *e, const struct rslc_type *t)
|
||||
if (!spv_inst3(e, SPV_SEC_TYPES, SpvOpTypeInt, id, 32, 0))
|
||||
return 0;
|
||||
break;
|
||||
case RSLC_BASE_DOUBLE:
|
||||
if (!spv_inst2(e, SPV_SEC_TYPES, SpvOpTypeFloat, id, 64))
|
||||
return 0;
|
||||
e->need_float64 = true;
|
||||
break;
|
||||
default:
|
||||
if (!spv_inst2(e, SPV_SEC_TYPES, SpvOpTypeFloat, id, 32))
|
||||
return 0;
|
||||
@@ -1625,7 +1652,7 @@ static uint32_t spv_type(struct spv_emit *e, const struct rslc_type *t)
|
||||
w[0] = img; w[1] = s; w[2] = (uint32_t)t->rows - 1;
|
||||
w[3] = (t->cols & RSLC_SX_SHADOW) ? 1 : 0;
|
||||
w[4] = (t->cols & RSLC_SX_ARRAY) ? 1 : 0;
|
||||
w[5] = 0; w[6] = 1; w[7] = 0;
|
||||
w[5] = (t->cols & RSLC_SX_MS) ? 1 : 0; w[6] = 1; w[7] = 0;
|
||||
if (t->rows == RSLC_SDIM_1D)
|
||||
e->need_sampled1d = true;
|
||||
if (t->rows == RSLC_SDIM_CUBE && (t->cols & RSLC_SX_ARRAY))
|
||||
@@ -1639,6 +1666,7 @@ static uint32_t spv_type(struct spv_emit *e, const struct rslc_type *t)
|
||||
}
|
||||
case RSLC_TY_IMAGE2D:
|
||||
case RSLC_TY_IMAGE2DARRAY:
|
||||
case RSLC_TY_IMAGE2DMS:
|
||||
{
|
||||
/* a storage image: Sampled 2, its format from the layout (cols) */
|
||||
uint32_t s = spv_type(e, rslc_type_basic(e->c, RSLC_TY_SCALAR, (enum rslc_base)t->base, 1, 1));
|
||||
@@ -1646,9 +1674,14 @@ static uint32_t spv_type(struct spv_emit *e, const struct rslc_type *t)
|
||||
return 0;
|
||||
ti = &e->tinfo[t->id];
|
||||
w[0] = id; w[1] = s; w[2] = 1 /* 2D */; w[3] = 0;
|
||||
w[4] = t->kind == RSLC_TY_IMAGE2DARRAY; w[5] = 0; w[6] = 2; w[7] = t->cols;
|
||||
if (t->cols == RSLC_IMGFMT_R8 || t->cols == RSLC_IMGFMT_RG8)
|
||||
w[4] = t->kind == RSLC_TY_IMAGE2DARRAY; w[5] = t->kind == RSLC_TY_IMAGE2DMS;
|
||||
w[6] = 2; w[7] = t->cols;
|
||||
if (!t->cols)
|
||||
e->need_storageimagewritewithoutformat = true;
|
||||
else if (!RSLC_IMGFMT_IS_BASE(t->cols))
|
||||
e->need_storageimageextended = true;
|
||||
if (t->kind == RSLC_TY_IMAGE2DMS)
|
||||
e->need_storageimagemultisample = true;
|
||||
if (!spv_inst(e, SPV_SEC_TYPES, SpvOpTypeImage, w, 8))
|
||||
return 0;
|
||||
ti->img = id;
|
||||
@@ -1661,7 +1694,7 @@ static uint32_t spv_type(struct spv_emit *e, const struct rslc_type *t)
|
||||
if (!s)
|
||||
return 0;
|
||||
ti = &e->tinfo[t->id];
|
||||
w[0] = id; w[1] = s; w[2] = 6; w[3] = 0; w[4] = 0; w[5] = 0; w[6] = 2; w[7] = 0;
|
||||
w[0] = id; w[1] = s; w[2] = 6; w[3] = 0; w[4] = 0; w[5] = t->cols == 2; w[6] = 2; w[7] = 0;
|
||||
if (!spv_inst(e, SPV_SEC_TYPES, SpvOpTypeImage, w, 8))
|
||||
return 0;
|
||||
ti->img = id;
|
||||
@@ -1726,16 +1759,49 @@ static uint32_t spv_ptr(struct spv_emit *e, uint32_t tid, unsigned sc)
|
||||
return 0;
|
||||
return e->tinfo[i].ptr[sc];
|
||||
}
|
||||
/* A laid-out type or an ad-hoc one: make a fresh pointer type. */
|
||||
/* A laid-out type or an ad-hoc one: one pointer type for each it is
|
||||
* asked of, kept by the type's id and the storage class. */
|
||||
for (i = 0; i < e->nxptrs; i++)
|
||||
if (e->xptrs[i].type == tid && e->xptrs[i].bits == sc)
|
||||
return e->xptrs[i].id;
|
||||
{
|
||||
uint32_t p = spv_id(e);
|
||||
if (!spv_inst3(e, SPV_SEC_TYPES, SpvOpTypePointer, p, sc, tid))
|
||||
return 0;
|
||||
if (e->nxptrs == e->xptrs_cap)
|
||||
{
|
||||
uint32_t ncap = e->xptrs_cap ? e->xptrs_cap * 2 : 32;
|
||||
struct spv_const *nv = (struct spv_const*)rslc_arena_alloc(e->c, ncap * sizeof(*nv));
|
||||
if (!nv)
|
||||
return 0;
|
||||
if (e->nxptrs)
|
||||
memcpy(nv, e->xptrs, e->nxptrs * sizeof(*nv));
|
||||
e->xptrs = nv;
|
||||
e->xptrs_cap = ncap;
|
||||
}
|
||||
e->xptrs[e->nxptrs].type = tid;
|
||||
e->xptrs[e->nxptrs].bits = sc;
|
||||
e->xptrs[e->nxptrs].id = p;
|
||||
e->nxptrs++;
|
||||
return p;
|
||||
}
|
||||
}
|
||||
|
||||
/* Function parameters are pointers; samplers point at the uniform. */
|
||||
/* A const in parameter is never written: it is handed over as its
|
||||
* value, with no variable made and stored for it at the call and no
|
||||
* load in the function (as glslang does). Opaque types stay pointers. */
|
||||
static bool spv_param_byval(const struct rslc_var *v)
|
||||
{
|
||||
if (v->storage != RSLC_ST_PARAM || v->dir != RSLC_DIR_IN || !(v->quals & RSLC_VQ_PRECISE))
|
||||
return false;
|
||||
#ifdef RSLC_EXTENDED
|
||||
if (RSLC_TY_IS_IMAGE(v->type->kind) || v->type->kind == RSLC_TY_SUBPASSINPUT)
|
||||
return false;
|
||||
#endif
|
||||
return !RSLC_TY_IS_SAMPLER(v->type->kind);
|
||||
}
|
||||
|
||||
/* The other function parameters are pointers; samplers point at the uniform. */
|
||||
static unsigned spv_param_sc(const struct rslc_type *t)
|
||||
{
|
||||
#ifdef RSLC_EXTENDED
|
||||
@@ -1852,11 +1918,38 @@ static uint32_t spv_const_composite(struct spv_emit *e, const struct rslc_type *
|
||||
}
|
||||
else
|
||||
return 0;
|
||||
/* a vector made before is that one again */
|
||||
if (t->kind == RSLC_TY_VEC)
|
||||
{
|
||||
uint32_t i;
|
||||
for (i = 0; i < e->nvconsts; i++)
|
||||
if (e->vconsts[i].type == tid && !memcmp(e->vconsts[i].c, words + 2, n * sizeof(uint32_t)))
|
||||
return e->vconsts[i].id;
|
||||
}
|
||||
id = spv_id(e);
|
||||
words[0] = tid;
|
||||
words[1] = id;
|
||||
if (!spv_inst(e, SPV_SEC_TYPES, SpvOpConstantComposite, words, 2 + n))
|
||||
return 0;
|
||||
if (t->kind == RSLC_TY_VEC)
|
||||
{
|
||||
if (e->nvconsts == e->vconsts_cap)
|
||||
{
|
||||
uint32_t ncap = e->vconsts_cap ? e->vconsts_cap * 2 : 32;
|
||||
struct spv_vconst *nv = (struct spv_vconst*)rslc_arena_alloc(e->c, ncap * sizeof(*nv));
|
||||
if (!nv)
|
||||
return 0;
|
||||
if (e->nvconsts)
|
||||
memcpy(nv, e->vconsts, e->nvconsts * sizeof(*nv));
|
||||
e->vconsts = nv;
|
||||
e->vconsts_cap = ncap;
|
||||
}
|
||||
e->vconsts[e->nvconsts].type = tid;
|
||||
e->vconsts[e->nvconsts].id = id;
|
||||
memset(e->vconsts[e->nvconsts].c, 0, sizeof(e->vconsts[e->nvconsts].c));
|
||||
memcpy(e->vconsts[e->nvconsts].c, words + 2, n * sizeof(uint32_t));
|
||||
e->nvconsts++;
|
||||
}
|
||||
return id;
|
||||
}
|
||||
|
||||
@@ -2077,10 +2170,13 @@ static uint32_t spv_convert(struct spv_emit *e, uint32_t v, const struct rslc_ty
|
||||
return 0;
|
||||
if (from->base == to->base)
|
||||
return v;
|
||||
if (to->base == RSLC_BASE_FLOAT)
|
||||
/* float to double and back */
|
||||
if (RSLC_BASE_IS_REAL(to->base) && RSLC_BASE_IS_REAL(from->base))
|
||||
return spv_r1(e, (enum spv_op)115 /* OpFConvert */, tid, v, prec);
|
||||
if (RSLC_BASE_IS_REAL(to->base))
|
||||
return spv_r1(e, from->base == RSLC_BASE_INT ? SpvOpConvertSToF
|
||||
: from->base == RSLC_BASE_UINT ? SpvOpConvertUToF : SpvOpSelect, tid, v, prec);
|
||||
if (from->base == RSLC_BASE_FLOAT)
|
||||
if (RSLC_BASE_IS_REAL(from->base))
|
||||
return spv_r1(e, to->base == RSLC_BASE_INT ? SpvOpConvertFToS : SpvOpConvertFToU, tid, v, prec);
|
||||
if ((from->base == RSLC_BASE_INT && to->base == RSLC_BASE_UINT)
|
||||
|| (from->base == RSLC_BASE_UINT && to->base == RSLC_BASE_INT))
|
||||
@@ -2175,7 +2271,7 @@ static uint32_t spv_mat_columnwise(struct spv_emit *e, enum spv_op op, const str
|
||||
|
||||
static enum spv_op spv_binop(const struct rslc_type *t, uint16_t op)
|
||||
{
|
||||
bool f = t->base == RSLC_BASE_FLOAT;
|
||||
bool f = RSLC_BASE_IS_REAL(t->base);
|
||||
bool s = t->base == RSLC_BASE_INT;
|
||||
switch (op)
|
||||
{
|
||||
@@ -2345,10 +2441,52 @@ static uint32_t spv_temp_of(struct spv_emit *e, uint32_t val, const struct rslc_
|
||||
return tmp;
|
||||
}
|
||||
|
||||
/* The steps from a variable to the part of it an lvalue names, kept
|
||||
* until the whole expression is walked so that they go out as one
|
||||
* access chain: a[i].b.c is one instruction and one pointer type, not
|
||||
* three of each. */
|
||||
#define SPV_CHAIN_MAX 12
|
||||
struct spv_chain
|
||||
{
|
||||
uint32_t idx[SPV_CHAIN_MAX];
|
||||
unsigned n;
|
||||
uint32_t tid; /* what the last step points at */
|
||||
};
|
||||
|
||||
/* The chain so far, from root; nothing kept after it. */
|
||||
static uint32_t spv_chain_emit(struct spv_emit *e, uint32_t root, struct spv_chain *ch, unsigned sc)
|
||||
{
|
||||
uint32_t w[3 + SPV_CHAIN_MAX];
|
||||
uint32_t ptrt = spv_ptr(e, ch->tid, sc);
|
||||
if (!ptrt)
|
||||
return 0;
|
||||
w[0] = ptrt; w[1] = spv_id(e); w[2] = root;
|
||||
memcpy(w + 3, ch->idx, ch->n * sizeof(uint32_t));
|
||||
if (!spv_fn_inst(e, SpvOpAccessChain, w, 3 + ch->n))
|
||||
return 0;
|
||||
ch->n = 0;
|
||||
return w[1];
|
||||
}
|
||||
|
||||
static bool spv_chain_push(struct spv_emit *e, uint32_t *root, struct spv_chain *ch, unsigned sc,
|
||||
uint32_t idx, uint32_t tid)
|
||||
{
|
||||
if (ch->n == SPV_CHAIN_MAX)
|
||||
{
|
||||
*root = spv_chain_emit(e, *root, ch, sc);
|
||||
if (!*root)
|
||||
return false;
|
||||
}
|
||||
ch->idx[ch->n++] = idx;
|
||||
ch->tid = tid;
|
||||
return true;
|
||||
}
|
||||
|
||||
/* The pointer for an lvalue expression: an access chain from a
|
||||
* variable. sc receives its storage class, laid whether the pointee
|
||||
* is a laid-out block type. */
|
||||
static uint32_t spv_lvalue(struct spv_emit *e, uint32_t node, unsigned *sc, bool *laid, bool *std140)
|
||||
static uint32_t spv_lvalue_root(struct spv_emit *e, uint32_t node, unsigned *sc, bool *laid,
|
||||
bool *std140, struct spv_chain *ch)
|
||||
{
|
||||
const struct rslc_node *n = rslc_node_at(e->c, node);
|
||||
switch (n->op)
|
||||
@@ -2392,6 +2530,28 @@ static uint32_t spv_lvalue(struct spv_emit *e, uint32_t node, unsigned *sc, bool
|
||||
}
|
||||
return sv->ptr;
|
||||
}
|
||||
if (e->vars[v->id].byval)
|
||||
{
|
||||
/* a value indexed through a pointer: a variable holding it */
|
||||
*sc = SPV_SC_FUNCTION; *laid = false; *std140 = false;
|
||||
return spv_temp_of(e, e->vars[v->id].id, v->type);
|
||||
}
|
||||
if (v->storage == RSLC_ST_MEMBER)
|
||||
{
|
||||
/* a bare block member: the block, and the member's index */
|
||||
const struct rslc_blockdecl *b = &e->c->prog.blocks[v->block];
|
||||
uint32_t idx = spv_const_int(e, (int32_t)v->member);
|
||||
uint32_t root = e->block_var[v->block];
|
||||
uint32_t ftid;
|
||||
*sc = b->push ? SPV_SC_PUSHCONSTANT : SPV_SC_UNIFORM;
|
||||
*laid = true;
|
||||
*std140 = b->std140;
|
||||
e->member_active[v->block][v->member] = 1;
|
||||
ftid = spv_type_laid(e, b->type->fields[v->member].type, b->std140);
|
||||
if (!ftid || !idx || !spv_chain_push(e, &root, ch, *sc, idx, ftid))
|
||||
return 0;
|
||||
return root;
|
||||
}
|
||||
return spv_var_ptr(e, v, sc, laid, std140);
|
||||
}
|
||||
case RSLC_OP_MEMBER:
|
||||
@@ -2400,8 +2560,7 @@ static uint32_t spv_lvalue(struct spv_emit *e, uint32_t node, unsigned *sc, bool
|
||||
const struct rslc_node *bn = rslc_node_at(e->c, n->a);
|
||||
uint32_t base;
|
||||
uint32_t idx;
|
||||
uint32_t tid, ptrt, id;
|
||||
uint32_t w[4];
|
||||
uint32_t tid;
|
||||
if (n->op == RSLC_OP_MEMBER && bn->op == RSLC_OP_VAR
|
||||
&& e->c->prog.vars[bn->a]->storage == RSLC_ST_BLOCK)
|
||||
{
|
||||
@@ -2415,7 +2574,7 @@ static uint32_t spv_lvalue(struct spv_emit *e, uint32_t node, unsigned *sc, bool
|
||||
e->member_active[bv->block][n->b] = 1;
|
||||
}
|
||||
else
|
||||
base = spv_lvalue(e, n->a, sc, laid, std140);
|
||||
base = spv_lvalue_root(e, n->a, sc, laid, std140, ch);
|
||||
if (!base)
|
||||
return 0;
|
||||
if (n->op == RSLC_OP_MEMBER)
|
||||
@@ -2441,40 +2600,25 @@ static uint32_t spv_lvalue(struct spv_emit *e, uint32_t node, unsigned *sc, bool
|
||||
#ifdef RSLC_EXTENDED
|
||||
have_tid:
|
||||
#endif
|
||||
if (!tid)
|
||||
if (!tid || !spv_chain_push(e, &base, ch, *sc, idx, tid))
|
||||
return 0;
|
||||
ptrt = spv_ptr(e, tid, *sc);
|
||||
if (!ptrt)
|
||||
return 0;
|
||||
id = spv_id(e);
|
||||
w[0] = ptrt; w[1] = id; w[2] = base; w[3] = idx;
|
||||
if (!spv_fn_inst(e, SpvOpAccessChain, w, 4))
|
||||
return 0;
|
||||
return id;
|
||||
return base;
|
||||
}
|
||||
case RSLC_OP_SWIZZLE:
|
||||
{
|
||||
/* single-component swizzle is an access chain */
|
||||
unsigned cnt = n->aux & 3u;
|
||||
uint32_t base, idx, tid, ptrt, id;
|
||||
uint32_t w[4];
|
||||
uint32_t base, idx, tid;
|
||||
if (cnt != 1)
|
||||
return 0;
|
||||
base = spv_lvalue(e, n->a, sc, laid, std140);
|
||||
base = spv_lvalue_root(e, n->a, sc, laid, std140, ch);
|
||||
if (!base)
|
||||
return 0;
|
||||
idx = spv_const_int(e, (int32_t)((n->aux >> 2) & 3u));
|
||||
tid = spv_type(e, n->type);
|
||||
if (!idx || !tid)
|
||||
if (!idx || !tid || !spv_chain_push(e, &base, ch, *sc, idx, tid))
|
||||
return 0;
|
||||
ptrt = spv_ptr(e, tid, *sc);
|
||||
if (!ptrt)
|
||||
return 0;
|
||||
id = spv_id(e);
|
||||
w[0] = ptrt; w[1] = id; w[2] = base; w[3] = idx;
|
||||
if (!spv_fn_inst(e, SpvOpAccessChain, w, 4))
|
||||
return 0;
|
||||
return id;
|
||||
return base;
|
||||
}
|
||||
default:
|
||||
{
|
||||
@@ -2488,6 +2632,18 @@ have_tid:
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static uint32_t spv_lvalue(struct spv_emit *e, uint32_t node, unsigned *sc, bool *laid, bool *std140)
|
||||
{
|
||||
struct spv_chain ch;
|
||||
uint32_t root;
|
||||
ch.n = 0;
|
||||
root = spv_lvalue_root(e, node, sc, laid, std140, &ch);
|
||||
if (root && ch.n)
|
||||
return spv_chain_emit(e, root, &ch, *sc);
|
||||
return root;
|
||||
}
|
||||
|
||||
/* Stores val into the lvalue node; multi-component swizzles become a
|
||||
* load, insert, store. */
|
||||
static bool spv_assign_to(struct spv_emit *e, uint32_t node, uint32_t val)
|
||||
@@ -2897,7 +3053,7 @@ static uint32_t spv_construct_node(struct spv_emit *e, const struct rslc_node *n
|
||||
/* ext-inst builtin map; 0 = core op handled elsewhere */
|
||||
static uint32_t spv_bfn_ext(unsigned bfn, const struct rslc_type *t)
|
||||
{
|
||||
bool f = t->base == RSLC_BASE_FLOAT;
|
||||
bool f = RSLC_BASE_IS_REAL(t->base);
|
||||
bool s = t->base == RSLC_BASE_INT;
|
||||
switch (bfn)
|
||||
{
|
||||
@@ -3147,7 +3303,8 @@ static uint32_t spv_builtin_node(struct spv_emit *e, const struct rslc_node *n)
|
||||
return 0;
|
||||
#ifdef RSLC_EXTENDED
|
||||
/* multisampled and buffer images have no levels */
|
||||
if (t0->kind == RSLC_TY_SAMPLER2DMSARRAY || t0->kind == RSLC_TY_SAMPLERBUFFER)
|
||||
if ( t0->kind == RSLC_TY_SAMPLER2DMSARRAY || t0->kind == RSLC_TY_SAMPLERBUFFER
|
||||
|| (t0->kind == RSLC_TY_SAMPLERX && (t0->cols & RSLC_SX_MS)))
|
||||
return spv_r1(e, SpvOpImageQuerySize, tid, img, prec);
|
||||
#endif
|
||||
return spv_r2(e, SpvOpImageQuerySizeLod, tid, img, args[1], prec);
|
||||
@@ -3173,7 +3330,8 @@ static uint32_t spv_builtin_node(struct spv_emit *e, const struct rslc_node *n)
|
||||
/* a buffer has no level; a multisampled image takes the sample */
|
||||
if (t0->kind == RSLC_TY_SAMPLERBUFFER)
|
||||
return spv_image_op(e, SpvOpImageFetch, tid, img, args[1], 0, 0, NULL, 0, prec);
|
||||
if (t0->kind == RSLC_TY_SAMPLER2DMSARRAY)
|
||||
if ( t0->kind == RSLC_TY_SAMPLER2DMSARRAY
|
||||
|| (t0->kind == RSLC_TY_SAMPLERX && (t0->cols & RSLC_SX_MS)))
|
||||
{
|
||||
ops[0] = args[2];
|
||||
return spv_image_op(e, SpvOpImageFetch, tid, img, args[1], 0, SPV_IMG_SAMPLE, ops, 1, prec);
|
||||
@@ -3376,13 +3534,28 @@ static uint32_t spv_builtin_node(struct spv_emit *e, const struct rslc_node *n)
|
||||
}
|
||||
case RSLC_BFN_IMAGESTORE:
|
||||
{
|
||||
uint32_t w[3];
|
||||
uint32_t w[5];
|
||||
w[0] = args[0]; w[1] = args[1]; w[2] = args[2];
|
||||
if (nargs == 4)
|
||||
{
|
||||
/* a multisampled image: (image, coordinate, sample, texel) */
|
||||
w[2] = args[3]; w[3] = SPV_IMG_SAMPLE; w[4] = args[2];
|
||||
if (!spv_fn_inst(e, SpvOpImageWrite, w, 5))
|
||||
return 0;
|
||||
return tid;
|
||||
}
|
||||
if (!spv_fn_inst(e, SpvOpImageWrite, w, 3))
|
||||
return 0;
|
||||
return tid;
|
||||
}
|
||||
case RSLC_BFN_IMAGELOAD:
|
||||
if (nargs == 3)
|
||||
{
|
||||
/* a multisampled image: the sample is an image operand */
|
||||
uint32_t ops[1];
|
||||
ops[0] = args[2];
|
||||
return spv_image_op(e, SpvOpImageRead, tid, args[0], args[1], 0, SPV_IMG_SAMPLE, ops, 1, prec);
|
||||
}
|
||||
return spv_r2(e, SpvOpImageRead, tid, args[0], args[1], prec);
|
||||
case RSLC_BFN_IMAGESIZE:
|
||||
e->need_imagequery = true;
|
||||
@@ -3395,8 +3568,23 @@ static uint32_t spv_builtin_node(struct spv_emit *e, const struct rslc_node *n)
|
||||
rslc_type_basic(e->c, RSLC_TY_VEC, RSLC_BASE_INT, 2, 1), zero, &used);
|
||||
if (!coord)
|
||||
return 0;
|
||||
if (nargs == 2)
|
||||
{
|
||||
/* a multisampled attachment: the sample is an image operand */
|
||||
uint32_t ops[1];
|
||||
ops[0] = args[1];
|
||||
return spv_image_op(e, SpvOpImageRead, tid, args[0], coord, 0, SPV_IMG_SAMPLE, ops, 1, prec);
|
||||
}
|
||||
return spv_r2(e, SpvOpImageRead, tid, args[0], coord, prec);
|
||||
}
|
||||
case RSLC_BFN_TEXTURESAMPLES:
|
||||
{
|
||||
uint32_t img = spv_r1(e, SpvOpImage, e->tinfo[t0->id].img, args[0], 0);
|
||||
if (!img)
|
||||
return 0;
|
||||
e->need_imagequery = true;
|
||||
return spv_r1(e, 107 /* OpImageQuerySamples */, tid, img, prec);
|
||||
}
|
||||
case RSLC_BFN_SUBGROUPELECT:
|
||||
{
|
||||
/* subgroup scope; SPIR-V 1.3 */
|
||||
@@ -3741,6 +3929,39 @@ static uint32_t spv_rvalue_impl(struct spv_emit *e, uint32_t node)
|
||||
switch (n->op)
|
||||
{
|
||||
case RSLC_OP_CONST:
|
||||
if (n->type->base == RSLC_BASE_DOUBLE)
|
||||
{
|
||||
/* 64 bits, low word first; one constant for each value */
|
||||
uint32_t dt = spv_type(e, n->type), k, id;
|
||||
uint32_t w[4];
|
||||
if (!dt)
|
||||
return 0;
|
||||
for (k = 0; k < e->nvconsts; k++)
|
||||
if (e->vconsts[k].type == dt && e->vconsts[k].c[0] == n->b && e->vconsts[k].c[1] == n->c)
|
||||
return e->vconsts[k].id;
|
||||
id = spv_id(e);
|
||||
w[0] = dt; w[1] = id; w[2] = n->b; w[3] = n->c;
|
||||
if (!spv_inst(e, SPV_SEC_TYPES, SpvOpConstant, w, 4))
|
||||
return 0;
|
||||
if (e->nvconsts == e->vconsts_cap)
|
||||
{
|
||||
uint32_t ncap = e->vconsts_cap ? e->vconsts_cap * 2 : 32;
|
||||
struct spv_vconst *nv = (struct spv_vconst*)rslc_arena_alloc(e->c, ncap * sizeof(*nv));
|
||||
if (!nv)
|
||||
return 0;
|
||||
if (e->nvconsts)
|
||||
memcpy(nv, e->vconsts, e->nvconsts * sizeof(*nv));
|
||||
e->vconsts = nv;
|
||||
e->vconsts_cap = ncap;
|
||||
}
|
||||
memset(&e->vconsts[e->nvconsts], 0, sizeof(e->vconsts[0]));
|
||||
e->vconsts[e->nvconsts].type = dt;
|
||||
e->vconsts[e->nvconsts].id = id;
|
||||
e->vconsts[e->nvconsts].c[0] = n->b;
|
||||
e->vconsts[e->nvconsts].c[1] = n->c;
|
||||
e->nvconsts++;
|
||||
return id;
|
||||
}
|
||||
return spv_const_bits(e, n->type, n->a);
|
||||
case RSLC_OP_VAR:
|
||||
{
|
||||
@@ -3749,6 +3970,8 @@ static uint32_t spv_rvalue_impl(struct spv_emit *e, uint32_t node)
|
||||
uint32_t ptr;
|
||||
if (e->vars[v->id].is_const)
|
||||
return spv_const_var(e, v);
|
||||
if (e->vars[v->id].byval)
|
||||
return e->vars[v->id].id;
|
||||
ptr = spv_var_ptr(e, v, &sc, &laid, &std140);
|
||||
if (!ptr)
|
||||
return 0;
|
||||
@@ -3763,7 +3986,8 @@ static uint32_t spv_rvalue_impl(struct spv_emit *e, uint32_t node)
|
||||
|| rslc_node_at(e->c, n->b)->op == RSLC_OP_CONST;
|
||||
bool base_is_ptr = base->op == RSLC_OP_VAR || base->op == RSLC_OP_MEMBER
|
||||
|| base->op == RSLC_OP_INDEX || (base->op == RSLC_OP_SWIZZLE && (base->aux & 3u) == 1);
|
||||
if (base->op == RSLC_OP_VAR && e->vars[e->c->prog.vars[base->a]->id].is_const)
|
||||
if (base->op == RSLC_OP_VAR && (e->vars[e->c->prog.vars[base->a]->id].is_const
|
||||
|| e->vars[e->c->prog.vars[base->a]->id].byval))
|
||||
base_is_ptr = false;
|
||||
if (!base_is_ptr && const_idx)
|
||||
{
|
||||
@@ -3841,6 +4065,13 @@ static uint32_t spv_rvalue_impl(struct spv_emit *e, uint32_t node)
|
||||
return 0;
|
||||
continue;
|
||||
}
|
||||
if (spv_param_byval(pv))
|
||||
{
|
||||
temps[i] = spv_rvalue(e, an);
|
||||
if (!temps[i])
|
||||
return 0;
|
||||
continue;
|
||||
}
|
||||
temps[i] = spv_local(e, pv->type, NULL, pv->precision);
|
||||
if (!temps[i])
|
||||
return 0;
|
||||
@@ -3887,6 +4118,37 @@ static uint32_t spv_rvalue_impl(struct spv_emit *e, uint32_t node)
|
||||
return 0;
|
||||
switch (n->aux)
|
||||
{
|
||||
#ifdef RSLC_EXTENDED
|
||||
case RSLC_UN_ARRAYLEN:
|
||||
{
|
||||
/* OpArrayLength of the block and the member's index: the
|
||||
* array is the last member of a buffer block, named bare
|
||||
* or through the block's instance */
|
||||
const struct rslc_var *v = NULL;
|
||||
uint32_t member = 0, len, utid;
|
||||
uint32_t w[4];
|
||||
if (an->op == RSLC_OP_VAR && e->c->prog.vars[an->a]->storage == RSLC_ST_MEMBER)
|
||||
{
|
||||
v = e->c->prog.vars[an->a];
|
||||
member = v->member;
|
||||
}
|
||||
else if (an->op == RSLC_OP_MEMBER && rslc_node_at(e->c, an->a)->op == RSLC_OP_VAR
|
||||
&& e->c->prog.vars[rslc_node_at(e->c, an->a)->a]->storage == RSLC_ST_BLOCK)
|
||||
{
|
||||
v = e->c->prog.vars[rslc_node_at(e->c, an->a)->a];
|
||||
member = an->b;
|
||||
}
|
||||
utid = spv_type(e, rslc_type_basic(e->c, RSLC_TY_SCALAR, RSLC_BASE_UINT, 1, 1));
|
||||
if (!v || !utid)
|
||||
return 0;
|
||||
e->member_active[v->block][member] = 1;
|
||||
len = spv_id(e);
|
||||
w[0] = utid; w[1] = len; w[2] = e->block_var[v->block]; w[3] = member;
|
||||
if (!spv_fn_inst(e, 68 /* OpArrayLength */, w, 4))
|
||||
return 0;
|
||||
return spv_r1(e, SpvOpBitcast, tid, len, 0);
|
||||
}
|
||||
#endif
|
||||
case RSLC_UN_NEG:
|
||||
{
|
||||
uint32_t v = spv_rvalue(e, n->a);
|
||||
@@ -3907,7 +4169,7 @@ static uint32_t spv_rvalue_impl(struct spv_emit *e, uint32_t node)
|
||||
}
|
||||
return spv_construct(e, n->type, cols, n->type->cols, prec);
|
||||
}
|
||||
return spv_r1(e, n->type->base == RSLC_BASE_FLOAT ? SpvOpFNegate : SpvOpSNegate, tid, v, prec);
|
||||
return spv_r1(e, RSLC_BASE_IS_REAL(n->type->base) ? SpvOpFNegate : SpvOpSNegate, tid, v, prec);
|
||||
}
|
||||
case RSLC_UN_NOT:
|
||||
{
|
||||
@@ -3928,9 +4190,12 @@ static uint32_t spv_rvalue_impl(struct spv_emit *e, uint32_t node)
|
||||
const struct rslc_type *t = an->type;
|
||||
bool inc = n->aux == RSLC_UN_PREINC || n->aux == RSLC_UN_POSTINC;
|
||||
bool post = n->aux == RSLC_UN_POSTINC || n->aux == RSLC_UN_POSTDEC;
|
||||
if (!ptr)
|
||||
/* no one pointer to it - several components of a vector
|
||||
* (v.xy--): read as a value, written back as an assignment
|
||||
* to the same place is */
|
||||
if (!ptr && (e->c->failed || t->kind == RSLC_TY_MAT))
|
||||
return 0;
|
||||
old = spv_load(e, t, ptr, prec);
|
||||
old = ptr ? spv_load(e, t, ptr, prec) : spv_rvalue(e, n->a);
|
||||
if (!old)
|
||||
return 0;
|
||||
if (t->base == RSLC_BASE_FLOAT)
|
||||
@@ -3951,7 +4216,7 @@ static uint32_t spv_rvalue_impl(struct spv_emit *e, uint32_t node)
|
||||
}
|
||||
else
|
||||
nv = one ? spv_r2(e, spv_binop(t, inc ? RSLC_BIN_ADD : RSLC_BIN_SUB), tid, old, one, prec) : 0;
|
||||
if (!nv || !spv_store(e, ptr, nv))
|
||||
if (!nv || !(ptr ? spv_store(e, ptr, nv) : spv_assign_to(e, n->a, nv)))
|
||||
return 0;
|
||||
return post ? old : nv;
|
||||
}
|
||||
@@ -4413,7 +4678,8 @@ static bool spv_function(struct spv_emit *e, const struct rslc_func *f)
|
||||
w[0] = 0; w[1] = rtid;
|
||||
for (i = 0; i < f->nparams; i++)
|
||||
{
|
||||
w[2 + i] = spv_ptr_to(e, f->params[i]->type, spv_param_sc(f->params[i]->type));
|
||||
w[2 + i] = spv_param_byval(f->params[i]) ? spv_type(e, f->params[i]->type)
|
||||
: spv_ptr_to(e, f->params[i]->type, spv_param_sc(f->params[i]->type));
|
||||
if (!w[2 + i])
|
||||
return false;
|
||||
}
|
||||
@@ -4427,10 +4693,12 @@ static bool spv_function(struct spv_emit *e, const struct rslc_func *f)
|
||||
for (i = 0; i < f->nparams; i++)
|
||||
{
|
||||
const struct rslc_var *pv = f->params[i];
|
||||
uint32_t ptr = spv_ptr_to(e, pv->type, spv_param_sc(pv->type));
|
||||
uint32_t ptr = spv_param_byval(pv) ? spv_type(e, pv->type)
|
||||
: spv_ptr_to(e, pv->type, spv_param_sc(pv->type));
|
||||
uint32_t pid = spv_id(e);
|
||||
if (!ptr || !spv_inst2(e, SPV_SEC_FUNCS, SpvOpFunctionParameter, ptr, pid))
|
||||
return false;
|
||||
e->vars[pv->id].byval = spv_param_byval(pv);
|
||||
e->vars[pv->id].id = pid;
|
||||
e->vars[pv->id].sc = (uint8_t)spv_param_sc(pv->type);
|
||||
if (pv->name && !spv_name(e, pid, pv->name->str))
|
||||
@@ -4696,6 +4964,12 @@ static bool spv_global_var(struct spv_emit *e, const struct rslc_var *v)
|
||||
return false;
|
||||
if (v->interp == RSLC_INTERP_NOPERSPECTIVE && !spv_decorate(e, id, SPV_DEC_NOPERSPECTIVE))
|
||||
return false;
|
||||
if (v->interp == RSLC_INTERP_PERVERTEX)
|
||||
{
|
||||
if (!spv_decorate(e, id, 5285u /* PerVertexKHR */))
|
||||
return false;
|
||||
e->need_barycentric = true;
|
||||
}
|
||||
if ((v->quals & RSLC_VQ_CENTROID) && !spv_decorate(e, id, SPV_DEC_CENTROID))
|
||||
return false;
|
||||
if ((v->quals & RSLC_VQ_SAMPLE) && !spv_decorate(e, id, SPV_DEC_SAMPLE))
|
||||
@@ -4776,6 +5050,9 @@ static bool spv_builtin_var(struct spv_emit *e, const struct rslc_var *v)
|
||||
: v->builtin == RSLC_BI_LOCALINVOCATIONINDEX ? SPV_BI_LOCALINVOCATIONINDEX
|
||||
: v->builtin == RSLC_BI_HELPERINVOCATION ? SPV_BI_HELPERINVOCATION
|
||||
: v->builtin == RSLC_BI_POINTCOORD ? SPV_BI_POINTCOORD
|
||||
: v->builtin == RSLC_BI_FRAGSTENCILREF ? 5014u /* FragStencilRefEXT */
|
||||
: v->builtin == RSLC_BI_BARYCOORD ? 5286u /* BaryCoordKHR */
|
||||
: v->builtin == RSLC_BI_BARYCOORDNOPERSP ? 5287u /* BaryCoordNoPerspKHR */
|
||||
: v->builtin == RSLC_BI_PRIMITIVEID ? SPV_BI_PRIMITIVEID : SPV_BI_POSITION;
|
||||
if (!ptr)
|
||||
return false;
|
||||
@@ -4784,6 +5061,10 @@ static bool spv_builtin_var(struct spv_emit *e, const struct rslc_var *v)
|
||||
e->need_samplerateshading = true;
|
||||
if (v->builtin == RSLC_BI_FRAGDEPTH)
|
||||
e->writes_fragdepth = true;
|
||||
if (v->builtin == RSLC_BI_FRAGSTENCILREF)
|
||||
e->writes_stencilref = true;
|
||||
if (v->builtin == RSLC_BI_BARYCOORD || v->builtin == RSLC_BI_BARYCOORDNOPERSP)
|
||||
e->need_barycentric = true;
|
||||
id = spv_id(e);
|
||||
if (!spv_inst3(e, SPV_SEC_TYPES, SpvOpVariable, ptr, id, sc))
|
||||
return false;
|
||||
@@ -4923,6 +5204,20 @@ static bool spv_pervertex(struct spv_emit *e)
|
||||
|| !spv_mdecorate1(e, st, 2, SPV_DEC_BUILTIN, SPV_BI_CLIPDISTANCE)
|
||||
|| !spv_mdecorate1(e, st, 3, SPV_DEC_BUILTIN, SPV_BI_CULLDISTANCE))
|
||||
return false;
|
||||
/* a member made invariant: invariant gl_Position; */
|
||||
{
|
||||
uint32_t k;
|
||||
for (k = 1; k < e->c->prog.nvars; k++)
|
||||
{
|
||||
const struct rslc_var *bv = e->c->prog.vars[k];
|
||||
if (bv->storage == RSLC_ST_BUILTIN && (bv->quals & RSLC_VQ_INVARIANT)
|
||||
&& RSLC_BI_IN_PERVERTEX(bv->builtin)
|
||||
&& !spv_mdecorate(e, st, bv->builtin == RSLC_BI_POSITION ? 0
|
||||
: bv->builtin == RSLC_BI_POINTSIZE ? 1 : bv->builtin == RSLC_BI_CLIPDISTANCE ? 2 : 3,
|
||||
SPV_DEC_INVARIANT))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
ptr = spv_ptr(e, st, SPV_SC_OUTPUT);
|
||||
if (!ptr)
|
||||
return false;
|
||||
@@ -5183,6 +5478,12 @@ static bool rslc_emit_spirv(struct rslc_ctx *c, struct rslc_result *result)
|
||||
return false;
|
||||
if (e->need_clipdistance && !spv_inst1(e, SPV_SEC_CAPS, SpvOpCapability, SPV_CAP_CLIPDISTANCE))
|
||||
return false;
|
||||
if (e->writes_stencilref && !spv_inst1(e, SPV_SEC_CAPS, SpvOpCapability, 5013u /* StencilExportEXT */))
|
||||
return false;
|
||||
if (e->need_barycentric && !spv_inst1(e, SPV_SEC_CAPS, SpvOpCapability, 5284u /* FragmentBarycentricKHR */))
|
||||
return false;
|
||||
if (e->need_float64 && !spv_inst1(e, SPV_SEC_CAPS, SpvOpCapability, 10u /* Float64 */))
|
||||
return false;
|
||||
if (e->need_culldistance && !spv_inst1(e, SPV_SEC_CAPS, SpvOpCapability, SPV_CAP_CULLDISTANCE))
|
||||
return false;
|
||||
if (e->need_imagequery && !spv_inst1(e, SPV_SEC_CAPS, SpvOpCapability, SPV_CAP_IMAGEQUERY))
|
||||
@@ -5190,6 +5491,12 @@ static bool rslc_emit_spirv(struct rslc_ctx *c, struct rslc_result *result)
|
||||
if (e->need_storageimageextended &&
|
||||
!spv_inst1(e, SPV_SEC_CAPS, SpvOpCapability, SPV_CAP_STORAGEIMAGEEXTENDED))
|
||||
return false;
|
||||
if (e->need_storageimagewritewithoutformat
|
||||
&& !spv_inst1(e, SPV_SEC_CAPS, SpvOpCapability, 56u /* StorageImageWriteWithoutFormat */))
|
||||
return false;
|
||||
if (e->need_storageimagemultisample
|
||||
&& !spv_inst1(e, SPV_SEC_CAPS, SpvOpCapability, 27u /* StorageImageMultisample */))
|
||||
return false;
|
||||
#ifdef RSLC_EXTENDED
|
||||
if (e->need_inputattachment
|
||||
&& !spv_inst1(e, SPV_SEC_CAPS, SpvOpCapability, SPV_CAP_INPUTATTACHMENT))
|
||||
@@ -5237,6 +5544,12 @@ static bool rslc_emit_spirv(struct rslc_ctx *c, struct rslc_result *result)
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
if (e->writes_stencilref && !spv_inst_str(e, SPV_SEC_CAPS, SpvOpExtension, NULL, 0,
|
||||
"SPV_EXT_shader_stencil_export", NULL, 0))
|
||||
return false;
|
||||
if (e->need_barycentric && !spv_inst_str(e, SPV_SEC_CAPS, SpvOpExtension, NULL, 0,
|
||||
"SPV_KHR_fragment_shader_barycentric", NULL, 0))
|
||||
return false;
|
||||
if (!spv_inst_str(e, SPV_SEC_CAPS, SpvOpExtInstImport, &e->ext_id, 1, "GLSL.std.450", NULL, 0))
|
||||
return false;
|
||||
if (!spv_inst2(e, SPV_SEC_CAPS, SpvOpMemoryModel, 0 /* Logical */, 1 /* GLSL450 */))
|
||||
@@ -5263,6 +5576,9 @@ static bool rslc_emit_spirv(struct rslc_ctx *c, struct rslc_result *result)
|
||||
if (e->writes_fragdepth
|
||||
&& !spv_inst2(e, SPV_SEC_ENTRY, SpvOpExecutionMode, main_id, 12u /* DepthReplacing */))
|
||||
return false;
|
||||
if (e->writes_stencilref
|
||||
&& !spv_inst2(e, SPV_SEC_ENTRY, SpvOpExecutionMode, main_id, 5027u /* StencilRefReplacingEXT */))
|
||||
return false;
|
||||
#ifdef RSLC_EXTENDED
|
||||
for (i = 0; i < c->prog.nxmodes; i += 1 + c->prog.xmodes[i])
|
||||
{
|
||||
|
||||
@@ -224,14 +224,19 @@ enum rslc_kw
|
||||
/* RSLC_EXTENDED: the depth comparing ones */
|
||||
RSLC_KW_SAMPLER1DSHADOW, RSLC_KW_SAMPLER2DSHADOW, RSLC_KW_SAMPLER2DARRAYSHADOW,
|
||||
RSLC_KW_SAMPLERCUBESHADOW, RSLC_KW_SAMPLERCUBEARRAYSHADOW,
|
||||
/* RSLC_EXTENDED: one multisampled image, not an array of them */
|
||||
RSLC_KW_SAMPLER2DMS, RSLC_KW_ISAMPLER2DMS, RSLC_KW_USAMPLER2DMS,
|
||||
/* RSLC_EXTENDED image types: float, int, uint of each */
|
||||
RSLC_KW_IMAGE2D, RSLC_KW_IIMAGE2D, RSLC_KW_UIMAGE2D,
|
||||
RSLC_KW_IMAGE2DARRAY, RSLC_KW_IIMAGE2DARRAY, RSLC_KW_UIMAGE2DARRAY,
|
||||
RSLC_KW_IMAGE2DMS, RSLC_KW_IIMAGE2DMS, RSLC_KW_UIMAGE2DMS,
|
||||
/* RSLC_EXTENDED: a fragment stage's input attachment */
|
||||
RSLC_KW_SUBPASSINPUT,
|
||||
RSLC_KW_SUBPASSINPUT, RSLC_KW_SUBPASSINPUTMS,
|
||||
/* RSLC_EXTENDED: a 64-bit float scalar */
|
||||
RSLC_KW_DOUBLE,
|
||||
/* RSLC_EXTENDED qualifiers */
|
||||
RSLC_KW_BUFFER, RSLC_KW_READONLY, RSLC_KW_WRITEONLY, RSLC_KW_COHERENT,
|
||||
RSLC_KW_RESTRICT, RSLC_KW_VOLATILE, RSLC_KW_SHARED,
|
||||
RSLC_KW_RESTRICT, RSLC_KW_VOLATILE, RSLC_KW_SHARED, RSLC_KW_PERVERTEX,
|
||||
RSLC_KW_COUNT
|
||||
};
|
||||
|
||||
@@ -260,6 +265,7 @@ enum rslc_tkind
|
||||
/* RSLC_EXTENDED storage images; cols is the RSLC_IMGFMT_* */
|
||||
RSLC_TY_IMAGE2D,
|
||||
RSLC_TY_IMAGE2DARRAY,
|
||||
RSLC_TY_IMAGE2DMS, /* multisampled: read and written a sample at a time; SPIR-V only */
|
||||
/* RSLC_EXTENDED: a float input attachment */
|
||||
RSLC_TY_SUBPASSINPUT,
|
||||
/* RSLC_EXTENDED: any other sampler; rows is its RSLC_SDIM_*, cols its
|
||||
@@ -273,8 +279,10 @@ enum rslc_tkind
|
||||
#define RSLC_SDIM_CUBE 4
|
||||
#define RSLC_SX_ARRAY 1u
|
||||
#define RSLC_SX_SHADOW 2u
|
||||
#define RSLC_SX_MS 4u /* sampler2DMS: 2D, fetched a sample at a time */
|
||||
|
||||
#define RSLC_TY_IS_IMAGE(k) ((k) == RSLC_TY_IMAGE2D || (k) == RSLC_TY_IMAGE2DARRAY)
|
||||
#define RSLC_TY_IS_IMAGE(k) ((k) == RSLC_TY_IMAGE2D || (k) == RSLC_TY_IMAGE2DARRAY \
|
||||
|| (k) == RSLC_TY_IMAGE2DMS)
|
||||
/* A storage image type's rows: its access. */
|
||||
#define RSLC_IMG_READWRITE 1
|
||||
#define RSLC_IMG_WRITEONLY 2
|
||||
@@ -286,6 +294,19 @@ enum rslc_tkind
|
||||
#define RSLC_IMGFMT_RGBA16F 2
|
||||
#define RSLC_IMGFMT_R32F 3
|
||||
#define RSLC_IMGFMT_RGBA8 4
|
||||
#define RSLC_IMGFMT_RGBA8_SNORM 5
|
||||
#define RSLC_IMGFMT_RG32F 6
|
||||
#define RSLC_IMGFMT_RG16F 7
|
||||
#define RSLC_IMGFMT_R11G11B10F 8
|
||||
#define RSLC_IMGFMT_R16F 9
|
||||
#define RSLC_IMGFMT_RGBA16 10
|
||||
#define RSLC_IMGFMT_RGB10_A2 11
|
||||
#define RSLC_IMGFMT_RG16 12
|
||||
#define RSLC_IMGFMT_R16 14
|
||||
/* The formats every device with storage images has; the others need
|
||||
* StorageImageExtendedFormats. */
|
||||
#define RSLC_IMGFMT_IS_BASE(f) ((f) <= RSLC_IMGFMT_RGBA8_SNORM || ((f) >= RSLC_IMGFMT_RGBA32I \
|
||||
&& (f) <= RSLC_IMGFMT_R32I) || ((f) >= RSLC_IMGFMT_RGBA32UI && (f) <= RSLC_IMGFMT_R32UI))
|
||||
#define RSLC_IMGFMT_RG8 13
|
||||
#define RSLC_IMGFMT_R8 15
|
||||
#define RSLC_IMGFMT_RGBA32I 21
|
||||
@@ -298,7 +319,10 @@ enum rslc_tkind
|
||||
#define RSLC_IMGFMT_R32UI 33
|
||||
|
||||
/* The last keyword that names a type. */
|
||||
#define RSLC_KW_LAST_TYPE RSLC_KW_SUBPASSINPUT
|
||||
#define RSLC_KW_LAST_TYPE RSLC_KW_DOUBLE
|
||||
|
||||
/* float or double: the bases the floating-point operations take */
|
||||
#define RSLC_BASE_IS_REAL(b) ((b) == RSLC_BASE_FLOAT || (b) == RSLC_BASE_DOUBLE)
|
||||
|
||||
/* Every sampler kind: sampler2D, and the RSLC_EXTENDED ones. */
|
||||
#define RSLC_TY_IS_SAMPLER(k) ((k) == RSLC_TY_SAMPLER2D \
|
||||
@@ -316,6 +340,10 @@ struct rslc_field
|
||||
uint8_t interp; /* RSLC_EXTENDED: an in / out block member's */
|
||||
uint8_t quals; /* RSLC_VQ_* of that member */
|
||||
uint8_t pad;
|
||||
uint32_t offset_p1; /* RSLC_EXTENDED, a block's member that
|
||||
says layout(offset = n): n + 1, and
|
||||
the members after it follow from
|
||||
there; 0 when it does not */
|
||||
};
|
||||
|
||||
struct rslc_type
|
||||
@@ -364,6 +392,7 @@ enum rslc_storage
|
||||
#define RSLC_INTERP_FLAT 1
|
||||
#define RSLC_INTERP_NOPERSPECTIVE 2
|
||||
#define RSLC_INTERP_SMOOTH 3
|
||||
#define RSLC_INTERP_PERVERTEX 4 /* RSLC_EXTENDED: pervertexEXT, each vertex's value; SPIR-V only */
|
||||
|
||||
#define RSLC_VQ_CENTROID 1u
|
||||
#define RSLC_VQ_SAMPLE 2u
|
||||
@@ -425,6 +454,9 @@ struct rslc_var
|
||||
#define RSLC_BI_HELPERINVOCATION 18 /* RSLC_EXTENDED, fragment */
|
||||
#define RSLC_BI_POINTCOORD 19 /* RSLC_EXTENDED, fragment */
|
||||
#define RSLC_BI_PRIMITIVEID 20 /* RSLC_EXTENDED, geometry gl_PrimitiveIDIn */
|
||||
#define RSLC_BI_FRAGSTENCILREF 21 /* RSLC_EXTENDED, fragment out; SPIR-V only */
|
||||
#define RSLC_BI_BARYCOORD 22 /* RSLC_EXTENDED, fragment in; SPIR-V only */
|
||||
#define RSLC_BI_BARYCOORDNOPERSP 23
|
||||
|
||||
/* RSLC_EXTENDED: a geometry stage's primitives, in and out. */
|
||||
#define RSLC_GS_POINTS 1
|
||||
@@ -527,6 +559,7 @@ enum rslc_op
|
||||
#define RSLC_UN_PREDEC 5
|
||||
#define RSLC_UN_POSTINC 6
|
||||
#define RSLC_UN_POSTDEC 7
|
||||
#define RSLC_UN_ARRAYLEN 8 /* RSLC_EXTENDED: length() of a buffer block's unsized last member; SPIR-V only */
|
||||
|
||||
#define RSLC_BIN_NONE 0
|
||||
#define RSLC_BIN_ADD 1
|
||||
@@ -616,6 +649,7 @@ enum rslc_bfn
|
||||
RSLC_BFN_MEMORYBARRIERBUFFER, RSLC_BFN_MEMORYBARRIERIMAGE, RSLC_BFN_GROUPMEMORYBARRIER,
|
||||
RSLC_BFN_IMAGESTORE, RSLC_BFN_IMAGELOAD, RSLC_BFN_IMAGESIZE,
|
||||
RSLC_BFN_SUBPASSLOAD, /* RSLC_EXTENDED, fragment */
|
||||
RSLC_BFN_TEXTURESAMPLES, /* RSLC_EXTENDED: a multisampled sampler's count */
|
||||
/* RSLC_EXTENDED: subgroup operations */
|
||||
RSLC_BFN_SUBGROUPELECT, RSLC_BFN_SUBGROUPMIN, RSLC_BFN_SUBGROUPMAX,
|
||||
RSLC_BFN_COUNT
|
||||
|
||||
@@ -100,8 +100,8 @@ uint32_t cdfs_get_first_sector(cdfs_file_t* file);
|
||||
* sector 16, assuming raw 2352-byte sectors. If the probe fails
|
||||
* (e.g. a cooked 2048-byte image), stream_sector_size is left 0 and
|
||||
* the caller must fill in stream_sector_size / stream_sector_header_size
|
||||
* itself (the path adapters below derive them from the file size or
|
||||
* the cue sheet). */
|
||||
* itself (the path adapters in streams/cdfs_file.c derive them from the
|
||||
* file size or the cue sheet). */
|
||||
cdfs_track_t* cdfs_track_from_stream(
|
||||
intfstream_t* stream,
|
||||
unsigned first_sector_offset,
|
||||
@@ -132,37 +132,14 @@ typedef struct cdfs_cue_info
|
||||
bool cdfs_parse_cue(const char *cue_text, size_t len,
|
||||
unsigned track_index, cdfs_cue_info_t *out);
|
||||
|
||||
/* --- Path adapters: open by filename, then wrap ------------------------ */
|
||||
/* Sets @track's sector size and header size from its stream's length
|
||||
* - 2352, 2048 or 2336 bytes a sector, whichever divides it - and
|
||||
* leaves them 0 when none does. For a raw track whose ISO-9660 probe in
|
||||
* cdfs_track_from_stream found nothing. */
|
||||
void cdfs_track_sector_size_from_length(cdfs_track_t* track);
|
||||
|
||||
/* opens the specified track in a CD or virtual CD file - the resulting stream should be passed to
|
||||
* cdfs_open_file to get access to a file within the CD.
|
||||
*
|
||||
* supported files:
|
||||
* real CD - path will be in the form "cdrom://drive1.cue" or "cdrom://d:/drive.cue"
|
||||
* bin/cue - path will point to the cue file
|
||||
* chd - path will point to the chd file
|
||||
*
|
||||
* for bin/cue files, the following storage modes are supported:
|
||||
* MODE2/2352
|
||||
* MODE1/2352
|
||||
* MODE1/2048 - untested
|
||||
* MODE2/2336 - untested
|
||||
*/
|
||||
cdfs_track_t* cdfs_open_track(const char* path, unsigned int track_index);
|
||||
|
||||
/* opens the first data track in a CD or virtual CD file. see cdfs_open_track for supported file formats
|
||||
*/
|
||||
cdfs_track_t* cdfs_open_data_track(const char* path);
|
||||
|
||||
/* opens a raw track file for a CD or virtual CD.
|
||||
*
|
||||
* supported files:
|
||||
* real CD - path will be in the form "cdrom://drive1-track01.bin" or "cdrom://d:/drive-track01.bin"
|
||||
* BIN handles obtain their own validated geometry.
|
||||
* bin - path will point to the bin file
|
||||
* iso - path will point to the iso file
|
||||
*/
|
||||
cdfs_track_t* cdfs_open_raw_track(const char* path);
|
||||
/* cdfs_open_track, cdfs_open_data_track and cdfs_open_raw_track, which
|
||||
* open a track by filename, are in <streams/cdfs_file.h>. */
|
||||
|
||||
/* closes the CD or virtual CD track and frees the associated memory */
|
||||
void cdfs_close_track(cdfs_track_t* track);
|
||||
|
||||
@@ -158,16 +158,22 @@ data_transfer_t *data_transfer_open_source(size_t len,
|
||||
* NULL unless the transfer completed successfully. */
|
||||
uint8_t *data_transfer_source_detach(data_transfer_t *dt, size_t *len);
|
||||
|
||||
data_transfer_t *data_transfer_open_window(const char *path, size_t keep);
|
||||
|
||||
/* Borrow a seekable source for the window's lifetime. The callback writes
|
||||
* directly into final window storage, returning bytes read or -1 on error.
|
||||
* A short read fails the operation. Closing the window does not close ud. */
|
||||
* A short read fails the operation. Closing the window does not close ud
|
||||
* unless data_transfer_set_release() said how.
|
||||
* data_transfer_open_window(path), which opens a file for it, is in
|
||||
* <streams/data_transfer_file.h>. */
|
||||
typedef int64_t (*data_transfer_read_at_t)(void *ud, uint64_t offset,
|
||||
uint8_t *dst, size_t bytes);
|
||||
data_transfer_t *data_transfer_open_window_reader(size_t length, size_t keep,
|
||||
data_transfer_read_at_t read_at, void *ud);
|
||||
|
||||
/* What frees a reader's ud when the transfer is freed */
|
||||
typedef void (*data_transfer_release_t)(void *ud);
|
||||
void data_transfer_set_release(data_transfer_t *dt,
|
||||
data_transfer_release_t release);
|
||||
|
||||
const uint8_t *data_transfer_window_base(data_transfer_t *dt, size_t *len);
|
||||
/* True when the transfer is genuinely windowed - address space was
|
||||
* reserved and only the window is committed. False when the platform
|
||||
@@ -253,8 +259,10 @@ bool data_transfer_window_feed(data_transfer_t *dt, size_t tell,
|
||||
* behind the consumer. There the bytes at tell itself are what is
|
||||
* missing - a lap that landed past the head, an open that rebased -
|
||||
* and leaving them for a later tick leaves the consumer's very next
|
||||
* read on unresident pages; that catch-up is one unbudgeted extend,
|
||||
* exactly what window_feed has always done. budget == 0 disables
|
||||
* read on unresident pages; that catch-up is one unbudgeted extend
|
||||
* from margin bytes behind tell, never from further back: a frontier
|
||||
* further behind than that moves up first, so a gap the consumer has
|
||||
* passed is never read in. budget == 0 disables
|
||||
* the ceiling entirely, making this window_feed by another name.
|
||||
*
|
||||
* *resident_hi (may be NULL) reports the sequential bound this call
|
||||
@@ -296,8 +304,10 @@ void data_transfer_arena_release(data_transfer_arena_t *a);
|
||||
* when commit_cap is 0. There is no built-in ceiling: a caller that
|
||||
* asks for no cap gets no cap, and a file too large for memory is
|
||||
* refused at open rather than presented as a capped prefix. */
|
||||
data_transfer_t *data_transfer_open_prefix(const char *path,
|
||||
size_t commit_cap);
|
||||
data_transfer_t *data_transfer_open_prefix_reader(size_t length,
|
||||
size_t commit_cap, data_transfer_read_at_t read_at, void *ud);
|
||||
/* data_transfer_open_prefix(path, commit_cap), which opens a file for
|
||||
* it, is in <streams/data_transfer_file.h>. */
|
||||
|
||||
/* The fill stopped at commit_cap with the file continuing beyond it.
|
||||
* Terminal: iterate() will not advance further. */
|
||||
|
||||
@@ -33,6 +33,8 @@
|
||||
|
||||
RETRO_BEGIN_DECLS
|
||||
|
||||
struct data_transfer;
|
||||
|
||||
enum image_process_code
|
||||
{
|
||||
IMAGE_PROCESS_ERROR = -2,
|
||||
@@ -344,24 +346,25 @@ bool image_texture_scale(struct texture_image *img,
|
||||
bool image_texture_load_buffer(struct texture_image *img,
|
||||
enum image_type_enum type, void *s, size_t len);
|
||||
|
||||
/* The loader run to completion over a whole buffer or a file, with
|
||||
* @req naming what the caller takes (NULL: the ordinary image). The
|
||||
* abort hook is asked between steps; it may be NULL. False leaves
|
||||
* @img empty. */
|
||||
/* The loader run to completion over a whole buffer, with @req naming
|
||||
* what the caller takes (NULL: the ordinary image). The abort hook is
|
||||
* asked between steps; it may be NULL. False leaves @img empty. Loading
|
||||
* by path - image_texture_load and the rest - is in
|
||||
* <streams/image_texture_file.h>. */
|
||||
bool image_texture_load_buffer_request(struct texture_image *img,
|
||||
enum image_type_enum type, const void *buf, size_t len,
|
||||
const image_texture_request_t *req,
|
||||
bool (*should_abort)(void *ud), void *ud);
|
||||
bool image_texture_load_request(struct texture_image *img,
|
||||
const char *path, const image_texture_request_t *req,
|
||||
bool (*should_abort)(void *ud), void *ud);
|
||||
|
||||
/* image_texture_load_request answering, from the bytes it read, whether
|
||||
* the file is an animated PNG: 1 yes, 0 a still, -1 not a PNG or no
|
||||
* decode. Spares a caller that keeps an animation's bytes a second
|
||||
* read of every still to ask. @png_probe may be NULL. */
|
||||
bool image_texture_load_request_ex(struct texture_image *img,
|
||||
const char *path, const image_texture_request_t *req,
|
||||
/* image_texture_load_buffer_request over bytes @keep, a data_transfer,
|
||||
* holds: an image that can go on reading them (a compressed texture)
|
||||
* takes @keep, frees it with itself and sets *@kept; *@png_probe, where
|
||||
* not NULL, says whether the bytes are an animated PNG - 1 yes, 0 a
|
||||
* still, -1 not a PNG or no decode. @keep and @kept may be NULL. */
|
||||
bool image_texture_load_buffer_probe(struct data_transfer *keep,
|
||||
bool *kept, struct texture_image *img,
|
||||
enum image_type_enum type, const void *buf, size_t len,
|
||||
const image_texture_request_t *req,
|
||||
bool (*should_abort)(void *ud), void *ud, int *png_probe);
|
||||
|
||||
/* Run @fn for every index below @n, across the cores at once where
|
||||
@@ -382,27 +385,14 @@ void image_texture_set_run_ex(unsigned n,
|
||||
void (*thread_done)(void));
|
||||
|
||||
/* Decode the @n files @paths name into @imgs together, each as
|
||||
* image_texture_load_request does with @req; a path that is NULL,
|
||||
* empty or unreadable leaves its image empty. Returns how many
|
||||
* decoded. In formats/image_texture_set.c. */
|
||||
* image_texture_load_request (<streams/image_texture_file.h>) does with
|
||||
* @req; a path that is NULL, empty or unreadable leaves its image
|
||||
* empty. Returns how many decoded. In formats/image_texture_set.c. */
|
||||
unsigned image_texture_load_set(const char *const *paths,
|
||||
struct texture_image *imgs, unsigned n,
|
||||
const image_texture_request_t *req);
|
||||
|
||||
/* ->pix10 is an ask on the way in and an answer on the way out: set
|
||||
* it before the call to have a decoder that can emit XRGB2101010 do
|
||||
* so, and read it after to find out whether it did. Clear on entry
|
||||
* means the ordinary 8-bit image, as before. */
|
||||
bool image_texture_load(struct texture_image *img, const char *path);
|
||||
|
||||
/* image_texture_load with an abort hook: both decode stages are
|
||||
* incremental (one chunk / one pass per step) and @should_abort is
|
||||
* asked between steps; returning true abandons the decode, which then
|
||||
* fails cleanly. For decodes on worker threads that must stop promptly
|
||||
* at shutdown or when their result is no longer wanted. @should_abort
|
||||
* may be NULL. */
|
||||
bool image_texture_load_ex(struct texture_image *img, const char *path,
|
||||
bool (*should_abort)(void *ud), void *ud);
|
||||
void image_texture_free(struct texture_image *img);
|
||||
|
||||
/* Force a CPU decode of a compressed texture_image into ->pixels (RGBA8).
|
||||
|
||||
@@ -261,9 +261,27 @@ uint64_t rflac_total_frames(const rflac_t *f);
|
||||
* is the common case: a table is optional, and a stream carried in a
|
||||
* container never has one. That is not a failure of the seek, only of
|
||||
* this shortcut, and the caller's fallback is the same as above.
|
||||
*
|
||||
* The frame the offset is at is not reported, and rflac_seek_resumed()
|
||||
* needs it: rflac_seek_point() gives both.
|
||||
*/
|
||||
int rflac_seek(rflac_t *f, uint64_t frame, uint64_t *byte_offset);
|
||||
|
||||
/**
|
||||
* rflac_seek_point:
|
||||
* @f : decoder
|
||||
* @frame : PCM frame to reach
|
||||
* @byte_offset: receives where the caller should resume input
|
||||
* @point_frame: receives the PCM frame that input starts at
|
||||
*
|
||||
* As rflac_seek(), and also the seek point's own frame (at or before
|
||||
* @frame), which is what rflac_seek_resumed() is to be told and where
|
||||
* output resumes: decoding forward @frame - @point_frame frames reaches
|
||||
* the target.
|
||||
*/
|
||||
int rflac_seek_point(rflac_t *f, uint64_t frame, uint64_t *byte_offset,
|
||||
uint64_t *point_frame);
|
||||
|
||||
/**
|
||||
* rflac_seek_resumed:
|
||||
* @f : decoder
|
||||
|
||||
@@ -62,6 +62,45 @@ int rtga_process_image(rtga_t *rtga, void **buf,
|
||||
|
||||
bool rtga_set_buf_ptr(rtga_t *rtga, void *data);
|
||||
|
||||
/* An image decoded straight to where its rows go, in one call, with
|
||||
* the decoder's memory kept for the next image.
|
||||
*
|
||||
* size the image's size, once the header is read, before any row;
|
||||
* false stops the decode (NULL: any size)
|
||||
* row where row y's width * 4 bytes are to be written: 4-byte
|
||||
* aligned memory of the caller's, or NULL for rtga's own row
|
||||
* (NULL: always rtga's)
|
||||
* done row y is written, at px - where row() said, or rtga's own
|
||||
* row, valid until the next row is begun (NULL: not told)
|
||||
*
|
||||
* Each row is asked for once and told done once, in the file's order:
|
||||
* bottom to top for a TGA stored bottom-up. y counts from the top.
|
||||
* Pixels are BGRA, or RGBA with @rgba, in byte order. A file that
|
||||
* ends early is finished as rtga_process_image() finishes it. */
|
||||
struct rtga_rows
|
||||
{
|
||||
bool (*size)(void *user, unsigned width, unsigned height);
|
||||
uint8_t *(*row)(void *user, unsigned y);
|
||||
void (*done)(void *user, unsigned y, const uint8_t *px);
|
||||
void *user;
|
||||
};
|
||||
|
||||
/**
|
||||
* rtga_decode_rows:
|
||||
* @data, @len : the whole file, read where it lies
|
||||
* @rgba : RGBA rather than BGRA
|
||||
* @rows : where the rows go
|
||||
* @width, @height : receive the size (0 if the header was not read)
|
||||
*
|
||||
* Abandons any rtga_process_image() decode on @rtga.
|
||||
*
|
||||
* Returns: true when every row was written; false for a header this
|
||||
* decoder does not take, or when @rows->size said no.
|
||||
*/
|
||||
bool rtga_decode_rows(rtga_t *rtga, const uint8_t *data, size_t len,
|
||||
bool rgba, const struct rtga_rows *rows,
|
||||
unsigned *width, unsigned *height);
|
||||
|
||||
void rtga_free(rtga_t *rtga);
|
||||
|
||||
rtga_t *rtga_alloc(void);
|
||||
|
||||
@@ -48,7 +48,18 @@
|
||||
* stage's gl_ClipDistance / gl_CullDistance (sized by the highest
|
||||
* constant element written; HLSL and MSL refuse them yet), in / out
|
||||
* interface blocks, RSLC_STAGE_GEOMETRY (SPIR-V and GLSL only), and
|
||||
* storage buffers with the atomic builtins (SPIR-V and GLSL only).
|
||||
* storage buffers with the atomic builtins (SPIR-V and GLSL only),
|
||||
* and what a generated stage says: #version 420 to 440 for the same
|
||||
* dialect, gl_WorkGroupSize, and a uniform block that says std430
|
||||
* (placed as glslang places it under GL_EXT_scalar_block_layout) or
|
||||
* ends in an array of unstated length
|
||||
* (GL_EXT_uniform_buffer_unsized_array; SPIR-V only), a block member
|
||||
* that says its layout(offset) (SPIR-V only),
|
||||
* gl_FragStencilRefARB (SPIR-V only), and one multisampled image read
|
||||
* and written a sample at a time: sampler2DMS and image2DMS with their
|
||||
* i and u forms (SPIR-V only), the two-channel and 16-bit image
|
||||
* formats, and a write-only image that leaves its format out (SPIR-V
|
||||
* only).
|
||||
* Without it
|
||||
* none of that is accepted, nothing is compiled in for it, and every
|
||||
* destination is word for word what it was. RSLC_NO_DXBC omits the
|
||||
@@ -94,7 +105,8 @@ enum rslc_base
|
||||
RSLC_BASE_BOOL,
|
||||
RSLC_BASE_INT,
|
||||
RSLC_BASE_UINT,
|
||||
RSLC_BASE_FLOAT
|
||||
RSLC_BASE_FLOAT,
|
||||
RSLC_BASE_DOUBLE /* RSLC_EXTENDED: a 64-bit scalar, never a constant; SPIR-V only */
|
||||
};
|
||||
|
||||
#define RSLC_MAX_SAMPLERS 16
|
||||
|
||||
@@ -290,6 +290,9 @@ struct rslc_var *rslc_b_io_block(struct rslc_builder *b, const struct rslc_b_qua
|
||||
const char *type_name, const char *instance,
|
||||
const struct rslc_type *const *types, const char *const *names,
|
||||
const unsigned *interps, const unsigned *mquals, uint32_t n, int32_t array_len);
|
||||
/* length() of an array: its declared length, or for the unsized last
|
||||
* member of a storage block what the buffer bound has (SPIR-V only). */
|
||||
uint32_t rslc_b_length(struct rslc_builder *b, uint32_t e);
|
||||
/* A layout on the stage's in or out alone: early_fragment_tests, a
|
||||
* local size, a geometry primitive, invocations or max_vertices. */
|
||||
bool rslc_b_layout(struct rslc_builder *b, const struct rslc_b_quals *q);
|
||||
|
||||
@@ -0,0 +1,293 @@
|
||||
/* Copyright (C) 2010-2026 The RetroArch team
|
||||
*
|
||||
* ---------------------------------------------------------------------------------------
|
||||
* The following license statement only applies to this file (retro_deque.h).
|
||||
* ---------------------------------------------------------------------------------------
|
||||
*
|
||||
* Permission is hereby granted, free of charge,
|
||||
* to any person obtaining a copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation the rights to
|
||||
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
|
||||
* and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
||||
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef __LIBRETRO_SDK_RETRO_DEQUE_H
|
||||
#define __LIBRETRO_SDK_RETRO_DEQUE_H
|
||||
|
||||
/*
|
||||
* retro_deque.h - a double-ended queue of one type, in one block
|
||||
*
|
||||
* What std::deque is used for - push at either end, pop at either end,
|
||||
* the n-th element, insert and erase in the middle, in order - without
|
||||
* what it costs: std::deque keeps its elements in blocks of a few dozen
|
||||
* and takes a block from the heap, or gives one back, each time its
|
||||
* contents cross a block's end. A queue that is pushed at one end and
|
||||
* popped at the other does that for ever, however few elements it holds.
|
||||
*
|
||||
* Here the elements are a ring in one block, a power of two long, set
|
||||
* aside when the queue is made for as many elements as the caller says it
|
||||
* holds. Pushing, popping, inserting and erasing never allocate and never
|
||||
* free. A push or insert into a queue that holds its limit is refused,
|
||||
* with one compare (the caller decides: drop, wait, or reserve more - the
|
||||
* one call besides init that allocates). Where the old code relied on a
|
||||
* queue with no limit, push_back_growing (and push_front_growing) reserves twice as much when the
|
||||
* limit is reached, and so allocates only then.
|
||||
*
|
||||
* The functions are made for the element's type with
|
||||
* RETRO_DEQUE_DEFINE(name, type), so an element is copied as what it is
|
||||
* (a compare-and-mask and a store for a word-sized one) and not through
|
||||
* memcpy with a size known only when the program runs:
|
||||
*
|
||||
* struct deadline { uint64_t when; void *who; };
|
||||
* RETRO_DEQUE_DEFINE(deadlines, struct deadline)
|
||||
*
|
||||
* deadlines_t q;
|
||||
* deadlines_init(&q, 60); (holds 60, in a block of 64)
|
||||
* deadlines_push_back(&q, &d); (false when full)
|
||||
* deadlines_insert(&q, 3, &d); (before the 4th oldest)
|
||||
* d = *deadlines_front(&q);
|
||||
* deadlines_pop_front(&q);
|
||||
* deadlines_erase(&q, i);
|
||||
* deadlines_free(&q);
|
||||
*
|
||||
* Insert and erase move whichever side of the place is shorter, so the
|
||||
* ends cost one element and the middle half the queue.
|
||||
*
|
||||
* The type has to be one C copies by assignment (anything C89 has; in
|
||||
* C++, a trivially copyable type). Not thread safe: one thread, or the
|
||||
* caller's own lock. C89; MSVC compiles it.
|
||||
*
|
||||
* The allocator is malloc and free unless RETRO_DEQUE_MALLOC and
|
||||
* RETRO_DEQUE_FREE are defined before this header is included.
|
||||
*/
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <boolean.h>
|
||||
#include <retro_inline.h>
|
||||
|
||||
#ifndef RETRO_DEQUE_MALLOC
|
||||
#define RETRO_DEQUE_MALLOC(bytes) malloc(bytes)
|
||||
#endif
|
||||
#ifndef RETRO_DEQUE_FREE
|
||||
#define RETRO_DEQUE_FREE(ptr) free(ptr)
|
||||
#endif
|
||||
|
||||
/* C's casts in C, C++'s in C++: a C++ unit that warns of old-style casts
|
||||
* (or refuses them) includes this as it is. */
|
||||
#ifdef __cplusplus
|
||||
#define RETRO_DEQUE_CAST(to, value) static_cast<to>(value)
|
||||
#else
|
||||
#define RETRO_DEQUE_CAST(to, value) ((to)(value))
|
||||
#endif
|
||||
|
||||
#define RETRO_DEQUE_DEFINE(name, type) \
|
||||
/* The element's type by one name, so that "const" in front of it means \
|
||||
* the element whatever the type is spelled as: with a pointer type \
|
||||
* written out, "const type *" would be a pointer to a pointer to const. */ \
|
||||
typedef type name##_value_t; \
|
||||
typedef struct name \
|
||||
{ \
|
||||
name##_value_t *data; /* mask + 1 elements */ \
|
||||
size_t mask; /* the block's length, a power of two, less one */ \
|
||||
size_t first; /* where the oldest is */ \
|
||||
size_t count; \
|
||||
size_t limit; /* how many it holds: what init or reserve was asked for; \
|
||||
* 0 while it has no block */ \
|
||||
} name##_t; \
|
||||
\
|
||||
/* A queue that holds @limit elements, no more (its block is the next \
|
||||
* power of two). False when there is no memory; the queue then holds \
|
||||
* nothing and refuses every push. */ \
|
||||
static INLINE bool name##_init(name##_t *q, size_t limit) \
|
||||
{ \
|
||||
size_t capacity = 1; \
|
||||
while (capacity < limit && capacity < RETRO_DEQUE_CAST(size_t, -1) / 2 / sizeof(name##_value_t)) \
|
||||
capacity <<= 1; \
|
||||
q->first = 0; \
|
||||
q->count = 0; \
|
||||
q->mask = 0; \
|
||||
q->limit = 0; \
|
||||
q->data = capacity >= limit ? RETRO_DEQUE_CAST(name##_value_t*, RETRO_DEQUE_MALLOC(capacity * sizeof(name##_value_t))) : NULL; \
|
||||
if (!q->data) \
|
||||
return false; \
|
||||
q->mask = capacity - 1; \
|
||||
q->limit = limit; \
|
||||
return true; \
|
||||
} \
|
||||
\
|
||||
/* Safe on a queue whose init failed, and on one already freed */ \
|
||||
static INLINE void name##_free(name##_t *q) \
|
||||
{ \
|
||||
if (q->data) \
|
||||
RETRO_DEQUE_FREE(q->data); \
|
||||
q->data = NULL; \
|
||||
q->mask = 0; \
|
||||
q->first = 0; \
|
||||
q->count = 0; \
|
||||
q->limit = 0; \
|
||||
} \
|
||||
\
|
||||
static INLINE size_t name##_count(const name##_t *q) { return q->count; } \
|
||||
static INLINE bool name##_empty(const name##_t *q) { return q->count == 0; } \
|
||||
static INLINE size_t name##_limit(const name##_t *q) { return q->limit; } \
|
||||
static INLINE bool name##_full(const name##_t *q) { return q->count >= q->limit; } \
|
||||
\
|
||||
/* The @i-th oldest; @i below the count */ \
|
||||
static INLINE name##_value_t *name##_at(const name##_t *q, size_t i) \
|
||||
{ \
|
||||
return &q->data[(q->first + i) & q->mask]; \
|
||||
} \
|
||||
\
|
||||
static INLINE name##_value_t *name##_front(const name##_t *q) { return &q->data[q->first]; } \
|
||||
static INLINE name##_value_t *name##_back(const name##_t *q) \
|
||||
{ \
|
||||
return &q->data[(q->first + q->count - 1) & q->mask]; \
|
||||
} \
|
||||
\
|
||||
/* Holds @limit from now on (never fewer than it holds already): the \
|
||||
* elements move to a larger block when that takes one, in order. False \
|
||||
* when there is no memory, the queue then as it was. Pointers into the \
|
||||
* queue are invalid afterwards. */ \
|
||||
static INLINE bool name##_reserve(name##_t *q, size_t limit) \
|
||||
{ \
|
||||
name##_t larger; \
|
||||
size_t i; \
|
||||
if (limit <= q->limit) \
|
||||
return true; \
|
||||
if (q->data && limit <= q->mask + 1) \
|
||||
{ \
|
||||
q->limit = limit; \
|
||||
return true; \
|
||||
} \
|
||||
if (!name##_init(&larger, limit)) \
|
||||
return false; \
|
||||
for (i = 0; i < q->count; i++) \
|
||||
larger.data[i] = *name##_at(q, i); \
|
||||
larger.count = q->count; \
|
||||
if (q->data) \
|
||||
RETRO_DEQUE_FREE(q->data); \
|
||||
*q = larger; \
|
||||
return true; \
|
||||
} \
|
||||
\
|
||||
/* False when the queue is full: nothing changed */ \
|
||||
static INLINE bool name##_push_back(name##_t *q, const name##_value_t *value) \
|
||||
{ \
|
||||
if (name##_full(q)) \
|
||||
return false; \
|
||||
q->data[(q->first + q->count) & q->mask] = *value; \
|
||||
q->count++; \
|
||||
return true; \
|
||||
} \
|
||||
\
|
||||
/* For a queue with no limit of its own (what a std::deque was used as): \
|
||||
* when it holds its limit, it is first made to hold twice as many - \
|
||||
* that push allocates, the others do not. Give it at init the most it \
|
||||
* is expected to hold, and it never will. False only when there is no \
|
||||
* memory: nothing changed. */ \
|
||||
static INLINE bool name##_push_back_growing(name##_t *q, const name##_value_t *value) \
|
||||
{ \
|
||||
if (name##_full(q) && !name##_reserve(q, q->limit ? q->limit * 2 : 8)) \
|
||||
return false; \
|
||||
q->data[(q->first + q->count) & q->mask] = *value; \
|
||||
q->count++; \
|
||||
return true; \
|
||||
} \
|
||||
\
|
||||
static INLINE bool name##_push_front(name##_t *q, const name##_value_t *value) \
|
||||
{ \
|
||||
if (name##_full(q)) \
|
||||
return false; \
|
||||
q->first = (q->first - 1) & q->mask; \
|
||||
q->data[q->first] = *value; \
|
||||
q->count++; \
|
||||
return true; \
|
||||
} \
|
||||
\
|
||||
/* push_front for a queue with no limit of its own, as push_back_growing */ \
|
||||
static INLINE bool name##_push_front_growing(name##_t *q, const name##_value_t *value) \
|
||||
{ \
|
||||
if (name##_full(q) && !name##_reserve(q, q->limit ? q->limit * 2 : 8)) \
|
||||
return false; \
|
||||
q->first = (q->first - 1) & q->mask; \
|
||||
q->data[q->first] = *value; \
|
||||
q->count++; \
|
||||
return true; \
|
||||
} \
|
||||
\
|
||||
/* The queue is not empty */ \
|
||||
static INLINE void name##_pop_front(name##_t *q) \
|
||||
{ \
|
||||
q->first = (q->first + 1) & q->mask; \
|
||||
q->count--; \
|
||||
} \
|
||||
\
|
||||
static INLINE void name##_pop_back(name##_t *q) \
|
||||
{ \
|
||||
q->count--; \
|
||||
} \
|
||||
\
|
||||
/* @value before the @i-th oldest (@i up to the count: at the end). \
|
||||
* False when the queue is full: nothing changed */ \
|
||||
static INLINE bool name##_insert(name##_t *q, size_t i, const name##_value_t *value) \
|
||||
{ \
|
||||
size_t k; \
|
||||
if (name##_full(q) || i > q->count) \
|
||||
return false; \
|
||||
if (i < q->count - i) \
|
||||
{ \
|
||||
/* the older side is the shorter: it moves one place down */ \
|
||||
q->first = (q->first - 1) & q->mask; \
|
||||
for (k = 0; k < i; k++) \
|
||||
q->data[(q->first + k) & q->mask] = q->data[(q->first + k + 1) & q->mask]; \
|
||||
} \
|
||||
else \
|
||||
{ \
|
||||
for (k = q->count; k > i; k--) \
|
||||
q->data[(q->first + k) & q->mask] = q->data[(q->first + k - 1) & q->mask]; \
|
||||
} \
|
||||
q->data[(q->first + i) & q->mask] = *value; \
|
||||
q->count++; \
|
||||
return true; \
|
||||
} \
|
||||
\
|
||||
/* The @i-th oldest goes; @i below the count */ \
|
||||
static INLINE void name##_erase(name##_t *q, size_t i) \
|
||||
{ \
|
||||
size_t k; \
|
||||
if (i < q->count - 1 - i) \
|
||||
{ \
|
||||
for (k = i; k > 0; k--) \
|
||||
q->data[(q->first + k) & q->mask] = q->data[(q->first + k - 1) & q->mask]; \
|
||||
q->first = (q->first + 1) & q->mask; \
|
||||
} \
|
||||
else \
|
||||
{ \
|
||||
for (k = i; k + 1 < q->count; k++) \
|
||||
q->data[(q->first + k) & q->mask] = q->data[(q->first + k + 1) & q->mask]; \
|
||||
} \
|
||||
q->count--; \
|
||||
} \
|
||||
\
|
||||
static INLINE void name##_clear(name##_t *q) \
|
||||
{ \
|
||||
q->first = 0; \
|
||||
q->count = 0; \
|
||||
} \
|
||||
\
|
||||
/* So that a use of the macro ends in a semicolon, or not, as the caller \
|
||||
* likes */ \
|
||||
typedef name##_t name##_deque_defined_t
|
||||
|
||||
#endif
|
||||
@@ -60,6 +60,15 @@
|
||||
#define _DEFAULT_SOURCE 1
|
||||
#endif
|
||||
|
||||
/* glibc before 2.19 knows only these spellings (webOS ships 2.12);
|
||||
* later ones accept them silently next to _DEFAULT_SOURCE. */
|
||||
#ifndef _BSD_SOURCE
|
||||
#define _BSD_SOURCE 1
|
||||
#endif
|
||||
#ifndef _SVID_SOURCE
|
||||
#define _SVID_SOURCE 1
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,68 @@
|
||||
/* Copyright (C) 2010-2026 The RetroArch team
|
||||
*
|
||||
* ---------------------------------------------------------------------------------------
|
||||
* The following license statement only applies to this file (cdfs_file.h).
|
||||
* ---------------------------------------------------------------------------------------
|
||||
*
|
||||
* Permission is hereby granted, free of charge,
|
||||
* to any person obtaining a copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation the rights to
|
||||
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
|
||||
* and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
||||
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef __LIBRETRO_SDK_STREAMS_CDFS_FILE_H__
|
||||
#define __LIBRETRO_SDK_STREAMS_CDFS_FILE_H__
|
||||
|
||||
#include <retro_common_api.h>
|
||||
#include <formats/cdfs.h>
|
||||
|
||||
RETRO_BEGIN_DECLS
|
||||
|
||||
/* cdfs tracks opened by filename: the path is resolved and the file
|
||||
* opened through intfstream, then handed to cdfs_track_from_stream.
|
||||
* Close the track with cdfs_close_track. */
|
||||
|
||||
/* opens the specified track in a CD or virtual CD file - the resulting stream should be passed to
|
||||
* cdfs_open_file to get access to a file within the CD.
|
||||
*
|
||||
* supported files:
|
||||
* real CD - path will be in the form "cdrom://drive1.cue" or "cdrom://d:/drive.cue"
|
||||
* bin/cue - path will point to the cue file
|
||||
* chd - path will point to the chd file
|
||||
*
|
||||
* for bin/cue files, the following storage modes are supported:
|
||||
* MODE2/2352
|
||||
* MODE1/2352
|
||||
* MODE1/2048 - untested
|
||||
* MODE2/2336 - untested
|
||||
*/
|
||||
cdfs_track_t* cdfs_open_track(const char* path, unsigned int track_index);
|
||||
|
||||
/* opens the first data track in a CD or virtual CD file. see cdfs_open_track for supported file formats
|
||||
*/
|
||||
cdfs_track_t* cdfs_open_data_track(const char* path);
|
||||
|
||||
/* opens a raw track file for a CD or virtual CD.
|
||||
*
|
||||
* supported files:
|
||||
* real CD - path will be in the form "cdrom://drive1-track01.bin" or "cdrom://d:/drive-track01.bin"
|
||||
* BIN handles obtain their own validated geometry.
|
||||
* bin - path will point to the bin file
|
||||
* iso - path will point to the iso file
|
||||
*/
|
||||
cdfs_track_t* cdfs_open_raw_track(const char* path);
|
||||
|
||||
RETRO_END_DECLS
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,48 @@
|
||||
/* Copyright (C) 2010-2026 The RetroArch team
|
||||
*
|
||||
* ---------------------------------------------------------------------------------------
|
||||
* The following license statement only applies to this file (data_transfer_file.h).
|
||||
* ---------------------------------------------------------------------------------------
|
||||
*
|
||||
* Permission is hereby granted, free of charge,
|
||||
* to any person obtaining a copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation the rights to
|
||||
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
|
||||
* and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
||||
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef __LIBRETRO_SDK_STREAMS_DATA_TRANSFER_FILE_H__
|
||||
#define __LIBRETRO_SDK_STREAMS_DATA_TRANSFER_FILE_H__
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#include <retro_common_api.h>
|
||||
#include <formats/data_transfer.h>
|
||||
|
||||
RETRO_BEGIN_DECLS
|
||||
|
||||
/* data_transfer over a file opened by path through filestream, so
|
||||
* 64-bit offsets and VFS-only paths (archive members, Android
|
||||
* content:// documents, frontend overrides) work like any other.
|
||||
* The transfer owns the file and closes it when freed. */
|
||||
|
||||
/* data_transfer_open_prefix_reader over the file at @path */
|
||||
data_transfer_t *data_transfer_open_prefix(const char *path,
|
||||
size_t commit_cap);
|
||||
|
||||
/* data_transfer_open_window_reader over the file at @path */
|
||||
data_transfer_t *data_transfer_open_window(const char *path, size_t keep);
|
||||
|
||||
RETRO_END_DECLS
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,68 @@
|
||||
/* Copyright (C) 2010-2026 The RetroArch team
|
||||
*
|
||||
* ---------------------------------------------------------------------------------------
|
||||
* The following license statement only applies to this file (image_texture_file.h).
|
||||
* ---------------------------------------------------------------------------------------
|
||||
*
|
||||
* Permission is hereby granted, free of charge,
|
||||
* to any person obtaining a copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation the rights to
|
||||
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
|
||||
* and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
||||
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef __LIBRETRO_SDK_STREAMS_IMAGE_TEXTURE_FILE_H__
|
||||
#define __LIBRETRO_SDK_STREAMS_IMAGE_TEXTURE_FILE_H__
|
||||
|
||||
#include <retro_common_api.h>
|
||||
#include <boolean.h>
|
||||
#include <formats/image.h>
|
||||
|
||||
RETRO_BEGIN_DECLS
|
||||
|
||||
/* Images loaded by path: the file read whole through
|
||||
* data_transfer_open_prefix - filestream/VFS routing, so overlays and
|
||||
* driver assets from archive members or content:// documents load like
|
||||
* any other, 64-bit lengths, honest short reads - and decoded by
|
||||
* image_texture_load_buffer_probe. */
|
||||
|
||||
/* ->pix10 is an ask on the way in and an answer on the way out: set
|
||||
* it before the call to have a decoder that can emit XRGB2101010 do
|
||||
* so, and read it after to find out whether it did. Clear on entry
|
||||
* means the ordinary 8-bit image, as before. */
|
||||
bool image_texture_load(struct texture_image *img, const char *path);
|
||||
|
||||
/* image_texture_load with an abort hook: both decode stages are
|
||||
* incremental (one chunk / one pass per step) and @should_abort is
|
||||
* asked between steps; returning true abandons the decode, which then
|
||||
* fails cleanly. For decodes on worker threads that must stop promptly
|
||||
* at shutdown or when their result is no longer wanted. @should_abort
|
||||
* may be NULL. */
|
||||
bool image_texture_load_ex(struct texture_image *img, const char *path,
|
||||
bool (*should_abort)(void *ud), void *ud);
|
||||
|
||||
bool image_texture_load_request(struct texture_image *img,
|
||||
const char *path, const image_texture_request_t *req,
|
||||
bool (*should_abort)(void *ud), void *ud);
|
||||
|
||||
/* image_texture_load_request answering, from the bytes it read, whether
|
||||
* the file is an animated PNG: 1 yes, 0 a still, -1 not a PNG or no
|
||||
* decode. Spares a caller that keeps an animation's bytes a second
|
||||
* read of every still to ask. @png_probe may be NULL. */
|
||||
bool image_texture_load_request_ex(struct texture_image *img,
|
||||
const char *path, const image_texture_request_t *req,
|
||||
bool (*should_abort)(void *ud), void *ud, int *png_probe);
|
||||
|
||||
RETRO_END_DECLS
|
||||
|
||||
#endif
|
||||
+1
-1
@@ -2,7 +2,7 @@
|
||||
#ifndef LIBRETRO_RDVD_STREAM_H
|
||||
#define LIBRETRO_RDVD_STREAM_H
|
||||
#include <formats/rdvd_demux.h>
|
||||
#include <formats/rdvd_vfs.h>
|
||||
#include <streams/rdvd_vfs.h>
|
||||
RETRO_BEGIN_DECLS
|
||||
enum rdvd_stream_status
|
||||
{
|
||||
File renamed without changes.
@@ -187,6 +187,9 @@ struct libretro_vfs_implementation_file
|
||||
#endif
|
||||
{
|
||||
int64_t size;
|
||||
/* where a stdio stream stands, kept here so a write need not ask
|
||||
* stdio to keep size; -1 when not known, and stdio is asked */
|
||||
int64_t stdio_pos;
|
||||
uint64_t mappos;
|
||||
uint64_t mapsize;
|
||||
#if defined(_WIN32) && !defined(_XBOX) && !defined(HAVE_MMAP)
|
||||
@@ -199,6 +202,14 @@ struct libretro_vfs_implementation_file
|
||||
HANDLE fh;
|
||||
/* CreateFileMapping handle backing `mapped`; NULL when unmapped */
|
||||
HANDLE map_handle;
|
||||
/* A stream on a handle of its own (not VFS_WIN32_NO_NATIVE): its
|
||||
* position, the part of the file `buf` holds - read from it, or, with
|
||||
* ndirty set, written to it and not yet to the file - and an error */
|
||||
uint64_t npos;
|
||||
uint64_t nbuf_at;
|
||||
size_t nbuf_len;
|
||||
int nerr;
|
||||
int ndirty;
|
||||
#if !defined(_XBOX) && !defined(HAVE_MMAP)
|
||||
void *warm; /* mapped read-ahead thread, NULL until used */
|
||||
#endif
|
||||
@@ -247,6 +258,17 @@ struct libretro_vfs_implementation_file
|
||||
retro_atomic_size_t append_end;
|
||||
#ifdef VFS_HAVE_AT_HANDLE
|
||||
retro_atomic_ptr_t at_handle;
|
||||
/* at_handle is synchronous - Windows' cached-read fast path - and
|
||||
* held by one call at a time, at_sync_busy saying so; a call that
|
||||
* finds it held takes at_ovl, the same file opened for overlapped
|
||||
* I/O, whose calls Windows runs side by side. Each overlapped call
|
||||
* waits on an event of its own: one of at_ev, claimed by its bit in
|
||||
* at_ev_busy and made at first use, or a passing one when all are in
|
||||
* use. A native stream's own handle serves as its at_handle. */
|
||||
retro_atomic_ptr_t at_ovl;
|
||||
HANDLE at_ev[8];
|
||||
retro_atomic_int_t at_ev_busy;
|
||||
retro_atomic_int_t at_sync_busy;
|
||||
#endif
|
||||
};
|
||||
#endif
|
||||
|
||||
@@ -2822,8 +2822,14 @@ static void retro_task_threaded_gather(void)
|
||||
* running or parked, or one finished and not yet retired. */
|
||||
static bool task_threaded_busy(void)
|
||||
{
|
||||
/* A finishing task counts itself finished before it stops counting
|
||||
* as running, and goes on the finished stack only after both; the
|
||||
* gather moves it to the retire list before it stops counting as
|
||||
* finished. The count, not the stack, so a finish between the
|
||||
* running count and the stack is never seen as nothing at all. */
|
||||
return retro_atomic_load_acquire_int(&tasks_running_count)
|
||||
> retro_atomic_load_acquire_int(&tasks_scheduled_count)
|
||||
|| retro_atomic_load_acquire_int(&tasks_finished_count)
|
||||
|| retro_atomic_load_acquire_ptr(&task_finished_stack)
|
||||
|| task_retire.front;
|
||||
}
|
||||
|
||||
@@ -58,6 +58,8 @@ static uint8_t *data;
|
||||
static uint8_t *frame;
|
||||
static size_t frame_len;
|
||||
static uint64_t read_octets;
|
||||
static unsigned read_at_calls;
|
||||
static bool positional; /* the VFS offers v6 reads at offsets */
|
||||
|
||||
static void fail(const char *what, const char *detail)
|
||||
{
|
||||
@@ -81,6 +83,17 @@ static int64_t count_read(struct retro_vfs_file_handle *h, void *s,
|
||||
return r;
|
||||
}
|
||||
|
||||
static int64_t count_read_at(struct retro_vfs_file_handle *h, void *s,
|
||||
uint64_t len, int64_t offset)
|
||||
{
|
||||
int64_t r = retro_vfs_file_read_at_impl(
|
||||
(libretro_vfs_implementation_file*)h, s, len, offset);
|
||||
read_at_calls++;
|
||||
if (r > 0)
|
||||
read_octets += (uint64_t)r;
|
||||
return r;
|
||||
}
|
||||
|
||||
static void install_vfs(bool lend)
|
||||
{
|
||||
static struct retro_vfs_interface iface;
|
||||
@@ -99,10 +112,16 @@ static void install_vfs(bool lend)
|
||||
iface.remove = retro_vfs_file_remove_impl;
|
||||
iface.rename = retro_vfs_file_rename_impl;
|
||||
info.required_interface_version = FILESTREAM_REQUIRED_VFS_VERSION;
|
||||
if (positional)
|
||||
{
|
||||
iface.read_at = count_read_at;
|
||||
info.required_interface_version = 6;
|
||||
}
|
||||
info.iface = &iface;
|
||||
filestream_vfs_init(&info);
|
||||
filestream_set_mapped_ptr_cb(lend ? lend_mapped_ptr : NULL);
|
||||
read_octets = 0;
|
||||
read_octets = 0;
|
||||
read_at_calls = 0;
|
||||
}
|
||||
|
||||
static int extract_cb(const char *name, const char *valid_exts,
|
||||
@@ -280,11 +299,20 @@ int main(void)
|
||||
return 2;
|
||||
fclose(f);
|
||||
|
||||
/* without v6 an extract's blocks are sought and read; with it, read
|
||||
* at their offsets */
|
||||
check_read("read, plain file", false);
|
||||
check_read("read, lent", true);
|
||||
check_extract("extract, plain file", false);
|
||||
check_extract("extract, lent", true);
|
||||
check_window();
|
||||
positional = true;
|
||||
check_read("read at offsets, plain file", false);
|
||||
check_extract("extract at offsets, plain file", false);
|
||||
if (!read_at_calls)
|
||||
fail("extract at offsets, plain file", "no read at an offset");
|
||||
check_window();
|
||||
positional = false;
|
||||
install_vfs(false);
|
||||
if ( file_archive_get_file_crc32(ZST_PATH "#archive_zst_test.bin")
|
||||
!= encoding_crc32(0, data, DATA_LEN))
|
||||
|
||||
@@ -19,6 +19,11 @@ TARGET_TEST15 := vfs_positional_nommap_test
|
||||
TARGET_TEST16 := vfs_positional_off32_test
|
||||
TARGET_TEST17 := vfs_positional_pathreopen_test
|
||||
TARGET_TEST18 := vfs_positional_ntverify_test
|
||||
TARGET_TEST19 := vfs_positional_wide_test
|
||||
TARGET_TEST20 := vfs_seek_read_mix_test
|
||||
TARGET_TEST21 := vfs_write_mix_test
|
||||
BENCH_CRT := vfs_native_bench
|
||||
BENCH_NATIVE := vfs_native_bench_native
|
||||
|
||||
LIBRETRO_COMM_DIR := ../../..
|
||||
|
||||
@@ -122,6 +127,11 @@ endif
|
||||
ifeq ($(MMAP),1)
|
||||
CFLAGS += -DHAVE_MMAP
|
||||
endif
|
||||
# As RetroArch's Makefile builds Linux: what glibc declares only under it
|
||||
# (fallocate's hole punching) is what a frontend runs
|
||||
ifeq ($(UNAME_S),Linux)
|
||||
CFLAGS += -D_GNU_SOURCE
|
||||
endif
|
||||
|
||||
# vfs_bulk_read_test asserts which I/O path an open lands on, and the
|
||||
# interesting configuration - descriptor I/O with no file mapping - is
|
||||
@@ -134,6 +144,14 @@ ifeq ($(DESCRIPTOR_IO),1)
|
||||
CFLAGS += -DVFS_HAVE_DESCRIPTOR_IO=1
|
||||
endif
|
||||
|
||||
# 'make VFS_NATIVE=0' builds the suite with Windows streams on the C
|
||||
# runtime (VFS_WIN32_NO_NATIVE) rather than a handle of their own;
|
||||
# elsewhere it changes nothing.
|
||||
VFS_NATIVE ?=
|
||||
ifeq ($(VFS_NATIVE),0)
|
||||
CFLAGS += -DVFS_WIN32_NO_NATIVE
|
||||
endif
|
||||
|
||||
# FAKE_CFR=1 stands in a copy_file_range that copies to EOF whatever
|
||||
# len it was given (what gVisor was observed doing); FAKE_CFR=2 one
|
||||
# that copies three times len. Both exercise vfs_v5_metadata_test's
|
||||
@@ -166,7 +184,8 @@ all: $(TARGET) $(TARGET_TEST) $(TARGET_TEST2) $(TARGET_TEST3) $(TARGET_TEST4) \
|
||||
$(TARGET_TEST5) $(TARGET_TEST6) $(TARGET_TEST8) $(TARGET_TEST9) \
|
||||
$(TARGET_TEST10) $(TARGET_TEST11) $(TARGET_TEST12) $(TARGET_TEST13) \
|
||||
$(TARGET_TEST14) $(TARGET_TEST15) $(TARGET_TEST16) $(TARGET_TEST17) \
|
||||
$(TARGET_TEST18) $(RENAME_TARGET)
|
||||
$(TARGET_TEST18) $(TARGET_TEST19) $(TARGET_TEST20) $(TARGET_TEST21) $(BENCH_CRT) $(BENCH_NATIVE) \
|
||||
$(RENAME_TARGET)
|
||||
|
||||
%.o: %.c
|
||||
$(CC) -c -o $@ $< $(CFLAGS)
|
||||
@@ -248,6 +267,39 @@ $(NTVERIFY_VFS_OBJ): $(LIBRETRO_COMM_DIR)/vfs/vfs_implementation.c
|
||||
$(TARGET_TEST18): vfs_positional_required.o $(NTVERIFY_OBJS)
|
||||
$(CC) -o $@ $^ $(LDFLAGS) -lpthread
|
||||
|
||||
# vfs_positional_wide_test: the positional test from twelve threads
|
||||
vfs_positional_wide.o: vfs_positional_test.c
|
||||
$(CC) -c -o $@ $< $(CFLAGS) -DVFS_POSITIONAL_TEST_REQUIRED -DTHREADS=12
|
||||
|
||||
$(TARGET_TEST19): vfs_positional_wide.o $(COMMON_OBJS)
|
||||
$(CC) -o $@ $^ $(LDFLAGS) -lpthread
|
||||
|
||||
$(TARGET_TEST20): vfs_seek_read_mix_test.o $(COMMON_OBJS)
|
||||
$(CC) -o $@ $^ $(LDFLAGS)
|
||||
|
||||
$(TARGET_TEST21): vfs_write_mix_test.o $(COMMON_OBJS)
|
||||
$(CC) -o $@ $^ $(LDFLAGS)
|
||||
|
||||
# vfs_native_bench / vfs_native_bench_native: read-only stream timings
|
||||
# through the C runtime (VFS_WIN32_NO_NATIVE), and through the stream's
|
||||
# own handle - run both on Windows and compare. Elsewhere the two are
|
||||
# the same build.
|
||||
CRT_VFS_OBJ := vfs_implementation_crt.o
|
||||
BENCH_OBJS := $(filter-out %/vfs_implementation.o,$(COMMON_OBJS)) \
|
||||
rthreads_ra.o retro_eventcount_ra.o
|
||||
|
||||
$(CRT_VFS_OBJ): $(LIBRETRO_COMM_DIR)/vfs/vfs_implementation.c
|
||||
$(CC) -c -o $@ $< $(CFLAGS) -DVFS_WIN32_NO_NATIVE
|
||||
|
||||
vfs_native_bench_crt.o: vfs_native_bench.c
|
||||
$(CC) -c -o $@ $< $(CFLAGS) -DVFS_WIN32_NO_NATIVE
|
||||
|
||||
$(BENCH_CRT): vfs_native_bench_crt.o $(BENCH_OBJS) $(CRT_VFS_OBJ)
|
||||
$(CC) -o $@ $^ $(LDFLAGS) $(RA_LIBS) -lpthread
|
||||
|
||||
$(BENCH_NATIVE): vfs_native_bench.o $(COMMON_OBJS) rthreads_ra.o retro_eventcount_ra.o
|
||||
$(CC) -o $@ $^ $(LDFLAGS) $(RA_LIBS) -lpthread
|
||||
|
||||
$(TARGET_TEST10): vfs_view_test.o $(VIEW_OBJS)
|
||||
$(CC) -o $@ $^ $(LDFLAGS)
|
||||
|
||||
@@ -326,7 +378,11 @@ clean:
|
||||
rm -f $(TARGET) $(TARGET_TEST) $(TARGET_TEST2) $(TARGET_TEST3) \
|
||||
$(TARGET_TEST4) $(TARGET_TEST5) $(TARGET_TEST6) \
|
||||
$(TARGET_TEST7) $(TARGET_TEST8) $(TARGET_TEST9) $(TARGET_TEST10) \
|
||||
$(TARGET_TEST11) $(TARGET_TEST12) $(TARGET_TEST13) $(TARGET_TEST14) $(TARGET_TEST15) $(TARGET_TEST16) $(TARGET_TEST17) $(TARGET_TEST18) $(CLOSE_VFS_OBJ) \
|
||||
$(TARGET_TEST11) $(TARGET_TEST12) $(TARGET_TEST13) $(TARGET_TEST14) $(TARGET_TEST15) $(TARGET_TEST16) $(TARGET_TEST17) $(TARGET_TEST18) $(TARGET_TEST19) \
|
||||
$(TARGET_TEST20) vfs_seek_read_mix_test.o \
|
||||
$(TARGET_TEST21) vfs_write_mix_test.o \
|
||||
$(BENCH_CRT) $(BENCH_NATIVE) vfs_positional_wide.o \
|
||||
vfs_native_bench.o vfs_native_bench_crt.o $(CRT_VFS_OBJ) $(CLOSE_VFS_OBJ) \
|
||||
$(OBJS) $(FAULT_FS_OBJ) $(RENAME_VFS_OBJ) \
|
||||
$(RA_VFS_OBJ) $(HOOK_VFS_OBJ) rthreads_ra.o retro_eventcount_ra.o \
|
||||
$(NOMMAP_VFS_OBJ) vfs_positional_required.o $(OFF32_VFS_OBJ) \
|
||||
|
||||
@@ -271,16 +271,38 @@ static void test_path_selection(void)
|
||||
retro_vfs_file_close_impl(s);
|
||||
}
|
||||
|
||||
/* No hint: unchanged, buffered. */
|
||||
/* No hint: unchanged, buffered - on a handle of its own where
|
||||
* Windows takes read-only streams off the C runtime. */
|
||||
s = retro_vfs_file_open_impl(path, RETRO_VFS_FILE_ACCESS_READ,
|
||||
RETRO_VFS_FILE_ACCESS_HINT_NONE);
|
||||
CHECK(s != NULL, "plain open failed");
|
||||
if (s)
|
||||
{
|
||||
#if !defined(VFS_WIN32_NO_NATIVE) && defined(_WIN32) && !defined(_XBOX)
|
||||
CHECK(s->fp == NULL && s->fh != NULL,
|
||||
"plain read-only open is not on its own handle");
|
||||
#else
|
||||
CHECK(s->fp != NULL, "plain open left stdio");
|
||||
#endif
|
||||
retro_vfs_file_close_impl(s);
|
||||
}
|
||||
|
||||
/* ... and so do write streams */
|
||||
s = retro_vfs_file_open_impl("bulk_select_pw.bin",
|
||||
RETRO_VFS_FILE_ACCESS_WRITE, RETRO_VFS_FILE_ACCESS_HINT_NONE);
|
||||
CHECK(s != NULL, "plain write open failed");
|
||||
if (s)
|
||||
{
|
||||
#if !defined(VFS_WIN32_NO_NATIVE) && defined(_WIN32) && !defined(_XBOX)
|
||||
CHECK(s->fp == NULL && s->fh != NULL,
|
||||
"plain write open is not on its own handle");
|
||||
#else
|
||||
CHECK(s->fp != NULL, "plain write open left stdio");
|
||||
#endif
|
||||
retro_vfs_file_close_impl(s);
|
||||
remove("bulk_select_pw.bin");
|
||||
}
|
||||
|
||||
/* Write modes take the descriptor path where the platform lists
|
||||
* descriptor writes, and stay buffered where it does not. */
|
||||
s = retro_vfs_file_open_impl("bulk_select_w.bin",
|
||||
|
||||
@@ -0,0 +1,408 @@
|
||||
/* Copyright (C) 2010-2026 The RetroArch team
|
||||
*
|
||||
* ---------------------------------------------------------------------------------------
|
||||
* The following license statement only applies to this file (vfs_native_bench.c).
|
||||
* ---------------------------------------------------------------------------------------
|
||||
*
|
||||
* Permission is hereby granted, free of charge,
|
||||
* to any person obtaining a copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation the rights to
|
||||
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
|
||||
* and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
||||
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
/* Stream throughput through filestream: the C runtime path
|
||||
* (vfs_native_bench, VFS_WIN32_NO_NATIVE) against the stream's own
|
||||
* handle (vfs_native_bench_native), the same source built twice. On
|
||||
* anything but Windows the two are the same build.
|
||||
*
|
||||
* sequential reads of 1, 16, 256, 4096 and 65536 bytes, and 1 MiB
|
||||
* 4 KiB at random offsets by seek and read, and by filestream_read_at
|
||||
* 4 KiB at random offsets by filestream_read_at from 4 threads
|
||||
* open, read 4 KiB, close, over 1000 small files
|
||||
* sequential writes of 16, 256, 4096 and 65536 bytes, and 1 MiB, to a
|
||||
* new file, its close included
|
||||
* 64 bytes written at random offsets of a 4 MiB file by seek and write
|
||||
* create, write 4 KiB, close, over 1000 small files
|
||||
*
|
||||
* Each case runs five times and reports the best, in ns per call and
|
||||
* MB/s; the file is read once first so every case runs from the cache.
|
||||
* Usage: vfs_native_bench [file] (default: makes a 64 MiB one here) */
|
||||
|
||||
#include <retro_posix_source.h>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <streams/file_stream.h>
|
||||
#include <rthreads/rthreads.h>
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <windows.h>
|
||||
#else
|
||||
#include <time.h>
|
||||
#endif
|
||||
|
||||
#define FILE_BYTES (64u << 20)
|
||||
#define RUNS 5
|
||||
#define RANDOM_OPS 20000
|
||||
#define SMALL_FILES 1000
|
||||
|
||||
static double now_ns(void)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
static LARGE_INTEGER f;
|
||||
LARGE_INTEGER c;
|
||||
if (!f.QuadPart)
|
||||
QueryPerformanceFrequency(&f);
|
||||
QueryPerformanceCounter(&c);
|
||||
return (double)c.QuadPart * 1e9 / (double)f.QuadPart;
|
||||
#else
|
||||
struct timespec t;
|
||||
clock_gettime(CLOCK_MONOTONIC, &t);
|
||||
return (double)t.tv_sec * 1e9 + (double)t.tv_nsec;
|
||||
#endif
|
||||
}
|
||||
|
||||
static uint8_t *buf;
|
||||
static int64_t size;
|
||||
|
||||
static void report(const char *what, double ns, double calls, double bytes)
|
||||
{
|
||||
printf("%-34s %10.1f ns/call %9.1f MB/s\n", what, ns / calls,
|
||||
bytes / (ns / 1e9) / 1e6);
|
||||
}
|
||||
|
||||
static void sequential(const char *path, int64_t chunk)
|
||||
{
|
||||
double best = 0, bytes = 0, calls = 0;
|
||||
int r;
|
||||
char what[64];
|
||||
int64_t limit = chunk < 256 ? (int64_t)(4u << 20) : size;
|
||||
for (r = 0; r < RUNS; r++)
|
||||
{
|
||||
RFILE *f = filestream_open(path, RETRO_VFS_FILE_ACCESS_READ,
|
||||
RETRO_VFS_FILE_ACCESS_HINT_NONE);
|
||||
int64_t done = 0, got;
|
||||
double t0, t;
|
||||
unsigned n = 0;
|
||||
if (!f)
|
||||
return;
|
||||
t0 = now_ns();
|
||||
while (done < limit && (got = filestream_read(f, buf, chunk)) > 0)
|
||||
{
|
||||
done += got;
|
||||
n++;
|
||||
}
|
||||
t = now_ns() - t0;
|
||||
filestream_close(f);
|
||||
if (!r || t < best)
|
||||
{
|
||||
best = t;
|
||||
bytes = (double)done;
|
||||
calls = (double)n;
|
||||
}
|
||||
}
|
||||
snprintf(what, sizeof(what), "sequential %lld B", (long long)chunk);
|
||||
report(what, best, calls, bytes);
|
||||
}
|
||||
|
||||
static void random_reads(const char *path, int at)
|
||||
{
|
||||
double best = 0;
|
||||
int r;
|
||||
for (r = 0; r < RUNS; r++)
|
||||
{
|
||||
RFILE *f = filestream_open(path, RETRO_VFS_FILE_ACCESS_READ,
|
||||
RETRO_VFS_FILE_ACCESS_HINT_NONE);
|
||||
unsigned x = 12345, k;
|
||||
double t0, t;
|
||||
if (!f)
|
||||
return;
|
||||
t0 = now_ns();
|
||||
for (k = 0; k < RANDOM_OPS; k++)
|
||||
{
|
||||
int64_t off;
|
||||
x = x * 1103515245u + 12345u;
|
||||
off = (int64_t)((x >> 4) % (uint32_t)(size - 4096));
|
||||
if (at)
|
||||
{
|
||||
if (filestream_read_at(f, buf, 4096, off) != 4096)
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
filestream_seek(f, off, RETRO_VFS_SEEK_POSITION_START);
|
||||
if (filestream_read(f, buf, 4096) != 4096)
|
||||
break;
|
||||
}
|
||||
}
|
||||
t = now_ns() - t0;
|
||||
filestream_close(f);
|
||||
if (!r || t < best)
|
||||
best = t;
|
||||
}
|
||||
report(at ? "random 4 KiB, read_at" : "random 4 KiB, seek+read", best,
|
||||
RANDOM_OPS, (double)RANDOM_OPS * 4096);
|
||||
}
|
||||
|
||||
struct worker
|
||||
{
|
||||
RFILE *f;
|
||||
unsigned seed;
|
||||
};
|
||||
|
||||
static void work(void *ud)
|
||||
{
|
||||
struct worker *w = (struct worker*)ud;
|
||||
uint8_t b[4096];
|
||||
unsigned x = w->seed, k;
|
||||
for (k = 0; k < RANDOM_OPS / 4; k++)
|
||||
{
|
||||
x = x * 1103515245u + 12345u;
|
||||
filestream_read_at(w->f, b, sizeof(b),
|
||||
(int64_t)((x >> 4) % (uint32_t)(size - 4096)));
|
||||
}
|
||||
}
|
||||
|
||||
static void threaded_reads(const char *path)
|
||||
{
|
||||
double best = 0;
|
||||
int r, i;
|
||||
RFILE *f = filestream_open(path, RETRO_VFS_FILE_ACCESS_READ,
|
||||
RETRO_VFS_FILE_ACCESS_HINT_NONE);
|
||||
if (!f || filestream_read_at(f, buf, 1, 0) != 1)
|
||||
{
|
||||
printf("%-34s no reads at offsets here\n", "random 4 KiB, read_at x4");
|
||||
filestream_close(f);
|
||||
return;
|
||||
}
|
||||
for (r = 0; r < RUNS; r++)
|
||||
{
|
||||
sthread_t *t[4];
|
||||
struct worker w[4];
|
||||
double t0 = now_ns(), dt;
|
||||
for (i = 0; i < 4; i++)
|
||||
{
|
||||
w[i].f = f;
|
||||
w[i].seed = 7u + 977u * (unsigned)i;
|
||||
t[i] = sthread_create(work, &w[i]);
|
||||
}
|
||||
for (i = 0; i < 4; i++)
|
||||
if (t[i])
|
||||
sthread_join(t[i]);
|
||||
dt = now_ns() - t0;
|
||||
if (!r || dt < best)
|
||||
best = dt;
|
||||
}
|
||||
filestream_close(f);
|
||||
report("random 4 KiB, read_at x4 threads", best, RANDOM_OPS,
|
||||
(double)RANDOM_OPS * 4096);
|
||||
}
|
||||
|
||||
static void small_files(void)
|
||||
{
|
||||
char name[64];
|
||||
unsigned i;
|
||||
double best = 0;
|
||||
int r;
|
||||
for (i = 0; i < SMALL_FILES; i++)
|
||||
{
|
||||
snprintf(name, sizeof(name), "vfs_bench_small_%04u.bin", i);
|
||||
filestream_write_file(name, buf, 4096);
|
||||
}
|
||||
for (r = 0; r < RUNS; r++)
|
||||
{
|
||||
double t0 = now_ns(), t;
|
||||
for (i = 0; i < SMALL_FILES; i++)
|
||||
{
|
||||
RFILE *f;
|
||||
snprintf(name, sizeof(name), "vfs_bench_small_%04u.bin", i);
|
||||
if ((f = filestream_open(name, RETRO_VFS_FILE_ACCESS_READ,
|
||||
RETRO_VFS_FILE_ACCESS_HINT_NONE)))
|
||||
{
|
||||
filestream_read(f, buf, 4096);
|
||||
filestream_close(f);
|
||||
}
|
||||
}
|
||||
t = now_ns() - t0;
|
||||
if (!r || t < best)
|
||||
best = t;
|
||||
}
|
||||
for (i = 0; i < SMALL_FILES; i++)
|
||||
{
|
||||
snprintf(name, sizeof(name), "vfs_bench_small_%04u.bin", i);
|
||||
remove(name);
|
||||
}
|
||||
report("open + read 4 KiB + close", best, SMALL_FILES,
|
||||
(double)SMALL_FILES * 4096);
|
||||
}
|
||||
|
||||
static void sequential_writes(int64_t chunk)
|
||||
{
|
||||
double best = 0;
|
||||
int r;
|
||||
char what[64];
|
||||
int64_t limit = chunk < 4096 ? (int64_t)(4u << 20) : (int64_t)(32u << 20);
|
||||
for (r = 0; r < RUNS; r++)
|
||||
{
|
||||
RFILE *f = filestream_open("vfs_bench_w.bin",
|
||||
RETRO_VFS_FILE_ACCESS_WRITE, RETRO_VFS_FILE_ACCESS_HINT_NONE);
|
||||
int64_t done = 0;
|
||||
double t0, t;
|
||||
if (!f)
|
||||
return;
|
||||
t0 = now_ns();
|
||||
while (done < limit && filestream_write(f, buf, chunk) == chunk)
|
||||
done += chunk;
|
||||
filestream_close(f);
|
||||
t = now_ns() - t0;
|
||||
if (!r || t < best)
|
||||
best = t;
|
||||
}
|
||||
remove("vfs_bench_w.bin");
|
||||
snprintf(what, sizeof(what), "write sequential %lld B", (long long)chunk);
|
||||
report(what, best, (double)(limit / chunk), (double)limit);
|
||||
}
|
||||
|
||||
static void random_writes(void)
|
||||
{
|
||||
double best = 0;
|
||||
int r;
|
||||
RFILE *f = filestream_open("vfs_bench_w.bin",
|
||||
RETRO_VFS_FILE_ACCESS_WRITE, RETRO_VFS_FILE_ACCESS_HINT_NONE);
|
||||
if (!f)
|
||||
return;
|
||||
filestream_write(f, buf, 1u << 20);
|
||||
filestream_write(f, buf, 1u << 20);
|
||||
filestream_write(f, buf, 1u << 20);
|
||||
filestream_write(f, buf, 1u << 20);
|
||||
filestream_close(f);
|
||||
for (r = 0; r < RUNS; r++)
|
||||
{
|
||||
unsigned x = 4321, k;
|
||||
double t0, t;
|
||||
if (!(f = filestream_open("vfs_bench_w.bin",
|
||||
RETRO_VFS_FILE_ACCESS_WRITE
|
||||
| RETRO_VFS_FILE_ACCESS_UPDATE_EXISTING,
|
||||
RETRO_VFS_FILE_ACCESS_HINT_NONE)))
|
||||
break;
|
||||
t0 = now_ns();
|
||||
for (k = 0; k < RANDOM_OPS; k++)
|
||||
{
|
||||
x = x * 1103515245u + 12345u;
|
||||
filestream_seek(f, (int64_t)((x >> 4) % ((4u << 20) - 64)),
|
||||
RETRO_VFS_SEEK_POSITION_START);
|
||||
if (filestream_write(f, buf, 64) != 64)
|
||||
break;
|
||||
}
|
||||
filestream_close(f);
|
||||
t = now_ns() - t0;
|
||||
if (!r || t < best)
|
||||
best = t;
|
||||
}
|
||||
remove("vfs_bench_w.bin");
|
||||
report("write random 64 B, seek+write", best, RANDOM_OPS,
|
||||
(double)RANDOM_OPS * 64);
|
||||
}
|
||||
|
||||
static void small_writes(void)
|
||||
{
|
||||
char name[64];
|
||||
unsigned i;
|
||||
double best = 0;
|
||||
int r;
|
||||
for (r = 0; r < RUNS; r++)
|
||||
{
|
||||
double t0 = now_ns(), t;
|
||||
for (i = 0; i < SMALL_FILES; i++)
|
||||
{
|
||||
RFILE *f;
|
||||
snprintf(name, sizeof(name), "vfs_bench_small_%04u.bin", i);
|
||||
if ((f = filestream_open(name, RETRO_VFS_FILE_ACCESS_WRITE,
|
||||
RETRO_VFS_FILE_ACCESS_HINT_NONE)))
|
||||
{
|
||||
filestream_write(f, buf, 4096);
|
||||
filestream_close(f);
|
||||
}
|
||||
}
|
||||
t = now_ns() - t0;
|
||||
if (!r || t < best)
|
||||
best = t;
|
||||
}
|
||||
for (i = 0; i < SMALL_FILES; i++)
|
||||
{
|
||||
snprintf(name, sizeof(name), "vfs_bench_small_%04u.bin", i);
|
||||
remove(name);
|
||||
}
|
||||
report("create + write 4 KiB + close", best, SMALL_FILES,
|
||||
(double)SMALL_FILES * 4096);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
const char *path = argc > 1 ? argv[1] : "vfs_bench_64m.bin";
|
||||
int made = 0;
|
||||
RFILE *f;
|
||||
if (!(buf = (uint8_t*)malloc(1u << 20)))
|
||||
return 1;
|
||||
memset(buf, 0x5A, 1u << 20);
|
||||
if (argc < 2)
|
||||
{
|
||||
unsigned i;
|
||||
if (!(f = filestream_open(path, RETRO_VFS_FILE_ACCESS_WRITE,
|
||||
RETRO_VFS_FILE_ACCESS_HINT_NONE)))
|
||||
return 1;
|
||||
for (i = 0; i < FILE_BYTES >> 20; i++)
|
||||
filestream_write(f, buf, 1u << 20);
|
||||
filestream_close(f);
|
||||
made = 1;
|
||||
}
|
||||
if (!(f = filestream_open(path, RETRO_VFS_FILE_ACCESS_READ,
|
||||
RETRO_VFS_FILE_ACCESS_HINT_NONE)))
|
||||
return 1;
|
||||
size = filestream_get_size(f);
|
||||
while (filestream_read(f, buf, 1u << 20) > 0) { }
|
||||
filestream_close(f);
|
||||
if (size < 8192)
|
||||
return 1;
|
||||
|
||||
#ifdef VFS_WIN32_NO_NATIVE
|
||||
printf("C runtime streams, %lld bytes\n", (long long)size);
|
||||
#else
|
||||
printf("native streams, %lld bytes\n", (long long)size);
|
||||
#endif
|
||||
sequential(path, 1);
|
||||
sequential(path, 16);
|
||||
sequential(path, 256);
|
||||
sequential(path, 4096);
|
||||
sequential(path, 65536);
|
||||
sequential(path, 1 << 20);
|
||||
random_reads(path, 0);
|
||||
random_reads(path, 1);
|
||||
threaded_reads(path);
|
||||
small_files();
|
||||
sequential_writes(16);
|
||||
sequential_writes(256);
|
||||
sequential_writes(4096);
|
||||
sequential_writes(65536);
|
||||
sequential_writes(1 << 20);
|
||||
random_writes();
|
||||
small_writes();
|
||||
|
||||
if (made)
|
||||
remove(path);
|
||||
free(buf);
|
||||
return 0;
|
||||
}
|
||||
@@ -23,9 +23,11 @@
|
||||
* Unix host, which always has pread(), or on Windows NT, which always
|
||||
* has a second handle, a -1 there fails instead - the build without
|
||||
* HAVE_MMAP (vfs_positional_nommap_test) holds that as Android builds,
|
||||
* vfs_positional_pathreopen_test the path reopen, and
|
||||
* vfs_positional_pathreopen_test the path reopen,
|
||||
* vfs_positional_ntverify_test the handle opened relative to the
|
||||
* stream's, checked against it as NT before Vista has it checked. */
|
||||
* stream's, checked against it as NT before Vista has it checked, and
|
||||
* vfs_positional_wide_test twelve threads at once - more than a
|
||||
* Windows stream keeps events for, so some calls make their own. */
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
@@ -67,7 +69,9 @@ static void thread_join(test_thread_t t)
|
||||
#endif
|
||||
|
||||
#define FILE_SIZE (4u << 20)
|
||||
#ifndef THREADS
|
||||
#define THREADS 4
|
||||
#endif
|
||||
#define READS 20000
|
||||
#define RECORDS 5000
|
||||
#define RECORD_LEN 64
|
||||
|
||||
@@ -0,0 +1,149 @@
|
||||
/* Copyright (C) 2010-2026 The RetroArch team
|
||||
*
|
||||
* ---------------------------------------------------------------------------------------
|
||||
* The following license statement only applies to this file (vfs_seek_read_mix_test.c).
|
||||
* ---------------------------------------------------------------------------------------
|
||||
*
|
||||
* Permission is hereby granted, free of charge,
|
||||
* to any person obtaining a copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation the rights to
|
||||
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
|
||||
* and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
||||
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
/* A read-only stream read the way parsers read it: seeks to random
|
||||
* places, reads of every size from 1 byte to past 64 KiB - some
|
||||
* across where a refill ends - runs of sequential reads after a seek,
|
||||
* seeks back into what was just read, seeks past the end, and the
|
||||
* position after each. Every byte is checked against the fixture, whose
|
||||
* bytes depend on where they lie. On Windows this is the stream's own
|
||||
* buffer sizing its refills by how it is being read. */
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <streams/file_stream.h>
|
||||
|
||||
#define FILE_BYTES ((3u << 20) + 1234)
|
||||
#define ROUNDS 20000
|
||||
|
||||
static int failures;
|
||||
#define CHECK(c) do { if (!(c)) { fprintf(stderr, "%s:%d: check failed: %s\n", \
|
||||
__FILE__, __LINE__, #c); failures++; } } while (0)
|
||||
|
||||
static uint8_t at(uint64_t i)
|
||||
{
|
||||
return (uint8_t)((i * 2654435761u) >> 11);
|
||||
}
|
||||
|
||||
static int same(const uint8_t *p, uint64_t off, uint64_t n)
|
||||
{
|
||||
uint64_t i;
|
||||
for (i = 0; i < n; i++)
|
||||
if (p[i] != at(off + i))
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
static const char path[] = "vfs_seek_read_mix.bin";
|
||||
static const unsigned sizes[] = { 1, 3, 16, 100, 512, 4095, 4096, 4097,
|
||||
30000, 65535, 65536, 65537, 200000 };
|
||||
uint8_t *data = (uint8_t*)malloc(FILE_BYTES);
|
||||
uint8_t *buf = (uint8_t*)malloc(200000);
|
||||
RFILE *f;
|
||||
unsigned x = 4242, k;
|
||||
uint64_t i;
|
||||
|
||||
if (!data || !buf)
|
||||
return 1;
|
||||
for (i = 0; i < FILE_BYTES; i++)
|
||||
data[i] = at(i);
|
||||
CHECK(filestream_write_file(path, data, FILE_BYTES));
|
||||
free(data);
|
||||
|
||||
f = filestream_open(path, RETRO_VFS_FILE_ACCESS_READ,
|
||||
RETRO_VFS_FILE_ACCESS_HINT_NONE);
|
||||
CHECK(f != NULL);
|
||||
if (!f)
|
||||
return 1;
|
||||
CHECK(filestream_get_size(f) == FILE_BYTES);
|
||||
|
||||
for (k = 0; k < ROUNDS; k++)
|
||||
{
|
||||
int64_t pos, got, want, left;
|
||||
unsigned run, r;
|
||||
x = x * 1103515245u + 12345u;
|
||||
switch ((x >> 28) & 3)
|
||||
{
|
||||
case 0: /* anywhere */
|
||||
pos = (int64_t)((x >> 3) % FILE_BYTES);
|
||||
break;
|
||||
case 1: /* back a little, into what was read */
|
||||
pos = filestream_tell(f) - (int64_t)((x >> 8) % 70000);
|
||||
if (pos < 0)
|
||||
pos = 0;
|
||||
break;
|
||||
case 2: /* near the end, or past it */
|
||||
pos = FILE_BYTES - 70000 + (int64_t)((x >> 8) % 80000);
|
||||
break;
|
||||
default: /* on from where it is */
|
||||
pos = filestream_tell(f);
|
||||
break;
|
||||
}
|
||||
CHECK(filestream_seek(f, pos, RETRO_VFS_SEEK_POSITION_START) >= 0);
|
||||
CHECK(filestream_tell(f) == pos);
|
||||
run = 1 + ((x >> 12) & 7);
|
||||
for (r = 0; r < run; r++)
|
||||
{
|
||||
x = x * 1103515245u + 12345u;
|
||||
want = (int64_t)sizes[(x >> 20) % (sizeof(sizes) / sizeof(sizes[0]))];
|
||||
left = pos < (int64_t)FILE_BYTES ? (int64_t)FILE_BYTES - pos : 0;
|
||||
got = filestream_read(f, buf, want);
|
||||
CHECK(got == (want < left ? want : left));
|
||||
if (got > 0)
|
||||
{
|
||||
CHECK(same(buf, (uint64_t)pos, (uint64_t)got));
|
||||
pos += got;
|
||||
}
|
||||
CHECK(filestream_tell(f) == pos);
|
||||
if (failures > 20)
|
||||
break;
|
||||
}
|
||||
if (failures > 20)
|
||||
break;
|
||||
}
|
||||
|
||||
/* and straight through, after all that */
|
||||
CHECK(filestream_seek(f, 0, RETRO_VFS_SEEK_POSITION_START) >= 0);
|
||||
for (i = 0; i < FILE_BYTES; )
|
||||
{
|
||||
int64_t got = filestream_read(f, buf, 7777);
|
||||
if (got <= 0 || !same(buf, i, (uint64_t)got))
|
||||
{
|
||||
failures++;
|
||||
break;
|
||||
}
|
||||
i += (uint64_t)got;
|
||||
}
|
||||
CHECK(i == FILE_BYTES);
|
||||
CHECK(filestream_read(f, buf, 10) == 0);
|
||||
|
||||
filestream_close(f);
|
||||
remove(path);
|
||||
free(buf);
|
||||
printf("vfs seek/read mix: %s\n", failures ? "FAIL" : "ok");
|
||||
return failures ? 1 : 0;
|
||||
}
|
||||
@@ -42,6 +42,8 @@
|
||||
|
||||
#if !defined(_WIN32)
|
||||
#include <unistd.h> /* geteuid: root ignores mode bits */
|
||||
#else
|
||||
#include <windows.h> /* CreateFileA: whether the system holds to it */
|
||||
#endif
|
||||
|
||||
#define DIR_NAME "v5_meta_dir"
|
||||
@@ -102,6 +104,21 @@ static bool files_equal(const char *a, const char *b)
|
||||
return same;
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
/* Whether the system itself refuses @path to a writer: Windows does
|
||||
* for a read-only file, Wine run as root does not, as root ignores the
|
||||
* mode bits Wine keeps the attribute in */
|
||||
static bool system_refuses_write(const char *path)
|
||||
{
|
||||
HANDLE h = CreateFileA(path, GENERIC_WRITE, FILE_SHARE_READ, NULL,
|
||||
OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
|
||||
if (h == INVALID_HANDLE_VALUE)
|
||||
return true;
|
||||
CloseHandle(h);
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
static void test_readonly(const char *dir)
|
||||
{
|
||||
char p[512];
|
||||
@@ -120,6 +137,10 @@ static void test_readonly(const char *dir)
|
||||
if (geteuid() == 0)
|
||||
SKIP("open-for-write-denied check (running as root, mode bits are not enforced)");
|
||||
else
|
||||
#else
|
||||
if (!system_refuses_write(p))
|
||||
SKIP("open-for-write-denied check (the system lets this process write a read-only file: Wine as root)");
|
||||
else
|
||||
#endif
|
||||
{
|
||||
RFILE *f = filestream_open(p, RETRO_VFS_FILE_ACCESS_WRITE, RETRO_VFS_FILE_ACCESS_HINT_NONE);
|
||||
|
||||
@@ -0,0 +1,341 @@
|
||||
/* Copyright (C) 2010-2026 The RetroArch team
|
||||
*
|
||||
* ---------------------------------------------------------------------------------------
|
||||
* The following license statement only applies to this file (vfs_write_mix_test.c).
|
||||
* ---------------------------------------------------------------------------------------
|
||||
*
|
||||
* Permission is hereby granted, free of charge,
|
||||
* to any person obtaining a copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation the rights to
|
||||
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
|
||||
* and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
||||
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
/* A stream written the way savers, config writers and patchers write
|
||||
* it, in every mode a stream is opened to write in: runs of small
|
||||
* writes, writes of every size up to past 64 KiB, writes past the end
|
||||
* that leave a gap, seeks of every kind, reads back, positions and
|
||||
* sizes after each, flushes, truncates shorter and longer, holes
|
||||
* punched, reads at offsets, and closing and opening it again to update it. A copy of
|
||||
* the file kept here says what every read, position and size must be,
|
||||
* and what the file holds when it is closed. On Windows this is the
|
||||
* stream's own buffer holding the writes (VFS_WIN32_NO_NATIVE: the C
|
||||
* runtime's). */
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <boolean.h>
|
||||
#include <libretro.h>
|
||||
#include <streams/file_stream.h>
|
||||
|
||||
#define PATH "vfs_write_mix_test.bin"
|
||||
#define MODEL_MAX (2u << 20)
|
||||
#define POS_MAX (1u << 20)
|
||||
#define ROUNDS 12000
|
||||
|
||||
static int failures;
|
||||
static uint8_t *model;
|
||||
static uint8_t *scratch;
|
||||
static uint64_t msize;
|
||||
static uint64_t mpos;
|
||||
static uint32_t seed = 0x2545f491u;
|
||||
|
||||
#define CHECK(c) do { if (!(c)) { fprintf(stderr, \
|
||||
"%s:%d: round %u, mode %u: check failed: %s\n", __FILE__, __LINE__, \
|
||||
round_no, mode_no, #c); failures++; } } while (0)
|
||||
|
||||
static unsigned round_no;
|
||||
static unsigned mode_no;
|
||||
|
||||
static uint32_t rnd(uint32_t n)
|
||||
{
|
||||
seed ^= seed << 13;
|
||||
seed ^= seed >> 17;
|
||||
seed ^= seed << 5;
|
||||
return n ? seed % n : 0;
|
||||
}
|
||||
|
||||
/* @len bytes written at @at of the copy, zeroes in any gap before them */
|
||||
static void model_write(uint64_t at, const uint8_t *s, uint64_t len)
|
||||
{
|
||||
if (at > msize)
|
||||
memset(model + msize, 0, (size_t)(at - msize));
|
||||
memcpy(model + at, s, (size_t)len);
|
||||
if (at + len > msize)
|
||||
msize = at + len;
|
||||
}
|
||||
|
||||
static void model_truncate(uint64_t len)
|
||||
{
|
||||
if (len > msize)
|
||||
memset(model + msize, 0, (size_t)(len - msize));
|
||||
msize = len;
|
||||
}
|
||||
|
||||
static void fill(uint8_t *p, uint64_t len)
|
||||
{
|
||||
uint64_t i;
|
||||
uint8_t b = (uint8_t)rnd(256);
|
||||
for (i = 0; i < len; i++)
|
||||
p[i] = (uint8_t)(b + i * 7 + (i >> 9));
|
||||
}
|
||||
|
||||
/* The file as it stands on disk, read without the stream */
|
||||
static void check_disk(void)
|
||||
{
|
||||
FILE *f = fopen(PATH, "rb");
|
||||
uint64_t got;
|
||||
if (!f)
|
||||
{
|
||||
CHECK(f != NULL);
|
||||
return;
|
||||
}
|
||||
got = (uint64_t)fread(scratch, 1, MODEL_MAX, f);
|
||||
fclose(f);
|
||||
CHECK(got == msize);
|
||||
CHECK(got != msize || memcmp(scratch, model, (size_t)got) == 0);
|
||||
}
|
||||
|
||||
static void run_mode(unsigned mode, unsigned reopen_mode, bool readable,
|
||||
bool creates)
|
||||
{
|
||||
RFILE *f;
|
||||
unsigned r;
|
||||
|
||||
if (!creates)
|
||||
{
|
||||
/* an update opens what is there */
|
||||
FILE *seedf = fopen(PATH, "wb");
|
||||
msize = 4096 + rnd(70000);
|
||||
fill(model, msize);
|
||||
if (seedf)
|
||||
{
|
||||
fwrite(model, 1, (size_t)msize, seedf);
|
||||
fclose(seedf);
|
||||
}
|
||||
}
|
||||
else
|
||||
msize = 0;
|
||||
mpos = 0;
|
||||
if (!(f = filestream_open(PATH, mode, RETRO_VFS_FILE_ACCESS_HINT_NONE)))
|
||||
{
|
||||
CHECK(f != NULL);
|
||||
return;
|
||||
}
|
||||
CHECK(filestream_get_size(f) == (int64_t)msize);
|
||||
|
||||
for (r = 0; r < ROUNDS; r++)
|
||||
{
|
||||
uint32_t op = rnd(100);
|
||||
round_no = r;
|
||||
if (op < 35 || (op < 48 && mpos >= POS_MAX))
|
||||
{
|
||||
/* a run of small writes */
|
||||
uint64_t len = 1 + rnd(300);
|
||||
if (mpos + len > MODEL_MAX)
|
||||
continue;
|
||||
fill(scratch, len);
|
||||
CHECK(filestream_write(f, scratch, (int64_t)len) == (int64_t)len);
|
||||
model_write(mpos, scratch, len);
|
||||
mpos += len;
|
||||
}
|
||||
else if (op < 48)
|
||||
{
|
||||
/* up to past the buffer; one in four at least as large */
|
||||
uint64_t len = (op < 45) ? 300 + rnd(70000)
|
||||
: 65536 + rnd(140000);
|
||||
if (mpos + len > MODEL_MAX)
|
||||
continue;
|
||||
fill(scratch, len);
|
||||
CHECK(filestream_write(f, scratch, (int64_t)len) == (int64_t)len);
|
||||
model_write(mpos, scratch, len);
|
||||
mpos += len;
|
||||
}
|
||||
else if (op < 60)
|
||||
{
|
||||
uint64_t to = rnd((uint32_t)(msize + 100000));
|
||||
if (to > POS_MAX)
|
||||
to = rnd(POS_MAX);
|
||||
CHECK(filestream_seek(f, (int64_t)to,
|
||||
RETRO_VFS_SEEK_POSITION_START) >= 0);
|
||||
mpos = to;
|
||||
}
|
||||
else if (op < 65)
|
||||
{
|
||||
int64_t d = (int64_t)rnd(140000) - 70000;
|
||||
if ((int64_t)mpos + d < 0 || mpos + (uint64_t)(d < 0 ? 0 : d) > POS_MAX)
|
||||
continue;
|
||||
CHECK(filestream_seek(f, d, RETRO_VFS_SEEK_POSITION_CURRENT) >= 0);
|
||||
mpos = (uint64_t)((int64_t)mpos + d);
|
||||
}
|
||||
else if (op < 68)
|
||||
{
|
||||
int64_t d = -(int64_t)rnd((uint32_t)msize + 1) + (int64_t)rnd(5000);
|
||||
if ((int64_t)msize + d < 0 || msize + (uint64_t)(d < 0 ? 0 : d) > POS_MAX)
|
||||
continue;
|
||||
CHECK(filestream_seek(f, d, RETRO_VFS_SEEK_POSITION_END) >= 0);
|
||||
mpos = (uint64_t)((int64_t)msize + d);
|
||||
}
|
||||
else if (op < 80)
|
||||
{
|
||||
uint64_t len = 1 + ((op & 1) ? rnd(300) : rnd(90000));
|
||||
uint64_t want = mpos >= msize ? 0
|
||||
: (msize - mpos < len ? msize - mpos : len);
|
||||
int64_t got = filestream_read(f, scratch, (int64_t)len);
|
||||
if (!readable)
|
||||
CHECK(got <= 0);
|
||||
else
|
||||
{
|
||||
CHECK(got == (int64_t)want);
|
||||
CHECK(got != (int64_t)want
|
||||
|| memcmp(scratch, model + mpos, (size_t)want) == 0);
|
||||
if (got > 0)
|
||||
mpos += (uint64_t)got;
|
||||
}
|
||||
}
|
||||
else if (op < 84)
|
||||
CHECK(filestream_tell(f) == (int64_t)mpos);
|
||||
else if (op < 87)
|
||||
CHECK(filestream_get_size(f) == (int64_t)msize);
|
||||
else if (op < 89)
|
||||
{
|
||||
CHECK(filestream_flush(f) == 0);
|
||||
CHECK(filestream_tell(f) == (int64_t)mpos);
|
||||
}
|
||||
else if (op < 92)
|
||||
{
|
||||
uint64_t to = rnd((uint32_t)(msize + 50000));
|
||||
if (to > POS_MAX)
|
||||
continue;
|
||||
CHECK(filestream_truncate(f, (int64_t)to) == 0);
|
||||
model_truncate(to);
|
||||
CHECK(filestream_get_size(f) == (int64_t)msize);
|
||||
CHECK(filestream_tell(f) == (int64_t)mpos);
|
||||
}
|
||||
else if (op < 93)
|
||||
{
|
||||
/* zeroes where the filesystem can free a range, the size kept;
|
||||
* -1 where it cannot */
|
||||
uint64_t at = rnd((uint32_t)(msize + 1000));
|
||||
uint64_t len = 1 + rnd(150000);
|
||||
if (filestream_punch_hole(f, (int64_t)at, (int64_t)len) == 0
|
||||
&& at < msize)
|
||||
memset(model + at, 0, (size_t)(msize - at < len
|
||||
? msize - at : len));
|
||||
CHECK(filestream_get_size(f) == (int64_t)msize);
|
||||
CHECK(filestream_tell(f) == (int64_t)mpos);
|
||||
}
|
||||
else if (op < 96)
|
||||
{
|
||||
/* at an offset, from the file itself: what was written goes
|
||||
* there first */
|
||||
uint64_t at = rnd((uint32_t)(msize + 1000));
|
||||
uint64_t len = 1 + rnd(80000);
|
||||
uint64_t want = at >= msize ? 0
|
||||
: (msize - at < len ? msize - at : len);
|
||||
int64_t got;
|
||||
CHECK(filestream_flush(f) == 0);
|
||||
got = filestream_read_at(f, scratch, (int64_t)len, (int64_t)at);
|
||||
/* -1 is a stream with no reads at offsets */
|
||||
CHECK(got == -1 || got == (int64_t)want);
|
||||
CHECK(got != (int64_t)want
|
||||
|| memcmp(scratch, model + at, (size_t)want) == 0);
|
||||
CHECK(filestream_tell(f) == (int64_t)mpos);
|
||||
}
|
||||
else
|
||||
{
|
||||
CHECK(filestream_close(f) == 0);
|
||||
check_disk();
|
||||
if (!(f = filestream_open(PATH, reopen_mode,
|
||||
RETRO_VFS_FILE_ACCESS_HINT_NONE)))
|
||||
{
|
||||
CHECK(f != NULL);
|
||||
return;
|
||||
}
|
||||
readable = true; /* every update stream reads */
|
||||
mpos = 0;
|
||||
CHECK(filestream_get_size(f) == (int64_t)msize);
|
||||
}
|
||||
}
|
||||
CHECK(filestream_close(f) == 0);
|
||||
check_disk();
|
||||
}
|
||||
|
||||
/* Writes the stream still holds, then a hole punched and a truncate
|
||||
* over them: they go into the file first, so what is left of them is
|
||||
* the zeroes and the cut */
|
||||
static void check_held_writes(void)
|
||||
{
|
||||
RFILE *f;
|
||||
uint8_t b[4000];
|
||||
int punched;
|
||||
round_no = 0;
|
||||
mode_no = 4;
|
||||
memset(b, 0xaa, sizeof(b));
|
||||
if (!(f = filestream_open(PATH, RETRO_VFS_FILE_ACCESS_WRITE,
|
||||
RETRO_VFS_FILE_ACCESS_HINT_NONE)))
|
||||
{
|
||||
CHECK(f != NULL);
|
||||
return;
|
||||
}
|
||||
CHECK(filestream_write(f, b, sizeof(b)) == (int64_t)sizeof(b));
|
||||
punched = filestream_punch_hole(f, 0, 2000) == 0;
|
||||
CHECK(filestream_write(f, b, sizeof(b)) == (int64_t)sizeof(b));
|
||||
CHECK(filestream_truncate(f, 6000) == 0);
|
||||
CHECK(filestream_close(f) == 0);
|
||||
memset(model, 0xaa, 6000);
|
||||
if (punched)
|
||||
memset(model, 0, 2000);
|
||||
msize = 6000;
|
||||
check_disk();
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
if ( !(model = (uint8_t*)malloc(MODEL_MAX))
|
||||
|| !(scratch = (uint8_t*)malloc(MODEL_MAX)))
|
||||
return 2;
|
||||
|
||||
check_held_writes();
|
||||
mode_no = 0;
|
||||
run_mode(RETRO_VFS_FILE_ACCESS_WRITE,
|
||||
RETRO_VFS_FILE_ACCESS_WRITE | RETRO_VFS_FILE_ACCESS_UPDATE_EXISTING,
|
||||
false, true);
|
||||
mode_no = 1;
|
||||
run_mode(RETRO_VFS_FILE_ACCESS_READ_WRITE,
|
||||
RETRO_VFS_FILE_ACCESS_READ_WRITE
|
||||
| RETRO_VFS_FILE_ACCESS_UPDATE_EXISTING, true, true);
|
||||
mode_no = 2;
|
||||
run_mode(RETRO_VFS_FILE_ACCESS_WRITE
|
||||
| RETRO_VFS_FILE_ACCESS_UPDATE_EXISTING,
|
||||
RETRO_VFS_FILE_ACCESS_WRITE | RETRO_VFS_FILE_ACCESS_UPDATE_EXISTING,
|
||||
true, false);
|
||||
mode_no = 3;
|
||||
run_mode(RETRO_VFS_FILE_ACCESS_READ_WRITE
|
||||
| RETRO_VFS_FILE_ACCESS_UPDATE_EXISTING,
|
||||
RETRO_VFS_FILE_ACCESS_READ_WRITE
|
||||
| RETRO_VFS_FILE_ACCESS_UPDATE_EXISTING, true, false);
|
||||
|
||||
remove(PATH);
|
||||
free(model);
|
||||
free(scratch);
|
||||
if (failures)
|
||||
{
|
||||
printf("vfs_write_mix_test: %d check(s) failed\n", failures);
|
||||
return 1;
|
||||
}
|
||||
printf("vfs_write_mix_test: every read, position, size and file matched\n");
|
||||
return 0;
|
||||
}
|
||||
@@ -22,11 +22,26 @@ static int injected_close(int fd)
|
||||
descriptor_closes++;
|
||||
return fail_descriptor ? -1 : result;
|
||||
}
|
||||
#ifdef _WIN32
|
||||
#include <windows.h>
|
||||
/* Streams on Windows NT close their own handle: that is the descriptor
|
||||
* close there. Nothing else in these checks closes a handle. */
|
||||
static BOOL injected_close_handle(HANDLE h)
|
||||
{
|
||||
BOOL result = CloseHandle(h);
|
||||
descriptor_closes++;
|
||||
return fail_descriptor ? FALSE : result;
|
||||
}
|
||||
#define CloseHandle injected_close_handle
|
||||
#endif
|
||||
#define fclose injected_fclose
|
||||
#define close injected_close
|
||||
#include "../../../vfs/vfs_implementation.c"
|
||||
#undef fclose
|
||||
#undef close
|
||||
#ifdef _WIN32
|
||||
#undef CloseHandle
|
||||
#endif
|
||||
|
||||
static int RETRO_CALLCONV callback_close(struct retro_vfs_file_handle *file)
|
||||
{
|
||||
@@ -49,11 +64,20 @@ int main(void)
|
||||
if (file)
|
||||
{
|
||||
CHECK(filestream_write(file, "data", 4) == 4);
|
||||
#ifdef VFS_NATIVE
|
||||
/* written through its own handle, closed with it */
|
||||
before = descriptor_closes;
|
||||
fail_descriptor = 1;
|
||||
CHECK(filestream_close(file) == -1);
|
||||
CHECK(descriptor_closes == before + 1);
|
||||
fail_descriptor = 0;
|
||||
#else
|
||||
before = stdio_closes;
|
||||
fail_stdio = 1;
|
||||
CHECK(filestream_close(file) == -1);
|
||||
CHECK(stdio_closes == before + 1);
|
||||
fail_stdio = 0;
|
||||
#endif
|
||||
}
|
||||
native = retro_vfs_file_open_impl("close_result.bin", RETRO_VFS_FILE_ACCESS_READ, 0);
|
||||
CHECK(native != NULL);
|
||||
|
||||
@@ -23,10 +23,19 @@ SOURCES := \
|
||||
|
||||
OBJS := $(call sample_objs,$(SOURCES:.c=.o))
|
||||
|
||||
# -DHAVE_MMAP so the local branch really produces a mapping; without
|
||||
# HAVE_MMAP so the local branch really produces a mapping; without
|
||||
# it the zero-copy check has nothing to assert and only reports a skip.
|
||||
CFLAGS += -Wall -pedantic -std=gnu99 -g1 -DHAVE_MMAP \
|
||||
-I$(LIBRETRO_COMM_DIR)/include
|
||||
# Windows maps through vfs.h's _WIN32 backend and has no <sys/mman.h>.
|
||||
UNAME_S := $(shell uname -s 2>/dev/null)
|
||||
ifneq (,$(filter MINGW% MSYS% CYGWIN%,$(UNAME_S)))
|
||||
MMAP ?= 0
|
||||
else
|
||||
MMAP ?= 1
|
||||
endif
|
||||
CFLAGS += -Wall -pedantic -std=gnu99 -g1 -I$(LIBRETRO_COMM_DIR)/include
|
||||
ifeq ($(MMAP),1)
|
||||
CFLAGS += -DHAVE_MMAP
|
||||
endif
|
||||
|
||||
# The wrapper-allocation failure cases exist because that path is
|
||||
# otherwise unreachable: calloc does not fail on demand, so the branch
|
||||
|
||||
@@ -52,6 +52,11 @@
|
||||
* either, or the loose-file probe cost that the design promises to
|
||||
* preserve is gone.
|
||||
*
|
||||
* 4. Positional I/O (v6). A local handle reads and writes at an offset
|
||||
* without moving its position; a frontend handle reaches the
|
||||
* frontend's read_at and write_at only when it advertised v6, and
|
||||
* reports -1 otherwise so the caller seeks and reads.
|
||||
*
|
||||
* Build: make (SANITIZER=address,undefined for a checked run)
|
||||
* Run: ./vfs_hybrid_test
|
||||
*/
|
||||
@@ -170,6 +175,42 @@ static int fe_closedir(struct retro_vfs_dir_handle *d)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* ---- v6 ---- */
|
||||
|
||||
static int fe_read_ats;
|
||||
|
||||
static int64_t fe_read_at(struct retro_vfs_file_handle *s, void *d,
|
||||
uint64_t l, int64_t o)
|
||||
{
|
||||
(void)s; (void)o;
|
||||
fe_read_ats++;
|
||||
memset(d, 'P', (size_t)l);
|
||||
return (int64_t)l;
|
||||
}
|
||||
|
||||
static int64_t fe_write_at(struct retro_vfs_file_handle *s, const void *d,
|
||||
uint64_t l, int64_t o)
|
||||
{
|
||||
(void)s; (void)d; (void)o;
|
||||
return (int64_t)l;
|
||||
}
|
||||
|
||||
static int64_t fe_read_at_poison(struct retro_vfs_file_handle *s, void *d,
|
||||
uint64_t l, int64_t o)
|
||||
{
|
||||
(void)s; (void)d; (void)l; (void)o;
|
||||
fe_above_version = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int64_t fe_write_at_poison(struct retro_vfs_file_handle *s,
|
||||
const void *d, uint64_t l, int64_t o)
|
||||
{
|
||||
(void)s; (void)d; (void)l; (void)o;
|
||||
fe_above_version = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* A frontend that filled only its v1 members. Everything above is
|
||||
* poisoned rather than left NULL, so a version-gating slip is a
|
||||
* reported failure and not a NULL check that happens to save us. */
|
||||
@@ -190,6 +231,31 @@ static struct retro_vfs_interface iface_v3 =
|
||||
fe_dirent_name, fe_dirent_is_dir, fe_closedir
|
||||
};
|
||||
|
||||
/* v5 with its v6 slots poisoned, and v6 */
|
||||
static struct retro_vfs_interface iface_v5 =
|
||||
{
|
||||
fe_get_path, fe_open, fe_close, fe_size, fe_tell, fe_seek,
|
||||
fe_read, fe_write, fe_flush, fe_remove, fe_rename,
|
||||
fe_truncate,
|
||||
fe_stat, fe_mkdir, fe_opendir, fe_readdir,
|
||||
fe_dirent_name, fe_dirent_is_dir, fe_closedir,
|
||||
NULL,
|
||||
NULL, NULL, NULL, NULL, NULL, NULL, NULL,
|
||||
fe_read_at_poison, fe_write_at_poison
|
||||
};
|
||||
|
||||
static struct retro_vfs_interface iface_v6 =
|
||||
{
|
||||
fe_get_path, fe_open, fe_close, fe_size, fe_tell, fe_seek,
|
||||
fe_read, fe_write, fe_flush, fe_remove, fe_rename,
|
||||
fe_truncate,
|
||||
fe_stat, fe_mkdir, fe_opendir, fe_readdir,
|
||||
fe_dirent_name, fe_dirent_is_dir, fe_closedir,
|
||||
NULL,
|
||||
NULL, NULL, NULL, NULL, NULL, NULL, NULL,
|
||||
fe_read_at, fe_write_at
|
||||
};
|
||||
|
||||
static struct retro_vfs_interface *iface_in_use;
|
||||
|
||||
static bool env_cb(unsigned cmd, void *data)
|
||||
@@ -366,6 +432,85 @@ int main(void)
|
||||
remove("hybrid_plain.bin");
|
||||
}
|
||||
|
||||
/* ---- 4: positional I/O ---- */
|
||||
{
|
||||
RFILE *f;
|
||||
char buf[8];
|
||||
int64_t got;
|
||||
|
||||
{
|
||||
FILE *w = fopen("hybrid_at.bin", "wb");
|
||||
fwrite("0123456789", 1, 10, w);
|
||||
fclose(w);
|
||||
}
|
||||
|
||||
fe_reset(6);
|
||||
iface_in_use = &iface_v6;
|
||||
vfs_hybrid_init(env_cb, NULL);
|
||||
|
||||
f = filestream_open("hybrid_at.bin",
|
||||
RETRO_VFS_FILE_ACCESS_READ | RETRO_VFS_FILE_ACCESS_WRITE
|
||||
| RETRO_VFS_FILE_ACCESS_UPDATE_EXISTING,
|
||||
RETRO_VFS_FILE_ACCESS_HINT_NONE);
|
||||
CHECK(f != NULL, "v6 local: plain path opens");
|
||||
if (f)
|
||||
{
|
||||
memset(buf, 0, sizeof(buf));
|
||||
got = filestream_read_at(f, buf, 3, 6);
|
||||
#if defined(_WIN32) || defined(__unix__) || defined(__APPLE__)
|
||||
CHECK(got == 3 && memcmp(buf, "678", 3) == 0,
|
||||
"v6 local: read_at reads at the offset");
|
||||
CHECK(filestream_write_at(f, "AB", 2, 2) == 2,
|
||||
"v6 local: write_at writes at the offset");
|
||||
memset(buf, 0, sizeof(buf));
|
||||
got = filestream_read_at(f, buf, 4, 0);
|
||||
CHECK(got == 4 && memcmp(buf, "01AB", 4) == 0,
|
||||
"v6 local: the write is on disk");
|
||||
CHECK(filestream_tell(f) == 0,
|
||||
"v6 local: the position is where it was");
|
||||
#else
|
||||
printf("skip: no positional I/O on this platform (%d)\n", (int)got);
|
||||
#endif
|
||||
CHECK(fe_read_ats == 0, "v6 local: the frontend is not called");
|
||||
filestream_close(f);
|
||||
}
|
||||
|
||||
f = filestream_open("content://media/item",
|
||||
RETRO_VFS_FILE_ACCESS_READ, RETRO_VFS_FILE_ACCESS_HINT_NONE);
|
||||
CHECK(f != NULL, "v6: uri path opens through the frontend");
|
||||
if (f)
|
||||
{
|
||||
memset(buf, 0, sizeof(buf));
|
||||
got = filestream_read_at(f, buf, 2, 4);
|
||||
CHECK(got == 2 && buf[0] == 'P' && fe_read_ats == 1,
|
||||
"v6: read_at reaches the frontend's read_at");
|
||||
CHECK(filestream_write_at(f, "x", 1, 0) == 1,
|
||||
"v6: write_at reaches the frontend's write_at");
|
||||
filestream_close(f);
|
||||
}
|
||||
|
||||
fe_reset(5);
|
||||
iface_in_use = &iface_v5;
|
||||
vfs_hybrid_init(env_cb, NULL);
|
||||
|
||||
f = filestream_open("content://media/item",
|
||||
RETRO_VFS_FILE_ACCESS_READ, RETRO_VFS_FILE_ACCESS_HINT_NONE);
|
||||
CHECK(f != NULL, "v5: uri path opens through the frontend");
|
||||
if (f)
|
||||
{
|
||||
got = filestream_read_at(f, buf, 2, 4);
|
||||
CHECK(got == -1 && !fe_above_version,
|
||||
"v5: read_at reports -1 without calling a v6 member");
|
||||
got = filestream_write_at(f, "x", 1, 0);
|
||||
CHECK(got == -1 && !fe_above_version,
|
||||
"v5: write_at reports -1 without calling a v6 member");
|
||||
filestream_close(f);
|
||||
}
|
||||
CHECK(fe_files_live == 0, "v6: frontend file handles released");
|
||||
|
||||
remove("hybrid_at.bin");
|
||||
}
|
||||
|
||||
if (test_fails)
|
||||
{
|
||||
printf("== %d FAILURES ==\n", test_fails);
|
||||
|
||||
@@ -20,7 +20,7 @@ COMMON_SOURCES := \
|
||||
$(LIBRETRO_COMM_DIR)/file/file_path_io.c \
|
||||
$(LIBRETRO_COMM_DIR)/formats/audio_transfer.c \
|
||||
$(LIBRETRO_COMM_DIR)/formats/mp3/rmp3.c \
|
||||
$(LIBRETRO_COMM_DIR)/formats/data_transfer.c \
|
||||
$(LIBRETRO_COMM_DIR)/formats/data_transfer.c $(LIBRETRO_COMM_DIR)/streams/data_transfer_file.c \
|
||||
$(LIBRETRO_COMM_DIR)/formats/aac/raac.c \
|
||||
$(LIBRETRO_COMM_DIR)/formats/opus/ropus.c \
|
||||
$(LIBRETRO_COMM_DIR)/formats/vorbis/rvorbis.c \
|
||||
|
||||
@@ -56,6 +56,7 @@
|
||||
|
||||
#include <formats/audio.h>
|
||||
#include <formats/data_transfer.h>
|
||||
#include <streams/data_transfer_file.h>
|
||||
#include <formats/rwebm.h>
|
||||
|
||||
#if defined(__linux__) || defined(__unix__) || defined(__APPLE__)
|
||||
|
||||
@@ -15,7 +15,7 @@ SOURCES := audio_transfer_flac_test.c \
|
||||
$(LIBRETRO_COMM_DIR)/file/file_path.c \
|
||||
$(LIBRETRO_COMM_DIR)/file/file_path_io.c \
|
||||
$(LIBRETRO_COMM_DIR)/formats/audio_transfer.c \
|
||||
$(LIBRETRO_COMM_DIR)/formats/data_transfer.c \
|
||||
$(LIBRETRO_COMM_DIR)/formats/data_transfer.c $(LIBRETRO_COMM_DIR)/streams/data_transfer_file.c \
|
||||
$(LIBRETRO_COMM_DIR)/formats/flac/rflac.c \
|
||||
$(LIBRETRO_COMM_DIR)/formats/webm/rwebm.c \
|
||||
$(LIBRETRO_COMM_DIR)/memmap/memmap.c \
|
||||
|
||||
@@ -9,7 +9,10 @@
|
||||
* the frames as delimited packets, all given up front;
|
||||
* - demuxed input grown a packet at a time, each growth only after
|
||||
* the decoder has run dry - the progressive source the growth
|
||||
* contract in <formats/audio.h> is written for.
|
||||
* contract in <formats/audio.h> is written for;
|
||||
* - seeks in a native .flac buffer that carries a seek table, which
|
||||
* take the table's shortcut, each landing on the frame asked for
|
||||
* (rflac_seek_point gives the seek point's own frame as well).
|
||||
*
|
||||
* FLAC is lossless, so every lane must give back exactly the PCM that
|
||||
* went in: every sample, and not one frame more or less. The fixture
|
||||
@@ -27,6 +30,7 @@
|
||||
#include <math.h>
|
||||
|
||||
#include <formats/audio.h>
|
||||
#include <formats/rflac.h>
|
||||
#include <formats/rwebm.h>
|
||||
#include <streams/file_stream.h>
|
||||
|
||||
@@ -263,6 +267,64 @@ static void lane_demuxed(const struct demuxed *d, int grow)
|
||||
free(pcm);
|
||||
}
|
||||
|
||||
/* Seeks in a buffer with a seek table: points at frames 0, 4096, 8192
|
||||
* and 12288. Each lands on the frame asked for, between the points and
|
||||
* on them, forward and back; resumed from a point at its own frame, not
|
||||
* at frame 0 (which landed a seek the point's frames past its target). */
|
||||
static void lane_seek(const char *path)
|
||||
{
|
||||
static const uint64_t targets[] = { 10000, 4096, 15000, 100, 12288,
|
||||
19999, 6000 };
|
||||
const char *lane = "native .flac buffer, seek table";
|
||||
size_t len, i;
|
||||
void *buf = load(path, &len);
|
||||
void *ctx = audio_transfer_new(AUDIO_TYPE_FLAC);
|
||||
int16_t pcm[1000 * 2];
|
||||
rflac_t *f = rflac_new();
|
||||
uint64_t at = 0, point = 0;
|
||||
|
||||
/* The point itself, straight from rflac */
|
||||
CHECK(f != NULL, "%s: rflac_new", lane);
|
||||
if (f)
|
||||
{
|
||||
rflac_set_in(f, (const uint8_t*)buf, len);
|
||||
rflac_set_eof(f);
|
||||
while (!rflac_format(f))
|
||||
if (rflac_process(f, NULL, NULL) != RFLAC_PROCESS_NEXT)
|
||||
break;
|
||||
CHECK(rflac_seek_point(f, 10000, &at, &point) == RFLAC_PROCESS_NEXT
|
||||
&& point == 8192 && at > 0 && at < len,
|
||||
"%s: the point for frame 10000 is frame %lu at byte %lu", lane,
|
||||
(unsigned long)point, (unsigned long)at);
|
||||
rflac_free(f);
|
||||
}
|
||||
|
||||
audio_transfer_set_buffer_ptr(ctx, AUDIO_TYPE_FLAC, buf, len);
|
||||
CHECK(audio_transfer_start(ctx, AUDIO_TYPE_FLAC), "%s: start failed", lane);
|
||||
for (i = 0; i < sizeof(targets) / sizeof(targets[0]); i++)
|
||||
{
|
||||
const size_t want = SRC_FRAMES - targets[i] < 1000
|
||||
? (size_t)(SRC_FRAMES - targets[i]) : 1000;
|
||||
size_t got, k;
|
||||
CHECK(audio_transfer_seek(ctx, AUDIO_TYPE_FLAC, targets[i]),
|
||||
"%s: seek to %lu refused", lane, (unsigned long)targets[i]);
|
||||
got = drain(ctx, pcm, 0, want, 1000);
|
||||
CHECK(got == want, "%s: %lu frames after a seek to %lu, %lu wanted",
|
||||
lane, (unsigned long)got, (unsigned long)targets[i],
|
||||
(unsigned long)want);
|
||||
for (k = 0; k < got; k++)
|
||||
if ( pcm[k * 2] != src_pcm[(targets[i] + k) * 2]
|
||||
|| pcm[k * 2 + 1] != src_pcm[(targets[i] + k) * 2 + 1])
|
||||
{
|
||||
CHECK(0, "%s: after a seek to %lu, frame %lu differs", lane,
|
||||
(unsigned long)targets[i], (unsigned long)k);
|
||||
break;
|
||||
}
|
||||
}
|
||||
audio_transfer_free(ctx, AUDIO_TYPE_FLAC);
|
||||
free(buf);
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
struct demuxed d;
|
||||
@@ -273,6 +335,7 @@ int main(void)
|
||||
lane_buffer("native .flac buffer", "fixtures/tone.flac");
|
||||
lane_buffer("Matroska buffer", "fixtures/tone.mka");
|
||||
lane_windowed("fixtures/tone.mka", 4096, 2048);
|
||||
lane_seek("fixtures/tone_seektable.flac");
|
||||
|
||||
mka = load("fixtures/tone.mka", &mka_len);
|
||||
demux_mka((const uint8_t*)mka, mka_len, &d);
|
||||
|
||||
Binary file not shown.
@@ -1,12 +1,13 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Regenerates fixtures/tone.flac and fixtures/tone.mka.
|
||||
"""Regenerates fixtures/tone.flac, fixtures/tone.mka and
|
||||
fixtures/tone_seektable.flac.
|
||||
|
||||
20000 stereo frames at 44100 Hz - not a multiple of the encoder's block
|
||||
size, so each stream ends on a short frame - of two sines with a little
|
||||
low-order noise so the predictor has residual to code. The test
|
||||
regenerates the same PCM from the same formula and compares every
|
||||
sample, so keep the two in step. Needs ffmpeg; the committed fixtures
|
||||
are what CI uses."""
|
||||
sample, so keep the two in step. Needs ffmpeg, and flac and metaflac
|
||||
for the seek table; the committed fixtures are what CI uses."""
|
||||
|
||||
import math
|
||||
import os
|
||||
@@ -34,4 +35,13 @@ for out in ("tone.flac", "tone.mka"):
|
||||
"-f", "s16le", "-ar", str(RATE), "-ac", "2",
|
||||
"-i", raw, "-c:a", "flac",
|
||||
os.path.join(fix, out)])
|
||||
# The same PCM through the reference encoder, with a seek table: a point
|
||||
# at each of the first frames (ffmpeg writes no table). The seek lane
|
||||
# lands between them.
|
||||
seek = os.path.join(fix, "tone_seektable.flac")
|
||||
subprocess.check_call(["flac", "--silent", "--force", "--force-raw-format",
|
||||
"--endian=little", "--sign=signed", "--channels=2",
|
||||
"--bps=16", "--sample-rate=" + str(RATE), "-S-",
|
||||
"-o", seek, raw])
|
||||
subprocess.check_call(["metaflac", "--add-seekpoint=5x", seek])
|
||||
os.remove(raw)
|
||||
@@ -3,6 +3,7 @@ include ../../sample_objdir.mk
|
||||
TARGET := cdfs_dir_record_test$(EXE)
|
||||
TARGET_TEST2 := cdfs_pure_api_test$(EXE)
|
||||
TARGET_TEST3 := cdfs_io_error_test$(EXE)
|
||||
TARGET_TEST4 := cdfs_file_test$(EXE)
|
||||
.DEFAULT_GOAL := all
|
||||
|
||||
LIBRETRO_COMM_DIR := ../../..
|
||||
@@ -53,6 +54,10 @@ endif
|
||||
|
||||
TEST3_OBJS := $(SAMPLE_OBJDIR)/cdfs_io_error_test.o $(call sample_objs,$(filter-out cdfs_pure_api_test.o $(LIBRETRO_COMM_DIR)/formats/cdfs/cdfs.o,$(TEST2_SOURCES:.c=.o)))
|
||||
|
||||
# cdfs_file_test: the same stack and the path openers over it
|
||||
TEST4_OBJS := $(SAMPLE_OBJDIR)/cdfs_file_test.o $(call sample_objs,$(filter-out cdfs_pure_api_test.o,$(TEST2_SOURCES:.c=.o))) \
|
||||
$(call sample_objs,$(LIBRETRO_COMM_DIR)/streams/cdfs_file.o)
|
||||
|
||||
ifeq ($(OS),Windows_NT)
|
||||
LDLIBS += -lwinmm
|
||||
endif
|
||||
@@ -64,7 +69,10 @@ $(SAMPLE_OBJDIR)/cdfs_io_error_test.o: cdfs_io_error_test.c $(LIBRETRO_COMM_DIR)
|
||||
$(TARGET_TEST3): $(TEST3_OBJS)
|
||||
$(CC) -o $@ $^ $(LDFLAGS) $(LDLIBS)
|
||||
|
||||
all: $(TARGET) $(TARGET_TEST2) $(TARGET_TEST3)
|
||||
$(TARGET_TEST4): $(TEST4_OBJS)
|
||||
$(CC) -o $@ $^ $(LDFLAGS) $(LDLIBS)
|
||||
|
||||
all: $(TARGET) $(TARGET_TEST2) $(TARGET_TEST3) $(TARGET_TEST4)
|
||||
|
||||
%.o: %.c
|
||||
$(CC) -c -o $@ $< $(CFLAGS)
|
||||
@@ -81,6 +89,7 @@ $(TARGET_TEST2): $(TEST2_OBJS)
|
||||
|
||||
clean:
|
||||
rm -rf $(SAMPLE_OBJDIR)
|
||||
rm -f $(TARGET) $(TARGET_TEST2) $(TARGET_TEST3) $(OBJS) $(TEST2_OBJS) $(TEST3_OBJS)
|
||||
rm -f $(TARGET) $(TARGET_TEST2) $(TARGET_TEST3) $(TARGET_TEST4) $(OBJS) \
|
||||
$(TEST2_OBJS) $(TEST3_OBJS) $(TEST4_OBJS)
|
||||
|
||||
.PHONY: clean
|
||||
@@ -0,0 +1,193 @@
|
||||
/* Copyright (C) 2010-2026 The RetroArch team
|
||||
*
|
||||
* ---------------------------------------------------------------------------------------
|
||||
* The following license statement only applies to this file (cdfs_file_test.c).
|
||||
* ---------------------------------------------------------------------------------------
|
||||
*
|
||||
* Permission is hereby granted, free of charge,
|
||||
* to any person obtaining a copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation the rights to
|
||||
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
|
||||
* and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
||||
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
/* streams/cdfs_file.c: tracks opened by filename.
|
||||
*
|
||||
* - a cooked .iso and a raw .bin with no volume descriptor take their
|
||||
* sector layout from their length (2048 and 2352 bytes a sector);
|
||||
* - a cue sheet in a directory of its own names its bin relatively, is
|
||||
* resolved against its own directory, and gives the MODE1/2352 layout
|
||||
* and the INDEX 01 offset of the track it selects;
|
||||
* - cdfs_open_data_track takes a cue's first data track, an .iso as raw;
|
||||
* - each opened track reads back the marker written in its sector 3;
|
||||
* - an unknown extension opens only as track 1, and then only if it is
|
||||
* a bin or an iso.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <formats/cdfs.h>
|
||||
#include <file/file_path.h>
|
||||
#include <streams/cdfs_file.h>
|
||||
#include <streams/file_stream.h>
|
||||
|
||||
#define SECTORS 24
|
||||
|
||||
static int failures;
|
||||
#define CHECK(c) do { if (!(c)) { fprintf(stderr, "%s:%d: check failed: %s\n", \
|
||||
__FILE__, __LINE__, #c); failures++; } } while (0)
|
||||
|
||||
static const char dir[] = "cdfs_file_tmp";
|
||||
static const char iso[] = "cdfs_file_tmp/cooked.iso";
|
||||
static const char bin[] = "cdfs_file_tmp/raw.bin";
|
||||
static const char cue[] = "cdfs_file_tmp/disc.cue";
|
||||
static const char pregap[] = "cdfs_file_tmp/pregap.bin";
|
||||
static const char pre_cue[] = "cdfs_file_tmp/pregap.cue";
|
||||
static const char odd[] = "cdfs_file_tmp/disc.img";
|
||||
|
||||
/* @sectors of @size bytes, user data at @header; sector s's data
|
||||
* starts with "S<s>" and the rest of every byte is s */
|
||||
static int write_image(const char *path, unsigned sectors, unsigned size,
|
||||
unsigned header, unsigned lead)
|
||||
{
|
||||
uint8_t buf[2352];
|
||||
unsigned s;
|
||||
RFILE *f = filestream_open(path, RETRO_VFS_FILE_ACCESS_WRITE,
|
||||
RETRO_VFS_FILE_ACCESS_HINT_NONE);
|
||||
if (!f)
|
||||
return 0;
|
||||
memset(buf, 0xEE, sizeof(buf));
|
||||
for (s = 0; s < lead; s++)
|
||||
filestream_write(f, buf, size);
|
||||
for (s = 0; s < sectors; s++)
|
||||
{
|
||||
memset(buf, (int)s, size);
|
||||
sprintf((char*)buf + header, "S%u", s);
|
||||
if (filestream_write(f, buf, size) != (int64_t)size)
|
||||
{
|
||||
filestream_close(f);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
filestream_close(f);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int write_text(const char *path, const char *text)
|
||||
{
|
||||
return filestream_write_file(path, text, (int64_t)strlen(text));
|
||||
}
|
||||
|
||||
/* Sector 3 of the track, through the cdfs raw view */
|
||||
static void reads_marker(cdfs_track_t *track, const char *what)
|
||||
{
|
||||
cdfs_file_t file;
|
||||
char got[8];
|
||||
(void)what;
|
||||
memset(got, 0, sizeof(got));
|
||||
CHECK(cdfs_open_file(&file, track, NULL) == 1);
|
||||
cdfs_seek_sector(&file, 3);
|
||||
CHECK(cdfs_read_file(&file, got, 2) == 2);
|
||||
CHECK(memcmp(got, "S3", 2) == 0);
|
||||
cdfs_close_file(&file);
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
cdfs_track_t *t;
|
||||
|
||||
path_mkdir(dir);
|
||||
CHECK(write_image(iso, SECTORS, 2048, 0, 0));
|
||||
CHECK(write_image(bin, SECTORS, 2352, 16, 0));
|
||||
/* two sectors ahead of INDEX 01 */
|
||||
CHECK(write_image(pregap, SECTORS, 2352, 16, 2));
|
||||
CHECK(write_image(odd, SECTORS, 2048, 0, 0));
|
||||
CHECK(write_text(cue,
|
||||
"FILE \"raw.bin\" BINARY\n"
|
||||
" TRACK 01 MODE1/2352\n"
|
||||
" INDEX 01 00:00:00\n"));
|
||||
CHECK(write_text(pre_cue,
|
||||
"FILE \"pregap.bin\" BINARY\n"
|
||||
" TRACK 01 MODE1/2352\n"
|
||||
" INDEX 00 00:00:00\n"
|
||||
" INDEX 01 00:00:02\n"));
|
||||
|
||||
/* a cooked iso: 2048 from the length */
|
||||
if ((t = cdfs_open_raw_track(iso)))
|
||||
{
|
||||
CHECK(t->stream_sector_size == 2048 && t->stream_sector_header_size == 0);
|
||||
reads_marker(t, "iso");
|
||||
cdfs_close_track(t);
|
||||
}
|
||||
else
|
||||
failures++;
|
||||
|
||||
/* a raw bin: 2352 from the length */
|
||||
if ((t = cdfs_open_raw_track(bin)))
|
||||
{
|
||||
CHECK(t->stream_sector_size == 2352);
|
||||
cdfs_close_track(t);
|
||||
}
|
||||
else
|
||||
failures++;
|
||||
|
||||
/* the cue, its bin found beside it */
|
||||
if ((t = cdfs_open_track(cue, 1)))
|
||||
{
|
||||
CHECK(t->stream_sector_size == 2352 && t->stream_sector_header_size == 16);
|
||||
CHECK(t->first_sector_offset == 0);
|
||||
reads_marker(t, "cue");
|
||||
cdfs_close_track(t);
|
||||
}
|
||||
else
|
||||
failures++;
|
||||
|
||||
/* INDEX 01 two sectors in */
|
||||
if ((t = cdfs_open_track(pre_cue, 1)))
|
||||
{
|
||||
CHECK(t->first_sector_offset == 2 * 2352);
|
||||
reads_marker(t, "cue pregap");
|
||||
cdfs_close_track(t);
|
||||
}
|
||||
else
|
||||
failures++;
|
||||
|
||||
/* first data track: of the cue, and an iso as raw */
|
||||
if ((t = cdfs_open_data_track(cue)))
|
||||
{
|
||||
CHECK(t->stream_sector_size == 2352);
|
||||
cdfs_close_track(t);
|
||||
}
|
||||
else
|
||||
failures++;
|
||||
if ((t = cdfs_open_data_track(iso)))
|
||||
{
|
||||
CHECK(t->stream_sector_size == 2048);
|
||||
cdfs_close_track(t);
|
||||
}
|
||||
else
|
||||
failures++;
|
||||
|
||||
/* not a bin, iso, cue or chd */
|
||||
CHECK(cdfs_open_track(odd, 2) == NULL);
|
||||
CHECK(cdfs_open_track(odd, 1) == NULL);
|
||||
CHECK(cdfs_open_track("cdfs_file_tmp/missing.cue", 1) == NULL);
|
||||
|
||||
remove(iso); remove(bin); remove(pregap); remove(odd);
|
||||
remove(cue); remove(pre_cue);
|
||||
remove(dir);
|
||||
printf("cdfs file: %s\n", failures ? "FAIL" : "ok");
|
||||
return failures ? 1 : 0;
|
||||
}
|
||||
@@ -161,7 +161,7 @@ int main(void)
|
||||
memset(&layout, 0, sizeof(layout));
|
||||
layout.stream = track.stream;
|
||||
sparse_size = UINT64_C(4194307) * sizes[mode];
|
||||
cdfs_determine_sector_size_from_file_size(&layout);
|
||||
cdfs_track_sector_size_from_length(&layout);
|
||||
CHECK(layout.stream_sector_size == sizes[mode]);
|
||||
sparse_size = UINT64_C(4194304) * sizes[mode] + 12345;
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@ COMMON_SOURCES := \
|
||||
$(LIBRETRO_COMM_DIR)/encodings/encoding_utf.c \
|
||||
$(LIBRETRO_COMM_DIR)/file/file_path.c \
|
||||
$(LIBRETRO_COMM_DIR)/file/file_path_io.c \
|
||||
$(LIBRETRO_COMM_DIR)/formats/data_transfer.c \
|
||||
$(LIBRETRO_COMM_DIR)/formats/data_transfer.c $(LIBRETRO_COMM_DIR)/streams/data_transfer_file.c \
|
||||
$(LIBRETRO_COMM_DIR)/memmap/memmap.c \
|
||||
$(LIBRETRO_COMM_DIR)/streams/file_stream.c \
|
||||
$(LIBRETRO_COMM_DIR)/string/rstrtod.c \
|
||||
|
||||
@@ -63,6 +63,7 @@
|
||||
|
||||
#include <boolean.h>
|
||||
#include <formats/data_transfer.h>
|
||||
#include <streams/data_transfer_file.h>
|
||||
|
||||
static int test_fails;
|
||||
|
||||
|
||||
@@ -49,6 +49,7 @@
|
||||
#include <string.h>
|
||||
#include <stdint.h>
|
||||
#include <formats/data_transfer.h>
|
||||
#include <streams/data_transfer_file.h>
|
||||
#include <memmap.h>
|
||||
#ifdef DT_COUNT_IO
|
||||
#include <streams/file_stream.h>
|
||||
|
||||
@@ -66,6 +66,7 @@
|
||||
#include <string.h>
|
||||
#include <stdint.h>
|
||||
#include <formats/data_transfer.h>
|
||||
#include <streams/data_transfer_file.h>
|
||||
#include <memmap.h>
|
||||
#ifdef DT_COUNT_IO
|
||||
#include <streams/file_stream.h>
|
||||
@@ -1046,6 +1047,76 @@ static int callback_window_check(void)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* A consumer far into a large file - one that starts there, moves on,
|
||||
* then steps back, which the feed takes for a lap - is fed from margin
|
||||
* bytes behind it: the gap it has passed is never read in, whatever the
|
||||
* frontier was. */
|
||||
typedef struct far_source { size_t length; size_t bytes; } far_source;
|
||||
static int64_t far_read_at(void *ud, uint64_t offset, uint8_t *dst,
|
||||
size_t bytes)
|
||||
{
|
||||
far_source *src = (far_source*)ud;
|
||||
size_t i;
|
||||
if (offset > src->length || bytes > src->length - (size_t)offset)
|
||||
return -1;
|
||||
for (i = 0; i < bytes; i++)
|
||||
dst[i] = (uint8_t)((offset + i) * 29 + 7);
|
||||
src->bytes += bytes;
|
||||
return (int64_t)bytes;
|
||||
}
|
||||
static int far_matches(const uint8_t *base, size_t at, size_t n)
|
||||
{
|
||||
size_t i;
|
||||
for (i = 0; i < n; i++)
|
||||
if (base[at + i] != (uint8_t)((at + i) * 29 + 7))
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
static int far_feed_check(void)
|
||||
{
|
||||
far_source src;
|
||||
data_transfer_t *dt;
|
||||
const uint8_t *base;
|
||||
size_t blen = 0, step, tell, bound = MARGIN + LOOKAHD + (2u << 20);
|
||||
const size_t len = 256u << 20;
|
||||
if (!data_transfer_reserve_supported())
|
||||
{
|
||||
printf("[skip] far feed: no reservations on this build\n");
|
||||
return 0;
|
||||
}
|
||||
memset(&src, 0, sizeof(src));
|
||||
src.length = len;
|
||||
if (!(dt = data_transfer_open_window_reader(len, KEEP, far_read_at, &src)))
|
||||
{ fail("far feed: open"); return 1; }
|
||||
base = data_transfer_window_base(dt, &blen);
|
||||
for (step = 0; step < 3; step++)
|
||||
{
|
||||
size_t before = src.bytes;
|
||||
tell = len - (1u << 20) + (step == 1 ? 300u * 1024 : step == 2 ? 260u * 1024 : 0);
|
||||
if (!data_transfer_window_feed(dt, tell, LOOKAHD, MARGIN)
|
||||
|| !far_matches(base, tell, 4096))
|
||||
{
|
||||
printf("[FAIL] far feed: step %u, bytes at tell\n", (unsigned)step);
|
||||
bad = 1;
|
||||
break;
|
||||
}
|
||||
if (src.bytes - before > bound
|
||||
|| data_transfer_window_resident(dt) > KEEP + bound)
|
||||
{
|
||||
printf("[FAIL] far feed: step %u read %lu bytes, resident %lu\n",
|
||||
(unsigned)step, (unsigned long)(src.bytes - before),
|
||||
(unsigned long)data_transfer_window_resident(dt));
|
||||
bad = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
data_transfer_free(dt);
|
||||
if (step < 3)
|
||||
return 1;
|
||||
ok("far feed: started, moved and stepped back far into a file, read only near the consumer");
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
const char *path = "dtwin.bin";
|
||||
@@ -1154,6 +1225,7 @@ int main(void)
|
||||
#endif
|
||||
|
||||
bad |= callback_window_check();
|
||||
bad |= far_feed_check();
|
||||
bad |= sparse_skip_check();
|
||||
bad |= pin_check();
|
||||
bad |= feed_budget_check();
|
||||
|
||||
@@ -34,11 +34,11 @@ $(TARGET_TEST3): rdvd_ifo_test.c ../../../formats/dvd/rdvd_ifo.c ../../../includ
|
||||
$(TARGET_TEST4): rdvd_source_test.c ../../../formats/dvd/rdvd_source.c ../../../formats/dvd/rdvd_ifo.c ../../../include/formats/rdvd_source.h ../../../include/formats/rdvd_ifo.h
|
||||
$(CC) $(CFLAGS) rdvd_source_test.c ../../../formats/dvd/rdvd_source.c ../../../formats/dvd/rdvd_ifo.c -o $@ $(LDFLAGS)
|
||||
|
||||
DVD_STREAM_HEADERS := ../../../include/formats/rdvd_stream.h ../../../include/formats/rdvd_demux.h
|
||||
DVD_STREAM_SOURCES := ../../../formats/dvd/rdvd_stream.c ../../../formats/dvd/rdvd_demux.c ../../../formats/dvd/rdvd_packet.c ../../../formats/mpeg1/rmpeg1_ps.c
|
||||
DVD_STREAM_HEADERS := ../../../include/streams/rdvd_stream.h ../../../include/formats/rdvd_demux.h
|
||||
DVD_STREAM_SOURCES := ../../../streams/rdvd_stream.c ../../../formats/dvd/rdvd_demux.c ../../../formats/dvd/rdvd_packet.c ../../../formats/mpeg1/rmpeg1_ps.c
|
||||
|
||||
$(TARGET_TEST5): $(DVD_STREAM_HEADERS) $(DVD_STREAM_SOURCES) rdvd_vfs_test.c ../../../formats/dvd/rdvd_vfs.c ../../../formats/dvd/rdvd_source.c ../../../formats/dvd/rdvd_ifo.c ../../../queues/retro_spsc.c ../../../include/formats/rdvd_vfs.h
|
||||
$(CC) $(CFLAGS) -DHAVE_THREADS $(DVD_STREAM_SOURCES) rdvd_vfs_test.c ../../../formats/dvd/rdvd_vfs.c ../../../formats/dvd/rdvd_source.c ../../../formats/dvd/rdvd_ifo.c ../../../queues/retro_spsc.c -o $@ $(LDFLAGS)
|
||||
$(TARGET_TEST5): $(DVD_STREAM_HEADERS) $(DVD_STREAM_SOURCES) rdvd_vfs_test.c ../../../streams/rdvd_vfs.c ../../../formats/dvd/rdvd_source.c ../../../formats/dvd/rdvd_ifo.c ../../../queues/retro_spsc.c ../../../include/streams/rdvd_vfs.h
|
||||
$(CC) $(CFLAGS) -DHAVE_THREADS $(DVD_STREAM_SOURCES) rdvd_vfs_test.c ../../../streams/rdvd_vfs.c ../../../formats/dvd/rdvd_source.c ../../../formats/dvd/rdvd_ifo.c ../../../queues/retro_spsc.c -o $@ $(LDFLAGS)
|
||||
|
||||
ifneq ($(findstring mingw,$(shell $(CC) -dumpmachine)),)
|
||||
VFS_REAL_LIBS :=
|
||||
@@ -46,8 +46,8 @@ else
|
||||
VFS_REAL_LIBS := -lpthread -lrt
|
||||
endif
|
||||
VFS_REAL_SOURCES := ../../../formats/dvd/rdvd_lpcm.c ../../../formats/lpcm/rlpcm.c ../../../formats/dvd/rdvd_ac3.c ../../../formats/ac3/rac3_frame.c ../../../formats/ac3/rac3_decode.c ../../../rthreads/tpool.c ../../../rthreads/retro_eventcount.c ../../../rthreads/rthreads.c ../../../features/features_cpu.c ../../../compat/fopen_utf8.c ../../../compat/compat_strl.c ../../../encodings/encoding_utf.c ../../../file/file_path.c ../../../file/file_path_io.c ../../../file/retro_dirent.c ../../../streams/file_stream.c ../../../string/rstrtod.c ../../../time/rtime.c ../../../vfs/vfs_implementation.c
|
||||
$(TARGET_TEST6): $(DVD_STREAM_HEADERS) $(DVD_STREAM_SOURCES) rdvd_vfs_real_test.c ../../../formats/dvd/rdvd_vfs.c ../../../formats/dvd/rdvd_source.c ../../../formats/dvd/rdvd_ifo.c ../../../queues/retro_spsc.c $(VFS_REAL_SOURCES)
|
||||
$(CC) $(filter-out -std=c89 -Werror,$(CFLAGS)) -std=gnu99 -DHAVE_THREADS $(DVD_STREAM_SOURCES) rdvd_vfs_real_test.c ../../../formats/dvd/rdvd_vfs.c ../../../formats/dvd/rdvd_source.c ../../../formats/dvd/rdvd_ifo.c ../../../queues/retro_spsc.c $(VFS_REAL_SOURCES) -o $@ $(LDFLAGS) $(VFS_REAL_LIBS) -lm
|
||||
$(TARGET_TEST6): $(DVD_STREAM_HEADERS) $(DVD_STREAM_SOURCES) rdvd_vfs_real_test.c ../../../streams/rdvd_vfs.c ../../../formats/dvd/rdvd_source.c ../../../formats/dvd/rdvd_ifo.c ../../../queues/retro_spsc.c $(VFS_REAL_SOURCES)
|
||||
$(CC) $(filter-out -std=c89 -Werror,$(CFLAGS)) -std=gnu99 -DHAVE_THREADS $(DVD_STREAM_SOURCES) rdvd_vfs_real_test.c ../../../streams/rdvd_vfs.c ../../../formats/dvd/rdvd_source.c ../../../formats/dvd/rdvd_ifo.c ../../../queues/retro_spsc.c $(VFS_REAL_SOURCES) -o $@ $(LDFLAGS) $(VFS_REAL_LIBS) -lm
|
||||
|
||||
AC3_SOURCES := ../../../formats/dvd/rdvd_ac3.c ../../../formats/ac3/rac3_frame.c
|
||||
$(TARGET_TEST7): rdvd_ac3_test.c $(AC3_SOURCES) ../../../include/formats/rdvd_ac3.h
|
||||
|
||||
@@ -9,8 +9,8 @@
|
||||
#else
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
#include <formats/rdvd_vfs.h>
|
||||
#include <formats/rdvd_stream.h>
|
||||
#include <streams/rdvd_vfs.h>
|
||||
#include <streams/rdvd_stream.h>
|
||||
#include <rthreads/rthreads.h>
|
||||
#include <retro_atomic.h>
|
||||
#include <features/features_cpu.h>
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
/* Copyright (C) 2026 The RetroArch team. SPDX-License-Identifier: MIT */
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <formats/rdvd_vfs.h>
|
||||
#include <formats/rdvd_stream.h>
|
||||
#include <streams/rdvd_vfs.h>
|
||||
#include <streams/rdvd_stream.h>
|
||||
struct RFILE
|
||||
{
|
||||
uint64_t size, position;
|
||||
|
||||
@@ -18,9 +18,9 @@ SOURCES := image_loader_test.c \
|
||||
$(LIBRETRO_COMM_DIR)/features/features_cpu.c \
|
||||
$(LIBRETRO_COMM_DIR)/file/file_path.c \
|
||||
$(LIBRETRO_COMM_DIR)/file/file_path_io.c \
|
||||
$(LIBRETRO_COMM_DIR)/formats/data_transfer.c \
|
||||
$(LIBRETRO_COMM_DIR)/formats/data_transfer.c $(LIBRETRO_COMM_DIR)/streams/data_transfer_file.c \
|
||||
$(LIBRETRO_COMM_DIR)/formats/dds/rdds.c \
|
||||
$(LIBRETRO_COMM_DIR)/formats/image_texture.c \
|
||||
$(LIBRETRO_COMM_DIR)/formats/image_texture.c $(LIBRETRO_COMM_DIR)/streams/image_texture_file.c \
|
||||
$(LIBRETRO_COMM_DIR)/formats/image_texture_scale.c \
|
||||
$(LIBRETRO_COMM_DIR)/formats/image_texture_set.c \
|
||||
$(LIBRETRO_COMM_DIR)/formats/image_transfer.c \
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
#include <boolean.h>
|
||||
#include <encodings/crc32.h>
|
||||
#include <formats/image.h>
|
||||
#include <streams/image_texture_file.h>
|
||||
|
||||
#define W 24
|
||||
#define H 16
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
include ../../sample_objdir.mk
|
||||
|
||||
TARGET := rflac_s16_test
|
||||
TARGET_TEST := rflac_s16_nosimd_test
|
||||
|
||||
LIBRETRO_COMM_DIR := ../../..
|
||||
|
||||
SOURCES := \
|
||||
$(LIBRETRO_COMM_DIR)/compat/fopen_utf8.c \
|
||||
$(LIBRETRO_COMM_DIR)/compat/compat_strl.c \
|
||||
$(LIBRETRO_COMM_DIR)/encodings/encoding_utf.c \
|
||||
$(LIBRETRO_COMM_DIR)/features/features_cpu.c \
|
||||
$(LIBRETRO_COMM_DIR)/file/file_path.c \
|
||||
$(LIBRETRO_COMM_DIR)/file/file_path_io.c \
|
||||
$(LIBRETRO_COMM_DIR)/streams/file_stream.c \
|
||||
$(LIBRETRO_COMM_DIR)/string/rstrtod.c \
|
||||
$(LIBRETRO_COMM_DIR)/string/stdstring.c \
|
||||
$(LIBRETRO_COMM_DIR)/time/rtime.c \
|
||||
$(LIBRETRO_COMM_DIR)/vfs/vfs_implementation.c
|
||||
|
||||
OBJS := $(call sample_objs,$(SOURCES:.c=.o))
|
||||
|
||||
CFLAGS += -Wall -pedantic -std=gnu99 -g1 -O1 -I$(LIBRETRO_COMM_DIR)/include
|
||||
LDFLAGS += -lm
|
||||
|
||||
ifneq ($(SANITIZER),)
|
||||
CFLAGS := -fsanitize=$(SANITIZER) -fno-omit-frame-pointer $(CFLAGS)
|
||||
LDFLAGS := -fsanitize=$(SANITIZER) $(LDFLAGS)
|
||||
endif
|
||||
|
||||
all: $(TARGET) $(TARGET_TEST)
|
||||
|
||||
$(SAMPLE_OBJDIR)/%.o: $$(call sample_src,$$*).c
|
||||
@mkdir -p $(@D)
|
||||
$(CC) -c -o $@ $< $(CFLAGS)
|
||||
|
||||
# rflac itself twice: with the SIMD loops the target has, and without
|
||||
$(SAMPLE_OBJDIR)/rflac_simd.o: $(LIBRETRO_COMM_DIR)/formats/flac/rflac.c
|
||||
@mkdir -p $(@D)
|
||||
$(CC) -c -o $@ $< $(CFLAGS)
|
||||
|
||||
$(SAMPLE_OBJDIR)/rflac_nosimd.o: $(LIBRETRO_COMM_DIR)/formats/flac/rflac.c
|
||||
@mkdir -p $(@D)
|
||||
$(CC) -c -o $@ $< $(CFLAGS) -DRFLAC_NO_SIMD
|
||||
|
||||
$(TARGET): rflac_s16_test.o $(SAMPLE_OBJDIR)/rflac_simd.o $(OBJS)
|
||||
$(CC) -o $@ $^ $(LDFLAGS)
|
||||
|
||||
$(TARGET_TEST): rflac_s16_test.o $(SAMPLE_OBJDIR)/rflac_nosimd.o $(OBJS)
|
||||
$(CC) -o $@ $^ $(LDFLAGS)
|
||||
|
||||
rflac_s16_test.o: rflac_s16_test.c
|
||||
$(CC) -c -o $@ $< $(CFLAGS)
|
||||
|
||||
check: all
|
||||
./$(TARGET)
|
||||
./$(TARGET_TEST)
|
||||
|
||||
clean:
|
||||
rm -rf $(SAMPLE_OBJDIR)
|
||||
rm -f $(TARGET) $(TARGET_TEST) rflac_s16_test.o
|
||||
|
||||
.PHONY: all check clean
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,104 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Regenerates the fixtures of rflac_s16_test: FLAC streams of 16, 24 and
|
||||
32 bits, mono, stereo and six channels, made by the reference encoder
|
||||
from PCM built with integers only, which the test builds again the same
|
||||
way and so knows every sample of. The stereo streams are made so that the
|
||||
encoder picks each of the four channel assignments somewhere (checked
|
||||
here with flac --analyze), and the 32-bit one so that its side channel
|
||||
needs 33 bits. Needs flac; the committed fixtures are what CI uses."""
|
||||
|
||||
import os
|
||||
import subprocess
|
||||
import struct
|
||||
|
||||
FRAMES = 5003 # not a multiple of 4: the SIMD loops' scalar tails run too
|
||||
here = os.path.dirname(os.path.abspath(__file__))
|
||||
fix = os.path.join(here, "fixtures")
|
||||
os.makedirs(fix, exist_ok=True)
|
||||
|
||||
|
||||
def lcg(seed):
|
||||
while True:
|
||||
seed = (seed * 1103515245 + 12345) & 0xffffffff
|
||||
yield seed
|
||||
|
||||
|
||||
def tri(i, period, amp):
|
||||
"""A triangle wave, integer only: rises then falls over period frames."""
|
||||
p = i % period
|
||||
half = period // 2
|
||||
v = p if p < half else period - p
|
||||
return (v * 2 * amp) // half - amp
|
||||
|
||||
|
||||
def sample(kind, bps, ch, i, c, noise):
|
||||
top = (1 << (bps - 1)) - 1
|
||||
n = next(noise) >> 20 # 12 bits of noise
|
||||
if kind == "stereo":
|
||||
# Frames 0-1023 the two noisy about one wave (mid/side), 1024-2047
|
||||
# left clean and right the same with noise (left/side), 2048-3071
|
||||
# the other way round (side/right), the rest unrelated
|
||||
# (independent)
|
||||
base = tri(i, 397, top // 2)
|
||||
seg = i // 1024
|
||||
if seg == 0:
|
||||
v = base + (n if c else -n)
|
||||
elif seg == 1:
|
||||
v = base if c == 0 else base + n
|
||||
elif seg == 2:
|
||||
v = base + n if c == 0 else base
|
||||
else:
|
||||
v = tri(i, 397 + 113 * c, top // 3) + n * (c + 1)
|
||||
elif kind == "wide":
|
||||
# Full scale and opposite: left - right needs 33 bits
|
||||
v = top - (n & 255) if c == 0 else -top + (n & 255)
|
||||
if i % 7 == 0:
|
||||
v = -v
|
||||
else:
|
||||
v = tri(i, 211 + 37 * c, top // 2) + n
|
||||
v = max(-top - 1, min(top, v))
|
||||
return v
|
||||
|
||||
|
||||
def pcm(kind, bps, ch):
|
||||
noise = lcg(bps * 131 + ch)
|
||||
out = bytearray()
|
||||
width = (bps + 7) // 8
|
||||
for i in range(FRAMES):
|
||||
for c in range(ch):
|
||||
v = sample(kind, bps, ch, i, c, noise)
|
||||
out += (v & ((1 << (width * 8)) - 1)).to_bytes(width, "little")
|
||||
return out
|
||||
|
||||
|
||||
FIXTURES = [
|
||||
("s16_stereo.flac", "stereo", 16, 2),
|
||||
("s24_stereo.flac", "stereo", 24, 2),
|
||||
("s24_mono.flac", "mono", 24, 1),
|
||||
("s24_6ch.flac", "multi", 24, 6),
|
||||
("s32_wide.flac", "wide", 32, 2),
|
||||
]
|
||||
|
||||
seen = set()
|
||||
for name, kind, bps, ch in FIXTURES:
|
||||
raw = os.path.join(fix, "src.raw")
|
||||
out = os.path.join(fix, name)
|
||||
with open(raw, "wb") as f:
|
||||
f.write(pcm(kind, bps, ch))
|
||||
subprocess.check_call(["flac", "--silent", "--force", "--force-raw-format",
|
||||
"--endian=little", "--sign=signed",
|
||||
"--channels=" + str(ch), "--bps=" + str(bps),
|
||||
"--sample-rate=48000", "-8", "-b", "1024",
|
||||
"-o", out, raw])
|
||||
os.remove(raw)
|
||||
if ch == 2:
|
||||
a = subprocess.run(["flac", "--analyze", "--stdout", out],
|
||||
capture_output=True, text=True).stdout
|
||||
for line in a.splitlines():
|
||||
for tag in ("INDEPENDENT", "LEFT_SIDE", "RIGHT_SIDE", "MID_SIDE"):
|
||||
if "channel_assignment=" + tag in line:
|
||||
seen.add(tag)
|
||||
missing = {"INDEPENDENT", "LEFT_SIDE", "RIGHT_SIDE", "MID_SIDE"} - seen
|
||||
if missing:
|
||||
raise SystemExit("channel assignments not made: " + ", ".join(sorted(missing)))
|
||||
print("assignments:", ", ".join(sorted(seen)))
|
||||
@@ -0,0 +1,157 @@
|
||||
/* rflac's 16-bit output, against the PCM each fixture was encoded from:
|
||||
*
|
||||
* - a stream of 16 bits comes out exactly as it went in;
|
||||
* - one of 24 or 32 bits comes out rounded to the nearest 16-bit step
|
||||
* (halves up) and clamped at the top - not cut down, which left every
|
||||
* sample half a step low on average;
|
||||
* - both, through every way a stereo frame's channels are coded
|
||||
* (independent, left/side, side/right, mid/side, which the stereo
|
||||
* fixtures hold each of), a 32-bit stream's 33-bit side channel, mono
|
||||
* and six channels, and the SIMD loops and their scalar tails (the
|
||||
* fixtures are 5003 frames).
|
||||
*
|
||||
* Built twice: as the target builds (SSE2 on x86-64, NEON on ARM) and with
|
||||
* RFLAC_NO_SIMD, the scalar loops. The PCM is rebuilt here exactly as
|
||||
* mkfixtures.py builds it, with integers only. */
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <formats/rflac.h>
|
||||
#include <streams/file_stream.h>
|
||||
|
||||
#define FRAMES 5003
|
||||
|
||||
static unsigned failures;
|
||||
|
||||
#define CHECK(cond, ...) do { if (!(cond)) { failures++; \
|
||||
fprintf(stderr, "FAIL %s:%d: ", __FILE__, __LINE__); \
|
||||
fprintf(stderr, __VA_ARGS__); fputc('\n', stderr); } } while (0)
|
||||
|
||||
static int64_t tri(int64_t i, int64_t period, int64_t amp)
|
||||
{
|
||||
int64_t p = i % period;
|
||||
int64_t half = period / 2;
|
||||
int64_t v = p < half ? p : period - p;
|
||||
return (v * 2 * amp) / half - amp;
|
||||
}
|
||||
|
||||
/* mkfixtures.py's sample(), step for step */
|
||||
static int64_t sample(const char *kind, unsigned bps, unsigned c, int64_t i,
|
||||
uint32_t *seed)
|
||||
{
|
||||
int64_t top = ((int64_t)1 << (bps - 1)) - 1;
|
||||
int64_t n, v;
|
||||
*seed = *seed * 1103515245u + 12345u;
|
||||
n = (int64_t)(*seed >> 20);
|
||||
if (!strcmp(kind, "stereo"))
|
||||
{
|
||||
int64_t base = tri(i, 397, top / 2);
|
||||
int64_t seg = i / 1024;
|
||||
if (seg == 0)
|
||||
v = base + (c ? n : -n);
|
||||
else if (seg == 1)
|
||||
v = c == 0 ? base : base + n;
|
||||
else if (seg == 2)
|
||||
v = c == 0 ? base + n : base;
|
||||
else
|
||||
v = tri(i, 397 + 113 * c, top / 3) + n * (c + 1);
|
||||
}
|
||||
else if (!strcmp(kind, "wide"))
|
||||
{
|
||||
v = c == 0 ? top - (n & 255) : -top + (n & 255);
|
||||
if (i % 7 == 0)
|
||||
v = -v;
|
||||
}
|
||||
else
|
||||
v = tri(i, 211 + 37 * c, top / 2) + n;
|
||||
if (v > top)
|
||||
v = top;
|
||||
if (v < -top - 1)
|
||||
v = -top - 1;
|
||||
return v;
|
||||
}
|
||||
|
||||
/* The 16-bit value a sample of @bps bits should give: its value at 16
|
||||
* bits, rounded to the nearest step, halves up, the top clamped */
|
||||
static int16_t expect(int64_t v, unsigned bps)
|
||||
{
|
||||
int64_t r;
|
||||
if (bps <= 16)
|
||||
return (int16_t)(v * ((int64_t)1 << (16 - bps)));
|
||||
r = (v + ((int64_t)1 << (bps - 17))) >> (bps - 16);
|
||||
return (int16_t)(r > 32767 ? 32767 : r);
|
||||
}
|
||||
|
||||
static void run(const char *name, const char *kind, unsigned bps, unsigned ch)
|
||||
{
|
||||
char path[256];
|
||||
void *buf = NULL;
|
||||
int64_t len = 0;
|
||||
rflac_t *f = rflac_new();
|
||||
int16_t *out = (int16_t*)calloc((size_t)(FRAMES + 64) * ch, sizeof(int16_t));
|
||||
size_t got = 0, i;
|
||||
uint32_t seed = bps * 131 + ch;
|
||||
unsigned bad = 0, idle = 0;
|
||||
long sum_err = 0;
|
||||
int result;
|
||||
|
||||
snprintf(path, sizeof(path), "fixtures/%s", name);
|
||||
if (!filestream_read_file(path, &buf, &len) || len <= 0)
|
||||
{
|
||||
fprintf(stderr, "cannot read %s (run from the sample directory)\n", path);
|
||||
exit(2);
|
||||
}
|
||||
CHECK(f != NULL, "%s: rflac_new", name);
|
||||
rflac_set_in(f, (const uint8_t*)buf, (size_t)len);
|
||||
rflac_set_eof(f);
|
||||
do
|
||||
{
|
||||
size_t wrote = 0;
|
||||
rflac_set_out_s16(f, out + got * ch, FRAMES + 64 - got);
|
||||
result = rflac_process(f, NULL, &wrote);
|
||||
got += wrote;
|
||||
idle = wrote ? 0 : idle + 1;
|
||||
} while (result == RFLAC_PROCESS_NEXT && idle < 4);
|
||||
|
||||
CHECK(got == FRAMES, "%s: %lu frames, %d wanted", name, (unsigned long)got,
|
||||
FRAMES);
|
||||
for (i = 0; i < got * ch && i < (size_t)FRAMES * ch; i++)
|
||||
{
|
||||
int64_t v = sample(kind, bps, (unsigned)(i % ch), (int64_t)(i / ch), &seed);
|
||||
int16_t want = expect(v, bps);
|
||||
if (out[i] != want)
|
||||
{
|
||||
if (!bad)
|
||||
fprintf(stderr, " %s: sample %lu is %d, %d wanted (from %lld)\n",
|
||||
name, (unsigned long)i, out[i], want, (long long)v);
|
||||
bad++;
|
||||
}
|
||||
sum_err += out[i] - want;
|
||||
}
|
||||
CHECK(bad == 0, "%s: %u of %lu samples off", name, bad,
|
||||
(unsigned long)(got * ch));
|
||||
(void)sum_err;
|
||||
rflac_free(f);
|
||||
free(out);
|
||||
free(buf);
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
run("s16_stereo.flac", "stereo", 16, 2);
|
||||
run("s24_stereo.flac", "stereo", 24, 2);
|
||||
run("s24_mono.flac", "mono", 24, 1);
|
||||
run("s24_6ch.flac", "multi", 24, 6);
|
||||
run("s32_wide.flac", "wide", 32, 2);
|
||||
|
||||
if (failures)
|
||||
{
|
||||
fprintf(stderr, "%u failure(s)\n", failures);
|
||||
return 1;
|
||||
}
|
||||
printf("[pass] rflac_s16_test\n");
|
||||
return 0;
|
||||
}
|
||||
Loaded 100 of 184 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user