diff options
author | Thorbjørn Lindeijer <bjorn@lindeijer.nl> | 2025-02-28 08:37:52 +0100 |
---|---|---|
committer | Thorbjørn Lindeijer <bjorn@lindeijer.nl> | 2025-02-28 08:41:27 +0100 |
commit | ddd86b80a2090d40e61126d81c69fad6a0545a14 (patch) | |
tree | 629b98a222d8a7bceeb7966021347fdf83d076e0 /src | |
parent | 81a2f0829a9a0a0d524028fbff42273387078613 (diff) | |
download | mana-ddd86b80a2090d40e61126d81c69fad6a0545a14.tar.gz mana-ddd86b80a2090d40e61126d81c69fad6a0545a14.tar.bz2 mana-ddd86b80a2090d40e61126d81c69fad6a0545a14.tar.xz mana-ddd86b80a2090d40e61126d81c69fad6a0545a14.zip |
Replaced buffered SDL_RWops wrapper with PHYSFS_setBuffer
This removes the buffered SDL_RWops functionality that was just added in
51b0c3239265ddee2d1bf445f873299cc8193ab9. We just call PHYSFS_setBuffer
instead.
Using the PhysicsFS buffer is a little bit slower, likely because it
operates at a lower level, but it is fast enough for our purposes.
Uncompressed music files loaded from ZIP files can start playing in
about 1-2 ms.
I've also landed a fix for the problem in SDL_mixer, which works even
better in any case: https://github.com/libsdl-org/SDL_mixer/pull/671
Diffstat (limited to 'src')
-rw-r--r-- | src/CMakeLists.txt | 2 | ||||
-rw-r--r-- | src/utils/bufferedrwops.c | 184 | ||||
-rw-r--r-- | src/utils/bufferedrwops.h | 30 | ||||
-rw-r--r-- | src/utils/filesystem.h | 22 |
4 files changed, 13 insertions, 225 deletions
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index f47cbf5b..b7cfe091 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -368,8 +368,6 @@ set(SRCS resources/wallpaper.h utils/base64.cpp utils/base64.h - utils/bufferedrwops.c - utils/bufferedrwops.h utils/copynpaste.cpp utils/copynpaste.h utils/dtor.h diff --git a/src/utils/bufferedrwops.c b/src/utils/bufferedrwops.c deleted file mode 100644 index e12f8b5b..00000000 --- a/src/utils/bufferedrwops.c +++ /dev/null @@ -1,184 +0,0 @@ -/* - * Buffered SDL_RWops implementation - * Copyright (C) 2025 Thorbjørn Lindeijer - * - * Written as part of a conversation between a human and an AI assistant - * on GitHub Copilot Chat. - * - * This code is released into the public domain. You may use it freely - * for any purpose, including commercial applications. - * - * 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 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. - */ - -#include "bufferedrwops.h" - -#include <string.h> - -#define BUFFER_SIZE 2048 - -typedef struct BufferedRWops { - SDL_RWops* source; // The underlying RWops we're wrapping - size_t bufferPos; // Current position within buffer - size_t bufferFill; // How much valid data is in buffer - Sint64 virtualPos; // Current virtual stream position - Uint8 buffer[BUFFER_SIZE]; // Our read-ahead buffer -} BufferedRWops; - -static Sint64 SDLCALL buffered_size(SDL_RWops* context) { - BufferedRWops* br = (BufferedRWops*)context->hidden.unknown.data1; - return SDL_RWsize(br->source); -} - -static Sint64 SDLCALL buffered_seek(SDL_RWops* context, Sint64 offset, int whence) { - BufferedRWops* br = (BufferedRWops*)context->hidden.unknown.data1; - Sint64 newPos; - - // Calculate new position - switch (whence) { - case RW_SEEK_SET: - newPos = offset; - break; - case RW_SEEK_CUR: - newPos = br->virtualPos + offset; - break; - case RW_SEEK_END: { - Sint64 size = SDL_RWsize(br->source); - if (size < 0) return -1; - newPos = size + offset; - break; - } - default: - SDL_SetError("Invalid whence value"); - return -1; - } - - if (newPos < 0) { - SDL_SetError("Attempt to seek before start of file"); - return -1; - } - - // Check if new position is within our buffer - Sint64 bufferStart = br->virtualPos - br->bufferPos; - Sint64 bufferEnd = bufferStart + br->bufferFill; - - if (newPos >= bufferStart && newPos < bufferEnd) { - // Position is within buffer - just update buffer position - br->bufferPos = (size_t)(newPos - bufferStart); - } else { - // Position is outside buffer - need to seek in source - if (SDL_RWseek(br->source, newPos, RW_SEEK_SET) < 0) { - return -1; - } - br->bufferPos = 0; - br->bufferFill = 0; - } - - br->virtualPos = newPos; - return newPos; -} - -static size_t SDLCALL buffered_read(SDL_RWops* context, void* dst, size_t size, size_t maxnum) { - BufferedRWops* br = (BufferedRWops*)context->hidden.unknown.data1; - size_t total = size * maxnum; - size_t copied = 0; - - // Handle single byte reads separately since it is a common case when - // SDL_mixer uses stb_vorbis. - if (total == 1 && br->bufferPos < br->bufferFill) { - *(Uint8*)dst = br->buffer[br->bufferPos]; - ++br->bufferPos; - ++br->virtualPos; - return 1; - } - - while (copied < total) { - size_t remaining = total - copied; - - // If buffer is empty or exhausted, try to fill it - if (br->bufferPos >= br->bufferFill) { - // If we want to read more than the buffer size, bypass the buffer - if (remaining >= BUFFER_SIZE) { - size_t read = SDL_RWread(br->source, dst + copied, 1, remaining); - copied += read; - br->virtualPos += read; - break; // Nothing more to read - } - - br->bufferPos = 0; - br->bufferFill = SDL_RWread(br->source, br->buffer, 1, BUFFER_SIZE); - - if (br->bufferFill == 0) - break; // EOF or error - } - - // Copy what we can from the buffer - size_t available = br->bufferFill - br->bufferPos; - size_t toCopy = remaining < available ? remaining : available; - - memcpy(dst + copied, br->buffer + br->bufferPos, toCopy); - br->bufferPos += toCopy; - copied += toCopy; - br->virtualPos += toCopy; - } - - return copied / size; -} - -static size_t SDLCALL buffered_write(SDL_RWops* context, const void* ptr, size_t size, size_t num) { - SDL_SetError("Write operations not supported on buffered read-only RWops"); - return 0; -} - -static int SDLCALL buffered_close(SDL_RWops* context) { - if (context) { - BufferedRWops* br = (BufferedRWops*)context->hidden.unknown.data1; - if (br) { - if (br->source) { - SDL_RWclose(br->source); - } - SDL_free(br); - } - SDL_FreeRW(context); - } - return 0; -} - -SDL_RWops* createBufferedRWops(SDL_RWops* source) { - if (!source) { - SDL_SetError("NULL source RWops"); - return NULL; - } - - BufferedRWops* br = (BufferedRWops*)SDL_malloc(sizeof(BufferedRWops)); - if (!br) { - SDL_SetError("Out of memory"); - return NULL; - } - - br->source = source; - br->bufferPos = 0; - br->bufferFill = 0; - br->virtualPos = 0; - - SDL_RWops* rw = SDL_AllocRW(); - if (!rw) { - SDL_free(br); - return NULL; - } - - rw->size = buffered_size; - rw->seek = buffered_seek; - rw->read = buffered_read; - rw->write = buffered_write; - rw->close = buffered_close; - rw->hidden.unknown.data1 = br; - - return rw; -} diff --git a/src/utils/bufferedrwops.h b/src/utils/bufferedrwops.h deleted file mode 100644 index bba29467..00000000 --- a/src/utils/bufferedrwops.h +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Buffered SDL_RWops implementation - * Copyright (C) 2025 Thorbjørn Lindeijer - * - * Written as part of a conversation between a human and an AI assistant - * on GitHub Copilot Chat. - * - * This code is released into the public domain. You may use it freely - * for any purpose, including commercial applications. - * - * 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 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. - */ - -#include "SDL.h" - -#ifdef __cplusplus -extern "C" { -#endif - -SDL_RWops* createBufferedRWops(SDL_RWops* source); - -#ifdef __cplusplus -} -#endif diff --git a/src/utils/filesystem.h b/src/utils/filesystem.h index 0363cd22..3474e7c4 100644 --- a/src/utils/filesystem.h +++ b/src/utils/filesystem.h @@ -23,7 +23,6 @@ // Suppress deprecation warnings for PHYSFS_getUserDir #define PHYSFS_DEPRECATED -#include "utils/bufferedrwops.h" #include "utils/physfsrwops.h" #include <optional> @@ -267,18 +266,23 @@ inline SDL_RWops *openRWops(const std::string &path) /** * Creates a buffered SDL_RWops. * - * Used to workaround a performance issue when SDL_mixer is using stb_vorbis. - * The overhead of calling PHYSFS_readBytes each time is too high because - * stb_vorbis requests the file one byte at a time. + * Used to workaround a performance issue when SDL_mixer is using stb_vorbis, + * in which case the file is read one byte at a time. * * See https://github.com/libsdl-org/SDL_mixer/issues/670 */ -inline SDL_RWops *openBufferedRWops(const std::string &path) +inline SDL_RWops *openBufferedRWops(const std::string &path, + PHYSFS_uint64 bufferSize = 2048) { - auto rw = PHYSFSRWOPS_openRead(path.c_str()); - if (auto buffered = createBufferedRWops(rw)) - return buffered; - return rw; + if (auto file = PHYSFS_openRead(path.c_str())) + { + PHYSFS_setBuffer(file, bufferSize); + if (auto rw = PHYSFSRWOPS_makeRWops(file)) + return rw; + else + PHYSFS_close(file); + } + return nullptr; } inline void *loadFile(const std::string &path, size_t &datasize) |