mirror of
https://github.com/libretro/parallel-dc.git
synced 2026-11-07 04:25:58 -10:00
A CHD was read through one decoded hunk and a thread that decoded one hunk ahead. A game that plays CD audio while it streams data reads the two from different places a sector at a time, so each threw out the other's hunk and aimed the thread away from it, and the hunks were decoded on the emulation thread: a stutter a GDI never shows. Each stream (data tracks, audio tracks) now reads through its own libretro-common vfs_prefetch, whose thread decodes the hunks ahead on an image handle of its own, one window per hunk; sectors are lent from the windows in place, and a read command's Prefetch() sets the data stream's thread going before the first sector is asked for. The core defines HAVE_THREADS where it builds threads, so the prefetcher is not compiled to stubs. tools/chd/chd_streams_test, run from tools/chd/run.sh and now in CI, reads audio and data at once and fails when the reading thread does the decoding.
103 lines
4.2 KiB
C
103 lines
4.2 KiB
C
/* Copyright (C) 2026 The RetroArch team
|
|
*
|
|
* ---------------------------------------------------------------------------------------
|
|
* The following license statement only applies to this file (vfs_prefetch.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_VFS_PREFETCH_H
|
|
#define __LIBRETRO_SDK_VFS_PREFETCH_H
|
|
|
|
#include <stdint.h>
|
|
#include <stddef.h>
|
|
|
|
#include <retro_common_api.h>
|
|
|
|
RETRO_BEGIN_DECLS
|
|
|
|
/* A background prefetcher for a file read over a network: a thread of
|
|
* its own fetches the windows ahead of the reader on a connection of
|
|
* its own, so a run of sequential reads is served from memory and the
|
|
* reading thread never waits for the network. The reader's side is
|
|
* lock-free: a read copies from a ready window or reports a miss (and
|
|
* the caller reads the network itself, as it would without this), and
|
|
* a seek out of the fetched range retargets the thread.
|
|
*
|
|
* Only built with HAVE_THREADS; without it vfs_prefetch_new() returns
|
|
* NULL and everything else is a no-op, so callers keep one code path. */
|
|
|
|
struct vfs_prefetch;
|
|
|
|
/* Read @len octets at @off into @buf; the octets read (short at end of
|
|
* file), -1 on failure. Runs on the prefetch thread, on that thread's
|
|
* own connection. */
|
|
typedef int64_t (*vfs_prefetch_fetch_t)(void *user, uint64_t off, uint8_t *buf, size_t len);
|
|
|
|
/**
|
|
* vfs_prefetch_new:
|
|
* @window : octets per window (a client's read-ahead size fits)
|
|
* @slots : windows kept ahead, 2 or more; memory is window x slots
|
|
* @start : the offset to begin at (0 for a fresh file)
|
|
*
|
|
* Starts the thread. NULL when threads are unavailable or memory is.
|
|
**/
|
|
struct vfs_prefetch *vfs_prefetch_new(vfs_prefetch_fetch_t fetch, void *user,
|
|
size_t window, unsigned slots, uint64_t start);
|
|
|
|
/**
|
|
* vfs_prefetch_read:
|
|
*
|
|
* Copy up to @len octets at @off from the fetched windows. Returns the
|
|
* octets copied (fewer than @len when the range runs past what is
|
|
* ready, 0 when a ready window says the file ends there), or -1 when
|
|
* nothing at @off is ready: the caller then reads the network itself.
|
|
* Consumed windows are recycled as the reader moves past them.
|
|
**/
|
|
int64_t vfs_prefetch_read(struct vfs_prefetch *p, uint64_t off, uint8_t *buf, size_t len);
|
|
|
|
/**
|
|
* vfs_prefetch_view:
|
|
*
|
|
* vfs_prefetch_read() without the copy: points @ptr at the octets at
|
|
* @off in a ready window and returns how many follow there, to the end
|
|
* of that window; 0 at the end of the file, -1 on a miss, which
|
|
* retargets the thread as a read's miss does. Only the reader recycles
|
|
* windows, so @ptr stays valid until its next read, view or seek.
|
|
**/
|
|
int64_t vfs_prefetch_view(struct vfs_prefetch *p, uint64_t off, const uint8_t **ptr);
|
|
|
|
/**
|
|
* vfs_prefetch_seek:
|
|
*
|
|
* The reader will continue at @off: when that is outside what is
|
|
* fetched or being fetched, the thread starts over there.
|
|
**/
|
|
void vfs_prefetch_seek(struct vfs_prefetch *p, uint64_t off);
|
|
|
|
/**
|
|
* vfs_prefetch_free:
|
|
*
|
|
* Stops the thread (waiting for a fetch in flight to end) and frees
|
|
* everything. @user is the caller's to release afterwards.
|
|
**/
|
|
void vfs_prefetch_free(struct vfs_prefetch *p);
|
|
|
|
RETRO_END_DECLS
|
|
|
|
#endif
|